Files
EVOLV/tools/physics-sanity/test/balance.test.js
znetsixe 6e6699c763 tools: add physics-sanity + Docker MCP scaffolding + tools/README
- tools/physics-sanity/ — JS library of cross-node balance helpers
  (mass / hydraulic / hydraulic-power / oxygen-transfer / energy) with
  7 unit tests + a CLI demo. Designed for `require()` from per-node
  integration tests where shape-based unit tests miss physically-
  impossible plant states.
- tools/docker-compose.yml + tools/mcp/{node-red-admin,influxdb,browser}
  scaffolding — placeholder Dockerfiles + a ROADMAP.md for the Node-RED
  admin MCP. Compose file is the target shape for the Q3-2026 migration
  to the central MCP server; the per-service Dockerfile stays in this
  repo as the canonical definition either way. Implementations are TODO.
- tools/README.md — top-level tooling index; documents the CI order for
  running every tool on a PR.
- .gitignore: ignore tools/.env (developer-specific MCP endpoints).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 10:16:47 +02:00

58 lines
1.8 KiB
JavaScript

'use strict';
const { test } = require('node:test');
const assert = require('node:assert/strict');
const lib = require('../index.js');
test('mass balance closes for steady-state pass-through', () => {
const r = lib.assertMassBalance({ inflowKgPerS: 10, outflowKgPerS: 10 });
assert.equal(r.ok, true);
assert.equal(r.residualKgPerS, 0);
});
test('mass balance reports residual when leaking', () => {
const r = lib.assertMassBalance({ inflowKgPerS: 10, outflowKgPerS: 9.5 });
assert.equal(r.ok, false);
assert.equal(Math.round(r.residualKgPerS * 1000), 500);
});
test('hydraulic balance: ΔP = pumpHead - friction - static', () => {
const r = lib.assertHydraulicBalance({
headerSuctionPa: 0,
headerDischargePa: 90000,
pumpHeadPa: 100000,
frictionPa: 8000,
staticHeadPa: 2000,
});
assert.equal(r.ok, true);
});
test('hydraulic power Q·H / η — within 0.5% relative tolerance', () => {
const Q = 0.03;
const H = 100000;
const eta = 0.65;
const shaft = (Q * H) / eta;
const r = lib.assertHydraulicPower({ flowM3PerS: Q, headPa: H, shaftPowerW: shaft, efficiency: eta });
assert.equal(r.ok, true);
});
test('hydraulic power flags eta=0', () => {
const r = lib.assertHydraulicPower({ flowM3PerS: 0.03, headPa: 100000, shaftPowerW: 5000, efficiency: 0 });
assert.equal(r.ok, false);
});
test('OTR check uses standard KLa formula', () => {
const kla = 0.002;
const cs = 9.0;
const c = 2.0;
const V = 20;
const otr = kla * (cs - c) * V / 1e6;
const r = lib.assertOxygenTransfer({ klaPerS: kla, csMgPerL: cs, cMgPerL: c, volumeM3: V, otrKgPerS: otr });
assert.equal(r.ok, true);
});
test('energy balance: heat-in + work-in = heat-out + work-out + accumulation', () => {
const r = lib.assertEnergyBalance({ heatInW: 1000, workInW: 200, heatOutW: 700, workOutW: 400, accumulationW: 100 });
assert.equal(r.ok, true);
});