@@ -173,12 +173,13 @@ def sf(_v: float, num_sig_figs: int = 5) -> float:
173
173
sam_economics .project_npv .value = sf (single_owner .Outputs .project_return_aftertax_npv * 1e-6 )
174
174
sam_economics .capex .value = single_owner .Outputs .adjusted_installed_cost * 1e-6
175
175
176
- royalty_rate = model .economics .royalty_rate .quantity ().to ('dimensionless' ).magnitude
177
- ppa_revenue_row = _cash_flow_profile_row (cash_flow , 'PPA revenue ($)' )
178
- royalties_unit = sam_economics .royalties_opex .CurrentUnits .value .replace ('/yr' , '' )
179
- sam_economics .royalties_opex .value = [
180
- quantity (x * royalty_rate , 'USD' ).to (royalties_unit ).magnitude for x in ppa_revenue_row
181
- ]
176
+ if model .economics .royalty_rate .Provided :
177
+ # Assumes that royalties opex is the only possible O&M production-based expense - this logic will need to be
178
+ # updated if more O&M production-based expenses are added to SAM-EM
179
+ sam_economics .royalties_opex .value = [
180
+ quantity (it , 'USD / year' ).to (sam_economics .royalties_opex .CurrentUnits ).magnitude
181
+ for it in _cash_flow_profile_row (cash_flow , 'O&M production-based expense ($)' )
182
+ ]
182
183
183
184
sam_economics .nominal_discount_rate .value , sam_economics .wacc .value = _calculate_nominal_discount_rate_and_wacc (
184
185
model , single_owner
@@ -408,28 +409,11 @@ def _get_single_owner_parameters(model: Model) -> dict[str, Any]:
408
409
geophires_ptr_tenths = Decimal (econ .PTR .value )
409
410
ret ['property_tax_rate' ] = float (geophires_ptr_tenths * Decimal (100 ))
410
411
411
- ppa_price_schedule_per_kWh = _ppa_pricing_model (
412
- model .surfaceplant .plant_lifetime .value ,
413
- econ .ElecStartPrice .value ,
414
- econ .ElecEndPrice .value ,
415
- econ .ElecEscalationStart .value ,
416
- econ .ElecEscalationRate .value ,
417
- )
412
+ ppa_price_schedule_per_kWh = _get_ppa_price_schedule_per_kWh (model )
418
413
ret ['ppa_price_input' ] = ppa_price_schedule_per_kWh
419
414
420
- royalty_rate_schedule = _get_royalty_rate_schedule (model )
421
415
if model .economics .royalty_rate .Provided :
422
- # For each year, calculate the royalty as a $/MWh variable cost.
423
- # The royalty is a percentage of revenue (MWh * $/MWh). By setting the
424
- # variable O&M rate to (PPA Price * Royalty Rate), SAM's calculation
425
- # (Rate * MWh) will correctly yield the total royalty payment.
426
- variable_om_schedule_per_MWh = [
427
- (price_kwh * 1000 ) * royalty_fraction # TODO use pint unit conversion instead
428
- for price_kwh , royalty_fraction in zip (ppa_price_schedule_per_kWh , royalty_rate_schedule )
429
- ]
430
-
431
- # The PySAM parameter for variable operating cost in $/MWh is 'om_production'.
432
- ret ['om_production' ] = variable_om_schedule_per_MWh
416
+ ret ['om_production' ] = _get_royalties_variable_om_per_MWh_schedule (model )
433
417
434
418
# Debt/equity ratio ('Fraction of Investment in Bonds' parameter)
435
419
ret ['debt_percent' ] = _pct (econ .FIB )
@@ -452,6 +436,24 @@ def _get_single_owner_parameters(model: Model) -> dict[str, Any]:
452
436
return ret
453
437
454
438
439
+ def _get_royalties_variable_om_per_MWh_schedule (model : Model ):
440
+ """TODO price unit in method name"""
441
+
442
+ royalty_rate_schedule = _get_royalty_rate_schedule (model )
443
+ ppa_price_schedule_per_kWh = _get_ppa_price_schedule_per_kWh (model )
444
+
445
+ # For each year, calculate the royalty as a $/MWh variable cost.
446
+ # The royalty is a percentage of revenue (MWh * $/MWh). By setting the
447
+ # variable O&M rate to (PPA Price * Royalty Rate), SAM's calculation
448
+ # (Rate * MWh) will correctly yield the total royalty payment.
449
+ variable_om_schedule_per_MWh = [
450
+ (price_kWh * 1000 ) * royalty_fraction # TODO use pint unit conversion instead
451
+ for price_kWh , royalty_fraction in zip (ppa_price_schedule_per_kWh , royalty_rate_schedule )
452
+ ]
453
+
454
+ return variable_om_schedule_per_MWh
455
+
456
+
455
457
def _get_fed_and_state_tax_rates (geophires_ctr_tenths : float ) -> tuple [list [float ]]:
456
458
geophires_ctr_tenths = Decimal (geophires_ctr_tenths )
457
459
max_fed_rate_tenths = Decimal (0.21 )
@@ -469,9 +471,22 @@ def _pct(econ_value: Parameter) -> float:
469
471
return econ_value .quantity ().to (convertible_unit ('%' )).magnitude
470
472
471
473
474
+ def _get_ppa_price_schedule_per_kWh (model : Model ) -> list [float ]:
475
+ """TODO price unit in method name"""
476
+
477
+ econ = model .economics
478
+ return _ppa_pricing_model (
479
+ model .surfaceplant .plant_lifetime .value ,
480
+ econ .ElecStartPrice .value ,
481
+ econ .ElecEndPrice .value ,
482
+ econ .ElecEscalationStart .value ,
483
+ econ .ElecEscalationRate .value ,
484
+ )
485
+
486
+
472
487
def _ppa_pricing_model (
473
488
plant_lifetime : int , start_price : float , end_price : float , escalation_start_year : int , escalation_rate : float
474
- ) -> list :
489
+ ) -> list [ float ] :
475
490
# See relevant comment in geophires_x.EconomicsUtils.BuildPricingModel re:
476
491
# https://github.com/NREL/GEOPHIRES-X/issues/340?title=Price+Escalation+Start+Year+seemingly+off+by+1.
477
492
# We use the same utility method here for the sake of consistency despite technical incorrectness.
0 commit comments