Skip to content

Commit b142b10

Browse files
Merge pull request NREL#334 from softwareengineerprogrammer/main
Lateral sections default value & output fixes [v3.7.6]
2 parents 8102fb3 + a61e544 commit b142b10

31 files changed

+139
-80
lines changed

.bumpversion.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 3.7.5
2+
current_version = 3.7.6
33
commit = True
44
tag = True
55

.cookiecutterrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ default_context:
5454
sphinx_doctest: "no"
5555
sphinx_theme: "sphinx-py3doc-enhanced-theme"
5656
test_matrix_separate_coverage: "no"
57-
version: 3.7.5
57+
version: 3.7.6
5858
version_manager: "bump2version"
5959
website: "https://github.com/NREL"
6060
year_from: "2023"

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ GEOPHIRES-X (2023-2025)
1616

1717
3.7.5: Injection Well Drilling and Completion Capital Cost Adjustment Factor, if not provided, is now automatically set to the provided value of Well Drilling and Completion Capital Cost Adjustment Factor.
1818

19+
3.7.6: Fixes bugs pertaining to specifying non-CLGS laterals and display of per-lateral costs.
1920

2021
3.6
2122
^^^

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ Free software: `MIT license <LICENSE>`__
5656
:alt: Supported implementations
5757
:target: https://pypi.org/project/geophires-x
5858

59-
.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.7.5.svg
59+
.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.7.6.svg
6060
:alt: Commits since latest release
61-
:target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.7.5...main
61+
:target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.7.6...main
6262

6363
.. |docs| image:: https://readthedocs.org/projects/GEOPHIRES-X/badge/?style=flat
6464
:target: https://nrel.github.io/GEOPHIRES-X

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
year = '2025'
1919
author = 'NREL'
2020
copyright = f'{year}, {author}'
21-
version = release = '3.7.5'
21+
version = release = '3.7.6'
2222

2323
pygments_style = 'trac'
2424
templates_path = ['./templates']

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def read(*names, **kwargs):
1313

