# Run

The run lifecycle: the driver, the content-addressed bundle it writes per solve, and the run and result readers.

## femforge.run

Run lifecycle: the content-addressed bundle a `Driver` writes per solve and a `Run` opens for reading.

Modules:

- **`artifacts`** –

  The run side of the built-in artifacts: rebinding a cached mesh's groups, the bundle file policy, and rehydration.
- **`bundle`** –

  Read and write the `runs/<run_id>/` bundle: a content-addressed run directory holding the manifest, the typed result and the pinned geometry.
- **`convergence`** –

  Mesh convergence: re-run one configuration at increasing refinement, then Richardson-extrapolate the quantity of interest.
- **`driver`** –

  The driver: assemble one run end-to-end (identity, reuse, mesh + sif + solve, parse, bundle, publish, retention); the generators (`Mesher`, `Sim`) stay run-ignorant.
- **`electrical`** –

  Per-phase electrical quantities of a current-conduction run, from the electrode flux columns in `Result.raw` and `boundaries.yml`.
- **`entities`** –

  Named bodies and boundaries of a run bundle, resolved to the mesh ids the VTU's `GeometryIds` cell array uses.
- **`fields`** –

  Fluent numpy queries over a solved VTU's fields, keyed by body names, tags, and mesh ids.
- **`inspect`** –

  The discoverable face of a run bundle: `Run(sim_dir)` opens typed results, named entities, components, and the VTU field query.
- **`kinds`** –

  Run identity and the built-in artifact kinds: the STEP a geometry builds, the Elmer mesh a config meshes, and the bundle a run leaves.
- **`magnetic`** –

  Per-component magnetic quantities of a magnetostatics run, joined from the SaveScalars columns in `Result.raw` and `components.yml`.
- **`manifest`** –

  Run provenance: the self-describing run manifest and the git version stamp.
- **`parse`** –

  Parse Elmer outputs into a typed `Result`: SaveScalars columns for the numbers, the solver log for status, residual and wall time.
- **`purge`** –

  Post-hoc retention over existing run bundles: one bundle or a whole runs directory, dry-run by default.
- **`reap`** –

  Abandoned runs: the bundles left `running` with no live claim on them, marked so and their claims broken.
- **`result`** –

  The typed outcome of a solve: Elmer outputs parsed into validated, pint-typed fields.
- **`retention`** –

  Run retention: which classes of bundle files leave after a run, the policy that says so, and the engine that applies it.
- **`stages`** –

  Pipeline stage vocabulary: the driver's stages in execution order, plus the timed event a run records per stage.

Classes:

- **`BundlePolicy`** –

  Which bundle files a published run bundle carries.
- **`MeshGroupsFile`** –

  `groups.json`: the group records and element count a mesh artifact carries beside ElmerGrid's files.
- **`MeshRebindError`** –

  A cached mesh cannot be bound to the current config's domain.
- **`ConvergencePoint`** –

  One rung of the refinement ladder: the refinement level, the resulting quantity, and its relative change from the previous (coarser) rung.
- **`RichardsonEstimate`** –

  Richardson extrapolation of a ladder's finest three rungs, assuming q(h) = q\* + C·hᵖ.
- **`Driver`** –

  Runs each config through the pipeline into a `runs/<run_id>/` bundle and returns its typed `Result`.
- **`Bodies`** –

  Name-keyed collection of a run's bodies.
- **`Body`** –

  One named body group of a run.
- **`Boundaries`** –

  Name-keyed collection of a run's boundaries, with adjacency lookups powered by the `A__B` name convention.
- **`Boundary`** –

  One named boundary group of a run.
- **`BoundaryKind`** –

  What a boundary group separates: two bodies, a body's external shell, or its trace on the 2D symmetry axis.
- **`Entities`** –

  A bundle's named bodies and boundaries.
- **`FieldMesh`** –

  A solved VTU as cell-space numpy arrays with named-body selection; open one via `load` or `Run.mesh()`.
- **`FieldQuery`** –

  One field array under an immutable cell mask: filters return refined copies, terminals return numpy values.
- **`Run`** –

  One run bundle (`runs/<run_id>/`) opened for reading.
- **`SifComponent`** –

  One sif Component block with its lumped force and torque.
- **`MeshSpec`** –

  Everything that decides the Elmer mesh files and their group names, and nothing that only decides the solve.
- **`RunSpec`** –

  The bundle kind's spec: the config a run solved.
- **`RunManifest`** –

  The self-describing record of one run.
- **`ReapedRun`** –

  One run `reap_runs` settled.
- **`MeshStats`** –

  Per-run mesh size/quality summary, recorded in the manifest so a sweep can be audited for mesh consistency.
- **`Result`** –

  The typed outcome of one solve.
- **`BundleEntry`** –

  One file found in a bundle, with the retention class its name falls in.
- **`Cleanup`** –

  When a file class leaves the bundle; definition order is escalation order.
- **`CleanupRecord`** –

  One cleanup pass over a bundle: when, by whom, under which policy, and what left.
- **`CleanupReport`** –

  The outcome of applying a plan: what left (or would have, in a dry run) and what could not be removed.
- **`Preset`** –

  Named retention policies, from keep-everything to removing the run directory.
- **`RetentionPolicy`** –

  One `Cleanup` rule per bundle file class; the default keeps everything the run cannot re-fetch.
- **`Stage`** –

  The driver's pipeline stages, in execution order.
- **`StageEvent`** –

  One stage's timing record: created at stage start, mutated with `finished_at` when the stage completes.

Functions:

- **`bundle_files`** –

  The bundle-relative posix paths `policy` publishes from `sim_dir`.
- **`rebind_groups`** –

  Rebuild a cached mesh's groups against this config's domain, matching materials by tag and `EntityTag` objects by selector; ids stay placeholders until `reconcile_ids`.
- **`rehydrate_bundle`** –

  Fetch every store-sourced input the manifest names back into the bundle.
- **`load_manifest`** –

  Read a bundle's `manifest.json` back into a typed `RunManifest` — a delegate to `RunManifest.load`.
- **`load_result`** –

  Read a bundle's `result.json` back into a typed `Result` — a delegate to `Result.load`.
- **`write_atomic`** –

  Replace `path` atomically: write a `.tmp` sibling, then `os.replace` — a reader never sees a half-written file.
- **`write_bundle`** –

  Write `manifest.json` and `result.json` into `sim_dir`, pin the source STEP as `geometry.step` unless the run already holds one, and return the bundle.
- **`write_manifest`** –

  Atomically write (or overwrite) `manifest.json` in `sim_dir` (created if needed) and return its path.
- **`convergence_ladder`** –

  Run `base_config` at each refinement level and report the quantity of interest plus its relative change between rungs.
- **`richardson`** –

  Richardson-extrapolate the finest three rungs of a ladder whose `scales` are mesh-size multipliers (larger is coarser) paired with `values`.
- **`boundary_fluxes`** –

  Elmer bc index -\> integrated conductive flux (A) from the SaveScalars columns in `Result.raw`.
- **`electrode_currents`** –

  Boundary block name -\> terminal current (A), joining the flux columns against the bc-index name map.
- **`phase_currents`** –

  Phase index ("0", "1", ...) -\> terminal current (A): the largest-magnitude current among each phase's named electrode currents.
- **`read_boundary_names`** –

  The `boundaries.yml` map the Sim writes (`<bc index>: <boundary block name>`), as a dict.
- **`resistance`** –

  R = ΔV / I for one phase; raises on a (numerically) zero current.
- **`load_entities`** –

  Read a bundle's bodies/boundaries: `entities.json` when present, else `mesh.names` enriched from the manifest config, else empty.
- **`write_entities`** –

  Write `entities.json` mapping each group's reconciled mesh id to its named record; call after `reconcile_ids`.
- **`select_array`** –

  Resolve a VTU array name for a solver variable: exact (nodal), then the elemental `"<name> e"`, then substring — VTU names come back lowercased.
- **`find_runs`** –

  The bundles under `runs_dir` whose metadata matches, newest first; this reads every manifest, so prefer `RunIndex` over thousands of bundles.
- **`mesh_spec`** –

  The mesh artifact spec of `config` (`step_key_override` when the driver already resolved the geometry).
- **`step_digest`** –

  SHA-256 of a STEP with its `FILE_NAME` header (the export timestamp) masked; of the path string when the file is absent, so pure-logic keys stay deterministic.
- **`component_forces`** –

  Component name -\> lumped magnetic force (N, per radian in an axisymmetric run), joining the force triples against the `components.yml` map.
- **`component_torques`** –

  Component name -\> lumped magnetic torque (N\*m about the authored origin/axis, per radian in an axisymmetric run), joining the torque triples against the `components.yml` map.
- **`eddy_current_power`** –

  The total eddy current power (W, per radian in an axisymmetric run), or None when the run exported none (static solves have no eddy losses).
- **`force_triples`** –

  Column stem -\> per-component force Vec3 (N, per radian in an axisymmetric run) from the SaveScalars columns in `Result.raw`.
- **`magnetic_field_energy`** –

  The total magnetic field energy (J, per radian in an axisymmetric run), or None when the run exported none.
- **`read_component_names`** –

  The `components.yml` map the Sim writes (`<component id>: <Component name>`) — the same flat format as `boundaries.yml`.
- **`cache_key`** –

  Full-run identity: every result-affecting input plus `RUN_PRODUCER_VERSION`; never the code commit, the machine, or the run metadata.
- **`git_sha`** –

  The current commit, with `-dirty` appended when the working tree has uncommitted changes; None without git or outside a repository.
- **`mesh_key`** –

  The mesh artifact key of `config`: invariant to materials' properties, conditions, physics, and metadata.
- **`step_key`** –

  The solve-frame geometry identity a run and its mesh key on: the STEP artifact key of `step_spec(config)`.
- **`parse_result`** –

  Parse a finished run directory into a typed `Result`, revolving force and torque to full-revolution totals under an `Axisymmetric` frame.
