An agent-based model (ABM) of the tara iti (New Zealand fairy tern, Sternula nereis davisae), one of New Zealand's rarest endemic seabirds. The model simulates population dynamics across breeding seasons, incorporating storm disturbance and predation pressure.
The model tracks individual birds through their life stages and breeding attempts over annual cycles. It is designed to explore how storm events and predator activity interact with breeding outcomes to drive population trends in this critically endangered species.
Key processes modelled:
- Breeding dynamics – courtship, clutch laying (up to 3 relay attempts per season), incubation, hatching, and chick fledging
- Storm disturbance – stochastic storms of varying severity that cause nest abandonment, egg loss, chick mortality, and reduced bird condition
- Predation – avian (gulls, kahu/harrier, shorebirds) and mammalian (rats, cats, other mammals) predator incursions that remove eggs and chicks
- Seasonal mortality – overwinter survival rates by life stage (chick/juvenile, immature, adult)
- Energetics – individual condition scores that influence courtship, nest abandonment thresholds, and survival
tara_iti_ABM/
├── breeding_storm_predation2.nlogox # Main NetLogo model (breeding + storm + predation)
├── TI_breeding.nlogo # Earlier prototype breeding-only model
├── TI_move_fish.nlogo # Prototype movement/foraging model
├── parameters/
│ ├── model_params.R # R script for deriving model parameters from empirical data
│ ├── parameters.Rproj # RStudio project file
│ └── raw_data/
│ ├── egg_level_data_2000_2025.csv # Per-nest egg and hatching records
│ └── captures_morphometrics_data_1990_2025.csv # Individual capture and morphometric records
└── .github/
├── workflows/
│ └── run-model.yml # CI workflow: ABC calibration + 50-year simulation + commit
├── experiments/
│ └── default.xml # Reference BehaviorSpace experiment (NetLogo 7 format)
└── abc/
├── abc_runner.py # ABC rejection-sampling calibration script
└── set_best_params.py # Injects best-fit ABC parameters into model; sets 50-year run
Each bird is an individual agent with attributes including:
| Attribute | Description |
|---|---|
sex |
"male" or "female" |
age_years / age_day |
Continuous age |
life_stage |
"chick", "juvenile", "immature", or "adult" |
condition |
Energetic condition (0–1); drives key behavioural decisions |
breeding? |
Whether the bird will make a breeding attempt this season |
clutch_state |
"none", "incubating", "failed", "hatched", or "finished" |
clutch_no |
Number of clutches attempted in the current season (max 3) |
Patches carry a habitat_suitability score used to assign nesting territories. The model runs on a daily time step; one tick equals one day.
The model starts in mid-April (day 122) in the non-breeding season and advances day by day. Each year spans approximately 365 ticks; the full model run covers 50 years (18 300 ticks). Processes are gated by season ("breeding" / "non-breeding").
Breeding
| Slider | Default | Description |
|---|---|---|
initial_number |
50 | Starting number of birds |
courting_threshold |
10 | Days of courtship before pairing |
court_success |
0.75 | Per-day probability of successful pairing |
breeding_condition |
0.80 | Minimum condition to attempt breeding |
mean_clutch_no |
1.68 | Mean number of clutches attempted per season |
incubation_cond_loss |
0.025 | Daily condition loss during incubation |
abandon_threshold |
0.20 | Condition below which a nest is abandoned |
hatch_failure |
0.20 | Per-egg probability of failing to hatch |
cooling_off_days |
10 | Rest days required before a relay attempt |
Energetics & mortality
| Slider | Default | Description |
|---|---|---|
mean_foraging_gain |
0.060 | Mean daily condition gain from foraging |
mean_metabolic_loss |
0.060 | Mean daily condition loss |
winter_adult_mort |
0.0004 | Daily overwinter mortality rate – adults |
winter_imm_mort |
0.0003 | Daily overwinter mortality rate – immatures |
winter_juv_mort |
0.0005 | Daily overwinter mortality rate – juveniles |
Storms
| Slider | Default | Description |
|---|---|---|
storm_prob |
0.030 | Daily probability of a storm occurring |
nest_risk_thresh_small/large/extreme |
0.50 / 0.60 / 0.95 | Condition loss thresholds per storm severity |
nest_loss_small/large/extreme |
0.15 / 0.20 / 0.50 | Proportion of nests lost per severity level |
Predation
| Slider | Default | Description |
|---|---|---|
avian_predator_prob |
0.016 | Daily probability of an avian predator event |
mam_predator_prob |
0.018 | Daily probability of a mammalian predator event |
gull/kahu/shorebird_egg_take |
1.0 / 1.5 / 2.0 | Mean eggs taken per avian predator visit |
rat/cat/other_egg_take |
1.3 / 1.5 / 2.0 | Mean eggs taken per mammalian predator visit |
- NetLogo ≥ 7.0.0 (the model file targets NetLogo 7.x)
- R ≥ 4.0 with the
tidyversepackage (forparameters/model_params.Ronly) - Python ≥ 3.9 (for
abc_runner.py; uses only the standard library)
- Open
breeding_storm_predation2.nlogoxin NetLogo. - Adjust sliders on the Interface tab as required.
- Click setup then go.
NetLogo can be run without a GUI using the bundled netlogo-headless.sh script. The default BehaviorSpace experiment is embedded directly in the model file (NetLogo 7 reads experiments from the model, not from a separate setup file):
/path/to/NetLogo-7.0.4-64/netlogo-headless.sh \
--model breeding_storm_predation2.nlogox \
--experiment default \
--table results.csvresults.csv will contain one row per run (final metrics only, since runMetricsEveryStep="false") with columns total_birds, fledged, and total_eggs.
Every push and pull request triggers the .github/workflows/run-model.yml workflow, which runs the full calibration-to-simulation pipeline:
- Installs Java 21 (Temurin) and downloads NetLogo 7.0.4 (cached between runs).
- Runs ABC calibration — draws 300 Latin-Hypercube-sampled parameter sets, simulates each for 365 ticks, and ranks them by distance from observed field data. Uploads
abc_all.csvandabc_accepted.csvas theabc-resultsartifact. - Applies best-fit parameters —
set_best_params.pyreads the top-ranked row fromabc_all.csv, patches the<experiments>block inbreeding_storm_predation2.nlogoxwith the 49 calibrated parameter values and a 50-year time limit (18 250 ticks), and writesbest_params.csv. - Runs a 50-year headless simulation using the updated
defaultBehaviorSpace experiment and uploadsresults.csvandbest_params.csvas thesimulation-resultsartifact. - Commits the updated model file back to the repository (push events only; skipped on pull requests). The commit message includes
[skip ci]to prevent a re-trigger loop.
abc_runner.py uses ABC rejection sampling with Latin Hypercube Sampling (LHS) to calibrate the model against field-observed breeding outcomes.
-
Draws 300 parameter sets from uniform priors using LHS. LHS divides each parameter's range into 300 equal strata and samples one point per stratum, giving much better prior coverage than pure random sampling across 49 parameters.
-
For each parameter set (run in parallel across all available CPU cores):
-
Injects a BehaviorSpace experiment into a temporary copy of the model.
-
Runs NetLogo headlessly for 365 ticks.
-
Computes a normalised Euclidean distance between the simulated summary statistics and field-observed targets:
Summary statistic Field target Normalisation SD fledge_rate(fledglings ÷ eggs)0.307 0.15 eggs_per_50(eggs ÷ initial birds)0.458 0.20 Both targets are derived from
parameters/raw_data/egg_level_data_2000_2025.csv(seasons 2000–2025, n = 25).
-
-
Accepts the top 20% of runs (lowest distance) as the approximate posterior.
All 49 modifiable sliders are varied (only initial_number is fixed at 50):
| Group | Parameters |
|---|---|
| Breeding | patch_threshold, courting_threshold, court_success, breeding_condition, mean_clutch_no, incubation_cond_loss, abandon_threshold, hatch_failure, with_chick_cond_loss, cooling_off_days, courting_condition, candle_success |
| Energetics | mean_foraging_gain, sd_foraging_gain, mean_metabolic_loss, sd_metabolic_loss, mean_breeding_prep, sd_breeding_prep, chick_health, juv_cond_loss |
| Mortality | winter_adult_mort, winter_imm_mort, winter_juv_mort, winter_elder_mort |
| Storms | storm_prob, nest_risk_thresh_small/large/extreme, nest_loss_small/large/extreme, small/large/extreme_cond_loss |
| Predation | avian_predator_prob, mam_predator_prob, sibling_attack, gull/kahu/shorebird_egg_take, gull/kahu/shorebird_chick_take, rat/cat/other_egg_take, rat/cat/other_chick_take |
# Default: 300 LHS samples, 20% acceptance, 4 workers, seed 42
python3 .github/abc/abc_runner.py
# Custom settings via environment variables
NETLOGO_SCRIPT=/path/to/netlogo-headless.sh \
MODEL_FILE=breeding_storm_predation2.nlogox \
ABC_N_SAMPLES=100 \
ABC_N_WORKERS=8 \
ABC_ACCEPT_FRACTION=0.10 \
ABC_SEED=123 \
python3 .github/abc/abc_runner.py| Variable | Default | Description |
|---|---|---|
NETLOGO_SCRIPT |
./NetLogo-7.0.4-64/netlogo-headless.sh |
Path to NetLogo headless launcher |
MODEL_FILE |
breeding_storm_predation2.nlogox |
Path to model file |
ABC_N_SAMPLES |
300 |
Number of LHS samples to draw |
ABC_N_WORKERS |
CPU count | Parallel worker threads |
ABC_ACCEPT_FRACTION |
0.20 |
Top fraction accepted as posterior |
ABC_SEED |
42 |
Random seed for reproducibility |
| File | Contents |
|---|---|
abc_all.csv |
All successful runs: parameter values, simulated summary stats (fledge_rate, eggs_per_50, total_birds, fledged, total_eggs), and distance |
abc_accepted.csv |
Accepted runs only (top ACCEPT_FRACTION by distance) — the approximate posterior |
The posterior parameter ranges printed to stdout identify which combinations of parameters are most consistent with observed breeding outcomes.
After ABC completes, set_best_params.py automates the step of applying the calibrated parameters to the model and running a long-term projection.
- Reads
abc_all.csv(sorted ascending by distance) and picks the first row — the best-fit parameter set. - Patches the
<experiments>block insidebreeding_storm_predation2.nlogoxin-place, setting:- all 49 calibrated parameter values as
<enumeratedValueSet>constants, initial_number = 50(fixed, not calibrated by ABC),timeLimit = 18250(50 years × 365 ticks).
- all 49 calibrated parameter values as
- Writes
best_params.csv(two-column:parameter,value) for traceability.
The updated model file is then run headlessly for the full 50 years and the modified .nlogox is committed back to the repository so the BehaviorSpace experiment always reflects the latest best-fit calibration.
# Defaults: MODEL_FILE=breeding_storm_predation2.nlogox, ABC_ALL_CSV=abc_all.csv
python3 .github/abc/set_best_params.py
# Custom paths
MODEL_FILE=path/to/model.nlogox \
ABC_ALL_CSV=path/to/abc_all.csv \
python3 .github/abc/set_best_params.py| Variable | Default | Description |
|---|---|---|
MODEL_FILE |
breeding_storm_predation2.nlogox |
Path to the .nlogox model file to patch |
ABC_ALL_CSV |
abc_all.csv |
Path to the ABC output (row 0 must be the best-fit run) |
| File | Contents |
|---|---|
best_params.csv |
Best-fit parameter set written by set_best_params.py (50 rows: parameter name + value) |
results.csv |
50-year simulation output: one row with final total_birds, fledged, total_eggs |
Parameter estimation is handled by parameters/model_params.R, which reads:
egg_level_data_2000_2025.csv– nest-level egg and hatch records from the 2000–2025 breeding seasonscaptures_morphometrics_data_1990_2025.csv– individual band/resight records with morphometric data (1990–2025)
These datasets are used to derive breeding parameters (e.g., clutch frequencies, breeding ages, sex ratios) and survival estimates.