1# Plate Dynamics Lab 2
3An interactive lab for the sound of a small metal plate hung from a thread and struck by a hammer: a 30 mm square of 1 mm 6061-T6 aluminium, a thread through a hole near its top edge, a 10 g steel hammer at 0.45 m/s. The lab solves the problem two independent ways and lets you compare them: a Rayleigh-Ritz solution of the Kirchhoff-Love thin-plate energy on a tensor-Legendre basis with the strike applied as a prescribed Hertz pulse, and a Mindlin-Reissner finite-element mesh whose modes are struck by a hammer integrated against the moving plate. Both are modal: the modes of the plate on its thread are solved once and kept, and every strike is a sum of damped oscillators, so trying strike points, hammers and microphones is near instant. The app shows the plate on its thread, its modes and their shapes, the contact force and the hammer's motion, the displacement and surface-stress fields of the strike transient, a probe time series and spectrum, and the radiated sound, which you can play or save; the comparison tab lines up the two methods' modes, contact numbers, spectra and sounds.
5The numerics run in [numbl](https://github.com/flatironinstitute/numbl), a MATLAB-syntax environment executing in the browser, in double precision. The MATLAB source shown on screen is the source that ran: edit any file and rerun. No physics is computed in JavaScript; the host draws arrays and plays samples.
7This lab succeeds [plate-dynamics-lab](../plate-dynamics-lab), whose explicit finite-difference time stepping under gravity turned out to be a less realistic model of a struck plate than the two modal simulators in [sim-docs](../sim-docs), which this lab follows constant for constant. The old lab's WebGPU time loop has no counterpart here: nothing in a modal method is expensive enough to need it, except a very large finite-element eigensolve, and that is done in double precision in numbl instead (see below).
9## Layout
11```
12packages/core problem spec, .m generation, numbl runner, result typing, field primitives (no UI)
13packages/core/src/m the MATLAB library: plate_rr_modes.m, plate_fem_modes.m, plate_synthesize.m, ...
14packages/components React components: code view, parameter panel, 2-D field view, 3-D scene, plots, equations, run status, audio, movie
15apps/lab the app: one arrangement of the components
16```
18## The object
20A rectangular plate of width W, height H and thickness h with a hole of diameter 0.8 mm on its centreline 1.5 mm below the top edge, hung from a thread of length L. The thread holds the hole with the linearized pendulum stiffness m g / L against transverse motion, a soft point spring and not a clamp, and a dashpot at the hole stands for the loss where the thread rubs it. Coordinates are metres from the plate's centre, x to the right and y towards the top edge, as in the simulators this lab follows. A steel hammer with a rounded tip strikes the plate at a chosen point; a microphone sits on the plate's normal.
22## The two methods
24**Rayleigh-Ritz, Kirchhoff-Love** (`plate_rr_modes.m`, `plate_rr_strike.m`). The bending energy of a thin plate, with no shear deformation or rotary inertia, is minimized over products of Legendre polynomials up to order 12 per side, 169 coefficients, integrated by 30-point Gauss-Legendre quadrature per side. The hole subtracts its mass at its position and the thread adds its spring there; the generalized eigenproblem is solved densely after a Cholesky reduction. The rigid rotations and the pendulum mode are dropped. The strike is the force history of a Hertz contact on a rigid target, a sin^(3/2) pulse of the duration and impulse the Hertz formulas give, applied to every mode through Duhamel's integral by quadrature. The plate does not push back on the hammer.
26**Finite elements, Mindlin-Reissner** (`plate_fem_modes.m`, `plate_fem_strike.m`). Bilinear four-node elements with three unknowns per node (the deflection and two rotations) carry the bending and transverse-shear energies, both integrated with 2 x 2 Gauss points, and the mass with rotary inertia. This is the element of the reference simulator, kept as it is: full integration of the shear term locks on a thin plate, so the partials converge from above as the mesh is refined, by several percent between 32 x 32 and 80 x 80. The default mesh is 48 x 48, about 3 percent high on the main partial and a few seconds to solve; 80 x 80, where the reference was run, is one setting away and takes tens of seconds. The three rigid-body modes (two free rotations about the hole and the pendulum) are kept, so that the collision can put momentum into the freely hanging plate, and left out of the sound. The hammer and the plate are integrated together as one system, the hammer's Hertz spring with a Hunt-Crossley loss against the sum of all the modes under it, by fourth-order Runge-Kutta until they separate; the very light plate moves away under the hammer, so the contact is longer, the force far lower and the impulse smaller than the rigid-target pulse.
28The two share the plate, the hammer, the damping constants and the radiation model, and nothing else. Each mode is damped by three losses: the material's structural Q, an air-loading term that grows with frequency, and the thread's dashpot acting on the mode's displacement at the hole. The pressure at the microphone is a compact-source estimate, each mode's volume velocity reduced below the plate's coincidence frequency by the front/back cancellation of a small unbaffled plate; it is meaningful in timbre and decay, approximate in level, so the export is peak-normalized. All of this is the reference simulators' modelling, constant for constant, and the tests check it against their diagnostics (see below).
30## Two stages, and what is kept
32The modes depend on the plate, its elastic constants, the hole, the thread's length, the sample rate (modes are kept below 0.47 fs) and the method's numerics. The strike depends on everything else: the hammer, the strike point, the probe, the microphone, the material's Q, the dashpot and the render settings. The generated script `main.m` calls the two stages in turn, and the lab runs them as two scripts: the modes struct comes back to the host and is kept, and a later run whose modes key is unchanged goes straight to the strike, which takes a couple of seconds for four seconds of sound at 96 kHz. Editing `main.m` runs it as written, in one go.
34## The eigensolver
36numbl's own `eigs` is far too slow on the finite-element problem (twenty thousand unknowns at 80 x 80), but its sparse Cholesky after an approximate-minimum-degree ordering is fast, so `plate_fem_eigs.m` is a subspace iteration written on top of it: one factorization of K - sigma M with a negative shift, which keeps the matrix positive definite, then repeated block solves and Rayleigh-Ritz projections until the lowest eigenvalues settle. It is a block method, which matters for a square plate, whose spectrum is full of degenerate pairs. The block is sized from the modal density of a thin plate and enlarged if the wanted set does not reach past the cutoff; the count of iterations and the largest residual are reported under Run details. Everything stays in double precision.
38## Verification
40`packages/core/test/fixtures` holds the diagnostics the two Python simulators in `sim-docs` write, at the reference centre hit and an off-centre hit, for the Rayleigh-Ritz method at orders 12 and 8 and for the finite-element method at meshes of 16, 24, 32 and 48, together with the first 50 ms of each reference WAV. The tests run the library in numbl under Node against them: the mode frequencies agree to a part in ten million, the contact time, peak force, impulse and exit velocities to a part in a hundred thousand (the contact integrators differ), and the peak-normalized sound to the quantization of the WAV for Rayleigh-Ritz and to a few parts in ten thousand for the finite elements, where the two eigensolvers mix the degenerate pairs differently. The finite-element method is also checked to converge from above with mesh refinement, and the two-stage run to equal the single script bit for bit.
42## Saving a run
44- **The sound**, as a 24-bit mono WAV at the simulation's own sample rate, peak-normalized to the render's dBFS setting, as the reference simulators write theirs.
45- **The movie**, as WebM (VP9 and Opus) or MP4 where the browser records it: the 3-D scene as it stands, with a title bar carrying the simulated time and a caption bar carrying the field and its colour scale. With the sound it is recorded in real time, so the frames, which cover the first two milliseconds of the strike, flash by at the start; the slow-motion option plays the frames one after another at thirty a second without the sound, which could not be slowed a thousandfold. The recorder is the browser's own `MediaRecorder`, so the tab has to stay in front, and a machine that cannot keep up drops frames rather than slowing down.
47## The live app
49The lab is deployed at <https://plate-dynamics-lab-2.vault1-sites.magland.org/>. `.mochi/workflows/deploy.yml` typechecks, runs the tests and builds `apps/lab` on every push to `main`, then publishes it; a regression against the reference diagnostics stops the deployment. The site sits at the root of an origin of its own, which the repository's site label gives it, so the build's base path comes from `configure-pages` rather than being written into the Vite config, and the page can use `localStorage` for your settings and source edits.
51## Running
53```bash
54npm install
55npm run dev # the lab, http://localhost:5173
56npm test # vitest: the .m files in numbl under Node against the reference diagnostics
57npm run typecheck
58npm run build
59```
61The `.m` files also run unchanged from the numbl CLI, Octave or MATLAB:
63```bash
64npx numbl run --path packages/core/src/m my_script.m
65```
67## Honest limits
69- Both models are linear: no denting of the plate, no chatter of the hammer, no thread buzz, no room, no microphone colouring. A hard strike on aluminium would dent it, and the model does not know.
70- Kirchhoff-Love theory has no shear deformation or rotary inertia, so the Rayleigh-Ritz partials in the tens of kHz run a little high; the Mindlin-Reissner element carries the shear but locks on a thin plate, so its partials converge from above with the mesh. The two are a percent or two apart on this plate, and neither is the truth.
71- Neither contact model has been checked against a measured hammer; the prescribed pulse ignores the plate's motion, and the coupled model's loss is a rule of thumb tuned to the nominal restitution.
72- The damping constants are the reference simulators' order-of-magnitude figures, chosen, not measured. The thread is a point spring; the hole is a point mass correction (Rayleigh-Ritz) or not cut at all (finite elements).
73- The radiated sound is a compact-source estimate with a smooth cancellation below coincidence, not a solution of the diffraction problem: right in timbre and decay, approximate in level.
74- The finite-element method's rigid-body modes have no gravitational restoring force, so after the strike the plate drifts in rotation at a constant rate rather than swinging; they are shown in the total displacement and kept out of the sound.
75- Stresses are second derivatives of the fields (for the finite elements, first-order gradients of the rotations) and converge more slowly than the frequencies.