> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ionworks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Simulation settings

> Attach persistent mesh and solver settings to a model or parameterized model so every simulation runs on the grid the parameters need.

**Simulation settings** are an optional persistent bag of PyBaMM `Simulation`
keyword arguments (`var_pts`, `submesh_types`, `spatial_methods`, `solver`)
that Ionworks stores on a [Model](/build/models) or
[Parameterized Model](/build/parameterized-models) and re-applies every time
the model is simulated.

A model's equations do not depend on the mesh or solver, but the settings a
fitted parameter set *needs* often do. A fitted solid diffusivity with a steep
near-surface gradient, for example, needs a refined, surface-clustered particle
mesh to solve accurately — and defaulting back to PyBaMM's coarse grid would
give the wrong voltage. Simulation settings let you pin those requirements to
the model or parameter set once, so every downstream simulation runs on the
grid and solver the parameters were fit against.

## When to use them

Attach simulation settings when either of these is true:

* Your parameters were fit with a non-default mesh (for example, a refined
  particle mesh with a `Chebyshev1DSubMesh`) and you want that mesh applied
  automatically whenever the parameterized model runs.
* A specific model configuration requires a particular solver or solver
  tolerance to be numerically stable.

If your model runs correctly on PyBaMM's defaults, you do not need to set
anything — leave `simulation_settings` unset (or `None`) and PyBaMM's own
`default_var_pts` / `default_submesh_types` / `default_spatial_methods` /
`default_solver` are used.

## What you can configure

All fields are optional. Any field you omit falls back to the model default.

| Field             | Type                              | Purpose                                                                                         |
| ----------------- | --------------------------------- | ----------------------------------------------------------------------------------------------- |
| `var_pts`         | `{spatial_variable: int}`         | Number of mesh points per spatial variable (e.g. `{"r_n": 16, "r_p": 16}`).                     |
| `submesh_types`   | `{domain: submesh config}`        | Serialized `pybamm.MeshGenerator` per domain (e.g. `"negative particle"`).                      |
| `spatial_methods` | `{domain: spatial method config}` | Serialized `pybamm.SpatialMethod` per domain.                                                   |
| `solver`          | `dict` or `pybamm.BaseSolver`     | Serialized `pybamm.BaseSolver.to_config()` payload (solver class name plus tolerances/options). |

Every class named in a stored block is checked against a static allowlist on
write, so a class that isn't listed below is rejected with a `BAD_REQUEST`
rather than stored and resolved later.

Allowed spatial variables for `var_pts`: `x_n`, `x_s`, `x_p`, `r_n`, `r_p`,
`R_n`, `R_p`, `y`, `z`, and their `_prim` / `_sec` variants for composite
electrodes. Values must be positive integers.

Allowed submesh classes: `Uniform1DSubMesh`, `Exponential1DSubMesh`,
`Chebyshev1DSubMesh`, `UserSupplied1DSubMesh`, `SpectralVolume1DSubMesh`,
`SymbolicUniform1DSubMesh`, `SubMesh0D`.

Allowed spatial-method classes: `FiniteVolume`, `SpectralVolume`,
`ZeroDimensionalSpatialMethod`.

Allowed solver classes: `IDAKLUSolver`, `AlgebraicSolver`, `NonlinearSolver`.
`CasadiSolver` and `ScipySolver` are deprecated in PyBaMM and cannot be
persisted. See [Ionworks DAE Solver](/guide/modeling/ionworks-solver) for the
fast default used behind `ionworkspipeline.Simulation`.

<Warning>
  `geometry` cannot be persisted. A stored geometry override is rejected on
  write with `simulation_settings.geometry is not supported` — deserializing one
  reconstructs arbitrary PyBaMM expression-tree symbols from JSON, which the
  class allowlist above cannot bound. Persist `var_pts` / `submesh_types` /
  `spatial_methods` / `solver` instead, and pass a geometry override at run time
  via `simulation_kwargs` if you need one.
</Warning>

## Precedence

At simulation time, Ionworks merges the persisted settings with a fixed
precedence — higher layers win, per key:

```
runtime simulation_kwargs  >  parameterized model  >  model  >  pybamm defaults
```

* **Mesh keys** (`var_pts`, `submesh_types`, `spatial_methods`) merge *per
  key* — and per key over the model's own PyBaMM defaults, so the map handed
  to `Simulation` is always complete. A parameterized model can therefore
  refine just `r_n` and `r_p` without restating the rest of the grid, and a
  runtime `simulation_kwargs` that names only `r_p` overrides that one entry
  rather than dropping the persisted `r_n`.
