ECM Simple Degradation¶
This example demonstrates the simple degradation model of the Equivalent
Circuit Model (ECM), where capacity and resistance evolve as functions of
capacity throughput – the running integral of absolute current,
int |I| dt in A.h.
Key concepts demonstrated:
Turning degradation on with the
"capacity"/"resistance scale"optionsUsing the ready-made
linear_capacity_loss/linear_resistance_increasehelpers for the common “X% per full equivalent cycle” caseWriting a custom degradation function of any of the degradation inputs (here, calendar time)
Reading the degradation outputs from the solution
Complete ECM Simple Degradation Example¶
"""
ECM Simple Degradation Example
This example demonstrates the simple degradation model of the Equivalent Circuit
Model (ECM), where capacity and resistance evolve as functions of *capacity
throughput* -- the running integral of absolute current, ``int |I| dt`` in A.h.
Key concepts demonstrated:
- Turning degradation on with the ``"capacity"`` / ``"resistance scale"`` options
- Using the ready-made ``linear_capacity_loss`` / ``linear_resistance_increase``
helpers for the common "X% per full equivalent cycle" case
- Writing a custom degradation function of any of the degradation inputs
(here, calendar time)
- Reading the degradation outputs from the solution
"""
import ionworkspipeline as iwp
import pybamm
# A simple linear OCV as a function of state of charge (in %).
def ocv(soc, T):
return 2.5 + (4.2 - 2.5) * (soc / 100)
# Base ECM parameters (a 1 A.h cell, single RC pair).
parameter_values = pybamm.ParameterValues(
{
"Nominal cell capacity [A.h]": 1,
"Initial SOC": 100,
"Initial temperature [K]": 298.15,
"Ambient temperature [K]": 298.15,
"Open-circuit voltage [V]": ocv,
"Lower voltage cut-off [V]": 2.5,
"Upper voltage cut-off [V]": 4.2,
"R0 [Ohm]": 0.02,
"R_rc [Ohm]": [0.01],
"C_rc [F]": [1000],
"Current function [A]": 1,
}
)
# A protocol that accumulates throughput: repeated full discharge / charge.
experiment = pybamm.Experiment(
[("Discharge at 1C until 2.5V", "Charge at 1C until 4.2V")] * 5
)
# ---------------------------------------------------------------------------
# 1. Standard "X% per full equivalent cycle" degradation using the helpers.
# One full equivalent cycle = a throughput of 2 * nominal capacity.
# ---------------------------------------------------------------------------
linear_values = parameter_values.copy()
linear_values["Capacity [A.h]"] = iwp.models.linear_capacity_loss
linear_values["Capacity loss per equivalent cycle [%]"] = 1.0
linear_values["Resistance scale"] = iwp.models.linear_resistance_increase
linear_values["Resistance increase per equivalent cycle [%]"] = 2.0
linear_model = iwp.models.ECM(
{"rc pairs": "1", "capacity": "function", "resistance scale": "function"}
)
linear_solution = iwp.Simulation(
linear_model, parameter_values=linear_values, experiment=experiment
).solve()
print("Linear per-cycle degradation")
print(" throughput [A.h]:", linear_solution["Capacity throughput [A.h]"].entries[-1])
print(" capacity [A.h]: ", linear_solution["Capacity [A.h]"].entries[-1])
print(" resistance scale:", linear_solution["Resistance scale"].entries[-1])
# ---------------------------------------------------------------------------
# 2. A custom degradation function. Each function receives two positional
# arguments, (Q_throughput, time) -- both monotonic accumulators of the past
# trajectory -- and uses whatever subset it needs. Here capacity fades with
# calendar time (a constant loss rate per second), ignoring throughput.
# ---------------------------------------------------------------------------
fade_rate = 1e-4 # A.h lost per second
def calendar_capacity(Q_throughput, time):
return 1.0 - fade_rate * time
custom_values = parameter_values.copy()
custom_values["Capacity [A.h]"] = calendar_capacity
custom_model = iwp.models.ECM({"rc pairs": "1", "capacity": "function"})
custom_solution = iwp.Simulation(
custom_model, parameter_values=custom_values, experiment=experiment
).solve()
print("Custom calendar-time degradation")
print(" elapsed time [s]:", custom_solution["Time [s]"].entries[-1])
print(" capacity [A.h]: ", custom_solution["Capacity [A.h]"].entries[-1])