New
Apps 272Makers 272This week 272
Promote your app−30%
SwarmSim screenshot 1
0views in
the last 30 days

SwarmSim

Computational simulations of emergent flocking, phase transitions, and predator-prey dynamics

Built with Claude Code

by ninjahawk · added Oct 29, 2026
no ratings0 comments
Open app Code

Emergent Flocking & Collective Evasion

PHY 351 — Independent Summer Research  |  UNCG

A computational study of force-based flocking agents on a periodic 2D domain, extended with predator-prey dynamics. Based on Charbonneau (2017), Ch. 10.

Where the Law Runs Out — the summer's results site

Three complex systems, one measured claim: typical behavior obeys sharp laws, and the events that matter escape them.

▶ Open Interactive Demo  ·  📋 Research Log


Project status — where we are

Summer complete (2026-07-18): 123 findings across three chapters — flocking F1–F94 (findings.md), sandpiles S1–S22 (findings_sandpile.md), earthquakes E1–E7 (findings_earthquake.md). The argument the summer adds up to, with its central figures, is on the results site: Where the Law Runs Out.

Flocking threads (first chapter), and whether they're closed:

Thread Findings State
Baseline & validation F1–F4 ✅ closed
Phase transition (is the solid→fluid a true transition?) F2, F8, F12, F17, F38–F40, F50 ✅ closed — it's a crossover; no base_r^n potential crystallizes
Predator strategy (2D + 3D) F5–F35, F43–F45, F49, F53, F65 ✅ closed — only encirclement disrupts, and only in 2D
Predator–prey arms race (predictive encirclement vs collective escape) F66–F71 ✅ closed
Contagion / vaccination / kinematic mixing F10–F37, F47–F48, F52, F54–F64 ✅ closed — slow-recoverer targeting is the one strategy that beats random
Leadership / collective decision-making (incl. many-wrongs) F72–F86 ✅ closed (paused)
Co-adaptation / evolution (heritable traits under selection) F87–F94 ✅ complete arc (newest work)

Most recent work — the co-adaptation thread (F87–F94): prey evolve a heritable escape weight under capture/removal predation. The F70 "dangerous valley" is a strong evolutionary brake (F87): escape is stable once present but can't evolve from scratch — it's a barrier to origination, not invasion (F88), and the resulting escape equilibrium is a robust mixed strategy (F89). With a co-evolving predator the arms race is asymmetric (F90), a self-test corrected an over-reading of it (F91), and under an energy-budget fitness model the brake proves model-independent while escape's persistence does not (F92). A second heritable trait, the alignment strength α, is also bistable — high alignment is a protective ESS that can't evolve from low (F93) — but unlike escape it does not invade from a seeded minority either (F94), because alignment is a mutual coupling rather than a free-rideable shared signal. All of it runs on a validated, fast harness (vectorized_predator.py, vectorized_predator_prey.py).

Where to pick up next: see the end of findings.md (Open Questions / Next Directions). The natural next experiments — a third fitness model (proximity-survival) or a two-trait predator (lead + aggression) — each rest on the same harness.


Second chapter — Sandpiles & Self-Organized Criticality

A new topic (Charbonneau Ch. 5), distinct from the flocking work below. Code in sandpile/, findings in findings_sandpile.md (S-series). The arc: validated 1-D core → rigorous critical exponents → the chapter's 2-D "Grand Challenge" → a universality comparison against the canonical abelian (Bak–Tang–Wiesenfeld) sandpile.

Headline result: the phenomenon of self-organized criticality (scale-free avalanches as a dynamical attractor, no parameter tuning) is robust, but the critical exponents are not — they depend on both dimension and toppling rule:

model avalanche exponent same class?
1-D slope sandpile τ_E ≈ 1.03 (D_E ≈ 2.0) —
2-D slope sandpile τ_E ≈ 0.87 ✗ differs from 1-D (purely dimensional; local rule identical)
2-D canonical BTW τ_S ≈ 1.14 (lit. ~1.2) ✗ differs from 2-D slope (τ_S ≈ 0.89)

The 2-D slope-vs-BTW split is now confirmed by a second, convention-free measure: the avalanche duration exponent, τ_T ≈ 0.56 (slope) vs ≈ 1.22 (BTW), stable across lattices up to 512 a side (S10). That measurement needed an active-list, numba-compiled engine (Exercise 5, S9) that reproduces the original engines bit-for-bit and runs ~600× faster in 2-D.

