# Elmer solvers

Typed Elmer configuration: Pydantic models whose fields are their sif keywords, grouped by physics.

## femforge.elmer.config

Typed Elmer configuration: Pydantic models whose fields ARE their sif keywords, grouped by physics (`common`, `electromagnetism`, `thermal`, `mechanics`, `output`), plus the per-physics presets.

Modules:

- **`common`** –

  What every physics shares: the keyword-model base, the solver hierarchy's base and mixins, the Simulation/Constants blocks, the stack container, presets, and selection.
- **`electromagnetism`** –

  Electromagnetic solvers: magnetostatics (3D and axisymmetric), electrostatics, current conduction, and the coil current source.
- **`mechanics`** –

  Mechanical solvers: linear elasticity.
- **`output`** –

  Output solvers: VTU result files and scalar reductions.
- **`thermal`** –

  Thermal solvers: steady heat conduction.

Classes:

- **`PhysicsSelection`** –

  Which physics the Elmer stage solves; `SimulationConfig` derives the solved stack from this selection and its frame.
- **`PhysicsConfig`** –

  The fully populated, typed solver stack for one physics: ordered solvers plus simulation settings, activated constants and operating point.
- **`OperatingPoint`** –

  The point at which a material's property functions are evaluated when stripping to Elmer's bare SI floats.
- **`SimulationSettings`** –

  The Elmer `Simulation` block. Defaults are a 3D Cartesian steady-state run, used for magnetostatics and steady thermal.
- **`Constants`** –

  The Elmer `Constants` block: each constant is an optional field carrying its sif keyword; a physics sets only the constants it needs, and an unset one is omitted.
- **`SifKeywordModel`** –

  Base for a config model whose fields ARE its sif keywords: each field carries its Elmer keyword as `Map(...)`
- **`SolverBase`** –

  Base for a typed Elmer solver: its fields are its sif keywords.
- **`LinearSystem`** –

  Mixin: the `Linear System *` settings for a solver that does a linear solve. Defaults are sensible iterative values; concrete solvers tune the iteration cap and tolerances they need.
- **`NonlinearSystem`** –

  Mixin: the `Nonlinear System *` (outer-iteration) convergence settings.
- **`SteadyState`** –

  Mixin: the `Steady State Convergence Tolerance` for a steady-state run.
- **`MagnetoDynamicsSolver`** –

  3D magnetostatics (WhitneyAVSolver), with each magnet's drive entering as `Magnetization` on its material rather than as a body force.
- **`MagnetoDynamics2DSolver`** –

  Axisymmetric magnetostatics (MagnetoDynamics2D): the scalar azimuthal potential A\_phi under `Coordinate System = Axi Symmetric`.
- **`MagnetoDynamicsCalcFieldsSolver`** –

  Post-processing: recover B, H, fluxes and nodal forces from the computed A. Inherits `Linear System *` because
- **`ResultOutputSolver`** –

  Write VTU output for ParaView.
- **`HeatSolver`** –

  SCAFFOLD: steady heat conduction. Wire-ready; its boundary conditions land on the adjacency-derived surface groups via the condition-driven boundary loop.
