Animated groups · reports · clockwork orbits

How to find a time-symmetric animation

How the looping spiral-lattice animation with clockwork symmetry r33₁3₂ was actually found: the five steps of the search that produced it, re-run for this page and reproducing the published file bit for bit. One equation throughout, the Brusselator.

The animation#

The published animation: one period of 6.390159918584649 time units in 48 frames, two cells across. Drawn on it, as in the correspondence catalogue, are the three generators of its symmetry r33₁3₂. Each is a 120° anticlockwise turn about the point it marks, and the number beside it is what the turn costs, as a fraction of the period: about γ it changes nothing, about α it gives the film a third of a period later, about β two thirds later. At α and β the chemicals never move; the film turns around them.

Three different things go into this film, and the page keeps them apart: a symmetry, which says how the picture repeats in space and time; an equation, which says how the chemicals change from one moment to the next; and a method, which finds a film that obeys both.

Its symmetry#

In orbifold notation the symmetry group is r33₁3₂. Each 3 is a kind of centre about which the picture can be turned by 120°: plain 3 is γ in the picture above, 3₁ is α and 3₂ is β. The subscript counts the thirds of a period that an anticlockwise turn about that centre costs. So turning the picture about γ changes nothing; about α it shows the film as it will be a third of a period later; about β, two thirds later. The prefix r says that sliding the picture by a third of a cell costs a third of a period as well.

Written out, with q=(u,v)q = (u, v) the two chemicals and each of the nine symmetries gg of the simulation box carrying its cost τg∈{0,13,23}\tau_g \in \{0, \tfrac13, \tfrac23\}:

q(gx, t+τgT)=q(x,t)q(g x,\ t + \tau_g T) = q(x, t)

for every symmetry gg, every point xx, every instant tt.

Only three of the nine cost nothing: doing nothing, and the two turns about γ. Those are all a single frame shows. The other six relate a frame to the one a third or two thirds of a period later.

The correspondence catalogue lists this group under ³3³3¹3: the same orbifold, with each centre marked by how many different moments its turns reach (³3 for α and β, ¹3 for γ). Its number there, 227, is only its place in Xu and Wu's list of the 275 spacetime groups.

The equation#

One equation, the Brusselator: two chemicals, uu and vv, react and diffuse at the same rate.

ut=∇2u+a−(b+1) u+u2v,vt=∇2v+b u−u2v,\begin{aligned} u_t &= \nabla^2 u + a - (b+1)\,u + u^2 v, \\ v_t &= \nabla^2 v + b\,u - u^2 v, \end{aligned}

with a=1a = 1, b=2.3b = 2.3, on a triangular mesh of 36 × 36 nodes in a periodic box of side 40.

Nothing in the equation knows about the symmetry. What has to be found: the film repeats with period TT, so a single frame q0q_0 and the number TT determine all of it; everything else comes from simulating forward. The search is for one frame, 2 × 36 × 36 = 2 592 numbers, and one period. Nobody chooses TT; it comes out as 6.390159918584649.

Why simulating does not work#

The obvious approach is to start somewhere, simulate, and wait. It fails for two reasons.

Why you cannot simply simulate. This starts exactly on the published orbit, disturbed only by single-precision round-off, and simulates forward with the three zero-cost symmetries imposed at every step. One frame per period, 313 periods: the spirals fade, by period 135 none are left, and every point ends up oscillating in unison.

So instead of simulating towards the answer, the method writes the requirement as an equation for the frame and the period and solves it by Newton's method, which never asks whether a solution is stable. Newton's method needs a good first guess, and most of the work goes into that guess.

The method in five steps#

  1. The rest state and how it oscillates → a first guess for the period, and how uu and vv swing together.
  2. A starting pattern from the Brusselator's slow envelope → a pattern with the right symmetry, grown from random noise.
  3. Turn it into a Brusselator frame → the first guess for step 4.
  4. Solve for one frame and one number → the exact frame and period, by Newton–Krylov.
  5. Make the film and check it → the published animation.

