/ proof-of-concept / plate-dynamics-lab-2
Sign in
proof-of-concept / plate-dynamics-lab-2
plate-dynamics-lab-2 / README.md
11.2 KBPreviewCodeBlameHistoryRaw

Plate Dynamics Lab 2#

An 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.

The numerics run in 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.

This lab succeeds 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, 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).

Layout#

packages/core        problem spec, .m generation, numbl runner, result typing, field primitives (no UI)
packages/core/src/m  the MATLAB library: plate_rr_modes.m, plate_fem_modes.m, plate_synthesize.m, ...
packages/components  React components: code view, parameter panel, 2-D field view, 3-D scene, plots, equations, run status, audio, movie
apps/lab             the app: one arrangement of the components

The object#

A 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.

The two methods#

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.

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.

The 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).

Two stages, and what is kept#

The 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.

The eigensolver#

numbl'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.

Verification#

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.

Saving a run#

The live app#

The 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.

Running#

npm install
npm run dev              # the lab, http://localhost:5173
npm test                 # vitest: the .m files in numbl under Node against the reference diagnostics
npm run typecheck
npm run build

The .m files also run unchanged from the numbl CLI, Octave or MATLAB:

npx numbl run --path packages/core/src/m my_script.m

Honest limits#

↑↓ move↵openescclose