Numerical stability diagnostics

September 10, 2026

Konjugate's solver is fixed-step Explicit Euler, and nothing else. That's a deliberate, permanent choice, not a current limitation, but it comes with a real constraint: Explicit Euler is only stable for a given step size against a given system's own dynamics. Step too coarsely and a model doesn't fail gracefully, it diverges. Until recently the only detector for that was a hard non-finite check, a throw once a state had already become NaN or Infinity. Numerical stability diagnostics catches it earlier, in three phases: before a run starts, while it runs, and once it's done.

The example is a single node, deliberately built to be unstable: Value starts at 10, and a source term drives dValue/dt = -rate * Value with rate = 2.5, on a 1-second global step with the default 1 substep. Explicit Euler's own stability condition for this shape of equation is 0 <= rate * step <= 2; at rate * step = 2.5, it's already past that boundary.

Before the run: a warning that names the parameter

Nothing has to be run for this to show up. The moment the model and the step size disagree, the validation panel reports it directly:

The validation panel showing a numerical-stability warning
Amplification factor 1.5 > 1 per global step, attributed to this node's own 'Rate' source-term parameter, with a concrete substep count to fix it.

This comes from a real linearized stability check, a local Jacobian computed by finite-difference perturbation of the node's own equations, not a guess. Its eigenvalues say whether Explicit Euler is stable at the current step size, and which state and which parameter are most responsible. That's what makes the message specific rather than generic: not "this model might be unstable" but which parameter to look at and how many substeps would fix it.

Clicking the warning jumps straight to that parameter, not just the general node editor:

The source term editor, opened directly to the Rate parameter's value field
The same click-to-navigate every other validation warning in Konjugate already offers, extended to reach a specific parameter row instead of stopping at the owning node or edge.

Only equation-only nodes get this check. A node with a C++ or Python source term is skipped rather than probed, since safely assessing one would mean actually compiling and running it just to validate a document.

During the run: watching for a system that starts stable and drifts

The pre-run check only ever looks at the model's initial state. A nonlinear system can start stable and grow unstable partway through a run, which is what the second phase watches for. Stability monitoring, on by default in the run dialog, opts a run into that:

On by default. Turning it off for a specific run costs nothing extra to check every node's own growth every step, so the toggle exists for the rare case that even that cost isn't wanted.

Running the model for 10 seconds does exactly what the pre-run warning predicted:

The completed run: Value has grown to 576.650
576.650, from a starting value of 10 -- the node's own label now carries a warning badge, and a new Stability button has appeared in the toolbar with a count of 3.

What actually got found

Stability, the new toolbar button, opens a running list of everything found for this result:

The Numerical stability findings panel, listing three findings
A cheap per-step growth-trend check catches it first, at 5 seconds; that trend triggers a second, more expensive re-check of the node's real dynamics at its current state, confirming amplification factor 1.5 again; and once the run finishes, a post-run scan recognizes the classic Explicit-Euler sawtooth signature across the whole trajectory.

Three different checks, three different costs, looking at the same real problem from different angles. The cheap growth-trend check runs for every monitored node on every step; the expensive re-check only runs once a node's own trend actually looks suspicious, so a well-behaved run never pays for it regardless of how large the model is. The post-run scan costs nothing extra at all, since it only ever looks at a result that already exists.

Checking convergence, not just survival

Check convergence asks a stronger question than the other three: not just whether a run stayed finite, but whether it actually converged to a trustworthy answer. It re-runs the flagged node at repeatedly doubled substep counts and compares consecutive doublings until they agree within a tight tolerance, the same idea behind Richardson extrapolation.

The convergence check result: converged at 32 substeps, currently 1
32 substeps, not the 2 the pre-run warning suggested. Stability and convergence are different bars: 2 substeps stops this run from diverging, but Explicit Euler's own first-order accuracy means agreeing to within 1% between doublings can genuinely need many more.

Apply: 32 substeps commits that count the same way every other model edit in this series has, as one undoable step.

Running it again

The identical 10 seconds, with nothing changed except that one substep count:

The re-run: Value has settled to 4.95510e-11, with no stability findings
Real exponential decay: 10 * e^(-2.5 * 10) is approximately 5.19e-11, close to what 32 substeps of Explicit Euler actually produces. No warning badge, no Stability button, no findings.

Where to go from here

"Running a simulation" covers the run dialog and run configuration this page builds on. "Writing a relationship in C++ or Python" covers the one case this diagnostic can't reach: a programmable source term or relationship, skipped by the pre-run check for the same reason a bare equation isn't.

Follow Konjugate on LinkedIn for updates, or subscribe via RSS.

Comments