Skip to content

Commit 5647c7e

Browse files
Merge pull request #108 from softwareengineerprogrammer/theoretical-basis-tooltip-text-updates
Update tooltip texts from theoretical basis [v3.9.64] NREL#427
2 parents c585340 + 2b51fa0 commit 5647c7e

15 files changed

+199
-49
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.9.62
2+
current_version = 3.9.64
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.9.62
57+
version: 3.9.64
5858
version_manager: "bump2version"
5959
website: "https://github.com/NREL"
6060
year_from: "2023"

README.rst

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

61-
.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.9.62.svg
61+
.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.9.64.svg
6262
:alt: Commits since latest release
63-
:target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.9.62...main
63+
:target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.9.64...main
6464

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

docs/Theoretical-Basis-for-GEOPHIRES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Theoretical Basis for GEOPHIRES
22

3-
This document describes the foundational theoretical basis for GEOPHIRES, adapted from the 2019 paper, "[GEOPHIRES v2.0: updated geothermal techno-economic simulation tool](https://doi.org/10.1186/s40517-019-0119-6)" by Koenraad F. Beckers & Kevin McCabe. The core theories described here remain valid and relevant to the current software.
3+
This document describes the foundational theoretical basis for GEOPHIRES, adapted from the 2019 paper "[GEOPHIRES v2.0: updated geothermal techno-economic simulation tool](https://doi.org/10.1186/s40517-019-0119-6)" by Koenraad F. Beckers & Kevin McCabe. The core theories described here remain valid and relevant to the current software.
44

55
However, the text has not been comprehensively updated to include the theory for features added in GEOPHIRES v3 (GEOPHIRES-X) and later.
66
While pointers to the documentation for newer models have been added, the detailed technical descriptions herein still pertain to v2.0.

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.9.62'
21+
version = release = '3.9.64'
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.9.62',
16+
version='3.9.64',
1717
license='MIT',
1818
description='GEOPHIRES is a free and open-source geothermal techno-economic simulator.',
1919
long_description='{}\n{}'.format(

src/geophires_x/Economics.py

Lines changed: 102 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,8 @@ def __init__(self, model: Model):
625625
CurrentUnits=CurrencyUnit.MDOLLARS,
626626
Provided=False,
627627
Valid=False,
628-
ToolTipText="Total reservoir stimulation capital cost, including indirect costs and contingency."
628+
ToolTipText='Total reservoir stimulation capital cost, including indirect costs and contingency. '
629+
f'For traditional hydrothermal reservoirs, this parameter should be set to $0.'
629630
)
630631

631632
max_stimulation_cost_per_well_MUSD = 100
@@ -938,7 +939,18 @@ def __init__(self, model: Model):
938939
UnitType=Units.NONE,
939940
Required=True,
940941
ErrMessage="assume default number of time steps per year (4)",
941-
ToolTipText="Number of internal simulation time steps per year"
942+
ToolTipText='Number of internal simulation time steps per year. GEOPHIRES assumes linear time '
943+
'discretization with a user-provided number of time steps per year over the lifetime of the '
944+
'plant. The default is four time steps per year, meaning a time step of 3 months. '
945+
'At every time step, GEOPHIRES calculates the reservoir output temperature, production '
946+
'wellhead temperature, direct-use heat and/or electricity power output (in MW), pressure '
947+
'drops and pumping power. On an annual basis, GEOPHIRES calculates the O&M costs and '
948+
'direct-use heat and/or electricity production. To investigate seasonal effects, e.g., to '
949+
'assess the impact of more geothermal heat demand for district heating in winter than in '
950+
'summer, the user can select a smaller time step, e.g., a month (or 12 time steps per year). '
951+
'For even shorter timescale effects, e.g., to account for an hourly varying ambient '
952+
'temperature or investigate the response in plant operation to a fluctuating revenue rate), '
953+
'the user can select an even smaller time step, e.g., 1 h (or 8760 time steps per year).'
942954
)
943955
self.FCR = self.ParameterDict[self.FCR.Name] = floatParameter(
944956
"Fixed Charge Rate",
@@ -1824,22 +1836,28 @@ def __init__(self, model: Model):
18241836
f'costs per well. '
18251837
f'Provide {self.ccstimadjfactor.Name} to multiply the correlation-calculated cost. '
18261838
f'Provide {self.ccstimfixed.Name} to override the correlation and set your own '
1827-
f'total stimulation cost.'
1839+
f'total stimulation cost. '
1840+
f'For traditional hydrothermal reservoirs, {self.ccstimfixed.Name} should be set to $0.'
18281841
)
18291842

