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

Synchronize discount rate and fixed internal rate automatically #39

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.6.2
current_version = 3.6.3
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.6.2
version: 3.6.3
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 @@ -51,9 +51,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.6.2.svg
.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.6.3.svg
:alt: Commits since latest release
:target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.6.2...main
:target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.6.3...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 = '2024'
author = 'NREL'
copyright = f'{year}, {author}'
version = release = '3.6.2'
version = release = '3.6.3'

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.6.2',
version='3.6.3',
license='MIT',
description='GEOPHIRES is a free and open-source geothermal techno-economic simulator.',
long_description='{}\n{}'.format(
Expand Down
37 changes: 36 additions & 1 deletion src/geophires_x/Economics.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import math
import os
import sys
import numpy as np
import numpy_financial as npf
Expand Down Expand Up @@ -845,7 +846,7 @@ def __init__(self, model: Model):
Min=0.0,
Max=1.0,
UnitType=Units.PERCENT,
PreferredUnits=PercentUnit.PERCENT,
PreferredUnits=PercentUnit.TENTH,
CurrentUnits=PercentUnit.TENTH,
ErrMessage=f'assume default discount rate ({discount_rate_default_val})',
ToolTipText="Discount rate used in the Standard Levelized Cost Model"
Expand Down Expand Up @@ -1711,6 +1712,12 @@ def __init__(self, model: Model):
PreferredUnits=MassUnit.LB,
CurrentUnits=MassUnit.LB
)
self.interest_rate = self.OutputParameterDict[self.interest_rate.Name] = OutputParameter(
Name='Interest Rate',
UnitType=Units.PERCENT,
PreferredUnits=PercentUnit.PERCENT,
CurrentUnits=PercentUnit.PERCENT
)
self.TotalRevenue = self.OutputParameterDict[self.TotalRevenue.Name] = OutputParameter(
Name="Annual Revenue from Project",
UnitType=Units.CURRENCYFREQUENCY,
Expand Down Expand Up @@ -2147,15 +2154,43 @@ def read_parameters(self, model: Model) -> None:
if key.startswith("AddOn"):
self.DoAddOnCalculations.value = True
break

for key in model.InputParameters.keys():
if key.startswith("S-DAC-GT"):
self.DoSDACGTCalculations.value = True
break

coerce_int_params_to_enum_values(self.ParameterDict)
self.sync_interest_rate(model)

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

def sync_interest_rate(self, model):
def discount_rate_display() -> str:
return str(self.discountrate.quantity()).replace(' dimensionless', '')

if self.discountrate.Provided ^ self.FixedInternalRate.Provided:
if self.discountrate.Provided:
self.FixedInternalRate.value = self.discountrate.quantity().to(
convertible_unit(self.FixedInternalRate.CurrentUnits)).magnitude
model.logger.info(f'Set {self.FixedInternalRate.Name} to {self.FixedInternalRate.quantity()} '
f'because {self.discountrate.Name} was provided ({discount_rate_display()})')
else:
self.discountrate.value = self.FixedInternalRate.quantity().to(
convertible_unit(self.discountrate.CurrentUnits)).magnitude
model.logger.info(
f'Set {self.discountrate.Name} to {discount_rate_display()} because '
f'{self.FixedInternalRate.Name} was provided ({self.FixedInternalRate.quantity()})')

if self.discountrate.Provided and self.FixedInternalRate.Provided \
and self.discountrate.quantity().to(convertible_unit(self.FixedInternalRate.CurrentUnits)).magnitude \
!= self.FixedInternalRate.value:
model.logger.warning(f'{self.discountrate.Name} and {self.FixedInternalRate.Name} provided with different '
f'values ({discount_rate_display()}; {self.FixedInternalRate.quantity()}). '
f'It is recommended to only provide one of these values.')

self.interest_rate.value = self.discountrate.quantity().to(convertible_unit(self.interest_rate.CurrentUnits)).magnitude

def Calculate(self, model: Model) -> None:
"""
The Calculate function is where all the calculations are done.
Expand Down
6 changes: 2 additions & 4 deletions src/geophires_x/Outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
PlantType
from geophires_x.GeoPHIRESUtils import UpgradeSymbologyOfUnits, render_default, InsertImagesIntoHTML
from geophires_x.Parameter import Parameter
from geophires_x.Units import convertible_unit, Units, PercentUnit

NL = '\n'
validFilenameChars = "-_.() %s%s" % (string.ascii_letters, string.digits)
Expand Down Expand Up @@ -1637,10 +1638,7 @@ def PrintOutputs(self, model: Model):
f.write(f' Fixed Charge Rate (FCR): {model.economics.FCR.value*100.0:10.2f} ' + model.economics.FCR.CurrentUnits.value + NL)
elif model.economics.econmodel.value == EconomicModel.STANDARDIZED_LEVELIZED_COST:
f.write(' Economic Model = ' + model.economics.econmodel.value.value + NL)

# FIXME discountrate should not be multiplied by 100 here -
# it appears to be incorrectly claiming its units are percent when the actual value is in tenths.
f.write(f' Interest Rate: {model.economics.discountrate.value*100.0:10.2f} {model.economics.discountrate.CurrentUnits.value}\n')
f.write(f' {model.economics.interest_rate.Name}: {model.economics.interest_rate.value:10.2f} {model.economics.interest_rate.CurrentUnits.value}\n')

elif model.economics.econmodel.value == EconomicModel.BICYCLE:
f.write(' Economic Model = ' + model.economics.econmodel.value.value + NL)
Expand Down
2 changes: 2 additions & 0 deletions src/geophires_x/SUTRAEconomics.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,8 @@ def read_parameters(self, model: Model) -> None:
else:
model.logger.info('No parameters read because no content provided')

self.sync_interest_rate(model)

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

def Calculate(self, model: Model) -> None:
Expand Down
5 changes: 1 addition & 4 deletions src/geophires_x/SUTRAOutputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,7 @@ def PrintOutputs(self, model: Model):
f.write(f" Fixed Charge Rate (FCR): {model.economics.FCR.value*100.0:10.2f} " + model.economics.FCR.CurrentUnits.value + NL)
elif model.economics.econmodel.value == EconomicModel.STANDARDIZED_LEVELIZED_COST:
f.write(" Economic Model = " + model.economics.econmodel.value.value + NL)

# FIXME discountrate should not be multiplied by 100 here -
# it appears to be incorrectly claiming its units are percent when the actual value is in tenths.
f.write(f" Interest Rate: {model.economics.discountrate.value*100.0:10.2f} {model.economics.discountrate.CurrentUnits.value}\n")
f.write(f' {model.economics.interest_rate.Name}: {model.economics.interest_rate.value:10.2f} {model.economics.interest_rate.CurrentUnits.value}\n')

elif model.economics.econmodel.value == EconomicModel.BICYCLE:
f.write(" Economic Model = " + model.economics.econmodel.value.value + NL)
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.6.2'
__version__ = '3.6.3'
10 changes: 5 additions & 5 deletions tests/examples/SUTRAExample1.out
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

Simulation Metadata
----------------------
GEOPHIRES Version: 3.6.0
Simulation Date: 2024-10-15
Simulation Time: 13:07
Calculation Time: 0.507 sec
GEOPHIRES Version: 3.6.2
Simulation Date: 2024-10-18
Simulation Time: 10:43
Calculation Time: 0.516 sec

***SUMMARY OF RESULTS***

Expand All @@ -23,7 +23,7 @@ Simulation Metadata
***ECONOMIC PARAMETERS***

Economic Model = Standard Levelized Cost
Interest Rate: 7.00
Interest Rate: 7.00 %
Accrued financing during construction: 0.00 %
Project lifetime: 30 yr

Expand Down
1 change: 0 additions & 1 deletion tests/examples/example10_HP.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ Heat Pump COP, 2.8, --- [-]
Plant Lifetime,30, ---[years]
Economic Model,2, ---BICYCLE Levelized Cost Model
Discount Rate, 0.05, --- [-] Required if Standard LCOE/LCOH model is selected. See manual for details.
Fixed Internal Rate, 5, -- matches Discount Rate
Inflation Rate During Construction,0.05, ---[-]

# ***Capital and O&M Cost Parameters***
Expand Down
1 change: 0 additions & 1 deletion tests/examples/example11_AC.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ Absorption Chiller COP, 0.72, --- [-]
Plant Lifetime,30, ---[years]
Economic Model,2, ---BICYCLE Levelized Cost Model
Discount Rate, 0.05, --- [-] Required if Standard LCOE/LCOH model is selected. See manual for details.
Fixed Internal Rate, 5, -- matches Discount Rate
Inflation Rate During Construction,0.05, ---[-]

# ***Capital and O&M Cost Parameters***
Expand Down
1 change: 0 additions & 1 deletion tests/examples/example13.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ District Heating Road Length,3, ---[km] supersedes model option 2 if any val
Plant Lifetime,30, --- [years]
Economic Model,2, --- Should be 1 (FCR model), 2 (Standard LCOE/LCOH model), or 3 (Bicycle model).
Discount Rate,0.05,
Fixed Internal Rate,5, -- matches Discount Rate
Inflation Rate During Construction,0, --- [-]
Well Drilling and Completion Capital Cost Adjustment Factor,1, --- [-] Use built-in well cost correlation as is
Well Drilling Cost Correlation,1, --- [-] Use built-in well drilling cost correlation #1
Expand Down
1 change: 0 additions & 1 deletion tests/examples/example2.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ Surface Temperature,15, ---[deg.C]
Plant Lifetime,25, ---[years]
Economic Model,2, ---Standard Levelized Cost Model
Discount Rate,.05, ---[-]
Fixed Internal Rate,5, -- matches Discount Rate
Inflation Rate During Construction,0, ---[-]

# ***Capital and O&M Cost Parameters***
Expand Down
1 change: 0 additions & 1 deletion tests/examples/example_SHR-2.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ Tax Relief Per Year, 2.212
Do Carbon Price Calculations, True
Time steps per year, 6
Discount Rate, 0.06
Fixed Internal Rate, 6, -- matches Discount Rate
Reservoir Stimulation Capital Cost, 18
Well Drilling and Completion Capital Cost Adjustment Factor, 2
Injection Well Drilling and Completion Capital Cost Adjustment Factor, 2
Expand Down
44 changes: 44 additions & 0 deletions tests/test_geophires_x.py
Original file line number Diff line number Diff line change
Expand Up @@ -537,3 +537,47 @@ def s(r):

# deprecated is ignored if both are present.
self.assertDictEqual(both_params.result, non_deprecated_param.result)

def test_discount_rate_and_fixed_internal_rate(self):
def input_params(discount_rate=None, fixed_internal_rate=None):
params = {
'End-Use Option': EndUseOption.ELECTRICITY.value,
'Reservoir Model': 1,
'Reservoir Depth': 3,
'Gradient 1': 50,
}

if discount_rate is not None:
params['Discount Rate'] = discount_rate

if fixed_internal_rate is not None:
params['Fixed Internal Rate'] = fixed_internal_rate

return GeophiresInputParameters(params)

client = GeophiresXClient()

# noinspection PyPep8Naming
def assertHasLogRecordWithMessage(logs_, message):
assert message in [record.message for record in logs_.records]

with self.assertLogs(level='INFO') as logs:
result = client.get_geophires_result(input_params(discount_rate='0.042'))

assert result is not None
assert result.result['ECONOMIC PARAMETERS']['Interest Rate']['value'] == 4.2
assert result.result['ECONOMIC PARAMETERS']['Interest Rate']['unit'] == '%'
assertHasLogRecordWithMessage(
logs, 'Set Fixed Internal Rate to 4.2 percent because Discount Rate was provided (0.042)'
)

with self.assertLogs(level='INFO') as logs2:
result2 = client.get_geophires_result(input_params(fixed_internal_rate='4.2'))

assert result2 is not None
assert result2.result['ECONOMIC PARAMETERS']['Interest Rate']['value'] == 4.2
assert result2.result['ECONOMIC PARAMETERS']['Interest Rate']['unit'] == '%'

assertHasLogRecordWithMessage(
logs2, 'Set Discount Rate to 0.042 because Fixed Internal Rate was provided (4.2 percent)'
)