Step 1 · The rest state and how it oscillates

Where nothing varies in space, the chemicals can sit still at u∗=a=1u^* = a = 1, v∗=b/a=2.3v^* = b/a = 2.3, where production and consumption balance. Linearise the reaction there:

J=(b−1a2−b−a2)=(1.31−2.3−1),eigenvalues λ=0.15±0.9887 i.\begin{gathered} J = \begin{pmatrix} b-1 & a^2 \\ -b & -a^2 \end{pmatrix} = \begin{pmatrix} 1.3 & 1 \\ -2.3 & -1 \end{pmatrix}, \\ \text{eigenvalues } \lambda = 0.15 \pm 0.9887\, i. \end{gathered}

The positive real part means the rest state is unstable: any small disturbance starts to oscillate, and grows into a steady oscillation. (That happens once b>1+a2=2b > 1 + a^2 = 2; at b=2.3b = 2.3 we are just past it.) The imaginary part gives the first guess for the period, T0=2π/0.9887=6.3551T_0 = 2\pi/0.9887 = 6.3551, within 1 % of the final answer. The eigenvector, e=(1, −1.15+0.9887 i)\mathbf e = (1,\ −1.15 + 0.9887\,i), says how vv moves relative to uu in that oscillation; step 3 uses it.

Step 2 · A starting pattern from the Brusselator's slow envelope

Close to the onset of oscillation, every point of the Brusselator swings almost like a sine wave at the frequency of step 1. What varies across space and time is only how strongly each point swings, and in which phase. Put those two into one complex number per point, A(x,t)A(x, t): the envelope of the oscillation. Near onset the Brusselator reduces to an equation for the envelope alone,

At=A+(1+iα) D ∇2A−(1−iβ) ∣A∣2A,A_t = A + (1 + i\alpha)\, D\, \nabla^2 A - (1 - i\beta)\, |A|^2 A,

whose constants come from the Brusselator: α=0\alpha = 0 because the two chemicals diffuse equally, and β=0.11\beta = 0.11, close to the Brusselator's own value 1/91/9 at a=1a = 1. The job set D=2D = 2 and measured the box in envelope units as 402μ=30.9840\sqrt{2\mu} = 30.98, with μ=b−1−a2=0.3\mu = b - 1 - a^2 = 0.3 the distance past onset.

The envelope has one property the full Brusselator lacks: multiply a solution by a constant phase eiφe^{i\varphi} and you get another solution. So its simplest periodic solutions are rotating waves, A(x,t)=B(x) eiΩtA(x,t) = B(x)\, e^{i\Omega t}: a fixed pattern whose phase turns steadily. For such a solution, waiting a fraction τ\tau of the period just multiplies the whole field by e2πiτe^{2\pi i \tau}, so the symmetry of the film becomes

B(gx)=e−2πiτg B(x)B(g x) = e^{-2\pi i \tau_g}\, B(x)

for every symmetry gg: a condition on one frame.

It is linear, so any field can be made to satisfy it by averaging over the nine symmetries, (PB)(x)=19∑ge2πiτgB(gx)(PB)(x) = \tfrac19 \sum_g e^{2\pi i \tau_g} B(g x). And because the envelope equation respects both the turns and the phase rotation, a field that satisfies the condition keeps satisfying it as it is simulated. The job did exactly that:

  1. Start from random complex noise (random seed 1) and apply PP.
  2. Simulate the envelope 50 time units at a time, applying PP again after each stretch (only to remove round-off), and test whether the field has become a rotating wave, that is, whether At=iΩAA_t = i\Omega A for a single number Ω\Omega. The job allowed 600 time units.
  3. Polish: solve F(B)=iΩBF(B) = i\Omega B to round-off with Newton's method, where FF is the right-hand side of the envelope equation.
Step 2 as it ran. Brightness is ∣B∣|B|, how strongly each point of the envelope swings (each panel on its own scale, two cells across, the same view as the film at the top). Random noise organises within about 12 time units into a lattice with dark dots where nothing swings.

