@@ -726,6 +726,22 @@ def __init__(self, model: Model):
726
726
f"If not provided, this value will be set automatically to the same value as "
727
727
f"{ self .production_well_cost_adjustment_factor .Name } ."
728
728
)
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
+
729
745
self .oamwellfixed = self .ParameterDict [self .oamwellfixed .Name ] = floatParameter (
730
746
"Wellfield O&M Cost" ,
731
747
DefaultValue = - 1.0 ,
@@ -1677,11 +1693,11 @@ def __init__(self, model: Model):
1677
1693
f'total stimulation cost.'
1678
1694
)
1679
1695
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
+ )
1682
1700
1683
- # See TODO re:parameterizing indirect costs at src/geophires_x/Economics.py:652
1684
- # (https://github.com/NREL/GEOPHIRES-X/issues/383)
1685
1701
self .Cexpl = self .OutputParameterDict [self .Cexpl .Name ] = OutputParameter (
1686
1702
Name = "Exploration cost" ,
1687
1703
display_name = 'Exploration costs' ,
@@ -2429,9 +2445,8 @@ def Calculate(self, model: Model) -> None:
2429
2445
1750 * injpumphpcorrected ** 0.7 ) * 3 * injpumphpcorrected ** (- 0.11 )
2430
2446
self .Cpumps = Cpumpsinj + Cpumpsprod
2431
2447
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 * (
2435
2450
(model .wellbores .nprod .value + model .wellbores .ninj .value ) * 750 * 500. + self .Cpumps ) / 1E6
2436
2451
2437
2452
self .calculate_plant_costs (model )
@@ -2441,8 +2456,8 @@ def Calculate(self, model: Model) -> None:
2441
2456
if self .ccexplfixed .Valid :
2442
2457
self .Cexpl .value = self .ccexplfixed .value
2443
2458
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
2446
2461
2447
2462
# Surface Piping Length Costs (M$) #assumed $750k/km
2448
2463
self .Cpiping .value = 750 / 1000 * model .surfaceplant .piping_length .value
@@ -2698,6 +2713,10 @@ def Calculate(self, model: Model) -> None:
2698
2713
self ._calculate_derived_outputs (model )
2699
2714
model .logger .info (f'complete { __class__ !s} : { sys ._getframe ().f_code .co_name } ' )
2700
2715
2716
+ @property
2717
+ def _indirect_cost_factor (self ) -> float :
2718
+ return 1 + self .indirect_cost .quantity ().to ('dimensionless' ).magnitude
2719
+
2701
2720
def calculate_stimulation_costs (self , model : Model ) -> PlainQuantity :
2702
2721
if self .ccstimfixed .Valid :
2703
2722
stimulation_costs = self .ccstimfixed .quantity ().to (self .Cstim .CurrentUnits ).magnitude
@@ -2728,9 +2747,8 @@ def calculate_plant_costs(self, model: Model) -> None:
2728
2747
if self .ccplantfixed .Valid :
2729
2748
self .Cplant .value = self .ccplantfixed .value
2730
2749
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 (
2734
2752
model .surfaceplant .HeatExtracted .value ) * 1000.
2735
2753
2736
2754
# absorption chiller
@@ -2739,11 +2757,11 @@ def calculate_plant_costs(self, model: Model) -> None:
2739
2757
self .Cplant .value = self .ccplantfixed .value
2740
2758
else :
2741
2759
# 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
2744
2762
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
2747
2765
2748
2766
# now add chiller cost to surface plant cost
2749
2767
self .Cplant .value += self .chillercapex .value
@@ -2754,11 +2772,11 @@ def calculate_plant_costs(self, model: Model) -> None:
2754
2772
self .Cplant .value = self .ccplantfixed .value
2755
2773
else :
2756
2774
# 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
2759
2777
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
2762
2780
2763
2781
# now add heat pump cost to surface plant cost
2764
2782
self .Cplant .value += self .heatpumpcapex .value
@@ -2768,9 +2786,8 @@ def calculate_plant_costs(self, model: Model) -> None:
2768
2786
if self .ccplantfixed .Valid :
2769
2787
self .Cplant .value = self .ccplantfixed .value
2770
2788
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 (
2774
2791
model .surfaceplant .HeatExtracted .value ) * 1000.
2775
2792
2776
2793
# add 65$/KW for peaking boiler
@@ -2939,24 +2956,23 @@ def calculate_plant_costs(self, model: Model) -> None:
2939
2956
# factor 1.10 to convert from 2016 to 2022
2940
2957
direct_plant_cost_MUSD = self .ccplantadjfactor .value * self .Cplantcorrelation * 1.02 * 1.10
2941
2958
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
2945
2961
self .CAPEX_cost_electricity_plant = self .Cplant .value
2946
2962
2947
2963
# 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
2949
2965
if model .surfaceplant .enduse_option .value in [EndUseOptions .COGENERATION_TOPPING_EXTRA_ELECTRICITY ,
2950
2966
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 (
2952
2968
model .surfaceplant .HeatProduced .value / model .surfaceplant .enduse_efficiency_factor .value ) * 1000.
2953
2969
elif model .surfaceplant .enduse_option .value in [EndUseOptions .COGENERATION_BOTTOMING_EXTRA_HEAT ,
2954
2970
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 (
2956
2972
model .surfaceplant .HeatProduced .value / model .surfaceplant .enduse_efficiency_factor .value ) * 1000.
2957
2973
elif model .surfaceplant .enduse_option .value in [EndUseOptions .COGENERATION_PARALLEL_EXTRA_ELECTRICITY ,
2958
2974
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 (
2960
2976
model .surfaceplant .HeatProduced .value / model .surfaceplant .enduse_efficiency_factor .value ) * 1000.
2961
2977
2962
2978
self .Cplant .value = self .Cplant .value + self .CAPEX_cost_heat_plant
0 commit comments