1414
setup(
1515
name='geophires-x',
16-
version='3.7.5',
16+
version='3.7.6',
1717
license='MIT',
1818
description='GEOPHIRES is a free and open-source geothermal techno-economic simulator.',
1919
long_description='{}\n{}'.format(

src/geophires_x/AGSEconomics.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,5 +259,6 @@ def Calculate(self, model: Model) -> None:
259259
self.Coam.value = self.AverageOPEX_Plant * 1000
260260
self.Coam.CurrentUnits = CurrencyFrequencyUnit.KDOLLARSPERYEAR
261261

262+
self._calculate_derived_outputs(model)
262263
model.logger.info(f'complete {__class__!s}: {sys._getframe().f_code.co_name}')
263264

src/geophires_x/AGSWellBores.py

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,11 @@ def __init__(self, model: Model):
515515
# NB: inputs we already have ("already have it") need to be set at ReadParameter time so values are set at the
516516
# last possible time
517517

518+
# Assume CLGS has 1 lateral by default (Non-CLGS default value is 0)
519+
self.numnonverticalsections.value = 1
520+
self.numnonverticalsections.ErrMessage = (f'assume default for Number of Nonvertical Wellbore Sections '
521+
f'({self.numnonverticalsections.value})')
522+
518523
self.time_operation = self.ParameterDict[self.time_operation.Name] = floatParameter(
519524
"Closed Loop Calculation Start Year",
520525
DefaultValue=0.01,
@@ -561,39 +566,28 @@ def read_parameters(self, model: Model) -> None:
561566
model.logger.info(f'Init {str(__class__)}: {sys._getframe().f_code.co_name}')
562567
super().read_parameters(model) # read the default parameters
563568
# if we call super, we don't need to deal with setting the parameters here, just deal with the special cases
564-
# for the variables in this class because the call to the super.readparameters will set all the variables,
569+
# for the variables in this class because the call to the super.read_parameters will set all the variables,
565570
# including the ones that are specific to this class
566571

567-
if len(model.InputParameters) > 0:
568-
# loop through all the parameters that the user wishes to set, looking for parameters that match this object
569-
for item in self.ParameterDict.items():
570-
ParameterToModify = item[1]
571-
key = ParameterToModify.Name.strip()
572-
if key in model.InputParameters:
573-
ParameterReadIn = model.InputParameters[key]
574-
# just handle special cases for this class - the call to super set all the values,
575-
# including the value unique to this class
576-
else:
577-
model.logger.info("No parameters read because no content provided")
578-
579572
# handle error checking and special cases:
573+
580574
if model.reserv.numseg.value > 1:
581-
msg = ('Warning: CLGS model can only handle a single layer gradient segment. '
575+
msg = ('CLGS model can only handle a single layer gradient segment. '
582576
'Number of Segments set to 1, Gradient set to Gradient[0], and Depth set to Reservoir Depth.')
583-
print(msg)
577+
print(f'Warning: {msg}')
584578
model.logger.warning(msg)
585579
model.reserv.numseg.value = 1
586580

587581
if self.ninj.value > 0:
588-
msg = ('Warning: CLGS model considers the only the production wellbore parameters. '
582+
msg = ('CLGS model considers the only the production wellbore parameters. '
589583
'Anything related to the injection wellbore is ignored.')
590-
print(msg)
584+
print(f'Warning: {msg}')
591585
model.logger.warning(msg)
592586

593587
if self.nprod.value != 1:
594-
msg = ('Warning: CLGS model considers the only a single production wellbore (coaxial or uloop). '
595-
'Number of production wellboreset set 1.')
596-
print(msg)
588+
msg = ('CLGS model considers the only a single production wellbore (coaxial or uloop). '
589+
'Number of production wellbores set to 1.')
590+
print(f'Warning: {msg}')
597591
model.logger.warning(msg)
598592

599593
# inputs we already have - needs to be set at ReadParameter time so values set at the latest possible time

src/geophires_x/Economics.py

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,8 @@ def calculate_cost_of_non_vertical_section(model: Model, length_m: float, well_c
120120
f'{fixed_well_cost_name} (fixed cost per well) instead.'
121121
)
122122

123-
casing_factor = 1.0
124-
if not NonverticalsCased:
125-
# assume that casing & cementing costs 50% of drilling costs
126-
casing_factor = 0.5
123+
# assume that casing & cementing costs 50% of drilling costs
124+
casing_factor = 1.0 if NonverticalsCased else 0.5
127125

128126
if model.economics.Nonvertical_drilling_cost_per_m.Provided or well_correlation is WellDrillingCostCorrelation.SIMPLE:
129127
cost_of_non_vertical_section = casing_factor * ((num_nonvertical_sections * nonvertical_drilling_cost_per_m * length_per_section_m)) * 1E-6
@@ -1003,8 +1001,8 @@ def __init__(self, model: Model):
10031001
PreferredUnits=CostPerDistanceUnit.DOLLARSPERM,
10041002
CurrentUnits=CostPerDistanceUnit.DOLLARSPERM,
10051003
ErrMessage="assume default all-in cost for drill vertical well segment(s) (1000 $/m)",
1006-
ToolTipText="Set user specified all-in cost per meter of vertical drilling," +
1007-
" including drilling, casing, cement, insulated insert"
1004+
ToolTipText="Set user specified all-in cost per meter of vertical drilling, including drilling, casing, "
1005+
"cement, insulated insert"
10081006
)
10091007
self.Nonvertical_drilling_cost_per_m = self.ParameterDict[
10101008
self.Nonvertical_drilling_cost_per_m.Name] = floatParameter(
@@ -1015,9 +1013,9 @@ def __init__(self, model: Model):
10151013
UnitType=Units.COSTPERDISTANCE,
10161014
PreferredUnits=CostPerDistanceUnit.DOLLARSPERM,
10171015
CurrentUnits=CostPerDistanceUnit.DOLLARSPERM,
1018-
ErrMessage="assume default all-in cost for drill non-vertical well segment(s) (1300 $/m)",
1019-
ToolTipText="Set user specified all-in cost per meter of non-vertical drilling, including" +
1020-
" drilling, casing, cement, insulated insert"
1016+
ErrMessage="assume default all-in cost for drill non-vertical well segment(s) ($1300/m)",
1017+
ToolTipText="Set user specified all-in cost per meter of non-vertical drilling, including drilling, "
1018+
"casing, cement, insulated insert"
10211019
)
10221020

10231021
# absorption chiller
@@ -1554,11 +1552,16 @@ def __init__(self, model: Model):
15541552
PreferredUnits=CurrencyUnit.MDOLLARS,
15551553
CurrentUnits=CurrencyUnit.MDOLLARS
15561554
)
1555+
15571556
self.Cwell = self.OutputParameterDict[self.Cwell.Name] = OutputParameter(
15581557
Name="Wellfield cost",
15591558
UnitType=Units.CURRENCY,
15601559
PreferredUnits=CurrencyUnit.MDOLLARS,
1561-
CurrentUnits=CurrencyUnit.MDOLLARS
1560+
CurrentUnits=CurrencyUnit.MDOLLARS,
1561+
1562+
# See TODO re:parameterizing indirect costs at src/geophires_x/Economics.py:652
1563+
ToolTipText="Includes total drilling and completion cost of all injection and production wells and "
1564+
"laterals, plus 5% indirect costs."
15621565
)
15631566
self.Coamwell = self.OutputParameterDict[self.Coamwell.Name] = OutputParameter(
15641567
Name="O&M Wellfield cost",
@@ -1797,6 +1800,12 @@ def __init__(self, model: Model):
17971800
PreferredUnits=CurrencyUnit.MDOLLARS,
17981801
CurrentUnits=CurrencyUnit.MDOLLARS
17991802
)
1803+
self.cost_per_lateral_section = self.OutputParameterDict[self.cost_per_lateral_section.Name] = OutputParameter(
1804+
Name='Drilling and completion costs per non-vertical section',
1805+
UnitType=Units.CURRENCY,
1806+
PreferredUnits=CurrencyUnit.MDOLLARS,
1807+
CurrentUnits=CurrencyUnit.MDOLLARS
1808+
)
18001809
self.cost_to_junction_section = self.OutputParameterDict[self.cost_to_junction_section.Name] = OutputParameter(
18011810
Name="Cost of the entire section of a well from bottom of vertical to junction with laterals",
18021811
UnitType=Units.CURRENCY,
@@ -2286,13 +2295,16 @@ def Calculate(self, model: Model) -> None:
22862295
self.injection_well_cost_adjustment_factor.value)
22872296

22882297
if hasattr(model.wellbores, 'numnonverticalsections') and model.wellbores.numnonverticalsections.Provided:
2289-
self.cost_lateral_section.value = calculate_cost_of_non_vertical_section(model, tot_horiz_m,
2290-
self.wellcorrelation.value,
2291-
self.Nonvertical_drilling_cost_per_m.value,
2292-
model.wellbores.numnonverticalsections.value,
2293-
self.per_injection_well_cost.Name,
2294-
model.wellbores.NonverticalsCased.value,
2295-
self.production_well_cost_adjustment_factor.value)
2298+
self.cost_lateral_section.value = calculate_cost_of_non_vertical_section(
2299+
model,
2300+
tot_horiz_m,
2301+
self.wellcorrelation.value,
2302+
self.Nonvertical_drilling_cost_per_m.value,
2303+
model.wellbores.numnonverticalsections.value,
2304+
self.Nonvertical_drilling_cost_per_m.Name,
2305+
model.wellbores.NonverticalsCased.value,
2306+
self.production_well_cost_adjustment_factor.value
2307+
)
22962308
else:
22972309
self.cost_lateral_section.value = 0.0
22982310
# cost of the well field
@@ -2881,7 +2893,20 @@ def Calculate(self, model: Model) -> None:
28812893
np.average(model.surfaceplant.ElectricityProduced.quantity().to(
28822894
'MW').magnitude * self.jobs_created_per_MW_electricity.value))
28832895

2896+
self._calculate_derived_outputs(model)
28842897
model.logger.info(f'complete {__class__!s}: {sys._getframe().f_code.co_name}')
28852898

2899+
def _calculate_derived_outputs(self, model: Model) -> None:
2900+
"""
2901+
Subclasses should call _calculate_derived_outputs at the end of their Calculate methods to populate output
2902+
values that are derived from subclass-calculated outputs.
2903+
"""
2904+
2905+
if hasattr(self, 'cost_lateral_section') and self.cost_lateral_section.value != 0:
2906+
self.cost_per_lateral_section.value = (
2907+
self.cost_lateral_section.quantity().to(self.cost_per_lateral_section.CurrentUnits).magnitude
2908+
/ model.wellbores.numnonverticalsections.value
2909+
)
2910+
28862911
def __str__(self):
28872912
return "Economics"

src/geophires_x/EconomicsAddOns.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ def Calculate(self, model: Model) -> None:
373373
# recalculate LCOE/LCOH
374374
self.LCOE.value, self.LCOH.value, LCOC = Economics.CalculateLCOELCOHLCOC(self, model)
375375

376+
self._calculate_derived_outputs(model)
376377
model.logger.info(f'complete {str(__class__)}: {sys._getframe().f_code.co_name}')
377378

378379
def __str__(self):

0 commit comments

Comments
 (0)