Skip to content

Commit

Permalink
Merge pull request #334 from softwareengineerprogrammer/main
Browse files Browse the repository at this point in the history
Lateral sections default value & output fixes [v3.7.6]
  • Loading branch information
softwareengineerprogrammer authored Feb 6, 2025
2 parents 8102fb3 + a61e544 commit b142b10
Show file tree
Hide file tree
Showing 31 changed files with 139 additions and 80 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 3.7.5
current_version = 3.7.6
commit = True
tag = True

Expand Down
2 changes: 1 addition & 1 deletion .cookiecutterrc
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ default_context:
sphinx_doctest: "no"
sphinx_theme: "sphinx-py3doc-enhanced-theme"
test_matrix_separate_coverage: "no"
version: 3.7.5
version: 3.7.6
version_manager: "bump2version"
website: "https://github.com/NREL"
year_from: "2023"
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ GEOPHIRES-X (2023-2025)

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.

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

3.6
^^^
Expand Down
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ Free software: `MIT license <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.7.5.svg
.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.7.6.svg
:alt: Commits since latest release
:target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.7.5...main
:target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.7.6...main

.. |docs| image:: https://readthedocs.org/projects/GEOPHIRES-X/badge/?style=flat
:target: https://nrel.github.io/GEOPHIRES-X
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
year = '2025'
author = 'NREL'
copyright = f'{year}, {author}'
version = release = '3.7.5'
version = release = '3.7.6'

pygments_style = 'trac'
templates_path = ['./templates']
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def read(*names, **kwargs):

setup(
name='geophires-x',
version='3.7.5',
version='3.7.6',
license='MIT',
description='GEOPHIRES is a free and open-source geothermal techno-economic simulator.',
long_description='{}\n{}'.format(
Expand Down
1 change: 1 addition & 0 deletions src/geophires_x/AGSEconomics.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,5 +259,6 @@ def Calculate(self, model: Model) -> None:
self.Coam.value = self.AverageOPEX_Plant * 1000
self.Coam.CurrentUnits = CurrencyFrequencyUnit.KDOLLARSPERYEAR

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

34 changes: 14 additions & 20 deletions src/geophires_x/AGSWellBores.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,11 @@ def __init__(self, model: Model):
# NB: inputs we already have ("already have it") need to be set at ReadParameter time so values are set at the
# last possible time

# Assume CLGS has 1 lateral by default (Non-CLGS default value is 0)
self.numnonverticalsections.value = 1
self.numnonverticalsections.ErrMessage = (f'assume default for Number of Nonvertical Wellbore Sections '
f'({self.numnonverticalsections.value})')

self.time_operation = self.ParameterDict[self.time_operation.Name] = floatParameter(
"Closed Loop Calculation Start Year",
DefaultValue=0.01,
Expand Down Expand Up @@ -561,39 +566,28 @@ def read_parameters(self, model: Model) -> None:
model.logger.info(f'Init {str(__class__)}: {sys._getframe().f_code.co_name}')
super().read_parameters(model) # read the default parameters
# if we call super, we don't need to deal with setting the parameters here, just deal with the special cases
# for the variables in this class because the call to the super.readparameters will set all the variables,
# for the variables in this class because the call to the super.read_parameters will set all the variables,
# including the ones that are specific to this class

if len(model.InputParameters) > 0:
# loop through all the parameters that the user wishes to set, looking for parameters that match this object
for item in self.ParameterDict.items():
ParameterToModify = item[1]
key = ParameterToModify.Name.strip()
if key in model.InputParameters:
ParameterReadIn = model.InputParameters[key]
# just handle special cases for this class - the call to super set all the values,
# including the value unique to this class
else:
model.logger.info("No parameters read because no content provided")

# handle error checking and special cases:

if model.reserv.numseg.value > 1:
msg = ('Warning: CLGS model can only handle a single layer gradient segment. '
msg = ('CLGS model can only handle a single layer gradient segment. '
'Number of Segments set to 1, Gradient set to Gradient[0], and Depth set to Reservoir Depth.')
print(msg)
print(f'Warning: {msg}')
model.logger.warning(msg)
model.reserv.numseg.value = 1

if self.ninj.value > 0:
msg = ('Warning: CLGS model considers the only the production wellbore parameters. '
msg = ('CLGS model considers the only the production wellbore parameters. '
'Anything related to the injection wellbore is ignored.')
print(msg)
print(f'Warning: {msg}')
model.logger.warning(msg)

if self.nprod.value != 1:
msg = ('Warning: CLGS model considers the only a single production wellbore (coaxial or uloop). '
'Number of production wellboreset set 1.')
print(msg)
msg = ('CLGS model considers the only a single production wellbore (coaxial or uloop). '
'Number of production wellbores set to 1.')
print(f'Warning: {msg}')
model.logger.warning(msg)

# inputs we already have - needs to be set at ReadParameter time so values set at the latest possible time
Expand Down
59 changes: 42 additions & 17 deletions src/geophires_x/Economics.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,8 @@ def calculate_cost_of_non_vertical_section(model: Model, length_m: float, well_c
f'{fixed_well_cost_name} (fixed cost per well) instead.'
)

casing_factor = 1.0
if not NonverticalsCased:
# assume that casing & cementing costs 50% of drilling costs
casing_factor = 0.5
# assume that casing & cementing costs 50% of drilling costs
casing_factor = 1.0 if NonverticalsCased else 0.5

if model.economics.Nonvertical_drilling_cost_per_m.Provided or well_correlation is WellDrillingCostCorrelation.SIMPLE:
cost_of_non_vertical_section = casing_factor * ((num_nonvertical_sections * nonvertical_drilling_cost_per_m * length_per_section_m)) * 1E-6
Expand Down Expand Up @@ -1003,8 +1001,8 @@ def __init__(self, model: Model):
PreferredUnits=CostPerDistanceUnit.DOLLARSPERM,
CurrentUnits=CostPerDistanceUnit.DOLLARSPERM,
ErrMessage="assume default all-in cost for drill vertical well segment(s) (1000 $/m)",
ToolTipText="Set user specified all-in cost per meter of vertical drilling," +
" including drilling, casing, cement, insulated insert"
ToolTipText="Set user specified all-in cost per meter of vertical drilling, including drilling, casing, "
"cement, insulated insert"
)
self.Nonvertical_drilling_cost_per_m = self.ParameterDict[
self.Nonvertical_drilling_cost_per_m.Name] = floatParameter(
Expand All @@ -1015,9 +1013,9 @@ def __init__(self, model: Model):
UnitType=Units.COSTPERDISTANCE,
PreferredUnits=CostPerDistanceUnit.DOLLARSPERM,
CurrentUnits=CostPerDistanceUnit.DOLLARSPERM,
ErrMessage="assume default all-in cost for drill non-vertical well segment(s) (1300 $/m)",
ToolTipText="Set user specified all-in cost per meter of non-vertical drilling, including" +
" drilling, casing, cement, insulated insert"
ErrMessage="assume default all-in cost for drill non-vertical well segment(s) ($1300/m)",
ToolTipText="Set user specified all-in cost per meter of non-vertical drilling, including drilling, "
"casing, cement, insulated insert"
)

# absorption chiller
Expand Down Expand Up @@ -1554,11 +1552,16 @@ def __init__(self, model: Model):
PreferredUnits=CurrencyUnit.MDOLLARS,
CurrentUnits=CurrencyUnit.MDOLLARS
)

self.Cwell = self.OutputParameterDict[self.Cwell.Name] = OutputParameter(
Name="Wellfield cost",
UnitType=Units.CURRENCY,
PreferredUnits=CurrencyUnit.MDOLLARS,
CurrentUnits=CurrencyUnit.MDOLLARS
CurrentUnits=CurrencyUnit.MDOLLARS,

# See TODO re:parameterizing indirect costs at src/geophires_x/Economics.py:652
ToolTipText="Includes total drilling and completion cost of all injection and production wells and "
"laterals, plus 5% indirect costs."
)
self.Coamwell = self.OutputParameterDict[self.Coamwell.Name] = OutputParameter(
Name="O&M Wellfield cost",
Expand Down Expand Up @@ -1797,6 +1800,12 @@ def __init__(self, model: Model):
PreferredUnits=CurrencyUnit.MDOLLARS,
CurrentUnits=CurrencyUnit.MDOLLARS
)
self.cost_per_lateral_section = self.OutputParameterDict[self.cost_per_lateral_section.Name] = OutputParameter(
Name='Drilling and completion costs per non-vertical section',
UnitType=Units.CURRENCY,
PreferredUnits=CurrencyUnit.MDOLLARS,
CurrentUnits=CurrencyUnit.MDOLLARS
)
self.cost_to_junction_section = self.OutputParameterDict[self.cost_to_junction_section.Name] = OutputParameter(
Name="Cost of the entire section of a well from bottom of vertical to junction with laterals",
UnitType=Units.CURRENCY,
Expand Down Expand Up @@ -2286,13 +2295,16 @@ def Calculate(self, model: Model) -> None:
self.injection_well_cost_adjustment_factor.value)

if hasattr(model.wellbores, 'numnonverticalsections') and model.wellbores.numnonverticalsections.Provided:
self.cost_lateral_section.value = calculate_cost_of_non_vertical_section(model, tot_horiz_m,
self.wellcorrelation.value,
self.Nonvertical_drilling_cost_per_m.value,
model.wellbores.numnonverticalsections.value,
self.per_injection_well_cost.Name,
model.wellbores.NonverticalsCased.value,
self.production_well_cost_adjustment_factor.value)
self.cost_lateral_section.value = calculate_cost_of_non_vertical_section(
model,
tot_horiz_m,
self.wellcorrelation.value,
self.Nonvertical_drilling_cost_per_m.value,
model.wellbores.numnonverticalsections.value,
self.Nonvertical_drilling_cost_per_m.Name,
model.wellbores.NonverticalsCased.value,
self.production_well_cost_adjustment_factor.value
)
else:
self.cost_lateral_section.value = 0.0
# cost of the well field
Expand Down Expand Up @@ -2881,7 +2893,20 @@ def Calculate(self, model: Model) -> None:
np.average(model.surfaceplant.ElectricityProduced.quantity().to(
'MW').magnitude * self.jobs_created_per_MW_electricity.value))

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

def _calculate_derived_outputs(self, model: Model) -> None:
"""
Subclasses should call _calculate_derived_outputs at the end of their Calculate methods to populate output
values that are derived from subclass-calculated outputs.
"""

if hasattr(self, 'cost_lateral_section') and self.cost_lateral_section.value != 0:
self.cost_per_lateral_section.value = (
self.cost_lateral_section.quantity().to(self.cost_per_lateral_section.CurrentUnits).magnitude
/ model.wellbores.numnonverticalsections.value
)

def __str__(self):
return "Economics"
1 change: 1 addition & 0 deletions src/geophires_x/EconomicsAddOns.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ def Calculate(self, model: Model) -> None:
# recalculate LCOE/LCOH
self.LCOE.value, self.LCOH.value, LCOC = Economics.CalculateLCOELCOHLCOC(self, model)

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

def __str__(self):
Expand Down
3 changes: 2 additions & 1 deletion src/geophires_x/EconomicsCCUS.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,8 @@ def Calculate(self, model) -> None:
self.ProjectMOIC.value = self.ProjectCummCashFlow.value[len(self.ProjectCummCashFlow.value) - 1] / (
model.economics.CCap.value + (model.economics.Coam.value * model.surfaceplant.plant_lifetime.value))

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

def __str__(self):
return "EconomicsCCUS"
4 changes: 3 additions & 1 deletion src/geophires_x/EconomicsS_DAC_GT.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,4 +676,6 @@ def Calculate(self, model: Model) -> None:
# (self.CarbonExtractedAnnually.value[i] * model.economics.CarbonPrice.value[i]))
# if i > 0:
# model.economics.CarbonCummCashFlow.value[i] = model.economics.CarbonCummCashFlow.value[i - 1] + model.economics.CarbonRevenue.value[i]
model.logger.info("Complete " + str(__class__) + ": " + sys._getframe().f_code.co_name)

self._calculate_derived_outputs(model)
model.logger.info(f'Complete {str(__class__)}: {sys._getframe().f_code.co_name}')
2 changes: 1 addition & 1 deletion src/geophires_x/GeoPHIRESUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ def heat_capacity_water_J_per_kg_per_K(
max_allowed_temp_degC = 600
if not isinstance(Twater_degC, numbers.Real) or Twater_degC < 0 or Twater_degC > max_allowed_temp_degC:
raise ValueError(
f'Invalid input for Twater_degC.'
f'Invalid input for Twater_degC. '
f'Twater_degC must be a non-negative number and must be within the range of 0 to {max_allowed_temp_degC} '
f'degrees Celsius. The input value was: {Twater_degC}'
)
Expand Down
2 changes: 1 addition & 1 deletion src/geophires_x/Outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1810,7 +1810,7 @@ def PrintOutputs(self, model: Model):
elif econ.cost_lateral_section.value > 0.0:
f.write(f' Drilling and completion costs per vertical production well: {econ.cost_one_production_well.value:10.2f} ' + econ.cost_one_production_well.CurrentUnits.value + NL)
f.write(f' Drilling and completion costs per vertical injection well: {econ.cost_one_injection_well.value:10.2f} ' + econ.cost_one_injection_well.CurrentUnits.value + NL)
f.write(f' Drilling and completion costs per non-vertical section: {econ.cost_lateral_section.value:10.2f} ' + econ.cost_lateral_section.CurrentUnits.value + NL)
f.write(f' {econ.cost_per_lateral_section.Name}: {econ.cost_per_lateral_section.value:10.2f} {econ.cost_lateral_section.CurrentUnits.value}\n')
else:
f.write(f' Drilling and completion costs per 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: {model.economics.Cstim.value:10.2f} ' + model.economics.Cstim.CurrentUnits.value + NL)
Expand Down
4 changes: 2 additions & 2 deletions src/geophires_x/Reservoir.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ def __init__(self, model: Model):
CurrentUnits=TemperatureUnit.CELSIUS,
Required=True,
ErrMessage="assume default maximum temperature (400 deg.C)",
ToolTipText="Maximum allowable reservoir temperature (e.g. due to drill bit or logging tools constraints). \
GEOPHIRES will cap the drilling depth to stay below this maximum temperature."
ToolTipText="Maximum allowable reservoir temperature (e.g. due to drill bit or logging tools constraints). "
"GEOPHIRES will cap the drilling depth to stay below this maximum temperature."
)

self.numseg = self.ParameterDict[self.numseg.Name] = intParameter(
Expand Down
1 change: 1 addition & 0 deletions src/geophires_x/SBTEconomics.py
Original file line number Diff line number Diff line change
Expand Up @@ -845,5 +845,6 @@ def Calculate(self, model: Model) -> None:
# Calculate LCOE/LCOH
self.LCOE.value, self.LCOH.value, self.LCOC.value = CalculateLCOELCOHLCOC(self, model)

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

5 changes: 5 additions & 0 deletions src/geophires_x/SBTWellbores.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ def __init__(self, model: Model):
# NB: inputs we already have ("already have it") need to be set at ReadParameter time so values are set at the
# last possible time

# Assume CLGS has 1 lateral by default (Non-CLGS default value is 0)
self.numnonverticalsections.value = 1
self.numnonverticalsections.ErrMessage = (f'assume default for Number of Nonvertical Wellbore Sections '
f'({self.numnonverticalsections.value})')

self.vertical_section_length = self.ParameterDict[self.vertical_section_length.Name] = floatParameter(
'Vertical Section Length',
DefaultValue=2000.0,
Expand Down
3 changes: 2 additions & 1 deletion src/geophires_x/SUTRAEconomics.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,8 @@ def Calculate(self, model: Model) -> None:
* 1e8
) # cents/kWh

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

def __str__(self):
return "Economics"
8 changes: 5 additions & 3 deletions src/geophires_x/WellBores.py
Original file line number Diff line number Diff line change
Expand Up @@ -1030,10 +1030,10 @@ def __init__(self, model: Model):
)
self.numnonverticalsections = self.ParameterDict[self.numnonverticalsections.Name] = intParameter(
"Number of Multilateral Sections",
DefaultValue=1,
DefaultValue=0,
AllowableRange=list(range(0, 101, 1)),
UnitType=Units.NONE,
ErrMessage="assume default for Number of Nonvertical Wellbore Sections (1)",
ErrMessage="assume default for Number of Nonvertical Wellbore Sections (0)",
ToolTipText="Number of Nonvertical Wellbore Sections"
)
self.NonverticalsCased = self.ParameterDict[self.NonverticalsCased.Name] = boolParameter(
Expand All @@ -1042,7 +1042,9 @@ def __init__(self, model: Model):
Required=False,
Provided=False,
Valid=True,
ErrMessage="assume default value (False)"
ErrMessage="assume default value (False)",
ToolTipText="If set to True, casing & cementing are assumed to comprise 50% of drilling costs "
"(doubling cost compared to uncased)."
)

# local variable initiation
Expand Down
2 changes: 1 addition & 1 deletion src/geophires_x/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '3.7.5'
__version__ = '3.7.6'
Loading

0 comments on commit b142b10

Please sign in to comment.