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";
28}
30export const defaultParams: PhysParams = {
31 Lx: 18 * 0.0254,
32 Ly: 12 * 0.0254,
33 h: 0.79e-3,
34 E: 193e9,
35 nu: 0.29,
36 rho: 7900,
37 sigma0: 1.15,
38 sigma1: 1.08e-4,
39 holeInset: 0.012,
40 pushAmount: 0.01,
41 fingerRadius: 0.01,
42 rampTime: 0.1,
43 holdMax: 0.5,
adc495dFinite finger release: ramp the recorded reaction force to zero over a release timeJeremy Magland 44 releaseTime: 0.02,
46 settleKE: 1e-8,
47 spacing: 0.004,
48 fs: 48000,
49 fps: 120,
50 duration: 4,
51 listenerDist: 1.0,
52 listenerAzimuth: 0,
53 listenerElevation: 0,
54 radiation: "rayleigh",
55};
57export const SPEED_OF_SOUND = 343; // [m/s]
58export const RHO_AIR = 1.2; // [kg/m^3]
59export const HIST_LEN = 256; // ring buffer length for the retarded-time audio sum [samples]
60export const RB_INTERVAL = 32; // steps between rigid-body projections while ringing
61export const RB_INTERVAL_HELD = 4; // while the finger constraint is active
63export interface Grid {
64 nx: number;
65 ny: number;
66 hx: number;
67 hy: number;
68 n: number;
69}
71export interface Material {
72 D: number; // bending stiffness [N m]
73 C: number; // membrane stiffness E h / (1 - nu^2) [N/m]
74 G: number; // membrane shear stiffness E h / (2 (1 + nu)) [N/m]
75 nu: number;
76 rhoh: number; // mass per unit area [kg/m^2]
77}
79export function makeGrid(p: PhysParams): Grid {
80 const nx = Math.max(6, Math.round(p.Lx / p.spacing) + 1);
81 const ny = Math.max(6, Math.round(p.Ly / p.spacing) + 1);
82 return { nx, ny, hx: p.Lx / (nx - 1), hy: p.Ly / (ny - 1), n: nx * ny };
83}
85export function makeMaterial(p: PhysParams): Material {
86 return {
87 D: (p.E * p.h ** 3) / (12 * (1 - p.nu * p.nu)),
88 C: (p.E * p.h) / (1 - p.nu * p.nu),
89 G: (p.E * p.h) / (2 * (1 + p.nu)),
90 nu: p.nu,
91 rhoh: p.rho * p.h,
92 };
93}
95/** Node coordinates relative to the plate center. */
96export function nodeX(g: Grid, i: number): number {
97 return (i - (g.nx - 1) / 2) * g.hx;
98}
99export function nodeY(g: Grid, j: number): number {
100 return (j - (g.ny - 1) / 2) * g.hy;
101}
103/** Trapezoidal weight of a node: 1 in the interior, 1/2 on an edge, 1/4 at a corner. */
104export function trapWeight(g: Grid, i: number, j: number): number {
105 const tx = i === 0 || i === g.nx - 1 ? 0.5 : 1;
106 const ty = j === 0 || j === g.ny - 1 ? 0.5 : 1;
107 return tx * ty;
108}
110/** Decay time T60 [s] of a mode with angular frequency omega under the two-parameter loss model. */
111export function t60(m: Material, p: PhysParams, omega: number): number {
112 const kappa = Math.sqrt(m.D / m.rhoh);
113 const k2 = omega / kappa;
114 const sigma = p.sigma0 + p.sigma1 * k2;
115 return (3 * Math.LN10) / sigma; // amplitude factor 1000 = 60 dB
116}