Scenario JSON
A scenario is a config.json plus a geometry.wkt walkable area, in a
directory or a ZIP. This page lists the JSON keys that change a result, with
their defaults. The meaning of the model parameters is on the
Models pages; this page links there rather than repeating
the equations.
The keys are read by load_scenario and the JuPedSim set-up in
simulation_init.py. A key that is not listed here is either layout data for
JuPedSim Web (config.ui_state) or not read by
the run.
Where scenario files come from
Scenario files are drawn in JuPedSim Web and
exported as a ZIP, or copied from an example and edited. The app does not
write the pyFDS-Evac keys (routing, exit schedules, familiarity, the
sign’s max_distance); add them by hand after the last export.
Create a scenario shows the three routes and how to
check the result.
Top-level structure
{
"config": { "simulation_settings": { "simulationParams": {…}, "baseSeed": … } },
"exits": { "<id>": {…} },
"distributions": { "<id>": { "coordinates": […], "parameters": {…} } },
"checkpoints": { "<id>": {…} },
"waypoints": { "<id>": {…} },
"zones": { "<id>": {…} },
"journeys": [ { "id": …, "stages": […] } ],
"transitions": [ { "from": …, "to": …, "journey_id": … } ],
"journeys_v2": [ { "id": …, "sequence": […] } ],
"waypoint_routing": {…},
"routing": {…}
}journeys and transitions are the stage graph that set-up builds on; a
journey needs its transitions
(#504).
journeys_v2, as JuPedSim Web writes it, is converted into them only when
journeys is empty; see
Create a scenario.
waypoints carry signs only.
Simulation settings
Under config.simulation_settings.
| Key | Default | Effect |
|---|---|---|
simulationParams.max_simulation_time | 300 s | The run stops here. The run also ends earlier, with end_reason all_mobile_left, when only incapacitated agents remain; see Reading the results. With --fds-dir it must not exceed the last FDS slice time by more than one output interval, unless --allow-fds-horizon-hold is given (#340). |
simulationParams.model_type | "CollisionFreeSpeedModel" | The JuPedSim movement model; see Movement models › Choosing a model. |
simulationParams.number, .radius, .v0 | 10, 0.2 m, 1.25 m/s | Deck-wide defaults for a spawn area that does not set its own number, radius or v0, with and without journeys; see Spawn areas. A deck without spawn areas places number agents in the whole walkable area. number is read with Python’s int() (3.9 and "3" give 3), radius and v0 with float(), so numeric strings are accepted; null means unset. number must be 0 or more, radius and v0 finite and greater than 0, and true or false is not a number. A value that does not convert, such as "3.9" for number or "fast" for v0, a number below 0, a radius or v0 of 0, below 0, infinite or NaN ("1e400", "nan"), or a boolean, stops the run with a ValueError that names the key. |
simulationParams.smoke_slice_height | none (--smoke-slice-height, 1.6 m) | Absolute FDS slice height [m] for smoke, gases and sign legibility. pyfds-evac --smoke-slice-height overrides it; the run prints the value when it comes from here. pyfds-evac init writes the floor level plus HUMAN_SMOKE_HEIGHT. The GUI and TUI do not read it yet. |
baseSeed | 42 | Random seed; run.py --seed overrides it. |
simulationParams.dt is not read: the JuPedSim step is 0.01 s, and the
trajectory is written every tenth step (10 frames/s).
Social force model
Read when model_type is "SocialForceModel". The symbols are those of
Helbing, Farkas & Vicsek (2000), Eqs. (1)–(3)
(doi:10.1038/35035023); a missing key takes
JuPedSim 1.4.2’s default.
Key (simulationParams.) | JuPedSim parameter | Symbol | Unit | Default |
|---|---|---|---|---|
sfm_body_force | SocialForceModel(body_force=) | k | kg s⁻² | 120000 |
sfm_friction | SocialForceModel(friction=) | κ | kg m⁻¹ s⁻¹ | always 0, see below |
agent_strength | agent_scale, per agent | A, agent term | N | 2000 |
sfm_obstacle_scale | obstacle_scale, per agent | A, wall term | N | 2000 |
agent_range | force_distance, per agent | B | m | 0.08 |
relaxation_time | reaction_time, per agent | τ | s | 0.5 |
Negative, non-finite or non-numeric sfm_body_force, sfm_friction or
sfm_obstacle_scale stop the run with ValueError. JuPedSim 1.4.2 has
one friction for the agent and wall terms and applies the wall term with
the wrong sign
(jupedsim#1677),
so friction is always passed as 0, also when sfm_friction is missing
(#635).
A deck that declares sfm_friction above 0 gets a warning. The run
manifest records what JuPedSim received under sfm.
Spawn areas: distributions.<id>.parameters
parameters is an object, or the same object as a JSON-encoded string;
both read the same everywhere: the run, --show-config, the TUI, the GUI
and the Scenario views
(#644):
"parameters": {"number": 20, "v0": 1.2}
"parameters": "{\"number\": 20, \"v0\": 1.2}"A missing or null parameters, a string that is not valid JSON, and a
string or value that is not an object ("null", "[]", "42", [])
read as no parameters, so every key takes its default. The
Scenario.set_agent_params(), set_agent_count() and
set_flow_schedule() setters store an object: a string is replaced by
the object it encodes, with all its keys, and an unreadable value by an
empty object, before the setter’s keys are written.
| Key | Default | Range | Effect |
|---|---|---|---|
number | simulationParams.number, else 10 | ≥ 0 | Agents placed at the start by number, or added over the window with use_flow_spawning. Not read, and not checked, in the percentage modes. No value is taken from another spawn area. |
distribution_mode | "by_number" | by_number, by_percentage, fill_area, until_full | by_percentage fills the polygon to percentage (default 50) of its capacity estimate: ⌊estimate × percentage / 100⌋ agents, at least 1. The estimate is ⌊A / (π r²) / 2⌋ for the spawn area A clipped to the walkable area and the largest agent radius r (radius, or mean + 3 × radius_std up to 1 m for a Gaussian radius; at least 0.1 m). number is not read. The same count applies at the start and with use_flow_spawning; --print-summary shows it. percentage is clamped to 1–100; a value that is not a number takes the default. The by_percentage count is exact: when JuPedSim cannot seat it, the run stops with a SpawnCapacityError, which near 100 % can happen. fill_area and until_full take the same count, with percentage defaulting to 100, as an upper bound: at the start they place as many agents as fit up to it, the run prints Distribution '<id>': fill_area placed N of at most M agents, and the result (ScenarioResult.fill_placement, the closing summary line) records it; --print-summary shows up to M agents, and --export-only does not report it as over-full. With or without journeys, every exact count is placed before any fill mode, whatever the deck order, and the fill modes share what is left. |
v0 | simulationParams.v0, else 1.25 m/s | finite, ≥ 0 | Clear-air walking speed, for every movement model (FDS+Evac VEL_MEAN; 1.2 before). Every smoke, irritant and zone factor multiplies this value. |
v0_distribution | "constant" | constant, gaussian | gaussian draws per agent with v0_std; draws are clipped to [0.1, 5.0] m/s. |
v0_std | none | — | Spread of the Gaussian draw. |
radius | simulationParams.radius, else 0.2 m | finite, > 0 | Body radius: packing, spawn spacing, and the radius + 0.5 m arrival distance at a checkpoint (an agent also arrives when its centre is inside the checkpoint polygon). An agent leaves at an exit when its centre enters the exit polygon or comes within 0.03 m of it. |
radius_distribution | "constant" | constant, gaussian | gaussian draws per agent with radius_std, clipped to [0.1 m, b], where b = min(max(radius + 3 × radius_std, 0.1), 1.0) m is the radius placement spaces agents for. Only agents placed at the start are drawn; flow-spawned agents take radius. |
radius_std | none | — | Spread of the Gaussian draw. |
use_premovement | constant 10 s when no pre-movement key is set, with a warning | true, false | Delay before the agent starts moving. Setting any pre-movement key, including use_premovement: false, turns the default off. |
premovement_distribution | "gamma" | gamma, lognormal, weibull, uniform, constant | Distribution of the delay. |
premovement_param_a, premovement_param_b | the preset of the distribution | — | Override the preset; the presets and their sources are in Coming from FDS+Evac. |
premovement_seed | none | — | Separate seed for the pre-movement draw. When set, the pre-movement times are the same for every run seed. |
premovement_offset_s | none | ≥ 0 s | A fixed delay added to every agent’s drawn pre-movement time, so nobody starts earlier. It stands for FDS+Evac’s detection time, and it is also the way to let the fire develop before the alarm (Pathfinder’s time offset for FDS results): the FDS clock is not shifted, agents are in the building from ignition, and while they wait the smoke update skips them, so they accrue no dose (see Spawn areas). pyfds-evac init writes it. It needs use_premovement: true, and without it the run stops with a ValueError. Applies with and without journeys; ignored with flow spawning. |
use_flow_spawning | false | — | Add agents over time instead of at the start (no pre-movement then). |
flow_start_time, flow_end_time | 0 s, 10 s | — | Window of flow spawning. A flow agent keeps twice the larger radius from every agent already in the run; while no position in the area is that free, it waits, and one still waiting when the window closes does not enter (agents_not_spawned); a run whose building then empties ends there (end_reason flows_closed). Besides that spacing, only a position JuPedSim refuses (too close to an agent or a wall, or outside the walkable area) makes the agent wait; any other error while building a flow agent, such as no valid entry stage, no exit or exit journey, or an invalid agent parameter, stops the run with an error naming the distribution, its source and the position, and is not counted in agents_not_spawned. |
flow_schedule | none | a list of windows | Flow windows that replace flow_start_time, flow_end_time and number: each {"flow_start_time", "flow_end_time", "number"} (aliases start_time_s, end_time_s, sim_count) adds number agents at evenly spaced times within its window, as written, at most one per 0.01 s time step; a window shorter than number × 0.01 s, or one whose agents per second exceed the area’s capacity estimate, stops the run. Agents of a window that the time limit cuts off count as not spawned; a run whose building empties after the last flow window with agents still to enter closed ends then (end_reason flows_closed). An entry that is not an object, a time that is not a finite number, a start below 0, an end not after the start or a number that is not a whole number ≥ 1 stops the run. Each of these errors is a SpawnConfigError (a ValueError) that names the distribution, printed by the CLI in one line; --export-only reports the same. Scenario.set_flow_schedule() writes it. |
initial_number | 0 | ≥ 0 | With flow_schedule: agents placed at the start beside the scheduled ones, checked against the area’s capacity as number is. Ignored, and not checked, without flow_schedule. |
familiarity | "full" | full, discovery, or a probability in [0, 1] | What the agents know of the exits at the start; see Models › Wayfinding. |
entrance | none | an exit id | One exit, reachable from the spawn area, that the agents know from the start. |
no_known_exit | "default_route" | default_route, explore, return, stay | What an agent does while no exit is reachable in its map. default_route (the FDS+Evac counterpart) follows the journey, or without one the nearest exit on foot, and flags leaving by an exit not in the map. explore, return and stay need rerouting and are refused with --smoke-blind; see Models › Wayfinding §2.4. |
A spawn area’s own number, radius and v0 are read as the
simulationParams values are: number with int(), radius and v0
with float(), so numeric strings are accepted, and true or false is
not a number. The bounds are the same with one exception: a spawn area’s
v0 may be 0, which places agents that stand still (the stationary FED
verification cases use it), while simulationParams.v0 must be greater
than 0, since it is the speed of every spawn area that sets none. A value
that does not convert or is out of range, such as "-1" for number or
"nan" for v0, stops the run at the start with a ValueError that
names the distribution and the key (desired_speed when the deck set that
alias). null means unset, as for the simulationParams values: the
spawn area takes the deck-wide default
(#649).
desired_speed, desired_speed_distribution and desired_speed_std are
aliases of v0, v0_distribution and v0_std. An alias set alone is used
as its v0* key. An alias and its v0* key with the same value are
accepted; with different values the run stops at the start with a
ValueError that names the distribution and both keys
(Troubleshooting). --show-config, the TUI
and the GUI report the same error before a run. This check applies to the
scenario JSON; Scenario.set_agent_params() in Python lets desired_speed
win instead
(#586).
While an agent waits out its pre-movement time, the smoke update skips it, so it starts at its full clear-air speed.
Exits: exits.<id>
| Key | Default | Effect |
|---|---|---|
coordinates | required | Exit polygon. |
enable_throughput_throttling, max_throughput | false, 0 | Cap the removal rate at the exit: an agent whose centre is inside the exit polygon, or within 0.03 m of it, is removed only if at least 1/max_throughput s have passed since the last removal there; otherwise it waits. A throttled exit is steered directly. This caps the rate; it does not model door flow. A max_throughput of 0 disables the cap. |
capacity_agents_per_s | routing.default_exit_capacity (1.3 agents/s) | Exit capacity used to estimate queue time when routes are priced. |
open_from_s | none (open from the start) | The exit opens at this time [s], a finite number ≥ 0. |
closed_after_s | none (never closes) | The exit closes at this time [s], a finite number ≥ 0 and greater than open_from_s. |
sign | an omni-directional sign at the exit’s centre, c = 3 | The sign agents read; keys below. |
An exit with open_from_s or closed_after_s is open while
open_from_s ≤ t < closed_after_s
(#373). While
it is closed:
- it removes nobody: an agent standing in it waits there;
- routes are ranked without it, both the opening choice and every re-evaluation, and it is no target for exploring or wandering. An exit is judged by whether it is open at the time of ranking, not at the time the agent would arrive;
- an agent whose route ends at it re-evaluates at the next reroute check
(every second), whatever
--reroute-intervalsays, and takes the best open exit it knows. The switch is logged with the reasonexit_closedwhen the exit has closed (closed_after_spassed). An exit that has not opened yet (open_from_sstill ahead) was never the agent’s exit: the first open exit it is given is logged asinitial. An agent that knows no open exit acts by its spawn area’sno_known_exitmode. Under the default,default_route, it walks to the nearest open exit on foot, which need not be in its map, and the switch is logged asdefault_route. Underexploreit is not told of another exit: it heads for a known node it has not visited, or else wanders over the nodes it knows; an agent that knows only its spawn area and the closed exit has neither, so it keeps its route and waits at the closed exit. With every exit closed, an agent keeps its route and waits.
When an exit opens, nobody is made to re-decide: an agent takes it at its next
regular re-evaluation, up to --reroute-interval later.
A run without rerouting (--no-enable-rerouting or --smoke-blind) honours
the schedule too
(#395). At the
same one-second check, only the agents whose route ends at a closed exit
choose again, once for each closure, scored as their opening choice is: on
the map they hold, without the queue term, with the smoke at that time, or in
clear air when the run is smoke-blind. An agent that knows no open exit takes
the default route to the nearest open exit on foot; with no exit open it waits
and is checked again every second. The route history logs the switches:
exit_closed for an exit that has closed, initial for an agent whose exit
has not opened yet and is sent to an open one it knows, and default_route
for an agent sent to an exit it does not know. Without rerouting nobody
re-decides when an exit opens.
A schedule cannot be combined with --replay-exits, and a run with a
scheduled exit stops with an error when an agent walks a JuPedSim journey
instead of a routed path (a spawn area with no journey in a scenario that has
journeys). Without a schedule nothing changes.
Signs: sign
Exits, checkpoints and waypoints can carry a sign. A node without one gets
an omni-directional sign at its position with c = 3, so every exit is
subject to smoke-dependent legibility.
| Key | Default | Effect |
|---|---|---|
x, y | required in an authored sign | Sign position [m]. |
alpha | none (omni-directional) | Bearing the sign faces [°], clockwise from north (+y); the sign is readable only from the side it faces. |
c | 3 | The constant C of the legibility law V = C/K. |
max_distance | --max-sign-distance (30 m) | Farthest reading distance for this sign, even in clear air. |
The legibility rule is on Models › Wayfinding.
Checkpoints and zones
| Key | Object | Default | Effect |
|---|---|---|---|
coordinates | both | required | Polygon. |
speed_factor | both | 1.0 | Multiplies the speed of agents inside; clipped to [0, 3]. A negative or non-numeric value becomes 1.0. |
waiting_time | checkpoint | 0 s | Time agents wait at the checkpoint. |
waiting_time_distribution, waiting_time_std | checkpoint | constant, 1.0 s | "gaussian" draws the wait per agent. |
enable_throughput_throttling, max_throughput | checkpoint | false, 1.0 | Cap the flow through the checkpoint. |
Where to draw checkpoints, how large, and how they join the route graph: How do I place checkpoints?.
Journey splits: waypoint_routing
A journey in which a checkpoint has more than one outgoing transition needs the shares of agents that take each branch:
"waypoint_routing": {
"<checkpoint id>": {
"<journey id>": {
"destinations": [ { "target": "<stage id>", "percentage": 60 }, … ]
}
}
}| Key | Default | Effect |
|---|---|---|
destinations[].target | required | A stage the journey’s transitions lead to from this checkpoint. Every such stage must be listed. |
destinations[].percentage | 0 | Share of the agents sent to target. The sum must be greater than 0. |
Without it, or with a target missing, the set-up stops with Explicit routing required for <checkpoint> in <journey> or Explicit routing for <checkpoint> in <journey> is incomplete (_create_journeys_with_percentages
in simulation_init.py). The shares set the authored journey only. With
rerouting on, the default of run.py, the route-cost model picks each
agent’s exit: in assets/t_junction with 40 agents, shares of 99/1 and 1/99
both sent all 40 to the nearer east exit, while with --no-enable-rerouting
99/1 sent all 40 west. JuPedSim Web does not write this key.
Route choice: routing
The routing block sets the route-cost model. Its keys, defaults and the
constants that no key can set are listed in one place,
Models › Routing › Parameters.
cost_model must be exactly "gate" or "additive" (lower case); without
the key it is "gate". Any other value stops the run with a ValueError
that lists the two names, also with --no-enable-rerouting, since the first
exit choice uses the cost model too.
fallback_return_lockout_s, the 10 s return lockout between two refused
routes (#458), was removed in #187. A scenario that still sets
it stops with a ValueError naming the removal, also with
--no-enable-rerouting; delete the key. path_switch_anchor, the
fraction of the walked path’s rank_cost a new path to the same exit
had to come in under, was removed in the same issue and is refused the
same way: the path is now taken whenever it is strictly cheaper.
tau_return_margin, the
factor (0.8) on tau_max that a rival exit’s optical depth had to come in
under, was removed in the same issue and is refused the same way: every
exit is now refused above tau_max. fed_return_margin, the
fraction of fed_rejection_threshold above which a rival exit was
refused for dose, was removed in the same issue and is refused the same
way: a rival is now held to fed_rejection_threshold, as the current
exit is.
Each switching margin must be a number in its range, or the run stops with
a ValueError naming the key, also with --no-enable-rerouting:
exit_switch_anchor,
current_exit_discount and clean_exit_margin in
(0, 1]; tau_deadband and fallback_switch_margin in [0, 1]. The
value that switches a margin off is in range: 1, or 0 for tau_deadband
and fallback_switch_margin. fallback_switch_margin = 1 keeps the
current exit between two refused routes unless it must be fled, the
scenario-file way to the hold rule.
The routing keys alpha, beta, min_speed_factor and
base_speed_m_per_s only estimate travel time when a route is priced. They
do not change how fast agents walk.
fed_rate_memo (default true) remembers the FED rate a route reads at a
point for the run; false reads the model every time. It changes run time
only; see Segment caching. A value that is
not true or false stops the run with a ValueError naming the key.
Not in the JSON
Some parameters exist only in Python, as fields of a config object:
- the smoke-speed law and its parameters (
SmokeSpeedConfig:speed_law,alpha,beta,min_speed_factor,visibility_factor_c); a configured run always uses the Lund law with the defaults on Models › Smoke speed; RerouteConfig.exit_switch_anchor, which, when set, overrides theroutingkeyexit_switch_anchorfor that run.
Tracking issue for this reference: #119.