From 9fd91d9b42d350640333ac948e87d1841075e69f Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Wed, 30 Jul 2025 07:54:13 -0700 Subject: [PATCH 01/13] Dedicated output parameters for Injection/production well casing IDs --- src/geophires_x/Outputs.py | 4 +- src/geophires_x/WellBores.py | 37 ++++++++++++++++--- .../geophires-result.json | 12 +++++- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index 51bcdee5..580bd5f2 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -327,8 +327,8 @@ def PrintOutputs(self, model: Model): f.write(' User-provided production well temperature drop\n') f.write(f' Constant production well temperature drop: {model.wellbores.tempdropprod.value:10.1f} ' + model.wellbores.tempdropprod.PreferredUnits.value + NL) f.write(f' Flowrate per production well: {model.wellbores.prodwellflowrate.value:10.1f} ' + model.wellbores.prodwellflowrate.CurrentUnits.value + NL) - f.write(f' Injection well casing ID: {model.wellbores.injwelldiam.value:10.3f} ' + model.wellbores.injwelldiam.CurrentUnits.value + NL) - f.write(f' Production well casing ID: {model.wellbores.prodwelldiam.value:10.3f} ' + model.wellbores.prodwelldiam.CurrentUnits.value + NL) + f.write(f' {model.wellbores.injection_well_casing_inner_diameter.display_name}: {model.wellbores.injection_well_casing_inner_diameter.value:10.3f} {model.wellbores.injection_well_casing_inner_diameter.CurrentUnits.value}\n') + f.write(f' {model.wellbores.production_well_casing_inner_diameter.display_name}: {model.wellbores.prodwelldiam.value:10.3f} {model.wellbores.prodwelldiam.CurrentUnits.value}\n') f.write(f' {model.wellbores.redrill.display_name}: {model.wellbores.redrill.value:10.0f}\n') if model.surfaceplant.enduse_option.value in [EndUseOptions.ELECTRICITY, EndUseOptions.COGENERATION_TOPPING_EXTRA_HEAT, EndUseOptions.COGENERATION_TOPPING_EXTRA_ELECTRICITY, EndUseOptions.COGENERATION_BOTTOMING_EXTRA_ELECTRICITY, EndUseOptions.COGENERATION_BOTTOMING_EXTRA_HEAT, EndUseOptions.COGENERATION_PARALLEL_EXTRA_HEAT, EndUseOptions.COGENERATION_PARALLEL_EXTRA_ELECTRICITY]: f.write(' Power plant type: ' + str(model.surfaceplant.plant_type.value.value) + NL) diff --git a/src/geophires_x/WellBores.py b/src/geophires_x/WellBores.py index dca1eb52..9042be29 100644 --- a/src/geophires_x/WellBores.py +++ b/src/geophires_x/WellBores.py @@ -3,7 +3,7 @@ from pint.facets.plain import PlainQuantity from .Parameter import floatParameter, intParameter, boolParameter, OutputParameter, ReadParameter, \ - coerce_int_params_to_enum_values + coerce_int_params_to_enum_values, Parameter from geophires_x.GeoPHIRESUtils import vapor_pressure_water_kPa, quantity, static_pressure_MPa from geophires_x.GeoPHIRESUtils import density_water_kg_per_m3 from geophires_x.GeoPHIRESUtils import viscosity_water_Pa_sec @@ -738,6 +738,7 @@ def __init__(self, model: Model): "same value." ) + # noinspection SpellCheckingInspection self.prodwelldiam = self.ParameterDict[self.prodwelldiam.Name] = floatParameter( "Production Well Diameter", DefaultValue=8.0, @@ -748,9 +749,10 @@ def __init__(self, model: Model): CurrentUnits=LengthUnit.INCHES, Required=True, ErrMessage="assume default production well diameter (8 inch)", - ToolTipText="Inner diameter of production wellbore (assumed constant along the wellbore) to calculate \ - frictional pressure drop and wellbore heat transmission with Rameys model" + ToolTipText='Inner diameter of production wellbore (assumed constant along the wellbore) to calculate ' + 'frictional pressure drop and wellbore heat transmission with Rameys model' ) + # noinspection SpellCheckingInspection self.injwelldiam = self.ParameterDict[self.injwelldiam.Name] = floatParameter( "Injection Well Diameter", DefaultValue=8.0, @@ -761,8 +763,8 @@ def __init__(self, model: Model): CurrentUnits=LengthUnit.INCHES, Required=True, ErrMessage="assume default injection well diameter (8 inch)", - ToolTipText="Inner diameter of production wellbore (assumed constant along the wellbore) to calculate " - "frictional pressure drop and wellbore heat transmission with Rameys model" + ToolTipText='Inner diameter of production wellbore (assumed constant along the wellbore) to calculate ' + 'frictional pressure drop and wellbore heat transmission with Rameys model' ) self.rameyoptionprod = self.ParameterDict[self.rameyoptionprod.Name] = boolParameter( "Ramey Production Wellbore Model", @@ -1097,6 +1099,21 @@ def __init__(self, model: Model): self.MyPath = __file__ # Results - used by other objects or printed in output downstream + + self.injection_well_casing_inner_diameter = self.OutputParameterDict[self.injection_well_casing_inner_diameter.Name] = OutputParameter( + Name='Injection well casing ID', + UnitType=self.injwelldiam.UnitType, + PreferredUnits=self.injwelldiam.PreferredUnits, + CurrentUnits=self.injwelldiam.CurrentUnits, + ToolTipText=self.injwelldiam.ToolTipText, + ) + self.production_well_casing_inner_diameter = self.OutputParameterDict[self.production_well_casing_inner_diameter.Name] = OutputParameter( + Name='Production well casing ID', + UnitType=self.prodwelldiam.UnitType, + PreferredUnits=self.prodwelldiam.PreferredUnits, + CurrentUnits=self.prodwelldiam.CurrentUnits, + ToolTipText=self.prodwelldiam.ToolTipText, + ) self.production_reservoir_pressure = self.OutputParameterDict[self.production_reservoir_pressure.Name] = OutputParameter( Name="Calculated Reservoir Pressure", value=self.Phydrostatic.value, @@ -1537,4 +1554,14 @@ def Calculate(self, model: Model) -> None: # negative pumping power values become zero (b/c we are not generating electricity) self.PumpingPower.value = [0. if x < 0. else x for x in self.PumpingPower.value] + # Injection/production well casing ID have same value as inputs but exist as separate output parameters due to + # having a different display name. + self._set_output_param_from_input_param(self.injwelldiam, self.injection_well_casing_inner_diameter) + self._set_output_param_from_input_param(self.prodwelldiam, self.production_well_casing_inner_diameter) + model.logger.info(f'complete {self.__class__.__name__}: {__name__}') + + # noinspection PyMethodMayBeStatic + def _set_output_param_from_input_param(self, input_param: Parameter, output_param: OutputParameter) -> None: + output_param.value = input_param.value + output_param.CurrentUnits = input_param.CurrentUnits diff --git a/src/geophires_x_schema_generator/geophires-result.json b/src/geophires_x_schema_generator/geophires-result.json index 9b2251cc..5d33b0d4 100644 --- a/src/geophires_x_schema_generator/geophires-result.json +++ b/src/geophires_x_schema_generator/geophires-result.json @@ -212,8 +212,16 @@ }, "Average production well temperature drop": {}, "Flowrate per production well": {}, - "Injection well casing ID": {}, - "Production well casing ID": {}, + "Injection well casing ID": { + "type": "number", + "description": "Inner diameter of production wellbore (assumed constant along the wellbore) to calculate frictional pressure drop and wellbore heat transmission with Rameys model", + "units": "in" + }, + "Production well casing ID": { + "type": "number", + "description": "Inner diameter of production wellbore (assumed constant along the wellbore) to calculate frictional pressure drop and wellbore heat transmission with Rameys model", + "units": "in" + }, "Number of times redrilling": { "type": "number", "description": "redrill", From 2cae8ad93dac7c15c16d3e2551f87a9ced9ab74e Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Wed, 30 Jul 2025 08:05:46 -0700 Subject: [PATCH 02/13] sync casing ID output params for AGS, SBT, and SUTRA --- src/geophires_x/AGSWellBores.py | 2 ++ src/geophires_x/SBTWellbores.py | 2 ++ src/geophires_x/SUTRAWellBores.py | 4 +++- src/geophires_x/WellBores.py | 21 +++++++++++++-------- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/geophires_x/AGSWellBores.py b/src/geophires_x/AGSWellBores.py index 210a94fe..8998e2b3 100644 --- a/src/geophires_x/AGSWellBores.py +++ b/src/geophires_x/AGSWellBores.py @@ -1028,6 +1028,8 @@ def Calculate(self, model: Model) -> None: # (b/c we are not generating electricity) = thermosiphon is happening! self.PumpingPower.value = [max(x, 0.) for x in self.PumpingPower.value] + self._sync_output_params_from_input_params() + model.logger.info(f'complete {str(__class__)}: {sys._getframe().f_code.co_name}') def __str__(self): diff --git a/src/geophires_x/SBTWellbores.py b/src/geophires_x/SBTWellbores.py index 86082a5d..6cce5737 100644 --- a/src/geophires_x/SBTWellbores.py +++ b/src/geophires_x/SBTWellbores.py @@ -286,6 +286,8 @@ def Calculate(self, model: Model) -> None: # (b/c we are not generating electricity) = thermosiphon is happening! self.PumpingPower.value = [max(x, 0.) for x in self.PumpingPower.value] + self._sync_output_params_from_input_params() + model.logger.info(f'complete {str(__class__)}: {sys._getframe().f_code.co_name}') def CalculateNonverticalPressureDrop(self, model, value, time_max, al): diff --git a/src/geophires_x/SUTRAWellBores.py b/src/geophires_x/SUTRAWellBores.py index ab77aea1..60fa81d9 100644 --- a/src/geophires_x/SUTRAWellBores.py +++ b/src/geophires_x/SUTRAWellBores.py @@ -342,4 +342,6 @@ def Calculate(self, model: Model) -> None: / model.surfaceplant.pump_efficiency.value ) - model.logger.info("complete " + str(__class__) + ": " + sys._getframe().f_code.co_name) + self._sync_output_params_from_input_params() + + model.logger.info(f'complete {str(__class__)}: {sys._getframe().f_code.co_name}') diff --git a/src/geophires_x/WellBores.py b/src/geophires_x/WellBores.py index 9042be29..2146c6d3 100644 --- a/src/geophires_x/WellBores.py +++ b/src/geophires_x/WellBores.py @@ -1554,14 +1554,19 @@ def Calculate(self, model: Model) -> None: # negative pumping power values become zero (b/c we are not generating electricity) self.PumpingPower.value = [0. if x < 0. else x for x in self.PumpingPower.value] - # Injection/production well casing ID have same value as inputs but exist as separate output parameters due to - # having a different display name. - self._set_output_param_from_input_param(self.injwelldiam, self.injection_well_casing_inner_diameter) - self._set_output_param_from_input_param(self.prodwelldiam, self.production_well_casing_inner_diameter) + self._sync_output_params_from_input_params() model.logger.info(f'complete {self.__class__.__name__}: {__name__}') - # noinspection PyMethodMayBeStatic - def _set_output_param_from_input_param(self, input_param: Parameter, output_param: OutputParameter) -> None: - output_param.value = input_param.value - output_param.CurrentUnits = input_param.CurrentUnits + def _sync_output_params_from_input_params(self) -> None: + """ + Handles setting output parameters whose values are based on 1:1 corresponding input parameters. + """ + + def _set_output_param_from_input_param(input_param: Parameter, output_param: OutputParameter) -> None: + output_param.value = input_param.quantity().to(output_param.CurrentUnits).magnitude + + # Injection/production well casing ID have same value as inputs but exist as separate output parameters due to + # having a different display name. + _set_output_param_from_input_param(self.injwelldiam, self.injection_well_casing_inner_diameter) + _set_output_param_from_input_param(self.prodwelldiam, self.production_well_casing_inner_diameter) From 82143e02b6a142c59321787d7ecf22ac9af1bae9 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Wed, 30 Jul 2025 08:59:53 -0700 Subject: [PATCH 03/13] Use production output parameter value --- src/geophires_x/Outputs.py | 2 +- src/geophires_x/SUTRAOutputs.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index 580bd5f2..dd0b6857 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -328,7 +328,7 @@ def PrintOutputs(self, model: Model): f.write(f' Constant production well temperature drop: {model.wellbores.tempdropprod.value:10.1f} ' + model.wellbores.tempdropprod.PreferredUnits.value + NL) f.write(f' Flowrate per production well: {model.wellbores.prodwellflowrate.value:10.1f} ' + model.wellbores.prodwellflowrate.CurrentUnits.value + NL) f.write(f' {model.wellbores.injection_well_casing_inner_diameter.display_name}: {model.wellbores.injection_well_casing_inner_diameter.value:10.3f} {model.wellbores.injection_well_casing_inner_diameter.CurrentUnits.value}\n') - f.write(f' {model.wellbores.production_well_casing_inner_diameter.display_name}: {model.wellbores.prodwelldiam.value:10.3f} {model.wellbores.prodwelldiam.CurrentUnits.value}\n') + f.write(f' {model.wellbores.production_well_casing_inner_diameter.display_name}: {model.wellbores.production_well_casing_inner_diameter.value:10.3f} {model.wellbores.production_well_casing_inner_diameter.CurrentUnits.value}\n') f.write(f' {model.wellbores.redrill.display_name}: {model.wellbores.redrill.value:10.0f}\n') if model.surfaceplant.enduse_option.value in [EndUseOptions.ELECTRICITY, EndUseOptions.COGENERATION_TOPPING_EXTRA_HEAT, EndUseOptions.COGENERATION_TOPPING_EXTRA_ELECTRICITY, EndUseOptions.COGENERATION_BOTTOMING_EXTRA_ELECTRICITY, EndUseOptions.COGENERATION_BOTTOMING_EXTRA_HEAT, EndUseOptions.COGENERATION_PARALLEL_EXTRA_HEAT, EndUseOptions.COGENERATION_PARALLEL_EXTRA_ELECTRICITY]: f.write(' Power plant type: ' + str(model.surfaceplant.plant_type.value.value) + NL) diff --git a/src/geophires_x/SUTRAOutputs.py b/src/geophires_x/SUTRAOutputs.py index 58a047c1..813188cf 100644 --- a/src/geophires_x/SUTRAOutputs.py +++ b/src/geophires_x/SUTRAOutputs.py @@ -104,8 +104,8 @@ def PrintOutputs(self, model: Model): f.write(f' Pump efficiency: {pump_efficiency_display}{NL}') f.write(f" Lifetime Average Well Flow Rate: {np.average(abs(model.wellbores.ProductionWellFlowRates.value)):10.1f} " + model.wellbores.ProductionWellFlowRates.CurrentUnits.value + NL) - f.write(f" Injection well casing ID: {model.wellbores.injwelldiam.value:10.3f} " + model.wellbores.injwelldiam.CurrentUnits.value + NL) - f.write(f" Production well casing ID: {model.wellbores.prodwelldiam.value:10.3f} " + model.wellbores.prodwelldiam.CurrentUnits.value + NL) + f.write(f' {model.wellbores.injection_well_casing_inner_diameter.display_name}: {model.wellbores.injection_well_casing_inner_diameter.value:10.3f} {model.wellbores.injection_well_casing_inner_diameter.CurrentUnits.value}\n') + f.write(f' {model.wellbores.production_well_casing_inner_diameter.display_name}: {model.wellbores.production_well_casing_inner_diameter.value:10.3f} {model.wellbores.production_well_casing_inner_diameter.CurrentUnits.value}\n') f.write(NL) f.write(NL) f.write(" ***RESERVOIR SIMULATION RESULTS***" + NL) From 5008d0ac06fb63992563a7e2e655c1ab09bafdd5 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Wed, 30 Jul 2025 09:05:08 -0700 Subject: [PATCH 04/13] fix Injection Well Diameter tooltip text typo --- src/geophires_x/WellBores.py | 2 +- src/geophires_x_schema_generator/geophires-result.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/geophires_x/WellBores.py b/src/geophires_x/WellBores.py index 2146c6d3..9d32bd30 100644 --- a/src/geophires_x/WellBores.py +++ b/src/geophires_x/WellBores.py @@ -763,7 +763,7 @@ def __init__(self, model: Model): CurrentUnits=LengthUnit.INCHES, Required=True, ErrMessage="assume default injection well diameter (8 inch)", - ToolTipText='Inner diameter of production wellbore (assumed constant along the wellbore) to calculate ' + ToolTipText='Inner diameter of injection wellbore (assumed constant along the wellbore) to calculate ' 'frictional pressure drop and wellbore heat transmission with Rameys model' ) self.rameyoptionprod = self.ParameterDict[self.rameyoptionprod.Name] = boolParameter( diff --git a/src/geophires_x_schema_generator/geophires-result.json b/src/geophires_x_schema_generator/geophires-result.json index 5d33b0d4..00cab4c7 100644 --- a/src/geophires_x_schema_generator/geophires-result.json +++ b/src/geophires_x_schema_generator/geophires-result.json @@ -214,7 +214,7 @@ "Flowrate per production well": {}, "Injection well casing ID": { "type": "number", - "description": "Inner diameter of production wellbore (assumed constant along the wellbore) to calculate frictional pressure drop and wellbore heat transmission with Rameys model", + "description": "Inner diameter of injection wellbore (assumed constant along the wellbore) to calculate frictional pressure drop and wellbore heat transmission with Rameys model", "units": "in" }, "Production well casing ID": { From 6cd71800c7ba79bfe9caa0eccb5fcecd490c877a Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Wed, 30 Jul 2025 13:26:00 -0700 Subject: [PATCH 05/13] Increase max allowed surface plant cost to $10B --- src/geophires_x/Economics.py | 2 +- src/geophires_x_schema_generator/geophires-request.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 4b0073ae..d7ad098e 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -760,7 +760,7 @@ def __init__(self, model: Model): "Surface Plant Capital Cost", DefaultValue=-1.0, Min=0, - Max=1000, + Max=10000, UnitType=Units.CURRENCY, PreferredUnits=CurrencyUnit.MDOLLARS, CurrentUnits=CurrencyUnit.MDOLLARS, diff --git a/src/geophires_x_schema_generator/geophires-request.json b/src/geophires_x_schema_generator/geophires-request.json index 494a2978..292d7dbd 100644 --- a/src/geophires_x_schema_generator/geophires-request.json +++ b/src/geophires_x_schema_generator/geophires-request.json @@ -1510,7 +1510,7 @@ "category": "Economics", "default": -1.0, "minimum": 0, - "maximum": 1000 + "maximum": 10000 }, "Surface Plant Capital Cost Adjustment Factor": { "description": "Multiplier for built-in surface plant capital cost correlation", From 2a9af955dcf5a6ac0fb9e4eaa9dd17907a4f165c Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 31 Jul 2025 07:48:48 -0700 Subject: [PATCH 06/13] Additional case in test_production_well_stimulation_cost --- tests/test_geophires_x.py | 49 ++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/tests/test_geophires_x.py b/tests/test_geophires_x.py index d64e67c4..1e2c5607 100644 --- a/tests/test_geophires_x.py +++ b/tests/test_geophires_x.py @@ -3,6 +3,7 @@ import tempfile import uuid from pathlib import Path +from typing import Any from typing import Optional from geophires_x.OptionList import PlantType @@ -965,31 +966,39 @@ def test_sbt_coaxial_raises_error(self): 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: + def _get_result( + prod_well_stim_MUSD: Optional[int] = None, + inj_well_stim_MUSD: Optional[int] = None, + additional_params: Optional[dict[str, Any]] = None, + ) -> GeophiresXResult: + if additional_params is None: + additional_params = {} + p = {} if prod_well_stim_MUSD is not None: p['Reservoir Stimulation Capital Cost per Production Well'] = prod_well_stim_MUSD + if inj_well_stim_MUSD is not None: + p['Reservoir Stimulation Capital Cost per Injection Well'] = inj_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, - ) + input_params: ImmutableGeophiresInputParameters = ImmutableGeophiresInputParameters( + from_file_path=self._get_test_file_path('geophires_x_tests/generic-egs-case.txt'), + params={**p, **additional_params}, ) + return GeophiresXClient().get_geophires_result(input_params) 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 + default_contingency_factor = 1.15 + indirect_and_contingency = 1.05 * default_contingency_factor # default indirect cost factor and contingency self.assertAlmostEqual( ( 2 * ( result_no_prod_stim.result['CAPITAL COSTS (M$)']['Stimulation costs']['value'] - / (indirect_and_contingency) + / indirect_and_contingency ) ) * indirect_and_contingency, @@ -997,6 +1006,28 @@ def _get_result(prod_well_stim_MUSD: Optional[int] = None) -> GeophiresXResult: places=1, ) + doublets = 59 + # fmt:off + result_4M_per_well: GeophiresXResult = _get_result( + 4, + 4, + { + 'Reservoir Stimulation Indirect Capital Cost Percentage': 0, + 'Number of Production Wells': doublets, + 'Number of Injection Wells': doublets, + + # offset contingency + 'Reservoir Stimulation Capital Cost Adjustment Factor': 1/default_contingency_factor, + } + ) + # fmt:on + + self.assertAlmostEqual( + (4 * doublets * 2), + result_4M_per_well.result['CAPITAL COSTS (M$)']['Stimulation costs']['value'], + places=1, + ) + def test_indirect_costs(self): def _get_result( indirect_cost_percent: Optional[int] = None, From 95b850fd42036f4f48df1863f7bf8a308827c3d3 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 31 Jul 2025 08:37:58 -0700 Subject: [PATCH 07/13] WIP - initial impl - examples not yet updated - FIXME, LCOH calculation broken somewhere --- src/geophires_x/Economics.py | 99 ++++++++++++++++++++++++------------ src/geophires_x/Outputs.py | 8 ++- 2 files changed, 73 insertions(+), 34 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index d7ad098e..f389f65a 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -1,5 +1,8 @@ +from __future__ import annotations + import math import sys +# noinspection PyPackageRequirements import numpy as np import numpy_financial as npf from pint.facets.plain import PlainQuantity @@ -349,7 +352,7 @@ def CalculateFinancialPerformance(plantlifetime: int, return NPV, IRR, VIR, MOIC -def CalculateLCOELCOHLCOC(econ, model: Model) -> tuple: +def CalculateLCOELCOHLCOC(econ, model: Model) -> tuple[float, float, float]: """ CalculateLCOELCOH calculates the levelized cost of electricity and heat for the project. :param econ: Economics object @@ -357,21 +360,42 @@ def CalculateLCOELCOHLCOC(econ, model: Model) -> tuple: :param model: The model object :type model: :class:`~geophires_x.Model.Model` :return: LCOE: The levelized cost of electricity and LCOH: The levelized cost of heat and LCOC: The levelized cost of cooling - :rtype: tuple + :rtype: tuple[float, float, float] """ LCOE = LCOH = LCOC = 0.0 CCap_elec = (econ.CCap.value * econ.CAPEX_heat_electricity_plant_ratio.value) Coam_elec = (econ.Coam.value * econ.CAPEX_heat_electricity_plant_ratio.value) CCap_heat = (econ.CCap.value * (1.0 - econ.CAPEX_heat_electricity_plant_ratio.value)) Coam_heat = (econ.Coam.value * (1.0 - econ.CAPEX_heat_electricity_plant_ratio.value)) + + def _capex_total_plus_construction_inflation() -> float: + # TODO unit conversions + # TODO should be return value instead of mutating econ + econ.inflation_cost_during_construction.value = econ.CCap.value * econ.inflrateconstruction.value + + return econ.CCap.value + econ.inflation_cost_during_construction.value + + def _construction_inflation_cost_elec_heat() -> tuple[float, float]: + # TODO unit conversions + construction_inflation_cost_elec = CCap_elec * econ.inflrateconstruction.value + construction_inflation_cost_heat = CCap_heat * econ.inflrateconstruction.value + + # TODO should be return value instead of mutating econ + econ.inflation_cost_during_construction.value = (construction_inflation_cost_elec + + construction_inflation_cost_heat) + + return CCap_elec + construction_inflation_cost_elec, CCap_heat + construction_inflation_cost_heat + # Calculate LCOE/LCOH/LCOC if econ.econmodel.value == EconomicModel.FCR: + capex_total_plus_infl = _capex_total_plus_construction_inflation() + if model.surfaceplant.enduse_option.value == EndUseOptions.ELECTRICITY: - LCOE = (econ.FCR.value * (1 + econ.inflrateconstruction.value) * econ.CCap.value + econ.Coam.value) / \ + LCOE = (econ.FCR.value * capex_total_plus_infl + econ.Coam.value) / \ np.average(model.surfaceplant.NetkWhProduced.value) * 1E8 # cents/kWh elif (model.surfaceplant.enduse_option.value == EndUseOptions.HEAT and model.surfaceplant.plant_type.value not in [PlantType.ABSORPTION_CHILLER, PlantType.HEAT_PUMP, PlantType.DISTRICT_HEATING]): - LCOH = (econ.FCR.value * (1 + econ.inflrateconstruction.value) * econ.CCap.value + econ.Coam.value + + LCOH = (econ.FCR.value * capex_total_plus_infl + econ.Coam.value + econ.averageannualpumpingcosts.value) / np.average( model.surfaceplant.HeatkWhProduced.value) * 1E8 # cents/kWh LCOH = LCOH * 2.931 # $/Million Btu @@ -382,38 +406,40 @@ def CalculateLCOELCOHLCOC(econ, model: Model) -> tuple: EndUseOptions.COGENERATION_BOTTOMING_EXTRA_HEAT, EndUseOptions.COGENERATION_PARALLEL_EXTRA_HEAT, EndUseOptions.COGENERATION_PARALLEL_EXTRA_ELECTRICITY]: - LCOE = (econ.FCR.value * (1 + econ.inflrateconstruction.value) * CCap_elec + Coam_elec) / np.average(model.surfaceplant.NetkWhProduced.value) * 1E8 # cents/kWh - LCOH = (econ.FCR.value * (1 + econ.inflrateconstruction.value) * CCap_heat + Coam_heat + econ.averageannualpumpingcosts.value) / np.average(model.surfaceplant.HeatkWhProduced.value) * 1E8 # cents/kWh + capex_elec_plus_infl, capex_heat_plus_infl = _construction_inflation_cost_elec_heat() + LCOE = (econ.FCR.value * capex_elec_plus_infl + Coam_elec) / np.average(model.surfaceplant.NetkWhProduced.value) * 1E8 # cents/kWh + LCOH = (econ.FCR.value * capex_heat_plus_infl + Coam_heat + econ.averageannualpumpingcosts.value) / np.average(model.surfaceplant.HeatkWhProduced.value) * 1E8 # cents/kWh LCOH = LCOH * 2.931 # $/Million Btu elif model.surfaceplant.enduse_option.value == EndUseOptions.HEAT and model.surfaceplant.plant_type.value == PlantType.ABSORPTION_CHILLER: - LCOC = (econ.FCR.value * ( - 1 + econ.inflrateconstruction.value) * econ.CCap.value + econ.Coam.value + econ.averageannualpumpingcosts.value) / np.average( + LCOC = (econ.FCR.value * capex_total_plus_infl + econ.Coam.value + econ.averageannualpumpingcosts.value) / np.average( model.surfaceplant.cooling_kWh_Produced.value) * 1E8 # cents/kWh LCOC = LCOC * 2.931 # $/Million Btu elif model.surfaceplant.enduse_option.value == EndUseOptions.HEAT and model.surfaceplant.plant_type.value == PlantType.HEAT_PUMP: - LCOH = (econ.FCR.value * ( - 1 + econ.inflrateconstruction.value) * econ.CCap.value + econ.Coam.value + econ.averageannualpumpingcosts.value + econ.averageannualheatpumpelectricitycost.value) / np.average( + LCOH = (econ.FCR.value * capex_total_plus_infl + + econ.Coam.value + econ.averageannualpumpingcosts.value + econ.averageannualheatpumpelectricitycost.value) / np.average( model.surfaceplant.HeatkWhProduced.value) * 1E8 # cents/kWh LCOH = LCOH * 2.931 # $/Million Btu elif model.surfaceplant.enduse_option.value == EndUseOptions.HEAT and model.surfaceplant.plant_type.value == PlantType.DISTRICT_HEATING: - LCOH = (econ.FCR.value * ( - 1 + econ.inflrateconstruction.value) * econ.CCap.value + econ.Coam.value + econ.averageannualpumpingcosts.value + econ.averageannualngcost.value) / model.surfaceplant.annual_heating_demand.value * 1E2 # cents/kWh + LCOH = (econ.FCR.value * capex_total_plus_infl + + econ.Coam.value + econ.averageannualpumpingcosts.value + econ.averageannualngcost.value) / model.surfaceplant.annual_heating_demand.value * 1E2 # cents/kWh LCOH = LCOH * 2.931 # $/Million Btu elif econ.econmodel.value == EconomicModel.STANDARDIZED_LEVELIZED_COST: discount_vector = 1. / np.power(1 + econ.discountrate.value, np.linspace(0, model.surfaceplant.plant_lifetime.value - 1, model.surfaceplant.plant_lifetime.value)) + capex_total_plus_infl = _capex_total_plus_construction_inflation() + if model.surfaceplant.enduse_option.value == EndUseOptions.ELECTRICITY: - LCOE = ((1 + econ.inflrateconstruction.value) * econ.CCap.value + np.sum( + LCOE = (capex_total_plus_infl + np.sum( econ.Coam.value * discount_vector)) / np.sum( model.surfaceplant.NetkWhProduced.value * discount_vector) * 1E8 # cents/kWh elif model.surfaceplant.enduse_option.value == EndUseOptions.HEAT and \ model.surfaceplant.plant_type.value not in [PlantType.ABSORPTION_CHILLER, PlantType.HEAT_PUMP, PlantType.DISTRICT_HEATING]: econ.averageannualpumpingcosts.value = np.average( model.surfaceplant.PumpingkWh.value) * model.surfaceplant.electricity_cost_to_buy.value / 1E6 # M$/year - LCOH = ((1 + econ.inflrateconstruction.value) * econ.CCap.value + np.sum(( - econ.Coam.value + model.surfaceplant.PumpingkWh.value * model.surfaceplant.electricity_cost_to_buy.value / 1E6) * discount_vector)) / np.sum( + LCOH = (capex_total_plus_infl + np.sum(( + econ.Coam.value + model.surfaceplant.PumpingkWh.value * model.surfaceplant.electricity_cost_to_buy.value / 1E6) * discount_vector)) / np.sum( model.surfaceplant.HeatkWhProduced.value * discount_vector) * 1E8 # cents/kWh LCOH = LCOH * 2.931 # $/MMBTU @@ -424,24 +450,30 @@ def CalculateLCOELCOHLCOC(econ, model: Model) -> tuple: EndUseOptions.COGENERATION_BOTTOMING_EXTRA_HEAT, EndUseOptions.COGENERATION_PARALLEL_EXTRA_HEAT, EndUseOptions.COGENERATION_PARALLEL_EXTRA_ELECTRICITY]: - LCOE = ((1 + econ.inflrateconstruction.value) * CCap_elec + np.sum(Coam_elec * discount_vector)) / np.sum(model.surfaceplant.NetkWhProduced.value * discount_vector) * 1E8 # cents/kWh - LCOH = ((1 + econ.inflrateconstruction.value) * CCap_heat + + capex_elec_plus_infl, capex_heat_plus_infl = _construction_inflation_cost_elec_heat() + + LCOE = (capex_elec_plus_infl + np.sum(Coam_elec * discount_vector)) / np.sum(model.surfaceplant.NetkWhProduced.value * discount_vector) * 1E8 # cents/kWh + LCOH = (capex_heat_plus_infl * CCap_heat + np.sum((Coam_heat + model.surfaceplant.PumpingkWh.value * model.surfaceplant.electricity_cost_to_buy.value / 1E6) * discount_vector)) / np.sum(model.surfaceplant.HeatkWhProduced.value * discount_vector) * 1E8 # cents/kWh LCOH = LCOH * 2.931 # $/MMBTU elif model.surfaceplant.enduse_option.value == EndUseOptions.HEAT and model.surfaceplant.plant_type.value == PlantType.ABSORPTION_CHILLER: - LCOC = ((1 + econ.inflrateconstruction.value) * econ.CCap.value + np.sum(( - econ.Coam.value + model.surfaceplant.PumpingkWh.value * model.surfaceplant.electricity_cost_to_buy.value / 1E6) * discount_vector)) / np.sum( + capex_total_plus_infl = _capex_total_plus_construction_inflation() + + LCOC = (capex_total_plus_infl + np.sum(( + econ.Coam.value + model.surfaceplant.PumpingkWh.value * model.surfaceplant.electricity_cost_to_buy.value / 1E6) * discount_vector)) / np.sum( model.surfaceplant.cooling_kWh_Produced.value * discount_vector) * 1E8 # cents/kWh LCOC = LCOC * 2.931 # $/Million Btu elif model.surfaceplant.enduse_option.value == EndUseOptions.HEAT and model.surfaceplant.plant_type.value == PlantType.HEAT_PUMP: - LCOH = ((1 + econ.inflrateconstruction.value) * econ.CCap.value + np.sum( + capex_total_plus_infl = _capex_total_plus_construction_inflation() + LCOH = (capex_total_plus_infl + np.sum( (econ.Coam.value + model.surfaceplant.PumpingkWh.value * model.surfaceplant.electricity_cost_to_buy.value / 1E6 + model.surfaceplant.heat_pump_electricity_kwh_used.value * model.surfaceplant.electricity_cost_to_buy.value / 1E6) * discount_vector)) / np.sum( model.surfaceplant.HeatkWhProduced.value * discount_vector) * 1E8 # cents/kWh LCOH = LCOH * 2.931 # $/Million Btu elif model.surfaceplant.enduse_option.value == EndUseOptions.HEAT and model.surfaceplant.plant_type.value == PlantType.DISTRICT_HEATING: - LCOH = ((1 + econ.inflrateconstruction.value) * econ.CCap.value + np.sum( + capex_total_plus_infl = _capex_total_plus_construction_inflation() + LCOH = (capex_total_plus_infl + np.sum( (econ.Coam.value + model.surfaceplant.PumpingkWh.value * model.surfaceplant.electricity_cost_to_buy.value / 1E6 + econ.annualngcost.value) * discount_vector)) / np.sum( model.surfaceplant.annual_heating_demand.value * discount_vector) * 1E2 # cents/kWh @@ -457,10 +489,12 @@ def CalculateLCOELCOHLCOC(econ, model: Model) -> tuple: CRF = i_ave / (1 - np.power(1 + i_ave, -model.surfaceplant.plant_lifetime.value)) inflation_vector = np.power(1 + econ.RINFL.value, np.linspace(1, model.surfaceplant.plant_lifetime.value, model.surfaceplant.plant_lifetime.value)) discount_vector = 1. / np.power(1 + i_ave, np.linspace(1, model.surfaceplant.plant_lifetime.value, model.surfaceplant.plant_lifetime.value)) - NPV_cap = np.sum((1 + econ.inflrateconstruction.value) * econ.CCap.value * CRF * discount_vector) - NPV_fc = np.sum((1 + econ.inflrateconstruction.value) * econ.CCap.value * econ.PTR.value * inflation_vector * discount_vector) - NPV_it = np.sum(econ.CTR.value / (1 - econ.CTR.value) * ((1 + econ.inflrateconstruction.value) * econ.CCap.value * CRF - econ.CCap.value / model.surfaceplant.plant_lifetime.value) * discount_vector) - NPV_itc = (1 + econ.inflrateconstruction.value) * econ.CCap.value * econ.RITC.value / (1 - econ.CTR.value) + capex_total_plus_infl = _capex_total_plus_construction_inflation() + + NPV_cap = np.sum(capex_total_plus_infl * CRF * discount_vector) + NPV_fc = np.sum(capex_total_plus_infl * econ.PTR.value * inflation_vector * discount_vector) + NPV_it = np.sum(econ.CTR.value / (1 - econ.CTR.value) * (capex_total_plus_infl * CRF - econ.CCap.value / model.surfaceplant.plant_lifetime.value) * discount_vector) + NPV_itc = capex_total_plus_infl * econ.RITC.value / (1 - econ.CTR.value) if model.surfaceplant.enduse_option.value == EndUseOptions.ELECTRICITY: NPV_oandm = np.sum(econ.Coam.value * inflation_vector * discount_vector) @@ -479,21 +513,22 @@ def CalculateLCOELCOHLCOC(econ, model: Model) -> tuple: EndUseOptions.COGENERATION_BOTTOMING_EXTRA_HEAT, EndUseOptions.COGENERATION_PARALLEL_EXTRA_HEAT, EndUseOptions.COGENERATION_PARALLEL_EXTRA_ELECTRICITY]: + capex_elec_plus_infl, capex_heat_plus_infl = _construction_inflation_cost_elec_heat() - NPVcap_elec = np.sum((1 + econ.inflrateconstruction.value) * CCap_elec * CRF * discount_vector) - NPVfc_elec = np.sum((1 + econ.inflrateconstruction.value) * CCap_elec * econ.PTR.value * inflation_vector * discount_vector) - NPVit_elec = np.sum(econ.CTR.value / (1 - econ.CTR.value) * ((1 + econ.inflrateconstruction.value) * CCap_elec * CRF - CCap_elec / model.surfaceplant.plant_lifetime.value) * discount_vector) - NPVitc_elec = (1 + econ.inflrateconstruction.value) * CCap_elec * econ.RITC.value / (1 - econ.CTR.value) + NPVcap_elec = np.sum(capex_elec_plus_infl * CRF * discount_vector) + NPVfc_elec = np.sum(capex_elec_plus_infl * econ.PTR.value * inflation_vector * discount_vector) + NPVit_elec = np.sum(econ.CTR.value / (1 - econ.CTR.value) * (capex_elec_plus_infl * CRF - CCap_elec / model.surfaceplant.plant_lifetime.value) * discount_vector) + NPVitc_elec = capex_elec_plus_infl * econ.RITC.value / (1 - econ.CTR.value) NPVoandm_elec = np.sum(Coam_elec * inflation_vector * discount_vector) NPVgrt_elec = econ.GTR.value / (1 - econ.GTR.value) * (NPVcap_elec + NPVoandm_elec + NPVfc_elec + NPVit_elec - NPVitc_elec) LCOE = ((NPVcap_elec + NPVoandm_elec + NPVfc_elec + NPVit_elec + NPVgrt_elec - NPVitc_elec) / np.sum(model.surfaceplant.NetkWhProduced.value * inflation_vector * discount_vector) * 1E8) - NPV_cap_heat = np.sum((1 + econ.inflrateconstruction.value) * CCap_heat * CRF * discount_vector) + NPV_cap_heat = np.sum(capex_heat_plus_infl * CRF * discount_vector) NPV_fc_heat = np.sum((1 + econ.inflrateconstruction.value) * (econ.CCap.value * (1.0 - econ.CAPEX_heat_electricity_plant_ratio.value)) * econ.PTR.value * inflation_vector * discount_vector) - NPV_it_heat = np.sum(econ.CTR.value / (1 - econ.CTR.value) * ((1 + econ.inflrateconstruction.value) * CCap_heat * CRF - CCap_heat / model.surfaceplant.plant_lifetime.value) * discount_vector) - NPV_itc_heat = (1 + econ.inflrateconstruction.value) * CCap_heat * econ.RITC.value / (1 - econ.CTR.value) + NPV_it_heat = np.sum(econ.CTR.value / (1 - econ.CTR.value) * (capex_heat_plus_infl * CRF - CCap_heat / model.surfaceplant.plant_lifetime.value) * discount_vector) + NPV_itc_heat = capex_heat_plus_infl * econ.RITC.value / (1 - econ.CTR.value) NPV_oandm_heat = np.sum((econ.Coam.value * (1.0 - econ.CAPEX_heat_electricity_plant_ratio.value)) * inflation_vector * discount_vector) NPV_grt_heat = econ.GTR.value / (1 - econ.GTR.value) * (NPV_cap_heat + NPV_oandm_heat + NPV_fc_heat + NPV_it_heat - NPV_itc_heat) diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index dd0b6857..41c2536d 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -497,8 +497,12 @@ def PrintOutputs(self, model: Model): # expenditure. pass - if is_sam_econ_model: - # TODO calculate & display for other economic models + display_inflation_during_construction_in_capital_costs = is_sam_econ_model \ + or (econ.econmodel.value in [EconomicModel.BICYCLE, EconomicModel.FCR, + EconomicModel.STANDARDIZED_LEVELIZED_COST] + and + econ.inflation_cost_during_construction.value != 0.) + if display_inflation_during_construction_in_capital_costs: icc_label = Outputs._field_label(econ.inflation_cost_during_construction.display_name, 47) f.write(f' {icc_label}{econ.inflation_cost_during_construction.value:10.2f} {econ.inflation_cost_during_construction.CurrentUnits.value}\n') From b44fda1ef0c2de07d23a91ac1ec43914c1a354d1 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 31 Jul 2025 08:42:15 -0700 Subject: [PATCH 08/13] Update examples that correctly have inflation costs during construction added to Capital Costs - TODO to display this in ECONOMIC PARAMETERS instead --- tests/examples/Fervo_Norbeck_Latimer_2023.out | 11 ++++++----- tests/examples/Fervo_Project_Cape-2.out | 11 ++++++----- tests/examples/Fervo_Project_Cape-3.out | 11 ++++++----- tests/examples/Fervo_Project_Cape.out | 11 ++++++----- tests/examples/example10_HP.out | 13 +++++++------ tests/examples/example11_AC.out | 13 +++++++------ tests/examples/example3.out | 13 +++++++------ 7 files changed, 45 insertions(+), 38 deletions(-) diff --git a/tests/examples/Fervo_Norbeck_Latimer_2023.out b/tests/examples/Fervo_Norbeck_Latimer_2023.out index f133e97a..4388a9b7 100644 --- a/tests/examples/Fervo_Norbeck_Latimer_2023.out +++ b/tests/examples/Fervo_Norbeck_Latimer_2023.out @@ -4,10 +4,10 @@ Simulation Metadata ---------------------- - GEOPHIRES Version: 3.9.28 - Simulation Date: 2025-07-02 - Simulation Time: 12:19 - Calculation Time: 0.481 sec + GEOPHIRES Version: 3.9.47 + Simulation Date: 2025-07-31 + Simulation Time: 08:38 + Calculation Time: 0.476 sec ***SUMMARY OF RESULTS*** @@ -24,7 +24,7 @@ Simulation Metadata ***ECONOMIC PARAMETERS*** Economic Model = BICYCLE - Accrued financing during construction: 5.00 % + Accrued financing during construction: 5.00 % Project lifetime: 10 yr Capacity factor: 90.0 % Project NPV: -13.03 MUSD @@ -103,6 +103,7 @@ Simulation Metadata Field gathering system costs: 1.52 MUSD Total surface equipment costs: 12.85 MUSD Exploration costs: 3.62 MUSD + Inflation costs during construction: 1.41 MUSD Total capital costs: 28.12 MUSD diff --git a/tests/examples/Fervo_Project_Cape-2.out b/tests/examples/Fervo_Project_Cape-2.out index f4debd0d..5c358377 100644 --- a/tests/examples/Fervo_Project_Cape-2.out +++ b/tests/examples/Fervo_Project_Cape-2.out @@ -4,10 +4,10 @@ Simulation Metadata ---------------------- - GEOPHIRES Version: 3.9.28 - Simulation Date: 2025-07-02 - Simulation Time: 12:19 - Calculation Time: 0.722 sec + GEOPHIRES Version: 3.9.47 + Simulation Date: 2025-07-31 + Simulation Time: 08:38 + Calculation Time: 0.707 sec ***SUMMARY OF RESULTS*** @@ -24,7 +24,7 @@ Simulation Metadata ***ECONOMIC PARAMETERS*** Economic Model = BICYCLE - Accrued financing during construction: 5.00 % + Accrued financing during construction: 5.00 % Project lifetime: 15 yr Capacity factor: 90.0 % Project NPV: 42.34 MUSD @@ -101,6 +101,7 @@ Simulation Metadata Field gathering system costs: 1.68 MUSD Total surface equipment costs: 26.36 MUSD Exploration costs: 0.00 MUSD + Inflation costs during construction: 2.54 MUSD Total capital costs: 50.76 MUSD diff --git a/tests/examples/Fervo_Project_Cape-3.out b/tests/examples/Fervo_Project_Cape-3.out index 93f1c996..93cade9e 100644 --- a/tests/examples/Fervo_Project_Cape-3.out +++ b/tests/examples/Fervo_Project_Cape-3.out @@ -4,10 +4,10 @@ Simulation Metadata ---------------------- - GEOPHIRES Version: 3.9.28 - Simulation Date: 2025-07-02 - Simulation Time: 12:19 - Calculation Time: 0.956 sec + GEOPHIRES Version: 3.9.47 + Simulation Date: 2025-07-31 + Simulation Time: 08:38 + Calculation Time: 0.940 sec ***SUMMARY OF RESULTS*** @@ -24,7 +24,7 @@ Simulation Metadata ***ECONOMIC PARAMETERS*** Economic Model = BICYCLE - Accrued financing during construction: 5.00 % + Accrued financing during construction: 5.00 % Project lifetime: 20 yr Capacity factor: 90.0 % Project NPV: 4580.36 MUSD @@ -102,6 +102,7 @@ Simulation Metadata Total surface equipment costs: 969.26 MUSD Exploration costs: 30.00 MUSD Investment Tax Credit: -459.83 MUSD + Inflation costs during construction: 53.65 MUSD Total capital costs: 1072.95 MUSD diff --git a/tests/examples/Fervo_Project_Cape.out b/tests/examples/Fervo_Project_Cape.out index af4fa2e9..a4c3747f 100644 --- a/tests/examples/Fervo_Project_Cape.out +++ b/tests/examples/Fervo_Project_Cape.out @@ -4,10 +4,10 @@ Simulation Metadata ---------------------- - GEOPHIRES Version: 3.9.28 - Simulation Date: 2025-07-02 - Simulation Time: 12:19 - Calculation Time: 0.720 sec + GEOPHIRES Version: 3.9.47 + Simulation Date: 2025-07-31 + Simulation Time: 08:38 + Calculation Time: 0.707 sec ***SUMMARY OF RESULTS*** @@ -24,7 +24,7 @@ Simulation Metadata ***ECONOMIC PARAMETERS*** Economic Model = BICYCLE - Accrued financing during construction: 5.00 % + Accrued financing during construction: 5.00 % Project lifetime: 15 yr Capacity factor: 90.0 % Project NPV: 520.01 MUSD @@ -101,6 +101,7 @@ Simulation Metadata Field gathering system costs: 23.00 MUSD Total surface equipment costs: 357.63 MUSD Exploration costs: 0.00 MUSD + Inflation costs during construction: 24.14 MUSD Total capital costs: 482.83 MUSD diff --git a/tests/examples/example10_HP.out b/tests/examples/example10_HP.out index 51991cce..5107f2db 100644 --- a/tests/examples/example10_HP.out +++ b/tests/examples/example10_HP.out @@ -4,10 +4,10 @@ Simulation Metadata ---------------------- - GEOPHIRES Version: 3.9.7 - Simulation Date: 2025-05-15 - Simulation Time: 10:13 - Calculation Time: 0.096 sec + GEOPHIRES Version: 3.9.47 + Simulation Date: 2025-07-31 + Simulation Time: 08:38 + Calculation Time: 0.105 sec ***SUMMARY OF RESULTS*** @@ -26,7 +26,7 @@ Simulation Metadata Economic Model = Standard Levelized Cost Interest Rate: 5.00 % - Accrued financing during construction: 5.00 % + Accrued financing during construction: 5.00 % Project lifetime: 30 yr Capacity factor: 90.0 % Project NPV: 15.18 MUSD @@ -64,7 +64,7 @@ Simulation Metadata m/A Drawdown Parameter: 0.00002 1/year Bottom-hole temperature: 109.50 degC Reservoir volume calculated with fracture separation and number of fractures as input - Number of fractures: 12.00 + Number of fractures: 12 Fracture separation: 80.00 meter Reservoir volume: 176000000 m**3 Reservoir hydrostatic pressure: 20488.96 kPa @@ -99,6 +99,7 @@ Simulation Metadata Field gathering system costs: 2.55 MUSD Total surface equipment costs: 10.33 MUSD Exploration costs: 3.32 MUSD + Inflation costs during construction: 1.39 MUSD Total capital costs: 27.72 MUSD diff --git a/tests/examples/example11_AC.out b/tests/examples/example11_AC.out index 5cff8880..37915dba 100644 --- a/tests/examples/example11_AC.out +++ b/tests/examples/example11_AC.out @@ -4,10 +4,10 @@ Simulation Metadata ---------------------- - GEOPHIRES Version: 3.9.7 - Simulation Date: 2025-05-15 - Simulation Time: 10:13 - Calculation Time: 0.096 sec + GEOPHIRES Version: 3.9.47 + Simulation Date: 2025-07-31 + Simulation Time: 08:38 + Calculation Time: 0.107 sec ***SUMMARY OF RESULTS*** @@ -27,7 +27,7 @@ Simulation Metadata Economic Model = Standard Levelized Cost Interest Rate: 5.00 % - Accrued financing during construction: 5.00 % + Accrued financing during construction: 5.00 % Project lifetime: 30 yr Capacity factor: 90.0 % Project NPV: 10.11 MUSD @@ -65,7 +65,7 @@ Simulation Metadata m/A Drawdown Parameter: 0.00002 1/year Bottom-hole temperature: 109.50 degC Reservoir volume calculated with fracture separation and number of fractures as input - Number of fractures: 12.00 + Number of fractures: 12 Fracture separation: 80.00 meter Reservoir volume: 176000000 m**3 Reservoir hydrostatic pressure: 20488.96 kPa @@ -100,6 +100,7 @@ Simulation Metadata Field gathering system costs: 2.34 MUSD Total surface equipment costs: 8.96 MUSD Exploration costs: 3.32 MUSD + Inflation costs during construction: 1.32 MUSD Total capital costs: 26.35 MUSD diff --git a/tests/examples/example3.out b/tests/examples/example3.out index 01516178..b0f5a3c8 100644 --- a/tests/examples/example3.out +++ b/tests/examples/example3.out @@ -4,10 +4,10 @@ Simulation Metadata ---------------------- - GEOPHIRES Version: 3.9.7 - Simulation Date: 2025-05-15 - Simulation Time: 10:12 - Calculation Time: 0.113 sec + GEOPHIRES Version: 3.9.47 + Simulation Date: 2025-07-31 + Simulation Time: 08:38 + Calculation Time: 0.123 sec ***SUMMARY OF RESULTS*** @@ -26,7 +26,7 @@ Simulation Metadata ***ECONOMIC PARAMETERS*** Economic Model = BICYCLE - Accrued financing during construction: 5.00 % + Accrued financing during construction: 5.00 % Project lifetime: 35 yr Capacity factor: 90.0 % Project NPV: -2.38 MUSD @@ -66,7 +66,7 @@ Simulation Metadata m/A Drawdown Parameter: 0.00002 1/year Bottom-hole temperature: 232.00 degC Reservoir volume calculated with fracture separation and number of fractures as input - Number of fractures: 12.00 + Number of fractures: 12 Fracture separation: 80.00 meter Reservoir volume: 176000000 m**3 Reservoir hydrostatic pressure: 29019.48 kPa @@ -97,6 +97,7 @@ Simulation Metadata Field gathering system costs: 3.17 MUSD Total surface equipment costs: 67.22 MUSD Exploration costs: 4.64 MUSD + Inflation costs during construction: 5.18 MUSD Total capital costs: 103.68 MUSD From 02058cc2e29330f2b0cdb2183fd74f0fc856198b Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 31 Jul 2025 08:45:06 -0700 Subject: [PATCH 09/13] fix impl error introduced in 95b850fd42036f4f48df1863f7bf8a308827c3d3 that was affecting example13 (regenerated with no change in results) --- src/geophires_x/Economics.py | 2 +- tests/examples/example13.out | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index f389f65a..181a1677 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -453,7 +453,7 @@ def _construction_inflation_cost_elec_heat() -> tuple[float, float]: capex_elec_plus_infl, capex_heat_plus_infl = _construction_inflation_cost_elec_heat() LCOE = (capex_elec_plus_infl + np.sum(Coam_elec * discount_vector)) / np.sum(model.surfaceplant.NetkWhProduced.value * discount_vector) * 1E8 # cents/kWh - LCOH = (capex_heat_plus_infl * CCap_heat + + LCOH = (capex_heat_plus_infl + np.sum((Coam_heat + model.surfaceplant.PumpingkWh.value * model.surfaceplant.electricity_cost_to_buy.value / 1E6) * discount_vector)) / np.sum(model.surfaceplant.HeatkWhProduced.value * discount_vector) * 1E8 # cents/kWh LCOH = LCOH * 2.931 # $/MMBTU diff --git a/tests/examples/example13.out b/tests/examples/example13.out index 006909f4..7b881308 100644 --- a/tests/examples/example13.out +++ b/tests/examples/example13.out @@ -4,10 +4,10 @@ Simulation Metadata ---------------------- - GEOPHIRES Version: 3.9.36 - Simulation Date: 2025-07-25 - Simulation Time: 11:30 - Calculation Time: 0.037 sec + GEOPHIRES Version: 3.9.47 + Simulation Date: 2025-07-31 + Simulation Time: 08:44 + Calculation Time: 0.036 sec ***SUMMARY OF RESULTS*** @@ -27,7 +27,7 @@ Simulation Metadata Economic Model = Standard Levelized Cost Interest Rate: 5.00 % - Accrued financing during construction: 0.00 % + Accrued financing during construction: 0.00 % Project lifetime: 30 yr Capacity factor: 80.0 % Project NPV: -31.42 MUSD From a3cd9d27ca53278559bf36a682e1d92fc153358c Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 31 Jul 2025 09:00:19 -0700 Subject: [PATCH 10/13] Output Inflation costs during construction in ECONOMIC PARAMETERS after Accrued financing during construction for economic models that don't treat inflation costs as capital costs (non-SAM-EM) --- src/geophires_x/EconomicsUtils.py | 8 ++++---- src/geophires_x/Outputs.py | 20 ++++++++++++++----- src/geophires_x_client/geophires_x_result.py | 3 +++ .../geophires-result.json | 9 +++++++-- tests/examples/Fervo_Norbeck_Latimer_2023.out | 6 +++--- tests/examples/Fervo_Project_Cape-2.out | 6 +++--- tests/examples/Fervo_Project_Cape-3.out | 6 +++--- tests/examples/Fervo_Project_Cape.out | 6 +++--- tests/examples/example10_HP.out | 4 ++-- tests/examples/example11_AC.out | 4 ++-- tests/examples/example3.out | 6 +++--- 11 files changed, 48 insertions(+), 30 deletions(-) diff --git a/src/geophires_x/EconomicsUtils.py b/src/geophires_x/EconomicsUtils.py index 71dd8c62..1f586a14 100644 --- a/src/geophires_x/EconomicsUtils.py +++ b/src/geophires_x/EconomicsUtils.py @@ -139,8 +139,8 @@ def total_capex_parameter_output_parameter() -> OutputParameter: UnitType=Units.CURRENCY, CurrentUnits=CurrencyUnit.MDOLLARS, PreferredUnits=CurrencyUnit.MDOLLARS, - ToolTipText="The total capital expenditure (CAPEX) required to construct the plant. " - "This value includes all direct and indirect costs, contingency, and any cost escalation from " - "inflation during construction. It is used as the total installed cost input for " - "SAM Economic Models." + ToolTipText='The total capital expenditure (CAPEX) required to construct the plant. ' + 'This value includes all direct and indirect costs, and contingency. ' + 'For SAM Economic models, it also includes any cost escalation from inflation during construction. ' + 'It is used as the total installed cost input for SAM Economic Models.' ) diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index 41c2536d..26c082fa 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -274,6 +274,20 @@ def PrintOutputs(self, model: Model): acf_label = Outputs._field_label(acf.display_name, 49) f.write(f' {acf_label}{acf.value:10.2f} {acf.CurrentUnits.value}\n') + display_inflation_costs_in_economic_parameters: bool = ( + econ.econmodel.value in [EconomicModel.BICYCLE, + EconomicModel.FCR, + EconomicModel.STANDARDIZED_LEVELIZED_COST] + and + econ.inflation_cost_during_construction.value != 0. + ) + if display_inflation_costs_in_economic_parameters: + # Inflation cost is displayed here for economic models that don't treat inflation cost as a + # capital cost + icc: OutputParameter = econ.inflation_cost_during_construction + icc_label = Outputs._field_label(icc.display_name, 49) + f.write(f' {icc_label}{icc.value:10.2f} {icc.CurrentUnits.value}\n') + f.write(f' Project lifetime: {model.surfaceplant.plant_lifetime.value:10.0f} {model.surfaceplant.plant_lifetime.CurrentUnits.value}\n') f.write(f' Capacity factor: {model.surfaceplant.utilization_factor.value * 100:10.1f} %\n') @@ -497,11 +511,7 @@ def PrintOutputs(self, model: Model): # expenditure. pass - display_inflation_during_construction_in_capital_costs = is_sam_econ_model \ - or (econ.econmodel.value in [EconomicModel.BICYCLE, EconomicModel.FCR, - EconomicModel.STANDARDIZED_LEVELIZED_COST] - and - econ.inflation_cost_during_construction.value != 0.) + display_inflation_during_construction_in_capital_costs = is_sam_econ_model if display_inflation_during_construction_in_capital_costs: icc_label = Outputs._field_label(econ.inflation_cost_during_construction.display_name, 47) f.write(f' {icc_label}{econ.inflation_cost_during_construction.value:10.2f} {econ.inflation_cost_during_construction.CurrentUnits.value}\n') diff --git a/src/geophires_x_client/geophires_x_result.py b/src/geophires_x_client/geophires_x_result.py index c44e3b2f..e2ad9029 100644 --- a/src/geophires_x_client/geophires_x_result.py +++ b/src/geophires_x_client/geophires_x_result.py @@ -79,6 +79,8 @@ class GeophiresXResult: 'Nominal Discount Rate', 'WACC', 'Accrued financing during construction', + # Displayed for economic models that don't treat inflation costs as capital costs (non-SAM-EM) + 'Inflation costs during construction', 'Project lifetime', 'Capacity factor', 'Project NPV', @@ -261,6 +263,7 @@ class GeophiresXResult: 'Total surface equipment costs', 'Exploration costs', 'Investment Tax Credit', + # Displayed for economic models that treat inflation costs as capital costs (SAM-EM) 'Inflation costs during construction', 'Total Add-on CAPEX', 'Total capital costs', diff --git a/src/geophires_x_schema_generator/geophires-result.json b/src/geophires_x_schema_generator/geophires-result.json index 00cab4c7..fa345748 100644 --- a/src/geophires_x_schema_generator/geophires-result.json +++ b/src/geophires_x_schema_generator/geophires-result.json @@ -20,7 +20,7 @@ }, "Total CAPEX": { "type": "number", - "description": "The total capital expenditure (CAPEX) required to construct the plant. This value includes all direct and indirect costs, contingency, and any cost escalation from inflation during construction. It is used as the total installed cost input for SAM Economic Models.", + "description": "The total capital expenditure (CAPEX) required to construct the plant. This value includes all direct and indirect costs, and contingency. For SAM Economic models, it also includes any cost escalation from inflation during construction. It is used as the total installed cost input for SAM Economic Models.", "units": "MUSD" }, "Average Direct-Use Heat Production": {}, @@ -100,6 +100,11 @@ "description": "The accrued inflation on total capital costs over the construction period, as defined by Inflation Rate During Construction. For SAM Economic Models, this is calculated automatically by compounding Inflation Rate over Construction Years if Inflation Rate During Construction is not provided.", "units": "%" }, + "Inflation costs during construction": { + "type": "number", + "description": "The calculated amount of cost escalation due to inflation over the construction period.", + "units": "MUSD" + }, "Project lifetime": {}, "Capacity factor": {}, "Project NPV": { @@ -432,7 +437,7 @@ "Annualized capital costs": {}, "Total CAPEX": { "type": "number", - "description": "The total capital expenditure (CAPEX) required to construct the plant. This value includes all direct and indirect costs, contingency, and any cost escalation from inflation during construction. It is used as the total installed cost input for SAM Economic Models.", + "description": "The total capital expenditure (CAPEX) required to construct the plant. This value includes all direct and indirect costs, and contingency. For SAM Economic models, it also includes any cost escalation from inflation during construction. It is used as the total installed cost input for SAM Economic Models.", "units": "MUSD" }, "Drilling Cost": {}, diff --git a/tests/examples/Fervo_Norbeck_Latimer_2023.out b/tests/examples/Fervo_Norbeck_Latimer_2023.out index 4388a9b7..f8d443af 100644 --- a/tests/examples/Fervo_Norbeck_Latimer_2023.out +++ b/tests/examples/Fervo_Norbeck_Latimer_2023.out @@ -6,8 +6,8 @@ Simulation Metadata ---------------------- GEOPHIRES Version: 3.9.47 Simulation Date: 2025-07-31 - Simulation Time: 08:38 - Calculation Time: 0.476 sec + Simulation Time: 08:57 + Calculation Time: 0.475 sec ***SUMMARY OF RESULTS*** @@ -25,6 +25,7 @@ Simulation Metadata Economic Model = BICYCLE Accrued financing during construction: 5.00 % + Inflation costs during construction: 1.41 MUSD Project lifetime: 10 yr Capacity factor: 90.0 % Project NPV: -13.03 MUSD @@ -103,7 +104,6 @@ Simulation Metadata Field gathering system costs: 1.52 MUSD Total surface equipment costs: 12.85 MUSD Exploration costs: 3.62 MUSD - Inflation costs during construction: 1.41 MUSD Total capital costs: 28.12 MUSD diff --git a/tests/examples/Fervo_Project_Cape-2.out b/tests/examples/Fervo_Project_Cape-2.out index 5c358377..b58acb30 100644 --- a/tests/examples/Fervo_Project_Cape-2.out +++ b/tests/examples/Fervo_Project_Cape-2.out @@ -6,8 +6,8 @@ Simulation Metadata ---------------------- GEOPHIRES Version: 3.9.47 Simulation Date: 2025-07-31 - Simulation Time: 08:38 - Calculation Time: 0.707 sec + Simulation Time: 08:57 + Calculation Time: 0.709 sec ***SUMMARY OF RESULTS*** @@ -25,6 +25,7 @@ Simulation Metadata Economic Model = BICYCLE Accrued financing during construction: 5.00 % + Inflation costs during construction: 2.54 MUSD Project lifetime: 15 yr Capacity factor: 90.0 % Project NPV: 42.34 MUSD @@ -101,7 +102,6 @@ Simulation Metadata Field gathering system costs: 1.68 MUSD Total surface equipment costs: 26.36 MUSD Exploration costs: 0.00 MUSD - Inflation costs during construction: 2.54 MUSD Total capital costs: 50.76 MUSD diff --git a/tests/examples/Fervo_Project_Cape-3.out b/tests/examples/Fervo_Project_Cape-3.out index 93cade9e..1d91ce0e 100644 --- a/tests/examples/Fervo_Project_Cape-3.out +++ b/tests/examples/Fervo_Project_Cape-3.out @@ -6,8 +6,8 @@ Simulation Metadata ---------------------- GEOPHIRES Version: 3.9.47 Simulation Date: 2025-07-31 - Simulation Time: 08:38 - Calculation Time: 0.940 sec + Simulation Time: 08:57 + Calculation Time: 0.951 sec ***SUMMARY OF RESULTS*** @@ -25,6 +25,7 @@ Simulation Metadata Economic Model = BICYCLE Accrued financing during construction: 5.00 % + Inflation costs during construction: 53.65 MUSD Project lifetime: 20 yr Capacity factor: 90.0 % Project NPV: 4580.36 MUSD @@ -102,7 +103,6 @@ Simulation Metadata Total surface equipment costs: 969.26 MUSD Exploration costs: 30.00 MUSD Investment Tax Credit: -459.83 MUSD - Inflation costs during construction: 53.65 MUSD Total capital costs: 1072.95 MUSD diff --git a/tests/examples/Fervo_Project_Cape.out b/tests/examples/Fervo_Project_Cape.out index a4c3747f..ec3f23f1 100644 --- a/tests/examples/Fervo_Project_Cape.out +++ b/tests/examples/Fervo_Project_Cape.out @@ -6,8 +6,8 @@ Simulation Metadata ---------------------- GEOPHIRES Version: 3.9.47 Simulation Date: 2025-07-31 - Simulation Time: 08:38 - Calculation Time: 0.707 sec + Simulation Time: 08:57 + Calculation Time: 0.712 sec ***SUMMARY OF RESULTS*** @@ -25,6 +25,7 @@ Simulation Metadata Economic Model = BICYCLE Accrued financing during construction: 5.00 % + Inflation costs during construction: 24.14 MUSD Project lifetime: 15 yr Capacity factor: 90.0 % Project NPV: 520.01 MUSD @@ -101,7 +102,6 @@ Simulation Metadata Field gathering system costs: 23.00 MUSD Total surface equipment costs: 357.63 MUSD Exploration costs: 0.00 MUSD - Inflation costs during construction: 24.14 MUSD Total capital costs: 482.83 MUSD diff --git a/tests/examples/example10_HP.out b/tests/examples/example10_HP.out index 5107f2db..95394cca 100644 --- a/tests/examples/example10_HP.out +++ b/tests/examples/example10_HP.out @@ -6,7 +6,7 @@ Simulation Metadata ---------------------- GEOPHIRES Version: 3.9.47 Simulation Date: 2025-07-31 - Simulation Time: 08:38 + Simulation Time: 08:57 Calculation Time: 0.105 sec ***SUMMARY OF RESULTS*** @@ -27,6 +27,7 @@ Simulation Metadata Economic Model = Standard Levelized Cost Interest Rate: 5.00 % Accrued financing during construction: 5.00 % + Inflation costs during construction: 1.39 MUSD Project lifetime: 30 yr Capacity factor: 90.0 % Project NPV: 15.18 MUSD @@ -99,7 +100,6 @@ Simulation Metadata Field gathering system costs: 2.55 MUSD Total surface equipment costs: 10.33 MUSD Exploration costs: 3.32 MUSD - Inflation costs during construction: 1.39 MUSD Total capital costs: 27.72 MUSD diff --git a/tests/examples/example11_AC.out b/tests/examples/example11_AC.out index 37915dba..cb655659 100644 --- a/tests/examples/example11_AC.out +++ b/tests/examples/example11_AC.out @@ -6,7 +6,7 @@ Simulation Metadata ---------------------- GEOPHIRES Version: 3.9.47 Simulation Date: 2025-07-31 - Simulation Time: 08:38 + Simulation Time: 08:57 Calculation Time: 0.107 sec ***SUMMARY OF RESULTS*** @@ -28,6 +28,7 @@ Simulation Metadata Economic Model = Standard Levelized Cost Interest Rate: 5.00 % Accrued financing during construction: 5.00 % + Inflation costs during construction: 1.32 MUSD Project lifetime: 30 yr Capacity factor: 90.0 % Project NPV: 10.11 MUSD @@ -100,7 +101,6 @@ Simulation Metadata Field gathering system costs: 2.34 MUSD Total surface equipment costs: 8.96 MUSD Exploration costs: 3.32 MUSD - Inflation costs during construction: 1.32 MUSD Total capital costs: 26.35 MUSD diff --git a/tests/examples/example3.out b/tests/examples/example3.out index b0f5a3c8..9d4c402a 100644 --- a/tests/examples/example3.out +++ b/tests/examples/example3.out @@ -6,8 +6,8 @@ Simulation Metadata ---------------------- GEOPHIRES Version: 3.9.47 Simulation Date: 2025-07-31 - Simulation Time: 08:38 - Calculation Time: 0.123 sec + Simulation Time: 08:57 + Calculation Time: 0.124 sec ***SUMMARY OF RESULTS*** @@ -27,6 +27,7 @@ Simulation Metadata Economic Model = BICYCLE Accrued financing during construction: 5.00 % + Inflation costs during construction: 5.18 MUSD Project lifetime: 35 yr Capacity factor: 90.0 % Project NPV: -2.38 MUSD @@ -97,7 +98,6 @@ Simulation Metadata Field gathering system costs: 3.17 MUSD Total surface equipment costs: 67.22 MUSD Exploration costs: 4.64 MUSD - Inflation costs during construction: 5.18 MUSD Total capital costs: 103.68 MUSD From 20931d635318c7a1a38cf484fc0b97046b7abf86 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 31 Jul 2025 12:11:36 -0700 Subject: [PATCH 11/13] py38 compatibility - test_production_well_stimulation_cost/test_geophires_x.py --- tests/test_geophires_x.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/test_geophires_x.py b/tests/test_geophires_x.py index 1e2c5607..0ac9abf1 100644 --- a/tests/test_geophires_x.py +++ b/tests/test_geophires_x.py @@ -1,10 +1,11 @@ +from __future__ import annotations + import math import os import tempfile import uuid from pathlib import Path from typing import Any -from typing import Optional from geophires_x.OptionList import PlantType from geophires_x.OptionList import WellDrillingCostCorrelation @@ -270,7 +271,7 @@ def _sanitize_nan(self, r: GeophiresXResult) -> None: except TypeError: pass - def _get_unequal_dicts_approximate_percent_difference(self, d1: dict, d2: dict) -> Optional[float]: + def _get_unequal_dicts_approximate_percent_difference(self, d1: dict, d2: dict) -> float | None: for i in range(99): try: self.assertDictAlmostEqual(d1, d2, percent=i) @@ -967,9 +968,9 @@ def test_sbt_coaxial_raises_error(self): def test_production_well_stimulation_cost(self): def _get_result( - prod_well_stim_MUSD: Optional[int] = None, - inj_well_stim_MUSD: Optional[int] = None, - additional_params: Optional[dict[str, Any]] = None, + prod_well_stim_MUSD: int | None = None, + inj_well_stim_MUSD: int | None = None, + additional_params: dict[str, Any] | None = None, ) -> GeophiresXResult: if additional_params is None: additional_params = {} @@ -1030,9 +1031,9 @@ def _get_result( def test_indirect_costs(self): def _get_result( - indirect_cost_percent: Optional[int] = None, - stimulation_indirect_cost_percent: Optional[int] = None, - wellfield_indirect_cost_percent: Optional[int] = None, + indirect_cost_percent: int | None = None, + stimulation_indirect_cost_percent: int | None = None, + wellfield_indirect_cost_percent: int | None = None, input_file_path: str = 'geophires_x_tests/generic-egs-case.txt', ) -> float: p = {} @@ -1130,7 +1131,7 @@ def wellfield_cost(result_cap_costs): def test_contingency(self): def _get_result( - contingency_percentage: Optional[int] = None, + contingency_percentage: int | None = None, input_file_path: str = 'geophires_x_tests/generic-egs-case.txt', ) -> float: p = {} From 023a7f069cd7174121f00841d48dd99578e438f0 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 1 Aug 2025 07:45:17 -0700 Subject: [PATCH 12/13] Address unit conversion TODO --- src/geophires_x/Economics.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 181a1677..b89cb1fa 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -369,20 +369,23 @@ def CalculateLCOELCOHLCOC(econ, model: Model) -> tuple[float, float, float]: Coam_heat = (econ.Coam.value * (1.0 - econ.CAPEX_heat_electricity_plant_ratio.value)) def _capex_total_plus_construction_inflation() -> float: - # TODO unit conversions # TODO should be return value instead of mutating econ - econ.inflation_cost_during_construction.value = econ.CCap.value * econ.inflrateconstruction.value + econ.inflation_cost_during_construction.value = quantity( + econ.CCap.value * econ.inflrateconstruction.value, + econ.CCap.CurrentUnits + ).to(econ.inflation_cost_during_construction.CurrentUnits).magnitude return econ.CCap.value + econ.inflation_cost_during_construction.value def _construction_inflation_cost_elec_heat() -> tuple[float, float]: - # TODO unit conversions construction_inflation_cost_elec = CCap_elec * econ.inflrateconstruction.value construction_inflation_cost_heat = CCap_heat * econ.inflrateconstruction.value # TODO should be return value instead of mutating econ - econ.inflation_cost_during_construction.value = (construction_inflation_cost_elec - + construction_inflation_cost_heat) + econ.inflation_cost_during_construction.value = quantity( + construction_inflation_cost_elec+ construction_inflation_cost_heat, + econ.CCap.CurrentUnits + ).to(econ.inflation_cost_during_construction.CurrentUnits).magnitude return CCap_elec + construction_inflation_cost_elec, CCap_heat + construction_inflation_cost_heat From 4413ffaffa73ab4accb862cc149c06fdb5a0f5ab Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 1 Aug 2025 08:20:08 -0700 Subject: [PATCH 13/13] =?UTF-8?q?Bump=20version:=203.9.47=20=E2=86=92=203.?= =?UTF-8?q?9.48?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- .cookiecutterrc | 2 +- README.rst | 4 ++-- docs/conf.py | 2 +- setup.py | 2 +- src/geophires_x/__init__.py | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 4915a551..20217b64 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.9.47 +current_version = 3.9.48 commit = True tag = True diff --git a/.cookiecutterrc b/.cookiecutterrc index 8cef84ab..b803036f 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -54,7 +54,7 @@ default_context: sphinx_doctest: "no" sphinx_theme: "sphinx-py3doc-enhanced-theme" test_matrix_separate_coverage: "no" - version: 3.9.47 + version: 3.9.48 version_manager: "bump2version" website: "https://github.com/NREL" year_from: "2023" diff --git a/README.rst b/README.rst index 4569b460..50ad772f 100644 --- a/README.rst +++ b/README.rst @@ -58,9 +58,9 @@ Free software: `MIT license `__ :alt: Supported implementations :target: https://pypi.org/project/geophires-x -.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.9.47.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.9.48.svg :alt: Commits since latest release - :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.9.47...main + :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.9.48...main .. |docs| image:: https://readthedocs.org/projects/GEOPHIRES-X/badge/?style=flat :target: https://nrel.github.io/GEOPHIRES-X diff --git a/docs/conf.py b/docs/conf.py index cde53599..b0ec6fb1 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,7 +18,7 @@ year = '2025' author = 'NREL' copyright = f'{year}, {author}' -version = release = '3.9.47' +version = release = '3.9.48' pygments_style = 'trac' templates_path = ['./templates'] diff --git a/setup.py b/setup.py index 5b79185b..9d4710c5 100755 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ def read(*names, **kwargs): setup( name='geophires-x', - version='3.9.47', + version='3.9.48', license='MIT', description='GEOPHIRES is a free and open-source geothermal techno-economic simulator.', long_description='{}\n{}'.format( diff --git a/src/geophires_x/__init__.py b/src/geophires_x/__init__.py index eaf2a48e..d10ed0f6 100644 --- a/src/geophires_x/__init__.py +++ b/src/geophires_x/__init__.py @@ -1 +1 @@ -__version__ = '3.9.47' +__version__ = '3.9.48'