Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 6 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,18 @@ jobs:
matrix:
include:
# All Python versions against the latest PyBaMM
- {python-version: "3.10", pybamm-version: "26.7"}
- {python-version: "3.11", pybamm-version: "26.7"}
- {python-version: "3.12", pybamm-version: "26.7"}
- {python-version: "3.13", pybamm-version: "26.7"}
- {python-version: "3.14", pybamm-version: "26.7"}
- {python-version: "3.10", pybamm-version: "26.8"}
- {python-version: "3.11", pybamm-version: "26.8"}
- {python-version: "3.12", pybamm-version: "26.8"}
- {python-version: "3.13", pybamm-version: "26.8"}
- {python-version: "3.14", pybamm-version: "26.8"}
# Older PyBaMM versions against the latest Python they support
- {python-version: "3.13", pybamm-version: "25.12"}
- {python-version: "3.14", pybamm-version: "26.3"}
- {python-version: "3.14", pybamm-version: "26.4"}
- {python-version: "3.14", pybamm-version: "26.5"}
- {python-version: "3.14", pybamm-version: "26.6"}
- {python-version: "3.14", pybamm-version: "26.7"}

steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Thermal sub-model and heat-source options are injected automatically — pass th
| `lead_acid.Full` | `Sulzer2019` | ❌ DAE | ❌ DAE | ✅ | ✅ |
| `equivalent_circuit.Thevenin` | `ECM_Example` | ✅ | ✅ | ✅ ³ | ✅ ³ |

¹ PyBaMM < 26.7 only — from 26.7 on `LOQS` is a DAE, use a `CellCoSim*` block instead.
¹ Not on PyBaMM 26.7 — there `LOQS` is a DAE, use a `CellCoSim*` block instead. It is an ODE again from 26.8 on.

² PyBaMM < 26.7 only — pass `pybamm_solver=pybamm.CasadiSolver(mode="safe")`; the default `IDAKLUSolver` errors on `LOQS`. Fixed in 26.7.

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ dependencies = [
# is skipped on Emscripten so the pure-Python parts of the toolbox
# (`pathsim_batt.thermal`) still install in the browser.
# Upper-bounded: each new minor release is vetted before being allowed.
"pybamm>=25.12,<26.8; sys_platform != 'emscripten'",
"pybamm>=25.12,<26.9;sys_platform != 'emscripten'",
]

[project.optional-dependencies]
Expand Down
39 changes: 20 additions & 19 deletions tests/cells/test_lead_acid.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

Block / model matrix covered
-----------------------------
lead_acid.LOQS — ODE on PyBaMM < 26.7, DAE from 26.7 on → CoSim blocks only there
lead_acid.LOQS — ODE (all 4 blocks), except on PyBaMM 26.7 where it is a DAE
→ CoSim blocks only there
lead_acid.Full — DAE → CoSim blocks only
"""

Expand Down Expand Up @@ -31,10 +32,10 @@

# PyBaMM 26.7 registers "voltage as a state" centrally on every
# BaseBatteryModel (default "true"), and lead-acid models don't support
# disabling it, so LOQS is a DAE from 26.7 on and can no longer run in the
# monolithic (ODE-only) blocks.
_PYBAMM_VERSION = tuple(int(x) for x in pybamm.__version__.split(".")[:2])
_LOQS_IS_ODE = _PYBAMM_VERSION < (26, 7)
# disabling it, so LOQS is a DAE there and can't run in the monolithic
# (ODE-only) blocks. 26.8 defaults it back to "false", making LOQS an ODE
# again. Detect this from the model itself rather than the version number.
_LOQS_IS_ODE = not pybamm.lead_acid.LOQS().algebraic

# ---------------------------------------------------------------------------
# lead_acid.LOQS (ODE — all 4 blocks)
Expand All @@ -44,8 +45,8 @@
class TestLeadAcidLOQS(unittest.TestCase):
"""lead_acid.LOQS with Sulzer2019 parameters.

