Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 35 additions & 28 deletions ssc/cmod_hybrid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,6 @@ class cm_hybrid : public compute_module

ssc_module_exec_with_error(module, input, compute_module);

ssc_number_t system_capacity = compute_module_inputs->table.lookup("system_capacity")->num;
hybridSystemCapacity += system_capacity;
hybridTotalInstalledCost += compute_module_inputs->table.lookup("total_installed_cost")->num;

ssc_data_t compute_module_outputs = ssc_data_create();

int pidx = 0;
Expand All @@ -174,6 +170,13 @@ class cm_hybrid : public compute_module
if (compute_module_inputs->table.lookup("system_use_lifetime_output"))
system_use_lifetime_output = compute_module_inputs->table.lookup("system_use_lifetime_output")->num;

ssc_number_t system_capacity = compute_module_inputs->table.lookup("system_capacity")->num;
if ((compute_module == "pvsamv1") || (compute_module == "pvwattsv8")) {
ssc_data_get_number(compute_module_outputs, "system_capacity_ac", &system_capacity);
}
hybridSystemCapacity += system_capacity;
hybridTotalInstalledCost += compute_module_inputs->table.lookup("total_installed_cost")->num;

// get minimum timestep from gen vector
ssc_number_t* curGen = ssc_data_get_array(compute_module_outputs, "gen", &len);
currentTimeStepsPerHour = len / 8760;
Expand Down Expand Up @@ -287,26 +290,6 @@ class cm_hybrid : public compute_module
}
}

// monthly energy generated
size_t step_per_hour = maximumTimeStepsPerHour;
ssc_number_t* pGenMonthly = ((var_table*)outputs)->allocate("monthly_energy", 12);
size_t c = 0;
for (int m = 0; m < 12; m++) // each month
{
pGenMonthly[m] = 0;
for (size_t d = 0; d < util::nday[m]; d++) // for each day in each month
for (int h = 0; h < 24; h++) // for each hour in each day
for (size_t j = 0; j < step_per_hour; j++)
pGenMonthly[m] += pGen[c++];
}

ssc_number_t pGenAnnual = 0;
for (size_t i = 0; i < genLength; i++)
pGenAnnual += pGen[i];
((var_table*)outputs)->assign("annual_energy", var_data(pGenAnnual));



