@@ -968,7 +968,7 @@ def __init__(self, model: Model):
968968 )
969969
970970 self .royalty_rate = self .ParameterDict [self .royalty_rate .Name ] = floatParameter (
971- " Royalty Rate" ,
971+ ' Royalty Rate' ,
972972 DefaultValue = 0. ,
973973 Min = 0.0 ,
974974 Max = 1.0 ,
@@ -978,6 +978,18 @@ def __init__(self, model: Model):
978978 ToolTipText = "Royalty rate used in SAM Economic Models." # FIXME WIP TODO documentation
979979 )
980980
981+ self .royalty_holder_discount_rate = self .ParameterDict [self .royalty_holder_discount_rate .Name ] = floatParameter (
982+ 'Royalty Holder Discount Rate' ,
983+ DefaultValue = 0.05 ,
984+ Min = 0.0 ,
985+ Max = 1.0 ,
986+ UnitType = Units .PERCENT ,
987+ PreferredUnits = PercentUnit .TENTH ,
988+ CurrentUnits = PercentUnit .TENTH ,
989+ ToolTipText = "Royalty holder discount rate used in SAM Economic Models." # FIXME WIP TODO documentation
990+ )
991+
992+
981993 self .discount_initial_year_cashflow = self .ParameterDict [self .discount_initial_year_cashflow .Name ] = boolParameter (
982994 'Discount Initial Year Cashflow' ,
983995 DefaultValue = False ,
@@ -2134,6 +2146,33 @@ def __init__(self, model: Model):
21342146 UnitType = Units .NONE ,
21352147 )
21362148
2149+ # Results for the Royalty Holder
2150+ self .royalty_holder_npv = self .OutputParameterDict [self .royalty_holder_npv .Name ] = OutputParameter (
2151+ 'Royalty Holder NPV' ,
2152+ UnitType = Units .CURRENCY ,
2153+ PreferredUnits = CurrencyUnit .MDOLLARS ,
2154+ CurrentUnits = CurrencyUnit .MDOLLARS ,
2155+ ToolTipText = "Net Present Value (NPV) of the royalty holder's cash flow stream."
2156+ )
2157+ self .royalty_holder_annual_revenue = self .OutputParameterDict [
2158+ self .royalty_holder_annual_revenue .Name
2159+ ] = OutputParameter (
2160+ 'Royalty Holder Annual Revenue' ,
2161+ UnitType = Units .CURRENCYFREQUENCY ,
2162+ PreferredUnits = CurrencyFrequencyUnit .MDOLLARSPERYEAR ,
2163+ CurrentUnits = CurrencyFrequencyUnit .MDOLLARSPERYEAR ,
2164+ ToolTipText = "The royalty holder's annual revenue stream from the royalty agreement."
2165+ )
2166+ self .royalty_holder_total_revenue = self .OutputParameterDict [
2167+ self .royalty_holder_total_revenue .Name
2168+ ] = OutputParameter (
2169+ 'Royalty Holder Total Revenue' ,
2170+ UnitType = Units .CURRENCY ,
2171+ PreferredUnits = CurrencyUnit .MDOLLARS ,
2172+ CurrentUnits = CurrencyUnit .MDOLLARS ,
2173+ ToolTipText = 'The total (undiscounted) revenue received by the royalty holder over the project lifetime.'
2174+ )
2175+
21372176 model .logger .info (f'Complete { __class__ !s} : { sys ._getframe ().f_code .co_name } ' )
21382177
21392178 def read_parameters (self , model : Model ) -> None :
@@ -2504,38 +2543,8 @@ def Calculate(self, model: Model) -> None:
25042543 self .discount_initial_year_cashflow .value
25052544 )
25062545
2507- non_calculated_output_placeholder_val = - 1
25082546 if self .econmodel .value == EconomicModel .SAM_SINGLE_OWNER_PPA :
2509- self .sam_economics_calculations = calculate_sam_economics (model )
2510-
2511- # Setting capex_total distinguishes capex from CCap's display name of 'Total capital costs',
2512- # since SAM Economic Model doesn't subtract ITC from this value.
2513- self .capex_total .value = (self .sam_economics_calculations .capex .quantity ()
2514- .to (self .capex_total .CurrentUnits .value ).magnitude )
2515- self .CCap .value = (self .sam_economics_calculations .capex .quantity ()
2516- .to (self .CCap .CurrentUnits .value ).magnitude )
2517-
2518- average_annual_royalties = np .average (
2519- self .sam_economics_calculations .royalties_opex .value [1 :] # ignore pre-revenue year(s) (Year 0)
2520- )
2521- if average_annual_royalties > 0 :
2522- self .royalties_average_annual_cost .value = average_annual_royalties
2523- self .Coam .value += self .royalties_average_annual_cost .quantity ().to (self .Coam .CurrentUnits .value ).magnitude
2524-
2525- self .wacc .value = self .sam_economics_calculations .wacc .value
2526- self .nominal_discount_rate .value = self .sam_economics_calculations .nominal_discount_rate .value
2527- self .ProjectNPV .value = self .sam_economics_calculations .project_npv .quantity ().to (
2528- convertible_unit (self .ProjectNPV .CurrentUnits )).magnitude
2529-
2530- self .ProjectIRR .value = non_calculated_output_placeholder_val # SAM calculates After-Tax IRR instead
2531- self .after_tax_irr .value = self .sam_economics_calculations .after_tax_irr .quantity ().to (
2532- convertible_unit (self .ProjectIRR .CurrentUnits )).magnitude
2533-
2534- self .ProjectMOIC .value = self .sam_economics_calculations .moic .value
2535- self .ProjectVIR .value = self .sam_economics_calculations .project_vir .value
2536-
2537- # TODO remove or clarify project payback period: https://github.com/NREL/GEOPHIRES-X/issues/413
2538- self .ProjectPaybackPeriod .value = self .sam_economics_calculations .project_payback_period .value
2547+ self ._calculate_sam_economics (model )
25392548
25402549 # Calculate the project payback period
25412550 if self .econmodel .value != EconomicModel .SAM_SINGLE_OWNER_PPA :
@@ -3267,6 +3276,51 @@ def calculate_cashflow(self, model: Model) -> None:
32673276 for i in range (1 , model .surfaceplant .plant_lifetime .value + model .surfaceplant .construction_years .value , 1 ):
32683277 self .TotalCummRevenue .value [i ] = self .TotalCummRevenue .value [i - 1 ] + self .TotalRevenue .value [i ]
32693278
3279+ def _calculate_sam_economics (self , model : Model ) -> None :
3280+ non_calculated_output_placeholder_val = - 1
3281+ self .sam_economics_calculations = calculate_sam_economics (model )
3282+
3283+ # Setting capex_total distinguishes capex from CCap's display name of 'Total capital costs',
3284+ # since SAM Economic Model doesn't subtract ITC from this value.
3285+ self .capex_total .value = (self .sam_economics_calculations .capex .quantity ()
3286+ .to (self .capex_total .CurrentUnits .value ).magnitude )
3287+ self .CCap .value = (self .sam_economics_calculations .capex .quantity ()
3288+ .to (self .CCap .CurrentUnits .value ).magnitude )
3289+
3290+
3291+ if self .royalty_rate .Provided :
3292+ average_annual_royalties = np .average (
3293+ self .sam_economics_calculations .royalties_opex .value [1 :] # ignore pre-revenue year(s) (Year 0)
3294+ )
3295+
3296+ self .royalties_average_annual_cost .value = average_annual_royalties
3297+ self .Coam .value += self .royalties_average_annual_cost .quantity ().to (self .Coam .CurrentUnits .value ).magnitude
3298+
3299+ self .royalty_holder_npv .value = calculate_npv (
3300+ self .royalty_holder_discount_rate .value ,
3301+ self .sam_economics_calculations .royalties_opex .value ,
3302+ self .discount_initial_year_cashflow .value
3303+ )
3304+ # FIXME WIP
3305+ # self.royalty_holder_annual_revenue
3306+ # self.royalty_holder_total_revenue
3307+
3308+
3309+ self .wacc .value = self .sam_economics_calculations .wacc .value
3310+ self .nominal_discount_rate .value = self .sam_economics_calculations .nominal_discount_rate .value
3311+ self .ProjectNPV .value = self .sam_economics_calculations .project_npv .quantity ().to (
3312+ convertible_unit (self .ProjectNPV .CurrentUnits )).magnitude
3313+
3314+ self .ProjectIRR .value = non_calculated_output_placeholder_val # SAM calculates After-Tax IRR instead
3315+ self .after_tax_irr .value = self .sam_economics_calculations .after_tax_irr .quantity ().to (
3316+ convertible_unit (self .ProjectIRR .CurrentUnits )).magnitude
3317+
3318+ self .ProjectMOIC .value = self .sam_economics_calculations .moic .value
3319+ self .ProjectVIR .value = self .sam_economics_calculations .project_vir .value
3320+
3321+ # TODO remove or clarify project payback period: https://github.com/NREL/GEOPHIRES-X/issues/413
3322+ self .ProjectPaybackPeriod .value = self .sam_economics_calculations .project_payback_period .value
3323+
32703324 # noinspection SpellCheckingInspection
32713325 def _calculate_derived_outputs (self , model : Model ) -> None :
32723326 """
0 commit comments