From c4d11a78713fe084a20fcdb18b541c0fc0d4378a Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 29 Jan 2025 15:38:18 -0500 Subject: [PATCH 1/8] follow LAMMPS programming conventions more closely --- src/kspace.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/kspace.cpp b/src/kspace.cpp index 7d51c46ea8..d5f96441ea 100644 --- a/src/kspace.cpp +++ b/src/kspace.cpp @@ -33,15 +33,15 @@ static constexpr double SMALL = 0.00001; /* ---------------------------------------------------------------------- */ -KSpace::KSpace(LAMMPS *lmp) : Pointers(lmp) +KSpace::KSpace(LAMMPS *lmp) : + Pointers(lmp), eatom(nullptr), vatom(nullptr), gcons(nullptr), dgcons(nullptr) { order_allocated = 0; energy = 0.0; virial[0] = virial[1] = virial[2] = virial[3] = virial[4] = virial[5] = 0.0; triclinic_support = 1; - ewaldflag = pppmflag = msmflag = dispersionflag = tip4pflag = - dipoleflag = spinflag = 0; + ewaldflag = pppmflag = msmflag = dispersionflag = tip4pflag = dipoleflag = spinflag = 0; compute_flag = 1; group_group_enable = 0; stagger_flag = 0; @@ -83,14 +83,17 @@ KSpace::KSpace(LAMMPS *lmp) : Pointers(lmp) accuracy_real_6 = -1.0; accuracy_kspace_6 = -1.0; + qqrd2e = force->qqrd2e; + g_ewald = g_ewald_6 = 0.0; + scale = 1.0; + neighrequest_flag = 1; mixflag = 0; splittol = 1.0e-6; + scale = 1.0; maxeatom = maxvatom = 0; - eatom = nullptr; - vatom = nullptr; centroidstressflag = CENTROID_NOTAVAIL; execution_space = Host; From e3dd2790f96438f48f9aa90c538b751b50001aaf Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 29 Jan 2025 15:39:53 -0500 Subject: [PATCH 2/8] add KSpace style zero to KSPACE and zero2 as plugin demo --- examples/plugins/CMakeLists.txt | 4 + examples/plugins/kspace_zero2.cpp | 117 ++++++++++++++++++++++++++++++ examples/plugins/kspace_zero2.h | 33 +++++++++ examples/plugins/kspaceplugin.cpp | 35 +++++++++ src/KSPACE/kspace_zero.cpp | 117 ++++++++++++++++++++++++++++++ src/KSPACE/kspace_zero.h | 40 ++++++++++ 6 files changed, 346 insertions(+) create mode 100644 examples/plugins/kspace_zero2.cpp create mode 100644 examples/plugins/kspace_zero2.h create mode 100644 examples/plugins/kspaceplugin.cpp create mode 100644 src/KSPACE/kspace_zero.cpp create mode 100644 src/KSPACE/kspace_zero.h diff --git a/examples/plugins/CMakeLists.txt b/examples/plugins/CMakeLists.txt index 60cbd01d73..ce1de8d436 100644 --- a/examples/plugins/CMakeLists.txt +++ b/examples/plugins/CMakeLists.txt @@ -94,6 +94,10 @@ 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) +add_library(kspaceplugin MODULE kspaceplugin.cpp kspace_zero2.cpp) +target_include_directories(kspaceplugin PRIVATE "${LAMMPS_HEADER_DIR}/KSPACE") +target_link_libraries(kspaceplugin PRIVATE lammps) + set_target_properties(morse2plugin nve2plugin helloplugin zero2plugin PROPERTIES PREFIX "" SUFFIX ".so") # MacOS seems to need this diff --git a/examples/plugins/kspace_zero2.cpp b/examples/plugins/kspace_zero2.cpp new file mode 100644 index 0000000000..c5d6f1b73b --- /dev/null +++ b/examples/plugins/kspace_zero2.cpp @@ -0,0 +1,117 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + 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) +------------------------------------------------------------------------- */ + +#include "kspace_zero2.h" + +#include "atom.h" +#include "comm.h" +#include "domain.h" +#include "error.h" +#include "force.h" +#include "pair.h" + +#include + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +KSpaceZero2::KSpaceZero2(LAMMPS *lmp) : KSpace(lmp) +{ + ewaldflag = 1; + pppmflag = 1; + msmflag = 1; + dispersionflag = 1; + tip4pflag = 1; + dipoleflag = 1; + spinflag = 1; +} + +KSpaceZero2::~KSpaceZero2() +{ + fprintf(stderr, "In destructor for KSpace zero2. This = %p\n", this); +} + +/* ---------------------------------------------------------------------- */ + +void KSpaceZero2::settings(int narg, char **arg) +{ + if (narg != 1) error->all(FLERR, "Illegal kspace_style {} command", force->kspace_style); + + accuracy_relative = fabs(utils::numeric(FLERR, arg[0], false, lmp)); + if (accuracy_relative > 1.0) + error->all(FLERR, "Invalid relative accuracy {:g} for kspace_style {}", accuracy_relative, + force->kspace_style); + if ((narg != 0) && (narg != 1)) error->all(FLERR, "Illegal kspace_style command"); +} + +/* ---------------------------------------------------------------------- */ + +void KSpaceZero2::init() +{ + if (comm->me == 0) utils::logmesg(lmp, "Dummy KSpace initialization ...\n"); + + // error checks + + if (force->pair == nullptr) error->all(FLERR, "KSpace solver requires a pair style"); + if (!atom->q_flag) error->all(FLERR, "KSpace style zero2 requires atom attribute q"); + + // compute two charge force + + two_charge(); + + int itmp; + auto p_cutoff = (double *) force->pair->extract("cut_coul", itmp); + if (p_cutoff == nullptr) error->all(FLERR, "KSpace style is incompatible with Pair style"); + double cutoff = *p_cutoff; + + qsum_qsq(); + + accuracy = accuracy_relative * two_charge_force; + + // make initial g_ewald estimate + // based on desired accuracy and real space cutoff + // fluid-occupied volume used to estimate real-space error + // zprd used rather than zprd_slab + + if (!gewaldflag) { + if (accuracy <= 0.0) error->all(FLERR, "KSpace accuracy must be > 0"); + if (q2 == 0.0) error->all(FLERR, "Must use 'kspace_modify gewald' for uncharged system"); + g_ewald = accuracy * sqrt(atom->natoms * cutoff * domain->xprd * domain->yprd * domain->zprd) / + (2.0 * q2); + if (g_ewald >= 1.0) + g_ewald = (1.35 - 0.15 * log(accuracy)) / cutoff; + else + g_ewald = sqrt(-log(g_ewald)) / cutoff; + } + + if (comm->me == 0) std::string mesg = fmt::format(" G vector (1/distance) = {:.8g}\n", g_ewald); +} + +/* ---------------------------------------------------------------------- */ + +void KSpaceZero2::setup() +{ + if (comm->me == 0) utils::logmesg(lmp, "Dummy KSpace setup\n"); +} + +/* ---------------------------------------------------------------------- */ + +void KSpaceZero2::compute(int eflag, int vflag) +{ + ev_init(eflag, vflag); +} diff --git a/examples/plugins/kspace_zero2.h b/examples/plugins/kspace_zero2.h new file mode 100644 index 0000000000..7539dd4609 --- /dev/null +++ b/examples/plugins/kspace_zero2.h @@ -0,0 +1,33 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/ Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + 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_KSPACE_ZERO2_H +#define LMP_KSPACE_ZERO2_H + +#include "kspace.h" + +namespace LAMMPS_NS { + +class KSpaceZero2 : public KSpace { + public: + KSpaceZero2(class LAMMPS *); + ~KSpaceZero2() override; + + void init() override; + void setup() override; + void settings(int, char **) override; + + void compute(int, int) override; +}; +} // namespace LAMMPS_NS +#endif diff --git a/examples/plugins/kspaceplugin.cpp b/examples/plugins/kspaceplugin.cpp new file mode 100644 index 0000000000..3e7b4bd047 --- /dev/null +++ b/examples/plugins/kspaceplugin.cpp @@ -0,0 +1,35 @@ + +#include "lammpsplugin.h" + +#include "comm.h" +#include "command.h" +#include "error.h" +#include "version.h" + +#include + +#include "kspace_zero2.h" + +using namespace LAMMPS_NS; + +static KSpace *zero2creator(LAMMPS *lmp) +{ + KSpace *ptr = (KSpace *) new KSpaceZero2(lmp); + fprintf(stderr, "Created zero2 instance at %p\n", ptr); + return ptr; +} + +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 = "kspace"; + plugin.name = "zero2"; + plugin.info = "zero2 KSpace style v1.0"; + plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; + plugin.creator.v1 = (lammpsplugin_factory1 *) &zero2creator; + plugin.handle = handle; + (*register_plugin)(&plugin, lmp); +} diff --git a/src/KSPACE/kspace_zero.cpp b/src/KSPACE/kspace_zero.cpp new file mode 100644 index 0000000000..028d42401e --- /dev/null +++ b/src/KSPACE/kspace_zero.cpp @@ -0,0 +1,117 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + 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) +------------------------------------------------------------------------- */ + +#include "kspace_zero.h" + +#include "atom.h" +#include "comm.h" +#include "domain.h" +#include "error.h" +#include "force.h" +#include "pair.h" + +#include + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +KSpaceZero::KSpaceZero(LAMMPS *lmp) : KSpace(lmp) +{ + ewaldflag = 1; + pppmflag = 1; + msmflag = 1; + dispersionflag = 1; + tip4pflag = 1; + dipoleflag = 1; + spinflag = 1; +} + +KSpaceZero::~KSpaceZero() +{ + fprintf(stderr, "In destructor for KSpace zero. This = %p\n", this); +} + +/* ---------------------------------------------------------------------- */ + +void KSpaceZero::settings(int narg, char **arg) +{ + if (narg != 1) error->all(FLERR, "Illegal kspace_style {} command", force->kspace_style); + + accuracy_relative = fabs(utils::numeric(FLERR, arg[0], false, lmp)); + if (accuracy_relative > 1.0) + error->all(FLERR, "Invalid relative accuracy {:g} for kspace_style {}", accuracy_relative, + force->kspace_style); + if ((narg != 0) && (narg != 1)) error->all(FLERR, "Illegal kspace_style command"); +} + +/* ---------------------------------------------------------------------- */ + +void KSpaceZero::init() +{ + if (comm->me == 0) utils::logmesg(lmp, "Dummy KSpace initialization ...\n"); + + // error checks + + if (force->pair == nullptr) error->all(FLERR, "KSpace solver requires a pair style"); + if (!atom->q_flag) error->all(FLERR, "KSpace style zero requires atom attribute q"); + + // compute two charge force + + two_charge(); + + int itmp; + auto p_cutoff = (double *) force->pair->extract("cut_coul", itmp); + if (p_cutoff == nullptr) error->all(FLERR, "KSpace style is incompatible with Pair style"); + double cutoff = *p_cutoff; + + qsum_qsq(); + + accuracy = accuracy_relative * two_charge_force; + + // make initial g_ewald estimate + // based on desired accuracy and real space cutoff + // fluid-occupied volume used to estimate real-space error + // zprd used rather than zprd_slab + + if (!gewaldflag) { + if (accuracy <= 0.0) error->all(FLERR, "KSpace accuracy must be > 0"); + if (q2 == 0.0) error->all(FLERR, "Must use 'kspace_modify gewald' for uncharged system"); + g_ewald = accuracy * sqrt(atom->natoms * cutoff * domain->xprd * domain->yprd * domain->zprd) / + (2.0 * q2); + if (g_ewald >= 1.0) + g_ewald = (1.35 - 0.15 * log(accuracy)) / cutoff; + else + g_ewald = sqrt(-log(g_ewald)) / cutoff; + } + + if (comm->me == 0) std::string mesg = fmt::format(" G vector (1/distance) = {:.8g}\n", g_ewald); +} + +/* ---------------------------------------------------------------------- */ + +void KSpaceZero::setup() +{ + if (comm->me == 0) utils::logmesg(lmp, "Dummy KSpace setup\n"); +} + +/* ---------------------------------------------------------------------- */ + +void KSpaceZero::compute(int eflag, int vflag) +{ + ev_init(eflag, vflag); +} diff --git a/src/KSPACE/kspace_zero.h b/src/KSPACE/kspace_zero.h new file mode 100644 index 0000000000..9c76c15de7 --- /dev/null +++ b/src/KSPACE/kspace_zero.h @@ -0,0 +1,40 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/ Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + 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 KSPACE_CLASS +// clang-format off +KSpaceStyle(zero,KSpaceZero); +// clang-format on +#else + +#ifndef LMP_KSPACE_ZERO_H +#define LMP_KSPACE_ZERO_H + +#include "kspace.h" + +namespace LAMMPS_NS { + +class KSpaceZero : public KSpace { + public: + KSpaceZero(class LAMMPS *); + ~KSpaceZero() override; + + void init() override; + void setup() override; + void settings(int, char **) override; + + void compute(int, int) override; +}; +} // namespace LAMMPS_NS +#endif +#endif From 464b9f4b0371dc5d209d9465355c8b838010872d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 11 Jun 2025 00:33:44 -0400 Subject: [PATCH 3/8] add support for loading run and min styles --- doc/src/plugin.rst | 2 +- examples/plugins/CMakeLists.txt | 11 +- examples/plugins/min_cg2.cpp | 192 ++++++++++++++ examples/plugins/min_cg2.h | 29 ++ examples/plugins/runminplugin.cpp | 45 ++++ examples/plugins/verlet2.cpp | 426 ++++++++++++++++++++++++++++++ examples/plugins/verlet2.h | 39 +++ src/PLUGIN/plugin.cpp | 34 ++- 8 files changed, 771 insertions(+), 7 deletions(-) create mode 100644 examples/plugins/min_cg2.cpp create mode 100644 examples/plugins/min_cg2.h create mode 100644 examples/plugins/runminplugin.cpp create mode 100644 examples/plugins/verlet2.cpp create mode 100644 examples/plugins/verlet2.h diff --git a/doc/src/plugin.rst b/doc/src/plugin.rst index f8f668789d..83a20b3248 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 *bond* or *angle* or *dihedral* or *improper* or *kspace* or *compute* or *fix* or *region* or *command* + *style* = *pair* or *bond* or *angle* or *dihedral* or *improper* or *kspace* or *compute* or *fix* or *region* or *command* or *run* or *min* *list* = print a list of currently loaded plugins *clear* = unload all currently loaded plugins diff --git a/examples/plugins/CMakeLists.txt b/examples/plugins/CMakeLists.txt index 60cbd01d73..e49ba6ba88 100644 --- a/examples/plugins/CMakeLists.txt +++ b/examples/plugins/CMakeLists.txt @@ -94,7 +94,10 @@ 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 "" SUFFIX ".so") +add_library(runminplugin MODULE runminplugin.cpp min_cg2.cpp verlet2.cpp) +target_link_libraries(runminplugin PRIVATE lammps) + +set_target_properties(morse2plugin nve2plugin helloplugin zero2plugin runminplugin PROPERTIES PREFIX "" SUFFIX ".so") # MacOS seems to need this if(CMAKE_SYSTEM_NAME STREQUAL Darwin) @@ -105,13 +108,13 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") set_target_properties(morse2plugin nve2plugin helloplugin zero2plugin PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE) if(CMAKE_CROSSCOMPILING) - set_target_properties(morse2plugin nve2plugin helloplugin zero2plugin + set_target_properties(morse2plugin nve2plugin helloplugin zero2plugin runminplugin PROPERTIES LINK_FLAGS "-Wl,--export-all-symbols") endif() else() - set_target_properties(morse2plugin nve2plugin helloplugin zero2plugin PROPERTIES + set_target_properties(morse2plugin nve2plugin helloplugin zero2plugin runminplugin PROPERTIES LINK_FLAGS "-rdynamic") endif() add_custom_target(plugins ALL ${CMAKE_COMMAND} -E echo "Building Plugins" - DEPENDS morse2plugin nve2plugin helloplugin zero2plugin morse2plugin) + DEPENDS morse2plugin nve2plugin helloplugin zero2plugin morse2plugin runminplugin) diff --git a/examples/plugins/min_cg2.cpp b/examples/plugins/min_cg2.cpp new file mode 100644 index 0000000000..4b9d519d45 --- /dev/null +++ b/examples/plugins/min_cg2.cpp @@ -0,0 +1,192 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + 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 "min_cg2.h" + +#include "error.h" +#include "output.h" +#include "timer.h" +#include "update.h" + +#include + +using namespace LAMMPS_NS; + +// EPS_ENERGY = minimum normalization for energy tolerance + +static constexpr double EPS_ENERGY = 1.0e-8; + +/* ---------------------------------------------------------------------- */ + +MinCG2::MinCG2(LAMMPS *lmp) : MinLineSearch(lmp) {} + +/* ---------------------------------------------------------------------- + minimization via conjugate gradient iterations +------------------------------------------------------------------------- */ + +int MinCG2::iterate(int maxiter) +{ + int i,m,n,fail,ntimestep; + double beta,gg,dot[2],dotall[2],fdotf; + double *fatom,*gatom,*hatom; + + // nlimit = max # of CG iterations before restarting + // set to ndoftotal unless too big + + int nlimit = static_cast (MIN(MAXSMALLINT,ndoftotal)); + + // initialize working vectors + + for (i = 0; i < nvec; i++) h[i] = g[i] = fvec[i]; + if (nextra_atom) + for (m = 0; m < nextra_atom; m++) { + fatom = fextra_atom[m]; + gatom = gextra_atom[m]; + hatom = hextra_atom[m]; + n = extra_nlen[m]; + for (i = 0; i < n; i++) hatom[i] = gatom[i] = fatom[i]; + } + if (nextra_global) + for (i = 0; i < nextra_global; i++) hextra[i] = gextra[i] = fextra[i]; + + gg = fnorm_sqr(); + + for (int iter = 0; iter < maxiter; iter++) { + + if (timer->check_timeout(niter)) + return TIMEOUT; + + ntimestep = ++update->ntimestep; + niter++; + + // line minimization along direction h from current atom->x + + eprevious = ecurrent; + fail = (this->*linemin)(ecurrent,alpha_final); + if (fail) return fail; + + // function evaluation criterion + + if (neval >= update->max_eval) return MAXEVAL; + + // energy tolerance criterion + + if (fabs(ecurrent-eprevious) < + update->etol * 0.5*(fabs(ecurrent) + fabs(eprevious) + EPS_ENERGY)) + return ETOL; + + // force tolerance criterion + + dot[0] = dot[1] = 0.0; + for (i = 0; i < nvec; i++) { + dot[0] += fvec[i]*fvec[i]; + dot[1] += fvec[i]*g[i]; + } + + if (nextra_atom) + for (m = 0; m < nextra_atom; m++) { + fatom = fextra_atom[m]; + gatom = gextra_atom[m]; + n = extra_nlen[m]; + for (i = 0; i < n; i++) { + dot[0] += fatom[i]*fatom[i]; + dot[1] += fatom[i]*gatom[i]; + } + } + MPI_Allreduce(dot,dotall,2,MPI_DOUBLE,MPI_SUM,world); + if (nextra_global) + for (i = 0; i < nextra_global; i++) { + dotall[0] += fextra[i]*fextra[i]; + dotall[1] += fextra[i]*gextra[i]; + } + + fdotf = 0.0; + if (update->ftol > 0.0) { + if (normstyle == MAX) fdotf = fnorm_max(); // max force norm + else if (normstyle == INF) fdotf = fnorm_inf(); // infinite force norm + else if (normstyle == TWO) fdotf = dotall[0]; // same as fnorm_sqr(), Euclidean force 2-norm + else error->all(FLERR,"Illegal min_modify command"); + if (fdotf < update->ftol*update->ftol) return FTOL; + } + + // update new search direction h from new f = -Grad(x) and old g + // this is Polak-Ribieri formulation + // beta = dotall[0]/gg would be Fletcher-Reeves + // reinitialize CG every ndof iterations by setting beta = 0.0 + + beta = MAX(0.0,(dotall[0] - dotall[1])/gg); + if ((niter+1) % nlimit == 0) beta = 0.0; + gg = dotall[0]; + + for (i = 0; i < nvec; i++) { + g[i] = fvec[i]; + h[i] = g[i] + beta*h[i]; + } + if (nextra_atom) + for (m = 0; m < nextra_atom; m++) { + fatom = fextra_atom[m]; + gatom = gextra_atom[m]; + hatom = hextra_atom[m]; + n = extra_nlen[m]; + for (i = 0; i < n; i++) { + gatom[i] = fatom[i]; + hatom[i] = gatom[i] + beta*hatom[i]; + } + } + if (nextra_global) + for (i = 0; i < nextra_global; i++) { + gextra[i] = fextra[i]; + hextra[i] = gextra[i] + beta*hextra[i]; + } + + // reinitialize CG if new search direction h is not downhill + + dot[0] = 0.0; + for (i = 0; i < nvec; i++) dot[0] += g[i]*h[i]; + if (nextra_atom) + for (m = 0; m < nextra_atom; m++) { + gatom = gextra_atom[m]; + hatom = hextra_atom[m]; + n = extra_nlen[m]; + for (i = 0; i < n; i++) dot[0] += gatom[i]*hatom[i]; + } + MPI_Allreduce(dot,dotall,1,MPI_DOUBLE,MPI_SUM,world); + if (nextra_global) + for (i = 0; i < nextra_global; i++) + dotall[0] += gextra[i]*hextra[i]; + + if (dotall[0] <= 0.0) { + for (i = 0; i < nvec; i++) h[i] = g[i]; + if (nextra_atom) + for (m = 0; m < nextra_atom; m++) { + gatom = gextra_atom[m]; + hatom = hextra_atom[m]; + n = extra_nlen[m]; + for (i = 0; i < n; i++) hatom[i] = gatom[i]; + } + if (nextra_global) + for (i = 0; i < nextra_global; i++) hextra[i] = gextra[i]; + } + + // output for thermo, dump, restart files + + if (output->next == ntimestep) { + timer->stamp(); + output->write(ntimestep); + timer->stamp(Timer::OUTPUT); + } + } + + return MAXITER; +} diff --git a/examples/plugins/min_cg2.h b/examples/plugins/min_cg2.h new file mode 100644 index 0000000000..f8c083b971 --- /dev/null +++ b/examples/plugins/min_cg2.h @@ -0,0 +1,29 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + 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_MIN_CG2_H +#define LMP_MIN_CG2_H + +#include "min_linesearch.h" + +namespace LAMMPS_NS { + +class MinCG2 : public MinLineSearch { + public: + MinCG2(class LAMMPS *); + int iterate(int) override; +}; + +} // namespace LAMMPS_NS + +#endif diff --git a/examples/plugins/runminplugin.cpp b/examples/plugins/runminplugin.cpp new file mode 100644 index 0000000000..9f098d6aab --- /dev/null +++ b/examples/plugins/runminplugin.cpp @@ -0,0 +1,45 @@ + +#include "lammpsplugin.h" + +#include "version.h" + +#include + +#include "min_cg2.h" +#include "verlet2.h" + +using namespace LAMMPS_NS; + +static Min *min_cg2creator(LAMMPS *lmp) +{ + return new MinCG2(lmp); +} + +static Integrate *verlet2creator(LAMMPS *lmp, int argc, char **argv) +{ + return new Verlet2(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 = "min"; + plugin.name = "cg2"; + plugin.info = "CG2 minimize style v1.0"; + plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; + plugin.creator.v1 = (lammpsplugin_factory1 *) &min_cg2creator; + plugin.handle = handle; + (*register_plugin)(&plugin, lmp); + + plugin.style = "run"; + plugin.name = "verlet2"; + plugin.info = "Verlet2 run style v1.0"; + plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; + plugin.creator.v2 = (lammpsplugin_factory2 *) &verlet2creator; + plugin.handle = handle; + (*register_plugin)(&plugin, lmp); +} diff --git a/examples/plugins/verlet2.cpp b/examples/plugins/verlet2.cpp new file mode 100644 index 0000000000..b88857645f --- /dev/null +++ b/examples/plugins/verlet2.cpp @@ -0,0 +1,426 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + 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 "verlet2.h" + +#include "angle.h" +#include "atom.h" +#include "atom_vec.h" +#include "bond.h" +#include "comm.h" +#include "dihedral.h" +#include "domain.h" +#include "error.h" +#include "fix.h" +#include "force.h" +#include "improper.h" +#include "kspace.h" +#include "modify.h" +#include "neighbor.h" +#include "output.h" +#include "pair.h" +#include "timer.h" +#include "update.h" + +#include + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +Verlet2::Verlet2(LAMMPS *lmp, int narg, char **arg) : + Integrate(lmp, narg, arg) {} + +Verlet2::~Verlet2() +{ + // do nothing +} + +/* ---------------------------------------------------------------------- + initialization before run +------------------------------------------------------------------------- */ + +void Verlet2::init() +{ + Integrate::init(); + + // warn if no fixes doing time integration + + bool do_time_integrate = false; + for (const auto &fix : modify->get_fix_list()) + if (fix->time_integrate) do_time_integrate = true; + + if (!do_time_integrate && (comm->me == 0)) + error->warning(FLERR,"No fixes with time integration, atoms won't move" + utils::errorurl(28)); + + // virial_style: + // VIRIAL_PAIR if computed explicitly in pair via sum over pair interactions + // VIRIAL_FDOTR if computed implicitly in pair by + // virial_fdotr_compute() via sum over ghosts + + if (force->newton_pair) virial_style = VIRIAL_FDOTR; + else virial_style = VIRIAL_PAIR; + + // setup lists of computes for global and per-atom PE and pressure + + ev_setup(); + + // detect if fix omp is present for clearing force arrays + + if (modify->get_fix_by_id("package_omp")) external_force_clear = 1; + + // set flags for arrays to clear in force_clear() + + torqueflag = extraflag = 0; + if (atom->torque_flag) torqueflag = 1; + if (atom->avec->forceclearflag) extraflag = 1; + + // orthogonal vs triclinic simulation box + + triclinic = domain->triclinic; +} + +/* ---------------------------------------------------------------------- + setup before run +------------------------------------------------------------------------- */ + +void Verlet2::setup(int flag) +{ + if (comm->me == 0 && screen) { + fputs("Setting up Verlet2 run ...\n",screen); + if (flag) { + utils::print(screen," Unit style : {}\n" + " Current step : {}\n" + " Time step : {}\n", + update->unit_style,update->ntimestep,update->dt); + timer->print_timeout(screen); + } + } + + if (lmp->kokkos) + error->all(FLERR,"KOKKOS package requires run_style verlet2/kk"); + + update->setupflag = 1; + + // setup domain, communication and neighboring + // acquire ghosts + // build neighbor lists + + atom->setup(); + modify->setup_pre_exchange(); + if (triclinic) domain->x2lamda(atom->nlocal); + domain->pbc(); + domain->reset_box(); + comm->setup(); + if (neighbor->style) neighbor->setup_bins(); + comm->exchange(); + if (atom->sortfreq > 0) atom->sort(); + comm->borders(); + if (triclinic) domain->lamda2x(atom->nlocal+atom->nghost); + domain->image_check(); + domain->box_too_small_check(); + modify->setup_pre_neighbor(); + neighbor->build(1); + modify->setup_post_neighbor(); + neighbor->ncalls = 0; + + // compute all forces + + force->setup(); + ev_set(update->ntimestep); + force_clear(); + modify->setup_pre_force(vflag); + + if (pair_compute_flag) force->pair->compute(eflag,vflag); + else if (force->pair) force->pair->compute_dummy(eflag,vflag); + + if (atom->molecular != Atom::ATOMIC) { + if (force->bond) force->bond->compute(eflag,vflag); + if (force->angle) force->angle->compute(eflag,vflag); + if (force->dihedral) force->dihedral->compute(eflag,vflag); + if (force->improper) force->improper->compute(eflag,vflag); + } + + if (force->kspace) { + force->kspace->setup(); + if (kspace_compute_flag) force->kspace->compute(eflag,vflag); + else force->kspace->compute_dummy(eflag,vflag); + } + + modify->setup_pre_reverse(eflag,vflag); + if (force->newton) comm->reverse_comm(); + + modify->setup(vflag); + output->setup(flag); + update->setupflag = 0; +} + +/* ---------------------------------------------------------------------- + setup without output + flag = 0 = just force calculation + flag = 1 = reneighbor and force calculation +------------------------------------------------------------------------- */ + +void Verlet2::setup_minimal(int flag) +{ + update->setupflag = 1; + + // setup domain, communication and neighboring + // acquire ghosts + // build neighbor lists + + if (flag) { + modify->setup_pre_exchange(); + if (triclinic) domain->x2lamda(atom->nlocal); + domain->pbc(); + domain->reset_box(); + comm->setup(); + if (neighbor->style) neighbor->setup_bins(); + comm->exchange(); + comm->borders(); + if (triclinic) domain->lamda2x(atom->nlocal+atom->nghost); + domain->image_check(); + domain->box_too_small_check(); + modify->setup_pre_neighbor(); + neighbor->build(1); + modify->setup_post_neighbor(); + neighbor->ncalls = 0; + } + + // compute all forces + + ev_set(update->ntimestep); + force_clear(); + modify->setup_pre_force(vflag); + + if (pair_compute_flag) force->pair->compute(eflag,vflag); + else if (force->pair) force->pair->compute_dummy(eflag,vflag); + + if (atom->molecular != Atom::ATOMIC) { + if (force->bond) force->bond->compute(eflag,vflag); + if (force->angle) force->angle->compute(eflag,vflag); + if (force->dihedral) force->dihedral->compute(eflag,vflag); + if (force->improper) force->improper->compute(eflag,vflag); + } + + if (force->kspace) { + force->kspace->setup(); + if (kspace_compute_flag) force->kspace->compute(eflag,vflag); + else force->kspace->compute_dummy(eflag,vflag); + } + + modify->setup_pre_reverse(eflag,vflag); + if (force->newton) comm->reverse_comm(); + + modify->setup(vflag); + update->setupflag = 0; +} + +/* ---------------------------------------------------------------------- + run for N steps +------------------------------------------------------------------------- */ + +void Verlet2::run(int n) +{ + bigint ntimestep; + int nflag,sortflag; + + int n_post_integrate = modify->n_post_integrate; + int n_pre_exchange = modify->n_pre_exchange; + int n_pre_neighbor = modify->n_pre_neighbor; + int n_post_neighbor = modify->n_post_neighbor; + int n_pre_force = modify->n_pre_force; + int n_pre_reverse = modify->n_pre_reverse; + int n_post_force_any = modify->n_post_force_any; + int n_end_of_step = modify->n_end_of_step; + + if (atom->sortfreq > 0) sortflag = 1; + else sortflag = 0; + + for (int i = 0; i < n; i++) { + if (timer->check_timeout(i)) { + update->nsteps = i; + break; + } + + ntimestep = ++update->ntimestep; + ev_set(ntimestep); + + // initial time integration + + timer->stamp(); + modify->initial_integrate(vflag); + if (n_post_integrate) modify->post_integrate(); + timer->stamp(Timer::MODIFY); + + // regular communication vs neighbor list rebuild + + nflag = neighbor->decide(); + + if (nflag == 0) { + timer->stamp(); + comm->forward_comm(); + timer->stamp(Timer::COMM); + } else { + if (n_pre_exchange) { + timer->stamp(); + modify->pre_exchange(); + timer->stamp(Timer::MODIFY); + } + if (triclinic) domain->x2lamda(atom->nlocal); + domain->pbc(); + if (domain->box_change) { + domain->reset_box(); + comm->setup(); + if (neighbor->style) neighbor->setup_bins(); + } + timer->stamp(); + comm->exchange(); + if (sortflag && ntimestep >= atom->nextsort) atom->sort(); + comm->borders(); + if (triclinic) domain->lamda2x(atom->nlocal+atom->nghost); + timer->stamp(Timer::COMM); + if (n_pre_neighbor) { + modify->pre_neighbor(); + timer->stamp(Timer::MODIFY); + } + neighbor->build(1); + timer->stamp(Timer::NEIGH); + if (n_post_neighbor) { + modify->post_neighbor(); + timer->stamp(Timer::MODIFY); + } + } + + // force computations + // important for pair to come before bonded contributions + // since some bonded potentials tally pairwise energy/virial + // and Pair:ev_tally() needs to be called before any tallying + + force_clear(); + + timer->stamp(); + + if (n_pre_force) { + modify->pre_force(vflag); + timer->stamp(Timer::MODIFY); + } + + if (pair_compute_flag) { + force->pair->compute(eflag,vflag); + timer->stamp(Timer::PAIR); + } + + if (atom->molecular != Atom::ATOMIC) { + if (force->bond) force->bond->compute(eflag,vflag); + if (force->angle) force->angle->compute(eflag,vflag); + if (force->dihedral) force->dihedral->compute(eflag,vflag); + if (force->improper) force->improper->compute(eflag,vflag); + timer->stamp(Timer::BOND); + } + + if (kspace_compute_flag) { + force->kspace->compute(eflag,vflag); + timer->stamp(Timer::KSPACE); + } + + if (n_pre_reverse) { + modify->pre_reverse(eflag,vflag); + timer->stamp(Timer::MODIFY); + } + + // reverse communication of forces + + if (force->newton) { + comm->reverse_comm(); + timer->stamp(Timer::COMM); + } + + // force modifications, final time integration, diagnostics + + if (n_post_force_any) modify->post_force(vflag); + modify->final_integrate(); + if (n_end_of_step) modify->end_of_step(); + timer->stamp(Timer::MODIFY); + + // all output + + if (ntimestep == output->next) { + timer->stamp(); + output->write(ntimestep); + timer->stamp(Timer::OUTPUT); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void Verlet2::cleanup() +{ + modify->post_run(); + domain->box_too_small_check(); + update->update_time(); +} + +/* ---------------------------------------------------------------------- + clear force on own & ghost atoms + clear other arrays as needed +------------------------------------------------------------------------- */ + +void Verlet2::force_clear() +{ + size_t nbytes; + + if (external_force_clear) return; + + // clear force on all particles + // if either newton flag is set, also include ghosts + // when using threads always clear all forces. + + int nlocal = atom->nlocal; + + if (neighbor->includegroup == 0) { + nbytes = sizeof(double) * nlocal; + if (force->newton) nbytes += sizeof(double) * atom->nghost; + + if (nbytes) { + memset(&atom->f[0][0],0,3*nbytes); + if (torqueflag) memset(&atom->torque[0][0],0,3*nbytes); + if (extraflag) atom->avec->force_clear(0,nbytes); + } + + // neighbor includegroup flag is set + // clear force only on initial nfirst particles + // if either newton flag is set, also include ghosts + + } else { + nbytes = sizeof(double) * atom->nfirst; + + if (nbytes) { + memset(&atom->f[0][0],0,3*nbytes); + if (torqueflag) memset(&atom->torque[0][0],0,3*nbytes); + if (extraflag) atom->avec->force_clear(0,nbytes); + } + + if (force->newton) { + nbytes = sizeof(double) * atom->nghost; + + if (nbytes) { + memset(&atom->f[nlocal][0],0,3*nbytes); + if (torqueflag) memset(&atom->torque[nlocal][0],0,3*nbytes); + if (extraflag) atom->avec->force_clear(nlocal,nbytes); + } + } + } +} diff --git a/examples/plugins/verlet2.h b/examples/plugins/verlet2.h new file mode 100644 index 0000000000..ece4bd45d9 --- /dev/null +++ b/examples/plugins/verlet2.h @@ -0,0 +1,39 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + 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_VERLET2_H +#define LMP_VERLET2_H + +#include "integrate.h" + +namespace LAMMPS_NS { + +class Verlet2 : public Integrate { + public: + Verlet2(class LAMMPS *, int, char **); + ~Verlet2() override; + void init() override; + void setup(int flag) override; + void setup_minimal(int) override; + void run(int) override; + void force_clear() override; + void cleanup() override; + + protected: + int triclinic; // 0 if domain is orthog, 1 if triclinic + int torqueflag, extraflag; +}; + +} // namespace LAMMPS_NS + +#endif diff --git a/src/PLUGIN/plugin.cpp b/src/PLUGIN/plugin.cpp index f4a0903be4..0a52e027f3 100644 --- a/src/PLUGIN/plugin.cpp +++ b/src/PLUGIN/plugin.cpp @@ -21,6 +21,7 @@ #include "force.h" #include "input.h" #include "modify.h" +#include "update.h" #include #include @@ -249,6 +250,22 @@ void plugin_register(lammpsplugin_t *plugin, void *ptr) } (*command_map)[plugin->name] = (Input::CommandCreator) plugin->creator.v1; + } else if (pstyle == "run") { + auto integrate_map = lmp->update->integrate_map; + if (integrate_map->find(plugin->name) != integrate_map->end()) { + if (lmp->comm->me == 0) + lmp->error->warning(FLERR, "Overriding built-in run style {} from plugin", plugin->name); + } + (*integrate_map)[plugin->name] = (Update::IntegrateCreator) plugin->creator.v2; + + } else if (pstyle == "min") { + auto minimize_map = lmp->update->minimize_map; + if (minimize_map->find(plugin->name) != minimize_map->end()) { + if (lmp->comm->me == 0) + lmp->error->warning(FLERR, "Overriding built-in run style {} from plugin", plugin->name); + } + (*minimize_map)[plugin->name] = (Update::MinimizeCreator) plugin->creator.v1; + } else { utils::logmesg(lmp, "Loading plugins for {} styles not yet implemented\n", pstyle); pluginlist.pop_back(); @@ -272,7 +289,8 @@ void plugin_unload(const char *style, const char *name, LAMMPS *lmp) (strcmp(style, "angle") != 0) && (strcmp(style, "dihedral") != 0) && (strcmp(style, "improper") != 0) && (strcmp(style, "kspace") != 0) && (strcmp(style, "compute") != 0) && (strcmp(style, "fix") != 0) && - (strcmp(style, "region") != 0) && (strcmp(style, "command") != 0)) { + (strcmp(style, "region") != 0) && (strcmp(style, "command") != 0) && + (strcmp(style, "run") != 0) && (strcmp(style, "min") != 0)) { if (me == 0) utils::logmesg(lmp, "Ignoring unload: {} is not a supported plugin style\n", style); return; @@ -395,6 +413,18 @@ void plugin_unload(const char *style, const char *name, LAMMPS *lmp) auto command_map = lmp->input->command_map; auto found = command_map->find(name); if (found != command_map->end()) command_map->erase(name); + + } else if (pstyle == "run") { + + auto integrate_map = lmp->update->integrate_map; + auto found = integrate_map->find(name); + if (found != integrate_map->end()) integrate_map->erase(name); + + } else if (pstyle == "min") { + + auto minimize_map = lmp->update->minimize_map; + auto found = minimize_map->find(name); + if (found != minimize_map->end()) minimize_map->erase(name); } // if reference count is down to zero, close DSO handle. @@ -410,7 +440,7 @@ void plugin_unload(const char *style, const char *name, LAMMPS *lmp) void plugin_clear(LAMMPS *lmp) { - verbose = false; + verbose = true; while (pluginlist.size() > 0) { auto p = pluginlist.begin(); plugin_unload(p->style, p->name, lmp); From df08818fef3b4c401152e7568a5eaadfd930ff58 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 11 Jun 2025 02:52:28 -0400 Subject: [PATCH 4/8] refactor handling of plugin loading to make plugin a global property --- doc/src/Fortran.rst | 15 +- doc/src/Library_create.rst | 6 + doc/src/plugin.rst | 38 +++- examples/COUPLE/plugin/liblammpsplugin.c | 1 + examples/COUPLE/plugin/liblammpsplugin.h | 1 + fortran/lammps.f90 | 10 +- python/lammps/core.py | 7 +- src/PLUGIN/plugin.cpp | 222 ++++++++++++++++++++--- src/PLUGIN/plugin.h | 2 + src/lammps.cpp | 8 +- src/library.cpp | 39 +++- src/library.h | 1 + src/main.cpp | 1 + src/update.cpp | 7 + tools/swig/lammps.i | 2 + 15 files changed, 305 insertions(+), 55 deletions(-) diff --git a/doc/src/Fortran.rst b/doc/src/Fortran.rst index 0a8434f63d..3d61473068 100644 --- a/doc/src/Fortran.rst +++ b/doc/src/Fortran.rst @@ -69,10 +69,11 @@ statement. Internally, it will call either :cpp:func:`lammps_open_fortran` or :cpp:func:`lammps_open_no_mpi` from the C library API to create the class instance. All arguments are optional and :cpp:func:`lammps_mpi_init` will be called automatically -if it is needed. Similarly, a possible call to -:cpp:func:`lammps_mpi_finalize` is integrated into the :f:func:`close` -function and triggered with the optional logical argument set to -``.TRUE.``. Here is a simple example: +if it is needed. Similarly, optional calls to +:cpp:func:`lammps_mpi_finalize`, :cpp:func:`lammps_kokkos_finalize`, +:cpp:func:`lammps_python_finalize`, and :cpp:func:`lammps_plugin_finalize` +are integrated into the :f:func:`close` function and triggered with the +optional logical argument set to ``.TRUE.``. Here is a simple example: .. code-block:: fortran @@ -521,8 +522,8 @@ Procedures Bound to the :f:type:`lammps` Derived Type This method will close down the LAMMPS instance through calling :cpp:func:`lammps_close`. If the *finalize* argument is present and has a value of ``.TRUE.``, then this subroutine also calls - :cpp:func:`lammps_kokkos_finalize` and - :cpp:func:`lammps_mpi_finalize`. + :cpp:func:`lammps_kokkos_finalize`, :cpp:func:`lammps_mpi_finalize`, + :cpp:func:`lammps_python_finalize`, and :cpp:func:`lammps_plugin_finalize`. :o finalize: shut down the MPI environment of the LAMMPS library if ``.TRUE.``. @@ -530,6 +531,8 @@ Procedures Bound to the :f:type:`lammps` Derived Type :to: :cpp:func:`lammps_close` :to: :cpp:func:`lammps_mpi_finalize` :to: :cpp:func:`lammps_kokkos_finalize` + :to: :cpp:func:`lammps_python_finalize` + :to: :cpp:func:`lammps_plugin_finalize` -------- diff --git a/doc/src/Library_create.rst b/doc/src/Library_create.rst index 546db9b3be..5566b04e9b 100644 --- a/doc/src/Library_create.rst +++ b/doc/src/Library_create.rst @@ -11,6 +11,7 @@ This section documents the following functions: - :cpp:func:`lammps_mpi_finalize` - :cpp:func:`lammps_kokkos_finalize` - :cpp:func:`lammps_python_finalize` +- :cpp:func:`lammps_plugin_finalize` - :cpp:func:`lammps_error` -------------------- @@ -119,5 +120,10 @@ calling program. ----------------------- +.. doxygenfunction:: lammps_plugin_finalize + :project: progguide + +----------------------- + .. doxygenfunction:: lammps_error :project: progguide diff --git a/doc/src/plugin.rst b/doc/src/plugin.rst index 83a20b3248..a6ce845309 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* or *clear* +* command = *load* or *unload* or *list* or *clear* or *restore* * args = list of arguments for a particular plugin command .. parsed-literal:: @@ -20,6 +20,7 @@ Syntax *style* = *pair* or *bond* or *angle* or *dihedral* or *improper* or *kspace* or *compute* or *fix* or *region* or *command* or *run* or *min* *list* = print a list of currently loaded plugins *clear* = unload all currently loaded plugins + *restore* = restore all loaded plugins Examples """""""" @@ -31,6 +32,7 @@ Examples plugin unload command hello plugin list plugin clear + plugin restore Description """"""""""" @@ -40,22 +42,46 @@ 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. +.. admonition:: Plugins are a global, per-executable property + :class: Hint + + Unlike most settings in LAMMPS, plugins are a per-executable global + property. Loading a plugin means that it is not only available for + the current LAMMPS instance but for all *future* LAMMPS instances. + + After a :doc:`clear ` command, all currently loaded plugins + will be restored and do not need to be loaded again. + + When using the library interface or the Python or Fortran module + to create multiple concurrent LAMMPS instances, all plugins should + be loaded by the first created LAMMPS instance as all future instances + will inherit them. To import plugins that were loaded by a different + LAMMPS instance, use the *restore* command. + + 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 +plugin DSO with the given filename. A message with information about +the plugin style and name and more will be printed. Individual DSO +files may contain multiple plugins. If a plugin is already loaded +it will be skipped. 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. +that style instance will be deleted and replaced by the default setting +for that style. 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. +.. versionadded:: TBD + +The *restore* command will restore all currently loaded plugins. +This allows to "import" plugins into a different LAMMPS instance. + .. admonition:: Automatic loading of plugins :class: note @@ -79,7 +105,7 @@ 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 +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. There are no checks, so if there is a mismatch the plugin object diff --git a/examples/COUPLE/plugin/liblammpsplugin.c b/examples/COUPLE/plugin/liblammpsplugin.c index 5003b3826b..21f330df9e 100644 --- a/examples/COUPLE/plugin/liblammpsplugin.c +++ b/examples/COUPLE/plugin/liblammpsplugin.c @@ -79,6 +79,7 @@ liblammpsplugin_t *liblammpsplugin_load(const char *lib) ADDSYM(mpi_finalize); ADDSYM(kokkos_finalize); ADDSYM(python_finalize); + ADDSYM(plugin_finalize); ADDSYM(error); ADDSYM(expand); diff --git a/examples/COUPLE/plugin/liblammpsplugin.h b/examples/COUPLE/plugin/liblammpsplugin.h index 73cef7bc19..637f3db79b 100644 --- a/examples/COUPLE/plugin/liblammpsplugin.h +++ b/examples/COUPLE/plugin/liblammpsplugin.h @@ -134,6 +134,7 @@ struct _liblammpsplugin { void (*mpi_finalize)(); void (*kokkos_finalize)(); void (*python_finalize)(); + void (*plugin_finalize)(); void (*error)(void *, int, const char *); char *(*expand)(void *, const char *); diff --git a/fortran/lammps.f90 b/fortran/lammps.f90 index 4baff4d03a..ed9e7b2b6b 100644 --- a/fortran/lammps.f90 +++ b/fortran/lammps.f90 @@ -408,6 +408,12 @@ MODULE LIBLAMMPS SUBROUTINE lammps_kokkos_finalize() BIND(C) END SUBROUTINE lammps_kokkos_finalize + SUBROUTINE lammps_python_finalize() BIND(C) + END SUBROUTINE lammps_python_finalize + + SUBROUTINE lammps_plugin_finalize() BIND(C) + END SUBROUTINE lammps_plugin_finalize + SUBROUTINE lammps_error(handle, error_type, error_text) BIND(C) IMPORT :: c_ptr, c_int IMPLICIT NONE @@ -1135,7 +1141,7 @@ CONTAINS SIZE_IMAGEINT = lmp_extract_setting(lmp_open, 'imageint') END FUNCTION lmp_open - ! Combined Fortran wrapper around lammps_close() and lammps_mpi_finalize() + ! Combined Fortran wrapper around lammps_close() and lammps_*_finalize() SUBROUTINE lmp_close(self, finalize) CLASS(lammps), INTENT(IN) :: self LOGICAL, INTENT(IN), OPTIONAL :: finalize @@ -1146,6 +1152,8 @@ CONTAINS IF (finalize) THEN CALL lammps_kokkos_finalize() CALL lammps_mpi_finalize() + CALL lammps_python_finalize() + CALL lammps_plugin_finalize() END IF END IF END SUBROUTINE lmp_close diff --git a/python/lammps/core.py b/python/lammps/core.py index d808fc713f..cab3171cd3 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -653,8 +653,9 @@ class lammps(object): def finalize(self): """Shut down the MPI communication and Kokkos environment (if active) through the - library interface by calling :cpp:func:`lammps_mpi_finalize` and - :cpp:func:`lammps_kokkos_finalize`. + library interface by calling :cpp:func:`lammps_mpi_finalize`, + :cpp:func:`lammps_kokkos_finalize`, :cpp:func:`lammps_python_finalize`, and + :cpp:func:`lammps_plugin_finalize` You cannot create or use any LAMMPS instances after this function is called unless LAMMPS was compiled without MPI and without Kokkos support. @@ -662,6 +663,8 @@ class lammps(object): self.close() self.lib.lammps_kokkos_finalize() self.lib.lammps_mpi_finalize() + self.lib.lammps_python_finalize() + self.lib.lammps_plugin_finalize() # ------------------------------------------------------------------------- diff --git a/src/PLUGIN/plugin.cpp b/src/PLUGIN/plugin.cpp index 0a52e027f3..7326563003 100644 --- a/src/PLUGIN/plugin.cpp +++ b/src/PLUGIN/plugin.cpp @@ -58,6 +58,9 @@ void Plugin::command(int narg, char **arg) } else if (cmd == "clear") { plugin_clear(lmp); + } else if (cmd == "restore") { + plugin_restore(lmp, false); + } else if (cmd == "list") { if (comm->me == 0) { int num = plugin_get_num_plugins(); @@ -68,15 +71,16 @@ void Plugin::command(int narg, char **arg) } } } else - error->all(FLERR, "Illegal plugin command"); + error->all(FLERR, "Unknown plugin command {}", cmd); } // auto-load DSOs from designated folder(s) void plugin_auto_load(LAMMPS *lmp) { #if defined(LMP_PLUGIN) + bool oldverbose = verbose; + verbose = false; for (const auto &plugin_dir : platform::list_pathenv("LAMMPS_PLUGIN_PATH")) { - verbose = false; int count = 0; for (const auto &file : platform::list_directory(plugin_dir)) { if (utils::strmatch(file, "\\plugin.so$")) @@ -84,6 +88,7 @@ void plugin_auto_load(LAMMPS *lmp) } if (lmp->comm->me == 0) utils::logmesg(lmp, "Loaded {} plugins from {}\n", count, plugin_dir); } + verbose = oldverbose; #endif } @@ -262,7 +267,8 @@ void plugin_register(lammpsplugin_t *plugin, void *ptr) auto minimize_map = lmp->update->minimize_map; if (minimize_map->find(plugin->name) != minimize_map->end()) { if (lmp->comm->me == 0) - lmp->error->warning(FLERR, "Overriding built-in run style {} from plugin", plugin->name); + lmp->error->warning(FLERR, "Overriding built-in minimize style {} from plugin", + plugin->name); } (*minimize_map)[plugin->name] = (Update::MinimizeCreator) plugin->creator.v1; @@ -319,9 +325,6 @@ void plugin_unload(const char *style, const char *name, LAMMPS *lmp) 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) { @@ -332,82 +335,90 @@ void plugin_unload(const char *style, const char *name, LAMMPS *lmp) } } - } else if (pstyle == "bond") { + auto found = lmp->force->pair_map->find(name); + if (found != lmp->force->pair_map->end()) lmp->force->pair_map->erase(found); - auto found = lmp->force->bond_map->find(name); - if (found != lmp->force->bond_map->end()) lmp->force->bond_map->erase(found); + } else if (pstyle == "bond") { // 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->bond_map->find(name); + if (found != lmp->force->bond_map->end()) lmp->force->bond_map->erase(found); - auto found = lmp->force->angle_map->find(name); - if (found != lmp->force->angle_map->end()) lmp->force->angle_map->erase(found); + } else if (pstyle == "angle") { // 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->angle_map->find(name); + if (found != lmp->force->angle_map->end()) lmp->force->angle_map->erase(found); - auto found = lmp->force->dihedral_map->find(name); - if (found != lmp->force->dihedral_map->end()) lmp->force->dihedral_map->erase(found); + } else if (pstyle == "dihedral") { // 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->dihedral_map->find(name); + if (found != lmp->force->dihedral_map->end()) lmp->force->dihedral_map->erase(found); - auto found = lmp->force->improper_map->find(name); - if (found != lmp->force->improper_map->end()) lmp->force->improper_map->erase(found); + } else if (pstyle == "improper") { // 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); + auto found = lmp->force->improper_map->find(name); + if (found != lmp->force->improper_map->end()) lmp->force->improper_map->erase(found); + } else if (pstyle == "kspace") { + // must delete kspace style instance if in use + + if ((lmp->force->kspace_style != nullptr) && (lmp->force->kspace_match(name, 1) != nullptr)) + lmp->force->create_kspace("none", 0); + auto kspace_map = lmp->force->kspace_map; auto found = kspace_map->find(name); if (found != kspace_map->end()) kspace_map->erase(name); } 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); - // must delete all compute instances using this compute style for (auto &icompute : lmp->modify->get_compute_by_style(name)) lmp->modify->delete_compute(icompute->id); - } else if (pstyle == "fix") { + auto compute_map = lmp->modify->compute_map; + auto found = compute_map->find(name); + if (found != compute_map->end()) compute_map->erase(name); - auto fix_map = lmp->modify->fix_map; - auto found = fix_map->find(name); - if (found != fix_map->end()) fix_map->erase(name); + } else if (pstyle == "fix") { // must delete all fix instances using this fix style for (auto &ifix : lmp->modify->get_fix_by_style(name)) lmp->modify->delete_fix(ifix->id); + auto fix_map = lmp->modify->fix_map; + auto found = fix_map->find(name); + if (found != fix_map->end()) fix_map->erase(name); + } else if (pstyle == "region") { + for (auto &iregion : lmp->domain->get_region_by_style(name)) + lmp->domain->delete_region(iregion); + auto region_map = lmp->domain->region_map; auto found = region_map->find(name); if (found != region_map->end()) region_map->erase(name); - for (auto &iregion : lmp->domain->get_region_by_style(name)) - lmp->domain->delete_region(iregion); - } else if (pstyle == "command") { auto command_map = lmp->input->command_map; @@ -416,12 +427,24 @@ void plugin_unload(const char *style, const char *name, LAMMPS *lmp) } else if (pstyle == "run") { + // must restore default run style if plugin style is in use + + if (strcmp(name, lmp->update->integrate_style) == 0) { + char *str = (char *) "verlet"; + lmp->update->create_integrate(1, &str, 1); + } auto integrate_map = lmp->update->integrate_map; auto found = integrate_map->find(name); if (found != integrate_map->end()) integrate_map->erase(name); } else if (pstyle == "min") { + // must restore default minimize style if plugin style is in use + + if (strcmp(name, lmp->update->minimize_style) == 0) { + char *str = (char *) "cg"; + lmp->update->create_minimize(1, &str, 1); + } auto minimize_map = lmp->update->minimize_map; auto found = minimize_map->find(name); if (found != minimize_map->end()) minimize_map->erase(name); @@ -434,18 +457,157 @@ void plugin_unload(const char *style, const char *name, LAMMPS *lmp) #endif } +/* -------------------------------------------------------------------- + restore previously loaded plugins + -------------------------------------------------------------------- */ + +void plugin_restore(LAMMPS *lmp, bool warnflag) +{ + for (auto &plugin : pluginlist) { + if (lmp->comm->me == 0) + utils::logmesg(lmp, "Restoring plugin: {} by {}\n", plugin.info, plugin.author); + + std::string pstyle = plugin.style; + if (pstyle == "pair") { + auto pair_map = lmp->force->pair_map; + if (pair_map->find(plugin.name) != pair_map->end()) { + if (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "Overriding built-in pair style {} from plugin", plugin.name); + } + (*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 (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "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 (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "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 (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "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 (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "Overriding built-in improper style {} from plugin", + plugin.name); + } + (*improper_map)[plugin.name] = (Force::ImproperCreator) plugin.creator.v1; + + } else if (pstyle == "kspace") { + auto kspace_map = lmp->force->kspace_map; + if (kspace_map->find(plugin.name) != kspace_map->end()) { + if (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "Overriding built-in kspace style {} from plugin", + plugin.name); + } + (*kspace_map)[plugin.name] = (Force::KSpaceCreator) plugin.creator.v1; + + } else if (pstyle == "compute") { + auto compute_map = lmp->modify->compute_map; + if (compute_map->find(plugin.name) != compute_map->end()) { + if (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "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()) { + if (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "Overriding built-in fix style {} from plugin", plugin.name); + } + (*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 (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "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()) { + if (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "Overriding built-in command style {} from plugin", + plugin.name); + } + (*command_map)[plugin.name] = (Input::CommandCreator) plugin.creator.v1; + + } else if (pstyle == "run") { + auto integrate_map = lmp->update->integrate_map; + if (integrate_map->find(plugin.name) != integrate_map->end()) { + if (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "Overriding built-in run style {} from plugin", plugin.name); + } + (*integrate_map)[plugin.name] = (Update::IntegrateCreator) plugin.creator.v2; + + } else if (pstyle == "min") { + auto minimize_map = lmp->update->minimize_map; + if (minimize_map->find(plugin.name) != minimize_map->end()) { + if (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "Overriding built-in minimize style {} from plugin", + plugin.name); + } + (*minimize_map)[plugin.name] = (Update::MinimizeCreator) plugin.creator.v1; + } + } +} + /* -------------------------------------------------------------------- unload all loaded plugins -------------------------------------------------------------------- */ void plugin_clear(LAMMPS *lmp) { + bool oldverbose = verbose; verbose = true; while (pluginlist.size() > 0) { auto p = pluginlist.begin(); plugin_unload(p->style, p->name, lmp); } - verbose = true; + verbose = oldverbose; +} + +/* -------------------------------------------------------------------- + unload all shared objects + -------------------------------------------------------------------- */ + +void plugin_finalize() +{ +#if defined(LMP_PLUGIN) + while (pluginlist.size() > 0) { + auto p = pluginlist.begin(); + + void *handle = p->handle; + plugin_erase(p->style, p->name); + + // if reference count is down to zero, close DSO handle. + + --dso_refcounter[handle]; + if (dso_refcounter[handle] == 0) { platform::dlclose(handle); } + } +#endif } /* -------------------------------------------------------------------- diff --git a/src/PLUGIN/plugin.h b/src/PLUGIN/plugin.h index 4d8d6c29f8..838f32581c 100644 --- a/src/PLUGIN/plugin.h +++ b/src/PLUGIN/plugin.h @@ -34,10 +34,12 @@ class Plugin : public Command { void plugin_auto_load(LAMMPS *); int plugin_load(const char *, LAMMPS *); void plugin_register(lammpsplugin_t *, void *); +void plugin_restore(LAMMPS *, bool); void plugin_unload(const char *, const char *, LAMMPS *); void plugin_erase(const char *, const char *); void plugin_clear(LAMMPS *); +void plugin_finalize(); int plugin_get_num_plugins(); int plugin_find(const char *, const char *); diff --git a/src/lammps.cpp b/src/lammps.cpp index c891cea14d..98839e33fb 100644 --- a/src/lammps.cpp +++ b/src/lammps.cpp @@ -883,8 +883,9 @@ void LAMMPS::create() python = new Python(this); - // auto-load plugins + // restore and auto-load plugins #if defined(LMP_PLUGIN) + plugin_restore(this, true); plugin_auto_load(this); #endif } @@ -991,11 +992,6 @@ void LAMMPS::init() void LAMMPS::destroy() { - // must wipe out all plugins first, if configured -#if defined(LMP_PLUGIN) - plugin_clear(this); -#endif - delete update; update = nullptr; diff --git a/src/library.cpp b/src/library.cpp index a8acbade52..aacea70269 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -357,7 +357,8 @@ completed and then MPI is cleanly shut down. After calling this function no more MPI calls may be made. *See also* - :cpp:func:`lammps_kokkos_finalize`, :cpp:func:`lammps_python_finalize` + :cpp:func:`lammps_kokkos_finalize`, :cpp:func:`lammps_python_finalize`, + :cpp:func:`lammps_plugin_finalize` \endverbatim */ void lammps_mpi_finalize() @@ -389,7 +390,8 @@ closed (to release associated resources). After calling this function no Kokkos functionality may be used. *See also* - :cpp:func:`lammps_mpi_finalize`, :cpp:func:`lammps_python_finalize` + :cpp:func:`lammps_mpi_finalize`, :cpp:func:`lammps_python_finalize`, + :cpp:func:`lammps_plugin_finalize` \endverbatim */ void lammps_kokkos_finalize() @@ -399,6 +401,35 @@ void lammps_kokkos_finalize() /* ---------------------------------------------------------------------- */ +/** Unload all plugins and release the corresponding DSO handles + * +\verbatim embed:rst + +.. versionadded:: TBD + +This function clears the list of all loaded plugins and closes the +corresponding DSO handles and releases the imported executable code. + +However, this is **not** done when a LAMMPS instance is deleted because +plugins and their shared objects are global properties. + +This function can be called to explicitly clear out all loaded plugins +in case it is safe to do so. + +*See also* + :cpp:func:`lammps_mpi_finalize`, :cpp:func:`lammps_kokkos_finalize`, + :cpp:func:`lammps_python_finalize` +\endverbatim */ + +void lammps_plugin_finalize() +{ +#if defined(LMP_PLUGIN) + plugin_finalize(); +#endif +} + +/* ---------------------------------------------------------------------- */ + /** Clear the embedded Python environment * \verbatim embed:rst @@ -425,7 +456,8 @@ This function can be called to explicitly clear the Python environment in case it is safe to do so. *See also* - :cpp:func:`lammps_mpi_finalize`, :cpp:func:`lammps_kokkos_finalize` + :cpp:func:`lammps_mpi_finalize`, :cpp:func:`lammps_kokkos_finalize`, + :cpp:func:`lammps_plugin_finalize` \endverbatim */ void lammps_python_finalize() @@ -433,7 +465,6 @@ void lammps_python_finalize() Python::finalize(); } - /* ---------------------------------------------------------------------- */ /** Call a LAMMPS Error class function diff --git a/src/library.h b/src/library.h index a4ca396628..08b619eb78 100644 --- a/src/library.h +++ b/src/library.h @@ -140,6 +140,7 @@ void lammps_close(void *handle); void lammps_mpi_init(); void lammps_mpi_finalize(); void lammps_kokkos_finalize(); +void lammps_plugin_finalize(); void lammps_python_finalize(); void lammps_error(void *handle, int error_type, const char *error_text); diff --git a/src/main.cpp b/src/main.cpp index 951d828b51..65f5a9658d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -35,6 +35,7 @@ static void finalize() { lammps_kokkos_finalize(); lammps_python_finalize(); + lammps_plugin_finalize(); } /* ---------------------------------------------------------------------- diff --git a/src/update.cpp b/src/update.cpp index 4140a31731..950ac59b40 100644 --- a/src/update.cpp +++ b/src/update.cpp @@ -99,6 +99,13 @@ Update::Update(LAMMPS *lmp) : Update::~Update() { + // restore default styles to avoid segfaults from plugins + char *str = (char *) "verlet"; + create_integrate(1, &str, 1); + + str = (char *) "cg"; + create_minimize(1, &str, 1); + delete[] unit_style; delete[] integrate_style; diff --git a/tools/swig/lammps.i b/tools/swig/lammps.i index 283b5d8b4e..6761ce7acf 100644 --- a/tools/swig/lammps.i +++ b/tools/swig/lammps.i @@ -116,6 +116,7 @@ extern void lammps_mpi_init(); extern void lammps_mpi_finalize(); extern void lammps_kokkos_finalize(); extern void lammps_python_finalize(); +extern void lammps_plugin_finalize(); extern void lammps_error(void *handle, int error_type, const char *error_text); extern char *lammps_expand(void *handle, const char *line); @@ -315,6 +316,7 @@ extern void lammps_mpi_init(); extern void lammps_mpi_finalize(); extern void lammps_kokkos_finalize(); extern void lammps_python_finalize(); +extern void lammps_plugin_finalize(); extern void lammps_error(void *handle, int error_type, const char *error_text); extern char *lammps_expand(void *handle, const char *line); From 0fd7a40d92419d5f9a091da827c5f873dbdc68d5 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 11 Jun 2025 03:40:33 -0400 Subject: [PATCH 5/8] update documentation for writing plugins --- doc/src/Developer_plugins.rst | 42 ++++++++++++++++++++++++----------- doc/src/plugin.rst | 2 +- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/doc/src/Developer_plugins.rst b/doc/src/Developer_plugins.rst index 354350dde7..c14c4c3a1c 100644 --- a/doc/src/Developer_plugins.rst +++ b/doc/src/Developer_plugins.rst @@ -68,24 +68,25 @@ Members of ``lammpsplugin_t`` * - author - String with the name and email of the author * - creator.v1 - - Pointer to factory function for pair, bond, angle, dihedral, improper, kspace, or command styles + - Pointer to factory function for pair, bond, angle, dihedral, improper, kspace, command, or minimize styles * - creator.v2 - - Pointer to factory function for compute, fix, or region styles + - Pointer to factory function for compute, fix, region, or run styles * - handle - Pointer to the open DSO file handle Only one of the two 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) or command 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. +(i.e. pair, bond, angle, dihedral, or improper styles), command styles, +or minimize 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, +region, or run 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. Pair style example ^^^^^^^^^^^^^^^^^^ @@ -247,8 +248,8 @@ DSO handle. The registration function is called with a pointer to the address of this struct and the pointer of the LAMMPS class. The registration function will then add the factory function of the plugin style to the respective style map under the provided name. It will also make a copy of the struct -in a list of all loaded plugins and update the reference counter for loaded -plugins from this specific DSO file. +in a global 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 @@ -263,6 +264,21 @@ the plugin will override the existing code. This can be used to modify the behavior of existing styles or to debug new versions of them without having to re-compile or re-install all of LAMMPS. +.. versionupdated:: 12Jun2025 + +When using the :doc:`clear ` command, plugins are not unloaded +but restored to their respective style maps. This also applies when +multiple LAMMPS instances are created and deleted through the library +interface. The :doc:`plugin load ` load command may be issued +again, but for existing plugins they will be skipped. To replace +plugins they must be explicitly unloaded with :doc:`plugin unload +`. When multiple LAMMPS instances are created concurrently, any +loaded plugins will be added to the global list of plugins, but are not +immediately available to any LAMMPS instance that was created before +loading the plugin. To "import" such plugins, the :doc:`plugin restore +` may be used. Plugins are only removed when they are explicitly +unloaded or the LAMMPS interface is "finalized". + Compiling plugins ^^^^^^^^^^^^^^^^^ diff --git a/doc/src/plugin.rst b/doc/src/plugin.rst index a6ce845309..e5198604c7 100644 --- a/doc/src/plugin.rst +++ b/doc/src/plugin.rst @@ -77,7 +77,7 @@ styles and names. The *clear* command will unload all currently loaded plugins. -.. versionadded:: TBD +.. versionadded:: 12Jun2025 The *restore* command will restore all currently loaded plugins. This allows to "import" plugins into a different LAMMPS instance. From 75c341543a91a8cb3be2fed50feba154a9fe34fa Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 11 Jun 2025 04:05:41 -0400 Subject: [PATCH 6/8] add missing comment --- src/PLUGIN/plugin.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/PLUGIN/plugin.cpp b/src/PLUGIN/plugin.cpp index 7326563003..600d2fd8fa 100644 --- a/src/PLUGIN/plugin.cpp +++ b/src/PLUGIN/plugin.cpp @@ -412,6 +412,8 @@ void plugin_unload(const char *style, const char *name, LAMMPS *lmp) } else if (pstyle == "region") { + // must delete all region instances using this region style + for (auto &iregion : lmp->domain->get_region_by_style(name)) lmp->domain->delete_region(iregion); From 6ccb1b745c577fc76ba26bae66b04255db78c631 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 11 Jun 2025 04:14:27 -0400 Subject: [PATCH 7/8] remove debug code, move to toplevel dir and document kspace style zero --- doc/src/Commands_kspace.rst | 2 ++ doc/src/Developer_plugins.rst | 2 +- doc/src/kspace_style.rst | 12 +++++++++++- examples/plugins/CMakeLists.txt | 1 - examples/plugins/kspace_zero2.cpp | 5 ----- examples/plugins/kspace_zero2.h | 1 - examples/plugins/kspaceplugin.cpp | 1 - src/{KSPACE => }/kspace_zero.cpp | 5 ----- src/{KSPACE => }/kspace_zero.h | 1 - 9 files changed, 14 insertions(+), 16 deletions(-) rename src/{KSPACE => }/kspace_zero.cpp (97%) rename src/{KSPACE => }/kspace_zero.h (97%) diff --git a/doc/src/Commands_kspace.rst b/doc/src/Commands_kspace.rst index 0d9b34a2cc..c37d9eee48 100644 --- a/doc/src/Commands_kspace.rst +++ b/doc/src/Commands_kspace.rst @@ -31,3 +31,5 @@ OPT. * :doc:`pppm/dielectric ` * :doc:`pppm/electrode (i) ` * :doc:`scafacos ` + * :doc:`zero ` + diff --git a/doc/src/Developer_plugins.rst b/doc/src/Developer_plugins.rst index c14c4c3a1c..a26a522a01 100644 --- a/doc/src/Developer_plugins.rst +++ b/doc/src/Developer_plugins.rst @@ -264,7 +264,7 @@ the plugin will override the existing code. This can be used to modify the behavior of existing styles or to debug new versions of them without having to re-compile or re-install all of LAMMPS. -.. versionupdated:: 12Jun2025 +.. versionchanged:: 12Jun2025 When using the :doc:`clear ` command, plugins are not unloaded but restored to their respective style maps. This also applies when diff --git a/doc/src/kspace_style.rst b/doc/src/kspace_style.rst index e7d5e93d72..3f9bdbb747 100644 --- a/doc/src/kspace_style.rst +++ b/doc/src/kspace_style.rst @@ -32,6 +32,7 @@ .. index:: kspace_style msm/cg/omp .. index:: kspace_style msm/dielectric .. index:: kspace_style scafacos +.. index:: kspace_style zero kspace_style command ==================== @@ -43,7 +44,7 @@ Syntax kspace_style style value -* style = *none* or *ewald* or *ewald/dipole* or *ewald/dipole/spin* or *ewald/disp* or *ewald/disp/dipole* or *ewald/omp* or *ewald/electrode* or *pppm* or *pppm/cg* or *pppm/disp* or *pppm/tip4p* or *pppm/stagger* or *pppm/disp/tip4p* or *pppm/gpu* or *pppm/intel* or *pppm/disp/intel* or *pppm/kk* or *pppm/omp* or *pppm/cg/omp* or *pppm/disp/tip4p/omp* or *pppm/tip4p/omp* or *pppm/dielectic* or *pppm/disp/dielectric* or *pppm/electrode* or *pppm/electrode/intel* or *msm* or *msm/cg* or *msm/omp* or *msm/cg/omp* or *msm/dielectric* or *scafacos* +* style = *none* or *ewald* or *ewald/dipole* or *ewald/dipole/spin* or *ewald/disp* or *ewald/disp/dipole* or *ewald/omp* or *ewald/electrode* or *pppm* or *pppm/cg* or *pppm/disp* or *pppm/tip4p* or *pppm/stagger* or *pppm/disp/tip4p* or *pppm/gpu* or *pppm/intel* or *pppm/disp/intel* or *pppm/kk* or *pppm/omp* or *pppm/cg/omp* or *pppm/disp/tip4p/omp* or *pppm/tip4p/omp* or *pppm/dielectic* or *pppm/disp/dielectric* or *pppm/electrode* or *pppm/electrode/intel* or *msm* or *msm/cg* or *msm/omp* or *msm/cg/omp* or *msm/dielectric* or *scafacos* or *zero* .. parsed-literal:: @@ -121,6 +122,7 @@ Syntax *scafacos* values = method accuracy method = fmm or p2nfft or p3m or ewald or direct accuracy = desired relative error in forces + *zero* value = none Examples """""""" @@ -132,6 +134,7 @@ Examples kspace_style msm 1.0e-4 kspace_style scafacos fmm 1.0e-4 kspace_style none + kspace_style zero Used in input scripts: @@ -375,6 +378,13 @@ other ScaFaCoS options currently exposed to LAMMPS. ---------- +.. versionadded:: 12Jun2025 + +The *zero* style does not do any calculations, but is compatible +with all pair styles that require some version of a kspace style. + +---------- + The specified *accuracy* determines the relative RMS error in per-atom forces calculated by the long-range solver. It is set as a dimensionless number, relative to the force that two unit point diff --git a/examples/plugins/CMakeLists.txt b/examples/plugins/CMakeLists.txt index 813cc9d1dc..836e1353b0 100644 --- a/examples/plugins/CMakeLists.txt +++ b/examples/plugins/CMakeLists.txt @@ -95,7 +95,6 @@ add_library(zero2plugin MODULE zero2plugin.cpp pair_zero2.cpp bond_zero2.cpp target_link_libraries(zero2plugin PRIVATE lammps) add_library(kspaceplugin MODULE kspaceplugin.cpp kspace_zero2.cpp) -target_include_directories(kspaceplugin PRIVATE "${LAMMPS_HEADER_DIR}/KSPACE") target_link_libraries(kspaceplugin PRIVATE lammps) add_library(runminplugin MODULE runminplugin.cpp min_cg2.cpp verlet2.cpp) diff --git a/examples/plugins/kspace_zero2.cpp b/examples/plugins/kspace_zero2.cpp index c5d6f1b73b..742f0871f5 100644 --- a/examples/plugins/kspace_zero2.cpp +++ b/examples/plugins/kspace_zero2.cpp @@ -41,11 +41,6 @@ KSpaceZero2::KSpaceZero2(LAMMPS *lmp) : KSpace(lmp) spinflag = 1; } -KSpaceZero2::~KSpaceZero2() -{ - fprintf(stderr, "In destructor for KSpace zero2. This = %p\n", this); -} - /* ---------------------------------------------------------------------- */ void KSpaceZero2::settings(int narg, char **arg) diff --git a/examples/plugins/kspace_zero2.h b/examples/plugins/kspace_zero2.h index 7539dd4609..033c2caee5 100644 --- a/examples/plugins/kspace_zero2.h +++ b/examples/plugins/kspace_zero2.h @@ -21,7 +21,6 @@ namespace LAMMPS_NS { class KSpaceZero2 : public KSpace { public: KSpaceZero2(class LAMMPS *); - ~KSpaceZero2() override; void init() override; void setup() override; diff --git a/examples/plugins/kspaceplugin.cpp b/examples/plugins/kspaceplugin.cpp index 3e7b4bd047..4b93aa27e7 100644 --- a/examples/plugins/kspaceplugin.cpp +++ b/examples/plugins/kspaceplugin.cpp @@ -15,7 +15,6 @@ using namespace LAMMPS_NS; static KSpace *zero2creator(LAMMPS *lmp) { KSpace *ptr = (KSpace *) new KSpaceZero2(lmp); - fprintf(stderr, "Created zero2 instance at %p\n", ptr); return ptr; } diff --git a/src/KSPACE/kspace_zero.cpp b/src/kspace_zero.cpp similarity index 97% rename from src/KSPACE/kspace_zero.cpp rename to src/kspace_zero.cpp index 028d42401e..6f3e891558 100644 --- a/src/KSPACE/kspace_zero.cpp +++ b/src/kspace_zero.cpp @@ -41,11 +41,6 @@ KSpaceZero::KSpaceZero(LAMMPS *lmp) : KSpace(lmp) spinflag = 1; } -KSpaceZero::~KSpaceZero() -{ - fprintf(stderr, "In destructor for KSpace zero. This = %p\n", this); -} - /* ---------------------------------------------------------------------- */ void KSpaceZero::settings(int narg, char **arg) diff --git a/src/KSPACE/kspace_zero.h b/src/kspace_zero.h similarity index 97% rename from src/KSPACE/kspace_zero.h rename to src/kspace_zero.h index 9c76c15de7..d55b437fc2 100644 --- a/src/KSPACE/kspace_zero.h +++ b/src/kspace_zero.h @@ -27,7 +27,6 @@ namespace LAMMPS_NS { class KSpaceZero : public KSpace { public: KSpaceZero(class LAMMPS *); - ~KSpaceZero() override; void init() override; void setup() override; From 2d8ef50e49ab1b0ddb472040d7ce4804a061b8af Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 11 Jun 2025 04:33:25 -0400 Subject: [PATCH 8/8] address output issue reported by GitHub Copilot (but properly and in both locations) --- examples/plugins/kspace_zero2.cpp | 2 +- src/kspace_zero.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/plugins/kspace_zero2.cpp b/examples/plugins/kspace_zero2.cpp index 742f0871f5..6c2a63339c 100644 --- a/examples/plugins/kspace_zero2.cpp +++ b/examples/plugins/kspace_zero2.cpp @@ -94,7 +94,7 @@ void KSpaceZero2::init() g_ewald = sqrt(-log(g_ewald)) / cutoff; } - if (comm->me == 0) std::string mesg = fmt::format(" G vector (1/distance) = {:.8g}\n", g_ewald); + if (comm->me == 0) utils::logmesg(lmp, " G vector (1/distance) = {:.8g}\n", g_ewald); } /* ---------------------------------------------------------------------- */ diff --git a/src/kspace_zero.cpp b/src/kspace_zero.cpp index 6f3e891558..0443898d8d 100644 --- a/src/kspace_zero.cpp +++ b/src/kspace_zero.cpp @@ -94,7 +94,7 @@ void KSpaceZero::init() g_ewald = sqrt(-log(g_ewald)) / cutoff; } - if (comm->me == 0) std::string mesg = fmt::format(" G vector (1/distance) = {:.8g}\n", g_ewald); + if (comm->me == 0) utils::logmesg(lmp, " G vector (1/distance) = {:.8g}\n", g_ewald); } /* ---------------------------------------------------------------------- */