forked from NatLabRockies/GEOPHIRES-X
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathEconomicsUtils.py
More file actions
255 lines (213 loc) · 10.3 KB
/
Copy pathEconomicsUtils.py
File metadata and controls
255 lines (213 loc) · 10.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
from __future__ import annotations
from geophires_x.Parameter import OutputParameter
from geophires_x.Units import Units, PercentUnit, TimeUnit, CurrencyUnit, CurrencyFrequencyUnit, EnergyCostUnit
CONSTRUCTION_CAPEX_SCHEDULE_PARAMETER_NAME = 'Construction CAPEX Schedule'
_YEAR_INDEX_VALUE_EXPLANATION_SNIPPET = (
f'The value is specified as a project year index corresponding to the ' f'Year row in the cash flow profile'
)
def BuildPricingModel(
plantlifetime: int,
StartPrice: float,
EndPrice: float,
EscalationStartYear: int,
EscalationRate: float,
PTCAddition: list,
) -> list:
"""
BuildPricingModel builds the price model array for the project lifetime. It is used to calculate the revenue
stream for the project.
:param plantlifetime: The lifetime of the project in years
:type plantlifetime: int
:param StartPrice: The price in the first year of the project in $/kWh
:type StartPrice: float
:param EndPrice: The price in the last year of the project in $/kWh
:type EndPrice: float
:param EscalationStartYear: The year the price escalation starts in years (not including construction years) in years
:type EscalationStartYear: int
:param EscalationRate: The rate of price escalation in $/kWh/year
:type EscalationRate: float
:param PTCAddition: The PTC addition array for the project in $/kWh
:type PTCAddition: list
:return: Price: The price model array for the project in $/kWh
:rtype: list
"""
Price = [0.0] * plantlifetime
for i in range(0, plantlifetime, 1):
Price[i] = StartPrice
if i >= EscalationStartYear:
# TODO: This is arguably an unwanted/incorrect interpretation of escalation start year, see
# https://github.com/NREL/GEOPHIRES-X/issues/340?title=Price+Escalation+Start+Year+seemingly+off+by+1
Price[i] = Price[i] + ((i - EscalationStartYear) * EscalationRate)
if Price[i] > EndPrice:
Price[i] = EndPrice
Price[i] = Price[i] + PTCAddition[i]
return Price
def lcoh_output_parameter() -> OutputParameter:
return OutputParameter(
Name="LCOH",
display_name='Direct-Use heat breakeven price (LCOH)',
UnitType=Units.ENERGYCOST,
PreferredUnits=EnergyCostUnit.DOLLARSPERMMBTU, # $/MMBTU
CurrentUnits=EnergyCostUnit.DOLLARSPERMMBTU,
)
def lcoc_output_parameter() -> OutputParameter:
return OutputParameter(
Name="LCOC",
display_name='Direct-Use Cooling Breakeven Price (LCOC)',
UnitType=Units.ENERGYCOST,
PreferredUnits=EnergyCostUnit.DOLLARSPERMMBTU,
CurrentUnits=EnergyCostUnit.DOLLARSPERMMBTU,
)
_SAM_EM_MOIC_RETURNS_TAX_QUALIFIER = 'after-tax'
def moic_parameter() -> OutputParameter:
return OutputParameter(
"Project MOIC",
ToolTipText='Project Multiple of Invested Capital. For SAM Economic Models, this is calculated as the '
f'sum of Total {_SAM_EM_MOIC_RETURNS_TAX_QUALIFIER} returns (total value received) '
'divided by Issuance of equity (total capital invested).',
UnitType=Units.PERCENT,
PreferredUnits=PercentUnit.TENTH,
CurrentUnits=PercentUnit.TENTH,
)
def project_vir_parameter() -> OutputParameter:
return OutputParameter(
"Project Value Investment Ratio",
display_name='Project VIR=PI=PIR',
UnitType=Units.PERCENT,
PreferredUnits=PercentUnit.TENTH,
CurrentUnits=PercentUnit.TENTH,
ToolTipText="Value Investment Ratio (VIR). "
"VIR is frequently referred to interchangeably as Profitability Index (PI) or "
"Profit Investment Ratio (PIR) in financial literature. "
"All three terms describe the same fundamental ratio: the present value of future cash flows "
"divided by the initial investment. "
"For SAM Economic Models, this metric is calculated as the Levered Equity Profitability Index. "
"It is calculated as the Present Value of After-Tax Equity Cash Flows (Returns) divided by the "
"Present Value of Equity Invested. It measures the efficiency of the sponsor's specific capital "
"contribution, accounting for leverage.",
)
def project_payback_period_parameter() -> OutputParameter:
return OutputParameter(
"Project Payback Period",
UnitType=Units.TIME,
PreferredUnits=TimeUnit.YEAR,
CurrentUnits=TimeUnit.YEAR,
ToolTipText='The time at which cumulative cash flow reaches zero. '
'For projects that never pay back, the calculated value will be "N/A". '
'For SAM Economic Models, this is Simple Payback Period (SPB): the time at which cumulative non-discounted '
'cash flow reaches zero, calculated using non-discounted after-tax net cash flow. '
'See https://samrepo.nrelcloud.org/help/mtf_payback.html for important considerations regarding the '
'limitations of this metric.',
)
def after_tax_irr_parameter() -> OutputParameter:
return OutputParameter(
Name='After-tax IRR',
UnitType=Units.PERCENT,
CurrentUnits=PercentUnit.PERCENT,
PreferredUnits=PercentUnit.PERCENT,
ToolTipText='The After-tax IRR (internal rate of return) is the nominal discount rate that corresponds to '
'a net present value (NPV) of zero for PPA SAM Economic models. '
# TODO describe backfilled calculation using After-tax net cash flow
'See https://samrepo.nrelcloud.org/help/mtf_irr.html.',
)
def real_discount_rate_parameter() -> OutputParameter:
return OutputParameter(
Name="Real Discount Rate",
UnitType=Units.PERCENT,
CurrentUnits=PercentUnit.PERCENT,
PreferredUnits=PercentUnit.PERCENT,
)
def nominal_discount_rate_parameter() -> OutputParameter:
return OutputParameter(
Name="Nominal Discount Rate",
ToolTipText="Nominal Discount Rate is displayed for SAM Economic Models. "
"It is calculated "
"per https://samrepo.nrelcloud.org/help/fin_single_owner.html?q=nominal+discount+rate: "
"Nominal Discount Rate = [ ( 1 + Real Discount Rate ÷ 100 ) "
"× ( 1 + Inflation Rate ÷ 100 ) - 1 ] × 100.",
UnitType=Units.PERCENT,
CurrentUnits=PercentUnit.PERCENT,
PreferredUnits=PercentUnit.PERCENT,
)
def wacc_output_parameter() -> OutputParameter:
return OutputParameter(
Name='WACC',
ToolTipText='Weighted Average Cost of Capital displayed for SAM Economic Models. '
'It is calculated per https://samrepo.nrelcloud.org/help/fin_commercial.html?q=wacc: '
'WACC = [ Nominal Discount Rate ÷ 100 × (1 - Debt Percent ÷ 100) '
'+ Debt Percent ÷ 100 × Loan Rate ÷ 100 × (1 - Effective Tax Rate ÷ 100 ) ] × 100; '
'Effective Tax Rate = [ Federal Tax Rate ÷ 100 × ( 1 - State Tax Rate ÷ 100 ) '
'+ State Tax Rate ÷ 100 ] × 100; ',
UnitType=Units.PERCENT,
CurrentUnits=PercentUnit.PERCENT,
PreferredUnits=PercentUnit.PERCENT,
)
def overnight_capital_cost_output_parameter() -> OutputParameter:
return OutputParameter(
Name='Overnight Capital Cost',
UnitType=Units.CURRENCY,
PreferredUnits=CurrencyUnit.MDOLLARS,
CurrentUnits=CurrencyUnit.MDOLLARS,
ToolTipText='Overnight Capital Cost (OCC) represents the total capital cost required '
'to construct the plant if it were built instantly ("overnight"). '
'This value excludes time-dependent costs such as inflation, '
'royalty supplemental payments, and '
'interest incurred during the construction period.',
)
def inflation_cost_during_construction_output_parameter() -> OutputParameter:
return OutputParameter(
Name='Inflation costs during construction',
UnitType=Units.CURRENCY,
PreferredUnits=CurrencyUnit.MDOLLARS,
CurrentUnits=CurrencyUnit.MDOLLARS,
ToolTipText='The calculated amount of cost escalation due to inflation over the construction period.',
)
def interest_during_construction_output_parameter() -> OutputParameter:
return OutputParameter(
Name='Interest during construction',
UnitType=Units.CURRENCY,
PreferredUnits=CurrencyUnit.MDOLLARS,
CurrentUnits=CurrencyUnit.MDOLLARS,
ToolTipText='Interest During Construction (IDC) is the total accumulated interest '
'incurred on debt during the construction phase. This cost is capitalized '
'(added to the loan principal and total installed cost) rather than paid in cash.',
)
def total_capex_parameter_output_parameter() -> OutputParameter:
return OutputParameter(
Name='Total CAPEX',
UnitType=Units.CURRENCY,
CurrentUnits=CurrencyUnit.MDOLLARS,
PreferredUnits=CurrencyUnit.MDOLLARS,
ToolTipText='The total capital expenditure (CAPEX) required to construct the plant. '
'This value includes all direct and indirect costs, and contingency. '
'For SAM Economic models, it also includes any cost escalation from inflation during construction. '
'It is used as the total installed cost input for SAM Economic Models.',
)
def royalty_cost_output_parameter() -> OutputParameter:
"""
Note: this is an internal-only intermediate output parameter, so it has no user-facing tooltip text.
See usage in EconomicsSam.py.
"""
return OutputParameter(
Name='Royalty Cost',
UnitType=Units.CURRENCYFREQUENCY,
PreferredUnits=CurrencyFrequencyUnit.DOLLARSPERYEAR,
CurrentUnits=CurrencyFrequencyUnit.DOLLARSPERYEAR,
)
def investment_tax_credit_output_parameter() -> OutputParameter:
return OutputParameter(
Name="Investment Tax Credit Value",
display_name='Investment Tax Credit',
UnitType=Units.CURRENCY,
PreferredUnits=CurrencyUnit.MDOLLARS,
CurrentUnits=CurrencyUnit.MDOLLARS,
ToolTipText='Represents the total undiscounted ITC sum. '
'For SAM Economic Models, this accounts for the standard Year 1 Federal ITC as well as any '
'applicable State ITCs or multi-year credit schedules.',
)
def expand_schedule_dsl(schedule_strings: list[str | float], total_years: int) -> list[float]:
"""
Deprecated, call ParameterUtils.expand_schedule_dsl
"""
from geophires_x.ParameterUtils import expand_schedule_dsl
return expand_schedule_dsl(schedule_strings, total_years)