diff --git a/CHANGELOG.md b/CHANGELOG.md index b65d1342f6..ebe4abdd66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,13 @@ # [Unreleased](https://github.com/pybamm-team/PyBaMM/) ## Features + - Added sensitivity calculation support for `pybamm.Simulation` and `pybamm.Experiment` ([#4415](https://github.com/pybamm-team/PyBaMM/pull/4415)) - Added OpenMP parallelization to IDAKLU solver for lists of input parameters ([#4449](https://github.com/pybamm-team/PyBaMM/pull/4449)) +- Added a lithium ion equivalent circuit model with split open circuit voltages for each electrode (`SplitOCVR`). ([#4330](https://github.com/pybamm-team/PyBaMM/pull/4330)) ## Optimizations + - Removed the `start_step_offset` setting and disabled minimum `dt` warnings for drive cycles with the (`IDAKLUSolver`). ([#4416](https://github.com/pybamm-team/PyBaMM/pull/4416)) ## Features @@ -12,10 +15,10 @@ - Added phase-dependent particle options to LAM #4369 ## Breaking changes + - The parameters "... electrode OCP entropic change [V.K-1]" and "... electrode volume change" are now expected to be functions of stoichiometry only instead of functions of both stoichiometry and maximum concentration ([#4427](https://github.com/pybamm-team/PyBaMM/pull/4427)) - Renamed `set_events` function to `add_events_from` to better reflect its purpose. ([#4421](https://github.com/pybamm-team/PyBaMM/pull/4421)) - # [v24.9.0](https://github.com/pybamm-team/PyBaMM/tree/v24.9.0) - 2024-09-03 ## Features diff --git a/docs/source/api/models/lithium_ion/ecm_split_ocv.rst b/docs/source/api/models/lithium_ion/ecm_split_ocv.rst new file mode 100644 index 0000000000..a7d833cf55 --- /dev/null +++ b/docs/source/api/models/lithium_ion/ecm_split_ocv.rst @@ -0,0 +1,7 @@ +Equivalent Circuit Model with Split OCV (SplitOCVR) +===================================================== + +.. autoclass:: pybamm.lithium_ion.SplitOCVR + :members: + +.. footbibliography:: diff --git a/docs/source/api/models/lithium_ion/index.rst b/docs/source/api/models/lithium_ion/index.rst index 1a72c3c662..52efe44d6b 100644 --- a/docs/source/api/models/lithium_ion/index.rst +++ b/docs/source/api/models/lithium_ion/index.rst @@ -12,3 +12,4 @@ Lithium-ion Models msmr yang2017 electrode_soh + ecm_split_ocv diff --git a/src/pybamm/models/full_battery_models/lithium_ion/__init__.py b/src/pybamm/models/full_battery_models/lithium_ion/__init__.py index b02868dbe9..556e8de31c 100644 --- a/src/pybamm/models/full_battery_models/lithium_ion/__init__.py +++ b/src/pybamm/models/full_battery_models/lithium_ion/__init__.py @@ -24,8 +24,9 @@ from .Yang2017 import Yang2017 from .mpm import MPM from .msmr import MSMR +from .basic_splitOCVR import SplitOCVR __all__ = ['Yang2017', 'base_lithium_ion_model', 'basic_dfn', 'basic_dfn_composite', 'basic_dfn_half_cell', 'basic_spm', 'dfn', 'electrode_soh', 'electrode_soh_half_cell', 'mpm', 'msmr', - 'newman_tobias', 'spm', 'spme'] + 'newman_tobias', 'spm', 'spme', 'basic_splitOCVR'] diff --git a/src/pybamm/models/full_battery_models/lithium_ion/basic_splitOCVR.py b/src/pybamm/models/full_battery_models/lithium_ion/basic_splitOCVR.py new file mode 100644 index 0000000000..386a5c08fc --- /dev/null +++ b/src/pybamm/models/full_battery_models/lithium_ion/basic_splitOCVR.py @@ -0,0 +1,100 @@ +# +# Equivalent Circuit Model with split OCV +# +import pybamm + + +class SplitOCVR(pybamm.BaseModel): + """Basic Equivalent Circuit Model that uses two OCV functions + for each electrode. This model is easily parameterizable with minimal parameters. + This class differs from the :class: pybamm.equivalent_circuit.Thevenin() due + to dual OCV functions to make up the voltage from each electrode. + + Parameters + ---------- + name: str, optional + The name of the model. + """ + + def __init__(self, name="ECM with split OCV"): + super().__init__(name) + + ###################### + # Variables + ###################### + # All variables are only time-dependent + # No domain definition needed + + theta_n = pybamm.Variable("Negative particle stoichiometry") + theta_p = pybamm.Variable("Positive particle stoichiometry") + Q = pybamm.Variable("Discharge capacity [A.h]") + V = pybamm.Variable("Voltage [V]") + + # model is isothermal + I = pybamm.FunctionParameter("Current function [A]", {"Time [s]": pybamm.t}) + + # Capacity equation + self.rhs[Q] = I / 3600 + self.initial_conditions[Q] = pybamm.Scalar(0) + + # Capacity in each electrode + Q_n = pybamm.Parameter("Negative electrode capacity [A.h]") + Q_p = pybamm.Parameter("Positive electrode capacity [A.h]") + + # State of charge electrode equations + theta_n_0 = pybamm.Parameter("Negative electrode initial stoichiometry") + theta_p_0 = pybamm.Parameter("Positive electrode initial stoichiometry") + self.rhs[theta_n] = -I / Q_n / 3600 + self.rhs[theta_p] = I / Q_p / 3600 + self.initial_conditions[theta_n] = theta_n_0 + self.initial_conditions[theta_p] = theta_p_0 + + # Resistance for IR expression + R = pybamm.Parameter("Ohmic resistance [Ohm]") + + # Open-circuit potential for each electrode + Un = pybamm.FunctionParameter( + "Negative electrode OCP [V]", {"Negative particle stoichiometry": theta_n} + ) + Up = pybamm.FunctionParameter( + "Positive electrode OCP [V]", {"Positive particle stoichiometry": theta_p} + ) + + # Voltage expression + V = Up - Un - I * R + + # Parameters for Voltage cutoff + voltage_high_cut = pybamm.Parameter("Upper voltage cut-off [V]") + voltage_low_cut = pybamm.Parameter("Lower voltage cut-off [V]") + + self.variables = { + "Negative particle stoichiometry": theta_n, + "Positive particle stoichiometry": theta_p, + "Current [A]": I, + "Discharge capacity [A.h]": Q, + "Voltage [V]": V, + "Times [s]": pybamm.t, + "Positive electrode OCP [V]": Up, + "Negative electrode OCP [V]": Un, + "Current function [A]": I, + } + + # Events specify points at which a solution should terminate + self.events += [ + pybamm.Event("Minimum voltage [V]", V - voltage_low_cut), + pybamm.Event("Maximum voltage [V]", voltage_high_cut - V), + pybamm.Event("Maximum Negative Electrode stoichiometry", 0.999 - theta_n), + pybamm.Event("Maximum Positive Electrode stoichiometry", 0.999 - theta_p), + pybamm.Event("Minimum Negative Electrode stoichiometry", theta_n - 0.0001), + pybamm.Event("Minimum Positive Electrode stoichiometry", theta_p - 0.0001), + ] + + @property + def default_quick_plot_variables(self): + return [ + "Voltage [V]", + ["Negative particle stoichiometry", "Positive particle stoichiometry"], + "Negative electrode OCP [V]", + "Positive electrode OCP [V]", + "Current [A]", + ] diff --git a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_splitOCVR.py b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_splitOCVR.py new file mode 100644 index 0000000000..ef4391acdd --- /dev/null +++ b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_splitOCVR.py @@ -0,0 +1,56 @@ +# +# Test that the model works with an example parameter set +# +import pybamm +import numpy as np +import tests + + +class TestSplitOCVR: + def test_basic_processing(self): + # example parameters + qp0 = 8.73231852 + qn0 = 5.82761507 + theta0_n = 0.9013973983641687 * 0.9 + theta0_p = 0.5142305254580026 * 0.83 + + # OCV functions + def Un(theta_n): + Un = ( + 0.1493 + + 0.8493 * np.exp(-61.79 * theta_n) + + 0.3824 * np.exp(-665.8 * theta_n) + - np.exp(39.42 * theta_n - 41.92) + - 0.03131 * np.arctan(25.59 * theta_n - 4.099) + - 0.009434 * np.arctan(32.49 * theta_n - 15.74) + ) + return Un + + def Up(theta_p): + Up = ( + -10.72 * theta_p**4 + + 23.88 * theta_p**3 + - 16.77 * theta_p**2 + + 2.595 * theta_p + + 4.563 + ) + return Up + + pars = pybamm.ParameterValues( + { + "Positive electrode capacity [A.h]": qp0, + "Ohmic resistance [Ohm]": 0.001, + "Negative electrode initial stoichiometry": theta0_n, + "Lower voltage cut-off [V]": 2.8, + "Positive electrode initial stoichiometry": theta0_p, + "Upper voltage cut-off [V]": 4.2, + "Negative electrode capacity [A.h]": qn0, + "Current function [A]": 5, + "Positive electrode OCP [V]": Up, + "Negative electrode OCP [V]": Un, + "Nominal cell capacity [A.h]": 5, + } + ) + model = pybamm.lithium_ion.SplitOCVR() + modeltest = tests.StandardModelTest(model) + modeltest.test_all(param=pars) diff --git a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_splitOCVR.py b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_splitOCVR.py new file mode 100644 index 0000000000..e807ec1607 --- /dev/null +++ b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_splitOCVR.py @@ -0,0 +1,15 @@ +# +# Test for the ecm split-OCV model +# +import pybamm + + +class TestSplitOCVR: + def test_ecmsplitocv_well_posed(self): + model = pybamm.lithium_ion.SplitOCVR() + model.check_well_posedness() + + def test_get_default_quick_plot_variables(self): + model = pybamm.lithium_ion.SplitOCVR() + variables = model.default_quick_plot_variables + assert "Current [A]" in variables