# @evolv/physics-sanity Cross-node physical-balance helpers. Import from any node's test files to assert that scenario states close mass, hydraulic, hydraulic-power, oxygen-transfer, or energy balances within a stated tolerance. ## Why Per-node unit tests verify shape and behaviour. They don't catch physically impossible plant states that arise from cross-node coupling — e.g. a pumpingStation reporting outflow > inflow + accumulation, or a diffuser reporting OTR inconsistent with its KLa × ΔC × V. These helpers don't replace per-node tests. They sit on top of an integration scenario and assert the closing balance. ## Usage ```js const sanity = require('../../../tools/physics-sanity'); test('three-pump station closes the hydraulic balance', () => { // … drive the scenario, take a snapshot … const r = sanity.assertHydraulicBalance({ headerSuctionPa: ps.suctionPressurePa, headerDischargePa: ps.dischargePressurePa, pumpHeadPa: sumOfPumpHeads, frictionPa: pipeFrictionEstimate, }); assert.equal(r.ok, true, sanity.reportToString(r)); }); ``` ## Helpers exported | Function | Asserts | |---|---| | `assertMassBalance({ inflowKgPerS, outflowKgPerS, accumulationKgPerS })` | `in - out - accumulation ≈ 0` | | `assertHydraulicBalance({ headerSuctionPa, headerDischargePa, pumpHeadPa, frictionPa, staticHeadPa })` | `ΔP_headers ≈ pumpHead - friction - static` | | `assertHydraulicPower({ flowM3PerS, headPa, shaftPowerW, efficiency })` | `shaft ≈ Q·H / η` | | `assertOxygenTransfer({ klaPerS, csMgPerL, cMgPerL, otrKgPerS, volumeM3 })` | `OTR ≈ KLa · (Cs - C) · V` | | `assertEnergyBalance({ heatInW, workInW, heatOutW, workOutW, accumulationW })` | `Q_in + W_in ≈ Q_out + W_out + ΔE` | Each returns `{ ok, label, ...residuals }`. `reportToString(r)` formats for human-readable failure messages. ## CLI demo ```bash node tools/physics-sanity/bin/physics-sanity.js ``` Runs four sanity-check scenarios against the helpers (smoke-test for the library itself). ## Tolerance defaults | Domain | Absolute | Relative | |---|---|---| | mass | 1e-6 kg/s | 0.1 % | | hydraulic ΔP | 50 Pa (0.5 mbar) | 0.1 % | | hydraulic power | 1 W | 0.5 % | | OTR | 1e-4 kg/s | 0.5 % | | energy | 1 W | 0.1 % | Override per call with `absTol` / `relTol`. ## Where to use this Out-of-the-box destinations: | Scenario | Where to add | Calls | |---|---|---| | pumpingStation hydraulic closure | `nodes/pumpingStation/test/integration/` | `assertHydraulicBalance`, `assertHydraulicPower` | | reactor → settler mass balance | `nodes/reactor/test/integration/` | `assertMassBalance` | | diffuser OTR vs reactor uptake | `nodes/diffuser/test/integration/` | `assertOxygenTransfer` | | machineGroupControl efficiency sanity | `nodes/machineGroupControl/test/integration/` | `assertHydraulicPower` | A future tool can scan integration tests and report which scenarios do or don't have a closing-balance assertion.