- **`result_vtu`** –

  The run's newest ParaView output: the `.pvtu` master when the solve was partitioned, else the newest `.vtu`; None when there is none.
- **`reap_runs`** –

  Mark every bundle under `runs_dir` left `running` with no live claim on it as `abandoned`, and break every claim that went silent.
- **`apply_cleanup`** –

  Remove every planned path; never raises, so a path that cannot be removed lands in `report.failed`.
- **`bundle_inventory`** –

  Every file under a bundle with the retention class its name matches; a name no class claims is filed under `RunFile.RUN_DIR`, the only rule that reaches it.
- **`plan_cleanup`** –

  Resolve `policy` against one bundle into the sized paths each applying rule removes; refuses a symlinked or junctioned directory.

Attributes:

- **`SolverFn`** –

  A solve step that fills `sim_dir` with Elmer outputs and reports the mesh it built; injecting one replaces the gmsh and Elmer pipeline.
- **`GEOMETRY_IDS_ARRAY`** –

  The Int cell array `ResultOutputSolver` (Save Geometry Ids) writes: each cell's mesh id, bodies and boundaries alike.
- **`ELMER_MESH`** (`ArtifactKind[MeshSpec]`) –

  ElmerGrid's mesh files plus `groups.json`; produced by the driver, which owns gmsh and ElmerGrid.
- **`RUN_BUNDLE`** (`ArtifactKind[RunSpec]`) –

  A finished run bundle, keyed by `cache_key` so its store key and its `run_id` agree.
- **`STEP`** (`ArtifactKind[StepSpec]`) –

  The solve-frame STEP of a parametric geometry or an authored file, keyed by its source (`HashExclude` fields left out) and the frame.
- **`ManifestStatus`** –

  A manifest's lifecycle status: `"running"` while the pipeline executes, then the terminal `RunStatus`, or `"abandoned"` when its claim went silent first.
- **`RunStatus`** –

  The terminal state of a solve: completed cleanly, failed to converge, or errored out.

### SolverFn

```python
SolverFn = Callable[[SimulationConfig, Path], MeshStats]
```

A solve step that fills `sim_dir` with Elmer outputs and reports the mesh it built; injecting one replaces the gmsh and Elmer pipeline.

### GEOMETRY\_IDS\_ARRAY

```python
GEOMETRY_IDS_ARRAY = 'GeometryIds'
```

The Int cell array `ResultOutputSolver` (Save Geometry Ids) writes: each cell's mesh id, bodies and boundaries alike.

### ELMER\_MESH

```python
ELMER_MESH: ArtifactKind[MeshSpec] = ArtifactKind(name=BuiltinKind.ELMER_MESH, spec_type=MeshSpec, producer_version=MESH_PRODUCER_VERSION, required_files=(BundleFile.MESH_HEADER, BundleFile.MESH_NODES, BundleFile.MESH_ELEMENTS, BundleFile.MESH_BOUNDARY, BundleFile.MESH_NAMES, BundleFile.GROUPS))
```

ElmerGrid's mesh files plus `groups.json`; produced by the driver, which owns gmsh and ElmerGrid.

### RUN\_BUNDLE

```python
RUN_BUNDLE: ArtifactKind[RunSpec] = ArtifactKind(name=BuiltinKind.RUN_BUNDLE, spec_type=RunSpec, producer_version=RUN_PRODUCER_VERSION, required_files=(BundleFile.MANIFEST, BundleFile.RESULT), key=lambda spec: cache_key(spec.config), in_place=True)
```

A finished run bundle, keyed by `cache_key` so its store key and its `run_id` agree.

### STEP

```python
STEP: ArtifactKind[StepSpec] = ArtifactKind(name=BuiltinKind.STEP, spec_type=StepSpec, producer_version=STEP_PRODUCER_VERSION, required_files=(BundleFile.GEOMETRY_STEP,), produce=_build_step)
```

The solve-frame STEP of a parametric geometry or an authored file, keyed by its source (`HashExclude` fields left out) and the frame.

### ManifestStatus

```python
ManifestStatus = Literal['running', 'abandoned'] | RunStatus
```

A manifest's lifecycle status: `"running"` while the pipeline executes, then the terminal `RunStatus`, or `"abandoned"` when its claim went silent first.

### RunStatus

```python
RunStatus = Literal['ok', 'diverged', 'error']
```

The terminal state of a solve: completed cleanly, failed to converge, or errored out.

### BundlePolicy

Bases: `StrEnum`

Which bundle files a published run bundle carries.

Attributes:

- **`CORE`** –

  Manifest, result, sidecars, sif, scalars, logs, geometry: everything but fields and meshes.
- **`FIELDS`** –

  Core plus the VTU output.
- **`FULL`** –

  Every bundle file, meshes and partitioning included.
- **`classes`** (`tuple[RunFile, ...]`) –

  The retention classes this policy publishes.

#### CORE

```python
CORE = 'core'
```

Manifest, result, sidecars, sif, scalars, logs, geometry: everything but fields and meshes.

#### FIELDS

```python
FIELDS = 'fields'
```

Core plus the VTU output.

#### FULL

```python
FULL = 'full'
```

Every bundle file, meshes and partitioning included.

#### classes

```python
classes: tuple[RunFile, ...]
```

The retention classes this policy publishes.

### MeshGroupsFile

Bases: `BaseModel`

`groups.json`: the group records and element count a mesh artifact carries beside ElmerGrid's files.

Config:

- `use_attribute_docstrings`: `True`

Fields:

- `bodies` (`list[BodyGroupRecord]`)
- `boundaries` (`list[BoundaryGroupRecord]`)
- `element_count` (`int`)

#### bodies

```python
bodies: list[BodyGroupRecord]
```

The body group records, in mesher order.

#### boundaries

```python
boundaries: list[BoundaryGroupRecord]
```

The boundary group records, in mesher order.

#### element\_count

```python
element_count: int = 0
```

Elements of the body dimension in the mesh.

#### capture

```python
capture(physical_groups: Iterable[_BodySource], boundary_groups: Iterable[_BoundarySource], element_count: int) -> MeshGroupsFile
```

Record the mesher's groups.

Parameters:

- **`physical_groups`** (`Iterable[_BodySource]`) –

  The body groups, in mesher order.
- **`boundary_groups`** (`Iterable[_BoundarySource]`) –

  The boundary groups, in mesher order.
- **`element_count`** (`int`) –

  Elements of the body dimension in the mesh.

#### load

```python
load(sim_dir: str | Path) -> MeshGroupsFile
```

Read `groups.json` from `sim_dir`.

#### write

```python
write(sim_dir: str | Path) -> Path
```

Write `groups.json` into `sim_dir` and return its path.

### MeshRebindError

Bases: `ArtifactError`

A cached mesh cannot be bound to the current config's domain.

### ConvergencePoint

Bases: `BaseModel`

One rung of the refinement ladder: the refinement level, the resulting quantity, and its relative change from the previous (coarser) rung.

Config:

- `arbitrary_types_allowed`: `True`
- `use_attribute_docstrings`: `True`

Fields:

- `refinement` (`float`)
- `quantity` (`Quantity`)
- `delta_vs_previous` (`float | None`)

#### refinement

```python
refinement: float
```

`r` `[-]`: Refinement factor applied to global\_mesh\_size (\>1 = finer than the base).

#### quantity

```python
quantity: Quantity
```

`q` `[?]`: The quantity of interest at this level (a pint quantity, e.g. force magnitude in N).

#### delta\_vs\_previous

```python
delta_vs_previous: float | None = None
```

`δq` `[-]`: Relative change |q − q\_prev| / |q\_prev| vs the previous rung; None for the first (no baseline).

### RichardsonEstimate

Bases: `BaseModel`

Richardson extrapolation of a ladder's finest three rungs, assuming q(h) = q\* + C·hᵖ.

Config:

- `use_attribute_docstrings`: `True`

Fields:

- `observed_order` (`float`)
- `extrapolated_value` (`float`)
- `uncertainty` (`float`)

#### observed\_order

```python
observed_order: float
```

`p` `[-]`: Observed convergence order in q(h) = q\* + C·hᵖ; `inf` when the finest two rungs are identical.

#### extrapolated\_value

```python
extrapolated_value: float
```

`q*` `[?]`: The h→0 value, in the ladder's own units.

#### uncertainty

```python
uncertainty: float
```

`|q* − q_finest|` `[?]`: The size of the extrapolation correction, the honest error scale of the finest rung.

### Driver

```python
Driver(*, runs_dir: str | Path = 'runs', code_sha: str | None = None, solver: SolverFn | None = None, mpi_processes: int = 1, backend: ExecutionBackend | None = None, mesher: MeshBackend | None = None, docker: bool | None = None, docker_image: str | None = None, retention: RetentionPolicy | None = None, artifacts: ArtifactCache | None = None, reuse: bool = True, bundle_policy: BundlePolicy = FIELDS, telemetry: str | Path | None = None)
```

Runs each config through the pipeline into a `runs/<run_id>/` bundle and returns its typed `Result`.

The config alone decides a run's identity: execution settings such as the backend, the mesher, the rank count, retention and telemetry never enter it.

Parameters:

- **`runs_dir`** (`str | Path`, default: `'runs'` ) –

  The directory each run's bundle is written under.
- **`code_sha`** (`str | None`, default: `None` ) –

  The code version each manifest records; None reads it from git.
- **`solver`** (`SolverFn | None`, default: `None` ) –

  A replacement for the staged pipeline, for tests that run without gmsh or Elmer.
- **`mpi_processes`** (`int`, default: `1` ) –

  The ElmerSolver rank count; more than 1 needs a partitioned backend.
- **`backend`** (`ExecutionBackend | None`, default: `None` ) –

  Where ElmerGrid and ElmerSolver run; None picks Docker or native Elmer from `docker`, `docker_image` and `mpi_processes`.
- **`mesher`** (`MeshBackend | None`, default: `None` ) –

  Where gmsh meshes each STEP; None meshes in this process, and a `WorkerMesher` meshes in a child process.
