From 55840e745925f4fbd59e8f7f0228c6895a6ecccf Mon Sep 17 00:00:00 2001 From: Brian Mirletz Date: Tue, 10 Oct 2023 10:06:32 -0600 Subject: [PATCH 1/9] Ssc 1058 ac loss accounting (#1061) * Work in progress commit. Loss accounting is improved but still leaves unsatisfactory unmet critical load. Requires philosophical discussion before proceeding - might make sense to feed back to dispatch current instead. * finalize powerflow loss accounting that allows the ac connected battery to deliver sufficient power to load * fix spelling in comment --- shared/lib_battery_powerflow.cpp | 49 ++++++----- .../lib_battery_powerflow_test.cpp | 84 +++++++++++++++++++ 2 files changed, 114 insertions(+), 19 deletions(-) diff --git a/shared/lib_battery_powerflow.cpp b/shared/lib_battery_powerflow.cpp index dfca723ef..657267b51 100644 --- a/shared/lib_battery_powerflow.cpp +++ b/shared/lib_battery_powerflow.cpp @@ -351,6 +351,11 @@ void BatteryPowerFlow::calculateACConnected() // Code simplification to remove redundancy for code that should use either critical load or actual load double calc_load_ac = (m_BatteryPower->isOutageStep ? P_crit_load_ac : P_load_ac); + double P_required_for_load = calc_load_ac; + + if (ac_loss_percent_post_battery < 1) { // Account for possible divide by zero + P_required_for_load /= (1 - ac_loss_percent_post_battery); + } // charging and idle if (P_battery_ac <= 0) @@ -365,11 +370,11 @@ void BatteryPowerFlow::calculateACConnected() if (m_BatteryPower->chargeOnlySystemExceedLoad) { P_pv_to_load_ac = P_pv_ac; - if (P_pv_to_load_ac > calc_load_ac) { - P_pv_to_load_ac = calc_load_ac; + if (P_pv_to_load_ac > P_required_for_load) { + P_pv_to_load_ac = P_required_for_load; } // Fuel cell goes to load next - P_fuelcell_to_load_ac = std::fmin(calc_load_ac - P_pv_to_load_ac, P_fuelcell_ac); + P_fuelcell_to_load_ac = std::fmin(P_required_for_load - P_pv_to_load_ac, P_fuelcell_ac); } // Excess PV can go to battery, if PV can cover charging losses @@ -398,11 +403,11 @@ void BatteryPowerFlow::calculateACConnected() P_pv_to_load_ac = P_pv_ac - P_pv_to_batt_ac; } - if (P_pv_to_load_ac > calc_load_ac) { - P_pv_to_load_ac = calc_load_ac; + if (P_pv_to_load_ac > P_required_for_load) { + P_pv_to_load_ac = P_required_for_load; } // Fuel cell goes to load next - P_fuelcell_to_load_ac = std::fmin(calc_load_ac - P_pv_to_load_ac, P_fuelcell_ac); + P_fuelcell_to_load_ac = std::fmin(P_required_for_load - P_pv_to_load_ac, P_fuelcell_ac); } // Fuelcell can also charge battery @@ -439,6 +444,7 @@ void BatteryPowerFlow::calculateACConnected() // discharging, not idle else { + // Test if battery is discharging erroneously if (!m_BatteryPower->canDischarge && P_battery_ac > 0) { P_batt_to_grid_ac = P_batt_to_load_ac = 0; @@ -448,9 +454,9 @@ void BatteryPowerFlow::calculateACConnected() P_pv_to_load_ac = P_pv_ac; // Excess PV production, no other component meets load - if (P_pv_ac >= calc_load_ac) + if (P_pv_ac >= P_required_for_load) { - P_pv_to_load_ac = calc_load_ac; + P_pv_to_load_ac = P_required_for_load; P_fuelcell_to_load_ac = 0; P_batt_to_load_ac = 0; @@ -459,14 +465,14 @@ void BatteryPowerFlow::calculateACConnected() P_fuelcell_to_grid_ac = P_fuelcell_ac; } else { - P_fuelcell_to_load_ac = std::fmin(P_fuelcell_ac, calc_load_ac - P_pv_to_load_ac); - P_batt_to_load_ac = std::fmin(P_battery_ac - P_system_loss_ac, calc_load_ac - P_pv_to_load_ac - P_fuelcell_to_load_ac); + P_fuelcell_to_load_ac = std::fmin(P_fuelcell_ac, P_required_for_load - P_pv_to_load_ac); + P_batt_to_load_ac = std::fmin(P_battery_ac - P_system_loss_ac, P_required_for_load - P_pv_to_load_ac - P_fuelcell_to_load_ac); } } else { - P_batt_to_load_ac = std::fmin(P_battery_ac, calc_load_ac); - P_fuelcell_to_load_ac = std::fmin(P_fuelcell_ac, calc_load_ac - P_batt_to_load_ac); - P_pv_to_load_ac = std::fmin(std::fmax(0, calc_load_ac - P_fuelcell_to_load_ac - P_batt_to_load_ac), P_pv_ac); + P_batt_to_load_ac = std::fmin(P_battery_ac, P_required_for_load); + P_fuelcell_to_load_ac = std::fmin(P_fuelcell_ac, P_required_for_load - P_batt_to_load_ac); + P_pv_to_load_ac = std::fmin(std::fmax(0, P_required_for_load - P_fuelcell_to_load_ac - P_batt_to_load_ac), P_pv_ac); P_pv_to_grid_ac = std::fmax(0, P_pv_ac - P_pv_to_load_ac); P_fuelcell_to_grid_ac = std::fmax(0, P_fuelcell_ac - P_fuelcell_to_load_ac); } @@ -483,11 +489,8 @@ void BatteryPowerFlow::calculateACConnected() P_fuelcell_to_grid_ac = 0; } + // Preliminary batt to grid for DC losses P_batt_to_grid_ac = P_battery_ac - P_system_loss_ac - P_batt_to_load_ac - P_batt_to_pv_inverter; - if (m_BatteryPower->isOutageStep && P_batt_to_grid_ac > tolerance) { - m_BatteryPower->powerBatteryDC = (P_battery_ac - P_batt_to_grid_ac) / m_BatteryPower->singlePointEfficiencyDCToAC; - return calculateACConnected(); - } P_fuelcell_to_grid_ac = P_fuelcell_ac - P_fuelcell_to_load_ac; P_batt_to_system_loss = P_system_loss_ac; @@ -503,15 +506,23 @@ void BatteryPowerFlow::calculateACConnected() // Compute total system output and grid power flow P_gen_ac = P_pv_ac + P_fuelcell_ac + P_inverter_draw_ac + P_battery_ac - P_system_loss_ac; + // Final batt to grid for outage accounting + if (P_battery_ac > 0) + { + P_batt_to_grid_ac = P_battery_ac * (1 - ac_loss_percent_post_battery) - P_system_loss_ac - P_batt_to_load_ac - P_batt_to_pv_inverter; + if (m_BatteryPower->isOutageStep && P_batt_to_grid_ac > tolerance) { + m_BatteryPower->powerBatteryDC = (P_battery_ac - P_batt_to_grid_ac) / m_BatteryPower->singlePointEfficiencyDCToAC; + return calculateACConnected(); + } + } + // Apply AC losses to powerflow - note that these are applied to gen later P_pv_to_batt_ac *= (1 - ac_loss_percent_post_battery); P_pv_to_load_ac *= (1 - ac_loss_percent_post_battery); P_pv_to_batt_ac *= (1 - ac_loss_percent_post_battery); P_pv_to_grid_ac *= (1 - ac_loss_percent_post_battery); P_grid_to_batt_ac *= (1 - ac_loss_percent_post_battery); - P_grid_to_load_ac *= (1 - ac_loss_percent_post_battery); P_batt_to_load_ac *= (1 - ac_loss_percent_post_battery); - P_batt_to_grid_ac *= (1 - ac_loss_percent_post_battery); P_fuelcell_to_batt_ac *= (1 - ac_loss_percent_post_battery); P_fuelcell_to_load_ac *= (1 - ac_loss_percent_post_battery); P_fuelcell_to_grid_ac *= (1 - ac_loss_percent_post_battery); diff --git a/test/shared_test/lib_battery_powerflow_test.cpp b/test/shared_test/lib_battery_powerflow_test.cpp index 4f4afe99e..be8c1cd03 100644 --- a/test/shared_test/lib_battery_powerflow_test.cpp +++ b/test/shared_test/lib_battery_powerflow_test.cpp @@ -3055,6 +3055,47 @@ TEST_F(BatteryPowerFlowTest_lib_battery_powerflow, AC_system_w_ac_losses) { gen = m_batteryPower->powerSystem + m_batteryPower->powerBatteryAC; EXPECT_NEAR(m_batteryPower->powerGeneratedBySystem, gen, error); EXPECT_NEAR(m_batteryPower->powerLoad, 0, error); + + // Post batt loss affects meeting critical load + m_batteryPower->acLossPostBattery = 0.05; + m_batteryPower->acLossWiring = 0; + m_batteryPower->powerBatteryDC = 50; + m_batteryPower->isOutageStep = true; + m_batteryPower->powerCritLoad = 50; + m_batteryPower->powerLoad = 50; + m_batteryPowerFlow->calculate(); + + EXPECT_NEAR(m_batteryPower->powerBatteryAC, 48, error); + EXPECT_NEAR(m_batteryPower->powerSystemToLoad, 0, error); + EXPECT_NEAR(m_batteryPower->powerSystemToBatteryAC, 0, error); + EXPECT_NEAR(m_batteryPower->powerGridToBattery, 0, error); + EXPECT_NEAR(m_batteryPower->powerBatteryToLoad, 45.6, error); + EXPECT_NEAR(m_batteryPower->powerCritLoadUnmet, 4.4, error); + EXPECT_NEAR(m_batteryPower->powerSystemToGrid, 0, error); + EXPECT_NEAR(m_batteryPower->powerConversionLoss, 2, error); + EXPECT_NEAR(m_batteryPower->powerSystemLoss, 0.0, error); + EXPECT_NEAR(m_batteryPower->powerLoad, 50, error); + + // Increasing batt power allows meeting critical load + m_batteryPower->acLossPostBattery = 0.05; + m_batteryPower->acLossWiring = 0; + m_batteryPower->powerBatteryDC = 60; + m_batteryPower->isOutageStep = true; + m_batteryPower->powerLoad = 50; + m_batteryPower->powerCritLoad = 50; + m_batteryPowerFlow->calculate(); + + EXPECT_NEAR(m_batteryPower->powerBatteryAC, 55.4, error); + EXPECT_NEAR(m_batteryPower->powerBatteryDC, 57.7, error); + EXPECT_NEAR(m_batteryPower->powerSystemToLoad, 0, error); + EXPECT_NEAR(m_batteryPower->powerSystemToBatteryAC, 0, error); + EXPECT_NEAR(m_batteryPower->powerGridToBattery, 0, error); + EXPECT_NEAR(m_batteryPower->powerBatteryToLoad, 50.0, error); + EXPECT_NEAR(m_batteryPower->powerCritLoadUnmet, 0.0, error); + EXPECT_NEAR(m_batteryPower->powerSystemToGrid, 0, error); + EXPECT_NEAR(m_batteryPower->powerConversionLoss, 2.308, error); + EXPECT_NEAR(m_batteryPower->powerSystemLoss, 0.0, error); + EXPECT_NEAR(m_batteryPower->powerLoad, 50, error); } TEST_F(BatteryPowerFlowTest_lib_battery_powerflow, DC_system_w_ac_losses) { @@ -3259,4 +3300,47 @@ TEST_F(BatteryPowerFlowTest_lib_battery_powerflow, DC_system_w_ac_losses) { EXPECT_NEAR(m_batteryPower->powerConversionLoss, 3.738, error); EXPECT_NEAR(m_batteryPower->powerSystemLoss, 0.0, error); EXPECT_NEAR(m_batteryPower->powerLoad, 0, error); + + m_batteryPower->acXfmrLoadLoss = 0.0; + m_batteryPower->acXfmrNoLoadLoss = 0.0; + + // Post batt loss affects meeting critical load + m_batteryPower->acLossPostBattery = 0.05; + m_batteryPower->acLossWiring = 0; + m_batteryPower->powerBatteryDC = 50; + m_batteryPower->isOutageStep = true; + m_batteryPower->powerCritLoad = 50; + m_batteryPower->powerLoad = 50; + m_batteryPowerFlow->calculate(); + + EXPECT_NEAR(m_batteryPower->powerBatteryAC, 46.26, error); + EXPECT_NEAR(m_batteryPower->powerSystemToLoad, 0, error); + EXPECT_NEAR(m_batteryPower->powerSystemToBatteryAC, 0, error); + EXPECT_NEAR(m_batteryPower->powerGridToBattery, 0, error); + EXPECT_NEAR(m_batteryPower->powerBatteryToLoad, 43.947, error); + EXPECT_NEAR(m_batteryPower->powerCritLoadUnmet, 6.053, error); + EXPECT_NEAR(m_batteryPower->powerSystemToGrid, 0, error); + EXPECT_NEAR(m_batteryPower->powerConversionLoss, 3.738, error); + EXPECT_NEAR(m_batteryPower->powerSystemLoss, 0.0, error); + EXPECT_NEAR(m_batteryPower->powerLoad, 50, error); + + // Increasing batt power allows meeting critical load + m_batteryPower->acLossPostBattery = 0.05; + m_batteryPower->acLossWiring = 0; + m_batteryPower->powerBatteryDC = 60; + m_batteryPower->isOutageStep = true; + m_batteryPower->powerLoad = 50; + m_batteryPower->powerCritLoad = 50; + m_batteryPowerFlow->calculate(); + + EXPECT_NEAR(m_batteryPower->powerBatteryAC, 52.63, error); + EXPECT_NEAR(m_batteryPower->powerSystemToLoad, 0, error); + EXPECT_NEAR(m_batteryPower->powerSystemToBatteryAC, 0, error); + EXPECT_NEAR(m_batteryPower->powerGridToBattery, 0, error); + EXPECT_NEAR(m_batteryPower->powerBatteryToLoad, 50.0, error); + EXPECT_NEAR(m_batteryPower->powerCritLoadUnmet, 0.0, error); + EXPECT_NEAR(m_batteryPower->powerSystemToGrid, 0, error); + EXPECT_NEAR(m_batteryPower->powerConversionLoss, 3.99, error); + EXPECT_NEAR(m_batteryPower->powerSystemLoss, 0.0, error); + EXPECT_NEAR(m_batteryPower->powerLoad, 50, error); } From 25572ccbccfdfe967936839eb5b2a2f295bfdde1 Mon Sep 17 00:00:00 2001 From: Brian Mirletz Date: Tue, 10 Oct 2023 10:40:32 -0600 Subject: [PATCH 2/9] Add internal resistance to voltage table calculations (#1016) * Address loss diagram issue for POA reference cell in SAM issue 1366 * Working without pressure column Need to remove some modifications in cmod_pvsamv1 - set to SAM_1366 branch * Reset POA_P mode - uses decomposition with and without pressure column * Add wfpoa to reported outputs to facilitate troubleshooting * Size the collector loop mass flow based on n_collectors (#1004) * Update to GitHub Actions to latest Ubuntu Ubuntu-18.04 deprecated as of 4/2023 https://github.blog/changelog/2022-08-09-github-actions-the-ubuntu-18-04-actions-runner-image-is-being-deprecated-and-will-be-removed-by-12-1-22/ * Add internal resistance to voltage table calculations * Update voltage equations and tests to account for zero current at full battery, new resistance in table tests * update resilience tests and fix voltage at 100 percent dod --------- Co-authored-by: Steven Janzou Co-authored-by: Paul Gilman Co-authored-by: Matthew Boyd <30417543+Matthew-Boyd@users.noreply.github.com> --- shared/lib_battery_voltage.cpp | 23 ++++++---- shared/lib_battery_voltage.h | 2 +- test/shared_test/lib_battery_voltage_test.cpp | 46 +++++++++---------- test/shared_test/lib_battery_voltage_test.h | 4 ++ test/shared_test/lib_resilience_test.cpp | 8 ++-- 5 files changed, 46 insertions(+), 37 deletions(-) diff --git a/shared/lib_battery_voltage.cpp b/shared/lib_battery_voltage.cpp index fd42afed4..8c53f3db7 100644 --- a/shared/lib_battery_voltage.cpp +++ b/shared/lib_battery_voltage.cpp @@ -181,7 +181,7 @@ voltage_t *voltage_table_t::clone() { return new voltage_table_t(*this); } -double voltage_table_t::calculate_voltage(double DOD) { +double voltage_table_t::calculate_voltage(double DOD, double I) { DOD = fmax(0., DOD); DOD = fmin(DOD, 100.); @@ -189,23 +189,27 @@ double voltage_table_t::calculate_voltage(double DOD) { while (row < params->voltage_table.size() && DOD > params->voltage_table[row][0]) { row++; } + // + if (DOD < tolerance || DOD > 100. - tolerance) { + I = 0.0; // At full or empty, current must go to zero + } - return fmax(slopes[row] * DOD + intercepts[row], 0); + return fmax(slopes[row] * DOD + intercepts[row], 0) - I * params->resistance; } void voltage_table_t::set_initial_SOC(double init_soc) { - state->cell_voltage = calculate_voltage(100. - init_soc); + state->cell_voltage = calculate_voltage(100. - init_soc, 0.0); } double voltage_table_t::calculate_voltage_for_current(double I, double q, double qmax, double) { double DOD = (q - I * params->dt_hr) / qmax * 100.; - return calculate_voltage(DOD) * params->num_cells_series; + return calculate_voltage(DOD, I / params->num_strings) * params->num_cells_series; } -void voltage_table_t::updateVoltage(double q, double qmax, double, const double, double) { +void voltage_table_t::updateVoltage(double q, double qmax, double I, const double, double) { double DOD = 100. * (1 - q / qmax); - state->cell_voltage = calculate_voltage(DOD); + state->cell_voltage = calculate_voltage(DOD, I / params->num_strings); } // helper fx to calculate depth of discharge from current and max capacities @@ -215,7 +219,7 @@ double voltage_table_t::calculate_max_charge_w(double q, double qmax, double, do double current = (q - qmax) / params->dt_hr; if (max_current) *max_current = current; - return calculate_voltage(0.) * current * params->num_cells_series; + return calculate_voltage(0., current / params->num_strings) * current * params->num_cells_series; } double voltage_table_t::calculate_max_discharge_w(double q, double qmax, double, double *max_current) { @@ -230,7 +234,7 @@ double voltage_table_t::calculate_max_discharge_w(double q, double qmax, double, dod = fmin(100, dod); dod = fmax(0, dod); double current = qmax * ((1. - DOD0 / 100.) - (1. - dod / 100.)) / params->dt_hr; - double p = calculate_voltage(dod) * current; + double p = calculate_voltage(dod, current / params->num_strings) * current; if (p > max_P) { max_P = p; max_I = current; @@ -291,7 +295,8 @@ double voltage_table_t::calculate_current_for_target_w(double P_watts, double q, auto DOD_upper = params->voltage_table[upper][0]; auto DOD_lower = params->voltage_table[lower][0]; if (DOD_new <= DOD_upper && DOD_new >= DOD_lower) { - double P = (q - (100. - DOD_new) * qmax/100) * (a * DOD_new + b); + current = qmax * ((1. - DOD / 100.) - (1. - DOD_new / 100.)) / params->dt_hr; + double P = current * (a * DOD_new + b - current / params->num_strings * params->resistance); if (std::abs(P) > std::abs(P_best)) { P_best = P; DOD_best = DOD_new; diff --git a/shared/lib_battery_voltage.h b/shared/lib_battery_voltage.h index 550ba98d0..9b069261f 100644 --- a/shared/lib_battery_voltage.h +++ b/shared/lib_battery_voltage.h @@ -165,7 +165,7 @@ class voltage_table_t : public voltage_t { std::vector slopes; std::vector intercepts; - double calculate_voltage(double DOD); + double calculate_voltage(double DOD, double I); private: void initialize(); diff --git a/test/shared_test/lib_battery_voltage_test.cpp b/test/shared_test/lib_battery_voltage_test.cpp index f50169b82..a3498ebf0 100644 --- a/test/shared_test/lib_battery_voltage_test.cpp +++ b/test/shared_test/lib_battery_voltage_test.cpp @@ -608,7 +608,7 @@ TEST_F(voltage_table_lib_battery_voltage_test, updateCapacityTest){ I = 5; cap->updateCapacity(I, dt_hour); // qmx = 10, I = 4.5, q0 = 0.5 model->updateVoltage(cap->q0(), cap->qmax(), cap->I(), 0, dt_hour); - EXPECT_NEAR(model->cell_voltage(), 1.35, tol); + EXPECT_NEAR(model->cell_voltage(), 1.336, tol); EXPECT_NEAR(cap->q0(), 0.5, tol); } @@ -631,7 +631,7 @@ TEST_F(voltage_table_lib_battery_voltage_test, updateCapacitySubHourly){ I = 5; cap->updateCapacity(I, dt_hour); // qmx = 10, I = 4.5, q0 = 0.5 model->updateVoltage(cap->q0(), cap->qmax(), cap->I(), 0, dt_hour); - EXPECT_NEAR(model->cell_voltage(), 3.504, tol); + EXPECT_NEAR(model->cell_voltage(), 3.492, tol); EXPECT_NEAR(cap->q0(), 2.5, tol); } @@ -654,7 +654,7 @@ TEST_F(voltage_table_lib_battery_voltage_test, updateCapacitySubMinute){ I = 5; cap->updateCapacity(I, dt_hour); // qmx = 10, I = 4.5, q0 = 0.5 model->updateVoltage(cap->q0(), cap->qmax(), cap->I(), 0, dt_hour); - EXPECT_NEAR(model->cell_voltage(), 3.689, tol); + EXPECT_NEAR(model->cell_voltage(), 3.677, tol); EXPECT_NEAR(cap->q0(), 4.975, 1e-3); } @@ -888,15 +888,15 @@ TEST_F(voltage_table_lib_battery_voltage_test, calculateMaxDischargeHourly){ // start at half SOC double max_current; double power = model->calculate_max_discharge_w(cap->q0(), cap->qmax(), 0, &max_current); - EXPECT_NEAR(power, 1194, 1); // current ~4 + EXPECT_NEAR(power, 1192.6, 1); // current ~4 double max_current_calc = model->calculate_current_for_target_w(power - 1, cap->q0(), cap->qmax(), 0); - EXPECT_NEAR(max_current_calc, 2.45, 1e-2); + EXPECT_NEAR(max_current_calc, 2.44, 1e-2); // Does not empty battery for highest power cap->updateCapacity(max_current, dt_hour); EXPECT_NEAR(cap->SOC(), 25.5, 1e-3); // Check power - model->updateVoltage(cap->q0(), cap->qmax(), max_current_calc, 0, dt_hour); - EXPECT_NEAR(max_current_calc * model->battery_voltage(), power, 2); + model->updateVoltage(cap->q0(), cap->qmax(), max_current, 0, dt_hour); + EXPECT_NEAR(max_current * model->battery_voltage(), power, 2); // start at empty SOC power = model->calculate_max_discharge_w(cap->q0(), cap->qmax(), 0, &max_current); @@ -915,15 +915,15 @@ TEST_F(voltage_table_lib_battery_voltage_test, calculateMaxDischargeHourly){ while (cap->SOC() < 95) cap->updateCapacity(I, dt_hour); power = model->calculate_max_discharge_w(cap->q0(), cap->qmax(), 0, &max_current); - EXPECT_NEAR(power, 3569, 1); + EXPECT_NEAR(power, 3553, 1); max_current_calc = model->calculate_current_for_target_w(power - 1, cap->q0(), cap->qmax(), 0); - EXPECT_NEAR(max_current_calc, max_current, 1e-2); + EXPECT_NEAR(max_current_calc, max_current, 1e-1); // Does not empty battery for highest power cap->updateCapacity(max_current, dt_hour); EXPECT_NEAR(cap->SOC(), 27.02, 1e-2); // Check power - model->updateVoltage(cap->q0(), cap->qmax(), max_current_calc, 0, dt_hour); - EXPECT_NEAR(max_current_calc * model->battery_voltage(), power, 2); + model->updateVoltage(cap->q0(), cap->qmax(), max_current, 0, dt_hour); + EXPECT_NEAR(max_current * model->battery_voltage(), power, 2); } TEST_F(voltage_table_lib_battery_voltage_test, calculateMaxDischargeHourlyInputErrors) { @@ -962,17 +962,17 @@ TEST_F(voltage_table_lib_battery_voltage_test, calculateMaxDischargeSubHourly){ // start at half SOC double max_current; double power = model->calculate_max_discharge_w(cap->q0(), cap->qmax(), 0, &max_current); - EXPECT_NEAR(power, 2388, 1); // current ~4 + EXPECT_NEAR(power, 2381, 1); // current ~4 double max_current_calc = model->calculate_current_for_target_w(power - 1, cap->q0(), cap->qmax(), 0); - EXPECT_NEAR(max_current, 4.9, 1e-2); - EXPECT_NEAR(max_current_calc, 4.9, 1e-2); + EXPECT_NEAR(max_current, 4.90, 1e-2); + EXPECT_NEAR(max_current_calc, 4.90, 1e-1); //4.88 as of 10/9/2023 // Does not empty battery for highest power cap->updateCapacity(max_current, dt_hour); EXPECT_NEAR(cap->SOC(), 25.5, 1e-3); // start at empty SOC power = model->calculate_max_discharge_w(cap->q0(), cap->qmax(), 0, &max_current); - EXPECT_NEAR(power, 1163, 1); + EXPECT_NEAR(power, 1161, 1); max_current_calc = model->calculate_current_for_target_w(power - 1, cap->q0(), cap->qmax(), 0); EXPECT_NEAR(max_current, 2.44, 1e-1); EXPECT_NEAR(max_current_calc, 2.44, 1e-1); @@ -985,9 +985,9 @@ TEST_F(voltage_table_lib_battery_voltage_test, calculateMaxDischargeSubHourly){ while (cap->SOC() < 95) cap->updateCapacity(I, dt_hour); power = model->calculate_max_discharge_w(cap->q0(), cap->qmax(), 0, &max_current); - EXPECT_NEAR(power, 7139, 1); + EXPECT_NEAR(power, 7073, 1); max_current_calc = model->calculate_current_for_target_w(power - 1, cap->q0(), cap->qmax(), 0); - EXPECT_NEAR(max_current_calc, max_current, 1e-2); + EXPECT_NEAR(max_current_calc, max_current, 2e-1); // Does not empty battery for highest power cap->updateCapacity(max_current, dt_hour); EXPECT_NEAR(cap->SOC(), 27.02, 1e-2); @@ -1000,7 +1000,7 @@ TEST_F(voltage_table_lib_battery_voltage_test, calculateMaxDischargeHourly_table // start at half SOC double max_current; double power = model->calculate_max_discharge_w(cap->q0(), cap->qmax(), 293, &max_current); - EXPECT_NEAR(power, 719.25, 1); // current ~4 + EXPECT_NEAR(power, 714.44, 1); // current ~4 double max_current_calc = model->calculate_current_for_target_w(power, cap->q0(), cap->qmax(), 293); EXPECT_NEAR(max_current, 3.94, 1e-2); EXPECT_NEAR(max_current_calc, 3.94, 1e-2); @@ -1028,7 +1028,7 @@ TEST_F(voltage_table_lib_battery_voltage_test, calculateMaxDischargeHourly_table while (cap->SOC() < 95) cap->updateCapacity(I, dt_hour); power = model->calculate_max_discharge_w(cap->q0(), cap->qmax(), 293, &max_current); - EXPECT_NEAR(power, 1480.35, 1); + EXPECT_NEAR(power, 1462.98, 1); max_current_calc = model->calculate_current_for_target_w(power, cap->q0(), cap->qmax(), 293); EXPECT_NEAR(max_current, 7.5, 1e-2); EXPECT_NEAR(max_current_calc, 7.5, 1e-2); @@ -1047,7 +1047,7 @@ TEST_F(voltage_table_lib_battery_voltage_test, calculateMaxDischargeSubMinute){ // start at half SOC double max_current; double power = model->calculate_max_discharge_w(cap->q0(), cap->qmax(), 0, &max_current); - EXPECT_NEAR(power, 238891, 1); // current ~8 + EXPECT_NEAR(power, 164727, 1); // current ~8 double max_current_calc = model->calculate_current_for_target_w(power, cap->q0(), cap->qmax(), 0); EXPECT_NEAR(max_current_calc, max_current, 0.2); // Does not empty battery for highest power @@ -1059,7 +1059,7 @@ TEST_F(voltage_table_lib_battery_voltage_test, calculateMaxDischargeSubMinute){ // start at empty SOC power = model->calculate_max_discharge_w(cap->q0(), cap->qmax(), 0, &max_current); - EXPECT_NEAR(power, 116333, 1); + EXPECT_NEAR(power, 97792, 1); max_current_calc = model->calculate_current_for_target_w(power, cap->q0(), cap->qmax(), 0); EXPECT_NEAR(max_current_calc, max_current, 1e-1); // Does not empty battery for highest power @@ -1074,12 +1074,12 @@ TEST_F(voltage_table_lib_battery_voltage_test, calculateMaxDischargeSubMinute){ while (cap->SOC() < 95) cap->updateCapacity(I, dt_hour); power = model->calculate_max_discharge_w(cap->q0(), cap->qmax(), 0, &max_current); - EXPECT_NEAR(power, 685795, 1); + EXPECT_NEAR(power, 207281, 1); max_current_calc = model->calculate_current_for_target_w(power, cap->q0(), cap->qmax(), 0); EXPECT_NEAR(max_current_calc, max_current, 0.3); // Does not empty battery for highest power cap->updateCapacity(max_current, dt_hour); - EXPECT_NEAR(cap->SOC(), 24.52, 1e-3); + EXPECT_NEAR(cap->SOC(), 48.0, 1e-3); // Check power model->updateVoltage(cap->q0(), cap->qmax(), max_current_calc, 293, dt_hour); EXPECT_NEAR(max_current_calc * model->battery_voltage(), power, 1e-2); diff --git a/test/shared_test/lib_battery_voltage_test.h b/test/shared_test/lib_battery_voltage_test.h index aa856ff3b..af506e69e 100644 --- a/test/shared_test/lib_battery_voltage_test.h +++ b/test/shared_test/lib_battery_voltage_test.h @@ -127,6 +127,8 @@ class voltage_table_lib_battery_voltage_test : public lib_battery_voltage_test 88.9, Vnom, 99, 0}); table = util::matrix_t(4, 2, &vals); + R = 0.02; + cap = std::unique_ptr(new capacity_lithium_ion_t(10, 50, 95, 5, dt_hr)); model = std::unique_ptr(new voltage_table_t(n_cells_series, n_strings, voltage_nom, table, R, dt_hr)); model->set_initial_SOC(50); @@ -137,6 +139,7 @@ class voltage_table_lib_battery_voltage_test : public lib_battery_voltage_test std::vector voltage_vals = { 0, 1.7, 4, 1.69, 5, 1.58, 60, 1.5, 85, 1.4, 90, 1.3, 93, 1.2, 95, 1, 96, 0.9 }; util::matrix_t voltage_table(9, 2, &voltage_vals); + R = 0.02; voltage_nom = 1.5; cap = std::unique_ptr(new capacity_lithium_ion_t(10, 50, 95, 5, dt_hr)); model = std::unique_ptr(new voltage_table_t(n_cells_series, n_strings, voltage_nom, voltage_table, R, @@ -174,6 +177,7 @@ class voltage_table_lib_battery_voltage_test : public lib_battery_voltage_test n_cells_series = 6; n_strings = 28; voltage_nom = 12; + R = 0.02; cap = std::unique_ptr(new capacity_lithium_ion_t(10, 50, 95, 5, dt_hr)); model = std::unique_ptr(new voltage_table_t(n_cells_series, n_strings, voltage_nom, voltage_table, R, diff --git a/test/shared_test/lib_resilience_test.cpp b/test/shared_test/lib_resilience_test.cpp index 9d72619cb..8531e2220 100644 --- a/test/shared_test/lib_resilience_test.cpp +++ b/test/shared_test/lib_resilience_test.cpp @@ -329,13 +329,13 @@ TEST_F(ResilienceTest_lib_resilience, VoltageTable) cap.updateCapacity(current, 1); volt.updateVoltage(cap.q0(), cap.qmax(), cap.I(), 0, 0.); EXPECT_NEAR(cap.SOC(), 44.445, 1e-3); - EXPECT_NEAR(volt.cell_voltage(), 1.773, 1e-3); + EXPECT_NEAR(volt.cell_voltage(), 1.873, 1e-3); current = -1; cap.updateCapacity(current, 1); volt.updateVoltage(cap.q0(), cap.qmax(), cap.I(), 0, 0.); EXPECT_NEAR(cap.SOC(), 88.889, 1e-3); - EXPECT_NEAR(volt.cell_voltage(), 2.777, 1e-3); + EXPECT_NEAR(volt.cell_voltage(), 2.877, 1e-3); } TEST_F(ResilienceTest_lib_resilience, DischargeVoltageTable){ @@ -359,7 +359,7 @@ TEST_F(ResilienceTest_lib_resilience, DischargeVoltageTable){ cap.updateCapacity(req_cur, 1); volt.updateVoltage(cap.q0(), cap.qmax(), cap.I(), 0, 1); double v = volt.cell_voltage(); - EXPECT_NEAR(req_cur * v, 0.5, 1e-2); + EXPECT_NEAR(req_cur * v, 0.48, 1e-2); // test max discharge cap = capacity_lithium_ion_t(2.25, 50, 100, 0, 1); @@ -397,7 +397,7 @@ TEST_F(ResilienceTest_lib_resilience, ChargeVoltageTable){ cap.updateCapacity(req_cur, 1); volt.updateVoltage(cap.q0(), cap.qmax(), cap.I(), 0, 1); double v = volt.cell_voltage(); - EXPECT_NEAR(req_cur * v, -1.5, 1e-2); + EXPECT_NEAR(req_cur * v, -1.58, 1e-2); // test max charge double max_p = volt.calculate_max_charge_w(cap.q0(), cap.qmax(), 0, ¤t); From 1e9835119a7a62de8b0a865f51e6556a2867b8a2 Mon Sep 17 00:00:00 2001 From: Janine Keith Date: Wed, 11 Oct 2023 16:08:20 -0600 Subject: [PATCH 3/9] added single timestep test of pvsamv1 where hour !=0 (#1065) --- test/ssc_test/cmod_pvsamv1_test.cpp | 44 +++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/ssc_test/cmod_pvsamv1_test.cpp b/test/ssc_test/cmod_pvsamv1_test.cpp index a5aa906e3..3cd1d3746 100644 --- a/test/ssc_test/cmod_pvsamv1_test.cpp +++ b/test/ssc_test/cmod_pvsamv1_test.cpp @@ -1273,6 +1273,8 @@ TEST_F(CMPvsamv1PowerIntegration_cmod_pvsamv1, NonAnnual) gen = ssc_data_get_array(data, "gen", nullptr)[12]; EXPECT_NEAR(gen, 3.078, 0.01) << "Gen at noon"; + + //free the weather data free_weatherdata_array(weather_data); } @@ -1310,6 +1312,48 @@ TEST_F(CMPvsamv1PowerIntegration_cmod_pvsamv1, NonAnnualWithLeapDay) free_weatherdata_array(weather_data); } +//test single timestep that doesn't start at hour 0 +TEST_F(CMPvsamv1PowerIntegration_cmod_pvsamv1, SingleTimestepNoon) +{ + //set up a weather data array containing a single data point at noon + const int length = 1; + double month[length] = { 6 }; + double day[length] = { 21 }; + double hour[length] = { 12 }; + double dn[length] = { 700 }; //set dn and df to a value to get power out + double df[length] = { 100 }; + + var_data month_vd = var_data(month, length); + var_data day_vd = var_data(day, length); + var_data hour_vd = var_data(hour, length); + var_data dn_vd = var_data(dn, length); + var_data df_vd = var_data(df, length); + + auto weather_data = create_weatherdata_array(length); + weather_data->assign("month", month_vd); + weather_data->assign("day", day_vd); + weather_data->assign("hour", hour_vd); + weather_data->assign("dn", dn_vd); + weather_data->assign("df", df_vd); + + ssc_data_unassign(data, "solar_resource_file"); + ssc_data_set_table(data, "solar_resource_data", weather_data); + + std::vector load(length, 1); + ssc_data_set_array(data, "load", &load[0], (int)load.size()); + + //run the tests + EXPECT_FALSE(run_module(data, "pvsamv1")); + + ssc_number_t dc_net, gen; + dc_net = ssc_data_get_array(data, "dc_net", nullptr)[0]; + EXPECT_NEAR(dc_net, 0.743, 0.01) << "DC Net Energy at noon"; + + gen = ssc_data_get_array(data, "gen", nullptr)[0]; + EXPECT_NEAR(gen, 0.704, 0.01) << "Gen at noon"; + free_weatherdata_array(weather_data); +} + //a couple of 8760 weather data array tests, one base case and one that includes Feb 29 but not Dec 31 TEST_F(CMPvsamv1PowerIntegration_cmod_pvsamv1, WeatherDataCases) From 54da8f09bff13f6347e8d7f18e349c98d10cba40 Mon Sep 17 00:00:00 2001 From: Matt Prilliman Date: Mon, 16 Oct 2023 11:14:16 -0500 Subject: [PATCH 4/9] Fix ssc equation docs for mhk equations --- ssc/cmod_mhk_eqns.h | 38 +++++++++++++++++++++++++++++++++++--- ssc/ssc_equations.h | 2 +- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/ssc/cmod_mhk_eqns.h b/ssc/cmod_mhk_eqns.h index 77c511047..d2756cbc5 100644 --- a/ssc/cmod_mhk_eqns.h +++ b/ssc/cmod_mhk_eqns.h @@ -53,18 +53,50 @@ static const char* me_array_cable_length_doc = SSCEXPORT bool me_array_cable_length(ssc_data_t data); +static const char* tidal_turbine_calculate_powercurve_doc = +"Calculates the tidal energy converter power output for tidal velocity bins in an ME array\\n" +"Input: var_table with key-value pairs\\n" +" 'tidal_turbine_rotor_diameter' - double [m]\\n" +" 'number_rotors' - integer [-]\\n" +" 'tidal_turbine_max_cp' - double [-]\\n" +" 'pto_efficiency' - double [%]\\n" +" 'cut_in' - double [m/s]\\n" +" 'cut_out' - double [m/s]\\n" +" 'tidal_resource' - matrix [-]\\n" +" 'generator_rated_capacity' - matrix [-]\\n" +"Output: key-value pairs added to var_table\\n" +" 'tidal_turbine_powercurve_tidespeeds' - array [m/s]\\n" +" 'tidal_turbine_powercurve_powerout' - array [kW]\\n" +" 'error - string [-]\\n"; + + SSCEXPORT bool tidal_turbine_calculate_powercurve(ssc_data_t data); static const char* me_array_cable_voltage_doc = "Calculates the cable voltages in an ME array\\n" "Input: var_table with key-value pairs\\n" " 'devices_per_row' - double [-]\\n" +" 'device_rated_power' - double [kW]\\n" +" 'system_capacity' - double [kW]\\n" " 'device_spacing_in_row' - double [m]\\n" -" 'number_rows' - double [-]\\n" " 'row_spacing' - double [m]\\n" -" 'cable_system_overbuild' - double [%]\\n" +" 'inter_array_cable_length' - double [m]\\n" +" 'riser_cable_length' - double [m]\\n" +" 'export_cable_length' - double [m]\\n" +" 'use_onshore_substation' - double [-]\\n" +" 'load_grid_voltage' - double [-]\\n" "Output: key-value pairs added to var_table\\n" -" 'inter_array_cable_voltage' - double [m]\\n"; +" 'array_cable_voltage' - double [V]\\n" +" 'array_cable_cost' - double [$]\\n" +" 'array_cable_cost_total' - double [$]\\n" +" 'export_cable_voltage' - double [V]\\n" +" 'export_cable_cost' - double [$]\\n" +" 'export_cable_cost_total' - double [$]\\n" +" 'riser_cable_voltage' - double [V]\\n" +" 'riser_cable_cost' - double [$]\\n" +" 'riser_cable_cost_total' - double [$]\\n" +" 'onshore_substation_cost_total' - double [$]\\n" +" 'offshore_substation_cost_total' - double [$]\\n"; SSCEXPORT bool me_array_cable_voltage(ssc_data_t data); diff --git a/ssc/ssc_equations.h b/ssc/ssc_equations.h index 3c3574fbf..a415ff107 100644 --- a/ssc/ssc_equations.h +++ b/ssc/ssc_equations.h @@ -86,7 +86,7 @@ static ssc_equation_entry ssc_equation_table [] = { "Marine energy", me_array_cable_length_doc, false, true}, {"tidal_turbine_calculate_powercurve", tidal_turbine_calculate_powercurve, - "Marine energy", me_array_cable_length_doc, + "Marine energy", tidal_turbine_calculate_powercurve_doc, false, true}, {"me_array_cable_voltage", me_array_cable_voltage, "Marine energy", me_array_cable_voltage_doc, From 49524b48e8fcc3b8755b6750a0ab5bde46e39faf Mon Sep 17 00:00:00 2001 From: Brian Mirletz Date: Mon, 16 Oct 2023 10:36:50 -0600 Subject: [PATCH 5/9] Ssc 1123 price signals to retail rate naming (#1064) * Rename confusing forecast enum to retail rate * rename additional comments and vartab info --- shared/lib_battery_dispatch.h | 2 +- shared/lib_battery_dispatch_automatic_btm.cpp | 6 +++--- shared/lib_battery_dispatch_automatic_btm.h | 4 ++-- shared/lib_utility_rate.cpp | 2 +- ssc/cmod_battery.cpp | 10 +++++----- .../lib_battery_dispatch_automatic_btm_test.cpp | 16 ++++++++-------- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/shared/lib_battery_dispatch.h b/shared/lib_battery_dispatch.h index 5fef65a53..eecb8ecb3 100644 --- a/shared/lib_battery_dispatch.h +++ b/shared/lib_battery_dispatch.h @@ -57,7 +57,7 @@ class dispatch_t public: enum FOM_MODES { FOM_AUTOMATED_ECONOMIC, FOM_PV_SMOOTHING, FOM_CUSTOM_DISPATCH, FOM_MANUAL }; - enum BTM_MODES { PEAK_SHAVING, MAINTAIN_TARGET, CUSTOM_DISPATCH, MANUAL, FORECAST, SELF_CONSUMPTION }; + enum BTM_MODES { PEAK_SHAVING, MAINTAIN_TARGET, CUSTOM_DISPATCH, MANUAL, RETAIL_RATE, SELF_CONSUMPTION }; enum METERING { BEHIND, FRONT }; enum WEATHER_FORECAST_CHOICE { WF_LOOK_AHEAD, WF_LOOK_BEHIND, WF_CUSTOM }; enum LOAD_FORECAST_CHOICE { LOAD_LOOK_AHEAD, LOAD_LOOK_BEHIND, LOAD_CUSTOM }; diff --git a/shared/lib_battery_dispatch_automatic_btm.cpp b/shared/lib_battery_dispatch_automatic_btm.cpp index 4dc065e18..8c1b5d640 100644 --- a/shared/lib_battery_dispatch_automatic_btm.cpp +++ b/shared/lib_battery_dispatch_automatic_btm.cpp @@ -173,7 +173,7 @@ double dispatch_automatic_behind_the_meter_t::power_grid_target() { return _P_ta void dispatch_automatic_behind_the_meter_t::setup_rate_forecast() { - if (_mode == dispatch_t::FORECAST) + if (_mode == dispatch_t::RETAIL_RATE) { forecast_setup rate_setup(_steps_per_hour, _nyears); @@ -196,7 +196,7 @@ void dispatch_automatic_behind_the_meter_t::update_dispatch(size_t year, size_t // [kWh] - the maximum energy that can be cycled double E_max = 0; - if (_mode == dispatch_t::FORECAST) + if (_mode == dispatch_t::RETAIL_RATE) { // Hourly rolling forecast horizon if ((hour_of_year != _hour_last_updated) || m_outage_manager->recover_from_outage) @@ -883,7 +883,7 @@ void dispatch_automatic_behind_the_meter_t::costToCycle() m_cycleCost = 0.01 * capacityPercentDamagePerCycle * m_battReplacementCostPerKWH[curr_year] * _Battery->get_params().nominal_energy; } else { - // Should only apply to BattWatts. BattWatts doesn't have price signal dispatch, so this is fine. + // Should only apply to BattWatts. BattWatts doesn't have retal rate dispatch, so this is fine. m_cycleCost = 0.0; } } diff --git a/shared/lib_battery_dispatch_automatic_btm.h b/shared/lib_battery_dispatch_automatic_btm.h index f59e50946..2d963a18a 100644 --- a/shared/lib_battery_dispatch_automatic_btm.h +++ b/shared/lib_battery_dispatch_automatic_btm.h @@ -37,7 +37,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "lib_utility_rate.h" /* - * Data for price signal dispatch (FORECAST) to compare dispatch plans in the cost_based_target_power function + * Data for retail rate dispatch to compare dispatch plans in the cost_based_target_power function */ struct dispatch_plan { @@ -154,7 +154,7 @@ class dispatch_automatic_behind_the_meter_t : public dispatch_automatic_t void target_power(double E_max, size_t idx, FILE* p = NULL, bool debug = false); void apply_target_power(size_t day_index); - /*! Functions used by price signal dispatch */ + /*! Functions used by retail rate dispatch */ double compute_costs(size_t idx, size_t year, size_t hour_of_year, FILE* p = NULL, bool debug = false); // Initial computation of no-dispatch costs, assigned hourly to grid points void cost_based_target_power(size_t idx, size_t year, size_t hour_of_year, double no_dispatch_cost, double E_max, FILE* p = NULL, const bool debug = false); // Optimizing loop, runs twelve possible dispatch scenarios void plan_dispatch_for_cost(dispatch_plan& plan, size_t idx, double E_max, double startingEnergy); // Generates each dispatch plan (input argument) diff --git a/shared/lib_utility_rate.cpp b/shared/lib_utility_rate.cpp index d192e2281..0092bb058 100644 --- a/shared/lib_utility_rate.cpp +++ b/shared/lib_utility_rate.cpp @@ -354,7 +354,7 @@ void UtilityRateForecast::initializeMonth(int month, size_t year) } else { // Standard demand charges - // Ignore any peak charges lower than the average gross load - this prevents the price signal from showing demand charges on the first hour of each month when the load is not really a peak + // Ignore any peak charges lower than the average gross load - this prevents the retail rate forcast from showing demand charges on the first hour of each month when the load is not really a peak double avg_load = m_monthly_avg_load_forecast[year * 12 + month]; curr_month.dc_flat_peak = avg_load; for (int period = 0; period < (int)curr_month.dc_periods.size(); period++) diff --git a/ssc/cmod_battery.cpp b/ssc/cmod_battery.cpp index 0840d463f..e2e334d3e 100644 --- a/ssc/cmod_battery.cpp +++ b/ssc/cmod_battery.cpp @@ -162,7 +162,7 @@ var_info vtab_battery_inputs[] = { { SSC_INPUT, SSC_ARRAY, "batt_target_power_monthly", "Grid target power on monthly basis", "kW", "", "BatteryDispatch", "en_batt=1&batt_meter_position=0&batt_dispatch_choice=1", "", "" }, { SSC_INPUT, SSC_NUMBER, "batt_target_choice", "Target power input option", "0/1", "0=InputMonthlyTarget,1=InputFullTimeSeries", "BatteryDispatch", "en_batt=1&en_standalone_batt=0&batt_meter_position=0&batt_dispatch_choice=1", "", "" }, { SSC_INPUT, SSC_ARRAY, "batt_custom_dispatch", "Custom battery power for every time step", "kW", "kWAC if AC-connected, else kWDC", "BatteryDispatch", "en_batt=1&en_standalone_batt=0&batt_dispatch_choice=2","", "" }, - { SSC_INPUT, SSC_NUMBER, "batt_dispatch_choice", "Battery dispatch algorithm", "0/1/2/3/4/5", "If behind the meter: 0=PeakShaving,1=InputGridTarget,2=InputBatteryPower,3=ManualDispatch,4=PriceSignalForecast,5=SelfConsumption if front of meter: 0=AutomatedEconomic,1=PV_Smoothing,2=InputBatteryPower,3=ManualDispatch", "BatteryDispatch", "en_batt=1", "", "" }, + { SSC_INPUT, SSC_NUMBER, "batt_dispatch_choice", "Battery dispatch algorithm", "0/1/2/3/4/5", "If behind the meter: 0=PeakShaving,1=InputGridTarget,2=InputBatteryPower,3=ManualDispatch,4=RetailRateDispatch,5=SelfConsumption if front of meter: 0=AutomatedEconomic,1=PV_Smoothing,2=InputBatteryPower,3=ManualDispatch", "BatteryDispatch", "en_batt=1", "", "" }, { SSC_INPUT, SSC_NUMBER, "batt_dispatch_auto_can_fuelcellcharge", "Charging from fuel cell allowed for automated dispatch?", "0/1", "", "BatteryDispatch", "", "", "" }, { SSC_INPUT, SSC_NUMBER, "batt_dispatch_auto_can_gridcharge", "Grid charging allowed for automated dispatch?", "0/1", "", "BatteryDispatch", "", "", "" }, { SSC_INPUT, SSC_NUMBER, "batt_dispatch_auto_can_charge", "System charging allowed for automated dispatch?", "0/1", "", "BatteryDispatch", "", "", "" }, @@ -1036,7 +1036,7 @@ battstor::battstor(var_table& vt, bool setup_model, size_t nrec, double dt_hr, c } - bool cycleCostRelevant = (batt_vars->batt_meter_position == dispatch_t::BEHIND && batt_vars->batt_dispatch == dispatch_t::FORECAST) || + bool cycleCostRelevant = (batt_vars->batt_meter_position == dispatch_t::BEHIND && batt_vars->batt_dispatch == dispatch_t::RETAIL_RATE) || (batt_vars->batt_meter_position == dispatch_t::FRONT && (batt_vars->batt_dispatch != dispatch_t::FOM_MANUAL && batt_vars->batt_dispatch != dispatch_t::FOM_CUSTOM_DISPATCH)); if (cycleCostRelevant && batt_vars->batt_cycle_cost_choice == dispatch_t::MODEL_CYCLE_COST) { outCostToCycle = vt.allocate("batt_cost_to_cycle", nrec * nyears); @@ -1383,7 +1383,7 @@ void battstor::parse_configuration() prediction_index = 0; if (batt_meter_position == dispatch_t::BEHIND) { - if (batt_dispatch == dispatch_t::PEAK_SHAVING || batt_dispatch == dispatch_t::MAINTAIN_TARGET || batt_dispatch == dispatch_t::FORECAST || + if (batt_dispatch == dispatch_t::PEAK_SHAVING || batt_dispatch == dispatch_t::MAINTAIN_TARGET || batt_dispatch == dispatch_t::RETAIL_RATE || batt_dispatch == dispatch_t::SELF_CONSUMPTION) { switch (batt_weather_forecast) { @@ -1781,7 +1781,7 @@ bool battstor::uses_forecast() { return batt_vars->batt_dispatch == dispatch_t::FOM_AUTOMATED_ECONOMIC || batt_vars->batt_dispatch == dispatch_t::FOM_PV_SMOOTHING; } else { - return batt_vars->batt_dispatch == dispatch_t::FORECAST || dispatch_t::PEAK_SHAVING; + return batt_vars->batt_dispatch == dispatch_t::RETAIL_RATE || dispatch_t::PEAK_SHAVING; } } @@ -1946,7 +1946,7 @@ void battstor::outputs_topology_dependent() } } - bool cycleCostRelevant = (batt_vars->batt_meter_position == dispatch_t::BEHIND && batt_vars->batt_dispatch == dispatch_t::FORECAST) || + bool cycleCostRelevant = (batt_vars->batt_meter_position == dispatch_t::BEHIND && batt_vars->batt_dispatch == dispatch_t::RETAIL_RATE) || (batt_vars->batt_meter_position == dispatch_t::FRONT && (batt_vars->batt_dispatch != dispatch_t::FOM_MANUAL && batt_vars->batt_dispatch != dispatch_t::FOM_CUSTOM_DISPATCH)); if (cycleCostRelevant && batt_vars->batt_cycle_cost_choice == dispatch_t::MODEL_CYCLE_COST) { outCostToCycle[index] = (ssc_number_t)(dispatch_model->cost_to_cycle_per_kwh()); diff --git a/test/shared_test/lib_battery_dispatch_automatic_btm_test.cpp b/test/shared_test/lib_battery_dispatch_automatic_btm_test.cpp index 77d1a7781..1194fc7e4 100644 --- a/test/shared_test/lib_battery_dispatch_automatic_btm_test.cpp +++ b/test/shared_test/lib_battery_dispatch_automatic_btm_test.cpp @@ -308,7 +308,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, TestBasicForecast) { dispatchAutoBTM = new dispatch_automatic_behind_the_meter_t(batteryModel, dtHour, SOC_min, SOC_max, currentChoice, max_current, max_current, max_power, max_power, max_power, max_power, - 0, dispatch_t::BTM_MODES::FORECAST, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, + 0, dispatch_t::BTM_MODES::RETAIL_RATE, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); @@ -364,7 +364,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, TestSummerPeak) { dispatchAutoBTM = new dispatch_automatic_behind_the_meter_t(batteryModel, dtHour, SOC_min, SOC_max, currentChoice, max_current, max_current, max_power, max_power, max_power, max_power, - 0, dispatch_t::BTM_MODES::FORECAST, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, + 0, dispatch_t::BTM_MODES::RETAIL_RATE, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); @@ -404,7 +404,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, TestSummerPeakNetMeteringCredits) { dispatchAutoBTM = new dispatch_automatic_behind_the_meter_t(batteryModel, dtHour, SOC_min, SOC_max, currentChoice, max_current, max_current, max_power, max_power, max_power, max_power, - 0, dispatch_t::BTM_MODES::FORECAST, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, + 0, dispatch_t::BTM_MODES::RETAIL_RATE, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); @@ -444,7 +444,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, TestSummerPeakGridCharging) { dispatchAutoBTM = new dispatch_automatic_behind_the_meter_t(batteryModel, dtHour, SOC_min, SOC_max, currentChoice, max_current, max_current, max_power, max_power, max_power, max_power, - 0, dispatch_t::BTM_MODES::FORECAST, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, + 0, dispatch_t::BTM_MODES::RETAIL_RATE, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, true, canGridCharge, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); @@ -485,7 +485,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, TestSummerPeakGridChargingSubhourly) { dispatchAutoBTM = new dispatch_automatic_behind_the_meter_t(batteryModel, dtHour, SOC_min, SOC_max, currentChoice, max_current, max_current, max_power, max_power, max_power, max_power, - 0, dispatch_t::BTM_MODES::FORECAST, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, + 0, dispatch_t::BTM_MODES::RETAIL_RATE, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, true, canGridCharge, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); @@ -539,7 +539,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, TestCommercialPeakForecasting) { dispatchAutoBTM = new dispatch_automatic_behind_the_meter_t(batteryModel, dtHour, SOC_min, SOC_max, currentChoice, max_current, max_current, max_power, max_power, max_power, max_power, - 0, dispatch_t::BTM_MODES::FORECAST, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, + 0, dispatch_t::BTM_MODES::RETAIL_RATE, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, true, true, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); @@ -1224,7 +1224,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMGridOutagePeakShavingEmp } -TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMGridOutagePriceSignalsEmptyAndFull) { +TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMGridOutageRetailRAteEmptyAndFull) { double dtHour = 1; CreateBattery(dtHour); @@ -1236,7 +1236,7 @@ TEST_F(AutoBTMTest_lib_battery_dispatch, DispatchAutoBTMGridOutagePriceSignalsEm dispatchAutoBTM = new dispatch_automatic_behind_the_meter_t(batteryModel, dtHour, SOC_min, SOC_max, currentChoice, max_current, max_current, max_power * defaultEff, max_power / defaultEff, max_power, max_power, - 0, dispatch_t::BTM_MODES::FORECAST, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, + 0, dispatch_t::BTM_MODES::RETAIL_RATE, dispatch_t::WEATHER_FORECAST_CHOICE::WF_LOOK_AHEAD, 0, 1, 24, 1, true, true, false, false, util_rate, replacementCost, cyclingChoice, cyclingCost, omCost, interconnection_limit, chargeOnlySystemExceedLoad, dischargeOnlyLoadExceedSystem, dischargeToGrid, min_outage_soc, dispatch_t::LOAD_FORECAST_CHOICE::LOAD_LOOK_AHEAD); From 1763c4d870325d4735fb853324eee5cb568c3625 Mon Sep 17 00:00:00 2001 From: Matt Prilliman Date: Mon, 16 Oct 2023 13:13:44 -0500 Subject: [PATCH 6/9] Add missing initialization of distance_to_shore --- ssc/cmod_mhk_eqns.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ssc/cmod_mhk_eqns.cpp b/ssc/cmod_mhk_eqns.cpp index d9d2001a8..50db50120 100644 --- a/ssc/cmod_mhk_eqns.cpp +++ b/ssc/cmod_mhk_eqns.cpp @@ -178,7 +178,7 @@ bool me_array_cable_voltage(ssc_data_t data) { return false; } - double devices_per_row, device_spacing_in_row, number_rows, row_spacing, cable_system_overbuild, floating_array, export_cable_redundancy, water_depth, number_devices, distance_to_shore; + double devices_per_row, device_spacing_in_row, distance_to_shore; double device_rated_power, system_capacity, inter_array_cable_length, riser_cable_length, export_cable_length; double use_onshore_substation, load_grid_voltage; vt_get_number(vt, "devices_per_row", &devices_per_row); @@ -190,7 +190,7 @@ bool me_array_cable_voltage(ssc_data_t data) { vt_get_number(vt, "export_cable_length", &export_cable_length); vt_get_number(vt, "use_onshore_substation", &use_onshore_substation); vt_get_number(vt, "load_grid_voltage", &load_grid_voltage); - + vt_get_number(vt, "distance_to_shore", &distance_to_shore); double PF = 0.95; //Power Factor double angle = acos(PF); From daaf3920c5dde48156614d897edf9b97f477f826 Mon Sep 17 00:00:00 2001 From: Matt Prilliman Date: Mon, 16 Oct 2023 15:48:17 -0500 Subject: [PATCH 7/9] Initialize variables --- ssc/cmod_mhk_eqns.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/ssc/cmod_mhk_eqns.cpp b/ssc/cmod_mhk_eqns.cpp index 50db50120..f6f72280f 100644 --- a/ssc/cmod_mhk_eqns.cpp +++ b/ssc/cmod_mhk_eqns.cpp @@ -45,7 +45,7 @@ bool me_array_cable_length(ssc_data_t data) return false; } - double devices_per_row, device_spacing_in_row, number_rows, row_spacing, cable_system_overbuild, floating_array, export_cable_redundancy, water_depth, number_devices, distance_to_shore; + double devices_per_row, device_spacing_in_row, number_rows, row_spacing, cable_system_overbuild, floating_array, export_cable_redundancy, water_depth, number_devices, distance_to_shore = 0; vt_get_number(vt, "devices_per_row", &devices_per_row); vt_get_number(vt, "device_spacing_in_row", &device_spacing_in_row); @@ -96,12 +96,9 @@ bool tidal_turbine_calculate_powercurve(ssc_data_t data) return false; } - double turbine_size, rotor_diameter, elevation, max_tip_speed, max_tip_sp_ratio, cut_in, - cut_out, rotor_area, generator_rated_capacity, water_depth, velocity_power_law_fit, number_rotors; - int drive_train; + double rotor_diameter, cut_in, + cut_out, rotor_area, generator_rated_capacity, number_rotors = 0; util::matrix_t tidal_resource; - double min_vel; - int max_cp_length, pto_efficiency_length; std::vector pto_efficiency; std::vector max_cp; @@ -178,9 +175,9 @@ bool me_array_cable_voltage(ssc_data_t data) { return false; } - double devices_per_row, device_spacing_in_row, distance_to_shore; - double device_rated_power, system_capacity, inter_array_cable_length, riser_cable_length, export_cable_length; - double use_onshore_substation, load_grid_voltage; + double devices_per_row, device_spacing_in_row, distance_to_shore = 0; + double device_rated_power, system_capacity, inter_array_cable_length, riser_cable_length, export_cable_length = 0; + double use_onshore_substation, load_grid_voltage = 0; vt_get_number(vt, "devices_per_row", &devices_per_row); vt_get_number(vt, "device_rated_power", &device_rated_power); vt_get_number(vt, "system_capacity", &system_capacity); From 6da17cea9276227456cd925c386419301f875a40 Mon Sep 17 00:00:00 2001 From: Matt Prilliman Date: Mon, 16 Oct 2023 15:57:28 -0500 Subject: [PATCH 8/9] Add else statements to fix control path not returning value error --- ssc/cmod_mhk_eqns.cpp | 94 +++++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 44 deletions(-) diff --git a/ssc/cmod_mhk_eqns.cpp b/ssc/cmod_mhk_eqns.cpp index f6f72280f..de8b8950b 100644 --- a/ssc/cmod_mhk_eqns.cpp +++ b/ssc/cmod_mhk_eqns.cpp @@ -208,22 +208,22 @@ bool me_array_cable_voltage(ssc_data_t data) { } vt->assign("export_cable_type", export_cable_type); //Riser Cable - double riser_cable_voltage = 0; - double riser_cable_cost = 0; //$/m - if ( array_cable_rated_power_per_row < 4) { + double riser_cable_voltage = 0.0; + double riser_cable_cost = 0.0; //$/m + if ( array_cable_rated_power_per_row < 4.0) { riser_cable_voltage = 7.2; riser_cable_cost = 57.955 * riser_cable_rated_power_per_device; } - else if (riser_cable_rated_power_per_device >= 5 && riser_cable_rated_power_per_device < 9) { - riser_cable_voltage = 12; + else if (riser_cable_rated_power_per_device >= 5.0 && riser_cable_rated_power_per_device < 9.0) { + riser_cable_voltage = 12.0; riser_cable_cost = 47.214 * riser_cable_rated_power_per_device - 91.05; } - else if (riser_cable_rated_power_per_device >= 9 && riser_cable_rated_power_per_device < 14) { - riser_cable_voltage = 24; + else if (riser_cable_rated_power_per_device >= 9.0 && riser_cable_rated_power_per_device < 14.0) { + riser_cable_voltage = 24.0; riser_cable_cost = 22.748 * riser_cable_rated_power_per_device - 68.376; } - else if (riser_cable_rated_power_per_device >= 14) { - riser_cable_voltage = 36; + else { + riser_cable_voltage = 36.0; riser_cable_cost = 20.82 * riser_cable_rated_power_per_device - 163.14; } vt->assign("riser_cable_voltage", riser_cable_voltage); @@ -232,26 +232,26 @@ bool me_array_cable_voltage(ssc_data_t data) { vt->assign("riser_cable_cost_total", riser_cable_cost_total); //Array Cable - double array_cable_voltage = 0; - double array_cable_cost = 0; - if ( array_cable_rated_power_per_row < 4) { + double array_cable_voltage = 0.0; + double array_cable_cost = 0.0; + if ( array_cable_rated_power_per_row < 4.0) { array_cable_voltage = 7.2; array_cable_cost = 44.245 * array_cable_rated_power_per_row; } - else if ( array_cable_rated_power_per_row >= 4 && array_cable_rated_power_per_row < 9) { - array_cable_voltage = 12; + else if ( array_cable_rated_power_per_row >= 4.0 && array_cable_rated_power_per_row < 9.0) { + array_cable_voltage = 12.0; array_cable_cost = 31.029 * array_cable_rated_power_per_row - 40.744; } - else if ( array_cable_rated_power_per_row >= 9 && array_cable_rated_power_per_row < 14) { - array_cable_voltage = 24; + else if ( array_cable_rated_power_per_row >= 9.0 && array_cable_rated_power_per_row < 14.0) { + array_cable_voltage = 24.0; array_cable_cost = 17.348 * array_cable_rated_power_per_row - 61.467; } - else if ( array_cable_rated_power_per_row >= 14 && array_cable_rated_power_per_row < 30) { - array_cable_voltage = 36; + else if ( array_cable_rated_power_per_row >= 14.0 && array_cable_rated_power_per_row < 30.0) { + array_cable_voltage = 36.0; array_cable_cost = 13.791 * array_cable_rated_power_per_row - 93.272; } - else if (array_cable_rated_power_per_row >= 30) { - array_cable_voltage = 66; + else { + array_cable_voltage = 66.0; array_cable_cost = 11.984 * array_cable_rated_power_per_row - 155.97; } vt->assign("array_cable_voltage", array_cable_voltage); @@ -264,62 +264,62 @@ bool me_array_cable_voltage(ssc_data_t data) { double export_cable_cost = 0; double offshore_substation_voltage = 0; if (export_cable_type == 0) { - if (export_cable_rated_power_array_ac < 4) { + if (export_cable_rated_power_array_ac < 4.0) { export_cable_voltage = 7.2; export_cable_cost = 44.245 * export_cable_rated_power_array_ac; offshore_substation_voltage = 8; //kVAC } - else if (export_cable_rated_power_array_ac >= 4 && export_cable_rated_power_array_ac < 9) { + else if (export_cable_rated_power_array_ac >= 4.0 && export_cable_rated_power_array_ac < 9.0) { export_cable_voltage = 12; export_cable_cost = 31.029 * export_cable_rated_power_array_ac - 40.744; offshore_substation_voltage = 15; //kVAC } - else if (export_cable_rated_power_array_ac >= 9 && export_cable_rated_power_array_ac < 14) { + else if (export_cable_rated_power_array_ac >= 9.0 && export_cable_rated_power_array_ac < 14.0) { export_cable_voltage = 24; export_cable_cost = 17.348 * export_cable_rated_power_array_ac - 61.467; offshore_substation_voltage = 25; //kVAC } - else if (export_cable_rated_power_array_ac >= 14 && export_cable_rated_power_array_ac < 30) { + else if (export_cable_rated_power_array_ac >= 14.0 && export_cable_rated_power_array_ac < 30.0) { export_cable_voltage = 36; export_cable_cost = 13.791 * export_cable_rated_power_array_ac - 93.272; offshore_substation_voltage = 46; //kVAC } - else if (export_cable_rated_power_array_ac >= 30 && export_cable_rated_power_array_ac < 40) { + else if (export_cable_rated_power_array_ac >= 30.0 && export_cable_rated_power_array_ac < 40.0) { export_cable_voltage = 66; array_cable_cost = 11.984 * export_cable_rated_power_array_ac - 155.97; offshore_substation_voltage = 69; //kVAC } - else if (export_cable_rated_power_array_ac >= 40 && export_cable_rated_power_array_ac < 121) { + else if (export_cable_rated_power_array_ac >= 40.0 && export_cable_rated_power_array_ac < 121.0) { export_cable_voltage = 72.5; array_cable_cost = 9.8977 * export_cable_rated_power_array_ac - 195.75; offshore_substation_voltage = 115; //kVAC } - else if (export_cable_rated_power_array_ac >= 121 && export_cable_rated_power_array_ac < 250) { + else if (export_cable_rated_power_array_ac >= 121.0 && export_cable_rated_power_array_ac < 250.0) { export_cable_voltage = 145; array_cable_cost = 10.046 * export_cable_rated_power_array_ac - 886.49; offshore_substation_voltage = 161; //kVAC } - else if (export_cable_rated_power_array_ac >= 250 && export_cable_rated_power_array_ac < 550) { - export_cable_voltage = 220; + else if (export_cable_rated_power_array_ac >= 250.0 && export_cable_rated_power_array_ac < 550.0) { + export_cable_voltage = 220.0; array_cable_cost = 5.2937 * export_cable_rated_power_array_ac - 318.15; - offshore_substation_voltage = 230; //kVAC + offshore_substation_voltage = 230.0; //kVAC } - else if (export_cable_rated_power_array_ac >= 550) { - export_cable_voltage = 400; + else { + export_cable_voltage = 400.0; array_cable_cost = 7.7566 * export_cable_rated_power_array_ac - 2704.6; - offshore_substation_voltage = 415; //kVAC + offshore_substation_voltage = 415.0; //kVAC } } else { - if (export_cable_rated_power_array_hvdc < 500) { - export_cable_voltage = 150; + if (export_cable_rated_power_array_hvdc < 500.0) { + export_cable_voltage = 150.0; export_cable_cost = 2.5026 * export_cable_rated_power_array_hvdc; - offshore_substation_voltage = 161; //kV HVDC + offshore_substation_voltage = 161.0; //kV HVDC } else { - export_cable_voltage = 300; + export_cable_voltage = 300.0; export_cable_cost = 2.0375 * export_cable_rated_power_array_hvdc - 516.02; - offshore_substation_voltage = 345; //kVAC + offshore_substation_voltage = 345.0; //kVAC } } vt->assign("export_cable_voltage", export_cable_voltage); @@ -339,14 +339,17 @@ bool me_array_cable_voltage(ssc_data_t data) { double static_var_compensator_cost = 105060 * reactive_power; //HVDC Electrical equipment double hvdc_converter_station_cost = 142.61 * system_capacity; - double offshore_substation_cost_total = 0; - if (array_cable_voltage != export_cable_voltage && export_cable_type == 0) { + double offshore_substation_cost_total = 0.0; + if (array_cable_voltage != export_cable_voltage && export_cable_type == 0.0) { offshore_substation_cost_total = offshore_foundation_cost + circuit_breaker_cost + ac_switchgear_cost + transformer_cost + shunt_reactor_cost + series_capacitor_cost + static_var_compensator_cost; } - else if (array_cable_voltage != export_cable_voltage && export_cable_type == 1) { + else if (array_cable_voltage != export_cable_voltage && export_cable_type == 1.0) { offshore_substation_cost_total = offshore_foundation_cost + hvdc_converter_station_cost; } + else { + //do nothing + } vt->assign("offshore_substation_cost_total", offshore_substation_cost_total); //Onshore substation @@ -360,14 +363,17 @@ bool me_array_cable_voltage(ssc_data_t data) { double onshore_static_var_compensator_cost = 105060 * reactive_power; //HVDC Electrical equipment double onshore_hvdc_converter_station_cost = 142.61 * system_capacity; - double onshore_substation_cost_total = 0; - if (use_onshore_substation==0 && export_cable_type == 0) { + double onshore_substation_cost_total = 0.0; + if (use_onshore_substation== 0.0 && export_cable_type == 0.0) { onshore_substation_cost_total = onshore_foundation_cost + onshore_circuit_breaker_cost + onshore_ac_switchgear_cost + onshore_transformer_cost + onshore_shunt_reactor_cost + onshore_series_capacitor_cost + onshore_static_var_compensator_cost; } - else if (use_onshore_substation==0 && export_cable_type == 1) { + else if (use_onshore_substation== 0.0 && export_cable_type == 1.0) { onshore_substation_cost_total = onshore_foundation_cost + onshore_hvdc_converter_station_cost; } + else { + //do nothing + } vt->assign("onshore_substation_cost_total", onshore_substation_cost_total); } From a84f45e41f887624a4d029cad18db501e0146ed2 Mon Sep 17 00:00:00 2001 From: Matt Prilliman Date: Mon, 16 Oct 2023 16:03:11 -0500 Subject: [PATCH 9/9] Return status variable for array cable voltage ssc equation --- ssc/cmod_mhk_eqns.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ssc/cmod_mhk_eqns.cpp b/ssc/cmod_mhk_eqns.cpp index de8b8950b..5f20a71e1 100644 --- a/ssc/cmod_mhk_eqns.cpp +++ b/ssc/cmod_mhk_eqns.cpp @@ -349,6 +349,7 @@ bool me_array_cable_voltage(ssc_data_t data) { } else { //do nothing + offshore_substation_cost_total = 0.0; } vt->assign("offshore_substation_cost_total", offshore_substation_cost_total); @@ -375,6 +376,7 @@ bool me_array_cable_voltage(ssc_data_t data) { //do nothing } vt->assign("onshore_substation_cost_total", onshore_substation_cost_total); + return true; }