/ proof-of-concept / fput-plate
Sign in
proof-of-concept / fput-plate
fput-plate / README.md
53 lines · 7.6 KBCodeBlameHistory
3The fourth chaos toy: the FPUT chain made two-dimensional, as a toy cymbal or gong. It follows `../gong-wire`, which added bending stiffness and a dome to the FPUT chain but sounded thin. The reason is the mode count: a one-dimensional wire with plate-like dispersion has fewer and fewer modes per octave as the pitch rises, so its high band holds only a handful of widely spaced partials. A two-dimensional plate has roughly a constant number of modes per unit frequency, so each octave up holds about twice as many as the one below, and that dense top is what a cymbal's wash is made of.
5## The model (`plate.py`)
7$N \times N$ unit beads on a square grid move up and down, $w_{ij}$; the boundary ring is pinned and the bending simply supported. The sheet may be built as a shallow dome $z = 16 H_d x(1-x)y(1-y)$. The energy is
9$$\sum_{\text{links}} \left[\tfrac12 T D^2 + \beta\left(z' D + \tfrac12 D^2\right)^2\right] + \tfrac12 K_b \sum_{ij} (\Delta_h w)_{ij}^2,$$
11with $D$ the height difference across a link to one of the four neighbours, $z'$ its rest slope, and $\Delta_h$ the 5-point Laplacian. The links are the FPUT-$\beta$ springs in two directions (a height difference stretches a link by about $D^2/2$), standing in for a plate's midplane stretching; bending gives plate-like dispersion; the dome adds stiffness and the three-wave coupling of curved shells. Without a dome the modes are products of sines with $\omega^2 = T\lambda + K_b\lambda^2$, $\lambda = 4\sin^2(k\pi/2(N+1)) + 4\sin^2(l\pi/2(N+1))$, and mode energies are computed in this basis (approximately, when there is a dome). Damping is friction plus dashpots between neighbours, applied explicitly each step; time stepping is leapfrog. Checks: the force equals minus the energy gradient to 2e-11, the sine products are exact modes of the flat sheet to 4e-16, and energy is conserved to 1e-5 to 1e-4.
13One simplification to keep in mind: the links pull only vertically, while a real plate's midplane can also slide sideways and partly relax the stretching. The sheet's nonlinearity is therefore stiffer than a plate's; FPUT makes the same simplification.
15## Results (2026-10-05)
17With 24 x 24 beads there are 576 modes. With bending stiffness 25 and the lowest mode at 100 Hz they reach 16.8 kHz; with bending 4 they reach only 8.6 kHz, but even then the count per octave rises steadily: 2, 7, 18, 36, 104 and 300 modes in the octaves from 100 Hz up to 6.4 kHz.
19**Exploration, undamped** (`experiments.py`, `results/explore.png`; strike centered on bead (8, 10), radius 2). Share of the energy above 4 kHz at 10, 50 and 300 ms, dome 10:
21| bending | top mode | E = 3000 | E = 10000 | E = 30000 |
22| --- | --- | --- | --- | --- |
23| 8 | 11.4 kHz | 5%, 25%, 60% | 30%, 59%, 69% | 50%, 70%, 74% |
24| 25 | 16.8 kHz | 0.2%, 1%, 13% | 3%, 13%, 49% | 21%, 54%, 74% |
26The sheet needs much harder strikes than the wire or the chain (around $E = 10^4$ rather than 10 to 50), because the bending stiffness dominates the linear forces and the stretching only competes at larger amplitudes. At bending 25 and $E = 10^4$ the energy climbs into the band above 4 kHz over 300 ms, a gradual bloom; at $E = 3 \times 10^4$ it climbs within tens of milliseconds. A dome of height 10 adds a few percent; without one the picture is the same.
28**Bloom without chaos, unless damping is light.** In the web app (lowest mode ringing 4 s, highest 0.8 s) the Gong preset ($E = 10^4$) puts 22% of the energy above 4 kHz at 145 ms, and the Crash preset ($3 \times 10^4$) 47% at 152 ms. With the highest mode ringing only 0.25 s, even the Crash strike was essentially regular: a perturbation grew by about $e^{1.2}$ in all, and strikes differing by $10^{-6}$ ended identical. Strikes differing by $10^{-3}$ still drifted apart, through amplitude-dependent pitch shifts rather than exponential separation. Undamped, the same strike is chaotic (growth about $e^{12}$ in 0.6 s). So in this toy, as in the gong wire, the high-frequency bloom is robust and reproducible, while chaos needs both a hard strike and light damping of the high modes.
30**Where the chaos is** (`chaos_scan.py`). Pairs of strikes differing by $10^{-6}$ in strength, 1.5 s of sound, lowest mode at 100 Hz ringing 6 s, strike at bead (9, 10) with radius 1.6. Reported: RMS difference of the final energy share per band, in dB, for the highest mode ringing 0.8 / 2 / 5 s. Values below about 0.1 dB mean the strikes ended the same; above 1 dB they sound different.
32| bending, dome | E = 10^4 | E = 3 x 10^4 | E = 10^5 |
33| --- | --- | --- | --- |
34| 8, flat | 0.00 / 0.05 / 0.30 | 1.5 / 2.3 / 3.2 | 2.9 / 2.8 / 4.0 |
35| 8, dome 10 | 0.05 / 0.63 / 1.8 | 2.2 / 4.7 / 2.3 | 3.1 / 5.5 / 2.7 |
36| 25, flat | 0.03 / 0.02 / 0.04 | 0.02 / 0.04 / 0.04 | 0.11 / 2.5 / 2.5 |
37| 25, dome 10 | 0.01 / 0.01 / 0.02 | 0.03 / 0.35 / 0.24 | 1.6 / 3.6 / 4.2 |
39Softer bending makes chaos far easier to reach: at bending 8 every strike of $3 \times 10^4$ or more is chaotic, while bending 25 needs about $10^5$ and a high-mode ring of 2 s or more. Presumably this is because the stretching competes with a weaker linear restoring force, so the same strike is relatively more nonlinear. A dome helps in most cases, and so does lighter damping of the high modes, with some exceptions (bending 8, dome 10, $E = 3 \times 10^4$ differs less at 5 s than at 2 s; with only one pair per setting these numbers carry noise). The app's original presets (bending 25, $E \le 3 \times 10^4$, high ring 0.8 s) all sit in the regular corner, which is why hits there sounded identical.
41**Time step.** The app steps at about 0.6 of leapfrog's linear stability limit. Waveforms differ from runs with 4 times smaller steps (the top modes accumulate phase error over hundreds of periods), but the share of energy above 4 kHz over time and the final spectrum by band agree within a few percent, which is what the sound depends on.
43## Web application
45`web/index.html` (published as a private artifact at https://claude.ai/artifact/UeCkZbQqcbzpk3e4kvCmr5) runs 8 strikes and a separate Lyapunov run in parallel Web Workers. Controls: strike energy, bending stiffness, dome height, beads per side, strike position (sliders, or click the sheet) and radius, listener position (sliders, or shift-click) or the whole sheet summed, the difference between strikes, ring times, pitch of the lowest mode (held so the top stays under 20 kHz), and length. Panels: the sheet from above with heights as color (with slow motion), strike A's energy per frequency band over time, the share above 1 and 4 kHz for all strikes, their final spectra by band, the local Lyapunov exponent with the energy, and a spectrogram of any strike. Presets: Soft ($10^3$), Gong ($10^4$), Crash ($3 \times 10^4$), Flat (no dome), all regular; and two chosen from the scan to be chaotic: Chaotic gong (bending 8, $E = 3 \times 10^4$, high ring 2 s, lowest mode 150 Hz; final spectra differ by 1.5 dB in the browser) and Chaotic crash (bending 25, $E = 10^5$, high ring 2 s; 1.1 dB). Computing a preset takes about 5 to 10 s on a 12-core laptop; the Lyapunov run, which also integrates the tangent dynamics, takes about three times as long as a strike and sets the wait. The step code works on zero-padded arrays with the substeps fused into three passes; it gives bit-for-bit the same output as the plain version and runs about 1.5 times faster. Locally: `cd web && python -m http.server`.
47## Running
49Experiments run on the office workstation (`~/work/fput-plate`, Python 3.9 with numpy), plots locally:
51 WORKERS=12 python experiments.py # results/explore.npz (weaker strikes)
52 TAG=explore2 WORKERS=12 python experiments.py # results/explore2.npz (harder strikes)
53 python plots.py # results/explore.png
↑↓ move↵openescclose