@@ -173,12 +173,13 @@ def sf(_v: float, num_sig_figs: int = 5) -> float:
173173 sam_economics .project_npv .value = sf (single_owner .Outputs .project_return_aftertax_npv * 1e-6 )
174174 sam_economics .capex .value = single_owner .Outputs .adjusted_installed_cost * 1e-6
175175
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+ ]
182183
183184 sam_economics .nominal_discount_rate .value , sam_economics .wacc .value = _calculate_nominal_discount_rate_and_wacc (
184185 model , single_owner
@@ -408,28 +409,11 @@ def _get_single_owner_parameters(model: Model) -> dict[str, Any]:
408409 geophires_ptr_tenths = Decimal (econ .PTR .value )
409410 ret ['property_tax_rate' ] = float (geophires_ptr_tenths * Decimal (100 ))
410411
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 )
418413 ret ['ppa_price_input' ] = ppa_price_schedule_per_kWh
419414
420- royalty_rate_schedule = _get_royalty_rate_schedule (model )
421415 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 )
433417
434418 # Debt/equity ratio ('Fraction of Investment in Bonds' parameter)
435419 ret ['debt_percent' ] = _pct (econ .FIB )
@@ -452,6 +436,24 @@ def _get_single_owner_parameters(model: Model) -> dict[str, Any]:
452436 return ret
453437
454438
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+
455457def _get_fed_and_state_tax_rates (geophires_ctr_tenths : float ) -> tuple [list [float ]]:
456458 geophires_ctr_tenths = Decimal (geophires_ctr_tenths )
457459 max_fed_rate_tenths = Decimal (0.21 )
@@ -469,9 +471,22 @@ def _pct(econ_value: Parameter) -> float:
469471 return econ_value .quantity ().to (convertible_unit ('%' )).magnitude
470472
471473
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+
472487def _ppa_pricing_model (
473488 plant_lifetime : int , start_price : float , end_price : float , escalation_start_year : int , escalation_rate : float
474- ) -> list :
489+ ) -> list [ float ] :
475490 # See relevant comment in geophires_x.EconomicsUtils.BuildPricingModel re:
476491 # https://github.com/NREL/GEOPHIRES-X/issues/340?title=Price+Escalation+Start+Year+seemingly+off+by+1.
477492 # We use the same utility method here for the sake of consistency despite technical incorrectness.
0 commit comments