Skip to content

PedestrianDynamics/pyFDS-Evac

Repository files navigation

code quality tests

pyFDS-Evac

Fire Dynamics Simulator (FDS) coupled evacuation modeling with smoke-speed reduction, toxic gas dose (FED), and dynamic route rerouting.

The project includes:

  • Smoke-speed model (visibility/extinction-based speed reduction)
  • Full ISO 13571 FED model (toxic gas dose accumulation)
  • Dynamic smoke-based route rerouting
  • JuPedSim scenario loading and simulation

Installation

This project uses uv for dependency management.

uv sync

Development

Activate the virtual environment:

uv shell

Run a JSON-first scenario with the CLI runner:

uv run run.py --scenario assets/ISO-table21 --cleanup

Smoke-speed model

See docs/smoke-speed-model.md for the full model description, configuration, and API reference.

The smoke-speed model uses extinction coefficient K [1/m] as the primary input. For real FDS output, fdsreader provides the local extinction field via SliceFieldSampler. For verification cases such as ISO 20414 Table 21, the runner can also apply a constant extinction coefficient directly.

FDS data access

All FDS slice data is read through a single library:

  • fdsreader — reads raw FDS slice quantities with nearest-neighbor spatial and temporal lookup via SliceFieldSampler (pyfds_evac/core/fds_sampling.py)
  • Used by both the smoke-speed model (extinction K [1/m]) and the FED model (CO, CO2, O2, and optional irritant gases)
  • When a scenario needs both extinction and FED fields from the same FDS case, pass a shared fdsreader.Simulation instance to avoid parsing the directory twice (see FDS sampling API)

Run the ISO Table 21 corridor with a constant extinction coefficient:

uv run run.py \
  --scenario assets/ISO-table21 \
  --constant-extinction 1.0 \
  --smoke-update-interval 0.1 \
  --output-smoke-history /tmp/iso-table21-smoke-history.csv \
  --cleanup

Run the smoke-speed model against FDS results read through fdsreader:

uv run run.py \
  --scenario assets/ISO-table21 \
  --fds-dir fds_data \
  --smoke-update-interval 0.1 \
  --output-smoke-history /tmp/iso-table21-fds-smoke-history.csv \
  --cleanup

Inspect the FDS quantities available through fdsreader:

uv run run.py --inspect-fds --fds-dir fds_data --scenario assets/ISO-table21

Plot smoke-speed history for a single agent:

uv run python scripts/plot_smoke_history.py \
  --input /tmp/iso-table21-smoke-history.csv \
  --output /tmp/iso-table21-smoke-history.png \
  --agent-id 1

Plot aggregate smoke-speed history:

uv run python scripts/plot_smoke_history.py \
  --input /tmp/iso-table21-smoke-history.csv \
  --output /tmp/iso-table21-smoke-history-aggregate.png

Generate a stable ISO Table 21 sweep artifact under artifacts/:

uv run python scripts/generate_iso_table21_sweep.py

Figure: ISO Table 21 sweep

Generate the FDS+Evac smoke-density vs speed verification plot:

uv run python scripts/generate_smoke_density_speed_plot.py

Figure: soot_density vs speed

FED Model (Fractional Effective Dose)

The FED model implements the full ISO 13571 / Purser formulation as described in Section 3.4 of the FDS+Evac Technical Reference and User's Guide (Korhonen, 2021).

Implemented equation (guide Eq. 12)

$$ \mathrm{FED}_{\mathrm{tot}} = \bigl(\mathrm{FED}_{\mathrm{CO}} + \mathrm{FED}_{\mathrm{CN}} + \mathrm{FED}_{\mathrm{NO_x}} + \mathrm{FLD}_{\mathrm{irr}}\bigr) \times \mathrm{HV}_{\mathrm{CO_2}} + \mathrm{FED}_{\mathrm{O_2}} $$

Term Guide Eq. Formula Input
FED_CO (13) $\int 2.764 \times 10^{-5}, C_{\mathrm{CO}}^{1.036}, dt$ CO (ppm)
FED_CN (14-15) $\int \bigl(\exp(C_{\mathrm{CN}}/43)/220 - 0.0045\bigr), dt$, where $C_{\mathrm{CN}} = C_{\mathrm{HCN}} - C_{\mathrm{NO_2}}$ HCN, NO2 (ppm)
FED_NOx (16) $\int C_{\mathrm{NO_x}}/1500, dt$, where $C_{\mathrm{NO_x}} = C_{\mathrm{NO}} + C_{\mathrm{NO_2}}$ NO, NO2 (ppm)
FLD_irr (17) $\int \sum_i C_i / F_{\mathrm{FLD},i}, dt$ HCl, HBr, HF, SO2, NO2, acrolein, formaldehyde (ppm)
HV_CO2 (19) $\exp(0.1903, C_{\mathrm{CO_2}} + 2.0004)/7.1$ CO2 (vol %)
FED_O2 (18) $\int 1/\bigl(60, \exp(8.13 - 0.54,(20.9 - C_{\mathrm{O_2}}))\bigr), dt$ O2 (vol %)

