Beam: integrator step 0.2 / omega_eff (was 0.03): 6.5x fewer steps, top-mode pitch error 2.9 cents, statistically equivalent notes
2 changed files+10−4
src/beam/README.mdmodified+2−2View file
@@ -11,7 +11,7 @@ Live site: https://chaos-apps--listening-for-chaos.vault1-sites.magland.org/beam
1111 ## Model
1212
1313 - SI units. Defaults m = 1 kg, k = 0, κ = 20 N/m, k3 = 4000 N/m^3, a = 0.5 m (drawing only), τ = 540 s, local stretching, clamped ends.
14-- Resolution: the Parameters values are for the beam with 8 masses (REF_N); Masses (2 to 64) refines the same beam (`refine` in `beam.ts`): same length, rise, drag, strike energies; with spacing ratio r = span(8) / span(N) (span N + 1 spacings between two supports, N for a cantilever) m x r, k / r, κ / r^3, k3 / r^3, a x r, and the loss dashpots / r (τ top is converted so a wave of given length decays alike at any N). N = 8 returns the parameters unchanged (checked, all ends). The derived values show the scaled constants when N is not 8. Before this (to 2026-10-09) more masses made the beam longer at fixed spacing: mode 1 fell as ~1/N^2 (0.211 Hz at N = 8, 0.015 Hz at 32), so at fixed τ the note shrank (7.9 s at N = 8, 0.6 s at 32, 100 Hz pitch). Now (clamped, local, τ 540, τ top 50): f1 0.207 / 0.211 / 0.206 / 0.202 / 0.200 Hz at N = 4 / 8 / 16 / 32 / 64; f2/f1 2.24 / 2.59 / 2.71 / 2.75 / 2.754 and f3/f1 3.37 / 4.68 / 5.20 / 5.35 / 5.39 (beam theory 2.757, 5.404); pinned 3.998, 8.986 at 64 (4, 9); cantilever 6.265, 17.53 (6.27, 17.55); mode-1 decay ~375 s, mode 3 ~122 s; nonlinear scale 176 / 167 / 161 / 158 mJ at 8 / 16 / 32 / 64; default note 7.9 / 7.7 / 7.5 s. Cost: the step falls ~1/N^2 (0.58 ms at 8, 0.087 ms at 32 for 2.51 J), so the default note (with the Lyapunov shadow) takes ~1 s to compute at N = 8, 8.2 s at 16 and 55 s at 32 in the pane. The strike and listening masses are indices, so at larger N "mass 2" is a narrower blow nearer the end.
14+- Resolution: the Parameters values are for the beam with 8 masses (REF_N); Masses (2 to 64) refines the same beam (`refine` in `beam.ts`): same length, rise, drag, strike energies; with spacing ratio r = span(8) / span(N) (span N + 1 spacings between two supports, N for a cantilever) m x r, k / r, κ / r^3, k3 / r^3, a x r, and the loss dashpots / r (τ top is converted so a wave of given length decays alike at any N). N = 8 returns the parameters unchanged (checked, all ends). The derived values show the scaled constants when N is not 8. Before this (to 2026-10-09) more masses made the beam longer at fixed spacing: mode 1 fell as ~1/N^2 (0.211 Hz at N = 8, 0.015 Hz at 32), so at fixed τ the note shrank (7.9 s at N = 8, 0.6 s at 32, 100 Hz pitch). Now (clamped, local, τ 540, τ top 50): f1 0.207 / 0.211 / 0.206 / 0.202 / 0.200 Hz at N = 4 / 8 / 16 / 32 / 64; f2/f1 2.24 / 2.59 / 2.71 / 2.75 / 2.754 and f3/f1 3.37 / 4.68 / 5.20 / 5.35 / 5.39 (beam theory 2.757, 5.404); pinned 3.998, 8.986 at 64 (4, 9); cantilever 6.265, 17.53 (6.27, 17.55); mode-1 decay ~375 s, mode 3 ~122 s; nonlinear scale 176 / 167 / 161 / 158 mJ at 8 / 16 / 32 / 64; default note 7.9 / 7.7 / 7.5 s. Cost: the step falls ~1/N^2 (0.58 ms at 8, 0.087 ms at 32 for 2.51 J), so the default note (with the Lyapunov shadow) took ~1 s to compute at N = 8, 8.2 s at 16 and 55 s at 32 in the pane at step 0.03; at 0.2, 0.3 s, 1.5 s and 7.9 s. The strike and listening masses are indices, so at larger N "mass 2" is a narrower blow nearer the end.
1515 - Masses on rails: only sideways motion u_j, so every axial force is taken by the rails and tension and stretching are defined at a free end too.
1616 - Potential: springs between neighbours (and to each support), k d^2 / 2 with d = u_j - u_{j-1}; bending κ c^2 / 2 at each mass with both neighbours, c = u_{j-1} - 2 u_j + u_{j+1} (supports at u = 0). Pinned support: no extra term (ghost u_{-2} = -u_0 gives c = 0 there). Clamped: a joint at the support with ghost u_{-2} = u_0, c = 2 u_0, energy 2 κ u_0^2. Free end: no spring or joint beyond the last mass.
1717 - Stretching: "local", k3 d^4 / 4 per spring (FPUT); or "Kirchhoff", k3 S^2 / (4 nS) with S the sum of d^2 over the nS springs, which acts as one tension k3 S / nS shared by all springs. They agree when all springs are stretched alike.
@@ -22,7 +22,7 @@ Live site: https://chaos-apps--listening-for-chaos.vault1-sites.magland.org/beam
2222 - Nonlinear scale: the energy of mode 1 alone at the amplitude where its stretching energy equals its linear energy (N = 8, local: clamped 176 mJ, pinned 8.7 mJ, cantilever 1.1 mJ).
2323 - Damping: drag proportional to mass, every mode's amplitude decays as exp(-t / τ); applied exactly in half steps around each Verlet step, dissipated energy in W.
2424 - Frequency-dependent loss (default off): a dashpot alongside each spring, force -c (L v) with L the springs' second-difference matrix (the discrete form of Bilbao's plate loss term), so short waves lose more. Set by τ top, the amplitude decay time it gives the top of the band, c = 2 m / (τ top λmax(L)); a mode with q = S L S decays at c q / 2m on top of the drag. The derived values list each mode's decay time (drag and loss together; for clamped and cantilever ends the loss is treated as diagonal in the modes). Integrated as two Crank-Nicolson half steps around the Verlet step (tridiagonal, O(N), never adds energy); the dissipated energy goes into W. Checked (Node): τ top = 0 bit-identical to before (all ends, both forms, straight and curved); predicted and measured mode decay times agree to 1 to 2% (N = 8, τ top 5 s: clamped 122 s for mode 1 down to 5 s for mode 8); energy balance <= 1.5e-5 (N = 16, 10 J, 300 T1); N = 64 step 1.0 -> 2.4 µs. Default strike with τ top = 50 s (decay times 374 s for mode 1 down to 46 s): the upper lines die first and the note settles onto mode 1; spectral centroid 1000 Hz at 0.1 s, 160 Hz at 1 s, 100 Hz from 2 s (without loss 860 Hz at 1 s, 840 Hz at 3 s); note 3.6 s instead of 7.8 s. The header's predicted note length uses the slowest mode, so it overestimates when most of the energy starts in fast-decaying modes (5.5 s predicted here).
25-- Integrator: velocity Verlet, step 0.03 / omega_eff, omega_eff^2 = (Kmax + 12 k3 d^2) / m, Kmax the top eigenvalue of the stiffness matrix and d = (4 E / k3)^(1/4) (Kirchhoff: the k3 term over sqrt(nS)). Relative energy error <= 8e-5 over 200 mode-1 periods, all ends and both forms, 10 mJ to 100 J.
25+- Integrator: velocity Verlet, step 0.2 / omega_eff (0.03 until 2026-10-09), omega_eff^2 = (Kmax + 12 k3 d^2) / m, Kmax the top eigenvalue of the stiffness matrix and d = (4 E / k3)^(1/4) (Kirchhoff: the k3 term over sqrt(nS)). Relative energy error <= 3.5e-3 over 100 mode-1 periods (bounded wobble, no drift; at 0.03 it was <= 8e-5), all ends, both forms, straight and curved, 10 mJ to 100 J, N = 8 and 16. Step looseness measured (Node): top-mode pitch error 0.06 cents at 0.03, 2.9 at 0.2, 6.5 at 0.3 (Verlet dispersion); λ, level and centroid of the default note at N = 8 and 16 show no step effect beyond the spread of 1e-9-jittered strikes up to 0.5; at N = 32 octave-band energies of the loud bands stay within ~2.5 dB of a 0.01 reference (0.03: 1.4 dB). 6.5x fewer steps at N = 32 (N = 8 is limited to about one step per audio sample). Earlier numbers in this README were measured at 0.03: same physics, different realizations of chaotic notes.
2626 - Default note (clamped, local, 2.51 J on mass 2, τ 540 s, 100 Hz): λ ~0.27 /s at the strike (λ T1 ~1.3), falling into the noise of the 10 s window (±0.05 /s) by ~1.6 s of sound (E ~0.14 J), as the spectrogram resolves into lines. Kirchhoff at the same strike: ~0.04 /s, then noise. A 50 mJ strike: noise throughout. Cantilever: ~0.2 /s for the first ~0.3 s of its 1.3 s note. On Modes, the shares shuffle while λ is up and freeze into steady stripes as it falls (~2 s of sound); on Mode bars about 6 to 7 of the 8 modes share the energy early on. λ vs E over a 2.51 J and a 25 J note (clamped, local): λ in the noise around 0 at low energy, above it from roughly 0.3 J, ~1.4 /s at 25 J (25 J note: 7.5 s, computed in 2.1 s).
2727 - First look (offline, N = 8, hammer on mass 2, no damping, two-trajectory Lyapunov exponent times the mode-1 period T1): local stretching is chaotic from ~1 J (clamped 0.57 at 1 J, 3.3 at 10 J; pinned 1.8 at 1 J; cantilever 1.2 at 0.1 J). Kirchhoff is much less chaotic at the same energy (clamped 0.04 at 1 J, 0.24 at 10 J; pinned ~0 up to 1 J, 0.09 at 10 J).
2828
src/beam/beam.tsmodified+8−2View file
@@ -412,16 +412,22 @@ export function decayTimes(P: Params, modes: Modes): Float64Array {
412412 });
413413 }
414414
415-// Integrator step (s) for motion with energy E: 0.03 / omega, with omega from
415+// Integrator step (s) for motion with energy E: STEP / omega, with omega from
416416 // the stiffest the beam can be. Linear part: the top mode, Kmax. Stretching
417417 // adds at most 12 k3 d^2 + 24 k3 D d with d = (4 E / k3)^(1/4), the largest
418418 // offset a spring holding all of E could reach, and D the largest rest offset
419419 // (Kirchhoff spreads it over nS springs, sqrt(nS) less).
420+// omega h at the top of the band. 0.2 (was 0.03 until 2026-10-09): Verlet
421+// raises the top mode's pitch by 2.9 cents (lower modes far less) and the
422+// energy wobbles by up to ~0.4% (bounded, no drift); λ, level and brightness
423+// of a note are indistinguishable from 0.03 (measured up to N = 32), at 6.5x
424+// fewer steps. A note at another step is a different, equivalent realization.
425+const STEP = 0.2;
420426 export function stepFor(P: Params, modes: Modes, E: number): number {
421427 const d2 = P.k3 > 0 ? Math.sqrt((4 * Math.max(E, 0)) / P.k3) : 0;
422428 const D = restOffsets(P).reduce((a, x) => Math.max(a, Math.abs(x)), 0);
423429 const nl = (12 * P.k3 * d2 + 24 * P.k3 * D * Math.sqrt(d2)) / (P.stretch === "kirchhoff" ? Math.sqrt(springCount(P)) : 1);
424- return 0.03 * Math.sqrt(P.m / (modes.Kmax + nl));
430+ return STEP * Math.sqrt(P.m / (modes.Kmax + nl));
425431 }
426432
427433 // Strikes from rest. All the energy starts as kinetic energy, so the total is