Skip to content

Commit d03b208

Browse files
Parameterize Indirect Cost Percentage (default = 12%)
1 parent 7ae2df7 commit d03b208

File tree

4 files changed

+84
-52
lines changed

4 files changed

+84
-52
lines changed

src/geophires_x/Economics.py

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)