Skip to content

Commit

Permalink
Merge pull request #146 from softwareengineerprogrammer/main
Browse files Browse the repository at this point in the history
Fix Revenue & Cashflow Profile Units, Update Utilization Efficiency (v3.4.16)
  • Loading branch information
softwareengineerprogrammer authored Mar 5, 2024
2 parents e94df0e + 81e2ff1 commit 926dda3
Show file tree
Hide file tree
Showing 51 changed files with 1,040 additions and 946 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.4.14
current_version = 3.4.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.4.14
version: 3.4.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 @@ -47,9 +47,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.4.14.svg
.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.4.16.svg
:alt: Commits since latest release
:target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.4.14...main
:target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.4.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 = '2023'
author = 'NREL'
copyright = f'{year}, {author}'
version = release = '3.4.14'
version = release = '3.4.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.4.14',
version='3.4.16',
license='MIT',
description='GEOPHIRES is a free and open-source geothermal techno-economic simulator.',
long_description='{}\n{}'.format(
Expand Down
8 changes: 3 additions & 5 deletions src/geophires_x/Economics.py
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,6 @@ def __init__(self, model: Model):
)
self.DoAddOnCalculations = self.ParameterDict[self.DoAddOnCalculations.Name] = boolParameter(
"Do AddOn Calculations",
value=False,
DefaultValue=False,
UnitType=Units.NONE,
Required=False,
Expand All @@ -820,7 +819,6 @@ def __init__(self, model: Model):
)
self.DoCarbonCalculations = self.ParameterDict[self.DoCarbonCalculations.Name] = boolParameter(
"Do Carbon Price Calculations",
value=False,
DefaultValue=False,
UnitType=Units.NONE,
Required=False,
Expand Down Expand Up @@ -1301,19 +1299,19 @@ def __init__(self, model: Model):
"Electricity Sale Price Model",
UnitType=Units.ENERGYCOST,
PreferredUnits=EnergyCostUnit.CENTSSPERKWH,
CurrentUnits=EnergyCostUnit.CENTSSPERKWH
CurrentUnits=EnergyCostUnit.DOLLARSPERKWH,
)
self.HeatPrice = self.OutputParameterDict[self.HeatPrice.Name] = OutputParameter(
"Heat Sale Price Model",
UnitType=Units.ENERGYCOST,
PreferredUnits=EnergyCostUnit.CENTSSPERKWH,
CurrentUnits=EnergyCostUnit.CENTSSPERKWH
CurrentUnits=EnergyCostUnit.DOLLARSPERKWH,
)
self.CoolingPrice = self.OutputParameterDict[self.CoolingPrice.Name] = OutputParameter(
"Heat Sale Price Model",
UnitType=Units.ENERGYCOST,
PreferredUnits=EnergyCostUnit.CENTSSPERKWH,
CurrentUnits=EnergyCostUnit.CENTSSPERKWH
CurrentUnits=EnergyCostUnit.DOLLARSPERKWH,
)
self.CarbonPrice = self.OutputParameterDict[self.CarbonPrice.Name] = OutputParameter(
"Carbon Price Model",
Expand Down
2 changes: 2 additions & 0 deletions src/geophires_x/GEOPHIRES3_newunits.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
USD = [currency]
cents = USD / 100
7 changes: 3 additions & 4 deletions src/geophires_x/GeoPHIRESUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from functools import lru_cache
from typing import Optional

import pint
import scipy
from pint.facets.plain import PlainQuantity
from scipy.interpolate import interp1d
Expand All @@ -19,6 +18,7 @@
import CoolProp.CoolProp as CP

from geophires_x.Parameter import ParameterEntry
from geophires_x.Units import get_unit_registry

_logger = logging.getLogger('root') # TODO use __name__ instead of root

Expand Down Expand Up @@ -85,14 +85,13 @@
0.4,
0.4,
0.4,
0.4,
0.5, # Extrapolate from fig 2 in https://geothermal-energy-journal.springeropen.com/articles/10.1186/s40517-019-0119-6
]
)

_interp_util_eff_func = interp1d(_T, _UtilEff)

_ureg = pint.get_application_registry()
_ureg.load_definitions(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'GEOPHIRES3_newunits.txt'))
_ureg = get_unit_registry()