1830-
contingency_and_indirect_costs_tooltip = (
1831-
f'plus {self.contingency_percentage.quantity().to(convertible_unit("%")).magnitude:g}% contingency '
1843+
# TODO switch order to align with theoretical basis, which lists indirect costs first
1844+
contingency_and_indirect_costs_tooltip_stem = (
1845+
f'{self.contingency_percentage.quantity().to(convertible_unit("%")).magnitude:g}% contingency '
18321846
f'plus {self.indirect_capital_cost_percentage.quantity().to(convertible_unit("%")).magnitude}% '
18331847
f'indirect costs'
18341848
)
1849+
contingency_and_indirect_costs_tooltip = (
1850+
f'plus {contingency_and_indirect_costs_tooltip_stem}'
1851+
)
18351852

18361853
self.Cexpl = self.OutputParameterDict[self.Cexpl.Name] = OutputParameter(
18371854
Name="Exploration cost",
18381855
display_name='Exploration costs',
18391856
UnitType=Units.CURRENCY,
18401857
PreferredUnits=CurrencyUnit.MDOLLARS,
18411858
CurrentUnits=CurrencyUnit.MDOLLARS,
1842-
ToolTipText=f'Default correlation: 60% of the cost of one production well '
1859+
ToolTipText=f'The built-in exploration cost correlation considers drilling of a slim-hole well at 60% of '
1860+
f'the cost of a regular well, $1M for geophysical and field work, '
18431861
f'{contingency_and_indirect_costs_tooltip}. '
18441862
f'Provide {self.ccexpladjfactor.Name} to multiply the default correlation. '
18451863
f'Provide {self.ccexplfixed.Name} to override the default correlation and set your own cost.'
@@ -1865,14 +1883,21 @@ def __init__(self, model: Model):
18651883
ToolTipText='Drilling and completion cost per well, including indirect costs '
18661884
f'(default: {self.wellfield_indirect_capital_cost_percentage.DefaultValue}%).'
18671885
)
1886+
1887+
# noinspection SpellCheckingInspection
18681888
self.Coamwell = self.OutputParameterDict[self.Coamwell.Name] = OutputParameter(
18691889
Name="O&M Wellfield cost",
18701890
display_name='Wellfield maintenance costs',
18711891
UnitType=Units.CURRENCYFREQUENCY,
18721892
PreferredUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR,
1873-
CurrentUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR
1874-
# TODO TooltipText to document how this is calculated
1893+
CurrentUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR,
1894+
# TODO parameterize relevant constants in tooltip text
1895+
ToolTipText='The built-in correlation for the wellfield O&M costs is similar as the surface plant O&M '
1896+
'costs: it assumes that it consists of 1% of the total wellfield plus field gathering system '
1897+
'costs (for annual non-labor costs) and 25% of the labor costs (the other 75% of the labor '
1898+
'costs are assigned to the surface plant O&M costs).'
18751899
)
1900+
18761901
self.redrilling_annual_cost = self.OutputParameterDict[self.redrilling_annual_cost.Name] = OutputParameter(
18771902
Name="Redrilling costs",
18781903
UnitType=Units.CURRENCYFREQUENCY,
@@ -1884,25 +1909,85 @@ def __init__(self, model: Model):
18841909
f'The total is then divided over {model.surfaceplant.plant_lifetime.Name} years to calculate '
18851910
f'Redrilling costs per year.'
18861911
)
1912+
# noinspection SpellCheckingInspection
18871913
self.Cplant = self.OutputParameterDict[self.Cplant.Name] = OutputParameter(
18881914
Name="Surface Plant cost",
1915+
display_name='Surface power plant costs',
18891916
UnitType=Units.CURRENCY,
18901917
PreferredUnits=CurrencyUnit.MDOLLARS,
1891-
CurrentUnits=CurrencyUnit.MDOLLARS
1918+
CurrentUnits=CurrencyUnit.MDOLLARS,
1919+
# TODO incorporate direct references to relevant parameters for adjusting correlation in tooltip text
1920+
# TODO interpolate relevant constants (that are currently hardcoded) in tooltip text
1921+
ToolTipText='The built-in power plant cost correlations are based on the original correlations developed '
1922+
'by Beckers (2016), indexed to 2017 using the IHS Markit North American Power Capital Costs '
1923+
'Index (NAPCCI) excluding nuclear plants (IHS 2018). The ORC power plant cost data have been '
1924+
'updated with data from the 2016 GETEM tool (DOE 2016) and the geothermal binary power plants '
1925+
'study by Verkis (2014). '
1926+
# Note: actual author name above is "Verkís" but the unicode accented i may cause unexpected
1927+
# problems in consumers.
1928+
'Figure 4 in the Theoretical Basis shows the power plant capital cost expressed in $ kWe-1 '
1929+
'as a function of plant size and initial production temperature for subcritical ORC and '
1930+
'double-flash power plants. '
1931+
f'The default correlations in GEOPHIRES include {contingency_and_indirect_costs_tooltip_stem}. '
1932+
'For the same plant size and production temperature, double-flash power plants are considered '
1933+
'about 25% more expensive than single-flash power plants (Zeyghami 2010), and supercritical '
1934+
'ORC plants are roughly 10% more than subcritical ORC plants (Astolfi et al. 2014). A wide '
1935+
'range in power plant specific cost values is reported in academic and popular literature. '
1936+
'The GEOPHIRES built-in surface plant cost correlations represent typical values. However, '
1937+
'the user is recommended to provide their own power plant cost data if available for their '
1938+
'case study. The ORC plant specific cost decreases only moderately at higher temperatures. '
1939+
'The reasons are that when increasing the temperature, the ORC plant design also changes: '
1940+
'(1) a different organic fluid is selected, (2) piping, pump, heat exchangers, and other '
1941+
'equipment are designed to handle the higher temperature (and potentially also pressure), '
1942+
'requiring thicker walls, potentially different materials, etc., and (3) additional components '
1943+
'may be implemented, such as a heat recuperator, making the design and operation more complex. '
1944+
'Unlike flash power plants, ORC plants are a small, niche market, typically case specific, '
1945+
'and rely on relatively young technology, which has not been subject yet to decades of '
1946+
'technological advancement. The cost for direct-use heat applications is highly dependent '
1947+
'on the type of application. A generic cost of $250 kWth-1 is assumed '
1948+
f'{contingency_and_indirect_costs_tooltip}. '
1949+
'However, users are encouraged to provide their own cost figures for '
1950+
'their specific application. Beckers and Young (2017) collected several cost figures to '
1951+
'estimate the surface equipment cost for geothermal district-heating systems.'
18921952
)
18931953
self.Coamplant = self.OutputParameterDict[self.Coamplant.Name] = OutputParameter(
18941954
Name="O&M Surface Plant costs",
18951955
display_name='Power plant maintenance costs',
18961956
UnitType=Units.CURRENCYFREQUENCY,
18971957
PreferredUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR,
1898-
CurrentUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR
1958+
CurrentUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR,
1959+
# TODO parameterize relevant constants in tooltip text
1960+
# TODO update index year and/or make indexing parameterizable in tooltip text
1961+
ToolTipText='GEOPHIRES estimates the annual surface plant O&M costs as the sum of 1.5% of the total plant '
1962+
'capital cost (for annual non-labor costs), and 75% of the annual labor costs. The other 25% '
1963+
'of the labor costs are assigned to the wellfield O&M cost. The labor costs are calculated '
1964+
'internally in GEOPHIRES using the 2014 labor costs provided by Beckers (2016), indexed to '
1965+
'2017 using the Bureau of Labor Statistics (BLS) Employment Cost Index for utilities (2018). '
1966+
'The original 2014 labor cost correlation expresses the labor costs as a function of the plant '
1967+
'size (MW) using an approximate logarithmic curve fit to the built-in labor cost data in '
1968+
'GETEM.'
18991969
)
1970+
# noinspection SpellCheckingInspection
19001971
self.Cgath = self.OutputParameterDict[self.Cgath.Name] = OutputParameter(
19011972
Name="Field gathering system cost",
19021973
display_name='Field gathering system costs',
19031974
UnitType=Units.CURRENCY,
19041975
PreferredUnits=CurrencyUnit.MDOLLARS,
1905-
CurrentUnits=CurrencyUnit.MDOLLARS
1976+
CurrentUnits=CurrencyUnit.MDOLLARS,
1977+
# TODO interpolate constant values in tooltip text instead of hardcoding in tooltip text
1978+
ToolTipText='The built-in cost correlation for estimating the field gathering system cost includes '
1979+
'the cost for surface piping from each well to the plant and pumps for production and '
1980+
'injection wells. The length of the surface piping is assumed 750 m per well at a cost of '
1981+
'$500 per meter. The pumping cost for each pump in the production wells (line-shaft pumps) '
1982+
'and a single pump for the injection wells is calculated with the same correlation as GETEM. '
1983+
f'Contingency (default: '
1984+
f'{self.contingency_percentage.quantity().to(convertible_unit("%")).magnitude:g}%). '
1985+
f'and indirect costs (default: '
1986+
f'{self.indirect_capital_cost_percentage.quantity().to(convertible_unit("%")).magnitude}%) '
1987+
f'are added. '
1988+
'The built-in cost correlation does not include the cost of pipelines to an off-site heat '
1989+
'user or a district-heating system. These costs are estimated at $750 per meter pipeline '
1990+
'length and can be manually added by the user to the pipeline distribution costs.'
19061991
)
19071992
self.Cpiping = self.OutputParameterDict[self.Cpiping.Name] = OutputParameter(
19081993
Name="Transmission pipeline costs",
@@ -1917,7 +2002,9 @@ def __init__(self, model: Model):
19172002
UnitType=Units.CURRENCYFREQUENCY,
19182003
PreferredUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR,
19192004
CurrentUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR,
1920-
ToolTipText='Assumes $3.5/1,000 gallons of water' # TODO parameterize
2005+
ToolTipText=f'Default correlation: Assumes $3.50/1,000 gallons of water. '
2006+
f'Provide {self.oamwateradjfactor.Name} to multiply the default correlation.'
2007+
# Note: $3.50 could possibly be parameterized, but adjustment factor param serves the same purpose for now.
19212008
)
19222009
self.CCap = self.OutputParameterDict[self.CCap.Name] = OutputParameter(
19232010
Name="Total Capital Cost",
@@ -1934,7 +2021,9 @@ def __init__(self, model: Model):
19342021
display_name='Total operating and maintenance costs',
19352022
UnitType=Units.CURRENCYFREQUENCY,
19362023
PreferredUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR,
1937-
CurrentUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR
2024+
CurrentUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR,
2025+
ToolTipText=f'GEOPHIRES estimates the annual O&M costs as the sum of the annual surface plant, wellfield, '
2026+
f'make-up water, and pumping O&M costs.'
19382027
)
19392028
self.averageannualpumpingcosts = OutputParameter(
19402029
Name="Average Annual Pumping Costs",

src/geophires_x/OptionList.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,22 @@ def __ne__(self, other):
2525
return str(self) != str(other)
2626

2727

28+
_EXTRA_HEAT_SNIPPET = 'Heat sales considered as extra income'
29+
_EXTRA_ELECTRICITY_SNIPPET = 'Electricity sales considered as extra income'
30+
31+
2832
class EndUseOptions(GeophiresInputEnum):
29-
ELECTRICITY = 1, "Electricity"
30-
HEAT = 2, "Direct-Use Heat"
31-
COGENERATION_TOPPING_EXTRA_HEAT = 31, "Cogeneration Topping Cycle, Heat sales considered as extra income"
32-
COGENERATION_TOPPING_EXTRA_ELECTRICITY = 32, "Cogeneration Topping Cycle, Electricity sales considered as extra income"
33-
COGENERATION_BOTTOMING_EXTRA_HEAT = 41, "Cogeneration Bottoming Cycle, Heat sales considered as extra income"
34-
COGENERATION_BOTTOMING_EXTRA_ELECTRICITY = 42, "Cogeneration Bottoming Cycle, Electricity sales considered as extra income"
35-
COGENERATION_PARALLEL_EXTRA_HEAT = 51, "Cogeneration Parallel Cycle, Heat sales considered as extra income"
36-
COGENERATION_PARALLEL_EXTRA_ELECTRICITY = 52, "Cogeneration Parallel Cycle, Electricity sales considered as extra income"
33+
ELECTRICITY = 1, 'Electricity'
34+
HEAT = 2, 'Direct-Use Heat'
35+
COGENERATION_TOPPING_EXTRA_HEAT = 31, f'Cogeneration Topping Cycle, {_EXTRA_HEAT_SNIPPET}'
36+
COGENERATION_TOPPING_EXTRA_ELECTRICITY = 32, f'Cogeneration Topping Cycle, {_EXTRA_ELECTRICITY_SNIPPET}'
37+
COGENERATION_BOTTOMING_EXTRA_HEAT = 41, f'Cogeneration Bottoming Cycle, {_EXTRA_HEAT_SNIPPET}'
38+
COGENERATION_BOTTOMING_EXTRA_ELECTRICITY = 42, f'Cogeneration Bottoming Cycle, {_EXTRA_ELECTRICITY_SNIPPET}'
39+
COGENERATION_PARALLEL_EXTRA_HEAT = 51, f'Cogeneration Parallel Cycle, {_EXTRA_HEAT_SNIPPET}'
40+
COGENERATION_PARALLEL_EXTRA_ELECTRICITY = 52, f'Cogeneration Parallel Cycle, {_EXTRA_ELECTRICITY_SNIPPET}'
3741

3842
@staticmethod
39-
def from_input_string(input_string: str):
43+
def from_input_string(input_string: str) -> 'EndUseOptions':
4044
"""
4145
:rtype: EndUseOptions
4246
"""
@@ -48,11 +52,17 @@ def from_input_string(input_string: str):
4852
raise ValueError(f'Unknown End-Use Option input value: {input_string}')
4953

5054
@staticmethod
51-
def from_int(int_val):
55+
def from_int(int_val: int) -> 'EndUseOptions':
56+
"""
57+
:rtype: EndUseOptions
58+
"""
59+
5260
for member in __class__:
5361
if member.int_value == int_val:
5462
return member
5563

64+
raise ValueError(f'Unknown End-Use Option integer input value: {int_val}')
65+
5666

5767
class PlantType(GeophiresInputEnum):
5868
SUB_CRITICAL_ORC = 1, "Subcritical ORC"

0 commit comments

Comments
 (0)