And the criticality is exact only because the bulk is conservative: breaking conservation (destroying a fraction of toppled sand) collapses the cutoff scaling from N² to sub-N¹ and truncates the avalanche distribution at a dissipation-set size — singling out conservation among the four SOC ingredients (the OFC earthquake-model sensitivity, in the simplest sandpile).

Validated against the chapter: mass conservation to 3×10⁻¹¹, N-independent power-law avalanche PDFs, initial-condition independence (the SOC attractor), and a clarification of the angle-of-repose claim (the mean slope sits ~16% below Z_c independent of grain size; only the peak pre-avalanche slope approaches Z_c as grains shrink).

sandpile/
  sandpile1d.py    1-D slope model (eqs 5.1–5.10), avalanche measurement
  validate1d.py    conservation, SOC signatures, IC-independence, eps-sweep
  fss1d.py         1-D finite-size scaling: τ_E, D_E, D_T + data collapse
  repose_peak.py   angle-of-repose mean-vs-peak slope reconciliation
  sandpile2d.py    2-D bond-slope generalization (Grand Challenge)
  fss2d.py         2-D finite-size scaling + 1-D-vs-2-D comparison
  btw_compare.py   canonical 2-D abelian BTW, same pipeline (universality)
  dissipation.py   break bulk conservation -> is conservation necessary for SOC?
  dissipation2d.py 2-D version of the conservation test (transfers from 1-D)
  duration_compare.py  duration-exponent universality cross-check (self-test, S6)
  falloff.py       boundary-evacuation avalanches vs bulk avalanches (Exercise 2)
  sandpile_fast.py active-list, numba-compiled 1-D+2-D engine (Exercise 5, S9)
  duration_fss2d.py large-L duration FSS; resolves S6 (S10)
  moments.py       moment-scaling machinery + FSS self-test (S11)
  moment_slope.py  slope-model moment spectrum: filamentary area (S12)
  conditional.py   conditional avalanche exponents; ballistic front (S13)
  geometry2d.py    per-avalanche footprint geometry: the filament (S14)
  stochastic_split.py  tunable split toward Manna; the causal test (S15)
  geometry1d.py    1-D footprint geometry = the dimensional anchor (S16)
  equilibrate2d.py reusable warmup to a verified-stationary L>256 (S16)
  duration_closure.py  single-scale exact in space, residual in time (S17)
  area_multifractality.py  area multifractality is asymptotic, not a corona (S18)
  filament_fattening.py  typical-vs-tail footprint split: the filament does not fatten (S19)
  repose_scaling.py     repose model selection — log(L) beats 1/L by AIC (S20)
  repose_scaling_L1024.py  out-of-sample confirmation at held-out L = 768/1024 (S21)
  manna.py              literal 2-D Manna under the same pipeline — the measured anchor (S22)

Exercises addressed: 2 (falloff avalanches, S8), 3 (initial-condition independence, S1), 4 (eps-dependence of the angle of repose, S2), 5 (fast active-list engine, S9), 6 (the 2-D Grand Challenge, S4) — plus the conservation study (S5/S7) and a self-test that was inconclusive (S6) then resolved (S10).

The scaling-theory arc (S11–S18). Beyond the exercises, the chapter builds one extended argument about what a 2-D slope avalanche actually is. Moment-spectrum analysis (S11–S12) finds the avalanche footprint is filamentary — its area grows only linearly with system size (D_area ≈ 1), against the compact D ≈ 2 of canonical BTW through the same pipeline — while the total toppling activity is space-filling (≈ L²): a thin front that sweeps its own bonds ≈ L times. Conditional exponents (S13) and a direct footprint recording (S14) confirm it geometrically — a constant-width, one-bond-wide, ballistic filament (topple time ≈ distance from the seed), thinner than the exactly-solvable directed sandpile (D = 3/2). A tunable stochastic split toward the Manna class (S15) shows the filament is caused by the deterministic rule (its dimension climbs 1 → 1.87 as randomness is switched on), yet the model never collapses onto simple Manna scaling. A 1-D dimensional anchor (S16) proves D ≈ 1 is intrinsic to a slope avalanche, not a 2-D artifact, and ships an equilibration tool that reaches a verified-stationary L = 512. With it, the last two findings pin down how single-scale the model is: the spatial relations become exactly single-scale as L → ∞ (size ∝ area², area ∝ duration), but duration stays a permanently loose proxy for spatial extent (S17), and the avalanche area distribution stays multifractal asymptotically rather than healing to simple scaling (S18) — the model is single-scale in its means (the typical avalanche) and multifractal in its tails (the largest avalanches). Four closing findings tie the chapter off: the typical filament does not fatten with system size — the apparent fattening at five seeds was tail undersampling (S19); the angle of repose most likely diverges logarithmically with system size rather than saturating, chosen by model selection (S20) and then confirmed out of sample by frozen-fit predictions at held-out L = 768/1024, which land above the saturating model's own ceiling (S21); and the Manna comparison point was measured under the identical pipeline rather than quoted (S22) — closing the placement triangle. It sits in its own place in the SOC landscape: more filamentary than the directed sandpile, outside the stochastic Manna class, a distinct anomalous class from BTW. The whole arc is drawn together in a standalone write-up, report_sandpile.md; full per-finding detail (S-series) is in findings_sandpile.md.


