jupedsim_scenarios.runner#
High-level helpers for loading and running JuPedSim web-UI scenario JSON files.
A thin scenario layer on top of the simulation primitives in utils.simulation_init and shared.direct_steering_runtime. Used by the trajectory regression test and the scripts/run_scenario.py CLI; the web runtime itself goes through services.simulation_service.
This module replaced the previous backend/core/scenario.py mirror — see the chore/drop-core-mirror PR. The longer-term plan is to migrate to jupedsim.internal.scenarios (jupedsim PR #1565) once it lands upstream.
Usage:
from scenarios import load_scenario, run_scenario
scenario = load_scenario("scenario.zip")
print(scenario.summary())
result = run_scenario(scenario)
print(result.metrics)
df = result.trajectory_dataframe()
Classes#
A loaded scenario ready for inspection and execution. |
|
Results from running a scenario. |
|
Drive a scenario tick-by-tick with inspection between steps. |
Functions#
|
Load a scenario from a ZIP archive, a directory, or a self-contained JSON file. |
|
Run a scenario to completion. Thin wrapper around |
|
Write |
Module Contents#
- class Scenario[source]#
A loaded scenario ready for inspection and execution.
- add_distribution(coordinates, *, key: str | None = None, number: int = 10, **agent_params) str[source]#
Add a spawn distribution. Returns its id.
coordinatesaccepts a shapelyPolygonor any iterable of(x, y)pairs. The polygon is automatically closed.keypicks a specific id;Noneauto-generates one asjps-distributions_{n}. Extra keyword arguments are validated through the same allow-list asset_agent_paramsso typos surface immediately.
- add_exit(coordinates, *, key: str | None = None, max_throughput: float = 0.0) str[source]#
Add an exit polygon. Returns its id.
- add_stage(coordinates, *, key: str | None = None, waiting_time: float = 0.0) str[source]#
Add a waypoint / checkpoint stage. Returns its id.
The web-UI JSON calls these “checkpoints”; the Python API uses “stage” to match jupedsim’s runtime vocabulary.
rawkeeps the JSON name, so existing web exports load unchanged.
- add_zone(coordinates, *, key: str | None = None, speed_factor: float = 1.0) str[source]#
Add a speed-modifier zone. Returns its id.
- copy() Scenario[source]#
Return an independent deep copy of this scenario.
The clone shares no mutable state with the original — mutating the clone (adding distributions, changing the seed, etc.) does not affect the source. Used internally by
run_sweepto isolate per-trial scenarios.For a copy with one or two fields changed, copy first then assign:
clone = base.copy() clone.seed = 99 clone.max_simulation_time = 60
- plot(ax=None, *, show_journeys: bool = True, trajectories=None, show_trajectories: bool | None = None)[source]#
Plot the scenario geometry with labeled distributions, exits, zones, and checkpoints.
When
show_journeysis true (default) and the scenario defines journeys, curved arrows connect the elements of each journey in route order.Pass
trajectories— aScenarioResultfrom a completed run, or apedpy.TrajectoryData— to overlay the agent paths on the geometry viapedpy.plot_trajectories(). A single call then shows the plan (exits, journeys) and what the agents actually did:result = run_scenario(scenario, seed=42) scenario.plot(trajectories=result)
show_trajectoriesforces the overlay on/off; left atNoneit is drawn whenevertrajectoriesis given.Returns the matplotlib Axes so callers can further customise the figure.
- set_agent_count(distribution_id: int | str, count: int)[source]#
Switch a distribution to by-number spawning with
countagents.Sets
number=countAND unconditionally forcesdistribution_mode="by_number"— so a distribution previously configured asby_densitywill be flipped. If you want to update the count without changing the spawning mode, callset_agent_params(id, number=count)directly.
- set_agent_params(distribution_id: int | str, **kwargs)[source]#
Set agent parameters for a distribution.
Supported keys:
radius,desired_speed,radius_distribution,radius_std,desired_speed_distribution,desired_speed_std,use_flow_spawning,flow_start_time,flow_end_time,distribution_mode,number.v0,v0_std, andv0_distributionare accepted as deprecated aliases for thedesired_speed*keys and emit aDeprecationWarning. They will be removed in a future release.
- set_checkpoint_waiting_time(checkpoint_id: int | str, waiting_time: float)[source]#
Set the waiting time for a checkpoint/stage.
- set_flow_schedule(distribution_id: int | str, schedule: list[dict], *, keep_initial_agents: bool = False)[source]#
Attach a time-windowed inflow schedule to one source distribution.
- set_model_params(**kwargs)[source]#
Set model-specific parameters (e.g. strength_neighbor_repulsion, range_neighbor_repulsion).
- to_json() str[source]#
Serialize the scenario as self-contained JSON.
Returns the JSON string. To write to disk, use
save_scenario()(mirrorsload_scenario()).The output embeds
walkable_area_wktat the top level soload_scenario()can read it back via the self-contained-JSON branch — completing the build → run → persist loop introduced by R2.1’sadd_*methods.Round-trip is value-preserving for everything the public API touches (geometry, seed, model_type, sim_params, exits, distributions, stages, zones, journeys) provided every value is JSON-serializable. The default
json.dumps()encoder is used with no fallback, so an unsupported type stuffed intoscenario.raw(orsim_params) raisesTypeErrorat save time rather than silently stringifying — surfacing the mistake before the round-trip lies about what was preserved.
- property distributions: types.MappingProxyType[str, Any][source]#
- property exits: types.MappingProxyType[str, Any][source]#
- property stages: types.MappingProxyType[str, Any][source]#
- property zones: types.MappingProxyType[str, Any][source]#
- class ScenarioResult[source]#
Results from running a scenario.
- as_pedpy_trajectory()[source]#
Return the trajectory as a
pedpy.TrajectoryData.Thin adapter so scientists doing pedpy analysis don’t have to rebuild the dataframe and look up the frame rate themselves:
result = run_scenario(scenario, seed=42) traj = result.as_pedpy_trajectory() pedpy.compute_classic_density(traj=traj, ...)
pedpy is already a hard dependency of this package (used internally for
WalkableAreaetc.) so the import is direct.
- cleanup() int[source]#
Delete the temporary SQLite trajectory file.
Returns the number of files removed (0 or 1) so callers and
SweepResult.cleanupcan report totals without re-checking.
- trajectory_dataframe()[source]#
Load trajectory data into a pandas DataFrame.
Columns: frame, id, x, y, ori_x, ori_y
- visualise(*, every_nth_frame: int | None = None, width: int = 800, height: int = 800, radius: float = 0.2, title_note: str = '', save_path: str | pathlib.Path | None = None)[source]#
Interactive plotly playback of the trajectory.
Thin wrapper around jupedsim’s own notebook animation (
jupedsim.internal.notebook_utils.animate()): agents are drawn as circles coloured by speed, with orientation arrows, a play button and a time slider. Returns aplotly.graph_objects.Figurethat renders inline in Jupyter with no extra steps — justresult.visualise()as the last line of a cell.Trajectory, frame rate and geometry are read straight from the run’s SQLite file (which carries the orientation columns this animation needs).
radiusis the drawn agent radius in metres.every_nth_framesubsamples frames so long runs stay light. Left atNone(the default) it is chosen automatically so the animation lands near_TARGET_ANIMATION_FRAMESframes: short runs play every frame, huge runs are thinned to stay responsive. Pass an explicit positive integer to override.If
save_pathis given the figure is written to disk:.htmlproduces a self-contained, interactive page; any other suffix is passed to plotly’s static image export.
- class ScenarioRunner(scenario: Scenario, *, seed: int | None = None, dt: float | None = None, every_nth_frame: int = 10, output_path: str | pathlib.Path | None = None)[source]#
Drive a scenario tick-by-tick with inspection between steps.
Matches the imperative shape of
jupedsim.Simulationso users familiar with the lower-level API feel at home:with ScenarioRunner(scenario, seed=42) as runner: runner.run_until(10.0) print(runner.elapsed_time, runner.agent_count) # mutate the simulation or scenario here, then continue: runner.run_until(20.0) result = runner.result()
To run all the way to
scenario.max_simulation_time, callrun_until()with no argument (the loop also stops early once every agent has evacuated):with ScenarioRunner(scenario, seed=42) as runner: runner.run_until() result = runner.result()
Outside a context manager you can call
close()directly; the sqlite trajectory file survivesclose()so the returnedScenarioResultkeeps working.- close() None[source]#
Close the trajectory writer and clean up the config tempfile.
Idempotent. Does NOT remove the sqlite trajectory file — call
ScenarioResult.cleanup()for that, or just keep the file.
- result() ScenarioResult[source]#
Build a
ScenarioResultreflecting the runner’s current state.Safe to call multiple times — each invocation snapshots the live metrics. The runner does NOT close the writer when
result()is called, so subsequentstep()/run_untilcalls keep appending to the same sqlite file.
- run_until(target_time: float | None = None) None[source]#
Run forward until
elapsed_time >= target_time.target_time=None(default) meansscenario.max_simulation_time— i.e. “run to completion”. Stops early once every agent has evacuated (and any flow spawning has emptied its budget).target_timeis clamped toscenario.max_simulation_timeso callers can’t drive the simulation past the scenario’s configured horizon by accident.target_time=0is accepted and is a no-op att=0.
- load_scenario(path: str) Scenario[source]#
Load a scenario from a ZIP archive, a directory, or a self-contained JSON file.
Three input shapes are supported:
Directory — contains one
*.jsonand one*.wktfile.ZIP archive — same two files packed together.
Self-contained JSON — a single
.jsonfile whose top-level object embeds the walkable geometry as"walkable_area_wkt". This is what the CLI consumes.
- run_scenario(scenario: Scenario, *, seed: int | None = None, dt: float | None = None, every_nth_frame: int = 10, output_path: str | pathlib.Path | None = None) ScenarioResult[source]#
Run a scenario to completion. Thin wrapper around
ScenarioRunner.
- save_scenario(scenario: Scenario, path: str | pathlib.Path) None[source]#
Write
scenariotopathas self-contained JSON.Side-effecting counterpart to
Scenario.to_json()(which returns the JSON as a string), mirroringload_scenario(). Parent directories are created if missing.