- **`docker`** (`bool | None`, default: `None` ) –

  True runs Elmer in Docker and False runs it natively; it cannot be combined with `backend`.
- **`docker_image`** (`str | None`, default: `None` ) –

  An image tag to run Elmer in instead of femforge's own image; it cannot be combined with `backend`.
- **`retention`** (`RetentionPolicy | None`, default: `None` ) –

  What leaves each bundle after its evaluation; `evaluate(retention=...)` overrides it for one call.
- **`artifacts`** (`ArtifactCache | None`, default: `None` ) –

  The cache the STEP, mesh and bundle stages read from and publish to; None keeps runs store-free.
- **`reuse`** (`bool`, default: `True` ) –

  Whether to return a finished bundle with the same identity instead of solving again.
- **`bundle_policy`** (`BundlePolicy`, default: `FIELDS` ) –

  Which bundle files a publish carries, and so which files a reused bundle must still hold.
- **`telemetry`** (`str | Path | None`, default: `None` ) –

  The telemetry root for each run's index row and log records; None falls back to `FEMFORGE_TELEMETRY`.

Methods:

- **`evaluate`** –

  Return the finished bundle for `config` when one is reusable, else mesh + sif + solve it under a claim on its
- **`run_index`** –

  The index this driver records into, or None when telemetry is off — which is also what keeps polars unimported.
- **`publish`** –

  The `Stage.PUBLISH` default: commit the files `bundle_policy` selects, store-sourced inputs excluded, to the artifact cache under the run identity.
- **`cleanup`** –

  The `Stage.CLEANUP` default: apply `policy` against the run's terminal status and record what left on the manifest.
- **`on_stage_event`** –

  Default stage-event sink: record the event on the active run's manifest and persist it (override + `super()` to add behavior).
- **`prepare_geometry`** –

  Materialize the solve-frame STEP into the run directory (from the artifact cache when one is configured) and repoint `state.config` at it.
- **`pre_mesh`** –

  Hook before meshing (e.g. adjust `state.config` or drop extra inputs into `state.sim_dir`).
- **`mesh`** –

  Mesh with `mesher` and convert with ElmerGrid, served from the artifact cache when one is configured, then bind the groups to this config.
- **`post_mesh`** –

  Hook between meshing and sim construction (e.g. amend the Elmer mesh files in `state.sim_dir`).
- **`build_sim`** –

  Populate `state.sim`: the typed `Sim` from the config's resolved solver stack, MPI-adjusted when partitioned.
- **`configure_sim`** –

  Hook to edit the typed solver stack (`state.sim.config`) before the sif is generated. Prefer authoring
- **`write_sif`** –

  Generate the sif object graph from the typed stack and write the sif into the run directory.
- **`run_solver`** –

  Partition the mesh when the solve is parallel (a partitioned backend is checked at construction), then run ElmerSolver on `backend`.
- **`mesh_stats`** –

  The built mesh's `MeshStats`: the meshed element count plus the config's size settings.

#### evaluate

```python
evaluate(config: SimulationConfig, *, retention: RetentionPolicy | None = None) -> Result
```

Return the finished bundle for `config` when one is reusable, else mesh + sif + solve it under a claim on its run directory, parse the result, write and publish the bundle, then apply the retention policy.

The run directory is `bundle_name(cache_key)` — a memorable name derived from the content hash — so re-evaluating identical inputs still targets the same bundle. `retention` overrides the driver's policy for this call.

#### run\_index

```python
run_index() -> RunIndex | None
```

The index this driver records into, or None when telemetry is off — which is also what keeps polars unimported.

#### publish

```python
publish(manifest: RunManifest, sim_dir: Path, config: SimulationConfig, result: Result) -> ArtifactRef
```

The `Stage.PUBLISH` default: commit the files `bundle_policy` selects, store-sourced inputs excluded, to the artifact cache under the run identity.

The store keeps the manifest as it stands on disk at publish time.

Parameters:

- **`manifest`** (`RunManifest`) –

  The run's manifest, which records the published reference.
- **`sim_dir`** (`Path`) –

  The run directory the files come from.
- **`config`** (`SimulationConfig`) –

  The config whose identity keys the published bundle.
- **`result`** (`Result`) –

  The parsed result, whose Elmer version the store records.

#### cleanup

```python
cleanup(manifest: RunManifest, sim_dir: Path, policy: RetentionPolicy) -> CleanupReport
```

The `Stage.CLEANUP` default: apply `policy` against the run's terminal status and record what left on the manifest.

Override it to push the bundle elsewhere first, then call `super()`.

Parameters:

- **`manifest`** (`RunManifest`) –

  The run's manifest, which records the cleanup.
- **`sim_dir`** (`Path`) –

  The run directory to clean.
- **`policy`** (`RetentionPolicy`) –

  The retention policy to apply.

#### on\_stage\_event

```python
on_stage_event(event: StageEvent) -> None
```

Default stage-event sink: record the event on the active run's manifest and persist it (override + `super()` to add behavior).

#### prepare\_geometry

```python
prepare_geometry(state: SolveState) -> None
```

Materialize the solve-frame STEP into the run directory (from the artifact cache when one is configured) and repoint `state.config` at it.

#### pre\_mesh

```python
pre_mesh(state: SolveState) -> None
```

Hook before meshing (e.g. adjust `state.config` or drop extra inputs into `state.sim_dir`).

#### mesh

```python
mesh(state: SolveState) -> None
```

Mesh with `mesher` and convert with ElmerGrid, served from the artifact cache when one is configured, then bind the groups to this config.

#### post\_mesh

```python
post_mesh(state: SolveState) -> None
```

Hook between meshing and sim construction (e.g. amend the Elmer mesh files in `state.sim_dir`).

#### build\_sim

```python
build_sim(state: SolveState) -> None
```

Populate `state.sim`: the typed `Sim` from the config's resolved solver stack, MPI-adjusted when partitioned.

#### configure\_sim

```python
configure_sim(state: SolveState) -> None
```

Hook to edit the typed solver stack (`state.sim.config`) before the sif is generated. Prefer authoring `config.physics.overrides` instead — edits made here happen after the run identity is computed.

#### write\_sif

```python
write_sif(state: SolveState) -> None
```

Generate the sif object graph from the typed stack and write the sif into the run directory.

#### run\_solver

```python
run_solver(state: SolveState) -> None
```

Partition the mesh when the solve is parallel (a partitioned backend is checked at construction), then run ElmerSolver on `backend`.

#### mesh\_stats

```python
mesh_stats(state: SolveState) -> MeshStats
```

The built mesh's `MeshStats`: the meshed element count plus the config's size settings.

### Bodies

```python
Bodies(bodies: Iterable[Body])
```

Name-keyed collection of a run's bodies.

Methods:

- **`get`** –

  The body by exact name, or None.
- **`with_material`** –

  Every body whose material tag matches (case-insensitive).
- **`with_tag`** –

  Every body whose selector tags carry `key=value`.

#### get

```python
get(name: str) -> Body | None
```

The body by exact name, or None.

#### with\_material

```python
with_material(tag: str) -> list[Body]
```

Every body whose material tag matches (case-insensitive).

#### with\_tag

```python
with_tag(key: TagKey | str, value: str) -> list[Body]
```

Every body whose selector tags carry `key=value`.

### Body

```python
Body(name: str, mesh_id: int, material: str | None = None, tags: Mapping[str, str] = dict[str, str]())
```

One named body group of a run.

Attributes:

- **`name`** (`str`) –

  The body group's name, shared by the mesh, the sif and `entities.json`.
- **`mesh_id`** (`int`) –

  The body's `GeometryIds` value in the VTU.
- **`material`** (`str | None`) –

  The material tag the body was grouped by; None when unknown.
- **`tags`** (`Mapping[str, str]`) –

  The selector tags the body was grouped by, merged into one mapping.

#### name

```python
name: str
```

The body group's name, shared by the mesh, the sif and `entities.json`.

#### mesh\_id

```python
mesh_id: int
```

The body's `GeometryIds` value in the VTU.

#### material

```python
material: str | None = None
```

The material tag the body was grouped by; None when unknown.

#### tags

```python
tags: Mapping[str, str] = field(default_factory=dict[str, str])
```

The selector tags the body was grouped by, merged into one mapping.

### Boundaries

```python
Boundaries(boundaries: Iterable[Boundary])
```

Name-keyed collection of a run's boundaries, with adjacency lookups powered by the `A__B` name convention.

Methods:

- **`get`** –

  The boundary by exact name, or None.
- **`between`** –

  The interface boundary between two bodies, whichever order the mesher named it.
- **`external`** –

  The body's external shell boundary (`<BODY>__EXT`).
- **`axis`** –

  The body's symmetry-axis boundary (`<BODY>__AXIS`), or None when it has none.
- **`of_body`** –

  Every boundary bordering the body.

#### get

```python
get(name: str) -> Boundary | None
```

The boundary by exact name, or None.

#### between

```python
between(a: Body | str, b: Body | str) -> Boundary
```

The interface boundary between two bodies, whichever order the mesher named it.

#### external

```python
external(body: Body | str) -> Boundary
```

The body's external shell boundary (`<BODY>__EXT`).

#### axis

```python
axis(body: Body | str) -> Boundary | None
```

The body's symmetry-axis boundary (`<BODY>__AXIS`), or None when it has none.

#### of\_body

```python
of_body(body: Body | str) -> list[Boundary]
```

Every boundary bordering the body.

### Boundary

```python
Boundary(name: str, mesh_id: int, bodies: tuple[str, ...], kind: BoundaryKind, sif_index: int | None = None)
```

One named boundary group of a run.

Attributes:

- **`name`** (`str`) –

  The boundary group's name, such as `A__B` or `A__EXT`.
- **`mesh_id`** (`int`) –

  The boundary's `GeometryIds` value in the VTU.
- **`bodies`** (`tuple[str, ...]`) –

  The names of the bodies the boundary borders.
- **`kind`** (`BoundaryKind`) –

  What the boundary separates.
