Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow for custom tracker rotation angles timeseries input for subarrays #1071

Merged
merged 19 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
22835a9
Setting up checks for user input tilt angles, likely to be incorporated
mjprilliman Oct 3, 2023
2eb2959
Merge branch 'develop' into user-tilt-angles
mjprilliman Oct 11, 2023
f6ec275
Setting up framework for custom rotation angles
mjprilliman Oct 12, 2023
080f986
Passing custom rotation angles to irradproc incidence angle
mjprilliman Oct 19, 2023
8953a57
Merge branch 'develop' into user-tilt-angles
mjprilliman Oct 19, 2023
8ff0b98
Updated incidence calls throughout code, debugged custom rotation angles
mjprilliman Oct 19, 2023
0f00de7
Applied user temperatures, tilt angles across subarrays; need to test
mjprilliman Oct 20, 2023
a8563a6
Fix custom angle check for when option is not selected
mjprilliman Oct 20, 2023
5d389f2
Add ssc tests
mjprilliman Oct 24, 2023
3ff549c
Merge branch 'develop' into user-tilt-angles
mjprilliman Oct 24, 2023
e052f87
Remove accidental tiltequalslat test addition
mjprilliman Oct 24, 2023
96138f7
Set default use custom tilt angle boolean to 0 in irrad setup
mjprilliman Oct 24, 2023
f061325
Fix boolean check, rename variables to rotation angle, apply rotation
mjprilliman Oct 25, 2023
585b04c
Removing rotation limit checks
mjprilliman Oct 25, 2023
581aded
Update var names and lables for custom cell temps, custom tracker
mjprilliman Oct 26, 2023
b62aa4b
Rename custom cell temp array csv file for ssc test
mjprilliman Oct 26, 2023
e30bb35
clean up tracker rotation labels
cpaulgilman Oct 26, 2023
f9f24f6
Merge branch 'user-tilt-angles' of https://github.com/NREL/ssc into u…
cpaulgilman Oct 26, 2023
5018ac2
Fix subarray 2 custom tracker rotation label
mjprilliman Oct 27, 2023
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
28 changes: 21 additions & 7 deletions shared/lib_irradproc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1142,7 +1142,7 @@ solarpos_spa(int year, int month, int day, int hour, double minute, double secon

void incidence(int mode, double tilt, double sazm, double rlim, double zen,
double azm, bool en_backtrack, double gcr, double slope_tilt, double slope_azm,
bool force_to_stow, double stow_angle_deg, double angle[5]) {
bool force_to_stow, double stow_angle_deg, bool useCustomAngle, double customAngle, double angle[5]) {
/*
Calculate panel orientation, angle of incidence with beam radiation, and
tracker rotation angles (where applicable).
Expand Down Expand Up @@ -1224,6 +1224,11 @@ void incidence(int mode, double tilt, double sazm, double rlim, double zen,
rot = backtracking_rotation;
}

/*Check if custom tracker rotation angles enabled, apply timeseries value*/
if (useCustomAngle) {
rot = customAngle * DTOR; //overwrite rotation angle with input from array
}

/* Find tilt angle for the tracking surface */
arg = cos(xtilt) * cos(rot);
if (arg < -1.0)
Expand Down Expand Up @@ -1751,6 +1756,7 @@ void irrad::setup() {
poaRearDirectDiffuse = 0.;
poaRearRowReflections = 0.;
poaRearSelfShaded = 0.;
useCustomRotAngles = 0.;

}

Expand All @@ -1766,7 +1772,7 @@ irrad::irrad(weather_record wf, weather_header hdr,
double groundCoverageRatioIn, double slopeTiltIn, double slopeAzmIn, std::vector<double> monthlyTiltDegrees,
std::vector<double> userSpecifiedAlbedo,
poaDecompReq *poaAllIn,
bool useSpatialAlbedos, const util::matrix_t<double>* userSpecifiedSpatialAlbedos, bool enableSubhourlyClipping) :
bool useSpatialAlbedos, const util::matrix_t<double>* userSpecifiedSpatialAlbedos, bool enableSubhourlyClipping, bool useCustomRotAngles, double customRotAngle) :
skyModel(skyModelIn), radiationMode(radiationModeIn), trackingMode(trackModeIn),
enableBacktrack(backtrackingEnabled), forceToStow(forceToStowIn),
delt(dtHour), tiltDegrees(tiltDegreesIn), surfaceAzimuthDegrees(azimuthDegreesIn),
Expand Down Expand Up @@ -1795,6 +1801,8 @@ irrad::irrad(weather_record wf, weather_header hdr,

set_subhourly_clipping(enableSubhourlyClipping);

set_custom_rot_angles(useCustomRotAngles, customRotAngle);

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);
Expand Down Expand Up @@ -1988,6 +1996,12 @@ void irrad::set_subhourly_clipping(bool enable)
if (enable) this->enableSubhourlyClipping = true;
}

