Skip to content

Commit 1b7dff1

Browse files
use class for Economics.sam_economics
1 parent e16ef31 commit 1b7dff1

File tree

3 files changed

+61
-40
lines changed

3 files changed

+61
-40
lines changed

src/geophires_x/Economics.py

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import numpy_financial as npf
55
import geophires_x.Model as Model
66
from geophires_x import EconomicsSam
7-
from geophires_x.EconomicsSam import calculate_sam_economics
7+
from geophires_x.EconomicsSam import calculate_sam_economics, SamEconomics
88
from geophires_x.EconomicsUtils import BuildPricingModel
99
from geophires_x.OptionList import Configuration, WellDrillingCostCorrelation, EconomicModel, EndUseOptions, PlantType, \
1010
_WellDrillingCostCorrelationCitation
@@ -442,7 +442,7 @@ def CalculateLCOELCOHLCOC(econ, model: Model) -> tuple:
442442
LCOH = LCOH * 2.931 # $/Million Btu
443443
elif econ.econmodel.value == EconomicModel.SAM_SINGLE_OWNER_PPA:
444444
# Designated as nominal (as opposed to real) in parameter tooltip text
445-
LCOE = econ.sam_economics.value['LCOE (nominal)']['value']
445+
LCOE = econ.sam_economics.lcoe_nominal.quantity().to(convertible_unit(econ.LCOE.CurrentUnits.value)).magnitude
446446
else:
447447
# must be BICYCLE
448448
# average return on investment (tax and inflation adjusted)
@@ -1518,6 +1518,9 @@ def __init__(self, model: Model):
15181518
self.annualheatincome = 0.0
15191519
self.InputFile = ""
15201520
self.Cplantcorrelation = 0.0
1521+
1522+
self.sam_economics: SamEconomics = None
1523+
15211524
sclass = str(__class__).replace("<class \'", "")
15221525
self.MyClass = sclass.replace("\'>", "")
15231526
self.MyPath = os.path.abspath(__file__)
@@ -1829,14 +1832,6 @@ def __init__(self, model: Model):
18291832
PreferredUnits=TimeUnit.YEAR,
18301833
CurrentUnits=TimeUnit.YEAR
18311834
)
1832-
1833-
# FIXME TODO/WIP representation in schema...
1834-
self.sam_economics = self.OutputParameterDict[self.sam_economics.Name] = OutputParameter(
1835-
'SAM Economics',
1836-
UnitType=Units.NONE,
1837-
json_parameter_type='object',
1838-
)
1839-
18401835
self.RITCValue = self.OutputParameterDict[self.RITCValue.Name] = OutputParameter(
18411836
Name="Investment Tax Credit Value",
18421837
display_name='Investment Tax Credit',
@@ -2760,9 +2755,9 @@ def Calculate(self, model: Model) -> None:
27602755
)
27612756

27622757
if self.econmodel.value == EconomicModel.SAM_SINGLE_OWNER_PPA:
2763-
self.sam_economics.value = calculate_sam_economics(model)
2764-
self.ProjectNPV.value = self.sam_economics.value['NPV']['value']
2765-
self.ProjectIRR.value = self.sam_economics.value['IRR']['value']
2758+
self.sam_economics = calculate_sam_economics(model)
2759+
self.ProjectNPV.value = self.sam_economics.project_npv.quantity().to(convertible_unit(self.ProjectNPV.CurrentUnits)).magnitude
2760+
self.ProjectIRR.value = self.sam_economics.project_irr.quantity().to(convertible_unit(self.ProjectIRR.CurrentUnits)).magnitude
27662761
# FIXME WIP VIR + MOIC
27672762
self.ProjectVIR.value, self.ProjectMOIC.value = -1, -1
27682763

src/geophires_x/EconomicsSam.py

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import json
44
import os
5+
from dataclasses import dataclass, field
56
from functools import lru_cache
67
from math import isnan
78
from pathlib import Path
@@ -22,17 +23,46 @@
2223