- **`sif_index`** (`int | None`) –

  The boundary's Boundary Condition block number in the sif; None when no condition produced a block.

#### name

```python
name: str
```

The boundary group's name, such as `A__B` or `A__EXT`.

#### mesh\_id

```python
mesh_id: int
```

The boundary's `GeometryIds` value in the VTU.

#### bodies

```python
bodies: tuple[str, ...]
```

The names of the bodies the boundary borders.

#### kind

```python
kind: BoundaryKind
```

What the boundary separates.

#### sif\_index

```python
sif_index: int | None = None
```

The boundary's Boundary Condition block number in the sif; None when no condition produced a block.

### BoundaryKind

Bases: `StrEnum`

What a boundary group separates: two bodies, a body's external shell, or its trace on the 2D symmetry axis.

Attributes:

- **`INTERFACE`** –

  The interface between two bodies (`A__B`).
- **`EXTERNAL`** –

  A body's external shell (`A__EXT`).
- **`AXIS`** –

  A body's trace on the 2D symmetry axis (`A__AXIS`).

#### INTERFACE

```python
INTERFACE = 'Interface'
```

The interface between two bodies (`A__B`).

#### EXTERNAL

```python
EXTERNAL = 'External'
```

A body's external shell (`A__EXT`).

#### AXIS

```python
AXIS = 'Axis'
```

A body's trace on the 2D symmetry axis (`A__AXIS`).

### Entities

```python
Entities(bodies: Bodies, boundaries: Boundaries)
```

A bundle's named bodies and boundaries.

Attributes:

- **`bodies`** (`Bodies`) –

  The named bodies.
- **`boundaries`** (`Boundaries`) –

  The named boundaries.

#### bodies

```python
bodies: Bodies
```

The named bodies.

#### boundaries

```python
boundaries: Boundaries
```

The named boundaries.

### FieldMesh

```python
FieldMesh(*, arrays: Mapping[str, FloatArray], measure: FloatArray, geometry_ids: IntArray | None = None, centers: FloatArray | None = None, bodies: Bodies | None = None, grid: UnstructuredGrid | None = None)
```

A solved VTU as cell-space numpy arrays with named-body selection; open one via `load` or `Run.mesh()`.

Parameters:

- **`arrays`** (`Mapping[str, FloatArray]`) –

  Field array name -\> per-cell values.
- **`measure`** (`FloatArray`) –

  Per-cell measure: volume in 3D, area in 2D.
- **`geometry_ids`** (`IntArray | None`, default: `None` ) –

  Per-cell `GeometryIds` mesh ids; None when the VTU carries none.
- **`centers`** (`FloatArray | None`, default: `None` ) –

  Per-cell center coordinates; None disables position filters and revolved integrals.
- **`bodies`** (`Bodies | None`, default: `None` ) –

  The run's named bodies; None disables selection by body, material or tag.
- **`grid`** (`UnstructuredGrid | None`, default: `None` ) –

  The source pyvista grid; None disables extracting cells for plotting.

Methods:

- **`load`** –

  Read the bundle's newest VTU (via `result_vtu`), attaching body names from the bundle's sidecars when not given.
- **`from_grid`** –

  Extract every field array to cell space (nodal ones projected once), the cell measure, GeometryIds, and cell centers.
- **`get`** –

  Start a query over one field array; all cells are selected until filtered (boundary overlay cells included).
- **`extract_cells`** –

  The masked cells as a pyvista subgrid (for plotting); requires construction from a live grid.

Attributes:

- **`array_names`** (`list[str]`) –

  Every available field array name.

#### array\_names

```python
array_names: list[str]
```

Every available field array name.

#### load

```python
load(sim_dir: str | Path, bodies: Bodies | None = None) -> FieldMesh
```

Read the bundle's newest VTU (via `result_vtu`), attaching body names from the bundle's sidecars when not given.

#### from\_grid

```python
from_grid(grid: UnstructuredGrid, bodies: Bodies | None = None) -> FieldMesh
```

Extract every field array to cell space (nodal ones projected once), the cell measure, GeometryIds, and cell centers.

#### get

```python
get(variable: ElmerVariable | str) -> FieldQuery
```

Start a query over one field array; all cells are selected until filtered (boundary overlay cells included).

#### extract\_cells

```python
extract_cells(mask: BoolArray) -> UnstructuredGrid
```

The masked cells as a pyvista subgrid (for plotting); requires construction from a live grid.

### FieldQuery

```python
FieldQuery(mesh: FieldMesh, name: str, values: FloatArray, mask: BoolArray)
```

One field array under an immutable cell mask: filters return refined copies, terminals return numpy values.

Parameters:

- **`mesh`** (`FieldMesh`) –

  The field mesh the array belongs to.
- **`name`** (`str`) –

  The VTU array name.
- **`values`** (`FloatArray`) –

  The array's per-cell values over the full grid.
- **`mask`** (`BoolArray`) –

  One flag per cell of the full grid; True keeps the cell.

Methods:

- **`where_body`** –

  Keep the named bodies' own cells (boundary overlay cells excluded).
- **`where_material`** –

  Keep the cells of every body with this material tag (case-insensitive).
- **`where_tag`** –

  Keep the cells of every body tagged `key=value`.
- **`where_ids`** –

  Keep cells whose `GeometryIds` value is one of `ids` — raw mesh ids, so boundary cells are reachable too.
- **`where_mask`** –

  AND an arbitrary cell mask into the selection (one flag per cell of the full grid).
- **`where_pos`** –

  Keep cells whose center lies inside the inclusive per-axis `(min, max)` bounds.
- **`magnitude`** –

  A scalar query of per-cell |value|: the row norm of a vector field, the absolute value of a scalar one.
- **`sum`** –

  Plain sum of the selected values: a float for scalars, a per-component vector otherwise.
- **`max`** –

  Component-wise maximum over the selection; raises when nothing matched.
- **`min`** –

  Component-wise minimum over the selection; raises when nothing matched.
- **`mean`** –

  Measure-weighted mean over the selection (so cell size doesn't bias it; boundary overlay cells weigh 0).
- **`integrate`** –

  ∫ value · dV over the selection (in a 2D model: the plane-slice ∫ value · dA — see `integrate_revolved` for the axisymmetric volume integral).
- **`integrate_revolved`** –

  ∫ value · 2π·r dA over a 2D axisymmetric selection — the full-revolution volume integral, with r the radial (x) cell-center coordinate.
- **`total_measure`** –

  The selection's total volume (3D) or area (2D).
- **`cells`** –

  The selected cells as a pyvista subgrid, for plotting.

Attributes:

- **`values`** (`FloatArray`) –

  The selected cells' values, in cell order.
- **`measures`** (`FloatArray`) –

  The selected cells' measure (volume in 3D, area in 2D); boundary overlay cells measure 0.
- **`cell_indices`** (`IntArray`) –

  The selected cells' indices into the full grid.

#### values

```python
values: FloatArray
```

The selected cells' values, in cell order.

#### measures

```python
measures: FloatArray
```

The selected cells' measure (volume in 3D, area in 2D); boundary overlay cells measure 0.

#### cell\_indices

```python
cell_indices: IntArray
```

The selected cells' indices into the full grid.

#### where\_body

```python
where_body(*bodies: Body | str) -> FieldQuery
```

Keep the named bodies' own cells (boundary overlay cells excluded).

#### where\_material

```python
where_material(tag: str) -> FieldQuery
```

Keep the cells of every body with this material tag (case-insensitive).

#### where\_tag

```python
where_tag(key: TagKey | str, value: str) -> FieldQuery
```

Keep the cells of every body tagged `key=value`.

#### where\_ids

```python
where_ids(ids: Iterable[int]) -> FieldQuery
```

Keep cells whose `GeometryIds` value is one of `ids` — raw mesh ids, so boundary cells are reachable too.

#### where\_mask

```python
where_mask(mask: BoolArray) -> FieldQuery
```

AND an arbitrary cell mask into the selection (one flag per cell of the full grid).

#### where\_pos

```python
where_pos(x: Bounds | None = None, y: Bounds | None = None, z: Bounds | None = None) -> FieldQuery
```

Keep cells whose center lies inside the inclusive per-axis `(min, max)` bounds.

Parameters:

- **`x`** (`Bounds | None`, default: `None` ) –

  The `(min, max)` bounds on x; None leaves x unbounded, and a None bound leaves its side open.
- **`y`** (`Bounds | None`, default: `None` ) –

  The bounds on y, as for `x`.
- **`z`** (`Bounds | None`, default: `None` ) –

  The bounds on z, as for `x`.

#### magnitude

```python
magnitude() -> FieldQuery
```

A scalar query of per-cell |value|: the row norm of a vector field, the absolute value of a scalar one.

#### sum

```python
sum() -> float | FloatArray
```

Plain sum of the selected values: a float for scalars, a per-component vector otherwise.

#### max

```python
max() -> float | FloatArray
```

Component-wise maximum over the selection; raises when nothing matched.

#### min

```python
min() -> float | FloatArray
```

Component-wise minimum over the selection; raises when nothing matched.

#### mean

```python
mean() -> float | FloatArray
```

Measure-weighted mean over the selection (so cell size doesn't bias it; boundary overlay cells weigh 0).

#### integrate

```python
integrate() -> float | FloatArray
```

∫ value · dV over the selection (in a 2D model: the plane-slice ∫ value · dA — see `integrate_revolved` for the axisymmetric volume integral).

#### integrate\_revolved

```python
integrate_revolved() -> float | FloatArray
```

∫ value · 2π·r dA over a 2D axisymmetric selection — the full-revolution volume integral, with r the radial (x) cell-center coordinate.

#### total\_measure

```python
total_measure() -> float
```

The selection's total volume (3D) or area (2D).

#### cells

```python
cells() -> UnstructuredGrid
```

The selected cells as a pyvista subgrid, for plotting.

### Run

```python
Run(sim_dir: str | Path)
```

One run bundle (`runs/<run_id>/`) opened for reading.

Methods:

- **`from_result`** –

  Open the bundle a Result was loaded from or written to (the driver and `Result.load` stamp `run_dir`).
- **`latest`** –

  The most recently created bundle under a runs directory (by manifest `created_at`, then mtime).
- **`purged`** –

  The newest cleanup pass that removed `file_class` (or the whole run directory), else None.
- **`mesh`** –

  The solved VTU as a fluent field query, body names attached (lazy pyvista import; cached per Run); names the retention pass when the VTU was purged.

Attributes:

- **`meta`** (`RunMeta`) –

  The run's searchable metadata (description + tags), as recorded in the manifest's config.
- **`result`** (`Result`) –

  The typed result: `result.json` when written, else parsed fresh from the Elmer outputs in the bundle.
- **`manifest`** (`RunManifest`) –

  The run's provenance manifest.
- **`cleanups`** (`list[CleanupRecord]`) –

  Every retention pass recorded on the manifest (empty when the manifest is missing or unreadable).
- **`entities`** (`Entities`) –

  Named bodies and boundaries from the richest available source (entities.json, else enriched mesh.names).
- **`bodies`** (`Bodies`) –

  The run's named bodies (`mesh_id` == the VTU's `GeometryIds` values).
- **`boundaries`** (`Boundaries`) –

  The run's named boundaries, with `between`/`external`/`axis` lookups.
- **`components`** (`list[SifComponent]`) –

  Every sif Component with its lumped force/torque, joined from `components.yml` and the result.
- **`component_forces`** (`dict[str, VectorQuantity]`) –

  Lumped magnetic force (N) per Component name.
- **`component_torques`** (`dict[str, VectorQuantity]`) –

  Lumped magnetic torque (N·m) per Component name.
- **`electrode_currents`** (`dict[str, float]`) –

  Terminal current (A) per named electrode boundary, from a conduction solve's flux columns.
- **`phase_currents`** (`dict[str, float]`) –

  Terminal current (A) per phase index ("0", "1", ...), from the named electrode currents.
- **`vtu_path`** (`Path | None`) –

  The run's ParaView output (the partitioned master when one exists), or None when the solve wrote none.

#### meta

```python
meta: RunMeta
```

The run's searchable metadata (description + tags), as recorded in the manifest's config.

#### result

```python
result: Result
```

The typed result: `result.json` when written, else parsed fresh from the Elmer outputs in the bundle.

#### manifest

```python
manifest: RunManifest
```

The run's provenance manifest.

#### cleanups

```python
cleanups: list[CleanupRecord]
```

Every retention pass recorded on the manifest (empty when the manifest is missing or unreadable).

#### entities

```python
entities: Entities
```

Named bodies and boundaries from the richest available source (entities.json, else enriched mesh.names).

#### bodies

```python
bodies: Bodies
```

The run's named bodies (`mesh_id` == the VTU's `GeometryIds` values).

