Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unit conversion bug fixes #99

Merged
merged 18 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
e2caefd
Fix to https://github.com/NREL/GEOPHIRES-X/issues/92 ported from http…
softwareengineerprogrammer Jan 24, 2024
022594b
Deduplicate error messages in Parameter.ConvertOutputUnits
softwareengineerprogrammer Jan 26, 2024
c3a0de4
Deduplicate remaining error/warning messages in Parameter.py
softwareengineerprogrammer Jan 26, 2024
062dfc5
Partial fix
softwareengineerprogrammer Jan 26, 2024
c047eeb
More partial fix
softwareengineerprogrammer Jan 26, 2024
34b6ab3
Make ConvertUnits back unit-testable
softwareengineerprogrammer Jan 28, 2024
e9b2224
Show filenames when file content equality assertion fails
softwareengineerprogrammer Jan 28, 2024
a101e80
Fixed, underlying issue with AGS input unit conversions documented in…
softwareengineerprogrammer Jan 28, 2024
219e384
Fix missing comma in Beckers_et_al_2023_Tabulated_Database_Uloop_wate…
softwareengineerprogrammer Jan 28, 2024
30f3d59
Various cleanup an unit-testing in-roads towards resolving cylindrica…
softwareengineerprogrammer Jan 29, 2024
4e897fe
Set CurrentUnits in ConvertUnits
softwareengineerprogrammer Jan 29, 2024
af1ae11
Clean up FIXME comments
softwareengineerprogrammer Jan 29, 2024
c94a0c6
README TODO re: HDR naming
softwareengineerprogrammer Jan 29, 2024
2b5a635
Remove type reference that apparently trips up py38 in GH Actions
softwareengineerprogrammer Jan 29, 2024
ac3340c
Remove comments from example cylindrical reservoir test input files
softwareengineerprogrammer Jan 29, 2024
04a7bff
Switch relevant Beckers_et_al example cylindrical reservoir input dep…
softwareengineerprogrammer Jan 29, 2024
a30c734
Merge pull request #4 from softwareengineerprogrammer/output-conversi…
softwareengineerprogrammer Jan 29, 2024
86d2331
Bump version: 3.3.0 → 3.3.1
softwareengineerprogrammer Jan 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.3.0
current_version = 3.3.1
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.3.0
version: 3.3.1
version_manager: "bump2version"
website: "https://github.com/NREL"
year_from: "2023"
Expand Down
6 changes: 4 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,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/NREL/GEOPHIRES-X/v3.3.0.svg
.. |commits-since| image:: https://img.shields.io/github/commits-since/NREL/GEOPHIRES-X/v3.3.1.svg
:alt: Commits since latest release
:target: https://github.com/NREL/GEOPHIRES-X/compare/v3.3.0...main
:target: https://github.com/NREL/GEOPHIRES-X/compare/v3.3.1...main