* **`solver`** is replaced *wholesale* by the highest layer that sets it — a
  partial solver is ambiguous.
* An explicit per-run `simulation_kwargs` override always wins, so debugging
  a single run never requires editing the persisted settings.

<Note>
  Pipeline **design objectives** are the one exception: only the resolved
  `var_pts` and `solver` are folded into them. A persisted `submesh_types` or
  `spatial_methods` is deliberately not applied there, because a design
  objective needs the symbolic submesh the design-optimization converter
  installs. Protocol simulations and the model/parameterized-model simulate path
  apply all four keys.
</Note>

## Attaching settings through the API

Both `client.model` and `client.parameterized_model` accept a
`simulation_settings` field on create and update. See the
[Python API reference](/build/api) for the full sub-client surface.

### On a model

```python theme={null}
from ionworks import Ionworks

client = Ionworks()

model = client.model.create({
    "name": "DFN with refined particle mesh",
    "config": {"type": "DFN"},
    "simulation_settings": {
        "var_pts": {"r_n": 20, "r_p": 20},
    },
})
```

### On a parameterized model

Parameter-specific settings take precedence over the base model's settings,
so this is where fitted-parameter mesh refinements usually belong:

```python theme={null}
param_model = client.parameterized_model.create("your-cell-spec-id", {
    "name": "NMC622 fitted parameters",
    "model_id": model.id,
    "parameters": {
        "Negative electrode diffusivity [m2.s-1]": 3.3e-14,
    },
    "simulation_settings": {
        "var_pts": {"r_n": 32, "r_p": 32},
        "solver": {
            "type": "IDAKLUSolver",
            "atol": 1e-8,
            "rtol": 1e-6,
        },
    },
})
```

Update the settings later:

```python theme={null}
client.parameterized_model.update(
    "your-cell-spec-id",
    param_model.id,
    {"simulation_settings": {"var_pts": {"r_n": 40, "r_p": 40}}},
)
```

<Warning>
  The update is partial at the *field* level, not inside `simulation_settings`:
  whatever you send **replaces the whole stored block**. The call above discards
  the `solver` persisted at create time. To change one key, read the current
  settings, merge locally, and send the complete block:

  ```python theme={null}
  current = client.parameterized_model.get(param_model.id).simulation_settings or {}

  client.parameterized_model.update(
      "your-cell-spec-id",
      param_model.id,
      {"simulation_settings": {**current, "var_pts": {"r_n": 40, "r_p": 40}}},
  )
  ```

  The per-key merging described under [Precedence](#precedence) applies across
  the model / parameterized-model / runtime *layers* at simulation time — it does
  not merge an update into the row you are writing.
</Warning>

Passing `"simulation_settings": None` clears the field and inherits the base
model's settings (or the PyBaMM defaults if the base model has none either).

## Attaching settings in a pipeline

The [`ionworks_schema`](/pipelines/overview) model classes (`GITTModel`,
`MSMRFullCellModel`, `MSMRHalfCellModel`, `LumpedSPMR`, `LumpedSPMeR`, `ECM`,
...) accept a `simulation_settings` argument that is serialized alongside the
model config when the pipeline is submitted:

```python theme={null}
import pybamm
import ionworks_schema as iws

model = iws.models.GITTModel(
    options={"working electrode": "positive"},
    simulation_settings=iws.models.SimulationSettings(
        var_pts={"r_p": 32},
        submesh_types={
            "positive particle": pybamm.MeshGenerator(
                pybamm.Chebyshev1DSubMesh,
            ),
        },
    ),
)
```

The pipeline serializes the live `MeshGenerator` / `BaseSolver` objects to
their canonical JSON form automatically, so the same settings survive a round
trip through the API.

## Serialized form

Under the hood, simulation settings are stored as a flat JSON dict — this is
also what `client.model.get(...).simulation_settings` returns and what the
migration column holds. A typical block looks like:

```json theme={null}
{
  "var_pts": {"r_n": 20, "r_p": 20},
  "submesh_types": {
    "negative particle": {
      "$type": "type",
      "class": "pybamm.meshes.one_dimensional_submeshes.Chebyshev1DSubMesh",
      "submesh_params": {}
    }
  },
  "solver": {
    "type": "IDAKLUSolver",
    "atol": 1e-8,
    "rtol": 1e-6
  }
}
```

You can pass this dict form directly, or build it with the
`SimulationSettings` schema class and let `to_config()` produce the same
payload for you.
