Room depth at each time sample
64-bitThe water depth in every room at each time step, from the outside level and the openings the water has passed.
level(t) = linear between hydrograph points; depth = max(0, level − floor) for a room the water reaches
Sample times are i × dt, not a running sum, so they do not drift. The interpolation divides by max(1e-9, Δt), and an opening counts as passed only when the level is more than 1e-9 m above its sill; both are absolute values. Depths are stored in a Float64Array.
export function floodRun({ rooms, openings = [], hydrograph, dt = 60 }) {
const pts = (hydrograph || []).slice().sort((x, y) => x[0] - y[0]);
if (pts.length < 2) return { ok: false, reason: 'nohydrograph' };
const T = pts[pts.length - 1][0], n = Math.floor(T / dt) + 1, times = Array.from({ length: n }, (_, i) => i * dt);
const at = (t) => { if (t <= pts[0][0]) return pts[0][1]; for (let i = 1; i < pts.length; i++) if (t <= pts[i][0]) { const f = (t - pts[i - 1][0]) / Math.max(1e-9, pts[i][0] - pts[i - 1][0]); return pts[i - 1][1] + f * (pts[i][1] - pts[i - 1][1]); } return pts[pts.length - 1][1]; };
const level = times.map(at), byId = new Map(rooms.map((r) => [r.id, r]));
const entry = (o) => { const base = o.b === null ? (byId.get(o.a)?.floor ?? 0) : Math.max(byId.get(o.a)?.floor ?? 0, byId.get(o.b)?.floor ?? 0); return base + (o.sill || 0); };
const depth = rooms.map(() => new Float64Array(n));
for (let i = 0; i < n; i++) {
const w = level[i], wet = new Set(), queue = [];
for (const o of openings) if (o.b === null && w > entry(o) + 1e-9 && byId.get(o.a) && w > byId.get(o.a).floor) { if (!wet.has(o.a)) { wet.add(o.a); queue.push(o.a); } }
while (queue.length) {
const cur = queue.shift();
for (const o of openings) {
if (o.b === null) continue;
const other = o.a === cur ? o.b : o.b === cur ? o.a : null;
if (other === null || wet.has(other)) continue;
if (w > entry(o) + 1e-9 && w > byId.get(other).floor) { wet.add(other); queue.push(other); }
}
}
rooms.forEach((r, k) => { if (wet.has(r.id)) depth[k][i] = Math.max(0, w - r.floor); });
}
const out = rooms.map((r, k) => {
let first = null, max = 0, wetFor = 0;
for (let i = 0; i < n; i++) { if (depth[k][i] > 0) { if (first === null) first = times[i]; wetFor += dt; } if (depth[k][i] > max) max = depth[k][i]; }
return { id: r.id, name: r.name, firstWet: first, maxDepth: Math.round(max * 1000) / 1000, wetFor, depth: depth[k] };
});
return { ok: true, times, level, dt, rooms: out, wetCount: out.filter((x) => x.firstWet !== null).length, peak: Math.max(...level) };
}