if (fuelcells.size() > 0) { // run single fuel cell if present

percent = 100.0f * ((float)(generators.size() + fuelcells.size()) / (float)(generators.size() + fuelcells.size() + batteries.size() + financials.size()));
Expand Down Expand Up @@ -564,8 +547,35 @@ class cm_hybrid : public compute_module
if (batteries.size() > 0) {
use_batt_output = true;
pBattGen = ((var_table*)outputs)->lookup(batteries[0])->table.as_array("gen", &battGenLen);
if (battGenLen == genLength) {
for (size_t g = 0; g < genLength; g++) {
// Batt's gen is an inout that includes other system components
pGen[g] = pBattGen[g];
}
}
else {
throw exec_error("hybrid", util::format("Battery gen length incorrect, battery timeseries contains %d entries, generators contain %d", battGenLen, genLength));
}
}

// monthly energy generated
size_t step_per_hour = maximumTimeStepsPerHour;
ssc_number_t* pGenMonthly = ((var_table*)outputs)->allocate("monthly_energy", 12);
size_t c = 0;
for (int m = 0; m < 12; m++) // each month
{
pGenMonthly[m] = 0;
for (size_t d = 0; d < util::nday[m]; d++) // for each day in each month
for (int h = 0; h < 24; h++) // for each hour in each day
for (size_t j = 0; j < step_per_hour; j++)
pGenMonthly[m] += pGen[c++];
}

ssc_number_t pGenAnnual = 0;
for (size_t i = 0; i < genLength; i++)
pGenAnnual += pGen[i];
((var_table*)outputs)->assign("annual_energy", var_data(pGenAnnual));

ssc_number_t* pHybridOMSum = ((var_table*)outputs)->allocate("cf_hybrid_om_sum", analysisPeriod + 1); // add to top level "output" - assumes analysis period the same for all generators

for (int i = 0; i <= analysisPeriod; i++)
Expand Down Expand Up @@ -636,10 +646,7 @@ class cm_hybrid : public compute_module
var_data* compute_module_inputs = input_table->table.lookup(hybridVarTable);
var_table& input = compute_module_inputs->table;

// if (use_batt_output)
// ssc_data_set_array(static_cast<ssc_data_t>(&input), "gen", pBattGen, (int)battGenLen);
// else
ssc_data_set_array(static_cast<ssc_data_t>(&input), "gen", pGen, (int)genLength);
ssc_data_set_array(static_cast<ssc_data_t>(&input), "gen", pGen, (int)genLength);

if (batteries.size() > 0)
ssc_data_set_number(static_cast<ssc_data_t>(&input), "is_hybrid", 1); // for updating battery outputs to annual length in update_battery_outputs in common_financial.cpp
Expand Down
3 changes: 3 additions & 0 deletions ssc/cmod_pvsamv1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,8 @@ static var_info _cm_vtab_pvsamv1[] = {
//miscellaneous outputs
{ SSC_OUTPUT, SSC_NUMBER, "ts_shift_hours", "Sun position time offset", "hours", "", "Miscellaneous", "", "", "" },
{ SSC_OUTPUT, SSC_NUMBER, "nameplate_dc_rating", "System nameplate DC rating", "kW", "", "Miscellaneous", "*", "", "" },
{ SSC_OUTPUT, SSC_NUMBER, "system_capacity_ac", "System nameplate AC rating", "kWac", "", "Miscellaneous", "", "", "" },



// test outputs
Expand Down Expand Up @@ -3594,6 +3596,7 @@ void cm_pvsamv1::exec()
kWhACperkWAC = annual_energy / nameplate_ac_kW;
}
assign("capacity_factor_ac", var_data((ssc_number_t)(kWhACperkWAC / 87.6)));
assign("system_capacity_ac", var_data((ssc_number_t)nameplate_ac_kW));

if (is_assigned("load"))
{
Expand Down
2 changes: 2 additions & 0 deletions ssc/cmod_pvwattsv8.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ static var_info _cm_vtab_pvwattsv8[] = {

{ SSC_OUTPUT, SSC_NUMBER, "ts_shift_hours", "Time offset for interpreting time series outputs", "hours","", "Miscellaneous", "*", "", "" },
{ SSC_OUTPUT, SSC_NUMBER, "percent_complete", "Estimated percent of total completed simulation", "%", "", "Miscellaneous", "", "", "" },
{ SSC_OUTPUT, SSC_NUMBER, "system_capacity_ac", "System nameplate AC rating", "kWac", "", "Miscellaneous", "", "", "" },

var_info_invalid };

Expand Down Expand Up @@ -1442,6 +1443,7 @@ class cm_pvwattsv8 : public compute_module

// for battery model, specify a number of inverters
assign("inverter_efficiency", var_data((ssc_number_t)(as_double("inv_eff"))));
assign("system_capacity_ac", var_data((ssc_number_t)pv.ac_nameplate / 1000.0));

if (en_snowloss && snowmodel.badValues > 0)
log(util::format("The snow model has detected %d bad snow depth values (less than 0 or greater than 610 cm). These values have been set to zero.", snowmodel.badValues), SSC_WARNING);
Expand Down
67 changes: 53 additions & 14 deletions test/ssc_test/cmod_hybrid_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ TEST_F(CmodHybridTest, PVWattsv8WindBatterySingleOwner) {
{
int len;

ssc_number_t pvannualenergy, windannualenergy, battannualenergy, npv;
ssc_number_t pvannualenergy, windannualenergy, battannualenergy, npv, total_energy;
ssc_number_t* battchargeenergy;
ssc_number_t* battdischargeenergy;
auto outputs = ssc_data_get_table(dat, "output");
auto inputs = ssc_data_get_table(dat, "input");

Expand All @@ -84,21 +86,27 @@ TEST_F(CmodHybridTest, PVWattsv8WindBatterySingleOwner) {
auto batt_outputs = ssc_data_get_table(outputs, "battery");
auto batt_inputs = ssc_data_get_table(inputs, "battery");
ssc_data_get_number(batt_outputs, "annual_energy", &battannualenergy);
battchargeenergy = ssc_data_get_array(batt_outputs, "batt_annual_charge_energy", &len);
battdischargeenergy = ssc_data_get_array(batt_outputs, "batt_annual_discharge_energy", &len);
EXPECT_NEAR(battannualenergy, 570565000, 570565000 * 0.01);
EXPECT_NEAR(battchargeenergy[1], 81116970, 81116970 * 0.001);
EXPECT_NEAR(battdischargeenergy[1], 72917429, 72917429 * 0.001);

auto hybrid_outputs = ssc_data_get_table(outputs, "Hybrid");

ssc_number_t value;
ssc_data_get_number(hybrid_outputs, "annual_energy", &total_energy);

auto ebitda = ssc_data_get_array(hybrid_outputs, "cf_ebitda", &len);
auto revenue = ssc_data_get_array(hybrid_outputs, "cf_total_revenue", &len);
auto om_expenses = ssc_data_get_array(hybrid_outputs, "cf_operating_expenses", &len);
ssc_data_get_number(hybrid_outputs, "project_return_aftertax_npv", &npv);

EXPECT_NEAR(om_expenses[1], 10772001, 1);
EXPECT_NEAR(revenue[1], 31651347, 1);
EXPECT_NEAR(ebitda[1], 20879346, 1);
EXPECT_NEAR(npv, -246312045, 246312045 * 0.001);
EXPECT_NEAR(om_expenses[1], 10425847, 1);
EXPECT_NEAR(revenue[1], 33062516, 1);
EXPECT_NEAR(ebitda[1], 22636669, 1);
EXPECT_NEAR(npv, -227222606, 227222606 * 0.001);

EXPECT_NEAR(total_energy, battannualenergy, total_energy * 0.001);
EXPECT_NEAR(total_energy, pvannualenergy + windannualenergy - battchargeenergy[1] + battdischargeenergy[1], total_energy * 0.001);
}
ssc_data_free(dat);
dat = nullptr;
Expand Down Expand Up @@ -134,7 +142,10 @@ TEST_F(CmodHybridTest, PVWattsv8WindBatteryHostDeveloper) {
EXPECT_FALSE(errors);
if (!errors)
{
ssc_number_t pvannualenergy, windannualenergy, npv;
int len;
ssc_number_t pvannualenergy, windannualenergy, battannualenergy, npv, total_energy;
ssc_number_t* battchargeenergy;
ssc_number_t* battdischargeenergy;
auto outputs = ssc_data_get_table(dat, "output");

auto pv_outputs = ssc_data_get_table(outputs, "pvwattsv8");
Expand All @@ -145,9 +156,23 @@ TEST_F(CmodHybridTest, PVWattsv8WindBatteryHostDeveloper) {
ssc_data_get_number(wind_outputs, "annual_energy", &windannualenergy);
EXPECT_NEAR(windannualenergy, 187767, 187767 * 0.01);

//,
auto battery_outputs = ssc_data_get_table(outputs, "battery");
ssc_data_get_number(battery_outputs, "annual_energy", &battannualenergy);
battchargeenergy = ssc_data_get_array(battery_outputs, "batt_annual_charge_energy", &len);
battdischargeenergy = ssc_data_get_array(battery_outputs, "batt_annual_discharge_energy", &len);
EXPECT_NEAR(battannualenergy, 1118877, 1118877 * 0.01);
EXPECT_NEAR(battchargeenergy[1], 83565, 83565 * 0.001);
EXPECT_NEAR(battdischargeenergy[1], 76334, 76334 * 0.001);

auto hybrid_outputs = ssc_data_get_table(outputs, "Hybrid");
ssc_data_get_number(hybrid_outputs, "project_return_aftertax_npv", &npv);
EXPECT_NEAR(npv, -182666, 182665 * 0.001);
EXPECT_NEAR(npv, -168769, 168769 * 0.001);

ssc_data_get_number(hybrid_outputs, "annual_energy", &total_energy);

EXPECT_NEAR(total_energy, battannualenergy, total_energy * 0.001);
EXPECT_NEAR(total_energy, pvannualenergy + windannualenergy - battchargeenergy[1] + battdischargeenergy[1], total_energy * 0.001);
}
ssc_data_free(dat);
dat = nullptr;
Expand Down Expand Up @@ -179,7 +204,10 @@ TEST_F(CmodHybridTest, CustomGenerationPVWattsWindFuelCellBatteryHybrid_SingleOw
EXPECT_FALSE(errors);
if (!errors)
{
ssc_number_t genericannualenergy, pvannualenergy, windannualenergy, battannualenergy, npv;
ssc_number_t genericannualenergy, pvannualenergy, windannualenergy, battannualenergy, fuelcellannualenergy, npv, total_energy;
ssc_number_t* battchargeenergy;
ssc_number_t* battdischargeenergy;

int len;
auto outputs = ssc_data_get_table(dat, "output");

Expand All @@ -195,13 +223,19 @@ TEST_F(CmodHybridTest, CustomGenerationPVWattsWindFuelCellBatteryHybrid_SingleOw
ssc_data_get_number(wind_outputs, "annual_energy", &windannualenergy);
auto wind_om_expenses = ssc_data_get_array(wind_outputs, "cf_operating_expenses", &len);

auto fuelcell_outputs = ssc_data_get_table(outputs, "fuelcell");
ssc_data_get_number(fuelcell_outputs, "annual_energy_discharged", &fuelcellannualenergy);

auto batt_outputs = ssc_data_get_table(outputs, "battery");
ssc_data_get_number(batt_outputs, "annual_energy", &battannualenergy);
auto batt_om_expenses = ssc_data_get_array(batt_outputs, "cf_operating_expenses", &len);
battchargeenergy = ssc_data_get_array(batt_outputs, "batt_annual_charge_energy", &len);
battdischargeenergy = ssc_data_get_array(batt_outputs, "batt_annual_discharge_energy", &len);

auto hybrid_outputs = ssc_data_get_table(outputs, "Hybrid");
ssc_data_get_number(hybrid_outputs, "project_return_aftertax_npv", &npv);
auto ebitda = ssc_data_get_array(hybrid_outputs, "cf_ebitda", &len);
ssc_data_get_number(hybrid_outputs, "annual_energy", &total_energy);

auto revenue = ssc_data_get_array(hybrid_outputs, "cf_total_revenue", &len);
auto om_expenses = ssc_data_get_array(hybrid_outputs, "cf_operating_expenses", &len);
Expand All @@ -210,12 +244,17 @@ TEST_F(CmodHybridTest, CustomGenerationPVWattsWindFuelCellBatteryHybrid_SingleOw
EXPECT_NEAR(genericannualenergy, 756864000., 1e6);
EXPECT_NEAR(pvannualenergy, 211907456., 1e6);
EXPECT_NEAR(windannualenergy, 366975552., 1e6);
EXPECT_NEAR(fuelcellannualenergy, 1561993, 1e6);
EXPECT_NEAR(battannualenergy, 1331720000., 1e6);
EXPECT_NEAR(battchargeenergy[1], 55372248, 55372248 * 0.001);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good and thanks for adding the additional outputs in the test!

EXPECT_NEAR(battdischargeenergy[1], 49818321, 49818321 * 0.001);
EXPECT_NEAR(total_energy, battannualenergy, total_energy * 0.001);
EXPECT_NEAR(total_energy, pvannualenergy + windannualenergy + genericannualenergy + fuelcellannualenergy - battchargeenergy[1] + battdischargeenergy[1], total_energy * 0.001);

EXPECT_NEAR(om_expenses[1], 90570832., 1e5);
EXPECT_NEAR(revenue[1], 66865452., 1e5);
EXPECT_NEAR(ebitda[1], -23705384., 1e5);
EXPECT_NEAR(npv, -1754023822., 1e6);
EXPECT_NEAR(om_expenses[1], 90224679., 1e5);
EXPECT_NEAR(revenue[1], 66590988., 1e5);
EXPECT_NEAR(ebitda[1], -23633690., 1e5);
EXPECT_NEAR(npv, -1750593259., 1e6);
}
ssc_data_free(dat);
dat = nullptr;
Expand Down
Loading