Skip to content

Releases: pybamm-team/PyBaMM

PyBaMM 26.7.1.0

Choose a tag to compare

@BradyPlanden BradyPlanden released this 22 Jul 15:56
f234b0e

Breaking changes

  • Reverted the "voltage as a state" default back to "false", undoing the v26.7.0.0 change to "true". Promoting voltage to an algebraic state turns SPM/SPMe into DAEs and places voltage under solver error control, which caused IDAKLU error-test failures on solves of discontinuous (pulsed) current profiles at the default tolerances, and lowered voltage-output accuracy. The option remains available ({"voltage as a state": "true"}) and is still enabled automatically for the "explicit power" and "explicit resistance" operating modes. (#5670)

Features

  • Exposed the per-value parameter serialisation dispatch publicly as pybamm.serialize_parameter_value/pybamm.deserialize_parameter_value, and convert_parameter_values_to_json now accepts any Mapping (not just a ParameterValues), so a raw {name: value} mapping can be serialised without constructing a throwaway ParameterValues. (#5663)

pybammsolvers 0.9.0

Choose a tag to compare

@BradyPlanden BradyPlanden released this 21 Jul 13:06
982fd38

First pybammsolvers release from the PyBaMM monorepo, tagged in the pybammsolvers-v* namespace. The published version comes from version.py (0.9.0); the release workflow's check_version job asserts the tag, version.py, pyproject.toml, and vcpkg.json all agree.

Breaking changes

  • The minimum supported NumPy version is now 2.0.0 (previously unpinned). NumPy 1.x is no longer supported. (#5647)

Features

  • Now ships Linux aarch64 (arm64) wheels, built on manylinux_2_34 native ARM runners. The libstdc++ std::string ABI (_GLIBCXX_USE_CXX11_ABI) is detected from the linked CasADi library instead of hard-coded, resolving the unresolved-symbol failure against CasADi's C++11-ABI aarch64 wheel. (#5653)
  • PyBaMM and pybammsolvers now develop in a single repository (a UV workspace under packages/) while continuing to release independently to PyPI. Release tags are namespaced (pybamm-v* and pybammsolvers-v*). (#5512)

Bug fixes

  • Fixed the Windows wheels failing to build: vcpkg's transitive reference-LAPACK/Fortran chain broke on a pruned upstream MSYS2 download (a 404). The Windows build now uses a suitesparse-klu-only sundials port and vcpkg 2026.06.24 on a windows-2025 runner. (#5650)
  • Fixed the source distribution bundling the SUNDIALS/SuiteSparse submodule trees (~291 MB, over PyPI's per-file limit) when built from a checkout with the submodules initialised; the sdist now excludes them. (#5623)
  • Fixed the editable auto-rebuild failing to configure when the SUNDIALS/SuiteSparse submodules are absent even though the libraries were already built in .idaklu. (#5623)
  • Fixed the Dependabot uv update job failing to resolve the workspace (ModuleNotFoundError: No module named 'scikit_build_core'): pybammsolvers now declares a static version in pyproject.toml, so build-tool-less resolvers read its metadata without invoking the scikit-build-core backend. version.py remains the single source of truth, mirrored into pyproject.toml and vcpkg.json by the sync-pybammsolvers-version pre-commit hook. (#5655)

Full changelog: https://github.com/pybamm-team/PyBaMM/blob/main/CHANGELOG.md

PyBaMM 26.7.0.0

Choose a tag to compare

@BradyPlanden BradyPlanden released this 21 Jul 15:06
f6f23cd

Breaking changes

  • The minimum supported NumPy version is now 2.0.0 for both pybamm and pybammsolvers (previously unpinned). NumPy 1.x is no longer supported. (#5647)
  • The "voltage as a state" model option now defaults to "true": voltage is solved as an algebraic state, making standard models DAEs. Reading Voltage [V] from a solution is now an O(1) state lookup instead of a post-solve expression evaluation (up to 73% faster end-to-end for dense output; 8-24% faster experiment cycling across SPM/SPMe/DFN; up to 48% faster with in-solver output_variables=["Voltage [V]"]). The default IDAKLUSolver, CasadiSolver (all modes), and JaxSolver(method="BDF") handle DAEs; ODE-only solvers (ScipySolver, JaxSolver(method="RK45")) require {"voltage as a state": "false", "surface form": "false"} for SPM/SPMe, which remains a supported configuration. Known trade-off: continuous solves of rapidly alternating current profiles (e.g. interpolant drive cycles with more than ~25 current reversals in one solve) can be slower, up to ~2x for SPM in the worst measured case; the legacy configuration above restores previous performance for these workloads. Experiment-driven cycling is unaffected. (#5573)
  • CasadiSolver, ScipySolver, and CasadiAlgebraicSolver are deprecated; use IDAKLUSolver (or NonlinearSolver for algebraic systems) instead. It is now the default for every model. (#5624)

Features

  • pybammsolvers now ships Linux aarch64 (arm64) wheels, built on manylinux_2_34 native ARM runners. The libstdc++ std::string ABI (_GLIBCXX_USE_CXX11_ABI) is now detected from the linked CasADi library instead of hard-coded, resolving the unresolved-symbol failure against CasADi's C++11-ABI aarch64 wheel. (#5653)
  • Added skip_surface_form_check option to EISSimulation to bypass the surface form validation. (#5632)
  • Added ability to export pybamm.Simulation with experiments to DiffSL format (#5557)
  • PyBaMM and pybammsolvers now develop in a single repository — a UV workspace under packages/ — while continuing to release independently to PyPI. Release tags are namespaced (pybamm-v* and pybammsolvers-v*), and PyBaMM's CI now tests against the in-repo solver on every platform. The published pybamm package and its dependency on pybammsolvers are unchanged for users. See RELEASE.md for the release model. (#5512)
  • Legacy BPX v0.x files/objects now load again: bpx itself detects and converts them to the v1.x schema on a best-effort basis (with a UserWarning), so ParameterValues.create_from_bpx/create_from_bpx_obj no longer raise a ValidationError. PyBaMM officially supports bpx>=1. (#5574)
  • create_from_bpx/create_from_bpx_obj now also accept BPX files that omit State fields (or the whole State section): the ambient/initial temperatures default to the reference temperature and the initial electrolyte concentration to 1000 mol.m-3 (logged), while opt-in fields (initial hysteresis state, heat transfer coefficient) are left for the model to default. (#5574)

Bug fixes

  • Fixed ParameterValues.to_json (and Serialise of parameter values) raising TypeError: ... got multiple values for argument on create_from_bpx outputs, whose functional parameters are functools.partial objects with keyword-bound arguments (e.g. constant diffusivity, tabular OCPs, exchange-current density). The serialiser now only creates symbols for a partial's remaining required arguments, leaving bound and defaulted arguments in place. (#5641)
  • Fixed the Dependabot uv update job failing to resolve the workspace (ModuleNotFoundError: No module named 'scikit_build_core'): pybammsolvers now declares a static version in pyproject.toml, so build-tool-less resolvers read its metadata without invoking the scikit-build-core backend. version.py remains the single source of truth, mirrored into pyproject.toml and vcpkg.json by a new sync-pybammsolvers-version pre-commit hook. (#5655)
  • Fixed the pybammsolvers Windows wheels failing to build: vcpkg's transitive reference-LAPACK/Fortran toolchain chain broke on a pruned upstream MSYS2 download (a 404). The Windows build now avoids that chain, using a suitesparse-klu-only sundials port and vcpkg 2026.06.24 on a windows-2025 runner. (#5650)
  • Fixed the deprecated "<X> electrode diffusivity [m2.s-1]" alias silently overriding the current "<X> particle diffusivity [m2.s-1]": the current name now takes precedence in ParameterValues (instead of being overwritten by the deprecated alias), and a warning is raised when both are set, so setting or fitting particle diffusivity is no longer a silent no-op. The deprecated key is retained for backward compatibility, and create_from_bpx now emits only the current particle diffusivity name. (#5642)
  • Fixed the pybammsolvers source distribution bundling the SUNDIALS/SuiteSparse submodule trees (~291 MB, over PyPI's per-file limit) when built from a checkout with the submodules initialised; the sdist now excludes them. (#5623)
  • Fixed the pybammsolvers editable auto-rebuild failing to configure when the SUNDIALS/SuiteSparse submodules are absent even though the libraries were already built in .idaklu; the from-source bootstrap (and its submodule requirement) is now skipped once the libraries exist. (#5623)
  • Fixed the Read the Docs documentation build, which broke once pybammsolvers became a workspace member with no published wheel during the release: RTD now fetches the SUNDIALS/SuiteSparse submodules and installs gfortran/libopenblas so the solver builds from source. Link checking was also restructured so transient external-link failures no longer block builds: Sphinx linkcheck is advisory, and the CI lychee check runs offline on PRs with a weekly external sweep that files a tracking issue. (#5659)

v26.6.2.0

Choose a tag to compare

@BradyPlanden BradyPlanden released this 16 Jun 16:27

Features

  • create_from_bpx/create_from_bpx_obj now apply a BPX State Initial state-of-charge via set_initial_state (supporting blended electrodes and validated to lie in [0, 1]) instead of ignoring it. (#5616)
  • The parameter "Contact resistance [Ohm]" can now be a function of the volume-averaged cell temperature (in addition to a constant), and works with all thermal options. The accompanying documentation now clarifies that this represents a lumped series resistance rather than a true contact resistance (BPX#130); the name is retained to avoid a breaking change. (#5604)

Bug fixes

  • Fixed BPX import leaving interpolated-table OCP hysteresis branches (OCP (lithiation) [V], OCP (delithiation) [V]) as raw data tuples instead of usable expression-tree objects. Remaining FloatFunctionTable placeholders are now converted generically, so new BPX table parameters no longer need a hand-maintained conversion list. (#5610)
  • RegulariseSqrtAndPower no longer regularises state-independent bases, fixing corrupted small rate constants in exchange-current density functions. (#5600)
  • Fixed Serialise.load_custom_model leaving a loaded lithium-ion model's cached param built from default options instead of the restored ones, so a model loaded with non-default options (e.g. composite "particle phases") had parameters inconsistent with its options. (#5599)
  • Fixed BatteryModelOptions letting two invalid MSMR option sets pass validation and then crash during parameter construction with invalid literal for int() with base 10: 'none'; both now raise a clear OptionError. The all-or-nothing MSMR check now detects MSMR inside a per-electrode tuple (e.g. "open-circuit potential": ("MSMR", "single")), and an MSMR model must set "number of MSMR reactions" to a positive integer rather than the default "none". (#5599)
  • Fixed Serialise.serialise_custom_model not recording a custom model's discretisation recipe (geometry, var_pts, submesh_types, spatial_methods), so a custom model reloaded via the BaseModel fallback (when its defining package is unavailable) was no longer discretisable. (#5609)

v26.6.1.1

Choose a tag to compare

@aabills aabills released this 11 Jun 21:50

Bug fixes

  • Fixed load_custom_model dropping tuple-valued options on JSON round-trip. (#5595)

v26.6.1.0

Choose a tag to compare

@MarcBerliner MarcBerliner released this 11 Jun 14:59

Features

  • The "voltage as a state" option is now registered centrally in BaseBatteryModel and supports all operating modes. SPM/SPMe promote "surface form" to "algebraic" automatically when a particle-size distribution is used (previously an error) as well as for non-default kinetics. CasadiSolver integrator failures on DAE models solved without algebraic initial-condition perturbation now include an actionable hint. (#5572)

Bug fixes

  • Fixed CasADi conversion of a differentiated Interpolant, which returned the original function. (#5583)
  • Fixed the "explicit power" and "explicit resistance" operating modes, which failed to build with a ModelError. These modes now default "voltage as a state" to "true", which breaks the circular dependency between current and voltage (I = P/V), and raise a clear OptionError if it is explicitly disabled. (#5572)
  • Fixed latexify() crashing with a NotImplementedError for models whose boundary conditions contain boundary_gradient nodes (e.g. DFN with {"surface form": "algebraic"}). (#5572)
  • Fixed unified experiment crash when the ambient-temperature parameter value is a pybamm.Scalar. (#5587)
  • Unified experiment calculate_sensitivities no longer leaks internal control inputs into the parameter Jacobian. (#5587)
  • Corrected the overflow-clamp threshold in the lithium-ion OCP asymptote helper so the softplus and its linear continuation match exactly. (#5588)
  • Fixed unified experiment building redundant control branches and a dense switching-control Jacobian. (#5589)

v26.6.0.0

Choose a tag to compare

@BradyPlanden BradyPlanden released this 04 Jun 14:43
8daf72a

Breaking changes

  • Electrode electronic conductivity supplied as a function must now accept (stoichiometry, temperature). A constant value is unaffected, but a conductivity function previously written as f(temperature) must be updated to f(stoichiometry, temperature); supplying a temperature-only function now raises a clear error pointing at the new signature. (#5556)

Features

  • Electrode electronic conductivity can now be specified as a function of stoichiometry (in addition to temperature) for all lithium-ion and sodium-ion models. (#5556)
  • Unified PyBaMM serialisation onto a single safe-or-loud encode/decode kernel. Serialisation now either round-trips or raises SerialisationError, never silently dropping a field, across the expression tree, discretised models, meshes, solvers, experiments and parameter values. There is one canonical on-disk format, and files saved by older PyBaMM versions continue to load via backward-compatible readers. Note that save_model(model, mesh=...) now raises for meshes containing submeshes that cannot round-trip (those without a _from_json hook, e.g. Exponential1DSubMesh); previously the save succeeded but the mesh could not be reloaded. Derived caches such as Array.entries_string are no longer stored on disk and are recomputed from the stored entries on load, so a stale value in a legacy file is replaced by the recomputed one. (#5560, #5561)

Bug fixes

  • convert_symbol_from_json is strict again: raw strings, lists, and dicts without a $type/type tag raise SerialisationError (restoring pre-kernel validation) instead of being returned unchanged. Constructor-style legacy nodes ({"type": ..., "children": [...]} without name/domains) keep decoding via the class constructor, as the pre-kernel reader did. Decoding a node missing a key its codec requires now raises a descriptive SerialisationError instead of a bare KeyError, and SerialisationError is importable as pybamm.SerialisationError. (#5567)
  • Fixed legacy geometry deserialisation over-stripping the symbol_ key prefix as a character set, which raised KeyError for variable names composed of those characters (e.g. the current-collector variable y). (#5561)
  • Fixed unified experiment mode using excessive memory and time for experiments with many cycles. (#5554)
  • Fixed unified experiment mode inlining every step's equations; switching now dispatches via a casadi.Function.conditional switch. (#5562)

Optimizations

  • Fixed two O(N²) slowdowns in long experiment/ageing simulations where Solution.__add__/copy re-did whole-accumulation work on every step append: time-series validation now re-checks only the joined boundary, and Solution.observable is computed lazily. (#5550)
  • Experiment/ageing accumulation now folds per-cycle solutions in a single O(N) pass via Solution.from_sub_solutions, removing the residual O(N²) list concatenation in repeated Solution.__add__, and fixes an aliasing bug where __add__ mutated the left operand's sensitivities. (#5551)

v26.5.0

Choose a tag to compare

@MarcBerliner MarcBerliner released this 27 May 15:02
6f7b34c

Breaking changes

  • Simulation.solve now always clears Simulation.solution before solving, so a failed solve leaves it as None rather than retaining the previous result. (#5528)

Bug fixes

  • Fixed Simulation.solve retaining the prior solution alongside the new one during repeated solves. (#5528)
  • IDAKLUSolver no longer caches redundant serialised function bytes, reducing memory and pickle size. (#5528)

Features

  • Default summary_variables now include per-phase LAM%, capacity, total lithium, SEI loss, SEI-on-cracks loss, and lithium plating loss for composite electrodes; aggregate per-electrode entries are unchanged. (#5516)
  • Added a store_first_last kwarg to solvers. When True, only the first and last sample of each integration window (one experiment step in Simulation.solve, or the full [t_eval[0], t_eval[-1]] window in solve) are stored. Composes with output_variables for memory-light ageing simulations whose post-processing only reads per-step first/last values. Has effect on solvers that support intra-solve interpolation (IDAKLUSolver); other solvers warn and no-op. Note: with this flag set, intra-step interpolation falls back to linear across the whole step, so it is not appropriate when post-processing queries an intra-step time. (#5499)
  • Added NonlinearSolver as the default nonlinear solver, which replaces CasadiAlgebraicSolver. IDAKLUSolver now computes the initial conditions in C++ by default. (#5459)

v26.4.4

Choose a tag to compare

@MarcBerliner MarcBerliner released this 22 May 20:34

Bug fixes

  • Fixed BaseStep hashing collapsing different control types (e.g. CRate(4.2) and Voltage(4.2)) with same value. (#5529)
  • Fixed ProcessedVariable.sensitivities raising KeyError when calculate_sensitivities was passed a subset of inputs and a non-target input parameter (e.g. a pybamm.InputParameter used in an experiment step) appeared in the variable's expression tree. (#5518)

v26.4.3

Choose a tag to compare

@rtimms rtimms released this 13 May 09:02

v26.4.3 - 2026-05-12

Bug fixes

  • ProcessedVariableComputed.__init__ now defers the heavy initialise_* work (numpy concatenate/flatten, xarray DataArray build) until entries or _xr_data_array is actually read. On a 500-cycle SPM with output_variables set and save_at_cycles=N, solve wall time dropped from ~30 s to ~2.5 s (~12×); per-step __init__ cost dropped from 47% of solve to 1.7%.
  • IDAKLUSolver now records Solution.closest_event_idx after a SUNDIALS root return, so BaseSolver.get_termination_reason can short-circuit instead of re-walking every TERMINATION event's symbolic expression on the Python side. On a 1000-cycle SPM with output_variables set, cumulative allocations dropped 25% (~445 MB) and wall time 16%; the eliminated path was hot in long event-terminated cycling experiments. (#5502)
  • Fixed Serialise.serialise_experiment / deserialise_experiment dropping every constructor argument other than per-step value / duration / terminations / temperature. The top-level period, temperature, and termination arguments to pybamm.Experiment and the per-step period, tags, description, start_time, direction, and skip_ok arguments to BaseStep are now written by to_config() and parsed back by from_config(), so JSON round-tripped experiments preserve user intent. The Resistance step type was also missing from the deserialiser's step-type map and now round-trips correctly.
  • Fixed Serialise.load_custom_model reconstructing Event.event_type as the bare enum name string (e.g. "TERMINATION") instead of the corresponding EventType member. The custom JSON encoder writes Enum values as their .name, but the loader was passing the string straight through to pybamm.Event.__init__, so models round-tripped through to_json / from_json carried string event types rather than EventType enum values. (#5498)
  • Fixed Serialise.serialise_solver silently dropping root_method (and any other nested BaseSolver __init__ argument). After construction, root_method is a BaseSolver instance rather than the original string, so it failed json.dumps and was omitted from the config — making the deserialised solver fall back to the default. to_config() / from_config() now recurse into nested solver values, preserving root_method and its tolerances across the round-trip. (#5497)
  • Fixed Serialise.serialise_experiment silently dropping per-step temperature overrides. The JSON-config round-trip via Experiment.to_config() / Experiment.from_config() now preserves temperature for current, voltage, power, c-rate, and rest steps. (#5496)
  • Fixed Serialise._to_json_safe coercing Python bool values to 0/1 ints because bool is a subclass of int. IDAKLUSolver.to_config() now emits its bool options (compile, print_stats, silence_sundials_errors, etc.) as JSON true/false so they round-trip through strict-bool deserialisers. (#5495)