CO dose in a uniform room
| Component | Gas FED and incapacitation (Models › FED) |
| Level | FDS case: a full run on FDS output |
| Asset | assets/fed_incap_co_2000ppm |
| Expected value from | hand calculation, and FDS’s own FED device |
| Status | passes: criteria 1 and 2, and criterion 3 pooled over 10 seeds (D = 0.023 against 0.043) |

What is tested
Whether the dose an agent accumulates in pyFDS-Evac is the dose the equations give, and whether agents stop when it says they should. The room holds the same gas everywhere and at all times, so every agent, wherever it walks, must follow one curve, FED(t), that can be computed by hand. Any difference comes from the code: reading the FDS slice, sampling it at the agent, summing the dose, or applying the threshold.
Equation
The gas FED, as in FDS’s FED function
(Models › FED; the published law is on
Fundamentals › Asphyxiant FED), with C in ppm for CO and in
volume percent for CO₂ and O₂, and t in minutes:
FED_CO = 2.764e-5 x (C_CO)^1.036 x t
HV_CO2 = exp(0.1903 x C_CO2 + 2.0004) / 7.1 if C_CO2 > 0
HV_CO2 = 1 if C_CO2 <= 0 (or not finite)
FED_O2 = t / exp[8.13 - 0.54 x (20.9 - C_O2)] only below 20 % O2
FED = FED_CO x HV_CO2 + FED_O2With a constant mixture the dose grows linearly, and FED = 1 is reached at
$$ t^{*} = \frac{60}{r_{\mathrm{CO}}\,\mathrm{HV}_{\mathrm{CO_2}}}\ \text{s}, \qquad r_{\mathrm{CO}} = 2.764\times10^{-5}\,C_{\mathrm{CO}}^{1.036}\ \text{min}^{-1}. $$In probabilistic mode (opt-in) agent i stops at its
own threshold \(D_i = \exp(\sigma Z_i)\), \(Z_i \sim N(0,1)\), σ = 0.94. Because
FED grows linearly, agent i stops at \(D_i\,t^{*}\), and the fraction of
agents stopped by time t is
Setup

- FDS: a sealed 30 × 30 × 3 m room, no fire, no vents, four meshes. At t = 0 it is filled with 2000 ppm CO, 500 ppm CO₂, 20.9 % O₂ and soot for K ≈ 1 /m. Slices at 1.6 m, which FDS places at 1.5 m on this 0.5 m grid.
- Agents: 100 agents walk a loop between four corner checkpoints, so they stay in the room and keep moving; the field is sampled at each agent’s position every second.
- Runs: once with
--incapacitation-mode deterministic(the default, given explicitly), and with--incapacitation-mode probabilisticonce for each of the seeds 42 to 51.
Expected
The concentrations are read from the FDS slice itself, independently of pyFDS-Evac: 1999.86 ppm CO, 0.049992 % CO₂ and 20.8985 % O₂, the same in every cell and at all 1001 slice times. O₂ is above 20 %, so its term is zero. Then
| Quantity | Value |
|---|---|
| \(r_{\mathrm{CO}}\) | 0.072673 /min |
| \(\mathrm{HV}_{\mathrm{CO_2}}\) | 1.05108 |
| \(t^{*}\), hand calculation | 785.49 s |
\(t^{*}\), FDS’s own FED device in the same run | 785.44 s |
| agents stopped by 999 s, probabilistic, \(F(999\ \mathrm{s})\) | 60.1 % |
Earlier versions of this page quoted 782.4 s. That value added an O₂ term at 20.9 % O₂, which FDS and pyFDS-Evac apply only below 20 %.
Result

All 100 agents follow the hand calculation to within 1.4 × 10⁻¹⁴, round-off in the last digit. FDS’s own device lies up to 7.7 × 10⁻⁵ above it at 1000 s, a relative difference of 6 × 10⁻⁵.

| Check | Expected | Simulated |
|---|---|---|
| FED of every agent against the hand calculation | equal | max difference 1.4 × 10⁻¹⁴ |
| deterministic: incapacitation time | 785.49 s | all 100 agents at 786.0 s (first FED update after t*) |
| probabilistic, 10 seeds pooled: agents stopped by 999 s | 60.1 % | 599 of 1000 |
| probabilistic, 10 seeds pooled: largest gap between the curves | ≤ 0.043 | 0.023 (p = 0.68) |
Pass criteria
- Dose. Every agent’s FED equals the hand calculation to round-off, |FED − FED_hand| ≤ 10⁻⁹.
- Deterministic stop. Every agent stops at the first FED update at or
after t*: \(t^{*} \le t_i < t^{*} + \Delta t\), with the update interval
Δt = 1 s (
--smoke-update-interval). - Probabilistic stop. The empirical fraction of stopped agents, pooled over the probabilistic runs of seeds 42 to 51, stays within the 95 % Kolmogorov–Smirnov band of F(t), \(1.36/\sqrt{n} = 0.043\) for the pooled n = 1000 agents. Agents whose threshold lies beyond the end of the run are counted as not yet stopped. Pooled, D = 0.023 (p = 0.68), so it passes. Each seed alone, against its own band of 0.136 for n = 100, gives D = 0.038 to 0.141 (median 0.067); one of the ten, seed 42 with D = 0.141, leaves its band, as one seed in twenty is expected to at the 5 % level.
Run it yourself
The FDS output is not in the repository (254 MB). Either get it from the
project’s data folder (fds-evac-data/fed_incap_co_2000ppm/fds/), or rerun
FDS, about one minute on four cores:
cd assets/fed_incap_co_2000ppm
mpiexec -n 4 fds fed_incap_co_2000ppm.fdsThen run pyFDS-Evac in both modes, the probabilistic one for each seed,
and draw the figures. <data> holds fds/ (the FDS output) and evac/:
uv run python run.py --scenario assets/fed_incap_co_2000ppm \
--fds-dir <data>/fds --incapacitation-mode deterministic \
--output-sqlite <data>/evac/deterministic/run.sqlite \
--output-fed-history <data>/evac/deterministic/fed_history.csv
for seed in $(seq 42 51); do
uv run python run.py --scenario assets/fed_incap_co_2000ppm \
--fds-dir <data>/fds --incapacitation-mode probabilistic --seed $seed \
--output-sqlite <data>/evac/probabilistic_seeds/$seed/run.sqlite \
--output-fed-history <data>/evac/probabilistic_seeds/$seed/fed_history.csv
done
uv run python scripts/verification/co_room_figures.py --data <data>The animation shows the run of the lowest seed.
The evacuation itself takes about three minutes. Before it starts, the first
run builds the sign-visibility cache for the 25 checkpoints at every FDS time,
which takes much longer
(#236); add
--vis-cache <file> to every run so the later ones reuse it.
The numbers and figures on this page come from these commands, stored in
fds-evac-data/fed_incap_co_2000ppm/evac_353/: the deterministic run at
fbe8878, the ten probabilistic runs at d22b6dc.
Limits
- One concentration. The 4000 and 8000 ppm decks
(
assets/fed_incap_co_4000ppm,assets/fed_incap_co_8000ppm) have CO₂ and O₂ slices at 2.0 m only (CO also at 0.5–2.5 m) and no 1.6 m slices, and have not been rerun. - CO only, with CO₂ at an ambient level and O₂ above 20 %, so the O₂ term and
the optional gases (HCN, NOx, irritants) are not exercised here. The FDS
FED_FICcase covers those as an equation-level check. - A uniform field cannot show whether the field is sampled at the agent’s current position; that needs a gradient (#24).
- A uniform field also hides which slice height the dose is read from. That is checked separately in a room with CO in layers (test_fed_slice_height.py); until #238 was fixed, the FED read the first slice of each species in the deck.
- This page is not yet an automated test: the check runs from the figure script on the stored output.