2324
# noinspection PyPackageRequirements
2425
import PySAM.Utilityrate5 as UtilityRate
26+
from pint.facets.plain import PlainQuantity
2527
from tabulate import tabulate
2628

2729
from geophires_x import Model as Model
2830
from geophires_x.EconomicsSamCashFlow import _calculate_sam_economics_cash_flow
2931
from geophires_x.EconomicsUtils import BuildPricingModel
3032
from geophires_x.GeoPHIRESUtils import is_float, is_int
3133
from geophires_x.OptionList import EconomicModel, EndUseOptions
32-
from geophires_x.Parameter import Parameter
33-
from geophires_x.Units import convertible_unit
34+
from geophires_x.Parameter import Parameter, HasQuantity, OutputParameter
35+
from geophires_x.Units import convertible_unit, EnergyCostUnit, CurrencyUnit, Units, PercentUnit
3436

35-
_SAM_CASH_FLOW_PROFILE_KEY = 'Cash Flow'
37+
38+
@dataclass
39+
class SamEconomics:
40+
sam_cash_flow_profile: list[list[Any]]
41+
42+
lcoe_nominal: OutputParameter = field(
43+
default_factory=lambda: OutputParameter(
44+
UnitType=Units.ENERGYCOST,
45+
CurrentUnits=EnergyCostUnit.CENTSSPERKWH,
46+
)
47+
)
48+
capex: OutputParameter = field(
49+
default_factory=lambda: OutputParameter(
50+
UnitType=Units.CURRENCY,
51+
CurrentUnits=CurrencyUnit.MDOLLARS,
52+
)
53+
)
54+
project_npv: OutputParameter = field(
55+
default_factory=lambda: OutputParameter(
56+
UnitType=Units.CURRENCY,
57+
CurrentUnits=CurrencyUnit.MDOLLARS,
58+
)
59+
)
60+
project_irr: OutputParameter = field(
61+
default_factory=lambda: OutputParameter(
62+
UnitType=Units.PERCENT,
63+
CurrentUnits=PercentUnit.PERCENT,
64+
)
65+
)
3666

3767

3868
def validate_read_parameters(model: Model):
@@ -63,8 +93,14 @@ def _inv_msg(param_name: str, invalid_value: Any, supported_description: str) ->
6393
)
6494

6595

96+
class _Quantity(HasQuantity):
97+
def __init__(self, value: Any, units: str) -> None:
98+
self.value = value
99+
self.units = units
100+
101+
66102
@lru_cache(maxsize=12)
67-
def calculate_sam_economics(model: Model) -> dict[str, dict[str, Any]]:
103+
def calculate_sam_economics(model: Model) -> SamEconomics:
68104
custom_gen = CustomGeneration.new()
69105
grid = Grid.from_existing(custom_gen)
70106
utility_rate = UtilityRate.from_existing(custom_gen)
@@ -97,25 +133,16 @@ def calculate_sam_economics(model: Model) -> dict[str, dict[str, Any]]:
97133

98134
cash_flow = _calculate_sam_economics_cash_flow(model, single_owner)
99135

100-
data = [
101-
('LCOE (nominal)', single_owner.Outputs.lcoe_nom, 'cents/kWh'),
102-
('IRR', single_owner.Outputs.project_return_aftertax_irr, '%'),
103-
('NPV', single_owner.Outputs.project_return_aftertax_npv * 1e-6, 'MUSD'),
104-
('CAPEX', single_owner.Outputs.adjusted_installed_cost * 1e-6, 'MUSD'),
105-
(_SAM_CASH_FLOW_PROFILE_KEY, cash_flow, None),
106-
]
107-
108-
ret = {}
109-
for e in data:
110-
key = e[0]
136+
def sf(_v: float) -> float:
137+
return _sig_figs(_v, 5)
111138

