Skip to content

Commit

Permalink
Merge branch 'develop' into custom-generation-profile-tech
Browse files Browse the repository at this point in the history
  • Loading branch information
mjprilliman committed Oct 28, 2024
2 parents 7a8ea18 + fa3bae1 commit 393ce0c
Show file tree
Hide file tree
Showing 19 changed files with 196 additions and 80 deletions.
70 changes: 57 additions & 13 deletions shared/lib_irradproc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1842,29 +1842,66 @@ irrad::irrad(weather_header hdr,
}

int irrad::check() {
if (year < 0 || month < 0 || day < 0 || hour < 0 || minute < 0 || delt > 1) return -1;
if (year < 0 || month < 0 || day < 0 || hour < 0 || minute < 0 || delt > 1 ) {
errorMessage = util::format("Invalid year (%d), month (%d), hour (%d), minute (%d) data, or invalid time step (%lg) hours", year, month, day, hour, minute, delt);
return -1;
}
if (latitudeDegrees < -90 || latitudeDegrees > 90 || longitudeDegrees < -180 || longitudeDegrees > 180 ||
timezone < -15 || timezone > 15)
timezone < -15 || timezone > 15) {
errorMessage = util::format("Invalid latitude (%lg), longitude (%lg), or time zone (%lg), latitude must be between -90 and 90 degrees, longitude must be between -180 and 180 degrees, time zone must be between -15 and 15", latitudeDegrees, longitudeDegrees, timezone);
return -2;
if (radiationMode < irrad::DN_DF || radiationMode > irrad::POA_P || skyModel < 0 || skyModel > 2) return -3;
if (trackingMode < 0 || trackingMode > 4) return -4;
}
if (radiationMode < irrad::DN_DF || radiationMode > irrad::POA_P || skyModel < 0 || skyModel > 2) {
errorMessage = util::format("Invalid radiation mode (%d) or sky model (%d)", radiationMode, skyModel);
return -3;
}
if (trackingMode < 0 || trackingMode > 4) {
errorMessage = util::format("Invalid tracking mode (%d)", trackingMode);
return -4;
}
if (radiationMode == irrad::DN_DF &&
(directNormal < 0 || directNormal > irrad::irradiationMax || diffuseHorizontal < 0 || diffuseHorizontal > 1500))
(directNormal < 0 || directNormal > irrad::irradiationMax || diffuseHorizontal < 0 || diffuseHorizontal > 1500)) {
errorMessage = util::format("Invalid DNI (%lg) or DHI (%lg), DNI must be between 0 and %lg W/m2, DHI must be between 0 and 1500 W/m2", directNormal, diffuseHorizontal, irrad::irradiationMax);
return -5;
}
if (radiationMode == irrad::DN_GH &&
(globalHorizontal < 0 || globalHorizontal > 1500 || directNormal < 0 || directNormal > 1500))
(globalHorizontal < 0 || globalHorizontal > 1500 || directNormal < 0 || directNormal > 1500)) {
errorMessage = util::format("Invalid DNI (%lg) or GHI (%lg), DNI must be between 0 and %lg W/m2, GHI must be between 0 and 1500 W/m2", directNormal, diffuseHorizontal, irrad::irradiationMax);
return -6;
if (albedo < 0 || albedo > 1) return -7;
if (tiltDegrees < 0 || tiltDegrees > 90) return -8;
if (surfaceAzimuthDegrees < 0 || surfaceAzimuthDegrees >= 360) return -9;
if (rotationLimitDegrees < -90 || rotationLimitDegrees > 90) return -10;
if (stowAngleDegrees < -90 || stowAngleDegrees > 90) return -12;
}
if (albedo < 0 || albedo > 1) {
errorMessage = util::format("Invalid albedo (%lg), must be between 0 and 1", albedo);
return -7;
}
if (tiltDegrees < 0 || tiltDegrees > 90) {
errorMessage = util::format("Invalid tilt angle (%lg), must be between 0 and 90 degrees", tiltDegrees);
return -8;
}
if (surfaceAzimuthDegrees < 0 || surfaceAzimuthDegrees >= 360) {
errorMessage = util::format("Invalid azimuth (%lg), must be between 0 and 360 degrees", surfaceAzimuthDegrees);
return -9;
}
if (rotationLimitDegrees < -90 || rotationLimitDegrees > 90) {
errorMessage = util::format("Invalid tracker rotation limit (%lg), must be between -90 and 90 degrees", rotationLimitDegrees);
return -10;
}
if (stowAngleDegrees < -90 || stowAngleDegrees > 90) {
errorMessage = util::format("Invalid stow angle (%lg), must be between -90 and 90 degrees", stowAngleDegrees);
return -12;
}
if (radiationMode == irrad::GH_DF &&
(globalHorizontal < 0 || globalHorizontal > 1500 || diffuseHorizontal < 0 || diffuseHorizontal > 1500))
(globalHorizontal < 0 || globalHorizontal > 1500 || diffuseHorizontal < 0 || diffuseHorizontal > 1500)) {
errorMessage = util::format("Invalid GHI (%lg) or DHI (%lg), must be between 0 and 1500 W/m2", globalHorizontal, diffuseHorizontal);
return -11;
}

