Campaign: an MDI driver facility for SEAMM#

Date:

2026-07-06

Packages:

seamm_mdi (new), dimer_builder_step, seamm, mopac_step

Summary#

This campaign created seamm_mdi, a reusable driver for the MDI (MolSSI Driver Interface), and used it as the energy engine for the new Dimer Builder plug-in’s energy-based contact search. It grew out of the Dimer Builder work: rather than run an external code once per geometry through a sub-flowchart, drive a persistent engine over MDI and evaluate many geometries cheaply.

Motivation#

Several SEAMM drivers (energy scans, reaction paths, and Dimer Builder’s contact search) need the energy — and sometimes the gradient — at many nearby geometries. Launching the external code afresh for each point is dominated by the code’s start-up time, especially for the inexpensive methods one actually uses for these tasks (semiempirical, forcefields). MDI solves this: launch the engine once, then send geometries and read back energies/forces over a socket.

It also simplifies the user experience. Instead of assembling a sub-flowchart inside a step, the user sets up a model chemistry and the step drives it. And it keeps SEAMM’s dependencies off the compute cluster: only a lightweight engine wrapper needs to be there, not the whole driver stack.

Architecture#

MDIEngine (driver)

A small driver object. start() picks a free TCP port, begins listening, launches the engine, accepts its connection, and sends the fixed topology (natoms, elements). Then set_coordinates / energy / forces drive it for as many geometries as wanted; close() sends EXIT and reaps the engine. Used as a context manager.

Units in one place

Every driving method takes an optional units= string and converts via seamm_util.Q_. Defaults are MDI-native (bohr, hartree, hartree/bohr). This keeps conversion out of every consumer.

Engine wrappers, reused

The engine side is the existing mopac_mdi.py / tblite_mdi.py wrapper scripts (in mopac_step / xtb_step): standalone scripts that wrap the code’s Python API and speak MDI. An engine does not need native MDI support — any code with a Python API can be wrapped. They are -role ENGINE (connect-only), so no change was needed to reuse them.

Key decisions#

Socket direction: driver listens, engine connects#

Per the MDI standard, -role picks the network server: the driver listens on its port and the engine connects to it (hence the engine gets -hostname). This is the only direction that works when one driver serves several engines. It is also the direction that works for SEAMM’s distributed execution model: the driver runs on the JobServer and the engine, launched on a compute node, dials back to the JobServer (the allowed direction through institutional firewalls; a port range is opened to the cluster by the local admin — the approach QCArchive uses successfully). The engine wrapper never has to be reached into on the compute node.

Note

A stale comment in lammps_step claims “the QM engine is the listener”; that is incorrect. The driver listens; the engine connects. (Two small LAMMPS cleanups fall out of this: fix that comment and drop a superfluous -hostname on the driver launch line.)

Local now, remote later#

The first release launches the engine as a local subprocess (localhost). Remote launching through the SEAMM executor — ship the wrapper, submit to the queue, engine dials back — is planned; it mirrors the normal remote-code flow (input files in, requested files back) with the socket replacing most of the file round-trip. Crucially, SEAMM itself is never installed on the cluster.

Cross-package changes#

dimer_builder_step

Added contact method = energy: read the upstream _model_chemistry (a Model Chemistry step, as LAMMPS does), start one dimer engine, and find the energy minimum along each approach axis to anchor the scan (falling back to the van der Waals contact for orientations with no binding well). seamm_mdi is imported lazily, so the van der Waals path has no dependency on it.

seamm (core)

Added Node.previous_nodes(node_type=None) and the matching TkNode.previous_nodes — a reusable way to find preceding steps of a given type. Dimer Builder uses it to show a “needs a Model Chemistry step” hint only when one is not already upstream.

mopac_step

The mopac_mdi.py engine logged connection/build/timing/exit lines at INFO, cluttering driver output. Demoted to DEBUG so normal runs are quiet; the driver reports what it needs (which model chemistry, how many calls).

orca_step

Brought up as the campaign’s high-level reference method — the accurate level the DES370K-style training set (energies and forces for the dimers and clusters the Dimer Builder generates) is computed at. The full ORCA DFT functional catalogue (117 functionals) now lives in metadata.py with each functional’s category, analytic-vs-numeric gradient availability, and citations; the GUI presents a Method → functional-type → functional cascade; the gradient keyword is chosen automatically (EnGrad, or NumGrad for DLPNO-CCSD(T) and the non-self-consistent wB97M(2)/wB97X-2); gradients, charges, and the other array/vector results are now storable as configuration properties; and ORCA runs in parallel by default ([orca-step] ncores/memory). This is the plug-in the future orca_mdi.py wrapper will drive as a persistent engine.

Status and remaining work#

Done and validated (against real MOPAC, across conda environments): MDIEngine local mode, unit conversions, and the Dimer Builder energy-contact search. An orca_mdi.py engine (in orca_step) was added and validated the same way against real ORCA: it runs the orca binary per geometry in a persistent work dir (reusing orca.gbw for the SCF guess), and ORCAStep advertises mdi_capable for analytic-gradient methods with the real keyword + basis in mdi_method_arg/mdi_basis_arg. HF/def2-SVP water through MDIEngine matched a direct ORCA run (-75.96098399 Ha).

Remaining:

  • Remote / executor launching of the engine (the queue + dial-back path).

  • Wire ORCA into the Dimer Builder: _open_energy_engine must pass the model chemistry’s mdi_basis_arg as well as its method (MOPAC/xTB need only a method; ORCA also needs a basis). The engine itself is done and tested.

  • Retrofit energy_scan and reaction_path (and eventually the LAMMPS QM path) onto the facility.

  • Demote the tblite_mdi.py / mace_mdi.py engine logging as was done for MOPAC; and the two LAMMPS comment/launch cleanups noted above.