ovito-auto-viz 0.4.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. ovito_auto_viz-0.4.2/CITATION.cff +37 -0
  2. ovito_auto_viz-0.4.2/LICENSE +28 -0
  3. ovito_auto_viz-0.4.2/MANIFEST.in +3 -0
  4. ovito_auto_viz-0.4.2/PKG-INFO +506 -0
  5. ovito_auto_viz-0.4.2/README.md +489 -0
  6. ovito_auto_viz-0.4.2/pyproject.toml +33 -0
  7. ovito_auto_viz-0.4.2/setup.cfg +4 -0
  8. ovito_auto_viz-0.4.2/src/ovito_auto_viz.egg-info/PKG-INFO +506 -0
  9. ovito_auto_viz-0.4.2/src/ovito_auto_viz.egg-info/SOURCES.txt +29 -0
  10. ovito_auto_viz-0.4.2/src/ovito_auto_viz.egg-info/dependency_links.txt +1 -0
  11. ovito_auto_viz-0.4.2/src/ovito_auto_viz.egg-info/entry_points.txt +2 -0
  12. ovito_auto_viz-0.4.2/src/ovito_auto_viz.egg-info/requires.txt +4 -0
  13. ovito_auto_viz-0.4.2/src/ovito_auto_viz.egg-info/top_level.txt +1 -0
  14. ovito_auto_viz-0.4.2/src/ovzm/__init__.py +24 -0
  15. ovito_auto_viz-0.4.2/src/ovzm/card.py +160 -0
  16. ovito_auto_viz-0.4.2/src/ovzm/cli.py +164 -0
  17. ovito_auto_viz-0.4.2/src/ovzm/crystal.py +158 -0
  18. ovito_auto_viz-0.4.2/src/ovzm/grains.py +252 -0
  19. ovito_auto_viz-0.4.2/src/ovzm/grid.py +210 -0
  20. ovito_auto_viz-0.4.2/src/ovzm/importer.py +147 -0
  21. ovito_auto_viz-0.4.2/src/ovzm/labels.py +223 -0
  22. ovito_auto_viz-0.4.2/src/ovzm/pipelinebuild.py +375 -0
  23. ovito_auto_viz-0.4.2/src/ovzm/presets/dxa-standard.yaml +25 -0
  24. ovito_auto_viz-0.4.2/src/ovzm/presets/segregation-map.yaml +20 -0
  25. ovito_auto_viz-0.4.2/src/ovzm/runner.py +472 -0
  26. ovito_auto_viz-0.4.2/src/ovzm/scene.py +383 -0
  27. ovito_auto_viz-0.4.2/src/ovzm/schema/vizcard.schema.json +713 -0
  28. ovito_auto_viz-0.4.2/tests/test_example_physics.py +72 -0
  29. ovito_auto_viz-0.4.2/tests/test_grains.py +312 -0
  30. ovito_auto_viz-0.4.2/tests/test_ovito_compat.py +194 -0
  31. ovito_auto_viz-0.4.2/tests/test_packaging.py +120 -0