.. |docs| image:: https://readthedocs.org/projects/GEOPHIRES-X/badge/?style=flat
:target: https://nrel.github.io/GEOPHIRES-X
Expand Down Expand Up @@ -298,3 +298,5 @@ you may want to follow `the Development instructions <CONTRIBUTING.rst#developme
(You can also create a fork after doing an editable install so don't worry about picking this method if you're unsure.)

.. TODO feedback section - why user feedback is important/valuable, how to file issues/contact authors

.. TODO FAQ/trivia section - "HDR" naming (HDR.out, HDR.json) is for Hot Dry Rock
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 = '2023'
author = 'NREL'
copyright = f'{year}, {author}'
version = release = '3.3.0'
version = release = '3.3.1'

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.3.0',
version='3.3.1',
license='MIT',
description='GEOPHIRES is a free and open-source geothermal techno-economic simulator.',
long_description='{}\n{}'.format(
Expand Down
2 changes: 1 addition & 1 deletion src/geophires_x/AGSOutputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def PrintOutputs(self, model: Model):
if not param.UnitsMatch:
ConvertUnitsBack(param, model)

# now we need to loop thru all thw output parameters to update their units to whatever units the user has specified.
# now we need to loop thru all the output parameters to update their units to whatever units the user has specified.
# i.e., they may have specified that all LENGTH results must be in feet, so we need to convert
# those from whatever LENGTH unit they are to feet.
# same for all the other classes of units (TEMPERATURE, DENSITY, etc).
Expand Down
28 changes: 14 additions & 14 deletions src/geophires_x/AGSWellBores.py
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ def read_parameters(self, model: Model) -> None:
:type model: :class:`~geophires_x.Model.Model`
:return: None
"""
model.logger.info("Init " + str(__class__) + ": " + sys._getframe().f_code.co_name)
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,
Expand Down Expand Up @@ -679,28 +679,28 @@ def read_parameters(self, model: Model) -> None:

# handle error checking and special cases:
if model.reserv.numseg.value > 1:
print("Warning: 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.")
model.logger.warning("Warning: 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.")
msg = "Warning: 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)
model.logger.warning(msg)
model.reserv.numseg.value = 1

if self.ninj.value > 0:
print("Warning: CLGS model considers the only the production wellbore parameters. Anything related to the \
injection wellbore is ignored.")
model.logger.warning("Warning: CLGS model considers the only the production well bore parameters. Anything related to the \
injection wellbore is ignored.")
msg = "Warning: CLGS model considers the only the production wellbore parameters. Anything related to the \
injection wellbore is ignored."
print(msg)
model.logger.warning(msg)

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

# inputs we already have - needs to be set at ReadParameter time so values set at the latest possible time
self.krock = model.reserv.krock.value # same units are GEOPHIRES

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

# code from Koenraad
def calculatedrillinglengths(self, model) -> tuple:
Expand Down
123 changes: 64 additions & 59 deletions src/geophires_x/CylindricalReservoir.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class CylindricalReservoir(Reservoir):
The CylindricalReservoir class is a subclass of the Reservoir class in a straightforward conduction-only model.
It inherits from the primary Reservoir model but offers new parameters and calculations.
"""

def __init__(self, model: Model):
"""
The __init__ function is called automatically when a class is instantiated.
Expand All @@ -24,7 +25,7 @@ def __init__(self, model: Model):
:return: None
"""
model.logger.info(f'Init {str(__class__)}: {sys._getframe().f_code.co_name}')
super().__init__(model) # initialize the parent parameters and variables
super().__init__(model) # initialize the parent parameters and variables

self.InputDepth = self.ParameterDict[self.InputDepth.Name] = floatParameter(
"Cylindrical Reservoir Input Depth",
Expand All @@ -37,7 +38,7 @@ def __init__(self, model: Model):
CurrentUnits=LengthUnit.KILOMETERS,
Required=True,
ErrMessage="assume default cylindrical reservoir depth (3 km)",
ToolTipText="Depth of the inflow end of a cylindrical reservoir"
ToolTipText="Depth of the inflow end of a cylindrical reservoir",
)

self.OutputDepth = self.ParameterDict[self.OutputDepth.Name] = floatParameter(
Expand All @@ -50,8 +51,8 @@ def __init__(self, model: Model):
PreferredUnits=LengthUnit.KILOMETERS,
CurrentUnits=LengthUnit.KILOMETERS,
Required=True,
ErrMessage="assume default cyclindrical reservoir input depth (3 km)",
ToolTipText="Depth of the outflow end of a cyclindrical reservoir"
ErrMessage="assume default cylindrical reservoir input depth (3 km)",
ToolTipText="Depth of the outflow end of a cylindrical reservoir",
)
self.Length = self.ParameterDict[self.Length.Name] = floatParameter(
"Cylindrical Reservoir Length",
Expand All @@ -64,7 +65,7 @@ def __init__(self, model: Model):
CurrentUnits=LengthUnit.KILOMETERS,
Required=True,
ErrMessage="assume default cylindrical reservoir length (4 km)",
ToolTipText="Length of cylindrical reservoir"
ToolTipText="Length of cylindrical reservoir",
)
self.RadiusOfEffect = self.ParameterDict[self.RadiusOfEffect.Name] = floatParameter(
"Cylindrical Reservoir Radius of Effect",
Expand All @@ -76,20 +77,21 @@ def __init__(self, model: Model):
PreferredUnits=LengthUnit.METERS,
CurrentUnits=LengthUnit.METERS,
ErrMessage="assume default cylindrical reservoir radius of effect (30 m)",
ToolTipText="The radius of effect - the distance into the rock from the center of the cylinder that will" +
" be perturbed by at least 1 C"
ToolTipText="The radius of effect - the distance into the rock from the center of the cylinder that will"
+ " be perturbed by at least 1 C",
)
self.RadiusOfEffectFactor = self.ParameterDict[self.RadiusOfEffectFactor.Name] = floatParameter(
"Cylindrical Reservoir Radius of Effect Factor",
value=1.0,
DefaultValue=1.0,
Min=0.0, Max=10.0,
Min=0.0,
Max=10.0,
UnitType=Units.PERCENT,
PreferredUnits=PercentUnit.TENTH,
CurrentUnits=PercentUnit.TENTH,
ErrMessage="assume default cyclindrical reservoir radius of effect reduction factor (0.1)",
ToolTipText="The radius of effect reduction factor - to account for the fact that we cannot extract 100%" +
" of the heat in the cylinder."
ToolTipText="The radius of effect reduction factor - to account for the fact that we cannot extract 100%"
+ " of the heat in the cylinder.",
)

sclass = str(__class__).replace("<class \'", "")
Expand All @@ -107,47 +109,46 @@ def __init__(self, model: Model):
CurrentUnits=LengthUnit.KILOMETERS,
Required=True,
ErrMessage="assume default cyclindrical reservoir depth (3 km)",
ToolTipText="Depth of the inflow end of a cyclindrical reservoir"
ToolTipText="Depth of the inflow end of a cyclindrical reservoir",
)
self.waterloss = self.ParameterDict[self.waterloss.Name] = floatParameter(
"Water Loss Fraction",
value=0.0,
DefaultValue=0.0,
Min=0.0, Max=0.99,
Min=0.0,
Max=0.99,
UnitType=Units.PERCENT,
PreferredUnits=PercentUnit.TENTH,
CurrentUnits=PercentUnit.TENTH,
ErrMessage="assume default water loss fraction (0)",
ToolTipText="Fraction of water lost in the reservoir defined as (total geofluid lost)/(total geofluid produced).")
ToolTipText="Fraction of water lost in the reservoir defined as (total geofluid lost)/(total geofluid produced).",
)

# Results - used by other objects or printed in output downstream
self.SurfaceArea = self.OutputParameterDict[self.SurfaceArea.Name] = OutputParameter(
"Cylindrical Reservoir Surface Area",
UnitType=Units.AREA,
PreferredUnits=AreaUnit.METERS2,
CurrentUnits=AreaUnit.METERS2
CurrentUnits=AreaUnit.METERS2,
)
self.averagegradient = self.OutputParameterDict[self.averagegradient.Name] = OutputParameter(
"averagegradient",
UnitType=Units.NONE
"averagegradient", UnitType=Units.NONE
)
self.timevector = self.OutputParameterDict[self.timevector.Name] = OutputParameter(
"Time Vector",
value=[],
UnitType=Units.NONE
"Time Vector", value=[], UnitType=Units.NONE
)
self.Tresoutput = self.OutputParameterDict[self.Tresoutput.Name] = OutputParameter(
"Reservoir Temperature History",
value=[],
UnitType=Units.TEMPERATURE,
PreferredUnits=TemperatureUnit.CELSIUS,
CurrentUnits=TemperatureUnit.CELSIUS
CurrentUnits=TemperatureUnit.CELSIUS,
)

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

def __str__(self):
return "CylindricalReservoir"
return 'CylindricalReservoir'

def read_parameters(self, model: Model) -> None:
"""
Expand All @@ -160,41 +161,31 @@ def read_parameters(self, model: Model) -> None:
:type model: :class:`~geophires_x.Model.Model`
:return: None
"""
model.logger.info(f"Init {str(__class__)}: {sys._getframe().f_code.co_name}")
model.logger.info(f'Init {str(__class__)}: {sys._getframe().f_code.co_name}')

super().read_parameters(model)
# 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, including the ones that are
# specific to this class

# Deal with all the parameter values that the user has provided. They should really only provide values
# that they want to change from the default values, but they can provide a value that is already set because
# it is a default value set in __init__. It will ignore those.
# This also deals with all the special cases that need to be taken care of after a value has been
# read in and checked.
# If you choose to subclass this master class, you can also choose to override this method (or not),
# and if you do, do it before or after you call you own version of this method. If you do, you can also
# choose to call this method from you class, which can effectively modify all these
# superclass parameters in your class.

# Deal with special cases that need to be taken care of after a value has been read in and checked.

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:
# just handle special cases for this class - the call to super set all thr values,
# including the value unique to this class
# if input depth is set and not output, assume output is the same as input
if ParameterToModify.Name == "Cylindrical Reservoir Input Depth":
if "Cylindrical Reservoir Output Depth" not in model.InputParameters:
# Just handle special cases for this class - the call to super set all the values,
# including the value unique to this class.

if ParameterToModify.Name == 'Cylindrical Reservoir Input Depth':
if 'Cylindrical Reservoir Output Depth' not in model.InputParameters:
# If input depth is set and not output, assume output is the same as input
self.OutputDepth.value = self.InputDepth.value
else:
model.logger.info("No parameters read because no content provided")
model.logger.info(f"complete {str(__class__)}: {sys._getframe().f_code.co_name}")
model.logger.info('No parameters read because no content provided')

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

@lru_cache(maxsize=1024)
def Calculate(self, model:Model) -> None:
def Calculate(self, model: Model) -> None:
"""
The Calculate function is where all the calculations are done.
This function can be called multiple times, and will only recalculate what has changed each time it is called.
Expand All @@ -213,26 +204,40 @@ def Calculate(self, model:Model) -> None:
model.logger.info(f"Init {str(__class__)}: {sys._getframe().f_code.co_name}")

# specify time-stepping vectors
self.timevector.value = np.linspace(0, model.surfaceplant.plant_lifetime.value,
model.economics.timestepsperyear.value * model.surfaceplant.plant_lifetime.value)
self.timevector.value = np.linspace(
0,
model.surfaceplant.plant_lifetime.value,
model.economics.timestepsperyear.value * model.surfaceplant.plant_lifetime.value,
)
self.averagegradient.value = self.gradient.value[0]

self.Trock.value = self.Tsurf.value + (self.gradient.value[0] * (self.InputDepth.value * 1000.0))
# initialize with the Initial reservoir temperature
self.Tresoutput.value = np.array(len(self.timevector.value) * [self.Trock.value])
# depth in this case is actually the total length of the drilled assembly
self.depth.value = self.InputDepth.value/1000.0 + self.OutputDepth.value + self.Length.value
self.depth.value = self.InputDepth.value / 1000.0 + self.OutputDepth.value + self.Length.value
# Total volume of all laterals but hollow cylinder - doesn't include drilled-out area, units = m3
self.resvolcalc.value = model.wellbores.numnonverticalsections.value * math.pi * (self.Length.value * 1000.0) *\
((pow(self.RadiusOfEffect.value, 2)) - pow(model.wellbores.prodwelldiam.value, 2))
self.SurfaceArea.value = (2.0 * math.pi * self.RadiusOfEffect.value * (self.Length.value * 1000.0)) +\
(2.0 * math.pi * pow(self.RadiusOfEffect.value, 2)) # m3
self.InitialReservoirHeatContent.value = (self.RadiusOfEffectFactor.value *
self.resvolcalc.value*self.rhorock.value*self.cprock.value*
(self.Trock.value-model.wellbores.Tinj.value))/1E15 # 10^15 J
self.cpwater.value = heatcapacitywater(model.wellbores.Tinj.value*0.5 +
(self.Trock.value*0.9+model.wellbores.Tinj.value*0.1)*0.5)
self.rhowater.value = densitywater(model.wellbores.Tinj.value*0.5 +
(self.Trock.value*0.9+model.wellbores.Tinj.value*0.1)*0.5)
self.resvolcalc.value = (
model.wellbores.numnonverticalsections.value
* math.pi
* (self.Length.value * 1000.0)
* ((pow(self.RadiusOfEffect.value, 2)) - pow(model.wellbores.prodwelldiam.value, 2))
)
self.SurfaceArea.value = (2.0 * math.pi * self.RadiusOfEffect.value * (self.Length.value * 1000.0)) + (
2.0 * math.pi * pow(self.RadiusOfEffect.value, 2)
) # m3
self.InitialReservoirHeatContent.value = (
self.RadiusOfEffectFactor.value
* self.resvolcalc.value
* self.rhorock.value
* self.cprock.value
* (self.Trock.value - model.wellbores.Tinj.value)
) / 1e15 # 10^15 J
self.cpwater.value = heatcapacitywater(
model.wellbores.Tinj.value * 0.5 + (self.Trock.value * 0.9 + model.wellbores.Tinj.value * 0.1) * 0.5
)
self.rhowater.value = densitywater(
model.wellbores.Tinj.value * 0.5 + (self.Trock.value * 0.9 + model.wellbores.Tinj.value * 0.1) * 0.5
)

model.logger.info(f"complete {str(__class__)}: {sys._getframe().f_code.co_name}")
2 changes: 1 addition & 1 deletion src/geophires_x/Model.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def __init__(self, enable_geophires_logging_config=True):
self.logger = logging.getLogger('root')

if enable_geophires_logging_config:
logging.config.fileConfig(Path('logging.conf'))
logging.config.fileConfig(Path(Path(__file__).parent,'logging.conf'))
self.logger.setLevel(logging.INFO)

self.logger.info(f'Init {__class__}: {__name__}')
Expand Down
Loading
Loading