void irrad::set_custom_rot_angles(bool enable, double angle)
{
this->useCustomRotAngles = enable;
this->customRotAngle = angle;
}

void irrad::set_sky_model(int sm, double alb, const std::vector<double> &albSpatial) {
this->skyModel = sm;
this->albedo = alb;
Expand Down Expand Up @@ -2176,7 +2190,7 @@ int irrad::calc() {
// compute incidence angles onto fixed or tracking surface
incidence(trackingMode, tiltDegrees, surfaceAzimuthDegrees, rotationLimitDegrees, sunAnglesRadians[1],
sunAnglesRadians[0],
enableBacktrack, groundCoverageRatio, slopeTilt, slopeAzm, forceToStow, stowAngleDegrees, surfaceAnglesRadians);
enableBacktrack, groundCoverageRatio, slopeTilt, slopeAzm, forceToStow, stowAngleDegrees, useCustomRotAngles, customRotAngle, surfaceAnglesRadians);
if (radiationMode < irrad::POA_R) {
double hextra = sunAnglesRadians[8];
double hbeam = directNormal *
Expand Down Expand Up @@ -2576,7 +2590,7 @@ void irrad::getFrontSurfaceIrradiances(double pvFrontShadeFraction, double rowTo
// Calculate irradiance components for a 90 degree tilt to get horizon brightening
double angleTmp[5] = {0, 0, 0, 0, 0}; // ([0] = incidence angle, [1] = tilt)
incidence(0, 90.0, 180.0, 45.0, solarZenithRadians, solarAzimuthRadians, this->enableBacktrack,
this->groundCoverageRatio, this->slopeTilt, this->slopeAzm, this->forceToStow, this->stowAngleDegrees, angleTmp);
this->groundCoverageRatio, this->slopeTilt, this->slopeAzm, this->forceToStow, this->stowAngleDegrees, this->useCustomRotAngles, this->customRotAngle, angleTmp);
perez(0, calculatedDirectNormal, calculatedDiffuseHorizontal, albedo, angleTmp[0], angleTmp[1], solarZenithRadians,
poa, diffc);
double horizonDiffuse = diffc[2];
Expand Down Expand Up @@ -2712,7 +2726,7 @@ void irrad::getFrontSurfaceIrradiances(double pvFrontShadeFraction, double rowTo
// Calculate and add direct and circumsolar irradiance components
incidence(0, tiltRadians * RTOD, surfaceAzimuthRadians * RTOD, 45.0, solarZenithRadians, solarAzimuthRadians,
this->enableBacktrack, this->groundCoverageRatio, this->slopeTilt, this->slopeAzm,
this->forceToStow, this->stowAngleDegrees, surfaceAnglesRadians);
this->forceToStow, this->stowAngleDegrees, this->useCustomRotAngles, this->customRotAngle, surfaceAnglesRadians);
perez(0, calculatedDirectNormal, calculatedDiffuseHorizontal, albedo, surfaceAnglesRadians[0],
surfaceAnglesRadians[1], solarZenithRadians, poa, diffc);

Expand Down Expand Up @@ -2757,7 +2771,7 @@ void irrad::getBackSurfaceIrradiances(double pvBackShadeFraction, double rowToRo
// Calculate components for a 90 degree tilt to get horizon brightening
double surfaceAnglesRadians90[5] = {0, 0, 0, 0, 0};
incidence(0, 90.0, 180.0, 45.0, solarZenithRadians, solarAzimuthRadians, this->enableBacktrack,
this->groundCoverageRatio, this->slopeTilt, this->slopeAzm, this->forceToStow, this->stowAngleDegrees, surfaceAnglesRadians90);
this->groundCoverageRatio, this->slopeTilt, this->slopeAzm, this->forceToStow, this->stowAngleDegrees, this->useCustomRotAngles, this->customRotAngle, surfaceAnglesRadians90);
perez(0, calculatedDirectNormal, calculatedDiffuseHorizontal, albedo, surfaceAnglesRadians90[0],
surfaceAnglesRadians90[1], solarZenithRadians, planeOfArrayIrradianceRear, diffuseIrradianceRear);
double horizonDiffuse = diffuseIrradianceRear[2];
Expand Down Expand Up @@ -2957,7 +2971,7 @@ void irrad::getBackSurfaceIrradiances(double pvBackShadeFraction, double rowToRo
// Calculate and add direct and circumsolar irradiance components
incidence(0, 180.0 - tiltRadians * RTOD, (surfaceAzimuthRadians * RTOD - 180.0), 45.0, solarZenithRadians,
solarAzimuthRadians, this->enableBacktrack,
this->groundCoverageRatio, this->slopeTilt, this->slopeAzm, this->forceToStow, this->stowAngleDegrees, surfaceAnglesRadians);
this->groundCoverageRatio, this->slopeTilt, this->slopeAzm, this->forceToStow, this->stowAngleDegrees, this->useCustomRotAngles, this->customRotAngle, surfaceAnglesRadians);
perez(0, calculatedDirectNormal, calculatedDiffuseHorizontal, albedo, surfaceAnglesRadians[0],
surfaceAnglesRadians[1], solarZenithRadians, planeOfArrayIrradianceRear, diffuseIrradianceRear);

Expand Down
12 changes: 10 additions & 2 deletions shared/lib_irradproc.h
Original file line number Diff line number Diff line change
Expand Up @@ -724,14 +724,16 @@ void solarpos_spa(int year, int month, int day, int hour, double minute, double
* \param[in] slope_azm azimuth angle of slopted terrain relative to tracker azimuth in radians
* \param[in] force_to_stow: force the single-axis tracking array to the stow angle specified in the next input
* \param[in] stow_angle_deg: the angle to force the single-axis tracking array to stow to, in degrees
* \param[in] useCustomAngle: use custom rotation angles for single axis tracking 0/1
* \param[in] customAngle: custom rotation angle to use, in degrees
* \param[out] angle array of elements to return angles to calling function
* \param[out] angle[0] incident angle in radians
* \param[out] angle[1] tilt angle of surface from horizontal in radians
* \param[out] angle[2] surface azimuth in radians, measured east from north
* \param[out] angle[3] tracking axis rotation angle in radians, measured from surface normal of unrotating axis (only for 1 axis trackers)
* \param[out] angle[4] backtracking difference (rot - ideal_rot) will be zero except in case of backtracking for 1 axis tracking
*/
void incidence(int mode, double tilt, double sazm, double rlim, double zen, double azm, bool en_backtrack, double gcr, double slope_tilt, double slope_azm, bool force_to_stow, double stow_angle_deg, double angle[5]);
void incidence(int mode, double tilt, double sazm, double rlim, double zen, double azm, bool en_backtrack, double gcr, double slope_tilt, double slope_azm, bool force_to_stow, double stow_angle_deg, bool useCustomAngle, double customAngle, double angle[5]);


/**
Expand Down Expand Up @@ -991,6 +993,10 @@ class irrad
//Enable subhourly clipping correction
bool enableSubhourlyClipping;

//Custom rotation angles for single-axis trackers
bool useCustomRotAngles;
double customRotAngle; // custom tracker rotation angle in degrees

// Subarray properties
double tiltDegrees; ///< Surface tilt of subarray in degrees
double surfaceAzimuthDegrees; ///< Surface azimuth of subarray in degrees
Expand Down Expand Up @@ -1055,7 +1061,7 @@ class irrad
double dtHour, double tiltDegrees, double azimuthDegrees, double trackerRotationLimitDegrees, double stowAngleDegreesIn,
double groundCoverageRatio, double slopeTilt, double slopeAzm, std::vector<double> monthlyTiltDegrees, std::vector<double> userSpecifiedAlbedo,
poaDecompReq* poaAllIn,
bool useSpatialAlbedos = false, const util::matrix_t<double>* userSpecifiedSpatialAlbedos = nullptr, bool enableSubhourlyClipping = false);
bool useSpatialAlbedos = false, const util::matrix_t<double>* userSpecifiedSpatialAlbedos = nullptr, bool enableSubhourlyClipping = false, bool useCustomRotAngles = false, double customRotAngle = 0);

/// Construct the irrad class with an Irradiance_IO() object and Subarray_IO() object
irrad();
Expand All @@ -1078,6 +1084,8 @@ class irrad
//Set whether to use subhourly clipping model
void set_subhourly_clipping(bool enable = false);

void set_custom_rot_angles(bool enable = false, double angle = 0);

/// Set the sky model for the irradiance processor, using \link Irradiance_IO::SKYMODEL
void set_sky_model(int skymodel, double albedo, const std::vector<double> &albedoSpatial = std::vector<double>());

Expand Down
33 changes: 32 additions & 1 deletion shared/lib_pv_io_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,8 @@ Subarray_IO::Subarray_IO(compute_module* cm, const std::string& cmName, size_t s
nModulesPerString = cm->as_integer(prefix + "modules_per_string");
mpptInput = cm->as_integer(prefix + "mppt_input");
trackMode = cm->as_integer(prefix + "track_mode");
useCustomRotAngles = cm->as_boolean(prefix + "use_custom_rot_angles");
useCustomCellTemp = cm->as_boolean(prefix + "use_custom_cell_temp");
tiltEqualLatitude = 0;
if (cm->is_assigned(prefix + "tilt_eq_lat")) tiltEqualLatitude = cm->as_boolean(prefix + "tilt_eq_lat");

Expand All @@ -376,6 +378,35 @@ Subarray_IO::Subarray_IO(compute_module* cm, const std::string& cmName, size_t s
if (monthlyTiltDegrees[i] < 0.0) throw exec_error(cmName, "Subarray " + util::to_string((int)subarrayNumber) + " monthly tilt angles cannot be negative.");
}
}

/* Insert checks for custom tracker rotation angles here*/
if (useCustomRotAngles == 1) {
if (cm->is_assigned(prefix + "custom_rot_angles_array")) {
customRotAngles = cm->as_vector_double(prefix + "custom_rot_angles_array");
for (int i = 0; i < customRotAngles.size(); i++) {
if (customRotAngles[i] > 90.0 || customRotAngles[i] < -90.0) throw exec_error(cmName, "Subarray " + util::to_string((int)subarrayNumber) + " custom tracker rotation angles must be between -90 and 90 degrees.");
}
}
else {
throw exec_error(cmName, "Subarray " + util::to_string((int)subarrayNumber) + " custom tracker rotation angles required but not assigned.");
}
}



/* Insert checks for using custom cell temperature array*/
if (useCustomCellTemp == 1) {
if (cm->is_assigned(prefix + "custom_cell_temp_array")) {
customCellTempArray = cm->as_vector_double(prefix + "custom_cell_temp_array");
for (int i = 0; i < customCellTempArray.size(); i++) {
if (customCellTempArray[i] > 100.0) throw exec_error(cmName, "Subarray " + util::to_string((int)subarrayNumber) + " custom cell temperature cannot be greater than 100 degrees Celsius.");
}
}
else {
throw exec_error(cmName, "Subarray " + util::to_string((int)subarrayNumber) + " custom cell temperatures required but not assigned.");
}
}

//azimuth required for fixed tilt, single axis, and seasonal tilt- can't check for this in variable table so check here
azimuthDegrees = std::numeric_limits<double>::quiet_NaN();
if (trackMode == irrad::FIXED_TILT || trackMode == irrad::SINGLE_AXIS || trackMode == irrad::SEASONAL_TILT)
Expand Down Expand Up @@ -642,7 +673,7 @@ void PVSystem_IO::SetupPOAInput()


if (tms[2] > 0) {
incidence(Subarrays[nn]->trackMode, Subarrays[nn]->tiltDegrees, Subarrays[nn]->azimuthDegrees, Subarrays[nn]->trackerRotationLimitDegrees, sun[1], sun[0], Subarrays[nn]->backtrackingEnabled, Subarrays[nn]->groundCoverageRatio, Subarrays[nn]->slopeTilt, Subarrays[nn]->slopeAzm, false, 0.0, angle);
incidence(Subarrays[nn]->trackMode, Subarrays[nn]->tiltDegrees, Subarrays[nn]->azimuthDegrees, Subarrays[nn]->trackerRotationLimitDegrees, sun[1], sun[0], Subarrays[nn]->backtrackingEnabled, Subarrays[nn]->groundCoverageRatio, Subarrays[nn]->slopeTilt, Subarrays[nn]->slopeAzm, false, 0.0, false, 0.0, angle);
}
else {
angle[0] = -999;
Expand Down
4 changes: 4 additions & 0 deletions shared/lib_pv_io_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,10 @@ struct Subarray_IO
double slopeTilt; // Angle of sloped terrain [degrees]
double slopeAzm; // azimuth of sloped terrain relative to tracker azimuth [degrees]
double tiltDegrees; // The surface tilt [degrees]
flag useCustomRotAngles; // Use custom timeseries rotation angles
std::vector<double> customRotAngles; //Custom timeseries rotation angles [degrees]
flag useCustomCellTemp;
std::vector<double> customCellTempArray;
double azimuthDegrees; // The surface azimuth [degrees]
int trackMode; // The tracking mode [0 = fixed, 1 = single-axis tracking, 2 = two-axis tracking, 3 = azimuth-axis tracking, 4 = seasonal-tilt
double trackerRotationLimitDegrees; // The rotational limit of the tracker [degrees]
Expand Down
2 changes: 1 addition & 1 deletion ssc/cmod_irradproc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ static var_info _cm_vtab_irradproc[] = {
{ SSC_OUTPUT, SSC_ARRAY, "incidence", "Incidence angle to surface", "deg", "", "Irradiance Processor", "*", "LENGTH_EQUAL=beam", "" },
{ SSC_OUTPUT, SSC_ARRAY, "surf_tilt", "Surface tilt angle", "deg", "", "Irradiance Processor", "*", "LENGTH_EQUAL=beam", "" },
{ SSC_OUTPUT, SSC_ARRAY, "surf_azm", "Surface azimuth angle", "deg", "", "Irradiance Processor", "*", "LENGTH_EQUAL=beam", "" },
{ SSC_OUTPUT, SSC_ARRAY, "axis_rotation", "Tracking axis rotation angle", "deg", "", "Irradiance Processor", "*", "LENGTH_EQUAL=beam", "" },
{ SSC_OUTPUT, SSC_ARRAY, "axis_rotation", "Tracker rotation angle", "deg", "", "Irradiance Processor", "*", "LENGTH_EQUAL=beam", "" },
{ SSC_OUTPUT, SSC_ARRAY, "bt_diff", "Backtracking difference from ideal rotation", "deg", "", "Irradiance Processor", "*", "LENGTH_EQUAL=beam", "" },

{ SSC_OUTPUT, SSC_ARRAY, "sun_azm", "Solar azimuth", "deg", "", "Irradiance Processor", "*", "LENGTH_EQUAL=beam", "" },
Expand Down
Loading
Loading