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

Fix Revenue & Cashflow Profile Units, Update Utilization Efficiency (v3.4.16) #146

Merged
merged 15 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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.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
Loading