8.1.7. Using LAMMPS with the MDI library for code coupling¶
Client/server coupling of two (or more) codes is where one code is the “client” and sends request messages (data) to one (or more) “server” code(s). A server responds to each request with a reply message (data). This enables two (or more) codes to work in tandem to perform a simulation. LAMMPS can act as either a client or server code; it does this by using the MolSSI Driver Interface (MDI) library, developed by the Molecular Sciences Software Institute (MolSSI), which is supported by the MDI package.
Alternate methods for code coupling with LAMMPS are described on the Howto couple doc page.
Some advantages of client/server coupling are that the codes can run as stand-alone executables; they need not be linked together. Thus neither code needs to have a library interface. This also makes it easy to run the two codes on different numbers of processors. If a message protocol (format and content) is defined for a particular kind of simulation, then in principle any code which implements the client-side protocol can be used in tandem with any code which implements the server-side protocol. Neither code needs to know what specific other code it is working with.
In MDI nomenclature, a client code is the “driver”, and a server code is an “engine”. One driver code can communicate with one or more instances of one or more engine codes. Driver and engine codes can be written in any language: C, C++, Fortran, Python, etc.
In addition to allowing driver and engine(s) to run as stand-alone executables, MDI also enables an engine to be a plugin to the client code. In this scenario, server code(s) are compiled as shared libraries, and one (or more) instances of the server are instantiated by the driver code. If the driver code runs in parallel, it can split its MPI communicator into multiple sub-communicators, and launch each plugin engine instance on a sub-communicator. Driver processors within that sub-communicator exchange messages with the corresponding engine instance, and can also send MPI messages to other processors in the driver. The driver code can also destroy engine instances and re-instantiate them. LAMMPS can operate as either a stand-alone or plugin MDI engine. When it operates as a driver, if can use either stand-alone or plugin MDI engines.
The way in which an MDI driver communicates with an MDI engine is by making MDI_Send() and MDI_Recv() calls, which are conceptually similar to MPI_Send() and MPI_Recv() calls. Each send or receive operation uses a string to identify the command name, and optionally some data, which can be a single value or vector of values of any data type. Inside the MDI library, data is exchanged between the driver and engine via MPI calls or sockets. This a run-time choice by the user.
The MDI package provides a mdi engine command which enables LAMMPS to operate as an MDI engine. Its doc page explains the variety of standard and custom MDI commands which the LAMMPS engine recognizes and can respond to.
The package also provides a mdi plugin command which enables LAMMPS to operate as an MDI driver and load an MDI engine as a plugin library.
The package also has a fix mdi/aimd <fix_mdi_aimd> command in which LAMMPS operates as an MDI driver to perform ab initio MD simulations in conjunction with a quantum mechanics code. Its post_force() method illustrates how a driver issues MDI commands to another code. This command can be used to couple to an MDI engine which is either a stand-alone code or a plugin library.
The examples/mdi directory contains Python scripts and LAMMPS input script which use LAMMPS as either an MDI driver or engine or both. Three example use cases are provided:
Run ab initio MD (AIMD) using 2 instances of LAMMPS, one as driver and one as an engine. As an engine, LAMMPS is a surrogate for a quantum code.
A Python script driver invokes a sequence of unrelated LAMMPS calculations. Calculations can be single-point energy/force evaluations, MD runs, or energy minimizations.
Run AIMD with a Python driver code and 2 LAMMPS instances as engines. The first LAMMPS instance performs MD timestepping. The second LAMMPS instance acts as a surrogate QM code to compute forces.
Note that in any of these example where LAMMPS is used as an engine, an actual QM code (which supports MDI) could be used in its place, without modifying other code or scripts, except to specify the name of the QM code.
The examples/mdi/README file explains how to launch both driver and engine codes so that they communicate using the MDI library via either MPI or sockets.
Currently there are two quantum DFT codes which have direct MDI support, Quantum ESPRESSO (QE) and INQ. There are also several QM codes which have indirect support through QCEngine or i-PI. The former means they require a wrapper program (QCEngine) with MDI support which writes/read files to pass data to the quantum code itself. The list of QCEngine-supported and i-PI-supported quantum codes is on the MDI webpage.
Here is how to build QE as a stand-alone
pw.x file which can be
used in stand-alone mode:
% git clone --branch mdi_plugin https://github.com/MolSSI-MDI/q-e.git <base_path>/q-e % build the executable pw.x, following the `QE build guide <https://gitlab.com/QEF/q-e/-/wikis/Developers/CMake-build-system>`_
Here is how to build QE as a shared library which can be used in plugin mode, which results in a libqemdi.so file in <base_path>/q-e/MDI/src:
% git clone --branch mdi_plugin https://github.com/MolSSI-MDI/q-e.git <base_path>/q-e % cd <base_path>/q-e % ./configure --enable-parallel --enable-openmp --enable-shared FFLAGS="-fPIC" FCFLAGS="-fPIC" CFLAGS="-fPIC" foxflags="-fPIC" try_foxflags="-fPIC" % make -j 4 mdi
INQ cannot be built as a stand-alone code; it is by design a library. Here is how to build INQ as a shared library which can be used in plugin mode, which results in a libinqmdi.so file in <base_path>/inq/build/examples:
% git clone --branch mdi --recurse-submodules https://gitlab.com/taylor-a-barnes/inq.git <base_path>/inq % cd <base_path>/inq % mkdir -p build % cd build % ../configure --prefix=<install_path>/install % make -j 4 % make install