diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 06de9ae2..3d62e47b 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -572,6 +572,7 @@ def __init__(self, model: Model): ToolTipText="Specify the economic model to calculate the levelized cost of energy. " + '; '.join([f'{it.int_value}: {it.value}' for it in EconomicModel]) ) + self.ccstimfixed = self.ParameterDict[self.ccstimfixed.Name] = floatParameter( "Reservoir Stimulation Capital Cost", DefaultValue=-1.0, @@ -584,6 +585,38 @@ def __init__(self, model: Model): Valid=False, ToolTipText="Total reservoir stimulation capital cost" ) + + max_stimulation_cost_per_well_MUSD = 100 + self.stimulation_cost_per_injection_well = \ + self.ParameterDict[self.stimulation_cost_per_injection_well.Name] = floatParameter( + 'Reservoir Stimulation Capital Cost per Injection Well', + DefaultValue=1.25, + Min=0, + Max=max_stimulation_cost_per_well_MUSD, + UnitType=Units.CURRENCY, + PreferredUnits=CurrencyUnit.MDOLLARS, + CurrentUnits=CurrencyUnit.MDOLLARS, + Provided=False, + ToolTipText='Reservoir stimulation capital cost per injection well' + ) + + stimulation_cost_per_production_well_default_value_MUSD = 0 + stimulation_cost_per_production_well_default_value_note = \ + '. By default, only the injection wells are assumed to be stimulated unless this parameter is provided.' \ + if stimulation_cost_per_production_well_default_value_MUSD == 0 else '' + self.stimulation_cost_per_production_well = \ + self.ParameterDict[self.stimulation_cost_per_production_well.Name] = floatParameter( + 'Reservoir Stimulation Capital Cost per Production Well', + DefaultValue=stimulation_cost_per_production_well_default_value_MUSD, + Min=0, + Max=max_stimulation_cost_per_well_MUSD, + UnitType=Units.CURRENCY, + PreferredUnits=CurrencyUnit.MDOLLARS, + CurrentUnits=CurrencyUnit.MDOLLARS, + ToolTipText=f'Reservoir stimulation capital cost per production well' + f'{stimulation_cost_per_production_well_default_value_note}' + ) + self.ccstimadjfactor = self.ParameterDict[self.ccstimadjfactor.Name] = floatParameter( "Reservoir Stimulation Capital Cost Adjustment Factor", DefaultValue=1.0, @@ -594,7 +627,7 @@ def __init__(self, model: Model): CurrentUnits=PercentUnit.TENTH, Provided=False, Valid=True, - ToolTipText="Multiplier for built-in reservoir stimulation capital cost correlation" + ToolTipText="Multiplier for reservoir stimulation capital cost correlation" ) self.ccexplfixed = self.ParameterDict[self.ccexplfixed.Name] = floatParameter( "Exploration Capital Cost", @@ -1606,7 +1639,7 @@ def __init__(self, model: Model): ) # TODO https://github.com/NREL/GEOPHIRES-X/issues/383?title=Parameterize+indirect+cost+factor - contingency_and_indirect_costs_tooltip = 'plus 15% contingency plus 12% indirect costs' + stimulation_contingency_and_indirect_costs_tooltip = 'plus 15% contingency plus 5% indirect costs' # noinspection SpellCheckingInspection self.Cstim = self.OutputParameterDict[self.Cstim.Name] = OutputParameter( @@ -1614,11 +1647,19 @@ def __init__(self, model: Model): UnitType=Units.CURRENCY, PreferredUnits=CurrencyUnit.MDOLLARS, CurrentUnits=CurrencyUnit.MDOLLARS, - ToolTipText=f'Default correlation: $1.25M per injection well {contingency_and_indirect_costs_tooltip}. ' - f'Provide {self.ccstimadjfactor.Name} to multiply the default correlation. ' - f'Provide {self.ccstimfixed.Name} to override the default correlation and set your own cost.' + ToolTipText=f'Default correlation: ${self.stimulation_cost_per_injection_well.value}M ' + f'per injection well {stimulation_contingency_and_indirect_costs_tooltip}. ' + f'Provide {self.stimulation_cost_per_injection_well.Name} and ' + f'{self.stimulation_cost_per_production_well.Name} to set the correlation ' + f'costs per well. ' + f'Provide {self.ccstimadjfactor.Name} to multiply the correlation-calculated cost. ' + f'Provide {self.ccstimfixed.Name} to override the correlation and set your own ' + f'total stimulation cost.' ) + # TODO https://github.com/NREL/GEOPHIRES-X/issues/383?title=Parameterize+indirect+cost+factor + contingency_and_indirect_costs_tooltip = 'plus 15% contingency plus 12% indirect costs' + # See TODO re:parameterizing indirect costs at src/geophires_x/Economics.py:652 # (https://github.com/NREL/GEOPHIRES-X/issues/383) self.Cexpl = self.OutputParameterDict[self.Cexpl.Name] = OutputParameter( @@ -2336,12 +2377,18 @@ def Calculate(self, model: Model) -> None: if self.ccstimfixed.Valid: self.Cstim.value = self.ccstimfixed.value else: - base_stimulation_cost_MUSD_per_injection_well = 1.25 # TODO parameterize + stim_cost_per_injection_well = self.stimulation_cost_per_injection_well.quantity().to( + self.Cstim.CurrentUnits).magnitude + stim_cost_per_production_well = self.stimulation_cost_per_production_well.quantity().to( + self.Cstim.CurrentUnits).magnitude # 1.15 for 15% contingency and 1.05 for 5% indirect costs # TODO https://github.com/NREL/GEOPHIRES-X/issues/383?title=Parameterize+indirect+cost+factor - self.Cstim.value = (base_stimulation_cost_MUSD_per_injection_well * self.ccstimadjfactor.value - * model.wellbores.ninj.value + self.Cstim.value = (( + stim_cost_per_injection_well * model.wellbores.ninj.value + + stim_cost_per_production_well * model.wellbores.nprod.value + ) + * self.ccstimadjfactor.value * 1.05 * 1.15) # field gathering system costs (M$) diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index 50dc13c1..d1c45300 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -478,7 +478,7 @@ def PrintOutputs(self, model: Model): if model.economics.totalcapcost.Valid and model.wellbores.redrill.value > 0: f.write(f' Drilling and completion costs (for redrilling):{model.economics.Cwell.value:10.2f} ' + model.economics.Cwell.CurrentUnits.value + NL) f.write(f' Drilling and completion costs per redrilled well: {(model.economics.Cwell.value/(model.wellbores.nprod.value+model.wellbores.ninj.value)):10.2f} ' + model.economics.Cwell.CurrentUnits.value + NL) - f.write(f' Stimulation costs (for redrilling): {model.economics.Cstim.value:10.2f} ' + model.economics.Cstim.CurrentUnits.value + NL) + f.write(f' Stimulation costs (for redrilling): {econ.Cstim.value:10.2f} {econ.Cstim.CurrentUnits.value}\n') if model.economics.RITCValue.value: f.write(f' {model.economics.RITCValue.display_name}: {-1*model.economics.RITCValue.value:10.2f} {model.economics.RITCValue.CurrentUnits.value}\n') diff --git a/src/geophires_x_schema_generator/geophires-request.json b/src/geophires_x_schema_generator/geophires-request.json index 61e3b937..779604ea 100644 --- a/src/geophires_x_schema_generator/geophires-request.json +++ b/src/geophires_x_schema_generator/geophires-request.json @@ -1395,8 +1395,26 @@ "minimum": 0, "maximum": 1000 }, + "Reservoir Stimulation Capital Cost per Injection Well": { + "description": "Reservoir stimulation capital cost per injection well", + "type": "number", + "units": "MUSD", + "category": "Economics", + "default": 1.25, + "minimum": 0, + "maximum": 100 + }, + "Reservoir Stimulation Capital Cost per Production Well": { + "description": "Reservoir stimulation capital cost per production well. By default, only the injection wells are assumed to be stimulated unless this parameter is provided.", + "type": "number", + "units": "MUSD", + "category": "Economics", + "default": 0, + "minimum": 0, + "maximum": 100 + }, "Reservoir Stimulation Capital Cost Adjustment Factor": { - "description": "Multiplier for built-in reservoir stimulation capital cost correlation", + "description": "Multiplier for reservoir stimulation capital cost correlation", "type": "number", "units": "", "category": "Economics", diff --git a/src/geophires_x_schema_generator/geophires-result.json b/src/geophires_x_schema_generator/geophires-result.json index 52fcb5af..9cba6618 100644 --- a/src/geophires_x_schema_generator/geophires-result.json +++ b/src/geophires_x_schema_generator/geophires-result.json @@ -351,7 +351,7 @@ "Drilling and completion costs per redrilled well": {}, "Stimulation costs": { "type": "number", - "description": "Default correlation: $1.25M per injection well plus 15% contingency plus 12% indirect costs. Provide Reservoir Stimulation Capital Cost Adjustment Factor to multiply the default correlation. Provide Reservoir Stimulation Capital Cost to override the default correlation and set your own cost.", + "description": "Default correlation: $1.25M per injection well plus 15% contingency plus 5% indirect costs. Provide Reservoir Stimulation Capital Cost per Injection Well and Reservoir Stimulation Capital Cost per Production Well to set the correlation costs per well. Provide Reservoir Stimulation Capital Cost Adjustment Factor to multiply the correlation-calculated cost. Provide Reservoir Stimulation Capital Cost to override the correlation and set your own total stimulation cost.", "units": "MUSD" }, "Stimulation costs (for redrilling)": {}, diff --git a/tests/test_geophires_x.py b/tests/test_geophires_x.py index 7ded3f88..eefec5b8 100644 --- a/tests/test_geophires_x.py +++ b/tests/test_geophires_x.py @@ -12,6 +12,7 @@ from geophires_x_client import _get_logger from geophires_x_client.geophires_input_parameters import EndUseOption from geophires_x_client.geophires_input_parameters import GeophiresInputParameters +from geophires_x_client.geophires_input_parameters import ImmutableGeophiresInputParameters from geophires_x_tests.test_options_list import WellDrillingCostCorrelationTestCase from tests.base_test_case import BaseTestCase @@ -941,3 +942,36 @@ def test_sbt_coaxial_raises_error(self): ) client.get_geophires_result(params) self.assertIn('SBT with coaxial configuration is not implemented', str(e.exception)) + + def test_production_well_stimulation_cost(self): + def _get_result(prod_well_stim_MUSD: Optional[int] = None) -> GeophiresXResult: + p = {} + if prod_well_stim_MUSD is not None: + p['Reservoir Stimulation Capital Cost per Production Well'] = prod_well_stim_MUSD + + return GeophiresXClient().get_geophires_result( + ImmutableGeophiresInputParameters( + from_file_path=self._get_test_file_path('geophires_x_tests/generic-egs-case.txt'), + params=p, + ) + ) + + result_no_prod_stim: GeophiresXResult = _get_result() + + result_prod_stim: GeophiresXResult = _get_result(1.25) + + # TODO https://github.com/NREL/GEOPHIRES-X/issues/383?title=Parameterize+indirect+cost+factor + indirect_and_contingency = 1.05 * 1.15 + + self.assertAlmostEqual( + ( + 2 + * ( + result_no_prod_stim.result['CAPITAL COSTS (M$)']['Stimulation costs']['value'] + / (indirect_and_contingency) + ) + ) + * indirect_and_contingency, + result_prod_stim.result['CAPITAL COSTS (M$)']['Stimulation costs']['value'], + places=1, + )