@@ -726,6 +726,22 @@ def __init__(self, model: Model):
726726 f"If not provided, this value will be set automatically to the same value as "
727727 f"{ self .production_well_cost_adjustment_factor .Name } ."
728728 )
729+
730+ self .indirect_cost = self .ParameterDict [self .indirect_cost .Name ] = floatParameter (
731+ 'Indirect Cost Percentage' ,
732+ DefaultValue = 12 ,
733+ Min = 0 ,
734+ Max = 100 ,
735+ UnitType = Units .PERCENT ,
736+ PreferredUnits = PercentUnit .PERCENT ,
737+ CurrentUnits = PercentUnit .PERCENT ,
738+ ToolTipText = f'The default indirect cost percentage applied to capital costs. This value is used for all '
739+ f'cost categories unless a more specific indirect cost parameter is provided. For example, '
740+ f'reservoir stimulation costs use { self .stimulation_indirect_capital_cost .Name } '
741+ f'(default { self .stimulation_indirect_capital_cost .DefaultValue } %).'
742+ # FIXME WIP mention drilling also has 5% default indirect cost
743+ )
744+
729745 self .oamwellfixed = self .ParameterDict [self .oamwellfixed .Name ] = floatParameter (
730746 "Wellfield O&M Cost" ,
731747 DefaultValue = - 1.0 ,
@@ -1677,11 +1693,11 @@ def __init__(self, model: Model):
16771693 f'total stimulation cost.'
16781694 )
16791695
1680- # TODO https://github.com/NREL/GEOPHIRES-X/issues/383?title=Parameterize+indirect+cost+factor
1681- contingency_and_indirect_costs_tooltip = 'plus 15% contingency plus 12% indirect costs'
1696+ contingency_and_indirect_costs_tooltip = (
1697+ f'plus 15% contingency ' # TODO https://github.com/NREL/GEOPHIRES-X/issues/383
1698+ f'plus { self .indirect_cost .quantity ().to (convertible_unit ("%" )).magnitude } % indirect costs'
1699+ )
16821700
1683- # See TODO re:parameterizing indirect costs at src/geophires_x/Economics.py:652
1684- # (https://github.com/NREL/GEOPHIRES-X/issues/383)
16851701 self .Cexpl = self .OutputParameterDict [self .Cexpl .Name ] = OutputParameter (
16861702 Name = "Exploration cost" ,
16871703 display_name = 'Exploration costs' ,
@@ -2429,9 +2445,8 @@ def Calculate(self, model: Model) -> None:
24292445 1750 * injpumphpcorrected ** 0.7 ) * 3 * injpumphpcorrected ** (- 0.11 )
24302446 self .Cpumps = Cpumpsinj + Cpumpsprod
24312447
2432- # Based on GETEM 2016: 1.15 for 15% contingency and 1.12 for 12% indirect costs
2433- # TODO https://github.com/NREL/GEOPHIRES-X/issues/383?title=Parameterize+indirect+cost+factor
2434- self .Cgath .value = 1.15 * self .ccgathadjfactor .value * 1.12 * (
2448+ # Based on GETEM 2016: 1.15 for 15% contingency TODO https://github.com/NREL/GEOPHIRES-X/issues/383
2449+ self .Cgath .value = 1.15 * self .ccgathadjfactor .value * self ._indirect_cost_factor * (
24352450 (model .wellbores .nprod .value + model .wellbores .ninj .value ) * 750 * 500. + self .Cpumps ) / 1E6
24362451
24372452 self .calculate_plant_costs (model )
@@ -2441,8 +2456,8 @@ def Calculate(self, model: Model) -> None:
24412456 if self .ccexplfixed .Valid :
24422457 self .Cexpl .value = self .ccexplfixed .value
24432458 else :
2444- self .Cexpl .value = 1.15 * self .ccexpladjfactor .value * 1.12 * (
2445- 1. + self .cost_one_production_well .value * 0.6 ) # 1.15 for 15% contingency and 1.12 for 12% indirect costs
2459+ self .Cexpl .value = 1.15 * self .ccexpladjfactor .value * self . _indirect_cost_factor * (
2460+ 1. + self .cost_one_production_well .value * 0.6 ) # 1.15 for 15% contingency TODO https://github.com/NREL/GEOPHIRES-X/issues/383
24462461
24472462 # Surface Piping Length Costs (M$) #assumed $750k/km
24482463 self .Cpiping .value = 750 / 1000 * model .surfaceplant .piping_length .value
@@ -2698,6 +2713,10 @@ def Calculate(self, model: Model) -> None:
26982713 self ._calculate_derived_outputs (model )
26992714 model .logger .info (f'complete { __class__ !s} : { sys ._getframe ().f_code .co_name } ' )
27002715
2716+ @property
2717+ def _indirect_cost_factor (self ) -> float :
2718+ return 1 + self .indirect_cost .quantity ().to ('dimensionless' ).magnitude
2719+
27012720 def calculate_stimulation_costs (self , model : Model ) -> PlainQuantity :
27022721 if self .ccstimfixed .Valid :
27032722 stimulation_costs = self .ccstimfixed .quantity ().to (self .Cstim .CurrentUnits ).magnitude
@@ -2728,9 +2747,8 @@ def calculate_plant_costs(self, model: Model) -> None:
27282747 if self .ccplantfixed .Valid :
27292748 self .Cplant .value = self .ccplantfixed .value
27302749 else :
2731- # 1.15 for 15% contingency and 1.12 for 12% indirect costs
2732- # TODO https://github.com/NREL/GEOPHIRES-X/issues/383?title=Parameterize+indirect+cost+factor
2733- self .Cplant .value = 1.12 * 1.15 * self .ccplantadjfactor .value * 250E-6 * np .max (
2750+ # 1.15 for 15% contingency TODO https://github.com/NREL/GEOPHIRES-X/issues/383
2751+ self .Cplant .value = self ._indirect_cost_factor * 1.15 * self .ccplantadjfactor .value * 250E-6 * np .max (
27342752 model .surfaceplant .HeatExtracted .value ) * 1000.
27352753
27362754 # absorption chiller
@@ -2739,11 +2757,11 @@ def calculate_plant_costs(self, model: Model) -> None:
27392757 self .Cplant .value = self .ccplantfixed .value
27402758 else :
27412759 # this is for the direct-use part all the way up to the absorption chiller
2742- self .Cplant .value = 1.12 * 1.15 * self .ccplantadjfactor .value * 250E-6 * np .max (
2743- model .surfaceplant .HeatExtracted .value ) * 1000. # 1.15 for 15% contingency and 1.12 for 12% indirect costs
2760+ self .Cplant .value = self . _indirect_cost_factor * 1.15 * self .ccplantadjfactor .value * 250E-6 * np .max (
2761+ model .surfaceplant .HeatExtracted .value ) * 1000. # 1.15 for 15% contingency TODO https://github.com/NREL/GEOPHIRES-X/issues/383
27442762 if self .chillercapex .value == - 1 : # no value provided by user, use built-in correlation ($2500/ton)
2745- self .chillercapex .value = 1.12 * 1.15 * np .max (
2746- model .surfaceplant .cooling_produced .value ) * 1000 / 3.517 * 2500 / 1e6 # $2,500/ton of cooling. 1.15 for 15% contingency and 1.12 for 12% indirect costs
2763+ self .chillercapex .value = self . _indirect_cost_factor * 1.15 * np .max (
2764+ model .surfaceplant .cooling_produced .value ) * 1000 / 3.517 * 2500 / 1e6 # $2,500/ton of cooling. 1.15 for 15% contingency TODO https://github.com/NREL/GEOPHIRES-X/issues/383
27472765
27482766 # now add chiller cost to surface plant cost
27492767 self .Cplant .value += self .chillercapex .value
@@ -2754,11 +2772,11 @@ def calculate_plant_costs(self, model: Model) -> None:
27542772 self .Cplant .value = self .ccplantfixed .value
27552773 else :
27562774 # this is for the direct-use part all the way up to the heat pump
2757- self .Cplant .value = 1.12 * 1.15 * self .ccplantadjfactor .value * 250E-6 * np .max (
2758- model .surfaceplant .HeatExtracted .value ) * 1000. # 1.15 for 15% contingency and 1.12 for 12% indirect costs
2775+ self .Cplant .value = self . _indirect_cost_factor * 1.15 * self .ccplantadjfactor .value * 250E-6 * np .max (
2776+ model .surfaceplant .HeatExtracted .value ) * 1000. # 1.15 for 15% contingency
27592777 if self .heatpumpcapex .value == - 1 : # no value provided by user, use built-in correlation ($150/kWth)
2760- self .heatpumpcapex .value = 1.12 * 1.15 * np .max (
2761- model .surfaceplant .HeatProduced .value ) * 1000 * 150 / 1e6 # $150/kW. 1.15 for 15% contingency and 1.12 for 12% indirect costs
2778+ self .heatpumpcapex .value = self . _indirect_cost_factor * 1.15 * np .max (
2779+ model .surfaceplant .HeatProduced .value ) * 1000 * 150 / 1e6 # $150/kW. 1.15 for 15% contingency TODO https://github.com/NREL/GEOPHIRES-X/issues/383
27622780
27632781 # now add heat pump cost to surface plant cost
27642782 self .Cplant .value += self .heatpumpcapex .value
@@ -2768,9 +2786,8 @@ def calculate_plant_costs(self, model: Model) -> None:
27682786 if self .ccplantfixed .Valid :
27692787 self .Cplant .value = self .ccplantfixed .value
27702788 else :
2771- # 1.15 for 15% contingency and 1.12 for 12% indirect costs
2772- # TODO https://github.com/NREL/GEOPHIRES-X/issues/383?title=Parameterize+indirect+cost+factor
2773- self .Cplant .value = 1.12 * 1.15 * self .ccplantadjfactor .value * 250E-6 * np .max (
2789+ # 1.15 for 15% contingency TODO https://github.com/NREL/GEOPHIRES-X/issues/383
2790+ self .Cplant .value = self ._indirect_cost_factor * 1.15 * self .ccplantadjfactor .value * 250E-6 * np .max (
27742791 model .surfaceplant .HeatExtracted .value ) * 1000.
27752792
27762793 # add 65$/KW for peaking boiler
@@ -2939,24 +2956,23 @@ def calculate_plant_costs(self, model: Model) -> None:
29392956 # factor 1.10 to convert from 2016 to 2022
29402957 direct_plant_cost_MUSD = self .ccplantadjfactor .value * self .Cplantcorrelation * 1.02 * 1.10
29412958
2942- # factor 1.15 for 15% contingency and 1.12 for 12% indirect costs.
2943- # TODO https://github.com/NREL/GEOPHIRES-X/issues/383?title=Parameterize+indirect+cost+factor
2944- self .Cplant .value = 1.12 * 1.15 * direct_plant_cost_MUSD
2959+ # factor 1.15 for 15% contingency TODO https://github.com/NREL/GEOPHIRES-X/issues/383
2960+ self .Cplant .value = self ._indirect_cost_factor * 1.15 * direct_plant_cost_MUSD
29452961 self .CAPEX_cost_electricity_plant = self .Cplant .value
29462962
29472963 # add direct-use plant cost of co-gen system to Cplant (only of no total Cplant was provided)
2948- if not self .ccplantfixed .Valid : # 1.15 below for contingency and 1.12 for indirect costs
2964+ if not self .ccplantfixed .Valid : # 1.15 below for contingency TODO https://github.com/NREL/GEOPHIRES-X/issues/383
29492965 if model .surfaceplant .enduse_option .value in [EndUseOptions .COGENERATION_TOPPING_EXTRA_ELECTRICITY ,
29502966 EndUseOptions .COGENERATION_TOPPING_EXTRA_HEAT ]: # enduse_option = 3: cogen topping cycle
2951- self .CAPEX_cost_heat_plant = 1.12 * 1.15 * self .ccplantadjfactor .value * 250E-6 * np .max (
2967+ self .CAPEX_cost_heat_plant = self . _indirect_cost_factor * 1.15 * self .ccplantadjfactor .value * 250E-6 * np .max (
29522968 model .surfaceplant .HeatProduced .value / model .surfaceplant .enduse_efficiency_factor .value ) * 1000.
29532969 elif model .surfaceplant .enduse_option .value in [EndUseOptions .COGENERATION_BOTTOMING_EXTRA_HEAT ,
29542970 EndUseOptions .COGENERATION_BOTTOMING_EXTRA_ELECTRICITY ]: # enduse_option = 4: cogen bottoming cycle
2955- self .CAPEX_cost_heat_plant = 1.12 * 1.15 * self .ccplantadjfactor .value * 250E-6 * np .max (
2971+ self .CAPEX_cost_heat_plant = self . _indirect_cost_factor * 1.15 * self .ccplantadjfactor .value * 250E-6 * np .max (
29562972 model .surfaceplant .HeatProduced .value / model .surfaceplant .enduse_efficiency_factor .value ) * 1000.
29572973 elif model .surfaceplant .enduse_option .value in [EndUseOptions .COGENERATION_PARALLEL_EXTRA_ELECTRICITY ,
29582974 EndUseOptions .COGENERATION_PARALLEL_EXTRA_HEAT ]: # cogen parallel cycle
2959- self .CAPEX_cost_heat_plant = 1.12 * 1.15 * self .ccplantadjfactor .value * 250E-6 * np .max (
2975+ self .CAPEX_cost_heat_plant = self . _indirect_cost_factor * 1.15 * self .ccplantadjfactor .value * 250E-6 * np .max (
29602976 model .surfaceplant .HeatProduced .value / model .surfaceplant .enduse_efficiency_factor .value ) * 1000.
29612977
29622978 self .Cplant .value = self .Cplant .value + self .CAPEX_cost_heat_plant
0 commit comments