ODE model (all 4 blocks) on PyBaMM < 26.7; DAE (CoSim blocks only) from
26.7 on. Sulzer2019 cutoffs: lower 1.75 V, upper 2.42 V, nominal capacity
ODE model (all 4 blocks), except on PyBaMM 26.7 where it is a DAE (CoSim
blocks only). Sulzer2019 cutoffs: lower 1.75 V, upper 2.42 V, nominal capacity
17 A·h.
"""

Expand All @@ -57,32 +58,32 @@ def setUp(self):
def _model(self):
return pybamm.lead_acid.LOQS()

@unittest.skipUnless(_LOQS_IS_ODE, "LOQS is a DAE on PyBaMM >= 26.7")
@unittest.skipUnless(_LOQS_IS_ODE, "LOQS is a DAE on this PyBaMM version")
def test_electrical_smoke(self):
cell = run_electrical(self._model(), self.pv, current=17.0)
assert_electrical_outputs(self, cell, self.v_lo, self.v_hi)

@unittest.skipUnless(_LOQS_IS_ODE, "LOQS is a DAE on PyBaMM >= 26.7")
@unittest.skipUnless(_LOQS_IS_ODE, "LOQS is a DAE on this PyBaMM version")
def test_electrothermal_smoke(self):
cell = run_electrothermal(self._model(), self.pv, current=17.0)
assert_electrothermal_outputs(self, cell, self.v_lo, self.v_hi)

@unittest.skipIf(_LOQS_IS_ODE, "LOQS is a pure ODE on PyBaMM < 26.7")
@unittest.skipIf(_LOQS_IS_ODE, "LOQS is a pure ODE on this PyBaMM version")
def test_monolithic_electrical_raises(self):
"""LOQS is a DAE on PyBaMM >= 26.7 — CellElectrical must raise."""
"""Where LOQS is a DAE (PyBaMM 26.7), CellElectrical must raise."""
with self.assertRaises(NotImplementedError):
CellElectrical(model=self._model(), parameter_values=self.pv)

@unittest.skipIf(_LOQS_IS_ODE, "LOQS is a pure ODE on PyBaMM < 26.7")
@unittest.skipIf(_LOQS_IS_ODE, "LOQS is a pure ODE on this PyBaMM version")
def test_monolithic_electrothermal_raises(self):
"""LOQS is a DAE on PyBaMM >= 26.7 — CellElectrothermal must raise."""
"""Where LOQS is a DAE (PyBaMM 26.7), CellElectrothermal must raise."""
with self.assertRaises(NotImplementedError):
CellElectrothermal(model=self._model(), parameter_values=self.pv)

def test_cosim_electrical_smoke(self):
# On PyBaMM < 26.7, LOQS disables its Jacobian and IDAKLUSolver (the
# co-sim default) errors without one; CasadiSolver works on every
# pinned version (25.12-26.7), so use it explicitly here.
# pinned version (25.12-26.8), so use it explicitly here.
solver = pybamm.CasadiSolver(mode="safe")
cell = CellCoSimElectrical(
model=self._model(),
Expand Down Expand Up @@ -126,19 +127,19 @@ def test_cosim_electrothermal_smoke(self):
sim.run(2)
assert_electrothermal_outputs(self, cell, self.v_lo, self.v_hi)

@unittest.skipUnless(_LOQS_IS_ODE, "LOQS is a DAE on PyBaMM >= 26.7")
@unittest.skipUnless(_LOQS_IS_ODE, "LOQS is a DAE on this PyBaMM version")
def test_electrical_soc_decreases(self):
"""SOC must decrease under discharge current."""
cell = run_electrical(self._model(), self.pv, current=17.0, duration=60)
self.assertLess(float(cell.outputs[2]), 1.0)

@unittest.skipUnless(_LOQS_IS_ODE, "LOQS is a DAE on PyBaMM >= 26.7")
@unittest.skipUnless(_LOQS_IS_ODE, "LOQS is a DAE on this PyBaMM version")
def test_cutoff_values_match_parameter_set(self):
cell = CellElectrical(model=self._model(), parameter_values=self.pv)
self.assertAlmostEqual(cell._v_lower, self.v_lo)
self.assertAlmostEqual(cell._v_upper, self.v_hi)

@unittest.skipUnless(_LOQS_IS_ODE, "LOQS is a DAE on PyBaMM >= 26.7")
@unittest.skipUnless(_LOQS_IS_ODE, "LOQS is a DAE on this PyBaMM version")
def test_q_dot_nonzero_during_discharge(self):
"""Q_dot must be strictly positive during discharge (isothermal LOQS).

Expand All @@ -152,7 +153,7 @@ def test_q_dot_nonzero_during_discharge(self):
"Q_dot is zero — thermal model may not compute heat sources",
)

@unittest.skipUnless(_LOQS_IS_ODE, "LOQS is a DAE on PyBaMM >= 26.7")
@unittest.skipUnless(_LOQS_IS_ODE, "LOQS is a DAE on this PyBaMM version")
def test_tamb_affects_temperature(self):
"""A warmer ambient temperature must yield a higher output cell temperature."""
solver = pybamm.CasadiSolver(mode="safe")
Expand Down Expand Up @@ -183,7 +184,7 @@ def test_tamb_affects_temperature(self):
),
)

@unittest.skipUnless(_LOQS_IS_ODE, "LOQS is a DAE on PyBaMM >= 26.7")
@unittest.skipUnless(_LOQS_IS_ODE, "LOQS is a DAE on this PyBaMM version")
def test_soc_scale_factor(self):
"""SOC must be well below 1.0 after sustained discharge.

Expand Down
Loading