ovito-auto-viz
Fast, automated, reproducible and FAIR visualization of atomistic simulations with OVITO — self-labeling figures with complete metadata and provenance.
You — or the LLM of your choice — describe the figure in a ~20-line YAML
viz card; the ovzm CLI turns
it into a publication-quality image, movie, or ready-to-open OVITO session.
Everything on the figure — Burgers vectors, line directions, character
angles, composition, colorbars, per-grain tripods — is computed from the
data itself. And every figure permanently remembers who made it, from
which data file (SHA-256), with which settings, under which funding, and
which publication it belongs to: the full provenance record is embedded
inside the PNG. Works hands-on, in scripts, on HPC clusters — and
conversationally through an LLM agent skill.
The whole story in one picture: a LAMMPS dump and the simulation project's
context go in; a ~20-line viz card — written by you or by an AI agent —
tells ovzm render what the figure is; out come a publication-ready image
AND its complete provenance, embedded inside the PNG itself.
Why
Scientific figures take time to make and are amongst the least FAIR objects in a publication:
pixels with no metadata, produced by GUI clicks nobody can repeat, separated
from their data the moment they are exported. ovzm inverts this:
- The figure is a text file. The card is versionable, diffable, reviewable, and re-runs identically on your laptop or a cluster. Same card + same data = same figure, years later. The card can even be extracted from the figure if it goes missing.
- The physics labels itself. DXA dislocation segments get their Burgers vector, line direction, and character angle computed from the data — nucleated defects with zero metadata are labeled just as well as constructed ones. Composition, colorbar limits, and crystal-axis tripods are derived, never typed.
- A figure never forgets. Creator (mandatory — the run aborts without
it), affiliation, ORCID, project, funding, input file with SHA-256, the
fully resolved card and scene: all of it is written to a
.prov.yamlsidecar AND embedded into the PNG itself as a compressed text chunk. Image and provenance cannot be separated by copying, renaming, emailing, or archiving. - Comparisons are honest.
ovzm gridrenders one card over many inputs with identical camera, magnification, styling, and colorbar limits — the manual-GUI failure mode this tool exists to kill. - LLM-ready by design. The card is one shared interface for humans, scripts, and AI agents. With the bundled agent skill, "glide-plane view of dump.1530000.gz, Cu segregation, paper quality" becomes a rendered figure — the agent writes the card, collects attribution context from your project directory, and asks (never guesses) whatever the data cannot provide. The tool itself requires no AI; the skill is an optional adapter on top.
The showcase figure
A crack in tungsten runs in from the left, kinks onto a phosphorus-decorated
grain boundary, and continues along it (bcc W dark gray, defective atoms
white, P orange, one Miller-labeled tripod per grain — CNA + the
structure_colors/pinned-species styling and the grains: tripods, all from
one card). Configuration from
Tian et al., Acta Materialia 259 (2023) 119256.
The image carries its own receipt — clone the repo and ask it where it comes
from:
ovzm prov docs/readme-hero.png # prints creator, input file + SHA-256,
# the resolved card, and the paper's DOI
Install
ovzm is an ordinary Python package (Python ≥ 3.9). Its main dependency,
the ovito Python module (free of
charge, DXA included), is installed automatically by pip — you do NOT
need the OVITO desktop application. Install into a conda/mamba environment
or a venv, never into a system Python (many systems ship no
user-writable pip, and installing one globally is a good way to break the
OS package manager).
Linux
python -m venv ~/venvs/ovzm && source ~/venvs/ovzm/bin/activate # or: conda activate <env>
pip install ovito-auto-viz
Headless machines (clusters, CI, containers) additionally need the
graphics runtime the ovito module links against — no GPU or display
required (rendering uses the Tachyon software ray-tracer). Since ovito 3.16
the Linux module renders through Vulkan and needs a Vulkan driver
even for offscreen renders; Mesa's lavapipe software driver does the job
without a GPU:
sudo apt-get install libopengl0 libegl1 libgl1 libglx0 libxkbcommon0 mesa-vulkan-drivers # Debian/Ubuntu
# Fedora/RHEL: libglvnd-opengl libglvnd-egl libglvnd-glx libxkbcommon mesa-vulkan-drivers
# openSUSE: ... libvulkan_lvp
If ovzm render stops with "Could not initialize the Vulkan graphics
backend", that driver is what is missing. On a cluster without root, ask the
admins for it or fall back to pip install "ovito<3.16", which still uses
OpenGL and needs only the first five packages.
On HPC, install into a venv under scratch (never $HOME) and run renders
inside a batch job, not on a login node.
macOS (Intel and Apple Silicon)
conda activate <your-env> # conda-forge env recommended on macOS
pip install ovito-auto-viz
Known upstream wheel bug: on Apple-silicon Macs ovito==3.16.1 fails to
import with Library not loaded: @loader_path/libospray.3.2.0.dylib (the
wheel ships the file only as libospray.3.dylib). Either
pip install "ovito==3.15.5", or add the missing name:
ln -s libospray.3.dylib "$(python -c 'import ovito,os;print(os.path.dirname(ovito.__file__))')/plugins/libospray.3.2.0.dylib"
Windows
The ovito module ships Windows wheels, so the same install should work in
an Anaconda Prompt or venv (not yet routinely tested — reports welcome):
pip install ovito-auto-viz
If anything misbehaves natively, WSL2 + the Linux instructions above is the reliable fallback.
Check the install — this must print JSON:
ovzm schema | head -3
The latest development version comes straight from GitHub:
pip install git+https://github.com/biterik/ovito-auto-viz.git — and for
development, install editable from a clone:
pip install -e /absolute/path/to/ovito-auto-viz.
Quickstart
# my-figure.yaml
name: d90-glideplane
extends: dxa-standard # preset: PTM + DXA + auto labels
input:
file: dump_min_sgcmc_d90_T300.1530000.gz
crystal:
x: [-1, 0, 1] # or "auto" -> parsed from x-101_y1-21_z111 filenames
y: [1, -2, 1]
z: [1, 1, 1]
lattice: fcc
view:
direction: top # named view, or a Miller direction like [111]
atoms:
show: non_fcc
names: {1: Ni, 2: Cu}
colors: {1: [0.62, 0.66, 0.72], 2: [0.90, 0.45, 0.10]}
ovzm render my-figure.yaml # -> <input>__<name>.png + .prov.yaml sidecar
ovzm grid comparison.yaml # same card over grid.inputs -> one labeled grid
ovzm session my-figure.yaml # -> .ovito file: open in the GUI, pipeline + camera preset
ovzm import something.ovito # best-effort reverse: GUI session -> card YAML
ovzm info dump.gz # types, box, frames, filename-parsed orientation
ovzm prov figure.png # print the provenance embedded in the image
ovzm validate my-figure.yaml # schema check with readable errors
ovzm schema # print the JSON schema
Try it in five minutes
examples/try-it/ is fully self-contained — nothing to download: a
numpy-only script generates a small fcc Ni box with four edge dislocations
(a quadrupole — a closed system with net b = 0), and one card renders it
with every label computed from the positions alone:
cd examples/try-it
python make_edge_dipoles.py # ~14k atoms, a few seconds
ovzm render edge-dipoles.yaml --ask # asks once who you are — attribution is mandatory
ovzm prov edge-dipoles__edge-dipoles.png
See examples/try-it/README.md for the
walkthrough and the expected (computed) label block;
tests/test_example_physics.py asserts the DXA ground truth of this
example in CI.
Using it with an LLM (the agent skill)
skills/ovito-auto-viz/SKILL.md is an
Agent Skill: a plain instruction
file (no code) that teaches a Claude session when to use ovzm, how to
compose cards from the presets, and — crucially — what it must ask
instead of guessing (species names, crystal orientation, figure creator,
colorbar units). Attribution context is collected automatically from the
project directory (ovzm-project.yaml, personal identity file, filename
conventions), so a one-line request becomes a rendered, fully attributed
figure plus the card to version with your project. The tool itself never
requires any AI — the skill is an optional adapter on top.
Install:
-
Claude Code (CLI): copy the skill folder into your skills directory —
mkdir -p ~/.claude/skills cp -R skills/ovito-auto-viz ~/.claude/skills/
It then triggers automatically on requests like "render the standard DXA view of this dump" in any project where
ovzmis installed. -
Claude Desktop / Cowork: add the skill via the app's Settings → Capabilities/Skills (upload or point it at
skills/ovito-auto-viz/), or ask Claude in a session to packageSKILL.mdas an installable.skillfile for your account.
Usage is then conversational: "glide-plane view of
SGCMC-D90/dump.1530000.gz, Cu segregation, paper quality" — the session
writes the card (asking for whatever the required-information table says it
cannot detect), runs ovzm render, and shows you the PNG plus the card so
you can version it with the project.
Attribution of figures
Every figure records who made it. meta.creator is mandatory (the run
aborts without it); project, funding, affiliation, email, orcid
are optional. Because people work on several projects with different
funding and even different affiliations, attribution is resolved
per key, git-config style:
- the card's own
meta:block (most specific), - an
ovzm-project.yamlfound by walking UP the directory tree from the card and from the input data — put one in each simulation project root (e.g.SIMULATIONS/EAM-DISLOCS-Ni-Cu/ovzm-project.yaml); a file in a subdirectory (thread) overrides the project root's, - a personal
~/.config/ovzm/identity.yaml(or$OVZM_IDENTITY) — typically justcreator: Jane Doe, --askprompts interactively as the last resort (creator only).
A project file for a single-user project:
# SIMULATIONS/EAM-DISLOCS-Ni-Cu/ovzm-project.yaml
project: EAM-DISLOCS-Ni-Cu
funding: NFDI-MatWerk (DFG 460247524)
creator: Erik Bitzek
people:
Erik Bitzek:
affiliation: MPI for Sustainable Materials, Duesseldorf / FAU WW8, Fuerth
orcid: 0000-0001-7430-3694
For multi-user projects, omit the top-level creator (each user's identity
file supplies their name) and list everyone under people: — the resolved
creator's entry fills in their affiliation/email/ORCID for that project.
All resolved values land in the .prov.yaml and inside the PNG; every YAML
the tool writes carries the tool-credit header ("created with
ovito-auto-viz … funded by NFDI-MatWerk").
Reading the provenance inside a PNG
The embedded record is a standard PNG text chunk (key ovzm_prov), so it
is accessible with or without this tool:
ovzm prov figure.png # with ovito-auto-viz installed
ovzm prov figure.png > figure.prov.yaml # ... e.g. to regenerate a lost sidecar
Without ovzm, two lines of Python (Pillow):
from PIL import Image
print(Image.open("figure.png").text["ovzm_prov"])
or on the command line with common metadata tools:
exiftool -b -Ovzm_prov figure.png # exiftool
identify -verbose figure.png # ImageMagick: listed under Properties
The chunk survives copying, renaming, emailing, and archiving — anything
that treats the file as bytes. It does NOT survive operations that
re-encode the pixels: screenshots, format conversion (PNG→JPEG), or
"export/save for web" in image editors. For a figure that will be
re-encoded (e.g. embedded in a PDF), keep the .prov.yaml sidecar
alongside — it is the identical record.
Comparison grids
ovzm grid renders the SAME card over several inputs (grid.inputs,
optional grid.labels/grid.cols) into one figure with identical camera,
magnification, styling, and colorbar limits across panels — the manual-GUI
failure mode this tool exists to kill. Tripod and legend are drawn once
(first panel; grid.tripod: all for every panel); each panel carries only
its title, and the per-panel analysis results (composition, DXA, …) live in
the grid's provenance under panels:.
Per-grain tripods (bicrystals, polycrystals)
A grains: block draws one labeled coordinate tripod per grain — anchored at
a user-provided origin, oriented by user-provided Miller triplets (x/y/z
are the crystal directions of this grain lying along the sim-box axes, same
semantics as the crystal: block). Origins and orientations are never
guessed; give them inline or via an external grains file (file: XOR
items:):
grains:
file: grains-d005.yaml # EITHER external file (path relative to the card)
items: # OR inline — exactly one of the two
- name: upper # optional; defaults g1, g2, ...
origin: [12.0, 40.0, 88.5] # Å, cartesian, simulation frame
x: [-1, 0, 1]
y: [1, -2, 1]
z: [1, 1, 1]
tripod: # styling, all optional
size: 20 # arm length in Å (default: 5% of the largest box edge, min 10 Å)
axes: box # 'box' (arms along the box axes, labeled with each
# grain's x/y/z) or 3 Miller triplets drawn in each
# grain's own frame, e.g. [[1,0,0],[0,1,0],[0,0,1]]
show_names: true
The external grains file is a YAML mapping with the same grains: list —
write it by hand, from a Voronoi construction, or from a segmentation tool
(float triplets are accepted). Arms are drawn on top of the atoms with
world-anchored length, so tripods stay visible inside dense grains and come
out identical on every ovzm grid panel. The resolved grains (plus the
grains-file SHA-256) land in the provenance like everything else.
tests/make_bicrystal.py generates a Σ5 [001] bicrystal + grains file to
try it on. (The hero image above uses exactly this feature — one tripod per
grain of the W bicrystal.)
Required minimum information
ovzm refuses to guess what cannot be auto-detected. The contract:
| info | auto-detected from | if not detectable |
|---|---|---|
| input file | — | always required in the card |
| species names (multi-type files) | type names in the file (data files); dumps have none | hard error — add atoms.names: {1: Ni, 2: Cu} or run with --ask to be prompted |
| crystal orientation | x-101_y1-21_z111-style filenames |
b/ξ labels fall back to DXA's lattice frame, flagged in the label block |
| grain origins/orientations (tripods) | — | hard error if a grains: block is incomplete — never guessed |
| figure creator | ovzm-project.yaml / identity file | hard error — attribution is not optional |
| lattice (for DXA) | — | preset default (fcc in dxa-standard); set crystal.lattice |
| view | — | preset default (front); set view.direction |
| what is shown | — | preset default (non_fcc in dxa-standard); set atoms.show |
| colorbar units | — | set annotate.colorbar.units; limits are resolved automatically and recorded in the prov file |
ovzm info <file> prints what a file does and does not carry (types, frames,
box, filename-encoded orientation) — use it before writing a card.
Naming convention
Every figure has an ID: <input-stem>__<card-name>[__<tag>]. The card name
encodes the figure's identity (view + analysis, e.g. d90-dxa-top); the
optional output.tag distinguishes variants. Image and provenance sidecar
share the ID exactly:
dump_min_sgcmc_d90.1530000__d90-dxa-top.png
dump_min_sgcmc_d90.1530000__d90-dxa-top.png.prov.yaml
Multiple figures from the same input differ in card name; the same card on
multiple inputs differs in stem. The .prov.yaml records the ID, input
SHA-256, the resolved card, AND the fully resolved scene: per-type species
names / colors / radii, colorbar limits + units, camera, composition, and the
complete DXA result (per-segment b, ξ, character, net Burgers vector, and the
measured lattice-constant estimate). The image itself can therefore stay
visually clean — nothing is lost as long as the sidecar travels with it.
What gets labeled automatically
With annotate.labels: auto (the default in dxa-standard), the label block
is composed from the data: timestep; total composition (e.g. x(Cu) = 0.0100)
plus shown-atom counts; DXA segment count and total line length; per-family
breakdown (perfect, Shockley, stair-rod, Hirth, Frank); the net Burgers
vector with its character (net b = 1/2[-101], 90° edge); and per-segment
b = 1/6[-211] (Shockley partial), ξ = [1-21], 60° mixed, L = 100 Å.
Frame discipline: DXA's true_burgers_vector lives in DXA's own lattice
frame, which can differ from the card's crystal frame by a cubic symmetry
operation. All geometry (Miller indices in the card frame, line directions,
character angles) is therefore computed from the segment's spatial Burgers
vector; the true vector is used only for family classification and the
|b_spatial|/|b_true| lattice-constant estimate. Character names are
conservative: edge ≥ 85°, screw ≤ 5°, otherwise the angle plus mixed.
If a dcreator .disparam is around it can serve as a cross-check, but
nothing requires metadata — nucleated dislocations label identically.
The coordinate tripod is labeled with the crystal axes (x=[-101], …) and a
view.fit_margin (default 1.15) keeps all overlays outside the projected
cell. Whenever atoms carry color information there is a legend: a
continuous colorbar for color_coding (limits resolved from the shown data
when range: auto, units from annotate.colorbar.units), or a discrete
per-type legend when coloring by species.
Presets and styling
Cards inherit via extends: (chains allowed; search path: $OVZM_PRESET_PATH,
then ./presets/, then the presets bundled with the package in
src/ovzm/presets/). Shipped:
dxa-standard— PTM + DXA, non-fcc atoms only, Miller tripod, auto labels.segregation-map— all atoms colored by type, solute rendered larger.
Output quality presets: draft (1280×720, fast), slide (1920×1080),
paper (3200×2400, ambient occlusion) — override width/height freely
(the hero above is paper at 1600×1600). Background white/black/
transparent. Mixed coloring is a card key away: color_by: structure with
atoms.structure_colors (e.g. bcc dark gray, defective atoms white) while
types listed in atoms.colors stay pinned to their species color — that is
how the hero shows a structure-colored W matrix with orange P solutes.
Multimillion-atom workflow
The intended pattern for big data is analyze once, render many: the
expensive step is PTM/DXA, not rendering. Typical defect views delete the
fcc/bulk atoms (atoms.show: non_fcc), which cuts the rendered particle count
by ~100×. For files too large for a laptop, run the same card on the cluster
headless inside a batch job; only PNGs come back. Positions at %.4f Å are
well above every OVITO method's noise floor.
Documentation
docs/SCHEMA.md— every card key, generated fromsrc/ovzm/schema/vizcard.schema.json(regenerate withpython tools/gen-schema-md.py).- Put
# yaml-language-server: $schema=<path>/vizcard.schema.jsonat the top of a card for editor autocompletion. skills/ovito-auto-viz/SKILL.md— the agent skill (see above).
Author, funding, citation
Author: Erik Bitzek (ORCID 0000-0001-7430-3694, e.bitzek@mpi-susmat.de) — ¹ Max-Planck-Institut for Sustainable Materials, Düsseldorf, Germany; ² Institute of Materials Simulation (WW8), Friedrich-Alexander-Universität Erlangen-Nürnberg (FAU), Fürth, Germany. Built with Claude/LLM assistance.
Funding: Deutsche Forschungsgemeinschaft (DFG, German Research Foundation) — NFDI 38/1, project number 460247524 (NFDI-MatWerk consortium).
License: BSD-3-Clause. Citation: see CITATION.cff
(GitHub's "Cite this repository" button).
Known issues / roadmap
- Sessions carry overlays only with ovito ≥ 3.16.1: older modules
(observed on 3.15.5) write corrupt
.ovitofiles when viewport overlays are in the scene, so thereovzm sessionskips them (grain tripods included) and says so; the pipeline, styling and camera are always saved. Useovzm renderfor annotated output on older versions. The provenance records which case applied (session_overlays). - Keep the OVITO GUI and the
ovitomodule on matching versions when exchanging session files. - Roadmap: a small self-contained example (generated structure, nothing to
download) to try everything on; DXA segment b/ξ re-expressed in each
grain's local frame; grain origins/orientations imported from grain
segmentation / Voronoi tool output; comparison-grid shared colorbar for
multi-property panels; movie polish (frame ranges);
.zstinput; broaderovzm importmodifier coverage; ontology-mapped (JSON-LD) provenance export.
Release files for ovito-auto-viz 0.4.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ovito_auto_viz-0.4.2.tar.gz | 66.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ovito_auto_viz-0.4.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 125.6 kB
Release files / ovito_auto_viz-0.4.2.tar.gz
| Download URL | ovito_auto_viz-0.4.2.tar.gz |
|---|---|
| Size | 66.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0a6541d38fbca5691a5fbaa6d34d03983f35f75d273d7c1595ec577e1c2d2c5e
|
|
BLAKE2b-256 checksum How to use checksums |
8396d6a93e01a7284317fd36f04db49b0233aeb459c77d2697468cbad804b495
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.
Transparency logRelease files / ovito_auto_viz-0.4.2-py3-none-any.whl
| Download URL | ovito_auto_viz-0.4.2-py3-none-any.whl |
|---|---|
| Size | 58.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
630285c1912e6263f67cc67acad4a1d23276a2f5715fe47a9eb1ddab6398ab1d
|
|
BLAKE2b-256 checksum How to use checksums |
d9cf70c39df58dc7fb03375f3ac1d493c6166341642c19cd80137459d098de4d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.
Transparency log