Evacuation with and without the fire
Egress tools such as Pathfinder are commonly run without the fire. The required safe escape time (RSET) is taken from that run and compared with the available safe escape time (ASET) from FDS. Engineers we consulted regard this uncoupled comparison as conservative. But Purser (2003, p. 92) noted that most travel-time calculations assumed no interaction between occupants and the fire effluent. This page shows how to make the same comparison with pyFDS-Evac, what a run coupled to the fire adds, and whether the uncoupled run is conservative for one fire: the 2 MW PVC fire of A crowd in a fire.
You need the fire_2MW_PVC FDS output of that page (step 3 there says
where to get it) in $FDS. Each run below takes 4 to 6 s.
The answer in short
For this fire and this T-junction, with 100 people placed at t = 0:
- The workflow carries over exactly. A smoke-blind run with the fire (arm U) gives the same trajectories as a run without it (arm C), and it records the dose along those fire-free paths.
- RSET. The uncoupled RSET is shorter than the speed-only coupled RSET in every seed. It is shorter than the fully coupled RSET in every seed when people wait 30 or 60 s before moving. With no wait the two are within the seed spread: the coupled RSET is later in 13 of 20 seeds, by a median of 0.7 s (sign test p = 0.26), because nearly everyone in the coupled run takes the far exit and avoids the smoke.
- Dose. Whether the uncoupled run over- or under-states the dose depends on the metric (see the dose table).
- Exit usage differs: without the fire everyone takes the near exit B. With smoke-aware routing (arm R), 100 % take the far exit A when people move at once; with 30 s of pre-movement 51 % do, and with 60 s 3 %: by then every route is refused, and the router takes the one with the lowest optical depth, the shorter route to exit B, although it passes the densest smoke by the burner.
- The pass/fail verdict at a fixed point is the same in every arm for visibility (fails), and in U, S, R and R-na for FED 0.3 (passes). For HCl with no wait, at ISO FEC 1 (an incapacitation-level value, above the design value 0.3), U and R pass at the junction and the branch mouth in every seed and S at the branch mouth in 3 of 20. Summed over seeds and points, U passes 40 times, R 40, R-na 39 and S 3.
This fire has no margin to lose: at every point the visibility limit is reached before the last person gets out, in every arm and seed. So the study shows what the uncoupled run misses once the margin is gone. It cannot tell whether a design that passes without the fire also passes with it. It is one fire in one geometry, run with research software, and it gives no design verdict.
1. The classic comparison: run without the fire
The scenario is assets/t_junction/config_initial_pre0.json: 100 agents
spread over the dead-end branch of the T, all familiar with both exits, no
pre-movement, and a time limit of 270 s. The two variants
config_initial_pre30.json and config_initial_pre60.json differ only in a
constant pre-movement of 30 or 60 s.
Arm C, no fire. Leave out --fds-dir:
mkdir -p ww
SC=assets/t_junction/config_initial_pre0.json
uv run python run.py --scenario $SC --seed 4 \
--output-sqlite ww/c.sqlite --output-exit-history ww/c_exits.csvThe output ends with:
Simulation finished in 50.48 s (100/100 evacuated).This is the run an egress-only tool gives you. Take RSET from it and compare it with the location ASET from FDS, as in A crowd in a fire › At fixed points.
Arm U, smoke-blind. The same run, with the fire sampled but not acting.
--smoke-blind keeps every agent at its free speed and on its fire-free
route, and still writes what each agent breathed and saw
(#341):
uv run python run.py --scenario $SC --seed 4 --fds-dir "$FDS" --smoke-blind \
--output-sqlite ww/u.sqlite --output-exit-history ww/u_exits.csv \
--output-fed-history ww/u_fed.csv --output-smoke-history ww/u_smoke.csvSmoke-blind: rerouting is off.
Smoke-blind: FED is recorded, incapacitation and FIC slowdown off.
…
Simulation finished in 50.48 s (100/100 evacuated).fdsreader also logs Module vents: could not convert string to float for
this deck. It does not affect the result.
A scenario with an exit that closes (closed_after_s, for example a front
door that jams) also runs smoke-blind
(#395). An
agent whose exit closes chooses again, once for each closure, in clear air, as it chose its
first exit; nobody else re-decides. Arm C re-decides through the reroute
pass, so with a closure U walks exactly as the no-fire run with
--no-enable-rerouting, not necessarily as C.
Read RSET from the trajectory, not from the Simulation finished line.
The ensemble how-to explains why and how. For one
seed:
import sqlite3
import numpy as np
import pandas as pd
for run in ("c", "u"):
with sqlite3.connect(f"ww/{run}.sqlite") as con:
fps = float(con.execute("SELECT value FROM metadata WHERE key='fps'").fetchone()[0])
last = pd.read_sql("SELECT MAX(frame) AS f FROM trajectory_data GROUP BY id", con).f / fps
exits = pd.read_csv(f"ww/{run}_exits.csv").exit_id.value_counts().to_dict()
print(f"{run.upper()}: RSET_last {last.max():.1f} s, "
f"p95 {np.sort(last)[94]:.1f} s, exits {exits}")C: RSET_last 50.4 s, p95 48.0 s, exits {'exit_B_right': 100}
U: RSET_last 50.4 s, p95 48.0 s, exits {'exit_B_right': 100}RSET_last is the last agent out; p95 the 95th of 100. The clock starts
at ignition. The run models no detection and no alarm, so RSET here is
pre-movement plus travel: with no pre-movement, the agents move at ignition,
which no standard timeline assumes. Add tdet and
twarn yourself (ISO/TR 16738:2009, Eq. 2, as on
ASET and RSET).
If the run stopped at the time limit with people still inside, RSET_last
prints the 270 s cap and exits counts the exit assigned to each agent,
out or not. That is not an RSET; see Sensitivity arms.
Read the dose along the fire-free paths from U’s histories:
import pandas as pd
fed = pd.read_csv("ww/u_fed.csv")
smoke = pd.read_csv("ww/u_smoke.csv")
hist = fed.merge(smoke[["time_s", "agent_id", "extinction_per_m"]],
on=["time_s", "agent_id"])
hist["hcl_ppm"] = 900 * hist.fic # HCl is the only irritant in this deck
per_agent = hist.groupby("agent_id").agg(
max_fed=("fed_cumulative", "max"),
max_fic=("fic", "max"),
s_hcl300=("hcl_ppm", lambda v: int((v >= 300).sum())),
s_k03=("extinction_per_m", lambda v: int((v >= 0.3).sum())),
)
print(per_agent.describe().loc[["50%", "max"]].round(2).to_string())
print(f"agents at HCl >= 300 ppm for at least 1 s: {(per_agent.s_hcl300 > 0).sum()}") max_fed max_fic s_hcl300 s_k03
50% 0.00 1.24 4.0 9.0
max 0.01 11.84 9.0 19.0
agents at HCl >= 300 ppm for at least 1 s: 82The histories are written once per second, so s_hcl300 and s_k03 are
seconds at HCl ≥ 300 ppm and at K ≥ 0.3 1/m. HCl = 900 × fic holds
only because HCl is the only irritant of this deck
(A crowd in a fire › How the numbers are computed).
The classic comparison uses no such dose: it compares location ASET with
RSET only. The per-agent dose is extra post-processing, as on
A crowd in a fire › Each agent against its own limits.
The location ASET comes from the FDS output alone, so it is the same for every arm. The study uses four points of the location ASET table, times from ignition:
| Point | K ≥ 0.3 1/m | HCl ≥ 300 ppm | HCl ≥ 1000 ppm | FED ≥ 0.3 |
|---|---|---|---|---|
| Exit B | 18 s | 28 s | 40 s | not by 300 s |
| Junction | 24 s | 45 s | 59 s | 274 s |
| Branch mouth | 46 s | 51 s | 68 s | 275 s |
| Exit A | 45 s | 47 s | 50 s | not by 300 s |
The K limit is the Engineers Australia visibility of 10 m with C = 3 (EA 2014, Fig. 8, Tenability Criteria – Short Exposure, p. 15). The HCl limits are ISO FEC 0.3 and 1. Every agent starts in the branch, so every agent passes the branch mouth and the junction.
2. What coupling adds
Two further arms let the fire act, one effect at a time.
Arm S, speed only. Smoke slows the agents, but each keeps the exit it
took in U. --replay-exits reads U’s exit history and pairs agents by
origin and spawn order (#352):
uv run python run.py --scenario $SC --seed 4 --fds-dir "$FDS" \
--no-enable-rerouting --disable-tenability --replay-exits ww/u_exits.csv \
--output-sqlite ww/s.sqlite --output-exit-history ww/s_exits.csvReplaying the exits of 100 agents from ww/u_exits.csv.
…
Simulation finished in 68.17 s (100/100 evacuated).Arm R, speed and routing. The default coupled run. Smoke slows the agents and enters their route cost:
uv run python run.py --scenario $SC --seed 4 --fds-dir "$FDS" \
--disable-tenability \
--output-sqlite ww/r.sqlite --output-exit-history ww/r_exits.csvSimulation finished in 54.80 s (100/100 evacuated).With --debug, C and R also print Reroute debug lines; they do not affect
the result.
--disable-tenability in S and R records FED and FIC but lets nobody be
incapacitated, so that everyone leaves and RSET stays defined. It also
turns off --enable-fic-speed (Usage).
The study adds incapacitation and the HCl slowdown as separate arms
(below).
Run the RSET snippet above with ("c", "u", "s", "r"):
C: RSET_last 50.4 s, p95 48.0 s, exits {'exit_B_right': 100}
U: RSET_last 50.4 s, p95 48.0 s, exits {'exit_B_right': 100}
S: RSET_last 68.1 s, p95 63.2 s, exits {'exit_B_right': 100}
R: RSET_last 54.7 s, p95 51.0 s, exits {'exit_A_left': 98, 'exit_B_right': 2}In this seed smoke slows S by 17.7 s (68.1 against 50.4 s). R is 4.3 s slower than U; across seeds R is later than U in 13 of 20 (below). Against S, which keeps exit B, R is faster because 98 of its 100 agents went to the far exit A and avoided B’s smoke. One seed says little: the study below runs 20 seeds with no pre-movement and 10 each with 30 and 60 s.
Reading the arms. U → S isolates the effect of smoke on speed. S → R isolates routing. R against R-na, the same run with the route cost’s look ahead in time off, isolates that look ahead.
Evacuation over time

Agents out (of 100) against time since ignition [s], median over seeds
(line) and min–max (band); n = 20 seeds with no pre-movement, 10 at 30 and
60 s. Dotted: location ASET for K ≥ 0.3 1/m at exit B, the junction and
exit A. Exit times from the trajectory at 0.1 s. Regenerated by
scripts/docs/fire_blind_vs_coupled.py.
The last agent out, median [min, max] over seeds, seconds from ignition:
| Arm | pre-movement 0 s | 30 s | 60 s |
|---|---|---|---|
| U (= C) | 53.3 [50.1, 56.9] | 83.3 [80.4, 85.8] | 113.3 [110.4, 115.8] |
| S | 70.0 [65.9, 80.9] | 124.2 [113.3, 128.7] | 162.4 [160.8, 183.8] |
| R | 54.5 [50.5, 57.1] | 95.5 [92.8, 97.5] | 162.4 [156.9, 171.7] |
| R-na | 56.6 [54.4, 60.6] | 94.6 [90.3, 101.6] | 164.6 [160.8, 189.1] |
Exit usage

Agents leaving by exit A [%], bar: median over seeds, dots: one seed each.
Exit A is 20 m from the junction, exit B 10 m. Regenerated by
scripts/docs/fire_blind_vs_coupled.py.
C, U and S send everyone to exit B, the nearer one. R sends 100 % [94, 100]
to exit A with no pre-movement, 51 % [49, 55] at 30 s and 3 % [0, 6] at
60 s. The initial exit is chosen in _assign_initial_exit (scenario.py),
and R’s route cost prices the smoke each route meets on the way. With
foresight on (the default "anticipate": true of RouteCostConfig,
route_graph.py), the cost reads the smoke at the time the agent would
arrive at each point (_arrival_time), from the whole FDS record
(#125). No
occupant could perceive that. With the look ahead off (R-na), the cost
still reads the current smoke along the whole route, including parts no
occupant could see: 97 % [95, 98] go to exit A with no pre-movement,
95 % [91, 99] at 30 s and 6 % [4, 7] at 60 s. At 30 s R takes B far more
often than R-na because R also sees the smoke that will reach the long
walk to A.
When every route is refused before anyone moves, the router takes the route with the lowest optical depth τ. With 60 s of pre-movement, both routes are refused before anyone walks (seed 7: the route to B from 9 s, the route to A from 25 s; at 59–60 s τ is 55–109 on the route to B and 85–144 on the route to A). Between two refused routes the lower τ decides (#458). Here that is the shorter route to exit B (17.7 m against 24.7 m), although it passes the densest smoke near the burner (kmax 7.2–27.3 1/m on B against 6.0–8.4 on A). It is not the safer route: R’s largest max FED at 60 s is 0.19, against 0.06 in U. The effect is strongest at 60 s, where 97 % of R’s agents go past the burner to B; at 0 and 30 s R sends 100 % and 51 % to exit A.
A code-level alternative, fallback_rule="hold", keeps the exit an agent
has once every route is refused, whatever the smoke on it, until the
current route crosses the FED limit and a rival does not
(Route rejection;
#750). On the seeds of this
page, R under "hold" sends 100 % [100, 100] to exit A at every
pre-movement, against 100 % [94, 100], 51 % [49, 55] and 3 % [0, 6] under
the default "tau". The median largest max FED is 0.006, 0.024 and 0.100
under "hold", against 0.006, 0.070 and 0.178; the median number of
fallback exit changes per run is 0 under "hold", against 0, 195 and 267
(at most 6, 201 and 278). "hold" is experimental: no scenario key or
flag sets it, and it is neither recommended nor validated. Whether the
rule should send everyone to the exit by the fire is open (#696).
U and R, animated

Arms U (top) and R (bottom), pre-movement 30 s, seed 4 (R’s last agent
leaves at 95.3 s, the seed closest to the median of 95.5 s), 8 times real
time. Background: K from the FDS output, log scale. Dots are coloured and
sized by their speed factor, as in
The run, animated. Regenerated by
scripts/docs/study_animations.py.
Both crowds wait 30 s while the smoke spreads from the burner near exit B. U then walks at free speed through that smoke to exit B, all 100 agents, and the last is out at 80.4 s. In R, 54 agents turn to the far exit A and 46 go to B; the smoke slows them, and the last is out at 95.3 s. The animation shows 30 s of pre-movement because it shows both effects, the exit and the speed. With no wait, the two arms differ mainly in the exit.
Pre-movement does not simply add

RSET_last [s from ignition] against constant pre-movement [s], seeds
shared by all three pre-movements (n = 10). Thick: median; faint: one seed.
Dashed: slope 1 through U at 0 s. S and R are drawn slightly left and right
of the tick to keep the markers apart. Regenerated by
scripts/docs/fire_blind_vs_coupled.py.
For the same seed, RSET(pre) − pre − RSET(0) is exactly 0.00 s for U and C in every seed. That follows from how the run is built: the pre-movement is constant, everyone starts together, and nothing reacts to the fire. S adds 19.8 s more than the pre-movement at 30 s and 32.0 s more at 60 s; R adds 10.1 s and 47.8 s (medians over seeds). Every seed adds at least 13.3 s in S and 7.6 s in R at 30 s. The fire grows while people wait, so a later start meets thicker smoke. An uncoupled run cannot show that: waiting longer only shifts its curve.
3. Is the uncoupled run conservative here?
What “conservative” means. The decision rests on whether ASET exceeds RSET by an adequate margin (ISO/TR 16738:2009, Eq. 1 and §5.6, as quoted on ASET and RSET). U is conservative relative to another arm X if its error makes the margin look smaller than X does. The reference is a model, R or S, not reality. R’s route cost looks ahead in time, so R-na, without that look ahead, is a second model reference. It is not a perceptual one: it still reads the smoke along the whole route.
| Metric | U is conservative relative to X if |
|---|---|
| RSET (last, p95) | RSETU ≥ RSETX, tested against S and R separately |
| Location margin, same point and criterion | ASET − RSETU ≤ ASET − RSETX; this follows the RSET row, since the location ASET is shared |
| Per-agent margin (first crossing − own exit) | smaller for U than for X, on the same agent |
| People inside at the location ASET | at least as many in U as in X |
| Dose (peak FIC, time above an HCl level, max FED) | at least as large in U as in X |
| Exit usage | neither: it is a scenario assumption, so the page reports how it differs |
Counts below are seeds (or agents) in which U is above, equal to, or below X. With 10 seeds, 10 of one sign gives a two-sided sign-test p of 0.002; with 20, 20 of one sign gives 2 × 10⁻⁶. The page runs many such tests, so treat p near 0.05 as no evidence.
RSET

RSET_last difference per seed [s], same seed in both arms. Above 0: U gets
out earlier. Black bar: median. Regenerated by
scripts/docs/fire_blind_vs_coupled.py.
| Pair | pre-movement 0 s | 30 s | 60 s |
|---|---|---|---|
| S − U | +17.1 s; S > U in 20/20 | +40.4 s; 10/10 | +49.4 s; 10/10 |
| R − U | +0.7 s [−5.2, +5.4]; R > U in 13/20 (p = 0.26) | +11.8 s; 10/10 | +49.0 s; 10/10 |
| R − S | R < S in 20/20 | R < S in 10/10 (p = 0.002) | R < S in 6/10, R > S in 3/10 (p = 0.51) |
| R-na − R | +2.2 s [−0.7, +5.9]; R-na > R in 16/20, R-na < R in 3/20 (p = 0.004) | −0.8 s; R-na > R in 4/10 (p = 0.75) | +1.9 s; R-na > R in 6/10, R-na < R in 3/10 (p = 0.51) |
- Against S, U is not conservative at any pre-movement: smoke slows people, and U leaves that out.
- Against R, U is not conservative at 30 and 60 s. With no pre-movement the two are not resolved. R is later than U in 13 of 20 seeds on the last agent out (p = 0.26) and on p95 in 7 of 20 (median −0.9 s). The median gap on the last agent, 0.7 s, is far smaller than the seed-to-seed spread of U itself (50.1–56.9 s). With no wait almost every R agent goes to exit A and meets little smoke.
- The gap grows with pre-movement, for R − U from under 1 s to 49 s and for S − U from 17 s to 49 s (medians), because the fire grows while people wait. The largest single R − U, 57.3 s, is at 60 s.
- R against S: R is faster in 20 of 20 seeds with no pre-movement and in 10 of 10 at 30 s, by avoiding exit B’s smoke. At 60 s R and S take nearly the same exit and are not resolved: R earlier in 6, later in 3 of 10 (p = 0.51).
ASET − RSET at fixed points

Location ASET − RSET_last [s] at four fixed points, the same points for
every arm; marker: median over seeds, line: min–max. Right of 0: the last
agent is out before the limit is met there. Regenerated by
scripts/docs/fire_blind_vs_coupled.py.
Seeds in which the margin is positive (pass), at exit B / junction / branch mouth / exit A. Bold: the most passes at that point where the arms differ.
| Criterion | pre-movement | U | S | R | R-na |
|---|---|---|---|---|---|
| K ≥ 0.3 1/m | 0, 30, 60 s | 0/0/0/0 | 0/0/0/0 | 0/0/0/0 | 0/0/0/0 |
| HCl ≥ 300 ppm | 0 s | 0/0/2/0 of 20 | 0/0/0/0 | 0/0/2/0 | 0/0/0/0 |
| HCl ≥ 300 ppm | 30, 60 s | 0/0/0/0 | 0/0/0/0 | 0/0/0/0 | 0/0/0/0 |
| HCl ≥ 1000 ppm | 0 s | 0/20/20/0 of 20 | 0/0/3/0 | 0/20/20/0 | 0/19/20/0 |
| HCl ≥ 1000 ppm | 30, 60 s | 0/0/0/0 | 0/0/0/0 | 0/0/0/0 | 0/0/0/0 |
| FED ≥ 0.3 | 0, 30, 60 s | all pass | all pass | all pass | all pass |
- Visibility fails in every arm, seed and pre-movement; FED 0.3 passes in every seed of U, S, R and R-na. For these criteria the arm changes the size of the margin, not the verdict. R+FIC at 30 and 60 s is undetermined for FED 0.3: its RSET is censored (Sensitivity arms).
- HCl with no pre-movement. U’s median margin at the junction under HCl 1000 ppm is about +6 s (59 − 53.3), so U passes there in every seed. R passes as often as U (40 point-passes in each), R-na 39 times and S 3 times. Under HCl 300 ppm U and R pass at the branch mouth in 2 of 20 seeds. At every point and criterion U passes at least as often as each coupled arm. These counts hold only to a few seeds: changing nothing but the per-agent random draws (#361) moved them by up to 3 of 20 seeds at a point.
- The differences sit at HCl 1000 ppm (ISO FEC 1, an incapacitation-level value) and at the branch mouth under FEC 0.3, and the HCl in this deck is probably overestimated (see Limits).
People inside when the junction reaches the visibility limit (24 s): with no pre-movement a median of 72 in U, 72 in S, 78 in R and 90 in R-na; U has fewer than R in 20 of 20 seeds (median 6 fewer). At 30 and 60 s all 100 are inside in every arm. So U is not conservative on this count.
Dose

Share of agents with a value ≤ x, pooled over seeds (2,000 agents with no
pre-movement, 1,000 at 30 and 60 s). Curve further right: more dose. Values
until each agent’s exit, at z = 2.0 m (the deck’s only slice height; the
1.6 m default resolves to it, see
A crowd in a fire › The fire), 1 s
resolution. Seconds at K ≥ 0.3
1/m are secondary: obscuration alone is not treated as incapacitating for
people who are not performing tasks (ISO 13571:2012, §4.5, note).
Regenerated by scripts/docs/fire_blind_vs_coupled.py.
Per seed, U against R. R-na differs from R at 30 s (95 % against 51 % at exit A) and gives nearly the same exits at 0 s (97 % against 100 %) and at 60 s (6 % against 3 %).
| Dose metric | pre-movement 0 s | 30 s | 60 s |
|---|---|---|---|
| Median of the agents’ peak FIC | U higher in 20/20 | U higher in 10/10 | U lower in 10/10 |
| Agent-seconds at HCl ≥ 300 ppm | U higher in 19/20 | U lower in 10/10 | U lower in 10/10 |
| Agent-seconds at HCl ≥ 1000 ppm | U higher in 20/20 | U lower in 10/10 | U lower in 10/10 |
| Max FED | U higher in 17/20 | U lower in 10/10 | U lower in 10/10 |
| Agents past K 0.3 or HCl 300 ppm before getting out | U more in 20/20 | 100 in every arm | 100 in every arm |
- Against R, “conservative” depends on the metric and the pre-movement. With no pre-movement U over-states every dose metric: the peak FIC and the time above 1000 ppm in 20 of 20 seeds, the time above 300 ppm in 19 and the max FED in 17. U walks into exit B’s HCl, which almost every R agent avoids. At 30 s U still over-states the median peak FIC (10 of 10) but under-states the time above both HCl levels and the max FED in every seed: about half of R’s agents walk to B later, in thicker smoke. At 60 s U under-states every metric in every seed.
- Against S, U under-states the run totals in every seed, at every pre-movement: agent-seconds at HCl ≥ 300 and ≥ 1000 ppm and at K ≥ 0.3 1/m, and max FED. On single agents it does not always: U’s peak FIC is higher than S’s for 617 of 2,000 agents with no pre-movement, 287 of 1,000 at 30 s and 270 of 1,000 at 60 s.
- The largest max FED of any agent is 0.06 in U, 0.19 in R and 0.19 in S, each at 60 s. FED 0.3 passes at the four fixed points in every arm (table above), and no agent of U, S, R or R-na reaches it. A FED below 0.3 or 1 is not a statement of tenability: FED 0.3 is a threshold for susceptible people, and FED < 1 does not mean safe (Incapacitation thresholds).
Per-agent margin, first crossing of K 0.3 1/m minus the agent’s own exit, compared on the same agent (seed, origin, spawn order):
| pre-movement 0 s | 30 s | 60 s | |
|---|---|---|---|
| Agents crossing in both U and R | 566 of 2,000 | 1,000 of 1,000 | 1,000 of 1,000 |
| Median R − U among them | −3.8 s (R smaller in 429) | −6.3 s (836) | −26.6 s (998) |
| Median S − U, crossing in both | −1.8 s (S smaller in 1,551 of 1,894) | −14.1 s (972) | −27.8 s (998) |
With no pre-movement, 1,332 agents cross the limit in U but not in R, and none the other way round. Counting an agent that never crosses as having an infinite margin, R has the larger margin for 1,466 agents and U for 429. So with no pre-movement the direction depends on how agents who never cross are counted; at 30 and 60 s U’s margin is larger (not conservative) for 836 and 998 of 1,000 agents.
Is exit usage conservative?
Exit usage is not conservative or otherwise. Choosing exits is part of the scenario: each design fire scenario is analysed with design occupant scenarios, and the occupants’ initial route choice is one of the variables of such a scenario (Nilsson and Fahy 2016, pp. 2047, 2061). Here the difference is large: exit B for everyone without the fire, exit A for 100 % with it when people move at once, for about half after 30 s of waiting and for few after 60 s. It also drives the dose differences above.
Sensitivity arms
Incapacitation, per-agent thresholds, HCl slowdown and route look ahead
| Arm | Flags on top of R | Result |
|---|---|---|
| R-na | "anticipate": false in the scenario’s routing block (see below) | With no pre-movement 2.2 s later than R in median (R-na > R in 16 of 20, p = 0.004), with 97 % [95, 98] at exit A against 100 % in R. At 30 s: 95 % at A against 51 %, last out 0.8 s earlier in median. At 60 s: 6 % at A against 3 %. |
| R-det | tenability on (FED 1 incapacitates) | Identical to R: nobody reaches FED 1. |
| R-prob | R-det with --incapacitation-mode probabilistic | Identical to R at 0 and 30 s: nobody is incapacitated. At 60 s, 8 agents are incapacitated in 6 of 10 seeds and 9 are inside at 270 s; RSET is censored (> 270 s) in 6 seeds. The median last exit among the rest is 162.4 s, as in R. Their dose counts only until incapacitation. |
| R+FIC | R-det with --enable-fic-speed | No pre-movement: RSET 98.5 s [83.9, 118.3], with 14.5 [8, 24] agents at the 0.3 speed floor. At 30 s: median RSET 225.8 s, censored in 1 of 10 seeds, 1 of 1,000 agents inside at 270 s. At 60 s: censored in 10 of 10 seeds, 397 of 1,000 inside, and 22 agents per seed (median) reach FED 0.3. Max FED reaches 0.48 at 30 and at 60 s. |
To build R-na, add the key to a copy of the scenario and save the copy
next to a copy of geometry.wkt, which the scenario needs beside it:
mkdir -p ww/na
cp assets/t_junction/geometry.wkt ww/na/
uv run python - <<'EOF'
import json
from pathlib import Path
config = json.loads(Path("assets/t_junction/config_initial_pre0.json").read_text())
config["routing"]["anticipate"] = False
Path("ww/na/config_initial_pre0.json").write_text(json.dumps(config, indent=2))
EOF
uv run python run.py --scenario ww/na/config_initial_pre0.json --seed 4 \
--fds-dir "$FDS" --disable-tenability \
--output-sqlite ww/rna.sqlite --output-exit-history ww/rna_exits.csvSimulation finished in 60.68 s (100/100 evacuated).97 leave by exit A and 3 by exit B; in R, 98 take exit A and 2 exit B. The study script builds the same files
(write_noanticipate).
A censored RSET is only known to exceed 270 s. It is never compared by size
with U’s. An agent incapacitated or still inside at 270 s never counts as
out, so a coupled RSET cannot look shorter because people dropped out of
the count. The R-prob and R+FIC runs that reach 270 s with agents inside
are incomplete, and run.py exits with status 2 for them
(Exit status). R+FIC is an upper bound: it is mostly censored, its dose stops at
the cap, and it rests on HCl that is probably overestimated.
4. Run the whole study
uv run python scripts/docs/fire_blind_vs_coupled.py --data "$FDS" --runs RUNSRUNS must lie outside the repository. The script runs arms C, U, S, R,
R-na, R-det, R-prob and R+FIC for seeds 4–23 with no pre-movement and 4–13
at 30 and 60 s, one run.py process per arm and seed
(#198). It
skips runs that already exist, prints every number on this page, writes
summary_runs.csv and summary_agents.csv into RUNS, and redraws the
figures in site/static/images/fire-blind/. At 9c820e0f the 320 runs
took 481 s (about 8 min) of wall time on 8 workers, the default of
--workers, on an Apple M3 Pro; the analysis alone takes about 20 s. Seeds 1–3 were used for
a pilot and are left out.
The animation above is drawn from the same RUNS folder:
uv run python scripts/docs/study_animations.py fire-blind --data "$FDS" --runs RUNSProvenance. The numbers and figures on this page come from runs of
commit c619a046 on main (0.5.0), from a worktree of that commit, made
with the two commands above (Python 3.13.4). Each run’s manifest records
that commit and agent_seeding: spawn-key-blake2b-v2. The seed-4
walkthrough above gives trajectories identical to those study runs. When the code changes, re-run the
study into a new RUNS folder and compare the printed report with this
page.
Limits
One fire with no margin. Here, at every point, the visibility limit is met before the last person gets out, in every arm. Schröder et al. (2020, §4) state that the effects of smoke, heat and toxic gases on route choice and walking speed “should play a subordinate role as long as the safety margin is sufficiently greater than the limiting state”. In our view, the regime near the limit is where practice decides, and this study does not test it.
The size of the S − U and R − U gaps is not measured behaviour. The smoke-speed law is fitted to Frantzich and Nilsson’s data, K ≈ 1.9–7.4 1/m (read from their Fig. 14; see walking speed in smoke), and has a floor of 0.1 from K = 11.1 1/m. Share of moving agent-seconds below / within / above that range, and at the floor:
Arm pre-movement 0 s 30 s 60 s S 83 / 11 / 6 %, floor 3 % 41 / 37 / 22 %, floor 11 % 1 / 46 / 53 %, floor 18 % R 94 / 6 / 0 %, floor 0 % 45 / 46 / 9 %, floor 5 % 1 / 47 / 52 %, floor 18 % Below 1.9 1/m the law is outside the data too. There it slows people by 2.4 % at K = 0.3 1/m, the visibility limit, rising to about 15 % at 1.9 1/m.

Left: speed factor v/v₀ [-] against K [1/m] of the default law; shaded: the data range. Right: K at the moving agent [1/m], share of agent-seconds [%], pooled over all three pre-movements; the last bin holds K ≥ 24.5. “At the floor” counts K ≥ 11.1 1/m. Regenerated by
scripts/docs/fire_blind_vs_coupled.py.Route foresight. R’s route cost reads the smoke ahead from the whole FDS record (#125). R’s avoidance of exit B is therefore not a claim that people would see it coming. R-na removes only the look ahead in time: it still reads the current smoke along the whole route, including parts no occupant could see. With no pre-movement it sends 97 % [95, 98] to exit A against 100 % in R, at 30 s 95 % against 51 %, and at 60 s 6 % against 3 %. At 30 s foresight therefore moves agents toward exit B, the exit by the fire, when it sees smoke coming on the longer route.
All routes refused before people move. Then the router takes the route with the lowest optical depth τ (#458), here the shorter route to exit B past the burner. It is not the safer route: at 60 s the median over seeds of the largest max FED is 0.18 under the default rule, against 0.10 under the code-level
fallback_rule="hold", which keeps everyone on the route to A. At 0 and 30 s most R agents already take exit A. Whether this rule is right is open (#696).HCl is probably overestimated. The deck has no HCl loss to walls (A crowd in a fire › What this does not show). That makes the HCl crossings early and inflates R+FIC.
The HCl slowdown is opt-in, off in S and R, and incapacitation uses the FDS+Evac FED (FDS+Evac has no irritant slowdown), not HCl.
Constant pre-movement. Everyone waits the same 0, 30 or 60 s. The three values were fixed from the fire before any run: 0 s moves before the junction reaches the visibility limit (24 s), 30 s falls between the first and last of the six points of A crowd in a fire (18–46 s), 60 s after all of them. Babrauskas et al. (2010, pp. 346–347, 351) criticise pre-movement times of 0–80 s used for homes as unrealistic. Citing an NRCC study, they report that healthy occupants of a single-family house at night can need up to 11 min from alarm to exit (p. 346). That figure includes travel and is counted from the alarm, not the fire. Those are residential values and do not transfer to this geometry.
Everyone knows both exits (
familiarity: "full"). Agents who discover exits could fail to find one, and the familiarity draw depends on the JuPedSim id (#198). For sign loss in this fire, see A crowd in a fire.The time limit is 270 s, 30 s before the end of the FDS output. Route foresight samples ahead of the current time and can read past the end (#356); the longest route, 30 m from the branch to exit A, takes 23 s at the 1.3 m/s the cost assumes. Configured waypoints are not included in that bound.
Per agent goes beyond ISO 13571, which treats populations, not individuals (§5.2). The FED is the FDS+Evac FED, not the ISO asphyxiant FED.
The ASET side is not built in. The location ASET and the crossings are computed by the script, not by the engine (#210).
Sources
- Babrauskas, V., Fleming, J. M., & Russell, B. D. (2010). RSET/ASET, a flawed concept for fire safety assessment. Fire and Materials, 34(7), 341–355, pp. 346–347, 351. doi:10.1002/fam.1025
- Engineers Australia Society of Fire Safety (2014). Practice note for tenability criteria in building fires, version 2.0, §5.2 and Fig. 8, Tenability Criteria – Short Exposure, p. 15. Full reference on ASET and RSET.
- Frantzich, H., & Nilsson, D. (2003). Utrymning genom tät rök: beteende och förflyttning, Fig. 14. Full reference on walking speed in smoke.
- ISO 13571:2012, §4.5 (note) and §5.2; ISO/TR 16738:2009, Eq. 1, Eq. 2 and §5.6: paraphrased; see ASET and RSET.
- Nilsson, D., & Fahy, R. (2016). Selecting scenarios for deterministic fire safety engineering analysis: life safety for occupants. SFPE Handbook of Fire Protection Engineering, 5th ed., Ch. 57, pp. 2047, 2061. doi:10.1007/978-1-4939-2565-0_57
- Purser, D. A. (2003). ASET and RSET: addressing some issues in relation to occupant behaviour and tenability. Fire Safety Science, 7, 91–102, p. 92. doi:10.3801/IAFSS.FSS.7-91
- Schröder, B., Arnold, L., & Seyfried, A. (2020). A map representation of the ASET-RSET concept. Fire Safety Journal, 115, 103154, §4. doi:10.1016/j.firesaf.2020.103154
- Code:
_assign_initial_exitinpyfds_evac/core/scenario.py;RouteCostConfigand_arrival_timeinpyfds_evac/core/route_graph.py.
What next
- Usage › Smoke-blind runs and exit replay: every flag of the three arms.
- How do I get the egress time from an ensemble? for RSET over many seeds.
- ASET and RSET for the concepts.
- ASET-RSET maps after Schröder et al. (2020): arm U only, as ASET − RSET maps.