From 02249322876aab463e045ead0304182f52b0aa45 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 25 Jul 2025 11:00:49 -0700 Subject: [PATCH 01/12] WIP - display redrilling costs in O&M --- src/geophires_x/Economics.py | 23 +++++++++++++++++--- src/geophires_x/Outputs.py | 8 +++---- src/geophires_x_client/geophires_x_result.py | 1 + tests/examples/Fervo_Project_Cape-4.out | 9 ++++---- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 15b92426..17e1f2a9 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -1775,6 +1775,14 @@ def __init__(self, model: Model): UnitType=Units.CURRENCYFREQUENCY, PreferredUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR, CurrentUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR + # TODO TooltipText to document how this is calculated + ) + self.redrilling_annual_cost = self.OutputParameterDict[self.redrilling_annual_cost.Name] = OutputParameter( + Name="Redrilling costs", + UnitType=Units.CURRENCYFREQUENCY, + PreferredUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR, + CurrentUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR, + # FIXME WIP TooltipText ) self.Cplant = self.OutputParameterDict[self.Cplant.Name] = OutputParameter( Name="Surface Plant cost", @@ -1809,7 +1817,7 @@ def __init__(self, model: Model): UnitType=Units.CURRENCYFREQUENCY, PreferredUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR, CurrentUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR, - ToolTipText='Assumes $3.5/1,000 gallons of water' + ToolTipText='Assumes $3.5/1,000 gallons of water' # TODO parameterize ) self.CCap = self.OutputParameterDict[self.CCap.Name] = OutputParameter( Name="Total Capital Cost", @@ -2521,8 +2529,10 @@ def Calculate(self, model: Model) -> None: if model.wellbores.redrill.value > 0: # account for well redrilling - self.Coam.value = self.Coam.value + \ - (self.Cwell.value + self.Cstim.value) * model.wellbores.redrill.value / model.surfaceplant.plant_lifetime.value + redrilling_costs: PlainQuantity = self.calculate_redrilling_costs(model) + self.redrilling_annual_cost.value = redrilling_costs.to(self.redrilling_annual_cost.CurrentUnits).magnitude + self.Coam.value += redrilling_costs.to(self.Coam.CurrentUnits).magnitude + # Add in the AnnualLicenseEtc and TaxRelief self.Coam.value = self.Coam.value + self.AnnualLicenseEtc.value - self.TaxRelief.value @@ -2751,6 +2761,11 @@ def calculate_stimulation_costs(self, model: Model) -> PlainQuantity: return quantity(stimulation_costs, self.Cstim.CurrentUnits) + def calculate_redrilling_costs(self, model) -> PlainQuantity: + return ((self.Cwell.quantity() + self.Cstim.quantity()) + * model.wellbores.redrill.quantity() + / model.surfaceplant.plant_lifetime.quantity()) + def calculate_field_gathering_costs(self, model: Model) -> None: if self.ccgathfixed.Valid: self.Cgath.value = self.ccgathfixed.value @@ -3164,5 +3179,7 @@ def _calculate_derived_outputs(self, model: Model) -> None: (model.wellbores.nprod.value + model.wellbores.ninj.value) ) + + def __str__(self): return "Economics" diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index d1c45300..7dce945e 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -475,10 +475,6 @@ def PrintOutputs(self, model: Model): f.write(f' District Heating System Cost: {model.economics.dhdistrictcost.value:10.2f} {model.economics.dhdistrictcost.CurrentUnits.value}\n') f.write(f' Total surface equipment costs: {(model.economics.Cplant.value+model.economics.Cgath.value):10.2f} ' + model.economics.Cplant.CurrentUnits.value + NL) f.write(f' {model.economics.Cexpl.display_name}: {model.economics.Cexpl.value:10.2f} {model.economics.Cexpl.CurrentUnits.value}\n') - 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): {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') @@ -506,6 +502,10 @@ def PrintOutputs(self, model: Model): f.write(f' Annual District Heating O&M Cost: {model.economics.dhdistrictoandmcost.value:10.2f} {model.economics.dhdistrictoandmcost.CurrentUnits.value}\n') f.write(f' Average Annual Peaking Fuel Cost: {model.economics.averageannualngcost.value:10.2f} {model.economics.averageannualngcost.CurrentUnits.value}\n') + if model.wellbores.redrill.value > 0: + redrill_label = Outputs._field_label(econ.redrilling_annual_cost.display_name, 47) + f.write(f' {redrill_label}{econ.redrilling_annual_cost.value:10.2f} {econ.redrilling_annual_cost.CurrentUnits.value}\n') + f.write(f' {econ.Coam.display_name}: {(econ.Coam.value + econ.averageannualpumpingcosts.value + econ.averageannualheatpumpelectricitycost.value):10.2f} {econ.Coam.CurrentUnits.value}\n') else: f.write(f' {econ.Coam.display_name}: {econ.Coam.value:10.2f} {econ.Coam.CurrentUnits.value}\n') diff --git a/src/geophires_x_client/geophires_x_result.py b/src/geophires_x_client/geophires_x_result.py index 7b34f3dc..c2722dba 100644 --- a/src/geophires_x_client/geophires_x_result.py +++ b/src/geophires_x_client/geophires_x_result.py @@ -290,6 +290,7 @@ class GeophiresXResult: # SUTRA 'Average annual auxiliary fuel cost', 'Average annual pumping cost', + 'Redrilling costs', 'Total average annual O&M costs', ], 'SURFACE EQUIPMENT SIMULATION RESULTS': [ diff --git a/tests/examples/Fervo_Project_Cape-4.out b/tests/examples/Fervo_Project_Cape-4.out index 377cd823..4c31b53f 100644 --- a/tests/examples/Fervo_Project_Cape-4.out +++ b/tests/examples/Fervo_Project_Cape-4.out @@ -4,10 +4,10 @@ Simulation Metadata ---------------------- - GEOPHIRES Version: 3.9.28 - Simulation Date: 2025-07-02 - Simulation Time: 12:19 - Calculation Time: 1.773 sec + GEOPHIRES Version: 3.9.36 + Simulation Date: 2025-07-25 + Simulation Time: 10:59 + Calculation Time: 1.734 sec ***SUMMARY OF RESULTS*** @@ -113,6 +113,7 @@ Simulation Metadata Wellfield maintenance costs: 6.20 MUSD/yr Power plant maintenance costs: 25.43 MUSD/yr Water costs: 24.86 MUSD/yr + Redrilling costs: 70.46 MUSD/yr Total operating and maintenance costs: 126.95 MUSD/yr From 643c38dee2ff1265f9c0c86878073da5fd17d9b1 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 25 Jul 2025 11:26:14 -0700 Subject: [PATCH 02/12] Redrilling costs tooltip text (still WIP to update affected unit tests) --- src/geophires_x/Economics.py | 6 +++++- src/geophires_x/Outputs.py | 2 +- src/geophires_x/WellBores.py | 1 + .../geophires-result.json | 11 ++++++++++- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 17e1f2a9..667a7336 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -1782,7 +1782,11 @@ def __init__(self, model: Model): UnitType=Units.CURRENCYFREQUENCY, PreferredUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR, CurrentUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR, - # FIXME WIP TooltipText + ToolTipText=f'Total redrilling costs over the {model.surfaceplant.plant_lifetime.Name} are calculated as ' + f'({self.Cwell.display_name} + {self.Cstim.display_name}) ' + f'× {model.wellbores.redrill.display_name}. ' + f'The total is then divided over {model.surfaceplant.plant_lifetime.Name} years to calculate ' + f'Redrilling costs per year.' ) self.Cplant = self.OutputParameterDict[self.Cplant.Name] = OutputParameter( Name="Surface Plant cost", diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index 7dce945e..3c740245 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -325,7 +325,7 @@ def PrintOutputs(self, model: Model): 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' Number of times redrilling: {model.wellbores.redrill.value:10.0f}'+NL) + 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) f.write(NL) diff --git a/src/geophires_x/WellBores.py b/src/geophires_x/WellBores.py index af33257b..dca1eb52 100644 --- a/src/geophires_x/WellBores.py +++ b/src/geophires_x/WellBores.py @@ -1120,6 +1120,7 @@ def __init__(self, model: Model): ) self.redrill = self.OutputParameterDict[self.redrill.Name] = OutputParameter( Name="redrill", + display_name='Number of times redrilling', UnitType=Units.NONE ) self.PumpingPowerProd = self.OutputParameterDict[self.PumpingPowerProd.Name] = OutputParameter( diff --git a/src/geophires_x_schema_generator/geophires-result.json b/src/geophires_x_schema_generator/geophires-result.json index 9cba6618..49f29f8e 100644 --- a/src/geophires_x_schema_generator/geophires-result.json +++ b/src/geophires_x_schema_generator/geophires-result.json @@ -197,7 +197,11 @@ "Flowrate per production well": {}, "Injection well casing ID": {}, "Production well casing ID": {}, - "Number of times redrilling": {}, + "Number of times redrilling": { + "type": "number", + "description": "redrill", + "units": null + }, "Power plant type": {}, "Fluid": {}, "Design": {}, @@ -440,6 +444,11 @@ "OPEX": {}, "Average annual auxiliary fuel cost": {}, "Average annual pumping cost": {}, + "Redrilling costs": { + "type": "number", + "description": "Total redrilling costs over the Plant Lifetime are calculated as (Drilling and completion costs + Stimulation costs) \u00d7 Number of times redrilling. The total is then divided over Plant Lifetime years to calculate Redrilling costs per year.", + "units": "MUSD/yr" + }, "Total average annual O&M costs": {} } }, From e5f9286814cf3d3316cfded3cb7b86fbd4dd0283 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 25 Jul 2025 11:31:25 -0700 Subject: [PATCH 03/12] Regenerate example13 (has redrilling) --- tests/examples/example13.out | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/examples/example13.out b/tests/examples/example13.out index b903fc30..006909f4 100644 --- a/tests/examples/example13.out +++ b/tests/examples/example13.out @@ -4,10 +4,10 @@ Simulation Metadata ---------------------- - GEOPHIRES Version: 3.9.7 - Simulation Date: 2025-05-15 - Simulation Time: 10:12 - Calculation Time: 0.032 sec + GEOPHIRES Version: 3.9.36 + Simulation Date: 2025-07-25 + Simulation Time: 11:30 + Calculation Time: 0.037 sec ***SUMMARY OF RESULTS*** @@ -109,6 +109,7 @@ Simulation Metadata Wellfield maintenance costs: 0.62 MUSD/yr Power plant maintenance costs: 1.30 MUSD/yr Water costs: 0.00 MUSD/yr + Redrilling costs: 0.92 MUSD/yr Total operating and maintenance costs: 2.84 MUSD/yr From 92ba009d92f5d85811d33ca4fca0a73d23985dab Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 25 Jul 2025 11:38:36 -0700 Subject: [PATCH 04/12] =?UTF-8?q?Bump=20version:=203.9.36=20=E2=86=92=203.?= =?UTF-8?q?9.37?= 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 6cf7bacd..42d95ec9 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.9.36 +current_version = 3.9.37 commit = True tag = True diff --git a/.cookiecutterrc b/.cookiecutterrc index 1cbbc98b..7e997096 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.36 + version: 3.9.37 version_manager: "bump2version" website: "https://github.com/NREL" year_from: "2023" diff --git a/README.rst b/README.rst index 1f10631e..ece75ede 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.36.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.9.37.svg :alt: Commits since latest release - :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.9.36...main + :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.9.37...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 3d8e4309..55bbcfe1 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.36' +version = release = '3.9.37' pygments_style = 'trac' templates_path = ['./templates'] diff --git a/setup.py b/setup.py index e7f55c66..8b02bb02 100755 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ def read(*names, **kwargs): setup( name='geophires-x', - version='3.9.36', + version='3.9.37', 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 0c4a7494..6c4e6ecb 100644 --- a/src/geophires_x/__init__.py +++ b/src/geophires_x/__init__.py @@ -1 +1 @@ -__version__ = '3.9.36' +__version__ = '3.9.37' From cc6185f503d701998b959c242f650d36aff28b19 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 25 Jul 2025 11:47:08 -0700 Subject: [PATCH 05/12] Order client result fields such that total opex is displayed as last line item by default --- src/geophires_x_client/geophires_x_result.py | 6 +++--- .../geophires-result.json | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/geophires_x_client/geophires_x_result.py b/src/geophires_x_client/geophires_x_result.py index c2722dba..eb367580 100644 --- a/src/geophires_x_client/geophires_x_result.py +++ b/src/geophires_x_client/geophires_x_result.py @@ -284,14 +284,14 @@ class GeophiresXResult: 'Annual District Heating O&M Cost', 'Average Annual Peaking Fuel Cost', 'Average annual pumping costs', - 'Total operating and maintenance costs', - # AGS/CLGS - 'OPEX', # SUTRA 'Average annual auxiliary fuel cost', 'Average annual pumping cost', 'Redrilling costs', 'Total average annual O&M costs', + 'Total operating and maintenance costs', + # AGS/CLGS + 'OPEX', ], 'SURFACE EQUIPMENT SIMULATION RESULTS': [ 'Initial geofluid availability', diff --git a/src/geophires_x_schema_generator/geophires-result.json b/src/geophires_x_schema_generator/geophires-result.json index 49f29f8e..a2fe3a18 100644 --- a/src/geophires_x_schema_generator/geophires-result.json +++ b/src/geophires_x_schema_generator/geophires-result.json @@ -436,12 +436,6 @@ "units": "MUSD/yr" }, "Average annual pumping costs": {}, - "Total operating and maintenance costs": { - "type": "number", - "description": "Total O&M Cost", - "units": "MUSD/yr" - }, - "OPEX": {}, "Average annual auxiliary fuel cost": {}, "Average annual pumping cost": {}, "Redrilling costs": { @@ -449,7 +443,13 @@ "description": "Total redrilling costs over the Plant Lifetime are calculated as (Drilling and completion costs + Stimulation costs) \u00d7 Number of times redrilling. The total is then divided over Plant Lifetime years to calculate Redrilling costs per year.", "units": "MUSD/yr" }, - "Total average annual O&M costs": {} + "Total average annual O&M costs": {}, + "Total operating and maintenance costs": { + "type": "number", + "description": "Total O&M Cost", + "units": "MUSD/yr" + }, + "OPEX": {} } }, "SURFACE EQUIPMENT SIMULATION RESULTS": { From f2bfd6dc0361084d56cc0e9ceeef3130562219af Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 25 Jul 2025 11:47:13 -0700 Subject: [PATCH 06/12] =?UTF-8?q?Bump=20version:=203.9.37=20=E2=86=92=203.?= =?UTF-8?q?9.38?= 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 42d95ec9..8aef3a49 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.9.37 +current_version = 3.9.38 commit = True tag = True diff --git a/.cookiecutterrc b/.cookiecutterrc index 7e997096..076ef373 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.37 + version: 3.9.38 version_manager: "bump2version" website: "https://github.com/NREL" year_from: "2023" diff --git a/README.rst b/README.rst index ece75ede..014bde30 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.37.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.9.38.svg :alt: Commits since latest release - :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.9.37...main + :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.9.38...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 55bbcfe1..180aa4d6 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.37' +version = release = '3.9.38' pygments_style = 'trac' templates_path = ['./templates'] diff --git a/setup.py b/setup.py index 8b02bb02..bacbe98c 100755 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ def read(*names, **kwargs): setup( name='geophires-x', - version='3.9.37', + version='3.9.38', 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 6c4e6ecb..71aa675a 100644 --- a/src/geophires_x/__init__.py +++ b/src/geophires_x/__init__.py @@ -1 +1 @@ -__version__ = '3.9.37' +__version__ = '3.9.38' From f78603e3c9c7023c3f15cb9b5bf4e7c518d0941d Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 25 Jul 2025 12:03:53 -0700 Subject: [PATCH 07/12] increase max allowed total capex param to $100B --- src/geophires_x/Economics.py | 3 ++- src/geophires_x_schema_generator/geophires-request.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 667a7336..afa2acfe 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -868,7 +868,8 @@ def __init__(self, model: Model): "Total Capital Cost", DefaultValue=-1.0, Min=0, - Max=1000, + # pint treats GUSD as billions of dollars (G for giga) + Max=quantity(100, 'GUSD').to('MUSD').magnitude, 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 8def2347..90967ba1 100644 --- a/src/geophires_x_schema_generator/geophires-request.json +++ b/src/geophires_x_schema_generator/geophires-request.json @@ -1591,7 +1591,7 @@ "category": "Economics", "default": -1.0, "minimum": 0, - "maximum": 1000 + "maximum": 100000.0 }, "Total O&M Cost": { "description": "Total initial O&M cost.", From 00846f8c16986c20c3c7fe2a7e446ac7ae0d5caf Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 25 Jul 2025 12:24:49 -0700 Subject: [PATCH 08/12] test_redrilling_costs - FIXME WIP to re-add redrilling-specific capex costs displayed when total capital costs are provided --- tests/test_geophires_x.py | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/test_geophires_x.py b/tests/test_geophires_x.py index 58119d40..f3ff84da 100644 --- a/tests/test_geophires_x.py +++ b/tests/test_geophires_x.py @@ -1219,3 +1219,49 @@ def test_exploration_cost(self): self.assertEqual(exploration_cost_MUSD, result.result['CAPITAL COSTS (M$)']['Exploration costs']['value']) self.assertEqual('MUSD', result.result['CAPITAL COSTS (M$)']['Exploration costs']['unit']) + + def test_redrilling_costs(self): + total_capex_specified_result = GeophiresXClient().get_geophires_result( + ImmutableGeophiresInputParameters( + from_file_path=self._get_test_file_path('examples/Fervo_Project_Cape-4.txt'), + params={'Total Capital Cost': 2500}, + ) + ) + + for result in [ + GeophiresXResult(self._get_test_file_path('examples/Fervo_Project_Cape-4.out')), + total_capex_specified_result, + ]: + result_redrills = result.result['ENGINEERING PARAMETERS']['Number of times redrilling']['value'] + self.assertGreater(result_redrills, 0) + + result_opex = result.result['OPERATING AND MAINTENANCE COSTS (M$/yr)'] + opex_sum = 0 + expected_opex_line_items = [ + 'Wellfield maintenance costs', + 'Power plant maintenance costs', + 'Water costs', + 'Redrilling costs', + ] + for opex_line_item in expected_opex_line_items: + opex_sum += result_opex[opex_line_item]['value'] + + self.assertAlmostEqual(result_opex['Total operating and maintenance costs']['value'], opex_sum, places=1) + + result_capex = result.result['CAPITAL COSTS (M$)'] + if result_capex.get('Drilling and completion costs') is not None: + expected_annual_redrilling_cost = ( + ( + result_capex['Drilling and completion costs']['value'] + + result_capex['Stimulation costs']['value'] + ) + * result_redrills + ) / result.result['ECONOMIC PARAMETERS']['Project lifetime']['value'] + + self.assertAlmostEqual( + expected_annual_redrilling_cost, result_opex['Redrilling costs']['value'], places=2 + ) + else: + # Individual capex line items are not calculated when user provides Total Capital Cost. + # FIXME WIP output/check + pass From a8aa8e6a407a37b595739d9b4a56766a91d9bc1f Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 25 Jul 2025 12:30:52 -0700 Subject: [PATCH 09/12] Re-implement previous behavior to display drilling/stimulation costs for redrilling when total capex is provided --- src/geophires_x/Outputs.py | 6 ++++++ tests/test_geophires_x.py | 26 +++++++++++--------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index 3c740245..30ffb347 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -475,6 +475,12 @@ def PrintOutputs(self, model: Model): f.write(f' District Heating System Cost: {model.economics.dhdistrictcost.value:10.2f} {model.economics.dhdistrictcost.CurrentUnits.value}\n') f.write(f' Total surface equipment costs: {(model.economics.Cplant.value+model.economics.Cgath.value):10.2f} ' + model.economics.Cplant.CurrentUnits.value + NL) f.write(f' {model.economics.Cexpl.display_name}: {model.economics.Cexpl.value:10.2f} {model.economics.Cexpl.CurrentUnits.value}\n') + + if model.economics.totalcapcost.Valid and model.wellbores.redrill.value > 0: + f.write(f' Drilling and completion costs (for redrilling):{econ.Cwell.value:10.2f} {econ.Cwell.CurrentUnits.value}\n') + f.write(f' Drilling and completion costs per redrilled well: {(econ.Cwell.value/(model.wellbores.nprod.value+model.wellbores.ninj.value)):10.2f} {econ.Cwell.CurrentUnits.value}\n') + 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/tests/test_geophires_x.py b/tests/test_geophires_x.py index f3ff84da..77352278 100644 --- a/tests/test_geophires_x.py +++ b/tests/test_geophires_x.py @@ -1249,19 +1249,15 @@ def test_redrilling_costs(self): self.assertAlmostEqual(result_opex['Total operating and maintenance costs']['value'], opex_sum, places=1) result_capex = result.result['CAPITAL COSTS (M$)'] - if result_capex.get('Drilling and completion costs') is not None: - expected_annual_redrilling_cost = ( - ( - result_capex['Drilling and completion costs']['value'] - + result_capex['Stimulation costs']['value'] - ) - * result_redrills - ) / result.result['ECONOMIC PARAMETERS']['Project lifetime']['value'] - - self.assertAlmostEqual( - expected_annual_redrilling_cost, result_opex['Redrilling costs']['value'], places=2 + capex_field_suffix = ( + '' if result_capex.get('Drilling and completion costs') is not None else ' (for redrilling)' + ) + expected_annual_redrilling_cost = ( + ( + result_capex[f'Drilling and completion costs{capex_field_suffix}']['value'] + + result_capex[f'Stimulation costs{capex_field_suffix}']['value'] ) - else: - # Individual capex line items are not calculated when user provides Total Capital Cost. - # FIXME WIP output/check - pass + * result_redrills + ) / result.result['ECONOMIC PARAMETERS']['Project lifetime']['value'] + + self.assertAlmostEqual(expected_annual_redrilling_cost, result_opex['Redrilling costs']['value'], places=2) From 856ade4199bb59ed9b61c8459a743a40cce351f3 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 25 Jul 2025 12:30:58 -0700 Subject: [PATCH 10/12] =?UTF-8?q?Bump=20version:=203.9.38=20=E2=86=92=203.?= =?UTF-8?q?9.39?= 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 8aef3a49..3bd2bc4b 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.9.38 +current_version = 3.9.39 commit = True tag = True diff --git a/.cookiecutterrc b/.cookiecutterrc index 076ef373..375ca83f 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.38 + version: 3.9.39 version_manager: "bump2version" website: "https://github.com/NREL" year_from: "2023" diff --git a/README.rst b/README.rst index 014bde30..b792872b 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.38.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.9.39.svg :alt: Commits since latest release - :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.9.38...main + :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.9.39...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 180aa4d6..f98be0a5 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.38' +version = release = '3.9.39' pygments_style = 'trac' templates_path = ['./templates'] diff --git a/setup.py b/setup.py index bacbe98c..6ff8060f 100755 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ def read(*names, **kwargs): setup( name='geophires-x', - version='3.9.38', + version='3.9.39', 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 71aa675a..59f22dae 100644 --- a/src/geophires_x/__init__.py +++ b/src/geophires_x/__init__.py @@ -1 +1 @@ -__version__ = '3.9.38' +__version__ = '3.9.39' From 4f9c500368b2db47f640dcdfb005b6fe72d487d3 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 25 Jul 2025 14:35:15 -0700 Subject: [PATCH 11/12] Workaround https://github.com/NREL/GEOPHIRES-X/issues/404 - don't display incorrect SAM-EM ITCs for now (TODO to extract from cash flow profile) --- src/geophires_x/Outputs.py | 11 ++++++++++- tests/examples/Fervo_Project_Cape-4.out | 7 +++---- tests/examples/example_SAM-single-owner-PPA-2.out | 9 ++++----- tests/examples/example_SAM-single-owner-PPA.out | 9 ++++----- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index 30ffb347..f6bc8819 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -482,7 +482,16 @@ def PrintOutputs(self, model: Model): 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') + if model.economics.econmodel.value != EconomicModel.SAM_SINGLE_OWNER_PPA: + f.write(f' {model.economics.RITCValue.display_name}: {-1*model.economics.RITCValue.value:10.2f} {model.economics.RITCValue.CurrentUnits.value}\n') + else: + # TODO Extract value from SAM Cash Flow Profile per + # https://github.com/NREL/GEOPHIRES-X/issues/404. + # For now we skip displaying the value because it can be/probably is usually mathematically + # inaccurate, and even if it's not, it's redundant with the cash flow profile and also + # misleading/confusing/wrong to display it as a capital cost since it is not a capital + # expenditure. + pass capex_label = Outputs._field_label(econ.CCap.display_name, 50) f.write(f' {capex_label}{econ.CCap.value:10.2f} {econ.CCap.CurrentUnits.value}\n') diff --git a/tests/examples/Fervo_Project_Cape-4.out b/tests/examples/Fervo_Project_Cape-4.out index 4c31b53f..e461faed 100644 --- a/tests/examples/Fervo_Project_Cape-4.out +++ b/tests/examples/Fervo_Project_Cape-4.out @@ -4,10 +4,10 @@ Simulation Metadata ---------------------- - GEOPHIRES Version: 3.9.36 + GEOPHIRES Version: 3.9.39 Simulation Date: 2025-07-25 - Simulation Time: 10:59 - Calculation Time: 1.734 sec + Simulation Time: 14:32 + Calculation Time: 1.794 sec ***SUMMARY OF RESULTS*** @@ -104,7 +104,6 @@ Simulation Metadata Field gathering system costs: 56.44 MUSD Total surface equipment costs: 1560.49 MUSD Exploration costs: 30.00 MUSD - Investment Tax Credit: -688.54 MUSD Total CAPEX: 2639.39 MUSD diff --git a/tests/examples/example_SAM-single-owner-PPA-2.out b/tests/examples/example_SAM-single-owner-PPA-2.out index 9a0af2ae..bcd63c41 100644 --- a/tests/examples/example_SAM-single-owner-PPA-2.out +++ b/tests/examples/example_SAM-single-owner-PPA-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.975 sec + GEOPHIRES Version: 3.9.39 + Simulation Date: 2025-07-25 + Simulation Time: 14:32 + Calculation Time: 0.983 sec ***SUMMARY OF RESULTS*** @@ -105,7 +105,6 @@ Simulation Metadata Field gathering system costs: 70.43 MUSD Total surface equipment costs: 969.26 MUSD Exploration costs: 30.00 MUSD - Investment Tax Credit: -459.83 MUSD Total CAPEX: 1609.42 MUSD diff --git a/tests/examples/example_SAM-single-owner-PPA.out b/tests/examples/example_SAM-single-owner-PPA.out index 07be3655..e71832f9 100644 --- a/tests/examples/example_SAM-single-owner-PPA.out +++ b/tests/examples/example_SAM-single-owner-PPA.out @@ -4,10 +4,10 @@ Simulation Metadata ---------------------- - GEOPHIRES Version: 3.9.28 - Simulation Date: 2025-07-02 - Simulation Time: 12:19 - Calculation Time: 1.163 sec + GEOPHIRES Version: 3.9.39 + Simulation Date: 2025-07-25 + Simulation Time: 14:32 + Calculation Time: 1.177 sec ***SUMMARY OF RESULTS*** @@ -106,7 +106,6 @@ Simulation Metadata Field gathering system costs: 5.80 MUSD Total surface equipment costs: 150.23 MUSD Exploration costs: 3.89 MUSD - Investment Tax Credit: -63.71 MUSD Total CAPEX: 222.97 MUSD From 70daea9a2c324905acd3b0f6f4ff5b219a0abff4 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 25 Jul 2025 14:44:01 -0700 Subject: [PATCH 12/12] =?UTF-8?q?Bump=20version:=203.9.39=20=E2=86=92=203.?= =?UTF-8?q?9.40?= 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 3bd2bc4b..a0f79492 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.9.39 +current_version = 3.9.40 commit = True tag = True diff --git a/.cookiecutterrc b/.cookiecutterrc index 375ca83f..00ae6670 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.39 + version: 3.9.40 version_manager: "bump2version" website: "https://github.com/NREL" year_from: "2023" diff --git a/README.rst b/README.rst index b792872b..2eb8ba44 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.39.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.9.40.svg :alt: Commits since latest release - :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.9.39...main + :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.9.40...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 f98be0a5..b2fdba14 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.39' +version = release = '3.9.40' pygments_style = 'trac' templates_path = ['./templates'] diff --git a/setup.py b/setup.py index 6ff8060f..1e04355c 100755 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ def read(*names, **kwargs): setup( name='geophires-x', - version='3.9.39', + version='3.9.40', 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 59f22dae..ac839cc1 100644 --- a/src/geophires_x/__init__.py +++ b/src/geophires_x/__init__.py @@ -1 +1 @@ -__version__ = '3.9.39' +__version__ = '3.9.40'