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). Thenset_coordinates/energy/forcesdrive it for as many geometries as wanted;close()sendsEXITand reaps the engine. Used as a context manager.- Units in one place
Every driving method takes an optional
units=string and converts viaseamm_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.pywrapper scripts (inmopac_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_stepAdded
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_mdiis imported lazily, so the van der Waals path has no dependency on it.seamm(core)Added
Node.previous_nodes(node_type=None)and the matchingTkNode.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_stepThe
mopac_mdi.pyengine 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_stepBrought 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.pywith 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, orNumGradforDLPNO-CCSD(T)and the non-self-consistentwB97M(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 futureorca_mdi.pywrapper 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_enginemust pass the model chemistry’smdi_basis_argas well as its method (MOPAC/xTB need only a method; ORCA also needs a basis). The engine itself is done and tested.Retrofit
energy_scanandreaction_path(and eventually the LAMMPS QM path) onto the facility.Demote the
tblite_mdi.py/mace_mdi.pyengine logging as was done for MOPAC; and the two LAMMPS comment/launch cleanups noted above.