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.14.0
current_version = 3.14.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.14.0
version: 3.14.1
version_manager: "bump2version"
website: "https://github.com/NREL"
year_from: "2023"
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ GEOPHIRES v3 (2023-2026)
3.14
^^^^

3.14: `Well integrity parameterization to trigger redrilling; Drawdown Parameter Schedule; Input params CSV unit and comment parsing; State ITC Amount; SAM-EM Other Incentives and One-time Grants fix <https://github.com/NatLabRockies/GEOPHIRES-X/pull/502>`__ | `release <https://github.com/NREL/GEOPHIRES-X/releases/tag/v3.14.0>`__ | **Changed:** SAM Economic Models inputs with Other Incentives and One-time Grants Etc parameters now calculate Overnight Capital Cost and Total CAPEX correctly. See `the tracking issue <https://github.com/NatLabRockies/GEOPHIRES-X/issues/504>`__ for details.
3.14: `Well integrity parameterization to trigger redrilling; Drawdown Parameter Schedule; Input params CSV unit and comment parsing; State ITC Amount; SAM-EM Other Incentives and One-time Grants fix; Stimulation costs per well outputs <https://github.com/NatLabRockies/GEOPHIRES-X/pull/502>`__ | `release <https://github.com/NREL/GEOPHIRES-X/releases/tag/v3.14.1>`__ | **Changed:** SAM Economic Models inputs with Other Incentives and One-time Grants Etc parameters now calculate Overnight Capital Cost and Total CAPEX correctly. See `the tracking issue <https://github.com/NatLabRockies/GEOPHIRES-X/issues/504>`__ for details.


3.13
Expand Down
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,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.14.0.svg
.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.14.1.svg
:alt: Commits since latest release
:target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.14.0...main
:target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.14.1...main

.. |docs| image:: https://readthedocs.org/projects/GEOPHIRES-X/badge/?style=flat
:target: https://softwareengineerprogrammer.github.io/GEOPHIRES
Expand Down
206 changes: 103 additions & 103 deletions docs/_images/fervo_project_cape-5-sensitivity-analysis-irr.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
200 changes: 100 additions & 100 deletions docs/_images/fervo_project_cape-5-sensitivity-analysis-lcoe.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
210 changes: 105 additions & 105 deletions docs/_images/fervo_project_cape-5-sensitivity-analysis-project_npv.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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.14.0'
version = release = '3.14.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.14.0',
version='3.14.1',
license='MIT',
description='GEOPHIRES is a free and open-source geothermal techno-economic simulator.',
long_description='{}\n{}'.format(
Expand Down
111 changes: 97 additions & 14 deletions src/geophires_x/Economics.py
Original file line number Diff line number Diff line change
Expand Up @@ -1972,6 +1972,34 @@ def __init__(self, model: Model):
f'total stimulation cost. '
f'For traditional hydrothermal reservoirs, {self.ccstimfixed.Name} should be set to $0.'
)
# noinspection SpellCheckingInspection
self.cstim_per_well = self.OutputParameterDict[self.cstim_per_well.Name] = OutputParameter(
Name='Stimulation costs per well',
value=None,
UnitType=Units.CURRENCY,
PreferredUnits=CurrencyUnit.MDOLLARS,
CurrentUnits=CurrencyUnit.MDOLLARS,
ToolTipText='Stimulation cost per well, including direct and indirect costs and contingency.'
)
# noinspection SpellCheckingInspection
self.cstim_per_production_well = self.OutputParameterDict[self.cstim_per_production_well.Name] = OutputParameter(
Name='Stimulation costs per production well',
value=None,
UnitType=Units.CURRENCY,
PreferredUnits=CurrencyUnit.MDOLLARS,
CurrentUnits=CurrencyUnit.MDOLLARS,
ToolTipText='Stimulation cost per producton well, including direct and indirect costs and contingency.'
)
# noinspection SpellCheckingInspection
self.cstim_per_injection_well = self.OutputParameterDict[self.cstim_per_injection_well.Name] = OutputParameter(
Name='Stimulation costs per injection well',
value=None,
UnitType=Units.CURRENCY,
PreferredUnits=CurrencyUnit.MDOLLARS,
CurrentUnits=CurrencyUnit.MDOLLARS,
ToolTipText='Stimulation cost per injection well, including direct and indirect costs and contingency.'
)


