Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Sam 1869 battery adjust losses #1241

Merged
merged 26 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
687e0c3
Add a few required variables. Need to make a lot of decisions to use …
brtietz Oct 22, 2024
54cd4dc
Merge branch 'develop' of https://github.com/NREL/ssc into sam_1869_b…
brtietz Oct 28, 2024
12988d2
Merge branch 'develop' of https://github.com/NREL/ssc into sam_1869_b…
brtietz Nov 5, 2024
fe43909
Merge branch 'develop' of https://github.com/NREL/ssc into sam_1869_b…
brtietz Nov 5, 2024
d9120a4
Merge branch 'fix_haf_for_custom_generation' into sam_1869_battery_ad…
brtietz Nov 5, 2024
efd4d87
Implement adjust losses within the capacity model. Not yet fully hook…
brtietz Nov 6, 2024
e1b08df
mid-stream updates for power losses
brtietz Nov 6, 2024
8f747ed
Merge branch 'develop' of https://github.com/NREL/ssc into sam_1869_b…
brtietz Nov 6, 2024
638998f
First cut change of definition for powerflow AC losses to match new c…
brtietz Nov 6, 2024
7bb945e
Add adjust losses to batt stateful, clean up cmod batt to fix seh exc…
brtietz Nov 7, 2024
f8a604b
Test updates for new capacity and availability loss conventions
brtietz Nov 7, 2024
559122e
First cut at availability losses testing. Revealed that DC battery st…
brtietz Nov 7, 2024
282d2a6
Add automated and manual dispatch tests, add current restriction to a…
brtietz Nov 7, 2024
ebc31f7
Pass data through all compute modules to losses and powerflow code
brtietz Nov 8, 2024
bd44be4
Add fallback code if batt_adjust is not defined
brtietz Nov 8, 2024
e8fd9ed
Refactor max power and current to be functions instead of calculating…
brtietz Nov 8, 2024
7beb55e
Merge branch 'develop' of https://github.com/NREL/ssc into sam_1869_b…
brtietz Nov 11, 2024
2ac26d8
Fix typo in manual dispatch power vs current
brtietz Nov 11, 2024
efb7743
Run adjustment code based on which variables are actually available
brtietz Nov 11, 2024
d05b4f7
Improved error handling for battery adjust losses
brtietz Nov 11, 2024
3cb0df8
Pass analysis period to haf setup function
brtietz Nov 12, 2024
e06f183
Use correct nyears to account for system lifetime mode == 0
brtietz Nov 13, 2024
2bb5351
Add output for total performance adjustment loss at a given timestep
brtietz Nov 13, 2024
cc88f4e
Merge branch 'develop' of https://github.com/NREL/ssc into sam_1869_b…
brtietz Nov 13, 2024
a87f41d
Make ssc variable labels consistent with UI forms, help, and the loss…
brtietz Nov 13, 2024
3ed4abe
bring cmod battery in line with name chage
brtietz Nov 13, 2024
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
38 changes: 26 additions & 12 deletions shared/lib_battery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ Define Losses
*/
void losses_t::initialize() {
state = std::make_shared<losses_state>();
state->loss_kw = 0;
state->ancillary_loss_kw = 0;
if (params->loss_choice == losses_params::MONTHLY) {
if (params->monthly_charge_loss.size() == 1) {
params->monthly_charge_loss = std::vector<double>(12, params->monthly_charge_loss[0]);
Expand Down Expand Up @@ -210,19 +210,21 @@ void losses_t::initialize() {
}
}

losses_t::losses_t(const std::vector<double>& monthly_charge, const std::vector<double>& monthly_discharge, const std::vector<double>& monthly_idle) {
losses_t::losses_t(const std::vector<double>& monthly_charge, const std::vector<double>& monthly_discharge, const std::vector<double>& monthly_idle, const std::vector<double>& adjust_losses) {
params = std::make_shared<losses_params>();
params->loss_choice = losses_params::MONTHLY;
params->monthly_charge_loss = monthly_charge;
params->monthly_discharge_loss = monthly_discharge;
params->monthly_idle_loss = monthly_idle;
params->adjust_loss = adjust_losses;
initialize();
}

losses_t::losses_t(const std::vector<double>& schedule_loss) {
losses_t::losses_t(const std::vector<double>& schedule_loss, const std::vector<double>& adjust_losses) {
params = std::make_shared<losses_params>();
params->loss_choice = losses_params::SCHEDULE;
params->schedule_loss = schedule_loss;
params->adjust_loss = adjust_losses;
initialize();
}

Expand Down Expand Up @@ -252,18 +254,24 @@ void losses_t::run_losses(size_t lifetimeIndex, double dtHour, double charge_ope
// update system losses depending on user input
if (params->loss_choice == losses_params::MONTHLY) {
if (charge_operation == capacity_state::CHARGE)
state->loss_kw = params->monthly_charge_loss[monthIndex];
state->ancillary_loss_kw = params->monthly_charge_loss[monthIndex];
if (charge_operation == capacity_state::DISCHARGE)
state->loss_kw = params->monthly_discharge_loss[monthIndex];
state->ancillary_loss_kw = params->monthly_discharge_loss[monthIndex];
if (charge_operation == capacity_state::NO_CHARGE)
state->loss_kw = params->monthly_idle_loss[monthIndex];
state->ancillary_loss_kw = params->monthly_idle_loss[monthIndex];
}
else if (params->loss_choice == losses_params::SCHEDULE) {
state->loss_kw = params->schedule_loss[lifetimeIndex % params->schedule_loss.size()];
state->ancillary_loss_kw = params->schedule_loss[lifetimeIndex % params->schedule_loss.size()];
}

state->adjust_loss_percent = getAvailabilityLoss(lifetimeIndex);
}

double losses_t::getLoss() { return state->loss_kw; }
double losses_t::getAncillaryLoss() { return state->ancillary_loss_kw; }

double losses_t::getAvailabilityLoss(size_t lifetimeIndex) {
return params->adjust_loss[lifetimeIndex % params->adjust_loss.size()];
}

losses_state losses_t::get_state() { return *state; }

Expand Down Expand Up @@ -584,7 +592,7 @@ double battery_t::run(size_t lifetimeIndex, double &I) {

while (iterate_count < 5) {
runThermalModel(I, lifetimeIndex);
runCapacityModel(I);
runCapacityModel(I, lifetimeIndex);

double numerator = std::abs(I - I_initial);
if ((numerator > 0.0) && (numerator / std::abs(I_initial) > tolerance)) {
Expand Down Expand Up @@ -622,12 +630,14 @@ double battery_t::estimateCycleDamage() {
return lifetime->estimateCycleDamage();
}

void battery_t::runCapacityModel(double &I) {
void battery_t::runCapacityModel(double &I, size_t lifetimeIndex) {
// Don't update max capacity if the battery is idle
if (std::abs(I) > tolerance) {
// Need to first update capacity model to ensure temperature accounted for
capacity->updateCapacityForThermal(thermal->capacity_percent());
}
double availability_loss = losses->getAvailabilityLoss(lifetimeIndex);
capacity->updateCapacityForAvailability(availability_loss);
capacity->updateCapacity(I, params->dt_hr);
}

Expand Down Expand Up @@ -772,8 +782,12 @@ double battery_t::calculate_loss(double power, size_t lifetimeIndex) {
}
}

double battery_t::getLoss() {
return losses->getLoss();
double battery_t::getAncillaryLoss() {
return losses->getAncillaryLoss();
}

double battery_t::getAvailabilityLoss(size_t lifetimeIndex) {
return losses->getAvailabilityLoss(lifetimeIndex);
}

battery_state battery_t::get_state() { return *state; }
Expand Down
21 changes: 15 additions & 6 deletions shared/lib_battery.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ class thermal_t {
*/

struct losses_state {
double loss_kw;
double ancillary_loss_kw;
double adjust_loss_percent;

friend std::ostream &operator<<(std::ostream &os, const losses_state &p);
};
Expand All @@ -166,6 +167,7 @@ struct losses_params {
std::vector<double> monthly_discharge_loss;
std::vector<double> monthly_idle_loss;
std::vector<double> schedule_loss;
std::vector<double> adjust_loss;

friend std::ostream &operator<<(std::ostream &os, const losses_params &p);
};
Expand All @@ -181,17 +183,19 @@ class losses_t {
* \param[in] monthly_charge vector (size 1 for annual or 12 for monthly) containing battery system losses when charging (kW) (applied to PV or grid)
* \param[in] monthly_discharge vector (size 1 for annual or 12 for monthly) containing battery system losses when discharge (kW) (applied to battery power)
* \param[in] monthly_idle vector (size 1 for annual or 12 for monthly) containing battery system losses when idle (kW) (applied to PV or grid)
* \param[in] adjust_losses vector (size 0 for constant or per timestep) containing battery system availability losses (%) (applies to both power and energy capacity - if a system has 4 packs, a 25% loss means one pack is offline)
*/
losses_t(const std::vector<double>& monthly_charge, const std::vector<double>& monthly_discharge, const std::vector<double>& monthly_idle);
losses_t(const std::vector<double>& monthly_charge, const std::vector<double>& monthly_discharge, const std::vector<double>& monthly_idle, const std::vector<double>& adjust_losses);

/**
* \function losses_t
*
* Construct the losses object for schedule of timeseries losses
*
* \param[in] schedule_loss vector (size 0 for constant or per timestep) containing battery system losses
* \param[in] adjust_losses vector (size 0 for constant or per timestep) containing battery system availability losses (%) (applies to both power and energy capacity - if a system has 4 packs, a 25% loss means one pack is offline)
*/
explicit losses_t(const std::vector<double>& schedule_loss = std::vector<double>(1, 0));
explicit losses_t(const std::vector<double>& schedule_loss = std::vector<double>(1, 0), const std::vector<double>& adjust_losses = std::vector<double>(1,0));

explicit losses_t(std::shared_ptr<losses_params> p);

Expand All @@ -203,7 +207,9 @@ class losses_t {
void run_losses(size_t lifetimeIndex, double dt_hour, double charge_operation);

/// Get the loss at the specified simulation index (year 1)
double getLoss();
double getAncillaryLoss();

double getAvailabilityLoss(size_t lifetimeIndex);

losses_state get_state();

Expand Down Expand Up @@ -362,7 +368,7 @@ class battery_t {
double estimateCycleDamage();

// Run a component level model
void runCapacityModel(double &I);
void runCapacityModel(double &I, size_t lifetimeIndex);

void runVoltageModel();

Expand Down Expand Up @@ -409,7 +415,10 @@ class battery_t {
double calculate_loss(double power, size_t lifetimeIndex);

// Get the losses at the current step
double getLoss();
double getAncillaryLoss();

// Get the adjust loss at the current timestep
double getAvailabilityLoss(size_t lifetimeIndex);

battery_state get_state();

Expand Down
48 changes: 42 additions & 6 deletions shared/lib_battery_capacity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,19 @@ capacity_params capacity_t::get_params() { return *params; }
capacity_state capacity_t::get_state() { return *state; }

void capacity_t::check_SOC() {
double q_upper = state->qmax_lifetime * params->maximum_SOC * 0.01;
double q_lower = state->qmax_lifetime * params->minimum_SOC * 0.01;
double max_SOC_available = params->maximum_SOC * (1 - state->percent_unavailable);
double min_SOC_available = params->minimum_SOC * (1 - state->percent_unavailable);

double q_upper = state->qmax_lifetime * max_SOC_available * 0.01;
double q_lower = state->qmax_lifetime * min_SOC_available * 0.01;

// set capacity to upper thermal limit
if (q_upper > state->qmax_thermal * params->maximum_SOC * 0.01) {
q_upper = state->qmax_thermal * params->maximum_SOC * 0.01;
if (q_upper > state->qmax_thermal * max_SOC_available * 0.01) {
q_upper = state->qmax_thermal * max_SOC_available * 0.01;
}
// do this so battery can cycle full depth and we calculate correct SOC min
if (q_lower > state->qmax_thermal * params->minimum_SOC * 0.01) {
q_lower = state->qmax_thermal * params->minimum_SOC * 0.01;
if (q_lower > state->qmax_thermal * min_SOC_available * 0.01) {
q_lower = state->qmax_thermal * min_SOC_available * 0.01;
}

if (state->q0 > q_upper + tolerance) {
Expand All @@ -175,6 +178,7 @@ void capacity_t::check_SOC() {
}

void capacity_t::update_SOC() {
// Want to continue to define SOC as nameplate minus degradation (availability losses lower SOC, not nameplate)
double max = fmin(state->qmax_lifetime, state->qmax_thermal);
if (max == 0) {
state->q0 = 0;
Expand Down Expand Up @@ -282,6 +286,8 @@ void capacity_kibam_t::replace_battery(double replacement_percent) {
state->leadacid.q2_0 = state->q0 - state->leadacid.q1_0;
state->SOC = params->initial_SOC;
state->SOC_prev = 50;
state->percent_unavailable = 0.0;
state->percent_unavailable_prev = 0.0;
update_SOC();
}

Expand Down Expand Up @@ -438,6 +444,22 @@ void capacity_kibam_t::updateCapacityForLifetime(double capacity_percent) {
update_SOC();
}

void capacity_kibam_t::updateCapacityForAvailability(double availability_percent) {
state->percent_unavailable_prev = state->percent_unavailable;
state->percent_unavailable = availability_percent;

double timestep_loss = state->percent_unavailable_prev - state->percent_unavailable;
if (timestep_loss > 1e-7) {
double q0_orig = state->q0;
state->q0 *= (1 - timestep_loss);
state->leadacid.q1 *= (1 - timestep_loss);
state->leadacid.q2 *= (1 - timestep_loss);
state->I_loss += (q0_orig - state->q0) / params->dt_hr;
}

update_SOC();
}

double capacity_kibam_t::q1() { return state->leadacid.q1_0; }

double capacity_kibam_t::q2() { return state->leadacid.q2_0; }
Expand Down Expand Up @@ -533,6 +555,20 @@ void capacity_lithium_ion_t::updateCapacityForLifetime(double capacity_percent)
update_SOC();
}

void capacity_lithium_ion_t::updateCapacityForAvailability(double availability_percent) {
state->percent_unavailable_prev = state->percent_unavailable;
state->percent_unavailable = availability_percent;

double timestep_loss = state->percent_unavailable - state->percent_unavailable_prev;
if (timestep_loss > 1e-7) {
double q0_orig = state->q0;
state->q0 *= (1 - timestep_loss);
state->I_loss += (q0_orig - state->q0) / params->dt_hr;
}

update_SOC();
}

double capacity_lithium_ion_t::q1() { return state->q0; }

double capacity_lithium_ion_t::q10() { return state->qmax_lifetime; }
8 changes: 8 additions & 0 deletions shared/lib_battery_capacity.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ struct capacity_state {
double I_loss; // [A] - Lifetime and thermal losses
double SOC; // [%] - State of Charge
double SOC_prev; // [%] - previous step
double percent_unavailable; // [%] - Percent of system that is down
double percent_unavailable_prev; // [%] - Percent of system that was down last step

enum {
CHARGE, NO_CHARGE, DISCHARGE
Expand Down Expand Up @@ -120,6 +122,8 @@ class capacity_t {

virtual void replace_battery(double replacement_percent) = 0;

virtual void updateCapacityForAvailability(double availability_percent) = 0;

void change_SOC_limits(double min, double max) {
params->minimum_SOC = min;
params->maximum_SOC = max;
Expand Down Expand Up @@ -199,6 +203,8 @@ class capacity_kibam_t : public capacity_t {

void replace_battery(double replacement_percent) override;

void updateCapacityForAvailability(double availability_percent) override;

double q1() override; // Available charge
double q2(); // Bound charge
double q10() override; // Capacity at 10 hour discharge rate
Expand Down Expand Up @@ -254,6 +260,8 @@ class capacity_lithium_ion_t : public capacity_t {

void replace_battery(double replacement_percent) override;

void updateCapacityForAvailability(double availability_percent) override;

double q1() override; // Available charge
double q10() override; // Capacity at 10 hour discharge rate
};
Expand Down
Loading