Skip to content

feat: structured trajectory output + visualization tutorial#331

Open
HaoZeke wants to merge 25 commits intoTheochemUI:mainfrom
HaoZeke:feat/structured-trajectory-output
Open

feat: structured trajectory output + visualization tutorial#331
HaoZeke wants to merge 25 commits intoTheochemUI:mainfrom
HaoZeke:feat/structured-trajectory-output

Conversation

@HaoZeke
Copy link
Copy Markdown
Collaborator

@HaoZeke HaoZeke commented Mar 25, 2026

Add per-iteration .dat output for minimization and saddle search when write_movies=true:

  • HelperFunctions.cpp: Writes {prefix}.dat TSV alongside movie .con files. Columns: iteration, step_size, convergence, energy.
  • MinModeSaddleSearch.cpp: Writes climb.dat TSV alongside climb.con. Columns: iteration, step_size, delta_e, convergence, eigenvalue, torque, angle, rotations.

Add visualization tutorial (tutorials/visualization.md) with figures:

  • Minimization: profile, convergence panel, 2D optimization landscape (Pt heptamer, Morse)
  • Saddle search: profile, convergence panel, 2D optimization landscape (Pt heptamer, dimer)
  • NEB: energy profile, 2D reaction valley projection (Diels-Alder, PET-MAD)

Figures generated from real eOn runs, however given the newest con-file spec this may be reworked after #307.

Needs:

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 25, 2026

eOn Documentation Preview

Download: documentation.zip

Unzip and open index.html to view.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 26, 2026

Benchmark Results

Note

All benchmarks unchanged

Count
⚪ Unchanged 8
8 unchanged benchmark(s)
Benchmark Before After Ratio
bench_eonclient.TimeMinimizationLJCluster.peakmem_minimization_lbfgs 27.2M 27M ~1x
bench_eonclient.TimeMinimizationLJCluster.time_minimization_lbfgs 25.9±0ms 25.9±0ms ~1x
bench_eonclient.TimeNEBMorsePt.peakmem_neb 27.2M 27M ~1x
bench_eonclient.TimeNEBMorsePt.time_neb 252±0ms 245±0ms ~0.97x
bench_eonclient.TimePointMorsePt.peakmem_point_evaluation 27.2M 27M ~1x
bench_eonclient.TimePointMorsePt.time_point_evaluation 8.63±0ms 8.15±0ms ~0.95x
bench_eonclient.TimeSaddleSearchMorseDimer.peakmem_saddle_search_dimer 27.2M 27M ~1x
bench_eonclient.TimeSaddleSearchMorseDimer.time_saddle_search_dimer 56.9±0ms 52.4±0ms ~0.92x
Details
  • Base: 69d87ab1
  • Head: 6b45f67e
  • Runner: ubuntu-22.04
Raw asv-spyglass output
All benchmarks:

| Change   | Before   | After    |   Ratio | Benchmark (Parameter)                                                  |
|----------|----------|----------|---------|------------------------------------------------------------------------|
|          | 27.2M    | 27M      |    1    | bench_eonclient.TimeMinimizationLJCluster.peakmem_minimization_lbfgs   |
|          | 25.9±0ms | 25.9±0ms |    1    | bench_eonclient.TimeMinimizationLJCluster.time_minimization_lbfgs      |
|          | 27.2M    | 27M      |    1    | bench_eonclient.TimeNEBMorsePt.peakmem_neb                             |
|          | 252±0ms  | 245±0ms  |    0.97 | bench_eonclient.TimeNEBMorsePt.time_neb                                |
|          | 27.2M    | 27M      |    1    | bench_eonclient.TimePointMorsePt.peakmem_point_evaluation              |
|          | 8.63±0ms | 8.15±0ms |    0.95 | bench_eonclient.TimePointMorsePt.time_point_evaluation                 |
|          | 27.2M    | 27M      |    1    | bench_eonclient.TimeSaddleSearchMorseDimer.peakmem_saddle_search_dimer |
|          | 56.9±0ms | 52.4±0ms |    0.92 | bench_eonclient.TimeSaddleSearchMorseDimer.time_saddle_search_dimer    |

