diff --git a/shared/lib_irradproc.cpp b/shared/lib_irradproc.cpp index c2357da26..35db1295a 100644 --- a/shared/lib_irradproc.cpp +++ b/shared/lib_irradproc.cpp @@ -1751,6 +1751,7 @@ void irrad::setup() { poaRearDirectDiffuse = 0.; poaRearRowReflections = 0.; poaRearSelfShaded = 0.; + } irrad::irrad() { @@ -1765,7 +1766,7 @@ irrad::irrad(weather_record wf, weather_header hdr, double groundCoverageRatioIn, double slopeTiltIn, double slopeAzmIn, std::vector monthlyTiltDegrees, std::vector userSpecifiedAlbedo, poaDecompReq *poaAllIn, - bool useSpatialAlbedos, const util::matrix_t* userSpecifiedSpatialAlbedos) : + bool useSpatialAlbedos, const util::matrix_t* userSpecifiedSpatialAlbedos, bool enableSubhourlyClipping) : skyModel(skyModelIn), radiationMode(radiationModeIn), trackingMode(trackModeIn), enableBacktrack(backtrackingEnabled), forceToStow(forceToStowIn), delt(dtHour), tiltDegrees(tiltDegreesIn), surfaceAzimuthDegrees(azimuthDegreesIn), @@ -1792,6 +1793,8 @@ irrad::irrad(weather_record wf, weather_header hdr, set_optional(hdr.elev, wf.pres, wf.tdry); set_sky_model(skyModel, albedo, albedoSpatial); + set_subhourly_clipping(enableSubhourlyClipping); + if (radiationMode == irrad::DN_DF) set_beam_diffuse(wf.dn, wf.df); else if (radiationMode == irrad::DN_GH) set_global_beam(wf.gh, wf.dn); else if (radiationMode == irrad::GH_DF) set_global_diffuse(wf.gh, wf.df); @@ -1980,6 +1983,11 @@ void irrad::set_optional(double elev, double pres, double t_amb) //defaults of 0 this->tamb = t_amb; } +void irrad::set_subhourly_clipping(bool enable) +{ + if (enable) this->enableSubhourlyClipping = true; +} + void irrad::set_sky_model(int sm, double alb, const std::vector &albSpatial) { this->skyModel = sm; this->albedo = alb; @@ -2151,7 +2159,9 @@ int irrad::calc() { } //clearsky - ineichen(clearskyIrradiance, RTOD * sunAnglesRadians[1], 1.5, 1.0, elevation); + if (enableSubhourlyClipping) { + ineichen(clearskyIrradiance, RTOD * sunAnglesRadians[1], 1.5, 1.0, elevation); + } planeOfArrayIrradianceFront[0] = planeOfArrayIrradianceFront[1] = planeOfArrayIrradianceFront[2] = 0; @@ -2211,35 +2221,48 @@ int irrad::calc() { isotropic(hextra, calculatedDirectNormal, calculatedDiffuseHorizontal, albedo, surfaceAnglesRadians[0], surfaceAnglesRadians[1], sunAnglesRadians[1], planeOfArrayIrradianceFront, diffuseIrradianceFront); - isotropic(hextra, clearskyIrradiance[1], clearskyIrradiance[2], albedo, - surfaceAnglesRadians[0], surfaceAnglesRadians[1], sunAnglesRadians[1], - planeOfArrayIrradianceFrontCS, diffuseIrradianceFrontCS); break; case 1: hdkr(hextra, calculatedDirectNormal, calculatedDiffuseHorizontal, albedo, surfaceAnglesRadians[0], surfaceAnglesRadians[1], sunAnglesRadians[1], planeOfArrayIrradianceFront, diffuseIrradianceFront); - hdkr(hextra, clearskyIrradiance[1], clearskyIrradiance[2], albedo, surfaceAnglesRadians[0], - surfaceAnglesRadians[1], sunAnglesRadians[1], planeOfArrayIrradianceFrontCS, - diffuseIrradianceFrontCS); break; default: perez(hextra, calculatedDirectNormal, calculatedDiffuseHorizontal, albedo, surfaceAnglesRadians[0], surfaceAnglesRadians[1], sunAnglesRadians[1], planeOfArrayIrradianceFront, diffuseIrradianceFront); - perez(hextra, clearskyIrradiance[1], clearskyIrradiance[2], albedo, surfaceAnglesRadians[0], - surfaceAnglesRadians[1], sunAnglesRadians[1], planeOfArrayIrradianceFrontCS, - diffuseIrradianceFrontCS); break; } + + if (enableSubhourlyClipping) { + switch (skyModel) { + case 0: + isotropic(hextra, clearskyIrradiance[1], clearskyIrradiance[2], albedo, + surfaceAnglesRadians[0], surfaceAnglesRadians[1], sunAnglesRadians[1], + planeOfArrayIrradianceFrontCS, diffuseIrradianceFrontCS); + break; + case 1: + hdkr(hextra, clearskyIrradiance[1], clearskyIrradiance[2], albedo, surfaceAnglesRadians[0], + surfaceAnglesRadians[1], sunAnglesRadians[1], planeOfArrayIrradianceFrontCS, + diffuseIrradianceFrontCS); + break; + default: + perez(hextra, clearskyIrradiance[1], clearskyIrradiance[2], albedo, surfaceAnglesRadians[0], + surfaceAnglesRadians[1], sunAnglesRadians[1], planeOfArrayIrradianceFrontCS, + diffuseIrradianceFrontCS); + break; + } + } } else { // Sev 2015/09/11 - perform a POA decomp. int errorcode = poaDecomp(weatherFilePOA, surfaceAnglesRadians, sunAnglesRadians, albedo, poaAll, directNormal, diffuseHorizontal, globalHorizontal, planeOfArrayIrradianceFront, diffuseIrradianceFront); - int errorcode_cs = poaDecomp(weatherFilePOA, surfaceAnglesRadians, sunAnglesRadians, albedo, poaAll, - clearskyIrradiance[1], clearskyIrradiance[2], clearskyIrradiance[0], planeOfArrayIrradianceFrontCS, - diffuseIrradianceFrontCS); + if (enableSubhourlyClipping) { + int errorcode_cs = poaDecomp(weatherFilePOA, surfaceAnglesRadians, sunAnglesRadians, albedo, poaAll, + clearskyIrradiance[1], clearskyIrradiance[2], clearskyIrradiance[0], planeOfArrayIrradianceFrontCS, + diffuseIrradianceFrontCS); + } calculatedDirectNormal = directNormal; calculatedDiffuseHorizontal = diffuseHorizontal; diff --git a/shared/lib_irradproc.h b/shared/lib_irradproc.h index 71c66acdd..864aff360 100644 --- a/shared/lib_irradproc.h +++ b/shared/lib_irradproc.h @@ -988,6 +988,9 @@ class irrad int year, month, day, hour; double minute, delt; + //Enable subhourly clipping correction + bool enableSubhourlyClipping; + // Subarray properties double tiltDegrees; ///< Surface tilt of subarray in degrees double surfaceAzimuthDegrees; ///< Surface azimuth of subarray in degrees @@ -1052,7 +1055,7 @@ class irrad double dtHour, double tiltDegrees, double azimuthDegrees, double trackerRotationLimitDegrees, double stowAngleDegreesIn, double groundCoverageRatio, double slopeTilt, double slopeAzm, std::vector monthlyTiltDegrees, std::vector userSpecifiedAlbedo, poaDecompReq* poaAllIn, - bool useSpatialAlbedos = false, const util::matrix_t* userSpecifiedSpatialAlbedos = nullptr); + bool useSpatialAlbedos = false, const util::matrix_t* userSpecifiedSpatialAlbedos = nullptr, bool enableSubhourlyClipping = false); /// Construct the irrad class with an Irradiance_IO() object and Subarray_IO() object irrad(); @@ -1072,6 +1075,9 @@ class irrad // Set optional parameters for solarpos_spa calculation void set_optional(double elev = 0, double pres = 1013.25, double t_amb = 15); + //Set whether to use subhourly clipping model + void set_subhourly_clipping(bool enable = false); + /// Set the sky model for the irradiance processor, using \link Irradiance_IO::SKYMODEL void set_sky_model(int skymodel, double albedo, const std::vector &albedoSpatial = std::vector()); diff --git a/shared/lib_pv_io_manager.cpp b/shared/lib_pv_io_manager.cpp index 2a82a74ac..6e3494b9b 100644 --- a/shared/lib_pv_io_manager.cpp +++ b/shared/lib_pv_io_manager.cpp @@ -936,6 +936,7 @@ void PVSystem_IO::AllocateOutputs(compute_module* cm) p_systemDCPowerCS = cm->allocate("dc_net_clearsky", numberOfLifetimeRecords); p_subhourlyClippingLoss = cm->allocate("subhourly_clipping_loss", numberOfLifetimeRecords); + p_subhourlyClippingLossFactor = cm->allocate("subhourly_clipping_loss_factor", numberOfLifetimeRecords); if (Simulation->useLifetimeOutput) { diff --git a/shared/lib_pv_io_manager.h b/shared/lib_pv_io_manager.h index 6bd713f8d..812a97763 100644 --- a/shared/lib_pv_io_manager.h +++ b/shared/lib_pv_io_manager.h @@ -419,6 +419,7 @@ struct PVSystem_IO ssc_number_t *p_systemACPower; // kWac ssc_number_t *p_subhourlyClippingLoss; + ssc_number_t* p_subhourlyClippingLossFactor; ssc_number_t* p_ClippingPotential; }; diff --git a/shared/lib_shared_inverter.cpp b/shared/lib_shared_inverter.cpp index 434cc956f..3e73fd40b 100644 --- a/shared/lib_shared_inverter.cpp +++ b/shared/lib_shared_inverter.cpp @@ -59,6 +59,7 @@ SharedInverter::SharedInverter(int inverterType, size_t numberOfInverters, powerDC_kW = 0.; powerAC_kW = 0.; + powerAC_kW_clipping = 0.; efficiencyAC = 96.; powerClipLoss_kW = 0.; powerConsumptionLoss_kW = 0.; @@ -84,6 +85,7 @@ SharedInverter::SharedInverter(const SharedInverter& orig) { powerDC_kW = orig.powerDC_kW; powerAC_kW = orig.powerAC_kW; + powerAC_kW_clipping = orig.powerAC_kW_clipping; powerClipLoss_kW = orig.powerClipLoss_kW; powerConsumptionLoss_kW = orig.powerConsumptionLoss_kW; powerNightLoss_kW = orig.powerNightLoss_kW; diff --git a/ssc/cmod_pvsamv1.cpp b/ssc/cmod_pvsamv1.cpp index e39f01640..97f111547 100644 --- a/ssc/cmod_pvsamv1.cpp +++ b/ssc/cmod_pvsamv1.cpp @@ -651,7 +651,7 @@ static var_info _cm_vtab_pvsamv1[] = { { SSC_OUTPUT, SSC_ARRAY, "subarray1_modeff", "Subarray 1 Module efficiency", "%", "", "Time Series (Subarray 1)", "*", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "subarray1_celltemp", "Subarray 1 Cell temperature", "C", "", "Time Series (Subarray 1)", "*", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "subarray1_celltempSS", "Subarray 1 Cell temperature (steady state)", "C", "", "Time Series (Subarray 1)", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "subarray1_dni_index", "Subarray 1 DNI Index", "W/m2", "", "Time Series (Subarray 1)", "", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "subarray1_dni_index", "Subarray 1 DNI Index", "", "", "Time Series (Subarray 1)", "", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "subarray1_poa_beam_front_cs", "Subarray 1 beam clearsky irradiance", "W/m2", "", "Time Series (Subarray 1)", "", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "subarray1_poa_diffuse_front_cs", "Subarray 1 diffuse clearsky irradiance", "W/m2", "", "Time Series (Subarray 1)", "", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "subarray1_poa_ground_front_cs", "Subarray 1 ground reflected clearsky irradiance", "W/m2", "", "Time Series (Subarray 1)", "", "", "" }, @@ -794,7 +794,7 @@ static var_info _cm_vtab_pvsamv1[] = { //SEV: total dc snow loss time series (not a required output) { SSC_OUTPUT, SSC_ARRAY, "dc_snow_loss", "DC power loss due to snow", "kW", "", "Time Series (Array)", "", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "dc_net", "Inverter DC input power", "kW", "", "Time Series (Array)", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "dc_net_clearsky", "Inverter DC clearsky input power", "kW", "", "Time Series (Array)", "", "", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "dc_net_clearsky", "Inverter DC clearsky input power", "kW", "", "Time Series (Array)", "", "", "" }, // DC Daily losses { SSC_OUTPUT, SSC_ARRAY, "dc_lifetime_loss", "DC lifetime daily loss", "kW", "", "Time Series (DC Loss)", "", "", "" }, @@ -817,6 +817,7 @@ static var_info _cm_vtab_pvsamv1[] = { { SSC_OUTPUT, SSC_ARRAY, "ac_gross", "Inverter AC output power", "kW", "", "Time Series (Array)", "*", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "clipping_potential", "Clipping potential", "", "", "Time Series (Inverter)", "", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "subhourly_clipping_loss", "Subhourly clipping correction loss", "kW", "", "Time Series (Inverter)", "", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "subhourly_clipping_loss_factor", "Subhourly clipping correction loss factor", "", "", "Time Series (Inverter)", "", "", "" }, // transformer model outputs { SSC_OUTPUT, SSC_ARRAY, "xfmr_nll_ts", "Transformer no load loss", "kW", "", "Time Series (Transformer)", "", "", "" }, @@ -997,6 +998,7 @@ static var_info _cm_vtab_pvsamv1[] = { */ { SSC_OUTPUT, SSC_NUMBER, "annual_ac_wiring_loss", "AC wiring loss", "kWh", "", "Annual (Year 1)", "", "", "" }, { SSC_OUTPUT, SSC_NUMBER, "annual_subhourly_clipping_loss", "Subhourly clipping correction loss", "kWh", "", "Annual (Year 1)", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "annual_subhourly_clipping_factor", "Subhourly clipping correction loss factor", "", "", "Annual (Year 1)", "", "", "" }, { SSC_OUTPUT, SSC_NUMBER, "annual_transmission_loss", "Transmission loss", "kWh", "", "Annual (Year 1)", "", "", "" }, @@ -1508,7 +1510,7 @@ void cm_pvsamv1::exec() Irradiance->dtHour, Subarrays[nn]->tiltDegrees, Subarrays[nn]->azimuthDegrees, Subarrays[nn]->trackerRotationLimitDegrees, 0.0, Subarrays[nn]->groundCoverageRatio, Subarrays[nn]->slopeTilt, Subarrays[nn]->slopeAzm, Subarrays[nn]->monthlyTiltDegrees, Irradiance->userSpecifiedMonthlyAlbedo, Subarrays[nn]->poa.poaAll.get(), - Irradiance->useSpatialAlbedos, &Irradiance->userSpecifiedMonthlySpatialAlbedos); + Irradiance->useSpatialAlbedos, &Irradiance->userSpecifiedMonthlySpatialAlbedos, as_boolean("enable_subhourly_clipping")); int code = irr.calc(); @@ -2541,6 +2543,7 @@ void cm_pvsamv1::exec() double annual_dc_loss_ond = 0, annual_ac_loss_ond = 0; // (TR) double annual_subhourly_clipping_loss = 0; + double annual_subhourly_clipping_factor = 0; /* if (as_boolean("enable_subhourly_clipping")) { for (size_t inrec = 0; inrec < nrec; inrec++) { @@ -2623,6 +2626,7 @@ void cm_pvsamv1::exec() double acpwr_gross = 0, ac_wiringloss = 0, transmissionloss = 0; double ac_subhourlyclipping_loss = 0; + double ac_subhourlyclipping_factor = 0; cur_load = p_load_full[idx]; //set DC voltages for use in AC power calculation @@ -2700,7 +2704,7 @@ void cm_pvsamv1::exec() //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 + 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; double clip_pot = (dcPower_kW_csky - paco ) / (paco); @@ -2737,6 +2741,8 @@ void cm_pvsamv1::exec() //acpwr_gross *= (1 - sub_clipping_matrix.at(dni_row, clip_pot_col)); if (dcPower_kW_csky > 0.0) { ac_subhourlyclipping_loss = sub_clipping_matrix.at(dni_row, clip_pot_col) * std::abs(acpwr_gross); + //ac_subhourlyclipping_loss = sub_clipping_matrix.at(dni_row, clip_pot_col) * sharedInverter->powerAC_kW_clipping; + ac_subhourlyclipping_factor = sub_clipping_matrix.at(dni_row, clip_pot_col); } else { //No inverter clipping at night time, skip checks? ac_subhourlyclipping_loss = 0.0; @@ -2758,6 +2764,7 @@ void cm_pvsamv1::exec() annual_ac_wiring_loss += ac_wiringloss * ts_hour; annual_subhourly_clipping_loss += ac_subhourlyclipping_loss; + annual_subhourly_clipping_factor += ac_subhourlyclipping_factor; } if (iyear == 0 || save_full_lifetime_variables == 1) @@ -2769,6 +2776,7 @@ void cm_pvsamv1::exec() PVSystem->p_inverterThermalLoss[idx] = (ssc_number_t)(sharedInverter->powerTempLoss_kW); PVSystem->p_acWiringLoss[idx] = (ssc_number_t)(ac_wiringloss); PVSystem->p_subhourlyClippingLoss[idx] = (ssc_number_t)(ac_subhourlyclipping_loss); + PVSystem->p_subhourlyClippingLossFactor[idx] = (ssc_number_t)(ac_subhourlyclipping_factor); if (offline) { PVSystem->p_inverterNightTimeLoss[idx] = 0.0; @@ -3198,6 +3206,7 @@ void cm_pvsamv1::exec() assign("annual_ac_wiring_loss", var_data((ssc_number_t)annual_ac_wiring_loss)); assign("annual_subhourly_clipping_loss", var_data((ssc_number_t)annual_subhourly_clipping_loss)); + assign("annual_subhourly_clipping_factor", var_data((ssc_number_t)annual_subhourly_clipping_factor)); assign("annual_transmission_loss", var_data((ssc_number_t)annual_transmission_loss)); @@ -3343,15 +3352,15 @@ void cm_pvsamv1::exec() assign("annual_ac_battery_loss_percent", var_data((ssc_number_t)percent)); sys_output -= annual_ac_battery_loss; + if (annual_ac_gross > 0) percent = 100.0 * annual_subhourly_clipping_loss / annual_ac_gross; + assign("annual_subhourly_clipping_loss_percent", var_data((ssc_number_t)percent)); + sys_output -= annual_subhourly_clipping_loss; + percent = 0.; if (annual_ac_gross > 0) percent = 100.0 * annual_ac_wiring_loss / annual_ac_gross; assign("annual_ac_wiring_loss_percent", var_data((ssc_number_t)percent)); sys_output -= annual_ac_wiring_loss; - if (annual_ac_gross > 0) percent = 100.0 * annual_subhourly_clipping_loss / annual_ac_gross; - assign("annual_subhourly_clipping_loss_percent", var_data((ssc_number_t)percent)); - sys_output -= annual_subhourly_clipping_loss; - percent = 0.; if (annual_ac_gross > 0) percent = 100.0 * annual_transmission_loss / annual_ac_gross; assign("annual_transmission_loss_percent", var_data((ssc_number_t)percent));