Repository navigation
Math Wave Equation
The Ripple view is a pond: a two-dimensional membrane, a grid of heights and velocities, kicked by the processes that run on the machine. This page is the mathematics under it, independent of any one view: where the wave equation comes from, what a finite-difference scheme does to it, why there is a speed limit on the time step, how much the lattice distorts the waves, and how a boundary that is only a grid edge can be made to swallow them. Every parameter quoted for isotop is read from src/medium.rs; the view that uses the scheme is View Ripple.
The page keeps three things apart. Theory is standard numerical analysis and physics and would be the same in any program. Code is what Wave in medium.rs does. Inference is what the pictures let you conclude. Numbers marked measured come from a stdlib Python port of Wave::step and Wave::advance that reproduces the Rust unit test waves_spread_at_their_speed_and_die_out_at_the_border (a ring at
The equation was first written for a violin string. Jean le Rond d'Alembert derived the one-dimensional wave equation in 1747 and found its general solution as the sum of two shapes travelling in opposite directions. Euler took it up in 1748 and 1749 and showed how to build the solution from the initial shape. Daniel Bernoulli countered that any motion of the string is an infinite sum of sine harmonics, without being able to say how to find the coefficients. The three disagreed for decades over a question that sounds modest: whether an initial shape with a corner, such as a plucked string, counts as a function at all. D'Alembert and Euler wanted a single analytic expression; Bernoulli argued on physical grounds that the plucked string existed whether or not the mathematics approved.
Fourier's work on heat (presented 1807, published in book form in 1822) supplied the missing coefficient formula, and Dirichlet's 1829 convergence theorem is the usual date given for the end of the argument. The two-dimensional membrane and the three-dimensional wave followed in the 19th century; Hadamard's Lectures on Cauchy's Problem (1923) gave the reason that sharp signals leave no tail in three dimensions but do in two, the fact the Huygens section above relies on.
The lattice came later and from a different motive. Courant, Friedrichs and Lewy published their paper in Mathematische Annalen in 1928 to prove that solutions of such equations exist, by showing that finite-difference approximations converge. The condition on the time step that every explicit wave solver since has had to respect appeared there as a by-product, and the machines it would constrain did not yet exist. The sponge at the border descends from the 1970s and 1980s work on artificial boundaries (Engquist and Majda 1977; Israeli and Orszag 1981); Berenger's perfectly matched layer followed in 1994.
A membrane of surface density
In one dimension this is d'Alembert's equation,
Real water is not this medium. Surface waves on a pond obey
Wave in medium.rs integrates a damped Klein-Gordon equation with a forcing term:
with WAVE_SPEED), a background DAMPING) raised near the border by a sponge (below), RESTORE), and
Put
Three consequences, each used later.
-
Cutoff. With
$\gamma = 0$ ,$\omega^2 = c^2k^2 + \kappa$ : waves exist only above$\omega_c = \sqrt{\kappa} = 0.707\ \mathrm{rad/s}$ (0.113 Hz). The pebbles sound at 0.35 to 1.25 Hz, well above it. -
Mild dispersion. Phase speed
$v_\varphi = c,\omega/\sqrt{\omega^2-\kappa}$ and group speed$v_g = c\sqrt{\omega^2-\kappa}/\omega$ . At 0.35 Hz these are$1.056,c$ and$0.947,c$ ; at 1.25 Hz,$1.004,c$ and$0.996,c$ . The restoring term makes long waves slightly dispersive, and that is a larger departure than the lattice causes at the same end of the band. -
Spatial decay of a driven wave. At real driving frequency the wavenumber is complex,
$k^2 = (\omega^2 - \kappa + i\gamma\omega)/c^2$ , and for$\gamma \ll \omega$
With
The response of the undamped two-dimensional equation to an initial velocity impulse
with
Cover the plane with a square grid of spacing
The five-point Laplacian
The leading error depends on direction: it is
Wave::step uses the nine-point stencil
which is a blend
The medium.rs ("its error is nearly isotropic, so rings stay round instead of squaring off along the grid axes") is exactly this. I checked the expansions numerically: at
The measured spread between axis and diagonal falls as
Put a plane wave in the semi-discrete system:
Wave::step updates, for each interior node,
with the new
the classical second-order central scheme; the stored
so time stepping adds
For
The band diagram (right) shows what the pebbles use: their
Take one Fourier mode. Let
Both eigenvalues lie in the unit disc if and only if
The worst mode is the highest-frequency one,
I confirmed this by computing the spectral radius of
Damping lowers the limit: at the sponge's maximum
What isotop does. Wave::advance(time, force) clamps the elapsed time to steps = ceil(elapsed * WAVE_SPEED / 0.5) equal substeps, so --fps 10 (elapsed 0.1 s) that is three substeps of 0.0333 s, f32; the margin is wide enough that rounding is not a stability concern (the unit test runs it for 30 s without growth).
step loops over 1..h-1 and 1..w-1: the outermost ring of nodes is never updated, and drive refuses to write within one cell of it. Those nodes stay at
Wave::new raises the damping near each edge. With
This is a sponge layer in the sense of Israeli and Orszag (1981), not a perfectly matched layer (Berenger 1994) and not a one-way radiation condition (Engquist and Majda 1977): it removes energy by friction, and a friction gradient reflects. A wave that enters the sponge loses amplitude by about
I measured the reflection coefficient with the exact scheme at normal incidence (one-dimensional run, which is exact for an axis-aligned plane wave because the nine-point stencil reduces to the three-point one when nothing varies in
| frequency | wavelength | amplitude reflected | energy reflected |
|---|---|---|---|
| 0.35 Hz | 40 cells | 0.46 | 21% |
| 0.50 Hz | 28 cells | 0.31 | 10% |
| 0.80 Hz | 17.5 cells | 0.18 | 3.2% |
| 1.00 Hz | 14 cells | 0.18 | 3.3% |
| 1.25 Hz | 11.2 cells | 0.19 | 3.4% |
At the short-wavelength end the reflection is the geometric-optics estimate 0.19; at long wavelengths the damping changes too quickly relative to the wavelength and the layer reflects like a partial mirror, up to 46% in amplitude at the lowest pebble pitch. Oblique waves should reflect more, since the component of the wavelength across the layer is longer (expected, not measured). The border therefore absorbs, but is not an absorbing boundary condition in the strict sense, and the background
cover (called by footprint in the Ripple view) pads the layout by margin = 8 + 0.08 * extent world units on each side and covers it with 150 cells on the longer side. With render::cover in render.rs and the 150-cell grid is the argument footprint in views/ripple.rs passes to it; the match with the sponge width is not stated in a comment, so I treat it as design intent supported by arithmetic, not as a documented invariant.)
Wave::drive(p, amount) adds amount times a fixed footprint to the velocity around the node nearest
A pebble drives the pond with a force
applied each substep as drive(at, loudness * sin(...) * dt), so that the velocity increment is
The steady response of the continuum equation to a point force of frequency
| symbol | name in medium.rs
|
value |
|---|---|---|
WAVE_SPEED |
14 cells per second | |
DAMPING |
0.35 per second | |
| sponge strength | SPONGE |
6 per second, quadratic in depth |
| sponge width | SPONGE_CELLS |
10 cells |
RESTORE |
0.5 per second squared | |
| frame step clamp | in advance
|
elapsed in |
| substeps | in advance
|
|
| grid | Grid::covering(..., 150) |
150 cells on the longer side (footprint in views/ripple.rs, through render::cover) |
| forcing footprint | Wave::drive |
|
| scheme | Wave::step |
nine-point Laplacian, symplectic Euler |
-
Wavelength and speed are real, the metric is not. In grid units everything above is exact for the scheme. World-space speed is
$14\times$ the cell size, and the cell size changes with the layout (150 cells always span the layout plus margin), so the pond crosses in 10.7 s whatever the machine looks like. Compare wavelengths in cells, never in the world units the camera uses. -
A ripple's reach is set by the damping, not by the process. Amplitude falls by
$e^{-1}$ in 79 cells at the pebble frequencies (before geometric spreading), independent of CPU. A louder process is a larger amplitude, not a longer range, until the amplitude falls under what the shading can show. - Short waves arrive late. A packet at the shortest pitch lags by about 3% over the crossing, a quarter wavelength. It matters for the phase relationship between distant sources and is invisible in the shading otherwise.
- The edge reflects some. At low pitches the sponge sends back 20% to 46% of the amplitude at normal incidence, so a faint standing pattern near the border is expected, not a bug.
-
Interference is exact superposition. The medium is linear, so any pattern is the sum of the single-source patterns; the marbled look of a busy pond is the sum of 130 or so Hankel waves of different pitch (in the demo at
--time 12, about 130 of 192 processes exceed the 0.3% threshold), not a nonlinear effect.
-
src/medium.rs:Wave(new,drive,step,advance, test-onlyenergy),Grid::covering, the constantsWAVE_SPEED,DAMPING,SPONGE,SPONGE_CELLS,RESTORE; testwaves_spread_at_their_speed_and_die_out_at_the_border. -
src/views/ripple.rs: theViewModuleimplementation ofRipple(sources,wave.advance) andfootprint(the grid over the pebbles). -
src/render.rs:cover(grid and margin),similar(when the medium survives a layout change). - Figure scripts:
docs/figures/wave-dispersion.py,docs/figures/wave-anisotropy.py,docs/figures/wave-sponge-reflection.py(shared drawing helperdocs/figures/figkit_w4.py).
- A perfectly matched layer. Replace the sponge by a split-field PML (Berenger) of the same 10 cells: the reflection should fall below 1% at all pitches and angles. The cost is auxiliary fields on the border ring only.
-
A fourth-order stencil. The
$k^4/12$ error of the nine-point Laplacian can be cancelled by the compact (Mehrstellen) correction on the right-hand side; that removes the$-k^2/24$ spatial error and leaves the time-stepping term$+\nu^2k^2/24$ , so at 11 cells the phase error would go from$-0.98%$ to$+0.33%$ (arithmetic, not an experiment). Would anyone see it? -
Real water. Swap the
$c^2k^2 + \kappa$ dispersion for capillary-gravity waves by adding a$\nabla^4$ term (a bending stiffness): short waves then run faster, the opposite of the lattice's lag, and the two could be tuned to cancel at one pitch. -
Anisotropy on purpose. A sheet with
$c_x \ne c_y$ would make the pond an anisotropic medium; the pebbles' rings become ellipses, and the stencil weights would have to follow. -
Measure the wake. The 2D Green's function predicts a positive tail behind a drip; compare the lattice response at a probe with
$1/\sqrt{c^2t^2-r^2}$ , with and without$\kappa$ , to see$\kappa$ turn the tail into ringing at$\omega_c$ . -
Kappa as a mass. The restoring term makes the pond a Klein-Gordon field with angular cutoff
$\omega_c = \sqrt{\kappa} = 0.707$ rad/s; at$c = 14$ cells per second its reduced Compton wavelength,$c/\omega_c$ , is 19.8 cells. The pond is, in this sense, a universe whose lightest particle is about 13 percent of its width. - Open: is the sponge's quadratic profile a good one for the 3 to 1 span of wavelengths here? A cubic or a cosine taper has lower reflection at long wavelengths for the same total damping.
- R. Courant, K. Friedrichs, H. Lewy, "Über die partiellen Differenzengleichungen der mathematischen Physik", Mathematische Annalen 100 (1928), 32-74.
- J. Hadamard, Lectures on Cauchy's Problem in Linear Partial Differential Equations (Yale University Press, 1923).
- R. Courant and D. Hilbert, Methods of Mathematical Physics, vol. II (Interscience, 1962).
- E. Hairer, C. Lubich, G. Wanner, Geometric Numerical Integration, 2nd ed. (Springer, 2006).
- J. W. Strikwerda, Finite Difference Schemes and Partial Differential Equations, 2nd ed. (SIAM, 2004).
- L. N. Trefethen, "Group velocity in finite difference schemes", SIAM Review 24 (1982), 113-136.
- R. Vichnevetsky and J. B. Bowles, Fourier Analysis of Numerical Approximations of Hyperbolic Equations (SIAM, 1982).
- M. Israeli and S. A. Orszag, "Approximation of radiation boundary conditions", Journal of Computational Physics 41 (1981), 115-135.
- B. Engquist and A. Majda, "Absorbing boundary conditions for the numerical simulation of waves", Mathematics of Computation 31 (1977), 629-651.
- J.-P. Berenger, "A perfectly matched layer for the absorption of electromagnetic waves", Journal of Computational Physics 114 (1994), 185-200.
- H. Lamb, Hydrodynamics, 6th ed. (Cambridge University Press, 1932).
- M. Patra and M. Karttunen, "Stencils with isotropic discretization error for differential operators", Numerical Methods for Partial Differential Equations 22 (2006), 936-953 (unverified page range).
isotop — maximalism for its own sake. Source: tfpickard/isotop. This wiki is published from wiki/ in the repository; edit it there. This footer is decorative.
Philosophy · Gallery · Glossary
Theory
Overview
Sampling: Linux
Sampling: macOS
Background sampler
Model and rates
Life events
History
Journal
Scene and display list
Camera and projection
Rasterization
GPU and parity
Picking
Terminal graphics
Stable layout
Determinism
Simulation toolkit
Colour
Text safety
Mathematics
Fourier epicycles
Wave equation
Flocking
Spatial hashing
Stellar photometry
Lorentz and Schlegel
Reaction-diffusion
Spiking neurons
Schwarzschild
Views
City · Orbit · Ripple
Flow · Cores · Cells
Strata · Globe · Reef
Coop · Matrix
Ptolemy · Tesseract
Necropolis · Cortex
Horizon · Murmuration
Arbor · HR
Hive · Switchboard
Armorial · Lights
Planned
Mold · Chamber
Anthill · Plumbing
Morph · Ouija
Canyon · Die
Guides
Writing a view
Recipes
Headless renders
Contributing to the wiki