Working with JuPedSim Web-UI Scenarios in Python#

Load scenario JSON files exported from the web UI and run them programmatically.

Workflow:

  1. Design your scenario in the web UI (geometry, exits, distributions, etc.)

  2. Export the scenario as ZIP

  3. Load it here, inspect/modify, run, and analyze

import logging
from datetime import datetime

# Silence jupedsim-scenarios' INFO/DEBUG output. See the howto
# "How do I inspect a scenario?" for the full list of levels.
logging.getLogger("jupedsim_scenarios").setLevel(logging.WARNING)

print(f"Executed on {datetime.now().strftime('%d.%m.%Y, %H:%M')}")

from jupedsim_scenarios import load_scenario, run_scenario, run_sweep
Executed on 21.07.2026, 09:41

1. Load and inspect a scenario#

scenario = load_scenario("scenario_files/template-scenario.zip")
print(scenario.summary())
Scenario: /home/runner/work/jupedsim-scenarios/jupedsim-scenarios/docs/source/notebooks/cookbook/scenario_files/template-scenario.zip
  Model:         CollisionFreeSpeedModel
  Seed:          420
  Max time:      300s
  Exits:         1
  Distributions: 1
  Stages:        0
  Zones:         0
  Journeys:      0
  Agents:        ~20
    jps-distributions_0: 20 agents

2. Modify parameters before running#

scenario.set_agent_count(0, 20)
scenario.max_simulation_time = 60
print(scenario.summary())
Scenario: /home/runner/work/jupedsim-scenarios/jupedsim-scenarios/docs/source/notebooks/cookbook/scenario_files/template-scenario.zip
  Model:         CollisionFreeSpeedModel
  Seed:          420
  Max time:      60s
  Exits:         1
  Distributions: 1
  Stages:        0
  Zones:         0
  Journeys:      0
  Agents:        ~20
    jps-distributions_0: 20 agents

3. Run the simulation#

result = run_scenario(scenario)

print(f"Success:          {result.success}")
print(f"Evacuation time:  {result.evacuation_time:.2f}s")
print(f"Total agents:     {result.total_agents}")
print(f"Evacuated:        {result.agents_evacuated}")
print(f"Remaining:        {result.agents_remaining}")
Success:          True
Evacuation time:  31.54s
Total agents:     20
Evacuated:        20
Remaining:        0

4. Analyze trajectory data#

df = result.trajectory_dataframe()
print(f"Trajectory: {len(df)} rows, {df['id'].nunique()} agents, {df['frame'].nunique()} frames")
df.head(10)
Trajectory: 5103 rows, 20 agents, 316 frames
frame id x y ori_x ori_y
0 0 1 -14.300273 -0.496209 0.0 0.0
1 0 2 -14.392734 -3.765182 0.0 0.0
2 0 3 -13.250484 1.182421 0.0 0.0
3 0 4 -14.230214 -4.158393 0.0 0.0
4 0 5 -13.709934 0.887278 0.0 0.0
5 0 6 -14.449449 -2.563665 0.0 0.0
6 0 7 -13.428694 -4.395289 0.0 0.0
7 0 8 -13.441598 3.403408 0.0 0.0
8 0 9 -13.422521 -2.653949 0.0 0.0
9 0 10 -13.455260 -0.606839 0.0 0.0

5. Plot trajectories#

import pedpy

traj = pedpy.TrajectoryData(df, frame_rate=result.frame_rate)
walkable_area = pedpy.WalkableArea(scenario.walkable_polygon)

pedpy.plot_trajectories(
    walkable_area=walkable_area,
    traj=traj,
).set_title("Agent trajectories")
Text(0.5, 1.0, 'Agent trajectories')
../../_images/4023596b8b6ecafda53501c731ea25b2da49c179bdd73147dc62b0ed707924f8.png

6. Parameter sweep — compare seeds#

# Seed-only sweep — run_sweep handles per-trial .copy()/.cleanup()
# bookkeeping and dispatches the trials in parallel.
seeds = [1, 2, 3, 4, 5]
sweep = run_sweep(scenario, seeds=seeds, workers=4)
evac_times = sweep.to_dataframe()["evacuation_time"].tolist()
sweep.cleanup()

for s, t in zip(seeds, evac_times, strict=False):
    print(f"  Seed {s}: {t:.2f}s")

print(f"\nMean: {sum(evac_times)/len(evac_times):.2f}s, "
      f"Min: {min(evac_times):.2f}s, Max: {max(evac_times):.2f}s")
