5778036cymbalsim: scripted plate and cymbal designer with a modal linear model and a von Karman GPU modelJeremy Magland 1// The assembled linear stiffness against the reference forces, on flat and
5778036cymbalsim: scripted plate and cymbal designer with a modal linear model and a von Karman GPU modelJeremy Magland 3import { test } from "node:test";
4import assert from "node:assert/strict";
2481a63Remove the standJeremy Magland 5import { makeGrid, withMask, type Plate, nodeMasses, nodeX, nodeY } from "../src/params.ts";
5778036cymbalsim: scripted plate and cymbal designer with a modal linear model and a von Karman GPU modelJeremy Magland 6import { uniformPlate, accelerations, fields } from "../src/model/reference.ts";
7import { assemble, stiffnessMatvec, toGrid, fromGrid, isFlatPlate } from "../src/model/assemble.ts";
2a1a6daDerive the losses from physical constants instead of a prescribed decay curveJeremy Magland 9const bronze = { E: 110e9, nu: 0.34, rho: 8800, eta: 0, thermal: null };
5778036cymbalsim: scripted plate and cymbal designer with a modal linear model and a von Karman GPU modelJeremy Magland 10
11function checkAgainstReference(pl: Plate, fieldsN: 1 | 3, seed = 3): number {
12 const g = pl.grid;
13 const sys = assemble(pl, fieldsN);
14 const masses = nodeMasses(pl);
15 let s = seed >>> 0;
16 const rnd = () => { s = (s * 1664525 + 1013904223) >>> 0; return s / 4294967296 - 0.5; };
17 const x = new Float64Array(sys.n);
18 for (let i = 0; i < sys.n; i++) x[i] = rnd();
19 const y = new Float64Array(sys.n);
20 stiffnessMatvec(sys, x, y);
21 // The reference force at a tiny amplitude, scaled back: -M a(eps x) / eps.
22 const eps = 1e-9;
23 const fx = toGrid(sys, x.map((v) => v * eps), g.n);
24 const out = fields(g.n);
5778036cymbalsim: scripted plate and cymbal designer with a modal linear model and a von Karman GPU modelJeremy Magland 26 const fm = { u: out.u.map((a, k) => (-a * masses[k]) / eps), v: out.v.map((a, k) => (-a * masses[k]) / eps), w: out.w.map((a, k) => (-a * masses[k]) / eps) };
27 const yref = fromGrid(sys, fm);
28 let err = 0, nrm = 0;
29 for (let i = 0; i < sys.n; i++) { err += (y[i] - yref[i]) ** 2; nrm += yref[i] ** 2; }
30 return Math.sqrt(err / nrm);
31}
33test("K x equals the linearized reference force on a flat rectangle (one and three fields)", () => {
34 const pl = uniformPlate(makeGrid(0.2, 0.15, 0.02), bronze, 1e-3);
35 assert.ok(isFlatPlate(pl));
36 assert.ok(checkAgainstReference(pl, 1) < 1e-6);
37 assert.ok(checkAgainstReference(pl, 3) < 1e-6);
38});
2481a63Remove the standJeremy Magland 40test("K x equals the linearized reference force on a bowed, tapered plate with a hole", () => {
5778036cymbalsim: scripted plate and cymbal designer with a modal linear model and a von Karman GPU modelJeremy Magland 41 const g = withMask(makeGrid(0.2, 0.2, 0.0125), (x, y) => x * x + y * y <= 0.1 ** 2 && x * x + y * y >= 0.01 ** 2);
42 const w0 = new Float64Array(g.n);
43 for (let j = 0; j < g.ny; j++) for (let i = 0; i < g.nx; i++) w0[j * g.nx + i] = 3e-3 * Math.sin(20 * nodeX(g, i)) * Math.cos(15 * nodeY(g, j));
5778036cymbalsim: scripted plate and cymbal designer with a modal linear model and a von Karman GPU modelJeremy Magland 45 for (let k = 0; k < g.n; k++) if (pl.h[k] > 0) pl.h[k] = 1e-3 * (1 + 0.4 * Math.abs(nodeX(g, k % g.nx)) / 0.1);
46 assert.ok(!isFlatPlate(pl));
47 const sys = assemble(pl);
48 assert.equal(sys.fields, 3);
49 const rel = checkAgainstReference(pl, 3);
50 assert.ok(rel < 1e-6, `relative error ${rel}`);
51});
53test("the band is narrow: the shorter grid axis is innermost", () => {
54 const wide = assemble(uniformPlate(makeGrid(0.4, 0.1, 0.01), bronze, 1e-3));
55 const tall = assemble(uniformPlate(makeGrid(0.1, 0.4, 0.01), bronze, 1e-3));
56 assert.equal(wide.bw, tall.bw);
57 assert.ok(wide.bw <= 2 * 11 + 2, `bw ${wide.bw}`);
58});