f8a8a6cNonlinear steel sheet gong simulator with WebGPUJeremy Magland 1// Physical and numerical parameters of the simulation, plus derived quantities.
3export interface PhysParams {
4 Lx: number; // plate width along x [m]
5 Ly: number; // plate height along y [m]
6 h: number; // thickness [m]
7 E: number; // Young's modulus [Pa]
8 nu: number; // Poisson ratio
9 rho: number; // density [kg/m^3]
10 sigma0: number; // frequency-independent loss [1/s]
11 sigma1: number; // frequency-dependent loss [m^2/s]
bfde5e4High-frequency ringing: in-plane loss, band-limited listener sum, damping defaultsJeremy Magland 12 sigmaIn: number; // additional loss of the in-plane motion [1/s]
f8a8a6cNonlinear steel sheet gong simulator with WebGPUJeremy Magland 13 holeInset: number; // distance of the thread hole from the two edges of the top-left corner [m]
14 pushAmount: number; // corner displacement [m]
15 fingerRadius: number; // radius of the pushed patch [m]
16 rampTime: number; // duration of the push ramp [s]
17 holdMax: number; // maximum settle time before release [s]
adc495dFinite finger release: ramp the recorded reaction force to zero over a release timeJeremy Magland 18 releaseTime: number; // time over which the finger force falls to zero [s]
f8a8a6cNonlinear steel sheet gong simulator with WebGPUJeremy Magland 19 settleSigma0: number; // artificial damping during push and settle [1/s]
20 settleKE: number; // kinetic energy threshold that counts as settled [J]
21 spacing: number; // requested grid spacing [m]
22 fs: number; // audio sample rate [Hz]
23 fps: number; // movie frame rate (also the batch rate) [Hz]
24 duration: number; // recorded duration after release [s]
25 listenerDist: number; // [m]
26 listenerAzimuth: number; // [deg], rotation about the plate's vertical axis (0 = on the normal)
27 listenerElevation: number; // [deg]
28 radiation: "rayleigh" | "dipole";
8dcc11fInitial-shape imperfection (Marguerre shallow-shell strains)Jeremy Magland 29 bowShape: BowShape; // stress-free initial shape of the sheet
30 bowAmplitude: number; // sag of the initial shape, center relative to the edges [m]
10998acDrumstick strike excitation (Hertz contact with a free mass)Jeremy Magland 31 excitation: Excitation;
32 strikeFracX: number; // strike point as a fraction of the width from the left edge
33 strikeFracY: number; // strike point as a fraction of the height from the bottom edge
34 strikeVelocity: number; // stick tip speed at impact [m/s]
35 stickMass: number; // effective mass of the stick [kg]
36 contactStiffness: number; // Hertz contact stiffness K [N/m^alpha]
37 contactExponent: number; // Hertz exponent alpha (1.5 for a sphere)
38 contactRadius: number; // radius of the contact patch [m]
10998acDrumstick strike excitation (Hertz contact with a free mass)Jeremy Magland 41export type Excitation = "push" | "strike";
8dcc11fInitial-shape imperfection (Marguerre shallow-shell strains)Jeremy Magland 43export type BowShape = "none" | "width" | "height" | "dish";
f8a8a6cNonlinear steel sheet gong simulator with WebGPUJeremy Magland 45export const defaultParams: PhysParams = {
46 Lx: 18 * 0.0254,
47 Ly: 12 * 0.0254,
48 h: 0.79e-3,
49 E: 193e9,
50 nu: 0.29,
51 rho: 7900,
52 sigma0: 1.15,
bfde5e4High-frequency ringing: in-plane loss, band-limited listener sum, damping defaultsJeremy Magland 53 sigma1: 5e-4,
54 sigmaIn: 20,
56 pushAmount: 0.01,
57 fingerRadius: 0.01,
58 rampTime: 0.1,
59 holdMax: 0.5,
adc495dFinite finger release: ramp the recorded reaction force to zero over a release timeJeremy Magland 60 releaseTime: 0.02,
62 settleKE: 1e-8,
63 spacing: 0.004,
64 fs: 48000,
65 fps: 120,
66 duration: 4,
67 listenerDist: 1.0,
68 listenerAzimuth: 0,
69 listenerElevation: 0,
70 radiation: "rayleigh",
8dcc11fInitial-shape imperfection (Marguerre shallow-shell strains)Jeremy Magland 71 bowShape: "width",
72 bowAmplitude: 3e-3,
10998acDrumstick strike excitation (Hertz contact with a free mass)Jeremy Magland 73 excitation: "push",
74 strikeFracX: 0.7,
75 strikeFracY: 0.35,
76 strikeVelocity: 3,
77 stickMass: 0.03,
78 contactStiffness: 5e8,
79 contactExponent: 1.5,
80 contactRadius: 5e-3,
10998acDrumstick strike excitation (Hertz contact with a free mass)Jeremy Magland 83/** Strike point in plate coordinates (relative to the center). */
84export function strikePoint(p: PhysParams): [number, number] {
85 return [(p.strikeFracX - 0.5) * p.Lx, (p.strikeFracY - 0.5) * p.Ly];
86}
8dcc11fInitial-shape imperfection (Marguerre shallow-shell strains)Jeremy Magland 88/** Stress-free initial shape w0 at every node. */
89export function initialShape(p: PhysParams, g: Grid): Float32Array<ArrayBuffer> {
90 const w0 = new Float32Array(g.n);
91 if (p.bowShape === "none" || p.bowAmplitude === 0) return w0;
92 for (let j = 0; j < g.ny; j++) {
93 for (let i = 0; i < g.nx; i++) {
94 const X = (2 * nodeX(g, i)) / p.Lx; // -1..1
95 const Y = (2 * nodeY(g, j)) / p.Ly;
96 let f = 0;
97 if (p.bowShape === "width") f = 1 - X * X;
98 else if (p.bowShape === "height") f = 1 - Y * Y;
99 else f = 1 - 0.5 * (X * X + Y * Y);
100 w0[j * g.nx + i] = p.bowAmplitude * f;
101 }
102 }
103 return w0;
104}
f8a8a6cNonlinear steel sheet gong simulator with WebGPUJeremy Magland 106export const SPEED_OF_SOUND = 343; // [m/s]
107export const RHO_AIR = 1.2; // [kg/m^3]
108export const HIST_LEN = 256; // ring buffer length for the retarded-time audio sum [samples]
109export const RB_INTERVAL = 32; // steps between rigid-body projections while ringing
110export const RB_INTERVAL_HELD = 4; // while the finger constraint is active
112export interface Grid {
113 nx: number;
114 ny: number;
115 hx: number;
116 hy: number;
117 n: number;
118}
120export interface Material {
121 D: number; // bending stiffness [N m]
122 C: number; // membrane stiffness E h / (1 - nu^2) [N/m]
123 G: number; // membrane shear stiffness E h / (2 (1 + nu)) [N/m]
124 nu: number;
125 rhoh: number; // mass per unit area [kg/m^2]
126}
128export function makeGrid(p: PhysParams): Grid {
129 const nx = Math.max(6, Math.round(p.Lx / p.spacing) + 1);
130 const ny = Math.max(6, Math.round(p.Ly / p.spacing) + 1);
131 return { nx, ny, hx: p.Lx / (nx - 1), hy: p.Ly / (ny - 1), n: nx * ny };
132}
134export function makeMaterial(p: PhysParams): Material {
135 return {
136 D: (p.E * p.h ** 3) / (12 * (1 - p.nu * p.nu)),
137 C: (p.E * p.h) / (1 - p.nu * p.nu),
138 G: (p.E * p.h) / (2 * (1 + p.nu)),
139 nu: p.nu,
140 rhoh: p.rho * p.h,
141 };
142}
144/** Node coordinates relative to the plate center. */
145export function nodeX(g: Grid, i: number): number {
146 return (i - (g.nx - 1) / 2) * g.hx;
147}
148export function nodeY(g: Grid, j: number): number {
149 return (j - (g.ny - 1) / 2) * g.hy;
150}
152/** Trapezoidal weight of a node: 1 in the interior, 1/2 on an edge, 1/4 at a corner. */
153export function trapWeight(g: Grid, i: number, j: number): number {
154 const tx = i === 0 || i === g.nx - 1 ? 0.5 : 1;
155 const ty = j === 0 || j === g.ny - 1 ? 0.5 : 1;
156 return tx * ty;
157}
159/** Decay time T60 [s] of a mode with angular frequency omega under the two-parameter loss model. */
160export function t60(m: Material, p: PhysParams, omega: number): number {
161 const kappa = Math.sqrt(m.D / m.rhoh);
162 const k2 = omega / kappa;
163 const sigma = p.sigma0 + p.sigma1 * k2;
164 return (3 * Math.LN10) / sigma; // amplitude factor 1000 = 60 dB
165}