From 404acd3b02598f4f17243b9ae6cc7082e0a8606b Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 8 Jan 2026 15:23:10 -0800 Subject: [PATCH 1/7] Add Number of Injection Wells per Production Well parameter --- src/geophires_x/WellBores.py | 38 ++++++++++++++++++---- tests/geophires_x_tests/test_well_bores.py | 14 ++++++++ 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/geophires_x/WellBores.py b/src/geophires_x/WellBores.py index 9e6e9249e..2de00703a 100644 --- a/src/geophires_x/WellBores.py +++ b/src/geophires_x/WellBores.py @@ -740,6 +740,15 @@ def __init__(self, model: Model): ToolTipText="Pass this parameter to set the Number of Production Wells and Number of Injection Wells to " "same value." ) + self.ninj_per_production_well = self.ParameterDict[self.ninj_per_production_well.Name] = floatParameter( + "Number of Injection Wells per Production Well", + DefaultValue=1, + Min=0, + Max=max_doublets-1, + UnitType=Units.NONE, + Required=False, + ToolTipText="Number of (identical) injection wells per production well" + ) # noinspection SpellCheckingInspection self.prodwelldiam = self.ParameterDict[self.prodwelldiam.Name] = floatParameter( @@ -1361,20 +1370,35 @@ 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) + self._set_well_counts_from_parameters(model) + + model.logger.info(f"read parameters complete {self.__class__.__name__}: {__name__}") + def _set_well_counts_from_parameters(self, model: Model): + mutually_exclusive_well_count_params = [self.doublets_count, self.ninj_per_production_well] + provided_well_count_params = [it for it in mutually_exclusive_well_count_params if it.Provided] + if len(provided_well_count_params) > 1: + raise ValueError(f'Only one of [{", ".join([it.Name for it in mutually_exclusive_well_count_params])}] ' + f'may be provided.') + + def _raise_incompatible_param_error(incompatible_param: intParameter, with_param: intParameter): + msg = f'{incompatible_param.Name} may not be provided when {with_param.Name} is provided.' + model.logger.error(msg) + raise ValueError(msg) + + if self.doublets_count.Provided: for num_wells_param in [self.ninj, self.nprod]: if num_wells_param.Provided: - _error(num_wells_param) + _raise_incompatible_param_error(num_wells_param, self.doublets_count) 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__}") + if self.ninj_per_production_well.Provided: + if self.ninj.Provided: + _raise_incompatible_param_error(self.ninj, self.ninj_per_production_well) + + self.ninj.value = int(math.ceil(self.nprod.value * self.ninj_per_production_well.value)) def Calculate(self, model: Model) -> None: """ diff --git a/tests/geophires_x_tests/test_well_bores.py b/tests/geophires_x_tests/test_well_bores.py index 7db431f1b..8f665159a 100644 --- a/tests/geophires_x_tests/test_well_bores.py +++ b/tests/geophires_x_tests/test_well_bores.py @@ -82,6 +82,20 @@ def test_number_of_doublets_non_integer(self): self.assertEqual(prod_inj_lcoe_2[0], 199) self.assertEqual(prod_inj_lcoe_2[1], 199) + def test_number_of_injection_wells_per_production_well(self): + r_ratio: GeophiresXResult = self._get_result( + { + 'Number of Production Wells': 63, + 'Number of Injection Wells per Production Well': 0.6666, # 3:2 ratio + } + ) + + r_explicit_counts: GeophiresXResult = self._get_result( + {'Number of Production Wells': 63, 'Number of Injection Wells': 42} + ) + + self.assertEqual(self._prod_inj_lcoe_production(r_explicit_counts), self._prod_inj_lcoe_production(r_ratio)) + # noinspection PyMethodMayBeStatic def _get_result(self, _params) -> GeophiresXResult: params = GeophiresInputParameters( From b26e8cfce6e765296beefe70da8c5024b6d2a62c Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 8 Jan 2026 15:36:18 -0800 Subject: [PATCH 2/7] additional test coverage --- tests/geophires_x_tests/test_well_bores.py | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/geophires_x_tests/test_well_bores.py b/tests/geophires_x_tests/test_well_bores.py index 8f665159a..56bd291e5 100644 --- a/tests/geophires_x_tests/test_well_bores.py +++ b/tests/geophires_x_tests/test_well_bores.py @@ -96,6 +96,36 @@ def test_number_of_injection_wells_per_production_well(self): self.assertEqual(self._prod_inj_lcoe_production(r_explicit_counts), self._prod_inj_lcoe_production(r_ratio)) + self.assertEqual( + self._prod_inj_lcoe_production( + self._get_result( + { + 'Number of Production Wells': 2, # default value + 'Number of Injection Wells per Production Well': 3, + } + ) + ), + self._prod_inj_lcoe_production(self._get_result({'Number of Injection Wells per Production Well': 3})), + ) + + with self.assertRaises(RuntimeError): + self._get_result( + { + 'Number of Production Wells': 63, + 'Number of Injection Wells per Production Well': 0.6666, # 3:2 ratio + 'Number of Injection Wells': 42, + } + ) + + with self.assertRaises(RuntimeError): + self._get_result( + { + 'Number of Production Wells': 63, + 'Number of Injection Wells per Production Well': 0.6666, # 3:2 ratio + 'Number of Doublets': 52, + } + ) + # noinspection PyMethodMayBeStatic def _get_result(self, _params) -> GeophiresXResult: params = GeophiresInputParameters( From e950ffe1870608a11de93bcc65fc1c4a449da17d Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 8 Jan 2026 15:37:23 -0800 Subject: [PATCH 3/7] improve tooltip text and regenerate schema --- src/geophires_x/WellBores.py | 4 +++- src/geophires_x_schema_generator/geophires-request.json | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/geophires_x/WellBores.py b/src/geophires_x/WellBores.py index 2de00703a..8d097be6b 100644 --- a/src/geophires_x/WellBores.py +++ b/src/geophires_x/WellBores.py @@ -747,7 +747,9 @@ def __init__(self, model: Model): Max=max_doublets-1, UnitType=Units.NONE, Required=False, - ToolTipText="Number of (identical) injection wells per production well" + ToolTipText="Number of (identical) injection wells per production well. " + "For example, provide 0.666 to specify a 3:2 production:injection well ratio. " + "The number of injection wells will be rounded up to the nearest integer." ) # noinspection SpellCheckingInspection diff --git a/src/geophires_x_schema_generator/geophires-request.json b/src/geophires_x_schema_generator/geophires-request.json index be86aef39..2a83bd355 100644 --- a/src/geophires_x_schema_generator/geophires-request.json +++ b/src/geophires_x_schema_generator/geophires-request.json @@ -664,6 +664,15 @@ "minimum": 0, "maximum": 200 }, + "Number of Injection Wells per Production Well": { + "description": "Number of (identical) injection wells per production well. For example, provide 0.666 to specify a 3:2 production:injection well ratio. The number of injection wells will be rounded up to the nearest integer.", + "type": "number", + "units": null, + "category": "Well Bores", + "default": 1, + "minimum": 0, + "maximum": 199 + }, "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", From 0584418f5bf0aa7bc76cea75754768cc0733b44e Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 8 Jan 2026 15:41:42 -0800 Subject: [PATCH 4/7] align unit test value precision with tooltip text example --- tests/geophires_x_tests/test_well_bores.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/geophires_x_tests/test_well_bores.py b/tests/geophires_x_tests/test_well_bores.py index 56bd291e5..30b2ebace 100644 --- a/tests/geophires_x_tests/test_well_bores.py +++ b/tests/geophires_x_tests/test_well_bores.py @@ -86,7 +86,7 @@ def test_number_of_injection_wells_per_production_well(self): r_ratio: GeophiresXResult = self._get_result( { 'Number of Production Wells': 63, - 'Number of Injection Wells per Production Well': 0.6666, # 3:2 ratio + 'Number of Injection Wells per Production Well': 0.666, # 3:2 ratio } ) From d184c55743150cee05ebbf180e5e284d146be1ad Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 8 Jan 2026 15:43:02 -0800 Subject: [PATCH 5/7] suppress spell-check for param name (ninj used for consistency with existing injection well count parameter) --- src/geophires_x/WellBores.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/geophires_x/WellBores.py b/src/geophires_x/WellBores.py index 8d097be6b..c4cddeb07 100644 --- a/src/geophires_x/WellBores.py +++ b/src/geophires_x/WellBores.py @@ -740,6 +740,7 @@ def __init__(self, model: Model): ToolTipText="Pass this parameter to set the Number of Production Wells and Number of Injection Wells to " "same value." ) + # noinspection SpellCheckingInspection self.ninj_per_production_well = self.ParameterDict[self.ninj_per_production_well.Name] = floatParameter( "Number of Injection Wells per Production Well", DefaultValue=1, From 9a2a5c9b986b750d4c1014093f2b0bf9a503a56d Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 8 Jan 2026 15:48:59 -0800 Subject: [PATCH 6/7] =?UTF-8?q?Bump=20version:=203.10.24=20=E2=86=92=203.1?= =?UTF-8?q?0.25?= 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 b16532369..d397297b7 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.10.24 +current_version = 3.10.25 commit = True tag = True diff --git a/.cookiecutterrc b/.cookiecutterrc index 56f2be8c4..1efcb17eb 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.10.24 + version: 3.10.25 version_manager: "bump2version" website: "https://github.com/NREL" year_from: "2023" diff --git a/README.rst b/README.rst index f912204ab..1c97d6b29 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.10.24.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.10.25.svg :alt: Commits since latest release - :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.10.24...main + :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.10.25...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 26674a995..011468e17 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,7 +18,7 @@ year = '2025' author = 'NREL' copyright = f'{year}, {author}' -version = release = '3.10.24' +version = release = '3.10.25' pygments_style = 'trac' templates_path = ['./templates'] diff --git a/setup.py b/setup.py index 7aa1b7fe8..ff1b24c85 100755 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ def read(*names, **kwargs): setup( name='geophires-x', - version='3.10.24', + version='3.10.25', 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 8a9ccd93e..891f6ea2f 100644 --- a/src/geophires_x/__init__.py +++ b/src/geophires_x/__init__.py @@ -1 +1 @@ -__version__ = '3.10.24' +__version__ = '3.10.25' From c80bfea3a3930dd75539db034395c2c2f6040880 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 8 Jan 2026 15:50:11 -0800 Subject: [PATCH 7/7] v3.10.25 CHANGELOG entry --- CHANGELOG.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0c2f9f01e..b817a7080 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,6 +8,8 @@ GEOPHIRES v3 (2023-2025) 3.10 ^^^^ +3.10.25: `Add Number of Injection Wells per Production Well parameter `__ + 3.10: `SAM Economic Models: Multiple Construction Years; Number of Fractures per Stimulated Well parameter; Royalty Rate Escalation Start Year parameter `__ | `release `__ 3.9