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
4 changes: 4 additions & 0 deletions shared/lib_battery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,8 @@ double battery_t::calculate_max_charge_kw(double *max_current_A) {
thermal->updateTemperature(current, state->last_idx + 1);
qmax = capacity->qmax() * thermal->capacity_percent() * 0.01 * SOC_ratio;
}
current = std::min(0.0, current);
power_W = std::min(0.0, power_W);
if (max_current_A)
*max_current_A = current;
*thermal->state = thermal_initial;
Expand All @@ -556,6 +558,8 @@ double battery_t::calculate_max_discharge_kw(double *max_current_A) {
thermal->updateTemperature(current, state->last_idx + 1);
qmax = capacity->qmax() * thermal->capacity_percent() * 0.01;
}
current = std::max(0.0, current);
power_W = std::max(0.0, power_W);
if (max_current_A)
*max_current_A = current;
*thermal->state = thermal_initial;
Expand Down
61 changes: 33 additions & 28 deletions shared/lib_pv_io_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ Irradiance_IO::Irradiance_IO(compute_module* cm, std::string cmName)
if (weatherDataProvider->has_message()) cm->log(weatherDataProvider->message(), SSC_WARNING);
}
else {
throw exec_error(cmName, "No weather data supplied");
throw exec_error(cmName, "No weather data supplied.");
}

// assumes instantaneous values, unless hourly file with no minute column specified
Expand All @@ -166,7 +166,7 @@ Irradiance_IO::Irradiance_IO(compute_module* cm, std::string cmName)
tsShiftHours = 0.5;
}
else
throw exec_error(cmName, "subhourly and non-annual weather files must specify the minute for each record");
throw exec_error(cmName, "Subhourly and non-annual weather files must specify the minute for each record.");

weatherDataProvider->header(&weatherHeader);

Expand All @@ -182,7 +182,7 @@ Irradiance_IO::Irradiance_IO(compute_module* cm, std::string cmName)
}

if (weatherDataProvider->annualSimulation() && numberOfWeatherFileRecords % 8760 != 0)
throw exec_error(cmName, util::format("invalid number of data records (%zu): must be an integer multiple of 8760", numberOfWeatherFileRecords));
throw exec_error(cmName, util::format("Invalid number of data records (%zu): must be an integer multiple of 8760.", numberOfWeatherFileRecords));
if (weatherDataProvider->annualSimulation() && (stepsPerHour < 1 || stepsPerHour > 60))
throw exec_error(cmName, util::format("%d timesteps per hour found. Weather data should be single year.", stepsPerHour));

