Writing plugins --------------- Plugins provide a mechanism to add functionality to a LAMMPS executable without recompiling LAMMPS. This uses the operating system's capability to load dynamic shared object (DSO) files in a way similar shared libraries and then references specific functions those DSOs. Any DSO file with plugins has to include an initialization function with a specific name that has to follow specific rules. When loading the DSO, this function is called and will then register the contained plugin(s) with LAMMPS. From the programmer perspective this can work because of the object oriented design where all pair style commands are derived from the class Pair, all fix style commands from the class Fix and so on and only functions from those base classes are called directly. When a :doc:`pair_style` command or :doc:`fix` command is issued a new instance of such a derived class is created. This is done by a so-called factory function which is mapped to the style name. Thus when, for example, the LAMMPS processes the command ``pair_style lj/cut 2.5``, LAMMPS will look up the factory function for creating the ``PairLJCut`` class and then execute it. The return value of that function is a ``Pair *`` pointer and the pointer will be assigned to the location for the currently active pair style. A plugin thus has to implement such a factory function and register it with LAMMPS so that it gets added to the map of available styles of the given category. To register a plugin with LAMMPS an initialization function has to be called that follows specific rules explained below. As an example, for a hypothetical pair style "morse2" implemented in a class ``PairMorse2`` in the files ``pair_morse2.h`` and ``pair_morse2.cpp`` the file with the factory function and initialization function would look like this: .. code-block:: C++ #include "lammpsplugin.h" #include "version.h" #include "pair_morse2.h" using namespace LAMMPS_NS; static Pair *morse2creator(LAMMPS *lmp) { return new PairMorse2(lmp); } extern "C" void lammpsplugin_init(void *lmp, void *handle, void *regfunc) { lammpsplugin_regfunc register_plugin = (lammpsplugin_regfunc) regfunc; lammpsplugin_t plugin; plugin.version = LAMMPS_VERSION; plugin.style = "pair"; plugin.name = "morse2"; plugin.info = "Morse2 variant pair style v1.0"; plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; plugin.creator.v1 = (lammpsplugin_factory1 *) &morse2creator; plugin.handle = handle; (*register_plugin)(&plugin,lmp); } The factory function in this example is called ``morse2creator()``. It receives a pointer to the LAMMPS class as only argument and thus has to be assigned to the *creator.v1* member of the plugin struct and cast to the ``lammpsplugin_factory1`` pointer type. It returns a pointer to the allocated class instance derived from the ``Pair`` class. This function may be declared static to avoid clashes with other plugins. The name of the derived class, ``PairMorse2``, must be unique inside the entire LAMMPS executable. If the factory function would be for a fix or compute, which take three arguments (a pointer to the LAMMPS class, the number of arguments and the list of argument strings), then the pointer type is ``lammpsplugin_factory2`` and it must be assigned to the *creator.v2* member of the plugin struct. Below is an example for that: .. code-block:: C++ #include "lammpsplugin.h" #include "version.h" #include "fix_nve2.h" using namespace LAMMPS_NS; static Fix *nve2creator(LAMMPS *lmp, int argc, char **argv) { return new FixNVE2(lmp,argc,argv); } extern "C" void lammpsplugin_init(void *lmp, void *handle, void *regfunc) { lammpsplugin_regfunc register_plugin = (lammpsplugin_regfunc) regfunc; lammpsplugin_t plugin; plugin.version = LAMMPS_VERSION; plugin.style = "fix"; plugin.name = "nve2"; plugin.info = "NVE2 variant fix style v1.0"; plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; plugin.creator.v2 = (lammpsplugin_factory2 *) &nve2creator; plugin.handle = handle; (*register_plugin)(&plugin,lmp); } For command styles there is a third variant of factory function as demonstrated in the following example, which also shows that the implementation of the plugin class may also be within the same file as the plugin interface code: .. code-block:: C++ #include "lammpsplugin.h" #include "comm.h" #include "error.h" #include "pointers.h" #include "version.h" #include namespace LAMMPS_NS { class Hello : protected Pointers { public: Hello(class LAMMPS *lmp) : Pointers(lmp) {}; void command(int, char **); }; } using namespace LAMMPS_NS; void Hello::command(int argc, char **argv) { if (argc != 1) error->all(FLERR,"Illegal hello command"); if (comm->me == 0) utils::logmesg(lmp,fmt::format("Hello, {}!\n",argv[0])); } static void hellocreator(LAMMPS *lmp, int argc, char **argv) { Hello hello(lmp); hello.command(argc,argv); } extern "C" void lammpsplugin_init(void *lmp, void *handle, void *regfunc) { lammpsplugin_t plugin; lammpsplugin_regfunc register_plugin = (lammpsplugin_regfunc) regfunc; plugin.version = LAMMPS_VERSION; plugin.style = "command"; plugin.name = "hello"; plugin.info = "Hello world command v1.0"; plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; plugin.creator.v3 = (lammpsplugin_factory3 *) &hellocreator; plugin.handle = handle; (*register_plugin)(&plugin,lmp); } The initialization function **must** be called ``lammpsplugin_init``, it **must** have C bindings and it takes three void pointers as arguments. The first is a pointer to the LAMMPS class that calls it and it needs to be passed to the registration function. The second argument is a pointer to the internal handle of the DSO file, this needs to be added to the plugin info struct, so that the DSO can be closed and unloaded when all its contained plugins are unloaded. The third argument is a function pointer to the registration function and needs to be stored in a variable of ``lammpsplugin_regfunc`` type and then called with a pointer to the ``lammpsplugin_`` struct and the pointer to the LAMMPS instance as arguments to register a single plugin. There may be multiple calls to multiple plugins in the same initialization function. To register a plugin a struct of the ``lammpsplugin_t`` needs to be filled with relevant info: current LAMMPS version string, kind of style, name of style, info string, author string, pointer to factory function, and the DSO handle. The registration function is called with a pointer to the address of this struct and the pointer of the LAMMPS class. The registration function will then add the factory function of the plugin style to the respective style map under the provided name. It will also make a copy of the struct in a list of all loaded plugins and update the reference counter for loaded plugins from this specific DSO file. The pair style itself (i.e. the PairMorse2 class in this example) can be written just like any other pair style that is included in LAMMPS. For a plugin, the use of the ``PairStyle`` macro in the section encapsulated by ``#ifdef PAIR_CLASS`` is not needed, since the mapping of the class name to the style name is done by the plugin registration function with the information from the ``lammpsplugin_t`` struct. It may be included in case the new code is intended to be later included in LAMMPS directly.