Skip to content

Recording

The recording layer captures a per-step history of where every vehicle is, for later animation. It is deliberately matplotlib-free (it is imported by the core), so recording a run adds no plotting dependency; rendering the history to a video lives separately under Visualizations.

Enable it with Network.compile(record_history=True); the result is a SimulationHistory on Simulation.history (JSON-serialisable via save/load). See Movement animations.

History container

SimulationHistory dataclass

SimulationHistory(time_step: float, node_positions: dict = dict(), link_endpoints: dict = dict(), connector_nodes: dict = dict(), frames: list[Frame] = list())

A recorded run: static geometry plus one :class:Frame per step.

Produced by a simulation compiled with record_history=True (exposed as Simulation.history) and consumed by :mod:mesoltm.visualizations.animation to render frames/video. It is self-contained — geometry is captured alongside the frames — so :meth:save/:meth:load let the video be generated later, without the live simulation.

Attributes:

Name Type Description
time_step float

Simulation step dt in seconds.

node_positions dict

node_id -> (x, y) | None.

link_endpoints dict

real link_id -> (u, v).

connector_nodes dict

connector link_id -> node_id it is attached to.

frames list[Frame]

One :class:Frame per captured step, in order.

from_state classmethod

from_state(state: NetworkState) -> SimulationHistory

Create an empty history, capturing the geometry from state.

to_dict

to_dict() -> dict

Return a JSON-serialisable dict (ids stringified as dict keys).

from_dict classmethod

from_dict(data: dict) -> SimulationHistory

Rebuild a history from :meth:to_dict output (ids become strings).

save

save(path: str) -> str

Write the history to path as JSON and return the path.

load classmethod

load(path: str) -> SimulationHistory

Read a history written by :meth:save.

Frame and snapshots

Frame dataclass

Frame(step: int, time: float, agents: list[AgentSnapshot], waiting: list[WaitingSnapshot], link_occupancy: dict[int | str, int])

A full snapshot of the network at one simulation step.

Attributes:

Name Type Description
step int

The simulation step index this frame captures.

time float

The simulated time in seconds (step * dt).

agents list[AgentSnapshot]

Agents on real links (see :class:AgentSnapshot).

waiting list[WaitingSnapshot]

Agents waiting to enter, by node (see :class:WaitingSnapshot).

link_occupancy dict[int | str, int]

link_id -> live vehicle count for every real link.

AgentSnapshot dataclass

AgentSnapshot(vehicle_id: int, link_id: int | str, queue_index: int, queue_len: int, route: Sequence[int | str], next_link_id: int | str | None, next_node: NodeId | None, category: str = DEFAULT_CATEGORY, props: dict = dict())

One agent travelling on a real link at a given step.

The whole remaining route is logged verbatim from vehicle.route (never recomputed), so the plan recorded here is exactly the one the simulation used — including any exogenous mid-run rerouting — and the next hop is simply read back from it.

Attributes:

Name Type Description
vehicle_id int

The vehicle's id (drawn as its label).

link_id int | str

The real link the agent occupies.

queue_index int

FIFO position on the link; 0 is the front (the vehicle nearest the downstream node, next to leave).

queue_len int

Number of vehicles on the link this step.

route Sequence[int | str]

The agent's remaining real-link route from the current link onward (route[0] == link_id), copied from vehicle.route; empty when the vehicle carries no explicit route (e.g. pure node-by-node policy routing, which stores no plan on the vehicle).

next_link_id int | str | None

The link the agent will enter next (route[1]), or None if the current link is the last known one.

next_node NodeId | None

The node the agent heads toward next (downstream node of next_link_id), used to draw the intention arrow; None at the end of a route.

category str

Colour/legend category (see :data:ClassifyFn).

props dict

A snapshot copy of the vehicle's props metadata (see :class:~mesoltm.core.vehicle.Vehicle) at this step, so a color_by callable (or later analysis) can colour/inspect agents by arbitrary per-vehicle information.

WaitingSnapshot dataclass

WaitingSnapshot(vehicle_id: int, node_id: NodeId, route: Sequence[int | str], next_link_id: int | str | None, next_node: NodeId | None, category: str = DEFAULT_CATEGORY, props: dict = dict())

One agent waiting to enter the network at a node.

Waiting means either queued in an origin's vertical entry queue or sitting on an access connector — in both cases the agent is drawn beside node_id, the node it is waiting to enter.

Attributes:

Name Type Description
vehicle_id int

The vehicle's id.

node_id NodeId

The node the agent is waiting at / wants to enter.

route Sequence[int | str]

The agent's planned real-link route (from vehicle.route), empty when it carries no explicit plan.

next_link_id int | str | None

The (real) link it intends to enter next (route[0]), or None.

next_node NodeId | None

The node it heads toward next, or None.

category str

Colour/legend category (see :data:ClassifyFn).

props dict

A snapshot copy of the vehicle's metadata (see :class:AgentSnapshot).

Capture helpers

capture_frame

capture_frame(state: NetworkState, classify: ClassifyFn | None = None, step: int | None = None) -> Frame

Build a :class:Frame from the live network state.

No route is ever computed here. Each agent's remaining plan is read verbatim from its vehicle.route (via :meth:NetworkState.remaining_real_route, the same helper the rerouting interface uses), so the logged route always matches what the simulation actually ran — including any exogenous mid-run rerouting — and the drawn next hop is simply read back from it.

Parameters:

Name Type Description Default
state NetworkState

The live :class:~mesoltm.network.state.NetworkState.

required
classify ClassifyFn | None

Optional classify(vehicle, state) -> str for per-agent colour categories; None labels every agent identically.

None
step int | None

Step index to stamp on the frame; defaults to state.step (kept current by the simulation loop).

None

Returns:

Type Description
Frame

The captured frame (scalars only; safe to keep as the sim advances).

record_run

record_run(sim: Simulation, classify: ClassifyFn | None = None) -> SimulationHistory

Run a simulation to completion, recording a frame per step.

A convenience for the batch case (no custom stepping/injection): it enables history logging, runs sim and returns the populated :class:SimulationHistory. For a custom step loop (e.g. a controller that injects vehicles), compile with record_history=True instead and read sim.history after the loop.

Parameters:

Name Type Description Default
sim Simulation

A compiled simulation (its network_state must be set).

required
classify ClassifyFn | None

Optional per-agent category classifier.

None

Returns:

Type Description
SimulationHistory

The recorded history.