Third chapter — Earthquakes (the Olami–Feder–Christensen model)

A new topic (Charbonneau Ch. 8), the direct continuation of the sandpile conservation thread. Code in earthquake/, findings in findings_earthquake.md (E-series). The OFC model is a 2-D lattice of "force" values driven uniformly until a node hits threshold and topples, passing a fraction α to each neighbor (0 ≤ α ≤ 0.25). At α = 0.25 redistribution is conservative; for α < 0.25 the bulk dissipates (1−4α) per topple — so α is a continuous knob on the exact ingredient (bulk conservation) that finding S5 singled out for SOC.

Headline result: OFC reproduces the Gutenberg–Richter law, and conservation tunes criticality. The avalanche-size cutoff grows with system size when (near‑)conservative and saturates to a dissipation‑set scale when not — the S5 result made quantitative on the canonical earthquake model. And unlike the sandpile, the nonconservative model is deterministic and develops synchronized domains that drive quasi‑periodic recurrent avalanching — temporal structure the sandpile entirely lacks.

α (conservation) PDF slope (book) cutoff vs L regime
0.25 (conservative) −1.19 (−1.19) grows steeply (D ≈ 3.2) critical
0.20 −1.87 (−1.92) ≈ L² (D ≈ 1.95) critical‑like
0.10 (60% dissipated) −3.52 (−3.34) flat (D ≈ −0.15) subcritical

Validated against the chapter: the fig.-8.7 PDF slopes (all three match), the fig.-8.6 synchronization domains forming from a random start, the fig.-8.4 recurrent avalanching (raw period ~10,600 at α=0.15, matching the book's ~10,960; forcing-corrected ~3,982 vs the book's 4,002; vanishing at the conservative α=0.25). The size–duration relation steepens with conservation (E ~ T^γ, γ = 1.47 → 1.89 as α: 0.15 → 0.25). A self-test (Exercise 4) confirms the quasi‑periodicity is fragile: a ±0.01 per‑topple jitter in α halves the recurrence peak, because synchronization needs the redistribution to preserve equality exactly.

Grand Challenge (earthquake prediction): a forecaster that phase-locks to the recurrence rhythm beats chance only modestly (1.5–1.7×) and misses the largest events (top‑decile recall 0.09) — reproducing, in a fully-known noise-free model, the book's point that large events sit in an unpredictable tail.

earthquake/
  ofc.py                core OFC model + 6 validation self-tests (open/periodic BC,
                        per-topple stochastic α, Exercise-3 skip-forcing, bit-identical)
  ofc_gr.py             E1: Gutenberg–Richter validation (fig. 8.7)
  ofc_fss.py            E2: finite-size scaling / conservation test (cf. S5/S7)
  ofc_quasiperiodic.py  E3: recurrence period + synchronization domains (figs. 8.4/8.6)
  ofc_et.py             E4: avalanche size–duration relation (Exercise 5)
  ofc_speedup.py        E5: forcing skip-ahead speedup (Exercise 3)
  ofc_stochastic_alpha.py  E6: self-test — does stochastic α break periodicity? (Ex. 4)
  ofc_predict.py        E7: Grand Challenge — earthquake prediction (Exercise 6)

Exercises addressed: 2 (finite-size scaling, E2), 3 (skip-forcing, E5), 4 (stochastic-α self-test, E6), 5 (size–duration relation, E4), 6 (the Grand Challenge prediction, E7) — plus the validation (E1) and the recurrence/synchronization study (E3).


Model

N agents move on a periodic unit square under four forces each timestep:

Force Description
Repulsion Short-range — keeps agents from overlapping (range 2r₀)
Alignment Drives velocity toward mean of neighbors within r_f
Self-propulsion Corrects speed toward target v₀
Noise Uniform random perturbation in [−η, η]

Key analytical result: equilibrium cruise speed is v_eq = v₀ + α/μ — not v₀. Derived from force balance in an aligned flock; verified experimentally to within 0.002 across four α values.

