Skip to content

Commit

Permalink
Change cf_energy_net to cf_energy_sales (#1066)
Browse files Browse the repository at this point in the history
* Change cf_energy_net to cf_energy_sales

Calculate O&M production based expense, PBI, PTC, LCOE from cf_energy_sales for all financial models.

Remove return on equity (ROE) from Community Solar model. Was originally implemented specifically for India and then removed from other financial models.

* Fix test results for updated annual energy in cash flow

* Retry fixing test results

* Fix test Attempt 3 include "_branch" in file name

* Update all input test json files and references to files

---------

Co-authored-by: Steven Janzou <[email protected]>
  • Loading branch information
cpaulgilman and sjanzou authored Oct 26, 2023
1 parent 8c1c921 commit 870f579
Show file tree
Hide file tree
Showing 21 changed files with 2,774 additions and 2,850 deletions.
93 changes: 7 additions & 86 deletions ssc/cmod_communitysolar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,6 @@ static var_info _cm_vtab_communitysolar[] = {
{ SSC_OUTPUT, SSC_ARRAY, "cf_utility_bill", "Electricity purchase", "$", "", "", "", "LENGTH_EQUAL=cf_length", "" },


/* return on equity from SAM for India */
{ SSC_INPUT, SSC_ARRAY, "roe_input", "Return on equity", "", "", "Financial Parameters", "?=20", "", "" },
{ SSC_OUTPUT, SSC_ARRAY, "cf_return_on_equity", "Return on equity", "$/kWh", "", "Return on Equity", "*", "LENGTH_EQUAL=cf_length", "" },
{ SSC_OUTPUT, SSC_ARRAY, "cf_return_on_equity_input", "Return on equity input", "%", "", "Return on Equity", "*", "LENGTH_EQUAL=cf_length", "" },
{ SSC_OUTPUT, SSC_ARRAY, "cf_return_on_equity_dollars", "Return on equity dollars", "$", "", "Return on Equity", "*", "LENGTH_EQUAL=cf_length", "" },
{ SSC_OUTPUT, SSC_ARRAY, "cf_lcog_costs", "Total LCOG costs", "$", "", "Return on Equity", "*", "LENGTH_EQUAL=cf_length", "" },
{ SSC_OUTPUT, SSC_NUMBER, "lcog_om", "LCOG O and M", "cents/kWh", "", "Return on Equity", "*", "", "" },
{ SSC_OUTPUT, SSC_NUMBER, "lcog_depr", "LCOG depreciation", "cents/kWh", "", "Return on Equity", "*", "", "" },
{ SSC_OUTPUT, SSC_NUMBER, "lcog_loan_int", "LCOG loan interest", "cents/kWh", "", "Return on Equity", "*", "", "" },
{ SSC_OUTPUT, SSC_NUMBER, "lcog_wc_int", "LCOG working capital interest", "cents/kWh", "", "Return on Equity", "*", "", "" },
{ SSC_OUTPUT, SSC_NUMBER, "lcog_roe", "LCOG return on equity", "cents/kWh", "", "Return on Equity", "*", "", "" },
{ SSC_OUTPUT, SSC_NUMBER, "lcog", "LCOG Levelized cost of generation", "cents/kWh", "", "Return on Equity", "*", "", "" },

/*loan moratorium from Sara for India Documentation\India\Loan Moratorum
assumptions:
1) moratorium period begins at beginning of loan term
Expand Down Expand Up @@ -1016,11 +1003,6 @@ enum {
CF_Recapitalization,
CF_Recapitalization_boolean,

CF_return_on_equity_input,
CF_return_on_equity_dollars,
CF_return_on_equity,
CF_lcog_costs,

CF_Annual_Costs,
CF_pretax_dscr,

Expand Down Expand Up @@ -1626,7 +1608,7 @@ class cm_communitysolar : public compute_module
cf.at(CF_om_production_expense, i) *= cf.at(CF_energy_without_battery, i);
}
else {
cf.at(CF_om_production_expense, i) *= cf.at(CF_energy_net, i);
cf.at(CF_om_production_expense, i) *= cf.at(CF_energy_sales, i);
}
cf.at(CF_om_capacity_expense, i) *= nameplate;
cf.at(CF_om_capacity1_expense, i) *= nameplate1;
Expand Down Expand Up @@ -1801,11 +1783,11 @@ class cm_communitysolar : public compute_module
for (size_t i = 0; i <= nyears; i++) {

// revenue to system owner from subscriber payments
cf.at(CF_subscriber1_share_of_generation, i) = cf.at(CF_subscriber1_share_fraction, i) * cf.at(CF_energy_net, i);
cf.at(CF_subscriber2_share_of_generation, i) = cf.at(CF_subscriber2_share_fraction, i) * cf.at(CF_energy_net, i);
cf.at(CF_subscriber3_share_of_generation, i) = cf.at(CF_subscriber3_share_fraction, i) * cf.at(CF_energy_net, i);
cf.at(CF_subscriber4_share_of_generation, i) = cf.at(CF_subscriber4_share_fraction, i) * cf.at(CF_energy_net, i);
cf.at(CF_unsubscribed_share_of_generation, i) = cf.at(CF_unsubscribed_share_fraction, i) * cf.at(CF_energy_net, i);
cf.at(CF_subscriber1_share_of_generation, i) = cf.at(CF_subscriber1_share_fraction, i) * cf.at(CF_energy_sales, i);
cf.at(CF_subscriber2_share_of_generation, i) = cf.at(CF_subscriber2_share_fraction, i) * cf.at(CF_energy_sales, i);
cf.at(CF_subscriber3_share_of_generation, i) = cf.at(CF_subscriber3_share_fraction, i) * cf.at(CF_energy_sales, i);
cf.at(CF_subscriber4_share_of_generation, i) = cf.at(CF_subscriber4_share_fraction, i) * cf.at(CF_energy_sales, i);
cf.at(CF_unsubscribed_share_of_generation, i) = cf.at(CF_unsubscribed_share_fraction, i) * cf.at(CF_energy_sales, i);

cf.at(CF_subscriber1_revenue_generation, i) = cf.at(CF_subscriber1_share_of_generation, i) * cf.at(CF_subscriber1_generation_payment, i);
cf.at(CF_subscriber2_revenue_generation, i) = cf.at(CF_subscriber2_share_of_generation, i) * cf.at(CF_subscriber2_generation_payment, i);
Expand Down Expand Up @@ -1860,7 +1842,7 @@ class cm_communitysolar : public compute_module
}

// operating expenses
cf.at(CF_recurring_generation, i) *= cf.at(CF_energy_net, i);
cf.at(CF_recurring_generation, i) *= cf.at(CF_energy_sales, i);
cf.at(CF_recurring_capacity, i) *= nameplate;

// twice??
Expand Down Expand Up @@ -2003,25 +1985,6 @@ class cm_communitysolar : public compute_module
}
}

// return on equity based on workbook and emails from Sara Turner for SAM for India
size_t roe_count;
ssc_number_t *roe_input = 0;
roe_input = as_array("roe_input", &roe_count);
if (roe_count > 0)
{
if (roe_count == 1) // single value input
{
for (i = 0; i < nyears; i++)
cf.at(CF_return_on_equity_input, i + 1) = roe_input[0]/100.0;
}
else // schedule
{
for (i = 0; i < nyears && i < (int)roe_count; i++)
cf.at(CF_return_on_equity_input, i + 1) = roe_input[i]/100.0;
}
}



for (i=1; i<=nyears; i++)
{
Expand Down Expand Up @@ -3161,10 +3124,6 @@ class cm_communitysolar : public compute_module

for (i=0; i<=nyears; i++)
{
// cf.at(CF_return_on_equity_dollars, i) = issuance_of_equity * cf.at(CF_return_on_equity_input, i);
cf.at(CF_return_on_equity_dollars, i) = (issuance_of_equity - cf.at(CF_reserve_receivables,0)) * cf.at(CF_return_on_equity_input, i);
if (cf.at(CF_energy_net, i) != 0)
cf.at(CF_return_on_equity, i) = cf.at(CF_return_on_equity_dollars, i) / cf.at(CF_energy_net, i);
// cf.at(CF_project_operating_activities,i) = cf.at(CF_ebitda,i) + cf.at(CF_pbi_total,i) + cf.at(CF_reserve_interest,i) - cf.at(CF_debt_payment_interest,i);
cf.at(CF_project_operating_activities,i) = cf.at(CF_ebitda,i) + cf.at(CF_reserve_interest,i) - cf.at(CF_debt_payment_interest,i) +
(1.0 - pbi_fed_for_ds_frac) * cf.at(CF_pbi_fed,i) +
Expand Down Expand Up @@ -3903,10 +3862,6 @@ class cm_communitysolar : public compute_module

save_cf(CF_Recapitalization, nyears, "cf_recapitalization");

save_cf(CF_return_on_equity_input, nyears, "cf_return_on_equity_input");
save_cf(CF_return_on_equity_dollars, nyears, "cf_return_on_equity_dollars");
save_cf(CF_return_on_equity, nyears, "cf_return_on_equity");

// community solar cashflow outputs
save_cf(CF_subscriber1_share_fraction, nyears, "cf_subscriber1_share_fraction");
save_cf(CF_subscriber2_share_fraction, nyears, "cf_subscriber2_share_fraction");
Expand Down Expand Up @@ -3988,40 +3943,6 @@ class cm_communitysolar : public compute_module
assign("subscriber3_npv", var_data((ssc_number_t)(npv(CF_subscriber3_net_benefit, nyears, nom_discount_rate) + cf.at(CF_subscriber3_net_benefit, 0))));
assign("subscriber4_npv", var_data((ssc_number_t)(npv(CF_subscriber4_net_benefit, nyears, nom_discount_rate) + cf.at(CF_subscriber4_net_benefit, 0))));

for (i = 0; i <= nyears; i++)
{
cf.at(CF_lcog_costs, i) = cf.at(CF_om_capacity_expense, i)
+ cf.at(CF_feddepr_total, i)
+ cf.at(CF_debt_payment_interest, i)
+ cf.at(CF_reserve_interest, i)
+ cf.at(CF_return_on_equity_dollars, i);
}
save_cf(CF_lcog_costs, nyears, "cf_lcog_costs");

double lcog_om = npv(CF_om_capacity_expense, nyears, nom_discount_rate);
if (npv_energy_nom != 0) lcog_om = lcog_om * 100.0 / npv_energy_nom;
assign("lcog_om", var_data((ssc_number_t)lcog_om));

double lcog_depr = npv(CF_feddepr_total, nyears, nom_discount_rate);
if (npv_energy_nom != 0) lcog_depr = lcog_depr * 100.0 / npv_energy_nom;
assign("lcog_depr", var_data((ssc_number_t)lcog_depr));

double lcog_loan_int = npv(CF_debt_payment_interest, nyears, nom_discount_rate);
if (npv_energy_nom != 0) lcog_loan_int = lcog_loan_int * 100.0 / npv_energy_nom;
assign("lcog_loan_int", var_data((ssc_number_t)lcog_loan_int));

double lcog_wc_int = npv(CF_reserve_interest, nyears, nom_discount_rate);
if (npv_energy_nom != 0) lcog_wc_int = lcog_wc_int * 100.0 / npv_energy_nom;
assign("lcog_wc_int", var_data((ssc_number_t)lcog_wc_int));

double lcog_roe = npv(CF_return_on_equity_dollars, nyears, nom_discount_rate);
if (npv_energy_nom != 0) lcog_roe = lcog_roe * 100.0 / npv_energy_nom;
assign("lcog_roe", var_data((ssc_number_t)lcog_roe));

double lcog_nom = npv(CF_lcog_costs, nyears, nom_discount_rate);
if (npv_energy_nom != 0) lcog_nom = lcog_nom * 100.0 / npv_energy_nom;
assign("lcog", var_data((ssc_number_t)lcog_nom));

// dispatch
/* Community solar not available with storage, need to address price signal dispatch with no PPA if we enable with storage
std::vector<double> ppa_cf;
Expand Down
2 changes: 1 addition & 1 deletion ssc/cmod_host_developer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2822,7 +2822,7 @@ class cm_host_developer : public compute_module
cf.at(CF_host_energy_value, i) = (double)arrp[i];
// calculated ppa in cents/kWh - cash flow line items in $
for ( i = 1; i <= nyears; i++)
cf.at(CF_agreement_cost, i) = (ppa/100.0) * cf.at(CF_energy_net, i) * pow(1 + ppa_escalation, i - 1);
cf.at(CF_agreement_cost, i) = (ppa/100.0) * cf.at(CF_energy_sales, i) * pow(1 + ppa_escalation, i - 1);

for (i = 1; i <= nyears; i++)
{
Expand Down

This file was deleted.

Loading

0 comments on commit 870f579

Please sign in to comment.