/ proof-of-concept / plate-mechanics-sim
Sign in
proof-of-concept / plate-mechanics-sim
plate-mechanics-sim / packages / core / test / modal.test.ts
64 lines · 2.9 KBCodeBlameHistory
2546961Plate mechanics lab: Kirchhoff-Love plates solved by numbl in the browserJeremy Magland 1import { describe, expect, it } from "vitest";
2import { solve, testSpec, maxAbs } from "./helpers";
4describe("modal superposition", () => {
5 it("reproduces the closed-form fundamental frequency of the simply supported rectangle", async () => {
6 const spec = testSpec();
7 const { result } = await solve(spec, "modal");
8 const { Lx: a, Ly: b, h } = spec.plate;
9 const { E, nu, rho } = spec.material;
10 const D = (E * h ** 3) / (12 * (1 - nu ** 2));
11 const kappa = Math.sqrt(D / (rho * h));
12 const f11 = (kappa / (2 * Math.PI)) * ((Math.PI / a) ** 2 + (Math.PI / b) ** 2);
13 expect(result.modes).toBeDefined();
14 expect(result.modes!.f_hz[0]).toBeCloseTo(f11, 6);
15 expect(result.modes!.m[0]).toBe(1);
16 expect(result.modes!.n[0]).toBe(1);
17 // Modes are sorted by frequency and all below fmax
18 const f = result.modes!.f_hz;
19 for (let k = 1; k < f.length; k++) expect(f[k]).toBeGreaterThanOrEqual(f[k - 1]);
20 expect(f[f.length - 1]).toBeLessThanOrEqual(spec.modal.fmax);
21 });
23 it("produces a finite, decaying response with a plausible amplitude", async () => {
24 const spec = testSpec();
25 const { result, run } = await solve(spec, "modal");
26 expect(result.method).toBe("modal");
27 expect(result.info.stable).toBe(true);
28 expect(result.t.length).toBe(Math.round(spec.output.fs * spec.output.duration) + 1);
29 const w = result.probe.w;
30 expect(w.every(Number.isFinite)).toBe(true);
31 // 50 N hammer on a 2 mm steel sheet: deflection of order 0.1 mm, stress of order 10 MPa
32 const peak = maxAbs(w);
33 expect(peak).toBeGreaterThan(1e-5);
34 expect(peak).toBeLessThan(1e-3);
35 const vmPeak = maxAbs(result.probe.vm);
36 expect(vmPeak).toBeGreaterThan(1e6);
37 expect(vmPeak).toBeLessThan(1e8);
38 // damping: the last tenth of the record is quieter than the first tenth
39 const n = w.length;
40 expect(maxAbs(w.subarray(Math.floor(0.9 * n)))).toBeLessThan(maxAbs(w.subarray(0, Math.floor(0.1 * n))));
41 // frames and audio have the promised shapes
42 expect(result.frames.w.nx).toBe(spec.output.grid[0]);
43 expect(result.frames.w.ny).toBe(spec.output.grid[1]);
44 expect(result.frames.w.nf).toBe(result.frames.t.length);
45 expect(result.audio.p.length).toBe(result.t.length);
46 expect(result.audio.fs).toBe(spec.output.fs);
47 expect(run.backend.engine).toBe("numbl");
48 expect(run.wallMs).toBeGreaterThan(0);
49 });
51 it("refuses clamped edges with a legible message", async () => {
52 const spec = testSpec();
53 spec.bc = "clamped";
54 await expect(solve(spec, "modal")).rejects.toThrow(/simply supported/);
55 });
57 it("handles harmonic forcing", async () => {
58 const spec = testSpec();
59 spec.excitation = { type: "harmonic", x: 0.1, y: 0.08, force: 5, frequency: 500, radius: 0.012 };
60 const { result } = await solve(spec, "modal");
61 expect(result.probe.w.every(Number.isFinite)).toBe(true);
62 expect(maxAbs(result.probe.w)).toBeGreaterThan(0);
63 });
64});
↑↓ move↵openescclose