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]
12 holeInset: number; // distance of the thread hole from the two edges of the top-left corner [m]
13 pushAmount: number; // corner displacement [m]
14 fingerRadius: number; // radius of the pushed patch [m]
15 rampTime: number; // duration of the push ramp [s]
16 holdMax: number; // maximum settle time before release [s]
adc495dFinite finger release: ramp the recorded reaction force to zero over a release timeJeremy Magland 17 releaseTime: number; // time over which the finger force falls to zero [s]
f8a8a6cNonlinear steel sheet gong simulator with WebGPUJeremy Magland 18 settleSigma0: number; // artificial damping during push and settle [1/s]
19 settleKE: number; // kinetic energy threshold that counts as settled [J]
20 spacing: number; // requested grid spacing [m]
21 fs: number; // audio sample rate [Hz]
22 fps: number; // movie frame rate (also the batch rate) [Hz]
23 duration: number; // recorded duration after release [s]
24 listenerDist: number; // [m]
25 listenerAzimuth: number; // [deg], rotation about the plate's vertical axis (0 = on the normal)
26 listenerElevation: number; // [deg]
27 radiation: "rayleigh" | "dipole";
8dcc11fInitial-shape imperfection (Marguerre shallow-shell strains)Jeremy Magland 28 bowShape: BowShape; // stress-free initial shape of the sheet
29 bowAmplitude: number; // sag of the initial shape, center relative to the edges [m]
8dcc11fInitial-shape imperfection (Marguerre shallow-shell strains)Jeremy Magland 32export type BowShape = "none" | "width" | "height" | "dish";
f8a8a6cNonlinear steel sheet gong simulator with WebGPUJeremy Magland 34export const defaultParams: PhysParams = {
35 Lx: 18 * 0.0254,
36 Ly: 12 * 0.0254,
37 h: 0.79e-3,
38 E: 193e9,
39 nu: 0.29,
40 rho: 7900,
41 sigma0: 1.15,
42 sigma1: 1.08e-4,
43 holeInset: 0.012,
44 pushAmount: 0.01,
45 fingerRadius: 0.01,
46 rampTime: 0.1,
47 holdMax: 0.5,
adc495dFinite finger release: ramp the recorded reaction force to zero over a release timeJeremy Magland 48 releaseTime: 0.02,
50 settleKE: 1e-8,
51 spacing: 0.004,
52 fs: 48000,
53 fps: 120,
54 duration: 4,
55 listenerDist: 1.0,
56 listenerAzimuth: 0,
57 listenerElevation: 0,
58 radiation: "rayleigh",
8dcc11fInitial-shape imperfection (Marguerre shallow-shell strains)Jeremy Magland 59 bowShape: "width",
60 bowAmplitude: 3e-3,
8dcc11fInitial-shape imperfection (Marguerre shallow-shell strains)Jeremy Magland 63/** Stress-free initial shape w0 at every node. */
64export function initialShape(p: PhysParams, g: Grid): Float32Array<ArrayBuffer> {
65 const w0 = new Float32Array(g.n);
66 if (p.bowShape === "none" || p.bowAmplitude === 0) return w0;
67 for (let j = 0; j < g.ny; j++) {
68 for (let i = 0; i < g.nx; i++) {
69 const X = (2 * nodeX(g, i)) / p.Lx; // -1..1
70 const Y = (2 * nodeY(g, j)) / p.Ly;
71 let f = 0;
72 if (p.bowShape === "width") f = 1 - X * X;
73 else if (p.bowShape === "height") f = 1 - Y * Y;
74 else f = 1 - 0.5 * (X * X + Y * Y);
75 w0[j * g.nx + i] = p.bowAmplitude * f;
76 }
77 }
78 return w0;
79}
f8a8a6cNonlinear steel sheet gong simulator with WebGPUJeremy Magland 81export const SPEED_OF_SOUND = 343; // [m/s]
82export const RHO_AIR = 1.2; // [kg/m^3]
83export const HIST_LEN = 256; // ring buffer length for the retarded-time audio sum [samples]
84export const RB_INTERVAL = 32; // steps between rigid-body projections while ringing
85export const RB_INTERVAL_HELD = 4; // while the finger constraint is active
87export interface Grid {
88 nx: number;
89 ny: number;
90 hx: number;
91 hy: number;
92 n: number;
93}
95export interface Material {
96 D: number; // bending stiffness [N m]
97 C: number; // membrane stiffness E h / (1 - nu^2) [N/m]
98 G: number; // membrane shear stiffness E h / (2 (1 + nu)) [N/m]
99 nu: number;
100 rhoh: number; // mass per unit area [kg/m^2]
101}
103export function makeGrid(p: PhysParams): Grid {
104 const nx = Math.max(6, Math.round(p.Lx / p.spacing) + 1);
105 const ny = Math.max(6, Math.round(p.Ly / p.spacing) + 1);
106 return { nx, ny, hx: p.Lx / (nx - 1), hy: p.Ly / (ny - 1), n: nx * ny };
107}
109export function makeMaterial(p: PhysParams): Material {
110 return {
111 D: (p.E * p.h ** 3) / (12 * (1 - p.nu * p.nu)),
112 C: (p.E * p.h) / (1 - p.nu * p.nu),
113 G: (p.E * p.h) / (2 * (1 + p.nu)),
114 nu: p.nu,
115 rhoh: p.rho * p.h,
116 };
117}
119/** Node coordinates relative to the plate center. */
120export function nodeX(g: Grid, i: number): number {
121 return (i - (g.nx - 1) / 2) * g.hx;
122}
123export function nodeY(g: Grid, j: number): number {
124 return (j - (g.ny - 1) / 2) * g.hy;
125}
127/** Trapezoidal weight of a node: 1 in the interior, 1/2 on an edge, 1/4 at a corner. */
128export function trapWeight(g: Grid, i: number, j: number): number {
129 const tx = i === 0 || i === g.nx - 1 ? 0.5 : 1;
130 const ty = j === 0 || j === g.ny - 1 ? 0.5 : 1;
131 return tx * ty;
132}
134/** Decay time T60 [s] of a mode with angular frequency omega under the two-parameter loss model. */
135export function t60(m: Material, p: PhysParams, omega: number): number {
136 const kappa = Math.sqrt(m.D / m.rhoh);
137 const k2 = omega / kappa;
138 const sigma = p.sigma0 + p.sigma1 * k2;
139 return (3 * Math.LN10) / sigma; // amplitude factor 1000 = 60 dB
140}