Verification
The model is verified: tests check that its components reproduce hand calculations and adapted IMO test cases. It is also compared with the results published in the EvacuatioNZ verification report; no EvacuatioNZ runs were made. A published network of a real building is compared with its EvacuatioNZ results in The Station nightclub. It is not validated: it has not been compared with evacuation drills, experiments or JuPedSim’s microscopic models.
Tests
The tests are in tests/. Run them from a clone of the repository:
uv run --extra cli --extra tui --group tui-test pytest -qThis runs the tests that every pull request runs on Python 3.13: the
hand calculations and IMO cases, the EvacuatioNZ cases, the property
tests on random networks (hypothesis profile pr, derandomized), the
documented-number tests, the tests of the command line and those of the
terminal UI. It ends with no failures and lists the long tests as
deselected. In a fresh environment, a plain uv run pytest -q skips the tests of the coloured
help and the pilot flows and snapshots of the terminal UI
(tests/test_tui.py); uv run --extra cli pytest -q, the Python 3.10
leg of CI, skips only the latter.
The terminal UI is tested by pilot flows, by a byte-for-byte comparison
of its result files with those of jupedsim-network run, and by SVG
snapshots, which are also the figures of the
Terminal UI page.
Two further commands run the long tests:
uv run --extra cli pytest -m slow
uv run pytest -m fuzz --hypothesis-profile=fuzzThe first runs the documented numbers of long runs: the 300-run case
study, the max_density and hydraulic-coefficient sweeps, the time-step example in
Limitations and the
snippets that call run_many(500). CI runs it as its own job, docs-numbers, and the site
deploys only when it passes. The second runs the long property runs and
fixed-seed sweeps of .github/workflows/fuzz.yml, weekly and on demand.
The property tests check on random networks that
- no agent is lost;
- no link, and no shared two-way connection under
"bounding"and"estimate", passes more than $1 + C\,t$ agents in any interval $t$; - no node exceeds
max_density; - a seed reproduces its run;
- networks with no two-way pair in use give the same result under all
three
counterflowvalues; - route tables take only tied shortest links.
The weekly job also reports how the evacuation time changes when $\Delta t$ is halved. It does not assert that a smaller $\Delta t$ never gives a later time, because that does not hold (Limitations).
The documented-number tests (tests/test_docs_numbers.py) rebuild every
number on these pages that a run produces from the code and compare it
with the page text exactly.
The table lists the tests with a hand-calculated expectation. “Result”
is what the test measures with the current code. In the counterflow
rows, values separated by “/” are for counterflow="independent" /
"estimate" / "bounding"; the derivation is under
Counterflow at a shared door.
| Case | Expected | Result | Tolerance | Test |
|---|---|---|---|---|
| IMO test 1: 40 m at 1.0 m/s | 40 s | 40.0 s | 0.5 s | test_free_walking_speed_imo_test_1 |
| IMO test 3 geometry, stair speed from 18/28 cm steps, as in EvacuatioNZ [2, §2.1.3] | $10/(k(1-0.266\cdot0.54)) = 10.84$ s | 11.0 s ($\Delta t = 0.1$ s) | 0.25 s | test_stair_speed_imo_test_3 |
| IMO test 4 geometry, 100 agents, 1 m door, $F_s = 1.33$, hydraulic door flow as in EvacuatioNZ [2, §2.2] | $(N-1)/C = 99/0.931 = 106.3$ s | 106.5 s | 1 s | test_door_flow_imo_test_4 |
| 50 agents, 0.6 m opening | $(N-1)/C = 49/0.78 = 62.8$ s | 63.0 s | 0.5 s | test_opening_has_no_boundary_layer |
| 200 agents at 2 m⁻² walk 20 m [3, p. 15] | $20/(1.4\,(1-0.266\cdot2)) = 30.5$ s | 31.0 s | 0.6 s | test_congested_walking_speed |
| Merge weights 1 : 3 over 150 s | flow ratio 3 | 3.0 | 15 % | test_merge_weights_split_flow |
| Stair 7/11 in: $k$ and $F_s = k/(4a)$ | 1.08 m/s (SFPE Table 67.2 [1, p. 2174]), 1.01 persons/s/m (SFPE Table 67.5 [1, p. 2176]) | 1.081, 1.016 | 0.01 | test_stair_specific_flow_follows_geometry |
One start node, 30 m of 18/28 cm stair against 35 m of level path to the exit, routing_policy="shortest_free_flow_time" (Routes) | $k_\text{stair} = 51.8\sqrt{28/18}/60 = 1.0768$ m/s; costs $30/(0.85636 \cdot 1.0768) = 32.53$ s (stair) and $35/(0.85636 \cdot 1.4) = 29.19$ s (level), with $0.85636 = 1 - 0.266 \cdot 0.54$ the density floor factor; shortest distance takes the stair, shortest time the level path | 32.53 s, 29.19 s; routes as expected | $10^{-6}$ relative | test_analytical_level_and_stair_costs |
| Supply ramp between passages, 45 agents per room | passages at $t_q + \Delta t\,\lceil k/(\varphi\,C\,\Delta t)\rceil$, $k = 1 \ldots 45$; first exit 94.5 s, $T = 150.0$ s | equal | exact | test_supply_ramp_holds_between_passages |
| Steady density on the ramp, exit door 0.7 m and 0.9 m | $40\,D^*$ = 90.11 and 80.16 agents | mean 90.11 and 80.16, maximum 91 and 81 | 0.5 agent; maximum ≤ $\lceil 40\,D^* \rceil$ | test_supply_ramp_sets_steady_density |
Chain of five 100 m² rooms A–B–C–D–E with exits at A and E (safe nodes s0 and s1); all six connections are 1 m doors of length 0; $N = 50$ agents in E, 1.2 m/s, no pre-movement, $\Delta t = 0.5\,\mathrm{s}$; both split_ties settings (Routes) | $C = 1.3\,(1-2\cdot0.15) = 0.91$ /s; $\Delta t\,\lceil (N-1)/(C\,\Delta t)\rceil = 0.5\cdot\lceil 49/0.455\rceil = 54.0$ s | 54.0 s, E->s1 = 50, all other links 0 | exact | test_zero_length_chain_uses_adjacent_exit |
| Two 200 m² rooms A and B swap 20 + 20 agents through one two-way 1 m door | independent: 21.5 s; estimate ($g \le 1$): $T \ge 43.5\,\mathrm{s}$, recursion 44.0 s; bounding ($g \le 0.94$): $T \ge 46.5\,\mathrm{s}$, recursion 46.5 s | 21.5 / 44.0 / 46.5 s; pair passes $\le 1 + C\,t$ | exact | test_counterflow_door_swap_balanced |
| Same door, 36 agents in A against 4 in B | recursion: $T$ = 39.0 / 45.5 / 47.0 s; B’s passages at 0.5, 1.5, 2.5, 3.5 / 4.0, 8.5, 14.5, 20.5 / 4.0, 9.0, 15.5, 22.0 s | equal | exact | test_counterflow_door_minor_ten_percent |
| 1.2 m stair entry: 20 evacuees leave the flight, 4 responders enter it | recursion: evacuees’ $T$ = 21.5 / 29.0 / 30.0 s; responders out at 1.5–4.5 / 4.0, 9.0, 12.5, 19.5 / 4.0, 9.5, 13.5, 20.5 s | equal | exact | test_counterflow_stair_responders |
Chain A–E of the row above, 10 agents per room, no target, both split_ties | $\Delta t\,\lceil (N_A - 1)/(C\,\Delta t)\rceil$ = 26.5 s ($N_A = 25$) and 32.0 s ($N_A = 30$), the same under all three values; no link used both ways | equal | exact | test_default_routing_has_no_counterflow |
| 1 agent in A against 50 in B, B of 20 m² ($D_B = 2.5\,\mathrm{m^{-2}}$) | A’s agent exits at 13.0 s (estimate) and 14.0 s (bounding); $T$ = 57.0 s and 58.0 s | equal | exact | test_counterflow_supply_uses_door_share |
The two supply-ramp tests check that the code delivers the share
$\varphi_n$ of the
update scheme,
with $D_\text{max} = 2.75\,\text{m}^{-2}$ (the default max_density)
and $D_\text{peak} = 1.88\,\text{m}^{-2}$. In the first, two 20 m²
rooms of 45 agents each swap occupants through two one-way doors, 1 m
wide and 2 m long, with $C\,\Delta t = 0.455$. The swap keeps
$D = 2.25\,\text{m}^{-2}$ in both rooms, so one share
$\varphi = 0.5745$ holds for all 45 passages. Walking 2 m at
$0.5621\,\text{m/s}$ takes 8 steps, so the first step with a queue
starts at $t_q = 3.5$ s. Each group then leaves through a 50 m one-way
link to the far exit. Walking 50 m at the same speed takes 178 steps,
so the first agent to pass, at 5.5 s, exits at 94.5 s.
$T = 150.0$ s follows from a step-by-step
recursion of these exits. In the second, a 40 m² room
is fed through a 1 m one-way door ($C_\text{in} = 0.91\,\text{s}^{-1}$)
and left through a door of width $w$, with
$C_\text{out} = 1.3\,(w - 0.3) = 0.52$ and $0.78\,\text{s}^{-1}$.
Setting $\varphi\,C_\text{in} = C_\text{out}$ gives
$D^* = D_\text{max} - (D_\text{max} - D_\text{peak})\,C_\text{out}/C_\text{in}$.
The test averages the occupancy over $t \ge 500\,\text{s}$ for
$w = 0.7\,\text{m}$ and over $t \ge 800\,\text{s}$ for
$w = 0.9\,\text{m}$.
Further tests check pre-movement delays, route choice, conservation of
agents, that max_density is never exceeded, that no link passes more
than $1 + C\,t$ agents in any interval $t$, reproducibility with a
fixed seed, the stair-range warning, nan for incomplete runs, quantiles with incomplete runs,
the error messages, that counterflow rejects unknown values
(test_counterflow_rejects_unknown_value), and the means and
truncation of the distributions. The two room swaps of
test_supply_carry_admits_large_agent_swap are pinned to
counterflow="independent".
Counterflow at a shared door
The rule is described in Counterflow. All five rows use $\Delta t = 0.5\,\mathrm{s}$, seed 1 and no pre-movement. Every agent queues at its first link in step 0; it reaches its next link, the exit or, for a responder, flight→floor, one step after its previous passage.
Door geometry (door_swap in tests/test_network.py). Rooms A and
B are joined by connect("A", "B", width=1.0), a two-way door of
length 0: $C = 1.3\,(1 - 0.3) = 0.91\,\mathrm{s^{-1}}$ and
$C\,\Delta t = 0.455$. A’s agents head for B’s exit and B’s for A’s;
both exits are 20 m openings of length 0. An agent that passes the door
in step $k = 0, 1, \ldots$ is recorded in link_flow at
$(k + 1)\,\Delta t$ and exits at $(k + 2)\,\Delta t$.
independent. A fresh link passes its first agent in step 0 and its
$k$-th agent ($k \ge 2$) in step $\lceil (k - 1)/(C\,\Delta t) - 1\rceil$.
With 20 agents: $\lceil 19/0.455 - 1\rceil = 41$, so
$T = 43 \cdot 0.5\,\mathrm{s} = 21.5\,\mathrm{s}$. With 36:
$\lceil 35/0.455 - 1\rceil = 76$, $T = 39.0\,\mathrm{s}$; the 4 agents in B
pass in steps 0, 2, 4 and 6.
Bound for the shared door. In steps $0 \ldots m$ a pair passes at
most $1 + g\,C\,\Delta t\,(m + 1)$ agents. Forty passages with $g \le 1$
need $m \ge 39/0.455 - 1$, so $m \ge 85$ and
$T \ge 87 \cdot 0.5\,\mathrm{s} = 43.5\,\mathrm{s}$. With $g \le 0.94$:
$m \ge \lceil 39/(0.94 \cdot 0.455) - 1\rceil = 91$, so
$T \ge 46.5\,\mathrm{s}$, which bounding attains.
Recursion. The estimate and bounding values apply the rule step
by step. With $n_A$, $n_B$ the queues, $s$ the minor share,
$g = g_0 + (g_{1/2} - g_0)\,2s$, $y_A = 0.17 + 0.66\,n_A/(n_A + n_B)$,
budget $B_P = c_P + g\,C\,\Delta t$ and key
$\lambda = \sigma + (j + 1)/y$, the first steps of the balanced swap under
estimate ($g_0 = 0.90$, $g_{1/2} = 1.00$, $c_P = 1$ at the start) are:
| Step | $n_A$, $n_B$ | $s$ | $g$ | $B_P$ | $\lambda_A$, $\lambda_B$ | Passes |
|---|---|---|---|---|---|---|
| 0 | 20, 20 | 0.5 | 1 | 1.455 | 2.000, 2.000 | A (tie: A→B was created first) |
| 1 | 19, 20 | 0.4872 | 0.99744 | 0.909 | – | none |
| 2 | 19, 20 | 0.4872 | 0.99744 | 1.363 | 4.034, 1.967 | B |
The first steps of 36 against 4 under bounding ($g_0 = 0.84$,
$g_{1/2} = 0.94$) are:
| Step | $n_A$, $n_B$ | $s$ | $g$ | $B_P$ | $\lambda_A$, $\lambda_B$ | Passes |
|---|---|---|---|---|---|---|
| 0 | 36, 4 | 0.1000 | 0.8600 | 1.391 | 1.309, 4.237 | A |
| 1 | 35, 4 | 0.1026 | 0.8605 | 0.783 | – | none |
| 2 | 35, 4 | 0.1026 | 0.8605 | 1.174 | 2.621, 4.207 | A |
| 5 | 34, 4 | 0.1053 | 0.8611 | 1.350 | 3.936, 4.176 | A |
| 7 | 33, 4 | 0.1081 | 0.8616 | 1.134 | 5.254, 4.143 | B |
In step 7, B’s key is still $1/y_B = 1/0.2414 = 4.143$ ($\sigma_B = 0$), so B’s first agent passes and is recorded at 4.0 s.
Stair (test_counterflow_stair_responders). Node “flight” of 30 m²
with 18/28 cm steps; rooms “lobby” and “floor” of 100 m²; safe node
“street” off the lobby and “roof” off the floor, through 20 m one-way
openings of length 0. connect("flight", "lobby", width=1.2, kind="stair") and the same to “floor”:
$k = 51.8\sqrt{28/18}\ \mathrm{m/min} = 1.0768\,\mathrm{m/s}$,
$F_s = k/(4a) = 1.0120\,\mathrm{s^{-1}m^{-1}}$,
$C = 1.0120 \cdot 0.9 = 0.9108\,\mathrm{s^{-1}}$ and
$C\,\Delta t = 0.4554$. The stair values $g_0$, $g_{1/2}$ apply. The 20
evacuees leave the flight to the lobby and exit at $(k + 2)\,\Delta t$; the
4 responders start in the lobby, target the roof, pass lobby→flight in
step $k$, flight→floor in step $k + 1$ and exit at $(k + 3)\,\Delta t$.
independent: evacuees $\lceil 19/0.4554 - 1\rceil = 41$, so 21.5 s;
responders in steps 0, 2, 4, 6, so out at 1.5, 2.5, 3.5 and 4.5 s.
Chain. Rooms A–E as in the chain row above, with 10 agents per
room. A’s queue never empties, so
$T = \Delta t\,\lceil (N_A - 1)/(C\,\Delta t)\rceil$, where $N_A$ is the
number of agents leaving through A->s0. With ties split, C’s 10 agents
split 5/5, so $N_A = 25$ and $T = 26.5\,\mathrm{s}$. split_ties=False
sends C’s 10 via B (and warns), so $N_A = 30$ and $T = 32.0\,\mathrm{s}$.
The link totals for A->s0, B->A, C->B, C->D, D->E, E->s1
are 25, 15, 5, 5, 15, 25 and 30, 20, 10, 0, 10, 20.
Supply uses the door share. The door geometry, with B of 20 m²
holding 50 agents ($D_B = 2.5\,\mathrm{m^{-2}}$) and 1 agent in A. B is
above $D_\text{peak} = 1.88\,\mathrm{m^{-2}}$, so it admits A’s agent only
when $\alpha_B = \gamma_B + \varphi_B\,\tilde C_A\,\Delta t \ge 1$, with
$\varphi_B = (2.75 - D_B)/(2.75 - 1.88)$ and $\tilde C_A = y_A\,g\,C$, A’s
share of the door. Under bounding, $y_A = 0.17 + 0.66/51 = 0.183$,
$g = 0.844$ and $\tilde C_A\,\Delta t \approx 0.070$. $D_B$ falls by
$0.05\,\mathrm{m^{-2}}$ with each agent that leaves B.
| Step | $D_B$ (m⁻²) | $\alpha_B$ | $\gamma_B$ after the step | $B_P$ | Passes |
|---|---|---|---|---|---|
| 0 | 2.50 | 0.020 | 0.020 | 1.384 | B |
| 5 | 2.40 | 0.154 | 0.154 | 1.304 | B |
| 10 | 2.30 | 0.328 | 0.328 | 1.225 | B |
| 15 | 2.20 | 0.540 | 0.540 | 1.146 | B |
| 20 | 2.10 | 0.793 | 0.793 | 1.067 | B |
| 24 | 2.00 | 1.027 | 1.000 (cap) | 0.605 | none |
| 25 | 2.00 | 1.062 | 1.000 (cap) | 0.989 | none |
| 26 | 2.00 | 1.062 | 0.062 | 1.374 | A |
$\alpha_B$ first exceeds 1 in step 24, but the pair budget allows a
passage only in step 26, where A’s key wins: A’s agent exits at
$28 \cdot 0.5\,\mathrm{s} = 14.0\,\mathrm{s}$. With $C\,\Delta t$ in place of
$\tilde C_A\,\Delta t$ it would exit at 6.0 s (5.5 s under estimate);
with $\tilde C_A = 0$, only after $D_B$ falls below $1.88\,\mathrm{m^{-2}}$,
at 17.5 s (16.5 s).
IMO tests as written and as tested
The IMO test cases are in MSC.1/Circ.1533, Appendix 2, paragraphs 3–6 [4].
- Test 1 (one person, 2 m × 40 m corridor, 1 m/s, 40 s) is tested as written.
- Test 3 as written asks that one person with a walking speed of 1 m/s covers a 2 m wide stair of 10 m, measured along the incline, in 10 s [4]. With 18/28 cm steps this model does not meet it, by design. On a stair the input speed only caps the walking speed, which comes from the stair geometry: $k = 51.8\sqrt{T/R}$ gives 1.077 m/s for 18/28 cm steps, and $k(1 - 0.266 \cdot 0.54) = 0.922$ m/s. A lone walker takes 10.84 s by hand calculation (11.0 s in the run at $\Delta t = 0.1$ s) instead of 10 s. Shallower steps with $T/R \ge 1.83$ cap the stair speed at or above 1 m/s. The test checks this adaptation, the same one EvacuatioNZ uses for its stair verification [2, §2.1.3].
- Test 4 asks for 100 persons in an 8 m × 5 m room with a 1 m exit, and that the flow over the whole period does not exceed 1.33 persons/s [4]. The link capacity of 0.931 persons/s and the mean flow of 100/106.5 s = 0.94 persons/s both meet this criterion. Specific flow, boundary layers and a target time are not part of the test; the construction $1.33 \times 0.7$ m is EvacuatioNZ’s [2, §2.2].
Passage time: (N−1)/C and N/C
The door rows check the model’s own convention, in which the first agent passes at once and the last after $(N-1)/C$ (update scheme). The SFPE hand calculation is $N/C$ [1, Eq. 67.9, p. 2177]:
| Case | Model | $(N-1)/C$ | SFPE $N/C$ | EvacuatioNZ [2, §2.2] |
|---|---|---|---|---|
| IMO 4 geometry | 106.5 s | 106.3 s | 107.4 s | 107.5 s by hand, 108 s simulated |
| 0.6 m opening | 63.0 s | 62.8 s | 64.1 s |
The SFPE value for IMO 4 lies 1.07 s from the test’s expectation, more than its tolerance of 1 s. The model result lies 0.9 s below it.
EvacuatioNZ verification cases
The cases of the EvacuatioNZ verification report version 2.11
[2] are rebuilt with this model and compared three ways:
the hand value, the result published in the report, and this model at
$\Delta t = 0.5$ s. No EvacuatioNZ runs were made. Page numbers are the
printed page numbers of the report; the PDF page is one higher. The
tests are in tests/test_evacuationz_verification.py. Every expected
value in them is a hand calculation; values read from figures of the
report are never asserted.
The report’s inputs are mapped the same way in every case. These rules are accepted for the EvacuatioNZ cases only:
- A door (
enz_door) is a door with $b = 0.15$ m and $F_s = 1.33$ persons/s/m, EvacuatioNZ’s value, passed explicitly in each case; the model’s default stays $F_s = 1.3$. An untyped connection is an opening with $b = 0$ and $F_s = 1.33$, and stairs (enz_stairs) are a stair node with $F_s = k/(4a)$ from the step geometry. - A connection that is both stair and door is one link with the lower of the two capacities.
- This model walks a link’s length inside the source node, so stair travel is put on the link leaving the stair node. The total distance is unchanged.
- A stair given by its height $H$ has the length $L = H\sqrt{1 + (T/R)^2}$.
- A random start distance is $U(0, L_{\max})$ with $L_{\max}$ stated per case; the report does not give the range.
| Case | Report | Hand value | EvacuatioNZ | This model | Explanation |
|---|---|---|---|---|---|
| Travel speed, 40 m along a link or from a start distance at 1.0 m/s | §2.1.1–2.1.2, pp. 6–7 | 40 s | 40.5 s | 40.0 s | test_travel_speed_from_start_distance |
| Stair speed, 10 m of 180/280 mm steps | §2.1.3, p. 8 | 10.9 s (Eq. 2.1), 10.5 s (SFPE table) | 12 s | 11.0 s ($\Delta t = 0.1$ s), 11.5 s ($\Delta t = 0.5$ s) | At $\Delta t = 0.5$ s: 10.84 s, plus one step for the transfer into the stair, rounded up to the step. test_stair_speed_imo_test_3, test_stair_flow_uncongested |
| Door flow, IMO 4 room, 100 agents, 1 m door | §2.2, p. 9 | 107.5 s ($N/C$ with $C = 0.93$) | 108 s | 106.5 s | This model’s convention is $(N-1)/C = 106.3$ s, see above |
| Door flow, 1–3 m doors and 1 m opening, 1–100 agents | §2.2, p. 10 | $(N-1)/C$ | Fig. 2.2, graph only | table below | test_door_flow_widths |
| Stair flow, 1–1000 agents, stairs 10 × 1, 200 × 1 and 200 × 5 m | §2.3, pp. 12–13 | $L/S_0 + (N-1)/C$ | Fig. 2.6, graph only | table below | test_stair_flow_* |
| Fire Engineering Design Guide, 90 agents, room over one stair | §2.4, pp. 13–15 | 186 s (report, p. 14), the sum of the FEDG’s 0.20 + 2.9 min [18, p. 231]; no room walk | 179 s (p. 14); 168 s with random start (p. 15) | first exit 30.5 s; last exit 129.0 s | Capacity bound $30.5 + 89/0.911 = 128.2$ s; exit C is merged into the stair link (lower capacity), see below. test_fedg_first_exit, test_fedg_last_exit_bound |
| SFPE Handbook nine-storey building | §2.5, pp. 15–17 | 1524 s (SFPE Solution A, 25.4 min; Solution B 1518 s); ≥ 1538.9 s in this model’s conventions ($t_{\text{first}} + 1199/0.811$) | 1871 s (v2.11); 1471 ± 3 s in [7, manuscript p. 20; Fig. 10] (2009, different network) | 1548.5 s | 300 per floor on floors 2–9 [1, p. 2181] [7, Fig. 11] and a 36 in exit door per stair from the SFPE example [1, p. 2181]; the report gives neither, and neither EvacuatioNZ document gives the ground-floor exit. The exit door (0.811/s) controls: $70.0 + 1199/0.811$. Stair length 6.76 m from $H = 12$ ft. Landings are nodes: each link’s length is walked in the node it leaves, so the 4.8 m of landing travel is level travel and corridor→landing is 4.8 m shorter below the top floor. With the landing travel walked on the stair link instead, 1915.5 s. Agents start at the room door; the report draws a random start distance. With a start $U(0, 91.44)$ m, 1542.5 s (seed 1; first exit 64.0 s instead of 70.0 s; the exit door still controls). test_sfpe_nine_storey |
| SFPE Guide on Human Behavior, example 1, 300 agents, start at the door | §2.6, pp. 18–20 | 246 s (Guide); 234.95 s in this model’s conventions | 221.5 s | 235.5 s | $149/0.682 + 15.27/S_0$. test_sfpe_guide_example_1 |
| The same, start 200 ft from the door | §2.6, pp. 18–20 | 300 s (Guide); 285.8 s in this model’s conventions | 283 s | 286.0 s | Plus 60.96 m at 1.199 m/s |
| Distributions: fixed 45 and 120 s, $U(10, 100)$, $N(120, 30)$, log-normal mean 5, sd 2 | §3.1, pp. 21–25 | means 55, 120, 5; sd 25.98, 30, 2 | Figs. 3.1–3.3, graph only | means 54.88, 119.66, 4.97; sd 26.07, 29.82, 1.99 (20 000 samples) | test_fixed_distribution_is_exact, test_distribution_moments |
| Room clearance, IMO 4 room, fixed delay 0, 30, 120 s | §3.2, p. 25 | $d + (N-1)/C$ | Fig. 3.4, graph only (≈ 114, 144, 234 s) | 106.5, 136.5, 226.5 s (start 0) | A fixed delay shifts the result by exactly $d$, also with a dispersed start $U(0, 8)$ m and the same seed. EvacuatioNZ’s dispersed run without delay lies about 6 s above its own 108 s with the start at the door; this model gives 106.5 s at the door and 106.5–107.0 s with a dispersed start. test_room_clearance_* |
| Room clearance, triangular delay (0, $m$, $2m$), $m$ = 15, 30, 60 s, start at the door, seed 0 | §3.2, p. 25 | $\max_i(\tau_{(i)} + (N-1-i)/C)$: 107.84, 110.41, 128.01 s | Fig. 3.4, graph only | 108.5, 111.0, 128.5 s | Each lies 0.49–0.66 s above its bound. test_room_clearance_triangular_bound |
| Room clearance, fixed delay 0, dispersed start $U(0, 8)$ m, 20 seeds | §3.2, p. 25 | $(N-1)/C = 106.3$ s | Fig. 3.4, graph only (≈ 114 s) | 106.5–107.0 s, mean 106.6 s | No seed lies below the hand value. test_room_clearance_dispersed_without_delay |
| Exit choice, minimum distance | §5.1.1, p. 31 | Exit 5 | Exit 5 | Exit 5 | test_exit_choice |
| Exit choice, specified exit | §5.1.5, p. 33 | Exit 6 | Exit 6 | Exit 6 | test_exit_choice |
| Required connection | §5.2, pp. 34–35 | direct door | direct door | through Room 2 and Room 3 | Not modelled. test_required_connection_is_not_modelled |
Door and opening flow with the IMO 4 room, $F_s = 1.33$, start at the door. Every result lies within one step above $(N-1)/C$:
| Width | $N = 1$ | $N = 10$ | $N = 100$ |
|---|---|---|---|
| 1 m door | 0.5 s | 10.0 s | 106.5 s |
| 2 m door | 0.5 s | 4.0 s | 44.0 s |
| 3 m door | 0.5 s | 3.0 s | 28.0 s |
| 1 m opening | 0.5 s | 7.0 s | 74.5 s |
Stair flow with 180/280 mm steps: $k = 1.0768$ m/s, free stair speed $S_0 = k(1 - 0.266 \cdot 0.54) = 0.9221$ m/s and $C = 1.012\,(w - 0.3)$. The text of §2.3 names two stairs; the caption of Fig. 2.7 names 200 m stairs of 1.0 m and 5.0 m. All three are run.
| Stair | $N$ | This model | $L/S_0 + (N-1)/C$ | Peak on the stair |
|---|---|---|---|---|
| 10 × 1 m | 1 | 11.5 s | 10.84 s | 1 |
| 10 × 1 m | 10 | 26.0 s | 23.55 s | 10 |
| 10 × 1 m | 100 | 169.0 s | 150.60 s | 27 (limit) |
| 10 × 1 m | 1000 | 1439.5 s | 1421.07 s | 27 (limit) |
| 200 × 1 m | 1 | 217.5 s | 216.90 s | 1 |
| 200 × 1 m | 10 | 230.0 s | 229.60 s | 10 |
| 200 × 1 m | 100 | 357.0 s | 356.65 s | 100 |
| 200 × 1 m | 1000 | 1984.5 s | 1627.12 s | 550 (limit) |
| 200 × 5 m | 1 | 217.5 s | 216.90 s | 1 |
| 200 × 5 m | 10 | 219.0 s | 218.79 s | 10 |
| 200 × 5 m | 100 | 238.0 s | 237.71 s | 100 |
| 200 × 5 m | 1000 | 444.5 s | 426.93 s | 1000 |
While fewer than $0.54\,L\,w$ agents are on the stair, everyone walks at $S_0$ and the result lies within two steps of the hand value. With more agents the stair fills and everyone on it walks at $k(1 - 0.266\,D)$, so the hand value is only a lower bound. On the 10 m stair the node stays at its limit of 27 agents (2.7 m⁻²), and the slower walk adds about 18 s. On the 200 × 1 m stair the node holds 550 agents, its limit, and all walk at about 0.29 m/s. The flow from the first to the last exit is 1.012 persons/s per metre of effective width for both 1000-agent runs on the 200 m stairs, equal to the stair’s $F_s = k/(4a) = 1.012$. Fig. 2.7 of the report shows 1.0 persons/s/m.
Fire Engineering Design Guide. The example is the FEDG’s room over one stair [18, pp. 229–231]. The 10 × 10 m room holds $N_o = A_f D_o = 100 \times 0.9 = 90$ people, with $D_o = 0.9$ m⁻² from Table 11.1 [18, p. 229]. The report starts all agents 20 m from door B, a 1.0 m door into a 10 × 1.2 m stair [2, §2.4, p. 13], and this model’s test does the same. The stair link, $1.012 \times 0.9 = 0.911$ persons/s, limits the exit, not exit C with $1.33 \times 0.7 = 0.931$ persons/s (door B has the same capacity). The first agent reaches door B at 19.0 s and leaves the stair at 30.5 s: 30.0 s at the free stair speed, slowed by the agents that follow onto the stair. The capacity bound for the last exit is $30.5 + 89/0.911 = 128.2$ s; this model gives 129.0 s.
How the FEDG gets 186 s
The report’s hand value is 186 s [2, §2.4, p. 14]. The FEDG does not print it. It prints the stair walk $t_{ts} = 10/48.8 = 0.20$ min and the passage through exit C, a 1.0 m door at the foot of the stair, $t_{qc} = 90/31.4 = 2.9$ min [18, pp. 229, 231]. Their sum, 3.1 min or 186 s, is the evacuation time of 8.0 min less the response time of 4.9 min [18, pp. 230–231]. Unrounded, the same chain gives 184.8 s, 12.3 s on the stair and 172.5 s at exit C, with $D_s = 0.915$ m⁻² against the printed 0.92 (our arithmetic). Spearpoint (2009) gives a movement time of 185 s [7, manuscript p. 16].
The hand value contains no room walk. The FEDG assumes that the first person enters exit B at the start of the evacuation [18, p. 230], and Spearpoint (2009) writes that “the people immediately reach the top of the stairs” [7, manuscript p. 16]. The FEDG’s 20 m is $L_t$, the distance from the furthest point to exit B. It gives the last person’s walk, 0.30 min [18, p. 229], which is shorter than the 2.2 min queue at door B [18, p. 230] and does not enter the result. Starting every agent 20 m from the door is the report’s choice [2, §2.4, p. 13].
The FEDG computes the flow at door B as speed times density at the design density and carries it downstream: the stair density follows from that flow, and exit C passes the stair’s specific flow [18, pp. 230–231]:
- door B: $F_s = 63.9 \times 0.9 = 57.5$ persons/min/m and $F_a = 57.5 \times 0.7 = 40.3$ persons/min (0.67 persons/s);
- stair: $F_s = 40.3/0.9 = 44.8$ persons/min/m gives $D_s = 0.92$ m⁻² (the other root is 2.84) and $S = 48.8$ m/min;
- exit C, effective width 0.7 m: $F_a = 44.8 \times 0.7 = 31.4$ persons/min (0.52 persons/s). This flow controls.
The FEDG gives the stair length as $L_s = 10.0$ m and uses it as a walked distance, $t_{ts} = L_s/S$ [18, pp. 229, 231]. The test uses 10.0 m for the stair node and the stair link, as Listing 2.10 does (lines 17 and 40) [2, §2.4, pp. 13–14]. The FEDG does not say whether 10 m is the walked length, the horizontal run or a height. Its plan, Fig. 11.6, draws the stair along the 10 m wall [18, p. 229], which suggests a horizontal run. The walked length would then be $10\sqrt{1 + (180/280)^2} = 11.9$ m, about 2 s more. For the convention of this model, see Limitations.
This model uses capacities instead: 0.931 persons/s at door B and
0.911 persons/s on the stair link. EvacuatioNZ puts exit C, the 1.0 m
door, on the stair connection (enz_door in Listing 2.10, lines 46–48)
[2, §2.4, pp. 13–14]. By the rule above, this model maps
that connection to one link with the lower of the two capacities. The
stair’s 0.911 persons/s is below the door’s 0.931 persons/s, so
fedg() uses the stair link and exit C does not bind. The
FEDG’s lower flow at exit C (0.522 persons/s) accounts for most of
the 57 s between 186 s and 129 s:
$89/0.522 - 89/0.911 \approx 73$ s more, 19 s less for the room walk
and about 1 s more on the stair (our arithmetic).
EvacuatioNZ’s 179 s is about 50 s longer than this model’s result. In Fig. 2.8 its stair holds about 33 agents from about 80 s to 133 s, and its exits run at about 0.6 agents/s, below both capacities. In this model the stair holds at most 14 agents. The report does not say what limits the flow out of a full node in EvacuatioNZ, so the difference is unexplained. The two documents use different EvacuatioNZ versions and inputs: Spearpoint (2009) uses a zero-length room–stair connection and minimum, maximum or random start positions and reports 257 s, 275 s and 260 ± 4 s [7, manuscript pp. 16–17]; the report uses a 0.1 m connection and a 20 m start and gives 179 s and 168 s [2, §2.4, pp. 14–15].
Tied stairs. EvacuatioNZ splits the agents of §2.6 between the two
stairs with a least-populated-connection rule. This model has no such
rule; the two stairs are tied, and agents alternate between them
(Networks). This gives
the same 150/150 split by a different, fixed rule. With
split_ties=False all 300 agents take one stair and leave after
455.5 s (start at the door) and 506.0 s (start 200 ft away). The
report’s EvacuatioNZ result
of 221.5 s lies within 1.6 s of $150/0.682 = 219.9$ s, although the
last agent still has about 16 s of stair to descend; the Guide’s 246 s
does not match $N/C$ at the door or the stair with these constants.
In §2.5 the two stairs are tied at each corridor, so 150 agents per
floor go to each, the same 150/150 split as the SFPE Solution B, which
says “Divide each floor in half to produce two exit calculation zones”
[1, p. 2182]. With split_ties=False all 2400 agents take
one stair and leave after 3027.5 s.
In §5.1 the report gives no node sizes or widths; the test assumes 25 m² nodes, 1 m doors and a start $U(0, 5)$ m and checks only the exit used. The report’s distances of 5 m to Exit 5 and 15 m to Exit 3 leave out the 1 m link from Room 1 to Room 2; the full paths are 6 m and 16 m.
Rules of the report that this model does not have: door leaves and door closers (§2.2), the maximum-distance, minimum-nodes and random single exit choices (§5.1.2–5.1.4), required connections (§5.2) and the least-populated connection (§2.6).
Door flow and merging
(a) IMO test 4 geometry. The model follows $1 + C\,t$, the $(N-1)/C$ convention, to within one time step. The SFPE hand calculation $C\,t$ lies one agent lower and ends at $N/C = 107.4$ s; the inset shows the last 13 s. (b) Two rooms feeding a full corridor with merge weights 1 : 3. Over 150 s the flows reach 3 : 1. At the start, room a passes no one from 2.0 s to 17.0 s while room b catches up on the share it lost before the corridor filled (Limitations).
Sensitivity to max_density
In EvacuatioNZ version 1.2, raising the maximum node density from 2.5 to 3.5 m⁻² more than doubled the evacuation time of a 21-storey hotel without pre-movement delay, from 539 s to 1232 s [5, Table 5.5, p. 55]. With pre-movement delay it rose from 796 s to 1156 s. These are means of 100 runs with the local door queue density fixed at 2.2 m⁻²; the runs ranged from 529 to 553 s at 2.5 m⁻² and from 1107 to 1502 s at 3.5 m⁻², both without delay. Tsai judged the 3.5 m⁻² result improbable, since it was longer without delay than with delay [5, p. 56], and recommended 2.5 m⁻² [5, pp. 62, 69]. Tsai attributes the wider spread to queuing at higher allowed densities [5, p. 56]. We attribute the increase to the speed–density relation: close to the jam density $1/a = 3.76$ m⁻², a full node lets almost no one move, and a hard capacity limit lets nodes fill up to that density until the stream locks up.
The figure repeats the experiment with this model on a ten-storey
building with one stair, comparing the two settings of
supply_reduction. With a hard limit only, the evacuation time grows
sharply above 3 m⁻². With supply reduction it stays almost constant.
This shows only that supply reduction removes the effect in this
building. The building is not the one Tsai used, so it says nothing
about EvacuatioNZ’s numbers.
Ten floors of 60 agents each (400 m² per floor, pre-movement U(30, 120) s) and one stair of 1.2 m with 0.9 m doors, all connected one way. The line is the median of 30 runs and the band covers 5–95 %. No run was incomplete.
Sensitivity to the hydraulic coefficients
HydraulicParameters lets a study change the SFPE coefficients
(Networks).
The figure changes one coefficient at a time in the one-stair building
of the max_density sweep, with the default max_density. The stair is
the bottleneck, so the evacuation time should follow its capacity: 600
agents pass its effective width of 0.9 m at the maximum specific flow
$k_\text{stair}/(4a)$, after the earliest pre-movement time of 30 s.
This bound is the dashed line.
- The stair coefficient sets $k_\text{stair}$, so the time falls as $1/k$: from 924 s at 40 m/min to 590 s at 65 m/min.
- The slope $a$ scales the maximum flow as $1/a$, so the time grows in
proportion to $a$: from 540 s at 0.19 to 726 s at 0.266. With the
default
door_specific_flowof 1.3, $a$ is at most 0.269 (Networks), so the sweep ends at the default. Below 0.182 the peak-flow density $1/(2a)$ exceeds the defaultmax_densityand supply reduction switches off, so the sweep starts at 0.19. door_specific_flowchanges nothing here: the ten doors together pass far more than the stair even at 0.7 persons/s/m. The median moves by 2 s between 0.7 and 1.3.
In every case the median lies 35 to 41 s above the bound: the start-up of the stream and the last agent’s walk down the stair. The line is the median of 30 runs and the band covers 5–95 %. No run of the sweep was incomplete. The sweep shows which coefficient this building responds to; it says nothing about which values are right. Changed coefficients are study assumptions, not validated ranges.
Reproducing the figures
The figures and animations on this site are produced by
scripts/figures/make_figures.py and scripts/figures/make_gifs.py:
uv run --group docs python scripts/figures/make_figures.py OUTDIR
uv run --group docs python scripts/figures/make_gifs.py OUTDIRWithout OUTDIR they write to site/static/images/network/. The
figures of the Terminal UI
page are the snapshots of the terminal UI tests;
scripts/figures/copy_tui_snapshots.py copies them, by default to
site/static/images/tui/:
uv run --group docs python scripts/figures/copy_tui_snapshots.py OUTDIR

