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.