@HaoZeke HaoZeke force-pushed the feat/structured-trajectory-output branch from 410617d to 5a19e63 Compare March 27, 2026 19:40
@HaoZeke HaoZeke force-pushed the feat/structured-trajectory-output branch 3 times, most recently from c273140 to ada5ac5 Compare April 20, 2026 00:06
HaoZeke added 9 commits April 20, 2026 02:18
When write_movies = true is set in [Debug], emit TSV-formatted
trajectory data alongside the .con movie files:

- HelperFunctions: per-iteration {prefix}.dat next to each minimization
  movie. Columns: iteration, step_size, convergence, energy.
- MinModeSaddleSearch: climb.dat next to climb.con. Columns: iteration,
  step_size, delta_e, convergence, eigenvalue, torque, angle, rotations.

These structured outputs feed the rgpycrumbs/chemparseplot tutorial
toolchain: 2D reaction-valley projections, convergence panels, and
energy profiles for minimization, saddle search, and NEB.
- pixi.toml: bump readcon ^0.4 -> ^0.7 (renames is_fixed -> fixed in
  the parsed atom struct), add sccache, add docs-mta environment that
  combines docs+develop+metatomic features for the tutorial notebook,
  add ira>=2 to docs/linux-64 for IRA-based plotting, swap myst-parser
  for myst-nb (>=1.1) so visualization.md can execute notebook cells,
  add meson-python (so 'pip install -e' inside the docs env can build
  the eon Python package without build isolation), and pin the
  visualization toolchain (rgpycrumbs[analysis] >=1.5,
  chemparseplot[neb,plot] >=1.5.4, adjustText).
- eon/fileio.py: readcon 0.7 renamed Atom.is_fixed -> Atom.fixed.
- pixi.lock: regenerated for the new dep set.

NOTE: adjustText is declared explicitly because rgpycrumbs's analysis
extras don't currently pull it in (rgpycrumbs/eon/plt_neb.py imports
it at module top level).
Adds an executable MyST-NB notebook (docs/source/tutorials/
visualization.md) that runs eOn with PET-MAD on HCN isomerization,
then renders energy profiles, convergence panels, and 2D reaction
landscapes via rgpycrumbs and chemparseplot. Figures regenerate on
every docs build.

- visualization.md: jupytext-format MyST notebook covering minimization,
  saddle search, and NEB with 2D landscape projections.
- tutorials/data/{reactant,product}.con: HCN endpoint structures.
- tutorials/index.md, user_guide/{minimization,saddle_search}.md:
  cross-link the new tutorial.
- conf.py: enable myst_nb, configure cell-execution timeout, surface
  notebook tracebacks for CI.
- ci_docs.yml: install docs-mta env, set LD_LIBRARY_PATH so the
  eonclient subprocess inside the notebook can find libmetatensor and
  libtorch shared objects, and patch the ipykernel spec for myst-nb.
cbindgen is needed by the readcon-core Rust subproject during meson
setup. It is not packaged for conda-forge or PyPI, so cargo install
is the only path. Two improvements over the prior workaround:

- ensure_cbindgen now installs into $CONDA_PREFIX so the binary lives
  in the active env's bin (auto-on-PATH for any subsequent 'pixi run'
  command in the same env). Previously cargo's default ~/.cargo/bin
  was outside the env PATH, making the binary invisible to
  meson-via-pixi-run unless users manually set PATH.
- setupeon depends-on ensure_cbindgen so 'pixi r mkeon release' (and
  any direct pixi r setupeon invocation) auto-bootstraps cbindgen
  before meson setup runs.

CI workflows that explicitly call 'pixi run ensure_cbindgen' before
meson setup continue to work unchanged.
…slinks

The previous tutorial NEB config (7 images, converged_force=0.01, no
energy-weighted springs, no SIDPP, ad-hoc OCI tuning) didn't converge
for HCN -> HNC isomerization with PET-MAD-XS. Replace with the canonical
HCN settings used in HaoZeke/eon_orchestrator examples/hcn_isom (which
produces a clean ~1.8 eV barrier in production):

- 18 images
- SIDPP path initialization (sidpp_growth_alpha=0.33)
- energy-weighted springs (ew_ksp_min=0.972, ew_ksp_max=9.72)
- climbing image with climbing_image_converged_only=true
- OCI-NEB MMF refinement (Goswami et al. 2026)
- converged_force=0.0514221 (10^-3 Ha/Bohr, standard chemistry precision)

