Skip to content

Movement animations

mesoltm can render a video of vehicles moving link-to-link. This is a two-stage process: record a per-step history during the run (matplotlib-free), then render it to a video or frames (needs the [plot] extra).

1. Record the history

Turn on history capture at compile time. The recorded run exposes the frames on Simulation.history:

sim = net.compile(time_step=1.0, total_time=400.0, record_history=True)
sim.run()
history = sim.history            # a SimulationHistory (JSON-serialisable)

The recorder logs, per step, each vehicle's position, its remaining route (read straight from vehicle.route — never recomputed, so the log always matches what ran), and a copy of its props metadata. Vehicles waiting to enter are logged as a count on their origin node. Optionally persist it:

sim = net.compile(..., record_history=True, history_path="run.json")
sim.run()                        # writes run.json on completion
# or explicitly: sim.save_history("run.json")

Recording is cheap and dependency-free

History capture adds no plotting dependency (mesoltm.recording is matplotlib-free) but does cost memory, so it is off by default.

2. Render to video or frames

The rendering helpers live in mesoltm.visualizations.animation (needs [plot]; MP4 also needs ffmpeg on the PATH — otherwise it falls back to an animated GIF via Pillow).

from mesoltm.visualizations import animate_simulation
animate_simulation(sim, "run.mp4", fps=25)     # from a recorded simulation

Other entry points:

  • animate_history(history, out_path, ...) — from a SimulationHistory.
  • animate_from_history_file(path, out_path, ...) — from a saved JSON log.
  • save_frames(frames, layout, out_dir, ...) — per-step PNGs.
  • save_animation(frames, layout, out_path, fps=25, subsample=1.0, ...) — MP4/GIF (subsample > 1 slows playback, < 1 speeds it up).

Colouring vehicles

color_by chooses what a dot's colour means:

color_by Meaning
"category" (default) The classification set at record time (history_classify)
"next_link" The link each vehicle takes next (read from the logged route)
None Uniform colour (colouring off)
a callable fn(snapshot) -> str Anything on the snapshot — most usefully snapshot.props

A custom callable is the most flexible — colour by a vehicle class, operator, or any metadata you attached via Vehicle(props=...), which round-trips through the log:

from mesoltm.visualizations import animate_simulation
animate_simulation(
    sim, "by_class.mp4",
    color_by=lambda snap: "tab:red" if snap.props.get("vclass") == "truck" else "tab:blue",
)

To assign categories at record time instead, pass history_classify to compile():

sim = net.compile(..., record_history=True,
                  history_classify=lambda veh, state: veh.props.get("group", "default"))

The renderer scales markers, nodes, and labels to the network so dense grids stay readable, and auto-drops per-agent detail when crowded (overridable via detail, show_agent_ids, show_next_link). See the Visualizations reference and grid_visualization.py / bottleneck_access_policy.py.