def quantity(value: float, unit: str) -> PlainQuantity:
Expand Down
73 changes: 44 additions & 29 deletions src/geophires_x/Outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
import numpy as np
from matplotlib import pyplot as plt
import geophires_x.Model as Model
from geophires_x.Parameter import ConvertUnitsBack, ConvertOutputUnits, LookupUnits
from geophires_x.Economics import Economics
from geophires_x.Parameter import ConvertUnitsBack, ConvertOutputUnits, LookupUnits, OutputParameter
from geophires_x.OptionList import EndUseOptions, EconomicModel, ReservoirModel, FractureShape, ReservoirVolume, \
PlantType

Expand Down Expand Up @@ -87,17 +88,21 @@ def PrintOutputs(self, model: Model):
param = obj.ParameterDict[key]
if not param.UnitsMatch: ConvertUnitsBack(param, model)

# now we need to loop through all thw output parameters to update their units to
# now we need to loop through all the output parameters to update their units to
# whatever units the user has specified.
# i.e., they may have specified that all LENGTH results must be in feet, so we need to convert those
# from whatever LENGTH unit they are to feet.
# same for all the other classes of units (TEMPERATURE, DENSITY, etc).

for obj in [model.reserv, model.wellbores, model.surfaceplant, model.economics]:
for key in obj.OutputParameterDict:
output_param:OutputParameter = obj.OutputParameterDict[key]
if key in self.ParameterDict:
if self.ParameterDict[key] != obj.OutputParameterDict[key].CurrentUnits:
ConvertOutputUnits(obj.OutputParameterDict[key], self.ParameterDict[key], model)
if self.ParameterDict[key].PreferredUnits != output_param.CurrentUnits:
ConvertOutputUnits(output_param, self.ParameterDict[key].PreferredUnits, model)
elif not output_param.UnitsMatch:
obj.OutputParameterDict[key] = output_param.with_preferred_units()


# write results to output file and screen

Expand All @@ -110,7 +115,7 @@ def PrintOutputs(self, model: Model):
f.write("Simulation Metadata\n")
f.write("----------------------\n")
f.write(f' GEOPHIRES Version: {geophires_x.__version__}\n')
f.write(" GEOPHIRES Build Date: 2022-06-30\n")
f.write(" GEOPHIRES Build Date: 2024-03-05\n") # FIXME TODO https://github.com/NREL/GEOPHIRES-X/issues/139
f.write(" Simulation Date: "+ datetime.datetime.now().strftime("%Y-%m-%d\n"))
f.write(" Simulation Time: "+ datetime.datetime.now().strftime("%H:%M\n"))
f.write(" Calculation Time: "+"{0:10.3f}".format((time.time()-model.tic)) + " sec\n")
Expand Down Expand Up @@ -185,7 +190,7 @@ def PrintOutputs(self, model: Model):
payback_period_val = model.economics.ProjectPaybackPeriod.value
project_payback_period_display = f'{payback_period_val:10.2f} {model.economics.ProjectPaybackPeriod.PreferredUnits.value}' \
if payback_period_val > 0.0 else 'N/A'
f.write(f' Project Payback Period: {project_payback_period_display}\n')
f.write(f' Project Payback Period: {project_payback_period_display}\n')

if model.surfaceplant.enduse_option.value in [EndUseOptions.COGENERATION_TOPPING_EXTRA_HEAT,
EndUseOptions.COGENERATION_BOTTOMING_EXTRA_HEAT,
Expand Down Expand Up @@ -621,48 +626,58 @@ def PrintOutputs(self, model: Model):
"Year Electricity | Heat | Cooling | Carbon | Project" + NL)
f.write(
"Since Price Ann. Rev. Cumm. Rev. | Price Ann. Rev. Cumm. Rev. | Price Ann. Rev. Cumm. Rev. | Price Ann. Rev. Cumm. Rev. | OPEX Net Rev. Net Cashflow" + NL)
econ = model.economics
econ:Economics = model.economics

def o(output_param: OutputParameter):
# TODO generalize this and/or FIXME make it unnecessary
if output_param.Name in econ.OutputParameterDict:
return econ.OutputParameterDict[output_param.Name]
else:
return o