return 0;
}

std::string irrad::getErrorMessage() {
return errorMessage;
}

void irrad::setup_solarpos_outputs_for_lifetime(size_t ts_per_year) {
solarpos_outputs_for_lifetime.resize(ts_per_year);
}
Expand Down Expand Up @@ -2066,10 +2103,17 @@ void irrad::set_optional(double elev, double pres, double t_amb) //defaults of 0
this->elevation = elev;
if (!std::isnan(pres) && pres > 800)
this->pressure = pres;
if (!std::isnan(tamb))
if (!std::isnan(t_amb))
this->tamb = t_amb;
}

void irrad::get_optional(double *elev, double *pres, double *t_amb) //defaults of 0 meters elevation, atmospheric pressure, 15°C average annual temperature
{
*elev = this->elevation;
*pres = this->pressure;
*t_amb = this->tamb;
}

void irrad::set_subhourly_clipping(bool enable)
{
if (enable) this->enableSubhourlyClipping = true;
Expand Down
8 changes: 8 additions & 0 deletions shared/lib_irradproc.h
Original file line number Diff line number Diff line change
Expand Up @@ -1049,6 +1049,9 @@ class irrad
int year, month, day, hour;
double minute, delt;

//Error messages
std::string errorMessage;

//Enable subhourly clipping correction
bool enableSubhourlyClipping;

Expand Down Expand Up @@ -1148,6 +1151,9 @@ class irrad
/// Set the location for the irradiance processor
void set_location(double lat, double lon, double tz);

/// Get optional parameters for solarpos_spac calculation
void get_optional(double* elev, double* pres, double* t_amb);

// Set optional parameters for solarpos_spa calculation
void set_optional(double elev = 0, double pres = 1013.25, double t_amb = 15);

Expand Down Expand Up @@ -1283,6 +1289,8 @@ class irrad
/// Return the front surface irradiances, used by \link calc_rear_side()
void getFrontSurfaceIrradiances(double pvBackShadeFraction, double rowToRow, double verticalHeight, double clearanceGround, double distanceBetweenRows, double horizontalLength, std::vector<double> frontGroundGHI, std::vector<double>& frontIrradiance, double& frontAverageIrradiance, std::vector<double>& frontReflected);

std::string getErrorMessage();

/// Return the solarpos outputs for a given timestep
bool getStoredSolarposOutputs();

Expand Down
1 change: 1 addition & 0 deletions shared/lib_pv_io_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ Irradiance_IO::Irradiance_IO(compute_module* cm, std::string cmName)
}
else if (cm->is_assigned("solar_resource_data")) {
weatherDataProvider = std::unique_ptr<weather_data_provider>(new weatherdata(cm->lookup("solar_resource_data")));
if (!weatherDataProvider->ok()) throw exec_error(cmName, weatherDataProvider->message());
if (weatherDataProvider->has_message()) cm->log(weatherDataProvider->message(), SSC_WARNING);
}
else {
Expand Down
14 changes: 7 additions & 7 deletions shared/lib_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -924,28 +924,28 @@ size_t util::hour_of_day(size_t hour_of_year)
size_t util::hour_of_year(size_t month, size_t day, size_t hour)
{
size_t h = 0;
bool ok = true;
std::vector<size_t> days_in_months = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
//first add the days from all the preceding completed months
if (month >= 1 && month <= 12)
{
for (size_t m = 0; m < (month - 1); m++)
h += days_in_months[m] * 24;
}
else ok = false;
else
throw std::runtime_error(util::format("unable to determine hour of year (0-8759) because month is out of range for time stamp (month-day-hour): %d-%d-%d", month, day, hour));
//then add days in the current month up to the current day
if (day >= 1 && day <= days_in_months[month - 1])
h += (day - 1) * 24;
else if (month == 2 && day == 29) //special check for leap day present in data
h += (27 * 24); //for leap day, repeat Feb 28 in annual indexes, because hour_of_year is used to index 8760 non-leap-year arrays.
else ok = false;
else
throw std::runtime_error(util::format("unable to determine hour of year (0 - 8759) because day is out of range for time stamp (month-day-hour): %d-%d-%d", month, day, hour));
if (hour >= 0 && hour <= 23)
h += hour;
else ok = false;
else
throw std::runtime_error(util::format("unable to determine hour of year (0 - 8759) because hour is out of range for time stamp (month-day-hour): %d-%d-%d", month, day, hour));
if (hour > 8759)
throw std::runtime_error("hour_of_year range is (0-8759) but calculated hour is > 8759: " + std::to_string(hour));
if (!ok)
throw std::runtime_error("hour_of_year input month, day, or hour out of correct range for m-d-h: " + std::to_string(month) + "-" + std::to_string(day) + "-" + std::to_string(hour));
throw std::runtime_error(util::format("unable to determine hour of year (0 - 8759) because hour is greater than 8759 for time stamp (month-day-hour): %d-%d-%d", month, day, hour));
return h;
}

Expand Down
39 changes: 17 additions & 22 deletions ssc/cmod_battery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1326,7 +1326,7 @@ battstor::battstor(var_table& vt, bool setup_model, size_t nrec, double dt_hr, c
if (dispatch_automatic_front_of_meter_t* dispatch_fom = dynamic_cast<dispatch_automatic_front_of_meter_t*>(dispatch_model))
{
if (batt_vars->batt_custom_dispatch.size() != 8760 * step_per_hour) {
throw exec_error("battery", "Length of batt_custom_dispatch must be 8760 * steps_per_hour.");
throw exec_error("battery", util::format("Battery custom dispatch time step (%d minutes) does not match simulation time step (%d minutes). Length of batt_custom_dispatch (%d) must be steps_per_hour (%lg) * 8760.", batt_vars->batt_custom_dispatch.size()/8760*60, (int)(60/step_per_hour), batt_vars->batt_custom_dispatch.size(), (double)step_per_hour));
}
dispatch_fom->set_custom_dispatch(batt_vars->batt_custom_dispatch);
}
Expand Down Expand Up @@ -1360,7 +1360,7 @@ battstor::battstor(var_table& vt, bool setup_model, size_t nrec, double dt_hr, c
if (dispatch_automatic_behind_the_meter_t* dispatch_btm = dynamic_cast<dispatch_automatic_behind_the_meter_t*>(dispatch_model))
{
if (batt_vars->batt_custom_dispatch.size() != 8760 * step_per_hour) {
throw exec_error("battery", "Length of batt_custom_dispach must be 8760 * steps_per_hour.");
throw exec_error("battery", util::format("Input battery power targets time step (%d minutes) does not match simulation time step (%d minutes). Length of batt_custom_dispatch (%d) must be steps_per_hour (%lg) * 8760.", batt_vars->batt_custom_dispatch.size() / 8760 * 60, (int)(60 / step_per_hour), batt_vars->batt_custom_dispatch.size(), (double)step_per_hour));
}
dispatch_btm->set_custom_dispatch(batt_vars->batt_custom_dispatch);
}
Expand Down Expand Up @@ -2243,6 +2243,7 @@ static var_info _cm_vtab_battery[] = {
{ SSC_INOUT, SSC_NUMBER, "percent_complete", "Estimated simulation status", "%", "", "Simulation", "", "", "" },
{ SSC_INPUT, SSC_NUMBER, "system_use_lifetime_output", "Lifetime simulation", "0/1", "0=SingleYearRepeated,1=RunEveryYear", "Lifetime", "?=0", "BOOLEAN", "" },
{ SSC_INPUT, SSC_NUMBER, "analysis_period", "Lifetime analysis period", "years", "The number of years in the simulation", "Lifetime", "system_use_lifetime_output=1","", "" },
{ SSC_INPUT, SSC_NUMBER, "timestep_minutes", "Simulation timestep", "minutes", "The number of minutes in each timestep", "Simulation", "en_standalone_batt=1","", "" },
{ SSC_INPUT, SSC_NUMBER, "en_batt", "Enable battery storage model", "0/1", "", "BatterySystem", "?=0", "", "" },
{ SSC_INPUT, SSC_NUMBER, "en_standalone_batt", "Enable standalone battery storage model", "0/1", "", "BatterySystem", "?=0", "", "" },
{ SSC_INPUT, SSC_NUMBER, "en_wave_batt", "Enable wave battery storage model", "0/1", "", "BatterySystem", "?=0", "", "" },
Expand Down Expand Up @@ -2290,29 +2291,23 @@ class cm_battery : public compute_module
std::vector<ssc_number_t> load_lifetime, load_year_one;
std::vector<ssc_number_t> grid_curtailment;
size_t nload;
size_t ngrid;
std::string timestep_source = "weather file";
bool use_lifetime = as_boolean("system_use_lifetime_output");
// System generation output, which is lifetime (if system_lifetime_output == true);
if (as_boolean("en_standalone_batt")) {
if (is_assigned("load")) {
load_year_one = as_vector_ssc_number_t("load");
nload = load_year_one.size();
if (use_lifetime)
power_input_lifetime.resize(nload * as_integer("analysis_period"), 0.0);
else
power_input_lifetime.resize(nload, 0.0);
}
else {
grid_curtailment = as_vector_ssc_number_t("grid_curtailment");
ngrid = grid_curtailment.size();
if (use_lifetime)
power_input_lifetime.resize(ngrid * as_integer("analysis_period"), 0.0);
else
power_input_lifetime.resize(ngrid, 0.0);
}
int timestep_minutes = as_number("timestep_minutes");
size_t n_rec_year_1 = 8760 * 60 / timestep_minutes;

if (use_lifetime)
power_input_lifetime.resize(n_rec_year_1 * as_integer("analysis_period"), 0.0);
else
power_input_lifetime.resize(n_rec_year_1, 0.0);

ssc_number_t* p_gen = allocate("gen", power_input_lifetime.size());
for (size_t i = 0; i < power_input_lifetime.size(); i++)
p_gen[i] = power_input_lifetime[i];

timestep_source = "simulation timestep";
}
else if (as_boolean("en_wave_batt")) {
power_input_lifetime = as_vector_ssc_number_t("energy_hourly_kW");
Expand All @@ -2333,7 +2328,7 @@ class cm_battery : public compute_module
nload = load_year_one.size();
// Array length for non-lifetime mode, lifetime mode, and hourly load
if (nload != n_rec_lifetime && nload != n_rec_lifetime / analysis_period && nload != 8760)
throw exec_error("battery", "Electric load must have either the same time step as the weather file, or 8760 time steps.");
throw exec_error("battery", "Electric load must have either the same time step as the " + timestep_source + ", or 8760 time steps.");
}

// resilience metrics for battery
Expand All @@ -2346,7 +2341,7 @@ class cm_battery : public compute_module
p_crit_load = as_vector_ssc_number_t("crit_load");
size_t n_crit_load = p_crit_load.size();
if (n_crit_load != n_rec_single_year && n_crit_load != 8760)
throw exec_error("battery", "Critical load crit_load must have same number of values as weather file, or 8760.");
throw exec_error("battery", "Critical load crit_load must have same number of values as " + timestep_source + ", or 8760.");
if (n_crit_load != nload)
throw exec_error("battery", "Critical load crit_load must have same number of values as load.");

Expand Down Expand Up @@ -2422,7 +2417,7 @@ class cm_battery : public compute_module
}
// Array length for non-lifetime mode, lifetime mode, and hourly load
else if (nload != n_rec_lifetime && nload != n_rec_lifetime / analysis_period && nload != 8760) {
throw exec_error("battery", "Electric load forecast must have either the same time step as the weather file, or 8760 time steps.");
throw exec_error("battery", "Electric load forecast must have either the same time step as the " + timestep_source + ", or 8760 time steps.");
}
}
if (p_load_forecast_in.size() > 0) {
Expand Down
6 changes: 3 additions & 3 deletions ssc/cmod_irradproc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ class cm_irradproc : public compute_module
else {
elev = as_double("elevation");
if (elev < 0 || elev > 5100) {
throw exec_error("irradproc", "The elevation input is outside of the expected range. Please make sure that the units are in meters");
throw exec_error("irradproc", util::format("The elevation (%lg) must be between 0 and 5100 meters", elev));
}
}
if (!is_assigned("tamb")) {
Expand All @@ -160,7 +160,7 @@ class cm_irradproc : public compute_module
else {
tamb = as_double("tamb");
if (tamb > 128 || tamb < -50) {
throw exec_error("irradproc", "The annual average temperature input is outside of the expected range. Please make sure that the units are in degrees Celsius");
throw exec_error("irradproc", util::format("The ambient temperature (%lg) must be between -50 and 128 degrees Celsius", tamb));
}
}
if (!is_assigned("pressure")) {
Expand All @@ -169,7 +169,7 @@ class cm_irradproc : public compute_module
else {
pres = as_double("pressure");
if (pres > 2000 || pres < 500) {
throw exec_error("irradproc", "The atmospheric pressure input is outside of the expected range. Please make sure that the units are in millibars");
throw exec_error("irradproc", util::format("The atmospheric pressure (%lg) must be between 500 and 2000 millibars", pres));
}
}

Expand Down
9 changes: 8 additions & 1 deletion ssc/cmod_mhk_tidal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ class cm_mhk_tidal : public compute_module
public:
cm_mhk_tidal() {
add_var_info(_cm_vtab_mhk_tidal);
add_var_info(vtab_adjustment_factors);

}

void exec() {
Expand Down Expand Up @@ -237,6 +239,11 @@ class cm_mhk_tidal : public compute_module
ssc_number_t* tidal_velocity = as_array("tidal_velocity", &number_records);
ssc_number_t* p_gen = allocate("gen", number_records);
int power_bin = 0;

adjustment_factors haf(this, "adjust");
if (!haf.setup(number_records, 1))
throw exec_error("mhk_tidal", "Failed to set up adjustment factors: " + haf.error());

//Find velocity bin of power array
for (int i = 0; i < number_records; i++) {
if (tidal_velocity[i] >= tidal_power_curve.at(tidal_power_curve.nrows() - 1, 0)) {
Expand All @@ -255,7 +262,7 @@ class cm_mhk_tidal : public compute_module

}
}
p_gen[i] = tidal_power_curve.at(power_bin, 1) * (1 - total_loss/100.0) * number_devices; //kW
p_gen[i] = tidal_power_curve.at(power_bin, 1) * (1 - total_loss/100.0) * number_devices * haf(i); //kW
//p_annual_energy_dist[i] = p_gen[i] * 8760.0 / number_records;
annual_energy += p_gen[i] * 8760.0 / number_records;
p_annual_energy_dist[power_bin] += p_gen[i] * 8760.0 / number_records;
Expand Down
Loading

0 comments on commit 393ce0c

Please sign in to comment.