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

nan, inf, -inf

{"$type": "builtins.float", "value": "NaN"} (or "Infinity" / "-Infinity")

numpy.ndarray

{"$type": "numpy.ndarray", "data": [...], "dtype": "float64"}

slice

{"$type": "builtins.slice", "start": 0, "stop": 1, "step": null}

tuple

{"$type": "builtins.tuple", "items": [...]} (so it does not decode back as a list)

a class itself (not an instance)

{"$type": "type", "class": "module.ClassName"}

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

{"py/object": "dotted.path", "py/id": 1, ...}

discretised-model files

py/object becomes $type; py/id is dropped

{"type": "ClassName", ...}

symbol, parameter-values and experiment files

bare class names resolve as pybamm.ClassName

{"class": "ClassName", "module": "some.module"}

submesh and spatial-method type references

becomes a {"$type": "type"} class reference

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:

pybamm.Experiment

static deserialise_solver(data: dict)[source]#

Convert a config dict to a pybamm.BaseSolver instance.

Handles CompositeSolver by recursively deserialising sub_solvers.

Parameters:

data (dict) – Config dict as produced by serialise_solver().

Return type:

pybamm.BaseSolver

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.

Parameters:

filename (str or dict) – Path to the JSON file containing the saved geometry, or a dictionary containing the serialised geometry data.

Returns:

The reconstructed geometry object.

Return type:

pybamm.Geometry

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.BaseModel or 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:

pybamm.BaseModel

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.

Parameters:

filename (str or dict) – Path to the JSON file containing the saved spatial methods, or a dictionary containing the serialised spatial methods data.

Returns:

Dictionary mapping domain names to spatial method instances.

Return type:

dict

static load_submesh_types(filename: str | dict) dict[source]#

Loads submesh types from a JSON file or dictionary.

Parameters:

filename (str or dict) – Path to the JSON file containing the saved submesh types, or a dictionary containing the serialised submesh types data.

Returns:

Dictionary mapping domain names to MeshGenerator objects.

Return type:

dict

static load_var_pts(filename: str | dict) dict[source]#

Loads var_pts from a JSON file or dictionary.

Parameters:

filename (str or dict) – Path to the JSON file containing the saved var_pts, or a dictionary containing the serialised var_pts data.

Returns:

Dictionary mapping spatial variable names (strings) to number of points.

Return type:

dict

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 saved

  • mesh (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.

Parameters:
  • parameters (dict) – A dictionary of parameter names and values. Values can be numeric, PyBaMM symbols, or callables.

  • filename (str, optional) – If given, saves the serialized parameters to this file.

static save_spatial_methods(spatial_methods: dict, filename: str | Path | None = None) None[source]#

Saves spatial methods to a JSON file.

Parameters:
  • spatial_methods (dict) – Dictionary mapping domain names to spatial method instances.

  • 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_submesh_types(submesh_types: dict, filename: str | Path | None = None) None[source]#

Saves submesh types to a JSON file.

Parameters:
  • submesh_types (dict) – Dictionary mapping domain names to submesh classes.

  • 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_var_pts(var_pts: dict, filename: str | Path | None = None) None[source]#

Saves var_pts to a JSON file.

Parameters:
  • var_pts (dict) – Dictionary mapping spatial variable names to number of points.

  • filename (str or Path, optional) – The desired name of the JSON file. If not provided, a name will be generated using current datetime.

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:

dict

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:

dict

Raises:

AttributeError – If the model is missing required sections

static serialise_experiment(experiment) dict[source]#

Convert a pybamm.Experiment to a JSON-serialisable dict.

Returns {"cycles": [[step_config, ...], ...]}, grouping steps into cycles according to experiment.cycle_lengths.

Parameters:

experiment (pybamm.Experiment) – The experiment to serialise.

Returns:

Config dict with key "cycles".

Return type:

dict

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 serialised

  • mesh (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:

dict

static serialise_solver(solver) dict[source]#

Convert a pybamm.BaseSolver to a JSON-serialisable config dict.

Uses inspect.signature to discover __init__ parameters, reads the corresponding attribute values from the instance (trying both solver.<name> and solver._<name>), and filters out values that are not JSON-serialisable. Handles CompositeSolver recursively.

Parameters:

solver (pybamm.BaseSolver) – The solver to serialise.

Returns:

Config dict with a "type" key and one key per serialisable init parameter.

Return type:

dict

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.

Parameters:

spatial_methods (dict) – Dictionary mapping domain names to spatial method instances.

Returns:

A JSON-serialisable dictionary representation of the spatial methods

Return type:

dict

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.

Parameters:

submesh_types (dict) – Dictionary mapping domain names to submesh classes or MeshGenerator objects.

Returns:

A JSON-serialisable dictionary representation of the submesh types

Return type:

dict

static serialise_var_pts(var_pts: dict) dict[source]#

Converts a var_pts dictionary to a JSON-serialisable dictionary.

Parameters:

var_pts (dict) – Dictionary mapping spatial variable names (str or SpatialVariable) to number of points (int).

Returns:

A JSON-serialisable dictionary representation of var_pts

Return type:

dict