/ proof-of-concept / cymsimweb
Sign in
proof-of-concept / cymsimweb
cymsimweb / tests / shell.test.ts
53 lines · 2.9 KBCodeBlameHistory
2import assert from "node:assert/strict";
3import { CymbalProfile } from "../src/model/geometry.ts";
4import { diskMesh } from "../src/model/mesh.ts";
5import { buildShell } from "../src/model/shell.ts";
6import { shellChecks } from "../src/model/forces.ts";
7import { buildPatch, freeContactTime, maxCompression } from "../src/model/exciter.ts";
8import { fixture, maxAbs, maxAbsDiff } from "./helpers.ts";
10const cfg = fixture.config;
11const profile = new CymbalProfile(cfg.profile, cfg.imperfections);
12const mesh = diskMesh(cfg.profile.radius, cfg.mesh.element_size, cfg.mesh.smoothing);
13const shell = buildShell(mesh, profile, cfg.material, cfg.numerics);
15test("dent-free shell matches the Python assembly", () => {
16 const f = fixture.shell;
17 const rel = (a: number, b: number) => Math.abs(a - b) / Math.abs(b);
18 assert.ok(rel(shell.mass, f.mass) < 1e-10);
19 assert.ok(rel(shell.omegaMax, f.omega_max) < 1e-8, `omega ${shell.omegaMax} vs ${f.omega_max}`);
20 assert.equal(shell.limitingElement, f.limiting_element);
c24896fTime step from the global stability bound, as in cymsim; Resume falls back to the element bound for older runs; accept output.probesJeremy Magland 21 // the global bound by the same power iteration as cymsim's (start vector and stopping rule included)
22 assert.ok(rel(shell.omegaGlobal, fixture.timestep.omega_global) < 1e-9, `global omega ${shell.omegaGlobal} vs ${fixture.timestep.omega_global}`);
23 assert.equal(shell.omegaGlobalIterations, fixture.timestep.omega_global_iterations);
24 assert.ok(shell.omegaGlobalConverged && shell.omegaGlobal < shell.omegaMax && shell.dtStable === 2 / shell.omegaGlobal);
1e29264cymsimweb: WebGPU companion of cymsim with YAML/form editor, live spectrogram and playbackJeremy Magland 25 assert.ok(rel(shell.maxSlope, f.max_slope) < 1e-10);
26 assert.ok(rel(Math.min(...shell.hE), f.h_e_min) < 1e-10 && rel(Math.max(...shell.hE), f.h_e_max) < 1e-10);
27 assert.ok(rel(Math.max(...shell.z0Node), f.z0_max) < 1e-10);
28 assert.ok(maxAbsDiff(shell.z0Node.subarray(0, 5), f.z0_node_first5) < 1e-14);
29 assert.ok(maxAbsDiff(shell.Ke.subarray(0, 81), f.Ke0) < 1e-8 * maxAbs(f.Ke0));
30 assert.ok(rel(shell.De[0], f.D0) < 1e-12 && maxAbsDiff(shell.Cm.subarray(0, 3), f.Cm0) < 1e-6);
31 assert.ok(maxAbsDiff(shell.M.subarray(0, 15), f.M_first3.flat()) < 1e-12);
32 const N = mesh.n;
33 assert.ok(maxAbsDiff(shell.Rm.subarray(4 * 5 * N, 4 * 5 * N + 15), f.Rm4_first3.flat()) < 1e-8);
34 assert.deepEqual(shell.zeroModes, [6, 6]);
35});
37test("element and rigid-mode checks", () => {
38 const c = shellChecks(shell);
39 assert.ok(c.rigidTiltResidual < 1e-12);
40 assert.ok(c.constantCurvatureError < 1e-8);
41 assert.ok(c.rigidModeRatio < 1e-8, `rigid ratio ${c.rigidModeRatio}`);
42});
44test("contact patch matches Python", () => {
45 const p = buildPatch(cfg.hits[0], mesh, profile);
46 const f = fixture.patch;
47 assert.deepEqual(Array.from(p.nodes), f.nodes.flat());
48 assert.ok(maxAbsDiff(p.weights, f.weights.flat()) < 1e-10);
49 assert.ok(maxAbsDiff(p.offsets, f.offsets) < 1e-12);
50 assert.ok(Math.abs(p.kTotal / f.k_total - 1) < 1e-12);
51 assert.ok(Math.abs(maxCompression(p) / f.max_compression - 1) < 1e-10);
52 assert.ok(Math.abs(freeContactTime(p) / f.free_contact_time - 1) < 1e-6);
53});
↑↓ move↵openescclose