INFO - Using fallback logic: No journeys defined
INFO - Processing with parameters: {'number': 20, 'radius': 0.2, 'v0': 1.3, 'distribution_mode': 'by_number', 'percentage': None, 'use_flow_spawning': False, 'flow_start_time': 0, 'flow_end_time': 10, 'use_premovement': False, 'premovement_distribution': 'gamma', 'premovement_param_a': None, 'premovement_param_b': None, 'premovement_seed': None, 'radius_distribution': 'constant', 'v0_distribution': 'constant'}
INFO - Using default parameters: v0=1.3, radius=0.2, n_agents=20
INFO - Distribution jps-distributions_0: {'number': 20, 'radius': 0.2, 'v0': 1.3, 'distribution_mode': 'by_number', 'percentage': None, 'use_flow_spawning': False, 'flow_start_time': 0, 'flow_end_time': 10, 'strict_spawning': False, 'use_premovement': False, 'premovement_distribution': 'gamma', 'premovement_param_a': None, 'premovement_param_b': None, 'premovement_seed': None, 'radius_distribution': 'constant', 'radius_std': None, 'v0_distribution': 'constant', 'v0_std': None}
INFO - Added 20 agents using fallback logic (immediate), prepared 0 flow sources
INFO - Using fallback logic: No journeys defined
INFO - Processing with parameters: {'number': 20, 'radius': 0.2, 'v0': 1.3, 'distribution_mode': 'by_number', 'percentage': None, 'use_flow_spawning': False, 'flow_start_time': 0, 'flow_end_time': 10, 'use_premovement': False, 'premovement_distribution': 'gamma', 'premovement_param_a': None, 'premovement_param_b': None, 'premovement_seed': None, 'radius_distribution': 'constant', 'v0_distribution': 'constant'}
INFO - Using default parameters: v0=1.3, radius=0.2, n_agents=20
INFO - Distribution jps-distributions_0: {'number': 20, 'radius': 0.2, 'v0': 1.3, 'distribution_mode': 'by_number', 'percentage': None, 'use_flow_spawning': False, 'flow_start_time': 0, 'flow_end_time': 10, 'strict_spawning': False, 'use_premovement': False, 'premovement_distribution': 'gamma', 'premovement_param_a': None, 'premovement_param_b': None, 'premovement_seed': None, 'radius_distribution': 'constant', 'radius_std': None, 'v0_distribution': 'constant', 'v0_std': None}
INFO - Added 20 agents using fallback logic (immediate), prepared 0 flow sources
INFO - Using fallback logic: No journeys defined
INFO - Processing with parameters: {'number': 20, 'radius': 0.2, 'v0': 1.3, 'distribution_mode': 'by_number', 'percentage': None, 'use_flow_spawning': False, 'flow_start_time': 0, 'flow_end_time': 10, 'use_premovement': False, 'premovement_distribution': 'gamma', 'premovement_param_a': None, 'premovement_param_b': None, 'premovement_seed': None, 'radius_distribution': 'constant', 'v0_distribution': 'constant'}
INFO - Using default parameters: v0=1.3, radius=0.2, n_agents=20
INFO - Distribution jps-distributions_0: {'number': 20, 'radius': 0.2, 'v0': 1.3, 'distribution_mode': 'by_number', 'percentage': None, 'use_flow_spawning': False, 'flow_start_time': 0, 'flow_end_time': 10, 'strict_spawning': False, 'use_premovement': False, 'premovement_distribution': 'gamma', 'premovement_param_a': None, 'premovement_param_b': None, 'premovement_seed': None, 'radius_distribution': 'constant', 'radius_std': None, 'v0_distribution': 'constant', 'v0_std': None}
INFO - Using fallback logic: No journeys defined
INFO - Processing with parameters: {'number': 20, 'radius': 0.2, 'v0': 1.3, 'distribution_mode': 'by_number', 'percentage': None, 'use_flow_spawning': False, 'flow_start_time': 0, 'flow_end_time': 10, 'use_premovement': False, 'premovement_distribution': 'gamma', 'premovement_param_a': None, 'premovement_param_b': None, 'premovement_seed': None, 'radius_distribution': 'constant', 'v0_distribution': 'constant'}
INFO - Using default parameters: v0=1.3, radius=0.2, n_agents=20
INFO - Distribution jps-distributions_0: {'number': 20, 'radius': 0.2, 'v0': 1.3, 'distribution_mode': 'by_number', 'percentage': None, 'use_flow_spawning': False, 'flow_start_time': 0, 'flow_end_time': 10, 'strict_spawning': False, 'use_premovement': False, 'premovement_distribution': 'gamma', 'premovement_param_a': None, 'premovement_param_b': None, 'premovement_seed': None, 'radius_distribution': 'constant', 'radius_std': None, 'v0_distribution': 'constant', 'v0_std': None}
INFO - Added 20 agents using fallback logic (immediate), prepared 0 flow sources
INFO - Added 20 agents using fallback logic (immediate), prepared 0 flow sources
INFO - Using fallback logic: No journeys defined
INFO - Processing with parameters: {'number': 20, 'radius': 0.2, 'v0': 1.3, 'distribution_mode': 'by_number', 'percentage': None, 'use_flow_spawning': False, 'flow_start_time': 0, 'flow_end_time': 10, 'use_premovement': False, 'premovement_distribution': 'gamma', 'premovement_param_a': None, 'premovement_param_b': None, 'premovement_seed': None, 'radius_distribution': 'constant', 'v0_distribution': 'constant'}
INFO - Using default parameters: v0=1.3, radius=0.2, n_agents=20
INFO - Distribution jps-distributions_0: {'number': 20, 'radius': 0.2, 'v0': 1.3, 'distribution_mode': 'by_number', 'percentage': None, 'use_flow_spawning': False, 'flow_start_time': 0, 'flow_end_time': 10, 'strict_spawning': False, 'use_premovement': False, 'premovement_distribution': 'gamma', 'premovement_param_a': None, 'premovement_param_b': None, 'premovement_seed': None, 'radius_distribution': 'constant', 'radius_std': None, 'v0_distribution': 'constant', 'v0_std': None}
INFO - Added 20 agents using fallback logic (immediate), prepared 0 flow sources
  Seed 1: 31.64s
  Seed 2: 30.29s
  Seed 3: 30.29s
  Seed 4: 29.52s
  Seed 5: 30.28s

