diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 7e83c5bb..4f7e5fad 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,7 +1,21 @@ [bumpversion] -current_version = 3.9.27 +current_version = 3.9.28-rc.0 commit = True tag = True +parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\-(?P[a-z]+)\.(?P\d+))? +serialize = + {major}.{minor}.{patch}-{prerelease_label}.{prerelease} + {major}.{minor}.{patch} + +[bumpversion:part:prerelease] +first_value = 0 + +[bumpversion:part:prerelease_label] +optional_value = rc +values = + alpha + beta + rc [bumpversion:file:setup.py] search = version='{current_version}' diff --git a/.cookiecutterrc b/.cookiecutterrc index 4db3d99a..d4ea9f3e 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.27 + version: 3.9.28 version_manager: "bump2version" website: "https://github.com/NREL" year_from: "2023" diff --git a/README.rst b/README.rst index 617ef5fe..01fb83f3 100644 --- a/README.rst +++ b/README.rst @@ -56,9 +56,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.27.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.9.28.svg :alt: Commits since latest release - :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.9.27...main + :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.9.28...main .. |docs| image:: https://readthedocs.org/projects/GEOPHIRES-X/badge/?style=flat :target: https://nrel.github.io/GEOPHIRES-X @@ -300,8 +300,8 @@ well drilling, surface plant, etc.) are either provided by the user or calculate correlations. For more information on the theoretical basis for GEOPHIRES see -`GEOPHIRES v2.0: updated geothermal techno‐economic simulation tool (Beckers & McCabe, 2019) `__ -and `GEOPHIRES reference materials `__. +`GEOPHIRES v2.0: updated geothermal techno‐economic simulation tool (Beckers & McCabe, 2019) `__ +and `GEOPHIRES reference materials `__. Parameters ---------- @@ -513,7 +513,7 @@ Extending GEOPHIRES-X Additional Documentation ------------------------ -Additional materials can be found in `/References `__. +Additional materials can be found in `/References `__. Development diff --git a/References/README.md b/References/README.md new file mode 100644 index 00000000..07f0fdb6 --- /dev/null +++ b/References/README.md @@ -0,0 +1,32 @@ + +# GEOPHIRES +* [GEOPHIRES v2.0 User Manual.pdf](https://github.com/NREL/GEOPHIRES-X/blob/fb5caadfa419c3bd05de656a33700d085fbc0432/References/GEOPHIRES%20v2.0%20User%20Manual.pdf) [![](https://zenodo.org/badge/doi/10.1186/s40517-019-0119-6.svg)](https://doi.org/10.1186/s40517-019-0119-6) +* [Beckers 2019 GEOPHIRES v2.pdf](https://github.com/NREL/GEOPHIRES-X/blob/fb5caadfa419c3bd05de656a33700d085fbc0432/References/Beckers%202019%20GEOPHIRES%20v2.pdf) +* [Beckers 2018 Introducing GEOPHIRES v2.pdf](https://github.com/NREL/GEOPHIRES-X/blob/fb5caadfa419c3bd05de656a33700d085fbc0432/References/Beckers%202018%20Introducing%20GEOPHIRES%20v2.pdf) +* [Beckers 2016 Dissertation.pdf](https://github.com/NREL/GEOPHIRES-X/blob/fb5caadfa419c3bd05de656a33700d085fbc0432/References/Beckers%202016%20Dissertation.pdf) [![](https://zenodo.org/badge/doi/10.7298/X4736NTC.svg)](https://doi.org/10.7298/X4736NTC) +* [Beckers 2013 GEOPHIRES v1.pdf](https://github.com/NREL/GEOPHIRES-X/blob/fb5caadfa419c3bd05de656a33700d085fbc0432/References/Beckers%202013%20GEOPHIRES%20v1.pdf) + +# Reference +* [GeoVision report.pdf](https://github.com/NREL/GEOPHIRES-X/blob/ea5c67d43fe77c83a64208d8e2ab92a9abea3742/References/GeoVision%20report.pdf) +* [US-EIA_2013_LCOE-and-LACE-supplement.pdf](https://github.com/NREL/GEOPHIRES-X/blob/7adc9598944a78487e5071ca23a06bfd17248c16/References/US-EIA_2013_LCOE-and-LACE-supplement.pdf) +* [LACETable.txt](https://github.com/NREL/GEOPHIRES-X/blob/fb5caadfa419c3bd05de656a33700d085fbc0432/References/LACETable.txt) +* [Drill cost correlation converting to per foot cost.xlsx](https://github.com/NREL/GEOPHIRES-X/blob/aae93f36ca5e01f4b700ddb5a59d0ec600aed3d4/References/Drill%20cost%20correlation%20converting%20to%20per%20foot%20cost.xlsx) + +# Fervo +* [fervo_energy_white_paper.pdf](https://github.com/NREL/GEOPHIRES-X/blob/703c967b0b1fe9f6d619b1e786686ba07fb0fe59/References/fervo_energy_white_paper.pdf) +* [fervo_drilling costs.pdf](https://github.com/softwareengineerprogrammer/GEOPHIRES-X/blob/62ecc37385cc89dcac36a3684c258e454bcc0241/References/fervo_drilling%20costs.pdf) + +# JEDI +* [JEDI.pdf](https://github.com/NREL/GEOPHIRES-X/blob/a7d1ff2e0d47e957c19c3b6dec28fece16905dcb/References/JEDI.pdf) +* [01d-jedi-geothermal-model-rel-gt12-23-16.xlsm](https://github.com/NREL/GEOPHIRES-X/raw/a7d1ff2e0d47e957c19c3b6dec28fece16905dcb/References/01d-jedi-geothermal-model-rel-gt12-23-16.xlsm) + +# HIP-RA +* [Muffler-Cataldi_1978_ HIP-RA.pdf](https://github.com/NREL/GEOPHIRES-X/blob/7adc9598944a78487e5071ca23a06bfd17248c16/References/Muffler-Cataldi_1978_%20HIP-RA.pdf) +* [Garg-Combs_2011_HIP-RA-Reexamination.pdf](https://github.com/NREL/GEOPHIRES-X/blob/7adc9598944a78487e5071ca23a06bfd17248c16/References/Garg-Combs_2011_HIP-RA-Reexamination.pdf) + +# CLGS +* [Beckers-et-al_2023_Tabulated-Database-CLGS.pdf](https://github.com/NREL/GEOPHIRES-X/blob/7adc9598944a78487e5071ca23a06bfd17248c16/References/Beckers-et-al_2023_Tabulated-Database-CLGS.pdf) +* [Wanju-et-al_2021_CLGS-Energy-Recovery.pdf](https://github.com/NREL/GEOPHIRES-X/blob/7adc9598944a78487e5071ca23a06bfd17248c16/References/Wanju-et-al_2021_CLGS-Energy-Recovery.pdf) + +# S-DAC-GT +* [JPSE2023.pdf](https://github.com/NREL/GEOPHIRES-X/blob/ea5c67d43fe77c83a64208d8e2ab92a9abea3742/References/JPSE2023.pdf) diff --git a/References/references.md b/References/references.md index 07f0fdb6..341f6216 100644 --- a/References/references.md +++ b/References/references.md @@ -1,32 +1 @@ - -# GEOPHIRES -* [GEOPHIRES v2.0 User Manual.pdf](https://github.com/NREL/GEOPHIRES-X/blob/fb5caadfa419c3bd05de656a33700d085fbc0432/References/GEOPHIRES%20v2.0%20User%20Manual.pdf) [![](https://zenodo.org/badge/doi/10.1186/s40517-019-0119-6.svg)](https://doi.org/10.1186/s40517-019-0119-6) -* [Beckers 2019 GEOPHIRES v2.pdf](https://github.com/NREL/GEOPHIRES-X/blob/fb5caadfa419c3bd05de656a33700d085fbc0432/References/Beckers%202019%20GEOPHIRES%20v2.pdf) -* [Beckers 2018 Introducing GEOPHIRES v2.pdf](https://github.com/NREL/GEOPHIRES-X/blob/fb5caadfa419c3bd05de656a33700d085fbc0432/References/Beckers%202018%20Introducing%20GEOPHIRES%20v2.pdf) -* [Beckers 2016 Dissertation.pdf](https://github.com/NREL/GEOPHIRES-X/blob/fb5caadfa419c3bd05de656a33700d085fbc0432/References/Beckers%202016%20Dissertation.pdf) [![](https://zenodo.org/badge/doi/10.7298/X4736NTC.svg)](https://doi.org/10.7298/X4736NTC) -* [Beckers 2013 GEOPHIRES v1.pdf](https://github.com/NREL/GEOPHIRES-X/blob/fb5caadfa419c3bd05de656a33700d085fbc0432/References/Beckers%202013%20GEOPHIRES%20v1.pdf) - -# Reference -* [GeoVision report.pdf](https://github.com/NREL/GEOPHIRES-X/blob/ea5c67d43fe77c83a64208d8e2ab92a9abea3742/References/GeoVision%20report.pdf) -* [US-EIA_2013_LCOE-and-LACE-supplement.pdf](https://github.com/NREL/GEOPHIRES-X/blob/7adc9598944a78487e5071ca23a06bfd17248c16/References/US-EIA_2013_LCOE-and-LACE-supplement.pdf) -* [LACETable.txt](https://github.com/NREL/GEOPHIRES-X/blob/fb5caadfa419c3bd05de656a33700d085fbc0432/References/LACETable.txt) -* [Drill cost correlation converting to per foot cost.xlsx](https://github.com/NREL/GEOPHIRES-X/blob/aae93f36ca5e01f4b700ddb5a59d0ec600aed3d4/References/Drill%20cost%20correlation%20converting%20to%20per%20foot%20cost.xlsx) - -# Fervo -* [fervo_energy_white_paper.pdf](https://github.com/NREL/GEOPHIRES-X/blob/703c967b0b1fe9f6d619b1e786686ba07fb0fe59/References/fervo_energy_white_paper.pdf) -* [fervo_drilling costs.pdf](https://github.com/softwareengineerprogrammer/GEOPHIRES-X/blob/62ecc37385cc89dcac36a3684c258e454bcc0241/References/fervo_drilling%20costs.pdf) - -# JEDI -* [JEDI.pdf](https://github.com/NREL/GEOPHIRES-X/blob/a7d1ff2e0d47e957c19c3b6dec28fece16905dcb/References/JEDI.pdf) -* [01d-jedi-geothermal-model-rel-gt12-23-16.xlsm](https://github.com/NREL/GEOPHIRES-X/raw/a7d1ff2e0d47e957c19c3b6dec28fece16905dcb/References/01d-jedi-geothermal-model-rel-gt12-23-16.xlsm) - -# HIP-RA -* [Muffler-Cataldi_1978_ HIP-RA.pdf](https://github.com/NREL/GEOPHIRES-X/blob/7adc9598944a78487e5071ca23a06bfd17248c16/References/Muffler-Cataldi_1978_%20HIP-RA.pdf) -* [Garg-Combs_2011_HIP-RA-Reexamination.pdf](https://github.com/NREL/GEOPHIRES-X/blob/7adc9598944a78487e5071ca23a06bfd17248c16/References/Garg-Combs_2011_HIP-RA-Reexamination.pdf) - -# CLGS -* [Beckers-et-al_2023_Tabulated-Database-CLGS.pdf](https://github.com/NREL/GEOPHIRES-X/blob/7adc9598944a78487e5071ca23a06bfd17248c16/References/Beckers-et-al_2023_Tabulated-Database-CLGS.pdf) -* [Wanju-et-al_2021_CLGS-Energy-Recovery.pdf](https://github.com/NREL/GEOPHIRES-X/blob/7adc9598944a78487e5071ca23a06bfd17248c16/References/Wanju-et-al_2021_CLGS-Energy-Recovery.pdf) - -# S-DAC-GT -* [JPSE2023.pdf](https://github.com/NREL/GEOPHIRES-X/blob/ea5c67d43fe77c83a64208d8e2ab92a9abea3742/References/JPSE2023.pdf) +See [References README](README.md). diff --git a/docs/Fervo_Project_Cape-4.md b/docs/Fervo_Project_Cape-4.md index 06354385..e4df0606 100644 --- a/docs/Fervo_Project_Cape-4.md +++ b/docs/Fervo_Project_Cape-4.md @@ -8,7 +8,7 @@ Financial results are calculated using the [SAM Single Owner PPA Economic Model](https://softwareengineerprogrammer.github.io/GEOPHIRES/SAM-Economic-Models.html#sam-single-owner-ppa). -Key case study results include LCOE = $75.5/MWh and CAPEX = $4290/kW. +Key case study results include LCOE = $75.5/MWh and CAPEX = $4900/kW. [Click here](https://gtp.scientificwebservices.com/geophires/?geophires-example-id=Fervo_Project_Cape-4) to interactively explore the case study in the GEOPHIRES web interface. @@ -80,20 +80,21 @@ in source code for the complete results. ### Economic Results -| Metric | Result Value | Reference Value(s) | Reference Source | -|------------------------------------|----------------------------------------------------------|--------------------------|---------------------------------------------| -| LCOE | $75.5/MWh | $80/MWh | Horne et al, 2025 | -| Project capital costs: Total CAPEX | $2.64B | | | -| Project capital costs: $/kW | $4290/kW (based on maximum total electricity generation) | $4500/kW, $3000–$6000/kW | Horne et al, 2025; Latimer, 2025. | -| Well Drilling and Completion Cost | $3.96M/well (including 5% indirect costs) | $<4M/well | Latimer, 2025. | -| WACC | 8.3% | 8.3% | Matson, 2024. | -| After-tax IRR | 31.5% | 15–25% | Typical levered returns for energy projects | +| Metric | Result Value | Reference Value(s) | Reference Source | +|------------------------------------|--------------------------------------------------------|------------------------------------|--------------------------------------------------| +| LCOE | $75.5/MWh | $80/MWh | Horne et al, 2025 | +| Project capital costs: Total CAPEX | $2.64B | | | +| Project capital costs: $/kW | $4900/kW (based on maximum net electricity generation) | $5000/kW; $4500/kW; $3000–$6000/kW | McClure, 2024; Horne et al, 2025; Latimer, 2025. | +| Well Drilling and Completion Cost | $3.96M/well (including 5% indirect costs) | $<4M/well | Latimer, 2025. | +| WACC | 8.3% | 8.3% | Matson, 2024. | +| After-tax IRR | 31.5% | 15–25% | Typical levered returns for energy projects | ### Technical & Engineering Results | Metric | Result Value | Reference Value(s) | Reference Source | |-------------------------------------------------|--------------|----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Minimum Net Electricity Generation | 504 MW | 500 MW | Fervo Energy, 2025. The 500 MW PPA is interpreted to mean that Cape Station's net electricity generation must never fall below 500 MWe. | +| Maximum Net Electricity Generation | 537 MW | | | | Maximum Total Electricity Generation | 615 MW | | Actual maximum total generation may be bounded or constrained by modular power plant design not modeled in this case study. For example, a modular design with 50MW units may constrain maximum total generation to 600 MW. | | Number of times redrilling | 3 | 3–6 | Redrilling expected to be required within 5–10 years of project start | | Average Production Temperature | 199℃ | 204℃, 190.6–198.6℃ (optimal plant operating range) | Trent, 2024; Norbeck et al, 2024. | @@ -145,6 +146,10 @@ Matson, M. (2024, September 11). Fervo Energy Technology Day 2024: Entering "the Geothermal Energy. https://www.linkedin.com/pulse/fervo-energy-technology-day-2024-entering-geothermal-decade-matson-n4stc/ +McClure, M. (2024, September 12). Digesting the Bonkers, Incredible, Off-the-Charts, Spectacular Results from the Fervo +and FORGE Enhanced Geothermal Projects. ResFrac Corporation Blog. +https://www.resfrac.com/blog/digesting-the-bonkers-incredible-off-the-charts-spectacular-results-from-the-fervo-and-forge-enhanced-geothermal-projects + NREL. (2024). Annual Technology Baseline: Recent Public Geothermal Power Purchase Agreement Pricing. https://atb.nrel.gov/electricity/2024/geothermal diff --git a/docs/conf.py b/docs/conf.py index c73260c4..f4601138 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.27' +version = release = '3.9.28' pygments_style = 'trac' templates_path = ['./templates'] diff --git a/setup.py b/setup.py index 051f0bae..e7775dbf 100755 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ def read(*names, **kwargs): setup( name='geophires-x', - version='3.9.27', + version='3.9.28', license='MIT', description='GEOPHIRES is a free and open-source geothermal techno-economic simulator.', long_description='{}\n{}'.format( diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 5cf3f02a..06de9ae2 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -1695,7 +1695,8 @@ def __init__(self, model: Model): display_name='Water costs', UnitType=Units.CURRENCYFREQUENCY, PreferredUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR, - CurrentUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR + CurrentUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR, + ToolTipText='Assumes $3.5/1,000 gallons of water' ) self.CCap = self.OutputParameterDict[self.CCap.Name] = OutputParameter( Name="Total Capital Cost", @@ -2490,6 +2491,7 @@ def Calculate(self, model: Model) -> None: self.Coamwater.value = self.oamwaterfixed.value else: # here is assumed 1 l per kg maybe correct with real temp. (M$/year) 925$/ML = 3.5$/1,000 gallon + # TODO parameterize self.Coamwater.value = self.oamwateradjfactor.value * (model.wellbores.nprod.value * model.wellbores.prodwellflowrate.value * model.reserv.waterloss.value * model.surfaceplant.utilization_factor.value * diff --git a/src/geophires_x/SBTEconomics.py b/src/geophires_x/SBTEconomics.py index 3d36aa68..c39efc73 100644 --- a/src/geophires_x/SBTEconomics.py +++ b/src/geophires_x/SBTEconomics.py @@ -640,6 +640,7 @@ def Calculate(self, model: Model) -> None: self.Coamwater.value = self.oamwaterfixed.value else: # here is assumed 1 l per kg maybe correct with real temp. (M$/year) 925$/ML = 3.5$/1,000 gallon + # TODO parameterize self.Coamwater.value = self.oamwateradjfactor.value * (model.wellbores.nprod.value * model.wellbores.prodwellflowrate.value * model.reserv.waterloss.value * model.surfaceplant.utilization_factor.value * diff --git a/src/geophires_x/WellBores.py b/src/geophires_x/WellBores.py index ec25bf92..efbbf178 100644 --- a/src/geophires_x/WellBores.py +++ b/src/geophires_x/WellBores.py @@ -708,24 +708,36 @@ def __init__(self, model: Model): self.ParameterDict = {} self.OutputParameterDict = {} + max_doublets = 200 + # noinspection SpellCheckingInspection self.nprod = self.ParameterDict[self.nprod.Name] = intParameter( "Number of Production Wells", DefaultValue=2, - AllowableRange=list(range(1, 201, 1)), + AllowableRange=list(range(1, max_doublets+1, 1)), UnitType=Units.NONE, - Required=True, + Required=False, ErrMessage="assume default number of production wells (2)", ToolTipText="Number of (identical) production wells" ) + # noinspection SpellCheckingInspection self.ninj = self.ParameterDict[self.ninj.Name] = intParameter( "Number of Injection Wells", DefaultValue=2, - AllowableRange=list(range(0, 201, 1)), + AllowableRange=list(range(0, max_doublets+1, 1)), UnitType=Units.NONE, - Required=True, + Required=False, ErrMessage="assume default number of injection wells (2)", ToolTipText="Number of (identical) injection wells" ) + self.doublets_count = self.ParameterDict[self.doublets_count.Name] = intParameter( + "Number of Doublets", + DefaultValue=2, + AllowableRange=list(range(0, max_doublets+1, 1)), + UnitType=Units.NONE, + ToolTipText="Pass this parameter to set the Number of Production Wells and Number of Injection Wells to " + "same value." + ) + self.prodwelldiam = self.ParameterDict[self.prodwelldiam.Name] = floatParameter( "Production Well Diameter", DefaultValue=8.0, @@ -1317,6 +1329,19 @@ def read_parameters(self, model: Model) -> None: coerce_int_params_to_enum_values(self.ParameterDict) + if self.doublets_count.Provided: + def _error(num_wells_param_:intParameter): + msg = f'{num_wells_param_.Name} may not be provided when {self.doublets_count.Name} is provided.' + model.logger.error(msg) + raise ValueError(msg) + + for num_wells_param in [self.ninj, self.nprod]: + if num_wells_param.Provided: + _error(num_wells_param) + + self.ninj.value = self.doublets_count.value + self.nprod.value = self.doublets_count.value + model.logger.info(f"read parameters complete {self.__class__.__name__}: {__name__}") def Calculate(self, model: Model) -> None: diff --git a/src/geophires_x/__init__.py b/src/geophires_x/__init__.py index 2c006aa1..636f44a8 100644 --- a/src/geophires_x/__init__.py +++ b/src/geophires_x/__init__.py @@ -1 +1 @@ -__version__ = '3.9.27' +__version__ = '3.9.28' diff --git a/src/geophires_x_schema_generator/geophires-request.json b/src/geophires_x_schema_generator/geophires-request.json index 1928dfd5..9dd80df7 100644 --- a/src/geophires_x_schema_generator/geophires-request.json +++ b/src/geophires_x_schema_generator/geophires-request.json @@ -637,6 +637,15 @@ "minimum": 0, "maximum": 200 }, + "Number of Doublets": { + "description": "Pass this parameter to set the Number of Production Wells and Number of Injection Wells to same value.", + "type": "integer", + "units": null, + "category": "Well Bores", + "default": 2, + "minimum": 0, + "maximum": 200 + }, "Production Well Diameter": { "description": "Inner diameter of production wellbore (assumed constant along the wellbore) to calculate frictional pressure drop and wellbore heat transmission with Rameys model", "type": "number", diff --git a/src/geophires_x_schema_generator/geophires-result.json b/src/geophires_x_schema_generator/geophires-result.json index 8fadbe38..54e53f20 100644 --- a/src/geophires_x_schema_generator/geophires-result.json +++ b/src/geophires_x_schema_generator/geophires-result.json @@ -415,7 +415,7 @@ }, "Water costs": { "type": "number", - "description": "O&M Make-up Water costs", + "description": "O&M Make-up Water costs. Assumes $3.5/1,000 gallons of water", "units": "MUSD/yr" }, "Average Reservoir Pumping Cost": {}, diff --git a/tests/examples/Fervo_Project_Cape-4.out b/tests/examples/Fervo_Project_Cape-4.out index 55113f51..60702917 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.24 - Simulation Date: 2025-06-20 - Simulation Time: 14:04 - Calculation Time: 1.575 sec + GEOPHIRES Version: 3.9.28 + Simulation Date: 2025-06-23 + Simulation Time: 10:15 + Calculation Time: 1.610 sec ***SUMMARY OF RESULTS*** diff --git a/tests/examples/Fervo_Project_Cape-4.txt b/tests/examples/Fervo_Project_Cape-4.txt index f07c8196..773c7519 100644 --- a/tests/examples/Fervo_Project_Cape-4.txt +++ b/tests/examples/Fervo_Project_Cape-4.txt @@ -55,8 +55,7 @@ Number of Fractures, 12036, -- 102 fractures per well Fracture Separation, 18, -- Per https://eartharxiv.org/repository/view/7665/, lateral length is 4700 ft = 1432 m. Dividing 1432 by 80 = ~18 m fracture spacing. Fracture Shape, 3, -- Square Fracture Height, 165.3, -- Based on total fracture surface area of 30 million ft^2 per well https://pangea.stanford.edu/ERE/pdf/IGAstandard/SGW/2025/Fercho.pdf -Number of Injection Wells, 59 -Number of Production Wells, 59 +Number of Doublets, 59 Production Flow Rate per Well, 107, -- Maximum flow rate achieved at Cape Station per https://www.businesswire.com/news/home/20240910997008/en/Fervo-Energys-Record-Breaking-Production-Results-Showcase-Rapid-Scale-Up-of-Enhanced-Geothermal Production Well Diameter, 9.625, -- Next standard size up from 7", implied by announcement of "increasing casing diameter" diff --git a/tests/geophires_x_tests/test_fervo_project_cape_4.py b/tests/geophires_x_tests/test_fervo_project_cape_4.py index 542114c1..4544759d 100644 --- a/tests/geophires_x_tests/test_fervo_project_cape_4.py +++ b/tests/geophires_x_tests/test_fervo_project_cape_4.py @@ -87,10 +87,10 @@ def __init__(self, vu: dict[str, Any]): capex_q = _Q(results_in_markdown['Project capital costs: Total CAPEX']).quantity() markdown_capex_USD_per_kW = ( capex_q.to('USD').magnitude - / _Q(results_in_markdown['Maximum Total Electricity Generation']).quantity().to('kW').magnitude + / _Q(results_in_markdown['Maximum Net Electricity Generation']).quantity().to('kW').magnitude ) self.assertAlmostEqual( - sig_figs(markdown_capex_USD_per_kW, 3), results_in_markdown['Project capital costs: $/kW']['value'] + sig_figs(markdown_capex_USD_per_kW, 2), results_in_markdown['Project capital costs: $/kW']['value'] ) field_mapping = { @@ -123,9 +123,9 @@ def __init__(self, vu: dict[str, Any]): result_capex_USD_per_kW = ( _Q(example_result._get_result_field('Total CAPEX')).quantity().to('USD').magnitude - / _Q(example_result._get_result_field('Maximum Total Electricity Generation')).quantity().to('kW').magnitude + / _Q(example_result._get_result_field('Maximum Net Electricity Generation')).quantity().to('kW').magnitude ) - self.assertAlmostEqual(sig_figs(result_capex_USD_per_kW, 3), sig_figs(markdown_capex_USD_per_kW, 3)) + self.assertAlmostEqual(sig_figs(result_capex_USD_per_kW, 2), sig_figs(markdown_capex_USD_per_kW, 2)) num_doublets = inputs_in_markdown['Number of Doublets']['value'] self.assertEqual( @@ -187,6 +187,7 @@ def parse_markdown_results_structured(self, markdown_text: str) -> dict: 'LCOE', 'Maximum Total Electricity Generation', 'Minimum Net Electricity Generation', + 'Maximum Net Electricity Generation', 'Number of times redrilling', 'Project capital costs: Total CAPEX', 'Project capital costs: $/kW', diff --git a/tests/geophires_x_tests/test_well_bores.py b/tests/geophires_x_tests/test_well_bores.py new file mode 100644 index 00000000..7db431f1 --- /dev/null +++ b/tests/geophires_x_tests/test_well_bores.py @@ -0,0 +1,99 @@ +from __future__ import annotations + +from base_test_case import BaseTestCase + +# ruff: noqa: I001 # Successful module initialization is dependent on this specific import order. + +# noinspection PyProtectedMember +from geophires_x_client import GeophiresInputParameters +from geophires_x_client import GeophiresXClient +from geophires_x_client import GeophiresXResult + + +class WellBoresTestCase(BaseTestCase): + + def test_number_of_doublets(self): + r_prod_inj: GeophiresXResult = self._get_result( + { + 'Number of Production Wells': 10, + 'Number of Injection Wells': 10, + } + ) + + r_doublets: GeophiresXResult = self._get_result( + { + 'Number of Doublets': 10, + } + ) + + self.assertEqual(self._prod_inj_lcoe_production(r_doublets), self._prod_inj_lcoe_production(r_prod_inj)) + + def test_number_of_doublets_validation(self): + with self.assertRaises(RuntimeError): + self._get_result( + { + 'Number of Production Wells': 10, + 'Number of Injection Wells': 10, + 'Number of Doublets': 10, + } + ) + + with self.assertRaises(RuntimeError): + self._get_result( + { + 'Number of Production Wells': 10, + 'Number of Doublets': 10, + } + ) + + with self.assertRaises(RuntimeError): + self._get_result( + { + 'Number of Injection Wells': 10, + 'Number of Doublets': 10, + } + ) + + def test_number_of_doublets_non_integer(self): + """ + Non-integer values are relevant for MC simulations, since distributions produce floats, and we want + Number of Doublets to be compatible with MC. + """ + + prod_inj_lcoe = self._prod_inj_lcoe_production( + self._get_result( + { + 'Number of Doublets': 40.7381, + } + ) + ) + + self.assertEqual(prod_inj_lcoe[0], 40) + self.assertEqual(prod_inj_lcoe[1], 40) + + prod_inj_lcoe_2 = self._prod_inj_lcoe_production( + self._get_result( + { + 'Number of Doublets': 199.2, + } + ) + ) + + self.assertEqual(prod_inj_lcoe_2[0], 199) + self.assertEqual(prod_inj_lcoe_2[1], 199) + + # noinspection PyMethodMayBeStatic + def _get_result(self, _params) -> GeophiresXResult: + params = GeophiresInputParameters( + {'Reservoir Depth': 5, 'Gradient 1': 74, 'Power Plant Type': 2, 'Maximum Temperature': 600, **_params} + ) + return GeophiresXClient().get_geophires_result(params) + + # noinspection PyMethodMayBeStatic + def _prod_inj_lcoe_production(self, _r: GeophiresXResult) -> tuple[int, int, float, float]: + return ( + _r.result['ENGINEERING PARAMETERS']['Number of Production Wells']['value'], + _r.result['ENGINEERING PARAMETERS']['Number of Injection Wells']['value'], + _r.result['SUMMARY OF RESULTS']['Electricity breakeven price']['value'], + _r.result['SURFACE EQUIPMENT SIMULATION RESULTS']['Average Net Electricity Generation']['value'], + )