Skip to content

Commit

Permalink
Add internal resistance to voltage table calculations (#1016)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
Co-authored-by: Paul Gilman <[email protected]>
Co-authored-by: Matthew Boyd <[email protected]>
  • Loading branch information
4 people authored Oct 10, 2023
1 parent 55840e7 commit 25572cc
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 37 deletions.
23 changes: 14 additions & 9 deletions shared/lib_battery_voltage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,31 +181,35 @@ 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.);

size_t row = 0;
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
Expand All @@ -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) {
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion shared/lib_battery_voltage.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ class voltage_table_t : public voltage_t {
std::vector<double> slopes;
std::vector<double> intercepts;

double calculate_voltage(double DOD);
double calculate_voltage(double DOD, double I);

private:
void initialize();
Expand Down
46 changes: 23 additions & 23 deletions test/shared_test/lib_battery_voltage_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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);
}

Expand All @@ -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);
}

Expand Down Expand Up @@ -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);
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions test/shared_test/lib_battery_voltage_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<double>(4, 2, &vals);

R = 0.02;

cap = std::unique_ptr<capacity_lithium_ion_t>(new capacity_lithium_ion_t(10, 50, 95, 5, dt_hr));
model = std::unique_ptr<voltage_t>(new voltage_table_t(n_cells_series, n_strings, voltage_nom, table, R, dt_hr));
model->set_initial_SOC(50);
Expand All @@ -137,6 +139,7 @@ class voltage_table_lib_battery_voltage_test : public lib_battery_voltage_test
std::vector<double> 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<double> voltage_table(9, 2, &voltage_vals);

R = 0.02;
voltage_nom = 1.5;
cap = std::unique_ptr<capacity_lithium_ion_t>(new capacity_lithium_ion_t(10, 50, 95, 5, dt_hr));
model = std::unique_ptr<voltage_t>(new voltage_table_t(n_cells_series, n_strings, voltage_nom, voltage_table, R,
Expand Down Expand Up @@ -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<capacity_lithium_ion_t>(new capacity_lithium_ion_t(10, 50, 95, 5, dt_hr));
model = std::unique_ptr<voltage_t>(new voltage_table_t(n_cells_series, n_strings, voltage_nom, voltage_table, R,
Expand Down
8 changes: 4 additions & 4 deletions test/shared_test/lib_resilience_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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){
Expand All @@ -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);
Expand Down Expand Up @@ -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, &current);
Expand Down

0 comments on commit 25572cc

Please sign in to comment.