- **`StatCurrentSolver`** –

  Static current conduction (Elmer's StatCurrentSolve): solves `Potential` from ∇·(σ∇φ) = 0, excited by per-phase
- **`CoilCurrentSolver`** –

  Elmer's `CoilSolver`: the divergence-conforming coil current distribution, the source stage of the `COIL_MAGNETOSTATICS` chain.
- **`SaveScalarsSolver`** –

  SaveScalars (SaveData): export scalar reductions to `scalars.dat` and its `.names` sidecar.
- **`ElectrostaticsSolver`** –

  SCAFFOLD: electrostatics (StatElecSolver). No nonlinear iteration.
- **`StressSolver`** –

  SCAFFOLD: linear elasticity (Elmer's StressSolve). Consumes `Youngs Modulus` and `Poisson Ratio` material
- **`GenericSolver`** –

  Escape hatch: an arbitrary Elmer solver block for anything the typed hierarchy doesn't model yet — set
- **`CoordinateSystem`** –

  Elmer's `Coordinate System`.
- **`SimulationType`** –

  Elmer's `Simulation Type`.
- **`TimesteppingMethod`** –

  Elmer's `Timestepping Method` (paired with `BDF Order` for BDF).
- **`ExecSolver`** –

  When Elmer executes a solver (the `Exec Solver` keyword).
- **`LinearSolverType`** –

  The `Linear System Solver` strategy: an iterative Krylov method or a direct factorization.
- **`IterativeMethod`** –

  The Krylov method for an iterative linear solve (`Linear System Iterative Method`).
- **`Preconditioning`** –

  The `Linear System Preconditioning` for an iterative solve.

Functions:

- **`physics_config`** –

  The fully-populated, typed solver stack for `physics`, keyed by an exhaustive `match` on the `Physics` enum (a new physics is a static obligation).
- **`solved_stack`** –

  The preset stack `physics` solves as under `frame`; an axisymmetric form runs in the Axi Symmetric coordinate system.
- **`prepare_for_mpi`** –

  Adjust a solver stack for a partitioned (MPI) solve: CoilSolver's serial-only umfpack Direct solve switches to MUMPS, and any other Direct solve raises.
- **`needs_halobc`** –

  Whether partitioning must add halo elements (`-halobc`): only the coil stack's boundary-terminated electrodes need them, and elsewhere they skew CalcFields' force lumping.
- **`convergence_targets`** –

  The per-(solver, loop) iteration caps and tolerances the stack authors, keyed the way Elmer tags ComputeChange lines.
- **`bare_save_scalars`** –

  A SaveScalars with no configured reductions: exports every registered `res:` simulation/component scalar (Component force/torque, field energy) to `scalars.dat`.

Attributes:

- **`STEFAN_BOLTZMANN`** (`float`) –

  `σ_SB` `[W/(m²·K⁴)]`: The Stefan-Boltzmann constant thermal radiation needs.
- **`SifValue`** –

  An Elmer sif keyword value, copied verbatim into the sif.

### STEFAN\_BOLTZMANN

```python
STEFAN_BOLTZMANN: float = 5.6704e-08
```

`σ_SB` `[W/(m²·K⁴)]`: The Stefan-Boltzmann constant thermal radiation needs.

### SifValue

```python
SifValue = str | int | float | bool
```

An Elmer sif keyword value, copied verbatim into the sif.

### PhysicsSelection

Bases: `ConfigArea`

Which physics the Elmer stage solves; `SimulationConfig` derives the solved stack from this selection and its frame.

Fields:

- `selected` (`Physics`)
- `overrides` (`PhysicsConfig | None`)

Validators:

- `selectable` → `selected`

#### selected

```python
selected: Physics = Physics.MAGNETOSTATICS
```

The physics as authored (magnetostatics / thermal / ...); the config's frame decides the form it solves as.

#### overrides

```python
overrides: PhysicsConfig | None = None
```

The authored solver stack for this run (see `SimulationConfig.customize_physics`); `None` solves the preset. Part of the config, so authored settings feed the run identity (`cache_key`) and the manifest.

#### selectable

```python
selectable(selected: Physics) -> Physics
```

Reject the internal axisymmetric stack.

#### check\_frame

```python
check_frame(frame: Frame) -> None
```

Raise when `selected` has no form under `frame` or authored `overrides` disagree with it.

### PhysicsConfig

Bases: `BaseModel`

The fully populated, typed solver stack for one physics: ordered solvers plus simulation settings, activated constants and operating point.

Config:

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

Fields:

- `solvers` (`list[SolverUnion]`)
- `simulation` (`SimulationSettings`)
- `constants` (`Constants`)
- `operating_point` (`OperatingPoint`)

#### solvers

```python
solvers: list[SolverUnion]
```

The ordered concrete solver instances Elmer runs for this physics.

#### simulation

```python
simulation: SimulationSettings
```

The Elmer Simulation block (run type, coordinate system, output files).

#### constants

```python
constants: Constants
```

The physical constants this physics activates.

#### operating\_point

```python
operating_point: OperatingPoint
```

Where material property functions are evaluated when stripping to SI.

#### solver

```python
solver(kind: type[SolverT]) -> SolverT
```

The live solver instance of type `kind` in this stack; raises `KeyError` when it has none.

#### check\_frame

```python
check_frame(frame: Frame) -> None
```

Raise when this stack's coordinate system disagrees with `frame`: per-radian exactly when the frame is axisymmetric.

### OperatingPoint

Bases: `BaseModel`

The point at which a material's property functions are evaluated when stripping to Elmer's bare SI floats. `Static` properties ignore it; temperature-dependent properties consume it via `as_mapping`.

Config:

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

Fields:

- `temperature` (`Temperature`)

#### temperature

```python
temperature: Temperature
```

`T` `[K]`: Evaluation temperature for material property functions.

#### as\_mapping

```python
as_mapping() -> dict[str, Quantity]
```

The ephemeral name-\>quantity mapping the property functions consume (`prop(**at)`).

### SimulationSettings

Bases: `SifKeywordModel`

The Elmer `Simulation` block. Defaults are a 3D Cartesian steady-state run, used for magnetostatics and steady thermal.

Fields:

- `extra_keywords` (`dict[str, SifValue]`)
- `max_output_level` (`int`)
- `coordinate_system` (`CoordinateSystem`)
- `coordinate_mapping` (`str`)
- `simulation_type` (`SimulationType`)
- `steady_state_max_iterations` (`int`)
- `output_intervals` (`int`)
- `timestepping_method` (`TimesteppingMethod`)
- `bdf_order` (`int`)
- `solver_input_file` (`str`)
- `post_file` (`str | None`)
- `output_file` (`str | None`)

#### max\_output\_level

```python
max_output_level: int = 5
```

Verbosity of ElmerSolver's console output (0-quiet … higher-noisier).

#### coordinate\_system

```python
coordinate_system: CoordinateSystem = CoordinateSystem.CARTESIAN
```

The simulation coordinate system.

#### coordinate\_mapping

```python
coordinate_mapping: str = '1 2 3'
```

Axis permutation for the coordinate system.

#### simulation\_type

```python
simulation_type: SimulationType = SimulationType.STEADY_STATE
```

Steady-state, transient, or scanning run.

#### steady\_state\_max\_iterations

```python
steady_state_max_iterations: int = 1
```

Max steady-state (coupled-system) iterations.

#### output\_intervals

```python
output_intervals: int = 1
```

Write results every N steps.

#### timestepping\_method

```python
timestepping_method: TimesteppingMethod = TimesteppingMethod.BDF
```

Time integration scheme.

#### bdf\_order

```python
bdf_order: int = 1
```

Order of the BDF timestepping method.

#### solver\_input\_file

```python
solver_input_file: str = 'case.sif'
```

Name of the generated .sif.

#### post\_file

```python
post_file: str | None = None
```

ParaView/VTU post-processing output file; None (the default) leaves VTU output to the ResultOutputSolver — setting both trips Elmer's AddVtuOutputSolverHack and its log spam.

#### output\_file

```python
output_file: str | None = None
```

ElmerSolver restart/result file; None (the default) skips it — nothing downstream reads it and it grows to multi-GB on large solves. Set a name only when a restart is wanted.

### Constants

Bases: `SifKeywordModel`

The Elmer `Constants` block: each constant is an optional field carrying its sif keyword; a physics sets only the constants it needs, and an unset one is omitted.

Fields:

- `extra_keywords` (`dict[str, SifValue]`)
- `permeability_of_vacuum` (`float | None`)
- `permittivity_of_vacuum` (`float | None`)
- `stefan_boltzmann` (`float | None`)

#### permeability\_of\_vacuum

```python
permeability_of_vacuum: float | None = None
```

`μ₀` `[H/m]`: Needed by the magnetostatic solver.

#### permittivity\_of\_vacuum

```python
permittivity_of_vacuum: float | None = None
```

`ε₀` `[F/m]`: Needed by the electrostatic solver.

#### stefan\_boltzmann

```python
stefan_boltzmann: float | None = None
```

`σ_SB` `[W/(m²·K⁴)]`: Needed for thermal radiation.

### SifKeywordModel

Bases: `BaseModel`

Base for a config model whose fields ARE its sif keywords: each field carries its Elmer keyword as `Map(...)` metadata, and `to_elmer` reflects the pairs back out via `mapped_items` (no parallel string-keyed table).

Fields:

- `extra_keywords` (`dict[str, SifValue]`)

#### extra\_keywords

```python
extra_keywords: dict[str, SifValue]
```

Escape hatch: verbatim sif keyword/value pairs merged (last, so they win) into `to_elmer` — any Elmer keyword the typed fields don't model yet stays authorable without subclassing.

#### to\_elmer

```python
to_elmer() -> dict[str, SifValue]
```

The sif keyword/value dict reflected from each field's `Map(...)` keyword, with `extra_keywords` merged last.

None fields are left out so Elmer keeps its defaults, and a list expands to a numbered family such as `Vector Field 1..n`.

### SolverBase

Bases: `SifKeywordModel`

Base for a typed Elmer solver: its fields are its sif keywords.

Fields:

- `extra_keywords` (`dict[str, SifValue]`)
- `kind` (`str`)
- `equation` (`EquationField`)
- `procedure` (`ProcedureField`)
- `variable` (`VariableField`)
- `variable_dofs` (`VariableDofsField`)
- `exec_solver` (`ExecSolverField`)
- `stabilize` (`StabilizeField`)
- `optimize_bandwidth` (`OptimizeBandwidthField`)

#### kind

```python
kind: str
```

Discriminator that names this solver, doubling as its sif Solver label.

#### required\_material\_keys

```python
required_material_keys: frozenset[ElmerMaterialKeyword] = frozenset()
```

The material keywords every body this solver acts on must supply, checked by `Sim.validate`.

#### conductor\_only

```python
conductor_only: bool = False
```

Restrict this solver's equation to conductor bodies: solving a conduction variable over dielectrics too puts \~21 decades of σ contrast into one matrix — singular for a direct factorization, a stall for preconditioned CG.

### LinearSystem

Bases: `BaseModel`

Mixin: the `Linear System *` settings for a solver that does a linear solve. Defaults are sensible iterative values; concrete solvers tune the iteration cap and tolerances they need.

Config:

- `use_attribute_docstrings`: `True`

Fields:

- `linear_system_solver` (`LinearSolverTypeField`)
- `linear_system_direct_method` (`LinearDirectMethodField`)
- `linear_system_iterative_method` (`LinearIterativeMethodField`)
- `linear_system_max_iterations` (`LinearMaxIterationsField`)
- `linear_system_convergence_tolerance` (`LinearConvToleranceField`)
- `linear_system_preconditioning` (`LinearPreconditioningField`)
- `linear_system_residual_output` (`LinearResidualOutputField`)
- `linear_system_abort_not_converged` (`LinearAbortNotConvergedField`)
- `bicgstabl_polynomial_degree` (`BiCGStablPolynomialDegreeField`)

### NonlinearSystem

Bases: `BaseModel`

Mixin: the `Nonlinear System *` (outer-iteration) convergence settings.

Fields:

- `nonlinear_system_convergence_tolerance` (`NonlinearConvToleranceField`)
- `nonlinear_system_max_iterations` (`NonlinearMaxIterationsField`)
- `nonlinear_system_newton_after_iterations` (`NonlinearNewtonAfterIterationsField`)
- `nonlinear_system_newton_after_tolerance` (`NonlinearNewtonAfterToleranceField`)
- `nonlinear_system_relaxation_factor` (`NonlinearRelaxationFactorField`)
- `nonlinear_system_convergence_measure` (`NonlinearConvMeasureField`)

### SteadyState

Bases: `BaseModel`

Mixin: the `Steady State Convergence Tolerance` for a steady-state run.

Fields:

- `steady_state_convergence_tolerance` (`SteadyStateConvToleranceField`)

### MagnetoDynamicsSolver

Bases: `SolverBase`, `LinearSystem`, `NonlinearSystem`, `SteadyState`

3D magnetostatics (WhitneyAVSolver), with each magnet's drive entering as `Magnetization` on its material rather than as a body force.

Leave `variable` unset, so the far-field boundary condition can still name `AV {e}`.

Fields:

- `steady_state_convergence_tolerance` (`SteadyStateConvToleranceField`)
- `nonlinear_system_convergence_measure` (`NonlinearConvMeasureField`)
- `linear_system_solver` (`LinearSolverTypeField`)
- `linear_system_direct_method` (`LinearDirectMethodField`)
- `extra_keywords` (`dict[str, SifValue]`)
- `variable` (`VariableField`)
- `variable_dofs` (`VariableDofsField`)
- `exec_solver` (`ExecSolverField`)
- `stabilize` (`StabilizeField`)
- `kind` (`Literal['MagnetoDynamics']`)
- `equation` (`EquationField`)
- `procedure` (`ProcedureField`)
- `optimize_bandwidth` (`OptimizeBandwidthField`)
- `nonlinear_system_convergence_tolerance` (`NonlinearConvToleranceField`)
- `nonlinear_system_max_iterations` (`NonlinearMaxIterationsField`)
- `nonlinear_system_newton_after_iterations` (`NonlinearNewtonAfterIterationsField`)
- `nonlinear_system_newton_after_tolerance` (`NonlinearNewtonAfterToleranceField`)
- `nonlinear_system_relaxation_factor` (`NonlinearRelaxationFactorField`)
- `linear_system_iterative_method` (`LinearIterativeMethodField`)
- `bicgstabl_polynomial_degree` (`BiCGStablPolynomialDegreeField`)
- `linear_system_preconditioning` (`LinearPreconditioningField`)
- `linear_system_max_iterations` (`LinearMaxIterationsField`)
- `linear_system_convergence_tolerance` (`LinearConvToleranceField`)
- `linear_system_abort_not_converged` (`LinearAbortNotConvergedField`)
- `linear_system_residual_output` (`LinearResidualOutputField`)
- `fix_input_current_density` (`bool | None`)
- `use_elemental_coilcurrent` (`bool | None`)

#### kind

```python
kind: Literal['MagnetoDynamics'] = 'MagnetoDynamics'
```

Discriminator that names this solver, doubling as its sif Solver label.

#### fix\_input\_current\_density

```python
fix_input_current_density: bool | None = None
```

Project the source J onto a divergence-free field before the AV solve: the discrete J is not exactly solenoidal edge-element-wise, and an unfixed source makes the gauge-non-unique system inconsistent. None omits the keyword.

#### use\_elemental\_coilcurrent

```python
use_elemental_coilcurrent: bool | None = None
```

Read the source J from CoilSolver's *elemental* (DG) field instead of a nodal Body Force: nodal J is Galerkin-averaged across the conductor-air interface and silently dilutes the drive \~2x on thin traces. Requires a CoilSolver in the stack; None omits the keyword.

### MagnetoDynamics2DSolver

Bases: `SolverBase`, `LinearSystem`, `NonlinearSystem`, `SteadyState`

Axisymmetric magnetostatics (MagnetoDynamics2D): the scalar azimuthal potential A\_phi under `Coordinate System = Axi Symmetric`.

The mesh lies flat in the XY plane (Z = 0), with X the radial distance from the symmetry axis (X \>= 0) and Y the position along it.

Fields:

- `nonlinear_system_newton_after_iterations` (`NonlinearNewtonAfterIterationsField`)
- `nonlinear_system_newton_after_tolerance` (`NonlinearNewtonAfterToleranceField`)
- `linear_system_iterative_method` (`LinearIterativeMethodField`)
- `linear_system_max_iterations` (`LinearMaxIterationsField`)
- `linear_system_convergence_tolerance` (`LinearConvToleranceField`)
- `linear_system_preconditioning` (`LinearPreconditioningField`)
- `linear_system_residual_output` (`LinearResidualOutputField`)
- `linear_system_abort_not_converged` (`LinearAbortNotConvergedField`)
- `bicgstabl_polynomial_degree` (`BiCGStablPolynomialDegreeField`)
- `extra_keywords` (`dict[str, SifValue]`)
- `exec_solver` (`ExecSolverField`)
- `stabilize` (`StabilizeField`)
- `kind` (`Literal['MagnetoDynamics2D']`)
- `equation` (`EquationField`)
- `procedure` (`ProcedureField`)
- `variable` (`VariableField`)
- `variable_dofs` (`VariableDofsField`)
- `optimize_bandwidth` (`OptimizeBandwidthField`)
- `nonlinear_system_max_iterations` (`NonlinearMaxIterationsField`)
- `nonlinear_system_convergence_tolerance` (`NonlinearConvToleranceField`)
- `nonlinear_system_convergence_measure` (`NonlinearConvMeasureField`)
- `nonlinear_system_relaxation_factor` (`NonlinearRelaxationFactorField`)
- `linear_system_solver` (`LinearSolverTypeField`)
- `linear_system_direct_method` (`LinearDirectMethodField`)
- `steady_state_convergence_tolerance` (`SteadyStateConvToleranceField`)

#### kind

```python
kind: Literal['MagnetoDynamics2D'] = 'MagnetoDynamics2D'
```

Discriminator that names this solver, doubling as its sif Solver label.

### MagnetoDynamicsCalcFieldsSolver

Bases: `SolverBase`, `LinearSystem`

Post-processing: recover B, H, fluxes and nodal forces from the computed A. Inherits `Linear System *` because nodal-field recovery solves an (SPD, hence CG) mass-matrix system — without a linear solver Elmer aborts.

Fields:

- `linear_system_solver` (`LinearSolverTypeField`)
- `linear_system_direct_method` (`LinearDirectMethodField`)
- `linear_system_convergence_tolerance` (`LinearConvToleranceField`)
- `linear_system_preconditioning` (`LinearPreconditioningField`)
- `linear_system_residual_output` (`LinearResidualOutputField`)
- `linear_system_abort_not_converged` (`LinearAbortNotConvergedField`)
- `bicgstabl_polynomial_degree` (`BiCGStablPolynomialDegreeField`)
- `extra_keywords` (`dict[str, SifValue]`)
- `variable` (`VariableField`)
- `variable_dofs` (`VariableDofsField`)
- `exec_solver` (`ExecSolverField`)
- `stabilize` (`StabilizeField`)
- `kind` (`Literal['MagnetoDynamicsCalcFields']`)
- `equation` (`EquationField`)
- `procedure` (`ProcedureField`)
- `optimize_bandwidth` (`OptimizeBandwidthField`)
- `linear_system_iterative_method` (`LinearIterativeMethodField`)
- `linear_system_max_iterations` (`LinearMaxIterationsField`)
- `potential_variable` (`str | None`)
- `average_within_materials` (`bool | None`)
- `calculate_magnetic_field_strength` (`bool`)
- `calculate_magnetic_flux_density` (`bool`)
- `calculate_nodal_forces` (`bool`)
- `calculate_nodal_fields` (`bool`)
- `calculate_joule_heating` (`bool | None`)
- `calculate_electric_field` (`bool | None`)
- `calculate_magnetic_vector_potential` (`bool | None`)
- `separate_magnetic_energy` (`bool | None`)
- `calculate_relative_permeability` (`bool | None`)
- `calculate_elemental_fields` (`bool`)

#### kind

```python
kind: Literal['MagnetoDynamicsCalcFields'] = 'MagnetoDynamicsCalcFields'
```

Discriminator that names this solver, doubling as its sif Solver label.

#### potential\_variable

```python
potential_variable: str | None = None
```

The vector-potential variable to post-process; None lets the solver pick up the (default-named) MgDyn variable.

#### average\_within\_materials

```python
average_within_materials: bool | None = True
```

Average recovered fields within each material so the B/H jump at a magnet surface is resolved cleanly.

#### calculate\_magnetic\_field\_strength

```python
calculate_magnetic_field_strength: bool = True
```

Recover H.

#### calculate\_magnetic\_flux\_density

```python
calculate_magnetic_flux_density: bool = True
```

Recover B.

#### calculate\_nodal\_forces

```python
calculate_nodal_forces: bool = True
```

Recover nodal forces (drives the motor-force optimisation).

#### calculate\_nodal\_fields

```python
calculate_nodal_fields: bool = False
```

Recover nodal field values.

#### calculate\_joule\_heating

```python
calculate_joule_heating: bool | None = None
```

Recover the eddy/Joule loss field. Identically zero for a steady real-valued solve — set False there: the zero-RHS projection it triggers when a Current Density body force is present segfaults CalcFields.

#### calculate\_electric\_field

```python
calculate_electric_field: bool | None = None
```

Recover E = -dA/dt - grad V. Identically zero at steady state — set False for the chained coil solve, where its zero-RHS projection is what segfaults CalcFields after the B components.

#### calculate\_magnetic\_vector\_potential

```python
calculate_magnetic_vector_potential: bool | None = None
```

Project the edge-element av onto a nodal A field; None keeps the solver default.

#### separate\_magnetic\_energy

```python
separate_magnetic_energy: bool | None = None
```

Export the magnetic field energy as SaveScalars-visible scalars (the ElmerCSC mgdyn\_bh recipe) — the energy-method inductance observable: L\_eff = 2W/I².

#### calculate\_relative\_permeability

```python
calculate_relative_permeability: bool | None = None
```

Recover the local relative permeability; the mgdyn\_bh energy recipe enables it alongside the energy export.

#### calculate\_elemental\_fields

```python
calculate_elemental_fields: bool = True
```

Recover elemental field values.

### ResultOutputSolver

Bases: `SolverBase`

Write VTU output for ParaView.

Fields:

- `extra_keywords` (`dict[str, SifValue]`)
- `variable` (`VariableField`)
- `variable_dofs` (`VariableDofsField`)
- `stabilize` (`StabilizeField`)
- `optimize_bandwidth` (`OptimizeBandwidthField`)
- `kind` (`Literal['ResultOutputSolver']`)
- `equation` (`EquationField`)
- `procedure` (`ProcedureField`)
- `exec_solver` (`ExecSolverField`)
- `vtu_format` (`bool`)
- `output_file_name` (`str`)
- `save_geometry_ids` (`bool`)
- `saved_scalar_fields` (`list[str] | None`)
- `saved_vector_fields` (`list[str] | None`)
- `discontinuous_bodies` (`bool | None`)

#### kind

```python
kind: Literal['ResultOutputSolver'] = 'ResultOutputSolver'
```

Discriminator that names this solver, doubling as its sif Solver label.

#### vtu\_format

```python
vtu_format: bool = True
```

Write VTU (ParaView) output.

#### output\_file\_name

```python
output_file_name: str = 'File "case"'
```

Stem of the written VTU (Elmer emits e.g. `case_t0001.vtu`); pinned so the name never depends on Elmer defaults.

#### save\_geometry\_ids

```python
save_geometry_ids: bool = True
```

Tag output cells with their gmsh geometry ids.

#### saved\_scalar\_fields

```python
saved_scalar_fields: list[str] | None = None
```

Scalar fields to save (each a full sif RHS, e.g. `'String "joule heating"'`); None saves every active variable.

#### saved\_vector\_fields

```python
saved_vector_fields: list[str] | None = None
```

Vector fields to save; None saves every active variable.

#### discontinuous\_bodies

```python
discontinuous_bodies: bool | None = None
```

Average elemental fields body-wise onto a minimal discontinuous node set, so the VTU keeps cross-body discontinuities.

### HeatSolver

Bases: `SolverBase`, `LinearSystem`, `NonlinearSystem`, `SteadyState`

SCAFFOLD: steady heat conduction. Wire-ready; its boundary conditions land on the adjacency-derived surface groups via the condition-driven boundary loop.

Fields:

- `nonlinear_system_newton_after_iterations` (`NonlinearNewtonAfterIterationsField`)
- `nonlinear_system_newton_after_tolerance` (`NonlinearNewtonAfterToleranceField`)
- `nonlinear_system_relaxation_factor` (`NonlinearRelaxationFactorField`)
- `nonlinear_system_convergence_measure` (`NonlinearConvMeasureField`)
- `linear_system_iterative_method` (`LinearIterativeMethodField`)
- `linear_system_convergence_tolerance` (`LinearConvToleranceField`)
- `linear_system_residual_output` (`LinearResidualOutputField`)
- `linear_system_abort_not_converged` (`LinearAbortNotConvergedField`)
- `bicgstabl_polynomial_degree` (`BiCGStablPolynomialDegreeField`)
- `extra_keywords` (`dict[str, SifValue]`)
- `exec_solver` (`ExecSolverField`)
- `kind` (`Literal['HeatSolver']`)
- `equation` (`EquationField`)
- `procedure` (`ProcedureField`)
- `variable` (`VariableField`)
- `variable_dofs` (`VariableDofsField`)
- `stabilize` (`StabilizeField`)
- `optimize_bandwidth` (`OptimizeBandwidthField`)
- `linear_system_solver` (`LinearSolverTypeField`)
- `linear_system_direct_method` (`LinearDirectMethodField`)
- `steady_state_convergence_tolerance` (`SteadyStateConvToleranceField`)
- `nonlinear_system_convergence_tolerance` (`NonlinearConvToleranceField`)
- `nonlinear_system_max_iterations` (`NonlinearMaxIterationsField`)
- `linear_system_max_iterations` (`LinearMaxIterationsField`)
- `linear_system_preconditioning` (`LinearPreconditioningField`)

#### kind

```python
kind: Literal['HeatSolver'] = 'HeatSolver'
```

Discriminator that names this solver, doubling as its sif Solver label.

### StatCurrentSolver

Bases: `SolverBase`, `LinearSystem`, `NonlinearSystem`, `SteadyState`

Static current conduction (Elmer's StatCurrentSolve): solves `Potential` from ∇·(σ∇φ) = 0, excited by per-phase `FixedPotential` terminal pairs. The nonlinear block is inert while σ is Static.

Fields:

- `nonlinear_system_newton_after_iterations` (`NonlinearNewtonAfterIterationsField`)
- `nonlinear_system_newton_after_tolerance` (`NonlinearNewtonAfterToleranceField`)
- `nonlinear_system_relaxation_factor` (`NonlinearRelaxationFactorField`)
- `nonlinear_system_convergence_measure` (`NonlinearConvMeasureField`)
- `linear_system_preconditioning` (`LinearPreconditioningField`)
- `linear_system_residual_output` (`LinearResidualOutputField`)
- `linear_system_abort_not_converged` (`LinearAbortNotConvergedField`)
- `bicgstabl_polynomial_degree` (`BiCGStablPolynomialDegreeField`)
- `extra_keywords` (`dict[str, SifValue]`)
- `exec_solver` (`ExecSolverField`)
- `kind` (`Literal['StatCurrent']`)
- `equation` (`EquationField`)
- `procedure` (`ProcedureField`)
- `variable` (`VariableField`)
- `variable_dofs` (`VariableDofsField`)
- `stabilize` (`StabilizeField`)
- `optimize_bandwidth` (`OptimizeBandwidthField`)
- `steady_state_convergence_tolerance` (`SteadyStateConvToleranceField`)
- `nonlinear_system_convergence_tolerance` (`NonlinearConvToleranceField`)
- `nonlinear_system_max_iterations` (`NonlinearMaxIterationsField`)
- `linear_system_solver` (`LinearSolverTypeField`)
- `linear_system_direct_method` (`LinearDirectMethodField`)
- `linear_system_iterative_method` (`LinearIterativeMethodField`)
- `linear_system_max_iterations` (`LinearMaxIterationsField`)
- `linear_system_convergence_tolerance` (`LinearConvToleranceField`)
- `calculate_volume_current` (`bool`)
- `calculate_joule_heating` (`bool`)

#### kind

```python
kind: Literal['StatCurrent'] = 'StatCurrent'
```

Discriminator that names this solver, doubling as its sif Solver label.

#### calculate\_volume\_current

```python
calculate_volume_current: bool = True
```

Recover the volume current density J = -σ∇φ.

#### calculate\_joule\_heating

```python
calculate_joule_heating: bool = True
```

Recover the Joule loss density σ|∇φ|² (the heat-solve coupling source).

### CoilCurrentSolver

Bases: `SolverBase`, `LinearSystem`, `NonlinearSystem`, `SteadyState`

Elmer's `CoilSolver`: the divergence-conforming coil current distribution, the source stage of the `COIL_MAGNETOSTATICS` chain.

It acts only on `CoilDrive` bodies, because Elmer requires every body it covers to belong to a `Component`.

Config:

- `validate_assignment`: `True`
- `use_attribute_docstrings`: `True`

Fields:

- `steady_state_convergence_tolerance` (`SteadyStateConvToleranceField`)
- `nonlinear_system_convergence_tolerance` (`NonlinearConvToleranceField`)
- `nonlinear_system_max_iterations` (`NonlinearMaxIterationsField`)
- `nonlinear_system_newton_after_iterations` (`NonlinearNewtonAfterIterationsField`)
- `nonlinear_system_newton_after_tolerance` (`NonlinearNewtonAfterToleranceField`)
- `nonlinear_system_relaxation_factor` (`NonlinearRelaxationFactorField`)
- `nonlinear_system_convergence_measure` (`NonlinearConvMeasureField`)
- `linear_system_convergence_tolerance` (`LinearConvToleranceField`)
- `linear_system_preconditioning` (`LinearPreconditioningField`)
- `linear_system_residual_output` (`LinearResidualOutputField`)
- `linear_system_abort_not_converged` (`LinearAbortNotConvergedField`)
- `bicgstabl_polynomial_degree` (`BiCGStablPolynomialDegreeField`)
- `extra_keywords` (`dict[str, SifValue]`)
- `variable` (`VariableField`)
- `variable_dofs` (`VariableDofsField`)
- `stabilize` (`StabilizeField`)
- `kind` (`Literal['CoilCurrent']`)
- `equation` (`EquationField`)
- `procedure` (`ProcedureField`)
- `exec_solver` (`ExecSolverField`)
- `optimize_bandwidth` (`OptimizeBandwidthField`)
- `linear_system_solver` (`LinearSolverTypeField`)
- `linear_system_direct_method` (`LinearDirectMethodField`)
- `linear_system_iterative_method` (`LinearIterativeMethodField`)
- `linear_system_max_iterations` (`LinearMaxIterationsField`)
- `coil_closed` (`bool`)
- `normalize_coil_current` (`bool`)
- `calculate_elemental_fields` (`bool`)

#### kind

```python
kind: Literal['CoilCurrent'] = 'CoilCurrent'
```

Discriminator that names this solver, doubling as its sif Solver label.

#### coil\_closed

```python
coil_closed: bool = False
```

False: an open coil whose ends are the `Coil Start` / `Coil End` electrode boundaries.

#### normalize\_coil\_current

```python
normalize_coil_current: bool = False
```

Must stay OFF for a massive coil: it rescales |J| to a UNIFORM magnitude, which on a varying cross-section authors a non-conservative source that Jfix redistributes (silently reduced drive current in the winding). `Desired Coil Current` scaling happens regardless, so the total current is still exact with this off.

#### calculate\_elemental\_fields

```python
calculate_elemental_fields: bool = True
```

Export the DG `CoilCurrent e` field (what `Use Elemental CoilCurrent` reads); the nodal field stays too.

### SaveScalarsSolver

Bases: `SolverBase`

SaveScalars (SaveData): export scalar reductions to `scalars.dat` and its `.names` sidecar.

Its `diffusive flux` operator integrates each `Save Scalars = True` electrode's terminal current, the `I` in `R = ΔV / I`.

Fields:

- `extra_keywords` (`dict[str, SifValue]`)
- `variable` (`VariableField`)
- `variable_dofs` (`VariableDofsField`)
- `stabilize` (`StabilizeField`)
- `optimize_bandwidth` (`OptimizeBandwidthField`)
- `kind` (`Literal['SaveScalars']`)
- `equation` (`EquationField`)
- `procedure` (`ProcedureField`)
- `exec_solver` (`ExecSolverField`)
- `save_component_results` (`bool | None`)
- `parallel_reduce` (`bool | None`)
- `filename` (`str`)
- `variable_1` (`str | None`)
- `operator_1` (`str | None`)
- `coefficient_1` (`str | None`)
- `variable_2` (`str | None`)
- `operator_2` (`str | None`)
- `variable_3` (`str | None`)
- `operator_3` (`str | None`)

#### kind

```python
kind: Literal['SaveScalars'] = 'SaveScalars'
```

Discriminator that names this solver, doubling as its sif Solver label.

#### save\_component\_results

```python
save_component_results: bool | None = None
```

Also export the scalars arising from Component sections.

#### parallel\_reduce

```python
parallel_reduce: bool | None = None
```

Reduce a partitioned run's output to one `scalars.dat` instead of per-rank `.dat.<rank>` files.

#### filename

```python
filename: str = '"scalars.dat"'
```

The .dat the scalars land in (the run parser reads it, or its per-rank `.dat.<rank>` siblings, via the `.names` sidecar).

#### variable\_1

```python
variable_1: str | None = ElmerVariable.POTENTIAL
```

The variable the operator reduces.

#### operator\_1

```python
operator_1: str | None = 'diffusive flux'
```

The reduction: boundary integral of the conjugate flux of `variable_1`.

#### coefficient\_1

```python
coefficient_1: str | None = 'Electric Conductivity'
```

The material coefficient weighting the flux (σ ∂φ/∂n integrates to a current).

#### variable\_2

```python
variable_2: str | None = ElmerVariable.JOULE_HEATING
```

Second reduction: the Joule loss field (requires `Calculate Joule Heating`).

#### operator\_2

```python
operator_2: str | None = 'int'
```

Volume integral: total dissipated power P (W), the flux-independent cross-check R = ΔV²/P.

#### variable\_3

```python
variable_3: str | None = None
```

Third reduction; the coupled joule-heating stack sets `Temperature` here.

#### operator\_3

```python
operator_3: str | None = None
```

Reduction for `variable_3`; the coupled stack uses `max` (peak temperature).

### ElectrostaticsSolver

Bases: `SolverBase`, `LinearSystem`, `SteadyState`

SCAFFOLD: electrostatics (StatElecSolver). No nonlinear iteration.

Fields:

- `linear_system_solver` (`LinearSolverTypeField`)
- `linear_system_direct_method` (`LinearDirectMethodField`)
- `linear_system_iterative_method` (`LinearIterativeMethodField`)
- `linear_system_preconditioning` (`LinearPreconditioningField`)
- `linear_system_residual_output` (`LinearResidualOutputField`)
- `linear_system_abort_not_converged` (`LinearAbortNotConvergedField`)
- `bicgstabl_polynomial_degree` (`BiCGStablPolynomialDegreeField`)
- `extra_keywords` (`dict[str, SifValue]`)
- `variable_dofs` (`VariableDofsField`)
- `exec_solver` (`ExecSolverField`)
- `optimize_bandwidth` (`OptimizeBandwidthField`)
- `kind` (`Literal['Electrostatics']`)
- `equation` (`EquationField`)
- `procedure` (`ProcedureField`)
- `variable` (`VariableField`)
- `stabilize` (`StabilizeField`)
- `steady_state_convergence_tolerance` (`SteadyStateConvToleranceField`)
- `linear_system_max_iterations` (`LinearMaxIterationsField`)
- `linear_system_convergence_tolerance` (`LinearConvToleranceField`)
- `calculate_electric_field` (`bool`)

#### kind

```python
kind: Literal['Electrostatics'] = 'Electrostatics'
```

Discriminator that names this solver, doubling as its sif Solver label.

#### calculate\_electric\_field

```python
calculate_electric_field: bool = True
```

Recover E from the potential.

### StressSolver

Bases: `SolverBase`, `LinearSystem`, `NonlinearSystem`, `SteadyState`

SCAFFOLD: linear elasticity (Elmer's StressSolve). Consumes `Youngs Modulus` and `Poisson Ratio` material keywords; the latter is not yet stored on MechanicalProperties.

Fields:

- `nonlinear_system_newton_after_iterations` (`NonlinearNewtonAfterIterationsField`)
- `nonlinear_system_newton_after_tolerance` (`NonlinearNewtonAfterToleranceField`)
- `nonlinear_system_relaxation_factor` (`NonlinearRelaxationFactorField`)
- `nonlinear_system_convergence_measure` (`NonlinearConvMeasureField`)
- `linear_system_solver` (`LinearSolverTypeField`)
- `linear_system_direct_method` (`LinearDirectMethodField`)
- `linear_system_iterative_method` (`LinearIterativeMethodField`)
- `linear_system_convergence_tolerance` (`LinearConvToleranceField`)
- `linear_system_preconditioning` (`LinearPreconditioningField`)
- `linear_system_residual_output` (`LinearResidualOutputField`)
- `linear_system_abort_not_converged` (`LinearAbortNotConvergedField`)
- `bicgstabl_polynomial_degree` (`BiCGStablPolynomialDegreeField`)
- `extra_keywords` (`dict[str, SifValue]`)
- `exec_solver` (`ExecSolverField`)
- `stabilize` (`StabilizeField`)
- `kind` (`Literal['StressSolver']`)
- `equation` (`EquationField`)
- `procedure` (`ProcedureField`)
- `variable` (`VariableField`)
- `variable_dofs` (`VariableDofsField`)
- `optimize_bandwidth` (`OptimizeBandwidthField`)
- `steady_state_convergence_tolerance` (`SteadyStateConvToleranceField`)
- `nonlinear_system_convergence_tolerance` (`NonlinearConvToleranceField`)
- `nonlinear_system_max_iterations` (`NonlinearMaxIterationsField`)
- `linear_system_max_iterations` (`LinearMaxIterationsField`)
- `calculate_stresses` (`bool`)

#### kind

```python
kind: Literal['StressSolver'] = 'StressSolver'
```

Discriminator that names this solver, doubling as its sif Solver label.

#### calculate\_stresses

```python
calculate_stresses: bool = True
```

Recover the stress tensor from the displacement.

### GenericSolver

Bases: `SolverBase`, `LinearSystem`, `NonlinearSystem`, `SteadyState`

Escape hatch: an arbitrary Elmer solver block for anything the typed hierarchy doesn't model yet — set `equation`/`procedure` and author the rest through `extra_keywords` (plus the shared typed knobs).

Fields:

- `steady_state_convergence_tolerance` (`SteadyStateConvToleranceField`)
- `nonlinear_system_convergence_tolerance` (`NonlinearConvToleranceField`)
- `nonlinear_system_max_iterations` (`NonlinearMaxIterationsField`)
- `nonlinear_system_newton_after_iterations` (`NonlinearNewtonAfterIterationsField`)
- `nonlinear_system_newton_after_tolerance` (`NonlinearNewtonAfterToleranceField`)
- `nonlinear_system_relaxation_factor` (`NonlinearRelaxationFactorField`)
- `nonlinear_system_convergence_measure` (`NonlinearConvMeasureField`)
- `linear_system_solver` (`LinearSolverTypeField`)
- `linear_system_direct_method` (`LinearDirectMethodField`)
- `linear_system_iterative_method` (`LinearIterativeMethodField`)
- `linear_system_max_iterations` (`LinearMaxIterationsField`)
- `linear_system_convergence_tolerance` (`LinearConvToleranceField`)
- `linear_system_preconditioning` (`LinearPreconditioningField`)
- `linear_system_residual_output` (`LinearResidualOutputField`)
- `linear_system_abort_not_converged` (`LinearAbortNotConvergedField`)
- `bicgstabl_polynomial_degree` (`BiCGStablPolynomialDegreeField`)
- `extra_keywords` (`dict[str, SifValue]`)
- `equation` (`EquationField`)
- `procedure` (`ProcedureField`)
- `variable` (`VariableField`)
- `variable_dofs` (`VariableDofsField`)
- `exec_solver` (`ExecSolverField`)
- `stabilize` (`StabilizeField`)
- `optimize_bandwidth` (`OptimizeBandwidthField`)
- `kind` (`Literal['Generic']`)

#### kind

```python
kind: Literal['Generic'] = 'Generic'
```

Discriminator that names this solver, doubling as its sif Solver label.

### CoordinateSystem

Bases: `StrEnum`

Elmer's `Coordinate System`.

Attributes:

- **`CARTESIAN`** –

  Cartesian coordinates.
- **`CYLINDRIC`** –

  Full cylindrical coordinates.
- **`CYLINDRIC_SYMMETRIC`** –

  2D cylindrically symmetric (r, z) coordinates, integrated per radian.
- **`AXI_SYMMETRIC`** –

  2D axisymmetric (r, z) coordinates, integrated per radian.
- **`POLAR`** –

  Polar coordinates.
- **`per_radian`** (`bool`) –

  Whether Elmer integrates domain integrals per radian here (the 2D symmetric systems), so lumped outputs need a 2π revolution factor.

#### CARTESIAN

```python
CARTESIAN = 'Cartesian'
```

Cartesian coordinates.

#### CYLINDRIC

```python
CYLINDRIC = 'Cylindric'
```

Full cylindrical coordinates.

#### CYLINDRIC\_SYMMETRIC

```python
CYLINDRIC_SYMMETRIC = 'Cylindric Symmetric'
```

2D cylindrically symmetric (r, z) coordinates, integrated per radian.

#### AXI\_SYMMETRIC

```python
AXI_SYMMETRIC = 'Axi Symmetric'
```

2D axisymmetric (r, z) coordinates, integrated per radian.

#### POLAR

```python
POLAR = 'Polar'
```

Polar coordinates.

#### per\_radian

```python
per_radian: bool
```

Whether Elmer integrates domain integrals per radian here (the 2D symmetric systems), so lumped outputs need a 2π revolution factor.

### SimulationType

Bases: `StrEnum`

Elmer's `Simulation Type`.

Attributes:

- **`STEADY_STATE`** –

  A steady-state solve.
- **`TRANSIENT`** –

  A time-dependent solve.
- **`SCANNING`** –

  A sweep of steady-state solves over a scanned parameter.

#### STEADY\_STATE

```python
STEADY_STATE = 'Steady state'
```

A steady-state solve.

#### TRANSIENT

```python
TRANSIENT = 'Transient'
```

A time-dependent solve.

#### SCANNING

```python
SCANNING = 'Scanning'
```

A sweep of steady-state solves over a scanned parameter.

### TimesteppingMethod

Bases: `StrEnum`

Elmer's `Timestepping Method` (paired with `BDF Order` for BDF).

Attributes:

- **`BDF`** –

  Backward differentiation formula, of order `bdf_order`.
- **`NEWMARK`** –

  The Newmark-beta method.
- **`RUNGE_KUTTA`** –

  A Runge-Kutta method.

#### BDF

```python
BDF = 'BDF'
```

Backward differentiation formula, of order `bdf_order`.

#### NEWMARK

```python
NEWMARK = 'Newmark'
```

The Newmark-beta method.

#### RUNGE\_KUTTA

```python
RUNGE_KUTTA = 'Runge-Kutta'
```

A Runge-Kutta method.

### ExecSolver

Bases: `StrEnum`

When Elmer executes a solver (the `Exec Solver` keyword).

Attributes:

- **`ALWAYS`** –

  Run in the normal solve sequence.
- **`BEFORE_SIMULATION`** –

  Run once before the simulation.
- **`AFTER_SIMULATION`** –

  Run once after the simulation.
- **`BEFORE_TIMESTEP`** –

  Run at the start of each timestep.
- **`AFTER_TIMESTEP`** –

  Run at the end of each timestep.
- **`NEVER`** –

  Never run.

#### ALWAYS

```python
ALWAYS = 'Always'
```

Run in the normal solve sequence.

#### BEFORE\_SIMULATION

```python
BEFORE_SIMULATION = 'Before simulation'
```

Run once before the simulation.

#### AFTER\_SIMULATION

```python
AFTER_SIMULATION = 'After simulation'
```

Run once after the simulation.

#### BEFORE\_TIMESTEP

```python
BEFORE_TIMESTEP = 'Before timestep'
```

Run at the start of each timestep.

#### AFTER\_TIMESTEP

```python
AFTER_TIMESTEP = 'After timestep'
```

Run at the end of each timestep.

#### NEVER

```python
NEVER = 'Never'
```

Never run.

### LinearSolverType

Bases: `StrEnum`

The `Linear System Solver` strategy: an iterative Krylov method or a direct factorization.

Attributes:

- **`ITERATIVE`** –

  A Krylov iteration, chosen by `linear_system_iterative_method`.
- **`DIRECT`** –

  A direct factorization, chosen by `linear_system_direct_method`.

#### ITERATIVE

```python
ITERATIVE = 'Iterative'
```

A Krylov iteration, chosen by `linear_system_iterative_method`.

#### DIRECT

```python
DIRECT = 'Direct'
```

A direct factorization, chosen by `linear_system_direct_method`.

### IterativeMethod

Bases: `StrEnum`

The Krylov method for an iterative linear solve (`Linear System Iterative Method`).

Attributes:

- **`BICGSTAB`** –

  Biconjugate gradient stabilized.
- **`BICGSTABL`** –

  BiCGStab(l), of degree `bicgstabl_polynomial_degree`.
- **`CG`** –

  Conjugate gradient, for symmetric positive-definite systems.
- **`CGS`** –

  Conjugate gradient squared.
- **`GMRES`** –

  Generalized minimal residual.
- **`GCR`** –

  Generalized conjugate residual.
- **`TFQMR`** –

  Transpose-free quasi-minimal residual.

#### BICGSTAB

```python
BICGSTAB = 'BiCGStab'
```

Biconjugate gradient stabilized.

#### BICGSTABL

```python
BICGSTABL = 'BiCGStabl'
```

BiCGStab(l), of degree `bicgstabl_polynomial_degree`.

#### CG

```python
CG = 'CG'
```

Conjugate gradient, for symmetric positive-definite systems.

#### CGS

```python
CGS = 'CGS'
```

Conjugate gradient squared.

#### GMRES

```python
GMRES = 'GMRES'
```

Generalized minimal residual.

#### GCR

```python
GCR = 'GCR'
```

Generalized conjugate residual.

#### TFQMR

```python
TFQMR = 'TFQMR'
```

Transpose-free quasi-minimal residual.

### Preconditioning

Bases: `StrEnum`

The `Linear System Preconditioning` for an iterative solve.

Attributes:

- **`NONE`** –

  No preconditioning.
- **`DIAGONAL`** –

  Diagonal (Jacobi) scaling.
- **`ILU0`** –

  Incomplete LU without fill-in.
- **`ILU1`** –

  Incomplete LU with fill level 1.
- **`ILU2`** –

  Incomplete LU with fill level 2.
- **`ILU`** –

  Incomplete LU without an explicit fill level.
- **`ILUT`** –

  Incomplete LU with threshold dropping.

#### NONE

```python
NONE = 'None'
```

No preconditioning.

#### DIAGONAL

```python
DIAGONAL = 'Diagonal'
```

Diagonal (Jacobi) scaling.

#### ILU0

```python
ILU0 = 'ILU0'
```

Incomplete LU without fill-in.

#### ILU1

```python
ILU1 = 'ILU1'
```

Incomplete LU with fill level 1.

#### ILU2

```python
ILU2 = 'ILU2'
```

Incomplete LU with fill level 2.

#### ILU

```python
ILU = 'ILU'
```

Incomplete LU without an explicit fill level.

#### ILUT

```python
ILUT = 'ILUT'
```

Incomplete LU with threshold dropping.

### physics\_config

```python
physics_config(physics: Physics) -> PhysicsConfig
```

The fully-populated, typed solver stack for `physics`, keyed by an exhaustive `match` on the `Physics` enum (a new physics is a static obligation).

### solved\_stack

```python
solved_stack(physics: Physics, frame: Frame) -> PhysicsConfig
```

The preset stack `physics` solves as under `frame`; an axisymmetric form runs in the Axi Symmetric coordinate system.

### prepare\_for\_mpi

```python
prepare_for_mpi(config: PhysicsConfig) -> None
```

Adjust a solver stack for a partitioned (MPI) solve: CoilSolver's serial-only umfpack Direct solve switches to MUMPS, and any other Direct solve raises.

### needs\_halobc

```python
needs_halobc(config: PhysicsConfig) -> bool
```

Whether partitioning must add halo elements (`-halobc`): only the coil stack's boundary-terminated electrodes need them, and elsewhere they skew CalcFields' force lumping.

### convergence\_targets

```python
convergence_targets(config: PhysicsConfig) -> dict[tuple[str, Literal['NS', 'SS']], LoopTargets]
```

The per-(solver, loop) iteration caps and tolerances the stack authors, keyed the way Elmer tags ComputeChange lines.

### bare\_save\_scalars

```python
bare_save_scalars() -> SaveScalarsSolver
```

A SaveScalars with no configured reductions: exports every registered `res:` simulation/component scalar (Component force/torque, field energy) to `scalars.dat`.
