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": {…}, "journey_weights": […] } },
  "checkpoints":   { "<id>": {…} },
  "waypoints":     { "<id>": {…} },
  "zones":         { "<id>": {…} },
  "journeys_v2":   [ { "id": …, "sequence": […] } ],
  "routing":       {…}
}

waypoints carry signs only. The older journey keys (journeys, transitions, waypoint_routing) are still read; see Old format (still read).

Journeys: journeys_v2

journeys_v2 is the journey format of JuPedSim Web and of pyFDS-Evac (#900). A scenario written by pyFDS-Evac opens in the app, and a scenario drawn in the app runs here with its journeys as the agents’ starting routes. The file format is shared; how the agents walk (route choice, rerouting, smoke, FED) is pyFDS-Evac’s own.

"journeys_v2": [
  {"id": "journey-a", "name": "Via the corridor", "color": "#4e79a7",
   "sequence": ["jps-checkpoints_0", "jps-exits_0"]}
],
"distributions": {
  "jps-distributions_0": {
    "coordinates": [[0, 0], [4, 0], [4, 4], [0, 4]],
    "parameters": {"number": 20},
    "journey_weights": [{"journey_id": "journey-a", "weight": 100}]
  }
}
KeyWhereDefaultEffect
id, name, colorjourneys_v2[]id requiredid is what journey_weights names. name and color are for the app.
sequencejourneys_v2[]requiredCheckpoint and exit ids in walking order, without the start area. An entry without an id or with an empty sequence is ignored.
journey_weights[].journey_id, .weightdistributions.<id>noneThe journeys a start area uses and their weights. The weights of one start area are normalised to shares.
loopjourneys_v2[]falsepyFDS-Evac key, unknown to the app. With true, the sequence repeats: after its last stage the agent walks back to the first and goes on. Use it for a patrol that never ends.
allocationjourneys_v2[]"preference"See allocation.

Weights. A start area’s agents are split over its journeys at spawn by exact counts, not by random draws: each journey gets its rounded share and the last one takes the rest. A weight that cannot be read as a number, or is 0 or less, is skipped. A start area with no usable weight, and a start area without journey_weights, has no journey: at spawn its agents walk to the nearest exit by walking distance.

The weights set each agent’s starting journey and the split of the counts at spawn. With rerouting on, the default of run.py, the route-cost model picks each agent’s exit, and the shares need not carry over to the exits taken; only --no-enable-rerouting keeps the drawn exits. See Journey splits and Models › Routing.

Several journeys per start area. Give a start area one journey_weights entry for each journey it uses. Two journeys with weights 60 and 40 send 60 % of the agents along the first sequence and 40 % along the second. A route that splits at a checkpoint is written as two journeys that share their first stages.

Journeys: allocation

KeyDefaultValuesEffect
journeys_v2[].allocation"preference""preference", "hard""preference": when the journey’s spawn area reaches one exit and the deck has at least two, the agent keeps that exit unless another exit with an unobstructed direct route from the spawn area costs less than exit_switch_anchor (0.9) times the allocated route. "hard": the agent walks to the journey’s exit, as before. Any other value raises ValueError naming the journey.

One hard journey through a spawn area makes the whole area hard. The rule is on Models › Routing. In the old format the key sits on journeys[].

Simulation settings

Under config.simulation_settings.

KeyDefaultEffect
simulationParams.max_simulation_time300 sThe run stops here. The run also ends earlier, with end_reason all_mobile_left, when only incapacitated agents remain, or with deadlock when every mobile agent has stood still for 100 s; 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, .v010, 0.2 m, 1.25 m/sDeck-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_heightnone (--smoke-slice-height, 1.6 m)Absolute FDS slice height [m] for smoke, gases and sign legibility. It is not relative to the floor, so sloped floors and stages are sampled at the wrong height above the feet (Limitations). 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.
baseSeed42Random seed; run.py --seed overrides it.

simulationParams.dt is the JuPedSim time step in seconds (default 0.01). It must divide 0.1 s (for example 0.1, 0.05, 0.025, 0.02, 0.01 or 0.005); 0, a negative value, NaN, infinity and a value that is not a number are rejected with an error that names simulationParams.dt. The trajectory keeps 10 frames/s whatever dt is, and Result.dt and the run manifest report the step. Exit times and other schedules take effect at the first step at or after their time, so up to one dt late. The Social Force and generalized centrifugal force models are untested above 0.01 s and log a warning there.

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 parameterSymbolUnitDefault
sfm_body_forceSocialForceModel(body_force=)kkg s⁻²120000
sfm_frictionSocialForceModel(friction=)κkg m⁻¹ s⁻¹always 0, see below
agent_strengthagent_scale, per agentA, agent termN2000
sfm_obstacle_scaleobstacle_scale, per agentA, wall termN2000
agent_rangeforce_distance, per agentBm0.08
relaxation_timereaction_time, per agentτs0.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.

KeyDefaultRangeEffect
numbersimulationParams.number, else 10≥ 0Agents 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_fullby_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.
v0simulationParams.v0, else 1.25 m/sfinite, ≥ 0Clear-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, gaussiangaussian draws per agent with v0_std; draws are clipped to [0.1, 5.0] m/s.
v0_stdnone—Spread of the Gaussian draw.
radiussimulationParams.radius, else 0.2 mfinite, > 0Body 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, gaussiangaussian 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_stdnone—Spread of the Gaussian draw.
use_premovementconstant 10 s when no pre-movement key is set, with a warningtrue, falseDelay 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, constantDistribution of the delay.
premovement_param_a, premovement_param_bthe preset of the distribution—Override the preset; the presets and their sources are in Coming from FDS+Evac.
premovement_presetnoneone of the 16 cvm2_* namesA constant pre-travel time from C/VM2 Table 3.3 (list). Cannot be combined with premovement_distribution, premovement_param_a/b or use_premovement: false. premovement_offset_s adds detection and notification.
premovement_seednone—Separate seed for the pre-movement draw. When set, the pre-movement times are the same for every run seed.
premovement_offset_snone≥ 0 sA 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_spawningfalse—Add agents over time instead of at the start (no pre-movement then).
flow_start_time, flow_end_time0 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_schedulenonea list of windowsFlow 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 time step (simulationParams.dt, 0.01 s by default); a window shorter than number × dt, 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_number0≥ 0With 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.
entrancenonean exit idOne exit, reachable from the spawn area, that the agents know from the start.
no_known_exit"default_route"default_route, explore, return, stayWhat 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>

KeyDefaultEffect
coordinatesrequiredExit polygon.
enable_throughput_throttling, max_throughputfalse, 0Cap 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_srouting.default_exit_capacity (1.3 agents/s)Exit capacity used to estimate queue time when routes are priced.
open_from_snone (open from the start)The exit opens at this time [s], a finite number ≥ 0.
closed_after_snone (never closes)The exit closes at this time [s], a finite number ≥ 0 and greater than open_from_s.
signan omni-directional sign at the exit’s centre, c = 3The sign agents read; keys below. false: no sign.

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-interval says, and takes the best open exit it knows. The switch is logged with the reason exit_closed when the exit has closed (closed_after_s passed). An exit that has not opened yet (open_from_s still ahead) was never the agent’s exit: the first open exit it is given is logged as initial. An agent that knows no open exit acts by its spawn area’s no_known_exit mode. 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 as default_route. Under explore it 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.

"sign": false gives the node no sign, for an exit nobody can see from the room, the case the Station validation needs (Fahy); assets/station_fahy does not set it yet. A discovery agent then never learns that exit or checkpoint by sight or by reaching a neighbouring node, with or without a visibility model; it knows it only through "full" familiarity, the familiarity draw or the distribution’s entrance (#376). An agent that arrives on the node while walking a route it knew learns it as before. null or no sign keeps the default sign, and false is the only boolean value that is read (true is not valid). An agent that knows no exit under the default no_known_exit mode "default_route" still walks its default route, which may end at an unsigned exit; "explore" avoids that. If every way out of a spawn area runs through unsigned nodes, its explore, return and stay agents that have no entrance and no familiarity can learn no exit and stay where they spawned; the run warns once per such distribution.

KeyDefaultEffect
x, yrequired in an authored signSign position [m].
alphanone (omni-directional)Bearing the sign faces [°], clockwise from north (+y); the sign is readable only from the side it faces.
c3The 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

KeyObjectDefaultEffect
coordinatesbothrequiredPolygon.
speed_factorboth1.0Multiplies the speed of agents inside; clipped to [0, 3]. A negative or non-numeric value becomes 1.0.
waiting_timecheckpoint0 sTime agents wait at the checkpoint.
waiting_time_distribution, waiting_time_stdcheckpointconstant, 1.0 s"gaussian" draws the wait per agent.
enable_throughput_throttling, max_throughputcheckpointfalse, 1.0Cap the flow through the checkpoint.

Where to draw checkpoints, how large, and how they join the route graph: How do I place checkpoints?.

Old format (still read)

Files written before journeys_v2 carry journeys (the stages of each journey), transitions (the from → to pairs) and, for a split, a top-level waypoint_routing. They load and run as before. The loader reads journeys_v2 only when journeys is empty: a file that has both runs on journeys. A journey in journeys needs its transitions (#504).

pyfds-evac-convert rewrites such a file as journeys_v2:

pyfds-evac-convert config.json -o config_v2.json

Each route a start area walks becomes one journeys_v2 entry, listed in that start area’s journey_weights with weights scaled to sum to 100 where that keeps the split unchanged. Exit shares at a checkpoint become one weighted entry per branch, and a patrol that returns to its first checkpoint becomes "loop": true. A start area whose journey leaves the exit to the route choice gets no journey, and the command prints a note for it. The command stops with error: … and exit code 1 when a loop returns to a stage other than the first, when waypoint_routing cannot be written as separate branches, or when the file has both journeys and journeys_v2.

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 }, … ]
    }
  }
}
KeyDefaultEffect
destinations[].targetrequiredA stage the journey’s transitions lead to from this checkpoint. Every such stage must be listed.
destinations[].percentage0Share 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. In journeys_v2 a split is two journeys with weights.

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. impassable_extinction_threshold, the route-average extinction above which the additive model fled a non-visible route regardless of the exit-switch anchor, was removed in #835 (part of #187) and is refused the same way: smoke never bypasses the anchor, only a FED violation does.

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 the routing key exit_switch_anchor for that run.

Tracking issue for this reference: #119.

Last updated on