Serialise#
PyBaMM serialises expression trees, discretised models, meshes, solvers and
related objects through a single encode/decode kernel
(pybamm.expression_tree.operations.serialise_kernel). The kernel is
safe-or-loud: a value either round-trips, or encoding raises
SerialisationError naming the offending class and field. It never silently
drops data. There is one canonical wire format (each node carries a $type
tag holding its dotted module.ClassName path), and a read-only compatibility
layer keeps files written by older PyBaMM versions loading.
Wire format#
encode produces plain JSON. Native values (None, booleans, integers,
strings, finite floats) pass through unchanged, lists and dicts stay JSON
arrays/objects, and every other object becomes a tagged node: its class-specific
fields plus a "$type" key holding the dotted module.ClassName path. Child
symbols (and other serialisable sub-objects) are recursively encoded under
"children". For example, pybamm.Index(pybamm.StateVector(slice(0, 2)), 0)
encodes as:
{
"name": "Index[0]",
"index": {"start": 0, "stop": 1, "step": null},
"check_size": false,
"children": [
{
"name": "y[0:2]",
"domains": {"primary": [], "secondary": [], "tertiary": [], "quaternary": []},
"y_slice": [{"start": 0, "stop": 2, "step": null}],
"evaluation_array": [true, true],
"$type": "pybamm.expression_tree.state_vector.StateVector"
}
],
"$type": "pybamm.expression_tree.unary_operators.Index"
}
On decode the "$type" path is resolved with importlib, so classes defined
outside the pybamm package round-trip too. Apart from "$type" and
"children", the keys of a node belong to the class (its constructor
parameters, or whatever its to_json hook emits).
A few non-class values the kernel encodes itself use reserved "$type" tags:
Value |
Encoding |
|---|---|
|
|
|
|
|
|
|
|
a class itself (not an instance) |
|
Legacy formats (read-only)#
Files written by older PyBaMM versions used three different shapes. All three
still load: decode passes every dict through normalise_legacy, which maps
them onto the canonical tag. New files are always written in the canonical
format; the legacy shapes are never written.
Legacy shape |
Previously written by |
Normalisation |
|---|---|---|
|
discretised-model files |
|
|
symbol, parameter-values and experiment files |
bare class names resolve as |
|
submesh and spatial-method type references |
becomes a |
Making a class serialisable#
Usually you do nothing. A new Symbol (or other serialisable) subclass is
encoded automatically by the introspection DefaultCodec: it reads each
__init__ parameter back off the instance (by the same name, or the
_-prefixed private attribute) and emits it. If your class stores its
constructor arguments as same-named attributes, it round-trips with no extra
code.
The safe-or-loud contract tells you when you must act. If a stored or
required __init__ parameter is not captured, encode raises
SerialisationError naming the class and the parameter the first time the
class is serialised. The strategy-coverage meta-tests
(tests/unit/test_serialisation/test_symbol_strategy_coverage.py and
test_base_strategy_coverage.py) turn a new concrete subclass with no
round-trip coverage into a CI failure. So a gap is a loud failure, never a silent
one.
Add a hook when reconstruction is irregular. Define to_json /
_from_json on the class when it cannot be rebuilt by simply replaying its
__init__ parameters, for example when a constructor argument is itself a
Symbol or another serialisable object. The kernel only recurses the
children list, so any ``Symbol``-valued argument must travel through
``children``; every other field a hook emits must already be JSON-native.
class MyEvent(pybamm.Symbol):
def __init__(self, name, expression):
super().__init__(name, children=[expression])
def to_json(self):
# ``expression`` is a Symbol, so it rides in children; the kernel
# recurses children and reconstructs it for us.
return {"name": self.name, "children": [self.children[0]]}
@classmethod
def _from_json(cls, snippet):
return cls(snippet["name"], snippet["children"][0])
Opt out genuinely derived fields explicitly. If a constructor parameter is
re-derived on construction (so it need not be stored) or is a non-serialisable
transient, list its name in the class’s _serialise_derived_params frozenset.
This is a reviewed, in-diff decision that the coverage guard honours; it is not a
runtime skip. See Mesh, SubMesh and DomainConcatenation for existing
examples (each waives construction inputs it rebuilds from other serialised
fields on decode).
class MySubMesh(pybamm.SubMesh):
# ``builder`` is only used to compute ``edges`` in __init__; the mesh is
# reconstructed from ``edges``, so ``builder`` is never serialised.
_serialise_derived_params = frozenset({"builder"})
Register round-trip coverage. Add (or extend) a strategy under
tests/strategies/ so your class is exercised by the round-trip property
tests, or – if it is intentionally not independently round-trippable – add it to
the relevant base’s exemption set in the coverage meta-test with a one-line
reason. The meta-test will tell you exactly which classes are uncovered.
Class references#
A bare class (rather than an instance) is serialised through the kernel’s
class-reference codec as {"$type": "type", "class": "module.ClassName"} (used
for submesh types and spatial methods). The legacy {"class": ..., "module":
...} shape is still read on decode.
API#
- class pybamm.expression_tree.operations.serialise.Serialise[source]#
Converts a discretised model to and from a JSON file.
- static deserialise_experiment(data: dict)[source]#
Convert a config dict to a
pybamm.Experiment.Accepts
{"cycles": [[step_config, ...], ...]}(new format) or{"steps": [step_config, ...]}(legacy flat format).- Parameters:
data (dict) – Config dict as produced by
serialise_experiment().- Return type:
- static deserialise_solver(data: dict)[source]#
Convert a config dict to a
pybamm.BaseSolverinstance.Handles
CompositeSolverby recursively deserialisingsub_solvers.- Parameters:
data (dict) – Config dict as produced by
serialise_solver().- Return type:
- static deserialise_spatial_method_item(method_info: dict)[source]#
Deserialise a spatial method from either the kernel class_reference shape or the legacy {class, module} shape.
- static deserialise_submesh_item(submesh_info: dict, return_class_only: bool = False)[source]#
Deserialise a SubMesh class / MeshGenerator from either the kernel class_reference shape or the legacy {class, module} shape.
- static load_custom_geometry(filename: str | dict) Geometry[source]#
Loads a custom PyBaMM geometry from a JSON file or dictionary.
- static load_custom_model(filename: str | dict) BaseModel[source]#
Loads a custom (symbolic) PyBaMM model from a JSON file or dictionary.
Reconstructs a model saved using save_custom_model, including its rhs, algebraic equations, initial and boundary conditions, events, and variables. Returns a fully symbolic model ready for further processing or discretisation.
Automatically detects and decompresses data that was serialised with compression enabled (compress=True in serialise_custom_model).
- Parameters:
filename (str or dict) – Path to the JSON file containing the saved model, or a dictionary containing the serialised model data (optionally compressed).
- Returns:
The reconstructed symbolic PyBaMM model.
- Return type:
pybamm.BaseModelor subclass
Example
>>> import pybamm >>> model = pybamm.lithium_ion.BasicDFN() >>> from pybamm.expression_tree.operations.serialise import Serialise >>> Serialise.save_custom_model(model, "basicdfn_model.json") >>> loaded_model = Serialise.load_custom_model("basicdfn_model.json")
- load_model(filename: str | dict, battery_model: BaseModel | None = None) BaseModel[source]#
Loads a discretised, ready to solve model into PyBaMM.
A new pybamm battery model instance will be created, which can be solved and the results plotted as usual.
Currently only available for pybamm models which have previously been written out using the save_model() option.
Warning: This only loads in discretised models. If you wish to make edits to the model or initial conditions, a new model will need to be constructed seperately.
- Parameters:
filename (str or dict) – Path to the JSON file containing the serialised model file, or a dictionary containing the serialised model data
battery_model (
pybamm.BaseModel(optional)) – PyBaMM model to be created (e.g. pybamm.lithium_ion.SPM), which will override any model names within the file. If None, the function will look for the saved object path, present if the original model came from PyBaMM.
- Returns:
A PyBaMM model object, of type specified either in the JSON or in battery_model.
- Return type:
- static load_parameters(filename)[source]#
Load a JSON file of parameters (either from Serialise.save_parameters or from a standard pybamm.ParameterValues.save), and return a pybamm.ParameterValues object.
If a value is a dict with a “$type” (kernel) or legacy “type” key, deserialize it as a PyBaMM symbol.
Otherwise (float, int, bool, str, list, dict-without-type), leave it as-is.
- static load_spatial_methods(filename: str | dict) dict[source]#
Loads spatial methods from a JSON file or dictionary.
- static load_submesh_types(filename: str | dict) dict[source]#
Loads submesh types from a JSON file or dictionary.
- static load_var_pts(filename: str | dict) dict[source]#
Loads var_pts from a JSON file or dictionary.
- static save_custom_geometry(geometry: Geometry, filename: str | Path | None = None) None[source]#
Saves a custom PyBaMM geometry to a JSON file.
- Parameters:
geometry (
pybamm.Geometry) – The geometry object to be saved.filename (str or Path, optional) – The desired name of the JSON file. If not provided, a name will be generated using current datetime.
- static save_custom_model(model: BaseModel, filename: str | Path | None = None, compress: bool = False) None[source]#
Saves a custom (non-discretised) PyBaMM model to a JSON file. Works for user defined models that are subclasses of BaseModel.
This includes symbolic expressions for rhs, algebraic, initial and boundary conditions, events, and variables. Useful for storing or sharing models before discretisation.
- Parameters:
model (
pybamm.BaseModel) – The custom symbolic model to be saved.filename (str, optional) – The desired name of the JSON file. If not provided, a name will be generated from the model name and current datetime.
compress (bool, optional) – If True, the model data will be compressed using zlib before saving. This can significantly reduce file size. Default is False.
Example
>>> import pybamm >>> model = pybamm.lithium_ion.BasicDFN() >>> from pybamm.expression_tree.operations.serialise import Serialise >>> Serialise.save_custom_model(model, "basicdfn_model.json") >>> # Or with compression: >>> Serialise.save_custom_model(model, "basicdfn_model.json", compress=True)
- save_model(model: BaseModel, mesh: Mesh | None = None, variables: None = None, filename: str | None = None)[source]#
Saves a discretised model to a JSON file.
As the model is discretised and ready to solve, only the right hand side, algebraic and initial condition variables are saved.
- Parameters:
model (
pybamm.BaseModel) – The discretised model to be savedmesh (
pybamm.Mesh(optional)) – The mesh the model has been discretised over. Not neccesary to solve the model when read in, but required to use pybamm’s plotting tools.variables (None (optional)) – This parameter is deprecated and enabled by default.
filename (str (optional)) – The desired name of the JSON file. If no name is provided, one will be created based on the model name, and the current datetime.
- static save_parameters(parameters: dict, filename=None)[source]#
Serializes a dictionary of parameters to a JSON file. The values can be numbers, PyBaMM symbols, or callables.
- static save_spatial_methods(spatial_methods: dict, filename: str | Path | None = None) None[source]#
Saves spatial methods to a JSON file.
- static save_submesh_types(submesh_types: dict, filename: str | Path | None = None) None[source]#
Saves submesh types to a JSON file.
- static save_var_pts(var_pts: dict, filename: str | Path | None = None) None[source]#
Saves var_pts to a JSON file.
- static serialise_custom_geometry(geometry: Geometry) dict[source]#
Converts a custom PyBaMM geometry to a JSON-serialisable dictionary.
- Parameters:
geometry (
pybamm.Geometry) – The geometry object to be serialised.- Returns:
A JSON-serialisable dictionary representation of the geometry
- Return type:
- static serialise_custom_model(model: BaseModel, compress: bool = False) dict[source]#
Converts a custom (non-discretised) PyBaMM model to a JSON-serialisable dictionary.
This includes symbolic expressions for rhs, algebraic, initial and boundary conditions, events, and variables. Works for user defined models that are subclasses of BaseModel.
- Parameters:
model (
pybamm.BaseModel) – The custom symbolic model to be serialised.compress (bool, optional) – If True, the resulting dictionary will be compressed using zlib and encoded as base64. The output will contain a “compressed” flag set to True and a “data” field with the compressed payload. Default is False.
- Returns:
A JSON-serialisable dictionary representation of the model. If compress is True, returns {“compressed”: True, “data”: <base64-encoded-zlib-data>}.
- Return type:
- Raises:
AttributeError – If the model is missing required sections
- static serialise_experiment(experiment) dict[source]#
Convert a
pybamm.Experimentto a JSON-serialisable dict.Returns
{"cycles": [[step_config, ...], ...]}, grouping steps into cycles according toexperiment.cycle_lengths.- Parameters:
experiment (
pybamm.Experiment) – The experiment to serialise.- Returns:
Config dict with key
"cycles".- Return type:
- Raises:
NotImplementedError – If a step uses a custom callable (
CustomTermination,CustomStepExplicit,CustomStepImplicit); these have no JSON representation.
- serialise_model(model: BaseModel, mesh: Mesh | None = None, variables: None = None) dict[source]#
Converts a discretised model to a JSON-serialisable dictionary.
As the model is discretised and ready to solve, only the right hand side, algebraic and initial condition variables are serialised.
- Parameters:
model (
pybamm.BaseModel) – The discretised model to be serialisedmesh (
pybamm.Mesh(optional)) – The mesh the model has been discretised over. Not necessary to solve the model when read in, but required to use pybamm’s plotting tools.variables (None (optional)) – This parameter is deprecated and enabled by default.
- Returns:
A JSON-serialisable dictionary representation of the model
- Return type:
- static serialise_solver(solver) dict[source]#
Convert a
pybamm.BaseSolverto a JSON-serialisable config dict.Uses
inspect.signatureto discover__init__parameters, reads the corresponding attribute values from the instance (trying bothsolver.<name>andsolver._<name>), and filters out values that are not JSON-serialisable. HandlesCompositeSolverrecursively.- Parameters:
solver (
pybamm.BaseSolver) – The solver to serialise.- Returns:
Config dict with a
"type"key and one key per serialisable init parameter.- Return type:
- static serialise_spatial_method_item(method) dict[source]#
Serialise a spatial method. The class is encoded via the kernel’s class_reference codec; its options ride alongside under “options”.
- static serialise_spatial_methods(spatial_methods: dict) dict[source]#
Converts a dictionary of spatial methods to a JSON-serialisable dictionary.
- static serialise_submesh_item(submesh_item) dict[source]#
Serialise a SubMesh class or MeshGenerator. The class is encoded via the kernel’s class_reference codec ({“$type”: “type”, “class”: dotted-path}); a MeshGenerator’s params ride alongside under “submesh_params”.
- static serialise_submesh_types(submesh_types: dict) dict[source]#
Converts a dictionary of submesh types to a JSON-serialisable dictionary.