112-
as_val = e[1]
113-
if key != _SAM_CASH_FLOW_PROFILE_KEY:
114-
as_val = {'value': _sig_figs(e[1], 5), 'unit': e[2]}
139+
sam_economics: SamEconomics = SamEconomics(sam_cash_flow_profile=cash_flow)
140+
sam_economics.lcoe_nominal.value = sf(single_owner.Outputs.lcoe_nom)
141+
sam_economics.project_irr.value = sf(single_owner.Outputs.project_return_aftertax_irr)
142+
sam_economics.project_npv.value = sf(single_owner.Outputs.project_return_aftertax_npv * 1e-6)
143+
sam_economics.capex.value = single_owner.Outputs.adjusted_installed_cost * 1e-6
115144

116-
ret[key] = as_val
117-
118-
return ret
145+
return sam_economics
119146

120147

121148
def get_sam_cash_flow_profile_tabulated_output(model: Model, **tabulate_kw_args) -> str:
@@ -143,7 +170,7 @@ def get_entry_display(entry: Any) -> str:
143170
return entry_display
144171
return entry
145172

146-
profile_display = model.economics.sam_economics.value[_SAM_CASH_FLOW_PROFILE_KEY].copy()
173+
profile_display = model.economics.sam_economics.sam_cash_flow_profile.copy()
147174
for i in range(len(profile_display)):
148175
for j in range(len(profile_display[i])):
149176
profile_display[i][j] = get_entry_display(profile_display[i][j])

tests/geophires_x_tests/test_economics_sam.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
from geophires_x.EconomicsSam import (
1717
calculate_sam_economics,
1818
_sig_figs,
19-
_SAM_CASH_FLOW_PROFILE_KEY,
2019
get_sam_cash_flow_profile_tabulated_output,
2120
_ppa_pricing_model,
2221
)
@@ -137,7 +136,7 @@ def test_cash_flow(self):
137136
m: Model = EconomicsSamTestCase._new_model(Path(self._egs_test_file_path()))
138137

139138
sam_econ = calculate_sam_economics(m)
140-
cash_flow = sam_econ[_SAM_CASH_FLOW_PROFILE_KEY]
139+
cash_flow = sam_econ.sam_cash_flow_profile
141140
self.assertIsNotNone(cash_flow)
142141

143142
print(
@@ -234,7 +233,7 @@ def test_property_tax_rate(self):
234233
)
235234

236235
sam_econ = calculate_sam_economics(m)
237-
cash_flow = sam_econ[_SAM_CASH_FLOW_PROFILE_KEY]
236+
cash_flow = sam_econ.sam_cash_flow_profile
238237

239238
def get_row(name: str):
240239
return EconomicsSamTestCase._get_cash_flow_row(cash_flow, name)
@@ -249,7 +248,7 @@ def assert_incentives(params, expected_ibi_usd):
249248
m: Model = EconomicsSamTestCase._new_model(self._egs_test_file_path(), additional_params=params)
250249

251250
sam_econ = calculate_sam_economics(m)
252-
cash_flow = sam_econ[_SAM_CASH_FLOW_PROFILE_KEY]
251+
cash_flow = sam_econ.sam_cash_flow_profile
253252

254253
def get_row(name: str):
255254
return EconomicsSamTestCase._get_cash_flow_row(cash_flow, name)
@@ -283,7 +282,7 @@ def assert_ptc(params, expected_ptc_usd_per_kWh):
283282
m: Model = EconomicsSamTestCase._new_model(self._egs_test_file_path(), additional_params=params)
284283

285284
sam_econ = calculate_sam_economics(m)
286-
cash_flow = sam_econ[_SAM_CASH_FLOW_PROFILE_KEY]
285+
cash_flow = sam_econ.sam_cash_flow_profile
287286

288287
def get_row(name: str):
289288
return EconomicsSamTestCase._get_cash_flow_row(cash_flow, name)

0 commit comments

Comments
 (0)