import { NodeRunner } from "../src/node"; import { FD_METHOD, LIBRARY_FILES, buildRunBundle, cloneSpec, generateParamsBlock, parsePlateResult, scenarioPreset, isPlainStruct, type PlainStruct, type PlainValue, type PlateResult, type ProblemSpec, type RunResult, } from "../src/index"; export const runner = new NodeRunner(); /** A small, fast problem for tests: the default scenario on a coarse grid, a few milliseconds. */ export function testSpec(scenario = "sheet-on-thread"): ProblemSpec { const s = cloneSpec(scenarioPreset(scenario).spec); s.output.nframes = 8; const aspect = s.plate.Ly / s.plate.Lx; s.fd.nx = 21; s.fd.ny = Math.max(7, Math.round(21 * aspect) + (Math.round(21 * aspect) % 2 === 0 ? 1 : 0)); s.fd.duration = 0.002; return s; } export async function solve(spec: ProblemSpec): Promise<{ run: RunResult; result: PlateResult }> { const run = await runner.run({ bundle: buildRunBundle(spec, FD_METHOD) }); if (!run.ok) throw new Error(`fd failed: ${run.error}\n${run.output.join("")}`); return { run, result: parsePlateResult(run.value) }; } /** Run a custom script after the params block and return the struct it leaves in `out`. */ export async function runScript(spec: ProblemSpec, body: string): Promise { const main = [generateParamsBlock(spec), "", body, ""].join("\n"); const run = await runner.run({ bundle: { files: [{ name: "script.m", source: main }, ...LIBRARY_FILES], entry: "script.m", resultVariable: "out", }, }); if (!run.ok) throw new Error(`script failed: ${run.error}\n${run.output.join("")}`); if (!isPlainStruct(run.value)) throw new Error("script did not leave a struct in out"); return run.value; } export function scalar(s: PlainStruct, name: string): number { const v: PlainValue | undefined = s.fields[name]; if (typeof v === "number") return v; if (typeof v === "boolean") return v ? 1 : 0; if (v && typeof v === "object" && v.kind === "tensor" && v.data.length >= 1) return v.data[0]; throw new Error(`${name} is not a scalar`); } export function vector(s: PlainStruct, name: string): Float64Array { const v: PlainValue | undefined = s.fields[name]; if (typeof v === "number") return Float64Array.of(v); if (v && typeof v === "object" && v.kind === "tensor") return v.data; throw new Error(`${name} is not a vector`); } export function relL2(a: ArrayLike, b: ArrayLike): number { let num = 0; let den = 0; for (let i = 0; i < a.length; i++) { num += (a[i] - b[i]) ** 2; den += a[i] ** 2; } return Math.sqrt(num / Math.max(den, 1e-300)); } export function maxAbs(a: ArrayLike): number { let m = 0; for (let i = 0; i < a.length; i++) m = Math.max(m, Math.abs(a[i])); return m; } export { cloneSpec };