From 27a81ffc867bbbd198c81276d405bd729e27e645 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 9 Mar 2021 15:34:48 -0500 Subject: [PATCH 01/57] add initial version of a plugin loader interface (for pair styles) --- src/input.cpp | 10 ++++++ src/input.h | 1 + src/lammpsplugin.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++++ src/lammpsplugin.h | 44 +++++++++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 src/lammpsplugin.cpp create mode 100644 src/lammpsplugin.h diff --git a/src/input.cpp b/src/input.cpp index df5cf0efbe..57ce64815d 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -29,6 +29,7 @@ #include "group.h" #include "improper.h" #include "kspace.h" +#include "lammpsplugin.h" #include "memory.h" #include "min.h" #include "modify.h" @@ -714,6 +715,7 @@ int Input::execute_command() else if (!strcmp(command,"include")) include(); else if (!strcmp(command,"jump")) jump(); else if (!strcmp(command,"label")) label(); + else if (!strcmp(command,"load_plugin")) load_plugin(); else if (!strcmp(command,"log")) log(); else if (!strcmp(command,"next")) next_command(); else if (!strcmp(command,"partition")) partition(); @@ -1029,6 +1031,14 @@ void Input::label() /* ---------------------------------------------------------------------- */ +void Input::load_plugin() +{ + if (narg != 1) error->all(FLERR,"Illegal load_plugin command"); + lammpsplugin_load(arg[0],lmp); +} + +/* ---------------------------------------------------------------------- */ + void Input::log() { if ((narg < 1) || (narg > 2)) error->all(FLERR,"Illegal log command"); diff --git a/src/input.h b/src/input.h index a86b10a686..3b7b6d79c7 100644 --- a/src/input.h +++ b/src/input.h @@ -79,6 +79,7 @@ class Input : protected Pointers { void include(); void jump(); void label(); + void load_plugin(); void log(); void next_command(); void partition(); diff --git a/src/lammpsplugin.cpp b/src/lammpsplugin.cpp new file mode 100644 index 0000000000..d3d409de41 --- /dev/null +++ b/src/lammpsplugin.cpp @@ -0,0 +1,76 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://lammps.sandia.gov/, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "lammpsplugin.h" + +#include "error.h" +#include "force.h" +#include "lammps.h" +#include "modify.h" + + +#ifdef _WIN32 +#include +#else +#include +#endif + +namespace LAMMPS_NS +{ + + void lammpsplugin_load(const char *file, void *ptr) + { + LAMMPS *lmp = (LAMMPS *)ptr; +#if defined(WIN32) + utils::logmsg(lmp,"Loading of plugins not supported on Windows yet\n"); +#else + void *dso = dlopen(file,RTLD_NOW); + if (dso == nullptr) { + utils::logmesg(lmp,fmt::format("Loading of plugin from file {} failed: " + "{}", file, utils::getsyserror())); + return; + } + + void *initfunc = dlsym(dso,"lammpsplugin_init"); + if (initfunc == nullptr) { + dlclose(dso); + utils::logmesg(lmp,fmt::format("Symbol lookup failure in file {}",file)); + return; + } + ((lammpsplugin_initfunc)(initfunc))(ptr); +#endif + } + + void lammpsplugin_register(lammpsplugin_t *plugin, void *ptr) + { + LAMMPS *lmp = (LAMMPS *)ptr; + + if (plugin == nullptr) return; + if (plugin->version) + utils::logmesg(lmp,fmt::format("Loading: {}\n compiled for LAMMPS " + "version {} loaded into LAMMPS " + "version {}\n", plugin->info, + plugin->version, lmp->version)); + std::string pstyle = plugin->style; + if (pstyle == "pair") { + (*(lmp->force->pair_map))[plugin->name] = + (LAMMPS_NS::Force::PairCreator)plugin->creator; + } else { + utils::logmesg(lmp,fmt::format("Loading plugin for {} styles not " + "yet implemented\n", pstyle)); + } + } +} + + + diff --git a/src/lammpsplugin.h b/src/lammpsplugin.h new file mode 100644 index 0000000000..9784c456ee --- /dev/null +++ b/src/lammpsplugin.h @@ -0,0 +1,44 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef LMP_LAMMPSPLUGIN_H +#define LMP_LAMMPSPLUGIN_H + +#ifdef __cplusplus +extern "C" { +#endif + + typedef void *(lammpsplugin_factory)(void *); + typedef void (*lammpsplugin_initfunc)(void *); + + typedef struct { + const char *version; + const char *style; + const char *name; + const char *info; + lammpsplugin_factory *creator; + } lammpsplugin_t; + + void lammpsplugin_init(void *); + +#ifdef __cplusplus +} +#endif + +namespace LAMMPS_NS +{ + extern void lammpsplugin_load(const char *, void *); + extern void lammpsplugin_register(lammpsplugin_t *, void *); +} + +#endif From f0381b48ca95068b6c4ee222240607c4bf14f036 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 9 Mar 2021 15:50:27 -0500 Subject: [PATCH 02/57] add example for loading a pair style --- examples/plugins/Makefile | 17 ++ examples/plugins/morse2plugin.cpp | 29 +++ examples/plugins/pair_morse2.cpp | 359 ++++++++++++++++++++++++++++++ examples/plugins/pair_morse2.h | 70 ++++++ 4 files changed, 475 insertions(+) create mode 100644 examples/plugins/Makefile create mode 100644 examples/plugins/morse2plugin.cpp create mode 100644 examples/plugins/pair_morse2.cpp create mode 100644 examples/plugins/pair_morse2.h diff --git a/examples/plugins/Makefile b/examples/plugins/Makefile new file mode 100644 index 0000000000..0c4365dfd8 --- /dev/null +++ b/examples/plugins/Makefile @@ -0,0 +1,17 @@ +CXX=mpicxx +CXXFLAGS=-I../../src -Wall -O3 -fPIC +LD=$(CXX) -shared -rdynamic + +morse2plugin.so: morse2plugin.o pair_morse2.o + $(LD) -o $@ $^ + +.cpp.o: + $(CXX) -o $@ $(CXXFLAGS) -c $< + +pair_morse2.o: pair_morse2.cpp pair_morse2.h +morse2plugin.o: morse2plugin.cpp pair_morse2.h + +clean: + rm -f *~ *.so *.o log.lammps + + diff --git a/examples/plugins/morse2plugin.cpp b/examples/plugins/morse2plugin.cpp new file mode 100644 index 0000000000..bb21f3da95 --- /dev/null +++ b/examples/plugins/morse2plugin.cpp @@ -0,0 +1,29 @@ + +#include "lammpsplugin.h" + +#include "lammps.h" +#include "version.h" + +#include + +#include "pair_morse2.h" + +using namespace LAMMPS_NS; + +static Pair *morse2creator(LAMMPS *lmp) +{ + return new PairMorse2(lmp); +} + +static lammpsplugin_t plugin; +void lammpsplugin_init(void *lmp) +{ + memset(&plugin,0,sizeof(lammpsplugin_t)); + plugin.version = LAMMPS_VERSION; + plugin.style = "pair"; + plugin.name = "morse2"; + plugin.info = "Morse2 variant pair style v1.0 by Axel Kohlmeyer"; + plugin.creator = (lammpsplugin_factory *) &morse2creator; + + lammpsplugin_register(&plugin, (LAMMPS *)lmp); +} diff --git a/examples/plugins/pair_morse2.cpp b/examples/plugins/pair_morse2.cpp new file mode 100644 index 0000000000..a235ad297c --- /dev/null +++ b/examples/plugins/pair_morse2.cpp @@ -0,0 +1,359 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://lammps.sandia.gov/, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "pair_morse2.h" + +#include +#include +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "neigh_list.h" +#include "memory.h" +#include "error.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +PairMorse2::PairMorse2(LAMMPS *lmp) : Pair(lmp) +{ + writedata = 1; +} + +/* ---------------------------------------------------------------------- */ + +PairMorse2::~PairMorse2() +{ + if (allocated) { + memory->destroy(setflag); + memory->destroy(cutsq); + + memory->destroy(cut); + memory->destroy(d0); + memory->destroy(alpha); + memory->destroy(r0); + memory->destroy(morse1); + memory->destroy(offset); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairMorse2::compute(int eflag, int vflag) +{ + int i,j,ii,jj,inum,jnum,itype,jtype; + double xtmp,ytmp,ztmp,delx,dely,delz,evdwl,fpair; + double rsq,r,dr,dexp,factor_lj; + int *ilist,*jlist,*numneigh,**firstneigh; + + evdwl = 0.0; + ev_init(eflag,vflag); + + double **x = atom->x; + double **f = atom->f; + int *type = atom->type; + int nlocal = atom->nlocal; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + + inum = list->inum; + ilist = list->ilist; + numneigh = list->numneigh; + firstneigh = list->firstneigh; + + // loop over neighbors of my atoms + + for (ii = 0; ii < inum; ii++) { + i = ilist[ii]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + jlist = firstneigh[i]; + jnum = numneigh[i]; + + for (jj = 0; jj < jnum; jj++) { + j = jlist[jj]; + factor_lj = special_lj[sbmask(j)]; + j &= NEIGHMASK; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r = sqrt(rsq); + dr = r - r0[itype][jtype]; + dexp = exp(-alpha[itype][jtype] * dr); + fpair = factor_lj * morse1[itype][jtype] * (dexp*dexp - dexp) / r; + + f[i][0] += delx*fpair; + f[i][1] += dely*fpair; + f[i][2] += delz*fpair; + if (newton_pair || j < nlocal) { + f[j][0] -= delx*fpair; + f[j][1] -= dely*fpair; + f[j][2] -= delz*fpair; + } + + if (eflag) { + evdwl = d0[itype][jtype] * (dexp*dexp - 2.0*dexp) - + offset[itype][jtype]; + evdwl *= factor_lj; + } + + if (evflag) ev_tally(i,j,nlocal,newton_pair, + evdwl,0.0,fpair,delx,dely,delz); + } + } + } + + if (vflag_fdotr) virial_fdotr_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairMorse2::allocate() +{ + allocated = 1; + int n = atom->ntypes; + + memory->create(setflag,n+1,n+1,"pair:setflag"); + for (int i = 1; i <= n; i++) + for (int j = i; j <= n; j++) + setflag[i][j] = 0; + + memory->create(cutsq,n+1,n+1,"pair:cutsq"); + + memory->create(cut,n+1,n+1,"pair:cut"); + memory->create(d0,n+1,n+1,"pair:d0"); + memory->create(alpha,n+1,n+1,"pair:alpha"); + memory->create(r0,n+1,n+1,"pair:r0"); + memory->create(morse1,n+1,n+1,"pair:morse1"); + memory->create(offset,n+1,n+1,"pair:offset"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairMorse2::settings(int narg, char **arg) +{ + if (narg != 1) error->all(FLERR,"Illegal pair_style command"); + + cut_global = utils::numeric(FLERR,arg[0],false,lmp); + + // reset cutoffs that have been explicitly set + + if (allocated) { + int i,j; + for (i = 1; i <= atom->ntypes; i++) + for (j = i; j <= atom->ntypes; j++) + if (setflag[i][j]) cut[i][j] = cut_global; + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairMorse2::coeff(int narg, char **arg) +{ + if (narg < 5 || narg > 6) + error->all(FLERR,"Incorrect args for pair coefficients"); + if (!allocated) allocate(); + + int ilo,ihi,jlo,jhi; + utils::bounds(FLERR,arg[0],1,atom->ntypes,ilo,ihi,error); + utils::bounds(FLERR,arg[1],1,atom->ntypes,jlo,jhi,error); + + double d0_one = utils::numeric(FLERR,arg[2],false,lmp); + double alpha_one = utils::numeric(FLERR,arg[3],false,lmp); + double r0_one = utils::numeric(FLERR,arg[4],false,lmp); + + double cut_one = cut_global; + if (narg == 6) cut_one = utils::numeric(FLERR,arg[5],false,lmp); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + for (int j = MAX(jlo,i); j <= jhi; j++) { + d0[i][j] = d0_one; + alpha[i][j] = alpha_one; + r0[i][j] = r0_one; + cut[i][j] = cut_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all(FLERR,"Incorrect args for pair coefficients"); +} + + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairMorse2::init_one(int i, int j) +{ + if (setflag[i][j] == 0) error->all(FLERR,"All pair coeffs are not set"); + + morse1[i][j] = 2.0*d0[i][j]*alpha[i][j]; + + if (offset_flag) { + double alpha_dr = -alpha[i][j] * (cut[i][j] - r0[i][j]); + offset[i][j] = d0[i][j] * (exp(2.0*alpha_dr) - 2.0*exp(alpha_dr)); + } else offset[i][j] = 0.0; + + d0[j][i] = d0[i][j]; + alpha[j][i] = alpha[i][j]; + r0[j][i] = r0[i][j]; + morse1[j][i] = morse1[i][j]; + offset[j][i] = offset[i][j]; + + return cut[i][j]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairMorse2::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + for (j = i; j <= atom->ntypes; j++) { + fwrite(&setflag[i][j],sizeof(int),1,fp); + if (setflag[i][j]) { + fwrite(&d0[i][j],sizeof(double),1,fp); + fwrite(&alpha[i][j],sizeof(double),1,fp); + fwrite(&r0[i][j],sizeof(double),1,fp); + fwrite(&cut[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairMorse2::read_restart(FILE *fp) +{ + read_restart_settings(fp); + + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + for (j = i; j <= atom->ntypes; j++) { + if (me == 0) utils::sfread(FLERR,&setflag[i][j],sizeof(int),1,fp,nullptr,error); + MPI_Bcast(&setflag[i][j],1,MPI_INT,0,world); + if (setflag[i][j]) { + if (me == 0) { + utils::sfread(FLERR,&d0[i][j],sizeof(double),1,fp,nullptr,error); + utils::sfread(FLERR,&alpha[i][j],sizeof(double),1,fp,nullptr,error); + utils::sfread(FLERR,&r0[i][j],sizeof(double),1,fp,nullptr,error); + utils::sfread(FLERR,&cut[i][j],sizeof(double),1,fp,nullptr,error); + } + MPI_Bcast(&d0[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&alpha[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&r0[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairMorse2::write_restart_settings(FILE *fp) +{ + fwrite(&cut_global,sizeof(double),1,fp); + fwrite(&offset_flag,sizeof(int),1,fp); + fwrite(&mix_flag,sizeof(int),1,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairMorse2::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + utils::sfread(FLERR,&cut_global,sizeof(double),1,fp,nullptr,error); + utils::sfread(FLERR,&offset_flag,sizeof(int),1,fp,nullptr,error); + utils::sfread(FLERR,&mix_flag,sizeof(int),1,fp,nullptr,error); + } + MPI_Bcast(&cut_global,1,MPI_DOUBLE,0,world); + MPI_Bcast(&offset_flag,1,MPI_INT,0,world); + MPI_Bcast(&mix_flag,1,MPI_INT,0,world); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to data file +------------------------------------------------------------------------- */ + +void PairMorse2::write_data(FILE *fp) +{ + for (int i = 1; i <= atom->ntypes; i++) + fprintf(fp,"%d %g %g %g\n",i,d0[i][i],alpha[i][i],r0[i][i]); +} + +/* ---------------------------------------------------------------------- + proc 0 writes all pairs to data file +------------------------------------------------------------------------- */ + +void PairMorse2::write_data_all(FILE *fp) +{ + for (int i = 1; i <= atom->ntypes; i++) + for (int j = i; j <= atom->ntypes; j++) + fprintf(fp,"%d %d %g %g %g %g\n", + i,j,d0[i][j],alpha[i][j],r0[i][j],cut[i][j]); +} + +/* ---------------------------------------------------------------------- */ + +double PairMorse2::single(int /*i*/, int /*j*/, int itype, int jtype, double rsq, + double /*factor_coul*/, double factor_lj, + double &fforce) +{ + double r,dr,dexp,phi; + + r = sqrt(rsq); + dr = r - r0[itype][jtype]; + dexp = exp(-alpha[itype][jtype] * dr); + fforce = factor_lj * morse1[itype][jtype] * (dexp*dexp - dexp) / r; + + phi = d0[itype][jtype] * (dexp*dexp - 2.0*dexp) - offset[itype][jtype]; + return factor_lj*phi; +} + +/* ---------------------------------------------------------------------- */ + +void *PairMorse2::extract(const char *str, int &dim) +{ + dim = 2; + if (strcmp(str,"d0") == 0) return (void *) d0; + if (strcmp(str,"r0") == 0) return (void *) r0; + if (strcmp(str,"alpha") == 0) return (void *) alpha; + return nullptr; +} diff --git a/examples/plugins/pair_morse2.h b/examples/plugins/pair_morse2.h new file mode 100644 index 0000000000..c20f381c63 --- /dev/null +++ b/examples/plugins/pair_morse2.h @@ -0,0 +1,70 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef LMP_PAIR_MORSE2_H +#define LMP_PAIR_MORSE2_H + +#include "pair.h" + +namespace LAMMPS_NS { + +class PairMorse2 : public Pair { + public: + PairMorse2(class LAMMPS *); + virtual ~PairMorse2(); + virtual void compute(int, int); + + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + void write_data(FILE *); + void write_data_all(FILE *); + double single(int, int, int, int, double, double, double, double &); + void *extract(const char *, int &); + + protected: + double cut_global; + double **cut; + double **d0,**alpha,**r0; + double **morse1; + double **offset; + + virtual void allocate(); +}; + +} + +#endif + +/* ERROR/WARNING messages: + +E: Illegal ... command + +Self-explanatory. Check the input script syntax and compare to the +documentation for the command. You can use -echo screen as a +command-line option when running LAMMPS to see the offending line. + +E: Incorrect args for pair coefficients + +Self-explanatory. Check the input script or data file. + +E: All pair coeffs are not set + +All pair coefficients must be set in the data file or by the +pair_coeff command before running a simulation. + +*/ From fb39ceaaeb3fbfa6ed9c84a8974bf4fb466e5fe2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 9 Mar 2021 16:00:14 -0500 Subject: [PATCH 03/57] fix whitespace and typo --- src/lammpsplugin.cpp | 7 ++----- src/lammpsplugin.h | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/lammpsplugin.cpp b/src/lammpsplugin.cpp index d3d409de41..744203cd5e 100644 --- a/src/lammpsplugin.cpp +++ b/src/lammpsplugin.cpp @@ -27,12 +27,12 @@ namespace LAMMPS_NS { - + void lammpsplugin_load(const char *file, void *ptr) { LAMMPS *lmp = (LAMMPS *)ptr; #if defined(WIN32) - utils::logmsg(lmp,"Loading of plugins not supported on Windows yet\n"); + utils::logmesg(lmp,"Loading of plugins not supported on Windows yet\n"); #else void *dso = dlopen(file,RTLD_NOW); if (dso == nullptr) { @@ -71,6 +71,3 @@ namespace LAMMPS_NS } } } - - - diff --git a/src/lammpsplugin.h b/src/lammpsplugin.h index 9784c456ee..13076cc86f 100644 --- a/src/lammpsplugin.h +++ b/src/lammpsplugin.h @@ -18,7 +18,7 @@ extern "C" { #endif - typedef void *(lammpsplugin_factory)(void *); + typedef void *(lammpsplugin_factory)(void *); typedef void (*lammpsplugin_initfunc)(void *); typedef struct { From f68a7094ad5fe72c8e6caa18ce6276d76d4cdda1 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 9 Mar 2021 18:44:51 -0500 Subject: [PATCH 04/57] include /omp variant into plugin example --- examples/plugins/Makefile | 9 +- examples/plugins/morse2plugin.cpp | 13 ++- examples/plugins/pair_morse2_omp.cpp | 160 +++++++++++++++++++++++++++ examples/plugins/pair_morse2_omp.h | 41 +++++++ 4 files changed, 218 insertions(+), 5 deletions(-) create mode 100644 examples/plugins/pair_morse2_omp.cpp create mode 100644 examples/plugins/pair_morse2_omp.h diff --git a/examples/plugins/Makefile b/examples/plugins/Makefile index 0c4365dfd8..2f563f924e 100644 --- a/examples/plugins/Makefile +++ b/examples/plugins/Makefile @@ -1,15 +1,16 @@ CXX=mpicxx -CXXFLAGS=-I../../src -Wall -O3 -fPIC -LD=$(CXX) -shared -rdynamic +CXXFLAGS=-I../../src -Wall -O3 -fPIC -I../../src/USER-OMP -fopenmp +LD=$(CXX) -shared -rdynamic -fopenmp -morse2plugin.so: morse2plugin.o pair_morse2.o +morse2plugin.so: morse2plugin.o pair_morse2.o pair_morse2_omp.o $(LD) -o $@ $^ .cpp.o: $(CXX) -o $@ $(CXXFLAGS) -c $< pair_morse2.o: pair_morse2.cpp pair_morse2.h -morse2plugin.o: morse2plugin.cpp pair_morse2.h +pair_morse2_omp.o: pair_morse2_omp.cpp pair_morse2_omp.h pair_morse2.h +morse2plugin.o: morse2plugin.cpp pair_morse2.h pair_morse2_omp.h clean: rm -f *~ *.so *.o log.lammps diff --git a/examples/plugins/morse2plugin.cpp b/examples/plugins/morse2plugin.cpp index bb21f3da95..fbb0882a92 100644 --- a/examples/plugins/morse2plugin.cpp +++ b/examples/plugins/morse2plugin.cpp @@ -7,6 +7,7 @@ #include #include "pair_morse2.h" +#include "pair_morse2_omp.h" using namespace LAMMPS_NS; @@ -15,6 +16,11 @@ static Pair *morse2creator(LAMMPS *lmp) return new PairMorse2(lmp); } +static Pair *morse2ompcreator(LAMMPS *lmp) +{ + return new PairMorse2OMP(lmp); +} + static lammpsplugin_t plugin; void lammpsplugin_init(void *lmp) { @@ -24,6 +30,11 @@ void lammpsplugin_init(void *lmp) plugin.name = "morse2"; plugin.info = "Morse2 variant pair style v1.0 by Axel Kohlmeyer"; plugin.creator = (lammpsplugin_factory *) &morse2creator; - + lammpsplugin_register(&plugin, (LAMMPS *)lmp); + + plugin.style = "pair"; + plugin.name = "morse2/omp"; + plugin.info = "Morse2 variant pair style for OpenMP v1.0 by Axel Kohlmeyer"; + plugin.creator = (lammpsplugin_factory *) &morse2ompcreator; lammpsplugin_register(&plugin, (LAMMPS *)lmp); } diff --git a/examples/plugins/pair_morse2_omp.cpp b/examples/plugins/pair_morse2_omp.cpp new file mode 100644 index 0000000000..14438c764b --- /dev/null +++ b/examples/plugins/pair_morse2_omp.cpp @@ -0,0 +1,160 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://lammps.sandia.gov/, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + This software is distributed under the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Axel Kohlmeyer (Temple U) +------------------------------------------------------------------------- */ + +#include "pair_morse2_omp.h" + +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "neigh_list.h" +#include "suffix.h" + +#include + +#include "omp_compat.h" +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +PairMorse2OMP::PairMorse2OMP(LAMMPS *lmp) : + PairMorse2(lmp), ThrOMP(lmp, THR_PAIR) +{ + suffix_flag |= Suffix::OMP; + respa_enable = 0; +} + +/* ---------------------------------------------------------------------- */ + +void PairMorse2OMP::compute(int eflag, int vflag) +{ + ev_init(eflag,vflag); + + const int nall = atom->nlocal + atom->nghost; + const int nthreads = comm->nthreads; + const int inum = list->inum; + +#if defined(_OPENMP) +#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(eflag,vflag) +#endif + { + int ifrom, ito, tid; + + loop_setup_thr(ifrom, ito, tid, inum, nthreads); + ThrData *thr = fix->get_thr(tid); + thr->timer(Timer::START); + ev_setup_thr(eflag, vflag, nall, eatom, vatom, nullptr, thr); + + if (evflag) { + if (eflag) { + if (force->newton_pair) eval<1,1,1>(ifrom, ito, thr); + else eval<1,1,0>(ifrom, ito, thr); + } else { + if (force->newton_pair) eval<1,0,1>(ifrom, ito, thr); + else eval<1,0,0>(ifrom, ito, thr); + } + } else { + if (force->newton_pair) eval<0,0,1>(ifrom, ito, thr); + else eval<0,0,0>(ifrom, ito, thr); + } + + thr->timer(Timer::PAIR); + reduce_thr(this, eflag, vflag, thr); + } // end of omp parallel region +} + +template +void PairMorse2OMP::eval(int iifrom, int iito, ThrData * const thr) +{ + int i,j,ii,jj,jnum,itype,jtype; + double xtmp,ytmp,ztmp,delx,dely,delz,evdwl,fpair; + double rsq,r,dr,dexp,factor_lj; + int *ilist,*jlist,*numneigh,**firstneigh; + + evdwl = 0.0; + + const dbl3_t * _noalias const x = (dbl3_t *) atom->x[0]; + dbl3_t * _noalias const f = (dbl3_t *) thr->get_f()[0]; + const int * _noalias const type = atom->type; + const int nlocal = atom->nlocal; + const double * _noalias const special_lj = force->special_lj; + double fxtmp,fytmp,fztmp; + + ilist = list->ilist; + numneigh = list->numneigh; + firstneigh = list->firstneigh; + + // loop over neighbors of my atoms + + for (ii = iifrom; ii < iito; ++ii) { + + i = ilist[ii]; + xtmp = x[i].x; + ytmp = x[i].y; + ztmp = x[i].z; + itype = type[i]; + jlist = firstneigh[i]; + jnum = numneigh[i]; + fxtmp=fytmp=fztmp=0.0; + + for (jj = 0; jj < jnum; jj++) { + j = jlist[jj]; + factor_lj = special_lj[sbmask(j)]; + j &= NEIGHMASK; + + delx = xtmp - x[j].x; + dely = ytmp - x[j].y; + delz = ztmp - x[j].z; + rsq = delx*delx + dely*dely + delz*delz; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r = sqrt(rsq); + dr = r - r0[itype][jtype]; + dexp = exp(-alpha[itype][jtype] * dr); + fpair = factor_lj * morse1[itype][jtype] * (dexp*dexp - dexp) / r; + + fxtmp += delx*fpair; + fytmp += dely*fpair; + fztmp += delz*fpair; + if (NEWTON_PAIR || j < nlocal) { + f[j].x -= delx*fpair; + f[j].y -= dely*fpair; + f[j].z -= delz*fpair; + } + + if (EFLAG) { + evdwl = d0[itype][jtype] * (dexp*dexp - 2.0*dexp) - + offset[itype][jtype]; + evdwl *= factor_lj; + } + + if (EVFLAG) ev_tally_thr(this,i,j,nlocal,NEWTON_PAIR, + evdwl,0.0,fpair,delx,dely,delz,thr); + } + } + f[i].x += fxtmp; + f[i].y += fytmp; + f[i].z += fztmp; + } +} + +/* ---------------------------------------------------------------------- */ + +double PairMorse2OMP::memory_usage() +{ + double bytes = memory_usage_thr(); + bytes += PairMorse2::memory_usage(); + + return bytes; +} diff --git a/examples/plugins/pair_morse2_omp.h b/examples/plugins/pair_morse2_omp.h new file mode 100644 index 0000000000..c5ed7b5765 --- /dev/null +++ b/examples/plugins/pair_morse2_omp.h @@ -0,0 +1,41 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Axel Kohlmeyer (Temple U) +------------------------------------------------------------------------- */ + +#ifndef LMP_PAIR_MORSE2_OMP_H +#define LMP_PAIR_MORSE2_OMP_H + +#include "pair_morse2.h" +#include "thr_omp.h" + +namespace LAMMPS_NS { + +class PairMorse2OMP : public PairMorse2, public ThrOMP { + + public: + PairMorse2OMP(class LAMMPS *); + + virtual void compute(int, int); + virtual double memory_usage(); + + private: + template + void eval(int ifrom, int ito, ThrData * const thr); +}; + +} + +#endif From 592490be4ed469afc3b752edd5a684a80f195d10 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 10 Mar 2021 09:50:28 -0500 Subject: [PATCH 05/57] add unload/list commands --- src/input.cpp | 36 +++++++++++++++++++++++++--------- src/input.h | 2 +- src/lammpsplugin.cpp | 46 +++++++++++++++++++++++++++++++++++++++++++- src/lammpsplugin.h | 24 ++++++++++++++++------- 4 files changed, 90 insertions(+), 18 deletions(-) diff --git a/src/input.cpp b/src/input.cpp index 57ce64815d..07e3c417f6 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -715,10 +715,10 @@ int Input::execute_command() else if (!strcmp(command,"include")) include(); else if (!strcmp(command,"jump")) jump(); else if (!strcmp(command,"label")) label(); - else if (!strcmp(command,"load_plugin")) load_plugin(); else if (!strcmp(command,"log")) log(); else if (!strcmp(command,"next")) next_command(); else if (!strcmp(command,"partition")) partition(); + else if (!strcmp(command,"plugin")) plugin(); else if (!strcmp(command,"print")) print(); else if (!strcmp(command,"python")) python(); else if (!strcmp(command,"quit")) quit(); @@ -1031,14 +1031,6 @@ void Input::label() /* ---------------------------------------------------------------------- */ -void Input::load_plugin() -{ - if (narg != 1) error->all(FLERR,"Illegal load_plugin command"); - lammpsplugin_load(arg[0],lmp); -} - -/* ---------------------------------------------------------------------- */ - void Input::log() { if ((narg < 1) || (narg > 2)) error->all(FLERR,"Illegal log command"); @@ -1107,6 +1099,32 @@ void Input::partition() /* ---------------------------------------------------------------------- */ +void Input::plugin() +{ + if (narg < 1) error->all(FLERR,"Illegal plugin command"); + std::string cmd = arg[0]; + if (cmd == "load") { + if (narg < 2) error->all(FLERR,"Illegal plugin load command"); + for (int i=1; i < narg; ++i) + lammpsplugin_load(arg[i],lmp); + } else if (cmd == "unload") { + if (narg != 3) error->all(FLERR,"Illegal plugin unload command"); + lammpsplugin_unload(arg[1],arg[2],lmp); + } else if (cmd == "list") { + if (comm->me == 0) { + int num = lammpsplugin_get_num_plugins(); + utils::logmesg(lmp,"Currently loaded plugins\n"); + for (int i=0; i < num; ++i) { + auto entry = lammpsplugin_info(i); + utils::logmesg(lmp,fmt::format("{:4}: {} style plugin {}\n", + i+1,entry->style,entry->name)); + } + } + } else error->all(FLERR,"Illegal plugin command"); +} + +/* ---------------------------------------------------------------------- */ + void Input::print() { if (narg < 1) error->all(FLERR,"Illegal print command"); diff --git a/src/input.h b/src/input.h index 3b7b6d79c7..809a7a5f94 100644 --- a/src/input.h +++ b/src/input.h @@ -79,10 +79,10 @@ class Input : protected Pointers { void include(); void jump(); void label(); - void load_plugin(); void log(); void next_command(); void partition(); + void plugin(); void print(); void python(); void quit(); diff --git a/src/lammpsplugin.cpp b/src/lammpsplugin.cpp index 744203cd5e..3086b829c0 100644 --- a/src/lammpsplugin.cpp +++ b/src/lammpsplugin.cpp @@ -18,6 +18,7 @@ #include "lammps.h" #include "modify.h" +#include #ifdef _WIN32 #include @@ -27,6 +28,7 @@ namespace LAMMPS_NS { + static std::vector pluginlist; void lammpsplugin_load(const char *file, void *ptr) { @@ -34,7 +36,7 @@ namespace LAMMPS_NS #if defined(WIN32) utils::logmesg(lmp,"Loading of plugins not supported on Windows yet\n"); #else - void *dso = dlopen(file,RTLD_NOW); + void *dso = dlopen(file,RTLD_NOW|RTLD_GLOBAL); if (dso == nullptr) { utils::logmesg(lmp,fmt::format("Loading of plugin from file {} failed: " "{}", file, utils::getsyserror())); @@ -48,6 +50,7 @@ namespace LAMMPS_NS return; } ((lammpsplugin_initfunc)(initfunc))(ptr); +// dlclose(dso); #endif } @@ -61,6 +64,12 @@ namespace LAMMPS_NS "version {} loaded into LAMMPS " "version {}\n", plugin->info, plugin->version, lmp->version)); + lammpsplugin_entry_t entry; + entry.style = plugin->style; + entry.name = plugin->name; + entry.handle = nullptr; + pluginlist.push_back(entry); + std::string pstyle = plugin->style; if (pstyle == "pair") { (*(lmp->force->pair_map))[plugin->name] = @@ -68,6 +77,41 @@ namespace LAMMPS_NS } else { utils::logmesg(lmp,fmt::format("Loading plugin for {} styles not " "yet implemented\n", pstyle)); + pluginlist.pop_back(); + } + } + + int lammpsplugin_get_num_plugins() + { + return pluginlist.size(); + } + + const lammpsplugin_entry_t *lammpsplugin_info(int idx) + { + if ((idx < 0) || idx >= pluginlist.size()) return nullptr; + + return &pluginlist[idx]; + } + + int lammpsplugin_find(const char *style, const char *name) + { + for (int i=0; i < pluginlist.size(); ++i) { + if ((strcmp(style,pluginlist[i].style) == 0) + && (strcmp(name,pluginlist[i].name) == 0)) + return i; + } + return -1; + } + + void lammpsplugin_unload(const char *style, const char *name, void *ptr) + { + LAMMPS *lmp = (LAMMPS *)ptr; + std::string pstyle = style; + if (pstyle == "pair") { + auto found = lmp->force->pair_map->find(name); + if (found != lmp->force->pair_map->end()) { + lmp->force->pair_map->erase(found); + } } } } diff --git a/src/lammpsplugin.h b/src/lammpsplugin.h index 13076cc86f..36c442aa5e 100644 --- a/src/lammpsplugin.h +++ b/src/lammpsplugin.h @@ -14,9 +14,9 @@ #ifndef LMP_LAMMPSPLUGIN_H #define LMP_LAMMPSPLUGIN_H -#ifdef __cplusplus +// C style API and data structures required for dynamic loading + extern "C" { -#endif typedef void *(lammpsplugin_factory)(void *); typedef void (*lammpsplugin_initfunc)(void *); @@ -29,16 +29,26 @@ extern "C" { lammpsplugin_factory *creator; } lammpsplugin_t; - void lammpsplugin_init(void *); + typedef struct { + const char *style; + const char *name; + const void *handle; + } lammpsplugin_entry_t; -#ifdef __cplusplus + // prototype for initializer function required + // to load a plugin; uses C bindings + + void lammpsplugin_init(void *); } -#endif namespace LAMMPS_NS { - extern void lammpsplugin_load(const char *, void *); - extern void lammpsplugin_register(lammpsplugin_t *, void *); + extern void lammpsplugin_load(const char *, void *); + extern void lammpsplugin_register(lammpsplugin_t *, void *); + extern int lammpsplugin_get_num_plugins(); + extern const lammpsplugin_entry_t *lammpsplugin_info(int); + extern int lammpsplugin_find(const char *, const char *); + extern void lammpsplugin_unload(const char *, const char *, void *); } #endif From 8e1ccb6123e4d1800ad0f4eac27ec22aa1ce870d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 07:26:57 -0500 Subject: [PATCH 06/57] next iteration: rename functions/files, split header, store dso handle --- examples/plugins/morse2plugin.cpp | 12 +++++++----- src/input.cpp | 10 +++++----- src/lammpsplugin.h | 24 ++++++----------------- src/{lammpsplugin.cpp => plugin.cpp} | 27 ++++++++++++-------------- src/plugin.h | 29 ++++++++++++++++++++++++++++ 5 files changed, 59 insertions(+), 43 deletions(-) rename src/{lammpsplugin.cpp => plugin.cpp} (81%) create mode 100644 src/plugin.h diff --git a/examples/plugins/morse2plugin.cpp b/examples/plugins/morse2plugin.cpp index fbb0882a92..d7290edfb0 100644 --- a/examples/plugins/morse2plugin.cpp +++ b/examples/plugins/morse2plugin.cpp @@ -22,19 +22,21 @@ static Pair *morse2ompcreator(LAMMPS *lmp) } static lammpsplugin_t plugin; -void lammpsplugin_init(void *lmp) +extern "C" void lammpsplugin_init(void *lmp, void *handle, lammpsplugin_regfunc register_plugin) { memset(&plugin,0,sizeof(lammpsplugin_t)); plugin.version = LAMMPS_VERSION; plugin.style = "pair"; plugin.name = "morse2"; - plugin.info = "Morse2 variant pair style v1.0 by Axel Kohlmeyer"; + plugin.info = "Morse2 variant pair style v1.0"; + plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; plugin.creator = (lammpsplugin_factory *) &morse2creator; - lammpsplugin_register(&plugin, (LAMMPS *)lmp); + plugin.handle = handle; + register_plugin(&plugin,lmp); plugin.style = "pair"; plugin.name = "morse2/omp"; - plugin.info = "Morse2 variant pair style for OpenMP v1.0 by Axel Kohlmeyer"; + plugin.info = "Morse2 variant pair style for OpenMP v1.0"; plugin.creator = (lammpsplugin_factory *) &morse2ompcreator; - lammpsplugin_register(&plugin, (LAMMPS *)lmp); + register_plugin(&plugin,lmp); } diff --git a/src/input.cpp b/src/input.cpp index 07e3c417f6..73a73cd369 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -29,13 +29,13 @@ #include "group.h" #include "improper.h" #include "kspace.h" -#include "lammpsplugin.h" #include "memory.h" #include "min.h" #include "modify.h" #include "neighbor.h" #include "output.h" #include "pair.h" +#include "plugin.h" #include "special.h" #include "style_command.h" #include "thermo.h" @@ -1106,16 +1106,16 @@ void Input::plugin() if (cmd == "load") { if (narg < 2) error->all(FLERR,"Illegal plugin load command"); for (int i=1; i < narg; ++i) - lammpsplugin_load(arg[i],lmp); + plugin_load(arg[i],lmp); } else if (cmd == "unload") { if (narg != 3) error->all(FLERR,"Illegal plugin unload command"); - lammpsplugin_unload(arg[1],arg[2],lmp); + plugin_unload(arg[1],arg[2],lmp); } else if (cmd == "list") { if (comm->me == 0) { - int num = lammpsplugin_get_num_plugins(); + int num = plugin_get_num_plugins(); utils::logmesg(lmp,"Currently loaded plugins\n"); for (int i=0; i < num; ++i) { - auto entry = lammpsplugin_info(i); + auto entry = plugin_info(i); utils::logmesg(lmp,fmt::format("{:4}: {} style plugin {}\n", i+1,entry->style,entry->name)); } diff --git a/src/lammpsplugin.h b/src/lammpsplugin.h index 36c442aa5e..6cdf67e09b 100644 --- a/src/lammpsplugin.h +++ b/src/lammpsplugin.h @@ -14,41 +14,29 @@ #ifndef LMP_LAMMPSPLUGIN_H #define LMP_LAMMPSPLUGIN_H -// C style API and data structures required for dynamic loading +// C style API and data structure required for dynamic loading extern "C" { typedef void *(lammpsplugin_factory)(void *); - typedef void (*lammpsplugin_initfunc)(void *); typedef struct { const char *version; const char *style; const char *name; const char *info; + const char *author; lammpsplugin_factory *creator; + void *handle; } lammpsplugin_t; - typedef struct { - const char *style; - const char *name; - const void *handle; - } lammpsplugin_entry_t; + typedef void (*lammpsplugin_regfunc)(lammpsplugin_t *, void *); + typedef void (*lammpsplugin_initfunc)(void *, void *, lammpsplugin_regfunc); // prototype for initializer function required // to load a plugin; uses C bindings - void lammpsplugin_init(void *); -} - -namespace LAMMPS_NS -{ - extern void lammpsplugin_load(const char *, void *); - extern void lammpsplugin_register(lammpsplugin_t *, void *); - extern int lammpsplugin_get_num_plugins(); - extern const lammpsplugin_entry_t *lammpsplugin_info(int); - extern int lammpsplugin_find(const char *, const char *); - extern void lammpsplugin_unload(const char *, const char *, void *); + void lammpsplugin_init(void *, void *, lammpsplugin_regfunc); } #endif diff --git a/src/lammpsplugin.cpp b/src/plugin.cpp similarity index 81% rename from src/lammpsplugin.cpp rename to src/plugin.cpp index 3086b829c0..f81c453afb 100644 --- a/src/lammpsplugin.cpp +++ b/src/plugin.cpp @@ -11,7 +11,7 @@ See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ -#include "lammpsplugin.h" +#include "plugin.h" #include "error.h" #include "force.h" @@ -28,9 +28,9 @@ namespace LAMMPS_NS { - static std::vector pluginlist; + static std::vector pluginlist; - void lammpsplugin_load(const char *file, void *ptr) + void plugin_load(const char *file, void *ptr) { LAMMPS *lmp = (LAMMPS *)ptr; #if defined(WIN32) @@ -46,15 +46,15 @@ namespace LAMMPS_NS void *initfunc = dlsym(dso,"lammpsplugin_init"); if (initfunc == nullptr) { dlclose(dso); - utils::logmesg(lmp,fmt::format("Symbol lookup failure in file {}",file)); + utils::logmesg(lmp,fmt::format("Symbol lookup failure in file {}\n",file)); return; } - ((lammpsplugin_initfunc)(initfunc))(ptr); + ((lammpsplugin_initfunc)(initfunc))(ptr,dso,&plugin_register); // dlclose(dso); #endif } - void lammpsplugin_register(lammpsplugin_t *plugin, void *ptr) + void plugin_register(lammpsplugin_t *plugin, void *ptr) { LAMMPS *lmp = (LAMMPS *)ptr; @@ -64,11 +64,8 @@ namespace LAMMPS_NS "version {} loaded into LAMMPS " "version {}\n", plugin->info, plugin->version, lmp->version)); - lammpsplugin_entry_t entry; - entry.style = plugin->style; - entry.name = plugin->name; - entry.handle = nullptr; - pluginlist.push_back(entry); + + pluginlist.push_back(*plugin); std::string pstyle = plugin->style; if (pstyle == "pair") { @@ -81,19 +78,19 @@ namespace LAMMPS_NS } } - int lammpsplugin_get_num_plugins() + int plugin_get_num_plugins() { return pluginlist.size(); } - const lammpsplugin_entry_t *lammpsplugin_info(int idx) + const lammpsplugin_t *plugin_info(int idx) { if ((idx < 0) || idx >= pluginlist.size()) return nullptr; return &pluginlist[idx]; } - int lammpsplugin_find(const char *style, const char *name) + int plugin_find(const char *style, const char *name) { for (int i=0; i < pluginlist.size(); ++i) { if ((strcmp(style,pluginlist[i].style) == 0) @@ -103,7 +100,7 @@ namespace LAMMPS_NS return -1; } - void lammpsplugin_unload(const char *style, const char *name, void *ptr) + void plugin_unload(const char *style, const char *name, void *ptr) { LAMMPS *lmp = (LAMMPS *)ptr; std::string pstyle = style; diff --git a/src/plugin.h b/src/plugin.h new file mode 100644 index 0000000000..546ab2a644 --- /dev/null +++ b/src/plugin.h @@ -0,0 +1,29 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef LMP_PLUGIN_H +#define LMP_PLUGIN_H + +#include "lammpsplugin.h" + +namespace LAMMPS_NS +{ + void plugin_load(const char *, void *); + void plugin_register(lammpsplugin_t *, void *); + int plugin_get_num_plugins(); + const lammpsplugin_t *plugin_info(int); + int plugin_find(const char *, const char *); + void plugin_unload(const char *, const char *, void *); +} + +#endif From ffda7fcc04f926821ccd87dbe7cd7a51ad5f13a7 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 14:02:21 -0500 Subject: [PATCH 07/57] simpler interfaces --- examples/plugins/morse2plugin.cpp | 32 ++++++++++++++++--------------- src/lammpsplugin.h | 4 ++-- src/plugin.cpp | 27 +++++++++++++++++++------- src/plugin.h | 5 +++-- 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/examples/plugins/morse2plugin.cpp b/examples/plugins/morse2plugin.cpp index d7290edfb0..cfb6689cae 100644 --- a/examples/plugins/morse2plugin.cpp +++ b/examples/plugins/morse2plugin.cpp @@ -22,21 +22,23 @@ static Pair *morse2ompcreator(LAMMPS *lmp) } static lammpsplugin_t plugin; -extern "C" void lammpsplugin_init(void *lmp, void *handle, lammpsplugin_regfunc register_plugin) +extern "C" void lammpsplugin_init(void *lmp, void *handle, void *regfunc) { - memset(&plugin,0,sizeof(lammpsplugin_t)); - 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 = (lammpsplugin_factory *) &morse2creator; - plugin.handle = handle; - register_plugin(&plugin,lmp); + // register plain morse2 pair style + lammpsplugin_regfunc register_plugin = (lammpsplugin_regfunc) regfunc; + memset(&plugin,0,sizeof(lammpsplugin_t)); + 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 = (lammpsplugin_factory *) &morse2creator; + plugin.handle = handle; + register_plugin(&plugin,lmp); - plugin.style = "pair"; - plugin.name = "morse2/omp"; - plugin.info = "Morse2 variant pair style for OpenMP v1.0"; - plugin.creator = (lammpsplugin_factory *) &morse2ompcreator; - register_plugin(&plugin,lmp); + // also register morse2/omp pair style. only need to update changed fields + plugin.name = "morse2/omp"; + plugin.info = "Morse2 variant pair style for OpenMP v1.0"; + plugin.creator = (lammpsplugin_factory *) &morse2ompcreator; + register_plugin(&plugin,lmp); } diff --git a/src/lammpsplugin.h b/src/lammpsplugin.h index 6cdf67e09b..34a0b60811 100644 --- a/src/lammpsplugin.h +++ b/src/lammpsplugin.h @@ -31,12 +31,12 @@ extern "C" { } lammpsplugin_t; typedef void (*lammpsplugin_regfunc)(lammpsplugin_t *, void *); - typedef void (*lammpsplugin_initfunc)(void *, void *, lammpsplugin_regfunc); + typedef void (*lammpsplugin_initfunc)(void *, void *, void *); // prototype for initializer function required // to load a plugin; uses C bindings - void lammpsplugin_init(void *, void *, lammpsplugin_regfunc); + void lammpsplugin_init(void *, void *, void *); } #endif diff --git a/src/plugin.cpp b/src/plugin.cpp index f81c453afb..e978d90059 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -18,6 +18,7 @@ #include "lammps.h" #include "modify.h" +#include #include #ifdef _WIN32 @@ -28,14 +29,20 @@ namespace LAMMPS_NS { + // store list of plugin information data for loaded styles static std::vector pluginlist; + // map of dso handles + static std::map dso_refcounter; - void plugin_load(const char *file, void *ptr) + // load DSO and call included registration function + void plugin_load(const char *file, LAMMPS *lmp) { - LAMMPS *lmp = (LAMMPS *)ptr; #if defined(WIN32) utils::logmesg(lmp,"Loading of plugins not supported on Windows yet\n"); #else + + // open DSO from given path + void *dso = dlopen(file,RTLD_NOW|RTLD_GLOBAL); if (dso == nullptr) { utils::logmesg(lmp,fmt::format("Loading of plugin from file {} failed: " @@ -43,17 +50,23 @@ namespace LAMMPS_NS return; } + // look up lammpsplugin_init() function in DSO. must have C bindings. + void *initfunc = dlsym(dso,"lammpsplugin_init"); if (initfunc == nullptr) { dlclose(dso); utils::logmesg(lmp,fmt::format("Symbol lookup failure in file {}\n",file)); return; } - ((lammpsplugin_initfunc)(initfunc))(ptr,dso,&plugin_register); -// dlclose(dso); + + // call initializer function loaded from DSO and pass pointer to LAMMPS instance, + // the DSO handle (for reference counting) and plugin registration function pointer + + ((lammpsplugin_initfunc)(initfunc))((void *)lmp, dso, (void *)&plugin_register); #endif } + // register new style from plugin with LAMMPS void plugin_register(lammpsplugin_t *plugin, void *ptr) { LAMMPS *lmp = (LAMMPS *)ptr; @@ -99,10 +112,10 @@ namespace LAMMPS_NS } return -1; } - - void plugin_unload(const char *style, const char *name, void *ptr) + + // remove plugin from given style table + void plugin_unload(const char *style, const char *name, LAMMPS *lmp) { - LAMMPS *lmp = (LAMMPS *)ptr; std::string pstyle = style; if (pstyle == "pair") { auto found = lmp->force->pair_map->find(name); diff --git a/src/plugin.h b/src/plugin.h index 546ab2a644..051382ac27 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -18,12 +18,13 @@ namespace LAMMPS_NS { - void plugin_load(const char *, void *); + class LAMMPS; + void plugin_load(const char *, LAMMPS *); void plugin_register(lammpsplugin_t *, void *); int plugin_get_num_plugins(); const lammpsplugin_t *plugin_info(int); int plugin_find(const char *, const char *); - void plugin_unload(const char *, const char *, void *); + void plugin_unload(const char *, const char *, LAMMPS *); } #endif From c78ddb29ddf72dc148e1615c02a6af1c30f0ac9d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 16:57:37 -0500 Subject: [PATCH 08/57] implement reference counting and means to close unused DSO. Must delete pair style when plugin version is in use --- src/input.cpp | 2 +- src/plugin.cpp | 80 ++++++++++++++++++++++++++++++++++++++++---------- src/plugin.h | 3 +- 3 files changed, 68 insertions(+), 17 deletions(-) diff --git a/src/input.cpp b/src/input.cpp index 73a73cd369..6d1564ecb6 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -1115,7 +1115,7 @@ void Input::plugin() int num = plugin_get_num_plugins(); utils::logmesg(lmp,"Currently loaded plugins\n"); for (int i=0; i < num; ++i) { - auto entry = plugin_info(i); + auto entry = plugin_get_info(i); utils::logmesg(lmp,fmt::format("{:4}: {} style plugin {}\n", i+1,entry->style,entry->name)); } diff --git a/src/plugin.cpp b/src/plugin.cpp index e978d90059..a3126b8acd 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -17,9 +17,10 @@ #include "force.h" #include "lammps.h" #include "modify.h" +#include "pair.h" #include -#include +#include #ifdef _WIN32 #include @@ -30,7 +31,7 @@ namespace LAMMPS_NS { // store list of plugin information data for loaded styles - static std::vector pluginlist; + static std::list pluginlist; // map of dso handles static std::map dso_refcounter; @@ -72,13 +73,18 @@ namespace LAMMPS_NS LAMMPS *lmp = (LAMMPS *)ptr; if (plugin == nullptr) return; - if (plugin->version) - utils::logmesg(lmp,fmt::format("Loading: {}\n compiled for LAMMPS " - "version {} loaded into LAMMPS " - "version {}\n", plugin->info, + utils::logmesg(lmp,fmt::format("Loading plugin: {} by {}\n", + plugin->info, plugin->author)); + if ((plugin->version) && (strcmp(plugin->version,lmp->version) != 0)) + utils::logmesg(lmp,fmt::format(" compiled for LAMMPS version {} " + "loaded into LAMMPS version {}\n", plugin->version, lmp->version)); - pluginlist.push_back(*plugin); + if (dso_refcounter.find(plugin->handle) != dso_refcounter.end()) { + ++ dso_refcounter[plugin->handle]; + } else { + dso_refcounter[plugin->handle] = 1; + } std::string pstyle = plugin->style; if (pstyle == "pair") { @@ -96,32 +102,76 @@ namespace LAMMPS_NS return pluginlist.size(); } - const lammpsplugin_t *plugin_info(int idx) + const lammpsplugin_t *plugin_get_info(int idx) { - if ((idx < 0) || idx >= pluginlist.size()) return nullptr; - - return &pluginlist[idx]; + int i=0; + for (auto p=pluginlist.begin(); p != pluginlist.end(); ++p) { + if (i == idx) return &(*p); + ++i; + } + return nullptr; } int plugin_find(const char *style, const char *name) { - for (int i=0; i < pluginlist.size(); ++i) { - if ((strcmp(style,pluginlist[i].style) == 0) - && (strcmp(name,pluginlist[i].name) == 0)) + int i=0; + for (auto entry : pluginlist) { + if ((strcmp(style,entry.style) == 0) + && (strcmp(name,entry.name) == 0)) return i; + ++i; } return -1; } - // remove plugin from given style table + void plugin_erase(const char *style, const char *name) + { + fmt::print("Erasing {} style {} from list with {} plugins\n", + style, name, pluginlist.size()); + for (auto p=pluginlist.begin(); p != pluginlist.end(); ++p) { + if ((strcmp(style,p->style) == 0) + && (strcmp(name,p->name) == 0)) { + pluginlist.erase(p); + return; + } + } + } + + // remove plugin from given style table and plugin list. + // close dso handle if this is the last plugin from that dso. void plugin_unload(const char *style, const char *name, LAMMPS *lmp) { + int idx = plugin_find(style,name); + if (idx < 0) + lmp->error->all(FLERR,fmt::format("{} style {} is not loaded from " + "a plugin", style, name)); + auto plugin = plugin_get_info(idx); + void *handle = plugin->handle; + utils::logmesg(lmp,fmt::format("Unloading {} style {}\n",style,name)); + plugin_erase(style,name); + std::string pstyle = style; if (pstyle == "pair") { auto found = lmp->force->pair_map->find(name); if (found != lmp->force->pair_map->end()) { lmp->force->pair_map->erase(found); } + + // must delete pair style instance if in use + if (lmp->force->pair_style) { + if (utils::strmatch(lmp->force->pair_style,"^hybrid")) { + if (lmp->force->pair_match(name,1,1) != nullptr) + lmp->force->create_pair("none",0); + } else { + if (strcmp(lmp->force->pair_style,name) == 0) + lmp->force->create_pair("none",0); + } + } + } + + -- dso_refcounter[handle]; + if (dso_refcounter[handle] == 0) { + dlclose(handle); } } } diff --git a/src/plugin.h b/src/plugin.h index 051382ac27..172b2f0c37 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -22,8 +22,9 @@ namespace LAMMPS_NS void plugin_load(const char *, LAMMPS *); void plugin_register(lammpsplugin_t *, void *); int plugin_get_num_plugins(); - const lammpsplugin_t *plugin_info(int); + const lammpsplugin_t *plugin_get_info(int); int plugin_find(const char *, const char *); + void plugin_erase(const char *, const char *); void plugin_unload(const char *, const char *, LAMMPS *); } From b8ae2f5c6f3a4f4e53e19695733c696eb1e631d1 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 17:41:08 -0500 Subject: [PATCH 09/57] add comments, extra checks, have output only on MPI rank 0 --- src/plugin.cpp | 123 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 86 insertions(+), 37 deletions(-) diff --git a/src/plugin.cpp b/src/plugin.cpp index a3126b8acd..1b9db01780 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -13,6 +13,7 @@ #include "plugin.h" +#include "comm.h" #include "error.h" #include "force.h" #include "lammps.h" @@ -38,16 +39,18 @@ namespace LAMMPS_NS // load DSO and call included registration function void plugin_load(const char *file, LAMMPS *lmp) { + int me = lmp->comm->me; #if defined(WIN32) - utils::logmesg(lmp,"Loading of plugins not supported on Windows yet\n"); + lmp->error->all(FLERR,"Loading of plugins on Windows not yet supported\n"); #else - // open DSO from given path + // open DSO file from given path load symbols globally void *dso = dlopen(file,RTLD_NOW|RTLD_GLOBAL); if (dso == nullptr) { - utils::logmesg(lmp,fmt::format("Loading of plugin from file {} failed: " - "{}", file, utils::getsyserror())); + if (me == 0) + utils::logmesg(lmp,fmt::format("Open of plugin file {} failed: {}", + file,utils::getsyserror())); return; } @@ -56,30 +59,44 @@ namespace LAMMPS_NS void *initfunc = dlsym(dso,"lammpsplugin_init"); if (initfunc == nullptr) { dlclose(dso); - utils::logmesg(lmp,fmt::format("Symbol lookup failure in file {}\n",file)); + if (me == 0) + utils::logmesg(lmp,fmt::format("Plugin symbol lookup failure in " + "file {}\n",file)); return; } - // call initializer function loaded from DSO and pass pointer to LAMMPS instance, - // the DSO handle (for reference counting) and plugin registration function pointer + // call initializer function loaded from DSO and pass a pointer + // to the LAMMPS instance, the DSO handle (for reference counting) + // and plugin registration function pointer - ((lammpsplugin_initfunc)(initfunc))((void *)lmp, dso, (void *)&plugin_register); + ((lammpsplugin_initfunc)(initfunc))((void *)lmp, dso, + (void *)&plugin_register); #endif } - // register new style from plugin with LAMMPS + // register a new style from a plugin with LAMMPS + // this is the callback function that is called from within + // the plugin initializer function. all plugin information + // is taken from the lammpsplugin_t struct. + void plugin_register(lammpsplugin_t *plugin, void *ptr) { LAMMPS *lmp = (LAMMPS *)ptr; + int me = lmp->comm->me; if (plugin == nullptr) return; - utils::logmesg(lmp,fmt::format("Loading plugin: {} by {}\n", - plugin->info, plugin->author)); - if ((plugin->version) && (strcmp(plugin->version,lmp->version) != 0)) - utils::logmesg(lmp,fmt::format(" compiled for LAMMPS version {} " - "loaded into LAMMPS version {}\n", - plugin->version, lmp->version)); + if (me == 0) { + utils::logmesg(lmp,fmt::format("Loading plugin: {} by {}\n", + plugin->info, plugin->author)); + // print version info only if the versions of host and plugin don't match + if ((plugin->version) && (strcmp(plugin->version,lmp->version) != 0)) + utils::logmesg(lmp,fmt::format(" compiled for LAMMPS version {} " + "loaded into LAMMPS version {}\n", + plugin->version, lmp->version)); + } + pluginlist.push_back(*plugin); + if (dso_refcounter.find(plugin->handle) != dso_refcounter.end()) { ++ dso_refcounter[plugin->handle]; } else { @@ -88,8 +105,15 @@ namespace LAMMPS_NS std::string pstyle = plugin->style; if (pstyle == "pair") { - (*(lmp->force->pair_map))[plugin->name] = - (LAMMPS_NS::Force::PairCreator)plugin->creator; + auto pair_map = lmp->force->pair_map; + if (pair_map->find(plugin->name) != pair_map->end()) { + if (lmp->comm->me == 0) + lmp->error->warning(FLERR,fmt::format("Overriding built-in pair " + "style {} from plugin", + plugin->name)); + } + + (*pair_map)[plugin->name] = (Force::PairCreator)plugin->creator; } else { utils::logmesg(lmp,fmt::format("Loading plugin for {} styles not " "yet implemented\n", pstyle)); @@ -97,20 +121,14 @@ namespace LAMMPS_NS } } + // number of styles loaded from plugin files + int plugin_get_num_plugins() { return pluginlist.size(); } - const lammpsplugin_t *plugin_get_info(int idx) - { - int i=0; - for (auto p=pluginlist.begin(); p != pluginlist.end(); ++p) { - if (i == idx) return &(*p); - ++i; - } - return nullptr; - } + // return position index in list of given plugin of given style int plugin_find(const char *style, const char *name) { @@ -124,10 +142,22 @@ namespace LAMMPS_NS return -1; } + // get pointer to plugin initializer struct at position idx + + const lammpsplugin_t *plugin_get_info(int idx) + { + int i=0; + for (auto p=pluginlist.begin(); p != pluginlist.end(); ++p) { + if (i == idx) return &(*p); + ++i; + } + return nullptr; + } + + // remove plugin of given name and style from internal lists + void plugin_erase(const char *style, const char *name) { - fmt::print("Erasing {} style {} from list with {} plugins\n", - style, name, pluginlist.size()); for (auto p=pluginlist.begin(); p != pluginlist.end(); ++p) { if ((strcmp(style,p->style) == 0) && (strcmp(name,p->name) == 0)) { @@ -138,26 +168,43 @@ namespace LAMMPS_NS } // remove plugin from given style table and plugin list. - // close dso handle if this is the last plugin from that dso. + // optionally close the DSO handle if last plugin from that DSO + // must delete style instance if style is currently active. + void plugin_unload(const char *style, const char *name, LAMMPS *lmp) { + int me = lmp->comm->me; + + // ignore unload request if not loaded from a plugin int idx = plugin_find(style,name); - if (idx < 0) - lmp->error->all(FLERR,fmt::format("{} style {} is not loaded from " - "a plugin", style, name)); - auto plugin = plugin_get_info(idx); - void *handle = plugin->handle; - utils::logmesg(lmp,fmt::format("Unloading {} style {}\n",style,name)); + if (idx < 0) { + if (me == 0) + utils::logmesg(lmp,fmt::format("Ignoring unload of {} style {}: not " + "loaded from a plugin", style, name)); + return; + } + + // copy of DSO handle for later + void *handle = plugin_get_info(idx)->handle; + + // remove selected plugin from list of plugins + + if (me == 0) + utils::logmesg(lmp,fmt::format("Unloading {} style {}\n",style,name)); plugin_erase(style,name); + // remove style of given name from corresponding map + // must delete style instance if currently active so + // we can close the DSO handle if the last reference is gone. + std::string pstyle = style; if (pstyle == "pair") { auto found = lmp->force->pair_map->find(name); - if (found != lmp->force->pair_map->end()) { + if (found != lmp->force->pair_map->end()) lmp->force->pair_map->erase(found); - } // must delete pair style instance if in use + if (lmp->force->pair_style) { if (utils::strmatch(lmp->force->pair_style,"^hybrid")) { if (lmp->force->pair_match(name,1,1) != nullptr) @@ -169,6 +216,8 @@ namespace LAMMPS_NS } } + // if reference count is down to zero, close DSO handle. + -- dso_refcounter[handle]; if (dso_refcounter[handle] == 0) { dlclose(handle); From ee8246f590979e561562fdc621d9598d7004bd53 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 17:44:05 -0500 Subject: [PATCH 10/57] recover compilation on windows --- src/plugin.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugin.cpp b/src/plugin.cpp index 1b9db01780..19a5c1b754 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -58,7 +58,9 @@ namespace LAMMPS_NS void *initfunc = dlsym(dso,"lammpsplugin_init"); if (initfunc == nullptr) { +#ifndef WIN32 dlclose(dso); +#endif if (me == 0) utils::logmesg(lmp,fmt::format("Plugin symbol lookup failure in " "file {}\n",file)); From 3ec9f2fd5e0b72a942a91ca7ad78807c51b04279 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 17:44:11 -0500 Subject: [PATCH 11/57] whitespace --- src/plugin.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugin.cpp b/src/plugin.cpp index 19a5c1b754..7e7bd6e86f 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -33,7 +33,7 @@ namespace LAMMPS_NS { // store list of plugin information data for loaded styles static std::list pluginlist; - // map of dso handles + // map of dso handles static std::map dso_refcounter; // load DSO and call included registration function @@ -45,7 +45,7 @@ namespace LAMMPS_NS #else // open DSO file from given path load symbols globally - + void *dso = dlopen(file,RTLD_NOW|RTLD_GLOBAL); if (dso == nullptr) { if (me == 0) @@ -125,7 +125,7 @@ namespace LAMMPS_NS // number of styles loaded from plugin files - int plugin_get_num_plugins() + int plugin_get_num_plugins() { return pluginlist.size(); } @@ -210,7 +210,7 @@ namespace LAMMPS_NS if (lmp->force->pair_style) { if (utils::strmatch(lmp->force->pair_style,"^hybrid")) { if (lmp->force->pair_match(name,1,1) != nullptr) - lmp->force->create_pair("none",0); + lmp->force->create_pair("none",0); } else { if (strcmp(lmp->force->pair_style,name) == 0) lmp->force->create_pair("none",0); From c61de8740c65c8f9555cc08ac6d23fc281a52d4e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 19:33:07 -0500 Subject: [PATCH 12/57] add preliminary documentation for plugin command and about how to write plugins --- doc/src/Commands_all.rst | 1 + doc/src/Developer.rst | 1 + doc/src/Developer_plugins.rst | 98 +++++++++++++++++++++++++++++++++++ doc/src/commands_list.rst | 1 + 4 files changed, 101 insertions(+) create mode 100644 doc/src/Developer_plugins.rst diff --git a/doc/src/Commands_all.rst b/doc/src/Commands_all.rst index 132425948e..b43fd0ed56 100644 --- a/doc/src/Commands_all.rst +++ b/doc/src/Commands_all.rst @@ -86,6 +86,7 @@ An alphabetic list of all general LAMMPS commands. * :doc:`pair_style ` * :doc:`pair_write ` * :doc:`partition ` + * :doc:`plugin ` * :doc:`prd ` * :doc:`print ` * :doc:`processors ` diff --git a/doc/src/Developer.rst b/doc/src/Developer.rst index 3a0c03b7ea..f54bc4152f 100644 --- a/doc/src/Developer.rst +++ b/doc/src/Developer.rst @@ -14,6 +14,7 @@ of time and requests from the LAMMPS user community. Developer_flow Developer_write Developer_notes + Developer_plugins Developer_unittest Classes Developer_utils diff --git a/doc/src/Developer_plugins.rst b/doc/src/Developer_plugins.rst new file mode 100644 index 0000000000..ffbcf8a667 --- /dev/null +++ b/doc/src/Developer_plugins.rst @@ -0,0 +1,98 @@ +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 = (lammpsplugin_factory *) &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 argument and 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. + +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 added to +the plugin info struct, so that the DSO can be close 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. + +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, DSO handle. +The 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. diff --git a/doc/src/commands_list.rst b/doc/src/commands_list.rst index 2ec20ac220..e30d5c52dc 100644 --- a/doc/src/commands_list.rst +++ b/doc/src/commands_list.rst @@ -77,6 +77,7 @@ Commands pair_style pair_write partition + plugin prd print processors From dfe4f7a49d353a22c1c9cd56f2027e0b3a9d9ef3 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 19:33:50 -0500 Subject: [PATCH 13/57] small tweaks and simplify --- examples/plugins/morse2plugin.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/plugins/morse2plugin.cpp b/examples/plugins/morse2plugin.cpp index cfb6689cae..286aa30c35 100644 --- a/examples/plugins/morse2plugin.cpp +++ b/examples/plugins/morse2plugin.cpp @@ -1,7 +1,6 @@ #include "lammpsplugin.h" -#include "lammps.h" #include "version.h" #include @@ -21,12 +20,12 @@ static Pair *morse2ompcreator(LAMMPS *lmp) return new PairMorse2OMP(lmp); } -static lammpsplugin_t plugin; extern "C" void lammpsplugin_init(void *lmp, void *handle, void *regfunc) { - // register plain morse2 pair style + lammpsplugin_t plugin; lammpsplugin_regfunc register_plugin = (lammpsplugin_regfunc) regfunc; - memset(&plugin,0,sizeof(lammpsplugin_t)); + + // register plain morse2 pair style plugin.version = LAMMPS_VERSION; plugin.style = "pair"; plugin.name = "morse2"; @@ -34,11 +33,11 @@ extern "C" void lammpsplugin_init(void *lmp, void *handle, void *regfunc) plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; plugin.creator = (lammpsplugin_factory *) &morse2creator; plugin.handle = handle; - register_plugin(&plugin,lmp); + (*register_plugin)(&plugin,lmp); // also register morse2/omp pair style. only need to update changed fields plugin.name = "morse2/omp"; plugin.info = "Morse2 variant pair style for OpenMP v1.0"; plugin.creator = (lammpsplugin_factory *) &morse2ompcreator; - register_plugin(&plugin,lmp); + (*register_plugin)(&plugin,lmp); } From b252946fba664c51e92a508b49a96ffd0ace0717 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 19:34:13 -0500 Subject: [PATCH 14/57] now fix windows compile for real --- src/plugin.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugin.cpp b/src/plugin.cpp index 7e7bd6e86f..07e3c60dd0 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -222,7 +222,9 @@ namespace LAMMPS_NS -- dso_refcounter[handle]; if (dso_refcounter[handle] == 0) { +#ifndef WIN32 dlclose(handle); +#endif } } } From f982d98574269cd4e04db9616664f1758b1cb83d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 19:34:28 -0500 Subject: [PATCH 15/57] small tweaks --- examples/plugins/Makefile | 2 +- src/plugin.cpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/plugins/Makefile b/examples/plugins/Makefile index 2f563f924e..5d60ae8651 100644 --- a/examples/plugins/Makefile +++ b/examples/plugins/Makefile @@ -1,5 +1,5 @@ CXX=mpicxx -CXXFLAGS=-I../../src -Wall -O3 -fPIC -I../../src/USER-OMP -fopenmp +CXXFLAGS=-I../../src -Wall -Wextra -O3 -fPIC -I../../src/USER-OMP -fopenmp LD=$(CXX) -shared -rdynamic -fopenmp morse2plugin.so: morse2plugin.o pair_morse2.o pair_morse2_omp.o diff --git a/src/plugin.cpp b/src/plugin.cpp index 07e3c60dd0..f2499e6528 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -58,9 +58,8 @@ namespace LAMMPS_NS void *initfunc = dlsym(dso,"lammpsplugin_init"); if (initfunc == nullptr) { -#ifndef WIN32 dlclose(dso); -#endif + if (me == 0) utils::logmesg(lmp,fmt::format("Plugin symbol lookup failure in " "file {}\n",file)); @@ -182,7 +181,7 @@ namespace LAMMPS_NS if (idx < 0) { if (me == 0) utils::logmesg(lmp,fmt::format("Ignoring unload of {} style {}: not " - "loaded from a plugin", style, name)); + "loaded from a plugin\n", style, name)); return; } From 9209cbba92a9a271b8642a86b766b4e507f9d2ee Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 21:19:04 -0500 Subject: [PATCH 16/57] add support for loading plugins for fixes --- doc/src/Developer_plugins.rst | 41 ++++++- examples/plugins/Makefile | 8 ++ examples/plugins/fix_nve2.cpp | 171 ++++++++++++++++++++++++++++++ examples/plugins/fix_nve2.h | 58 ++++++++++ examples/plugins/morse2plugin.cpp | 5 +- examples/plugins/nve2plugin.cpp | 30 ++++++ src/lammpsplugin.h | 6 +- src/plugin.cpp | 14 ++- 8 files changed, 325 insertions(+), 8 deletions(-) create mode 100644 examples/plugins/fix_nve2.cpp create mode 100644 examples/plugins/fix_nve2.h create mode 100644 examples/plugins/nve2plugin.cpp diff --git a/doc/src/Developer_plugins.rst b/doc/src/Developer_plugins.rst index ffbcf8a667..59402165d1 100644 --- a/doc/src/Developer_plugins.rst +++ b/doc/src/Developer_plugins.rst @@ -57,17 +57,54 @@ function would look like this: plugin.name = "morse2"; plugin.info = "Morse2 variant pair style v1.0"; plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; - plugin.creator = (lammpsplugin_factory *) &morse2creator; + plugin.creator1 = (lammpsplugin_factory1 *) &morse2creator; + plugin.creator2 = nullptr; 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 argument and returns a +receives a pointer to the LAMMPS class as only argument and thus has to +be assigned to the *creator1* 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 *creator2* 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.creator1 = nullptr; + plugin.creator2 = (lammpsplugin_factory2 *) &nve2creator; + 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. diff --git a/examples/plugins/Makefile b/examples/plugins/Makefile index 5d60ae8651..c8ec554d5d 100644 --- a/examples/plugins/Makefile +++ b/examples/plugins/Makefile @@ -2,9 +2,14 @@ CXX=mpicxx CXXFLAGS=-I../../src -Wall -Wextra -O3 -fPIC -I../../src/USER-OMP -fopenmp LD=$(CXX) -shared -rdynamic -fopenmp +default: morse2plugin.so nve2plugin.so + morse2plugin.so: morse2plugin.o pair_morse2.o pair_morse2_omp.o $(LD) -o $@ $^ +nve2plugin.so: nve2plugin.o fix_nve2.o + $(LD) -o $@ $^ + .cpp.o: $(CXX) -o $@ $(CXXFLAGS) -c $< @@ -12,6 +17,9 @@ pair_morse2.o: pair_morse2.cpp pair_morse2.h pair_morse2_omp.o: pair_morse2_omp.cpp pair_morse2_omp.h pair_morse2.h morse2plugin.o: morse2plugin.cpp pair_morse2.h pair_morse2_omp.h +fix_nve2.o: fix_nve2.cpp fix_nve2.h +nve2plugin.o: nve2plugin.cpp fix_nve2.h + clean: rm -f *~ *.so *.o log.lammps diff --git a/examples/plugins/fix_nve2.cpp b/examples/plugins/fix_nve2.cpp new file mode 100644 index 0000000000..bb6f53b810 --- /dev/null +++ b/examples/plugins/fix_nve2.cpp @@ -0,0 +1,171 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://lammps.sandia.gov/, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "fix_nve2.h" +#include +#include "atom.h" +#include "force.h" +#include "update.h" +#include "respa.h" +#include "error.h" + +using namespace LAMMPS_NS; +using namespace FixConst; + +/* ---------------------------------------------------------------------- */ + +FixNVE2::FixNVE2(LAMMPS *lmp, int narg, char **arg) : + Fix(lmp, narg, arg) +{ + if (strcmp(style,"nve/sphere") != 0 && narg < 3) + error->all(FLERR,"Illegal fix nve command"); + + dynamic_group_allow = 1; + time_integrate = 1; +} + +/* ---------------------------------------------------------------------- */ + +int FixNVE2::setmask() +{ + int mask = 0; + mask |= INITIAL_INTEGRATE; + mask |= FINAL_INTEGRATE; + mask |= INITIAL_INTEGRATE_RESPA; + mask |= FINAL_INTEGRATE_RESPA; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixNVE2::init() +{ + dtv = update->dt; + dtf = 0.5 * update->dt * force->ftm2v; + + if (strstr(update->integrate_style,"respa")) + step_respa = ((Respa *) update->integrate)->step; +} + +/* ---------------------------------------------------------------------- + allow for both per-type and per-atom mass +------------------------------------------------------------------------- */ + +void FixNVE2::initial_integrate(int /*vflag*/) +{ + double dtfm; + + // update v and x of atoms in group + + double **x = atom->x; + double **v = atom->v; + double **f = atom->f; + double *rmass = atom->rmass; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + if (igroup == atom->firstgroup) nlocal = atom->nfirst; + + if (rmass) { + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + dtfm = dtf / rmass[i]; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + x[i][0] += dtv * v[i][0]; + x[i][1] += dtv * v[i][1]; + x[i][2] += dtv * v[i][2]; + } + + } else { + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]]; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + x[i][0] += dtv * v[i][0]; + x[i][1] += dtv * v[i][1]; + x[i][2] += dtv * v[i][2]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixNVE2::final_integrate() +{ + double dtfm; + + // update v of atoms in group + + double **v = atom->v; + double **f = atom->f; + double *rmass = atom->rmass; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + if (igroup == atom->firstgroup) nlocal = atom->nfirst; + + if (rmass) { + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + dtfm = dtf / rmass[i]; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + } + + } else { + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]]; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixNVE2::initial_integrate_respa(int vflag, int ilevel, int /*iloop*/) +{ + dtv = step_respa[ilevel]; + dtf = 0.5 * step_respa[ilevel] * force->ftm2v; + + // innermost level - NVE update of v and x + // all other levels - NVE update of v + + if (ilevel == 0) initial_integrate(vflag); + else final_integrate(); +} + +/* ---------------------------------------------------------------------- */ + +void FixNVE2::final_integrate_respa(int ilevel, int /*iloop*/) +{ + dtf = 0.5 * step_respa[ilevel] * force->ftm2v; + final_integrate(); +} + +/* ---------------------------------------------------------------------- */ + +void FixNVE2::reset_dt() +{ + dtv = update->dt; + dtf = 0.5 * update->dt * force->ftm2v; +} diff --git a/examples/plugins/fix_nve2.h b/examples/plugins/fix_nve2.h new file mode 100644 index 0000000000..f1b2244128 --- /dev/null +++ b/examples/plugins/fix_nve2.h @@ -0,0 +1,58 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifdef FIX_CLASS + +FixStyle(nve2,FixNVE2) + +#else + +#ifndef LMP_FIX_NVE2_H +#define LMP_FIX_NVE2_H + +#include "fix.h" + +namespace LAMMPS_NS { + +class FixNVE2 : public Fix { + public: + FixNVE2(class LAMMPS *, int, char **); + virtual ~FixNVE2() {} + int setmask(); + virtual void init(); + virtual void initial_integrate(int); + virtual void final_integrate(); + virtual void initial_integrate_respa(int, int, int); + virtual void final_integrate_respa(int, int); + virtual void reset_dt(); + + protected: + double dtv,dtf; + double *step_respa; + int mass_require; +}; + +} + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Illegal ... command + +Self-explanatory. Check the input script syntax and compare to the +documentation for the command. You can use -echo screen as a +command-line option when running LAMMPS to see the offending line. + +*/ diff --git a/examples/plugins/morse2plugin.cpp b/examples/plugins/morse2plugin.cpp index 286aa30c35..7151e35cd2 100644 --- a/examples/plugins/morse2plugin.cpp +++ b/examples/plugins/morse2plugin.cpp @@ -31,13 +31,14 @@ extern "C" void lammpsplugin_init(void *lmp, void *handle, void *regfunc) plugin.name = "morse2"; plugin.info = "Morse2 variant pair style v1.0"; plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; - plugin.creator = (lammpsplugin_factory *) &morse2creator; + plugin.creator1 = (lammpsplugin_factory1 *) &morse2creator; + plugin.creator2 = nullptr; plugin.handle = handle; (*register_plugin)(&plugin,lmp); // also register morse2/omp pair style. only need to update changed fields plugin.name = "morse2/omp"; plugin.info = "Morse2 variant pair style for OpenMP v1.0"; - plugin.creator = (lammpsplugin_factory *) &morse2ompcreator; + plugin.creator1 = (lammpsplugin_factory1 *) &morse2ompcreator; (*register_plugin)(&plugin,lmp); } diff --git a/examples/plugins/nve2plugin.cpp b/examples/plugins/nve2plugin.cpp new file mode 100644 index 0000000000..9c9ce3d8d2 --- /dev/null +++ b/examples/plugins/nve2plugin.cpp @@ -0,0 +1,30 @@ + +#include "lammpsplugin.h" + +#include "version.h" + +#include + +#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_t plugin; + lammpsplugin_regfunc register_plugin = (lammpsplugin_regfunc) regfunc; + + 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.creator2 = (lammpsplugin_factory2 *) &nve2creator; + plugin.handle = handle; + (*register_plugin)(&plugin,lmp); +} diff --git a/src/lammpsplugin.h b/src/lammpsplugin.h index 34a0b60811..958235fdaa 100644 --- a/src/lammpsplugin.h +++ b/src/lammpsplugin.h @@ -18,7 +18,8 @@ extern "C" { - typedef void *(lammpsplugin_factory)(void *); + typedef void *(lammpsplugin_factory1)(void *); + typedef void *(lammpsplugin_factory2)(void *, int, char **); typedef struct { const char *version; @@ -26,7 +27,8 @@ extern "C" { const char *name; const char *info; const char *author; - lammpsplugin_factory *creator; + lammpsplugin_factory1 *creator1; + lammpsplugin_factory2 *creator2; void *handle; } lammpsplugin_t; diff --git a/src/plugin.cpp b/src/plugin.cpp index f2499e6528..7a42734bd9 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -114,10 +114,20 @@ namespace LAMMPS_NS plugin->name)); } - (*pair_map)[plugin->name] = (Force::PairCreator)plugin->creator; + (*pair_map)[plugin->name] = (Force::PairCreator)plugin->creator1; + } else if (pstyle == "fix") { + auto fix_map = lmp->modify->fix_map; + if (fix_map->find(plugin->name) != fix_map->end()) { + if (lmp->comm->me == 0) + lmp->error->warning(FLERR,fmt::format("Overriding built-in fix " + "style {} from plugin", + plugin->name)); + } + + (*fix_map)[plugin->name] = (Modify::FixCreator)plugin->creator2; } else { utils::logmesg(lmp,fmt::format("Loading plugin for {} styles not " - "yet implemented\n", pstyle)); + "yet implemented\n",pstyle)); pluginlist.pop_back(); } } From 3d1c6b30af02a2e375be66de98180d6609bf6fab Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 21:19:29 -0500 Subject: [PATCH 17/57] refuse to load a plugin over an existing plugin for the same style --- src/plugin.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/plugin.cpp b/src/plugin.cpp index 7a42734bd9..d2ea0943ad 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -86,14 +86,25 @@ namespace LAMMPS_NS int me = lmp->comm->me; if (plugin == nullptr) return; + + // ignore load request if same plugin already loaded + int idx = plugin_find(plugin->style,plugin->name); + if (idx >= 0) { + if (me == 0) + utils::logmesg(lmp,fmt::format("Ignoring load of {} style {}: must " + "unload existing {} plugin first\n", + plugin->style,plugin->name,plugin->name)); + return; + } + if (me == 0) { utils::logmesg(lmp,fmt::format("Loading plugin: {} by {}\n", - plugin->info, plugin->author)); + plugin->info,plugin->author)); // print version info only if the versions of host and plugin don't match if ((plugin->version) && (strcmp(plugin->version,lmp->version) != 0)) utils::logmesg(lmp,fmt::format(" compiled for LAMMPS version {} " "loaded into LAMMPS version {}\n", - plugin->version, lmp->version)); + plugin->version,lmp->version)); } pluginlist.push_back(*plugin); @@ -191,7 +202,7 @@ namespace LAMMPS_NS if (idx < 0) { if (me == 0) utils::logmesg(lmp,fmt::format("Ignoring unload of {} style {}: not " - "loaded from a plugin\n", style, name)); + "loaded from a plugin\n",style,name)); return; } From 620dd09509b9ec7cbecbe1c0acc971e242617f19 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 21:36:14 -0500 Subject: [PATCH 18/57] delete active fixes when unloading fix style plugin --- src/plugin.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugin.cpp b/src/plugin.cpp index d2ea0943ad..0eece6793e 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -236,6 +236,10 @@ namespace LAMMPS_NS lmp->force->create_pair("none",0); } } + } else if (pstyle == "fix") { + for (int ifix = lmp->modify->find_fix_by_style(name); + ifix >= 0; ifix = lmp->modify->find_fix_by_style(name)) + lmp->modify->delete_fix(ifix); } // if reference count is down to zero, close DSO handle. From 347db1458daa89f5ec9ef743d32c625f2b3aaf1e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 22:34:23 -0500 Subject: [PATCH 19/57] always link with libdl.so/.a or equivalent except on windows --- cmake/CMakeLists.txt | 5 +++++ cmake/Modules/Packages/USER-MOLFILE.cmake | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 06600d02c4..7081d6aa80 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -240,6 +240,11 @@ if(BUILD_OMP) target_link_libraries(lammps PRIVATE OpenMP::OpenMP_CXX) endif() +# link with -ldl or equivalent for plugin loading; except on Windows +if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + target_link_libraries(lammps PRIVATE ${CMAKE_DL_LIBS}) +endif() + # Compiler specific features for testing if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") option(ENABLE_COVERAGE "Enable collecting code coverage data" OFF) diff --git a/cmake/Modules/Packages/USER-MOLFILE.cmake b/cmake/Modules/Packages/USER-MOLFILE.cmake index 4d414acead..427f0ed6fa 100644 --- a/cmake/Modules/Packages/USER-MOLFILE.cmake +++ b/cmake/Modules/Packages/USER-MOLFILE.cmake @@ -2,8 +2,4 @@ set(MOLFILE_INCLUDE_DIR "${LAMMPS_LIB_SOURCE_DIR}/molfile" CACHE STRING "Path to set(MOLFILE_INCLUDE_DIRS "${MOLFILE_INCLUDE_DIR}") add_library(molfile INTERFACE) target_include_directories(molfile INTERFACE ${MOLFILE_INCLUDE_DIRS}) -# no need to link with -ldl on windows -if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") - target_link_libraries(molfile INTERFACE ${CMAKE_DL_LIBS}) -endif() target_link_libraries(lammps PRIVATE molfile) From 256c478a6bae76c044efb7c262e858b547f3c286 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 22:34:47 -0500 Subject: [PATCH 20/57] reorder functions and make header and implemention order consistent --- src/plugin.cpp | 132 +++++++++++++++++++++++++++---------------------- src/plugin.h | 11 +++-- 2 files changed, 80 insertions(+), 63 deletions(-) diff --git a/src/plugin.cpp b/src/plugin.cpp index 0eece6793e..85870952e9 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -31,9 +31,10 @@ namespace LAMMPS_NS { - // store list of plugin information data for loaded styles + // list of plugin information data for loaded styles static std::list pluginlist; - // map of dso handles + + // map for counting references to dso handles static std::map dso_refcounter; // load DSO and call included registration function @@ -44,7 +45,7 @@ namespace LAMMPS_NS lmp->error->all(FLERR,"Loading of plugins on Windows not yet supported\n"); #else - // open DSO file from given path load symbols globally + // open DSO file from given path; load symbols globally void *dso = dlopen(file,RTLD_NOW|RTLD_GLOBAL); if (dso == nullptr) { @@ -54,7 +55,8 @@ namespace LAMMPS_NS return; } - // look up lammpsplugin_init() function in DSO. must have C bindings. + // look up lammpsplugin_init() function in DSO + // function must have C bindings so there is no name mangling void *initfunc = dlsym(dso,"lammpsplugin_init"); if (initfunc == nullptr) { @@ -70,15 +72,17 @@ namespace LAMMPS_NS // to the LAMMPS instance, the DSO handle (for reference counting) // and plugin registration function pointer - ((lammpsplugin_initfunc)(initfunc))((void *)lmp, dso, - (void *)&plugin_register); + (*(lammpsplugin_initfunc)(initfunc))((void *)lmp, dso, + (void *)&plugin_register); #endif } - // register a new style from a plugin with LAMMPS - // this is the callback function that is called from within - // the plugin initializer function. all plugin information - // is taken from the lammpsplugin_t struct. + /* -------------------------------------------------------------------- + register a new style from a plugin with LAMMPS + this is the callback function that is called from within + the plugin initializer function. all plugin information + is taken from the lammpsplugin_t struct. + -------------------------------------------------------------------- */ void plugin_register(lammpsplugin_t *plugin, void *ptr) { @@ -143,55 +147,11 @@ namespace LAMMPS_NS } } - // number of styles loaded from plugin files - - int plugin_get_num_plugins() - { - return pluginlist.size(); - } - - // return position index in list of given plugin of given style - - int plugin_find(const char *style, const char *name) - { - int i=0; - for (auto entry : pluginlist) { - if ((strcmp(style,entry.style) == 0) - && (strcmp(name,entry.name) == 0)) - return i; - ++i; - } - return -1; - } - - // get pointer to plugin initializer struct at position idx - - const lammpsplugin_t *plugin_get_info(int idx) - { - int i=0; - for (auto p=pluginlist.begin(); p != pluginlist.end(); ++p) { - if (i == idx) return &(*p); - ++i; - } - return nullptr; - } - - // remove plugin of given name and style from internal lists - - void plugin_erase(const char *style, const char *name) - { - for (auto p=pluginlist.begin(); p != pluginlist.end(); ++p) { - if ((strcmp(style,p->style) == 0) - && (strcmp(name,p->name) == 0)) { - pluginlist.erase(p); - return; - } - } - } - - // remove plugin from given style table and plugin list. - // optionally close the DSO handle if last plugin from that DSO - // must delete style instance if style is currently active. + /* -------------------------------------------------------------------- + remove plugin from given style table and plugin list + optionally close the DSO handle if it is the last plugin from that DSO + must also delete style instances if style is currently active + -------------------------------------------------------------------- */ void plugin_unload(const char *style, const char *name, LAMMPS *lmp) { @@ -251,4 +211,58 @@ namespace LAMMPS_NS #endif } } + + /* -------------------------------------------------------------------- + remove plugin of given name and style from internal lists + -------------------------------------------------------------------- */ + + void plugin_erase(const char *style, const char *name) + { + for (auto p=pluginlist.begin(); p != pluginlist.end(); ++p) { + if ((strcmp(style,p->style) == 0) + && (strcmp(name,p->name) == 0)) { + pluginlist.erase(p); + return; + } + } + } + + /* -------------------------------------------------------------------- + number of styles loaded from plugin files + -------------------------------------------------------------------- */ + + int plugin_get_num_plugins() + { + return pluginlist.size(); + } + + /* -------------------------------------------------------------------- + return position index in list of given plugin of given style + -------------------------------------------------------------------- */ + + int plugin_find(const char *style, const char *name) + { + int i=0; + for (auto entry : pluginlist) { + if ((strcmp(style,entry.style) == 0) + && (strcmp(name,entry.name) == 0)) + return i; + ++i; + } + return -1; + } + + /* -------------------------------------------------------------------- + get pointer to plugin initializer struct at position idx + -------------------------------------------------------------------- */ + + const lammpsplugin_t *plugin_get_info(int idx) + { + int i=0; + for (auto p=pluginlist.begin(); p != pluginlist.end(); ++p) { + if (i == idx) return &(*p); + ++i; + } + return nullptr; + } } diff --git a/src/plugin.h b/src/plugin.h index 172b2f0c37..32a5a280a1 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -19,13 +19,16 @@ namespace LAMMPS_NS { class LAMMPS; + void plugin_load(const char *, LAMMPS *); void plugin_register(lammpsplugin_t *, void *); - int plugin_get_num_plugins(); - const lammpsplugin_t *plugin_get_info(int); - int plugin_find(const char *, const char *); - void plugin_erase(const char *, const char *); + void plugin_unload(const char *, const char *, LAMMPS *); + void plugin_erase(const char *, const char *); + + int plugin_get_num_plugins(); + int plugin_find(const char *, const char *); + const lammpsplugin_t *plugin_get_info(int); } #endif From 83583c465eb6f2b88d7eb12befb8b65e5065e508 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 22:58:22 -0500 Subject: [PATCH 21/57] add support for command plugins with example --- examples/plugins/Makefile | 8 +++-- examples/plugins/helloplugin.cpp | 49 +++++++++++++++++++++++++++++++ examples/plugins/morse2plugin.cpp | 1 + examples/plugins/nve2plugin.cpp | 2 ++ src/lammpsplugin.h | 2 ++ src/plugin.cpp | 15 ++++++++-- 6 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 examples/plugins/helloplugin.cpp diff --git a/examples/plugins/Makefile b/examples/plugins/Makefile index c8ec554d5d..bf6bc804be 100644 --- a/examples/plugins/Makefile +++ b/examples/plugins/Makefile @@ -2,7 +2,10 @@ CXX=mpicxx CXXFLAGS=-I../../src -Wall -Wextra -O3 -fPIC -I../../src/USER-OMP -fopenmp LD=$(CXX) -shared -rdynamic -fopenmp -default: morse2plugin.so nve2plugin.so +default: morse2plugin.so nve2plugin.so helloplugin.so + +helloplugin.so: helloplugin.o + $(LD) -o $@ $^ morse2plugin.so: morse2plugin.o pair_morse2.o pair_morse2_omp.o $(LD) -o $@ $^ @@ -13,6 +16,8 @@ nve2plugin.so: nve2plugin.o fix_nve2.o .cpp.o: $(CXX) -o $@ $(CXXFLAGS) -c $< +helloplugin.o: helloplugin.cpp + pair_morse2.o: pair_morse2.cpp pair_morse2.h pair_morse2_omp.o: pair_morse2_omp.cpp pair_morse2_omp.h pair_morse2.h morse2plugin.o: morse2plugin.cpp pair_morse2.h pair_morse2_omp.h @@ -23,4 +28,3 @@ nve2plugin.o: nve2plugin.cpp fix_nve2.h clean: rm -f *~ *.so *.o log.lammps - diff --git a/examples/plugins/helloplugin.cpp b/examples/plugins/helloplugin.cpp new file mode 100644 index 0000000000..1c9d0c2779 --- /dev/null +++ b/examples/plugins/helloplugin.cpp @@ -0,0 +1,49 @@ + +#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.creator1 = nullptr; + plugin.creator2 = nullptr; + plugin.creator3 = (lammpsplugin_factory3 *) &hellocreator; + plugin.handle = handle; + (*register_plugin)(&plugin,lmp); +} diff --git a/examples/plugins/morse2plugin.cpp b/examples/plugins/morse2plugin.cpp index 7151e35cd2..dfc341e0c0 100644 --- a/examples/plugins/morse2plugin.cpp +++ b/examples/plugins/morse2plugin.cpp @@ -33,6 +33,7 @@ extern "C" void lammpsplugin_init(void *lmp, void *handle, void *regfunc) plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; plugin.creator1 = (lammpsplugin_factory1 *) &morse2creator; plugin.creator2 = nullptr; + plugin.creator3 = nullptr; plugin.handle = handle; (*register_plugin)(&plugin,lmp); diff --git a/examples/plugins/nve2plugin.cpp b/examples/plugins/nve2plugin.cpp index 9c9ce3d8d2..785ec8dbe1 100644 --- a/examples/plugins/nve2plugin.cpp +++ b/examples/plugins/nve2plugin.cpp @@ -24,7 +24,9 @@ extern "C" void lammpsplugin_init(void *lmp, void *handle, void *regfunc) plugin.name = "nve2"; plugin.info = "NVE2 variant fix style v1.0"; plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; + plugin.creator1 = nullptr; plugin.creator2 = (lammpsplugin_factory2 *) &nve2creator; + plugin.creator3 = nullptr; plugin.handle = handle; (*register_plugin)(&plugin,lmp); } diff --git a/src/lammpsplugin.h b/src/lammpsplugin.h index 958235fdaa..3de2bcdcdf 100644 --- a/src/lammpsplugin.h +++ b/src/lammpsplugin.h @@ -20,6 +20,7 @@ extern "C" { typedef void *(lammpsplugin_factory1)(void *); typedef void *(lammpsplugin_factory2)(void *, int, char **); + typedef void (lammpsplugin_factory3)(void *, int, char **); typedef struct { const char *version; @@ -29,6 +30,7 @@ extern "C" { const char *author; lammpsplugin_factory1 *creator1; lammpsplugin_factory2 *creator2; + lammpsplugin_factory3 *creator3; void *handle; } lammpsplugin_t; diff --git a/src/plugin.cpp b/src/plugin.cpp index 85870952e9..ea1c46b705 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -15,6 +15,7 @@ #include "comm.h" #include "error.h" +#include "input.h" #include "force.h" #include "lammps.h" #include "modify.h" @@ -128,8 +129,8 @@ namespace LAMMPS_NS "style {} from plugin", plugin->name)); } - (*pair_map)[plugin->name] = (Force::PairCreator)plugin->creator1; + } else if (pstyle == "fix") { auto fix_map = lmp->modify->fix_map; if (fix_map->find(plugin->name) != fix_map->end()) { @@ -138,8 +139,18 @@ namespace LAMMPS_NS "style {} from plugin", plugin->name)); } - (*fix_map)[plugin->name] = (Modify::FixCreator)plugin->creator2; + + } else if (pstyle == "command") { + auto command_map = lmp->input->command_map; + if (command_map->find(plugin->name) != command_map->end()) { + if (lmp->comm->me == 0) + lmp->error->warning(FLERR,fmt::format("Overriding built-in fix " + "style {} from plugin", + plugin->name)); + } + (*command_map)[plugin->name] = (Input::CommandCreator)plugin->creator3; + } else { utils::logmesg(lmp,fmt::format("Loading plugin for {} styles not " "yet implemented\n",pstyle)); From dde00ab344340ffbf29fb5eb34462f1a8439319e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 23:10:26 -0500 Subject: [PATCH 22/57] add plugin command documentation --- doc/src/plugin.rst | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 doc/src/plugin.rst diff --git a/doc/src/plugin.rst b/doc/src/plugin.rst new file mode 100644 index 0000000000..42d53aabe3 --- /dev/null +++ b/doc/src/plugin.rst @@ -0,0 +1,76 @@ +.. index:: plugin + +plugin command +============== + +Syntax +"""""" + +.. parsed-literal:: + + plugin command args + +* command = *load* or *unload* or *list* +* args = list of arguments for a particular plugin command + + .. parsed-literal:: + + *load* file = load plugin(s) from shared object in *file* + *unload* style name = unload plugin *name* of style *style* + *list* = print a list of currently loaded plugins + +Examples +"""""""" + +.. code-block:: LAMMPS + + plugin load morse2plugin.so + plugin unload pair morse2/omp + plugin list + +Description +""""""""""" + +The plugin command allows to load (and unload) additional styles and +commands into a LAMMPS binary from so-called dynamic shared object (DSO) +files. This enables to add new functionality to an existing LAMMPS +binary without having to recompile and link the entire executable. + +The *load* command will load and initialize all plugins contained in the +plugin DSO with the given filename. A message with information the +plugin style and name and more will be printed. Individual DSO files +may contain multiple plugins. More details about how to write and +compile the plugin DSO is given in programmer's guide part of the manual +under :doc:`Developer_plugins`. + +The *unload* command will remove the given style or the given name from +the list of available styles. If the plugin style is currently in use, +that style instance will be deleted. + +The *list* command will print a list of the loaded plugins and their +styles and names. + + +Restrictions +"""""""""""" + +Plugins are currently not available on Windows. + +Plugins are dependent on the LAMMPS binary interface (ABI) +and particularly the MPI library used. So they are not guaranteed +to work when the plugin was compiled with a different MPI library +or different compilation settings or a different LAMMPS version. +If there is a mismatch the *plugin* command may fail to load the +plugin(s) or data corruption or crashes may happen. + + +Related commands +"""""""""""""""" + +none + + +Default +""""""" + +none From 524c62994eba5be3c1786ffaab32fc8d3edf9226 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 23:12:00 -0500 Subject: [PATCH 23/57] update docs --- doc/src/plugin.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/src/plugin.rst b/doc/src/plugin.rst index 42d53aabe3..25a55565b9 100644 --- a/doc/src/plugin.rst +++ b/doc/src/plugin.rst @@ -17,6 +17,7 @@ Syntax *load* file = load plugin(s) from shared object in *file* *unload* style name = unload plugin *name* of style *style* + *style* = *pair* or *fix* or *command* *list* = print a list of currently loaded plugins Examples @@ -26,6 +27,7 @@ Examples plugin load morse2plugin.so plugin unload pair morse2/omp + plugin unload command hello plugin list Description From d95d5f19547a88dc595922f5254c4b308ec643ac Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 23:52:35 -0500 Subject: [PATCH 24/57] store different factory variants in a union --- examples/plugins/helloplugin.cpp | 4 +--- examples/plugins/morse2plugin.cpp | 6 ++---- examples/plugins/nve2plugin.cpp | 4 +--- src/lammpsplugin.h | 8 +++++--- src/plugin.cpp | 6 +++--- 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/examples/plugins/helloplugin.cpp b/examples/plugins/helloplugin.cpp index 1c9d0c2779..11f2cfb891 100644 --- a/examples/plugins/helloplugin.cpp +++ b/examples/plugins/helloplugin.cpp @@ -41,9 +41,7 @@ extern "C" void lammpsplugin_init(void *lmp, void *handle, void *regfunc) plugin.name = "hello"; plugin.info = "Hello world command v1.0"; plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; - plugin.creator1 = nullptr; - plugin.creator2 = nullptr; - plugin.creator3 = (lammpsplugin_factory3 *) &hellocreator; + plugin.creator.v3 = (lammpsplugin_factory3 *) &hellocreator; plugin.handle = handle; (*register_plugin)(&plugin,lmp); } diff --git a/examples/plugins/morse2plugin.cpp b/examples/plugins/morse2plugin.cpp index dfc341e0c0..a4151dcc3d 100644 --- a/examples/plugins/morse2plugin.cpp +++ b/examples/plugins/morse2plugin.cpp @@ -31,15 +31,13 @@ extern "C" void lammpsplugin_init(void *lmp, void *handle, void *regfunc) plugin.name = "morse2"; plugin.info = "Morse2 variant pair style v1.0"; plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; - plugin.creator1 = (lammpsplugin_factory1 *) &morse2creator; - plugin.creator2 = nullptr; - plugin.creator3 = nullptr; + plugin.creator.v1 = (lammpsplugin_factory1 *) &morse2creator; plugin.handle = handle; (*register_plugin)(&plugin,lmp); // also register morse2/omp pair style. only need to update changed fields plugin.name = "morse2/omp"; plugin.info = "Morse2 variant pair style for OpenMP v1.0"; - plugin.creator1 = (lammpsplugin_factory1 *) &morse2ompcreator; + plugin.creator.v1 = (lammpsplugin_factory1 *) &morse2ompcreator; (*register_plugin)(&plugin,lmp); } diff --git a/examples/plugins/nve2plugin.cpp b/examples/plugins/nve2plugin.cpp index 785ec8dbe1..d17db1dfc7 100644 --- a/examples/plugins/nve2plugin.cpp +++ b/examples/plugins/nve2plugin.cpp @@ -24,9 +24,7 @@ extern "C" void lammpsplugin_init(void *lmp, void *handle, void *regfunc) plugin.name = "nve2"; plugin.info = "NVE2 variant fix style v1.0"; plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; - plugin.creator1 = nullptr; - plugin.creator2 = (lammpsplugin_factory2 *) &nve2creator; - plugin.creator3 = nullptr; + plugin.creator.v2 = (lammpsplugin_factory2 *) &nve2creator; plugin.handle = handle; (*register_plugin)(&plugin,lmp); } diff --git a/src/lammpsplugin.h b/src/lammpsplugin.h index 3de2bcdcdf..1baed9799d 100644 --- a/src/lammpsplugin.h +++ b/src/lammpsplugin.h @@ -28,9 +28,11 @@ extern "C" { const char *name; const char *info; const char *author; - lammpsplugin_factory1 *creator1; - lammpsplugin_factory2 *creator2; - lammpsplugin_factory3 *creator3; + union { + lammpsplugin_factory1 *v1; + lammpsplugin_factory2 *v2; + lammpsplugin_factory3 *v3; + } creator; void *handle; } lammpsplugin_t; diff --git a/src/plugin.cpp b/src/plugin.cpp index ea1c46b705..a95fde5b73 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -129,7 +129,7 @@ namespace LAMMPS_NS "style {} from plugin", plugin->name)); } - (*pair_map)[plugin->name] = (Force::PairCreator)plugin->creator1; + (*pair_map)[plugin->name] = (Force::PairCreator)plugin->creator.v1; } else if (pstyle == "fix") { auto fix_map = lmp->modify->fix_map; @@ -139,7 +139,7 @@ namespace LAMMPS_NS "style {} from plugin", plugin->name)); } - (*fix_map)[plugin->name] = (Modify::FixCreator)plugin->creator2; + (*fix_map)[plugin->name] = (Modify::FixCreator)plugin->creator.v2; } else if (pstyle == "command") { auto command_map = lmp->input->command_map; @@ -149,7 +149,7 @@ namespace LAMMPS_NS "style {} from plugin", plugin->name)); } - (*command_map)[plugin->name] = (Input::CommandCreator)plugin->creator3; + (*command_map)[plugin->name] = (Input::CommandCreator)plugin->creator.v3; } else { utils::logmesg(lmp,fmt::format("Loading plugin for {} styles not " From 930c0fca30fc24a3180d0ded815b8716063e5af4 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Mar 2021 23:59:23 -0500 Subject: [PATCH 25/57] must link with -ldl --- src/MAKE/Makefile.mpi | 2 +- src/MAKE/Makefile.serial | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MAKE/Makefile.mpi b/src/MAKE/Makefile.mpi index db134de68a..c66e66c217 100644 --- a/src/MAKE/Makefile.mpi +++ b/src/MAKE/Makefile.mpi @@ -13,7 +13,7 @@ DEPFLAGS = -M LINK = mpicxx LINKFLAGS = -g -O3 -LIB = +LIB = -ldl SIZE = size ARCHIVE = ar diff --git a/src/MAKE/Makefile.serial b/src/MAKE/Makefile.serial index a0b2959c4b..daf04cc5b0 100644 --- a/src/MAKE/Makefile.serial +++ b/src/MAKE/Makefile.serial @@ -13,7 +13,7 @@ DEPFLAGS = -M LINK = g++ LINKFLAGS = -g -O -LIB = +LIB = -ldl SIZE = size ARCHIVE = ar From a2bcd7fe68f0501733da98c9f941bae9f514b4f5 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 12 Mar 2021 00:07:52 -0500 Subject: [PATCH 26/57] programmer documentation update --- doc/src/Developer_plugins.rst | 64 +++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/doc/src/Developer_plugins.rst b/doc/src/Developer_plugins.rst index 59402165d1..192c57ccbb 100644 --- a/doc/src/Developer_plugins.rst +++ b/doc/src/Developer_plugins.rst @@ -57,15 +57,14 @@ function would look like this: plugin.name = "morse2"; plugin.info = "Morse2 variant pair style v1.0"; plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; - plugin.creator1 = (lammpsplugin_factory1 *) &morse2creator; - plugin.creator2 = nullptr; + 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 *creator1* member of the plugin struct and cast to the +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. @@ -74,7 +73,7 @@ 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 *creator2* member of the plugin struct. +and it must be assigned to the *creator.v2* member of the plugin struct. Below is an example for that: .. code-block:: C++ @@ -100,12 +99,65 @@ Below is an example for that: plugin.name = "nve2"; plugin.info = "NVE2 variant fix style v1.0"; plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; - plugin.creator1 = nullptr; - plugin.creator2 = (lammpsplugin_factory2 *) &nve2creator; + 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 From c3f6fb914ff9ff287482f805e97663c6a356e2df Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 12 Mar 2021 11:43:37 -0500 Subject: [PATCH 27/57] add CMake build environment demo for plugins --- examples/plugins/CMakeLists.txt | 56 +++++++++++++++ examples/plugins/LAMMPSInterfaceCXX.cmake | 86 +++++++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 examples/plugins/CMakeLists.txt create mode 100644 examples/plugins/LAMMPSInterfaceCXX.cmake diff --git a/examples/plugins/CMakeLists.txt b/examples/plugins/CMakeLists.txt new file mode 100644 index 0000000000..2fb8ca746f --- /dev/null +++ b/examples/plugins/CMakeLists.txt @@ -0,0 +1,56 @@ +########################################## +# CMake build system for plugin examples. +# The is meant to be used as a template for plugins that are +# distributed independent from the LAMMPS package. +########################################## + +cmake_minimum_required(VERSION 3.10) + +# enforce out-of-source build +if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) + message(FATAL_ERROR "In-source builds are not allowed. You must create and use a build directory. " + "Please remove CMakeCache.txt and CMakeFiles first.") +endif() + +project(plugins VERSION 1.0 LANGUAGES CXX) + +# NOTE: the next line should be commented out when used outside of the LAMMPS package +get_filename_component(LAMMPS_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../../src ABSOLUTE) +set(LAMMPS_HEADER_DIR ${LAMMPS_SOURCE_DIR} CACHE PATH "Location of LAMMPS headers") +if(NOT LAMMPS_HEADER_DIR) + message(FATAL_ERROR "Must set LAMMPS_HEADER_DIR") +endif() + +# by default, install into $HOME/.local (not /usr/local), +# so that no root access (and sudo) is needed +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "$ENV{HOME}/.local" CACHE PATH "Default install path" FORCE) +endif() + +# C++11 is required +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# bail out on windows +if(CMAKE_SYSTEM_NAME STREQUAL Windows) + message(FATAL_ERROR "LAMMPS plugins are currently not supported on Windows") +endif() + +set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}) +include(CheckIncludeFileCXX) +include(LAMMPSInterfaceCXX) + +########################## +# building the plugins + +add_library(morse2plugin MODULE morse2plugin.cpp pair_morse2.cpp pair_morse2_omp.cpp) +target_include_directories(morse2plugin PRIVATE "${LAMMPS_HEADER_DIR}/USER-OMP") +target_link_libraries(morse2plugin PRIVATE lammps) + +add_library(nve2plugin MODULE nve2plugin.cpp fix_nve2.cpp) +target_link_libraries(nve2plugin PRIVATE lammps) + +add_library(helloplugin MODULE helloplugin.cpp) +target_link_libraries(helloplugin PRIVATE lammps) + +set_target_properties(morse2plugin nve2plugin helloplugin PROPERTIES PREFIX "") diff --git a/examples/plugins/LAMMPSInterfaceCXX.cmake b/examples/plugins/LAMMPSInterfaceCXX.cmake new file mode 100644 index 0000000000..02f9159319 --- /dev/null +++ b/examples/plugins/LAMMPSInterfaceCXX.cmake @@ -0,0 +1,86 @@ +# Cmake script code to define the LAMMPS C++ interface +# settings required for building LAMMPS plugins + +################################################################################ +# helper function +function(validate_option name values) + string(TOLOWER ${${name}} needle_lower) + string(TOUPPER ${${name}} needle_upper) + list(FIND ${values} ${needle_lower} IDX_LOWER) + list(FIND ${values} ${needle_upper} IDX_UPPER) + if(${IDX_LOWER} LESS 0 AND ${IDX_UPPER} LESS 0) + list_to_bulletpoints(POSSIBLE_VALUE_LIST ${${values}}) + message(FATAL_ERROR "\n########################################################################\n" + "Invalid value '${${name}}' for option ${name}\n" + "\n" + "Possible values are:\n" + "${POSSIBLE_VALUE_LIST}" + "########################################################################") + endif() +endfunction(validate_option) + +################################################################################# +# LAMMPS C++ interface. We only need the header related parts. +add_library(lammps INTERFACE) +target_include_directories(lammps INTERFACE ${LAMMPS_HEADER_DIR}) + +################################################################################ +# MPI configuration +if(NOT CMAKE_CROSSCOMPILING) + set(MPI_CXX_SKIP_MPICXX TRUE) + find_package(MPI QUIET) + option(BUILD_MPI "Build MPI version" ${MPI_FOUND}) +else() + option(BUILD_MPI "Build MPI version" OFF) +endif() + +if(BUILD_MPI) + find_package(MPI REQUIRED) + option(LAMMPS_LONGLONG_TO_LONG "Workaround if your system or MPI version does not recognize 'long long' data types" OFF) + if(LAMMPS_LONGLONG_TO_LONG) + target_compile_definitions(lammps INTERFACE -DLAMMPS_LONGLONG_TO_LONG) + endif() + target_link_libraries(lammps INTERFACE MPI::MPI_CXX) +else() + target_include_directories(lammps INTERFACE "${LAMMPS_SOURCE_DIR}/STUBS") +endif() + +set(LAMMPS_SIZES "smallbig" CACHE STRING "LAMMPS integer sizes (smallsmall: all 32-bit, smallbig: 64-bit #atoms #timesteps, bigbig: also 64-bit imageint, 64-bit atom ids)") +set(LAMMPS_SIZES_VALUES smallbig bigbig smallsmall) +set_property(CACHE LAMMPS_SIZES PROPERTY STRINGS ${LAMMPS_SIZES_VALUES}) +validate_option(LAMMPS_SIZES LAMMPS_SIZES_VALUES) +string(TOUPPER ${LAMMPS_SIZES} LAMMPS_SIZES) +target_compile_definitions(lammps INTERFACE -DLAMMPS_${LAMMPS_SIZES}) + +################################################################################ +# detect if we may enable OpenMP support by default +set(BUILD_OMP_DEFAULT OFF) +find_package(OpenMP QUIET) +if(OpenMP_FOUND) + check_include_file_cxx(omp.h HAVE_OMP_H_INCLUDE) + if(HAVE_OMP_H_INCLUDE) + set(BUILD_OMP_DEFAULT ON) + endif() +endif() + +option(BUILD_OMP "Build with OpenMP support" ${BUILD_OMP_DEFAULT}) + +if(BUILD_OMP) + find_package(OpenMP REQUIRED) + check_include_file_cxx(omp.h HAVE_OMP_H_INCLUDE) + if(NOT HAVE_OMP_H_INCLUDE) + message(FATAL_ERROR "Cannot find the 'omp.h' header file required for full OpenMP support") + endif() + + if (((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 9.0)) OR + (CMAKE_CXX_COMPILER_ID STREQUAL "PGI") OR + ((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0)) OR + ((CMAKE_CXX_COMPILER_ID STREQUAL "Intel") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.0))) + # GCC 9.x and later plus Clang 10.x and later implement strict OpenMP 4.0 semantics for consts. + # Intel 18.0 was tested to support both, so we switch to OpenMP 4+ from 19.x onward to be safe. + target_compile_definitions(lammps INTERFACE -DLAMMPS_OMP_COMPAT=4) + else() + target_compile_definitions(lammps INTERFACE -DLAMMPS_OMP_COMPAT=3) + endif() + target_link_libraries(lammps INTERFACE OpenMP::OpenMP_CXX) +endif() From 7b4e14317685278c5e62217aaffddd5bdb2773cb Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 12 Mar 2021 14:29:40 -0500 Subject: [PATCH 28/57] support building plugins on MacOS (tested on version 11.0 aka Big Sur) --- examples/plugins/CMakeLists.txt | 10 +++++++++- examples/plugins/Makefile | 2 +- examples/plugins/Makefile.macos | 30 ++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 examples/plugins/Makefile.macos diff --git a/examples/plugins/CMakeLists.txt b/examples/plugins/CMakeLists.txt index 2fb8ca746f..74a1cd4e3b 100644 --- a/examples/plugins/CMakeLists.txt +++ b/examples/plugins/CMakeLists.txt @@ -53,4 +53,12 @@ target_link_libraries(nve2plugin PRIVATE lammps) add_library(helloplugin MODULE helloplugin.cpp) target_link_libraries(helloplugin PRIVATE lammps) -set_target_properties(morse2plugin nve2plugin helloplugin PROPERTIES PREFIX "") +set_target_properties(morse2plugin nve2plugin helloplugin PROPERTIES + PREFIX "" + LINK_FLAGS "-rdynamic") + +# MacOS seems to need this +if(CMAKE_SYSTEM_NAME STREQUAL Darwin) + set_target_properties(morse2plugin nve2plugin helloplugin PROPERTIES + LINK_FLAGS "-Wl,-undefined,dynamic_lookup") +endif() diff --git a/examples/plugins/Makefile b/examples/plugins/Makefile index bf6bc804be..dbb0023991 100644 --- a/examples/plugins/Makefile +++ b/examples/plugins/Makefile @@ -26,5 +26,5 @@ fix_nve2.o: fix_nve2.cpp fix_nve2.h nve2plugin.o: nve2plugin.cpp fix_nve2.h clean: - rm -f *~ *.so *.o log.lammps + rm -rf *~ *.so *.dylib *.o log.lammps CMakeCache.txt CMakeFiles diff --git a/examples/plugins/Makefile.macos b/examples/plugins/Makefile.macos new file mode 100644 index 0000000000..2490418a09 --- /dev/null +++ b/examples/plugins/Makefile.macos @@ -0,0 +1,30 @@ +CXX=mpicxx +CXXFLAGS=-I../../src -Wall -Wextra -O3 -fPIC -I../../src/USER-OMP +LD=$(CXX) -bundle -rdynamic -Wl,-undefined,dynamic_lookup + +default: morse2plugin.dylib nve2plugin.dylib helloplugin.dylib + +helloplugin.dylib: helloplugin.o + $(LD) -o $@ $^ + +morse2plugin.dylib: morse2plugin.o pair_morse2.o pair_morse2_omp.o + $(LD) -o $@ $^ + +nve2plugin.dylib: nve2plugin.o fix_nve2.o + $(LD) -o $@ $^ + +.cpp.o: + $(CXX) -o $@ $(CXXFLAGS) -c $< + +helloplugin.o: helloplugin.cpp + +pair_morse2.o: pair_morse2.cpp pair_morse2.h +pair_morse2_omp.o: pair_morse2_omp.cpp pair_morse2_omp.h pair_morse2.h +morse2plugin.o: morse2plugin.cpp pair_morse2.h pair_morse2_omp.h + +fix_nve2.o: fix_nve2.cpp fix_nve2.h +nve2plugin.o: nve2plugin.cpp fix_nve2.h + +clean: + rm -rf *~ *.dylib *.dylib *.o log.lammps CMakeCache.txt CMakeFiles + From 4b1924fad10322d5173d982115bcdb42252a9477 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 12 Mar 2021 14:29:59 -0500 Subject: [PATCH 29/57] add missing check --- src/plugin.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/plugin.cpp b/src/plugin.cpp index a95fde5b73..68b9978353 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -207,10 +207,16 @@ namespace LAMMPS_NS lmp->force->create_pair("none",0); } } + } else if (pstyle == "fix") { for (int ifix = lmp->modify->find_fix_by_style(name); ifix >= 0; ifix = lmp->modify->find_fix_by_style(name)) lmp->modify->delete_fix(ifix); + + } else if (pstyle == "command") { + auto command_map = lmp->input->command_map; + auto cmd = command_map->find(name); + if (cmd != command_map->end()) command_map->erase(name); } // if reference count is down to zero, close DSO handle. From 1c222286e22e699068a0021221d9c8b1090681e2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 12 Mar 2021 14:30:08 -0500 Subject: [PATCH 30/57] correct output --- src/plugin.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/plugin.cpp b/src/plugin.cpp index 68b9978353..f9bd36f875 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -51,8 +51,7 @@ namespace LAMMPS_NS void *dso = dlopen(file,RTLD_NOW|RTLD_GLOBAL); if (dso == nullptr) { if (me == 0) - utils::logmesg(lmp,fmt::format("Open of plugin file {} failed: {}", - file,utils::getsyserror())); + utils::logmesg(lmp,fmt::format("Open of file {} failed\n",file)); return; } @@ -145,7 +144,7 @@ namespace LAMMPS_NS auto command_map = lmp->input->command_map; if (command_map->find(plugin->name) != command_map->end()) { if (lmp->comm->me == 0) - lmp->error->warning(FLERR,fmt::format("Overriding built-in fix " + lmp->error->warning(FLERR,fmt::format("Overriding built-in command " "style {} from plugin", plugin->name)); } From 98013a1528ab55d9209eff6de4d604574d7bd57e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 12 Mar 2021 15:32:50 -0500 Subject: [PATCH 31/57] add draft version of unit test (will have to be improved for MacOS) --- unittest/commands/CMakeLists.txt | 19 +++++++++++++++++++ unittest/commands/test_simple_commands.cpp | 9 +++++++++ 2 files changed, 28 insertions(+) diff --git a/unittest/commands/CMakeLists.txt b/unittest/commands/CMakeLists.txt index 7a804820cb..0fb0c8088e 100644 --- a/unittest/commands/CMakeLists.txt +++ b/unittest/commands/CMakeLists.txt @@ -1,5 +1,24 @@ +# build LAMMPS plugins, but not on Windows +if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + ExternalProject_Add(plugins + SOURCE_DIR "${LAMMPS_DIR}/examples/plugins" + BINARY_DIR "${CMAKE_BINARY_DIR}/plugins" + CMAKE_ARGS ${CMAKE_REQUEST_PIC} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_INSTALL_PREFIX= + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} + -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} + BUILD_BYPRODUCTS /morse2plugin${CMAKE_STATIC_LIBRARY_SUFFIX} + /nve2plugin${CMAKE_STATIC_LIBRARY_SUFFIX} + /helloplugin${CMAKE_STATIC_LIBRARY_SUFFIX} + INSTALL_COMMAND "" + TEST_COMMAND "") +endif() + add_executable(test_simple_commands test_simple_commands.cpp) +add_dependencies(test_simple_commands plugins) target_link_libraries(test_simple_commands PRIVATE lammps GTest::GMock GTest::GTest) add_test(NAME SimpleCommands COMMAND test_simple_commands WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/unittest/commands/test_simple_commands.cpp b/unittest/commands/test_simple_commands.cpp index 4fce58c668..09f81b8f71 100644 --- a/unittest/commands/test_simple_commands.cpp +++ b/unittest/commands/test_simple_commands.cpp @@ -344,6 +344,15 @@ TEST_F(SimpleCommandsTest, Units) TEST_FAILURE(".*ERROR: Illegal units command.*", lmp->input->one("units unknown");); } +TEST_F(SimpleCommandsTest, Plugin) +{ + ::testing::internal::CaptureStdout(); + lmp->input->one("plugin load plugins/helloplugin.so"); + auto text = ::testing::internal::GetCapturedStdout(); + + ASSERT_THAT(text, MatchesRegex(".*Loading plugin: Hello world command.*")); +} + TEST_F(SimpleCommandsTest, Shell) { if (!verbose) ::testing::internal::CaptureStdout(); From 93bbaef5473b3ad126b28865e50b8081c7245aeb Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 12 Mar 2021 17:28:22 -0500 Subject: [PATCH 32/57] add unit tests for plugin command --- unittest/commands/test_simple_commands.cpp | 66 +++++++++++++++++++++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/unittest/commands/test_simple_commands.cpp b/unittest/commands/test_simple_commands.cpp index 09f81b8f71..08fe53a6b9 100644 --- a/unittest/commands/test_simple_commands.cpp +++ b/unittest/commands/test_simple_commands.cpp @@ -346,11 +346,73 @@ TEST_F(SimpleCommandsTest, Units) TEST_F(SimpleCommandsTest, Plugin) { +#if defined(__APPLE__) + std::string loadfmt("plugin load plugins/{}plugin.dylib"); +#else + std::string loadfmt("plugin load plugins/{}plugin.so"); +#endif ::testing::internal::CaptureStdout(); - lmp->input->one("plugin load plugins/helloplugin.so"); + lmp->input->one(fmt::format(loadfmt, "hello")); auto text = ::testing::internal::GetCapturedStdout(); - + if (verbose) std::cout << text; ASSERT_THAT(text, MatchesRegex(".*Loading plugin: Hello world command.*")); + + ::testing::internal::CaptureStdout(); + lmp->input->one(fmt::format(loadfmt, "xxx")); + text = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << text; + ASSERT_THAT(text, MatchesRegex(".*Open of file plugins/xxx.* failed.*")); + + ::testing::internal::CaptureStdout(); + lmp->input->one(fmt::format(loadfmt, "nve2")); + text = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << text; + ASSERT_THAT(text, MatchesRegex(".*Loading plugin: NVE2 variant fix style.*")); + ::testing::internal::CaptureStdout(); + lmp->input->one("plugin list"); + text = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << text; + ASSERT_THAT(text, MatchesRegex(".*1: command style plugin hello" + ".*2: fix style plugin nve2.*")); + + ::testing::internal::CaptureStdout(); + lmp->input->one(fmt::format(loadfmt, "hello")); + text = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << text; + ASSERT_THAT(text, MatchesRegex(".*Ignoring load of command style hello: " + "must unload existing hello plugin.*")); + + ::testing::internal::CaptureStdout(); + lmp->input->one("plugin unload command hello"); + text = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << text; + ASSERT_THAT(text, MatchesRegex(".*Unloading command style hello.*")); + + ::testing::internal::CaptureStdout(); + lmp->input->one("plugin unload pair nve2"); + text = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << text; + ASSERT_THAT(text, MatchesRegex(".*Ignoring unload of pair style nve2: " + "not loaded from a plugin.*")); + + ::testing::internal::CaptureStdout(); + lmp->input->one("plugin unload fix nve2"); + text = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << text; + ASSERT_THAT(text, MatchesRegex(".*Unloading fix style nve2.*")); + + ::testing::internal::CaptureStdout(); + lmp->input->one("plugin unload fix nve"); + text = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << text; + ASSERT_THAT(text, MatchesRegex(".*Ignoring unload of fix style nve: " + "not loaded from a plugin.*")); + + ::testing::internal::CaptureStdout(); + lmp->input->one("plugin list"); + text = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << text; + ASSERT_THAT(text, MatchesRegex(".*Currently loaded plugins.*")); } TEST_F(SimpleCommandsTest, Shell) From b2085f56d6478552110fcdcc5f16921b30a885b4 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 12 Mar 2021 18:35:39 -0500 Subject: [PATCH 33/57] install compiled plugins into the current working directory of the tester --- unittest/commands/CMakeLists.txt | 15 ++++++++++----- unittest/commands/test_simple_commands.cpp | 6 +++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/unittest/commands/CMakeLists.txt b/unittest/commands/CMakeLists.txt index 0fb0c8088e..6c68bfb274 100644 --- a/unittest/commands/CMakeLists.txt +++ b/unittest/commands/CMakeLists.txt @@ -3,17 +3,22 @@ if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") ExternalProject_Add(plugins SOURCE_DIR "${LAMMPS_DIR}/examples/plugins" - BINARY_DIR "${CMAKE_BINARY_DIR}/plugins" + BINARY_DIR ${CMAKE_BINARY_DIR}/build-plugins + INSTALL_DIR ${CMAKE_BINARY_DIR} CMAKE_ARGS ${CMAKE_REQUEST_PIC} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_INSTALL_PREFIX= -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} - BUILD_BYPRODUCTS /morse2plugin${CMAKE_STATIC_LIBRARY_SUFFIX} - /nve2plugin${CMAKE_STATIC_LIBRARY_SUFFIX} - /helloplugin${CMAKE_STATIC_LIBRARY_SUFFIX} - INSTALL_COMMAND "" + BUILD_BYPRODUCTS /morse2plugin${CMAKE_SHARED_LIBRARY_SUFFIX} + /nve2plugin${CMAKE_SHARED_LIBRARY_SUFFIX} + /helloplugin${CMAKE_SHARED_LIBRARY_SUFFIX} + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_if_different + /morse2plugin${CMAKE_SHARED_LIBRARY_SUFFIX} + /nve2plugin${CMAKE_SHARED_LIBRARY_SUFFIX} + /helloplugin${CMAKE_SHARED_LIBRARY_SUFFIX} + ${CMAKE_CURRENT_BINARY_DIR} TEST_COMMAND "") endif() diff --git a/unittest/commands/test_simple_commands.cpp b/unittest/commands/test_simple_commands.cpp index 08fe53a6b9..0e26734dfb 100644 --- a/unittest/commands/test_simple_commands.cpp +++ b/unittest/commands/test_simple_commands.cpp @@ -347,9 +347,9 @@ TEST_F(SimpleCommandsTest, Units) TEST_F(SimpleCommandsTest, Plugin) { #if defined(__APPLE__) - std::string loadfmt("plugin load plugins/{}plugin.dylib"); + std::string loadfmt("plugin load {}plugin.dylib"); #else - std::string loadfmt("plugin load plugins/{}plugin.so"); + std::string loadfmt("plugin load {}plugin.so"); #endif ::testing::internal::CaptureStdout(); lmp->input->one(fmt::format(loadfmt, "hello")); @@ -361,7 +361,7 @@ TEST_F(SimpleCommandsTest, Plugin) lmp->input->one(fmt::format(loadfmt, "xxx")); text = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << text; - ASSERT_THAT(text, MatchesRegex(".*Open of file plugins/xxx.* failed.*")); + ASSERT_THAT(text, MatchesRegex(".*Open of file xxx.* failed.*")); ::testing::internal::CaptureStdout(); lmp->input->one(fmt::format(loadfmt, "nve2")); From 3e90b1971a9f9890be584c47db12c2d922de5b37 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 12 Mar 2021 22:21:11 -0500 Subject: [PATCH 34/57] add preliminary support for compiling/loading plugins on windows --- doc/src/plugin.rst | 3 ++ examples/plugins/CMakeLists.txt | 14 +++----- examples/plugins/Makefile.serial | 30 ++++++++++++++++ src/MAKE/Makefile.mpi | 2 +- src/MAKE/Makefile.serial | 2 +- src/plugin.cpp | 59 ++++++++++++++++++++++++-------- 6 files changed, 85 insertions(+), 25 deletions(-) create mode 100644 examples/plugins/Makefile.serial diff --git a/doc/src/plugin.rst b/doc/src/plugin.rst index 25a55565b9..a75e39ccae 100644 --- a/doc/src/plugin.rst +++ b/doc/src/plugin.rst @@ -58,6 +58,9 @@ Restrictions Plugins are currently not available on Windows. +For the loading of plugins to work, the LAMMPS library must be +:ref:`compiled as a shared library `. + Plugins are dependent on the LAMMPS binary interface (ABI) and particularly the MPI library used. So they are not guaranteed to work when the plugin was compiled with a different MPI library diff --git a/examples/plugins/CMakeLists.txt b/examples/plugins/CMakeLists.txt index 74a1cd4e3b..2db2c041fd 100644 --- a/examples/plugins/CMakeLists.txt +++ b/examples/plugins/CMakeLists.txt @@ -31,11 +31,6 @@ endif() set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# bail out on windows -if(CMAKE_SYSTEM_NAME STREQUAL Windows) - message(FATAL_ERROR "LAMMPS plugins are currently not supported on Windows") -endif() - set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}) include(CheckIncludeFileCXX) include(LAMMPSInterfaceCXX) @@ -53,12 +48,13 @@ target_link_libraries(nve2plugin PRIVATE lammps) add_library(helloplugin MODULE helloplugin.cpp) target_link_libraries(helloplugin PRIVATE lammps) -set_target_properties(morse2plugin nve2plugin helloplugin PROPERTIES - PREFIX "" - LINK_FLAGS "-rdynamic") +set_target_properties(morse2plugin nve2plugin helloplugin PROPERTIES PREFIX "") -# MacOS seems to need this if(CMAKE_SYSTEM_NAME STREQUAL Darwin) set_target_properties(morse2plugin nve2plugin helloplugin PROPERTIES LINK_FLAGS "-Wl,-undefined,dynamic_lookup") +elseif(CMAKE_SYSTEM_NAME STREQUAL Windows) + set_target_properties(morse2plugin nve2plugin helloplugin PROPERTIES LINK_FLAGS "-Wl,--undefined") +else() + set_target_properties(morse2plugin nve2plugin helloplugin PROPERTIES LINK_FLAGS "-rdynamic") endif() diff --git a/examples/plugins/Makefile.serial b/examples/plugins/Makefile.serial new file mode 100644 index 0000000000..feb58bd9b3 --- /dev/null +++ b/examples/plugins/Makefile.serial @@ -0,0 +1,30 @@ +CXX=g++ +CXXFLAGS=-I../../src -I../../src/STUBS -Wall -Wextra -O3 -fPIC -I../../src/USER-OMP -fopenmp +LD=$(CXX) -shared -rdynamic -fopenmp + +default: morse2plugin.so nve2plugin.so helloplugin.so + +helloplugin.so: helloplugin.o + $(LD) -o $@ $^ + +morse2plugin.so: morse2plugin.o pair_morse2.o pair_morse2_omp.o + $(LD) -o $@ $^ + +nve2plugin.so: nve2plugin.o fix_nve2.o + $(LD) -o $@ $^ + +.cpp.o: + $(CXX) -o $@ $(CXXFLAGS) -c $< + +helloplugin.o: helloplugin.cpp + +pair_morse2.o: pair_morse2.cpp pair_morse2.h +pair_morse2_omp.o: pair_morse2_omp.cpp pair_morse2_omp.h pair_morse2.h +morse2plugin.o: morse2plugin.cpp pair_morse2.h pair_morse2_omp.h + +fix_nve2.o: fix_nve2.cpp fix_nve2.h +nve2plugin.o: nve2plugin.cpp fix_nve2.h + +clean: + rm -rf *~ *.so *.dylib *.o log.lammps CMakeCache.txt CMakeFiles + diff --git a/src/MAKE/Makefile.mpi b/src/MAKE/Makefile.mpi index c66e66c217..68e79e9e8e 100644 --- a/src/MAKE/Makefile.mpi +++ b/src/MAKE/Makefile.mpi @@ -18,7 +18,7 @@ SIZE = size ARCHIVE = ar ARFLAGS = -rc -SHLIBFLAGS = -shared +SHLIBFLAGS = -shared -rdynamic # --------------------------------------------------------------------- # LAMMPS-specific settings, all OPTIONAL diff --git a/src/MAKE/Makefile.serial b/src/MAKE/Makefile.serial index daf04cc5b0..8b4e2e5982 100644 --- a/src/MAKE/Makefile.serial +++ b/src/MAKE/Makefile.serial @@ -18,7 +18,7 @@ SIZE = size ARCHIVE = ar ARFLAGS = -rc -SHLIBFLAGS = -shared +SHLIBFLAGS = -shared -rdynamic # --------------------------------------------------------------------- # LAMMPS-specific settings, all OPTIONAL diff --git a/src/plugin.cpp b/src/plugin.cpp index f9bd36f875..2c4f2da396 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -24,12 +24,49 @@ #include #include -#ifdef _WIN32 +#if defined(_WIN32) #include #else #include #endif +#if defined(_WIN32) + +// open a shared object file +static void *my_dlopen(const char *fname) { + return (void *)LoadLibrary(fname); +} + +// resolve a symbol in shared object +static void *my_dlsym(void *h, const char *sym) { + return (void *)GetProcAddress((HINSTANCE)h, sym); +} + +// close a shared object +static int my_dlclose(void *h) { + /* FreeLibrary returns nonzero on success */ + return !FreeLibrary((HINSTANCE)h); +} + +#else + +// open a shared object file +static void *my_dlopen(const char *fname) { + return dlopen(fname, RTLD_NOW|RTLD_GLOBAL); +} + +// resolve a symbol in shared object +static void *my_dlsym(void *h, const char *sym) { + return dlsym(h, sym); +} + +// close a shared object +static int my_dlclose(void *h) { + return dlclose(h); +} + +#endif + namespace LAMMPS_NS { // list of plugin information data for loaded styles @@ -42,13 +79,11 @@ namespace LAMMPS_NS void plugin_load(const char *file, LAMMPS *lmp) { int me = lmp->comm->me; -#if defined(WIN32) - lmp->error->all(FLERR,"Loading of plugins on Windows not yet supported\n"); -#else // open DSO file from given path; load symbols globally - - void *dso = dlopen(file,RTLD_NOW|RTLD_GLOBAL); + + void *dso = my_dlopen(file); + if (dso == nullptr) { if (me == 0) utils::logmesg(lmp,fmt::format("Open of file {} failed\n",file)); @@ -58,9 +93,10 @@ namespace LAMMPS_NS // look up lammpsplugin_init() function in DSO // function must have C bindings so there is no name mangling - void *initfunc = dlsym(dso,"lammpsplugin_init"); + void *initfunc = my_dlsym(dso,"lammpsplugin_init"); + if (initfunc == nullptr) { - dlclose(dso); + my_dlclose(dso); if (me == 0) utils::logmesg(lmp,fmt::format("Plugin symbol lookup failure in " @@ -74,7 +110,6 @@ namespace LAMMPS_NS (*(lammpsplugin_initfunc)(initfunc))((void *)lmp, dso, (void *)&plugin_register); -#endif } /* -------------------------------------------------------------------- @@ -221,11 +256,7 @@ namespace LAMMPS_NS // if reference count is down to zero, close DSO handle. -- dso_refcounter[handle]; - if (dso_refcounter[handle] == 0) { -#ifndef WIN32 - dlclose(handle); -#endif - } + if (dso_refcounter[handle] == 0) my_dlclose(handle); } /* -------------------------------------------------------------------- From d05137455c660e2c801ebffddc36ca9eaf51b8d2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 12 Mar 2021 22:21:24 -0500 Subject: [PATCH 35/57] ignore build folders --- examples/plugins/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 examples/plugins/.gitignore diff --git a/examples/plugins/.gitignore b/examples/plugins/.gitignore new file mode 100644 index 0000000000..0ec9e7f982 --- /dev/null +++ b/examples/plugins/.gitignore @@ -0,0 +1 @@ +/build* From 4ae7f84c2ac0552c3f123e6e9f02911b526ed6b9 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 12 Mar 2021 22:27:38 -0500 Subject: [PATCH 36/57] whitespace --- src/plugin.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugin.cpp b/src/plugin.cpp index 2c4f2da396..73fcfd947e 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -81,9 +81,9 @@ namespace LAMMPS_NS int me = lmp->comm->me; // open DSO file from given path; load symbols globally - + void *dso = my_dlopen(file); - + if (dso == nullptr) { if (me == 0) utils::logmesg(lmp,fmt::format("Open of file {} failed\n",file)); From e3d9c3126b12c081598b3ac36e5497edca8f12bd Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 13 Mar 2021 10:25:44 -0500 Subject: [PATCH 37/57] revert back to not supporting loading plugins on windows --- examples/plugins/CMakeLists.txt | 14 +++++--- src/plugin.cpp | 57 ++++++++------------------------- 2 files changed, 22 insertions(+), 49 deletions(-) diff --git a/examples/plugins/CMakeLists.txt b/examples/plugins/CMakeLists.txt index 2db2c041fd..74a1cd4e3b 100644 --- a/examples/plugins/CMakeLists.txt +++ b/examples/plugins/CMakeLists.txt @@ -31,6 +31,11 @@ endif() set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) +# bail out on windows +if(CMAKE_SYSTEM_NAME STREQUAL Windows) + message(FATAL_ERROR "LAMMPS plugins are currently not supported on Windows") +endif() + set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}) include(CheckIncludeFileCXX) include(LAMMPSInterfaceCXX) @@ -48,13 +53,12 @@ target_link_libraries(nve2plugin PRIVATE lammps) add_library(helloplugin MODULE helloplugin.cpp) target_link_libraries(helloplugin PRIVATE lammps) -set_target_properties(morse2plugin nve2plugin helloplugin PROPERTIES PREFIX "") +set_target_properties(morse2plugin nve2plugin helloplugin PROPERTIES + PREFIX "" + LINK_FLAGS "-rdynamic") +# MacOS seems to need this if(CMAKE_SYSTEM_NAME STREQUAL Darwin) set_target_properties(morse2plugin nve2plugin helloplugin PROPERTIES LINK_FLAGS "-Wl,-undefined,dynamic_lookup") -elseif(CMAKE_SYSTEM_NAME STREQUAL Windows) - set_target_properties(morse2plugin nve2plugin helloplugin PROPERTIES LINK_FLAGS "-Wl,--undefined") -else() - set_target_properties(morse2plugin nve2plugin helloplugin PROPERTIES LINK_FLAGS "-rdynamic") endif() diff --git a/src/plugin.cpp b/src/plugin.cpp index 73fcfd947e..d654535f56 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -24,49 +24,12 @@ #include #include -#if defined(_WIN32) +#ifdef _WIN32 #include #else #include #endif -#if defined(_WIN32) - -// open a shared object file -static void *my_dlopen(const char *fname) { - return (void *)LoadLibrary(fname); -} - -// resolve a symbol in shared object -static void *my_dlsym(void *h, const char *sym) { - return (void *)GetProcAddress((HINSTANCE)h, sym); -} - -// close a shared object -static int my_dlclose(void *h) { - /* FreeLibrary returns nonzero on success */ - return !FreeLibrary((HINSTANCE)h); -} - -#else - -// open a shared object file -static void *my_dlopen(const char *fname) { - return dlopen(fname, RTLD_NOW|RTLD_GLOBAL); -} - -// resolve a symbol in shared object -static void *my_dlsym(void *h, const char *sym) { - return dlsym(h, sym); -} - -// close a shared object -static int my_dlclose(void *h) { - return dlclose(h); -} - -#endif - namespace LAMMPS_NS { // list of plugin information data for loaded styles @@ -79,11 +42,13 @@ namespace LAMMPS_NS void plugin_load(const char *file, LAMMPS *lmp) { int me = lmp->comm->me; +#if defined(WIN32) + lmp->error->all(FLERR,"Loading of plugins on Windows is not supported\n"); +#else // open DSO file from given path; load symbols globally - void *dso = my_dlopen(file); - + void *dso = dlopen(file,RTLD_NOW|RTLD_GLOBAL); if (dso == nullptr) { if (me == 0) utils::logmesg(lmp,fmt::format("Open of file {} failed\n",file)); @@ -93,10 +58,9 @@ namespace LAMMPS_NS // look up lammpsplugin_init() function in DSO // function must have C bindings so there is no name mangling - void *initfunc = my_dlsym(dso,"lammpsplugin_init"); - + void *initfunc = dlsym(dso,"lammpsplugin_init"); if (initfunc == nullptr) { - my_dlclose(dso); + dlclose(dso); if (me == 0) utils::logmesg(lmp,fmt::format("Plugin symbol lookup failure in " @@ -110,6 +74,7 @@ namespace LAMMPS_NS (*(lammpsplugin_initfunc)(initfunc))((void *)lmp, dso, (void *)&plugin_register); +#endif } /* -------------------------------------------------------------------- @@ -256,7 +221,11 @@ namespace LAMMPS_NS // if reference count is down to zero, close DSO handle. -- dso_refcounter[handle]; - if (dso_refcounter[handle] == 0) my_dlclose(handle); + if (dso_refcounter[handle] == 0) { +#ifndef WIN32 + dlclose(handle); +#endif + } } /* -------------------------------------------------------------------- From 76cff1ed1ec1ce562ed58d4c3f2e0aa483d18444 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 13 Mar 2021 12:17:20 -0500 Subject: [PATCH 38/57] add library interface for introspection of loaded plugins --- src/library.cpp | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ src/library.h | 3 +++ 2 files changed, 57 insertions(+) diff --git a/src/library.cpp b/src/library.cpp index 2a7bbf07b3..ae3212d6f3 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -38,6 +38,7 @@ #include "neighbor.h" #include "region.h" #include "output.h" +#include "plugin.h" #include "thermo.h" #include "timer.h" #include "universe.h" @@ -4581,6 +4582,59 @@ int lammps_id_name(void *handle, const char *category, int idx, return 0; } +/* ---------------------------------------------------------------------- */ + +/** Count the number of loaded plugins + * +\verbatim embed:rst +This function counts how many plugins are currently loaded. + +.. versionadded:: 10Mar2021 + +\endverbatim + * + * \return number of loaded plugins + */ +int lammps_plugin_count() +{ + return plugin_get_num_plugins(); +} + +/* ---------------------------------------------------------------------- */ + +/** Look up the info of a loaded plugin by its index in the list of plugins + * +\verbatim embed:rst +This function copies the name of the *style* plugin with the index +*idx* into the provided C-style string buffer. The length of the buffer +must be provided as *buf_size* argument. If the name of the style +exceeds the length of the buffer, it will be truncated accordingly. +If the index is out of range, the function returns 0 and *buffer* is +set to an empty string, otherwise 1. + +.. versionadded:: 10Mar2021 + +\endverbatim + * + * \param idx index of the plugin in the list all or *style* plugins + * \param stylebuf string buffer to copy the style of the plugin to + * \param namebuf string buffer to copy the name of the plugin to + * \param buf_size size of the provided string buffers + * \return 1 if successful, otherwise 0 + */ +int lammps_plugin_name(int idx, char *stylebuf, char *namebuf, int buf_size) +{ + stylebuf[0] = namebuf[0] = '\0'; + + const lammpsplugin_t *plugin = plugin_get_info(idx); + if (plugin) { + strncpy(stylebuf,plugin->style,buf_size); + strncpy(namebuf,plugin->name,buf_size); + return 1; + } + return 0; +} + // ---------------------------------------------------------------------- // utility functions // ---------------------------------------------------------------------- diff --git a/src/library.h b/src/library.h index d98bf426b3..11cd72388a 100644 --- a/src/library.h +++ b/src/library.h @@ -205,6 +205,9 @@ int lammps_has_id(void *, const char *, const char *); int lammps_id_count(void *, const char *); int lammps_id_name(void *, const char *, int, char *, int); +int lammps_plugin_count(); +int lammps_plugin_name(int, char *, char *, int); + /* ---------------------------------------------------------------------- * Utility functions * ---------------------------------------------------------------------- */ From dd94bac0c8b3b61cb1805d703f189b78958a2f3d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 13 Mar 2021 12:17:45 -0500 Subject: [PATCH 39/57] better error message when trying to unload an unsupported plugin style --- src/plugin.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/plugin.cpp b/src/plugin.cpp index d654535f56..fb3186c69e 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -167,6 +167,16 @@ namespace LAMMPS_NS { int me = lmp->comm->me; + // ignore unload request from unsupported style categories + if ((strcmp(style,"pair") != 0) + && (strcmp(style,"fix") != 0) + && (strcmp(style,"command") != 0)) { + if (me == 0) + utils::logmesg(lmp,fmt::format("Ignoring unload: {} is not a " + "supported plugin style\n",style)); + return; + } + // ignore unload request if not loaded from a plugin int idx = plugin_find(style,name); if (idx < 0) { From 79d438e090e9b72ac6db2d04403005bad3086645 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 13 Mar 2021 12:18:07 -0500 Subject: [PATCH 40/57] add support for plugin command to LAMMPS shell --- tools/lammps-shell/lammps-shell.cpp | 66 ++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/tools/lammps-shell/lammps-shell.cpp b/tools/lammps-shell/lammps-shell.cpp index 7ace6d6819..8153c1bcfa 100644 --- a/tools/lammps-shell/lammps-shell.cpp +++ b/tools/lammps-shell/lammps-shell.cpp @@ -128,6 +128,7 @@ const char *cmdlist[] = {"clear", "pair_modify", "pair_style", "pair_write", + "plugin", "processors", "region", "reset_timestep", @@ -347,6 +348,59 @@ static char *variable_expand_generator(const char *text, int state) return nullptr; } +static char *plugin_generator(const char *text, int state) +{ + const char *subcmd[] = {"load", "unload", "list", NULL}; + const char *sub; + static std::size_t idx, len; + if (!state) idx = 0; + len = strlen(text); + + while ((sub = subcmd[idx]) != NULL) { + ++idx; + if (strncmp(text,sub,len) == 0) + return dupstring(sub); + } + return nullptr; +} + +static char *plugin_style_generator(const char *text, int state) +{ + const char *styles[] = {"pair", "fix", "command", NULL}; + const char *s; + static std::size_t idx, len; + if (!state) idx = 0; + len = strlen(text); + while ((s = styles[idx]) != NULL) { + ++idx; + if (strncmp(text,s,len) == 0) + return dupstring(s); + } + return nullptr; +} + +static char *plugin_name_generator(const char *text, int state) +{ + auto words = utils::split_words(text); + if (words.size() < 4) return nullptr; + + static std::size_t idx, len; + if (!state) idx = 0; + len = words[3].size(); + int nmax = lammps_plugin_count(); + + while (idx < nmax) { + char style[buflen], name[buflen]; + lammps_plugin_name(idx, style, name, buflen); + ++idx; + if (words[2] == style) { + if (strncmp(name, words[3].c_str(), len) == 0) + return dupstring(name); + } + } + return nullptr; +} + static char *atom_generator(const char *text, int state) { return style_generator(text, state); @@ -477,14 +531,21 @@ static char **cmd_completion(const char *text, int start, int) matches = rl_completion_matches(text, dump_id_generator); } else if (words[0] == "fix_modify") { matches = rl_completion_matches(text, fix_id_generator); + } else if (words[0] == "plugin") { + matches = rl_completion_matches(text, plugin_generator); } } else if (words.size() == 2) { // expand third word // these commands have a group name as 3rd word - if ((words[0] == "fix") || (words[0] == "compute") || (words[0] == "dump")) { + if ((words[0] == "fix") + || (words[0] == "compute") + || (words[0] == "dump")) { matches = rl_completion_matches(text, group_generator); } else if (words[0] == "region") { matches = rl_completion_matches(text, region_generator); + // plugin style is the third word + } else if ((words[0] == "plugin") && (words[1] == "unload")) { + matches = rl_completion_matches(text, plugin_style_generator); } } else if (words.size() == 3) { // expand fourth word @@ -495,6 +556,9 @@ static char **cmd_completion(const char *text, int start, int) matches = rl_completion_matches(text, compute_generator); } else if (words[0] == "dump") { matches = rl_completion_matches(text, dump_generator); + // plugin name is the fourth word + } else if ((words[0] == "plugin") && (words[1] == "unload")) { + matches = rl_completion_matches(rl_line_buffer, plugin_name_generator); } } } From 88760fa648a69352bd333e6b06c412acb17eec08 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 13 Mar 2021 12:40:04 -0500 Subject: [PATCH 41/57] add plugin clear command to unload all loaded plugins --- doc/src/plugin.rst | 18 ++++++++++++------ src/input.cpp | 2 ++ src/plugin.cpp | 12 ++++++++++++ src/plugin.h | 1 + tools/lammps-shell/lammps-shell.cpp | 2 +- 5 files changed, 28 insertions(+), 7 deletions(-) diff --git a/doc/src/plugin.rst b/doc/src/plugin.rst index a75e39ccae..e8a5f67e3c 100644 --- a/doc/src/plugin.rst +++ b/doc/src/plugin.rst @@ -10,7 +10,7 @@ Syntax plugin command args -* command = *load* or *unload* or *list* +* command = *load* or *unload* or *list* or *clear* * args = list of arguments for a particular plugin command .. parsed-literal:: @@ -19,6 +19,7 @@ Syntax *unload* style name = unload plugin *name* of style *style* *style* = *pair* or *fix* or *command* *list* = print a list of currently loaded plugins + *clear* = unload all currently loaded plugins Examples """""""" @@ -29,6 +30,7 @@ Examples plugin unload pair morse2/omp plugin unload command hello plugin list + plugin clear Description """"""""""" @@ -52,21 +54,25 @@ that style instance will be deleted. The *list* command will print a list of the loaded plugins and their styles and names. +The *clear* command will unload all currently loaded plugins. + Restrictions """""""""""" -Plugins are currently not available on Windows. +Plugins are not available on Windows. -For the loading of plugins to work, the LAMMPS library must be -:ref:`compiled as a shared library `. +For the loading of plugins to work the LAMMPS library must be +:ref:`compiled as a shared library `. If plugins +access functions or classes from a package, LAMMPS must have +been compiled with that package included. Plugins are dependent on the LAMMPS binary interface (ABI) and particularly the MPI library used. So they are not guaranteed to work when the plugin was compiled with a different MPI library or different compilation settings or a different LAMMPS version. -If there is a mismatch the *plugin* command may fail to load the -plugin(s) or data corruption or crashes may happen. +There are no checks, so if there is a mismatch the plugin object +will either not load or data corruption and crashes may happen. Related commands diff --git a/src/input.cpp b/src/input.cpp index 6d1564ecb6..1df60f5298 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -1110,6 +1110,8 @@ void Input::plugin() } else if (cmd == "unload") { if (narg != 3) error->all(FLERR,"Illegal plugin unload command"); plugin_unload(arg[1],arg[2],lmp); + } else if (cmd == "clear") { + plugin_clear(lmp); } else if (cmd == "list") { if (comm->me == 0) { int num = plugin_get_num_plugins(); diff --git a/src/plugin.cpp b/src/plugin.cpp index fb3186c69e..99b1b086c5 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -238,6 +238,18 @@ namespace LAMMPS_NS } } + /* -------------------------------------------------------------------- + unload all loaded plugins + -------------------------------------------------------------------- */ + + void plugin_clear(LAMMPS *lmp) + { + while (pluginlist.size() > 0) { + auto p = pluginlist.begin(); + plugin_unload(p->style,p->name,lmp); + } + } + /* -------------------------------------------------------------------- remove plugin of given name and style from internal lists -------------------------------------------------------------------- */ diff --git a/src/plugin.h b/src/plugin.h index 32a5a280a1..90952224a6 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -25,6 +25,7 @@ namespace LAMMPS_NS void plugin_unload(const char *, const char *, LAMMPS *); void plugin_erase(const char *, const char *); + void plugin_clear(LAMMPS *); int plugin_get_num_plugins(); int plugin_find(const char *, const char *); diff --git a/tools/lammps-shell/lammps-shell.cpp b/tools/lammps-shell/lammps-shell.cpp index 8153c1bcfa..6c8873093f 100644 --- a/tools/lammps-shell/lammps-shell.cpp +++ b/tools/lammps-shell/lammps-shell.cpp @@ -350,7 +350,7 @@ static char *variable_expand_generator(const char *text, int state) static char *plugin_generator(const char *text, int state) { - const char *subcmd[] = {"load", "unload", "list", NULL}; + const char *subcmd[] = {"load", "unload", "list", "clear", NULL}; const char *sub; static std::size_t idx, len; if (!state) idx = 0; From 10189760c68799d957e29c389c06ceb6242e5822 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 13 Mar 2021 12:40:49 -0500 Subject: [PATCH 42/57] fix issue of not removing unloaded plugins from fix map --- src/plugin.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/plugin.cpp b/src/plugin.cpp index 99b1b086c5..2d18da64ce 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -186,7 +186,7 @@ namespace LAMMPS_NS return; } - // copy of DSO handle for later + // make copy of DSO handle for later use void *handle = plugin_get_info(idx)->handle; // remove selected plugin from list of plugins @@ -201,6 +201,7 @@ namespace LAMMPS_NS std::string pstyle = style; if (pstyle == "pair") { + auto found = lmp->force->pair_map->find(name); if (found != lmp->force->pair_map->end()) lmp->force->pair_map->erase(found); @@ -218,14 +219,20 @@ namespace LAMMPS_NS } } else if (pstyle == "fix") { + + auto fix_map = lmp->modify->fix_map; + auto found = fix_map->find(name); + if (found != fix_map->end()) fix_map->erase(name); + for (int ifix = lmp->modify->find_fix_by_style(name); ifix >= 0; ifix = lmp->modify->find_fix_by_style(name)) lmp->modify->delete_fix(ifix); } else if (pstyle == "command") { + auto command_map = lmp->input->command_map; - auto cmd = command_map->find(name); - if (cmd != command_map->end()) command_map->erase(name); + auto found = command_map->find(name); + if (found != command_map->end()) command_map->erase(name); } // if reference count is down to zero, close DSO handle. From 98fa3661f382c423748f296e246417f5b4d0698e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 13 Mar 2021 12:41:01 -0500 Subject: [PATCH 43/57] silence compiler warning --- src/library.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/library.cpp b/src/library.cpp index ae3212d6f3..3fed6ba8cd 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -988,12 +988,12 @@ to then decide how to cast the (void*) pointer and access the data. \endverbatim * - * \param handle pointer to a previously created LAMMPS instance + * \param handle pointer to a previously created LAMMPS instance (unused) * \param name string with the name of the extracted property * \return integer constant encoding the data type of the property * or -1 if not found. */ -int lammps_extract_global_datatype(void *handle, const char *name) +int lammps_extract_global_datatype(void * /*handle*/, const char *name) { if (strcmp(name,"dt") == 0) return LAMMPS_DOUBLE; if (strcmp(name,"ntimestep") == 0) return LAMMPS_BIGINT; From 15e30ed44da6d9147f08fedc44e5770df4c93da8 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 13 Mar 2021 18:41:36 -0500 Subject: [PATCH 44/57] report dynamic linker error messages on failures --- src/plugin.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/plugin.cpp b/src/plugin.cpp index 2d18da64ce..e252ce67e1 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -48,23 +48,26 @@ namespace LAMMPS_NS // open DSO file from given path; load symbols globally + dlerror(); void *dso = dlopen(file,RTLD_NOW|RTLD_GLOBAL); if (dso == nullptr) { if (me == 0) - utils::logmesg(lmp,fmt::format("Open of file {} failed\n",file)); + utils::logmesg(lmp,fmt::format("Open of file {} failed: {}\n", + file,dlerror())); return; } // look up lammpsplugin_init() function in DSO // function must have C bindings so there is no name mangling + dlerror(); void *initfunc = dlsym(dso,"lammpsplugin_init"); if (initfunc == nullptr) { dlclose(dso); if (me == 0) utils::logmesg(lmp,fmt::format("Plugin symbol lookup failure in " - "file {}\n",file)); + "file {}: {}\n",file,dlerror())); return; } From 11894f83b9b705eb152a0525e7bd5c04834aac7d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 16 Mar 2021 19:50:45 -0400 Subject: [PATCH 45/57] clarify and fix grammar issues --- doc/src/Developer_plugins.rst | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/doc/src/Developer_plugins.rst b/doc/src/Developer_plugins.rst index 192c57ccbb..7275c12605 100644 --- a/doc/src/Developer_plugins.rst +++ b/doc/src/Developer_plugins.rst @@ -162,17 +162,20 @@ 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 added to -the plugin info struct, so that the DSO can be close and unloaded when -all its contained plugins are unloaded. The third 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. +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, DSO handle. -The the registration function is called with a pointer to the address of -this struct and the pointer of the LAMMPS class. The registration function +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 From 125ae33ccfa4aa97212607bab3487fb4193d015e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 16 Mar 2021 22:43:13 -0400 Subject: [PATCH 46/57] convert plugin functionality into a package --- cmake/CMakeLists.txt | 19 +++++-- cmake/presets/most.cmake | 2 +- lib/README | 20 ++++--- lib/plugin/README | 16 ++++++ src/.gitignore | 4 ++ src/MAKE/Makefile.mpi | 2 +- src/MAKE/Makefile.serial | 2 +- src/Makefile | 2 +- src/PLUGIN/Install.sh | 64 ++++++++++++++++++++++ src/PLUGIN/README | 12 ++++ src/{ => PLUGIN}/plugin.cpp | 49 +++++++++++++++++ src/{ => PLUGIN}/plugin.h | 15 ++++- src/input.cpp | 30 ---------- src/library.cpp | 8 +++ unittest/commands/CMakeLists.txt | 2 +- unittest/commands/test_simple_commands.cpp | 2 + 16 files changed, 198 insertions(+), 51 deletions(-) create mode 100644 lib/plugin/README create mode 100755 src/PLUGIN/Install.sh create mode 100644 src/PLUGIN/README rename src/{ => PLUGIN}/plugin.cpp (87%) rename src/{ => PLUGIN}/plugin.h (84%) diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 7081d6aa80..f05c7f97eb 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -113,7 +113,7 @@ option(CMAKE_VERBOSE_MAKEFILE "Generate verbose Makefiles" OFF) set(STANDARD_PACKAGES ASPHERE BODY CLASS2 COLLOID COMPRESS DIPOLE GRANULAR KSPACE LATTE MANYBODY MC MESSAGE MISC MLIAP MOLECULE PERI POEMS - QEQ REPLICA RIGID SHOCK SPIN SNAP SRD KIM PYTHON MSCG MPIIO VORONOI + PLUGIN QEQ REPLICA RIGID SHOCK SPIN SNAP SRD KIM PYTHON MSCG MPIIO VORONOI USER-ADIOS USER-ATC USER-AWPMD USER-BOCS USER-CGDNA USER-MESODPD USER-CGSDK USER-COLVARS USER-DIFFRACTION USER-DPD USER-DRUDE USER-EFF USER-FEP USER-H5MD USER-LB USER-MANIFOLD USER-MEAMC USER-MESONT USER-MGPT USER-MISC USER-MOFFF @@ -240,11 +240,6 @@ if(BUILD_OMP) target_link_libraries(lammps PRIVATE OpenMP::OpenMP_CXX) endif() -# link with -ldl or equivalent for plugin loading; except on Windows -if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") - target_link_libraries(lammps PRIVATE ${CMAKE_DL_LIBS}) -endif() - # Compiler specific features for testing if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") option(ENABLE_COVERAGE "Enable collecting code coverage data" OFF) @@ -538,6 +533,18 @@ foreach(PKG_WITH_INCL CORESHELL QEQ USER-OMP USER-SDPD KOKKOS OPT USER-INTEL GPU endif() endforeach() +if(PKG_PLUGIN) + if(BUILD_SHARED_LIBS) + target_compile_definitions(lammps PRIVATE -DLMP_PLUGIN) + else() + message(WARNING "Plugin loading will not work unless BUILD_SHARED_LIBS is enabled") + endif() + # link with -ldl or equivalent for plugin loading; except on Windows + if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + target_link_libraries(lammps PRIVATE ${CMAKE_DL_LIBS}) + endif() +endif() + ###################################################################### # the windows version of LAMMPS requires a couple extra libraries # and the MPI library - if use - has to be linked right before those diff --git a/cmake/presets/most.cmake b/cmake/presets/most.cmake index bddefc077b..5dc58b735b 100644 --- a/cmake/presets/most.cmake +++ b/cmake/presets/most.cmake @@ -4,7 +4,7 @@ set(ALL_PACKAGES ASPHERE BODY CLASS2 COLLOID COMPRESS CORESHELL DIPOLE GRANULAR KSPACE MANYBODY MC MISC MLIAP MOLECULE OPT PERI - POEMS PYTHON QEQ REPLICA RIGID SHOCK SNAP SPIN SRD VORONOI + PLUGIN POEMS PYTHON QEQ REPLICA RIGID SHOCK SNAP SPIN SRD VORONOI USER-BOCS USER-CGDNA USER-CGSDK USER-COLVARS USER-DIFFRACTION USER-DPD USER-DRUDE USER-EFF USER-FEP USER-MEAMC USER-MESODPD USER-MISC USER-MOFFF USER-OMP USER-PHONON USER-REACTION diff --git a/lib/README b/lib/README index d89490e202..26c3fad9e0 100644 --- a/lib/README +++ b/lib/README @@ -19,12 +19,12 @@ atc atomistic-to-continuum methods, USER-ATC package from Reese Jones, Jeremy Templeton, Jon Zimmerman (Sandia) awpmd antisymmetrized wave packet molecular dynamics, AWPMD package from Ilya Valuev (JIHT RAS) -colvars collective variable module (Metadynamics, ABF and more) +colvars collective variable module (Metadynamics, ABF and more) from Giacomo Fiorin and Jerome Henin (ICMS, Temple U) compress hook to system lib for performing I/O compression, COMPRESS pkg - from Axel Kohlmeyer (Temple U) -gpu general GPU routines, GPU package - from Mike Brown (ORNL) + from Axel Kohlmeyer (Temple U) +gpu general GPU routines, GPU package + from Mike Brown (ORNL) h5md ch5md library for output of MD data in HDF5 format from Pierre de Buyl (KU Leuven) kim hooks to the KIM library, used by KIM package @@ -32,26 +32,28 @@ kim hooks to the KIM library, used by KIM package kokkos Kokkos package for GPU and many-core acceleration from Kokkos development team (Sandia) linalg set of BLAS and LAPACK routines needed by USER-ATC package - from Axel Kohlmeyer (Temple U) + from Axel Kohlmeyer (Temple U) message client/server communication library via MPI, sockets, files - from Steve Plimpton (Sandia) + from Steve Plimpton (Sandia) molfile hooks to VMD molfile plugins, used by the USER-MOLFILE package from Axel Kohlmeyer (Temple U) and the VMD development team mscg hooks to the MSCG library, used by fix_mscg command from Jacob Wagner and Greg Voth group (U Chicago) netcdf hooks to a NetCDF library installed on your system from Lars Pastewka (Karlsruhe Institute of Technology) -poems POEMS rigid-body integration package, POEMS package +plugin settings to load styles into LAMMPS from plugins + from Axel Kohlmeyer (Temple U) +poems POEMS rigid-body integration package, POEMS package from Rudranarayan Mukherjee (RPI) python hooks to the system Python library, used by the PYTHON package from the LAMMPS development team -qmmm quantum mechanics/molecular mechanics coupling interface +qmmm quantum mechanics/molecular mechanics coupling interface from Axel Kohlmeyer (Temple U) quip interface to QUIP/libAtoms framework, USER-QUIP package from Albert Bartok-Partay and Gabor Csanyi (U Cambridge) smd hooks to Eigen library, used by USER-SMD package from Georg Ganzenmueller (Ernst Mach Institute, Germany) voronoi hooks to the Voro++ library, used by compute voronoi/atom command - from Daniel Schwen (LANL) + from Daniel Schwen (LANL) vtk hooks to the VTK library, used by dump custom/vtk command from Richard Berger (JKU) diff --git a/lib/plugin/README b/lib/plugin/README new file mode 100644 index 0000000000..15abea011d --- /dev/null +++ b/lib/plugin/README @@ -0,0 +1,16 @@ +This directory has a Makefile.lammps file with settings that allows +LAMMPS to dynamically link LAMMPS plugins. More details about this +are in the manual. + +In order to be able to dynamically load and execute the plugins from +inside LAMMPS, you need to link with a system library containing functions +like dlopen(), dlsym() and so on for dynamic linking of executable code +into an executable. This library is defined by setting the plugin_SYSLIB +variable in the Makefile.lammps file in this dir. For this mechanism +to work, LAMMPS must be built as a shared library (i.e. with mode=shared). + +For Linux and most current unix-like operating systems, this can be +kept at the default setting of "-ldl" (on some platforms this library +is called "-ldld"). The Windows platform is currently not supported. + +See the header of Makefile.lammps for more info. diff --git a/src/.gitignore b/src/.gitignore index 45ec71e485..6fa3aef513 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -172,6 +172,10 @@ /pair_lj_charmmfsw_coul_long.cpp /pair_lj_charmmfsw_coul_long.h +/plugin.cpp +/plugin.h +/lammpsplugin.h + /atom_vec_spin.cpp /atom_vec_spin.h /compute_spin.cpp diff --git a/src/MAKE/Makefile.mpi b/src/MAKE/Makefile.mpi index 68e79e9e8e..9776b0153e 100644 --- a/src/MAKE/Makefile.mpi +++ b/src/MAKE/Makefile.mpi @@ -13,7 +13,7 @@ DEPFLAGS = -M LINK = mpicxx LINKFLAGS = -g -O3 -LIB = -ldl +LIB = SIZE = size ARCHIVE = ar diff --git a/src/MAKE/Makefile.serial b/src/MAKE/Makefile.serial index 8b4e2e5982..0f5952f317 100644 --- a/src/MAKE/Makefile.serial +++ b/src/MAKE/Makefile.serial @@ -13,7 +13,7 @@ DEPFLAGS = -M LINK = g++ LINKFLAGS = -g -O -LIB = -ldl +LIB = SIZE = size ARCHIVE = ar diff --git a/src/Makefile b/src/Makefile index 679cbe7b97..a63c49e344 100644 --- a/src/Makefile +++ b/src/Makefile @@ -48,7 +48,7 @@ endif PACKAGE = asphere body class2 colloid compress coreshell dipole gpu \ granular kim kokkos kspace latte manybody mc message misc \ - mliap molecule mpiio mscg opt peri poems \ + mliap molecule mpiio mscg opt peri plugin poems \ python qeq replica rigid shock snap spin srd voronoi PACKUSER = user-adios user-atc user-awpmd user-bocs user-cgdna user-cgsdk user-colvars \ diff --git a/src/PLUGIN/Install.sh b/src/PLUGIN/Install.sh new file mode 100755 index 0000000000..0df642193e --- /dev/null +++ b/src/PLUGIN/Install.sh @@ -0,0 +1,64 @@ +# Install/unInstall package files in LAMMPS +# mode = 0/1/2 for uninstall/install/update + +mode=$1 + +# enforce using portable C locale +LC_ALL=C +export LC_ALL + +# arg1 = file, arg2 = file it depends on + +action () { + if (test $mode = 0) then + rm -f ../$1 + elif (! cmp -s $1 ../$1) then + if (test -z "$2" || test -e ../$2) then + cp $1 .. + if (test $mode = 2) then + echo " updating src/$1" + fi + fi + elif (test -n "$2") then + if (test ! -e ../$2) then + rm -f ../$1 + fi + fi +} + +# all package files with no dependencies + +for file in *.cpp *.h; do + test -f ${file} && action $file +done + +# edit 2 Makefile.package files to include/exclude package info + +if (test $1 = 1) then + + if (test -e ../Makefile.package) then + sed -i -e 's/[^ \t]*plugin[^ \t]* //' ../Makefile.package + sed -i -e 's|^PKG_SYSINC =[ \t]*|&$(plugin_SYSINC) |' ../Makefile.package + sed -i -e 's|^PKG_SYSLIB =[ \t]*|&$(plugin_SYSLIB) |' ../Makefile.package + sed -i -e 's|^PKG_SYSPATH =[ \t]*|&$(plugin_SYSPATH) |' ../Makefile.package + fi + + if (test -e ../Makefile.package.settings) then + sed -i -e '/^include.*plugin.*$/d' ../Makefile.package.settings + # multiline form needed for BSD sed on Macs + sed -i -e '4 i \ +include ..\/..\/lib\/plugin\/Makefile.lammps +' ../Makefile.package.settings + fi + +elif (test $1 = 0) then + + if (test -e ../Makefile.package) then + sed -i -e 's/[^ \t]*plugin[^ \t]* //' ../Makefile.package + fi + + if (test -e ../Makefile.package.settings) then + sed -i -e '/^include.*plugin.*$/d' ../Makefile.package.settings + fi + +fi diff --git a/src/PLUGIN/README b/src/PLUGIN/README new file mode 100644 index 0000000000..4515428e35 --- /dev/null +++ b/src/PLUGIN/README @@ -0,0 +1,12 @@ +This package provides a plugin command that can load LAMMPS +styles linked into shared object files into a LAMMPS binary +at run time without having to recompile and relink LAMMPS. +For more details please see the LAMMPS manual. + +To be able to dynamically load and execute the plugins from inside +LAMMPS, you need to link with an appropriate system library, which +is done using the settings in lib/plugin/Makefile.lammps. See +that file and the lib/plugin/README file for more details. + +The person who created this package is Axel Kohlmeyer at Temple U +(akohlmey at gmail.com). Contact him directly if you have questions. diff --git a/src/plugin.cpp b/src/PLUGIN/plugin.cpp similarity index 87% rename from src/plugin.cpp rename to src/PLUGIN/plugin.cpp index e252ce67e1..1449d0d90f 100644 --- a/src/plugin.cpp +++ b/src/PLUGIN/plugin.cpp @@ -38,9 +38,53 @@ namespace LAMMPS_NS // map for counting references to dso handles static std::map dso_refcounter; + +/* ---------------------------------------------------------------------- */ + + Plugin::Plugin(LAMMPS *lmp) : Pointers(lmp) {} + +/* ---------------------------------------------------------------------- */ + + void Plugin::command(int narg, char **arg) + { + if (narg < 1) error->all(FLERR,"Illegal plugin command"); + +#if defined(LMP_PLUGIN) + std::string cmd = arg[0]; + if (cmd == "load") { + if (narg < 2) error->all(FLERR,"Illegal plugin load command"); + for (int i=1; i < narg; ++i) + plugin_load(arg[i],lmp); + + } else if (cmd == "unload") { + if (narg != 3) error->all(FLERR,"Illegal plugin unload command"); + plugin_unload(arg[1],arg[2],lmp); + + } else if (cmd == "clear") { + plugin_clear(lmp); + + } else if (cmd == "list") { + if (comm->me == 0) { + int num = plugin_get_num_plugins(); + utils::logmesg(lmp,"Currently loaded plugins\n"); + for (int i=0; i < num; ++i) { + auto entry = plugin_get_info(i); + utils::logmesg(lmp,fmt::format("{:4}: {} style plugin {}\n", + i+1,entry->style,entry->name)); + } + } + } else error->all(FLERR,"Illegal plugin command"); +#else + if (comm->me == 0) + error->warning(FLERR,"Ignoring plugin command. LAMMPS must be built as " + "a shared library for it to work."); +#endif + } + // load DSO and call included registration function void plugin_load(const char *file, LAMMPS *lmp) { +#if defined(LMP_PLUGIN) int me = lmp->comm->me; #if defined(WIN32) lmp->error->all(FLERR,"Loading of plugins on Windows is not supported\n"); @@ -77,6 +121,7 @@ namespace LAMMPS_NS (*(lammpsplugin_initfunc)(initfunc))((void *)lmp, dso, (void *)&plugin_register); +#endif #endif } @@ -89,6 +134,7 @@ namespace LAMMPS_NS void plugin_register(lammpsplugin_t *plugin, void *ptr) { +#if defined(LMP_PLUGIN) LAMMPS *lmp = (LAMMPS *)ptr; int me = lmp->comm->me; @@ -158,6 +204,7 @@ namespace LAMMPS_NS "yet implemented\n",pstyle)); pluginlist.pop_back(); } +#endif } /* -------------------------------------------------------------------- @@ -168,6 +215,7 @@ namespace LAMMPS_NS void plugin_unload(const char *style, const char *name, LAMMPS *lmp) { +#if defined(LMP_PLUGIN) int me = lmp->comm->me; // ignore unload request from unsupported style categories @@ -246,6 +294,7 @@ namespace LAMMPS_NS dlclose(handle); #endif } +#endif } /* -------------------------------------------------------------------- diff --git a/src/plugin.h b/src/PLUGIN/plugin.h similarity index 84% rename from src/plugin.h rename to src/PLUGIN/plugin.h index 90952224a6..946b08db1a 100644 --- a/src/plugin.h +++ b/src/PLUGIN/plugin.h @@ -11,14 +11,26 @@ See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ +#ifdef COMMAND_CLASS + +CommandStyle(plugin,Plugin) + +#else + #ifndef LMP_PLUGIN_H #define LMP_PLUGIN_H #include "lammpsplugin.h" +#include "pointers.h" namespace LAMMPS_NS { - class LAMMPS; + + class Plugin : protected Pointers { + public: + Plugin(class LAMMPS *); + void command(int, char **); + }; void plugin_load(const char *, LAMMPS *); void plugin_register(lammpsplugin_t *, void *); @@ -33,3 +45,4 @@ namespace LAMMPS_NS } #endif +#endif diff --git a/src/input.cpp b/src/input.cpp index 9979bb7705..eeb211a6d1 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -35,7 +35,6 @@ #include "neighbor.h" #include "output.h" #include "pair.h" -#include "plugin.h" #include "special.h" #include "style_command.h" #include "thermo.h" @@ -718,7 +717,6 @@ int Input::execute_command() else if (!strcmp(command,"log")) log(); else if (!strcmp(command,"next")) next_command(); else if (!strcmp(command,"partition")) partition(); - else if (!strcmp(command,"plugin")) plugin(); else if (!strcmp(command,"print")) print(); else if (!strcmp(command,"python")) python(); else if (!strcmp(command,"quit")) quit(); @@ -1097,34 +1095,6 @@ void Input::partition() /* ---------------------------------------------------------------------- */ -void Input::plugin() -{ - if (narg < 1) error->all(FLERR,"Illegal plugin command"); - std::string cmd = arg[0]; - if (cmd == "load") { - if (narg < 2) error->all(FLERR,"Illegal plugin load command"); - for (int i=1; i < narg; ++i) - plugin_load(arg[i],lmp); - } else if (cmd == "unload") { - if (narg != 3) error->all(FLERR,"Illegal plugin unload command"); - plugin_unload(arg[1],arg[2],lmp); - } else if (cmd == "clear") { - plugin_clear(lmp); - } else if (cmd == "list") { - if (comm->me == 0) { - int num = plugin_get_num_plugins(); - utils::logmesg(lmp,"Currently loaded plugins\n"); - for (int i=0; i < num; ++i) { - auto entry = plugin_get_info(i); - utils::logmesg(lmp,fmt::format("{:4}: {} style plugin {}\n", - i+1,entry->style,entry->name)); - } - } - } else error->all(FLERR,"Illegal plugin command"); -} - -/* ---------------------------------------------------------------------- */ - void Input::print() { if (narg < 1) error->all(FLERR,"Illegal print command"); diff --git a/src/library.cpp b/src/library.cpp index 68ce180107..414d0f9f3a 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -38,7 +38,9 @@ #include "neighbor.h" #include "region.h" #include "output.h" +#if defined(LMP_PLUGIN) #include "plugin.h" +#endif #include "thermo.h" #include "timer.h" #include "universe.h" @@ -4590,7 +4592,11 @@ This function counts how many plugins are currently loaded. */ int lammps_plugin_count() { +#if defined(LMP_PLUGIN) return plugin_get_num_plugins(); +#else + return 0; +#endif } /* ---------------------------------------------------------------------- */ @@ -4617,6 +4623,7 @@ set to an empty string, otherwise 1. */ int lammps_plugin_name(int idx, char *stylebuf, char *namebuf, int buf_size) { +#if defined(LMP_PLUGIN) stylebuf[0] = namebuf[0] = '\0'; const lammpsplugin_t *plugin = plugin_get_info(idx); @@ -4625,6 +4632,7 @@ int lammps_plugin_name(int idx, char *stylebuf, char *namebuf, int buf_size) strncpy(namebuf,plugin->name,buf_size); return 1; } +#endif return 0; } diff --git a/unittest/commands/CMakeLists.txt b/unittest/commands/CMakeLists.txt index 50f85475d3..6fae0c2463 100644 --- a/unittest/commands/CMakeLists.txt +++ b/unittest/commands/CMakeLists.txt @@ -1,6 +1,6 @@ # build LAMMPS plugins, but not on Windows -if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") +if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" AND PKG_PLUGIN) ExternalProject_Add(plugins SOURCE_DIR "${LAMMPS_DIR}/examples/plugins" BINARY_DIR ${CMAKE_BINARY_DIR}/build-plugins diff --git a/unittest/commands/test_simple_commands.cpp b/unittest/commands/test_simple_commands.cpp index 448224f730..d98146d4f3 100644 --- a/unittest/commands/test_simple_commands.cpp +++ b/unittest/commands/test_simple_commands.cpp @@ -367,6 +367,7 @@ TEST_F(SimpleCommandsTest, Units) TEST_FAILURE(".*ERROR: Illegal units command.*", lmp->input->one("units unknown");); } +#if defined(LMP_PLUGIN) TEST_F(SimpleCommandsTest, Plugin) { #if defined(__APPLE__) @@ -437,6 +438,7 @@ TEST_F(SimpleCommandsTest, Plugin) if (verbose) std::cout << text; ASSERT_THAT(text, MatchesRegex(".*Currently loaded plugins.*")); } +#endif TEST_F(SimpleCommandsTest, Shell) { From 28e986c266580f87d1f100f22e309ea7f2d60c1e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 16 Mar 2021 23:25:46 -0400 Subject: [PATCH 47/57] add python module support for plugins --- python/lammps/core.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/python/lammps/core.py b/python/lammps/core.py index eaf78dfa0c..e75647fe44 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -264,6 +264,9 @@ class lammps(object): self.lib.lammps_id_name.argtypes = [c_void_p, c_char_p, c_int, c_char_p, c_int] + self.lib.lammps_plugin_count.argtypes = [ ] + self.lib.lammps_plugin_name.argtypes = [c_int, c_char_p, c_char_p, c_int] + self.lib.lammps_version.argtypes = [c_void_p] self.lib.lammps_get_os_info.argtypes = [c_char_p, c_int] @@ -1594,6 +1597,29 @@ class lammps(object): # ------------------------------------------------------------------------- + def available_plugins(self, category): + """Returns a list of plugins available for a given category + + This is a wrapper around the functions :cpp:func:`lammps_plugin_count()` + and :cpp:func:`lammps_plugin_name()` of the library interface. + + .. versionadded:: 10Mar2021 + + :return: list of style/name pairs of loaded plugins + :rtype: list + """ + + available_plugins = [] + num = self.lib.lammps_plugin_count(self.lmp) + sty = create_string_buffer(100) + nam = create_string_buffer(100) + for idx in range(num): + self.lib.lammps_plugin_name(idx, sty, nam, 100) + available_plugins.append([sty.value.decode(), nam.value.decode()]) + return available_plugins + + # ------------------------------------------------------------------------- + def set_fix_external_callback(self, fix_name, callback, caller=None): import numpy as np From 78126c5eb3f9f9cd961901301f435c0fdef7fed7 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 16 Mar 2021 23:32:08 -0400 Subject: [PATCH 48/57] fix cmake unit test issue --- unittest/commands/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unittest/commands/CMakeLists.txt b/unittest/commands/CMakeLists.txt index 6fae0c2463..a658c8a25d 100644 --- a/unittest/commands/CMakeLists.txt +++ b/unittest/commands/CMakeLists.txt @@ -23,7 +23,9 @@ if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" AND PKG_PLUGIN) endif() add_executable(test_simple_commands test_simple_commands.cpp) -add_dependencies(test_simple_commands plugins) +if(PKG_PLUGIN) + add_dependencies(test_simple_commands plugins) +endif() target_link_libraries(test_simple_commands PRIVATE lammps GTest::GMock GTest::GTest) add_test(NAME SimpleCommands COMMAND test_simple_commands WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) From 2a6fcee5e00b70dafa49b5992507a1eaa721f797 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 16 Mar 2021 23:42:06 -0400 Subject: [PATCH 49/57] add missing file (was ignored by default) --- lib/plugin/Makefile.lammps | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 lib/plugin/Makefile.lammps diff --git a/lib/plugin/Makefile.lammps b/lib/plugin/Makefile.lammps new file mode 100644 index 0000000000..87f4f8a771 --- /dev/null +++ b/lib/plugin/Makefile.lammps @@ -0,0 +1,16 @@ +# This file contains the hooks to build and link LAMMPS so it can load plugins +# +# The plugin_SYSINC and plugin_SYSPATH variables do not typically need +# to be set. If the dl library is not in a place the linker can find +# it, specify its directory via the plugin_SYSPATH variable, e.g. +# -Ldir. + +# ----------------------------------------------------------- + +# Settings that the LAMMPS build will import when this package is installed + +ifeq ($(mode),shared) +plugin_SYSINC = -DLMP_PLUGIN +endif +plugin_SYSLIB = -ldl +plugin_SYSPATH = From ddc77be911e2f61967a1db4685be1c405015d32e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 16 Mar 2021 23:52:44 -0400 Subject: [PATCH 50/57] update docs for converting the plugin feature to a package --- doc/src/Packages_details.rst | 23 +++++++++++++++++++++++ doc/src/plugin.rst | 5 ++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/doc/src/Packages_details.rst b/doc/src/Packages_details.rst index e858593c4c..301e6bfd4a 100644 --- a/doc/src/Packages_details.rst +++ b/doc/src/Packages_details.rst @@ -50,6 +50,7 @@ page gives those details. * :ref:`MSCG ` * :ref:`OPT ` * :ref:`PERI ` + * :ref:`PLUGIN ` * :ref:`POEMS ` * :ref:`PYTHON ` * :ref:`QEQ ` @@ -843,6 +844,28 @@ Foster (UTSA). ---------- +.. _PKG-PLUGIN: + +PLUGIN package +-------------- + +**Contents:** + +A :doc:`plugin ` command that can load and unload several +kind of styles in LAMMPS from shared object files at runtime without +having to recompile and relink LAMMPS. + +**Authors:** Axel Kohlmeyer (Temple U) + +**Supporting info:** + +* src/PLUGIN: filenames -> commands +* :doc:`plugin command ` +* :doc:`Information on writing plugins ` +* examples/plugin + +---------- + .. _PKG-POEMS: POEMS package diff --git a/doc/src/plugin.rst b/doc/src/plugin.rst index e8a5f67e3c..48638bf97a 100644 --- a/doc/src/plugin.rst +++ b/doc/src/plugin.rst @@ -60,7 +60,10 @@ The *clear* command will unload all currently loaded plugins. Restrictions """""""""""" -Plugins are not available on Windows. +The *plugin* command is part of the PLUGIN package. It is +only enabled if LAMMPS was built with that package. +See the :doc:`Build package ` doc page for +more info. Plugins are not available on Windows. For the loading of plugins to work the LAMMPS library must be :ref:`compiled as a shared library `. If plugins From 9b29b1594bd35f32a1d728e6c8d32a355c324665 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 18 Mar 2021 14:40:41 -0400 Subject: [PATCH 51/57] add support for bond/angle/dihedral/improper plugins --- examples/plugins/CMakeLists.txt | 8 +- examples/plugins/Makefile | 28 +--- examples/plugins/Makefile.common | 36 ++++ examples/plugins/Makefile.macos | 30 +--- examples/plugins/Makefile.serial | 28 +--- examples/plugins/angle_zero2.cpp | 152 +++++++++++++++++ examples/plugins/angle_zero2.h | 57 +++++++ examples/plugins/bond_zero2.cpp | 161 ++++++++++++++++++ examples/plugins/bond_zero2.h | 58 +++++++ examples/plugins/dihedral_zero2.cpp | 121 ++++++++++++++ examples/plugins/dihedral_zero2.h | 57 +++++++ examples/plugins/improper_zero2.cpp | 122 ++++++++++++++ examples/plugins/improper_zero2.h | 53 ++++++ examples/plugins/pair_zero2.cpp | 247 ++++++++++++++++++++++++++++ examples/plugins/pair_zero2.h | 77 +++++++++ examples/plugins/zero2plugin.cpp | 78 +++++++++ src/PLUGIN/plugin.cpp | 94 ++++++++++- 17 files changed, 1323 insertions(+), 84 deletions(-) create mode 100644 examples/plugins/Makefile.common create mode 100644 examples/plugins/angle_zero2.cpp create mode 100644 examples/plugins/angle_zero2.h create mode 100644 examples/plugins/bond_zero2.cpp create mode 100644 examples/plugins/bond_zero2.h create mode 100644 examples/plugins/dihedral_zero2.cpp create mode 100644 examples/plugins/dihedral_zero2.h create mode 100644 examples/plugins/improper_zero2.cpp create mode 100644 examples/plugins/improper_zero2.h create mode 100644 examples/plugins/pair_zero2.cpp create mode 100644 examples/plugins/pair_zero2.h create mode 100644 examples/plugins/zero2plugin.cpp diff --git a/examples/plugins/CMakeLists.txt b/examples/plugins/CMakeLists.txt index 74a1cd4e3b..6e29676530 100644 --- a/examples/plugins/CMakeLists.txt +++ b/examples/plugins/CMakeLists.txt @@ -53,12 +53,16 @@ target_link_libraries(nve2plugin PRIVATE lammps) add_library(helloplugin MODULE helloplugin.cpp) target_link_libraries(helloplugin PRIVATE lammps) -set_target_properties(morse2plugin nve2plugin helloplugin PROPERTIES +add_library(zero2plugin MODULE zero2plugin.cpp pair_zero2.cpp bond_zero2.cpp + angle_zero2.cpp dihedral_zero2.cpp improper_zero2.cpp) +target_link_libraries(zero2plugin PRIVATE lammps) + +set_target_properties(morse2plugin nve2plugin helloplugin zero2plugin PROPERTIES PREFIX "" LINK_FLAGS "-rdynamic") # MacOS seems to need this if(CMAKE_SYSTEM_NAME STREQUAL Darwin) - set_target_properties(morse2plugin nve2plugin helloplugin PROPERTIES + set_target_properties(morse2plugin nve2plugin helloplugin zero2plugin PROPERTIES LINK_FLAGS "-Wl,-undefined,dynamic_lookup") endif() diff --git a/examples/plugins/Makefile b/examples/plugins/Makefile index dbb0023991..f4d8b41086 100644 --- a/examples/plugins/Makefile +++ b/examples/plugins/Makefile @@ -1,30 +1,6 @@ CXX=mpicxx CXXFLAGS=-I../../src -Wall -Wextra -O3 -fPIC -I../../src/USER-OMP -fopenmp LD=$(CXX) -shared -rdynamic -fopenmp +DSOEXT=.so -default: morse2plugin.so nve2plugin.so helloplugin.so - -helloplugin.so: helloplugin.o - $(LD) -o $@ $^ - -morse2plugin.so: morse2plugin.o pair_morse2.o pair_morse2_omp.o - $(LD) -o $@ $^ - -nve2plugin.so: nve2plugin.o fix_nve2.o - $(LD) -o $@ $^ - -.cpp.o: - $(CXX) -o $@ $(CXXFLAGS) -c $< - -helloplugin.o: helloplugin.cpp - -pair_morse2.o: pair_morse2.cpp pair_morse2.h -pair_morse2_omp.o: pair_morse2_omp.cpp pair_morse2_omp.h pair_morse2.h -morse2plugin.o: morse2plugin.cpp pair_morse2.h pair_morse2_omp.h - -fix_nve2.o: fix_nve2.cpp fix_nve2.h -nve2plugin.o: nve2plugin.cpp fix_nve2.h - -clean: - rm -rf *~ *.so *.dylib *.o log.lammps CMakeCache.txt CMakeFiles - +include Makefile.common diff --git a/examples/plugins/Makefile.common b/examples/plugins/Makefile.common new file mode 100644 index 0000000000..e78fa13feb --- /dev/null +++ b/examples/plugins/Makefile.common @@ -0,0 +1,36 @@ +default: morse2plugin$(DSOEXT) nve2plugin$(DSOEXT) helloplugin$(DSOEXT) zero2plugin$(DSOEXT) + +helloplugin$(DSOEXT): helloplugin.o + $(LD) -o $@ $^ + +morse2plugin$(DSOEXT): morse2plugin.o pair_morse2.o pair_morse2_omp.o + $(LD) -o $@ $^ + +nve2plugin$(DSOEXT): nve2plugin.o fix_nve2.o + $(LD) -o $@ $^ + +zero2plugin$(DSOEXT): zero2plugin.o pair_zero2.o bond_zero2.o angle_zero2.o dihedral_zero2.o improper_zero2.o + $(LD) -o $@ $^ + +.cpp.o: + $(CXX) -o $@ $(CXXFLAGS) -c $< + +helloplugin.o: helloplugin.cpp + +pair_morse2.o: pair_morse2.cpp pair_morse2.h +pair_morse2_omp.o: pair_morse2_omp.cpp pair_morse2_omp.h pair_morse2.h +morse2plugin.o: morse2plugin.cpp pair_morse2.h pair_morse2_omp.h + +fix_nve2.o: fix_nve2.cpp fix_nve2.h +nve2plugin.o: nve2plugin.cpp fix_nve2.h + +pair_zero2.o: pair_zero2.cpp pair_zero2.h +bond_zero2.o: bond_zero2.cpp bond_zero2.h +angle_zero2.o: angle_zero2.cpp angle_zero2.h +dihedral_zero2.o: dihedral_zero2.cpp dihedral_zero2.h +improper_zero2.o: improper_zero2.cpp improper_zero2.h +zero2plugin.o: zero2plugin.cpp pair_zero2.h bond_zero2.h angle_zero2.h dihedral_zero2.h + +clean: + rm -rf *~ *.so *.dylib *.o log.lammps CMakeCache.txt CMakeFiles + diff --git a/examples/plugins/Makefile.macos b/examples/plugins/Makefile.macos index 2490418a09..a7c20ff90f 100644 --- a/examples/plugins/Makefile.macos +++ b/examples/plugins/Makefile.macos @@ -1,30 +1,6 @@ CXX=mpicxx -CXXFLAGS=-I../../src -Wall -Wextra -O3 -fPIC -I../../src/USER-OMP +CXXFLAGS=-I../../src -Wall -Wextra -O3 -fPIC -I../../src/USER-OMP LD=$(CXX) -bundle -rdynamic -Wl,-undefined,dynamic_lookup +DSOEXT=.dylib -default: morse2plugin.dylib nve2plugin.dylib helloplugin.dylib - -helloplugin.dylib: helloplugin.o - $(LD) -o $@ $^ - -morse2plugin.dylib: morse2plugin.o pair_morse2.o pair_morse2_omp.o - $(LD) -o $@ $^ - -nve2plugin.dylib: nve2plugin.o fix_nve2.o - $(LD) -o $@ $^ - -.cpp.o: - $(CXX) -o $@ $(CXXFLAGS) -c $< - -helloplugin.o: helloplugin.cpp - -pair_morse2.o: pair_morse2.cpp pair_morse2.h -pair_morse2_omp.o: pair_morse2_omp.cpp pair_morse2_omp.h pair_morse2.h -morse2plugin.o: morse2plugin.cpp pair_morse2.h pair_morse2_omp.h - -fix_nve2.o: fix_nve2.cpp fix_nve2.h -nve2plugin.o: nve2plugin.cpp fix_nve2.h - -clean: - rm -rf *~ *.dylib *.dylib *.o log.lammps CMakeCache.txt CMakeFiles - +include Makefile.common diff --git a/examples/plugins/Makefile.serial b/examples/plugins/Makefile.serial index feb58bd9b3..ecc7631a05 100644 --- a/examples/plugins/Makefile.serial +++ b/examples/plugins/Makefile.serial @@ -1,30 +1,6 @@ CXX=g++ CXXFLAGS=-I../../src -I../../src/STUBS -Wall -Wextra -O3 -fPIC -I../../src/USER-OMP -fopenmp LD=$(CXX) -shared -rdynamic -fopenmp +DSOEXT=.so -default: morse2plugin.so nve2plugin.so helloplugin.so - -helloplugin.so: helloplugin.o - $(LD) -o $@ $^ - -morse2plugin.so: morse2plugin.o pair_morse2.o pair_morse2_omp.o - $(LD) -o $@ $^ - -nve2plugin.so: nve2plugin.o fix_nve2.o - $(LD) -o $@ $^ - -.cpp.o: - $(CXX) -o $@ $(CXXFLAGS) -c $< - -helloplugin.o: helloplugin.cpp - -pair_morse2.o: pair_morse2.cpp pair_morse2.h -pair_morse2_omp.o: pair_morse2_omp.cpp pair_morse2_omp.h pair_morse2.h -morse2plugin.o: morse2plugin.cpp pair_morse2.h pair_morse2_omp.h - -fix_nve2.o: fix_nve2.cpp fix_nve2.h -nve2plugin.o: nve2plugin.cpp fix_nve2.h - -clean: - rm -rf *~ *.so *.dylib *.o log.lammps CMakeCache.txt CMakeFiles - +include Makefile.common diff --git a/examples/plugins/angle_zero2.cpp b/examples/plugins/angle_zero2.cpp new file mode 100644 index 0000000000..c0d01f14a5 --- /dev/null +++ b/examples/plugins/angle_zero2.cpp @@ -0,0 +1,152 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://lammps.sandia.gov/, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Carsten Svaneborg (SDU) +------------------------------------------------------------------------- */ + +#include "angle_zero2.h" + +#include "atom.h" +#include "comm.h" +#include "math_const.h" +#include "memory.h" +#include "error.h" + +#include + +using namespace LAMMPS_NS; +using namespace MathConst; + +/* ---------------------------------------------------------------------- */ + +AngleZero2::AngleZero2(LAMMPS *lmp) : Angle(lmp), coeffflag(1) {} + +/* ---------------------------------------------------------------------- */ + +AngleZero2::~AngleZero2() +{ + if (allocated && !copymode) { + memory->destroy(setflag); + memory->destroy(theta0); + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleZero2::compute(int eflag, int vflag) +{ + ev_init(eflag,vflag); +} + +/* ---------------------------------------------------------------------- */ + +void AngleZero2::settings(int narg, char **arg) +{ + if ((narg != 0) && (narg != 1)) + error->all(FLERR,"Illegal angle_style command"); + + if (narg == 1) { + if (strcmp("nocoeff",arg[0]) == 0) coeffflag=0; + else error->all(FLERR,"Illegal angle_style command"); + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleZero2::allocate() +{ + allocated = 1; + int n = atom->nangletypes; + + memory->create(theta0,n+1,"angle:theta0"); + memory->create(setflag,n+1,"angle:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more types +------------------------------------------------------------------------- */ + +void AngleZero2::coeff(int narg, char **arg) +{ + if ((narg < 1) || (coeffflag && narg > 2)) + error->all(FLERR,"Incorrect args for angle coefficients"); + + if (!allocated) allocate(); + + int ilo,ihi; + utils::bounds(FLERR,arg[0],1,atom->nangletypes,ilo,ihi,error); + + double theta0_one = 0.0; + if (coeffflag && (narg == 2)) + theta0_one = utils::numeric(FLERR,arg[1],false,lmp); + + // convert theta0 from degrees to radians + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + setflag[i] = 1; + theta0[i] = theta0_one/180.0 * MY_PI; + count++; + } + + if (count == 0) error->all(FLERR,"Incorrect args for angle coefficients"); +} + +/* ---------------------------------------------------------------------- */ + +double AngleZero2::equilibrium_angle(int i) +{ + return theta0[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void AngleZero2::write_restart(FILE *fp) { + fwrite(&theta0[1],sizeof(double),atom->nangletypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void AngleZero2::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + utils::sfread(FLERR,&theta0[1],sizeof(double),atom->nangletypes,fp,nullptr,error); + } + MPI_Bcast(&theta0[1],atom->nangletypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nangletypes; i++) setflag[i] = 1; +} +/* ---------------------------------------------------------------------- + proc 0 writes to data file +------------------------------------------------------------------------- */ + +void AngleZero2::write_data(FILE *fp) +{ + for (int i = 1; i <= atom->nangletypes; i++) + fprintf(fp,"%d %g\n",i,theta0[i]/MY_PI*180.0); +} + +/* ---------------------------------------------------------------------- */ + +double AngleZero2::single(int /*type*/, int /*i1*/, int /*i2*/, int /*i3*/) +{ + return 0.0; +} diff --git a/examples/plugins/angle_zero2.h b/examples/plugins/angle_zero2.h new file mode 100644 index 0000000000..15a16b197f --- /dev/null +++ b/examples/plugins/angle_zero2.h @@ -0,0 +1,57 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef LMP_ANGLE_ZERO2_H +#define LMP_ANGLE_ZERO2_H + +#include "angle.h" + +namespace LAMMPS_NS { + +class AngleZero2 : public Angle { + public: + AngleZero2(class LAMMPS *); + virtual ~AngleZero2(); + virtual void compute(int, int); + virtual void coeff(int, char **); + virtual void settings(int, char **); + + double equilibrium_angle(int); + void write_restart(FILE *); + void read_restart(FILE *); + void write_data(FILE *); + + double single(int, int, int, int); + + protected: + double *theta0; + int coeffflag; + + void allocate(); +}; + +} + +#endif + +/* ERROR/WARNING messages: + +E: Illegal ... command + +UNDOCUMENTED + +E: Incorrect args for angle coefficients + +Self-explanatory. Check the input script or data file. + +*/ diff --git a/examples/plugins/bond_zero2.cpp b/examples/plugins/bond_zero2.cpp new file mode 100644 index 0000000000..b015a60ed3 --- /dev/null +++ b/examples/plugins/bond_zero2.cpp @@ -0,0 +1,161 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://lammps.sandia.gov/, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Carsten Svaneborg (SDU) +------------------------------------------------------------------------- */ + +#include "bond_zero2.h" + +#include "atom.h" +#include "comm.h" +#include "error.h" +#include "memory.h" + +#include + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +BondZero2::BondZero2(LAMMPS *lmp) : Bond(lmp), coeffflag(1) {} + +/* ---------------------------------------------------------------------- */ + +BondZero2::~BondZero2() +{ + if (allocated && !copymode) { + memory->destroy(setflag); + memory->destroy(r0); + } +} + +/* ---------------------------------------------------------------------- */ + +void BondZero2::compute(int eflag, int vflag) +{ + ev_init(eflag,vflag); +} + +/* ---------------------------------------------------------------------- */ + +void BondZero2::settings(int narg, char **arg) +{ + if ((narg != 0) && (narg != 1)) + error->all(FLERR,"Illegal bond_style command"); + + if (narg == 1) { + if (strcmp("nocoeff",arg[0]) == 0) coeffflag=0; + else error->all(FLERR,"Illegal bond_style command"); + } +} + +/* ---------------------------------------------------------------------- */ + +void BondZero2::allocate() +{ + allocated = 1; + int n = atom->nbondtypes; + + memory->create(r0,n+1,"bond:r0"); + memory->create(setflag,n+1,"bond:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more types +------------------------------------------------------------------------- */ + +void BondZero2::coeff(int narg, char **arg) +{ + if ((narg < 1) || (coeffflag && narg > 2)) + error->all(FLERR,"Incorrect args for bond coefficients"); + + if (!allocated) allocate(); + + int ilo,ihi; + utils::bounds(FLERR,arg[0],1,atom->nbondtypes,ilo,ihi,error); + + double r0_one = 0.0; + if (coeffflag && (narg == 2)) + r0_one = utils::numeric(FLERR,arg[1],false,lmp); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + setflag[i] = 1; + r0[i] = r0_one; + count++; + } + + if (count == 0) error->all(FLERR,"Incorrect args for bond coefficients"); +} + +/* ---------------------------------------------------------------------- + return an equilbrium bond length +------------------------------------------------------------------------- */ + +double BondZero2::equilibrium_distance(int i) +{ + return r0[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void BondZero2::write_restart(FILE *fp) { + fwrite(&r0[1],sizeof(double),atom->nbondtypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void BondZero2::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + utils::sfread(FLERR,&r0[1],sizeof(double),atom->nbondtypes,fp,nullptr,error); + } + MPI_Bcast(&r0[1],atom->nbondtypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nbondtypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to data file +------------------------------------------------------------------------- */ + +void BondZero2::write_data(FILE *fp) +{ + for (int i = 1; i <= atom->nbondtypes; i++) + fprintf(fp,"%d %g\n",i,r0[i]); +} + +/* ---------------------------------------------------------------------- */ + +double BondZero2::single(int /*type*/, double /*rsq*/, int /*i*/, int /*j*/, + double & /*fforce*/) +{ + return 0.0; +} + +/* ---------------------------------------------------------------------- */ + +void *BondZero2::extract(const char *str, int &dim) +{ + dim = 1; + if (strcmp(str,"r0")==0) return (void*) r0; + return nullptr; +} diff --git a/examples/plugins/bond_zero2.h b/examples/plugins/bond_zero2.h new file mode 100644 index 0000000000..37609561a7 --- /dev/null +++ b/examples/plugins/bond_zero2.h @@ -0,0 +1,58 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef LMP_BOND_ZERO2_H +#define LMP_BOND_ZERO2_H + +#include "bond.h" + +namespace LAMMPS_NS { + +class BondZero2 : public Bond { + public: + BondZero2(class LAMMPS *); + virtual ~BondZero2(); + virtual void compute(int, int); + virtual void settings(int, char **); + + void coeff(int, char **); + double equilibrium_distance(int); + void write_restart(FILE *); + void read_restart(FILE *); + void write_data(FILE *); + + double single(int, double, int, int, double &); + virtual void *extract(const char *, int &); + + protected: + double *r0; + int coeffflag; + + virtual void allocate(); +}; + +} + +#endif + +/* ERROR/WARNING messages: + +E: Illegal ... command + +UNDOCUMENTED + +E: Incorrect args for bond coefficients + +Self-explanatory. Check the input script or data file. + +*/ diff --git a/examples/plugins/dihedral_zero2.cpp b/examples/plugins/dihedral_zero2.cpp new file mode 100644 index 0000000000..00d9817c96 --- /dev/null +++ b/examples/plugins/dihedral_zero2.cpp @@ -0,0 +1,121 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://lammps.sandia.gov/, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Carsten Svaneborg (SDU) +------------------------------------------------------------------------- */ + +#include "dihedral_zero2.h" + +#include "atom.h" +#include "error.h" +#include "memory.h" + +#include + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +DihedralZero2::DihedralZero2(LAMMPS *lmp) : Dihedral(lmp), coeffflag(1) +{ + writedata = 1; +} + +/* ---------------------------------------------------------------------- */ + +DihedralZero2::~DihedralZero2() +{ + if (allocated && !copymode) { + memory->destroy(setflag); + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralZero2::compute(int eflag, int vflag) +{ + ev_init(eflag,vflag); +} + +/* ---------------------------------------------------------------------- */ + +void DihedralZero2::settings(int narg, char **arg) +{ + if ((narg != 0) && (narg != 1)) + error->all(FLERR,"Illegal dihedral_style command"); + + if (narg == 1) { + if (strcmp("nocoeff",arg[0]) == 0) coeffflag=0; + else error->all(FLERR,"Illegal dihedral_style command"); + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralZero2::allocate() +{ + allocated = 1; + int n = atom->ndihedraltypes; + + memory->create(setflag,n+1,"dihedral:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more types +------------------------------------------------------------------------- */ + +void DihedralZero2::coeff(int narg, char **arg) +{ + if ((narg < 1) || (coeffflag && narg > 1)) + error->all(FLERR,"Incorrect args for dihedral coefficients"); + + if (!allocated) allocate(); + + int ilo,ihi; + utils::bounds(FLERR,arg[0],1,atom->ndihedraltypes,ilo,ihi,error); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + setflag[i] = 1; + count++; + } + + if (count == 0) error->all(FLERR,"Incorrect args for dihedral coefficients"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void DihedralZero2::write_restart(FILE * /*fp*/) {} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void DihedralZero2::read_restart(FILE * /*fp*/) +{ + allocate(); + for (int i = 1; i <= atom->ndihedraltypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to data file +------------------------------------------------------------------------- */ + +void DihedralZero2::write_data(FILE *fp) { + for (int i = 1; i <= atom->ndihedraltypes; i++) + fprintf(fp,"%d\n",i); +} diff --git a/examples/plugins/dihedral_zero2.h b/examples/plugins/dihedral_zero2.h new file mode 100644 index 0000000000..510e384340 --- /dev/null +++ b/examples/plugins/dihedral_zero2.h @@ -0,0 +1,57 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. + + Identical to dihedral harmonic, except if all k's are zero the + force loop is skipped. + +------------------------------------------------------------------------- */ + +#ifndef LMP_DIHEDRAL_ZERO2_H +#define LMP_DIHEDRAL_ZERO2_H + +#include "dihedral.h" + +namespace LAMMPS_NS { + +class DihedralZero2 : public Dihedral { + public: + DihedralZero2(class LAMMPS *); + virtual ~DihedralZero2(); + virtual void compute(int, int); + virtual void coeff(int, char **); + virtual void settings(int, char **); + + void write_restart(FILE *); + void read_restart(FILE *); + void write_data(FILE *); + + protected: + int coeffflag; + + virtual void allocate(); +}; + +} + +#endif + +/* ERROR/WARNING messages: + +E: Illegal ... command + +UNDOCUMENTED + +E: Incorrect args for dihedral coefficients + +UNDOCUMENTED + +*/ diff --git a/examples/plugins/improper_zero2.cpp b/examples/plugins/improper_zero2.cpp new file mode 100644 index 0000000000..2370b32a02 --- /dev/null +++ b/examples/plugins/improper_zero2.cpp @@ -0,0 +1,122 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://lammps.sandia.gov/, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Carsten Svaneborg (SDU) +------------------------------------------------------------------------- */ + +#include "improper_zero2.h" + +#include "atom.h" +#include "error.h" +#include "memory.h" + +#include + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +ImproperZero2::ImproperZero2(LAMMPS *lmp) : Improper(lmp), coeffflag(1) +{ + writedata = 1; +} + +/* ---------------------------------------------------------------------- */ + +ImproperZero2::~ImproperZero2() +{ + if (allocated && !copymode) { + memory->destroy(setflag); + } +} + +/* ---------------------------------------------------------------------- */ + +void ImproperZero2::compute(int eflag, int vflag) +{ + ev_init(eflag,vflag); +} + +/* ---------------------------------------------------------------------- */ + +void ImproperZero2::settings(int narg, char **arg) +{ + if ((narg != 0) && (narg != 1)) + error->all(FLERR,"Illegal improper_style command"); + + if (narg == 1) { + if (strcmp("nocoeff",arg[0]) == 0) coeffflag=0; + else error->all(FLERR,"Illegal improper_style command"); + } +} + +/* ---------------------------------------------------------------------- */ + +void ImproperZero2::allocate() +{ + allocated = 1; + int n = atom->nimpropertypes; + + memory->create(setflag,n+1,"improper:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more types +------------------------------------------------------------------------- */ + +void ImproperZero2::coeff(int narg, char **arg) +{ + if ((narg < 1) || (coeffflag && narg > 1)) + error->all(FLERR,"Incorrect args for improper coefficients"); + + if (!allocated) allocate(); + + int ilo,ihi; + utils::bounds(FLERR,arg[0],1,atom->nimpropertypes,ilo,ihi,error); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + setflag[i] = 1; + count++; + } + + if (count == 0) error->all(FLERR,"Incorrect args for improper coefficients"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void ImproperZero2::write_restart(FILE * /*fp*/) {} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void ImproperZero2::read_restart(FILE * /*fp*/) +{ + allocate(); + for (int i = 1; i <= atom->nimpropertypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to data file +------------------------------------------------------------------------- */ + +void ImproperZero2::write_data(FILE *fp) { + for (int i = 1; i <= atom->nimpropertypes; i++) + fprintf(fp,"%d\n",i); +} + diff --git a/examples/plugins/improper_zero2.h b/examples/plugins/improper_zero2.h new file mode 100644 index 0000000000..64dd94920a --- /dev/null +++ b/examples/plugins/improper_zero2.h @@ -0,0 +1,53 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef LMP_IMPROPER_ZERO2_H +#define LMP_IMPROPER_ZERO2_H + +#include "improper.h" + +namespace LAMMPS_NS { + +class ImproperZero2 : public Improper { + public: + ImproperZero2(class LAMMPS *); + virtual ~ImproperZero2(); + virtual void compute(int, int); + virtual void coeff(int, char **); + virtual void settings(int, char **); + + void write_restart(FILE *); + void read_restart(FILE *); + void write_data(FILE *); + + protected: + int coeffflag; + + virtual void allocate(); +}; + +} + +#endif + +/* ERROR/WARNING messages: + +E: Illegal ... command + +UNDOCUMENTED + +E: Incorrect args for improper coefficients + +Self-explanatory. Check the input script or data file. + +*/ diff --git a/examples/plugins/pair_zero2.cpp b/examples/plugins/pair_zero2.cpp new file mode 100644 index 0000000000..d8e23c902d --- /dev/null +++ b/examples/plugins/pair_zero2.cpp @@ -0,0 +1,247 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://lammps.sandia.gov/, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Carsten Svaneborg (SDU) +------------------------------------------------------------------------- */ + +#include "pair_zero2.h" + +#include "atom.h" +#include "comm.h" +#include "memory.h" +#include "error.h" + +#include + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +PairZero2::PairZero2(LAMMPS *lmp) : Pair(lmp) { + coeffflag=1; + writedata=1; + single_enable=1; + respa_enable=1; +} + +/* ---------------------------------------------------------------------- */ + +PairZero2::~PairZero2() +{ + if (allocated) { + memory->destroy(setflag); + memory->destroy(cutsq); + memory->destroy(cut); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairZero2::compute(int eflag, int vflag) +{ + ev_init(eflag,vflag); + if (vflag_fdotr) virial_fdotr_compute(); +} + +/* ---------------------------------------------------------------------- */ + +void PairZero2::compute_outer(int eflag, int vflag) +{ + ev_init(eflag,vflag); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairZero2::allocate() +{ + allocated = 1; + int n = atom->ntypes; + + memory->create(setflag,n+1,n+1,"pair:setflag"); + for (int i = 1; i <= n; i++) + for (int j = i; j <= n; j++) + setflag[i][j] = 0; + + memory->create(cutsq,n+1,n+1,"pair:cutsq"); + memory->create(cut,n+1,n+1,"pair:cut"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairZero2::settings(int narg, char **arg) +{ + if ((narg != 1) && (narg != 2)) + error->all(FLERR,"Illegal pair_style command"); + + cut_global = utils::numeric(FLERR,arg[0],false,lmp); + if (narg == 2) { + if (strcmp("nocoeff",arg[1]) == 0) coeffflag=0; + else error->all(FLERR,"Illegal pair_style command"); + } + + // reset cutoffs that have been explicitly set + + if (allocated) { + int i,j; + for (i = 1; i <= atom->ntypes; i++) + for (j = i+1; j <= atom->ntypes; j++) + cut[i][j] = cut_global; + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairZero2::coeff(int narg, char **arg) +{ + if ((narg < 2) || (coeffflag && narg > 3)) + error->all(FLERR,"Incorrect args for pair coefficients"); + + if (!allocated) allocate(); + + int ilo,ihi,jlo,jhi; + utils::bounds(FLERR,arg[0],1,atom->ntypes,ilo,ihi,error); + utils::bounds(FLERR,arg[1],1,atom->ntypes,jlo,jhi,error); + + double cut_one = cut_global; + if (coeffflag && (narg == 3)) cut_one = utils::numeric(FLERR,arg[2],false,lmp); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + for (int j = MAX(jlo,i); j <= jhi; j++) { + cut[i][j] = cut_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all(FLERR,"Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairZero2::init_one(int i, int j) +{ + if (setflag[i][j] == 0) { + cut[i][j] = mix_distance(cut[i][i],cut[j][j]); + } + + return cut[i][j]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairZero2::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + for (j = i; j <= atom->ntypes; j++) { + fwrite(&setflag[i][j],sizeof(int),1,fp); + if (setflag[i][j]) { + fwrite(&cut[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairZero2::read_restart(FILE *fp) +{ + read_restart_settings(fp); + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + for (j = i; j <= atom->ntypes; j++) { + if (me == 0) utils::sfread(FLERR,&setflag[i][j],sizeof(int),1,fp,nullptr,error); + MPI_Bcast(&setflag[i][j],1,MPI_INT,0,world); + if (setflag[i][j]) { + if (me == 0) { + utils::sfread(FLERR,&cut[i][j],sizeof(double),1,fp,nullptr,error); + } + MPI_Bcast(&cut[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairZero2::write_restart_settings(FILE *fp) +{ + fwrite(&cut_global,sizeof(double),1,fp); + fwrite(&coeffflag,sizeof(int),1,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairZero2::read_restart_settings(FILE *fp) +{ + int me = comm->me; + if (me == 0) { + utils::sfread(FLERR,&cut_global,sizeof(double),1,fp,nullptr,error); + utils::sfread(FLERR,&coeffflag,sizeof(int),1,fp,nullptr,error); + } + MPI_Bcast(&cut_global,1,MPI_DOUBLE,0,world); + MPI_Bcast(&coeffflag,1,MPI_INT,0,world); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to data file +------------------------------------------------------------------------- */ + +void PairZero2::write_data(FILE *fp) +{ + for (int i = 1; i <= atom->ntypes; i++) + fprintf(fp,"%d\n",i); +} + +/* ---------------------------------------------------------------------- + proc 0 writes all pairs to data file +------------------------------------------------------------------------- */ + +void PairZero2::write_data_all(FILE *fp) +{ + for (int i = 1; i <= atom->ntypes; i++) + for (int j = i; j <= atom->ntypes; j++) + fprintf(fp,"%d %d %g\n",i,j,cut[i][j]); +} + +/* ---------------------------------------------------------------------- */ + +double PairZero2::single(int /*i*/, int /*j*/, int /* itype */, int /* jtype */, + double /* rsq */, double /*factor_coul*/, + double /* factor_lj */, double &fforce) +{ + fforce = 0.0; + return 0.0; +} + diff --git a/examples/plugins/pair_zero2.h b/examples/plugins/pair_zero2.h new file mode 100644 index 0000000000..39aa160913 --- /dev/null +++ b/examples/plugins/pair_zero2.h @@ -0,0 +1,77 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. + + Pair zero is a dummy pair interaction useful for requiring a + force cutoff distance in the absence of pair-interactions or + with hybrid/overlay if a larger force cutoff distance is required. + + This can be used in conjunction with bond/create to create bonds + that are longer than the cutoff of a given force field, or to + calculate radial distribution functions for models without + pair interactions. + +------------------------------------------------------------------------- */ + +#ifndef LMP_PAIR_ZERO2_H +#define LMP_PAIR_ZERO2_H + +#include "pair.h" + +namespace LAMMPS_NS { + +class PairZero2 : public Pair { + public: + PairZero2(class LAMMPS *); + virtual ~PairZero2(); + virtual void compute(int, int); + virtual void compute_outer(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + void write_data(FILE *); + void write_data_all(FILE *); + double single(int, int, int, int, double, double, double, double &); + + protected: + double cut_global; + double **cut; + int coeffflag; + + virtual void allocate(); +}; + +} + +#endif + +/* ERROR/WARNING messages: + +E: Illegal ... command + +Self-explanatory. Check the input script syntax and compare to the +documentation for the command. You can use -echo screen as a +command-line option when running LAMMPS to see the offending line. + +E: Incorrect args for pair coefficients + +Self-explanatory. Check the input script or data file. + +U: Pair cutoff < Respa interior cutoff + +One or more pairwise cutoffs are too short to use with the specified +rRESPA cutoffs. + +*/ diff --git a/examples/plugins/zero2plugin.cpp b/examples/plugins/zero2plugin.cpp new file mode 100644 index 0000000000..f181eae781 --- /dev/null +++ b/examples/plugins/zero2plugin.cpp @@ -0,0 +1,78 @@ + +#include "lammpsplugin.h" +#include "version.h" + +#include + +#include "pair_zero2.h" +#include "bond_zero2.h" +#include "angle_zero2.h" +#include "dihedral_zero2.h" +#include "improper_zero2.h" + +using namespace LAMMPS_NS; + +static Pair *pairzerocreator(LAMMPS *lmp) +{ + return new PairZero2(lmp); +} + +static Bond *bondzerocreator(LAMMPS *lmp) +{ + return new BondZero2(lmp); +} + +static Angle *anglezerocreator(LAMMPS *lmp) +{ + return new AngleZero2(lmp); +} + +static Dihedral *dihedralzerocreator(LAMMPS *lmp) +{ + return new DihedralZero2(lmp); +} + +static Improper *improperzerocreator(LAMMPS *lmp) +{ + return new ImproperZero2(lmp); +} + +extern "C" void lammpsplugin_init(void *lmp, void *handle, void *regfunc) +{ + lammpsplugin_t plugin; + lammpsplugin_regfunc register_plugin = (lammpsplugin_regfunc) regfunc; + + // register zero2 pair style + plugin.version = LAMMPS_VERSION; + plugin.style = "pair"; + plugin.name = "zero2"; + plugin.info = "Zero2 variant pair style v1.0"; + plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; + plugin.creator.v1 = (lammpsplugin_factory1 *) &pairzerocreator; + plugin.handle = handle; + (*register_plugin)(&plugin,lmp); + + // register zero2 bond style + plugin.style = "bond"; + plugin.info = "Zero2 variant bond style v1.0"; + plugin.creator.v1 = (lammpsplugin_factory1 *) &bondzerocreator; + (*register_plugin)(&plugin,lmp); + + // register zero2 angle style + plugin.style = "angle"; + plugin.info = "Zero2 variant angle style v1.0"; + plugin.creator.v1 = (lammpsplugin_factory1 *) &anglezerocreator; + (*register_plugin)(&plugin,lmp); + + // register zero2 dihedral style + plugin.style = "dihedral"; + plugin.info = "Zero2 variant dihedral style v1.0"; + plugin.creator.v1 = (lammpsplugin_factory1 *) &dihedralzerocreator; + (*register_plugin)(&plugin,lmp); + + // register zero2 improper style + plugin.style = "improper"; + plugin.info = "Zero2 variant improper style v1.0"; + plugin.creator.v1 = (lammpsplugin_factory1 *) &improperzerocreator; + (*register_plugin)(&plugin,lmp); +} diff --git a/src/PLUGIN/plugin.cpp b/src/PLUGIN/plugin.cpp index 1449d0d90f..91d22b2897 100644 --- a/src/PLUGIN/plugin.cpp +++ b/src/PLUGIN/plugin.cpp @@ -19,7 +19,6 @@ #include "force.h" #include "lammps.h" #include "modify.h" -#include "pair.h" #include #include @@ -179,6 +178,46 @@ namespace LAMMPS_NS } (*pair_map)[plugin->name] = (Force::PairCreator)plugin->creator.v1; + } else if (pstyle == "bond") { + auto bond_map = lmp->force->bond_map; + if (bond_map->find(plugin->name) != bond_map->end()) { + if (lmp->comm->me == 0) + lmp->error->warning(FLERR,fmt::format("Overriding built-in bond " + "style {} from plugin", + plugin->name)); + } + (*bond_map)[plugin->name] = (Force::BondCreator)plugin->creator.v1; + + } else if (pstyle == "angle") { + auto angle_map = lmp->force->angle_map; + if (angle_map->find(plugin->name) != angle_map->end()) { + if (lmp->comm->me == 0) + lmp->error->warning(FLERR,fmt::format("Overriding built-in angle " + "style {} from plugin", + plugin->name)); + } + (*angle_map)[plugin->name] = (Force::AngleCreator)plugin->creator.v1; + + } else if (pstyle == "dihedral") { + auto dihedral_map = lmp->force->dihedral_map; + if (dihedral_map->find(plugin->name) != dihedral_map->end()) { + if (lmp->comm->me == 0) + lmp->error->warning(FLERR,fmt::format("Overriding built-in dihedral " + "style {} from plugin", + plugin->name)); + } + (*dihedral_map)[plugin->name] = (Force::DihedralCreator)plugin->creator.v1; + + } else if (pstyle == "improper") { + auto improper_map = lmp->force->improper_map; + if (improper_map->find(plugin->name) != improper_map->end()) { + if (lmp->comm->me == 0) + lmp->error->warning(FLERR,fmt::format("Overriding built-in improper " + "style {} from plugin", + plugin->name)); + } + (*improper_map)[plugin->name] = (Force::ImproperCreator)plugin->creator.v1; + } else if (pstyle == "fix") { auto fix_map = lmp->modify->fix_map; if (fix_map->find(plugin->name) != fix_map->end()) { @@ -219,8 +258,9 @@ namespace LAMMPS_NS int me = lmp->comm->me; // ignore unload request from unsupported style categories - if ((strcmp(style,"pair") != 0) - && (strcmp(style,"fix") != 0) + if ((strcmp(style,"pair") != 0) && (strcmp(style,"bond") != 0) + && (strcmp(style,"angle") != 0) && (strcmp(style,"dihedral") != 0) + && (strcmp(style,"improper") != 0) && (strcmp(style,"fix") != 0) && (strcmp(style,"command") != 0)) { if (me == 0) utils::logmesg(lmp,fmt::format("Ignoring unload: {} is not a " @@ -269,6 +309,54 @@ namespace LAMMPS_NS } } + } else if (pstyle == "bond") { + + auto found = lmp->force->bond_map->find(name); + if (found != lmp->force->bond_map->end()) + lmp->force->bond_map->erase(found); + + // must delete bond style instance if in use + + if ((lmp->force->bond_style != nullptr) + && (lmp->force->bond_match(name) != nullptr)) + lmp->force->create_bond("none",0); + + } else if (pstyle == "angle") { + + auto found = lmp->force->angle_map->find(name); + if (found != lmp->force->angle_map->end()) + lmp->force->angle_map->erase(found); + + // must delete angle style instance if in use + + if ((lmp->force->angle_style != nullptr) + && (lmp->force->angle_match(name) != nullptr)) + lmp->force->create_angle("none",0); + + } else if (pstyle == "dihedral") { + + auto found = lmp->force->dihedral_map->find(name); + if (found != lmp->force->dihedral_map->end()) + lmp->force->dihedral_map->erase(found); + + // must delete dihedral style instance if in use + + if ((lmp->force->dihedral_style) + && (lmp->force->dihedral_match(name) != nullptr)) + lmp->force->create_dihedral("none",0); + + } else if (pstyle == "improper") { + + auto found = lmp->force->improper_map->find(name); + if (found != lmp->force->improper_map->end()) + lmp->force->improper_map->erase(found); + + // must delete improper style instance if in use + + if ((lmp->force->improper_style != nullptr) + && (lmp->force->improper_match(name) != nullptr)) + lmp->force->create_improper("none",0); + } else if (pstyle == "fix") { auto fix_map = lmp->modify->fix_map; From fbb3bb14af47b7c893ede7cb4dd39f33826946be Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 18 Mar 2021 17:08:49 -0400 Subject: [PATCH 52/57] synchronize interface to managing computes in Modify with that of fixes - add find_compute_by_style() - add delete_compute(int) --- src/modify.cpp | 21 ++++++++++++++++++++- src/modify.h | 11 +++++++---- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/modify.cpp b/src/modify.cpp index 2c0fd434d6..9cb90fc36f 100644 --- a/src/modify.cpp +++ b/src/modify.cpp @@ -1326,7 +1326,12 @@ void Modify::delete_compute(const std::string &id) { int icompute = find_compute(id); if (icompute < 0) error->all(FLERR,"Could not find compute ID to delete"); - delete compute[icompute]; + delete_compute(icompute); +} + +void Modify::delete_compute(int icompute) +{ + if (compute[icompute]) delete compute[icompute]; // move other Computes down in list one slot @@ -1347,6 +1352,20 @@ int Modify::find_compute(const std::string &id) return -1; } +/* ---------------------------------------------------------------------- + find a compute by style + return index of compute or -1 if not found +------------------------------------------------------------------------- */ + +int Modify::find_compute_by_style(const char *style) +{ + int icompute; + for (icompute = 0; icompute < ncompute; icompute++) + if (utils::strmatch(compute[icompute]->style,style)) break; + if (icompute == ncompute) return -1; + return icompute; +} + /* ---------------------------------------------------------------------- clear invoked flag of all computes called everywhere that computes are used, before computes are invoked diff --git a/src/modify.h b/src/modify.h index ba8efd6525..0f4f7e32d2 100644 --- a/src/modify.h +++ b/src/modify.h @@ -109,21 +109,24 @@ class Modify : protected Pointers { void delete_fix(int); int find_fix(const std::string &); int find_fix_by_style(const char *); - int check_package(const char *); - int check_rigid_group_overlap(int); - int check_rigid_region_overlap(int, class Region *); - int check_rigid_list_overlap(int *); void add_compute(int, char **, int trysuffix=1); void add_compute(const std::string &, int trysuffix=1); void modify_compute(int, char **); void delete_compute(const std::string &); + void delete_compute(int); int find_compute(const std::string &); + int find_compute_by_style(const char *); void clearstep_compute(); void addstep_compute(bigint); void addstep_compute_all(bigint); + int check_package(const char *); + int check_rigid_group_overlap(int); + int check_rigid_region_overlap(int, class Region *); + int check_rigid_list_overlap(int *); + void write_restart(FILE *); int read_restart(FILE *); void restart_deallocate(int); From b2309f624606a56682282ab48b9b81a154e51eb4 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 18 Mar 2021 17:09:08 -0400 Subject: [PATCH 53/57] add support for computes --- src/PLUGIN/plugin.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/PLUGIN/plugin.cpp b/src/PLUGIN/plugin.cpp index 91d22b2897..255202b86b 100644 --- a/src/PLUGIN/plugin.cpp +++ b/src/PLUGIN/plugin.cpp @@ -218,6 +218,16 @@ namespace LAMMPS_NS } (*improper_map)[plugin->name] = (Force::ImproperCreator)plugin->creator.v1; + } else if (pstyle == "compute") { + auto compute_map = lmp->modify->compute_map; + if (compute_map->find(plugin->name) != compute_map->end()) { + if (lmp->comm->me == 0) + lmp->error->warning(FLERR,fmt::format("Overriding built-in compute " + "style {} from plugin", + plugin->name)); + } + (*compute_map)[plugin->name] = (Modify::ComputeCreator)plugin->creator.v2; + } else if (pstyle == "fix") { auto fix_map = lmp->modify->fix_map; if (fix_map->find(plugin->name) != fix_map->end()) { @@ -260,7 +270,8 @@ namespace LAMMPS_NS // ignore unload request from unsupported style categories if ((strcmp(style,"pair") != 0) && (strcmp(style,"bond") != 0) && (strcmp(style,"angle") != 0) && (strcmp(style,"dihedral") != 0) - && (strcmp(style,"improper") != 0) && (strcmp(style,"fix") != 0) + && (strcmp(style,"improper") != 0) && (strcmp(style,"compute") != 0) + && (strcmp(style,"fix") != 0) && (strcmp(style,"command") != 0)) { if (me == 0) utils::logmesg(lmp,fmt::format("Ignoring unload: {} is not a " @@ -357,6 +368,16 @@ namespace LAMMPS_NS && (lmp->force->improper_match(name) != nullptr)) lmp->force->create_improper("none",0); + } else if (pstyle == "compute") { + + auto compute_map = lmp->modify->compute_map; + auto found = compute_map->find(name); + if (found != compute_map->end()) compute_map->erase(name); + + for (int icompute = lmp->modify->find_compute_by_style(name); + icompute >= 0; icompute = lmp->modify->find_compute_by_style(name)) + lmp->modify->delete_compute(icompute); + } else if (pstyle == "fix") { auto fix_map = lmp->modify->fix_map; From 03793d5e4dba32bcce433f32c585dc916e7c4147 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 18 Mar 2021 17:23:25 -0400 Subject: [PATCH 54/57] simplify --- src/modify.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/modify.cpp b/src/modify.cpp index 9cb90fc36f..80cf2667fe 100644 --- a/src/modify.cpp +++ b/src/modify.cpp @@ -1099,11 +1099,9 @@ int Modify::find_fix(const std::string &id) int Modify::find_fix_by_style(const char *style) { - int ifix; - for (ifix = 0; ifix < nfix; ifix++) - if (utils::strmatch(fix[ifix]->style,style)) break; - if (ifix == nfix) return -1; - return ifix; + for (int ifix = 0; ifix < nfix; ifix++) + if (utils::strmatch(fix[ifix]->style,style)) return ifix; + return -1; } /* ---------------------------------------------------------------------- @@ -1359,11 +1357,9 @@ int Modify::find_compute(const std::string &id) int Modify::find_compute_by_style(const char *style) { - int icompute; - for (icompute = 0; icompute < ncompute; icompute++) - if (utils::strmatch(compute[icompute]->style,style)) break; - if (icompute == ncompute) return -1; - return icompute; + for (int icompute = 0; icompute < ncompute; icompute++) + if (utils::strmatch(compute[icompute]->style,style)) return icompute; + return -1; } /* ---------------------------------------------------------------------- From 023d42b5bb90a289c16fbcfefba9dd42063f7635 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 18 Mar 2021 18:03:12 -0400 Subject: [PATCH 55/57] add support for region style plugins --- src/PLUGIN/plugin.cpp | 23 ++++++++++++++++++++++- src/domain.cpp | 24 +++++++++++++++++++++++- src/domain.h | 2 ++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/PLUGIN/plugin.cpp b/src/PLUGIN/plugin.cpp index 255202b86b..b52e1a1959 100644 --- a/src/PLUGIN/plugin.cpp +++ b/src/PLUGIN/plugin.cpp @@ -14,6 +14,7 @@ #include "plugin.h" #include "comm.h" +#include "domain.h" #include "error.h" #include "input.h" #include "force.h" @@ -238,6 +239,16 @@ namespace LAMMPS_NS } (*fix_map)[plugin->name] = (Modify::FixCreator)plugin->creator.v2; + } else if (pstyle == "region") { + auto region_map = lmp->domain->region_map; + if (region_map->find(plugin->name) != region_map->end()) { + if (lmp->comm->me == 0) + lmp->error->warning(FLERR,fmt::format("Overriding built-in region " + "style {} from plugin", + plugin->name)); + } + (*region_map)[plugin->name] = (Domain::RegionCreator)plugin->creator.v2; + } else if (pstyle == "command") { auto command_map = lmp->input->command_map; if (command_map->find(plugin->name) != command_map->end()) { @@ -271,7 +282,7 @@ namespace LAMMPS_NS if ((strcmp(style,"pair") != 0) && (strcmp(style,"bond") != 0) && (strcmp(style,"angle") != 0) && (strcmp(style,"dihedral") != 0) && (strcmp(style,"improper") != 0) && (strcmp(style,"compute") != 0) - && (strcmp(style,"fix") != 0) + && (strcmp(style,"fix") != 0) && (strcmp(style,"region") != 0) && (strcmp(style,"command") != 0)) { if (me == 0) utils::logmesg(lmp,fmt::format("Ignoring unload: {} is not a " @@ -388,6 +399,16 @@ namespace LAMMPS_NS ifix >= 0; ifix = lmp->modify->find_fix_by_style(name)) lmp->modify->delete_fix(ifix); + } else if (pstyle == "region") { + + auto region_map = lmp->domain->region_map; + auto found = region_map->find(name); + if (found != region_map->end()) region_map->erase(name); + + for (int iregion = lmp->domain->find_region_by_style(name); + iregion >= 0; iregion = lmp->domain->find_region_by_style(name)) + lmp->domain->delete_region(iregion); + } else if (pstyle == "command") { auto command_map = lmp->input->command_map; diff --git a/src/domain.cpp b/src/domain.cpp index c6dee63221..abf0d70079 100644 --- a/src/domain.cpp +++ b/src/domain.cpp @@ -1816,8 +1816,18 @@ void Domain::delete_region(int narg, char **arg) int iregion = find_region(arg[0]); if (iregion == -1) error->all(FLERR,"Delete region ID does not exist"); + delete_region(iregion); +} + +void Domain::delete_region(int iregion) +{ + if ((iregion < 0) || (iregion >= nregion)) return; + + // delete and move other Regions down in list one slot + delete regions[iregion]; - regions[iregion] = regions[nregion-1]; + for (int i = iregion+1; iregion < nregion; ++i) + regions[i-1] = regions[i]; nregion--; } @@ -1833,6 +1843,18 @@ int Domain::find_region(const std::string &name) return -1; } +/* ---------------------------------------------------------------------- + return region index if name matches existing region style + return -1 if no such region +------------------------------------------------------------------------- */ + +int Domain::find_region_by_style(const std::string &name) +{ + for (int iregion = 0; iregion < nregion; iregion++) + if (name == regions[iregion]->style) return iregion; + return -1; +} + /* ---------------------------------------------------------------------- (re)set boundary settings flag = 0, called from the input script diff --git a/src/domain.h b/src/domain.h index 99349edb5d..5009a67d28 100644 --- a/src/domain.h +++ b/src/domain.h @@ -130,7 +130,9 @@ class Domain : protected Pointers { void set_lattice(int, char **); void add_region(int, char **); void delete_region(int, char **); + void delete_region(int); int find_region(const std::string &); + int find_region_by_style(const std::string &); void set_boundary(int, char **, int); void set_box(int, char **); void print_box(const std::string &); From a5ce7c1ac9e6758c0d3606913f4d54c71d635f09 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 18 Mar 2021 18:03:26 -0400 Subject: [PATCH 56/57] better checks for valid data --- src/modify.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/modify.cpp b/src/modify.cpp index 80cf2667fe..79a0ecd41f 100644 --- a/src/modify.cpp +++ b/src/modify.cpp @@ -1069,10 +1069,12 @@ void Modify::delete_fix(const std::string &id) void Modify::delete_fix(int ifix) { - if (fix[ifix]) delete fix[ifix]; - atom->update_callback(ifix); + if ((ifix < 0) || (ifix >= nfix)) return; - // move other Fixes and fmask down in list one slot + // delete instance and move other Fixes and fmask down in list one slot + + delete fix[ifix]; + atom->update_callback(ifix); for (int i = ifix+1; i < nfix; i++) fix[i-1] = fix[i]; for (int i = ifix+1; i < nfix; i++) fmask[i-1] = fmask[i]; @@ -1329,10 +1331,11 @@ void Modify::delete_compute(const std::string &id) void Modify::delete_compute(int icompute) { - if (compute[icompute]) delete compute[icompute]; + if ((icompute < 0) || (icompute >= ncompute)) return; - // move other Computes down in list one slot + // delete and move other Computes down in list one slot + delete compute[icompute]; for (int i = icompute+1; i < ncompute; i++) compute[i-1] = compute[i]; ncompute--; } From 024a9600b15df903b614978bf655f56c06e63a26 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 18 Mar 2021 18:04:13 -0400 Subject: [PATCH 57/57] update docs --- doc/src/Developer_plugins.rst | 120 +++++++++++++++----- doc/src/plugin.rst | 2 +- doc/utils/sphinx-config/false_positives.txt | 1 + 3 files changed, 96 insertions(+), 27 deletions(-) diff --git a/doc/src/Developer_plugins.rst b/doc/src/Developer_plugins.rst index 7275c12605..68f1f1387c 100644 --- a/doc/src/Developer_plugins.rst +++ b/doc/src/Developer_plugins.rst @@ -2,36 +2,95 @@ 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. +without recompiling LAMMPS. The functionality for this and the +:doc:`plugin command ` are implemented in the +:ref:`PLUGIN package ` which must be installed to use plugins. + +Plugins use the operating system's capability to load dynamic shared +object (DSO) files in a way similar shared libraries and then reference +specific functions in those DSOs. Any DSO file with plugins has to include +an initialization function with a specific name, "lammpsplugin_init", that +has to follow specific rules described below. When loading the DSO with +the "plugin" command, this function is looked up and 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. +oriented design of LAMMPS where all pair style commands are derived from +the class Pair, all fix style commands from the class Fix and so on and +usually only functions present in 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. +A DSO file with 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 present in the DSO file called +``lammpsplugin_init`` which is called with three ``void *`` arguments: +a pointer to the current LAMMPS instance, a pointer to the opened DSO +handle, and a pointer to the registration function. The registration +function takes two arguments: a pointer to a ``lammpsplugin_t`` struct +with information about the plugin and a pointer to the current LAMMPS +instance. Please see below for an example of how the registration is +done. +Members of ``lammpsplugin_t`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -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 +.. list-table:: + :header-rows: 1 + :widths: auto + + * - Member + - Description + * - version + - LAMMPS Version string the plugin was compiled for + * - style + - Style of the plugin (pair, bond, fix, command, etc.) + * - name + - Name of the plugin style + * - info + - String with information about the plugin + * - author + - String with the name and email of the author + * - creator.v1 + - Pointer to factory function for pair, bond, angle, dihedral, or improper styles + * - creator.v2 + - Pointer to factory function for compute, fix, or region styles + * - creator.v3 + - Pointer to factory function for command styles + * - handle + - Pointer to the open DSO file handle + +Only one of the three alternate creator entries can be used at a time +and which of those is determined by the style of plugin. The "creator.v1" +element is for factory functions of supported styles computing forces (i.e. +pair, bond, angle, dihedral, or improper styles) and the function takes +as single argument the pointer to the LAMMPS instance. The factory function +is cast to the ``lammpsplugin_factory1`` type before assignment. The +"creator.v2" element is for factory functions creating an instance of +a fix, compute, or region style and takes three arguments: a pointer to +the LAMMPS instance, an integer with the length of the argument list and +a ``char **`` pointer to the list of arguments. The factory function pointer +needs to be cast to the ``lammpsplugin_factory2`` type before assignment. +The "creator.v3" element takes the same arguments as "creator.v3" but is +specific to creating command styles: the factory function has to instantiate +the command style locally passing the LAMMPS pointer as argument and then +call its "command" member function with the number and list of arguments. +The factory function pointer needs to be cast to the +``lammpsplugin_factory3`` type before assignment. + +Pair style example +^^^^^^^^^^^^^^^^^^ + +As an example, a hypothetical pair style plugin "morse2" implemented in +a class ``PairMorse2`` in the files ``pair_morse2.h`` and +``pair_morse2.cpp`` with the factory function and initialization function would look like this: .. code-block:: C++ @@ -70,6 +129,10 @@ 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. + +Fix style example +^^^^^^^^^^^^^^^^^ + 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`` @@ -104,6 +167,8 @@ Below is an example for that: (*register_plugin)(&plugin,lmp); } +Command style example +^^^^^^^^^^^^^^^^^^^^^ 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 @@ -158,6 +223,9 @@ file as the plugin interface code: (*register_plugin)(&plugin,lmp); } +Additional Details +^^^^^^^^^^^^^^^^^^ + 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 @@ -167,7 +235,7 @@ 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 +pointer to the ``lammpsplugin_t`` 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. diff --git a/doc/src/plugin.rst b/doc/src/plugin.rst index 48638bf97a..02228636bc 100644 --- a/doc/src/plugin.rst +++ b/doc/src/plugin.rst @@ -17,7 +17,7 @@ Syntax *load* file = load plugin(s) from shared object in *file* *unload* style name = unload plugin *name* of style *style* - *style* = *pair* or *fix* or *command* + *style* = *pair* or *bond* or *angle* or *dihedral* or *improper* or *compute* or *fix* or *region* or *command* *list* = print a list of currently loaded plugins *clear* = unload all currently loaded plugins diff --git a/doc/utils/sphinx-config/false_positives.txt b/doc/utils/sphinx-config/false_positives.txt index 1d66028621..38a6971f4c 100644 --- a/doc/utils/sphinx-config/false_positives.txt +++ b/doc/utils/sphinx-config/false_positives.txt @@ -1588,6 +1588,7 @@ lammps Lammps LAMMPS lammpsplot +lammpsplugin Lampis Lamoureux Lanczos