1import { describe, expect, it } from "vitest";
2import { buildPlanBundle, getMethod, parseFdPlan } from "../src/index";
3import { runner, solve, testSpec, maxAbs } from "./helpers";
5describe("finite differences", () => {
6 it("plans a time step at the stability bound", async () => {
7 const spec = testSpec();
8 const run = await runner.run({ bundle: buildPlanBundle(spec, getMethod("fd"))! });
9 expect(run.ok).toBe(true);
10 if (!run.ok) return;
11 const plan = parseFdPlan(run.value);
12 const { Lx, Ly, h } = spec.plate;
13 const { E, nu, rho } = spec.material;
14 const kappa = Math.sqrt((E * h ** 3) / (12 * (1 - nu ** 2)) / (rho * h));
15 const dx = Lx / (spec.fd.nx - 1);
16 const dy = Ly / (spec.fd.ny - 1);
17 const lamMax = 4 / dx ** 2 + 4 / dy ** 2;
18 const s1 = spec.damping.sigma1;
19 const dtMax = 2 / (lamMax * (s1 + Math.sqrt(kappa ** 2 + s1 ** 2)));
20 expect(plan.dt_max).toBeCloseTo(dtMax, 12);
21 expect(plan.dt).toBeCloseTo(spec.fd.cfl * dtMax, 12);
22 expect(plan.cell_steps).toBe(plan.ncells * plan.nsteps);
23 });
25 it("is stable just below the CFL limit and diverges just above it", async () => {
26 const spec = testSpec();
27 spec.fd.cfl = 0.98;
28 const stable = await solve(spec, "fd");
29 expect(stable.result.info.stable).toBe(true);
30 expect(stable.result.probe.w.every(Number.isFinite)).toBe(true);
32 spec.fd.cfl = 1.03;
33 const blown = await solve(spec, "fd");
34 expect(blown.result.info.stable).toBe(false);
35 expect(blown.result.info.diverged_step).toBeGreaterThan(0);
36 expect(blown.result.info.diverged_step).toBeLessThanOrEqual(blown.result.info.nsteps);
37 // everything returned is still finite, so the interface can show the blow-up
38 expect(blown.result.probe.w.every(Number.isFinite)).toBe(true);
39 expect(blown.result.frames.w.data.every(Number.isFinite)).toBe(true);
40 expect(blown.result.info.notes.some((n) => /diverged/.test(n))).toBe(true);
41 });
43 it("supports clamped edges and gives a stiffer response than simply supported", async () => {
44 const ss = testSpec();
45 const cl = testSpec();
46 cl.bc = "clamped";
47 const a = await solve(ss, "fd");
48 const b = await solve(cl, "fd");
49 expect(b.result.info.stable).toBe(true);
50 expect(maxAbs(b.result.probe.w)).toBeLessThan(maxAbs(a.result.probe.w));
51 });
53 it("reports its problem size and a short audio clip", async () => {
54 const spec = testSpec();
55 const { result } = await solve(spec, "fd");
56 expect(result.info.ncells).toBe(spec.fd.nx * spec.fd.ny);
57 expect(result.info.extra.cell_steps).toBe(result.info.ncells * result.info.nsteps);
58 const expectedAudio = Math.ceil(result.t.length * result.info.dt * spec.output.fs);
59 expect(Math.abs(result.audio.p.length - expectedAudio)).toBeLessThanOrEqual(1);
60 expect(result.audio.p.every(Number.isFinite)).toBe(true);
61 });
62});