Solver objects¶
Elmer is the solver backend femforge implements today. femforge models every block of Elmer's solver input file (the sif) as a typed pydantic model whose fields are its sif keywords. A preset builds one such stack per physics, the sif writer turns the stack and the mesh groups into a sif, and two parsers read ElmerSolver's output back into typed values. This page covers the model tree, the field-to-keyword mapping, the writer, the checks a solve runs before it writes, and the parsers.
The solver model tree¶
src/femforge/elmer/config groups the models by physics, and its __init__.py re-exports every public name, so callers import from femforge.elmer.config (src/femforge/elmer/config/__init__.py:5-32).
| Subpackage | Holds |
|---|---|
common |
the keyword base, the solver base and its mixins, the Simulation and Constants blocks, the operating point, the generic solver, PhysicsConfig, the presets and the physics config area |
electromagnetism |
3D and axisymmetric magnetostatics, the field post-processor, current conduction, the coil current source, electrostatics |
thermal |
heat conduction |
mechanics |
linear elasticity |
output |
VTU output and scalar reductions |
The hierarchy has one base and three mixins:
- SifKeywordModel: fields carry sif keywords, and
to_elmer()reflects them out (src/femforge/elmer/config/common/keywords.py:15-41)- SolverBase:
kind,equation,procedure,variableand the other keywords every solver block has (src/femforge/elmer/config/common/solver_base.py:172-191) - SimulationSettings and Constants: the
SimulationandConstantsblocks
- SolverBase:
- Mixins a concrete solver composes beside
SolverBase:- LinearSystem: the
Linear System *keywords (src/femforge/elmer/config/common/solver_base.py:138-152) - NonlinearSystem: the
Nonlinear System *keywords (src/femforge/elmer/config/common/solver_base.py:155-163) - SteadyState:
Steady State Convergence Tolerance(src/femforge/elmer/config/common/solver_base.py:166-169)
- LinearSystem: the
This table is generated from the classes at build time:
| Solver | Sif label | Procedure | Mixins | Required material keywords | Conductors only |
|---|---|---|---|---|---|
MagnetoDynamicsSolver |
MagnetoDynamics |
"MagnetoDynamics" "WhitneyAVSolver" |
LinearSystem, NonlinearSystem, SteadyState | Relative Permeability |
no |
MagnetoDynamics2DSolver |
MagnetoDynamics2D |
"MagnetoDynamics2D" "MagnetoDynamics2D" |
LinearSystem, NonlinearSystem, SteadyState | Relative Permeability |
no |
MagnetoDynamicsCalcFieldsSolver |
MagnetoDynamicsCalcFields |
"MagnetoDynamics" "MagnetoDynamicsCalcFields" |
LinearSystem | none | no |
CoilCurrentSolver |
CoilCurrent |
"CoilSolver" "CoilSolver" |
LinearSystem, NonlinearSystem, SteadyState | Electric Conductivity |
yes |
StatCurrentSolver |
StatCurrent |
"StatCurrentSolve" "StatCurrentSolver" |
LinearSystem, NonlinearSystem, SteadyState | Electric Conductivity |
yes |
ElectrostaticsSolver |
Electrostatics |
"StatElecSolve" "StatElecSolver" |
LinearSystem, SteadyState | Relative Permittivity |
no |
HeatSolver |
HeatSolver |
"HeatSolve" "HeatSolver" |
LinearSystem, NonlinearSystem, SteadyState | Heat Conductivity |
no |
StressSolver |
StressSolver |
"StressSolve" "StressSolver" |
LinearSystem, NonlinearSystem, SteadyState | none | no |
ResultOutputSolver |
ResultOutputSolver |
"ResultOutputSolve" "ResultOutputSolver" |
none | none | no |
SaveScalarsSolver |
SaveScalars |
"SaveData" "SaveScalars" |
none | none | no |
GenericSolver |
Generic |
required | LinearSystem, NonlinearSystem, SteadyState | none | no |
Two class variables carry the cross-object rules, and as class variables they never reach the sif. required_material_keys names the material keywords every body the solver acts on must supply (src/femforge/elmer/config/common/solver_base.py:178-179). It is typed as ElmerMaterialKeyword, the same enum a material's properties emit, so the requirement and the emission share one definition. conductor_only restricts the solver's equation to conductor bodies, because a conduction variable solved over dielectrics too puts about 21 decades of conductivity contrast into one matrix: singular for a direct factorization and a stall for preconditioned CG (src/femforge/elmer/config/common/solver_base.py:181-183).
The simulation, constants and operating-point blocks¶
| Model | Becomes | Notes |
|---|---|---|
| SimulationSettings | the Simulation block |
Defaults to a 3D Cartesian steady-state run. Post File and Output File stay unset: setting Post File beside the VTU output solver triggers Elmer's VTU output hack and its log noise, and nothing reads the restart file, which grows to gigabytes (src/femforge/elmer/config/common/simulation.py:62-98). |
| Constants | the Constants block |
Every constant is unset until a preset activates it, and an unset one stays out of the sif (src/femforge/elmer/config/common/constants.py:15-25). |
| OperatingPoint | no block | The temperature material property functions are evaluated at when they are stripped to SI floats (src/femforge/elmer/config/common/operating_point.py:10-25). |
CoordinateSystem.per_radian marks the two symmetric 2D systems, where Elmer integrates per radian and lumped outputs need a 2π factor (src/femforge/elmer/config/common/simulation.py:30-33).
PhysicsConfig and the presets¶
PhysicsConfig is the whole stack: the ordered solvers, the simulation block, the constants and the operating point (src/femforge/elmer/config/common/physics_config.py:47-62). solvers is a list of SolverUnion, a union discriminated on kind, so every concrete field survives a dump and a reload. A plain list[SolverBase] would drop them, and the stack could not ride inside SimulationConfig, the run identity or the manifest (src/femforge/elmer/config/common/physics_config.py:28-44). solver(kind) returns the live instance of one solver class for typed editing (src/femforge/elmer/config/common/physics_config.py:64-69).
physics_config builds the preset for one Physics with an exhaustive match, so a new physics is a type error until it has a preset (src/femforge/elmer/config/common/presets.py:34-139). The table lists each preset as physics_config builds it. magnetostatics_2d is the form magnetostatics solves as under Axisymmetric, never an authored choice, and solved_stack switches the thermal preset to Axi Symmetric under that frame (src/femforge/elmer/config/common/presets.py:24-31).
| Physics | Solvers, in order | Coordinate system | Constants set |
|---|---|---|---|
magnetostatics |
MagnetoDynamicsSolver, MagnetoDynamicsCalcFieldsSolver, SaveScalarsSolver, ResultOutputSolver | Cartesian |
Permeability of Vacuum |
magnetostatics_2d |
MagnetoDynamics2DSolver, MagnetoDynamicsCalcFieldsSolver, SaveScalarsSolver, ResultOutputSolver | Axi Symmetric |
Permeability of Vacuum |
thermal |
HeatSolver, SaveScalarsSolver, ResultOutputSolver | Cartesian |
Stefan Boltzmann |
electrostatics |
ElectrostaticsSolver, ResultOutputSolver | Cartesian |
Permittivity of Vacuum |
current_conduction |
StatCurrentSolver, SaveScalarsSolver, ResultOutputSolver | Cartesian |
none |
joule_heating |
StatCurrentSolver, HeatSolver, SaveScalarsSolver, ResultOutputSolver | Cartesian |
Stefan Boltzmann |
coil_magnetostatics |
CoilCurrentSolver, MagnetoDynamicsSolver, MagnetoDynamicsCalcFieldsSolver, SaveScalarsSolver, ResultOutputSolver | Cartesian |
Permeability of Vacuum |
linear_elasticity |
StressSolver, ResultOutputSolver | Cartesian |
none |
The comments in the presets record why each stack looks the way it does. Three matter most:
COIL_MAGNETOSTATICSchains CoilSolver into WhitneyAV and drives the field solve from CoilSolver's elemental current, because a nodal current source dilutes the drive about twofold on thin conductors (src/femforge/elmer/config/common/presets.py:96-104).MAGNETOSTATICS_2Dpins CalcFields' potential variable and keeps nodal fields on, because turning them off also suppresses the nodal force variable the force reduction reads (src/femforge/elmer/config/common/presets.py:45-55).LINEAR_ELASTICITYhas a complete stack, but its per-body wiring inSimis a stub (src/femforge/elmer/config/common/presets.py:132,src/femforge/elmer/sim.py:473-479).
Three functions adjust or read a whole stack:
| Function | Does | Where |
|---|---|---|
| prepare_for_mpi | switches CoilSolver's serial umfpack solve to MUMPS and raises for any other direct solve, which is unvalidated under partitioning | src/femforge/elmer/config/common/physics_config.py:82-88 |
needs_halobc |
asks for halo elements only when the stack holds CoilSolver: its boundary-terminated electrodes need them, and elsewhere they skew the force lumping | src/femforge/elmer/config/common/physics_config.py:91-93 |
convergence_targets |
the iteration cap and tolerance of each solver's nonlinear and steady-state loop, keyed the way Elmer tags its convergence lines | src/femforge/elmer/config/common/physics_config.py:96-115 |
Conditions and physics shows the user side of editing a stack.
Fields are sif keywords¶
A field names its sif keyword with Map metadata on its Annotated type, as in max_output_level: Annotated[int, Map("Max Output Level")] (src/femforge/elmer/config/common/simulation.py:65). mapped_items reflects the (keyword, value) pairs back out and skips every field whose value is None (src/femforge/common/pydantic.py:34-45). No table beside the models lists the keywords, so a keyword cannot drift from its field.
SifKeywordModel.to_elmer() builds the keyword dict on demand and never stores it (src/femforge/elmer/config/common/keywords.py:27-41):
- A field left at None stays out of the sif, so Elmer keeps its own default for that keyword.
- A list expands to a numbered keyword family, such as
Vector Field 1toVector Field n. extra_keywordsmerges last and wins, so any Elmer keyword the typed fields lack stays authorable without a subclass (src/femforge/elmer/config/common/keywords.py:23-25).- Fixed vocabularies are StrEnums whose values are the exact sif strings, so the value written is the enum member itself (
src/femforge/elmer/config/common/solver_base.py:15-96).
from femforge.elmer.config import MagnetoDynamicsSolver, physics_config
from femforge.elmer.physics import Physics
stack = physics_config(Physics.MAGNETOSTATICS)
solver = stack.solver(MagnetoDynamicsSolver)
solver.linear_system_max_iterations = 2000
solver.extra_keywords["Linear System GCR Restart"] = 50
block = solver.to_elmer()
assert block["Linear System Max Iterations"] == 2000
assert "Variable" not in block
A keyword several solvers share is declared once as a module-level Annotated alias, such as LinearMaxIterationsField, and every solver that overrides its default reuses the alias (src/femforge/elmer/config/common/solver_base.py:99-135). The alias also carries the field description, because a concrete override would reset a description that came from a docstring (src/femforge/elmer/config/common/solver_base.py:143). kind has no Map: it names the solver in the SolverUnion and doubles as the sif Solver block's label (src/femforge/elmer/sim.py:229-234).
The mesh side follows the same rule: GmshOptions.apply reflects the same Map tags into gmsh option setters (src/femforge/meshing/gmsh_options.py:114-133).
Keyword tables¶
The tables below are generated from the models at build time. Rows follow the field order of the model, which is also the order to_elmer writes the keywords in. "unset" means the field defaults to None and the keyword stays out of the sif. A preset can override a default: the thermal stack, for instance, builds its own SaveScalars reductions.
Simulation and constants¶
SimulationSettings
| Field | Sif keyword | Default |
|---|---|---|
max_output_level |
Max Output Level |
5 |
coordinate_system |
Coordinate System |
Cartesian |
coordinate_mapping |
Coordinate Mapping(3) |
1 2 3 |
simulation_type |
Simulation Type |
Steady state |
steady_state_max_iterations |
Steady State Max Iterations |
1 |
output_intervals |
Output Intervals |
1 |
timestepping_method |
Timestepping Method |
BDF |
bdf_order |
BDF Order |
1 |
solver_input_file |
Solver Input File |
case.sif |
post_file |
Post File |
unset |
output_file |
Output File |
unset |
Constants
| Field | Sif keyword | Default |
|---|---|---|
permeability_of_vacuum |
Permeability of Vacuum |
unset |
permittivity_of_vacuum |
Permittivity of Vacuum |
unset |
stefan_boltzmann |
Stefan Boltzmann |
unset |
Electromagnetism¶
MagnetoDynamicsSolver
| Field | Sif keyword | Default |
|---|---|---|
steady_state_convergence_tolerance |
Steady State Convergence Tolerance |
unset |
nonlinear_system_convergence_tolerance |
Nonlinear System Convergence Tolerance |
1e-10 |
nonlinear_system_max_iterations |
Nonlinear System Max Iterations |
50 |
nonlinear_system_newton_after_iterations |
Nonlinear System Newton After Iterations |
3 |
nonlinear_system_newton_after_tolerance |
Nonlinear System Newton After Tolerance |
0.001 |
nonlinear_system_relaxation_factor |
Nonlinear System Relaxation Factor |
1.0 |
nonlinear_system_convergence_measure |
Nonlinear System Convergence Measure |
unset |
linear_system_solver |
Linear System Solver |
Iterative |
linear_system_direct_method |
Linear System Direct Method |
unset |
linear_system_iterative_method |
Linear System Iterative Method |
BiCGStabl |
linear_system_max_iterations |
Linear System Max Iterations |
500 |
linear_system_convergence_tolerance |
Linear System Convergence Tolerance |
1e-08 |
linear_system_preconditioning |
Linear System Preconditioning |
None |
linear_system_residual_output |
Linear System Residual Output |
10 |
linear_system_abort_not_converged |
Linear System Abort Not Converged |
False |
bicgstabl_polynomial_degree |
BiCGStabl polynomial degree |
4 |
equation |
Equation |
MgDyn |
procedure |
Procedure |
"MagnetoDynamics" "WhitneyAVSolver" |
variable |
Variable |
unset |
variable_dofs |
Variable Dofs |
unset |
exec_solver |
Exec Solver |
Always |
stabilize |
Stabilize |
unset |
optimize_bandwidth |
Optimize Bandwidth |
True |
fix_input_current_density |
Fix Input Current Density |
unset |
use_elemental_coilcurrent |
Use Elemental CoilCurrent |
unset |
MagnetoDynamics2DSolver
| Field | Sif keyword | Default |
|---|---|---|
steady_state_convergence_tolerance |
Steady State Convergence Tolerance |
1e-06 |
nonlinear_system_convergence_tolerance |
Nonlinear System Convergence Tolerance |
1e-07 |
nonlinear_system_max_iterations |
Nonlinear System Max Iterations |
50 |
nonlinear_system_newton_after_iterations |
Nonlinear System Newton After Iterations |
unset |
nonlinear_system_newton_after_tolerance |
Nonlinear System Newton After Tolerance |
unset |
nonlinear_system_relaxation_factor |
Nonlinear System Relaxation Factor |
1.0 |
nonlinear_system_convergence_measure |
Nonlinear System Convergence Measure |
solution |
linear_system_solver |
Linear System Solver |
Direct |
linear_system_direct_method |
Linear System Direct Method |
umfpack |
linear_system_iterative_method |
Linear System Iterative Method |
BiCGStab |
linear_system_max_iterations |
Linear System Max Iterations |
unset |
linear_system_convergence_tolerance |
Linear System Convergence Tolerance |
1e-08 |
linear_system_preconditioning |
Linear System Preconditioning |
ILU0 |
linear_system_residual_output |
Linear System Residual Output |
unset |
linear_system_abort_not_converged |
Linear System Abort Not Converged |
unset |
bicgstabl_polynomial_degree |
BiCGStabl polynomial degree |
unset |
equation |
Equation |
MgDyn2D |
procedure |
Procedure |
"MagnetoDynamics2D" "MagnetoDynamics2D" |
variable |
Variable |
Potential |
variable_dofs |
Variable Dofs |
1 |
exec_solver |
Exec Solver |
Always |
stabilize |
Stabilize |
unset |
optimize_bandwidth |
Optimize Bandwidth |
True |
MagnetoDynamicsCalcFieldsSolver
| Field | Sif keyword | Default |
|---|---|---|
linear_system_solver |
Linear System Solver |
Iterative |
linear_system_direct_method |
Linear System Direct Method |
unset |
linear_system_iterative_method |
Linear System Iterative Method |
CG |
linear_system_max_iterations |
Linear System Max Iterations |
500 |
linear_system_convergence_tolerance |
Linear System Convergence Tolerance |
1e-08 |
linear_system_preconditioning |
Linear System Preconditioning |
ILU0 |
linear_system_residual_output |
Linear System Residual Output |
unset |
linear_system_abort_not_converged |
Linear System Abort Not Converged |
unset |
bicgstabl_polynomial_degree |
BiCGStabl polynomial degree |
unset |
equation |
Equation |
MgDynCalc |
procedure |
Procedure |
"MagnetoDynamics" "MagnetoDynamicsCalcFields" |
variable |
Variable |
unset |
variable_dofs |
Variable Dofs |
unset |
exec_solver |
Exec Solver |
Always |
stabilize |
Stabilize |
unset |
optimize_bandwidth |
Optimize Bandwidth |
True |
potential_variable |
Potential Variable |
unset |
average_within_materials |
Average Within Materials |
True |
calculate_magnetic_field_strength |
Calculate Magnetic Field Strength |
True |
calculate_magnetic_flux_density |
Calculate Magnetic Flux Density |
True |
calculate_nodal_forces |
Calculate Nodal Forces |
True |
calculate_nodal_fields |
Calculate Nodal Fields |
False |
calculate_joule_heating |
Calculate Joule Heating |
unset |
calculate_electric_field |
Calculate Electric Field |
unset |
calculate_magnetic_vector_potential |
Calculate Magnetic Vector Potential |
unset |
separate_magnetic_energy |
Separate Magnetic Energy |
unset |
calculate_relative_permeability |
Calculate Relative Permeability |
unset |
calculate_elemental_fields |
Calculate Elemental Fields |
True |
CoilCurrentSolver
| Field | Sif keyword | Default |
|---|---|---|
steady_state_convergence_tolerance |
Steady State Convergence Tolerance |
unset |
nonlinear_system_convergence_tolerance |
Nonlinear System Convergence Tolerance |
unset |
nonlinear_system_max_iterations |
Nonlinear System Max Iterations |
unset |
nonlinear_system_newton_after_iterations |
Nonlinear System Newton After Iterations |
unset |
nonlinear_system_newton_after_tolerance |
Nonlinear System Newton After Tolerance |
unset |
nonlinear_system_relaxation_factor |
Nonlinear System Relaxation Factor |
unset |
nonlinear_system_convergence_measure |
Nonlinear System Convergence Measure |
unset |
linear_system_solver |
Linear System Solver |
Direct |
linear_system_direct_method |
Linear System Direct Method |
umfpack |
linear_system_iterative_method |
Linear System Iterative Method |
BiCGStab |
linear_system_max_iterations |
Linear System Max Iterations |
2000 |
linear_system_convergence_tolerance |
Linear System Convergence Tolerance |
1e-08 |
linear_system_preconditioning |
Linear System Preconditioning |
ILU0 |
linear_system_residual_output |
Linear System Residual Output |
unset |
linear_system_abort_not_converged |
Linear System Abort Not Converged |
unset |
bicgstabl_polynomial_degree |
BiCGStabl polynomial degree |
unset |
equation |
Equation |
CoilSolver |
procedure |
Procedure |
"CoilSolver" "CoilSolver" |
variable |
Variable |
unset |
variable_dofs |
Variable Dofs |
unset |
exec_solver |
Exec Solver |
Before simulation |
stabilize |
Stabilize |
unset |
optimize_bandwidth |
Optimize Bandwidth |
True |
coil_closed |
Coil Closed |
False |
normalize_coil_current |
Normalize Coil Current |
False |
calculate_elemental_fields |
Calculate Elemental Fields |
True |
StatCurrentSolver
| Field | Sif keyword | Default |
|---|---|---|
steady_state_convergence_tolerance |
Steady State Convergence Tolerance |
1e-06 |
nonlinear_system_convergence_tolerance |
Nonlinear System Convergence Tolerance |
1e-08 |
nonlinear_system_max_iterations |
Nonlinear System Max Iterations |
20 |
nonlinear_system_newton_after_iterations |
Nonlinear System Newton After Iterations |
unset |
nonlinear_system_newton_after_tolerance |
Nonlinear System Newton After Tolerance |
unset |
nonlinear_system_relaxation_factor |
Nonlinear System Relaxation Factor |
unset |
nonlinear_system_convergence_measure |
Nonlinear System Convergence Measure |
unset |
linear_system_solver |
Linear System Solver |
Direct |
linear_system_direct_method |
Linear System Direct Method |
umfpack |
linear_system_iterative_method |
Linear System Iterative Method |
CG |
linear_system_max_iterations |
Linear System Max Iterations |
5000 |
linear_system_convergence_tolerance |
Linear System Convergence Tolerance |
1e-06 |
linear_system_preconditioning |
Linear System Preconditioning |
ILU0 |
linear_system_residual_output |
Linear System Residual Output |
unset |
linear_system_abort_not_converged |
Linear System Abort Not Converged |
unset |
bicgstabl_polynomial_degree |
BiCGStabl polynomial degree |
unset |
equation |
Equation |
Stat Current Solver |
procedure |
Procedure |
"StatCurrentSolve" "StatCurrentSolver" |
variable |
Variable |
Potential |
variable_dofs |
Variable Dofs |
1 |
exec_solver |
Exec Solver |
Always |
stabilize |
Stabilize |
True |
optimize_bandwidth |
Optimize Bandwidth |
True |
calculate_volume_current |
Calculate Volume Current |
True |
calculate_joule_heating |
Calculate Joule Heating |
True |
ElectrostaticsSolver
| Field | Sif keyword | Default |
|---|---|---|
steady_state_convergence_tolerance |
Steady State Convergence Tolerance |
1e-05 |
linear_system_solver |
Linear System Solver |
Iterative |
linear_system_direct_method |
Linear System Direct Method |
unset |
linear_system_iterative_method |
Linear System Iterative Method |
BiCGStab |
linear_system_max_iterations |
Linear System Max Iterations |
500 |
linear_system_convergence_tolerance |
Linear System Convergence Tolerance |
1e-10 |
linear_system_preconditioning |
Linear System Preconditioning |
ILU0 |
linear_system_residual_output |
Linear System Residual Output |
unset |
linear_system_abort_not_converged |
Linear System Abort Not Converged |
unset |
bicgstabl_polynomial_degree |
BiCGStabl polynomial degree |
unset |
equation |
Equation |
Electrostatics |
procedure |
Procedure |
"StatElecSolve" "StatElecSolver" |
variable |
Variable |
Potential |
variable_dofs |
Variable Dofs |
unset |
exec_solver |
Exec Solver |
Always |
stabilize |
Stabilize |
True |
optimize_bandwidth |
Optimize Bandwidth |
unset |
calculate_electric_field |
Calculate Electric Field |
True |
Thermal¶
HeatSolver
| Field | Sif keyword | Default |
|---|---|---|
steady_state_convergence_tolerance |
Steady State Convergence Tolerance |
1e-06 |
nonlinear_system_convergence_tolerance |
Nonlinear System Convergence Tolerance |
1e-06 |
nonlinear_system_max_iterations |
Nonlinear System Max Iterations |
50 |
nonlinear_system_newton_after_iterations |
Nonlinear System Newton After Iterations |
unset |
nonlinear_system_newton_after_tolerance |
Nonlinear System Newton After Tolerance |
unset |
nonlinear_system_relaxation_factor |
Nonlinear System Relaxation Factor |
unset |
nonlinear_system_convergence_measure |
Nonlinear System Convergence Measure |
unset |
linear_system_solver |
Linear System Solver |
Direct |
linear_system_direct_method |
Linear System Direct Method |
umfpack |
linear_system_iterative_method |
Linear System Iterative Method |
BiCGStab |
linear_system_max_iterations |
Linear System Max Iterations |
1000 |
linear_system_convergence_tolerance |
Linear System Convergence Tolerance |
1e-08 |
linear_system_preconditioning |
Linear System Preconditioning |
ILU |
linear_system_residual_output |
Linear System Residual Output |
unset |
linear_system_abort_not_converged |
Linear System Abort Not Converged |
unset |
bicgstabl_polynomial_degree |
BiCGStabl polynomial degree |
unset |
equation |
Equation |
HeatSolver |
procedure |
Procedure |
"HeatSolve" "HeatSolver" |
variable |
Variable |
Temperature |
variable_dofs |
Variable Dofs |
1 |
exec_solver |
Exec Solver |
Always |
stabilize |
Stabilize |
True |
optimize_bandwidth |
Optimize Bandwidth |
True |
Mechanics¶
StressSolver
| Field | Sif keyword | Default |
|---|---|---|
steady_state_convergence_tolerance |
Steady State Convergence Tolerance |
1e-06 |
nonlinear_system_convergence_tolerance |
Nonlinear System Convergence Tolerance |
1e-07 |
nonlinear_system_max_iterations |
Nonlinear System Max Iterations |
1 |
nonlinear_system_newton_after_iterations |
Nonlinear System Newton After Iterations |
unset |
nonlinear_system_newton_after_tolerance |
Nonlinear System Newton After Tolerance |
unset |
nonlinear_system_relaxation_factor |
Nonlinear System Relaxation Factor |
unset |
nonlinear_system_convergence_measure |
Nonlinear System Convergence Measure |
unset |
linear_system_solver |
Linear System Solver |
Iterative |
linear_system_direct_method |
Linear System Direct Method |
unset |
linear_system_iterative_method |
Linear System Iterative Method |
BiCGStab |
linear_system_max_iterations |
Linear System Max Iterations |
5000 |
linear_system_convergence_tolerance |
Linear System Convergence Tolerance |
1e-08 |
linear_system_preconditioning |
Linear System Preconditioning |
ILU0 |
linear_system_residual_output |
Linear System Residual Output |
unset |
linear_system_abort_not_converged |
Linear System Abort Not Converged |
unset |
bicgstabl_polynomial_degree |
BiCGStabl polynomial degree |
unset |
equation |
Equation |
Linear elasticity |
procedure |
Procedure |
"StressSolve" "StressSolver" |
variable |
Variable |
Displacement |
variable_dofs |
Variable Dofs |
3 |
exec_solver |
Exec Solver |
Always |
stabilize |
Stabilize |
unset |
optimize_bandwidth |
Optimize Bandwidth |
True |
calculate_stresses |
Calculate Stresses |
True |
Output¶
ResultOutputSolver
| Field | Sif keyword | Default |
|---|---|---|
equation |
Equation |
ResultOutputSolver |
procedure |
Procedure |
"ResultOutputSolve" "ResultOutputSolver" |
variable |
Variable |
unset |
variable_dofs |
Variable Dofs |
unset |
exec_solver |
Exec Solver |
After timestep |
stabilize |
Stabilize |
unset |
optimize_bandwidth |
Optimize Bandwidth |
unset |
vtu_format |
VTU Format |
True |
output_file_name |
Output File Name |
File "case" |
save_geometry_ids |
Save Geometry Ids |
True |
saved_scalar_fields |
Scalar Field |
unset |
saved_vector_fields |
Vector Field |
unset |
discontinuous_bodies |
Discontinuous Bodies |
unset |
SaveScalarsSolver
| Field | Sif keyword | Default |
|---|---|---|
equation |
Equation |
SaveScalars |
procedure |
Procedure |
"SaveData" "SaveScalars" |
variable |
Variable |
unset |
variable_dofs |
Variable Dofs |
unset |
exec_solver |
Exec Solver |
After timestep |
stabilize |
Stabilize |
unset |
optimize_bandwidth |
Optimize Bandwidth |
unset |
save_component_results |
Save Component Results |
unset |
parallel_reduce |
Parallel Reduce |
unset |
filename |
Filename |
"scalars.dat" |
variable_1 |
Variable 1 |
Potential |
operator_1 |
Operator 1 |
diffusive flux |
coefficient_1 |
Coefficient 1 |
Electric Conductivity |
variable_2 |
Variable 2 |
Joule Heating |
operator_2 |
Operator 2 |
int |
variable_3 |
Variable 3 |
unset |
operator_3 |
Operator 3 |
unset |
Generic¶
GenericSolver is the escape hatch for a solver the hierarchy does not model: set equation and procedure, and author the rest through extra_keywords and the shared typed knobs (src/femforge/elmer/config/common/generic.py:10-17).
GenericSolver
| Field | Sif keyword | Default |
|---|---|---|
steady_state_convergence_tolerance |
Steady State Convergence Tolerance |
unset |
nonlinear_system_convergence_tolerance |
Nonlinear System Convergence Tolerance |
unset |
nonlinear_system_max_iterations |
Nonlinear System Max Iterations |
unset |
nonlinear_system_newton_after_iterations |
Nonlinear System Newton After Iterations |
unset |
nonlinear_system_newton_after_tolerance |
Nonlinear System Newton After Tolerance |
unset |
nonlinear_system_relaxation_factor |
Nonlinear System Relaxation Factor |
unset |
nonlinear_system_convergence_measure |
Nonlinear System Convergence Measure |
unset |
linear_system_solver |
Linear System Solver |
Iterative |
linear_system_direct_method |
Linear System Direct Method |
unset |
linear_system_iterative_method |
Linear System Iterative Method |
BiCGStab |
linear_system_max_iterations |
Linear System Max Iterations |
unset |
linear_system_convergence_tolerance |
Linear System Convergence Tolerance |
1e-08 |
linear_system_preconditioning |
Linear System Preconditioning |
ILU0 |
linear_system_residual_output |
Linear System Residual Output |
unset |
linear_system_abort_not_converged |
Linear System Abort Not Converged |
unset |
bicgstabl_polynomial_degree |
BiCGStabl polynomial degree |
unset |
equation |
Equation |
required |
procedure |
Procedure |
required |
variable |
Variable |
unset |
variable_dofs |
Variable Dofs |
unset |
exec_solver |
Exec Solver |
Always |
stabilize |
Stabilize |
unset |
optimize_bandwidth |
Optimize Bandwidth |
unset |
The sif writer¶
src/femforge/elmer/sif.py follows pyelmer's public API shape as an independent implementation (NOTICE:6-7). It writes the same text pyelmer writes, always with LF line endings (src/femforge/elmer/sif.py:1). tests/elmer/test_sif_writer.py compares its output with golden files pyelmer wrote, and checks the LF endings and the registry semantics below (tests/elmer/test_sif_writer.py:1).
| Behavior | Why | Where |
|---|---|---|
| Constructing a block under an existing name with the same arguments returns the registered block; different arguments raise | a block can be requested from several places, and a name clash is a wiring error | src/femforge/elmer/sif.py:49-58 |
Blocks initialize in __new__, with no __init__ |
Python must not re-run an initializer on a block the registry returns, which would reset its id and references | src/femforge/elmer/sif.py:61-62 |
| Section ids are assigned 1 to N in insertion order at write time | a block references another by id, and the id exists only once every block is registered | src/femforge/elmer/sif.py:432-436 |
| The document writes Header, Simulation, Constants, then Equation, Solver, Material, Body, Boundary Condition, Body Force and Component sections | the reference layout | src/femforge/elmer/sif.py:393-412 |
boundaries.yml and components.yml map each assigned id to its block name |
the parsers join Elmer's id-named scalar columns back to names | src/femforge/elmer/sif.py:420-430 |
Sim and generate()¶
Sim turns the mesh groups and a solver stack into a sif in two phases (src/femforge/elmer/sim.py:59-63):
- The constructor is cheap. It maps the physics through the frame, takes the given stack or the preset, and checks the stack and every group's conditions against the frame. It builds no sif objects (
src/femforge/elmer/sim.py:65-104). generate()builds the sif graph from the possibly edited stack: the simulation and constants blocks, then solvers, equations, materials, bodies and boundaries in dependency order, thenvalidate()(src/femforge/elmer/sim.py:108-129).write(dir)writes the start-info file, the sif and the two id maps, and requiresgenerate()first (src/femforge/elmer/sim.py:483-500).
The driver's build_sim stage constructs Sim from the resolved stack and applies prepare_for_mpi for a partitioned solve; write_sif calls generate().write() (src/femforge/run/driver.py:636-662). Sim takes no config object: its bodies come from the mesh groups, and it owns only its solver stack.
Bodies, boundaries and components¶
| Source | sif block | Where |
|---|---|---|
| each physical group | one Body targeting the group's id |
src/femforge/elmer/sim.py:305-306 |
| the stack | Main Equation with every solver, plus a second equation without the conductor_only solvers when a non-conductor body exists |
src/femforge/elmer/sim.py:236-256 |
| a coil stack | CoilSolver's equation covers only the bodies with a CoilDrive, because CoilSolver fails on a body outside every Component; WhitneyAV and CalcFields stay in both equations, because splitting them corrupts CalcFields' degree-of-freedom mapping |
src/femforge/elmer/sim.py:246-251 |
| each distinct material | one shared Material |
src/femforge/elmer/sim.py:263-287 |
| a magnet body | a Material of its own carrying the Magnetization vector, because WhitneyAV reads it from the material; a missing remanence or direction leaves a commented marker for validate() to raise on |
src/femforge/elmer/sim.py:445-471 |
| body-target conditions | one Body Force per body, without Magnetization, CoilDrive and ForceComponent |
src/femforge/elmer/sim.py:329-353 |
each CoilDrive instance |
one coil Component over every body that carries it |
src/femforge/elmer/sim.py:296-297, src/femforge/elmer/sim.py:355-357 |
each ForceComponent instance, and each magnet without one |
one force Component; the automatic one is named after the group |
src/femforge/elmer/sim.py:299-303, src/femforge/elmer/sim.py:359-365 |
| boundary-target conditions of the bordering bodies | one merged Boundary Condition per surface group, because Elmer gives each boundary element one section and silently ignores a second |
src/femforge/elmer/sim.py:377-397 |
| a magnetic stack's external shell | the far field AV {e} = 0, or Potential = 0 in 2D; the 2D symmetry axis gets the exact Potential = 0 |
src/femforge/elmer/sim.py:427-439 |
A component groups bodies by condition identity, so one CoilDrive authored on a phase's tag reaches every body of that phase as one Component.
Checks before the solve¶
validate() runs at the end of generate() and covers what pydantic cannot see across objects (src/femforge/elmer/sim.py:133-225). It collects every error into one ValueError and issues each smell as a SolverConfigWarning (src/femforge/elmer/sim.py:46-47).
| Check | Outcome | Where |
|---|---|---|
Each body's material emits every keyword its solvers require: the keys of every solver, plus the keys of conductor_only solvers on conductors; an equivalent keyword counts, such as an H-B Curve for Relative Permeability |
error | src/femforge/elmer/sim.py:143-160 |
| No emitted material value is still a pint quantity | error | src/femforge/elmer/sim.py:162-167 |
Every magnet under a magnet-driven physics has a Magnetization with a direction |
error | src/femforge/elmer/sim.py:169-175 |
A coil solve has at least one CoilDrive |
warning | src/femforge/elmer/sim.py:177-184 |
| A physics that needs a boundary condition to be well posed resolved one | warning | src/femforge/elmer/sim.py:26-28, src/femforge/elmer/sim.py:186-193 |
| A force Component has CalcFields with nodal forces on, and a SaveScalars to export it | warning | src/femforge/elmer/sim.py:195-222 |
The constructor raises earlier for an unknown physics and for a stack or condition that cannot solve the frame (src/femforge/elmer/sim.py:86-102). Before a partitioned solve, the driver also warns when a coil stack computes nodal forces, because the halo elements it then partitions with corrupt the Component force lumping (src/femforge/run/driver.py:671-680).
The solver-log parser¶
SolverLogParser.feed turns one ElmerSolver output line into a typed event, or None (src/femforge/elmer/solver_log.py:156-188):
| Event | From | Where |
|---|---|---|
ConvergenceSample |
a ComputeChange line: solver, loop, iteration, norm, relative change |
src/femforge/elmer/solver_log.py:26-46 |
LinearResidual |
a linear iteration line, only when the parser was built with capture_linear=True |
src/femforge/elmer/solver_log.py:49-60 |
SolverMessage |
a WARNING or ERROR line |
src/femforge/elmer/solver_log.py:63-68 |
SolveFinished |
the SOLVER TOTAL TIME line |
src/femforge/elmer/solver_log.py:71-79 |
The convergence line's solver tag is the sif Equation name lowercased, and some Elmer builds omit it (src/femforge/elmer/solver_log.py:15-16), so both sides normalize names with normalize_solver_key (src/femforge/elmer/solver_log.py:100-102).
solve_case runs ElmerSolver through a backend and feeds every streamed line to the parser (src/femforge/elmer/runtime/solve.py:30-108). It writes each convergence sample to convergence.jsonl as one flushed line, logs it through a ConvergenceRenderer against the stack's targets, and logs each warning and error at its own level. It builds one renderer per solve, so one solve's progress baseline never leaks into the next (src/femforge/elmer/runtime/solve.py:57-94). The driver writes the same targets to convergence_targets.json immediately before the solve (src/femforge/run/driver.py:682-692); Telemetry covers how that file is read back.
After the solve, parse_result reads the finished bundle into a Result (src/femforge/run/parse.py:177-232):
- Status comes from
elmersolver.log:errorwhen the log is missing, any line holdsERRORor the run never printedALL DONE;divergedon a non-convergence marker or a NaN;okotherwise (src/femforge/run/parse.py:126-174). - Scalars come from each
.datand its.dat.namessidecar, reading the last row, because a sweep or a transient run appends rows (src/femforge/run/parse.py:43-70). - A partitioned solve takes rank 0. It writes per-rank
.dat.<rank>files, and the exported values are already globally reduced, so rank 0 stands in for the whole and the parser never sums. Disagreeing ranks log a warning, because they mean a reduction without a global operator (src/femforge/run/parse.py:90-123). - Forces read only the global CalcFields sums, so per-Component triples never fold into the total. The per-Component values join
components.yml, and anAxisymmetricframe revolves every force and torque to the full revolution (src/femforge/run/parse.py:186-217).
Results covers the user side.
The electrical flux mapping¶
A current-conduction run measures each phase's terminal current with Elmer's own flux integral, and src/femforge/run/electrical.py maps the result back to names:
- FixedPotential emits
Save Scalars = Logical Truebeside its potential, which marks the electrode for boundary integration (src/femforge/physical/conditions.py:292-297). - SaveScalars' default first reduction is the
diffusive fluxofPotentialweighted byElectric Conductivity, which integrates σ ∂φ/∂n over each marked boundary to a current (src/femforge/elmer/config/output/save_scalars.py:34-41). Its second reduction integrates the Joule loss density to a power P, a cross-check through R = ΔV²/P (src/femforge/elmer/config/output/save_scalars.py:43-47). - Elmer names each flux column by its boundary index, as in
diffusive flux: potential over bc 3; boundary_fluxes reads the index out of the column name (src/femforge/run/electrical.py:10-21). - read_boundary_names reads the index-to-name map
Simwrote asboundaries.yml(src/femforge/run/electrical.py:24-31). The file is flatint: nameYAML, and the reader splits each line at its first colon. - electrode_currents joins the two into a current per named boundary block (
src/femforge/run/electrical.py:34-36). - phase_currents keeps, per
PHASE<n>index in the names, the current of largest magnitude, because Elmer integrates an internal electrode's flux from one side, and the pad side can read about zero (src/femforge/run/electrical.py:43-54). - resistance divides ΔV by that current and raises on a zero current (
src/femforge/run/electrical.py:57-61).
Run.electrode_currents and Run.phase_currents read a bundle through these functions (src/femforge/run/inspect.py:184-189).