Skip to content
Alistair HomewoodDusk Signal
HomeCorollaryPARSECUnit-CheckerSim DebuggerInterests
Get in touch

Solo project · JOSS paper submitted

Sim-Debugger

Conservation-law monitoring for numerical simulations

Sim-Debugger instruments a simulation, monitors invariants during the run, and reports failures in physics language rather than vague generic debugging output.

Back to homeGitHub repo
01

Why conservation law monitoring matters

Numerical simulations of physical systems should preserve the invariants implied by the underlying model. Total energy, momentum, charge, and similar quantities are often the first place a broken integrator or missing term will reveal itself.

Traditional debugging tools do not really help with that. They can tell you where time was spent or what a variable was at one instant, but not when the simulation stopped respecting the physics it was supposed to represent.

energy residualtimestepfirst clearly bad stepforward Euler driftBoris / symplectic baseline
The relevant picture is not a random UI panel. It is the behaviour of the simulation itself: stable baseline versus clear drift onset.
02

How the tool hooks into a simulation

The idea is to wrap an existing simulation loop with monitors instead of rewriting the simulation from scratch. Users register invariants, run the program, and let the tool track how those quantities move as the state evolves.

That structure makes the tool useful across different domains. A particle pusher, an N-body simulation, or a fluid code can all expose the quantities that matter and let Sim-Debugger watch for the moment they start drifting.

boris_pusher.pypython
import numpy as np
from sim_debugger import monitor, invariant

@invariant("total_energy")
def energy(state):
    v = state["velocity"]
    return 0.5 * state["mass"] * np.dot(v, v)

@monitor(dt=0.01, steps=50000)
def boris_step(state, E, B, dt):
    q, m = state["charge"], state["mass"]
    v_minus = state["velocity"] + (q * dt / (2 * m)) * E
    t = (q * dt / (2 * m)) * B
    s = 2 * t / (1 + np.dot(t, t))
    v_prime = v_minus + np.cross(v_minus, t)
    v_plus  = v_minus + np.cross(v_prime, s)
    state["velocity"] = v_plus + (q * dt / (2 * m)) * E
    state["position"] += state["velocity"] * dt
    return state
03

Output

The output speaks to the language of the simulation author. Instead of saying a scalar changed, it says that total energy drifted by a certain amount, when that drift became noticeable, and what kind of numerical mistake the pattern is consistent with.

Example diagnostic output
$ sim-debugger run boris_pusher.py --dt 0.01 --steps 50000

[sim-debugger] Initial energy: 4.500000e+02 J
[step 10000] total_energy: 4.500013e+02 J (+0.0003%)
[step 20000] total_energy: 4.500028e+02 J (+0.0006%)
[step 30000] total_energy: 4.500891e+02 J (+0.020%)
[step 40000] total_energy: 4.518700e+02 J (+0.416%)

VIOLATION DETECTED at step 40000

Quantity:   total energy
Drift:      +0.416% over 40,000 steps
Onset:      step ~28,000
Likely cause:
  E-field coupling introduces secular
  energy error at large |E|*dt.

Suggestion:
  reduce dt in high-field regions
This is the kind of report that makes the tool useful during real simulation work: identify the quantity, the onset, and the likely cause.
04

Importance to me

Sim-Debugger captures the part of my work that is most concerned with trust in scientific computation. It is not enough for a simulation to run. It has to remain physically meaningful, and the tooling around it should make that easy to inspect.

Alistair Homewood · Physics + Math (UBC), Developing the future of deep space active radiation shielding.

alistairhomewood@gmail.comGitHubLinkedInTikTok