The dark dots were predicted before any simulation. A turn about α leaves the point α where it is, so there the condition reads B(α)=e−2πi/3B(α)B(\alpha) = e^{-2\pi i/3} B(\alpha), which forces B(α)=0B(\alpha) = 0; the same holds at β. They are the points α and β of the film at the top, where the chemicals never move.

By t=50t = 50 the envelope is a rotating wave to 1.9e−8; the finite time step keeps it from getting closer than 3.4e−9, short of the job's test of 1e−9, so the run goes the full 600 time units (12 000 steps) and one polishing step finishes at 1.5e−14. The envelope is an approximation of the Brusselator, good near onset: the seed needs the right symmetry and roughly the right shape, and step 4 does the rest.

The sign. The minus in e−2πiτge^{-2\pi i \tau_g} assumes the phase turns forwards. The job did not work that out in advance; it tried e+2πiτge^{+2\pi i \tau_g} first. That also settles into a rotating wave, but with its clock running backwards: ⅓ and ⅔ swap, so α and β trade places. Checked against the catalogue entry it fails, with an error of 1.73 against a limit of 2e−7; the minus sign passes at 1.4e−15.

Step 3 · Turn it into a Brusselator frame

The envelope pattern becomes a frame for the two chemicals through the eigenvector of step 1:

q0(x)=q∗+A Re⁡(B(x) e)rms.q_0(x) = q^* + A\, \frac{\operatorname{Re}\big(B(x)\, \mathbf e\big)}{\text{rms}}.

Taking the real part gives a snapshot of the oscillation at one instant; multiplying by e\mathbf e makes vv lag uu the way the oscillation requires; the field is scaled to unit root-mean-square and then by the amplitude A=0.25A = 0.25. (Had Newton failed from 0.25, the job would have tried 0.50; it did not need to.) The period guess is T0=6.3551T_0 = 6.3551 from step 1. This seed has the right symmetry, inherited from BB, but not the right shape or size: it misses the equation of step 4 by 6.9e−2.

Step 4 · Solve for one frame and one number

The equation. Take a candidate frame q0q_0 and period TT. Simulate the Brusselator for a third of a period. Then apply a symmetry gg that costs a third of a period, say an anticlockwise turn about α. If (q0,T)(q_0, T) is the answer, you are back where you started:

F(q0,T)=Sg ΦT/3(q0)−q0=0.F(q_0, T) = S_g\, \Phi_{T/3}(q_0) - q_0 = 0.

Here Φs\Phi_s means "simulate forward for time ss" (RK4, time step 0.02, no symmetry imposed), and SgS_g moves every value to where gg sends it. A third of a period is enough: doing it three times gives the whole period, and the ⅔ relations follow. Two more ingredients make the problem square:

Newton–Krylov. Newton's method linearises FF at the current guess and solves the resulting linear system for a correction. The matrix of that system has one column per unknown, and each column would cost a simulation, so it is never built. A Krylov solver (LGMRES) needs only the matrix times a vector, and that is one extra simulation: (F(z+εv)−F(z))/ε\big(F(z + \varepsilon v) - F(z)\big)/\varepsilon. That is all "Newton–Krylov" means. The job used SciPy's root(method='krylov'), with the period as log⁡T\log T so it stays positive.

Newton stalls for 35 steps, then converges in 8