Mean: 30.40s, Min: 29.52s, Max: 31.64s

7. Parameter sweep — compare agent counts#

import matplotlib.pyplot as plt

base_scenario = load_scenario("scenario_files/template-scenario.zip")
counts = [5, 10, 20, 30]

# Sweep the agent-count axis via run_sweep — parallel trials, no
# manual .copy() / .cleanup() bookkeeping.
sweep = run_sweep(
    base_scenario,
    axes={"n": counts},
    apply={"n": lambda s, v: s.set_agent_count(0, v)},
    workers=4,
)
df = sweep.to_dataframe()
sweep.cleanup()

results_by_count = dict(zip(df["n"], df["evacuation_time"], strict=False))
for n, t in results_by_count.items():
    print(f"{n} agents: {t:.2f}s")

times = [results_by_count[n] for n in counts]
INFO - Using fallback logic: No journeys defined
INFO - Using fallback logic: No journeys defined
INFO - Using fallback logic: No journeys defined
INFO - Processing with parameters: {'number': 30, 'radius': 0.2, 'v0': 1.3, 'distribution_mode': 'by_number', 'percentage': None, 'use_flow_spawning': False, 'flow_start_time': 0, 'flow_end_time': 10, 'use_premovement': False, 'premovement_distribution': 'gamma', 'premovement_param_a': None, 'premovement_param_b': None, 'premovement_seed': None, 'radius_distribution': 'constant', 'v0_distribution': 'constant'}
INFO - Processing with parameters: {'number': 10, 'radius': 0.2, 'v0': 1.3, 'distribution_mode': 'by_number', 'percentage': None, 'use_flow_spawning': False, 'flow_start_time': 0, 'flow_end_time': 10, 'use_premovement': False, 'premovement_distribution': 'gamma', 'premovement_param_a': None, 'premovement_param_b': None, 'premovement_seed': None, 'radius_distribution': 'constant', 'v0_distribution': 'constant'}
INFO - Using default parameters: v0=1.3, radius=0.2, n_agents=30
INFO - Using default parameters: v0=1.3, radius=0.2, n_agents=10
INFO - Using fallback logic: No journeys defined
INFO - Processing with parameters: {'number': 20, 'radius': 0.2, 'v0': 1.3, 'distribution_mode': 'by_number', 'percentage': None, 'use_flow_spawning': False, 'flow_start_time': 0, 'flow_end_time': 10, 'use_premovement': False, 'premovement_distribution': 'gamma', 'premovement_param_a': None, 'premovement_param_b': None, 'premovement_seed': None, 'radius_distribution': 'constant', 'v0_distribution': 'constant'}
INFO - Using default parameters: v0=1.3, radius=0.2, n_agents=20
INFO - Distribution jps-distributions_0: {'number': 30, 'radius': 0.2, 'v0': 1.3, 'distribution_mode': 'by_number', 'percentage': None, 'use_flow_spawning': False, 'flow_start_time': 0, 'flow_end_time': 10, 'strict_spawning': False, 'use_premovement': False, 'premovement_distribution': 'gamma', 'premovement_param_a': None, 'premovement_param_b': None, 'premovement_seed': None, 'radius_distribution': 'constant', 'radius_std': None, 'v0_distribution': 'constant', 'v0_std': None}
INFO - Distribution jps-distributions_0: {'number': 10, 'radius': 0.2, 'v0': 1.3, 'distribution_mode': 'by_number', 'percentage': None, 'use_flow_spawning': False, 'flow_start_time': 0, 'flow_end_time': 10, 'strict_spawning': False, 'use_premovement': False, 'premovement_distribution': 'gamma', 'premovement_param_a': None, 'premovement_param_b': None, 'premovement_seed': None, 'radius_distribution': 'constant', 'radius_std': None, 'v0_distribution': 'constant', 'v0_std': None}
INFO - Processing with parameters: {'number': 5, 'radius': 0.2, 'v0': 1.3, 'distribution_mode': 'by_number', 'percentage': None, 'use_flow_spawning': False, 'flow_start_time': 0, 'flow_end_time': 10, 'use_premovement': False, 'premovement_distribution': 'gamma', 'premovement_param_a': None, 'premovement_param_b': None, 'premovement_seed': None, 'radius_distribution': 'constant', 'v0_distribution': 'constant'}
INFO - Using default parameters: v0=1.3, radius=0.2, n_agents=5
INFO - Distribution jps-distributions_0: {'number': 20, 'radius': 0.2, 'v0': 1.3, 'distribution_mode': 'by_number', 'percentage': None, 'use_flow_spawning': False, 'flow_start_time': 0, 'flow_end_time': 10, 'strict_spawning': False, 'use_premovement': False, 'premovement_distribution': 'gamma', 'premovement_param_a': None, 'premovement_param_b': None, 'premovement_seed': None, 'radius_distribution': 'constant', 'radius_std': None, 'v0_distribution': 'constant', 'v0_std': None}
INFO - Distribution jps-distributions_0: {'number': 5, 'radius': 0.2, 'v0': 1.3, 'distribution_mode': 'by_number', 'percentage': None, 'use_flow_spawning': False, 'flow_start_time': 0, 'flow_end_time': 10, 'strict_spawning': False, 'use_premovement': False, 'premovement_distribution': 'gamma', 'premovement_param_a': None, 'premovement_param_b': None, 'premovement_seed': None, 'radius_distribution': 'constant', 'radius_std': None, 'v0_distribution': 'constant', 'v0_std': None}
INFO - Added 5 agents using fallback logic (immediate), prepared 0 flow sources
INFO - Added 10 agents using fallback logic (immediate), prepared 0 flow sources
INFO - Added 20 agents using fallback logic (immediate), prepared 0 flow sources
INFO - Added 30 agents using fallback logic (immediate), prepared 0 flow sources
5 agents: 24.25s
10 agents: 26.69s
20 agents: 31.54s
30 agents: 33.97s

