Skip to content

Solo project

Unit-Checker

Static analysis for physical dimensions in scientific code

Unit-Checker infers and verifies physical units in scientific code so dimensional mistakes can be caught before a long simulation quietly produces nonsense.

01

Why this matters

Scientific programs are full of quantities with physical units: meters, seconds, kilograms, pascals. Programming languages do not understand any of that structure by default. As far as the compiler is concerned, they are all just numbers.

That means you can add a force to a velocity, multiply pressure by time when you meant to divide, or pass imperial data into an SI calculation and still get output that looks plausible. That is the kind of bug Unit-Checker is built to expose.

Typical failure mode
# Mars Climate Orbiter style mismatch
thrust_impulse_lm  = 500.0   # lbf*s
thrust_impulse_jpl = 200.0   # N*s
total_impulse = thrust_impulse_lm + thrust_impulse_jpl

error: cannot add imperial impulse
       to SI impulse without conversion
The value of the tool is not abstract. It is in catching this class of physics mistake before runtime.
02

A concrete Navier-Stokes example

A good project page for Unit-Checker should show a real example, not just a slogan. In the snippet below, the pressure gradient is being added directly to acceleration terms without dividing by density first.

The code still runs, but the dimensions do not make sense. Unit-Checker follows the units through the computation and flags the bad line before the simulation ever reaches runtime.

navier_stokes.pypython
# @units: rho [kg/m³], v [m/s], P [Pa], mu [Pa·s], g [m/s²], dt [s]
def navier_stokes_step(rho, v_x, v_y, P, mu, dx, dy, dt, g):
    dP_dx = (P[1:, :] - P[:-1, :]) / dx
    dP_dy = (P[:, 1:] - P[:, :-1]) / dy

    laplacian_vx = (v_x[2:,:] - 2*v_x[1:-1,:] + v_x[:-2,:]) / dx**2
    visc_x = mu * laplacian_vx / rho
    advect_x = v_x * (v_x[1:,:] - v_x[:-1,:]) / dx

    # dimensional error
    accel_x = -dP_dx + visc_x - advect_x + g

    v_x_new = v_x[1:-1,:] + accel_x * dt
    return v_x_new
03

How dimensions are represented

Internally, every quantity is represented as a seven-component SI base-dimension vector. That makes it possible to propagate units algebraically through arbitrary expressions instead of depending on a hand-maintained list of derived-unit names.

QuantitySymbolUnitVector
LengthLm[1, 0, 0, 0, 0, 0, 0]
MassMkg[0, 1, 0, 0, 0, 0, 0]
TimeTs[0, 0, 1, 0, 0, 0, 0]
CurrentIA[0, 0, 0, 1, 0, 0, 0]
TemperatureΘK[0, 0, 0, 0, 1, 0, 0]
AmountNmol[0, 0, 0, 0, 0, 1, 0]
Luminous intensityJcd[0, 0, 0, 0, 0, 0, 1]
Velocityvm/s[1, 0, -1, 0, 0, 0, 0]
ForceFN[1, 1, -2, 0, 0, 0, 0]
PressurePPa[-1, 1, -2, 0, 0, 0, 0]
EnergyEJ[2, 1, -2, 0, 0, 0, 0]
Dynamic viscosityμPa·s[-1, 1, -1, 0, 0, 0, 0]
Base dimensions and a few derived examples. This is the core representation that makes the analysis tractable.
04

What I want this project page to show

The point of this project page is to make the idea legible very quickly: sparse annotations go in, dimension vectors propagate through code, and the tool surfaces the exact expression where the physics stops making sense.