Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
4f7057b
fix(core): stop ~ForeFireModel freeing an uninitialised pointer
HugoFara Aug 12, 2026
826e7b8
test(models): cover every propagation and flux model, build with -Wal…
HugoFara Aug 12, 2026
4087aaf
ci: run the native workflows on pull requests to dev
HugoFara Aug 12, 2026
27e0741
ci: take ownership of build/ before running ctest on Linux
HugoFara Aug 12, 2026
0196cd0
fix(models): stop freeing the properties array twice
HugoFara Aug 12, 2026
4ecc171
fix(propagation): keep ROS finite and damped at high dead moisture
HugoFara Aug 11, 2026
f7cf7fd
test(propagation): add dead fuel moisture invariant suite
HugoFara Aug 11, 2026
81089ea
fix(databroker): stop "moisture" being served the five-slot getter
HugoFara Aug 11, 2026
5f9d520
test(propagation): cover the models that register "moisture"
HugoFara Aug 11, 2026
3fb92a5
ci: run the model invariant suite on Linux
HugoFara Aug 11, 2026
75216d6
ci: run the invariants workflow on pull requests to dev
HugoFara Aug 12, 2026
b65f538
mv old files
antonio-leblanc Aug 12, 2026
f00d722
done
antonio-leblanc Aug 12, 2026
243a388
Merge pull request #156 from HugoFara/test/model-unit-suite
antonio-leblanc Aug 12, 2026
8b8f5b1
Merge branch 'master' into dev
antonio-leblanc Aug 12, 2026
9f02b4d
Merge pull request #158 from HugoFara/fix/moisture-ros-guards
antonio-leblanc Aug 12, 2026
7e40c6b
Merge remote-tracking branch 'origin/fix/model-properties-double-dele…
HugoFara Aug 12, 2026
823292d
Merge pull request #173 from forefireAPI/recover/stacked-fixes
HugoFara Aug 12, 2026
7c769c7
test(http): pin the HTTP command server's behaviour before hardening it
HugoFara Aug 12, 2026
e1df887
Merge pull request #174 from forefireAPI/test/http-server-characteris…
HugoFara Aug 12, 2026
eeead4f
test: add a concurrency stress test for free-threaded builds
HugoFara Aug 11, 2026
f95a5b2
fix(core): make the atom id counter atomic and the parameters singlet…
HugoFara Aug 11, 2026
b3ff1ff
refactor(core): give each string representation its own buffer
HugoFara Aug 12, 2026
411190f
Merge pull request #176 from forefireAPI/test/threading-stress
HugoFara Aug 12, 2026
41af42e
fix(tools): restore genForeFireCase.py, with the axis order fixed
HugoFara Aug 12, 2026
f270125
ci: add a blocking AddressSanitizer job
HugoFara Aug 12, 2026
478f232
Merge pull request #177 from forefireAPI/fix/atomic-id-and-singleton
antonio-leblanc Aug 12, 2026
cea09f4
Merge pull request #178 from forefireAPI/refactor/string-representati…
antonio-leblanc Aug 12, 2026
e4bffea
Merge pull request #180 from forefireAPI/ci/address-sanitizer
HugoFara Aug 12, 2026
b9bb29e
Merge branch 'master' into dev
antonio-leblanc Aug 12, 2026
2673b4e
fix(tests): make runANN work, and run it in CI
HugoFara Aug 12, 2026
f7e2892
Merge pull request #183 from forefireAPI/fix/wire-runann
HugoFara Aug 12, 2026
f9b20ac
docs: add a CHANGELOG
HugoFara Aug 12, 2026
b8c562f
docs: record the dev-only work, and the options it added
HugoFara Aug 12, 2026
96e359e
docs(rtd): document the pip install path
HugoFara Aug 12, 2026
c4ee9c5
docs(tests): correct the test READMEs against the tree
HugoFara Aug 12, 2026
7002dd8
docs(commands): document emit, and de-duplicate clear
HugoFara Aug 12, 2026
75c4441
docs: add what only exists on dev
HugoFara Aug 12, 2026
3c688bc
Merge pull request #185 from HugoFara/doc-changelog-dev
antonio-leblanc Aug 13, 2026
b81f686
Merge pull request #187 from HugoFara/doc-pip-and-stale-refs-dev
antonio-leblanc Aug 13, 2026
1d96b2e
Merge master into dev to keep dev from falling behind
antonio-leblanc Aug 13, 2026
b0bdc74
Merge pull request #188 from forefireAPI/sync-master-into-dev
antonio-leblanc Aug 13, 2026
ff2e68d
docs(python): document the NumPy array path, no NetCDF or GIS
HugoFara Aug 13, 2026
e0d4e1b
feat(python): add a forefire-validate landscape checker
HugoFara Aug 13, 2026
f872d43
Merge pull request #189 from forefireAPI/doc/numpy-case-page
antonio-leblanc Aug 13, 2026
1cd6891
Merge pull request #190 from forefireAPI/feat/landscape-validate
antonio-leblanc Aug 14, 2026
d3c9703
build: move stb_image_write.h into third_party/
HugoFara Aug 14, 2026
f319083
refactor(core): declare copy-assignment on FFPoint and FFVector
HugoFara Aug 14, 2026
7467f40
Merge pull request #191 from forefireAPI/refactor/ffpoint-copy-assign…
antonio-leblanc Aug 17, 2026
0cdc9ac
Merge pull request #192 from forefireAPI/build/vendor-stb-image-write
antonio-leblanc Aug 17, 2026
c5b243c
Merge remote-tracking branch 'origin/dev' into fix/restore-genforefir…
antonio-leblanc Aug 18, 2026
f4b3fd8
Merge pull request #179 from forefireAPI/fix/restore-genforefirecase
antonio-leblanc Aug 18, 2026
0765090
[python] helpers: import matplotlib only in the plotting functions
yanissrairi Oct 5, 2026
31a642d
Merge pull request #193 from yanissrairi/perf/lazy-matplotlib
antonio-leblanc Oct 5, 2026
613c83d
[tests] ignore PNG outputs in tests/runff
antonio-leblanc Oct 16, 2026
9e06ade
fix(core): stop quit[] from ending the host process (#181)
HugoFara Oct 21, 2026
cacd887
fix: clear[] crashed after a fire whose front had stopped (#194)
yanissrairi Oct 21, 2026
06014db
fix(core): give the propagation models and layers an owner (#182)
HugoFara Oct 21, 2026
6416da0
fix: clear[] aborted after a fire front merged with itself and was to…
yanissrairi Oct 21, 2026
d340c12
fix: segfault when a fire front was reduced to two nodes (#198)
yanissrairi Oct 21, 2026
61d9428
build: remove two stray parentheses from the unit test list
antonio-leblanc Oct 21, 2026
3caee53
fix(propagation): divide by the surface-to-volume ratio in Balbi2020 …
antonio-leblanc Oct 21, 2026
b1dd86d
feat(python): run a simulation in its own worker process (#200)
HugoFara Oct 21, 2026
648e5b4
fix(python): raise instead of segfaulting before FireDomain[...] (#202)
HugoFara Oct 21, 2026
df5a454
Merge master into dev (#199, #203)
antonio-leblanc Oct 21, 2026
3eb20d5
Merge pull request #204 from antonio-leblanc/merge/master-into-dev
antonio-leblanc Oct 21, 2026
db9a619
fix(core): stop the data broker freeing the domain's propagative laye…
HugoFara Oct 22, 2026
b496272
docs(changelog): fill in [Unreleased] ahead of v2.6.0 (#207)
HugoFara Oct 22, 2026
15ef907
fix(core): addIndexLayer returns true when it registers a fuel table …
antonio-leblanc Oct 22, 2026
77c3107
feat(build): FOREFIRE_ENABLE_NETCDF to build without NetCDF (#208)
antonio-leblanc Oct 22, 2026
32b9834
feat(build): compile the engine to WebAssembly (#210)
HugoFara Oct 23, 2026
6b57d34
feat(wasm): run a fire in the browser on real terrain (#211)
HugoFara Oct 23, 2026
e142544
feat(wasm): colour the burn by arrival time and the front by rate of …
HugoFara Oct 24, 2026
fee3d68
ci: pull only the runff LFS data, cached between runs
antonio-leblanc Oct 27, 2026
bd0b3d3
docker: skip the LFS check in the builder stage
antonio-leblanc Oct 27, 2026
e752723
Merge pull request #215 from antonio-leblanc/ci/lfs-runff-only
antonio-leblanc Oct 27, 2026
81ecf01
ci: build the WASM demo once and deploy it from the same workflow
antonio-leblanc Oct 27, 2026
3067639
ci: skip the builds on docs-only changes
antonio-leblanc Oct 27, 2026
2ee38cd
ci: cancel superseded PR runs
antonio-leblanc Oct 27, 2026
9802ccb
Merge pull request #216 from antonio-leblanc/ci/trim-runs
antonio-leblanc Oct 27, 2026
73eea82
ci: move actions off Node 20
antonio-leblanc Oct 27, 2026
4f3d48a
docs: link the WASM demo from the README
antonio-leblanc Oct 27, 2026
12cf2f7
docs: give the WASM demo its own icon
antonio-leblanc Oct 27, 2026
f624720
Merge pull request #217 from antonio-leblanc/ci/node24-actions
antonio-leblanc Oct 27, 2026
e907c91
Merge pull request #218 from antonio-leblanc/docs/wasm-demo-link
antonio-leblanc Oct 27, 2026
8d90c82
ci(docker): fix the image build on dev (#222)
antonio-leblanc Oct 27, 2026
576a3c0
perf: inline hot-path accessors, make FFPoint and FFVector trivially …
yanissrairi Oct 27, 2026
ffd84a6
docs: fix command examples that fail or are silently ignored (#223)
antonio-leblanc Oct 27, 2026
8090b4b
fix: use 3.59e-4 in the Rothermel wind exponent (#219)
antonio-leblanc Oct 27, 2026
0339d12
fix: stretch a fuel map that misses the domain, like the other layers…
yanissrairi Oct 27, 2026
9f4247b
chore(release): bump version to v2.6.0 (#214)
antonio-leblanc Oct 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/actions/lfs-runff/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Fetch runff LFS data
description: >
Pulls only the LFS files tests/runff reads (~4 MB of the ~61 MB in LFS),
cached by object id so a run downloads nothing unless they change.
Checkout must run with `lfs: false` first.

runs:
using: composite
steps:
- name: LFS cache key
id: key
shell: bash
run: echo "key=lfs-runff-$(git lfs ls-files -l -I 'tests/runff/*' | cut -d' ' -f1,3 | git hash-object --stdin)" >> "$GITHUB_OUTPUT"

- uses: actions/cache@v6
with:
path: .git/lfs
key: ${{ steps.key.outputs.key }}

- name: Pull runff LFS data
shell: bash
run: git lfs pull --include="tests/runff/*"
18 changes: 14 additions & 4 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@ name: 📦 Build & Test Docker Image

on:
push:
paths-ignore: [ "docs/**", "**/*.md" ]
branches: [ "master" ]
tags: [ 'v*.*.*' ]
pull_request:
paths-ignore: [ "docs/**", "**/*.md" ]
branches: [ "master" ]
workflow_dispatch:

# A new push to a PR cancels its older run. Pushes to branches, tags and
# releases each get their own group, so none is ever queued or dropped.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
Expand All @@ -23,18 +31,20 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v6
with:
lfs: true
lfs: false # only tests/runff is needed, fetched below

- uses: ./.github/actions/lfs-runff

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
Expand All @@ -45,7 +55,7 @@ jobs:

- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v5
uses: docker/build-push-action@v7
with:
context: .
# Only push if the ref starts with 'refs/tags/'
Expand Down
65 changes: 65 additions & 0 deletions .github/workflows/invariants.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: 🔬 Model Invariants (Linux)

# Complements the runff test in main.yml. That one compares ForeFire against
# frozen ForeFire output, so it cannot distinguish a physics fix from a
# physics regression. This one asserts properties that follow from the
# published spread equations and holds no reference data.
#
# It is a separate workflow because it needs the pyforefire extension, which
# install-forefire.sh does not build: outside a wheel build, CMakeLists
# defaults FOREFIRE_BUILD_PYTHON to OFF.

on:
push:
paths-ignore: [ "docs/**", "**/*.md" ]
branches:
- "master"
- "dev"
pull_request:
paths-ignore: [ "docs/**", "**/*.md" ]
branches: [ "master", "dev" ]
workflow_dispatch:

# A new push to a PR cancels its older run. Pushes to branches, tags and
# releases each get their own group, so none is ever queued or dropped.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true

jobs:
invariants:
name: Dead fuel moisture invariants
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
# No `lfs: true` here, unlike main.yml. The suite builds its fuel, wind,
# temperature and moisture layers in memory and needs no fixtures.
- name: Checkout repository
uses: actions/checkout@v6

- name: Install Dependencies
run: |
sudo apt-get update -y
sudo apt-get install -y --no-install-recommends \
build-essential cmake python3 python3-pip python3-venv
# NetCDF C++ legacy API for the build, C base at runtime
sudo apt-get install -y --no-install-recommends \
libnetcdf-dev libnetcdf-c++4-dev

- name: Build and install pyforefire
# A venv keeps this clear of PEP 668 externally-managed system Python.
# pip's build isolation pulls scikit-build-core and pybind11 from
# pyproject.toml, and numpy comes in as a runtime dependency.
run: |
python3 -m venv .venv
./.venv/bin/python -m pip install --upgrade pip
./.venv/bin/python -m pip install .

- name: Check pyforefire imports
run: ./.venv/bin/python -c "import pyforefire; print(pyforefire.__file__)"

- name: Run dead fuel moisture invariants
run: ./.venv/bin/python tests/python/test_moisture_invariants.py

- name: Run the worker process runner test
run: ./.venv/bin/python tests/python/test_runner.py
22 changes: 20 additions & 2 deletions .github/workflows/macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,48 @@ name: 🧪 Test Native (macOS)

on:
push:
paths-ignore: [ "docs/**", "**/*.md" ]
branches:
- brew
pull_request:
branches: [ "master" ]
paths-ignore: [ "docs/**", "**/*.md" ]
branches: [ "master", "dev" ]
workflow_dispatch:

# A new push to a PR cancels its older run. Pushes to branches, tags and
# releases each get their own group, so none is ever queued or dropped.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true

jobs:
build-native-macos:
runs-on: macos-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
lfs: true
lfs: false # only tests/runff is needed, fetched below

- uses: ./.github/actions/lfs-runff

- name: Make install script executable
run: chmod +x ./install-forefire-osx.sh

- name: Build ForeFire using macOS install script
# The LFS check would curl back the LFS files the step above skips.
env:
FOREFIRE_CHECK_LFS: "OFF"
run: ./install-forefire-osx.sh

- name: Check ForeFire version
run: ./bin/forefire -v

# Also the check that the recorded rates of spread survive a different
# compiler and a different CPU.
- name: Run C++ unit tests
run: ctest --test-dir build --output-on-failure

- name: Install Python test dependencies
run: pip3 install --break-system-packages lxml xarray netCDF4

Expand Down
42 changes: 37 additions & 5 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,31 @@ name: 🧪 Test Native (Linux)

on:
push:
paths-ignore: [ "docs/**", "**/*.md" ]
branches:
- "master"
# - "dev"
- "dev"
pull_request:
branches: [ "master" ]
paths-ignore: [ "docs/**", "**/*.md" ]
branches: [ "master", "dev" ]
workflow_dispatch:

# A new push to a PR cancels its older run. Pushes to branches, tags and
# releases each get their own group, so none is ever queued or dropped.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true

jobs:
build-native:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
lfs: true # we use LFS to store large files (e.g. data.nc)
lfs: false # only tests/runff is needed, fetched below

- uses: ./.github/actions/lfs-runff

- name: Verify LFS test asset (data.nc)
run: |
Expand All @@ -38,11 +48,24 @@ jobs:
pip3 install --no-cache-dir lxml xarray netCDF4

- name: ForeFire build
run: sudo bash ./install-forefire.sh -y
# The LFS check would curl back the LFS files the step above skips.
run: sudo env FOREFIRE_CHECK_LFS=OFF bash ./install-forefire.sh -y

- name: Check ForeFire version
run: ./bin/forefire -v

# The unit tests are built by install-forefire.sh along with everything
# else (FOREFIRE_BUILD_TESTS defaults on outside wheel builds), so this
# only has to run them. They cover the propagation and flux models one
# call at a time, which is what runff below cannot reach: it exercises a
# single case through Rothermel.
- name: Run C++ unit tests
run: |
# The build step above runs under sudo, so build/ is root-owned and
# ctest cannot create build/Testing/Temporary as the runner user.
sudo chown -R "$(id -u):$(id -g)" build
ctest --test-dir build --output-on-failure

- name: Add Build/Runtime Diagnostics
run: |
echo "--- ForeFire Linkage ---"
Expand All @@ -56,4 +79,13 @@ jobs:
- name: Run 'runff' Test Script
run: |
cd tests/runff
bash ff-run.bash # Execute the complete test logic script
bash ff-run.bash # Execute the complete test logic script

# The only coverage for ANNPropagationModel and BMapLoggerForANNTraining:
# both read a .ffann network in their constructor and abort without one,
# so the unit suite cannot reach them. ANN_test is built by the step
# above, since FOREFIRE_BUILD_TOOLS defaults on outside wheel builds.
- name: Run 'runANN' Test Script
run: |
cd tests/runANN
bash run.bash
96 changes: 96 additions & 0 deletions .github/workflows/sanitizers.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: 🧼 Sanitizers (Linux)

# AddressSanitizer over both test paths: the unit suite, which constructs and
# destroys every propagation and flux model one call at a time, and runff,
# which runs a full simulation with a reload and NetCDF and KML output.
#
# This is a blocking check from the day it lands, which is only possible
# because ForeFire reports zero ASan errors today — no use-after-free, no
# overflow, no double free, on either path. There is no backlog to clear
# first, and the point of turning it on now is to keep it that way. See #162.
#
# Leaks are blocking for the unit suite. They were not when this job landed —
# nothing owned a PropagationModel, so ASan reported every one as leaked, about
# 4.7 MB in all. #159 gave the models, the propagative layer and two orphaned
# constant-layer arrays an owner, which took the unit suite to zero, so the
# gate went on. runff still leaks ~200 kB on paths the unit suite does not
# reach, so its leak check stays informational until those are found too.
#
# A separate workflow rather than a job in main.yml because it needs its own
# build: install-forefire.sh builds with -O3 -flto -fomit-frame-pointer, which
# is the opposite of what a sanitizer wants.

on:
push:
paths-ignore: [ "docs/**", "**/*.md" ]
branches:
- "master"
- "dev"
pull_request:
paths-ignore: [ "docs/**", "**/*.md" ]
branches: [ "master", "dev" ]
workflow_dispatch:

# A new push to a PR cancels its older run. Pushes to branches, tags and
# releases each get their own group, so none is ever queued or dropped.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true

jobs:
address-sanitizer:
name: AddressSanitizer
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
lfs: false # only tests/runff is needed, fetched below

- uses: ./.github/actions/lfs-runff

- name: Install Dependencies
run: |
sudo apt-get update -y
sudo apt-get install -y --no-install-recommends \
build-essential cmake python3 python3-pip
sudo apt-get install -y --no-install-recommends \
libnetcdf-dev libnetcdf-c++4-dev
pip3 install --no-cache-dir --break-system-packages lxml xarray netCDF4

- name: Build with AddressSanitizer
# FOREFIRE_SANITIZE also switches the compile flags to -g -O1
# -fno-omit-frame-pointer, so the stack traces are readable.
run: |
cmake -S . -B build-asan -DFOREFIRE_SANITIZE=address -DFOREFIRE_CHECK_LFS=OFF
cmake --build build-asan -j"$(nproc)"

# Leaks are blocking here: the unit suite reports none since the model
# and layer ownership fix in #159, and this is what keeps it that way.
- name: Unit tests under ASan
env:
ASAN_OPTIONS: detect_leaks=1
run: ctest --test-dir build-asan --output-on-failure

# runff still leaks about 200 kB over 783 allocations, on paths a single
# simulation reaches and the unit suite does not, so its leak check stays
# informational below. Errors are blocking here as they always were.
- name: runff under ASan
env:
ASAN_OPTIONS: detect_leaks=0
run: |
cd tests/runff
bash ff-run.bash

# Informational only, and only for runff now. Keeps the figure visible so
# the remaining ownership gaps can be found and this can join the gate.
- name: runff leak report (informational)
continue-on-error: true
env:
ASAN_OPTIONS: detect_leaks=1
run: |
echo "::group::runff leaks"
cd tests/runff
bash ff-run.bash || true
echo "::endgroup::"
Loading
Loading