Irritant Ct values (ppm·min) from guide Table 2:

Species HCl HBr HF SO2 NO2 acrolein formaldehyde
F_FLD 114000 114000 87000 12000 1900 4500 22500

Gas species are read from FDS slice outputs via fdsreader. Required species: CO, CO2, O2. Optional species (HCN, NO, NO2, HCl, HBr, HF, SO2, acrolein, formaldehyde) are loaded when available; missing species default to 0 and contribute nothing to the FED sum. With only the three required species, the model reduces to the original FDS+Evac default pathway: FED_CO * HV_CO2 + FED_O2.

Recent additions

The FED model was extended in March 2026 to include all ISO 13571 terms:

  • HCN (hydrogen cyanide) and NO2 (nitrogen dioxide): CN-term for narcosis, where NO2 has a protective effect (C_CN = C_HCN - C_NO2)
  • NO (nitric oxide): Added to NOx-term alongside NO2
  • Multiple irritant gases: HCl, HBr, HF, SO2, NO2, acrolein, formaldehyde with species-specific Ct thresholds from guide Table 2

All new terms are fully tested with constant-exposure unit tests in tests/test_fed.py.

Verification

  • Equation-level constant-exposure checks for all ISO 13571 terms are covered in tests/test_fed.py
  • An ISO Table 22 style stationary benchmark is covered with assets/ISO-table22, comparing the runtime FED=1 crossing time against the analytical reference

Generate the ISO Table 22 stationary FED verification figure:

uv run python scripts/generate_iso_table22_stationary_plot.py

Figure: ISO Table 22 stationary FED verification

What is not implemented yet

  • Incapacitation effects on agent motion (FED >= 1 → speed = 0)
  • Thermal FED terms (radiant heat, convective heat)

Usage

Inspect which local FDS cases support FED:

uv run python - <<'PY'
from pyfds_evac.core import inspect_fds_quantities, list_simulations
for path in list_simulations("fds_data"):
    inv = inspect_fds_quantities(path)
    print(path, inv.canonical_slice_names(), inv.supports_default_fed())
PY

Run a scenario with FED accumulation from FDS data:

uv run run.py \
  --scenario assets/ISO-table21 \
  --fds-dir fds_data/haspel \
  --smoke-slice-height 2.1 \
  --smoke-update-interval 1.0 \
  --output-fed-history /tmp/iso-fed-history.csv \
  --cleanup

Note: if a point lies outside the FDS domain, the implementation falls back to ambient conditions.

Dynamic route rerouting

See docs/routing.md for the full routing model, cost formulas, and API reference.

The routing system implements smoke-aware path planning with dynamic rerouting:

  • StageGraph: Dijkstra-based shortest-path routing on a graph of stages (distributions, exits)
  • Route cost evaluation: Samples extinction (K) and FED quantities along candidate paths to compute smoke exposure
  • Dynamic rerouting: Agents recompute routes at configurable intervals, selecting lower-exposure paths when available
  • Throughput throttling: Optional exit flux limiting via enable_throughput_throttling and max_throughput in scenario config

Usage

Run the haspel scenario with smoke-aware rerouting:

uv run run.py \
  --scenario assets/haspel \
  --fds-dir fds_data/haspel \
  --smoke-update-interval 1.0 \
  --reroute-interval 30.0 \
  --output-fed-history /tmp/haspel-fed-history.csv \
  --cleanup

References

Reference materials are stored in materials/:

Assets

Scenario definitions are stored in assets/:

  • ISO-table21: ISO 20414 corridor verification case (single exit)
  • ISO-table22: ISO 20414 stationary benchmark (single agent, analytical FED=1 time)
  • haspel: Multi-exit scenario with three zones and dynamic rerouting
  • basic: Minimal scenarios for smoke-speed verification
  • HC: Hazard composition cases
  • social_force: Social force model test cases

Dependencies

  • jupedsim
  • pedpy
  • fdsreader
  • plotly
  • nbformat

About

Python implementation of human behavior in fire

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors