From dddf82742e0ecff727410f40af35ea659dca5e8c Mon Sep 17 00:00:00 2001 From: rtimms Date: Mon, 31 Aug 2026 11:58:13 +0100 Subject: [PATCH 1/6] fix: rename composite ESOH phase-capacity inputs off the MSMR name pattern The composite electrode-SOH model named its phase-capacity input parameters Q_n_1 / Q_p_1 / Q_n_2 / Q_p_2. Those match the deprecated-MSMR name pattern in msmr.py, so check_parameter_values reported every composite solve as using a renamed parameter -- naming an internal input the caller never set and cannot change -- and injected a phantom "... host site occupancy capacity (N) [A.h]" key into the solver inputs alongside it. The names are internal to electrode_soh_composite: built there, consumed there. Renaming them to Q_n_prim / Q_n_sec / Q_p_prim / Q_p_sec matches the prim/sec vocabulary already used for the phases (param.n.prim, param.n.sec) and leaves the MSMR check to fire only on real MSMR names. Fixes #5737 Co-Authored-By: Claude Opus 5 --- .../lithium_ion/electrode_soh_composite.py | 62 +++++++++---------- .../test_lithium_ion/test_electrode_soh.py | 17 +++++ 2 files changed, 48 insertions(+), 31 deletions(-) diff --git a/packages/pybamm/src/pybamm/models/full_battery_models/lithium_ion/electrode_soh_composite.py b/packages/pybamm/src/pybamm/models/full_battery_models/lithium_ion/electrode_soh_composite.py index 2eb9d7fa9b..0079e1da75 100644 --- a/packages/pybamm/src/pybamm/models/full_battery_models/lithium_ion/electrode_soh_composite.py +++ b/packages/pybamm/src/pybamm/models/full_battery_models/lithium_ion/electrode_soh_composite.py @@ -144,14 +144,14 @@ def _get_electrode_capacity_equation(options, electrode): i_am_composite = check_if_composite(options, electrode) stoich_variables = _get_stoich_variables(options) direction = _get_direction(electrode) - Q_1 = pybamm.InputParameter(f"Q_{e}_1") + Q_1 = pybamm.InputParameter(f"Q_{e}_prim") Q = ( direction * (stoich_variables[f"{prefix}_100_1"] - stoich_variables[f"{prefix}_0_1"]) * Q_1 ) if i_am_composite: - Q_2 = pybamm.InputParameter(f"Q_{e}_2") + Q_2 = pybamm.InputParameter(f"Q_{e}_sec") Q += ( direction * (stoich_variables[f"{prefix}_100_2"] - stoich_variables[f"{prefix}_0_2"]) @@ -168,20 +168,20 @@ def _get_cyclable_lithium_equation(options, soc="100"): """ x_soc_1 = pybamm.Variable(f"x_{soc}_1", bounds=(0, 1)) y_soc_1 = pybamm.Variable(f"y_{soc}_1", bounds=(0, 1)) - Q_n_1 = pybamm.InputParameter("Q_n_1") - Q_p_1 = pybamm.InputParameter("Q_p_1") - lithium_primary_phases = Q_n_1 * x_soc_1 + Q_p_1 * y_soc_1 + Q_n_prim = pybamm.InputParameter("Q_n_prim") + Q_p_prim = pybamm.InputParameter("Q_p_prim") + lithium_primary_phases = Q_n_prim * x_soc_1 + Q_p_prim * y_soc_1 lithium_secondary_phases = 0.0 is_positive_composite = check_if_composite(options, "positive") is_negative_composite = check_if_composite(options, "negative") if is_positive_composite: - Q_p_2 = pybamm.InputParameter("Q_p_2") + Q_p_sec = pybamm.InputParameter("Q_p_sec") y_soc_2 = pybamm.Variable(f"y_{soc}_2", bounds=(0, 1)) - lithium_secondary_phases += Q_p_2 * y_soc_2 + lithium_secondary_phases += Q_p_sec * y_soc_2 if is_negative_composite: - Q_n_2 = pybamm.InputParameter("Q_n_2") + Q_n_sec = pybamm.InputParameter("Q_n_sec") x_soc_2 = pybamm.Variable(f"x_{soc}_2", bounds=(0, 1)) - lithium_secondary_phases += Q_n_2 * x_soc_2 + lithium_secondary_phases += Q_n_sec * x_soc_2 return lithium_primary_phases + lithium_secondary_phases @@ -470,18 +470,18 @@ def __init__( ) elif initialization_method == "SOC": soc_init = pybamm.InputParameter("SOC_init") - negative_soc = x_init_1 * pybamm.InputParameter("Q_n_1") + negative_soc = x_init_1 * pybamm.InputParameter("Q_n_prim") if is_negative_composite: x_init_2 = variables["x_init_2"] - negative_soc += x_init_2 * pybamm.InputParameter("Q_n_2") + negative_soc += x_init_2 * pybamm.InputParameter("Q_n_sec") - negative_0_soc = x_0_1 * pybamm.InputParameter("Q_n_1") + negative_0_soc = x_0_1 * pybamm.InputParameter("Q_n_prim") if is_negative_composite: - negative_0_soc += x_0_2 * pybamm.InputParameter("Q_n_2") + negative_0_soc += x_0_2 * pybamm.InputParameter("Q_n_sec") - negative_100_soc = x_100_1 * pybamm.InputParameter("Q_n_1") + negative_100_soc = x_100_1 * pybamm.InputParameter("Q_n_prim") if is_negative_composite: - negative_100_soc += x_100_2 * pybamm.InputParameter("Q_n_2") + negative_100_soc += x_100_2 * pybamm.InputParameter("Q_n_sec") self.algebraic[x_init_1] = ( (negative_soc - negative_0_soc) / (negative_100_soc - negative_0_soc) ) - soc_init @@ -584,15 +584,15 @@ def solve_split( is_positive_composite = check_if_composite(options, "positive") is_negative_composite = check_if_composite(options, "negative") - Q_n_1 = parameter_values.evaluate(param.n.prim.Q_init, inputs=inputs) - Q_p_1 = parameter_values.evaluate(param.p.prim.Q_init, inputs=inputs) - Qs = {"Q_n_1": Q_n_1, "Q_p_1": Q_p_1} + Q_n_prim = parameter_values.evaluate(param.n.prim.Q_init, inputs=inputs) + Q_p_prim = parameter_values.evaluate(param.p.prim.Q_init, inputs=inputs) + Qs = {"Q_n_prim": Q_n_prim, "Q_p_prim": Q_p_prim} if is_positive_composite: - Q_p_2 = parameter_values.evaluate(param.p.sec.Q_init, inputs=inputs) - Qs["Q_p_2"] = Q_p_2 + Q_p_sec = parameter_values.evaluate(param.p.sec.Q_init, inputs=inputs) + Qs["Q_p_sec"] = Q_p_sec if is_negative_composite: - Q_n_2 = parameter_values.evaluate(param.n.sec.Q_init, inputs=inputs) - Qs["Q_n_2"] = Q_n_2 + Q_n_sec = parameter_values.evaluate(param.n.sec.Q_init, inputs=inputs) + Qs["Q_n_sec"] = Q_n_sec Q_Li = parameter_values.evaluate(param.Q_Li_particles_init, inputs=inputs) @@ -621,8 +621,8 @@ def solve_split( f"{initial_value!r} of type {type(initial_value).__name__}" ) - Q_n_total = Q_n_1 + (Qs.get("Q_n_2", 0)) - Q_p_total = Q_p_1 + (Qs.get("Q_p_2", 0)) + Q_n_total = Q_n_prim + (Qs.get("Q_n_sec", 0)) + Q_p_total = Q_p_prim + (Qs.get("Q_p_sec", 0)) primary_options = _get_primary_only_options(options) # _ElectrodeSOH uses get_equilibrium_direction internally for the equilibrium @@ -819,15 +819,15 @@ def solve_full( is_positive_composite = check_if_composite(options, "positive") is_negative_composite = check_if_composite(options, "negative") - Q_n_1 = parameter_values.evaluate(param.n.prim.Q_init, inputs=inputs) - Q_p_1 = parameter_values.evaluate(param.p.prim.Q_init, inputs=inputs) - Qs = {"Q_n_1": Q_n_1, "Q_p_1": Q_p_1} + Q_n_prim = parameter_values.evaluate(param.n.prim.Q_init, inputs=inputs) + Q_p_prim = parameter_values.evaluate(param.p.prim.Q_init, inputs=inputs) + Qs = {"Q_n_prim": Q_n_prim, "Q_p_prim": Q_p_prim} if is_positive_composite: - Q_p_2 = parameter_values.evaluate(param.p.sec.Q_init, inputs=inputs) - Qs["Q_p_2"] = Q_p_2 + Q_p_sec = parameter_values.evaluate(param.p.sec.Q_init, inputs=inputs) + Qs["Q_p_sec"] = Q_p_sec if is_negative_composite: - Q_n_2 = parameter_values.evaluate(param.n.sec.Q_init, inputs=inputs) - Qs["Q_n_2"] = Q_n_2 + Q_n_sec = parameter_values.evaluate(param.n.sec.Q_init, inputs=inputs) + Qs["Q_n_sec"] = Q_n_sec Q_Li = parameter_values.evaluate(param.Q_Li_particles_init, inputs=inputs) diff --git a/packages/pybamm/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py b/packages/pybamm/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py index 460a11179a..eb21025a8b 100644 --- a/packages/pybamm/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py +++ b/packages/pybamm/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py @@ -3,6 +3,7 @@ # import contextlib +import warnings import pytest @@ -552,6 +553,22 @@ def test_chen2020_composite_solve_with_hysteresis(self): f"Stoichiometry {key} out of bounds: {results[key]}" ) + def test_phase_capacity_inputs_are_not_deprecated_msmr_names(self): + # The phase capacities are named "Q_n_prim"/"Q_n_sec" rather than + # "Q_n_1"/"Q_n_2" because the numbered form matches the deprecated-MSMR + # name pattern, so check_parameter_values would report every composite + # solve as using a renamed parameter the caller never set. + pvals = pybamm.ParameterValues("Chen2020_composite") + options = {"particle phases": ("2", "1")} + + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + pybamm.lithium_ion.get_initial_stoichiometries_composite( + 0.5, pvals, options=options + ) + + assert [w for w in caught if issubclass(w.category, DeprecationWarning)] == [] + class TestElectrodeSOHMSMR: def test_known_solution(self, options): From 795eda134893ab43a74badde5edb16c68c6aa2f8 Mon Sep 17 00:00:00 2001 From: rtimms Date: Mon, 31 Aug 2026 11:58:59 +0100 Subject: [PATCH 2/6] docs: changelog entry for the composite ESOH input rename Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a158f7d3f..723abe964d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Bug fixes - The `integration` nox session no longer installs the `pydiffsol` extra on macOS Intel CI runners, where it has no working build. ([#5726](https://github.com/pybamm-team/PyBaMM/pull/5726)) +- The composite electrode-SOH model's phase-capacity inputs are named `Q_n_prim` / `Q_n_sec` (and the positive equivalents) instead of `Q_n_1` / `Q_n_2`. The numbered form matched the deprecated-MSMR name pattern, so every composite solve emitted a `DeprecationWarning` about an internal input the caller never set, and injected an unused `... host site occupancy capacity (N) [A.h]` key into the solver inputs. ([#5738](https://github.com/pybamm-team/PyBaMM/pull/5738)) # [v26.8.0.0](https://github.com/pybamm-team/PyBaMM/tree/pybamm-v26.8.0.0) - 2026-08-13 From d44451d244dda5a6d36afa9f1a5cdf5c8bdaee9a Mon Sep 17 00:00:00 2001 From: rtimms Date: Mon, 31 Aug 2026 12:09:36 +0100 Subject: [PATCH 3/6] fix: narrow the regression assertion to rename warnings Asserting that no DeprecationWarning at all escapes a composite solve made the test answerable by any third-party deprecation. Match the rename message instead, which is the warning the collision actually produces. Also drop the changelog's claim that the phantom renamed key reaches the solver: _set_up_model_inputs keeps only the names the model declares, so it is discarded. The warning was the whole user-visible symptom. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 2 +- .../test_lithium_ion/test_electrode_soh.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 723abe964d..e6b579b288 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## Bug fixes - The `integration` nox session no longer installs the `pydiffsol` extra on macOS Intel CI runners, where it has no working build. ([#5726](https://github.com/pybamm-team/PyBaMM/pull/5726)) -- The composite electrode-SOH model's phase-capacity inputs are named `Q_n_prim` / `Q_n_sec` (and the positive equivalents) instead of `Q_n_1` / `Q_n_2`. The numbered form matched the deprecated-MSMR name pattern, so every composite solve emitted a `DeprecationWarning` about an internal input the caller never set, and injected an unused `... host site occupancy capacity (N) [A.h]` key into the solver inputs. ([#5738](https://github.com/pybamm-team/PyBaMM/pull/5738)) +- The composite electrode-SOH model's phase-capacity inputs are named `Q_n_prim` / `Q_n_sec` (and the positive equivalents) instead of `Q_n_1` / `Q_n_2`. The numbered form matched the deprecated-MSMR name pattern, so every composite solve emitted a `DeprecationWarning` naming an internal input the caller never set and cannot change. ([#5738](https://github.com/pybamm-team/PyBaMM/pull/5738)) # [v26.8.0.0](https://github.com/pybamm-team/PyBaMM/tree/pybamm-v26.8.0.0) - 2026-08-13 diff --git a/packages/pybamm/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py b/packages/pybamm/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py index eb21025a8b..3aa4b483ad 100644 --- a/packages/pybamm/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py +++ b/packages/pybamm/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py @@ -567,7 +567,8 @@ def test_phase_capacity_inputs_are_not_deprecated_msmr_names(self): 0.5, pvals, options=options ) - assert [w for w in caught if issubclass(w.category, DeprecationWarning)] == [] + renamed = [w for w in caught if "has been renamed to" in str(w.message)] + assert renamed == [] class TestElectrodeSOHMSMR: From 03d5506b22bb0726c934191ac6004b53acc7d95d Mon Sep 17 00:00:00 2001 From: rtimms Date: Mon, 31 Aug 2026 12:12:37 +0100 Subject: [PATCH 4/6] test: pin the guard to the MSMR rename specifically Matching any "has been renamed to" warning made the test answerable by an unrelated deprecation in the parameter set -- as "electrode diffusivity" -> "particle diffusivity" would be, from the same call site. Co-Authored-By: Claude Opus 5 --- .../test_lithium_ion/test_electrode_soh.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/pybamm/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py b/packages/pybamm/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py index 3aa4b483ad..c226197159 100644 --- a/packages/pybamm/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py +++ b/packages/pybamm/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py @@ -567,7 +567,9 @@ def test_phase_capacity_inputs_are_not_deprecated_msmr_names(self): 0.5, pvals, options=options ) - renamed = [w for w in caught if "has been renamed to" in str(w.message)] + renamed = [ + w for w in caught if "host site occupancy capacity" in str(w.message) + ] assert renamed == [] From 7c62e6047566399424ec260f8f1b96888c916d79 Mon Sep 17 00:00:00 2001 From: rtimms Date: Mon, 31 Aug 2026 15:08:09 +0100 Subject: [PATCH 5/6] docs: name the old -> new mapping and where the names are visible The keys show up in all_inputs on the ESOH sub-simulation's solution, which a caller can hold via the public esoh_sim kwarg, so anyone introspecting them needs the mapping rather than a description of the fix. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6b579b288..6ab7c4def8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## Bug fixes - The `integration` nox session no longer installs the `pydiffsol` extra on macOS Intel CI runners, where it has no working build. ([#5726](https://github.com/pybamm-team/PyBaMM/pull/5726)) -- The composite electrode-SOH model's phase-capacity inputs are named `Q_n_prim` / `Q_n_sec` (and the positive equivalents) instead of `Q_n_1` / `Q_n_2`. The numbered form matched the deprecated-MSMR name pattern, so every composite solve emitted a `DeprecationWarning` naming an internal input the caller never set and cannot change. ([#5738](https://github.com/pybamm-team/PyBaMM/pull/5738)) +- The composite electrode-SOH model's phase-capacity inputs are renamed `Q_n_1` -> `Q_n_prim`, `Q_n_2` -> `Q_n_sec`, `Q_p_1` -> `Q_p_prim`, `Q_p_2` -> `Q_p_sec`. The numbered form matched the deprecated-MSMR name pattern, so every composite solve emitted a `DeprecationWarning` naming an input the caller never set and cannot change. No documented API takes these names, but they are visible in `all_inputs` on the ESOH sub-simulation's solution, so code introspecting those keys must use the new spellings. ([#5738](https://github.com/pybamm-team/PyBaMM/pull/5738)) # [v26.8.0.0](https://github.com/pybamm-team/PyBaMM/tree/pybamm-v26.8.0.0) - 2026-08-13 From 291054db80e0bf6b081e16f788049cee42862d6c Mon Sep 17 00:00:00 2001 From: rtimms Date: Fri, 25 Sep 2026 10:41:01 +0100 Subject: [PATCH 6/6] fix: use descriptive composite ESOH capacity inputs --- CHANGELOG.md | 5 +- .../lithium_ion/electrode_soh_composite.py | 69 +++++++++++++------ .../test_lithium_ion/test_electrode_soh.py | 24 +++++-- 3 files changed, 72 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ab7c4def8..841e8e72b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,12 @@ # [Unreleased](https://github.com/pybamm-team/PyBaMM/) +## Breaking changes + +- The composite electrode-SOH model's phase-capacity inputs are renamed `Q_n_1` -> `Primary: Negative electrode capacity [A.h]`, `Q_n_2` -> `Secondary: Negative electrode capacity [A.h]`, `Q_p_1` -> `Primary: Positive electrode capacity [A.h]`, and `Q_p_2` -> `Secondary: Positive electrode capacity [A.h]`. The numbered names matched the deprecated-MSMR pattern, so every composite solve emitted a `DeprecationWarning` for an internal input. Code solving a standalone `ElectrodeSOHComposite` simulation must use the new names. ([#5738](https://github.com/pybamm-team/PyBaMM/pull/5738)) + ## Bug fixes - The `integration` nox session no longer installs the `pydiffsol` extra on macOS Intel CI runners, where it has no working build. ([#5726](https://github.com/pybamm-team/PyBaMM/pull/5726)) -- The composite electrode-SOH model's phase-capacity inputs are renamed `Q_n_1` -> `Q_n_prim`, `Q_n_2` -> `Q_n_sec`, `Q_p_1` -> `Q_p_prim`, `Q_p_2` -> `Q_p_sec`. The numbered form matched the deprecated-MSMR name pattern, so every composite solve emitted a `DeprecationWarning` naming an input the caller never set and cannot change. No documented API takes these names, but they are visible in `all_inputs` on the ESOH sub-simulation's solution, so code introspecting those keys must use the new spellings. ([#5738](https://github.com/pybamm-team/PyBaMM/pull/5738)) # [v26.8.0.0](https://github.com/pybamm-team/PyBaMM/tree/pybamm-v26.8.0.0) - 2026-08-13 diff --git a/packages/pybamm/src/pybamm/models/full_battery_models/lithium_ion/electrode_soh_composite.py b/packages/pybamm/src/pybamm/models/full_battery_models/lithium_ion/electrode_soh_composite.py index 0079e1da75..86e76f8a4e 100644 --- a/packages/pybamm/src/pybamm/models/full_battery_models/lithium_ion/electrode_soh_composite.py +++ b/packages/pybamm/src/pybamm/models/full_battery_models/lithium_ion/electrode_soh_composite.py @@ -133,6 +133,16 @@ def _get_prefix(electrode): return "x" +def _get_phase_capacity_name(electrode, phase): + """Get the input name for an electrode phase capacity.""" + return f"{phase.capitalize()}: {electrode.capitalize()} electrode capacity [A.h]" + + +def _get_phase_capacity_input(electrode, phase): + """Get the input parameter for an electrode phase capacity.""" + return pybamm.InputParameter(_get_phase_capacity_name(electrode, phase)) + + def _get_electrode_capacity_equation(options, electrode): """ Build equation for electrode capacity in composite electrodes. @@ -140,18 +150,17 @@ def _get_electrode_capacity_equation(options, electrode): Returns Q = sum_i Q_i * (stoich_100_i - stoich_0_i) for all phases. """ prefix = _get_prefix(electrode) - e = electrode[0] i_am_composite = check_if_composite(options, electrode) stoich_variables = _get_stoich_variables(options) direction = _get_direction(electrode) - Q_1 = pybamm.InputParameter(f"Q_{e}_prim") + Q_1 = _get_phase_capacity_input(electrode, "primary") Q = ( direction * (stoich_variables[f"{prefix}_100_1"] - stoich_variables[f"{prefix}_0_1"]) * Q_1 ) if i_am_composite: - Q_2 = pybamm.InputParameter(f"Q_{e}_sec") + Q_2 = _get_phase_capacity_input(electrode, "secondary") Q += ( direction * (stoich_variables[f"{prefix}_100_2"] - stoich_variables[f"{prefix}_0_2"]) @@ -168,18 +177,18 @@ def _get_cyclable_lithium_equation(options, soc="100"): """ x_soc_1 = pybamm.Variable(f"x_{soc}_1", bounds=(0, 1)) y_soc_1 = pybamm.Variable(f"y_{soc}_1", bounds=(0, 1)) - Q_n_prim = pybamm.InputParameter("Q_n_prim") - Q_p_prim = pybamm.InputParameter("Q_p_prim") + Q_n_prim = _get_phase_capacity_input("negative", "primary") + Q_p_prim = _get_phase_capacity_input("positive", "primary") lithium_primary_phases = Q_n_prim * x_soc_1 + Q_p_prim * y_soc_1 lithium_secondary_phases = 0.0 is_positive_composite = check_if_composite(options, "positive") is_negative_composite = check_if_composite(options, "negative") if is_positive_composite: - Q_p_sec = pybamm.InputParameter("Q_p_sec") + Q_p_sec = _get_phase_capacity_input("positive", "secondary") y_soc_2 = pybamm.Variable(f"y_{soc}_2", bounds=(0, 1)) lithium_secondary_phases += Q_p_sec * y_soc_2 if is_negative_composite: - Q_n_sec = pybamm.InputParameter("Q_n_sec") + Q_n_sec = _get_phase_capacity_input("negative", "secondary") x_soc_2 = pybamm.Variable(f"x_{soc}_2", bounds=(0, 1)) lithium_secondary_phases += Q_n_sec * x_soc_2 return lithium_primary_phases + lithium_secondary_phases @@ -470,18 +479,26 @@ def __init__( ) elif initialization_method == "SOC": soc_init = pybamm.InputParameter("SOC_init") - negative_soc = x_init_1 * pybamm.InputParameter("Q_n_prim") + negative_soc = x_init_1 * _get_phase_capacity_input("negative", "primary") if is_negative_composite: x_init_2 = variables["x_init_2"] - negative_soc += x_init_2 * pybamm.InputParameter("Q_n_sec") + negative_soc += x_init_2 * _get_phase_capacity_input( + "negative", "secondary" + ) - negative_0_soc = x_0_1 * pybamm.InputParameter("Q_n_prim") + negative_0_soc = x_0_1 * _get_phase_capacity_input("negative", "primary") if is_negative_composite: - negative_0_soc += x_0_2 * pybamm.InputParameter("Q_n_sec") + negative_0_soc += x_0_2 * _get_phase_capacity_input( + "negative", "secondary" + ) - negative_100_soc = x_100_1 * pybamm.InputParameter("Q_n_prim") + negative_100_soc = x_100_1 * _get_phase_capacity_input( + "negative", "primary" + ) if is_negative_composite: - negative_100_soc += x_100_2 * pybamm.InputParameter("Q_n_sec") + negative_100_soc += x_100_2 * _get_phase_capacity_input( + "negative", "secondary" + ) self.algebraic[x_init_1] = ( (negative_soc - negative_0_soc) / (negative_100_soc - negative_0_soc) ) - soc_init @@ -586,13 +603,16 @@ def solve_split( Q_n_prim = parameter_values.evaluate(param.n.prim.Q_init, inputs=inputs) Q_p_prim = parameter_values.evaluate(param.p.prim.Q_init, inputs=inputs) - Qs = {"Q_n_prim": Q_n_prim, "Q_p_prim": Q_p_prim} + Qs = { + _get_phase_capacity_name("negative", "primary"): Q_n_prim, + _get_phase_capacity_name("positive", "primary"): Q_p_prim, + } if is_positive_composite: Q_p_sec = parameter_values.evaluate(param.p.sec.Q_init, inputs=inputs) - Qs["Q_p_sec"] = Q_p_sec + Qs[_get_phase_capacity_name("positive", "secondary")] = Q_p_sec if is_negative_composite: Q_n_sec = parameter_values.evaluate(param.n.sec.Q_init, inputs=inputs) - Qs["Q_n_sec"] = Q_n_sec + Qs[_get_phase_capacity_name("negative", "secondary")] = Q_n_sec Q_Li = parameter_values.evaluate(param.Q_Li_particles_init, inputs=inputs) @@ -621,8 +641,12 @@ def solve_split( f"{initial_value!r} of type {type(initial_value).__name__}" ) - Q_n_total = Q_n_prim + (Qs.get("Q_n_sec", 0)) - Q_p_total = Q_p_prim + (Qs.get("Q_p_sec", 0)) + Q_n_total = Q_n_prim + ( + Qs.get(_get_phase_capacity_name("negative", "secondary"), 0) + ) + Q_p_total = Q_p_prim + ( + Qs.get(_get_phase_capacity_name("positive", "secondary"), 0) + ) primary_options = _get_primary_only_options(options) # _ElectrodeSOH uses get_equilibrium_direction internally for the equilibrium @@ -821,13 +845,16 @@ def solve_full( Q_n_prim = parameter_values.evaluate(param.n.prim.Q_init, inputs=inputs) Q_p_prim = parameter_values.evaluate(param.p.prim.Q_init, inputs=inputs) - Qs = {"Q_n_prim": Q_n_prim, "Q_p_prim": Q_p_prim} + Qs = { + _get_phase_capacity_name("negative", "primary"): Q_n_prim, + _get_phase_capacity_name("positive", "primary"): Q_p_prim, + } if is_positive_composite: Q_p_sec = parameter_values.evaluate(param.p.sec.Q_init, inputs=inputs) - Qs["Q_p_sec"] = Q_p_sec + Qs[_get_phase_capacity_name("positive", "secondary")] = Q_p_sec if is_negative_composite: Q_n_sec = parameter_values.evaluate(param.n.sec.Q_init, inputs=inputs) - Qs["Q_n_sec"] = Q_n_sec + Qs[_get_phase_capacity_name("negative", "secondary")] = Q_n_sec Q_Li = parameter_values.evaluate(param.Q_Li_particles_init, inputs=inputs) diff --git a/packages/pybamm/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py b/packages/pybamm/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py index c226197159..1ddc3ee59f 100644 --- a/packages/pybamm/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py +++ b/packages/pybamm/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py @@ -553,11 +553,27 @@ def test_chen2020_composite_solve_with_hysteresis(self): f"Stoichiometry {key} out of bounds: {results[key]}" ) + def test_phase_capacity_input_names(self): + options = {"particle phases": ("2", "1")} + simulation = pybamm.Simulation( + pybamm.lithium_ion.ElectrodeSOHComposite(options), + parameter_values=pybamm.ParameterValues("Chen2020_composite"), + ) + simulation.build() + + capacity_names = { + parameter.name + for parameter in simulation.built_model.input_parameters + if parameter.name.endswith("electrode capacity [A.h]") + } + + assert capacity_names == { + "Primary: Negative electrode capacity [A.h]", + "Secondary: Negative electrode capacity [A.h]", + "Primary: Positive electrode capacity [A.h]", + } + def test_phase_capacity_inputs_are_not_deprecated_msmr_names(self): - # The phase capacities are named "Q_n_prim"/"Q_n_sec" rather than - # "Q_n_1"/"Q_n_2" because the numbered form matches the deprecated-MSMR - # name pattern, so check_parameter_values would report every composite - # solve as using a renamed parameter the caller never set. pvals = pybamm.ParameterValues("Chen2020_composite") options = {"particle phases": ("2", "1")}