Model Description
A faithful, code-aligned description of the inhibition-field model behind the simulator.
TL;DR — how it works (one screen)
- New primordia are born on a fixed ring at radius
R. - Each step all primordia drift outward by
v * dt. - The next angle is chosen by minimizing an inhibition field F(θ) = Σ K(d) across
angleSamplescandidates. - The discrete argmin is refined by a parabolic refinement to obtain θ*.
- Optional Gaussian noise perturbs θ*; divergence mean/std are reported.
Overview (what we model)
We model a growing shoot apex as a 2D disk with a birth ring of radius . Primordia are point-like organs defined by polar coordinates . They are created on the ring () and then drift outward over time. Each existing primordium contributes an inhibitory influence. A new primordium appears at the angle where the total inhibition is minimal.
This is an inhibition‑field model in the spirit of Douady–Couder, implemented as a fast discrete optimizer over candidate angles.
[!KEY IDEA] Spiral counts emerge from repeated argmin of an inhibition field on the birth ring while older primordia drift outward.
The field value at angle θ is computed by summing kernel contributions from active primordia using distances dᵢ(θ) from the ring point c(θ).
Objects & Parameters
Geometry and time
- : birth ring radius (
cfg.R) - : radial drift speed (
cfg.v) - : time step (
cfg.dt) - : cutoff radius for "active" primordia (
cfg.maxR)
Sampling / search
- : number of discrete candidate angles on (
cfg.angleSamples) - Parabolic refinement: sub-sample minimizer around the best discrete sample
Kernel (inhibition strength vs distance)
The kernel configuration is a tagged union:
- exp:
- gaussian:
- softPower:
- hardCoreExp: exp kernel + hard exclusion for
- custom: user expression via
expr-eval
Noise
Optional angular noise applied after is computed:
- enabled (
cfg.noise.enabled) - in degrees (
cfg.noise.sigmaThetaDeg) - seed (
cfg.noise.seed) for determinism
[!TRY THIS] Set
noise.enabled=falseto see a near‑deterministic convergence of divergence statistics; then enable noise with to observe a broader divergence distribution.
Step algorithm
Each simulation step repeats:
- Drift — all primordia move outward:
- Update active set — keep only primordia with
- Compute inhibition field — for each candidate angle, sum kernel contributions from active primordia
- Find θ* — pick the angle that minimizes
- Add primordium — place new point at
Optional Gaussian noise can be added to θ* before placement.
In the code, the active set is implemented as a moving suffix index (firstActiveIndex) because radii increase monotonically with age under constant drift.
The placement angle θ* is the argmin of F(θ) over the birth ring.
Inhibition field & argmin(θ)
For a candidate angle , define the candidate point on the ring:
Each active primordium has Cartesian position:
Distance to the candidate:
Total inhibition field:
Placement rule:
In code, the optimizer is "grid search + parabolic refinement":
[!IMPLEMENTATION MAPPING]
- Discrete scan and argmin:
src/sim/simulator.ts→findMinimumFieldAngle()- Refinement uses and clamps offset to
Kernels
exp
gaussian
softPower
hardCoreExp
Same as exp, but invalidates a candidate angle if any active primordium is closer than :
custom
Users can provide an expression string like:
A * exp(-d / lambda)
The simulator evaluates it with a stable parameter scope and safety guards (exceptions / NaN / Infinity → penalty).
[!IMPLEMENTATION MAPPING]
src/sim/kernel.ts→compileKernel()(all kernel types) andvalidateExpression()(custom kernels).
Metrics (divergence mean/std)
The simulator reports divergence angle statistics over the most recent steps (default ):
[!IMPLEMENTATION MAPPING]
src/sim/simulator.ts→computeDivergenceMetrics(N=200)returns{mean, stdDev, count}in degrees.
Why you sometimes see 19 spirals
Spiral counts (parastichies) are tied to how the emergent divergence angle is approximated by rationals.
- If is close to a rational , you get near‑alignments every steps, which can make a -family of spirals visually prominent.
- The golden angle corresponds to the "most irrational" number (continued fraction ), whose convergents are Fibonacci ratios — hence Fibonacci spiral pairs dominate.
- With different kernel reach, maxR, sampling resolution, or noise, the effective can drift toward other good rational approximations, temporarily highlighting non‑Fibonacci counts (like 19).
[!KEY IDEA] The simulator doesn't "pick Fibonacci numbers". It repeatedly minimizes ; visible spiral counts are an emergent geometric consequence of the resulting divergence statistics.
Mapping to code
Core algorithm and data structures:
- Simulation step:
phyllotaxis-modelling/src/sim/simulator.ts→step() - Drift:
driftAllPrimordia() - Active set as suffix:
firstActiveIndex+advanceFirstActiveIndex() - Active arrays:
buildActiveArrays()stores denseactiveXs/activeYsfor speed - Argmin field + refinement:
findMinimumFieldAngle() - Noise:
GaussianRNGinsrc/sim/prng.ts+wrapAngle() - Kernel compilation:
src/sim/kernel.ts - Config & presets:
src/sim/config.ts(defaultConfig(),PRESETS) - Validation:
src/sim/validation.ts
UI hooks (for context):
src/ui/ui.tswires controls, import/export, and field plot toggles.
Performance notes
This implementation is optimized for a tight inner loop:
- Candidate angle arrays are precomputed once at init (
candidateCos,candidateSin). - Active set is a moving suffix (older primordia drift outward monotonically).
- Active coordinates are stored in dense
Float64Arrays each step for cache-friendly iteration. - Hard-core kernel can early-exit with a large penalty.
FAQ
What does the kernel actually do?
The kernel sets how strongly a primordium inhibits candidate positions at distance . Changing the kernel shape (and its length scale like or ) changes how many primordia matter at once and how sharp the minimum of is.
Why do we need angle sampling + refinement?
We approximate by scanning a discrete grid of angles for speed, then recover sub-grid precision by fitting a parabola to the minimum sample and its two neighbors.
References
How these papers relate to the model
| Model feature | Key references |
|---|---|
| Argmin on inhibition field ("least crowded spot") | Hofmeister → Douady & Couder |
| Kernel (local repulsion) | Douady & Couder, Levitov, Adler |
| Golden angle / Fibonacci emergence | Douady & Couder, Adler, Levitov |
| Mathematical lattice theory | van Iterson |
| L-systems / computational approach | Prusinkiewicz & Lindenmayer |
Core bibliography
-
Hofmeister, W. (1868). Allgemeine Morphologie der Gewächse. Leipzig: Engelmann. — First statement of the rule: each primordium forms in the "least crowded" spot on the meristem.
-
van Iterson, G. (1907). Mathematische und mikroskopisch-anatomische Studien über Blattstellungen. Jena: Fischer. — First purely mathematical treatment; introduced cylindrical lattice packing that underlies spiral counts.
-
Adler, I. (1974). A model of contact pressure in phyllotaxis. Journal of Theoretical Biology, 45, 1–79. — Shows that contact pressure leads to Fibonacci progressions and convergence to the golden angle.
-
Levitov, L. S. (1991). Energetic approach to phyllotaxis. Europhysics Letters, 14(6), 533–539. — Variational/energy-minimization derivation of why noble numbers (including golden ratio) are selected.
-
Douady, S. & Couder, Y. (1992). Phyllotaxis as a physical self-organized growth process. Physical Review Letters, 68(13), 2098–2101. — Landmark experiment: ferrofluid droplets on a ring spontaneously produce Fibonacci spirals via local inhibition.
-
Douady, S. & Couder, Y. (1996). Phyllotaxis as a dynamical self-organizing process (Parts I–III). Journal of Theoretical Biology, 178, 255–312. — Full theoretical model: iterative placement, bifurcation diagrams, and the "Hofmeister rule" formalized.
-
Prusinkiewicz, P. & Lindenmayer, A. (1990). The Algorithmic Beauty of Plants. New York: Springer-Verlag. — Computational models (L-systems) for plant morphogenesis including phyllotaxis.
Further reading (biological mechanisms)
-
Reinhardt, D. et al. (2003). Regulation of phyllotaxis by polar auxin transport. Nature, 426, 255–260. — Molecular evidence that auxin redistribution implements effective repulsion between primordia.
-
Kuhlemeier, C. (2007). Phyllotaxis. Trends in Plant Science, 12(4), 143–150. — Review connecting classical models to modern auxin-based understanding.