Newton residual by iteration: flat near 6.9e−2 for 35 iterations, then down to 5.5e−16 in 8 more. 1 10−4 10−8 10−12 10−16 0 10 20 30 40 43 Newton iteration 35 steps that barely move 5.5e−16 iteration 0: residual 6.91e−2, T = 6.355087901 iteration 1: residual 6.90e−2, T = 6.355376850 iteration 2: residual 6.90e−2, T = 6.355640164 iteration 3: residual 6.89e−2, T = 6.355908375 iteration 4: residual 6.89e−2, T = 6.356217922 iteration 5: residual 6.88e−2, T = 6.356646522 iteration 6: residual 6.87e−2, T = 6.357485507 iteration 7: residual 6.87e−2, T = 6.359399204 iteration 8: residual 6.86e−2, T = 6.358540913 iteration 9: residual 6.86e−2, T = 6.358765997 iteration 10: residual 6.86e−2, T = 6.358634143 iteration 11: residual 6.86e−2, T = 6.358760603 iteration 12: residual 6.86e−2, T = 6.358725697 iteration 13: residual 6.97e−2, T = 6.363100669 iteration 14: residual 6.91e−2, T = 6.355541294 iteration 15: residual 6.91e−2, T = 6.355867273 iteration 16: residual 6.90e−2, T = 6.356345367 iteration 17: residual 6.89e−2, T = 6.357412892 iteration 18: residual 6.89e−2, T = 6.358794076 iteration 19: residual 6.89e−2, T = 6.358048852 iteration 20: residual 6.90e−2, T = 6.356641794 iteration 21: residual 6.89e−2, T = 6.358732336 iteration 22: residual 6.87e−2, T = 6.356632251 iteration 23: residual 6.87e−2, T = 6.357124595 iteration 24: residual 6.85e−2, T = 6.358245639 iteration 25: residual 6.85e−2, T = 6.358917788 iteration 26: residual 6.85e−2, T = 6.359072885 iteration 27: residual 6.85e−2, T = 6.358629551 iteration 28: residual 6.85e−2, T = 6.359601405 iteration 29: residual 6.84e−2, T = 6.357315230 iteration 30: residual 6.84e−2, T = 6.359505330 iteration 31: residual 6.84e−2, T = 6.358589162 iteration 32: residual 6.84e−2, T = 6.359059941 iteration 33: residual 6.84e−2, T = 6.358875481 iteration 34: residual 6.84e−2, T = 6.359107067 iteration 35: residual 6.53e−2, T = 6.362924196 iteration 36: residual 5.34e−2, T = 6.362740916 iteration 37: residual 2.16e−2, T = 6.464592811 iteration 38: residual 8.92e−3, T = 6.388589295 iteration 39: residual 1.10e−3, T = 6.391058216 iteration 40: residual 4.79e−5, T = 6.390270795 iteration 41: residual 5.66e−6, T = 6.390149298 iteration 42: residual 3.82e−11, T = 6.390159919 iteration 43: residual 5.48e−16, T = 6.390159919

Root-mean-square residual of FF against Newton iteration, on a log scale. Hover over the chart for each iteration's values, or open the table below.

For 35 iterations almost nothing happens: the residual goes from 6.9e−2 to 6.5e−2 and the period wanders between 6.355 and 6.363. The guess is outside the region where Newton's method converges quickly, and the steps are small and cautious. Then it gets close, and 8 iterations take the residual to 5.5e−16; in the last few, the number of correct digits roughly doubles each step, the signature of Newton's method. The whole solve took 43 iterations and 462 simulations of a third of a period, about 52 seconds on an ordinary laptop. The period it lands on is 6.390159918584649.

All 43 iterations
iterationresidual (rms)period TT
0 (seed)6.91e−26.355087901
16.90e−26.355376850
26.90e−26.355640164
36.89e−26.355908375
46.89e−26.356217922
56.88e−26.356646522
66.87e−26.357485507
76.87e−26.359399204
86.86e−26.358540913
96.86e−26.358765997
106.86e−26.358634143
116.86e−26.358760603
126.86e−26.358725697
136.97e−26.363100669
146.91e−26.355541294
156.91e−26.355867273
166.90e−26.356345367
176.89e−26.357412892
186.89e−26.358794076
196.89e−26.358048852
206.90e−26.356641794
216.89e−26.358732336
226.87e−26.356632251
236.87e−26.357124595
246.85e−26.358245639
256.85e−26.358917788
266.85e−26.359072885
276.85e−26.358629551
286.85e−26.359601405
296.84e−26.357315230
306.84e−26.359505330
316.84e−26.358589162
326.84e−26.359059941
336.84e−26.358875481
346.84e−26.359107067
356.53e−26.362924196
365.34e−26.362740916
372.16e−26.464592811
388.92e−36.388589295
391.10e−36.391058216
404.79e−56.390270795
415.66e−66.390149298
423.82e−116.390159919
435.48e−166.390159919
Step 4 as it ran: the u chemical of the frame Newton is working on, with its residual (each panel on its own scale). Through the long stall the picture hardly changes; within a few iterations after it, the honeycomb of the seed becomes the triangles of bright spots of the published film.