8. Plot results#

fig, ax = plt.subplots(figsize=(7, 4.5))

bars = ax.bar(
    [str(n) for n in counts],
    times,
    color="#4C72B0",
    width=0.6
)

ax.set_xlabel("Number of Agents", fontsize=12)
ax.set_ylabel("Evacuation Time (s)", fontsize=12)
ax.set_title("Evacuation Time vs. Agent Count", fontsize=13, pad=10)

ax.tick_params(axis="both", labelsize=11)

ax.grid(axis="y", linestyle="--", alpha=0.4)

# cleaner axes
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)

for bar in bars:
    height = bar.get_height()
    ax.text(
        bar.get_x() + bar.get_width() / 2,
        height,
        f"{height:.1f}",
        ha="center",
        va="bottom",
        fontsize=10
    )

plt.tight_layout()
plt.show()
INFO - Using categorical units to plot a list of strings that are all parsable as floats or dates. If these strings should be plotted as numbers, cast to the appropriate data type before plotting.
INFO - Using categorical units to plot a list of strings that are all parsable as floats or dates. If these strings should be plotted as numbers, cast to the appropriate data type before plotting.
../../_images/a3044c5b7386dd0d6d63384f3d8d3cbeed71e8032f521ccb6a9e1809b3ea4b21.png

Cleanup#

result.cleanup()
print("Done.")
Done.