Create a scenario
A scenario is two files: config.json (exits, spawn areas, agents, journeys,
settings) and geometry.wkt (the walkable area). They sit in a folder or a
ZIP, and pyfds-evac --scenario reads either. There are four ways to get
them:
| Route | Use it when |
|---|---|
| Draw it in JuPedSim Web | You start from a plan, a CAD drawing or nothing. |
| Start from an example | A small change to a working scenario is enough. |
| Start from the FDS deck | You have an FDS or FDS+Evac deck: pyfds-evac init DECK.fds writes the scenario folder. |
| Match the FDS deck | You couple the run to FDS output. Do this in addition to one of the first two. |
Whatever the route, check the scenario before you
use its results. The keys of config.json and their defaults are on
Scenario JSON.
Draw it in JuPedSim Web
JuPedSim Web is the browser app of JuPedSim. You
draw the walkable area, obstacles, exits, spawn areas (called
distributions), checkpoints, zones and journeys. It imports geometry from
DXF (CAD) and IFC (BIM) files. Creating and saving scenarios needs a login
with Helmholtz AAI, which accepts external accounts (see the
FZJ page of the app). To run the app on your own machine, see the
Docker set-up in
jupedsim-web-community
(docker/README.md).
Draw and export
Draw the scenario and export it with Download as ZIP (JSON + WKT). The
ZIP holds config.json and geometry.wkt. Download Project ZIP adds a
README.md, run.py and requirements.txt; pyFDS-Evac reads that ZIP too.
Load the ZIP
There is no need to unzip it:
pyfds-evac --scenario scenario.zip --print-summary --export-onlyIn a source checkout, prefix the commands on this page with uv run, as in
uv run pyfds-evac or uv run python -m ….
Add the pyFDS-Evac keys last
The app writes the movement model, seed, exits, spawn areas, checkpoints,
zones, journeys and exit or checkpoint signs. It does not know pyFDS-Evac’s
route choice, exit schedules, familiarity or FDS options. Add those keys by
hand in config.json:
| Key | Where | Reference |
|---|---|---|
routing | top level | Route choice |
open_from_s, closed_after_s, capacity_agents_per_s | exits.<id> | Exits |
max_distance | sign | Signs |
familiarity, entrance | distributions.<id>.parameters | Spawn areas |
waypoint_routing | top level | Journey splits |
FDS settings (--fds-dir, the slice height and the others) are command-line
options and never go into the JSON; see Usage.
config.json from
its own state. A re-export drops routing, open_from_s, closed_after_s,
capacity_agents_per_s and the sign’s max_distance. Finish the geometry,
exits, spawn areas and journeys in the app, export, and only then add the
pyFDS-Evac keys. Keep a note or a small script of your hand edits, so you
can apply them again after the next export
(jupedsim-web-community#182).What the app writes, key by key
project_version.config.simulation_settings:simulationParams(themodel_type, the model constants,max_simulation_time,dt),numberOfSimulationsandbaseSeed. pyFDS-Evac reads neitherdtnornumberOfSimulations.config.ui_state(useShortestPaths,boundaries): layout data for the app. The run does not read it.exits.<id>:coordinates,enable_throughput_throttling,max_throughput, and an optionalsign.distributions.<id>.parameters:number,distribution_modeandpercentage, thev0*andradius*keys,use_premovementand thepremovement_*keys,use_flow_spawningwithflow_start_timeandflow_end_time; and next to the parameters,journey_weights.checkpoints.<id>:waiting_timewith its distribution and spread, the throughput keys,speed_factor(always 1) and an optionalsign.zones.<id>.speed_factor, andobstacleswith their heights.journeys: [],transitions: []andjourneys_v2, which holds each journey as a sequence of exits and checkpoints.
Signs. The app’s sign editor (Edit Sign) sets the position, the
bearing (° from north) and the contrast factor c (Jin), with the presets
Reflective (3) and Light-emitting (8). It writes sign: {x, y, alpha, c}
on exits and checkpoints. alpha is the bearing the sign faces, clockwise
from north (+y); the sign is readable only from the side it faces. The app
does not write max_distance.
Journeys. load_scenario converts journeys_v2 into journeys and
transitions (_migrate_journeys_v2 in pyfds_evac/core/scenario.py). It
does so only when journeys is empty, and only for a spawn area with exactly
one journey_weights entry. A spawn area split over several journeys is not
converted.
Journeys without transitions
A file whose journeys list stages but which has no transitions is
refused at load with an error that names each journey
(#504,
check_journey_transitions).
Older app exports have this shape, for example the bottleneck-zone example
and 24 of the 55 scenario ZIPs of jupedsim-web-community at commit
82404ee. Of those, 23 also carry journeys_v2;
standards/rimea/scenario_files/Rimea-12d-bottleneck.zip does not and needs
a re-export or hand-written transitions.
If the file also has journeys_v2, set "journeys": [] and
"transitions": [] so that journeys_v2 is converted. Otherwise export the
scenario again from the current app, or add the transitions by hand.
Open a scenario in the app again
--export-app-bundle DIR writes the scenario as the app reads it, as
config.json and geometry.wkt. Add --export-only to skip the run:
pyfds-evac --scenario scenario.zip --export-app-bundle bundle --export-onlyInitialization started.The folder bundle then holds config.json and geometry.wkt. Zip the two
files and load the ZIP in the app. The GUI writes the same bundle to
<run folder>/bundle.
What the bundle holds
_export_app_bundle in pyfds_evac/core/run_outputs.py writes the scenario JSON as
loaded and the walkable area as WKT. The command-line options, such as
--seed, are not written. Loading adds two things:
simulationParams.max_simulation_time(300 s) when it is missing;journeysandtransitionsbuilt fromjourneys_v2.
journeys_v2, ui_state and keys the app does not know are kept. The app
ignores the unknown keys and drops them on its next export, so apply your
hand edits again afterwards.
Start from an example
Each example page has a Files box with a ZIP of its inputs. Two good starting points:
- Quickstart:
assets/ISO-table21, one agent in a corridor 100 m long and 2 m wide. No FDS output and no journeys. - A crowd in a fire:
assets/t_junction, two exits with signs, aroutingblock, flow spawning, and the FDS deck.
A ZIP keeps the layout of the repository: the scenario is in
assets/<name>/config.json and assets/<name>/geometry.wkt. The steps
below use the Quickstart scenario, from a source checkout or from the
unpacked Quickstart ZIP (cd pyfds-evac-quickstart first).
Copy the scenario
cp -r assets/ISO-table21 my-scenarioEdit what you need
Open my-scenario/config.json and my-scenario/geometry.wkt in an editor.
The keys most often changed:
- Walkable area:
geometry.wkt, onePOLYGONin metres, with holes for obstacles. ISO-table21 isPOLYGON((-50 1, 50 1, 50 -1, -50 -1, -50 1)). - Exits:
exits.<id>.coordinates, a closed ring with the first point repeated at the end. - Spawn areas:
distributions.<id>.coordinates. - Number of agents:
distributions.<id>.parameters.number, withdistribution_mode: "by_number". - Seed:
config.simulation_settings.baseSeed(default 42;--seedoverrides it). - Duration:
config.simulation_settings.simulationParams.max_simulation_time(default 300 s).
Exits, spawn areas and checkpoints must lie inside the walkable area; see
How do I place checkpoints?. With
--fds-dir, max_simulation_time must not exceed the last FDS slice time by
more than one output interval, unless you pass --allow-fds-horizon-hold.
For a first try, change the number of agents from 1 to 3:
"number": 3,Run it
pyfds-evac --scenario my-scenario --print-summary --export-only
pyfds-evac --scenario my-scenarioIf the run stops with “requested 20 agents but area can hold at most ~6”
The spawn area of ISO-table21 is about 0.94 m × 1.81 m. With "number": 20
the summary still reads Agents: ~20, but the run, and --export-only,
stop with exit status 1 and print:
pyfds-evac: error: Distribution 'jps-distributions_0': requested 20 agents but area can hold at most ~6. Reduce the number of agents or enlarge the distribution area.The estimate is an upper bound. With "number": 5 the count passes the
check, but JuPedSim places only 4 agents, and the run stops with exit
status 1 and prints:
pyfds-evac: error: Distribution 'jps-distributions_0': could not place the 5 requested agents (Only 4 of 5 could be placed. density: 2.35 p/m²). The capacity estimate ~6 is an upper bound. Reduce the number of agents or enlarge the distribution area.Enlarge the polygon in distributions.<id>.coordinates, or lower number.
Check the scenario
Three checks, from weakest to strongest:
The files load.
pyfds-evac --scenario DIR --print-summary --export-onlyprints the model, seed, maximum time, the counts and the journeys. It returns before the JuPedSim set-up. A spawn area that asks for more agents than its capacity estimate stops it with the run’s error line and exit status 1. It does not catch an agent that never moves, a count within the estimate that JuPedSim cannot place, or a polygon outside the walkable area. Each spawn area is checked on its own, so it also misses spawn areas that overlap: the agents placed in one take room from the other, and the run can still stop withcould not place the N requested agents.The scenario runs. Run it without
--export-only. Set a smallmax_simulation_timefirst if the full run is long. A complete run ends withSimulation finished in … s (N/N evacuated).and exit status 0. A run in which not every agent left ends withSimulation incomplete: time limit reached …and exit status 2 (EXIT_INCOMPLETEinpyfds_evac/cli.py). Withmax_simulation_timeset to 30 in the copy above:Simulation incomplete: time limit reached after 30.00 s (0/3 evacuated, 3 remaining).In the GUI. Upload the folder’s two files or the ZIP in Core and click Add to list; see Web GUI.
Match the FDS deck
When you pass --fds-dir, the walkable area and the FDS deck must use the
same coordinate frame: x and y in metres, the same origin, no swapped axes.
The walkable area, exits, spawn areas, checkpoints and signs must lie inside
the FDS meshes and inside the slices pyFDS-Evac samples. Outside the slices,
agents read ambient air and clear sight (K = 0), as in
FDS+Evac. The
slices a deck must write are on
What your FDS case must provide.
Check the coverage
Run once with --fds-dir DIR. When everything lies inside the slices, no
coverage line is printed. In a source checkout, the ISO-table21 corridor
coupled to its tracked FDS output is such a case:
pyfds-evac --scenario assets/iso_table21_coupled \
--fds-dir assets/iso_table21_coupled/fds...
Simulation finished in 85.30 s (1/1 evacuated).When something lies outside, the set-up prints what and how much, and the
run still finishes. This excerpt is from a scenario coupled to the FDS output
of a different geometry: the bottleneck-zone example of
jupedsim-web-community (25 m × 10 m, 50 agents) with transitions added and
its zone removed, run with --fds-dir assets/iso_table21_coupled/fds and
--allow-fds-horizon-hold, because its max_simulation_time of 300 s
exceeds the 150 s of FDS output:
WARNING:pyfds_evac.core.fds_coverage:FDS coverage: outside the FDS slices (SOOT EXTINCTION COEFFICIENT), agents read ambient air and clear sight: walkable area 185.00 m² (90.2 %); exit jps-exits_0 18.00 m²; distribution jps-distributions_0 34.00 m²; sign jps-exits_0; edge jps-distributions_0 -> jps-exits_0 21.50 m. Walkable area x 0.00..25.00, y 0.00..10.00 m, FDS domain x -50.00..50.00, y -1.00..1.00 m.
...
Simulation finished in 59.21 s (50/50 evacuated).
Outside the FDS domain: 50 agent(s), 2106 sample(s), about 2106.0 agent-seconds of ambient air and clear sight.--require-fds-coverage turns the warning into an error (FdsDomainError)
and the run stops with exit status 1. Each row of the smoke and FED histories
has an in_fds_domain column. The details are on
FDS slice sampling.
Deck first: draw the walkable area in the deck’s frame
Read the extent of the meshes and obstructions from the deck. XB on
&MESH and &OBST lists x0, x1, y0, y1, z0, z1. Draw the walkable area
with these coordinates, by hand or in JuPedSim Web. If the deck was built
from a CAD plan, the app can import the same DXF file; check the coordinates
after the import against the deck.
Generating the walkable area from the deck
pyfds-evac init DECK.fds derives the walkable area from the deck and
writes it to geometry.wkt; scripts/generate_walkable_from_fds.py writes
the same polygon alone. The rule is in
Usage › What the importer derives, and
Start from your own FDS case walks through it.
The consistency between deck and WKT is tracked in
#26.Walkable area first: generate the deck geometry
wkt_to_fds writes the FDS geometry from a walkable area, in the same frame
by construction:
python -m pyfds_evac.core.wkt_to_fds geometry.wkt --geometry-only --chid bottleneck > bottleneck.fdsFor a bottleneck 25 m × 10 m:
&HEAD CHID='bottleneck', TITLE='Generated from JuPedSim walkable WKT' /
&MESH IJK=102,42,12, XB=-0.250,25.250,-0.250,10.250,0.0,3.0 /
&TIME T_END=120.0 /
&MISC TMPA=20.0 /
! --- walls: complement of the walkable area (6 OBSTs) ---
&OBST XB=-0.250,25.250,-0.250,0.000,0.0,3.0 /
...
&OBST XB=25.000,25.250,0.000,10.000,0.0,3.0 /
&TAIL /The walls are the complement of the walkable area within the mesh, closed
all round. The deck has no &VENT SURF_ID='OPEN' at the exits; add openings
where your fire scenario needs them.
Options and the fire template
The cell size is set from the smallest gap between vertex coordinates,
which in a rectilinear plan is the thinnest wall (default 0.25 m, at least
0.1 m); --dx sets it. --z-max sets the height (3 m), --meshes NX NY
splits the domain into NX × NY meshes, and --t-end sets the end time
(120 s).
Without --geometry-only, the deck also gets a placeholder burner
(--hrrpua, default 800 kW/m², on a 0.5 m square at --fire-xy) and the
extinction and CO, CO2, O2 slices at 1.6 m. This fire is a template. Replace
it with your design fire.
Sources: the module docstring and wkt_to_fds in
pyfds_evac/core/wkt_to_fds.py; tests in tests/test_wkt_to_fds.py.
Next steps
- Scenario JSON: every key that changes a result.
- Usage: the command-line options.
- What your FDS case must provide: the slices and yields a coupled run reads.
- Troubleshooting: error messages and their fixes.