1// Twin separation summary. Usage: tsx tests/twin.ts script.m duration [eps]
2import { readFileSync, readdirSync } from "fs";
3import { runScript } from "../src/sim/runScript";
4import { simulate } from "../src/sim/solver";
5import { runTwin } from "../src/sim/twin";
6import { growthRate } from "../src/sim/analysis";
7const helpers = readdirSync("matlab/helpers").map(name => ({ name, source: readFileSync("matlab/helpers/" + name, "utf8") }));
8const { sys } = runScript(readFileSync(process.argv[2], "utf8"), helpers, () => {});
9sys.duration = Number(process.argv[3]);
10const eps = Number(process.argv[4] ?? 1e-9);
11const saved: Float64Array[] = [];
12const a = simulate(sys, { onFrame: (_f, _t, x) => saved.push(x.slice()) });
13const { sep } = runTwin(sys, saved, a.dt, eps);
14const out: string[] = [];
15const step = Math.max(1, Math.floor(sep.length / 14));
16for (let i = 0; i < sep.length; i += step) out.push(`${a.frameTimes[i].toFixed(2)}:${Math.log10(sep[i] + 1e-30).toFixed(1)}`);
17const g = growthRate(a.frameTimes, sep, eps);
18console.log(`rate ${g?.toFixed(0) ?? "-"} drift ${a.energyDrift.toExponential(1)} | log10 sep: ${out.join(" ")}`);