Visualizing Optimization Trajectories

eOn can embed structured per-iteration metadata directly into trajectory movie frames when write_movies = true is set in the [Debug] section of config.ini. These metadata-rich .con movies enable rich 2D reaction-valley projections and convergence plots via rgpycrumbs and chemparseplot.

For one compatibility window, write_deprecated_outs = true can also be used to emit the older minimization and saddle-search .dat sidecars. NEB continues to write its existing neb.dat / neb_*.dat outputs alongside the .con movies.

This tutorial walks through two complete workflows with PET-MAD:

  • a single-ended minimization of a perturbed vinyl alcohol structure

  • an OCI-NEB calculation for vinyl alcohol -> acetaldehyde

For each workflow we run eonclient, then visualize the result through the public rgpycrumbs dispatcher so the tutorial matches current command-line usage.

Setup

Minimization

We start by minimizing a slightly distorted vinyl alcohol molecule to demonstrate the single-ended optimization outputs consumed by rgpycrumbs eon plt-min.

Energy profile

min_profile = plot_dir / "min_profile.png"
run_rgpycrumbs(
    "eon", "plt-min",
    "--job-dir", str(min_dir),
    "--prefix", "minimization",
    "--plot-type", "profile",
    "--dpi", "150",
    "--output", str(min_profile),
)
show_plot(min_profile)
$ /home/runner/work/eOn/eOn/.pixi/envs/docs-mta/bin/python3.12 -m rgpycrumbs.cli eon plt-min --job-dir /tmp/eon_tutorial_mccjsfde/minimization --prefix minimization --plot-type profile --dpi 150 --output /tmp/eon_tutorial_mccjsfde/plots/min_profile.png
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
Cell In[5], line 2
      1 min_profile = plot_dir / "min_profile.png"
----> 2 run_rgpycrumbs(
      3     "eon", "plt-min",
      4     "--job-dir", str(min_dir),
      5     "--prefix", "minimization",
      6     "--plot-type", "profile",
      7     "--dpi", "150",
      8     "--output", str(min_profile),
      9 )
     10 show_plot(min_profile)

Cell In[3], line 30, in run_rgpycrumbs(group, command, cwd, timeout, *args)
     24 def run_rgpycrumbs(group, command, *args, cwd=None, timeout=600):
     25     # 600s caps first-call rgpycrumbs overhead on the weakest GHA runner.
     26     # Landscape plots (grad_matern / grad_imq surface fits) dominate wall
     27     # time; profile and convergence panels land well under a minute locally,
     28     # but cold matplotlib + CLI import on ubuntu-latest has been observed
     29     # past the old 180s ceiling.
---> 30     return run_cli_or_raise(
     31         [sys.executable, "-m", "rgpycrumbs.cli", group, command, *args],
     32         cwd=cwd,
     33         timeout=timeout,
     34     )

Cell In[3], line 8, in run_cli_or_raise(args, cwd, timeout)
      6 def run_cli_or_raise(args, *, cwd=None, timeout=300):
      7     print("$", " ".join(args))
----> 8     result = subprocess.run(
      9         args,
     10         cwd=cwd,
     11         capture_output=True,
     12         text=True,
     13         timeout=timeout,
     14     )
     15     if result.stdout:
     16         print(result.stdout)

File ~/work/eOn/eOn/.pixi/envs/docs-mta/lib/python3.12/subprocess.py:550, in run(input, capture_output, timeout, check, *popenargs, **kwargs)
    548 with Popen(*popenargs, **kwargs) as process:
    549     try:
--> 550         stdout, stderr = process.communicate(input, timeout=timeout)
    551     except TimeoutExpired as exc:
    552         process.kill()

File ~/work/eOn/eOn/.pixi/envs/docs-mta/lib/python3.12/subprocess.py:1209, in Popen.communicate(self, input, timeout)
   1206     endtime = None
   1208 try:
-> 1209     stdout, stderr = self._communicate(input, endtime, timeout)
   1210 except KeyboardInterrupt:
   1211     # https://bugs.python.org/issue25942
   1212     # See the detailed comment in .wait().
   1213     if timeout is not None:

File ~/work/eOn/eOn/.pixi/envs/docs-mta/lib/python3.12/subprocess.py:2115, in Popen._communicate(self, input, endtime, orig_timeout)
   2108     self._check_timeout(endtime, orig_timeout,
   2109                         stdout, stderr,
   2110                         skip_check_and_raise=True)
   2111     raise RuntimeError(  # Impossible :)
   2112         '_check_timeout(..., skip_check_and_raise=True) '
   2113         'failed to raise TimeoutExpired.')
-> 2115 ready = selector.select(timeout)
   2116 self._check_timeout(endtime, orig_timeout, stdout, stderr)
   2118 # XXX Rewrite these to use non-blocking I/O on the file
   2119 # objects; they are no longer using C stdio!

File ~/work/eOn/eOn/.pixi/envs/docs-mta/lib/python3.12/selectors.py:415, in _PollLikeSelector.select(self, timeout)
    413 ready = []
    414 try:
--> 415     fd_event_list = self._selector.poll(timeout)
    416 except InterruptedError:
    417     return ready

KeyboardInterrupt: 

2D optimization landscape

min_landscape = plot_dir / "min_landscape.png"
run_rgpycrumbs(
    "eon", "plt-min",
    "--job-dir", str(min_dir),
    "--prefix", "minimization",
    "--plot-type", "landscape",
    "--project-path",
    "--surface-type", "grad_matern",
    "--plot-structures", "endpoints",
    "--strip-renderer", "xyzrender",
    "--perspective-tilt", "8",
    "--dpi", "150",
    "--output", str(min_landscape),
)
show_plot(min_landscape)

Convergence panel

min_convergence = plot_dir / "min_convergence.png"
run_rgpycrumbs(
    "eon", "plt-min",
    "--job-dir", str(min_dir),
    "--prefix", "minimization",
    "--plot-type", "convergence",
    "--dpi", "150",
    "--output", str(min_convergence),
)
show_plot(min_convergence)

Nudged Elastic Band

We run an OCI-NEB calculation on the vinyl alcohol -> acetaldehyde keto-enol tautomerization using PET-MAD.

Energy profile

neb_profile = plot_dir / "neb_profile.png"
run_rgpycrumbs(
    "eon", "plt-neb",
    "--input-dat-pattern", str(neb_dir / "neb_*.dat"),
    "--con-file", str(neb_dir / "neb.con"),
    "--ira-kmax", "14",
    "--force-recompute",
    "--rc-mode", "rmsd",
    "--plot-type", "profile",
    "--plot-structures", "crit_points",
    "--strip-renderer", "xyzrender",
    "--perspective-tilt", "8",
    "--zoom-ratio", "0.15",
    "--dpi", "150",
    "--output-file", str(neb_profile),
)
show_plot(neb_profile)

2D reaction landscape

neb_landscape = plot_dir / "neb_landscape.png"
run_rgpycrumbs(
    "eon", "plt-neb",
    "--input-dat-pattern", str(neb_dir / "neb_*.dat"),
    "--input-path-pattern", str(neb_dir / "neb_path_*.con"),
    "--con-file", str(neb_dir / "neb.con"),
    "--ira-kmax", "14",
    "--force-recompute",
    "--rc-mode", "rmsd",
    "--plot-type", "landscape",
    "--surface-type", "grad_imq",
    "--show-pts",
    "--landscape-path", "all",
    "--project-path",
    "--plot-structures", "crit_points",
    "--strip-renderer", "xyzrender",
    "--strip-dividers",
    "--perspective-tilt", "8",
    "--zoom-ratio", "0.2",
    "--show-legend",
    "--dpi", "150",
    "--output-file", str(neb_landscape),
)
show_plot(neb_landscape)

See also

For producing your own NEB trajectories beyond this tutorial:

  • The eon-pet-neb example in the lab-cosmo/atomistic-cookbook: a step-by-step PET-MAD NEB walkthrough using ASE for path setup. This is the canonical metatomic-consumer integration test for eOn.

  • HaoZeke/eon_orchestrator: Snakemake-orchestrated workflow for batches of NEB calculations with PET-MAD, including IRA pre-alignment, endpoint minimization, energy profiles, and 2D RMSD landscapes. The vinyl alcohol config used in this tutorial is taken from examples/vinyl_alcohol/.

Further reading