Skip to content

Commit 61eb154

Browse files
Merge pull request #35 from softwareengineerprogrammer/output-file-default-behavior
Output file default behavior
2 parents 3ca3b25 + 104fe35 commit 61eb154

22 files changed

+127
-97
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.5.7
2+
current_version = 3.6.0
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.5.7
57+
version: 3.6.0
5858
version_manager: "bump2version"
5959
website: "https://github.com/NREL"
6060
year_from: "2023"

.gitignore

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ tests/__pycache__
66
# requirements defined in setup.py
77
requirements.txt
88

9-
# Temp files
9+
# Temp/output files
1010
.*.sw[po]
1111
*~
1212
*.bak
@@ -15,6 +15,13 @@ requirements.txt
1515
*.json
1616
all_messages_conf.log
1717
HDR.out
18+
src/geophires_x/*.png
19+
src/geophires_x/*.html
20+
*HEATING_COOLING*.png
21+
*CASHFLOW_PROFILE*.png
22+
Geothermal_district_heating_system_with_peaking_boilers.png
23+
*.html
24+
!docs/*.html
1825

1926
# C extensions
2027
*.so

CHANGELOG.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,22 @@ Changelog
55
GEOPHIRES-X (2023-2024)
66
------------------------
77

8+
3.6
9+
^^^
10+
11+
`release <https://github.com/NREL/GEOPHIRES-X/releases/tag/v3.6.0>`__ | `diff <https://github.com/NREL/GEOPHIRES-X/compare/v3.5.0...v3.6.0>`__
12+
13+
Changes default output file path to the original working directory instead of the the GEOPHIRES module source directory (usually ``geophires-x`` or ``src/geophires_x``, depending on package installation type).
14+
This affects:
15+
16+
1. Users who call GEOPHIRES as a script from a working directory outside of the module source directory and pass no output file argument or a non-absolute output file argument e.g. ``python ./geophires-x/GEOPHIRESv3.py my-input.txt``. In prior versions, the output file would have been generated at ``./geophires_x/HDR.out``; in v.3.6 it is generated at ``./HDR.out`` instead. (Users who call GEOPHIRES as a module – ``python -m geophires_x my-input.txt`` – will see no change since the module has always output relative to the working directory.)
17+
18+
2. Inputs with ``HTML Output File`` and/or ``Improved Text Output File`` parameters specified as non-absolute paths. The associated output files will now be generated relative to the working directory instead of the GEOPHIRES module source directory.
19+
20+
21+
Affected users who do not want the new behavior can specify absolute output paths instead of relative ones e.g. ``python ./geophires-x/GEOPHIRESv3.py my-input.txt /home/user/my-geophires-project/geophires-x/HDR.out``
22+
(Most users are expected to be unaffected.)
23+
824
3.5
925
^^^
1026

README.rst

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

54-
.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.5.7.svg
54+
.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.6.0.svg
5555
:alt: Commits since latest release
56-
:target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.5.7...main
56+
:target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.6.0...main
5757

5858
.. |docs| image:: https://readthedocs.org/projects/GEOPHIRES-X/badge/?style=flat
5959
: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 = '2024'
1919
author = 'NREL'
2020
copyright = f'{year}, {author}'
21-
version = release = '3.5.7'
21+
version = release = '3.6.0'
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.5.7',
16+
version='3.6.0',
1717
license='MIT',
1818
description='GEOPHIRES is a free and open-source geothermal techno-economic simulator.',
1919
long_description='{}\n{}'.format(

src/geophires_x/GEOPHIRESv3.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ def main(enable_geophires_logging_config=True):
1919
logging will be configured in the Model class.
2020
:return: None
2121
"""
22+
original_cwd:Path = Path.cwd().absolute()
23+
2224
# set the starting directory to be the directory that this file is in
2325
os.chdir(os.path.dirname(os.path.abspath(__file__)))
2426

@@ -33,7 +35,7 @@ def main(enable_geophires_logging_config=True):
3335
model = Model.Model(enable_geophires_logging_config=enable_geophires_logging_config)
3436

3537
# read the parameters that apply to the model
36-
model.read_parameters()
38+
model.read_parameters(default_output_path=original_cwd)
3739

3840
# Calculate the entire model
3941
model.Calculate()
@@ -61,7 +63,7 @@ def main(enable_geophires_logging_config=True):
6163
supress_warnings=True)
6264
json_merged = {**json_merged, **json.loads(json_sdacgt)}
6365

64-
json_outputfile = 'HDR.json'
66+
json_outputfile = Path(original_cwd, 'HDR.json')
6567
if len(sys.argv) > 2:
6668
output_arg = str(sys.argv[2])
6769
output_arg_path = Path(output_arg)
@@ -71,7 +73,7 @@ def main(enable_geophires_logging_config=True):
7173

7274
# if the user has asked for it, copy the output file to the screen
7375
if model.outputs.printoutput:
74-
outputfile = 'HDR.out'
76+
outputfile = Path(original_cwd, 'HDR.out')
7577
if len(sys.argv) > 2:
7678
outputfile = sys.argv[2]
7779

src/geophires_x/Model.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import sys
2+
from email.policy import default
23
from pathlib import Path
34
import logging
45
import time
@@ -196,9 +197,10 @@ def __init__(self, enable_geophires_logging_config=True, input_file=None):
196197
def __str__(self):
197198
return "Model"
198199

199-
def read_parameters(self) -> None:
200+
def read_parameters(self, default_output_path: Path = None) -> None:
200201
"""
201202
The read_parameters function reads the parameters from the input file and stores them in a dictionary.
203+
:param default_output_path: Relative path for non-absolute output path parameters
202204
:return: None
203205
"""
204206
self.logger.info(f'Init {__class__}: {__name__}')
@@ -209,17 +211,17 @@ def read_parameters(self) -> None:
209211
self.wellbores.read_parameters(self)
210212
self.surfaceplant.read_parameters(self)
211213
self.economics.read_parameters(self)
212-
self.outputs.read_parameters(self)
214+
self.outputs.read_parameters(self, default_output_path=default_output_path)
213215

214216
# having read in the parameters, we now need to set up the objects that are specific to the user's choices
215217
# if we find out we have an add-ons, read the parameters
216218
if self.economics.DoAddOnCalculations.value:
217219
self.addeconomics.read_parameters(self)
218-
self.addoutputs.read_parameters(self)
220+
self.addoutputs.read_parameters(self, default_output_path=default_output_path)
219221
# if we find out we have an S-DAC-GT calculation, read for the parameters
220222
if self.economics.DoSDACGTCalculations.value:
221223
self.sdacgteconomics.read_parameters(self)
222-
self.sdacgtoutputs.read_parameters(self)
224+
self.sdacgtoutputs.read_parameters(self, default_output_path=default_output_path)
223225

224226
# Once we are done reading and processing parameters,
225227
# we reset the objects to more specific objects based on user choices

src/geophires_x/Outputs.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from geophires_x.OptionList import EndUseOptions, EconomicModel, ReservoirModel, FractureShape, ReservoirVolume, \
2323
PlantType
2424
from geophires_x.GeoPHIRESUtils import UpgradeSymbologyOfUnits, render_default, InsertImagesIntoHTML
25+
from geophires_x.Parameter import Parameter
2526

2627
NL = '\n'
2728
validFilenameChars = "-_.() %s%s" % (string.ascii_letters, string.digits)
@@ -635,28 +636,34 @@ class Outputs:
635636
"""
636637
This class handles all the outputs for the GEOPHIRESv3 model.
637638
"""
639+
638640
def __init__(self, model:Model, output_file:str ='HDR.out'):
639641
model.logger.info(f'Init {__class__!s}: {__name__}')
640642
self.ParameterDict = {}
641643
self.OutputParameterDict = {}
644+
self.filepath_parameter_names = []
645+
646+
def filepath_parameter(p: Parameter) -> Parameter:
647+
self.filepath_parameter_names.append(p.Name)
648+
return p
642649

643-
self.text_output_file = self.ParameterDict[self.text_output_file.Name] = strParameter(
650+
self.text_output_file = self.ParameterDict[self.text_output_file.Name] = filepath_parameter(strParameter(
644651
'Improved Text Output File',
645652
DefaultValue='GEOPHIRES_Text.html',
646653
Required=False,
647654
Provided=False,
648655
ErrMessage='assume no improved text output',
649656
ToolTipText='Provide a improved text output name if you want to have improved text output (no output if not provided)',
650-
)
657+
))
651658

652-
self.html_output_file = self.ParameterDict[self.html_output_file.Name] = strParameter(
659+
self.html_output_file = self.ParameterDict[self.html_output_file.Name] = filepath_parameter(strParameter(
653660
'HTML Output File',
654661
DefaultValue='GEOPHIRES.html',
655662
Required=False,
656663
Provided=False,
657664
ErrMessage='assume no HTML output',
658665
ToolTipText='Provide a HTML output name if you want to have HTML output (no output if not provided)',
659-
)
666+
))
660667

661668
self.printoutput = self.ParameterDict[self.printoutput.Name] = boolParameter(
662669
'Print Output to Console',
@@ -677,7 +684,7 @@ def __init__(self, model:Model, output_file:str ='HDR.out'):
677684
def __str__(self):
678685
return 'Outputs'
679686

680-
def read_parameters(self, model:Model) -> None:
687+
def read_parameters(self, model: Model, default_output_path: Path = None) -> None:
681688
"""
682689
The read_parameters function reads in the parameters from a dictionary and stores them in the parameters.
683690
It also handles special cases that need to be handled after a value has been read in and checked.
@@ -692,6 +699,8 @@ def read_parameters(self, model:Model) -> None:
692699
to call this method from you class, which can effectively modify all these superclass parameters in your class.
693700
:param model: The container class of the application, giving access to everything else, including the logger
694701
:type model: :class:`~geophires_x.Model.Model`
702+
:param default_output_path: Relative path for non-absolute output path parameters
703+
:type default_output_path: pathlib.Path
695704
:return: None
696705
"""
697706
model.logger.info(f'Init {__class__!s}: {__name__}')
@@ -702,6 +711,15 @@ def read_parameters(self, model:Model) -> None:
702711
key = ParameterToModify.Name.strip()
703712
if key in model.InputParameters:
704713
ParameterReadIn = model.InputParameters[key]
714+
715+
if key in self.filepath_parameter_names:
716+
if not Path(ParameterReadIn.sValue).is_absolute() and default_output_path is not None:
717+
original_val = ParameterReadIn.sValue
718+
ParameterReadIn.sValue = str(
719+
default_output_path.joinpath(Path(ParameterReadIn.sValue)).absolute())
720+
model.logger.info(f'Adjusted {key} path to {ParameterReadIn.sValue} because original value '
721+
f'({original_val}) was not an absolute path.')
722+
705723
# Before we change the parameter, let's assume that the unit preferences will match
706724
# - if they don't, the later code will fix this.
707725
ParameterToModify.CurrentUnits = ParameterToModify.PreferredUnits
@@ -724,8 +742,6 @@ def read_parameters(self, model:Model) -> None:
724742
if key.startswith('Units:'):
725743
self.ParameterDict[key.replace('Units:', '')] = LookupUnits(model.InputParameters[key].sValue)[0]
726744

727-
# handle special cases
728-
729745
model.logger.info(f'Complete {__class__!s}: {__name__}')
730746

731747
def PrintOutputs(self, model: Model):

src/geophires_x/SUTRAOutputs.py

Lines changed: 8 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
import geophires_x
66
import numpy as np
77
import geophires_x.Model as Model
8-
from .Parameter import LookupUnits
8+
from geophires_x.Outputs import Outputs
99
from .OptionList import EconomicModel
1010

1111
NL="\n"
1212

13-
class SUTRAOutputs:
14-
"""TODO should inherit from Outputs"""
13+
14+
class SUTRAOutputs(Outputs):
1515

1616
def __init__(self, model:Model, output_file:str ='HDR.out'):
1717
"""
@@ -35,45 +35,8 @@ def __init__(self, model:Model, output_file:str ='HDR.out'):
3535
model.logger.info(f'Complete {str(__class__)}: {sys._getframe().f_code.co_name}')
3636

3737
def __str__(self):
38-
return 'Outputs'
38+
return 'SUTRAOutputs'
3939

40-
def read_parameters(self, model:Model) -> None:
41-
"""
42-
The read_parameters function reads in the parameters from a dictionary and stores them in the parameters.
43-
It also handles special cases that need to be handled after a value has been read in and checked.
44-
If you choose to subclass this master class, you can also choose to override this method (or not), and if you do
45-
Deals with all the parameter values that the user has provided. They should really only provide values that
46-
they want to change from the default values, but they can provide a value that is already set because it is a
47-
default value set in __init__. It will ignore those.
48-
This also deals with all the special cases that need to be taken care of after a value has been read in
49-
and checked.
50-
If you choose to subclass this master class, you can also choose to override this method (or not),
51-
and if you do, do it before or after you call you own version of this method. If you do, you can also choose
52-
to call this method from you class, which can effectively modify all these superclass parameters in your class.
53-
:param model: The container class of the application, giving access to everything else, including the logger
54-
:type model: :class:`~geophires_x.Model.Model`
55-
:return: None
56-
"""
57-
model.logger.info(f'Init {str(__class__)}: {sys._getframe().f_code.co_name}')
58-
59-
if len(model.InputParameters) > 0:
60-
# if the user wants it, we need to know if the user wants to copy the contents of the
61-
# output file to the screen - this serves as the screen report
62-
if "Print Output to Console" in model.InputParameters:
63-
ParameterReadIn = model.InputParameters["Print Output to Console"]
64-
if ParameterReadIn.sValue == "0":
65-
self.printoutput = False
66-
67-
# loop through all the parameters that the user wishes to set, looking for parameters that contain the
68-
# prefix "Units:" - that means we want to set a special case for converting this
69-
# output parameter to new units
70-
for key in model.InputParameters.keys():
71-
if key.startswith("Units:"):
72-
self.ParameterDict[key.replace("Units:", "")] = LookupUnits(model.InputParameters[key].sValue)[0]
73-
74-
# handle special cases
75-
76-
model.logger.info(f'Complete {str(__class__)}: {sys._getframe().f_code.co_name}')
7740

7841
def PrintOutputs(self, model: Model):
7942
"""
@@ -84,30 +47,8 @@ def PrintOutputs(self, model: Model):
8447
"""
8548
model.logger.info(f'Init {str(__class__)}: {sys._getframe().f_code.co_name}')
8649

87-
# Deal with converting Units back to PreferredUnits, if required.
88-
# before we write the outputs, we go thru all the parameters for all of the objects and set the values back
89-
# to the units that the user entered the data in
90-
# We do this because the value may be displayed in the output, and we want the user to recognize their value,
91-
# not some converted value
92-
# for obj in [model.reserv, model.wellbores, model.surfaceplant, model.economics]:
93-
# for key in obj.ParameterDict:
94-
# param = obj.ParameterDict[key]
95-
# if not param.UnitsMatch: ConvertUnitsBack(param, model)
96-
97-
# now we need to loop through all thw output parameters to update their units to
98-
# whatever units the user has specified.
99-
# i.e., they may have specified that all LENGTH results must be in feet, so we need to convert those
100-
# from whatever LENGTH unit they are to feet.
101-
# same for all the other classes of units (TEMPERATURE, DENSITY, etc).
102-
103-
#for obj in [model.reserv, model.wellbores, model.surfaceplant, model.economics]:
104-
# for key in obj.OutputParameterDict:
105-
# if key in self.ParameterDict:
106-
# if self.ParameterDict[key] != obj.OutputParameterDict[key].CurrentUnits:
107-
# ConvertOutputUnits(obj.OutputParameterDict[key], self.ParameterDict[key], model)
10850

10951
# write results to output file and screen
110-
11152
try:
11253
with open(self.output_file,'w', encoding='UTF-8') as f:
11354
f.write(' *****************\n')
@@ -218,10 +159,10 @@ def PrintOutputs(self, model: Model):
218159
except BaseException as ex:
219160
tb = sys.exc_info()[2]
220161
print(str(ex))
221-
print("Error: GEOPHIRES Failed to write the output file. Exiting....Line %i" % tb.tb_lineno)
162+
msg = "Error: GEOPHIRES Failed to write the output file. Exiting....Line %i" % tb.tb_lineno
163+
print(msg)
222164
model.logger.critical(str(ex))
223-
model.logger.critical("Error: GEOPHIRES Failed to write the output file. Exiting....Line %i" % tb.tb_lineno)
224-
# FIXME raise exception instead of sys.exit()
225-
sys.exit()
165+
model.logger.critical(msg)
166+
raise RuntimeError(msg)
226167

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

src/geophires_x/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '3.5.7'
1+
__version__ = '3.6.0'

tests/examples/Beckers_et_al_2023_Tabulated_Database_Coaxial_water_heat.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Exploration Capital Cost, 0
1515
Production Flow Rate per Well, 20, ---- kg/s for water / 40 kg/s for sCO2
1616
Cylindrical Reservoir Input Depth, 3.0, -----kilometers
1717
Gradient 1, 60.0, ----deg.c/km
18-
Total Nonvertical Length, 9000, ----- m
18+
Nonvertical Length per Multilateral Section, 9000, ----- m
1919
Production Well Diameter,8.5, --- [inch]
2020
Reservoir Depth, 3.0, -----kilometers
2121
Injection Temperature, 60.0, -----deg.C

tests/examples/Beckers_et_al_2023_Tabulated_Database_Uloop_sCO2_elec.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ All-in Nonvertical Drilling Costs, 1000.0
1515
Production Flow Rate per Well, 40, ---- kg/s for water / 40 kg/s for sCO2
1616
Cylindrical Reservoir Input Depth, 3000.0 meter,
1717
Gradient 1, 60.0, ----deg.c/km
18-
Total Nonvertical Length, 9000
18+
Nonvertical Length per Multilateral Section, 9000
1919
Production Well Diameter,8.5, --- [inch]
2020
Injection Temperature, 60.0, -----deg.C
2121
Plant Lifetime, 40, --- years

tests/examples/Beckers_et_al_2023_Tabulated_Database_Uloop_sCO2_heat.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Exploration Capital Cost, 0
1616
Production Flow Rate per Well, 40, ---- kg/s for water / 40 kg/s for sCO2
1717
Cylindrical Reservoir Input Depth, 3.0, -----kilometers
1818
Gradient 1, 60.0, ----deg.c/km
19-
Total Nonvertical Length, 9000, ----- m
19+
Nonvertical Length per Multilateral Section, 9000, ----- m
2020
Production Well Diameter,8.5, --- [inch]
2121
Injection Temperature, 60.0, -----deg.C
2222
Plant Lifetime, 40, --- years

0 commit comments

Comments
 (0)