# TODO switch order to align with theoretical basis, which lists indirect costs first
contingency_and_indirect_costs_tooltip_stem = (
Expand Down Expand Up @@ -2446,15 +2474,19 @@ def __init__(self, model: Model):
self.RITCValue = self.OutputParameterDict[self.RITCValue.Name] = investment_tax_credit_output_parameter()
self.cost_one_production_well = self.OutputParameterDict[self.cost_one_production_well.Name] = OutputParameter(
Name="Cost of One Production Well",
display_name='Drilling and completion costs per production well',
UnitType=Units.CURRENCY,
PreferredUnits=CurrencyUnit.MDOLLARS,
CurrentUnits=CurrencyUnit.MDOLLARS
CurrentUnits=CurrencyUnit.MDOLLARS,
ToolTipText='Drilling and completion costs per vertical production well'
)
self.cost_one_injection_well = self.OutputParameterDict[self.cost_one_injection_well.Name] = OutputParameter(
Name="Cost of One Injection Well",
display_name='Drilling and completion costs per injection well',
UnitType=Units.CURRENCY,
PreferredUnits=CurrencyUnit.MDOLLARS,
CurrentUnits=CurrencyUnit.MDOLLARS
CurrentUnits=CurrencyUnit.MDOLLARS,
ToolTipText='Drilling and completion costs per vertical injection well'
)
self.cost_lateral_section = self.OutputParameterDict[self.cost_lateral_section.Name] = OutputParameter(
Name="Cost of the entire (multi-) lateral section of a well",
Expand Down Expand Up @@ -3047,25 +3079,76 @@ def calculate_wellfield_costs(self, model: Model) -> None:
)

def calculate_stimulation_costs(self, model: Model) -> PlainQuantity:
production_wells_stimulated: bool = self.stimulation_cost_per_production_well.Provided
if self.ccstimfixed.Valid:
stimulation_costs = self.ccstimfixed.quantity().to(self.Cstim.CurrentUnits).magnitude
stimulation_costs_cstim_u = self.ccstimfixed.quantity().to(self.Cstim.CurrentUnits).magnitude

# Ideally we'd infer per-well costs per the below logic, but this doesn't necessarily
# cleanly map to legacy parameterizations that may have implicitly assumed that stimulation costs include
# both production and injection wells, even though the default behavior is and always has been only
# injection wells are stimulated. Production wells are only assumed to be stimulated when
# Reservoir Stimulation Capital Cost per Production Well is provided, which was added in v3.9.32.

# num_stimulated_wells = model.wellbores.ninj.value
# if production_wells_stimulated:
# num_stimulated_wells += model.wellbores.nprod.value
#
# self.cstim_per_well.value = (
# self.ccstimfixed.quantity() / num_stimulated_wells
# ).to(self.cstim_per_well.CurrentUnits).magnitude
# else:
# self.cstim_per_injection_well.value = (
# self.ccstimfixed.quantity() / num_stimulated_wells
# ).to(self.cstim_per_injection_well.CurrentUnits).magnitude

ret = quantity(stimulation_costs_cstim_u, self.Cstim.CurrentUnits)
else:
stim_cost_per_injection_well = self.stimulation_cost_per_injection_well.quantity().to(
direct_stim_cost_per_injection_well_cstim_u = self.stimulation_cost_per_injection_well.quantity().to(
self.Cstim.CurrentUnits).magnitude
stim_cost_per_production_well = self.stimulation_cost_per_production_well.quantity().to(
direct_stim_cost_per_production_well_cstim_u = self.stimulation_cost_per_production_well.quantity().to(
self.Cstim.CurrentUnits).magnitude

stimulation_costs = (
(
stim_cost_per_injection_well * model.wellbores.ninj.value
+ stim_cost_per_production_well * model.wellbores.nprod.value
)
* self.ccstimadjfactor.value
* self._stimulation_indirect_cost_factor
* self._contingency_factor
def _total_cost_per_well(direct_cost_per_well) -> float:
return (direct_cost_per_well * self.ccstimadjfactor.value * self._stimulation_indirect_cost_factor
* self._contingency_factor)

total_stim_cost_per_injection_well_cstim_u = _total_cost_per_well(
direct_stim_cost_per_injection_well_cstim_u)
total_stim_cost_per_production_well_cstim_u = _total_cost_per_well(
direct_stim_cost_per_production_well_cstim_u)

stimulation_costs_cstim_u = (
total_stim_cost_per_injection_well_cstim_u * model.wellbores.ninj.value
+ total_stim_cost_per_production_well_cstim_u * model.wellbores.nprod.value
)

return quantity(stimulation_costs, self.Cstim.CurrentUnits)
ret = quantity(stimulation_costs_cstim_u, self.Cstim.CurrentUnits)

if self.stimulation_cost_per_injection_well.Provided or self.stimulation_cost_per_production_well.Provided:
self.cstim_per_injection_well.value = quantity(
total_stim_cost_per_injection_well_cstim_u, self.Cstim.CurrentUnits).to(
self.cstim_per_injection_well.CurrentUnits).magnitude

if production_wells_stimulated:
self.cstim_per_production_well.value = quantity(
total_stim_cost_per_production_well_cstim_u, self.Cstim.CurrentUnits).to(
self.cstim_per_production_well.CurrentUnits).magnitude
else:
# Only injection wells are assumed to be stimulated unless production well cost param is provided,
# so keep this value as None instead of 0
pass

if total_stim_cost_per_injection_well_cstim_u == total_stim_cost_per_production_well_cstim_u:
self.cstim_per_well.value = ret.to(
self.cstim_per_well.CurrentUnits).magnitude / (model.wellbores.ninj.value + model.wellbores.nprod.value)
else:
pass # Leave cstim_per_well value = None
else:
# Ideally we'd infer per-well costs per the above logic; see relevant comment above re: legacy
# parameterizations.
pass

return ret

def calculate_field_gathering_costs(self, model: Model) -> None:
if self.ccgathfixed.Valid:
Expand Down
41 changes: 36 additions & 5 deletions src/geophires_x/Outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,18 +485,18 @@ def PrintOutputs(self, model: Model):
f.write(f' {model.economics.Cwell.display_name}: {model.economics.Cwell.value:10.2f} {model.economics.Cwell.CurrentUnits.value}\n')

if 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 vertical production well: {econ.cost_one_production_well.value:10.2f} {econ.cost_one_production_well.CurrentUnits.value}\n')
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}\n')
f.write(f' {econ.cost_per_lateral_section.Name}: {econ.cost_per_lateral_section.value:10.2f} {econ.cost_lateral_section.CurrentUnits.value}\n')
elif round(econ.cost_one_production_well.value, 4) != round(econ.cost_one_injection_well.value, 4) \
and model.economics.cost_one_injection_well.value != -1:
f.write(f' Drilling and completion costs per 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 injection well: {econ.cost_one_injection_well.value:10.2f} ' + econ.cost_one_injection_well.CurrentUnits.value + NL)
f.write(f' {econ.cost_one_production_well.display_name}: {econ.cost_one_production_well.value:10.2f} {econ.cost_one_production_well.CurrentUnits.value}\n')
f.write(f' {econ.cost_one_injection_well.display_name}: {econ.cost_one_injection_well.value:10.2f} {econ.cost_one_injection_well.CurrentUnits.value}\n')
else:
cpw_label = Outputs._field_label(econ.drilling_and_completion_costs_per_well.display_name, 47)
f.write(f' {cpw_label}{econ.drilling_and_completion_costs_per_well.value:10.2f} {econ.Cwell.CurrentUnits.value}\n')

f.write(f' {econ.Cstim.display_name}: {econ.Cstim.value:10.2f} {econ.Cstim.CurrentUnits.value}\n')
self.write_stimulation_costs_outputs(econ, f)

f.write(f' {econ.Cplant.display_name}: {econ.Cplant.value:10.2f} {econ.Cplant.CurrentUnits.value}\n')
if model.surfaceplant.enduse_option.value.is_cogeneration_end_use_option:
Expand Down Expand Up @@ -962,6 +962,35 @@ def o(output_param: OutputParameter):
f'{ii:3.0f} {o(econ.ElecPrice).value[ii]:5.2f} {o(econ.ElecRevenue).value[ii]:5.2f} {o(econ.ElecCummRevenue).value[ii]:5.2f} | {o(econ.HeatPrice).value[ii]:5.2f} {o(econ.HeatRevenue).value[ii]:5.2f} {o(econ.HeatCummRevenue).value[ii]:5.2f} | {o(econ.CoolingPrice).value[ii]:5.2f} {o(econ.CoolingRevenue).value[ii]:5.2f} {o(econ.CoolingCummRevenue).value[ii]:5.2f} | {o(econ.CarbonPrice).value[ii]:5.2f} {o(econ.CarbonRevenue).value[ii]:5.2f} {o(econ.CarbonCummCashFlow).value[ii]:5.2f} | {opex:5.2f} {o(econ.TotalRevenue).value[ii]:5.2f} {o(econ.TotalCummRevenue).value[ii]:5.2f}\n')
f.write(NL)

# noinspection PyMethodMayBeStatic
def write_stimulation_costs_outputs(self, econ: Economics, f) -> None:
f.write(
f' '
f'{econ.Cstim.display_name}: {econ.Cstim.value:10.2f}'
f' '
f'{econ.Cstim.CurrentUnits.value}\n'
)

def _write_output(_stim_cost_per_well_output: OutputParameter) -> None:
if _stim_cost_per_well_output.value is not None:
scw_label = Outputs._field_label(_stim_cost_per_well_output.display_name, 43)
# noinspection PyStringConversionWithoutDunderMethod
f.write(
f' '
f'{scw_label}{_stim_cost_per_well_output.value:10.2f}'
f' '
f'{_stim_cost_per_well_output.CurrentUnits.value}\n'
)

if econ.cstim_per_well.value is not None:
_write_output(econ.cstim_per_well)
else:
for stim_cost_per_well_output in [
econ.cstim_per_production_well,
econ.cstim_per_injection_well
]:
_write_output(stim_cost_per_well_output)

# noinspection PyMethodMayBeStatic
def get_sam_cash_flow_profile_output(self, model):
ret = '\n'
Expand Down Expand Up @@ -1006,6 +1035,8 @@ def _print_extended_economics_header(self, f_output_file: TextIOWrapper | None =
if close_f:
f_output_file.close()



@staticmethod
def _field_label(field_name: str, print_width_before_value: int) -> str:
return f'{field_name}:{" " * (print_width_before_value - len(field_name) - 1)}'
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.14.0'
__version__ = '3.14.1'
3 changes: 3 additions & 0 deletions src/geophires_x_client/geophires_x_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ class GeophiresXResult:
'Drilling and completion costs (for redrilling)',
'Drilling and completion costs per redrilled well',
'Stimulation costs',
'Stimulation costs per well',
'Stimulation costs per production well',
'Stimulation costs per injection well',
'Stimulation costs (for redrilling)',
'Surface power plant costs',
'of which Electrical Plant Cost',
Expand Down
Loading
Loading