How do I drive a scenario tick-by-tick and inspect it mid-run?#
run_scenario(...) is fire-and-forget: it builds a simulation, runs
to completion, and hands back a ScenarioResult. You can’t peek
between steps or change anything once it’s going.
ScenarioRunner is the interactive shape of the same loop — run a
bit, inspect, mutate the underlying simulation, then keep going.
Same physics, same trajectory writer, just under your control.
import logging
from datetime import datetime
logging.getLogger("jupedsim_scenarios").setLevel(logging.WARNING)
print(f"Executed on {datetime.now().strftime('%d.%m.%Y, %H:%M')}")
from jupedsim_scenarios import ScenarioRunner, load_scenario
scenario = load_scenario("../assets/bottleneck.zip")
Executed on 21.07.2026, 09:45
Run in chunks and inspect between#
run_until(t) advances the simulation until
elapsed_time >= t. Between calls you can read elapsed_time,
agent_count, iterate over agents(), or reach into the underlying
jupedsim.Simulation via runner.simulation for anything the
wrapper doesn’t expose.
with ScenarioRunner(scenario, seed=42) as runner:
runner.run_until(5.0)
print(f"t={runner.elapsed_time:5.2f}s agents={runner.agent_count}")
runner.run_until(15.0)
print(f"t={runner.elapsed_time:5.2f}s agents={runner.agent_count}")
runner.run_until(30.0)
print(f"t={runner.elapsed_time:5.2f}s agents={runner.agent_count}")
t= 5.00s agents=50
t=15.00s agents=40
t=30.00s agents=25
Run to completion#
run_until() with no argument runs to
scenario.max_simulation_time (or until every agent evacuates,
whichever comes first). Calling .result() at any time snapshots
the live metrics — the writer stays open, so you can keep stepping
afterwards.
with ScenarioRunner(scenario, seed=42) as runner:
runner.run_until()
result = runner.result()
print("success: ", result.success)
print("evacuation time:", result.evacuation_time, "s")
print("agents: ", result.total_agents)
result.cleanup()
success: True
evacuation time: 53.18 s
agents: 50
1
Partial trajectory mid-run#
.result() is safe to call mid-simulation — it builds a snapshot
from whatever’s currently in the trajectory writer. Handy for
feeding the data through pedpy before deciding whether to
continue.
with ScenarioRunner(scenario, seed=42) as runner:
runner.run_until(10.0)
partial = runner.result()
traj = partial.as_pedpy_trajectory()
print(f"frames captured: {len(traj.data['frame'].unique())}")
print(f"agents tracked: {len(traj.data['id'].unique())}")
partial.cleanup()
frames captured: 101
agents tracked: 50
1
When to reach for ScenarioRunner instead of run_scenario#
You want to inspect state between steps (debugging, custom stopping criteria, visualising intermediate frames).
You want to drive the simulation past a checkpoint with custom logic — e.g. pause when the queue at an exit reaches some threshold, then change the world via
runner.simulation.You want a partial result for analysis without waiting for the full run.
If you just want “run it and give me the result”, run_scenario
stays the shorter call.