Skip to content

Commit

Permalink
update examples
Browse files Browse the repository at this point in the history
  • Loading branch information
valentinsulzer committed Dec 5, 2022
1 parent 8e88114 commit 8059bde
Show file tree
Hide file tree
Showing 6 changed files with 1,285 additions and 1,399 deletions.
2 changes: 2 additions & 0 deletions docs/source/models/lithium_ion/electrode_soh.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ Electrode SOH models
:members:

.. autofunction:: pybamm.lithium_ion.get_initial_stoichiometries

.. autofunction:: pybamm.lithium_ion.get_min_max_stoichiometries
180 changes: 48 additions & 132 deletions examples/notebooks/models/electrode-state-of-health.ipynb

Large diffs are not rendered by default.

2,283 changes: 1,099 additions & 1,184 deletions examples/notebooks/simulating-long-experiments.ipynb

Large diffs are not rendered by default.

168 changes: 102 additions & 66 deletions pybamm/models/full_battery_models/lithium_ion/electrode_soh.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,104 @@ def _check_esoh_feasible(self, inputs):
)
)

def get_initial_stoichiometries(self, initial_value):
"""
Calculate initial stoichiometries to start off the simulation at a particular
state of charge, given voltage limits, open-circuit potentials, etc defined by
parameter_values
Parameters
----------
initial_value : float
Target initial value.
If integer, interpreted as SOC, must be between 0 and 1.
If string e.g. "4 V", interpreted as voltage, must be between V_min and V_max.
Returns
-------
x, y
The initial stoichiometries that give the desired initial state of charge
"""
parameter_values = self.parameter_values
param = self.param
x_0, x_100, y_100, y_0 = self.get_min_max_stoichiometries()

if isinstance(initial_value, str) and initial_value.endswith("V"):
V_init = float(initial_value[:-1])
V_min = parameter_values.evaluate(param.voltage_low_cut_dimensional)
V_max = parameter_values.evaluate(param.voltage_high_cut_dimensional)

if not V_min < V_init < V_max:
raise ValueError(
f"Initial voltage {V_init}V is outside the voltage limits "
f"({V_min}, {V_max})"
)

# Solve simple model for initial soc based on target voltage
soc_model = pybamm.BaseModel()
soc = pybamm.Variable("soc")
Up = param.p.prim.U_dimensional
Un = param.n.prim.U_dimensional
T_ref = parameter_values["Reference temperature [K]"]
x = x_0 + soc * (x_100 - x_0)
y = y_0 - soc * (y_0 - y_100)

soc_model.algebraic[soc] = Up(y, T_ref) - Un(x, T_ref) - V_init
# initial guess for soc linearly interpolates between 0 and 1
# based on V linearly interpolating between V_max and V_min
soc_model.initial_conditions[soc] = (V_init - V_min) / (V_max - V_min)
soc_model.variables["soc"] = soc
parameter_values.process_model(soc_model)
initial_soc = pybamm.AlgebraicSolver().solve(soc_model, [0])["soc"].data[0]
elif isinstance(initial_value, (int, float)):
initial_soc = initial_value
if not 0 <= initial_soc <= 1:
raise ValueError("Initial SOC should be between 0 and 1")

else:
raise ValueError(
"Initial value must be a float between 0 and 1, or a string ending in 'V'"
)

x = x_0 + initial_soc * (x_100 - x_0)
y = y_0 - initial_soc * (y_0 - y_100)

return x, y

def get_min_max_stoichiometries(self):
"""
Calculate min/max stoichiometries
given voltage limits, open-circuit potentials, etc defined by parameter_values
Returns
-------
x_0, x_100, y_100, y_0
The min/max stoichiometries
"""
parameter_values = self.parameter_values
param = self.param

V_min = parameter_values.evaluate(param.voltage_low_cut_dimensional)
V_max = parameter_values.evaluate(param.voltage_high_cut_dimensional)
Q_n = parameter_values.evaluate(param.n.Q_init)
Q_p = parameter_values.evaluate(param.p.Q_init)

if self.known_value == "cyclable lithium capacity":
Q_Li = parameter_values.evaluate(param.Q_Li_particles_init)
inputs = {
"V_min": V_min,
"V_max": V_max,
"Q_n": Q_n,
"Q_p": Q_p,
"Q_Li": Q_Li,
}
elif self.known_value == "cell capacity":
Q = parameter_values.evaluate(param.Q / param.n_electrodes_parallel)
inputs = {"V_min": V_min, "V_max": V_max, "Q_n": Q_n, "Q_p": Q_p, "Q": Q}
# Solve the model and check outputs
sol = self.solve(inputs)
return [sol[var].data[0] for var in ["x_0", "x_100", "y_100", "y_0"]]


def get_initial_stoichiometries(
initial_value, parameter_values, param=None, known_value="cyclable lithium capacity"
Expand Down Expand Up @@ -425,52 +523,8 @@ def get_initial_stoichiometries(
x, y
The initial stoichiometries that give the desired initial state of charge
"""
param = param or pybamm.LithiumIonParameters()
x_0, x_100, y_100, y_0 = get_min_max_stoichiometries(
parameter_values, param, known_value
)

if isinstance(initial_value, str) and initial_value.endswith("V"):
V_init = float(initial_value[:-1])
V_min = parameter_values.evaluate(param.voltage_low_cut_dimensional)
V_max = parameter_values.evaluate(param.voltage_high_cut_dimensional)

if not V_min < V_init < V_max:
raise ValueError(
f"Initial voltage {V_init}V is outside the voltage limits "
f"({V_min}, {V_max})"
)

# Solve simple model for initial soc based on target voltage
soc_model = pybamm.BaseModel()
soc = pybamm.Variable("soc")
Up = param.p.prim.U_dimensional
Un = param.n.prim.U_dimensional
T_ref = parameter_values["Reference temperature [K]"]
x = x_0 + soc * (x_100 - x_0)
y = y_0 - soc * (y_0 - y_100)

soc_model.algebraic[soc] = Up(y, T_ref) - Un(x, T_ref) - V_init
# initial guess for soc linearly interpolates between 0 and 1
# based on V linearly interpolating between V_max and V_min
soc_model.initial_conditions[soc] = (V_init - V_min) / (V_max - V_min)
soc_model.variables["soc"] = soc
parameter_values.process_model(soc_model)
initial_soc = pybamm.AlgebraicSolver().solve(soc_model, [0])["soc"].data[0]
elif isinstance(initial_value, (int, float)):
initial_soc = initial_value
if not 0 <= initial_soc <= 1:
raise ValueError("Initial SOC should be between 0 and 1")

else:
raise ValueError(
"Initial value must be a float between 0 and 1, or a string ending in 'V'"
)

x = x_0 + initial_soc * (x_100 - x_0)
y = y_0 - initial_soc * (y_0 - y_100)

return x, y
esoh_solver = ElectrodeSOHSolver(parameter_values, param, known_value)
return esoh_solver.get_initial_stoichiometries(initial_value)


def get_min_max_stoichiometries(
Expand All @@ -494,23 +548,5 @@ def get_min_max_stoichiometries(
x_0, x_100, y_100, y_0
The min/max stoichiometries
"""
param = param or pybamm.LithiumIonParameters()

V_min = parameter_values.evaluate(param.voltage_low_cut_dimensional)
V_max = parameter_values.evaluate(param.voltage_high_cut_dimensional)
Q_n = parameter_values.evaluate(param.n.Q_init)
Q_p = parameter_values.evaluate(param.p.Q_init)

esoh_solver = pybamm.lithium_ion.ElectrodeSOHSolver(
parameter_values, param, known_value
)

if known_value == "cyclable lithium capacity":
Q_Li = parameter_values.evaluate(param.Q_Li_particles_init)
inputs = {"V_min": V_min, "V_max": V_max, "Q_n": Q_n, "Q_p": Q_p, "Q_Li": Q_Li}
elif known_value == "cell capacity":
Q = parameter_values.evaluate(param.Q / param.n_electrodes_parallel)
inputs = {"V_min": V_min, "V_max": V_max, "Q_n": Q_n, "Q_p": Q_p, "Q": Q}
# Solve the model and check outputs
sol = esoh_solver.solve(inputs)
return [sol[var].data[0] for var in ["x_0", "x_100", "y_100", "y_0"]]
esoh_solver = ElectrodeSOHSolver(parameter_values, param, known_value)
return esoh_solver.get_min_max_stoichiometries()
29 changes: 29 additions & 0 deletions pybamm/parameters/parameter_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,35 @@ def update(self, values, check_conflict=False, check_already_exists=True, path="
# reset processed symbols
self._processed_symbols = {}

def set_initial_stoichiometries(
self,
initial_value,
param=None,
known_value="cyclable lithium capacity",
inplace=True,
):
"""
Set the initial stoichiometry of each electrode, based on the initial
SOC or voltage
"""
param = param or pybamm.LithiumIonParameters()
x, y = pybamm.lithium_ion.get_initial_stoichiometries(
initial_value, self, param=param, known_value=known_value
)
if inplace:
parameter_values = self
else:
parameter_values = self.copy()
c_n_max = self.evaluate(param.n.prim.c_max)
c_p_max = self.evaluate(param.p.prim.c_max)
parameter_values.update(
{
"Initial concentration in negative electrode [mol.m-3]": x * c_n_max,
"Initial concentration in positive electrode [mol.m-3]": y * c_p_max,
}
)
return parameter_values

def check_parameter_values(self, values):
# Make sure typical current is non-zero
if "Typical current [A]" in values and values["Typical current [A]"] == 0:
Expand Down
22 changes: 5 additions & 17 deletions pybamm/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def __init__(
C_rate=None,
):
self.parameter_values = parameter_values or model.default_parameter_values
self._unprocessed_parameter_values = self.parameter_values

if isinstance(model, pybamm.lithium_ion.BasicDFNHalfCell):
if experiment is not None:
Expand Down Expand Up @@ -385,27 +386,14 @@ def set_initial_soc(self, initial_soc):
self._built_model = None
self.op_conds_to_built_models = None

c_n_init = self.parameter_values[
"Initial concentration in negative electrode [mol.m-3]"
]
c_p_init = self.parameter_values[
"Initial concentration in positive electrode [mol.m-3]"
]
param = self.model.param
c_n_max = self.parameter_values.evaluate(param.n.prim.c_max)
c_p_max = self.parameter_values.evaluate(param.p.prim.c_max)
x, y = pybamm.lithium_ion.get_initial_stoichiometries(
initial_soc, self.parameter_values, param
)
self.parameter_values.update(
{
"Initial concentration in negative electrode [mol.m-3]": x * c_n_max,
"Initial concentration in positive electrode [mol.m-3]": y * c_p_max,
}
self.parameter_values = (
self._unprocessed_parameter_values.set_initial_stoichiometries(
initial_soc, param=param, inplace=False
)
)
# Save solved initial SOC in case we need to re-build the model
self._built_initial_soc = initial_soc
return c_n_init, c_p_init

def build(self, check_model=True, initial_soc=None):
"""
Expand Down

0 comments on commit 8059bde

Please sign in to comment.