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>
This commit is contained in:
57
tools/physics-sanity/test/balance.test.js
Normal file
57
tools/physics-sanity/test/balance.test.js
Normal file
@@ -0,0 +1,57 @@
|
||||
'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);
|
||||
});
|
||||
Reference in New Issue
Block a user