Skip to content

Commit

Permalink
Functions to lib_financial for all financial models
Browse files Browse the repository at this point in the history
  • Loading branch information
cpaulgilman committed Oct 17, 2024
1 parent 8362764 commit 3202a6d
Show file tree
Hide file tree
Showing 11 changed files with 424 additions and 1,669 deletions.
75 changes: 45 additions & 30 deletions shared/lib_financial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <limits>
#include "lib_financial.h"

using namespace libfin;
double libfin::min(double a, double b)
{ // handle NaN
if ((a != a) || (b != b))
return 0;
else
return (a < b) ? a : b;
}

double libfin::max(double a, double b)
{ // handle NaN
if ((a != a) || (b != b))
return 0;
else
return (a > b) ? a : b;
}

/* ported from http://code.google.com/p/irr-newtonraphson-calculator/ */
bool is_valid_iter_bound(double estimated_return_rate)
Expand Down Expand Up @@ -83,8 +97,8 @@ double irr_scale_factor(const std::vector<double>& cf_vector_unscaled, int count

bool is_valid_irr(const std::vector<double>& cf_vector, int count, double residual, double tolerance, int number_of_iterations, int max_iterations, double calculated_irr, double scale_factor)
{
double npv_of_irr = npv(cf_vector, count, calculated_irr) + cf_vector.at(0);
double npv_of_irr_plus_delta = npv(cf_vector, count, calculated_irr + 0.001) + cf_vector.at(0);
double npv_of_irr = libfin::npv(cf_vector, count, calculated_irr) + cf_vector.at(0);
double npv_of_irr_plus_delta = libfin::npv(cf_vector, count, calculated_irr + 0.001) + cf_vector.at(0);
bool is_valid = ((number_of_iterations < max_iterations) && (std::abs(residual) < tolerance) && (npv_of_irr > npv_of_irr_plus_delta) && (std::abs(npv_of_irr / scale_factor) < tolerance));
//if (!is_valid)
//{
Expand Down Expand Up @@ -230,39 +244,40 @@ double libfin::npv(const std::vector<double>& cf_vector, int nyears, double rate
return result * rr;
}

double libfin::payback(const std::vector<double> &CumulativePayback, const std::vector<double> &Payback, int Count)
double libfin::payback(const std::vector<double> &cumulative_payback, const std::vector<double> &payback, int nyears)
{
/*
Return payback in years of inputs streams
Payback occures when cumulative stream is > 0
Find exact payback by subtracting cumulative / payback
*/
double dPayback = 1e99; // report as > analysis period
bool bolPayback = false;
int iPayback = 0;
int i = 1;
while ((i<Count) && (!bolPayback))
{
if (CumulativePayback[i] > 0)
{
bolPayback = true;
iPayback = i;
}
i++;
}
double dpb = std::numeric_limits<double>::quiet_NaN();
bool bpb = false;
int ipb = 0;
int i = 1;
while ((i<nyears) && (!bpb))
{
if (cumulative_payback[i] > 0)
{
bpb = true;
ipb = i;
}
i++;
}

if (bolPayback)
if (bpb)
{
if (Payback[iPayback] !=0)
if (payback[ipb] !=0)
{
dPayback = iPayback - CumulativePayback[iPayback] /Payback[iPayback];
dpb = ipb - cumulative_payback[ipb] / payback[ipb];
}
else
{
dPayback = iPayback;
dpb = ipb;
}
}
return dPayback;

return dpb;
}


Expand All @@ -271,9 +286,9 @@ Returns the payment on the principal for a given period for an investment based
Syntax
PPMT(rate,per,nper,pv,fv,type)
libfin::ppmt(rate,per,nper,pv,fv,type)
For a more complete description of the arguments in PPMT, see PV.
For a more complete description of the arguments in libfin::ppmt, see PV.
Rate is the interest rate per period.
Expand All @@ -296,30 +311,30 @@ Make sure that you are consistent about the units you use for specifying rate an
*/
double libfin::pow1pm1 (double x, double y)
double pow1pm1 (double x, double y)
{
return (x <= -1) ? pow (1 + x, y) - 1 : exp(y * log(1.0 + x)) - 1;
}
double libfin::pow1p (double x, double y)
double pow1p (double x, double y)
{
return (std::abs(x) > 0.5) ? pow (1 + x, y) : exp (y * log(1.0 + x));
}
double libfin::fvifa (double rate, double nper)
double fvifa (double rate, double nper)
{
return (rate == 0) ? nper : pow1pm1 (rate, nper) / rate;
}

double libfin::pvif (double rate, double nper)
double pvif (double rate, double nper)
{
return pow1p (rate, nper);
}

double libfin::pmt (double rate, double nper, double pv, double fv, int type)
double pmt (double rate, double nper, double pv, double fv, int type)
{
return ((-pv * pvif (rate, nper) - fv ) / ((1.0 + rate * type) * fvifa (rate, nper)));
}

double libfin::ipmt (double rate, double per, double nper, double pv, double fv, int type)
double ipmt (double rate, double per, double nper, double pv, double fv, int type)
{
double p = pmt (rate, nper, pv, fv, 0);
double ip = -(pv * pow1p (rate, per - 1) * rate + p * pow1pm1 (rate, per - 1));
Expand Down
24 changes: 11 additions & 13 deletions shared/lib_financial.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <vector>

namespace libfin {

//double irr(double tolerance, int maxIterations, const std::vector<double> &CashFlows, int Count);
double min(double a, double b);
double max(double a, double b);
double irr(const std::vector<double>& cf, int count, double initial_guess = -2, double tolerance = 1e-6, int max_iterations = 100);
//double npv(double Rate, const std::vector<double> &CashFlows, int Count);
double npv(const std::vector<double>& cf, int nyears, double rate);
double payback(const std::vector<double> &CumulativePayback, const std::vector<double> &Payback, int Count);

double pow1pm1 (double x, double y);
double pow1p (double x, double y);
double fvifa (double rate, double nper);
double pvif (double rate, double nper);
double pmt (double rate, double nper, double pv, double fv, int type);
double ipmt (double rate, double per, double nper, double pv, double fv, int type) ;
double ppmt (double rate, double per, double nper, double pv, double fv, int type) ;

long round_irs(double number);

double ppmt(double rate, double per, double nper, double pv, double fv, int type);
double payback(const std::vector<double>& CumulativePayback, const std::vector<double>& Payback, int Count);
}

double pow1pm1(double x, double y);
double pow1p(double x, double y);
double fvifa(double rate, double nper);
double pvif(double rate, double nper);
double pmt(double rate, double nper, double pv, double fv, int type);
double ipmt(double rate, double per, double nper, double pv, double fv, int type);

#endif
87 changes: 29 additions & 58 deletions ssc/cmod_cashloan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "lib_financial.h"
#include "lib_util.h"
#include "common_financial.h"
using namespace libfin;

#include <sstream>

static var_info vtab_cashloan[] = {
Expand Down Expand Up @@ -792,14 +792,14 @@ class cm_cashloan : public compute_module
// SAM 1038
itc_sta_per = 0.0;
for (size_t k = 0; k <= nyears; k++) {
cf.at(CF_itc_sta_percent_amount, k) = min(cf.at(CF_itc_sta_percent_maxvalue, k), cf.at(CF_itc_sta_percent_amount, k) * state_itc_basis);
cf.at(CF_itc_sta_percent_amount, k) = libfin::min(cf.at(CF_itc_sta_percent_maxvalue, k), cf.at(CF_itc_sta_percent_amount, k) * state_itc_basis);
itc_sta_per += cf.at(CF_itc_sta_percent_amount, k);
}

// SAM 1038
itc_fed_per = 0.0;
for (size_t k = 0; k <= nyears; k++) {
cf.at(CF_itc_fed_percent_amount, k) = min(cf.at(CF_itc_fed_percent_maxvalue, k), cf.at(CF_itc_fed_percent_amount, k) * federal_itc_basis);
cf.at(CF_itc_fed_percent_amount, k) = libfin::min(cf.at(CF_itc_fed_percent_maxvalue, k), cf.at(CF_itc_fed_percent_amount, k) * federal_itc_basis);
itc_fed_per += cf.at(CF_itc_fed_percent_amount, k);
}

Expand Down Expand Up @@ -958,7 +958,7 @@ class cm_cashloan : public compute_module
{
cf.at(CF_debt_balance, i-1) = loan_amount;
cf.at(CF_debt_payment_interest, i) = loan_amount * loan_rate;
cf.at(CF_debt_payment_principal,i) = -ppmt( loan_rate, // Rate
cf.at(CF_debt_payment_principal,i) = -libfin::ppmt( loan_rate, // Rate
i, // Period
loan_term, // Number periods
loan_amount, // Present Value
Expand Down Expand Up @@ -1204,10 +1204,10 @@ class cm_cashloan : public compute_module

save_cf(CF_parasitic_cost, nyears, "cf_parasitic_cost");

double npv_energy_real = npv(CF_energy_sales, nyears, real_discount_rate);
double npv_energy_real = libfin::npv(cf.row(CF_energy_sales).to_vector(), nyears, real_discount_rate);
// if (npv_energy_real == 0.0) throw general_error("lcoe real failed because energy npv is zero");
// double lcoe_real = -( cf.at(CF_after_tax_net_equity_cost_flow,0) + npv(CF_after_tax_net_equity_cost_flow, nyears, nom_discount_rate) ) * 100 / npv_energy_real;
double lcoe_real = -( cf.at(CF_after_tax_net_equity_cost_flow,0) + npv(CF_after_tax_net_equity_cost_flow, nyears, nom_discount_rate) - npv(CF_parasitic_cost, nyears, nom_discount_rate) ) * 100;
// double lcoe_real = -( cf.at(CF_after_tax_net_equity_cost_flow,0) + libfin::npv(CF_after_tax_net_equity_cost_flow, nyears, nom_discount_rate) ) * 100 / npv_energy_real;
double lcoe_real = -( cf.at(CF_after_tax_net_equity_cost_flow,0) + libfin::npv(cf.row(CF_after_tax_net_equity_cost_flow).to_vector(), nyears, nom_discount_rate) - libfin::npv(cf.row(CF_parasitic_cost).to_vector(), nyears, nom_discount_rate)) * 100;
if (npv_energy_real == 0.0)
{
lcoe_real = std::numeric_limits<double>::quiet_NaN();
Expand All @@ -1217,10 +1217,10 @@ class cm_cashloan : public compute_module
lcoe_real /= npv_energy_real;
}

double npv_energy_nom = npv( CF_energy_sales, nyears, nom_discount_rate );
double npv_energy_nom = libfin::npv(cf.row(CF_energy_sales).to_vector(), nyears, nom_discount_rate );
// if (npv_energy_nom == 0.0) throw general_error("lcoe nom failed because energy npv is zero");
// double lcoe_nom = -( cf.at(CF_after_tax_net_equity_cost_flow,0) + npv(CF_after_tax_net_equity_cost_flow, nyears, nom_discount_rate) ) * 100 / npv_energy_nom;
double lcoe_nom = -( cf.at(CF_after_tax_net_equity_cost_flow,0) + npv(CF_after_tax_net_equity_cost_flow, nyears, nom_discount_rate) - npv(CF_parasitic_cost, nyears, nom_discount_rate)) * 100;
// double lcoe_nom = -( cf.at(CF_after_tax_net_equity_cost_flow,0) + libfin::npv(CF_after_tax_net_equity_cost_flow, nyears, nom_discount_rate) ) * 100 / npv_energy_nom;
double lcoe_nom = -( cf.at(CF_after_tax_net_equity_cost_flow,0) + libfin::npv(cf.row(CF_after_tax_net_equity_cost_flow).to_vector(), nyears, nom_discount_rate) - libfin::npv(cf.row(CF_parasitic_cost).to_vector(), nyears, nom_discount_rate)) * 100;
if (npv_energy_nom == 0.0)
{
lcoe_nom = std::numeric_limits<double>::quiet_NaN();
Expand All @@ -1230,19 +1230,19 @@ class cm_cashloan : public compute_module
lcoe_nom /= npv_energy_nom;
}

double net_present_value = cf.at(CF_after_tax_cash_flow, 0) + npv(CF_after_tax_cash_flow, nyears, nom_discount_rate );
double net_present_value = cf.at(CF_after_tax_cash_flow, 0) + libfin::npv(cf.row(CF_after_tax_cash_flow).to_vector(), nyears, nom_discount_rate);
double irr = libfin::irr(cf.row(CF_after_tax_cash_flow).to_vector(),nyears)*100;

double payback = compute_payback(CF_cumulative_payback_with_expenses, CF_payback_with_expenses, nyears);
double payback = libfin::payback(cf.row(CF_cumulative_payback_with_expenses).to_vector(), cf.row(CF_payback_with_expenses).to_vector(), nyears);
// Added for Owen Zinaman for Mexico Rates and analyses 9/26/16
//- see C:\Projects\SAM\Documentation\Payback\DiscountedPayback_2016.9.26
double discounted_payback = compute_payback(CF_discounted_cumulative_payback, CF_discounted_payback, nyears);
double discounted_payback = libfin::payback(cf.row(CF_discounted_cumulative_payback).to_vector(), cf.row(CF_discounted_payback).to_vector(), nyears);

// save outputs


double npv_fed_ptc = npv(CF_ptc_fed,nyears,nom_discount_rate);
double npv_sta_ptc = npv(CF_ptc_sta,nyears,nom_discount_rate);
double npv_fed_ptc = libfin::npv(cf.row(CF_ptc_fed).to_vector(), nyears, nom_discount_rate);
double npv_sta_ptc = libfin::npv(cf.row(CF_ptc_sta).to_vector(), nyears, nom_discount_rate);

// TODO check this
// npv_fed_ptc /= (1.0 - effective_tax_rate);
Expand Down Expand Up @@ -1309,7 +1309,7 @@ class cm_cashloan : public compute_module

for (i = 0; i < (int)count; i++)
cf.at(CF_nte, i) = (double) (ub_wo_sys[i] - ub_w_sys[i]) *100.0;// $ to cents
double lnte_real = npv( CF_nte, nyears, nom_discount_rate );
double lnte_real = libfin::npv(cf.row(CF_nte).to_vector(), nyears, nom_discount_rate);

for (i = 0; i < (int)count; i++)
if (cf.at(CF_energy_net,i) > 0) cf.at(CF_nte,i) /= cf.at(CF_energy_net,i);
Expand Down Expand Up @@ -1486,14 +1486,14 @@ class cm_cashloan : public compute_module
save_cf( CF_cumulative_payback_without_expenses, nyears, "cf_cumulative_payback_without_expenses" );

// for cost stacked bars
//npv(CF_energy_value, nyears, nom_discount_rate)
//libfin::npv(CF_energy_value, nyears, nom_discount_rate)
// present value of o and m value - note - present value is distributive - sum of pv = pv of sum
double pvAnnualOandM = npv(CF_om_fixed_expense, nyears, nom_discount_rate);
double pvFixedOandM = npv(CF_om_capacity_expense, nyears, nom_discount_rate);
double pvVariableOandM = npv(CF_om_production_expense, nyears, nom_discount_rate);
double pvFuelOandM = npv(CF_om_fuel_expense, nyears, nom_discount_rate);
double pvOptFuel1OandM = npv(CF_om_opt_fuel_1_expense, nyears, nom_discount_rate);
double pvOptFuel2OandM = npv(CF_om_opt_fuel_2_expense, nyears, nom_discount_rate);
double pvAnnualOandM = libfin::npv(cf.row(CF_om_fixed_expense).to_vector(), nyears, nom_discount_rate);
double pvFixedOandM = libfin::npv(cf.row(CF_om_capacity_expense).to_vector(), nyears, nom_discount_rate);
double pvVariableOandM = libfin::npv(cf.row(CF_om_production_expense).to_vector(), nyears, nom_discount_rate);
double pvFuelOandM = libfin::npv(cf.row(CF_om_fuel_expense).to_vector(), nyears, nom_discount_rate);
double pvOptFuel1OandM = libfin::npv(cf.row(CF_om_opt_fuel_1_expense).to_vector(), nyears, nom_discount_rate);
double pvOptFuel2OandM = libfin::npv(cf.row(CF_om_opt_fuel_2_expense).to_vector(), nyears, nom_discount_rate);
// double pvWaterOandM = NetPresentValue(sv[svNominalDiscountRate], cf[cfAnnualWaterCost], analysis_period);

assign( "present_value_oandm", var_data((ssc_number_t)(pvAnnualOandM + pvFixedOandM + pvVariableOandM + pvFuelOandM))); // + pvWaterOandM);
Expand All @@ -1502,8 +1502,8 @@ class cm_cashloan : public compute_module
assign( "present_value_fuel", var_data((ssc_number_t)(pvFuelOandM + pvOptFuel1OandM + pvOptFuel2OandM)));

// present value of insurance and property tax
double pvInsurance = npv(CF_insurance_expense, nyears, nom_discount_rate);
double pvPropertyTax = npv(CF_property_tax_expense, nyears, nom_discount_rate);
double pvInsurance = libfin::npv(cf.row(CF_insurance_expense).to_vector(), nyears, nom_discount_rate);
double pvPropertyTax = libfin::npv(cf.row(CF_property_tax_expense).to_vector(), nyears, nom_discount_rate);

assign( "present_value_insandproptax", var_data((ssc_number_t)(pvInsurance + pvPropertyTax)));

Expand All @@ -1528,7 +1528,7 @@ class cm_cashloan : public compute_module
for (int i=0;i<=nyears;i++)
arrp[i] = (ssc_number_t)cf.at(cf_line, i);
}

/*
double compute_payback( int cf_cpb, int cf_pb, int nyears )
{
// may need to determine last negative to positive transition for high replacement costs - see C:\Projects\SAM\Documentation\FinancialIssues\Payback_2015.9.8
Expand All @@ -1555,20 +1555,7 @@ class cm_cashloan : public compute_module
return dPayback;
}


double npv(int cf_line, int nyears, double rate)
{
if (rate <= -1.0) throw general_error("cannot calculate NPV with discount rate less or equal to -1.0");

double rr = 1/(1+rate);
double result = 0;
for (int i=nyears;i>0;i--)
result = rr * result + cf.at(cf_line,i);

return result*rr;
}

*/
void compute_production_incentive( int cf_line, int nyears, const std::string &s_val, const std::string &s_term, const std::string &s_escal )
{
size_t len = 0;
Expand Down Expand Up @@ -1599,7 +1586,7 @@ class cm_cashloan : public compute_module
if (len == 1)
{
for (int i=1;i<=nyears;i++)
cf.at(cf_line, i) = (i <= term) ? cf.at(CF_energy_sales,i) / 1000.0 * round_irs(1000.0 * parr[0] * pow(1 + escal, i-1)) : 0.0;
cf.at(cf_line, i) = (i <= term) ? cf.at(CF_energy_sales,i) / 1000.0 * libfin::round_irs(1000.0 * parr[0] * pow(1 + escal, i-1)) : 0.0;
}
else
{
Expand All @@ -1623,7 +1610,7 @@ class cm_cashloan : public compute_module
size_t len = 0;
ssc_number_t *p = as_array(name, &len);
for (int i=1;i<=(int)len && i <= nyears;i++)
cf.at(cf_line, i) = min( scale*p[i-1], max );
cf.at(cf_line, i) = libfin::min( scale*p[i-1], max );
}

double taxable_incentive_income(int year, const std::string &fed_or_sta)
Expand Down Expand Up @@ -1730,22 +1717,6 @@ class cm_cashloan : public compute_module
}
}

double min(double a, double b)
{ // handle NaN
if ((a != a) || (b != b))
return 0;
else
return (a < b) ? a : b;
}

double max(double a, double b)
{ // handle NaN
if ((a != a) || (b != b))
return 0;
else
return (a > b) ? a : b;
}

};

DEFINE_MODULE_ENTRY( cashloan, "Residential/Commerical Finance model.", 1 );
Loading

0 comments on commit 3202a6d

Please sign in to comment.