f.write("Start ("
+ econ.ElecPrice.PreferredUnits.value +
")(" + econ.ElecRevenue.PreferredUnits.value +
") (" + econ.ElecCummRevenue.PreferredUnits.value +
") |(" + econ.HeatPrice.PreferredUnits.value +
") (" + econ.HeatRevenue.PreferredUnits.value +
") (" + econ.HeatCummRevenue.PreferredUnits.value +
") |(" + econ.CoolingPrice.PreferredUnits.value +
") (" + econ.CoolingRevenue.PreferredUnits.value +
") (" + econ.CoolingCummRevenue.PreferredUnits.value +
") |(" + econ.CarbonPrice.PreferredUnits.value +
") (" + econ.CarbonRevenue.PreferredUnits.value +
") (" + econ.CarbonCummCashFlow.PreferredUnits.value +
") |(" + econ.Coam.PreferredUnits.value +
") (" + econ.TotalRevenue.PreferredUnits.value +
") (" + econ.TotalCummRevenue.PreferredUnits.value + ")\n")
+ o(econ.ElecPrice).CurrentUnits.value +
")(" + o(econ.ElecRevenue).CurrentUnits.value +
") (" + o(econ.ElecCummRevenue).CurrentUnits.value +
") |(" + o(econ.HeatPrice).CurrentUnits.value +
") (" + o(econ.HeatRevenue).CurrentUnits.value +
") (" + o(econ.HeatCummRevenue).CurrentUnits.value +
") |(" + o(econ.CoolingPrice).CurrentUnits.value +
") (" + o(econ.CoolingRevenue).CurrentUnits.value +
") (" + o(econ.CoolingCummRevenue).CurrentUnits.value +
") |(" + o(econ.CarbonPrice).CurrentUnits.value +
") (" + o(econ.CarbonRevenue).CurrentUnits.value +
") (" + o(econ.CarbonCummCashFlow).CurrentUnits.value +
") |(" + o(econ.Coam).CurrentUnits.value +
") (" + o(econ.TotalRevenue).CurrentUnits.value +
") (" + o(econ.TotalCummRevenue).CurrentUnits.value + ")\n")
f.write(
"________________________________________________________________________________________________________________________________________________________________________________________" + NL)
# running years...
for ii in range(0, (
model.surfaceplant.construction_years.value + model.surfaceplant.plant_lifetime.value - 1), 1):

if ii < model.surfaceplant.construction_years.value:
OPEX = 0.0 # zero out the OPEX during construction years
else:
OPEX = econ.Coam.value
OPEX = o(econ.Coam).value
f.write(
f"{ii + 1:3.0f} {econ.ElecPrice.value[ii]:5.2f} {econ.ElecRevenue.value[ii]:5.2f} {econ.ElecCummRevenue.value[ii]:5.2f} | {econ.HeatPrice.value[ii]:5.2f} {econ.HeatRevenue.value[ii]:5.2f} {econ.HeatCummRevenue.value[ii]:5.2f} | {econ.CoolingPrice.value[ii]:5.2f} {econ.CoolingRevenue.value[ii]:5.2f} {econ.CoolingCummRevenue.value[ii]:5.2f} | {econ.CarbonPrice.value[ii]:5.2f} {econ.CarbonRevenue.value[ii]:5.2f} {econ.CarbonCummCashFlow.value[ii]:5.2f} | {OPEX:5.2f} {econ.TotalRevenue.value[ii]:5.2f} {econ.TotalCummRevenue.value[ii]:5.2f}\n")
f"{ii + 1: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)

if model.economics.DoAddOnCalculations.value: model.addoutputs.PrintOutputs(model)
if model.economics.DoSDACGTCalculations.value: model.sdacgtoutputs.PrintOutputs(model)

except BaseException as ex:
tb = sys.exc_info()[2]
print (str(ex))
print("Error: GEOPHIRES Failed to write the output file. Exiting....Line %i" % tb.tb_lineno)
msg = "Error: GEOPHIRES Failed to write the output file. Exiting....Line %i" % tb.tb_lineno
print(str(ex))
print(msg)
model.logger.critical(str(ex))
model.logger.critical("Error: GEOPHIRES Failed to write the output file. Exiting....Line %i" % tb.tb_lineno)
sys.exit()
model.logger.critical(msg)
raise RuntimeError(msg) from ex

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

def MakeDistrictHeatingPlot(self, model: Model):
"""
Expand Down
39 changes: 23 additions & 16 deletions src/geophires_x/Parameter.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
# copyright, 2023, Malcolm I Ross
import copy
import os.path
import dataclasses

import sys
from array import array
from typing import List, Optional, Any
from dataclasses import dataclass, field
from enum import IntEnum
from forex_python.converter import CurrencyRates, CurrencyCodes
import pint

from abc import ABC

from pint.facets.plain import PlainQuantity

from geophires_x.Units import *

ureg = pint.get_application_registry()
ureg.load_definitions(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'GEOPHIRES3_newunits.txt'))

_ureg = get_unit_registry()

class HasQuantity(ABC):

Expand All @@ -26,7 +23,7 @@ def quantity(self) -> PlainQuantity:
:rtype: pint.registry.Quantity - note type annotation uses PlainQuantity due to issues with python 3.8 failing
to import the Quantity TypeAlias
"""
return ureg.Quantity(self.value, str(self.CurrentUnits.value))
return _ureg.Quantity(self.value, str(self.CurrentUnits.value))


@dataclass
Expand Down Expand Up @@ -67,7 +64,17 @@ class OutputParameter(HasQuantity):
# set to PreferredUnits by default assuming that the current units are the preferred units -
# they will only change if the read function reads a different unit associated with a parameter
CurrentUnits: Enum = PreferredUnits
UnitsMatch: bool = True

@property
def UnitsMatch(self) -> str:
return self.CurrentUnits == self.PreferredUnits

def with_preferred_units(self) -> Any: # Any is a proxy for Self
ret: OutputParameter = dataclasses.replace(self)
ret.value = ret.quantity().to(ret.PreferredUnits).magnitude
ret.CurrentUnits = ret.PreferredUnits
return ret



@dataclass
Expand Down Expand Up @@ -511,8 +518,8 @@ def ConvertUnits(ParamToModify, strUnit: str, model) -> str:
try:
# Make a Pint Quantity out of the old value: the amount of the unit doesn't matter,
# just the units, so I set the amount to 0
Old_valQ = ureg.Quantity(0.000, str(ParamToModify.CurrentUnits.value))
New_valQ = ureg.Quantity(float(val), currType) # Make a Pint Quantity out of the new value
Old_valQ = _ureg.Quantity(0.000, str(ParamToModify.CurrentUnits.value))
New_valQ = _ureg.Quantity(float(val), currType) # Make a Pint Quantity out of the new value
except BaseException as ex:
print(str(ex))
msg = (
Expand Down Expand Up @@ -696,7 +703,7 @@ def parameter_with_units_converted_back_to_preferred_units(param: Parameter, mod
if isinstance(param.CurrentUnits, pint.Quantity):
currQ = param.CurrentUnits
else:
currQ = ureg.Quantity(float(val), currType) # Make a Pint Quantity out of the new value
currQ = _ureg.Quantity(float(val), currType) # Make a Pint Quantity out of the new value
except BaseException as ex:
print(str(ex))
msg = (
Expand Down Expand Up @@ -856,10 +863,10 @@ def ConvertOutputUnits(oparam: OutputParameter, newUnit: Units, model):
# this is a simple unit conversion: it could be just units (meters->feet) or simple currency ($->EUR)
# or compound Currency (MUSD-EUR)
try:
fromQ = ureg.Quantity(
fromQ = _ureg.Quantity(
oparam.value, str(oparam.PreferredUnits.value)
) # Make a Pint Quantity out of the value
toQ = ureg.Quantity(0, str(newUnit.value)) # Make a Pint Quantity out of the new value
toQ = _ureg.Quantity(0, str(newUnit.value)) # Make a Pint Quantity out of the new value
except BaseException as ex:
print(str(ex))
msg = (
Expand Down Expand Up @@ -900,10 +907,10 @@ def ConvertOutputUnits(oparam: OutputParameter, newUnit: Units, model):
i = 0
for arrayval in oparam.value:
try:
fromQ = ureg.Quantity(
fromQ = _ureg.Quantity(
oparam.value[i], str(oparam.PreferredUnits.value)
) # Make a Pint Quantity out of from the value
toQ = ureg.Quantity(0, str(newUnit.value)) # Make a Pint Quantity out of the new value
toQ = _ureg.Quantity(0, str(newUnit.value)) # Make a Pint Quantity out of the new value
except BaseException as ex:
print(str(ex))
msg = (
Expand Down Expand Up @@ -997,7 +1004,7 @@ def ConvertOutputUnits(oparam: OutputParameter, newUnit: Units, model):
# so just do the simple factor conversion and exit
oparam.value = oparam.value * Factor
oparam.CurrentUnits = DefUnit
oparam.UnitsMatch = False
# oparam.UnitsMatch = False
return

# start the currency conversion process
Expand Down
12 changes: 12 additions & 0 deletions src/geophires_x/Units.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
# copyright, 2023, Malcolm I Ross
from enum import IntEnum, Enum, auto

import pint
import os


_UREG = None
def get_unit_registry():
global _UREG
if _UREG is None:
_UREG = pint.get_application_registry()
_UREG.load_definitions(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'GEOPHIRES3_newunits.txt'))

return _UREG

class Units(IntEnum):
"""All possible systems of measure"""
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.4.14'
__version__ = '3.4.16'
Loading

0 comments on commit 926dda3

Please sign in to comment.