|
FhSim
3.1.0
Marine systems simulation
|
The <SIMULATION> section controls how the simulation is executed: the integration method, timing, tolerances, and state serialisation. It also supports alternative state providers (replay, network).
<SIMULATION>. If you are here because a simulation is slow, produces NaN, or needs its Jacobian checked, jump to the diagnostics cookbook at the end of the page.<INTEGRATION> is accepted as a deprecated alias for <SIMULATION> (same child elements and attributes). If both are present, FhSim reports an error. Rename <INTEGRATION> to <SIMULATION> — the legacy name will be removed in a future release.The most common configuration uses the <Integrator> element to run an ODE integration:
| Element | Parent | Required | Description |
|---|---|---|---|
<Timing> | <SIMULATION> | Yes | Simulation time span. Not enforced by the parser, but an absent <Timing> leaves TEnd at 0. |
<Integrator> | <SIMULATION> | Yes | Method selection and integrator settings. |
<StepControl> | <Integrator> | No | Step-size bounds, tolerances, order limits. |
<LinearSolver> | <Integrator> | No | Linear solver for implicit methods. |
<Jacobian> | <Integrator> | No | Jacobian matrix type/assembly strategy. |
<Diagnostics> | <Integrator> | No | Unified diagnostics configuration. |
<SundialsExtra> | <Integrator> | No | Expert passthrough for Sundials API calls. |
| Attribute | Default | Description |
|---|---|---|
TStart | 0.0 | Simulation start time (seconds). |
TEnd | 0.0 | Simulation end time (seconds). Omitting it runs a simulation of zero length. |
$Variable substitution does not reach <Timing>. TEnd="$Duration" is not rejected — the literal text is handed to the numeric conversion, which yields 0, and the run ends immediately. The same holds for every <Integrator> child element. See Where substitution applies.| Attribute | Applies to | Default | Description |
|---|---|---|---|
Method | All | — (required) | Integration method name (see table below). |
NumCores | Engine | "1" | Thread count: "1", "perCpuCore", "perSimObject", or a positive integer. |
InitialStatesFile | All | "" | Binary file to load initial state (warm-start / chaining). |
FinalStatesFile | All | "" | Binary file to save final state (enables run chaining). |
MaxOrder is a <StepControl> attribute (see below); on <Integrator> it is rejected.
$Variable references inside <SIMULATION>. No <Integrator> child element does, and neither does <Timing>.All methods are selected via Method="<name>".
Method is required and has no fallback: an <Integrator> without it stops the run with *"`<Integrator>` element is missing the required 'Method'
attribute."* The recommendation below is a starting point, not a default that applies when the attribute is omitted.| Method | Type | Order | Description |
|---|---|---|---|
RK45_i | Explicit | 4(5) | Runge-Kutta Cash-Karp, adaptive step. Recommended starting choice for a non-stiff system. |
DOPRI54_i | Explicit | 5(4) | Dormand-Prince, adaptive step. Higher accuracy. |
RKF45_i | Explicit | 4(5) | Runge-Kutta-Fehlberg, adaptive. Classic embedded pair. |
BS23_i | Explicit | 2(3) | Bogacki-Shampine, adaptive. Good for mild problems. |
Euler_i | Explicit | 1 | Forward Euler, fixed step. Simplest integrator. |
Heun_i | Explicit | 2 | Heun's method (improved Euler), adaptive. |
BackwardEuler_i | Implicit | 1 | Backward (implicit) Euler, fixed step. L-stable. |
StableSolver_i | Explicit | 1 | Multi-stage stability-enhanced explicit scheme, fixed step. Takes no <LinearSolver>. |
| Method | Type | Infrastructure | Description |
|---|---|---|---|
BDF | Implicit | CVODE | Backward Differentiation Formula (orders 1–5). Best for stiff systems. |
Adams | Explicit | CVODE | Adams-Moulton multi-step. Best for non-stiff, smooth solutions. |
DIRK | Implicit | ARKODE | Diagonally Implicit Runge-Kutta. Configurable Butcher tables. |
ERK | Explicit | ARKODE | Explicit Runge-Kutta from ARKODE. High-order methods. |
| Scenario | Recommended |
|---|---|
| General-purpose, non-stiff | RK45_i (Engine) or ERK (Sundials) |
| High accuracy, non-stiff | DOPRI54_i or ERK with tight tolerances |
| Stiff system, production use | BDF (Sundials) |
| Stiff system, moderate stiffness | DIRK (Sundials) |
| Fixed-step, simplest possible | Euler_i or BackwardEuler_i |
| Real-time with strict step budget | BackwardEuler_i or StableSolver_i with fixed Step |
| Large sparse stiff systems | BDF + <LinearSolver Type="SPGMR"/> + <Jacobian Type="sparse"/> |
Controls step-size bounds, error tolerances, and solver order. All attributes are optional.
| Attribute | Default | Description |
|---|---|---|
AbsTol | 1e-4 | Absolute error tolerance (adaptive methods). |
RelTol | 1e-4 | Relative error tolerance (adaptive methods). |
StepMax | Engine: 10.0; Sundials: 0 (no limit) | Maximum internal step size. |
StepMin | Engine: 1e-60; Sundials: 0 (no limit) | Minimum internal step size. |
Step | 0 (adaptive) | Fixed step size. If > 0, disables adaptive stepping. |
Step0 | 0 (auto) | Initial step size suggestion (Sundials only). |
MaxSteps | 0 (solver default) | Max internal steps between output points (Sundials). |
MaxOrder | 0 (solver default) | Maximum BDF/Adams order, 1–5. CVODES only. |
StepMax = 10.0 and StepMin = 1e-60 when <StepControl> says nothing; the Sundials methods use 0, which means "impose
no limit". An Engine run therefore never takes a step longer than 10 s even with no <StepControl> element at all.AbsTol, RelTol, StepMax, StepMin and Step are applied only when given as a positive value. Writing StepMax="0" does not remove the Engine clamp — the attribute is ignored and the default stands.MaxOrder reaches CVODES only, so it affects BDF and Adams. DIRK and ERK run on ARKODE, which never sees it; the Engine methods have no order setting. A value outside 1–5 is an error.Step > 0 (fixed-step mode), AbsTol, RelTol, StepMax, and StepMin are ignored. Fixed-step mode is mandatory for BackwardEuler_i, Euler_i, and StableSolver_i.Configures the linear solver used inside Newton iterations of implicit methods. Ignored for explicit methods.
auto actually picks, when to override it, and the structure each named solver expects. SUNDIALS and linear-solver configuration has the matching Sundials-side reference and worked <Integrator> snippets.| Attribute | Default | Description |
|---|---|---|
Type | "auto" | Solver type (see table below). |
MaxIter | 50 | Max iterations (iterative solvers). |
Tol | 1e-10 | Convergence tolerance (iterative solvers). |
Restart | 50 | GMRES restart count. |
BandUpper | 0 (auto) | Upper bandwidth (band type). |
BandLower | 0 (auto) | Lower bandwidth (band type). |
BorderMaxSize | 64 | Max number of dense border (global-coupling) states accepted by the bordered_schur solver (named or auto-selected). |
BorderStates | (auto) | Comma-separated global state indices to force as border states for the bordered_schur solver; empty = auto-detect. |
Engine linear solver types:
| Type | Description |
|---|---|
auto | Automatic selection based on system structure and size. |
dense | Direct dense LU. Best for small systems (< 100 states). |
sparse | Sparse LU with METIS ordering. Best for large sparse systems. |
band | Banded LU. Best for chain-coupled systems. |
block_tridiagonal | Thomas algorithm for block-tridiagonal structure. |
near_tridiagonal | Woodbury extension for near-tridiagonal systems. |
bordered_schur | Banded interior plus a few dense global-coupling "border" states, solved through a Schur complement. Tuned with BorderMaxSize / BorderStates. |
component | Direct solve per connected component of the SimObject coupling graph, with structured local solvers. |
iterative | BiCGSTAB with block-diagonal preconditioner. |
iterative_gmres | GMRES with block-diagonal preconditioner. |
Note:
autopicksbordered_schuritself when the Jacobian structure matches; naming it forces that path and fails clearly when the structure does not fit.
| Attribute | Default | Description |
|---|---|---|
Type | "SPGMR" | Linear solver: "DENSE", "BAND", or "SPGMR". |
Preconditioner | "auto" | SPGMR only. Model-side preconditioner, using the Engine linear solver names above (auto, component, block_tridiagonal, near_tridiagonal, iterative, iterative_gmres, ...). |
NonlinearSolver | "newton" | Nonlinear solver: "newton" or "fixed_point". |
FixedPointAccel | 0 | fixed_point only. Number of Anderson acceleration vectors (0 = plain fixed-point iteration). |
FixedPointDamping | 1.0 | fixed_point only. Damping factor in (0, 1]; 1.0 disables damping. |
BandUpper | 0 (auto) | Upper bandwidth (BAND type). |
BandLower | 0 (auto) | Lower bandwidth (BAND type). |
SUNDIALS and linear-solver configuration lists which <Jacobian Type> / <LinearSolver Type> pairs are valid and which are rejected at startup.
| Attribute | Applies to | Default | Description |
|---|---|---|---|
Type | Sundials | "auto" | Jacobian matrix storage: "auto" (dense), "dense", "sparse", "band", "none". Engine methods derive storage from the linear solver. |
Policy | Sundials | "auto" | How Jacobians are computed: "auto", "analytical", "hybrid", "numeric". See Implicit Integration with Analytical Jacobians. |
SparsityImageSize | All | 2000 | Pixel-size cap for the sparsity image. A system with more states than this is written at one pixel per block of states rather than one per state. |
SparsityImageSize is not the switch that turns the image on. The image is written by the <JacobianSparsityDump> diagnostic; this attribute only bounds its size. Setting SparsityImageSize="512" without a <Diagnostics><Settings><JacobianSparsityDump/> writes nothing. ="0" is ignored — only a value greater than zero is applied — so the way to suppress the image is <JacobianSparsityDump SparsityImageSize="0"/>, which does accept zero.Type="none" disables Jacobian assembly — the solver uses a matrix-free approach. Only valid with <LinearSolver Type="SPGMR"/>.See Implicit Integration with Analytical Jacobians for details on analytical Jacobian implementation, SUNDIALS and linear-solver configuration for the storage formats and the valid Type/LinearSolver combinations, and Jacobian diagnostics and troubleshooting for checking a Jacobian that looks wrong.
Unified diagnostics for Engine and Sundials methods. Diagnostics are disabled by default; the element must be present to activate them.
<JacobianVerify> and <JacobianSparsityDump> print, the files they write, and how to read the result.| Attribute | Default | Description |
|---|---|---|
Enabled | True | Set to False to parse but suppress all diagnostics output. |
JsonOutput | diagnostics.json | Path for structured JSON output written incrementally during the run. Pass JsonOutput="" to disable JSON output. |
<Diagnostics> adds overhead at every accepted step. BlockingState, LogStates, and JacobianVerify each perform additional computation and/or I/O. For production or benchmarking runs, remove the <Diagnostics> element entirely.The performance cost scales with:
EveryNSteps="1", StepBelow that fires often) incur proportionally more overhead.JacobianVerify** — performs a full finite-difference Jacobian evaluation on each trigger, making it expensive for large models. It already fires only once by default; do not widen that with <Triggers EveryNSteps> unless you mean to.LogStates** — buffers all state values in memory; memory use grows proportionally to the number of accepted steps.JsonOutput writes to disk at every accepted step; disable with JsonOutput="" if I/O is a bottleneck.An absent <Diagnostics> element disables all six diagnostics. A present one enables all six — each <Settings> child is an opportunity to configure or switch off a diagnostic that is already on, not a switch that turns one on. <Diagnostics Enabled="False"/> is parsed and then suppressed wholesale.
There is no single "off" default. Each diagnostic has its own gates, chosen so that a bare <Diagnostics/> is useful without tuning:
| Diagnostic | Default gates | Default settings |
|---|---|---|
<BlockingState> | StepBelow = 10 × <StepControl StepMin>, MinInterval = (TEnd − TStart) / 100 | Count="3" |
<JacobianVerify> | Once = true | TopSimObjects="5", TopPorts="5" |
<JacobianSparsityDump> | Once = true (fires at initialisation) | Mode="block", SparsityImageSize="2000" |
<LogStates> | EveryNSteps = 100 | — |
<SundialsStepInfo> | MinInterval = (TEnd − TStart) / 100 | — (no-op for Engine methods) |
<StatePrintOnNanInf> | fires on NaN/Inf, once | — |
BlockingState threshold is derived from the effective StepMin. An Engine run that leaves <StepControl StepMin> unset gets 10 × 1e-60, which no real step ever falls below, so the diagnostic stays quiet. Set <StepControl StepMin> to a realistic floor, or give <Triggers StepBelow> an explicit value.<Triggers> does not switch diagnostics on — it overrides the defaults in the table above. An attribute you omit leaves each diagnostic's own default in place; an attribute you give replaces it for every diagnostic that reads it.
| Attribute | Overrides the default for | Description |
|---|---|---|
StepBelow | BlockingState only | Emit when the step size falls below this threshold. |
MinInterval | all but StatePrintOnNanInf | Minimum sim-time interval between emissions. |
EveryNSteps | all but StatePrintOnNanInf | Emit every N accepted steps. |
Once | all but StatePrintOnNanInf | Emit only once, then disable. |
<StatePrintOnNanInf> ignores <Triggers> entirely. It always fires on the first NaN/Inf and only then, whatever the element says. <Triggers StepBelow> likewise reaches BlockingState alone — it does not gate JacobianVerify, LogStates or the rest. A negative or unparsable value is rejected with a warning and the default is kept.Every child takes Enabled plus the three sink attributes described below, in addition to its own settings:
| Element | Own attributes | Description |
|---|---|---|
<BlockingState/> | Count | Print top-N states contributing to step rejection. |
<LogStates/> | — | Record accepted states in internal buffers. |
<JacobianVerify/> | TopSimObjects, TopPorts | Compare analytical Jacobian against numerical. |
<JacobianSparsityDump/> | Mode (off/block/full), SparsityImageSize | Write Jacobian sparsity diagnostics and a PNG image. |
<SundialsStepInfo/> | — | Print per-step Sundials diagnostics. |
<StatePrintOnNanInf/> | — | Print state values when NaN/Inf is detected. |
| Common attribute | Default | Description |
|---|---|---|
Enabled | true | false switches this one diagnostic off while leaving the others on. |
Console | true | Mirror this diagnostic to stdout (ANSI-coloured on a terminal). |
Log | true | Mirror it to the main simulation log file. |
Sidecar | true | Write it to a separate plain-text file — see below. |
<JacobianSparsityDump Mode="off"/> writes nothing at all, and <JacobianSparsityDump SparsityImageSize="0"/> keeps the textual dump but suppresses the PNG. A SparsityImageSize given here takes precedence over the one on <Jacobian>.
Each diagnostic writes to three independent channels, all on by default. This is where the diag_*.log files that appear next to a run come from.
Sidecar value | Effect |
|---|---|
on / true / 1 (default) | Append to the auto-named file diag_<Name>.log. |
off / false / 0 | Write no sidecar file. |
| anything else | Treated as a filename and used verbatim. |
The auto-named files are diag_BlockingState.log, diag_JacobianVerify.log, diag_JacobianSparsityDump.log, diag_LogStates.log, diag_SundialsStepInfo.log and diag_StatePrintOnNanInf.log, resolved like any other relative path (see Path resolution).
Sidecar="off" on the diagnostics you do not need to keep, or clear the files between runs.The following example shows an <Integrator> element with every child element present and every <Diagnostics> sub-element configured. In practice you would only include the sub-elements you actually need.
EveryNSteps belongs on the <Triggers> element, not on <Diagnostics> itself. An attribute placed directly on <Diagnostics> is silently ignored and the diagnostics fall back to their defaults.The JSON output written by JsonOutput can be visualised in the browser using diagnostics.html, which is installed next to the executable — so in the directory the simulation writes the JSON file into. That is playpen/bin/diagnostics.html in the playpen layout, or share/fhsim/diagnostics.html in a plain install.
Usage:
JsonOutput set: diagnostics.html in a web browser.diag.json file via the file picker in the viewer.Alternatively, fhsim_plot diag.json writes a copy of the viewer with the snapshot stream baked in and opens it for you.
The viewer renders time-series plots of step size, per-state error estimates, and any other structured data recorded in the JSON snapshot stream.
https://cdn.plot.ly. On a machine without internet access, put plotly-2.35.2.min.js next to it and that copy is used instead.Expert passthrough for SUNDIALS optional-input API calls not covered by the structured schema.
| Attribute | Example | Description |
|---|---|---|
ARKodeSetTableNum | "SDIRK_2_1_2" | Select a specific DIRK Butcher table. |
ARKodeSetFixedStep | "0.005" | Force fixed-step in ARKODE. |
CVodeSetMaxStep | "0.1" | Override maximum step size in CVODE. |
ERKStepSetTableNum | "HEUN_EULER_2_1_2" | Select explicit RK table for ERK. |
See the SUNDIALS documentation for the full list of available API function names.
linear_solver and jacobian_policy are no longer accepted here. Use <LinearSolver Preconditioner="..."/> and <Jacobian Policy="..."/> instead; the parser reports an error naming the replacement.To chain multiple simulation runs — save the end state of one run and resume from it in the next — use FinalStatesFile and InitialStatesFile:
Run 1 — save final state:
Run 2 — resume from saved state:
(1 + N) × 8 bytes: the first double is the simulation time; the following N doubles are the ODE state vector. The Engine backend also writes FinalStatesFile on SIGINT/SIGTERM (Ctrl-C), making it safe for long-running interruptible runs.InitialStatesFile also sets the simulation start time from the time stored in the file, overriding <Timing TStart>. A file whose size does not match (1 + N) × 8 for the current model stops the run with an error reporting both sizes.Both state-file paths honour the environment variable BROOM_INPUT_DIR, which lets a batch harness keep its chaining files outside the working directory without editing the XML. When the variable is set and non-empty:
InitialStatesFile is used as written if that path already exists; otherwise $BROOM_INPUT_DIR is prepended to it.FinalStatesFile always has $BROOM_INPUT_DIR prepended, whether or not the bare path exists.When the variable is unset or empty both paths are used as written, resolved against the working directory like any other relative path — see Path resolution. This applies to these two attributes only; no other path in the input file consults the variable.
Instead of an <Integrator>, the <SIMULATION> section can host a <Replay> or <Network> provider. Use exactly one provider child.
Replays a previously recorded CSV file step by step. Useful for offline analysis and visualisation of past simulation results.
Receives simulation state over ZeroMQ from a remote publisher. The timeout attribute (ms) controls how long to wait for data before raising an error.
The <SIMULATION> element supports a simulationSpeed attribute to enable real-time pacing:
| Value | Meaning |
|---|---|
0 (default) | Run as fast as possible. |
1.0 | Real-time. |
2.0 | Twice real-time speed. |
0.5 | Half real-time speed. |
simulationSpeed is read from the <SIMULATION> element itself, not from <Integrator>, and it is the only way to pace a headless FhSim run: -s / --simulation-speed is compiled into the visualisation executable FhVis alone, and FhSim rejects it as an unknown option. FhSimUI spells the same control --rtf / --rt. Where a command-line pacing flag does exist, it overrides this attribute. See Real-time pacing for the flag tables.
The <Diagnostics> element is most useful when a simulation behaves unexpectedly. Use the recipes below to identify the cause.
Enable JSON output and open the diagnostics viewer:
Then open playpen/bin/diagnostics.html and load diag.json, or run fhsim_plot diag.json.
Identify which states force small steps:
Look at the reported states — they typically point to stiff couplings that need tighter modelling or an implicit method.
Print state values at the moment the error occurs:
Compare analytical entries against finite-difference estimates:
Run a short simulation and inspect the log output for discrepancies.
Dump the Jacobian sparsity pattern to an image file:
This writes jacobian_sparsity.png into the working directory, showing which states are coupled. <JacobianSparsityDump> already fires once, at initialisation, so no <Triggers> element is needed. SparsityImageSize caps the image at 512 pixels per side; a larger system is drawn one pixel per block of states.
<Diagnostics> element.<Jacobian Type> / <LinearSolver Type> reference for scenario authors.Type="auto" picks and when to override it.<JacobianVerify> and <JacobianSparsityDump> output.