Skip to content

Commit

Permalink
Pass number of inverters to calculation, update clipping loss ac calcs
Browse files Browse the repository at this point in the history
  • Loading branch information
mjprilliman committed Aug 16, 2023
1 parent 894fde2 commit d35d4f0
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 16 deletions.
2 changes: 1 addition & 1 deletion shared/lib_pv_io_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,7 @@ PVSystem_IO::PVSystem_IO(compute_module* cm, std::string cmName, Simulation_IO*
enableSnowModel = cm->as_boolean("en_snow_model");

// The shared inverter of the PV array and a tightly-coupled DC connected battery
std::unique_ptr<SharedInverter> tmpSharedInverter(new SharedInverter(Inverter->inverterType, numberOfInverters, &Inverter->sandiaInverter, &Inverter->partloadInverter, &Inverter->ondInverter));
std::unique_ptr<SharedInverter> tmpSharedInverter(new SharedInverter(Inverter->inverterType, numberOfInverters, &Inverter->sandiaInverter, &Inverter->partloadInverter, &Inverter->ondInverter, numberOfInvertersClipping));
m_sharedInverter = std::move(tmpSharedInverter);

// Register shared inverter with inverter_IO
Expand Down
60 changes: 56 additions & 4 deletions shared/lib_shared_inverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,11 +266,11 @@ void SharedInverter::calculateACPower(const double powerDC_kW_in, const double D
if (m_tempEnabled) {
calculateTempDerate(DCStringVoltage, tempC, powerDC_Watts, power_ratio, tempLoss);
}

/*
if (m_numInvertersClipping > 0) {
m_sandiaInverter->acpower(std::abs(powerDC_Watts) / m_numInvertersClipping, DCStringVoltage, &powerAC_Watts_clipping, &P_par_clipping, &P_lr_clipping, &efficiencyAC, &powerClipLoss_kW, &powerConsumptionLoss_kW, &powerNightLoss_kW);
}
}*/

if (m_inverterType == SANDIA_INVERTER || m_inverterType == DATASHEET_INVERTER || m_inverterType == COEFFICIENT_GENERATOR)
m_sandiaInverter->acpower(std::abs(powerDC_Watts) / m_numInverters, DCStringVoltage, &powerAC_Watts, &P_par, &P_lr, &efficiencyAC, &powerClipLoss_kW, &powerConsumptionLoss_kW, &powerNightLoss_kW);
Expand All @@ -285,19 +285,71 @@ void SharedInverter::calculateACPower(const double powerDC_kW_in, const double D
efficiencyAC = NONE_INVERTER_EFF;
powerAC_Watts = powerDC_Watts * efficiencyAC;
}

/*
if (m_subhourlyClippingEnabled == 1) {
powerAC_kW_clipping = powerAC_Watts * m_numInverters * util::watt_to_kilowatt;
return;
}*/

// Convert units to kW- no need to scale to system size because passed in as power to total number of inverters
powerDC_kW = powerDC_Watts * util::watt_to_kilowatt;
convertOutputsToKWandScale(tempLoss, powerAC_Watts);
powerAC_kW_clipping = powerAC_Watts / 1000.0;

// In event shared inverter is charging a battery only, need to re-convert to negative power
if (negativePower) {
powerAC_kW = -1.0 * std::abs(powerAC_kW);
}
}

void SharedInverter::calculateACPower(const double powerDC_kW_in, const double DCStringVoltage, double tempC, bool clippingEnabled)
{
double P_par, P_lr;
double P_par_clipping, P_lr_clipping;
bool negativePower = powerDC_kW_in < 0 ? true : false;


dcWiringLoss_ond_kW = 0.0;
acWiringLoss_ond_kW = 0.0;

// Power quantities go in and come out in units of W
double powerDC_Watts = powerDC_kW_in * util::kilowatt_to_watt;
double powerAC_Watts = 0.0;
double powerAC_Watts_clipping = 0.0;
Tdry_C = tempC;
StringV = DCStringVoltage;
double tempLoss = 0.0;
double power_ratio = 1.0;
if (m_tempEnabled) {
calculateTempDerate(DCStringVoltage, tempC, powerDC_Watts, power_ratio, tempLoss);
}

if (clippingEnabled) {
m_sandiaInverter->acpower(std::abs(powerDC_Watts) / m_numInvertersClipping, DCStringVoltage, &powerAC_Watts_clipping, &P_par_clipping, &P_lr_clipping, &efficiencyAC, &powerClipLoss_kW, &powerConsumptionLoss_kW, &powerNightLoss_kW);

}
/*
if (m_inverterType == SANDIA_INVERTER || m_inverterType == DATASHEET_INVERTER || m_inverterType == COEFFICIENT_GENERATOR)
m_sandiaInverter->acpower(std::abs(powerDC_Watts) / m_numInverters, DCStringVoltage, &powerAC_Watts, &P_par, &P_lr, &efficiencyAC, &powerClipLoss_kW, &powerConsumptionLoss_kW, &powerNightLoss_kW);
else if (m_inverterType == PARTLOAD_INVERTER)
m_partloadInverter->acpower(std::abs(powerDC_Watts) / m_numInverters, &powerAC_Watts, &P_lr, &P_par, &efficiencyAC, &powerClipLoss_kW, &powerNightLoss_kW);
else if (m_inverterType == OND_INVERTER)
m_ondInverter->acpower(std::abs(powerDC_Watts) / m_numInverters, DCStringVoltage, tempC, &powerAC_Watts, &P_par, &P_lr, &efficiencyAC, &powerClipLoss_kW, &powerConsumptionLoss_kW, &powerNightLoss_kW, &dcWiringLoss_ond_kW, &acWiringLoss_ond_kW);
else if (m_inverterType == NONE) {
powerClipLoss_kW = 0.;
powerConsumptionLoss_kW = 0.;
powerNightLoss_kW = 0.;
efficiencyAC = NONE_INVERTER_EFF;
powerAC_Watts = powerDC_Watts * efficiencyAC;
}
*/

if (clippingEnabled) {
m_subhourlyClippingEnabled = true;
powerAC_kW_clipping = powerAC_Watts_clipping * m_numInvertersClipping * util::watt_to_kilowatt;
return;
}
}

/* This function takes input inverter DC power (kW) per MPPT input for a SINGLE multi-mppt inverter, DC voltage (V) per input, and ambient temperature (deg C), and calculates output for the total number of inverters in the system */
void SharedInverter::calculateACPower(const std::vector<double> powerDC_kW_in, const std::vector<double> DCStringVoltage, double tempC)
{
Expand Down
2 changes: 2 additions & 0 deletions shared/lib_shared_inverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ class SharedInverter
/// Given the combined PV plus battery DC power (kW), voltage and ambient T, compute the AC power (kW) for a single inverter with one MPPT input
void calculateACPower(const double powerDC_kW, const double DCStringVoltage, double tempC);

void calculateACPower(const double powerDC_kW, const double DCStringVoltage, double tempC, bool clippingEnabled);

/// Given the combined PV plus battery DC power (kW), voltage and ambient T, compute the AC power (kW) for a single inverter with multiple MPPT inputs
void calculateACPower(const std::vector<double> powerDC_kW, const std::vector<double> DCStringVoltage, double tempC);

Expand Down
71 changes: 60 additions & 11 deletions ssc/cmod_pvsamv1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1904,7 +1904,12 @@ void cm_pvsamv1::exec()
PVSystem->p_poaBeamFrontCS[nn][idx] = (ssc_number_t)ibeam_csky;
PVSystem->p_poaDiffuseFrontCS[nn][idx] = (ssc_number_t)(iskydiff_csky);
PVSystem->p_poaDiffuseFrontCS[nn][idx] = (ssc_number_t)(ignddiff_csky);
PVSystem->p_DNIIndex[nn][idx] = (ssc_number_t)(ibeam / ibeam_csky);
if (ibeam_csky != 0) {
PVSystem->p_DNIIndex[nn][idx] = (ssc_number_t)(ibeam / ibeam_csky);
}
else {
PVSystem->p_DNIIndex[nn][idx] = 0;
}

// only save first-year spatial outputs
if (iyear == 0) {
Expand Down Expand Up @@ -2396,6 +2401,9 @@ void cm_pvsamv1::exec()
//assign net DC power output
PVSystem->p_systemDCPower[idx] += (ssc_number_t)(dcPowerNetPerSubarray[nn] * util::watt_to_kilowatt);

//Clearsky DC Power
PVSystem->p_systemDCPowerCS[idx] += (ssc_number_t)(Subarrays[nn]->dcPowerSubarrayCS * util::watt_to_kilowatt);

//add this subarray's net DC power to the appropriate MPPT input and to the total system DC power
PVSystem->p_dcPowerNetPerMppt[Subarrays[nn]->mpptInput - 1][idx] += (ssc_number_t)(dcPowerNetPerSubarray[nn]); //need to subtract 1 from mppt input number because those are 1-indexed
dcPowerNetTotalSystem += dcPowerNetPerSubarray[nn];
Expand Down Expand Up @@ -2509,6 +2517,49 @@ void cm_pvsamv1::exec()
wdprov->rewind();

double annual_dc_loss_ond = 0, annual_ac_loss_ond = 0; // (TR)
double annual_subhourly_clipping_loss = 0;

if (as_boolean("enable_subhourly_clipping")) {
for (size_t inrec = 0; inrec < nrec; inrec++) {
idx = inrec;
double dcPower_kW_csky = PVSystem->p_systemDCPowerCS[idx];
//Calculate DNI clearness index (time step basis)
double dni_clearness_index = PVSystem->p_DNIIndex[0][idx];
//Calculate Clipping Potential ((P_dc,dryclean - P_ac,0) / P_ac,0) (time step basis)
sharedInverter->calculateACPower(dcPower_kW_csky, dcVoltagePerMppt[0], Irradiance->weatherRecord.tdry, as_boolean("enable_subhourly_clipping")); //DC batteries not allowed with multiple MPPT, so can just use MPPT 1's voltage
double clip_pot = (dcPower_kW_csky - sharedInverter->powerAC_kW_clipping) / sharedInverter->powerAC_kW_clipping;
//Lookup matrix for percentage effect based on DNI index, Clipping potential //Lookup bias error in matrix (unitless) [CP, DNI]
util::matrix_t<double> sub_clipping_matrix = as_matrix("subhourly_clipping_matrix");
size_t nrows = sub_clipping_matrix.nrows();
size_t ncols = sub_clipping_matrix.ncols();
size_t dni_row = 0;
size_t clip_pot_col = 0;
double clip_correction = 0;
if (dni_clearness_index < sub_clipping_matrix.at(1, 0)) dni_row = 1;
else if (dni_clearness_index > sub_clipping_matrix.at(nrows - 1, 0)) dni_row = nrows - 1;
else {
for (size_t r = 1; r < nrows; r++) {
if (dni_clearness_index > sub_clipping_matrix.at(r, 0) && dni_clearness_index < sub_clipping_matrix.at(r + 1, 0)) {
dni_row = r;
}
}
}

//Clipping potential indexing
if (clip_pot < sub_clipping_matrix.at(0, 1)) clip_pot_col = 1;
else if (clip_pot > sub_clipping_matrix.at(0, ncols - 1)) clip_pot_col = ncols - 1;
else {
for (size_t c = 1; c < ncols; c++) {
if (clip_pot > sub_clipping_matrix.at(0, c) && clip_pot < sub_clipping_matrix.at(0, c + 1)) {
clip_pot_col = c;
}
}
}

//acpwr_gross *= (1 - sub_clipping_matrix.at(dni_row, clip_pot_col));
annual_subhourly_clipping_loss += sub_clipping_matrix.at(dni_row, clip_pot_col);
}
}

for (size_t iyear = 0; iyear < nyears; iyear++)
{
Expand Down Expand Up @@ -2548,6 +2599,7 @@ void cm_pvsamv1::exec()
}

double acpwr_gross = 0, ac_wiringloss = 0, transmissionloss = 0;
double ac_subhourlyclipping_loss = 0;
cur_load = p_load_full[idx];

//set DC voltages for use in AC power calculation
Expand All @@ -2569,11 +2621,6 @@ void cm_pvsamv1::exec()
}
}

if (as_boolean("enable_subhourly_clipping")) {
sharedInverter->calculateACPower(dcPower_kW_csky, dcVoltagePerMppt[0], Irradiance->weatherRecord.tdry); //DC batteries not allowed with multiple MPPT, so can just use MPPT 1's voltage

}

//run AC power calculation
if (en_batt && (batt_topology == ChargeController::DC_CONNECTED)) // DC-connected battery
{
Expand Down Expand Up @@ -2625,12 +2672,12 @@ void cm_pvsamv1::exec()
sharedInverter->calculateACPower(dcPowerNetPerMppt_kW, dcVoltagePerMppt, Irradiance->weatherRecord.tdry);
acpwr_gross = sharedInverter->powerAC_kW;
}

/*
if (as_boolean("enable_subhourly_clipping")) {
//Calculate DNI clearness index (time step basis)
double dni_clearness_index = PVSystem->p_DNIIndex[0][idx];
//Calculate Clipping Potential ((P_dc,dryclean - P_ac,0) / P_ac,0) (time step basis)
sharedInverter->calculateACPower(dcPower_kW_csky, dcVoltagePerMppt[0], Irradiance->weatherRecord.tdry); //DC batteries not allowed with multiple MPPT, so can just use MPPT 1's voltage
sharedInverter->calculateACPower(dcPower_kW_csky, dcVoltagePerMppt[0], Irradiance->weatherRecord.tdry, as_boolean("enable_subhourly_clipping")); //DC batteries not allowed with multiple MPPT, so can just use MPPT 1's voltage
double clip_pot = (dcPower_kW_csky - sharedInverter->powerAC_kW_clipping) / sharedInverter->powerAC_kW_clipping;
//Lookup matrix for percentage effect based on DNI index, Clipping potential //Lookup bias error in matrix (unitless) [CP, DNI]
util::matrix_t<double> sub_clipping_matrix = as_matrix("subhourly_clipping_matrix");
Expand Down Expand Up @@ -2660,11 +2707,13 @@ void cm_pvsamv1::exec()
}
}

acpwr_gross *= (1 - sub_clipping_matrix.at(dni_row, clip_pot_col));
//acpwr_gross *= (1 - sub_clipping_matrix.at(dni_row, clip_pot_col));
annual_subhourly_clipping_loss += sub_clipping_matrix.at(dni_row, clip_pot_col);
}

*/

ac_wiringloss = std::abs(acpwr_gross) * PVSystem->acLossPercent * 0.01;
ac_subhourlyclipping_loss = std::abs(acpwr_gross) * annual_subhourly_clipping_loss;

// accumulate first year annual energy
if (iyear == 0)
Expand Down Expand Up @@ -2702,7 +2751,7 @@ void cm_pvsamv1::exec()
PVSystem->p_systemDCPower[idx] = (ssc_number_t)(sharedInverter->powerDC_kW);

//ac losses should always be subtracted, this means you can't just multiply by the derate because at nighttime it will add power
PVSystem->p_systemACPower[idx] = (ssc_number_t)(acpwr_gross - ac_wiringloss);
PVSystem->p_systemACPower[idx] = (ssc_number_t)(acpwr_gross - ac_wiringloss - ac_subhourlyclipping_loss);
// AC connected batteries will set this laster
if (en_batt && (batt_topology == ChargeController::DC_CONNECTED)) {
batt->outGenWithoutBattery[idx] -= std::abs(batt->outGenWithoutBattery[idx]) * PVSystem->acLossPercent * 0.01;;
Expand Down

0 comments on commit d35d4f0

Please sign in to comment.