Movies API Reference

Functions for assembling an animation from a sequence of simulation outputs.

The workflow separates the expensive part from the cheap part on purpose: getmovie reads the outputs and produces frames, savemovie stores them, and moviefromframes encodes frames that already exist. Rendering a second version therefore does not re-read the simulation.

getmovie varies time: one frame per snapshot. To vary the angle instead, sweeping the camera around a single snapshot with a frame that cannot drift, use rotation_sequence. To vary both, getmovie takes angles (a full turn at each snapshot) or sweep (one moving angle across the series).

For a frame getmovie cannot produce, such as a cutting plane, timeseries reduces each snapshot to whatever you like, including a slice, and the resulting vector of maps goes straight into a MeraMovie for savemovie.

Mera.getmovieFunction
getmovie(path, quantity; unit=:standard, datatype=:hydro, outputs=:all, mera_files=false,
         direction=:z, los=nothing, up=nothing, theta=nothing, phi=nothing,
         inclination=nothing, azimuth=nothing, position_angle=nothing, axis=nothing,
         angle_unit=:deg, center=[:boxcenter], range_unit=:standard,
         xrange=[missing,missing], yrange=[missing,missing], zrange=[missing,missing],
         res=nothing, lmax=nothing, pxsize=nothing, weighting=nothing,
         fov=nothing, fov_unit=nothing, aperture=nothing, binning=nothing, nmax=nothing,
         angles=nothing, sweep=nothing, angle_var=:azimuth,
         time_unit=:Myr, verbose=true) -> MeraMovie

Project quantity for every output of a simulation and collect the maps into a MeraMovie — the frames of a movie of the run evolving. Like timeseries it loads one snapshot at a time (RAM-safe) and discovers outputs the same way (RAMSES or mera_files).

Axis-aligned by default (direction=:z); for an off-axis movie use any of projection's view controls — a los/up (e.g. from face_on/edge_on), the angles inclination/azimuth (or theta/phi, position_angle, with angle_unit), or axis=:angmom to auto-orient face-on. res/pxsize/lmax and the region keywords cut cost per frame.

Hold the frame still. Without fov an off-axis projection auto-fits its window to the rotated data, which differs from snapshot to snapshot: the object appears to zoom, only the first frame's extent is recorded, and a size change between snapshots stops the encode. Pass fov/fov_unit (with aperture=:circle|:square) to select a fixed sphere about center, exactly as rotation_sequence does, so every frame shares one window.

Moving the camera — two different movies, and neither invents a frame; every one is a real projection from a real viewpoint:

  • angles sweeps that whole list at every snapshot, giving outputs x angles frames: orbit, then step time. The snapshot stays resident while its angles render, trading the one-snapshot-at-a-time memory profile for the extra viewpoints.
  • sweep advances one angle across the series: one frame per snapshot, each a different time and angle, orbit while time passes. Give one value per output, or a (lo, hi) pair to spread linearly over them. Memory profile unchanged.

angle_var picks which angle they drive: :azimuth (default), :inclination or :position_angle. Setting that same angle explicitly as well is an error, not a silent override.

datatype=:particles works and takes a Symbol weighting; the hydro [quantity, unit] spelling is accepted too. Gravity and clumps cannot be projected alone and are refused with a message naming the alternative.

m  = getmovie("/data/sim", :sd)                              # face-up density movie, all outputs
fr = face_on(gethydro(getinfo(1, "/data/sim")))             # a fixed orientation …
m  = getmovie("/data/sim", :sd; los=fr.los, up=fr.up, center=fr.center)

# a steady off-axis movie: one fixed camera frame for every snapshot
m  = getmovie("/data/sim", :sd; inclination=60, axis=:angmom,
              fov=15, fov_unit=:kpc, aperture=:square, pxsize=[0.5, :kpc])

m  = getmovie("/data/sim", :sd; angles=0:5:355, fov=15, fov_unit=:kpc)  # a turn at each snapshot
m  = getmovie("/data/sim", :sd; sweep=(0, 180), fov=15, fov_unit=:kpc)  # turning as it evolves
m  = getmovie("/data/sim", :sd; datatype=:particles, fov=15, fov_unit=:kpc)   # stars, not gas
savemovie(m, "density.gif")

Returns a MeraMovie; m.frames[k] is the 2D map of output m.outputs[k] at time m.times[k]. See savemovie to write a GIF.

Mera.savemovieFunction
savemovie(m::MeraMovie, file="movie.gif"; colormap=:fire, log=true, colorrange=:global,
          clip=(0.0, 1.0), fps=10, tags=nothing, annotate=true, save_frames=nothing,
          verbose=true) -> file

