Skip to content

Commit

Permalink
Merge pull request #39 from softwareengineerprogrammer/combine-discou…
Browse files Browse the repository at this point in the history
…nt-rate-and-fixed-internal-rate-3

Synchronize discount rate and fixed internal rate automatically
  • Loading branch information
softwareengineerprogrammer authored Oct 18, 2024
2 parents eccce2f + 62b2d19 commit 6281bd8
Show file tree
Hide file tree
Showing 17 changed files with 97 additions and 26 deletions.
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)'
)

0 comments on commit 6281bd8

Please sign in to comment.