Notice what never comes up in this step: stability. F=0F = 0 holds for an unstable film just as well as for a stable one, which is exactly why this works where simulating did not.

Step 5 · Make the film and check it

The published file is the solved frame polished once more, 48 frames of one period, averaged over the nine symmetries so that symmetric samples are identical, and stored in single precision. Re-running all five steps for this page reproduces it exactly: the same SHA-256 hash, e47c12f9099f…, and a maximum difference of 0.

Where the symmetry comes from#

Does the Brusselator make this symmetric pattern by itself, or does the mesh? Neither. The symmetry is put in on purpose, in two steps, and after that only checked.

stepsymmetry put in
2 · envelopeall nine, as conditions on one frame, by averaging the noise at the start and the field every 50 time units
3 · liftnothing new: the seed inherits it
4 · Newtonthe three free symmetries, by averaging the frame; one symmetry that costs a third of a period is the equation itself; the other five follow from these four
5 · filmnothing: the Brusselator runs freely, and the film is checked against all nine
exportall nine, averaged once more so that symmetric samples are identical in the stored file

The averaging in step 2 is what makes the pattern. Run the same noise through the same envelope equation without it, and the symmetric part, 11 % of the noise at the start, is down to 3.5e−6 of the field by t=50t = 50; every point then oscillates in unison. The mesh does nothing beyond letting each symmetry carry mesh points onto mesh points, which is why its side, 36, is a multiple of 3.

Run it yourself#

The search ran on 7 September 2026 as one job of 204 in a batch, g227-bruss-a1b23-L40-s1: 53.75 seconds on a two-core cloud container, about $0.0023, no graphics processor. The script code/original.py runs the same job on your machine, one step at a time, with the repository's own functions, and records everything this page quotes, the control of the last section included:

git clone https://github.com/yaroslavvb/animated-groups-fable
cd animated-groups-fable/docs/reports/clockwork-orbits/code
python3 original.py   # ~2 minutes
python3 figures.py    # the pictures

original.py needs NumPy and SciPy and writes its record into original-run/; figures.py needs Pillow and redraws this page's picture panels from that record.

The functions it calls live in rdlab.py: cgl_relative_equilibrium and cgl_polish (step 2), seed_from_complex (step 3), rpo_polish (step 4) and audit (step 5). The job itself is run_brusselator in the batch driver, and the export is in build_equation_atlas.py. The code README explains what each output means.

Every setting the job used.
equationBrusselator, a=1a = 1, b=2.3b = 2.3, equal diffusion 1
meshtriangular, 36 × 36 nodes, box side 40; RK4, time step at most 0.02
symmetryr33₁3₂ (³3³3¹3 in the catalogue, entry g227), nine operations on the box
step 2envelope: α=0\alpha = 0, β=0.11\beta = 0.11, D=2D = 2, box 30.98; noise with random seed 1; up to 600 time units; sign ++ tried first, then −-
step 3amplitude 0.25 (0.50 as a fallback), T0=2π/ωT_0 = 2\pi/\omega
step 4Newton–Krylov, LGMRES with at most 40 inner steps, at most 60 Newton steps, tolerance 1e−11; accepted below 1e−8
step 596 frames; the site's audit limits; re-solve at 48 × 48