Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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.7.13
current_version = 3.7.16
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.13
version: 3.7.16
version_manager: "bump2version"
website: "https://github.com/NREL"
year_from: "2023"
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.13.svg
.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.7.16.svg
:alt: Commits since latest release
:target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.7.13...main
:target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.7.16...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.13'
version = release = '3.7.16'

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.13',
version='3.7.16',
license='MIT',
description='GEOPHIRES is a free and open-source geothermal techno-economic simulator.',
long_description='{}\n{}'.format(
Expand Down
10 changes: 8 additions & 2 deletions src/geophires_x/Economics.py
Original file line number Diff line number Diff line change
Expand Up @@ -1221,7 +1221,13 @@ def __init__(self, model: Model):
Max=100,
UnitType=Units.ENERGYCOST,
PreferredUnits=EnergyCostUnit.DOLLARSPERKWH,
CurrentUnits=EnergyCostUnit.DOLLARSPERKWH
CurrentUnits=EnergyCostUnit.DOLLARSPERKWH,
ToolTipText="The maximum price to which the electricity sale price can escalate. For example, if "
"Starting Electricity Sale Price = 0.10 USD/kWh and Electricity Escalation Rate = "
"0.01 USD/kWh/yr: Electricity Price will reach 0.15 USD/kWh after 4 years of escalation. "
"The price will then remain at 0.15 USD/kWh for the remaining years of the project lifetime. "
"If the Ending Electricity Sale Price is not reached by escalation during the project "
"lifetime, then the value will have no effect beyond allowing escalation to occur every year."
)
self.ElecEscalationStart = self.ParameterDict[self.ElecEscalationStart.Name] = intParameter(
"Electricity Escalation Start Year",
Expand Down Expand Up @@ -1541,7 +1547,7 @@ def __init__(self, model: Model):
CurrentUnits=EnergyCostUnit.DOLLARSPERMMBTU
) # $/MMBTU
self.Cstim = self.OutputParameterDict[self.Cstim.Name] = OutputParameter(
Name="O&M Surface Plant costs",
Name="O&M Surface Plant costs", # FIXME wrong name - should be Stimulation Costs
UnitType=Units.CURRENCY,
PreferredUnits=CurrencyUnit.MDOLLARS,
CurrentUnits=CurrencyUnit.MDOLLARS
Expand Down
68 changes: 38 additions & 30 deletions src/geophires_x/OutputsS_DAC_GT.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,35 +27,41 @@ def PrintOutputs(self, model) -> tuple:
sdac_results: list[OutputTableItem] = []
f.write(NL)
f.write(NL)
f.write(" ***S_DAC_GT ECONOMICS***" + NL)
f.write(" ***S-DAC-GT ECONOMICS***\n")
f.write(NL)
f.write(NL)
f.write(f" S-DAC-GT Report: Levelized Cost of Direct Air Capture (LCOD)" + NL)

msdac = model.sdacgteconomics

f.write(f" S-DAC-GT Report: Levelized Cost of Direct Air Capture (LCOD)\n")
sdac_results.append(OutputTableItem('S-DAC-GT Report: Levelized Cost of Direct Air Capture (LCOD)'))
f.write(f" Using grid-based electricity only: {model.sdacgteconomics.LCOD_elec.value:10.2f} " + model.sdacgteconomics.LCOD_elec.PreferredUnits.value + NL)
sdac_results.append(OutputTableItem('Using grid-based electricity only', '{0:10.2f}'.format(model.sdacgteconomics.LCOD_elec.value), model.sdacgteconomics.LCOD_elec.PreferredUnits.value))
f.write(f" Using natural gas only: {model.sdacgteconomics.LCOD_ng.value:10.2f} " + model.sdacgteconomics.LCOD_ng.PreferredUnits.value + NL)
sdac_results.append(OutputTableItem('Using natural gas only', '{0:10.2f}'.format(model.sdacgteconomics.LCOD_ng.value), model.sdacgteconomics.LCOD_ng.PreferredUnits.value))
f.write(f" Using geothermal energy only: {model.sdacgteconomics.LCOD_geo.value:10.2f} " + model.sdacgteconomics.LCOD_geo.PreferredUnits.value + NL + NL)
sdac_results.append(OutputTableItem('Using geothermal energy only', '{0:10.2f}'.format(model.sdacgteconomics.LCOD_geo.value), model.sdacgteconomics.LCOD_geo.PreferredUnits.value))
f.write(f" S-DAC-GT Report: CO2 Intensity of process (percent of CO2 mitigated that is emitted by S-DAC process)" + NL)
lcod_prefix = 'LCOD'
f.write(f" {lcod_prefix} using grid-based electricity only: {model.sdacgteconomics.LCOD_elec.value:10.2f} {msdac.LCOD_elec.PreferredUnits.value}\n")
sdac_results.append(OutputTableItem(f'Using grid-based electricity only', '{0:10.2f}'.format(msdac.LCOD_elec.value), msdac.LCOD_elec.PreferredUnits.value))
f.write(f" {lcod_prefix} using natural gas only: {model.sdacgteconomics.LCOD_ng.value:10.2f} {msdac.LCOD_ng.PreferredUnits.value}\n")
sdac_results.append(OutputTableItem('Using natural gas only', '{0:10.2f}'.format(model.sdacgteconomics.LCOD_ng.value), msdac.LCOD_ng.PreferredUnits.value))
f.write(f" {lcod_prefix} using geothermal energy only: {model.sdacgteconomics.LCOD_geo.value:10.2f} {msdac.LCOD_geo.PreferredUnits.value}\n\n")
sdac_results.append(OutputTableItem(f'Using geothermal energy only', '{0:10.2f}'.format(msdac.LCOD_geo.value), msdac.LCOD_geo.PreferredUnits.value))

f.write(f" S-DAC-GT Report: CO2 Intensity of process (percent of CO2 mitigated that is emitted by S-DAC process)\n")
sdac_results.append(OutputTableItem('S-DAC-GT Report: CO2 Intensity of process (percent of CO2 mitigated that is emitted by S-DAC process)'))
f.write(f" Using grid-based electricity only: {model.sdacgteconomics.CO2total_elec.value*100.0:10.2f}%" + NL)
sdac_results.append(OutputTableItem('Using grid-based electricity only', '{0:10.2f}'.format(model.sdacgteconomics.CO2total_elec.value*100.0), '%'))
f.write(f" Using natural gas only: {model.sdacgteconomics.CO2total_ng.value*100:10.2f}%" + NL)
sdac_results.append(OutputTableItem('Using natural gas only', '{0:10.2f}'.format(model.sdacgteconomics.CO2total_ng.value*100.0), '%'))
f.write(f" Using geothermal energy only: {model.sdacgteconomics.CO2total_geo.value*100:10.2f}%" + NL + NL)
sdac_results.append(OutputTableItem('Using geothermal energy only', '{0:10.2f}'.format(model.sdacgteconomics.CO2total_geo.value*100.0), '%'))
f.write(f" Geothermal LCOH: {model.sdacgteconomics.LCOH.value:10.4f} " + model.sdacgteconomics.LCOH.PreferredUnits.value + NL)
sdac_results.append(OutputTableItem('Geothermal LCOH', '{0:10.4f}'.format(model.sdacgteconomics.LCOH.value), model.sdacgteconomics.LCOH.PreferredUnits.value))
f.write(f" Geothermal Ratio (electricity vs heat):{model.sdacgteconomics.percent_thermal_energy_going_to_heat.value*100:10.4f}%" + NL)
sdac_results.append(OutputTableItem('Geothermal Ratio (electricity vs heat)', '{0:10.4f}'.format(model.sdacgteconomics.percent_thermal_energy_going_to_heat.value*100.0), '%'))
f.write(f" Percent Energy Devoted To Process: {model.sdacgteconomics.EnergySplit.value*100:10.4f}%" + NL + NL)
sdac_results.append(OutputTableItem('Percent Energy Devoted To Process', '{0:10.4f}'.format(model.sdacgteconomics.EnergySplit.value*100.0), '%'))
f.write(f" Total Tonnes of CO2 Captured: {model.sdacgteconomics.CarbonExtractedTotal.value:,.2f} " + model.sdacgteconomics.CarbonExtractedTotal.PreferredUnits.value + NL)
sdac_results.append(OutputTableItem('Total Tonnes of CO2 Captured', '{0:,.2f}'.format(model.sdacgteconomics.CarbonExtractedTotal.value), model.sdacgteconomics.CarbonExtractedTotal.PreferredUnits.value))
f.write(f" Total Cost of Capture: {model.sdacgteconomics.S_DAC_GTCummCashFlow.value[len(model.sdacgteconomics.S_DAC_GTCummCashFlow.value)-1]:,.2f} " + model.sdacgteconomics.S_DAC_GTCummCashFlow.PreferredUnits.value + NL)
sdac_results.append(OutputTableItem('Total Cost of Capture', '{0:,.2f}'.format(model.sdacgteconomics.S_DAC_GTCummCashFlow.value[len(model.sdacgteconomics.S_DAC_GTCummCashFlow.value)-1]), model.sdacgteconomics.S_DAC_GTCummCashFlow.PreferredUnits.value))
co2i_prefix = 'CO2 Intensity'
f.write(f" {co2i_prefix} using grid-based electricity only: {msdac.CO2total_elec.value*100.0:10.2f} %\n") # TODO CurrentUnits
sdac_results.append(OutputTableItem('Using grid-based electricity only', '{0:10.2f}'.format(msdac.CO2total_elec.value*100.0), '%'))
f.write(f" {co2i_prefix} using natural gas only: {msdac.CO2total_ng.value*100:10.2f} %\n") # TODO CurrentUnits
sdac_results.append(OutputTableItem('Using natural gas only', '{0:10.2f}'.format(msdac.CO2total_ng.value*100.0), '%'))
f.write(f" {co2i_prefix} using geothermal energy only: {msdac.CO2total_geo.value*100:10.2f} %\n\n") # TODO CurrentUnits
sdac_results.append(OutputTableItem('Using geothermal energy only', '{0:10.2f}'.format(msdac.CO2total_geo.value*100.0), '%'))
f.write(f" Geothermal LCOH: {msdac.LCOH.value:10.4f} {msdac.LCOH.PreferredUnits.value}\n")
sdac_results.append(OutputTableItem('Geothermal LCOH', '{0:10.4f}'.format(msdac.LCOH.value), msdac.LCOH.PreferredUnits.value))
f.write(f" Geothermal Ratio (electricity vs heat):{msdac.percent_thermal_energy_going_to_heat.value*100:10.4f} %\n") # TODO CurrentUnits
sdac_results.append(OutputTableItem('Geothermal Ratio (electricity vs heat)', '{0:10.4f}'.format(msdac.percent_thermal_energy_going_to_heat.value*100.0), '%'))
f.write(f" Percent Energy Devoted To Process: {msdac.EnergySplit.value*100:10.4f} %\n\n") # TODO CurrentUnits
sdac_results.append(OutputTableItem('Percent Energy Devoted To Process', '{0:10.4f}'.format(msdac.EnergySplit.value*100.0), '%'))
f.write(f" Total Tonnes of CO2 Captured: {msdac.CarbonExtractedTotal.value:,.2f} {msdac.CarbonExtractedTotal.PreferredUnits.value}\n")
sdac_results.append(OutputTableItem('Total Tonnes of CO2 Captured', '{0:,.2f}'.format(msdac.CarbonExtractedTotal.value), msdac.CarbonExtractedTotal.PreferredUnits.value))
f.write(f" Total Cost of Capture: {msdac.S_DAC_GTCummCashFlow.value[len(msdac.S_DAC_GTCummCashFlow.value)-1]:,.2f} {msdac.S_DAC_GTCummCashFlow.PreferredUnits.value}\n")
sdac_results.append(OutputTableItem('Total Cost of Capture', '{0:,.2f}'.format(msdac.S_DAC_GTCummCashFlow.value[len(msdac.S_DAC_GTCummCashFlow.value)-1]), msdac.S_DAC_GTCummCashFlow.PreferredUnits.value))
f.write(NL)

# Build the data frame to hold the SDAC result profile
Expand All @@ -77,9 +83,9 @@ def PrintOutputs(self, model) -> tuple:

f.write(NL)
f.write(" **********************" + NL)
f.write(" * S_DAC_GT PROFILE *" + NL)
f.write(" * S-DAC-GT PROFILE *" + NL)
f.write(" **********************" + NL)
f.write("Year Carbon Cumm. Carbon S_DAC_GT S_DAC_GT Cumm. Cumm. Cost" + NL)
f.write("Year Carbon Cumm. Carbon S-DAC-GT S-DAC-GT Cumm. Cumm. Cost" + NL)
f.write("Since Captured Captured Annual Cost Cash Flow Cost Per Tonne" + NL)
f.write("Start ("+model.sdacgteconomics.CarbonExtractedAnnually.PreferredUnits.value +
") ("+model.sdacgteconomics.S_DAC_GTCummCarbonExtracted.PreferredUnits.value +
Expand All @@ -93,11 +99,13 @@ def PrintOutputs(self, model) -> tuple:

except BaseException as ex:
tb = sys.exc_info()[2]
msg = "Error: GEOPHIRES failed to Failed to write the output file. Exiting...Line %i" % tb.tb_lineno

print(str(ex))
print("Error: GEOPHIRES failed to Failed to write the output file. Exiting....Line %i" % tb.tb_lineno)
print(msg)
model.logger.critical(str(ex))
model.logger.critical("Error: GEOPHIRES failed to Failed to write the output file. Exiting....Line %i" % tb.tb_lineno)
sys.exit()
model.logger.critical(msg)
raise RuntimeError(msg, e)

model.logger.info(f'Complete {str(__class__)}: {__name__}')

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.13'
__version__ = '3.7.16'
41 changes: 40 additions & 1 deletion src/geophires_x_client/geophires_x_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,19 @@ class GeophiresXResult:
'Project MOIC (including carbon credit)',
'Project Payback Period (including carbon credit)',
],
'S-DAC-GT ECONOMICS': [
# TODO S-DAC-GT Report sub-titles as string value fields
'LCOD using grid-based electricity only',
'LCOD using natural gas only',
'LCOD using geothermal energy only',
'CO2 Intensity using grid-based electricity only',
'CO2 Intensity using natural gas only',
'CO2 Intensity using geothermal energy only',
'Geothermal LCOH',
'Geothermal Ratio (electricity vs heat)',
'Percent Energy Devoted To Process',
'Total Cost of Capture',
],
'ENGINEERING PARAMETERS': [
'Number of Production Wells',
'Number of Injection Wells',
Expand Down Expand Up @@ -359,6 +372,10 @@ def __init__(self, output_file_path, logger_name=None):
if ccus_profile is not None:
self.result['CCUS PROFILE'] = ccus_profile

sdacgt_profile = self._get_sdacgt_profile()
if sdacgt_profile is not None:
self.result['S-DAC-GT PROFILE'] = sdacgt_profile

self.result['metadata'] = {'output_file_path': self.output_file_path}
for metadata_field in GeophiresXResult._METADATA_FIELDS:
self.result['metadata'][metadata_field] = self._get_equal_sign_delimited_field(metadata_field)
Expand Down Expand Up @@ -583,6 +600,27 @@ def extract_table_header(lines: list) -> list:
self._logger.debug(f'Failed to get extended economic profile: {e}')
return None

def _get_sdacgt_profile(self):
def extract_table_header(lines: list) -> list:
# Tried various regexy approaches to extract this programmatically but landed on hard-coding.
return [
'Year Since Start',
'Carbon Captured (tonne/yr)',
'Cumm. Carbon Captured (tonne)',
'S-DAC-GT Annual Cost (USD/yr)',
'S-DAC-GT Cumm. Cash Flow (USD)',
'Cumm. Cost Per Tonne (USD/tonne)',
]

try:
lines = self._get_profile_lines('S-DAC-GT PROFILE')
profile = [extract_table_header(lines)]
profile.extend(self._extract_addons_style_table_data(lines))
return profile
except BaseException as e:
self._logger.debug(f'Failed to get S-DAC-GT profile: {e}')
return None

def _get_ccus_profile(self):
"""
FIXME TODO - transform from revenue & cashflow if present (CCUS profile replaced by revenue & cashflow
Expand Down Expand Up @@ -669,10 +707,11 @@ def _get_data_from_profile_lines(self, profile_lines):
return data

def _parse_number(self, number_str, field='string') -> int | float:
if number_str == 'N/A':
if number_str == 'N/A' or number_str is None:
return None

try:
number_str = number_str.replace(',', '')
if '.' in number_str:
# TODO should probably ideally use decimal.Decimal to preserve precision,
# i.e. 1.00 for USD instead of 1.0
Expand Down
2 changes: 1 addition & 1 deletion src/geophires_x_schema_generator/geophires-request.json
Original file line number Diff line number Diff line change
Expand Up @@ -1956,7 +1956,7 @@
"maximum": 100
},
"Ending Electricity Sale Price": {
"description": "",
"description": "The maximum price to which the electricity sale price can escalate. For example, if Starting Electricity Sale Price = 0.10 USD/kWh and Electricity Escalation Rate = 0.01 USD/kWh/yr: Electricity Price will reach 0.15 USD/kWh after 4 years of escalation. The price will then remain at 0.15 USD/kWh for the remaining years of the project lifetime. If the Ending Electricity Sale Price is not reached by escalation during the project lifetime, then the value will have no effect beyond allowing escalation to occur every year.",
"type": "number",
"units": "USD/kWh",
"category": "Economics",
Expand Down
Loading