Expand Down Expand Up @@ -251,73 +251,78 @@ void Irradiance_IO::checkWeatherFile(compute_module* cm, std::string cmName)
for (size_t idx = 0; idx < numberOfWeatherFileRecords; idx++)
{
if (!weatherDataProvider->read(&weatherRecord))
throw exec_error(cmName, "could not read data line " + util::to_string((int)(idx + 1)) + " in weather file");
throw exec_error(cmName, "Could not read data line " + util::to_string((int)(idx + 1)) + " in weather file.");

// Check for missing data
if ((weatherRecord.gh != weatherRecord.gh) && (radiationMode == irrad::DN_GH || radiationMode == irrad::GH_DF)) {
cm->log(util::format("missing global irradiance %lg W/m2 at time [y:%d m:%d d:%d h:%d, minute:%lg], exiting",
cm->log(util::format("Missing global irradiance %lg W/m2 at time [y:%d m:%d d:%d h:%d, minute:%lg], exiting.",
weatherRecord.gh, weatherRecord.year, weatherRecord.month, weatherRecord.day, weatherRecord.hour, weatherRecord.minute), SSC_ERROR, (float)idx);
return;
}
if ((weatherRecord.dn != weatherRecord.dn) && (radiationMode == irrad::DN_DF || radiationMode == irrad::DN_GH)) {
cm->log(util::format("missing beam irradiance %lg W/m2 at time [y:%d m:%d d:%d h:%d, minute:%lg], exiting",
cm->log(util::format("Missing beam irradiance %lg W/m2 at time [y:%d m:%d d:%d h:%d, minute:%lg], exiting.",
weatherRecord.dn, weatherRecord.year, weatherRecord.month, weatherRecord.day, weatherRecord.hour, weatherRecord.minute), SSC_ERROR, (float)idx);
return;
}
if ((weatherRecord.df != weatherRecord.df) && (radiationMode == irrad::DN_DF || radiationMode == irrad::GH_DF)) {
cm->log(util::format("missing diffuse irradiance %lg W/m2 at time [y:%d m:%d d:%d h:%d, minute:%lg], exiting",
cm->log(util::format("Missing diffuse irradiance %lg W/m2 at time [y:%d m:%d d:%d h:%d, minute:%lg], exiting.",
weatherRecord.df, weatherRecord.year, weatherRecord.month, weatherRecord.day, weatherRecord.hour, weatherRecord.minute), SSC_ERROR, (float)idx);
return;
}
if ((weatherRecord.poa != weatherRecord.poa) && (radiationMode == irrad::POA_R || radiationMode == irrad::POA_P)) {
cm->log(util::format("missing POA irradiance %lg W/m2 at time [y:%d m:%d d:%d h:%d, minute:%lg], exiting",
cm->log(util::format("Missing POA irradiance %lg W/m2 at time [y:%d m:%d d:%d h:%d, minute:%lg], exiting.",
weatherRecord.poa, weatherRecord.year, weatherRecord.month, weatherRecord.day, weatherRecord.hour, weatherRecord.minute), SSC_ERROR, (float)idx);
return;
}
if (weatherRecord.tdry != weatherRecord.tdry) {
cm->log(util::format("missing temperature %lg W/m2 at time [y:%d m:%d d:%d h:%d, minute:%lg], exiting",
cm->log(util::format("Missing temperature %lg W/m2 at time [y:%d m:%d d:%d h:%d, minute:%lg], exiting.",
weatherRecord.tdry, weatherRecord.year, weatherRecord.month, weatherRecord.day, weatherRecord.hour, weatherRecord.minute), SSC_ERROR, (float)idx);
return;
}
if (weatherRecord.wspd != weatherRecord.wspd) {
cm->log(util::format("missing wind speed %lg W/m2 at time [y:%d m:%d d:%d h:%d, minute:%lg], exiting",
cm->log(util::format("Missing wind speed %lg W/m2 at time [y:%d m:%d d:%d h:%d, minute:%lg], exiting.",
weatherRecord.wspd, weatherRecord.year, weatherRecord.month, weatherRecord.day, weatherRecord.hour, weatherRecord.minute), SSC_ERROR, (float)idx);
return;
}

// Check for bad data
if ((weatherRecord.gh < 0 || weatherRecord.gh > irrad::irradiationMax) && (radiationMode == irrad::DN_GH || radiationMode == irrad::GH_DF))
{
cm->log(util::format("Out of range global irradiance %lg W/m2 at time [y:%d m:%d d:%d h:%d minute:%lg], set to zero",
cm->log(util::format("Out of range global irradiance %lg W/m2 at time [y:%d m:%d d:%d h:%d minute:%lg], set to zero.",
weatherRecord.gh, weatherRecord.year, weatherRecord.month, weatherRecord.day, weatherRecord.hour, weatherRecord.minute), SSC_WARNING, (float)idx);
weatherRecord.gh = 0;
}
if ((weatherRecord.dn < 0 || weatherRecord.dn > irrad::irradiationMax) && (radiationMode == irrad::DN_DF || radiationMode == irrad::DN_GH))
{
cm->log(util::format("Out of range beam irradiance %lg W/m2 at time [y:%d m:%d d:%d h:%d minute:%lg], set to zero",
cm->log(util::format("Out of range beam irradiance %lg W/m2 at time [y:%d m:%d d:%d h:%d minute:%lg], set to zero.",
weatherRecord.dn, weatherRecord.year, weatherRecord.month, weatherRecord.day, weatherRecord.hour, weatherRecord.minute), SSC_WARNING, (float)idx);
weatherRecord.dn = 0;
}
if ((weatherRecord.df < 0 || weatherRecord.df > irrad::irradiationMax) && (radiationMode == irrad::DN_DF || radiationMode == irrad::GH_DF))
{
cm->log(util::format("Out of range diffuse irradiance %lg W/m2 at time [y:%d m:%d d:%d h:%d minute:%lg], set to zero",
cm->log(util::format("Out of range diffuse irradiance %lg W/m2 at time [y:%d m:%d d:%d h:%d minute:%lg], set to zero.",
weatherRecord.df, weatherRecord.year, weatherRecord.month, weatherRecord.day, weatherRecord.hour, weatherRecord.minute), SSC_WARNING, (float)idx);
weatherRecord.df = 0;
}
if ((weatherRecord.poa < 0 || weatherRecord.poa > irrad::irradiationMax) && (radiationMode == irrad::POA_R || radiationMode == irrad::POA_P))
{
cm->log(util::format("Out of range POA irradiance %lg W/m2 at time [y:%d m:%d d:%d h:%d minute:%lg], set to zero",
cm->log(util::format("Out of range POA irradiance %lg W/m2 at time [y:%d m:%d d:%d h:%d minute:%lg], set to zero.",
weatherRecord.poa, weatherRecord.year, weatherRecord.month, weatherRecord.day, weatherRecord.hour, weatherRecord.minute), SSC_WARNING, (float)idx);
weatherRecord.poa = 0;
}
if (useWeatherFileAlbedo && (weatherRecord.alb <= 0 || weatherRecord.alb >= 1))
{
if (num_alb_errors < 5)
{
cm->log(util::format("Out of range albedo %lg at time [y:%d m:%d d:%d h:%d minute:%lg]. This warning only appears for the first five instances of this error.",
weatherRecord.alb, weatherRecord.year, weatherRecord.month, weatherRecord.day, weatherRecord.hour, weatherRecord.minute), SSC_WARNING, (float)idx);
}
num_alb_errors++;
weatherRecord.alb = 0;
}
}
if (num_alb_errors > 0)
cm->log(util::format("Weather file albedo has %d invalid values, using monthly value", (int)num_alb_errors), SSC_WARNING);
cm->log(util::format("Weather file albedo has %d invalid values, using user-defined or default values for these time steps. See albedo output variable.", (int)num_alb_errors), SSC_WARNING);

weatherDataProvider->rewind();
}
Expand Down Expand Up @@ -381,7 +386,7 @@ Subarray_IO::Subarray_IO(compute_module* cm, const std::string& cmName, size_t s
{
int n = cm->as_integer(prefix + "nstrings");
if (n < 0) {
throw exec_error(cmName, "invalid string allocation between subarrays. all subarrays must have zero or positive number of strings.");
throw exec_error(cmName, "Invalid string allocation between subarrays. All subarrays must have zero or positive number of strings.");
}

nStrings = n;
Expand Down Expand Up @@ -486,11 +491,11 @@ Subarray_IO::Subarray_IO(compute_module* cm, const std::string& cmName, size_t s
(1 - nameplateLossPercent));

if (groundCoverageRatio < 0.01)
throw exec_error(cmName, "array ground coverage ratio must obey 0.01 < gcr");
throw exec_error(cmName, "Array ground coverage ratio must obey 0.01 < gcr.");


monthlySoiling = cm->as_vector_double(prefix + "soiling");
if (monthlySoiling.size() != 12) throw exec_error(cmName, "soiling loss array must have 12 values: subarray " + util::to_string((int)(subarrayNumber)));
if (monthlySoiling.size() != 12) throw exec_error(cmName, "Soiling loss array must have 12 values: subarray " + util::to_string((int)(subarrayNumber)) + ".");

//convert from % to derate
for (size_t m = 0; m < monthlySoiling.size(); m++)
Expand Down Expand Up @@ -603,7 +608,7 @@ void PVSystem_IO::SetupPOAInput()
for (size_t ii = 0; ii < Irradiance->numberOfWeatherFileRecords; ii++) {

if (!wdprov->read(&wf)) {
throw exec_error("pvsamv1", "could not read data line " + util::to_string((int)(ii + 1)) + " in weather file while loading POA data");
throw exec_error("pvsamv1", "Could not read data line " + util::to_string((int)(ii + 1)) + " in weather file while loading POA data.");
}
int month_idx = wf.month - 1;

Expand Down Expand Up @@ -807,20 +812,20 @@ PVSystem_IO::PVSystem_IO(compute_module* cm, std::string cmName, Simulation_IO*
if (enableDCLifetimeLosses)
{
if (!Simulation->annualSimulation)
throw exec_error(cmName, "Lifetime daily losses cannot be entered with non-annual weather data");
throw exec_error(cmName, "Lifetime daily losses cannot be entered with non-annual weather data.");

dcLifetimeLosses = cm->as_vector_double("dc_lifetime_losses");
if (dcLifetimeLosses.size() != Simulation->numberOfYears * 365)
throw exec_error(cmName, "Length of the lifetime daily DC losses array must be equal to the analysis period * 365 days/year");
throw exec_error(cmName, "Length of the lifetime daily DC losses array must be equal to the analysis period * 365 days/year.");
}
if (enableACLifetimeLosses)
{
if (!Simulation->annualSimulation)
throw exec_error(cmName, "Lifetime daily losses cannot be entered with non-annual weather data");
throw exec_error(cmName, "Lifetime daily losses cannot be entered with non-annual weather data.");

acLifetimeLosses = cm->as_vector_double("ac_lifetime_losses");
if (acLifetimeLosses.size() != Simulation->numberOfYears * 365)
throw exec_error(cmName, "Length of the lifetime daily AC losses array must be equal to the analysis period * 365 days/year");
throw exec_error(cmName, "Length of the lifetime daily AC losses array must be equal to the analysis period * 365 days/year.");
}
}

Expand Down Expand Up @@ -1052,7 +1057,7 @@ Module_IO::Module_IO(compute_module* cm, std::string cmName, double dcLoss)
simpleEfficiencyModel.Rad[i] = cm->as_double(util::format("spe_rad%d", i));
simpleEfficiencyModel.Eff[i] = 0.01 * cm->as_double(util::format("spe_eff%d", i));
if (i > 0 && simpleEfficiencyModel.Rad[i] <= simpleEfficiencyModel.Rad[i - 1])
throw exec_error(cmName, "simpleEfficiencyModel model radiation levels must increase monotonically");
throw exec_error(cmName, "Simple Efficiency Model model radiation levels must increase monotonically.");
}

simpleEfficiencyModel.Gamma = cm->as_double("spe_temp_coeff");
Expand Down Expand Up @@ -1091,7 +1096,7 @@ Module_IO::Module_IO(compute_module* cm, std::string cmName, double dcLoss)
sandiaCellTemp.DT0 = cm->as_double("spe_dT");
break;
default:
throw exec_error(cmName, "invalid simpleEfficiencyModel module structure and mounting");
throw exec_error(cmName, "Invalid Simple Efficiency Model module structure and mounting.");
}

simpleEfficiencyModel.fd = cm->as_double("spe_fd");
Expand Down Expand Up @@ -1479,7 +1484,7 @@ Module_IO::Module_IO(compute_module* cm, std::string cmName, double dcLoss)
cellTempModel = &mockCellTemp;
}
else {
throw exec_error(cmName, "invalid temperature model type");
throw exec_error(cmName, "Invalid temperature model type.");
}

mlModuleModel.initializeManual();
Expand All @@ -1495,7 +1500,7 @@ Module_IO::Module_IO(compute_module* cm, std::string cmName, double dcLoss)
}
else
{
throw exec_error(cmName, "invalid pv module model type");
throw exec_error(cmName, "Invalid pv module model type.");
}

// TODO: reimplement, but fix issues that this causes with some tests
Expand Down Expand Up @@ -1669,7 +1674,7 @@ Inverter_IO::Inverter_IO(compute_module* cm, std::string cmName)
}
else
{
throw exec_error("pvsamv1", "invalid inverter model type");
throw exec_error("pvsamv1", "Invalid inverter model type.");
}
}

Expand Down
9 changes: 8 additions & 1 deletion ssc/cmod_merchantplant_eqns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ bool mp_ancillary_services(ssc_data_t data)
// if none enabled then check passes
ancillary_services_success = (!mp_enable_energy_market_revenue && !mp_enable_ancserv1 && !mp_enable_ancserv2 && !mp_enable_ancserv3 && !mp_enable_ancserv4);

size_t nsteps = 0, nsteps_per_year = 8760;
size_t nsteps = 0, nsteps_per_year = 8760; // Don't panic, this will be re-evaluted on line 214
if (en_mp_energy_market)
nsteps = std::max(nsteps, mp_energy_market_revenue.nrows());
if (en_mp_ancserv1)
Expand All @@ -161,6 +161,13 @@ bool mp_ancillary_services(ssc_data_t data)
nsteps = std::max(nsteps, mp_ancserv4_revenue.nrows());

if (nsteps < (8760 * (size_t)analysis_period)) nsteps = 8760 * (size_t)analysis_period; // extrapolated timeseries has minimum of hourly values for use in all forecasting
if (vt->as_boolean("system_use_lifetime_output"))
{
if (nsteps < system_gen.ncols()) nsteps = system_gen.ncols();
}
else {
if (nsteps < system_gen.ncols() * (size_t)analysis_period) nsteps = system_gen.ncols() * (size_t)analysis_period;
}

std::vector<ssc_number_t> energy_market_revenue(nsteps, 0.0);
std::vector<ssc_number_t> ancillary_services1_revenue(nsteps, 0.0);
Expand Down
13 changes: 7 additions & 6 deletions ssc/cmod_pvwattsv8.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,13 @@ class cm_pvwattsv8 : public compute_module
if ((std::isfinite(wf.alb) && (wf.alb > 0 && wf.alb < 1)))
alb = wf.alb;
else
{
if (n_alb_errs < 5) // display warning up to 5 times
{
log(util::format("Invalid albedo input value %f [year:%d month:%d day:%d hour:%d minute:%lg]. Using default albedo value %f (snow) or %f (no snow). This warning only appears for the first five instances of this error.", wf.alb, wf.year, wf.month, wf.day, wf.hour, wf.minute, as_double("albedo_default_snow"), as_double("albedo_default")), SSC_NOTICE);
}
n_alb_errs++;
}
}
else
{
Expand All @@ -884,11 +890,6 @@ class cm_pvwattsv8 : public compute_module
alb = as_double("albedo_default_snow");
else
alb = as_double("albedo_default");
if (!use_wf_albedo && n_alb_errs < 5) // display warning up to 5 times
{
log(util::format("Albedo input value is not valid for time step %d. Using default albedo value of %f (snow) or %f (no snow). This warning only appears for the first five instances of this error.", idx, as_double("albedo_default_snow"), as_double("albedo_default")), SSC_NOTICE);
n_alb_errs++;
}
}

// report albedo value as output
Expand Down Expand Up @@ -1388,7 +1389,7 @@ class cm_pvwattsv8 : public compute_module
}

if (n_alb_errs > 0)
log(util::format("Weather file albedo has %d invalid values, using monthly value", (int)n_alb_errs), SSC_WARNING);
log(util::format("Weather file albedo has %d invalid values, using user-specified or default values for those time steps. See Albedo output variable.", (int)n_alb_errs), SSC_WARNING);

wdprov->rewind();
}
Expand Down
3 changes: 1 addition & 2 deletions ssc/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -840,7 +840,7 @@ forecast_price_signal::forecast_price_signal(var_table *vt)

bool forecast_price_signal::setup(size_t step_per_hour)
{
size_t nsteps = 8760 * step_per_hour;;
size_t nsteps = 8760 * step_per_hour; // nsteps is for 1 year (is multiplied by nyears later)

int forecast_price_signal_model = vartab->as_integer("forecast_price_signal_model");
size_t nyears = vartab->as_unsigned_long("analysis_period");
Expand Down Expand Up @@ -973,7 +973,6 @@ bool forecast_price_signal::setup(size_t step_per_hour)
mp_ancserv_4_revenue_mat.assign(mp_ancserv4_revenue_in, nrows, ncols);
}

if ( nsteps > 8760 * nyears) step_per_hour = nsteps / (8760 * nyears);
if (step_per_hour < 1 || step_per_hour > 60 || step_per_hour * 8760 != nsteps)
{
m_error = util::format("The requested number of timesteps must be a multiple of 8760. Instead requested timesteps is %d.", (int)nsteps);
Expand Down
Loading
Loading