Releases: pybamm-team/PyBaMM
Releases · pybamm-team/PyBaMM
Release list
PyBaMM 26.7.1.0
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, andconvert_parameter_values_to_jsonnow accepts anyMapping(not just aParameterValues), so a raw{name: value}mapping can be serialised without constructing a throwawayParameterValues. (#5663)
pybammsolvers 0.9.0
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 onmanylinux_2_34native ARM runners. The libstdc++std::stringABI (_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
pybammsolversnow develop in a single repository (a UV workspace underpackages/) while continuing to release independently to PyPI. Release tags are namespaced (pybamm-v*andpybammsolvers-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 awindows-2025runner. (#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
uvupdate job failing to resolve the workspace (ModuleNotFoundError: No module named 'scikit_build_core'):pybammsolversnow declares a staticversioninpyproject.toml, so build-tool-less resolvers read its metadata without invoking the scikit-build-core backend.version.pyremains the single source of truth, mirrored intopyproject.tomlandvcpkg.jsonby thesync-pybammsolvers-versionpre-commit hook. (#5655)
Full changelog: https://github.com/pybamm-team/PyBaMM/blob/main/CHANGELOG.md
PyBaMM 26.7.0.0
Breaking changes
- The minimum supported NumPy version is now
2.0.0for bothpybammandpybammsolvers(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-solveroutput_variables=["Voltage [V]"]). The defaultIDAKLUSolver,CasadiSolver(all modes), andJaxSolver(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, andCasadiAlgebraicSolverare deprecated; useIDAKLUSolver(orNonlinearSolverfor algebraic systems) instead. It is now the default for every model. (#5624)
Features
pybammsolversnow ships Linuxaarch64(arm64) wheels, built onmanylinux_2_34native ARM runners. The libstdc++std::stringABI (_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_checkoption toEISSimulationto bypass the surface form validation. (#5632) - Added ability to export pybamm.Simulation with experiments to DiffSL format (#5557)
- PyBaMM and
pybammsolversnow develop in a single repository — a UV workspace underpackages/— while continuing to release independently to PyPI. Release tags are namespaced (pybamm-v*andpybammsolvers-v*), and PyBaMM's CI now tests against the in-repo solver on every platform. The publishedpybammpackage and its dependency onpybammsolversare unchanged for users. SeeRELEASE.mdfor the release model. (#5512) - Legacy BPX v0.x files/objects now load again:
bpxitself detects and converts them to the v1.x schema on a best-effort basis (with aUserWarning), soParameterValues.create_from_bpx/create_from_bpx_objno longer raise aValidationError. PyBaMM officially supportsbpx>=1. (#5574) create_from_bpx/create_from_bpx_objnow also accept BPX files that omitStatefields (or the wholeStatesection): 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(andSerialiseof parameter values) raisingTypeError: ... got multiple values for argumentoncreate_from_bpxoutputs, whose functional parameters arefunctools.partialobjects 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
uvupdate job failing to resolve the workspace (ModuleNotFoundError: No module named 'scikit_build_core'):pybammsolversnow declares a staticversioninpyproject.toml, so build-tool-less resolvers read its metadata without invoking the scikit-build-core backend.version.pyremains the single source of truth, mirrored intopyproject.tomlandvcpkg.jsonby a newsync-pybammsolvers-versionpre-commit hook. (#5655) - Fixed the
pybammsolversWindows 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 asuitesparse-klu-only sundials port and vcpkg 2026.06.24 on awindows-2025runner. (#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 inParameterValues(instead of being overwritten by the deprecated alias), and a warning is raised when both are set, so setting or fittingparticle diffusivityis no longer a silent no-op. The deprecated key is retained for backward compatibility, andcreate_from_bpxnow emits only the currentparticle diffusivityname. (#5642) - Fixed the
pybammsolverssource 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
pybammsolverseditable 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
pybammsolversbecame a workspace member with no published wheel during the release: RTD now fetches the SUNDIALS/SuiteSparse submodules and installsgfortran/libopenblasso the solver builds from source. Link checking was also restructured so transient external-link failures no longer block builds: Sphinxlinkcheckis advisory, and the CIlycheecheck runs offline on PRs with a weekly external sweep that files a tracking issue. (#5659)
v26.6.2.0
Features
create_from_bpx/create_from_bpx_objnow apply a BPXStateInitial state-of-chargeviaset_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. RemainingFloatFunctionTableplaceholders are now converted generically, so new BPX table parameters no longer need a hand-maintained conversion list. (#5610) RegulariseSqrtAndPowerno longer regularises state-independent bases, fixing corrupted small rate constants in exchange-current density functions. (#5600)- Fixed
Serialise.load_custom_modelleaving a loaded lithium-ion model's cachedparambuilt 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
BatteryModelOptionsletting two invalid MSMR option sets pass validation and then crash during parameter construction withinvalid literal for int() with base 10: 'none'; both now raise a clearOptionError. 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_modelnot recording a custom model's discretisation recipe (geometry,var_pts,submesh_types,spatial_methods), so a custom model reloaded via theBaseModelfallback (when its defining package is unavailable) was no longer discretisable. (#5609)
v26.6.1.1
v26.6.1.0
Features
- The "voltage as a state" option is now registered centrally in
BaseBatteryModeland 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.CasadiSolverintegrator 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 clearOptionErrorif it is explicitly disabled. (#5572) - Fixed
latexify()crashing with aNotImplementedErrorfor models whose boundary conditions containboundary_gradientnodes (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_sensitivitiesno 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
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 asf(temperature)must be updated tof(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 thatsave_model(model, mesh=...)now raises for meshes containing submeshes that cannot round-trip (those without a_from_jsonhook, e.g.Exponential1DSubMesh); previously the save succeeded but the mesh could not be reloaded. Derived caches such asArray.entries_stringare 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_jsonis strict again: raw strings, lists, and dicts without a$type/typetag raiseSerialisationError(restoring pre-kernel validation) instead of being returned unchanged. Constructor-style legacy nodes ({"type": ..., "children": [...]}withoutname/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 descriptiveSerialisationErrorinstead of a bareKeyError, andSerialisationErroris importable aspybamm.SerialisationError. (#5567)- Fixed legacy geometry deserialisation over-stripping the
symbol_key prefix as a character set, which raisedKeyErrorfor variable names composed of those characters (e.g. the current-collector variabley). (#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.conditionalswitch. (#5562)
Optimizations
- Fixed two O(N²) slowdowns in long experiment/ageing simulations where
Solution.__add__/copyre-did whole-accumulation work on every step append: time-series validation now re-checks only the joined boundary, andSolution.observableis 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 repeatedSolution.__add__, and fixes an aliasing bug where__add__mutated the left operand's sensitivities. (#5551)
v26.5.0
Breaking changes
Simulation.solvenow always clearsSimulation.solutionbefore solving, so a failed solve leaves it asNonerather than retaining the previous result. (#5528)
Bug fixes
- Fixed
Simulation.solveretaining the prior solution alongside the new one during repeated solves. (#5528) IDAKLUSolverno longer caches redundant serialised function bytes, reducing memory and pickle size. (#5528)
Features
- Default
summary_variablesnow 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_lastkwarg to solvers. WhenTrue, only the first and last sample of each integration window (one experiment step inSimulation.solve, or the full[t_eval[0], t_eval[-1]]window insolve) are stored. Composes withoutput_variablesfor 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
NonlinearSolveras the default nonlinear solver, which replacesCasadiAlgebraicSolver.IDAKLUSolvernow computes the initial conditions in C++ by default. (#5459)
v26.4.4
Bug fixes
- Fixed
BaseStephashing collapsing different control types (e.g.CRate(4.2)andVoltage(4.2)) with same value. (#5529) - Fixed
ProcessedVariable.sensitivitiesraisingKeyErrorwhencalculate_sensitivitieswas passed a subset of inputs and a non-target input parameter (e.g. apybamm.InputParameterused in an experiment step) appeared in the variable's expression tree. (#5518)
v26.4.3
v26.4.3 - 2026-05-12
Bug fixes
ProcessedVariableComputed.__init__now defers the heavyinitialise_*work (numpy concatenate/flatten, xarrayDataArraybuild) untilentriesor_xr_data_arrayis actually read. On a 500-cycle SPM withoutput_variablesset andsave_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%.IDAKLUSolvernow recordsSolution.closest_event_idxafter a SUNDIALS root return, soBaseSolver.get_termination_reasoncan short-circuit instead of re-walking every TERMINATION event's symbolic expression on the Python side. On a 1000-cycle SPM withoutput_variablesset, 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_experimentdropping every constructor argument other than per-stepvalue/duration/terminations/temperature. The top-levelperiod,temperature, andterminationarguments topybamm.Experimentand the per-stepperiod,tags,description,start_time,direction, andskip_okarguments toBaseStepare now written byto_config()and parsed back byfrom_config(), so JSON round-tripped experiments preserve user intent. TheResistancestep type was also missing from the deserialiser's step-type map and now round-trips correctly. - Fixed
Serialise.load_custom_modelreconstructingEvent.event_typeas the bare enum name string (e.g."TERMINATION") instead of the correspondingEventTypemember. The custom JSON encoder writes Enum values as their.name, but the loader was passing the string straight through topybamm.Event.__init__, so models round-tripped throughto_json/from_jsoncarried string event types rather thanEventTypeenum values. (#5498) - Fixed
Serialise.serialise_solversilently droppingroot_method(and any other nestedBaseSolver__init__argument). After construction,root_methodis aBaseSolverinstance rather than the original string, so it failedjson.dumpsand was omitted from the config — making the deserialised solver fall back to the default.to_config()/from_config()now recurse into nested solver values, preservingroot_methodand its tolerances across the round-trip. (#5497) - Fixed
Serialise.serialise_experimentsilently dropping per-steptemperatureoverrides. The JSON-config round-trip viaExperiment.to_config()/Experiment.from_config()now preservestemperaturefor current, voltage, power, c-rate, and rest steps. (#5496) - Fixed
Serialise._to_json_safecoercing Pythonboolvalues to0/1ints becauseboolis a subclass ofint.IDAKLUSolver.to_config()now emits its bool options (compile,print_stats,silence_sundials_errors, etc.) as JSONtrue/falseso they round-trip through strict-bool deserialisers. (#5495)