Write a MeraMovie to an animated GIF (file), using the bundled FileIO/Images (no extra package needed). Each frame is normalised over a colour range and mapped through a colormap.

  • colormap:fire (default) or :gray, or a function t∈[0,1] -> (r,g,b).
  • log — map log10 of the (positive) values (default; good for density).
  • colorrange:global (one range across all frames — steady brightness), :perframe, or an explicit (lo, hi) (already in log space when log=true).
  • clip — drop this lower/upper quantile fraction when auto-computing the range.
  • fps — playback frame rate of the GIF.

Tags (per-frame labels). Pass tags to label each frame:

  • :time"t=<time> <unit>", :output"output 00001";
  • a vector of strings (one per frame); or a function k -> String;
  • a tuple of any of the above stacks multiple lines, e.g. tags=(:output, :time).

The labels are printed (when verbose) and, when annotate=true, burned onto the frames with a built-in bitmap font (no font dependency). Control the look:

  • tag_scale — font size: :auto (default, scales with the frame) or an integer.
  • tag_position:topleft (default), :topright, :bottomleft, :bottomright, or an explicit (row, col) pixel.
  • tag_color:white (default), :yellow, :red, :cyan, :green, :black, an RGB, or an (r,g,b) tuple.

Scratch frames. Set save_frames to a directory to also write each rendered frame as frame_00001.png, … in it (the directory is created). Those PNGs can be re-assembled later with moviefromframes — or fed to ffmpeg for an MP4.

Persisting the movie object. A file ending in .jld2 stores the whole MeraMovie (the numeric frames + metadata) to a JLD2 file instead of encoding a GIF — reload it with loadmovie. Same Julia-native way savemap persists a map; nothing is re-rendered, so it round-trips exactly.

savemovie(m, "density.gif"; tags=:time, fps=12)
savemovie(m, "density.gif"; tags=(:output, :time), tag_position=:bottomright, tag_color=:yellow)
savemovie(m, "density.gif"; tags=:output, tag_scale=3, save_frames="frames/")
savemovie(m, "density.jld2")        # persist the MeraMovie object (→ loadmovie)
Mera.loadmovieFunction
loadmovie(filename; verbose=true) -> MeraMovie

Load a MeraMovie saved with savemovie(m, "….jld2"). The numeric frames and metadata round-trip exactly, so you can re-savemovie it to a GIF (with different tags/colormap) without re-running getmovie. JLD2-native, like loadmap.

Mera.moviefromframesFunction
moviefromframes(dir, file="movie.gif"; pattern=r"\.png$"i, fps=10, verbose=true) -> file

Assemble an animated GIF from existing image files in dir (e.g. PNG frames you rendered yourself, or wrote earlier with savemovie(...; save_frames=dir)). Files matching pattern are sorted by name and stacked in that order. The complement to savemovie's save_frames.

moviefromframes("frames/", "movie.gif"; fps=12)
Mera.makethumbFunction
makethumb(source, out; width=800, height=450, mode=:fit, anchor=:center,
          frame=0.4, background=:auto) -> String

Make a fixed-size preview image from a figure or an animation, and return the path written.

source is a saved picture (PNG, JPEG, and anything else FileIO reads) or an animated GIF. out is the file to write, a PNG. Both sizes default to the 800 by 450 used by the cards in the Gallery.

Two ways to deal with a picture whose shape does not match the card:

  • mode=:fit (the default) scales the whole picture in and pads the rest, so nothing is lost. A wide multi-panel figure keeps its outer panels.
  • mode=:crop fills the card and cuts off the overflow. Right only when the subject is centred and the edges carry nothing. anchor picks what survives: :center, :top, :bottom, :left or :right.

background is the colour the padding uses. The default :auto takes it from the picture's own corners, so padding a figure drawn on black stays black instead of putting a bright border round every card. Pass any Colorant to choose it yourself.

frame selects where in an animation to take the picture, as a position from 0.0 to 1.0. The default of 0.4 is deliberate: an animation that opens on a static pose makes a dull preview from its first frame. It is ignored for a still picture.

A movie (MP4 and friends) is not read here, because decoding video would add a large binary dependency to every install for a job that is done once. Save a frame from the code that made the movie, or point this at the preview GIF.

makethumb("media/density.png", "media/my_recipe_thumb.png")
makethumb("media/preview.gif", "media/my_recipe_thumb.png"; frame=0.25)
makethumb("media/wide_strip.png", "card.png"; mode=:crop, anchor=:top)

See also Gallery and shared workflows for how these are used, and savefits for exporting the data rather than a picture.