#### boundaries

```python
boundaries: Boundaries
```

The run's named boundaries, with `between`/`external`/`axis` lookups.

#### components

```python
components: list[SifComponent]
```

Every sif Component with its lumped force/torque, joined from `components.yml` and the result.

#### component\_forces

```python
component_forces: dict[str, VectorQuantity]
```

Lumped magnetic force (N) per Component name.

#### component\_torques

```python
component_torques: dict[str, VectorQuantity]
```

Lumped magnetic torque (N·m) per Component name.

#### electrode\_currents

```python
electrode_currents: dict[str, float]
```

Terminal current (A) per named electrode boundary, from a conduction solve's flux columns.

#### phase\_currents

```python
phase_currents: dict[str, float]
```

Terminal current (A) per phase index ("0", "1", ...), from the named electrode currents.

#### vtu\_path

```python
vtu_path: Path | None
```

The run's ParaView output (the partitioned master when one exists), or None when the solve wrote none.

#### from\_result

```python
from_result(result: Result) -> Run
```

Open the bundle a Result was loaded from or written to (the driver and `Result.load` stamp `run_dir`).

#### latest

```python
latest(runs_dir: str | Path) -> Run
```

The most recently created bundle under a runs directory (by manifest `created_at`, then mtime).

#### purged

```python
purged(file_class: RunFile) -> CleanupRecord | None
```

The newest cleanup pass that removed `file_class` (or the whole run directory), else None.

#### mesh

```python
mesh() -> FieldMesh
```

The solved VTU as a fluent field query, body names attached (lazy pyvista import; cached per Run); names the retention pass when the VTU was purged.

### SifComponent

```python
SifComponent(name: str, sif_index: int, force: VectorQuantity | None = None, torque: VectorQuantity | None = None)
```

One sif Component block with its lumped force and torque.

Attributes:

- **`name`** (`str`) –

  The Component name, as `components.yml` records it.
- **`sif_index`** (`int`) –

  The Component block number in the sif.
- **`force`** (`VectorQuantity | None`) –

  `F⃗` `[N]`: Lumped magnetic force on the Component; None when the solve exported none.
- **`torque`** (`VectorQuantity | None`) –

  `M⃗` `[N·m]`: Lumped magnetic torque on the Component; None when the solve exported none.

#### name

```python
name: str
```

The Component name, as `components.yml` records it.

#### sif\_index

```python
sif_index: int
```

The Component block number in the sif.

#### force

```python
force: VectorQuantity | None = None
```

`F⃗` `[N]`: Lumped magnetic force on the Component; None when the solve exported none.

#### torque

```python
torque: VectorQuantity | None = None
```

`M⃗` `[N·m]`: Lumped magnetic torque on the Component; None when the solve exported none.

### MeshSpec

Bases: `BaseModel`

Everything that decides the Elmer mesh files and their group names, and nothing that only decides the solve.

Config:

- `use_attribute_docstrings`: `True`

Fields:

- `step_key` (`str`)
- `model_dim` (`Literal[2, 3]`)
- `global_mesh_size` (`float`)
- `gmsh_options` (`GmshOptions`)
- `deterministic` (`bool`)
- `tags` (`list[TagSelectorSpec]`)
- `materials` (`list[str]`)

#### step\_key

```python
step_key: str
```

The geometry identity (`step_key`).

#### model\_dim

```python
model_dim: Literal[2, 3]
```

Body dimension of the STEP, from the frame: 2D cross-section or 3D solids.

#### global\_mesh\_size

```python
global_mesh_size: float
```

`h` `[m]`: Domain-wide target element size.

#### gmsh\_options

```python
gmsh_options: GmshOptions
```

The gmsh knobs (thread count and terminal output are `HashExclude` and drop out).

#### deterministic

```python
deterministic: bool
```

Whether meshing was pinned to one thread for reproducibility.

#### tags

```python
tags: list[TagSelectorSpec]
```

The domain's tags in domain order (an unnamed body's group name concatenates tag names in that order).

#### materials

```python
materials: list[str]
```

The sorted material tags; an unnamed body's group name starts with its material tag.

### RunSpec

Bases: `BaseModel`

The bundle kind's spec: the config a run solved.

Config:

- `arbitrary_types_allowed`: `True`
- `use_attribute_docstrings`: `True`

Fields:

- `config` (`SimulationConfig`)

#### config

```python
config: SimulationConfig
```

The full typed input.

### RunManifest

Bases: `BaseModel`

The self-describing record of one run.

Config:

- `arbitrary_types_allowed`: `True`
- `use_attribute_docstrings`: `True`

Fields:

- `run_id` (`str`)
- `cache_key` (`str`)
- `created_at` (`datetime`)
- `git_sha` (`str | None`)
- `elmer_version` (`str | None`)
- `config` (`SimulationConfig`)
- `physics` (`Physics`)
- `physics_config` (`PhysicsConfig`)
- `mesh_key` (`str`)
- `mesh_name` (`str`)
- `mesh_stats` (`MeshStats | None`)
- `status` (`ManifestStatus`)
- `stage_history` (`list[StageEvent]`)
- `artifacts` (`list[ArtifactRef]`)
- `published` (`ArtifactRef | None`)
- `cleanups` (`list[CleanupRecord]`)
- `notes` (`str`)

Validators:

- `_derive_mesh_name`

#### run\_id

```python
run_id: str
```

The bundle directory name: `<adjective>-<noun>-<hash8>` derived from `cache_key`.

#### cache\_key

```python
cache_key: str
```

The full content hash of the result-affecting inputs; `run_id` is its derived name.

#### created\_at

```python
created_at: datetime
```

When this run was assembled (UTC). Provenance only — never feeds run\_id.

#### git\_sha

```python
git_sha: str | None = None
```

Code version that produced the run (`-dirty` when uncommitted changes were present). Provenance only — never feeds run\_id.

#### elmer\_version

```python
elmer_version: str | None = None
```

ElmerSolver version parsed from the log, if available.

#### config

```python
config: SimulationConfig
```

The full typed input that drove both the mesh and the sif.

#### physics

```python
physics: Physics
```

Which physics preset was solved.

#### physics\_config

```python
physics_config: PhysicsConfig
```

The resolved solver stack the run solved with (preset or authored overrides).

#### mesh\_key

```python
mesh_key: str
```

The mesh artifact key (geometry, mesh settings, body grouping).

#### mesh\_name

```python
mesh_name: str = ''
```

The mesh artifact's store directory name; always derived from `mesh_key`, never read from input.

#### mesh\_stats

```python
mesh_stats: MeshStats | None = None
```

Mesh size/quality summary for consistency auditing.

#### status

```python
status: ManifestStatus
```

Lifecycle status: `"running"` while the pipeline executes, then the terminal solve status (mirrors Result.status), or `"abandoned"` when its claim went silent first.

#### stage\_history

```python
stage_history: list[StageEvent]
```

Per-stage start/finish timeline, appended as the pipeline runs.

#### artifacts

```python
artifacts: list[ArtifactRef]
```

Store-sourced inputs this run consumed (STEP, mesh): kind, key, origin, and the bundle files they materialized — enough to re-hydrate the directory and for retention to know what is re-fetchable.