@@ -0,0 +1,37 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software, please cite it as below."
3
+ title: ovito-auto-viz
4
+ abstract: >-
5
+ Declarative, reproducible OVITO visualization of LAMMPS files: YAML
6
+ viz-cards rendered to publication-quality images, comparison grids,
7
+ movies, or ready-to-open OVITO sessions, with all crystallographic
8
+ labels (Burgers vectors, line directions, character angles) computed
9
+ from the data itself and full provenance embedded in every output.
10
+ type: software
11
+ authors:
12
+ - family-names: Bitzek
13
+ given-names: Erik
14
+ orcid: "https://orcid.org/0000-0001-7430-3694"
15
+ email: e.bitzek@mpi-susmat.de
16
+ affiliation: >-
17
+ Max-Planck-Institut for Sustainable Materials, Düsseldorf, Germany;
18
+ Institute of Materials Simulation (WW8), Friedrich-Alexander-Universität
19
+ Erlangen-Nürnberg (FAU), Fürth, Germany
20
+ doi: 10.5281/zenodo.21796154
21
+ identifiers:
22
+ - type: doi
23
+ value: 10.5281/zenodo.21796154
24
+ description: Concept DOI, resolves to the latest version
25
+ repository-code: "https://github.com/biterik/ovito-auto-viz"
26
+ url: "https://github.com/biterik/ovito-auto-viz"
27
+ license: BSD-3-Clause
28
+ version: 0.4.2
29
+ date-released: 2026-09-24
30
+ keywords:
31
+ - OVITO
32
+ - LAMMPS
33
+ - molecular dynamics
34
+ - dislocations
35
+ - visualization
36
+ - materials science
37
+ - reproducibility
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Erik Bitzek
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,3 @@
1
+ include LICENSE README.md CITATION.cff
2
+ recursive-include src/ovzm/presets *.yaml
3
+ recursive-include src/ovzm/schema *.json
@@ -0,0 +1,506 @@
1
+ Metadata-Version: 2.4
2
+ Name: ovito-auto-viz
3
+ Version: 0.4.2
4
+ Summary: Declarative, reproducible OVITO visualization of LAMMPS files: YAML viz-cards -> images, movies, or ready-to-open OVITO sessions.
5
+ Author-email: Erik Bitzek <e.bitzek@mpi-susmat.de>
6
+ License: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/biterik/ovito-auto-viz
8
+ Project-URL: Repository, https://github.com/biterik/ovito-auto-viz
9
+ Requires-Python: >=3.9
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: ovito>=3.12
13
+ Requires-Dist: PyYAML>=6.0
14
+ Requires-Dist: jsonschema>=4.0
15
+ Requires-Dist: numpy
16
+ Dynamic: license-file
17
+
18
+ # ovito-auto-viz
19
+
20
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.21796154.svg)](https://doi.org/10.5281/zenodo.21796154)
21
+ [![CI](https://github.com/biterik/ovito-auto-viz/actions/workflows/ci.yml/badge.svg)](https://github.com/biterik/ovito-auto-viz/actions/workflows/ci.yml)
22
+ [![PyPI](https://img.shields.io/pypi/v/ovito-auto-viz)](https://pypi.org/project/ovito-auto-viz/)
23
+ [![License: BSD-3-Clause](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](LICENSE)
24
+
25
+ **Fast, automated, reproducible and FAIR visualization of atomistic
26
+ simulations with [OVITO](https://www.ovito.org/) — self-labeling figures
27
+ with complete metadata and provenance.**
28
+
29
+ You — or the LLM of your choice — describe the figure in a ~20-line YAML
30
+ **viz card**; the `ovzm` CLI turns
31
+ it into a publication-quality image, movie, or ready-to-open OVITO session.
32
+ Everything on the figure — Burgers vectors, line directions, character
33
+ angles, composition, colorbars, per-grain tripods — is **computed from the
34
+ data itself**. And every figure **permanently remembers** who made it, from
35
+ which data file (SHA-256), with which settings, under which funding, and
36
+ which publication it belongs to: the full provenance record is embedded
37
+ inside the PNG. Works hands-on, in scripts, on HPC clusters — and
38
+ conversationally through an LLM agent skill.
39
+
40
+ ![From simulation to figure and provenance in one step](docs/graphical-abstract.png)
41
+
42
+ *The whole story in one picture: a LAMMPS dump and the simulation project's
43
+ context go in; a ~20-line **viz card** — written by you or by an AI agent —
44
+ tells `ovzm render` what the figure is; out come a publication-ready image
45
+ AND its complete provenance, embedded inside the PNG itself.*
46
+
47
+ ## Why
48
+
49
+ Scientific figures take time to make and are amongst the **least FAIR objects** in a publication:
50
+ pixels with no metadata, produced by GUI clicks nobody can repeat, separated
51
+ from their data the moment they are exported. `ovzm` inverts this:
52
+
53
+ - **The figure is a text file.** The card is versionable, diffable,
54
+ reviewable, and re-runs identically on your laptop or a cluster. Same
55
+ card + same data = same figure, years later. The card can even be
56
+ extracted from the figure if it goes missing.
57
+ - **The physics labels itself.** DXA dislocation segments get their Burgers
58
+ vector, line direction, and character angle computed from the data —
59
+ nucleated defects with zero metadata are labeled just as well as
60
+ constructed ones. Composition, colorbar limits, and crystal-axis tripods
61
+ are derived, never typed.
62
+ - **A figure never forgets.** Creator (mandatory — the run aborts without
63
+ it), affiliation, ORCID, project, funding, input file with SHA-256, the
64
+ fully resolved card and scene: all of it is written to a `.prov.yaml`
65
+ sidecar AND embedded into the PNG itself as a compressed text chunk.
66
+ Image and provenance cannot be separated by copying, renaming, emailing,
67
+ or archiving.
68
+ - **Comparisons are honest.** `ovzm grid` renders one card over many inputs
69
+ with identical camera, magnification, styling, and colorbar limits — the
70
+ manual-GUI failure mode this tool exists to kill.
71
+ - **LLM-ready by design.** The card is one shared interface for humans,
72
+ scripts, and AI agents. With the bundled agent skill, *"glide-plane view
73
+ of dump.1530000.gz, Cu segregation, paper quality"* becomes a rendered
74
+ figure — the agent writes the card, collects attribution context from
75
+ your project directory, and asks (never guesses) whatever the data cannot
76
+ provide. The tool itself requires no AI; the skill is an optional adapter
77
+ on top.
78
+
79
+ ## The showcase figure
80
+
81
+ ![W grain-boundary crack with phosphorus segregation](docs/readme-hero.png)
82
+
83
+ *A crack in tungsten runs in from the left, kinks onto a phosphorus-decorated
84
+ grain boundary, and continues along it (bcc W dark gray, defective atoms
85
+ white, P orange, one Miller-labeled tripod per grain — CNA + the
86
+ `structure_colors`/pinned-species styling and the `grains:` tripods, all from
87
+ one card). Configuration from
88
+ [Tian et al., Acta Materialia 259 (2023) 119256](https://doi.org/10.1016/j.actamat.2023.119256).
89
+ The image carries its own receipt — clone the repo and ask it where it comes
90
+ from:*
91
+
92
+ ```bash
93
+ ovzm prov docs/readme-hero.png # prints creator, input file + SHA-256,
94
+ # the resolved card, and the paper's DOI
95
+ ```
96
+
97
+ ## Install
98
+
99
+ `ovzm` is an ordinary Python package (Python ≥ 3.9). Its main dependency,
100
+ the [`ovito`](https://pypi.org/project/ovito/) Python module (free of
101
+ charge, DXA included), is installed **automatically** by pip — you do NOT
102
+ need the OVITO desktop application. Install into a conda/mamba environment
103
+ or a venv, **never** into a system Python (many systems ship no
104
+ user-writable `pip`, and installing one globally is a good way to break the
105
+ OS package manager).
106
+
107
+ **Linux**
108
+
109
+ ```bash
110
+ python -m venv ~/venvs/ovzm && source ~/venvs/ovzm/bin/activate # or: conda activate <env>
111
+ pip install ovito-auto-viz
112
+ ```
113
+
114
+ Headless machines (clusters, CI, containers) additionally need the
115
+ graphics runtime the `ovito` module links against — no GPU or display
116
+ required (rendering uses the Tachyon software ray-tracer). Since ovito 3.16
117
+ the Linux module renders through **Vulkan** and needs a Vulkan *driver*
118
+ even for offscreen renders; Mesa's `lavapipe` software driver does the job
119
+ without a GPU:
120
+
121
+ ```bash
122
+ sudo apt-get install libopengl0 libegl1 libgl1 libglx0 libxkbcommon0 mesa-vulkan-drivers # Debian/Ubuntu
123
+ # Fedora/RHEL: libglvnd-opengl libglvnd-egl libglvnd-glx libxkbcommon mesa-vulkan-drivers
124
+ # openSUSE: ... libvulkan_lvp
125
+ ```
126
+
127
+ If `ovzm render` stops with "Could not initialize the Vulkan graphics
128
+ backend", that driver is what is missing. On a cluster without root, ask the
129
+ admins for it or fall back to `pip install "ovito<3.16"`, which still uses
130
+ OpenGL and needs only the first five packages.
131
+
132
+ On HPC, install into a venv under scratch (never `$HOME`) and run renders
133
+ inside a batch job, not on a login node.
134
+
135
+ **macOS** (Intel and Apple Silicon)
136
+
137
+ ```bash
138
+ conda activate <your-env> # conda-forge env recommended on macOS
139
+ pip install ovito-auto-viz
140
+ ```
141
+
142
+ Known upstream wheel bug: on Apple-silicon Macs `ovito==3.16.1` fails to
143
+ import with `Library not loaded: @loader_path/libospray.3.2.0.dylib` (the
144
+ wheel ships the file only as `libospray.3.dylib`). Either
145
+ `pip install "ovito==3.15.5"`, or add the missing name:
146
+
147
+ ```bash
148
+ ln -s libospray.3.dylib "$(python -c 'import ovito,os;print(os.path.dirname(ovito.__file__))')/plugins/libospray.3.2.0.dylib"
149
+ ```
150
+
151
+ **Windows**
152
+
153
+ The `ovito` module ships Windows wheels, so the same install should work in
154
+ an Anaconda Prompt or venv (not yet routinely tested — reports welcome):
155
+
156
+ ```powershell
157
+ pip install ovito-auto-viz
158
+ ```
159
+
160
+ If anything misbehaves natively, WSL2 + the Linux instructions above is the
161
+ reliable fallback.
162
+
163
+ **Check the install** — this must print JSON:
164
+
165
+ ```bash
166
+ ovzm schema | head -3
167
+ ```
168
+
169
+ The latest development version comes straight from GitHub:
170
+ `pip install git+https://github.com/biterik/ovito-auto-viz.git` — and for
171
+ development, install editable from a clone:
172
+ `pip install -e /absolute/path/to/ovito-auto-viz`.
173
+
174
+ ## Quickstart
175
+
176
+ ```yaml
177
+ # my-figure.yaml
178
+ name: d90-glideplane
179
+ extends: dxa-standard # preset: PTM + DXA + auto labels
180
+ input:
181
+ file: dump_min_sgcmc_d90_T300.1530000.gz
182
+ crystal:
183
+ x: [-1, 0, 1] # or "auto" -> parsed from x-101_y1-21_z111 filenames
184
+ y: [1, -2, 1]
185
+ z: [1, 1, 1]
186
+ lattice: fcc
187
+ view:
188
+ direction: top # named view, or a Miller direction like [111]
189
+ atoms:
190
+ show: non_fcc
191
+ names: {1: Ni, 2: Cu}
192
+ colors: {1: [0.62, 0.66, 0.72], 2: [0.90, 0.45, 0.10]}
193
+ ```
194
+
195
+ ```bash
196
+ ovzm render my-figure.yaml # -> <input>__<name>.png + .prov.yaml sidecar
197
+ ovzm grid comparison.yaml # same card over grid.inputs -> one labeled grid
198
+ ovzm session my-figure.yaml # -> .ovito file: open in the GUI, pipeline + camera preset
199
+ ovzm import something.ovito # best-effort reverse: GUI session -> card YAML
200
+ ovzm info dump.gz # types, box, frames, filename-parsed orientation
201
+ ovzm prov figure.png # print the provenance embedded in the image
202
+ ovzm validate my-figure.yaml # schema check with readable errors
203
+ ovzm schema # print the JSON schema
204
+ ```
205
+
206
+ ## Try it in five minutes
207
+
208
+ `examples/try-it/` is fully self-contained — nothing to download: a
209
+ numpy-only script generates a small fcc Ni box with four edge dislocations
210
+ (a quadrupole — a closed system with **net b = 0**), and one card renders it
211
+ with every label computed from the positions alone:
212
+
213
+ ```bash
214
+ cd examples/try-it
215
+ python make_edge_dipoles.py # ~14k atoms, a few seconds
216
+ ovzm render edge-dipoles.yaml --ask # asks once who you are — attribution is mandatory
217
+ ovzm prov edge-dipoles__edge-dipoles.png
218
+ ```
219
+
220
+ See [`examples/try-it/README.md`](examples/try-it/README.md) for the
221
+ walkthrough and the expected (computed) label block;
222
+ `tests/test_example_physics.py` asserts the DXA ground truth of this
223
+ example in CI.
224
+
225
+ ## Using it with an LLM (the agent skill)
226
+
227
+ `skills/ovito-auto-viz/SKILL.md` is an
228
+ [Agent Skill](https://github.com/anthropics/skills): a plain instruction
229
+ file (no code) that teaches a Claude session when to use `ovzm`, how to
230
+ compose cards from the presets, and — crucially — what it must **ask**
231
+ instead of guessing (species names, crystal orientation, figure creator,
232
+ colorbar units). Attribution context is collected automatically from the
233
+ project directory (`ovzm-project.yaml`, personal identity file, filename
234
+ conventions), so a one-line request becomes a rendered, fully attributed
235
+ figure plus the card to version with your project. The tool itself never
236
+ requires any AI — the skill is an optional adapter on top.
237
+
238
+ Install:
239
+
240
+ - **Claude Code** (CLI): copy the skill folder into your skills directory —
241
+
242
+ ```bash
243
+ mkdir -p ~/.claude/skills
244
+ cp -R skills/ovito-auto-viz ~/.claude/skills/
245
+ ```
246
+
247
+ It then triggers automatically on requests like "render the standard DXA
248
+ view of this dump" in any project where `ovzm` is installed.
249
+
250
+ - **Claude Desktop / Cowork**: add the skill via the app's
251
+ Settings → Capabilities/Skills (upload or point it at
252
+ `skills/ovito-auto-viz/`), or ask Claude in a session to package
253
+ `SKILL.md` as an installable `.skill` file for your account.
254
+
255
+ Usage is then conversational: *"glide-plane view of
256
+ `SGCMC-D90/dump.1530000.gz`, Cu segregation, paper quality"* — the session
257
+ writes the card (asking for whatever the required-information table says it
258
+ cannot detect), runs `ovzm render`, and shows you the PNG plus the card so
259
+ you can version it with the project.
260
+
261
+ ## Attribution of figures
262
+
263
+ Every figure records who made it. `meta.creator` is **mandatory** (the run
264
+ aborts without it); `project`, `funding`, `affiliation`, `email`, `orcid`
265
+ are optional. Because people work on several projects with different
266
+ funding and even different affiliations, attribution is resolved
267
+ **per key, git-config style**:
268
+
269
+ 1. the card's own `meta:` block (most specific),
270
+ 2. an **`ovzm-project.yaml`** found by walking UP the directory tree from
271
+ the card and from the input data — put one in each simulation project
272
+ root (e.g. `SIMULATIONS/EAM-DISLOCS-Ni-Cu/ovzm-project.yaml`); a file in
273
+ a subdirectory (thread) overrides the project root's,
274
+ 3. a personal `~/.config/ovzm/identity.yaml` (or `$OVZM_IDENTITY`) —
275
+ typically just `creator: Jane Doe`,
276
+ 4. `--ask` prompts interactively as the last resort (creator only).
277
+
278
+ A project file for a single-user project:
279
+
280
+ ```yaml
281
+ # SIMULATIONS/EAM-DISLOCS-Ni-Cu/ovzm-project.yaml
282
+ project: EAM-DISLOCS-Ni-Cu
283
+ funding: NFDI-MatWerk (DFG 460247524)
284
+ creator: Erik Bitzek
285
+ people:
286
+ Erik Bitzek:
287
+ affiliation: MPI for Sustainable Materials, Duesseldorf / FAU WW8, Fuerth
288
+ orcid: 0000-0001-7430-3694
289
+ ```
290
+
291
+ For multi-user projects, omit the top-level `creator` (each user's identity
292
+ file supplies their name) and list everyone under `people:` — the resolved
293
+ creator's entry fills in their affiliation/email/ORCID for that project.
294
+ All resolved values land in the `.prov.yaml` and inside the PNG; every YAML
295
+ the tool writes carries the tool-credit header ("created with
296
+ ovito-auto-viz … funded by NFDI-MatWerk").
297
+
298
+ ## Reading the provenance inside a PNG
299
+
300
+ The embedded record is a standard PNG text chunk (key `ovzm_prov`), so it
301
+ is accessible with or without this tool:
302
+
303
+ ```bash
304
+ ovzm prov figure.png # with ovito-auto-viz installed
305
+ ovzm prov figure.png > figure.prov.yaml # ... e.g. to regenerate a lost sidecar
306
+ ```
307
+
308
+ Without `ovzm`, two lines of Python (Pillow):
309
+
310
+ ```python
311
+ from PIL import Image
312
+ print(Image.open("figure.png").text["ovzm_prov"])
313
+ ```
314
+
315
+ or on the command line with common metadata tools:
316
+
317
+ ```bash
318
+ exiftool -b -Ovzm_prov figure.png # exiftool
319
+ identify -verbose figure.png # ImageMagick: listed under Properties
320
+ ```
321
+
322
+ The chunk survives copying, renaming, emailing, and archiving — anything
323
+ that treats the file as bytes. It does NOT survive operations that
324
+ re-encode the pixels: screenshots, format conversion (PNG→JPEG), or
325
+ "export/save for web" in image editors. For a figure that will be
326
+ re-encoded (e.g. embedded in a PDF), keep the `.prov.yaml` sidecar
327
+ alongside — it is the identical record.
328
+
329
+ ## Comparison grids
330
+
331
+ `ovzm grid` renders the SAME card over several inputs (`grid.inputs`,
332
+ optional `grid.labels`/`grid.cols`) into one figure with **identical camera,
333
+ magnification, styling, and colorbar limits** across panels — the manual-GUI
334
+ failure mode this tool exists to kill. Tripod and legend are drawn once
335
+ (first panel; `grid.tripod: all` for every panel); each panel carries only
336
+ its title, and the per-panel analysis results (composition, DXA, …) live in
337
+ the grid's provenance under `panels:`.
338
+
339
+ ## Per-grain tripods (bicrystals, polycrystals)
340
+
341
+ A `grains:` block draws one labeled coordinate tripod per grain — anchored at
342
+ a user-provided origin, oriented by user-provided Miller triplets (`x`/`y`/`z`
343
+ are the crystal directions of *this grain* lying along the sim-box axes, same
344
+ semantics as the `crystal:` block). Origins and orientations are **never
345
+ guessed**; give them inline or via an external grains file (`file:` XOR
346
+ `items:`):
347
+
348
+ ```yaml
349
+ grains:
350
+ file: grains-d005.yaml # EITHER external file (path relative to the card)
351
+ items: # OR inline — exactly one of the two
352
+ - name: upper # optional; defaults g1, g2, ...
353
+ origin: [12.0, 40.0, 88.5] # Å, cartesian, simulation frame
354
+ x: [-1, 0, 1]
355
+ y: [1, -2, 1]
356
+ z: [1, 1, 1]
357
+ tripod: # styling, all optional
358
+ size: 20 # arm length in Å (default: 5% of the largest box edge, min 10 Å)
359
+ axes: box # 'box' (arms along the box axes, labeled with each
360
+ # grain's x/y/z) or 3 Miller triplets drawn in each
361
+ # grain's own frame, e.g. [[1,0,0],[0,1,0],[0,0,1]]
362
+ show_names: true
363
+ ```
364
+
365
+ The external grains file is a YAML mapping with the same `grains:` list —
366
+ write it by hand, from a Voronoi construction, or from a segmentation tool
367
+ (float triplets are accepted). Arms are drawn on top of the atoms with
368
+ world-anchored length, so tripods stay visible inside dense grains and come
369
+ out identical on every `ovzm grid` panel. The resolved grains (plus the
370
+ grains-file SHA-256) land in the provenance like everything else.
371
+ `tests/make_bicrystal.py` generates a Σ5 [001] bicrystal + grains file to
372
+ try it on. (The hero image above uses exactly this feature — one tripod per
373
+ grain of the W bicrystal.)
374
+
375
+ ## Required minimum information
376
+
377
+ `ovzm` refuses to guess what cannot be auto-detected. The contract:
378
+
379
+ | info | auto-detected from | if not detectable |
380
+ |------|--------------------|-------------------|
381
+ | input file | — | always required in the card |
382
+ | 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 |
383
+ | crystal orientation | `x-101_y1-21_z111`-style filenames | b/ξ labels fall back to DXA's lattice frame, flagged in the label block |
384
+ | grain origins/orientations (tripods) | — | **hard error** if a `grains:` block is incomplete — never guessed |
385
+ | figure creator | ovzm-project.yaml / identity file | **hard error** — attribution is not optional |
386
+ | lattice (for DXA) | — | preset default (`fcc` in `dxa-standard`); set `crystal.lattice` |
387
+ | view | — | preset default (`front`); set `view.direction` |
388
+ | what is shown | — | preset default (`non_fcc` in `dxa-standard`); set `atoms.show` |
389
+ | colorbar units | — | set `annotate.colorbar.units`; limits are resolved automatically and recorded in the prov file |
390
+
391
+ `ovzm info <file>` prints what a file does and does not carry (types, frames,
392
+ box, filename-encoded orientation) — use it before writing a card.
393
+
394
+ ## Naming convention
395
+
396
+ Every figure has an ID: `<input-stem>__<card-name>[__<tag>]`. The card name
397
+ encodes the figure's identity (view + analysis, e.g. `d90-dxa-top`); the
398
+ optional `output.tag` distinguishes variants. Image and provenance sidecar
399
+ share the ID exactly:
400
+
401
+ ```
402
+ dump_min_sgcmc_d90.1530000__d90-dxa-top.png
403
+ dump_min_sgcmc_d90.1530000__d90-dxa-top.png.prov.yaml
404
+ ```
405
+
406
+ Multiple figures from the same input differ in card name; the same card on
407
+ multiple inputs differs in stem. The `.prov.yaml` records the ID, input
408
+ SHA-256, the resolved card, AND the fully resolved scene: per-type species
409
+ names / colors / radii, colorbar limits + units, camera, composition, and the
410
+ complete DXA result (per-segment b, ξ, character, net Burgers vector, and the
411
+ measured lattice-constant estimate). The image itself can therefore stay
412
+ visually clean — nothing is lost as long as the sidecar travels with it.
413
+
414
+ ## What gets labeled automatically
415
+
416
+ With `annotate.labels: auto` (the default in `dxa-standard`), the label block
417
+ is composed from the data: timestep; total composition (e.g. `x(Cu) = 0.0100`)
418
+ plus shown-atom counts; DXA segment count and total line length; per-family
419
+ breakdown (perfect, Shockley, stair-rod, Hirth, Frank); the **net Burgers
420
+ vector with its character** (`net b = 1/2[-101], 90° edge`); and per-segment
421
+ `b = 1/6[-211] (Shockley partial), ξ = [1-21], 60° mixed, L = 100 Å`.
422
+
423
+ Frame discipline: DXA's `true_burgers_vector` lives in DXA's own lattice
424
+ frame, which can differ from the card's crystal frame by a cubic symmetry
425
+ operation. All geometry (Miller indices in the card frame, line directions,
426
+ character angles) is therefore computed from the segment's *spatial* Burgers
427
+ vector; the true vector is used only for family classification and the
428
+ |b_spatial|/|b_true| lattice-constant estimate. Character names are
429
+ conservative: `edge` ≥ 85°, `screw` ≤ 5°, otherwise the angle plus `mixed`.
430
+ If a dcreator `.disparam` is around it can serve as a cross-check, but
431
+ nothing requires metadata — nucleated dislocations label identically.
432
+
433
+ The coordinate tripod is labeled with the crystal axes (`x=[-101]`, …) and a
434
+ `view.fit_margin` (default 1.15) keeps all overlays outside the projected
435
+ cell. **Whenever atoms carry color information there is a legend**: a
436
+ continuous colorbar for `color_coding` (limits resolved from the shown data
437
+ when `range: auto`, units from `annotate.colorbar.units`), or a discrete
438
+ per-type legend when coloring by species.
439
+
440
+ ## Presets and styling
441
+
442
+ Cards inherit via `extends:` (chains allowed; search path: `$OVZM_PRESET_PATH`,
443
+ then `./presets/`, then the presets bundled with the package in
444
+ `src/ovzm/presets/`). Shipped:
445
+
446
+ - `dxa-standard` — PTM + DXA, non-fcc atoms only, Miller tripod, auto labels.
447
+ - `segregation-map` — all atoms colored by type, solute rendered larger.
448
+
449
+ Output quality presets: `draft` (1280×720, fast), `slide` (1920×1080),
450
+ `paper` (3200×2400, ambient occlusion) — override `width`/`height` freely
451
+ (the hero above is `paper` at 1600×1600). Background `white`/`black`/
452
+ `transparent`. Mixed coloring is a card key away: `color_by: structure` with
453
+ `atoms.structure_colors` (e.g. bcc dark gray, defective atoms white) while
454
+ types listed in `atoms.colors` stay pinned to their species color — that is
455
+ how the hero shows a structure-colored W matrix with orange P solutes.
456
+
457
+ ## Multimillion-atom workflow
458
+
459
+ The intended pattern for big data is **analyze once, render many**: the
460
+ expensive step is PTM/DXA, not rendering. Typical defect views delete the
461
+ fcc/bulk atoms (`atoms.show: non_fcc`), which cuts the rendered particle count
462
+ by ~100×. For files too large for a laptop, run the same card on the cluster
463
+ headless inside a batch job; only PNGs come back. Positions at `%.4f` Å are
464
+ well above every OVITO method's noise floor.
465
+
466
+ ## Documentation
467
+
468
+ - `docs/SCHEMA.md` — every card key, generated from
469
+ `src/ovzm/schema/vizcard.schema.json` (regenerate with
470
+ `python tools/gen-schema-md.py`).
471
+ - Put `# yaml-language-server: $schema=<path>/vizcard.schema.json` at the top
472
+ of a card for editor autocompletion.
473
+ - `skills/ovito-auto-viz/SKILL.md` — the agent skill (see above).
474
+
475
+ ## Author, funding, citation
476
+
477
+ **Author:** Erik Bitzek ([ORCID 0000-0001-7430-3694](https://orcid.org/0000-0001-7430-3694),
478
+ <e.bitzek@mpi-susmat.de>) — ¹ Max-Planck-Institut for Sustainable Materials,
479
+ Düsseldorf, Germany; ² Institute of Materials Simulation (WW8),
480
+ Friedrich-Alexander-Universität Erlangen-Nürnberg (FAU), Fürth, Germany.
481
+ Built with Claude/LLM assistance.
482
+
483
+ **Funding:** Deutsche Forschungsgemeinschaft (DFG, German Research
484
+ Foundation) — NFDI 38/1, project number 460247524
485
+ (**[NFDI-MatWerk](https://nfdi-matwerk.de) consortium**).
486
+
487
+ **License:** BSD-3-Clause. **Citation:** see [`CITATION.cff`](CITATION.cff)
488
+ (GitHub's "Cite this repository" button).
489
+
490
+ ## Known issues / roadmap
491
+
492
+ - **Sessions carry overlays only with ovito ≥ 3.16.1**: older modules
493
+ (observed on 3.15.5) write corrupt `.ovito` files when viewport overlays
494
+ are in the scene, so there `ovzm session` skips them (grain tripods
495
+ included) and says so; the pipeline, styling and camera are always saved.
496
+ Use `ovzm render` for annotated output on older versions. The
497
+ provenance records which case applied (`session_overlays`).
498
+ - Keep the OVITO GUI and the `ovito` module on matching versions when
499
+ exchanging session files.
500
+ - Roadmap: a small self-contained example (generated structure, nothing to
501
+ download) to try everything on; DXA segment b/ξ re-expressed in each
502
+ grain's local frame; grain origins/orientations imported from grain
503
+ segmentation / Voronoi tool output; comparison-grid shared colorbar for
504
+ multi-property panels; movie polish (frame ranges); `.zst` input; broader
505
+ `ovzm import` modifier coverage; ontology-mapped (JSON-LD) provenance
506
+ export.