/ proof-of-concept / plate-dynamics-lab-2
Sign in
proof-of-concept / plate-dynamics-lab-2
plate-dynamics-lab-2 / packages / core / src / spec / validate.ts
85 lines · 4.5 KBCodeBlameHistory
2004981Plate dynamics lab 2: a hammer on a hung plate by Rayleigh-Ritz and by Mindlin-Reissner finite elementsJeremy Magland 1import { onPlate, type ProblemSpec } from "./types";
3export interface ValidationIssue {
4 /** Dotted path into the spec, e.g. "plate.thickness" */
5 path: string;
6 message: string;
7 severity: "error" | "warning";
8}
10/**
11 * Structural checks a control panel can show before running anything. The
12 * `.m` code repeats the checks that matter for the numerics, so a spec that
13 * passes here can still be rejected by the solver with a legible message.
14 */
15export function validateSpec(spec: ProblemSpec): ValidationIssue[] {
16 const out: ValidationIssue[] = [];
17 const err = (path: string, message: string) => out.push({ path, message, severity: "error" });
18 const warn = (path: string, message: string) => out.push({ path, message, severity: "warning" });
20 const { plate, material, hole, thread, hammer, strike, probe, mic, render, rr, fem } = spec;
21 if (!(plate.width > 0)) err("plate.width", "must be positive");
22 if (!(plate.height > 0)) err("plate.height", "must be positive");
23 if (!(plate.thickness > 0)) err("plate.thickness", "must be positive");
24 if (plate.thickness > 0.1 * Math.min(plate.width, plate.height))
25 warn(
26 "plate.thickness",
27 "thickness above a tenth of the shorter side: the Kirchhoff-Love method is unreliable; the Mindlin-Reissner method carries the shear",
28 );
29 if (!(material.E > 0)) err("material.E", "must be positive");
30 if (!(material.nu >= 0 && material.nu < 0.5)) err("material.nu", "must lie in [0, 0.5)");
31 if (!(material.rho > 0)) err("material.rho", "must be positive");
32 if (!(material.Q > 0)) err("material.Q", "must be positive");
34 if (!(hole.diameter > 0)) err("hole.diameter", "must be positive");
35 if (!(hole.edge_offset > hole.diameter / 2))
36 err("hole.edge_offset", "the hole must lie inside the plate: offset above the hole's radius");
37 if (!(hole.edge_offset < plate.height / 2))
38 err("hole.edge_offset", "the hole must lie above the plate's centre");
39 if (!(thread.length > 0)) err("thread.length", "must be positive");
40 if (thread.dashpot < 0) err("thread.dashpot", "cannot be negative");
42 if (!(hammer.mass > 0)) err("hammer.mass", "must be positive");
43 if (!(hammer.tip_radius > 0)) err("hammer.tip_radius", "must be positive");
44 if (!(hammer.speed > 0)) err("hammer.speed", "must be positive");
45 if (!(hammer.E > 0)) err("hammer.E", "must be positive");
46 if (!(hammer.nu >= 0 && hammer.nu < 0.5)) err("hammer.nu", "must lie in [0, 0.5)");
47 if (!(hammer.restitution >= 0 && hammer.restitution <= 1))
48 err("hammer.restitution", "must lie in [0, 1]");
49 if (hammer.speed > 2)
50 warn(
51 "hammer.speed",
52 "a hard strike would dent aluminium; the model is linear and does not know",
53 );
55 if (!onPlate(plate, strike.x, strike.y)) err("strike", "strike point lies outside the plate");
56 if (!onPlate(plate, probe.x, probe.y)) err("probe", "probe lies outside the plate");
58 if (!(mic.distance > 0)) err("mic.distance", "must be positive");
59 if (!(mic.air_density > 0)) err("mic.air_density", "must be positive");
60 if (!(mic.sound_speed > 0)) err("mic.sound_speed", "must be positive");
62 if (!(render.fs > 0)) err("render.fs", "sample rate must be positive");
63 if (!(render.duration > 0)) err("render.duration", "duration must be positive");
64 if (render.duration > 10) warn("render.duration", "a long render is slow to synthesize");
65 if (!(render.modes >= 1)) err("render.modes", "keep at least one mode");
66 if (render.nframes < 2) err("render.nframes", "need at least 2 frames");
67 if (!(render.frame_span > 0)) err("render.frame_span", "must be positive");
68 if (render.frame_span > render.duration)
69 err("render.frame_span", "the frames cannot outlast the render");
70 if (!(render.normalize_dbfs <= 0)) err("render.normalize_dbfs", "a peak above 0 dBFS clips");
72 if (!Number.isInteger(rr.basis_order) || rr.basis_order < 2)
73 err("rr.basis_order", "the basis needs order 2 or more");
74 if (rr.basis_order > 24) warn("rr.basis_order", "a high order costs time and conditioning");
75 if (!Number.isInteger(rr.quadrature_order) || rr.quadrature_order < rr.basis_order + 2)
76 err("rr.quadrature_order", "use at least basis order + 2 quadrature points per side");
77 if (!Number.isInteger(rr.grid) || rr.grid < 5)
78 err("rr.grid", "the display grid needs 5 nodes or more");
80 if (!Number.isInteger(fem.mesh_x) || fem.mesh_x < 4) err("fem.mesh_x", "at least 4 elements");
81 if (!Number.isInteger(fem.mesh_y) || fem.mesh_y < 4) err("fem.mesh_y", "at least 4 elements");
82 if (fem.mesh_x * fem.mesh_y > 100 * 100)
83 warn("fem", "more than 10,000 elements: the eigensolve will take minutes");
84 return out;
↑↓ move↵openescclose