#### published

```python
published: ArtifactRef | None = None
```

The bundle's own store entry (kind `run_bundle`, key == `cache_key`, the files its policy included); None when never published.

#### cleanups

```python
cleanups: list[CleanupRecord]
```

Every retention pass applied to this bundle (the driver's cleanup stage, then any post-hoc purge), oldest first.

#### notes

```python
notes: str = ''
```

Free-form provenance note. Cosmetic — never feeds run\_id.

#### current\_stage

```python
current_stage: Stage | None
```

The stage currently running — the first event without a finish time — or None when idle/finished; a crashed stage stays current through cleanup.

#### cached\_inputs

```python
cached_inputs() -> frozenset[str]
```

Bundle-relative paths the artifact store can re-fetch, so retention may drop them freely.

#### load

```python
load(path: str | Path) -> 'RunManifest'
```

Load a bundle's `manifest.json` (pass the bundle directory or the file itself).

#### build

```python
build(config: SimulationConfig, *, status: ManifestStatus, run_id: str | None = None, mesh_stats: MeshStats | None = None, code_sha: str | None = None, elmer_version: str | None = None, created_at: datetime | None = None, notes: str = '') -> 'RunManifest'
```

Assemble the manifest of `config`: its `cache_key` and `mesh_key`, and the physics and solver stack the config solves.

Parameters:

- **`config`** (`SimulationConfig`) –

  The run's full typed input.
- **`status`** (`ManifestStatus`) –

  The manifest's lifecycle status.
- **`run_id`** (`str | None`, default: `None` ) –

  The bundle name to record verbatim; None derives it from the cache key.
- **`mesh_stats`** (`MeshStats | None`, default: `None` ) –

  The mesh size summary, when known.
- **`code_sha`** (`str | None`, default: `None` ) –

  The producing commit, stored as the manifest's `git_sha`.
- **`elmer_version`** (`str | None`, default: `None` ) –

  The ElmerSolver version, when known.
- **`created_at`** (`datetime | None`, default: `None` ) –

  When the run was assembled; None takes the current UTC time.
- **`notes`** (`str`, default: `''` ) –

  A free-form provenance note.

### ReapedRun

```python
ReapedRun(run_id: str, sim_dir: Path, owner: LockOwner | None, status: ManifestStatus | None)
```

One run `reap_runs` settled.

Attributes:

- **`run_id`** (`str`) –

  The run's bundle name.
- **`sim_dir`** (`Path`) –

  The run's bundle directory.
- **`owner`** (`LockOwner | None`) –

  Who held the run's claim and when it last beat; None when nobody did.
- **`status`** (`ManifestStatus | None`) –

  The status the run's bundle carries now; None without a readable manifest.

#### run\_id

```python
run_id: str
```

The run's bundle name.

#### sim\_dir

```python
sim_dir: Path
```

The run's bundle directory.

#### owner

```python
owner: LockOwner | None
```

Who held the run's claim and when it last beat; None when nobody did.

#### status

```python
status: ManifestStatus | None
```

The status the run's bundle carries now; None without a readable manifest.

### MeshStats

Bases: `BaseModel`

Per-run mesh size/quality summary, recorded in the manifest so a sweep can be audited for mesh consistency.

Config:

- `use_attribute_docstrings`: `True`

Fields:

- `element_count` (`int`)
- `min_element_size` (`float`)
- `max_element_size` (`float`)

#### element\_count

```python
element_count: int
```

Total number of volume elements in the solved mesh.

#### min\_element\_size

```python
min_element_size: float
```

`h_min` `[m]`: Smallest element edge length.

#### max\_element\_size

```python
max_element_size: float
```

`h_max` `[m]`: Largest element edge length.

### Result

Bases: `BaseModel`

The typed outcome of one solve.

Quantities stay pint-typed; they are stripped to floats only at the analysis boundary (`Study.to_dataframe`).

Config:

- `arbitrary_types_allowed`: `True`
- `use_attribute_docstrings`: `True`
- `extra`: `forbid`

Fields:

- `status` (`RunStatus`)
- `run_id` (`str | None`)
- `run_dir` (`Path | None`)
- `force` (`Force | None`)
- `torque` (`Torque | None`)
- `flux_density_max` (`FluxDensity | None`)
- `temperature_max` (`Temperature | None`)
- `residual` (`float | None`)
- `wall_time` (`float | None`)
- `elmer_version` (`str | None`)
- `frame` (`Frame`)
- `component_forces` (`dict[str, Force]`)
- `component_torques` (`dict[str, Torque]`)
- `raw` (`dict[str, float]`)

#### status

```python
status: RunStatus
```

Terminal state of the solve.

#### run\_id

```python
run_id: str | None = None
```

The run's identity (== the manifest's `run_id` and the bundle directory name — the memorable name derived from the cache key); the driver stamps it after parsing, so a bare parse (no run lifecycle) leaves it None.

#### run\_dir

```python
run_dir: Path | None = None
```

The bundle directory this result lives in, stamped by the driver and `load_result`; never serialized, and None once retention removed the directory.

#### force

```python
force: Force | None = None
```

`F⃗` `[N]`: Net force on the moving array, as a vector pint quantity.

#### torque

```python
torque: Torque | None = None
```

`M⃗` `[N·m]`: Net torque on the moving array, as a vector pint quantity.

#### flux\_density\_max

```python
flux_density_max: FluxDensity | None = None
```

`|B|_max` `[T]`: Peak flux density magnitude in the domain.

#### temperature\_max

```python
temperature_max: Temperature | None = None
```

`T_max` `[K]`: Peak temperature in the domain, from the thermal or coupled joule-heating solve — the max-current search observable.

#### residual

```python
residual: float | None = None
```

`Δ_rel` `[-]`: Final linear/nonlinear relative change reported by the solver.

#### wall\_time

```python
wall_time: float | None = None
```

`t_wall` `[s]`: Solver wall-clock time.

#### elmer\_version

```python
elmer_version: str | None = None
```

ElmerSolver version parsed from the log, if present.

#### frame

```python
frame: Frame
```

The run's solve frame. Every vector stays in solve coordinates; under `Axisymmetric` the typed force/torque fields are full-revolution totals with the radial component zeroed (zero net by symmetry), while `raw` keeps Elmer's per-radian columns.

#### component\_forces

```python
component_forces: dict[str, Force]
```

`F⃗` `[N]`: Lumped magnetic force on each force-carrying sif Component, keyed by its `components.yml` name.

#### component\_torques

```python
component_torques: dict[str, Torque]
```

`M⃗` `[N·m]`: Lumped magnetic torque (about the authored origin/axis) on each sif Component, keyed by its `components.yml` name.

#### raw

```python
raw: dict[str, float]
```

`[?]`: Escape hatch for un-modelled scalars the parser found but Result doesn't type yet, in whatever unit Elmer printed them.

#### load

```python
load(path: str | Path) -> Result
```

Load a bundle's `result.json` (pass the bundle directory or the file itself), stamping `run_dir` with where it was read from.

#### axial\_force

```python
axial_force(body: str, axis: Axis | None = None) -> Quantity
```

`F_ax` `[N]`: Component `body`'s force along the authored `axis`; an axisymmetric run defaults to its symmetry axis.

### BundleEntry

```python
BundleEntry(relative: str, size: int, file_class: RunFile)
```

One file found in a bundle, with the retention class its name falls in.

Attributes:

- **`relative`** (`str`) –

  Bundle-relative posix path.
- **`size`** (`int`) –

  File size in bytes.
- **`file_class`** (`RunFile`) –

  The retention class the file's name falls in.

#### relative

```python
relative: str
```

Bundle-relative posix path.

#### size

```python
size: int
```

File size in bytes.

#### file\_class

```python
file_class: RunFile
```

The retention class the file's name falls in.

### Cleanup

Bases: `StrEnum`

When a file class leaves the bundle; definition order is escalation order.

Methods:

- **`applies`** –

  Whether this rule removes its files for a run that ended in `status` (`ON_SUCCESS` means `ok` only).
- **`escalate`** –

  The stricter of two rules.

Attributes:

- **`NEVER`** –

  Keep the files.
- **`ON_SUCCESS`** –

  Remove the files when the run ended `ok`.
- **`ALWAYS`** –

  Remove the files whatever the run's status.

#### NEVER

```python
NEVER = 'never'
```

Keep the files.

#### ON\_SUCCESS

```python
ON_SUCCESS = 'on_success'
```

Remove the files when the run ended `ok`.

#### ALWAYS

```python
ALWAYS = 'always'
```

Remove the files whatever the run's status.

#### applies

```python
applies(status: ManifestStatus) -> bool
```

Whether this rule removes its files for a run that ended in `status` (`ON_SUCCESS` means `ok` only).

#### escalate

```python
escalate(other: Cleanup) -> Cleanup
```

The stricter of two rules.

### CleanupRecord

Bases: `BaseModel`

One cleanup pass over a bundle: when, by whom, under which policy, and what left.

Config:

- `use_attribute_docstrings`: `True`

Fields:

- `applied_at` (`datetime`)
- `trigger` (`CleanupTrigger`)
- `status` (`ManifestStatus`)
- `policy` (`RetentionPolicy`)
- `removed` (`list[RemovedFile]`)

#### applied\_at

```python
applied_at: datetime
```

When the pass ran (UTC).

#### trigger

```python
trigger: CleanupTrigger
```

The driver's cleanup stage or a post-hoc purge.

#### status

```python
status: ManifestStatus
```

The run status the `ON_SUCCESS` rules were resolved against.

#### policy

```python
policy: RetentionPolicy
```

The policy that was applied.

#### removed

```python
removed: list[RemovedFile]
```

Every path that left, with its class and the rule that removed it.

#### bytes\_freed

```python
bytes_freed: int
```

Total bytes the pass removed.

#### covers

```python
covers(file_class: RunFile) -> bool
```

Whether this pass removed anything of `file_class` (a removed run directory covers every class).

### CleanupReport

