Animations

hofmann can render multi-frame trajectories as GIF or MP4 animations using render_animation(). The same per-frame rendering pipeline used for static output is applied to each frame, so all visual options (bonds, polyhedra, colouring, styles) work identically.

Basic usage

Build a scene from a trajectory and call render_animation():

from ase.io import read
from hofmann import StructureScene

traj = read("trajectory.traj", index=":")
scene = StructureScene.from_ase(traj)
scene.render_animation("output.gif")

The output format is determined by the file extension — .gif for GIF or .mp4 for MP4. Animation rendering requires the animation optional extra:

pip install "hofmann[animation]"

The examples on this page use ASE for trajectory loading, which requires the ase extra:

pip install "hofmann[ase]"

Frame selection

By default all frames are rendered. Use the frames parameter to select a subset:

# Every 5th frame:
scene.render_animation("output.gif", frames=range(0, 100, 5))

# Specific frames:
scene.render_animation("output.gif", frames=[0, 10, 20, 50])

Resolution and size

Control the output resolution with dpi and figsize:

scene.render_animation(
    "output.gif",
    fps=10,               # default: 30
    dpi=100,              # default: 150
    figsize=(6.0, 6.0),   # default: (5.0, 5.0)
)

Example: CH4 vibration

A vibrating methane molecule styled to match the Getting Started example:

from ase.io import read
from hofmann import AtomStyle, BondSpec, StructureScene

traj = read("ch4_md.traj", index=":")

bonds = [BondSpec(species=("C", "H"), max_length=1.5, radius=0.055, colour=1.0)]
atom_styles = {
    "C": AtomStyle(radius=0.5, colour=0.7),
    "H": AtomStyle(radius=0.35, colour=1.0),
}

scene = StructureScene.from_ase(traj, bond_specs=bonds, atom_styles=atom_styles)
scene.render_animation("ch4_md.gif", fps=15, dpi=100, figsize=(4, 4))
CH4 vibration animation

Example: SrTiO3 perovskite MD

A single octahedral layer from a 4x4x4 SrTiO3 supercell at 1000 K. The trajectory has been pre-filtered to one Sr plane, one Ti plane, and their coordinating O. The hide_vertices option on the polyhedron spec hides the oxygen atoms to emphasise the polyhedral network:

from ase.io import read
from hofmann import (
    AtomStyle, BondSpec, PolyhedronSpec, StructureScene,
)

traj = read("srtio3_md.traj", index=":")

bonds = [BondSpec(species=("Ti", "O"), max_length=2.5)]
polyhedra = [
    PolyhedronSpec(
        centre="Ti", colour="steelblue", alpha=0.4,
        hide_centre=True, hide_bonds=True, hide_vertices=True,
    ),
]
atom_styles = {
    "Sr": AtomStyle(radius=1.2, colour="forestgreen"),
}

scene = StructureScene.from_ase(
    traj, bond_specs=bonds, polyhedra=polyhedra,
    atom_styles=atom_styles,
)
scene.render_animation(
    "srtio3_md.gif", fps=10, dpi=100, figsize=(6, 6),
    show_axes=False, show_cell=False,
    pbc_padding=1.0,
)
SrTiO3 perovskite MD animation

Per-frame colouring

set_atom_data() accepts 2-D arrays of shape (n_frames, n_atoms) so that colourmap-based colouring can vary per frame. This is useful for visualising properties that evolve over a trajectory, such as local order parameters, coordination environments, or spatial orientation.

# angle_data has shape (n_frames, n_atoms)
scene.set_atom_data("angle", angle_data)
scene.render_animation(
    "output.gif",
    colour_by="angle", cmap="twilight",
    colour_range=(0, 360),
)
Rotating ring coloured by azimuthal angle

Numeric data is automatically scaled using the global range across all frames so that colours are consistent throughout the animation. Use colour_range to fix the limits explicitly. Categorical (string) data uses a global label set so that the same label always maps to the same colour.

Polyhedra inherit colour_by colouring from their centre atom, so per-frame data on the centre atoms flows through to the polyhedra automatically. Here, the octahedral tilt angle of each TiO6 polyhedron in an SrTiO3 MD trajectory is mapped through the Reds colourmap — white for untilted, red for maximum tilt:

# tilt_data has shape (n_frames, n_atoms); NaN for non-Ti atoms.
scene.set_atom_data("tilt", tilt_data)
scene.render_animation(
    "srtio3_tilt.gif",
    colour_by="tilt", cmap="Reds",
    colour_range=(0, max_tilt),
)
SrTiO3 polyhedra coloured by octahedral tilt angle

Style keyword arguments

Any RenderStyle field can be passed as a keyword argument to render_animation(), just as with render_mpl():

scene.render_animation(
    "output.gif",
    show_bonds=False,
    show_polyhedra=True,
    pbc_padding=1.0,
)

Interactive trajectory viewing

Multi-frame scenes can also be explored interactively. See Interactive viewer for keyboard controls including frame navigation ([ / ], { / }), frame indicator (f), go-to-frame (g), and step size (s).