diff --git a/app/forefire/commands.md b/app/forefire/commands.md index 1c473ff..3cf6d19 100644 --- a/app/forefire/commands.md +++ b/app/forefire/commands.md @@ -77,6 +77,17 @@ std::string commandHelp = R"( - 'vel': Velocity vector (vx,vy,vz) associated with the trigger - 't': Time at which the trigger is activated; +## emit + emit[layer=;loc=(x,y,z);radius=;duration=;flux=] + Injects a flux into an existing flux layer, over an area, for a time span starting at the current domain time + Example: emit[layer=heatFlux;loc=(5000,5000,0);radius=50;duration=600;frp=120] + Arguments: + - 'layer': Name of the flux layer to emit into, as given to addLayer. 'name' is accepted as an alias + - 'loc': Centre of the emission in domain coordinates. Alternatives: 'lonlat' in WGS84, or a 'sw'/'ne' box, or a 'swlonlat'/'nelonlat' box + - 'radius': Radius in metres, giving a disc of area pi*r^2. Alternatives: 'surface' or 'area' in m2, or the area of the sw/ne box + - 'duration': Length of the emission in seconds, required. It starts at the current domain time + - 'flux': The emitted quantity per square metre per second, in the layer's own units. Alternatives, any one of: 'frp' in MW, converted using the FRPToWatts parameter and divided by the area; 'value' or 'val', a power divided by the area; 'total', an energy divided by area and duration; + ## goTo goTo[t=seconds] Advances the simulation to the specified time @@ -179,11 +190,9 @@ std::string commandHelp = R"( - 'filename.ff': Filename containing simulation commands to execute ## clear - clear - Clears all simulation data - Example: clear - Arguments: - - Clears the simulation data to reset the state + clear[] + Frees the fire domain and cancels every scheduled event. Parameters are kept, so a new FireDomain can be created without setting them again + Example: clear[] ## systemExec systemExec[command=] @@ -200,10 +209,6 @@ std::string commandHelp = R"( - 'host': Hostname or IP address for the server - 'port': Port number on which the server will listen; -## clear - clear[] - clears-up memory except parameters, no more schedueled events or fire domain. - ## quit quit[] Terminates the simulation diff --git a/docs/source/conf.py b/docs/source/conf.py index 57574fa..b87f841 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -6,10 +6,23 @@ # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information +import pathlib +import re + project = 'ForeFire' copyright = '2014 - 2025, J-B Filippi' author = 'Filippi, Jean Baptiste' -release = '2.0.0' + +# Single source of truth for the version, the same file CMake and +# scikit-build-core read. Hard-coding it here left the site advertising 2.0.0 +# for three releases. +_version_header = pathlib.Path(__file__).resolve().parents[2] / 'src' / 'include' / 'Version.h' +_match = re.search(r'ff_version\s*=\s*"v?([0-9]+\.[0-9]+\.[0-9]+)"', + _version_header.read_text(encoding='utf-8')) +if not _match: + raise RuntimeError(f'could not parse ff_version from {_version_header}') +release = _match.group(1) +version = release # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/docs/source/getting_started/installation.rst b/docs/source/getting_started/installation.rst index 0238a01..a91c65d 100644 --- a/docs/source/getting_started/installation.rst +++ b/docs/source/getting_started/installation.rst @@ -1,65 +1,137 @@ -Installation: Build from Source -================================ +Installation +============ -This guide explains how to install ForeFire by compiling it directly on your Linux/Unix-like system. This gives you a native build, but requires managing dependencies. -There are two main ways to build from source: +There are three ways to install ForeFire. Which one you want depends on what +you intend to do with it: -- Option 1: Using the provided install script (easiest for Debian/Ubuntu) -- Option 2: Following manual steps (for all systems or finer control). +.. list-table:: + :header-rows: 1 + :widths: 20 45 35 -Prerequisites -------------- + * - Method + - Use it when + - Section + * - **pip** + - You want to run simulations or drive ForeFire from Python. + - :ref:`install-pip` + * - **Docker** + - You are on Windows, or you want the web console with no setup. + - :doc:`quickstart` + * - **From source** + - You need MPI coupling with Meso-NH, a CPU-tuned build, or you are + working on ForeFire itself. + - :ref:`install-source` -If you choose the **manual build steps** (Option 2), or if you simply want to understand what tools are needed, you must ensure the following are installed on your system: +.. _install-pip: -- **C++ Compiler:** A modern C++ compiler (like `g++`). Package typically called `build-essential` or similar. -- **CMake:** Build system generator (`cmake`). -- **Make:** Build tool (`make`). -- **NetCDF Libraries:** ForeFire requires NetCDF support. The specific package needed is the C++ interface. +Install with pip +---------------- - - On **Debian/Ubuntu**, the install script uses `libnetcdf-c++4-dev`. - - On other systems, find the equivalent package (e.g., `netcdf-cxx-devel`, `netcdf-cxx4`). - - *Note: Ensure this is the correct/intended library. Older docs might mention `libnetcdf-cxx-legacy-dev`. Verify which one is actually required by the current CMake setup.* +This is the fastest route, and it needs no compiler, no CMake and no NetCDF +installation of your own — NetCDF is bundled inside the wheel. -Build Options -------------- +.. code-block:: bash -**Option 1: Using the Install Script (Recommended for Debian/Ubuntu)** + pip install forefire -The repository provides a convenience script (`install-forefire.sh`) that automates the process on Debian-based systems like Ubuntu. +Requirements +~~~~~~~~~~~~ -1. **Clone the repository:** +- **CPython 3.9 to 3.14.** The free-threaded builds (``cp314t``) are + deliberately not published: the C++ core still keeps mutable global state, so + a wheel advertising free-threading would silently re-enable the GIL on + import. +- **Linux** on ``x86_64`` or ``aarch64`` (``manylinux_2_28`` or newer), or + **macOS 14+ on Apple Silicon**. - .. code-block:: bash +There is no wheel for Intel macOS, musl-based Linux (Alpine), 32-bit targets, +Windows or PyPy. On those platforms pip falls back to the source distribution, +which needs the build prerequisites in :ref:`install-source`. Windows users are +better served by Docker — see :doc:`quickstart`. - git clone https://github.com/forefireAPI/forefire.git - cd forefire +What you get +~~~~~~~~~~~~ -2. **Run the install script:** +Both interfaces, from the one package: - .. warning:: +.. code-block:: bash - This script requires `sudo` privileges to install system packages using `apt`. Review the script if you have concerns. + forefire -v - .. code-block:: bash +.. code-block:: python - sudo bash install-forefire.sh + import pyforefire as forefire + + ff = forefire.ForeFire() + ff.execute("FireDomain[sw=(0,0,0);ne=(10000,10000,0);t=0]") + ff.addLayer("propagation", "Iso", "propagationModel") + ff.execute("startFire[loc=(5000,5000,0.0)]") + ff.execute("step[dt=1000]") + print(ff.execute("print[]")) + +.. note:: -**What the Install Script Does:** + The distribution on PyPI is called ``forefire``; the importable module keeps + its historical name, ``pyforefire``. -- **Updates Package Lists and Installs Dependencies:** Runs `apt-get update` and installs prerequisites listed above -- **Builds ForeFire:** using CMake and Make. -- **Reports Install Location:** Prints the location of the built binaries (usually `$PROJECT_ROOT/bin`). -- **(Optional) Updates PATH:** +What you do not get +~~~~~~~~~~~~~~~~~~~ - - Prompts the user if they want to add the ForeFire `bin` directory to their PATH permanently. - - If 'yes', it appends `export PATH=` and `export FOREFIREHOME=` lines to the user's `~/.bashrc` file. - - It tries to detect the correct user's home directory even when run with `sudo` (using `$SUDO_USER`). - - **Note:** This only modifies `.bashrc`. If you use a different shell (like `zsh` or `fish`), you will need to configure the PATH manually (see below). +Published wheels are built with three options turned off, so a few things are +only available in a source build: -**Option 2: Manual Build Steps (All Linux/Unix-like Systems)** +- **MPI coupling.** Fire-atmosphere runs with Meso-NH need + ``FOREFIRE_ENABLE_MPI``, so they need a source build. +- **CPU tuning.** Wheels are built without ``-march=native``, so that they run + on any machine of the right architecture. A source build with + ``FOREFIRE_NATIVE_ARCH=ON`` will be faster on the machine that built it. +- **The** ``ANN_test`` **helper**, built by ``FOREFIRE_BUILD_TOOLS``. -Use this method if you are not on Debian/Ubuntu, prefer manual control, or don't want to use the install script. +.. _install-source: + +Build from source +----------------- + +A native build, in exchange for managing the dependencies yourself. Two routes: +the install script (Debian/Ubuntu only), or manual steps (any Unix-like +system). + +Prerequisites +~~~~~~~~~~~~~ + +- **A C++ compiler**, such as ``g++``. On Debian/Ubuntu this comes with + ``build-essential``. +- **CMake** 3.15 or newer, and **Make**. +- **NetCDF — both the C library and the legacy C++4 API.** The C++4 API is a + separate package from the C library on every distribution, and it is the one + people usually miss. + + .. list-table:: + :header-rows: 1 + :widths: 25 75 + + * - System + - Packages + * - Debian/Ubuntu + - ``apt install libnetcdf-dev libnetcdf-c++4-dev`` + * - Fedora/RHEL + - ``dnf install netcdf-devel netcdf-cxx4-devel`` + * - macOS (Homebrew) + - ``brew install netcdf netcdf-cxx`` + + The library is named ``netcdf_c++4`` everywhere except Homebrew, which calls + it ``netcdf-cxx4`` (``netcdf-cxx`` in older bottles); CMake looks for all + three. The older ``libnetcdf-cxx-legacy-dev`` package is a *different*, + pre-C++4 API and will not work. + + If NetCDF is installed somewhere CMake does not search, point at it with + ``-DNETCDF_HOME=/path/to/netcdf`` (and ``-DNETCDF_CXX_HOME=...`` if the C++ + API lives elsewhere). + +Option 1: the install script (Debian/Ubuntu) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``install-forefire.sh`` automates the process on Debian-based systems. 1. **Clone the repository:** @@ -68,55 +140,143 @@ Use this method if you are not on Debian/Ubuntu, prefer manual control, or don't git clone https://github.com/forefireAPI/forefire.git cd forefire -2. **Install Prerequisites Manually:** - - Use your system's package manager to install `cmake`, `make`, a C++ compiler (`build-essential` or equivalent), and the required NetCDF C++ development library (e.g., `libnetcdf-c++4-dev`, `netcdf-cxx-devel`, etc.). - Example for Debian/Ubuntu (if not using the script): - - .. code-block:: bash +2. **Run the install script:** - sudo apt update - sudo apt install build-essential cmake libnetcdf-c++4-dev # Verify package name! + .. warning:: -3. **Create a build directory and run CMake & Make:** + This script requires ``sudo`` privileges to install system packages using + ``apt``. Review the script if you have concerns. .. code-block:: bash - mkdir build - cd build - cmake .. - make + sudo bash install-forefire.sh -The main executable `forefire` will be located at `../bin/forefire` (relative to the `build` directory). Check installation with - - .. code-block:: bash +**What the install script does:** - cd .. # Go back to the root of the repository - ./bin/forefire -v +- **Installs dependencies:** runs ``apt-get update`` and installs the + prerequisites listed above. +- **Builds ForeFire** using CMake and Make. +- **Reports the install location:** usually ``$PROJECT_ROOT/bin``. +- **(Optional) updates PATH:** + + - Prompts you before doing anything, and only if you agree appends + ``export PATH=`` and ``export FOREFIREHOME=`` lines to ``~/.bashrc``. + - It detects the invoking user's home directory even under ``sudo``, via + ``$SUDO_USER``. + - **Note:** this only modifies ``.bashrc``. For ``zsh`` or ``fish``, + configure the PATH manually (see below). -4. **Making ForeFire Executable System-Wide (Manual PATH setup)** +Option 2: manual build (any Linux/Unix-like system) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - If you built from source (manually or via the script but declined the automatic PATH update, or use a shell other than bash), the `forefire` executable is in the `bin` directory within the repository. To run it easily from any location, add this directory to your system's PATH environment variable. +Use this if you are not on Debian/Ubuntu, prefer manual control, or do not +want to run the install script. - **For the current terminal session:** +1. **Clone the repository:** .. code-block:: bash - # Execute this from the root of the forefire repository - export PATH=$PATH:`pwd`/bin + git clone https://github.com/forefireAPI/forefire.git + cd forefire - **Permanently:** +2. **Install the prerequisites** for your system, from the table above. - Add the following line to your shell's configuration file (e.g., `~/.bashrc`, `~/.zshrc`, `~/.profile`, or `~/.config/fish/config.fish`). Replace `/path/to/forefire` with the actual absolute path to the cloned repository. +3. **Configure and build:** .. code-block:: bash - export PATH="/path/to/forefire/bin:$PATH" + cmake -S . -B build + cmake --build build -j - *Optional:* The install script also sets `export FOREFIREHOME="/path/to/forefire"`. You may want to add this line as well, as some scripts or components might potentially use it. + The executable lands in ``bin/forefire``. Check it with: .. code-block:: bash - export FOREFIREHOME="/path/to/forefire" + ./bin/forefire -v - After editing your configuration file, either restart your terminal or reload the configuration (e.g., `source ~/.bashrc`). \ No newline at end of file +Build options +~~~~~~~~~~~~~ + +The build is driven by ``FOREFIRE_*`` CMake options; pass them at configure +time, for example ``cmake -S . -B build -DFOREFIRE_ENABLE_MPI=OFF``. + +.. list-table:: + :header-rows: 1 + :widths: 30 15 55 + + * - Option + - Default + - Effect + * - ``FOREFIRE_ENABLE_MPI`` + - ON + - Fire-atmosphere coupling with Meso-NH, when MPI is found. Replaces the + compiler with the MPI wrapper. + * - ``FOREFIRE_NATIVE_ARCH`` + - ON + - ``-march=native``. Turn it off for a binary you intend to move to + another machine. + * - ``FOREFIRE_BUILD_PYTHON`` + - OFF + - Build the ``pyforefire`` extension module. + * - ``FOREFIRE_STATIC_CORE`` + - OFF + - Link the core statically. + * - ``FOREFIRE_BUILD_TOOLS`` + - ON + - Build the ``ANN_test`` helper, needed by ``tests/runANN``. + * - ``FOREFIRE_CHECK_LFS`` + - ON + - Fail early if the Git LFS test fixtures were not pulled. + * - ``FOREFIRE_BUILD_TESTS`` + - ON + - Build the C++ unit tests and register them with CTest. + * - ``FOREFIRE_ENABLE_WARNINGS`` + - ON + - Compile ForeFire's own sources with ``-Wall -Wextra``. + * - ``FOREFIRE_WARNINGS_AS_ERRORS`` + - OFF + - Fail the build on a compiler warning. + * - ``FOREFIRE_SANITIZE`` + - *(empty)* + - Sanitizers to build with, passed to ``-fsanitize=``, for example + ``address,undefined``. + +Wheel builds flip six of these: MPI, native-arch, tools, tests and the LFS +check off, Python and the static core on. That is what makes a wheel run on a +machine other than the one that built it. + +``TESTING.md`` at the repository root covers the test suites and the sanitizer +build in detail. + +Making ForeFire available system-wide +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +After a source build, ``forefire`` lives in the repository's ``bin`` +directory. To run it from anywhere, add that directory to your PATH. + +**For the current terminal session:** + +.. code-block:: bash + + # Execute this from the root of the forefire repository + export PATH=$PATH:`pwd`/bin + +**Permanently:** + +Add the following to your shell's configuration file (``~/.bashrc``, +``~/.zshrc``, ``~/.profile``, or ``~/.config/fish/config.fish``), replacing +``/path/to/forefire`` with the absolute path to the cloned repository. + +.. code-block:: bash + + export PATH="/path/to/forefire/bin:$PATH" + +*Optional:* the install script also sets ``FOREFIREHOME``, which some scripts +and components use to locate the repository. + +.. code-block:: bash + + export FOREFIREHOME="/path/to/forefire" + +Then restart your terminal or reload the configuration, for example with +``source ~/.bashrc``. diff --git a/docs/source/getting_started/quickstart.rst b/docs/source/getting_started/quickstart.rst index 2a575b1..e4b5dfc 100644 --- a/docs/source/getting_started/quickstart.rst +++ b/docs/source/getting_started/quickstart.rst @@ -1,15 +1,62 @@ Quick Start =========== -This guide shows the quickest way to get the standard **ForeFire example simulation running** using its interactive web console, powered by Docker. This method bundles all dependencies, so you don't need to install them on your host system, and is the only way to run ForeFire on Windows. +Your first simulation, two ways: from a pip install in about a minute, or in +Docker with the interactive web console. + +.. _quickstart-pip: + +The one-minute version, with pip +-------------------------------- + +On Linux or macOS on Apple Silicon, nothing needs to be compiled and no +dependencies need to be installed on your system: + +.. code-block:: bash + + pip install forefire + +Then run a simulation from Python: + +.. code-block:: python + + import pyforefire as forefire + + ff = forefire.ForeFire() + ff.execute("FireDomain[sw=(0,0,0);ne=(10000,10000,0);t=0]") + ff.addLayer("propagation", "Iso", "propagationModel") + ff.execute("startFire[loc=(5000,5000,0.0)]") + ff.execute("step[dt=1000]") + print(ff.execute("print[]")) + +``print[]`` returns the state of the simulation as text — one ``FireNode`` +entry per node of the front, each with its location, velocity and time. The +same commands work in the interactive interpreter, which the same install +provides: + +.. code-block:: bash + + forefire + +See :doc:`installation` for which platforms have wheels, and +:doc:`/user_guide/forefire_script` for what these commands mean. + +.. _quickstart-docker: + +The full example, with Docker +----------------------------- + +This runs the standard **ForeFire example simulation** in its interactive web +console. It bundles all dependencies, so nothing is installed on your host +system, and it is the only way to run ForeFire on Windows. Prerequisites -------------- +~~~~~~~~~~~~~ - Docker installed and running on your system. - Git installed (for cloning the repository). Steps ------ +~~~~~ 1. **Clone the ForeFire repository:** Open your terminal and run: diff --git a/docs/source/index.rst b/docs/source/index.rst index 33ba48c..84ac7a8 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -6,6 +6,7 @@ Welcome to the official documentation for ForeFire — the open-source wildfire **Key Links:** - `GitHub Repository `_ +- `ForeFire on PyPI `_ — ``pip install forefire`` - `Online Demo Simulator `_ .. toctree:: diff --git a/docs/source/reference/commands.rst b/docs/source/reference/commands.rst index 7910f1e..d52eec4 100644 --- a/docs/source/reference/commands.rst +++ b/docs/source/reference/commands.rst @@ -336,6 +336,62 @@ Triggers a change in simulation data at a specific time and location. Can be use trigger[fuelType=wind;vel=(5.0,2.0,0.0);t=1800] # Trigger new wind at t=1800s +.. _cmd-emit: + +``emit`` +~~~~~~~~ + +.. code-block:: none + + emit[layer=;loc=(x,y,z);radius=;duration=;flux=] + +Injects a flux into an existing flux layer, over an area, for a time span +starting at the domain's current time. The layer must already exist — create it +with :ref:`addLayer ` first. + +It lets a source that is not the simulated fire front contribute to a flux +layer. The ``frp`` form takes fire radiative power in megawatts, the unit +satellite products report. + +**Arguments:** + +* ``layer=``: Name of the flux layer to emit into, as given to ``addLayer``. ``name=`` is accepted as an alias. **Required.** +* ``duration=``: Length of the emission, in seconds, starting at the domain's current time. **Required.** + +*Where* — give exactly one of: + +* ``loc=(x,y,z)``: Centre, in domain coordinates. +* ``lonlat=(lon,lat,z)``: Centre, in WGS84. +* ``sw=(x,y,z)`` with ``ne=(x,y,z)``: A box, in domain coordinates. Its centre and area are used. +* ``swlonlat=(lon,lat,z)`` with ``nelonlat=(lon,lat,z)``: The same, in WGS84. + +*Over what area* — from the box if one was given, otherwise: + +* ``radius=``: A disc, of area ``pi * r²``. +* ``surface=`` or ``area=``: The area directly. + +*How much* — give exactly one of: + +* ``flux=``: The emitted quantity per square metre per second, in the layer's own units — W/m² for a heat flux layer. +* ``frp=``: Fire radiative power in megawatts, converted to watts through the ``FRPToWatts`` parameter (1e7 if it is not set) and divided by the area. +* ``value=`` or ``val=``: A total power, divided by the area. +* ``total=``: A total energy, divided by the area and the duration. + +**Example:** + +.. code-block:: none + + addLayer[name=heatFlux;type=flux;modelName=heatFluxBasic] + emit[layer=heatFlux;lonlat=(9.05,41.95,0);radius=50;duration=600;frp=120] + +.. note:: + + Every failure is reported on standard output and the command returns an + error: no active ``FireDomain``, an unknown layer name, a missing + ``duration``, a missing location, or a magnitude given as ``frp``/``value``/ + ``total`` with no usable area. + + .. _cmd-print: ``print`` @@ -486,7 +542,7 @@ Executes ForeFire commands contained within the specified script file. The filen clear[] -Clears all simulation data (domain, fronts, nodes, loaded data) and scheduled events, resetting the simulation state. Parameters are generally kept. +Frees the fire domain — with its fronts, nodes and loaded data — and cancels every scheduled event. Parameters are kept, so a new ``FireDomain`` can be created without setting them again. **Example:** diff --git a/tests/README.md b/tests/README.md index 31844cd..ff00208 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,93 +1,149 @@ +# ForeFire test suite -# *FireFront* Test Suite +Six sets of tests, one per directory, covering the different interfaces and +use-cases of ForeFire. -This directory contains **five** sets of tests that showcase the different interfaces and use-cases of *ForeFire*. +| Directory | What it covers | Needs | +| --- | --- | --- | +| `unit` | C++ tests of one model at a time, without running a simulation, covering every registered propagation and flux model | Nothing beyond the build; see `unit/README.md` | +| `mnh_ideal` | ForeFire / **Meso-NH** coupling on an idealised atmospheric case | `SRC_MESONH` set; Meso-NH compiled with the ForeFire library in its `exe` directory | +| `mnh_real_nested` | ForeFire / **Meso-NH** coupling on a real nested case | Same as above | +| `python` | The Python bindings, through two example simulations | `PYTHONEXE` set to a Python interpreter that can `import pyforefire` | +| `runANN` | The built-in feed-forward network evaluator, on a network fitted to Rothermel | `bin/ANN_test`, built by default (`-DFOREFIRE_BUILD_TOOLS=ON`) | +| `runff` | The command-line interpreter: run a case, save and reload state, export KML and GeoJSON | ForeFire only | -| Folder / script | Purpose of the test | Specific dependencies | -| --------------- | ------------------ | --------------------- | -| `mnh_ideal` | *ForeFire* / **Meso-NH** coupling on an idealised atmospheric case | `SRC_MESONH` must be set; Meso-NH compiled with the *ForeFire* library placed in its `exe` directory | -| `mnh_real_nested` | *ForeFire* / **Meso-NH** coupling on a real nested case | Same requirements as above | -| `python` | Usage examples via the Python API:
• `idealized_wind.py` (360° rotating wind)
• `percolation.py` (propagation through fuels of random density) | Python bindings must be set and PYTHONEXE set to the python eneble binary | -| `runANN` | Activation of a serialised ANN graph (`rothermel.fann`) that reproduces the Rothermel model | `tensorflow` (or `torch` if a PyTorch graph is supplied) | -| `runff` | Using the *ForeFire* command-line interpreter to:
1. launch a real simulation;
2. save/load a state;
3. convert outputs (FF, KML, GeoJSON) | *ForeFire* only | +`TESTING.md` at the repository root describes each of these in more detail, +along with the sanitizer build. ---- +The `unit` suite is not run by `run.bash` below — it is a CTest suite, built +with everything else: -## 1. General prerequisites +```bash +cmake -S . -B build && cmake --build build -j +ctest --test-dir build --output-on-failure +``` -* **ForeFire** compiled and available in the `bin` dir. +## Prerequisites -* *(optional)* **Meso-NH** – required only for the `mnh_*` tests. -```bash +* **ForeFire built**, with the binaries in `bin/`. + +* The test fixtures are stored in **Git LFS**. `git lfs pull` if the `.nc` + files look like short text stubs. + +* *(optional)* **Meso-NH**, for the `mnh_*` tests only: + + ```bash export SRC_MESONH=/path/to/your/mesonh -``` + ``` -* *(optional)* **Python bindings** – required for the `python` folder +* *(optional)* **A Python interpreter with the bindings available**, for the + `python` tests only. This is either a build with + `-DFOREFIRE_BUILD_PYTHON=ON`, or an environment where `pip install forefire` + has been run: -```bash - export PYTHONEXE=/path/to/your/python_forefire_enabled -``` + ```bash + export PYTHONEXE=/path/to/your/python + ``` + +* The verification scripts under `runff` import `lxml`, `xarray` and + `netCDF4`: ---- + ```bash + pip install lxml xarray netCDF4 + ``` -## 2. Running the tests +## Running them ```bash - make test +cd tests +bash run.bash # or: make test ``` ---- +`run.bash` skips the `mnh_*` tests when `SRC_MESONH` is unset and the `python` +tests when `PYTHONEXE` is unset, runs `runff` and `runANN` unconditionally, and +prints a pass/fail summary. It exits non-zero if any suite failed. -## 3. Cleaning up +To clean up the outputs (ForeFire dumps, figures, NetCDF files): ```bash -make clean +bash clean.bash # or: make clean ``` -Removes intermediate files (ForeFire outputs, figures, NetCDF files, …) generated by all tests. +## What each set does + +### `mnh_ideal` + +Validates the coupling on a simplified atmospheric profile. Expect a fire front +consistent with the prescribed wind, plus NetCDF and KML outputs. + +### `mnh_real_nested` + +Reproduces a real fire scenario across two nested Meso-NH domains (real forcing +plus a high-resolution nest). It also exercises high-frequency output and the +HTTP web interface, which needs `FOREFIREHOME` set. + +### `python` + +`run.bash` runs two scripts with `$PYTHONEXE`: ---- +| Script | What it does | Output | +| --- | --- | --- | +| `percolation.py` | Four fires, each in a band of randomly filled fuel at a different density | `percolation.nc` | +| `idealizedwind.py` | Wind rotating from 0° to 360°, giving a circular front | `360wind.png`, written by ForeFire's own `plot[]` command | -## 4. Test set details +The only assertion is that `360wind.png` was produced and is not empty — these +are demonstrations of the API rather than tests of the physics. -### 4.1 `mnh_ideal` +`farsite_flat.py` is in this directory but is **not** run by `run.bash`. It +compares ForeFire against a FARSITE case, and needs `flatland.lcp`, which is +not in the repository — `python/README.md` has the download URL. -* **Goal** – validate the coupling on a simplified atmospheric profile. -* **Expected outcome** – a fire front consistent with the prescribed wind, plus NetCDF and KML outputs. +Three test scripts sit here too, and `run.bash` runs none of them: -### 4.2 `mnh_real_nested` +| File | What it checks | Where it runs | +| --- | --- | --- | +| `test_moisture_invariants.py` | That rate of spread responds to dead fuel moisture the way the spread equations require, for every model that reads it | CI, `invariants.yml` | +| `test_threading.py` | That eight concurrent simulations each reproduce the result they give alone | By hand only — it is the failing reproduction for [#175](https://github.com/forefireAPI/forefire/issues/175), and needs a free-threaded CPython | +| `test_wheel.py` | The cibuildwheel smoke test: the module imports, its vendored NetCDF resolves, the models registered, a trivial simulation advances | cibuildwheel, against an *installed* wheel | -* **Goal** – reproduce a real fire scenario with nested Meso-NH domains. -* **Features** +`TESTING.md` gives the command line for each. - * two domains (real forcing + high-resolution nest); - * performance and stability check. - * high frequency outputs and http web interface (requires FOREFIREHOME environment variable set) - +### `runANN` -### 4.3 `python` +Loads `Rothermel.ffann` — a small network fitted to the Rothermel propagation +model — evaluates it over the fuel, slope and wind combinations in +`modelrun.csv`, and checks the result. -| Script | Description | Expected artefacts | -| ------------------- | -------------------------------------------------------------------- | --------------------------- | -| `idealized_wind.py` | Wind rotating from 0° to 360°; circular fire front (regression test) | `360wind.png`, NetCDF files | -| `percolation.py` | Three fires propagating through randomly heterogeneous fuels | `percolation.nc` | +It needs no machine-learning framework. `ANN_test` is a ForeFire tool built +from `tools/runANN/ANNTest.cpp`, and the `.ffann` format is read by ForeFire's +own evaluator. -### 4.4 `runANN` +The check is the root mean squared error between what the network predicts and +what the propagation model produced, against a tolerance of 0.05. That +tolerance sits between the trained network (about 0.024) and a predictor that +ignores its inputs and returns the mean (0.097), so a network that stopped +working would fail. It needs no reference file — the earlier version diffed +against a `result.txt.ref` that was never committed, and so failed on its +second line every time it ran. -* **Goal** – verify *ForeFire*’s internal feed-forward routine on an ANN graph trained to approximate the Rothermel formula. -* **Steps** +### `runff` - 1. Load `rothermel.fann`. - 2. Activate on various *fuel / slope / wind* combinations. - 3. Compare with reference outputs. +Exercises the command-line interpreter. There are two entry points, and they do +different things: -### 4.5 `runff` -* **Goal** – verify *ForeFire*’s command line intrerpreter. +* **`run.bash`** — what `tests/run.bash` calls. Three successive scenarios: -Three successive scenarios: + 1. `real_case.ff` — run a real case, write NetCDF output and a `to_reload.ff` + state file; + 2. `reload_case.ff` — reload that state and export KML; + 3. `rungeojson.ff` — load, simulate, export GeoJSON, clear memory, reload the + GeoJSON to verify it. -1. **`realcase.ff`** – start a real case, save the state in FF format. -2. **`reload_case.ff`** Reload the state and export KML. -3. **`rungeojson.ff`** Single-run variant – load ► simulate ► export GeoJSON, clear memory, reload GeoJSON for verification. + It then checks that the expected artefacts exist and are not implausibly + small. ---- +* **`ff-run.bash`** — what CI calls, from `main.yml`, `macos.yml` and + `docker.yml`. It runs the first two scenarios and then compares the KML and + NetCDF against `real_case.kml.ref` and `ForeFire.0.nc.ref` with a numerical + tolerance, using `compare_kml.py` and `compare_nc.py`. This is the one that + can detect physics drift. diff --git a/tests/python/README.md b/tests/python/README.md index 49d433d..9b67b41 100644 --- a/tests/python/README.md +++ b/tests/python/README.md @@ -1,7 +1,83 @@ -# *Python-basesd* Test Suite +# Python examples and tests -## 1. farsite_flat.py -It runs a simulation based in Farsite software for a north-wind of 3mph. +Everything here needs the `pyforefire` module: either a build configured with +`-DFOREFIRE_BUILD_PYTHON=ON`, or `pip install forefire`. -Required the input .lcp file can be downloaded from: -https://github.com/mbedward/farsite/raw/refs/heads/master/examples/flatland/Inputs/a_lcpFiles/flatland.lcp +`../run.bash` runs only the first two, through `$PYTHONEXE`. + +## `percolation.py` + +Four fires side by side, each in an 80×60 band of fuel filled at random to a +different density (`k_coeffs = [.10, .3, .4, .5]`), so the bands sit either +side of the percolation threshold. Writes `percolation.nc`. A demonstration of +building a heterogeneous fuel map in numpy and handing it to ForeFire with +`addIndexLayer`. + +## `idealizedwind.py` + +One fire under a wind rotating from 0° to 360°, which should trace a circular +front. Writes `360wind.png` through ForeFire's own `plot[]` command — the one +artefact `../run.bash` checks for. + +## `farsite_flat.py` + +Reproduces a FARSITE benchmark case: flat terrain, a 3 mph north wind, seven +hours of spread, compared against the FARSITE result. + +**It cannot run as checked out.** It reads `flatland.lcp`, a landscape file +that is not in this repository. Download it first: + +```bash +curl -LO https://github.com/mbedward/farsite/raw/refs/heads/master/examples/flatland/Inputs/a_lcpFiles/flatland.lcp +``` + +The weather it uses, `flatland_3mph0deg7hr.raws`, *is* in this directory. + +## The test scripts + +These three are tests rather than examples, and `../run.bash` runs none of +them. They are plain scripts, not pytest modules. `TESTING.md` covers each in +detail, including how to build the module they import. + +### `test_moisture_invariants.py` + +Asserts, for every propagation model that consumes dead fuel moisture, that +rate of spread stays finite, falls as moisture rises, reaches zero at the +moisture of extinction, and responds to a dynamic moisture layer. It holds no +reference data — every assertion follows from the published spread equations — +so unlike `runff` it can tell a physics fix from a physics regression. + +Run in CI by `invariants.yml`. Manually: + +```bash +python tests/python/test_moisture_invariants.py # -v for every probe +``` + +`--model NAME` and `--test NAME`, both repeatable, narrow it down. + +### `test_threading.py` + +Runs eight simulations in threads and requires each to reproduce the node +count it produces alone. + +**It does not pass, by design**, and is not run in CI. It is the failing +reproduction for the shared-state problem in +[#175](https://github.com/forefireAPI/forefire/issues/175). It needs a +free-threaded CPython and `PYTHON_GIL=0`; on an ordinary interpreter the GIL +serialises every call and the test skips, reporting a green result that proves +nothing. + +```bash +PYTHON_GIL=0 python3.14t tests/python/test_threading.py +``` + +### `test_wheel.py` + +The smoke test `cibuildwheel` runs against a built wheel: the module imports, +its vendored NetCDF resolves, the propagation models registered, and a trivial +simulation advances. Run it against an *installed* `forefire`, never from the +source tree: + +```bash +python tests/python/test_wheel.py +```