```python
CleanupReport(plan: CleanupPlan, removed: list[RemovedFile] = list(), failed: list[tuple[str, str]] = list(), dry_run: bool = False)
```

The outcome of applying a plan: what left (or would have, in a dry run) and what could not be removed.

Attributes:

- **`plan`** (`CleanupPlan`) –

  The plan that was applied.
- **`removed`** (`list[RemovedFile]`) –

  Every path that left, or would have in a dry run.
- **`failed`** (`list[tuple[str, str]]`) –

  The `(path, error)` pairs of the paths that could not be removed.
- **`dry_run`** (`bool`) –

  Whether the pass only reported, removing nothing.
- **`bytes_freed`** (`int`) –

  Total bytes removed, or that a dry run would remove.

#### plan

```python
plan: CleanupPlan
```

The plan that was applied.

#### removed

```python
removed: list[RemovedFile] = field(default_factory=list)
```

Every path that left, or would have in a dry run.

#### failed

```python
failed: list[tuple[str, str]] = field(default_factory=list)
```

The `(path, error)` pairs of the paths that could not be removed.

#### dry\_run

```python
dry_run: bool = False
```

Whether the pass only reported, removing nothing.

#### bytes\_freed

```python
bytes_freed: int
```

Total bytes removed, or that a dry run would remove.

### Preset

Bases: `StrEnum`

Named retention policies, from keep-everything to removing the run directory.

Attributes:

- **`DEFAULT`** –

  Keep everything the run cannot re-fetch; drop store-sourced inputs.
- **`KEEP_ALL`** –

  Keep everything, store-sourced inputs included.
- **`LEAN`** –

  Drop the regenerable meshes after a successful run.
- **`MINIMAL`** –

  Lean plus the field output after a successful run.
- **`NUKE`** –

  Remove the run directory whatever happened.

#### DEFAULT

```python
DEFAULT = 'default'
```

Keep everything the run cannot re-fetch; drop store-sourced inputs.

#### KEEP\_ALL

```python
KEEP_ALL = 'keep_all'
```

Keep everything, store-sourced inputs included.

#### LEAN

```python
LEAN = 'lean'
```

Drop the regenerable meshes after a successful run.

#### MINIMAL

```python
MINIMAL = 'minimal'
```

Lean plus the field output after a successful run.

#### NUKE

```python
NUKE = 'nuke'
```

Remove the run directory whatever happened.

### RetentionPolicy

Bases: `ConfigArea`

One `Cleanup` rule per bundle file class; the default keeps everything the run cannot re-fetch.

Fields:

- `geometry` (`Cleanup`)
- `gmsh_mesh` (`Cleanup`)
- `elmer_mesh` (`Cleanup`)
- `partitioning` (`Cleanup`)
- `sif` (`Cleanup`)
- `fields` (`Cleanup`)
- `scalars` (`Cleanup`)
- `logs` (`Cleanup`)
- `sidecars` (`Cleanup`)
- `run_dir` (`Cleanup`)
- `cached_inputs` (`Cleanup`)

#### geometry

```python
geometry: Cleanup = Cleanup.NEVER
```

The `geometry.step` copy pinned into the bundle.

#### gmsh\_mesh

```python
gmsh_mesh: Cleanup = Cleanup.NEVER
```

The gmsh `.msh` (only in the bundle for a parametric geometry).

#### elmer\_mesh

```python
elmer_mesh: Cleanup = Cleanup.NEVER
```

ElmerGrid's `mesh.header/nodes/elements/boundary`.

#### partitioning

```python
partitioning: Cleanup = Cleanup.NEVER
```

The `partitioning.<n>/` directory and its log from a parallel solve.

#### sif

```python
sif: Cleanup = Cleanup.NEVER
```

`case.sif` and `ELMERSOLVER_STARTINFO`.

#### fields

```python
fields: Cleanup = Cleanup.NEVER
```

The VTU/PVTU field output, master and per-rank pieces together.

#### scalars

```python
scalars: Cleanup = Cleanup.NEVER
```

SaveScalars `.dat` exports and their `.names` sidecars.

#### logs

```python
logs: Cleanup = Cleanup.NEVER
```

ElmerGrid/ElmerSolver logs, `convergence.jsonl`, `run.log`.

#### sidecars

```python
sidecars: Cleanup = Cleanup.NEVER
```

`entities.json`, `boundaries.yml`, `components.yml`, `mesh.names`, `entities.sif`.

#### run\_dir

```python
run_dir: Cleanup = Cleanup.NEVER
```

The whole bundle, manifest included; when it applies nothing else matters.

#### cached\_inputs

```python
cached_inputs: Cleanup = Cleanup.ALWAYS
```

Escalation for files the artifact store can re-fetch (`manifest.artifacts`); the stricter of this and the class rule wins.

#### rules

```python
rules() -> list[tuple[RunFile, Cleanup]]
```

The `(file class, rule)` pairs, read off the `Map()` metadata.

#### default

```python
default() -> RetentionPolicy
```

Keep everything the run cannot re-fetch; drop store-sourced inputs.

#### keep\_all

```python
keep_all() -> RetentionPolicy
```

Nothing leaves, store-sourced inputs included.

#### lean

```python
lean() -> RetentionPolicy
```

Drop the regenerable meshes after a successful run.

#### minimal

```python
minimal() -> RetentionPolicy
```

Lean plus the field output: keep only what re-reading the numbers needs.

#### nuke

```python
nuke() -> RetentionPolicy
```

Remove the run directory whatever happened (throwaway evaluations, bundles already pushed elsewhere).

#### from\_preset

```python
from_preset(preset: Preset) -> RetentionPolicy
```

The policy a preset names.

### Stage

Bases: `StrEnum`

The driver's pipeline stages, in execution order.

Attributes:

- **`PREPARE_GEOMETRY`** –

  Build or fetch the solve-frame STEP.
- **`PRE_MESH`** –

  A hook before meshing.
- **`MESH`** –

  Mesh the STEP and convert it with ElmerGrid, or fetch the cached mesh.
- **`POST_MESH`** –

  A hook between meshing and building the sim.
- **`BUILD_SIM`** –

  Build the typed sim from the resolved solver stack.
- **`CONFIGURE_SIM`** –

  A hook to edit the solver stack before the sif is written.
- **`WRITE_SIF`** –

  Write the sif into the run directory.
- **`RUN_SOLVER`** –

  Run ElmerSolver, partitioning the mesh first for a parallel solve.
- **`MESH_STATS`** –

  Record the mesh size summary.
- **`PUBLISH`** –

  Commit the bundle to the artifact cache.
- **`CLEANUP`** –

  Apply the retention policy.

#### PREPARE\_GEOMETRY

```python
PREPARE_GEOMETRY = 'prepare_geometry'
```

Build or fetch the solve-frame STEP.

#### PRE\_MESH

```python
PRE_MESH = 'pre_mesh'
```

A hook before meshing.

#### MESH

```python
MESH = 'mesh'
```

Mesh the STEP and convert it with ElmerGrid, or fetch the cached mesh.

#### POST\_MESH

```python
POST_MESH = 'post_mesh'
```

A hook between meshing and building the sim.

#### BUILD\_SIM

```python
BUILD_SIM = 'build_sim'
```

Build the typed sim from the resolved solver stack.

#### CONFIGURE\_SIM

```python
CONFIGURE_SIM = 'configure_sim'
```

A hook to edit the solver stack before the sif is written.

#### WRITE\_SIF

```python
WRITE_SIF = 'write_sif'
```

Write the sif into the run directory.

#### RUN\_SOLVER

```python
RUN_SOLVER = 'run_solver'
```

Run ElmerSolver, partitioning the mesh first for a parallel solve.

#### MESH\_STATS

```python
MESH_STATS = 'mesh_stats'
```

Record the mesh size summary.

#### PUBLISH

```python
PUBLISH = 'publish'
```

Commit the bundle to the artifact cache.

#### CLEANUP

```python
CLEANUP = 'cleanup'
```

Apply the retention policy.

### StageEvent

Bases: `BaseModel`

One stage's timing record: created at stage start, mutated with `finished_at` when the stage completes.

Config:

- `use_attribute_docstrings`: `True`

Fields:

- `stage` (`Stage`)
- `started_at` (`datetime`)
- `finished_at` (`datetime | None`)

#### stage

```python
stage: Stage
```

Which pipeline stage this event times.

#### started\_at

```python
started_at: datetime
```

When the stage started (UTC).

#### finished\_at

```python
finished_at: datetime | None = None
```

When the stage finished (UTC); None while it runs — or forever, if it crashed.

### bundle\_files

```python
bundle_files(sim_dir: str | Path, policy: BundlePolicy, *, exclude: Iterable[str] = ()) -> list[str]
```

The bundle-relative posix paths `policy` publishes from `sim_dir`.

Parameters:

- **`sim_dir`** (`str | Path`) –

  The run bundle directory.
- **`policy`** (`BundlePolicy`) –

  Which bundle files to publish.
- **`exclude`** (`Iterable[str]`, default: `()` ) –

  Bundle-relative paths to leave out, such as the store-sourced inputs.

### rebind\_groups

```python
rebind_groups(groups: MeshGroupsFile, domain: DomainConfig) -> tuple[list[PhysicalGroup], list[BoundaryGroup]]
```

Rebuild a cached mesh's groups against this config's domain, matching materials by tag and `EntityTag` objects by selector; ids stay placeholders until `reconcile_ids`.

### rehydrate\_bundle

```python
rehydrate_bundle(sim_dir: str | Path, cache: ArtifactCache) -> list[ArtifactHandle[StepSpec] | ArtifactHandle[MeshSpec]]
```

Fetch every store-sourced input the manifest names back into the bundle.

### load\_manifest

```python
load_manifest(sim_dir: str | Path) -> RunManifest
```

Read a bundle's `manifest.json` back into a typed `RunManifest` — a delegate to `RunManifest.load`.

### load\_result

```python
load_result(sim_dir: str | Path) -> Result
```

