/ proof-of-concept / plate-dynamics-lab
Sign in
proof-of-concept / plate-dynamics-lab
plate-dynamics-lab / packages / core / test / helpers.ts
83 lines · 2.8 KBBlameHistoryRaw
1import { NodeRunner } from "../src/node";
2import {
3 FD_METHOD,
4 LIBRARY_FILES,
5 buildRunBundle,
6 cloneSpec,
7 generateParamsBlock,
8 parsePlateResult,
9 scenarioPreset,
10 isPlainStruct,
11 type PlainStruct,
12 type PlainValue,
13 type PlateResult,
14 type ProblemSpec,
15 type RunResult,
16} from "../src/index";
18export const runner = new NodeRunner();
20/** A small, fast problem for tests: the default scenario on a coarse grid, a few milliseconds. */
21export function testSpec(scenario = "sheet-on-thread"): ProblemSpec {
22 const s = cloneSpec(scenarioPreset(scenario).spec);
23 s.output.nframes = 8;
24 const aspect = s.plate.Ly / s.plate.Lx;
25 s.fd.nx = 21;
26 s.fd.ny = Math.max(7, Math.round(21 * aspect) + (Math.round(21 * aspect) % 2 === 0 ? 1 : 0));
27 s.fd.duration = 0.002;
28 return s;
31export async function solve(spec: ProblemSpec): Promise<{ run: RunResult; result: PlateResult }> {
32 const run = await runner.run({ bundle: buildRunBundle(spec, FD_METHOD) });
33 if (!run.ok) throw new Error(`fd failed: ${run.error}\n${run.output.join("")}`);
34 return { run, result: parsePlateResult(run.value) };
37/** Run a custom script after the params block and return the struct it leaves in `out`. */
38export async function runScript(spec: ProblemSpec, body: string): Promise<PlainStruct> {
39 const main = [generateParamsBlock(spec), "", body, ""].join("\n");
40 const run = await runner.run({
41 bundle: {
42 files: [{ name: "script.m", source: main }, ...LIBRARY_FILES],
43 entry: "script.m",
44 resultVariable: "out",
45 },
46 });
47 if (!run.ok) throw new Error(`script failed: ${run.error}\n${run.output.join("")}`);
48 if (!isPlainStruct(run.value)) throw new Error("script did not leave a struct in out");
49 return run.value;
52export function scalar(s: PlainStruct, name: string): number {
53 const v: PlainValue | undefined = s.fields[name];
54 if (typeof v === "number") return v;
55 if (typeof v === "boolean") return v ? 1 : 0;
56 if (v && typeof v === "object" && v.kind === "tensor" && v.data.length >= 1) return v.data[0];
57 throw new Error(`${name} is not a scalar`);
60export function vector(s: PlainStruct, name: string): Float64Array {
61 const v: PlainValue | undefined = s.fields[name];
62 if (typeof v === "number") return Float64Array.of(v);
63 if (v && typeof v === "object" && v.kind === "tensor") return v.data;
64 throw new Error(`${name} is not a vector`);
67export function relL2(a: ArrayLike<number>, b: ArrayLike<number>): number {
68 let num = 0;
69 let den = 0;
70 for (let i = 0; i < a.length; i++) {
71 num += (a[i] - b[i]) ** 2;
72 den += a[i] ** 2;
73 }
74 return Math.sqrt(num / Math.max(den, 1e-300));
77export function maxAbs(a: ArrayLike<number>): number {
78 let m = 0;
79 for (let i = 0; i < a.length; i++) m = Math.max(m, Math.abs(a[i]));
80 return m;
83export { cloneSpec };
↑↓ move↵openescclose