This repository contains a headless Python port of the legacy NetLogo CoCoNet model.
| Resource | Link |
|---|---|
| Documentation (installation, configuration, CLI, API, I/O) | gbrrestoration.github.io/coconet-python |
PyPI package (pip install coconet-python) |
pypi.org/project/coconet-python |
| Source | github.com/gbrrestoration/coconet-python |
The docs site is built with Jekyll from the documentation/ tree. Maintainers enable Settings → Pages → Build and deployment: GitHub Actions; pushes to main run Deploy documentation to GitHub Pages and update the URL above.
Links to the docs below use the published site so they work from this README on GitHub and on PyPI (which does not resolve repository-relative paths).
The same simulation engine is exposed for shell-oriented workflows and for programmatic control. Pick the entry point that matches how you are integrating the model.
Use this when you want files, flags, and environment variables—for example local terminals, Docker (the published image uses the CLI as its entrypoint), shell scripts, or CI jobs that only need to pass paths.
- Console script:
coconet(installed with the package). - Module form:
python -m coconet(same behaviour ascoconet). - Typical inputs:
--config(YAML),--parameter-file(legacy CSV),--reefs-file,--coastline-file,--output-file, plusCOCONET_*environment overrides. Seecoconet --helpand the CLI documentation.
The CLI handles logging bootstrap, optional CPU profiling (--profile, extra dependency), and applies a fixed set of command-line overrides on top of the shared configuration loader.
Use this when you need full control from Python—custom CLIs, web services, notebooks, schedulers, or multi-step pipelines. Install from PyPI (distribution name coconet-python; import package remains coconet):
pip install coconet-pythonThe stable library surface is load_coconet_config and run_coconet in coconet.api (re-exported from coconet). Build a CoconetConfig from YAML paths, legacy CSV, environment, an optional scenario dict, and keyword overrides, then run:
from coconet import load_coconet_config, run_coconet
cfg = load_coconet_config(
config_file="config/example.yaml",
reefs_file="legacy/reefs2024.csv",
coastline_file="legacy/coastline.csv",
output_file="out/run.csv",
)
run_coconet(cfg, configure_logs=True)For advanced embedding you can still construct CoconetModel directly from CoconetConfig. See the Python API page for precedence, logging, and __all__.
The legacy/ directory contains the original monolithic NetLogo model (CoCoNet V3_rubble.nlogo) and its data files. This port migrates the model to a modern Python stack for:
- headless execution in cloud/CI environments,
- typed, maintainable code,
- easier configuration through files and environment variables,
- reproducible scenario runs without a GUI.
legacy/- original NetLogo model and data files.coconet/- Python implementation:config.py- typed configuration and legacy parameter CSV parsing,model.py- simulation engine (ported procedures),api.py- library entry points (load_coconet_config,run_coconet) for PyPI consumers and embedders,cli.py- CLI entry point (coconet/python -m coconet),__main__.py- delegates to the CLI forpython -m coconet.
uv sync
uv run coconet --parameter-file legacy/I_2p6.csv --output-file output.csvYou can also use YAML config:
uv run coconet --config config/example.yaml --output-file output.csvA container image is built with GitHub Actions on pushes to the default branch (main) and on SemVer tags v*. It is published to GitHub Container Registry as ghcr.io/gbrrestoration/coconet-python (pull: docker pull ghcr.io/gbrrestoration/coconet-python:latest).
The image is based on python:3.12-slim-bookworm, installs dependencies with uv (uv sync --frozen), bundles legacy/ and config/ under /app, and runs as UID 1000. The container entrypoint is the coconet CLI (see Two ways to run CoCoNet). If you need the library interface instead, add pip install coconet-python (or copy the package) in your own image and call load_coconet_config / run_coconet from your code.
Point the CLI at inputs and outputs using flags (or YAML / environment variables). For data on the host, mount a host directory into the container and pass container paths to the CLI.
| Item | CLI flags | Environment (optional) |
|---|---|---|
| Reef table CSV | --reefs-file |
COCONET_REEFS_FILE |
| Coastline CSV | --coastline-file |
COCONET_COASTLINE_FILE |
| Scenario YAML | --config |
(YAML keys reefs_file, coastline_file, …) |
| Legacy parameter CSV | --parameter-file |
(set via YAML parameter_file if needed) |
| Run output CSV | --output-file |
COCONET_OUTPUT_FILE |
Example: bundled inputs, outputs on the host
mkdir -p ./out
docker run --rm \
-v "$(pwd)/out:/out" \
ghcr.io/gbrrestoration/coconet-python:latest \
--config /app/config/example.yaml \
--output-file /out/run.csvExample: custom inputs and outputs on the host
mkdir -p ./out
docker run --rm \
-v "/path/to/mydata:/data:ro" \
-v "$(pwd)/out:/out" \
ghcr.io/gbrrestoration/coconet-python:latest \
--reefs-file /data/reefs2024.csv \
--coastline-file /data/coastline.csv \
--parameter-file /data/myparams.csv \
--output-file /out/results.csvUse -e COCONET_ENSEMBLE_RUNS=2 (and other COCONET_* variables) if you prefer environment overrides; see below. Run docker run --rm ghcr.io/gbrrestoration/coconet-python:latest --help for all CLI options.
Permissions: the process in the image runs as UID 1000 (coconet). Writable bind mounts (for --output-file, profiling output, etc.) must allow that user to create files—for example sudo chown 1000:1000 ./out on the host directory, or a Docker volume instead of a root-owned host path. Overriding with --user is not recommended because /app in the image is not world-readable.
Environment overrides are supported with COCONET_ prefix, for example:
export COCONET_ENSEMBLE_RUNS=2
export COCONET_END_YEAR=1990
uv run coconet --parameter-file legacy/I_2p6.csvThe CLI includes lifecycle logging for setup, progress, and run completion.
You can control logging level with either CLI or environment variables:
uv run coconet --config config/example.yaml --log-level DEBUGexport COCONET_LOG_LEVEL=WARNING
uv run coconet --config config/example.yamlSupported levels are: CRITICAL, ERROR, WARNING, INFO, DEBUG.
If both are provided, CLI takes precedence over the environment variable.
The implementation ports the NetLogo procedures and keeps important NetLogo semantics where feasible (seed resets, annual loop structure, reef/site-level state updates, interventions, and output schema). This is designed to support model-output comparison workflows between the legacy and Python implementations.
The documentation site (Scenario parameter semantics) carries the same tables as the following sections for easier browsing in HTML.
Rows are Label, value pairs. Blank lines are skipped. Parsing is case-insensitive on the label. The first row whose first cell is exactly Ensemble (the output table header) ends the config block; everything after that is treated as data rows, not parameters.
Labels are mapped in coconet/config.py (label_to_attr). If a label from a template file is missing, the value stays at the Python default in CoconetConfig (for example legacy/I_2p6.csv has no Spinup backtrack (years) row, so the default 50 is used, and no CoTS vessels in active sector row, so default 0 is used).
YAML / environment-only (not read from the legacy CSV): reefs_file, coastline_file, output_file, ensemble_threads, log_level, search_mode, perfect_intervention, unregulated_fishing. Override these via --config, COCONET_* env vars, or CLI flags (--reefs-file, --coastline-file, --output-file, …) where supported.
| Legacy CSV label | CoconetConfig field |
Role |
|---|---|---|
| Climate scenario | SSP |
Shared socioeconomic pathway used for post-projection_year bleaching severity, coral growth pH stress (sqrt(SSP)), and related scenario branches. Supported values in code: 1.9, 2.6, 4.5, 7.0, 8.5. |
| Ensemble runs | ensemble_runs |
Number of stochastic ensemble replicates (1..ensemble_runs); ensemble 0 is spin-up. |
| Start year | start_year |
Calendar year when saved initial conditions are loaded for ensembles >= 1. |
| Spinup backtrack (years) | spinup_backtrack_years |
For ensemble 0 only, simulation begins at start_year - spinup_backtrack_years to warm up the system. |
| Save year | save_year |
Initial state for ensembles >= 1 is taken from the end of the spin-up trajectory in the year before save_year (see model loop). |
| Projection year | projection_year |
Before this year, historical cyclone/bleaching drivers apply where coded; from this year onward, scenario-based projection drivers apply. |
| End year | end_year |
Last calendar year in the main annual loop. |
| Search year | search_year |
When search_mode == 1, used to switch search behaviour and benefit accumulation (optimization-style mode; default search_mode is 0). |
All CoTS control logic runs only when year >= start_CoTS_control. Vessel counts set how many control “vessels” are simulated for that annual step; each non-zero regional/GBR/sector option runs its own control pass in sequence (separate dive budgets). Intervention reefs are still filtered by region for the regional modes.
| Legacy CSV label | CoconetConfig field |
Role |
|---|---|---|
| CoTS control start year | start_CoTS_control |
First year any CoTS control runs. |
| CoTS control ecological threshold (CoTS per ha) | eco_threshold |
Site-level CoTS density above which culling dives target that site; also scales post-cull age-class distribution. |
| CoTS control CoTS threshold (CoTS per ha) | CoTS_threshold |
Reef-level mean CoTS (S_manta_r) must be below this (or coral above coral_threshold) for the reef to be eligible for control. |
| CoTS control coral threshold (CoTS per ha) | coral_threshold |
If reef mean coral cover exceeds this, the reef is eligible even when CoTS density is high (legacy naming says “CoTS per ha” but the field is compared to coral cover). |
| CoTS vessels across GBR | CoTS_vessels_GBR |
Sets vessel count and control_region = "GBR" for control_cots(). |
| CoTS vessels in Far-northern Region | CoTS_vessels_FN |
Same for region FN. |
| CoTS vessels in Northern Region | CoTS_vessels_N |
Same for region N. |
| CoTS vessels in Central Region | CoTS_vessels_C |
Same for region C. |
| CoTS vessels in Southern Region | CoTS_vessels_S |
Same for region S. |
| CoTS vessels in active sector | CoTS_vessels_sector |
Vessel count for control_cots_by_sector(): targets the sector (1–11) with highest mean CoTS; uses a slightly different dive-cost formula than regional control. |
| Legacy CSV label | CoconetConfig field |
Role |
|---|---|---|
| Catchment restoration start year | start_catchment_restore |
First year catchment recovery can proceed. |
| Catchment restoration timescale (years) | restore_timeframe |
Each year, catchment_condition moves toward 1 by 1/restore_timeframe (exponential approach). Must be > 0 for the update to run. Used in cyclone/flood loading (flood_load). |
| Future zoning start year | start_modified_zoning |
Year assigned as future_rezone_year for the first rezoned_reefs reefs in priority order (among reefs that still have rezone_year == 9999). |
| Number of reefs included in future rezoning | rezoned_reefs |
Count of reefs that receive a planned future closure year. |
| Reduction in fisheries catch start year | start_modified_fishing |
From this year on, global annual emperor and coral-trout catch caps are multiplied by 1 - catch_reduction. |
| Fractional reduction in fisheries catches | catch_reduction |
Fractional cut applied to e_annual and g_annual (see apply_fishing). |
| Upper fish size limit start year | start_upper_sizelimit |
From this year, fishing removes no E_5 / G_5 (largest age classes). |
| Lower fish size limit start year | start_lower_sizelimit |
From this year, fishing removes no E_3 / G_3 (smallest targeted ages). |
| Exclude fishing from active outbreak reefs start year | start_CoTSlimit |
From this year, if a weighted CoTS outbreak index on the reef exceeds 68, all targeted emperor/trout catch on that visit is set to zero. |
| Legacy CSV label | CoconetConfig field |
Role |
|---|---|---|
| Emperor release start year | start_emperor_release |
First year release_fish() runs. |
| Number of release reefs | release_reefs |
Maximum reefs to stock per year (priority order, within intervention lat/lon box). |
| Maximum adult emperors (per ha) for release | release_threshold |
Reefs at or above this total adult emperor biomass are skipped (release targets depleted reefs). |
| Number of juvenile emperors released per reef | release_number |
Juveniles added to E_1, spread per reef area (ha). |
| Regional shading start year | start_regional_shading |
First year shade_regional_coral() runs. |
| Absolute DHW reduction due to regional shading (DHW) | regional_shading_reduction |
For reefs inside the intervention bounding box, bleaching DHW is multiplied by (1 - regional_shading[reef]) after this value is assigned (same multiplicative pattern as local shading; legacy label says “absolute” but the implementation scales DHW). |
Applied to most spatial interventions (not CoTS regional control, which uses reef region codes). Reef centre must satisfy intervene_lon_min < x < intervene_lon_max and intervene_lat_min < y < intervene_lat_max.
| Legacy CSV label | CoconetConfig field |
|---|---|
| Minimum longitude of interventions | intervene_lon_min |
| Maximum longitude of interventions | intervene_lon_max |
| Minimum latitude of interventions | intervene_lat_min |
| Maximum latitude of interventions | intervene_lat_max |
| Legacy CSV label | CoconetConfig field |
Role |
|---|---|---|
| Rubble consolidation start year | start_rubble_consolidation |
Enables consolidate_rubble(). |
| Annual number of consolidated reefs | consolidation_reefs |
Cap on reefs treated per year. |
| Minimum rubble cover threshold for consolidation [0 1] | consolidation_threshold |
Reef mean rubble must be strictly greater than this to qualify. |
| Total annual consolidated area (ha) | consolidation_hectares |
Divided by ha_per_site to give per-site rubble removal at a chosen site. |
| Thermally tolerant coral seeding start year | start_coral_seeding |
Enables seed_tt_coral(). |
| Annual number of reefs seeded with coral | seed_reefs |
Max reefs per year. |
| Maximum coral cover threshold for coral seeding [0 1] | seed_threshold |
Reefs at or above this mean coral cover are skipped. |
| Total annual area of seeded corals (ha) | seed_hectares |
Determines fractional cover added per seeding event. |
| Fraction of staghorn … hybridise … [0 1] | hybrid_fraction |
In spawn_corals(), scales hybridisation of branching (sa) with thermally tolerant (tt) corals for recruitment composition and thermal traits. |
| Dominance of thermally tolerant corals … [0 1] | dominance |
Weights how hybrid thermal tolerance blends toward tt vs sa in the same hybrid calculation. |
| Coral slicks start year | start_coral_slick |
Enables seed_coral_slick(). |
| Annual number of reefs with coral slicks released | slick_reefs |
Max reefs per year. |
| Maximum coral cover threshold for coral slicks [0 1] | slick_threshold |
Reefs with thermally tolerant reef-mean cover C_r["tt"] at or above this are skipped. |
| Total annual area of slick corals (ha) | slick_hectares |
Slick fraction per site; material is drawn from a random source site’s community composition. |
| Reef shading start year | start_reef_shading |
Enables shade_local_reef(). |
| Annual number of reefs locally shaded | shading_reefs |
Each chosen reef gets reef_shading[reef] = reef_shading_reduction. |
| Fractional DHW reduction due to local shading [0 1] | reef_shading_reduction |
Bleaching DHW multiplied by (1 - reef_shading[reef]). |
| Ocean acidification treatment start year | start_pH_protection |
Enables increase_pH(). |
| Annual number of reefs treated for ocean acidification | pH_reefs |
Reefs per year receive pH_protect[reef] = pH_protection. |
| Fractional protection from ocean acidification [0 1] | pH_protection |
From projection_year onward, coral growth uses pH_effect_t = (1 - pH_protect[reef]) * sqrt(SSP). |
search_mode(0default): normal output;1enables search-year logic and benefit tracking (seemodel.py).perfect_intervention: string mode applied from year 2026 onward (_apply_perfect_intervention), e.g. idealised starfish control or shading (not set via legacy CSV).unregulated_fishing: if true, sets zoning years so fishing regulation drivers are bypassed.
For the exact equations, see the corresponding methods on CoconetModel in coconet/model.py and the label map in coconet/config.py.