Also add a 'See also' section linking the canonical workflows for
producing your own NEB trajectories beyond this plotting tutorial:

- lab-cosmo/atomistic-cookbook eon-pet-neb (PET-MAD walkthrough w/ ASE)
- HaoZeke/eon_orchestrator (Snakemake batch workflow w/ IRA, plotting)
readcon-core's ConFrameWriter::from_path uses std::fs::File::create,
which truncates the destination on construction. Calling matter2con
once per minimization or NEB iteration therefore reconstructed the
writer every time and discarded the previously-written frames, leaving
movie .con files with only the most recent frame on disk regardless of
the iteration count.

Cache one ConFrameWriter per filename in a static map so the file
handle survives across iterations. Each subsequent call streams a
single new ConFrame through the existing writer, reusing the same
open file descriptor and appending naturally without truncation. The
writer's WriterDeleter (free_rkr_writer) closes the file when the
unique_ptr goes out of scope or is replaced by a non-append call.

This fixes the regression introduced in TheochemUI#307 (readcon-core migration)
where minimization/NEB movies showed 1-2 frames instead of one per
iteration, breaking 2D landscape plots that need at least three
trajectory points.
The HCN/HNC isomerization the tutorial originally used did not converge
cleanly with PET-MAD-XS at the 0.0514 eV/A chemistry threshold (3-atom
system + SIDPP grew images into unphysical regions and the OCI-NEB then
collapsed the path). Switch the live demo to the vinyl-alcohol ->
acetaldehyde keto-enol tautomerization that eon_orchestrator validated
against PET-MAD-XS in production (clean ~2 eV barrier in ~50 OCI-NEB
iterations on 8 atoms, 17 images).

- Reactant/product structures replaced with the orchestrator's
  pre-aligned, pre-minimized vinyl alcohol / acetaldehyde geometries.
- Notebook prose, comments, and the eon_orchestrator crosslink updated
  to reference vinyl_alcohol/.
- Minimization converged_force loosened to 0.0514221 eV/A (10^-3
  Ha/Bohr) so the perturbed minimization completes in <50 iter and the
  optimization landscape plot has enough trajectory points without
  hitting the optimizer's 200-iter ceiling.
The plt_neb CLI defaults to ira_kmax=1.8 (tight alignment cutoff used
during NEB endpoint alignment) and caches IRA-projected RMSD results
to .neb_landscape.parquet in the working directory. Two consequences
for the tutorial:

- ira_kmax=1.8 is the wrong value for the landscape projection; the
  orchestrator's reference config uses kmax=14 (permissive rotation
  matching) so the trajectory and MMF peaks land on the proper (s, d)
  reaction-valley frame instead of being pushed to the viewport edges.
- The parquet cache is keyed only on the dat/con file list, not on
  ira_kmax, so rerunning with a different kmax silently reuses stale
  results. --force-recompute sidesteps this.

With these flags the 2D landscape for the vinyl alcohol -> acetaldehyde
demo converges to the same shape as eon_orchestrator/results/plots/
vinyl_alcohol/2D_rmsd.png: all ~874 trajectory points (46 iterations
x 19 images) visible, SP captured at the top of the reaction valley,
s axis ~0-1 A, d axis about +/-0.6 A.
…smaller insets)

Three more knobs to align with the orchestrator's reference invocation:

- --rc-mode rmsd: profile plot's reaction coordinate becomes IRA RMSD
  from R (the orchestrator's reference shape) rather than the default
  cumulative path length, so the x-axis matches the landscape's s-axis.
- --zoom-ratio 0.15 (profile) / 0.2 (landscape): the default 0.4 inset
  scaling produced enormous structure overlays that crowded the plot
  area. Smaller values match the orchestrator's published figures.
- The landscape now shows all 874 trajectory points (46 iterations x
  19 images) clearly, with the saddle visible at the top of the
  reaction valley around s~0.5 A, d~0.5 A.
@HaoZeke HaoZeke force-pushed the feat/structured-trajectory-output branch from ada5ac5 to 55b0102 Compare April 20, 2026 00:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant