Files
pumpingStation/simulations/formatters/table.js

41 lines
1.4 KiB
JavaScript
Raw Normal View History

Add eval harness + Tier 2/3 mode template pages ### eval/ (scenario-based evaluation) Complements the unit tests under test/basic. Scenarios fluctuate inputs over simulated time, record every tick to JSONL, print a summary table + event log, and check expectations. Complementary to unit tests — these answer "how does the system respond to this input profile" rather than "is this function correct". - eval/run.js — driver; monkey-patches Date.now so the volume integrator ticks at 1 s/iter regardless of wall-clock - eval/scenarios/ — one file per scenario - levelbased-steady.js — constant inflow, demand converges - levelbased-storm.js — inflow surge, demand saturates - safety-dry-run-trip.js — manual mode, empty basin, safety trips - eval/formatters/table.js — ASCII summary of sampled ticks - eval/logs/ — per-scenario JSONL output (one line per tick) - eval/README.md — usage + scenario file shape + how to pipe into InfluxDB/Grafana All three starter scenarios PASS with their expectations. ### wiki/modes/ (tier template pages) The levelbased page templated Tier-1 modes (static transfer function). Added worked examples for the other two tiers so all mode pages share a common skeleton and new modes have something concrete to imitate: - flowbased.md — Tier 2 (PID on measured outflow) - powerbased.md — Tier 2 (levelbased curve clipped by grid power budget) - mpc.md — Tier 3 (optimisation + forecast; block diagram + scenario time-series instead of a fixed curve) - modes/README.md — updated with the three-tier classification table and diagram-type-per-tier guidance Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 16:49:41 +02:00
// ASCII table summary of scenario samples.
// Used by simulations/run.js.
Add eval harness + Tier 2/3 mode template pages ### eval/ (scenario-based evaluation) Complements the unit tests under test/basic. Scenarios fluctuate inputs over simulated time, record every tick to JSONL, print a summary table + event log, and check expectations. Complementary to unit tests — these answer "how does the system respond to this input profile" rather than "is this function correct". - eval/run.js — driver; monkey-patches Date.now so the volume integrator ticks at 1 s/iter regardless of wall-clock - eval/scenarios/ — one file per scenario - levelbased-steady.js — constant inflow, demand converges - levelbased-storm.js — inflow surge, demand saturates - safety-dry-run-trip.js — manual mode, empty basin, safety trips - eval/formatters/table.js — ASCII summary of sampled ticks - eval/logs/ — per-scenario JSONL output (one line per tick) - eval/README.md — usage + scenario file shape + how to pipe into InfluxDB/Grafana All three starter scenarios PASS with their expectations. ### wiki/modes/ (tier template pages) The levelbased page templated Tier-1 modes (static transfer function). Added worked examples for the other two tiers so all mode pages share a common skeleton and new modes have something concrete to imitate: - flowbased.md — Tier 2 (PID on measured outflow) - powerbased.md — Tier 2 (levelbased curve clipped by grid power budget) - mpc.md — Tier 3 (optimisation + forecast; block diagram + scenario time-series instead of a fixed curve) - modes/README.md — updated with the three-tier classification table and diagram-type-per-tier guidance Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 16:49:41 +02:00
function pad(s, n, left = false) {
s = String(s ?? '');
if (s.length >= n) return s.slice(0, n);
return left ? s.padStart(n) : s.padEnd(n);
}
function num(x, digits = 2) {
return Number.isFinite(x) ? x.toFixed(digits) : '—';
}
function formatTable(records, sampleEvery = 1) {
if (!records.length) return ' (no records)';
const header = ['t(s)', 'level(m)', 'vol(m3)', 'dir', 'netFlow(m3/s)', 'src', 'demand', 'safe'];
const rows = [];
for (let i = 0; i < records.length; i += sampleEvery) rows.push(records[i]);
if (rows[rows.length - 1] !== records[records.length - 1]) rows.push(records[records.length - 1]);
const widths = [6, 9, 9, 10, 14, 14, 8, 5];
const lines = [];
lines.push(header.map((h, i) => pad(h, widths[i], true)).join(' '));
lines.push(widths.map((w) => '─'.repeat(w)).join(' '));
for (const r of rows) {
lines.push([
pad(r.t, widths[0], true),
pad(num(r.level, 2), widths[1], true),
pad(num(r.volume, 2), widths[2], true),
pad(r.direction ?? '—', widths[3], true),
pad(num(r.netFlow, 5), widths[4], true),
pad(r.flowSource ?? '—', widths[5], true),
pad(num(r.percControl, 0) + '%', widths[6], true),
pad(r.safetyActive ? '⚠' : '·', widths[7], true),
].join(' '));
}
return lines.map((l) => ' ' + l).join('\n');
}
module.exports = { formatTable };