Skip to content

Commit

Permalink
Fix broken PVWatts-Wind-FuelCell-Battery configurations
Browse files Browse the repository at this point in the history
  • Loading branch information
sjanzou committed Aug 25, 2023
1 parent 5deabdc commit efa710c
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 114 deletions.
105 changes: 55 additions & 50 deletions ssc/cmod_host_developer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1027,65 +1027,70 @@ class cm_host_developer : public compute_module
std::vector<double> battery_discharged(nyears,0);
std::vector<double> fuelcell_discharged(nyears+1,0);

if (add_om_num_types > 0) //PV Battery
{
escal_or_annual(CF_om_fixed1_expense, nyears, "om_batt_fixed_cost", inflation_rate, 1.0, false, as_double("om_fixed_escal") * 0.01);
escal_or_annual(CF_om_production1_expense, nyears, "om_batt_variable_cost", inflation_rate, 0.001, false, as_double("om_production_escal") * 0.01); //$/MWh
escal_or_annual(CF_om_capacity1_expense, nyears, "om_batt_capacity_cost", inflation_rate, 1.0, false, as_double("om_capacity_escal") * 0.01);
nameplate1 = as_number("om_batt_nameplate");
if (as_integer("en_batt") == 1 || as_integer("en_standalone_batt") == 1)
battery_discharged = as_vector_double("batt_annual_discharge_energy");
}
if (battery_discharged.size() == 1) { // ssc #992
double first_val = battery_discharged[0];
battery_discharged.resize(nyears, first_val);
if (is_assigned("is_hybrid") && as_integer("is_hybrid") == 1) {
// already added in additional o and m - this is only necessary if dispatch and replacments at top level as when fuel cell and battery dispatch combined
}
if (battery_discharged.size() != nyears)
throw exec_error("hostdeveloper", util::format("battery_discharged size (%d) incorrect",(int)battery_discharged.size()));
else {

if (add_om_num_types > 1)
{
escal_or_annual(CF_om_fixed2_expense, nyears, "om_fuelcell_fixed_cost", inflation_rate, 1.0, false, as_double("om_fixed_escal")*0.01);
escal_or_annual(CF_om_production2_expense, nyears, "om_fuelcell_variable_cost", inflation_rate, 0.001, false, as_double("om_production_escal")*0.01);
escal_or_annual(CF_om_capacity2_expense, nyears, "om_fuelcell_capacity_cost", inflation_rate, 1.0, false, as_double("om_capacity_escal")*0.01);
nameplate2 = as_number("om_fuelcell_nameplate");
fuelcell_discharged = as_vector_double("fuelcell_annual_energy_discharged");
}
if (fuelcell_discharged.size()== 2) { // ssc #992
double first_val = fuelcell_discharged[1];
fuelcell_discharged.resize(nyears+1, first_val);
}
if (fuelcell_discharged.size() != nyears+1)
throw exec_error("hostdeveloper", util::format("fuelcell_discharged size (%d) incorrect",(int)fuelcell_discharged.size()));

// battery cost - replacement from lifetime analysis
if ((as_integer("en_batt") == 1 || as_integer("en_standalone_batt") == 1) && (as_integer("batt_replacement_option") > 0))
{
ssc_number_t* batt_rep = 0;
std::vector<ssc_number_t> replacement_percent;
if (add_om_num_types > 0) //PV Battery
{
escal_or_annual(CF_om_fixed1_expense, nyears, "om_batt_fixed_cost", inflation_rate, 1.0, false, as_double("om_fixed_escal") * 0.01);
escal_or_annual(CF_om_production1_expense, nyears, "om_batt_variable_cost", inflation_rate, 0.001, false, as_double("om_production_escal") * 0.01); //$/MWh
escal_or_annual(CF_om_capacity1_expense, nyears, "om_batt_capacity_cost", inflation_rate, 1.0, false, as_double("om_capacity_escal") * 0.01);
nameplate1 = as_number("om_batt_nameplate");
if (as_integer("en_batt") == 1 || as_integer("en_standalone_batt") == 1)
battery_discharged = as_vector_double("batt_annual_discharge_energy");
}
if (battery_discharged.size() == 1) { // ssc #992
double first_val = battery_discharged[0];
battery_discharged.resize(nyears, first_val);
}
if (battery_discharged.size() != nyears)
throw exec_error("hostdeveloper", util::format("battery_discharged size (%d) incorrect", (int)battery_discharged.size()));

batt_rep = as_array("batt_bank_replacement", &count); // replacements per year calculated
if (add_om_num_types > 1)
{
escal_or_annual(CF_om_fixed2_expense, nyears, "om_fuelcell_fixed_cost", inflation_rate, 1.0, false, as_double("om_fixed_escal") * 0.01);
escal_or_annual(CF_om_production2_expense, nyears, "om_fuelcell_variable_cost", inflation_rate, 0.001, false, as_double("om_production_escal") * 0.01);
escal_or_annual(CF_om_capacity2_expense, nyears, "om_fuelcell_capacity_cost", inflation_rate, 1.0, false, as_double("om_capacity_escal") * 0.01);
nameplate2 = as_number("om_fuelcell_nameplate");
fuelcell_discharged = as_vector_double("fuelcell_annual_energy_discharged");
}
if (fuelcell_discharged.size() == 2) { // ssc #992
double first_val = fuelcell_discharged[1];
fuelcell_discharged.resize(nyears + 1, first_val);
}
if (fuelcell_discharged.size() != nyears + 1)
throw exec_error("hostdeveloper", util::format("fuelcell_discharged size (%d) incorrect", (int)fuelcell_discharged.size()));

// battery cost - replacement from lifetime analysis
if ((as_integer("en_batt") == 1 || as_integer("en_standalone_batt") == 1) && (as_integer("batt_replacement_option") > 0))
{
ssc_number_t* batt_rep = 0;
std::vector<ssc_number_t> replacement_percent;

// replace at capacity percent
if (as_integer("batt_replacement_option") == 1) {
batt_rep = as_array("batt_bank_replacement", &count); // replacements per year calculated

for (i = 0; i < (int)count; i++) {
replacement_percent.push_back(100);
// replace at capacity percent
if (as_integer("batt_replacement_option") == 1) {

for (i = 0; i < (int)count; i++) {
replacement_percent.push_back(100);
}
}
}
else {// user specified
replacement_percent = as_vector_ssc_number_t("batt_replacement_schedule_percent");
}
double batt_cap = as_double("batt_computed_bank_capacity");
// updated 10/17/15 per 10/14/15 meeting
escal_or_annual(CF_battery_replacement_cost_schedule, nyears, "om_batt_replacement_cost", inflation_rate, batt_cap, false, as_double("om_replacement_cost_escal") * 0.01);
else {// user specified
replacement_percent = as_vector_ssc_number_t("batt_replacement_schedule_percent");
}
double batt_cap = as_double("batt_computed_bank_capacity");
// updated 10/17/15 per 10/14/15 meeting
escal_or_annual(CF_battery_replacement_cost_schedule, nyears, "om_batt_replacement_cost", inflation_rate, batt_cap, false, as_double("om_replacement_cost_escal") * 0.01);

for (i = 0; i < nyears && i < (int)count; i++) {
cf.at(CF_battery_replacement_cost, i + 1) = batt_rep[i] * replacement_percent[i] * 0.01 *
cf.at(CF_battery_replacement_cost_schedule, i + 1);
for (i = 0; i < nyears && i < (int)count; i++) {
cf.at(CF_battery_replacement_cost, i + 1) = batt_rep[i] * replacement_percent[i] * 0.01 *
cf.at(CF_battery_replacement_cost_schedule, i + 1);
}
}
}

// initialize energy and revenue
// initialize energy
// differs from samsim - accumulate hourly energy
Expand Down
24 changes: 22 additions & 2 deletions ssc/cmod_hybrid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,16 @@ setmodules( ['pvwattsv8', 'fuelcell', 'battery', 'grid', 'utilityrate5', 'therma
ssc_data_set_array(static_cast<ssc_data_t>(&input), "gen", pGen, (int)genLength); // check if issue with hourly PV and subhourly wind
//ssc_data_set_number(static_cast<ssc_data_t>(&input), "is_hybrid", 1);

ssc_module_exec(module, static_cast<ssc_data_t>(&input));
if (!ssc_module_exec(module, static_cast<ssc_data_t>(&input))) {
// merge in hybrid vartable for configurations where battery and fuel cell dispatch are combined and not in the technology bin
std::string hybridVarTable("Hybrid");
var_data* hybrid_inputs = input_table->table.lookup(hybridVarTable);// TODO - better naming of combined vartable?
if (compute_module_inputs->type != SSC_TABLE)
throw exec_error("hybrid", "No input input_table found for ." + hybridVarTable);
var_table& hybridinput = hybrid_inputs->table;
input.merge(hybridinput, false);
ssc_module_exec(module, static_cast<ssc_data_t>(&input));
}

ssc_data_t compute_module_outputs = ssc_data_create();

Expand Down Expand Up @@ -404,7 +413,18 @@ setmodules( ['pvwattsv8', 'fuelcell', 'battery', 'grid', 'utilityrate5', 'therma
//ssc_data_set_number(static_cast<ssc_data_t>(&input), "is_hybrid", 1);

//ssc_module_exec_with_handler(module, static_cast<ssc_data_t>(&input),default_internal_handler, m_handler); // handler not passed into compute module
ssc_module_exec(module, static_cast<ssc_data_t>(&input));
if (!ssc_module_exec(module, static_cast<ssc_data_t>(&input))) {
// merge in hybrid vartable for configurations where battery and fuel cell dispatch are combined and not in the technology bin
std::string hybridVarTable("Hybrid");
var_data* hybrid_inputs = input_table->table.lookup(hybridVarTable);// TODO - better naming of combined vartable?
if (compute_module_inputs->type != SSC_TABLE)
throw exec_error("hybrid", "No input input_table found for ." + hybridVarTable);
var_table& hybridinput = hybrid_inputs->table;
input.merge(hybridinput, false);
ssc_data_set_number(static_cast<ssc_data_t>(&input), "en_batt", 1); // should be done at UI level

ssc_module_exec(module, static_cast<ssc_data_t>(&input));
}

ssc_data_t compute_module_outputs = ssc_data_create();

Expand Down
Loading

0 comments on commit efa710c

Please sign in to comment.