Read a bundle's `result.json` back into a typed `Result` — a delegate to `Result.load`.

### write\_atomic

```python
write_atomic(path: Path, text: str) -> None
```

Replace `path` atomically: write a `.tmp` sibling, then `os.replace` — a reader never sees a half-written file.

### write\_bundle

```python
write_bundle(sim_dir: str | Path, manifest: RunManifest, result: Result) -> Path
```

Write `manifest.json` and `result.json` into `sim_dir`, pin the source STEP as `geometry.step` unless the run already holds one, and return the bundle.

Parameters:

- **`sim_dir`** (`str | Path`) –

  The run bundle directory, created when missing.
- **`manifest`** (`RunManifest`) –

  The run's manifest.
- **`result`** (`Result`) –

  The run's typed result.

### write\_manifest

```python
write_manifest(sim_dir: str | Path, manifest: RunManifest) -> Path
```

Atomically write (or overwrite) `manifest.json` in `sim_dir` (created if needed) and return its path.

### convergence\_ladder

```python
convergence_ladder(base_config: SimulationConfig, refinements: list[float], quantity_of: QuantityOf, evaluate: EvaluateFn) -> list[ConvergencePoint]
```

Run `base_config` at each refinement level and report the quantity of interest plus its relative change between rungs.

Parameters:

- **`base_config`** (`SimulationConfig`) –

  The configuration to refine.
- **`refinements`** (`list[float]`) –

  Factors that divide `global_mesh_size`; a factor above 1 is finer than the base.
- **`quantity_of`** (`QuantityOf`) –

  Extracts the quantity of interest from a result.
- **`evaluate`** (`EvaluateFn`) –

  Solves one configuration, such as a driver's `evaluate`.

### richardson

```python
richardson(scales: Sequence[float], values: Sequence[float]) -> RichardsonEstimate
```

Richardson-extrapolate the finest three rungs of a ladder whose `scales` are mesh-size multipliers (larger is coarser) paired with `values`.

Raises when fewer than three rungs are given, scales repeat, or the rung deltas are not shrinking.

### boundary\_fluxes

```python
boundary_fluxes(raw: Mapping[str, float]) -> dict[int, float]
```

Elmer bc index -\> integrated conductive flux (A) from the SaveScalars columns in `Result.raw`.

### electrode\_currents

```python
electrode_currents(raw: Mapping[str, float], names_by_bc: Mapping[int, str]) -> dict[str, float]
```

Boundary block name -\> terminal current (A), joining the flux columns against the bc-index name map.

### phase\_currents

```python
phase_currents(currents: Mapping[str, float]) -> dict[str, float]
```

Phase index ("0", "1", ...) -\> terminal current (A): the largest-magnitude current among each phase's named electrode currents.

### read\_boundary\_names

```python
read_boundary_names(path: str | Path) -> dict[int, str]
```

The `boundaries.yml` map the Sim writes (`<bc index>: <boundary block name>`), as a dict.

### resistance

```python
resistance(delta_volts: float, current_amps: float) -> float
```

R = ΔV / I for one phase; raises on a (numerically) zero current.

### load\_entities

```python
load_entities(sim_dir: str | Path, manifest: RunManifest | None = None) -> Entities
```

Read a bundle's bodies/boundaries: `entities.json` when present, else `mesh.names` enriched from the manifest config, else empty.

### write\_entities

```python
write_entities(sim_dir: str | Path, physical_groups: Iterable[_BodyGroupLike], boundary_groups: Iterable[_BoundaryGroupLike]) -> Path
```

Write `entities.json` mapping each group's reconciled mesh id to its named record; call after `reconcile_ids`.

Parameters:

- **`sim_dir`** (`str | Path`) –

  The run bundle directory.
- **`physical_groups`** (`Iterable[_BodyGroupLike]`) –

  The body groups, carrying ElmerGrid's ids.
- **`boundary_groups`** (`Iterable[_BoundaryGroupLike]`) –

  The boundary groups, carrying ElmerGrid's ids.

### select\_array

```python
select_array(names: Iterable[str], variable: ElmerVariable | str) -> str
```

Resolve a VTU array name for a solver variable: exact (nodal), then the elemental `"<name> e"`, then substring — VTU names come back lowercased.

### find\_runs

```python
find_runs(runs_dir: str | Path, *, tags: Mapping[str, str] | None = None, text: str | None = None) -> list[Run]
```

The bundles under `runs_dir` whose metadata matches, newest first; this reads every manifest, so prefer `RunIndex` over thousands of bundles.

Parameters:

- **`runs_dir`** (`str | Path`) –

  The runs directory to search; bundles with unreadable manifests are skipped.
- **`tags`** (`Mapping[str, str] | None`, default: `None` ) –

  Tag pairs a run's metadata must all carry; None matches any tags.
- **`text`** (`str | None`, default: `None` ) –

  Text to find, ignoring case, in the run name, description or notes; None matches any text.

### mesh\_spec

```python
mesh_spec(config: SimulationConfig, step_key_override: str | None = None) -> MeshSpec
```

The mesh artifact spec of `config` (`step_key_override` when the driver already resolved the geometry).

### step\_digest

```python
step_digest(path: str | Path) -> str
```

SHA-256 of a STEP with its `FILE_NAME` header (the export timestamp) masked; of the path string when the file is absent, so pure-logic keys stay deterministic.

### component\_forces

```python
component_forces(raw: Mapping[str, float], names_by_component: Mapping[int, str]) -> dict[str, Vec3]
```

Component name -\> lumped magnetic force (N, per radian in an axisymmetric run), joining the force triples against the `components.yml` map.

### component\_torques

```python
component_torques(raw: Mapping[str, float], names_by_component: Mapping[int, str]) -> dict[str, Vec3]
```

Component name -\> lumped magnetic torque (N\*m about the authored origin/axis, per radian in an axisymmetric run), joining the torque triples against the `components.yml` map.

### eddy\_current\_power

```python
eddy_current_power(raw: Mapping[str, float]) -> float | None
```

The total eddy current power (W, per radian in an axisymmetric run), or None when the run exported none (static solves have no eddy losses).

### force\_triples

```python
force_triples(raw: Mapping[str, float]) -> dict[str, Vec3]
```

Column stem -\> per-component force Vec3 (N, per radian in an axisymmetric run) from the SaveScalars columns in `Result.raw`.

### magnetic\_field\_energy

```python
magnetic_field_energy(raw: Mapping[str, float]) -> float | None
```

The total magnetic field energy (J, per radian in an axisymmetric run), or None when the run exported none.

### read\_component\_names

```python
read_component_names(path: str | Path) -> dict[int, str]
```

The `components.yml` map the Sim writes (`<component id>: <Component name>`) — the same flat format as `boundaries.yml`.

### cache\_key

```python
cache_key(config: SimulationConfig) -> str
```

Full-run identity: every result-affecting input plus `RUN_PRODUCER_VERSION`; never the code commit, the machine, or the run metadata.

### git\_sha

```python
git_sha(*, short: bool = True) -> str | None
```

The current commit, with `-dirty` appended when the working tree has uncommitted changes; None without git or outside a repository.

### mesh\_key

```python
mesh_key(config: SimulationConfig) -> str
```

The mesh artifact key of `config`: invariant to materials' properties, conditions, physics, and metadata.

### step\_key

```python
step_key(config: SimulationConfig) -> str
```

The solve-frame geometry identity a run and its mesh key on: the STEP artifact key of `step_spec(config)`.

### parse\_result

```python
parse_result(sim_dir: str | Path, frame: Frame | None = None) -> Result
```

Parse a finished run directory into a typed `Result`, revolving force and torque to full-revolution totals under an `Axisymmetric` frame.

`frame=None` reads the frame from the bundle's manifest, falling back to `Solid` for a directory without one.

### result\_vtu

```python
result_vtu(sim_dir: str | Path) -> Path | None
```

The run's newest ParaView output: the `.pvtu` master when the solve was partitioned, else the newest `.vtu`; None when there is none.

Use it instead of a `*.vtu` glob, which also matches a partitioned run's per-rank pieces.

### reap\_runs

```python
reap_runs(runs_dir: str | Path, *, alive: Alive = pid_exists, host: str = HOST) -> list[ReapedRun]
```

Mark every bundle under `runs_dir` left `running` with no live claim on it as `abandoned`, and break every claim that went silent.

Parameters:

- **`runs_dir`** (`str | Path`) –

  The runs directory holding the bundles and their claims.
- **`alive`** (`Alive`, default: `pid_exists` ) –

  Whether a process id is alive on this host.
- **`host`** (`str`, default: `HOST` ) –

  This host's name; a claim held here by a process that is no longer alive counts as silent.

### apply\_cleanup

```python
apply_cleanup(plan: CleanupPlan, *, dry_run: bool = False) -> CleanupReport
```

Remove every planned path; never raises, so a path that cannot be removed lands in `report.failed`.

A planned run directory is only reported (as `"."`); the caller removes it with `remove_run_dir` after its last manifest write.

### bundle\_inventory

```python
bundle_inventory(sim_dir: str | Path) -> list[BundleEntry]
```

Every file under a bundle with the retention class its name matches; a name no class claims is filed under `RunFile.RUN_DIR`, the only rule that reaches it.

### plan\_cleanup

```python
plan_cleanup(sim_dir: str | Path, policy: RetentionPolicy, *, status: ManifestStatus, cached: Iterable[str] = ()) -> CleanupPlan
```

Resolve `policy` against one bundle into the sized paths each applying rule removes; refuses a symlinked or junctioned directory.

Parameters:

- **`sim_dir`** (`str | Path`) –

  The run bundle directory.
- **`policy`** (`RetentionPolicy`) –

  The retention policy to resolve.
- **`status`** (`ManifestStatus`) –

  The run status the `ON_SUCCESS` rules resolve against.
- **`cached`** (`Iterable[str]`, default: `()` ) –

  Bundle-relative paths the store can re-fetch; `policy.cached_inputs` escalates their rules.