Default parameters: N=350, r₀=0.005, ε=0.1, r_f=0.1, α=1.0, v₀=1.0, μ=10.0, η=0.5, dt=0.01


Code Architecture

The codebase has two layers.

Core layer (model.py) — object-oriented foundation for all current and future experiments.

Flock          — holds prey positions, velocities, and all physics parameters
                 flock.evolve(predators=[...]) advances one timestep
Predator       — configurable predator agent; strategy='naive'|'encircle',
                 coord_alpha for predator-predator repulsion, enc_radius for encirclement
simulate()     — runs a full experiment, returns Phi and distance timeseries

New experiments import from model.py. To add a new experiment: import Flock, Predator, and simulate from model.py, set parameters, call simulate().

Legacy layer (flocking.py) — procedural implementation that predates the OOP refactor. The earlier experiment scripts (predator.py, multi_predator.py, encirclement.py, etc.) import from here. They are retained intact for reproducibility and are not broken by the model.py refactor.

Repository Layout

root/
  model.py          ← current API. New experiments import from here.
  geometry.py       ← shared helpers (Rg, AR) — used by both old and new scripts.

  flocking.py       ← legacy: procedural baseline.        [keep for reproducibility]
  analysis.py       ← legacy: validation sweeps.          [keep for reproducibility]
  predator.py       ← legacy: single-predator API.        [keep for reproducibility]
  multi_predator.py ← legacy: multi-predator helpers.     [keep for reproducibility]
  encirclement.py   ← legacy: encirclement scaffolding.   [keep for reproducibility]

predator/    predator strategy experiments
contagion/   panic, epidemic, and vaccination experiments
phase/       phase-transition and statistical-mechanics experiments
3d/          three-dimensional extension experiments
figures/     output PNG figures
outputs/     captured text/log output from runs

The five legacy scripts at root are intentionally retained: every numbered finding F5 through F16 was generated by them, and rerunning them must produce identical figures. New experiments should not import from them — they have no if __name__ == "__main__" guard and will side-effect on import. Use model.py instead.

Experiment scripts in subfolders automatically add the project root to sys.path so they can import the core library files above.

Investigation Progression

The experiments follow a logical arc, each motivated by the result before it:

Stage Question Scripts
1. Baseline Does the model reproduce expected flocking behavior? flocking.py, analysis.py
2. Phase transition Is the solid-to-fluid transition a true phase transition? phase/phase_transition.py, phase/compactness_phase.py, phase/compactness_search.py
3. Geometry What shape does the flock take? geometry.py
4. Single predator Does flocking help prey survive? predator.py
5. Predator strategy hierarchy How many and how coordinated do predators need to be to break the flock? multi_predator.py → predator/evasion_analysis.py → predator/coordinated_predators.py → encirclement.py → predator/encirclement_scaling.py → predator/fragmentation.py
6. Encirclement limits Does the encirclement floor scale with N? Is it stable long-term? Does a gap help the flock? predator/large_N_encirclement.py → predator/renc_scaling.py → predator/long_encirclement.py → predator/encirclement_gap.py
7. Minimum viable size Below what N does collective evasion fail? predator/min_flock_size.py
8. Reversibility Does encirclement damage reverse after predator removal? predator/reunion.py
9. Realistic predator What changes with limited sensing range? predator/predator_sensing.py
10. Internal panic Does panic from within disrupt the flock? contagion/panic.py
11. Contagion What if panic spreads by contact? (SI and SIS models) contagion/panic_contagion.py → contagion/contagion_sis.py
12. Hybrid stressors How do predation and contagion interact? contagion/hybrid_stressors.py → contagion/hybrid_sis.py → contagion/critical_shift.py → contagion/outbreak_removal.py
13. Herd immunity How many immune agents are needed to quench an outbreak? contagion/herd_immunity.py → contagion/targeted_immunity.py
14. Segregation Does a mixed-speed population spatially separate? contagion/segregation.py → contagion/segregation_alpha.py
15. Adaptive predators Do predators that track flock geometry outperform fixed strategy? predator/adaptive_encirclement.py
16. Vaccination strategies Can targeting high-degree or spatially-distributed agents reduce herd-immunity threshold? contagion/targeted_immunity.py → contagion/spatial_vaccination.py
17. Phase transition mechanism Why does the mod

Description taken from the repository README.

Ratings

–
0 ratings
5 0
4 0
3 0
2 0
1 0

Rate this app

Sign in to rate

Comments 0

Sign in to comment

No comments yet.

Related apps

#dev-tools