From fb2cf0c32a7ca4ae73b2097221e8efc1641d37e4 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Thu, 4 Aug 2022 14:05:18 -0600 Subject: [PATCH 001/189] Prototyping new nstencil/npair classes --- src/{npair_full_bin.cpp => npair_bin.cpp} | 76 +++++- src/{npair_full_bin.h => npair_bin.h} | 25 +- src/npair_bin_atomonly.cpp | 148 +++++++++++ ...ll_bin_atomonly.h => npair_bin_atomonly.h} | 15 +- src/npair_copy.cpp | 38 --- src/npair_copy.h | 42 ---- src/npair_full_bin_atomonly.cpp | 94 ------- src/npair_full_bin_ghost.cpp | 156 ------------ src/npair_full_bin_ghost.h | 47 ---- src/npair_full_multi.cpp | 143 ----------- src/npair_full_multi.h | 46 ---- src/npair_full_multi_old.cpp | 133 ---------- src/npair_full_multi_old.h | 46 ---- src/npair_full_nsq.cpp | 125 ---------- src/npair_full_nsq.h | 46 ---- src/npair_full_nsq_ghost.cpp | 138 ----------- src/npair_full_nsq_ghost.h | 47 ---- src/npair_half_bin_atomonly_newton.cpp | 116 --------- src/npair_half_bin_atomonly_newton.h | 46 ---- src/npair_half_bin_newtoff.cpp | 129 ---------- src/npair_half_bin_newtoff.h | 46 ---- src/npair_half_bin_newtoff_ghost.cpp | 162 ------------ src/npair_half_bin_newtoff_ghost.h | 46 ---- src/npair_half_bin_newton.cpp | 161 ------------ src/npair_half_bin_newton.h | 46 ---- src/npair_half_bin_newton_tri.cpp | 134 ---------- src/npair_half_bin_newton_tri.h | 46 ---- src/npair_half_multi_newtoff.cpp | 146 ----------- src/npair_half_multi_newtoff.h | 46 ---- src/npair_half_multi_newton.cpp | 194 --------------- src/npair_half_multi_newton.h | 42 ---- src/npair_half_multi_newton_tri.cpp | 159 ------------ src/npair_half_multi_newton_tri.h | 42 ---- src/npair_half_multi_old_newtoff.cpp | 136 ---------- src/npair_half_multi_old_newtoff.h | 46 ---- src/npair_half_multi_old_newton.cpp | 169 ------------- src/npair_half_multi_old_newton.h | 46 ---- src/npair_half_multi_old_newton_tri.cpp | 144 ----------- src/npair_half_multi_old_newton_tri.h | 46 ---- src/npair_half_nsq_newtoff.cpp | 125 ---------- src/npair_half_nsq_newtoff.h | 46 ---- src/npair_half_nsq_newtoff_ghost.cpp | 150 ----------- src/npair_half_nsq_newtoff_ghost.h | 46 ---- src/npair_half_nsq_newton.cpp | 142 ----------- src/npair_half_nsq_newton.h | 46 ---- src/npair_half_respa_bin_newtoff.cpp | 187 -------------- src/npair_half_respa_bin_newtoff.h | 46 ---- src/npair_half_respa_bin_newton.cpp | 233 ------------------ src/npair_half_respa_bin_newton.h | 46 ---- src/npair_half_respa_bin_newton_tri.cpp | 195 --------------- src/npair_half_respa_bin_newton_tri.h | 46 ---- src/npair_half_respa_nsq_newtoff.cpp | 182 -------------- src/npair_half_respa_nsq_newtoff.h | 46 ---- src/npair_half_respa_nsq_newton.cpp | 202 --------------- src/npair_half_respa_nsq_newton.h | 46 ---- src/npair_half_size_bin_newtoff.cpp | 138 ----------- src/npair_half_size_bin_newtoff.h | 46 ---- src/npair_half_size_bin_newton.cpp | 176 ------------- src/npair_half_size_bin_newton.h | 46 ---- src/npair_half_size_bin_newton_tri.cpp | 147 ----------- src/npair_half_size_bin_newton_tri.h | 46 ---- src/npair_half_size_multi_newtoff.cpp | 160 ------------ src/npair_half_size_multi_newtoff.h | 46 ---- src/npair_half_size_multi_newton.cpp | 213 ---------------- src/npair_half_size_multi_newton.h | 46 ---- src/npair_half_size_multi_newton_tri.cpp | 172 ------------- src/npair_half_size_multi_newton_tri.h | 46 ---- src/npair_half_size_multi_old_newtoff.cpp | 149 ----------- src/npair_half_size_multi_old_newtoff.h | 45 ---- src/npair_half_size_multi_old_newton.cpp | 187 -------------- src/npair_half_size_multi_old_newton.h | 45 ---- src/npair_half_size_multi_old_newton_tri.cpp | 156 ------------ src/npair_half_size_multi_old_newton_tri.h | 45 ---- src/npair_half_size_nsq_newtoff.cpp | 136 ---------- src/npair_half_size_nsq_newtoff.h | 46 ---- src/npair_half_size_nsq_newton.cpp | 154 ------------ src/npair_half_size_nsq_newton.h | 46 ---- src/npair_halffull_newtoff.cpp | 83 ------- src/npair_halffull_newtoff.h | 62 ----- src/npair_halffull_newton.cpp | 97 -------- src/npair_halffull_newton.h | 52 ---- src/npair_skip.cpp | 102 -------- src/npair_skip.h | 54 ---- src/npair_skip_respa.cpp | 163 ------------ src/npair_skip_respa.h | 48 ---- src/npair_skip_size.cpp | 88 ------- src/npair_skip_size.h | 47 ---- src/npair_skip_size_off2on.cpp | 100 -------- src/npair_skip_size_off2on.h | 48 ---- src/npair_skip_size_off2on_oneside.cpp | 163 ------------ src/npair_skip_size_off2on_oneside.h | 48 ---- ...l_half_bin_3d_tri.cpp => nstencil_bin.cpp} | 41 ++- src/nstencil_bin.h | 69 ++++++ src/nstencil_full_bin_2d.cpp | 37 --- src/nstencil_full_bin_2d.h | 42 ---- src/nstencil_full_bin_3d.cpp | 38 --- src/nstencil_full_bin_3d.h | 42 ---- src/nstencil_full_ghost_bin_2d.cpp | 44 ---- src/nstencil_full_ghost_bin_2d.h | 42 ---- src/nstencil_full_ghost_bin_3d.cpp | 45 ---- src/nstencil_full_ghost_bin_3d.h | 42 ---- src/nstencil_full_multi_2d.cpp | 81 ------ src/nstencil_full_multi_2d.h | 44 ---- src/nstencil_full_multi_3d.cpp | 86 ------- src/nstencil_full_multi_3d.h | 44 ---- src/nstencil_full_multi_old_2d.cpp | 51 ---- src/nstencil_full_multi_old_2d.h | 42 ---- src/nstencil_full_multi_old_3d.cpp | 52 ---- src/nstencil_full_multi_old_3d.h | 42 ---- src/nstencil_half_bin_2d.cpp | 38 --- src/nstencil_half_bin_2d.h | 42 ---- src/nstencil_half_bin_2d_tri.cpp | 38 --- src/nstencil_half_bin_2d_tri.h | 42 ---- src/nstencil_half_bin_3d.cpp | 39 --- src/nstencil_half_bin_3d.h | 42 ---- src/nstencil_half_bin_3d_tri.h | 42 ---- src/nstencil_half_multi_2d.cpp | 103 -------- src/nstencil_half_multi_2d.h | 44 ---- src/nstencil_half_multi_2d_tri.cpp | 100 -------- src/nstencil_half_multi_2d_tri.h | 44 ---- src/nstencil_half_multi_3d.cpp | 109 -------- src/nstencil_half_multi_3d.h | 44 ---- src/nstencil_half_multi_3d_tri.cpp | 106 -------- src/nstencil_half_multi_3d_tri.h | 44 ---- src/nstencil_half_multi_old_2d.cpp | 53 ---- src/nstencil_half_multi_old_2d.h | 41 --- src/nstencil_half_multi_old_2d_tri.cpp | 52 ---- src/nstencil_half_multi_old_2d_tri.h | 41 --- src/nstencil_half_multi_old_3d.cpp | 54 ---- src/nstencil_half_multi_old_3d.h | 41 --- src/nstencil_half_multi_old_3d_tri.cpp | 53 ---- src/nstencil_half_multi_old_3d_tri.h | 41 --- 132 files changed, 349 insertions(+), 10531 deletions(-) rename src/{npair_full_bin.cpp => npair_bin.cpp} (58%) rename src/{npair_full_bin.h => npair_bin.h} (60%) create mode 100644 src/npair_bin_atomonly.cpp rename src/{npair_full_bin_atomonly.h => npair_bin_atomonly.h} (71%) delete mode 100644 src/npair_copy.cpp delete mode 100644 src/npair_copy.h delete mode 100644 src/npair_full_bin_atomonly.cpp delete mode 100644 src/npair_full_bin_ghost.cpp delete mode 100644 src/npair_full_bin_ghost.h delete mode 100644 src/npair_full_multi.cpp delete mode 100644 src/npair_full_multi.h delete mode 100644 src/npair_full_multi_old.cpp delete mode 100644 src/npair_full_multi_old.h delete mode 100644 src/npair_full_nsq.cpp delete mode 100644 src/npair_full_nsq.h delete mode 100644 src/npair_full_nsq_ghost.cpp delete mode 100644 src/npair_full_nsq_ghost.h delete mode 100644 src/npair_half_bin_atomonly_newton.cpp delete mode 100644 src/npair_half_bin_atomonly_newton.h delete mode 100644 src/npair_half_bin_newtoff.cpp delete mode 100644 src/npair_half_bin_newtoff.h delete mode 100644 src/npair_half_bin_newtoff_ghost.cpp delete mode 100644 src/npair_half_bin_newtoff_ghost.h delete mode 100644 src/npair_half_bin_newton.cpp delete mode 100644 src/npair_half_bin_newton.h delete mode 100644 src/npair_half_bin_newton_tri.cpp delete mode 100644 src/npair_half_bin_newton_tri.h delete mode 100644 src/npair_half_multi_newtoff.cpp delete mode 100644 src/npair_half_multi_newtoff.h delete mode 100644 src/npair_half_multi_newton.cpp delete mode 100644 src/npair_half_multi_newton.h delete mode 100644 src/npair_half_multi_newton_tri.cpp delete mode 100644 src/npair_half_multi_newton_tri.h delete mode 100644 src/npair_half_multi_old_newtoff.cpp delete mode 100644 src/npair_half_multi_old_newtoff.h delete mode 100644 src/npair_half_multi_old_newton.cpp delete mode 100644 src/npair_half_multi_old_newton.h delete mode 100644 src/npair_half_multi_old_newton_tri.cpp delete mode 100644 src/npair_half_multi_old_newton_tri.h delete mode 100644 src/npair_half_nsq_newtoff.cpp delete mode 100644 src/npair_half_nsq_newtoff.h delete mode 100644 src/npair_half_nsq_newtoff_ghost.cpp delete mode 100644 src/npair_half_nsq_newtoff_ghost.h delete mode 100644 src/npair_half_nsq_newton.cpp delete mode 100644 src/npair_half_nsq_newton.h delete mode 100644 src/npair_half_respa_bin_newtoff.cpp delete mode 100644 src/npair_half_respa_bin_newtoff.h delete mode 100644 src/npair_half_respa_bin_newton.cpp delete mode 100644 src/npair_half_respa_bin_newton.h delete mode 100644 src/npair_half_respa_bin_newton_tri.cpp delete mode 100644 src/npair_half_respa_bin_newton_tri.h delete mode 100644 src/npair_half_respa_nsq_newtoff.cpp delete mode 100644 src/npair_half_respa_nsq_newtoff.h delete mode 100644 src/npair_half_respa_nsq_newton.cpp delete mode 100644 src/npair_half_respa_nsq_newton.h delete mode 100644 src/npair_half_size_bin_newtoff.cpp delete mode 100644 src/npair_half_size_bin_newtoff.h delete mode 100644 src/npair_half_size_bin_newton.cpp delete mode 100644 src/npair_half_size_bin_newton.h delete mode 100644 src/npair_half_size_bin_newton_tri.cpp delete mode 100644 src/npair_half_size_bin_newton_tri.h delete mode 100644 src/npair_half_size_multi_newtoff.cpp delete mode 100644 src/npair_half_size_multi_newtoff.h delete mode 100644 src/npair_half_size_multi_newton.cpp delete mode 100644 src/npair_half_size_multi_newton.h delete mode 100644 src/npair_half_size_multi_newton_tri.cpp delete mode 100644 src/npair_half_size_multi_newton_tri.h delete mode 100644 src/npair_half_size_multi_old_newtoff.cpp delete mode 100644 src/npair_half_size_multi_old_newtoff.h delete mode 100644 src/npair_half_size_multi_old_newton.cpp delete mode 100644 src/npair_half_size_multi_old_newton.h delete mode 100644 src/npair_half_size_multi_old_newton_tri.cpp delete mode 100644 src/npair_half_size_multi_old_newton_tri.h delete mode 100644 src/npair_half_size_nsq_newtoff.cpp delete mode 100644 src/npair_half_size_nsq_newtoff.h delete mode 100644 src/npair_half_size_nsq_newton.cpp delete mode 100644 src/npair_half_size_nsq_newton.h delete mode 100644 src/npair_halffull_newtoff.cpp delete mode 100644 src/npair_halffull_newtoff.h delete mode 100644 src/npair_halffull_newton.cpp delete mode 100644 src/npair_halffull_newton.h delete mode 100644 src/npair_skip.cpp delete mode 100644 src/npair_skip.h delete mode 100644 src/npair_skip_respa.cpp delete mode 100644 src/npair_skip_respa.h delete mode 100644 src/npair_skip_size.cpp delete mode 100644 src/npair_skip_size.h delete mode 100644 src/npair_skip_size_off2on.cpp delete mode 100644 src/npair_skip_size_off2on.h delete mode 100644 src/npair_skip_size_off2on_oneside.cpp delete mode 100644 src/npair_skip_size_off2on_oneside.h rename src/{nstencil_half_bin_3d_tri.cpp => nstencil_bin.cpp} (51%) create mode 100644 src/nstencil_bin.h delete mode 100644 src/nstencil_full_bin_2d.cpp delete mode 100644 src/nstencil_full_bin_2d.h delete mode 100644 src/nstencil_full_bin_3d.cpp delete mode 100644 src/nstencil_full_bin_3d.h delete mode 100644 src/nstencil_full_ghost_bin_2d.cpp delete mode 100644 src/nstencil_full_ghost_bin_2d.h delete mode 100644 src/nstencil_full_ghost_bin_3d.cpp delete mode 100644 src/nstencil_full_ghost_bin_3d.h delete mode 100644 src/nstencil_full_multi_2d.cpp delete mode 100644 src/nstencil_full_multi_2d.h delete mode 100644 src/nstencil_full_multi_3d.cpp delete mode 100644 src/nstencil_full_multi_3d.h delete mode 100644 src/nstencil_full_multi_old_2d.cpp delete mode 100644 src/nstencil_full_multi_old_2d.h delete mode 100644 src/nstencil_full_multi_old_3d.cpp delete mode 100644 src/nstencil_full_multi_old_3d.h delete mode 100644 src/nstencil_half_bin_2d.cpp delete mode 100644 src/nstencil_half_bin_2d.h delete mode 100644 src/nstencil_half_bin_2d_tri.cpp delete mode 100644 src/nstencil_half_bin_2d_tri.h delete mode 100644 src/nstencil_half_bin_3d.cpp delete mode 100644 src/nstencil_half_bin_3d.h delete mode 100644 src/nstencil_half_bin_3d_tri.h delete mode 100644 src/nstencil_half_multi_2d.cpp delete mode 100644 src/nstencil_half_multi_2d.h delete mode 100644 src/nstencil_half_multi_2d_tri.cpp delete mode 100644 src/nstencil_half_multi_2d_tri.h delete mode 100644 src/nstencil_half_multi_3d.cpp delete mode 100644 src/nstencil_half_multi_3d.h delete mode 100644 src/nstencil_half_multi_3d_tri.cpp delete mode 100644 src/nstencil_half_multi_3d_tri.h delete mode 100644 src/nstencil_half_multi_old_2d.cpp delete mode 100644 src/nstencil_half_multi_old_2d.h delete mode 100644 src/nstencil_half_multi_old_2d_tri.cpp delete mode 100644 src/nstencil_half_multi_old_2d_tri.h delete mode 100644 src/nstencil_half_multi_old_3d.cpp delete mode 100644 src/nstencil_half_multi_old_3d.h delete mode 100644 src/nstencil_half_multi_old_3d_tri.cpp delete mode 100644 src/nstencil_half_multi_old_3d_tri.h diff --git a/src/npair_full_bin.cpp b/src/npair_bin.cpp similarity index 58% rename from src/npair_full_bin.cpp rename to src/npair_bin.cpp index 64404ccf8d..294336a03a 100644 --- a/src/npair_full_bin.cpp +++ b/src/npair_bin.cpp @@ -12,29 +12,33 @@ See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ -#include "npair_full_bin.h" +#include "npair_bin.h" #include "neigh_list.h" #include "atom.h" #include "atom_vec.h" #include "molecule.h" +#include "neighbor.h" #include "domain.h" #include "my_page.h" #include "error.h" using namespace LAMMPS_NS; +using namespace NeighConst; /* ---------------------------------------------------------------------- */ -NPairFullBin::NPairFullBin(LAMMPS *lmp) : NPair(lmp) {} +template +NPairBin::NPairBin(LAMMPS *lmp) : NPair(lmp) {} /* ---------------------------------------------------------------------- binned neighbor list construction for all neighbors every neighbor pair appears in list of both atoms i and j ------------------------------------------------------------------------- */ -void NPairFullBin::build(NeighList *list) +template +void NPairBin::build(NeighList *list) { - int i,j,k,n,itype,jtype,ibin,which,imol,iatom,moltemplate; + int i,j,k,n,itype,jtype,ibin,bin_start,which,imol,iatom,moltemplate; tagint tagprev; double xtmp,ytmp,ztmp,delx,dely,delz,rsq; int *neighptr; @@ -77,14 +81,59 @@ void NPairFullBin::build(NeighList *list) tagprev = tag[i] - iatom - 1; } - // loop over all atoms in surrounding bins in stencil including self - // skip i = j - ibin = atom2bin[i]; for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (i == j) continue; + bin_start = binhead[ibin+stencil[k]]; + if (stencil[k] == 0) { + if (HALF && NEWTON && (!TRI)) { + // Half neighbor list, newton on, orthonormal + // loop over rest of atoms in i's bin, ghosts are at end of linked list + bin_start = bins[i]; + } + } + + for (j = bin_start; j >= 0; j = bins[j]) { + if (!HALF) { + // Full neighbor list + // only skip i = j + if (i == j) continue; + } else if (!NEWTON) { + // Half neighbor list, newton off + // only store pair if i < j + // stores own/own pairs only once + // stores own/ghost pairs on both procs + if (j <= i) continue; + } else if (TRI) { + // Half neighbor list, newton on, triclinic + // pairs for atoms j "below" i are excluded + // below = lower z or (equal z and lower y) or (equal zy and lower x) + // (equal zyx and j <= i) + // latter excludes self-self interaction but allows superposed atoms + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp) { + if (x[j][0] < xtmp) continue; + if (x[j][0] == xtmp && j <= i) continue; + } + } + } else { + // Half neighbor list, newton on, orthonormal + // store every pair for every bin in stencil,except for i's bin + + if (stencil[k] == 0) { + // if j is owned atom, store it, since j is beyond i in linked list + // if j is ghost, only store if j coords are "above and to the "right" of i + if (j >= nlocal) { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + } + } + } + } jtype = type[j]; if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; @@ -121,5 +170,12 @@ void NPairFullBin::build(NeighList *list) } list->inum = inum; - list->gnum = 0; + if (!HALF) list->gnum = 0; +} + +namespace LAMMPS_NS { +template class NPairBin<0,1,0>; +template class NPairBin<1,0,0>; +template class NPairBin<1,1,0>; +template class NPairBin<1,1,1>; } diff --git a/src/npair_full_bin.h b/src/npair_bin.h similarity index 60% rename from src/npair_full_bin.h rename to src/npair_bin.h index 4d40763271..af23880db7 100644 --- a/src/npair_full_bin.h +++ b/src/npair_bin.h @@ -13,23 +13,40 @@ #ifdef NPAIR_CLASS // clang-format off +typedef NPairBin<0, 1, 0> NPairFullBin; NPairStyle(full/bin, NPairFullBin, NP_FULL | NP_BIN | NP_MOLONLY | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBin<1, 0, 0> NPairHalfBinNewtoff; +NPairStyle(half/bin/newtoff, + NPairHalfBinNewtoff, + NP_HALF | NP_BIN | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBin<1, 1, 0> NPairHalfBinNewton; +NPairStyle(half/bin/newton, + NPairHalfBinNewton, + NP_HALF | NP_BIN | NP_MOLONLY | NP_NEWTON | NP_ORTHO); + +typedef NPairBin<1, 1, 1> NPairHalfBinNewtonTri; +NPairStyle(half/bin/newton/tri, + NPairHalfBinNewtonTri, + NP_HALF | NP_BIN | NP_NEWTON | NP_TRI); // clang-format on #else -#ifndef LMP_NPAIR_FULL_BIN_H -#define LMP_NPAIR_FULL_BIN_H +#ifndef LMP_NPAIR_BIN_H +#define LMP_NPAIR_BIN_H #include "npair.h" namespace LAMMPS_NS { -class NPairFullBin : public NPair { +template +class NPairBin : public NPair { public: - NPairFullBin(class LAMMPS *); + NPairBin(class LAMMPS *); void build(class NeighList *) override; }; diff --git a/src/npair_bin_atomonly.cpp b/src/npair_bin_atomonly.cpp new file mode 100644 index 0000000000..aa66089fc5 --- /dev/null +++ b/src/npair_bin_atomonly.cpp @@ -0,0 +1,148 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 "npair_bin_atomonly.h" + +#include "atom.h" +#include "error.h" +#include "neighbor.h" +#include "my_page.h" +#include "neigh_list.h" + +using namespace LAMMPS_NS; +using namespace NeighConst; + +/* ---------------------------------------------------------------------- */ + +template +NPairBinAtomonly::NPairBinAtomonly(LAMMPS *lmp) : NPair(lmp) {} + +/* ---------------------------------------------------------------------- + binned neighbor list construction for all neighbors + every neighbor pair appears in list of both atoms i and j +------------------------------------------------------------------------- */ + +template +void NPairBinAtomonly::build(NeighList *list) +{ + int i,j,k,n,itype,jtype,ibin,bin_start; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + int *neighptr; + + double **x = atom->x; + int *type = atom->type; + int *mask = atom->mask; + tagint *molecule = atom->molecule; + int nlocal = atom->nlocal; + if (includegroup) nlocal = atom->nfirst; + + int *ilist = list->ilist; + int *numneigh = list->numneigh; + int **firstneigh = list->firstneigh; + MyPage *ipage = list->ipage; + + int inum = 0; + ipage->reset(); + + for (i = 0; i < nlocal; i++) { + n = 0; + neighptr = ipage->vget(); + + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + + ibin = atom2bin[i]; + + for (k = 0; k < nstencil; k++) { + bin_start = binhead[ibin+stencil[k]]; + if (stencil[k] == 0) { + if (HALF && NEWTON && (!TRI)) { + // Half neighbor list, newton on, orthonormal + // loop over rest of atoms in i's bin, ghosts are at end of linked list + bin_start = bins[i]; + } + } + + for (j = bin_start; j >= 0; j = bins[j]) { + if (!HALF) { + // Full neighbor list + // only skip i = j + if (i == j) continue; + } else if (!NEWTON) { + // Half neighbor list, newton off + // only store pair if i < j + // stores own/own pairs only once + // stores own/ghost pairs on both procs + if (j <= i) continue; + } else if (TRI) { + // Half neighbor list, newton on, triclinic + // pairs for atoms j "below" i are excluded + // below = lower z or (equal z and lower y) or (equal zy and lower x) + // (equal zyx and j <= i) + // latter excludes self-self interaction but allows superposed atoms + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp) { + if (x[j][0] < xtmp) continue; + if (x[j][0] == xtmp && j <= i) continue; + } + } + } else { + // Half neighbor list, newton on, orthonormal + // store every pair for every bin in stencil,except for i's bin + + if (stencil[k] == 0) { + // if j is owned atom, store it, since j is beyond i in linked list + // if j is ghost, only store if j coords are "above and to the "right" of i + if (j >= nlocal) { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + } + } + } + } + + jtype = type[j]; + if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; + } + } + + ilist[inum++] = i; + firstneigh[i] = neighptr; + numneigh[i] = n; + ipage->vgot(n); + if (ipage->status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + } + + list->inum = inum; + if (!HALF) list->gnum = 0; +} + +namespace LAMMPS_NS { +template class NPairBinAtomonly<0,1,0>; +template class NPairBinAtomonly<1,1,0>; +} diff --git a/src/npair_full_bin_atomonly.h b/src/npair_bin_atomonly.h similarity index 71% rename from src/npair_full_bin_atomonly.h rename to src/npair_bin_atomonly.h index 8e3f96e59b..ae64863138 100644 --- a/src/npair_full_bin_atomonly.h +++ b/src/npair_bin_atomonly.h @@ -13,23 +13,30 @@ #ifdef NPAIR_CLASS // clang-format off +typedef NPairBinAtomonly<0, 1, 0> NPairFullBinAtomonly; NPairStyle(full/bin/atomonly, NPairFullBinAtomonly, NP_FULL | NP_BIN | NP_ATOMONLY | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBinAtomonly<1, 1, 0> NPairHalfBinAtomonlyNewton; +NPairStyle(half/bin/atomonly/newton, + NPairHalfBinAtomonlyNewton, + NP_HALF | NP_BIN | NP_ATOMONLY | NP_NEWTON | NP_ORTHO); // clang-format on #else -#ifndef LMP_NPAIR_FULL_BIN_ATOMONLY_H -#define LMP_NPAIR_FULL_BIN_ATOMONLY_H +#ifndef LMP_NPAIR_BIN_ATOMONLY_H +#define LMP_NPAIR_BIN_ATOMONLY_H #include "npair.h" namespace LAMMPS_NS { -class NPairFullBinAtomonly : public NPair { +template +class NPairBinAtomonly : public NPair { public: - NPairFullBinAtomonly(class LAMMPS *); + NPairBinAtomonly(class LAMMPS *); void build(class NeighList *) override; }; diff --git a/src/npair_copy.cpp b/src/npair_copy.cpp deleted file mode 100644 index b3cab53e0a..0000000000 --- a/src/npair_copy.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_copy.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairCopy::NPairCopy(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - create list which is simply a copy of parent list -------------------------------------------------------------------------- */ - -void NPairCopy::build(NeighList *list) -{ - NeighList *listcopy = list->listcopy; - - list->inum = listcopy->inum; - list->gnum = listcopy->gnum; - list->ilist = listcopy->ilist; - list->numneigh = listcopy->numneigh; - list->firstneigh = listcopy->firstneigh; - list->ipage = listcopy->ipage; -} diff --git a/src/npair_copy.h b/src/npair_copy.h deleted file mode 100644 index 36b80f83d9..0000000000 --- a/src/npair_copy.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(copy, - NPairCopy, - NP_COPY); -// clang-format on -#else - -#ifndef LMP_NPAIR_COPY_H -#define LMP_NPAIR_COPY_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairCopy : public NPair { - public: - NPairCopy(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/npair_full_bin_atomonly.cpp b/src/npair_full_bin_atomonly.cpp deleted file mode 100644 index b8c4378280..0000000000 --- a/src/npair_full_bin_atomonly.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_full_bin_atomonly.h" - -#include "atom.h" -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairFullBinAtomonly::NPairFullBinAtomonly(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction for all neighbors - every neighbor pair appears in list of both atoms i and j -------------------------------------------------------------------------- */ - -void NPairFullBinAtomonly::build(NeighList *list) -{ - int i,j,k,n,itype,jtype,ibin; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *molecule = atom->molecule; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - - // loop over all atoms in surrounding bins in stencil including self - // skip i = j - - ibin = atom2bin[i]; - - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (i == j) continue; - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; - list->gnum = 0; -} diff --git a/src/npair_full_bin_ghost.cpp b/src/npair_full_bin_ghost.cpp deleted file mode 100644 index e051a1d75a..0000000000 --- a/src/npair_full_bin_ghost.cpp +++ /dev/null @@ -1,156 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_full_bin_ghost.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairFullBinGhost::NPairFullBinGhost(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction for all neighbors - include neighbors of ghost atoms, but no "special neighbors" for ghosts - every neighbor pair appears in list of both atoms i and j -------------------------------------------------------------------------- */ - -void NPairFullBinGhost::build(NeighList *list) -{ - int i,j,k,n,itype,jtype,ibin,which,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int xbin,ybin,zbin,xbin2,ybin2,zbin2; - int *neighptr; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - int nall = nlocal + atom->nghost; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - // loop over owned & ghost atoms, storing neighbors - - for (i = 0; i < nall; i++) { - n = 0; - neighptr = ipage->vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in surrounding bins in stencil including self - // when i is a ghost atom, must check if stencil bin is out of bounds - // skip i = j - // no molecular test when i = ghost atom - - if (i < nlocal) { - ibin = atom2bin[i]; - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (i == j) continue; - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - } else { - ibin = coord2bin(x[i],xbin,ybin,zbin); - for (k = 0; k < nstencil; k++) { - xbin2 = xbin + stencilxyz[k][0]; - ybin2 = ybin + stencilxyz[k][1]; - zbin2 = zbin + stencilxyz[k][2]; - if (xbin2 < 0 || xbin2 >= mbinx || - ybin2 < 0 || ybin2 >= mbiny || - zbin2 < 0 || zbin2 >= mbinz) continue; - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (i == j) continue; - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighghostsq[itype][jtype]) neighptr[n++] = j; - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = atom->nlocal; - list->gnum = inum - atom->nlocal; -} diff --git a/src/npair_full_bin_ghost.h b/src/npair_full_bin_ghost.h deleted file mode 100644 index 4b7e08a0e1..0000000000 --- a/src/npair_full_bin_ghost.h +++ /dev/null @@ -1,47 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(full/bin/ghost, - NPairFullBinGhost, - NP_FULL | NP_BIN | NP_GHOST | NP_NEWTON | NP_NEWTOFF | - NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_FULL_BIN_GHOST_H -#define LMP_NPAIR_FULL_BIN_GHOST_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairFullBinGhost : public NPair { - public: - NPairFullBinGhost(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_full_multi.cpp b/src/npair_full_multi.cpp deleted file mode 100644 index 34b7a7bfa8..0000000000 --- a/src/npair_full_multi.cpp +++ /dev/null @@ -1,143 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_full_multi.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neighbor.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairFullMulti::NPairFullMulti(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction for all neighbors - multi stencil is icollection-jcollection dependent - every neighbor pair appears in list of both atoms i and j -------------------------------------------------------------------------- */ - -void NPairFullMulti::build(NeighList *list) -{ - int i,j,k,n,itype,jtype,icollection,jcollection,ibin,jbin,which,ns,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*s; - int js; - - int *collection = neighbor->collection; - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - itype = type[i]; - icollection = collection[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - ibin = atom2bin[i]; - - // loop through stencils for all collections - for (jcollection = 0; jcollection < ncollections; jcollection++) { - - // if same collection use own bin - if(icollection == jcollection) jbin = ibin; - else jbin = coord2bin(x[i], jcollection); - - // loop over all atoms in surrounding bins in stencil including self - // skip i = j - // use full stencil for all collection combinations - - s = stencil_multi[icollection][jcollection]; - ns = nstencil_multi[icollection][jcollection]; - - for (k = 0; k < ns; k++) { - js = binhead_multi[jcollection][jbin + s[k]]; - for (j = js; j >= 0; j = bins[j]) { - if (i == j) continue; - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; - list->gnum = 0; -} diff --git a/src/npair_full_multi.h b/src/npair_full_multi.h deleted file mode 100644 index dcd1b5d8a9..0000000000 --- a/src/npair_full_multi.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(full/multi, - NPairFullMulti, - NP_FULL | NP_MULTI | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_FULL_MULTI_H -#define LMP_NPAIR_FULL_MULTI_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairFullMulti : public NPair { - public: - NPairFullMulti(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_full_multi_old.cpp b/src/npair_full_multi_old.cpp deleted file mode 100644 index 7b89f35268..0000000000 --- a/src/npair_full_multi_old.cpp +++ /dev/null @@ -1,133 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_full_multi_old.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairFullMultiOld::NPairFullMultiOld(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction for all neighbors - multi-type stencil is itype dependent and is distance checked - every neighbor pair appears in list of both atoms i and j -------------------------------------------------------------------------- */ - -void NPairFullMultiOld::build(NeighList *list) -{ - int i,j,k,n,itype,jtype,ibin,which,ns,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*s; - double *cutsq,*distsq; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in other bins in stencil, including self - // skip if i,j neighbor cutoff is less than bin distance - // skip i = j - - ibin = atom2bin[i]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - cutsq = cutneighsq[itype]; - ns = nstencil_multi_old[itype]; - for (k = 0; k < ns; k++) { - for (j = binhead[ibin+s[k]]; j >= 0; j = bins[j]) { - jtype = type[j]; - if (cutsq[jtype] < distsq[k]) continue; - if (i == j) continue; - - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; - list->gnum = 0; -} diff --git a/src/npair_full_multi_old.h b/src/npair_full_multi_old.h deleted file mode 100644 index 0dd825fcce..0000000000 --- a/src/npair_full_multi_old.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(full/multi/old, - NPairFullMultiOld, - NP_FULL | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_FULL_MULTI_OLD_H -#define LMP_NPAIR_FULL_MULTI_OLD_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairFullMultiOld : public NPair { - public: - NPairFullMultiOld(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_full_nsq.cpp b/src/npair_full_nsq.cpp deleted file mode 100644 index c6427366f4..0000000000 --- a/src/npair_full_nsq.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_full_nsq.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "group.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairFullNsq::NPairFullNsq(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - N^2 search for all neighbors - every neighbor pair appears in list of both atoms i and j -------------------------------------------------------------------------- */ - -void NPairFullNsq::build(NeighList *list) -{ - int i,j,n,itype,jtype,which,bitmask,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - int nall = nlocal + atom->nghost; - if (includegroup) { - nlocal = atom->nfirst; - bitmask = group->bitmask[includegroup]; - } - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms, owned and ghost - // skip i = j - - for (j = 0; j < nall; j++) { - if (includegroup && !(mask[j] & bitmask)) continue; - if (i == j) continue; - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; - list->gnum = 0; -} diff --git a/src/npair_full_nsq.h b/src/npair_full_nsq.h deleted file mode 100644 index 0e65fcd027..0000000000 --- a/src/npair_full_nsq.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(full/nsq, - NPairFullNsq, - NP_FULL | NP_NSQ | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_FULL_NSQ_H -#define LMP_NPAIR_FULL_NSQ_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairFullNsq : public NPair { - public: - NPairFullNsq(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_full_nsq_ghost.cpp b/src/npair_full_nsq_ghost.cpp deleted file mode 100644 index 9550ae54f3..0000000000 --- a/src/npair_full_nsq_ghost.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_full_nsq_ghost.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairFullNsqGhost::NPairFullNsqGhost(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - N^2 search for all neighbors - include neighbors of ghost atoms, but no "special neighbors" for ghosts - every neighbor pair appears in list of both atoms i and j -------------------------------------------------------------------------- */ - -void NPairFullNsqGhost::build(NeighList *list) -{ - int i,j,n,itype,jtype,which,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - int nall = nlocal + atom->nghost; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - // loop over owned & ghost atoms, storing neighbors - - for (i = 0; i < nall; i++) { - n = 0; - neighptr = ipage->vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms, owned and ghost - // skip i = j - // no molecular test when i = ghost atom - - if (i < nlocal) { - for (j = 0; j < nall; j++) { - if (i == j) continue; - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } else { - for (j = 0; j < nall; j++) { - if (i == j) continue; - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighghostsq[itype][jtype]) neighptr[n++] = j; - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = atom->nlocal; - list->gnum = inum - atom->nlocal; -} diff --git a/src/npair_full_nsq_ghost.h b/src/npair_full_nsq_ghost.h deleted file mode 100644 index 6ae517ccf7..0000000000 --- a/src/npair_full_nsq_ghost.h +++ /dev/null @@ -1,47 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(full/nsq/ghost, - NPairFullNsqGhost, - NP_FULL | NP_NSQ | NP_GHOST | NP_NEWTON | NP_NEWTOFF | - NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_FULL_NSQ_GHOST_H -#define LMP_NPAIR_FULL_NSQ_GHOST_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairFullNsqGhost : public NPair { - public: - NPairFullNsqGhost(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_bin_atomonly_newton.cpp b/src/npair_half_bin_atomonly_newton.cpp deleted file mode 100644 index db84d38f6e..0000000000 --- a/src/npair_half_bin_atomonly_newton.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_bin_atomonly_newton.h" - -#include "atom.h" -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfBinAtomonlyNewton::NPairHalfBinAtomonlyNewton(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with full Newton's 3rd law - each owned atom i checks its own bin and other bins in Newton stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfBinAtomonlyNewton::build(NeighList *list) -{ - int i,j,k,n,itype,jtype,ibin; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *molecule = atom->molecule; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - - // loop over rest of atoms in i's bin, ghosts are at end of linked list - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the right" of i - - for (j = bins[i]; j >= 0; j = bins[j]) { - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; - } - - // loop over all atoms in other bins in stencil, store every pair - - ibin = atom2bin[i]; - - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_bin_atomonly_newton.h b/src/npair_half_bin_atomonly_newton.h deleted file mode 100644 index 7a3eaf34bc..0000000000 --- a/src/npair_half_bin_atomonly_newton.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/bin/atomonly/newton, - NPairHalfBinAtomonlyNewton, - NP_HALF | NP_BIN | NP_ATOMONLY | NP_NEWTON | NP_ORTHO); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_BIN_ATOMONLY_NEWTON_H -#define LMP_NPAIR_HALF_BIN_ATOMONLY_NEWTON_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfBinAtomonlyNewton : public NPair { - public: - NPairHalfBinAtomonlyNewton(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_bin_newtoff.cpp b/src/npair_half_bin_newtoff.cpp deleted file mode 100644 index 00318b2aa7..0000000000 --- a/src/npair_half_bin_newtoff.cpp +++ /dev/null @@ -1,129 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_bin_newtoff.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfBinNewtoff::NPairHalfBinNewtoff(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with partial Newton's 3rd law - each owned atom i checks own bin and other bins in stencil - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) -------------------------------------------------------------------------- */ - -void NPairHalfBinNewtoff::build(NeighList *list) -{ - int i,j,k,n,itype,jtype,ibin,which,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in other bins in stencil including self - // only store pair if i < j - // stores own/own pairs only once - // stores own/ghost pairs on both procs - - ibin = atom2bin[i]; - - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (j <= i) continue; - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - // OLD: if (which >= 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_bin_newtoff.h b/src/npair_half_bin_newtoff.h deleted file mode 100644 index db240f8bd9..0000000000 --- a/src/npair_half_bin_newtoff.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/bin/newtoff, - NPairHalfBinNewtoff, - NP_HALF | NP_BIN | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_BIN_NEWTOFF_H -#define LMP_NPAIR_HALF_BIN_NEWTOFF_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfBinNewtoff : public NPair { - public: - NPairHalfBinNewtoff(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_bin_newtoff_ghost.cpp b/src/npair_half_bin_newtoff_ghost.cpp deleted file mode 100644 index b85e3328e1..0000000000 --- a/src/npair_half_bin_newtoff_ghost.cpp +++ /dev/null @@ -1,162 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_bin_newtoff_ghost.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfBinNewtoffGhost::NPairHalfBinNewtoffGhost(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with partial Newton's 3rd law - include neighbors of ghost atoms, but no "special neighbors" for ghosts - owned and ghost atoms check own bin and other bins in stencil - pair stored once if i,j are both owned and i < j - pair stored by me if i owned and j ghost (also stored by proc owning j) - pair stored once if i,j are both ghost and i < j -------------------------------------------------------------------------- */ - -void NPairHalfBinNewtoffGhost::build(NeighList *list) -{ - int i,j,k,n,itype,jtype,ibin,which,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int xbin,ybin,zbin,xbin2,ybin2,zbin2; - int *neighptr; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - int nall = nlocal + atom->nghost; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nall; i++) { - n = 0; - neighptr = ipage->vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in other bins in stencil including self - // when i is a ghost atom, must check if stencil bin is out of bounds - // only store pair if i < j - // stores own/own pairs only once - // stores own/ghost pairs with owned atom only, on both procs - // stores ghost/ghost pairs only once - // no molecular test when i = ghost atom - - if (i < nlocal) { - ibin = atom2bin[i]; - - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (j <= i) continue; - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - } else { - ibin = coord2bin(x[i],xbin,ybin,zbin); - for (k = 0; k < nstencil; k++) { - xbin2 = xbin + stencilxyz[k][0]; - ybin2 = ybin + stencilxyz[k][1]; - zbin2 = zbin + stencilxyz[k][2]; - if (xbin2 < 0 || xbin2 >= mbinx || - ybin2 < 0 || ybin2 >= mbiny || - zbin2 < 0 || zbin2 >= mbinz) continue; - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (j <= i) continue; - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighghostsq[itype][jtype]) neighptr[n++] = j; - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = atom->nlocal; - list->gnum = inum - atom->nlocal; -} diff --git a/src/npair_half_bin_newtoff_ghost.h b/src/npair_half_bin_newtoff_ghost.h deleted file mode 100644 index b11e0f4802..0000000000 --- a/src/npair_half_bin_newtoff_ghost.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/bin/newtoff/ghost, - NPairHalfBinNewtoffGhost, - NP_HALF | NP_BIN | NP_NEWTOFF | NP_GHOST | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_BIN_NEWTOFF_GHOST_H -#define LMP_NPAIR_HALF_BIN_NEWTOFF_GHOST_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfBinNewtoffGhost : public NPair { - public: - NPairHalfBinNewtoffGhost(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_bin_newton.cpp b/src/npair_half_bin_newton.cpp deleted file mode 100644 index d07ebf01f9..0000000000 --- a/src/npair_half_bin_newton.cpp +++ /dev/null @@ -1,161 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_bin_newton.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfBinNewton::NPairHalfBinNewton(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with full Newton's 3rd law - each owned atom i checks its own bin and other bins in Newton stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfBinNewton::build(NeighList *list) -{ - int i,j,k,n,itype,jtype,ibin,which,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over rest of atoms in i's bin, ghosts are at end of linked list - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the right" of i - - for (j = bins[i]; j >= 0; j = bins[j]) { - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - // OLD: if (which >= 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - - // loop over all atoms in other bins in stencil, store every pair - - ibin = atom2bin[i]; - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - // OLD: if (which >= 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_bin_newton.h b/src/npair_half_bin_newton.h deleted file mode 100644 index 19f7b93ae4..0000000000 --- a/src/npair_half_bin_newton.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/bin/newton, - NPairHalfBinNewton, - NP_HALF | NP_BIN | NP_MOLONLY | NP_NEWTON | NP_ORTHO); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_BIN_NEWTON_H -#define LMP_NPAIR_HALF_BIN_NEWTON_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfBinNewton : public NPair { - public: - NPairHalfBinNewton(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_bin_newton_tri.cpp b/src/npair_half_bin_newton_tri.cpp deleted file mode 100644 index ef2e48a417..0000000000 --- a/src/npair_half_bin_newton_tri.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_bin_newton_tri.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfBinNewtonTri::NPairHalfBinNewtonTri(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with Newton's 3rd law for triclinic - each owned atom i checks its own bin and other bins in triclinic stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfBinNewtonTri::build(NeighList *list) -{ - int i,j,k,n,itype,jtype,ibin,which,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in bins in stencil - // pairs for atoms j "below" i are excluded - // below = lower z or (equal z and lower y) or (equal zy and lower x) - // (equal zyx and j <= i) - // latter excludes self-self interaction but allows superposed atoms - - ibin = atom2bin[i]; - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp) { - if (x[j][0] < xtmp) continue; - if (x[j][0] == xtmp && j <= i) continue; - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_bin_newton_tri.h b/src/npair_half_bin_newton_tri.h deleted file mode 100644 index ad270ae173..0000000000 --- a/src/npair_half_bin_newton_tri.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/bin/newton/tri, - NPairHalfBinNewtonTri, - NP_HALF | NP_BIN | NP_NEWTON | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_BIN_NEWTON_TRI_H -#define LMP_NPAIR_HALF_BIN_NEWTON_TRI_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfBinNewtonTri : public NPair { - public: - NPairHalfBinNewtonTri(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_multi_newtoff.cpp b/src/npair_half_multi_newtoff.cpp deleted file mode 100644 index cef28f4cb4..0000000000 --- a/src/npair_half_multi_newtoff.cpp +++ /dev/null @@ -1,146 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_multi_newtoff.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neighbor.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfMultiNewtoff::NPairHalfMultiNewtoff(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with partial Newton's 3rd law - multi stencil is icollection-jcollection dependent - each owned atom i checks own bin and other bins in stencil - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) -------------------------------------------------------------------------- */ - -void NPairHalfMultiNewtoff::build(NeighList *list) -{ - int i,j,k,n,itype,jtype,icollection,jcollection,ibin,jbin,which,ns,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*s; - int js; - - int *collection = neighbor->collection; - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - itype = type[i]; - icollection = collection[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - ibin = atom2bin[i]; - - // loop through stencils for all collections - for (jcollection = 0; jcollection < ncollections; jcollection++) { - - // if same collection use own bin - if (icollection == jcollection) jbin = ibin; - else jbin = coord2bin(x[i], jcollection); - - // loop over all atoms in other bins in stencil including self - // only store pair if i < j - // stores own/own pairs only once - // stores own/ghost pairs on both procs - // use full stencil for all collection combinations - - s = stencil_multi[icollection][jcollection]; - ns = nstencil_multi[icollection][jcollection]; - - for (k = 0; k < ns; k++) { - js = binhead_multi[jcollection][jbin + s[k]]; - for (j = js; j >= 0; j = bins[j]) { - if (j <= i) continue; - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_multi_newtoff.h b/src/npair_half_multi_newtoff.h deleted file mode 100644 index 3142587da6..0000000000 --- a/src/npair_half_multi_newtoff.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/multi/newtoff, - NPairHalfMultiNewtoff, - NP_HALF | NP_MULTI | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_MULTI_NEWTOFF_H -#define LMP_NPAIR_HALF_MULTI_NEWTOFF_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfMultiNewtoff : public NPair { - public: - NPairHalfMultiNewtoff(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_multi_newton.cpp b/src/npair_half_multi_newton.cpp deleted file mode 100644 index 3ee4ce5fde..0000000000 --- a/src/npair_half_multi_newton.cpp +++ /dev/null @@ -1,194 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_multi_newton.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neighbor.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfMultiNewton::NPairHalfMultiNewton(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with full Newton's 3rd law - multi stencil is icollection-jcollection dependent - each owned atom i checks its own bin and other bins in Newton stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfMultiNewton::build(NeighList *list) -{ - int i,j,k,n,itype,jtype,icollection,jcollection,ibin,jbin,which,ns,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*s; - int js; - - int *collection = neighbor->collection; - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == 2) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - itype = type[i]; - icollection = collection[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - ibin = atom2bin[i]; - - // loop through stencils for all collections - for (jcollection = 0; jcollection < ncollections; jcollection++) { - - // if same collection use own bin - if(icollection == jcollection) jbin = ibin; - else jbin = coord2bin(x[i], jcollection); - - // if same size: uses half stencil so check central bin - if(cutcollectionsq[icollection][icollection] == cutcollectionsq[jcollection][jcollection]){ - - if (icollection == jcollection) js = bins[i]; - else js = binhead_multi[jcollection][jbin]; - - // if same collection, - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the right" of i - - // if different collections, - // if j is owned atom, store it if j > i - // if j is ghost, only store if j coords are "above and to the right" of i - - for (j = js; j >= 0; j = bins[j]) { - if((icollection != jcollection) && (j < i)) continue; - - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - // for all collections, loop over all atoms in other bins in stencil, store every pair - // stencil is empty if i larger than j - // stencil is half if i same size as j - // stencil is full if i smaller than j - - s = stencil_multi[icollection][jcollection]; - ns = nstencil_multi[icollection][jcollection]; - - for (k = 0; k < ns; k++) { - js = binhead_multi[jcollection][jbin + s[k]]; - for (j = js; j >= 0; j = bins[j]) { - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_multi_newton.h b/src/npair_half_multi_newton.h deleted file mode 100644 index 55439eacca..0000000000 --- a/src/npair_half_multi_newton.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/multi/newton, - NPairHalfMultiNewton, - NP_HALF | NP_MULTI | NP_NEWTON | NP_ORTHO); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_MULTI_NEWTON_H -#define LMP_NPAIR_HALF_MULTI_NEWTON_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfMultiNewton : public NPair { - public: - NPairHalfMultiNewton(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/npair_half_multi_newton_tri.cpp b/src/npair_half_multi_newton_tri.cpp deleted file mode 100644 index 4a8bb8e19f..0000000000 --- a/src/npair_half_multi_newton_tri.cpp +++ /dev/null @@ -1,159 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_multi_newton_tri.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neighbor.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfMultiNewtonTri::NPairHalfMultiNewtonTri(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with Newton's 3rd law for triclinic - multi stencil is icollection-jcollection dependent - each owned atom i checks its own bin and other bins in triclinic stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfMultiNewtonTri::build(NeighList *list) -{ - int i,j,k,n,itype,jtype,icollection,jcollection,ibin,jbin,which,ns,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*s; - int js; - - int *collection = neighbor->collection; - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == 2) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - itype = type[i]; - icollection = collection[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - ibin = atom2bin[i]; - - // loop through stencils for all collections - for (jcollection = 0; jcollection < ncollections; jcollection++) { - - // if same collection use own bin - if (icollection == jcollection) jbin = ibin; - else jbin = coord2bin(x[i], jcollection); - - // loop over all atoms in bins in stencil - // stencil is empty if i larger than j - // stencil is half if i same size as j - // stencil is full if i smaller than j - // if half: pairs for atoms j "below" i are excluded - // below = lower z or (equal z and lower y) or (equal zy and lower x) - // (equal zyx and j <= i) - // latter excludes self-self interaction but allows superposed atoms - - s = stencil_multi[icollection][jcollection]; - ns = nstencil_multi[icollection][jcollection]; - - for (k = 0; k < ns; k++) { - js = binhead_multi[jcollection][jbin + s[k]]; - for (j = js; j >= 0; j = bins[j]) { - - // if same size (same collection), use half stencil - if(cutcollectionsq[icollection][icollection] == cutcollectionsq[jcollection][jcollection]){ - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp) { - if (x[j][0] < xtmp) continue; - if (x[j][0] == xtmp && j <= i) continue; - } - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_multi_newton_tri.h b/src/npair_half_multi_newton_tri.h deleted file mode 100644 index 2b6d69f332..0000000000 --- a/src/npair_half_multi_newton_tri.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/multi/newton/tri, - NPairHalfMultiNewtonTri, - NP_HALF | NP_MULTI | NP_NEWTON | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_MULTI_NEWTON_TRI_H -#define LMP_NPAIR_HALF_MULTI_NEWTON_TRI_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfMultiNewtonTri : public NPair { - public: - NPairHalfMultiNewtonTri(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/npair_half_multi_old_newtoff.cpp b/src/npair_half_multi_old_newtoff.cpp deleted file mode 100644 index eb6c7d9069..0000000000 --- a/src/npair_half_multi_old_newtoff.cpp +++ /dev/null @@ -1,136 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_multi_old_newtoff.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfMultiOldNewtoff::NPairHalfMultiOldNewtoff(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with partial Newton's 3rd law - each owned atom i checks own bin and other bins in stencil - multi-type stencil is itype dependent and is distance checked - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) -------------------------------------------------------------------------- */ - -void NPairHalfMultiOldNewtoff::build(NeighList *list) -{ - int i,j,k,n,itype,jtype,ibin,which,ns,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*s; - double *cutsq,*distsq; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in other bins in stencil including self - // only store pair if i < j - // skip if i,j neighbor cutoff is less than bin distance - // stores own/own pairs only once - // stores own/ghost pairs on both procs - - ibin = atom2bin[i]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - cutsq = cutneighsq[itype]; - ns = nstencil_multi_old[itype]; - for (k = 0; k < ns; k++) { - for (j = binhead[ibin+s[k]]; j >= 0; j = bins[j]) { - if (j <= i) continue; - jtype = type[j]; - if (cutsq[jtype] < distsq[k]) continue; - - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_multi_old_newtoff.h b/src/npair_half_multi_old_newtoff.h deleted file mode 100644 index 9418cf5a4c..0000000000 --- a/src/npair_half_multi_old_newtoff.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/multi/old/newtoff, - NPairHalfMultiOldNewtoff, - NP_HALF | NP_MULTI_OLD | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_MULTI_OLD_NEWTOFF_H -#define LMP_NPAIR_HALF_MULTI_OLD_NEWTOFF_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfMultiOldNewtoff : public NPair { - public: - NPairHalfMultiOldNewtoff(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_multi_old_newton.cpp b/src/npair_half_multi_old_newton.cpp deleted file mode 100644 index 9708b3e879..0000000000 --- a/src/npair_half_multi_old_newton.cpp +++ /dev/null @@ -1,169 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_multi_old_newton.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfMultiOldNewton::NPairHalfMultiOldNewton(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with full Newton's 3rd law - each owned atom i checks its own bin and other bins in Newton stencil - multi-type stencil is itype dependent and is distance checked - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfMultiOldNewton::build(NeighList *list) -{ - int i,j,k,n,itype,jtype,ibin,which,ns,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*s; - double *cutsq,*distsq; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over rest of atoms in i's bin, ghosts are at end of linked list - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the right" of i - - for (j = bins[i]; j >= 0; j = bins[j]) { - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - - // loop over all atoms in other bins in stencil, store every pair - // skip if i,j neighbor cutoff is less than bin distance - - ibin = atom2bin[i]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - cutsq = cutneighsq[itype]; - ns = nstencil_multi_old[itype]; - for (k = 0; k < ns; k++) { - for (j = binhead[ibin+s[k]]; j >= 0; j = bins[j]) { - jtype = type[j]; - if (cutsq[jtype] < distsq[k]) continue; - - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_multi_old_newton.h b/src/npair_half_multi_old_newton.h deleted file mode 100644 index 5c84b8ef98..0000000000 --- a/src/npair_half_multi_old_newton.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/multi/old/newton, - NPairHalfMultiOldNewton, - NP_HALF | NP_MULTI_OLD | NP_NEWTON | NP_ORTHO); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_MULTI_OLD_NEWTON_H -#define LMP_NPAIR_HALF_MULTI_OLD_NEWTON_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfMultiOldNewton : public NPair { - public: - NPairHalfMultiOldNewton(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_multi_old_newton_tri.cpp b/src/npair_half_multi_old_newton_tri.cpp deleted file mode 100644 index 683cc5f53c..0000000000 --- a/src/npair_half_multi_old_newton_tri.cpp +++ /dev/null @@ -1,144 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_multi_old_newton_tri.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfMultiOldNewtonTri::NPairHalfMultiOldNewtonTri(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with Newton's 3rd law for triclinic - each owned atom i checks its own bin and other bins in triclinic stencil - multi-type stencil is itype dependent and is distance checked - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfMultiOldNewtonTri::build(NeighList *list) -{ - int i,j,k,n,itype,jtype,ibin,which,ns,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*s; - double *cutsq,*distsq; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in bins, including self, in stencil - // skip if i,j neighbor cutoff is less than bin distance - // bins below self are excluded from stencil - // pairs for atoms j "below" i are excluded - // below = lower z or (equal z and lower y) or (equal zy and lower x) - // (equal zyx and j <= i) - // latter excludes self-self interaction but allows superposed atoms - - ibin = atom2bin[i]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - cutsq = cutneighsq[itype]; - ns = nstencil_multi_old[itype]; - for (k = 0; k < ns; k++) { - for (j = binhead[ibin+s[k]]; j >= 0; j = bins[j]) { - jtype = type[j]; - if (cutsq[jtype] < distsq[k]) continue; - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp) { - if (x[j][0] < xtmp) continue; - if (x[j][0] == xtmp && j <= i) continue; - } - } - - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_multi_old_newton_tri.h b/src/npair_half_multi_old_newton_tri.h deleted file mode 100644 index 016746cf12..0000000000 --- a/src/npair_half_multi_old_newton_tri.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/multi/old/newton/tri, - NPairHalfMultiOldNewtonTri, - NP_HALF | NP_MULTI_OLD | NP_NEWTON | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_MULTI_OLD_NEWTON_TRI_H -#define LMP_NPAIR_HALF_MULTI_OLD_NEWTON_TRI_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfMultiOldNewtonTri : public NPair { - public: - NPairHalfMultiOldNewtonTri(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_nsq_newtoff.cpp b/src/npair_half_nsq_newtoff.cpp deleted file mode 100644 index affcdcf39f..0000000000 --- a/src/npair_half_nsq_newtoff.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_nsq_newtoff.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "group.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfNsqNewtoff::NPairHalfNsqNewtoff(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - N^2 / 2 search for neighbor pairs with partial Newton's 3rd law - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) -------------------------------------------------------------------------- */ - -void NPairHalfNsqNewtoff::build(NeighList *list) -{ - int i,j,n,itype,jtype,which,bitmask,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - int nall = nlocal + atom->nghost; - if (includegroup) { - nlocal = atom->nfirst; - bitmask = group->bitmask[includegroup]; - } - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over remaining atoms, owned and ghost - // only store pair if i < j - - for (j = i+1; j < nall; j++) { - if (includegroup && !(mask[j] & bitmask)) continue; - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_nsq_newtoff.h b/src/npair_half_nsq_newtoff.h deleted file mode 100644 index 45bef5b0b0..0000000000 --- a/src/npair_half_nsq_newtoff.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/nsq/newtoff, - NPairHalfNsqNewtoff, - NP_HALF | NP_NSQ | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_NSQ_NEWTOFF_H -#define LMP_NPAIR_HALF_NSQ_NEWTOFF_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfNsqNewtoff : public NPair { - public: - NPairHalfNsqNewtoff(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_nsq_newtoff_ghost.cpp b/src/npair_half_nsq_newtoff_ghost.cpp deleted file mode 100644 index eebee1208b..0000000000 --- a/src/npair_half_nsq_newtoff_ghost.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_nsq_newtoff_ghost.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "group.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfNsqNewtoffGhost::NPairHalfNsqNewtoffGhost(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - N^2 / 2 search for neighbor pairs with partial Newton's 3rd law - include neighbors of ghost atoms, but no "special neighbors" for ghosts - pair stored once if i,j are both owned and i < j - pair stored by me if i owned and j ghost (also stored by proc owning j) - pair stored once if i,j are both ghost and i < j -------------------------------------------------------------------------- */ - -void NPairHalfNsqNewtoffGhost::build(NeighList *list) -{ - int i,j,n,itype,jtype,which,bitmask,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - int nall = nlocal + atom->nghost; - if (includegroup) { - nlocal = atom->nfirst; - bitmask = group->bitmask[includegroup]; - } - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - // loop over owned & ghost atoms, storing neighbors - - for (i = 0; i < nall; i++) { - n = 0; - neighptr = ipage->vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over remaining atoms, owned and ghost - // only store pair if i < j - // stores own/own pairs only once - // stores own/ghost pairs with owned atom only, on both procs - // stores ghost/ghost pairs only once - // no molecular test when i = ghost atom - - if (i < nlocal) { - for (j = i+1; j < nall; j++) { - if (includegroup && !(mask[j] & bitmask)) continue; - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - - } else { - for (j = i+1; j < nall; j++) { - if (includegroup && !(mask[j] & bitmask)) continue; - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = atom->nlocal; - list->gnum = inum - atom->nlocal; -} diff --git a/src/npair_half_nsq_newtoff_ghost.h b/src/npair_half_nsq_newtoff_ghost.h deleted file mode 100644 index 5d6cb69450..0000000000 --- a/src/npair_half_nsq_newtoff_ghost.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/nsq/newtoff/ghost, - NPairHalfNsqNewtoffGhost, - NP_HALF | NP_NSQ | NP_NEWTOFF | NP_GHOST | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_NSQ_NEWTOFF_GHOST_H -#define LMP_NPAIR_HALF_NSQ_NEWTOFF_GHOST_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfNsqNewtoffGhost : public NPair { - public: - NPairHalfNsqNewtoffGhost(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_nsq_newton.cpp b/src/npair_half_nsq_newton.cpp deleted file mode 100644 index 5263903657..0000000000 --- a/src/npair_half_nsq_newton.cpp +++ /dev/null @@ -1,142 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_nsq_newton.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "group.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfNsqNewton::NPairHalfNsqNewton(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - N^2 / 2 search for neighbor pairs with full Newton's 3rd law - every pair stored exactly once by some processor - decision on ghost atoms based on itag,jtag tests -------------------------------------------------------------------------- */ - -void NPairHalfNsqNewton::build(NeighList *list) -{ - int i,j,n,itype,jtype,which,bitmask,imol,iatom,moltemplate; - tagint itag,jtag,tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - int nall = nlocal + atom->nghost; - if (includegroup) { - nlocal = atom->nfirst; - bitmask = group->bitmask[includegroup]; - } - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - itag = tag[i]; - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over remaining atoms, owned and ghost - // itag = jtag is possible for long cutoffs that include images of self - - for (j = i+1; j < nall; j++) { - if (includegroup && !(mask[j] & bitmask)) continue; - - if (j >= nlocal) { - jtag = tag[j]; - if (itag > jtag) { - if ((itag+jtag) % 2 == 0) continue; - } else if (itag < jtag) { - if ((itag+jtag) % 2 == 1) continue; - } else { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_nsq_newton.h b/src/npair_half_nsq_newton.h deleted file mode 100644 index 6b7ec89b82..0000000000 --- a/src/npair_half_nsq_newton.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/nsq/newton, - NPairHalfNsqNewton, - NP_HALF | NP_NSQ | NP_NEWTON | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_NSQ_NEWTON_H -#define LMP_NPAIR_HALF_NSQ_NEWTON_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfNsqNewton : public NPair { - public: - NPairHalfNsqNewton(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_respa_bin_newtoff.cpp b/src/npair_half_respa_bin_newtoff.cpp deleted file mode 100644 index b0fb7cb097..0000000000 --- a/src/npair_half_respa_bin_newtoff.cpp +++ /dev/null @@ -1,187 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_respa_bin_newtoff.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfRespaBinNewtoff::NPairHalfRespaBinNewtoff(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - multiple respa lists - binned neighbor list construction with partial Newton's 3rd law - each owned atom i checks own bin and surrounding bins in non-Newton stencil - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) -------------------------------------------------------------------------- */ - -void NPairHalfRespaBinNewtoff::build(NeighList *list) -{ - int i,j,k,n,itype,jtype,ibin,n_inner,n_middle,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*neighptr_inner,*neighptr_middle; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int *ilist_inner = list->ilist_inner; - int *numneigh_inner = list->numneigh_inner; - int **firstneigh_inner = list->firstneigh_inner; - MyPage *ipage_inner = list->ipage_inner; - - int *ilist_middle,*numneigh_middle,**firstneigh_middle; - MyPage *ipage_middle; - int respamiddle = list->respamiddle; - if (respamiddle) { - ilist_middle = list->ilist_middle; - numneigh_middle = list->numneigh_middle; - firstneigh_middle = list->firstneigh_middle; - ipage_middle = list->ipage_middle; - } - - int inum = 0; - int which = 0; - int minchange = 0; - ipage->reset(); - ipage_inner->reset(); - if (respamiddle) ipage_middle->reset(); - - for (i = 0; i < nlocal; i++) { - n = n_inner = 0; - neighptr = ipage->vget(); - neighptr_inner = ipage_inner->vget(); - if (respamiddle) { - n_middle = 0; - neighptr_middle = ipage_middle->vget(); - } - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - ibin = atom2bin[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in surrounding bins in stencil including self - // only store pair if i < j - // stores own/own pairs only once - // stores own/ghost pairs on both procs - - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (j <= i) continue; - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if ((minchange = domain->minimum_image_check(delx,dely,delz))) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - - if (rsq < cut_inner_sq) { - if (which == 0) neighptr_inner[n_inner++] = j; - else if (minchange) neighptr_inner[n_inner++] = j; - else if (which > 0) - neighptr_inner[n_inner++] = j ^ (which << SBBITS); - } - - if (respamiddle && - rsq < cut_middle_sq && rsq > cut_middle_inside_sq) { - if (which == 0) neighptr_middle[n_middle++] = j; - else if (minchange) neighptr_middle[n_middle++] = j; - else if (which > 0) - neighptr_middle[n_middle++] = j ^ (which << SBBITS); - } - } - } - } - - ilist[inum] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - ilist_inner[inum] = i; - firstneigh_inner[i] = neighptr_inner; - numneigh_inner[i] = n_inner; - ipage_inner->vgot(n_inner); - if (ipage_inner->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - if (respamiddle) { - ilist_middle[inum] = i; - firstneigh_middle[i] = neighptr_middle; - numneigh_middle[i] = n_middle; - ipage_middle->vgot(n_middle); - if (ipage_middle->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - inum++; - } - - list->inum = inum; - list->inum_inner = inum; - if (respamiddle) list->inum_middle = inum; -} diff --git a/src/npair_half_respa_bin_newtoff.h b/src/npair_half_respa_bin_newtoff.h deleted file mode 100644 index 2782ae239e..0000000000 --- a/src/npair_half_respa_bin_newtoff.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/respa/bin/newtoff, - NPairHalfRespaBinNewtoff, - NP_HALF | NP_RESPA | NP_BIN | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_RESPA_BIN_NEWTOFF_H -#define LMP_NPAIR_HALF_RESPA_BIN_NEWTOFF_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfRespaBinNewtoff : public NPair { - public: - NPairHalfRespaBinNewtoff(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_respa_bin_newton.cpp b/src/npair_half_respa_bin_newton.cpp deleted file mode 100644 index 0bb6756497..0000000000 --- a/src/npair_half_respa_bin_newton.cpp +++ /dev/null @@ -1,233 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_respa_bin_newton.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfRespaBinNewton::NPairHalfRespaBinNewton(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - multiple respa lists - binned neighbor list construction with full Newton's 3rd law - each owned atom i checks its own bin and other bins in Newton stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfRespaBinNewton::build(NeighList *list) -{ - int i,j,k,n,itype,jtype,ibin,n_inner,n_middle,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*neighptr_inner,*neighptr_middle; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int *ilist_inner = list->ilist_inner; - int *numneigh_inner = list->numneigh_inner; - int **firstneigh_inner = list->firstneigh_inner; - MyPage *ipage_inner = list->ipage_inner; - - int *ilist_middle,*numneigh_middle,**firstneigh_middle; - MyPage *ipage_middle; - int respamiddle = list->respamiddle; - if (respamiddle) { - ilist_middle = list->ilist_middle; - numneigh_middle = list->numneigh_middle; - firstneigh_middle = list->firstneigh_middle; - ipage_middle = list->ipage_middle; - } - - int inum = 0; - int which = 0; - int minchange = 0; - ipage->reset(); - ipage_inner->reset(); - if (respamiddle) ipage_middle->reset(); - - for (i = 0; i < nlocal; i++) { - n = n_inner = 0; - neighptr = ipage->vget(); - neighptr_inner = ipage_inner->vget(); - if (respamiddle) { - n_middle = 0; - neighptr_middle = ipage_middle->vget(); - } - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over rest of atoms in i's bin, ghosts are at end of linked list - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the right" of i - - for (j = bins[i]; j >= 0; j = bins[j]) { - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if ((minchange = domain->minimum_image_check(delx,dely,delz))) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - - if (rsq < cut_inner_sq) { - if (which == 0) neighptr_inner[n_inner++] = j; - else if (minchange) neighptr_inner[n_inner++] = j; - else if (which > 0) neighptr_inner[n_inner++] = j ^ (which << SBBITS); - } - - if (respamiddle && - rsq < cut_middle_sq && rsq > cut_middle_inside_sq) { - if (which == 0) neighptr_middle[n_middle++] = j; - else if (minchange) neighptr_middle[n_middle++] = j; - else if (which > 0) - neighptr_middle[n_middle++] = j ^ (which << SBBITS); - } - } - } - - // loop over all atoms in other bins in stencil, store every pair - - ibin = atom2bin[i]; - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if ((minchange = domain->minimum_image_check(delx,dely,delz))) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - - if (rsq < cut_inner_sq) { - if (which == 0) neighptr_inner[n_inner++] = j; - else if (minchange) neighptr_inner[n_inner++] = j; - else if (which > 0) - neighptr_inner[n_inner++] = j ^ (which << SBBITS); - } - - if (respamiddle && - rsq < cut_middle_sq && rsq > cut_middle_inside_sq) { - if (which == 0) neighptr_middle[n_middle++] = j; - else if (minchange) neighptr_middle[n_middle++] = j; - else if (which > 0) - neighptr_middle[n_middle++] = j ^ (which << SBBITS); - } - } - } - } - - ilist[inum] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - ilist_inner[inum] = i; - firstneigh_inner[i] = neighptr_inner; - numneigh_inner[i] = n_inner; - ipage_inner->vgot(n_inner); - if (ipage_inner->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - if (respamiddle) { - ilist_middle[inum] = i; - firstneigh_middle[i] = neighptr_middle; - numneigh_middle[i] = n_middle; - ipage_middle->vgot(n_middle); - if (ipage_middle->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - inum++; - } - - list->inum = inum; - list->inum_inner = inum; - if (respamiddle) list->inum_middle = inum; -} diff --git a/src/npair_half_respa_bin_newton.h b/src/npair_half_respa_bin_newton.h deleted file mode 100644 index c83ca89891..0000000000 --- a/src/npair_half_respa_bin_newton.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/respa/bin/newton, - NPairHalfRespaBinNewton, - NP_HALF | NP_RESPA | NP_BIN | NP_NEWTON | NP_ORTHO); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_RESPA_BIN_NEWTON_H -#define LMP_NPAIR_HALF_RESPA_BIN_NEWTON_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfRespaBinNewton : public NPair { - public: - NPairHalfRespaBinNewton(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_respa_bin_newton_tri.cpp b/src/npair_half_respa_bin_newton_tri.cpp deleted file mode 100644 index c6c569d794..0000000000 --- a/src/npair_half_respa_bin_newton_tri.cpp +++ /dev/null @@ -1,195 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_respa_bin_newton_tri.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfRespaBinNewtonTri::NPairHalfRespaBinNewtonTri(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - multiple respa lists - binned neighbor list construction with Newton's 3rd law for triclinic - each owned atom i checks its own bin and other bins in triclinic stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfRespaBinNewtonTri::build(NeighList *list) -{ - int i,j,k,n,itype,jtype,ibin,n_inner,n_middle,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*neighptr_inner,*neighptr_middle; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int *ilist_inner = list->ilist_inner; - int *numneigh_inner = list->numneigh_inner; - int **firstneigh_inner = list->firstneigh_inner; - MyPage *ipage_inner = list->ipage_inner; - - int *ilist_middle,*numneigh_middle,**firstneigh_middle; - MyPage *ipage_middle; - int respamiddle = list->respamiddle; - if (respamiddle) { - ilist_middle = list->ilist_middle; - numneigh_middle = list->numneigh_middle; - firstneigh_middle = list->firstneigh_middle; - ipage_middle = list->ipage_middle; - } - - int inum = 0; - int which = 0; - int minchange = 0; - ipage->reset(); - ipage_inner->reset(); - if (respamiddle) ipage_middle->reset(); - - for (i = 0; i < nlocal; i++) { - n = n_inner = 0; - neighptr = ipage->vget(); - neighptr_inner = ipage_inner->vget(); - if (respamiddle) { - n_middle = 0; - neighptr_middle = ipage_middle->vget(); - } - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in bins in stencil - // pairs for atoms j "below" i are excluded - // below = lower z or (equal z and lower y) or (equal zy and lower x) - // (equal zyx and j <= i) - // latter excludes self-self interaction but allows superposed atoms - - ibin = atom2bin[i]; - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp) { - if (x[j][0] < xtmp) continue; - if (x[j][0] == xtmp && j <= i) continue; - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if ((minchange = domain->minimum_image_check(delx,dely,delz))) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - - if (rsq < cut_inner_sq) { - if (which == 0) neighptr_inner[n_inner++] = j; - else if (minchange) neighptr_inner[n_inner++] = j; - else if (which > 0) - neighptr_inner[n_inner++] = j ^ (which << SBBITS); - } - - if (respamiddle && - rsq < cut_middle_sq && rsq > cut_middle_inside_sq) { - if (which == 0) neighptr_middle[n_middle++] = j; - else if (minchange) neighptr_middle[n_middle++] = j; - else if (which > 0) - neighptr_middle[n_middle++] = j ^ (which << SBBITS); - } - } - } - } - - ilist[inum] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - ilist_inner[inum] = i; - firstneigh_inner[i] = neighptr_inner; - numneigh_inner[i] = n_inner; - ipage_inner->vgot(n_inner); - if (ipage_inner->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - if (respamiddle) { - ilist_middle[inum] = i; - firstneigh_middle[i] = neighptr_middle; - numneigh_middle[i] = n_middle; - ipage_middle->vgot(n_middle); - if (ipage_middle->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - inum++; - } - - list->inum = inum; - list->inum_inner = inum; - if (respamiddle) list->inum_middle = inum; -} diff --git a/src/npair_half_respa_bin_newton_tri.h b/src/npair_half_respa_bin_newton_tri.h deleted file mode 100644 index a5b573103c..0000000000 --- a/src/npair_half_respa_bin_newton_tri.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/respa/bin/newton/tri, - NPairHalfRespaBinNewtonTri, - NP_HALF | NP_RESPA | NP_BIN | NP_NEWTON | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_RESPA_BIN_NEWTON_TRI_H -#define LMP_NPAIR_HALF_RESPA_BIN_NEWTON_TRI_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfRespaBinNewtonTri : public NPair { - public: - NPairHalfRespaBinNewtonTri(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_respa_nsq_newtoff.cpp b/src/npair_half_respa_nsq_newtoff.cpp deleted file mode 100644 index ff512be7d9..0000000000 --- a/src/npair_half_respa_nsq_newtoff.cpp +++ /dev/null @@ -1,182 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_respa_nsq_newtoff.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "group.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfRespaNsqNewtoff::NPairHalfRespaNsqNewtoff(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - multiple respa lists - N^2 / 2 search for neighbor pairs with partial Newton's 3rd law - pair added to list if atoms i and j are both owned and i < j - pair added if j is ghost (also stored by proc owning j) -------------------------------------------------------------------------- */ - -void NPairHalfRespaNsqNewtoff::build(NeighList *list) -{ - int i,j,n,itype,jtype,n_inner,n_middle,bitmask,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*neighptr_inner,*neighptr_middle; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - int nall = nlocal + atom->nghost; - if (includegroup) { - nlocal = atom->nfirst; - bitmask = group->bitmask[includegroup]; - } - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int *ilist_inner = list->ilist_inner; - int *numneigh_inner = list->numneigh_inner; - int **firstneigh_inner = list->firstneigh_inner; - MyPage *ipage_inner = list->ipage_inner; - - int *ilist_middle,*numneigh_middle,**firstneigh_middle; - MyPage *ipage_middle; - int respamiddle = list->respamiddle; - if (respamiddle) { - ilist_middle = list->ilist_middle; - numneigh_middle = list->numneigh_middle; - firstneigh_middle = list->firstneigh_middle; - ipage_middle = list->ipage_middle; - } - - int inum = 0; - int which = 0; - int minchange = 0; - ipage->reset(); - ipage_inner->reset(); - if (respamiddle) ipage_middle->reset(); - - for (i = 0; i < nlocal; i++) { - n = n_inner = 0; - neighptr = ipage->vget(); - neighptr_inner = ipage_inner->vget(); - if (respamiddle) { - n_middle = 0; - neighptr_middle = ipage_middle->vget(); - } - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over remaining atoms, owned and ghost - - for (j = i+1; j < nall; j++) { - if (includegroup && !(mask[j] & bitmask)) continue; - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if ((minchange = domain->minimum_image_check(delx,dely,delz))) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - - if (rsq < cut_inner_sq) { - if (which == 0) neighptr_inner[n_inner++] = j; - else if (minchange) neighptr_inner[n_inner++] = j; - else if (which > 0) neighptr_inner[n_inner++] = j ^ (which << SBBITS); - } - - if (respamiddle && rsq < cut_middle_sq && rsq > cut_middle_inside_sq) { - if (which == 0) neighptr_middle[n_middle++] = j; - else if (minchange) neighptr_middle[n_middle++] = j; - else if (which > 0) - neighptr_middle[n_middle++] = j ^ (which << SBBITS); - } - } - } - - ilist[inum] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - ilist_inner[inum] = i; - firstneigh_inner[i] = neighptr_inner; - numneigh_inner[i] = n_inner; - ipage_inner->vgot(n_inner); - if (ipage_inner->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - if (respamiddle) { - ilist_middle[inum] = i; - firstneigh_middle[i] = neighptr_middle; - numneigh_middle[i] = n_middle; - ipage_middle->vgot(n_middle); - if (ipage_middle->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - inum++; - } - - list->inum = inum; - list->inum_inner = inum; - if (respamiddle) list->inum_middle = inum; -} diff --git a/src/npair_half_respa_nsq_newtoff.h b/src/npair_half_respa_nsq_newtoff.h deleted file mode 100644 index d13fb810da..0000000000 --- a/src/npair_half_respa_nsq_newtoff.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/respa/nsq/newtoff, - NPairHalfRespaNsqNewtoff, - NP_HALF | NP_RESPA | NP_NSQ | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_RESPA_NSQ_NEWTOFF_H -#define LMP_NPAIR_HALF_RESPA_NSQ_NEWTOFF_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfRespaNsqNewtoff : public NPair { - public: - NPairHalfRespaNsqNewtoff(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_respa_nsq_newton.cpp b/src/npair_half_respa_nsq_newton.cpp deleted file mode 100644 index 941ce41159..0000000000 --- a/src/npair_half_respa_nsq_newton.cpp +++ /dev/null @@ -1,202 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_respa_nsq_newton.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "group.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfRespaNsqNewton::NPairHalfRespaNsqNewton(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - multiple respa lists - N^2 / 2 search for neighbor pairs with full Newton's 3rd law - pair added to list if atoms i and j are both owned and i < j - if j is ghost only me or other proc adds pair - decision based on itag,jtag tests -------------------------------------------------------------------------- */ - -void NPairHalfRespaNsqNewton::build(NeighList *list) -{ - int i,j,n,itype,jtype,itag,jtag,n_inner,n_middle,bitmask; - int imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*neighptr_inner,*neighptr_middle; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - int nall = nlocal + atom->nghost; - if (includegroup) { - nlocal = atom->nfirst; - bitmask = group->bitmask[includegroup]; - } - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int *ilist_inner = list->ilist_inner; - int *numneigh_inner = list->numneigh_inner; - int **firstneigh_inner = list->firstneigh_inner; - MyPage *ipage_inner = list->ipage_inner; - - int *ilist_middle,*numneigh_middle,**firstneigh_middle; - MyPage *ipage_middle; - int respamiddle = list->respamiddle; - if (respamiddle) { - ilist_middle = list->ilist_middle; - numneigh_middle = list->numneigh_middle; - firstneigh_middle = list->firstneigh_middle; - ipage_middle = list->ipage_middle; - } - - int inum = 0; - int which = 0; - int minchange = 0; - ipage->reset(); - ipage_inner->reset(); - if (respamiddle) ipage_middle->reset(); - - for (i = 0; i < nlocal; i++) { - n = n_inner = 0; - neighptr = ipage->vget(); - neighptr_inner = ipage_inner->vget(); - if (respamiddle) { - n_middle = 0; - neighptr_middle = ipage_middle->vget(); - } - - itag = tag[i]; - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over remaining atoms, owned and ghost - - for (j = i+1; j < nall; j++) { - if (includegroup && !(mask[j] & bitmask)) continue; - - if (j >= nlocal) { - jtag = tag[j]; - if (itag > jtag) { - if ((itag+jtag) % 2 == 0) continue; - } else if (itag < jtag) { - if ((itag+jtag) % 2 == 1) continue; - } else { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if ((minchange = domain->minimum_image_check(delx,dely,delz))) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - - if (rsq < cut_inner_sq) { - if (which == 0) neighptr_inner[n_inner++] = j; - else if (minchange) neighptr_inner[n_inner++] = j; - else if (which > 0) neighptr_inner[n_inner++] = j ^ (which << SBBITS); - } - - if (respamiddle && - rsq < cut_middle_sq && rsq > cut_middle_inside_sq) { - if (which == 0) neighptr_middle[n_middle++] = j; - else if (minchange) neighptr_middle[n_middle++] = j; - else if (which > 0) - neighptr_middle[n_middle++] = j ^ (which << SBBITS); - } - } - } - - ilist[inum] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - ilist_inner[inum] = i; - firstneigh_inner[i] = neighptr_inner; - numneigh_inner[i] = n_inner; - ipage_inner->vgot(n_inner); - if (ipage_inner->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - if (respamiddle) { - ilist_middle[inum] = i; - firstneigh_middle[i] = neighptr_middle; - numneigh_middle[i] = n_middle; - ipage_middle->vgot(n_middle); - if (ipage_middle->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - inum++; - } - - list->inum = inum; - list->inum_inner = inum; - if (respamiddle) list->inum_middle = inum; -} diff --git a/src/npair_half_respa_nsq_newton.h b/src/npair_half_respa_nsq_newton.h deleted file mode 100644 index 9050ca0fde..0000000000 --- a/src/npair_half_respa_nsq_newton.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/respa/nsq/newton, - NPairHalfRespaNsqNewton, - NP_HALF | NP_RESPA | NP_NSQ | NP_NEWTON | NP_ORTHO); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_RESPA_NSQ_NEWTON_H -#define LMP_NPAIR_HALF_RESPA_NSQ_NEWTON_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfRespaNsqNewton : public NPair { - public: - NPairHalfRespaNsqNewton(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_size_bin_newtoff.cpp b/src/npair_half_size_bin_newtoff.cpp deleted file mode 100644 index de063c1eb9..0000000000 --- a/src/npair_half_size_bin_newtoff.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_size_bin_newtoff.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeBinNewtoff::NPairHalfSizeBinNewtoff(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - binned neighbor list construction with partial Newton's 3rd law - each owned atom i checks own bin and surrounding bins in non-Newton stencil - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) -------------------------------------------------------------------------- */ - -void NPairHalfSizeBinNewtoff::build(NeighList *list) -{ - int i,j,jh,k,n,ibin,which,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutsq; - int *neighptr; - - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int history = list->history; - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int mask_history = 1 << HISTBITS; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - ibin = atom2bin[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in surrounding bins in stencil including self - // only store pair if i < j - // stores own/own pairs only once - // stores own/ghost pairs on both procs - - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (j <= i) continue; - if (exclude && exclusion(i,j,type[i],type[j],mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_size_bin_newtoff.h b/src/npair_half_size_bin_newtoff.h deleted file mode 100644 index f1f9b2a34b..0000000000 --- a/src/npair_half_size_bin_newtoff.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/bin/newtoff, - NPairHalfSizeBinNewtoff, - NP_HALF | NP_SIZE | NP_BIN | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_BIN_NEWTOFF_H -#define LMP_NPAIR_HALF_SIZE_BIN_NEWTOFF_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeBinNewtoff : public NPair { - public: - NPairHalfSizeBinNewtoff(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_size_bin_newton.cpp b/src/npair_half_size_bin_newton.cpp deleted file mode 100644 index fbf5ce14d4..0000000000 --- a/src/npair_half_size_bin_newton.cpp +++ /dev/null @@ -1,176 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_size_bin_newton.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeBinNewton::NPairHalfSizeBinNewton(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - binned neighbor list construction with full Newton's 3rd law - each owned atom i checks its own bin and other bins in Newton stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfSizeBinNewton::build(NeighList *list) -{ - int i,j,jh,k,n,ibin,which,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutsq; - int *neighptr; - - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int history = list->history; - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int mask_history = 1 << HISTBITS; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over rest of atoms in i's bin, ghosts are at end of linked list - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the right" of i - - for (j = bins[i]; j >= 0; j = bins[j]) { - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - - if (exclude && exclusion(i,j,type[i],type[j],mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - - // loop over all atoms in other bins in stencil, store every pair - - ibin = atom2bin[i]; - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (exclude && exclusion(i,j,type[i],type[j],mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_size_bin_newton.h b/src/npair_half_size_bin_newton.h deleted file mode 100644 index a592969c46..0000000000 --- a/src/npair_half_size_bin_newton.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/bin/newton, - NPairHalfSizeBinNewton, - NP_HALF | NP_SIZE | NP_BIN | NP_NEWTON | NP_ORTHO); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_BIN_NEWTON_H -#define LMP_NPAIR_HALF_SIZE_BIN_NEWTON_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeBinNewton : public NPair { - public: - NPairHalfSizeBinNewton(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_size_bin_newton_tri.cpp b/src/npair_half_size_bin_newton_tri.cpp deleted file mode 100644 index 15728a596a..0000000000 --- a/src/npair_half_size_bin_newton_tri.cpp +++ /dev/null @@ -1,147 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_size_bin_newton_tri.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeBinNewtonTri::NPairHalfSizeBinNewtonTri(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - binned neighbor list construction with Newton's 3rd law for triclinic - each owned atom i checks its own bin and other bins in triclinic stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfSizeBinNewtonTri::build(NeighList *list) -{ - int i,j,jh,k,n,ibin,which,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutsq; - int *neighptr; - - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int history = list->history; - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int mask_history = 1 << HISTBITS; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in bins in stencil - // pairs for atoms j "below" i are excluded - // below = lower z or (equal z and lower y) or (equal zy and lower x) - // (equal zyx and j <= i) - // latter excludes self-self interaction but allows superposed atoms - - ibin = atom2bin[i]; - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp) { - if (x[j][0] < xtmp) continue; - if (x[j][0] == xtmp && j <= i) continue; - } - } - - if (exclude && exclusion(i,j,type[i],type[j],mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_size_bin_newton_tri.h b/src/npair_half_size_bin_newton_tri.h deleted file mode 100644 index 50861d560e..0000000000 --- a/src/npair_half_size_bin_newton_tri.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/bin/newton/tri, - NPairHalfSizeBinNewtonTri, - NP_HALF | NP_SIZE | NP_BIN | NP_NEWTON | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_BIN_NEWTON_TRI_H -#define LMP_NPAIR_HALF_SIZE_BIN_NEWTON_TRI_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeBinNewtonTri : public NPair { - public: - NPairHalfSizeBinNewtonTri(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_size_multi_newtoff.cpp b/src/npair_half_size_multi_newtoff.cpp deleted file mode 100644 index ecb70cd6c4..0000000000 --- a/src/npair_half_size_multi_newtoff.cpp +++ /dev/null @@ -1,160 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 -es 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 "npair_half_size_multi_newtoff.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neighbor.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeMultiNewtoff::NPairHalfSizeMultiNewtoff(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - binned neighbor list construction with partial Newton's 3rd law - multi stencil is icollection-jcollection dependent - each owned atom i checks own bin and other bins in stencil - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) -------------------------------------------------------------------------- */ - -void NPairHalfSizeMultiNewtoff::build(NeighList *list) -{ - int i,j,jh,k,n,itype,jtype,icollection,jcollection,ibin,jbin,ns; - int which,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutdistsq; - int *neighptr,*s; - int js; - - int *collection = neighbor->collection; - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int history = list->history; - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int mask_history = 1 << HISTBITS; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - itype = type[i]; - icollection = collection[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - ibin = atom2bin[i]; - - // loop through stencils for all collections - for (jcollection = 0; jcollection < ncollections; jcollection++) { - - // if same collection use own bin - if (icollection == jcollection) jbin = ibin; - else jbin = coord2bin(x[i], jcollection); - - // loop over all atoms in other bins in stencil including self - // only store pair if i < j - // stores own/own pairs only once - // stores own/ghost pairs on both procs - // use full stencil for all collection combinations - - s = stencil_multi[icollection][jcollection]; - ns = nstencil_multi[icollection][jcollection]; - - for (k = 0; k < ns; k++) { - js = binhead_multi[jcollection][jbin + s[k]]; - for (j = js; j >= 0; j = bins[j]) { - if (j <= i) continue; - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutdistsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutdistsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_size_multi_newtoff.h b/src/npair_half_size_multi_newtoff.h deleted file mode 100644 index b33634e5be..0000000000 --- a/src/npair_half_size_multi_newtoff.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/multi/newtoff, - NPairHalfSizeMultiNewtoff, - NP_HALF | NP_SIZE | NP_MULTI | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_MULTI_NEWTOFF_H -#define LMP_NPAIR_HALF_SIZE_MULTI_NEWTOFF_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeMultiNewtoff : public NPair { - public: - NPairHalfSizeMultiNewtoff(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_size_multi_newton.cpp b/src/npair_half_size_multi_newton.cpp deleted file mode 100644 index ee100596bc..0000000000 --- a/src/npair_half_size_multi_newton.cpp +++ /dev/null @@ -1,213 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_size_multi_newton.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neighbor.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeMultiNewton::NPairHalfSizeMultiNewton(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - binned neighbor list construction with full Newton's 3rd law - multi stencil is icollection-jcollection dependent - each owned atom i checks its own bin and other bins in Newton stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfSizeMultiNewton::build(NeighList *list) -{ - int i,j,jh,k,n,itype,jtype,icollection,jcollection,ibin,jbin,ns,js; - int which,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutdistsq; - int *neighptr,*s; - - int *collection = neighbor->collection; - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int history = list->history; - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int mask_history = 1 << HISTBITS; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - itype = type[i]; - icollection = collection[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - ibin = atom2bin[i]; - - // loop through stencils for all collections - for (jcollection = 0; jcollection < ncollections; jcollection++) { - - // if same collection use own bin - if (icollection == jcollection) jbin = ibin; - else jbin = coord2bin(x[i], jcollection); - - // if same size: uses half stencil so check central bin - if (cutcollectionsq[icollection][icollection] == cutcollectionsq[jcollection][jcollection]){ - - if (icollection == jcollection) js = bins[i]; - else js = binhead_multi[jcollection][jbin]; - - // if same collection, - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the right" of i - - // if different collections, - // if j is owned atom, store it if j > i - // if j is ghost, only store if j coords are "above and to the right" of i - - for (j = js; j >= 0; j = bins[j]) { - if ((icollection != jcollection) && (j < i)) continue; - - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutdistsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutdistsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - } - - // for all collections, loop over all atoms in other bins in stencil, store every pair - // stencil is empty if i larger than j - // stencil is half if i same size as j - // stencil is full if i smaller than j - - s = stencil_multi[icollection][jcollection]; - ns = nstencil_multi[icollection][jcollection]; - - for (k = 0; k < ns; k++) { - js = binhead_multi[jcollection][jbin + s[k]]; - for (j = js; j >= 0; j = bins[j]) { - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutdistsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutdistsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_size_multi_newton.h b/src/npair_half_size_multi_newton.h deleted file mode 100644 index 32139136e6..0000000000 --- a/src/npair_half_size_multi_newton.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/multi/newton, - NPairHalfSizeMultiNewton, - NP_HALF | NP_SIZE | NP_MULTI | NP_NEWTON | NP_ORTHO); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_MULTI_NEWTON_H -#define LMP_NPAIR_HALF_SIZE_MULTI_NEWTON_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeMultiNewton : public NPair { - public: - NPairHalfSizeMultiNewton(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_size_multi_newton_tri.cpp b/src/npair_half_size_multi_newton_tri.cpp deleted file mode 100644 index 9a170948b9..0000000000 --- a/src/npair_half_size_multi_newton_tri.cpp +++ /dev/null @@ -1,172 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_size_multi_newton_tri.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neighbor.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeMultiNewtonTri::NPairHalfSizeMultiNewtonTri(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - binned neighbor list construction with Newton's 3rd law for triclinic - multi stencil is icollection-jcollection dependent - each owned atom i checks its own bin and other bins in triclinic stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfSizeMultiNewtonTri::build(NeighList *list) -{ - int i,j,jh,k,n,itype,jtype,icollection,jcollection,ibin,jbin,ns,js; - int which,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutdistsq; - int *neighptr,*s; - - int *collection = neighbor->collection; - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int history = list->history; - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int mask_history = 1 << HISTBITS; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - itype = type[i]; - icollection = collection[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - ibin = atom2bin[i]; - - // loop through stencils for all collections - for (jcollection = 0; jcollection < ncollections; jcollection++) { - - // if same collection use own bin - if (icollection == jcollection) jbin = ibin; - else jbin = coord2bin(x[i], jcollection); - - // loop over all atoms in bins in stencil - // stencil is empty if i larger than j - // stencil is half if i same size as j - // stencil is full if i smaller than j - // if half: pairs for atoms j "below" i are excluded - // below = lower z or (equal z and lower y) or (equal zy and lower x) - // (equal zyx and j <= i) - // latter excludes self-self interaction but allows superposed atoms - - s = stencil_multi[icollection][jcollection]; - ns = nstencil_multi[icollection][jcollection]; - - for (k = 0; k < ns; k++) { - js = binhead_multi[jcollection][jbin + s[k]]; - for (j = js; j >= 0; j = bins[j]) { - - // if same size (same collection), use half stencil - if (cutcollectionsq[icollection][icollection] == cutcollectionsq[jcollection][jcollection]){ - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp) { - if (x[j][0] < xtmp) continue; - if (x[j][0] == xtmp && j <= i) continue; - } - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutdistsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutdistsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_size_multi_newton_tri.h b/src/npair_half_size_multi_newton_tri.h deleted file mode 100644 index 10e7a90d16..0000000000 --- a/src/npair_half_size_multi_newton_tri.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/multi/newton/tri, - NPairHalfSizeMultiNewtonTri, - NP_HALF | NP_SIZE | NP_MULTI | NP_NEWTON | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_MULTI_NEWTON_TRI_H -#define LMP_NPAIR_HALF_SIZE_MULTI_NEWTON_TRI_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeMultiNewtonTri : public NPair { - public: - NPairHalfSizeMultiNewtonTri(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_size_multi_old_newtoff.cpp b/src/npair_half_size_multi_old_newtoff.cpp deleted file mode 100644 index 4f8e4a8078..0000000000 --- a/src/npair_half_size_multi_old_newtoff.cpp +++ /dev/null @@ -1,149 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_size_multi_old_newtoff.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeMultiOldNewtoff::NPairHalfSizeMultiOldNewtoff(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - binned neighbor list construction with partial Newton's 3rd law - each owned atom i checks own bin and other bins in stencil - multi-type stencil is itype dependent and is distance checked - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) -------------------------------------------------------------------------- */ - -void NPairHalfSizeMultiOldNewtoff::build(NeighList *list) -{ - int i,j,jh,k,n,itype,jtype,ibin,ns,which,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutdistsq; - int *neighptr,*s; - double *cutsq,*distsq; - - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int history = list->history; - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int mask_history = 1 << HISTBITS; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in other bins in stencil including self - // only store pair if i < j - // skip if i,j neighbor cutoff is less than bin distance - // stores own/own pairs only once - // stores own/ghost pairs on both procs - - ibin = atom2bin[i]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - cutsq = cutneighsq[itype]; - ns = nstencil_multi_old[itype]; - for (k = 0; k < ns; k++) { - for (j = binhead[ibin+s[k]]; j >= 0; j = bins[j]) { - if (j <= i) continue; - jtype = type[j]; - if (cutsq[jtype] < distsq[k]) continue; - - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutdistsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutdistsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_size_multi_old_newtoff.h b/src/npair_half_size_multi_old_newtoff.h deleted file mode 100644 index c2ca814129..0000000000 --- a/src/npair_half_size_multi_old_newtoff.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/multi/old/newtoff, - NPairHalfSizeMultiOldNewtoff, - NP_HALF | NP_SIZE | NP_MULTI_OLD | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_MULTI_OLD_NEWTOFF_H -#define LMP_NPAIR_HALF_SIZE_MULTI_OLD_NEWTOFF_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeMultiOldNewtoff : public NPair { - public: - NPairHalfSizeMultiOldNewtoff(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED -*/ diff --git a/src/npair_half_size_multi_old_newton.cpp b/src/npair_half_size_multi_old_newton.cpp deleted file mode 100644 index 753c8c7d44..0000000000 --- a/src/npair_half_size_multi_old_newton.cpp +++ /dev/null @@ -1,187 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_size_multi_old_newton.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeMultiOldNewton::NPairHalfSizeMultiOldNewton(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - binned neighbor list construction with full Newton's 3rd law - each owned atom i checks its own bin and other bins in Newton stencil - multi-type stencil is itype dependent and is distance checked - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfSizeMultiOldNewton::build(NeighList *list) -{ - int i,j,jh,k,n,itype,jtype,ibin,ns,which,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutdistsq; - int *neighptr,*s; - double *cutsq,*distsq; - - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int history = list->history; - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int mask_history = 1 << HISTBITS; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over rest of atoms in i's bin, ghosts are at end of linked list - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the right" of i - - for (j = bins[i]; j >= 0; j = bins[j]) { - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutdistsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutdistsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - - // loop over all atoms in other bins in stencil, store every pair - // skip if i,j neighbor cutoff is less than bin distance - - ibin = atom2bin[i]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - cutsq = cutneighsq[itype]; - ns = nstencil_multi_old[itype]; - for (k = 0; k < ns; k++) { - for (j = binhead[ibin+s[k]]; j >= 0; j = bins[j]) { - jtype = type[j]; - if (cutsq[jtype] < distsq[k]) continue; - - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutdistsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutdistsq) { - if (history && rsq < radsum*radsum) - j = j ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_size_multi_old_newton.h b/src/npair_half_size_multi_old_newton.h deleted file mode 100644 index 2322b5bc42..0000000000 --- a/src/npair_half_size_multi_old_newton.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/multi/old/newton, - NPairHalfSizeMultiOldNewton, - NP_HALF | NP_SIZE | NP_MULTI_OLD | NP_NEWTON | NP_ORTHO); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_MULTI_OLD_NEWTON_H -#define LMP_NPAIR_HALF_SIZE_MULTI_OLD_NEWTON_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeMultiOldNewton : public NPair { - public: - NPairHalfSizeMultiOldNewton(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED -*/ diff --git a/src/npair_half_size_multi_old_newton_tri.cpp b/src/npair_half_size_multi_old_newton_tri.cpp deleted file mode 100644 index ee7a11a36e..0000000000 --- a/src/npair_half_size_multi_old_newton_tri.cpp +++ /dev/null @@ -1,156 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_size_multi_old_newton_tri.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeMultiOldNewtonTri::NPairHalfSizeMultiOldNewtonTri(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with Newton's 3rd law for triclinic - each owned atom i checks its own bin and other bins in triclinic stencil - multi-type stencil is itype dependent and is distance checked - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfSizeMultiOldNewtonTri::build(NeighList *list) -{ - int i,j,jh,k,n,itype,jtype,ibin,ns,which,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutdistsq; - int *neighptr,*s; - double *cutsq,*distsq; - - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int history = list->history; - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int mask_history = 1 << HISTBITS; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in bins, including self, in stencil - // skip if i,j neighbor cutoff is less than bin distance - // bins below self are excluded from stencil - // pairs for atoms j "below" i are excluded - // below = lower z or (equal z and lower y) or (equal zy and lower x) - // (equal zyx and j <= i) - // latter excludes self-self interaction but allows superposed atoms - - ibin = atom2bin[i]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - cutsq = cutneighsq[itype]; - ns = nstencil_multi_old[itype]; - for (k = 0; k < ns; k++) { - for (j = binhead[ibin+s[k]]; j >= 0; j = bins[j]) { - jtype = type[j]; - if (cutsq[jtype] < distsq[k]) continue; - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp) { - if (x[j][0] < xtmp) continue; - if (x[j][0] == xtmp && j <= i) continue; - } - } - - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutdistsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutdistsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_size_multi_old_newton_tri.h b/src/npair_half_size_multi_old_newton_tri.h deleted file mode 100644 index 1658abc717..0000000000 --- a/src/npair_half_size_multi_old_newton_tri.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/multi/old/newton/tri, - NPairHalfSizeMultiOldNewtonTri, - NP_HALF | NP_SIZE | NP_MULTI_OLD | NP_NEWTON | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_MULTI_OLD_NEWTON_TRI_H -#define LMP_NPAIR_HALF_SIZE_MULTI_OLD_NEWTON_TRI_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeMultiOldNewtonTri : public NPair { - public: - NPairHalfSizeMultiOldNewtonTri(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED -*/ diff --git a/src/npair_half_size_nsq_newtoff.cpp b/src/npair_half_size_nsq_newtoff.cpp deleted file mode 100644 index 9ace347901..0000000000 --- a/src/npair_half_size_nsq_newtoff.cpp +++ /dev/null @@ -1,136 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_size_nsq_newtoff.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "group.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeNsqNewtoff::NPairHalfSizeNsqNewtoff(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - N^2 / 2 search for neighbor pairs with partial Newton's 3rd law - pair added to list if atoms i and j are both owned and i < j - pair added if j is ghost (also stored by proc owning j) -------------------------------------------------------------------------- */ - -void NPairHalfSizeNsqNewtoff::build(NeighList *list) -{ - int i,j,jh,n,bitmask,which,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutsq; - int *neighptr; - - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - int nall = nlocal + atom->nghost; - if (includegroup) { - nlocal = atom->nfirst; - bitmask = group->bitmask[includegroup]; - } - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int history = list->history; - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int mask_history = 1 << HISTBITS; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over remaining atoms, owned and ghost - - for (j = i+1; j < nall; j++) { - if (includegroup && !(mask[j] & bitmask)) continue; - if (exclude && exclusion(i,j,type[i],type[j],mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_size_nsq_newtoff.h b/src/npair_half_size_nsq_newtoff.h deleted file mode 100644 index b263a907ed..0000000000 --- a/src/npair_half_size_nsq_newtoff.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/nsq/newtoff, - NPairHalfSizeNsqNewtoff, - NP_HALF | NP_SIZE | NP_NSQ | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_NSQ_NEWTOFF_H -#define LMP_NPAIR_HALF_SIZE_NSQ_NEWTOFF_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeNsqNewtoff : public NPair { - public: - NPairHalfSizeNsqNewtoff(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_half_size_nsq_newton.cpp b/src/npair_half_size_nsq_newton.cpp deleted file mode 100644 index 05409a0bab..0000000000 --- a/src/npair_half_size_nsq_newton.cpp +++ /dev/null @@ -1,154 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_half_size_nsq_newton.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "group.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeNsqNewton::NPairHalfSizeNsqNewton(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - N^2 / 2 search for neighbor pairs with full Newton's 3rd law - pair added to list if atoms i and j are both owned and i < j - if j is ghost only me or other proc adds pair - decision based on itag,jtag tests -------------------------------------------------------------------------- */ - -void NPairHalfSizeNsqNewton::build(NeighList *list) -{ - int i,j,jh,n,itag,jtag,bitmask,which,imol,iatom,moltemplate; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutsq; - int *neighptr; - - double **x = atom->x; - double *radius = atom->radius; - tagint *tag = atom->tag; - int *type = atom->type; - int *mask = atom->mask; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int nlocal = atom->nlocal; - int nall = nlocal + atom->nghost; - if (includegroup) { - nlocal = atom->nfirst; - bitmask = group->bitmask[includegroup]; - } - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) moltemplate = 1; - else moltemplate = 0; - - int history = list->history; - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int mask_history = 1 << HISTBITS; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - itag = tag[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over remaining atoms, owned and ghost - - for (j = i+1; j < nall; j++) { - if (includegroup && !(mask[j] & bitmask)) continue; - - if (j >= nlocal) { - jtag = tag[j]; - if (itag > jtag) { - if ((itag+jtag) % 2 == 0) continue; - } else if (itag < jtag) { - if ((itag+jtag) % 2 == 1) continue; - } else { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - } - - if (exclude && exclusion(i,j,type[i],type[j],mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_half_size_nsq_newton.h b/src/npair_half_size_nsq_newton.h deleted file mode 100644 index 17735ccc45..0000000000 --- a/src/npair_half_size_nsq_newton.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/nsq/newton, - NPairHalfSizeNsqNewton, - NP_HALF | NP_SIZE | NP_NSQ | NP_NEWTON | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_NSQ_NEWTON_H -#define LMP_NPAIR_HALF_SIZE_NSQ_NEWTON_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeNsqNewton : public NPair { - public: - NPairHalfSizeNsqNewton(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_halffull_newtoff.cpp b/src/npair_halffull_newtoff.cpp deleted file mode 100644 index 475325c2f0..0000000000 --- a/src/npair_halffull_newtoff.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_halffull_newtoff.h" - -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalffullNewtoff::NPairHalffullNewtoff(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - build half list from full list - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) - works if full list is a skip list - works for owned (non-ghost) list, also for ghost list - if ghost, also store neighbors of ghost atoms & set inum,gnum correctly -------------------------------------------------------------------------- */ - -void NPairHalffullNewtoff::build(NeighList *list) -{ - int i,j,ii,jj,n,jnum,joriginal; - int *neighptr,*jlist; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int *ilist_full = list->listfull->ilist; - int *numneigh_full = list->listfull->numneigh; - int **firstneigh_full = list->listfull->firstneigh; - int inum_full = list->listfull->inum; - if (list->ghost) inum_full += list->listfull->gnum; - - int inum = 0; - ipage->reset(); - - // loop over atoms in full list - - for (ii = 0; ii < inum_full; ii++) { - n = 0; - neighptr = ipage->vget(); - - // loop over parent full list - - i = ilist_full[ii]; - jlist = firstneigh_full[i]; - jnum = numneigh_full[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (j > i) neighptr[n++] = joriginal; - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; - if (list->ghost) list->gnum = list->listfull->gnum; -} diff --git a/src/npair_halffull_newtoff.h b/src/npair_halffull_newtoff.h deleted file mode 100644 index 0a00267449..0000000000 --- a/src/npair_halffull_newtoff.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(halffull/newtoff, - NPairHalffullNewtoff, - NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | - NP_ORTHO | NP_TRI); - -NPairStyle(halffull/newtoff/skip, - NPairHalffullNewtoff, - NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | - NP_ORTHO | NP_TRI | NP_SKIP); - -NPairStyle(halffull/newtoff/ghost, - NPairHalffullNewtoff, - NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | - NP_ORTHO | NP_TRI | NP_GHOST); - -NPairStyle(halffull/newtoff/skip/ghost, - NPairHalffullNewtoff, - NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | - NP_ORTHO | NP_TRI | NP_SKIP | NP_GHOST); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALFFULL_NEWTOFF_H -#define LMP_NPAIR_HALFFULL_NEWTOFF_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalffullNewtoff : public NPair { - public: - NPairHalffullNewtoff(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_halffull_newton.cpp b/src/npair_halffull_newton.cpp deleted file mode 100644 index d9ba02d5b0..0000000000 --- a/src/npair_halffull_newton.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_halffull_newton.h" - -#include "atom.h" -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalffullNewton::NPairHalffullNewton(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - build half list from full list - pair stored once if i,j are both owned and i < j - if j is ghost, only store if j coords are "above and to the right" of i - works if full list is a skip list -------------------------------------------------------------------------- */ - -void NPairHalffullNewton::build(NeighList *list) -{ - int i,j,ii,jj,n,jnum,joriginal; - int *neighptr,*jlist; - double xtmp,ytmp,ztmp; - - double **x = atom->x; - int nlocal = atom->nlocal; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int *ilist_full = list->listfull->ilist; - int *numneigh_full = list->listfull->numneigh; - int **firstneigh_full = list->listfull->firstneigh; - int inum_full = list->listfull->inum; - - int inum = 0; - ipage->reset(); - - // loop over parent full list - - for (ii = 0; ii < inum_full; ii++) { - n = 0; - neighptr = ipage->vget(); - - i = ilist_full[ii]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - - // loop over full neighbor list - - jlist = firstneigh_full[i]; - jnum = numneigh_full[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (j < nlocal) { - if (i > j) continue; - } else { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - neighptr[n++] = joriginal; - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_halffull_newton.h b/src/npair_halffull_newton.h deleted file mode 100644 index 95cc09ec0b..0000000000 --- a/src/npair_halffull_newton.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(halffull/newton, - NPairHalffullNewton, - NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_ORTHO | NP_TRI); - -NPairStyle(halffull/newton/skip, - NPairHalffullNewton, - NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_ORTHO | NP_TRI | NP_SKIP); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALFFULL_NEWTON_H -#define LMP_NPAIR_HALFFULL_NEWTON_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalffullNewton : public NPair { - public: - NPairHalffullNewton(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_skip.cpp b/src/npair_skip.cpp deleted file mode 100644 index 4ef0573dbb..0000000000 --- a/src/npair_skip.cpp +++ /dev/null @@ -1,102 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_skip.h" - -#include "atom.h" -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairSkip::NPairSkip(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - build skip list for subset of types from parent list - works for half and full lists - works for owned (non-ghost) list, also for ghost list - iskip and ijskip flag which atom types and type pairs to skip - if ghost, also store neighbors of ghost atoms & set inum,gnum correctly -------------------------------------------------------------------------- */ - -void NPairSkip::build(NeighList *list) -{ - int i,j,ii,jj,n,itype,jnum,joriginal; - int *neighptr,*jlist; - - int *type = atom->type; - int nlocal = atom->nlocal; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int *ilist_skip = list->listskip->ilist; - int *numneigh_skip = list->listskip->numneigh; - int **firstneigh_skip = list->listskip->firstneigh; - int num_skip = list->listskip->inum; - if (list->ghost) num_skip += list->listskip->gnum; - - int *iskip = list->iskip; - int **ijskip = list->ijskip; - - int inum = 0; - ipage->reset(); - - // loop over atoms in other list - // skip I atom entirely if iskip is set for type[I] - // skip I,J pair if ijskip is set for type[I],type[J] - - for (ii = 0; ii < num_skip; ii++) { - i = ilist_skip[ii]; - itype = type[i]; - if (iskip[itype]) continue; - - n = 0; - neighptr = ipage->vget(); - - // loop over parent non-skip list - - jlist = firstneigh_skip[i]; - jnum = numneigh_skip[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (ijskip[itype][type[j]]) continue; - neighptr[n++] = joriginal; - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; - if (list->ghost) { - int num = 0; - for (i = 0; i < inum; i++) - if (ilist[i] < nlocal) num++; - else break; - list->inum = num; - list->gnum = inum - num; - } -} diff --git a/src/npair_skip.h b/src/npair_skip.h deleted file mode 100644 index aa2b14e12a..0000000000 --- a/src/npair_skip.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(skip, - NPairSkip, - NP_SKIP | NP_HALF | NP_FULL | - NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); - -NPairStyle(skip/ghost, - NPairSkip, - NP_SKIP | NP_HALF | NP_FULL | - NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_GHOST); -// clang-format on -#else - -#ifndef LMP_NPAIR_SKIP_H -#define LMP_NPAIR_SKIP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairSkip : public NPair { - public: - NPairSkip(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_skip_respa.cpp b/src/npair_skip_respa.cpp deleted file mode 100644 index 373fe3f8db..0000000000 --- a/src/npair_skip_respa.cpp +++ /dev/null @@ -1,163 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_skip_respa.h" - -#include "atom.h" -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairSkipRespa::NPairSkipRespa(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - build skip list for subset of types from parent list - iskip and ijskip flag which atom types and type pairs to skip - this is for respa lists, copy the inner/middle values from parent -------------------------------------------------------------------------- */ - -void NPairSkipRespa::build(NeighList *list) -{ - int i,j,ii,jj,n,itype,jnum,joriginal,n_inner,n_middle; - int *neighptr,*jlist,*neighptr_inner,*neighptr_middle; - - int *type = atom->type; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int *ilist_skip = list->listskip->ilist; - int *numneigh_skip = list->listskip->numneigh; - int **firstneigh_skip = list->listskip->firstneigh; - int inum_skip = list->listskip->inum; - - int *iskip = list->iskip; - int **ijskip = list->ijskip; - - int *ilist_inner = list->ilist_inner; - int *numneigh_inner = list->numneigh_inner; - int **firstneigh_inner = list->firstneigh_inner; - MyPage *ipage_inner = list->ipage_inner; - int *numneigh_inner_skip = list->listskip->numneigh_inner; - int **firstneigh_inner_skip = list->listskip->firstneigh_inner; - - int *ilist_middle,*numneigh_middle,**firstneigh_middle; - MyPage *ipage_middle; - int *numneigh_middle_skip,**firstneigh_middle_skip; - int respamiddle = list->respamiddle; - if (respamiddle) { - ilist_middle = list->ilist_middle; - numneigh_middle = list->numneigh_middle; - firstneigh_middle = list->firstneigh_middle; - ipage_middle = list->ipage_middle; - numneigh_middle_skip = list->listskip->numneigh_middle; - firstneigh_middle_skip = list->listskip->firstneigh_middle; - } - - int inum = 0; - ipage->reset(); - ipage_inner->reset(); - if (respamiddle) ipage_middle->reset(); - - // loop over atoms in other list - // skip I atom entirely if iskip is set for type[I] - // skip I,J pair if ijskip is set for type[I],type[J] - - for (ii = 0; ii < inum_skip; ii++) { - i = ilist_skip[ii]; - itype = type[i]; - if (iskip[itype]) continue; - - n = n_inner = 0; - neighptr = ipage->vget(); - neighptr_inner = ipage_inner->vget(); - if (respamiddle) { - n_middle = 0; - neighptr_middle = ipage_middle->vget(); - } - - // loop over parent outer rRESPA list - - jlist = firstneigh_skip[i]; - jnum = numneigh_skip[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (ijskip[itype][type[j]]) continue; - neighptr[n++] = joriginal; - } - - // loop over parent inner rRESPA list - - jlist = firstneigh_inner_skip[i]; - jnum = numneigh_inner_skip[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (ijskip[itype][type[j]]) continue; - neighptr_inner[n_inner++] = joriginal; - } - - // loop over parent middle rRESPA list - - if (respamiddle) { - jlist = firstneigh_middle_skip[i]; - jnum = numneigh_middle_skip[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (ijskip[itype][type[j]]) continue; - neighptr_middle[n_middle++] = joriginal; - } - } - - ilist[inum] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - ilist_inner[inum] = i; - firstneigh_inner[i] = neighptr_inner; - numneigh_inner[i] = n_inner; - ipage_inner->vgot(n); - if (ipage_inner->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - if (respamiddle) { - ilist_middle[inum] = i; - firstneigh_middle[i] = neighptr_middle; - numneigh_middle[i] = n_middle; - ipage_middle->vgot(n); - if (ipage_middle->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - inum++; - } - - list->inum = inum; - list->inum_inner = inum; - if (respamiddle) list->inum_middle = inum; -} diff --git a/src/npair_skip_respa.h b/src/npair_skip_respa.h deleted file mode 100644 index b0938287b5..0000000000 --- a/src/npair_skip_respa.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(skip/half/respa, - NPairSkipRespa, - NP_SKIP | NP_RESPA | NP_HALF | NP_FULL | - NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_SKIP_RESPA_H -#define LMP_NPAIR_SKIP_RESPA_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairSkipRespa : public NPair { - public: - NPairSkipRespa(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_skip_size.cpp b/src/npair_skip_size.cpp deleted file mode 100644 index f4fe760e08..0000000000 --- a/src/npair_skip_size.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_skip_size.h" - -#include "atom.h" -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairSkipSize::NPairSkipSize(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - build skip list for subset of types from parent list - iskip and ijskip flag which atom types and type pairs to skip -------------------------------------------------------------------------- */ - -void NPairSkipSize::build(NeighList *list) -{ - int i,j,ii,jj,n,itype,jnum,joriginal; - int *neighptr,*jlist; - - int *type = atom->type; - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int *ilist_skip = list->listskip->ilist; - int *numneigh_skip = list->listskip->numneigh; - int **firstneigh_skip = list->listskip->firstneigh; - int inum_skip = list->listskip->inum; - - int *iskip = list->iskip; - int **ijskip = list->ijskip; - - int inum = 0; - ipage->reset(); - - // loop over atoms in other list - // skip I atom entirely if iskip is set for type[I] - // skip I,J pair if ijskip is set for type[I],type[J] - - for (ii = 0; ii < inum_skip; ii++) { - i = ilist_skip[ii]; - itype = type[i]; - if (iskip[itype]) continue; - - n = 0; - neighptr = ipage->vget(); - - // loop over parent non-skip size list - - jlist = firstneigh_skip[i]; - jnum = numneigh_skip[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (ijskip[itype][type[j]]) continue; - neighptr[n++] = joriginal; - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_skip_size.h b/src/npair_skip_size.h deleted file mode 100644 index df8d479185..0000000000 --- a/src/npair_skip_size.h +++ /dev/null @@ -1,47 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(skip/half/size, - NPairSkipSize, - NP_SKIP | NP_SIZE | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_SKIP_SIZE_H -#define LMP_NPAIR_SKIP_SIZE_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairSkipSize : public NPair { - public: - NPairSkipSize(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_skip_size_off2on.cpp b/src/npair_skip_size_off2on.cpp deleted file mode 100644 index e48ca345ff..0000000000 --- a/src/npair_skip_size_off2on.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_skip_size_off2on.h" - -#include "atom.h" -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairSkipSizeOff2on::NPairSkipSizeOff2on(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - build skip list for subset of types from parent list - iskip and ijskip flag which atom types and type pairs to skip - parent non-skip list used newton off, this skip list is newton on -------------------------------------------------------------------------- */ - -void NPairSkipSizeOff2on::build(NeighList *list) -{ - int i,j,ii,jj,n,itype,jnum,joriginal; - tagint itag,jtag; - int *neighptr,*jlist; - - tagint *tag = atom->tag; - int *type = atom->type; - int nlocal = atom->nlocal; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int *ilist_skip = list->listskip->ilist; - int *numneigh_skip = list->listskip->numneigh; - int **firstneigh_skip = list->listskip->firstneigh; - int inum_skip = list->listskip->inum; - - int *iskip = list->iskip; - int **ijskip = list->ijskip; - - int inum = 0; - ipage->reset(); - - // loop over atoms in other list - // skip I atom entirely if iskip is set for type[I] - // skip I,J pair if ijskip is set for type[I],type[J] - - for (ii = 0; ii < inum_skip; ii++) { - i = ilist_skip[ii]; - itype = type[i]; - if (iskip[itype]) continue; - itag = tag[i]; - - n = 0; - neighptr = ipage->vget(); - - // loop over parent non-skip size list and optionally its history info - - jlist = firstneigh_skip[i]; - jnum = numneigh_skip[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (ijskip[itype][type[j]]) continue; - - // only keep I,J when J = ghost if Itag < Jtag - - jtag = tag[j]; - if (j >= nlocal && jtag < itag) continue; - - neighptr[n++] = joriginal; - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_skip_size_off2on.h b/src/npair_skip_size_off2on.h deleted file mode 100644 index 39aee76b09..0000000000 --- a/src/npair_skip_size_off2on.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(skip/size/off2on, - NPairSkipSizeOff2on, - NP_SKIP | NP_SIZE | NP_OFF2ON | NP_HALF | - NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_SKIP_SIZE_OFF2ON_H -#define LMP_NPAIR_SKIP_SIZE_OFF2ON_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairSkipSizeOff2on : public NPair { - public: - NPairSkipSizeOff2on(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_skip_size_off2on_oneside.cpp b/src/npair_skip_size_off2on_oneside.cpp deleted file mode 100644 index 1e4b4ac78d..0000000000 --- a/src/npair_skip_size_off2on_oneside.cpp +++ /dev/null @@ -1,163 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "npair_skip_size_off2on_oneside.h" - -#include "atom.h" -#include "domain.h" -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairSkipSizeOff2onOneside::NPairSkipSizeOff2onOneside(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - build skip list for subset of types from parent list - iskip and ijskip flag which atom types and type pairs to skip - parent non-skip list used newton off and was not onesided, - this skip list is newton on and onesided -------------------------------------------------------------------------- */ - -void NPairSkipSizeOff2onOneside::build(NeighList *list) -{ - int i,j,ii,jj,n,itype,jnum,joriginal,flip,tmp; - int *surf,*jlist; - - int *type = atom->type; - int nlocal = atom->nlocal; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int *ilist_skip = list->listskip->ilist; - int *numneigh_skip = list->listskip->numneigh; - int **firstneigh_skip = list->listskip->firstneigh; - int inum_skip = list->listskip->inum; - - int *iskip = list->iskip; - int **ijskip = list->ijskip; - - if (domain->dimension == 2) surf = atom->line; - else surf = atom->tri; - - int inum = 0; - ipage->reset(); - - // two loops over parent list required, one to count, one to store - // because onesided constraint means pair I,J may be stored with I or J - // so don't know in advance how much space to alloc for each atom's neighs - - // first loop over atoms in other list to count neighbors - // skip I atom entirely if iskip is set for type[I] - // skip I,J pair if ijskip is set for type[I],type[J] - - for (i = 0; i < nlocal; i++) numneigh[i] = 0; - - for (ii = 0; ii < inum_skip; ii++) { - i = ilist_skip[ii]; - itype = type[i]; - if (iskip[itype]) continue; - - n = 0; - - // loop over parent non-skip size list - - jlist = firstneigh_skip[i]; - jnum = numneigh_skip[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (ijskip[itype][type[j]]) continue; - - // flip I,J if necessary to satisfy onesided constraint - // do not keep if I is now ghost - - if (surf[i] >= 0) { - if (j >= nlocal) continue; - tmp = i; - i = j; - j = tmp; - flip = 1; - } else flip = 0; - - numneigh[i]++; - if (flip) i = j; - } - } - - // allocate all per-atom neigh list chunks - - for (i = 0; i < nlocal; i++) { - if (numneigh[i] == 0) continue; - n = numneigh[i]; - firstneigh[i] = ipage->get(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - // second loop over atoms in other list to store neighbors - // skip I atom entirely if iskip is set for type[I] - // skip I,J pair if ijskip is set for type[I],type[J] - - for (i = 0; i < nlocal; i++) numneigh[i] = 0; - - for (ii = 0; ii < inum_skip; ii++) { - i = ilist_skip[ii]; - itype = type[i]; - if (iskip[itype]) continue; - - // loop over parent non-skip size list and optionally its history info - - jlist = firstneigh_skip[i]; - jnum = numneigh_skip[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (ijskip[itype][type[j]]) continue; - - // flip I,J if necessary to satisfy onesided constraint - // do not keep if I is now ghost - - if (surf[i] >= 0) { - if (j >= nlocal) continue; - tmp = i; - i = j; - j = tmp; - flip = 1; - } else flip = 0; - - // store j in neigh list, not joriginal, like other neigh methods - // OK, b/c there is no special list flagging for surfs - - firstneigh[i][numneigh[i]] = j; - numneigh[i]++; - if (flip) i = j; - } - - // only add atom I to ilist if it has neighbors - - if (numneigh[i]) ilist[inum++] = i; - } - - list->inum = inum; -} diff --git a/src/npair_skip_size_off2on_oneside.h b/src/npair_skip_size_off2on_oneside.h deleted file mode 100644 index 3f1cd6ef34..0000000000 --- a/src/npair_skip_size_off2on_oneside.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NPAIR_CLASS -// clang-format off -NPairStyle(skip/size/off2on/oneside, - NPairSkipSizeOff2onOneside, - NP_SKIP | NP_SIZE | NP_OFF2ON | NP_ONESIDE | NP_HALF | - NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | - NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_SKIP_SIZE_OFF2ON_ONESIDE_H -#define LMP_NPAIR_SKIP_SIZE_OFF2ON_ONESIDE_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairSkipSizeOff2onOneside : public NPair { - public: - NPairSkipSizeOff2onOneside(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/nstencil_half_bin_3d_tri.cpp b/src/nstencil_bin.cpp similarity index 51% rename from src/nstencil_half_bin_3d_tri.cpp rename to src/nstencil_bin.cpp index 8d1920ae8c..c5026cb338 100644 --- a/src/nstencil_half_bin_3d_tri.cpp +++ b/src/nstencil_bin.cpp @@ -12,28 +12,55 @@ See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ -#include "nstencil_half_bin_3d_tri.h" +#include "nstencil_bin.h" using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -NStencilHalfBin3dTri::NStencilHalfBin3dTri(LAMMPS *lmp) : - NStencil(lmp) {} +template +NStencilBin::NStencilBin(LAMMPS *lmp) : NStencil(lmp) {} /* ---------------------------------------------------------------------- create stencil based on bin geometry and cutoff ------------------------------------------------------------------------- */ -void NStencilHalfBin3dTri::create() +template +void NStencilBin::create() { int i,j,k; + bool bin_include; + + // For half stencils, only the upper plane is needed + int sy_min = sy; + int sz_min = sz; + if (HALF && (!DIM_3D)) sy_min = 0; + if (HALF && DIM_3D) sz_min = 0; nstencil = 0; - for (k = 0; k <= sz; k++) - for (j = -sy; j <= sy; j++) - for (i = -sx; i <= sx; i++) + for (k = -sz_min; k <= sz; k++) { + for (j = -sy_min; j <= sy; j++) { + for (i = -sx; i <= sx; i++) { + + // Half and ortho stencils only include own and "upper right" bins + if (HALF && (!DIM_3D) && (!TRI)) + if (! (j > 0 || (j == 0 && i >= 0))) continue; + if (HALF && DIM_3D && (!TRI)) + if (! (k > 0 || j > 0 || (j == 0 && i >= 0))) continue; + if (bin_distance(i,j,k) < cutneighmaxsq) stencil[nstencil++] = k*mbiny*mbinx + j*mbinx + i; + } + } + } +} + +namespace LAMMPS_NS { +template class NStencilBin<0,0,0>; +template class NStencilBin<0,1,0>; +template class NStencilBin<1,0,0>; +template class NStencilBin<1,0,1>; +template class NStencilBin<1,1,0>; +template class NStencilBin<1,1,1>; } diff --git a/src/nstencil_bin.h b/src/nstencil_bin.h new file mode 100644 index 0000000000..a10150fcda --- /dev/null +++ b/src/nstencil_bin.h @@ -0,0 +1,69 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 NSTENCIL_CLASS +// clang-format off +typedef NStencilBin<0, 0, 0> NStencilFullBin2d; +NStencilStyle(full/bin/2d, + NStencilFullBin2d, + NS_FULL | NS_BIN | NS_2D | NS_ORTHO | NS_TRI); + +typedef NStencilBin<0, 1, 0> NStencilFullBin3d; +NStencilStyle(full/bin/3d, + NStencilFullBin3d, + NS_FULL | NS_BIN | NS_3D | NS_ORTHO | NS_TRI); + +typedef NStencilBin<1, 0, 0> NStencilHalfBin2d; +NStencilStyle(half/bin/2d, + NStencilHalfBin2d, + NS_HALF | NS_BIN | NS_2D | NS_ORTHO); + +typedef NStencilBin<1, 0, 1> NStencilHalfBin2dTri; +NStencilStyle(half/bin/2d/tri, + NStencilHalfBin2dTri, + NS_HALF | NS_BIN | NS_2D | NS_TRI); + +typedef NStencilBin<1, 1, 0> NStencilHalfBin3d; +NStencilStyle(half/bin/3d, + NStencilHalfBin3d, + NS_HALF | NS_BIN | NS_3D | NS_ORTHO); + +typedef NStencilBin<1, 1, 1> NStencilHalfBin3dTri; +NStencilStyle(half/bin/3d/tri, + NStencilHalfBin3dTri, + NS_HALF | NS_BIN | NS_3D | NS_TRI); +// clang-format on +#else + +#ifndef LMP_NSTENCIL_BIN_H +#define LMP_NSTENCIL_BIN_H + +#include "nstencil.h" + +namespace LAMMPS_NS { + +template +class NStencilBin : public NStencil { + public: + NStencilBin(class LAMMPS *); + void create() override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif + +/* ERROR/WARNING messages: + +*/ diff --git a/src/nstencil_full_bin_2d.cpp b/src/nstencil_full_bin_2d.cpp deleted file mode 100644 index ba4ca97ed6..0000000000 --- a/src/nstencil_full_bin_2d.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "nstencil_full_bin_2d.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NStencilFullBin2d::NStencilFullBin2d(LAMMPS *lmp) : NStencil(lmp) {} - -/* ---------------------------------------------------------------------- - create stencil based on bin geometry and cutoff -------------------------------------------------------------------------- */ - -void NStencilFullBin2d::create() -{ - int i,j; - - nstencil = 0; - - for (j = -sy; j <= sy; j++) - for (i = -sx; i <= sx; i++) - if (bin_distance(i,j,0) < cutneighmaxsq) - stencil[nstencil++] = j*mbinx + i; -} diff --git a/src/nstencil_full_bin_2d.h b/src/nstencil_full_bin_2d.h deleted file mode 100644 index f4c630d3c2..0000000000 --- a/src/nstencil_full_bin_2d.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(full/bin/2d, - NStencilFullBin2d, - NS_FULL | NS_BIN | NS_2D | NS_ORTHO | NS_TRI); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_FULL_BIN_2D_H -#define LMP_NSTENCIL_FULL_BIN_2D_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilFullBin2d : public NStencil { - public: - NStencilFullBin2d(class LAMMPS *); - void create() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/nstencil_full_bin_3d.cpp b/src/nstencil_full_bin_3d.cpp deleted file mode 100644 index 8aa593eb0b..0000000000 --- a/src/nstencil_full_bin_3d.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "nstencil_full_bin_3d.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NStencilFullBin3d::NStencilFullBin3d(LAMMPS *lmp) : NStencil(lmp) {} - -/* ---------------------------------------------------------------------- - create stencil based on bin geometry and cutoff -------------------------------------------------------------------------- */ - -void NStencilFullBin3d::create() -{ - int i,j,k; - - nstencil = 0; - - for (k = -sz; k <= sz; k++) - for (j = -sy; j <= sy; j++) - for (i = -sx; i <= sx; i++) - if (bin_distance(i,j,k) < cutneighmaxsq) - stencil[nstencil++] = k*mbiny*mbinx + j*mbinx + i; -} diff --git a/src/nstencil_full_bin_3d.h b/src/nstencil_full_bin_3d.h deleted file mode 100644 index 463dda1f62..0000000000 --- a/src/nstencil_full_bin_3d.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(full/bin/3d, - NStencilFullBin3d, - NS_FULL | NS_BIN | NS_3D | NS_ORTHO | NS_TRI); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_FULL_BIN_3D_H -#define LMP_NSTENCIL_FULL_BIN_3D_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilFullBin3d : public NStencil { - public: - NStencilFullBin3d(class LAMMPS *); - void create() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/nstencil_full_ghost_bin_2d.cpp b/src/nstencil_full_ghost_bin_2d.cpp deleted file mode 100644 index b5a6bac56c..0000000000 --- a/src/nstencil_full_ghost_bin_2d.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "nstencil_full_ghost_bin_2d.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NStencilFullGhostBin2d::NStencilFullGhostBin2d(LAMMPS *lmp) : NStencil(lmp) -{ - xyzflag = 1; -} - -/* ---------------------------------------------------------------------- - create stencil based on bin geometry and cutoff -------------------------------------------------------------------------- */ - -void NStencilFullGhostBin2d::create() -{ - int i,j; - - nstencil = 0; - - for (j = -sy; j <= sy; j++) - for (i = -sx; i <= sx; i++) - if (bin_distance(i,j,0) < cutneighmaxsq) { - stencilxyz[nstencil][0] = i; - stencilxyz[nstencil][1] = j; - stencilxyz[nstencil][2] = 0; - stencil[nstencil++] = j*mbinx + i; - } -} diff --git a/src/nstencil_full_ghost_bin_2d.h b/src/nstencil_full_ghost_bin_2d.h deleted file mode 100644 index 6326aae5ea..0000000000 --- a/src/nstencil_full_ghost_bin_2d.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(full/ghost/bin/2d, - NStencilFullGhostBin2d, - NS_FULL | NS_GHOST | NS_BIN | NS_2D | NS_ORTHO | NS_TRI); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_FULL_GHOST_BIN_2D_H -#define LMP_NSTENCIL_FULL_GHOST_BIN_2D_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilFullGhostBin2d : public NStencil { - public: - NStencilFullGhostBin2d(class LAMMPS *); - void create() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/nstencil_full_ghost_bin_3d.cpp b/src/nstencil_full_ghost_bin_3d.cpp deleted file mode 100644 index 2023495c34..0000000000 --- a/src/nstencil_full_ghost_bin_3d.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "nstencil_full_ghost_bin_3d.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NStencilFullGhostBin3d::NStencilFullGhostBin3d(LAMMPS *lmp) : NStencil(lmp) -{ - xyzflag = 1; -} - -/* ---------------------------------------------------------------------- - create stencil based on bin geometry and cutoff -------------------------------------------------------------------------- */ - -void NStencilFullGhostBin3d::create() -{ - int i,j,k; - - nstencil = 0; - - for (k = -sz; k <= sz; k++) - for (j = -sy; j <= sy; j++) - for (i = -sx; i <= sx; i++) - if (bin_distance(i,j,k) < cutneighmaxsq) { - stencilxyz[nstencil][0] = i; - stencilxyz[nstencil][1] = j; - stencilxyz[nstencil][2] = k; - stencil[nstencil++] = k*mbiny*mbinx + j*mbinx + i; - } -} diff --git a/src/nstencil_full_ghost_bin_3d.h b/src/nstencil_full_ghost_bin_3d.h deleted file mode 100644 index eed98279aa..0000000000 --- a/src/nstencil_full_ghost_bin_3d.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(full/ghost/bin/3d, - NStencilFullGhostBin3d, - NS_FULL | NS_GHOST | NS_BIN | NS_3D | NS_ORTHO | NS_TRI); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_FULL_GHOST_BIN_3D_H -#define LMP_NSTENCIL_FULL_GHOST_BIN_3D_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilFullGhostBin3d : public NStencil { - public: - NStencilFullGhostBin3d(class LAMMPS *); - void create() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/nstencil_full_multi_2d.cpp b/src/nstencil_full_multi_2d.cpp deleted file mode 100644 index 52ae88d09e..0000000000 --- a/src/nstencil_full_multi_2d.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "nstencil_full_multi_2d.h" - -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NStencilFullMulti2d::NStencilFullMulti2d(LAMMPS *lmp) : NStencil(lmp) {} - -/* ---------------------------------------------------------------------- */ - -void NStencilFullMulti2d::set_stencil_properties() -{ - int n = ncollections; - int i, j; - - // Always look up neighbor using full stencil and neighbor's bin - - for (i = 0; i < n; i++) { - for (j = 0; j < n; j++) { - flag_half_multi[i][j] = false; - flag_skip_multi[i][j] = false; - bin_collection_multi[i][j] = j; - } - } -} - -/* ---------------------------------------------------------------------- - create stencils based on bin geometry and cutoff -------------------------------------------------------------------------- */ - -void NStencilFullMulti2d::create() -{ - int icollection, jcollection, bin_collection, i, j, ns; - int n = ncollections; - double cutsq; - - - for (icollection = 0; icollection < n; icollection++) { - for (jcollection = 0; jcollection < n; jcollection++) { - if (flag_skip_multi[icollection][jcollection]) { - nstencil_multi[icollection][jcollection] = 0; - continue; - } - - ns = 0; - - sx = stencil_sx_multi[icollection][jcollection]; - sy = stencil_sy_multi[icollection][jcollection]; - - mbinx = stencil_mbinx_multi[icollection][jcollection]; - mbiny = stencil_mbiny_multi[icollection][jcollection]; - - bin_collection = bin_collection_multi[icollection][jcollection]; - - cutsq = cutcollectionsq[icollection][jcollection]; - - for (j = -sy; j <= sy; j++) - for (i = -sx; i <= sx; i++) - if (bin_distance_multi(i,j,0,bin_collection) < cutsq) - stencil_multi[icollection][jcollection][ns++] = j*mbinx + i; - - nstencil_multi[icollection][jcollection] = ns; - } - } -} diff --git a/src/nstencil_full_multi_2d.h b/src/nstencil_full_multi_2d.h deleted file mode 100644 index 2ef43f1999..0000000000 --- a/src/nstencil_full_multi_2d.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(full/multi/2d, - NStencilFullMulti2d, NS_FULL | NS_MULTI | NS_2D | NS_ORTHO | NS_TRI); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_FULL_MULTI_2D_H -#define LMP_NSTENCIL_FULL_MULTI_2D_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilFullMulti2d : public NStencil { - public: - NStencilFullMulti2d(class LAMMPS *); - void create() override; - - protected: - void set_stencil_properties() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/nstencil_full_multi_3d.cpp b/src/nstencil_full_multi_3d.cpp deleted file mode 100644 index d48cf0c8d7..0000000000 --- a/src/nstencil_full_multi_3d.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "nstencil_full_multi_3d.h" - -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NStencilFullMulti3d::NStencilFullMulti3d(LAMMPS *lmp) : NStencil(lmp) {} - -/* ---------------------------------------------------------------------- */ - -void NStencilFullMulti3d::set_stencil_properties() -{ - int n = ncollections; - int i, j; - - // Always look up neighbor using full stencil and neighbor's bin - // Stencil cutoff set by i-j cutoff - - for (i = 0; i < n; i++) { - for (j = 0; j < n; j++) { - flag_half_multi[i][j] = true; - flag_skip_multi[i][j] = false; - bin_collection_multi[i][j] = j; - } - } -} - -/* ---------------------------------------------------------------------- - create stencils based on bin geometry and cutoff -------------------------------------------------------------------------- */ - -void NStencilFullMulti3d::create() -{ - int icollection, jcollection, bin_collection, i, j, k, ns; - int n = ncollections; - double cutsq; - - - for (icollection = 0; icollection < n; icollection++) { - for (jcollection = 0; jcollection < n; jcollection++) { - if (flag_skip_multi[icollection][jcollection]) { - nstencil_multi[icollection][jcollection] = 0; - continue; - } - - ns = 0; - - sx = stencil_sx_multi[icollection][jcollection]; - sy = stencil_sy_multi[icollection][jcollection]; - sz = stencil_sz_multi[icollection][jcollection]; - - mbinx = stencil_mbinx_multi[icollection][jcollection]; - mbiny = stencil_mbiny_multi[icollection][jcollection]; - mbinz = stencil_mbinz_multi[icollection][jcollection]; - - bin_collection = bin_collection_multi[icollection][jcollection]; - - cutsq = cutcollectionsq[icollection][jcollection]; - - for (k = -sz; k <= sz; k++) - for (j = -sy; j <= sy; j++) - for (i = -sx; i <= sx; i++) - if (bin_distance_multi(i,j,k,bin_collection) < cutsq) - stencil_multi[icollection][jcollection][ns++] = - k*mbiny*mbinx + j*mbinx + i; - - nstencil_multi[icollection][jcollection] = ns; - } - } -} diff --git a/src/nstencil_full_multi_3d.h b/src/nstencil_full_multi_3d.h deleted file mode 100644 index 894c688a9f..0000000000 --- a/src/nstencil_full_multi_3d.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(full/multi/3d, - NStencilFullMulti3d, NS_FULL | NS_MULTI | NS_3D | NS_ORTHO | NS_TRI); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_FULL_MULTI_3D_H -#define LMP_NSTENCIL_FULL_MULTI_3D_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilFullMulti3d : public NStencil { - public: - NStencilFullMulti3d(class LAMMPS *); - void create() override; - - protected: - void set_stencil_properties() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/nstencil_full_multi_old_2d.cpp b/src/nstencil_full_multi_old_2d.cpp deleted file mode 100644 index d653e1080e..0000000000 --- a/src/nstencil_full_multi_old_2d.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "nstencil_full_multi_old_2d.h" -#include "atom.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NStencilFullMultiOld2d::NStencilFullMultiOld2d(LAMMPS *lmp) : NStencil(lmp) {} - -/* ---------------------------------------------------------------------- - create stencil based on bin geometry and cutoff -------------------------------------------------------------------------- */ - -void NStencilFullMultiOld2d::create() -{ - int i,j,n; - double rsq,typesq; - int *s; - double *distsq; - - int ntypes = atom->ntypes; - for (int itype = 1; itype <= ntypes; itype++) { - typesq = cuttypesq[itype]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - n = 0; - for (j = -sy; j <= sy; j++) - for (i = -sx; i <= sx; i++) { - rsq = bin_distance(i,j,0); - if (rsq < typesq) { - distsq[n] = rsq; - s[n++] = j*mbinx + i; - } - } - nstencil_multi_old[itype] = n; - } -} diff --git a/src/nstencil_full_multi_old_2d.h b/src/nstencil_full_multi_old_2d.h deleted file mode 100644 index 9b56be0793..0000000000 --- a/src/nstencil_full_multi_old_2d.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(full/multi/old/2d, - NStencilFullMultiOld2d, - NS_FULL | NS_MULTI_OLD | NS_2D | NS_ORTHO | NS_TRI); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_FULL_MULTI_OLD_2D_H -#define LMP_NSTENCIL_FULL_MULTI_OLD_2D_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilFullMultiOld2d : public NStencil { - public: - NStencilFullMultiOld2d(class LAMMPS *); - void create() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/nstencil_full_multi_old_3d.cpp b/src/nstencil_full_multi_old_3d.cpp deleted file mode 100644 index 849ee5a9f9..0000000000 --- a/src/nstencil_full_multi_old_3d.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "nstencil_full_multi_old_3d.h" -#include "atom.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NStencilFullMultiOld3d::NStencilFullMultiOld3d(LAMMPS *lmp) : NStencil(lmp) {} - -/* ---------------------------------------------------------------------- - create stencil based on bin geometry and cutoff -------------------------------------------------------------------------- */ - -void NStencilFullMultiOld3d::create() -{ - int i,j,k,n; - double rsq,typesq; - int *s; - double *distsq; - - int ntypes = atom->ntypes; - for (int itype = 1; itype <= ntypes; itype++) { - typesq = cuttypesq[itype]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - n = 0; - for (k = -sz; k <= sz; k++) - for (j = -sy; j <= sy; j++) - for (i = -sx; i <= sx; i++) { - rsq = bin_distance(i,j,k); - if (rsq < typesq) { - distsq[n] = rsq; - s[n++] = k*mbiny*mbinx + j*mbinx + i; - } - } - nstencil_multi_old[itype] = n; - } -} diff --git a/src/nstencil_full_multi_old_3d.h b/src/nstencil_full_multi_old_3d.h deleted file mode 100644 index d19da9c95f..0000000000 --- a/src/nstencil_full_multi_old_3d.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(full/multi/old/3d, - NStencilFullMultiOld3d, - NS_FULL | NS_MULTI_OLD | NS_3D | NS_ORTHO | NS_TRI); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_FULL_MULTI_OLD_3D_H -#define LMP_NSTENCIL_FULL_MULTI_OLD_3D_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilFullMultiOld3d : public NStencil { - public: - NStencilFullMultiOld3d(class LAMMPS *); - void create() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/nstencil_half_bin_2d.cpp b/src/nstencil_half_bin_2d.cpp deleted file mode 100644 index 004d6a8016..0000000000 --- a/src/nstencil_half_bin_2d.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "nstencil_half_bin_2d.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NStencilHalfBin2d::NStencilHalfBin2d(LAMMPS *lmp) : NStencil(lmp) {} - -/* ---------------------------------------------------------------------- - create stencil based on bin geometry and cutoff -------------------------------------------------------------------------- */ - -void NStencilHalfBin2d::create() -{ - int i,j; - - nstencil = 0; - - for (j = 0; j <= sy; j++) - for (i = -sx; i <= sx; i++) - if (j > 0 || (j == 0 && i > 0)) - if (bin_distance(i,j,0) < cutneighmaxsq) - stencil[nstencil++] = j*mbinx + i; -} diff --git a/src/nstencil_half_bin_2d.h b/src/nstencil_half_bin_2d.h deleted file mode 100644 index f11138dd54..0000000000 --- a/src/nstencil_half_bin_2d.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(half/bin/2d, - NStencilHalfBin2d, - NS_HALF | NS_BIN | NS_2D | NS_ORTHO); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_HALF_BIN_2D_H -#define LMP_NSTENCIL_HALF_BIN_2D_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilHalfBin2d : public NStencil { - public: - NStencilHalfBin2d(class LAMMPS *); - void create() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/nstencil_half_bin_2d_tri.cpp b/src/nstencil_half_bin_2d_tri.cpp deleted file mode 100644 index 9f5ace1ed1..0000000000 --- a/src/nstencil_half_bin_2d_tri.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "nstencil_half_bin_2d_tri.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NStencilHalfBin2dTri::NStencilHalfBin2dTri(LAMMPS *lmp) : - NStencil(lmp) {} - -/* ---------------------------------------------------------------------- - create stencil based on bin geometry and cutoff -------------------------------------------------------------------------- */ - -void NStencilHalfBin2dTri::create() -{ - int i,j; - - nstencil = 0; - - for (j = 0; j <= sy; j++) - for (i = -sx; i <= sx; i++) - if (bin_distance(i,j,0) < cutneighmaxsq) - stencil[nstencil++] = j*mbinx + i; -} diff --git a/src/nstencil_half_bin_2d_tri.h b/src/nstencil_half_bin_2d_tri.h deleted file mode 100644 index 5088bc2edc..0000000000 --- a/src/nstencil_half_bin_2d_tri.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(half/bin/2d/tri, - NStencilHalfBin2dTri, - NS_HALF | NS_BIN | NS_2D | NS_TRI); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_HALF_BIN_2D_TRI_H -#define LMP_NSTENCIL_HALF_BIN_2D_TRI_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilHalfBin2dTri : public NStencil { - public: - NStencilHalfBin2dTri(class LAMMPS *); - void create() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/nstencil_half_bin_3d.cpp b/src/nstencil_half_bin_3d.cpp deleted file mode 100644 index a8cacdb601..0000000000 --- a/src/nstencil_half_bin_3d.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "nstencil_half_bin_3d.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NStencilHalfBin3d::NStencilHalfBin3d(LAMMPS *lmp) : NStencil(lmp) {} - -/* ---------------------------------------------------------------------- - create stencil based on bin geometry and cutoff -------------------------------------------------------------------------- */ - -void NStencilHalfBin3d::create() -{ - int i,j,k; - - nstencil = 0; - - for (k = 0; k <= sz; k++) - for (j = -sy; j <= sy; j++) - for (i = -sx; i <= sx; i++) - if (k > 0 || j > 0 || (j == 0 && i > 0)) - if (bin_distance(i,j,k) < cutneighmaxsq) - stencil[nstencil++] = k*mbiny*mbinx + j*mbinx + i; -} diff --git a/src/nstencil_half_bin_3d.h b/src/nstencil_half_bin_3d.h deleted file mode 100644 index f235d6688b..0000000000 --- a/src/nstencil_half_bin_3d.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(half/bin/3d, - NStencilHalfBin3d, - NS_HALF | NS_BIN | NS_3D | NS_ORTHO); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_HALF_BIN_3D_H -#define LMP_NSTENCIL_HALF_BIN_3D_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilHalfBin3d : public NStencil { - public: - NStencilHalfBin3d(class LAMMPS *); - void create() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/nstencil_half_bin_3d_tri.h b/src/nstencil_half_bin_3d_tri.h deleted file mode 100644 index fa2975ef0a..0000000000 --- a/src/nstencil_half_bin_3d_tri.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(half/bin/3d/tri, - NStencilHalfBin3dTri, - NS_HALF | NS_BIN | NS_3D | NS_TRI); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_HALF_BIN_3D_TRI_H -#define LMP_NSTENCIL_HALF_BIN_3D_TRI_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilHalfBin3dTri : public NStencil { - public: - NStencilHalfBin3dTri(class LAMMPS *); - void create() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/nstencil_half_multi_2d.cpp b/src/nstencil_half_multi_2d.cpp deleted file mode 100644 index 81713e183d..0000000000 --- a/src/nstencil_half_multi_2d.cpp +++ /dev/null @@ -1,103 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "nstencil_half_multi_2d.h" - -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NStencilHalfMulti2d::NStencilHalfMulti2d(LAMMPS *lmp) : - NStencil(lmp) {} - -/* ---------------------------------------------------------------------- */ - -void NStencilHalfMulti2d::set_stencil_properties() -{ - int n = ncollections; - int i, j; - - // Cross collections: use full stencil, looking one way through hierarchy - // smaller -> larger => use full stencil in larger bin - // larger -> smaller => no nstencil required - // If cut offs are same, use half stencil - - for (i = 0; i < n; i++) { - for (j = 0; j < n; j++) { - if(cutcollectionsq[i][i] > cutcollectionsq[j][j]) continue; - - flag_skip_multi[i][j] = false; - - if(cutcollectionsq[i][i] == cutcollectionsq[j][j]){ - flag_half_multi[i][j] = true; - bin_collection_multi[i][j] = i; - } else { - flag_half_multi[i][j] = false; - bin_collection_multi[i][j] = j; - } - } - } -} - -/* ---------------------------------------------------------------------- - create stencils based on bin geometry and cutoff -------------------------------------------------------------------------- */ - -void NStencilHalfMulti2d::create() -{ - int icollection, jcollection, bin_collection, i, j, ns; - int n = ncollections; - double cutsq; - - - for (icollection = 0; icollection < n; icollection++) { - for (jcollection = 0; jcollection < n; jcollection++) { - if (flag_skip_multi[icollection][jcollection]) { - nstencil_multi[icollection][jcollection] = 0; - continue; - } - - ns = 0; - - sx = stencil_sx_multi[icollection][jcollection]; - sy = stencil_sy_multi[icollection][jcollection]; - - mbinx = stencil_mbinx_multi[icollection][jcollection]; - mbiny = stencil_mbiny_multi[icollection][jcollection]; - - bin_collection = bin_collection_multi[icollection][jcollection]; - - cutsq = cutcollectionsq[icollection][jcollection]; - - if (flag_half_multi[icollection][jcollection]) { - for (j = 0; j <= sy; j++) - for (i = -sx; i <= sx; i++) - if (j > 0 || (j == 0 && i > 0)) { - if (bin_distance_multi(i,j,0,bin_collection) < cutsq) - stencil_multi[icollection][jcollection][ns++] = j*mbinx + i; - } - } else { - for (j = -sy; j <= sy; j++) - for (i = -sx; i <= sx; i++) - if (bin_distance_multi(i,j,0,bin_collection) < cutsq) - stencil_multi[icollection][jcollection][ns++] = j*mbinx + i; - } - - nstencil_multi[icollection][jcollection] = ns; - } - } -} - diff --git a/src/nstencil_half_multi_2d.h b/src/nstencil_half_multi_2d.h deleted file mode 100644 index 043d160b9e..0000000000 --- a/src/nstencil_half_multi_2d.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(half/multi/2d, - NStencilHalfMulti2d, NS_HALF | NS_MULTI | NS_2D | NS_ORTHO); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_HALF_MULTI_2D_H -#define LMP_NSTENCIL_HALF_MULTI_2D_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilHalfMulti2d : public NStencil { - public: - NStencilHalfMulti2d(class LAMMPS *); - void create() override; - - protected: - void set_stencil_properties() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/nstencil_half_multi_2d_tri.cpp b/src/nstencil_half_multi_2d_tri.cpp deleted file mode 100644 index d53c503fad..0000000000 --- a/src/nstencil_half_multi_2d_tri.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "nstencil_half_multi_2d_tri.h" - -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NStencilHalfMulti2dTri::NStencilHalfMulti2dTri(LAMMPS *lmp) : - NStencil(lmp) {} - -/* ---------------------------------------------------------------------- */ - -void NStencilHalfMulti2dTri::set_stencil_properties() -{ - int n = ncollections; - int i, j; - - // Cross collections: use full stencil, looking one way through hierarchy - // smaller -> larger => use full stencil in larger bin - // larger -> smaller => no nstencil required - // If cut offs are same, use half stencil - - for (i = 0; i < n; i++) { - for (j = 0; j < n; j++) { - if(cutcollectionsq[i][i] > cutcollectionsq[j][j]) continue; - - flag_skip_multi[i][j] = false; - - if(cutcollectionsq[i][i] == cutcollectionsq[j][j]){ - flag_half_multi[i][j] = true; - bin_collection_multi[i][j] = i; - } else { - flag_half_multi[i][j] = false; - bin_collection_multi[i][j] = j; - } - } - } -} - -/* ---------------------------------------------------------------------- - create stencils based on bin geometry and cutoff -------------------------------------------------------------------------- */ - -void NStencilHalfMulti2dTri::create() -{ - int icollection, jcollection, bin_collection, i, j, ns; - int n = ncollections; - double cutsq; - - - for (icollection = 0; icollection < n; icollection++) { - for (jcollection = 0; jcollection < n; jcollection++) { - if (flag_skip_multi[icollection][jcollection]) { - nstencil_multi[icollection][jcollection] = 0; - continue; - } - - ns = 0; - - sx = stencil_sx_multi[icollection][jcollection]; - sy = stencil_sy_multi[icollection][jcollection]; - - mbinx = stencil_mbinx_multi[icollection][jcollection]; - mbiny = stencil_mbiny_multi[icollection][jcollection]; - - bin_collection = bin_collection_multi[icollection][jcollection]; - - cutsq = cutcollectionsq[icollection][jcollection]; - - if (flag_half_multi[icollection][jcollection]) { - for (j = 0; j <= sy; j++) - for (i = -sx; i <= sx; i++) - if (bin_distance_multi(i,j,0,bin_collection) < cutsq) - stencil_multi[icollection][jcollection][ns++] = j*mbinx + i; - } else { - for (j = -sy; j <= sy; j++) - for (i = -sx; i <= sx; i++) - if (bin_distance_multi(i,j,0,bin_collection) < cutsq) - stencil_multi[icollection][jcollection][ns++] = j*mbinx + i; - } - - nstencil_multi[icollection][jcollection] = ns; - } - } -} diff --git a/src/nstencil_half_multi_2d_tri.h b/src/nstencil_half_multi_2d_tri.h deleted file mode 100644 index 1d1a469e72..0000000000 --- a/src/nstencil_half_multi_2d_tri.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(half/multi/2d/tri, - NStencilHalfMulti2dTri, NS_HALF | NS_MULTI | NS_2D | NS_TRI); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_HALF_MULTI_2D_TRI_H -#define LMP_NSTENCIL_HALF_MULTI_2D_TRI_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilHalfMulti2dTri : public NStencil { - public: - NStencilHalfMulti2dTri(class LAMMPS *); - void create() override; - - protected: - void set_stencil_properties() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/nstencil_half_multi_3d.cpp b/src/nstencil_half_multi_3d.cpp deleted file mode 100644 index ca4b4c4111..0000000000 --- a/src/nstencil_half_multi_3d.cpp +++ /dev/null @@ -1,109 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "nstencil_half_multi_3d.h" - -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NStencilHalfMulti3d::NStencilHalfMulti3d(LAMMPS *lmp) : - NStencil(lmp) {} - -/* ---------------------------------------------------------------------- */ - -void NStencilHalfMulti3d::set_stencil_properties() -{ - int n = ncollections; - int i, j; - - // Cross collections: use full stencil, looking one way through hierarchy - // smaller -> larger => use full stencil in larger bin - // larger -> smaller => no nstencil required - // If cut offs are same, use half stencil - - for (i = 0; i < n; i++) { - for (j = 0; j < n; j++) { - if(cutcollectionsq[i][i] > cutcollectionsq[j][j]) continue; - - flag_skip_multi[i][j] = false; - - if(cutcollectionsq[i][i] == cutcollectionsq[j][j]){ - flag_half_multi[i][j] = true; - bin_collection_multi[i][j] = i; - } else { - flag_half_multi[i][j] = false; - bin_collection_multi[i][j] = j; - } - } - } -} - -/* ---------------------------------------------------------------------- - create stencils based on bin geometry and cutoff -------------------------------------------------------------------------- */ - -void NStencilHalfMulti3d::create() -{ - int icollection, jcollection, bin_collection, i, j, k, ns; - int n = ncollections; - double cutsq; - - - for (icollection = 0; icollection < n; icollection++) { - for (jcollection = 0; jcollection < n; jcollection++) { - if (flag_skip_multi[icollection][jcollection]) { - nstencil_multi[icollection][jcollection] = 0; - continue; - } - - ns = 0; - - sx = stencil_sx_multi[icollection][jcollection]; - sy = stencil_sy_multi[icollection][jcollection]; - sz = stencil_sz_multi[icollection][jcollection]; - - mbinx = stencil_mbinx_multi[icollection][jcollection]; - mbiny = stencil_mbiny_multi[icollection][jcollection]; - mbinz = stencil_mbinz_multi[icollection][jcollection]; - - bin_collection = bin_collection_multi[icollection][jcollection]; - - cutsq = cutcollectionsq[icollection][jcollection]; - - if (flag_half_multi[icollection][jcollection]) { - for (k = 0; k <= sz; k++) - for (j = -sy; j <= sy; j++) - for (i = -sx; i <= sx; i++) - if (k > 0 || j > 0 || (j == 0 && i > 0)) { - if (bin_distance_multi(i,j,k,bin_collection) < cutsq) - stencil_multi[icollection][jcollection][ns++] = - k*mbiny*mbinx + j*mbinx + i; - } - } else { - for (k = -sz; k <= sz; k++) - for (j = -sy; j <= sy; j++) - for (i = -sx; i <= sx; i++) - if (bin_distance_multi(i,j,k,bin_collection) < cutsq) - stencil_multi[icollection][jcollection][ns++] = - k*mbiny*mbinx + j*mbinx + i; - } - - nstencil_multi[icollection][jcollection] = ns; - } - } -} - diff --git a/src/nstencil_half_multi_3d.h b/src/nstencil_half_multi_3d.h deleted file mode 100644 index 246d71f3e4..0000000000 --- a/src/nstencil_half_multi_3d.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(half/multi/3d, - NStencilHalfMulti3d, NS_HALF | NS_MULTI | NS_3D | NS_ORTHO); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_HALF_MULTI_3D_H -#define LMP_NSTENCIL_HALF_MULTI_3D_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilHalfMulti3d : public NStencil { - public: - NStencilHalfMulti3d(class LAMMPS *); - void create() override; - - protected: - void set_stencil_properties() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/nstencil_half_multi_3d_tri.cpp b/src/nstencil_half_multi_3d_tri.cpp deleted file mode 100644 index 60df9199a2..0000000000 --- a/src/nstencil_half_multi_3d_tri.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "nstencil_half_multi_3d_tri.h" - -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NStencilHalfMulti3dTri::NStencilHalfMulti3dTri(LAMMPS *lmp) : - NStencil(lmp) {} - -/* ---------------------------------------------------------------------- */ - -void NStencilHalfMulti3dTri::set_stencil_properties() -{ - int n = ncollections; - int i, j; - - // Cross collections: use full stencil, looking one way through hierarchy - // smaller -> larger => use full stencil in larger bin - // larger -> smaller => no nstencil required - // If cut offs are same, use half stencil - - for (i = 0; i < n; i++) { - for (j = 0; j < n; j++) { - if(cutcollectionsq[i][i] > cutcollectionsq[j][j]) continue; - - flag_skip_multi[i][j] = false; - - if(cutcollectionsq[i][i] == cutcollectionsq[j][j]){ - flag_half_multi[i][j] = true; - bin_collection_multi[i][j] = i; - } else { - flag_half_multi[i][j] = false; - bin_collection_multi[i][j] = j; - } - } - } -} - -/* ---------------------------------------------------------------------- - create stencils based on bin geometry and cutoff -------------------------------------------------------------------------- */ - -void NStencilHalfMulti3dTri::create() -{ - int icollection, jcollection, bin_collection, i, j, k, ns; - int n = ncollections; - double cutsq; - - - for (icollection = 0; icollection < n; icollection++) { - for (jcollection = 0; jcollection < n; jcollection++) { - if (flag_skip_multi[icollection][jcollection]) { - nstencil_multi[icollection][jcollection] = 0; - continue; - } - - ns = 0; - - sx = stencil_sx_multi[icollection][jcollection]; - sy = stencil_sy_multi[icollection][jcollection]; - sz = stencil_sz_multi[icollection][jcollection]; - - mbinx = stencil_mbinx_multi[icollection][jcollection]; - mbiny = stencil_mbiny_multi[icollection][jcollection]; - mbinz = stencil_mbinz_multi[icollection][jcollection]; - - bin_collection = bin_collection_multi[icollection][jcollection]; - - cutsq = cutcollectionsq[icollection][jcollection]; - - if (flag_half_multi[icollection][jcollection]) { - for (k = 0; k <= sz; k++) - for (j = -sy; j <= sy; j++) - for (i = -sx; i <= sx; i++) - if (bin_distance_multi(i,j,k,bin_collection) < cutsq) - stencil_multi[icollection][jcollection][ns++] = - k*mbiny*mbinx + j*mbinx + i; - } else { - for (k = -sz; k <= sz; k++) - for (j = -sy; j <= sy; j++) - for (i = -sx; i <= sx; i++) - if (bin_distance_multi(i,j,k,bin_collection) < cutsq) - stencil_multi[icollection][jcollection][ns++] = - k*mbiny*mbinx + j*mbinx + i; - } - - nstencil_multi[icollection][jcollection] = ns; - } - } -} diff --git a/src/nstencil_half_multi_3d_tri.h b/src/nstencil_half_multi_3d_tri.h deleted file mode 100644 index 2ced4b0c20..0000000000 --- a/src/nstencil_half_multi_3d_tri.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(half/multi/3d/tri, - NStencilHalfMulti3dTri, NS_HALF | NS_MULTI | NS_3D | NS_TRI); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_HALF_MULTI_3D_TRI_H -#define LMP_NSTENCIL_HALF_MULTI_3D_TRI_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilHalfMulti3dTri : public NStencil { - public: - NStencilHalfMulti3dTri(class LAMMPS *); - void create() override; - - protected: - void set_stencil_properties() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/nstencil_half_multi_old_2d.cpp b/src/nstencil_half_multi_old_2d.cpp deleted file mode 100644 index 7a2f5a25e2..0000000000 --- a/src/nstencil_half_multi_old_2d.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "nstencil_half_multi_old_2d.h" -#include "atom.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NStencilHalfMultiOld2d:: -NStencilHalfMultiOld2d(LAMMPS *lmp) : NStencil(lmp) {} - -/* ---------------------------------------------------------------------- - create stencil based on bin geometry and cutoff -------------------------------------------------------------------------- */ - -void NStencilHalfMultiOld2d::create() -{ - int i,j,n; - double rsq,typesq; - int *s; - double *distsq; - - int ntypes = atom->ntypes; - for (int itype = 1; itype <= ntypes; itype++) { - typesq = cuttypesq[itype]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - n = 0; - for (j = 0; j <= sy; j++) - for (i = -sx; i <= sx; i++) - if (j > 0 || (j == 0 && i > 0)) { - rsq = bin_distance(i,j,0); - if (rsq < typesq) { - distsq[n] = rsq; - s[n++] = j*mbinx + i; - } - } - nstencil_multi_old[itype] = n; - } -} diff --git a/src/nstencil_half_multi_old_2d.h b/src/nstencil_half_multi_old_2d.h deleted file mode 100644 index 387429d160..0000000000 --- a/src/nstencil_half_multi_old_2d.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(half/multi/old/2d, - NStencilHalfMultiOld2d, NS_HALF | NS_MULTI_OLD | NS_2D | NS_ORTHO); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_HALF_MULTI_OLD_2D_H -#define LMP_NSTENCIL_HALF_MULTI_OLD_2D_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilHalfMultiOld2d : public NStencil { - public: - NStencilHalfMultiOld2d(class LAMMPS *); - void create() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/nstencil_half_multi_old_2d_tri.cpp b/src/nstencil_half_multi_old_2d_tri.cpp deleted file mode 100644 index 7e5158cc31..0000000000 --- a/src/nstencil_half_multi_old_2d_tri.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "nstencil_half_multi_old_2d_tri.h" -#include "atom.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NStencilHalfMultiOld2dTri:: -NStencilHalfMultiOld2dTri(LAMMPS *lmp) : NStencil(lmp) {} - -/* ---------------------------------------------------------------------- - create stencil based on bin geometry and cutoff -------------------------------------------------------------------------- */ - -void NStencilHalfMultiOld2dTri::create() -{ - int i,j,n; - double rsq,typesq; - int *s; - double *distsq; - - int ntypes = atom->ntypes; - for (int itype = 1; itype <= ntypes; itype++) { - typesq = cuttypesq[itype]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - n = 0; - for (j = 0; j <= sy; j++) - for (i = -sx; i <= sx; i++) { - rsq = bin_distance(i,j,0); - if (rsq < typesq) { - distsq[n] = rsq; - s[n++] = j*mbinx + i; - } - } - nstencil_multi_old[itype] = n; - } -} diff --git a/src/nstencil_half_multi_old_2d_tri.h b/src/nstencil_half_multi_old_2d_tri.h deleted file mode 100644 index a81d37062c..0000000000 --- a/src/nstencil_half_multi_old_2d_tri.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(half/multi/old/2d/tri, - NStencilHalfMultiOld2dTri, NS_HALF | NS_MULTI_OLD | NS_2D | NS_TRI); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_HALF_MULTI_OLD_2D_TRI_H -#define LMP_NSTENCIL_HALF_MULTI_OLD_2D_TRI_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilHalfMultiOld2dTri : public NStencil { - public: - NStencilHalfMultiOld2dTri(class LAMMPS *); - void create() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/nstencil_half_multi_old_3d.cpp b/src/nstencil_half_multi_old_3d.cpp deleted file mode 100644 index cdb22c2d13..0000000000 --- a/src/nstencil_half_multi_old_3d.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "nstencil_half_multi_old_3d.h" -#include "atom.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NStencilHalfMultiOld3d:: -NStencilHalfMultiOld3d(LAMMPS *lmp) : NStencil(lmp) {} - -/* ---------------------------------------------------------------------- - create stencil based on bin geometry and cutoff -------------------------------------------------------------------------- */ - -void NStencilHalfMultiOld3d::create() -{ - int i,j,k,n; - double rsq,typesq; - int *s; - double *distsq; - - int ntypes = atom->ntypes; - for (int itype = 1; itype <= ntypes; itype++) { - typesq = cuttypesq[itype]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - n = 0; - for (k = 0; k <= sz; k++) - for (j = -sy; j <= sy; j++) - for (i = -sx; i <= sx; i++) - if (k > 0 || j > 0 || (j == 0 && i > 0)) { - rsq = bin_distance(i,j,k); - if (rsq < typesq) { - distsq[n] = rsq; - s[n++] = k*mbiny*mbinx + j*mbinx + i; - } - } - nstencil_multi_old[itype] = n; - } -} diff --git a/src/nstencil_half_multi_old_3d.h b/src/nstencil_half_multi_old_3d.h deleted file mode 100644 index d7d8157afa..0000000000 --- a/src/nstencil_half_multi_old_3d.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(half/multi_old/3d, - NStencilHalfMultiOld3d, NS_HALF | NS_MULTI_OLD | NS_3D | NS_ORTHO); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_HALF_MULTI_OLD_3D_H -#define LMP_NSTENCIL_HALF_MULTI_OLD_3D_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilHalfMultiOld3d : public NStencil { - public: - NStencilHalfMultiOld3d(class LAMMPS *); - void create() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ diff --git a/src/nstencil_half_multi_old_3d_tri.cpp b/src/nstencil_half_multi_old_3d_tri.cpp deleted file mode 100644 index 6fb9b6d3d1..0000000000 --- a/src/nstencil_half_multi_old_3d_tri.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 "nstencil_half_multi_old_3d_tri.h" -#include "atom.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NStencilHalfMultiOld3dTri:: -NStencilHalfMultiOld3dTri(LAMMPS *lmp) : NStencil(lmp) {} - -/* ---------------------------------------------------------------------- - create stencil based on bin geometry and cutoff -------------------------------------------------------------------------- */ - -void NStencilHalfMultiOld3dTri::create() -{ - int i,j,k,n; - double rsq,typesq; - int *s; - double *distsq; - - int ntypes = atom->ntypes; - for (int itype = 1; itype <= ntypes; itype++) { - typesq = cuttypesq[itype]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - n = 0; - for (k = 0; k <= sz; k++) - for (j = -sy; j <= sy; j++) - for (i = -sx; i <= sx; i++) { - rsq = bin_distance(i,j,k); - if (rsq < typesq) { - distsq[n] = rsq; - s[n++] = k*mbiny*mbinx + j*mbinx + i; - } - } - nstencil_multi_old[itype] = n; - } -} diff --git a/src/nstencil_half_multi_old_3d_tri.h b/src/nstencil_half_multi_old_3d_tri.h deleted file mode 100644 index 2a7a5a22bb..0000000000 --- a/src/nstencil_half_multi_old_3d_tri.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, 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 NSTENCIL_CLASS -// clang-format off -NStencilStyle(half/multi/old/3d/tri, - NStencilHalfMultiOld3dTri, NS_HALF | NS_MULTI_OLD | NS_3D | NS_TRI); -// clang-format on -#else - -#ifndef LMP_NSTENCIL_HALF_MULTI_OLD_3D_TRI_H -#define LMP_NSTENCIL_HALF_MULTI_OLD_3D_TRI_H - -#include "nstencil.h" - -namespace LAMMPS_NS { - -class NStencilHalfMultiOld3dTri : public NStencil { - public: - NStencilHalfMultiOld3dTri(class LAMMPS *); - void create() override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif - -/* ERROR/WARNING messages: - -*/ From 25b89473b203f4cfa80c3066a284a6a2f898f17f Mon Sep 17 00:00:00 2001 From: jtclemm Date: Sat, 3 Sep 2022 16:46:55 -0600 Subject: [PATCH 002/189] changing order of central bin --- src/nstencil_bin.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/nstencil_bin.cpp b/src/nstencil_bin.cpp index c5026cb338..45908671fd 100644 --- a/src/nstencil_bin.cpp +++ b/src/nstencil_bin.cpp @@ -39,15 +39,20 @@ void NStencilBin::create() nstencil = 0; + // Half and ortho stencils include central bin first + // This preserves the historical order of the neighbor list + // as the old npair classes used to separately parse the central bin first + if (HALF && (!TRI)) stencil[nstencil++] = 0; + for (k = -sz_min; k <= sz; k++) { for (j = -sy_min; j <= sy; j++) { for (i = -sx; i <= sx; i++) { - // Half and ortho stencils only include own and "upper right" bins + // Now only include "upper right" bins for half and ortho stencils if (HALF && (!DIM_3D) && (!TRI)) - if (! (j > 0 || (j == 0 && i >= 0))) continue; + if (! (j > 0 || (j == 0 && i > 0))) continue; if (HALF && DIM_3D && (!TRI)) - if (! (k > 0 || j > 0 || (j == 0 && i >= 0))) continue; + if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; if (bin_distance(i,j,k) < cutneighmaxsq) stencil[nstencil++] = k*mbiny*mbinx + j*mbinx + i; From 614fb3cbdd87de7d55d3e5d359dd29fb6b7308c4 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Sun, 16 Oct 2022 21:02:02 -0600 Subject: [PATCH 003/189] Adding other nstencil classes --- src/nstencil_bin.cpp | 5 +- src/nstencil_ghost_bin.cpp | 73 ++++++++++++++++++++ src/nstencil_ghost_bin.h | 69 +++++++++++++++++++ src/nstencil_multi.cpp | 133 +++++++++++++++++++++++++++++++++++++ src/nstencil_multi.h | 72 ++++++++++++++++++++ src/nstencil_multi_old.cpp | 85 ++++++++++++++++++++++++ src/nstencil_multi_old.h | 69 +++++++++++++++++++ 7 files changed, 503 insertions(+), 3 deletions(-) create mode 100644 src/nstencil_ghost_bin.cpp create mode 100644 src/nstencil_ghost_bin.h create mode 100644 src/nstencil_multi.cpp create mode 100644 src/nstencil_multi.h create mode 100644 src/nstencil_multi_old.cpp create mode 100644 src/nstencil_multi_old.h diff --git a/src/nstencil_bin.cpp b/src/nstencil_bin.cpp index 45908671fd..22fdc335e4 100644 --- a/src/nstencil_bin.cpp +++ b/src/nstencil_bin.cpp @@ -28,8 +28,7 @@ NStencilBin::NStencilBin(LAMMPS *lmp) : NStencil(lmp) {} template void NStencilBin::create() { - int i,j,k; - bool bin_include; + int i, j, k; // For half stencils, only the upper plane is needed int sy_min = sy; @@ -55,7 +54,7 @@ void NStencilBin::create() if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; if (bin_distance(i,j,k) < cutneighmaxsq) - stencil[nstencil++] = k*mbiny*mbinx + j*mbinx + i; + stencil[nstencil++] = k * mbiny * mbinx + j * mbinx + i; } } } diff --git a/src/nstencil_ghost_bin.cpp b/src/nstencil_ghost_bin.cpp new file mode 100644 index 0000000000..7531a3ff32 --- /dev/null +++ b/src/nstencil_ghost_bin.cpp @@ -0,0 +1,73 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 "nstencil_ghost_bin.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +template +NStencilGhostBin::NStencilGhostBin(LAMMPS *lmp) : NStencil(lmp) {} + +/* ---------------------------------------------------------------------- + create stencil based on bin geometry and cutoff +------------------------------------------------------------------------- */ + +template +void NStencilGhostBin::create() +{ + int i, j, k; + + // For half stencils, only the upper plane is needed + int sy_min = sy; + int sz_min = sz; + if (HALF && (!DIM_3D)) sy_min = 0; + if (HALF && DIM_3D) sz_min = 0; + + nstencil = 0; + + // Half and ortho stencils include central bin first + // This preserves the historical order of the neighbor list + // as the old npair classes used to separately parse the central bin first + if (HALF && (!TRI)) stencil[nstencil++] = 0; + + for (k = -sz_min; k <= sz; k++) { + for (j = -sy_min; j <= sy; j++) { + for (i = -sx; i <= sx; i++) { + + // Now only include "upper right" bins for half and ortho stencils + if (HALF && (!DIM_3D) && (!TRI)) + if (! (j > 0 || (j == 0 && i > 0))) continue; + if (HALF && DIM_3D && (!TRI)) + if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; + + if (bin_distance(i,j,k) < cutneighmaxsq) + stencilxyz[nstencil][0] = i; + stencilxyz[nstencil][1] = j; + stencilxyz[nstencil][2] = k; + stencil[nstencil++] = k * mbiny * mbinx + j * mbinx + i; + } + } + } +} + +namespace LAMMPS_NS { +template class NStencilGhostBin<0,0,0>; +template class NStencilGhostBin<0,1,0>; +template class NStencilGhostBin<1,0,0>; +template class NStencilGhostBin<1,0,1>; +template class NStencilGhostBin<1,1,0>; +template class NStencilGhostBin<1,1,1>; +} diff --git a/src/nstencil_ghost_bin.h b/src/nstencil_ghost_bin.h new file mode 100644 index 0000000000..31268cc30d --- /dev/null +++ b/src/nstencil_ghost_bin.h @@ -0,0 +1,69 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 NSTENCIL_CLASS +// clang-format off +typedef NStencilGhostBin<0, 0, 0> NStencilFullGhostBin2d; +NStencilStyle(full/ghost/bin/2d, + NStencilFullGhostBin2d, + NS_FULL | NS_GhostBin | NS_2D | NS_ORTHO | NS_TRI); + +typedef NStencilGhostBin<0, 1, 0> NStencilFullGhostBin3d; +NStencilStyle(full/ghost/bin/3d, + NStencilFullGhostBin3d, + NS_FULL | NS_GhostBin | NS_3D | NS_ORTHO | NS_TRI); + +typedef NStencilGhostBin<1, 0, 0> NStencilHalfGhostBin2d; +NStencilStyle(half/ghost/bin/2d, + NStencilHalfGhostBin2d, + NS_HALF | NS_GhostBin | NS_2D | NS_ORTHO); + +typedef NStencilGhostBin<1, 0, 1> NStencilHalfGhostBin2dTri; +NStencilStyle(half/ghost/bin/2d/tri, + NStencilHalfGhostBin2dTri, + NS_HALF | NS_GhostBin | NS_2D | NS_TRI); + +typedef NStencilGhostBin<1, 1, 0> NStencilHalfGhostBin3d; +NStencilStyle(half/ghost/bin/3d, + NStencilHalfGhostBin3d, + NS_HALF | NS_GhostBin | NS_3D | NS_ORTHO); + +typedef NStencilGhostBin<1, 1, 1> NStencilHalfGhostBin3dTri; +NStencilStyle(half/ghost/bin/3d/tri, + NStencilHalfGhostBin3dTri, + NS_HALF | NS_GhostBin | NS_3D | NS_TRI); +// clang-format on +#else + +#ifndef LMP_NSTENCIL_GHOST_BIN_H +#define LMP_NSTENCIL_GHOST_BIN_H + +#include "nstencil.h" + +namespace LAMMPS_NS { + +template +class NStencilGhostBin : public NStencil { + public: + NStencilGhostBin(class LAMMPS *); + void create() override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif + +/* ERROR/WARNING messages: + +*/ diff --git a/src/nstencil_multi.cpp b/src/nstencil_multi.cpp new file mode 100644 index 0000000000..6b0688d903 --- /dev/null +++ b/src/nstencil_multi.cpp @@ -0,0 +1,133 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 "nstencil_multi.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +template +NStencilMulti::NStencilMulti(LAMMPS *lmp) : NStencil(lmp) {} + +/* ---------------------------------------------------------------------- */ + +template +void NStencilMulti::set_stencil_properties() +{ + int n = ncollections; + int i, j; + + // FULL + // Always look up neighbor using full stencil and neighbor's bin + // Stencil cutoff set by i-j cutoff + + // HALF + // Cross collections: use full stencil, looking one way through hierarchy + // smaller -> larger => use full stencil in larger bin + // larger -> smaller => no nstencil required + // If cut offs are same, use half stencil + + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + if (HALF) + if (cutcollectionsq[i][i] > cutcollectionsq[j][j]) continue; + + flag_skip_multi[i][j] = false; + flag_half_multi[i][j] = false; + bin_collection_multi[i][j] = j; + + if (HALF) { + if (cutcollectionsq[i][i] == cutcollectionsq[j][j]) { + flag_half_multi[i][j] = true; + bin_collection_multi[i][j] = i; + } + } + } + } +} + +/* ---------------------------------------------------------------------- + create stencil based on bin geometry and cutoff +------------------------------------------------------------------------- */ + +template +void NStencilMulti::create() +{ + int icollection, jcollection, bin_collection, i, j, k, ns, half_flag; + int n = ncollections; + double cutsq; + + // For half stencils, only the upper plane is needed + int sy_min = sy; + int sz_min = sz; + if (HALF && (!DIM_3D)) sy_min = 0; + if (HALF && DIM_3D) sz_min = 0; + + for (icollection = 0; icollection < n; icollection++) { + for (jcollection = 0; jcollection < n; jcollection++) { + if (flag_skip_multi[icollection][jcollection]) { + nstencil_multi[icollection][jcollection] = 0; + continue; + } + + ns = 0; + + sx = stencil_sx_multi[icollection][jcollection]; + sy = stencil_sy_multi[icollection][jcollection]; + sz = stencil_sz_multi[icollection][jcollection]; + + mbinx = stencil_mbinx_multi[icollection][jcollection]; + mbiny = stencil_mbiny_multi[icollection][jcollection]; + mbinz = stencil_mbinz_multi[icollection][jcollection]; + + bin_collection = bin_collection_multi[icollection][jcollection]; + cutsq = cutcollectionsq[icollection][jcollection]; + + half_flag = flag_half_multi[icollection][jcollection]; + + // Half and ortho stencils include central bin first + // This preserves the historical order of the neighbor list + // as the old npair classes used to separately parse the central bin first + if (half_flag && (!TRI)) stencil[nstencil++] = 0; + + for (k = -sz_min; k <= sz; k++) { + for (j = -sy_min; j <= sy; j++) { + for (i = -sx; i <= sx; i++) { + + // Now only include "upper right" bins for half and ortho stencils + if (half_flag && (!DIM_3D) && (!TRI)) + if (! (j > 0 || (j == 0 && i > 0))) continue; + if (half_flag && DIM_3D && (!TRI)) + if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; + + if (bin_distance_multi(i,j,k,bin_collection) < cutsq) + stencil_multi[icollection][jcollection][ns++] = k * mbiny * mbinx + j * mbinx + i; + } + } + } + + nstencil_multi[icollection][jcollection] = ns; + } + } +} + +namespace LAMMPS_NS { +template class NStencilMulti<0,0,0>; +template class NStencilMulti<0,1,0>; +template class NStencilMulti<1,0,0>; +template class NStencilMulti<1,0,1>; +template class NStencilMulti<1,1,0>; +template class NStencilMulti<1,1,1>; +} diff --git a/src/nstencil_multi.h b/src/nstencil_multi.h new file mode 100644 index 0000000000..4747284f2d --- /dev/null +++ b/src/nstencil_multi.h @@ -0,0 +1,72 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 NSTENCIL_CLASS +// clang-format off +typedef NStencilMulti<0, 0, 0> NStencilFullMulti2d; +NStencilStyle(full/multi/2d, + NStencilFullMulti2d, + NS_FULL | NS_MULTI | NS_2D | NS_ORTHO | NS_TRI); + +typedef NStencilMulti<0, 1, 0> NStencilFullMulti3d; +NStencilStyle(full/multi/3d, + NStencilFullMulti3d, + NS_FULL | NS_MULTI | NS_3D | NS_ORTHO | NS_TRI); + +typedef NStencilMulti<1, 0, 0> NStencilHalfMulti2d; +NStencilStyle(half/multi/2d, + NStencilHalfMulti2d, + NS_HALF | NS_MULTI | NS_2D | NS_ORTHO); + +typedef NStencilMulti<1, 0, 1> NStencilHalfMulti2dTri; +NStencilStyle(half/multi/2d/tri, + NStencilHalfMulti2dTri, + NS_HALF | NS_MULTI | NS_2D | NS_TRI); + +typedef NStencilMulti<1, 1, 0> NStencilHalfMulti3d; +NStencilStyle(half/multi/3d, + NStencilHalfMulti3d, + NS_HALF | NS_MULTI | NS_3D | NS_ORTHO); + +typedef NStencilMulti<1, 1, 1> NStencilHalfMulti3dTri; +NStencilStyle(half/multi/3d/tri, + NStencilHalfMulti3dTri, + NS_HALF | NS_MULTI | NS_3D | NS_TRI); +// clang-format on +#else + +#ifndef LMP_NSTENCIL_MULTI_H +#define LMP_NSTENCIL_MULTI_H + +#include "nstencil.h" + +namespace LAMMPS_NS { + +template +class NStencilMulti : public NStencil { + public: + NStencilMulti(class LAMMPS *); + void create() override; + + protected: + void set_stencil_properties() override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif + +/* ERROR/WARNING messages: + +*/ diff --git a/src/nstencil_multi_old.cpp b/src/nstencil_multi_old.cpp new file mode 100644 index 0000000000..b6b99374fa --- /dev/null +++ b/src/nstencil_multi_old.cpp @@ -0,0 +1,85 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 "nstencil_multi_old.h" +#include "atom.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +template +NStencilMultiOld::NStencilMultiOld(LAMMPS *lmp) : NStencil(lmp) {} + +/* ---------------------------------------------------------------------- + create stencil based on bin geometry and cutoff +------------------------------------------------------------------------- */ + +template +void NStencilMultiOld::create() +{ + int i, j, k, n, itype; + double rsq, typesq; + int *s; + double *distsq; + int ntypes = atom->ntypes; + + // For half stencils, only the upper plane is needed + int sy_min = sy; + int sz_min = sz; + if (HALF && (!DIM_3D)) sy_min = 0; + if (HALF && DIM_3D) sz_min = 0; + + for (itype = 1; itype <= ntypes; itype++) { + + typesq = cuttypesq[itype]; + s = stencil_multi_old[itype]; + distsq = distsq_multi_old[itype]; + n = 0; + + // Half and ortho stencils include central bin first + // This preserves the historical order of the neighbor list + // as the old npair classes used to separately parse the central bin first + if (HALF && (!TRI)) s[n++] = 0; + + for (k = -sz_min; k <= sz; k++) { + for (j = -sy_min; j <= sy; j++) { + for (i = -sx; i <= sx; i++) { + + // Now only include "upper right" bins for half and ortho stencils + if (HALF && (!DIM_3D) && (!TRI)) + if (! (j > 0 || (j == 0 && i > 0))) continue; + if (HALF && DIM_3D && (!TRI)) + if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; + + rsq = bin_distance(i, j, 0); + if (rsq < typesq) { + distsq[n] = rsq; + s[n++] = k * mbiny * mbinx + j * mbinx + i; + } + } + } + } + nstencil_multi_old[itype] = n; + } +} + +namespace LAMMPS_NS { +template class NStencilMultiOld<0,0,0>; +template class NStencilMultiOld<0,1,0>; +template class NStencilMultiOld<1,0,0>; +template class NStencilMultiOld<1,0,1>; +template class NStencilMultiOld<1,1,0>; +template class NStencilMultiOld<1,1,1>; +} diff --git a/src/nstencil_multi_old.h b/src/nstencil_multi_old.h new file mode 100644 index 0000000000..b5190b6736 --- /dev/null +++ b/src/nstencil_multi_old.h @@ -0,0 +1,69 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 NSTENCIL_CLASS +// clang-format off +typedef NStencilMultiOld<0, 0, 0> NStencilFullMultiOld2d; +NStencilStyle(full/multi/old/2d, + NStencilFullMultiOld2d, + NS_FULL | NS_MULTI_OLD | NS_2D | NS_ORTHO | NS_TRI); + +typedef NStencilMultiOld<0, 1, 0> NStencilFullMultiOld3d; +NStencilStyle(full/multi/old/3d, + NStencilFullMultiOld3d, + NS_FULL | NS_MULTI_OLD | NS_3D | NS_ORTHO | NS_TRI); + +typedef NStencilMultiOld<1, 0, 0> NStencilHalfMultiOld2d; +NStencilStyle(half/multi/old/2d, + NStencilHalfMultiOld2d, + NS_HALF | NS_MULTI_OLD | NS_2D | NS_ORTHO); + +typedef NStencilMultiOld<1, 0, 1> NStencilHalfMultiOld2dTri; +NStencilStyle(half/multi/old/2d/tri, + NStencilHalfMultiOld2dTri, + NS_HALF | NS_MULTI_OLD | NS_2D | NS_TRI); + +typedef NStencilMultiOld<1, 1, 0> NStencilHalfMultiOld3d; +NStencilStyle(half/multi/old/3d, + NStencilHalfMultiOld3d, + NS_HALF | NS_MULTI_OLD | NS_3D | NS_ORTHO); + +typedef NStencilMultiOld<1, 1, 1> NStencilHalfMultiOld3dTri; +NStencilStyle(half/multi/old/3d/tri, + NStencilHalfMultiOld3dTri, + NS_HALF | NS_MULTI_OLD | NS_3D | NS_TRI); +// clang-format on +#else + +#ifndef LMP_NSTENCIL_MULTI_OLD_H +#define LMP_NSTENCIL_MULTI_OLD_H + +#include "nstencil.h" + +namespace LAMMPS_NS { + +template +class NStencilMultiOld : public NStencil { + public: + NStencilMultiOld(class LAMMPS *); + void create() override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif + +/* ERROR/WARNING messages: + +*/ From fcc47158b382440f6ca939f1bcbdfdd610a3e175 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Sat, 29 Oct 2022 20:35:04 -0600 Subject: [PATCH 004/189] Adding other npair classes --- src/npair_bin.cpp | 97 +++++++--- src/npair_bin.h | 35 +++- src/npair_bin_atomonly.cpp | 54 ++++-- src/npair_bin_atomonly.h | 37 +++- src/npair_bin_ghost.cpp | 188 +++++++++++++++++++ src/npair_bin_ghost.h | 53 ++++++ src/npair_halffull.cpp | 142 +++++++++++++++ src/npair_halffull.h | 115 ++++++++++++ src/npair_multi.cpp | 205 +++++++++++++++++++++ src/npair_multi.h | 85 +++++++++ src/npair_multi_old.cpp | 236 ++++++++++++++++++++++++ src/npair_multi_old.h | 85 +++++++++ src/npair_nsq.cpp | 205 +++++++++++++++++++++ src/npair_nsq.h | 76 ++++++++ src/npair_nsq_ghost.cpp | 176 ++++++++++++++++++ src/npair_nsq_ghost.h | 53 ++++++ src/npair_respa_bin.cpp | 240 +++++++++++++++++++++++++ src/npair_respa_bin.h | 58 ++++++ src/npair_respa_nsq.cpp | 214 ++++++++++++++++++++++ src/npair_respa_nsq.h | 53 ++++++ src/npair_skip.cpp | 102 +++++++++++ src/npair_skip.h | 59 ++++++ src/npair_skip_respa.cpp | 163 +++++++++++++++++ src/npair_skip_respa.h | 48 +++++ src/npair_skip_size_off2on.cpp | 100 +++++++++++ src/npair_skip_size_off2on.h | 48 +++++ src/npair_skip_size_off2on_oneside.cpp | 163 +++++++++++++++++ src/npair_skip_size_off2on_oneside.h | 48 +++++ src/nstencil_ghost_bin.h | 12 +- 29 files changed, 3098 insertions(+), 52 deletions(-) create mode 100644 src/npair_bin_ghost.cpp create mode 100644 src/npair_bin_ghost.h create mode 100644 src/npair_halffull.cpp create mode 100644 src/npair_halffull.h create mode 100644 src/npair_multi.cpp create mode 100644 src/npair_multi.h create mode 100644 src/npair_multi_old.cpp create mode 100644 src/npair_multi_old.h create mode 100644 src/npair_nsq.cpp create mode 100644 src/npair_nsq.h create mode 100644 src/npair_nsq_ghost.cpp create mode 100644 src/npair_nsq_ghost.h create mode 100644 src/npair_respa_bin.cpp create mode 100644 src/npair_respa_bin.h create mode 100644 src/npair_respa_nsq.cpp create mode 100644 src/npair_respa_nsq.h create mode 100644 src/npair_skip.cpp create mode 100644 src/npair_skip.h create mode 100644 src/npair_skip_respa.cpp create mode 100644 src/npair_skip_respa.h create mode 100644 src/npair_skip_size_off2on.cpp create mode 100644 src/npair_skip_size_off2on.h create mode 100644 src/npair_skip_size_off2on_oneside.cpp create mode 100644 src/npair_skip_size_off2on_oneside.h diff --git a/src/npair_bin.cpp b/src/npair_bin.cpp index 294336a03a..83f8c53ecc 100644 --- a/src/npair_bin.cpp +++ b/src/npair_bin.cpp @@ -27,23 +27,35 @@ using namespace NeighConst; /* ---------------------------------------------------------------------- */ -template -NPairBin::NPairBin(LAMMPS *lmp) : NPair(lmp) {} +template +NPairBin::NPairBin(LAMMPS *lmp) : NPair(lmp) {} /* ---------------------------------------------------------------------- - binned neighbor list construction for all neighbors - every neighbor pair appears in list of both atoms i and j + Full: + binned neighbor list construction for all neighbors + every neighbor pair appears in list of both atoms i and j + Half + Newtoff: + binned neighbor list construction with partial Newton's 3rd law + each owned atom i checks own bin and other bins in stencil + pair stored once if i,j are both owned and i < j + pair stored by me if j is ghost (also stored by proc owning j) + Half + Newton: + binned neighbor list construction with full Newton's 3rd law + each owned atom i checks its own bin and other bins in Newton stencil + every pair stored exactly once by some processor ------------------------------------------------------------------------- */ -template -void NPairBin::build(NeighList *list) +template +void NPairBin::build(NeighList *list) { - int i,j,k,n,itype,jtype,ibin,bin_start,which,imol,iatom,moltemplate; + int i,j,jh,k,n,itype,jtype,ibin,bin_start,which,imol,iatom,moltemplate; tagint tagprev; double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + double radsum,cut,cutsq; int *neighptr; double **x = atom->x; + double *radius = atom->radius; int *type = atom->type; int *mask = atom->mask; tagint *tag = atom->tag; @@ -59,6 +71,9 @@ void NPairBin::build(NeighList *list) if (molecular == Atom::TEMPLATE) moltemplate = 1; else moltemplate = 0; + int history = list->history; + int mask_history = 1 << HISTBITS; + int *ilist = list->ilist; int *numneigh = list->numneigh; int **firstneigh = list->firstneigh; @@ -141,22 +156,48 @@ void NPairBin::build(NeighList *list) delx = xtmp - x[j][0]; dely = ytmp - x[j][1]; delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; + rsq = delx * delx + dely * dely + delz * delz; - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; + if (SIZE) { + radsum = radius[i] + radius[j]; + cut = radsum + skin; + cutsq = cut * cut; + + if (rsq <= cutsq) { + jh = j; + if (history && rsq < radsum * radsum) + jh = jh ^ mask_history; + + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = jh; + else if (domain->minimum_image_check(delx,dely,delz)) + neighptr[n++] = jh; + else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); + } else neighptr[n++] = jh; + } + } else { + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (domain->minimum_image_check(delx,dely,delz)) + neighptr[n++] = j; + else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); + } else neighptr[n++] = j; + } } } } @@ -174,8 +215,12 @@ void NPairBin::build(NeighList *list) } namespace LAMMPS_NS { -template class NPairBin<0,1,0>; -template class NPairBin<1,0,0>; -template class NPairBin<1,1,0>; -template class NPairBin<1,1,1>; +template class NPairBin<0,1,0,0>; +template class NPairBin<1,0,0,0>; +template class NPairBin<1,1,0,0>; +template class NPairBin<1,1,1,0>; +template class NPairBin<0,1,0,1>; +template class NPairBin<1,0,0,1>; +template class NPairBin<1,1,0,1>; +template class NPairBin<1,1,1,1>; } diff --git a/src/npair_bin.h b/src/npair_bin.h index af23880db7..92e3243397 100644 --- a/src/npair_bin.h +++ b/src/npair_bin.h @@ -13,26 +13,47 @@ #ifdef NPAIR_CLASS // clang-format off -typedef NPairBin<0, 1, 0> NPairFullBin; +typedef NPairBin<0, 1, 0, 0> NPairFullBin; NPairStyle(full/bin, NPairFullBin, NP_FULL | NP_BIN | NP_MOLONLY | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairBin<1, 0, 0> NPairHalfBinNewtoff; +typedef NPairBin<1, 0, 0, 0> NPairHalfBinNewtoff; NPairStyle(half/bin/newtoff, NPairHalfBinNewtoff, - NP_HALF | NP_BIN | NP_NEWTOFF | NP_ORTHO | NP_TRI); + NP_HALF | NP_BIN | NP_MOLONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairBin<1, 1, 0> NPairHalfBinNewton; +typedef NPairBin<1, 1, 0, 0> NPairHalfBinNewton; NPairStyle(half/bin/newton, NPairHalfBinNewton, NP_HALF | NP_BIN | NP_MOLONLY | NP_NEWTON | NP_ORTHO); -typedef NPairBin<1, 1, 1> NPairHalfBinNewtonTri; +typedef NPairBin<1, 1, 1, 0> NPairHalfBinNewtonTri; NPairStyle(half/bin/newton/tri, NPairHalfBinNewtonTri, - NP_HALF | NP_BIN | NP_NEWTON | NP_TRI); + NP_HALF | NP_BIN | NP_MOLONLY | NP_NEWTON | NP_TRI); + +typedef NPairBin<0, 1, 0, 1> NPairFullSizeBin; +NPairStyle(full/size/bin, + NPairFullSizeBin, + NP_FULL | NP_SIZE | NP_BIN | NP_MOLONLY | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBin<1, 0, 0, 1> NPairHalfSizeBinNewtoff; +NPairStyle(half/size/bin/newtoff, + NPairHalfSizeBinNewtoff, + NP_HALF | NP_SIZE | NP_BIN | NP_MOLONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBin<1, 1, 0, 1> NPairHalfSizeBinNewton; +NPairStyle(half/size/bin/newton, + NPairHalfSizeBinNewton, + NP_HALF | NP_SIZE | NP_BIN | NP_MOLONLY | NP_NEWTON | NP_ORTHO); + +typedef NPairBin<1, 1, 1, 1> NPairHalfSizeBinNewtonTri; +NPairStyle(half/size/bin/newton/tri, + NPairHalfSizeBinNewtonTri, + NP_HALF | NP_SIZE | NP_BIN | NP_MOLONLY | NP_NEWTON | NP_TRI); // clang-format on #else @@ -43,7 +64,7 @@ NPairStyle(half/bin/newton/tri, namespace LAMMPS_NS { -template +template class NPairBin : public NPair { public: NPairBin(class LAMMPS *); diff --git a/src/npair_bin_atomonly.cpp b/src/npair_bin_atomonly.cpp index aa66089fc5..3b15580264 100644 --- a/src/npair_bin_atomonly.cpp +++ b/src/npair_bin_atomonly.cpp @@ -25,28 +25,43 @@ using namespace NeighConst; /* ---------------------------------------------------------------------- */ -template -NPairBinAtomonly::NPairBinAtomonly(LAMMPS *lmp) : NPair(lmp) {} +template +NPairBinAtomonly::NPairBinAtomonly(LAMMPS *lmp) : NPair(lmp) {} /* ---------------------------------------------------------------------- - binned neighbor list construction for all neighbors - every neighbor pair appears in list of both atoms i and j + Full: + binned neighbor list construction for all neighbors + every neighbor pair appears in list of both atoms i and j + Half + Newtoff: + binned neighbor list construction with partial Newton's 3rd law + each owned atom i checks own bin and other bins in stencil + pair stored once if i,j are both owned and i < j + pair stored by me if j is ghost (also stored by proc owning j) + Half + Newton: + binned neighbor list construction with full Newton's 3rd law + each owned atom i checks its own bin and other bins in Newton stencil + every pair stored exactly once by some processor ------------------------------------------------------------------------- */ -template -void NPairBinAtomonly::build(NeighList *list) +template +void NPairBinAtomonly::build(NeighList *list) { - int i,j,k,n,itype,jtype,ibin,bin_start; + int i,j,jh,k,n,itype,jtype,ibin,bin_start; double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + double radsum,cut,cutsq; int *neighptr; double **x = atom->x; + double *radius = atom->radius; int *type = atom->type; int *mask = atom->mask; tagint *molecule = atom->molecule; int nlocal = atom->nlocal; if (includegroup) nlocal = atom->nfirst; + int history = list->history; + int mask_history = 1 << HISTBITS; + int *ilist = list->ilist; int *numneigh = list->numneigh; int **firstneigh = list->firstneigh; @@ -126,7 +141,20 @@ void NPairBinAtomonly::build(NeighList *list) delz = ztmp - x[j][2]; rsq = delx*delx + dely*dely + delz*delz; - if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; + if (SIZE) { + radsum = radius[i] + radius[j]; + cut = radsum + skin; + cutsq = cut * cut; + + if (rsq <= cutsq) { + jh = j; + if (history && rsq < radsum * radsum) + jh = jh ^ mask_history; + neighptr[n++] = jh; + } + } else { + if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; + } } } @@ -143,6 +171,12 @@ void NPairBinAtomonly::build(NeighList *list) } namespace LAMMPS_NS { -template class NPairBinAtomonly<0,1,0>; -template class NPairBinAtomonly<1,1,0>; +template class NPairBinAtomonly<0,1,0,0>; +template class NPairBinAtomonly<1,0,0,0>; +template class NPairBinAtomonly<1,1,0,0>; +template class NPairBinAtomonly<1,1,1,0>; +template class NPairBinAtomonly<0,1,0,1>; +template class NPairBinAtomonly<1,0,0,1>; +template class NPairBinAtomonly<1,1,0,1>; +template class NPairBinAtomonly<1,1,1,1>; } diff --git a/src/npair_bin_atomonly.h b/src/npair_bin_atomonly.h index ae64863138..581700af7d 100644 --- a/src/npair_bin_atomonly.h +++ b/src/npair_bin_atomonly.h @@ -13,16 +13,47 @@ #ifdef NPAIR_CLASS // clang-format off -typedef NPairBinAtomonly<0, 1, 0> NPairFullBinAtomonly; +typedef NPairBinAtomonly<0, 1, 0, 0> NPairFullBinAtomonly; NPairStyle(full/bin/atomonly, NPairFullBinAtomonly, NP_FULL | NP_BIN | NP_ATOMONLY | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairBinAtomonly<1, 1, 0> NPairHalfBinAtomonlyNewton; +typedef NPairBinAtomonly<1, 0, 0, 0> NPairHalfBinAtomonlyNewtoff; +NPairStyle(half/bin/atomonly/newtoff, + NPairHalfBinAtomonlyNewtoff, + NP_HALF | NP_BIN | NP_ATOMONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBinAtomonly<1, 1, 0, 0> NPairHalfBinAtomonlyNewton; NPairStyle(half/bin/atomonly/newton, NPairHalfBinAtomonlyNewton, NP_HALF | NP_BIN | NP_ATOMONLY | NP_NEWTON | NP_ORTHO); + +typedef NPairBinAtomonly<1, 1, 1, 0> NPairHalfBinAtomonlyNewtonTri; +NPairStyle(half/bin/atomonly/newton/tri, + NPairHalfBinAtomonlyNewtonTri, + NP_HALF | NP_BIN | NP_ATOMONLY | NP_NEWTON | NP_TRI); + +typedef NPairBinAtomonly<0, 1, 0, 1> NPairFullSizeBinAtomonly; +NPairStyle(full/size/bin/atomonly, + NPairFullSizeBinAtomonly, + NP_FULL | NP_SIZE | NP_BIN | NP_ATOMONLY | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBinAtomonly<1, 0, 0, 1> NPairHalfSizeBinAtomonlyNewtoff; +NPairStyle(half/size/bin/atomonly/newtoff, + NPairHalfSizeBinAtomonlyNewtoff, + NP_HALF | NP_SIZE | NP_BIN | NP_ATOMONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBinAtomonly<1, 1, 0, 1> NPairHalfSizeBinAtomonlyNewton; +NPairStyle(half/size/bin/atomonly/newton, + NPairHalfSizeBinAtomonlyNewton, + NP_HALF | NP_SIZE | NP_BIN | NP_ATOMONLY | NP_NEWTON | NP_ORTHO); + +typedef NPairBinAtomonly<1, 1, 1, 1> NPairHalfSizeBinAtomonlyNewtonTri; +NPairStyle(half/size/bin/atomonly/newton/tri, + NPairHalfSizeBinAtomonlyNewtonTri, + NP_HALF | NP_SIZE | NP_BIN | NP_ATOMONLY | NP_NEWTON | NP_TRI); // clang-format on #else @@ -33,7 +64,7 @@ NPairStyle(half/bin/atomonly/newton, namespace LAMMPS_NS { -template +template class NPairBinAtomonly : public NPair { public: NPairBinAtomonly(class LAMMPS *); diff --git a/src/npair_bin_ghost.cpp b/src/npair_bin_ghost.cpp new file mode 100644 index 0000000000..5be3352095 --- /dev/null +++ b/src/npair_bin_ghost.cpp @@ -0,0 +1,188 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 "npair_bin_ghost.h" +#include "neigh_list.h" +#include "atom.h" +#include "atom_vec.h" +#include "molecule.h" +#include "neighbor.h" +#include "domain.h" +#include "my_page.h" +#include "error.h" + +using namespace LAMMPS_NS; +using namespace NeighConst; + +/* ---------------------------------------------------------------------- */ + +template +NPairBinGhost::NPairBinGhost(LAMMPS *lmp) : NPair(lmp) {} + +/* ---------------------------------------------------------------------- + Full: + binned neighbor list construction for all neighbors + include neighbors of ghost atoms, but no "special neighbors" for ghosts + every neighbor pair appears in list of both atoms i and j + Half + Newtoff: + binned neighbor list construction with partial Newton's 3rd law + include neighbors of ghost atoms, but no "special neighbors" for ghosts + owned and ghost atoms check own bin and other bins in stencil + pair stored once if i,j are both owned and i < j + pair stored by me if i owned and j ghost (also stored by proc owning j) + pair stored once if i,j are both ghost and i < j +------------------------------------------------------------------------- */ + + +template +void NPairBinGhost::build(NeighList *list) +{ + int i,j,k,n,itype,jtype,ibin,bin_start,which,imol,iatom,moltemplate; + tagint tagprev; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + int xbin,ybin,zbin,xbin2,ybin2,zbin2; + int *neighptr; + + double **x = atom->x; + int *type = atom->type; + int *mask = atom->mask; + tagint *tag = atom->tag; + tagint *molecule = atom->molecule; + tagint **special = atom->special; + int **nspecial = atom->nspecial; + int nlocal = atom->nlocal; + int nall = nlocal + atom->nghost; + if (includegroup) nlocal = atom->nfirst; + + int *molindex = atom->molindex; + int *molatom = atom->molatom; + Molecule **onemols = atom->avec->onemols; + if (molecular == Atom::TEMPLATE) moltemplate = 1; + else moltemplate = 0; + + int *ilist = list->ilist; + int *numneigh = list->numneigh; + int **firstneigh = list->firstneigh; + MyPage *ipage = list->ipage; + + int inum = 0; + ipage->reset(); + + // loop over owned & ghost atoms, storing neighbors + for (i = 0; i < nall; i++) { + n = 0; + neighptr = ipage->vget(); + + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + if (moltemplate) { + imol = molindex[i]; + iatom = molatom[i]; + tagprev = tag[i] - iatom - 1; + } + + if (i < nlocal) { + ibin = atom2bin[i]; + + // loop over all atoms in surrounding bins in stencil including self + // when i is a ghost atom, must check if stencil bin is out of bounds + // no molecular test when i = ghost atom + for (k = 0; k < nstencil; k++) { + bin_start = binhead[ibin+stencil[k]]; + for (j = bin_start; j >= 0; j = bins[j]) { + if (!HALF) { + // Full neighbor list + // only skip i = j + if (i == j) continue; + } else { + // Half neighbor list, newton off + // only store pair if i < j + // stores own/own pairs only once + // stores own/ghost pairs on both procs + // stores ghost/ghost pairs only once + if (j <= i) continue; + } + + jtype = type[j]; + if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx * delx + dely * dely + delz * delz; + + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (domain->minimum_image_check(delx,dely,delz)) + neighptr[n++] = j; + else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); + } else neighptr[n++] = j; + } + } + } + } else { + ibin = coord2bin(x[i],xbin,ybin,zbin); + for (k = 0; k < nstencil; k++) { + xbin2 = xbin + stencilxyz[k][0]; + ybin2 = ybin + stencilxyz[k][1]; + zbin2 = zbin + stencilxyz[k][2]; + if (xbin2 < 0 || xbin2 >= mbinx || + ybin2 < 0 || ybin2 >= mbiny || + zbin2 < 0 || zbin2 >= mbinz) continue; + for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { + if (!HALF) { + if (i == j) continue; + } else { + if (j <= i) continue; + } + + jtype = type[j]; + if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq <= cutneighghostsq[itype][jtype]) neighptr[n++] = j; + } + } + } + + ilist[inum++] = i; + firstneigh[i] = neighptr; + numneigh[i] = n; + ipage->vgot(n); + if (ipage->status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + } + + list->inum = atom->nlocal; + list->gnum = inum - atom->nlocal; +} + +namespace LAMMPS_NS { +template class NPairBinGhost<0>; +template class NPairBinGhost<1>; +} diff --git a/src/npair_bin_ghost.h b/src/npair_bin_ghost.h new file mode 100644 index 0000000000..602aaf986e --- /dev/null +++ b/src/npair_bin_ghost.h @@ -0,0 +1,53 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 NPAIR_CLASS +// clang-format off +typedef NPairBinGhost<0> NPairFullBinGhost; +NPairStyle(full/bin/ghost, + NPairFullBinGhost, + NP_FULL | NP_BIN | NP_NEWTON | NP_NEWTOFF | NP_GHOST | NP_ORTHO | NP_TRI); + +typedef NPairBinGhost<1> NPairHalfBinGhostNewtoff; +NPairStyle(half/bin/ghost/newtoff, + NPairHalfBinGhostNewtoff, + NP_HALF | NP_BIN | NP_NEWTOFF | NP_GHOST | NP_ORTHO | NP_TRI); +// clang-format on +#else + +#ifndef LMP_NPAIR_BIN_GHOST_H +#define LMP_NPAIR_BIN_GHOST_H + +#include "npair.h" + +namespace LAMMPS_NS { + +template +class NPairBinGhost : public NPair { + public: + NPairBinGhost(class LAMMPS *); + void build(class NeighList *) override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Neighbor list overflow, boost neigh_modify one + +UNDOCUMENTED + +*/ diff --git a/src/npair_halffull.cpp b/src/npair_halffull.cpp new file mode 100644 index 0000000000..b9652484ce --- /dev/null +++ b/src/npair_halffull.cpp @@ -0,0 +1,142 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 "npair_halffull.h" + +#include "atom.h" +#include "error.h" +#include "my_page.h" +#include "neigh_list.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +template +NPairHalffull::NPairHalffull(LAMMPS *lmp) : NPair(lmp) {} + +/* ---------------------------------------------------------------------- + build half list from full list + pair stored once if i,j are both owned and i < j + works if full list is a skip list + + Newtoff: + pair stored by me if j is ghost (also stored by proc owning j) + works for owned (non-ghost) list, also for ghost list + if ghost, also store neighbors of ghost atoms & set inum,gnum correctly + Newton: + if j is ghost, only store if j coords are "above and to the right" of i +------------------------------------------------------------------------- */ + +template +void NPairHalffull::build(NeighList *list) +{ + int i,j,ii,jj,n,jnum,joriginal; + int *neighptr,*jlist; + double xtmp,ytmp,ztmp; + double delx,dely,delz,rsq; + + double **x = atom->x; + int nlocal = atom->nlocal; + + int *ilist = list->ilist; + int *numneigh = list->numneigh; + int **firstneigh = list->firstneigh; + MyPage *ipage = list->ipage; + + int *ilist_full = list->listfull->ilist; + int *numneigh_full = list->listfull->numneigh; + int **firstneigh_full = list->listfull->firstneigh; + int inum_full = list->listfull->inum; + if (!NEWTON) + if (list->ghost) inum_full += list->listfull->gnum; + + int inum = 0; + ipage->reset(); + + double cutsq_custom = cutoff_custom * cutoff_custom; + + // loop over atoms in full list + + for (ii = 0; ii < inum_full; ii++) { + n = 0; + neighptr = ipage->vget(); + + // loop over parent full list + + i = ilist_full[ii]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + + jlist = firstneigh_full[i]; + jnum = numneigh_full[i]; + + for (jj = 0; jj < jnum; jj++) { + joriginal = jlist[jj]; + j = joriginal & NEIGHMASK; + if (NEWTON) { + if (j < nlocal) { + if (i > j) continue; + } else { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + } + } + + if (TRIM) { + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx * delx + dely * dely + delz * delz; + + if (rsq > cutsq_custom) continue; + } + neighptr[n++] = joriginal; + } else { + if (j > i) { + if (TRIM) { + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx * delx + dely * dely + delz * delz; + + if (rsq > cutsq_custom) continue; + } + neighptr[n++] = joriginal; + } + } + } + + ilist[inum++] = i; + firstneigh[i] = neighptr; + numneigh[i] = n; + ipage->vgot(n); + if (ipage->status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + } + + list->inum = inum; + if (!NEWTON) + if (list->ghost) list->gnum = list->listfull->gnum; +} + +namespace LAMMPS_NS { +template class NPairHalffull<0,0>; +template class NPairHalffull<1,0>; +template class NPairHalffull<0,1>; +template class NPairHalffull<1,1>; +} diff --git a/src/npair_halffull.h b/src/npair_halffull.h new file mode 100644 index 0000000000..519cb0ced1 --- /dev/null +++ b/src/npair_halffull.h @@ -0,0 +1,115 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 NPAIR_CLASS +// clang-format off +typedef NPairHalffull<0, 0> NPairHalffullNewtoff; +NPairStyle(halffull/newtoff, + NPairHalffullNewtoff, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | + NP_ORTHO | NP_TRI); + +typedef NPairHalffull<0, 0> NPairHalffullNewtoff; +NPairStyle(halffull/newtoff/skip, + NPairHalffullNewtoff, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | + NP_ORTHO | NP_TRI | NP_SKIP); + +typedef NPairHalffull<0, 0> NPairHalffullNewtoff; +NPairStyle(halffull/newtoff/ghost, + NPairHalffullNewtoff, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | + NP_ORTHO | NP_TRI | NP_GHOST); + +typedef NPairHalffull<0, 0> NPairHalffullNewtoff; +NPairStyle(halffull/newtoff/skip/ghost, + NPairHalffullNewtoff, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | + NP_ORTHO | NP_TRI | NP_SKIP | NP_GHOST); + +typedef NPairHalffull<1, 0> NPairHalffullNewton; +NPairStyle(halffull/newton, + NPairHalffullNewton, + NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_ORTHO | NP_TRI); + +typedef NPairHalffull<1, 0> NPairHalffullNewton; +NPairStyle(halffull/newton/skip, + NPairHalffullNewton, + NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_ORTHO | NP_TRI | NP_SKIP); + +typedef NPairHalffull<0, 1> NPairHalffullNewtoffTrim; +NPairStyle(halffull/newtoff/trim, + NPairHalffullNewtoffTrim, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | + NP_ORTHO | NP_TRI | NP_TRIM); + +typedef NPairHalffull<0, 1> NPairHalffullNewtoffTrim; +NPairStyle(halffull/newtoff/skip/trim, + NPairHalffullNewtoffTrim, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | + NP_ORTHO | NP_TRI | NP_SKIP | NP_TRIM); + +typedef NPairHalffull<0, 1> NPairHalffullNewtoffTrim; +NPairStyle(halffull/newtoff/ghost/trim, + NPairHalffullNewtoffTrim, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | + NP_ORTHO | NP_TRI | NP_GHOST | NP_TRIM); + +typedef NPairHalffull<0, 1> NPairHalffullNewtoffTrim; +NPairStyle(halffull/newtoff/skip/ghost/trim, + NPairHalffullNewtoffTrim, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | + NP_ORTHO | NP_TRI | NP_SKIP | NP_GHOST | NP_TRIM); + +typedef NPairHalffull<1, 1> NPairHalffullNewtonTrim; +NPairStyle(halffull/newton/trim, + NPairHalffullNewtonTrim, + NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_ORTHO | NP_TRI | NP_TRIM); + +typedef NPairHalffull<1, 1> NPairHalffullNewtonTrim; +NPairStyle(halffull/newton/skip/trim, + NPairHalffullNewtonTrim, + NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_ORTHO | NP_TRI | NP_SKIP | NP_TRIM); +// clang-format on +#else + +#ifndef LMP_NPAIR_HALFFULL_H +#define LMP_NPAIR_HALFFULL_H + +#include "npair.h" + +namespace LAMMPS_NS { + +template +class NPairHalffull : public NPair { + public: + NPairHalffull(class LAMMPS *); + void build(class NeighList *) override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Neighbor list overflow, boost neigh_modify one + +UNDOCUMENTED + +*/ diff --git a/src/npair_multi.cpp b/src/npair_multi.cpp new file mode 100644 index 0000000000..76e90ec674 --- /dev/null +++ b/src/npair_multi.cpp @@ -0,0 +1,205 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 "npair_multi.h" +#include "atom.h" +#include "atom_vec.h" +#include "domain.h" +#include "error.h" +#include "molecule.h" +#include "my_page.h" +#include "neighbor.h" +#include "neigh_list.h" + +using namespace LAMMPS_NS; +using namespace NeighConst; + +/* ---------------------------------------------------------------------- */ + +template +NPairMulti::NPairMulti(LAMMPS *lmp) : NPair(lmp) {} + +/* ---------------------------------------------------------------------- + multi stencil is icollection-jcollection dependent + Full: + binned neighbor list construction for all neighbors + every neighbor pair appears in list of both atoms i and j + Half + Newtoff: + binned neighbor list construction with partial Newton's 3rd law + each owned atom i checks own bin and other bins in stencil + pair stored once if i,j are both owned and i < j + pair stored by me if j is ghost (also stored by proc owning j) + Half + Newton: + binned neighbor list construction with full Newton's 3rd law + each owned atom i checks its own bin and other bins in Newton stencil + every pair stored exactly once by some processor +------------------------------------------------------------------------- */ + +template +void NPairMulti::build(NeighList *list) +{ + int i,j,jh,js,k,n,itype,jtype,icollection,jcollection,ibin,jbin,which,ns,imol,iatom,moltemplate; + tagint tagprev; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + double radsum,cut,cutsq; + int *neighptr,*s; + + int *collection = neighbor->collection; + double **x = atom->x; + double *radius = atom->radius; + int *type = atom->type; + int *mask = atom->mask; + tagint *tag = atom->tag; + tagint *molecule = atom->molecule; + tagint **special = atom->special; + int **nspecial = atom->nspecial; + int nlocal = atom->nlocal; + if (includegroup) nlocal = atom->nfirst; + + int *molindex = atom->molindex; + int *molatom = atom->molatom; + Molecule **onemols = atom->avec->onemols; + if (molecular == Atom::TEMPLATE) moltemplate = 1; + else moltemplate = 0; + + int history = list->history; + int mask_history = 1 << HISTBITS; + + int *ilist = list->ilist; + int *numneigh = list->numneigh; + int **firstneigh = list->firstneigh; + MyPage *ipage = list->ipage; + + int inum = 0; + ipage->reset(); + + for (i = 0; i < nlocal; i++) { + n = 0; + neighptr = ipage->vget(); + itype = type[i]; + icollection = collection[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + if (moltemplate) { + imol = molindex[i]; + iatom = molatom[i]; + tagprev = tag[i] - iatom - 1; + } + + ibin = atom2bin[i]; + + // loop through stencils for all collections + for (jcollection = 0; jcollection < ncollections; jcollection++) { + + // if same collection use own bin + if (icollection == jcollection) jbin = ibin; + else jbin = coord2bin(x[i], jcollection); + + s = stencil_multi[icollection][jcollection]; + ns = nstencil_multi[icollection][jcollection]; + + for (k = 0; k < ns; k++) { + js = binhead_multi[jcollection][jbin + s[k]]; + for (j = js; j >= 0; j = bins[j]) { + if (!HALF) { + // Full neighbor list + // only skip i = j + if (i == j) continue; + } else if (!NEWTON) { + // Half neighbor list, newton off + // only store pair if i < j + // stores own/own pairs only once + // stores own/ghost pairs on both procs + if (j <= i) continue; + } else if (TRI) { + // Half neighbor list, newton on, triclinic + // pairs for atoms j "below" i are excluded + // below = lower z or (equal z and lower y) or (equal zy and lower x) + // (equal zyx and j <= i) + // latter excludes self-self interaction but allows superposed atoms + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp) { + if (x[j][0] < xtmp) continue; + if (x[j][0] == xtmp && j <= i) continue; + } + } + } else { + // Half neighbor list, newton on, orthonormal + // store every pair for every bin in stencil,except for i's bin + + if (stencil[k] == 0) { + // if j is owned atom, store it, since j is beyond i in linked list + // if j is ghost, only store if j coords are "above and to the "right" of i + if (j >= nlocal) { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + } + } + } + } + + jtype = type[j]; + if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (domain->minimum_image_check(delx,dely,delz)) + neighptr[n++] = j; + else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); + } else neighptr[n++] = j; + } + } + } + } + + ilist[inum++] = i; + firstneigh[i] = neighptr; + numneigh[i] = n; + ipage->vgot(n); + if (ipage->status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + } + + list->inum = inum; + list->gnum = 0; +} + +namespace LAMMPS_NS { +template class NPairMulti<0,1,0,0>; +template class NPairMulti<1,0,0,0>; +template class NPairMulti<1,1,0,0>; +template class NPairMulti<1,1,1,0>; +template class NPairMulti<0,1,0,1>; +template class NPairMulti<1,0,0,1>; +template class NPairMulti<1,1,0,1>; +template class NPairMulti<1,1,1,1>; +} diff --git a/src/npair_multi.h b/src/npair_multi.h new file mode 100644 index 0000000000..288498dc00 --- /dev/null +++ b/src/npair_multi.h @@ -0,0 +1,85 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 NPAIR_CLASS +// clang-format off +typedef NPairMulti<0, 1, 0, 0> NPairFullMulti; +NPairStyle(full/Multi, + NPairFullMulti, + NP_FULL | NP_MULTI | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMulti<1, 0, 0, 0> NPairHalfMultiNewtoff; +NPairStyle(half/multi/newtoff, + NPairHalfMultiNewtoff, + NP_HALF | NP_MULTI | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMulti<1, 1, 0, 0> NPairHalfMultiNewton; +NPairStyle(half/multi/newton, + NPairHalfMultiNewton, + NP_HALF | NP_MULTI | NP_NEWTON | NP_ORTHO); + +typedef NPairMulti<1, 1, 1, 0> NPairHalfMultiNewtonTri; +NPairStyle(half/multi/newton/tri, + NPairHalfMultiNewtonTri, + NP_HALF | NP_MULTI | NP_NEWTON | NP_TRI); + +typedef NPairMulti<0, 1, 0, 1> NPairFullSizeMulti; +NPairStyle(full/size/Multi, + NPairFullSizeMulti, + NP_FULL | NP_SIZE | NP_MULTI | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMulti<1, 0, 0, 1> NPairHalfSizeMultiNewtoff; +NPairStyle(half/size/multi/newtoff, + NPairHalfSizeMultiNewtoff, + NP_HALF | NP_SIZE | NP_MULTI | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMulti<1, 1, 0, 1> NPairHalfSizeMultiNewton; +NPairStyle(half/size/multi/newton, + NPairHalfSizeMultiNewton, + NP_HALF | NP_SIZE | NP_MULTI | NP_NEWTON | NP_ORTHO); + +typedef NPairMulti<1, 1, 1, 1> NPairHalfSizeMultiNewtonTri; +NPairStyle(half/size/multi/newton/tri, + NPairHalfSizeMultiNewtonTri, + NP_HALF | NP_SIZE | NP_MULTI | NP_NEWTON | NP_TRI); +// clang-format on +#else + +#ifndef LMP_NPAIR_MULTI_H +#define LMP_NPAIR_MULTI_H + +#include "npair.h" + +namespace LAMMPS_NS { + +template +class NPairMulti : public NPair { + public: + NPairMulti(class LAMMPS *); + void build(class NeighList *) override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Neighbor list overflow, boost neigh_modify one + +UNDOCUMENTED + +*/ diff --git a/src/npair_multi_old.cpp b/src/npair_multi_old.cpp new file mode 100644 index 0000000000..28efc16797 --- /dev/null +++ b/src/npair_multi_old.cpp @@ -0,0 +1,236 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 "npair_multi_old.h" + +#include "atom.h" +#include "atom_vec.h" +#include "domain.h" +#include "error.h" +#include "molecule.h" +#include "my_page.h" +#include "neigh_list.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +template +NPairMultiOld::NPairMultiOld(LAMMPS *lmp) : NPair(lmp) {} + +/* ---------------------------------------------------------------------- + multi/old-type stencil is itype dependent and is distance checked + Full: + binned neighbor list construction for all neighbors + multi-type stencil is itype dependent and is distance checked + every neighbor pair appears in list of both atoms i and j + Half + newtoff: + binned neighbor list construction with partial Newton's 3rd law + each owned atom i checks own bin and other bins in stencil + multi-type stencil is itype dependent and is distance checked + pair stored once if i,j are both owned and i < j + pair stored by me if j is ghost (also stored by proc owning j) + Half + newton: + binned neighbor list construction with full Newton's 3rd law + each owned atom i checks its own bin and other bins in Newton stencil + multi-type stencil is itype dependent and is distance checked + every pair stored exactly once by some processor +------------------------------------------------------------------------- */ + +template +void NPairMultiOld::build(NeighList *list) +{ + int i,j,jh,k,n,itype,jtype,ibin,bin_start,which,ns,imol,iatom,moltemplate; + tagint tagprev; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + double radsum,cut,cutsq; + int *neighptr,*s; + double *cutsq,*distsq; + + double **x = atom->x; + double *radius = atom->radius; + int *type = atom->type; + int *mask = atom->mask; + tagint *tag = atom->tag; + tagint *molecule = atom->molecule; + tagint **special = atom->special; + int **nspecial = atom->nspecial; + int nlocal = atom->nlocal; + if (includegroup) nlocal = atom->nfirst; + + int *molindex = atom->molindex; + int *molatom = atom->molatom; + Molecule **onemols = atom->avec->onemols; + if (molecular == Atom::TEMPLATE) moltemplate = 1; + else moltemplate = 0; + + int history = list->history; + int mask_history = 1 << HISTBITS; + + int *ilist = list->ilist; + int *numneigh = list->numneigh; + int **firstneigh = list->firstneigh; + MyPage *ipage = list->ipage; + + int inum = 0; + ipage->reset(); + + for (i = 0; i < nlocal; i++) { + n = 0; + neighptr = ipage->vget(); + + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + if (moltemplate) { + imol = molindex[i]; + iatom = molatom[i]; + tagprev = tag[i] - iatom - 1; + } + + ibin = atom2bin[i]; + s = stencil_multi_old[itype]; + distsq = distsq_multi_old[itype]; + cutsq = cutneighsq[itype]; + ns = nstencil_multi_old[itype]; + for (k = 0; k < ns; k++) { + bin_start = binhead[ibin+stencil[k]]; + if (stencil[k] == 0) { + if (HALF && NEWTON && (!TRI)) { + // Half neighbor list, newton on, orthonormal + // loop over rest of atoms in i's bin, ghosts are at end of linked list + bin_start = bins[i]; + } + } + + for (j = bin_start; j >= 0; j = bins[j]) { + if (!HALF) { + // Full neighbor list + // only skip i = j + if (i == j) continue; + } else if (!NEWTON) { + // Half neighbor list, newton off + // only store pair if i < j + // stores own/own pairs only once + // stores own/ghost pairs on both procs + if (j <= i) continue; + } else if (TRI) { + // Half neighbor list, newton on, triclinic + // pairs for atoms j "below" i are excluded + // below = lower z or (equal z and lower y) or (equal zy and lower x) + // (equal zyx and j <= i) + // latter excludes self-self interaction but allows superposed atoms + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp) { + if (x[j][0] < xtmp) continue; + if (x[j][0] == xtmp && j <= i) continue; + } + } + } else { + // Half neighbor list, newton on, orthonormal + // store every pair for every bin in stencil,except for i's bin + + if (stencil[k] == 0) { + // if j is owned atom, store it, since j is beyond i in linked list + // if j is ghost, only store if j coords are "above and to the "right" of i + if (j >= nlocal) { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + } + } + } + } + + jtype = type[j]; + if (cutsq[jtype] < distsq[k]) continue; + if (i == j) continue; + + if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (SIZE) { + radsum = radius[i] + radius[j]; + cut = radsum + skin; + cutsq = cut * cut; + + if (rsq <= cutsq) { + jh = j; + if (history && rsq < radsum * radsum) + jh = jh ^ mask_history; + + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = jh; + else if (domain->minimum_image_check(delx,dely,delz)) + neighptr[n++] = jh; + else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); + } else neighptr[n++] = jh; + } + } else { + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (domain->minimum_image_check(delx,dely,delz)) + neighptr[n++] = j; + else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); + } else neighptr[n++] = j; + } + } + } + } + + ilist[inum++] = i; + firstneigh[i] = neighptr; + numneigh[i] = n; + ipage->vgot(n); + if (ipage->status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + } + + list->inum = inum; + list->gnum = 0; +} + +namespace LAMMPS_NS { +template class NPairMultiOld<0,1,0,0>; +template class NPairMultiOld<1,0,0,0>; +template class NPairMultiOld<1,1,0,0>; +template class NPairMultiOld<1,1,1,0>; +template class NPairMultiOld<0,1,0,1>; +template class NPairMultiOld<1,0,0,1>; +template class NPairMultiOld<1,1,0,1>; +template class NPairMultiOld<1,1,1,1>; +} diff --git a/src/npair_multi_old.h b/src/npair_multi_old.h new file mode 100644 index 0000000000..52b071ebc5 --- /dev/null +++ b/src/npair_multi_old.h @@ -0,0 +1,85 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 NPAIR_CLASS +// clang-format off +typedef NPairMultiOld<0, 1, 0, 0> NPairFullMultiOld; +NPairStyle(full/multi/old, + NPairFullMultiOld, + NP_FULL | NP_MULTI_OLD | NP_MOLONLY | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMultiOld<1, 0, 0, 0> NPairHalfMultiOldNewtoff; +NPairStyle(half/multi/old/newtoff, + NPairHalfMultiOldNewtoff, + NP_HALF | NP_MULTI_OLD | NP_MOLONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMultiOld<1, 1, 0, 0> NPairHalfMultiOldNewton; +NPairStyle(half/multi/old/newton, + NPairHalfMultiOldNewton, + NP_HALF | NP_MULTI_OLD | NP_MOLONLY | NP_NEWTON | NP_ORTHO); + +typedef NPairMultiOld<1, 1, 1, 0> NPairHalfMultiOldNewtonTri; +NPairStyle(half/multi/old/newton/tri, + NPairHalfMultiOldNewtonTri, + NP_HALF | NP_MULTI_OLD | NP_MOLONLY | NP_NEWTON | NP_TRI); + +typedef NPairMultiOld<0, 1, 0, 1> NPairFullSizeMultiOld; +NPairStyle(full/size/multi/old, + NPairFullSizeMultiOld, + NP_FULL | NP_SIZE | NP_MULTI_OLD | NP_MOLONLY | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMultiOld<1, 0, 0, 1> NPairHalfSizeMultiOldNewtoff; +NPairStyle(half/size/multi/old/newtoff, + NPairHalfSizeMultiOldNewtoff, + NP_HALF | NP_SIZE | NP_MULTI_OLD | NP_MOLONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMultiOld<1, 1, 0, 1> NPairHalfSizeMultiOldNewton; +NPairStyle(half/size/multi/old/newton, + NPairHalfSizeMultiOldNewton, + NP_HALF | NP_SIZE | NP_MULTI_OLD | NP_MOLONLY | NP_NEWTON | NP_ORTHO); + +typedef NPairMultiOld<1, 1, 1, 1> NPairHalfSizeMultiOldNewtonTri; +NPairStyle(half/size/multi/old/newton/tri, + NPairHalfSizeMultiOldNewtonTri, + NP_HALF | NP_SIZE | NP_MULTI_OLD | NP_MOLONLY | NP_NEWTON | NP_TRI); +// clang-format on +#else + +#ifndef LMP_NPAIR_MULTI_OLD_H +#define LMP_NPAIR_MULTI_OLD_H + +#include "npair.h" + +namespace LAMMPS_NS { + +template +class NPairMultiOld : public NPair { + public: + NPairMultiOld(class LAMMPS *); + void build(class NeighList *) override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Neighbor list overflow, boost neigh_modify one + +UNDOCUMENTED + +*/ diff --git a/src/npair_nsq.cpp b/src/npair_nsq.cpp new file mode 100644 index 0000000000..87a05e8136 --- /dev/null +++ b/src/npair_nsq.cpp @@ -0,0 +1,205 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 "npair_nsq.h" +#include "neigh_list.h" +#include "atom.h" +#include "atom_vec.h" +#include "molecule.h" +#include "neighbor.h" +#include "domain.h" +#include "my_page.h" +#include "error.h" + +using namespace LAMMPS_NS; +using namespace NeighConst; + +/* ---------------------------------------------------------------------- */ + +template +NPairNsq::NPairNsq(LAMMPS *lmp) : NPair(lmp) {} + +/* ---------------------------------------------------------------------- + Full: + N^2 search for all neighbors + every neighbor pair appears in list of both atoms i and j + Half + Newtoff: + N^2 / 2 search for neighbor pairs with partial Newton's 3rd law + pair stored once if i,j are both owned and i < j + pair stored by me if j is ghost (also stored by proc owning j) + Half + Newton: + N^2 / 2 search for neighbor pairs with full Newton's 3rd law + every pair stored exactly once by some processor + decision on ghost atoms based on itag,jtag tests +------------------------------------------------------------------------- */ + +template +void NPairNsq::build(NeighList *list) +{ + int i,j,jh,jstart,n,itype,jtype,which,bitmask,imol,iatom,moltemplate; + tagint itag,jtag,tagprev; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + double radsum,cut,cutsq; + int *neighptr; + + double **x = atom->x; + double *radius = atom->radius; + int *type = atom->type; + int *mask = atom->mask; + tagint *tag = atom->tag; + tagint *molecule = atom->molecule; + tagint **special = atom->special; + int **nspecial = atom->nspecial; + int nlocal = atom->nlocal; + int nall = nlocal + atom->nghost; + if (includegroup) { + nlocal = atom->nfirst; + bitmask = group->bitmask[includegroup]; + } + + int *molindex = atom->molindex; + int *molatom = atom->molatom; + Molecule **onemols = atom->avec->onemols; + if (molecular == Atom::TEMPLATE) moltemplate = 1; + else moltemplate = 0; + + int history = list->history; + int mask_history = 1 << HISTBITS; + + int *ilist = list->ilist; + int *numneigh = list->numneigh; + int **firstneigh = list->firstneigh; + MyPage *ipage = list->ipage; + + int inum = 0; + ipage->reset(); + + for (i = 0; i < nlocal; i++) { + n = 0; + neighptr = ipage->vget(); + + itag = tag[i]; + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + if (moltemplate) { + imol = molindex[i]; + iatom = molatom[i]; + tagprev = tag[i] - iatom - 1; + } + + // Full: loop over all atoms, owned and ghost, skip i = j + // Half: loop over remaining atoms, owned and ghost + // Newtoff: only store pair if i < j + // Newton: itag = jtag is possible for long cutoffs that include images of self + + if (!HALF) jstart = 0; + else jstart = i + 1; + + for (j = jstart; j < nall; j++) { + if (includegroup && !(mask[j] & bitmask)) continue; + + if (!HALF) { + // Full neighbor list + if (i == j) continue; + } else if (NEWTON) { + // Half neighbor list, newton on + if (j >= nlocal) { + jtag = tag[j]; + if (itag > jtag) { + if ((itag+jtag) % 2 == 0) continue; + } else if (itag < jtag) { + if ((itag+jtag) % 2 == 1) continue; + } else { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + } + } + } + } + + jtype = type[j]; + if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx * delx + dely * dely + delz * delz; + + if (SIZE) { + radsum = radius[i] + radius[j]; + cut = radsum + skin; + cutsq = cut * cut; + + if (rsq <= cutsq) { + jh = j; + if (history && rsq < radsum * radsum) + jh = jh ^ mask_history; + + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = jh; + else if (domain->minimum_image_check(delx,dely,delz)) + neighptr[n++] = jh; + else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); + } else neighptr[n++] = jh; + } + } else { + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (domain->minimum_image_check(delx,dely,delz)) + neighptr[n++] = j; + else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); + } else neighptr[n++] = j; + } + } + } + + ilist[inum++] = i; + firstneigh[i] = neighptr; + numneigh[i] = n; + ipage->vgot(n); + if (ipage->status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + } + + list->inum = inum; + if (!HALF) list->gnum = 0; +} + +namespace LAMMPS_NS { +template class NPairNsq<0,1,0>; +template class NPairNsq<1,0,0>; +template class NPairNsq<1,1,0>; +template class NPairNsq<0,1,1>; +template class NPairNsq<1,0,1>; +template class NPairNsq<1,1,1>; +} diff --git a/src/npair_nsq.h b/src/npair_nsq.h new file mode 100644 index 0000000000..4d616d33f2 --- /dev/null +++ b/src/npair_nsq.h @@ -0,0 +1,76 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 NPAIR_CLASS +// clang-format off +typedef NPairNsq<0, 1, 0> NPairFullNsq; +NPairStyle(full/nsq, + NPairFullNsq, + NP_FULL | NP_NSQ | NP_MOLONLY | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairNsq<1, 0, 0> NPairHalfNsqNewtoff; +NPairStyle(half/nsq/newtoff, + NPairHalfNsqNewtoff, + NP_HALF | NP_NSQ | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairNsq<1, 1, 0> NPairHalfNsqNewton; +NPairStyle(half/nsq/newton, + NPairHalfNsqNewton, + NP_HALF | NP_NSQ | NP_MOLONLY | NP_NEWTON | NP_ORTHO | NP_TRI); + +typedef NPairNsq<0, 1, 1> NPairFullSizeNsq; +NPairStyle(full/size/nsq, + NPairFullSizeNsq, + NP_FULL | NP_SIZE | NP_NSQ | NP_MOLONLY | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairNsq<1, 0, 1> NPairHalfSizeNsqNewtoff; +NPairStyle(half/size/nsq/newtoff, + NPairHalfSizeNsqNewtoff, + NP_HALF | NP_SIZE | NP_NSQ | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairNsq<1, 1, 1> NPairHalfSizeNsqNewton; +NPairStyle(half/size/nsq/newton, + NPairHalfSizeNsqNewton, + NP_HALF | NP_SIZE | NP_NSQ | NP_MOLONLY | NP_NEWTON | NP_ORTHO | NP_TRI); + +// clang-format on +#else + +#ifndef LMP_NPAIR_Nsq_H +#define LMP_NPAIR_Nsq_H + +#include "npair.h" + +namespace LAMMPS_NS { + +template +class NPairNsq : public NPair { + public: + NPairNsq(class LAMMPS *); + void build(class NeighList *) override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Neighbor list overflow, boost neigh_modify one + +UNDOCUMENTED + +*/ diff --git a/src/npair_nsq_ghost.cpp b/src/npair_nsq_ghost.cpp new file mode 100644 index 0000000000..4a733047c4 --- /dev/null +++ b/src/npair_nsq_ghost.cpp @@ -0,0 +1,176 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 "npair_nsq_ghost.h" +#include "neigh_list.h" +#include "atom.h" +#include "atom_vec.h" +#include "molecule.h" +#include "neighbor.h" +#include "domain.h" +#include "my_page.h" +#include "error.h" + +using namespace LAMMPS_NS; +using namespace NeighConst; + +/* ---------------------------------------------------------------------- */ + +template +NPairNsqGhost::NPairNsqGhost(LAMMPS *lmp) : NPair(lmp) {} + +/* ---------------------------------------------------------------------- + Full: + N^2 search for all neighbors + include neighbors of ghost atoms, but no "special neighbors" for ghosts + every neighbor pair appears in list of both atoms i and j + Half + Newtoff: + N^2 / 2 search for neighbor pairs with partial Newton's 3rd law + include neighbors of ghost atoms, but no "special neighbors" for ghosts + pair stored once if i,j are both owned and i < j + pair stored by me if i owned and j ghost (also stored by proc owning j) + pair stored once if i,j are both ghost and i < j +------------------------------------------------------------------------- */ + + +template +void NPairNsqGhost::build(NeighList *list) +{ + int i,j,jstart,n,itype,jtype,which,bitmask,imol,iatom,moltemplate; + tagint tagprev; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + int *neighptr; + + double **x = atom->x; + int *type = atom->type; + int *mask = atom->mask; + tagint *tag = atom->tag; + tagint *molecule = atom->molecule; + tagint **special = atom->special; + int **nspecial = atom->nspecial; + int nlocal = atom->nlocal; + int nall = nlocal + atom->nghost; + if (includegroup) { + nlocal = atom->nfirst; + bitmask = group->bitmask[includegroup]; + } + + int *molindex = atom->molindex; + int *molatom = atom->molatom; + Molecule **onemols = atom->avec->onemols; + if (molecular == Atom::TEMPLATE) moltemplate = 1; + else moltemplate = 0; + + int *ilist = list->ilist; + int *numneigh = list->numneigh; + int **firstneigh = list->firstneigh; + MyPage *ipage = list->ipage; + + int inum = 0; + ipage->reset(); + + // loop over owned & ghost atoms, storing neighbors + for (i = 0; i < nall; i++) { + n = 0; + neighptr = ipage->vget(); + + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + if (moltemplate) { + imol = molindex[i]; + iatom = molatom[i]; + tagprev = tag[i] - iatom - 1; + } + + // loop over all atoms, owned and ghost + // Full: + // skip i = j + // Half: + // only store pair if i < j + // stores own/own pairs only once + // stores own/ghost pairs with owned atom only, on both procs + // stores ghost/ghost pairs only once + // no molecular test when i = ghost atom + + if (!HALF) jstart = 0; + else jstart = i + 1; + + if (i < nlocal) { + for (j = jstart; j < nall; j++) { + if (includegroup && !(mask[j] & bitmask)) continue; // JTC: missing in original full version + if (!HALF) { + if (i == j) continue; + } + + jtype = type[j]; + if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx * delx + dely * dely + delz * delz; + + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (domain->minimum_image_check(delx,dely,delz)) + neighptr[n++] = j; + else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); + } else neighptr[n++] = j; + } + } + } else { + for (j = jstart; j < nall; j++) { + if (includegroup && !(mask[j] & bitmask)) continue; // JTC: missing in original full version + if (!HALF) { + if (i == j) continue; + } + + jtype = type[j]; + if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx * delx + dely * dely + delz * delz; + + if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; + } + } + + ilist[inum++] = i; + firstneigh[i] = neighptr; + numneigh[i] = n; + ipage->vgot(n); + if (ipage->status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + } + + list->inum = atom->nlocal; + list->gnum = inum - atom->nlocal; +} + +namespace LAMMPS_NS { +template class NPairNsqGhost<0>; +template class NPairNsqGhost<1>; +} diff --git a/src/npair_nsq_ghost.h b/src/npair_nsq_ghost.h new file mode 100644 index 0000000000..d1f34969d5 --- /dev/null +++ b/src/npair_nsq_ghost.h @@ -0,0 +1,53 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 NPAIR_CLASS +// clang-format off +typedef NPairNsqGhost<0> NPairFullNsqGhost; +NPairStyle(full/nsq/ghost, + NPairFullNsqGhost, + NP_FULL | NP_NSQ | NP_NEWTON | NP_NEWTOFF | NP_GHOST | NP_ORTHO | NP_TRI); + +typedef NPairNsqGhost<1> NPairHalfNsqGhostNewtoff; +NPairStyle(half/nsq/ghost/newtoff, + NPairHalfNsqGhostNewtoff, + NP_HALF | NP_NSQ | NP_NEWTOFF | NP_GHOST | NP_ORTHO | NP_TRI); +// clang-format on +#else + +#ifndef LMP_NPAIR_NSQ_GHOST_H +#define LMP_NPAIR_NSQ_GHOST_H + +#include "npair.h" + +namespace LAMMPS_NS { + +template +class NPairNsqGhost : public NPair { + public: + NPairNsqGhost(class LAMMPS *); + void build(class NeighList *) override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Neighbor list overflow, boost neigh_modify one + +UNDOCUMENTED + +*/ diff --git a/src/npair_respa_bin.cpp b/src/npair_respa_bin.cpp new file mode 100644 index 0000000000..7e8f79df5f --- /dev/null +++ b/src/npair_respa_bin.cpp @@ -0,0 +1,240 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 "npair_respa_bin.h" +#include "neigh_list.h" +#include "atom.h" +#include "atom_vec.h" +#include "molecule.h" +#include "domain.h" +#include "my_page.h" +#include "error.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +template +NPairRespaBin::NPairRespaBin(LAMMPS *lmp) : NPair(lmp) {} + +/* ---------------------------------------------------------------------- + multiple respa lists + Newtoff + binned neighbor list construction with partial Newton's 3rd law + each owned atom i checks own bin and surrounding bins in non-Newton stencil + pair stored once if i,j are both owned and i < j + pair stored by me if j is ghost (also stored by proc owning j) + Newton + binned neighbor list construction with full Newton's 3rd law + each owned atom i checks its own bin and other bins in Newton stencil + every pair stored exactly once by some processor +------------------------------------------------------------------------- */ + +template +void NPairRespaBin::build(NeighList *list) +{ + int i,j,k,n,itype,jtype,ibin,bin_start,n_inner,n_middle,imol,iatom,moltemplate; + tagint tagprev; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + int *neighptr,*neighptr_inner,*neighptr_middle; + + double **x = atom->x; + int *type = atom->type; + int *mask = atom->mask; + tagint *tag = atom->tag; + tagint *molecule = atom->molecule; + tagint **special = atom->special; + int **nspecial = atom->nspecial; + int nlocal = atom->nlocal; + if (includegroup) nlocal = atom->nfirst; + + int *molindex = atom->molindex; + int *molatom = atom->molatom; + Molecule **onemols = atom->avec->onemols; + if (molecular == Atom::TEMPLATE) moltemplate = 1; + else moltemplate = 0; + + int *ilist = list->ilist; + int *numneigh = list->numneigh; + int **firstneigh = list->firstneigh; + MyPage *ipage = list->ipage; + + int *ilist_inner = list->ilist_inner; + int *numneigh_inner = list->numneigh_inner; + int **firstneigh_inner = list->firstneigh_inner; + MyPage *ipage_inner = list->ipage_inner; + + int *ilist_middle,*numneigh_middle,**firstneigh_middle; + MyPage *ipage_middle; + int respamiddle = list->respamiddle; + if (respamiddle) { + ilist_middle = list->ilist_middle; + numneigh_middle = list->numneigh_middle; + firstneigh_middle = list->firstneigh_middle; + ipage_middle = list->ipage_middle; + } + + int inum = 0; + int which = 0; + int minchange = 0; + ipage->reset(); + ipage_inner->reset(); + if (respamiddle) ipage_middle->reset(); + + for (i = 0; i < nlocal; i++) { + n = n_inner = 0; + neighptr = ipage->vget(); + neighptr_inner = ipage_inner->vget(); + if (respamiddle) { + n_middle = 0; + neighptr_middle = ipage_middle->vget(); + } + + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + if (moltemplate) { + imol = molindex[i]; + iatom = molatom[i]; + tagprev = tag[i] - iatom - 1; + } + + ibin = atom2bin[i]; + + for (k = 0; k < nstencil; k++) { + bin_start = binhead[ibin+stencil[k]]; + if (stencil[k] == 0) { + if (HALF && NEWTON && (!TRI)) { + // Half neighbor list, newton on, orthonormal + // loop over rest of atoms in i's bin, ghosts are at end of linked list + bin_start = bins[i]; + } + } + + for (j = binstart; j >= 0; j = bins[j]) { + if (!NEWTON) { + // Half neighbor list, newton off + // only store pair if i < j + // stores own/own pairs only once + // stores own/ghost pairs on both procs + if (j <= i) continue; + } else if (TRI) { + // Half neighbor list, newton on, triclinic + // pairs for atoms j "below" i are excluded + // below = lower z or (equal z and lower y) or (equal zy and lower x) + // (equal zyx and j <= i) + // latter excludes self-self interaction but allows superposed atoms + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp) { + if (x[j][0] < xtmp) continue; + if (x[j][0] == xtmp && j <= i) continue; + } + } + } else { + // Half neighbor list, newton on, orthonormal + // store every pair for every bin in stencil,except for i's bin + + if (stencil[k] == 0) { + // if j is owned atom, store it, since j is beyond i in linked list + // if j is ghost, only store if j coords are "above and to the "right" of i + if (j >= nlocal) { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + } + } + } + } + + jtype = type[j]; + if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if ((minchange = domain->minimum_image_check(delx,dely,delz))) + neighptr[n++] = j; + else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); + } else neighptr[n++] = j; + + if (rsq < cut_inner_sq) { + if (which == 0) neighptr_inner[n_inner++] = j; + else if (minchange) neighptr_inner[n_inner++] = j; + else if (which > 0) + neighptr_inner[n_inner++] = j ^ (which << SBBITS); + } + + if (respamiddle && + rsq < cut_middle_sq && rsq > cut_middle_inside_sq) { + if (which == 0) neighptr_middle[n_middle++] = j; + else if (minchange) neighptr_middle[n_middle++] = j; + else if (which > 0) + neighptr_middle[n_middle++] = j ^ (which << SBBITS); + } + } + } + } + + ilist[inum] = i; + firstneigh[i] = neighptr; + numneigh[i] = n; + ipage->vgot(n); + if (ipage->status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + + ilist_inner[inum] = i; + firstneigh_inner[i] = neighptr_inner; + numneigh_inner[i] = n_inner; + ipage_inner->vgot(n_inner); + if (ipage_inner->status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + + if (respamiddle) { + ilist_middle[inum] = i; + firstneigh_middle[i] = neighptr_middle; + numneigh_middle[i] = n_middle; + ipage_middle->vgot(n_middle); + if (ipage_middle->status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + } + + inum++; + } + + list->inum = inum; + list->inum_inner = inum; + if (respamiddle) list->inum_middle = inum; +} + +namespace LAMMPS_NS { +template class NPairRespaBin<0,0>; +template class NPairRespaBin<1,0>; +template class NPairRespaBin<1,1>; +} diff --git a/src/npair_respa_bin.h b/src/npair_respa_bin.h new file mode 100644 index 0000000000..bf52ebcadc --- /dev/null +++ b/src/npair_respa_bin.h @@ -0,0 +1,58 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 NPAIR_CLASS +// clang-format off +typedef NPairRespaBin<0, 0> NPairHalfRespaBinNewtoff; +NPairStyle(half/respa/bin/newtoff, + NPairHalfRespaBinNewtoff, + NP_HALF | NP_RESPA | NP_BIN | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairRespaBin<1, 0> NPairHalfRespaBinNewton; +NPairStyle(half/respa/bin/newton, + NPairHalfRespaBinNewton, + NP_HALF | NP_RESPA | NP_BIN | NP_NEWTON | NP_ORTHO); + +typedef NPairRespaBin<1, 1> NPairHalfRespaBinNewtonTri; +NPairStyle(half/respa/bin/newton/tri, + NPairHalfRespaBinNewtonTri, + NP_HALF | NP_RESPA | NP_BIN | NP_NEWTON | NP_TRI); +// clang-format on +#else + +#ifndef LMP_NPAIR_RESPA_BIN_H +#define LMP_NPAIR_RESPA_BIN_H + +#include "npair.h" + +namespace LAMMPS_NS { + +template +class NPairRespaBin : public NPair { + public: + NPairRespaBin(class LAMMPS *); + void build(class NeighList *) override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Neighbor list overflow, boost neigh_modify one + +UNDOCUMENTED + +*/ diff --git a/src/npair_respa_nsq.cpp b/src/npair_respa_nsq.cpp new file mode 100644 index 0000000000..5587758cd4 --- /dev/null +++ b/src/npair_respa_nsq.cpp @@ -0,0 +1,214 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 "npair_respa_nsq.h" +#include "neigh_list.h" +#include "atom.h" +#include "atom_vec.h" +#include "group.h" +#include "molecule.h" +#include "domain.h" +#include "my_page.h" +#include "error.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +template +NPairRespaNsq::NPairRespaNsq(LAMMPS *lmp) : NPair(lmp) {} + +/* ---------------------------------------------------------------------- + multiple respa lists + Newtoff + N^2 / 2 search for neighbor pairs with partial Newton's 3rd law + pair added to list if atoms i and j are both owned and i < j + pair added if j is ghost (also stored by proc owning j) + Newton + N^2 / 2 search for neighbor pairs with full Newton's 3rd law + pair added to list if atoms i and j are both owned and i < j + if j is ghost only me or other proc adds pair + decision based on itag,jtag tests +------------------------------------------------------------------------- */ + +template +void NPairRespaNsq::build(NeighList *list) +{ + int i,j,n,itype,jtype,n_inner,n_middle,bitmask,imol,iatom,moltemplate; + tagint itag,jtag,tagprev; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + int *neighptr,*neighptr_inner,*neighptr_middle; + + double **x = atom->x; + int *type = atom->type; + int *mask = atom->mask; + tagint *tag = atom->tag; + tagint *molecule = atom->molecule; + tagint **special = atom->special; + int **nspecial = atom->nspecial; + int nlocal = atom->nlocal; + int nall = nlocal + atom->nghost; + if (includegroup) { + nlocal = atom->nfirst; + bitmask = group->bitmask[includegroup]; + } + + int *molindex = atom->molindex; + int *molatom = atom->molatom; + Molecule **onemols = atom->avec->onemols; + if (molecular == Atom::TEMPLATE) moltemplate = 1; + else moltemplate = 0; + + int *ilist = list->ilist; + int *numneigh = list->numneigh; + int **firstneigh = list->firstneigh; + MyPage *ipage = list->ipage; + + int *ilist_inner = list->ilist_inner; + int *numneigh_inner = list->numneigh_inner; + int **firstneigh_inner = list->firstneigh_inner; + MyPage *ipage_inner = list->ipage_inner; + + int *ilist_middle,*numneigh_middle,**firstneigh_middle; + MyPage *ipage_middle; + int respamiddle = list->respamiddle; + if (respamiddle) { + ilist_middle = list->ilist_middle; + numneigh_middle = list->numneigh_middle; + firstneigh_middle = list->firstneigh_middle; + ipage_middle = list->ipage_middle; + } + + int inum = 0; + int which = 0; + int minchange = 0; + ipage->reset(); + ipage_inner->reset(); + if (respamiddle) ipage_middle->reset(); + + for (i = 0; i < nlocal; i++) { + n = n_inner = 0; + neighptr = ipage->vget(); + neighptr_inner = ipage_inner->vget(); + if (respamiddle) { + n_middle = 0; + neighptr_middle = ipage_middle->vget(); + } + + itype = type[i]; + itag = tag[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + if (moltemplate) { + imol = molindex[i]; + iatom = molatom[i]; + tagprev = tag[i] - iatom - 1; + } + + // loop over remaining atoms, owned and ghost + + for (j = i+1; j < nall; j++) { + if (includegroup && !(mask[j] & bitmask)) continue; + + if (NEWTON) { + if (j >= nlocal) { + jtag = tag[j]; + if (itag > jtag) { + if ((itag+jtag) % 2 == 0) continue; + } else if (itag < jtag) { + if ((itag+jtag) % 2 == 1) continue; + } else { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + } + } + } + } + + jtype = type[j]; + if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if ((minchange = domain->minimum_image_check(delx,dely,delz))) + neighptr[n++] = j; + else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); + } else neighptr[n++] = j; + + if (rsq < cut_inner_sq) { + if (which == 0) neighptr_inner[n_inner++] = j; + else if (minchange) neighptr_inner[n_inner++] = j; + else if (which > 0) neighptr_inner[n_inner++] = j ^ (which << SBBITS); + } + + if (respamiddle && rsq < cut_middle_sq && rsq > cut_middle_inside_sq) { + if (which == 0) neighptr_middle[n_middle++] = j; + else if (minchange) neighptr_middle[n_middle++] = j; + else if (which > 0) + neighptr_middle[n_middle++] = j ^ (which << SBBITS); + } + } + } + + ilist[inum] = i; + firstneigh[i] = neighptr; + numneigh[i] = n; + ipage->vgot(n); + if (ipage->status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + + ilist_inner[inum] = i; + firstneigh_inner[i] = neighptr_inner; + numneigh_inner[i] = n_inner; + ipage_inner->vgot(n_inner); + if (ipage_inner->status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + + if (respamiddle) { + ilist_middle[inum] = i; + firstneigh_middle[i] = neighptr_middle; + numneigh_middle[i] = n_middle; + ipage_middle->vgot(n_middle); + if (ipage_middle->status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + } + + inum++; + } + + list->inum = inum; + list->inum_inner = inum; + if (respamiddle) list->inum_middle = inum; +} + +namespace LAMMPS_NS { +template class NPairRespaNsq<0>; +template class NPairRespaNsq<1>; +} diff --git a/src/npair_respa_nsq.h b/src/npair_respa_nsq.h new file mode 100644 index 0000000000..5d6ea60465 --- /dev/null +++ b/src/npair_respa_nsq.h @@ -0,0 +1,53 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 NPAIR_CLASS +// clang-format off +typedef NPairRespaNsq<0> NPairHalfRespaNsqNewtoff; +NPairStyle(half/respa/nsq/newtoff, + NPairHalfRespaNsqNewtoff, + NP_HALF | NP_RESPA | NP_NSQ | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairRespaNsq<1> NPairHalfRespaNsqNewton; +NPairStyle(half/respa/nsq/newton, + NPairHalfRespaNsqNewton, + NP_HALF | NP_RESPA | NP_NSQ | NP_NEWTON | NP_ORTHO | NP_TRI); //JTC: Originally didn't have TRI +// clang-format on +#else + +#ifndef LMP_NPAIR_RESPA_NSQ_H +#define LMP_NPAIR_RESPA_NSQ_H + +#include "npair.h" + +namespace LAMMPS_NS { + +template +class NPairRespaNsq : public NPair { + public: + NPairRespaNsq(class LAMMPS *); + void build(class NeighList *) override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Neighbor list overflow, boost neigh_modify one + +UNDOCUMENTED + +*/ diff --git a/src/npair_skip.cpp b/src/npair_skip.cpp new file mode 100644 index 0000000000..4ef0573dbb --- /dev/null +++ b/src/npair_skip.cpp @@ -0,0 +1,102 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 "npair_skip.h" + +#include "atom.h" +#include "error.h" +#include "my_page.h" +#include "neigh_list.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +NPairSkip::NPairSkip(LAMMPS *lmp) : NPair(lmp) {} + +/* ---------------------------------------------------------------------- + build skip list for subset of types from parent list + works for half and full lists + works for owned (non-ghost) list, also for ghost list + iskip and ijskip flag which atom types and type pairs to skip + if ghost, also store neighbors of ghost atoms & set inum,gnum correctly +------------------------------------------------------------------------- */ + +void NPairSkip::build(NeighList *list) +{ + int i,j,ii,jj,n,itype,jnum,joriginal; + int *neighptr,*jlist; + + int *type = atom->type; + int nlocal = atom->nlocal; + + int *ilist = list->ilist; + int *numneigh = list->numneigh; + int **firstneigh = list->firstneigh; + MyPage *ipage = list->ipage; + + int *ilist_skip = list->listskip->ilist; + int *numneigh_skip = list->listskip->numneigh; + int **firstneigh_skip = list->listskip->firstneigh; + int num_skip = list->listskip->inum; + if (list->ghost) num_skip += list->listskip->gnum; + + int *iskip = list->iskip; + int **ijskip = list->ijskip; + + int inum = 0; + ipage->reset(); + + // loop over atoms in other list + // skip I atom entirely if iskip is set for type[I] + // skip I,J pair if ijskip is set for type[I],type[J] + + for (ii = 0; ii < num_skip; ii++) { + i = ilist_skip[ii]; + itype = type[i]; + if (iskip[itype]) continue; + + n = 0; + neighptr = ipage->vget(); + + // loop over parent non-skip list + + jlist = firstneigh_skip[i]; + jnum = numneigh_skip[i]; + + for (jj = 0; jj < jnum; jj++) { + joriginal = jlist[jj]; + j = joriginal & NEIGHMASK; + if (ijskip[itype][type[j]]) continue; + neighptr[n++] = joriginal; + } + + ilist[inum++] = i; + firstneigh[i] = neighptr; + numneigh[i] = n; + ipage->vgot(n); + if (ipage->status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + } + + list->inum = inum; + if (list->ghost) { + int num = 0; + for (i = 0; i < inum; i++) + if (ilist[i] < nlocal) num++; + else break; + list->inum = num; + list->gnum = inum - num; + } +} diff --git a/src/npair_skip.h b/src/npair_skip.h new file mode 100644 index 0000000000..b2a0752117 --- /dev/null +++ b/src/npair_skip.h @@ -0,0 +1,59 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 NPAIR_CLASS +// clang-format off +NPairStyle(skip, + NPairSkip, + NP_SKIP | NP_HALF | NP_FULL | + NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +NPairStyle(skip/ghost, + NPairSkip, + NP_SKIP | NP_HALF | NP_FULL | + NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_GHOST); + +NPairStyle(skip/half/size, + NPairSkip, + NP_SKIP | NP_SIZE | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); +// clang-format on +#else + +#ifndef LMP_NPAIR_SKIP_H +#define LMP_NPAIR_SKIP_H + +#include "npair.h" + +namespace LAMMPS_NS { + +class NPairSkip : public NPair { + public: + NPairSkip(class LAMMPS *); + void build(class NeighList *) override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Neighbor list overflow, boost neigh_modify one + +UNDOCUMENTED + +*/ diff --git a/src/npair_skip_respa.cpp b/src/npair_skip_respa.cpp new file mode 100644 index 0000000000..373fe3f8db --- /dev/null +++ b/src/npair_skip_respa.cpp @@ -0,0 +1,163 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 "npair_skip_respa.h" + +#include "atom.h" +#include "error.h" +#include "my_page.h" +#include "neigh_list.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +NPairSkipRespa::NPairSkipRespa(LAMMPS *lmp) : NPair(lmp) {} + +/* ---------------------------------------------------------------------- + build skip list for subset of types from parent list + iskip and ijskip flag which atom types and type pairs to skip + this is for respa lists, copy the inner/middle values from parent +------------------------------------------------------------------------- */ + +void NPairSkipRespa::build(NeighList *list) +{ + int i,j,ii,jj,n,itype,jnum,joriginal,n_inner,n_middle; + int *neighptr,*jlist,*neighptr_inner,*neighptr_middle; + + int *type = atom->type; + + int *ilist = list->ilist; + int *numneigh = list->numneigh; + int **firstneigh = list->firstneigh; + MyPage *ipage = list->ipage; + + int *ilist_skip = list->listskip->ilist; + int *numneigh_skip = list->listskip->numneigh; + int **firstneigh_skip = list->listskip->firstneigh; + int inum_skip = list->listskip->inum; + + int *iskip = list->iskip; + int **ijskip = list->ijskip; + + int *ilist_inner = list->ilist_inner; + int *numneigh_inner = list->numneigh_inner; + int **firstneigh_inner = list->firstneigh_inner; + MyPage *ipage_inner = list->ipage_inner; + int *numneigh_inner_skip = list->listskip->numneigh_inner; + int **firstneigh_inner_skip = list->listskip->firstneigh_inner; + + int *ilist_middle,*numneigh_middle,**firstneigh_middle; + MyPage *ipage_middle; + int *numneigh_middle_skip,**firstneigh_middle_skip; + int respamiddle = list->respamiddle; + if (respamiddle) { + ilist_middle = list->ilist_middle; + numneigh_middle = list->numneigh_middle; + firstneigh_middle = list->firstneigh_middle; + ipage_middle = list->ipage_middle; + numneigh_middle_skip = list->listskip->numneigh_middle; + firstneigh_middle_skip = list->listskip->firstneigh_middle; + } + + int inum = 0; + ipage->reset(); + ipage_inner->reset(); + if (respamiddle) ipage_middle->reset(); + + // loop over atoms in other list + // skip I atom entirely if iskip is set for type[I] + // skip I,J pair if ijskip is set for type[I],type[J] + + for (ii = 0; ii < inum_skip; ii++) { + i = ilist_skip[ii]; + itype = type[i]; + if (iskip[itype]) continue; + + n = n_inner = 0; + neighptr = ipage->vget(); + neighptr_inner = ipage_inner->vget(); + if (respamiddle) { + n_middle = 0; + neighptr_middle = ipage_middle->vget(); + } + + // loop over parent outer rRESPA list + + jlist = firstneigh_skip[i]; + jnum = numneigh_skip[i]; + + for (jj = 0; jj < jnum; jj++) { + joriginal = jlist[jj]; + j = joriginal & NEIGHMASK; + if (ijskip[itype][type[j]]) continue; + neighptr[n++] = joriginal; + } + + // loop over parent inner rRESPA list + + jlist = firstneigh_inner_skip[i]; + jnum = numneigh_inner_skip[i]; + + for (jj = 0; jj < jnum; jj++) { + joriginal = jlist[jj]; + j = joriginal & NEIGHMASK; + if (ijskip[itype][type[j]]) continue; + neighptr_inner[n_inner++] = joriginal; + } + + // loop over parent middle rRESPA list + + if (respamiddle) { + jlist = firstneigh_middle_skip[i]; + jnum = numneigh_middle_skip[i]; + + for (jj = 0; jj < jnum; jj++) { + joriginal = jlist[jj]; + j = joriginal & NEIGHMASK; + if (ijskip[itype][type[j]]) continue; + neighptr_middle[n_middle++] = joriginal; + } + } + + ilist[inum] = i; + firstneigh[i] = neighptr; + numneigh[i] = n; + ipage->vgot(n); + if (ipage->status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + + ilist_inner[inum] = i; + firstneigh_inner[i] = neighptr_inner; + numneigh_inner[i] = n_inner; + ipage_inner->vgot(n); + if (ipage_inner->status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + + if (respamiddle) { + ilist_middle[inum] = i; + firstneigh_middle[i] = neighptr_middle; + numneigh_middle[i] = n_middle; + ipage_middle->vgot(n); + if (ipage_middle->status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + } + + inum++; + } + + list->inum = inum; + list->inum_inner = inum; + if (respamiddle) list->inum_middle = inum; +} diff --git a/src/npair_skip_respa.h b/src/npair_skip_respa.h new file mode 100644 index 0000000000..b0938287b5 --- /dev/null +++ b/src/npair_skip_respa.h @@ -0,0 +1,48 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 NPAIR_CLASS +// clang-format off +NPairStyle(skip/half/respa, + NPairSkipRespa, + NP_SKIP | NP_RESPA | NP_HALF | NP_FULL | + NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); +// clang-format on +#else + +#ifndef LMP_NPAIR_SKIP_RESPA_H +#define LMP_NPAIR_SKIP_RESPA_H + +#include "npair.h" + +namespace LAMMPS_NS { + +class NPairSkipRespa : public NPair { + public: + NPairSkipRespa(class LAMMPS *); + void build(class NeighList *) override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Neighbor list overflow, boost neigh_modify one + +UNDOCUMENTED + +*/ diff --git a/src/npair_skip_size_off2on.cpp b/src/npair_skip_size_off2on.cpp new file mode 100644 index 0000000000..e48ca345ff --- /dev/null +++ b/src/npair_skip_size_off2on.cpp @@ -0,0 +1,100 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 "npair_skip_size_off2on.h" + +#include "atom.h" +#include "error.h" +#include "my_page.h" +#include "neigh_list.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +NPairSkipSizeOff2on::NPairSkipSizeOff2on(LAMMPS *lmp) : NPair(lmp) {} + +/* ---------------------------------------------------------------------- + build skip list for subset of types from parent list + iskip and ijskip flag which atom types and type pairs to skip + parent non-skip list used newton off, this skip list is newton on +------------------------------------------------------------------------- */ + +void NPairSkipSizeOff2on::build(NeighList *list) +{ + int i,j,ii,jj,n,itype,jnum,joriginal; + tagint itag,jtag; + int *neighptr,*jlist; + + tagint *tag = atom->tag; + int *type = atom->type; + int nlocal = atom->nlocal; + + int *ilist = list->ilist; + int *numneigh = list->numneigh; + int **firstneigh = list->firstneigh; + MyPage *ipage = list->ipage; + + int *ilist_skip = list->listskip->ilist; + int *numneigh_skip = list->listskip->numneigh; + int **firstneigh_skip = list->listskip->firstneigh; + int inum_skip = list->listskip->inum; + + int *iskip = list->iskip; + int **ijskip = list->ijskip; + + int inum = 0; + ipage->reset(); + + // loop over atoms in other list + // skip I atom entirely if iskip is set for type[I] + // skip I,J pair if ijskip is set for type[I],type[J] + + for (ii = 0; ii < inum_skip; ii++) { + i = ilist_skip[ii]; + itype = type[i]; + if (iskip[itype]) continue; + itag = tag[i]; + + n = 0; + neighptr = ipage->vget(); + + // loop over parent non-skip size list and optionally its history info + + jlist = firstneigh_skip[i]; + jnum = numneigh_skip[i]; + + for (jj = 0; jj < jnum; jj++) { + joriginal = jlist[jj]; + j = joriginal & NEIGHMASK; + if (ijskip[itype][type[j]]) continue; + + // only keep I,J when J = ghost if Itag < Jtag + + jtag = tag[j]; + if (j >= nlocal && jtag < itag) continue; + + neighptr[n++] = joriginal; + } + + ilist[inum++] = i; + firstneigh[i] = neighptr; + numneigh[i] = n; + ipage->vgot(n); + if (ipage->status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + } + + list->inum = inum; +} diff --git a/src/npair_skip_size_off2on.h b/src/npair_skip_size_off2on.h new file mode 100644 index 0000000000..39aee76b09 --- /dev/null +++ b/src/npair_skip_size_off2on.h @@ -0,0 +1,48 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 NPAIR_CLASS +// clang-format off +NPairStyle(skip/size/off2on, + NPairSkipSizeOff2on, + NP_SKIP | NP_SIZE | NP_OFF2ON | NP_HALF | + NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); +// clang-format on +#else + +#ifndef LMP_NPAIR_SKIP_SIZE_OFF2ON_H +#define LMP_NPAIR_SKIP_SIZE_OFF2ON_H + +#include "npair.h" + +namespace LAMMPS_NS { + +class NPairSkipSizeOff2on : public NPair { + public: + NPairSkipSizeOff2on(class LAMMPS *); + void build(class NeighList *) override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Neighbor list overflow, boost neigh_modify one + +UNDOCUMENTED + +*/ diff --git a/src/npair_skip_size_off2on_oneside.cpp b/src/npair_skip_size_off2on_oneside.cpp new file mode 100644 index 0000000000..1e4b4ac78d --- /dev/null +++ b/src/npair_skip_size_off2on_oneside.cpp @@ -0,0 +1,163 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 "npair_skip_size_off2on_oneside.h" + +#include "atom.h" +#include "domain.h" +#include "error.h" +#include "my_page.h" +#include "neigh_list.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +NPairSkipSizeOff2onOneside::NPairSkipSizeOff2onOneside(LAMMPS *lmp) : + NPair(lmp) {} + +/* ---------------------------------------------------------------------- + build skip list for subset of types from parent list + iskip and ijskip flag which atom types and type pairs to skip + parent non-skip list used newton off and was not onesided, + this skip list is newton on and onesided +------------------------------------------------------------------------- */ + +void NPairSkipSizeOff2onOneside::build(NeighList *list) +{ + int i,j,ii,jj,n,itype,jnum,joriginal,flip,tmp; + int *surf,*jlist; + + int *type = atom->type; + int nlocal = atom->nlocal; + + int *ilist = list->ilist; + int *numneigh = list->numneigh; + int **firstneigh = list->firstneigh; + MyPage *ipage = list->ipage; + + int *ilist_skip = list->listskip->ilist; + int *numneigh_skip = list->listskip->numneigh; + int **firstneigh_skip = list->listskip->firstneigh; + int inum_skip = list->listskip->inum; + + int *iskip = list->iskip; + int **ijskip = list->ijskip; + + if (domain->dimension == 2) surf = atom->line; + else surf = atom->tri; + + int inum = 0; + ipage->reset(); + + // two loops over parent list required, one to count, one to store + // because onesided constraint means pair I,J may be stored with I or J + // so don't know in advance how much space to alloc for each atom's neighs + + // first loop over atoms in other list to count neighbors + // skip I atom entirely if iskip is set for type[I] + // skip I,J pair if ijskip is set for type[I],type[J] + + for (i = 0; i < nlocal; i++) numneigh[i] = 0; + + for (ii = 0; ii < inum_skip; ii++) { + i = ilist_skip[ii]; + itype = type[i]; + if (iskip[itype]) continue; + + n = 0; + + // loop over parent non-skip size list + + jlist = firstneigh_skip[i]; + jnum = numneigh_skip[i]; + + for (jj = 0; jj < jnum; jj++) { + joriginal = jlist[jj]; + j = joriginal & NEIGHMASK; + if (ijskip[itype][type[j]]) continue; + + // flip I,J if necessary to satisfy onesided constraint + // do not keep if I is now ghost + + if (surf[i] >= 0) { + if (j >= nlocal) continue; + tmp = i; + i = j; + j = tmp; + flip = 1; + } else flip = 0; + + numneigh[i]++; + if (flip) i = j; + } + } + + // allocate all per-atom neigh list chunks + + for (i = 0; i < nlocal; i++) { + if (numneigh[i] == 0) continue; + n = numneigh[i]; + firstneigh[i] = ipage->get(n); + if (ipage->status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + } + + // second loop over atoms in other list to store neighbors + // skip I atom entirely if iskip is set for type[I] + // skip I,J pair if ijskip is set for type[I],type[J] + + for (i = 0; i < nlocal; i++) numneigh[i] = 0; + + for (ii = 0; ii < inum_skip; ii++) { + i = ilist_skip[ii]; + itype = type[i]; + if (iskip[itype]) continue; + + // loop over parent non-skip size list and optionally its history info + + jlist = firstneigh_skip[i]; + jnum = numneigh_skip[i]; + + for (jj = 0; jj < jnum; jj++) { + joriginal = jlist[jj]; + j = joriginal & NEIGHMASK; + if (ijskip[itype][type[j]]) continue; + + // flip I,J if necessary to satisfy onesided constraint + // do not keep if I is now ghost + + if (surf[i] >= 0) { + if (j >= nlocal) continue; + tmp = i; + i = j; + j = tmp; + flip = 1; + } else flip = 0; + + // store j in neigh list, not joriginal, like other neigh methods + // OK, b/c there is no special list flagging for surfs + + firstneigh[i][numneigh[i]] = j; + numneigh[i]++; + if (flip) i = j; + } + + // only add atom I to ilist if it has neighbors + + if (numneigh[i]) ilist[inum++] = i; + } + + list->inum = inum; +} diff --git a/src/npair_skip_size_off2on_oneside.h b/src/npair_skip_size_off2on_oneside.h new file mode 100644 index 0000000000..3f1cd6ef34 --- /dev/null +++ b/src/npair_skip_size_off2on_oneside.h @@ -0,0 +1,48 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, 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 NPAIR_CLASS +// clang-format off +NPairStyle(skip/size/off2on/oneside, + NPairSkipSizeOff2onOneside, + NP_SKIP | NP_SIZE | NP_OFF2ON | NP_ONESIDE | NP_HALF | + NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | + NP_ORTHO | NP_TRI); +// clang-format on +#else + +#ifndef LMP_NPAIR_SKIP_SIZE_OFF2ON_ONESIDE_H +#define LMP_NPAIR_SKIP_SIZE_OFF2ON_ONESIDE_H + +#include "npair.h" + +namespace LAMMPS_NS { + +class NPairSkipSizeOff2onOneside : public NPair { + public: + NPairSkipSizeOff2onOneside(class LAMMPS *); + void build(class NeighList *) override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Neighbor list overflow, boost neigh_modify one + +UNDOCUMENTED + +*/ diff --git a/src/nstencil_ghost_bin.h b/src/nstencil_ghost_bin.h index 31268cc30d..cab6e6714c 100644 --- a/src/nstencil_ghost_bin.h +++ b/src/nstencil_ghost_bin.h @@ -16,32 +16,32 @@ typedef NStencilGhostBin<0, 0, 0> NStencilFullGhostBin2d; NStencilStyle(full/ghost/bin/2d, NStencilFullGhostBin2d, - NS_FULL | NS_GhostBin | NS_2D | NS_ORTHO | NS_TRI); + NS_FULL | NS_GHOST | NS_BIN | NS_2D | NS_ORTHO | NS_TRI); typedef NStencilGhostBin<0, 1, 0> NStencilFullGhostBin3d; NStencilStyle(full/ghost/bin/3d, NStencilFullGhostBin3d, - NS_FULL | NS_GhostBin | NS_3D | NS_ORTHO | NS_TRI); + NS_FULL | NS_GHOST | NS_BIN | NS_3D | NS_ORTHO | NS_TRI); typedef NStencilGhostBin<1, 0, 0> NStencilHalfGhostBin2d; NStencilStyle(half/ghost/bin/2d, NStencilHalfGhostBin2d, - NS_HALF | NS_GhostBin | NS_2D | NS_ORTHO); + NS_HALF | NS_GHOST | NS_BIN | NS_2D | NS_ORTHO); typedef NStencilGhostBin<1, 0, 1> NStencilHalfGhostBin2dTri; NStencilStyle(half/ghost/bin/2d/tri, NStencilHalfGhostBin2dTri, - NS_HALF | NS_GhostBin | NS_2D | NS_TRI); + NS_HALF | NS_GHOST | NS_BIN | NS_2D | NS_TRI); typedef NStencilGhostBin<1, 1, 0> NStencilHalfGhostBin3d; NStencilStyle(half/ghost/bin/3d, NStencilHalfGhostBin3d, - NS_HALF | NS_GhostBin | NS_3D | NS_ORTHO); + NS_HALF | NS_GHOST | NS_BIN | NS_3D | NS_ORTHO); typedef NStencilGhostBin<1, 1, 1> NStencilHalfGhostBin3dTri; NStencilStyle(half/ghost/bin/3d/tri, NStencilHalfGhostBin3dTri, - NS_HALF | NS_GhostBin | NS_3D | NS_TRI); + NS_HALF | NS_GHOST | NS_BIN | NS_3D | NS_TRI); // clang-format on #else From 6544fbd248e9a8a6486ca01422610f054ddb09c3 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Sun, 30 Oct 2022 21:04:31 -0600 Subject: [PATCH 005/189] Fixing compilation mistake, updating dev reference --- src/npair_bin.cpp | 4 ++-- src/npair_bin.h | 10 +--------- src/npair_bin_atomonly.cpp | 4 ++-- src/npair_bin_atomonly.h | 10 +--------- src/npair_multi_old.cpp | 10 +++++----- src/npair_nsq.cpp | 5 +++-- src/npair_nsq_ghost.cpp | 1 + src/npair_respa_bin.cpp | 4 ++-- 8 files changed, 17 insertions(+), 31 deletions(-) diff --git a/src/npair_bin.cpp b/src/npair_bin.cpp index 83f8c53ecc..41f4575444 100644 --- a/src/npair_bin.cpp +++ b/src/npair_bin.cpp @@ -1,8 +1,8 @@ // clang-format off -/* ---------------------------------------------------------------------- +/* -*- c++ -*- ---------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://www.lammps.org/, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov + 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 diff --git a/src/npair_bin.h b/src/npair_bin.h index 92e3243397..e9cd65fa34 100644 --- a/src/npair_bin.h +++ b/src/npair_bin.h @@ -1,7 +1,7 @@ /* -*- c++ -*- ---------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://www.lammps.org/, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov + 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 @@ -75,11 +75,3 @@ class NPairBin : public NPair { #endif #endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_bin_atomonly.cpp b/src/npair_bin_atomonly.cpp index 3b15580264..f658a44b60 100644 --- a/src/npair_bin_atomonly.cpp +++ b/src/npair_bin_atomonly.cpp @@ -1,8 +1,8 @@ // clang-format off -/* ---------------------------------------------------------------------- +/* -*- c++ -*- ---------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://www.lammps.org/, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov + 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 diff --git a/src/npair_bin_atomonly.h b/src/npair_bin_atomonly.h index 581700af7d..febe7c2f0e 100644 --- a/src/npair_bin_atomonly.h +++ b/src/npair_bin_atomonly.h @@ -1,7 +1,7 @@ /* -*- c++ -*- ---------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://www.lammps.org/, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov + 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 @@ -75,11 +75,3 @@ class NPairBinAtomonly : public NPair { #endif #endif - -/* ERROR/WARNING messages: - -E: Neighbor list overflow, boost neigh_modify one - -UNDOCUMENTED - -*/ diff --git a/src/npair_multi_old.cpp b/src/npair_multi_old.cpp index 28efc16797..3c61d4ba29 100644 --- a/src/npair_multi_old.cpp +++ b/src/npair_multi_old.cpp @@ -1,8 +1,8 @@ // clang-format off -/* ---------------------------------------------------------------------- +/* -*- c++ -*- ---------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://www.lammps.org/, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov + 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 @@ -56,7 +56,7 @@ void NPairMultiOld::build(NeighList *list) double xtmp,ytmp,ztmp,delx,dely,delz,rsq; double radsum,cut,cutsq; int *neighptr,*s; - double *cutsq,*distsq; + double *cutnsq,*distsq; double **x = atom->x; double *radius = atom->radius; @@ -103,7 +103,7 @@ void NPairMultiOld::build(NeighList *list) ibin = atom2bin[i]; s = stencil_multi_old[itype]; distsq = distsq_multi_old[itype]; - cutsq = cutneighsq[itype]; + cutnsq = cutneighsq[itype]; ns = nstencil_multi_old[itype]; for (k = 0; k < ns; k++) { bin_start = binhead[ibin+stencil[k]]; @@ -158,7 +158,7 @@ void NPairMultiOld::build(NeighList *list) } jtype = type[j]; - if (cutsq[jtype] < distsq[k]) continue; + if (cutnsq[jtype] < distsq[k]) continue; if (i == j) continue; if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; diff --git a/src/npair_nsq.cpp b/src/npair_nsq.cpp index 87a05e8136..77dafe6191 100644 --- a/src/npair_nsq.cpp +++ b/src/npair_nsq.cpp @@ -1,8 +1,8 @@ // clang-format off -/* ---------------------------------------------------------------------- +/* -*- c++ -*- ---------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://www.lammps.org/, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov + 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 @@ -16,6 +16,7 @@ #include "neigh_list.h" #include "atom.h" #include "atom_vec.h" +#include "group.h" #include "molecule.h" #include "neighbor.h" #include "domain.h" diff --git a/src/npair_nsq_ghost.cpp b/src/npair_nsq_ghost.cpp index 8ff34accf8..e9cc732e7d 100644 --- a/src/npair_nsq_ghost.cpp +++ b/src/npair_nsq_ghost.cpp @@ -16,6 +16,7 @@ #include "neigh_list.h" #include "atom.h" #include "atom_vec.h" +#include "group.h" #include "molecule.h" #include "neighbor.h" #include "domain.h" diff --git a/src/npair_respa_bin.cpp b/src/npair_respa_bin.cpp index 2f39ad547c..a3bcbf5124 100644 --- a/src/npair_respa_bin.cpp +++ b/src/npair_respa_bin.cpp @@ -116,14 +116,14 @@ void NPairRespaBin::build(NeighList *list) for (k = 0; k < nstencil; k++) { bin_start = binhead[ibin+stencil[k]]; if (stencil[k] == 0) { - if (HALF && NEWTON && (!TRI)) { + if (NEWTON && (!TRI)) { // Half neighbor list, newton on, orthonormal // loop over rest of atoms in i's bin, ghosts are at end of linked list bin_start = bins[i]; } } - for (j = binstart; j >= 0; j = bins[j]) { + for (j = bin_start; j >= 0; j = bins[j]) { if (!NEWTON) { // Half neighbor list, newton off // only store pair if i < j From c5181bb7c80ee3c28d2aec63e602d7745e384ef5 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Fri, 11 Nov 2022 16:56:21 -0700 Subject: [PATCH 006/189] Fixing various oversights in npair classes --- src/npair_bin_atomonly.cpp | 2 +- src/npair_bin_ghost.cpp | 16 +++++------ src/npair_multi.cpp | 54 ++++++++++++++++++++++++++++---------- src/npair_multi.h | 4 +-- src/npair_multi_old.h | 16 +++++------ 5 files changed, 59 insertions(+), 33 deletions(-) diff --git a/src/npair_bin_atomonly.cpp b/src/npair_bin_atomonly.cpp index f658a44b60..100b521b92 100644 --- a/src/npair_bin_atomonly.cpp +++ b/src/npair_bin_atomonly.cpp @@ -139,7 +139,7 @@ void NPairBinAtomonly::build(NeighList *list) delx = xtmp - x[j][0]; dely = ytmp - x[j][1]; delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; + rsq = delx * delx + dely * dely + delz * delz; if (SIZE) { radsum = radius[i] + radius[j]; diff --git a/src/npair_bin_ghost.cpp b/src/npair_bin_ghost.cpp index 615759508d..4cf30f10f9 100644 --- a/src/npair_bin_ghost.cpp +++ b/src/npair_bin_ghost.cpp @@ -103,17 +103,17 @@ void NPairBinGhost::build(NeighList *list) for (k = 0; k < nstencil; k++) { bin_start = binhead[ibin+stencil[k]]; for (j = bin_start; j >= 0; j = bins[j]) { - if (!HALF) { - // Full neighbor list - // only skip i = j - if (i == j) continue; - } else { + if (HALF) { // Half neighbor list, newton off // only store pair if i < j // stores own/own pairs only once // stores own/ghost pairs on both procs // stores ghost/ghost pairs only once if (j <= i) continue; + } else { + // Full neighbor list + // only skip i = j + if (i == j) continue; } jtype = type[j]; @@ -151,10 +151,10 @@ void NPairBinGhost::build(NeighList *list) ybin2 < 0 || ybin2 >= mbiny || zbin2 < 0 || zbin2 >= mbinz) continue; for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (!HALF) { - if (i == j) continue; - } else { + if (HALF) { if (j <= i) continue; + } else { + if (i == j) continue; } jtype = type[j]; diff --git a/src/npair_multi.cpp b/src/npair_multi.cpp index 103f5a1845..ab182f39c4 100644 --- a/src/npair_multi.cpp +++ b/src/npair_multi.cpp @@ -162,20 +162,46 @@ void NPairMulti::build(NeighList *list) delz = ztmp - x[j][2]; rsq = delx*delx + dely*dely + delz*delz; - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; + if (SIZE) { + radsum = radius[i] + radius[j]; + cut = radsum + skin; + cutsq = cut * cut; + + if (rsq <= cutsq) { + jh = j; + if (history && rsq < radsum * radsum) + jh = jh ^ mask_history; + + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = jh; + else if (domain->minimum_image_check(delx,dely,delz)) + neighptr[n++] = jh; + else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); + } else neighptr[n++] = jh; + } + } else { + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (domain->minimum_image_check(delx,dely,delz)) + neighptr[n++] = j; + else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); + } else neighptr[n++] = j; + } } } } diff --git a/src/npair_multi.h b/src/npair_multi.h index 4bb1315200..f6a68711e2 100644 --- a/src/npair_multi.h +++ b/src/npair_multi.h @@ -14,7 +14,7 @@ #ifdef NPAIR_CLASS // clang-format off typedef NPairMulti<0, 1, 0, 0> NPairFullMulti; -NPairStyle(full/Multi, +NPairStyle(full/multi, NPairFullMulti, NP_FULL | NP_MULTI | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); @@ -35,7 +35,7 @@ NPairStyle(half/multi/newton/tri, NP_HALF | NP_MULTI | NP_NEWTON | NP_TRI); typedef NPairMulti<0, 1, 0, 1> NPairFullSizeMulti; -NPairStyle(full/size/Multi, +NPairStyle(full/size/multi, NPairFullSizeMulti, NP_FULL | NP_SIZE | NP_MULTI | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); diff --git a/src/npair_multi_old.h b/src/npair_multi_old.h index 8eb1cc3b00..f01844ed57 100644 --- a/src/npair_multi_old.h +++ b/src/npair_multi_old.h @@ -16,44 +16,44 @@ typedef NPairMultiOld<0, 1, 0, 0> NPairFullMultiOld; NPairStyle(full/multi/old, NPairFullMultiOld, - NP_FULL | NP_MULTI_OLD | NP_MOLONLY | + NP_FULL | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); typedef NPairMultiOld<1, 0, 0, 0> NPairHalfMultiOldNewtoff; NPairStyle(half/multi/old/newtoff, NPairHalfMultiOldNewtoff, - NP_HALF | NP_MULTI_OLD | NP_MOLONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); + NP_HALF | NP_MULTI_OLD | NP_NEWTOFF | NP_ORTHO | NP_TRI); typedef NPairMultiOld<1, 1, 0, 0> NPairHalfMultiOldNewton; NPairStyle(half/multi/old/newton, NPairHalfMultiOldNewton, - NP_HALF | NP_MULTI_OLD | NP_MOLONLY | NP_NEWTON | NP_ORTHO); + NP_HALF | NP_MULTI_OLD | NP_NEWTON | NP_ORTHO); typedef NPairMultiOld<1, 1, 1, 0> NPairHalfMultiOldNewtonTri; NPairStyle(half/multi/old/newton/tri, NPairHalfMultiOldNewtonTri, - NP_HALF | NP_MULTI_OLD | NP_MOLONLY | NP_NEWTON | NP_TRI); + NP_HALF | NP_MULTI_OLD | NP_NEWTON | NP_TRI); typedef NPairMultiOld<0, 1, 0, 1> NPairFullSizeMultiOld; NPairStyle(full/size/multi/old, NPairFullSizeMultiOld, - NP_FULL | NP_SIZE | NP_MULTI_OLD | NP_MOLONLY | + NP_FULL | NP_SIZE | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); typedef NPairMultiOld<1, 0, 0, 1> NPairHalfSizeMultiOldNewtoff; NPairStyle(half/size/multi/old/newtoff, NPairHalfSizeMultiOldNewtoff, - NP_HALF | NP_SIZE | NP_MULTI_OLD | NP_MOLONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); + NP_HALF | NP_SIZE | NP_MULTI_OLD | NP_NEWTOFF | NP_ORTHO | NP_TRI); typedef NPairMultiOld<1, 1, 0, 1> NPairHalfSizeMultiOldNewton; NPairStyle(half/size/multi/old/newton, NPairHalfSizeMultiOldNewton, - NP_HALF | NP_SIZE | NP_MULTI_OLD | NP_MOLONLY | NP_NEWTON | NP_ORTHO); + NP_HALF | NP_SIZE | NP_MULTI_OLD | NP_NEWTON | NP_ORTHO); typedef NPairMultiOld<1, 1, 1, 1> NPairHalfSizeMultiOldNewtonTri; NPairStyle(half/size/multi/old/newton/tri, NPairHalfSizeMultiOldNewtonTri, - NP_HALF | NP_SIZE | NP_MULTI_OLD | NP_MOLONLY | NP_NEWTON | NP_TRI); + NP_HALF | NP_SIZE | NP_MULTI_OLD | NP_NEWTON | NP_TRI); // clang-format on #else From 11f3195b0c5a582d933d01f44d5a7e32145401eb Mon Sep 17 00:00:00 2001 From: jtclemm Date: Fri, 11 Nov 2022 20:35:52 -0700 Subject: [PATCH 007/189] Fixing more oversights in npair classes --- src/npair_nsq.h | 14 ++++++-------- src/npair_nsq_ghost.cpp | 9 ++++++--- src/npair_nsq_ghost.h | 6 +++--- src/npair_respa_nsq.cpp | 4 ++-- src/npair_skip.h | 5 ----- 5 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/npair_nsq.h b/src/npair_nsq.h index 505a72ea03..749ea3a78c 100644 --- a/src/npair_nsq.h +++ b/src/npair_nsq.h @@ -16,8 +16,7 @@ typedef NPairNsq<0, 1, 0> NPairFullNsq; NPairStyle(full/nsq, NPairFullNsq, - NP_FULL | NP_NSQ | NP_MOLONLY | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + NP_FULL | NP_NSQ | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); typedef NPairNsq<1, 0, 0> NPairHalfNsqNewtoff; NPairStyle(half/nsq/newtoff, @@ -27,13 +26,12 @@ NPairStyle(half/nsq/newtoff, typedef NPairNsq<1, 1, 0> NPairHalfNsqNewton; NPairStyle(half/nsq/newton, NPairHalfNsqNewton, - NP_HALF | NP_NSQ | NP_MOLONLY | NP_NEWTON | NP_ORTHO | NP_TRI); + NP_HALF | NP_NSQ | NP_NEWTON | NP_ORTHO | NP_TRI); typedef NPairNsq<0, 1, 1> NPairFullSizeNsq; NPairStyle(full/size/nsq, NPairFullSizeNsq, - NP_FULL | NP_SIZE | NP_NSQ | NP_MOLONLY | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + NP_FULL | NP_SIZE | NP_NSQ | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); typedef NPairNsq<1, 0, 1> NPairHalfSizeNsqNewtoff; NPairStyle(half/size/nsq/newtoff, @@ -43,13 +41,13 @@ NPairStyle(half/size/nsq/newtoff, typedef NPairNsq<1, 1, 1> NPairHalfSizeNsqNewton; NPairStyle(half/size/nsq/newton, NPairHalfSizeNsqNewton, - NP_HALF | NP_SIZE | NP_NSQ | NP_MOLONLY | NP_NEWTON | NP_ORTHO | NP_TRI); + NP_HALF | NP_SIZE | NP_NSQ | NP_NEWTON | NP_ORTHO | NP_TRI); // clang-format on #else -#ifndef LMP_NPAIR_Nsq_H -#define LMP_NPAIR_Nsq_H +#ifndef LMP_NPAIR_NSQ_H +#define LMP_NPAIR_NSQ_H #include "npair.h" diff --git a/src/npair_nsq_ghost.cpp b/src/npair_nsq_ghost.cpp index e9cc732e7d..5e4185f9d6 100644 --- a/src/npair_nsq_ghost.cpp +++ b/src/npair_nsq_ghost.cpp @@ -106,8 +106,8 @@ void NPairNsqGhost::build(NeighList *list) // stores ghost/ghost pairs only once // no molecular test when i = ghost atom - if (!HALF) jstart = 0; - else jstart = i + 1; + if (HALF) jstart = i + 1; + else jstart = 0; if (i < nlocal) { for (j = jstart; j < nall; j++) { @@ -155,7 +155,10 @@ void NPairNsqGhost::build(NeighList *list) delz = ztmp - x[j][2]; rsq = delx * delx + dely * dely + delz * delz; - if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; + if (HALF) { + if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; + } else { + if (rsq <= cutneighghostsq[itype][jtype]) neighptr[n++] = j; } } diff --git a/src/npair_nsq_ghost.h b/src/npair_nsq_ghost.h index 79dfce0c9c..516f0bd929 100644 --- a/src/npair_nsq_ghost.h +++ b/src/npair_nsq_ghost.h @@ -18,9 +18,9 @@ NPairStyle(full/nsq/ghost, NPairFullNsqGhost, NP_FULL | NP_NSQ | NP_NEWTON | NP_NEWTOFF | NP_GHOST | NP_ORTHO | NP_TRI); -typedef NPairNsqGhost<1> NPairHalfNsqGhostNewtoff; -NPairStyle(half/nsq/ghost/newtoff, - NPairHalfNsqGhostNewtoff, +typedef NPairNsqGhost<1> NPairHalfNsqNewtoffGhost; +NPairStyle(half/nsq/newtoff/ghost, + NPairHalfNsqNewtoffGhost, NP_HALF | NP_NSQ | NP_NEWTOFF | NP_GHOST | NP_ORTHO | NP_TRI); // clang-format on #else diff --git a/src/npair_respa_nsq.cpp b/src/npair_respa_nsq.cpp index ce73d534c8..7c816f305b 100644 --- a/src/npair_respa_nsq.cpp +++ b/src/npair_respa_nsq.cpp @@ -119,14 +119,14 @@ void NPairRespaNsq::build(NeighList *list) // loop over remaining atoms, owned and ghost - for (j = i+1; j < nall; j++) { + for (j = i + 1; j < nall; j++) { if (includegroup && !(mask[j] & bitmask)) continue; if (NEWTON) { if (j >= nlocal) { jtag = tag[j]; if (itag > jtag) { - if ((itag+jtag) % 2 == 0) continue; + if ((itag + jtag) % 2 == 0) continue; } else if (itag < jtag) { if ((itag+jtag) % 2 == 1) continue; } else { diff --git a/src/npair_skip.h b/src/npair_skip.h index 6eaf03e1c5..4e85174730 100644 --- a/src/npair_skip.h +++ b/src/npair_skip.h @@ -24,11 +24,6 @@ NPairStyle(skip/ghost, NP_SKIP | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_GHOST); - -NPairStyle(skip/half/size, - NPairSkip, - NP_SKIP | NP_SIZE | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); // clang-format on #else From b0dcaa38231ed1188f89a04ac7e20ea5f01e4bab Mon Sep 17 00:00:00 2001 From: jtclemm Date: Sat, 12 Nov 2022 16:18:11 -0700 Subject: [PATCH 008/189] Updating OPENMP package --- src/OPENMP/npair_bin_atomonly_omp.cpp | 195 ++++++++++++++ src/OPENMP/npair_bin_atomonly_omp.h | 78 ++++++ ..._ghost_omp.cpp => npair_bin_ghost_omp.cpp} | 49 +++- ..._bin_ghost_omp.h => npair_bin_ghost_omp.h} | 18 +- src/OPENMP/npair_bin_omp.cpp | 237 +++++++++++++++++ src/OPENMP/npair_bin_omp.h | 77 ++++++ src/OPENMP/npair_full_bin_atomonly_omp.cpp | 106 -------- src/OPENMP/npair_full_bin_atomonly_omp.h | 39 --- src/OPENMP/npair_full_bin_omp.cpp | 135 ---------- src/OPENMP/npair_full_bin_omp.h | 39 --- src/OPENMP/npair_full_multi_old_omp.cpp | 144 ---------- src/OPENMP/npair_full_multi_old_omp.h | 39 --- src/OPENMP/npair_full_multi_omp.cpp | 154 ----------- src/OPENMP/npair_full_multi_omp.h | 39 --- src/OPENMP/npair_full_nsq_ghost_omp.h | 39 --- src/OPENMP/npair_full_nsq_omp.cpp | 134 ---------- src/OPENMP/npair_full_nsq_omp.h | 39 --- .../npair_half_bin_atomonly_newton_omp.cpp | 126 --------- .../npair_half_bin_atomonly_newton_omp.h | 38 --- .../npair_half_bin_newtoff_ghost_omp.cpp | 174 ------------ src/OPENMP/npair_half_bin_newtoff_ghost_omp.h | 39 --- src/OPENMP/npair_half_bin_newtoff_omp.cpp | 139 ---------- src/OPENMP/npair_half_bin_newtoff_omp.h | 38 --- src/OPENMP/npair_half_bin_newton_omp.cpp | 172 ------------ src/OPENMP/npair_half_bin_newton_omp.h | 38 --- src/OPENMP/npair_half_bin_newton_tri_omp.cpp | 145 ---------- src/OPENMP/npair_half_bin_newton_tri_omp.h | 38 --- src/OPENMP/npair_half_multi_newtoff_omp.cpp | 157 ----------- src/OPENMP/npair_half_multi_newtoff_omp.h | 38 --- src/OPENMP/npair_half_multi_newton_omp.cpp | 205 --------------- src/OPENMP/npair_half_multi_newton_omp.h | 38 --- .../npair_half_multi_newton_tri_omp.cpp | 171 ------------ src/OPENMP/npair_half_multi_newton_tri_omp.h | 38 --- .../npair_half_multi_old_newtoff_omp.cpp | 146 ----------- src/OPENMP/npair_half_multi_old_newtoff_omp.h | 38 --- .../npair_half_multi_old_newton_omp.cpp | 179 ------------- src/OPENMP/npair_half_multi_old_newton_omp.h | 38 --- .../npair_half_multi_old_newton_tri_omp.cpp | 155 ----------- .../npair_half_multi_old_newton_tri_omp.h | 38 --- .../npair_half_nsq_newtoff_ghost_omp.cpp | 158 ----------- src/OPENMP/npair_half_nsq_newtoff_omp.cpp | 134 ---------- src/OPENMP/npair_half_nsq_newtoff_omp.h | 38 --- src/OPENMP/npair_half_nsq_newton_omp.cpp | 152 ----------- src/OPENMP/npair_half_nsq_newton_omp.h | 38 --- .../npair_half_respa_bin_newtoff_omp.cpp | 203 -------------- src/OPENMP/npair_half_respa_bin_newtoff_omp.h | 39 --- .../npair_half_respa_bin_newton_tri_omp.cpp | 210 --------------- .../npair_half_respa_bin_newton_tri_omp.h | 38 --- src/OPENMP/npair_half_respa_nsq_newtoff_omp.h | 39 --- .../npair_half_respa_nsq_newton_omp.cpp | 216 --------------- .../npair_half_size_bin_newtoff_omp.cpp | 151 ----------- src/OPENMP/npair_half_size_bin_newtoff_omp.h | 39 --- src/OPENMP/npair_half_size_bin_newton_omp.cpp | 187 ------------- src/OPENMP/npair_half_size_bin_newton_omp.h | 38 --- .../npair_half_size_bin_newton_tri_omp.cpp | 158 ----------- .../npair_half_size_bin_newton_tri_omp.h | 38 --- .../npair_half_size_multi_newtoff_omp.cpp | 172 ------------ .../npair_half_size_multi_newtoff_omp.h | 38 --- .../npair_half_size_multi_newton_omp.cpp | 225 ---------------- src/OPENMP/npair_half_size_multi_newton_omp.h | 38 --- .../npair_half_size_multi_newton_tri_omp.cpp | 187 ------------- .../npair_half_size_multi_newton_tri_omp.h | 38 --- .../npair_half_size_multi_old_newtoff_omp.cpp | 158 ----------- .../npair_half_size_multi_old_newtoff_omp.h | 39 --- .../npair_half_size_multi_old_newton_omp.h | 38 --- ...air_half_size_multi_old_newton_tri_omp.cpp | 166 ------------ ...npair_half_size_multi_old_newton_tri_omp.h | 38 --- .../npair_half_size_nsq_newtoff_omp.cpp | 147 ----------- src/OPENMP/npair_half_size_nsq_newtoff_omp.h | 39 --- src/OPENMP/npair_half_size_nsq_newton_omp.cpp | 166 ------------ src/OPENMP/npair_half_size_nsq_newton_omp.h | 39 --- src/OPENMP/npair_halffull_newtoff_omp.cpp | 90 ------- src/OPENMP/npair_halffull_newtoff_omp.h | 44 ---- src/OPENMP/npair_halffull_newtoff_trim_omp.h | 44 ---- src/OPENMP/npair_halffull_newton_omp.cpp | 107 -------- src/OPENMP/npair_halffull_newton_omp.h | 44 ---- src/OPENMP/npair_halffull_newton_trim_omp.cpp | 120 --------- src/OPENMP/npair_halffull_newton_trim_omp.h | 44 ---- ...ff_trim_omp.cpp => npair_halffull_omp.cpp} | 67 +++-- src/OPENMP/npair_halffull_omp.h | 107 ++++++++ src/OPENMP/npair_multi_old_omp.cpp | 243 +++++++++++++++++ src/OPENMP/npair_multi_old_omp.h | 77 ++++++ src/OPENMP/npair_multi_omp.cpp | 247 ++++++++++++++++++ src/OPENMP/npair_multi_omp.h | 75 ++++++ ..._ghost_omp.cpp => npair_nsq_ghost_omp.cpp} | 59 ++++- ...toff_ghost_omp.h => npair_nsq_ghost_omp.h} | 18 +- ...i_old_newton_omp.cpp => npair_nsq_omp.cpp} | 176 +++++++------ src/OPENMP/npair_nsq_omp.h | 66 +++++ ...newton_omp.cpp => npair_respa_bin_omp.cpp} | 134 +++++----- ...bin_newton_omp.h => npair_respa_bin_omp.h} | 22 +- ...ewtoff_omp.cpp => npair_respa_nsq_omp.cpp} | 48 +++- ...nsq_newton_omp.h => npair_respa_nsq_omp.h} | 18 +- src/npair_bin_ghost.cpp | 1 - src/npair_multi.cpp | 2 +- src/npair_multi_old.cpp | 1 - src/npair_nsq.h | 1 - src/npair_nsq_ghost.cpp | 2 +- src/npair_respa_bin.cpp | 2 +- 98 files changed, 1801 insertions(+), 7211 deletions(-) create mode 100644 src/OPENMP/npair_bin_atomonly_omp.cpp create mode 100644 src/OPENMP/npair_bin_atomonly_omp.h rename src/OPENMP/{npair_full_bin_ghost_omp.cpp => npair_bin_ghost_omp.cpp} (75%) rename src/OPENMP/{npair_full_bin_ghost_omp.h => npair_bin_ghost_omp.h} (67%) create mode 100644 src/OPENMP/npair_bin_omp.cpp create mode 100644 src/OPENMP/npair_bin_omp.h delete mode 100644 src/OPENMP/npair_full_bin_atomonly_omp.cpp delete mode 100644 src/OPENMP/npair_full_bin_atomonly_omp.h delete mode 100644 src/OPENMP/npair_full_bin_omp.cpp delete mode 100644 src/OPENMP/npair_full_bin_omp.h delete mode 100644 src/OPENMP/npair_full_multi_old_omp.cpp delete mode 100644 src/OPENMP/npair_full_multi_old_omp.h delete mode 100644 src/OPENMP/npair_full_multi_omp.cpp delete mode 100644 src/OPENMP/npair_full_multi_omp.h delete mode 100644 src/OPENMP/npair_full_nsq_ghost_omp.h delete mode 100644 src/OPENMP/npair_full_nsq_omp.cpp delete mode 100644 src/OPENMP/npair_full_nsq_omp.h delete mode 100644 src/OPENMP/npair_half_bin_atomonly_newton_omp.cpp delete mode 100644 src/OPENMP/npair_half_bin_atomonly_newton_omp.h delete mode 100644 src/OPENMP/npair_half_bin_newtoff_ghost_omp.cpp delete mode 100644 src/OPENMP/npair_half_bin_newtoff_ghost_omp.h delete mode 100644 src/OPENMP/npair_half_bin_newtoff_omp.cpp delete mode 100644 src/OPENMP/npair_half_bin_newtoff_omp.h delete mode 100644 src/OPENMP/npair_half_bin_newton_omp.cpp delete mode 100644 src/OPENMP/npair_half_bin_newton_omp.h delete mode 100644 src/OPENMP/npair_half_bin_newton_tri_omp.cpp delete mode 100644 src/OPENMP/npair_half_bin_newton_tri_omp.h delete mode 100644 src/OPENMP/npair_half_multi_newtoff_omp.cpp delete mode 100644 src/OPENMP/npair_half_multi_newtoff_omp.h delete mode 100644 src/OPENMP/npair_half_multi_newton_omp.cpp delete mode 100644 src/OPENMP/npair_half_multi_newton_omp.h delete mode 100644 src/OPENMP/npair_half_multi_newton_tri_omp.cpp delete mode 100644 src/OPENMP/npair_half_multi_newton_tri_omp.h delete mode 100644 src/OPENMP/npair_half_multi_old_newtoff_omp.cpp delete mode 100644 src/OPENMP/npair_half_multi_old_newtoff_omp.h delete mode 100644 src/OPENMP/npair_half_multi_old_newton_omp.cpp delete mode 100644 src/OPENMP/npair_half_multi_old_newton_omp.h delete mode 100644 src/OPENMP/npair_half_multi_old_newton_tri_omp.cpp delete mode 100644 src/OPENMP/npair_half_multi_old_newton_tri_omp.h delete mode 100644 src/OPENMP/npair_half_nsq_newtoff_ghost_omp.cpp delete mode 100644 src/OPENMP/npair_half_nsq_newtoff_omp.cpp delete mode 100644 src/OPENMP/npair_half_nsq_newtoff_omp.h delete mode 100644 src/OPENMP/npair_half_nsq_newton_omp.cpp delete mode 100644 src/OPENMP/npair_half_nsq_newton_omp.h delete mode 100644 src/OPENMP/npair_half_respa_bin_newtoff_omp.cpp delete mode 100644 src/OPENMP/npair_half_respa_bin_newtoff_omp.h delete mode 100644 src/OPENMP/npair_half_respa_bin_newton_tri_omp.cpp delete mode 100644 src/OPENMP/npair_half_respa_bin_newton_tri_omp.h delete mode 100644 src/OPENMP/npair_half_respa_nsq_newtoff_omp.h delete mode 100644 src/OPENMP/npair_half_respa_nsq_newton_omp.cpp delete mode 100644 src/OPENMP/npair_half_size_bin_newtoff_omp.cpp delete mode 100644 src/OPENMP/npair_half_size_bin_newtoff_omp.h delete mode 100644 src/OPENMP/npair_half_size_bin_newton_omp.cpp delete mode 100644 src/OPENMP/npair_half_size_bin_newton_omp.h delete mode 100644 src/OPENMP/npair_half_size_bin_newton_tri_omp.cpp delete mode 100644 src/OPENMP/npair_half_size_bin_newton_tri_omp.h delete mode 100644 src/OPENMP/npair_half_size_multi_newtoff_omp.cpp delete mode 100644 src/OPENMP/npair_half_size_multi_newtoff_omp.h delete mode 100644 src/OPENMP/npair_half_size_multi_newton_omp.cpp delete mode 100644 src/OPENMP/npair_half_size_multi_newton_omp.h delete mode 100644 src/OPENMP/npair_half_size_multi_newton_tri_omp.cpp delete mode 100644 src/OPENMP/npair_half_size_multi_newton_tri_omp.h delete mode 100644 src/OPENMP/npair_half_size_multi_old_newtoff_omp.cpp delete mode 100644 src/OPENMP/npair_half_size_multi_old_newtoff_omp.h delete mode 100644 src/OPENMP/npair_half_size_multi_old_newton_omp.h delete mode 100644 src/OPENMP/npair_half_size_multi_old_newton_tri_omp.cpp delete mode 100644 src/OPENMP/npair_half_size_multi_old_newton_tri_omp.h delete mode 100644 src/OPENMP/npair_half_size_nsq_newtoff_omp.cpp delete mode 100644 src/OPENMP/npair_half_size_nsq_newtoff_omp.h delete mode 100644 src/OPENMP/npair_half_size_nsq_newton_omp.cpp delete mode 100644 src/OPENMP/npair_half_size_nsq_newton_omp.h delete mode 100644 src/OPENMP/npair_halffull_newtoff_omp.cpp delete mode 100644 src/OPENMP/npair_halffull_newtoff_omp.h delete mode 100644 src/OPENMP/npair_halffull_newtoff_trim_omp.h delete mode 100644 src/OPENMP/npair_halffull_newton_omp.cpp delete mode 100644 src/OPENMP/npair_halffull_newton_omp.h delete mode 100644 src/OPENMP/npair_halffull_newton_trim_omp.cpp delete mode 100644 src/OPENMP/npair_halffull_newton_trim_omp.h rename src/OPENMP/{npair_halffull_newtoff_trim_omp.cpp => npair_halffull_omp.cpp} (59%) create mode 100644 src/OPENMP/npair_halffull_omp.h create mode 100644 src/OPENMP/npair_multi_old_omp.cpp create mode 100644 src/OPENMP/npair_multi_old_omp.h create mode 100644 src/OPENMP/npair_multi_omp.cpp create mode 100644 src/OPENMP/npair_multi_omp.h rename src/OPENMP/{npair_full_nsq_ghost_omp.cpp => npair_nsq_ghost_omp.cpp} (71%) rename src/OPENMP/{npair_half_nsq_newtoff_ghost_omp.h => npair_nsq_ghost_omp.h} (64%) rename src/OPENMP/{npair_half_size_multi_old_newton_omp.cpp => npair_nsq_omp.cpp} (51%) create mode 100644 src/OPENMP/npair_nsq_omp.h rename src/OPENMP/{npair_half_respa_bin_newton_omp.cpp => npair_respa_bin_omp.cpp} (67%) rename src/OPENMP/{npair_half_respa_bin_newton_omp.h => npair_respa_bin_omp.h} (55%) rename src/OPENMP/{npair_half_respa_nsq_newtoff_omp.cpp => npair_respa_nsq_omp.cpp} (81%) rename src/OPENMP/{npair_half_respa_nsq_newton_omp.h => npair_respa_nsq_omp.h} (63%) diff --git a/src/OPENMP/npair_bin_atomonly_omp.cpp b/src/OPENMP/npair_bin_atomonly_omp.cpp new file mode 100644 index 0000000000..2fe3baa014 --- /dev/null +++ b/src/OPENMP/npair_bin_atomonly_omp.cpp @@ -0,0 +1,195 @@ +// 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 "npair_bin_atomonly_omp.h" + +#include "atom.h" +#include "error.h" +#include "my_page.h" +#include "neigh_list.h" +#include "npair_omp.h" + +#include "omp_compat.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +template +NPairBinAtomonlyOmp::NPairBinAtomonlyOmp(LAMMPS *lmp) : NPair(lmp) {} + +/* ---------------------------------------------------------------------- + Full: + binned neighbor list construction for all neighbors + every neighbor pair appears in list of both atoms i and j + Half + Newtoff: + binned neighbor list construction with partial Newton's 3rd law + each owned atom i checks own bin and other bins in stencil + pair stored once if i,j are both owned and i < j + pair stored by me if j is ghost (also stored by proc owning j) + Half + Newton: + binned neighbor list construction with full Newton's 3rd law + each owned atom i checks its own bin and other bins in Newton stencil + every pair stored exactly once by some processor +------------------------------------------------------------------------- */ + +template +void NPairBinAtomonlyOmp::build(NeighList *list) +{ + const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; + + NPAIR_OMP_INIT; +#if defined(_OPENMP) +#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) +#endif + NPAIR_OMP_SETUP(nlocal); + + int i,j,jh,k,n,itype,jtype,ibin,bin_start; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + double radsum,cut,cutsq; + int *neighptr; + + double **x = atom->x; + double *radius = atom->radius; + int *type = atom->type; + int *mask = atom->mask; + tagint *molecule = atom->molecule; + + int history = list->history; + int mask_history = 1 << HISTBITS; + + int *ilist = list->ilist; + int *numneigh = list->numneigh; + int **firstneigh = list->firstneigh; + + // each thread has its own page allocator + MyPage &ipage = list->ipage[tid]; + ipage.reset(); + + // loop over owned atoms, storing neighbors + + for (i = ifrom; i < ito; i++) { + + n = 0; + neighptr = ipage.vget(); + + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + + // loop over all atoms in surrounding bins in stencil including self + // skip i = j + + ibin = atom2bin[i]; + + for (k = 0; k < nstencil; k++) { + bin_start = binhead[ibin+stencil[k]]; + if (stencil[k] == 0) { + if (HALF && NEWTON && (!TRI)) { + // Half neighbor list, newton on, orthonormal + // loop over rest of atoms in i's bin, ghosts are at end of linked list + bin_start = bins[i]; + } + } + + for (j = bin_start; j >= 0; j = bins[j]) { + if (!HALF) { + // Full neighbor list + // only skip i = j + if (i == j) continue; + } else if (!NEWTON) { + // Half neighbor list, newton off + // only store pair if i < j + // stores own/own pairs only once + // stores own/ghost pairs on both procs + if (j <= i) continue; + } else if (TRI) { + // Half neighbor list, newton on, triclinic + // pairs for atoms j "below" i are excluded + // below = lower z or (equal z and lower y) or (equal zy and lower x) + // (equal zyx and j <= i) + // latter excludes self-self interaction but allows superposed atoms + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp) { + if (x[j][0] < xtmp) continue; + if (x[j][0] == xtmp && j <= i) continue; + } + } + } else { + // Half neighbor list, newton on, orthonormal + // store every pair for every bin in stencil,except for i's bin + + if (stencil[k] == 0) { + // if j is owned atom, store it, since j is beyond i in linked list + // if j is ghost, only store if j coords are "above and to the "right" of i + if (j >= nlocal) { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + } + } + } + } + + jtype = type[j]; + if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (SIZE) { + radsum = radius[i] + radius[j]; + cut = radsum + skin; + cutsq = cut * cut; + + if (rsq <= cutsq) { + jh = j; + if (history && rsq < radsum * radsum) + jh = jh ^ mask_history; + neighptr[n++] = jh; + } + } else { + if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; + } + } + } + + ilist[i] = i; + firstneigh[i] = neighptr; + numneigh[i] = n; + ipage.vgot(n); + if (ipage.status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + } + NPAIR_OMP_CLOSE; + list->inum = nlocal; + if (!HALF) list->gnum = 0; +} + +namespace LAMMPS_NS { +template class NPairBinAtomonlyOmp<0,1,0,0>; +template class NPairBinAtomonlyOmp<1,0,0,0>; +template class NPairBinAtomonlyOmp<1,1,0,0>; +template class NPairBinAtomonlyOmp<1,1,1,0>; +template class NPairBinAtomonlyOmp<0,1,0,1>; +template class NPairBinAtomonlyOmp<1,0,0,1>; +template class NPairBinAtomonlyOmp<1,1,0,1>; +template class NPairBinAtomonlyOmp<1,1,1,1>; +} diff --git a/src/OPENMP/npair_bin_atomonly_omp.h b/src/OPENMP/npair_bin_atomonly_omp.h new file mode 100644 index 0000000000..c488486898 --- /dev/null +++ b/src/OPENMP/npair_bin_atomonly_omp.h @@ -0,0 +1,78 @@ +/* -*- 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 NPAIR_CLASS +// clang-format off +typedef NPairBinAtomonlyOmp<0, 1, 0, 0> NPairFullBinAtomonlyOmp; +NPairStyle(full/bin/atomonly/omp, + NPairFullBinAtomonlyOmp, + NP_FULL | NP_BIN | NP_OMP | NP_ATOMONLY | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBinAtomonlyOmp<1, 0, 0, 0> NPairHalfBinNewtoffAtomonlyOmp; +NPairStyle(half/bin/newtoff/atomonly/omp, + NPairHalfBinNewtoffAtomonlyOmp, + NP_HALF | NP_BIN | NP_OMP | NP_ATOMONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBinAtomonlyOmp<1, 1, 0, 0> NPairHalfBinNewtonAtomonlyOmp; +NPairStyle(half/bin/newton/atomonly/omp, + NPairHalfBinNewtonAtomonlyOmp, + NP_HALF | NP_BIN | NP_OMP | NP_ATOMONLY | NP_NEWTON | NP_ORTHO); + +typedef NPairBinAtomonlyOmp<1, 1, 1, 0> NPairHalfBinNewtonTriAtomonlyOmp; +NPairStyle(half/bin/newton/tri/atomonly/omp, + NPairHalfBinNewtonTriAtomonlyOmp, + NP_HALF | NP_BIN | NP_OMP | NP_ATOMONLY | NP_NEWTON | NP_TRI); + +typedef NPairBinAtomonlyOmp<0, 1, 0, 1> NPairFullSizeBinAtomonlyOmp; +NPairStyle(full/size/bin/atomonly/omp, + NPairFullSizeBinAtomonlyOmp, + NP_FULL | NP_SIZE | NP_BIN | NP_OMP | NP_ATOMONLY | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBinAtomonlyOmp<1, 0, 0, 1> NPairHalfSizeBinNewtoffAtomonlyOmp; +NPairStyle(half/size/bin/newtoff/atomonly/omp, + NPairHalfSizeBinNewtoffAtomonlyOmp, + NP_HALF | NP_SIZE | NP_BIN | NP_OMP | NP_ATOMONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBinAtomonlyOmp<1, 1, 0, 1> NPairHalfSizeBinNewtonAtomonlyOmp; +NPairStyle(half/size/bin/newton/atomonly/omp, + NPairHalfSizeBinNewtonAtomonlyOmp, + NP_HALF | NP_SIZE | NP_BIN | NP_OMP | NP_ATOMONLY | NP_NEWTON | NP_ORTHO); + +typedef NPairBinAtomonlyOmp<1, 1, 1, 1> NPairHalfSizeBinNewtonTriAtomonlyOmp; +NPairStyle(half/size/bin/newton/tri/atomonly/omp, + NPairHalfSizeBinNewtonTriAtomonlyOmp, + NP_HALF | NP_SIZE | NP_BIN | NP_OMP | NP_ATOMONLY | NP_NEWTON | NP_TRI); +// clang-format on +#else + + +#ifndef LMP_NPAIR_BIN_ATOMONLY_OMP_H +#define LMP_NPAIR_BIN_ATOMONLY_OMP_H + +#include "npair.h" + +namespace LAMMPS_NS { + +template +class NPairBinAtomonlyOmp : public NPair { + public: + NPairBinAtomonlyOmp(class LAMMPS *); + void build(class NeighList *) override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif diff --git a/src/OPENMP/npair_full_bin_ghost_omp.cpp b/src/OPENMP/npair_bin_ghost_omp.cpp similarity index 75% rename from src/OPENMP/npair_full_bin_ghost_omp.cpp rename to src/OPENMP/npair_bin_ghost_omp.cpp index 5723a418f5..8f3105dc75 100644 --- a/src/OPENMP/npair_full_bin_ghost_omp.cpp +++ b/src/OPENMP/npair_bin_ghost_omp.cpp @@ -13,7 +13,7 @@ ------------------------------------------------------------------------- */ #include "omp_compat.h" -#include "npair_full_bin_ghost_omp.h" +#include "npair_bin_ghost_omp.h" #include "npair_omp.h" #include "neigh_list.h" #include "atom.h" @@ -27,15 +27,25 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -NPairFullBinGhostOmp::NPairFullBinGhostOmp(LAMMPS *lmp) : NPair(lmp) {} +template +NPairBinGhostOmp::NPairBinGhostOmp(LAMMPS *lmp) : NPair(lmp) {} /* ---------------------------------------------------------------------- - binned neighbor list construction for all neighbors - include neighbors of ghost atoms, but no "special neighbors" for ghosts - every neighbor pair appears in list of both atoms i and j + Full: + binned neighbor list construction for all neighbors + include neighbors of ghost atoms, but no "special neighbors" for ghosts + every neighbor pair appears in list of both atoms i and j + Half + Newtoff: + binned neighbor list construction with partial Newton's 3rd law + include neighbors of ghost atoms, but no "special neighbors" for ghosts + owned and ghost atoms check own bin and other bins in stencil + pair stored once if i,j are both owned and i < j + pair stored by me if i owned and j ghost (also stored by proc owning j) + pair stored once if i,j are both ghost and i < j ------------------------------------------------------------------------- */ -void NPairFullBinGhostOmp::build(NeighList *list) +template +void NPairBinGhostOmp::build(NeighList *list) { const int nlocal = atom->nlocal; const int nall = nlocal + atom->nghost; @@ -48,7 +58,7 @@ void NPairFullBinGhostOmp::build(NeighList *list) #endif NPAIR_OMP_SETUP(nall); - int i,j,k,n,itype,jtype,ibin,which,imol,iatom; + int i,j,k,n,itype,jtype,ibin,bin_start,which,imol,iatom; tagint tagprev; double xtmp,ytmp,ztmp,delx,dely,delz,rsq; int xbin,ybin,zbin,xbin2,ybin2,zbin2; @@ -93,14 +103,24 @@ void NPairFullBinGhostOmp::build(NeighList *list) // loop over all atoms in surrounding bins in stencil including self // when i is a ghost atom, must check if stencil bin is out of bounds - // skip i = j // no molecular test when i = ghost atom if (i < nlocal) { ibin = atom2bin[i]; for (k = 0; k < nstencil; k++) { for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (i == j) continue; + if (HALF) { + // Half neighbor list, newton off + // only store pair if i < j + // stores own/own pairs only once + // stores own/ghost pairs on both procs + // stores ghost/ghost pairs only once + if (j <= i) continue; + } else { + // Full neighbor list + // only skip i = j + if (i == j) continue; + } jtype = type[j]; if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; @@ -138,7 +158,11 @@ void NPairFullBinGhostOmp::build(NeighList *list) ybin2 < 0 || ybin2 >= mbiny || zbin2 < 0 || zbin2 >= mbinz) continue; for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (i == j) continue; + if (HALF) { + if (j <= i) continue; + } else { + if (i == j) continue; + } jtype = type[j]; if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; @@ -164,3 +188,8 @@ void NPairFullBinGhostOmp::build(NeighList *list) list->inum = nlocal; list->gnum = nall - nlocal; } + +namespace LAMMPS_NS { +template class NPairBinGhostOmp<0>; +template class NPairBinGhostOmp<1>; +} diff --git a/src/OPENMP/npair_full_bin_ghost_omp.h b/src/OPENMP/npair_bin_ghost_omp.h similarity index 67% rename from src/OPENMP/npair_full_bin_ghost_omp.h rename to src/OPENMP/npair_bin_ghost_omp.h index 6de134dcf8..df18886e91 100644 --- a/src/OPENMP/npair_full_bin_ghost_omp.h +++ b/src/OPENMP/npair_bin_ghost_omp.h @@ -13,23 +13,29 @@ #ifdef NPAIR_CLASS // clang-format off +typedef NPairBinGhostOmp<0> NPairFullBinGhostOmp; NPairStyle(full/bin/ghost/omp, NPairFullBinGhostOmp, - NP_FULL | NP_BIN | NP_GHOST | NP_OMP | NP_NEWTON | NP_NEWTOFF | - NP_ORTHO | NP_TRI); + NP_FULL | NP_BIN | NP_GHOST | NP_OMP | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBinGhostOmp<1> NPairHalfBinNewtoffGhostOmp; +NPairStyle(half/bin/newtoff/ghost/omp, + NPairHalfBinNewtoffGhostOmp, + NP_HALF | NP_BIN | NP_GHOST | NP_OMP | NP_NEWTOFF | NP_ORTHO | NP_TRI); // clang-format on #else -#ifndef LMP_NPAIR_FULL_BIN_GHOST_OMP_H -#define LMP_NPAIR_FULL_BIN_GHOST_OMP_H +#ifndef LMP_NPAIR_BIN_GHOST_OMP_H +#define LMP_NPAIR_BIN_GHOST_OMP_H #include "npair.h" namespace LAMMPS_NS { -class NPairFullBinGhostOmp : public NPair { +template +class NPairBinGhostOmp : public NPair { public: - NPairFullBinGhostOmp(class LAMMPS *); + NPairBinGhostOmp(class LAMMPS *); void build(class NeighList *) override; }; diff --git a/src/OPENMP/npair_bin_omp.cpp b/src/OPENMP/npair_bin_omp.cpp new file mode 100644 index 0000000000..7029f71df7 --- /dev/null +++ b/src/OPENMP/npair_bin_omp.cpp @@ -0,0 +1,237 @@ +// 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 "omp_compat.h" +#include "npair_bin_omp.h" +#include "npair_omp.h" +#include "neigh_list.h" +#include "atom.h" +#include "atom_vec.h" +#include "molecule.h" +#include "domain.h" +#include "my_page.h" +#include "error.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +template +NPairBinOmp::NPairBinOmp(LAMMPS *lmp) : NPair(lmp) {} + +/* ---------------------------------------------------------------------- + Full: + binned neighbor list construction for all neighbors + every neighbor pair appears in list of both atoms i and j + Half + Newtoff: + binned neighbor list construction with partial Newton's 3rd law + each owned atom i checks own bin and other bins in stencil + pair stored once if i,j are both owned and i < j + pair stored by me if j is ghost (also stored by proc owning j) + Half + Newton: + binned neighbor list construction with full Newton's 3rd law + each owned atom i checks its own bin and other bins in Newton stencil + every pair stored exactly once by some processor +------------------------------------------------------------------------- */ + +template +void NPairBinOmp::build(NeighList *list) +{ + const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; + const int molecular = atom->molecular; + const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; + + NPAIR_OMP_INIT; +#if defined(_OPENMP) +#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) +#endif + NPAIR_OMP_SETUP(nlocal); + + int i,j,jh,k,n,itype,jtype,ibin,bin_start,which,imol,iatom; + tagint tagprev; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + double radsum,cut,cutsq; + int *neighptr; + + double **x = atom->x; + double *radius = atom->radius; + int *type = atom->type; + int *mask = atom->mask; + tagint *tag = atom->tag; + tagint *molecule = atom->molecule; + tagint **special = atom->special; + int **nspecial = atom->nspecial; + int *molindex = atom->molindex; + int *molatom = atom->molatom; + Molecule **onemols = atom->avec->onemols; + + int history = list->history; + int mask_history = 1 << HISTBITS; + + int *ilist = list->ilist; + int *numneigh = list->numneigh; + int **firstneigh = list->firstneigh; + + // each thread has its own page allocator + MyPage &ipage = list->ipage[tid]; + ipage.reset(); + + // loop over owned atoms, storing neighbors + + for (i = ifrom; i < ito; i++) { + + n = 0; + neighptr = ipage.vget(); + + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + if (moltemplate) { + imol = molindex[i]; + iatom = molatom[i]; + tagprev = tag[i] - iatom - 1; + } + + // loop over all atoms in surrounding bins in stencil including self + // skip i = j + + ibin = atom2bin[i]; + + for (k = 0; k < nstencil; k++) { + bin_start = binhead[ibin+stencil[k]]; + if (stencil[k] == 0) { + if (HALF && NEWTON && (!TRI)) { + // Half neighbor list, newton on, orthonormal + // loop over rest of atoms in i's bin, ghosts are at end of linked list + bin_start = bins[i]; + } + } + + for (j = bin_start; j >= 0; j = bins[j]) { + if (!HALF) { + // Full neighbor list + // only skip i = j + if (i == j) continue; + } else if (!NEWTON) { + // Half neighbor list, newton off + // only store pair if i < j + // stores own/own pairs only once + // stores own/ghost pairs on both procs + if (j <= i) continue; + } else if (TRI) { + // Half neighbor list, newton on, triclinic + // pairs for atoms j "below" i are excluded + // below = lower z or (equal z and lower y) or (equal zy and lower x) + // (equal zyx and j <= i) + // latter excludes self-self interaction but allows superposed atoms + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp) { + if (x[j][0] < xtmp) continue; + if (x[j][0] == xtmp && j <= i) continue; + } + } + } else { + // Half neighbor list, newton on, orthonormal + // store every pair for every bin in stencil,except for i's bin + + if (stencil[k] == 0) { + // if j is owned atom, store it, since j is beyond i in linked list + // if j is ghost, only store if j coords are "above and to the "right" of i + if (j >= nlocal) { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + } + } + } + } + + jtype = type[j]; + if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx * delx + dely * dely + delz * delz; + + if (SIZE) { + radsum = radius[i] + radius[j]; + cut = radsum + skin; + cutsq = cut * cut; + + if (rsq <= cutsq) { + jh = j; + if (history && rsq < radsum * radsum) + jh = jh ^ mask_history; + + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = jh; + else if (domain->minimum_image_check(delx,dely,delz)) + neighptr[n++] = jh; + else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); + } else neighptr[n++] = jh; + } + } else { + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (domain->minimum_image_check(delx,dely,delz)) + neighptr[n++] = j; + else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); + } else neighptr[n++] = j; + } + } + } + } + + ilist[i] = i; + firstneigh[i] = neighptr; + numneigh[i] = n; + ipage.vgot(n); + if (ipage.status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + } + NPAIR_OMP_CLOSE; + list->inum = nlocal; + if (!HALF) list->gnum = 0; +} + +namespace LAMMPS_NS { +template class NPairBinOmp<0,1,0,0>; +template class NPairBinOmp<1,0,0,0>; +template class NPairBinOmp<1,1,0,0>; +template class NPairBinOmp<1,1,1,0>; +template class NPairBinOmp<0,1,0,1>; +template class NPairBinOmp<1,0,0,1>; +template class NPairBinOmp<1,1,0,1>; +template class NPairBinOmp<1,1,1,1>; +} diff --git a/src/OPENMP/npair_bin_omp.h b/src/OPENMP/npair_bin_omp.h new file mode 100644 index 0000000000..595b789d92 --- /dev/null +++ b/src/OPENMP/npair_bin_omp.h @@ -0,0 +1,77 @@ +/* -*- 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 NPAIR_CLASS +// clang-format off +typedef NPairBinOmp<0, 1, 0, 0> NPairFullBinOmp; +NPairStyle(full/bin/omp, + NPairFullBinOmp, + NP_FULL | NP_BIN | NP_OMP | NP_MOLONLY | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBinOmp<1, 0, 0, 0> NPairHalfBinNewtoffOmp; +NPairStyle(half/bin/newtoff/omp, + NPairHalfBinNewtoffOmp, + NP_HALF | NP_BIN | NP_OMP | NP_MOLONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBinOmp<1, 1, 0, 0> NPairHalfBinNewtonOmp; +NPairStyle(half/bin/newton/omp, + NPairHalfBinNewtonOmp, + NP_HALF | NP_BIN | NP_OMP | NP_MOLONLY | NP_NEWTON | NP_ORTHO); + +typedef NPairBinOmp<1, 1, 1, 0> NPairHalfBinNewtonTriOmp; +NPairStyle(half/bin/newton/tri/omp, + NPairHalfBinNewtonTriOmp, + NP_HALF | NP_BIN | NP_OMP | NP_MOLONLY | NP_NEWTON | NP_TRI); + +typedef NPairBinOmp<0, 1, 0, 1> NPairFullSizeBinOmp; +NPairStyle(full/size/bin/omp, + NPairFullSizeBinOmp, + NP_FULL | NP_SIZE | NP_BIN | NP_OMP | NP_MOLONLY | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBinOmp<1, 0, 0, 1> NPairHalfSizeBinNewtoffOmp; +NPairStyle(half/size/bin/newtoff/omp, + NPairHalfSizeBinNewtoffOmp, + NP_HALF | NP_SIZE | NP_BIN | NP_OMP | NP_MOLONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBinOmp<1, 1, 0, 1> NPairHalfSizeBinNewtonOmp; +NPairStyle(half/size/bin/newton/omp, + NPairHalfSizeBinNewtonOmp, + NP_HALF | NP_SIZE | NP_BIN | NP_OMP | NP_MOLONLY | NP_NEWTON | NP_ORTHO); + +typedef NPairBinOmp<1, 1, 1, 1> NPairHalfSizeBinNewtonTriOmp; +NPairStyle(half/size/bin/newton/tri/omp, + NPairHalfSizeBinNewtonTriOmp, + NP_HALF | NP_SIZE | NP_BIN | NP_OMP | NP_MOLONLY | NP_NEWTON | NP_TRI); +// clang-format on +#else + +#ifndef LMP_NPAIR_BIN_OMP_H +#define LMP_NPAIR_BIN_OMP_H + +#include "npair.h" + +namespace LAMMPS_NS { + +template +class NPairBinOmp : public NPair { + public: + NPairBinOmp(class LAMMPS *); + void build(class NeighList *) override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif diff --git a/src/OPENMP/npair_full_bin_atomonly_omp.cpp b/src/OPENMP/npair_full_bin_atomonly_omp.cpp deleted file mode 100644 index 0a37cca287..0000000000 --- a/src/OPENMP/npair_full_bin_atomonly_omp.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// 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 "npair_full_bin_atomonly_omp.h" - -#include "atom.h" -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" -#include "npair_omp.h" - -#include "omp_compat.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairFullBinAtomonlyOmp::NPairFullBinAtomonlyOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction for all neighbors - every neighbor pair appears in list of both atoms i and j -------------------------------------------------------------------------- */ - -void NPairFullBinAtomonlyOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,k,n,itype,jtype,ibin; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *molecule = atom->molecule; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - // loop over owned atoms, storing neighbors - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - - // loop over all atoms in surrounding bins in stencil including self - // skip i = j - - ibin = atom2bin[i]; - - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (i == j) continue; - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; - list->gnum = 0; -} diff --git a/src/OPENMP/npair_full_bin_atomonly_omp.h b/src/OPENMP/npair_full_bin_atomonly_omp.h deleted file mode 100644 index 50b1aa753c..0000000000 --- a/src/OPENMP/npair_full_bin_atomonly_omp.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(full/bin/atomonly/omp, - NPairFullBinAtomonlyOmp, - NP_FULL | NP_BIN | NP_ATOMONLY | NP_OMP | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_FULL_BIN_ATOMONLY_OMP_H -#define LMP_NPAIR_FULL_BIN_ATOMONLY_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairFullBinAtomonlyOmp : public NPair { - public: - NPairFullBinAtomonlyOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_full_bin_omp.cpp b/src/OPENMP/npair_full_bin_omp.cpp deleted file mode 100644 index 94668002a9..0000000000 --- a/src/OPENMP/npair_full_bin_omp.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// 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 "omp_compat.h" -#include "npair_full_bin_omp.h" -#include "npair_omp.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairFullBinOmp::NPairFullBinOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction for all neighbors - every neighbor pair appears in list of both atoms i and j -------------------------------------------------------------------------- */ - -void NPairFullBinOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,k,n,itype,jtype,ibin,which,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - // loop over owned atoms, storing neighbors - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in surrounding bins in stencil including self - // skip i = j - - ibin = atom2bin[i]; - - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (i == j) continue; - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; - list->gnum = 0; -} diff --git a/src/OPENMP/npair_full_bin_omp.h b/src/OPENMP/npair_full_bin_omp.h deleted file mode 100644 index 333025a1fb..0000000000 --- a/src/OPENMP/npair_full_bin_omp.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(full/bin/omp, - NPairFullBinOmp, - NP_FULL | NP_BIN | NP_OMP | NP_NEWTON | NP_NEWTOFF | - NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_FULL_BIN_OMP_H -#define LMP_NPAIR_FULL_BIN_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairFullBinOmp : public NPair { - public: - NPairFullBinOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_full_multi_old_omp.cpp b/src/OPENMP/npair_full_multi_old_omp.cpp deleted file mode 100644 index f0ed6360ab..0000000000 --- a/src/OPENMP/npair_full_multi_old_omp.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* ---------------------------------------------------------------------- - 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 "npair_full_multi_old_omp.h" -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neigh_list.h" -#include "npair_omp.h" -#include "omp_compat.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairFullMultiOldOmp::NPairFullMultiOldOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction for all neighbors - multi-type stencil is itype dependent and is distance checked - every neighbor pair appears in list of both atoms i and j -------------------------------------------------------------------------- */ - -void NPairFullMultiOldOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i, j, k, n, itype, jtype, ibin, which, ns, imol, iatom; - tagint tagprev; - double xtmp, ytmp, ztmp, delx, dely, delz, rsq; - int *neighptr, *s; - double *cutsq, *distsq; - - // loop over each atom, storing neighbors - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in other bins in stencil, including self - // skip if i,j neighbor cutoff is less than bin distance - // skip i = j - - ibin = atom2bin[i]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - cutsq = cutneighsq[itype]; - ns = nstencil_multi_old[itype]; - for (k = 0; k < ns; k++) { - for (j = binhead[ibin + s[k]]; j >= 0; j = bins[j]) { - jtype = type[j]; - if (cutsq[jtype] < distsq[k]) continue; - if (i == j) continue; - - if (exclude && exclusion(i, j, itype, jtype, mask, molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx * delx + dely * dely + delz * delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i], nspecial[i], tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], onemols[imol]->nspecial[iatom], - tag[j] - tagprev); - else - which = 0; - if (which == 0) - neighptr[n++] = j; - else if (domain->minimum_image_check(delx, dely, delz)) - neighptr[n++] = j; - else if (which > 0) - neighptr[n++] = j ^ (which << SBBITS); - } else - neighptr[n++] = j; - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; - list->gnum = 0; -} diff --git a/src/OPENMP/npair_full_multi_old_omp.h b/src/OPENMP/npair_full_multi_old_omp.h deleted file mode 100644 index 5d9f4c2f88..0000000000 --- a/src/OPENMP/npair_full_multi_old_omp.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(full/multi/old/omp, - NPairFullMultiOldOmp, - NP_FULL | NP_MULTI_OLD | NP_OMP | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_FULL_MULTI_OLD_OMP_H -#define LMP_NPAIR_FULL_MULTI_OLD_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairFullMultiOldOmp : public NPair { - public: - NPairFullMultiOldOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_full_multi_omp.cpp b/src/OPENMP/npair_full_multi_omp.cpp deleted file mode 100644 index 1e39838381..0000000000 --- a/src/OPENMP/npair_full_multi_omp.cpp +++ /dev/null @@ -1,154 +0,0 @@ -// 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 "omp_compat.h" -#include "npair_full_multi_omp.h" -#include "npair_omp.h" -#include "neighbor.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairFullMultiOmp::NPairFullMultiOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction for all neighbors - multi stencil is icollection-jcollection dependent - every neighbor pair appears in list of both atoms i and j -------------------------------------------------------------------------- */ - -void NPairFullMultiOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,k,n,itype,jtype,icollection,jcollection,ibin,jbin,which,ns,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*s; - int js; - - // loop over each atom, storing neighbors - - int *collection = neighbor->collection; - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - icollection = collection[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - ibin = atom2bin[i]; - - // loop through stencils for all collections - for (jcollection = 0; jcollection < ncollections; jcollection++) { - - // if same collection use own bin - if (icollection == jcollection) jbin = ibin; - else jbin = coord2bin(x[i], jcollection); - - // loop over all atoms in surrounding bins in stencil including self - // skip i = j - // use full stencil for all collection combinations - - s = stencil_multi[icollection][jcollection]; - ns = nstencil_multi[icollection][jcollection]; - - for (k = 0; k < ns; k++) { - js = binhead_multi[jcollection][jbin + s[k]]; - for (j = js; j >= 0; j = bins[j]) { - if (i == j) continue; - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; - list->gnum = 0; -} diff --git a/src/OPENMP/npair_full_multi_omp.h b/src/OPENMP/npair_full_multi_omp.h deleted file mode 100644 index 0d71bf7bc6..0000000000 --- a/src/OPENMP/npair_full_multi_omp.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(full/multi/omp, - NPairFullMultiOmp, - NP_FULL | NP_MULTI | NP_OMP | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_FULL_MULTI_OMP_H -#define LMP_NPAIR_FULL_MULTI_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairFullMultiOmp : public NPair { - public: - NPairFullMultiOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_full_nsq_ghost_omp.h b/src/OPENMP/npair_full_nsq_ghost_omp.h deleted file mode 100644 index 448354d4ba..0000000000 --- a/src/OPENMP/npair_full_nsq_ghost_omp.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(full/nsq/ghost/omp, - NPairFullNsqGhostOmp, - NP_FULL | NP_NSQ | NP_GHOST | NP_OMP | NP_NEWTON | NP_NEWTOFF | - NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_FULL_NSQ_GHOST_OMP_H -#define LMP_NPAIR_FULL_NSQ_GHOST_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairFullNsqGhostOmp : public NPair { - public: - NPairFullNsqGhostOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_full_nsq_omp.cpp b/src/OPENMP/npair_full_nsq_omp.cpp deleted file mode 100644 index 6349906771..0000000000 --- a/src/OPENMP/npair_full_nsq_omp.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// 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 "omp_compat.h" -#include "npair_full_nsq_omp.h" -#include "npair_omp.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "group.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairFullNsqOmp::NPairFullNsqOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - N^2 search for all neighbors - every neighbor pair appears in list of both atoms i and j -------------------------------------------------------------------------- */ - -void NPairFullNsqOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int bitmask = (includegroup) ? group->bitmask[includegroup] : 0; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,n,itype,jtype,which,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int nall = atom->nlocal + atom->nghost; - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - // loop over owned atoms, storing neighbors - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms, owned and ghost - // skip i = j - - for (j = 0; j < nall; j++) { - if (includegroup && !(mask[j] & bitmask)) continue; - if (i == j) continue; - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; - list->gnum = 0; -} diff --git a/src/OPENMP/npair_full_nsq_omp.h b/src/OPENMP/npair_full_nsq_omp.h deleted file mode 100644 index 53e913a18c..0000000000 --- a/src/OPENMP/npair_full_nsq_omp.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(full/nsq/omp, - NPairFullNsqOmp, - NP_FULL | NP_NSQ | NP_OMP | NP_NEWTON | NP_NEWTOFF | - NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_FULL_NSQ_OMP_H -#define LMP_NPAIR_FULL_NSQ_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairFullNsqOmp : public NPair { - public: - NPairFullNsqOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_bin_atomonly_newton_omp.cpp b/src/OPENMP/npair_half_bin_atomonly_newton_omp.cpp deleted file mode 100644 index 1bc1199628..0000000000 --- a/src/OPENMP/npair_half_bin_atomonly_newton_omp.cpp +++ /dev/null @@ -1,126 +0,0 @@ -// 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 "npair_half_bin_atomonly_newton_omp.h" - -#include "atom.h" -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" -#include "npair_omp.h" - -#include "omp_compat.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfBinAtomonlyNewtonOmp::NPairHalfBinAtomonlyNewtonOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with full Newton's 3rd law - each owned atom i checks its own bin and other bins in Newton stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfBinAtomonlyNewtonOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,k,n,itype,jtype,ibin; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - // loop over each atom, storing neighbors - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *molecule = atom->molecule; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - - // loop over rest of atoms in i's bin, ghosts are at end of linked list - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the right" of i - - for (j = bins[i]; j >= 0; j = bins[j]) { - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; - } - - // loop over all atoms in other bins in stencil, store every pair - - ibin = atom2bin[i]; - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_bin_atomonly_newton_omp.h b/src/OPENMP/npair_half_bin_atomonly_newton_omp.h deleted file mode 100644 index dcec3aeee2..0000000000 --- a/src/OPENMP/npair_half_bin_atomonly_newton_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/bin/atomonly/newton/omp, - NPairHalfBinAtomonlyNewtonOmp, - NP_HALF | NP_BIN | NP_ATOMONLY | NP_NEWTON | NP_OMP | NP_ORTHO); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_BIN_ATOMONLY_NEWTON_OMP_H -#define LMP_NPAIR_HALF_BIN_ATOMONLY_NEWTON_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfBinAtomonlyNewtonOmp : public NPair { - public: - NPairHalfBinAtomonlyNewtonOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_bin_newtoff_ghost_omp.cpp b/src/OPENMP/npair_half_bin_newtoff_ghost_omp.cpp deleted file mode 100644 index 2255033204..0000000000 --- a/src/OPENMP/npair_half_bin_newtoff_ghost_omp.cpp +++ /dev/null @@ -1,174 +0,0 @@ -// 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 "omp_compat.h" -#include "npair_half_bin_newtoff_ghost_omp.h" -#include "npair_omp.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfBinNewtoffGhostOmp::NPairHalfBinNewtoffGhostOmp(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with partial Newton's 3rd law - include neighbors of ghost atoms, but no "special neighbors" for ghosts - owned and ghost atoms check own bin and other bins in stencil - pair stored once if i,j are both owned and i < j - pair stored by me if i owned and j ghost (also stored by proc owning j) - pair stored once if i,j are both ghost and i < j -------------------------------------------------------------------------- */ - -void NPairHalfBinNewtoffGhostOmp::build(NeighList *list) -{ - const int nlocal = atom->nlocal; - const int nall = nlocal + atom->nghost; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nall); - - int i,j,k,n,itype,jtype,ibin,which,imol,iatom; - tagint tagprev; - int xbin,ybin,zbin,xbin2,ybin2,zbin2; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - // loop over each atom, storing neighbors - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in other bins in stencil including self - // when i is a ghost atom, must check if stencil bin is out of bounds - // only store pair if i < j - // stores own/own pairs only once - // stores own/ghost pairs with owned atom only, on both procs - // stores ghost/ghost pairs only once - // no molecular test when i = ghost atom - - if (i < nlocal) { - ibin = atom2bin[i]; - - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (j <= i) continue; - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - } else { - ibin = coord2bin(x[i],xbin,ybin,zbin); - for (k = 0; k < nstencil; k++) { - xbin2 = xbin + stencilxyz[k][0]; - ybin2 = ybin + stencilxyz[k][1]; - zbin2 = zbin + stencilxyz[k][2]; - if (xbin2 < 0 || xbin2 >= mbinx || - ybin2 < 0 || ybin2 >= mbiny || - zbin2 < 0 || zbin2 >= mbinz) continue; - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (j <= i) continue; - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighghostsq[itype][jtype]) neighptr[n++] = j; - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; - list->gnum = nall - atom->nlocal; -} diff --git a/src/OPENMP/npair_half_bin_newtoff_ghost_omp.h b/src/OPENMP/npair_half_bin_newtoff_ghost_omp.h deleted file mode 100644 index 0258320f88..0000000000 --- a/src/OPENMP/npair_half_bin_newtoff_ghost_omp.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/bin/newtoff/ghost/omp, - NPairHalfBinNewtoffGhostOmp, - NP_HALF | NP_BIN | NP_NEWTOFF | NP_GHOST | NP_OMP | - NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_BIN_NEWTOFF_GHOST_OMP_H -#define LMP_NPAIR_HALF_BIN_NEWTOFF_GHOST_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfBinNewtoffGhostOmp : public NPair { - public: - NPairHalfBinNewtoffGhostOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_bin_newtoff_omp.cpp b/src/OPENMP/npair_half_bin_newtoff_omp.cpp deleted file mode 100644 index 36997d9bcb..0000000000 --- a/src/OPENMP/npair_half_bin_newtoff_omp.cpp +++ /dev/null @@ -1,139 +0,0 @@ -// 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 "omp_compat.h" -#include "npair_half_bin_newtoff_omp.h" -#include "npair_omp.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfBinNewtoffOmp::NPairHalfBinNewtoffOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with partial Newton's 3rd law - each owned atom i checks own bin and other bins in stencil - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) -------------------------------------------------------------------------- */ - -void NPairHalfBinNewtoffOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,k,n,itype,jtype,ibin,which,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - // loop over each atom, storing neighbors - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in other bins in stencil including self - // only store pair if i < j - // stores own/own pairs only once - // stores own/ghost pairs on both procs - - ibin = atom2bin[i]; - - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (j <= i) continue; - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_bin_newtoff_omp.h b/src/OPENMP/npair_half_bin_newtoff_omp.h deleted file mode 100644 index e5d3034667..0000000000 --- a/src/OPENMP/npair_half_bin_newtoff_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/bin/newtoff/omp, - NPairHalfBinNewtoffOmp, - NP_HALF | NP_BIN | NP_NEWTOFF | NP_OMP | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_BIN_NEWTOFF_OMP_H -#define LMP_NPAIR_HALF_BIN_NEWTOFF_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfBinNewtoffOmp : public NPair { - public: - NPairHalfBinNewtoffOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_bin_newton_omp.cpp b/src/OPENMP/npair_half_bin_newton_omp.cpp deleted file mode 100644 index 1663a2f14c..0000000000 --- a/src/OPENMP/npair_half_bin_newton_omp.cpp +++ /dev/null @@ -1,172 +0,0 @@ -// 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 "omp_compat.h" -#include "npair_half_bin_newton_omp.h" -#include "npair_omp.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfBinNewtonOmp::NPairHalfBinNewtonOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with full Newton's 3rd law - each owned atom i checks its own bin and other bins in Newton stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfBinNewtonOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,k,n,itype,jtype,ibin,which,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - // loop over each atom, storing neighbors - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over rest of atoms in i's bin, ghosts are at end of linked list - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the right" of i - - for (j = bins[i]; j >= 0; j = bins[j]) { - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - // OLD: if (which >= 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - - // loop over all atoms in other bins in stencil, store every pair - - ibin = atom2bin[i]; - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - // OLD: if (which >= 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_bin_newton_omp.h b/src/OPENMP/npair_half_bin_newton_omp.h deleted file mode 100644 index 68064cdf45..0000000000 --- a/src/OPENMP/npair_half_bin_newton_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/bin/newton/omp, - NPairHalfBinNewtonOmp, - NP_HALF | NP_BIN | NP_NEWTON | NP_OMP | NP_ORTHO); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_BIN_NEWTON_OMP_H -#define LMP_NPAIR_HALF_BIN_NEWTON_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfBinNewtonOmp : public NPair { - public: - NPairHalfBinNewtonOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_bin_newton_tri_omp.cpp b/src/OPENMP/npair_half_bin_newton_tri_omp.cpp deleted file mode 100644 index e754456ef1..0000000000 --- a/src/OPENMP/npair_half_bin_newton_tri_omp.cpp +++ /dev/null @@ -1,145 +0,0 @@ -// 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 "omp_compat.h" -#include "npair_half_bin_newton_tri_omp.h" -#include "npair_omp.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfBinNewtonTriOmp::NPairHalfBinNewtonTriOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with Newton's 3rd law for triclinic - each owned atom i checks its own bin and other bins in triclinic stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfBinNewtonTriOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,k,n,itype,jtype,ibin,which,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - // loop over each atom, storing neighbors - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in bins in stencil - // pairs for atoms j "below" i are excluded - // below = lower z or (equal z and lower y) or (equal zy and lower x) - // (equal zyx and j <= i) - // latter excludes self-self interaction but allows superposed atoms - - ibin = atom2bin[i]; - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp) { - if (x[j][0] < xtmp) continue; - if (x[j][0] == xtmp && j <= i) continue; - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_bin_newton_tri_omp.h b/src/OPENMP/npair_half_bin_newton_tri_omp.h deleted file mode 100644 index 90d5af5db1..0000000000 --- a/src/OPENMP/npair_half_bin_newton_tri_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/bin/newton/tri/omp, - NPairHalfBinNewtonTriOmp, - NP_HALF | NP_BIN | NP_NEWTON | NP_TRI | NP_OMP); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_BIN_NEWTON_TRI_OMP_H -#define LMP_NPAIR_HALF_BIN_NEWTON_TRI_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfBinNewtonTriOmp : public NPair { - public: - NPairHalfBinNewtonTriOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_multi_newtoff_omp.cpp b/src/OPENMP/npair_half_multi_newtoff_omp.cpp deleted file mode 100644 index 1b65653f76..0000000000 --- a/src/OPENMP/npair_half_multi_newtoff_omp.cpp +++ /dev/null @@ -1,157 +0,0 @@ -// 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 "omp_compat.h" -#include "npair_half_multi_newtoff_omp.h" -#include "npair_omp.h" -#include "neighbor.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfMultiNewtoffOmp::NPairHalfMultiNewtoffOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with partial Newton's 3rd law - multi stencil is icollection-jcollection dependent - each owned atom i checks own bin and other bins in stencil - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) -------------------------------------------------------------------------- */ - -void NPairHalfMultiNewtoffOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,k,n,itype,jtype,icollection,jcollection,ibin,jbin,which,ns,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*s; - int js; - - // loop over each atom, storing neighbors - - int *collection = neighbor->collection; - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - icollection = collection[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - ibin = atom2bin[i]; - - // loop through stencils for all collections - for (jcollection = 0; jcollection < ncollections; jcollection++) { - - // if same collection use own bin - if (icollection == jcollection) jbin = ibin; - else jbin = coord2bin(x[i], jcollection); - - // loop over all atoms in other bins in stencil including self - // only store pair if i < j - // stores own/own pairs only once - // stores own/ghost pairs on both procs - // use full stencil for all collection combinations - - s = stencil_multi[icollection][jcollection]; - ns = nstencil_multi[icollection][jcollection]; - - for (k = 0; k < ns; k++) { - js = binhead_multi[jcollection][jbin + s[k]]; - for (j = js; j >= 0; j = bins[j]) { - if (j <= i) continue; - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_multi_newtoff_omp.h b/src/OPENMP/npair_half_multi_newtoff_omp.h deleted file mode 100644 index 658f41f926..0000000000 --- a/src/OPENMP/npair_half_multi_newtoff_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/multi/newtoff/omp, - NPairHalfMultiNewtoffOmp, - NP_HALF | NP_MULTI | NP_NEWTOFF | NP_OMP | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_MULTI_NEWTOFF_OMP_H -#define LMP_NPAIR_HALF_MULTI_NEWTOFF_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfMultiNewtoffOmp : public NPair { - public: - NPairHalfMultiNewtoffOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_multi_newton_omp.cpp b/src/OPENMP/npair_half_multi_newton_omp.cpp deleted file mode 100644 index 8add1d3703..0000000000 --- a/src/OPENMP/npair_half_multi_newton_omp.cpp +++ /dev/null @@ -1,205 +0,0 @@ -// 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 "omp_compat.h" -#include "npair_half_multi_newton_omp.h" -#include "npair_omp.h" -#include "neighbor.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfMultiNewtonOmp::NPairHalfMultiNewtonOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with full Newton's 3rd law - multi stencil is icollection-jcollection dependent - each owned atom i checks its own bin and other bins in Newton stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfMultiNewtonOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,k,n,itype,jtype,icollection,jcollection,ibin,jbin,which,ns,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*s; - int js; - - // loop over each atom, storing neighbors - - int *collection = neighbor->collection; - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - icollection = collection[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - ibin = atom2bin[i]; - - // loop through stencils for all collections - for (jcollection = 0; jcollection < ncollections; jcollection++) { - - // if same collection use own bin - if (icollection == jcollection) jbin = ibin; - else jbin = coord2bin(x[i], jcollection); - - // if same size: uses half stencil so check central bin - if (cutcollectionsq[icollection][icollection] == cutcollectionsq[jcollection][jcollection]){ - - if (icollection == jcollection) js = bins[i]; - else js = binhead_multi[jcollection][jbin]; - - // if same collection, - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the right" of i - - // if different collections, - // if j is owned atom, store it if j > i - // if j is ghost, only store if j coords are "above and to the right" of i - - for (j = js; j >= 0; j = bins[j]) { - if ((icollection != jcollection) && (j < i)) continue; - - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - // for all collections, loop over all atoms in other bins in stencil, store every pair - // stencil is empty if i larger than j - // stencil is half if i same size as j - // stencil is full if i smaller than j - - s = stencil_multi[icollection][jcollection]; - ns = nstencil_multi[icollection][jcollection]; - - for (k = 0; k < ns; k++) { - js = binhead_multi[jcollection][jbin + s[k]]; - for (j = js; j >= 0; j = bins[j]) { - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_multi_newton_omp.h b/src/OPENMP/npair_half_multi_newton_omp.h deleted file mode 100644 index 44bee84653..0000000000 --- a/src/OPENMP/npair_half_multi_newton_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/multi/newton/omp, - NPairHalfMultiNewtonOmp, - NP_HALF | NP_MULTI | NP_NEWTON | NP_OMP | NP_ORTHO); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_MULTI_NEWTON_OMP_H -#define LMP_NPAIR_HALF_MULTI_NEWTON_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfMultiNewtonOmp : public NPair { - public: - NPairHalfMultiNewtonOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_multi_newton_tri_omp.cpp b/src/OPENMP/npair_half_multi_newton_tri_omp.cpp deleted file mode 100644 index a152d011a7..0000000000 --- a/src/OPENMP/npair_half_multi_newton_tri_omp.cpp +++ /dev/null @@ -1,171 +0,0 @@ -// 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 "omp_compat.h" -#include "npair_half_multi_newton_tri_omp.h" -#include "npair_omp.h" -#include "neighbor.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfMultiNewtonTriOmp::NPairHalfMultiNewtonTriOmp(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with Newton's 3rd law for triclinic - multi stencil is icollection-jcollection dependent - each owned atom i checks its own bin and other bins in triclinic stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfMultiNewtonTriOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,k,n,itype,jtype,ibin,jbin,icollection,jcollection,which,ns,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*s; - int js; - - // loop over each atom, storing neighbors - - int *collection = neighbor->collection; - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - icollection = collection[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - ibin = atom2bin[i]; - - // loop through stencils for all collections - for (jcollection = 0; jcollection < ncollections; jcollection++) { - - // if same collection use own bin - if (icollection == jcollection) jbin = ibin; - else jbin = coord2bin(x[i], jcollection); - - // loop over all atoms in bins in stencil - // stencil is empty if i larger than j - // stencil is half if i same size as j - // stencil is full if i smaller than j - // if half: pairs for atoms j "below" i are excluded - // below = lower z or (equal z and lower y) or (equal zy and lower x) - // (equal zyx and j <= i) - // latter excludes self-self interaction but allows superposed atoms - - s = stencil_multi[icollection][jcollection]; - ns = nstencil_multi[icollection][jcollection]; - - for (k = 0; k < ns; k++) { - js = binhead_multi[jcollection][jbin + s[k]]; - for (j = js; j >= 0; j = bins[j]) { - - // if same size (same collection), use half stencil - if (cutcollectionsq[icollection][icollection] == cutcollectionsq[jcollection][jcollection]){ - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp) { - if (x[j][0] < xtmp) continue; - if (x[j][0] == xtmp && j <= i) continue; - } - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_multi_newton_tri_omp.h b/src/OPENMP/npair_half_multi_newton_tri_omp.h deleted file mode 100644 index 21731f4f0b..0000000000 --- a/src/OPENMP/npair_half_multi_newton_tri_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/multi/newton/tri/omp, - NPairHalfMultiNewtonTriOmp, - NP_HALF | NP_MULTI | NP_NEWTON | NP_TRI | NP_OMP); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_MULTI_NEWTON_TRI_OMP_H -#define LMP_NPAIR_HALF_MULTI_NEWTON_TRI_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfMultiNewtonTriOmp : public NPair { - public: - NPairHalfMultiNewtonTriOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_multi_old_newtoff_omp.cpp b/src/OPENMP/npair_half_multi_old_newtoff_omp.cpp deleted file mode 100644 index ac5e9dae04..0000000000 --- a/src/OPENMP/npair_half_multi_old_newtoff_omp.cpp +++ /dev/null @@ -1,146 +0,0 @@ -// 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 "omp_compat.h" -#include "npair_half_multi_old_newtoff_omp.h" -#include "npair_omp.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfMultiOldNewtoffOmp::NPairHalfMultiOldNewtoffOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with partial Newton's 3rd law - each owned atom i checks own bin and other bins in stencil - multi-type stencil is itype dependent and is distance checked - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) -------------------------------------------------------------------------- */ - -void NPairHalfMultiOldNewtoffOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,k,n,itype,jtype,ibin,which,ns,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*s; - double *cutsq,*distsq; - - // loop over each atom, storing neighbors - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in other bins in stencil including self - // only store pair if i < j - // skip if i,j neighbor cutoff is less than bin distance - // stores own/own pairs only once - // stores own/ghost pairs on both procs - - ibin = atom2bin[i]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - cutsq = cutneighsq[itype]; - ns = nstencil_multi_old[itype]; - for (k = 0; k < ns; k++) { - for (j = binhead[ibin+s[k]]; j >= 0; j = bins[j]) { - if (j <= i) continue; - jtype = type[j]; - if (cutsq[jtype] < distsq[k]) continue; - - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_multi_old_newtoff_omp.h b/src/OPENMP/npair_half_multi_old_newtoff_omp.h deleted file mode 100644 index 26484d6c5b..0000000000 --- a/src/OPENMP/npair_half_multi_old_newtoff_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/multi/old/newtoff/omp, - NPairHalfMultiOldNewtoffOmp, - NP_HALF | NP_MULTI_OLD | NP_NEWTOFF | NP_OMP | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_MULTI_OLD_NEWTOFF_OMP_H -#define LMP_NPAIR_HALF_MULTI_OLD_NEWTOFF_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfMultiOldNewtoffOmp : public NPair { - public: - NPairHalfMultiOldNewtoffOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_multi_old_newton_omp.cpp b/src/OPENMP/npair_half_multi_old_newton_omp.cpp deleted file mode 100644 index baa9dd0724..0000000000 --- a/src/OPENMP/npair_half_multi_old_newton_omp.cpp +++ /dev/null @@ -1,179 +0,0 @@ -// 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 "omp_compat.h" -#include "npair_half_multi_old_newton_omp.h" -#include "npair_omp.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfMultiOldNewtonOmp::NPairHalfMultiOldNewtonOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with full Newton's 3rd law - each owned atom i checks its own bin and other bins in Newton stencil - multi-type stencil is itype dependent and is distance checked - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfMultiOldNewtonOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,k,n,itype,jtype,ibin,which,ns,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*s; - double *cutsq,*distsq; - - // loop over each atom, storing neighbors - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over rest of atoms in i's bin, ghosts are at end of linked list - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the right" of i - - for (j = bins[i]; j >= 0; j = bins[j]) { - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - - // loop over all atoms in other bins in stencil, store every pair - // skip if i,j neighbor cutoff is less than bin distance - - ibin = atom2bin[i]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - cutsq = cutneighsq[itype]; - ns = nstencil_multi_old[itype]; - for (k = 0; k < ns; k++) { - for (j = binhead[ibin+s[k]]; j >= 0; j = bins[j]) { - jtype = type[j]; - if (cutsq[jtype] < distsq[k]) continue; - - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_multi_old_newton_omp.h b/src/OPENMP/npair_half_multi_old_newton_omp.h deleted file mode 100644 index 8182b49bd7..0000000000 --- a/src/OPENMP/npair_half_multi_old_newton_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/multi/old/newton/omp, - NPairHalfMultiOldNewtonOmp, - NP_HALF | NP_MULTI_OLD | NP_NEWTON | NP_OMP | NP_ORTHO); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_MULTI_OLD_NEWTON_OMP_H -#define LMP_NPAIR_HALF_MULTI_OLD_NEWTON_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfMultiOldNewtonOmp : public NPair { - public: - NPairHalfMultiOldNewtonOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_multi_old_newton_tri_omp.cpp b/src/OPENMP/npair_half_multi_old_newton_tri_omp.cpp deleted file mode 100644 index e4895ff1a9..0000000000 --- a/src/OPENMP/npair_half_multi_old_newton_tri_omp.cpp +++ /dev/null @@ -1,155 +0,0 @@ -// 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 "omp_compat.h" -#include "npair_half_multi_old_newton_tri_omp.h" -#include "npair_omp.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfMultiOldNewtonTriOmp::NPairHalfMultiOldNewtonTriOmp(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with Newton's 3rd law for triclinic - each owned atom i checks its own bin and other bins in triclinic stencil - multi-type stencil is itype dependent and is distance checked - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfMultiOldNewtonTriOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,k,n,itype,jtype,ibin,which,ns,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*s; - double *cutsq,*distsq; - - // loop over each atom, storing neighbors - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in bins, including self, in stencil - // skip if i,j neighbor cutoff is less than bin distance - // bins below self are excluded from stencil - // pairs for atoms j "below" i are excluded - // below = lower z or (equal z and lower y) or (equal zy and lower x) - // (equal zyx and j <= i) - // latter excludes self-self interaction but allows superposed atoms - - ibin = atom2bin[i]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - cutsq = cutneighsq[itype]; - ns = nstencil_multi_old[itype]; - for (k = 0; k < ns; k++) { - for (j = binhead[ibin+s[k]]; j >= 0; j = bins[j]) { - jtype = type[j]; - if (cutsq[jtype] < distsq[k]) continue; - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp) { - if (x[j][0] < xtmp) continue; - if (x[j][0] == xtmp && j <= i) continue; - } - } - - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_multi_old_newton_tri_omp.h b/src/OPENMP/npair_half_multi_old_newton_tri_omp.h deleted file mode 100644 index 5efb007dc1..0000000000 --- a/src/OPENMP/npair_half_multi_old_newton_tri_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/multi/old/newton/tri/omp, - NPairHalfMultiOldNewtonTriOmp, - NP_HALF | NP_MULTI_OLD | NP_NEWTON | NP_TRI | NP_OMP); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_MULTI_OLD_NEWTON_TRI_OMP_H -#define LMP_NPAIR_HALF_MULTI_OLD_NEWTON_TRI_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfMultiOldNewtonTriOmp : public NPair { - public: - NPairHalfMultiOldNewtonTriOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_nsq_newtoff_ghost_omp.cpp b/src/OPENMP/npair_half_nsq_newtoff_ghost_omp.cpp deleted file mode 100644 index 388e51e1af..0000000000 --- a/src/OPENMP/npair_half_nsq_newtoff_ghost_omp.cpp +++ /dev/null @@ -1,158 +0,0 @@ -// 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 "omp_compat.h" -#include "npair_half_nsq_newtoff_ghost_omp.h" -#include "npair_omp.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "group.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfNsqNewtoffGhostOmp::NPairHalfNsqNewtoffGhostOmp(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - N^2 / 2 search for neighbor pairs with partial Newton's 3rd law - include neighbors of ghost atoms, but no "special neighbors" for ghosts - pair stored once if i,j are both owned and i < j - pair stored by me if i owned and j ghost (also stored by proc owning j) - pair stored once if i,j are both ghost and i < j -------------------------------------------------------------------------- */ - -void NPairHalfNsqNewtoffGhostOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int bitmask = (includegroup) ? group->bitmask[includegroup] : 0; - const int nall = nlocal + atom->nghost; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nall); - - int i,j,n,itype,jtype,which,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - // loop over owned & ghost atoms, storing neighbors - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over remaining atoms, owned and ghost - // only store pair if i < j - // stores own/own pairs only once - // stores own/ghost pairs with owned atom only, on both procs - // stores ghost/ghost pairs only once - // no molecular test when i = ghost atom - - if (i < nlocal) { - for (j = i+1; j < nall; j++) { - if (includegroup && !(mask[j] & bitmask)) continue; - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - - } else { - for (j = i+1; j < nall; j++) { - if (includegroup && !(mask[j] & bitmask)) continue; - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = atom->nlocal; - list->gnum = nall - atom->nlocal; -} diff --git a/src/OPENMP/npair_half_nsq_newtoff_omp.cpp b/src/OPENMP/npair_half_nsq_newtoff_omp.cpp deleted file mode 100644 index 002ea37e6b..0000000000 --- a/src/OPENMP/npair_half_nsq_newtoff_omp.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// 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 "omp_compat.h" -#include "npair_half_nsq_newtoff_omp.h" -#include "npair_omp.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "group.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfNsqNewtoffOmp::NPairHalfNsqNewtoffOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - N^2 / 2 search for neighbor pairs with partial Newton's 3rd law - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) -------------------------------------------------------------------------- */ - -void NPairHalfNsqNewtoffOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int bitmask = (includegroup) ? group->bitmask[includegroup] : 0; - const int nall = atom->nlocal + atom->nghost; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,n,itype,jtype,which,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - // loop over owned atoms, storing neighbors - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over remaining atoms, owned and ghost - // only store pair if i < j - - for (j = i+1; j < nall; j++) { - if (includegroup && !(mask[j] & bitmask)) continue; - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_nsq_newtoff_omp.h b/src/OPENMP/npair_half_nsq_newtoff_omp.h deleted file mode 100644 index 47a03750f7..0000000000 --- a/src/OPENMP/npair_half_nsq_newtoff_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/nsq/newtoff/omp, - NPairHalfNsqNewtoffOmp, - NP_HALF | NP_NSQ | NP_NEWTOFF | NP_OMP | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_NSQ_NEWTOFF_OMP_H -#define LMP_NPAIR_HALF_NSQ_NEWTOFF_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfNsqNewtoffOmp : public NPair { - public: - NPairHalfNsqNewtoffOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_nsq_newton_omp.cpp b/src/OPENMP/npair_half_nsq_newton_omp.cpp deleted file mode 100644 index cb08cb7f7a..0000000000 --- a/src/OPENMP/npair_half_nsq_newton_omp.cpp +++ /dev/null @@ -1,152 +0,0 @@ -// 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 "omp_compat.h" -#include "npair_half_nsq_newton_omp.h" -#include "npair_omp.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "group.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfNsqNewtonOmp::NPairHalfNsqNewtonOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - N^2 / 2 search for neighbor pairs with full Newton's 3rd law - every pair stored exactly once by some processor - decision on ghost atoms based on itag,jtag tests -------------------------------------------------------------------------- */ - -void NPairHalfNsqNewtonOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int bitmask = (includegroup) ? group->bitmask[includegroup] : 0; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,n,itype,jtype,itag,jtag,which,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr; - - // loop over each atom, storing neighbors - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int nall = atom->nlocal + atom->nghost; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itag = tag[i]; - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over remaining atoms, owned and ghost - // itag = jtag is possible for long cutoffs that include images of self - - for (j = i+1; j < nall; j++) { - if (includegroup && !(mask[j] & bitmask)) continue; - - if (j >= nlocal) { - jtag = tag[j]; - if (itag > jtag) { - if ((itag+jtag) % 2 == 0) continue; - } else if (itag < jtag) { - if ((itag+jtag) % 2 == 1) continue; - } else { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_nsq_newton_omp.h b/src/OPENMP/npair_half_nsq_newton_omp.h deleted file mode 100644 index 00e975d389..0000000000 --- a/src/OPENMP/npair_half_nsq_newton_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/nsq/newton/omp, - NPairHalfNsqNewtonOmp, - NP_HALF | NP_NSQ | NP_NEWTON | NP_OMP | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_NSQ_NEWTON_OMP_H -#define LMP_NPAIR_HALF_NSQ_NEWTON_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfNsqNewtonOmp : public NPair { - public: - NPairHalfNsqNewtonOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_respa_bin_newtoff_omp.cpp b/src/OPENMP/npair_half_respa_bin_newtoff_omp.cpp deleted file mode 100644 index c6be04419d..0000000000 --- a/src/OPENMP/npair_half_respa_bin_newtoff_omp.cpp +++ /dev/null @@ -1,203 +0,0 @@ -// 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 "omp_compat.h" -#include "npair_half_respa_bin_newtoff_omp.h" -#include "npair_omp.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfRespaBinNewtoffOmp::NPairHalfRespaBinNewtoffOmp(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - multiple respa lists - binned neighbor list construction with partial Newton's 3rd law - each owned atom i checks own bin and surrounding bins in non-Newton stencil - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) -------------------------------------------------------------------------- */ - -void NPairHalfRespaBinNewtoffOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; - - const int respamiddle = list->respamiddle; - -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,k,n,itype,jtype,ibin,n_inner,n_middle,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*neighptr_inner,*neighptr_middle; - - // loop over each atom, storing neighbors - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - int *ilist_inner = list->ilist_inner; - int *numneigh_inner = list->numneigh_inner; - int **firstneigh_inner = list->firstneigh_inner; - - int *ilist_middle,*numneigh_middle,**firstneigh_middle; - if (respamiddle) { - ilist_middle = list->ilist_middle; - numneigh_middle = list->numneigh_middle; - firstneigh_middle = list->firstneigh_middle; - } - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - MyPage &ipage_inner = list->ipage_inner[tid]; - ipage.reset(); - ipage_inner.reset(); - - MyPage *ipage_middle; - if (respamiddle) { - ipage_middle = list->ipage_middle + tid; - ipage_middle->reset(); - } - - int which = 0; - int minchange = 0; - - for (i = ifrom; i < ito; i++) { - - n = n_inner = 0; - neighptr = ipage.vget(); - neighptr_inner = ipage_inner.vget(); - if (respamiddle) { - n_middle = 0; - neighptr_middle = ipage_middle->vget(); - } - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - ibin = atom2bin[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in surrounding bins in stencil including self - // only store pair if i < j - // stores own/own pairs only once - // stores own/ghost pairs on both procs - - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (j <= i) continue; - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if ((minchange = domain->minimum_image_check(delx,dely,delz))) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - - if (rsq < cut_inner_sq) { - if (which == 0) neighptr_inner[n_inner++] = j; - else if (minchange) neighptr_inner[n_inner++] = j; - else if (which > 0) - neighptr_inner[n_inner++] = j ^ (which << SBBITS); - } - - if (respamiddle && - rsq < cut_middle_sq && rsq > cut_middle_inside_sq) { - if (which == 0) neighptr_middle[n_middle++] = j; - else if (minchange) neighptr_middle[n_middle++] = j; - else if (which > 0) - neighptr_middle[n_middle++] = j ^ (which << SBBITS); - } - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - ilist_inner[i] = i; - firstneigh_inner[i] = neighptr_inner; - numneigh_inner[i] = n_inner; - ipage.vgot(n_inner); - if (ipage_inner.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - if (respamiddle) { - ilist_middle[i] = i; - firstneigh_middle[i] = neighptr_middle; - numneigh_middle[i] = n_middle; - ipage_middle->vgot(n_middle); - if (ipage_middle->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; - list->inum_inner = nlocal; - if (respamiddle) list->inum_middle = nlocal; -} diff --git a/src/OPENMP/npair_half_respa_bin_newtoff_omp.h b/src/OPENMP/npair_half_respa_bin_newtoff_omp.h deleted file mode 100644 index 8ad6209d21..0000000000 --- a/src/OPENMP/npair_half_respa_bin_newtoff_omp.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/respa/bin/newtoff/omp, - NPairHalfRespaBinNewtoffOmp, - NP_HALF | NP_RESPA | NP_BIN | NP_NEWTOFF | NP_OMP | - NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_RESPA_BIN_NEWTOFF_OMP_H -#define LMP_NPAIR_HALF_RESPA_BIN_NEWTOFF_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfRespaBinNewtoffOmp : public NPair { - public: - NPairHalfRespaBinNewtoffOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_respa_bin_newton_tri_omp.cpp b/src/OPENMP/npair_half_respa_bin_newton_tri_omp.cpp deleted file mode 100644 index c998f71290..0000000000 --- a/src/OPENMP/npair_half_respa_bin_newton_tri_omp.cpp +++ /dev/null @@ -1,210 +0,0 @@ -// 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 "omp_compat.h" -#include "npair_half_respa_bin_newton_tri_omp.h" -#include "npair_omp.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfRespaBinNewtonTriOmp::NPairHalfRespaBinNewtonTriOmp(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - multiple respa lists - binned neighbor list construction with Newton's 3rd law for triclinic - each owned atom i checks its own bin and other bins in triclinic stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfRespaBinNewtonTriOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; - - const int respamiddle = list->respamiddle; - -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,k,n,itype,jtype,ibin,n_inner,n_middle,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*neighptr_inner,*neighptr_middle; - - // loop over each atom, storing neighbors - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - int *ilist_inner = list->ilist_inner; - int *numneigh_inner = list->numneigh_inner; - int **firstneigh_inner = list->firstneigh_inner; - - int *ilist_middle,*numneigh_middle,**firstneigh_middle; - if (respamiddle) { - ilist_middle = list->ilist_middle; - numneigh_middle = list->numneigh_middle; - firstneigh_middle = list->firstneigh_middle; - } - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - MyPage &ipage_inner = list->ipage_inner[tid]; - ipage.reset(); - ipage_inner.reset(); - - MyPage *ipage_middle; - if (respamiddle) { - ipage_middle = list->ipage_middle + tid; - ipage_middle->reset(); - } - - int which = 0; - int minchange = 0; - - for (i = ifrom; i < ito; i++) { - - n = n_inner = 0; - neighptr = ipage.vget(); - neighptr_inner = ipage_inner.vget(); - if (respamiddle) { - n_middle = 0; - neighptr_middle = ipage_middle->vget(); - } - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in bins in stencil - // pairs for atoms j "below" i are excluded - // below = lower z or (equal z and lower y) or (equal zy and lower x) - // (equal zyx and j <= i) - // latter excludes self-self interaction but allows superposed atoms - - ibin = atom2bin[i]; - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp) { - if (x[j][0] < xtmp) continue; - if (x[j][0] == xtmp && j <= i) continue; - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if ((minchange = domain->minimum_image_check(delx,dely,delz))) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - - if (rsq < cut_inner_sq) { - if (which == 0) neighptr_inner[n_inner++] = j; - else if (minchange) neighptr_inner[n_inner++] = j; - else if (which > 0) - neighptr_inner[n_inner++] = j ^ (which << SBBITS); - } - - if (respamiddle && - rsq < cut_middle_sq && rsq > cut_middle_inside_sq) { - if (which == 0) neighptr_middle[n_middle++] = j; - else if (minchange) neighptr_middle[n_middle++] = j; - else if (which > 0) - neighptr_middle[n_middle++] = j ^ (which << SBBITS); - } - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - ilist_inner[i] = i; - firstneigh_inner[i] = neighptr_inner; - numneigh_inner[i] = n_inner; - ipage_inner.vgot(n_inner); - if (ipage_inner.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - if (respamiddle) { - ilist_middle[i] = i; - firstneigh_middle[i] = neighptr_middle; - numneigh_middle[i] = n_middle; - ipage_middle->vgot(n_middle); - if (ipage_middle->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; - list->inum_inner = nlocal; - if (respamiddle) list->inum_middle = nlocal; -} diff --git a/src/OPENMP/npair_half_respa_bin_newton_tri_omp.h b/src/OPENMP/npair_half_respa_bin_newton_tri_omp.h deleted file mode 100644 index df45372960..0000000000 --- a/src/OPENMP/npair_half_respa_bin_newton_tri_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/respa/bin/newton/tri/omp, - NPairHalfRespaBinNewtonTriOmp, - NP_HALF | NP_RESPA | NP_BIN | NP_NEWTON | NP_TRI | NP_OMP); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_RESPA_BIN_NEWTON_TRI_OMP_H -#define LMP_NPAIR_HALF_RESPA_BIN_NEWTON_TRI_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfRespaBinNewtonTriOmp : public NPair { - public: - NPairHalfRespaBinNewtonTriOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_respa_nsq_newtoff_omp.h b/src/OPENMP/npair_half_respa_nsq_newtoff_omp.h deleted file mode 100644 index abd28fd51b..0000000000 --- a/src/OPENMP/npair_half_respa_nsq_newtoff_omp.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/respa/nsq/newtoff/omp, - NPairHalfRespaNsqNewtoffOmp, - NP_HALF | NP_RESPA | NP_NSQ | NP_NEWTOFF | NP_OMP | - NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_RESPA_NSQ_NEWTOFF_OMP_H -#define LMP_NPAIR_HALF_RESPA_NSQ_NEWTOFF_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfRespaNsqNewtoffOmp : public NPair { - public: - NPairHalfRespaNsqNewtoffOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_respa_nsq_newton_omp.cpp b/src/OPENMP/npair_half_respa_nsq_newton_omp.cpp deleted file mode 100644 index 6604861f74..0000000000 --- a/src/OPENMP/npair_half_respa_nsq_newton_omp.cpp +++ /dev/null @@ -1,216 +0,0 @@ -// 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 "omp_compat.h" -#include "npair_half_respa_nsq_newton_omp.h" -#include "npair_omp.h" -#include "neigh_list.h" -#include "atom.h" -#include "atom_vec.h" -#include "group.h" -#include "molecule.h" -#include "domain.h" -#include "my_page.h" -#include "error.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfRespaNsqNewtonOmp::NPairHalfRespaNsqNewtonOmp(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - multiple respa lists - N^2 / 2 search for neighbor pairs with full Newton's 3rd law - pair added to list if atoms i and j are both owned and i < j - if j is ghost only me or other proc adds pair - decision based on itag,jtag tests -------------------------------------------------------------------------- */ - -void NPairHalfRespaNsqNewtonOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int bitmask = (includegroup) ? group->bitmask[includegroup] : 0; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - - NPAIR_OMP_INIT; - - const int respamiddle = list->respamiddle; - -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,n,itype,jtype,itag,jtag,n_inner,n_middle,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *neighptr,*neighptr_inner,*neighptr_middle; - - // loop over each atom, storing neighbors - - double **x = atom->x; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int nall = atom->nlocal + atom->nghost; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - int *ilist_inner = list->ilist_inner; - int *numneigh_inner = list->numneigh_inner; - int **firstneigh_inner = list->firstneigh_inner; - - int *ilist_middle,*numneigh_middle,**firstneigh_middle; - if (respamiddle) { - ilist_middle = list->ilist_middle; - numneigh_middle = list->numneigh_middle; - firstneigh_middle = list->firstneigh_middle; - } - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - MyPage &ipage_inner = list->ipage_inner[tid]; - ipage.reset(); - ipage_inner.reset(); - - MyPage *ipage_middle; - if (respamiddle) { - ipage_middle = list->ipage_middle + tid; - ipage_middle->reset(); - } - - int which = 0; - int minchange = 0; - - for (i = ifrom; i < ito; i++) { - - n = n_inner = 0; - neighptr = ipage.vget(); - neighptr_inner = ipage_inner.vget(); - if (respamiddle) { - n_middle = 0; - neighptr_middle = ipage_middle->vget(); - } - - itag = tag[i]; - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over remaining atoms, owned and ghost - - for (j = i+1; j < nall; j++) { - if (includegroup && !(mask[j] & bitmask)) continue; - - if (j >= nlocal) { - jtag = tag[j]; - if (itag > jtag) { - if ((itag+jtag) % 2 == 0) continue; - } else if (itag < jtag) { - if ((itag+jtag) % 2 == 1) continue; - } else { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if ((minchange = domain->minimum_image_check(delx,dely,delz))) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - - if (rsq < cut_inner_sq) { - if (which == 0) neighptr_inner[n_inner++] = j; - else if (minchange) neighptr_inner[n_inner++] = j; - else if (which > 0) neighptr_inner[n_inner++] = j ^ (which << SBBITS); - } - - if (respamiddle && - rsq < cut_middle_sq && rsq > cut_middle_inside_sq) { - if (which == 0) neighptr_middle[n_middle++] = j; - else if (minchange) neighptr_middle[n_middle++] = j; - else if (which > 0) - neighptr_middle[n_middle++] = j ^ (which << SBBITS); - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - ilist_inner[i] = i; - firstneigh_inner[i] = neighptr_inner; - numneigh_inner[i] = n_inner; - ipage.vgot(n_inner); - if (ipage_inner.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - if (respamiddle) { - ilist_middle[i] = i; - firstneigh_middle[i] = neighptr_middle; - numneigh_middle[i] = n_middle; - ipage_middle->vgot(n_middle); - if (ipage_middle->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; - list->inum_inner = nlocal; - if (respamiddle) list->inum_middle = nlocal; -} diff --git a/src/OPENMP/npair_half_size_bin_newtoff_omp.cpp b/src/OPENMP/npair_half_size_bin_newtoff_omp.cpp deleted file mode 100644 index c205b67539..0000000000 --- a/src/OPENMP/npair_half_size_bin_newtoff_omp.cpp +++ /dev/null @@ -1,151 +0,0 @@ -// 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 "npair_half_size_bin_newtoff_omp.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neigh_list.h" -#include "npair_omp.h" - -#include "omp_compat.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeBinNewtoffOmp::NPairHalfSizeBinNewtoffOmp(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - binned neighbor list construction with partial Newton's 3rd law - each owned atom i checks own bin and surrounding bins in non-Newton stencil - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) -------------------------------------------------------------------------- */ - -void NPairHalfSizeBinNewtoffOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - const int history = list->history; - const int mask_history = 1 << HISTBITS; - - NPAIR_OMP_INIT; - -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,jh,k,n,ibin,which,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutsq; - int *neighptr; - - // loop over each atom, storing neighbors - - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - ibin = atom2bin[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in surrounding bins in stencil including self - // only store pair if i < j - // stores own/own pairs only once - // stores own/ghost pairs on both procs - - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (j <= i) continue; - if (exclude && exclusion(i,j,type[i],type[j],mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_size_bin_newtoff_omp.h b/src/OPENMP/npair_half_size_bin_newtoff_omp.h deleted file mode 100644 index a91836b152..0000000000 --- a/src/OPENMP/npair_half_size_bin_newtoff_omp.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/bin/newtoff/omp, - NPairHalfSizeBinNewtoffOmp, - NP_HALF | NP_SIZE | NP_BIN | NP_NEWTOFF | NP_OMP | - NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_BIN_NEWTOFF_OMP_H -#define LMP_NPAIR_HALF_SIZE_BIN_NEWTOFF_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeBinNewtoffOmp : public NPair { - public: - NPairHalfSizeBinNewtoffOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_size_bin_newton_omp.cpp b/src/OPENMP/npair_half_size_bin_newton_omp.cpp deleted file mode 100644 index 628057d41d..0000000000 --- a/src/OPENMP/npair_half_size_bin_newton_omp.cpp +++ /dev/null @@ -1,187 +0,0 @@ -// 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 "npair_half_size_bin_newton_omp.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neigh_list.h" -#include "npair_omp.h" - -#include "omp_compat.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeBinNewtonOmp::NPairHalfSizeBinNewtonOmp(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - binned neighbor list construction with full Newton's 3rd law - each owned atom i checks its own bin and other bins in Newton stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfSizeBinNewtonOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - const int history = list->history; - const int mask_history = 1 << HISTBITS; - - NPAIR_OMP_INIT; - -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,jh,k,n,ibin,which,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutsq; - int *neighptr; - - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over rest of atoms in i's bin, ghosts are at end of linked list - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the right" of i - - for (j = bins[i]; j >= 0; j = bins[j]) { - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - - if (exclude && exclusion(i,j,type[i],type[j],mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - - // loop over all atoms in other bins in stencil, store every pair - - ibin = atom2bin[i]; - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (exclude && exclusion(i,j,type[i],type[j],mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_size_bin_newton_omp.h b/src/OPENMP/npair_half_size_bin_newton_omp.h deleted file mode 100644 index efc554bb3c..0000000000 --- a/src/OPENMP/npair_half_size_bin_newton_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/bin/newton/omp, - NPairHalfSizeBinNewtonOmp, - NP_HALF | NP_SIZE | NP_BIN | NP_NEWTON | NP_OMP | NP_ORTHO); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_BIN_NEWTON_OMP_H -#define LMP_NPAIR_HALF_SIZE_BIN_NEWTON_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeBinNewtonOmp : public NPair { - public: - NPairHalfSizeBinNewtonOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_size_bin_newton_tri_omp.cpp b/src/OPENMP/npair_half_size_bin_newton_tri_omp.cpp deleted file mode 100644 index c320296442..0000000000 --- a/src/OPENMP/npair_half_size_bin_newton_tri_omp.cpp +++ /dev/null @@ -1,158 +0,0 @@ -// 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 "npair_half_size_bin_newton_tri_omp.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neigh_list.h" -#include "npair_omp.h" - -#include "omp_compat.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeBinNewtonTriOmp::NPairHalfSizeBinNewtonTriOmp(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - binned neighbor list construction with Newton's 3rd law for triclinic - each owned atom i checks its own bin and other bins in triclinic stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfSizeBinNewtonTriOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - const int history = list->history; - const int mask_history = 1 << HISTBITS; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,jh,k,n,ibin,which,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutsq; - int *neighptr; - - // loop over each atom, storing neighbors - - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in bins in stencil - // pairs for atoms j "below" i are excluded - // below = lower z or (equal z and lower y) or (equal zy and lower x) - // (equal zyx and j <= i) - // latter excludes self-self interaction but allows superposed atoms - - ibin = atom2bin[i]; - for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp) { - if (x[j][0] < xtmp) continue; - if (x[j][0] == xtmp && j <= i) continue; - } - } - - if (exclude && exclusion(i,j,type[i],type[j],mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_size_bin_newton_tri_omp.h b/src/OPENMP/npair_half_size_bin_newton_tri_omp.h deleted file mode 100644 index 65b46395ca..0000000000 --- a/src/OPENMP/npair_half_size_bin_newton_tri_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/bin/newton/tri/omp, - NPairHalfSizeBinNewtonTriOmp, - NP_HALF | NP_SIZE | NP_BIN | NP_NEWTON | NP_TRI | NP_OMP); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_BIN_NEWTON_TRI_OMP_H -#define LMP_NPAIR_HALF_SIZE_BIN_NEWTON_TRI_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeBinNewtonTriOmp : public NPair { - public: - NPairHalfSizeBinNewtonTriOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_size_multi_newtoff_omp.cpp b/src/OPENMP/npair_half_size_multi_newtoff_omp.cpp deleted file mode 100644 index 73564a150c..0000000000 --- a/src/OPENMP/npair_half_size_multi_newtoff_omp.cpp +++ /dev/null @@ -1,172 +0,0 @@ -// 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 "npair_half_size_multi_newtoff_omp.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neighbor.h" -#include "neigh_list.h" -#include "npair_omp.h" - -#include "omp_compat.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeMultiNewtoffOmp::NPairHalfSizeMultiNewtoffOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - binned neighbor list construction with partial Newton's 3rd law - multi stencil is icollection-jcollection dependent - each owned atom i checks own bin and other bins in stencil - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) -------------------------------------------------------------------------- */ - -void NPairHalfSizeMultiNewtoffOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - const int history = list->history; - const int mask_history = 1 << HISTBITS; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,jh,k,n,itype,jtype,icollection,jcollection,ibin,jbin,ns; - int which,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutdistsq; - int *neighptr,*s; - int js; - - // loop over each atom, storing neighbors - - int *collection = neighbor->collection; - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - icollection = collection[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - ibin = atom2bin[i]; - - // loop through stencils for all collections - for (jcollection = 0; jcollection < ncollections; jcollection++) { - - // if same collection use own bin - if(icollection == jcollection) jbin = ibin; - else jbin = coord2bin(x[i], jcollection); - - // loop over all atoms in other bins in stencil including self - // only store pair if i < j - // stores own/own pairs only once - // stores own/ghost pairs on both procs - // use full stencil for all collection combinations - - s = stencil_multi[icollection][jcollection]; - ns = nstencil_multi[icollection][jcollection]; - - for (k = 0; k < ns; k++) { - js = binhead_multi[jcollection][jbin + s[k]]; - for (j = js; j >=0; j = bins[j]) { - if (j <= i) continue; - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutdistsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutdistsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_size_multi_newtoff_omp.h b/src/OPENMP/npair_half_size_multi_newtoff_omp.h deleted file mode 100644 index 2e58d9ea38..0000000000 --- a/src/OPENMP/npair_half_size_multi_newtoff_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/multi/newtoff/omp, - NPairHalfSizeMultiNewtoffOmp, - NP_HALF | NP_SIZE | NP_MULTI | NP_NEWTOFF | NP_OMP | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_MULTI_NEWTOFF_OMP_H -#define LMP_NPAIR_HALF_SIZE_MULTI_NEWTOFF_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeMultiNewtoffOmp : public NPair { - public: - NPairHalfSizeMultiNewtoffOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_size_multi_newton_omp.cpp b/src/OPENMP/npair_half_size_multi_newton_omp.cpp deleted file mode 100644 index 0ed843ea0b..0000000000 --- a/src/OPENMP/npair_half_size_multi_newton_omp.cpp +++ /dev/null @@ -1,225 +0,0 @@ -// 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 "npair_half_size_multi_newton_omp.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neighbor.h" -#include "neigh_list.h" -#include "npair_omp.h" - -#include "omp_compat.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeMultiNewtonOmp::NPairHalfSizeMultiNewtonOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - binned neighbor list construction with full Newton's 3rd law - multi stencil is icollection-jcollection dependent - each owned atom i checks its own bin and other bins in Newton stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfSizeMultiNewtonOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - const int history = list->history; - const int mask_history = 1 << HISTBITS; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,jh,k,n,itype,jtype,icollection,jcollection,ibin,jbin,ns; - int which,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutdistsq; - int *neighptr,*s; - int js; - - // loop over each atom, storing neighbors - - int *collection = neighbor->collection; - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - icollection = collection[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - ibin = atom2bin[i]; - - // loop through stencils for all collections - for (jcollection = 0; jcollection < ncollections; jcollection++) { - - // if same collection use own bin - if(icollection == jcollection) jbin = ibin; - else jbin = coord2bin(x[i], jcollection); - - // if same size: uses half stencil so check central bin - if(cutcollectionsq[icollection][icollection] == cutcollectionsq[jcollection][jcollection]){ - - if(icollection == jcollection) js = bins[i]; - else js = binhead_multi[jcollection][jbin]; - - // if same collection, - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the right" of i - - // if different collections, - // if j is owned atom, store it if j > i - // if j is ghost, only store if j coords are "above and to the right" of i - - for (j = js; j >= 0; j = bins[j]) { - if(icollection != jcollection && j < i) continue; - - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutdistsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutdistsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - } - - // for all collections, loop over all atoms in other bins in stencil, store every pair - // stencil is empty if i larger than j - // stencil is half if i same size as j - // stencil is full if i smaller than j - - s = stencil_multi[icollection][jcollection]; - ns = nstencil_multi[icollection][jcollection]; - - for (k = 0; k < ns; k++) { - js = binhead_multi[jcollection][jbin + s[k]]; - for (j = js; j >= 0; j = bins[j]) { - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutdistsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutdistsq) { - if (history && rsq < radsum*radsum) - j = j ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - } - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_size_multi_newton_omp.h b/src/OPENMP/npair_half_size_multi_newton_omp.h deleted file mode 100644 index 99f9174913..0000000000 --- a/src/OPENMP/npair_half_size_multi_newton_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/multi/newton/omp, - NPairHalfSizeMultiNewtonOmp, - NP_HALF | NP_SIZE | NP_MULTI | NP_NEWTON | NP_OMP | NP_ORTHO); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_MULTI_NEWTON_OMP_H -#define LMP_NPAIR_HALF_SIZE_MULTI_NEWTON_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeMultiNewtonOmp : public NPair { - public: - NPairHalfSizeMultiNewtonOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_size_multi_newton_tri_omp.cpp b/src/OPENMP/npair_half_size_multi_newton_tri_omp.cpp deleted file mode 100644 index 9a0ead482b..0000000000 --- a/src/OPENMP/npair_half_size_multi_newton_tri_omp.cpp +++ /dev/null @@ -1,187 +0,0 @@ -// 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 "npair_half_size_multi_newton_tri_omp.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neighbor.h" -#include "neigh_list.h" -#include "npair_omp.h" - -#include "omp_compat.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeMultiNewtonTriOmp::NPairHalfSizeMultiNewtonTriOmp(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - binned neighbor list construction with Newton's 3rd law for triclinic - multi stencil is icollection-jcollection dependent - each owned atom i checks its own bin and other bins in triclinic stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfSizeMultiNewtonTriOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - const int history = list->history; - const int mask_history = 1 << HISTBITS; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,jh,k,n,itype,jtype,icollection,jcollection,ibin,jbin,ns; - int which,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutdistsq; - int *neighptr,*s; - int js; - - // loop over each atom, storing neighbors - - int *collection = neighbor->collection; - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - icollection = collection[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - ibin = atom2bin[i]; - - // loop through stencils for all collections - for (jcollection = 0; jcollection < ncollections; jcollection++) { - - // if same collection use own bin - if(icollection == jcollection) jbin = ibin; - else jbin = coord2bin(x[i], jcollection); - - - // loop over all atoms in bins in stencil - // stencil is empty if i larger than j - // stencil is half if i same size as j - // stencil is full if i smaller than j - // if half: pairs for atoms j "below" i are excluded - // below = lower z or (equal z and lower y) or (equal zy and lower x) - // (equal zyx and j <= i) - // latter excludes self-self interaction but allows superposed atoms - - s = stencil_multi[icollection][jcollection]; - ns = nstencil_multi[icollection][jcollection]; - - for (k = 0; k < ns; k++) { - js = binhead_multi[jcollection][jbin + s[k]]; - for (j = js; j >= 0; j = bins[j]) { - - // if same size (same collection), use half stencil - if(cutcollectionsq[icollection][icollection] == cutcollectionsq[jcollection][jcollection]){ - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp) { - if (x[j][0] < xtmp) continue; - if (x[j][0] == xtmp && j <= i) continue; - } - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutdistsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutdistsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_size_multi_newton_tri_omp.h b/src/OPENMP/npair_half_size_multi_newton_tri_omp.h deleted file mode 100644 index d4f6eacc27..0000000000 --- a/src/OPENMP/npair_half_size_multi_newton_tri_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/multi/newton/tri/omp, - NPairHalfSizeMultiNewtonTriOmp, - NP_HALF | NP_SIZE | NP_MULTI | NP_NEWTON | NP_TRI | NP_OMP); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_MULTI_NEWTON_TRI_OMP_H -#define LMP_NPAIR_HALF_SIZE_MULTI_NEWTON_TRI_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeMultiNewtonTriOmp : public NPair { - public: - NPairHalfSizeMultiNewtonTriOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_size_multi_old_newtoff_omp.cpp b/src/OPENMP/npair_half_size_multi_old_newtoff_omp.cpp deleted file mode 100644 index c368e71095..0000000000 --- a/src/OPENMP/npair_half_size_multi_old_newtoff_omp.cpp +++ /dev/null @@ -1,158 +0,0 @@ -// 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 "npair_half_size_multi_old_newtoff_omp.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neigh_list.h" -#include "npair_omp.h" - -#include "omp_compat.h" -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeMultiOldNewtoffOmp::NPairHalfSizeMultiOldNewtoffOmp(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - binned neighbor list construction with partial Newton's 3rd law - each owned atom i checks own bin and other bins in stencil - multi-type stencil is itype dependent and is distance checked - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) -------------------------------------------------------------------------- */ - -void NPairHalfSizeMultiOldNewtoffOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - const int history = list->history; - const int mask_history = 1 << HISTBITS; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,jh,k,n,itype,jtype,ibin,ns,which,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutdistsq; - int *neighptr,*s; - double *cutsq,*distsq; - - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in other bins in stencil including self - // only store pair if i < j - // skip if i,j neighbor cutoff is less than bin distance - // stores own/own pairs only once - // stores own/ghost pairs on both procs - - ibin = atom2bin[i]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - cutsq = cutneighsq[itype]; - ns = nstencil_multi_old[itype]; - for (k = 0; k < ns; k++) { - for (j = binhead[ibin+s[k]]; j >= 0; j = bins[j]) { - if (j <= i) continue; - jtype = type[j]; - if (cutsq[jtype] < distsq[k]) continue; - - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutdistsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutdistsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_size_multi_old_newtoff_omp.h b/src/OPENMP/npair_half_size_multi_old_newtoff_omp.h deleted file mode 100644 index 05eb0a5269..0000000000 --- a/src/OPENMP/npair_half_size_multi_old_newtoff_omp.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/multi/old/newtoff/omp, - NPairHalfSizeMultiOldNewtoffOmp, - NP_HALF | NP_SIZE | NP_MULTI_OLD | NP_NEWTOFF | NP_OMP | - NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_MULTI_OLD_NEWTOFF_OMP_H -#define LMP_NPAIR_HALF_SIZE_MULTI_OLD_NEWTOFF_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeMultiOldNewtoffOmp : public NPair { - public: - NPairHalfSizeMultiOldNewtoffOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_size_multi_old_newton_omp.h b/src/OPENMP/npair_half_size_multi_old_newton_omp.h deleted file mode 100644 index 70ae082dbb..0000000000 --- a/src/OPENMP/npair_half_size_multi_old_newton_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/multi/old/newton/omp, - NPairHalfSizeMultiOldNewtonOmp, - NP_HALF | NP_SIZE | NP_MULTI_OLD | NP_NEWTON | NP_OMP | NP_ORTHO); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_MULTI_OLD_NEWTON_OMP_H -#define LMP_NPAIR_HALF_SIZE_MULTI_OLD_NEWTON_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeMultiOldNewtonOmp : public NPair { - public: - NPairHalfSizeMultiOldNewtonOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_size_multi_old_newton_tri_omp.cpp b/src/OPENMP/npair_half_size_multi_old_newton_tri_omp.cpp deleted file mode 100644 index c74b191f66..0000000000 --- a/src/OPENMP/npair_half_size_multi_old_newton_tri_omp.cpp +++ /dev/null @@ -1,166 +0,0 @@ -// 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 "npair_half_size_multi_old_newton_tri_omp.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "my_page.h" -#include "neigh_list.h" -#include "npair_omp.h" - -#include "omp_compat.h" -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeMultiOldNewtonTriOmp::NPairHalfSizeMultiOldNewtonTriOmp(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - binned neighbor list construction with Newton's 3rd law for triclinic - each owned atom i checks its own bin and other bins in triclinic stencil - multi-type stencil is itype dependent and is distance checked - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfSizeMultiOldNewtonTriOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - const int history = list->history; - const int mask_history = 1 << HISTBITS; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,jh,k,n,itype,jtype,ibin,ns,which,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutdistsq; - int *neighptr,*s; - double *cutsq,*distsq; - - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over all atoms in bins, including self, in stencil - // skip if i,j neighbor cutoff is less than bin distance - // bins below self are excluded from stencil - // pairs for atoms j "below" i are excluded - // below = lower z or (equal z and lower y) or (equal zy and lower x) - // (equal zyx and j <= i) - // latter excludes self-self interaction but allows superposed atoms - - ibin = atom2bin[i]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - cutsq = cutneighsq[itype]; - ns = nstencil_multi_old[itype]; - for (k = 0; k < ns; k++) { - for (j = binhead[ibin+s[k]]; j >= 0; j = bins[j]) { - jtype = type[j]; - if (cutsq[jtype] < distsq[k]) continue; - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp) { - if (x[j][0] < xtmp) continue; - if (x[j][0] == xtmp && j <= i) continue; - } - } - - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutdistsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutdistsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_size_multi_old_newton_tri_omp.h b/src/OPENMP/npair_half_size_multi_old_newton_tri_omp.h deleted file mode 100644 index 3c1765b668..0000000000 --- a/src/OPENMP/npair_half_size_multi_old_newton_tri_omp.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/multi/old/newton/tri/omp, - NPairHalfSizeMultiOldNewtonTriOmp, - NP_HALF | NP_SIZE | NP_MULTI_OLD | NP_NEWTON | NP_TRI | NP_OMP); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_MULTI_OLD_NEWTON_TRI_OMP_H -#define LMP_NPAIR_HALF_SIZE_MULTI_OLD_NEWTON_TRI_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeMultiOldNewtonTriOmp : public NPair { - public: - NPairHalfSizeMultiOldNewtonTriOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_size_nsq_newtoff_omp.cpp b/src/OPENMP/npair_half_size_nsq_newtoff_omp.cpp deleted file mode 100644 index 020551bd8e..0000000000 --- a/src/OPENMP/npair_half_size_nsq_newtoff_omp.cpp +++ /dev/null @@ -1,147 +0,0 @@ -// 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 "npair_half_size_nsq_newtoff_omp.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "group.h" -#include "my_page.h" -#include "neigh_list.h" -#include "npair_omp.h" - -#include "omp_compat.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeNsqNewtoffOmp::NPairHalfSizeNsqNewtoffOmp(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - N^2 / 2 search for neighbor pairs with partial Newton's 3rd law - shear history must be accounted for when a neighbor pair is added - pair added to list if atoms i and j are both owned and i < j - pair added if j is ghost (also stored by proc owning j) -------------------------------------------------------------------------- */ - -void NPairHalfSizeNsqNewtoffOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int bitmask = (includegroup) ? group->bitmask[includegroup] : 0; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - const int history = list->history; - const int mask_history = 1 << HISTBITS; - - NPAIR_OMP_INIT; - -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,jh,n,which,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutsq; - int *neighptr; - - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int nall = atom->nlocal + atom->nghost; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over remaining atoms, owned and ghost - - for (j = i+1; j < nall; j++) { - if (includegroup && !(mask[j] & bitmask)) continue; - if (exclude && exclusion(i,j,type[i],type[j],mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_size_nsq_newtoff_omp.h b/src/OPENMP/npair_half_size_nsq_newtoff_omp.h deleted file mode 100644 index 93f038eed4..0000000000 --- a/src/OPENMP/npair_half_size_nsq_newtoff_omp.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/nsq/newtoff/omp, - NPairHalfSizeNsqNewtoffOmp, - NP_HALF | NP_SIZE | NP_NSQ | NP_NEWTOFF | NP_OMP | - NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_NSQ_NEWTOFF_OMP_H -#define LMP_NPAIR_HALF_SIZE_NSQ_NEWTOFF_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeNsqNewtoffOmp : public NPair { - public: - NPairHalfSizeNsqNewtoffOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_half_size_nsq_newton_omp.cpp b/src/OPENMP/npair_half_size_nsq_newton_omp.cpp deleted file mode 100644 index 35dc42ec5b..0000000000 --- a/src/OPENMP/npair_half_size_nsq_newton_omp.cpp +++ /dev/null @@ -1,166 +0,0 @@ -// 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 "npair_half_size_nsq_newton_omp.h" - -#include "atom.h" -#include "atom_vec.h" -#include "domain.h" -#include "error.h" -#include "molecule.h" -#include "group.h" -#include "my_page.h" -#include "neigh_list.h" -#include "npair_omp.h" - -#include "omp_compat.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfSizeNsqNewtonOmp::NPairHalfSizeNsqNewtonOmp(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - size particles - N^2 / 2 search for neighbor pairs with full Newton's 3rd law - shear history must be accounted for when a neighbor pair is added - pair added to list if atoms i and j are both owned and i < j - if j is ghost only me or other proc adds pair - decision based on itag,jtag tests -------------------------------------------------------------------------- */ - -void NPairHalfSizeNsqNewtonOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const int bitmask = (includegroup) ? group->bitmask[includegroup] : 0; - const int molecular = atom->molecular; - const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - const int history = list->history; - const int mask_history = 1 << HISTBITS; - - NPAIR_OMP_INIT; - -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i,j,jh,n,itag,jtag,which,imol,iatom; - tagint tagprev; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutsq; - int *neighptr; - - double **x = atom->x; - double *radius = atom->radius; - tagint *tag = atom->tag; - int *type = atom->type; - int *mask = atom->mask; - tagint *molecule = atom->molecule; - tagint **special = atom->special; - int **nspecial = atom->nspecial; - - int *molindex = atom->molindex; - int *molatom = atom->molatom; - Molecule **onemols = atom->avec->onemols; - - int nall = atom->nlocal + atom->nghost; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - for (i = ifrom; i < ito; i++) { - - n = 0; - neighptr = ipage.vget(); - - itag = tag[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - radi = radius[i]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; - } - - // loop over remaining atoms, owned and ghost - - for (j = i+1; j < nall; j++) { - if (includegroup && !(mask[j] & bitmask)) continue; - - if (j >= nlocal) { - jtag = tag[j]; - if (itag > jtag) { - if ((itag+jtag) % 2 == 0) continue; - } else if (itag < jtag) { - if ((itag+jtag) % 2 == 1) continue; - } else { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - } - - if (exclude && exclusion(i,j,type[i],type[j],mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; -} diff --git a/src/OPENMP/npair_half_size_nsq_newton_omp.h b/src/OPENMP/npair_half_size_nsq_newton_omp.h deleted file mode 100644 index 9a7e912007..0000000000 --- a/src/OPENMP/npair_half_size_nsq_newton_omp.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/size/nsq/newton/omp, - NPairHalfSizeNsqNewtonOmp, - NP_HALF | NP_SIZE | NP_NSQ | NP_NEWTON | NP_OMP | - NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_SIZE_NSQ_NEWTON_OMP_H -#define LMP_NPAIR_HALF_SIZE_NSQ_NEWTON_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalfSizeNsqNewtonOmp : public NPair { - public: - NPairHalfSizeNsqNewtonOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_halffull_newtoff_omp.cpp b/src/OPENMP/npair_halffull_newtoff_omp.cpp deleted file mode 100644 index 379ae149f8..0000000000 --- a/src/OPENMP/npair_halffull_newtoff_omp.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// 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 "npair_halffull_newtoff_omp.h" - -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" -#include "npair_omp.h" - -#include "omp_compat.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalffullNewtoffOmp::NPairHalffullNewtoffOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - build half list from full list - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) - works if full list is a skip list -------------------------------------------------------------------------- */ - -void NPairHalffullNewtoffOmp::build(NeighList *list) -{ - const int inum_full = list->listfull->inum; - - NPAIR_OMP_INIT; - -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(inum_full); - - int i,j,ii,jj,n,jnum,joriginal; - int *neighptr,*jlist; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - int *ilist_full = list->listfull->ilist; - int *numneigh_full = list->listfull->numneigh; - int **firstneigh_full = list->listfull->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - // loop over atoms in full list - - for (ii = ifrom; ii < ito; ii++) { - - n = 0; - neighptr = ipage.vget(); - - // loop over parent full list - - i = ilist_full[ii]; - jlist = firstneigh_full[i]; - jnum = numneigh_full[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (j > i) neighptr[n++] = joriginal; - } - - ilist[ii] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = inum_full; -} diff --git a/src/OPENMP/npair_halffull_newtoff_omp.h b/src/OPENMP/npair_halffull_newtoff_omp.h deleted file mode 100644 index f0ff24dcf9..0000000000 --- a/src/OPENMP/npair_halffull_newtoff_omp.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(halffull/newtoff/omp, - NPairHalffullNewtoffOmp, - NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | - NP_ORTHO | NP_TRI |NP_OMP); - -NPairStyle(halffull/newtoff/skip/omp, - NPairHalffullNewtoffOmp, - NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | - NP_ORTHO | NP_TRI | NP_SKIP | NP_OMP); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALFFULL_NEWTOFF_OMP_H -#define LMP_NPAIR_HALFFULL_NEWTOFF_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalffullNewtoffOmp : public NPair { - public: - NPairHalffullNewtoffOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_halffull_newtoff_trim_omp.h b/src/OPENMP/npair_halffull_newtoff_trim_omp.h deleted file mode 100644 index 19e1c55eeb..0000000000 --- a/src/OPENMP/npair_halffull_newtoff_trim_omp.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(halffull/newtoff/trim/omp, - NPairHalffullNewtoffTrimOmp, - NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | - NP_ORTHO | NP_TRI | NP_TRIM | NP_OMP); - -NPairStyle(halffull/newtoff/skip/trim/omp, - NPairHalffullNewtoffTrimOmp, - NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | - NP_ORTHO | NP_TRI | NP_SKIP | NP_TRIM | NP_OMP); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALFFULL_NEWTOFF_TRIM_OMP_H -#define LMP_NPAIR_HALFFULL_NEWTOFF_TRIM_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalffullNewtoffTrimOmp : public NPair { - public: - NPairHalffullNewtoffTrimOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_halffull_newton_omp.cpp b/src/OPENMP/npair_halffull_newton_omp.cpp deleted file mode 100644 index abd5f7eacb..0000000000 --- a/src/OPENMP/npair_halffull_newton_omp.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// 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 "npair_halffull_newton_omp.h" - -#include "atom.h" -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" -#include "npair_omp.h" - -#include "omp_compat.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalffullNewtonOmp::NPairHalffullNewtonOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - build half list from full list - pair stored once if i,j are both owned and i < j - if j is ghost, only store if j coords are "above and to the right" of i - works if full list is a skip list -------------------------------------------------------------------------- */ - -void NPairHalffullNewtonOmp::build(NeighList *list) -{ - const int inum_full = list->listfull->inum; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(inum_full); - - int i,j,ii,jj,n,jnum,joriginal; - int *neighptr,*jlist; - double xtmp,ytmp,ztmp; - - double **x = atom->x; - int nlocal = atom->nlocal; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - int *ilist_full = list->listfull->ilist; - int *numneigh_full = list->listfull->numneigh; - int **firstneigh_full = list->listfull->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - // loop over parent full list - - for (ii = ifrom; ii < ito; ii++) { - - n = 0; - neighptr = ipage.vget(); - - i = ilist_full[ii]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - - // loop over full neighbor list - - jlist = firstneigh_full[i]; - jnum = numneigh_full[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (j < nlocal) { - if (i > j) continue; - } else { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - neighptr[n++] = joriginal; - } - - ilist[ii] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = inum_full; -} diff --git a/src/OPENMP/npair_halffull_newton_omp.h b/src/OPENMP/npair_halffull_newton_omp.h deleted file mode 100644 index 3ce9fd9ebe..0000000000 --- a/src/OPENMP/npair_halffull_newton_omp.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(halffull/newton/omp, - NPairHalffullNewtonOmp, - NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_ORTHO | NP_TRI| NP_OMP); - -NPairStyle(halffull/newton/skip/omp, - NPairHalffullNewtonOmp, - NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_ORTHO | NP_TRI | NP_SKIP | NP_OMP); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALFFULL_NEWTON_OMP_H -#define LMP_NPAIR_HALFFULL_NEWTON_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalffullNewtonOmp : public NPair { - public: - NPairHalffullNewtonOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_halffull_newton_trim_omp.cpp b/src/OPENMP/npair_halffull_newton_trim_omp.cpp deleted file mode 100644 index 1446175013..0000000000 --- a/src/OPENMP/npair_halffull_newton_trim_omp.cpp +++ /dev/null @@ -1,120 +0,0 @@ -// 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 "npair_halffull_newton_trim_omp.h" - -#include "atom.h" -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" -#include "npair_omp.h" - -#include "omp_compat.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalffullNewtonTrimOmp::NPairHalffullNewtonTrimOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - build half list from full list and trim to shorter cutoff - pair stored once if i,j are both owned and i < j - if j is ghost, only store if j coords are "above and to the right" of i - works if full list is a skip list -------------------------------------------------------------------------- */ - -void NPairHalffullNewtonTrimOmp::build(NeighList *list) -{ - const int inum_full = list->listfull->inum; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(inum_full); - - int i,j,ii,jj,n,jnum,joriginal; - int *neighptr,*jlist; - double xtmp,ytmp,ztmp; - double delx,dely,delz,rsq; - - double **x = atom->x; - int nlocal = atom->nlocal; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - int *ilist_full = list->listfull->ilist; - int *numneigh_full = list->listfull->numneigh; - int **firstneigh_full = list->listfull->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - double cutsq_custom = cutoff_custom * cutoff_custom; - - // loop over parent full list - - for (ii = ifrom; ii < ito; ii++) { - - n = 0; - neighptr = ipage.vget(); - - i = ilist_full[ii]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - - // loop over full neighbor list - - jlist = firstneigh_full[i]; - jnum = numneigh_full[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (j < nlocal) { - if (i > j) continue; - } else { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - - // trim to shorter cutoff - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx * delx + dely * dely + delz * delz; - - if (rsq > cutsq_custom) continue; - - neighptr[n++] = joriginal; - } - - ilist[ii] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = inum_full; -} diff --git a/src/OPENMP/npair_halffull_newton_trim_omp.h b/src/OPENMP/npair_halffull_newton_trim_omp.h deleted file mode 100644 index 4cb84f1b3a..0000000000 --- a/src/OPENMP/npair_halffull_newton_trim_omp.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(halffull/newton/trim/omp, - NPairHalffullNewtonTrimOmp, - NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_ORTHO | NP_TRI| NP_TRIM | NP_OMP); - -NPairStyle(halffull/newton/skip/trim/omp, - NPairHalffullNewtonTrimOmp, - NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_ORTHO | NP_TRI | NP_SKIP | NP_TRIM | NP_OMP); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALFFULL_NEWTON_TRIM_OMP_H -#define LMP_NPAIR_HALFFULL_NEWTON_TRIM_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairHalffullNewtonTrimOmp : public NPair { - public: - NPairHalffullNewtonTrimOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_halffull_newtoff_trim_omp.cpp b/src/OPENMP/npair_halffull_omp.cpp similarity index 59% rename from src/OPENMP/npair_halffull_newtoff_trim_omp.cpp rename to src/OPENMP/npair_halffull_omp.cpp index d35b3b2ee8..559d8910b1 100644 --- a/src/OPENMP/npair_halffull_newtoff_trim_omp.cpp +++ b/src/OPENMP/npair_halffull_omp.cpp @@ -12,7 +12,7 @@ See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ -#include "npair_halffull_newtoff_trim_omp.h" +#include "npair_halffull_omp.h" #include "atom.h" #include "error.h" @@ -26,16 +26,24 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -NPairHalffullNewtoffTrimOmp::NPairHalffullNewtoffTrimOmp(LAMMPS *lmp) : NPair(lmp) {} +template +NPairHalffullOmp::NPairHalffullOmp(LAMMPS *lmp) : NPair(lmp) {} /* ---------------------------------------------------------------------- - build half list from full list and trim to shorter cutoff + build half list from full list pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) works if full list is a skip list + + Newtoff: + pair stored by me if j is ghost (also stored by proc owning j) + works for owned (non-ghost) list, also for ghost list + if ghost, also store neighbors of ghost atoms & set inum,gnum correctly + Newton: + if j is ghost, only store if j coords are "above and to the right" of i ------------------------------------------------------------------------- */ -void NPairHalffullNewtoffTrimOmp::build(NeighList *list) +template +void NPairHalffullOmp::build(NeighList *list) { const int inum_full = list->listfull->inum; @@ -52,6 +60,7 @@ void NPairHalffullNewtoffTrimOmp::build(NeighList *list) double delx,dely,delz,rsq; double **x = atom->x; + int nlocal = atom->nlocal; int *ilist = list->ilist; int *numneigh = list->numneigh; @@ -76,10 +85,6 @@ void NPairHalffullNewtoffTrimOmp::build(NeighList *list) // loop over parent full list i = ilist_full[ii]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - jlist = firstneigh_full[i]; jnum = numneigh_full[i]; @@ -87,16 +92,39 @@ void NPairHalffullNewtoffTrimOmp::build(NeighList *list) joriginal = jlist[jj]; j = joriginal & NEIGHMASK; - // trim to shorter cutoff + if (NEWTON) { + if (j < nlocal) { + if (i > j) continue; + } else { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + } + } - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx * delx + dely * dely + delz * delz; + if (TRIM) { + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx * delx + dely * dely + delz * delz; - if (rsq > cutsq_custom) continue; + if (rsq > cutsq_custom) continue; + } + neighptr[n++] = joriginal; + } else { + if (j > i) { + if (TRIM) { + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx * delx + dely * dely + delz * delz; - if (j > i) neighptr[n++] = joriginal; + if (rsq > cutsq_custom) continue; + } + neighptr[n++] = joriginal; + } + } } ilist[ii] = i; @@ -109,3 +137,10 @@ void NPairHalffullNewtoffTrimOmp::build(NeighList *list) NPAIR_OMP_CLOSE; list->inum = inum_full; } + +namespace LAMMPS_NS { +template class NPairHalffullOmp<0,0>; +template class NPairHalffullOmp<1,0>; +template class NPairHalffullOmp<0,1>; +template class NPairHalffullOmp<1,1>; +} diff --git a/src/OPENMP/npair_halffull_omp.h b/src/OPENMP/npair_halffull_omp.h new file mode 100644 index 0000000000..fca7fe37e8 --- /dev/null +++ b/src/OPENMP/npair_halffull_omp.h @@ -0,0 +1,107 @@ +/* -*- 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 NPAIR_CLASS +// clang-format off +typedef NPairHalffullOmp<0, 0> NPairHalffullOmpNewtoffOmp; +NPairStyle(halffull/newtoff/omp, + NPairHalffullOmpNewtoffOmp, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | + NP_ORTHO | NP_TRI | NP_OMP); + +typedef NPairHalffullOmp<0, 0> NPairHalffullOmpNewtoffOmp; +NPairStyle(halffull/newtoff/skip/omp, + NPairHalffullOmpNewtoffOmp, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | + NP_ORTHO | NP_TRI | NP_SKIP | NP_OMP); + +typedef NPairHalffullOmp<0, 0> NPairHalffullOmpNewtoffOmp; +NPairStyle(halffull/newtoff/ghost/omp, + NPairHalffullOmpNewtoffOmp, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | + NP_ORTHO | NP_TRI | NP_GHOST | NP_OMP); + +typedef NPairHalffullOmp<0, 0> NPairHalffullOmpNewtoffOmp; +NPairStyle(halffull/newtoff/skip/ghost/omp, + NPairHalffullOmpNewtoffOmp, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | + NP_ORTHO | NP_TRI | NP_SKIP | NP_GHOST | NP_OMP); + +typedef NPairHalffullOmp<1, 0> NPairHalffullOmpNewtonOmp; +NPairStyle(halffull/newton/omp, + NPairHalffullOmpNewtonOmp, + NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_ORTHO | NP_TRI | NP_OMP); + +typedef NPairHalffullOmp<1, 0> NPairHalffullOmpNewtonOmp; +NPairStyle(halffull/newton/skip/omp, + NPairHalffullOmpNewtonOmp, + NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_ORTHO | NP_TRI | NP_SKIP | NP_OMP); + +typedef NPairHalffullOmp<0, 1> NPairHalffullOmpNewtoffTrimOmp; +NPairStyle(halffull/newtoff/trim/omp, + NPairHalffullOmpNewtoffTrimOmp, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | + NP_ORTHO | NP_TRI | NP_TRIM | NP_OMP); + +typedef NPairHalffullOmp<0, 1> NPairHalffullOmpNewtoffTrimOmp; +NPairStyle(halffull/newtoff/skip/trim/omp, + NPairHalffullOmpNewtoffTrimOmp, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | + NP_ORTHO | NP_TRI | NP_SKIP | NP_TRIM | NP_OMP); + +typedef NPairHalffullOmp<0, 1> NPairHalffullOmpNewtoffTrimOmp; +NPairStyle(halffull/newtoff/ghost/trim/omp, + NPairHalffullOmpNewtoffTrimOmp, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | + NP_ORTHO | NP_TRI | NP_GHOST | NP_TRIM | NP_OMP); + +typedef NPairHalffullOmp<0, 1> NPairHalffullOmpNewtoffTrimOmp; +NPairStyle(halffull/newtoff/skip/ghost/trim/omp, + NPairHalffullOmpNewtoffTrimOmp, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_HALF | + NP_ORTHO | NP_TRI | NP_SKIP | NP_GHOST | NP_TRIM | NP_OMP); + +typedef NPairHalffullOmp<1, 1> NPairHalffullOmpNewtonTrimOmp; +NPairStyle(halffull/newton/trim/omp, + NPairHalffullOmpNewtonTrimOmp, + NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_ORTHO | NP_TRI | NP_TRIM | NP_OMP); + +typedef NPairHalffullOmp<1, 1> NPairHalffullOmpNewtonTrimOmp; +NPairStyle(halffull/newton/skip/trim/omp, + NPairHalffullOmpNewtonTrimOmp, + NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_ORTHO | NP_TRI | NP_SKIP | NP_TRIM | NP_OMP); +// clang-format on +#else + +#ifndef LMP_NPAIR_HALFFULL_OMP_H +#define LMP_NPAIR_HALFFULL_OMP_H + +#include "npair.h" + +namespace LAMMPS_NS { + +template +class NPairHalffullOmp : public NPair { + public: + NPairHalffullOmp(class LAMMPS *); + void build(class NeighList *) override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif diff --git a/src/OPENMP/npair_multi_old_omp.cpp b/src/OPENMP/npair_multi_old_omp.cpp new file mode 100644 index 0000000000..1c2e86df80 --- /dev/null +++ b/src/OPENMP/npair_multi_old_omp.cpp @@ -0,0 +1,243 @@ +/* ---------------------------------------------------------------------- + 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 "npair_multi_old_omp.h" +#include "atom.h" +#include "atom_vec.h" +#include "domain.h" +#include "error.h" +#include "molecule.h" +#include "my_page.h" +#include "neigh_list.h" +#include "npair_omp.h" +#include "omp_compat.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +template +NPairMultiOldOmp::NPairMultiOldOmp(LAMMPS *lmp) : NPair(lmp) {} + +/* ---------------------------------------------------------------------- + multi/old-type stencil is itype dependent and is distance checked + Full: + binned neighbor list construction for all neighbors + multi-type stencil is itype dependent and is distance checked + every neighbor pair appears in list of both atoms i and j + Half + newtoff: + binned neighbor list construction with partial Newton's 3rd law + each owned atom i checks own bin and other bins in stencil + multi-type stencil is itype dependent and is distance checked + pair stored once if i,j are both owned and i < j + pair stored by me if j is ghost (also stored by proc owning j) + Half + newton: + binned neighbor list construction with full Newton's 3rd law + each owned atom i checks its own bin and other bins in Newton stencil + multi-type stencil is itype dependent and is distance checked + every pair stored exactly once by some processor +------------------------------------------------------------------------- */ + +template +void NPairMultiOldOmp::build(NeighList *list) +{ + const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; + const int molecular = atom->molecular; + const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; + + NPAIR_OMP_INIT; +#if defined(_OPENMP) +#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) +#endif + NPAIR_OMP_SETUP(nlocal); + + int i, j, jh, k, n, itype, jtype, ibin, bin_start, which, ns, imol, iatom; + tagint tagprev; + double xtmp, ytmp, ztmp, delx, dely, delz, rsq; + double radsum,cut,cutsq; + int *neighptr, *s; + double *cutnsq, *distsq; + + // loop over each atom, storing neighbors + + double **x = atom->x; + double *radius = atom->radius; + int *type = atom->type; + int *mask = atom->mask; + tagint *tag = atom->tag; + tagint *molecule = atom->molecule; + tagint **special = atom->special; + int **nspecial = atom->nspecial; + + int *molindex = atom->molindex; + int *molatom = atom->molatom; + Molecule **onemols = atom->avec->onemols; + + int history = list->history; + int mask_history = 1 << HISTBITS; + + int *ilist = list->ilist; + int *numneigh = list->numneigh; + int **firstneigh = list->firstneigh; + + // each thread has its own page allocator + MyPage &ipage = list->ipage[tid]; + ipage.reset(); + + for (i = ifrom; i < ito; i++) { + + n = 0; + neighptr = ipage.vget(); + + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + if (moltemplate) { + imol = molindex[i]; + iatom = molatom[i]; + tagprev = tag[i] - iatom - 1; + } + + ibin = atom2bin[i]; + s = stencil_multi_old[itype]; + distsq = distsq_multi_old[itype]; + cutnsq = cutneighsq[itype]; + ns = nstencil_multi_old[itype]; + for (k = 0; k < ns; k++) { + bin_start = binhead[ibin+stencil[k]]; + if (stencil[k] == 0) { + if (HALF && NEWTON && (!TRI)) { + // Half neighbor list, newton on, orthonormal + // loop over rest of atoms in i's bin, ghosts are at end of linked list + bin_start = bins[i]; + } + } + + for (j = bin_start; j >= 0; j = bins[j]) { + if (!HALF) { + // Full neighbor list + // only skip i = j + if (i == j) continue; + } else if (!NEWTON) { + // Half neighbor list, newton off + // only store pair if i < j + // stores own/own pairs only once + // stores own/ghost pairs on both procs + if (j <= i) continue; + } else if (TRI) { + // Half neighbor list, newton on, triclinic + // pairs for atoms j "below" i are excluded + // below = lower z or (equal z and lower y) or (equal zy and lower x) + // (equal zyx and j <= i) + // latter excludes self-self interaction but allows superposed atoms + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp) { + if (x[j][0] < xtmp) continue; + if (x[j][0] == xtmp && j <= i) continue; + } + } + } else { + // Half neighbor list, newton on, orthonormal + // store every pair for every bin in stencil,except for i's bin + + if (stencil[k] == 0) { + // if j is owned atom, store it, since j is beyond i in linked list + // if j is ghost, only store if j coords are "above and to the "right" of i + if (j >= nlocal) { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + } + } + } + } + + jtype = type[j]; + if (cutnsq[jtype] < distsq[k]) continue; + + if (exclude && exclusion(i, j, itype, jtype, mask, molecule)) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx * delx + dely * dely + delz * delz; + + if (SIZE) { + radsum = radius[i] + radius[j]; + cut = radsum + skin; + cutsq = cut * cut; + + if (rsq <= cutsq) { + jh = j; + if (history && rsq < radsum * radsum) + jh = jh ^ mask_history; + + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = jh; + else if (domain->minimum_image_check(delx,dely,delz)) + neighptr[n++] = jh; + else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); + } else neighptr[n++] = jh; + } + } else { + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (domain->minimum_image_check(delx,dely,delz)) + neighptr[n++] = j; + else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); + } else neighptr[n++] = j; + } + } + } + } + + ilist[i] = i; + firstneigh[i] = neighptr; + numneigh[i] = n; + ipage.vgot(n); + if (ipage.status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); + } + NPAIR_OMP_CLOSE; + list->inum = nlocal; + list->gnum = 0; +} + +namespace LAMMPS_NS { +template class NPairMultiOldOmp<0,1,0,0>; +template class NPairMultiOldOmp<1,0,0,0>; +template class NPairMultiOldOmp<1,1,0,0>; +template class NPairMultiOldOmp<1,1,1,0>; +template class NPairMultiOldOmp<0,1,0,1>; +template class NPairMultiOldOmp<1,0,0,1>; +template class NPairMultiOldOmp<1,1,0,1>; +template class NPairMultiOldOmp<1,1,1,1>; +} diff --git a/src/OPENMP/npair_multi_old_omp.h b/src/OPENMP/npair_multi_old_omp.h new file mode 100644 index 0000000000..4251c6ed48 --- /dev/null +++ b/src/OPENMP/npair_multi_old_omp.h @@ -0,0 +1,77 @@ +/* -*- 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 NPAIR_CLASS +// clang-format off +typedef NPairMultiOldOmp<0, 1, 0, 0> NPairFullMultiOldOmp; +NPairStyle(full/multi/old/omp, + NPairFullMultiOldOmp, + NP_FULL | NP_MULTI_OLD | NP_OMP | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMultiOldOmp<1, 0, 0, 0> NPairHalfMultiOldNewtoffOmp; +NPairStyle(half/multi/old/newtoff/omp, + NPairHalfMultiOldNewtoffOmp, + NP_HALF | NP_MULTI_OLD | NP_OMP | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMultiOldOmp<1, 1, 0, 0> NPairHalfMultiOldNewtonOmp; +NPairStyle(half/multi/old/newton/omp, + NPairHalfMultiOldNewtonOmp, + NP_HALF | NP_MULTI_OLD | NP_OMP | NP_NEWTON | NP_ORTHO); + +typedef NPairMultiOldOmp<1, 1, 1, 0> NPairHalfMultiOldNewtonTriOmp; +NPairStyle(half/multi/old/newton/tri/omp, + NPairHalfMultiOldNewtonTriOmp, + NP_HALF | NP_MULTI_OLD | NP_OMP | NP_NEWTON | NP_TRI); + +typedef NPairMultiOldOmp<0, 1, 0, 1> NPairFullSizeMultiOldOmp; +NPairStyle(full/size/multi/old/omp, + NPairFullSizeMultiOldOmp, + NP_FULL | NP_SIZE | NP_MULTI_OLD | NP_OMP | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMultiOldOmp<1, 0, 0, 1> NPairHalfSizeMultiOldNewtoffOmp; +NPairStyle(half/size/multi/old/newtoff/omp, + NPairHalfSizeMultiOldNewtoffOmp, + NP_HALF | NP_SIZE | NP_MULTI_OLD | NP_OMP | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMultiOldOmp<1, 1, 0, 1> NPairHalfSizeMultiOldNewtonOmp; +NPairStyle(half/size/multi/old/newton/omp, + NPairHalfSizeMultiOldNewtonOmp, + NP_HALF | NP_SIZE | NP_MULTI_OLD | NP_OMP | NP_NEWTON | NP_ORTHO); + +typedef NPairMultiOldOmp<1, 1, 1, 1> NPairHalfSizeMultiOldNewtonTriOmp; +NPairStyle(half/size/multi/old/newton/tri/omp, + NPairHalfSizeMultiOldNewtonTriOmp, + NP_HALF | NP_SIZE | NP_MULTI_OLD | NP_OMP | NP_NEWTON | NP_TRI); +// clang-format on +#else + +#ifndef LMP_NPAIR_MULTI_OLD_OMP_H +#define LMP_NPAIR_MULTI_OLD_OMP_H + +#include "npair.h" + +namespace LAMMPS_NS { + +template +class NPairMultiOldOmp : public NPair { + public: + NPairMultiOldOmp(class LAMMPS *); + void build(class NeighList *) override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif diff --git a/src/OPENMP/npair_multi_omp.cpp b/src/OPENMP/npair_multi_omp.cpp new file mode 100644 index 0000000000..bbdce96ffc --- /dev/null +++ b/src/OPENMP/npair_multi_omp.cpp @@ -0,0 +1,247 @@ +// 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 "omp_compat.h" +#include "npair_multi_omp.h" +#include "npair_omp.h" +#include "neighbor.h" +#include "neigh_list.h" +#include "atom.h" +#include "atom_vec.h" +#include "molecule.h" +#include "domain.h" +#include "my_page.h" +#include "error.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +template +NPairMultiOmp::NPairMultiOmp(LAMMPS *lmp) : NPair(lmp) {} + +/* ---------------------------------------------------------------------- + multi stencil is icollection-jcollection dependent + Full: + binned neighbor list construction for all neighbors + every neighbor pair appears in list of both atoms i and j + Half + Newtoff: + binned neighbor list construction with partial Newton's 3rd law + each owned atom i checks own bin and other bins in stencil + pair stored once if i,j are both owned and i < j + pair stored by me if j is ghost (also stored by proc owning j) + Half + Newton: + binned neighbor list construction with full Newton's 3rd law + each owned atom i checks its own bin and other bins in Newton stencil + every pair stored exactly once by some processor +------------------------------------------------------------------------- */ + +template +void NPairMultiOmp::build(NeighList *list) +{ + const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; + const int molecular = atom->molecular; + const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; + + NPAIR_OMP_INIT; +#if defined(_OPENMP) +#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) +#endif + NPAIR_OMP_SETUP(nlocal); + + int i,j,jh,js,k,n,itype,jtype,icollection,jcollection,ibin,jbin,which,ns,imol,iatom; + tagint tagprev; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + double radsum,cut,cutsq; + int *neighptr,*s; + + // loop over each atom, storing neighbors + + int *collection = neighbor->collection; + double **x = atom->x; + double *radius = atom->radius; + int *type = atom->type; + int *mask = atom->mask; + tagint *tag = atom->tag; + tagint *molecule = atom->molecule; + tagint **special = atom->special; + int **nspecial = atom->nspecial; + + int *molindex = atom->molindex; + int *molatom = atom->molatom; + Molecule **onemols = atom->avec->onemols; + + int history = list->history; + int mask_history = 1 << HISTBITS; + + int *ilist = list->ilist; + int *numneigh = list->numneigh; + int **firstneigh = list->firstneigh; + + // each thread has its own page allocator + MyPage &ipage = list->ipage[tid]; + ipage.reset(); + + for (i = ifrom; i < ito; i++) { + + n = 0; + neighptr = ipage.vget(); + + itype = type[i]; + icollection = collection[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + if (moltemplate) { + imol = molindex[i]; + iatom = molatom[i]; + tagprev = tag[i] - iatom - 1; + } + + ibin = atom2bin[i]; + + // loop through stencils for all collections + for (jcollection = 0; jcollection < ncollections; jcollection++) { + + // if same collection use own bin + if (icollection == jcollection) jbin = ibin; + else jbin = coord2bin(x[i], jcollection); + + // loop over all atoms in surrounding bins in stencil including self + // skip i = j + // use full stencil for all collection combinations + + s = stencil_multi[icollection][jcollection]; + ns = nstencil_multi[icollection][jcollection]; + + for (k = 0; k < ns; k++) { + js = binhead_multi[jcollection][jbin + s[k]]; + for (j = js; j >= 0; j = bins[j]) { + if (!HALF) { + // Full neighbor list + // only skip i = j + if (i == j) continue; + } else if (!NEWTON) { + // Half neighbor list, newton off + // only store pair if i < j + // stores own/own pairs only once + // stores own/ghost pairs on both procs + if (j <= i) continue; + } else if (TRI) { + // Half neighbor list, newton on, triclinic + // pairs for atoms j "below" i are excluded + // below = lower z or (equal z and lower y) or (equal zy and lower x) + // (equal zyx and j <= i) + // latter excludes self-self interaction but allows superposed atoms + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp) { + if (x[j][0] < xtmp) continue; + if (x[j][0] == xtmp && j <= i) continue; + } + } + } else { + // Half neighbor list, newton on, orthonormal + // store every pair for every bin in stencil,except for i's bin + + if (stencil[k] == 0) { + // if j is owned atom, store it, since j is beyond i in linked list + // if j is ghost, only store if j coords are "above and to the "right" of i + if (j >= nlocal) { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + } + } + } + } + + jtype = type[j]; + if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + + if (SIZE) { + radsum = radius[i] + radius[j]; + cut = radsum + skin; + cutsq = cut * cut; + + if (rsq <= cutsq) { + jh = j; + if (history && rsq < radsum * radsum) + jh = jh ^ mask_history; + + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = jh; + else if (domain->minimum_image_check(delx,dely,delz)) + neighptr[n++] = jh; + else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); + } else neighptr[n++] = jh; + } + } else { + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (domain->minimum_image_check(delx,dely,delz)) + neighptr[n++] = j; + else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); + } else neighptr[n++] = j; + } + } + } + } + } + + ilist[i] = i; + firstneigh[i] = neighptr; + numneigh[i] = n; + ipage.vgot(n); + if (ipage.status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + } + NPAIR_OMP_CLOSE; + list->inum = nlocal; + list->gnum = 0; +} + +namespace LAMMPS_NS { +template class NPairMultiOmp<0,1,0,0>; +template class NPairMultiOmp<1,0,0,0>; +template class NPairMultiOmp<1,1,0,0>; +template class NPairMultiOmp<1,1,1,0>; +template class NPairMultiOmp<0,1,0,1>; +template class NPairMultiOmp<1,0,0,1>; +template class NPairMultiOmp<1,1,0,1>; +template class NPairMultiOmp<1,1,1,1>; +} diff --git a/src/OPENMP/npair_multi_omp.h b/src/OPENMP/npair_multi_omp.h new file mode 100644 index 0000000000..dd85ca8a8e --- /dev/null +++ b/src/OPENMP/npair_multi_omp.h @@ -0,0 +1,75 @@ +/* -*- 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 NPAIR_CLASS +// clang-format off +typedef NPairMultiOmp<0, 1, 0, 0> NPairFullMultiOmp; +NPairStyle(full/multi/omp, + NPairFullMultiOmp, + NP_FULL | NP_MULTI | NP_OMP | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMultiOmp<1, 0, 0, 0> NPairHalfMultiNewtoffOmp; +NPairStyle(half/multi/newtoff/omp, + NPairHalfMultiNewtoffOmp, + NP_HALF | NP_MULTI | NP_OMP | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMultiOmp<1, 1, 0, 0> NPairHalfMultiNewtonOmp; +NPairStyle(half/multi/newton/omp, + NPairHalfMultiNewtonOmp, + NP_HALF | NP_MULTI | NP_OMP | NP_NEWTON | NP_ORTHO); + +typedef NPairMultiOmp<1, 1, 1, 0> NPairHalfMultiNewtonTriOmp; +NPairStyle(half/multi/newton/tri/omp, + NPairHalfMultiNewtonTriOmp, + NP_HALF | NP_MULTI | NP_OMP | NP_NEWTON | NP_TRI); + +typedef NPairMultiOmp<0, 1, 0, 1> NPairFullSizeMultiOmp; +NPairStyle(full/size/multi/omp, + NPairFullSizeMultiOmp, + NP_FULL | NP_SIZE | NP_MULTI | NP_OMP | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMultiOmp<1, 0, 0, 1> NPairHalfSizeMultiNewtoffOmp; +NPairStyle(half/size/multi/newtoff/omp, + NPairHalfSizeMultiNewtoffOmp, + NP_HALF | NP_SIZE | NP_MULTI | NP_OMP | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMultiOmp<1, 1, 0, 1> NPairHalfSizeMultiNewtonOmp; +NPairStyle(half/size/multi/newton/omp, + NPairHalfSizeMultiNewtonOmp, + NP_HALF | NP_SIZE | NP_MULTI | NP_OMP | NP_NEWTON | NP_ORTHO); + +typedef NPairMultiOmp<1, 1, 1, 1> NPairHalfSizeMultiNewtonTriOmp; +NPairStyle(half/size/multi/newton/tri/omp, + NPairHalfSizeMultiNewtonTriOmp, + NP_HALF | NP_SIZE | NP_MULTI | NP_OMP | NP_NEWTON | NP_TRI); +// clang-format on +#else + +#ifndef LMP_NPAIR_MULTI_OMP_H +#define LMP_NPAIR_MULTI_OMP_H + +#include "npair.h" + +namespace LAMMPS_NS { + +template +class NPairMultiOmp : public NPair { + public: + NPairMultiOmp(class LAMMPS *); + void build(class NeighList *) override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif diff --git a/src/OPENMP/npair_full_nsq_ghost_omp.cpp b/src/OPENMP/npair_nsq_ghost_omp.cpp similarity index 71% rename from src/OPENMP/npair_full_nsq_ghost_omp.cpp rename to src/OPENMP/npair_nsq_ghost_omp.cpp index 05f402a99d..e1e82fb3ff 100644 --- a/src/OPENMP/npair_full_nsq_ghost_omp.cpp +++ b/src/OPENMP/npair_nsq_ghost_omp.cpp @@ -13,7 +13,7 @@ ------------------------------------------------------------------------- */ #include "omp_compat.h" -#include "npair_full_nsq_ghost_omp.h" +#include "npair_nsq_ghost_omp.h" #include "npair_omp.h" #include "neigh_list.h" #include "atom.h" @@ -27,15 +27,24 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -NPairFullNsqGhostOmp::NPairFullNsqGhostOmp(LAMMPS *lmp) : NPair(lmp) {} +template +NPairNsqGhostOmp::NPairNsqGhostOmp(LAMMPS *lmp) : NPair(lmp) {} /* ---------------------------------------------------------------------- - N^2 search for all neighbors - include neighbors of ghost atoms, but no "special neighbors" for ghosts - every neighbor pair appears in list of both atoms i and j + Full: + N^2 search for all neighbors + include neighbors of ghost atoms, but no "special neighbors" for ghosts + every neighbor pair appears in list of both atoms i and j + Half + Newtoff: + N^2 / 2 search for neighbor pairs with partial Newton's 3rd law + include neighbors of ghost atoms, but no "special neighbors" for ghosts + pair stored once if i,j are both owned and i < j + pair stored by me if i owned and j ghost (also stored by proc owning j) + pair stored once if i,j are both ghost and i < j ------------------------------------------------------------------------- */ -void NPairFullNsqGhostOmp::build(NeighList *list) +template +void NPairNsqGhostOmp::build(NeighList *list) { const int nlocal = atom->nlocal; const int nall = nlocal + atom->nghost; @@ -48,7 +57,7 @@ void NPairFullNsqGhostOmp::build(NeighList *list) #endif NPAIR_OMP_SETUP(nall); - int i,j,n,itype,jtype,which,imol,iatom; + int i,j,jstart,n,itype,jtype,which,imol,iatom; tagint tagprev; double xtmp,ytmp,ztmp,delx,dely,delz,rsq; int *neighptr; @@ -91,12 +100,24 @@ void NPairFullNsqGhostOmp::build(NeighList *list) } // loop over all atoms, owned and ghost - // skip i = j + // Full: + // skip i = j + // Half: + // only store pair if i < j + // stores own/own pairs only once + // stores own/ghost pairs with owned atom only, on both procs + // stores ghost/ghost pairs only once // no molecular test when i = ghost atom + if (HALF) jstart = i + 1; + else jstart = 0; + if (i < nlocal) { - for (j = 0; j < nall; j++) { - if (i == j) continue; + for (j = jstart; j < nall; j++) { + if (!HALF) { + if (i == j) continue; + } + jtype = type[j]; if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; @@ -121,8 +142,11 @@ void NPairFullNsqGhostOmp::build(NeighList *list) } } } else { - for (j = 0; j < nall; j++) { - if (i == j) continue; + for (j = jstart; j < nall; j++) { + if (!HALF) { + if (i == j) continue; + } + jtype = type[j]; if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; @@ -131,7 +155,11 @@ void NPairFullNsqGhostOmp::build(NeighList *list) delz = ztmp - x[j][2]; rsq = delx*delx + dely*dely + delz*delz; - if (rsq <= cutneighghostsq[itype][jtype]) neighptr[n++] = j; + if (HALF) { + if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; + } else { + if (rsq <= cutneighghostsq[itype][jtype]) neighptr[n++] = j; + } } } @@ -146,3 +174,8 @@ void NPairFullNsqGhostOmp::build(NeighList *list) list->inum = nlocal; list->gnum = nall - nlocal; } + +namespace LAMMPS_NS { +template class NPairNsqGhostOmp<0>; +template class NPairNsqGhostOmp<1>; +} diff --git a/src/OPENMP/npair_half_nsq_newtoff_ghost_omp.h b/src/OPENMP/npair_nsq_ghost_omp.h similarity index 64% rename from src/OPENMP/npair_half_nsq_newtoff_ghost_omp.h rename to src/OPENMP/npair_nsq_ghost_omp.h index 4df15104c3..1d33758bac 100644 --- a/src/OPENMP/npair_half_nsq_newtoff_ghost_omp.h +++ b/src/OPENMP/npair_nsq_ghost_omp.h @@ -13,23 +13,29 @@ #ifdef NPAIR_CLASS // clang-format off +typedef NPairNsqGhostOmp<0> NPairFullNsqGhostOmp; +NPairStyle(full/nsq/ghost/omp, + NPairFullNsqGhostOmp, + NP_FULL | NP_NSQ | NP_NEWTON | NP_NEWTOFF | NP_GHOST | NP_OMP | NP_ORTHO | NP_TRI); + +typedef NPairNsqGhostOmp<1> NPairHalfNsqNewtoffGhostOmp; NPairStyle(half/nsq/newtoff/ghost/omp, NPairHalfNsqNewtoffGhostOmp, - NP_HALF | NP_NSQ | NP_NEWTOFF | NP_GHOST | NP_OMP | - NP_ORTHO | NP_TRI); + NP_HALF | NP_NSQ | NP_NEWTOFF | NP_GHOST | NP_OMP | NP_ORTHO | NP_TRI); // clang-format on #else -#ifndef LMP_NPAIR_HALF_NSQ_NEWTOFF_GHOST_OMP_H -#define LMP_NPAIR_HALF_NSQ_NEWTOFF_GHOST_OMP_H +#ifndef LMP_NPAIR_NSQ_GHOST_OMP_H +#define LMP_NPAIR_NSQ_GHOST_OMP_H #include "npair.h" namespace LAMMPS_NS { -class NPairHalfNsqNewtoffGhostOmp : public NPair { +template +class NPairNsqGhostOmp : public NPair { public: - NPairHalfNsqNewtoffGhostOmp(class LAMMPS *); + NPairNsqGhostOmp(class LAMMPS *); void build(class NeighList *) override; }; diff --git a/src/OPENMP/npair_half_size_multi_old_newton_omp.cpp b/src/OPENMP/npair_nsq_omp.cpp similarity index 51% rename from src/OPENMP/npair_half_size_multi_old_newton_omp.cpp rename to src/OPENMP/npair_nsq_omp.cpp index 187efe04d1..232fee2d42 100644 --- a/src/OPENMP/npair_half_size_multi_old_newton_omp.cpp +++ b/src/OPENMP/npair_nsq_omp.cpp @@ -12,40 +12,48 @@ See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ -#include "npair_half_size_multi_old_newton_omp.h" - +#include "omp_compat.h" +#include "npair_nsq_omp.h" +#include "npair_omp.h" +#include "neigh_list.h" +#include "neighbor.h" #include "atom.h" #include "atom_vec.h" -#include "domain.h" -#include "error.h" +#include "group.h" #include "molecule.h" +#include "domain.h" #include "my_page.h" -#include "neigh_list.h" -#include "npair_omp.h" +#include "error.h" -#include "omp_compat.h" using namespace LAMMPS_NS; +using namespace NeighConst; /* ---------------------------------------------------------------------- */ -NPairHalfSizeMultiOldNewtonOmp::NPairHalfSizeMultiOldNewtonOmp(LAMMPS *lmp) : - NPair(lmp) {} +template +NPairNsqOmp::NPairNsqOmp(LAMMPS *lmp) : NPair(lmp) {} /* ---------------------------------------------------------------------- - size particles - binned neighbor list construction with full Newton's 3rd law - each owned atom i checks its own bin and other bins in Newton stencil - multi-type stencil is itype dependent and is distance checked - every pair stored exactly once by some processor + Full: + N^2 search for all neighbors + every neighbor pair appears in list of both atoms i and j + Half + Newtoff: + N^2 / 2 search for neighbor pairs with partial Newton's 3rd law + pair stored once if i,j are both owned and i < j + pair stored by me if j is ghost (also stored by proc owning j) + Half + Newton: + N^2 / 2 search for neighbor pairs with full Newton's 3rd law + every pair stored exactly once by some processor + decision on ghost atoms based on itag,jtag tests ------------------------------------------------------------------------- */ -void NPairHalfSizeMultiOldNewtonOmp::build(NeighList *list) +template +void NPairNsqOmp::build(NeighList *list) { const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; + const int bitmask = (includegroup) ? group->bitmask[includegroup] : 0; const int molecular = atom->molecular; const int moltemplate = (molecular == Atom::TEMPLATE) ? 1 : 0; - const int history = list->history; - const int mask_history = 1 << HISTBITS; NPAIR_OMP_INIT; #if defined(_OPENMP) @@ -53,12 +61,11 @@ void NPairHalfSizeMultiOldNewtonOmp::build(NeighList *list) #endif NPAIR_OMP_SETUP(nlocal); - int i,j,jh,k,n,itype,jtype,ibin,ns,which,imol,iatom; - tagint tagprev; + int i,j,jh,jstart,n,itype,jtype,which,imol,iatom; + tagint itag,jtag,tagprev; double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - double radi,radsum,cutdistsq; - int *neighptr,*s; - double *cutsq,*distsq; + double radsum,cut,cutsq; + int *neighptr; double **x = atom->x; double *radius = atom->radius; @@ -69,10 +76,14 @@ void NPairHalfSizeMultiOldNewtonOmp::build(NeighList *list) tagint **special = atom->special; int **nspecial = atom->nspecial; + int nall = atom->nlocal + atom->nghost; int *molindex = atom->molindex; int *molatom = atom->molatom; Molecule **onemols = atom->avec->onemols; + int history = list->history; + int mask_history = 1 << HISTBITS; + int *ilist = list->ilist; int *numneigh = list->numneigh; int **firstneigh = list->firstneigh; @@ -81,6 +92,8 @@ void NPairHalfSizeMultiOldNewtonOmp::build(NeighList *list) MyPage &ipage = list->ipage[tid]; ipage.reset(); + // loop over owned atoms, storing neighbors + for (i = ifrom; i < ito; i++) { n = 0; @@ -90,87 +103,66 @@ void NPairHalfSizeMultiOldNewtonOmp::build(NeighList *list) xtmp = x[i][0]; ytmp = x[i][1]; ztmp = x[i][2]; - radi = radius[i]; if (moltemplate) { imol = molindex[i]; iatom = molatom[i]; tagprev = tag[i] - iatom - 1; } - // loop over rest of atoms in i's bin, ghosts are at end of linked list - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the right" of i + // Full: loop over all atoms, owned and ghost, skip i = j + // Half: loop over remaining atoms, owned and ghost + // Newtoff: only store pair if i < j + // Newton: itag = jtag is possible for long cutoffs that include images of self - for (j = bins[i]; j >= 0; j = bins[j]) { - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + if (!HALF) jstart = 0; + else jstart = i + 1; + + for (j = jstart; j < nall; j++) { + if (includegroup && !(mask[j] & bitmask)) continue; + + if (!HALF) { + // Full neighbor list + if (i == j) continue; + } else if (NEWTON) { + // Half neighbor list, newton on + if (j >= nlocal) { + jtag = tag[j]; + if (itag > jtag) { + if ((itag+jtag) % 2 == 0) continue; + } else if (itag < jtag) { + if ((itag+jtag) % 2 == 1) continue; + } else { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + } + } } } jtype = type[j]; if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; + delx = xtmp - x[j][0]; dely = ytmp - x[j][1]; delz = ztmp - x[j][2]; rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutdistsq = (radsum+skin) * (radsum+skin); - if (rsq <= cutdistsq) { - jh = j; - if (history && rsq < radsum*radsum) - jh = jh ^ mask_history; + if (SIZE) { + radsum = radius[i] + radius[j]; + cut = radsum + skin; + cutsq = cut * cut; - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = jh; - else if (domain->minimum_image_check(delx,dely,delz)) - neighptr[n++] = jh; - else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); - } else neighptr[n++] = jh; - } - } - - // loop over all atoms in other bins in stencil, store every pair - // skip if i,j neighbor cutoff is less than bin distance - - ibin = atom2bin[i]; - s = stencil_multi_old[itype]; - distsq = distsq_multi_old[itype]; - cutsq = cutneighsq[itype]; - ns = nstencil_multi_old[itype]; - for (k = 0; k < ns; k++) { - for (j = binhead[ibin+s[k]]; j >= 0; j = bins[j]) { - jtype = type[j]; - if (cutsq[jtype] < distsq[k]) continue; - - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - radsum = radi + radius[j]; - cutdistsq = (radsum+skin) * (radsum+skin); - - if (rsq <= cutdistsq) { + if (rsq <= cutsq) { jh = j; - if (history && rsq < radsum*radsum) + if (history && rsq < radsum * radsum) jh = jh ^ mask_history; if (molecular != Atom::ATOMIC) { if (!moltemplate) which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) + else if (imol >= 0) which = find_special(onemols[imol]->special[iatom], onemols[imol]->nspecial[iatom], tag[j]-tagprev); @@ -181,6 +173,22 @@ void NPairHalfSizeMultiOldNewtonOmp::build(NeighList *list) else if (which > 0) neighptr[n++] = jh ^ (which << SBBITS); } else neighptr[n++] = jh; } + } else { + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i],nspecial[i],tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], + onemols[imol]->nspecial[iatom], + tag[j]-tagprev); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (domain->minimum_image_check(delx,dely,delz)) + neighptr[n++] = j; + else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); + } else neighptr[n++] = j; + } } } @@ -193,4 +201,14 @@ void NPairHalfSizeMultiOldNewtonOmp::build(NeighList *list) } NPAIR_OMP_CLOSE; list->inum = nlocal; + list->gnum = 0; +} + +namespace LAMMPS_NS { +template class NPairNsqOmp<0,1,0>; +template class NPairNsqOmp<1,0,0>; +template class NPairNsqOmp<1,1,0>; +template class NPairNsqOmp<0,1,1>; +template class NPairNsqOmp<1,0,1>; +template class NPairNsqOmp<1,1,1>; } diff --git a/src/OPENMP/npair_nsq_omp.h b/src/OPENMP/npair_nsq_omp.h new file mode 100644 index 0000000000..150ac143ad --- /dev/null +++ b/src/OPENMP/npair_nsq_omp.h @@ -0,0 +1,66 @@ +/* -*- 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 NPAIR_CLASS +// clang-format off + +typedef NPairNsqOmp<0, 1, 0> NPairFullNsqOmp; +NPairStyle(full/nsq/omp, + NPairFullNsqOmp, + NP_FULL | NP_NSQ | NP_OMP | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairNsqOmp<1, 0, 0> NPairHalfNsqNewtoffOmp; +NPairStyle(half/nsq/newtoff/omp, + NPairHalfNsqNewtoffOmp, + NP_HALF | NP_NSQ | NP_OMP | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairNsqOmp<1, 1, 0> NPairHalfNsqNewtonOmp; +NPairStyle(half/nsq/newton/omp, + NPairHalfNsqNewtonOmp, + NP_HALF | NP_NSQ | NP_OMP | NP_NEWTON | NP_ORTHO | NP_TRI); + +typedef NPairNsqOmp<0, 1, 1> NPairFullSizeNsqOmp; +NPairStyle(full/size/nsq/omp, + NPairFullSizeNsqOmp, + NP_FULL | NP_SIZE | NP_NSQ | NP_OMP | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairNsqOmp<1, 0, 1> NPairHalfSizeNsqNewtoffOmp; +NPairStyle(half/size/nsq/newtoff/omp, + NPairHalfSizeNsqNewtoffOmp, + NP_HALF | NP_SIZE | NP_NSQ | NP_OMP | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairNsqOmp<1, 1, 1> NPairHalfSizeNsqNewtonOmp; +NPairStyle(half/size/nsq/newton/omp, + NPairHalfSizeNsqNewtonOmp, + NP_HALF | NP_SIZE | NP_NSQ | NP_OMP | NP_NEWTON | NP_ORTHO | NP_TRI); +// clang-format on +#else + +#ifndef LMP_NPAIR_NSQ_OMP_H +#define LMP_NPAIR_NSQ_OMP_H + +#include "npair.h" + +namespace LAMMPS_NS { + +template +class NPairNsqOmp : public NPair { + public: + NPairNsqOmp(class LAMMPS *); + void build(class NeighList *) override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif diff --git a/src/OPENMP/npair_half_respa_bin_newton_omp.cpp b/src/OPENMP/npair_respa_bin_omp.cpp similarity index 67% rename from src/OPENMP/npair_half_respa_bin_newton_omp.cpp rename to src/OPENMP/npair_respa_bin_omp.cpp index 51fc7a2ed8..877f368083 100644 --- a/src/OPENMP/npair_half_respa_bin_newton_omp.cpp +++ b/src/OPENMP/npair_respa_bin_omp.cpp @@ -13,7 +13,7 @@ ------------------------------------------------------------------------- */ #include "omp_compat.h" -#include "npair_half_respa_bin_newton_omp.h" +#include "npair_respa_bin_omp.h" #include "npair_omp.h" #include "neigh_list.h" #include "atom.h" @@ -27,17 +27,25 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -NPairHalfRespaBinNewtonOmp::NPairHalfRespaBinNewtonOmp(LAMMPS *lmp) : +template +NPairRespaBinOmp::NPairRespaBinOmp(LAMMPS *lmp) : NPair(lmp) {} /* ---------------------------------------------------------------------- multiple respa lists - binned neighbor list construction with full Newton's 3rd law - each owned atom i checks its own bin and other bins in Newton stencil - every pair stored exactly once by some processor + Newtoff + binned neighbor list construction with partial Newton's 3rd law + each owned atom i checks own bin and surrounding bins in non-Newton stencil + pair stored once if i,j are both owned and i < j + pair stored by me if j is ghost (also stored by proc owning j) + Newton + binned neighbor list construction with full Newton's 3rd law + each owned atom i checks its own bin and other bins in Newton stencil + every pair stored exactly once by some processor ------------------------------------------------------------------------- */ -void NPairHalfRespaBinNewtonOmp::build(NeighList *list) +template +void NPairRespaBinOmp::build(NeighList *list) { const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; const int molecular = atom->molecular; @@ -52,7 +60,7 @@ void NPairHalfRespaBinNewtonOmp::build(NeighList *list) #endif NPAIR_OMP_SETUP(nlocal); - int i,j,k,n,itype,jtype,ibin,n_inner,n_middle,imol,iatom; + int i,j,k,n,itype,jtype,ibin,bin_start,n_inner,n_middle,imol,iatom; tagint tagprev; double xtmp,ytmp,ztmp,delx,dely,delz,rsq; int *neighptr,*neighptr_inner,*neighptr_middle; @@ -115,76 +123,68 @@ void NPairHalfRespaBinNewtonOmp::build(NeighList *list) xtmp = x[i][0]; ytmp = x[i][1]; ztmp = x[i][2]; + ibin = atom2bin[i]; if (moltemplate) { imol = molindex[i]; iatom = molatom[i]; tagprev = tag[i] - iatom - 1; } - // loop over rest of atoms in i's bin, ghosts are at end of linked list - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the right" of i - - for (j = bins[i]; j >= 0; j = bins[j]) { - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - - jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i],nspecial[i],tag[j]); - else if (imol >=0) - which = find_special(onemols[imol]->special[iatom], - onemols[imol]->nspecial[iatom], - tag[j]-tagprev); - else which = 0; - if (which == 0) neighptr[n++] = j; - else if ((minchange = domain->minimum_image_check(delx,dely,delz))) - neighptr[n++] = j; - else if (which > 0) neighptr[n++] = j ^ (which << SBBITS); - } else neighptr[n++] = j; - - if (rsq < cut_inner_sq) { - if (which == 0) neighptr_inner[n_inner++] = j; - else if (minchange) neighptr_inner[n_inner++] = j; - else if (which > 0) neighptr_inner[n_inner++] = j ^ (which << SBBITS); - } - - if (respamiddle && - rsq < cut_middle_sq && rsq > cut_middle_inside_sq) { - if (which == 0) neighptr_middle[n_middle++] = j; - else if (minchange) neighptr_middle[n_middle++] = j; - else if (which > 0) - neighptr_middle[n_middle++] = j ^ (which << SBBITS); - } - } - } - - // loop over all atoms in other bins in stencil, store every pair - - ibin = atom2bin[i]; for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { + bin_start = binhead[ibin+stencil[k]]; + if (stencil[k] == 0) { + if (NEWTON && (!TRI)) { + // Half neighbor list, newton on, orthonormal + // loop over rest of atoms in i's bin, ghosts are at end of linked list + bin_start = bins[i]; + } + } + + for (j = bin_start; j >= 0; j = bins[j]) { + if (!NEWTON) { + // Half neighbor list, newton off + // only store pair if i < j + // stores own/own pairs only once + // stores own/ghost pairs on both procs + if (j <= i) continue; + } else if (TRI) { + // Half neighbor list, newton on, triclinic + // pairs for atoms j "below" i are excluded + // below = lower z or (equal z and lower y) or (equal zy and lower x) + // (equal zyx and j <= i) + // latter excludes self-self interaction but allows superposed atoms + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp) { + if (x[j][0] < xtmp) continue; + if (x[j][0] == xtmp && j <= i) continue; + } + } + } else { + // Half neighbor list, newton on, orthonormal + // store every pair for every bin in stencil,except for i's bin + + if (stencil[k] == 0) { + // if j is owned atom, store it, since j is beyond i in linked list + // if j is ghost, only store if j coords are "above and to the "right" of i + if (j >= nlocal) { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + } + } + } + } + jtype = type[j]; if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; delx = xtmp - x[j][0]; dely = ytmp - x[j][1]; delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; + rsq = delx * delx + dely * dely + delz * delz; if (rsq <= cutneighsq[itype][jtype]) { if (molecular != Atom::ATOMIC) { @@ -247,3 +247,9 @@ void NPairHalfRespaBinNewtonOmp::build(NeighList *list) list->inum_inner = nlocal; if (respamiddle) list->inum_middle = nlocal; } + +namespace LAMMPS_NS { +template class NPairRespaBinOmp<0,0>; +template class NPairRespaBinOmp<1,0>; +template class NPairRespaBinOmp<1,1>; +} diff --git a/src/OPENMP/npair_half_respa_bin_newton_omp.h b/src/OPENMP/npair_respa_bin_omp.h similarity index 55% rename from src/OPENMP/npair_half_respa_bin_newton_omp.h rename to src/OPENMP/npair_respa_bin_omp.h index 695d0ce627..23daacbb8f 100644 --- a/src/OPENMP/npair_half_respa_bin_newton_omp.h +++ b/src/OPENMP/npair_respa_bin_omp.h @@ -13,22 +13,34 @@ #ifdef NPAIR_CLASS // clang-format off +typedef NPairRespaBinOmp<0, 0> NPairHalfRespaBinNewtoffOmp; +NPairStyle(half/respa/bin/newtoff, + NPairHalfRespaBinNewtoffOmp, + NP_HALF | NP_RESPA | NP_BIN | NP_OMP | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairRespaBinOmp<1, 0> NPairHalfRespaBinNewtonOmp; NPairStyle(half/respa/bin/newton/omp, NPairHalfRespaBinNewtonOmp, - NP_HALF | NP_RESPA | NP_BIN | NP_NEWTON | NP_OMP | NP_ORTHO); + NP_HALF | NP_RESPA | NP_BIN | NP_OMP | NP_NEWTON | NP_ORTHO); + +typedef NPairRespaBinOmp<1, 1> NPairHalfRespaBinNewtonTriOmp; +NPairStyle(half/respa/bin/newton/tri/omp, + NPairHalfRespaBinNewtonTriOmp, + NP_HALF | NP_RESPA | NP_BIN | NP_OMP | NP_NEWTON | NP_TRI); // clang-format on #else -#ifndef LMP_NPAIR_HALF_RESPA_BIN_NEWTON_OMP_H -#define LMP_NPAIR_HALF_RESPA_BIN_NEWTON_OMP_H +#ifndef LMP_NPAIR_RESPA_BIN_OMP_H +#define LMP_NPAIR_RESPA_BIN_OMP_H #include "npair.h" namespace LAMMPS_NS { -class NPairHalfRespaBinNewtonOmp : public NPair { +template +class NPairRespaBinOmp : public NPair { public: - NPairHalfRespaBinNewtonOmp(class LAMMPS *); + NPairRespaBinOmp(class LAMMPS *); void build(class NeighList *) override; }; diff --git a/src/OPENMP/npair_half_respa_nsq_newtoff_omp.cpp b/src/OPENMP/npair_respa_nsq_omp.cpp similarity index 81% rename from src/OPENMP/npair_half_respa_nsq_newtoff_omp.cpp rename to src/OPENMP/npair_respa_nsq_omp.cpp index 1167a7601b..7464f70570 100644 --- a/src/OPENMP/npair_half_respa_nsq_newtoff_omp.cpp +++ b/src/OPENMP/npair_respa_nsq_omp.cpp @@ -13,7 +13,7 @@ ------------------------------------------------------------------------- */ #include "omp_compat.h" -#include "npair_half_respa_nsq_newtoff_omp.h" +#include "npair_respa_nsq_omp.h" #include "npair_omp.h" #include "neigh_list.h" #include "atom.h" @@ -28,17 +28,25 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -NPairHalfRespaNsqNewtoffOmp::NPairHalfRespaNsqNewtoffOmp(LAMMPS *lmp) : +template +NPairRespaNsqOmp::NPairRespaNsqOmp(LAMMPS *lmp) : NPair(lmp) {} /* ---------------------------------------------------------------------- multiple respa lists - N^2 / 2 search for neighbor pairs with partial Newton's 3rd law - pair added to list if atoms i and j are both owned and i < j - pair added if j is ghost (also stored by proc owning j) + Newtoff + N^2 / 2 search for neighbor pairs with partial Newton's 3rd law + pair added to list if atoms i and j are both owned and i < j + pair added if j is ghost (also stored by proc owning j) + Newton + N^2 / 2 search for neighbor pairs with full Newton's 3rd law + pair added to list if atoms i and j are both owned and i < j + if j is ghost only me or other proc adds pair + decision based on itag,jtag tests ------------------------------------------------------------------------- */ -void NPairHalfRespaNsqNewtoffOmp::build(NeighList *list) +template +void NPairRespaNsqOmp::build(NeighList *list) { const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; const int bitmask = (includegroup) ? group->bitmask[includegroup] : 0; @@ -55,7 +63,7 @@ void NPairHalfRespaNsqNewtoffOmp::build(NeighList *list) NPAIR_OMP_SETUP(nlocal); int i,j,n,itype,jtype,n_inner,n_middle,imol,iatom; - tagint tagprev; + tagint itag, jtag, tagprev; double xtmp,ytmp,ztmp,delx,dely,delz,rsq; int *neighptr,*neighptr_inner,*neighptr_middle; @@ -116,6 +124,7 @@ void NPairHalfRespaNsqNewtoffOmp::build(NeighList *list) } itype = type[i]; + itag = tag[i]; xtmp = x[i][0]; ytmp = x[i][1]; ztmp = x[i][2]; @@ -127,8 +136,26 @@ void NPairHalfRespaNsqNewtoffOmp::build(NeighList *list) // loop over remaining atoms, owned and ghost - for (j = i+1; j < nall; j++) { + for (j = i + 1; j < nall; j++) { if (includegroup && !(mask[j] & bitmask)) continue; + + if (NEWTON) { + if (j >= nlocal) { + jtag = tag[j]; + if (itag > jtag) { + if ((itag + jtag) % 2 == 0) continue; + } else if (itag < jtag) { + if ((itag+jtag) % 2 == 1) continue; + } else { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + } + } + } + } + jtype = type[j]; if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; @@ -195,3 +222,8 @@ void NPairHalfRespaNsqNewtoffOmp::build(NeighList *list) list->inum_inner = nlocal; if (respamiddle) list->inum_middle = nlocal; } + +namespace LAMMPS_NS { +template class NPairRespaNsqOmp<0>; +template class NPairRespaNsqOmp<1>; +} diff --git a/src/OPENMP/npair_half_respa_nsq_newton_omp.h b/src/OPENMP/npair_respa_nsq_omp.h similarity index 63% rename from src/OPENMP/npair_half_respa_nsq_newton_omp.h rename to src/OPENMP/npair_respa_nsq_omp.h index d949ef7e93..66b0f634cd 100644 --- a/src/OPENMP/npair_half_respa_nsq_newton_omp.h +++ b/src/OPENMP/npair_respa_nsq_omp.h @@ -13,23 +13,29 @@ #ifdef NPAIR_CLASS // clang-format off +typedef NPairRespaNsqOmp<0> NPairHalfRespaNsqNewtoffOmp; +NPairStyle(half/respa/nsq/newtoff/omp, + NPairHalfRespaNsqNewtoff, + NP_HALF | NP_RESPA | NP_NSQ | NP_OMP | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairRespaNsqOmp<1> NPairHalfRespaNsqNewtonOmp; NPairStyle(half/respa/nsq/newton/omp, NPairHalfRespaNsqNewtonOmp, - NP_HALF | NP_RESPA | NP_NSQ | NP_NEWTON | NP_OMP | - NP_ORTHO | NP_TRI); + NP_HALF | NP_RESPA | NP_NSQ | NP_OMP | NP_NEWTON | NP_ORTHO | NP_TRI); // clang-format on #else -#ifndef LMP_NPAIR_HALF_RESPA_NSQ_NEWTON_OMP_H -#define LMP_NPAIR_HALF_RESPA_NSQ_NEWTON_OMP_H +#ifndef LMP_NPAIR_RESPA_NSQ_OMP_H +#define LMP_NPAIR_RESPA_NSQ_OMP_H #include "npair.h" namespace LAMMPS_NS { -class NPairHalfRespaNsqNewtonOmp : public NPair { +template +class NPairRespaNsqOmp : public NPair { public: - NPairHalfRespaNsqNewtonOmp(class LAMMPS *); + NPairRespaNsqOmp(class LAMMPS *); void build(class NeighList *) override; }; diff --git a/src/npair_bin_ghost.cpp b/src/npair_bin_ghost.cpp index 4cf30f10f9..45e2cd0358 100644 --- a/src/npair_bin_ghost.cpp +++ b/src/npair_bin_ghost.cpp @@ -44,7 +44,6 @@ NPairBinGhost::NPairBinGhost(LAMMPS *lmp) : NPair(lmp) {} pair stored once if i,j are both ghost and i < j ------------------------------------------------------------------------- */ - template void NPairBinGhost::build(NeighList *list) { diff --git a/src/npair_multi.cpp b/src/npair_multi.cpp index ab182f39c4..ba703691cd 100644 --- a/src/npair_multi.cpp +++ b/src/npair_multi.cpp @@ -105,7 +105,7 @@ void NPairMulti::build(NeighList *list) // if same collection use own bin if (icollection == jcollection) jbin = ibin; - else jbin = coord2bin(x[i], jcollection); + else jbin = coord2bin(x[i], jcollection); s = stencil_multi[icollection][jcollection]; ns = nstencil_multi[icollection][jcollection]; diff --git a/src/npair_multi_old.cpp b/src/npair_multi_old.cpp index 3c61d4ba29..967a366cf4 100644 --- a/src/npair_multi_old.cpp +++ b/src/npair_multi_old.cpp @@ -159,7 +159,6 @@ void NPairMultiOld::build(NeighList *list) jtype = type[j]; if (cutnsq[jtype] < distsq[k]) continue; - if (i == j) continue; if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; diff --git a/src/npair_nsq.h b/src/npair_nsq.h index 749ea3a78c..df6e332865 100644 --- a/src/npair_nsq.h +++ b/src/npair_nsq.h @@ -42,7 +42,6 @@ typedef NPairNsq<1, 1, 1> NPairHalfSizeNsqNewton; NPairStyle(half/size/nsq/newton, NPairHalfSizeNsqNewton, NP_HALF | NP_SIZE | NP_NSQ | NP_NEWTON | NP_ORTHO | NP_TRI); - // clang-format on #else diff --git a/src/npair_nsq_ghost.cpp b/src/npair_nsq_ghost.cpp index 5e4185f9d6..47009deff7 100644 --- a/src/npair_nsq_ghost.cpp +++ b/src/npair_nsq_ghost.cpp @@ -44,7 +44,6 @@ NPairNsqGhost::NPairNsqGhost(LAMMPS *lmp) : NPair(lmp) {} pair stored once if i,j are both ghost and i < j ------------------------------------------------------------------------- */ - template void NPairNsqGhost::build(NeighList *list) { @@ -159,6 +158,7 @@ void NPairNsqGhost::build(NeighList *list) if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; } else { if (rsq <= cutneighghostsq[itype][jtype]) neighptr[n++] = j; + } } } diff --git a/src/npair_respa_bin.cpp b/src/npair_respa_bin.cpp index a3bcbf5124..7710a9d8ab 100644 --- a/src/npair_respa_bin.cpp +++ b/src/npair_respa_bin.cpp @@ -167,7 +167,7 @@ void NPairRespaBin::build(NeighList *list) delx = xtmp - x[j][0]; dely = ytmp - x[j][1]; delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; + rsq = delx * delx + dely * dely + delz * delz; if (rsq <= cutneighsq[itype][jtype]) { if (molecular != Atom::ATOMIC) { From b3e6a0bfa3c471e93bb4e047a5678a9c793e21ee Mon Sep 17 00:00:00 2001 From: jtclemm Date: Sat, 12 Nov 2022 20:44:45 -0700 Subject: [PATCH 009/189] Fixing mistake in multi stencil --- src/npair_multi.cpp | 2 +- src/nstencil_multi.cpp | 24 ++++++++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/npair_multi.cpp b/src/npair_multi.cpp index ba703691cd..9f719f403a 100644 --- a/src/npair_multi.cpp +++ b/src/npair_multi.cpp @@ -160,7 +160,7 @@ void NPairMulti::build(NeighList *list) delx = xtmp - x[j][0]; dely = ytmp - x[j][1]; delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; + rsq = delx * delx + dely * dely + delz * delz; if (SIZE) { radsum = radius[i] + radius[j]; diff --git a/src/nstencil_multi.cpp b/src/nstencil_multi.cpp index 76329db12d..6e9fefea1e 100644 --- a/src/nstencil_multi.cpp +++ b/src/nstencil_multi.cpp @@ -68,12 +68,6 @@ void NStencilMulti::create() int n = ncollections; double cutsq; - // For half stencils, only the upper plane is needed - int sy_min = sy; - int sz_min = sz; - if (HALF && (!DIM_3D)) sy_min = 0; - if (HALF && DIM_3D) sz_min = 0; - for (icollection = 0; icollection < n; icollection++) { for (jcollection = 0; jcollection < n; jcollection++) { if (flag_skip_multi[icollection][jcollection]) { @@ -101,15 +95,25 @@ void NStencilMulti::create() // as the old npair classes used to separately parse the central bin first if (half_flag && (!TRI)) stencil[nstencil++] = 0; + // For half stencils, only the upper plane is needed + int sy_min = sy; + int sz_min = sz; + if (HALF) { + if (half_flag && (!DIM_3D)) sy_min = 0; + if (half_flag && DIM_3D) sz_min = 0; + } + for (k = -sz_min; k <= sz; k++) { for (j = -sy_min; j <= sy; j++) { for (i = -sx; i <= sx; i++) { // Now only include "upper right" bins for half and ortho stencils - if (half_flag && (!DIM_3D) && (!TRI)) - if (! (j > 0 || (j == 0 && i > 0))) continue; - if (half_flag && DIM_3D && (!TRI)) - if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; + if (HALF) { + if (half_flag && (!DIM_3D) && (!TRI)) + if (! (j > 0 || (j == 0 && i > 0))) continue; + if (half_flag && DIM_3D && (!TRI)) + if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; + } if (bin_distance_multi(i,j,k,bin_collection) < cutsq) stencil_multi[icollection][jcollection][ns++] = k * mbiny * mbinx + j * mbinx + i; From f3d5941301a4b5db55b04fbd263194147f854fc6 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Mon, 14 Nov 2022 20:48:02 -0700 Subject: [PATCH 010/189] Updating logic for multi --- src/OPENMP/npair_multi_omp.cpp | 55 +++++++++++++++++++++------------- src/npair.cpp | 2 ++ src/npair.h | 1 + src/npair_multi.cpp | 48 +++++++++++++++++++---------- src/nstencil_multi.cpp | 19 ++++++------ 5 files changed, 80 insertions(+), 45 deletions(-) diff --git a/src/OPENMP/npair_multi_omp.cpp b/src/OPENMP/npair_multi_omp.cpp index bbdce96ffc..e5db08e157 100644 --- a/src/OPENMP/npair_multi_omp.cpp +++ b/src/OPENMP/npair_multi_omp.cpp @@ -118,15 +118,16 @@ void NPairMultiOmp::build(NeighList *list) if (icollection == jcollection) jbin = ibin; else jbin = coord2bin(x[i], jcollection); - // loop over all atoms in surrounding bins in stencil including self - // skip i = j - // use full stencil for all collection combinations - s = stencil_multi[icollection][jcollection]; ns = nstencil_multi[icollection][jcollection]; for (k = 0; k < ns; k++) { js = binhead_multi[jcollection][jbin + s[k]]; + + // own-bin for half stencil + if (HALF) + if (flag_half_multi[icollection][jcollection] && s[k] == 0) js = bins[i]; + for (j = js; j >= 0; j = bins[j]) { if (!HALF) { // Full neighbor list @@ -144,40 +145,52 @@ void NPairMultiOmp::build(NeighList *list) // below = lower z or (equal z and lower y) or (equal zy and lower x) // (equal zyx and j <= i) // latter excludes self-self interaction but allows superposed atoms - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp) { - if (x[j][0] < xtmp) continue; - if (x[j][0] == xtmp && j <= i) continue; + + // if same size (same collection), use half stencil + if (flag_half_multi[icollection][jcollection]) { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp) { + if (x[j][0] < xtmp) continue; + if (x[j][0] == xtmp && j <= i) continue; + } } } } else { // Half neighbor list, newton on, orthonormal - // store every pair for every bin in stencil,except for i's bin + // if same size: uses half stencil so includes a check of the central bin + if (flag_half_multi[icollection][jcollection]){ + if (s[k] == 0) { + // if same collection, + // if j is owned atom, store it, since j is beyond i in linked list + // if j is ghost, only store if j coords are "above and to the right" of i - if (stencil[k] == 0) { - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the "right" of i - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + // if different collections, + // if j is owned atom, store it if j > i + // if j is ghost, only store if j coords are "above and to the right" of i + + if ((icollection != jcollection) && (j < i)) continue; + + if (j >= nlocal) { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + } } } } } jtype = type[j]; - if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; + if (exclude && exclusion(i,j,itype,jtype,mask,molecule)) continue; delx = xtmp - x[j][0]; dely = ytmp - x[j][1]; delz = ztmp - x[j][2]; rsq = delx*delx + dely*dely + delz*delz; - if (SIZE) { radsum = radius[i] + radius[j]; cut = radsum + skin; diff --git a/src/npair.cpp b/src/npair.cpp index c1615411c0..6fddcfef54 100644 --- a/src/npair.cpp +++ b/src/npair.cpp @@ -174,6 +174,8 @@ void NPair::copy_stencil_info() nstencil_multi = ns->nstencil_multi; stencil_multi = ns->stencil_multi; + + flag_half_multi = ns->flag_half_multi; } /* ---------------------------------------------------------------------- diff --git a/src/npair.h b/src/npair.h index 3eeb1d48f4..661196e586 100644 --- a/src/npair.h +++ b/src/npair.h @@ -96,6 +96,7 @@ class NPair : protected Pointers { int *nstencil_multi_old; int **stencil_multi_old; double **distsq_multi_old; + bool **flag_half_multi; int **nstencil_multi; int ***stencil_multi; diff --git a/src/npair_multi.cpp b/src/npair_multi.cpp index 9f719f403a..76f6d90f6e 100644 --- a/src/npair_multi.cpp +++ b/src/npair_multi.cpp @@ -112,6 +112,11 @@ void NPairMulti::build(NeighList *list) for (k = 0; k < ns; k++) { js = binhead_multi[jcollection][jbin + s[k]]; + + // own-bin for half stencil + if (HALF) + if (flag_half_multi[icollection][jcollection] && s[k] == 0) js = bins[i]; + for (j = js; j >= 0; j = bins[j]) { if (!HALF) { // Full neighbor list @@ -129,26 +134,39 @@ void NPairMulti::build(NeighList *list) // below = lower z or (equal z and lower y) or (equal zy and lower x) // (equal zyx and j <= i) // latter excludes self-self interaction but allows superposed atoms - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp) { - if (x[j][0] < xtmp) continue; - if (x[j][0] == xtmp && j <= i) continue; + + // if same size (same collection), use half stencil + if (flag_half_multi[icollection][jcollection]) { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp) { + if (x[j][0] < xtmp) continue; + if (x[j][0] == xtmp && j <= i) continue; + } } } } else { // Half neighbor list, newton on, orthonormal - // store every pair for every bin in stencil,except for i's bin + // if same size: uses half stencil so includes a check of the central bin + if (flag_half_multi[icollection][jcollection]) { + if (s[k] == 0) { + // if same collection, + // if j is owned atom, store it, since j is beyond i in linked list + // if j is ghost, only store if j coords are "above and to the right" of i - if (stencil[k] == 0) { - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the "right" of i - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + // if different collections, + // if j is owned atom, store it if j > i + // if j is ghost, only store if j coords are "above and to the right" of i + + if ((icollection != jcollection) && (j < i)) continue; + + if (j >= nlocal) { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; + } } } } diff --git a/src/nstencil_multi.cpp b/src/nstencil_multi.cpp index 6e9fefea1e..202dace29d 100644 --- a/src/nstencil_multi.cpp +++ b/src/nstencil_multi.cpp @@ -87,13 +87,13 @@ void NStencilMulti::create() bin_collection = bin_collection_multi[icollection][jcollection]; cutsq = cutcollectionsq[icollection][jcollection]; - half_flag = flag_half_multi[icollection][jcollection]; // Half and ortho stencils include central bin first // This preserves the historical order of the neighbor list // as the old npair classes used to separately parse the central bin first - if (half_flag && (!TRI)) stencil[nstencil++] = 0; + if (HALF && (!TRI)) + if (half_flag) stencil_multi[icollection][jcollection][ns++] = 0; // For half stencils, only the upper plane is needed int sy_min = sy; @@ -106,15 +106,16 @@ void NStencilMulti::create() for (k = -sz_min; k <= sz; k++) { for (j = -sy_min; j <= sy; j++) { for (i = -sx; i <= sx; i++) { - // Now only include "upper right" bins for half and ortho stencils - if (HALF) { - if (half_flag && (!DIM_3D) && (!TRI)) - if (! (j > 0 || (j == 0 && i > 0))) continue; - if (half_flag && DIM_3D && (!TRI)) - if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; + if (HALF && (!TRI)) { + if (half_flag) { + if (DIM_3D) { + if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; + } else { + if (! (j > 0 || (j == 0 && i > 0))) continue; + } + } } - if (bin_distance_multi(i,j,k,bin_collection) < cutsq) stencil_multi[icollection][jcollection][ns++] = k * mbiny * mbinx + j * mbinx + i; } From 8f81cd80b65ff594e0bfaf7fecd1c687b05a650a Mon Sep 17 00:00:00 2001 From: jtclemm Date: Tue, 22 Nov 2022 21:31:31 -0700 Subject: [PATCH 011/189] Fixing a few errors in npair/stencil classes --- src/npair_bin_ghost.cpp | 2 +- src/npair_multi.cpp | 2 +- src/nstencil_ghost_bin.cpp | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/npair_bin_ghost.cpp b/src/npair_bin_ghost.cpp index 45e2cd0358..f3b96492dc 100644 --- a/src/npair_bin_ghost.cpp +++ b/src/npair_bin_ghost.cpp @@ -149,7 +149,7 @@ void NPairBinGhost::build(NeighList *list) if (xbin2 < 0 || xbin2 >= mbinx || ybin2 < 0 || ybin2 >= mbiny || zbin2 < 0 || zbin2 >= mbinz) continue; - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { + for (j = binhead[ibin + stencil[k]]; j >= 0; j = bins[j]) { if (HALF) { if (j <= i) continue; } else { diff --git a/src/npair_multi.cpp b/src/npair_multi.cpp index 76f6d90f6e..c05aa7fc5c 100644 --- a/src/npair_multi.cpp +++ b/src/npair_multi.cpp @@ -114,7 +114,7 @@ void NPairMulti::build(NeighList *list) js = binhead_multi[jcollection][jbin + s[k]]; // own-bin for half stencil - if (HALF) + if (HALF && !TRI) if (flag_half_multi[icollection][jcollection] && s[k] == 0) js = bins[i]; for (j = js; j >= 0; j = bins[j]) { diff --git a/src/nstencil_ghost_bin.cpp b/src/nstencil_ghost_bin.cpp index d5c7c1a463..b83a0ffd46 100644 --- a/src/nstencil_ghost_bin.cpp +++ b/src/nstencil_ghost_bin.cpp @@ -18,7 +18,10 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ template -NStencilGhostBin::NStencilGhostBin(LAMMPS *lmp) : NStencil(lmp) {} +NStencilGhostBin::NStencilGhostBin(LAMMPS *lmp) : NStencil(lmp) +{ + xyzflag = 1; +} /* ---------------------------------------------------------------------- create stencil based on bin geometry and cutoff @@ -52,11 +55,12 @@ void NStencilGhostBin::create() if (HALF && DIM_3D && (!TRI)) if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; - if (bin_distance(i,j,k) < cutneighmaxsq) + if (bin_distance(i,j,k) < cutneighmaxsq) { stencilxyz[nstencil][0] = i; stencilxyz[nstencil][1] = j; stencilxyz[nstencil][2] = k; stencil[nstencil++] = k * mbiny * mbinx + j * mbinx + i; + } } } } From 58f9553bf10520f3641d6e2a196e1aa92c6889a5 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Wed, 23 Nov 2022 08:52:21 -0700 Subject: [PATCH 012/189] Copying fixes to omp --- src/OPENMP/npair_bin_ghost_omp.cpp | 2 +- src/OPENMP/npair_multi_omp.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OPENMP/npair_bin_ghost_omp.cpp b/src/OPENMP/npair_bin_ghost_omp.cpp index 8f3105dc75..dbdabbe869 100644 --- a/src/OPENMP/npair_bin_ghost_omp.cpp +++ b/src/OPENMP/npair_bin_ghost_omp.cpp @@ -108,7 +108,7 @@ void NPairBinGhostOmp::build(NeighList *list) if (i < nlocal) { ibin = atom2bin[i]; for (k = 0; k < nstencil; k++) { - for (j = binhead[ibin+stencil[k]]; j >= 0; j = bins[j]) { + for (j = binhead[ibin + stencil[k]]; j >= 0; j = bins[j]) { if (HALF) { // Half neighbor list, newton off // only store pair if i < j diff --git a/src/OPENMP/npair_multi_omp.cpp b/src/OPENMP/npair_multi_omp.cpp index e5db08e157..5e29b174f9 100644 --- a/src/OPENMP/npair_multi_omp.cpp +++ b/src/OPENMP/npair_multi_omp.cpp @@ -125,7 +125,7 @@ void NPairMultiOmp::build(NeighList *list) js = binhead_multi[jcollection][jbin + s[k]]; // own-bin for half stencil - if (HALF) + if (HALF && !TRI) if (flag_half_multi[icollection][jcollection] && s[k] == 0) js = bins[i]; for (j = js; j >= 0; j = bins[j]) { From c4676aabfdda3781ec8b246866327a39ed9a7527 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Wed, 23 Nov 2022 09:58:46 -0700 Subject: [PATCH 013/189] Adding missing variables from omp npair halffull --- src/OPENMP/npair_bin_atomonly_omp.cpp | 8 ++------ src/OPENMP/npair_halffull_omp.cpp | 4 ++++ src/npair_bin_atomonly.cpp | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/OPENMP/npair_bin_atomonly_omp.cpp b/src/OPENMP/npair_bin_atomonly_omp.cpp index 2fe3baa014..2e948bf2b9 100644 --- a/src/OPENMP/npair_bin_atomonly_omp.cpp +++ b/src/OPENMP/npair_bin_atomonly_omp.cpp @@ -80,7 +80,6 @@ void NPairBinAtomonlyOmp::build(NeighList *list) // loop over owned atoms, storing neighbors for (i = ifrom; i < ito; i++) { - n = 0; neighptr = ipage.vget(); @@ -89,13 +88,10 @@ void NPairBinAtomonlyOmp::build(NeighList *list) ytmp = x[i][1]; ztmp = x[i][2]; - // loop over all atoms in surrounding bins in stencil including self - // skip i = j - ibin = atom2bin[i]; for (k = 0; k < nstencil; k++) { - bin_start = binhead[ibin+stencil[k]]; + bin_start = binhead[ibin + stencil[k]]; if (stencil[k] == 0) { if (HALF && NEWTON && (!TRI)) { // Half neighbor list, newton on, orthonormal @@ -131,7 +127,7 @@ void NPairBinAtomonlyOmp::build(NeighList *list) } } else { // Half neighbor list, newton on, orthonormal - // store every pair for every bin in stencil,except for i's bin + // store every pair for every bin in stencil, except for i's bin if (stencil[k] == 0) { // if j is owned atom, store it, since j is beyond i in linked list diff --git a/src/OPENMP/npair_halffull_omp.cpp b/src/OPENMP/npair_halffull_omp.cpp index 559d8910b1..d23325630a 100644 --- a/src/OPENMP/npair_halffull_omp.cpp +++ b/src/OPENMP/npair_halffull_omp.cpp @@ -85,6 +85,10 @@ void NPairHalffullOmp::build(NeighList *list) // loop over parent full list i = ilist_full[ii]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + jlist = firstneigh_full[i]; jnum = numneigh_full[i]; diff --git a/src/npair_bin_atomonly.cpp b/src/npair_bin_atomonly.cpp index 100b521b92..5e3be0e8b0 100644 --- a/src/npair_bin_atomonly.cpp +++ b/src/npair_bin_atomonly.cpp @@ -82,7 +82,7 @@ void NPairBinAtomonly::build(NeighList *list) ibin = atom2bin[i]; for (k = 0; k < nstencil; k++) { - bin_start = binhead[ibin+stencil[k]]; + bin_start = binhead[ibin + stencil[k]]; if (stencil[k] == 0) { if (HALF && NEWTON && (!TRI)) { // Half neighbor list, newton on, orthonormal @@ -118,7 +118,7 @@ void NPairBinAtomonly::build(NeighList *list) } } else { // Half neighbor list, newton on, orthonormal - // store every pair for every bin in stencil,except for i's bin + // store every pair for every bin in stencil, except for i's bin if (stencil[k] == 0) { // if j is owned atom, store it, since j is beyond i in linked list From 781eb934c17935af8e381418bdcaa30612b7290b Mon Sep 17 00:00:00 2001 From: jtclemm Date: Mon, 27 Mar 2023 16:37:11 -0600 Subject: [PATCH 014/189] Adding mass to python numpy unittest of KE --- unittest/python/python-numpy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/unittest/python/python-numpy.py b/unittest/python/python-numpy.py index f8e65bcd85..839e5d03af 100644 --- a/unittest/python/python-numpy.py +++ b/unittest/python/python-numpy.py @@ -108,6 +108,7 @@ class PythonNumpy(unittest.TestCase): self.lmp.command("create_box 1 box") self.lmp.command("create_atoms 1 single 1.0 1.0 1.0") self.lmp.command("create_atoms 1 single 1.0 1.0 1.5") + self.lmp.command("mass 1 1.0") self.lmp.command("compute ke all ke/atom") natoms = self.lmp.get_natoms() self.assertEqual(natoms,2) From b456beb62fdda6cd70f1886523835cf80c5c9a01 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Tue, 28 Mar 2023 13:55:12 -0600 Subject: [PATCH 015/189] Reorganizing intel npair to work with cmake, adding intel stencil --- ...st_intel.cpp => npair_bin_ghost_intel.cpp} | 2 +- ..._ghost_intel.h => npair_bin_ghost_intel.h} | 4 +- src/INTEL/npair_bin_intel.cpp | 298 ++++++++++++++++++ ...n_newton_tri_intel.h => npair_bin_intel.h} | 31 +- src/INTEL/npair_full_bin_intel.cpp | 134 -------- src/INTEL/npair_full_bin_intel.h | 44 --- src/INTEL/npair_half_bin_newton_intel.cpp | 108 ------- src/INTEL/npair_half_bin_newton_intel.h | 43 --- src/INTEL/npair_half_bin_newton_tri_intel.cpp | 108 ------- ...rim_intel.cpp => npair_halffull_intel.cpp} | 202 +++++++++++- src/INTEL/npair_halffull_intel.h | 128 ++++++++ src/INTEL/npair_halffull_newtoff_intel.h | 44 --- src/INTEL/npair_halffull_newtoff_trim_intel.h | 44 --- src/INTEL/npair_halffull_newton_intel.cpp | 226 ------------- src/INTEL/npair_halffull_newton_intel.h | 61 ---- src/INTEL/npair_halffull_newton_trim_intel.h | 61 ---- src/INTEL/nstencil_bin_intel.cpp | 70 ++++ src/INTEL/nstencil_bin_intel.h | 65 ++++ src/neighbor.cpp | 1 + src/neighbor.h | 5 +- 20 files changed, 797 insertions(+), 882 deletions(-) rename src/INTEL/{npair_full_bin_ghost_intel.cpp => npair_bin_ghost_intel.cpp} (99%) rename src/INTEL/{npair_full_bin_ghost_intel.h => npair_bin_ghost_intel.h} (95%) create mode 100644 src/INTEL/npair_bin_intel.cpp rename src/INTEL/{npair_half_bin_newton_tri_intel.h => npair_bin_intel.h} (59%) delete mode 100644 src/INTEL/npair_full_bin_intel.cpp delete mode 100644 src/INTEL/npair_full_bin_intel.h delete mode 100644 src/INTEL/npair_half_bin_newton_intel.cpp delete mode 100644 src/INTEL/npair_half_bin_newton_intel.h delete mode 100644 src/INTEL/npair_half_bin_newton_tri_intel.cpp rename src/INTEL/{npair_halffull_newton_trim_intel.cpp => npair_halffull_intel.cpp} (56%) create mode 100644 src/INTEL/npair_halffull_intel.h delete mode 100644 src/INTEL/npair_halffull_newtoff_intel.h delete mode 100644 src/INTEL/npair_halffull_newtoff_trim_intel.h delete mode 100644 src/INTEL/npair_halffull_newton_intel.cpp delete mode 100644 src/INTEL/npair_halffull_newton_intel.h delete mode 100644 src/INTEL/npair_halffull_newton_trim_intel.h create mode 100644 src/INTEL/nstencil_bin_intel.cpp create mode 100644 src/INTEL/nstencil_bin_intel.h diff --git a/src/INTEL/npair_full_bin_ghost_intel.cpp b/src/INTEL/npair_bin_ghost_intel.cpp similarity index 99% rename from src/INTEL/npair_full_bin_ghost_intel.cpp rename to src/INTEL/npair_bin_ghost_intel.cpp index b7b9ee4aea..47c3b5bd20 100644 --- a/src/INTEL/npair_full_bin_ghost_intel.cpp +++ b/src/INTEL/npair_bin_ghost_intel.cpp @@ -16,7 +16,7 @@ Contributing authors: W. Michael Brown (Intel) ------------------------------------------------------------------------- */ -#include "npair_full_bin_ghost_intel.h" +#include "npair_bin_ghost_intel.h" #include "atom.h" #include "comm.h" diff --git a/src/INTEL/npair_full_bin_ghost_intel.h b/src/INTEL/npair_bin_ghost_intel.h similarity index 95% rename from src/INTEL/npair_full_bin_ghost_intel.h rename to src/INTEL/npair_bin_ghost_intel.h index 4ae5ddad5f..eada3237bc 100644 --- a/src/INTEL/npair_full_bin_ghost_intel.h +++ b/src/INTEL/npair_bin_ghost_intel.h @@ -25,8 +25,8 @@ NPairStyle(full/bin/ghost/intel, // clang-format on #else -#ifndef LMP_NPAIR_FULL_BIN_GHOST_INTEL_H -#define LMP_NPAIR_FULL_BIN_GHOST_INTEL_H +#ifndef LMP_NPAIR_BIN_GHOST_INTEL_H +#define LMP_NPAIR_BIN_GHOST_INTEL_H #include "npair_intel.h" diff --git a/src/INTEL/npair_bin_intel.cpp b/src/INTEL/npair_bin_intel.cpp new file mode 100644 index 0000000000..f4942022ec --- /dev/null +++ b/src/INTEL/npair_bin_intel.cpp @@ -0,0 +1,298 @@ +// 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. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: W. Michael Brown (Intel) +------------------------------------------------------------------------- */ + +#include "npair_bin_intel.h" + +#include "atom.h" +#include "comm.h" +#include "error.h" +#include "neigh_list.h" +#include "neighbor.h" + +using namespace LAMMPS_NS; + + +/* ---------------------------------------------------------------------- */ + +NPairHalfBinNewtonIntel::NPairHalfBinNewtonIntel(LAMMPS *lmp) : + NPairIntel(lmp) {} + +/* ---------------------------------------------------------------------- + binned neighbor list construction with full Newton's 3rd law + each owned atom i checks its own bin and other bins in Newton stencil + every pair stored exactly once by some processor +------------------------------------------------------------------------- */ + +void NPairHalfBinNewtonIntel::build(NeighList *list) +{ + if (nstencil / 2 > INTEL_MAX_STENCIL_CHECK) + error->all(FLERR, "Too many neighbor bins for INTEL package."); + + #ifdef _LMP_INTEL_OFFLOAD + if (exclude) + error->all(FLERR, "Exclusion lists not yet supported for Intel offload"); + #endif + + if (_fix->precision() == FixIntel::PREC_MODE_MIXED) + hbni(list, _fix->get_mixed_buffers()); + else if (_fix->precision() == FixIntel::PREC_MODE_DOUBLE) + hbni(list, _fix->get_double_buffers()); + else + hbni(list, _fix->get_single_buffers()); + + _fix->stop_watch(TIME_HOST_NEIGHBOR); +} + +template +void NPairHalfBinNewtonIntel:: +hbni(NeighList *list, IntelBuffers *buffers) { + const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; + list->inum = nlocal; + + int host_start = _fix->host_start_neighbor(); + const int off_end = _fix->offload_end_neighbor(); + + #ifdef _LMP_INTEL_OFFLOAD + if (off_end) grow_stencil(); + if (_fix->full_host_list()) host_start = 0; + int offload_noghost = _fix->offload_noghost(); + #endif + + buffers->grow_list(list, atom->nlocal, comm->nthreads, 0, off_end); + + int need_ic = 0; + if (atom->molecular != Atom::ATOMIC) + dminimum_image_check(need_ic, neighbor->cutneighmax, neighbor->cutneighmax, + neighbor->cutneighmax); + + #ifdef _LMP_INTEL_OFFLOAD + if (need_ic) { + if (offload_noghost) { + bin_newton(1, list, buffers, 0, off_end); + bin_newton(0, list, buffers, host_start, nlocal, + off_end); + } else { + bin_newton(1, list, buffers, 0, off_end); + bin_newton(0, list, buffers, host_start, nlocal); + } + } else { + if (offload_noghost) { + bin_newton(1, list, buffers, 0, off_end); + bin_newton(0, list, buffers, host_start, nlocal, + off_end); + } else { + bin_newton(1, list, buffers, 0, off_end); + bin_newton(0, list, buffers, host_start, nlocal); + } + } + #else + if (need_ic) + bin_newton(0, list, buffers, host_start, nlocal); + else + bin_newton(0, list, buffers, host_start, nlocal); + #endif +} + + +/* ---------------------------------------------------------------------- */ + +NPairHalfBinNewtonTriIntel::NPairHalfBinNewtonTriIntel(LAMMPS *lmp) : + NPairIntel(lmp) {} + +/* ---------------------------------------------------------------------- + binned neighbor list construction with Newton's 3rd law for triclinic + each owned atom i checks its own bin and other bins in triclinic stencil + every pair stored exactly once by some processor +------------------------------------------------------------------------- */ + +void NPairHalfBinNewtonTriIntel::build(NeighList *list) +{ + if (nstencil > INTEL_MAX_STENCIL) + error->all(FLERR, "Too many neighbor bins for INTEL package."); + + #ifdef _LMP_INTEL_OFFLOAD + if (exclude) + error->all(FLERR, "Exclusion lists not yet supported for Intel offload"); + #endif + + if (_fix->precision() == FixIntel::PREC_MODE_MIXED) + hbnti(list, _fix->get_mixed_buffers()); + else if (_fix->precision() == FixIntel::PREC_MODE_DOUBLE) + hbnti(list, _fix->get_double_buffers()); + else + hbnti(list, _fix->get_single_buffers()); + + _fix->stop_watch(TIME_HOST_NEIGHBOR); +} + +template +void NPairHalfBinNewtonTriIntel:: +hbnti(NeighList *list, IntelBuffers *buffers) { + const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; + list->inum = nlocal; + + int host_start = _fix->host_start_neighbor(); + const int off_end = _fix->offload_end_neighbor(); + + #ifdef _LMP_INTEL_OFFLOAD + if (off_end) grow_stencil(); + if (_fix->full_host_list()) host_start = 0; + int offload_noghost = _fix->offload_noghost(); + #endif + + buffers->grow_list(list, atom->nlocal, comm->nthreads, 0, off_end); + + int need_ic = 0; + if (atom->molecular != Atom::ATOMIC) + dminimum_image_check(need_ic, neighbor->cutneighmax, neighbor->cutneighmax, + neighbor->cutneighmax); + + #ifdef _LMP_INTEL_OFFLOAD + if (need_ic) { + if (offload_noghost) { + bin_newton(1, list, buffers, 0, off_end); + bin_newton(0, list, buffers, host_start, nlocal, + off_end); + } else { + bin_newton(1, list, buffers, 0, off_end); + bin_newton(0, list, buffers, host_start, nlocal); + } + } else { + if (offload_noghost) { + bin_newton(1, list, buffers, 0, off_end); + bin_newton(0, list, buffers, host_start, nlocal, + off_end); + } else { + bin_newton(1, list, buffers, 0, off_end); + bin_newton(0, list, buffers, host_start, nlocal); + } + } + #else + if (need_ic) + bin_newton(0, list, buffers, host_start, nlocal); + else + bin_newton(0, list, buffers, host_start, nlocal); + #endif +} + +/* ---------------------------------------------------------------------- */ + +NPairFullBinIntel::NPairFullBinIntel(LAMMPS *lmp) : NPairIntel(lmp) {} + +/* ---------------------------------------------------------------------- + binned neighbor list construction for all neighbors + every neighbor pair appears in list of both atoms i and j +------------------------------------------------------------------------- */ + +void NPairFullBinIntel::build(NeighList *list) +{ + if (nstencil > INTEL_MAX_STENCIL_CHECK) + error->all(FLERR, "Too many neighbor bins for INTEL package."); + + #ifdef _LMP_INTEL_OFFLOAD + if (exclude) + error->all(FLERR, "Exclusion lists not yet supported for Intel offload"); + #endif + + if (_fix->precision() == FixIntel::PREC_MODE_MIXED) + fbi(list, _fix->get_mixed_buffers()); + else if (_fix->precision() == FixIntel::PREC_MODE_DOUBLE) + fbi(list, _fix->get_double_buffers()); + else + fbi(list, _fix->get_single_buffers()); + + _fix->stop_watch(TIME_HOST_NEIGHBOR); +} + +template +void NPairFullBinIntel:: +fbi(NeighList *list, IntelBuffers *buffers) { + const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; + list->inum = nlocal; + list->gnum = 0; + + int host_start = _fix->host_start_neighbor();; + const int off_end = _fix->offload_end_neighbor(); + + #ifdef _LMP_INTEL_OFFLOAD + if (off_end) grow_stencil(); + if (_fix->full_host_list()) host_start = 0; + int offload_noghost = _fix->offload_noghost(); + #endif + + buffers->grow_list(list, atom->nlocal, comm->nthreads, + _fix->three_body_neighbor(), off_end, + _fix->nbor_pack_width()); + + int need_ic = 0; + if (atom->molecular != Atom::ATOMIC) + dminimum_image_check(need_ic, neighbor->cutneighmax, neighbor->cutneighmax, + neighbor->cutneighmax); + + #ifdef _LMP_INTEL_OFFLOAD + if (_fix->three_body_neighbor()) { + if (need_ic) { + if (offload_noghost) { + bin_newton(1, list, buffers, 0, off_end); + bin_newton(0, list, buffers, host_start, nlocal, off_end); + } else { + bin_newton(1, list, buffers, 0, off_end); + bin_newton(0, list, buffers, host_start, nlocal); + } + } else { + if (offload_noghost) { + bin_newton(1, list, buffers, 0, off_end); + bin_newton(0, list, buffers, host_start, nlocal, off_end); + } else { + bin_newton(1, list, buffers, 0, off_end); + bin_newton(0, list, buffers, host_start, nlocal); + } + } + } else { + if (need_ic) { + if (offload_noghost) { + bin_newton(1, list, buffers, 0, off_end); + bin_newton(0, list, buffers, host_start, nlocal, off_end); + } else { + bin_newton(1, list, buffers, 0, off_end); + bin_newton(0, list, buffers, host_start, nlocal); + } + } else { + if (offload_noghost) { + bin_newton(1, list, buffers, 0, off_end); + bin_newton(0, list, buffers, host_start, nlocal, off_end); + } else { + bin_newton(1, list, buffers, 0, off_end); + bin_newton(0, list, buffers, host_start, nlocal); + } + } + } + #else + if (_fix->three_body_neighbor()) { + if (need_ic) + bin_newton(0, list, buffers, host_start, nlocal); + else + bin_newton(0, list, buffers, host_start, nlocal); + } else { + if (need_ic) + bin_newton(0, list, buffers, host_start, nlocal); + else + bin_newton(0, list, buffers, host_start, nlocal); + } + #endif +} diff --git a/src/INTEL/npair_half_bin_newton_tri_intel.h b/src/INTEL/npair_bin_intel.h similarity index 59% rename from src/INTEL/npair_half_bin_newton_tri_intel.h rename to src/INTEL/npair_bin_intel.h index 8ef65c12e5..fd18f20be5 100644 --- a/src/INTEL/npair_half_bin_newton_tri_intel.h +++ b/src/INTEL/npair_bin_intel.h @@ -14,20 +14,38 @@ #ifdef NPAIR_CLASS // clang-format off +NPairStyle(half/bin/newton/intel, + NPairHalfBinNewtonIntel, + NP_HALF | NP_BIN | NP_NEWTON | NP_ORTHO | NP_INTEL); + NPairStyle(half/bin/newton/tri/intel, NPairHalfBinNewtonTriIntel, NP_HALF | NP_BIN | NP_NEWTON | NP_TRI | NP_INTEL); + +NPairStyle(full/bin/intel, + NPairFullBinIntel, + NP_FULL | NP_BIN | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | + NP_INTEL); // clang-format on #else -#ifndef LMP_NPAIR_HALF_BIN_NEWTON_INTEL_TRI_H -#define LMP_NPAIR_HALF_BIN_NEWTON_INTEL_TRI_H +#ifndef LMP_NPAIR_BIN_INTEL_H +#define LMP_NPAIR_BIN_INTEL_H #include "fix_intel.h" #include "npair_intel.h" namespace LAMMPS_NS { +class NPairHalfBinNewtonIntel : public NPairIntel { + public: + NPairHalfBinNewtonIntel(class LAMMPS *); + void build(class NeighList *) override; + + private: + template void hbni(NeighList *, IntelBuffers *); +}; + class NPairHalfBinNewtonTriIntel : public NPairIntel { public: NPairHalfBinNewtonTriIntel(class LAMMPS *); @@ -37,6 +55,15 @@ class NPairHalfBinNewtonTriIntel : public NPairIntel { template void hbnti(NeighList *, IntelBuffers *); }; +class NPairFullBinIntel : public NPairIntel { + public: + NPairFullBinIntel(class LAMMPS *); + void build(class NeighList *) override; + + private: + template void fbi(NeighList *, IntelBuffers *); +}; + } // namespace LAMMPS_NS #endif diff --git a/src/INTEL/npair_full_bin_intel.cpp b/src/INTEL/npair_full_bin_intel.cpp deleted file mode 100644 index 2f8af4c8af..0000000000 --- a/src/INTEL/npair_full_bin_intel.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// 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. -------------------------------------------------------------------------- */ - -/* ---------------------------------------------------------------------- - Contributing author: W. Michael Brown (Intel) -------------------------------------------------------------------------- */ - -#include "npair_full_bin_intel.h" - -#include "atom.h" -#include "comm.h" -#include "error.h" -#include "neigh_list.h" -#include "neighbor.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairFullBinIntel::NPairFullBinIntel(LAMMPS *lmp) : NPairIntel(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction for all neighbors - every neighbor pair appears in list of both atoms i and j -------------------------------------------------------------------------- */ - -void NPairFullBinIntel::build(NeighList *list) -{ - if (nstencil > INTEL_MAX_STENCIL_CHECK) - error->all(FLERR, "Too many neighbor bins for INTEL package."); - - #ifdef _LMP_INTEL_OFFLOAD - if (exclude) - error->all(FLERR, "Exclusion lists not yet supported for Intel offload"); - #endif - - if (_fix->precision() == FixIntel::PREC_MODE_MIXED) - fbi(list, _fix->get_mixed_buffers()); - else if (_fix->precision() == FixIntel::PREC_MODE_DOUBLE) - fbi(list, _fix->get_double_buffers()); - else - fbi(list, _fix->get_single_buffers()); - - _fix->stop_watch(TIME_HOST_NEIGHBOR); -} - -template -void NPairFullBinIntel:: -fbi(NeighList *list, IntelBuffers *buffers) { - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - list->inum = nlocal; - list->gnum = 0; - - int host_start = _fix->host_start_neighbor();; - const int off_end = _fix->offload_end_neighbor(); - - #ifdef _LMP_INTEL_OFFLOAD - if (off_end) grow_stencil(); - if (_fix->full_host_list()) host_start = 0; - int offload_noghost = _fix->offload_noghost(); - #endif - - buffers->grow_list(list, atom->nlocal, comm->nthreads, - _fix->three_body_neighbor(), off_end, - _fix->nbor_pack_width()); - - int need_ic = 0; - if (atom->molecular != Atom::ATOMIC) - dminimum_image_check(need_ic, neighbor->cutneighmax, neighbor->cutneighmax, - neighbor->cutneighmax); - - #ifdef _LMP_INTEL_OFFLOAD - if (_fix->three_body_neighbor()) { - if (need_ic) { - if (offload_noghost) { - bin_newton(1, list, buffers, 0, off_end); - bin_newton(0, list, buffers, host_start, nlocal, off_end); - } else { - bin_newton(1, list, buffers, 0, off_end); - bin_newton(0, list, buffers, host_start, nlocal); - } - } else { - if (offload_noghost) { - bin_newton(1, list, buffers, 0, off_end); - bin_newton(0, list, buffers, host_start, nlocal, off_end); - } else { - bin_newton(1, list, buffers, 0, off_end); - bin_newton(0, list, buffers, host_start, nlocal); - } - } - } else { - if (need_ic) { - if (offload_noghost) { - bin_newton(1, list, buffers, 0, off_end); - bin_newton(0, list, buffers, host_start, nlocal, off_end); - } else { - bin_newton(1, list, buffers, 0, off_end); - bin_newton(0, list, buffers, host_start, nlocal); - } - } else { - if (offload_noghost) { - bin_newton(1, list, buffers, 0, off_end); - bin_newton(0, list, buffers, host_start, nlocal, off_end); - } else { - bin_newton(1, list, buffers, 0, off_end); - bin_newton(0, list, buffers, host_start, nlocal); - } - } - } - #else - if (_fix->three_body_neighbor()) { - if (need_ic) - bin_newton(0, list, buffers, host_start, nlocal); - else - bin_newton(0, list, buffers, host_start, nlocal); - } else { - if (need_ic) - bin_newton(0, list, buffers, host_start, nlocal); - else - bin_newton(0, list, buffers, host_start, nlocal); - } - #endif -} diff --git a/src/INTEL/npair_full_bin_intel.h b/src/INTEL/npair_full_bin_intel.h deleted file mode 100644 index 58ff21d22c..0000000000 --- a/src/INTEL/npair_full_bin_intel.h +++ /dev/null @@ -1,44 +0,0 @@ -// clang-format off -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(full/bin/intel, - NPairFullBinIntel, - NP_FULL | NP_BIN | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | - NP_INTEL); -// clang-format on -#else - -#ifndef LMP_NPAIR_FULL_BIN_INTEL_H -#define LMP_NPAIR_FULL_BIN_INTEL_H - -#include "fix_intel.h" -#include "npair_intel.h" - -namespace LAMMPS_NS { - -class NPairFullBinIntel : public NPairIntel { - public: - NPairFullBinIntel(class LAMMPS *); - void build(class NeighList *) override; - - private: - template void fbi(NeighList *, IntelBuffers *); -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/INTEL/npair_half_bin_newton_intel.cpp b/src/INTEL/npair_half_bin_newton_intel.cpp deleted file mode 100644 index 24e8b01572..0000000000 --- a/src/INTEL/npair_half_bin_newton_intel.cpp +++ /dev/null @@ -1,108 +0,0 @@ -// 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. -------------------------------------------------------------------------- */ - -/* ---------------------------------------------------------------------- - Contributing author: W. Michael Brown (Intel) -------------------------------------------------------------------------- */ - -#include "npair_half_bin_newton_intel.h" - -#include "atom.h" -#include "comm.h" -#include "error.h" -#include "neigh_list.h" -#include "neighbor.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfBinNewtonIntel::NPairHalfBinNewtonIntel(LAMMPS *lmp) : - NPairIntel(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with full Newton's 3rd law - each owned atom i checks its own bin and other bins in Newton stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfBinNewtonIntel::build(NeighList *list) -{ - if (nstencil / 2 > INTEL_MAX_STENCIL_CHECK) - error->all(FLERR, "Too many neighbor bins for INTEL package."); - - #ifdef _LMP_INTEL_OFFLOAD - if (exclude) - error->all(FLERR, "Exclusion lists not yet supported for Intel offload"); - #endif - - if (_fix->precision() == FixIntel::PREC_MODE_MIXED) - hbni(list, _fix->get_mixed_buffers()); - else if (_fix->precision() == FixIntel::PREC_MODE_DOUBLE) - hbni(list, _fix->get_double_buffers()); - else - hbni(list, _fix->get_single_buffers()); - - _fix->stop_watch(TIME_HOST_NEIGHBOR); -} - -template -void NPairHalfBinNewtonIntel:: -hbni(NeighList *list, IntelBuffers *buffers) { - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - list->inum = nlocal; - - int host_start = _fix->host_start_neighbor(); - const int off_end = _fix->offload_end_neighbor(); - - #ifdef _LMP_INTEL_OFFLOAD - if (off_end) grow_stencil(); - if (_fix->full_host_list()) host_start = 0; - int offload_noghost = _fix->offload_noghost(); - #endif - - buffers->grow_list(list, atom->nlocal, comm->nthreads, 0, off_end); - - int need_ic = 0; - if (atom->molecular != Atom::ATOMIC) - dminimum_image_check(need_ic, neighbor->cutneighmax, neighbor->cutneighmax, - neighbor->cutneighmax); - - #ifdef _LMP_INTEL_OFFLOAD - if (need_ic) { - if (offload_noghost) { - bin_newton(1, list, buffers, 0, off_end); - bin_newton(0, list, buffers, host_start, nlocal, - off_end); - } else { - bin_newton(1, list, buffers, 0, off_end); - bin_newton(0, list, buffers, host_start, nlocal); - } - } else { - if (offload_noghost) { - bin_newton(1, list, buffers, 0, off_end); - bin_newton(0, list, buffers, host_start, nlocal, - off_end); - } else { - bin_newton(1, list, buffers, 0, off_end); - bin_newton(0, list, buffers, host_start, nlocal); - } - } - #else - if (need_ic) - bin_newton(0, list, buffers, host_start, nlocal); - else - bin_newton(0, list, buffers, host_start, nlocal); - #endif -} diff --git a/src/INTEL/npair_half_bin_newton_intel.h b/src/INTEL/npair_half_bin_newton_intel.h deleted file mode 100644 index 092d4f2101..0000000000 --- a/src/INTEL/npair_half_bin_newton_intel.h +++ /dev/null @@ -1,43 +0,0 @@ -// clang-format off -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(half/bin/newton/intel, - NPairHalfBinNewtonIntel, - NP_HALF | NP_BIN | NP_NEWTON | NP_ORTHO | NP_INTEL); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALF_BIN_NEWTON_INTEL_H -#define LMP_NPAIR_HALF_BIN_NEWTON_INTEL_H - -#include "fix_intel.h" -#include "npair_intel.h" - -namespace LAMMPS_NS { - -class NPairHalfBinNewtonIntel : public NPairIntel { - public: - NPairHalfBinNewtonIntel(class LAMMPS *); - void build(class NeighList *) override; - - private: - template void hbni(NeighList *, IntelBuffers *); -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/INTEL/npair_half_bin_newton_tri_intel.cpp b/src/INTEL/npair_half_bin_newton_tri_intel.cpp deleted file mode 100644 index a903ef8e9a..0000000000 --- a/src/INTEL/npair_half_bin_newton_tri_intel.cpp +++ /dev/null @@ -1,108 +0,0 @@ -// 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. -------------------------------------------------------------------------- */ - -/* ---------------------------------------------------------------------- - Contributing author: W. Michael Brown (Intel) -------------------------------------------------------------------------- */ - -#include "npair_half_bin_newton_tri_intel.h" - -#include "atom.h" -#include "comm.h" -#include "error.h" -#include "neigh_list.h" -#include "neighbor.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalfBinNewtonTriIntel::NPairHalfBinNewtonTriIntel(LAMMPS *lmp) : - NPairIntel(lmp) {} - -/* ---------------------------------------------------------------------- - binned neighbor list construction with Newton's 3rd law for triclinic - each owned atom i checks its own bin and other bins in triclinic stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -void NPairHalfBinNewtonTriIntel::build(NeighList *list) -{ - if (nstencil > INTEL_MAX_STENCIL) - error->all(FLERR, "Too many neighbor bins for INTEL package."); - - #ifdef _LMP_INTEL_OFFLOAD - if (exclude) - error->all(FLERR, "Exclusion lists not yet supported for Intel offload"); - #endif - - if (_fix->precision() == FixIntel::PREC_MODE_MIXED) - hbnti(list, _fix->get_mixed_buffers()); - else if (_fix->precision() == FixIntel::PREC_MODE_DOUBLE) - hbnti(list, _fix->get_double_buffers()); - else - hbnti(list, _fix->get_single_buffers()); - - _fix->stop_watch(TIME_HOST_NEIGHBOR); -} - -template -void NPairHalfBinNewtonTriIntel:: -hbnti(NeighList *list, IntelBuffers *buffers) { - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - list->inum = nlocal; - - int host_start = _fix->host_start_neighbor(); - const int off_end = _fix->offload_end_neighbor(); - - #ifdef _LMP_INTEL_OFFLOAD - if (off_end) grow_stencil(); - if (_fix->full_host_list()) host_start = 0; - int offload_noghost = _fix->offload_noghost(); - #endif - - buffers->grow_list(list, atom->nlocal, comm->nthreads, 0, off_end); - - int need_ic = 0; - if (atom->molecular != Atom::ATOMIC) - dminimum_image_check(need_ic, neighbor->cutneighmax, neighbor->cutneighmax, - neighbor->cutneighmax); - - #ifdef _LMP_INTEL_OFFLOAD - if (need_ic) { - if (offload_noghost) { - bin_newton(1, list, buffers, 0, off_end); - bin_newton(0, list, buffers, host_start, nlocal, - off_end); - } else { - bin_newton(1, list, buffers, 0, off_end); - bin_newton(0, list, buffers, host_start, nlocal); - } - } else { - if (offload_noghost) { - bin_newton(1, list, buffers, 0, off_end); - bin_newton(0, list, buffers, host_start, nlocal, - off_end); - } else { - bin_newton(1, list, buffers, 0, off_end); - bin_newton(0, list, buffers, host_start, nlocal); - } - } - #else - if (need_ic) - bin_newton(0, list, buffers, host_start, nlocal); - else - bin_newton(0, list, buffers, host_start, nlocal); - #endif -} diff --git a/src/INTEL/npair_halffull_newton_trim_intel.cpp b/src/INTEL/npair_halffull_intel.cpp similarity index 56% rename from src/INTEL/npair_halffull_newton_trim_intel.cpp rename to src/INTEL/npair_halffull_intel.cpp index e38375f750..42ecb716f5 100644 --- a/src/INTEL/npair_halffull_newton_trim_intel.cpp +++ b/src/INTEL/npair_halffull_intel.cpp @@ -13,10 +13,10 @@ ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- - Contributing author: Stan Moore (SNL) + Contributing author: W. Michael Brown (Intel) ------------------------------------------------------------------------- */ -#include "npair_halffull_newton_trim_intel.h" +#include "npair_halffull_intel.h" #include "atom.h" #include "comm.h" @@ -29,6 +29,204 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ +NPairHalffullNewtonIntel::NPairHalffullNewtonIntel(LAMMPS *lmp) : NPair(lmp) { + _fix = static_cast(modify->get_fix_by_id("package_intel")); + if (!_fix) error->all(FLERR, "The 'package intel' command is required for /intel styles"); +} + +/* ---------------------------------------------------------------------- + build half list from full list + pair stored once if i,j are both owned and i < j + if j is ghost, only store if j coords are "above and to the right" of i + works if full list is a skip list +------------------------------------------------------------------------- */ + +template +void NPairHalffullNewtonIntel::build_t(NeighList *list, + IntelBuffers *buffers) +{ + const int inum_full = list->listfull->inum; + const int nlocal = atom->nlocal; + const int e_nall = nlocal + atom->nghost; + const ATOM_T * _noalias const x = buffers->get_x(); + int * _noalias const ilist = list->ilist; + int * _noalias const numneigh = list->numneigh; + int ** _noalias const firstneigh = list->firstneigh; + const int * _noalias const ilist_full = list->listfull->ilist; + const int * _noalias const numneigh_full = list->listfull->numneigh; + const int ** _noalias const firstneigh_full = (const int ** const)list->listfull->firstneigh; // NOLINT + + #if defined(_OPENMP) + #pragma omp parallel + #endif + { + int tid, ifrom, ito; + IP_PRE_omp_range_id(ifrom, ito, tid, inum_full, comm->nthreads); + + // each thread has its own page allocator + MyPage &ipage = list->ipage[tid]; + ipage.reset(); + + // loop over parent full list + for (int ii = ifrom; ii < ito; ii++) { + int n = 0; + int *neighptr = ipage.vget(); + + const int i = ilist_full[ii]; + const flt_t xtmp = x[i].x; + const flt_t ytmp = x[i].y; + const flt_t ztmp = x[i].z; + + // loop over full neighbor list + + const int * _noalias const jlist = firstneigh_full[i]; + const int jnum = numneigh_full[i]; + + #if defined(LMP_SIMD_COMPILER) + #pragma vector aligned + #pragma ivdep + #endif + for (int jj = 0; jj < jnum; jj++) { + const int joriginal = jlist[jj]; + const int j = joriginal & NEIGHMASK; + int addme = 1; + if (j < nlocal) { + if (i > j) addme = 0; + } else { + if (x[j].z < ztmp) addme = 0; + if (x[j].z == ztmp) { + if (x[j].y < ytmp) addme = 0; + if (x[j].y == ytmp && x[j].x < xtmp) addme = 0; + } + } + if (addme) + neighptr[n++] = joriginal; + } + + ilist[ii] = i; + firstneigh[i] = neighptr; + numneigh[i] = n; + + int pad_end = n; + IP_PRE_neighbor_pad(pad_end, 0); + #if defined(LMP_SIMD_COMPILER) + #pragma vector aligned + #pragma loop_count min=1, max=INTEL_COMPILE_WIDTH-1, \ + avg=INTEL_COMPILE_WIDTH/2 + #endif + for ( ; n < pad_end; n++) + neighptr[n] = e_nall; + + ipage.vgot(n); + if (ipage.status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + } + } + list->inum = inum_full; +} + +/* ---------------------------------------------------------------------- + build half list from full 3-body list + half list is already stored as first part of 3-body list +------------------------------------------------------------------------- */ + +template +void NPairHalffullNewtonIntel::build_t3(NeighList *list, int *numhalf) +{ + const int inum_full = list->listfull->inum; + const int e_nall = atom->nlocal + atom->nghost; + int * _noalias const ilist = list->ilist; + int * _noalias const numneigh = list->numneigh; + int ** _noalias const firstneigh = list->firstneigh; + const int * _noalias const ilist_full = list->listfull->ilist; + const int * _noalias const numneigh_full = numhalf; + const int ** _noalias const firstneigh_full = (const int ** const)list->listfull->firstneigh; // NOLINT + + int packthreads = 1; + if (comm->nthreads > INTEL_HTHREADS) packthreads = comm->nthreads; + + #if defined(_OPENMP) + #pragma omp parallel if (packthreads > 1) + #endif + { + int tid, ifrom, ito; + IP_PRE_omp_range_id(ifrom, ito, tid, inum_full, packthreads); + + // each thread has its own page allocator + MyPage &ipage = list->ipage[tid]; + ipage.reset(); + + // loop over parent full list + for (int ii = ifrom; ii < ito; ii++) { + int n = 0; + int *neighptr = ipage.vget(); + + const int i = ilist_full[ii]; + + // loop over full neighbor list + + const int * _noalias const jlist = firstneigh_full[i]; + const int jnum = numneigh_full[ii]; + + #if defined(LMP_SIMD_COMPILER) + #pragma vector aligned + #pragma ivdep + #endif + for (int jj = 0; jj < jnum; jj++) { + const int joriginal = jlist[jj]; + neighptr[n++] = joriginal; + } + + ilist[ii] = i; + firstneigh[i] = neighptr; + numneigh[i] = n; + + int pad_end = n; + IP_PRE_neighbor_pad(pad_end, 0); + #if defined(LMP_SIMD_COMPILER) + #pragma vector aligned + #pragma loop_count min=1, max=INTEL_COMPILE_WIDTH-1, \ + avg=INTEL_COMPILE_WIDTH/2 + #endif + for ( ; n < pad_end; n++) + neighptr[n] = e_nall; + + ipage.vgot(n); + if (ipage.status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + } + } + list->inum = inum_full; +} + +/* ---------------------------------------------------------------------- */ + +void NPairHalffullNewtonIntel::build(NeighList *list) +{ + if (_fix->three_body_neighbor() == 0) { + if (_fix->precision() == FixIntel::PREC_MODE_MIXED) + build_t(list, _fix->get_mixed_buffers()); + else if (_fix->precision() == FixIntel::PREC_MODE_DOUBLE) + build_t(list, _fix->get_double_buffers()); + else + build_t(list, _fix->get_single_buffers()); + } else { + int *nhalf, *cnum; + if (_fix->precision() == FixIntel::PREC_MODE_MIXED) { + _fix->get_mixed_buffers()->get_list_data3(list->listfull, nhalf, cnum); + build_t3(list, nhalf); + } else if (_fix->precision() == FixIntel::PREC_MODE_DOUBLE) { + _fix->get_double_buffers()->get_list_data3(list->listfull, nhalf, cnum); + build_t3(list, nhalf); + } else { + _fix->get_single_buffers()->get_list_data3(list->listfull, nhalf, cnum); + build_t3(list, nhalf); + } + } +} + +/* ---------------------------------------------------------------------- */ + NPairHalffullNewtonTrimIntel::NPairHalffullNewtonTrimIntel(LAMMPS *lmp) : NPair(lmp) { _fix = static_cast(modify->get_fix_by_id("package_intel")); if (!_fix) error->all(FLERR, "The 'package intel' command is required for /intel styles"); diff --git a/src/INTEL/npair_halffull_intel.h b/src/INTEL/npair_halffull_intel.h new file mode 100644 index 0000000000..08c9312fff --- /dev/null +++ b/src/INTEL/npair_halffull_intel.h @@ -0,0 +1,128 @@ +// clang-format off +/* -*- 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. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: W. Michael Brown (Intel) +------------------------------------------------------------------------- */ + +// For Newton off, only used for hybrid to generate list for non-intel style. +// Use standard routines. + +#ifdef NPAIR_CLASS +// clang-format off +NPairStyle(halffull/newton/intel, + NPairHalffullNewtonIntel, + NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | + NP_ORTHO | NP_TRI| NP_INTEL); + +NPairStyle(halffull/newton/skip/intel, + NPairHalffullNewtonIntel, + NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | + NP_ORTHO | NP_TRI | NP_SKIP | NP_INTEL); + +NPairStyle(halffull/newtoff/intel, + NPairHalffullNewtoff, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_HALF | + NP_ORTHO | NP_TRI | NP_INTEL); + +NPairStyle(halffull/newtoff/skip/intel, + NPairHalffullNewtoff, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_HALF | + NP_ORTHO | NP_TRI | NP_SKIP | NP_INTEL); + +NPairStyle(halffull/newtoff/ghost/intel, + NPairHalffullNewtoff, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_HALF | + NP_ORTHO | NP_TRI | NP_GHOST | NP_INTEL); + +NPairStyle(halffull/newtoff/skip/ghost/intel, + NPairHalffullNewtoff, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_HALF | + NP_ORTHO | NP_TRI | NP_SKIP | NP_GHOST | NP_INTEL); + + +NPairStyle(halffull/newton/trim/intel, + NPairHalffullNewtonTrimIntel, + NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | + NP_ORTHO | NP_TRI| NP_TRIM | NP_INTEL); + +NPairStyle(halffull/newton/skip/trim/intel, + NPairHalffullNewtonTrimIntel, + NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | + NP_ORTHO | NP_TRI | NP_SKIP | NP_TRIM | NP_INTEL); + +NPairStyle(halffull/newtoff/trim/intel, + NPairHalffullNewtoffTrim, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_HALF | + NP_ORTHO | NP_TRI | NP_TRIM | NP_INTEL); + +NPairStyle(halffull/newtoff/skip/trim/intel, + NPairHalffullNewtoffTrim, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_HALF | + NP_ORTHO | NP_TRI | NP_SKIP | NP_TRIM | NP_INTEL); + +NPairStyle(halffull/newtoff/ghost/trim/intel, + NPairHalffullNewtoffTrim, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_HALF | + NP_ORTHO | NP_TRI | NP_GHOST | NP_TRIM | NP_INTEL); + +NPairStyle(halffull/newtoff/skip/ghost/trim/intel, + NPairHalffullNewtoffTrim, + NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_HALF | + NP_ORTHO | NP_TRI | NP_SKIP | NP_GHOST | NP_TRIM | NP_INTEL); +// clang-format on +#else + +#ifndef LMP_NPAIR_HALFFULL_INTEL_H +#define LMP_NPAIR_HALFFULL_INTEL_H + +#include "fix_intel.h" +#include "npair.h" + +#if defined(_OPENMP) +#include +#endif + +namespace LAMMPS_NS { + +class NPairHalffullNewtonIntel : public NPair { + public: + NPairHalffullNewtonIntel(class LAMMPS *); + void build(class NeighList *) override; + + protected: + FixIntel *_fix; + + template void build_t(NeighList *, IntelBuffers *); + + template void build_t3(NeighList *, int *); +}; + +class NPairHalffullNewtonTrimIntel : public NPair { + public: + NPairHalffullNewtonTrimIntel(class LAMMPS *); + void build(class NeighList *) override; + + protected: + FixIntel *_fix; + + template void build_t(NeighList *, IntelBuffers *); + + template void build_t3(NeighList *, int *, IntelBuffers *); +}; + +} // namespace LAMMPS_NS + +#endif +#endif diff --git a/src/INTEL/npair_halffull_newtoff_intel.h b/src/INTEL/npair_halffull_newtoff_intel.h deleted file mode 100644 index f77ddb74d6..0000000000 --- a/src/INTEL/npair_halffull_newtoff_intel.h +++ /dev/null @@ -1,44 +0,0 @@ -// clang-format off -/* -*- 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. -------------------------------------------------------------------------- */ - -/* ---------------------------------------------------------------------- - Contributing author: W. Michael Brown (Intel) -------------------------------------------------------------------------- */ - -// Only used for hybrid to generate list for non-intel style. Use -// standard routines. - -#ifdef NPAIR_CLASS -// clang-format off -NPairStyle(halffull/newtoff/intel, - NPairHalffullNewtoff, - NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_HALF | - NP_ORTHO | NP_TRI | NP_INTEL); - -NPairStyle(halffull/newtoff/skip/intel, - NPairHalffullNewtoff, - NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_HALF | - NP_ORTHO | NP_TRI | NP_SKIP | NP_INTEL); - -NPairStyle(halffull/newtoff/ghost/intel, - NPairHalffullNewtoff, - NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_HALF | - NP_ORTHO | NP_TRI | NP_GHOST | NP_INTEL); - -NPairStyle(halffull/newtoff/skip/ghost/intel, - NPairHalffullNewtoff, - NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_HALF | - NP_ORTHO | NP_TRI | NP_SKIP | NP_GHOST | NP_INTEL); -// clang-format on -#endif diff --git a/src/INTEL/npair_halffull_newtoff_trim_intel.h b/src/INTEL/npair_halffull_newtoff_trim_intel.h deleted file mode 100644 index d8594ce3b8..0000000000 --- a/src/INTEL/npair_halffull_newtoff_trim_intel.h +++ /dev/null @@ -1,44 +0,0 @@ -// clang-format off -/* -*- 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. -------------------------------------------------------------------------- */ - -/* ---------------------------------------------------------------------- - Contributing author: Stan Moore (SNL) -------------------------------------------------------------------------- */ - -// Only used for hybrid to generate list for non-intel style. Use -// standard routines. - -#ifdef NPAIR_CLASS -// clang-format off -NPairStyle(halffull/newtoff/trim/intel, - NPairHalffullNewtoffTrim, - NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_HALF | - NP_ORTHO | NP_TRI | NP_TRIM | NP_INTEL); - -NPairStyle(halffull/newtoff/skip/trim/intel, - NPairHalffullNewtoffTrim, - NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_HALF | - NP_ORTHO | NP_TRI | NP_SKIP | NP_TRIM | NP_INTEL); - -NPairStyle(halffull/newtoff/ghost/trim/intel, - NPairHalffullNewtoffTrim, - NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_HALF | - NP_ORTHO | NP_TRI | NP_GHOST | NP_TRIM | NP_INTEL); - -NPairStyle(halffull/newtoff/skip/ghost/trim/intel, - NPairHalffullNewtoffTrim, - NP_HALF_FULL | NP_NEWTOFF | NP_NSQ | NP_BIN | NP_MULTI | NP_HALF | - NP_ORTHO | NP_TRI | NP_SKIP | NP_GHOST | NP_TRIM | NP_INTEL); -// clang-format on -#endif diff --git a/src/INTEL/npair_halffull_newton_intel.cpp b/src/INTEL/npair_halffull_newton_intel.cpp deleted file mode 100644 index cd05d5f97a..0000000000 --- a/src/INTEL/npair_halffull_newton_intel.cpp +++ /dev/null @@ -1,226 +0,0 @@ -// 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. -------------------------------------------------------------------------- */ - -/* ---------------------------------------------------------------------- - Contributing author: W. Michael Brown (Intel) -------------------------------------------------------------------------- */ - -#include "npair_halffull_newton_intel.h" - -#include "atom.h" -#include "comm.h" -#include "error.h" -#include "modify.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairHalffullNewtonIntel::NPairHalffullNewtonIntel(LAMMPS *lmp) : NPair(lmp) { - _fix = static_cast(modify->get_fix_by_id("package_intel")); - if (!_fix) error->all(FLERR, "The 'package intel' command is required for /intel styles"); -} - -/* ---------------------------------------------------------------------- - build half list from full list - pair stored once if i,j are both owned and i < j - if j is ghost, only store if j coords are "above and to the right" of i - works if full list is a skip list -------------------------------------------------------------------------- */ - -template -void NPairHalffullNewtonIntel::build_t(NeighList *list, - IntelBuffers *buffers) -{ - const int inum_full = list->listfull->inum; - const int nlocal = atom->nlocal; - const int e_nall = nlocal + atom->nghost; - const ATOM_T * _noalias const x = buffers->get_x(); - int * _noalias const ilist = list->ilist; - int * _noalias const numneigh = list->numneigh; - int ** _noalias const firstneigh = list->firstneigh; - const int * _noalias const ilist_full = list->listfull->ilist; - const int * _noalias const numneigh_full = list->listfull->numneigh; - const int ** _noalias const firstneigh_full = (const int ** const)list->listfull->firstneigh; // NOLINT - - #if defined(_OPENMP) - #pragma omp parallel - #endif - { - int tid, ifrom, ito; - IP_PRE_omp_range_id(ifrom, ito, tid, inum_full, comm->nthreads); - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - // loop over parent full list - for (int ii = ifrom; ii < ito; ii++) { - int n = 0; - int *neighptr = ipage.vget(); - - const int i = ilist_full[ii]; - const flt_t xtmp = x[i].x; - const flt_t ytmp = x[i].y; - const flt_t ztmp = x[i].z; - - // loop over full neighbor list - - const int * _noalias const jlist = firstneigh_full[i]; - const int jnum = numneigh_full[i]; - - #if defined(LMP_SIMD_COMPILER) - #pragma vector aligned - #pragma ivdep - #endif - for (int jj = 0; jj < jnum; jj++) { - const int joriginal = jlist[jj]; - const int j = joriginal & NEIGHMASK; - int addme = 1; - if (j < nlocal) { - if (i > j) addme = 0; - } else { - if (x[j].z < ztmp) addme = 0; - if (x[j].z == ztmp) { - if (x[j].y < ytmp) addme = 0; - if (x[j].y == ytmp && x[j].x < xtmp) addme = 0; - } - } - if (addme) - neighptr[n++] = joriginal; - } - - ilist[ii] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - - int pad_end = n; - IP_PRE_neighbor_pad(pad_end, 0); - #if defined(LMP_SIMD_COMPILER) - #pragma vector aligned - #pragma loop_count min=1, max=INTEL_COMPILE_WIDTH-1, \ - avg=INTEL_COMPILE_WIDTH/2 - #endif - for ( ; n < pad_end; n++) - neighptr[n] = e_nall; - - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - } - list->inum = inum_full; -} - -/* ---------------------------------------------------------------------- - build half list from full 3-body list - half list is already stored as first part of 3-body list -------------------------------------------------------------------------- */ - -template -void NPairHalffullNewtonIntel::build_t3(NeighList *list, int *numhalf) -{ - const int inum_full = list->listfull->inum; - const int e_nall = atom->nlocal + atom->nghost; - int * _noalias const ilist = list->ilist; - int * _noalias const numneigh = list->numneigh; - int ** _noalias const firstneigh = list->firstneigh; - const int * _noalias const ilist_full = list->listfull->ilist; - const int * _noalias const numneigh_full = numhalf; - const int ** _noalias const firstneigh_full = (const int ** const)list->listfull->firstneigh; // NOLINT - - int packthreads = 1; - if (comm->nthreads > INTEL_HTHREADS) packthreads = comm->nthreads; - - #if defined(_OPENMP) - #pragma omp parallel if (packthreads > 1) - #endif - { - int tid, ifrom, ito; - IP_PRE_omp_range_id(ifrom, ito, tid, inum_full, packthreads); - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - // loop over parent full list - for (int ii = ifrom; ii < ito; ii++) { - int n = 0; - int *neighptr = ipage.vget(); - - const int i = ilist_full[ii]; - - // loop over full neighbor list - - const int * _noalias const jlist = firstneigh_full[i]; - const int jnum = numneigh_full[ii]; - - #if defined(LMP_SIMD_COMPILER) - #pragma vector aligned - #pragma ivdep - #endif - for (int jj = 0; jj < jnum; jj++) { - const int joriginal = jlist[jj]; - neighptr[n++] = joriginal; - } - - ilist[ii] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - - int pad_end = n; - IP_PRE_neighbor_pad(pad_end, 0); - #if defined(LMP_SIMD_COMPILER) - #pragma vector aligned - #pragma loop_count min=1, max=INTEL_COMPILE_WIDTH-1, \ - avg=INTEL_COMPILE_WIDTH/2 - #endif - for ( ; n < pad_end; n++) - neighptr[n] = e_nall; - - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - } - list->inum = inum_full; -} - -/* ---------------------------------------------------------------------- */ - -void NPairHalffullNewtonIntel::build(NeighList *list) -{ - if (_fix->three_body_neighbor() == 0) { - if (_fix->precision() == FixIntel::PREC_MODE_MIXED) - build_t(list, _fix->get_mixed_buffers()); - else if (_fix->precision() == FixIntel::PREC_MODE_DOUBLE) - build_t(list, _fix->get_double_buffers()); - else - build_t(list, _fix->get_single_buffers()); - } else { - int *nhalf, *cnum; - if (_fix->precision() == FixIntel::PREC_MODE_MIXED) { - _fix->get_mixed_buffers()->get_list_data3(list->listfull, nhalf, cnum); - build_t3(list, nhalf); - } else if (_fix->precision() == FixIntel::PREC_MODE_DOUBLE) { - _fix->get_double_buffers()->get_list_data3(list->listfull, nhalf, cnum); - build_t3(list, nhalf); - } else { - _fix->get_single_buffers()->get_list_data3(list->listfull, nhalf, cnum); - build_t3(list, nhalf); - } - } -} diff --git a/src/INTEL/npair_halffull_newton_intel.h b/src/INTEL/npair_halffull_newton_intel.h deleted file mode 100644 index 149983d08e..0000000000 --- a/src/INTEL/npair_halffull_newton_intel.h +++ /dev/null @@ -1,61 +0,0 @@ -// clang-format off -/* -*- 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. -------------------------------------------------------------------------- */ - -/* ---------------------------------------------------------------------- - Contributing author: W. Michael Brown (Intel) -------------------------------------------------------------------------- */ - -#ifdef NPAIR_CLASS -// clang-format off -NPairStyle(halffull/newton/intel, - NPairHalffullNewtonIntel, - NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | - NP_ORTHO | NP_TRI| NP_INTEL); - -NPairStyle(halffull/newton/skip/intel, - NPairHalffullNewtonIntel, - NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | - NP_ORTHO | NP_TRI | NP_SKIP | NP_INTEL); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALFFULL_NEWTON_INTEL_H -#define LMP_NPAIR_HALFFULL_NEWTON_INTEL_H - -#include "fix_intel.h" -#include "npair.h" - -#if defined(_OPENMP) -#include -#endif - -namespace LAMMPS_NS { - -class NPairHalffullNewtonIntel : public NPair { - public: - NPairHalffullNewtonIntel(class LAMMPS *); - void build(class NeighList *) override; - - protected: - FixIntel *_fix; - - template void build_t(NeighList *, IntelBuffers *); - - template void build_t3(NeighList *, int *); -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/INTEL/npair_halffull_newton_trim_intel.h b/src/INTEL/npair_halffull_newton_trim_intel.h deleted file mode 100644 index 0ca551d682..0000000000 --- a/src/INTEL/npair_halffull_newton_trim_intel.h +++ /dev/null @@ -1,61 +0,0 @@ -// clang-format off -/* -*- 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. -------------------------------------------------------------------------- */ - -/* ---------------------------------------------------------------------- - Contributing author: Stan Moore (SNL) -------------------------------------------------------------------------- */ - -#ifdef NPAIR_CLASS -// clang-format off -NPairStyle(halffull/newton/trim/intel, - NPairHalffullNewtonTrimIntel, - NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | - NP_ORTHO | NP_TRI| NP_TRIM | NP_INTEL); - -NPairStyle(halffull/newton/skip/trim/intel, - NPairHalffullNewtonTrimIntel, - NP_HALF_FULL | NP_NEWTON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | - NP_ORTHO | NP_TRI | NP_SKIP | NP_TRIM | NP_INTEL); -// clang-format on -#else - -#ifndef LMP_NPAIR_HALFFULL_NEWTON_TRIM_INTEL_H -#define LMP_NPAIR_HALFFULL_NEWTON_TRIM_INTEL_H - -#include "fix_intel.h" -#include "npair.h" - -#if defined(_OPENMP) -#include -#endif - -namespace LAMMPS_NS { - -class NPairHalffullNewtonTrimIntel : public NPair { - public: - NPairHalffullNewtonTrimIntel(class LAMMPS *); - void build(class NeighList *) override; - - protected: - FixIntel *_fix; - - template void build_t(NeighList *, IntelBuffers *); - - template void build_t3(NeighList *, int *, IntelBuffers *); -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/INTEL/nstencil_bin_intel.cpp b/src/INTEL/nstencil_bin_intel.cpp new file mode 100644 index 0000000000..36a79e2997 --- /dev/null +++ b/src/INTEL/nstencil_bin_intel.cpp @@ -0,0 +1,70 @@ +/* ---------------------------------------------------------------------- + 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 "nstencil_bin_intel.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +template +NStencilBinIntel::NStencilBinIntel(LAMMPS *lmp) : NStencil(lmp) {} + +/* ---------------------------------------------------------------------- + create stencil based on bin geometry and cutoff +------------------------------------------------------------------------- */ + +template +void NStencilBinIntel::create() +{ + int i, j, k; + + // For half stencils, only the upper plane is needed + int sy_min = sy; + int sz_min = sz; + if (HALF && (!DIM_3D)) sy_min = 0; + if (HALF && DIM_3D) sz_min = 0; + + nstencil = 0; + + // For Intel, half and ortho stencils do not include central bin + // as, historically, this was never included in a stencil. + // Non-Intel npair classes were updated to account for this change, + // but the Intel npair classes have not yet been updated + // if (HALF && (!TRI)) stencil[nstencil++] = 0; + + for (k = -sz_min; k <= sz; k++) { + for (j = -sy_min; j <= sy; j++) { + for (i = -sx; i <= sx; i++) { + + // Now only include "upper right" bins for half and ortho stencils + if (HALF && (!DIM_3D) && (!TRI)) + if (! (j > 0 || (j == 0 && i > 0))) continue; + if (HALF && DIM_3D && (!TRI)) + if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; + + if (bin_distance(i,j,k) < cutneighmaxsq) + stencil[nstencil++] = k * mbiny * mbinx + j * mbinx + i; + } + } + } +} + +namespace LAMMPS_NS { +template class NStencilBinIntel<0,0,0>; +template class NStencilBinIntel<0,1,0>; +template class NStencilBinIntel<1,0,0>; +template class NStencilBinIntel<1,0,1>; +template class NStencilBinIntel<1,1,0>; +template class NStencilBinIntel<1,1,1>; +} diff --git a/src/INTEL/nstencil_bin_intel.h b/src/INTEL/nstencil_bin_intel.h new file mode 100644 index 0000000000..e377db5fe5 --- /dev/null +++ b/src/INTEL/nstencil_bin_intel.h @@ -0,0 +1,65 @@ +/* -*- 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 NSTENCIL_CLASS +// clang-format off +typedef NStencilBinIntel<0, 0, 0> NStencilFullBin2dIntel; +NStencilStyle(full/bin/2d/intel, + NStencilFullBin2dIntel, + NS_FULL | NS_BIN | NS_2D | NS_ORTHO | NS_TRI | NS_INTEL); + +typedef NStencilBinIntel<0, 1, 0> NStencilFullBin3dIntel; +NStencilStyle(full/bin/3d/intel, + NStencilFullBin3dIntel, + NS_FULL | NS_BIN | NS_3D | NS_ORTHO | NS_TRI | NS_INTEL); + +typedef NStencilBinIntel<1, 0, 0> NStencilHalfBin2dIntel; +NStencilStyle(half/bin/2d/intel, + NStencilHalfBin2dIntel, + NS_HALF | NS_BIN | NS_2D | NS_ORTHO | NS_INTEL); + +typedef NStencilBinIntel<1, 0, 1> NStencilHalfBin2dTriIntel; +NStencilStyle(half/bin/2d/tri/intel, + NStencilHalfBin2dTriIntel, + NS_HALF | NS_BIN | NS_2D | NS_TRI | NS_INTEL); + +typedef NStencilBinIntel<1, 1, 0> NStencilHalfBin3dIntel; +NStencilStyle(half/bin/3d/intel, + NStencilHalfBin3dIntel, + NS_HALF | NS_BIN | NS_3D | NS_ORTHO | NS_INTEL); + +typedef NStencilBinIntel<1, 1, 1> NStencilHalfBin3dTriIntel; +NStencilStyle(half/bin/3d/tri/intel, + NStencilHalfBin3dTriIntel, + NS_HALF | NS_BIN | NS_3D | NS_TRI | NS_INTEL); +// clang-format on +#else + +#ifndef LMP_NSTENCIL_BIN_INTEL_H +#define LMP_NSTENCIL_BIN_INTEL_H + +#include "nstencil.h" + +namespace LAMMPS_NS { + +template +class NStencilBinIntel : public NStencil { + public: + NStencilBinIntel(class LAMMPS *); + void create() override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif diff --git a/src/neighbor.cpp b/src/neighbor.cpp index 006101da64..52979ed83e 100644 --- a/src/neighbor.cpp +++ b/src/neighbor.cpp @@ -1991,6 +1991,7 @@ int Neighbor::choose_stencil(NeighRequest *rq) // require match of these request flags and mask bits // (!A != !B) is effectively a logical xor + if (!rq->intel != !(mask & NS_INTEL)) continue; if (!rq->ghost != !(mask & NS_GHOST)) continue; if (!rq->ssa != !(mask & NS_SSA)) continue; diff --git a/src/neighbor.h b/src/neighbor.h index 9c51361aa8..fe2aea0779 100644 --- a/src/neighbor.h +++ b/src/neighbor.h @@ -302,8 +302,9 @@ namespace NeighConst { NS_ORTHO = 1 << 6, NS_TRI = 1 << 7, NS_GHOST = 1 << 8, - NS_SSA = 1 << 9, - NS_MULTI_OLD = 1 << 10 + NS_INTEL = 1 << 9, + NS_SSA = 1 << 10, + NS_MULTI_OLD = 1 << 11 }; enum { From 85765a2bf3242a2f6deb14a2d8c7084ec13076b4 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Mon, 12 Jun 2023 12:45:52 +0300 Subject: [PATCH 016/189] Include born_matrix() definition in bond_gaussian.h --- src/EXTRA-MOLECULE/bond_gaussian.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-MOLECULE/bond_gaussian.h b/src/EXTRA-MOLECULE/bond_gaussian.h index 7af6f1f4d9..e466df47d4 100644 --- a/src/EXTRA-MOLECULE/bond_gaussian.h +++ b/src/EXTRA-MOLECULE/bond_gaussian.h @@ -35,6 +35,7 @@ class BondGaussian : public Bond { void read_restart(FILE *) override; void write_data(FILE *) override; double single(int, double, int, int, double &) override; + void born_matrix(int, double, int, int, double &, double &) override; protected: int *nterms; From a05fcc326efd1f7ebef08516ddf15b262c155215 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Mon, 12 Jun 2023 12:47:21 +0300 Subject: [PATCH 017/189] Implement born_matrix() in bond_gaussian.cpp --- src/EXTRA-MOLECULE/bond_gaussian.cpp | 45 +++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-MOLECULE/bond_gaussian.cpp b/src/EXTRA-MOLECULE/bond_gaussian.cpp index baca0b6e1a..816ab51516 100644 --- a/src/EXTRA-MOLECULE/bond_gaussian.cpp +++ b/src/EXTRA-MOLECULE/bond_gaussian.cpp @@ -12,7 +12,7 @@ ------------------------------------------------------------------------- */ #include "bond_gaussian.h" - +#include #include "atom.h" #include "comm.h" #include "error.h" @@ -35,6 +35,7 @@ BondGaussian::BondGaussian(LAMMPS *lmp) : Bond(lmp), nterms(nullptr), bond_temperature(nullptr), alpha(nullptr), width(nullptr), r0(nullptr) { + born_matrix_enable = 1; } /* ---------------------------------------------------------------------- */ @@ -294,3 +295,45 @@ double BondGaussian::single(int type, double rsq, int /*i*/, int /*j*/, double & return -(force->boltz * bond_temperature[type]) * log(sum_g_i); } + +/* ---------------------------------------------------------------------- */ + +void BondGaussian::born_matrix(int type, double rsq, int /*i*/, int /*j*/, double &du, double &du2) +{ + double r = sqrt(rsq); + + // first derivative of energy with respect to distance + double sum_g_i = 0.0; + double sum_numerator = 0.0; + for (int i = 0; i < nterms[type]; i++) { + double dr = r - r0[type][i]; + double prefactor = (alpha[type][i] / (width[type][i] * sqrt(MY_PI2))); + double exponent = -2 * dr * dr / (width[type][i] * width[type][i]); + double g_i = prefactor * exp(exponent); + sum_g_i += g_i; + sum_numerator += g_i * dr / (width[type][i] * width[type][i]); + } + + if (sum_g_i < SMALL) sum_g_i = SMALL; + du = 4.0 * (force->boltz * bond_temperature[type]) * (sum_numerator / sum_g_i); + + // second derivative of energy with respect to distance + sum_g_i = 0.0; + double sum_dg_i = 0.0; + double sum_d2g_i = 0.0; + for (int i = 0; i < nterms[type]; i++) { + double dr = r - r0[type][i]; + double prefactor = (alpha[type][i] / (width[type][i] * sqrt(MY_PI2))); + double exponent = -2 * dr * dr / (width[type][i] * width[type][i]); + double g_i = prefactor * exp(exponent); + sum_g_i += g_i; + sum_dg_i -= 4.0 * g_i * dr / pow(width[type][i], 2); + sum_d2g_i += 4.0 * g_i * (4.0 * pow(r0[type][i], 2) - 8.0 * r0[type][i] * r - pow(width[type][i], 2) + 4.0 * r * r) / pow(width[type][i], 4) ; + } + + if (sum_g_i < SMALL) sum_g_i = SMALL; + double numerator = sum_d2g_i*sum_g_i - sum_dg_i*sum_dg_i; + double denominator = sum_g_i * sum_g_i; + + du2 = - (force->boltz * bond_temperature[type]) * numerator / denominator; +} From 42c843ff4f783069d409404be42fa89c43f60ccc Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 18 Jun 2023 18:24:24 +0300 Subject: [PATCH 018/189] remove iostream from bond_gaussian.cpp --- src/EXTRA-MOLECULE/bond_gaussian.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EXTRA-MOLECULE/bond_gaussian.cpp b/src/EXTRA-MOLECULE/bond_gaussian.cpp index 816ab51516..9a8546e278 100644 --- a/src/EXTRA-MOLECULE/bond_gaussian.cpp +++ b/src/EXTRA-MOLECULE/bond_gaussian.cpp @@ -12,7 +12,7 @@ ------------------------------------------------------------------------- */ #include "bond_gaussian.h" -#include + #include "atom.h" #include "comm.h" #include "error.h" From 4e17cc551e93faf1433bb002bfbde9838684c2eb Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 18 Jun 2023 18:25:35 +0300 Subject: [PATCH 019/189] inlcude born_matrix() definition in in angle_quartic.h --- src/EXTRA-MOLECULE/angle_quartic.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-MOLECULE/angle_quartic.h b/src/EXTRA-MOLECULE/angle_quartic.h index 3f0396f27b..7de51b24d1 100644 --- a/src/EXTRA-MOLECULE/angle_quartic.h +++ b/src/EXTRA-MOLECULE/angle_quartic.h @@ -35,6 +35,7 @@ class AngleQuartic : public Angle { void read_restart(FILE *) override; void write_data(FILE *) override; double single(int, int, int, int) override; + void born_matrix(int type, int i1, int i2, int i3, double &du, double &du2) override; protected: double *k2, *k3, *k4, *theta0; From eb8512ba2a988b9baea384fe96aa935c28e6005a Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 18 Jun 2023 18:26:48 +0300 Subject: [PATCH 020/189] implementation of born_matrix() for angle_quartic.cpp --- src/EXTRA-MOLECULE/angle_quartic.cpp | 41 +++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-MOLECULE/angle_quartic.cpp b/src/EXTRA-MOLECULE/angle_quartic.cpp index f28e209a77..eaccdbe608 100644 --- a/src/EXTRA-MOLECULE/angle_quartic.cpp +++ b/src/EXTRA-MOLECULE/angle_quartic.cpp @@ -37,7 +37,10 @@ using namespace MathConst; /* ---------------------------------------------------------------------- */ -AngleQuartic::AngleQuartic(LAMMPS *lmp) : Angle(lmp) {} +AngleQuartic::AngleQuartic(LAMMPS *lmp) : Angle(lmp) +{ + born_matrix_enable = 1; +} /* ---------------------------------------------------------------------- */ @@ -286,3 +289,39 @@ double AngleQuartic::single(int type, int i1, int i2, int i3) double dtheta4 = dtheta3 * dtheta; return k2[type] * dtheta2 + k3[type] * dtheta3 + k4[type] * dtheta4; } + +/* ---------------------------------------------------------------------- */ + +void AngleQuartic::born_matrix(int type, int i1, int i2, int i3, double &du, double &du2) +{ + double **x = atom->x; + + double delx1 = x[i1][0] - x[i2][0]; + double dely1 = x[i1][1] - x[i2][1]; + double delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(delx1,dely1,delz1); + double r1 = sqrt(delx1*delx1 + dely1*dely1 + delz1*delz1); + + double delx2 = x[i3][0] - x[i2][0]; + double dely2 = x[i3][1] - x[i2][1]; + double delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(delx2,dely2,delz2); + double r2 = sqrt(delx2*delx2 + dely2*dely2 + delz2*delz2); + + double c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + double theta = acos(c); + + double s = sqrt(1.0 - c*c); + if (s < SMALL) s = SMALL; + + double dtheta = theta - theta0[type]; + double dtheta2 = dtheta * dtheta; + double dtheta3 = dtheta2 * dtheta; + + du = -(2.0 * k2[type] * dtheta + 3.0 * k3[type] * dtheta2 + 4.0 * k4[type] * dtheta3) / s; + du2 = (2.0 * k2[type] + 6.0 * k3[type] * dtheta + 12.0 * k4[type] * dtheta2) / (s*s) - + (2.0 * k2[type] * dtheta + 3.0 * k3[type] * dtheta2 + 4.0 * k4[type] * dtheta3) * c / (s*s*s); +} From 365f4bc55945506de57e44dc0c6ec7102fa2d000 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 18 Jun 2023 18:44:28 +0300 Subject: [PATCH 021/189] non-zero born_matrix_enable flag in angle_fourier.cpp --- src/EXTRA-MOLECULE/angle_fourier.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-MOLECULE/angle_fourier.cpp b/src/EXTRA-MOLECULE/angle_fourier.cpp index 549da0c196..c7eb3d4fe4 100644 --- a/src/EXTRA-MOLECULE/angle_fourier.cpp +++ b/src/EXTRA-MOLECULE/angle_fourier.cpp @@ -39,6 +39,7 @@ using namespace MathConst; AngleFourier::AngleFourier(LAMMPS *lmp) : Angle(lmp) { + born_matrix_enable = 1; k = nullptr; C0 = nullptr; C1 = nullptr; From 2f227614615219d13538481646563c2a8111f251 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Mon, 19 Jun 2023 16:38:34 +0300 Subject: [PATCH 022/189] born_matrix() method in bond_mm3.h --- src/YAFF/bond_mm3.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/YAFF/bond_mm3.h b/src/YAFF/bond_mm3.h index 302c4052d0..ea89ac826d 100644 --- a/src/YAFF/bond_mm3.h +++ b/src/YAFF/bond_mm3.h @@ -35,6 +35,7 @@ class BondMM3 : public Bond { void read_restart(FILE *) override; void write_data(FILE *) override; double single(int, double, int, int, double &) override; + void born_matrix(int, double, int, int, double &, double &) override; protected: double *r0, *k2; From bfc969d5c5ee4da4e9062bffe7f96d55ffb318ab Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Mon, 19 Jun 2023 16:39:49 +0300 Subject: [PATCH 023/189] implementation of born_matrix in bond_mm3.cpp --- src/YAFF/bond_mm3.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/YAFF/bond_mm3.cpp b/src/YAFF/bond_mm3.cpp index a5ef6fb8bc..31ce2dad3e 100644 --- a/src/YAFF/bond_mm3.cpp +++ b/src/YAFF/bond_mm3.cpp @@ -31,7 +31,10 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -BondMM3::BondMM3(LAMMPS *lmp) : Bond(lmp) {} +BondMM3::BondMM3(LAMMPS *lmp) : Bond(lmp) +{ + born_matrix_enable = 1; +} /* ---------------------------------------------------------------------- */ @@ -219,3 +222,19 @@ double BondMM3::single(int type, double rsq, else fforce = 0.0; return k2[type]*dr2*(1.0+K3*dr+K4*dr2); } + +/* ---------------------------------------------------------------------- */ + +void BondMM3::born_matrix(int type, double rsq, int /*i*/, int /*j*/, double &du, double &du2) +{ + double r = sqrt(rsq); + double dr = r - r0[type]; + double dr2 = dr * dr; + double dr3 = dr2 * dr; + + double K3 = -2.55 * k2[type] /force->angstrom; + double K4 = 7.0 * k2[type] * 2.55 * 2.55 / (12.0 * force->angstrom * force->angstrom); + + du = 2.0 * k2[type] * dr + 3.0 * K3 * dr2 + 4.0 * K4 * dr3; + du2 = 2.0 * k2[type] + 6.0 * K3 * dr + 12.0 * K4 * dr2; +} From 6c9d42b7c363a66fa56739f7d3ba3dbc5a1952c2 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Mon, 19 Jun 2023 19:38:50 +0300 Subject: [PATCH 024/189] Include born_matrix() definition in bond_harmonic_shift_cut.h --- src/EXTRA-MOLECULE/bond_harmonic_shift_cut.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-MOLECULE/bond_harmonic_shift_cut.h b/src/EXTRA-MOLECULE/bond_harmonic_shift_cut.h index 752ac010d9..09d6ab5330 100644 --- a/src/EXTRA-MOLECULE/bond_harmonic_shift_cut.h +++ b/src/EXTRA-MOLECULE/bond_harmonic_shift_cut.h @@ -35,6 +35,7 @@ class BondHarmonicShiftCut : public Bond { void read_restart(FILE *) override; void write_data(FILE *) override; double single(int, double, int, int, double &) override; + void born_matrix(int, double, int, int, double &, double &) override; protected: double *k, *r0, *r1; From f6b259b1866d9d5316d6464eae7d551fea9fc760 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Mon, 19 Jun 2023 19:40:11 +0300 Subject: [PATCH 025/189] Implementing born_matrix in bond_harmonic_shift_cut.cpp --- .../bond_harmonic_shift_cut.cpp | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-MOLECULE/bond_harmonic_shift_cut.cpp b/src/EXTRA-MOLECULE/bond_harmonic_shift_cut.cpp index fedcb95ee8..ebcfdb0258 100644 --- a/src/EXTRA-MOLECULE/bond_harmonic_shift_cut.cpp +++ b/src/EXTRA-MOLECULE/bond_harmonic_shift_cut.cpp @@ -31,7 +31,10 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -BondHarmonicShiftCut::BondHarmonicShiftCut(LAMMPS *lmp) : Bond(lmp) {} +BondHarmonicShiftCut::BondHarmonicShiftCut(LAMMPS *lmp) : Bond(lmp) +{ + born_matrix_enable = 1; +} /* ---------------------------------------------------------------------- */ @@ -219,3 +222,19 @@ double BondHarmonicShiftCut::single(int type, double rsq, int /*i*/, int /*j*/, fforce = -2.0*k[type]*dr/r; return k[type]*(dr*dr - dr2*dr2); } + +/* ---------------------------------------------------------------------- */ + +void BondHarmonicShiftCut::born_matrix(int type, double rsq, int /*i*/, int /*j*/, double &du, double &du2) +{ + du = 0.0; + du2 = 0.0; + + double r = sqrt(rsq); + if (r>r1[type]) return; + + double dr = r - r0[type]; + + du2 = 2 * k[type]; + if (r > 0.0) du = du2 * dr; +} From ad3752431fe9711ef40f2d53cd28d7ab83153b73 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Mon, 19 Jun 2023 19:42:01 +0300 Subject: [PATCH 026/189] Regular pointer for coord and coordp in compute_stress_mop_profile.h --- src/EXTRA-COMPUTE/compute_stress_mop_profile.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h index 2b0ffef0f8..1b5b3a911f 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h @@ -49,7 +49,7 @@ class ComputeStressMopProfile : public Compute { int originflag; double origin, delta, offset, invdelta; int nbins; - double **coord, **coordp; + double *coord, *coordp; double **values_local, **values_global; double **bond_local, **bond_global; double **local_contribution; From dc1eb43cf2f15c61c0b44bb872407c4b5091e135 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Mon, 19 Jun 2023 19:47:34 +0300 Subject: [PATCH 027/189] Cleaning coord and coordp vectors in compute_stress_mop_profile.cpp --- .../compute_stress_mop_profile.cpp | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp index 1f1dc30733..3a85869f3c 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp @@ -258,7 +258,7 @@ void ComputeStressMopProfile::compute_array() MPI_Allreduce(&bond_local[0][0],&bond_global[0][0],nbins*nvalues,MPI_DOUBLE,MPI_SUM,world); for (int ibin=0; ibin ((hi-lo) * invdelta + 1.5); //allocate bin arrays - memory->create(coord,nbins,1,"stress/mop/profile:coord"); - memory->create(coordp,nbins,1,"stress/mop/profile:coordp"); + memory->create(coord,nbins,"stress/mop/profile:coord"); + memory->create(coordp,nbins,"stress/mop/profile:coordp"); memory->create(values_local,nbins,nvalues,"stress/mop/profile:values_local"); memory->create(values_global,nbins,nvalues,"stress/mop/profile:values_global"); memory->create(bond_local,nbins,nvalues,"stress/mop/profile:bond_local"); @@ -652,11 +652,11 @@ void ComputeStressMopProfile::setup_bins() // set bin coordinates for (i = 0; i < nbins; i++) { - coord[i][0] = offset + i*delta; - if (coord[i][0] < (domain->boxlo[dir]+domain->prd_half[dir])) { - coordp[i][0] = coord[i][0] + domain->prd[dir]; + coord[i] = offset + i*delta; + if (coord[i] < (domain->boxlo[dir]+domain->prd_half[dir])) { + coordp[i] = coord[i] + domain->prd[dir]; } else { - coordp[i][0] = coord[i][0] - domain->prd[dir]; + coordp[i] = coord[i] - domain->prd[dir]; } } } From 2631a159affeedef3dd2eeaaa1c3ca0529396b97 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 20 Jun 2023 15:41:09 +0300 Subject: [PATCH 028/189] define born_matrix in angle_mm3.h --- src/YAFF/angle_mm3.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/YAFF/angle_mm3.h b/src/YAFF/angle_mm3.h index 95009a9cf6..22f5bd746c 100644 --- a/src/YAFF/angle_mm3.h +++ b/src/YAFF/angle_mm3.h @@ -35,6 +35,7 @@ class AngleMM3 : public Angle { void read_restart(FILE *) override; void write_data(FILE *) override; double single(int, int, int, int) override; + void born_matrix(int type, int i1, int i2, int i3, double &du, double &du2) override; protected: double *theta0, *k2; From bb2d691e7863e5b4746da6fd8f6a951581cbde51 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 20 Jun 2023 15:42:47 +0300 Subject: [PATCH 029/189] implement born_matrix in angle_mm3.cpp --- src/YAFF/angle_mm3.cpp | 45 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/YAFF/angle_mm3.cpp b/src/YAFF/angle_mm3.cpp index c75a0d8308..af199f6fe9 100644 --- a/src/YAFF/angle_mm3.cpp +++ b/src/YAFF/angle_mm3.cpp @@ -36,7 +36,10 @@ using namespace MathConst; /* ---------------------------------------------------------------------- */ -AngleMM3::AngleMM3(LAMMPS *lmp) : Angle(lmp) {} +AngleMM3::AngleMM3(LAMMPS *lmp) : Angle(lmp) +{ + born_matrix_enable = 1; +} /* ---------------------------------------------------------------------- */ @@ -284,3 +287,43 @@ double AngleMM3::single(int type, int i1, int i2, int i3) return energy; } + +/* ---------------------------------------------------------------------- */ + +void AngleMM3::born_matrix(int type, int i1, int i2, int i3, double &du, double &du2) +{ + double **x = atom->x; + + double delx1 = x[i1][0] - x[i2][0]; + double dely1 = x[i1][1] - x[i2][1]; + double delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(delx1,dely1,delz1); + double r1 = sqrt(delx1*delx1 + dely1*dely1 + delz1*delz1); + + double delx2 = x[i3][0] - x[i2][0]; + double dely2 = x[i3][1] - x[i2][1]; + double delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(delx2,dely2,delz2); + double r2 = sqrt(delx2*delx2 + dely2*dely2 + delz2*delz2); + + double c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + double theta = acos(c); + + double s = sqrt(1.0 - c*c); + if (s < SMALL) s = SMALL; + s = 1.0/s; + + double dtheta = theta - theta0[type]; + double dtheta2 = dtheta*dtheta; + double dtheta3 = dtheta2*dtheta; + double dtheta4 = dtheta3*dtheta; + double dtheta5 = dtheta4*dtheta; + double df = 2.0 * dtheta - 2.406423 * dtheta2 + 0.735348 * dtheta3 - 0.65832 * dtheta4 + 1.42254 * dtheta5; + double d2f = 2.0 - 4.812846 * dtheta + 2.206044 * dtheta2 - 2.63328 * dtheta3 + 7.1127 * dtheta4; + + du = -k2[type] * df / s; + du2 = k2[type] * (d2f - df * c / s) / (s * s) ; +} From 345a834c7e0693770e37a13005118ad29e19ff7b Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 20 Jun 2023 16:20:57 +0300 Subject: [PATCH 030/189] Include definition of born_matrix() in angle_cosine_periodic.h --- src/EXTRA-MOLECULE/angle_cosine_periodic.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-MOLECULE/angle_cosine_periodic.h b/src/EXTRA-MOLECULE/angle_cosine_periodic.h index 4e584b4543..f04ed04784 100644 --- a/src/EXTRA-MOLECULE/angle_cosine_periodic.h +++ b/src/EXTRA-MOLECULE/angle_cosine_periodic.h @@ -35,6 +35,7 @@ class AngleCosinePeriodic : public Angle { void read_restart(FILE *) override; void write_data(FILE *) override; double single(int, int, int, int) override; + void born_matrix(int type, int i1, int i2, int i3, double &du, double &du2) override; protected: double *k; From 7f3a930d8923a13fb14c02e85d56edb703b1c2cd Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 20 Jun 2023 16:21:57 +0300 Subject: [PATCH 031/189] Implement born_matrix() in angle_cosine_periodic.cpp --- src/EXTRA-MOLECULE/angle_cosine_periodic.cpp | 40 +++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-MOLECULE/angle_cosine_periodic.cpp b/src/EXTRA-MOLECULE/angle_cosine_periodic.cpp index 15d0575f6d..34a8e9d8e5 100644 --- a/src/EXTRA-MOLECULE/angle_cosine_periodic.cpp +++ b/src/EXTRA-MOLECULE/angle_cosine_periodic.cpp @@ -38,7 +38,10 @@ using namespace MathSpecial; /* ---------------------------------------------------------------------- */ -AngleCosinePeriodic::AngleCosinePeriodic(LAMMPS *lmp) : Angle(lmp) {} +AngleCosinePeriodic::AngleCosinePeriodic(LAMMPS *lmp) : Angle(lmp) +{ + born_matrix_enable = 1; +} /* ---------------------------------------------------------------------- */ @@ -298,3 +301,38 @@ double AngleCosinePeriodic::single(int type, int i1, int i2, int i3) c = cos(acos(c)*multiplicity[type]); return 2.0*k[type]*(1.0-b[type]*powsign(multiplicity[type])*c); } + +/* ---------------------------------------------------------------------- */ + +void AngleCosinePeriodic::born_matrix(int type, int i1, int i2, int i3, double &du, double &du2) +{ + double **x = atom->x; + + double delx1 = x[i1][0] - x[i2][0]; + double dely1 = x[i1][1] - x[i2][1]; + double delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(delx1,dely1,delz1); + double r1 = sqrt(delx1*delx1 + dely1*dely1 + delz1*delz1); + + double delx2 = x[i3][0] - x[i2][0]; + double dely2 = x[i3][1] - x[i2][1]; + double delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(delx2,dely2,delz2); + double r2 = sqrt(delx2*delx2 + dely2*dely2 + delz2*delz2); + + double c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + double theta = acos(c); + + double s = sqrt(1.0 - c*c); + if (s < SMALL) s = SMALL; + s = 1.0/s; + + double m_angle = multiplicity[type] * theta; + double prefactor = -2.0 * k[type] * b[type] * powsign(multiplicity[type]) * multiplicity[type]; + + du = prefactor * sin(m_angle) / s; + du2 = prefactor * (c * sin(m_angle) - s * cos(m_angle) * multiplicity[type]) / (s * s * s); +} From fb31ffe17cbff6854bc60870a8be54258df7ed07 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 21 Jun 2023 10:56:53 +0300 Subject: [PATCH 032/189] Definition of born_matrix() in dihedral_helix.h --- src/EXTRA-MOLECULE/dihedral_helix.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-MOLECULE/dihedral_helix.h b/src/EXTRA-MOLECULE/dihedral_helix.h index 436895c5c3..172a8c3469 100644 --- a/src/EXTRA-MOLECULE/dihedral_helix.h +++ b/src/EXTRA-MOLECULE/dihedral_helix.h @@ -33,6 +33,7 @@ class DihedralHelix : public Dihedral { void write_restart(FILE *) override; void read_restart(FILE *) override; void write_data(FILE *) override; + void born_matrix(int, int, int, int, int, double &, double &) override; protected: double *aphi, *bphi, *cphi; From 7ab9da0212f1f3601f7238fe2997f9313d8cc923 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 21 Jun 2023 10:59:13 +0300 Subject: [PATCH 033/189] Implementation of born_matrix in dihedral_helix.cpp --- src/EXTRA-MOLECULE/dihedral_helix.cpp | 106 ++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/src/EXTRA-MOLECULE/dihedral_helix.cpp b/src/EXTRA-MOLECULE/dihedral_helix.cpp index 059bef74a4..1d99de6ba9 100644 --- a/src/EXTRA-MOLECULE/dihedral_helix.cpp +++ b/src/EXTRA-MOLECULE/dihedral_helix.cpp @@ -41,6 +41,7 @@ using namespace MathConst; DihedralHelix::DihedralHelix(LAMMPS *lmp) : Dihedral(lmp) { writedata = 1; + born_matrix_enable = 1; } /* ---------------------------------------------------------------------- */ @@ -324,3 +325,108 @@ void DihedralHelix::write_data(FILE *fp) for (int i = 1; i <= atom->ndihedraltypes; i++) fprintf(fp,"%d %g %g %g\n",i,aphi[i],bphi[i],cphi[i]); } + +/* ----------------------------------------------------------------------*/ + +void DihedralHelix::born_matrix(int nd, int i1, int i2, int i3, int i4, + double &du, double &du2) +{ + double vb1x,vb1y,vb1z,vb2x,vb2y,vb2z,vb3x,vb3y,vb3z,vb2xm,vb2ym,vb2zm; + double sb1,sb3,rb1,rb3,c0,b1mag2,b1mag,b2mag2; + double b2mag,b3mag2,b3mag,ctmp,r12c1,c1mag,r12c2; + double c2mag,sc1,sc2,s12,c; + double cx,cy,cz,cmag,dx,phi,si,siinv,sin2; + + int **dihedrallist = neighbor->dihedrallist; + double **x = atom->x; + + int type = dihedrallist[nd][4]; + + // 1st bond + + vb1x = x[i1][0] - x[i2][0]; + vb1y = x[i1][1] - x[i2][1]; + vb1z = x[i1][2] - x[i2][2]; + + // 2nd bond + + vb2x = x[i3][0] - x[i2][0]; + vb2y = x[i3][1] - x[i2][1]; + vb2z = x[i3][2] - x[i2][2]; + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + + // 3rd bond + + vb3x = x[i4][0] - x[i3][0]; + vb3y = x[i4][1] - x[i3][1]; + vb3z = x[i4][2] - x[i3][2]; + + // c0 calculation + + sb1 = 1.0 / (vb1x*vb1x + vb1y*vb1y + vb1z*vb1z); + sb3 = 1.0 / (vb3x*vb3x + vb3y*vb3y + vb3z*vb3z); + + rb1 = sqrt(sb1); + rb3 = sqrt(sb3); + + c0 = (vb1x*vb3x + vb1y*vb3y + vb1z*vb3z) * rb1*rb3; + + // 1st and 2nd angle + + b1mag2 = vb1x*vb1x + vb1y*vb1y + vb1z*vb1z; + b1mag = sqrt(b1mag2); + b2mag2 = vb2x*vb2x + vb2y*vb2y + vb2z*vb2z; + b2mag = sqrt(b2mag2); + b3mag2 = vb3x*vb3x + vb3y*vb3y + vb3z*vb3z; + b3mag = sqrt(b3mag2); + + ctmp = vb1x*vb2x + vb1y*vb2y + vb1z*vb2z; + r12c1 = 1.0 / (b1mag*b2mag); + c1mag = ctmp * r12c1; + + ctmp = vb2xm*vb3x + vb2ym*vb3y + vb2zm*vb3z; + r12c2 = 1.0 / (b2mag*b3mag); + c2mag = ctmp * r12c2; + + // cos and sin of 2 angles and final c + + sin2 = MAX(1.0 - c1mag*c1mag,0.0); + sc1 = sqrt(sin2); + if (sc1 < SMALL) sc1 = SMALL; + sc1 = 1.0/sc1; + + sin2 = MAX(1.0 - c2mag*c2mag,0.0); + sc2 = sqrt(sin2); + if (sc2 < SMALL) sc2 = SMALL; + sc2 = 1.0/sc2; + + s12 = sc1 * sc2; + c = (c0 + c1mag*c2mag) * s12; + + cx = vb1y*vb2z - vb1z*vb2y; + cy = vb1z*vb2x - vb1x*vb2z; + cz = vb1x*vb2y - vb1y*vb2x; + cmag = sqrt(cx*cx + cy*cy + cz*cz); + dx = (cx*vb3x + cy*vb3y + cz*vb3z)/cmag/b3mag; + + // error check + + if (c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) problem(FLERR, i1, i2, i3, i4); + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + phi = acos(c); + if (dx > 0.0) phi *= -1.0; + si = sin(phi); + if (fabs(si) < SMALLER) si = SMALLER; + siinv = 1.0/si; + + du = -aphi[type] + 3.0*bphi[type]*sin(3.0*phi)*siinv + + cphi[type]*sin(phi + MY_PI4)*siinv; + du2 = -(9.0*bphi[type]*cos(3.0*phi) + cphi[type]*cos(phi + MY_PI4))*siinv*siinv + + (3.0*bphi[type]*sin(3.0*phi) + cphi[type]*sin(phi + MY_PI4))*c*siinv*siinv*siinv; +} From ae96c9bd47e52d3c3066b40914b8e27ab6b042eb Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 22 Jun 2023 16:58:00 +0300 Subject: [PATCH 034/189] Define born_matrix() in dihedral_quadratic.h --- src/EXTRA-MOLECULE/dihedral_quadratic.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-MOLECULE/dihedral_quadratic.h b/src/EXTRA-MOLECULE/dihedral_quadratic.h index 90d8c3be6e..89f6fa3b25 100644 --- a/src/EXTRA-MOLECULE/dihedral_quadratic.h +++ b/src/EXTRA-MOLECULE/dihedral_quadratic.h @@ -33,6 +33,7 @@ class DihedralQuadratic : public Dihedral { void write_restart(FILE *) override; void read_restart(FILE *) override; void write_data(FILE *) override; + void born_matrix(int, int, int, int, int, double &, double &) override; protected: double *k, *phi0; From 8e1711c8031699756ba3b5d84fa4582ad161a357 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 22 Jun 2023 17:00:10 +0300 Subject: [PATCH 035/189] Implement born_matrix in dihedral_quadratic.cpp --- src/EXTRA-MOLECULE/dihedral_quadratic.cpp | 110 ++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/src/EXTRA-MOLECULE/dihedral_quadratic.cpp b/src/EXTRA-MOLECULE/dihedral_quadratic.cpp index cbe9e3e3a2..f576e6efdd 100644 --- a/src/EXTRA-MOLECULE/dihedral_quadratic.cpp +++ b/src/EXTRA-MOLECULE/dihedral_quadratic.cpp @@ -41,6 +41,7 @@ using namespace MathConst; DihedralQuadratic::DihedralQuadratic(LAMMPS *lmp) : Dihedral(lmp) { writedata = 1; + born_matrix_enable = 1; } /* ---------------------------------------------------------------------- */ @@ -327,3 +328,112 @@ void DihedralQuadratic::write_data(FILE *fp) for (int i = 1; i <= atom->ndihedraltypes; i++) fprintf(fp,"%d %g %g \n",i,k[i],phi0[i]*180.0/MY_PI); } + +/* ----------------------------------------------------------------------*/ + +void DihedralQuadratic::born_matrix(int nd, int i1, int i2, int i3, int i4, + double &du, double &du2) +{ + double vb1x,vb1y,vb1z,vb2x,vb2y,vb2z,vb3x,vb3y,vb3z,vb2xm,vb2ym,vb2zm; + double sb1,sb3,rb1,rb3,c0,b1mag2,b1mag,b2mag2; + double b2mag,b3mag2,b3mag,ctmp,r12c1,c1mag,r12c2; + double c2mag,sc1,sc2,s12,c; + double s1,s2,cx,cy,cz,cmag,dx,phi,si,siinv,sin2; + + int **dihedrallist = neighbor->dihedrallist; + double **x = atom->x; + + int type = dihedrallist[nd][4]; + + // 1st bond + + vb1x = x[i1][0] - x[i2][0]; + vb1y = x[i1][1] - x[i2][1]; + vb1z = x[i1][2] - x[i2][2]; + + // 2nd bond + + vb2x = x[i3][0] - x[i2][0]; + vb2y = x[i3][1] - x[i2][1]; + vb2z = x[i3][2] - x[i2][2]; + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + + // 3rd bond + vb3x = x[i4][0] - x[i3][0]; + vb3y = x[i4][1] - x[i3][1]; + vb3z = x[i4][2] - x[i3][2]; + + // c0 calculation + + sb1 = 1.0 / (vb1x*vb1x + vb1y*vb1y + vb1z*vb1z); + sb3 = 1.0 / (vb3x*vb3x + vb3y*vb3y + vb3z*vb3z); + + rb1 = sqrt(sb1); + rb3 = sqrt(sb3); + + c0 = (vb1x*vb3x + vb1y*vb3y + vb1z*vb3z) * rb1*rb3; + + // 1st and 2nd angle + + b1mag2 = vb1x*vb1x + vb1y*vb1y + vb1z*vb1z; + b1mag = sqrt(b1mag2); + b2mag2 = vb2x*vb2x + vb2y*vb2y + vb2z*vb2z; + b2mag = sqrt(b2mag2); + b3mag2 = vb3x*vb3x + vb3y*vb3y + vb3z*vb3z; + b3mag = sqrt(b3mag2); + + ctmp = vb1x*vb2x + vb1y*vb2y + vb1z*vb2z; + r12c1 = 1.0 / (b1mag*b2mag); + c1mag = ctmp * r12c1; + + ctmp = vb2xm*vb3x + vb2ym*vb3y + vb2zm*vb3z; + r12c2 = 1.0 / (b2mag*b3mag); + c2mag = ctmp * r12c2; + + // cos and sin of 2 angles and final c + + sin2 = MAX(1.0 - c1mag*c1mag,0.0); + sc1 = sqrt(sin2); + if (sc1 < SMALL) sc1 = SMALL; + sc1 = 1.0/sc1; + + sin2 = MAX(1.0 - c2mag*c2mag,0.0); + sc2 = sqrt(sin2); + if (sc2 < SMALL) sc2 = SMALL; + sc2 = 1.0/sc2; + + s1 = sc1 * sc1; + s2 = sc2 * sc2; + s12 = sc1 * sc2; + c = (c0 + c1mag*c2mag) * s12; + + cx = vb1y*vb2z - vb1z*vb2y; + cy = vb1z*vb2x - vb1x*vb2z; + cz = vb1x*vb2y - vb1y*vb2x; + cmag = sqrt(cx*cx + cy*cy + cz*cz); + dx = (cx*vb3x + cy*vb3y + cz*vb3z)/cmag/b3mag; + + // error check + + if (c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) + problem(FLERR, i1, i2, i3, i4); + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + phi = acos(c); + if (dx > 0.0) phi *= -1.0; + si = sin(phi); + if (fabs(si) < SMALLER) si = SMALLER; + siinv = 1.0/si; + + double dphi = phi-phi0[type]; + if (dphi > MY_PI) dphi -= 2*MY_PI; + else if (dphi < -MY_PI) dphi += 2*MY_PI; + + du = - 2.0 * k[type] * dphi * siinv; + du2 = 2.0 * k[type] * siinv * siinv * ( 1.0 - dphi * c * siinv) ; +} From 6e32d2932275271ced51152b3c849318c8956601 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 29 Jun 2023 08:53:50 +0300 Subject: [PATCH 036/189] clean up of originflag variable in compute_stress_mop_profile.cpp --- .../compute_stress_mop_profile.cpp | 38 ++++++++----------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp index 3a85869f3c..d37a941fde 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp @@ -38,7 +38,6 @@ using namespace LAMMPS_NS; enum { X, Y, Z }; -enum { LOWER, CENTER, UPPER, COORD }; enum { TOTAL, CONF, KIN, PAIR, BOND }; // clang-format off @@ -63,11 +62,15 @@ ComputeStressMopProfile::ComputeStressMopProfile(LAMMPS *lmp, int narg, char **a // bin parameters - if (strcmp(arg[4],"lower") == 0) originflag = LOWER; - else if (strcmp(arg[4],"center") == 0) originflag = CENTER; - else if (strcmp(arg[4],"upper") == 0) originflag = UPPER; - else originflag = COORD; - if (originflag == COORD) origin = utils::numeric(FLERR,arg[4],false,lmp); + if (strcmp(arg[4],"lower") == 0) { + origin = domain->boxlo[dir]; + } else if (strcmp(arg[4],"center") == 0) { + origin = 0.5 * (domain->boxlo[dir] + domain->boxhi[dir]); + } else if (strcmp(arg[4],"upper") == 0) { + origin = domain->boxhi[dir]; + } else { + origin = utils::numeric(FLERR,arg[4],false,lmp); + } delta = utils::numeric(FLERR,arg[5],false,lmp); invdelta = 1.0/delta; @@ -620,23 +623,14 @@ void ComputeStressMopProfile::setup_bins() boxlo = domain->boxlo; boxhi = domain->boxhi; - if (originflag == LOWER) origin = boxlo[dir]; - else if (originflag == UPPER) origin = boxhi[dir]; - else if (originflag == CENTER) - origin = 0.5 * (boxlo[dir] + boxhi[dir]); + if ((origin > domain->boxhi[dir]) || (origin < domain->boxlo[dir])) + error->all(FLERR,"Origin of bins for compute stress/mop/profile is out of bounds" ); - if (origin < boxlo[dir]) { - error->all(FLERR,"Origin of bins for compute stress/mop/profile is out of bounds" ); - } else { - n = static_cast ((origin - boxlo[dir]) * invdelta); - lo = origin - n*delta; - } - if (origin < boxhi[dir]) { - n = static_cast ((boxhi[dir] - origin) * invdelta); - hi = origin + n*delta; - } else { - error->all(FLERR,"Origin of bins for compute stress/mop/profile is out of bounds" ); - } + n = static_cast ((origin - boxlo[dir]) * invdelta); + lo = origin - n*delta; + + n = static_cast ((boxhi[dir] - origin) * invdelta); + hi = origin + n*delta; offset = lo; nbins = static_cast ((hi-lo) * invdelta + 1.5); From 484e7ad0e3c295514f063e1e7933f3683e93f383 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 29 Jun 2023 08:54:30 +0300 Subject: [PATCH 037/189] clean up of originflag from compute_stress_mop_profile.h --- src/EXTRA-COMPUTE/compute_stress_mop_profile.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h index 1b5b3a911f..5890505d71 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h @@ -46,7 +46,6 @@ class ComputeStressMopProfile : public Compute { int bondflag; - int originflag; double origin, delta, offset, invdelta; int nbins; double *coord, *coordp; From 005c15c07b3d4dd445ba276e8f3615852fdb7e71 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 5 Jul 2023 13:26:47 +0300 Subject: [PATCH 038/189] compute kinetic contribution without assuming orthogonal geometry --- src/EXTRA-COMPUTE/compute_stress_mop.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop.cpp b/src/EXTRA-COMPUTE/compute_stress_mop.cpp index 98e3bf7043..18e6ec6115 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop.cpp @@ -422,6 +422,11 @@ void ComputeStressMop::compute_pairs() xi[1] = atom->x[i][1]; xi[2] = atom->x[i][2]; + // minimum image of xi with respect to the plane + xi[dir] -= pos; + domain->minimum_image(xi[0], xi[1], xi[2]); + xi[dir] += pos; + //velocities at t vi[0] = atom->v[i][0]; vi[1] = atom->v[i][1]; @@ -447,10 +452,8 @@ void ComputeStressMop::compute_pairs() // at each timestep, must check atoms going through the // image of the plane that is closest to the box - double pos_temp = pos+copysign(1.0,domain->prd_half[dir]-pos)*domain->prd[dir]; - if (fabs(xi[dir]-pos)= 0)) { // sgn = copysign(1.0,vi[dir]-vcm[dir]); sgn = copysign(1.0,vi[dir]); From 94fa2f51c9189874ab84661519f0047cd1f0c9db Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 5 Jul 2023 13:38:56 +0300 Subject: [PATCH 039/189] compute kinetic contribution without assuming orthogonal geometry --- .../compute_stress_mop_profile.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp index d37a941fde..c8793586ea 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp @@ -453,7 +453,22 @@ void ComputeStressMopProfile::compute_pairs() pos = coord[ibin]; pos1 = coordp[ibin]; - if (((xi[dir]-pos)*(xj[dir]-pos)*(xi[dir]-pos1)*(xj[dir]-pos1)<0)) { + // minimum image of xi with respect to the plane + xi[dir] -= pos; + domain->minimum_image(xi[0], xi[1], xi[2]); + xi[dir] += pos; + + // minimum image of xj with respect to xi + xj[0] -= xi[0]; + xj[1] -= xi[1]; + xj[2] -= xi[2]; + domain->minimum_image(xi[0], xi[1], xi[2]); + xj[0] += xi[0]; + xj[1] += xi[1]; + xj[2] += xi[2]; + + double tau = (xi[dir] - pos) / (xi[dir] - xj[dir]); + if ((tau <= 1) && (tau >= 0)) { sgn = copysign(1.0,vi[dir]); From 79ed2d9e8bb1bd5fefbf6fcd24eff5fd2d452381 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 5 Jul 2023 16:35:25 +0300 Subject: [PATCH 040/189] Definition of compute_angle and related variables in compute_stress_mop_profile.h --- src/EXTRA-COMPUTE/compute_stress_mop_profile.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h index 5890505d71..c7214c055c 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h @@ -39,18 +39,20 @@ class ComputeStressMopProfile : public Compute { private: void compute_pairs(); void compute_bonds(); + void compute_angles(); void setup_bins(); int nvalues, dir; int *which; - int bondflag; + int bondflag, angleflag; double origin, delta, offset, invdelta; int nbins; double *coord, *coordp; double **values_local, **values_global; double **bond_local, **bond_global; + double **angle_local, **angle_global; double **local_contribution; double dt, nktv2p, ftm2v; From 9aa9bdd3ba4bd6f59cbe2723bedc46936cf77b62 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 5 Jul 2023 16:45:53 +0300 Subject: [PATCH 041/189] Implementation of compute_angles in compute_stress_mop_profile.cpp and related adjustments to flags/memory allocations --- .../compute_stress_mop_profile.cpp | 227 +++++++++++++++++- 1 file changed, 221 insertions(+), 6 deletions(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp index c8793586ea..dabbbc4781 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp @@ -13,7 +13,7 @@ /*------------------------------------------------------------------------ Contributing Authors : Romain Vermorel (LFCR), Laurent Joly (ULyon) - Support for bonds added by : Evangelos Voyiatzis (NovaMechanics) + Support for bonds and angles added by : Evangelos Voyiatzis (NovaMechanics) --------------------------------------------------------------------------*/ #include "compute_stress_mop_profile.h" @@ -38,7 +38,7 @@ using namespace LAMMPS_NS; enum { X, Y, Z }; -enum { TOTAL, CONF, KIN, PAIR, BOND }; +enum { TOTAL, CONF, KIN, PAIR, BOND, ANGLE }; // clang-format off /* ---------------------------------------------------------------------- */ @@ -107,6 +107,11 @@ ComputeStressMopProfile::ComputeStressMopProfile(LAMMPS *lmp, int narg, char **a which[nvalues] = BOND; nvalues++; } + } else if (strcmp(arg[iarg],"angle") == 0) { + for (i=0; i<3; i++) { + which[nvalues] = ANGLE; + nvalues++; + } } else error->all(FLERR, "Illegal compute stress/mop/profile command"); //break; iarg++; @@ -131,6 +136,8 @@ ComputeStressMopProfile::ComputeStressMopProfile(LAMMPS *lmp, int narg, char **a values_local = values_global = array = nullptr; bond_local = nullptr; bond_global = nullptr; + angle_local = nullptr; + angle_global = nullptr; local_contribution = nullptr; // bin setup @@ -159,6 +166,8 @@ ComputeStressMopProfile::~ComputeStressMopProfile() memory->destroy(values_global); memory->destroy(bond_local); memory->destroy(bond_global); + memory->destroy(angle_local); + memory->destroy(angle_global); memory->destroy(local_contribution); memory->destroy(array); } @@ -206,9 +215,14 @@ void ComputeStressMopProfile::init() if (force->bond) bondflag = 1; - if (force->angle) - if ((strcmp(force->angle_style, "zero") != 0) && (strcmp(force->angle_style, "none") != 0)) - error->all(FLERR,"compute stress/mop/profile does not account for angle potentials"); + if (force->angle) { + if (force->angle->born_matrix_enable == 0) { + if ((strcmp(force->angle_style, "zero") != 0) && (strcmp(force->angle_style, "none") != 0)) + error->all(FLERR,"compute stress/mop/profile does not account for angle potentials"); + } else { + angleflag = 1; + } + } if (force->dihedral) if ((strcmp(force->dihedral_style, "zero") != 0) && (strcmp(force->dihedral_style, "none") != 0)) error->all(FLERR,"compute stress/mop/profile does not account for dihedral potentials"); @@ -260,13 +274,27 @@ void ComputeStressMopProfile::compute_array() // sum bond contribution over all procs MPI_Allreduce(&bond_local[0][0],&bond_global[0][0],nbins*nvalues,MPI_DOUBLE,MPI_SUM,world); + if (angleflag) { + //Compute angle contribution on separate procs + compute_angles(); + } else { + for (int m = 0; m < nbins; m++) { + for (int i = 0; i < nvalues; i++) { + angle_local[m][i] = 0.0; + } + } + } + + // sum angle contribution over all procs + MPI_Allreduce(&angle_local[0][0],&angle_global[0][0],nbins*nvalues,MPI_DOUBLE,MPI_SUM,world); + for (int ibin=0; ibinx; + tagint *tag = atom->tag; + int *num_angle = atom->num_angle; + tagint **angle_atom1 = atom->angle_atom1; + tagint **angle_atom2 = atom->angle_atom2; + tagint **angle_atom3 = atom->angle_atom3; + int **angle_type = atom->angle_type; + int *mask = atom->mask; + + int *molindex = atom->molindex; + int *molatom = atom->molatom; + Molecule **onemols = atom->avec->onemols; + + int nlocal = atom->nlocal; + int molecular = atom->molecular; + + // loop over all atoms and their angles + Angle *angle = force->angle; + + double duang, du2ang; + double dx[3] = {0.0, 0.0, 0.0}; + double dx_left[3] = {0.0, 0.0, 0.0}; + double dx_right[3] = {0.0, 0.0, 0.0}; + double x_angle_left[3] = {0.0, 0.0, 0.0}; + double x_angle_middle[3] = {0.0, 0.0, 0.0}; + double x_angle_right[3] = {0.0, 0.0, 0.0}; + double dcos_theta[3] = {0.0, 0.0, 0.0}; + + // initialization + for (int m = 0; m < nbins; m++) { + for (int i = 0; i < nvalues; i++) { + angle_local[m][i] = 0.0; + } + local_contribution[m][0] = 0.0; + local_contribution[m][1] = 0.0; + local_contribution[m][2] = 0.0; + } + + + for (atom2 = 0; atom2 < nlocal; atom2++) { + if (!(mask[atom2] & groupbit)) continue; + + if (molecular == 1) + na = num_angle[atom2]; + else { + if (molindex[atom2] < 0) continue; + imol = molindex[atom2]; + iatom = molatom[atom2]; + na = onemols[imol]->num_angle[iatom]; + } + + for (int i = 0; i < na; i++) { + if (molecular == 1) { + if (tag[atom2] != angle_atom2[atom2][i]) continue; + atype = angle_type[atom2][i]; + atom1 = atom->map(angle_atom1[atom2][i]); + atom3 = atom->map(angle_atom3[atom2][i]); + } else { + if (tag[atom2] != onemols[imol]->angle_atom2[atom2][i]) continue; + atype = onemols[imol]->angle_type[atom2][i]; + tagprev = tag[atom2] - iatom - 1; + atom1 = atom->map(onemols[imol]->angle_atom1[atom2][i] + tagprev); + atom3 = atom->map(onemols[imol]->angle_atom3[atom2][i] + tagprev); + } + + if (atom1 < 0 || !(mask[atom1] & groupbit)) continue; + if (atom3 < 0 || !(mask[atom3] & groupbit)) continue; + if (atype <= 0) continue; + + for (int ibin = 0; ibinminimum_image(dx[0], dx[1], dx[2]); + x_angle_left[0] = dx[0]; + x_angle_left[1] = dx[1]; + x_angle_left[2] = dx[2]; + x_angle_left[dir] += pos; + + // minimum image of atom2 with respect to atom1 + dx_left[0] = x[atom2][0] - x_angle_left[0]; + dx_left[1] = x[atom2][1] - x_angle_left[1]; + dx_left[2] = x[atom2][2] - x_angle_left[2]; + domain->minimum_image(dx_left[0], dx_left[1], dx_left[2]); + x_angle_middle[0] = x_angle_left[0] + dx_left[0]; + x_angle_middle[1] = x_angle_left[1] + dx_left[1]; + x_angle_middle[2] = x_angle_left[2] + dx_left[2]; + + // minimum image of atom3 with respect to atom2 + dx_right[0] = x[atom3][0] - x_angle_middle[0]; + dx_right[1] = x[atom3][1] - x_angle_middle[1]; + dx_right[2] = x[atom3][2] - x_angle_middle[2]; + domain->minimum_image(dx_right[0], dx_right[1], dx_right[2]); + x_angle_right[0] = x_angle_middle[0] + dx_right[0]; + x_angle_right[1] = x_angle_middle[1] + dx_right[1]; + x_angle_right[2] = x_angle_middle[2] + dx_right[2]; + + // check if any bond vector crosses the plane of interest + double tau_right = (x_angle_right[dir] - pos) / (x_angle_right[dir] - x_angle_middle[dir]); + double tau_left = (x_angle_middle[dir] - pos) / (x_angle_middle[dir] - x_angle_left[dir]); + bool right_cross = ((tau_right >=0) && (tau_right <= 1)); + bool left_cross = ((tau_left >=0) && (tau_left <= 1)); + + // no bonds crossing the plane + if (!right_cross && !left_cross) continue; + + // compute the cos(theta) of the angle + r1 = sqrt(dx_left[0]*dx_left[0] + dx_left[1]*dx_left[1] + dx_left[2]*dx_left[2]); + r2 = sqrt(dx_right[0]*dx_right[0] + dx_right[1]*dx_right[1] + dx_right[2]*dx_right[2]); + cos_theta = -(dx_right[0]*dx_left[0] + dx_right[1]*dx_left[1] + dx_right[2]*dx_left[2])/(r1*r2); + + if (cos_theta > 1.0) cos_theta = 1.0; + if (cos_theta < -1.0) cos_theta = -1.0; + + // The method returns derivative with regards to cos(theta) + angle->born_matrix(atype, atom1, atom2, atom3, duang, du2ang); + // only right bond crossing the plane + if (right_cross && !left_cross) + { + double sgn = copysign(1.0, x_angle_right[dir] - pos); + dcos_theta[0] = sgn*(dx_right[0]*cos_theta/r2 + dx_left[0]/r1)/r2; + dcos_theta[1] = sgn*(dx_right[1]*cos_theta/r2 + dx_left[1]/r1)/r2; + dcos_theta[2] = sgn*(dx_right[2]*cos_theta/r2 + dx_left[2]/r1)/r2; + } + + // only left bond crossing the plane + if (!right_cross && left_cross) + { + double sgn = copysign(1.0, x_angle_left[dir] - pos); + dcos_theta[0] = -sgn*(dx_left[0]*cos_theta/r1 + dx_right[0]/r2)/r1; + dcos_theta[1] = -sgn*(dx_left[1]*cos_theta/r1 + dx_right[1]/r2)/r1; + dcos_theta[2] = -sgn*(dx_left[2]*cos_theta/r1 + dx_right[2]/r2)/r1; + } + + // both bonds crossing the plane + if (right_cross && left_cross) + { + // due to right bond + double sgn = copysign(1.0, x_angle_middle[dir] - pos); + dcos_theta[0] = -sgn*(dx_right[0]*cos_theta/r2 + dx_left[0]/r1)/r2; + dcos_theta[1] = -sgn*(dx_right[1]*cos_theta/r2 + dx_left[1]/r1)/r2; + dcos_theta[2] = -sgn*(dx_right[2]*cos_theta/r2 + dx_left[2]/r1)/r2; + + // due to left bond + dcos_theta[0] += sgn*(dx_left[0]*cos_theta/r1 + dx_right[0]/r2)/r1; + dcos_theta[1] += sgn*(dx_left[1]*cos_theta/r1 + dx_right[1]/r2)/r1; + dcos_theta[2] += sgn*(dx_left[2]*cos_theta/r1 + dx_right[2]/r2)/r1; + } + + // final contribution of the given angle term + local_contribution[ibin][0] += duang*dcos_theta[0]/area*nktv2p; + local_contribution[ibin][1] += duang*dcos_theta[1]/area*nktv2p; + local_contribution[ibin][2] += duang*dcos_theta[2]/area*nktv2p; + } + } + } + + // loop over the keywords and if necessary add the angle contribution + int m = 0; + while (m < nvalues) { + if (which[m] == CONF || which[m] == TOTAL || which[m] == ANGLE) { + for (int ibin = 0; ibin < nbins; ibin++) { + angle_local[ibin][m] = local_contribution[ibin][0]; + angle_local[ibin][m+1] = local_contribution[ibin][1]; + angle_local[ibin][m+2] = local_contribution[ibin][2]; + } + } + m += 3; + } +} + /* ---------------------------------------------------------------------- setup 1d bins and their extent and coordinates called at init() @@ -657,6 +870,8 @@ void ComputeStressMopProfile::setup_bins() memory->create(values_global,nbins,nvalues,"stress/mop/profile:values_global"); memory->create(bond_local,nbins,nvalues,"stress/mop/profile:bond_local"); memory->create(bond_global,nbins,nvalues,"stress/mop/profile:bond_global"); + memory->create(angle_local,nbins,nvalues,"stress/mop/profile:angle_local"); + memory->create(angle_global,nbins,nvalues,"stress/mop/profile:angle_global"); memory->create(local_contribution,nbins,3,"stress/mop/profile:local_contribution"); // set bin coordinates From 78f4e4f1a15061991a7a0073566df2ed43712715 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 5 Jul 2023 16:51:41 +0300 Subject: [PATCH 042/189] Update compute_stress_mop.rst --- doc/src/compute_stress_mop.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/src/compute_stress_mop.rst b/doc/src/compute_stress_mop.rst index 21c2963545..70986862fe 100644 --- a/doc/src/compute_stress_mop.rst +++ b/doc/src/compute_stress_mop.rst @@ -74,9 +74,7 @@ Between one and six keywords can be used to indicate which contributions to the stress must be computed: total stress (total), kinetic stress (kin), configurational stress (conf), stress due to bond stretching (bond), stress due to angle bending (angle) and/or due to pairwise -non-bonded interactions (pair). The angle keyword is currently -available only for the *stress/mop* command and **not** the -*stress/mop/profile* command. +non-bonded interactions (pair). NOTE 1: The configurational stress is computed considering all pairs of atoms where at least one atom belongs to group group-ID. @@ -136,7 +134,7 @@ requires the class method ``Pair::single()`` to be implemented, which is not possible for manybody potentials. In particular, compute *stress/mop/profile* does not work with more than two-body pair interactions, long range (kspace) interactions and -angle/dihedral/improper intramolecular interactions. Similarly, compute +dihedral/improper intramolecular interactions. Similarly, compute *stress/mop* does not work with more than two-body pair interactions, long range (kspace) interactions and dihedral/improper intramolecular interactions but works with all bond interactions with the class method From 9fde61fc4e358e44854cf73f8d5cb230b3bf7e7c Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 5 Jul 2023 16:59:02 +0300 Subject: [PATCH 043/189] Update compute_stress_mop_profile.cpp --- src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp index dabbbc4781..2dee2bf60f 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp @@ -18,6 +18,7 @@ #include "compute_stress_mop_profile.h" +#include "angle.h" #include "atom.h" #include "atom_vec.h" #include "bond.h" From e3792616ad1d749f526e9e9ae2d4a0b1e76e4214 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Tue, 1 Aug 2023 12:54:23 -0600 Subject: [PATCH 044/189] Moving line break in BPM doc pages for link, fixing prop/atom syntax --- doc/src/bond_bpm_rotational.rst | 4 ++-- doc/src/bond_bpm_spring.rst | 4 ++-- src/BPM/bond_bpm.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/src/bond_bpm_rotational.rst b/doc/src/bond_bpm_rotational.rst index 7459d491d6..6734bd7bfe 100644 --- a/doc/src/bond_bpm_rotational.rst +++ b/doc/src/bond_bpm_rotational.rst @@ -147,8 +147,8 @@ By default, pair forces are not calculated between bonded particles. Pair forces can alternatively be overlaid on top of bond forces by setting the *overlay/pair* keyword to *yes*. These settings require specific :doc:`special_bonds ` settings described in the -restrictions. Further details can be found in the :doc:`how to -` page on BPMs. +restrictions. Further details can be found in the :doc:`how to ` +page on BPMs. .. versionadded:: 28Mar2023 diff --git a/doc/src/bond_bpm_spring.rst b/doc/src/bond_bpm_spring.rst index 04ff4d5991..a03c832249 100644 --- a/doc/src/bond_bpm_spring.rst +++ b/doc/src/bond_bpm_spring.rst @@ -113,8 +113,8 @@ By default, pair forces are not calculated between bonded particles. Pair forces can alternatively be overlaid on top of bond forces by setting the *overlay/pair* keyword to *yes*. These settings require specific :doc:`special_bonds ` settings described in the -restrictions. Further details can be found in the :doc:`how to -` page on BPMs. +restrictions. Further details can be found in the :doc:`how to ` +page on BPMs. .. versionadded:: 28Mar2023 diff --git a/src/BPM/bond_bpm.cpp b/src/BPM/bond_bpm.cpp index 3ebeed3f1d..b484df7fab 100644 --- a/src/BPM/bond_bpm.cpp +++ b/src/BPM/bond_bpm.cpp @@ -224,7 +224,7 @@ void BondBPM::settings(int narg, char **arg) ifix = modify->get_fix_by_id(id_fix_prop_atom); if (!ifix) - ifix = modify->add_fix(fmt::format("{} all property/atom {} {} {} ghost yes", + ifix = modify->add_fix(fmt::format("{} all property/atom d_{} d_{} d_{} ghost yes", id_fix_prop_atom, x_ref_id, y_ref_id, z_ref_id)); int type_flag; From b81df7c21b6be9739f234d4dc9c73272b38cde9c Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 26 Sep 2023 16:12:09 +0300 Subject: [PATCH 045/189] Include methods and variables for dihedral contribution to compute_stress_mop_profile.h --- src/EXTRA-COMPUTE/compute_stress_mop_profile.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h index c7214c055c..b9b97617c0 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h @@ -40,12 +40,13 @@ class ComputeStressMopProfile : public Compute { void compute_pairs(); void compute_bonds(); void compute_angles(); + void compute_dihedrals(); void setup_bins(); int nvalues, dir; int *which; - int bondflag, angleflag; + int bondflag, angleflag, dihedralflag; double origin, delta, offset, invdelta; int nbins; @@ -53,6 +54,7 @@ class ComputeStressMopProfile : public Compute { double **values_local, **values_global; double **bond_local, **bond_global; double **angle_local, **angle_global; + double **dihedral_local, **dihedral_global; double **local_contribution; double dt, nktv2p, ftm2v; From e819b38a1831a56839cc13be2877099244ca4afe Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 26 Sep 2023 16:15:12 +0300 Subject: [PATCH 046/189] undo dihedral changes in compute_stress_mop_profile.h --- src/EXTRA-COMPUTE/compute_stress_mop_profile.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h index b9b97617c0..c7214c055c 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h @@ -40,13 +40,12 @@ class ComputeStressMopProfile : public Compute { void compute_pairs(); void compute_bonds(); void compute_angles(); - void compute_dihedrals(); void setup_bins(); int nvalues, dir; int *which; - int bondflag, angleflag, dihedralflag; + int bondflag, angleflag; double origin, delta, offset, invdelta; int nbins; @@ -54,7 +53,6 @@ class ComputeStressMopProfile : public Compute { double **values_local, **values_global; double **bond_local, **bond_global; double **angle_local, **angle_global; - double **dihedral_local, **dihedral_global; double **local_contribution; double dt, nktv2p, ftm2v; From 381d8de017c37794682e77a3825f4e1b9ddd4458 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 26 Sep 2023 16:22:09 +0300 Subject: [PATCH 047/189] initialization of angleflag in constructor compute_stress_mop_profile.cpp --- src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp index 2a82cc0f8b..3dfc9326ac 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp @@ -49,6 +49,7 @@ ComputeStressMopProfile::ComputeStressMopProfile(LAMMPS *lmp, int narg, char **a if (narg < 7) utils::missing_cmd_args(FLERR, "compute stress/mop/profile", error); bondflag = 0; + angleflag = 0; // set compute mode and direction of plane(s) for pressure calculation From ec458e2861b5b4950bfcd9a9dfb67bf1c636c319 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 26 Sep 2023 16:32:26 +0300 Subject: [PATCH 048/189] method and member variables definition for dihedrals in compute_stress_mop_profile.h --- src/EXTRA-COMPUTE/compute_stress_mop_profile.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h index c7214c055c..b9b97617c0 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h @@ -40,12 +40,13 @@ class ComputeStressMopProfile : public Compute { void compute_pairs(); void compute_bonds(); void compute_angles(); + void compute_dihedrals(); void setup_bins(); int nvalues, dir; int *which; - int bondflag, angleflag; + int bondflag, angleflag, dihedralflag; double origin, delta, offset, invdelta; int nbins; @@ -53,6 +54,7 @@ class ComputeStressMopProfile : public Compute { double **values_local, **values_global; double **bond_local, **bond_global; double **angle_local, **angle_global; + double **dihedral_local, **dihedral_global; double **local_contribution; double dt, nktv2p, ftm2v; From d40fb4a337c67c2f0c3bc79bf29b87353cd80ceb Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 26 Sep 2023 16:45:46 +0300 Subject: [PATCH 049/189] method implementation for dihedral contribution to compute_stress_mop_profile.cpp --- .../compute_stress_mop_profile.cpp | 361 +++++++++++++++++- 1 file changed, 354 insertions(+), 7 deletions(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp index 3dfc9326ac..27e420e71d 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp @@ -13,7 +13,7 @@ /*------------------------------------------------------------------------ Contributing Authors : Romain Vermorel (LFCR), Laurent Joly (ULyon) - Support for bonds and angles added by : Evangelos Voyiatzis (NovaMechanics) + Support for bonds, angles and dihedrals added by : Evangelos Voyiatzis (NovaMechanics) --------------------------------------------------------------------------*/ #include "compute_stress_mop_profile.h" @@ -23,6 +23,7 @@ #include "atom_vec.h" #include "bond.h" #include "comm.h" +#include "dihedral.h" #include "domain.h" #include "error.h" #include "force.h" @@ -38,8 +39,10 @@ using namespace LAMMPS_NS; +#define SMALL 0.001 + enum { X, Y, Z }; -enum { TOTAL, CONF, KIN, PAIR, BOND, ANGLE }; +enum { TOTAL, CONF, KIN, PAIR, BOND, ANGLE, DIHEDRAL }; /* ---------------------------------------------------------------------- */ @@ -50,6 +53,7 @@ ComputeStressMopProfile::ComputeStressMopProfile(LAMMPS *lmp, int narg, char **a bondflag = 0; angleflag = 0; + dihedralflag = 0; // set compute mode and direction of plane(s) for pressure calculation @@ -114,6 +118,11 @@ ComputeStressMopProfile::ComputeStressMopProfile(LAMMPS *lmp, int narg, char **a which[nvalues] = ANGLE; nvalues++; } + } else if (strcmp(arg[iarg],"dihedral") == 0) { + for (i=0; i<3; i++) { + which[nvalues] = DIHEDRAL; + nvalues++; + } } else error->all(FLERR, "Illegal compute stress/mop/profile command"); //break; @@ -141,6 +150,8 @@ ComputeStressMopProfile::ComputeStressMopProfile(LAMMPS *lmp, int narg, char **a bond_global = nullptr; angle_local = nullptr; angle_global = nullptr; + dihedral_local = nullptr; + dihedral_global = nullptr; local_contribution = nullptr; // bin setup @@ -171,6 +182,8 @@ ComputeStressMopProfile::~ComputeStressMopProfile() memory->destroy(bond_global); memory->destroy(angle_local); memory->destroy(angle_global); + memory->destroy(dihedral_local); + memory->destroy(dihedral_global); memory->destroy(local_contribution); memory->destroy(array); } @@ -227,10 +240,16 @@ void ComputeStressMopProfile::init() } } - if (force->dihedral) - if ((strcmp(force->dihedral_style, "zero") != 0) && - (strcmp(force->dihedral_style, "none") != 0)) - error->all(FLERR, "compute stress/mop/profile does not account for dihedral potentials"); + if (force->dihedral) { + if (force->dihedral->born_matrix_enable == 0) { + if ((strcmp(force->dihedral_style, "zero") != 0) && + (strcmp(force->dihedral_style, "none") != 0)) + error->all(FLERR, "compute stress/mop/profile does not account for dihedral potentials"); + } else { + dihedralflag = 1; + } + } + if (force->improper) if ((strcmp(force->improper_style, "zero") != 0) && (strcmp(force->improper_style, "none") != 0)) @@ -294,6 +313,17 @@ void ComputeStressMopProfile::compute_array() // sum angle contribution over all procs MPI_Allreduce(&angle_local[0][0],&angle_global[0][0],nbins*nvalues,MPI_DOUBLE,MPI_SUM,world); + + if (dihedralflag) { + //Compute dihedral contribution on separate procs + compute_dihedrals(); + } else { + for (int m = 0; m < nbins; m++) { + for (int i = 0; i < nvalues; i++) { + dihedral_local[m][i] = 0.0; + } + } + } for (int ibin = 0; ibin < nbins; ibin++) { array[ibin][0] = coord[ibin]; @@ -301,7 +331,7 @@ void ComputeStressMopProfile::compute_array() int mo = 1; int m = 0; while (m < nvalues) { - array[ibin][m + mo] = values_global[ibin][m] + bond_global[ibin][m] + angle_global[ibin][m]; + array[ibin][m + mo] = values_global[ibin][m] + bond_global[ibin][m] + angle_global[ibin][m] + dihedral_global[ibin][m]; m++; } } @@ -835,6 +865,321 @@ void ComputeStressMopProfile::compute_angles() } } +/*------------------------------------------------------------------------ + compute dihedral contribution to pressure of local proc + -------------------------------------------------------------------------*/ + +void ComputeStressMopProfile::compute_dihedrals() +{ + int i, nd, atom1, atom2, atom3, atom4, imol, iatom; + tagint tagprev; + double vb1x, vb1y, vb1z, vb2x, vb2y, vb2z, vb3x, vb3y, vb3z; + double vb2xm, vb2ym, vb2zm; + double sb1, sb2, sb3, rb1, rb3, c0, b1mag2, b1mag, b2mag2; + double b2mag, b3mag2, b3mag, c2mag, ctmp, r12c1, c1mag, r12c2; + double s1, s2, s12, sc1, sc2, a11, a22, a33, a12, a13, a23; + double df[3], f1[3], f2[3], f3[3], f4[3]; + double c, sx2, sy2, sz2, sin2; + + double **x = atom->x; + tagint *tag = atom->tag; + int *num_dihedral = atom->num_dihedral; + tagint **dihedral_atom1 = atom->dihedral_atom1; + tagint **dihedral_atom2 = atom->dihedral_atom2; + tagint **dihedral_atom3 = atom->dihedral_atom3; + tagint **dihedral_atom4 = atom->dihedral_atom4; + int *mask = atom->mask; + + int *molindex = atom->molindex; + int *molatom = atom->molatom; + Molecule **onemols = atom->avec->onemols; + + int nlocal = atom->nlocal; + int molecular = atom->molecular; + + // loop over all atoms and their dihedrals + + Dihedral *dihedral = force->dihedral; + + double dudih, du2dih; + + double diffx[3] = {0.0, 0.0, 0.0}; + double x_atom_1[3] = {0.0, 0.0, 0.0}; + double x_atom_2[3] = {0.0, 0.0, 0.0}; + double x_atom_3[3] = {0.0, 0.0, 0.0}; + double x_atom_4[3] = {0.0, 0.0, 0.0}; + + // initialization + for (int m = 0; m < nbins; m++) { + for (int i = 0; i < nvalues; i++) { + dihedral_local[m][i] = 0.0; + } + local_contribution[m][0] = 0.0; + local_contribution[m][1] = 0.0; + local_contribution[m][2] = 0.0; + } + + for (atom2 = 0; atom2 < nlocal; atom2++) { + if (!(mask[atom2] & groupbit)) continue; + + if (molecular == Atom::MOLECULAR) + nd = num_dihedral[atom2]; + else { + if (molindex[atom2] < 0) continue; + imol = molindex[atom2]; + iatom = molatom[atom2]; + nd = onemols[imol]->num_dihedral[iatom]; + } + + for (i = 0; i < nd; i++) { + if (molecular == 1) { + if (tag[atom2] != dihedral_atom2[atom2][i]) continue; + atom1 = atom->map(dihedral_atom1[atom2][i]); + atom3 = atom->map(dihedral_atom3[atom2][i]); + atom4 = atom->map(dihedral_atom4[atom2][i]); + } else { + if (tag[atom2] != onemols[imol]->dihedral_atom2[atom2][i]) continue; + tagprev = tag[atom2] - iatom - 1; + atom1 = atom->map(onemols[imol]->dihedral_atom1[atom2][i] + tagprev); + atom3 = atom->map(onemols[imol]->dihedral_atom3[atom2][i] + tagprev); + atom4 = atom->map(onemols[imol]->dihedral_atom4[atom2][i] + tagprev); + } + + if (atom1 < 0 || !(mask[atom1] & groupbit)) continue; + if (atom3 < 0 || !(mask[atom3] & groupbit)) continue; + if (atom4 < 0 || !(mask[atom4] & groupbit)) continue; + + for (int ibin = 0; ibinminimum_image(x_atom_1[0], x_atom_1[1], x_atom_1[2]); + x_atom_1[dir] += pos; + + // minimum image of atom2 with respect to atom1 + diffx[0] = x[atom2][0] - x_atom_1[0]; + diffx[1] = x[atom2][1] - x_atom_1[1]; + diffx[2] = x[atom2][2] - x_atom_1[2]; + domain->minimum_image(diffx[0], diffx[1], diffx[2]); + x_atom_2[0] = x_atom_1[0] + diffx[0]; + x_atom_2[1] = x_atom_1[1] + diffx[1]; + x_atom_2[2] = x_atom_1[2] + diffx[2]; + + // minimum image of atom3 with respect to atom2 + diffx[0] = x[atom3][0] - x_atom_2[0]; + diffx[1] = x[atom3][1] - x_atom_2[1]; + diffx[2] = x[atom3][2] - x_atom_2[2]; + domain->minimum_image(diffx[0], diffx[1], diffx[2]); + x_atom_3[0] = x_atom_2[0] + diffx[0]; + x_atom_3[1] = x_atom_2[1] + diffx[1]; + x_atom_3[2] = x_atom_2[2] + diffx[2]; + + // minimum image of atom3 with respect to atom2 + diffx[0] = x[atom4][0] - x_atom_3[0]; + diffx[1] = x[atom4][1] - x_atom_3[1]; + diffx[2] = x[atom4][2] - x_atom_3[2]; + domain->minimum_image(diffx[0], diffx[1], diffx[2]); + x_atom_4[0] = x_atom_3[0] + diffx[0]; + x_atom_4[1] = x_atom_3[1] + diffx[1]; + x_atom_4[2] = x_atom_3[2] + diffx[2]; + + // check if any bond vector crosses the plane of interest + double tau_right = (x_atom_2[dir] - pos) / (x_atom_2[dir] - x_atom_1[dir]); + double tau_middle = (x_atom_3[dir] - pos) / (x_atom_3[dir] - x_atom_2[dir]); + double tau_left = (x_atom_4[dir] - pos) / (x_atom_4[dir] - x_atom_3[dir]); + bool right_cross = ((tau_right >=0) && (tau_right <= 1)); + bool middle_cross = ((tau_middle >= 0) && (tau_middle <= 1)); + bool left_cross = ((tau_left >=0) && (tau_left <= 1)); + + // no bonds crossing the plane + if (!right_cross && !middle_cross && !left_cross) continue; + + dihedral->born_matrix(i, atom1, atom2, atom3, atom4, dudih, du2dih); + + // first bond + vb1x = x_atom_1[0] - x_atom_2[0]; + vb1y = x_atom_1[1] - x_atom_2[1]; + vb1z = x_atom_1[2] - x_atom_2[2]; + + // second bond + vb2x = x_atom_3[0] - x_atom_2[0]; + vb2y = x_atom_3[1] - x_atom_2[1]; + vb2z = x_atom_3[2] - x_atom_2[2]; + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + + // third bond + vb3x = x_atom_4[0] - x_atom_3[0]; + vb3y = x_atom_4[1] - x_atom_3[1]; + vb3z = x_atom_4[2] - x_atom_3[2]; + + // c0 calculation + sb1 = 1.0 / (vb1x*vb1x + vb1y*vb1y + vb1z*vb1z); + sb2 = 1.0 / (vb2x*vb2x + vb2y*vb2y + vb2z*vb2z); + sb3 = 1.0 / (vb3x*vb3x + vb3y*vb3y + vb3z*vb3z); + + rb1 = sqrt(sb1); + rb3 = sqrt(sb3); + + c0 = (vb1x*vb3x + vb1y*vb3y + vb1z*vb3z) * rb1*rb3; + // 1st and 2nd angle + b1mag2 = vb1x*vb1x + vb1y*vb1y + vb1z*vb1z; + b1mag = sqrt(b1mag2); + b2mag2 = vb2x*vb2x + vb2y*vb2y + vb2z*vb2z; + b2mag = sqrt(b2mag2); + b3mag2 = vb3x*vb3x + vb3y*vb3y + vb3z*vb3z; + b3mag = sqrt(b3mag2); + + ctmp = vb1x*vb2x + vb1y*vb2y + vb1z*vb2z; + r12c1 = 1.0 / (b1mag*b2mag); + c1mag = ctmp * r12c1; + + ctmp = vb2xm*vb3x + vb2ym*vb3y + vb2zm*vb3z; + r12c2 = 1.0 / (b2mag*b3mag); + c2mag = ctmp * r12c2; + + // cos and sin of 2 angles and final c + sin2 = MAX(1.0 - c1mag*c1mag,0.0); + sc1 = sqrt(sin2); + if (sc1 < SMALL) sc1 = SMALL; + sc1 = 1.0/sc1; + + sin2 = MAX(1.0 - c2mag*c2mag,0.0); + sc2 = sqrt(sin2); + if (sc2 < SMALL) sc2 = SMALL; + sc2 = 1.0/sc2; + + s1 = sc1 * sc1; + s2 = sc2 * sc2; + s12 = sc1 * sc2; + c = (c0 + c1mag*c2mag) * s12; + + // error check + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + // forces on each particle + double a = dudih; + c = c * a; + s12 = s12 * a; + a11 = c*sb1*s1; + a22 = -sb2 * (2.0*c0*s12 - c*(s1+s2)); + a33 = c*sb3*s2; + a12 = -r12c1 * (c1mag*c*s1 + c2mag*s12); + a13 = -rb1*rb3*s12; + a23 = r12c2 * (c2mag*c*s2 + c1mag*s12); + + sx2 = a12*vb1x + a22*vb2x + a23*vb3x; + sy2 = a12*vb1y + a22*vb2y + a23*vb3y; + sz2 = a12*vb1z + a22*vb2z + a23*vb3z; + + f1[0] = a11*vb1x + a12*vb2x + a13*vb3x; + f1[1] = a11*vb1y + a12*vb2y + a13*vb3y; + f1[2] = a11*vb1z + a12*vb2z + a13*vb3z; + + f2[0] = -sx2 - f1[0]; + f2[1] = -sy2 - f1[1]; + f2[2] = -sz2 - f1[2]; + + f4[0] = a13*vb1x + a23*vb2x + a33*vb3x; + f4[1] = a13*vb1y + a23*vb2y + a33*vb3y; + f4[2] = a13*vb1z + a23*vb2z + a33*vb3z; + + f3[0] = sx2 - f4[0]; + f3[1] = sy2 - f4[1]; + f3[2] = sz2 - f4[2]; + + // only right bond crossing the plane + if (right_cross && !middle_cross && !left_cross) + { + double sgn = copysign(1.0, x_atom_1[dir] - pos); + df[0] = sgn * f1[0]; + df[1] = sgn * f1[1]; + df[2] = sgn * f1[2]; + } + + // only middle bond crossing the plane + if (!right_cross && middle_cross && !left_cross) + { + double sgn = copysign(1.0, x_atom_2[dir] - pos); + df[0] = sgn * (f2[0] + f1[0]); + df[1] = sgn * (f2[1] + f1[1]); + df[2] = sgn * (f2[2] + f1[2]); + } + + // only left bond crossing the plane + if (!right_cross && !middle_cross && left_cross) + { + double sgn = copysign(1.0, x_atom_4[dir] - pos); + df[0] = sgn * f4[0]; + df[1] = sgn * f4[1]; + df[2] = sgn * f4[2]; + } + + // only right & middle bonds crossing the plane + if (right_cross && middle_cross && !left_cross) + { + double sgn = copysign(1.0, x_atom_2[dir] - pos); + df[0] = sgn * f2[0]; + df[1] = sgn * f2[1]; + df[2] = sgn * f2[2]; + } + + // only right & left bonds crossing the plane + if (right_cross && !middle_cross && left_cross) + { + double sgn = copysign(1.0, x_atom_1[dir] - pos); + df[0] = sgn * (f1[0] + f4[0]); + df[1] = sgn * (f1[1] + f4[1]); + df[2] = sgn * (f1[2] + f4[2]); + } + + // only middle & left bonds crossing the plane + if (!right_cross && middle_cross && left_cross) + { + double sgn = copysign(1.0, x_atom_3[dir] - pos); + df[0] = sgn * f3[0]; + df[1] = sgn * f3[1]; + df[2] = sgn * f3[2]; + } + + // all three bonds crossing the plane + if (right_cross && middle_cross && left_cross) + { + double sgn = copysign(1.0, x_atom_1[dir] - pos); + df[0] = sgn * (f1[0] + f3[0]); + df[1] = sgn * (f1[1] + f3[1]); + df[2] = sgn * (f1[2] + f3[2]); + } + + local_contribution[ibin][0] += df[0]/area*nktv2p; + local_contribution[ibin][1] += df[1]/area*nktv2p; + local_contribution[ibin][2] += df[2]/area*nktv2p; + } + } + } + + // loop over the keywords and if necessary add the dihedral contribution + int m = 0; + while (m < nvalues) { + if ((which[m] == CONF) || (which[m] == TOTAL) || (which[m] == DIHEDRAL)) { + for (int ibin = 0; ibin < nbins; ibin++) { + dihedral_local[ibin][m] = local_contribution[ibin][0]; + dihedral_local[ibin][m+1] = local_contribution[ibin][1]; + dihedral_local[ibin][m+2] = local_contribution[ibin][2]; + } + } + m += 3; + } + +} + /* ---------------------------------------------------------------------- setup 1d bins and their extent and coordinates called at init() @@ -870,6 +1215,8 @@ void ComputeStressMopProfile::setup_bins() memory->create(bond_global, nbins, nvalues, "stress/mop/profile:bond_global"); memory->create(angle_local, nbins, nvalues, "stress/mop/profile:angle_local"); memory->create(angle_global, nbins, nvalues, "stress/mop/profile:angle_global"); + memory->create(dihedral_local,nbins,nvalues,"stress/mop/profile:dihedral_local"); + memory->create(dihedral_global,nbins,nvalues,"stress/mop/profile:dihedral_global"); memory->create(local_contribution, nbins, 3, "stress/mop/profile:local_contribution"); // set bin coordinates From b86d1f655381c076a6b378bc8ea8f46aedf5c6de Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 26 Sep 2023 16:48:45 +0300 Subject: [PATCH 050/189] Update compute_stress_mop.rst for dihedral interactions --- doc/src/compute_stress_mop.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/src/compute_stress_mop.rst b/doc/src/compute_stress_mop.rst index 70986862fe..3cd7a67c9c 100644 --- a/doc/src/compute_stress_mop.rst +++ b/doc/src/compute_stress_mop.rst @@ -18,7 +18,7 @@ Syntax * style = *stress/mop* or *stress/mop/profile* * dir = *x* or *y* or *z* is the direction normal to the plane * args = argument specific to the compute style -* keywords = *kin* or *conf* or *total* or *pair* or *bond* or *angle* (one or more can be specified) +* keywords = *kin* or *conf* or *total* or *pair* or *bond* or *angle* or *dihedral* (one or more can be specified) .. parsed-literal:: @@ -68,13 +68,13 @@ Verlet algorithm. .. versionadded:: 15Jun2023 - contributions from bond and angle potentials + contributions from bond, angle and dihedral potentials -Between one and six keywords can be used to indicate which contributions +Between one and seven keywords can be used to indicate which contributions to the stress must be computed: total stress (total), kinetic stress (kin), configurational stress (conf), stress due to bond stretching -(bond), stress due to angle bending (angle) and/or due to pairwise -non-bonded interactions (pair). +(bond), stress due to angle bending (angle), stress due to dihedral terms (dihedral) +and/or due to pairwise non-bonded interactions (pair). NOTE 1: The configurational stress is computed considering all pairs of atoms where at least one atom belongs to group group-ID. @@ -134,7 +134,7 @@ requires the class method ``Pair::single()`` to be implemented, which is not possible for manybody potentials. In particular, compute *stress/mop/profile* does not work with more than two-body pair interactions, long range (kspace) interactions and -dihedral/improper intramolecular interactions. Similarly, compute +improper intramolecular interactions. Similarly, compute *stress/mop* does not work with more than two-body pair interactions, long range (kspace) interactions and dihedral/improper intramolecular interactions but works with all bond interactions with the class method From 1591b21617a196607306955e7d108f38b330ed1d Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 26 Sep 2023 16:53:16 +0300 Subject: [PATCH 051/189] remove whitespaces from compute_stress_mop_profile.cpp --- src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp index 27e420e71d..223e4da66a 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp @@ -324,7 +324,7 @@ void ComputeStressMopProfile::compute_array() } } } - + for (int ibin = 0; ibin < nbins; ibin++) { array[ibin][0] = coord[ibin]; @@ -540,7 +540,7 @@ void ComputeStressMopProfile::compute_pairs() vcross[0] = vi[0] - fi[0] * iterm; vcross[1] = vi[1] - fi[1] * iterm; vcross[2] = vi[2] - fi[2] * iterm; - + values_local[ibin][m] += imass * vcross[0] * sgn / dt / area * nktv2p / ftm2v; values_local[ibin][m + 1] += imass * vcross[1] * sgn / dt / area * nktv2p / ftm2v; values_local[ibin][m + 2] += imass * vcross[2] * sgn / dt / area * nktv2p / ftm2v; From 3445330cf1679addfdce5ab3e678b54669dc933d Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 28 Sep 2023 16:13:13 +0300 Subject: [PATCH 052/189] remove whitespace from compute_stress_mop.cpp --- src/EXTRA-COMPUTE/compute_stress_mop.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop.cpp b/src/EXTRA-COMPUTE/compute_stress_mop.cpp index a1077660e5..3f1ae008ea 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop.cpp @@ -433,7 +433,7 @@ void ComputeStressMop::compute_pairs() xi[dir] -= pos; domain->minimum_image(xi[0], xi[1], xi[2]); xi[dir] += pos; - + //velocities at t vi[0] = atom->v[i][0]; From ac435319fdf8b127ac33cfdf24ea6b8bfcc4649b Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 28 Sep 2023 16:15:03 +0300 Subject: [PATCH 053/189] Definition of compute_dihedral and related variables in compute_stress_mop.h --- src/EXTRA-COMPUTE/compute_stress_mop.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop.h b/src/EXTRA-COMPUTE/compute_stress_mop.h index 86140dc278..0a0ea8b55a 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop.h +++ b/src/EXTRA-COMPUTE/compute_stress_mop.h @@ -40,15 +40,17 @@ class ComputeStressMop : public Compute { void compute_pairs(); void compute_bonds(); void compute_angles(); + void compute_dihedrals(); int nvalues, dir; int *which; - int bondflag, angleflag; + int bondflag, angleflag, dihedralflag; double *values_local, *values_global; double *bond_local, *bond_global; double *angle_local, *angle_global; + double *dihedral_local, *dihedral_global; double pos, pos1, dt, nktv2p, ftm2v; double area; class NeighList *list; From ca449f1ea859d47e665ae98e5a58cf8aa9781e60 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 28 Sep 2023 16:25:52 +0300 Subject: [PATCH 054/189] Prepare for inclusion of dihedral contribution in compute_stress_mop.cpp --- src/EXTRA-COMPUTE/compute_stress_mop.cpp | 47 +++++++++++++++++++++--- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop.cpp b/src/EXTRA-COMPUTE/compute_stress_mop.cpp index 3f1ae008ea..d94ba61d71 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop.cpp @@ -23,6 +23,7 @@ #include "atom_vec.h" #include "bond.h" #include "comm.h" +#include "dihedral.h" #include "domain.h" #include "error.h" #include "force.h" @@ -38,8 +39,10 @@ using namespace LAMMPS_NS; +#define SMALL 0.001 + enum { X, Y, Z }; -enum { TOTAL, CONF, KIN, PAIR, BOND, ANGLE }; +enum { TOTAL, CONF, KIN, PAIR, BOND, ANGLE, DIHEDRAL }; /* ---------------------------------------------------------------------- */ @@ -49,6 +52,7 @@ ComputeStressMop::ComputeStressMop(LAMMPS *lmp, int narg, char **arg) : Compute( bondflag = 0; angleflag = 0; + dihedralflag = 0; // set compute mode and direction of plane(s) for pressure calculation @@ -129,6 +133,11 @@ ComputeStressMop::ComputeStressMop(LAMMPS *lmp, int narg, char **arg) : Compute( which[nvalues] = ANGLE; nvalues++; } + } else if (strcmp(arg[iarg],"dihedral") == 0) { + for (i=0; i<3; i++) { + which[nvalues] = DIHEDRAL; + nvalues++; + } } else error->all(FLERR, "Illegal compute stress/mop command"); //break; @@ -152,6 +161,8 @@ ComputeStressMop::ComputeStressMop(LAMMPS *lmp, int narg, char **arg) : Compute( bond_global = nullptr; angle_local = nullptr; angle_global = nullptr; + dihedral_local = nullptr; + dihedral_global = nullptr; // this fix produces a global vector @@ -162,6 +173,8 @@ ComputeStressMop::ComputeStressMop(LAMMPS *lmp, int narg, char **arg) : Compute( memory->create(bond_global, nvalues, "stress/mop:bond_global"); memory->create(angle_local, nvalues, "stress/mop:angle_local"); memory->create(angle_global, nvalues, "stress/mop:angle_global"); + memory->create(dihedral_local,nvalues,"stress/mop:dihedral_local"); + memory->create(dihedral_global,nvalues,"stress/mop:dihedral_global"); size_vector = nvalues; vector_flag = 1; @@ -180,6 +193,8 @@ ComputeStressMop::~ComputeStressMop() memory->destroy(bond_global); memory->destroy(angle_local); memory->destroy(angle_global); + memory->destroy(dihedral_local); + memory->destroy(dihedral_global); memory->destroy(vector); } @@ -233,9 +248,13 @@ void ComputeStressMop::init() } } if (force->dihedral) { - if ((strcmp(force->dihedral_style, "zero") != 0) && - (strcmp(force->dihedral_style, "none") != 0)) - error->all(FLERR, "compute stress/mop does not account for dihedral potentials"); + if (force->dihedral->born_matrix_enable == 0) { + if ((strcmp(force->dihedral_style, "zero") != 0) && + (strcmp(force->dihedral_style, "none") != 0)) + error->all(FLERR, "compute stress/mop does not account for dihedral potentials"); + } else { + dihedralflag = 1; + } } if (force->improper) { if ((strcmp(force->improper_style, "zero") != 0) && @@ -297,8 +316,18 @@ void ComputeStressMop::compute_vector() MPI_Allreduce(angle_local, angle_global, nvalues, MPI_DOUBLE, MPI_SUM, world); + if (dihedralflag) { + //Compute dihedral contribution on separate procs + compute_dihedrals(); + } else { + for (int i=0; i Date: Thu, 28 Sep 2023 16:29:15 +0300 Subject: [PATCH 055/189] remove whitespace from compute_stress_mop.cpp --- src/EXTRA-COMPUTE/compute_stress_mop.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop.cpp b/src/EXTRA-COMPUTE/compute_stress_mop.cpp index d94ba61d71..331f4e4841 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop.cpp @@ -325,7 +325,7 @@ void ComputeStressMop::compute_vector() // sum dihedral contribution over all procs MPI_Allreduce(dihedral_local,dihedral_global,nvalues,MPI_DOUBLE,MPI_SUM,world); - + for (int m = 0; m < nvalues; m++) { vector[m] = values_global[m] + bond_global[m] + angle_global[m] + dihedral_global[m]; } From bbd6b2846f83cef48ed3960fe2496c64ee37f99e Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 28 Sep 2023 16:59:03 +0300 Subject: [PATCH 056/189] implementation of compute_dihedral() in compute_stress_mop.cpp --- src/EXTRA-COMPUTE/compute_stress_mop.cpp | 297 +++++++++++++++++++++++ 1 file changed, 297 insertions(+) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop.cpp b/src/EXTRA-COMPUTE/compute_stress_mop.cpp index 331f4e4841..6c35b4ba07 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop.cpp @@ -825,4 +825,301 @@ void ComputeStressMop::compute_angles() void ComputeStressMop::compute_dihedrals() { + int i, nd, atom1, atom2, atom3, atom4, imol, iatom; + tagint tagprev; + double vb1x, vb1y, vb1z, vb2x, vb2y, vb2z, vb3x, vb3y, vb3z; + double vb2xm, vb2ym, vb2zm; + double sb1, sb2, sb3, rb1, rb3, c0, b1mag2, b1mag, b2mag2; + double b2mag, b3mag2, b3mag, c2mag, ctmp, r12c1, c1mag, r12c2; + double s1, s2, s12, sc1, sc2, a11, a22, a33, a12, a13, a23; + double df[3], f1[3], f2[3], f3[3], f4[3]; + double c, sx2, sy2, sz2, sin2; + + double **x = atom->x; + tagint *tag = atom->tag; + int *num_dihedral = atom->num_dihedral; + tagint **dihedral_atom1 = atom->dihedral_atom1; + tagint **dihedral_atom2 = atom->dihedral_atom2; + tagint **dihedral_atom3 = atom->dihedral_atom3; + tagint **dihedral_atom4 = atom->dihedral_atom4; + int *mask = atom->mask; + + int *molindex = atom->molindex; + int *molatom = atom->molatom; + Molecule **onemols = atom->avec->onemols; + + int nlocal = atom->nlocal; + int molecular = atom->molecular; + + // loop over all atoms and their dihedrals + + Dihedral *dihedral = force->dihedral; + + double dudih, du2dih; + + double diffx[3] = {0.0, 0.0, 0.0}; + double x_atom_1[3] = {0.0, 0.0, 0.0}; + double x_atom_2[3] = {0.0, 0.0, 0.0}; + double x_atom_3[3] = {0.0, 0.0, 0.0}; + double x_atom_4[3] = {0.0, 0.0, 0.0}; + + // initialization + for (int i = 0; i < nvalues; i++) { + dihedral_local[i] = 0.0; + } + double local_contribution[3] = {0.0, 0.0, 0.0}; + + for (atom2 = 0; atom2 < nlocal; atom2++) { + if (!(mask[atom2] & groupbit)) continue; + + if (molecular == Atom::MOLECULAR) + nd = num_dihedral[atom2]; + else { + if (molindex[atom2] < 0) continue; + imol = molindex[atom2]; + iatom = molatom[atom2]; + nd = onemols[imol]->num_dihedral[iatom]; + } + + for (i = 0; i < nd; i++) { + if (molecular == 1) { + if (tag[atom2] != dihedral_atom2[atom2][i]) continue; + atom1 = atom->map(dihedral_atom1[atom2][i]); + atom3 = atom->map(dihedral_atom3[atom2][i]); + atom4 = atom->map(dihedral_atom4[atom2][i]); + } else { + if (tag[atom2] != onemols[imol]->dihedral_atom2[atom2][i]) continue; + tagprev = tag[atom2] - iatom - 1; + atom1 = atom->map(onemols[imol]->dihedral_atom1[atom2][i] + tagprev); + atom3 = atom->map(onemols[imol]->dihedral_atom3[atom2][i] + tagprev); + atom4 = atom->map(onemols[imol]->dihedral_atom4[atom2][i] + tagprev); + } + + if (atom1 < 0 || !(mask[atom1] & groupbit)) continue; + if (atom3 < 0 || !(mask[atom3] & groupbit)) continue; + if (atom4 < 0 || !(mask[atom4] & groupbit)) continue; + + // minimum image of atom1 with respect to the plane of interest + x_atom_1[0] = x[atom1][0]; + x_atom_1[1] = x[atom1][1]; + x_atom_1[2] = x[atom1][2]; + x_atom_1[dir] -= pos; + domain->minimum_image(x_atom_1[0], x_atom_1[1], x_atom_1[2]); + x_atom_1[dir] += pos; + + // minimum image of atom2 with respect to atom1 + diffx[0] = x[atom2][0] - x_atom_1[0]; + diffx[1] = x[atom2][1] - x_atom_1[1]; + diffx[2] = x[atom2][2] - x_atom_1[2]; + domain->minimum_image(diffx[0], diffx[1], diffx[2]); + x_atom_2[0] = x_atom_1[0] + diffx[0]; + x_atom_2[1] = x_atom_1[1] + diffx[1]; + x_atom_2[2] = x_atom_1[2] + diffx[2]; + + // minimum image of atom3 with respect to atom2 + diffx[0] = x[atom3][0] - x_atom_2[0]; + diffx[1] = x[atom3][1] - x_atom_2[1]; + diffx[2] = x[atom3][2] - x_atom_2[2]; + domain->minimum_image(diffx[0], diffx[1], diffx[2]); + x_atom_3[0] = x_atom_2[0] + diffx[0]; + x_atom_3[1] = x_atom_2[1] + diffx[1]; + x_atom_3[2] = x_atom_2[2] + diffx[2]; + + // minimum image of atom3 with respect to atom2 + diffx[0] = x[atom4][0] - x_atom_3[0]; + diffx[1] = x[atom4][1] - x_atom_3[1]; + diffx[2] = x[atom4][2] - x_atom_3[2]; + domain->minimum_image(diffx[0], diffx[1], diffx[2]); + x_atom_4[0] = x_atom_3[0] + diffx[0]; + x_atom_4[1] = x_atom_3[1] + diffx[1]; + x_atom_4[2] = x_atom_3[2] + diffx[2]; + + // check if any bond vector crosses the plane of interest + double tau_right = (x_atom_2[dir] - pos) / (x_atom_2[dir] - x_atom_1[dir]); + double tau_middle = (x_atom_3[dir] - pos) / (x_atom_3[dir] - x_atom_2[dir]); + double tau_left = (x_atom_4[dir] - pos) / (x_atom_4[dir] - x_atom_3[dir]); + bool right_cross = ((tau_right >=0) && (tau_right <= 1)); + bool middle_cross = ((tau_middle >= 0) && (tau_middle <= 1)); + bool left_cross = ((tau_left >=0) && (tau_left <= 1)); + + // no bonds crossing the plane + if (!right_cross && !middle_cross && !left_cross) continue; + + dihedral->born_matrix(i, atom1, atom2, atom3, atom4, dudih, du2dih); + + // first bond + vb1x = x_atom_1[0] - x_atom_2[0]; + vb1y = x_atom_1[1] - x_atom_2[1]; + vb1z = x_atom_1[2] - x_atom_2[2]; + + // second bond + vb2x = x_atom_3[0] - x_atom_2[0]; + vb2y = x_atom_3[1] - x_atom_2[1]; + vb2z = x_atom_3[2] - x_atom_2[2]; + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + + // third bond + vb3x = x_atom_4[0] - x_atom_3[0]; + vb3y = x_atom_4[1] - x_atom_3[1]; + vb3z = x_atom_4[2] - x_atom_3[2]; + + // c0 calculation + sb1 = 1.0 / (vb1x*vb1x + vb1y*vb1y + vb1z*vb1z); + sb2 = 1.0 / (vb2x*vb2x + vb2y*vb2y + vb2z*vb2z); + sb3 = 1.0 / (vb3x*vb3x + vb3y*vb3y + vb3z*vb3z); + + rb1 = sqrt(sb1); + rb3 = sqrt(sb3); + + c0 = (vb1x*vb3x + vb1y*vb3y + vb1z*vb3z) * rb1*rb3; + // 1st and 2nd angle + b1mag2 = vb1x*vb1x + vb1y*vb1y + vb1z*vb1z; + b1mag = sqrt(b1mag2); + b2mag2 = vb2x*vb2x + vb2y*vb2y + vb2z*vb2z; + b2mag = sqrt(b2mag2); + b3mag2 = vb3x*vb3x + vb3y*vb3y + vb3z*vb3z; + b3mag = sqrt(b3mag2); + + ctmp = vb1x*vb2x + vb1y*vb2y + vb1z*vb2z; + r12c1 = 1.0 / (b1mag*b2mag); + c1mag = ctmp * r12c1; + + ctmp = vb2xm*vb3x + vb2ym*vb3y + vb2zm*vb3z; + r12c2 = 1.0 / (b2mag*b3mag); + c2mag = ctmp * r12c2; + + // cos and sin of 2 angles and final c + sin2 = MAX(1.0 - c1mag*c1mag,0.0); + sc1 = sqrt(sin2); + if (sc1 < SMALL) sc1 = SMALL; + sc1 = 1.0/sc1; + + sin2 = MAX(1.0 - c2mag*c2mag,0.0); + sc2 = sqrt(sin2); + if (sc2 < SMALL) sc2 = SMALL; + sc2 = 1.0/sc2; + + s1 = sc1 * sc1; + s2 = sc2 * sc2; + s12 = sc1 * sc2; + c = (c0 + c1mag*c2mag) * s12; + + // error check + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + // forces on each particle + double a = dudih; + c = c * a; + s12 = s12 * a; + a11 = c*sb1*s1; + a22 = -sb2 * (2.0*c0*s12 - c*(s1+s2)); + a33 = c*sb3*s2; + a12 = -r12c1 * (c1mag*c*s1 + c2mag*s12); + a13 = -rb1*rb3*s12; + a23 = r12c2 * (c2mag*c*s2 + c1mag*s12); + + sx2 = a12*vb1x + a22*vb2x + a23*vb3x; + sy2 = a12*vb1y + a22*vb2y + a23*vb3y; + sz2 = a12*vb1z + a22*vb2z + a23*vb3z; + + f1[0] = a11*vb1x + a12*vb2x + a13*vb3x; + f1[1] = a11*vb1y + a12*vb2y + a13*vb3y; + f1[2] = a11*vb1z + a12*vb2z + a13*vb3z; + + f2[0] = -sx2 - f1[0]; + f2[1] = -sy2 - f1[1]; + f2[2] = -sz2 - f1[2]; + + f4[0] = a13*vb1x + a23*vb2x + a33*vb3x; + f4[1] = a13*vb1y + a23*vb2y + a33*vb3y; + f4[2] = a13*vb1z + a23*vb2z + a33*vb3z; + + f3[0] = sx2 - f4[0]; + f3[1] = sy2 - f4[1]; + f3[2] = sz2 - f4[2]; + + // only right bond crossing the plane + if (right_cross && !middle_cross && !left_cross) + { + double sgn = copysign(1.0, x_atom_1[dir] - pos); + df[0] = sgn * f1[0]; + df[1] = sgn * f1[1]; + df[2] = sgn * f1[2]; + } + + // only middle bond crossing the plane + if (!right_cross && middle_cross && !left_cross) + { + double sgn = copysign(1.0, x_atom_2[dir] - pos); + df[0] = sgn * (f2[0] + f1[0]); + df[1] = sgn * (f2[1] + f1[1]); + df[2] = sgn * (f2[2] + f1[2]); + } + + // only left bond crossing the plane + if (!right_cross && !middle_cross && left_cross) + { + double sgn = copysign(1.0, x_atom_4[dir] - pos); + df[0] = sgn * f4[0]; + df[1] = sgn * f4[1]; + df[2] = sgn * f4[2]; + } + + // only right & middle bonds crossing the plane + if (right_cross && middle_cross && !left_cross) + { + double sgn = copysign(1.0, x_atom_2[dir] - pos); + df[0] = sgn * f2[0]; + df[1] = sgn * f2[1]; + df[2] = sgn * f2[2]; + } + + // only right & left bonds crossing the plane + if (right_cross && !middle_cross && left_cross) + { + double sgn = copysign(1.0, x_atom_1[dir] - pos); + df[0] = sgn * (f1[0] + f4[0]); + df[1] = sgn * (f1[1] + f4[1]); + df[2] = sgn * (f1[2] + f4[2]); + } + + // only middle & left bonds crossing the plane + if (!right_cross && middle_cross && left_cross) + { + double sgn = copysign(1.0, x_atom_3[dir] - pos); + df[0] = sgn * f3[0]; + df[1] = sgn * f3[1]; + df[2] = sgn * f3[2]; + } + + // all three bonds crossing the plane + if (right_cross && middle_cross && left_cross) + { + double sgn = copysign(1.0, x_atom_1[dir] - pos); + df[0] = sgn * (f1[0] + f3[0]); + df[1] = sgn * (f1[1] + f3[1]); + df[2] = sgn * (f1[2] + f3[2]); + } + + local_contribution[0] += df[0]/area*nktv2p; + local_contribution[1] += df[1]/area*nktv2p; + local_contribution[2] += df[2]/area*nktv2p; + } + } + + // loop over the keywords and if necessary add the dihedral contribution + int m = 0; + while (m < nvalues) { + if ((which[m] == CONF) || (which[m] == TOTAL) || (which[m] == DIHEDRAL)) { + dihedral_local[m] = local_contribution[0]; + dihedral_local[m+1] = local_contribution[1]; + dihedral_local[m+2] = local_contribution[2]; + } + m += 3; + } + } From dc84ab5e5fc826e8095eb7d2c2ec38628dc4c7f7 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 28 Sep 2023 17:06:11 +0300 Subject: [PATCH 057/189] Update compute_stress_mop.rst --- doc/src/compute_stress_mop.rst | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/doc/src/compute_stress_mop.rst b/doc/src/compute_stress_mop.rst index 3cd7a67c9c..74d4c618e7 100644 --- a/doc/src/compute_stress_mop.rst +++ b/doc/src/compute_stress_mop.rst @@ -132,14 +132,9 @@ size does not change in time, and axis-aligned planes. The method only works with two-body pair interactions, because it requires the class method ``Pair::single()`` to be implemented, which is not possible for manybody potentials. In particular, compute -*stress/mop/profile* does not work with more than two-body pair +*stress/mop/profile* and *stress/mop* do not work with more than two-body pair interactions, long range (kspace) interactions and -improper intramolecular interactions. Similarly, compute -*stress/mop* does not work with more than two-body pair interactions, -long range (kspace) interactions and dihedral/improper intramolecular -interactions but works with all bond interactions with the class method -single() implemented and all angle interactions with the class method -born_matrix() implemented. +improper intramolecular interactions. Related commands """""""""""""""" From d84ee0c4f1f97728d69a55163d8356eba16e290b Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 28 Sep 2023 17:48:17 +0300 Subject: [PATCH 058/189] Update compute_stress_mop_profile.cpp --- src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp index 223e4da66a..41b5f64a67 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp @@ -325,6 +325,9 @@ void ComputeStressMopProfile::compute_array() } } + // sum dihedral contribution over all procs + MPI_Allreduce(&dihedral_local[0][0],&dihedral_global[0][0],nbins*nvalues,MPI_DOUBLE,MPI_SUM,world); + for (int ibin = 0; ibin < nbins; ibin++) { array[ibin][0] = coord[ibin]; From 14f0045567adfd724cc598b7d7f082b35b6617b6 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Mon, 23 Oct 2023 10:27:18 -0600 Subject: [PATCH 059/189] Fixing missing variable in multi --- src/npair_multi.cpp | 2 ++ src/nstencil_multi.cpp | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/npair_multi.cpp b/src/npair_multi.cpp index e74e16470a..7cfb3b836b 100644 --- a/src/npair_multi.cpp +++ b/src/npair_multi.cpp @@ -92,6 +92,8 @@ void NPairMulti::build(NeighList *list) for (i = 0; i < nlocal; i++) { n = 0; neighptr = ipage->vget(); + + itag = tag[i]; itype = type[i]; icollection = collection[i]; xtmp = x[i][0]; diff --git a/src/nstencil_multi.cpp b/src/nstencil_multi.cpp index 8e504cc9b9..a73215a058 100644 --- a/src/nstencil_multi.cpp +++ b/src/nstencil_multi.cpp @@ -95,6 +95,8 @@ void NStencilMulti::create() // Half and ortho stencils include central bin first // This preserves the historical order of the neighbor list // as the old npair classes used to separately parse the central bin first + // This !TRI condition (and the one below) are now unnecessary + // since triclinic only uses full stencils - kept the flags for clarity if (HALF && (!TRI)) if (half_flag) stencil_multi[icollection][jcollection][ns++] = 0; @@ -119,7 +121,7 @@ void NStencilMulti::create() } } } - if (bin_distance_multi(i,j,k,bin_collection) < cutsq) + if (bin_distance_multi(i, j, k, bin_collection) < cutsq) stencil_multi[icollection][jcollection][ns++] = k * mbiny * mbinx + j * mbinx + i; } } From 3536cf9db968a20ad6d7fca9006b2b99f4f4af94 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Mon, 23 Oct 2023 12:59:13 -0600 Subject: [PATCH 060/189] Adding triclinic patch to intel stencil --- src/INTEL/nstencil_bin_intel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/INTEL/nstencil_bin_intel.cpp b/src/INTEL/nstencil_bin_intel.cpp index 36a79e2997..4b7dc31f46 100644 --- a/src/INTEL/nstencil_bin_intel.cpp +++ b/src/INTEL/nstencil_bin_intel.cpp @@ -32,8 +32,8 @@ void NStencilBinIntel::create() // For half stencils, only the upper plane is needed int sy_min = sy; int sz_min = sz; - if (HALF && (!DIM_3D)) sy_min = 0; - if (HALF && DIM_3D) sz_min = 0; + if ((!TRI) && HALF && (!DIM_3D)) sy_min = 0; + if ((!TRI) && HALF && DIM_3D) sz_min = 0; nstencil = 0; From 51577eff2ccde5a9bd734ac5f5e73f733adbf521 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Mon, 23 Oct 2023 16:14:56 -0600 Subject: [PATCH 061/189] Merging atomonly npair styles, minor fixes --- src/OPENMP/npair_bin_atomonly_omp.cpp | 202 -------------------------- src/OPENMP/npair_bin_atomonly_omp.h | 78 ---------- src/OPENMP/npair_bin_omp.cpp | 133 ++++++++++------- src/OPENMP/npair_bin_omp.h | 60 ++++++-- src/OPENMP/npair_multi_old_omp.cpp | 4 +- src/OPENMP/npair_multi_omp.cpp | 127 +++++++++------- src/OPENMP/npair_multi_omp.h | 74 +++++++--- src/OPENMP/npair_respa_bin_omp.cpp | 4 +- src/npair_bin.cpp | 143 ++++++++++-------- src/npair_bin.h | 54 +++++-- src/npair_bin_atomonly.cpp | 180 ----------------------- src/npair_bin_atomonly.h | 77 ---------- src/npair_multi.cpp | 137 ++++++++++------- src/npair_multi.h | 76 +++++++--- src/npair_multi_old.cpp | 6 +- src/npair_respa_bin.cpp | 4 +- src/nstencil_multi_old.cpp | 2 +- 17 files changed, 540 insertions(+), 821 deletions(-) delete mode 100644 src/OPENMP/npair_bin_atomonly_omp.cpp delete mode 100644 src/OPENMP/npair_bin_atomonly_omp.h delete mode 100644 src/npair_bin_atomonly.cpp delete mode 100644 src/npair_bin_atomonly.h diff --git a/src/OPENMP/npair_bin_atomonly_omp.cpp b/src/OPENMP/npair_bin_atomonly_omp.cpp deleted file mode 100644 index 2ddb6a109d..0000000000 --- a/src/OPENMP/npair_bin_atomonly_omp.cpp +++ /dev/null @@ -1,202 +0,0 @@ -// 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 "npair_bin_atomonly_omp.h" -#include "npair_omp.h" -#include "omp_compat.h" - -#include "atom.h" -#include "error.h" -#include "force.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -template -NPairBinAtomonlyOmp::NPairBinAtomonlyOmp(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - Full: - binned neighbor list construction for all neighbors - every neighbor pair appears in list of both atoms i and j - Half + Newtoff: - binned neighbor list construction with partial Newton's 3rd law - each owned atom i checks own bin and other bins in stencil - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) - Half + Newton: - binned neighbor list construction with full Newton's 3rd law - each owned atom i checks its own bin and other bins in Newton stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -template -void NPairBinAtomonlyOmp::build(NeighList *list) -{ - const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; - const double delta = 0.01 * force->angstrom; - - NPAIR_OMP_INIT; -#if defined(_OPENMP) -#pragma omp parallel LMP_DEFAULT_NONE LMP_SHARED(list) -#endif - NPAIR_OMP_SETUP(nlocal); - - int i, j, jh, k, n, itype, jtype, ibin, bin_start; - tagint itag, jtag; - double xtmp, ytmp, ztmp, delx, dely, delz, rsq, radsum, cut, cutsq; - int *neighptr; - - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *tag = atom->tag; - tagint *molecule = atom->molecule; - - int history = list->history; - int mask_history = 1 << HISTBITS; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - // loop over owned atoms, storing neighbors - - for (i = ifrom; i < ito; i++) { - n = 0; - neighptr = ipage.vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - - ibin = atom2bin[i]; - - for (k = 0; k < nstencil; k++) { - bin_start = binhead[ibin + stencil[k]]; - if (stencil[k] == 0) { - if (HALF && NEWTON && (!TRI)) { - // Half neighbor list, newton on, orthonormal - // loop over rest of atoms in i's bin, ghosts are at end of linked list - bin_start = bins[i]; - } - } - - for (j = bin_start; j >= 0; j = bins[j]) { - if (!HALF) { - // Full neighbor list - // only skip i = j - if (i == j) continue; - } else if (!NEWTON) { - // Half neighbor list, newton off - // only store pair if i < j - // stores own/own pairs only once - // stores own/ghost pairs on both procs - if (j <= i) continue; - } else if (TRI) { - // Half neighbor list, newton on, triclinic - // for triclinic, bin stencil is full in all 3 dims - // must use itag/jtag to eliminate half the I/J interactions - // cannot use I/J exact coord comparision - // b/c transforming orthog -> lambda -> orthog for ghost atoms - // with an added PBC offset can shift all 3 coords by epsilon - if (j <= i) continue; - if (j >= nlocal) { - jtag = tag[j]; - if (itag > jtag) { - if ((itag + jtag) % 2 == 0) continue; - } else if (itag < jtag) { - if ((itag + jtag) % 2 == 1) continue; - } else { - if (fabs(x[j][2] - ztmp) > delta) { - if (x[j][2] < ztmp) continue; - } else if (fabs(x[j][1] - ytmp) > delta) { - if (x[j][1] < ytmp) continue; - } else { - if (x[j][0] < xtmp) continue; - } - } - } - } else { - // Half neighbor list, newton on, orthonormal - // store every pair for every bin in stencil, except for i's bin - - if (stencil[k] == 0) { - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the "right" of i - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - } - } - - jtype = type[j]; - if (exclude && exclusion(i, j, itype, jtype, mask, molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx * delx + dely * dely + delz * delz; - - if (SIZE) { - radsum = radius[i] + radius[j]; - cut = radsum + skin; - cutsq = cut * cut; - - if (rsq <= cutsq) { - jh = j; - if (history && rsq < radsum * radsum) - jh = jh ^ mask_history; - neighptr[n++] = jh; - } - } else { - if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; - } - } - } - - ilist[i] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage.vgot(n); - if (ipage.status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - NPAIR_OMP_CLOSE; - list->inum = nlocal; - if (!HALF) list->gnum = 0; -} - -namespace LAMMPS_NS { -template class NPairBinAtomonlyOmp<0,1,0,0>; -template class NPairBinAtomonlyOmp<1,0,0,0>; -template class NPairBinAtomonlyOmp<1,1,0,0>; -template class NPairBinAtomonlyOmp<1,1,1,0>; -template class NPairBinAtomonlyOmp<0,1,0,1>; -template class NPairBinAtomonlyOmp<1,0,0,1>; -template class NPairBinAtomonlyOmp<1,1,0,1>; -template class NPairBinAtomonlyOmp<1,1,1,1>; -} diff --git a/src/OPENMP/npair_bin_atomonly_omp.h b/src/OPENMP/npair_bin_atomonly_omp.h deleted file mode 100644 index c488486898..0000000000 --- a/src/OPENMP/npair_bin_atomonly_omp.h +++ /dev/null @@ -1,78 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -typedef NPairBinAtomonlyOmp<0, 1, 0, 0> NPairFullBinAtomonlyOmp; -NPairStyle(full/bin/atomonly/omp, - NPairFullBinAtomonlyOmp, - NP_FULL | NP_BIN | NP_OMP | NP_ATOMONLY | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); - -typedef NPairBinAtomonlyOmp<1, 0, 0, 0> NPairHalfBinNewtoffAtomonlyOmp; -NPairStyle(half/bin/newtoff/atomonly/omp, - NPairHalfBinNewtoffAtomonlyOmp, - NP_HALF | NP_BIN | NP_OMP | NP_ATOMONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); - -typedef NPairBinAtomonlyOmp<1, 1, 0, 0> NPairHalfBinNewtonAtomonlyOmp; -NPairStyle(half/bin/newton/atomonly/omp, - NPairHalfBinNewtonAtomonlyOmp, - NP_HALF | NP_BIN | NP_OMP | NP_ATOMONLY | NP_NEWTON | NP_ORTHO); - -typedef NPairBinAtomonlyOmp<1, 1, 1, 0> NPairHalfBinNewtonTriAtomonlyOmp; -NPairStyle(half/bin/newton/tri/atomonly/omp, - NPairHalfBinNewtonTriAtomonlyOmp, - NP_HALF | NP_BIN | NP_OMP | NP_ATOMONLY | NP_NEWTON | NP_TRI); - -typedef NPairBinAtomonlyOmp<0, 1, 0, 1> NPairFullSizeBinAtomonlyOmp; -NPairStyle(full/size/bin/atomonly/omp, - NPairFullSizeBinAtomonlyOmp, - NP_FULL | NP_SIZE | NP_BIN | NP_OMP | NP_ATOMONLY | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); - -typedef NPairBinAtomonlyOmp<1, 0, 0, 1> NPairHalfSizeBinNewtoffAtomonlyOmp; -NPairStyle(half/size/bin/newtoff/atomonly/omp, - NPairHalfSizeBinNewtoffAtomonlyOmp, - NP_HALF | NP_SIZE | NP_BIN | NP_OMP | NP_ATOMONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); - -typedef NPairBinAtomonlyOmp<1, 1, 0, 1> NPairHalfSizeBinNewtonAtomonlyOmp; -NPairStyle(half/size/bin/newton/atomonly/omp, - NPairHalfSizeBinNewtonAtomonlyOmp, - NP_HALF | NP_SIZE | NP_BIN | NP_OMP | NP_ATOMONLY | NP_NEWTON | NP_ORTHO); - -typedef NPairBinAtomonlyOmp<1, 1, 1, 1> NPairHalfSizeBinNewtonTriAtomonlyOmp; -NPairStyle(half/size/bin/newton/tri/atomonly/omp, - NPairHalfSizeBinNewtonTriAtomonlyOmp, - NP_HALF | NP_SIZE | NP_BIN | NP_OMP | NP_ATOMONLY | NP_NEWTON | NP_TRI); -// clang-format on -#else - - -#ifndef LMP_NPAIR_BIN_ATOMONLY_OMP_H -#define LMP_NPAIR_BIN_ATOMONLY_OMP_H - -#include "npair.h" - -namespace LAMMPS_NS { - -template -class NPairBinAtomonlyOmp : public NPair { - public: - NPairBinAtomonlyOmp(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/OPENMP/npair_bin_omp.cpp b/src/OPENMP/npair_bin_omp.cpp index bbfa3323f1..81b5e085d6 100644 --- a/src/OPENMP/npair_bin_omp.cpp +++ b/src/OPENMP/npair_bin_omp.cpp @@ -29,8 +29,8 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -template -NPairBinOmp::NPairBinOmp(LAMMPS *lmp) : NPair(lmp) {} +template +NPairBinOmp::NPairBinOmp(LAMMPS *lmp) : NPair(lmp) {} /* ---------------------------------------------------------------------- Full: @@ -47,8 +47,8 @@ NPairBinOmp::NPairBinOmp(LAMMPS *lmp) : NPair(lmp) {} every pair stored exactly once by some processor ------------------------------------------------------------------------- */ -template -void NPairBinOmp::build(NeighList *list) +template +void NPairBinOmp::build(NeighList *list) { const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; const int molecular = atom->molecular; @@ -101,10 +101,12 @@ void NPairBinOmp::build(NeighList *list) xtmp = x[i][0]; ytmp = x[i][1]; ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; + if (!ATOMONLY) { + if (moltemplate) { + imol = molindex[i]; + iatom = molatom[i]; + tagprev = tag[i] - iatom - 1; + } } // loop over all atoms in surrounding bins in stencil including self @@ -113,9 +115,9 @@ void NPairBinOmp::build(NeighList *list) ibin = atom2bin[i]; for (k = 0; k < nstencil; k++) { - bin_start = binhead[ibin+stencil[k]]; - if (stencil[k] == 0) { - if (HALF && NEWTON && (!TRI)) { + bin_start = binhead[ibin + stencil[k]]; + if (HALF && NEWTON && (!TRI)) { + if (stencil[k] == 0) { // Half neighbor list, newton on, orthonormal // loop over rest of atoms in i's bin, ghosts are at end of linked list bin_start = bins[i]; @@ -159,7 +161,7 @@ void NPairBinOmp::build(NeighList *list) } } else { // Half neighbor list, newton on, orthonormal - // store every pair for every bin in stencil,except for i's bin + // store every pair for every bin in stencil, except for i's bin if (stencil[k] == 0) { // if j is owned atom, store it, since j is beyond i in linked list @@ -187,45 +189,58 @@ void NPairBinOmp::build(NeighList *list) cut = radsum + skin; cutsq = cut * cut; - if (rsq <= cutsq) { - jh = j; - if (history && rsq < radsum * radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i], nspecial[i], tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], onemols[imol]->nspecial[iatom], - tag[j] - tagprev); - else - which = 0; - if (which == 0) - neighptr[n++] = jh; - else if (domain->minimum_image_check(delx, dely, delz)) - neighptr[n++] = jh; - else if (which > 0) - neighptr[n++] = jh ^ (which << SBBITS); - } else + if (ATOMONLY) { + if (rsq <= cutsq) { + jh = j; + if (history && rsq < radsum * radsum) + jh = jh ^ mask_history; neighptr[n++] = jh; + } + } else { + if (rsq <= cutsq) { + jh = j; + if (history && rsq < radsum * radsum) + jh = jh ^ mask_history; + + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i], nspecial[i], tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], onemols[imol] ->nspecial[iatom], + tag[j] - tagprev); + else + which = 0; + if (which == 0) + neighptr[n++] = jh; + else if (domain->minimum_image_check(delx, dely, delz)) + neighptr[n++] = jh; + else if (which > 0) + neighptr[n++] = jh ^ (which << SBBITS); + } else + neighptr[n++] = jh; + } } } else { - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i], nspecial[i], tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], onemols[imol]->nspecial[iatom], - tag[j] - tagprev); - else which = 0; - if (which == 0) + if (ATOMONLY) { + if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; + } else { + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i], nspecial[i], tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], onemols[imol] ->nspecial[iatom], + tag[j] - tagprev); + else which = 0; + if (which == 0) + neighptr[n++] = j; + else if (domain->minimum_image_check(delx, dely, delz)) + neighptr[n++] = j; + else if (which > 0) + neighptr[n++] = j ^ (which << SBBITS); + } else neighptr[n++] = j; - else if (domain->minimum_image_check(delx, dely, delz)) - neighptr[n++] = j; - else if (which > 0) - neighptr[n++] = j ^ (which << SBBITS); - } else - neighptr[n++] = j; + } } } } @@ -243,12 +258,20 @@ void NPairBinOmp::build(NeighList *list) } namespace LAMMPS_NS { -template class NPairBinOmp<0,1,0,0>; -template class NPairBinOmp<1,0,0,0>; -template class NPairBinOmp<1,1,0,0>; -template class NPairBinOmp<1,1,1,0>; -template class NPairBinOmp<0,1,0,1>; -template class NPairBinOmp<1,0,0,1>; -template class NPairBinOmp<1,1,0,1>; -template class NPairBinOmp<1,1,1,1>; +template class NPairBinOmp<0,1,0,0,0>; +template class NPairBinOmp<1,0,0,0,0>; +template class NPairBinOmp<1,1,0,0,0>; +template class NPairBinOmp<1,1,1,0,0>; +template class NPairBinOmp<0,1,0,1,0>; +template class NPairBinOmp<1,0,0,1,0>; +template class NPairBinOmp<1,1,0,1,0>; +template class NPairBinOmp<1,1,1,1,0>; +template class NPairBinOmp<0,1,0,0,1>; +template class NPairBinOmp<1,0,0,0,1>; +template class NPairBinOmp<1,1,0,0,1>; +template class NPairBinOmp<1,1,1,0,1>; +template class NPairBinOmp<0,1,0,1,1>; +template class NPairBinOmp<1,0,0,1,1>; +template class NPairBinOmp<1,1,0,1,1>; +template class NPairBinOmp<1,1,1,1,1>; } diff --git a/src/OPENMP/npair_bin_omp.h b/src/OPENMP/npair_bin_omp.h index 595b789d92..dfe5429ff4 100644 --- a/src/OPENMP/npair_bin_omp.h +++ b/src/OPENMP/npair_bin_omp.h @@ -13,47 +13,89 @@ #ifdef NPAIR_CLASS // clang-format off -typedef NPairBinOmp<0, 1, 0, 0> NPairFullBinOmp; +typedef NPairBinOmp<0, 1, 0, 0, 0> NPairFullBinOmp; NPairStyle(full/bin/omp, NPairFullBinOmp, NP_FULL | NP_BIN | NP_OMP | NP_MOLONLY | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairBinOmp<1, 0, 0, 0> NPairHalfBinNewtoffOmp; +typedef NPairBinOmp<1, 0, 0, 0, 0> NPairHalfBinNewtoffOmp; NPairStyle(half/bin/newtoff/omp, NPairHalfBinNewtoffOmp, NP_HALF | NP_BIN | NP_OMP | NP_MOLONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairBinOmp<1, 1, 0, 0> NPairHalfBinNewtonOmp; +typedef NPairBinOmp<1, 1, 0, 0, 0> NPairHalfBinNewtonOmp; NPairStyle(half/bin/newton/omp, NPairHalfBinNewtonOmp, NP_HALF | NP_BIN | NP_OMP | NP_MOLONLY | NP_NEWTON | NP_ORTHO); -typedef NPairBinOmp<1, 1, 1, 0> NPairHalfBinNewtonTriOmp; +typedef NPairBinOmp<1, 1, 1, 0, 0> NPairHalfBinNewtonTriOmp; NPairStyle(half/bin/newton/tri/omp, NPairHalfBinNewtonTriOmp, NP_HALF | NP_BIN | NP_OMP | NP_MOLONLY | NP_NEWTON | NP_TRI); -typedef NPairBinOmp<0, 1, 0, 1> NPairFullSizeBinOmp; +typedef NPairBinOmp<0, 1, 0, 1, 0> NPairFullSizeBinOmp; NPairStyle(full/size/bin/omp, NPairFullSizeBinOmp, NP_FULL | NP_SIZE | NP_BIN | NP_OMP | NP_MOLONLY | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairBinOmp<1, 0, 0, 1> NPairHalfSizeBinNewtoffOmp; +typedef NPairBinOmp<1, 0, 0, 1, 0> NPairHalfSizeBinNewtoffOmp; NPairStyle(half/size/bin/newtoff/omp, NPairHalfSizeBinNewtoffOmp, NP_HALF | NP_SIZE | NP_BIN | NP_OMP | NP_MOLONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairBinOmp<1, 1, 0, 1> NPairHalfSizeBinNewtonOmp; +typedef NPairBinOmp<1, 1, 0, 1, 0> NPairHalfSizeBinNewtonOmp; NPairStyle(half/size/bin/newton/omp, NPairHalfSizeBinNewtonOmp, NP_HALF | NP_SIZE | NP_BIN | NP_OMP | NP_MOLONLY | NP_NEWTON | NP_ORTHO); -typedef NPairBinOmp<1, 1, 1, 1> NPairHalfSizeBinNewtonTriOmp; +typedef NPairBinOmp<1, 1, 1, 1, 0> NPairHalfSizeBinNewtonTriOmp; NPairStyle(half/size/bin/newton/tri/omp, NPairHalfSizeBinNewtonTriOmp, NP_HALF | NP_SIZE | NP_BIN | NP_OMP | NP_MOLONLY | NP_NEWTON | NP_TRI); + +typedef NPairBinOmp<0, 1, 0, 0, 1> NPairFullBinAtomonlyOmp; +NPairStyle(full/bin/atomonly/omp, + NPairFullBinAtomonlyOmp, + NP_FULL | NP_BIN | NP_OMP | NP_ATOMONLY | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBinOmp<1, 0, 0, 0, 1> NPairHalfBinNewtoffAtomonlyOmp; +NPairStyle(half/bin/newtoff/atomonly/omp, + NPairHalfBinNewtoffAtomonlyOmp, + NP_HALF | NP_BIN | NP_OMP | NP_ATOMONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBinOmp<1, 1, 0, 0, 1> NPairHalfBinNewtonAtomonlyOmp; +NPairStyle(half/bin/newton/atomonly/omp, + NPairHalfBinNewtonAtomonlyOmp, + NP_HALF | NP_BIN | NP_OMP | NP_ATOMONLY | NP_NEWTON | NP_ORTHO); + +typedef NPairBinOmp<1, 1, 1, 0, 1> NPairHalfBinNewtonTriAtomonlyOmp; +NPairStyle(half/bin/newton/tri/atomonly/omp, + NPairHalfBinNewtonTriAtomonlyOmp, + NP_HALF | NP_BIN | NP_OMP | NP_ATOMONLY | NP_NEWTON | NP_TRI); + +typedef NPairBinOmp<0, 1, 0, 1, 1> NPairFullSizeBinAtomonlyOmp; +NPairStyle(full/size/bin/atomonly/omp, + NPairFullSizeBinAtomonlyOmp, + NP_FULL | NP_SIZE | NP_BIN | NP_OMP | NP_ATOMONLY | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBinOmp<1, 0, 0, 1, 1> NPairHalfSizeBinNewtoffAtomonlyOmp; +NPairStyle(half/size/bin/newtoff/atomonly/omp, + NPairHalfSizeBinNewtoffAtomonlyOmp, + NP_HALF | NP_SIZE | NP_BIN | NP_OMP | NP_ATOMONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBinOmp<1, 1, 0, 1, 1> NPairHalfSizeBinNewtonAtomonlyOmp; +NPairStyle(half/size/bin/newton/atomonly/omp, + NPairHalfSizeBinNewtonAtomonlyOmp, + NP_HALF | NP_SIZE | NP_BIN | NP_OMP | NP_ATOMONLY | NP_NEWTON | NP_ORTHO); + +typedef NPairBinOmp<1, 1, 1, 1, 1> NPairHalfSizeBinNewtonTriAtomonlyOmp; +NPairStyle(half/size/bin/newton/tri/atomonly/omp, + NPairHalfSizeBinNewtonTriAtomonlyOmp, + NP_HALF | NP_SIZE | NP_BIN | NP_OMP | NP_ATOMONLY | NP_NEWTON | NP_TRI); // clang-format on #else @@ -64,7 +106,7 @@ NPairStyle(half/size/bin/newton/tri/omp, namespace LAMMPS_NS { -template +template class NPairBinOmp : public NPair { public: NPairBinOmp(class LAMMPS *); diff --git a/src/OPENMP/npair_multi_old_omp.cpp b/src/OPENMP/npair_multi_old_omp.cpp index 893af5a6b7..894ff9b987 100644 --- a/src/OPENMP/npair_multi_old_omp.cpp +++ b/src/OPENMP/npair_multi_old_omp.cpp @@ -118,8 +118,8 @@ void NPairMultiOldOmp::build(NeighList *list) cutnsq = cutneighsq[itype]; ns = nstencil_multi_old[itype]; for (k = 0; k < ns; k++) { - bin_start = binhead[ibin+stencil[k]]; - if (stencil[k] == 0) { + bin_start = binhead[ibin+s[k]]; + if (s[k] == 0) { if (HALF && NEWTON && (!TRI)) { // Half neighbor list, newton on, orthonormal // loop over rest of atoms in i's bin, ghosts are at end of linked list diff --git a/src/OPENMP/npair_multi_omp.cpp b/src/OPENMP/npair_multi_omp.cpp index 06b26f6066..7999ff5029 100644 --- a/src/OPENMP/npair_multi_omp.cpp +++ b/src/OPENMP/npair_multi_omp.cpp @@ -30,8 +30,8 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -template -NPairMultiOmp::NPairMultiOmp(LAMMPS *lmp) : NPair(lmp) {} +template +NPairMultiOmp::NPairMultiOmp(LAMMPS *lmp) : NPair(lmp) {} /* ---------------------------------------------------------------------- multi stencil is icollection-jcollection dependent @@ -49,8 +49,8 @@ NPairMultiOmp::NPairMultiOmp(LAMMPS *lmp) : NPair(lmp) every pair stored exactly once by some processor ------------------------------------------------------------------------- */ -template -void NPairMultiOmp::build(NeighList *list) +template +void NPairMultiOmp::build(NeighList *list) { const int nlocal = (includegroup) ? atom->nfirst : atom->nlocal; const int molecular = atom->molecular; @@ -104,10 +104,12 @@ void NPairMultiOmp::build(NeighList *list) xtmp = x[i][0]; ytmp = x[i][1]; ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; + if (!ATOMONLY) { + if (moltemplate) { + imol = molindex[i]; + iatom = molatom[i]; + tagprev = tag[i] - iatom - 1; + } } ibin = atom2bin[i]; @@ -214,46 +216,59 @@ void NPairMultiOmp::build(NeighList *list) cut = radsum + skin; cutsq = cut * cut; - if (rsq <= cutsq) { - jh = j; - if (history && rsq < radsum * radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i], nspecial[i], tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], onemols[imol]->nspecial[iatom], - tag[j] - tagprev); - else - which = 0; - if (which == 0) - neighptr[n++] = jh; - else if (domain->minimum_image_check(delx, dely, delz)) - neighptr[n++] = jh; - else if (which > 0) - neighptr[n++] = jh ^ (which << SBBITS); - } else + if (ATOMONLY) { + if (rsq <= cutsq) { + jh = j; + if (history && rsq < radsum * radsum) + jh = jh ^ mask_history; neighptr[n++] = jh; + } + } else { + if (rsq <= cutsq) { + jh = j; + if (history && rsq < radsum * radsum) + jh = jh ^ mask_history; + + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i], nspecial[i], tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], onemols [imol]->nspecial[iatom], + tag[j] - tagprev); + else + which = 0; + if (which == 0) + neighptr[n++] = jh; + else if (domain->minimum_image_check(delx, dely, delz)) + neighptr[n++] = jh; + else if (which > 0) + neighptr[n++] = jh ^ (which << SBBITS); + } else + neighptr[n++] = jh; + } } } else { - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i], nspecial[i], tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], onemols[imol]->nspecial[iatom], - tag[j] - tagprev); - else - which = 0; - if (which == 0) + if (ATOMONLY) { + if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; + } else { + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i], nspecial[i], tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], onemols [imol]->nspecial[iatom], + tag[j] - tagprev); + else + which = 0; + if (which == 0) + neighptr[n++] = j; + else if (domain->minimum_image_check(delx, dely, delz)) + neighptr[n++] = j; + else if (which > 0) + neighptr[n++] = j ^ (which << SBBITS); + } else neighptr[n++] = j; - else if (domain->minimum_image_check(delx, dely, delz)) - neighptr[n++] = j; - else if (which > 0) - neighptr[n++] = j ^ (which << SBBITS); - } else - neighptr[n++] = j; + } } } } @@ -272,12 +287,20 @@ void NPairMultiOmp::build(NeighList *list) } namespace LAMMPS_NS { -template class NPairMultiOmp<0,1,0,0>; -template class NPairMultiOmp<1,0,0,0>; -template class NPairMultiOmp<1,1,0,0>; -template class NPairMultiOmp<1,1,1,0>; -template class NPairMultiOmp<0,1,0,1>; -template class NPairMultiOmp<1,0,0,1>; -template class NPairMultiOmp<1,1,0,1>; -template class NPairMultiOmp<1,1,1,1>; +template class NPairMultiOmp<0,1,0,0,0>; +template class NPairMultiOmp<1,0,0,0,0>; +template class NPairMultiOmp<1,1,0,0,0>; +template class NPairMultiOmp<1,1,1,0,0>; +template class NPairMultiOmp<0,1,0,1,0>; +template class NPairMultiOmp<1,0,0,1,0>; +template class NPairMultiOmp<1,1,0,1,0>; +template class NPairMultiOmp<1,1,1,1,0>; +template class NPairMultiOmp<0,1,0,0,1>; +template class NPairMultiOmp<1,0,0,0,1>; +template class NPairMultiOmp<1,1,0,0,1>; +template class NPairMultiOmp<1,1,1,0,1>; +template class NPairMultiOmp<0,1,0,1,1>; +template class NPairMultiOmp<1,0,0,1,1>; +template class NPairMultiOmp<1,1,0,1,1>; +template class NPairMultiOmp<1,1,1,1,1>; } diff --git a/src/OPENMP/npair_multi_omp.h b/src/OPENMP/npair_multi_omp.h index dd85ca8a8e..bcb01c87cf 100644 --- a/src/OPENMP/npair_multi_omp.h +++ b/src/OPENMP/npair_multi_omp.h @@ -13,45 +13,85 @@ #ifdef NPAIR_CLASS // clang-format off -typedef NPairMultiOmp<0, 1, 0, 0> NPairFullMultiOmp; +typedef NPairMultiOmp<0, 1, 0, 0, 0> NPairFullMultiOmp; NPairStyle(full/multi/omp, NPairFullMultiOmp, - NP_FULL | NP_MULTI | NP_OMP | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + NP_FULL | NP_MULTI | NP_MOLONLY | NP_OMP | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairMultiOmp<1, 0, 0, 0> NPairHalfMultiNewtoffOmp; +typedef NPairMultiOmp<1, 0, 0, 0, 0> NPairHalfMultiNewtoffOmp; NPairStyle(half/multi/newtoff/omp, NPairHalfMultiNewtoffOmp, - NP_HALF | NP_MULTI | NP_OMP | NP_NEWTOFF | NP_ORTHO | NP_TRI); + NP_HALF | NP_MULTI | NP_MOLONLY | NP_OMP | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairMultiOmp<1, 1, 0, 0> NPairHalfMultiNewtonOmp; +typedef NPairMultiOmp<1, 1, 0, 0, 0> NPairHalfMultiNewtonOmp; NPairStyle(half/multi/newton/omp, NPairHalfMultiNewtonOmp, - NP_HALF | NP_MULTI | NP_OMP | NP_NEWTON | NP_ORTHO); + NP_HALF | NP_MULTI | NP_MOLONLY | NP_OMP | NP_NEWTON | NP_ORTHO); -typedef NPairMultiOmp<1, 1, 1, 0> NPairHalfMultiNewtonTriOmp; +typedef NPairMultiOmp<1, 1, 1, 0, 0> NPairHalfMultiNewtonTriOmp; NPairStyle(half/multi/newton/tri/omp, NPairHalfMultiNewtonTriOmp, - NP_HALF | NP_MULTI | NP_OMP | NP_NEWTON | NP_TRI); + NP_HALF | NP_MULTI | NP_MOLONLY | NP_OMP | NP_NEWTON | NP_TRI); -typedef NPairMultiOmp<0, 1, 0, 1> NPairFullSizeMultiOmp; +typedef NPairMultiOmp<0, 1, 0, 1, 0> NPairFullSizeMultiOmp; NPairStyle(full/size/multi/omp, NPairFullSizeMultiOmp, - NP_FULL | NP_SIZE | NP_MULTI | NP_OMP | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + NP_FULL | NP_SIZE | NP_MULTI | NP_MOLONLY | NP_OMP | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairMultiOmp<1, 0, 0, 1> NPairHalfSizeMultiNewtoffOmp; +typedef NPairMultiOmp<1, 0, 0, 1, 0> NPairHalfSizeMultiNewtoffOmp; NPairStyle(half/size/multi/newtoff/omp, NPairHalfSizeMultiNewtoffOmp, - NP_HALF | NP_SIZE | NP_MULTI | NP_OMP | NP_NEWTOFF | NP_ORTHO | NP_TRI); + NP_HALF | NP_SIZE | NP_MULTI | NP_MOLONLY | NP_OMP | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairMultiOmp<1, 1, 0, 1> NPairHalfSizeMultiNewtonOmp; +typedef NPairMultiOmp<1, 1, 0, 1, 0> NPairHalfSizeMultiNewtonOmp; NPairStyle(half/size/multi/newton/omp, NPairHalfSizeMultiNewtonOmp, - NP_HALF | NP_SIZE | NP_MULTI | NP_OMP | NP_NEWTON | NP_ORTHO); + NP_HALF | NP_SIZE | NP_MULTI | NP_MOLONLY | NP_OMP | NP_NEWTON | NP_ORTHO); -typedef NPairMultiOmp<1, 1, 1, 1> NPairHalfSizeMultiNewtonTriOmp; +typedef NPairMultiOmp<1, 1, 1, 1, 0> NPairHalfSizeMultiNewtonTriOmp; NPairStyle(half/size/multi/newton/tri/omp, NPairHalfSizeMultiNewtonTriOmp, - NP_HALF | NP_SIZE | NP_MULTI | NP_OMP | NP_NEWTON | NP_TRI); + NP_HALF | NP_SIZE | NP_MULTI | NP_MOLONLY | NP_OMP | NP_NEWTON | NP_TRI); + +typedef NPairMultiOmp<0, 1, 0, 0, 1> NPairFullMultiAtomonlyOmp; +NPairStyle(full/multi/atomonly/omp, + NPairFullMultiAtomonlyOmp, + NP_FULL | NP_MULTI | NP_ATOMONLY | NP_OMP | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMultiOmp<1, 0, 0, 0, 1> NPairHalfMultiAtomonlyNewtoffOmp; +NPairStyle(half/multi/atomonly/newtoff/omp, + NPairHalfMultiAtomonlyNewtoffOmp, + NP_HALF | NP_MULTI | NP_ATOMONLY | NP_OMP | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMultiOmp<1, 1, 0, 0, 1> NPairHalfMultiAtomonlyNewtonOmp; +NPairStyle(half/multi/atomonly/newton/omp, + NPairHalfMultiAtomonlyNewtonOmp, + NP_HALF | NP_MULTI | NP_ATOMONLY | NP_OMP | NP_NEWTON | NP_ORTHO); + +typedef NPairMultiOmp<1, 1, 1, 0, 1> NPairHalfMultiAtomonlyNewtonTriOmp; +NPairStyle(half/multi/atomonly/newton/tri/omp, + NPairHalfMultiAtomonlyNewtonTriOmp, + NP_HALF | NP_MULTI | NP_ATOMONLY | NP_OMP | NP_NEWTON | NP_TRI); + +typedef NPairMultiOmp<0, 1, 0, 1, 1> NPairFullSizeMultiAtomonlyOmp; +NPairStyle(full/size/multi/atomonly/omp, + NPairFullSizeMultiAtomonlyOmp, + NP_FULL | NP_SIZE | NP_MULTI | NP_ATOMONLY | NP_OMP | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMultiOmp<1, 0, 0, 1, 1> NPairHalfSizeMultiAtomonlyNewtoffOmp; +NPairStyle(half/size/multi/atomonly/newtoff/omp, + NPairHalfSizeMultiAtomonlyNewtoffOmp, + NP_HALF | NP_SIZE | NP_MULTI | NP_ATOMONLY | NP_OMP | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMultiOmp<1, 1, 0, 1, 1> NPairHalfSizeMultiAtomonlyNewtonOmp; +NPairStyle(half/size/multi/atomonly/newton/omp, + NPairHalfSizeMultiAtomonlyNewtonOmp, + NP_HALF | NP_SIZE | NP_MULTI | NP_ATOMONLY | NP_OMP | NP_NEWTON | NP_ORTHO); + +typedef NPairMultiOmp<1, 1, 1, 1, 1> NPairHalfSizeMultiAtomonlyNewtonTriOmp; +NPairStyle(half/size/multi/atomonly/newton/tri/omp, + NPairHalfSizeMultiAtomonlyNewtonTriOmp, + NP_HALF | NP_SIZE | NP_MULTI | NP_ATOMONLY | NP_OMP | NP_NEWTON | NP_TRI); // clang-format on #else @@ -62,7 +102,7 @@ NPairStyle(half/size/multi/newton/tri/omp, namespace LAMMPS_NS { -template +template class NPairMultiOmp : public NPair { public: NPairMultiOmp(class LAMMPS *); diff --git a/src/OPENMP/npair_respa_bin_omp.cpp b/src/OPENMP/npair_respa_bin_omp.cpp index e1a273b20a..b904e7136b 100644 --- a/src/OPENMP/npair_respa_bin_omp.cpp +++ b/src/OPENMP/npair_respa_bin_omp.cpp @@ -134,8 +134,8 @@ void NPairRespaBinOmp::build(NeighList *list) for (k = 0; k < nstencil; k++) { bin_start = binhead[ibin+stencil[k]]; - if (stencil[k] == 0) { - if (NEWTON && (!TRI)) { + if (NEWTON && (!TRI)) { + if (stencil[k] == 0) { // Half neighbor list, newton on, orthonormal // loop over rest of atoms in i's bin, ghosts are at end of linked list bin_start = bins[i]; diff --git a/src/npair_bin.cpp b/src/npair_bin.cpp index c8c2d23e44..d72a98e471 100644 --- a/src/npair_bin.cpp +++ b/src/npair_bin.cpp @@ -28,8 +28,8 @@ using namespace NeighConst; /* ---------------------------------------------------------------------- */ -template -NPairBin::NPairBin(LAMMPS *lmp) : NPair(lmp) {} +template +NPairBin::NPairBin(LAMMPS *lmp) : NPair(lmp) {} /* ---------------------------------------------------------------------- Full: @@ -46,8 +46,8 @@ NPairBin::NPairBin(LAMMPS *lmp) : NPair(lmp) {} every pair stored exactly once by some processor ------------------------------------------------------------------------- */ -template -void NPairBin::build(NeighList *list) +template +void NPairBin::build(NeighList *list) { int i, j, jh, k, n, itype, jtype, ibin, bin_start, which, imol, iatom, moltemplate; tagint itag, jtag, tagprev; @@ -70,10 +70,12 @@ void NPairBin::build(NeighList *list) int *molindex = atom->molindex; int *molatom = atom->molatom; Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) - moltemplate = 1; - else - moltemplate = 0; + if (!ATOMONLY) { + if (molecular == Atom::TEMPLATE) + moltemplate = 1; + else + moltemplate = 0; + } int history = list->history; int mask_history = 1 << HISTBITS; @@ -95,18 +97,20 @@ void NPairBin::build(NeighList *list) xtmp = x[i][0]; ytmp = x[i][1]; ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; + if (!ATOMONLY) { + if (moltemplate) { + imol = molindex[i]; + iatom = molatom[i]; + tagprev = tag[i] - iatom - 1; + } } ibin = atom2bin[i]; for (k = 0; k < nstencil; k++) { bin_start = binhead[ibin + stencil[k]]; - if (stencil[k] == 0) { - if (HALF && NEWTON && (!TRI)) { + if (HALF && NEWTON && (!TRI)) { + if (stencil[k] == 0) { // Half neighbor list, newton on, orthonormal // loop over rest of atoms in i's bin, ghosts are at end of linked list bin_start = bins[i]; @@ -149,7 +153,7 @@ void NPairBin::build(NeighList *list) } } else { // Half neighbor list, newton on, orthonormal - // store every pair for every bin in stencil,except for i's bin + // store every pair for every bin in stencil, except for i's bin if (stencil[k] == 0) { // if j is owned atom, store it, since j is beyond i in linked list @@ -177,46 +181,59 @@ void NPairBin::build(NeighList *list) cut = radsum + skin; cutsq = cut * cut; - if (rsq <= cutsq) { - jh = j; - if (history && rsq < radsum * radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i], nspecial[i], tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], onemols[imol]->nspecial[iatom], - tag[j] - tagprev); - else - which = 0; - if (which == 0) - neighptr[n++] = jh; - else if (domain->minimum_image_check(delx, dely, delz)) - neighptr[n++] = jh; - else if (which > 0) - neighptr[n++] = jh ^ (which << SBBITS); - } else + if (ATOMONLY) { + if (rsq <= cutsq) { + jh = j; + if (history && rsq < (radsum * radsum)) + jh = jh ^ mask_history; neighptr[n++] = jh; + } + } else { + if (rsq <= cutsq) { + jh = j; + if (history && rsq < (radsum * radsum)) + jh = jh ^ mask_history; + + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i], nspecial[i], tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], onemols [imol]->nspecial[iatom], + tag[j] - tagprev); + else + which = 0; + if (which == 0) + neighptr[n++] = jh; + else if (domain->minimum_image_check(delx, dely, delz)) + neighptr[n++] = jh; + else if (which > 0) + neighptr[n++] = jh ^ (which << SBBITS); + } else + neighptr[n++] = jh; + } } } else { - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i], nspecial[i], tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], onemols[imol]->nspecial[iatom], - tag[j] - tagprev); - else - which = 0; - if (which == 0) + if (ATOMONLY) { + if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; + } else { + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i], nspecial[i], tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], onemols [imol]->nspecial[iatom], + tag[j] - tagprev); + else + which = 0; + if (which == 0) + neighptr[n++] = j; + else if (domain->minimum_image_check(delx, dely, delz)) + neighptr[n++] = j; + else if (which > 0) + neighptr[n++] = j ^ (which << SBBITS); + } else neighptr[n++] = j; - else if (domain->minimum_image_check(delx, dely, delz)) - neighptr[n++] = j; - else if (which > 0) - neighptr[n++] = j ^ (which << SBBITS); - } else - neighptr[n++] = j; + } } } } @@ -234,12 +251,20 @@ void NPairBin::build(NeighList *list) } namespace LAMMPS_NS { -template class NPairBin<0,1,0,0>; -template class NPairBin<1,0,0,0>; -template class NPairBin<1,1,0,0>; -template class NPairBin<1,1,1,0>; -template class NPairBin<0,1,0,1>; -template class NPairBin<1,0,0,1>; -template class NPairBin<1,1,0,1>; -template class NPairBin<1,1,1,1>; +template class NPairBin<0,1,0,0,0>; +template class NPairBin<1,0,0,0,0>; +template class NPairBin<1,1,0,0,0>; +template class NPairBin<1,1,1,0,0>; +template class NPairBin<0,1,0,1,0>; +template class NPairBin<1,0,0,1,0>; +template class NPairBin<1,1,0,1,0>; +template class NPairBin<1,1,1,1,0>; +template class NPairBin<0,1,0,0,1>; +template class NPairBin<1,0,0,0,1>; +template class NPairBin<1,1,0,0,1>; +template class NPairBin<1,1,1,0,1>; +template class NPairBin<0,1,0,1,1>; +template class NPairBin<1,0,0,1,1>; +template class NPairBin<1,1,0,1,1>; +template class NPairBin<1,1,1,1,1>; } diff --git a/src/npair_bin.h b/src/npair_bin.h index e9cd65fa34..2cec108e46 100644 --- a/src/npair_bin.h +++ b/src/npair_bin.h @@ -13,47 +13,83 @@ #ifdef NPAIR_CLASS // clang-format off -typedef NPairBin<0, 1, 0, 0> NPairFullBin; +typedef NPairBin<0, 1, 0, 0, 0> NPairFullBin; NPairStyle(full/bin, NPairFullBin, NP_FULL | NP_BIN | NP_MOLONLY | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairBin<1, 0, 0, 0> NPairHalfBinNewtoff; +typedef NPairBin<1, 0, 0, 0, 0> NPairHalfBinNewtoff; NPairStyle(half/bin/newtoff, NPairHalfBinNewtoff, NP_HALF | NP_BIN | NP_MOLONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairBin<1, 1, 0, 0> NPairHalfBinNewton; +typedef NPairBin<1, 1, 0, 0, 0> NPairHalfBinNewton; NPairStyle(half/bin/newton, NPairHalfBinNewton, NP_HALF | NP_BIN | NP_MOLONLY | NP_NEWTON | NP_ORTHO); -typedef NPairBin<1, 1, 1, 0> NPairHalfBinNewtonTri; +typedef NPairBin<1, 1, 1, 0, 0> NPairHalfBinNewtonTri; NPairStyle(half/bin/newton/tri, NPairHalfBinNewtonTri, NP_HALF | NP_BIN | NP_MOLONLY | NP_NEWTON | NP_TRI); -typedef NPairBin<0, 1, 0, 1> NPairFullSizeBin; +typedef NPairBin<0, 1, 0, 1, 0> NPairFullSizeBin; NPairStyle(full/size/bin, NPairFullSizeBin, NP_FULL | NP_SIZE | NP_BIN | NP_MOLONLY | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairBin<1, 0, 0, 1> NPairHalfSizeBinNewtoff; +typedef NPairBin<1, 0, 0, 1, 0> NPairHalfSizeBinNewtoff; NPairStyle(half/size/bin/newtoff, NPairHalfSizeBinNewtoff, NP_HALF | NP_SIZE | NP_BIN | NP_MOLONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairBin<1, 1, 0, 1> NPairHalfSizeBinNewton; +typedef NPairBin<1, 1, 0, 1, 0> NPairHalfSizeBinNewton; NPairStyle(half/size/bin/newton, NPairHalfSizeBinNewton, NP_HALF | NP_SIZE | NP_BIN | NP_MOLONLY | NP_NEWTON | NP_ORTHO); -typedef NPairBin<1, 1, 1, 1> NPairHalfSizeBinNewtonTri; +typedef NPairBin<1, 1, 1, 1, 0> NPairHalfSizeBinNewtonTri; NPairStyle(half/size/bin/newton/tri, NPairHalfSizeBinNewtonTri, NP_HALF | NP_SIZE | NP_BIN | NP_MOLONLY | NP_NEWTON | NP_TRI); + +typedef NPairBin<1, 0, 0, 0, 1> NPairHalfBinAtomonlyNewtoff; +NPairStyle(half/bin/atomonly/newtoff, + NPairHalfBinAtomonlyNewtoff, + NP_HALF | NP_BIN | NP_ATOMONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBin<1, 1, 0, 0, 1> NPairHalfBinAtomonlyNewton; +NPairStyle(half/bin/atomonly/newton, + NPairHalfBinAtomonlyNewton, + NP_HALF | NP_BIN | NP_ATOMONLY | NP_NEWTON | NP_ORTHO); + +typedef NPairBin<1, 1, 1, 0, 1> NPairHalfBinAtomonlyNewtonTri; +NPairStyle(half/bin/atomonly/newton/tri, + NPairHalfBinAtomonlyNewtonTri, + NP_HALF | NP_BIN | NP_ATOMONLY | NP_NEWTON | NP_TRI); + +typedef NPairBin<0, 1, 0, 1, 1> NPairFullSizeBinAtomonly; +NPairStyle(full/size/bin/atomonly, + NPairFullSizeBinAtomonly, + NP_FULL | NP_SIZE | NP_BIN | NP_ATOMONLY | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBin<1, 0, 0, 1, 1> NPairHalfSizeBinAtomonlyNewtoff; +NPairStyle(half/size/bin/atomonly/newtoff, + NPairHalfSizeBinAtomonlyNewtoff, + NP_HALF | NP_SIZE | NP_BIN | NP_ATOMONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairBin<1, 1, 0, 1, 1> NPairHalfSizeBinAtomonlyNewton; +NPairStyle(half/size/bin/atomonly/newton, + NPairHalfSizeBinAtomonlyNewton, + NP_HALF | NP_SIZE | NP_BIN | NP_ATOMONLY | NP_NEWTON | NP_ORTHO); + +typedef NPairBin<1, 1, 1, 1, 1> NPairHalfSizeBinAtomonlyNewtonTri; +NPairStyle(half/size/bin/atomonly/newton/tri, + NPairHalfSizeBinAtomonlyNewtonTri, + NP_HALF | NP_SIZE | NP_BIN | NP_ATOMONLY | NP_NEWTON | NP_TRI); // clang-format on #else @@ -64,7 +100,7 @@ NPairStyle(half/size/bin/newton/tri, namespace LAMMPS_NS { -template +template class NPairBin : public NPair { public: NPairBin(class LAMMPS *); diff --git a/src/npair_bin_atomonly.cpp b/src/npair_bin_atomonly.cpp deleted file mode 100644 index 6cdd2e31ba..0000000000 --- a/src/npair_bin_atomonly.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* ---------------------------------------------------------------------- - 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 "npair_bin_atomonly.h" - -#include "atom.h" -#include "error.h" -#include "neighbor.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; -using namespace NeighConst; - -/* ---------------------------------------------------------------------- */ - -template -NPairBinAtomonly::NPairBinAtomonly(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - Full: - binned neighbor list construction for all neighbors - every neighbor pair appears in list of both atoms i and j - Half + Newtoff: - binned neighbor list construction with partial Newton's 3rd law - each owned atom i checks own bin and other bins in stencil - pair stored once if i,j are both owned and i < j - pair stored by me if j is ghost (also stored by proc owning j) - Half + Newton: - binned neighbor list construction with full Newton's 3rd law - each owned atom i checks its own bin and other bins in Newton stencil - every pair stored exactly once by some processor -------------------------------------------------------------------------- */ - -template -void NPairBinAtomonly::build(NeighList *list) -{ - int i, j, jh, k, n, itype, jtype, ibin, bin_start; - double xtmp, ytmp, ztmp, delx, dely, delz, rsq; - double radsum, cut, cutsq; - int *neighptr; - - double **x = atom->x; - double *radius = atom->radius; - int *type = atom->type; - int *mask = atom->mask; - tagint *molecule = atom->molecule; - int nlocal = atom->nlocal; - if (includegroup) nlocal = atom->nfirst; - - int history = list->history; - int mask_history = 1 << HISTBITS; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int inum = 0; - ipage->reset(); - - for (i = 0; i < nlocal; i++) { - n = 0; - neighptr = ipage->vget(); - - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - - ibin = atom2bin[i]; - - for (k = 0; k < nstencil; k++) { - bin_start = binhead[ibin + stencil[k]]; - if (stencil[k] == 0) { - if (HALF && NEWTON && (!TRI)) { - // Half neighbor list, newton on, orthonormal - // loop over rest of atoms in i's bin, ghosts are at end of linked list - bin_start = bins[i]; - } - } - - for (j = bin_start; j >= 0; j = bins[j]) { - if (!HALF) { - // Full neighbor list - // only skip i = j - if (i == j) continue; - } else if (!NEWTON) { - // Half neighbor list, newton off - // only store pair if i < j - // stores own/own pairs only once - // stores own/ghost pairs on both procs - if (j <= i) continue; - } else if (TRI) { - // Half neighbor list, newton on, triclinic - // pairs for atoms j "below" i are excluded - // below = lower z or (equal z and lower y) or (equal zy and lower x) - // (equal zyx and j <= i) - // latter excludes self-self interaction but allows superposed atoms - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp) { - if (x[j][0] < xtmp) continue; - if (x[j][0] == xtmp && j <= i) continue; - } - } - } else { - // Half neighbor list, newton on, orthonormal - // store every pair for every bin in stencil, except for i's bin - - if (stencil[k] == 0) { - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the "right" of i - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } - } - } - } - - jtype = type[j]; - if (exclude && exclusion(i, j, itype, jtype, mask, molecule)) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx * delx + dely * dely + delz * delz; - - if (SIZE) { - radsum = radius[i] + radius[j]; - cut = radsum + skin; - cutsq = cut * cut; - - if (rsq <= cutsq) { - jh = j; - if (history && rsq < (radsum * radsum)) - jh = jh ^ mask_history; - neighptr[n++] = jh; - } - } else { - if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; - } - } - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; - if (!HALF) list->gnum = 0; -} - -namespace LAMMPS_NS { -template class NPairBinAtomonly<0,1,0,0>; -template class NPairBinAtomonly<1,0,0,0>; -template class NPairBinAtomonly<1,1,0,0>; -template class NPairBinAtomonly<1,1,1,0>; -template class NPairBinAtomonly<0,1,0,1>; -template class NPairBinAtomonly<1,0,0,1>; -template class NPairBinAtomonly<1,1,0,1>; -template class NPairBinAtomonly<1,1,1,1>; -} diff --git a/src/npair_bin_atomonly.h b/src/npair_bin_atomonly.h deleted file mode 100644 index febe7c2f0e..0000000000 --- a/src/npair_bin_atomonly.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -typedef NPairBinAtomonly<0, 1, 0, 0> NPairFullBinAtomonly; -NPairStyle(full/bin/atomonly, - NPairFullBinAtomonly, - NP_FULL | NP_BIN | NP_ATOMONLY | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); - -typedef NPairBinAtomonly<1, 0, 0, 0> NPairHalfBinAtomonlyNewtoff; -NPairStyle(half/bin/atomonly/newtoff, - NPairHalfBinAtomonlyNewtoff, - NP_HALF | NP_BIN | NP_ATOMONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); - -typedef NPairBinAtomonly<1, 1, 0, 0> NPairHalfBinAtomonlyNewton; -NPairStyle(half/bin/atomonly/newton, - NPairHalfBinAtomonlyNewton, - NP_HALF | NP_BIN | NP_ATOMONLY | NP_NEWTON | NP_ORTHO); - -typedef NPairBinAtomonly<1, 1, 1, 0> NPairHalfBinAtomonlyNewtonTri; -NPairStyle(half/bin/atomonly/newton/tri, - NPairHalfBinAtomonlyNewtonTri, - NP_HALF | NP_BIN | NP_ATOMONLY | NP_NEWTON | NP_TRI); - -typedef NPairBinAtomonly<0, 1, 0, 1> NPairFullSizeBinAtomonly; -NPairStyle(full/size/bin/atomonly, - NPairFullSizeBinAtomonly, - NP_FULL | NP_SIZE | NP_BIN | NP_ATOMONLY | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); - -typedef NPairBinAtomonly<1, 0, 0, 1> NPairHalfSizeBinAtomonlyNewtoff; -NPairStyle(half/size/bin/atomonly/newtoff, - NPairHalfSizeBinAtomonlyNewtoff, - NP_HALF | NP_SIZE | NP_BIN | NP_ATOMONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); - -typedef NPairBinAtomonly<1, 1, 0, 1> NPairHalfSizeBinAtomonlyNewton; -NPairStyle(half/size/bin/atomonly/newton, - NPairHalfSizeBinAtomonlyNewton, - NP_HALF | NP_SIZE | NP_BIN | NP_ATOMONLY | NP_NEWTON | NP_ORTHO); - -typedef NPairBinAtomonly<1, 1, 1, 1> NPairHalfSizeBinAtomonlyNewtonTri; -NPairStyle(half/size/bin/atomonly/newton/tri, - NPairHalfSizeBinAtomonlyNewtonTri, - NP_HALF | NP_SIZE | NP_BIN | NP_ATOMONLY | NP_NEWTON | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_BIN_ATOMONLY_H -#define LMP_NPAIR_BIN_ATOMONLY_H - -#include "npair.h" - -namespace LAMMPS_NS { - -template -class NPairBinAtomonly : public NPair { - public: - NPairBinAtomonly(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/npair_multi.cpp b/src/npair_multi.cpp index 7cfb3b836b..37c6482a6a 100644 --- a/src/npair_multi.cpp +++ b/src/npair_multi.cpp @@ -29,8 +29,8 @@ using namespace NeighConst; /* ---------------------------------------------------------------------- */ -template -NPairMulti::NPairMulti(LAMMPS *lmp) : NPair(lmp) {} +template +NPairMulti::NPairMulti(LAMMPS *lmp) : NPair(lmp) {} /* ---------------------------------------------------------------------- multi stencil is icollection-jcollection dependent @@ -48,8 +48,8 @@ NPairMulti::NPairMulti(LAMMPS *lmp) : NPair(lmp) {} every pair stored exactly once by some processor ------------------------------------------------------------------------- */ -template -void NPairMulti::build(NeighList *list) +template +void NPairMulti::build(NeighList *list) { int i, j, jh, js, k, n, itype, jtype, ibin, jbin, icollection, jcollection, which, ns, imol, iatom, moltemplate; tagint itag, jtag, tagprev; @@ -73,10 +73,12 @@ void NPairMulti::build(NeighList *list) int *molindex = atom->molindex; int *molatom = atom->molatom; Molecule **onemols = atom->avec->onemols; - if (molecular == Atom::TEMPLATE) - moltemplate = 1; - else - moltemplate = 0; + if (!ATOMONLY) { + if (molecular == Atom::TEMPLATE) + moltemplate = 1; + else + moltemplate = 0; + } int history = list->history; int mask_history = 1 << HISTBITS; @@ -99,10 +101,12 @@ void NPairMulti::build(NeighList *list) xtmp = x[i][0]; ytmp = x[i][1]; ztmp = x[i][2]; - if (moltemplate) { - imol = molindex[i]; - iatom = molatom[i]; - tagprev = tag[i] - iatom - 1; + if (!ATOMONLY) { + if (moltemplate) { + imol = molindex[i]; + iatom = molatom[i]; + tagprev = tag[i] - iatom - 1; + } } ibin = atom2bin[i]; @@ -209,46 +213,59 @@ void NPairMulti::build(NeighList *list) cut = radsum + skin; cutsq = cut * cut; - if (rsq <= cutsq) { - jh = j; - if (history && rsq < radsum * radsum) - jh = jh ^ mask_history; - - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i], nspecial[i], tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], onemols[imol]->nspecial[iatom], - tag[j] - tagprev); - else - which = 0; - if (which == 0) - neighptr[n++] = jh; - else if (domain->minimum_image_check(delx, dely, delz)) - neighptr[n++] = jh; - else if (which > 0) - neighptr[n++] = jh ^ (which << SBBITS); - } else + if (ATOMONLY) { + if (rsq <= cutsq) { + jh = j; + if (history && rsq < (radsum * radsum)) + jh = jh ^ mask_history; neighptr[n++] = jh; + } + } else { + if (rsq <= cutsq) { + jh = j; + if (history && rsq < radsum * radsum) + jh = jh ^ mask_history; + + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i], nspecial[i], tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], onemols [imol]->nspecial[iatom], + tag[j] - tagprev); + else + which = 0; + if (which == 0) + neighptr[n++] = jh; + else if (domain->minimum_image_check(delx, dely, delz)) + neighptr[n++] = jh; + else if (which > 0) + neighptr[n++] = jh ^ (which << SBBITS); + } else + neighptr[n++] = jh; + } } } else { - if (rsq <= cutneighsq[itype][jtype]) { - if (molecular != Atom::ATOMIC) { - if (!moltemplate) - which = find_special(special[i], nspecial[i], tag[j]); - else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], onemols[imol]->nspecial[iatom], - tag[j] - tagprev); - else - which = 0; - if (which == 0) + if (ATOMONLY) { + if (rsq <= cutneighsq[itype][jtype]) neighptr[n++] = j; + } else { + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular != Atom::ATOMIC) { + if (!moltemplate) + which = find_special(special[i], nspecial[i], tag[j]); + else if (imol >= 0) + which = find_special(onemols[imol]->special[iatom], onemols [imol]->nspecial[iatom], + tag[j] - tagprev); + else + which = 0; + if (which == 0) + neighptr[n++] = j; + else if (domain->minimum_image_check(delx, dely, delz)) + neighptr[n++] = j; + else if (which > 0) + neighptr[n++] = j ^ (which << SBBITS); + } else neighptr[n++] = j; - else if (domain->minimum_image_check(delx, dely, delz)) - neighptr[n++] = j; - else if (which > 0) - neighptr[n++] = j ^ (which << SBBITS); - } else - neighptr[n++] = j; + } } } } @@ -267,12 +284,20 @@ void NPairMulti::build(NeighList *list) } namespace LAMMPS_NS { -template class NPairMulti<0,1,0,0>; -template class NPairMulti<1,0,0,0>; -template class NPairMulti<1,1,0,0>; -template class NPairMulti<1,1,1,0>; -template class NPairMulti<0,1,0,1>; -template class NPairMulti<1,0,0,1>; -template class NPairMulti<1,1,0,1>; -template class NPairMulti<1,1,1,1>; +template class NPairMulti<0,1,0,0,0>; +template class NPairMulti<1,0,0,0,0>; +template class NPairMulti<1,1,0,0,0>; +template class NPairMulti<1,1,1,0,0>; +template class NPairMulti<0,1,0,1,0>; +template class NPairMulti<1,0,0,1,0>; +template class NPairMulti<1,1,0,1,0>; +template class NPairMulti<1,1,1,1,0>; +template class NPairMulti<0,1,0,0,1>; +template class NPairMulti<1,0,0,0,1>; +template class NPairMulti<1,1,0,0,1>; +template class NPairMulti<1,1,1,0,1>; +template class NPairMulti<0,1,0,1,1>; +template class NPairMulti<1,0,0,1,1>; +template class NPairMulti<1,1,0,1,1>; +template class NPairMulti<1,1,1,1,1>; } diff --git a/src/npair_multi.h b/src/npair_multi.h index f6a68711e2..a82352f840 100644 --- a/src/npair_multi.h +++ b/src/npair_multi.h @@ -13,47 +13,89 @@ #ifdef NPAIR_CLASS // clang-format off -typedef NPairMulti<0, 1, 0, 0> NPairFullMulti; +typedef NPairMulti<0, 1, 0, 0, 0> NPairFullMulti; NPairStyle(full/multi, NPairFullMulti, - NP_FULL | NP_MULTI | + NP_FULL | NP_MULTI | NP_MOLONLY | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairMulti<1, 0, 0, 0> NPairHalfMultiNewtoff; +typedef NPairMulti<1, 0, 0, 0, 0> NPairHalfMultiNewtoff; NPairStyle(half/multi/newtoff, NPairHalfMultiNewtoff, - NP_HALF | NP_MULTI | NP_NEWTOFF | NP_ORTHO | NP_TRI); + NP_HALF | NP_MULTI | NP_MOLONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairMulti<1, 1, 0, 0> NPairHalfMultiNewton; +typedef NPairMulti<1, 1, 0, 0, 0> NPairHalfMultiNewton; NPairStyle(half/multi/newton, NPairHalfMultiNewton, - NP_HALF | NP_MULTI | NP_NEWTON | NP_ORTHO); + NP_HALF | NP_MULTI | NP_MOLONLY | NP_NEWTON | NP_ORTHO); -typedef NPairMulti<1, 1, 1, 0> NPairHalfMultiNewtonTri; +typedef NPairMulti<1, 1, 1, 0, 0> NPairHalfMultiNewtonTri; NPairStyle(half/multi/newton/tri, NPairHalfMultiNewtonTri, - NP_HALF | NP_MULTI | NP_NEWTON | NP_TRI); + NP_HALF | NP_MULTI | NP_MOLONLY | NP_NEWTON | NP_TRI); -typedef NPairMulti<0, 1, 0, 1> NPairFullSizeMulti; +typedef NPairMulti<0, 1, 0, 1, 0> NPairFullSizeMulti; NPairStyle(full/size/multi, NPairFullSizeMulti, - NP_FULL | NP_SIZE | NP_MULTI | + NP_FULL | NP_SIZE | NP_MULTI | NP_MOLONLY | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairMulti<1, 0, 0, 1> NPairHalfSizeMultiNewtoff; +typedef NPairMulti<1, 0, 0, 1, 0> NPairHalfSizeMultiNewtoff; NPairStyle(half/size/multi/newtoff, NPairHalfSizeMultiNewtoff, - NP_HALF | NP_SIZE | NP_MULTI | NP_NEWTOFF | NP_ORTHO | NP_TRI); + NP_HALF | NP_SIZE | NP_MULTI | NP_MOLONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairMulti<1, 1, 0, 1> NPairHalfSizeMultiNewton; +typedef NPairMulti<1, 1, 0, 1, 0> NPairHalfSizeMultiNewton; NPairStyle(half/size/multi/newton, NPairHalfSizeMultiNewton, - NP_HALF | NP_SIZE | NP_MULTI | NP_NEWTON | NP_ORTHO); + NP_HALF | NP_SIZE | NP_MULTI | NP_MOLONLY | NP_NEWTON | NP_ORTHO); -typedef NPairMulti<1, 1, 1, 1> NPairHalfSizeMultiNewtonTri; +typedef NPairMulti<1, 1, 1, 1, 0> NPairHalfSizeMultiNewtonTri; NPairStyle(half/size/multi/newton/tri, NPairHalfSizeMultiNewtonTri, - NP_HALF | NP_SIZE | NP_MULTI | NP_NEWTON | NP_TRI); + NP_HALF | NP_SIZE | NP_MULTI | NP_MOLONLY | NP_NEWTON | NP_TRI); + +typedef NPairMulti<0, 1, 0, 0, 1> NPairFullMultiAtomonly; +NPairStyle(full/multi/atomonly, + NPairFullMultiAtomonly, + NP_FULL | NP_MULTI | NP_ATOMONLY | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMulti<1, 0, 0, 0, 1> NPairHalfMultiAtomonlyNewtoff; +NPairStyle(half/multi/atomonly/newtoff, + NPairHalfMultiAtomonlyNewtoff, + NP_HALF | NP_MULTI | NP_ATOMONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMulti<1, 1, 0, 0, 1> NPairHalfMultiAtomonlyNewton; +NPairStyle(half/multi/atomonly/newton, + NPairHalfMultiAtomonlyNewton, + NP_HALF | NP_MULTI | NP_ATOMONLY | NP_NEWTON | NP_ORTHO); + +typedef NPairMulti<1, 1, 1, 0, 1> NPairHalfMultiAtomonlyNewtonTri; +NPairStyle(half/multi/atomonly/newton/tri, + NPairHalfMultiAtomonlyNewtonTri, + NP_HALF | NP_MULTI | NP_ATOMONLY | NP_NEWTON | NP_TRI); + +typedef NPairMulti<0, 1, 0, 1, 1> NPairFullSizeMultiAtomonly; +NPairStyle(full/size/multi/atomonly, + NPairFullSizeMultiAtomonly, + NP_FULL | NP_SIZE | NP_MULTI | NP_ATOMONLY | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMulti<1, 0, 0, 1, 1> NPairHalfSizeMultiAtomonlyNewtoff; +NPairStyle(half/size/multi/atomonly/newtoff, + NPairHalfSizeMultiAtomonlyNewtoff, + NP_HALF | NP_SIZE | NP_MULTI | NP_ATOMONLY | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairMulti<1, 1, 0, 1, 1> NPairHalfSizeMultiAtomonlyNewton; +NPairStyle(half/size/multi/atomonly/newton, + NPairHalfSizeMultiAtomonlyNewton, + NP_HALF | NP_SIZE | NP_MULTI | NP_ATOMONLY | NP_NEWTON | NP_ORTHO); + +typedef NPairMulti<1, 1, 1, 1, 1> NPairHalfSizeMultiAtomonlyNewtonTri; +NPairStyle(half/size/multi/atomonly/newton/tri, + NPairHalfSizeMultiAtomonlyNewtonTri, + NP_HALF | NP_SIZE | NP_MULTI | NP_ATOMONLY | NP_NEWTON | NP_TRI); // clang-format on #else @@ -64,7 +106,7 @@ NPairStyle(half/size/multi/newton/tri, namespace LAMMPS_NS { -template +template class NPairMulti : public NPair { public: NPairMulti(class LAMMPS *); diff --git a/src/npair_multi_old.cpp b/src/npair_multi_old.cpp index 1889061093..fa39182018 100644 --- a/src/npair_multi_old.cpp +++ b/src/npair_multi_old.cpp @@ -111,9 +111,9 @@ void NPairMultiOld::build(NeighList *list) cutnsq = cutneighsq[itype]; ns = nstencil_multi_old[itype]; for (k = 0; k < ns; k++) { - bin_start = binhead[ibin+stencil[k]]; - if (stencil[k] == 0) { - if (HALF && NEWTON && (!TRI)) { + bin_start = binhead[ibin + s[k]]; + if (HALF && NEWTON && (!TRI)) { + if (s[k] == 0) { // Half neighbor list, newton on, orthonormal // loop over rest of atoms in i's bin, ghosts are at end of linked list bin_start = bins[i]; diff --git a/src/npair_respa_bin.cpp b/src/npair_respa_bin.cpp index bcbecc05f1..085986903d 100644 --- a/src/npair_respa_bin.cpp +++ b/src/npair_respa_bin.cpp @@ -122,8 +122,8 @@ void NPairRespaBin::build(NeighList *list) for (k = 0; k < nstencil; k++) { bin_start = binhead[ibin+stencil[k]]; - if (stencil[k] == 0) { - if (NEWTON && (!TRI)) { + if (NEWTON && (!TRI)) { + if (stencil[k] == 0) { // Half neighbor list, newton on, orthonormal // loop over rest of atoms in i's bin, ghosts are at end of linked list bin_start = bins[i]; diff --git a/src/nstencil_multi_old.cpp b/src/nstencil_multi_old.cpp index a6b8f14263..6d34d8881f 100644 --- a/src/nstencil_multi_old.cpp +++ b/src/nstencil_multi_old.cpp @@ -69,7 +69,7 @@ void NStencilMultiOld::create() if (HALF && DIM_3D && (!TRI)) if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; - rsq = bin_distance(i, j, 0); + rsq = bin_distance(i, j, k); if (rsq < typesq) { distsq[n] = rsq; s[n++] = k * mbiny * mbinx + j * mbinx + i; From 8054d2807a3926cd95f1a37d9140f0c75cf84ccd Mon Sep 17 00:00:00 2001 From: jtclemm Date: Mon, 23 Oct 2023 16:54:56 -0600 Subject: [PATCH 062/189] Adding full atomonly bin npair style --- src/OPENMP/npair_multi_old_omp.cpp | 2 +- src/npair_bin.h | 6 ++++++ src/npair_multi_old.cpp | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/OPENMP/npair_multi_old_omp.cpp b/src/OPENMP/npair_multi_old_omp.cpp index 894ff9b987..08bc54a72b 100644 --- a/src/OPENMP/npair_multi_old_omp.cpp +++ b/src/OPENMP/npair_multi_old_omp.cpp @@ -166,7 +166,7 @@ void NPairMultiOldOmp::build(NeighList *list) // Half neighbor list, newton on, orthonormal // store every pair for every bin in stencil,except for i's bin - if (stencil[k] == 0) { + if (s[k] == 0) { // if j is owned atom, store it, since j is beyond i in linked list // if j is ghost, only store if j coords are "above and to the "right" of i if (j >= nlocal) { diff --git a/src/npair_bin.h b/src/npair_bin.h index 2cec108e46..94b7c7077e 100644 --- a/src/npair_bin.h +++ b/src/npair_bin.h @@ -55,6 +55,12 @@ NPairStyle(half/size/bin/newton/tri, NPairHalfSizeBinNewtonTri, NP_HALF | NP_SIZE | NP_BIN | NP_MOLONLY | NP_NEWTON | NP_TRI); +typedef NPairBin<0, 1, 0, 0, 1> NPairFullBinAtomonly; +NPairStyle(full/bin/atomonly, + NPairFullBinAtomonly, + NP_FULL | NP_BIN | NP_ATOMONLY | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + typedef NPairBin<1, 0, 0, 0, 1> NPairHalfBinAtomonlyNewtoff; NPairStyle(half/bin/atomonly/newtoff, NPairHalfBinAtomonlyNewtoff, diff --git a/src/npair_multi_old.cpp b/src/npair_multi_old.cpp index fa39182018..b2d77227cc 100644 --- a/src/npair_multi_old.cpp +++ b/src/npair_multi_old.cpp @@ -159,7 +159,7 @@ void NPairMultiOld::build(NeighList *list) // Half neighbor list, newton on, orthonormal // store every pair for every bin in stencil,except for i's bin - if (stencil[k] == 0) { + if (s[k] == 0) { // if j is owned atom, store it, since j is beyond i in linked list // if j is ghost, only store if j coords are "above and to the "right" of i if (j >= nlocal) { From 0f94e6030f1f895ebb15cba5faf197edcb63a9d6 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Tue, 24 Oct 2023 12:26:37 -0600 Subject: [PATCH 063/189] Adding separate ghost stencil for intel --- src/INTEL/nstencil_ghost_bin_intel.cpp | 76 ++++++++++++++++++++++++++ src/INTEL/nstencil_ghost_bin_intel.h | 65 ++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 src/INTEL/nstencil_ghost_bin_intel.cpp create mode 100644 src/INTEL/nstencil_ghost_bin_intel.h diff --git a/src/INTEL/nstencil_ghost_bin_intel.cpp b/src/INTEL/nstencil_ghost_bin_intel.cpp new file mode 100644 index 0000000000..cd8203eb64 --- /dev/null +++ b/src/INTEL/nstencil_ghost_bin_intel.cpp @@ -0,0 +1,76 @@ +/* ---------------------------------------------------------------------- + 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 "nstencil_ghost_bin_intel.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +template +NStencilGhostBinIntel::NStencilGhostBinIntel(LAMMPS *lmp) : NStencil(lmp) +{ + xyzflag = 1; +} + +/* ---------------------------------------------------------------------- + create stencil based on bin geometry and cutoff +------------------------------------------------------------------------- */ + +template +void NStencilGhostBinIntel::create() +{ + int i, j, k; + + // For half stencils, only the upper plane is needed + int sy_min = sy; + int sz_min = sz; + if (HALF && (!DIM_3D)) sy_min = 0; + if (HALF && DIM_3D) sz_min = 0; + + nstencil = 0; + + // For Intel, half and ortho stencils do not include central bin + // as, historically, this was never included in a stencil. + // Non-Intel npair classes were updated to account for this change, + // but the Intel npair classes have not yet been updated + // if (HALF && (!TRI)) stencil[nstencil++] = 0; + + for (k = -sz_min; k <= sz; k++) { + for (j = -sy_min; j <= sy; j++) { + for (i = -sx; i <= sx; i++) { + + // Now only include "upper right" bins for half and ortho stencils + if (HALF && (!DIM_3D) && (!TRI)) + if (! (j > 0 || (j == 0 && i > 0))) continue; + if (HALF && DIM_3D && (!TRI)) + if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; + + if (bin_distance(i,j,k) < cutneighmaxsq) { + stencilxyz[nstencil][0] = i; + stencilxyz[nstencil][1] = j; + stencilxyz[nstencil][2] = k; + } + } + } + } +} + +namespace LAMMPS_NS { +template class NStencilGhostBinIntel<0,0,0>; +template class NStencilGhostBinIntel<0,1,0>; +template class NStencilGhostBinIntel<1,0,0>; +template class NStencilGhostBinIntel<1,0,1>; +template class NStencilGhostBinIntel<1,1,0>; +template class NStencilGhostBinIntel<1,1,1>; +} diff --git a/src/INTEL/nstencil_ghost_bin_intel.h b/src/INTEL/nstencil_ghost_bin_intel.h new file mode 100644 index 0000000000..c76303309f --- /dev/null +++ b/src/INTEL/nstencil_ghost_bin_intel.h @@ -0,0 +1,65 @@ +/* -*- 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 NSTENCIL_CLASS +// clang-format off +typedef NStencilGhostBinIntel<0, 0, 0> NStencilFullGhostBin2dIntel; +NStencilStyle(full/ghost/bin/2d/intel, + NStencilFullGhostBin2dIntel, + NS_FULL | NS_GHOST | NS_BIN | NS_2D | NS_ORTHO | NS_TRI | NS_INTEL); + +typedef NStencilGhostBinIntel<0, 1, 0> NStencilFullGhostBin3dIntel; +NStencilStyle(full/ghost/bin/3d/intel, + NStencilFullGhostBin3dIntel, + NS_FULL | NS_GHOST | NS_BIN | NS_3D | NS_ORTHO | NS_TRI | NS_INTEL); + +typedef NStencilGhostBinIntel<1, 0, 0> NStencilHalfGhostBin2dIntel; +NStencilStyle(half/ghost/bin/2d/intel, + NStencilHalfGhostBin2dIntel, + NS_HALF | NS_GHOST | NS_BIN | NS_2D | NS_ORTHO | NS_INTEL); + +typedef NStencilGhostBinIntel<1, 0, 1> NStencilHalfGhostBin2dTriIntel; +NStencilStyle(half/ghost/bin/2d/tri/intel, + NStencilHalfGhostBin2dTriIntel, + NS_HALF | NS_GHOST | NS_BIN | NS_2D | NS_TRI | NS_INTEL); + +typedef NStencilGhostBinIntel<1, 1, 0> NStencilHalfGhostBin3dIntel; +NStencilStyle(half/ghost/bin/3d/intel, + NStencilHalfGhostBin3dIntel, + NS_HALF | NS_GHOST | NS_BIN | NS_3D | NS_ORTHO | NS_INTEL); + +typedef NStencilGhostBinIntel<1, 1, 1> NStencilHalfGhostBin3dTriIntel; +NStencilStyle(half/ghost/bin/3d/tri/intel, + NStencilHalfGhostBin3dTriIntel, + NS_HALF | NS_GHOST | NS_BIN | NS_3D | NS_TRI | NS_INTEL); +// clang-format on +#else + +#ifndef LMP_NSTENCIL_GHOST_BIN_INTEL_H +#define LMP_NSTENCIL_GHOST_BIN_INTEL_H + +#include "nstencil.h" + +namespace LAMMPS_NS { + +template +class NStencilGhostBinIntel : public NStencil { + public: + NStencilGhostBinIntel(class LAMMPS *); + void create() override; +}; + +} // namespace LAMMPS_NS + +#endif +#endif From 8f14cdcb3473b16ed17eb2d2bc0d9ad0d465c9eb Mon Sep 17 00:00:00 2001 From: jtclemm Date: Tue, 24 Oct 2023 13:55:43 -0600 Subject: [PATCH 064/189] Cleaning up ghost stencils --- src/INTEL/nstencil_bin_intel.cpp | 2 +- src/INTEL/nstencil_ghost_bin_intel.cpp | 42 ++++++-------------------- src/INTEL/nstencil_ghost_bin_intel.h | 26 ++-------------- src/nstencil_bin.cpp | 2 +- src/nstencil_ghost_bin.cpp | 40 ++++++------------------ src/nstencil_ghost_bin.h | 26 ++-------------- 6 files changed, 27 insertions(+), 111 deletions(-) diff --git a/src/INTEL/nstencil_bin_intel.cpp b/src/INTEL/nstencil_bin_intel.cpp index 4b7dc31f46..426fcec33c 100644 --- a/src/INTEL/nstencil_bin_intel.cpp +++ b/src/INTEL/nstencil_bin_intel.cpp @@ -53,7 +53,7 @@ void NStencilBinIntel::create() if (HALF && DIM_3D && (!TRI)) if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; - if (bin_distance(i,j,k) < cutneighmaxsq) + if (bin_distance(i, j, k) < cutneighmaxsq) stencil[nstencil++] = k * mbiny * mbinx + j * mbinx + i; } } diff --git a/src/INTEL/nstencil_ghost_bin_intel.cpp b/src/INTEL/nstencil_ghost_bin_intel.cpp index cd8203eb64..23d4930cc0 100644 --- a/src/INTEL/nstencil_ghost_bin_intel.cpp +++ b/src/INTEL/nstencil_ghost_bin_intel.cpp @@ -17,8 +17,8 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -template -NStencilGhostBinIntel::NStencilGhostBinIntel(LAMMPS *lmp) : NStencil(lmp) +template +NStencilGhostBinIntel::NStencilGhostBinIntel(LAMMPS *lmp) : NStencil(lmp) { xyzflag = 1; } @@ -27,39 +27,21 @@ NStencilGhostBinIntel::NStencilGhostBinIntel(LAMMPS *lmp) : N create stencil based on bin geometry and cutoff ------------------------------------------------------------------------- */ -template -void NStencilGhostBinIntel::create() +template +void NStencilGhostBinIntel::create() { int i, j, k; - // For half stencils, only the upper plane is needed - int sy_min = sy; - int sz_min = sz; - if (HALF && (!DIM_3D)) sy_min = 0; - if (HALF && DIM_3D) sz_min = 0; - nstencil = 0; - // For Intel, half and ortho stencils do not include central bin - // as, historically, this was never included in a stencil. - // Non-Intel npair classes were updated to account for this change, - // but the Intel npair classes have not yet been updated - // if (HALF && (!TRI)) stencil[nstencil++] = 0; - - for (k = -sz_min; k <= sz; k++) { - for (j = -sy_min; j <= sy; j++) { + for (k = -sz; k <= sz; k++) { + for (j = -sy; j <= sy; j++) { for (i = -sx; i <= sx; i++) { - - // Now only include "upper right" bins for half and ortho stencils - if (HALF && (!DIM_3D) && (!TRI)) - if (! (j > 0 || (j == 0 && i > 0))) continue; - if (HALF && DIM_3D && (!TRI)) - if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; - - if (bin_distance(i,j,k) < cutneighmaxsq) { + if (bin_distance(i, j, k) < cutneighmaxsq) { stencilxyz[nstencil][0] = i; stencilxyz[nstencil][1] = j; stencilxyz[nstencil][2] = k; + stencil[nstencil++] = k * mbiny * mbinx + j * mbinx + i; } } } @@ -67,10 +49,6 @@ void NStencilGhostBinIntel::create() } namespace LAMMPS_NS { -template class NStencilGhostBinIntel<0,0,0>; -template class NStencilGhostBinIntel<0,1,0>; -template class NStencilGhostBinIntel<1,0,0>; -template class NStencilGhostBinIntel<1,0,1>; -template class NStencilGhostBinIntel<1,1,0>; -template class NStencilGhostBinIntel<1,1,1>; +template class NStencilGhostBinIntel<0>; +template class NStencilGhostBinIntel<1>; } diff --git a/src/INTEL/nstencil_ghost_bin_intel.h b/src/INTEL/nstencil_ghost_bin_intel.h index c76303309f..f4ae9f9804 100644 --- a/src/INTEL/nstencil_ghost_bin_intel.h +++ b/src/INTEL/nstencil_ghost_bin_intel.h @@ -13,35 +13,15 @@ #ifdef NSTENCIL_CLASS // clang-format off -typedef NStencilGhostBinIntel<0, 0, 0> NStencilFullGhostBin2dIntel; +typedef NStencilGhostBinIntel<0> NStencilFullGhostBin2dIntel; NStencilStyle(full/ghost/bin/2d/intel, NStencilFullGhostBin2dIntel, NS_FULL | NS_GHOST | NS_BIN | NS_2D | NS_ORTHO | NS_TRI | NS_INTEL); -typedef NStencilGhostBinIntel<0, 1, 0> NStencilFullGhostBin3dIntel; +typedef NStencilGhostBinIntel<1> NStencilFullGhostBin3dIntel; NStencilStyle(full/ghost/bin/3d/intel, NStencilFullGhostBin3dIntel, NS_FULL | NS_GHOST | NS_BIN | NS_3D | NS_ORTHO | NS_TRI | NS_INTEL); - -typedef NStencilGhostBinIntel<1, 0, 0> NStencilHalfGhostBin2dIntel; -NStencilStyle(half/ghost/bin/2d/intel, - NStencilHalfGhostBin2dIntel, - NS_HALF | NS_GHOST | NS_BIN | NS_2D | NS_ORTHO | NS_INTEL); - -typedef NStencilGhostBinIntel<1, 0, 1> NStencilHalfGhostBin2dTriIntel; -NStencilStyle(half/ghost/bin/2d/tri/intel, - NStencilHalfGhostBin2dTriIntel, - NS_HALF | NS_GHOST | NS_BIN | NS_2D | NS_TRI | NS_INTEL); - -typedef NStencilGhostBinIntel<1, 1, 0> NStencilHalfGhostBin3dIntel; -NStencilStyle(half/ghost/bin/3d/intel, - NStencilHalfGhostBin3dIntel, - NS_HALF | NS_GHOST | NS_BIN | NS_3D | NS_ORTHO | NS_INTEL); - -typedef NStencilGhostBinIntel<1, 1, 1> NStencilHalfGhostBin3dTriIntel; -NStencilStyle(half/ghost/bin/3d/tri/intel, - NStencilHalfGhostBin3dTriIntel, - NS_HALF | NS_GHOST | NS_BIN | NS_3D | NS_TRI | NS_INTEL); // clang-format on #else @@ -52,7 +32,7 @@ NStencilStyle(half/ghost/bin/3d/tri/intel, namespace LAMMPS_NS { -template +template class NStencilGhostBinIntel : public NStencil { public: NStencilGhostBinIntel(class LAMMPS *); diff --git a/src/nstencil_bin.cpp b/src/nstencil_bin.cpp index accce7c395..2b7c15cff6 100644 --- a/src/nstencil_bin.cpp +++ b/src/nstencil_bin.cpp @@ -59,7 +59,7 @@ void NStencilBin::create() if (HALF && DIM_3D && (!TRI)) if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; - if (bin_distance(i,j,k) < cutneighmaxsq) + if (bin_distance(i, j, k) < cutneighmaxsq) stencil[nstencil++] = k * mbiny * mbinx + j * mbinx + i; } } diff --git a/src/nstencil_ghost_bin.cpp b/src/nstencil_ghost_bin.cpp index b83a0ffd46..81372bedaf 100644 --- a/src/nstencil_ghost_bin.cpp +++ b/src/nstencil_ghost_bin.cpp @@ -17,8 +17,8 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -template -NStencilGhostBin::NStencilGhostBin(LAMMPS *lmp) : NStencil(lmp) +template +NStencilGhostBin::NStencilGhostBin(LAMMPS *lmp) : NStencil(lmp) { xyzflag = 1; } @@ -27,35 +27,17 @@ NStencilGhostBin::NStencilGhostBin(LAMMPS *lmp) : NStencil(lm create stencil based on bin geometry and cutoff ------------------------------------------------------------------------- */ -template -void NStencilGhostBin::create() +template +void NStencilGhostBin::create() { int i, j, k; - // For half stencils, only the upper plane is needed - int sy_min = sy; - int sz_min = sz; - if (HALF && (!DIM_3D)) sy_min = 0; - if (HALF && DIM_3D) sz_min = 0; - nstencil = 0; - // Half and ortho stencils include central bin first - // This preserves the historical order of the neighbor list - // as the old npair classes used to separately parse the central bin first - if (HALF && (!TRI)) stencil[nstencil++] = 0; - - for (k = -sz_min; k <= sz; k++) { - for (j = -sy_min; j <= sy; j++) { + for (k = -sz; k <= sz; k++) { + for (j = -sy; j <= sy; j++) { for (i = -sx; i <= sx; i++) { - - // Now only include "upper right" bins for half and ortho stencils - if (HALF && (!DIM_3D) && (!TRI)) - if (! (j > 0 || (j == 0 && i > 0))) continue; - if (HALF && DIM_3D && (!TRI)) - if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; - - if (bin_distance(i,j,k) < cutneighmaxsq) { + if (bin_distance(i, j, k) < cutneighmaxsq) { stencilxyz[nstencil][0] = i; stencilxyz[nstencil][1] = j; stencilxyz[nstencil][2] = k; @@ -67,10 +49,6 @@ void NStencilGhostBin::create() } namespace LAMMPS_NS { -template class NStencilGhostBin<0,0,0>; -template class NStencilGhostBin<0,1,0>; -template class NStencilGhostBin<1,0,0>; -template class NStencilGhostBin<1,0,1>; -template class NStencilGhostBin<1,1,0>; -template class NStencilGhostBin<1,1,1>; +template class NStencilGhostBin<0>; +template class NStencilGhostBin<1>; } diff --git a/src/nstencil_ghost_bin.h b/src/nstencil_ghost_bin.h index ccf45fc502..ed4ae21be9 100644 --- a/src/nstencil_ghost_bin.h +++ b/src/nstencil_ghost_bin.h @@ -13,35 +13,15 @@ #ifdef NSTENCIL_CLASS // clang-format off -typedef NStencilGhostBin<0, 0, 0> NStencilFullGhostBin2d; +typedef NStencilGhostBin<0> NStencilFullGhostBin2d; NStencilStyle(full/ghost/bin/2d, NStencilFullGhostBin2d, NS_FULL | NS_GHOST | NS_BIN | NS_2D | NS_ORTHO | NS_TRI); -typedef NStencilGhostBin<0, 1, 0> NStencilFullGhostBin3d; +typedef NStencilGhostBin<1> NStencilFullGhostBin3d; NStencilStyle(full/ghost/bin/3d, NStencilFullGhostBin3d, NS_FULL | NS_GHOST | NS_BIN | NS_3D | NS_ORTHO | NS_TRI); - -typedef NStencilGhostBin<1, 0, 0> NStencilHalfGhostBin2d; -NStencilStyle(half/ghost/bin/2d, - NStencilHalfGhostBin2d, - NS_HALF | NS_GHOST | NS_BIN | NS_2D | NS_ORTHO); - -typedef NStencilGhostBin<1, 0, 1> NStencilHalfGhostBin2dTri; -NStencilStyle(half/ghost/bin/2d/tri, - NStencilHalfGhostBin2dTri, - NS_HALF | NS_GHOST | NS_BIN | NS_2D | NS_TRI); - -typedef NStencilGhostBin<1, 1, 0> NStencilHalfGhostBin3d; -NStencilStyle(half/ghost/bin/3d, - NStencilHalfGhostBin3d, - NS_HALF | NS_GHOST | NS_BIN | NS_3D | NS_ORTHO); - -typedef NStencilGhostBin<1, 1, 1> NStencilHalfGhostBin3dTri; -NStencilStyle(half/ghost/bin/3d/tri, - NStencilHalfGhostBin3dTri, - NS_HALF | NS_GHOST | NS_BIN | NS_3D | NS_TRI); // clang-format on #else @@ -52,7 +32,7 @@ NStencilStyle(half/ghost/bin/3d/tri, namespace LAMMPS_NS { -template +template class NStencilGhostBin : public NStencil { public: NStencilGhostBin(class LAMMPS *); From 745f2e6c54c462558f2a31ac71fbefe0326d4484 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Wed, 1 Nov 2023 12:22:30 -0600 Subject: [PATCH 065/189] Changing logic for self-bin check, newton+ortho --- src/OPENMP/npair_bin_ghost_omp.cpp | 2 +- src/OPENMP/npair_bin_omp.cpp | 8 +++--- src/OPENMP/npair_halffull_omp.cpp | 6 ++--- src/OPENMP/npair_multi_old_omp.cpp | 4 +-- src/OPENMP/npair_multi_omp.cpp | 36 ++++++++++++-------------- src/OPENMP/npair_nsq_ghost_omp.cpp | 2 +- src/OPENMP/npair_nsq_omp.cpp | 6 ++--- src/OPENMP/npair_respa_bin_omp.cpp | 14 +++++----- src/OPENMP/npair_respa_nsq_omp.cpp | 8 +++--- src/OPENMP/npair_trim_omp.cpp | 2 +- src/npair_bin.cpp | 10 +++---- src/npair_multi.cpp | 36 ++++++++++++-------------- src/npair_multi_old.cpp | 6 ++--- src/npair_nsq.cpp | 2 +- src/npair_respa_bin.cpp | 12 ++++----- src/npair_respa_nsq.cpp | 8 +++--- src/npair_skip_respa.cpp | 10 +++---- src/npair_skip_size_off2on_oneside.cpp | 2 +- 18 files changed, 85 insertions(+), 89 deletions(-) diff --git a/src/OPENMP/npair_bin_ghost_omp.cpp b/src/OPENMP/npair_bin_ghost_omp.cpp index d842294338..f542cf214a 100644 --- a/src/OPENMP/npair_bin_ghost_omp.cpp +++ b/src/OPENMP/npair_bin_ghost_omp.cpp @@ -185,7 +185,7 @@ void NPairBinGhostOmp::build(NeighList *list) firstneigh[i] = neighptr; numneigh[i] = n; ipage.vgot(n); - if (ipage.status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage.status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); } NPAIR_OMP_CLOSE; list->inum = nlocal; diff --git a/src/OPENMP/npair_bin_omp.cpp b/src/OPENMP/npair_bin_omp.cpp index 81b5e085d6..5b2189dec2 100644 --- a/src/OPENMP/npair_bin_omp.cpp +++ b/src/OPENMP/npair_bin_omp.cpp @@ -117,7 +117,7 @@ void NPairBinOmp::build(NeighList *list) for (k = 0; k < nstencil; k++) { bin_start = binhead[ibin + stencil[k]]; if (HALF && NEWTON && (!TRI)) { - if (stencil[k] == 0) { + if (k == 0) { // Half neighbor list, newton on, orthonormal // loop over rest of atoms in i's bin, ghosts are at end of linked list bin_start = bins[i]; @@ -163,7 +163,7 @@ void NPairBinOmp::build(NeighList *list) // Half neighbor list, newton on, orthonormal // store every pair for every bin in stencil, except for i's bin - if (stencil[k] == 0) { + if (k == 0) { // if j is owned atom, store it, since j is beyond i in linked list // if j is ghost, only store if j coords are "above and to the "right" of i if (j >= nlocal) { @@ -229,7 +229,7 @@ void NPairBinOmp::build(NeighList *list) if (!moltemplate) which = find_special(special[i], nspecial[i], tag[j]); else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], onemols[imol] ->nspecial[iatom], + which = find_special(onemols[imol]->special[iatom], onemols[imol]->nspecial[iatom], tag[j] - tagprev); else which = 0; if (which == 0) @@ -250,7 +250,7 @@ void NPairBinOmp::build(NeighList *list) firstneigh[i] = neighptr; numneigh[i] = n; ipage.vgot(n); - if (ipage.status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage.status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); } NPAIR_OMP_CLOSE; list->inum = nlocal; diff --git a/src/OPENMP/npair_halffull_omp.cpp b/src/OPENMP/npair_halffull_omp.cpp index 78ec11023f..bc646accbc 100644 --- a/src/OPENMP/npair_halffull_omp.cpp +++ b/src/OPENMP/npair_halffull_omp.cpp @@ -106,9 +106,9 @@ void NPairHalffullOmp::build(NeighList *list) if (j < nlocal) { if (i > j) continue; } else if (TRI) { - if (fabs(x[j][2]-ztmp) > delta) { + if (fabs(x[j][2] - ztmp) > delta) { if (x[j][2] < ztmp) continue; - } else if (fabs(x[j][1]-ytmp) > delta) { + } else if (fabs(x[j][1] - ytmp) > delta) { if (x[j][1] < ytmp) continue; } else { if (x[j][0] < xtmp) continue; @@ -149,7 +149,7 @@ void NPairHalffullOmp::build(NeighList *list) firstneigh[i] = neighptr; numneigh[i] = n; ipage.vgot(n); - if (ipage.status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage.status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); } NPAIR_OMP_CLOSE; list->inum = inum_full; diff --git a/src/OPENMP/npair_multi_old_omp.cpp b/src/OPENMP/npair_multi_old_omp.cpp index 08bc54a72b..d45f2d1f5f 100644 --- a/src/OPENMP/npair_multi_old_omp.cpp +++ b/src/OPENMP/npair_multi_old_omp.cpp @@ -119,7 +119,7 @@ void NPairMultiOldOmp::build(NeighList *list) ns = nstencil_multi_old[itype]; for (k = 0; k < ns; k++) { bin_start = binhead[ibin+s[k]]; - if (s[k] == 0) { + if (k == 0) { if (HALF && NEWTON && (!TRI)) { // Half neighbor list, newton on, orthonormal // loop over rest of atoms in i's bin, ghosts are at end of linked list @@ -166,7 +166,7 @@ void NPairMultiOldOmp::build(NeighList *list) // Half neighbor list, newton on, orthonormal // store every pair for every bin in stencil,except for i's bin - if (s[k] == 0) { + if (k == 0) { // if j is owned atom, store it, since j is beyond i in linked list // if j is ghost, only store if j coords are "above and to the "right" of i if (j >= nlocal) { diff --git a/src/OPENMP/npair_multi_omp.cpp b/src/OPENMP/npair_multi_omp.cpp index 7999ff5029..818470254c 100644 --- a/src/OPENMP/npair_multi_omp.cpp +++ b/src/OPENMP/npair_multi_omp.cpp @@ -131,7 +131,7 @@ void NPairMultiOmp::build(NeighList *list) // own-bin for half stencil if (HALF && !TRI) - if (flag_half_multi[icollection][jcollection] && s[k] == 0) js = bins[i]; + if (k == 0 && flag_half_multi[icollection][jcollection]) js = bins[i]; for (j = js; j >= 0; j = bins[j]) { if (!HALF) { @@ -180,24 +180,22 @@ void NPairMultiOmp::build(NeighList *list) } else { // Half neighbor list, newton on, orthonormal // if same size: uses half stencil so includes a check of the central bin - if (flag_half_multi[icollection][jcollection]){ - if (s[k] == 0) { - // if same collection, - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the right" of i + if (k == 0 && flag_half_multi[icollection][jcollection]) { + // if same collection, + // if j is owned atom, store it, since j is beyond i in linked list + // if j is ghost, only store if j coords are "above and to the right" of i - // if different collections, - // if j is owned atom, store it if j > i - // if j is ghost, only store if j coords are "above and to the right" of i + // if different collections, + // if j is owned atom, store it if j > i + // if j is ghost, only store if j coords are "above and to the right" of i - if ((icollection != jcollection) && (j < i)) continue; + if ((icollection != jcollection) && (j < i)) continue; - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } + if (j >= nlocal) { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; } } } @@ -233,7 +231,7 @@ void NPairMultiOmp::build(NeighList *list) if (!moltemplate) which = find_special(special[i], nspecial[i], tag[j]); else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], onemols [imol]->nspecial[iatom], + which = find_special(onemols[imol]->special[iatom], onemols[imol]->nspecial[iatom], tag[j] - tagprev); else which = 0; @@ -256,7 +254,7 @@ void NPairMultiOmp::build(NeighList *list) if (!moltemplate) which = find_special(special[i], nspecial[i], tag[j]); else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], onemols [imol]->nspecial[iatom], + which = find_special(onemols[imol]->special[iatom], onemols[imol]->nspecial[iatom], tag[j] - tagprev); else which = 0; @@ -279,7 +277,7 @@ void NPairMultiOmp::build(NeighList *list) firstneigh[i] = neighptr; numneigh[i] = n; ipage.vgot(n); - if (ipage.status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage.status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); } NPAIR_OMP_CLOSE; list->inum = nlocal; diff --git a/src/OPENMP/npair_nsq_ghost_omp.cpp b/src/OPENMP/npair_nsq_ghost_omp.cpp index 6a10876497..a270fbb84d 100644 --- a/src/OPENMP/npair_nsq_ghost_omp.cpp +++ b/src/OPENMP/npair_nsq_ghost_omp.cpp @@ -172,7 +172,7 @@ void NPairNsqGhostOmp::build(NeighList *list) firstneigh[i] = neighptr; numneigh[i] = n; ipage.vgot(n); - if (ipage.status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage.status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); } NPAIR_OMP_CLOSE; list->inum = nlocal; diff --git a/src/OPENMP/npair_nsq_omp.cpp b/src/OPENMP/npair_nsq_omp.cpp index 9577964401..c482fc8f2d 100644 --- a/src/OPENMP/npair_nsq_omp.cpp +++ b/src/OPENMP/npair_nsq_omp.cpp @@ -137,9 +137,9 @@ void NPairNsqOmp::build(NeighList *list) if (j >= nlocal) { jtag = tag[j]; if (itag > jtag) { - if ((itag+jtag) % 2 == 0) continue; + if ((itag + jtag) % 2 == 0) continue; } else if (itag < jtag) { - if ((itag+jtag) % 2 == 1) continue; + if ((itag + jtag) % 2 == 1) continue; } else if (TRI) { if (fabs(x[j][2] - ztmp) > delta) { if (x[j][2] < ztmp) continue; @@ -219,7 +219,7 @@ void NPairNsqOmp::build(NeighList *list) firstneigh[i] = neighptr; numneigh[i] = n; ipage.vgot(n); - if (ipage.status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage.status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); } NPAIR_OMP_CLOSE; list->inum = nlocal; diff --git a/src/OPENMP/npair_respa_bin_omp.cpp b/src/OPENMP/npair_respa_bin_omp.cpp index b904e7136b..c958167ba0 100644 --- a/src/OPENMP/npair_respa_bin_omp.cpp +++ b/src/OPENMP/npair_respa_bin_omp.cpp @@ -88,7 +88,7 @@ void NPairRespaBinOmp::build(NeighList *list) int *numneigh_inner = list->numneigh_inner; int **firstneigh_inner = list->firstneigh_inner; - int *ilist_middle,*numneigh_middle,**firstneigh_middle; + int *ilist_middle, *numneigh_middle, **firstneigh_middle; if (respamiddle) { ilist_middle = list->ilist_middle; numneigh_middle = list->numneigh_middle; @@ -133,9 +133,9 @@ void NPairRespaBinOmp::build(NeighList *list) } for (k = 0; k < nstencil; k++) { - bin_start = binhead[ibin+stencil[k]]; + bin_start = binhead[ibin + stencil[k]]; if (NEWTON && (!TRI)) { - if (stencil[k] == 0) { + if (k == 0) { // Half neighbor list, newton on, orthonormal // loop over rest of atoms in i's bin, ghosts are at end of linked list bin_start = bins[i]; @@ -177,7 +177,7 @@ void NPairRespaBinOmp::build(NeighList *list) // Half neighbor list, newton on, orthonormal // store every pair for every bin in stencil,except for i's bin - if (stencil[k] == 0) { + if (k == 0) { // if j is owned atom, store it, since j is beyond i in linked list // if j is ghost, only store if j coords are "above and to the "right" of i if (j >= nlocal) { @@ -242,20 +242,20 @@ void NPairRespaBinOmp::build(NeighList *list) firstneigh[i] = neighptr; numneigh[i] = n; ipage.vgot(n); - if (ipage.status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage.status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); ilist_inner[i] = i; firstneigh_inner[i] = neighptr_inner; numneigh_inner[i] = n_inner; ipage.vgot(n_inner); - if (ipage_inner.status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage_inner.status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); if (respamiddle) { ilist_middle[i] = i; firstneigh_middle[i] = neighptr_middle; numneigh_middle[i] = n_middle; ipage_middle->vgot(n_middle); - if (ipage_middle->status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage_middle->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); } } NPAIR_OMP_CLOSE; diff --git a/src/OPENMP/npair_respa_nsq_omp.cpp b/src/OPENMP/npair_respa_nsq_omp.cpp index 2c4ad5d251..6815b21544 100644 --- a/src/OPENMP/npair_respa_nsq_omp.cpp +++ b/src/OPENMP/npair_respa_nsq_omp.cpp @@ -99,7 +99,7 @@ void NPairRespaNsqOmp::build(NeighList *list) int *numneigh_inner = list->numneigh_inner; int **firstneigh_inner = list->firstneigh_inner; - int *ilist_middle,*numneigh_middle,**firstneigh_middle; + int *ilist_middle, *numneigh_middle, **firstneigh_middle; if (respamiddle) { ilist_middle = list->ilist_middle; numneigh_middle = list->numneigh_middle; @@ -222,20 +222,20 @@ void NPairRespaNsqOmp::build(NeighList *list) firstneigh[i] = neighptr; numneigh[i] = n; ipage.vgot(n); - if (ipage.status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage.status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); ilist_inner[i] = i; firstneigh_inner[i] = neighptr_inner; numneigh_inner[i] = n_inner; ipage.vgot(n_inner); - if (ipage_inner.status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage_inner.status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); if (respamiddle) { ilist_middle[i] = i; firstneigh_middle[i] = neighptr_middle; numneigh_middle[i] = n_middle; ipage_middle->vgot(n_middle); - if (ipage_middle->status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage_middle->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); } } NPAIR_OMP_CLOSE; diff --git a/src/OPENMP/npair_trim_omp.cpp b/src/OPENMP/npair_trim_omp.cpp index a4419663ff..0840c1a5f2 100644 --- a/src/OPENMP/npair_trim_omp.cpp +++ b/src/OPENMP/npair_trim_omp.cpp @@ -98,7 +98,7 @@ void NPairTrimOmp::build(NeighList *list) firstneigh[i] = neighptr; numneigh[i] = n; ipage.vgot(n); - if (ipage.status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage.status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); } NPAIR_OMP_CLOSE; list->inum = inum_copy; diff --git a/src/npair_bin.cpp b/src/npair_bin.cpp index d72a98e471..d3d3415bc0 100644 --- a/src/npair_bin.cpp +++ b/src/npair_bin.cpp @@ -110,7 +110,7 @@ void NPairBin::build(NeighList *list) for (k = 0; k < nstencil; k++) { bin_start = binhead[ibin + stencil[k]]; if (HALF && NEWTON && (!TRI)) { - if (stencil[k] == 0) { + if (k == 0) { // Half neighbor list, newton on, orthonormal // loop over rest of atoms in i's bin, ghosts are at end of linked list bin_start = bins[i]; @@ -155,7 +155,7 @@ void NPairBin::build(NeighList *list) // Half neighbor list, newton on, orthonormal // store every pair for every bin in stencil, except for i's bin - if (stencil[k] == 0) { + if (k == 0) { // if j is owned atom, store it, since j is beyond i in linked list // if j is ghost, only store if j coords are "above and to the "right" of i if (j >= nlocal) { @@ -198,7 +198,7 @@ void NPairBin::build(NeighList *list) if (!moltemplate) which = find_special(special[i], nspecial[i], tag[j]); else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], onemols [imol]->nspecial[iatom], + which = find_special(onemols[imol]->special[iatom], onemols[imol]->nspecial[iatom], tag[j] - tagprev); else which = 0; @@ -221,7 +221,7 @@ void NPairBin::build(NeighList *list) if (!moltemplate) which = find_special(special[i], nspecial[i], tag[j]); else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], onemols [imol]->nspecial[iatom], + which = find_special(onemols[imol]->special[iatom], onemols[imol]->nspecial[iatom], tag[j] - tagprev); else which = 0; @@ -243,7 +243,7 @@ void NPairBin::build(NeighList *list) firstneigh[i] = neighptr; numneigh[i] = n; ipage->vgot(n); - if (ipage->status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); } list->inum = inum; diff --git a/src/npair_multi.cpp b/src/npair_multi.cpp index 37c6482a6a..ec5a67fa82 100644 --- a/src/npair_multi.cpp +++ b/src/npair_multi.cpp @@ -128,7 +128,7 @@ void NPairMulti::build(NeighList *list) // own-bin for half stencil if (HALF && !TRI) - if (flag_half_multi[icollection][jcollection] && s[k] == 0) js = bins[i]; + if (k == 0 && flag_half_multi[icollection][jcollection]) js = bins[i]; for (j = js; j >= 0; j = bins[j]) { if (!HALF) { @@ -177,24 +177,22 @@ void NPairMulti::build(NeighList *list) } else { // Half neighbor list, newton on, orthonormal // if same size: uses half stencil so includes a check of the central bin - if (flag_half_multi[icollection][jcollection]) { - if (s[k] == 0) { - // if same collection, - // if j is owned atom, store it, since j is beyond i in linked list - // if j is ghost, only store if j coords are "above and to the right" of i + if (k == 0 && flag_half_multi[icollection][jcollection]) { + // if same collection, + // if j is owned atom, store it, since j is beyond i in linked list + // if j is ghost, only store if j coords are "above and to the right" of i - // if different collections, - // if j is owned atom, store it if j > i - // if j is ghost, only store if j coords are "above and to the right" of i + // if different collections, + // if j is owned atom, store it if j > i + // if j is ghost, only store if j coords are "above and to the right" of i - if ((icollection != jcollection) && (j < i)) continue; + if ((icollection != jcollection) && (j < i)) continue; - if (j >= nlocal) { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp) { - if (x[j][1] < ytmp) continue; - if (x[j][1] == ytmp && x[j][0] < xtmp) continue; - } + if (j >= nlocal) { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp) { + if (x[j][1] < ytmp) continue; + if (x[j][1] == ytmp && x[j][0] < xtmp) continue; } } } @@ -230,7 +228,7 @@ void NPairMulti::build(NeighList *list) if (!moltemplate) which = find_special(special[i], nspecial[i], tag[j]); else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], onemols [imol]->nspecial[iatom], + which = find_special(onemols[imol]->special[iatom], onemols[imol]->nspecial[iatom], tag[j] - tagprev); else which = 0; @@ -253,7 +251,7 @@ void NPairMulti::build(NeighList *list) if (!moltemplate) which = find_special(special[i], nspecial[i], tag[j]); else if (imol >= 0) - which = find_special(onemols[imol]->special[iatom], onemols [imol]->nspecial[iatom], + which = find_special(onemols[imol]->special[iatom], onemols[imol]->nspecial[iatom], tag[j] - tagprev); else which = 0; @@ -276,7 +274,7 @@ void NPairMulti::build(NeighList *list) firstneigh[i] = neighptr; numneigh[i] = n; ipage->vgot(n); - if (ipage->status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); } list->inum = inum; diff --git a/src/npair_multi_old.cpp b/src/npair_multi_old.cpp index b2d77227cc..a4ca1e7361 100644 --- a/src/npair_multi_old.cpp +++ b/src/npair_multi_old.cpp @@ -113,7 +113,7 @@ void NPairMultiOld::build(NeighList *list) for (k = 0; k < ns; k++) { bin_start = binhead[ibin + s[k]]; if (HALF && NEWTON && (!TRI)) { - if (s[k] == 0) { + if (k == 0) { // Half neighbor list, newton on, orthonormal // loop over rest of atoms in i's bin, ghosts are at end of linked list bin_start = bins[i]; @@ -159,7 +159,7 @@ void NPairMultiOld::build(NeighList *list) // Half neighbor list, newton on, orthonormal // store every pair for every bin in stencil,except for i's bin - if (s[k] == 0) { + if (k == 0) { // if j is owned atom, store it, since j is beyond i in linked list // if j is ghost, only store if j coords are "above and to the "right" of i if (j >= nlocal) { @@ -236,7 +236,7 @@ void NPairMultiOld::build(NeighList *list) firstneigh[i] = neighptr; numneigh[i] = n; ipage->vgot(n); - if (ipage->status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); } list->inum = inum; diff --git a/src/npair_nsq.cpp b/src/npair_nsq.cpp index 5a7b1e3227..c2f43d9cb0 100644 --- a/src/npair_nsq.cpp +++ b/src/npair_nsq.cpp @@ -211,7 +211,7 @@ void NPairNsq::build(NeighList *list) firstneigh[i] = neighptr; numneigh[i] = n; ipage->vgot(n); - if (ipage->status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); } list->inum = inum; diff --git a/src/npair_respa_bin.cpp b/src/npair_respa_bin.cpp index 085986903d..f2fb9f7486 100644 --- a/src/npair_respa_bin.cpp +++ b/src/npair_respa_bin.cpp @@ -81,7 +81,7 @@ void NPairRespaBin::build(NeighList *list) int **firstneigh_inner = list->firstneigh_inner; MyPage *ipage_inner = list->ipage_inner; - int *ilist_middle,*numneigh_middle,**firstneigh_middle; + int *ilist_middle, *numneigh_middle, **firstneigh_middle; MyPage *ipage_middle; int respamiddle = list->respamiddle; if (respamiddle) { @@ -123,7 +123,7 @@ void NPairRespaBin::build(NeighList *list) for (k = 0; k < nstencil; k++) { bin_start = binhead[ibin+stencil[k]]; if (NEWTON && (!TRI)) { - if (stencil[k] == 0) { + if (k == 0) { // Half neighbor list, newton on, orthonormal // loop over rest of atoms in i's bin, ghosts are at end of linked list bin_start = bins[i]; @@ -165,7 +165,7 @@ void NPairRespaBin::build(NeighList *list) // Half neighbor list, newton on, orthonormal // store every pair for every bin in stencil,except for i's bin - if (stencil[k] == 0) { + if (k == 0) { // if j is owned atom, store it, since j is beyond i in linked list // if j is ghost, only store if j coords are "above and to the "right" of i if (j >= nlocal) { @@ -230,20 +230,20 @@ void NPairRespaBin::build(NeighList *list) firstneigh[i] = neighptr; numneigh[i] = n; ipage->vgot(n); - if (ipage->status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); ilist_inner[inum] = i; firstneigh_inner[i] = neighptr_inner; numneigh_inner[i] = n_inner; ipage_inner->vgot(n_inner); - if (ipage_inner->status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage_inner->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); if (respamiddle) { ilist_middle[inum] = i; firstneigh_middle[i] = neighptr_middle; numneigh_middle[i] = n_middle; ipage_middle->vgot(n_middle); - if (ipage_middle->status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage_middle->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); } inum++; diff --git a/src/npair_respa_nsq.cpp b/src/npair_respa_nsq.cpp index 7f70addbc9..9ca166a491 100644 --- a/src/npair_respa_nsq.cpp +++ b/src/npair_respa_nsq.cpp @@ -92,7 +92,7 @@ void NPairRespaNsq::build(NeighList *list) int **firstneigh_inner = list->firstneigh_inner; MyPage *ipage_inner = list->ipage_inner; - int *ilist_middle,*numneigh_middle,**firstneigh_middle; + int *ilist_middle, *numneigh_middle, **firstneigh_middle; MyPage *ipage_middle; int respamiddle = list->respamiddle; if (respamiddle) { @@ -215,20 +215,20 @@ void NPairRespaNsq::build(NeighList *list) firstneigh[i] = neighptr; numneigh[i] = n; ipage->vgot(n); - if (ipage->status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); ilist_inner[inum] = i; firstneigh_inner[i] = neighptr_inner; numneigh_inner[i] = n_inner; ipage_inner->vgot(n_inner); - if (ipage_inner->status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage_inner->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); if (respamiddle) { ilist_middle[inum] = i; firstneigh_middle[i] = neighptr_middle; numneigh_middle[i] = n_middle; ipage_middle->vgot(n_middle); - if (ipage_middle->status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage_middle->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); } inum++; diff --git a/src/npair_skip_respa.cpp b/src/npair_skip_respa.cpp index ecd820e892..81cfc8a767 100644 --- a/src/npair_skip_respa.cpp +++ b/src/npair_skip_respa.cpp @@ -58,9 +58,9 @@ void NPairSkipRespa::build(NeighList *list) int *numneigh_inner_skip = list->listskip->numneigh_inner; int **firstneigh_inner_skip = list->listskip->firstneigh_inner; - int *ilist_middle,*numneigh_middle,**firstneigh_middle; + int *ilist_middle, *numneigh_middle, **firstneigh_middle; MyPage *ipage_middle; - int *numneigh_middle_skip,**firstneigh_middle_skip; + int *numneigh_middle_skip, **firstneigh_middle_skip; int respamiddle = list->respamiddle; if (respamiddle) { ilist_middle = list->ilist_middle; @@ -135,20 +135,20 @@ void NPairSkipRespa::build(NeighList *list) firstneigh[i] = neighptr; numneigh[i] = n; ipage->vgot(n); - if (ipage->status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); ilist_inner[inum] = i; firstneigh_inner[i] = neighptr_inner; numneigh_inner[i] = n_inner; ipage_inner->vgot(n); - if (ipage_inner->status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage_inner->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); if (respamiddle) { ilist_middle[inum] = i; firstneigh_middle[i] = neighptr_middle; numneigh_middle[i] = n_middle; ipage_middle->vgot(n); - if (ipage_middle->status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage_middle->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); } inum++; diff --git a/src/npair_skip_size_off2on_oneside.cpp b/src/npair_skip_size_off2on_oneside.cpp index d861a1e745..8974da35b2 100644 --- a/src/npair_skip_size_off2on_oneside.cpp +++ b/src/npair_skip_size_off2on_oneside.cpp @@ -107,7 +107,7 @@ void NPairSkipSizeOff2onOneside::build(NeighList *list) for (i = 0; i < nlocal; i++) { if (numneigh[i] == 0) continue; firstneigh[i] = ipage->get(numneigh[i]); - if (ipage->status()) error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + if (ipage->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); } // second loop over atoms in other list to store neighbors From 5830dec742dc5c847a72b4bb1e6c7b5fb2aea756 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Mon, 16 Oct 2023 15:09:10 -0600 Subject: [PATCH 066/189] new compute: reaxff/bonds/local --- src/REAXFF/compute_reaxff_bonds_local.cpp | 217 ++++++++++++++++++++++ src/REAXFF/compute_reaxff_bonds_local.h | 60 ++++++ 2 files changed, 277 insertions(+) create mode 100644 src/REAXFF/compute_reaxff_bonds_local.cpp create mode 100644 src/REAXFF/compute_reaxff_bonds_local.h diff --git a/src/REAXFF/compute_reaxff_bonds_local.cpp b/src/REAXFF/compute_reaxff_bonds_local.cpp new file mode 100644 index 0000000000..c32b404e86 --- /dev/null +++ b/src/REAXFF/compute_reaxff_bonds_local.cpp @@ -0,0 +1,217 @@ +// 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. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Richard Berger (LANL) +------------------------------------------------------------------------- */ + +#include "compute_reaxff_bonds_local.h" +#include "atom.h" +#include "molecule.h" +#include "update.h" +#include "force.h" +#include "memory.h" +#include "error.h" +#include "neigh_list.h" + +#include "pair_reaxff.h" +#include "reaxff_api.h" + +using namespace LAMMPS_NS; +using namespace ReaxFF; + +/* ---------------------------------------------------------------------- */ + +ComputeReaxFFBondsLocal::ComputeReaxFFBondsLocal(LAMMPS *lmp, int narg, char **arg) : + Compute(lmp, narg, arg), + alocal(nullptr), abo(nullptr), neighid(nullptr), numneigh(nullptr), reaxff(nullptr) +{ + if (atom->tag_consecutive() == 0) + error->all(FLERR,"Atom IDs must be consecutive for compute reaxff/bonds/local"); + + local_flag = 1; + + nvalues = 7 + 2*MAXREAXBOND; + prev_nvalues = 0; + + // initialize output + + nlocal = atom->nlocal; + + size_local_rows = atom->nlocal; + size_local_cols = 7 + 2*MAXREAXBOND; + + allocate(nlocal); +} + + +/* ---------------------------------------------------------------------- */ + +ComputeReaxFFBondsLocal::~ComputeReaxFFBondsLocal() +{ + memory->destroy(alocal); + destroy(); +} + +void ComputeReaxFFBondsLocal::destroy() +{ + memory->destroy(abo); + memory->destroy(neighid); + memory->destroy(numneigh); +} + +/* ---------------------------------------------------------------------- */ + +void ComputeReaxFFBondsLocal::allocate(int n) +{ + memory->create(abo,n,MAXREAXBOND,"reaxff/bonds/local:abo"); + memory->create(neighid,n,MAXREAXBOND,"reaxff/bonds/local:neighid"); + memory->create(numneigh,n,"reaxff/bonds/local:numneigh"); +} + +/* ---------------------------------------------------------------------- */ + +void ComputeReaxFFBondsLocal::init() +{ + reaxff = dynamic_cast(force->pair_match("^reax..",0)); + if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/bonds/local without " + "pair_style reaxff, reaxff/kk, or reaxff/omp"); +} + +/* ---------------------------------------------------------------------- */ + +int ComputeReaxFFBondsLocal::FindBond() +{ + int *ilist, i, ii, inum; + int j, pj, nj; + tagint jtag; + double bo_tmp,bo_cut; + + inum = reaxff->list->inum; + ilist = reaxff->list->ilist; + bond_data *bo_ij; + bo_cut = reaxff->api->control->bg_cut; + + tagint *tag = atom->tag; + int numbonds = 0; + + for (ii = 0; ii < inum; ii++) { + i = ilist[ii]; + nj = 0; + + for (pj = Start_Index(i, reaxff->api->lists); pj < End_Index(i, reaxff->api->lists); ++pj) { + bo_ij = &(reaxff->api->lists->select.bond_list[pj]); + j = bo_ij->nbr; + jtag = tag[j]; + bo_tmp = bo_ij->bo_data.BO; + + if (bo_tmp > bo_cut) { + neighid[i][nj] = jtag; + abo[i][nj] = bo_tmp; + nj ++; + } + } + numneigh[i] = nj; + if (nj > numbonds) numbonds = nj; + } + return numbonds; +} + +/* ---------------------------------------------------------------------- */ + +void ComputeReaxFFBondsLocal::compute_local() +{ + invoked_local = update->ntimestep; + + // count local entries and compute bond info + if (atom->nlocal > nlocal) { + destroy(); + allocate(atom->nlocal); + } + + { + const int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) { + numneigh[i] = 0; + for (int j = 0; j < MAXREAXBOND; j++) { + neighid[i][j] = 0; + abo[i][j] = 0.0; + } + } + } + + int maxnumbonds = FindBond(); + nvalues = 7+2*maxnumbonds; + + if(atom->nlocal > nlocal || nvalues > prev_nvalues) { + reallocate(); + } + + size_local_rows = nlocal; + size_local_cols = nvalues; + + for (int i = 0; i < nlocal; ++i) { + auto ptr = alocal[i]; + int numbonds = numneigh[i]; + ptr[0] = atom->tag[i]; + ptr[1] = atom->type[i]; + ptr[2] = numbonds; + + int j = 3; + + for (int k = 0; k < numbonds; k++) { + ptr[j++] = neighid[i][k]; + } + + ptr[j++] = (atom->molecule == nullptr) ? 0.0 : atom->molecule[i]; + + for (int k = 0; k < numbonds; k++) { + ptr[j++] = abo[i][k]; + } + + ptr[j++] = reaxff->api->workspace->total_bond_order[i]; + ptr[j++] = reaxff->api->workspace->nlp[i]; + ptr[j++] = atom->q[i]; + + // clear any remaining + for(; j < nvalues; ++j) { + ptr[j] = 0.0; + } + } +} + +void ComputeReaxFFBondsLocal::reallocate() +{ + nlocal = atom->nlocal; + + // grow array_local + memory->destroy(alocal); + memory->create(alocal,nlocal,nvalues,"reaxff/bonds/local:array_local"); + array_local = alocal; + + prev_nvalues = nvalues; +} + +/* ---------------------------------------------------------------------- + memory usage of local data +------------------------------------------------------------------------- */ + +double ComputeReaxFFBondsLocal::memory_usage() +{ + double bytes = (double)nlocal*nvalues * sizeof(double); + bytes += (double)(2*nlocal*MAXREAXBOND) * sizeof(double); + bytes += (double)(nlocal) * sizeof(int); + return bytes; +} diff --git a/src/REAXFF/compute_reaxff_bonds_local.h b/src/REAXFF/compute_reaxff_bonds_local.h new file mode 100644 index 0000000000..2347db3a26 --- /dev/null +++ b/src/REAXFF/compute_reaxff_bonds_local.h @@ -0,0 +1,60 @@ +/* -*- 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. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Richard Berger (LANL) +------------------------------------------------------------------------- */ + +#ifdef COMPUTE_CLASS +// clang-format off +ComputeStyle(reaxff/bonds/local,ComputeReaxFFBondsLocal); +// clang-format on +#else + +#ifndef LMP_COMPUTE_REAXFF_BONDS_LOCAL_H +#define LMP_COMPUTE_REAXFF_BONDS_LOCAL_H + +#include "compute.h" + +namespace LAMMPS_NS { + +class ComputeReaxFFBondsLocal : public Compute { + public: + ComputeReaxFFBondsLocal(class LAMMPS *, int, char **); + ~ComputeReaxFFBondsLocal() override; + void init() override; + void compute_local() override; + double memory_usage() override; + + private: + int nlocal; + int nvalues; + int prev_nvalues; + + double **alocal; + tagint **neighid; + double **abo; + int *numneigh; + class PairReaxFF *reaxff; + + int FindBond(); + + void allocate(int); + void destroy(); + void reallocate(); +}; + +} // namespace LAMMPS_NS + +#endif +#endif From bbc2794df29920bc1673ac0501518669fcfb822d Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Mon, 16 Oct 2023 15:09:16 -0600 Subject: [PATCH 067/189] add reaxff/bonds/local/kk --- .../compute_reaxff_bonds_local_kokkos.cpp | 125 ++++++++++++++++++ .../compute_reaxff_bonds_local_kokkos.h | 68 ++++++++++ src/KOKKOS/pair_reaxff_kokkos.cpp | 55 ++++++++ src/KOKKOS/pair_reaxff_kokkos.h | 19 +++ 4 files changed, 267 insertions(+) create mode 100644 src/KOKKOS/compute_reaxff_bonds_local_kokkos.cpp create mode 100644 src/KOKKOS/compute_reaxff_bonds_local_kokkos.h diff --git a/src/KOKKOS/compute_reaxff_bonds_local_kokkos.cpp b/src/KOKKOS/compute_reaxff_bonds_local_kokkos.cpp new file mode 100644 index 0000000000..a56d243ed6 --- /dev/null +++ b/src/KOKKOS/compute_reaxff_bonds_local_kokkos.cpp @@ -0,0 +1,125 @@ +// 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. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Richard Berger (LANL) +------------------------------------------------------------------------- */ + +#include "compute_reaxff_bonds_local_kokkos.h" +#include "atom.h" +#include "molecule.h" +#include "update.h" +#include "force.h" +#include "memory.h" +#include "error.h" +#include "neigh_list.h" + +#include "memory_kokkos.h" +#include "pair_reaxff_kokkos.h" +#include "reaxff_api.h" + +using namespace LAMMPS_NS; +using namespace ReaxFF; + +/* ---------------------------------------------------------------------- */ + +template +ComputeReaxFFBondsLocalKokkos::ComputeReaxFFBondsLocalKokkos(LAMMPS *lmp, int narg, char **arg) : + Compute(lmp, narg, arg), + alocal(nullptr), reaxff(nullptr) +{ + if (atom->tag_consecutive() == 0) + error->all(FLERR,"Atom IDs must be consecutive for compute reaxff/bonds/local"); + + local_flag = 1; + + nvalues = 7 + 2*MAXREAXBOND; + prev_nvalues = 0; + + // initialize output + + nlocal = atom->nlocal; + + size_local_rows = atom->nlocal; + size_local_cols = 7 + 2*MAXREAXBOND; + printf("RUNNING KOKKOS VERSION\n"); +} + + +/* ---------------------------------------------------------------------- */ + +template +ComputeReaxFFBondsLocalKokkos::~ComputeReaxFFBondsLocalKokkos() +{ + memoryKK->destroy_kokkos(k_alocal, alocal); +} + +/* ---------------------------------------------------------------------- */ + +template +void ComputeReaxFFBondsLocalKokkos::init() +{ + reaxff = dynamic_cast(force->pair_match("^reax../kk",0)); + if (reaxff == nullptr) error->all(FLERR,"Cannot use fix reaxff/bonds without " + "pair_style reaxff/kk"); +} + +/* ---------------------------------------------------------------------- */ + +template +void ComputeReaxFFBondsLocalKokkos::compute_local() +{ + invoked_local = update->ntimestep; + + int maxnumbonds = 0; + if (reaxff->execution_space == Device) + device_pair()->FindBond(maxnumbonds); + else + host_pair()->FindBond(maxnumbonds); + nvalues = 7+2*maxnumbonds; + + if(atom->nlocal > nlocal || nvalues > prev_nvalues) { + nlocal = atom->nlocal; + memoryKK->destroy_kokkos(k_alocal, alocal); + memoryKK->create_kokkos(k_alocal, alocal, atom->nlocal, nvalues,"reaxff/bonds/local:alocal"); + prev_nvalues = nvalues; + array_local = alocal; + } + + size_local_rows = nlocal; + size_local_cols = nvalues; + + if (reaxff->execution_space == Device) + device_pair()->PackBondInfo(k_alocal); + else + host_pair()->PackBondInfo(k_alocal); +} + +/* ---------------------------------------------------------------------- + memory usage of local data +------------------------------------------------------------------------- */ + +template +double ComputeReaxFFBondsLocalKokkos::memory_usage() +{ + double bytes = (double)nlocal*nvalues * sizeof(double); + return bytes; +} + +namespace LAMMPS_NS { +template class ComputeReaxFFBondsLocalKokkos; +#ifdef LMP_KOKKOS_GPU +template class ComputeReaxFFBondsLocalKokkos; +#endif +} diff --git a/src/KOKKOS/compute_reaxff_bonds_local_kokkos.h b/src/KOKKOS/compute_reaxff_bonds_local_kokkos.h new file mode 100644 index 0000000000..bfb5521199 --- /dev/null +++ b/src/KOKKOS/compute_reaxff_bonds_local_kokkos.h @@ -0,0 +1,68 @@ +/* -*- 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. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Richard Berger (LANL) +------------------------------------------------------------------------- */ + +#ifdef COMPUTE_CLASS +// clang-format off +ComputeStyle(reaxff/bonds/local/kk,ComputeReaxFFBondsLocalKokkos); +ComputeStyle(reaxff/bonds/local/kk/device,ComputeReaxFFBondsLocalKokkos); +ComputeStyle(reaxff/bonds/local/kk/host,ComputeReaxFFBondsLocalKokkos); +// clang-format on +#else + +#ifndef LMP_COMPUTE_REAXFF_BONDS_LOCAL_KOKKOS_H +#define LMP_COMPUTE_REAXFF_BONDS_LOCAL_KOKKOS_H + +#include "compute_reaxff_bonds_local.h" +#include "pair_reaxff_kokkos.h" +#include "kokkos_type.h" + +namespace LAMMPS_NS { + +template +class ComputeReaxFFBondsLocalKokkos : public Compute { + public: + using device_type = DeviceType; + using AT = ArrayTypes; + + ComputeReaxFFBondsLocalKokkos(class LAMMPS *, int, char **); + ~ComputeReaxFFBondsLocalKokkos() override; + void init() override; + void compute_local() override; + double memory_usage() override; + + private: + int nlocal; + int nvalues; + int prev_nvalues; + + double **alocal; + typename AT::tdual_float_2d k_alocal; + PairReaxFF *reaxff; + + auto device_pair() { + return dynamic_cast*>(reaxff); + } + + auto host_pair() { + return dynamic_cast*>(reaxff); + } +}; + +} // namespace LAMMPS_NS + +#endif +#endif diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index c7d54b80cd..fbf90f140d 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -4290,6 +4290,61 @@ void PairReaxFFKokkos::pack_bond_buffer_item(int i, int &j, const bo /* ---------------------------------------------------------------------- */ +template +void PairReaxFFKokkos::PackBondInfo(DAT::tdual_float_2d k_alocal) +{ + d_alocal = k_alocal.view(); + k_params_sing.template sync(); + atomKK->sync(execution_space,TAG_MASK|TYPE_MASK|Q_MASK|MOLECULE_MASK); + + tag = atomKK->k_tag.view(); + type = atomKK->k_type.view(); + q = atomKK->k_q.view(); + if (atom->molecule) + molecule = atomKK->k_molecule.view(); + + copymode = 1; + nlocal = atomKK->nlocal; + PairReaxKokkosPackBondInfoFunctor pack_bond_info_functor(this); + Kokkos::parallel_for(nlocal, pack_bond_info_functor); + copymode = 0; + + k_alocal.modify(); + k_alocal.sync(); +} + +template +KOKKOS_INLINE_FUNCTION +void PairReaxFFKokkos::pack_bond_info_item(const int i) const +{ + const int numbonds = d_numneigh_bonds[i]; + d_alocal(i,0) = tag[i]; + d_alocal(i,1) = type[i]; + d_alocal(i,2) = numbonds; + + int j = 3; + + for (int k = 0; k < numbonds; k++) { + d_alocal(i,j++) = d_neighid(i,k); + } + + d_alocal(i,j++) = molecule.data() ? molecule[i] : 0.0; + + for (int k = 0; k < numbonds; k++) { + d_alocal(i,j++) = d_abo(i,k); + } + + d_alocal(i,j++) = d_total_bo[i]; + d_alocal(i,j++) = paramssing(type[i]).nlp_opt - d_Delta_lp[i]; + d_alocal(i,j++) = q[i]; + + for(; j < d_alocal.extent(1); ++j) { + d_alocal(i,j) = 0.0; + } +} + +/* ---------------------------------------------------------------------- */ + template void PairReaxFFKokkos::FindBondSpecies() { diff --git a/src/KOKKOS/pair_reaxff_kokkos.h b/src/KOKKOS/pair_reaxff_kokkos.h index 1ad0955a1e..32007e9970 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.h +++ b/src/KOKKOS/pair_reaxff_kokkos.h @@ -135,6 +135,7 @@ class PairReaxFFKokkos : public PairReaxFF { double memory_usage(); void FindBond(int &); void PackBondBuffer(DAT::tdual_ffloat_1d, int &); + void PackBondInfo(DAT::tdual_float_2d); void FindBondSpecies(); template @@ -292,6 +293,9 @@ class PairReaxFFKokkos : public PairReaxFF { KOKKOS_INLINE_FUNCTION void pack_bond_buffer_item(int, int&, const bool&) const; + KOKKOS_INLINE_FUNCTION + void pack_bond_info_item(const int) const; + KOKKOS_INLINE_FUNCTION void operator()(TagPairReaxFindBondSpeciesZero, const int&) const; @@ -506,6 +510,8 @@ class PairReaxFFKokkos : public PairReaxFF { typename AT::t_ffloat_1d d_buf; DAT::tdual_int_scalar k_nbuf_local; + typename AT::t_float_2d d_alocal; + typedef Kokkos::View t_reax_int4_2d; t_reax_int4_2d d_angular_pack, d_torsion_pack; @@ -549,6 +555,19 @@ struct PairReaxKokkosPackBondBufferFunctor { } }; +template +struct PairReaxKokkosPackBondInfoFunctor { + using device_type = DeviceType; + using value_type = int; + PairReaxFFKokkos c; + PairReaxKokkosPackBondInfoFunctor(PairReaxFFKokkos* c_ptr):c(*c_ptr) {}; + + KOKKOS_INLINE_FUNCTION + void operator()(const int i) const { + c.pack_bond_info_item(i); + } +}; + } #endif From fea5f5a2438d5ef1c563e945783f93a9560a18f4 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Mon, 16 Oct 2023 16:52:34 -0600 Subject: [PATCH 068/189] First serial version of Steve's suggestion --- examples/reaxff/in.reaxff.tatb | 9 +- ...nds_local.cpp => compute_reaxff_bonds.cpp} | 174 +++++++++--------- ...f_bonds_local.h => compute_reaxff_bonds.h} | 27 ++- 3 files changed, 107 insertions(+), 103 deletions(-) rename src/REAXFF/{compute_reaxff_bonds_local.cpp => compute_reaxff_bonds.cpp} (57%) rename src/REAXFF/{compute_reaxff_bonds_local.h => compute_reaxff_bonds.h} (73%) diff --git a/examples/reaxff/in.reaxff.tatb b/examples/reaxff/in.reaxff.tatb index 6cf7828cf1..fd0d846154 100644 --- a/examples/reaxff/in.reaxff.tatb +++ b/examples/reaxff/in.reaxff.tatb @@ -31,8 +31,15 @@ neigh_modify delay 0 every 5 check no fix 1 all nve fix 2 all qeq/reaxff 1 0.0 10.0 1.0e-6 reaxff fix 4 all reaxff/bonds 5 bonds.reaxff +compute bonds all reaxff/bonds variable nqeq equal f_2 +# dumps out the local bond information +dump 1 all local 5 bonds_compute.reaxff c_bonds[1] c_bonds[2] c_bonds[3] + +# dumps out the peratom bond information +dump 2 all custom 5 bonds_atom.reaxff c_bonds[*] + thermo 5 thermo_style custom step temp epair etotal press & v_eb v_ea v_elp v_emol v_ev v_epen v_ecoa & @@ -50,6 +57,6 @@ timestep 0.0625 # axes yes 0.8 0.02 view 60 -30 #dump_modify 3 pad 3 -fix 3 all reaxff/species 1 5 5 species.tatb +#fix 3 all reaxff/species 1 5 5 species.tatb run 25 diff --git a/src/REAXFF/compute_reaxff_bonds_local.cpp b/src/REAXFF/compute_reaxff_bonds.cpp similarity index 57% rename from src/REAXFF/compute_reaxff_bonds_local.cpp rename to src/REAXFF/compute_reaxff_bonds.cpp index c32b404e86..7f1605263c 100644 --- a/src/REAXFF/compute_reaxff_bonds_local.cpp +++ b/src/REAXFF/compute_reaxff_bonds.cpp @@ -16,7 +16,7 @@ Contributing author: Richard Berger (LANL) ------------------------------------------------------------------------- */ -#include "compute_reaxff_bonds_local.h" +#include "compute_reaxff_bonds.h" #include "atom.h" #include "molecule.h" #include "update.h" @@ -33,39 +33,36 @@ using namespace ReaxFF; /* ---------------------------------------------------------------------- */ -ComputeReaxFFBondsLocal::ComputeReaxFFBondsLocal(LAMMPS *lmp, int narg, char **arg) : +ComputeReaxFFBonds::ComputeReaxFFBonds(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg), - alocal(nullptr), abo(nullptr), neighid(nullptr), numneigh(nullptr), reaxff(nullptr) + abo(nullptr), neighid(nullptr), numneigh(nullptr), reaxff(nullptr) { if (atom->tag_consecutive() == 0) - error->all(FLERR,"Atom IDs must be consecutive for compute reaxff/bonds/local"); + error->all(FLERR,"Atom IDs must be consecutive for compute reaxff/bonds"); local_flag = 1; - - nvalues = 7 + 2*MAXREAXBOND; - prev_nvalues = 0; + peratom_flag = 1; // initialize output - nlocal = atom->nlocal; + nlocal = -1; + prev_nbonds = -1; - size_local_rows = atom->nlocal; - size_local_cols = 7 + 2*MAXREAXBOND; + size_peratom_cols = 7; - allocate(nlocal); + size_local_rows = 0; + size_local_cols = 3; + + invoked_bonds = -1; } /* ---------------------------------------------------------------------- */ -ComputeReaxFFBondsLocal::~ComputeReaxFFBondsLocal() -{ - memory->destroy(alocal); - destroy(); -} - -void ComputeReaxFFBondsLocal::destroy() +ComputeReaxFFBonds::~ComputeReaxFFBonds() { + memory->destroy(array_local); + memory->destroy(array_atom); memory->destroy(abo); memory->destroy(neighid); memory->destroy(numneigh); @@ -73,25 +70,16 @@ void ComputeReaxFFBondsLocal::destroy() /* ---------------------------------------------------------------------- */ -void ComputeReaxFFBondsLocal::allocate(int n) -{ - memory->create(abo,n,MAXREAXBOND,"reaxff/bonds/local:abo"); - memory->create(neighid,n,MAXREAXBOND,"reaxff/bonds/local:neighid"); - memory->create(numneigh,n,"reaxff/bonds/local:numneigh"); -} - -/* ---------------------------------------------------------------------- */ - -void ComputeReaxFFBondsLocal::init() +void ComputeReaxFFBonds::init() { reaxff = dynamic_cast(force->pair_match("^reax..",0)); - if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/bonds/local without " - "pair_style reaxff, reaxff/kk, or reaxff/omp"); + if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/bonds without " + "pair_style reaxff, reaxff/kk, or reaxff/omp"); } /* ---------------------------------------------------------------------- */ -int ComputeReaxFFBondsLocal::FindBond() +int ComputeReaxFFBonds::FindBond() { int *ilist, i, ii, inum; int j, pj, nj; @@ -119,98 +107,108 @@ int ComputeReaxFFBondsLocal::FindBond() if (bo_tmp > bo_cut) { neighid[i][nj] = jtag; abo[i][nj] = bo_tmp; - nj ++; + nj++; } } numneigh[i] = nj; - if (nj > numbonds) numbonds = nj; + numbonds += nj; } return numbonds; } + /* ---------------------------------------------------------------------- */ -void ComputeReaxFFBondsLocal::compute_local() +void ComputeReaxFFBonds::compute_bonds() +{ + invoked_bonds = update->ntimestep; + + if (atom->nlocal > nlocal) { + memory->destroy(abo); + memory->destroy(neighid); + memory->destroy(numneigh); + memory->destroy(array_atom); + nlocal = atom->nlocal; + memory->create(abo, nlocal, MAXREAXBOND, "reaxff/bonds:abo"); + memory->create(neighid, nlocal, MAXREAXBOND, "reaxff/bonds:neighid"); + memory->create(numneigh, nlocal, "reaxff/bonds:numneigh"); + memory->create(array_atom, nlocal, 7, "reaxff/bonds:array_atom"); + } + + for (int i = 0; i < nlocal; i++) { + numneigh[i] = 0; + for (int j = 0; j < MAXREAXBOND; j++) { + neighid[i][j] = 0; + abo[i][j] = 0.0; + } + } + + nbonds = FindBond(); +} + +/* ---------------------------------------------------------------------- */ + +void ComputeReaxFFBonds::compute_local() { invoked_local = update->ntimestep; - // count local entries and compute bond info - if (atom->nlocal > nlocal) { - destroy(); - allocate(atom->nlocal); + if(invoked_bonds < update->ntimestep) { + compute_bonds(); } - { - const int nlocal = atom->nlocal; - - for (int i = 0; i < nlocal; i++) { - numneigh[i] = 0; - for (int j = 0; j < MAXREAXBOND; j++) { - neighid[i][j] = 0; - abo[i][j] = 0.0; - } - } + if(nbonds > prev_nbonds) { + // grow array_local + memory->destroy(array_local); + memory->create(array_local, nbonds, 3, "reaxff/bonds:array_local"); + prev_nbonds = nbonds; } - int maxnumbonds = FindBond(); - nvalues = 7+2*maxnumbonds; + size_local_rows = nbonds; - if(atom->nlocal > nlocal || nvalues > prev_nvalues) { - reallocate(); - } - - size_local_rows = nlocal; - size_local_cols = nvalues; + int b = 0; for (int i = 0; i < nlocal; ++i) { - auto ptr = alocal[i]; - int numbonds = numneigh[i]; - ptr[0] = atom->tag[i]; - ptr[1] = atom->type[i]; - ptr[2] = numbonds; - - int j = 3; + const int numbonds = numneigh[i]; for (int k = 0; k < numbonds; k++) { - ptr[j++] = neighid[i][k]; - } - - ptr[j++] = (atom->molecule == nullptr) ? 0.0 : atom->molecule[i]; - - for (int k = 0; k < numbonds; k++) { - ptr[j++] = abo[i][k]; - } - - ptr[j++] = reaxff->api->workspace->total_bond_order[i]; - ptr[j++] = reaxff->api->workspace->nlp[i]; - ptr[j++] = atom->q[i]; - - // clear any remaining - for(; j < nvalues; ++j) { - ptr[j] = 0.0; + auto bond = array_local[b++]; + bond[0] = i; + bond[1] = neighid[i][k]; + bond[2] = abo[i][k]; } } } -void ComputeReaxFFBondsLocal::reallocate() +/* ---------------------------------------------------------------------- */ + +void ComputeReaxFFBonds::compute_peratom() { - nlocal = atom->nlocal; + invoked_peratom = update->ntimestep; - // grow array_local - memory->destroy(alocal); - memory->create(alocal,nlocal,nvalues,"reaxff/bonds/local:array_local"); - array_local = alocal; + if(invoked_bonds < update->ntimestep) { + compute_bonds(); + } - prev_nvalues = nvalues; + for (int i = 0; i < nlocal; ++i) { + auto ptr = array_atom[i]; + ptr[0] = atom->tag[i]; + ptr[1] = atom->type[i]; + ptr[2] = numneigh[i]; + ptr[3] = (atom->molecule == nullptr) ? 0.0 : atom->molecule[i]; + ptr[4] = reaxff->api->workspace->total_bond_order[i]; + ptr[5] = reaxff->api->workspace->nlp[i]; + ptr[6] = atom->q[i]; + } } /* ---------------------------------------------------------------------- memory usage of local data ------------------------------------------------------------------------- */ -double ComputeReaxFFBondsLocal::memory_usage() +double ComputeReaxFFBonds::memory_usage() { - double bytes = (double)nlocal*nvalues * sizeof(double); + double bytes = (double)(nbonds*3) * sizeof(double); + bytes += (double)(nlocal*7) * sizeof(double); bytes += (double)(2*nlocal*MAXREAXBOND) * sizeof(double); bytes += (double)(nlocal) * sizeof(int); return bytes; diff --git a/src/REAXFF/compute_reaxff_bonds_local.h b/src/REAXFF/compute_reaxff_bonds.h similarity index 73% rename from src/REAXFF/compute_reaxff_bonds_local.h rename to src/REAXFF/compute_reaxff_bonds.h index 2347db3a26..6b00cef7ed 100644 --- a/src/REAXFF/compute_reaxff_bonds_local.h +++ b/src/REAXFF/compute_reaxff_bonds.h @@ -17,41 +17,40 @@ #ifdef COMPUTE_CLASS // clang-format off -ComputeStyle(reaxff/bonds/local,ComputeReaxFFBondsLocal); +ComputeStyle(reaxff/bonds,ComputeReaxFFBonds); // clang-format on #else -#ifndef LMP_COMPUTE_REAXFF_BONDS_LOCAL_H -#define LMP_COMPUTE_REAXFF_BONDS_LOCAL_H +#ifndef LMP_COMPUTE_REAXFF_BONDS_H +#define LMP_COMPUTE_REAXFF_BONDS_H #include "compute.h" namespace LAMMPS_NS { -class ComputeReaxFFBondsLocal : public Compute { +class ComputeReaxFFBonds : public Compute { public: - ComputeReaxFFBondsLocal(class LAMMPS *, int, char **); - ~ComputeReaxFFBondsLocal() override; + ComputeReaxFFBonds(class LAMMPS *, int, char **); + ~ComputeReaxFFBonds() override; void init() override; void compute_local() override; + void compute_peratom() override; + virtual void compute_bonds(); double memory_usage() override; - private: + protected: + bigint invoked_bonds; // last timestep on which compute_bonds() was invoked int nlocal; - int nvalues; - int prev_nvalues; + int nbonds; + int prev_nbonds; - double **alocal; tagint **neighid; double **abo; int *numneigh; class PairReaxFF *reaxff; + private: int FindBond(); - - void allocate(int); - void destroy(); - void reallocate(); }; } // namespace LAMMPS_NS From a72a3ed50d12365ebb41a02001454fe667036b23 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Tue, 17 Oct 2023 16:24:14 -0600 Subject: [PATCH 069/189] Kokkos version of compute reaxff/bonds --- src/KOKKOS/compute_reaxff_bonds_kokkos.cpp | 192 ++++++++++++++++++ ...kokkos.h => compute_reaxff_bonds_kokkos.h} | 30 ++- .../compute_reaxff_bonds_local_kokkos.cpp | 125 ------------ src/KOKKOS/pair_reaxff_kokkos.cpp | 55 ----- src/KOKKOS/pair_reaxff_kokkos.h | 19 -- 5 files changed, 206 insertions(+), 215 deletions(-) create mode 100644 src/KOKKOS/compute_reaxff_bonds_kokkos.cpp rename src/KOKKOS/{compute_reaxff_bonds_local_kokkos.h => compute_reaxff_bonds_kokkos.h} (67%) delete mode 100644 src/KOKKOS/compute_reaxff_bonds_local_kokkos.cpp diff --git a/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp b/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp new file mode 100644 index 0000000000..921acb9193 --- /dev/null +++ b/src/KOKKOS/compute_reaxff_bonds_kokkos.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. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Richard Berger (LANL) +------------------------------------------------------------------------- */ + +#include "compute_reaxff_bonds_kokkos.h" +#include "atom.h" +#include "molecule.h" +#include "update.h" +#include "force.h" +#include "memory.h" +#include "error.h" +#include "neigh_list.h" + +#include "memory_kokkos.h" +#include "pair_reaxff_kokkos.h" +#include "reaxff_api.h" + +using namespace LAMMPS_NS; +using namespace ReaxFF; + +/* ---------------------------------------------------------------------- */ + +template +ComputeReaxFFBondsKokkos::ComputeReaxFFBondsKokkos(LAMMPS *lmp, int narg, char **arg) : + ComputeReaxFFBonds(lmp, narg, arg), + nbuf(-1), buf(nullptr) +{ +} + + +/* ---------------------------------------------------------------------- */ + +template +ComputeReaxFFBondsKokkos::~ComputeReaxFFBondsKokkos() +{ + memoryKK->destroy_kokkos(k_buf, buf); +} + +/* ---------------------------------------------------------------------- */ + +template +void ComputeReaxFFBondsKokkos::init() +{ + reaxff = dynamic_cast(force->pair_match("^reax../kk",0)); + if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/bonds without " + "pair_style reaxff/kk"); +} + +/* ---------------------------------------------------------------------- */ +template +void ComputeReaxFFBondsKokkos::compute_bonds() +{ + if (atom->nlocal > nlocal) { + memory->destroy(array_atom); + nlocal = atom->nlocal; + memory->create(array_atom, nlocal, 7, "reaxff/bonds:array_atom"); + } + + // retrieve bond information from kokkos pair style. the data potentially + // lives on device. it is copied into buf on the host in a condensed format + // compute_local and compute_atom then expand the data from this buffer into + // appropiate arrays for consumption by others (e.g. dump local, dump custom + // or library interface) + int maxnumbonds = 0; + if (reaxff->execution_space == Device) + device_pair()->FindBond(maxnumbonds); + else + host_pair()->FindBond(maxnumbonds); + + nbuf = 1+(maxnumbonds*2 + 7)*nlocal; + + if(!buf || k_buf.extent(0) < nbuf) { + memoryKK->destroy_kokkos(k_buf, buf); + memoryKK->create_kokkos(k_buf, buf, nbuf, "reaxff/bonds:buf"); + } + + // Pass information to buffer, will sync to host + int nbuf_local; + if (reaxff->execution_space == Device) + device_pair()->PackBondBuffer(k_buf, nbuf_local); + else + host_pair()->PackBondBuffer(k_buf, nbuf_local); + buf[0] = nlocal; + + // Extract number of bonds from buffer + nbonds = 0; + int j = 1; + for (int i = 0; i < nlocal; i++) { + int numbonds = static_cast(buf[j+5]); + nbonds += numbonds; + j += 2*numbonds + 7; + } +} + +/* ---------------------------------------------------------------------- */ + +template +void ComputeReaxFFBondsKokkos::compute_local() +{ + invoked_local = update->ntimestep; + + if(invoked_bonds < update->ntimestep) { + compute_bonds(); + } + + if(nbonds > prev_nbonds) { + // grow array_local + memory->destroy(array_local); + memory->create(array_local, nbonds, 3, "reaxff/bonds:array_local"); + prev_nbonds = nbonds; + } + + size_local_rows = nbonds; + + // extract local bond information from buffer + int b = 0; + int j = 1; + + for (int i = 0; i < nlocal; ++i) { + const int numbonds = static_cast(buf[j+5]); + const int neigh_offset = j + 6; + const int bo_offset = neigh_offset + numbonds + 1; + for (int k = 0; k < numbonds; k++) { + auto bond = array_local[b++]; + bond[0] = i; + bond[1] = static_cast (buf[neigh_offset+k]); + bond[2] = buf[bo_offset+k]; + } + j += 2*numbonds + 7; + } +} + +/* ---------------------------------------------------------------------- */ + +template +void ComputeReaxFFBondsKokkos::compute_peratom() +{ + invoked_peratom = update->ntimestep; + + if(invoked_bonds < update->ntimestep) { + compute_bonds(); + } + + // extract peratom bond information from buffer + int j = 1; + for (int i = 0; i < nlocal; ++i) { + auto ptr = array_atom[i]; + int numbonds = static_cast(buf[j+5]); + const int mol_offset = j + 6 + numbonds; + ptr[0] = buf[j]; // jtag + ptr[1] = buf[j+1]; // itype + ptr[2] = numbonds; + ptr[3] = buf[mol_offset]; // mol + ptr[4] = buf[j+2]; // sbo + ptr[5] = buf[j+3]; // nlp + ptr[6] = buf[j+4]; // q + j += 2*numbonds + 7; + } +} + +/* ---------------------------------------------------------------------- + memory usage of local data +------------------------------------------------------------------------- */ + +template +double ComputeReaxFFBondsKokkos::memory_usage() +{ + double bytes = (double)(nbonds*3) * sizeof(double); + bytes += (double)(nlocal*7) * sizeof(double); + return bytes; +} + +namespace LAMMPS_NS { +template class ComputeReaxFFBondsKokkos; +#ifdef LMP_KOKKOS_GPU +template class ComputeReaxFFBondsKokkos; +#endif +} diff --git a/src/KOKKOS/compute_reaxff_bonds_local_kokkos.h b/src/KOKKOS/compute_reaxff_bonds_kokkos.h similarity index 67% rename from src/KOKKOS/compute_reaxff_bonds_local_kokkos.h rename to src/KOKKOS/compute_reaxff_bonds_kokkos.h index bfb5521199..45020ffa81 100644 --- a/src/KOKKOS/compute_reaxff_bonds_local_kokkos.h +++ b/src/KOKKOS/compute_reaxff_bonds_kokkos.h @@ -17,41 +17,39 @@ #ifdef COMPUTE_CLASS // clang-format off -ComputeStyle(reaxff/bonds/local/kk,ComputeReaxFFBondsLocalKokkos); -ComputeStyle(reaxff/bonds/local/kk/device,ComputeReaxFFBondsLocalKokkos); -ComputeStyle(reaxff/bonds/local/kk/host,ComputeReaxFFBondsLocalKokkos); +ComputeStyle(reaxff/bonds/kk,ComputeReaxFFBondsKokkos); +ComputeStyle(reaxff/bonds/kk/device,ComputeReaxFFBondsKokkos); +ComputeStyle(reaxff/bonds/kk/host,ComputeReaxFFBondsKokkos); // clang-format on #else -#ifndef LMP_COMPUTE_REAXFF_BONDS_LOCAL_KOKKOS_H -#define LMP_COMPUTE_REAXFF_BONDS_LOCAL_KOKKOS_H +#ifndef LMP_COMPUTE_REAXFF_BONDS_KOKKOS_H +#define LMP_COMPUTE_REAXFF_BONDS_KOKKOS_H -#include "compute_reaxff_bonds_local.h" +#include "compute_reaxff_bonds.h" #include "pair_reaxff_kokkos.h" #include "kokkos_type.h" namespace LAMMPS_NS { template -class ComputeReaxFFBondsLocalKokkos : public Compute { +class ComputeReaxFFBondsKokkos : public ComputeReaxFFBonds { public: using device_type = DeviceType; using AT = ArrayTypes; - ComputeReaxFFBondsLocalKokkos(class LAMMPS *, int, char **); - ~ComputeReaxFFBondsLocalKokkos() override; + ComputeReaxFFBondsKokkos(class LAMMPS *, int, char **); + ~ComputeReaxFFBondsKokkos() override; void init() override; void compute_local() override; + void compute_peratom() override; + void compute_bonds() override; double memory_usage() override; private: - int nlocal; - int nvalues; - int prev_nvalues; - - double **alocal; - typename AT::tdual_float_2d k_alocal; - PairReaxFF *reaxff; + int nbuf; + double *buf; + typename AT::tdual_float_1d k_buf; auto device_pair() { return dynamic_cast*>(reaxff); diff --git a/src/KOKKOS/compute_reaxff_bonds_local_kokkos.cpp b/src/KOKKOS/compute_reaxff_bonds_local_kokkos.cpp deleted file mode 100644 index a56d243ed6..0000000000 --- a/src/KOKKOS/compute_reaxff_bonds_local_kokkos.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// 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. -------------------------------------------------------------------------- */ - -/* ---------------------------------------------------------------------- - Contributing author: Richard Berger (LANL) -------------------------------------------------------------------------- */ - -#include "compute_reaxff_bonds_local_kokkos.h" -#include "atom.h" -#include "molecule.h" -#include "update.h" -#include "force.h" -#include "memory.h" -#include "error.h" -#include "neigh_list.h" - -#include "memory_kokkos.h" -#include "pair_reaxff_kokkos.h" -#include "reaxff_api.h" - -using namespace LAMMPS_NS; -using namespace ReaxFF; - -/* ---------------------------------------------------------------------- */ - -template -ComputeReaxFFBondsLocalKokkos::ComputeReaxFFBondsLocalKokkos(LAMMPS *lmp, int narg, char **arg) : - Compute(lmp, narg, arg), - alocal(nullptr), reaxff(nullptr) -{ - if (atom->tag_consecutive() == 0) - error->all(FLERR,"Atom IDs must be consecutive for compute reaxff/bonds/local"); - - local_flag = 1; - - nvalues = 7 + 2*MAXREAXBOND; - prev_nvalues = 0; - - // initialize output - - nlocal = atom->nlocal; - - size_local_rows = atom->nlocal; - size_local_cols = 7 + 2*MAXREAXBOND; - printf("RUNNING KOKKOS VERSION\n"); -} - - -/* ---------------------------------------------------------------------- */ - -template -ComputeReaxFFBondsLocalKokkos::~ComputeReaxFFBondsLocalKokkos() -{ - memoryKK->destroy_kokkos(k_alocal, alocal); -} - -/* ---------------------------------------------------------------------- */ - -template -void ComputeReaxFFBondsLocalKokkos::init() -{ - reaxff = dynamic_cast(force->pair_match("^reax../kk",0)); - if (reaxff == nullptr) error->all(FLERR,"Cannot use fix reaxff/bonds without " - "pair_style reaxff/kk"); -} - -/* ---------------------------------------------------------------------- */ - -template -void ComputeReaxFFBondsLocalKokkos::compute_local() -{ - invoked_local = update->ntimestep; - - int maxnumbonds = 0; - if (reaxff->execution_space == Device) - device_pair()->FindBond(maxnumbonds); - else - host_pair()->FindBond(maxnumbonds); - nvalues = 7+2*maxnumbonds; - - if(atom->nlocal > nlocal || nvalues > prev_nvalues) { - nlocal = atom->nlocal; - memoryKK->destroy_kokkos(k_alocal, alocal); - memoryKK->create_kokkos(k_alocal, alocal, atom->nlocal, nvalues,"reaxff/bonds/local:alocal"); - prev_nvalues = nvalues; - array_local = alocal; - } - - size_local_rows = nlocal; - size_local_cols = nvalues; - - if (reaxff->execution_space == Device) - device_pair()->PackBondInfo(k_alocal); - else - host_pair()->PackBondInfo(k_alocal); -} - -/* ---------------------------------------------------------------------- - memory usage of local data -------------------------------------------------------------------------- */ - -template -double ComputeReaxFFBondsLocalKokkos::memory_usage() -{ - double bytes = (double)nlocal*nvalues * sizeof(double); - return bytes; -} - -namespace LAMMPS_NS { -template class ComputeReaxFFBondsLocalKokkos; -#ifdef LMP_KOKKOS_GPU -template class ComputeReaxFFBondsLocalKokkos; -#endif -} diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index fbf90f140d..c7d54b80cd 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -4290,61 +4290,6 @@ void PairReaxFFKokkos::pack_bond_buffer_item(int i, int &j, const bo /* ---------------------------------------------------------------------- */ -template -void PairReaxFFKokkos::PackBondInfo(DAT::tdual_float_2d k_alocal) -{ - d_alocal = k_alocal.view(); - k_params_sing.template sync(); - atomKK->sync(execution_space,TAG_MASK|TYPE_MASK|Q_MASK|MOLECULE_MASK); - - tag = atomKK->k_tag.view(); - type = atomKK->k_type.view(); - q = atomKK->k_q.view(); - if (atom->molecule) - molecule = atomKK->k_molecule.view(); - - copymode = 1; - nlocal = atomKK->nlocal; - PairReaxKokkosPackBondInfoFunctor pack_bond_info_functor(this); - Kokkos::parallel_for(nlocal, pack_bond_info_functor); - copymode = 0; - - k_alocal.modify(); - k_alocal.sync(); -} - -template -KOKKOS_INLINE_FUNCTION -void PairReaxFFKokkos::pack_bond_info_item(const int i) const -{ - const int numbonds = d_numneigh_bonds[i]; - d_alocal(i,0) = tag[i]; - d_alocal(i,1) = type[i]; - d_alocal(i,2) = numbonds; - - int j = 3; - - for (int k = 0; k < numbonds; k++) { - d_alocal(i,j++) = d_neighid(i,k); - } - - d_alocal(i,j++) = molecule.data() ? molecule[i] : 0.0; - - for (int k = 0; k < numbonds; k++) { - d_alocal(i,j++) = d_abo(i,k); - } - - d_alocal(i,j++) = d_total_bo[i]; - d_alocal(i,j++) = paramssing(type[i]).nlp_opt - d_Delta_lp[i]; - d_alocal(i,j++) = q[i]; - - for(; j < d_alocal.extent(1); ++j) { - d_alocal(i,j) = 0.0; - } -} - -/* ---------------------------------------------------------------------- */ - template void PairReaxFFKokkos::FindBondSpecies() { diff --git a/src/KOKKOS/pair_reaxff_kokkos.h b/src/KOKKOS/pair_reaxff_kokkos.h index 32007e9970..1ad0955a1e 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.h +++ b/src/KOKKOS/pair_reaxff_kokkos.h @@ -135,7 +135,6 @@ class PairReaxFFKokkos : public PairReaxFF { double memory_usage(); void FindBond(int &); void PackBondBuffer(DAT::tdual_ffloat_1d, int &); - void PackBondInfo(DAT::tdual_float_2d); void FindBondSpecies(); template @@ -293,9 +292,6 @@ class PairReaxFFKokkos : public PairReaxFF { KOKKOS_INLINE_FUNCTION void pack_bond_buffer_item(int, int&, const bool&) const; - KOKKOS_INLINE_FUNCTION - void pack_bond_info_item(const int) const; - KOKKOS_INLINE_FUNCTION void operator()(TagPairReaxFindBondSpeciesZero, const int&) const; @@ -510,8 +506,6 @@ class PairReaxFFKokkos : public PairReaxFF { typename AT::t_ffloat_1d d_buf; DAT::tdual_int_scalar k_nbuf_local; - typename AT::t_float_2d d_alocal; - typedef Kokkos::View t_reax_int4_2d; t_reax_int4_2d d_angular_pack, d_torsion_pack; @@ -555,19 +549,6 @@ struct PairReaxKokkosPackBondBufferFunctor { } }; -template -struct PairReaxKokkosPackBondInfoFunctor { - using device_type = DeviceType; - using value_type = int; - PairReaxFFKokkos c; - PairReaxKokkosPackBondInfoFunctor(PairReaxFFKokkos* c_ptr):c(*c_ptr) {}; - - KOKKOS_INLINE_FUNCTION - void operator()(const int i) const { - c.pack_bond_info_item(i); - } -}; - } #endif From ca143e6ba8da5fba4e44e06472a84ec10fd0f4fe Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Tue, 17 Oct 2023 16:40:03 -0600 Subject: [PATCH 070/189] undo minor change --- examples/reaxff/in.reaxff.tatb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/reaxff/in.reaxff.tatb b/examples/reaxff/in.reaxff.tatb index fd0d846154..f422943a13 100644 --- a/examples/reaxff/in.reaxff.tatb +++ b/examples/reaxff/in.reaxff.tatb @@ -35,7 +35,7 @@ compute bonds all reaxff/bonds variable nqeq equal f_2 # dumps out the local bond information -dump 1 all local 5 bonds_compute.reaxff c_bonds[1] c_bonds[2] c_bonds[3] +dump 1 all local 5 bonds_local.reaxff c_bonds[1] c_bonds[2] c_bonds[3] # dumps out the peratom bond information dump 2 all custom 5 bonds_atom.reaxff c_bonds[*] @@ -57,6 +57,6 @@ timestep 0.0625 # axes yes 0.8 0.02 view 60 -30 #dump_modify 3 pad 3 -#fix 3 all reaxff/species 1 5 5 species.tatb +fix 3 all reaxff/species 1 5 5 species.tatb run 25 From 717e7b064965ec575eeb25ecbbd2d510d9e22100 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Wed, 18 Oct 2023 11:30:16 -0600 Subject: [PATCH 071/189] Address comments --- src/KOKKOS/compute_reaxff_bonds_kokkos.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp b/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp index 921acb9193..5c8885ce7a 100644 --- a/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp +++ b/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp @@ -39,6 +39,7 @@ ComputeReaxFFBondsKokkos::ComputeReaxFFBondsKokkos(LAMMPS *lmp, int ComputeReaxFFBonds(lmp, narg, arg), nbuf(-1), buf(nullptr) { + kokkosable = 1; } @@ -113,9 +114,8 @@ void ComputeReaxFFBondsKokkos::compute_local() { invoked_local = update->ntimestep; - if(invoked_bonds < update->ntimestep) { + if(invoked_bonds < update->ntimestep) compute_bonds(); - } if(nbonds > prev_nbonds) { // grow array_local @@ -151,9 +151,8 @@ void ComputeReaxFFBondsKokkos::compute_peratom() { invoked_peratom = update->ntimestep; - if(invoked_bonds < update->ntimestep) { + if(invoked_bonds < update->ntimestep) compute_bonds(); - } // extract peratom bond information from buffer int j = 1; From 9bffeb9512b595dd7fd2db4b27c7c46d3e45a034 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Wed, 18 Oct 2023 16:21:10 -0600 Subject: [PATCH 072/189] Next iteration --- examples/reaxff/in.reaxff.tatb | 2 +- src/KOKKOS/compute_reaxff_bonds_kokkos.cpp | 47 +++++++++--------- src/KOKKOS/compute_reaxff_bonds_kokkos.h | 4 +- src/KOKKOS/pair_reaxff_kokkos.cpp | 57 ++++++++++++++++++++++ src/KOKKOS/pair_reaxff_kokkos.h | 17 +++++++ src/REAXFF/compute_reaxff_bonds.cpp | 30 +++++------- src/REAXFF/compute_reaxff_bonds.h | 2 +- 7 files changed, 114 insertions(+), 45 deletions(-) diff --git a/examples/reaxff/in.reaxff.tatb b/examples/reaxff/in.reaxff.tatb index f422943a13..50f572a994 100644 --- a/examples/reaxff/in.reaxff.tatb +++ b/examples/reaxff/in.reaxff.tatb @@ -38,7 +38,7 @@ variable nqeq equal f_2 dump 1 all local 5 bonds_local.reaxff c_bonds[1] c_bonds[2] c_bonds[3] # dumps out the peratom bond information -dump 2 all custom 5 bonds_atom.reaxff c_bonds[*] +dump 2 all custom 5 bonds_atom.reaxff id type q c_bonds[*] thermo 5 thermo_style custom step temp epair etotal press & diff --git a/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp b/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp index 5c8885ce7a..f36832e6ac 100644 --- a/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp +++ b/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp @@ -68,7 +68,7 @@ void ComputeReaxFFBondsKokkos::compute_bonds() if (atom->nlocal > nlocal) { memory->destroy(array_atom); nlocal = atom->nlocal; - memory->create(array_atom, nlocal, 7, "reaxff/bonds:array_atom"); + memory->create(array_atom, nlocal, 3, "reaxff/bonds:array_atom"); } // retrieve bond information from kokkos pair style. the data potentially @@ -76,13 +76,14 @@ void ComputeReaxFFBondsKokkos::compute_bonds() // compute_local and compute_atom then expand the data from this buffer into // appropiate arrays for consumption by others (e.g. dump local, dump custom // or library interface) + int maxnumbonds = 0; if (reaxff->execution_space == Device) device_pair()->FindBond(maxnumbonds); else host_pair()->FindBond(maxnumbonds); - nbuf = 1+(maxnumbonds*2 + 7)*nlocal; + nbuf = (maxnumbonds*2 + 3)*nlocal; if(!buf || k_buf.extent(0) < nbuf) { memoryKK->destroy_kokkos(k_buf, buf); @@ -90,20 +91,21 @@ void ComputeReaxFFBondsKokkos::compute_bonds() } // Pass information to buffer, will sync to host + int nbuf_local; if (reaxff->execution_space == Device) - device_pair()->PackBondBuffer(k_buf, nbuf_local); + device_pair()->PackReducedBondBuffer(k_buf, nbuf_local); else - host_pair()->PackBondBuffer(k_buf, nbuf_local); - buf[0] = nlocal; + host_pair()->PackReducedBondBuffer(k_buf, nbuf_local); // Extract number of bonds from buffer + nbonds = 0; - int j = 1; + int j = 0; for (int i = 0; i < nlocal; i++) { - int numbonds = static_cast(buf[j+5]); + int numbonds = static_cast(buf[j+2]); nbonds += numbonds; - j += 2*numbonds + 7; + j += 2*numbonds + 3; } } @@ -127,20 +129,21 @@ void ComputeReaxFFBondsKokkos::compute_local() size_local_rows = nbonds; // extract local bond information from buffer + int b = 0; - int j = 1; + int j = 0; for (int i = 0; i < nlocal; ++i) { - const int numbonds = static_cast(buf[j+5]); - const int neigh_offset = j + 6; - const int bo_offset = neigh_offset + numbonds + 1; + const int numbonds = static_cast(buf[j+2]); + const int neigh_offset = j + 3; + const int bo_offset = neigh_offset + numbonds; for (int k = 0; k < numbonds; k++) { auto bond = array_local[b++]; bond[0] = i; bond[1] = static_cast (buf[neigh_offset+k]); bond[2] = buf[bo_offset+k]; } - j += 2*numbonds + 7; + j += 2*numbonds + 3; } } @@ -155,19 +158,15 @@ void ComputeReaxFFBondsKokkos::compute_peratom() compute_bonds(); // extract peratom bond information from buffer - int j = 1; + + int j = 0; for (int i = 0; i < nlocal; ++i) { auto ptr = array_atom[i]; - int numbonds = static_cast(buf[j+5]); - const int mol_offset = j + 6 + numbonds; - ptr[0] = buf[j]; // jtag - ptr[1] = buf[j+1]; // itype + int numbonds = static_cast(buf[j+2]); + ptr[0] = buf[j]; // sbo + ptr[1] = buf[j+1]; // nlp ptr[2] = numbonds; - ptr[3] = buf[mol_offset]; // mol - ptr[4] = buf[j+2]; // sbo - ptr[5] = buf[j+3]; // nlp - ptr[6] = buf[j+4]; // q - j += 2*numbonds + 7; + j += 2*numbonds + 3; } } @@ -179,7 +178,7 @@ template double ComputeReaxFFBondsKokkos::memory_usage() { double bytes = (double)(nbonds*3) * sizeof(double); - bytes += (double)(nlocal*7) * sizeof(double); + bytes += (double)(nlocal*3) * sizeof(double); return bytes; } diff --git a/src/KOKKOS/compute_reaxff_bonds_kokkos.h b/src/KOKKOS/compute_reaxff_bonds_kokkos.h index 45020ffa81..48f3860283 100644 --- a/src/KOKKOS/compute_reaxff_bonds_kokkos.h +++ b/src/KOKKOS/compute_reaxff_bonds_kokkos.h @@ -52,11 +52,11 @@ class ComputeReaxFFBondsKokkos : public ComputeReaxFFBonds { typename AT::tdual_float_1d k_buf; auto device_pair() { - return dynamic_cast*>(reaxff); + return static_cast*>(reaxff); } auto host_pair() { - return dynamic_cast*>(reaxff); + return static_cast*>(reaxff); } }; diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index c7d54b80cd..e298eca2da 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -4247,6 +4247,30 @@ void PairReaxFFKokkos::PackBondBuffer(DAT::tdual_ffloat_1d k_buf, in nbuf_local = k_nbuf_local.h_view(); } +/* ---------------------------------------------------------------------- */ + +template +void PairReaxFFKokkos::PackReducedBondBuffer(DAT::tdual_ffloat_1d k_buf, int &nbuf_local) +{ + d_buf = k_buf.view(); + k_params_sing.template sync(); + + copymode = 1; + nlocal = atomKK->nlocal; + PairReaxKokkosPackReducedBondBufferFunctor pack_bond_buffer_functor(this); + Kokkos::parallel_scan(nlocal,pack_bond_buffer_functor); + copymode = 0; + + k_buf.modify(); + k_nbuf_local.modify(); + + k_buf.sync(); + k_nbuf_local.sync(); + nbuf_local = k_nbuf_local.h_view(); +} + +/* ---------------------------------------------------------------------- */ + template KOKKOS_INLINE_FUNCTION void PairReaxFFKokkos::pack_bond_buffer_item(int i, int &j, const bool &final) const @@ -4288,6 +4312,39 @@ void PairReaxFFKokkos::pack_bond_buffer_item(int i, int &j, const bo k_nbuf_local.view()() = j - 1; } +template +KOKKOS_INLINE_FUNCTION +void PairReaxFFKokkos::pack_reduced_bond_buffer_item(int i, int &j, const bool &final) const +{ + const int numbonds = d_numneigh_bonds[i]; + if (final) { + d_buf[j] = d_total_bo[i]; + d_buf[j+1] = paramssing(type[i]).nlp_opt - d_Delta_lp[i]; + d_buf[j+2] = numbonds; + } + + j += 3; + + if (final) { + for (int k = 0; k < numbonds; ++k) { + d_buf[j+k] = d_neighid(i,k); + } + } + + j += numbonds; + + if (final) { + for (int k = 0; k < numbonds; k++) { + d_buf[j+k] = d_abo(i,k); + } + } + + j += numbonds; + + if (final && i == nlocal-1) + k_nbuf_local.view()() = j - 1; +} + /* ---------------------------------------------------------------------- */ template diff --git a/src/KOKKOS/pair_reaxff_kokkos.h b/src/KOKKOS/pair_reaxff_kokkos.h index 1ad0955a1e..571dd63fd1 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.h +++ b/src/KOKKOS/pair_reaxff_kokkos.h @@ -135,6 +135,7 @@ class PairReaxFFKokkos : public PairReaxFF { double memory_usage(); void FindBond(int &); void PackBondBuffer(DAT::tdual_ffloat_1d, int &); + void PackReducedBondBuffer(DAT::tdual_ffloat_1d, int &); void FindBondSpecies(); template @@ -292,6 +293,9 @@ class PairReaxFFKokkos : public PairReaxFF { KOKKOS_INLINE_FUNCTION void pack_bond_buffer_item(int, int&, const bool&) const; + KOKKOS_INLINE_FUNCTION + void pack_reduced_bond_buffer_item(int, int&, const bool&) const; + KOKKOS_INLINE_FUNCTION void operator()(TagPairReaxFindBondSpeciesZero, const int&) const; @@ -549,6 +553,19 @@ struct PairReaxKokkosPackBondBufferFunctor { } }; +template +struct PairReaxKokkosPackReducedBondBufferFunctor { + typedef DeviceType device_type; + typedef int value_type; + PairReaxFFKokkos c; + PairReaxKokkosPackReducedBondBufferFunctor(PairReaxFFKokkos* c_ptr):c(*c_ptr) {}; + + KOKKOS_INLINE_FUNCTION + void operator()(const int ii, int &j, const bool &final) const { + c.pack_reduced_bond_buffer_item(ii,j,final); + } +}; + } #endif diff --git a/src/REAXFF/compute_reaxff_bonds.cpp b/src/REAXFF/compute_reaxff_bonds.cpp index 7f1605263c..3b3520fda3 100644 --- a/src/REAXFF/compute_reaxff_bonds.cpp +++ b/src/REAXFF/compute_reaxff_bonds.cpp @@ -35,7 +35,7 @@ using namespace ReaxFF; ComputeReaxFFBonds::ComputeReaxFFBonds(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg), - abo(nullptr), neighid(nullptr), numneigh(nullptr), reaxff(nullptr) + abo(nullptr), neighid(nullptr), bondcount(nullptr), reaxff(nullptr) { if (atom->tag_consecutive() == 0) error->all(FLERR,"Atom IDs must be consecutive for compute reaxff/bonds"); @@ -48,7 +48,7 @@ ComputeReaxFFBonds::ComputeReaxFFBonds(LAMMPS *lmp, int narg, char **arg) : nlocal = -1; prev_nbonds = -1; - size_peratom_cols = 7; + size_peratom_cols = 3; size_local_rows = 0; size_local_cols = 3; @@ -65,7 +65,7 @@ ComputeReaxFFBonds::~ComputeReaxFFBonds() memory->destroy(array_atom); memory->destroy(abo); memory->destroy(neighid); - memory->destroy(numneigh); + memory->destroy(bondcount); } /* ---------------------------------------------------------------------- */ @@ -110,7 +110,7 @@ int ComputeReaxFFBonds::FindBond() nj++; } } - numneigh[i] = nj; + bondcount[i] = nj; numbonds += nj; } return numbonds; @@ -126,17 +126,17 @@ void ComputeReaxFFBonds::compute_bonds() if (atom->nlocal > nlocal) { memory->destroy(abo); memory->destroy(neighid); - memory->destroy(numneigh); + memory->destroy(bondcount); memory->destroy(array_atom); nlocal = atom->nlocal; memory->create(abo, nlocal, MAXREAXBOND, "reaxff/bonds:abo"); memory->create(neighid, nlocal, MAXREAXBOND, "reaxff/bonds:neighid"); - memory->create(numneigh, nlocal, "reaxff/bonds:numneigh"); - memory->create(array_atom, nlocal, 7, "reaxff/bonds:array_atom"); + memory->create(bondcount, nlocal, "reaxff/bonds:bondcount"); + memory->create(array_atom, nlocal, 3, "reaxff/bonds:array_atom"); } for (int i = 0; i < nlocal; i++) { - numneigh[i] = 0; + bondcount[i] = 0; for (int j = 0; j < MAXREAXBOND; j++) { neighid[i][j] = 0; abo[i][j] = 0.0; @@ -168,7 +168,7 @@ void ComputeReaxFFBonds::compute_local() int b = 0; for (int i = 0; i < nlocal; ++i) { - const int numbonds = numneigh[i]; + const int numbonds = bondcount[i]; for (int k = 0; k < numbonds; k++) { auto bond = array_local[b++]; @@ -191,13 +191,9 @@ void ComputeReaxFFBonds::compute_peratom() for (int i = 0; i < nlocal; ++i) { auto ptr = array_atom[i]; - ptr[0] = atom->tag[i]; - ptr[1] = atom->type[i]; - ptr[2] = numneigh[i]; - ptr[3] = (atom->molecule == nullptr) ? 0.0 : atom->molecule[i]; - ptr[4] = reaxff->api->workspace->total_bond_order[i]; - ptr[5] = reaxff->api->workspace->nlp[i]; - ptr[6] = atom->q[i]; + ptr[0] = reaxff->api->workspace->total_bond_order[i]; + ptr[1] = reaxff->api->workspace->nlp[i]; + ptr[2] = bondcount[i]; } } @@ -208,7 +204,7 @@ void ComputeReaxFFBonds::compute_peratom() double ComputeReaxFFBonds::memory_usage() { double bytes = (double)(nbonds*3) * sizeof(double); - bytes += (double)(nlocal*7) * sizeof(double); + bytes += (double)(nlocal*3) * sizeof(double); bytes += (double)(2*nlocal*MAXREAXBOND) * sizeof(double); bytes += (double)(nlocal) * sizeof(int); return bytes; diff --git a/src/REAXFF/compute_reaxff_bonds.h b/src/REAXFF/compute_reaxff_bonds.h index 6b00cef7ed..b876c9e02d 100644 --- a/src/REAXFF/compute_reaxff_bonds.h +++ b/src/REAXFF/compute_reaxff_bonds.h @@ -46,7 +46,7 @@ class ComputeReaxFFBonds : public Compute { tagint **neighid; double **abo; - int *numneigh; + int *bondcount; class PairReaxFF *reaxff; private: From afd0107f01ff552aba336ccc02af240f3aed33a8 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Wed, 8 Nov 2023 14:17:18 -0700 Subject: [PATCH 073/189] Add new files to makefile build system --- src/KOKKOS/Install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/KOKKOS/Install.sh b/src/KOKKOS/Install.sh index 489efc55a0..01db058d5b 100755 --- a/src/KOKKOS/Install.sh +++ b/src/KOKKOS/Install.sh @@ -165,6 +165,8 @@ action fix_qeq_reaxff_kokkos.cpp fix_qeq_reaxff.cpp action fix_qeq_reaxff_kokkos.h fix_qeq_reaxff.h action fix_reaxff_bonds_kokkos.cpp fix_reaxff_bonds.cpp action fix_reaxff_bonds_kokkos.h fix_reaxff_bonds.h +action compute_reaxff_bonds_kokkos.cpp compute_reaxff_bonds.cpp +action compute_reaxff_bonds_kokkos.h compute_reaxff_bonds.h action fix_reaxff_species_kokkos.cpp fix_reaxff_species.cpp action fix_reaxff_species_kokkos.h fix_reaxff_species.h action fix_rx_kokkos.cpp fix_rx.cpp From 16f0806da07289e3aea388b453e03ba24f042120 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Mon, 20 Nov 2023 15:36:46 -0700 Subject: [PATCH 074/189] Rename compute to reaxff/atom --- examples/reaxff/in.reaxff.tatb | 2 +- src/KOKKOS/Install.sh | 4 +- ...kos.cpp => compute_reaxff_atom_kokkos.cpp} | 46 ++++++------- ..._kokkos.h => compute_reaxff_atom_kokkos.h} | 14 ++-- src/KOKKOS/pair_reaxff_kokkos.cpp | 35 ++++++---- src/KOKKOS/pair_reaxff_kokkos.h | 7 +- ...axff_bonds.cpp => compute_reaxff_atom.cpp} | 64 ++++++++++++------- ...e_reaxff_bonds.h => compute_reaxff_atom.h} | 13 ++-- 8 files changed, 108 insertions(+), 77 deletions(-) rename src/KOKKOS/{compute_reaxff_bonds_kokkos.cpp => compute_reaxff_atom_kokkos.cpp} (76%) rename src/KOKKOS/{compute_reaxff_bonds_kokkos.h => compute_reaxff_atom_kokkos.h} (79%) rename src/REAXFF/{compute_reaxff_bonds.cpp => compute_reaxff_atom.cpp} (74%) rename src/REAXFF/{compute_reaxff_bonds.h => compute_reaxff_atom.h} (84%) diff --git a/examples/reaxff/in.reaxff.tatb b/examples/reaxff/in.reaxff.tatb index 50f572a994..967ed0a1d6 100644 --- a/examples/reaxff/in.reaxff.tatb +++ b/examples/reaxff/in.reaxff.tatb @@ -31,7 +31,7 @@ neigh_modify delay 0 every 5 check no fix 1 all nve fix 2 all qeq/reaxff 1 0.0 10.0 1.0e-6 reaxff fix 4 all reaxff/bonds 5 bonds.reaxff -compute bonds all reaxff/bonds +compute bonds all reaxff/atom bonds yes variable nqeq equal f_2 # dumps out the local bond information diff --git a/src/KOKKOS/Install.sh b/src/KOKKOS/Install.sh index 01db058d5b..28fe6d5cd6 100755 --- a/src/KOKKOS/Install.sh +++ b/src/KOKKOS/Install.sh @@ -165,8 +165,8 @@ action fix_qeq_reaxff_kokkos.cpp fix_qeq_reaxff.cpp action fix_qeq_reaxff_kokkos.h fix_qeq_reaxff.h action fix_reaxff_bonds_kokkos.cpp fix_reaxff_bonds.cpp action fix_reaxff_bonds_kokkos.h fix_reaxff_bonds.h -action compute_reaxff_bonds_kokkos.cpp compute_reaxff_bonds.cpp -action compute_reaxff_bonds_kokkos.h compute_reaxff_bonds.h +action compute_reaxff_atom_kokkos.cpp compute_reaxff_atom.cpp +action compute_reaxff_atom_kokkos.h compute_reaxff_atom.h action fix_reaxff_species_kokkos.cpp fix_reaxff_species.cpp action fix_reaxff_species_kokkos.h fix_reaxff_species.h action fix_rx_kokkos.cpp fix_rx.cpp diff --git a/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp similarity index 76% rename from src/KOKKOS/compute_reaxff_bonds_kokkos.cpp rename to src/KOKKOS/compute_reaxff_atom_kokkos.cpp index f36832e6ac..2485d9ae72 100644 --- a/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp +++ b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp @@ -16,7 +16,7 @@ Contributing author: Richard Berger (LANL) ------------------------------------------------------------------------- */ -#include "compute_reaxff_bonds_kokkos.h" +#include "compute_reaxff_atom_kokkos.h" #include "atom.h" #include "molecule.h" #include "update.h" @@ -35,8 +35,8 @@ using namespace ReaxFF; /* ---------------------------------------------------------------------- */ template -ComputeReaxFFBondsKokkos::ComputeReaxFFBondsKokkos(LAMMPS *lmp, int narg, char **arg) : - ComputeReaxFFBonds(lmp, narg, arg), +ComputeReaxFFAtomKokkos::ComputeReaxFFAtomKokkos(LAMMPS *lmp, int narg, char **arg) : + ComputeReaxFFAtom(lmp, narg, arg), nbuf(-1), buf(nullptr) { kokkosable = 1; @@ -46,7 +46,7 @@ ComputeReaxFFBondsKokkos::ComputeReaxFFBondsKokkos(LAMMPS *lmp, int /* ---------------------------------------------------------------------- */ template -ComputeReaxFFBondsKokkos::~ComputeReaxFFBondsKokkos() +ComputeReaxFFAtomKokkos::~ComputeReaxFFAtomKokkos() { memoryKK->destroy_kokkos(k_buf, buf); } @@ -54,21 +54,21 @@ ComputeReaxFFBondsKokkos::~ComputeReaxFFBondsKokkos() /* ---------------------------------------------------------------------- */ template -void ComputeReaxFFBondsKokkos::init() +void ComputeReaxFFAtomKokkos::init() { reaxff = dynamic_cast(force->pair_match("^reax../kk",0)); - if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/bonds without " + if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/atom without " "pair_style reaxff/kk"); } /* ---------------------------------------------------------------------- */ template -void ComputeReaxFFBondsKokkos::compute_bonds() +void ComputeReaxFFAtomKokkos::compute_bonds() { if (atom->nlocal > nlocal) { memory->destroy(array_atom); nlocal = atom->nlocal; - memory->create(array_atom, nlocal, 3, "reaxff/bonds:array_atom"); + memory->create(array_atom, nlocal, 3, "reaxff/atom:array_atom"); } // retrieve bond information from kokkos pair style. the data potentially @@ -83,20 +83,20 @@ void ComputeReaxFFBondsKokkos::compute_bonds() else host_pair()->FindBond(maxnumbonds); - nbuf = (maxnumbonds*2 + 3)*nlocal; + nbuf = ((store_bonds ? maxnumbonds*2 : 0) + 3)*nlocal; if(!buf || k_buf.extent(0) < nbuf) { memoryKK->destroy_kokkos(k_buf, buf); - memoryKK->create_kokkos(k_buf, buf, nbuf, "reaxff/bonds:buf"); + memoryKK->create_kokkos(k_buf, buf, nbuf, "reaxff/atom:buf"); } // Pass information to buffer, will sync to host int nbuf_local; if (reaxff->execution_space == Device) - device_pair()->PackReducedBondBuffer(k_buf, nbuf_local); + device_pair()->PackReducedBondBuffer(k_buf, nbuf_local, store_bonds); else - host_pair()->PackReducedBondBuffer(k_buf, nbuf_local); + host_pair()->PackReducedBondBuffer(k_buf, nbuf_local, store_bonds); // Extract number of bonds from buffer @@ -105,14 +105,14 @@ void ComputeReaxFFBondsKokkos::compute_bonds() for (int i = 0; i < nlocal; i++) { int numbonds = static_cast(buf[j+2]); nbonds += numbonds; - j += 2*numbonds + 3; + j += (store_bonds ? 2*numbonds : 0) + 3; } } /* ---------------------------------------------------------------------- */ template -void ComputeReaxFFBondsKokkos::compute_local() +void ComputeReaxFFAtomKokkos::compute_local() { invoked_local = update->ntimestep; @@ -122,7 +122,7 @@ void ComputeReaxFFBondsKokkos::compute_local() if(nbonds > prev_nbonds) { // grow array_local memory->destroy(array_local); - memory->create(array_local, nbonds, 3, "reaxff/bonds:array_local"); + memory->create(array_local, nbonds, 3, "reaxff/atom:array_local"); prev_nbonds = nbonds; } @@ -150,7 +150,7 @@ void ComputeReaxFFBondsKokkos::compute_local() /* ---------------------------------------------------------------------- */ template -void ComputeReaxFFBondsKokkos::compute_peratom() +void ComputeReaxFFAtomKokkos::compute_peratom() { invoked_peratom = update->ntimestep; @@ -166,7 +166,7 @@ void ComputeReaxFFBondsKokkos::compute_peratom() ptr[0] = buf[j]; // sbo ptr[1] = buf[j+1]; // nlp ptr[2] = numbonds; - j += 2*numbonds + 3; + j += (store_bonds ? 2*numbonds : 0) + 3; } } @@ -175,16 +175,18 @@ void ComputeReaxFFBondsKokkos::compute_peratom() ------------------------------------------------------------------------- */ template -double ComputeReaxFFBondsKokkos::memory_usage() +double ComputeReaxFFAtomKokkos::memory_usage() { - double bytes = (double)(nbonds*3) * sizeof(double); - bytes += (double)(nlocal*3) * sizeof(double); + double bytes = (double)(nlocal*3) * sizeof(double); + if(store_bonds) + bytes += (double)(nbonds*3) * sizeof(double); + bytes += (double)(nbuf > 0 ? nbuf * sizeof(double) : 0); return bytes; } namespace LAMMPS_NS { -template class ComputeReaxFFBondsKokkos; +template class ComputeReaxFFAtomKokkos; #ifdef LMP_KOKKOS_GPU -template class ComputeReaxFFBondsKokkos; +template class ComputeReaxFFAtomKokkos; #endif } diff --git a/src/KOKKOS/compute_reaxff_bonds_kokkos.h b/src/KOKKOS/compute_reaxff_atom_kokkos.h similarity index 79% rename from src/KOKKOS/compute_reaxff_bonds_kokkos.h rename to src/KOKKOS/compute_reaxff_atom_kokkos.h index 48f3860283..7037c7e308 100644 --- a/src/KOKKOS/compute_reaxff_bonds_kokkos.h +++ b/src/KOKKOS/compute_reaxff_atom_kokkos.h @@ -17,29 +17,29 @@ #ifdef COMPUTE_CLASS // clang-format off -ComputeStyle(reaxff/bonds/kk,ComputeReaxFFBondsKokkos); -ComputeStyle(reaxff/bonds/kk/device,ComputeReaxFFBondsKokkos); -ComputeStyle(reaxff/bonds/kk/host,ComputeReaxFFBondsKokkos); +ComputeStyle(reaxff/atom/kk,ComputeReaxFFAtomKokkos); +ComputeStyle(reaxff/atom/kk/device,ComputeReaxFFAtomKokkos); +ComputeStyle(reaxff/atom/kk/host,ComputeReaxFFAtomKokkos); // clang-format on #else #ifndef LMP_COMPUTE_REAXFF_BONDS_KOKKOS_H #define LMP_COMPUTE_REAXFF_BONDS_KOKKOS_H -#include "compute_reaxff_bonds.h" +#include "compute_reaxff_atom.h" #include "pair_reaxff_kokkos.h" #include "kokkos_type.h" namespace LAMMPS_NS { template -class ComputeReaxFFBondsKokkos : public ComputeReaxFFBonds { +class ComputeReaxFFAtomKokkos : public ComputeReaxFFAtom { public: using device_type = DeviceType; using AT = ArrayTypes; - ComputeReaxFFBondsKokkos(class LAMMPS *, int, char **); - ~ComputeReaxFFBondsKokkos() override; + ComputeReaxFFAtomKokkos(class LAMMPS *, int, char **); + ~ComputeReaxFFAtomKokkos() override; void init() override; void compute_local() override; void compute_peratom() override; diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index e298eca2da..914962f0e6 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -4250,15 +4250,21 @@ void PairReaxFFKokkos::PackBondBuffer(DAT::tdual_ffloat_1d k_buf, in /* ---------------------------------------------------------------------- */ template -void PairReaxFFKokkos::PackReducedBondBuffer(DAT::tdual_ffloat_1d k_buf, int &nbuf_local) +void PairReaxFFKokkos::PackReducedBondBuffer(DAT::tdual_ffloat_1d k_buf, int &nbuf_local, bool store_bonds) { d_buf = k_buf.view(); k_params_sing.template sync(); copymode = 1; nlocal = atomKK->nlocal; - PairReaxKokkosPackReducedBondBufferFunctor pack_bond_buffer_functor(this); - Kokkos::parallel_scan(nlocal,pack_bond_buffer_functor); + if(store_bonds) { + PairReaxKokkosPackReducedBondBufferFunctor pack_bond_buffer_functor(this); + Kokkos::parallel_scan(nlocal,pack_bond_buffer_functor); + } else { + PairReaxKokkosPackReducedBondBufferFunctor pack_bond_buffer_functor(this); + Kokkos::parallel_scan(nlocal,pack_bond_buffer_functor); + } + copymode = 0; k_buf.modify(); @@ -4313,6 +4319,7 @@ void PairReaxFFKokkos::pack_bond_buffer_item(int i, int &j, const bo } template +template KOKKOS_INLINE_FUNCTION void PairReaxFFKokkos::pack_reduced_bond_buffer_item(int i, int &j, const bool &final) const { @@ -4325,21 +4332,23 @@ void PairReaxFFKokkos::pack_reduced_bond_buffer_item(int i, int &j, j += 3; - if (final) { - for (int k = 0; k < numbonds; ++k) { - d_buf[j+k] = d_neighid(i,k); + if constexpr(STORE_BONDS) { + if (final) { + for (int k = 0; k < numbonds; ++k) { + d_buf[j+k] = d_neighid(i,k); + } } - } - j += numbonds; + j += numbonds; - if (final) { - for (int k = 0; k < numbonds; k++) { - d_buf[j+k] = d_abo(i,k); + if (final) { + for (int k = 0; k < numbonds; k++) { + d_buf[j+k] = d_abo(i,k); + } } - } - j += numbonds; + j += numbonds; + } if (final && i == nlocal-1) k_nbuf_local.view()() = j - 1; diff --git a/src/KOKKOS/pair_reaxff_kokkos.h b/src/KOKKOS/pair_reaxff_kokkos.h index 571dd63fd1..f246afcc86 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.h +++ b/src/KOKKOS/pair_reaxff_kokkos.h @@ -135,7 +135,7 @@ class PairReaxFFKokkos : public PairReaxFF { double memory_usage(); void FindBond(int &); void PackBondBuffer(DAT::tdual_ffloat_1d, int &); - void PackReducedBondBuffer(DAT::tdual_ffloat_1d, int &); + void PackReducedBondBuffer(DAT::tdual_ffloat_1d, int &, bool); void FindBondSpecies(); template @@ -293,6 +293,7 @@ class PairReaxFFKokkos : public PairReaxFF { KOKKOS_INLINE_FUNCTION void pack_bond_buffer_item(int, int&, const bool&) const; + template KOKKOS_INLINE_FUNCTION void pack_reduced_bond_buffer_item(int, int&, const bool&) const; @@ -553,7 +554,7 @@ struct PairReaxKokkosPackBondBufferFunctor { } }; -template +template struct PairReaxKokkosPackReducedBondBufferFunctor { typedef DeviceType device_type; typedef int value_type; @@ -562,7 +563,7 @@ struct PairReaxKokkosPackReducedBondBufferFunctor { KOKKOS_INLINE_FUNCTION void operator()(const int ii, int &j, const bool &final) const { - c.pack_reduced_bond_buffer_item(ii,j,final); + c.template pack_reduced_bond_buffer_item(ii,j,final); } }; diff --git a/src/REAXFF/compute_reaxff_bonds.cpp b/src/REAXFF/compute_reaxff_atom.cpp similarity index 74% rename from src/REAXFF/compute_reaxff_bonds.cpp rename to src/REAXFF/compute_reaxff_atom.cpp index 3b3520fda3..d975fe42f8 100644 --- a/src/REAXFF/compute_reaxff_bonds.cpp +++ b/src/REAXFF/compute_reaxff_atom.cpp @@ -16,7 +16,7 @@ Contributing author: Richard Berger (LANL) ------------------------------------------------------------------------- */ -#include "compute_reaxff_bonds.h" +#include "compute_reaxff_atom.h" #include "atom.h" #include "molecule.h" #include "update.h" @@ -33,14 +33,13 @@ using namespace ReaxFF; /* ---------------------------------------------------------------------- */ -ComputeReaxFFBonds::ComputeReaxFFBonds(LAMMPS *lmp, int narg, char **arg) : +ComputeReaxFFAtom::ComputeReaxFFAtom(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg), abo(nullptr), neighid(nullptr), bondcount(nullptr), reaxff(nullptr) { if (atom->tag_consecutive() == 0) - error->all(FLERR,"Atom IDs must be consecutive for compute reaxff/bonds"); + error->all(FLERR,"Atom IDs must be consecutive for compute reaxff/atom"); - local_flag = 1; peratom_flag = 1; // initialize output @@ -54,12 +53,25 @@ ComputeReaxFFBonds::ComputeReaxFFBonds(LAMMPS *lmp, int narg, char **arg) : size_local_cols = 3; invoked_bonds = -1; + + store_bonds = false; + + int iarg = 3; + while (iarg narg) utils::missing_cmd_args(FLERR, "compute reaxff/atom bonds", error); + store_bonds = utils::logical(FLERR, arg[iarg+1], false, lmp); + iarg += 2; + } else error->all(FLERR,"Illegal compute reaxff/atom command"); + } + + local_flag = store_bonds; } /* ---------------------------------------------------------------------- */ -ComputeReaxFFBonds::~ComputeReaxFFBonds() +ComputeReaxFFAtom::~ComputeReaxFFAtom() { memory->destroy(array_local); memory->destroy(array_atom); @@ -70,16 +82,16 @@ ComputeReaxFFBonds::~ComputeReaxFFBonds() /* ---------------------------------------------------------------------- */ -void ComputeReaxFFBonds::init() +void ComputeReaxFFAtom::init() { reaxff = dynamic_cast(force->pair_match("^reax..",0)); - if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/bonds without " + if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/atom without " "pair_style reaxff, reaxff/kk, or reaxff/omp"); } /* ---------------------------------------------------------------------- */ -int ComputeReaxFFBonds::FindBond() +int ComputeReaxFFAtom::FindBond() { int *ilist, i, ii, inum; int j, pj, nj; @@ -105,8 +117,10 @@ int ComputeReaxFFBonds::FindBond() bo_tmp = bo_ij->bo_data.BO; if (bo_tmp > bo_cut) { - neighid[i][nj] = jtag; - abo[i][nj] = bo_tmp; + if(store_bonds) { + neighid[i][nj] = jtag; + abo[i][nj] = bo_tmp; + } nj++; } } @@ -119,7 +133,7 @@ int ComputeReaxFFBonds::FindBond() /* ---------------------------------------------------------------------- */ -void ComputeReaxFFBonds::compute_bonds() +void ComputeReaxFFAtom::compute_bonds() { invoked_bonds = update->ntimestep; @@ -129,15 +143,17 @@ void ComputeReaxFFBonds::compute_bonds() memory->destroy(bondcount); memory->destroy(array_atom); nlocal = atom->nlocal; - memory->create(abo, nlocal, MAXREAXBOND, "reaxff/bonds:abo"); - memory->create(neighid, nlocal, MAXREAXBOND, "reaxff/bonds:neighid"); - memory->create(bondcount, nlocal, "reaxff/bonds:bondcount"); - memory->create(array_atom, nlocal, 3, "reaxff/bonds:array_atom"); + if(store_bonds) { + memory->create(abo, nlocal, MAXREAXBOND, "reaxff/atom:abo"); + memory->create(neighid, nlocal, MAXREAXBOND, "reaxff/atom:neighid"); + } + memory->create(bondcount, nlocal, "reaxff/atom:bondcount"); + memory->create(array_atom, nlocal, 3, "reaxff/atom:array_atom"); } for (int i = 0; i < nlocal; i++) { bondcount[i] = 0; - for (int j = 0; j < MAXREAXBOND; j++) { + for (int j = 0; store_bonds && j < MAXREAXBOND; j++) { neighid[i][j] = 0; abo[i][j] = 0.0; } @@ -148,7 +164,7 @@ void ComputeReaxFFBonds::compute_bonds() /* ---------------------------------------------------------------------- */ -void ComputeReaxFFBonds::compute_local() +void ComputeReaxFFAtom::compute_local() { invoked_local = update->ntimestep; @@ -159,7 +175,7 @@ void ComputeReaxFFBonds::compute_local() if(nbonds > prev_nbonds) { // grow array_local memory->destroy(array_local); - memory->create(array_local, nbonds, 3, "reaxff/bonds:array_local"); + memory->create(array_local, nbonds, 3, "reaxff/atom:array_local"); prev_nbonds = nbonds; } @@ -181,7 +197,7 @@ void ComputeReaxFFBonds::compute_local() /* ---------------------------------------------------------------------- */ -void ComputeReaxFFBonds::compute_peratom() +void ComputeReaxFFAtom::compute_peratom() { invoked_peratom = update->ntimestep; @@ -201,11 +217,13 @@ void ComputeReaxFFBonds::compute_peratom() memory usage of local data ------------------------------------------------------------------------- */ -double ComputeReaxFFBonds::memory_usage() +double ComputeReaxFFAtom::memory_usage() { - double bytes = (double)(nbonds*3) * sizeof(double); - bytes += (double)(nlocal*3) * sizeof(double); - bytes += (double)(2*nlocal*MAXREAXBOND) * sizeof(double); + double bytes = (double)(nlocal*3) * sizeof(double); bytes += (double)(nlocal) * sizeof(int); + if(store_bonds) { + bytes += (double)(2*nlocal*MAXREAXBOND) * sizeof(double); + bytes += (double)(nbonds*3) * sizeof(double); + } return bytes; } diff --git a/src/REAXFF/compute_reaxff_bonds.h b/src/REAXFF/compute_reaxff_atom.h similarity index 84% rename from src/REAXFF/compute_reaxff_bonds.h rename to src/REAXFF/compute_reaxff_atom.h index b876c9e02d..31b18e7238 100644 --- a/src/REAXFF/compute_reaxff_bonds.h +++ b/src/REAXFF/compute_reaxff_atom.h @@ -17,21 +17,21 @@ #ifdef COMPUTE_CLASS // clang-format off -ComputeStyle(reaxff/bonds,ComputeReaxFFBonds); +ComputeStyle(reaxff/atom,ComputeReaxFFAtom); // clang-format on #else -#ifndef LMP_COMPUTE_REAXFF_BONDS_H -#define LMP_COMPUTE_REAXFF_BONDS_H +#ifndef LMP_COMPUTE_REAXFF_ATOM_H +#define LMP_COMPUTE_REAXFF_ATOM_H #include "compute.h" namespace LAMMPS_NS { -class ComputeReaxFFBonds : public Compute { +class ComputeReaxFFAtom : public Compute { public: - ComputeReaxFFBonds(class LAMMPS *, int, char **); - ~ComputeReaxFFBonds() override; + ComputeReaxFFAtom(class LAMMPS *, int, char **); + ~ComputeReaxFFAtom() override; void init() override; void compute_local() override; void compute_peratom() override; @@ -43,6 +43,7 @@ class ComputeReaxFFBonds : public Compute { int nlocal; int nbonds; int prev_nbonds; + bool store_bonds; tagint **neighid; double **abo; From fd83ed4004a8d26b95f1f69e75b288804361bfd5 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Tue, 21 Nov 2023 13:42:51 -0700 Subject: [PATCH 075/189] compute reaxff/atom: add support for pair hybrid --- src/KOKKOS/compute_reaxff_atom_kokkos.cpp | 9 ++++--- src/REAXFF/compute_reaxff_atom.cpp | 30 ++++++++++++++++++++--- src/REAXFF/compute_reaxff_atom.h | 1 + 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/KOKKOS/compute_reaxff_atom_kokkos.cpp b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp index 2485d9ae72..845e8bc6e7 100644 --- a/src/KOKKOS/compute_reaxff_atom_kokkos.cpp +++ b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp @@ -56,9 +56,12 @@ ComputeReaxFFAtomKokkos::~ComputeReaxFFAtomKokkos() template void ComputeReaxFFAtomKokkos::init() { - reaxff = dynamic_cast(force->pair_match("^reax../kk",0)); - if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/atom without " - "pair_style reaxff/kk"); + ComputeReaxFFAtom::init(); + + if(!reaxff || !reaxff->kokkosable) { + error->all(FLERR,"Cannot use compute reaxff/atom/kk without " + "pair_style reaxff/kk"); + } } /* ---------------------------------------------------------------------- */ diff --git a/src/REAXFF/compute_reaxff_atom.cpp b/src/REAXFF/compute_reaxff_atom.cpp index d975fe42f8..1d7bdf0e7d 100644 --- a/src/REAXFF/compute_reaxff_atom.cpp +++ b/src/REAXFF/compute_reaxff_atom.cpp @@ -55,10 +55,21 @@ ComputeReaxFFAtom::ComputeReaxFFAtom(LAMMPS *lmp, int narg, char **arg) : invoked_bonds = -1; store_bonds = false; + nsub = 0; int iarg = 3; while (iarg narg) utils::missing_cmd_args(FLERR, "compute reaxff/atom pair", error); + ++iarg; + + if (isdigit(arg[iarg][0])) { + nsub = utils::inumeric(FLERR, arg[iarg], false, lmp); + ++iarg; + if (nsub > 0) continue; + } + error->all(FLERR, "Illegal compute reaxff/atom command"); + } else if (strcmp(arg[iarg], "bonds") == 0) { if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "compute reaxff/atom bonds", error); store_bonds = utils::logical(FLERR, arg[iarg+1], false, lmp); iarg += 2; @@ -84,9 +95,20 @@ ComputeReaxFFAtom::~ComputeReaxFFAtom() void ComputeReaxFFAtom::init() { - reaxff = dynamic_cast(force->pair_match("^reax..",0)); - if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/atom without " - "pair_style reaxff, reaxff/kk, or reaxff/omp"); + if (lmp->suffix_enable) { + if (lmp->suffix) + reaxff = dynamic_cast(force->pair_match(fmt::format("^reax../{}", lmp->suffix), 0, nsub)); + if (!reaxff && lmp->suffix2) + reaxff = dynamic_cast(force->pair_match(fmt::format("^reax../{}", lmp->suffix2), 0, nsub)); + } + + if (!reaxff) reaxff = dynamic_cast(force->pair_match("^reax..", 0, nsub)); + + if (!reaxff) error->all(FLERR,"Cannot use compute reaxff/atom without " + "pair_style reaxff or reaxff/omp"); + + if(reaxff->kokkosable && !kokkosable) + error->all(FLERR,"Cannot use compute reaxff/atom with pair_style reaxff/kk. Use reaxff/atom/kk."); } /* ---------------------------------------------------------------------- */ diff --git a/src/REAXFF/compute_reaxff_atom.h b/src/REAXFF/compute_reaxff_atom.h index 31b18e7238..1f9aaec1ae 100644 --- a/src/REAXFF/compute_reaxff_atom.h +++ b/src/REAXFF/compute_reaxff_atom.h @@ -43,6 +43,7 @@ class ComputeReaxFFAtom : public Compute { int nlocal; int nbonds; int prev_nbonds; + int nsub; bool store_bonds; tagint **neighid; From a5cc181358cab34f2edfa1ab79e04d71723bfa70 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Tue, 21 Nov 2023 14:03:10 -0700 Subject: [PATCH 076/189] Start with compute reaxff/atom documentation --- doc/src/Commands_compute.rst | 1 + doc/src/compute_reaxff_atom.rst | 67 +++++++++++++++++++++++++++++++++ doc/src/pair_reaxff.rst | 3 +- 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 doc/src/compute_reaxff_atom.rst diff --git a/doc/src/Commands_compute.rst b/doc/src/Commands_compute.rst index dbd6b58ce7..819a1b10d8 100644 --- a/doc/src/Commands_compute.rst +++ b/doc/src/Commands_compute.rst @@ -115,6 +115,7 @@ KOKKOS, o = OPENMP, t = OPT. * :doc:`property/grid ` * :doc:`property/local ` * :doc:`ptm/atom ` + * :doc:`reaxff/atom (k) ` * :doc:`rdf ` * :doc:`reduce ` * :doc:`reduce/chunk ` diff --git a/doc/src/compute_reaxff_atom.rst b/doc/src/compute_reaxff_atom.rst new file mode 100644 index 0000000000..4906f3fe5c --- /dev/null +++ b/doc/src/compute_reaxff_atom.rst @@ -0,0 +1,67 @@ +.. index:: fix reaxff/atom +.. index:: fix reaxff/atom/kk + +fix reaxff/atom command +======================= + +Accelerator Variants: *reaxff/atom/kk* + +Syntax +"""""" + +.. code-block:: LAMMPS + + compute ID group-ID reaxff/atom attribute args ... keyword value ... + +* ID, group-ID are documented in :doc:`compute ` command +* reaxff/atom = name of this compute command +* attribute = *pair* + + .. parsed-literal:: + + *pair* args = nsub + nsub = *n*-instance of a sub-style, if a pair style is used multiple times in a hybrid style + +* keyword = *bonds* + + .. parsed-literal:: + + *bonds* value = *no* or *yes* + *no* = ignore list of local bonds + *yes* = include list of local bonds + +Examples +"""""""" + +.. code-block:: LAMMPS + + compute 1 all reaxff/atom bonds yes + +Description +""""""""""" + +TODO + +---------- + +.. include:: accel_styles.rst + +---------- + +Restrictions +"""""""""""" + +The compute reaxff/atom command requires that the :doc:`pair_style reaxff +` is invoked. This fix is part of the REAXFF package. It is only +enabled if LAMMPS was built with that package. See the :doc:`Build package +` page for more info. + +Related commands +"""""""""""""""" + +:doc:`pair_style reaxff ` + +Default +""""""" + +The option defaults are *bonds* = *no*. diff --git a/doc/src/pair_reaxff.rst b/doc/src/pair_reaxff.rst index d28e15b0a2..03d53d1ff4 100644 --- a/doc/src/pair_reaxff.rst +++ b/doc/src/pair_reaxff.rst @@ -373,7 +373,8 @@ Related commands :doc:`pair_coeff `, :doc:`fix qeq/reaxff `, :doc:`fix acks2/reaxff `, :doc:`fix reaxff/bonds `, -:doc:`fix reaxff/species ` +:doc:`fix reaxff/species `, +:doc:`compute reaxff/atom ` Default """"""" From b72c34d4979b184adbb4aea83240e7bcb30e040e Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Tue, 21 Nov 2023 14:20:17 -0700 Subject: [PATCH 077/189] compute reaxff/atom: return tag[i] instead of i --- src/KOKKOS/compute_reaxff_atom_kokkos.cpp | 3 ++- src/REAXFF/compute_reaxff_atom.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/KOKKOS/compute_reaxff_atom_kokkos.cpp b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp index 845e8bc6e7..c703bc3552 100644 --- a/src/KOKKOS/compute_reaxff_atom_kokkos.cpp +++ b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp @@ -135,6 +135,7 @@ void ComputeReaxFFAtomKokkos::compute_local() int b = 0; int j = 0; + auto tag = atom->tag; for (int i = 0; i < nlocal; ++i) { const int numbonds = static_cast(buf[j+2]); @@ -142,7 +143,7 @@ void ComputeReaxFFAtomKokkos::compute_local() const int bo_offset = neigh_offset + numbonds; for (int k = 0; k < numbonds; k++) { auto bond = array_local[b++]; - bond[0] = i; + bond[0] = tag[i]; bond[1] = static_cast (buf[neigh_offset+k]); bond[2] = buf[bo_offset+k]; } diff --git a/src/REAXFF/compute_reaxff_atom.cpp b/src/REAXFF/compute_reaxff_atom.cpp index 1d7bdf0e7d..2e5d4058b8 100644 --- a/src/REAXFF/compute_reaxff_atom.cpp +++ b/src/REAXFF/compute_reaxff_atom.cpp @@ -202,6 +202,7 @@ void ComputeReaxFFAtom::compute_local() } size_local_rows = nbonds; + auto tag = atom->tag; int b = 0; @@ -210,7 +211,7 @@ void ComputeReaxFFAtom::compute_local() for (int k = 0; k < numbonds; k++) { auto bond = array_local[b++]; - bond[0] = i; + bond[0] = tag[i]; bond[1] = neighid[i][k]; bond[2] = abo[i][k]; } From 782ca9e0ff366c8a2e540bfc08d558155357fac5 Mon Sep 17 00:00:00 2001 From: Stan Gerald Moore Date: Fri, 1 Dec 2023 14:46:30 -0700 Subject: [PATCH 078/189] Thread over neighbors in addition to atoms when using a half neighbor list --- src/KOKKOS/kokkos.cpp | 4 +- src/KOKKOS/pair_kokkos.h | 318 ++++++++++++++++++++++++++++----------- 2 files changed, 236 insertions(+), 86 deletions(-) diff --git a/src/KOKKOS/kokkos.cpp b/src/KOKKOS/kokkos.cpp index c963cd52d0..5572f69901 100644 --- a/src/KOKKOS/kokkos.cpp +++ b/src/KOKKOS/kokkos.cpp @@ -608,8 +608,8 @@ void KokkosLMP::accelerator(int narg, char **arg) force->newton = force->newton_pair = force->newton_bond = newtonflag; - if (neigh_thread && neighflag != FULL) - error->all(FLERR,"Must use KOKKOS package option 'neigh full' with 'neigh/thread on'"); + if (neigh_thread && newtonflag) + error->all(FLERR,"Must use KOKKOS package option 'newton off' with 'neigh/thread on'"); neighbor->binsize_user = binsize; if (binsize <= 0.0) neighbor->binsizeflag = 0; diff --git a/src/KOKKOS/pair_kokkos.h b/src/KOKKOS/pair_kokkos.h index d3c766f5ae..c0b81cc594 100644 --- a/src/KOKKOS/pair_kokkos.h +++ b/src/KOKKOS/pair_kokkos.h @@ -116,6 +116,7 @@ struct PairComputeFunctor { // Loop over neighbors of one atom without coulomb interaction // This function is called in parallel + template KOKKOS_FUNCTION EV_FLOAT compute_item(const int& ii, @@ -161,7 +162,7 @@ struct PairComputeFunctor { fytmp += dely*fpair; fztmp += delz*fpair; - if ((NEIGHFLAG==HALF || NEIGHFLAG==HALFTHREAD) && (NEWTON_PAIR || j < c.nlocal)) { + if ((NEIGHFLAG == HALF || NEIGHFLAG == HALFTHREAD) && (NEWTON_PAIR || j < c.nlocal)) { a_f(j,0) -= delx*fpair; a_f(j,1) -= dely*fpair; a_f(j,2) -= delz*fpair; @@ -169,9 +170,9 @@ struct PairComputeFunctor { if (EVFLAG) { F_FLOAT evdwl = 0.0; - if (c.eflag) { + if (c.eflag_either) { evdwl = factor_lj * c.template compute_evdwl(rsq,i,j,itype,jtype); - ev.evdwl += (((NEIGHFLAG==HALF || NEIGHFLAG==HALFTHREAD)&&(NEWTON_PAIR||(j KOKKOS_FUNCTION EV_FLOAT compute_item(const int& ii, @@ -241,7 +243,7 @@ struct PairComputeFunctor { fytmp += dely*fpair; fztmp += delz*fpair; - if ((NEIGHFLAG==HALF || NEIGHFLAG==HALFTHREAD) && (NEWTON_PAIR || j < c.nlocal)) { + if ((NEIGHFLAG == HALF || NEIGHFLAG == HALFTHREAD) && (NEWTON_PAIR || j < c.nlocal)) { a_f(j,0) -= delx*fpair; a_f(j,1) -= dely*fpair; a_f(j,2) -= delz*fpair; @@ -250,14 +252,14 @@ struct PairComputeFunctor { if (EVFLAG) { F_FLOAT evdwl = 0.0; F_FLOAT ecoul = 0.0; - if (c.eflag) { + if (c.eflag_either) { if (rsq < (STACKPARAMS?c.m_cut_ljsq[itype][jtype]:c.d_cut_ljsq(itype,jtype))) { evdwl = factor_lj * c.template compute_evdwl(rsq,i,j,itype,jtype); - ev.evdwl += (((NEIGHFLAG==HALF || NEIGHFLAG==HALFTHREAD)&&(NEWTON_PAIR||(j(rsq,i,j,itype,jtype,factor_coul,qtmp); - ev.ecoul += (((NEIGHFLAG==HALF || NEIGHFLAG==HALFTHREAD)&&(NEWTON_PAIR||(j::member_type team, const NeighListKokkos &list, const NoCoulTag&) const { + auto a_f = dup_f.template access::value>(); + const int inum = team.league_size(); const int atoms_per_team = team.team_size(); const int firstatom = team.league_rank()*atoms_per_team; @@ -292,7 +297,7 @@ struct PairComputeFunctor { const X_FLOAT ztmp = c.x(i,2); const int itype = c.type(i); - if (ZEROFLAG) { + if (NEIGHFLAG == FULL && ZEROFLAG) { Kokkos::single(Kokkos::PerThread(team), [&] (){ f(i,0) = 0.0; f(i,1) = 0.0; @@ -321,35 +326,49 @@ struct PairComputeFunctor { const F_FLOAT fpair = factor_lj*c.template compute_fpair(rsq,i,j,itype,jtype); - ftmp.x += delx*fpair; - ftmp.y += dely*fpair; - ftmp.z += delz*fpair; + const F_FLOAT fx = delx*fpair; + const F_FLOAT fy = dely*fpair; + const F_FLOAT fz = delz*fpair; + + ftmp.x += fx; + ftmp.y += fy; + ftmp.z += fz; + + if ((NEIGHFLAG == HALF || NEIGHFLAG == HALFTHREAD) && j < c.nlocal) { + a_f(j,0) -= fx; + a_f(j,1) -= fy; + a_f(j,2) -= fz; + } } },fsum); Kokkos::single(Kokkos::PerThread(team), [&] () { - f(i,0) += fsum.x; - f(i,1) += fsum.y; - f(i,2) += fsum.z; + a_f(i,0) += fsum.x; + a_f(i,1) += fsum.y; + a_f(i,2) += fsum.z; }); }); } - // Use TeamPolicy, assume Newton off, Full Neighborlist, and no energy/virial + // TeamPolicy, newton off, and no energy/virial // Loop over neighbors of one atom with coulomb interaction // This function is called in parallel + KOKKOS_FUNCTION void compute_item_team(typename Kokkos::TeamPolicy::member_type team, const NeighListKokkos &list, const CoulTag& ) const { + auto a_f = dup_f.template access::value>(); + const int inum = team.league_size(); const int atoms_per_team = team.team_size(); int firstatom = team.league_rank()*atoms_per_team; int lastatom = firstatom + atoms_per_team < inum ? firstatom + atoms_per_team : inum; Kokkos::parallel_for(Kokkos::TeamThreadRange(team, firstatom, lastatom), [&] (const int &ii) { + const int i = list.d_ilist[ii]; const X_FLOAT xtmp = c.x(i,0); const X_FLOAT ytmp = c.x(i,1); @@ -357,8 +376,9 @@ struct PairComputeFunctor { const int itype = c.type(i); const F_FLOAT qtmp = c.q(i); - if (ZEROFLAG) { - Kokkos::single(Kokkos::PerThread(team), [&] (){ + if (NEIGHFLAG == FULL && ZEROFLAG) { + Kokkos::single(Kokkos::PerThread(team), [&] () + { f(i,0) = 0.0; f(i,1) = 0.0; f(i,2) = 0.0; @@ -391,34 +411,50 @@ struct PairComputeFunctor { if (rsq < (STACKPARAMS?c.m_cut_coulsq[itype][jtype]:c.d_cut_coulsq(itype,jtype))) fpair+=c.template compute_fcoul(rsq,i,j,itype,jtype,factor_coul,qtmp); - ftmp.x += delx*fpair; - ftmp.y += dely*fpair; - ftmp.z += delz*fpair; + const F_FLOAT fx = delx*fpair; + const F_FLOAT fy = dely*fpair; + const F_FLOAT fz = delz*fpair; + + ftmp.x += fx; + ftmp.y += fy; + ftmp.z += fz; + + if ((NEIGHFLAG == HALF || NEIGHFLAG == HALFTHREAD) && j < c.nlocal) { + a_f(j,0) -= fx; + a_f(j,1) -= fy; + a_f(j,2) -= fz; + } } + },fsum); Kokkos::single(Kokkos::PerThread(team), [&] () { - f(i,0) += fsum.x; - f(i,1) += fsum.y; - f(i,2) += fsum.z; + a_f(i,0) += fsum.x; + a_f(i,1) += fsum.y; + a_f(i,2) += fsum.z; }); }); } - - // Use TeamPolicy, assume Newton off, Full Neighborlist, and energy/virial + // TeamPolicy, newton off, and energy/virial // Loop over neighbors of one atom without coulomb interaction // This function is called in parallel + KOKKOS_FUNCTION EV_FLOAT compute_item_team_ev(typename Kokkos::TeamPolicy::member_type team, const NeighListKokkos &list, const NoCoulTag&) const { + auto a_f = dup_f.template access::value>(); + auto a_eatom = dup_eatom.template access::value>(); + auto a_vatom = dup_vatom.template access::value>(); + EV_FLOAT ev; const int inum = team.league_size(); const int atoms_per_team = team.team_size(); const int firstatom = team.league_rank()*atoms_per_team; const int lastatom = firstatom + atoms_per_team < inum ? firstatom + atoms_per_team : inum; + Kokkos::parallel_for(Kokkos::TeamThreadRange(team, firstatom, lastatom), [&] (const int &ii) { const int i = list.d_ilist[ii]; @@ -427,8 +463,9 @@ struct PairComputeFunctor { const X_FLOAT ztmp = c.x(i,2); const int itype = c.type(i); - if (ZEROFLAG) { - Kokkos::single(Kokkos::PerThread(team), [&] (){ + if (NEIGHFLAG == FULL && ZEROFLAG) { + Kokkos::single(Kokkos::PerThread(team), [&] () + { f(i,0) = 0.0; f(i,1) = 0.0; f(i,2) = 0.0; @@ -456,37 +493,85 @@ struct PairComputeFunctor { const F_FLOAT fpair = factor_lj*c.template compute_fpair(rsq,i,j,itype,jtype); - fev_tmp.f[0] += delx*fpair; - fev_tmp.f[1] += dely*fpair; - fev_tmp.f[2] += delz*fpair; + const F_FLOAT fx = delx*fpair; + const F_FLOAT fy = dely*fpair; + const F_FLOAT fz = delz*fpair; + + fev_tmp.f[0] += fx; + fev_tmp.f[1] += fy; + fev_tmp.f[2] += fz; + + const int I_CONTRIB = (NEIGHFLAG == HALF || NEIGHFLAG == HALFTHREAD); + const int J_CONTRIB = ((NEIGHFLAG == HALF || NEIGHFLAG == HALFTHREAD) && j < c.nlocal); + const E_FLOAT factor = J_CONTRIB?1.0:0.5; + + if (J_CONTRIB) { + a_f(j,0) -= fx; + a_f(j,1) -= fy; + a_f(j,2) -= fz; + } F_FLOAT evdwl = 0.0; - if (c.eflag) { + if (c.eflag_either) { evdwl = factor_lj * c.template compute_evdwl(rsq,i,j,itype,jtype); - fev_tmp.evdwl += 0.5*evdwl; + fev_tmp.evdwl += factor * evdwl; + + if (c.eflag_atom) { + const E_FLOAT epairhalf = 0.5 * evdwl; + + if (I_CONTRIB) + a_eatom[i] += epairhalf; + + if (J_CONTRIB) + a_eatom[j] += epairhalf; + } } + if (c.vflag_either) { - fev_tmp.v[0] += 0.5*delx*delx*fpair; - fev_tmp.v[1] += 0.5*dely*dely*fpair; - fev_tmp.v[2] += 0.5*delz*delz*fpair; - fev_tmp.v[3] += 0.5*delx*dely*fpair; - fev_tmp.v[4] += 0.5*delx*delz*fpair; - fev_tmp.v[5] += 0.5*dely*delz*fpair; + const E_FLOAT v0 = delx*delx*fpair; + const E_FLOAT v1 = dely*dely*fpair; + const E_FLOAT v2 = delz*delz*fpair; + const E_FLOAT v3 = delx*dely*fpair; + const E_FLOAT v4 = delx*delz*fpair; + const E_FLOAT v5 = dely*delz*fpair; + + fev_tmp.v[0] += factor*v0; + fev_tmp.v[1] += factor*v1; + fev_tmp.v[2] += factor*v2; + fev_tmp.v[3] += factor*v3; + fev_tmp.v[4] += factor*v4; + fev_tmp.v[5] += factor*v5; + + if (c.vflag_atom) { + if (I_CONTRIB) { + a_vatom(i,0) += 0.5*v0; + a_vatom(i,1) += 0.5*v1; + a_vatom(i,2) += 0.5*v2; + a_vatom(i,3) += 0.5*v3; + a_vatom(i,4) += 0.5*v4; + a_vatom(i,5) += 0.5*v5; + } + if (J_CONTRIB) { + a_vatom(j,0) += 0.5*v0; + a_vatom(j,1) += 0.5*v1; + a_vatom(j,2) += 0.5*v2; + a_vatom(j,3) += 0.5*v3; + a_vatom(j,4) += 0.5*v4; + a_vatom(j,5) += 0.5*v5; + } + } } } },fev); Kokkos::single(Kokkos::PerThread(team), [&] () { - f(i,0) += fev.f[0]; - f(i,1) += fev.f[1]; - f(i,2) += fev.f[2]; + a_f(i,0) += fev.f[0]; + a_f(i,1) += fev.f[1]; + a_f(i,2) += fev.f[2]; if (c.eflag_global) ev.evdwl += fev.evdwl; - if (c.eflag_atom) - d_eatom(i) += fev.evdwl; - if (c.vflag_global) { ev.v[0] += fev.v[0]; ev.v[1] += fev.v[1]; @@ -496,26 +581,37 @@ struct PairComputeFunctor { ev.v[5] += fev.v[5]; } - if (c.vflag_atom) { - d_vatom(i,0) += fev.v[0]; - d_vatom(i,1) += fev.v[1]; - d_vatom(i,2) += fev.v[2]; - d_vatom(i,3) += fev.v[3]; - d_vatom(i,4) += fev.v[4]; - d_vatom(i,5) += fev.v[5]; + if (NEIGHFLAG == FULL) { + + if (c.eflag_atom) + a_eatom(i) += fev.evdwl; + + if (c.vflag_atom) { + a_vatom(i,0) += fev.v[0]; + a_vatom(i,1) += fev.v[1]; + a_vatom(i,2) += fev.v[2]; + a_vatom(i,3) += fev.v[3]; + a_vatom(i,4) += fev.v[4]; + a_vatom(i,5) += fev.v[5]; + } } }); }); return ev; } - // Use TeamPolicy, assume Newton off, Full Neighborlist, and energy/virial + // TeamPolicy, newton off, and energy/virial // Loop over neighbors of one atom with coulomb interaction // This function is called in parallel + KOKKOS_FUNCTION EV_FLOAT compute_item_team_ev(typename Kokkos::TeamPolicy::member_type team, const NeighListKokkos &list, const CoulTag& ) const { + auto a_f = dup_f.template access::value>(); + auto a_eatom = dup_eatom.template access::value>(); + auto a_vatom = dup_vatom.template access::value>(); + EV_FLOAT ev; const int inum = team.league_size(); @@ -566,45 +662,92 @@ struct PairComputeFunctor { if (rsq < (STACKPARAMS?c.m_cut_coulsq[itype][jtype]:c.d_cut_coulsq(itype,jtype))) fpair+=c.template compute_fcoul(rsq,i,j,itype,jtype,factor_coul,qtmp); - fev_tmp.f[0] += delx*fpair; - fev_tmp.f[1] += dely*fpair; - fev_tmp.f[2] += delz*fpair; + const F_FLOAT fx = delx*fpair; + const F_FLOAT fy = dely*fpair; + const F_FLOAT fz = delz*fpair; + + fev_tmp.f[0] += fx; + fev_tmp.f[1] += fy; + fev_tmp.f[2] += fz; + + const int I_CONTRIB = (NEIGHFLAG == HALF || NEIGHFLAG == HALFTHREAD); + const int J_CONTRIB = ((NEIGHFLAG == HALF || NEIGHFLAG == HALFTHREAD) && j < c.nlocal); + const E_FLOAT factor = J_CONTRIB?1.0:0.5; + + if ((NEIGHFLAG == HALF || NEIGHFLAG == HALFTHREAD) && j < c.nlocal) { + a_f(j,0) -= fx; + a_f(j,1) -= fy; + a_f(j,2) -= fz; + } F_FLOAT evdwl = 0.0; F_FLOAT ecoul = 0.0; - if (c.eflag) { + if (c.eflag_either) { if (rsq < (STACKPARAMS?c.m_cut_ljsq[itype][jtype]:c.d_cut_ljsq(itype,jtype))) { evdwl = factor_lj * c.template compute_evdwl(rsq,i,j,itype,jtype); - fev_tmp.evdwl += 0.5*evdwl; + fev_tmp.evdwl += factor * evdwl; } if (rsq < (STACKPARAMS?c.m_cut_coulsq[itype][jtype]:c.d_cut_coulsq(itype,jtype))) { ecoul = c.template compute_ecoul(rsq,i,j,itype,jtype,factor_coul,qtmp); - fev_tmp.ecoul += 0.5*ecoul; + fev_tmp.ecoul += factor * ecoul; + } + + + if (c.eflag_atom) { + const E_FLOAT epairhalf = 0.5 * (evdwl + ecoul); + + if (I_CONTRIB) + a_eatom[i] += epairhalf; + + if (J_CONTRIB) + a_eatom[j] += epairhalf; } } + if (c.vflag_either) { - fev_tmp.v[0] += 0.5*delx*delx*fpair; - fev_tmp.v[1] += 0.5*dely*dely*fpair; - fev_tmp.v[2] += 0.5*delz*delz*fpair; - fev_tmp.v[3] += 0.5*delx*dely*fpair; - fev_tmp.v[4] += 0.5*delx*delz*fpair; - fev_tmp.v[5] += 0.5*dely*delz*fpair; + const E_FLOAT v0 = delx*delx*fpair; + const E_FLOAT v1 = dely*dely*fpair; + const E_FLOAT v2 = delz*delz*fpair; + const E_FLOAT v3 = delx*dely*fpair; + const E_FLOAT v4 = delx*delz*fpair; + const E_FLOAT v5 = dely*delz*fpair; + + fev_tmp.v[0] += factor*v0; + fev_tmp.v[1] += factor*v1; + fev_tmp.v[2] += factor*v2; + fev_tmp.v[3] += factor*v3; + fev_tmp.v[4] += factor*v4; + fev_tmp.v[5] += factor*v5; + + if (c.vflag_atom) { + if (I_CONTRIB) { + a_vatom(i,0) += 0.5*v0; + a_vatom(i,1) += 0.5*v1; + a_vatom(i,2) += 0.5*v2; + a_vatom(i,3) += 0.5*v3; + a_vatom(i,4) += 0.5*v4; + a_vatom(i,5) += 0.5*v5; + } + if (J_CONTRIB) { + a_vatom(j,0) += 0.5*v0; + a_vatom(j,1) += 0.5*v1; + a_vatom(j,2) += 0.5*v2; + a_vatom(j,3) += 0.5*v3; + a_vatom(j,4) += 0.5*v4; + a_vatom(j,5) += 0.5*v5; + } + } } } },fev); Kokkos::single(Kokkos::PerThread(team), [&] () { - f(i,0) += fev.f[0]; - f(i,1) += fev.f[1]; - f(i,2) += fev.f[2]; + a_f(i,0) += fev.f[0]; + a_f(i,1) += fev.f[1]; + a_f(i,2) += fev.f[2]; - if (c.eflag_global) { + if (c.eflag_global) ev.evdwl += fev.evdwl; - ev.ecoul += fev.ecoul; - } - - if (c.eflag_atom) - d_eatom(i) += fev.evdwl + fev.ecoul; if (c.vflag_global) { ev.v[0] += fev.v[0]; @@ -615,13 +758,19 @@ struct PairComputeFunctor { ev.v[5] += fev.v[5]; } - if (c.vflag_atom) { - d_vatom(i,0) += fev.v[0]; - d_vatom(i,1) += fev.v[1]; - d_vatom(i,2) += fev.v[2]; - d_vatom(i,3) += fev.v[3]; - d_vatom(i,4) += fev.v[4]; - d_vatom(i,5) += fev.v[5]; + if (NEIGHFLAG == FULL) { + + if (c.eflag_atom) + a_eatom(i) += fev.evdwl; + + if (c.vflag_atom) { + a_vatom(i,0) += fev.v[0]; + a_vatom(i,1) += fev.v[1]; + a_vatom(i,2) += fev.v[2]; + a_vatom(i,3) += fev.v[3]; + a_vatom(i,4) += fev.v[4]; + a_vatom(i,5) += fev.v[5]; + } } }); }); @@ -636,7 +785,7 @@ struct PairComputeFunctor { auto a_eatom = dup_eatom.template access::value>(); auto a_vatom = dup_vatom.template access::value>(); - const int EFLAG = c.eflag; + const int EFLAG = c.eflag_either; const int NEWTON_PAIR = c.newton_pair; const int VFLAG = c.vflag_either; @@ -657,7 +806,7 @@ struct PairComputeFunctor { const E_FLOAT v5 = dely*delz*fpair; if (c.vflag_global) { - if (NEIGHFLAG!=FULL) { + if (NEIGHFLAG != FULL) { if (NEWTON_PAIR) { ev.v[0] += v0; ev.v[1] += v1; @@ -747,7 +896,8 @@ struct PairComputeFunctor { // This uses the fact that failure to match template parameters is not an error. // By having the enable_if with a ! and without it, exactly one of the functions // pair_compute_neighlist will match - either the dummy version -// or the real one further below. +// or the real one further below + template EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t*> list) { EV_FLOAT ev; From a365cb15b0c340b7f56fcca76f9ded4d495f5c0e Mon Sep 17 00:00:00 2001 From: jtclemm Date: Sat, 9 Dec 2023 14:18:20 -0700 Subject: [PATCH 079/189] Updating class names in Intel --- src/INTEL/npair_halffull_intel.cpp | 8 ++++---- src/INTEL/npair_halffull_intel.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/INTEL/npair_halffull_intel.cpp b/src/INTEL/npair_halffull_intel.cpp index 5a70edae8b..134b768cc5 100644 --- a/src/INTEL/npair_halffull_intel.cpp +++ b/src/INTEL/npair_halffull_intel.cpp @@ -257,7 +257,7 @@ void NPairHalffullNewtonIntel::build(NeighList *list) /* ---------------------------------------------------------------------- */ -NPairHalffullNewtonTrimIntel::NPairHalffullNewtonTrimIntel(LAMMPS *lmp) : NPair(lmp) { +NPairHalffullTrimNewtonIntel::NPairHalffullTrimNewtonIntel(LAMMPS *lmp) : NPair(lmp) { _fix = static_cast(modify->get_fix_by_id("package_intel")); if (!_fix) error->all(FLERR, "The 'package intel' command is required for /intel styles"); } @@ -270,7 +270,7 @@ NPairHalffullNewtonTrimIntel::NPairHalffullNewtonTrimIntel(LAMMPS *lmp) : NPair( ------------------------------------------------------------------------- */ template -void NPairHalffullNewtonTrimIntel::build_t(NeighList *list, +void NPairHalffullTrimNewtonIntel::build_t(NeighList *list, IntelBuffers *buffers) { const int inum_full = list->listfull->inum; @@ -408,7 +408,7 @@ void NPairHalffullNewtonTrimIntel::build_t(NeighList *list, ------------------------------------------------------------------------- */ template -void NPairHalffullNewtonTrimIntel::build_t3(NeighList *list, int *numhalf, +void NPairHalffullTrimNewtonIntel::build_t3(NeighList *list, int *numhalf, IntelBuffers *buffers) { const int inum_full = list->listfull->inum; @@ -498,7 +498,7 @@ void NPairHalffullNewtonTrimIntel::build_t3(NeighList *list, int *numhalf, /* ---------------------------------------------------------------------- */ -void NPairHalffullNewtonTrimIntel::build(NeighList *list) +void NPairHalffullTrimNewtonIntel::build(NeighList *list) { if (_fix->three_body_neighbor() == 0 || domain->triclinic) { if (_fix->precision() == FixIntel::PREC_MODE_MIXED) diff --git a/src/INTEL/npair_halffull_intel.h b/src/INTEL/npair_halffull_intel.h index d8ba031176..a1f9adbbc4 100644 --- a/src/INTEL/npair_halffull_intel.h +++ b/src/INTEL/npair_halffull_intel.h @@ -109,9 +109,9 @@ class NPairHalffullNewtonIntel : public NPair { template void build_t3(NeighList *, int *); }; -class NPairHalffullNewtonTrimIntel : public NPair { +class NPairHalffullTrimNewtonIntel : public NPair { public: - NPairHalffullNewtonTrimIntel(class LAMMPS *); + NPairHalffullTrimNewtonIntel(class LAMMPS *); void build(class NeighList *) override; protected: From bab9fc55335f981e7fe6c41e137d90ef6acc138f Mon Sep 17 00:00:00 2001 From: jtclemm Date: Sat, 9 Dec 2023 22:00:22 -0700 Subject: [PATCH 080/189] Consolidating some skip npair classes --- src/npair_skip.cpp | 33 +++- src/npair_skip.h | 34 +++- src/npair_skip_respa.cpp | 49 ++++- src/npair_skip_respa.h | 14 +- src/npair_skip_size.cpp | 86 --------- src/npair_skip_size.h | 39 ---- src/npair_skip_size_off2on.cpp | 30 ++- src/npair_skip_size_off2on.h | 13 +- src/npair_skip_size_off2on_oneside.cpp | 44 ++++- src/npair_skip_size_off2on_oneside.h | 13 +- src/npair_skip_trim.cpp | 118 ------------ src/npair_skip_trim.h | 46 ----- src/npair_skip_trim_respa.cpp | 193 -------------------- src/npair_skip_trim_respa.h | 40 ---- src/npair_skip_trim_size.cpp | 102 ----------- src/npair_skip_trim_size.h | 39 ---- src/npair_skip_trim_size_off2on.cpp | 112 ------------ src/npair_skip_trim_size_off2on.h | 40 ---- src/npair_skip_trim_size_off2on_oneside.cpp | 185 ------------------- src/npair_skip_trim_size_off2on_oneside.h | 40 ---- 20 files changed, 214 insertions(+), 1056 deletions(-) delete mode 100644 src/npair_skip_size.cpp delete mode 100644 src/npair_skip_size.h delete mode 100644 src/npair_skip_trim.cpp delete mode 100644 src/npair_skip_trim.h delete mode 100644 src/npair_skip_trim_respa.cpp delete mode 100644 src/npair_skip_trim_respa.h delete mode 100644 src/npair_skip_trim_size.cpp delete mode 100644 src/npair_skip_trim_size.h delete mode 100644 src/npair_skip_trim_size_off2on.cpp delete mode 100644 src/npair_skip_trim_size_off2on.h delete mode 100644 src/npair_skip_trim_size_off2on_oneside.cpp delete mode 100644 src/npair_skip_trim_size_off2on_oneside.h diff --git a/src/npair_skip.cpp b/src/npair_skip.cpp index d9d4fa491f..fad06d699c 100644 --- a/src/npair_skip.cpp +++ b/src/npair_skip.cpp @@ -22,7 +22,8 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -NPairSkip::NPairSkip(LAMMPS *lmp) : NPair(lmp) {} +template +NPairSkipTemp::NPairSkipTemp(LAMMPS *lmp) : NPair(lmp) {} /* ---------------------------------------------------------------------- build skip list for subset of types from parent list @@ -32,7 +33,8 @@ NPairSkip::NPairSkip(LAMMPS *lmp) : NPair(lmp) {} if ghost, also store neighbors of ghost atoms & set inum,gnum correctly ------------------------------------------------------------------------- */ -void NPairSkip::build(NeighList *list) +template +void NPairSkipTemp::build(NeighList *list) { int i, j, ii, jj, n, itype, jnum, joriginal; int *neighptr, *jlist; @@ -57,6 +59,11 @@ void NPairSkip::build(NeighList *list) int inum = 0; ipage->reset(); + double **x = atom->x; + double xtmp, ytmp, ztmp; + double delx, dely, delz, rsq; + double cutsq_custom = cutoff_custom * cutoff_custom; + // loop over atoms in other list // skip I atom entirely if iskip is set for type[I] // skip I,J pair if ijskip is set for type[I],type[J] @@ -66,6 +73,12 @@ void NPairSkip::build(NeighList *list) itype = type[i]; if (iskip[itype]) continue; + if (TRIM) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + } + n = 0; neighptr = ipage->vget(); @@ -78,6 +91,15 @@ void NPairSkip::build(NeighList *list) joriginal = jlist[jj]; j = joriginal & NEIGHMASK; if (ijskip[itype][type[j]]) continue; + + if (TRIM) { + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx * delx + dely * dely + delz * delz; + if (rsq > cutsq_custom) continue; + } + neighptr[n++] = joriginal; } @@ -100,3 +122,10 @@ void NPairSkip::build(NeighList *list) list->gnum = inum - num; } } + +namespace LAMMPS_NS { +template class NPairSkipTemp<0,0>; +template class NPairSkipTemp<1,0>; +template class NPairSkipTemp<0,1>; +template class NPairSkipTemp<1,1>; +} diff --git a/src/npair_skip.h b/src/npair_skip.h index 4e85174730..06da1be85b 100644 --- a/src/npair_skip.h +++ b/src/npair_skip.h @@ -13,17 +13,46 @@ #ifdef NPAIR_CLASS // clang-format off +typedef NPairSkipTemp<0, 0> NPairSkip; NPairStyle(skip, NPairSkip, NP_SKIP | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); +typedef NPairSkipTemp<0, 0> NPairSkip; NPairStyle(skip/ghost, NPairSkip, NP_SKIP | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_GHOST); + +typedef NPairSkipTemp<1, 0> NPairSkipSize; +NPairStyle(skip/half/size, + NPairSkipSize, + NP_SKIP | NP_SIZE | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairSkipTemp<0, 1> NPairSkipTrim; +NPairStyle(skip/trim, + NPairSkipTrim, + NP_SKIP | NP_HALF | NP_FULL | + NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM); + +typedef NPairSkipTemp<0, 1> NPairSkipTrim; +NPairStyle(skip/ghost/trim, + NPairSkipTrim, + NP_SKIP | NP_HALF | NP_FULL | + NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_GHOST | NP_TRIM); + +typedef NPairSkipTemp<1, 1> NPairSkipTrimSize; +NPairStyle(skip/trim/half/size, + NPairSkipTrimSize, + NP_SKIP | NP_SIZE | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM); + // clang-format on #else @@ -34,9 +63,10 @@ NPairStyle(skip/ghost, namespace LAMMPS_NS { -class NPairSkip : public NPair { +template +class NPairSkipTemp : public NPair { public: - NPairSkip(class LAMMPS *); + NPairSkipTemp(class LAMMPS *); void build(class NeighList *) override; }; diff --git a/src/npair_skip_respa.cpp b/src/npair_skip_respa.cpp index 81cfc8a767..4c3dda91eb 100644 --- a/src/npair_skip_respa.cpp +++ b/src/npair_skip_respa.cpp @@ -23,7 +23,8 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -NPairSkipRespa::NPairSkipRespa(LAMMPS *lmp) : NPair(lmp) {} +template +NPairSkipRespaTemp::NPairSkipRespaTemp(LAMMPS *lmp) : NPair(lmp) {} /* ---------------------------------------------------------------------- build skip list for subset of types from parent list @@ -31,7 +32,8 @@ NPairSkipRespa::NPairSkipRespa(LAMMPS *lmp) : NPair(lmp) {} this is for respa lists, copy the inner/middle values from parent ------------------------------------------------------------------------- */ -void NPairSkipRespa::build(NeighList *list) +template +void NPairSkipRespaTemp::build(NeighList *list) { int i, j, ii, jj, n, itype, jnum, joriginal, n_inner, n_middle; int *neighptr, *jlist, *neighptr_inner, *neighptr_middle; @@ -76,6 +78,11 @@ void NPairSkipRespa::build(NeighList *list) ipage_inner->reset(); if (respamiddle) ipage_middle->reset(); + double **x = atom->x; + double xtmp, ytmp, ztmp; + double delx, dely, delz, rsq; + double cutsq_custom = cutoff_custom * cutoff_custom; + // loop over atoms in other list // skip I atom entirely if iskip is set for type[I] // skip I,J pair if ijskip is set for type[I],type[J] @@ -85,6 +92,12 @@ void NPairSkipRespa::build(NeighList *list) itype = type[i]; if (iskip[itype]) continue; + if (TRIM) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + } + n = n_inner = 0; neighptr = ipage->vget(); neighptr_inner = ipage_inner->vget(); @@ -102,6 +115,15 @@ void NPairSkipRespa::build(NeighList *list) joriginal = jlist[jj]; j = joriginal & NEIGHMASK; if (ijskip[itype][type[j]]) continue; + + if (TRIM) { + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx * delx + dely * dely + delz * delz; + if (rsq > cutsq_custom) continue; + } + neighptr[n++] = joriginal; } @@ -114,6 +136,15 @@ void NPairSkipRespa::build(NeighList *list) joriginal = jlist[jj]; j = joriginal & NEIGHMASK; if (ijskip[itype][type[j]]) continue; + + if (TRIM) { + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx * delx + dely * dely + delz * delz; + if (rsq > cutsq_custom) continue; + } + neighptr_inner[n_inner++] = joriginal; } @@ -127,6 +158,15 @@ void NPairSkipRespa::build(NeighList *list) joriginal = jlist[jj]; j = joriginal & NEIGHMASK; if (ijskip[itype][type[j]]) continue; + + if (TRIM) { + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx * delx + dely * dely + delz * delz; + if (rsq > cutsq_custom) continue; + } + neighptr_middle[n_middle++] = joriginal; } } @@ -158,3 +198,8 @@ void NPairSkipRespa::build(NeighList *list) list->inum_inner = inum; if (respamiddle) list->inum_middle = inum; } + +namespace LAMMPS_NS { +template class NPairSkipRespaTemp<0>; +template class NPairSkipRespaTemp<1>; +} diff --git a/src/npair_skip_respa.h b/src/npair_skip_respa.h index 822fcc290b..0fa4ece699 100644 --- a/src/npair_skip_respa.h +++ b/src/npair_skip_respa.h @@ -13,11 +13,20 @@ #ifdef NPAIR_CLASS // clang-format off +typedef NPairSkipRespaTemp<0> NPairSkipRespa; NPairStyle(skip/half/respa, NPairSkipRespa, NP_SKIP | NP_RESPA | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairSkipRespaTemp<0> NPairSkipTrimRespa; +NPairStyle(skip/trim/half/respa, + NPairSkipTrimRespa, + NP_SKIP | NP_RESPA | NP_HALF | NP_FULL | + NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM); + // clang-format on #else @@ -28,9 +37,10 @@ NPairStyle(skip/half/respa, namespace LAMMPS_NS { -class NPairSkipRespa : public NPair { +template +class NPairSkipRespaTemp : public NPair { public: - NPairSkipRespa(class LAMMPS *); + NPairSkipRespaTemp(class LAMMPS *); void build(class NeighList *) override; }; diff --git a/src/npair_skip_size.cpp b/src/npair_skip_size.cpp deleted file mode 100644 index 22883b4e60..0000000000 --- a/src/npair_skip_size.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* ---------------------------------------------------------------------- - 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 "npair_skip_size.h" - -#include "atom.h" -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairSkipSize::NPairSkipSize(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - build skip list for subset of types from parent list - iskip and ijskip flag which atom types and type pairs to skip -------------------------------------------------------------------------- */ - -void NPairSkipSize::build(NeighList *list) -{ - int i, j, ii, jj, n, itype, jnum, joriginal; - int *neighptr, *jlist; - - int *type = atom->type; - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int *ilist_skip = list->listskip->ilist; - int *numneigh_skip = list->listskip->numneigh; - int **firstneigh_skip = list->listskip->firstneigh; - int inum_skip = list->listskip->inum; - - int *iskip = list->iskip; - int **ijskip = list->ijskip; - - int inum = 0; - ipage->reset(); - - // loop over atoms in other list - // skip I atom entirely if iskip is set for type[I] - // skip I,J pair if ijskip is set for type[I],type[J] - - for (ii = 0; ii < inum_skip; ii++) { - i = ilist_skip[ii]; - itype = type[i]; - if (iskip[itype]) continue; - - n = 0; - neighptr = ipage->vget(); - - // loop over parent non-skip size list - - jlist = firstneigh_skip[i]; - jnum = numneigh_skip[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (ijskip[itype][type[j]]) continue; - neighptr[n++] = joriginal; - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_skip_size.h b/src/npair_skip_size.h deleted file mode 100644 index 9c2f23447b..0000000000 --- a/src/npair_skip_size.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(skip/half/size, - NPairSkipSize, - NP_SKIP | NP_SIZE | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -// clang-format on -#else - -#ifndef LMP_NPAIR_SKIP_SIZE_H -#define LMP_NPAIR_SKIP_SIZE_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairSkipSize : public NPair { - public: - NPairSkipSize(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/npair_skip_size_off2on.cpp b/src/npair_skip_size_off2on.cpp index f1b6d2f4fb..89e633b238 100644 --- a/src/npair_skip_size_off2on.cpp +++ b/src/npair_skip_size_off2on.cpp @@ -22,7 +22,8 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -NPairSkipSizeOff2on::NPairSkipSizeOff2on(LAMMPS *lmp) : NPair(lmp) {} +template +NPairSkipSizeOff2onTemp::NPairSkipSizeOff2onTemp(LAMMPS *lmp) : NPair(lmp) {} /* ---------------------------------------------------------------------- build skip list for subset of types from parent list @@ -30,7 +31,8 @@ NPairSkipSizeOff2on::NPairSkipSizeOff2on(LAMMPS *lmp) : NPair(lmp) {} parent non-skip list used newton off, this skip list is newton on ------------------------------------------------------------------------- */ -void NPairSkipSizeOff2on::build(NeighList *list) +template +void NPairSkipSizeOff2onTemp::build(NeighList *list) { int i, j, ii, jj, n, itype, jnum, joriginal; tagint itag, jtag; @@ -56,6 +58,11 @@ void NPairSkipSizeOff2on::build(NeighList *list) int inum = 0; ipage->reset(); + double **x = atom->x; + double xtmp, ytmp, ztmp; + double delx, dely, delz, rsq; + double cutsq_custom = cutoff_custom * cutoff_custom; + // loop over atoms in other list // skip I atom entirely if iskip is set for type[I] // skip I,J pair if ijskip is set for type[I],type[J] @@ -66,6 +73,12 @@ void NPairSkipSizeOff2on::build(NeighList *list) if (iskip[itype]) continue; itag = tag[i]; + if (TRIM) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + } + n = 0; neighptr = ipage->vget(); @@ -84,6 +97,14 @@ void NPairSkipSizeOff2on::build(NeighList *list) jtag = tag[j]; if (j >= nlocal && jtag < itag) continue; + if (TRIM) { + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx * delx + dely * dely + delz * delz; + if (rsq > cutsq_custom) continue; + } + neighptr[n++] = joriginal; } @@ -95,3 +116,8 @@ void NPairSkipSizeOff2on::build(NeighList *list) } list->inum = inum; } + +namespace LAMMPS_NS { +template class NPairSkipSizeOff2onTemp<0>; +template class NPairSkipSizeOff2onTemp<1>; +} diff --git a/src/npair_skip_size_off2on.h b/src/npair_skip_size_off2on.h index faed76f6c3..e26d2cdd87 100644 --- a/src/npair_skip_size_off2on.h +++ b/src/npair_skip_size_off2on.h @@ -13,11 +13,19 @@ #ifdef NPAIR_CLASS // clang-format off +typedef NPairSkipSizeOff2onTemp<0> NPairSkipSizeOff2on; NPairStyle(skip/size/off2on, NPairSkipSizeOff2on, NP_SKIP | NP_SIZE | NP_OFF2ON | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairSkipSizeOff2onTemp<0> NPairSkipTrimSizeOff2on; +NPairStyle(skip/trim/size/off2on, + NPairSkipTrimSizeOff2on, + NP_SKIP | NP_SIZE | NP_OFF2ON | NP_HALF | + NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM); // clang-format on #else @@ -28,9 +36,10 @@ NPairStyle(skip/size/off2on, namespace LAMMPS_NS { -class NPairSkipSizeOff2on : public NPair { +template +class NPairSkipSizeOff2onTemp : public NPair { public: - NPairSkipSizeOff2on(class LAMMPS *); + NPairSkipSizeOff2onTemp(class LAMMPS *); void build(class NeighList *) override; }; diff --git a/src/npair_skip_size_off2on_oneside.cpp b/src/npair_skip_size_off2on_oneside.cpp index 8974da35b2..7682b90d95 100644 --- a/src/npair_skip_size_off2on_oneside.cpp +++ b/src/npair_skip_size_off2on_oneside.cpp @@ -24,7 +24,8 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -NPairSkipSizeOff2onOneside::NPairSkipSizeOff2onOneside(LAMMPS *lmp) : +template +NPairSkipSizeOff2onOnesideTemp::NPairSkipSizeOff2onOnesideTemp(LAMMPS *lmp) : NPair(lmp) {} /* ---------------------------------------------------------------------- @@ -34,7 +35,8 @@ NPairSkipSizeOff2onOneside::NPairSkipSizeOff2onOneside(LAMMPS *lmp) : this skip list is newton on and onesided ------------------------------------------------------------------------- */ -void NPairSkipSizeOff2onOneside::build(NeighList *list) +template +void NPairSkipSizeOff2onOnesideTemp::build(NeighList *list) { int i, j, ii, jj, itype, jnum, joriginal, flip, tmp; int *surf, *jlist; @@ -61,6 +63,11 @@ void NPairSkipSizeOff2onOneside::build(NeighList *list) int inum = 0; ipage->reset(); + double **x = atom->x; + double xtmp, ytmp, ztmp; + double delx, dely, delz, rsq; + double cutsq_custom = cutoff_custom * cutoff_custom; + // two loops over parent list required, one to count, one to store // because onesided constraint means pair I,J may be stored with I or J // so don't know in advance how much space to alloc for each atom's neighs @@ -76,6 +83,12 @@ void NPairSkipSizeOff2onOneside::build(NeighList *list) itype = type[i]; if (iskip[itype]) continue; + if (TRIM) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + } + // loop over parent non-skip size list jlist = firstneigh_skip[i]; @@ -86,6 +99,14 @@ void NPairSkipSizeOff2onOneside::build(NeighList *list) j = joriginal & NEIGHMASK; if (ijskip[itype][type[j]]) continue; + if (TRIM) { + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx * delx + dely * dely + delz * delz; + if (rsq > cutsq_custom) continue; + } + // flip I,J if necessary to satisfy onesided constraint // do not keep if I is now ghost @@ -121,6 +142,12 @@ void NPairSkipSizeOff2onOneside::build(NeighList *list) itype = type[i]; if (iskip[itype]) continue; + if (TRIM) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + } + // loop over parent non-skip size list and optionally its history info jlist = firstneigh_skip[i]; @@ -131,6 +158,14 @@ void NPairSkipSizeOff2onOneside::build(NeighList *list) j = joriginal & NEIGHMASK; if (ijskip[itype][type[j]]) continue; + if (TRIM) { + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx * delx + dely * dely + delz * delz; + if (rsq > cutsq_custom) continue; + } + // flip I,J if necessary to satisfy onesided constraint // do not keep if I is now ghost @@ -157,3 +192,8 @@ void NPairSkipSizeOff2onOneside::build(NeighList *list) list->inum = inum; } + +namespace LAMMPS_NS { +template class NPairSkipSizeOff2onOnesideTemp<0>; +template class NPairSkipSizeOff2onOnesideTemp<1>; +} diff --git a/src/npair_skip_size_off2on_oneside.h b/src/npair_skip_size_off2on_oneside.h index 48eccf7faf..a5259ef04b 100644 --- a/src/npair_skip_size_off2on_oneside.h +++ b/src/npair_skip_size_off2on_oneside.h @@ -13,11 +13,19 @@ #ifdef NPAIR_CLASS // clang-format off +typedef NPairSkipSizeOff2onOnesideTemp<0> NPairSkipSizeOff2onOneside; NPairStyle(skip/size/off2on/oneside, NPairSkipSizeOff2onOneside, NP_SKIP | NP_SIZE | NP_OFF2ON | NP_ONESIDE | NP_HALF | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); + +typedef NPairSkipSizeOff2onOnesideTemp<1> NPairSkipTrimSizeOff2onOneside; +NPairStyle(skip/trim/size/off2on/oneside, + NPairSkipTrimSizeOff2onOneside, + NP_SKIP | NP_SIZE | NP_OFF2ON | NP_ONESIDE | NP_HALF | + NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | + NP_ORTHO | NP_TRI | NP_TRIM); // clang-format on #else @@ -28,9 +36,10 @@ NPairStyle(skip/size/off2on/oneside, namespace LAMMPS_NS { -class NPairSkipSizeOff2onOneside : public NPair { +template +class NPairSkipSizeOff2onOnesideTemp : public NPair { public: - NPairSkipSizeOff2onOneside(class LAMMPS *); + NPairSkipSizeOff2onOnesideTemp(class LAMMPS *); void build(class NeighList *) override; }; diff --git a/src/npair_skip_trim.cpp b/src/npair_skip_trim.cpp deleted file mode 100644 index a286a7e19e..0000000000 --- a/src/npair_skip_trim.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* ---------------------------------------------------------------------- - 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 "npair_skip_trim.h" - -#include "atom.h" -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairSkipTrim::NPairSkipTrim(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - build skip list for subset of types from parent list - works for half and full lists - works for owned (non-ghost) list, also for ghost list - iskip and ijskip flag which atom types and type pairs to skip - if ghost, also store neighbors of ghost atoms & set inum,gnum correctly -------------------------------------------------------------------------- */ - -void NPairSkipTrim::build(NeighList *list) -{ - int i, j, ii, jj, n, itype, jnum, joriginal; - int *neighptr, *jlist; - - int *type = atom->type; - int nlocal = atom->nlocal; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int *ilist_skip = list->listskip->ilist; - int *numneigh_skip = list->listskip->numneigh; - int **firstneigh_skip = list->listskip->firstneigh; - int num_skip = list->listskip->inum; - if (list->ghost) num_skip += list->listskip->gnum; - - int *iskip = list->iskip; - int **ijskip = list->ijskip; - - int inum = 0; - ipage->reset(); - - double **x = atom->x; - double xtmp, ytmp, ztmp; - double delx, dely, delz, rsq; - double cutsq_custom = cutoff_custom * cutoff_custom; - - // loop over atoms in other list - // skip I atom entirely if iskip is set for type[I] - // skip I,J pair if ijskip is set for type[I],type[J] - - for (ii = 0; ii < num_skip; ii++) { - i = ilist_skip[ii]; - itype = type[i]; - if (iskip[itype]) continue; - - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - - n = 0; - neighptr = ipage->vget(); - - // loop over parent non-skip list - - jlist = firstneigh_skip[i]; - jnum = numneigh_skip[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (ijskip[itype][type[j]]) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx * delx + dely * dely + delz * delz; - if (rsq > cutsq_custom) continue; - - neighptr[n++] = joriginal; - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; - if (list->ghost) { - int num = 0; - for (i = 0; i < inum; i++) - if (ilist[i] < nlocal) - num++; - else - break; - list->inum = num; - list->gnum = inum - num; - } -} diff --git a/src/npair_skip_trim.h b/src/npair_skip_trim.h deleted file mode 100644 index f2a26d654e..0000000000 --- a/src/npair_skip_trim.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(skip/trim, - NPairSkipTrim, - NP_SKIP | NP_HALF | NP_FULL | - NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM); - -NPairStyle(skip/ghost/trim, - NPairSkipTrim, - NP_SKIP | NP_HALF | NP_FULL | - NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_GHOST | NP_TRIM); -// clang-format on -#else - -#ifndef LMP_NPAIR_SKIP_TRIM_H -#define LMP_NPAIR_SKIP_TRIM_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairSkipTrim : public NPair { - public: - NPairSkipTrim(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/npair_skip_trim_respa.cpp b/src/npair_skip_trim_respa.cpp deleted file mode 100644 index 7dd040ca0a..0000000000 --- a/src/npair_skip_trim_respa.cpp +++ /dev/null @@ -1,193 +0,0 @@ -// 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 "npair_skip_trim_respa.h" - -#include "atom.h" -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairSkipTrimRespa::NPairSkipTrimRespa(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - build skip list for subset of types from parent list - iskip and ijskip flag which atom types and type pairs to skip - this is for respa lists, copy the inner/middle values from parent -------------------------------------------------------------------------- */ - -void NPairSkipTrimRespa::build(NeighList *list) -{ - int i,j,ii,jj,n,itype,jnum,joriginal,n_inner,n_middle; - int *neighptr,*jlist,*neighptr_inner,*neighptr_middle; - - int *type = atom->type; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int *ilist_skip = list->listskip->ilist; - int *numneigh_skip = list->listskip->numneigh; - int **firstneigh_skip = list->listskip->firstneigh; - int inum_skip = list->listskip->inum; - - int *iskip = list->iskip; - int **ijskip = list->ijskip; - - int *ilist_inner = list->ilist_inner; - int *numneigh_inner = list->numneigh_inner; - int **firstneigh_inner = list->firstneigh_inner; - MyPage *ipage_inner = list->ipage_inner; - int *numneigh_inner_skip = list->listskip->numneigh_inner; - int **firstneigh_inner_skip = list->listskip->firstneigh_inner; - - int *ilist_middle,*numneigh_middle,**firstneigh_middle; - MyPage *ipage_middle; - int *numneigh_middle_skip,**firstneigh_middle_skip; - int respamiddle = list->respamiddle; - if (respamiddle) { - ilist_middle = list->ilist_middle; - numneigh_middle = list->numneigh_middle; - firstneigh_middle = list->firstneigh_middle; - ipage_middle = list->ipage_middle; - numneigh_middle_skip = list->listskip->numneigh_middle; - firstneigh_middle_skip = list->listskip->firstneigh_middle; - } - - int inum = 0; - ipage->reset(); - ipage_inner->reset(); - if (respamiddle) ipage_middle->reset(); - - double **x = atom->x; - double xtmp, ytmp, ztmp; - double delx, dely, delz, rsq; - double cutsq_custom = cutoff_custom * cutoff_custom; - - // loop over atoms in other list - // skip I atom entirely if iskip is set for type[I] - // skip I,J pair if ijskip is set for type[I],type[J] - - for (ii = 0; ii < inum_skip; ii++) { - i = ilist_skip[ii]; - itype = type[i]; - if (iskip[itype]) continue; - - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - - n = n_inner = 0; - neighptr = ipage->vget(); - neighptr_inner = ipage_inner->vget(); - if (respamiddle) { - n_middle = 0; - neighptr_middle = ipage_middle->vget(); - } - - // loop over parent outer rRESPA list - - jlist = firstneigh_skip[i]; - jnum = numneigh_skip[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (ijskip[itype][type[j]]) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx * delx + dely * dely + delz * delz; - if (rsq > cutsq_custom) continue; - - neighptr[n++] = joriginal; - } - - // loop over parent inner rRESPA list - - jlist = firstneigh_inner_skip[i]; - jnum = numneigh_inner_skip[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (ijskip[itype][type[j]]) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx * delx + dely * dely + delz * delz; - if (rsq > cutsq_custom) continue; - - neighptr_inner[n_inner++] = joriginal; - } - - // loop over parent middle rRESPA list - - if (respamiddle) { - jlist = firstneigh_middle_skip[i]; - jnum = numneigh_middle_skip[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (ijskip[itype][type[j]]) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx * delx + dely * dely + delz * delz; - if (rsq > cutsq_custom) continue; - - neighptr_middle[n_middle++] = joriginal; - } - } - - ilist[inum] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - ilist_inner[inum] = i; - firstneigh_inner[i] = neighptr_inner; - numneigh_inner[i] = n_inner; - ipage_inner->vgot(n); - if (ipage_inner->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - - if (respamiddle) { - ilist_middle[inum] = i; - firstneigh_middle[i] = neighptr_middle; - numneigh_middle[i] = n_middle; - ipage_middle->vgot(n); - if (ipage_middle->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - inum++; - } - - list->inum = inum; - list->inum_inner = inum; - if (respamiddle) list->inum_middle = inum; -} diff --git a/src/npair_skip_trim_respa.h b/src/npair_skip_trim_respa.h deleted file mode 100644 index dcfe71c28d..0000000000 --- a/src/npair_skip_trim_respa.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(skip/trim/half/respa, - NPairSkipTrimRespa, - NP_SKIP | NP_RESPA | NP_HALF | NP_FULL | - NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM); -// clang-format on -#else - -#ifndef LMP_NPAIR_SKIP_TRIM_RESPA_H -#define LMP_NPAIR_SKIP_TRIM_RESPA_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairSkipTrimRespa : public NPair { - public: - NPairSkipTrimRespa(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/npair_skip_trim_size.cpp b/src/npair_skip_trim_size.cpp deleted file mode 100644 index fab70a78b5..0000000000 --- a/src/npair_skip_trim_size.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* ---------------------------------------------------------------------- - 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 "npair_skip_trim_size.h" - -#include "atom.h" -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairSkipTrimSize::NPairSkipTrimSize(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - build skip list for subset of types from parent list - iskip and ijskip flag which atom types and type pairs to skip -------------------------------------------------------------------------- */ - -void NPairSkipTrimSize::build(NeighList *list) -{ - int i, j, ii, jj, n, itype, jnum, joriginal; - int *neighptr, *jlist; - - int *type = atom->type; - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int *ilist_skip = list->listskip->ilist; - int *numneigh_skip = list->listskip->numneigh; - int **firstneigh_skip = list->listskip->firstneigh; - int inum_skip = list->listskip->inum; - - int *iskip = list->iskip; - int **ijskip = list->ijskip; - - int inum = 0; - ipage->reset(); - - double **x = atom->x; - double xtmp, ytmp, ztmp; - double delx, dely, delz, rsq; - double cutsq_custom = cutoff_custom * cutoff_custom; - - // loop over atoms in other list - // skip I atom entirely if iskip is set for type[I] - // skip I,J pair if ijskip is set for type[I],type[J] - - for (ii = 0; ii < inum_skip; ii++) { - i = ilist_skip[ii]; - itype = type[i]; - if (iskip[itype]) continue; - - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - - n = 0; - neighptr = ipage->vget(); - - // loop over parent non-skip size list - - jlist = firstneigh_skip[i]; - jnum = numneigh_skip[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (ijskip[itype][type[j]]) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx * delx + dely * dely + delz * delz; - if (rsq > cutsq_custom) continue; - - neighptr[n++] = joriginal; - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); - } - - list->inum = inum; -} diff --git a/src/npair_skip_trim_size.h b/src/npair_skip_trim_size.h deleted file mode 100644 index 3b536860ca..0000000000 --- a/src/npair_skip_trim_size.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(skip/trim/half/size, - NPairSkipTrimSize, - NP_SKIP | NP_SIZE | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM); -// clang-format on -#else - -#ifndef LMP_NPAIR_SKIP_TRIM_SIZE_H -#define LMP_NPAIR_SKIP_TRIM_SIZE_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairSkipTrimSize : public NPair { - public: - NPairSkipTrimSize(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/npair_skip_trim_size_off2on.cpp b/src/npair_skip_trim_size_off2on.cpp deleted file mode 100644 index 3e9a1e5f63..0000000000 --- a/src/npair_skip_trim_size_off2on.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* ---------------------------------------------------------------------- - 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 "npair_skip_trim_size_off2on.h" - -#include "atom.h" -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairSkipTrimSizeOff2on::NPairSkipTrimSizeOff2on(LAMMPS *lmp) : NPair(lmp) {} - -/* ---------------------------------------------------------------------- - build skip list for subset of types from parent list - iskip and ijskip flag which atom types and type pairs to skip - parent non-skip list used newton off, this skip list is newton on -------------------------------------------------------------------------- */ - -void NPairSkipTrimSizeOff2on::build(NeighList *list) -{ - int i, j, ii, jj, n, itype, jnum, joriginal; - tagint itag, jtag; - int *neighptr, *jlist; - - tagint *tag = atom->tag; - int *type = atom->type; - int nlocal = atom->nlocal; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int *ilist_skip = list->listskip->ilist; - int *numneigh_skip = list->listskip->numneigh; - int **firstneigh_skip = list->listskip->firstneigh; - int inum_skip = list->listskip->inum; - - int *iskip = list->iskip; - int **ijskip = list->ijskip; - - int inum = 0; - ipage->reset(); - - double **x = atom->x; - double xtmp, ytmp, ztmp; - double delx, dely, delz, rsq; - double cutsq_custom = cutoff_custom * cutoff_custom; - - // loop over atoms in other list - // skip I atom entirely if iskip is set for type[I] - // skip I,J pair if ijskip is set for type[I],type[J] - - for (ii = 0; ii < inum_skip; ii++) { - i = ilist_skip[ii]; - itype = type[i]; - if (iskip[itype]) continue; - itag = tag[i]; - - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - - n = 0; - neighptr = ipage->vget(); - - // loop over parent non-skip size list and optionally its history info - - jlist = firstneigh_skip[i]; - jnum = numneigh_skip[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (ijskip[itype][type[j]]) continue; - - // only keep I,J when J = ghost if Itag < Jtag - - jtag = tag[j]; - if (j >= nlocal && jtag < itag) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx * delx + dely * dely + delz * delz; - if (rsq > cutsq_custom) continue; - - neighptr[n++] = joriginal; - } - - ilist[inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - ipage->vgot(n); - if (ipage->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one"); - } - list->inum = inum; -} diff --git a/src/npair_skip_trim_size_off2on.h b/src/npair_skip_trim_size_off2on.h deleted file mode 100644 index 6e52082329..0000000000 --- a/src/npair_skip_trim_size_off2on.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(skip/trim/size/off2on, - NPairSkipTrimSizeOff2on, - NP_SKIP | NP_SIZE | NP_OFF2ON | NP_HALF | - NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM); -// clang-format on -#else - -#ifndef LMP_NPAIR_SKIP_TRIM_SIZE_OFF2ON_H -#define LMP_NPAIR_SKIP_TRIM_SIZE_OFF2ON_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairSkipTrimSizeOff2on : public NPair { - public: - NPairSkipTrimSizeOff2on(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/npair_skip_trim_size_off2on_oneside.cpp b/src/npair_skip_trim_size_off2on_oneside.cpp deleted file mode 100644 index 9d43ac8087..0000000000 --- a/src/npair_skip_trim_size_off2on_oneside.cpp +++ /dev/null @@ -1,185 +0,0 @@ -// 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 "npair_skip_trim_size_off2on_oneside.h" - -#include "atom.h" -#include "domain.h" -#include "error.h" -#include "my_page.h" -#include "neigh_list.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairSkipTrimSizeOff2onOneside::NPairSkipTrimSizeOff2onOneside(LAMMPS *lmp) : - NPair(lmp) {} - -/* ---------------------------------------------------------------------- - build skip list for subset of types from parent list - iskip and ijskip flag which atom types and type pairs to skip - parent non-skip list used newton off and was not onesided, - this skip list is newton on and onesided -------------------------------------------------------------------------- */ - -void NPairSkipTrimSizeOff2onOneside::build(NeighList *list) -{ - int i,j,ii,jj,itype,jnum,joriginal,flip,tmp; - int *surf,*jlist; - - int *type = atom->type; - int nlocal = atom->nlocal; - - int *ilist = list->ilist; - int *numneigh = list->numneigh; - int **firstneigh = list->firstneigh; - MyPage *ipage = list->ipage; - - int *ilist_skip = list->listskip->ilist; - int *numneigh_skip = list->listskip->numneigh; - int **firstneigh_skip = list->listskip->firstneigh; - int inum_skip = list->listskip->inum; - - int *iskip = list->iskip; - int **ijskip = list->ijskip; - - if (domain->dimension == 2) surf = atom->line; - else surf = atom->tri; - - int inum = 0; - ipage->reset(); - - double **x = atom->x; - double xtmp, ytmp, ztmp; - double delx, dely, delz, rsq; - double cutsq_custom = cutoff_custom * cutoff_custom; - - // two loops over parent list required, one to count, one to store - // because onesided constraint means pair I,J may be stored with I or J - // so don't know in advance how much space to alloc for each atom's neighs - - // first loop over atoms in other list to count neighbors - // skip I atom entirely if iskip is set for type[I] - // skip I,J pair if ijskip is set for type[I],type[J] - - for (i = 0; i < nlocal; i++) numneigh[i] = 0; - - for (ii = 0; ii < inum_skip; ii++) { - i = ilist_skip[ii]; - itype = type[i]; - if (iskip[itype]) continue; - - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - - // loop over parent non-skip size list - - jlist = firstneigh_skip[i]; - jnum = numneigh_skip[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (ijskip[itype][type[j]]) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx * delx + dely * dely + delz * delz; - if (rsq > cutsq_custom) continue; - - // flip I,J if necessary to satisfy onesided constraint - // do not keep if I is now ghost - - if (surf[i] >= 0) { - if (j >= nlocal) continue; - tmp = i; - i = j; - j = tmp; - flip = 1; - } else flip = 0; - - numneigh[i]++; - if (flip) i = j; - } - } - - // allocate all per-atom neigh list chunks - - for (i = 0; i < nlocal; i++) { - if (numneigh[i] == 0) continue; - firstneigh[i] = ipage->get(numneigh[i]); - if (ipage->status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - // second loop over atoms in other list to store neighbors - // skip I atom entirely if iskip is set for type[I] - // skip I,J pair if ijskip is set for type[I],type[J] - - for (i = 0; i < nlocal; i++) numneigh[i] = 0; - - for (ii = 0; ii < inum_skip; ii++) { - i = ilist_skip[ii]; - itype = type[i]; - if (iskip[itype]) continue; - - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - - // loop over parent non-skip size list and optionally its history info - - jlist = firstneigh_skip[i]; - jnum = numneigh_skip[i]; - - for (jj = 0; jj < jnum; jj++) { - joriginal = jlist[jj]; - j = joriginal & NEIGHMASK; - if (ijskip[itype][type[j]]) continue; - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx * delx + dely * dely + delz * delz; - if (rsq > cutsq_custom) continue; - - // flip I,J if necessary to satisfy onesided constraint - // do not keep if I is now ghost - - if (surf[i] >= 0) { - if (j >= nlocal) continue; - tmp = i; - i = j; - j = tmp; - flip = 1; - } else flip = 0; - - // store j in neigh list, not joriginal, like other neigh methods - // OK, b/c there is no special list flagging for surfs - - firstneigh[i][numneigh[i]] = j; - numneigh[i]++; - if (flip) i = j; - } - - // only add atom I to ilist if it has neighbors - - if (numneigh[i]) ilist[inum++] = i; - } - - list->inum = inum; -} diff --git a/src/npair_skip_trim_size_off2on_oneside.h b/src/npair_skip_trim_size_off2on_oneside.h deleted file mode 100644 index 27861123dd..0000000000 --- a/src/npair_skip_trim_size_off2on_oneside.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(skip/trim/size/off2on/oneside, - NPairSkipTrimSizeOff2onOneside, - NP_SKIP | NP_SIZE | NP_OFF2ON | NP_ONESIDE | NP_HALF | - NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | - NP_ORTHO | NP_TRI | NP_TRIM); -// clang-format on -#else - -#ifndef LMP_NPAIR_SKIP_TRIM_SIZE_OFF2ON_ONESIDE_H -#define LMP_NPAIR_SKIP_TRIM_SIZE_OFF2ON_ONESIDE_H - -#include "npair.h" - -namespace LAMMPS_NS { - -class NPairSkipTrimSizeOff2onOneside : public NPair { - public: - NPairSkipTrimSizeOff2onOneside(class LAMMPS *); - void build(class NeighList *) override; -}; - -} // namespace LAMMPS_NS - -#endif -#endif From 7c4017018233fc7f851889656b974651f4f0dc21 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Sun, 10 Dec 2023 21:37:13 -0700 Subject: [PATCH 081/189] Fixing errors in headers --- src/OPENMP/npair_skip_omp.h | 36 +++++++++++++++++++++ src/OPENMP/npair_skip_trim_omp.h | 55 -------------------------------- src/npair_skip_respa.h | 2 +- src/npair_skip_size_off2on.h | 2 +- 4 files changed, 38 insertions(+), 57 deletions(-) delete mode 100644 src/OPENMP/npair_skip_trim_omp.h diff --git a/src/OPENMP/npair_skip_omp.h b/src/OPENMP/npair_skip_omp.h index ce61968c17..937304ad3f 100644 --- a/src/OPENMP/npair_skip_omp.h +++ b/src/OPENMP/npair_skip_omp.h @@ -16,6 +16,7 @@ #ifdef NPAIR_CLASS // clang-format off + NPairStyle(skip/omp, NPairSkip, NP_SKIP | NP_HALF | NP_FULL | @@ -50,6 +51,41 @@ NPairStyle(skip/ghost/omp, NP_SKIP | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_OMP | NP_GHOST); + +NPairStyle(skip/trim/omp, + NPairSkipTrim, + NP_SKIP | NP_HALF | NP_FULL | + NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM | NP_OMP); + +NPairStyle(skip/trim/half/respa/omp, + NPairSkipTrimRespa, + NP_SKIP | NP_RESPA | NP_HALF | NP_FULL | + NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM | NP_OMP); + +NPairStyle(skip/trim/half/size/omp, + NPairSkipTrimSize, + NP_SKIP | NP_SIZE | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM | NP_OMP); + +NPairStyle(skip/trim/size/off2on/omp, + NPairSkipTrimSizeOff2on, + NP_SKIP | NP_SIZE | NP_OFF2ON | NP_HALF | + NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_MULTI_OLD | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM | NP_OMP); + +NPairStyle(skip/trim/size/off2on/oneside/omp, + NPairSkipTrimSizeOff2onOneside, + NP_SKIP | NP_SIZE | NP_OFF2ON | NP_ONESIDE | NP_HALF | + NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | + NP_ORTHO | NP_TRI | NP_TRIM | NP_OMP); + +NPairStyle(skip/trim/ghost/omp, + NPairSkipTrim, + NP_SKIP | NP_HALF | NP_FULL | + NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM | NP_OMP | NP_GHOST); // clang-format off #endif diff --git a/src/OPENMP/npair_skip_trim_omp.h b/src/OPENMP/npair_skip_trim_omp.h deleted file mode 100644 index aba6f50e17..0000000000 --- a/src/OPENMP/npair_skip_trim_omp.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -*- 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. -------------------------------------------------------------------------- */ - -// There is no benefit from multi-threading for skip lists, so we -// just forward the requests to the corresponding non-omp versions. - -#ifdef NPAIR_CLASS -// clang-format off -NPairStyle(skip/trim/omp, - NPairSkipTrim, - NP_SKIP | NP_HALF | NP_FULL | - NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM | NP_OMP); - -NPairStyle(skip/trim/half/respa/omp, - NPairSkipTrimRespa, - NP_SKIP | NP_RESPA | NP_HALF | NP_FULL | - NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM | NP_OMP); - -NPairStyle(skip/trim/half/size/omp, - NPairSkipTrimSize, - NP_SKIP | NP_SIZE | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM | NP_OMP); - -NPairStyle(skip/trim/size/off2on/omp, - NPairSkipTrimSizeOff2on, - NP_SKIP | NP_SIZE | NP_OFF2ON | NP_HALF | - NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_MULTI_OLD | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM | NP_OMP); - -NPairStyle(skip/trim/size/off2on/oneside/omp, - NPairSkipTrimSizeOff2onOneside, - NP_SKIP | NP_SIZE | NP_OFF2ON | NP_ONESIDE | NP_HALF | - NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | - NP_ORTHO | NP_TRI | NP_TRIM | NP_OMP); - -NPairStyle(skip/trim/ghost/omp, - NPairSkipTrim, - NP_SKIP | NP_HALF | NP_FULL | - NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM | NP_OMP | NP_GHOST); -// clang-format off -#endif - diff --git a/src/npair_skip_respa.h b/src/npair_skip_respa.h index 0fa4ece699..af25e84faf 100644 --- a/src/npair_skip_respa.h +++ b/src/npair_skip_respa.h @@ -20,7 +20,7 @@ NPairStyle(skip/half/respa, NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairSkipRespaTemp<0> NPairSkipTrimRespa; +typedef NPairSkipRespaTemp<1> NPairSkipTrimRespa; NPairStyle(skip/trim/half/respa, NPairSkipTrimRespa, NP_SKIP | NP_RESPA | NP_HALF | NP_FULL | diff --git a/src/npair_skip_size_off2on.h b/src/npair_skip_size_off2on.h index e26d2cdd87..b86100ae20 100644 --- a/src/npair_skip_size_off2on.h +++ b/src/npair_skip_size_off2on.h @@ -20,7 +20,7 @@ NPairStyle(skip/size/off2on, NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairSkipSizeOff2onTemp<0> NPairSkipTrimSizeOff2on; +typedef NPairSkipSizeOff2onTemp<1> NPairSkipTrimSizeOff2on; NPairStyle(skip/trim/size/off2on, NPairSkipTrimSizeOff2on, NP_SKIP | NP_SIZE | NP_OFF2ON | NP_HALF | From af74afdf629a08110f0793e3c0b0e7088a71c508 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Mon, 11 Dec 2023 09:02:55 -0700 Subject: [PATCH 082/189] Editing intel files (to be templated) --- src/INTEL/npair_skip_intel.cpp | 243 ++++++++++++++++++++++++- src/INTEL/npair_skip_intel.h | 28 +++ src/INTEL/npair_skip_trim_intel.cpp | 271 ---------------------------- src/INTEL/npair_skip_trim_intel.h | 62 ------- 4 files changed, 270 insertions(+), 334 deletions(-) delete mode 100644 src/INTEL/npair_skip_trim_intel.cpp delete mode 100644 src/INTEL/npair_skip_trim_intel.h diff --git a/src/INTEL/npair_skip_intel.cpp b/src/INTEL/npair_skip_intel.cpp index b023955dd9..8840f7ee43 100644 --- a/src/INTEL/npair_skip_intel.cpp +++ b/src/INTEL/npair_skip_intel.cpp @@ -13,7 +13,7 @@ ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- - Contributing author: W. Michael Brown (Intel) + Contributing author: W. Michael Brown (Intel), Stan Moore (SNL) ------------------------------------------------------------------------- */ #include "npair_skip_intel.h" @@ -224,3 +224,244 @@ void NPairSkipIntel::build(NeighList *list) } } } + +/* ---------------------------------------------------------------------- */ + +NPairSkipTrimIntel::NPairSkipTrimIntel(LAMMPS *lmp) : NPair(lmp) { + _fix = static_cast(modify->get_fix_by_id("package_intel")); + if (!_fix) error->all(FLERR, "The 'package intel' command is required for /intel styles"); + _inum_starts = new int[comm->nthreads]; + _inum_counts = new int[comm->nthreads]; + _full_props = nullptr; +} + +/* ---------------------------------------------------------------------- */ + +NPairSkipTrimIntel::~NPairSkipTrimIntel() { + delete []_inum_starts; + delete []_inum_counts; + delete[] _full_props; +} + +/* ---------------------------------------------------------------------- */ + +void NPairSkipTrimIntel::copy_neighbor_info() +{ + NPair::copy_neighbor_info(); + // Only need to set _full_props once; npair object deleted for changes + if (_full_props) return; + _full_props = new int[neighbor->nrequest]; + for (int i = 0; i < neighbor->nrequest; i++) + _full_props[i] = neighbor->requests[i]->full; +} + +/* ---------------------------------------------------------------------- + build skip list for subset of types from parent list + works for half and full lists + works for owned (non-ghost) list, also for ghost list + iskip and ijskip flag which atom types and type pairs to skip + if ghost, also store neighbors of ghost atoms & set inum,gnum correctly +------------------------------------------------------------------------- */ + +template +void NPairSkipTrimIntel::build_t(NeighList *list, int *numhalf, int *cnumneigh, + int *numhalf_skip, IntelBuffers *buffers) +{ + const int nlocal = atom->nlocal; + const int e_nall = nlocal + atom->nghost; + const ATOM_T * _noalias const x = buffers->get_x(); + const int * _noalias const type = atom->type; + int * _noalias const ilist = list->ilist; + int * _noalias const numneigh = list->numneigh; + int ** _noalias const firstneigh = (int ** const)list->firstneigh; // NOLINT + const int * _noalias const ilist_skip = list->listskip->ilist; + const int * _noalias const numneigh_skip = list->listskip->numneigh; + const int ** _noalias const firstneigh_skip = (const int ** const)list->listskip->firstneigh; // NOLINT + const int * _noalias const iskip = list->iskip; + const int ** _noalias const ijskip = (const int ** const)list->ijskip; // NOLINT + + const flt_t cutsq_custom = cutoff_custom * cutoff_custom; + int num_skip = list->listskip->inum; + if (list->ghost) num_skip += list->listskip->gnum; + + int packthreads; + if (comm->nthreads > INTEL_HTHREADS && THREE==0) + packthreads = comm->nthreads; + else + packthreads = 1; + + #if defined(_OPENMP) + #pragma omp parallel if (packthreads > 1) + #endif + { + int tid, ifrom, ito; + IP_PRE_omp_range_id(ifrom, ito, tid, num_skip, packthreads); + + // each thread has its own page allocator + MyPage &ipage = list->ipage[tid]; + ipage.reset(); + + int my_inum = ifrom; + _inum_starts[tid] = ifrom; + + // loop over parent full list + for (int ii = ifrom; ii < ito; ii++) { + const int i = ilist_skip[ii]; + const int itype = type[i]; + if (iskip[itype]) continue; + + const flt_t xtmp = x[i].x; + const flt_t ytmp = x[i].y; + const flt_t ztmp = x[i].z; + + int n = 0; + int *neighptr = ipage.vget(); + + // loop over parent non-skip list + + const int * _noalias const jlist = firstneigh_skip[i]; + const int jnum = numneigh_skip[i]; + + if (THREE) { + const int jnumhalf = numhalf_skip[ii]; + for (int jj = 0; jj < jnumhalf; jj++) { + const int joriginal = jlist[jj]; + const int j = joriginal & NEIGHMASK; + + int addme = 1; + if (ijskip[itype][type[j]]) addme = 0; + + // trim to shorter cutoff + + const flt_t delx = xtmp - x[j].x; + const flt_t dely = ytmp - x[j].y; + const flt_t delz = ztmp - x[j].z; + const flt_t rsq = delx * delx + dely * dely + delz * delz; + if (rsq > cutsq_custom) addme = 0; + + if (addme) + neighptr[n++] = joriginal; + } + numhalf[my_inum] = n; + + for (int jj = jnumhalf; jj < jnum; jj++) { + const int joriginal = jlist[jj]; + const int j = joriginal & NEIGHMASK; + + int addme = 1; + if (ijskip[itype][type[j]]) addme = 0; + + // trim to shorter cutoff + + const flt_t delx = xtmp - x[j].x; + const flt_t dely = ytmp - x[j].y; + const flt_t delz = ztmp - x[j].z; + const flt_t rsq = delx * delx + dely * dely + delz * delz; + if (rsq > cutsq_custom) addme = 0; + + if (addme) + neighptr[n++] = joriginal; + } + } else { + #if defined(LMP_SIMD_COMPILER) + #pragma vector aligned + #pragma ivdep + #endif + for (int jj = 0; jj < jnum; jj++) { + const int joriginal = jlist[jj]; + const int j = joriginal & NEIGHMASK; + + int addme = 1; + if (ijskip[itype][type[j]]) addme = 0; + + // trim to shorter cutoff + + const flt_t delx = xtmp - x[j].x; + const flt_t dely = ytmp - x[j].y; + const flt_t delz = ztmp - x[j].z; + const flt_t rsq = delx * delx + dely * dely + delz * delz; + if (rsq > cutsq_custom) addme = 0; + + if (addme) + neighptr[n++] = joriginal; + } + } + + ilist[my_inum++] = i; + firstneigh[i] = neighptr; + numneigh[i] = n; + + int pad_end = n; + IP_PRE_neighbor_pad(pad_end, 0); + #if defined(LMP_SIMD_COMPILER) + #pragma vector aligned + #pragma loop_count min=1, max=INTEL_COMPILE_WIDTH-1, \ + avg=INTEL_COMPILE_WIDTH/2 + #endif + for ( ; n < pad_end; n++) + neighptr[n] = e_nall; + + ipage.vgot(n); + if (ipage.status()) + error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); + } + + int last_inum = 0, loop_end; + _inum_counts[tid] = my_inum; + } + int inum = _inum_counts[0]; + for (int tid = 1; tid < packthreads; tid++) { + for (int i = _inum_starts[tid]; i < _inum_counts[tid]; i++) { + if (THREE) numhalf[inum] = numhalf[i]; + ilist[inum++] = ilist[i]; + } + } + list->inum = inum; + + if (THREE && num_skip > 0) { + int * const list_start = firstneigh[ilist[0]]; + for (int ii = 0; ii < inum; ii++) { + int i = ilist[ii]; + cnumneigh[ii] = static_cast(firstneigh[i] - list_start); + } + } + if (list->ghost) { + int num = 0; + int my_inum = list->inum; + for (int i = 0; i < my_inum; i++) + if (ilist[i] < nlocal) num++; + else break; + list->inum = num; + list->gnum = my_inum - num; + } +} + +/* ---------------------------------------------------------------------- */ + +void NPairSkipTrimIntel::build(NeighList *list) +{ + if (_fix->three_body_neighbor()==0 || + _full_props[list->listskip->index] == 0) { + if (_fix->precision() == FixIntel::PREC_MODE_MIXED) + build_t(list, nullptr, nullptr, nullptr, _fix->get_mixed_buffers()); + else if (_fix->precision() == FixIntel::PREC_MODE_DOUBLE) + build_t(list, nullptr, nullptr, nullptr, _fix->get_double_buffers()); + else + build_t(list, nullptr, nullptr, nullptr, _fix->get_single_buffers()); + } else { + int *nhalf, *cnumneigh, *nhalf_skip, *u; + if (_fix->precision() == FixIntel::PREC_MODE_MIXED) { + _fix->get_mixed_buffers()->get_list_data3(list->listskip,nhalf_skip,u); + _fix->get_mixed_buffers()->grow_data3(list, nhalf, cnumneigh); + build_t(list, nhalf, cnumneigh, nhalf_skip, _fix->get_mixed_buffers()); + } else if (_fix->precision() == FixIntel::PREC_MODE_DOUBLE) { + _fix->get_double_buffers()->get_list_data3(list->listskip,nhalf_skip,u); + _fix->get_double_buffers()->grow_data3(list, nhalf, cnumneigh); + build_t(list, nhalf, cnumneigh, nhalf_skip, _fix->get_double_buffers()); + } else { + _fix->get_single_buffers()->get_list_data3(list->listskip,nhalf_skip,u); + _fix->get_single_buffers()->grow_data3(list,nhalf,cnumneigh); + build_t(list, nhalf, cnumneigh, nhalf_skip, _fix->get_single_buffers()); + } + } +} diff --git a/src/INTEL/npair_skip_intel.h b/src/INTEL/npair_skip_intel.h index d38173895a..993f64bf96 100644 --- a/src/INTEL/npair_skip_intel.h +++ b/src/INTEL/npair_skip_intel.h @@ -25,6 +25,18 @@ NPairStyle(skip/ghost/intel, NP_SKIP | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_GHOST | NP_INTEL); + +NPairStyle(skip/trim/intel, + NPairSkipTrimIntel, + NP_SKIP | NP_HALF | NP_FULL | + NP_NSQ | NP_BIN | NP_MULTI | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM | NP_INTEL); + +NPairStyle(skip/trim/ghost/intel, + NPairSkipTrimIntel, + NP_SKIP | NP_HALF | NP_FULL | + NP_NSQ | NP_BIN | NP_MULTI | + NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM | NP_GHOST | NP_INTEL); // clang-format on #else @@ -55,6 +67,22 @@ class NPairSkipIntel : public NPair { void build_t(NeighList *, int *numhalf, int *cnumneigh, int *numhalf_skip); }; +class NPairSkipTrimIntel : public NPair { + public: + NPairSkipTrimIntel(class LAMMPS *); + ~NPairSkipTrimIntel() override; + void copy_neighbor_info() override; + void build(class NeighList *) override; + + protected: + FixIntel *_fix; + int *_inum_starts, *_inum_counts, *_full_props; + + template + void build_t(NeighList *, int *numhalf, int *cnumneigh, int *numhalf_skip, + IntelBuffers *); +}; + } // namespace LAMMPS_NS #endif diff --git a/src/INTEL/npair_skip_trim_intel.cpp b/src/INTEL/npair_skip_trim_intel.cpp deleted file mode 100644 index e16e1bc413..0000000000 --- a/src/INTEL/npair_skip_trim_intel.cpp +++ /dev/null @@ -1,271 +0,0 @@ -// 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. -------------------------------------------------------------------------- */ - -/* ---------------------------------------------------------------------- - Contributing author: Stan Moore (SNL) -------------------------------------------------------------------------- */ - -#include "npair_skip_trim_intel.h" - -#include "atom.h" -#include "comm.h" -#include "error.h" -#include "modify.h" -#include "my_page.h" -#include "neigh_list.h" -#include "neigh_request.h" -#include "neighbor.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -NPairSkipTrimIntel::NPairSkipTrimIntel(LAMMPS *lmp) : NPair(lmp) { - _fix = static_cast(modify->get_fix_by_id("package_intel")); - if (!_fix) error->all(FLERR, "The 'package intel' command is required for /intel styles"); - _inum_starts = new int[comm->nthreads]; - _inum_counts = new int[comm->nthreads]; - _full_props = nullptr; -} - -/* ---------------------------------------------------------------------- */ - -NPairSkipTrimIntel::~NPairSkipTrimIntel() { - delete []_inum_starts; - delete []_inum_counts; - delete[] _full_props; -} - -/* ---------------------------------------------------------------------- */ - -void NPairSkipTrimIntel::copy_neighbor_info() -{ - NPair::copy_neighbor_info(); - // Only need to set _full_props once; npair object deleted for changes - if (_full_props) return; - _full_props = new int[neighbor->nrequest]; - for (int i = 0; i < neighbor->nrequest; i++) - _full_props[i] = neighbor->requests[i]->full; -} - -/* ---------------------------------------------------------------------- - build skip list for subset of types from parent list - works for half and full lists - works for owned (non-ghost) list, also for ghost list - iskip and ijskip flag which atom types and type pairs to skip - if ghost, also store neighbors of ghost atoms & set inum,gnum correctly -------------------------------------------------------------------------- */ - -template -void NPairSkipTrimIntel::build_t(NeighList *list, int *numhalf, int *cnumneigh, - int *numhalf_skip, IntelBuffers *buffers) -{ - const int nlocal = atom->nlocal; - const int e_nall = nlocal + atom->nghost; - const ATOM_T * _noalias const x = buffers->get_x(); - const int * _noalias const type = atom->type; - int * _noalias const ilist = list->ilist; - int * _noalias const numneigh = list->numneigh; - int ** _noalias const firstneigh = (int ** const)list->firstneigh; // NOLINT - const int * _noalias const ilist_skip = list->listskip->ilist; - const int * _noalias const numneigh_skip = list->listskip->numneigh; - const int ** _noalias const firstneigh_skip = (const int ** const)list->listskip->firstneigh; // NOLINT - const int * _noalias const iskip = list->iskip; - const int ** _noalias const ijskip = (const int ** const)list->ijskip; // NOLINT - - const flt_t cutsq_custom = cutoff_custom * cutoff_custom; - int num_skip = list->listskip->inum; - if (list->ghost) num_skip += list->listskip->gnum; - - int packthreads; - if (comm->nthreads > INTEL_HTHREADS && THREE==0) - packthreads = comm->nthreads; - else - packthreads = 1; - - #if defined(_OPENMP) - #pragma omp parallel if (packthreads > 1) - #endif - { - int tid, ifrom, ito; - IP_PRE_omp_range_id(ifrom, ito, tid, num_skip, packthreads); - - // each thread has its own page allocator - MyPage &ipage = list->ipage[tid]; - ipage.reset(); - - int my_inum = ifrom; - _inum_starts[tid] = ifrom; - - // loop over parent full list - for (int ii = ifrom; ii < ito; ii++) { - const int i = ilist_skip[ii]; - const int itype = type[i]; - if (iskip[itype]) continue; - - const flt_t xtmp = x[i].x; - const flt_t ytmp = x[i].y; - const flt_t ztmp = x[i].z; - - int n = 0; - int *neighptr = ipage.vget(); - - // loop over parent non-skip list - - const int * _noalias const jlist = firstneigh_skip[i]; - const int jnum = numneigh_skip[i]; - - if (THREE) { - const int jnumhalf = numhalf_skip[ii]; - for (int jj = 0; jj < jnumhalf; jj++) { - const int joriginal = jlist[jj]; - const int j = joriginal & NEIGHMASK; - - int addme = 1; - if (ijskip[itype][type[j]]) addme = 0; - - // trim to shorter cutoff - - const flt_t delx = xtmp - x[j].x; - const flt_t dely = ytmp - x[j].y; - const flt_t delz = ztmp - x[j].z; - const flt_t rsq = delx * delx + dely * dely + delz * delz; - if (rsq > cutsq_custom) addme = 0; - - if (addme) - neighptr[n++] = joriginal; - } - numhalf[my_inum] = n; - - for (int jj = jnumhalf; jj < jnum; jj++) { - const int joriginal = jlist[jj]; - const int j = joriginal & NEIGHMASK; - - int addme = 1; - if (ijskip[itype][type[j]]) addme = 0; - - // trim to shorter cutoff - - const flt_t delx = xtmp - x[j].x; - const flt_t dely = ytmp - x[j].y; - const flt_t delz = ztmp - x[j].z; - const flt_t rsq = delx * delx + dely * dely + delz * delz; - if (rsq > cutsq_custom) addme = 0; - - if (addme) - neighptr[n++] = joriginal; - } - } else { - #if defined(LMP_SIMD_COMPILER) - #pragma vector aligned - #pragma ivdep - #endif - for (int jj = 0; jj < jnum; jj++) { - const int joriginal = jlist[jj]; - const int j = joriginal & NEIGHMASK; - - int addme = 1; - if (ijskip[itype][type[j]]) addme = 0; - - // trim to shorter cutoff - - const flt_t delx = xtmp - x[j].x; - const flt_t dely = ytmp - x[j].y; - const flt_t delz = ztmp - x[j].z; - const flt_t rsq = delx * delx + dely * dely + delz * delz; - if (rsq > cutsq_custom) addme = 0; - - if (addme) - neighptr[n++] = joriginal; - } - } - - ilist[my_inum++] = i; - firstneigh[i] = neighptr; - numneigh[i] = n; - - int pad_end = n; - IP_PRE_neighbor_pad(pad_end, 0); - #if defined(LMP_SIMD_COMPILER) - #pragma vector aligned - #pragma loop_count min=1, max=INTEL_COMPILE_WIDTH-1, \ - avg=INTEL_COMPILE_WIDTH/2 - #endif - for ( ; n < pad_end; n++) - neighptr[n] = e_nall; - - ipage.vgot(n); - if (ipage.status()) - error->one(FLERR,"Neighbor list overflow, boost neigh_modify one"); - } - - int last_inum = 0, loop_end; - _inum_counts[tid] = my_inum; - } - int inum = _inum_counts[0]; - for (int tid = 1; tid < packthreads; tid++) { - for (int i = _inum_starts[tid]; i < _inum_counts[tid]; i++) { - if (THREE) numhalf[inum] = numhalf[i]; - ilist[inum++] = ilist[i]; - } - } - list->inum = inum; - - if (THREE && num_skip > 0) { - int * const list_start = firstneigh[ilist[0]]; - for (int ii = 0; ii < inum; ii++) { - int i = ilist[ii]; - cnumneigh[ii] = static_cast(firstneigh[i] - list_start); - } - } - if (list->ghost) { - int num = 0; - int my_inum = list->inum; - for (int i = 0; i < my_inum; i++) - if (ilist[i] < nlocal) num++; - else break; - list->inum = num; - list->gnum = my_inum - num; - } -} - -/* ---------------------------------------------------------------------- */ - -void NPairSkipTrimIntel::build(NeighList *list) -{ - if (_fix->three_body_neighbor()==0 || - _full_props[list->listskip->index] == 0) { - if (_fix->precision() == FixIntel::PREC_MODE_MIXED) - build_t(list, nullptr, nullptr, nullptr, _fix->get_mixed_buffers()); - else if (_fix->precision() == FixIntel::PREC_MODE_DOUBLE) - build_t(list, nullptr, nullptr, nullptr, _fix->get_double_buffers()); - else - build_t(list, nullptr, nullptr, nullptr, _fix->get_single_buffers()); - } else { - int *nhalf, *cnumneigh, *nhalf_skip, *u; - if (_fix->precision() == FixIntel::PREC_MODE_MIXED) { - _fix->get_mixed_buffers()->get_list_data3(list->listskip,nhalf_skip,u); - _fix->get_mixed_buffers()->grow_data3(list, nhalf, cnumneigh); - build_t(list, nhalf, cnumneigh, nhalf_skip, _fix->get_mixed_buffers()); - } else if (_fix->precision() == FixIntel::PREC_MODE_DOUBLE) { - _fix->get_double_buffers()->get_list_data3(list->listskip,nhalf_skip,u); - _fix->get_double_buffers()->grow_data3(list, nhalf, cnumneigh); - build_t(list, nhalf, cnumneigh, nhalf_skip, _fix->get_double_buffers()); - } else { - _fix->get_single_buffers()->get_list_data3(list->listskip,nhalf_skip,u); - _fix->get_single_buffers()->grow_data3(list,nhalf,cnumneigh); - build_t(list, nhalf, cnumneigh, nhalf_skip, _fix->get_single_buffers()); - } - } -} diff --git a/src/INTEL/npair_skip_trim_intel.h b/src/INTEL/npair_skip_trim_intel.h deleted file mode 100644 index f0018e5df4..0000000000 --- a/src/INTEL/npair_skip_trim_intel.h +++ /dev/null @@ -1,62 +0,0 @@ -// clang-format off -/* -*- 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 NPAIR_CLASS -// clang-format off -NPairStyle(skip/trim/intel, - NPairSkipTrimIntel, - NP_SKIP | NP_HALF | NP_FULL | - NP_NSQ | NP_BIN | NP_MULTI | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM | NP_INTEL); - -NPairStyle(skip/trim/ghost/intel, - NPairSkipTrimIntel, - NP_SKIP | NP_HALF | NP_FULL | - NP_NSQ | NP_BIN | NP_MULTI | - NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM | NP_GHOST | NP_INTEL); -// clang-format on -#else - -#ifndef LMP_NPAIR_SKIP_TRIM_INTEL_H -#define LMP_NPAIR_SKIP_TRIM_INTEL_H - -#include "fix_intel.h" -#include "npair.h" - -#if defined(_OPENMP) -#include -#endif - -namespace LAMMPS_NS { - -class NPairSkipTrimIntel : public NPair { - public: - NPairSkipTrimIntel(class LAMMPS *); - ~NPairSkipTrimIntel() override; - void copy_neighbor_info() override; - void build(class NeighList *) override; - - protected: - FixIntel *_fix; - int *_inum_starts, *_inum_counts, *_full_props; - - template - void build_t(NeighList *, int *numhalf, int *cnumneigh, int *numhalf_skip, - IntelBuffers *); -}; - -} // namespace LAMMPS_NS - -#endif -#endif From e241f08cfe3c24a49d46cf9e7515b4db335406e4 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Mon, 11 Dec 2023 14:05:42 -0700 Subject: [PATCH 083/189] Finish first version of compute reaxff/atom docs --- doc/src/compute.rst | 1 + doc/src/compute_reaxff_atom.rst | 38 ++++++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/doc/src/compute.rst b/doc/src/compute.rst index 6737203618..6ef093c16d 100644 --- a/doc/src/compute.rst +++ b/doc/src/compute.rst @@ -280,6 +280,7 @@ The individual style names on the :doc:`Commands compute ` pag * :doc:`property/local ` - convert local attributes to local vectors/arrays * :doc:`ptm/atom ` - determines the local lattice structure based on the Polyhedral Template Matching method * :doc:`rdf ` - radial distribution function :math:`g(r)` histogram of group of atoms +* :doc:`reaxff/atom ` - extract ReaxFF bond information * :doc:`reduce ` - combine per-atom quantities into a single global value * :doc:`reduce/chunk ` - reduce per-atom quantities within each chunk * :doc:`reduce/region ` - same as compute reduce, within a region diff --git a/doc/src/compute_reaxff_atom.rst b/doc/src/compute_reaxff_atom.rst index 4906f3fe5c..9f690d19c3 100644 --- a/doc/src/compute_reaxff_atom.rst +++ b/doc/src/compute_reaxff_atom.rst @@ -1,8 +1,8 @@ -.. index:: fix reaxff/atom -.. index:: fix reaxff/atom/kk +.. index:: compute reaxff/atom +.. index:: compute reaxff/atom/kk -fix reaxff/atom command -======================= +compute reaxff/atom command +=========================== Accelerator Variants: *reaxff/atom/kk* @@ -40,7 +40,35 @@ Examples Description """"""""""" -TODO +Define a computation that extracts bond information computed by the ReaxFF +potential specified by :doc:`pair_style reaxff `. + +By default, it produces per-atom data that includes the following columns: + +* abo = atom bond order (sum of all bonds) +* nlp = number of lone pairs +* nb = number of bonds + +Bonds will only be included if its atoms are in the group. + +In addition, if ``bonds`` is set to ``yes``, the compute will also produce a +local array of all bonds on the current processor whose atoms are in the group. +The columns of each entry of this local array are: + +* id_i = atom i id of bond +* id_j = atom j id of bond +* bo = bond order of bond + +Output info +""""""""""" + +This compute calculates a per-atom array and local array depending on the +number of keywords. The number of rows in the local array is the number of +bonds as described above. Both per-atom and local array have 3 columns. + +The arrays can be accessed by any command that uses local and per-atom values +from a compute as input. See the :doc:`Howto output ` page for +an overview of LAMMPS output options. ---------- From 930fbe8c5df277cb0aa1195a5a7ad031a2585a5c Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Mon, 11 Dec 2023 14:07:48 -0700 Subject: [PATCH 084/189] reaxff/atom: First attempt of filtering by group --- src/KOKKOS/compute_reaxff_atom_kokkos.cpp | 4 +-- src/KOKKOS/pair_reaxff_kokkos.cpp | 37 +++++++++++++---------- src/KOKKOS/pair_reaxff_kokkos.h | 10 +++--- src/REAXFF/compute_reaxff_atom.cpp | 31 +++++++++++-------- 4 files changed, 47 insertions(+), 35 deletions(-) diff --git a/src/KOKKOS/compute_reaxff_atom_kokkos.cpp b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp index c703bc3552..c3e9367ff4 100644 --- a/src/KOKKOS/compute_reaxff_atom_kokkos.cpp +++ b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp @@ -82,9 +82,9 @@ void ComputeReaxFFAtomKokkos::compute_bonds() int maxnumbonds = 0; if (reaxff->execution_space == Device) - device_pair()->FindBond(maxnumbonds); + device_pair()->FindBond(maxnumbonds, groupbit); else - host_pair()->FindBond(maxnumbonds); + host_pair()->FindBond(maxnumbonds, groupbit); nbuf = ((store_bonds ? maxnumbonds*2 : 0) + 3)*nlocal; diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index 914962f0e6..29ac398128 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -4162,22 +4162,23 @@ double PairReaxFFKokkos::memory_usage() /* ---------------------------------------------------------------------- */ template -void PairReaxFFKokkos::FindBond(int &numbonds) +void PairReaxFFKokkos::FindBond(int &numbonds, int groupbit) { copymode = 1; Kokkos::parallel_for(Kokkos::RangePolicy(0,nmax),*this); bo_cut_bond = api->control->bg_cut; - atomKK->sync(execution_space,TAG_MASK); + atomKK->sync(execution_space,TAG_MASK|MASK_MASK); tag = atomKK->k_tag.view(); + mask = atomKK->k_mask.view(); const int inum = list->inum; NeighListKokkos* k_list = static_cast*>(list); d_ilist = k_list->d_ilist; numbonds = 0; - PairReaxKokkosFindBondFunctor find_bond_functor(this); + PairReaxKokkosFindBondFunctor find_bond_functor(this, groupbit); Kokkos::parallel_reduce(inum,find_bond_functor,numbonds); copymode = 0; } @@ -4194,24 +4195,28 @@ void PairReaxFFKokkos::operator()(TagPairReaxFindBondZero, const int template KOKKOS_INLINE_FUNCTION -void PairReaxFFKokkos::calculate_find_bond_item(int ii, int &numbonds) const +void PairReaxFFKokkos::calculate_find_bond_item(int ii, int &numbonds, int groupbit) const { const int i = d_ilist[ii]; int nj = 0; - const int j_start = d_bo_first[i]; - const int j_end = j_start + d_bo_num[i]; - for (int jj = j_start; jj < j_end; jj++) { - int j = d_bo_list[jj]; - j &= NEIGHMASK; - const tagint jtag = tag[j]; - const int j_index = jj - j_start; - double bo_tmp = d_BO(i,j_index); + if(mask[i] & groupbit) { + const int j_start = d_bo_first[i]; + const int j_end = j_start + d_bo_num[i]; + for (int jj = j_start; jj < j_end; jj++) { + int j = d_bo_list[jj]; + j &= NEIGHMASK; + if(mask[j] & groupbit) { + const tagint jtag = tag[j]; + const int j_index = jj - j_start; + double bo_tmp = d_BO(i,j_index); - if (bo_tmp > bo_cut_bond) { - d_neighid(i,nj) = jtag; - d_abo(i,nj) = bo_tmp; - nj++; + if (bo_tmp > bo_cut_bond) { + d_neighid(i,nj) = jtag; + d_abo(i,nj) = bo_tmp; + nj++; + } + } } } d_numneigh_bonds[i] = nj; diff --git a/src/KOKKOS/pair_reaxff_kokkos.h b/src/KOKKOS/pair_reaxff_kokkos.h index f246afcc86..f54350b04b 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.h +++ b/src/KOKKOS/pair_reaxff_kokkos.h @@ -133,7 +133,7 @@ class PairReaxFFKokkos : public PairReaxFF { void compute(int, int); void init_style(); double memory_usage(); - void FindBond(int &); + void FindBond(int &, int groupbit = 1); void PackBondBuffer(DAT::tdual_ffloat_1d, int &); void PackReducedBondBuffer(DAT::tdual_ffloat_1d, int &, bool); void FindBondSpecies(); @@ -288,7 +288,7 @@ class PairReaxFFKokkos : public PairReaxFF { void operator()(TagPairReaxFindBondZero, const int&) const; KOKKOS_INLINE_FUNCTION - void calculate_find_bond_item(int, int&) const; + void calculate_find_bond_item(int, int&, int) const; KOKKOS_INLINE_FUNCTION void pack_bond_buffer_item(int, int&, const bool&) const; @@ -417,6 +417,7 @@ class PairReaxFFKokkos : public PairReaxFF { typename AT::t_f_array f; typename AT::t_int_1d_randomread type; typename AT::t_tagint_1d_randomread tag; + typename AT::t_int_1d_randomread mask; typename AT::t_float_1d_randomread q; typename AT::t_tagint_1d_randomread molecule; @@ -526,8 +527,9 @@ template struct PairReaxKokkosFindBondFunctor { typedef DeviceType device_type; typedef int value_type; + int groupbit; PairReaxFFKokkos c; - PairReaxKokkosFindBondFunctor(PairReaxFFKokkos* c_ptr):c(*c_ptr) {}; + PairReaxKokkosFindBondFunctor(PairReaxFFKokkos* c_ptr, int groupbit):c(*c_ptr),groupbit(groupbit) {}; KOKKOS_INLINE_FUNCTION void join(int &dst, @@ -537,7 +539,7 @@ struct PairReaxKokkosFindBondFunctor { KOKKOS_INLINE_FUNCTION void operator()(const int ii, int &numbonds) const { - c.calculate_find_bond_item(ii,numbonds); + c.calculate_find_bond_item(ii,numbonds,groupbit); } }; diff --git a/src/REAXFF/compute_reaxff_atom.cpp b/src/REAXFF/compute_reaxff_atom.cpp index 2e5d4058b8..fba67ecacf 100644 --- a/src/REAXFF/compute_reaxff_atom.cpp +++ b/src/REAXFF/compute_reaxff_atom.cpp @@ -126,28 +126,33 @@ int ComputeReaxFFAtom::FindBond() bo_cut = reaxff->api->control->bg_cut; tagint *tag = atom->tag; + int * mask = atom->mask; int numbonds = 0; for (ii = 0; ii < inum; ii++) { i = ilist[ii]; - nj = 0; + if (mask[i] & groupbit) { + nj = 0; - for (pj = Start_Index(i, reaxff->api->lists); pj < End_Index(i, reaxff->api->lists); ++pj) { - bo_ij = &(reaxff->api->lists->select.bond_list[pj]); - j = bo_ij->nbr; - jtag = tag[j]; - bo_tmp = bo_ij->bo_data.BO; + for (pj = Start_Index(i, reaxff->api->lists); pj < End_Index(i, reaxff->api->lists); ++pj) { + bo_ij = &(reaxff->api->lists->select.bond_list[pj]); + j = bo_ij->nbr; + if (mask[j] & groupbit) { + jtag = tag[j]; + bo_tmp = bo_ij->bo_data.BO; - if (bo_tmp > bo_cut) { - if(store_bonds) { - neighid[i][nj] = jtag; - abo[i][nj] = bo_tmp; + if (bo_tmp > bo_cut) { + if(store_bonds) { + neighid[i][nj] = jtag; + abo[i][nj] = bo_tmp; + } + nj++; + } } - nj++; } + bondcount[i] = nj; + numbonds += nj; } - bondcount[i] = nj; - numbonds += nj; } return numbonds; } From 9100f486cb182651020d142a63cb4b30fbc4fa9d Mon Sep 17 00:00:00 2001 From: jtclemm Date: Tue, 12 Dec 2023 15:51:02 -0700 Subject: [PATCH 085/189] Clarify and fix logic in multi, simplify skip --- src/OPENMP/npair_multi_omp.cpp | 48 +++++++++++++++++----------------- src/npair_multi.cpp | 46 ++++++++++++++++---------------- src/npair_skip.cpp | 14 +++++----- src/npair_skip.h | 14 +++++----- 4 files changed, 60 insertions(+), 62 deletions(-) diff --git a/src/OPENMP/npair_multi_omp.cpp b/src/OPENMP/npair_multi_omp.cpp index 818470254c..3f8604572c 100644 --- a/src/OPENMP/npair_multi_omp.cpp +++ b/src/OPENMP/npair_multi_omp.cpp @@ -118,8 +118,7 @@ void NPairMultiOmp::build(NeighList *list) for (jcollection = 0; jcollection < ncollections; jcollection++) { - // if same collection use own bin - + // Use own bin for same collection if (icollection == jcollection) jbin = ibin; else jbin = coord2bin(x[i], jcollection); @@ -129,34 +128,34 @@ void NPairMultiOmp::build(NeighList *list) for (k = 0; k < ns; k++) { js = binhead_multi[jcollection][jbin + s[k]]; - // own-bin for half stencil - if (HALF && !TRI) - if (k == 0 && flag_half_multi[icollection][jcollection]) js = bins[i]; + // For half-newton-ortho, first check self bin (k == 0, always half) + // if checking its own binlist, skip all before i in linked list + if (HALF && NEWTON && !TRI) + if ((k == 0) && (icollection == jcollection)) js = bins[i]; for (j = js; j >= 0; j = bins[j]) { if (!HALF) { - // Full neighbor list + // Full neighbor list, only uses full stencils // only skip i = j if (i == j) continue; } else if (!NEWTON) { - // Half neighbor list, newton off + // Half neighbor list, newton off, only uses full stencils // only store pair if i < j // stores own/own pairs only once // stores own/ghost pairs on both procs if (j <= i) continue; } else if (TRI) { - // Half neighbor list, newton on, triclinic - // if same size (same collection), use half stencil - // Always have full stencil - // if same size (same collection), exclude half of interactions - // stencil is empty if i larger than j - // stencil is full if i smaller than j - // stencil is full if i same size as j - // for i smaller than j: - // must use itag/jtag to eliminate half the I/J interactions - // cannot use I/J exact coord comparision - // b/c transforming orthog -> lambda -> orthog for ghost atoms - // with an added PBC offset can shift all 3 coords by epsilon + // Half neighbor list, newton on, triclinic, only uses full stencils + // If different sizes -> full stencil (accept all, one-way search) + // If same size -> half stencil, exclude half of interactions + // stencil is empty if i larger than j + // stencil is full if i smaller than j + // stencil is full if i same size as j + // for i smaller than j: + // must use itag/jtag to eliminate half the I/J interactions + // cannot use I/J exact coord comparision + // b/c transforming orthog -> lambda -> orthog for ghost atoms + // with an added PBC offset can shift all 3 coords by epsilon if (flag_same_multi[icollection][jcollection]) { if (j <= i) continue; @@ -178,9 +177,10 @@ void NPairMultiOmp::build(NeighList *list) } } } else { - // Half neighbor list, newton on, orthonormal - // if same size: uses half stencil so includes a check of the central bin - if (k == 0 && flag_half_multi[icollection][jcollection]) { + // Half neighbor list, newton on, orthonormal, uses a mix of stencils + // If different sizes -> full stencil (accept all, one-way search) + // If same size -> half stencil (first includes a self bin search) + if (k == 0 && flag_same_multi[icollection][jcollection]) { // if same collection, // if j is owned atom, store it, since j is beyond i in linked list // if j is ghost, only store if j coords are "above and to the right" of i @@ -217,14 +217,14 @@ void NPairMultiOmp::build(NeighList *list) if (ATOMONLY) { if (rsq <= cutsq) { jh = j; - if (history && rsq < radsum * radsum) + if (history && rsq < (radsum * radsum)) jh = jh ^ mask_history; neighptr[n++] = jh; } } else { if (rsq <= cutsq) { jh = j; - if (history && rsq < radsum * radsum) + if (history && rsq < (radsum * radsum)) jh = jh ^ mask_history; if (molecular != Atom::ATOMIC) { diff --git a/src/npair_multi.cpp b/src/npair_multi.cpp index ec5a67fa82..b5f813c0a8 100644 --- a/src/npair_multi.cpp +++ b/src/npair_multi.cpp @@ -115,8 +115,7 @@ void NPairMulti::build(NeighList *list) for (jcollection = 0; jcollection < ncollections; jcollection++) { - // if same collection use own bin - + // Use own bin for same collection if (icollection == jcollection) jbin = ibin; else jbin = coord2bin(x[i], jcollection); @@ -126,34 +125,34 @@ void NPairMulti::build(NeighList *list) for (k = 0; k < ns; k++) { js = binhead_multi[jcollection][jbin + s[k]]; - // own-bin for half stencil - if (HALF && !TRI) - if (k == 0 && flag_half_multi[icollection][jcollection]) js = bins[i]; + // For half-newton-ortho, first check self bin (k == 0, always half) + // if checking its own binlist, skip all before i in linked list + if (HALF && NEWTON && !TRI) + if ((k == 0) && (icollection == jcollection)) js = bins[i]; for (j = js; j >= 0; j = bins[j]) { if (!HALF) { - // Full neighbor list + // Full neighbor list, only uses full stencils // only skip i = j if (i == j) continue; } else if (!NEWTON) { - // Half neighbor list, newton off + // Half neighbor list, newton off, only uses full stencils // only store pair if i < j // stores own/own pairs only once // stores own/ghost pairs on both procs if (j <= i) continue; } else if (TRI) { - // Half neighbor list, newton on, triclinic - // if same size (same collection), use half stencil - // Always have full stencil - // if same size (same collection), exclude half of interactions - // stencil is empty if i larger than j - // stencil is full if i smaller than j - // stencil is full if i same size as j - // for i smaller than j: - // must use itag/jtag to eliminate half the I/J interactions - // cannot use I/J exact coord comparision - // b/c transforming orthog -> lambda -> orthog for ghost atoms - // with an added PBC offset can shift all 3 coords by epsilon + // Half neighbor list, newton on, triclinic, only uses full stencils + // If different sizes -> full stencil (accept all, one-way search) + // If same size -> half stencil, exclude half of interactions + // stencil is empty if i larger than j + // stencil is full if i smaller than j + // stencil is full if i same size as j + // for i smaller than j: + // must use itag/jtag to eliminate half the I/J interactions + // cannot use I/J exact coord comparision + // b/c transforming orthog -> lambda -> orthog for ghost atoms + // with an added PBC offset can shift all 3 coords by epsilon if (flag_same_multi[icollection][jcollection]) { if (j <= i) continue; @@ -175,9 +174,10 @@ void NPairMulti::build(NeighList *list) } } } else { - // Half neighbor list, newton on, orthonormal - // if same size: uses half stencil so includes a check of the central bin - if (k == 0 && flag_half_multi[icollection][jcollection]) { + // Half neighbor list, newton on, orthonormal, uses a mix of stencils + // If different sizes -> full stencil (accept all, one-way search) + // If same size -> half stencil (first includes a self bin search) + if (k == 0 && flag_same_multi[icollection][jcollection]) { // if same collection, // if j is owned atom, store it, since j is beyond i in linked list // if j is ghost, only store if j coords are "above and to the right" of i @@ -221,7 +221,7 @@ void NPairMulti::build(NeighList *list) } else { if (rsq <= cutsq) { jh = j; - if (history && rsq < radsum * radsum) + if (history && rsq < (radsum * radsum)) jh = jh ^ mask_history; if (molecular != Atom::ATOMIC) { diff --git a/src/npair_skip.cpp b/src/npair_skip.cpp index fad06d699c..6afb43bc16 100644 --- a/src/npair_skip.cpp +++ b/src/npair_skip.cpp @@ -22,8 +22,8 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -template -NPairSkipTemp::NPairSkipTemp(LAMMPS *lmp) : NPair(lmp) {} +template +NPairSkipTemp::NPairSkipTemp(LAMMPS *lmp) : NPair(lmp) {} /* ---------------------------------------------------------------------- build skip list for subset of types from parent list @@ -33,8 +33,8 @@ NPairSkipTemp::NPairSkipTemp(LAMMPS *lmp) : NPair(lmp) {} if ghost, also store neighbors of ghost atoms & set inum,gnum correctly ------------------------------------------------------------------------- */ -template -void NPairSkipTemp::build(NeighList *list) +template +void NPairSkipTemp::build(NeighList *list) { int i, j, ii, jj, n, itype, jnum, joriginal; int *neighptr, *jlist; @@ -124,8 +124,6 @@ void NPairSkipTemp::build(NeighList *list) } namespace LAMMPS_NS { -template class NPairSkipTemp<0,0>; -template class NPairSkipTemp<1,0>; -template class NPairSkipTemp<0,1>; -template class NPairSkipTemp<1,1>; +template class NPairSkipTemp<0>; +template class NPairSkipTemp<1>; } diff --git a/src/npair_skip.h b/src/npair_skip.h index 06da1be85b..cb0d201555 100644 --- a/src/npair_skip.h +++ b/src/npair_skip.h @@ -13,41 +13,41 @@ #ifdef NPAIR_CLASS // clang-format off -typedef NPairSkipTemp<0, 0> NPairSkip; +typedef NPairSkipTemp<0> NPairSkip; NPairStyle(skip, NPairSkip, NP_SKIP | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairSkipTemp<0, 0> NPairSkip; +typedef NPairSkipTemp<0> NPairSkip; NPairStyle(skip/ghost, NPairSkip, NP_SKIP | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_GHOST); -typedef NPairSkipTemp<1, 0> NPairSkipSize; +typedef NPairSkipTemp<0> NPairSkipSize; NPairStyle(skip/half/size, NPairSkipSize, NP_SKIP | NP_SIZE | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI); -typedef NPairSkipTemp<0, 1> NPairSkipTrim; +typedef NPairSkipTemp<1> NPairSkipTrim; NPairStyle(skip/trim, NPairSkipTrim, NP_SKIP | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_TRIM); -typedef NPairSkipTemp<0, 1> NPairSkipTrim; +typedef NPairSkipTemp<1> NPairSkipTrim; NPairStyle(skip/ghost/trim, NPairSkipTrim, NP_SKIP | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | NP_NEWTON | NP_NEWTOFF | NP_ORTHO | NP_TRI | NP_GHOST | NP_TRIM); -typedef NPairSkipTemp<1, 1> NPairSkipTrimSize; +typedef NPairSkipTemp<1> NPairSkipTrimSize; NPairStyle(skip/trim/half/size, NPairSkipTrimSize, NP_SKIP | NP_SIZE | NP_HALF | NP_FULL | NP_NSQ | NP_BIN | NP_MULTI | NP_MULTI_OLD | @@ -63,7 +63,7 @@ NPairStyle(skip/trim/half/size, namespace LAMMPS_NS { -template +template class NPairSkipTemp : public NPair { public: NPairSkipTemp(class LAMMPS *); From 0cf4c9e7a32585ca7c78d4fd4df4f50c89d83d9b Mon Sep 17 00:00:00 2001 From: Stan Gerald Moore Date: Wed, 13 Dec 2023 11:07:42 -0700 Subject: [PATCH 086/189] Whitespace --- src/KOKKOS/compute_reaxff_atom_kokkos.cpp | 13 ++++++------- src/KOKKOS/pair_reaxff_kokkos.cpp | 6 +++--- src/REAXFF/compute_reaxff_atom.cpp | 17 +++++++---------- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/KOKKOS/compute_reaxff_atom_kokkos.cpp b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp index c3e9367ff4..8dbcb9441e 100644 --- a/src/KOKKOS/compute_reaxff_atom_kokkos.cpp +++ b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp @@ -42,7 +42,6 @@ ComputeReaxFFAtomKokkos::ComputeReaxFFAtomKokkos(LAMMPS *lmp, int na kokkosable = 1; } - /* ---------------------------------------------------------------------- */ template @@ -58,7 +57,7 @@ void ComputeReaxFFAtomKokkos::init() { ComputeReaxFFAtom::init(); - if(!reaxff || !reaxff->kokkosable) { + if (!reaxff || !reaxff->kokkosable) { error->all(FLERR,"Cannot use compute reaxff/atom/kk without " "pair_style reaxff/kk"); } @@ -88,7 +87,7 @@ void ComputeReaxFFAtomKokkos::compute_bonds() nbuf = ((store_bonds ? maxnumbonds*2 : 0) + 3)*nlocal; - if(!buf || k_buf.extent(0) < nbuf) { + if (!buf || k_buf.extent(0) < nbuf) { memoryKK->destroy_kokkos(k_buf, buf); memoryKK->create_kokkos(k_buf, buf, nbuf, "reaxff/atom:buf"); } @@ -119,10 +118,10 @@ void ComputeReaxFFAtomKokkos::compute_local() { invoked_local = update->ntimestep; - if(invoked_bonds < update->ntimestep) + if (invoked_bonds < update->ntimestep) compute_bonds(); - if(nbonds > prev_nbonds) { + if (nbonds > prev_nbonds) { // grow array_local memory->destroy(array_local); memory->create(array_local, nbonds, 3, "reaxff/atom:array_local"); @@ -158,7 +157,7 @@ void ComputeReaxFFAtomKokkos::compute_peratom() { invoked_peratom = update->ntimestep; - if(invoked_bonds < update->ntimestep) + if (invoked_bonds < update->ntimestep) compute_bonds(); // extract peratom bond information from buffer @@ -182,7 +181,7 @@ template double ComputeReaxFFAtomKokkos::memory_usage() { double bytes = (double)(nlocal*3) * sizeof(double); - if(store_bonds) + if (store_bonds) bytes += (double)(nbonds*3) * sizeof(double); bytes += (double)(nbuf > 0 ? nbuf * sizeof(double) : 0); return bytes; diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index 29ac398128..11a40970c2 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -4200,13 +4200,13 @@ void PairReaxFFKokkos::calculate_find_bond_item(int ii, int &numbond const int i = d_ilist[ii]; int nj = 0; - if(mask[i] & groupbit) { + if (mask[i] & groupbit) { const int j_start = d_bo_first[i]; const int j_end = j_start + d_bo_num[i]; for (int jj = j_start; jj < j_end; jj++) { int j = d_bo_list[jj]; j &= NEIGHMASK; - if(mask[j] & groupbit) { + if (mask[j] & groupbit) { const tagint jtag = tag[j]; const int j_index = jj - j_start; double bo_tmp = d_BO(i,j_index); @@ -4262,7 +4262,7 @@ void PairReaxFFKokkos::PackReducedBondBuffer(DAT::tdual_ffloat_1d k_ copymode = 1; nlocal = atomKK->nlocal; - if(store_bonds) { + if (store_bonds) { PairReaxKokkosPackReducedBondBufferFunctor pack_bond_buffer_functor(this); Kokkos::parallel_scan(nlocal,pack_bond_buffer_functor); } else { diff --git a/src/REAXFF/compute_reaxff_atom.cpp b/src/REAXFF/compute_reaxff_atom.cpp index fba67ecacf..1b54f3b82a 100644 --- a/src/REAXFF/compute_reaxff_atom.cpp +++ b/src/REAXFF/compute_reaxff_atom.cpp @@ -79,7 +79,6 @@ ComputeReaxFFAtom::ComputeReaxFFAtom(LAMMPS *lmp, int narg, char **arg) : local_flag = store_bonds; } - /* ---------------------------------------------------------------------- */ ComputeReaxFFAtom::~ComputeReaxFFAtom() @@ -107,7 +106,7 @@ void ComputeReaxFFAtom::init() if (!reaxff) error->all(FLERR,"Cannot use compute reaxff/atom without " "pair_style reaxff or reaxff/omp"); - if(reaxff->kokkosable && !kokkosable) + if (reaxff->kokkosable && !kokkosable) error->all(FLERR,"Cannot use compute reaxff/atom with pair_style reaxff/kk. Use reaxff/atom/kk."); } @@ -142,7 +141,7 @@ int ComputeReaxFFAtom::FindBond() bo_tmp = bo_ij->bo_data.BO; if (bo_tmp > bo_cut) { - if(store_bonds) { + if (store_bonds) { neighid[i][nj] = jtag; abo[i][nj] = bo_tmp; } @@ -157,7 +156,6 @@ int ComputeReaxFFAtom::FindBond() return numbonds; } - /* ---------------------------------------------------------------------- */ void ComputeReaxFFAtom::compute_bonds() @@ -170,7 +168,7 @@ void ComputeReaxFFAtom::compute_bonds() memory->destroy(bondcount); memory->destroy(array_atom); nlocal = atom->nlocal; - if(store_bonds) { + if (store_bonds) { memory->create(abo, nlocal, MAXREAXBOND, "reaxff/atom:abo"); memory->create(neighid, nlocal, MAXREAXBOND, "reaxff/atom:neighid"); } @@ -195,11 +193,10 @@ void ComputeReaxFFAtom::compute_local() { invoked_local = update->ntimestep; - if(invoked_bonds < update->ntimestep) { + if (invoked_bonds < update->ntimestep) compute_bonds(); - } - if(nbonds > prev_nbonds) { + if (nbonds > prev_nbonds) { // grow array_local memory->destroy(array_local); memory->create(array_local, nbonds, 3, "reaxff/atom:array_local"); @@ -229,7 +226,7 @@ void ComputeReaxFFAtom::compute_peratom() { invoked_peratom = update->ntimestep; - if(invoked_bonds < update->ntimestep) { + if (invoked_bonds < update->ntimestep) { compute_bonds(); } @@ -249,7 +246,7 @@ double ComputeReaxFFAtom::memory_usage() { double bytes = (double)(nlocal*3) * sizeof(double); bytes += (double)(nlocal) * sizeof(int); - if(store_bonds) { + if (store_bonds) { bytes += (double)(2*nlocal*MAXREAXBOND) * sizeof(double); bytes += (double)(nbonds*3) * sizeof(double); } From 61fd4d643b492cb55217a2a7d285b41d38b46f44 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 13 Dec 2023 19:20:08 -0500 Subject: [PATCH 087/189] correct logic for "cannot happen" case --- src/EXTRA-COMPUTE/compute_slcsa_atom.cpp | 126 +++++++++++------------ 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/src/EXTRA-COMPUTE/compute_slcsa_atom.cpp b/src/EXTRA-COMPUTE/compute_slcsa_atom.cpp index f12551085c..509362a73b 100644 --- a/src/EXTRA-COMPUTE/compute_slcsa_atom.cpp +++ b/src/EXTRA-COMPUTE/compute_slcsa_atom.cpp @@ -305,84 +305,84 @@ void ComputeSLCSAAtom::compute_peratom() int *mask = atom->mask; int nlocal = atom->nlocal; - double **compute_array; if (descriptorval.which == ArgInfo::COMPUTE) { if (!(descriptorval.val.c->invoked_flag & Compute::INVOKED_PERATOM)) { descriptorval.val.c->compute_peratom(); descriptorval.val.c->invoked_flag |= Compute::INVOKED_PERATOM; } - compute_array = descriptorval.val.c->array_atom; - } + double **compute_array = descriptorval.val.c->array_atom; - memory->create(full_descriptor, ncomps, "slcsa/atom:local descriptor"); - memory->create(projected_descriptor, nclasses - 1, "slcsa/atom:reduced descriptor"); - memory->create(scores, nclasses, "slcsa/atom:scores"); - memory->create(probas, nclasses, "slcsa/atom:probas"); - memory->create(prodright, nclasses - 1, "slcsa/atom:prodright"); - memory->create(dmaha, nclasses, "slcsa/atom:prodright"); + memory->create(full_descriptor, ncomps, "slcsa/atom:local descriptor"); + memory->create(projected_descriptor, nclasses - 1, "slcsa/atom:reduced descriptor"); + memory->create(scores, nclasses, "slcsa/atom:scores"); + memory->create(probas, nclasses, "slcsa/atom:probas"); + memory->create(prodright, nclasses - 1, "slcsa/atom:prodright"); + memory->create(dmaha, nclasses, "slcsa/atom:prodright"); - for (int i = 0; i < nlocal; i++) { - if (mask[i] & groupbit) { - for (int j = 0; j < ncomps; j++) { full_descriptor[j] = compute_array[i][j]; } - // Here comes the LDA + LR process - // 1st step : Retrieve mean database descriptor - for (int j = 0; j < ncomps; j++) { full_descriptor[j] -= database_mean_descriptor[j]; } - // 2nd step : Matrix multiplication to go from ncompsx1 -> (nclasses-1)*1 - for (int j = 0; j < nclasses - 1; j++) { - projected_descriptor[j] = 0.; - for (int k = 0; k < ncomps; k++) { - projected_descriptor[j] += full_descriptor[k] * lda_scalings[k][j]; - } - } - // 3rd step : Matrix multiplication - for (int j = 0; j < nclasses; j++) { - scores[j] = lr_bias[j]; - for (int k = 0; k < nclasses - 1; k++) { - scores[j] += lr_decision[j][k] * projected_descriptor[k]; - } - } - // 4th step : Matrix multiplication - double sumexpscores = 0.; - for (int j = 0; j < nclasses; j++) sumexpscores += exp(scores[j]); - for (int j = 0; j < nclasses; j++) { probas[j] = exp(scores[j]) / sumexpscores; } - - classification[i][nclasses] = argmax(probas, nclasses); - - // 5th step : Mahalanobis distance - for (int j = 0; j < nclasses; j++) { - prodright[0] = 0.; - prodright[1] = 0.; - prodright[2] = 0.; - for (int k = 0; k < nclasses - 1; k++) { - for (int l = 0; l < nclasses - 1; l++) { - prodright[k] += - (icov_list[j][k][l] * (projected_descriptor[k] - mean_projected_descriptors[j][k])); + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + for (int j = 0; j < ncomps; j++) full_descriptor[j] = compute_array[i][j]; + // Here comes the LDA + LR process + // 1st step : Retrieve mean database descriptor + for (int j = 0; j < ncomps; j++) full_descriptor[j] -= database_mean_descriptor[j]; + // 2nd step : Matrix multiplication to go from ncompsx1 -> (nclasses-1)*1 + for (int j = 0; j < nclasses - 1; j++) { + projected_descriptor[j] = 0.0; + for (int k = 0; k < ncomps; k++) { + projected_descriptor[j] += full_descriptor[k] * lda_scalings[k][j]; } } - double prodleft = 0.; - for (int k = 0; k < nclasses - 1; k++) { - prodleft += (prodright[k] * (projected_descriptor[k] - mean_projected_descriptors[j][k])); + // 3rd step : Matrix multiplication + for (int j = 0; j < nclasses; j++) { + scores[j] = lr_bias[j]; + for (int k = 0; k < nclasses - 1; k++) { + scores[j] += lr_decision[j][k] * projected_descriptor[k]; + } } - classification[i][j] = sqrt(prodleft); - } - // 6th step : Sanity check - int locclass = classification[i][nclasses]; + // 4th step : Matrix multiplication + double sumexpscores = 0.0; + for (int j = 0; j < nclasses; j++) sumexpscores += exp(scores[j]); + for (int j = 0; j < nclasses; j++) probas[j] = exp(scores[j]) / sumexpscores; - if (classification[i][locclass] > maha_thresholds[locclass]) { - classification[i][nclasses] = -1.; - } + classification[i][nclasses] = argmax(probas, nclasses); - } else { - for (int j = 0; j < ncols; j++) { classification[i][j] = -1.; } + // 5th step : Mahalanobis distance + for (int j = 0; j < nclasses; j++) { + prodright[0] = 0.0; + prodright[1] = 0.0; + prodright[2] = 0.0; + for (int k = 0; k < nclasses - 1; k++) { + for (int l = 0; l < nclasses - 1; l++) { + prodright[k] += (icov_list[j][k][l] * + (projected_descriptor[k] - mean_projected_descriptors[j][k])); + } + } + double prodleft = 0.0; + for (int k = 0; k < nclasses - 1; k++) { + prodleft += + (prodright[k] * (projected_descriptor[k] - mean_projected_descriptors[j][k])); + } + classification[i][j] = sqrt(prodleft); + } + // 6th step : Sanity check + int locclass = classification[i][nclasses]; + + if (classification[i][locclass] > maha_thresholds[locclass]) { + classification[i][nclasses] = -1.0; + } + + } else { + for (int j = 0; j < ncols; j++) classification[i][j] = -1.0; + } } + memory->destroy(full_descriptor); + memory->destroy(projected_descriptor); + memory->destroy(scores); + memory->destroy(probas); + memory->destroy(prodright); + memory->destroy(dmaha); } - memory->destroy(full_descriptor); - memory->destroy(projected_descriptor); - memory->destroy(scores); - memory->destroy(probas); - memory->destroy(prodright); - memory->destroy(dmaha); } int ComputeSLCSAAtom::compute_ncomps(int twojmax) From 48b5e16f5dfbe46bbed13c01ab9aba705975a7ee Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 13 Dec 2023 19:20:47 -0500 Subject: [PATCH 088/189] remove unused class members, initialize pointers, adjust programming style --- src/ML-PACE/compute_pace.cpp | 67 ++++++++++++++++-------------------- src/ML-PACE/compute_pace.h | 4 +-- 2 files changed, 30 insertions(+), 41 deletions(-) diff --git a/src/ML-PACE/compute_pace.cpp b/src/ML-PACE/compute_pace.cpp index cf3be8f47f..12693a58d9 100644 --- a/src/ML-PACE/compute_pace.cpp +++ b/src/ML-PACE/compute_pace.cpp @@ -45,10 +45,10 @@ struct ACECimpl { using namespace LAMMPS_NS; -enum{SCALAR,VECTOR,ARRAY}; +enum { SCALAR, VECTOR, ARRAY }; ComputePACE::ComputePACE(LAMMPS *lmp, int narg, char **arg) : - Compute(lmp, narg, arg), cutsq(nullptr), list(nullptr), pace(nullptr), - paceall(nullptr), pace_peratom(nullptr), map(nullptr), cg(nullptr) + Compute(lmp, narg, arg), cutsq(nullptr), list(nullptr), pace(nullptr), paceall(nullptr), + pace_peratom(nullptr), map(nullptr), cg(nullptr), c_pe(nullptr), c_virial(nullptr) { array_flag = 1; extarray = 0; @@ -71,33 +71,27 @@ ComputePACE::ComputePACE(LAMMPS *lmp, int narg, char **arg) : //read in file with CG coefficients or c_tilde coefficients auto potential_file_name = utils::get_potential_file_path(arg[3]); - delete acecimpl -> basis_set; - acecimpl -> basis_set = new ACECTildeBasisSet(potential_file_name); - double cut = acecimpl -> basis_set->cutoffmax; - cutmax = acecimpl -> basis_set->cutoffmax; + delete acecimpl->basis_set; + acecimpl->basis_set = new ACECTildeBasisSet(potential_file_name); + double cut = acecimpl->basis_set->cutoffmax; + cutmax = acecimpl->basis_set->cutoffmax; double cuti; double radelemall = 0.5; //# of rank 1, rank > 1 functions int n_r1, n_rp = 0; - n_r1 = acecimpl -> basis_set->total_basis_size_rank1[0]; - n_rp = acecimpl -> basis_set->total_basis_size[0]; + n_r1 = acecimpl->basis_set->total_basis_size_rank1[0]; + n_rp = acecimpl->basis_set->total_basis_size[0]; int ncoeff = n_r1 + n_rp; - - //int nvalues = ncoeff; - nvalues = ncoeff; - //----------------------------------------------------------- - //nperdim = ncoeff; - ndims_force = 3; ndims_virial = 6; bik_rows = 1; - yoffset = nvalues; //nperdim; - zoffset = 2*nvalues; //nperdim; + yoffset = nvalues; + zoffset = 2*nvalues; natoms = atom->natoms; if (bikflag) bik_rows = natoms; dgrad_rows = ndims_force*natoms; @@ -255,28 +249,27 @@ void ComputePACE::compute_array() const int typeoffset_local = ndims_peratom*nvalues*(itype-1); const int typeoffset_global = nvalues*(itype-1); - delete acecimpl -> ace; - acecimpl -> ace = new ACECTildeEvaluator(*acecimpl -> basis_set); - acecimpl -> ace->compute_projections = 1; - acecimpl -> ace->compute_b_grad = 1; + delete acecimpl->ace; + acecimpl->ace = new ACECTildeEvaluator(*acecimpl->basis_set); + acecimpl->ace->compute_projections = 1; + acecimpl->ace->compute_b_grad = 1; int n_r1, n_rp = 0; - n_r1 = acecimpl -> basis_set->total_basis_size_rank1[0]; - n_rp = acecimpl -> basis_set->total_basis_size[0]; + n_r1 = acecimpl->basis_set->total_basis_size_rank1[0]; + n_rp = acecimpl->basis_set->total_basis_size[0]; int ncoeff = n_r1 + n_rp; - acecimpl -> ace->element_type_mapping.init(ntypes+1); + acecimpl->ace->element_type_mapping.init(ntypes+1); for (int ik = 1; ik <= ntypes; ik++) { - for(int mu = 0; mu < acecimpl -> basis_set ->nelements; mu++){ + for(int mu = 0; mu < acecimpl->basis_set->nelements; mu++){ if (mu != -1) { if (mu == ik - 1) { map[ik] = mu; - acecimpl -> ace->element_type_mapping(ik) = mu; + acecimpl->ace->element_type_mapping(ik) = mu; } } } } - if (dgradflag) { // dBi/dRi tags @@ -307,9 +300,9 @@ void ComputePACE::compute_array() } // resize the neighbor cache after setting the basis - acecimpl -> ace->resize_neighbours_cache(max_jnum); - acecimpl -> ace->compute_atom(i, atom->x, atom->type, list->numneigh[i], list->firstneigh[i]); - Array1D Bs = acecimpl -> ace -> projections; + acecimpl->ace->resize_neighbours_cache(max_jnum); + acecimpl->ace->compute_atom(i, atom->x, atom->type, list->numneigh[i], list->firstneigh[i]); + Array1D Bs = acecimpl->ace->projections; for (int jj = 0; jj < jnum; jj++) { const int j = jlist[jj]; @@ -322,9 +315,9 @@ void ComputePACE::compute_array() // dimension: (n_descriptors,max_jnum,3) //example to access entries for neighbour jj after running compute_atom for atom i: for (int func_ind =0; func_ind < n_r1 + n_rp; func_ind++){ - DOUBLE_TYPE fx_dB = acecimpl -> ace -> neighbours_dB(func_ind,jj,0); - DOUBLE_TYPE fy_dB = acecimpl -> ace -> neighbours_dB(func_ind,jj,1); - DOUBLE_TYPE fz_dB = acecimpl -> ace -> neighbours_dB(func_ind,jj,2); + DOUBLE_TYPE fx_dB = acecimpl->ace->neighbours_dB(func_ind,jj,0); + DOUBLE_TYPE fy_dB = acecimpl->ace->neighbours_dB(func_ind,jj,1); + DOUBLE_TYPE fz_dB = acecimpl->ace->neighbours_dB(func_ind,jj,2); pacedi[func_ind] += fx_dB; pacedi[func_ind+yoffset] += fy_dB; pacedi[func_ind+zoffset] += fz_dB; @@ -333,15 +326,13 @@ void ComputePACE::compute_array() pacedj[func_ind+zoffset] -= fz_dB; } } else { - //printf("inside dBi/dRj logical : ncoeff = %d \n", ncoeff); for (int iicoeff = 0; iicoeff < ncoeff; iicoeff++) { // add to pace array for this proc - //printf("inside dBi/dRj loop\n"); // dBi/dRj - DOUBLE_TYPE fx_dB = acecimpl -> ace -> neighbours_dB(iicoeff,jj,0); - DOUBLE_TYPE fy_dB = acecimpl -> ace -> neighbours_dB(iicoeff,jj,1); - DOUBLE_TYPE fz_dB = acecimpl -> ace -> neighbours_dB(iicoeff,jj,2); + DOUBLE_TYPE fx_dB = acecimpl->ace->neighbours_dB(iicoeff,jj,0); + DOUBLE_TYPE fy_dB = acecimpl->ace->neighbours_dB(iicoeff,jj,1); + DOUBLE_TYPE fz_dB = acecimpl->ace->neighbours_dB(iicoeff,jj,2); pace[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][iicoeff+3] -= fx_dB; pace[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][iicoeff+3] -= fy_dB; pace[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][iicoeff+3] -= fz_dB; diff --git a/src/ML-PACE/compute_pace.h b/src/ML-PACE/compute_pace.h index 2f2f8d7f21..496c8a16d3 100644 --- a/src/ML-PACE/compute_pace.h +++ b/src/ML-PACE/compute_pace.h @@ -35,15 +35,13 @@ class ComputePACE : public Compute { private: int natoms, nmax, size_peratom, lastcol; - int ncoeff, nvalues, nperdim, yoffset, zoffset; + int nvalues, yoffset, zoffset; int ndims_peratom, ndims_force, ndims_virial; double **cutsq; class NeighList *list; double **pace, **paceall; double **pace_peratom; - double rcutfac; int *map; // map types to [0,nelements) - int nelements, chemflag; int bikflag, bik_rows, dgradflag, dgrad_rows; double *cg; double cutmax; From 9c01d64804cfd5265924ac62b4e4ea3dca54049f Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 13 Dec 2023 19:21:06 -0500 Subject: [PATCH 089/189] avoid accessing uninitialized data --- src/ML-SNAP/compute_sna_atom.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ML-SNAP/compute_sna_atom.cpp b/src/ML-SNAP/compute_sna_atom.cpp index 753751690d..3fe3cca92d 100644 --- a/src/ML-SNAP/compute_sna_atom.cpp +++ b/src/ML-SNAP/compute_sna_atom.cpp @@ -627,7 +627,7 @@ double ComputeSNAAtom::sum_weights(double * rsq, double * w, int ncounts) double ComputeSNAAtom::get_target_rcut(double S_target, double * rsq, double rcut, int ncounts, int weightmode, double delta) { - double S_sol; + double S_sol = 0.0; if (weightmode == 0) { double * www = weights(rsq, rcut, ncounts); S_sol = sum_weights(rsq, www, ncounts); From 757f28f8bf649729d4563e66b6a55c17470ff0cc Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 10:59:30 -0500 Subject: [PATCH 090/189] correct info about fix halt default setting --- doc/src/fix_halt.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/fix_halt.rst b/doc/src/fix_halt.rst index 4231c77cc5..25331804aa 100644 --- a/doc/src/fix_halt.rst +++ b/doc/src/fix_halt.rst @@ -183,4 +183,4 @@ Related commands Default """"""" -The option defaults are error = hard, message = yes, and path = ".". +The option defaults are error = soft, message = yes, and path = ".". From 727c3c95723391bd1bdc2dce1dee7332ac7748f2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 11:15:11 -0500 Subject: [PATCH 091/189] move call to determine free disk space to platform namespace --- src/fix_halt.cpp | 6 +++--- src/platform.cpp | 37 +++++++++++++++++++++++++++++++++++++ src/platform.h | 9 +++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/fix_halt.cpp b/src/fix_halt.cpp index fcfefe102d..36d660c90d 100644 --- a/src/fix_halt.cpp +++ b/src/fix_halt.cpp @@ -170,8 +170,8 @@ void FixHalt::init() // check if disk limit is supported if (attribute == DISKFREE) { - if (diskfree() < 0.0) - error->all(FLERR,"Disk limit not supported by OS or illegal path"); + if (!dlimit_path || platform::disk_free(dlimit_path) < 0.0) + error->all(FLERR, "Disk limit not supported by OS or illegal path"); } } @@ -196,7 +196,7 @@ void FixHalt::end_of_step() if (update->ntimestep != nextstep) return; attvalue = tlimit(); } else if (attribute == DISKFREE) { - attvalue = diskfree(); + attvalue = platform::disk_free(dlimit_path) / 1048576.0; // MBytes } else if (attribute == BONDMAX) { attvalue = bondmax(); } else { diff --git a/src/platform.cpp b/src/platform.cpp index 064f142425..c8a964076f 100644 --- a/src/platform.cpp +++ b/src/platform.cpp @@ -42,6 +42,7 @@ #define PSAPI_VERSION 2 #include +#include #include // for _get_osfhandle() #include #include @@ -61,6 +62,13 @@ #include #include #endif + +// for disk_free() +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || \ + defined(__OpenBSD__) || defined(__NetBSD__) +#include +#endif + //////////////////////////////////////////////////////////////////////// #include @@ -1047,6 +1055,35 @@ bool platform::file_is_readable(const std::string &path) } return false; } +/* ---------------------------------------------------------------------- + determine available disk space, if supported. Return -1 if not. +------------------------------------------------------------------------- */ + +double platform::disk_free(const std::string &path) +{ + double disk_free = -1.0; + +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || \ + defined(__OpenBSD__) || defined(__NetBSD__) + struct statvfs fs; + + if (path.size()) { + int rv = statvfs(path.c_str(), &fs); + if (rv == 0) { +#if defined(__linux__) + disk_free = fs.f_bavail * fs.f_bsize; +#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || \ + defined(__OpenBSD__) || defined(__NetBSD__) + disk_free = fs.f_bavail * fs.f_frsize; +#endif + } + } +#else define(_WIN32) + bigint free_bytes; + if (GetDiskFreeSpaceEx(path.c_str(), &free_bytes, nullptr, nullptr)) disk_free = free_bytes; +#endif + return disk_free; +} /* ---------------------------------------------------------------------- check if filename has a known compression extension diff --git a/src/platform.h b/src/platform.h index 036074c900..4328f873dd 100644 --- a/src/platform.h +++ b/src/platform.h @@ -377,6 +377,15 @@ namespace platform { bool file_is_readable(const std::string &path); + /*! Return free disk space in bytes of file system pointed to by path + * + * Returns -1.0 if the path is invalid or free space reporting not supported. + * + * \param path file or folder path in file system + * \return */ + + double disk_free(const std::string &path); + /*! Check if a file name ends in a known extension for a compressed file format * * Currently supported file extensions are: .gz, .bz2, .zst, .xz, .lzma, lz4 From 298ce1863c493023468c11caa443761c75ee3ded Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 11:15:28 -0500 Subject: [PATCH 092/189] coding style updates --- src/fix_halt.cpp | 154 +++++++++++++++++++---------------------------- 1 file changed, 62 insertions(+), 92 deletions(-) diff --git a/src/fix_halt.cpp b/src/fix_halt.cpp index 36d660c90d..b34c79867f 100644 --- a/src/fix_halt.cpp +++ b/src/fix_halt.cpp @@ -1,4 +1,3 @@ -// clang-format off /* ---------------------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://www.lammps.org/, Sandia National Laboratories @@ -31,49 +30,49 @@ using namespace LAMMPS_NS; using namespace FixConst; -enum{BONDMAX,TLIMIT,DISKFREE,VARIABLE}; -enum{LT,LE,GT,GE,EQ,NEQ,XOR}; -enum{HARD,SOFT,CONTINUE}; -enum{NOMSG=0,YESMSG=1}; +enum { BONDMAX, TLIMIT, DISKFREE, VARIABLE }; +enum { LT, LE, GT, GE, EQ, NEQ, XOR }; +enum { HARD, SOFT, CONTINUE }; +enum { NOMSG = 0, YESMSG = 1 }; /* ---------------------------------------------------------------------- */ FixHalt::FixHalt(LAMMPS *lmp, int narg, char **arg) : - Fix(lmp, narg, arg), idvar(nullptr), dlimit_path(nullptr) + Fix(lmp, narg, arg), idvar(nullptr), dlimit_path(nullptr) { - if (narg < 7) error->all(FLERR,"Illegal fix halt command"); - nevery = utils::inumeric(FLERR,arg[3],false,lmp); - if (nevery <= 0) error->all(FLERR,"Illegal fix halt command"); + if (narg < 7) utils::missing_cmd_args(FLERR, "fix halt", error); + nevery = utils::inumeric(FLERR, arg[3], false, lmp); + if (nevery <= 0) error->all(FLERR, "Illegal fix halt command: nevery must be > 0"); // comparison args idvar = nullptr; int iarg = 4; - if (strcmp(arg[iarg],"tlimit") == 0) { + if (strcmp(arg[iarg], "tlimit") == 0) { attribute = TLIMIT; - } else if (strcmp(arg[iarg],"diskfree") == 0) { + } else if (strcmp(arg[iarg], "diskfree") == 0) { attribute = DISKFREE; dlimit_path = utils::strdup("."); - } else if (strcmp(arg[iarg],"bondmax") == 0) { + } else if (strcmp(arg[iarg], "bondmax") == 0) { attribute = BONDMAX; } else { - ArgInfo argi(arg[iarg],ArgInfo::VARIABLE); + ArgInfo argi(arg[iarg], ArgInfo::VARIABLE); - if ((argi.get_type() == ArgInfo::UNKNOWN) - || (argi.get_type() == ArgInfo::NONE) - || (argi.get_dim() != 0)) - error->all(FLERR,"Invalid fix halt attribute"); + if ((argi.get_type() == ArgInfo::UNKNOWN) || (argi.get_type() == ArgInfo::NONE) || + (argi.get_dim() != 0)) + error->all(FLERR, "Invalid fix halt attribute {}", arg[iarg]); attribute = VARIABLE; idvar = argi.copy_name(); ivar = input->variable->find(idvar); - if (ivar < 0) error->all(FLERR,"Could not find fix halt variable name"); + if (ivar < 0) error->all(FLERR, "Could not find fix halt variable name"); if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR,"Fix halt variable is not equal-style variable"); + error->all(FLERR, "Fix halt variable is not equal-style variable"); } + // clang-format off ++iarg; if (strcmp(arg[iarg],"<") == 0) operation = LT; else if (strcmp(arg[iarg],"<=") == 0) operation = LE; @@ -85,7 +84,7 @@ FixHalt::FixHalt(LAMMPS *lmp, int narg, char **arg) : else error->all(FLERR,"Invalid fix halt operator"); ++iarg; - value = utils::numeric(FLERR,arg[iarg],false,lmp); + value = utils::numeric(FLERR, arg[iarg], false, lmp); // parse optional args @@ -93,38 +92,40 @@ FixHalt::FixHalt(LAMMPS *lmp, int narg, char **arg) : msgflag = YESMSG; ++iarg; while (iarg < narg) { - if (strcmp(arg[iarg],"error") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal fix halt command"); - if (strcmp(arg[iarg+1],"hard") == 0) eflag = HARD; - else if (strcmp(arg[iarg+1],"soft") == 0) eflag = SOFT; - else if (strcmp(arg[iarg+1],"continue") == 0) eflag = CONTINUE; - else error->all(FLERR,"Illegal fix halt command"); + if (strcmp(arg[iarg], "error") == 0) { + if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "fix halt error", error); + if (strcmp(arg[iarg + 1], "hard") == 0) eflag = HARD; + else if (strcmp(arg[iarg + 1], "soft") == 0) eflag = SOFT; + else if (strcmp(arg[iarg + 1], "continue") == 0) eflag = CONTINUE; + else error->all(FLERR, "Unknown fix halt error condition {}", arg[iarg]); iarg += 2; - } else if (strcmp(arg[iarg],"message") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal fix halt command"); - msgflag = utils::logical(FLERR,arg[iarg+1],false,lmp); + } else if (strcmp(arg[iarg], "message") == 0) { + if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "fix halt message", error); + msgflag = utils::logical(FLERR, arg[iarg + 1], false, lmp); iarg += 2; - } else if (strcmp(arg[iarg],"path") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal fix halt command"); + } else if (strcmp(arg[iarg], "path") == 0) { + if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "fix halt error", error); ++iarg; delete[] dlimit_path; // strip off outer quotes, if present - int len = strlen(arg[iarg])+1; - if ( ((arg[iarg][0] == '"') || (arg[iarg][0] == '\'')) - && (arg[iarg][0] == arg[iarg][len-2])) { - arg[iarg][len-2] = '\0'; - dlimit_path = utils::strdup(arg[iarg]+1); - } else dlimit_path = utils::strdup(arg[iarg]); + int len = strlen(arg[iarg]) + 1; + if (((arg[iarg][0] == '"') || (arg[iarg][0] == '\'')) && + (arg[iarg][0] == arg[iarg][len - 2])) { + arg[iarg][len - 2] = '\0'; + dlimit_path = utils::strdup(arg[iarg] + 1); + } else + dlimit_path = utils::strdup(arg[iarg]); ++iarg; - } else error->all(FLERR,"Illegal fix halt command"); + } else error->all(FLERR, "Unknown fix halt keyword {}", arg[iarg]); } + // clang-format on // add nfirst to all computes that store invocation times // since don't know a priori which are invoked via variables by this fix // once in end_of_step() can set timestep for ones actually invoked if (attribute == VARIABLE) { - const bigint nfirst = (update->ntimestep/nevery)*nevery + nevery; + const bigint nfirst = (update->ntimestep / nevery) * nevery + nevery; modify->addstep_compute_all(nfirst); } } @@ -133,8 +134,8 @@ FixHalt::FixHalt(LAMMPS *lmp, int narg, char **arg) : FixHalt::~FixHalt() { - delete [] idvar; - delete [] dlimit_path; + delete[] idvar; + delete[] dlimit_path; } /* ---------------------------------------------------------------------- */ @@ -156,14 +157,14 @@ void FixHalt::init() if (attribute == VARIABLE) { ivar = input->variable->find(idvar); - if (ivar < 0) error->all(FLERR,"Could not find fix halt variable name"); + if (ivar < 0) error->all(FLERR, "Could not find fix halt variable {}", idvar); if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR,"Fix halt variable is not equal-style variable"); + error->all(FLERR, "Fix halt variable {} is not equal-style variable", idvar); } // settings used by TLIMIT - nextstep = (update->ntimestep/nevery)*nevery + nevery; + nextstep = (update->ntimestep / nevery) * nevery + nevery; thisstep = -1; tratio = 0.5; @@ -205,6 +206,10 @@ void FixHalt::end_of_step() modify->addstep_compute(update->ntimestep + nevery); } + // ensure that the attribute is *exactly* the same on all ranks + + MPI_Bcast(&attvalue, 1, MPI_DOUBLE, 0, world); + // check if halt is triggered, else just return if (operation == LT) { @@ -220,21 +225,19 @@ void FixHalt::end_of_step() } else if (operation == NEQ) { if (attvalue == value) return; } else if (operation == XOR) { - if ((attvalue == 0.0 && value == 0.0) || - (attvalue != 0.0 && value != 0.0)) return; + if ((attvalue == 0.0 && value == 0.0) || (attvalue != 0.0 && value != 0.0)) return; } // hard halt -> exit LAMMPS // soft/continue halt -> trigger timer to break from run loop // print message with ID of fix halt in case multiple instances - std::string message = fmt::format("Fix halt condition for fix-id {} met on " - "step {} with value {}", + std::string message = fmt::format("Fix halt condition for fix-id {} met on step {} with value {}", id, update->ntimestep, attvalue); if (eflag == HARD) { - error->all(FLERR,message); - } else if (eflag == SOFT || eflag == CONTINUE) { - if (comm->me == 0 && msgflag == YESMSG) error->message(FLERR,message); + error->all(FLERR, message); + } else if ((eflag == SOFT) || (eflag == CONTINUE)) { + if ((comm->me == 0) && (msgflag == YESMSG)) error->message(FLERR, message); timer->force_timeout(); } } @@ -260,8 +263,8 @@ double FixHalt::bondmax() int **bondlist = neighbor->bondlist; int nbondlist = neighbor->nbondlist; - int i1,i2; - double delx,dely,delz,rsq; + int i1, i2; + double delx, dely, delz, rsq; double maxone = 0.0; for (int n = 0; n < nbondlist; n++) { @@ -272,12 +275,12 @@ double FixHalt::bondmax() dely = x[i1][1] - x[i2][1]; delz = x[i1][2] - x[i2][2]; - rsq = delx*delx + dely*dely + delz*delz; - maxone = MAX(rsq,maxone); + rsq = delx * delx + dely * dely + delz * delz; + maxone = MAX(rsq, maxone); } double maxall; - MPI_Allreduce(&maxone,&maxall,1,MPI_DOUBLE,MPI_MAX,world); + MPI_Allreduce(&maxone, &maxall, 1, MPI_DOUBLE, MPI_MAX, world); return sqrt(maxall); } @@ -291,48 +294,15 @@ double FixHalt::bondmax() double FixHalt::tlimit() { double cpu = timer->elapsed(Timer::TOTAL); - MPI_Bcast(&cpu,1,MPI_DOUBLE,0,world); + MPI_Bcast(&cpu, 1, MPI_DOUBLE, 0, world); if (cpu < value) { bigint elapsed = update->ntimestep - update->firststep; - bigint final = update->firststep + - static_cast (tratio*value/cpu * elapsed); - nextstep = (final/nevery)*nevery + nevery; + bigint final = update->firststep + static_cast(tratio * value / cpu * elapsed); + nextstep = (final / nevery) * nevery + nevery; if (nextstep == update->ntimestep) nextstep += nevery; tratio = 1.0; } return cpu; } - -/* ---------------------------------------------------------------------- - determine available disk space, if supported. Return -1 if not. -------------------------------------------------------------------------- */ -#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__NetBSD__) -#include -#endif -double FixHalt::diskfree() -{ -#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__NetBSD__) - struct statvfs fs; - double disk_free = -1.0; - - if (dlimit_path) { - disk_free = 1.0e100; - int rv = statvfs(dlimit_path,&fs); - if (rv == 0) { -#if defined(__linux__) - disk_free = fs.f_bavail*fs.f_bsize/1048576.0; -#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__NetBSD__) - disk_free = fs.f_bavail*fs.f_frsize/1048576.0; -#endif - } else - disk_free = -1.0; - - MPI_Bcast(&disk_free,1,MPI_DOUBLE,0,world); - } - return disk_free; -#else - return -1.0; -#endif -} From 54ff3cf78f345d90dc31b9bc8eecf9087d2853f8 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 11:21:09 -0500 Subject: [PATCH 093/189] include new platform call in docs --- doc/src/Developer_platform.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/src/Developer_platform.rst b/doc/src/Developer_platform.rst index cdc4bb6770..9b05299146 100644 --- a/doc/src/Developer_platform.rst +++ b/doc/src/Developer_platform.rst @@ -70,6 +70,9 @@ File and path functions and global constants .. doxygenfunction:: is_console :project: progguide +.. doxygenfunction:: disk_free + :project: progguide + .. doxygenfunction:: path_is_directory :project: progguide From 45ca21da3b3a968e7a6d028f947702e38e8baafa Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 11:21:16 -0500 Subject: [PATCH 094/189] fix typo --- tools/msi2lmp/src/SearchAndFill.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/msi2lmp/src/SearchAndFill.c b/tools/msi2lmp/src/SearchAndFill.c index ce344c5ab6..35de0c81fe 100644 --- a/tools/msi2lmp/src/SearchAndFill.c +++ b/tools/msi2lmp/src/SearchAndFill.c @@ -116,7 +116,7 @@ void SearchAndFill(struct FrcFieldItem *item) /* Read lines until keyword is found */ if (fseek(FrcF,file_pos,SEEK_SET) < 0) { - fprintf(stderr, "Resetting file stream failed: ", strerror(errno)); + fprintf(stderr, "Resetting file stream failed: %s\n", strerror(errno)); exit(2); } strcpy(line,"empty"); From eb4c85566e5ca4bcf40a4a811677c06ceb2369ec Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 13:06:20 -0500 Subject: [PATCH 095/189] fix up port of platform::disk_free() to Windows --- src/platform.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platform.cpp b/src/platform.cpp index c8a964076f..b2061fe681 100644 --- a/src/platform.cpp +++ b/src/platform.cpp @@ -42,7 +42,6 @@ #define PSAPI_VERSION 2 #include -#include #include // for _get_osfhandle() #include #include @@ -1079,8 +1078,9 @@ double platform::disk_free(const std::string &path) } } #else define(_WIN32) - bigint free_bytes; - if (GetDiskFreeSpaceEx(path.c_str(), &free_bytes, nullptr, nullptr)) disk_free = free_bytes; + bigint free_bytes = 0; + if (GetDiskFreeSpaceEx(path.c_str(), (PULARGE_INTEGER) &free_bytes, nullptr, nullptr)) + disk_free = free_bytes; #endif return disk_free; } From 4d6e70600e755cf4a2164655a50f3ba3505e572a Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 15:03:13 -0500 Subject: [PATCH 096/189] some more small changes for portability and clarity --- src/platform.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/platform.cpp b/src/platform.cpp index b2061fe681..b324bd0b5c 100644 --- a/src/platform.cpp +++ b/src/platform.cpp @@ -1060,7 +1060,7 @@ bool platform::file_is_readable(const std::string &path) double platform::disk_free(const std::string &path) { - double disk_free = -1.0; + double bytes_free = -1.0; #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || \ defined(__OpenBSD__) || defined(__NetBSD__) @@ -1070,19 +1070,19 @@ double platform::disk_free(const std::string &path) int rv = statvfs(path.c_str(), &fs); if (rv == 0) { #if defined(__linux__) - disk_free = fs.f_bavail * fs.f_bsize; + bytes_free = fs.f_bavail * fs.f_bsize; #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || \ defined(__OpenBSD__) || defined(__NetBSD__) - disk_free = fs.f_bavail * fs.f_frsize; + bytes_free = fs.f_bavail * fs.f_frsize; #endif } } -#else define(_WIN32) - bigint free_bytes = 0; - if (GetDiskFreeSpaceEx(path.c_str(), (PULARGE_INTEGER) &free_bytes, nullptr, nullptr)) - disk_free = free_bytes; +#elif defined(_WIN32) + uint64_t is_free = 0; + if (GetDiskFreeSpaceEx(path.c_str(), (PULARGE_INTEGER) &is_free, nullptr, nullptr)) + bytes_free = is_free; #endif - return disk_free; + return bytes_free; } /* ---------------------------------------------------------------------- From ae58fe273257fdc12ba57b010e4dd3ee65e2d4c5 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 15:30:13 -0500 Subject: [PATCH 097/189] silence compiler warning --- src/neighbor.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/neighbor.cpp b/src/neighbor.cpp index ea208f61ce..c6c959a894 100644 --- a/src/neighbor.cpp +++ b/src/neighbor.cpp @@ -1791,16 +1791,17 @@ void Neighbor::print_pairwise_info() out += fmt::format(", trim from ({})",rq->copylist+1); else out += fmt::format(", copy from ({})",rq->copylist+1); - } else if (rq->halffull) + } else if (rq->halffull) { if (rq->trim) out += fmt::format(", half/full trim from ({})",rq->halffulllist+1); else out += fmt::format(", half/full from ({})",rq->halffulllist+1); - else if (rq->skip) + } else if (rq->skip) { if (rq->trim) out += fmt::format(", skip trim from ({})",rq->skiplist+1); else out += fmt::format(", skip from ({})",rq->skiplist+1); + } out += "\n"; // list of neigh list attributes From 5a9c9981e747654221f6cfdd4a572d9c523f7d46 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 16:14:21 -0500 Subject: [PATCH 098/189] replace include file with forward declaration --- src/KOKKOS/atom_kokkos.cpp | 1 + src/KOKKOS/atom_kokkos.h | 3 +-- src/KOKKOS/min_kokkos.cpp | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/KOKKOS/atom_kokkos.cpp b/src/KOKKOS/atom_kokkos.cpp index bc393b29d8..c55c1d315b 100644 --- a/src/KOKKOS/atom_kokkos.cpp +++ b/src/KOKKOS/atom_kokkos.cpp @@ -25,6 +25,7 @@ #include "kokkos_base.h" #include "modify.h" #include "fix.h" +#include "fix_property_atom_kokkos.h" using namespace LAMMPS_NS; diff --git a/src/KOKKOS/atom_kokkos.h b/src/KOKKOS/atom_kokkos.h index 21a9aeebbd..6a3036375d 100644 --- a/src/KOKKOS/atom_kokkos.h +++ b/src/KOKKOS/atom_kokkos.h @@ -14,7 +14,6 @@ #include "atom.h" // IWYU pragma: export #include "kokkos_type.h" -#include "fix_property_atom_kokkos.h" #include @@ -27,7 +26,7 @@ class AtomKokkos : public Atom { public: bool sort_classic; int nprop_atom; - FixPropertyAtomKokkos** fix_prop_atom; + class FixPropertyAtomKokkos **fix_prop_atom; DAT::tdual_tagint_1d k_tag; DAT::tdual_int_1d k_type, k_mask; diff --git a/src/KOKKOS/min_kokkos.cpp b/src/KOKKOS/min_kokkos.cpp index c01a53c7b3..3460fe9009 100644 --- a/src/KOKKOS/min_kokkos.cpp +++ b/src/KOKKOS/min_kokkos.cpp @@ -21,6 +21,7 @@ #include "angle.h" #include "atom_kokkos.h" #include "atom_masks.h" +#include "atom_vec.h" #include "bond.h" #include "comm.h" #include "compute.h" From 603837c96ca07d84c638b9db1cb1f39b9cddbc0d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 16:21:04 -0500 Subject: [PATCH 099/189] add versionadded tag --- doc/src/compute_reaxff_atom.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/src/compute_reaxff_atom.rst b/doc/src/compute_reaxff_atom.rst index 9f690d19c3..997ad02e9f 100644 --- a/doc/src/compute_reaxff_atom.rst +++ b/doc/src/compute_reaxff_atom.rst @@ -40,6 +40,8 @@ Examples Description """"""""""" +.. versionadded:: TBD + Define a computation that extracts bond information computed by the ReaxFF potential specified by :doc:`pair_style reaxff `. From 7dab2b7eee941e28e01416ede9688e661c1cf520 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 16:21:17 -0500 Subject: [PATCH 100/189] add new package files to .gitignore --- src/.gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/.gitignore b/src/.gitignore index 3b7902303d..112a1486f7 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -633,6 +633,8 @@ /compute_ptm_atom.h /compute_rattlers_atom.cpp /compute_rattlers_atom.h +/compute_reaxff_atom.cpp +/compute_reaxff_atom.h /compute_rigid_local.cpp /compute_rigid_local.h /compute_slcsa_atom.cpp From ff0553e8592a888c7757c14a1f9fac6a688b03dd Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 16:50:30 -0500 Subject: [PATCH 101/189] fix typos --- src/balance.cpp | 2 +- src/fix_balance.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/balance.cpp b/src/balance.cpp index 42463752f9..c8bbead327 100644 --- a/src/balance.cpp +++ b/src/balance.cpp @@ -233,7 +233,7 @@ void Balance::command(int narg, char **arg) } if (style == SHIFT) { - const int blen = bstr.size(); + const int blen = bstr.size() + 1; for (int i = 0; i < blen; i++) { if (bstr[i] != 'x' && bstr[i] != 'y' && bstr[i] != 'z') error->all(FLERR,"Balance shift string is invalid"); diff --git a/src/fix_balance.cpp b/src/fix_balance.cpp index 7174765f52..f405de28b7 100644 --- a/src/fix_balance.cpp +++ b/src/fix_balance.cpp @@ -83,7 +83,7 @@ FixBalance::FixBalance(LAMMPS *lmp, int narg, char **arg) : // error checks if (lbstyle == SHIFT) { - int blen = bstr.size(); + const int blen = bstr.size() + 1; for (int i = 0; i < blen; i++) { if (bstr[i] != 'x' && bstr[i] != 'y' && bstr[i] != 'z') error->all(FLERR,"Fix balance shift string is invalid"); From 5f0bdca3f2cad56b84b9252820c9e82298cb4902 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 17:09:37 -0500 Subject: [PATCH 102/189] add missing override --- src/EXTRA-FIX/fix_nonaffine_displacement.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EXTRA-FIX/fix_nonaffine_displacement.h b/src/EXTRA-FIX/fix_nonaffine_displacement.h index 0a195dc08e..3341ab1834 100644 --- a/src/EXTRA-FIX/fix_nonaffine_displacement.h +++ b/src/EXTRA-FIX/fix_nonaffine_displacement.h @@ -32,7 +32,7 @@ class FixNonaffineDisplacement : public Fix { void post_constructor() override; void init() override; void init_list(int, class NeighList *) override; - void setup(int); + void setup(int) override; void post_force(int) override; void write_restart(FILE *fp) override; void restart(char *buf) override; @@ -62,7 +62,7 @@ class FixNonaffineDisplacement : public Fix { void calculate_D2Min(); void save_reference_state(); void minimum_image0(double *); - void grow_arrays(int); + void grow_arrays(int) override; }; } // namespace LAMMPS_NS From c4626e982f8a94d0a3d7594e5cd1712ca624d469 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 17:19:45 -0500 Subject: [PATCH 103/189] revert bogus change --- src/balance.cpp | 2 +- src/fix_balance.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/balance.cpp b/src/balance.cpp index c8bbead327..42463752f9 100644 --- a/src/balance.cpp +++ b/src/balance.cpp @@ -233,7 +233,7 @@ void Balance::command(int narg, char **arg) } if (style == SHIFT) { - const int blen = bstr.size() + 1; + const int blen = bstr.size(); for (int i = 0; i < blen; i++) { if (bstr[i] != 'x' && bstr[i] != 'y' && bstr[i] != 'z') error->all(FLERR,"Balance shift string is invalid"); diff --git a/src/fix_balance.cpp b/src/fix_balance.cpp index f405de28b7..23a56c0a9d 100644 --- a/src/fix_balance.cpp +++ b/src/fix_balance.cpp @@ -83,7 +83,7 @@ FixBalance::FixBalance(LAMMPS *lmp, int narg, char **arg) : // error checks if (lbstyle == SHIFT) { - const int blen = bstr.size() + 1; + const int blen = bstr.size(); for (int i = 0; i < blen; i++) { if (bstr[i] != 'x' && bstr[i] != 'y' && bstr[i] != 'z') error->all(FLERR,"Fix balance shift string is invalid"); From 172238f4cafc59c1ab703739c98fc65a026bdcf4 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 17:59:18 -0500 Subject: [PATCH 104/189] workaround hack for macOS --- src/PYTHON/python_impl.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/PYTHON/python_impl.cpp b/src/PYTHON/python_impl.cpp index c1c26c7bb3..ea4ac63ce7 100644 --- a/src/PYTHON/python_impl.cpp +++ b/src/PYTHON/python_impl.cpp @@ -79,7 +79,7 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp) // Force the stdout and stderr streams to be unbuffered. bool unbuffered = PYTHONUNBUFFERED != nullptr && strcmp(PYTHONUNBUFFERED, "1") == 0; -#if PY_VERSION_HEX >= 0x030800f0 +#if (PY_VERSION_HEX >= 0x030800f0) && !defined(__APPLE__) PyConfig config; PyConfig_InitPythonConfig(&config); config.buffered_stdio = !unbuffered; @@ -87,6 +87,8 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp) // Python Global configuration variable Py_UnbufferedStdioFlag = unbuffered; #endif +#else +#warning Cannot force stdout and stderr to be unbuffered #endif #ifdef MLIAP_PYTHON From 1df91f21a18d88006e07ec50e56c16ca7036b927 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 17:59:18 -0500 Subject: [PATCH 105/189] workaround hack for macOS --- src/PYTHON/python_impl.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/PYTHON/python_impl.cpp b/src/PYTHON/python_impl.cpp index c1c26c7bb3..ea4ac63ce7 100644 --- a/src/PYTHON/python_impl.cpp +++ b/src/PYTHON/python_impl.cpp @@ -79,7 +79,7 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp) // Force the stdout and stderr streams to be unbuffered. bool unbuffered = PYTHONUNBUFFERED != nullptr && strcmp(PYTHONUNBUFFERED, "1") == 0; -#if PY_VERSION_HEX >= 0x030800f0 +#if (PY_VERSION_HEX >= 0x030800f0) && !defined(__APPLE__) PyConfig config; PyConfig_InitPythonConfig(&config); config.buffered_stdio = !unbuffered; @@ -87,6 +87,8 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp) // Python Global configuration variable Py_UnbufferedStdioFlag = unbuffered; #endif +#else +#warning Cannot force stdout and stderr to be unbuffered #endif #ifdef MLIAP_PYTHON From abe635671625214731b59bc8dc95a9cbf1bc23fe Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 21:34:11 -0500 Subject: [PATCH 106/189] update help index table for recently added or changed styles --- tools/lammps-gui/help_index.table | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tools/lammps-gui/help_index.table b/tools/lammps-gui/help_index.table index 5ce4ae6203..5aa9e13dda 100644 --- a/tools/lammps-gui/help_index.table +++ b/tools/lammps-gui/help_index.table @@ -254,6 +254,7 @@ compute_nbond_atom.html compute nbond/atom compute_omega_chunk.html compute omega/chunk compute_orientorder_atom.html compute orientorder/atom compute_orientorder_atom.html compute orientorder/atom/kk +compute_pace.html compute pace compute_pair_local.html compute pair/local compute_pair.html compute pair compute_pe_atom.html compute pe/atom @@ -267,13 +268,17 @@ compute_property_chunk.html compute property/chunk compute_property_grid.html compute property/grid compute_property_local.html compute property/local compute_ptm_atom.html compute ptm/atom +compute_rattlers_atom.html compute rattlers/atom compute_rdf.html compute rdf +compute_reaxff_atom.html compute reaxff/atom +compute_reaxff_atom.html compute reaxff/atom/kk compute_reduce_chunk.html compute reduce/chunk compute_reduce.html compute reduce compute_reduce.html compute reduce/region compute_rigid_local.html compute rigid/local compute.html compute compute_saed.html compute saed +compute_slcsa_atom.html compute slcsa/atom compute_slice.html compute slice compute_smd_contact_radius.html compute smd/contact/radius compute_smd_damage.html compute smd/damage @@ -484,6 +489,7 @@ fix_drude_transform.html fix drude/transform/inverse fix_dt_reset.html fix dt/reset fix_dt_reset.html fix dt/reset/kk fix_efield.html fix efield +fix_efield.html fix efield/kk fix_efield.html fix efield/tip4p fix_ehex.html fix ehex fix_electrode.html fix electrode/conp @@ -565,6 +571,7 @@ fix_nh.html fix nvt/kk fix_nh.html fix nvt/omp fix_nh_uef.html fix npt/uef fix_nh_uef.html fix nvt/uef +fix_nonaffine_displacement.html fix nonaffine/displacement fix_nph_asphere.html fix nph/asphere fix_nph_asphere.html fix nph/asphere/omp fix_nph_body.html fix nph/body @@ -634,6 +641,7 @@ fix_polarize.html fix polarize/functional fix_pour.html fix pour fix_precession_spin.html fix precession/spin fix_press_berendsen.html fix press/berendsen +fix_press_langevin.html fix press/langevin fix_print.html fix print fix_propel_self.html fix propel/self fix_property_atom.html fix property/atom @@ -703,14 +711,17 @@ fix_spring_chunk.html fix spring/chunk fix_spring_rg.html fix spring/rg fix_spring.html fix spring fix_spring_self.html fix spring/self +fix_spring_self.html fix spring/self/kk fix_srd.html fix srd fix_store_force.html fix store/force fix_store_state.html fix store/state fix_temp_berendsen.html fix temp/berendsen +fix_temp_berendsen.html fix temp/berendsen/kk fix_temp_csvr.html fix temp/csld fix_temp_csvr.html fix temp/csvr fix_temp_rescale_eff.html fix temp/rescale/eff fix_temp_rescale.html fix temp/rescale +fix_temp_rescale.html fix temp/rescale/kk fix_tfmc.html fix tfmc fix_tgnh_drude.html fix tgnpt/drude fix_tgnh_drude.html fix tgnvt/drude @@ -980,6 +991,7 @@ pair_coul_shield.html pair_style coul/shield pair_coul_slater.html pair_style coul/slater pair_coul_slater.html pair_style coul/slater/cut pair_coul_slater.html pair_style coul/slater/long +pair_coul_slater.html pair_style coul/slater/long/gpu pair_coul_tt.html pair_style coul/tt pair_cs.html pair_style born/coul/dsf/cs pair_cs.html pair_style born/coul/long/cs @@ -1073,8 +1085,10 @@ pair_fep_soft.html pair_style lj/class2/coul/cut/soft pair_fep_soft.html pair_style lj/class2/coul/long/soft pair_fep_soft.html pair_style lj/class2/soft pair_fep_soft.html pair_style lj/cut/coul/cut/soft +pair_fep_soft.html pair_style lj/cut/coul/cut/soft/gpu pair_fep_soft.html pair_style lj/cut/coul/cut/soft/omp pair_fep_soft.html pair_style lj/cut/coul/long/soft +pair_fep_soft.html pair_style lj/cut/coul/long/soft/gpu pair_fep_soft.html pair_style lj/cut/coul/long/soft/omp pair_fep_soft.html pair_style lj/cut/soft pair_fep_soft.html pair_style lj/cut/soft/omp @@ -1225,7 +1239,9 @@ pair_meam_sw_spline.html pair_style meam/sw/spline pair_mesocnt.html pair_style mesocnt pair_mesocnt.html pair_style mesocnt/viscous pair_mesodpd.html pair_style edpd +pair_mesodpd.html pair_style edpd/gpu pair_mesodpd.html pair_style mdpd +pair_mesodpd.html pair_style mdpd/gpu pair_mesodpd.html pair_style mdpd/rhosum pair_mesodpd.html pair_style tdpd pair_mgpt.html pair_style mgpt @@ -1245,7 +1261,8 @@ pair_morse.html pair_style morse/smooth/linear/omp pair_multi_lucy.html pair_style multi/lucy pair_multi_lucy_rx.html pair_style multi/lucy/rx pair_multi_lucy_rx.html pair_style multi/lucy/rx/kk -pair_nb3b_harmonic.html pair_style nb3b/harmonic +pair_nb3b.html pair_style nb3b/harmonic +pair_nb3b.html pair_style nb3b/screened pair_nm.html pair_style nm/cut pair_nm.html pair_style nm/cut/coul/cut pair_nm.html pair_style nm/cut/coul/cut/omp @@ -1303,16 +1320,20 @@ pair_smd_triangulated_surface.html pair_style smd/tri_surface pair_smd_ulsph.html pair_style smd/ulsph pair_smtbq.html pair_style smtbq pair_snap.html pair_style snap +pair_snap.html pair_style snap/intel pair_snap.html pair_style snap/kk pair_soft.html pair_style soft pair_soft.html pair_style soft/gpu pair_soft.html pair_style soft/omp pair_sph_heatconduction.html pair_style sph/heatconduction +pair_sph_heatconduction.html pair_style sph/heatconduction/gpu pair_sph_idealgas.html pair_style sph/idealgas pair_sph_lj.html pair_style sph/lj +pair_sph_lj.html pair_style sph/lj/gpu pair_sph_rhosum.html pair_style sph/rhosum pair_sph_taitwater_morris.html pair_style sph/taitwater/morris pair_sph_taitwater.html pair_style sph/taitwater +pair_sph_taitwater.html pair_style sph/taitwater/gpu pair_spica.html pair_style lj/spica pair_spica.html pair_style lj/spica/coul/long pair_spica.html pair_style lj/spica/coul/long/gpu @@ -1384,6 +1405,7 @@ pair_write.html pair_write pair_ylz.html pair_style ylz pair_yukawa_colloid.html pair_style yukawa/colloid pair_yukawa_colloid.html pair_style yukawa/colloid/gpu +pair_yukawa_colloid.html pair_style yukawa/colloid/kk pair_yukawa_colloid.html pair_style yukawa/colloid/omp pair_yukawa.html pair_style yukawa pair_yukawa.html pair_style yukawa/gpu From 84af9e347675506305e4a46847d37b14cc1b3a71 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 22:02:39 -0500 Subject: [PATCH 107/189] remove hack --- src/PYTHON/python_impl.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/PYTHON/python_impl.cpp b/src/PYTHON/python_impl.cpp index ea4ac63ce7..02070ee151 100644 --- a/src/PYTHON/python_impl.cpp +++ b/src/PYTHON/python_impl.cpp @@ -79,7 +79,7 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp) // Force the stdout and stderr streams to be unbuffered. bool unbuffered = PYTHONUNBUFFERED != nullptr && strcmp(PYTHONUNBUFFERED, "1") == 0; -#if (PY_VERSION_HEX >= 0x030800f0) && !defined(__APPLE__) +#if (PY_VERSION_HEX >= 0x030800f0) PyConfig config; PyConfig_InitPythonConfig(&config); config.buffered_stdio = !unbuffered; @@ -87,8 +87,6 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp) // Python Global configuration variable Py_UnbufferedStdioFlag = unbuffered; #endif -#else -#warning Cannot force stdout and stderr to be unbuffered #endif #ifdef MLIAP_PYTHON From 95d1a41ee44a2fe05e4acb266bb550e4fa5dd3bc Mon Sep 17 00:00:00 2001 From: jtclemm Date: Fri, 15 Dec 2023 13:33:45 -0700 Subject: [PATCH 108/189] Fixing bpm/sphere error in fix move, displace atoms --- src/displace_atoms.cpp | 16 +++++++++++---- src/fix_move.cpp | 45 +++++++++++++++++++++++++++++++++--------- src/fix_move.h | 2 +- 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/displace_atoms.cpp b/src/displace_atoms.cpp index fa333f1bc2..5ecf5a2c9e 100644 --- a/src/displace_atoms.cpp +++ b/src/displace_atoms.cpp @@ -160,7 +160,7 @@ void DisplaceAtoms::command(int narg, char **arg) int *mask = atom->mask; int nlocal = atom->nlocal; - double fraction,dramp; + double fraction, dramp; for (i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { @@ -255,11 +255,12 @@ void DisplaceAtoms::command(int narg, char **arg) int line_flag = atom->line_flag; int tri_flag = atom->tri_flag; int body_flag = atom->body_flag; + int quat_atom_flag = atom->quat_flag; int theta_flag = 0; int quat_flag = 0; if (line_flag) theta_flag = 1; - if (ellipsoid_flag || tri_flag || body_flag) quat_flag = 1; + if (ellipsoid_flag || tri_flag || body_flag || quat_atom_flag) quat_flag = 1; // AtomVec pointers to retrieve per-atom storage of extra quantities @@ -269,6 +270,7 @@ void DisplaceAtoms::command(int narg, char **arg) auto avec_body = dynamic_cast(atom->style_match("body")); double **x = atom->x; + double **quat_atom = atom->quat; int *ellipsoid = atom->ellipsoid; int *line = atom->line; int *tri = atom->tri; @@ -313,7 +315,7 @@ void DisplaceAtoms::command(int narg, char **arg) // quats for ellipsoids, tris, and bodies - if (quat_flag) { + if (quat_flag && !quat_atom_flag) { quat = nullptr; if (ellipsoid_flag && ellipsoid[i] >= 0) quat = avec_ellipsoid->bonus[ellipsoid[i]].quat; @@ -322,12 +324,18 @@ void DisplaceAtoms::command(int narg, char **arg) else if (body_flag && body[i] >= 0) quat = avec_body->bonus[body[i]].quat; if (quat) { - MathExtra::quatquat(qrotate,quat,qnew); + MathExtra::quatquat(qrotate, quat, qnew); quat[0] = qnew[0]; quat[1] = qnew[1]; quat[2] = qnew[2]; quat[3] = qnew[3]; } + } else if (quat_atom_flag) { + MathExtra::quatquat(qrotate, quat_atom[i], qnew); + quat_atom[i][0] = qnew[0]; + quat_atom[i][1] = qnew[1]; + quat_atom[i][2] = qnew[2]; + quat_atom[i][3] = qnew[3]; } } } diff --git a/src/fix_move.cpp b/src/fix_move.cpp index 36bba410fc..99b5b30bec 100644 --- a/src/fix_move.cpp +++ b/src/fix_move.cpp @@ -276,10 +276,11 @@ FixMove::FixMove(LAMMPS *lmp, int narg, char **arg) : line_flag = atom->line_flag; tri_flag = atom->tri_flag; body_flag = atom->body_flag; + quat_atom_flag = atom->quat_flag; theta_flag = quat_flag = 0; if (line_flag) theta_flag = 1; - if (ellipsoid_flag || tri_flag || body_flag) quat_flag = 1; + if (ellipsoid_flag || tri_flag || body_flag || quat_atom_flag) quat_flag = 1; extra_flag = 0; if (omega_flag || angmom_flag || theta_flag || quat_flag) extra_flag = 1; @@ -329,7 +330,7 @@ FixMove::FixMove(LAMMPS *lmp, int narg, char **arg) : } } - if (quat_flag) { + if (quat_flag && !quat_atom_flag) { double *quat; for (int i = 0; i < nlocal; i++) { quat = nullptr; @@ -349,6 +350,16 @@ FixMove::FixMove(LAMMPS *lmp, int narg, char **arg) : } else qoriginal[i][0] = qoriginal[i][1] = qoriginal[i][2] = qoriginal[i][3] = 0.0; } + } else if (quat_atom_flag) { + double **quat_atom = atom->quat; + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + qoriginal[i][0] = quat_atom[i][0]; + qoriginal[i][1] = quat_atom[i][1]; + qoriginal[i][2] = quat_atom[i][2]; + qoriginal[i][3] = quat_atom[i][3]; + } + } } // nrestart = size of per-atom restart data @@ -521,6 +532,7 @@ void FixMove::initial_integrate(int /*vflag*/) double *radius = atom->radius; double *rmass = atom->rmass; double *mass = atom->mass; + double **quat_atom = atom->quat; int *type = atom->type; int *ellipsoid = atom->ellipsoid; int *line = atom->line; @@ -749,9 +761,9 @@ void FixMove::initial_integrate(int /*vflag*/) avec_line->bonus[atom->line[i]].theta = theta_new; } - // quats for ellipsoids, tris, and bodies + // quats for ellipsoids, tris, bodies, and bpm/sphere - if (quat_flag) { + if (quat_flag && !quat_atom_flag) { quat = nullptr; if (ellipsoid_flag && ellipsoid[i] >= 0) quat = avec_ellipsoid->bonus[ellipsoid[i]].quat; @@ -760,6 +772,8 @@ void FixMove::initial_integrate(int /*vflag*/) else if (body_flag && body[i] >= 0) quat = avec_body->bonus[body[i]].quat; if (quat) MathExtra::quatquat(qrotate, qoriginal[i], quat); + } else if (quat_atom_flag) { + MathExtra::quatquat(qrotate, qoriginal[i], quat_atom[i]); } } @@ -880,9 +894,9 @@ void FixMove::initial_integrate(int /*vflag*/) avec_line->bonus[atom->line[i]].theta = theta_new; } - // quats for ellipsoids, tris, and bodies + // quats for ellipsoids, tris, bodies, and bpm/sphere - if (quat_flag) { + if (quat_flag && !quat_atom_flag) { quat = nullptr; if (ellipsoid_flag && ellipsoid[i] >= 0) quat = avec_ellipsoid->bonus[ellipsoid[i]].quat; @@ -891,6 +905,8 @@ void FixMove::initial_integrate(int /*vflag*/) else if (body_flag && body[i] >= 0) quat = avec_body->bonus[body[i]].quat; if (quat) MathExtra::quatquat(qrotate, qoriginal[i], quat); + } else if (quat_atom_flag) { + MathExtra::quatquat(qrotate, qoriginal[i], quat_atom[i]); } } @@ -1263,6 +1279,7 @@ void FixMove::set_arrays(int i) double *quat; double **x = atom->x; + double **quat_atom = atom->quat; imageint *image = atom->image; int *ellipsoid = atom->ellipsoid; int *line = atom->line; @@ -1341,9 +1358,9 @@ void FixMove::set_arrays(int i) toriginal[i] = theta - 0.0; // NOTE: edit this line } - // quats for ellipsoids, tris, and bodies + // quats for ellipsoids, tris, bodies, and bpm/sphere - if (quat_flag) { + if (quat_flag & !quat_atom_flag) { quat = nullptr; if (ellipsoid_flag && ellipsoid[i] >= 0) quat = avec_ellipsoid->bonus[ellipsoid[i]].quat; @@ -1354,6 +1371,11 @@ void FixMove::set_arrays(int i) if (quat) { // qoriginal = f(quat,-delta); // NOTE: edit this line } + } else if (quat_atom_flag) { + // qoriginal[0] = quat_atom[i][0]; // NOTE: edit this line + // qoriginal[1] = quat_atom[i][1]; // NOTE: edit this line + // qoriginal[2] = quat_atom[i][2]; // NOTE: edit this line + // qoriginal[3] = quat_atom[i][3]; // NOTE: edit this line } } xoriginal[i][0] -= vx * delta; @@ -1400,7 +1422,7 @@ void FixMove::set_arrays(int i) // quats for ellipsoids, tris, and bodies - if (quat_flag) { + if (quat_flag && !quat_atom_flag) { quat = nullptr; if (ellipsoid_flag && ellipsoid[i] >= 0) quat = avec_ellipsoid->bonus[ellipsoid[i]].quat; @@ -1411,6 +1433,11 @@ void FixMove::set_arrays(int i) if (quat) { // qoriginal = f(quat,-delta); // NOTE: edit this line } + } else if (quat_atom_flag) { + // qoriginal[0] = quat_atom[i][0]; // NOTE: edit this line + // qoriginal[1] = quat_atom[i][1]; // NOTE: edit this line + // qoriginal[2] = quat_atom[i][2]; // NOTE: edit this line + // qoriginal[3] = quat_atom[i][3]; // NOTE: edit this line } } } diff --git a/src/fix_move.h b/src/fix_move.h index e3c018f54d..244a9d704a 100644 --- a/src/fix_move.h +++ b/src/fix_move.h @@ -61,7 +61,7 @@ class FixMove : public Fix { int xvar, yvar, zvar, vxvar, vyvar, vzvar; int xvarstyle, yvarstyle, zvarstyle, vxvarstyle, vyvarstyle, vzvarstyle; int extra_flag, omega_flag, angmom_flag; - int radius_flag, ellipsoid_flag, line_flag, tri_flag, body_flag; + int radius_flag, ellipsoid_flag, line_flag, tri_flag, body_flag, quat_atom_flag; int theta_flag, quat_flag; int nlevels_respa, nrestart; int time_origin; From 41495579a688bcb112fe6e85430951657607d9dd Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 00:18:05 -0500 Subject: [PATCH 109/189] update list --- doc/utils/sphinx-config/false_positives.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/utils/sphinx-config/false_positives.txt b/doc/utils/sphinx-config/false_positives.txt index ad3dee6a00..03e67b95cb 100644 --- a/doc/utils/sphinx-config/false_positives.txt +++ b/doc/utils/sphinx-config/false_positives.txt @@ -792,6 +792,7 @@ dispersionflag dissipative Dissipative distharm +distutils dl dlabel dlambda From e100a42087f0bf75a815fbe95cd837ba8ac2d22b Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 17:07:37 -0500 Subject: [PATCH 110/189] (re)throw EOF exception when next_dvector() has not yet read any items --- src/potential_file_reader.cpp | 2 ++ src/text_file_reader.cpp | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/potential_file_reader.cpp b/src/potential_file_reader.cpp index 2c0b9a6a55..613225a797 100644 --- a/src/potential_file_reader.cpp +++ b/src/potential_file_reader.cpp @@ -144,6 +144,8 @@ void PotentialFileReader::next_dvector(double *list, int n) { try { return reader->next_dvector(list, n); + } catch (EOFException &) { + throw EOFException("EOF reached"); } catch (FileReaderException &e) { error->one(FLERR, e.what()); } diff --git a/src/text_file_reader.cpp b/src/text_file_reader.cpp index 46a5fd33a9..0b8d717687 100644 --- a/src/text_file_reader.cpp +++ b/src/text_file_reader.cpp @@ -189,8 +189,9 @@ void TextFileReader::next_dvector(double *list, int n) char *ptr = next_line(); if (ptr == nullptr) { - // EOF - if (i < n) { + if (i == 0) { // EOF without any records + throw EOFException("EOF reached"); + } else if (i < n) { // EOF with incomplete data throw FileReaderException( fmt::format("Incorrect format in {} file! {}/{} values", filetype, i, n)); } From 96ef731f069c2d65121c8a0e7041f94e4e235df9 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 17:07:53 -0500 Subject: [PATCH 111/189] remove unused items --- src/MOLECULE/fix_cmap.cpp | 11 ----------- src/MOLECULE/fix_cmap.h | 3 +-- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/MOLECULE/fix_cmap.cpp b/src/MOLECULE/fix_cmap.cpp index f74c05ef06..edd52ee47b 100644 --- a/src/MOLECULE/fix_cmap.cpp +++ b/src/MOLECULE/fix_cmap.cpp @@ -87,9 +87,6 @@ FixCMAP::FixCMAP(LAMMPS *lmp, int narg, char **arg) : respa_level_support = 1; ilevel_respa = 0; - MPI_Comm_rank(world,&me); - MPI_Comm_size(world,&nprocs); - // allocate memory for CMAP data memory->create(g_axis,CMAPDIM,"cmap:g_axis"); @@ -184,10 +181,6 @@ void FixCMAP::init() for (i = 0; i < 6; i++) set_map_derivatives(cmapgrid[i],d1cmapgrid[i],d2cmapgrid[i],d12cmapgrid[i]); - // define newton_bond here in case restart file was read (not data file) - - newton_bond = force->newton_bond; - if (utils::strmatch(update->integrate_style,"^respa")) { ilevel_respa = (dynamic_cast(update->integrate))->nlevels-1; if (respa_level >= 0) ilevel_respa = MIN(respa_level,ilevel_respa); @@ -934,10 +927,6 @@ void FixCMAP::read_data_header(char *line) } catch (std::exception &e) { error->all(FLERR,"Invalid read data header line for fix cmap: {}", e.what()); } - - // not set in constructor because this fix could be defined before newton command - - newton_bond = force->newton_bond; } /* ---------------------------------------------------------------------- diff --git a/src/MOLECULE/fix_cmap.h b/src/MOLECULE/fix_cmap.h index fce76aa540..1c6aba95e0 100644 --- a/src/MOLECULE/fix_cmap.h +++ b/src/MOLECULE/fix_cmap.h @@ -65,8 +65,7 @@ class FixCMAP : public Fix { double memory_usage() override; private: - int nprocs, me; - int newton_bond, eflag_caller; + int eflag_caller; int ctype, ilevel_respa; int ncrosstermtypes, crossterm_per_atom, maxcrossterm; int ncrosstermlist; From fb6a5843b9d5457bded5aa323233f40ff728faaa Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 17:14:47 -0500 Subject: [PATCH 112/189] handle comments in CMAP coeff assignments --- src/MOLECULE/fix_cmap.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MOLECULE/fix_cmap.cpp b/src/MOLECULE/fix_cmap.cpp index edd52ee47b..cfc71f96fc 100644 --- a/src/MOLECULE/fix_cmap.cpp +++ b/src/MOLECULE/fix_cmap.cpp @@ -946,10 +946,10 @@ void FixCMAP::read_data_section(char * /*keyword*/, int /*n*/, char *buf, // loop over lines of CMAP crossterms // tokenize the line into values - // add crossterm to one of my atoms, depending on newton_bond + // add crossterm to one of my atoms for (const auto &line : lines) { - ValueTokenizer values(line); + ValueTokenizer values(utils::trim_comment(line)); try { values.skip(); itype = values.next_int(); From 4bf1b1d9c0d058ceaba03f1976709b3868684fa1 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 17:15:31 -0500 Subject: [PATCH 113/189] some refactoring and modernization --- src/MOLECULE/fix_cmap.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/MOLECULE/fix_cmap.cpp b/src/MOLECULE/fix_cmap.cpp index cfc71f96fc..1f137cb833 100644 --- a/src/MOLECULE/fix_cmap.cpp +++ b/src/MOLECULE/fix_cmap.cpp @@ -49,15 +49,14 @@ using namespace LAMMPS_NS; using namespace FixConst; using namespace MathConst; -#define MAXLINE 256 -#define LISTDELTA 10000 -#define LB_FACTOR 1.5 +static constexpr int LISTDELTA = 10000; +static constexpr double LB_FACTOR = 1.5; -#define CMAPMAX 6 // max # of CMAP terms stored by one atom -#define CMAPDIM 24 // grid map dimension is 24 x 24 -#define CMAPXMIN -360.0 -#define CMAPXMIN2 -180.0 -#define CMAPDX 15.0 // 360/CMAPDIM +static constexpr int CMAPMAX = 6; // max # of CMAP terms stored by one atom +static constexpr int CMAPDIM = 24; // grid map dimension is 24 x 24 +static constexpr double CMAPXMIN = -360.0; +static constexpr double CMAPXMIN2 = -180.0; +static constexpr double CMAPDX = 15.0; // 360/CMAPDIM /* ---------------------------------------------------------------------- */ @@ -90,10 +89,10 @@ FixCMAP::FixCMAP(LAMMPS *lmp, int narg, char **arg) : // allocate memory for CMAP data memory->create(g_axis,CMAPDIM,"cmap:g_axis"); - memory->create(cmapgrid,6,CMAPDIM,CMAPDIM,"cmap:grid"); - memory->create(d1cmapgrid,6,CMAPDIM,CMAPDIM,"cmap:d1grid"); - memory->create(d2cmapgrid,6,CMAPDIM,CMAPDIM,"cmap:d2grid"); - memory->create(d12cmapgrid,6,CMAPDIM,CMAPDIM,"cmap:d12grid"); + memory->create(cmapgrid,CMAPMAX,CMAPDIM,CMAPDIM,"cmap:grid"); + memory->create(d1cmapgrid,CMAPMAX,CMAPDIM,CMAPDIM,"cmap:d1grid"); + memory->create(d2cmapgrid,CMAPMAX,CMAPDIM,CMAPDIM,"cmap:d2grid"); + memory->create(d12cmapgrid,CMAPMAX,CMAPDIM,CMAPDIM,"cmap:d12grid"); // read and setup CMAP data @@ -231,6 +230,8 @@ void FixCMAP::min_setup(int vflag) void FixCMAP::pre_neighbor() { int i,m,atom1,atom2,atom3,atom4,atom5; + const int me = comm->me; + const int nprocs = comm->nprocs; // guesstimate initial length of local crossterm list // if ncmap was not set (due to read_restart, no read_data), From 1ab406ee1acbdbb47208b219ba3b5f33f8ecb627 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 17:16:06 -0500 Subject: [PATCH 114/189] read CMAP data blocks one at a time and catch EOF exception to stop reading --- src/MOLECULE/fix_cmap.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/MOLECULE/fix_cmap.cpp b/src/MOLECULE/fix_cmap.cpp index 1f137cb833..01b62d2a28 100644 --- a/src/MOLECULE/fix_cmap.cpp +++ b/src/MOLECULE/fix_cmap.cpp @@ -39,6 +39,7 @@ #include "memory.h" #include "potential_file_reader.h" #include "respa.h" +#include "text_file_reader.h" #include "update.h" #include @@ -631,15 +632,22 @@ void FixCMAP::read_grid_map(char *cmapfile) { if (comm->me == 0) { try { - memset(&cmapgrid[0][0][0], 0, 6*CMAPDIM*CMAPDIM*sizeof(double)); + ncrosstermtypes = 0; + memset(&cmapgrid[0][0][0], 0, CMAPMAX*CMAPDIM*CMAPDIM*sizeof(double)); + utils::logmesg(lmp, "Reading CMAP parameters from: {}\n", cmapfile); PotentialFileReader reader(lmp, cmapfile, "cmap grid"); - // there are six maps in this order. + // there may be up to six maps. + // the charmm36.cmap file has in this order. // alanine, alanine-proline, proline, proline-proline, glycine, glycine-proline. - // read as one big blob of numbers while ignoring comments - - reader.next_dvector(&cmapgrid[0][0][0],6*CMAPDIM*CMAPDIM); + // custom CMAP files created by charmm-gui may have fewer entries + // read one map at a time as a blob of numbers while ignoring comments + // and stop reading when whe have reached EOF. + for (ncrosstermtypes = 0; ncrosstermtypes < CMAPMAX; ++ncrosstermtypes) + reader.next_dvector(&cmapgrid[ncrosstermtypes][0][0],CMAPDIM*CMAPDIM); + } catch (EOFException &) { + utils::logmesg(lmp, " Read in CMAP data for {} crossterm types\n", ncrosstermtypes); } catch (std::exception &e) { error->one(FLERR,"Error reading CMAP potential file: {}", e.what()); } From 15a7b93361bc74f915b397c74a443022585dd5a7 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 17:40:31 -0500 Subject: [PATCH 115/189] relax epsilon to be compatible with most recent GCC compilers on Fedora 39 --- unittest/commands/test_groups.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittest/commands/test_groups.cpp b/unittest/commands/test_groups.cpp index b91a6108d9..7f0a054c40 100644 --- a/unittest/commands/test_groups.cpp +++ b/unittest/commands/test_groups.cpp @@ -314,7 +314,7 @@ TEST_F(GroupTest, Dynamic) command("group ramp variable grow");); } -constexpr double EPSILON = 1.0e-14; +constexpr double EPSILON = 1.0e-13; TEST_F(GroupTest, VariableFunctions) { From 695a81ef70b3a97b094c4e42ef82c8b9e603a649 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 21:50:38 -0500 Subject: [PATCH 116/189] avoid uninitialized data access --- src/MOLECULE/fix_cmap.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/MOLECULE/fix_cmap.cpp b/src/MOLECULE/fix_cmap.cpp index 01b62d2a28..cb4cb8cadc 100644 --- a/src/MOLECULE/fix_cmap.cpp +++ b/src/MOLECULE/fix_cmap.cpp @@ -86,6 +86,7 @@ FixCMAP::FixCMAP(LAMMPS *lmp, int narg, char **arg) : wd_section = 1; respa_level_support = 1; ilevel_respa = 0; + eflag_caller = 1; // allocate memory for CMAP data From 4ae4c8103d281e3c0979ccaa03dcb704741a3c47 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 22:46:02 -0500 Subject: [PATCH 117/189] step LAMMPS-GUI patch level --- tools/lammps-gui/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lammps-gui/CMakeLists.txt b/tools/lammps-gui/CMakeLists.txt index caae722865..b1469794bb 100644 --- a/tools/lammps-gui/CMakeLists.txt +++ b/tools/lammps-gui/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -project(lammps-gui VERSION 1.5.10 LANGUAGES CXX) +project(lammps-gui VERSION 1.5.11 LANGUAGES CXX) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) From d2ca1fe354cb7b595f5fad45aaec07fefea97ccf Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 23:00:06 -0500 Subject: [PATCH 118/189] avoid that mliappy is initialized multiple times --- src/PYTHON/python_impl.cpp | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/PYTHON/python_impl.cpp b/src/PYTHON/python_impl.cpp index 02070ee151..0db468d701 100644 --- a/src/PYTHON/python_impl.cpp +++ b/src/PYTHON/python_impl.cpp @@ -17,6 +17,7 @@ #include "python_impl.h" +#include "comm.h" #include "error.h" #include "input.h" #include "memory.h" @@ -56,7 +57,6 @@ #endif #include "mliap_unified_couple_kokkos.h" - #endif #endif @@ -90,23 +90,27 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp) #endif #ifdef MLIAP_PYTHON - // Inform python intialization scheme of the mliappy module. - // This -must- happen before python is initialized. - int err = PyImport_AppendInittab("mliap_model_python_couple", PyInit_mliap_model_python_couple); - if (err) error->all(FLERR, "Could not register MLIAPPY embedded python module."); + // cannot register mliappy module a second time + if (!Py_IsInitialized()) { + // Inform python intialization scheme of the mliappy module. + // This -must- happen before python is initialized. + int err = PyImport_AppendInittab("mliap_model_python_couple", PyInit_mliap_model_python_couple); + if (err) error->all(FLERR, "Could not register MLIAPPY embedded python module."); + + err = PyImport_AppendInittab("mliap_unified_couple", PyInit_mliap_unified_couple); + if (err) error->all(FLERR, "Could not register MLIAPPY unified embedded python module."); - err = PyImport_AppendInittab("mliap_unified_couple", PyInit_mliap_unified_couple); - if (err) error->all(FLERR, "Could not register MLIAPPY unified embedded python module."); #ifdef LMP_KOKKOS - // Inform python intialization scheme of the mliappy module. - // This -must- happen before python is initialized. - err = PyImport_AppendInittab("mliap_model_python_couple_kokkos", PyInit_mliap_model_python_couple_kokkos); - if (err) error->all(FLERR, "Could not register MLIAPPY embedded python module."); - - err = PyImport_AppendInittab("mliap_unified_couple_kokkos", PyInit_mliap_unified_couple_kokkos); - if (err) error->all(FLERR, "Could not register MLIAPPY unified embedded python module."); + // Inform python intialization scheme of the mliappy module. + // This -must- happen before python is initialized. + err = PyImport_AppendInittab("mliap_model_python_couple_kokkos", + PyInit_mliap_model_python_couple_kokkos); + if (err) error->all(FLERR, "Could not register MLIAPPY embedded python KOKKOS module."); + err = PyImport_AppendInittab("mliap_unified_couple_kokkos", PyInit_mliap_unified_couple_kokkos); + if (err) error->all(FLERR, "Could not register MLIAPPY unified embedded python KOKKOS module."); #endif + } #endif #if PY_VERSION_HEX >= 0x030800f0 && !defined(Py_LIMITED_API) @@ -120,7 +124,7 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp) // With Python 3.7 this function is now called by Py_Initialize() // Deprecated since version 3.9, will be removed in version 3.11 #if PY_VERSION_HEX < 0x030700f0 - if (!PyEval_ThreadsInitialized()) { PyEval_InitThreads(); } + if (!PyEval_ThreadsInitialized()) PyEval_InitThreads(); #endif PyUtils::GIL lock; From 58ed034d7a92718f075e237076b88ff669cf022b Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Sun, 17 Dec 2023 15:42:10 -0600 Subject: [PATCH 119/189] Updated Developer unittest for debugging failed unit tests individually --- doc/src/Developer_unittest.rst | 66 ++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/doc/src/Developer_unittest.rst b/doc/src/Developer_unittest.rst index e311fcdfc5..b517ca1802 100644 --- a/doc/src/Developer_unittest.rst +++ b/doc/src/Developer_unittest.rst @@ -526,3 +526,69 @@ The ``unittest/tools`` folder contains tests for programs in the shell, which are implemented as a python scripts using the ``unittest`` Python module and launching the tool commands through the ``subprocess`` Python module. + + +Troubleshooting failed unit tests +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The unit tests of a newly added features (e.g. pair, fix or compute styles) +are not automatically run when your pull request (PR) is submitted via GitHub. +To trigger the unit test(s), you need to add appropriate labels the PR, e.g., +``gpu_unit_tests`` to enable the unit tests for the GPU package. After the test +has started, you had better remove the label since every PR activity will +retrigger the test. + +For a failed build on GitHub, click on Details to go to the LAMMPS Jenkins CI web page. +Click on the "Exit" symbol near the "Logout" button on the top right of that page +to go to the classic view. In the classic view, there is a list of the individual runs +that make up this test run. You can click on any of those. Then on "Test Result" +to display the list of tests. Click on the Status column to sort the tests +based on their Failed or Passed status. Then click on the failed test to expand +its output. + +For example, the following output snippet shows the failed unit test + +.. code-block:: bash + + [ RUN ] PairStyle.gpu + /home/builder/workspace/dev/pull_requests/ubuntu_gpu/unit_tests/cmake_gpu_opencl_mixed_smallbig_clang_static/unittest/force-styles/test_main.cpp:63: Failure + Expected: (err) <= (epsilon) + Actual: 0.00018957912910606503 vs 0.0001 + Google Test trace: + /home/builder/workspace/dev/pull_requests/ubuntu_gpu/unit_tests/cmake_gpu_opencl_mixed_smallbig_clang_static/unittest/force-styles/test_main.cpp:56: EXPECT_FORCES: init_forces (newton off) + /home/builder/workspace/dev/pull_requests/ubuntu_gpu/unit_tests/cmake_gpu_opencl_mixed_smallbig_clang_static/unittest/force-styles/test_main.cpp:64: Failure + Expected: (err) <= (epsilon) + Actual: 0.00022892713393549854 vs 0.0001 + + +Note that the force style engine runs the same system on a rather +off-equilibrium system with few atoms for just a few steps, writes data and restart, +uses ``clean`` to reset, and then run with different settings and integrators. +Beyond potential issues/bugs in the source code, the mismatch between the expected +and actual values could be that force arrays are not properly cleared between +multiple run commands and/or when starting a run from restarts. + +As a rule of thumb, the test epsilon can often be in the range 5e-14 to 1e-13. +For "noisy" force kernels, e.g. those involving `exp()`, `log()` or `sin()` +operations and subject to compiler optimization, epsilon can be further relaxed, +sometimes epsilon can be relaxed to 1.e-12. If lookup tables are added, +we may need to go to 1.e-10 or even higher. + +To rerun the failed unit test individually, change to the ``build`` directory +and run the test with verbose output. For example, + +.. code-block:: bash + + env TEST_ARGS=-v ctest -R ^MolPairStyle:lj_cut_coul_long -V + +It is recommended to configure the build with ``-D BUILD_SHARED_LIBS=on`` to +shorten the build time during recompilation. Installing `ccache` in +your development environment helps speed up recompilation by +caching previous compilations and detecting when the same compilation +is being done again. + + + + + + From 188e1090e9e37272144890a33479670cc592dde9 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 17 Dec 2023 20:01:47 -0500 Subject: [PATCH 120/189] add some corrections and clarifications --- doc/src/Build_development.rst | 41 +++++++++------- doc/src/Developer_unittest.rst | 90 ++++++++++++++++++++++------------ 2 files changed, 80 insertions(+), 51 deletions(-) diff --git a/doc/src/Build_development.rst b/doc/src/Build_development.rst index d30a433d62..c674b2c258 100644 --- a/doc/src/Build_development.rst +++ b/doc/src/Build_development.rst @@ -255,16 +255,18 @@ A test run is then a a collection multiple individual test runs each with many comparisons to reference results based on template input files, individual command settings, relative error margins, and reference data stored in a YAML format file with ``.yaml`` -suffix. Currently the programs ``test_pair_style``, ``test_bond_style``, and -``test_angle_style`` are implemented. They will compare forces, energies and -(global) stress for all atoms after a ``run 0`` calculation and after a -few steps of MD with :doc:`fix nve `, each in multiple variants -with different settings and also for multiple accelerated styles. If a -prerequisite style or package is missing, the individual tests are -skipped. All tests will be executed on a single MPI process, so using -the CMake option ``-D BUILD_MPI=off`` can significantly speed up testing, -since this will skip the MPI initialization for each test run. -Below is an example command and output: +suffix. Currently the programs ``test_pair_style``, ``test_bond_style``, +``test_angle_style``, ``test_dihedral_style``, and +``test_improper_style`` are implemented. They will compare forces, +energies and (global) stress for all atoms after a ``run 0`` calculation +and after a few steps of MD with :doc:`fix nve `, each in +multiple variants with different settings and also for multiple +accelerated styles. If a prerequisite style or package is missing, the +individual tests are skipped. All force style tests will be executed on +a single MPI process, so using the CMake option ``-D BUILD_MPI=off`` can +significantly speed up testing, since this will skip the MPI +initialization for each test run. Below is an example command and +output: .. code-block:: console @@ -416,15 +418,16 @@ When compiling LAMMPS with enabled tests, most test executables will need to be linked against the LAMMPS library. Since this can be a very large library with many C++ objects when many packages are enabled, link times can become very long on machines that use the GNU BFD linker (e.g. -Linux systems). Alternatives like the ``lld`` linker of the LLVM project -or the ``gold`` linker available with GNU binutils can speed up this step -substantially. CMake will by default test if any of the two can be -enabled and use it when ``ENABLE_TESTING`` is active. It can also be -selected manually through the ``CMAKE_CUSTOM_LINKER`` CMake variable. -Allowed values are ``lld``, ``gold``, ``bfd``, or ``default``. The -``default`` option will use the system default linker otherwise, the -linker is chosen explicitly. This option is only available for the -GNU or Clang C++ compiler. +Linux systems). Alternatives like the ``mold`` linker, the ``lld`` +linker of the LLVM project, or the ``gold`` linker available with GNU +binutils can speed up this step substantially (in this order). CMake +will by default test if any of the three can be enabled and use it when +``ENABLE_TESTING`` is active. It can also be selected manually through +the ``CMAKE_CUSTOM_LINKER`` CMake variable. Allowed values are +``mold``, ``lld``, ``gold``, ``bfd``, or ``default``. The ``default`` +option will use the system default linker otherwise, the linker is +chosen explicitly. This option is only available for the GNU or Clang +C++ compilers. Tests for other components and utility functions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/src/Developer_unittest.rst b/doc/src/Developer_unittest.rst index b517ca1802..c0cdee6c09 100644 --- a/doc/src/Developer_unittest.rst +++ b/doc/src/Developer_unittest.rst @@ -121,7 +121,7 @@ will be suppressed and only a summary printed, but adding the '-V' option will then produce output from the tests above like the following: -.. code-block:: +.. code-block:: console [...] 1: [ RUN ] Tokenizer.empty_string @@ -531,24 +531,33 @@ Python module. Troubleshooting failed unit tests ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The unit tests of a newly added features (e.g. pair, fix or compute styles) -are not automatically run when your pull request (PR) is submitted via GitHub. -To trigger the unit test(s), you need to add appropriate labels the PR, e.g., -``gpu_unit_tests`` to enable the unit tests for the GPU package. After the test -has started, you had better remove the label since every PR activity will -retrigger the test. +The are by default no unit tests for newly added features (e.g. pair, fix, +or compute styles) unless your pull request also includes tests for the +added features. If you are modifying some features, you may see failures +for existing tests, if your modifications have some unexpected side effects +or your changes render the existing text invalid. If you are adding an +accelerated version of an existing style, then only tests for INTEL, +KOKKOS (with OpenMP only), OPENMP, and OPT will be run automatically. +Tests for the GPU package are time consuming and thus are only run +*after* a merge, or when a special label, ``gpu_unit_tests`` is added +to the pull request. After the test has started, it is often best to +remove the label since every PR activity will re-trigger the test (that +is a limitation of triggering a test with a label). Support for unit +tests with using KOKKOS with GPU acceleration is currently not supported. -For a failed build on GitHub, click on Details to go to the LAMMPS Jenkins CI web page. -Click on the "Exit" symbol near the "Logout" button on the top right of that page -to go to the classic view. In the classic view, there is a list of the individual runs -that make up this test run. You can click on any of those. Then on "Test Result" -to display the list of tests. Click on the Status column to sort the tests -based on their Failed or Passed status. Then click on the failed test to expand -its output. +When you see a failed build on GitHub, click on ``Details`` to be taken +to the corresponding LAMMPS Jenkins CI web page. Click on the "Exit" +symbol near the ``Logout`` button on the top right of that page to go to +the "classic view". In the classic view, there is a list of the +individual runs that make up this test run (they are shown but cannot be +inspected in the default view). You can click on any of those. +Clicking on ``Test Result`` will display the list of failed tests. Click +on the "Status" column to sort the tests based on their Failed or Passed +status. Then click on the failed test to expand its output. For example, the following output snippet shows the failed unit test -.. code-block:: bash +.. code-block:: console [ RUN ] PairStyle.gpu /home/builder/workspace/dev/pull_requests/ubuntu_gpu/unit_tests/cmake_gpu_opencl_mixed_smallbig_clang_static/unittest/force-styles/test_main.cpp:63: Failure @@ -560,19 +569,35 @@ For example, the following output snippet shows the failed unit test Expected: (err) <= (epsilon) Actual: 0.00022892713393549854 vs 0.0001 +Note that the force style engine runs one of a small number of systems +in a rather off-equilibrium configuration with a few atoms for a few +steps, writes data and restart files, uses :doc:`the clear command +` to reset LAMMPS, and then runs from those files with different +settings (e.g. newton on/off) and integrators (e.g. verlet vs. respa). +Beyond potential issues/bugs in the source code, the mismatch between +the expected and actual values could be that force arrays are not +properly cleared between multiple run commands or that class members are +not correctly initialized or written to or read from a data or restart +file. -Note that the force style engine runs the same system on a rather -off-equilibrium system with few atoms for just a few steps, writes data and restart, -uses ``clean`` to reset, and then run with different settings and integrators. -Beyond potential issues/bugs in the source code, the mismatch between the expected -and actual values could be that force arrays are not properly cleared between -multiple run commands and/or when starting a run from restarts. - -As a rule of thumb, the test epsilon can often be in the range 5e-14 to 1e-13. -For "noisy" force kernels, e.g. those involving `exp()`, `log()` or `sin()` -operations and subject to compiler optimization, epsilon can be further relaxed, -sometimes epsilon can be relaxed to 1.e-12. If lookup tables are added, -we may need to go to 1.e-10 or even higher. +While the epsilon (relative precision) for a single, `IEEE 754 compliant +`_, double precision floating +point operation is at about 2.2e-16, the achievable precision for the +tests is lower due to most numbers being sums over intermediate results +and the non-associativity of floating point math leading to larger +errors. In some cases specific properties of the tested style. As a +rule of thumb, the test epsilon can often be in the range 5.0e-14 to +1.0e-13. But for "noisy" force kernels, e.g. those a larger amount of +arithmetic operations involving `exp()`, `log()` or `sin()` functions, +and also due to the effect of compiler optimization or differences +between compilers or platforms, epsilon may need to be further relaxed, +sometimes epsilon can be relaxed to 1.0e-12. If interpolation or lookup +tables are used, epsilon may need to be set to 1.0e-10 or even higher. +For tests of accelerated styles, the per-test epsilon is multiplied +by empirical factors that take into account the differences in the order +of floating point operations or that some or most intermediate operations +may be done using approximations or with single precision floating point +math. To rerun the failed unit test individually, change to the ``build`` directory and run the test with verbose output. For example, @@ -581,11 +606,12 @@ and run the test with verbose output. For example, env TEST_ARGS=-v ctest -R ^MolPairStyle:lj_cut_coul_long -V -It is recommended to configure the build with ``-D BUILD_SHARED_LIBS=on`` to -shorten the build time during recompilation. Installing `ccache` in -your development environment helps speed up recompilation by -caching previous compilations and detecting when the same compilation -is being done again. +It is recommended to configure the build with ``-D +BUILD_SHARED_LIBS=on`` and use a custom linker to shorten the build time +during recompilation. Installing `ccache` in your development +environment helps speed up recompilation by caching previous +compilations and detecting when the same compilation is being done +again. Please see :doc:`Build_development` for further details. From 4c34bc2b9b5aa9d13f4e42660dd065ea4facdd88 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 17 Dec 2023 20:25:39 -0500 Subject: [PATCH 121/189] upgrade CodeQL GitHub action scripts to latest version --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 00c0e8642d..c7dd945f5f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: python-version: '3.x' - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/${{ matrix.language }}.yml @@ -55,4 +55,4 @@ jobs: cmake --build . --parallel 2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 From 94b62fa98baccb1b7a68f65ab09eff0093927411 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 17 Dec 2023 21:22:33 -0500 Subject: [PATCH 122/189] tweak epsilon --- unittest/force-styles/tests/mol-pair-lj_cut_coul_cut_soft.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittest/force-styles/tests/mol-pair-lj_cut_coul_cut_soft.yaml b/unittest/force-styles/tests/mol-pair-lj_cut_coul_cut_soft.yaml index e242a56029..485730531f 100644 --- a/unittest/force-styles/tests/mol-pair-lj_cut_coul_cut_soft.yaml +++ b/unittest/force-styles/tests/mol-pair-lj_cut_coul_cut_soft.yaml @@ -1,7 +1,7 @@ --- lammps_version: 17 Feb 2022 date_generated: Fri Mar 18 22:17:31 2022 -epsilon: 2e-13 +epsilon: 5e-13 skip_tests: prerequisites: ! | atom full From ba4ac991b6b62ef1feacb016554915145c4696c5 Mon Sep 17 00:00:00 2001 From: oywg11 Date: Mon, 18 Dec 2023 16:06:36 +0800 Subject: [PATCH 123/189] small fix for ilp/tmd and KC/full --- src/INTERLAYER/pair_ilp_tmd.cpp | 2 +- src/INTERLAYER/pair_kolmogorov_crespi_full.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/INTERLAYER/pair_ilp_tmd.cpp b/src/INTERLAYER/pair_ilp_tmd.cpp index 8b08de39c0..73f89803c2 100644 --- a/src/INTERLAYER/pair_ilp_tmd.cpp +++ b/src/INTERLAYER/pair_ilp_tmd.cpp @@ -210,7 +210,7 @@ void PairILPTMD::calc_FRep(int eflag, int /* vflag */) delki[1] = x[k][1] - x[i][1]; delki[2] = x[k][2] - x[i][2]; if (evflag) - ev_tally_xyz(k, j, nlocal, newton_pair, 0.0, 0.0, fk[0], fk[1], fk[2], delki[0], + ev_tally_xyz(k, i, nlocal, newton_pair, 0.0, 0.0, fk[0], fk[1], fk[2], delki[0], delki[1], delki[2]); } diff --git a/src/INTERLAYER/pair_kolmogorov_crespi_full.cpp b/src/INTERLAYER/pair_kolmogorov_crespi_full.cpp index b497ae3568..ad42ba1922 100644 --- a/src/INTERLAYER/pair_kolmogorov_crespi_full.cpp +++ b/src/INTERLAYER/pair_kolmogorov_crespi_full.cpp @@ -590,7 +590,7 @@ void PairKolmogorovCrespiFull::calc_FRep(int eflag, int /* vflag */) delki[1] = x[k][1] - x[i][1]; delki[2] = x[k][2] - x[i][2]; if (evflag) - ev_tally_xyz(k, j, nlocal, newton_pair, 0.0, 0.0, fk[0], fk[1], fk[2], delki[0], + ev_tally_xyz(k, i, nlocal, newton_pair, 0.0, 0.0, fk[0], fk[1], fk[2], delki[0], delki[1], delki[2]); } From 8dcf980d0abc2418d8f21f1ff6f6ac7451237e6d Mon Sep 17 00:00:00 2001 From: oywg11 Date: Mon, 18 Dec 2023 16:55:10 +0800 Subject: [PATCH 124/189] update doc of ilp/tmd --- doc/src/pair_ilp_tmd.rst | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/doc/src/pair_ilp_tmd.rst b/doc/src/pair_ilp_tmd.rst index 70a4768389..dcacc18ed6 100644 --- a/doc/src/pair_ilp_tmd.rst +++ b/doc/src/pair_ilp_tmd.rst @@ -22,12 +22,12 @@ Examples .. code-block:: LAMMPS pair_style hybrid/overlay ilp/tmd 16.0 1 - pair_coeff * * ilp/tmd MoS2.ILP Mo S S + pair_coeff * * ilp/tmd TMD.ILP Mo S S pair_style hybrid/overlay sw/mod sw/mod ilp/tmd 16.0 pair_coeff * * sw/mod 1 tmd.sw.mod Mo S S NULL NULL NULL - pair_coeff * * sw/mod 2 tmd.sw.mod NULL NULL NULL Mo S S - pair_coeff * * ilp/tmd MoS2.ILP Mo S S Mo S S + pair_coeff * * sw/mod 2 tmd.sw.mod NULL NULL NULL W Se Se + pair_coeff * * ilp/tmd TMD.ILP Mo S S W Se Se Description """"""""""" @@ -36,7 +36,7 @@ Description The *ilp/tmd* style computes the registry-dependent interlayer potential (ILP) potential for transition metal dichalcogenides (TMD) -as described in :ref:`(Ouyang7) `. +as described in :ref:`(Ouyang7) ` and ref:`(Jiang) `. .. math:: @@ -69,7 +69,7 @@ calculating the normals. each atom `i`, its six nearest neighboring atoms belonging to the same sub-layer are chosen to define the normal vector `{\bf n}_i`. -The parameter file (e.g. MoS2.ILP), is intended for use with *metal* +The parameter file (e.g. TMD.ILP), is intended for use with *metal* :doc:`units `, with energies in meV. Two additional parameters, *S*, and *rcut* are included in the parameter file. *S* is designed to facilitate scaling of energies. *rcut* is designed to build the neighbor @@ -77,7 +77,7 @@ list for calculating the normals for each atom pair. .. note:: - The parameters presented in the parameter file (e.g. MoS2.ILP), + The parameters presented in the parameter file (e.g. TMD.ILP), are fitted with taper function by setting the cutoff equal to 16.0 Angstrom. Using different cutoff or taper function should be careful. These parameters provide a good description in both short- and long-range @@ -133,10 +133,10 @@ if LAMMPS was built with that package. See the :doc:`Build package This pair style requires the newton setting to be *on* for pair interactions. -The MoS2.ILP potential file provided with LAMMPS (see the potentials +The TMD.ILP potential file provided with LAMMPS (see the potentials directory) are parameterized for *metal* units. You can use this potential with any LAMMPS units, but you would need to create your own -custom MoS2.ILP potential file with coefficients listed in the appropriate +custom TMD.ILP potential file with coefficients listed in the appropriate units, if your simulation does not use *metal* units. Related commands @@ -164,3 +164,7 @@ tap_flag = 1 .. _Ouyang7: **(Ouyang7)** W. Ouyang, et al., J. Chem. Theory Comput. 17, 7237 (2021). + +.. _Jiang: + +**(Jiang)** W. Jiang, et al., J. Phys. Chem. A, 127, 46, 9820–9830 (2023). From 20183ac9cc9680c608cb230a61c9474b3db8174b Mon Sep 17 00:00:00 2001 From: oywg11 Date: Mon, 18 Dec 2023 16:56:24 +0800 Subject: [PATCH 125/189] update potential file for ilp/tmd --- potentials/TMD.ILP | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 potentials/TMD.ILP diff --git a/potentials/TMD.ILP b/potentials/TMD.ILP new file mode 100644 index 0000000000..e7a9cbe558 --- /dev/null +++ b/potentials/TMD.ILP @@ -0,0 +1,25 @@ +# DATE: 2021-12-02 UNITS: metal CONTRIBUTOR: Wengen Ouyang w.g.ouyang@gmail.com +# CITATION: W. Ouyang, et al., J. Chem. Theory Comput. 17, 7237 (2021). +# CITATION: W. Jiang, et al., J. Phys. Chem. A, 127, 46, 9820–9830 (2023). +# Interlayer Potential (ILP) for bilayer and bulk Group-VI Transition Metal Dichalcogenides. +# The parameters below are fitted against the HSE + MBD-NL DFT reference data. +# +# -------------------- Repulsion Potential -------------------++++++++++++++++ Vdw Potential ++++++++++++++++********* +# beta(A) alpha delta(A) epsilon(meV) C(meV) d sR reff(A) C6(meV*A^6) S rcut +Mo Mo 5.579450 9.377662 2.027222 144.151775 97.978570 89.437597 2.059031 5.122055 491850.316195 1.0 4.0 +W W 5.530854 6.624992 1.983208 0.271792 140.174059 107.392585 1.356333 4.437591 691850.243962 1.0 4.0 +S S 3.161402 8.093263 1.953140 4.586764 118.065466 58.809416 0.215367 4.299600 148811.243409 1.0 4.0 +Se Se 3.938627 10.515924 2.415783 3.012583 22.400612 116.864517 0.151121 5.884241 112506.195626 1.0 4.0 +Mo W 5.412298 8.647128 2.108665 51.177950 184.342860 201.281256 2.547743 2.492287 99996.913401 1.0 4.0 +Mo S 3.627152 19.971375 7.585031 76.101931 3.317496 45.720328 0.947470 4.410425 150597.857716 1.0 4.0 +Mo Se 6.196447 4.844134 14.362005 7.407221 0.058823 27.156223 0.976771 3.979186 786029.840651 1.0 4.0 +W S 3.680136 11.163004 32.254117 110.019679 79.381335 138.340438 0.900750 8.875776 250600.809034 1.0 4.0 +W Se 3.559392 20.638856 1.202717 20.478669 197.422484 10.005271 1.052738 3.815817 288321.561114 1.0 4.0 +S Se 2.820092 7.491151 1.933323 141.532559 293.127817 90.470904 0.390492 4.170885 117688.987069 1.0 4.0 +# Symmetric Atom Pair +W Mo 5.412298 8.647128 2.108665 51.177950 184.342860 201.281256 2.547743 2.492287 99996.913401 1.0 4.0 +S Mo 3.627152 19.971375 7.585031 76.101931 3.317496 45.720328 0.947470 4.410425 150597.857716 1.0 4.0 +Se Mo 6.196447 4.844134 14.362005 7.407221 0.058823 27.156223 0.976771 3.979186 786029.840651 1.0 4.0 +S W 3.680136 11.163004 32.254117 110.019679 79.381335 138.340438 0.900750 8.875776 250600.809034 1.0 4.0 +Se W 3.559392 20.638856 1.202717 20.478669 197.422484 10.005271 1.052738 3.815817 288321.561114 1.0 4.0 +Se S 2.820092 7.491151 1.933323 141.532559 293.127817 90.470904 0.390492 4.170885 117688.987069 1.0 4.0 From 40f27eb7cf6cecc39be6090b07e66d96e7f68263 Mon Sep 17 00:00:00 2001 From: oywg11 Date: Mon, 18 Dec 2023 17:36:04 +0800 Subject: [PATCH 126/189] update doc file for ilp/tmd --- doc/src/pair_ilp_tmd.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/pair_ilp_tmd.rst b/doc/src/pair_ilp_tmd.rst index dcacc18ed6..b5d9ad60b5 100644 --- a/doc/src/pair_ilp_tmd.rst +++ b/doc/src/pair_ilp_tmd.rst @@ -36,7 +36,7 @@ Description The *ilp/tmd* style computes the registry-dependent interlayer potential (ILP) potential for transition metal dichalcogenides (TMD) -as described in :ref:`(Ouyang7) ` and ref:`(Jiang) `. +as described in :ref:`(Ouyang7) ` and :ref:`(Jiang) `. .. math:: From c0c64e812fabd092400fc08039cc56b7147aa936 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Mon, 18 Dec 2023 11:29:42 -0700 Subject: [PATCH 127/189] Enable default threading over neighbors for half list --- src/KOKKOS/pair_kokkos.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/KOKKOS/pair_kokkos.h b/src/KOKKOS/pair_kokkos.h index c0b81cc594..a160f84d95 100644 --- a/src/KOKKOS/pair_kokkos.h +++ b/src/KOKKOS/pair_kokkos.h @@ -933,8 +933,9 @@ EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t<(NEIGHFLAG&P EV_FLOAT ev; if (!fpair->lmp->kokkos->neigh_thread_set) - if (list->inum <= 16384 && NEIGHFLAG == FULL) - fpair->lmp->kokkos->neigh_thread = 1; + if (list->inum <= 16384) + if (NEIGHFLAG == FULL || !fpair->newton_pair) + fpair->lmp->kokkos->neigh_thread = 1; if (fpair->lmp->kokkos->neigh_thread) { From 3237c30117938f4a49bf7abac1ea0b0c1f54ae28 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 18 Dec 2023 15:40:14 -0500 Subject: [PATCH 128/189] reorganize the LAMMPS version and development description --- doc/github-development-workflow.md | 8 +-- doc/src/Howto_github.rst | 10 ++-- doc/src/Install_git.rst | 20 +++---- doc/src/Manual_version.rst | 91 +++++++++++++++++++----------- 4 files changed, 77 insertions(+), 52 deletions(-) diff --git a/doc/github-development-workflow.md b/doc/github-development-workflow.md index fccd75d29a..e16ae82764 100644 --- a/doc/github-development-workflow.md +++ b/doc/github-development-workflow.md @@ -36,10 +36,10 @@ requests. MUST be submitted as a pull request to GitHub. All changes to the "develop" branch must be made exclusively through merging pull requests. The "release" and "stable" branches, respectively, are only to be -updated upon feature or stable releases based on the associated -tags. Updates to the stable release in between stable releases +updated upon "feature releases" or "stable releases" based on the +associated tags. Updates to the stable release in between stable releases (for example, back-ported bug fixes) are first merged into the "maintenance" -branch and then into the "stable" branch as update releases. +branch and then into the "stable" branch as "stable update releases". Pull requests may also be submitted to (long-running) feature branches created by LAMMPS developers inside the LAMMPS project, if needed. Those @@ -131,7 +131,7 @@ testing -- that the code in the branch "develop" does not get easily broken. These tests are run after every update to a pull request. More extensive and time-consuming tests (including regression testing) are performed after code is merged to the "develop" branch. There are feature -releases of LAMMPS made about every 4-6 weeks at a point, when the LAMMPS +releases of LAMMPS made about every 4-8 weeks at a point, when the LAMMPS developers feel, that a sufficient number of changes have been included and all post-merge testing has been successful. These feature releases are marked with a `patch_` tag and the "release" branch diff --git a/doc/src/Howto_github.rst b/doc/src/Howto_github.rst index cbe1264d52..b81716c09d 100644 --- a/doc/src/Howto_github.rst +++ b/doc/src/Howto_github.rst @@ -480,11 +480,11 @@ Some recent changes to the workflow are not captured in this tutorial. For example, in addition to the *develop* branch, to which all new features should be submitted, there is also a *release*, a *stable*, and a *maintenance* branch; the *release* branch is updated from the -*develop* as part of a feature release, and *stable* (together with -*release*) are updated from *develop* when a stable release is made. In -between stable releases, selected bug fixes and infrastructure updates -are back-ported from the *develop* branch to the *maintenance* branch -and occasionally merged to *stable* as an update release. +*develop* branch as part of a "feature release", and *stable* (together +with *release*) are updated from *develop* when a "stable release" is +made. In between stable releases, selected bug fixes and infrastructure +updates are back-ported from the *develop* branch to the *maintenance* +branch and occasionally merged to *stable* as an update release. Furthermore, the naming of the release tags now follow the pattern "patch_" to simplify comparisons between releases. diff --git a/doc/src/Install_git.rst b/doc/src/Install_git.rst index b6d3ced0a5..45e364a226 100644 --- a/doc/src/Install_git.rst +++ b/doc/src/Install_git.rst @@ -28,16 +28,16 @@ provides `limited support for subversion clients `_. You can follow the LAMMPS development on 4 different git branches: -* **release** : this branch is updated with every patch or feature release; - updates are always "fast-forward" merges from *develop* -* **develop** : this branch follows the ongoing development and - is updated with every merge commit of a pull request -* **stable** : this branch is updated from the *release* branch with - every stable release version and also has selected bug fixes with every - update release when the *maintenance* branch is merged into it -* **maintenance** : this branch collects back-ported bug fixes from the - *develop* branch to the *stable* branch. It is used to update *stable* - for update releases and it synchronized with *stable* at each stable release. +* **develop** : this branch follows the ongoing development and is + updated with every merge commit of a pull request +* **release** : this branch is updated with every "feature release"; + updates are always "fast-forward" merges from *develop* +* **maintenance** : this branch collects back-ported bug fixes from the + *develop* branch to the *stable* branch. It is used to update the + *stable* branch for "stable update releases". +* **stable** : this branch is updated from the *release* branch with + every "stable release" version and also has selected bug fixes with + every "update release" when the *maintenance* branch is merged into it To access the git repositories on your box, use the clone command to create a local copy of the LAMMPS repository with a command like: diff --git a/doc/src/Manual_version.rst b/doc/src/Manual_version.rst index 8fb28fef84..f123267acf 100644 --- a/doc/src/Manual_version.rst +++ b/doc/src/Manual_version.rst @@ -3,45 +3,25 @@ What does a LAMMPS version mean The LAMMPS "version" is the date when it was released, such as 1 May 2014. LAMMPS is updated continuously, and we aim to keep it working -correctly and reliably at all times. You can follow its development -in a public `git repository on GitHub `_. - -Modifications of the LAMMPS source code (like bug fixes, code refactors, -updates to existing features, or addition of new features) are organized -into pull requests. Pull requests will be merged into the *develop* -branch of the git repository after they pass automated testing and code -review by the LAMMPS developers. When a sufficient number of changes -have accumulated *and* the *develop* branch version passes an extended -set of automated tests, we release it as a *feature release*, which are -currently made every 4 to 8 weeks. The *release* branch of the git -repository is updated with every such release. A summary of the most -important changes of the patch releases are on `this website page -`_. More detailed release notes are -`available on GitHub `_. - -Once or twice a year, we have a "stabilization period" where we apply -only bug fixes and small, non-intrusive changes to the *develop* -branch. At the same time, the code is subjected to more detailed and -thorough manual testing than the default automated testing. Also, -several variants of static code analysis are run to improve the overall -code quality, consistency, and compliance with programming standards, -best practices and style conventions. - -The release after such a stabilization period is called a *stable* -version and both, the *release* and the *stable* branches are updated -with it. Between stable releases, we collect back-ported bug fixes and -updates from the *develop* branch in the *maintenance* branch. From the -*maintenance* branch we make occasional update releases and update the -*stable* branch accordingly. +correctly and reliably at all times. Also, several variants of static +code analysis are run regularly to maintain or improve the overall code +quality, consistency, and compliance with programming standards, best +practices and style conventions. You can follow its development in a +public `git repository on GitHub `_. Each version of LAMMPS contains all the documented *features* up to and including its version date. For recently added features, we add markers to the documentation at which specific LAMMPS version a feature or keyword was added or significantly changed. -The version date is printed to the screen and log file every time you run -LAMMPS. It is also in the file src/version.h and in the LAMMPS -directory name created when you unpack a tarball. And it is on the +Identifying the Version +^^^^^^^^^^^^^^^^^^^^^^^ + +The version date is printed to the screen and log file every time you +run LAMMPS. There also is an indication, if a LAMMPS binary was +compiled from version with modifications **after** a release. +It is also visible in the file src/version.h and in the LAMMPS directory +name created when you unpack a downloaded tarball. And it is on the first page of the :doc:`manual `. * If you browse the HTML pages of the online version of the LAMMPS @@ -53,3 +33,48 @@ first page of the :doc:`manual `. * If you browse the HTML pages included in your downloaded tarball, they describe the version you have, which may be older than the online version. + +Development +^^^^^^^^^^^ + +Modifications of the LAMMPS source code (like bug fixes, code +refactoring, updates to existing features, or addition of new features) +are organized into pull requests. Pull requests will be merged into the +*develop* branch of the git repository after they pass automated testing +and code review by the LAMMPS developers. + +Feature Releases +^^^^^^^^^^^^^^^^ + +When a sufficient number of new features and updates have accumulated +*and* the LAMMPS version on the *develop* branch passes an extended set +of automated tests, we release it as a *feature release*, which are +currently made every 4 to 8 weeks. The *release* branch of the git +repository is updated with every such *feature release* and a tag in the +format ``patch_1May2014`` is added. A summary of the most important +changes of these releases for the current year are posted on `this +website page `_. More detailed release +notes are `available on GitHub +`_. + +Stable Releases +^^^^^^^^^^^^^^^ + +About once a year, we release a *stable release* version of LAMMPS. +This is done after a "stabilization period" where we apply only bug +fixes and small, non-intrusive changes to the *develop* branch but no +new features. At the same time, the code is subjected to more detailed +and thorough manual testing than the default automated testing. +After such a *stable release*, both the *release* and the *stable* +branches are updated and two tags are applied, a ``patch_1May2014`` format +and a ``stable_1May2014`` format tag. + +Stable Release Updates +^^^^^^^^^^^^^^^^^^^^^^ + +Between *stable releases*, we collect bug fixes and updates back-ported +from the *develop* branch in a branch called *maintenance*. From the +*maintenance* branch we make occasional *stable update releases* and +update the *stable* branch accordingly. The first update to the +``stable_1May2014`` release would be tagged as +``stable_1May2014_update1``. These updates contain no new features. From af222711aedc10d95baa35541e222d40b94c7605 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Mon, 18 Dec 2023 15:52:30 -0600 Subject: [PATCH 129/189] Added text to explain the output of ctest -V a bit more --- doc/src/Developer_unittest.rst | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/doc/src/Developer_unittest.rst b/doc/src/Developer_unittest.rst index c0cdee6c09..5cb7a0ebde 100644 --- a/doc/src/Developer_unittest.rst +++ b/doc/src/Developer_unittest.rst @@ -569,6 +569,10 @@ For example, the following output snippet shows the failed unit test Expected: (err) <= (epsilon) Actual: 0.00022892713393549854 vs 0.0001 +The failed assertions provide line numbers in the test source +(e.g. ``test_main.cpp:56``), from which one can understand what +specific assertion failed. + Note that the force style engine runs one of a small number of systems in a rather off-equilibrium configuration with a few atoms for a few steps, writes data and restart files, uses :doc:`the clear command @@ -606,15 +610,18 @@ and run the test with verbose output. For example, env TEST_ARGS=-v ctest -R ^MolPairStyle:lj_cut_coul_long -V +``ctest`` with the ``-V`` flag also shows the exact command line +of the test. One can then use ``gdb --args`` to furthere debug and +catch exceptions with the test command, for example, + +.. code-block:: bash + + gdb --args /path/to/lammps/build/test_pair_style "/path/to/lammps/unittest/force-styles/tests/mol-pair-lj_cut_coul_long.yaml" + + It is recommended to configure the build with ``-D BUILD_SHARED_LIBS=on`` and use a custom linker to shorten the build time during recompilation. Installing `ccache` in your development environment helps speed up recompilation by caching previous compilations and detecting when the same compilation is being done again. Please see :doc:`Build_development` for further details. - - - - - - From 8e4871c5e1d5527a0e10ac2a30e5048a14db5e55 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Mon, 18 Dec 2023 16:37:31 -0700 Subject: [PATCH 130/189] Tune team params --- src/KOKKOS/pair_kokkos.h | 61 +++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/src/KOKKOS/pair_kokkos.h b/src/KOKKOS/pair_kokkos.h index a160f84d95..bd543bbbba 100644 --- a/src/KOKKOS/pair_kokkos.h +++ b/src/KOKKOS/pair_kokkos.h @@ -19,10 +19,12 @@ #ifndef LMP_PAIR_KOKKOS_H #define LMP_PAIR_KOKKOS_H -#include "Kokkos_Macros.hpp" #include "pair.h" // IWYU pragma: export #include "neighbor_kokkos.h" #include "neigh_list_kokkos.h" +#include "math_special.h" +#include "update.h" +#include "Kokkos_Macros.hpp" #include "Kokkos_ScatterView.hpp" namespace LAMMPS_NS { @@ -907,24 +909,21 @@ EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t -int GetTeamSize(FunctorStyle& KOKKOS_GPU_ARG(functor), int KOKKOS_GPU_ARG(inum), - int KOKKOS_GPU_ARG(reduce_flag), int team_size, int KOKKOS_GPU_ARG(vector_length)) { +template +int GetMaxNeighs(NeighStyle* list) +{ + auto d_ilist = list->d_ilist; + auto d_numneigh = list->d_numneigh; + int inum = list->inum; -#ifdef LMP_KOKKOS_GPU - int team_size_max; + int maxneigh = 0; + Kokkos::parallel_reduce(inum, LAMMPS_LAMBDA(const int ii, int &maxneigh) { + const int i = d_ilist[ii]; + const int num_neighs = d_numneigh[i]; + maxneigh = MAX(maxneigh,num_neighs); + }, Kokkos::Max(maxneigh)); - if (reduce_flag) - team_size_max = Kokkos::TeamPolicy(inum,Kokkos::AUTO).team_size_max(functor,Kokkos::ParallelReduceTag()); - else - team_size_max = Kokkos::TeamPolicy(inum,Kokkos::AUTO).team_size_max(functor,Kokkos::ParallelForTag()); - - if (team_size*vector_length > team_size_max) - team_size = team_size_max/vector_length; -#else - team_size = 1; -#endif - return team_size; + return maxneigh; } // Submit ParallelFor for NEIGHFLAG=HALF,HALFTHREAD,FULL @@ -939,19 +938,35 @@ EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t<(NEIGHFLAG&P if (fpair->lmp->kokkos->neigh_thread) { - int vector_length = 8; - int atoms_per_team = 32; + static int vectorsize = 0; + static int lastcall = -1; + +#if defined(LMP_KOKKOS_GPU) + + #if defined(KOKKOS_ENABLE_HIP) + int max_vectorsize = 64; + #else + int max_vectorsize = 32; + #endif + + if (!vectorsize || lastcall < fpair->lmp->neighbor->lastcall) { + lastcall = fpair->lmp->update->ntimestep; + vectorsize = GetMaxNeighs(list); + vectorsize = MathSpecial::powint(2,int(log2(vectorsize))); // round down to nearest power of 2 + vectorsize = MIN(vectorsize,max_vectorsize); + } +#else + vectorsize = 1; +#endif if (fpair->atom->ntypes > MAX_TYPES_STACKPARAMS) { PairComputeFunctor ff(fpair,list); - atoms_per_team = GetTeamSize(ff, list->inum, (fpair->eflag || fpair->vflag), atoms_per_team, vector_length); - Kokkos::TeamPolicy > policy(list->inum,atoms_per_team,vector_length); + Kokkos::TeamPolicy > policy(list->inum,Kokkos::AUTO(),vectorsize); if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(policy,ff,ev); else Kokkos::parallel_for(policy,ff); } else { PairComputeFunctor ff(fpair,list); - atoms_per_team = GetTeamSize(ff, list->inum, (fpair->eflag || fpair->vflag), atoms_per_team, vector_length); - Kokkos::TeamPolicy > policy(list->inum,atoms_per_team,vector_length); + Kokkos::TeamPolicy > policy(list->inum,Kokkos::AUTO(),vectorsize); if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(policy,ff,ev); else Kokkos::parallel_for(policy,ff); } From 6c798412b459079b1d0275b3147c480d743b17ed Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 18 Dec 2023 18:52:42 -0500 Subject: [PATCH 131/189] fix typo and remove unnecessary quotes --- doc/src/Developer_unittest.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/Developer_unittest.rst b/doc/src/Developer_unittest.rst index 5cb7a0ebde..b48c3b4e17 100644 --- a/doc/src/Developer_unittest.rst +++ b/doc/src/Developer_unittest.rst @@ -611,12 +611,12 @@ and run the test with verbose output. For example, env TEST_ARGS=-v ctest -R ^MolPairStyle:lj_cut_coul_long -V ``ctest`` with the ``-V`` flag also shows the exact command line -of the test. One can then use ``gdb --args`` to furthere debug and +of the test. One can then use ``gdb --args`` to further debug and catch exceptions with the test command, for example, .. code-block:: bash - gdb --args /path/to/lammps/build/test_pair_style "/path/to/lammps/unittest/force-styles/tests/mol-pair-lj_cut_coul_long.yaml" + gdb --args /path/to/lammps/build/test_pair_style /path/to/lammps/unittest/force-styles/tests/mol-pair-lj_cut_coul_long.yaml It is recommended to configure the build with ``-D From e98df7018b5e73fa86bfb2ffce1077d5781fb3e7 Mon Sep 17 00:00:00 2001 From: oywg11 Date: Tue, 19 Dec 2023 08:17:43 +0800 Subject: [PATCH 132/189] update the example files --- examples/PACKAGES/interlayer/ilp_tmds/MoS2.ILP | 1 - examples/PACKAGES/interlayer/ilp_tmds/TMD.ILP | 1 + examples/PACKAGES/interlayer/ilp_tmds/in.mos2 | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) delete mode 120000 examples/PACKAGES/interlayer/ilp_tmds/MoS2.ILP create mode 120000 examples/PACKAGES/interlayer/ilp_tmds/TMD.ILP diff --git a/examples/PACKAGES/interlayer/ilp_tmds/MoS2.ILP b/examples/PACKAGES/interlayer/ilp_tmds/MoS2.ILP deleted file mode 120000 index 75dd894eef..0000000000 --- a/examples/PACKAGES/interlayer/ilp_tmds/MoS2.ILP +++ /dev/null @@ -1 +0,0 @@ -../../../../potentials/MoS2.ILP \ No newline at end of file diff --git a/examples/PACKAGES/interlayer/ilp_tmds/TMD.ILP b/examples/PACKAGES/interlayer/ilp_tmds/TMD.ILP new file mode 120000 index 0000000000..70f7ea18df --- /dev/null +++ b/examples/PACKAGES/interlayer/ilp_tmds/TMD.ILP @@ -0,0 +1 @@ +../../../../potentials/TMD.ILP \ No newline at end of file diff --git a/examples/PACKAGES/interlayer/ilp_tmds/in.mos2 b/examples/PACKAGES/interlayer/ilp_tmds/in.mos2 index b77f2fe719..0db4ec12d5 100644 --- a/examples/PACKAGES/interlayer/ilp_tmds/in.mos2 +++ b/examples/PACKAGES/interlayer/ilp_tmds/in.mos2 @@ -12,7 +12,7 @@ mass 4 95.94 pair_style hybrid/overlay sw/mod sw/mod ilp/tmd 16.0 pair_coeff * * sw/mod 1 tmd.sw.mod Mo S S NULL NULL NULL pair_coeff * * sw/mod 2 tmd.sw.mod NULL NULL NULL Mo S S -pair_coeff * * ilp/tmd MoS2.ILP Mo S S Mo S S +pair_coeff * * ilp/tmd TMD.ILP Mo S S Mo S S # Calculate the pair potential compute 0 all pair ilp/tmd From ab29200c60ad807ed3c7415738eb8c62737a3d50 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Tue, 19 Dec 2023 10:26:52 -0700 Subject: [PATCH 133/189] More tuning --- src/KOKKOS/pair_kokkos.h | 41 +++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/src/KOKKOS/pair_kokkos.h b/src/KOKKOS/pair_kokkos.h index bd543bbbba..55fe01f6d2 100644 --- a/src/KOKKOS/pair_kokkos.h +++ b/src/KOKKOS/pair_kokkos.h @@ -65,6 +65,7 @@ struct PairComputeFunctor { typename AT::t_f_array f; typename AT::t_efloat_1d d_eatom; typename AT::t_virial_array d_vatom; + int inum; using KKDeviceType = typename KKDevice::value; using DUP = NeedDup_v; @@ -97,6 +98,7 @@ struct PairComputeFunctor { dup_f = Kokkos::Experimental::create_scatter_view(c.f); dup_eatom = Kokkos::Experimental::create_scatter_view(c.d_eatom); dup_vatom = Kokkos::Experimental::create_scatter_view(c.d_vatom); + inum = list.inum; }; // Set copymode = 1 so parent allocations aren't destructed by copies of the style @@ -287,7 +289,6 @@ struct PairComputeFunctor { auto a_f = dup_f.template access::value>(); - const int inum = team.league_size(); const int atoms_per_team = team.team_size(); const int firstatom = team.league_rank()*atoms_per_team; const int lastatom = firstatom + atoms_per_team < inum ? firstatom + atoms_per_team : inum; @@ -364,13 +365,11 @@ struct PairComputeFunctor { auto a_f = dup_f.template access::value>(); - const int inum = team.league_size(); const int atoms_per_team = team.team_size(); int firstatom = team.league_rank()*atoms_per_team; int lastatom = firstatom + atoms_per_team < inum ? firstatom + atoms_per_team : inum; Kokkos::parallel_for(Kokkos::TeamThreadRange(team, firstatom, lastatom), [&] (const int &ii) { - const int i = list.d_ilist[ii]; const X_FLOAT xtmp = c.x(i,0); const X_FLOAT ytmp = c.x(i,1); @@ -452,11 +451,9 @@ struct PairComputeFunctor { EV_FLOAT ev; - const int inum = team.league_size(); const int atoms_per_team = team.team_size(); const int firstatom = team.league_rank()*atoms_per_team; const int lastatom = firstatom + atoms_per_team < inum ? firstatom + atoms_per_team : inum; - Kokkos::parallel_for(Kokkos::TeamThreadRange(team, firstatom, lastatom), [&] (const int &ii) { const int i = list.d_ilist[ii]; @@ -616,7 +613,6 @@ struct PairComputeFunctor { EV_FLOAT ev; - const int inum = team.league_size(); const int atoms_per_team = team.team_size(); const int firstatom = team.league_rank()*atoms_per_team; const int lastatom = firstatom + atoms_per_team < inum ? firstatom + atoms_per_team : inum; @@ -926,6 +922,14 @@ int GetMaxNeighs(NeighStyle* list) return maxneigh; } +template +void GetMaxTeamSize(FunctorStyle& functor, int inum, + int &teamsize_max_for, int &teamsize_max_reduce) +{ + teamsize_max_for = Kokkos::TeamPolicy(inum,Kokkos::AUTO).team_size_max(functor,Kokkos::ParallelForTag()); + teamsize_max_reduce = Kokkos::TeamPolicy(inum,Kokkos::AUTO).team_size_max(functor,Kokkos::ParallelReduceTag()); +} + // Submit ParallelFor for NEIGHFLAG=HALF,HALFTHREAD,FULL template EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t<(NEIGHFLAG&PairStyle::EnabledNeighFlags) != 0, NeighListKokkos*> list) { @@ -939,6 +943,7 @@ EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t<(NEIGHFLAG&P if (fpair->lmp->kokkos->neigh_thread) { static int vectorsize = 0; + static int atoms_per_team = 0; static int lastcall = -1; #if defined(LMP_KOKKOS_GPU) @@ -952,21 +957,39 @@ EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t<(NEIGHFLAG&P if (!vectorsize || lastcall < fpair->lmp->neighbor->lastcall) { lastcall = fpair->lmp->update->ntimestep; vectorsize = GetMaxNeighs(list); - vectorsize = MathSpecial::powint(2,int(log2(vectorsize))); // round down to nearest power of 2 + vectorsize = MathSpecial::powint(2,(int(log2(vectorsize) + 0.5))); // round to nearest power of 2 vectorsize = MIN(vectorsize,max_vectorsize); + + int teamsize_max_for,teamsize_max_reduce; + if (fpair->atom->ntypes > MAX_TYPES_STACKPARAMS) { + PairComputeFunctor ff(fpair,list); + GetMaxTeamSize(ff, list->inum, teamsize_max_for, teamsize_max_reduce); + } else { + PairComputeFunctor ff(fpair,list); + GetMaxTeamSize(ff, list->inum, teamsize_max_for, teamsize_max_reduce); + } + + int teamsize_max = teamsize_max_for; + if (fpair->eflag || fpair->vflag) + teamsize_max = teamsize_max_reduce; + atoms_per_team = teamsize_max/vectorsize; } #else vectorsize = 1; + atoms_per_team = 1; #endif + const int inum = list->inum; + const int num_teams = inum / atoms_per_team + (inum % atoms_per_team ? 1 : 0); + if (fpair->atom->ntypes > MAX_TYPES_STACKPARAMS) { PairComputeFunctor ff(fpair,list); - Kokkos::TeamPolicy > policy(list->inum,Kokkos::AUTO(),vectorsize); + Kokkos::TeamPolicy > policy(num_teams,atoms_per_team,vectorsize); if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(policy,ff,ev); else Kokkos::parallel_for(policy,ff); } else { PairComputeFunctor ff(fpair,list); - Kokkos::TeamPolicy > policy(list->inum,Kokkos::AUTO(),vectorsize); + Kokkos::TeamPolicy > policy(num_teams,atoms_per_team,vectorsize); if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(policy,ff,ev); else Kokkos::parallel_for(policy,ff); } From 86f87e0f7b85b8d157e790249abf31b54abb3bf3 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Tue, 19 Dec 2023 10:46:41 -0700 Subject: [PATCH 134/189] Update docs --- doc/src/package.rst | 16 ++++++++-------- src/KOKKOS/pair_kokkos.h | 31 ++++++++++++++++--------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/doc/src/package.rst b/doc/src/package.rst index 63a7f095ad..65392845e9 100644 --- a/doc/src/package.rst +++ b/doc/src/package.rst @@ -474,13 +474,13 @@ If the *neigh/thread* keyword is set to *off*, then the KOKKOS package threads only over atoms. However, for small systems, this may not expose enough parallelism to keep a GPU busy. When this keyword is set to *on*, the KOKKOS package threads over both atoms and neighbors of atoms. When -using *neigh/thread* *on*, a full neighbor list must also be used. Using -*neigh/thread* *on* may be slower for large systems, so this this option -is turned on by default only when there are 16K atoms or less owned by -an MPI rank and when using a full neighbor list. Not all KOKKOS-enabled -potentials support this keyword yet, and only thread over atoms. Many -simple pairwise potentials such as Lennard-Jones do support threading -over both atoms and neighbors. +using *neigh/thread* *on*, the :doc:`newton pair ` setting must +be "off". Using *neigh/thread* *on* may be slower for large systems, so +this this option is turned on by default only when running on one or +more GPUs and there are 16k atoms or less owned by an MPI rank. Not all +KOKKOS-enabled potentials support this keyword yet, and only thread over +atoms. Many simple pairwise potentials such as Lennard-Jones do support +threading over both atoms and neighbors. If the *neigh/transpose* keyword is set to *off*, then the KOKKOS package will use the same memory layout for building the neighbor list on @@ -732,7 +732,7 @@ comm = device, sort = device, neigh/transpose = off, gpu/aware = on. When LAMMPS can safely detect that GPU-aware MPI is not available, the default value of gpu/aware becomes "off". For CPUs or Xeon Phis, the option defaults are neigh = half, neigh/qeq = half, newton = on, binsize = 0.0, comm = no, and sort -= no. The option neigh/thread = on when there are 16K atoms or less on an MPI += no. For GPUs, option neigh/thread = on when there are 16k atoms or less on an MPI rank, otherwise it is "off". These settings are made automatically by the required "-k on" :doc:`command-line switch `. You can change them by using the package kokkos command in your input script or via the :doc:`-pk diff --git a/src/KOKKOS/pair_kokkos.h b/src/KOKKOS/pair_kokkos.h index 55fe01f6d2..eea2cd5316 100644 --- a/src/KOKKOS/pair_kokkos.h +++ b/src/KOKKOS/pair_kokkos.h @@ -935,8 +935,10 @@ template*> list) { EV_FLOAT ev; + const int inum = list->inum; + if (!fpair->lmp->kokkos->neigh_thread_set) - if (list->inum <= 16384) + if (fpair->lmp->kokkos->ngpus && inum <= 16000) if (NEIGHFLAG == FULL || !fpair->newton_pair) fpair->lmp->kokkos->neigh_thread = 1; @@ -947,26 +949,26 @@ EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t<(NEIGHFLAG&P static int lastcall = -1; #if defined(LMP_KOKKOS_GPU) - - #if defined(KOKKOS_ENABLE_HIP) - int max_vectorsize = 64; - #else - int max_vectorsize = 32; - #endif - if (!vectorsize || lastcall < fpair->lmp->neighbor->lastcall) { lastcall = fpair->lmp->update->ntimestep; vectorsize = GetMaxNeighs(list); vectorsize = MathSpecial::powint(2,(int(log2(vectorsize) + 0.5))); // round to nearest power of 2 + + #if defined(KOKKOS_ENABLE_HIP) + int max_vectorsize = 64; + #else + int max_vectorsize = 32; + #endif + vectorsize = MIN(vectorsize,max_vectorsize); int teamsize_max_for,teamsize_max_reduce; if (fpair->atom->ntypes > MAX_TYPES_STACKPARAMS) { PairComputeFunctor ff(fpair,list); - GetMaxTeamSize(ff, list->inum, teamsize_max_for, teamsize_max_reduce); + GetMaxTeamSize(ff, inum, teamsize_max_for, teamsize_max_reduce); } else { PairComputeFunctor ff(fpair,list); - GetMaxTeamSize(ff, list->inum, teamsize_max_for, teamsize_max_reduce); + GetMaxTeamSize(ff, inum, teamsize_max_for, teamsize_max_reduce); } int teamsize_max = teamsize_max_for; @@ -979,7 +981,6 @@ EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t<(NEIGHFLAG&P atoms_per_team = 1; #endif - const int inum = list->inum; const int num_teams = inum / atoms_per_team + (inum % atoms_per_team ? 1 : 0); if (fpair->atom->ntypes > MAX_TYPES_STACKPARAMS) { @@ -996,13 +997,13 @@ EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t<(NEIGHFLAG&P } else { if (fpair->atom->ntypes > MAX_TYPES_STACKPARAMS) { PairComputeFunctor ff(fpair,list); - if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(list->inum,ff,ev); - else Kokkos::parallel_for(list->inum,ff); + if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(inum,ff,ev); + else Kokkos::parallel_for(inum,ff); ff.contribute(); } else { PairComputeFunctor ff(fpair,list); - if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(list->inum,ff,ev); - else Kokkos::parallel_for(list->inum,ff); + if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(inum,ff,ev); + else Kokkos::parallel_for(inum,ff); ff.contribute(); } } From e72f186123df9d24f1f4f2bac5bdc4fad8d2a8e6 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Tue, 19 Dec 2023 22:01:18 -0700 Subject: [PATCH 135/189] check ghost vel in pair bpm/spring --- src/BPM/pair_bpm_spring.cpp | 13 +++++++++++++ src/BPM/pair_bpm_spring.h | 1 + 2 files changed, 14 insertions(+) diff --git a/src/BPM/pair_bpm_spring.cpp b/src/BPM/pair_bpm_spring.cpp index 1177156359..01cee91b4c 100644 --- a/src/BPM/pair_bpm_spring.cpp +++ b/src/BPM/pair_bpm_spring.cpp @@ -19,6 +19,7 @@ #include "force.h" #include "memory.h" #include "neigh_list.h" +#include "neighbor.h" #include @@ -202,6 +203,18 @@ void PairBPMSpring::coeff(int narg, char **arg) if (count == 0) error->all(FLERR, "Incorrect args for pair coefficients"); } +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairBPMSpring::init_style() +{ + if (comm->ghost_velocity == 0) + error->all(FLERR,"Pair bpm/spring requires ghost atoms store velocity"); + + neighbor->add_request(this); +} + /* ---------------------------------------------------------------------- init for one type pair i,j and corresponding j,i ------------------------------------------------------------------------- */ diff --git a/src/BPM/pair_bpm_spring.h b/src/BPM/pair_bpm_spring.h index 3cb281bff3..c10e4a3400 100644 --- a/src/BPM/pair_bpm_spring.h +++ b/src/BPM/pair_bpm_spring.h @@ -31,6 +31,7 @@ class PairBPMSpring : public Pair { void compute(int, int) override; void settings(int, char **) override; void coeff(int, char **) override; + void init_style() override; double init_one(int, int) override; void write_restart(FILE *) override; void read_restart(FILE *) override; From aa221e2f2bbaa5770eea4db4ae5ba38fcaffc990 Mon Sep 17 00:00:00 2001 From: oywg11 Date: Thu, 21 Dec 2023 10:28:53 +0800 Subject: [PATCH 136/189] add new potential file for aip/water/2dm --- .../aip_water_2dm/CBNOH.aip.water.2dm | 1 + .../aip_water_2dm/COH.aip.water.2dm | 1 - potentials/CBNOH.aip.water.2dm | 49 +++++++++++++++++++ potentials/COH.aip.water.2dm | 28 ----------- 4 files changed, 50 insertions(+), 29 deletions(-) create mode 120000 examples/PACKAGES/interlayer/aip_water_2dm/CBNOH.aip.water.2dm delete mode 120000 examples/PACKAGES/interlayer/aip_water_2dm/COH.aip.water.2dm create mode 100755 potentials/CBNOH.aip.water.2dm delete mode 100644 potentials/COH.aip.water.2dm diff --git a/examples/PACKAGES/interlayer/aip_water_2dm/CBNOH.aip.water.2dm b/examples/PACKAGES/interlayer/aip_water_2dm/CBNOH.aip.water.2dm new file mode 120000 index 0000000000..60c9c3a8f4 --- /dev/null +++ b/examples/PACKAGES/interlayer/aip_water_2dm/CBNOH.aip.water.2dm @@ -0,0 +1 @@ +../../../../potentials/CBNOH.aip.water.2dm \ No newline at end of file diff --git a/examples/PACKAGES/interlayer/aip_water_2dm/COH.aip.water.2dm b/examples/PACKAGES/interlayer/aip_water_2dm/COH.aip.water.2dm deleted file mode 120000 index fe5cccfcd2..0000000000 --- a/examples/PACKAGES/interlayer/aip_water_2dm/COH.aip.water.2dm +++ /dev/null @@ -1 +0,0 @@ -../../../../potentials/COH.aip.water.2dm \ No newline at end of file diff --git a/potentials/CBNOH.aip.water.2dm b/potentials/CBNOH.aip.water.2dm new file mode 100755 index 0000000000..5efcc99fcc --- /dev/null +++ b/potentials/CBNOH.aip.water.2dm @@ -0,0 +1,49 @@ +# DATE: 2023-12-20 UNITS: metal CONTRIBUTOR: Wengen Ouyang w.g.ouyang@gmail.com +# CITATION: Z. Feng, ..., and W. Ouyang, J. Phys. Chem. C 127(18), 8704 (2023). +# CITATION: Z. Feng, ..., and W. Ouyang, Langmuir 39(50), 18198-18207 (2023). +# Anisotropic Potential (AIP) for water/graphene and water/hBN heterojunctions +# The parameters below are fitted against the PBE + MBD-NL (graphene/water) and SCAN (hBN/water) DFT reference data. +# +# -------------------Repulsion Potential ------------------++++++++++++++ Vdw Potential ++++++++++++++++************ +# beta(A) alpha delta(A) epsilon(meV) C(meV) d sR reff(A) C6(meV*A^6) S rcut +# +# For graphene and hydrocarbons +C C 3.205843 7.511126 1.235334 1.528338E-5 37.530428 15.499947 0.7954443 3.681440 25.714535E3 1.0 2.0 +H H 3.974540 6.53799 1.080633 0.6700556 0.8333833 15.022371 0.7490632 2.767223 1.6159581E3 1.0 1.2 +C H 2.642950 12.91410 1.020257 0.9750012 25.340996 15.222927 0.8115998 3.887324 5.6874617E3 1.0 1.5 +H C 2.642950 12.91410 1.020257 0.9750012 25.340996 15.222927 0.8115998 3.887324 5.6874617E3 1.0 1.5 + +# For hBN +B B 3.143737 9.825139 1.936405 2.7848400 14.495957 15.199263 0.7834022 3.682950 49.498013E3 1.0 2.0 +N N 3.443196 7.084490 1.747349 2.9139991 46.508553 15.020370 0.8008370 3.551843 14.810151E3 1.0 2.0 +B N 3.295257 7.224311 2.872667 1.3715032 0.4347152 14.594578 0.8044028 3.765728 24.669996E3 1.0 2.0 +B H 2.718657 9.214551 3.273063 14.015714 14.760509 15.084752 0.7768383 3.640866 7.9642467E3 1.0 1.5 +N B 3.295257 7.224311 2.872667 1.3715032 0.4347152 14.594578 0.8044028 3.765728 24.669996E3 1.0 2.0 +H B 2.718657 9.214551 3.273063 14.015714 14.760509 15.084752 0.7768383 3.640866 7.9642467E3 1.0 1.5 + +# For water-graphene +C Ow 5.453696 6.181724 1.250255 3.349092 0.687806 9.057065 1.232495 2.775772 100226.555031 1.0 2.0 +C Hw 2.553809 9.686644 1.964892 41.776171 -16.300128 9.015685 0.744155 2.415456 7409.128564 1.0 2.0 +Ow C 5.453696 6.181724 1.250255 3.349092 0.687806 9.057065 1.232495 2.775772 100226.555031 1.0 1.2 +Hw C 2.553809 9.686644 1.964892 41.776171 -16.300128 9.015685 0.744155 2.415456 7409.128564 1.0 1.2 + +# For water-hBN +N Ow 3.530598 16.377816 1.285374 1.717537 1.339337 24.797794 0.771411 3.928357 33589.850651 1.0 2.0 +N Hw 4.029390 5.360546 0.950352 15.945549 -1.486701 10.797276 1.352684 2.293775 41247.181447 1.0 2.0 +B Ow 3.907514 7.842519 2.380078 32.122737 1.190485 17.482482 0.788174 2.368217 139539.370785 1.0 2.0 +B Hw 3.804966 2.356248 1.114761 9.193309 -5.922514 9.000572 1.334703 1.746122 43796.489158 1.0 2.0 +Ow N 3.530598 16.377816 1.285374 1.717537 1.339337 24.797794 0.771411 3.928357 33589.850651 1.0 1.2 +Hw N 4.029390 5.360546 0.950352 15.945549 -1.486701 10.797276 1.352684 2.293775 41247.181447 1.0 1.2 +Ow B 3.907514 7.842519 2.380078 32.122737 1.190485 17.482482 0.788174 2.368217 139539.370785 1.0 1.2 +Hw B 3.804966 2.356248 1.114761 9.193309 -5.922514 9.000572 1.334703 1.746122 43796.489158 1.0 1.2 + +# # The AIPs for other elements are tunred off +H Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +H Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Ow H 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Hw H 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 + +Ow Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Hw Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Ow Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Hw Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 diff --git a/potentials/COH.aip.water.2dm b/potentials/COH.aip.water.2dm deleted file mode 100644 index 5325399abe..0000000000 --- a/potentials/COH.aip.water.2dm +++ /dev/null @@ -1,28 +0,0 @@ -# DATE: 2022-12-02 UNITS: metal CONTRIBUTOR: Wengen Ouyang w.g.ouyang@gmail.com CITATION: Z. Feng, ..., and W. Ouyang, J. Phys. Chem. C 127, 8704 (2023). -# Anisotropic Interfacial Potential (AIP) parameters for water/graphene heterojunctions -# The parameters below are fitted against the PBE + MBD-NL DFT reference data from 2.5 A to 15 A. -# -# ----------------- Repulsion Potential ------------------++++++++++++++ Vdw Potential ++++++++++++++++************ -# beta(A) alpha delta(A) epsilon(meV) C(meV) d sR reff(A) C6(meV*A^6) S rcut -# For graphene and hydrocarbons -C C 3.205843 7.511126 1.235334 1.528338E-5 37.530428 15.499947 0.7954443 3.681440 25.714535E3 1.0 2.0 -H H 3.974540 6.53799 1.080633 0.6700556 0.8333833 15.022371 0.7490632 2.767223 1.6159581E3 1.0 1.2 -C H 2.642950 12.91410 1.020257 0.9750012 25.340996 15.222927 0.8115998 3.887324 5.6874617E3 1.0 1.5 -H C 2.642950 12.91410 1.020257 0.9750012 25.340996 15.222927 0.8115998 3.887324 5.6874617E3 1.0 1.5 - -# For water-graphene -C Ow 5.45369612 6.18172364 1.25025450 3.34909245 0.68780636 9.05706482 1.23249498 2.77577173 100226.55503127 1.0 2.0 -C Hw 2.55380862 9.68664390 1.96489198 41.77617053 -16.30012807 9.01568534 0.74415463 2.41545571 7409.12856378 1.0 2.0 -Ow C 5.45369612 6.18172364 1.25025450 3.34909245 0.68780636 9.05706482 1.23249498 2.77577173 100226.55503127 1.0 1.2 -Hw C 2.55380862 9.68664390 1.96489198 41.77617053 -16.30012807 9.01568534 0.74415463 2.41545571 7409.12856378 1.0 1.2 - -# # The ILPs for other systems are set to zero -H Ow 5.45369612 6.18172364 1.25025450 0.00000000 0.00000000 9.05706482 1.23249498 2.77577173 0.00000000 1.0 1.2 -H Hw 5.45369612 6.18172364 1.25025450 0.00000000 0.00000000 9.05706482 1.23249498 2.77577173 0.00000000 1.0 1.2 -Ow H 5.45369612 6.18172364 1.25025450 0.00000000 0.00000000 9.05706482 1.23249498 2.77577173 0.00000000 1.0 1.2 -Hw H 5.45369612 6.18172364 1.25025450 0.00000000 0.00000000 9.05706482 1.23249498 2.77577173 0.00000000 1.0 1.2 - -Ow Ow 5.45369612 6.18172364 1.25025450 0.00000000 0.00000000 9.05706482 1.23249498 2.77577173 0.00000000 1.0 1.2 -Hw Hw 5.45369612 6.18172364 1.25025450 0.00000000 0.00000000 9.05706482 1.23249498 2.77577173 0.00000000 1.0 1.2 -Ow Hw 5.45369612 6.18172364 1.25025450 0.00000000 0.00000000 9.05706482 1.23249498 2.77577173 0.00000000 1.0 1.2 -Hw Ow 5.45369612 6.18172364 1.25025450 0.00000000 0.00000000 9.05706482 1.23249498 2.77577173 0.00000000 1.0 1.2 From b58203fc52277d181cd4884bd54c952ced3a90ca Mon Sep 17 00:00:00 2001 From: oywg11 Date: Thu, 21 Dec 2023 10:44:03 +0800 Subject: [PATCH 137/189] update the doc file --- doc/src/pair_aip_water_2dm.rst | 37 ++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/doc/src/pair_aip_water_2dm.rst b/doc/src/pair_aip_water_2dm.rst index 5cee40edda..f480819ae4 100644 --- a/doc/src/pair_aip_water_2dm.rst +++ b/doc/src/pair_aip_water_2dm.rst @@ -22,13 +22,24 @@ Examples .. code-block:: LAMMPS pair_style hybrid/overlay aip/water/2dm 16.0 1 - pair_coeff * * aip/water/2dm COH.aip.water.2dm C Ow Hw + pair_coeff * * aip/water/2dm CBNOH.aip.water.2dm C Ow Hw pair_style hybrid/overlay aip/water/2dm 16.0 lj/cut/tip4p/long 2 3 1 1 0.1546 10 8.5 - pair_coeff 2 2 lj/cut/tip4p/long 8.0313e-3 3.1589 # O-O - pair_coeff 2 3 lj/cut/tip4p/long 0.0 0.0 # O-H - pair_coeff 3 3 lj/cut/tip4p/long 0.0 0.0 # H-H - pair_coeff * * aip/water/2dm COH.aip.water.2dm C Ow Hw + pair_coeff 2 2 lj/cut/tip4p/long 8.0313e-3 3.1589 # O-O + pair_coeff 2 3 lj/cut/tip4p/long 0.0 0.0 # O-H + pair_coeff 3 3 lj/cut/tip4p/long 0.0 0.0 # H-H + pair_coeff * * aip/water/2dm CBNOH.aip.water.2dm C Ow Hw + + pair_style hybrid/overlay aip/water/2dm 16.0 lj/cut/tip4p/long 3 4 1 1 0.1546 10 8.5 coul/shield 16.0 1 + pair_coeff 1*2 1*2 none + pair_coeff 3 3 lj/cut/tip4p/long 8.0313e-3 3.1589 # O-O + pair_coeff 3 4 lj/cut/tip4p/long 0.0 0.0 # O-H + pair_coeff 4 4 lj/cut/tip4p/long 0.0 0.0 # H-H + pair_coeff * * aip/water/2dm CBNOH.aip.water.2dm i B N Ow Hw + pair_coeff 1 3 coul/shield 1.333 + pair_coeff 1 4 coul/shield 1.333 + pair_coeff 2 3 coul/shield 1.333 + pair_coeff 2 4 coul/shield 1.333 Description """"""""""" @@ -37,7 +48,7 @@ Description The *aip/water/2dm* style computes the anisotropic interfacial potential (AIP) potential for interfaces of water with two-dimensional (2D) -materials as described in :ref:`(Feng) `. +materials as described in :ref:`(Feng1) ` and :ref:`(Feng2) `. .. math:: @@ -67,7 +78,7 @@ larger than :math:`r_c` :doc:`pair_style ilp_graphene_hbn oxygen-hydrogen bonds and the normal vector of the central oxygen atom is defined as their average. -The provided parameter file, ``COH.aip.water.2dm``, is intended for use +The provided parameter file, ``CBNOH.aip.water.2dm``, is intended for use with *metal* :doc:`units `, with energies in meV. Two additional parameters, *S*, and *rcut* are included in the parameter file. *S* is designed to facilitate scaling of energies; *rcut* is the cutoff for an @@ -77,7 +88,7 @@ the calculation of the normals for all atom pairs. .. note:: The parameters presented in the provided parameter file, - ``COH.aip.water.2dm``, are fitted with the taper function enabled by + ``CBNOH.aip.water.2dm``, are fitted with the taper function enabled by setting the cutoff equal to 16.0 Angstrom. Using a different cutoff or taper function setting should be carefully checked as they can lead to significant errors. These parameters provide a good @@ -134,7 +145,7 @@ if LAMMPS was built with that package. See the :doc:`Build package This pair style requires the newton setting to be *on* for pair interactions. -The ``COH.aip.water.2dm`` potential file provided with LAMMPS is +The ``CBNOH.aip.water.2dm`` potential file provided with LAMMPS is parameterized for *metal* units. You can use this pair style with any LAMMPS units, but you would need to create your own potential file with parameters in the appropriate units, if your simulation does not use @@ -162,6 +173,10 @@ tap_flag = 1 ---------- -.. _Feng: +.. _Feng1: -**(Feng)** Z. Feng and W. Ouyang et al., J. Phys. Chem. C. 127, 8704-8713 (2023). +**(Feng1)** Z. Feng, ..., and W. Ouyang, J. Phys. Chem. C. 127(18), 8704-8713 (2023). + +.. _Feng2: + +**(Feng2)** Z. Feng, ..., and W. Ouyang, Langmuir 39(50), 18198-18207 (2023). From b857cd9e39d8363f52a535220381ab8c662279d9 Mon Sep 17 00:00:00 2001 From: oywg11 Date: Thu, 21 Dec 2023 11:31:26 +0800 Subject: [PATCH 138/189] update the doc file --- doc/src/pair_aip_water_2dm.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/pair_aip_water_2dm.rst b/doc/src/pair_aip_water_2dm.rst index f480819ae4..f6c56eff6e 100644 --- a/doc/src/pair_aip_water_2dm.rst +++ b/doc/src/pair_aip_water_2dm.rst @@ -35,7 +35,7 @@ Examples pair_coeff 3 3 lj/cut/tip4p/long 8.0313e-3 3.1589 # O-O pair_coeff 3 4 lj/cut/tip4p/long 0.0 0.0 # O-H pair_coeff 4 4 lj/cut/tip4p/long 0.0 0.0 # H-H - pair_coeff * * aip/water/2dm CBNOH.aip.water.2dm i B N Ow Hw + pair_coeff * * aip/water/2dm CBNOH.aip.water.2dm B N Ow Hw pair_coeff 1 3 coul/shield 1.333 pair_coeff 1 4 coul/shield 1.333 pair_coeff 2 3 coul/shield 1.333 @@ -73,7 +73,7 @@ larger than :math:`r_c` :doc:`pair_style ilp_graphene_hbn .. note:: This pair style uses the atomic normal vector definition from - :ref:`(Feng) `), where the atomic normal vectors of the + :ref:`(Feng1) `), where the atomic normal vectors of the hydrogen atoms are assumed to lie along the corresponding oxygen-hydrogen bonds and the normal vector of the central oxygen atom is defined as their average. From dedbd8f6db58bc6582391477059c2b5e3eccc1ca Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 19 Dec 2023 23:37:07 -0500 Subject: [PATCH 139/189] improvements from clang-tidy --- examples/COUPLE/simple/simple.cpp | 18 +++++++++--------- src/DRUDE/fix_tgnh_drude.cpp | 1 - src/ML-PACE/compute_pace.cpp | 12 ++---------- src/ML-SNAP/compute_sna_atom.cpp | 6 ++---- src/OPENMP/npair_bin_ghost_omp.cpp | 2 +- src/REAXFF/compute_reaxff_atom.cpp | 5 ++--- src/RIGID/fix_rigid_nh_small.cpp | 1 - src/library.cpp | 2 +- src/nstencil_bin.cpp | 4 ++-- src/nstencil_multi.cpp | 4 ++-- src/nstencil_multi_old.cpp | 4 ++-- tools/lammps-gui/main.cpp | 2 +- tools/lammps-gui/preferences.cpp | 2 +- tools/lammps-gui/stdcapture.cpp | 6 +++--- unittest/fortran/wrap_configuration.cpp | 2 +- unittest/utils/test_lepton.cpp | 8 ++++---- 16 files changed, 33 insertions(+), 46 deletions(-) diff --git a/examples/COUPLE/simple/simple.cpp b/examples/COUPLE/simple/simple.cpp index c8727cc81f..f6365ac3de 100644 --- a/examples/COUPLE/simple/simple.cpp +++ b/examples/COUPLE/simple/simple.cpp @@ -67,7 +67,7 @@ int main(int narg, char **arg) FILE *fp; if (me == 0) { fp = fopen(arg[2],"r"); - if (fp == NULL) { + if (fp == nullptr) { printf("ERROR: Could not open LAMMPS input script\n"); MPI_Abort(MPI_COMM_WORLD,1); } @@ -78,14 +78,14 @@ int main(int narg, char **arg) // (could just send it to proc 0 of comm_lammps and let it Bcast) // all LAMMPS procs call input->one() on the line - LAMMPS *lmp = NULL; - if (lammps == 1) lmp = new LAMMPS(0,NULL,comm_lammps); + LAMMPS *lmp = nullptr; + if (lammps == 1) lmp = new LAMMPS(0,nullptr,comm_lammps); int n; char line[1024]; - while (1) { + while (true) { if (me == 0) { - if (fgets(line,1024,fp) == NULL) n = 0; + if (fgets(line,1024,fp) == nullptr) n = 0; else n = strlen(line) + 1; if (n == 0) fclose(fp); } @@ -101,8 +101,8 @@ int main(int narg, char **arg) // put coords back into LAMMPS // run a single step with changed coords - double *x = NULL; - double *v = NULL; + double *x = nullptr; + double *v = nullptr; if (lammps == 1) { lmp->input->one("run 10"); @@ -147,7 +147,7 @@ int main(int narg, char **arg) // create_atoms() to create new ones with old coords, vels // initial thermo should be same as step 20 - int *type = NULL; + int *type = nullptr; if (lammps == 1) { int natoms = static_cast (lmp->atom->natoms); @@ -155,7 +155,7 @@ int main(int narg, char **arg) for (int i = 0; i < natoms; i++) type[i] = 1; lmp->input->one("delete_atoms group all"); - lammps_create_atoms(lmp,natoms,NULL,type,x,v,NULL,0); + lammps_create_atoms(lmp,natoms,nullptr,type,x,v,nullptr,0); lmp->input->one("run 10"); } diff --git a/src/DRUDE/fix_tgnh_drude.cpp b/src/DRUDE/fix_tgnh_drude.cpp index 987408fe63..b23acd349b 100644 --- a/src/DRUDE/fix_tgnh_drude.cpp +++ b/src/DRUDE/fix_tgnh_drude.cpp @@ -1076,7 +1076,6 @@ void FixTGNHDrude::couple() void FixTGNHDrude::remap() { - int i; double oldlo,oldhi; double expfac; diff --git a/src/ML-PACE/compute_pace.cpp b/src/ML-PACE/compute_pace.cpp index 12693a58d9..b96432cfe3 100644 --- a/src/ML-PACE/compute_pace.cpp +++ b/src/ML-PACE/compute_pace.cpp @@ -73,10 +73,7 @@ ComputePACE::ComputePACE(LAMMPS *lmp, int narg, char **arg) : auto potential_file_name = utils::get_potential_file_path(arg[3]); delete acecimpl->basis_set; acecimpl->basis_set = new ACECTildeBasisSet(potential_file_name); - double cut = acecimpl->basis_set->cutoffmax; cutmax = acecimpl->basis_set->cutoffmax; - double cuti; - double radelemall = 0.5; //# of rank 1, rank > 1 functions @@ -178,7 +175,6 @@ void ComputePACE::init_list(int /*id*/, NeighList *ptr) void ComputePACE::compute_array() { int ntotal = atom->nlocal + atom->nghost; - double **f = atom->f; invoked_array = update->ntimestep; // grow pace_peratom array if necessary @@ -208,9 +204,6 @@ void ComputePACE::compute_array() // invoke full neighbor list (will copy or build if necessary) neighbor->build_one(list); - SPECIES_TYPE *mus; - NS_TYPE *ns; - LS_TYPE *ls; const int inum = list->inum; const int* const ilist = list->ilist; @@ -234,7 +227,6 @@ void ComputePACE::compute_array() // compute pace derivatives for each atom in group // use full neighbor list to count atoms less than cutoff - double** const x = atom->x; const int* const mask = atom->mask; const int ntypes = atom->ntypes; @@ -251,8 +243,8 @@ void ComputePACE::compute_array() delete acecimpl->ace; acecimpl->ace = new ACECTildeEvaluator(*acecimpl->basis_set); - acecimpl->ace->compute_projections = 1; - acecimpl->ace->compute_b_grad = 1; + acecimpl->ace->compute_projections = true; + acecimpl->ace->compute_b_grad = true; int n_r1, n_rp = 0; n_r1 = acecimpl->basis_set->total_basis_size_rank1[0]; n_rp = acecimpl->basis_set->total_basis_size[0]; diff --git a/src/ML-SNAP/compute_sna_atom.cpp b/src/ML-SNAP/compute_sna_atom.cpp index 3fe3cca92d..da49b15117 100644 --- a/src/ML-SNAP/compute_sna_atom.cpp +++ b/src/ML-SNAP/compute_sna_atom.cpp @@ -299,7 +299,7 @@ void ComputeSNAAtom::compute_peratom() // ############################################################################## // // ##### Start of section for computing bispectrum on nnn nearest neighbors ##### // // ############################################################################## // - if (nearest_neighbors_mode == true) { + if (nearest_neighbors_mode) { // ##### 1) : consider full neighbor list in rlist memory->create(distsq, jnum, "snann/atom:distsq"); memory->create(rlist, jnum, 3, "snann/atom:rlist"); @@ -308,7 +308,6 @@ void ComputeSNAAtom::compute_peratom() for (int jj = 0; jj < jnum; jj++) { int j = jlist[jj]; j &= NEIGHMASK; - int jtype = type[j]; const double delx = xtmp - x[j][0]; const double dely = ytmp - x[j][1]; @@ -614,10 +613,9 @@ double * ComputeSNAAtom::tanh_weights(double * rsq, double rcut, double delta, i return w; } -double ComputeSNAAtom::sum_weights(double * rsq, double * w, int ncounts) +double ComputeSNAAtom::sum_weights(double * /*rsq*/, double * w, int ncounts) { double S=0.; - double rloc=0.; for (int i=0; i::build(NeighList *list) #endif NPAIR_OMP_SETUP(nall); - int i, j, k, n, itype, jtype, ibin, bin_start, which, imol, iatom; + int i, j, k, n, itype, jtype, ibin, which, imol, iatom; tagint tagprev; double xtmp, ytmp, ztmp, delx, dely, delz, rsq; int xbin, ybin, zbin, xbin2, ybin2, zbin2; diff --git a/src/REAXFF/compute_reaxff_atom.cpp b/src/REAXFF/compute_reaxff_atom.cpp index 1b54f3b82a..cc9109915c 100644 --- a/src/REAXFF/compute_reaxff_atom.cpp +++ b/src/REAXFF/compute_reaxff_atom.cpp @@ -34,11 +34,10 @@ using namespace ReaxFF; /* ---------------------------------------------------------------------- */ ComputeReaxFFAtom::ComputeReaxFFAtom(LAMMPS *lmp, int narg, char **arg) : - Compute(lmp, narg, arg), - abo(nullptr), neighid(nullptr), bondcount(nullptr), reaxff(nullptr) + Compute(lmp, narg, arg), neighid(nullptr), abo(nullptr), bondcount(nullptr), reaxff(nullptr) { if (atom->tag_consecutive() == 0) - error->all(FLERR,"Atom IDs must be consecutive for compute reaxff/atom"); + error->all(FLERR, "Atom IDs must be consecutive for compute reaxff/atom"); peratom_flag = 1; diff --git a/src/RIGID/fix_rigid_nh_small.cpp b/src/RIGID/fix_rigid_nh_small.cpp index 952dc29032..3ee11e28d2 100644 --- a/src/RIGID/fix_rigid_nh_small.cpp +++ b/src/RIGID/fix_rigid_nh_small.cpp @@ -219,7 +219,6 @@ void FixRigidNHSmall::init() } } - int icompute; if (tcomputeflag) { temperature = modify->get_compute_by_id(id_temp); if (!temperature) diff --git a/src/library.cpp b/src/library.cpp index 1acdfc4787..a629df7b8c 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -709,7 +709,7 @@ void lammps_commands_string(void *handle, const char *str) break; } - lmp->input->one(cmd.c_str()); + lmp->input->one(cmd); } } } diff --git a/src/nstencil_bin.cpp b/src/nstencil_bin.cpp index 2b7c15cff6..ccefa16978 100644 --- a/src/nstencil_bin.cpp +++ b/src/nstencil_bin.cpp @@ -55,9 +55,9 @@ void NStencilBin::create() // Now only include "upper right" bins for half and ortho stencils if (HALF && (!DIM_3D) && (!TRI)) - if (! (j > 0 || (j == 0 && i > 0))) continue; + if (j <= 0 && (j != 0 || i <= 0)) continue; if (HALF && DIM_3D && (!TRI)) - if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; + if (k <= 0 && j <= 0 && (j != 0 || i <= 0)) continue; if (bin_distance(i, j, k) < cutneighmaxsq) stencil[nstencil++] = k * mbiny * mbinx + j * mbinx + i; diff --git a/src/nstencil_multi.cpp b/src/nstencil_multi.cpp index a73215a058..693c415876 100644 --- a/src/nstencil_multi.cpp +++ b/src/nstencil_multi.cpp @@ -115,9 +115,9 @@ void NStencilMulti::create() if (HALF && (!TRI)) { if (half_flag) { if (DIM_3D) { - if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; + if (k <= 0 && j <= 0 && (j != 0 || i <= 0)) continue; } else { - if (! (j > 0 || (j == 0 && i > 0))) continue; + if (j <= 0 && (j != 0 || i <= 0)) continue; } } } diff --git a/src/nstencil_multi_old.cpp b/src/nstencil_multi_old.cpp index 6d34d8881f..8648e6f73c 100644 --- a/src/nstencil_multi_old.cpp +++ b/src/nstencil_multi_old.cpp @@ -65,9 +65,9 @@ void NStencilMultiOld::create() // Now only include "upper right" bins for half and ortho stencils if (HALF && (!DIM_3D) && (!TRI)) - if (! (j > 0 || (j == 0 && i > 0))) continue; + if (j <= 0 && (j != 0 || i <= 0)) continue; if (HALF && DIM_3D && (!TRI)) - if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; + if (k <= 0 && j <= 0 && (j != 0 || i <= 0)) continue; rsq = bin_distance(i, j, k); if (rsq < typesq) { diff --git a/tools/lammps-gui/main.cpp b/tools/lammps-gui/main.cpp index cf09fbb892..d70e9d3e46 100644 --- a/tools/lammps-gui/main.cpp +++ b/tools/lammps-gui/main.cpp @@ -36,7 +36,7 @@ int main(int argc, char *argv[]) LammpsGui w(nullptr, infile); w.show(); - return a.exec(); + return QApplication::exec(); } // Local Variables: diff --git a/tools/lammps-gui/preferences.cpp b/tools/lammps-gui/preferences.cpp index fd01bb5046..27cc106008 100644 --- a/tools/lammps-gui/preferences.cpp +++ b/tools/lammps-gui/preferences.cpp @@ -177,7 +177,7 @@ void Preferences::accept() msg.exec(); const char *path = mystrdup(QCoreApplication::applicationFilePath()); const char *arg0 = mystrdup(QCoreApplication::arguments().at(0)); - execl(path, arg0, (char *)NULL); + execl(path, arg0, (char *)nullptr); } // reformatting settings diff --git a/tools/lammps-gui/stdcapture.cpp b/tools/lammps-gui/stdcapture.cpp index b09aebf053..8be543e70e 100644 --- a/tools/lammps-gui/stdcapture.cpp +++ b/tools/lammps-gui/stdcapture.cpp @@ -38,7 +38,7 @@ StdCapture::StdCapture() : m_oldStdOut(0), m_capturing(false) { // make stdout unbuffered so that we don't need to flush the stream - setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stdout, nullptr, _IONBF, 0); m_pipe[READ] = 0; m_pipe[WRITE] = 0; @@ -106,7 +106,7 @@ bool StdCapture::EndCapture() std::string StdCapture::GetChunk() { - if (!m_capturing) return std::string(); + if (!m_capturing) return {}; int bytesRead = 0; buf[0] = '\0'; @@ -120,7 +120,7 @@ std::string StdCapture::GetChunk() if (bytesRead > 0) { buf[bytesRead] = '\0'; } - return std::string(buf); + return {buf}; } std::string StdCapture::GetCapture() diff --git a/unittest/fortran/wrap_configuration.cpp b/unittest/fortran/wrap_configuration.cpp index 5fb744086e..08974d8a08 100644 --- a/unittest/fortran/wrap_configuration.cpp +++ b/unittest/fortran/wrap_configuration.cpp @@ -234,7 +234,7 @@ TEST_F(LAMMPS_configuration, style_count) { Info info(lmp); for (const auto &c : style_category) - EXPECT_EQ(f_lammps_style_count(c.c_str()), info.get_available_styles(c.c_str()).size()); + EXPECT_EQ(f_lammps_style_count(c.c_str()), info.get_available_styles(c).size()); }; TEST_F(LAMMPS_configuration, style_name) diff --git a/unittest/utils/test_lepton.cpp b/unittest/utils/test_lepton.cpp index 7b2c86f05f..a9fa6e3543 100644 --- a/unittest/utils/test_lepton.cpp +++ b/unittest/utils/test_lepton.cpp @@ -129,9 +129,9 @@ TEST(LeptonCustomFunction, zbl) */ class ExampleFunction : public Lepton::CustomFunction { - int getNumArguments() const { return 2; } - double evaluate(const double *arguments) const { return 2.0 * arguments[0] * arguments[1]; } - double evaluateDerivative(const double *arguments, const int *derivOrder) const + int getNumArguments() const override { return 2; } + double evaluate(const double *arguments) const override { return 2.0 * arguments[0] * arguments[1]; } + double evaluateDerivative(const double *arguments, const int *derivOrder) const override { if (derivOrder[0] == 1) { if (derivOrder[1] == 0) @@ -142,7 +142,7 @@ class ExampleFunction : public Lepton::CustomFunction { if (derivOrder[1] == 1 && derivOrder[0] == 0) return 2.0 * arguments[0]; return 0.0; } - Lepton::CustomFunction *clone() const { return new ExampleFunction(); } + Lepton::CustomFunction *clone() const override { return new ExampleFunction(); } }; /** From 3b1559470768a7edec5d2bd01129a229cbc2996d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 21 Dec 2023 15:58:29 -0500 Subject: [PATCH 140/189] update citation comment --- src/REAXFF/fix_reaxff.cpp | 2 +- src/REAXFF/reaxff_allocate.cpp | 2 +- src/REAXFF/reaxff_control.cpp | 2 +- src/REAXFF/reaxff_ffield.cpp | 2 +- src/REAXFF/reaxff_forces.cpp | 2 +- src/REAXFF/reaxff_hydrogen_bonds.cpp | 2 +- src/REAXFF/reaxff_list.cpp | 2 +- src/REAXFF/reaxff_lookup.cpp | 2 +- src/REAXFF/reaxff_multi_body.cpp | 2 +- src/REAXFF/reaxff_nonbonded.cpp | 2 +- src/REAXFF/reaxff_reset_tools.cpp | 2 +- src/REAXFF/reaxff_tool_box.cpp | 2 +- src/REAXFF/reaxff_torsion_angles.cpp | 2 +- src/REAXFF/reaxff_valence_angles.cpp | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/REAXFF/fix_reaxff.cpp b/src/REAXFF/fix_reaxff.cpp index 06941cd8a0..bec16b5d04 100644 --- a/src/REAXFF/fix_reaxff.cpp +++ b/src/REAXFF/fix_reaxff.cpp @@ -19,7 +19,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. ------------------------------------------------------------------------- */ #include "fix_reaxff.h" diff --git a/src/REAXFF/reaxff_allocate.cpp b/src/REAXFF/reaxff_allocate.cpp index ce56668a01..06ebc20f30 100644 --- a/src/REAXFF/reaxff_allocate.cpp +++ b/src/REAXFF/reaxff_allocate.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_control.cpp b/src/REAXFF/reaxff_control.cpp index d914765f45..99e498b428 100644 --- a/src/REAXFF/reaxff_control.cpp +++ b/src/REAXFF/reaxff_control.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_ffield.cpp b/src/REAXFF/reaxff_ffield.cpp index d5761eb343..c22330cc16 100644 --- a/src/REAXFF/reaxff_ffield.cpp +++ b/src/REAXFF/reaxff_ffield.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38, 245-259 (2012). This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_forces.cpp b/src/REAXFF/reaxff_forces.cpp index a4edfeee5c..274799c30c 100644 --- a/src/REAXFF/reaxff_forces.cpp +++ b/src/REAXFF/reaxff_forces.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_hydrogen_bonds.cpp b/src/REAXFF/reaxff_hydrogen_bonds.cpp index 6a56675f19..0389db7832 100644 --- a/src/REAXFF/reaxff_hydrogen_bonds.cpp +++ b/src/REAXFF/reaxff_hydrogen_bonds.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_list.cpp b/src/REAXFF/reaxff_list.cpp index 0ff0852a04..2989f717d6 100644 --- a/src/REAXFF/reaxff_list.cpp +++ b/src/REAXFF/reaxff_list.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_lookup.cpp b/src/REAXFF/reaxff_lookup.cpp index c0e7bf2c54..d9ee471caf 100644 --- a/src/REAXFF/reaxff_lookup.cpp +++ b/src/REAXFF/reaxff_lookup.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_multi_body.cpp b/src/REAXFF/reaxff_multi_body.cpp index 2390b54474..855d82623f 100644 --- a/src/REAXFF/reaxff_multi_body.cpp +++ b/src/REAXFF/reaxff_multi_body.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_nonbonded.cpp b/src/REAXFF/reaxff_nonbonded.cpp index 75cbd79b29..e0a8d092b2 100644 --- a/src/REAXFF/reaxff_nonbonded.cpp +++ b/src/REAXFF/reaxff_nonbonded.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_reset_tools.cpp b/src/REAXFF/reaxff_reset_tools.cpp index bebb2e2cfc..9de917e142 100644 --- a/src/REAXFF/reaxff_reset_tools.cpp +++ b/src/REAXFF/reaxff_reset_tools.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_tool_box.cpp b/src/REAXFF/reaxff_tool_box.cpp index 22ef299b41..aa6f831e95 100644 --- a/src/REAXFF/reaxff_tool_box.cpp +++ b/src/REAXFF/reaxff_tool_box.cpp @@ -10,7 +10,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_torsion_angles.cpp b/src/REAXFF/reaxff_torsion_angles.cpp index e9b6bc618d..29233a56dc 100644 --- a/src/REAXFF/reaxff_torsion_angles.cpp +++ b/src/REAXFF/reaxff_torsion_angles.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_valence_angles.cpp b/src/REAXFF/reaxff_valence_angles.cpp index ac3e2dbd1e..b46f09d23a 100644 --- a/src/REAXFF/reaxff_valence_angles.cpp +++ b/src/REAXFF/reaxff_valence_angles.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as From 2b31a2bed56841c3b4303ef1d3e5655a308587d2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 21 Dec 2023 16:07:58 -0500 Subject: [PATCH 141/189] gracefully handle reaxff parameter files without hydrogen bond parameters --- src/REAXFF/reaxff_ffield.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/REAXFF/reaxff_ffield.cpp b/src/REAXFF/reaxff_ffield.cpp index c22330cc16..6ca8dc6256 100644 --- a/src/REAXFF/reaxff_ffield.cpp +++ b/src/REAXFF/reaxff_ffield.cpp @@ -30,6 +30,7 @@ #include "error.h" #include "memory.h" #include "text_file_reader.h" +#include "tokenizer.h" #include "utils.h" #include @@ -40,6 +41,8 @@ using LAMMPS_NS::utils::open_potential; using LAMMPS_NS::utils::getsyserror; using LAMMPS_NS::utils::uppercase; +using LAMMPS_NS::EOFException; +using LAMMPS_NS::ValueTokenizer; namespace ReaxFF { @@ -538,17 +541,20 @@ namespace ReaxFF { } } - // next line is number of hydrogen bond parameters - - values = reader.next_values(0); - n = values.next_int(); - ++lineno; + // next line is number of hydrogen bond parameters. that block may be missing for (i = 0; i < ntypes; ++i) for (j = 0; j < ntypes; ++j) for (k = 0; k < ntypes; ++k) hbp[i][j][k].r0_hb = -1.0; + auto thisline = reader.next_line(); + if (!thisline) throw EOFException("ReaxFF parameter file has no hydrogen bond parameters"); + + values = ValueTokenizer(thisline); + n = values.next_int(); + ++lineno; + for (i = 0; i < n; ++i) { values = reader.next_values(0); ++lineno; @@ -570,6 +576,8 @@ namespace ReaxFF { } memory->destroy(tor_flag); + } catch (EOFException &e) { + error->warning(FLERR, e.what()); } catch (std::exception &e) { error->one(FLERR,e.what()); } From f8495eaf759bdc975b878b1b947f05d986b25873 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 21 Dec 2023 23:59:35 -0500 Subject: [PATCH 142/189] make certain nbonds is initialized --- src/REAXFF/compute_reaxff_atom.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/REAXFF/compute_reaxff_atom.cpp b/src/REAXFF/compute_reaxff_atom.cpp index cc9109915c..1834de0b4b 100644 --- a/src/REAXFF/compute_reaxff_atom.cpp +++ b/src/REAXFF/compute_reaxff_atom.cpp @@ -44,6 +44,7 @@ ComputeReaxFFAtom::ComputeReaxFFAtom(LAMMPS *lmp, int narg, char **arg) : // initialize output nlocal = -1; + nbonds = 0; prev_nbonds = -1; size_peratom_cols = 3; From a16a1a768c090adba55f2a16437bfd489e4b3977 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 22 Dec 2023 00:04:16 -0500 Subject: [PATCH 143/189] fix cut-n-paste bug and avoid uninitialized data access --- src/EXTRA-FIX/fix_nonaffine_displacement.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-FIX/fix_nonaffine_displacement.cpp b/src/EXTRA-FIX/fix_nonaffine_displacement.cpp index ef5481601f..c1de50c41d 100644 --- a/src/EXTRA-FIX/fix_nonaffine_displacement.cpp +++ b/src/EXTRA-FIX/fix_nonaffine_displacement.cpp @@ -71,6 +71,7 @@ FixNonaffineDisplacement::FixNonaffineDisplacement(LAMMPS *lmp, int narg, char * nevery = utils::inumeric(FLERR, arg[3], false, lmp); if (nevery <= 0) error->all(FLERR,"Illegal nevery value {} in fix nonaffine/displacement", nevery); + reference_timestep = update_timestep = offset_timestep = -1; int iarg = 4; if (strcmp(arg[iarg], "integrated") == 0) { nad_style = INTEGRATED; @@ -99,7 +100,7 @@ FixNonaffineDisplacement::FixNonaffineDisplacement(LAMMPS *lmp, int narg, char * if (strcmp(arg[iarg], "fixed") == 0) { reference_style = FIXED; reference_timestep = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); - if (update_timestep < 0) + if (reference_timestep < 0) error->all(FLERR, "Illegal reference timestep {} in fix nonaffine/displacement", arg[iarg + 1]); } else if (strcmp(arg[iarg], "update") == 0) { reference_style = UPDATE; From c4124c099584d4aca84d8a0ad140f0b732c08f5b Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 22 Dec 2023 10:28:45 -0500 Subject: [PATCH 144/189] correct statement about Xeon Phi support --- doc/src/package.rst | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/doc/src/package.rst b/doc/src/package.rst index 63a7f095ad..ba86835694 100644 --- a/doc/src/package.rst +++ b/doc/src/package.rst @@ -344,12 +344,10 @@ specify additional flags for the runtime build. ---------- -The *intel* style invokes settings associated with the use of the -INTEL package. All of its settings, except the *omp* and *mode* -keywords, are ignored if LAMMPS was not built with Xeon Phi -co-processor support. All of its settings, including the *omp* and -*mode* keyword are applicable if LAMMPS was built with co-processor -support. +The *intel* style invokes settings associated with the use of the INTEL +package. The keywords *balance*, *ghost*, *tpc*, and *tptask* are +**only** applicable if LAMMPS was built with Xeon Phi co-processor +support and are otherwise ignored. The *Nphi* argument sets the number of co-processors per node. This can be set to any value, including 0, if LAMMPS was not From efa37fff7f3cc161b98df5743d24a403109cb4ee Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 22 Dec 2023 11:31:31 -0500 Subject: [PATCH 145/189] remove cached copy of "layout" since this was not always initialized when used --- src/KOKKOS/grid3d_kokkos.cpp | 4 ++-- src/grid2d.cpp | 24 +++++++++--------------- src/grid2d.h | 1 - src/grid3d.cpp | 24 +++++++++--------------- src/grid3d.h | 1 - 5 files changed, 20 insertions(+), 34 deletions(-) diff --git a/src/KOKKOS/grid3d_kokkos.cpp b/src/KOKKOS/grid3d_kokkos.cpp index 9a82e0157d..7b97c417dd 100644 --- a/src/KOKKOS/grid3d_kokkos.cpp +++ b/src/KOKKOS/grid3d_kokkos.cpp @@ -640,7 +640,7 @@ void Grid3dKokkos::forward_comm(int caller, void *ptr, int which, in MPI_Datatype datatype) { if (caller == KSPACE) { - if (layout != Comm::LAYOUT_TILED) + if (comm->layout != Comm::LAYOUT_TILED) forward_comm_kspace_brick((KSpace *) ptr,which,nper,k_buf1,k_buf2,datatype); else forward_comm_kspace_tiled((KSpace *) ptr,which,nper,k_buf1,k_buf2,datatype); @@ -780,7 +780,7 @@ void Grid3dKokkos::reverse_comm(int caller, void *ptr, int which, in MPI_Datatype datatype) { if (caller == KSPACE) { - if (layout != Comm::LAYOUT_TILED) + if (comm->layout != Comm::LAYOUT_TILED) reverse_comm_kspace_brick((KSpace *) ptr,which,nper,k_buf1,k_buf2,datatype); else reverse_comm_kspace_tiled((KSpace *) ptr,which,nper,k_buf1,k_buf2,datatype); diff --git a/src/grid2d.cpp b/src/grid2d.cpp index 74fd105ec0..d39b7b4a78 100644 --- a/src/grid2d.cpp +++ b/src/grid2d.cpp @@ -114,7 +114,7 @@ Grid2d::Grid2d(LAMMPS *lmp, MPI_Comm gcomm, int gnx, int gny, int ixlo, int ixhi // additional intialization // other constructor invokes this from setup_grid() - initialize(); + Grid2d::initialize(); } /* ---------------------------------------------------------------------- */ @@ -522,7 +522,7 @@ void Grid2d::ghost_grid() // also ensure no other procs use ghost cells beyond +y limit if (yextra) { - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { if (comm->myloc[1] == comm->procgrid[1]-1) inyhi = outyhi = ny - 1; } else { if (comm->mysplit[1][1] == 1.0) inyhi = outyhi = ny - 1; @@ -553,15 +553,13 @@ void Grid2d::ghost_grid() void Grid2d::extract_comm_info() { - layout = comm->layout; - // for non TILED layout: // proc xyz lohi = my 4 neighbor procs in this MPI_Comm // these proc IDs can be overridden by caller using set_proc_neighs() // xyz split = copy of 1d vectors in Comm // grid2proc = copy of 3d array in Comm - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { procxlo = comm->procneigh[0][0]; procxhi = comm->procneigh[0][1]; procylo = comm->procneigh[1][0]; @@ -585,7 +583,7 @@ void Grid2d::extract_comm_info() // RCBinfo.cut = this proc's inlo in that dim // Allgather creates the tree of dims and cuts - if (layout == Comm::LAYOUT_TILED) { + if (comm->layout == Comm::LAYOUT_TILED) { rcbinfo = (RCBinfo *) memory->smalloc(nprocs*sizeof(RCBinfo),"grid3d:rcbinfo"); RCBinfo rcbone; @@ -615,7 +613,7 @@ void Grid2d::extract_comm_info() void Grid2d::setup_comm(int &nbuf1, int &nbuf2) { - if (layout != Comm::LAYOUT_TILED) setup_comm_brick(nbuf1,nbuf2); + if (comm->layout != Comm::LAYOUT_TILED) setup_comm_brick(nbuf1,nbuf2); else setup_comm_tiled(nbuf1,nbuf2); } @@ -1039,7 +1037,7 @@ void Grid2d::setup_comm_tiled(int &nbuf1, int &nbuf2) int Grid2d::ghost_adjacent() { - if (layout != Comm::LAYOUT_TILED) return ghost_adjacent_brick(); + if (comm->layout != Comm::LAYOUT_TILED) return ghost_adjacent_brick(); return ghost_adjacent_tiled(); } @@ -1085,7 +1083,7 @@ int Grid2d::ghost_adjacent_tiled() void Grid2d::forward_comm(int caller, void *ptr, int which, int nper, int nbyte, void *buf1, void *buf2, MPI_Datatype datatype) { - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { if (caller == KSPACE) forward_comm_brick((KSpace *) ptr,which,nper,nbyte, buf1,buf2,datatype); @@ -1190,7 +1188,7 @@ forward_comm_tiled(T *ptr, int which, int nper, int nbyte, void Grid2d::reverse_comm(int caller, void *ptr, int which, int nper, int nbyte, void *buf1, void *buf2, MPI_Datatype datatype) { - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { if (caller == KSPACE) reverse_comm_brick((KSpace *) ptr,which,nper,nbyte, buf1,buf2,datatype); @@ -1314,10 +1312,6 @@ void Grid2d::setup_remap(Grid2d *old, int &nremap_buf1, int &nremap_buf2) deallocate_remap(); - // set layout to current Comm layout - - layout = comm->layout; - // overlaps of my old decomp owned box with all owned boxes in new decomp // noverlap_old = # of overlaps, including self // overlap_old = vector of overlap info in Overlap data struct @@ -1654,7 +1648,7 @@ int Grid2d::compute_overlap(int ghostflag, int *box, int *pbc, Overlap *&overlap // test obox against appropriate layout - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { // find comm->procgrid indices in each dim for box bounds diff --git a/src/grid2d.h b/src/grid2d.h index 43316baad8..8316f840be 100644 --- a/src/grid2d.h +++ b/src/grid2d.h @@ -55,7 +55,6 @@ class Grid2d : protected Pointers { protected: int me, nprocs; - int layout; // not TILED or TILED, same as Comm class MPI_Comm gridcomm; // communicator for this class // usually world, but MSM calls with subset diff --git a/src/grid3d.cpp b/src/grid3d.cpp index c6cff3f317..6ca8f7895c 100644 --- a/src/grid3d.cpp +++ b/src/grid3d.cpp @@ -123,7 +123,7 @@ Grid3d::Grid3d(LAMMPS *lmp, MPI_Comm gcomm, int gnx, int gny, int gnz, // additional intialization // other constructor invokes this from setup_grid() - initialize(); + Grid3d::initialize(); } /* ---------------------------------------------------------------------- */ @@ -577,7 +577,7 @@ void Grid3d::ghost_grid() // also ensure no other procs use ghost cells beyond +z limit if (zextra) { - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { if (comm->myloc[2] == comm->procgrid[2]-1) inzhi = outzhi = nz - 1; } else { if (comm->mysplit[2][1] == 1.0) inzhi = outzhi = nz - 1; @@ -613,15 +613,13 @@ void Grid3d::ghost_grid() void Grid3d::extract_comm_info() { - layout = comm->layout; - // for non TILED layout: // proc xyz lohi = my 6 neighbor procs in this MPI_Comm // these proc IDs can be overridden by caller using set_proc_neighs() // xyz split = copy of 1d vectors in Comm // grid2proc = copy of 3d array in Comm - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { procxlo = comm->procneigh[0][0]; procxhi = comm->procneigh[0][1]; procylo = comm->procneigh[1][0]; @@ -649,7 +647,7 @@ void Grid3d::extract_comm_info() // RCBinfo.cut = this proc's inlo in that dim // Allgather creates the tree of dims and cuts - if (layout == Comm::LAYOUT_TILED) { + if (comm->layout == Comm::LAYOUT_TILED) { rcbinfo = (RCBinfo *) memory->smalloc(nprocs*sizeof(RCBinfo),"grid3d:rcbinfo"); RCBinfo rcbone; @@ -680,7 +678,7 @@ void Grid3d::extract_comm_info() void Grid3d::setup_comm(int &nbuf1, int &nbuf2) { - if (layout != Comm::LAYOUT_TILED) setup_comm_brick(nbuf1,nbuf2); + if (comm->layout != Comm::LAYOUT_TILED) setup_comm_brick(nbuf1,nbuf2); else setup_comm_tiled(nbuf1,nbuf2); } @@ -1207,7 +1205,7 @@ void Grid3d::setup_comm_tiled(int &nbuf1, int &nbuf2) int Grid3d::ghost_adjacent() { - if (layout != Comm::LAYOUT_TILED) return ghost_adjacent_brick(); + if (comm->layout != Comm::LAYOUT_TILED) return ghost_adjacent_brick(); return ghost_adjacent_tiled(); } @@ -1255,7 +1253,7 @@ int Grid3d::ghost_adjacent_tiled() void Grid3d::forward_comm(int caller, void *ptr, int which, int nper, int nbyte, void *buf1, void *buf2, MPI_Datatype datatype) { - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { if (caller == KSPACE) forward_comm_brick((KSpace *) ptr,which,nper,nbyte, buf1,buf2,datatype); @@ -1360,7 +1358,7 @@ forward_comm_tiled(T *ptr, int which, int nper, int nbyte, void Grid3d::reverse_comm(int caller, void *ptr, int which, int nper, int nbyte, void *buf1, void *buf2, MPI_Datatype datatype) { - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { if (caller == KSPACE) reverse_comm_brick((KSpace *) ptr,which,nper,nbyte, buf1,buf2,datatype); @@ -1484,10 +1482,6 @@ void Grid3d::setup_remap(Grid3d *old, int &nremap_buf1, int &nremap_buf2) deallocate_remap(); - // set layout to current Comm layout - - layout = comm->layout; - // overlaps of my old decomp owned box with all owned boxes in new decomp // noverlap_old = # of overlaps, including self // overlap_old = vector of overlap info in Overlap data struct @@ -1829,7 +1823,7 @@ int Grid3d::compute_overlap(int ghostflag, int *box, int *pbc, Overlap *&overlap return noverlap_list; } - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { // find comm->procgrid indices in each dim for box bounds diff --git a/src/grid3d.h b/src/grid3d.h index e4a8e276f5..6a15c2c942 100644 --- a/src/grid3d.h +++ b/src/grid3d.h @@ -57,7 +57,6 @@ class Grid3d : protected Pointers { protected: int me, nprocs; - int layout; // not TILED or TILED, same as Comm class MPI_Comm gridcomm; // communicator for this class // usually world, but MSM calls with subset From c964d8cda83b894cb51b787a9f831567daf4cb81 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 22 Dec 2023 11:35:35 -0500 Subject: [PATCH 146/189] skip python tests using numpy that fail randomly on macOS --- unittest/python/CMakeLists.txt | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/unittest/python/CMakeLists.txt b/unittest/python/CMakeLists.txt index b4ba281d93..f3b851620c 100644 --- a/unittest/python/CMakeLists.txt +++ b/unittest/python/CMakeLists.txt @@ -84,20 +84,26 @@ if(Python_EXECUTABLE) WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) set_tests_properties(PythonCommands PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") - add_test(NAME PythonNumpy - COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-numpy.py -v - WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) - set_tests_properties(PythonNumpy PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + # randomly failing on macOS with python 3.12 + if(NOT APPLE) + add_test(NAME PythonNumpy + COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-numpy.py -v + WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) + set_tests_properties(PythonNumpy PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + endif() add_test(NAME PythonCapabilities COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-capabilities.py -v WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) set_tests_properties(PythonCapabilities PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") - add_test(NAME PythonPyLammps - COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-pylammps.py -v - WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) - set_tests_properties(PythonPyLammps PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + # randomly failing on macOS with python 3.12 + if(NOT APPLE) + add_test(NAME PythonPyLammps + COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-pylammps.py -v + WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) + set_tests_properties(PythonPyLammps PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + endif() add_test(NAME PythonFormats COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-formats.py -v From 5b00ad8f719b8956ee0152c0ae757895132e47b0 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 22 Dec 2023 13:37:20 -0500 Subject: [PATCH 147/189] flag error if using INTEL package kspace styles with run style verlet/split --- src/INTEL/fix_intel.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/INTEL/fix_intel.cpp b/src/INTEL/fix_intel.cpp index cb60149885..30d119dd6a 100644 --- a/src/INTEL/fix_intel.cpp +++ b/src/INTEL/fix_intel.cpp @@ -553,6 +553,9 @@ void FixIntel::kspace_init_check() if (intel_pair == 0) error->all(FLERR,"Intel styles for kspace require intel pair style."); + + if (utils::strmatch(update->integrate_style, "^verlet/split")) + error->all(FLERR,"Intel styles for kspace are not compatible with run_style verlet/split"); } /* ---------------------------------------------------------------------- */ From 7cfbe775326570d17c6974df6cda89e984fe5a5d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 22 Dec 2023 13:42:56 -0500 Subject: [PATCH 148/189] report incompatibility of verlet/split with INTEL package kspace styles --- doc/src/run_style.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/src/run_style.rst b/doc/src/run_style.rst index 0804ce5c82..d2e47c0884 100644 --- a/doc/src/run_style.rst +++ b/doc/src/run_style.rst @@ -329,7 +329,8 @@ Restrictions The *verlet/split* style can only be used if LAMMPS was built with the REPLICA package. Correspondingly the *respa/omp* style is available only if the OPENMP package was included. See the :doc:`Build package -` page for more info. +` page for more info. It is not compatible with +kspace styles from the INTEL package. Whenever using rRESPA, the user should experiment with trade-offs in speed and accuracy for their system, and verify that they are From f147df8b4475e8306296b341f5e7259ac2c90c0d Mon Sep 17 00:00:00 2001 From: oywg11 Date: Sat, 23 Dec 2023 08:29:15 +0800 Subject: [PATCH 149/189] add full list of atom pairs --- potentials/CBNOH.aip.water.2dm | 37 +++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/potentials/CBNOH.aip.water.2dm b/potentials/CBNOH.aip.water.2dm index 5efcc99fcc..dae5e2c5d4 100755 --- a/potentials/CBNOH.aip.water.2dm +++ b/potentials/CBNOH.aip.water.2dm @@ -3,24 +3,13 @@ # CITATION: Z. Feng, ..., and W. Ouyang, Langmuir 39(50), 18198-18207 (2023). # Anisotropic Potential (AIP) for water/graphene and water/hBN heterojunctions # The parameters below are fitted against the PBE + MBD-NL (graphene/water) and SCAN (hBN/water) DFT reference data. + +# The parameters for bilayer graphene/graphene, graphene/hBN and hBN/hBN junctions are taken from +# CITATION: Ouyang, Mandelli, Urbakh, Hod, Nano Letters 18, 6009-6016 (2018). # # -------------------Repulsion Potential ------------------++++++++++++++ Vdw Potential ++++++++++++++++************ # beta(A) alpha delta(A) epsilon(meV) C(meV) d sR reff(A) C6(meV*A^6) S rcut # -# For graphene and hydrocarbons -C C 3.205843 7.511126 1.235334 1.528338E-5 37.530428 15.499947 0.7954443 3.681440 25.714535E3 1.0 2.0 -H H 3.974540 6.53799 1.080633 0.6700556 0.8333833 15.022371 0.7490632 2.767223 1.6159581E3 1.0 1.2 -C H 2.642950 12.91410 1.020257 0.9750012 25.340996 15.222927 0.8115998 3.887324 5.6874617E3 1.0 1.5 -H C 2.642950 12.91410 1.020257 0.9750012 25.340996 15.222927 0.8115998 3.887324 5.6874617E3 1.0 1.5 - -# For hBN -B B 3.143737 9.825139 1.936405 2.7848400 14.495957 15.199263 0.7834022 3.682950 49.498013E3 1.0 2.0 -N N 3.443196 7.084490 1.747349 2.9139991 46.508553 15.020370 0.8008370 3.551843 14.810151E3 1.0 2.0 -B N 3.295257 7.224311 2.872667 1.3715032 0.4347152 14.594578 0.8044028 3.765728 24.669996E3 1.0 2.0 -B H 2.718657 9.214551 3.273063 14.015714 14.760509 15.084752 0.7768383 3.640866 7.9642467E3 1.0 1.5 -N B 3.295257 7.224311 2.872667 1.3715032 0.4347152 14.594578 0.8044028 3.765728 24.669996E3 1.0 2.0 -H B 2.718657 9.214551 3.273063 14.015714 14.760509 15.084752 0.7768383 3.640866 7.9642467E3 1.0 1.5 - # For water-graphene C Ow 5.453696 6.181724 1.250255 3.349092 0.687806 9.057065 1.232495 2.775772 100226.555031 1.0 2.0 C Hw 2.553809 9.686644 1.964892 41.776171 -16.300128 9.015685 0.744155 2.415456 7409.128564 1.0 2.0 @@ -37,6 +26,26 @@ Hw N 4.029390 5.360546 0.950352 15.945549 -1.486701 10.797276 1.352684 2 Ow B 3.907514 7.842519 2.380078 32.122737 1.190485 17.482482 0.788174 2.368217 139539.370785 1.0 1.2 Hw B 3.804966 2.356248 1.114761 9.193309 -5.922514 9.000572 1.334703 1.746122 43796.489158 1.0 1.2 +# For graphene and hydrocarbons +C C 3.205843 7.511126 1.235334 1.528338E-5 37.530428 15.499947 0.7954443 3.681440 25.714535E3 1.0 2.0 +H H 3.974540 6.53799 1.080633 0.6700556 0.8333833 15.022371 0.7490632 2.767223 1.6159581E3 1.0 1.2 +C H 2.642950 12.91410 1.020257 0.9750012 25.340996 15.222927 0.8115998 3.887324 5.6874617E3 1.0 1.5 +H C 2.642950 12.91410 1.020257 0.9750012 25.340996 15.222927 0.8115998 3.887324 5.6874617E3 1.0 1.5 + +# For hBN +B B 3.143737 9.825139 1.936405 2.7848400 14.495957 15.199263 0.7834022 3.682950 49.498013E3 1.0 2.0 +N N 3.443196 7.084490 1.747349 2.9139991 46.508553 15.020370 0.8008370 3.551843 14.810151E3 1.0 2.0 +B N 3.295257 7.224311 2.872667 1.3715032 0.4347152 14.594578 0.8044028 3.765728 24.669996E3 1.0 2.0 +B H 2.718657 9.214551 3.273063 14.015714 14.760509 15.084752 0.7768383 3.640866 7.9642467E3 1.0 1.5 +N B 3.295257 7.224311 2.872667 1.3715032 0.4347152 14.594578 0.8044028 3.765728 24.669996E3 1.0 2.0 +H B 2.718657 9.214551 3.273063 14.015714 14.760509 15.084752 0.7768383 3.640866 7.9642467E3 1.0 1.5 + +# For graphene-hBN +C B 3.303662 10.54415 2.926741 16.719972 0.3571734 15.305254 0.7001581 3.097327 30.162869E3 1.0 2.0 +C N 3.253564 8.825921 1.059550 18.344740 21.913573 15.000000 0.7234983 3.013117 19.063095E3 1.0 2.0 +B C 3.303662 10.54415 2.926741 16.719972 0.3571734 15.305254 0.7001581 3.097327 30.162869E3 1.0 2.0 +N C 3.253564 8.825921 1.059550 18.344740 21.913573 15.000000 0.7234983 3.013117 19.063095E3 1.0 2.0 + # # The AIPs for other elements are tunred off H Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 H Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 From 0515f071727904702c8a940d88ef7ead41576a3e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 23 Dec 2023 02:51:21 -0500 Subject: [PATCH 150/189] whitespace --- doc/src/pair_aip_water_2dm.rst | 2 +- potentials/CBNOH.aip.water.2dm | 48 +++++++++++++++++----------------- potentials/TMD.ILP | 4 +-- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/doc/src/pair_aip_water_2dm.rst b/doc/src/pair_aip_water_2dm.rst index f6c56eff6e..b84202e69e 100644 --- a/doc/src/pair_aip_water_2dm.rst +++ b/doc/src/pair_aip_water_2dm.rst @@ -31,7 +31,7 @@ Examples pair_coeff * * aip/water/2dm CBNOH.aip.water.2dm C Ow Hw pair_style hybrid/overlay aip/water/2dm 16.0 lj/cut/tip4p/long 3 4 1 1 0.1546 10 8.5 coul/shield 16.0 1 - pair_coeff 1*2 1*2 none + pair_coeff 1*2 1*2 none pair_coeff 3 3 lj/cut/tip4p/long 8.0313e-3 3.1589 # O-O pair_coeff 3 4 lj/cut/tip4p/long 0.0 0.0 # O-H pair_coeff 4 4 lj/cut/tip4p/long 0.0 0.0 # H-H diff --git a/potentials/CBNOH.aip.water.2dm b/potentials/CBNOH.aip.water.2dm index dae5e2c5d4..83205c354f 100755 --- a/potentials/CBNOH.aip.water.2dm +++ b/potentials/CBNOH.aip.water.2dm @@ -1,4 +1,4 @@ -# DATE: 2023-12-20 UNITS: metal CONTRIBUTOR: Wengen Ouyang w.g.ouyang@gmail.com +# DATE: 2023-12-20 UNITS: metal CONTRIBUTOR: Wengen Ouyang w.g.ouyang@gmail.com # CITATION: Z. Feng, ..., and W. Ouyang, J. Phys. Chem. C 127(18), 8704 (2023). # CITATION: Z. Feng, ..., and W. Ouyang, Langmuir 39(50), 18198-18207 (2023). # Anisotropic Potential (AIP) for water/graphene and water/hBN heterojunctions @@ -11,20 +11,20 @@ # beta(A) alpha delta(A) epsilon(meV) C(meV) d sR reff(A) C6(meV*A^6) S rcut # # For water-graphene -C Ow 5.453696 6.181724 1.250255 3.349092 0.687806 9.057065 1.232495 2.775772 100226.555031 1.0 2.0 -C Hw 2.553809 9.686644 1.964892 41.776171 -16.300128 9.015685 0.744155 2.415456 7409.128564 1.0 2.0 -Ow C 5.453696 6.181724 1.250255 3.349092 0.687806 9.057065 1.232495 2.775772 100226.555031 1.0 1.2 -Hw C 2.553809 9.686644 1.964892 41.776171 -16.300128 9.015685 0.744155 2.415456 7409.128564 1.0 1.2 +C Ow 5.453696 6.181724 1.250255 3.349092 0.687806 9.057065 1.232495 2.775772 100226.555031 1.0 2.0 +C Hw 2.553809 9.686644 1.964892 41.776171 -16.300128 9.015685 0.744155 2.415456 7409.128564 1.0 2.0 +Ow C 5.453696 6.181724 1.250255 3.349092 0.687806 9.057065 1.232495 2.775772 100226.555031 1.0 1.2 +Hw C 2.553809 9.686644 1.964892 41.776171 -16.300128 9.015685 0.744155 2.415456 7409.128564 1.0 1.2 # For water-hBN -N Ow 3.530598 16.377816 1.285374 1.717537 1.339337 24.797794 0.771411 3.928357 33589.850651 1.0 2.0 -N Hw 4.029390 5.360546 0.950352 15.945549 -1.486701 10.797276 1.352684 2.293775 41247.181447 1.0 2.0 -B Ow 3.907514 7.842519 2.380078 32.122737 1.190485 17.482482 0.788174 2.368217 139539.370785 1.0 2.0 -B Hw 3.804966 2.356248 1.114761 9.193309 -5.922514 9.000572 1.334703 1.746122 43796.489158 1.0 2.0 -Ow N 3.530598 16.377816 1.285374 1.717537 1.339337 24.797794 0.771411 3.928357 33589.850651 1.0 1.2 -Hw N 4.029390 5.360546 0.950352 15.945549 -1.486701 10.797276 1.352684 2.293775 41247.181447 1.0 1.2 -Ow B 3.907514 7.842519 2.380078 32.122737 1.190485 17.482482 0.788174 2.368217 139539.370785 1.0 1.2 -Hw B 3.804966 2.356248 1.114761 9.193309 -5.922514 9.000572 1.334703 1.746122 43796.489158 1.0 1.2 +N Ow 3.530598 16.377816 1.285374 1.717537 1.339337 24.797794 0.771411 3.928357 33589.850651 1.0 2.0 +N Hw 4.029390 5.360546 0.950352 15.945549 -1.486701 10.797276 1.352684 2.293775 41247.181447 1.0 2.0 +B Ow 3.907514 7.842519 2.380078 32.122737 1.190485 17.482482 0.788174 2.368217 139539.370785 1.0 2.0 +B Hw 3.804966 2.356248 1.114761 9.193309 -5.922514 9.000572 1.334703 1.746122 43796.489158 1.0 2.0 +Ow N 3.530598 16.377816 1.285374 1.717537 1.339337 24.797794 0.771411 3.928357 33589.850651 1.0 1.2 +Hw N 4.029390 5.360546 0.950352 15.945549 -1.486701 10.797276 1.352684 2.293775 41247.181447 1.0 1.2 +Ow B 3.907514 7.842519 2.380078 32.122737 1.190485 17.482482 0.788174 2.368217 139539.370785 1.0 1.2 +Hw B 3.804966 2.356248 1.114761 9.193309 -5.922514 9.000572 1.334703 1.746122 43796.489158 1.0 1.2 # For graphene and hydrocarbons C C 3.205843 7.511126 1.235334 1.528338E-5 37.530428 15.499947 0.7954443 3.681440 25.714535E3 1.0 2.0 @@ -41,18 +41,18 @@ N B 3.295257 7.224311 2.872667 1.3715032 0.4347152 14.594578 0.804402 H B 2.718657 9.214551 3.273063 14.015714 14.760509 15.084752 0.7768383 3.640866 7.9642467E3 1.0 1.5 # For graphene-hBN -C B 3.303662 10.54415 2.926741 16.719972 0.3571734 15.305254 0.7001581 3.097327 30.162869E3 1.0 2.0 +C B 3.303662 10.54415 2.926741 16.719972 0.3571734 15.305254 0.7001581 3.097327 30.162869E3 1.0 2.0 C N 3.253564 8.825921 1.059550 18.344740 21.913573 15.000000 0.7234983 3.013117 19.063095E3 1.0 2.0 -B C 3.303662 10.54415 2.926741 16.719972 0.3571734 15.305254 0.7001581 3.097327 30.162869E3 1.0 2.0 +B C 3.303662 10.54415 2.926741 16.719972 0.3571734 15.305254 0.7001581 3.097327 30.162869E3 1.0 2.0 N C 3.253564 8.825921 1.059550 18.344740 21.913573 15.000000 0.7234983 3.013117 19.063095E3 1.0 2.0 -# # The AIPs for other elements are tunred off -H Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 -H Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 -Ow H 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 -Hw H 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +# The AIPs for other elements are turned off +H Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +H Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Ow H 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Hw H 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 -Ow Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 -Hw Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 -Ow Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 -Hw Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Ow Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Hw Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Ow Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Hw Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 diff --git a/potentials/TMD.ILP b/potentials/TMD.ILP index e7a9cbe558..18563199c9 100644 --- a/potentials/TMD.ILP +++ b/potentials/TMD.ILP @@ -1,10 +1,10 @@ -# DATE: 2021-12-02 UNITS: metal CONTRIBUTOR: Wengen Ouyang w.g.ouyang@gmail.com +# DATE: 2021-12-02 UNITS: metal CONTRIBUTOR: Wengen Ouyang w.g.ouyang@gmail.com # CITATION: W. Ouyang, et al., J. Chem. Theory Comput. 17, 7237 (2021). # CITATION: W. Jiang, et al., J. Phys. Chem. A, 127, 46, 9820–9830 (2023). # Interlayer Potential (ILP) for bilayer and bulk Group-VI Transition Metal Dichalcogenides. # The parameters below are fitted against the HSE + MBD-NL DFT reference data. # -# -------------------- Repulsion Potential -------------------++++++++++++++++ Vdw Potential ++++++++++++++++********* +# -------------------- Repulsion Potential -------------------++++++++++++++++ Vdw Potential ++++++++++++++++********* # beta(A) alpha delta(A) epsilon(meV) C(meV) d sR reff(A) C6(meV*A^6) S rcut Mo Mo 5.579450 9.377662 2.027222 144.151775 97.978570 89.437597 2.059031 5.122055 491850.316195 1.0 4.0 W W 5.530854 6.624992 1.983208 0.271792 140.174059 107.392585 1.356333 4.437591 691850.243962 1.0 4.0 From bdb0fc6b7a294b87569a977d413f09faec04600f Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 23 Dec 2023 11:35:39 -0500 Subject: [PATCH 151/189] replace non-ASCII character --- doc/src/pair_ilp_tmd.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/pair_ilp_tmd.rst b/doc/src/pair_ilp_tmd.rst index b5d9ad60b5..575bafdc91 100644 --- a/doc/src/pair_ilp_tmd.rst +++ b/doc/src/pair_ilp_tmd.rst @@ -167,4 +167,4 @@ tap_flag = 1 .. _Jiang: -**(Jiang)** W. Jiang, et al., J. Phys. Chem. A, 127, 46, 9820–9830 (2023). +**(Jiang)** W. Jiang, et al., J. Phys. Chem. A, 127, 46, 9820-9830 (2023). From b01065b3986be4be244d45470a975008f6763ea2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 23 Dec 2023 11:57:58 -0500 Subject: [PATCH 152/189] use "neato" layout only for lammps-classes.dot to avoid issues with recent graphviz versions --- doc/graphviz/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/graphviz/Makefile b/doc/graphviz/Makefile index a3e0c94c63..00b651e888 100644 --- a/doc/graphviz/Makefile +++ b/doc/graphviz/Makefile @@ -16,8 +16,11 @@ clean: rm -f $(IMGSVG) $(IMGPDF) $(IMGPNG) *~ ifeq ($(HAS_DOT),YES) -$(IMGDIR)/%.png: %.dot +$(IMGDIR)/lammps-classes.png : lammps-classes.dot dot -Tpng -Kneato -o $@ $< + +$(IMGDIR)/%.png: %.dot + dot -Tpng -Kdot -o $@ $< endif ifeq ($(HAS_DOT),NO) From 5d9f38433879fc8c6cb79bc10febf1a94f531b18 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 23 Dec 2023 11:58:40 -0500 Subject: [PATCH 153/189] recreate graphvis images with graphviz 8.1 --- doc/src/JPG/lammps-classes.png | Bin 293226 -> 311133 bytes doc/src/JPG/lammps-invoke-python.png | Bin 37244 -> 38861 bytes doc/src/JPG/pylammps-invoke-lammps.png | Bin 36936 -> 36838 bytes doc/src/JPG/python-invoke-lammps.png | Bin 28981 -> 28457 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/src/JPG/lammps-classes.png b/doc/src/JPG/lammps-classes.png index 71b5af9cccc92fa5594d34a032f5812efca037aa..d7ed506ab3c97e94af00e70769093da1be631d96 100644 GIT binary patch literal 311133 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
  • 678A?NMaTg-R> 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

    =OvmNb+I6y)o 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

    P6Z&oouMBQ;EAb#;G}a*XlN z-ZtizDnvW>Q{TILxB2hBn<~C_S5#Dlu4Mi*Sj^Dq{v#4^0EY?DKE*vUI{HZecoj_c z=ocUhzU4t-I^l

    E6A2d%v57v=SwbaAm?;qyai5LlJaD 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 z�I+Yu06|B-xxfJfhvoX_WA2v!2s2h)#+0j;+ZUY&Un=AF}L!hdDpACQU(g{KqLx6&3D3l;)BLlh)n6^RQ4^q5Z9&K$Q2Wxd;r)aJ2oa3a<$pi#u%Rv%M@;5OKfE=d5EnjC&0`w&A&qDBRb>o zU@(czQf(YjOl8L=n9N+zB36!ARtz^OYf08dQ`QVRkIE*KM$OX7>Ig?D)#JeiATS|G zNUEDd?26ZVIWqV~Kx317EQzz|z3kFO)c|_G7@&pPArrx$HslsnSRBw0=)Xqc4hyTO zxO-Vy0c0fI{k`iGSOUX|5HmKRSdnxFatCLFER)K3iP3-$9F=}gi%h7RM3nDN2aYM~ z9~L^MkBf@%s@XyjN1J1?;NQ*)niat~5iQG;-APD zJ%MCab6nu-i|ayC1kQu%0TvYJDFE+elrAFS&M=L!q;cqT}G2Pc5{MPo|P1;9W7Z=l?nXWx-pYXMO-$H=f zSqJJjOlvWNJgJ5%<}d}atdF%d{jAY-D$k}Ndu=isAaYp2Wk-S@XnEUBC zuhOK+R$a)MT^}ye7Qc|aMPNfB!v^yZXe)kiZ`;3rwt5Oofo1^BbyorPILA_U@b%q6 z>l7@Y)@R-pS?_iDyrV9cjPY)d<`?bgJZM8%Sy}6xQDDJLPxm7?$g&RL z8fsH(q6K5+s>HRcNV~YNxCeYIe$^IUhkD)IhgzW5wKJIT7nUPLGmEvC$qL4YqvD>@ z$;$QYzUHG6R~tu_{S=H|-MJ;Brlzk-yH}{Nwy?Ngt_`WWedQDm3oVqeor_cmto78gA;FY5aX0jJ~B`^yzh59I4x_+)qWRZr?rny@-mkR6LwHjkF8sU!0w{cObR#*e~*?EiT8fMu|7{^ z17b}S^!MLRk=-cUAGTK0H-&)P_55?x_ce-0Am%v38egdvnGEz$SVMXb9_3_{UXFm} zfg)1$)Zg|@|0`ri1C3|89yhC-WLrq1_9Pz^OM_R}{6)cUNPWzWjRlXg%Dhv6(8mPi zX9~0lsIT-lLSobHlT20N&~iZFBPty04FL#du5LP&lGWWUpq(U1Us2>dWr`RGFQ`Lc(3#ea5GSZhk*<^9nl)fuVBHng zZLJXick@U0?wJ#^3Rz_1XLQ~?Vo!>Jb)r{Ee<_p8{1AD|lI<~J80zH(%H znIIu-ZUx+^&@3K7l5Wd4x(d|_(HFC~%PE{RF$;;n&BN_NcStdkN<_sk#-c(I0*Gbq z@#9{R-Gl~Wdm`kE8_E#iS;9ZwZ&jYwL#R-=K?GEvbM*A!HA6d%8{m_{%L>WDpL zSiG{VGH*LU53ttDojbun&qL0}kfjYBF~(3mz>v)ArlorPzU$NTzZexUT(uzN0_}se z$e`qC@_`6L5xHuskH2^`JG&*X2K}oErP~&7QK_b+EQxzH9JiKb&)-dw8qdGxA0Uj0 zFlb`9!iZpA&6jUgN>o>-|5@hC#FJ<^FH{0nRupy8v2QY_t}};sV!|XoAVMO615i}m zIi>vpIU0vY-){|_r|F-C%pASXdI3_Tv%j}1Q{LF`FbM2n_r8X1PF!OV3T^3h0RxL8 zlaHsJ&2MC3_9QGcv<2&@3ky3@0kX>>*mw&ysM1B>$?QZq&Yy#LD5Hog+L1If<_jd` z`x>NY9%oC^SIgg^Gol?V8j6ssXei?xIrhOca1Qi7^83)Toc~EOGw{sa#N3NS=mqs; zc{N$}8eN#8nNoPMM~FxU%EmHou%_nmJf^B#ME%i)G%R9u(Q+WM_Mzy5p2$JsJ#fen zN28e;^Dyb7Q)kUXNAc;V_-E=etR6`-8GCNs53zG=pmg}9;HdNJ_G{K21A>oi`-CQx z(BxP#rF01B-Ygu?q3$<|ngcTg0lbl4^6Ooxiy7R(-K2U#&g=yXY|y--FE@fXl7B7g zOV-$=`xL^nQR0#XEy%0q@Q)_fVUF69zu)tzYKkOoyqhQ)p1*&8mx5hJtA#bmcx2&- zjFqC}3Xz99=SqFTcHs3qLJ*ZHDRbLUkg`@Gh=<3L9un8`uUte_Ik59z-mJDD~# zduu$<9Pw{EFzX>BR7<;QsHpNI3DPpK&0D#4`}TG|;RA}4*l1M?N++?P092rE&4xBO zosx39#=sTndVVqLpry-Pl{I{_`1_BcjL=}oA)^x$K_F2SmA9-GV}JUnych4^y+gXL z2f!4=v;(!Yf|eF@jF7oj4~UxGh*e-=!X(j9zaqBF!YtWqh@!J=gw+jt6`iTcTCkKy z$m#O1mn>N##z&OoVCMZ5V#33?a$|cl+Xc9i03J(kK)`=|ab?P~@8>8mOIbtH0vgq3&?wg~cSo~NH8oW48CO8vIOQpolas z{~9kjj^KKNrQ^@^{v7r!>zxzhAdWr1ukfqmq;Z?aODRT`XTU{EC_w$khw+gKpJsi=yfJq*X(T9-_9AWn~!RI-Gy67Brt4UxN;6>OG9PHjv=r>fRzGB)_DrF%}S_%qAgveGm6Y zePmc-(&(?$ICb+5GNkzJ7=J74@P0AcXmMX+cwQ#mKv#CXG^SuU);~s;ULzAM|F`k? zbqO~P-id4b@NdG;Ji|0V^|&9 zC)qXgHxxg~Yl4BoJvt@V*F0J1tLpea#4jg}{c?JGNyE+njT(~N1uI3`(Lr6E!t%k% z@ha`xf3n#ey?n3n2S3ocAhdC)LE=7V6vxo9PrVmXRBnCnC5WjRFwk(e*AV&TzG7st zyi5A-2m*R&0<4o}cY=Ip>cVET;0k1>3I^xrcse0wv*uDBtYL+w1p7d`So;dMTOH-Q z1=K!Z*A!!tcPXI7iG`4v^nuVkGatDB@w4j zFW&MBC)S*g-ICVz=ACt;eYO6(=a(hZ&6u9D0@|Uj5KF#=YyOhGGBh*)nWom*M5mjU zmxZNq{u`5+ihP5ph`!M*YtOv+4=mq(y&$t|%r}UQ3$#f*crJRR%ESCDY3B<2B1Fkb z^#+lH+EHRHj^1&1)%1rCh1jgXCuFz&Hb`{VS}cv5`e zi>GBR3lj;?&`&W$V|49-3IAI9W;YHtx4`N`I^f#B7gr9xd)|}+sEj|TR^vzST@lBWD>2qVu@tTS>5m~FWso9>WxmMA)VCy!?;Fwg=}-Vi;~yW#M%6}@$jmC{ z%CwodfwltOco=NFB7%8S8nI^2h|GuSS-uDN%paU>%^J{TH_lXhYavAog^hRmR!4$ zcMQ6_*k$dvUamKHJV&G!L(3=`}UakD)j)sR8M48_orRL z98Ts-Bw-M()4$XSo5i-%1H9$+GG(g%Djo910h|a3M3!^pz1zNR+hM*KF-NKaHpqN? zsMFfi+3Dn%r{~*sZt>-fPE_x8$DG<4*R_tbbuzN7nXtFfh-H^w-nQzKUEiqjm4`!L z)}}1;j?I(c`h7W)LOMLCzPXIlP3^My(=n)=(u#_Ns4o3=p$6Giv@+6Lpi+=CwKKL> zR&#)Wxz}5%Raq0hJ|$%^1JXZgFV#0aL?RUO2>dMG@C>;^d{3WW+ZsQj^6los|BtWt zj_dhv`~TlI*_4qol7^XNWG0mgX_+Z2D_HhN!QT} zrVE7M=sz$VLjWCg=cL&p3dB$VG%U?HFrdEI@0E+c$XXDA3)WN}@%pumFKaegzMPap zZ|eO^|Ci6}X9Cs_ItpyfpcBqpw)ShPc2U-~sULnUoY@qk%_jw8nJ6*R_wVMPC>pIop)$nKTJ9 z2OnPt%TRGBs{ix3leeX&bPtsHn1Jo2V-u6Ut;=Rx?b=mpX0}muW%r3=6eBvUOxukh`gE_ z*|3Y-Zr)wK-8!~86>CaHBxl{RE9uXR?D7FIi|Qk@#|5Dm5Eof@quZ&C<&^sE`Xzs7pZ4V!AB1a89lxP=@< z;?5XU@Q@lzpJt-7r`x`FGGl0re@Or=b5=^ZU4Ju{RZB%=&=n?GYAXjGzxslgahET% zA+*kQJWLWir+{U6_xi>r*K8z@^CdZ}^PIP7e3MV!$`vP5IJSHQx551CbdtxIjZJXm zUwD)cTO$`M1&2Hq`kAGu_3(WFOPOtbWWQP(JU)>mr+|%nQ(9_$zR{qQ2xt=KrtVsj zQ+L#;2)nmL*Ze1`(kP8&3!wZx0J3TXn9=VdObm5DHEjV@w6O2A_u#4X>5F(R!(f0c zN4H*eP)H7qU1X!q;<&<~?gRy{Vte)hGXtRkc*CZ1|KjdAQ#jS(5-G=n7C2oc^NZKoQ;i@$HB zhQF|5a%xob(Z3$%Wt=@{jww1`p0aR`_5A*RUfI7V20n^qU0B2|ts8}&dYY!iIIg6< z>dH=~Ua+MQO_Llhq;XPvvyI4@iWF-aX0N5C1yNiFzNwzxfx!@2tsr%x>}^phrV~}*s$U-GV5 z9q3z5+gATN6NAU$3J+$UJYHjktUAM?Sr`lVMevm|AxOLRTSYImviI%$ASHqG{63|I z(&*|-{XSn5Yjn2=cV~h6f#YxPa32ku>Uo3CBoT@xodQ7^6?6yj`$zdWK zE#w6d{b$Apu|(jzQ1z`3`J*sU^}YD#<@TGUb_TsGtG~?|Mk1;2M~-rX4J2m_$S=9X z=SlEd`I2R~v`R*I^Y;(&Jd^XQjUmZ{^N{b?RZ#yJF%QlzI5RaiYQ>wk{M`Xa8fF)? z`77!0`KJc3^}n~kKvnuf&Wl1c6KDK2HY*sb#XI`ZeqmfHjfO4>#~64d2Z%8(4T?@& z3X4TE>Yl7ZdnfG;NG{|Kx6kNOfEDqh(WV}N-@xCyw zaeTODT+uJBSSxA88~S&skp;UM(#s0dEIcP?N$B;sdu^w$6_FQF1>Dc}@W&Edz?DM7 zFN`;k1PE?_CcmOA`iDc9dY7UrBohAlg$1pF)Ix+S$vV;!JrgEMU$zfn{Scf*QWE%_ zFcRL(^T4Y-1(72%k%2UIrMk%5LEwsnH=)rE(x0>a;o*~+9J^iA+RhQ3AD^E$KB2sq zn-ls61atj%+bY!iyV(0x5bJ#^;TpY!+KBZd%O8!H;jDS-zU%qE`tkcdelu8fqO3=+ znwP%r34`c|I}i~Ym-0?YsE_1jSw(+~2dS#ro=of}gn!vVYoAxHR8jGwGjvj)PwyLk zez4;-cnpHzYB5620ELsJKJIp=^v$ICgJfj&xddP??tg`}!<2=5uo#4T=ScvP(e>l1 zelP#K5BC|gM^&CGd7;wD^A||fq<+|8$r0Cz*7(C?F+p8O7S|Nrk>H@1og_vZ7<(>6 zdQg?a-Y1gg&)Px;sA%jIv;5C&JvqkwGBdW6&7F z=L^hVr))6k%=L_umd8_q-@e7UF@LB?SEML>cWEBLp&eePLENtJEle? zy4K5rMY$cF+8>mu#;500a__j*5UNZn(mA>zj)h7zx$nU`WQ9Y~pcn6S1&p`ekC_eY zTuXXf@pHH){?tlZk(!!Hu%MAm7ySV6Q-&Ls=Dva2mS6_^L;w3fSnaM4K)jx+?I-Yv+UbIc#}c0E(rG016!^tc6KHYlnr z7*rt8bQBPxTzeZhi^UhF%RAvb7FI_0=jo-Ixkgmjo3^7><-AgRWJxDaJv~-h&LEbW z&Z0kqWL6=Kg^O#ipJq%X?RVBue>Mm~%-avxogz*9G9u7A&)II_sN(y2eTf>l!Fd)q z94hbXc4SiKm3u|y=k`5fMk(|601H2qy-Hz{O^4NqxQP3}Wi`4U;U7hW(dU*H_ z2ZR#A`jbQn@JWcCc+w+lUy37t#f6G;n?e8VDQ6PPGUahABoi4YEWzs~5hBt#+8Hs6 z9#b{o_q*cq#amMP)QB&YR`W7|_KmqDh^};F3@!KeCBcNt2g!lSGqGUyFUh z7zc_^Pe)@^^&`JI9fjgL0;z-8oQmd_8fXL`~_BluQCl01bh%P!~Xd!8a4i7P8Kk4_!-uJ$})%G1rWzSo#h^KovO|8ce`;sv&uM2>2$m z(STV$b4|yZ$1Y)EAOiWkm3ZzAp4_zImPBqV?x?SAYgFCFqB>DevE{uIe zgMwQ8lz=zGrLPtL!I-d|o9NcUr&OBg^k6!m)i_V;HnV!fq2MQQDF{*61A1zyv43lr zK`6;uGXRNJmt>M)wI(++bCS~NdYc2f`Z>Kra1|M;JL31}I!JW{b>T=P{(RrzwtkH? z<`7srYHsoQipd_^61La$3wRjz<@C01rO`G1@V^ez|DJQ!dQbkh;O&G+dx_p!{paR5 zpWmI!J(Jm!4q2O5SZL1NAOb6Ht4V{MHkNZ$iqM05Pm%zr)mgg5q#h9zA2#+;(0CfL zvo-2E%@ES5a(-PzI7lNefy>VfyBqE4xU4LShQf}sL;jcr^#J-si^qZYy?gK&fs^iu zLL3ZI^!2OaZ~DV-Q`Rg^T5C-2TO({&wr6^MXnO^e;4BLQsw#7n zXw60Sj%s3f)sNYzSnhLh_V`T<;SM^FeVO)yTc;TtiG2W& z@FedBv|VdBZc=B`mu*hD^!HiQXs=q>0TM{yD!Rt9-R3^OqiIwH=;CA&OqGfb&XTh{ zg#;mN7}jutDloi2l_Ff=D29Q7iY;UCK-@d&?ap4V>$&%II0@w9r?g#Qc?S6Hw z48hFt5?3nm1{9_sCQ2~W*;F0S&+Wv?ENARe2%m%wN7<^ETRQhbY`quA{}+s&R`{f( zL~Yw|?7YsX-nc4NtiFw)b)QQ!0ufmIEHDaE#n`T2dGZbYJ$QxgFLhZ6p-)8!>D&k-1nj2tn9f7Y@9 zxdTBF`rlvvg4SL*elX0tsdAc6_28$%zDv?0?0&6=&6){D(OtW>#=WVX)d879|9R^j ziEg~DymrIaP8RdO9{4yr4{p+(8DgX*yl(~`OyRO=tMwqiz*%l`H9YkTEZC~uGD(HQ_iCu*l;?L2VNSK=JA(Xb5$8KjBBxj{uN(8Lmo7bGR9IFo;%tFt=%6I5g7GNz$cPal`$Uo;yE55Oa`k&VdThL79ZYS| zLeJykhg_QT`h1I`UKm>e(^_}z=tr;>EsO@>WYjxxTT*)!0;!-M&xr9Ux zqfxk;walr6fAI2^E3I3#s%=C~d4c(wv5c>dmtFr7{XMCc_W-F#D7v}u(hwF>a7OQ4 z$M)BwvA$QxGHUS~Fqwt+21Kj25JQxM!^!=Y4Zn2f1q0Uw=aHL}V*C~0OS*Ug+`xVr z>;!`zG-QZCcmlKYN9ku=RV}r&QI-rI=pY0nYRpBls8BRG;1LpF42~rn9zztbcVhW| zSiIwKlRDe~0jfoKv-haYfHL|Co=3CmR7LwQYyvU zPuGBYV>y49MDy}&nq=2RJFhH)ITul;xT#IMjx^`{E*&lECI1_=b+wGRv0$nqU@^MG zJxbDbo zeQ;I;oaiV8L_s9RvN_2#B*Sh8S07=xmh_F}B6+Mg1AW?qRO_>(1M!|4F?pxw>o!v9 zrm5M^yUT?O7n~WWx@-SK8V44MCXSmm);wwJ_q6xqVMUhL+z%n2P92Dc6Kj z*$+7^=YGNb*r~h>$&3iLS0AC6c+%Iu#fu|NCP2lvH@8uiRi%I`KOrpNhHq%`Vl1*& z8sU73hb%PlHr`ofa*PH8JPTiqYA^mbka_!FXdZ8KAm%R#*&}%(D0TibR{(5-P!R3a z0@((&`!b`HNb9I~1U)rZV4FcKGE>JreHTl#h)JKO0!dQ}wS0(!CP|R{*v|NbmWIa7$7#H6#x#i}SimxMNW`X-xMDrz z`=~E>ZrzgU6wS+$c1H=ehaAh@AIjbXq-J&~nox-m0NxG(7FVMZZGyoWFHSnChr+)Q}C=$fcGf#dNHF27+hHamoJ?}5I zdB_wl;b3>S+4k1gZv~JKgI3>&N&GpQds^H{e=FlEzBpNtO}WpF|A_5Gn)mwQtu!10 z4E;8zY~um&?TX|i=>2BDltkIB0D_z0*JX1QII;n>SXP5{-+MC98av!kmO5P~>@>ec;}wOaH0K8j&bI*^2_KnlosZ|NUi}w<`U| zf8y`uXpTF?vI^kHcH7MuOy==w@?7qS6`L8pmaJ$)G+}G#e~xOrs=y+pB^%kPq5PBT6gXURdt6=k<;Rwa$Ae6FlX1=)irj+77N}} zgwfxoTN@D8a6sADO@Spa=_6`_sQsF&m?FJHze$59zou353H$aIoYU{uFA2E~8kyRk z@!X@ixkqKR|L=F9Wk5;#JGNpBkGZ1jk=G&+?_v`C9NL@zpCHTehQ7k2Fxl}3raz&R zdLFl2TR|`T=t9!#XC!EBppkxOfqM+d57`JzpBvLjme1C%xr31|9e>a=_ z8DF`qwbvZ^`zWP{%Nz;F(ReS$+q?&JHpjO|&Z%6YU>2@@e^q`nG-_cv0oYjhN&Mw(z*0%M>wKO&L z@GpR4J_S7HJ5vH1CYzgmo(nEGaa&#OR5O6mq)9qxv9kLK*Xyi`+t^b5PZn>H&72U&{zlfSbG*%8X7BEt=G8{U_{-nc14vA9DZ zXR^;)c}KO*`{SH5e#PGt`D8oA5@bjmHjf`93&JfO>E6I?J0+kE0YU?XNc^coPn(vt zp{(A%d9(IzzpxM8zgT;`h|D*4g`HtxXd57EiN7O4YTwKg=5pF)>ySi6c$u3NRTJ6RZMvN%uqy<40ze5ty}kX$u=uG z9Q!nZ4qHB&@b|Qqvd0mGO7^1mTUTompYWF;cyw_#L(GopIj6LMoqfoC_|z}pbUE>1 zRo_$Yygj5+^9R+Z!-8zmT$9TcS;*F9id>x#lFk?9CLY49Fwfbr{vxvN!ZMAM=Z^Wzg8{ll-DPiR&tnIKGCFXHaQLwAZz3*OX1R zw9wefPgcPh6gYE!&cWVfD&U_n`PlA-Ef^9-{&(s6>NOu(z*k;lTjcvvoMj<}$jun} zXmt9=KcYI|jA-wzCE@2NVp3Xa&);&6AWceJ-3_d~0g{_!XmfY3uHBubmAngqH*E%l z^_lyAPwp7H(%F2y7nWoPqb@}HvmGf4Zk`)iSyOAux;@(t@6g{p_3IK7M~fl)gcVKv zA3Ae_**?)TN6x8)o+YE6UIW?uPX<) z&(zb;^ctfti!)MS4iPzR+jsO3;vE!wFQe}k%wohOLMl;5B7hS%dZz)6!_meW0^ z>;q@O1~pfZRXu|f0>&AsV&`0&Qb|MFbc0Mkg@q^Fjy~13Il4Kt@F#y%_-sV6=5@D+lO#|E?@WcQ8Zd3W);pPya_>EF~^NGZC%Ao{74qNfG$rsv_ zboTj>)4DS#s6BI@h?MY{N^h!cy6Sj7>Q&W*i~0l3wa`ca*WbZEt2Jdkb%aT&7KYW@ z5?&2P(q4VMv%ZwGkdonmk_YcDuzmFO=~KP%Oule?tFGw*US3^OR#C0#NO1w~hOVh4 z(XGfVBFz6vm24$(!bkZ9s;})w=>#O{a{JN0Yy6};=h?9pWE8j=-$}~ZX%IYOrr^5Q zOX~O}Gjksg*nh-&um2)5M1^0PdxSp@p5{JpeQwcnz8g~;Wr}wCAWns5M23s(okyXP znsbD0hNzncF=s(1rM!06I4=V}&2^m+=vKju-`=m}-40C?>`*&6Dv|(90EIBc&J&OX@iNWQOYY$?f0p z_i+G3OeGass2#d^{`NUY8M&T^j&h?0O`S|BByYj|N@me7soF=vmv{G{vi8gEw!%qJKU?8Vy^hpq@#Xa>@Lf#DwB|H= zzto@R_`QqzXp5i&2l7dE9NlSM-9o1@j+=-i{`WoJx03JCAT-0paNREh1v9W&#qH$e ze#9R{O^UYSy26ExO=U0Wf~KJrbCnn^`T3Gk3REfx@Z5(7x0!{F3`+x=GXZ^kvjznO zm2szEAbWIy)+Ba=%;+2B%JPB`)nPemV@bwr$qVJxrX2X-#E@JLyd!4G4+6ED3k$}a zhIyCBs+^U1Aiz(|b}eH77V?{BUv&6f5a6iqV=J$5I8F!I{!+d6Yc2Nl6po=TqcHRs z&z1CnBgGYC0bkbsDbMuIeW5FvKydk4?v|SRJGd3;M3QJ;Btu_R6Z~%Pne!c|O(Vg7 z+g;tATux&QVDv*=6_#oKaslTCFpFw3D_b5 zAmS`X0(uO5-n)`9Z9cDuFd)oSlr#ysM_l+Lx4J4BnzPAEq5;#8stc6~|0C(%)m4A^ zFMcLvsMgXC&-)740)}RzT#ua$nnCaYauYLA7m#$3$B~qh!e{I2UTB>(VPAB|+4MSs zfk6l?hgwGAX$cCe`S zWt287syCTK#3KqQKK1LZZ*F9C-C_LzJP5}{ zo1a?kG37Q0P}4_qc!6D#TP|>hBl4~&rw4UOPQ7q7A)#q>AR6U77{L{qbP)?gj4vBV z=w{qDz{=@tKs$eWg|ffbTDOSNeJvaMC%gc0Ia9pzZ|f<(9rD?0*RD;hXum~Q7Cg~X zK>NA6lZzoci3Mq+tvv<+LT(;!>fX0~JM?$C1?*(Irxfpn!u)&GeBc0ahohGriCAst zr;`qs&ZJdvni@EbsNRSQ0Cr0usSH%$7+`nl+rgh9Q7mJ2k<6G@!Es`!m6W{irMcsQ zD{}Tn0!?!o-}kq|yT3Yhc(muR!!4#*IcvVU*1UPgtJ69TqrkdXu+Y+>tYp?~k4xo; zM{Ly|zjN2m5AF8{qbUEf+QA`vwW4+Fv*+vd=#g^B!ShIL|H)GmuO8~tNm(bvL8;Bt zb+PAGl^%!scSb2zmQ~5^Jq7@ELFO5CP)ChPy?@&EJ&t*HRmlLr^0#CvNnRQcEe&IO zS@VCrK{)1J{-g8d+5-QGRBI32{CDrRPy_*NO-^R$^YVh$vHbgBR@&c5L`!2{9qy&u ztleOt#b*wj_(Raq?;O6ec&q)ISo6T=w2`$?A}d1BCRtpxSQ}8VxNO%HfL6^4k;`;W z-R-u!OmoxE>LWigTw-0<-RHdYV=FB{r5|h(=V{EvrF&jI*8O#mn}5wht=6pr|DMdb ztT+8|v2^v&F8cLMH(v4VVxVkn{QCRiY`ozfxm $#JP*{*cq%vwLcm25Zs`L#&N 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

    g` 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

    7dv&CM*whQ=^g=|{f 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 H&!7GqHwD-U diff --git a/doc/src/JPG/lammps-invoke-python.png b/doc/src/JPG/lammps-invoke-python.png index d1e25f52ea1fd7944391009f31fe4b176e4812c4..890963a5778cefe8c99c9f2056fd5e5539380f6f 100644 GIT binary patch literal 38861 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쮞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 E4~TtAtN;K2 literal 37244 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 VY~7&?{~Y6TkCdc~C7-|fzW@rQ^Wp#i diff --git a/doc/src/JPG/python-invoke-lammps.png b/doc/src/JPG/python-invoke-lammps.png index 0c456028dbbed2d119f3afc432c6418c08294b44..6e44b8d56e13012c29c86e698912e6c449a65084 100644 GIT binary patch literal 28457 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 Date: Sat, 23 Dec 2023 12:06:38 -0500 Subject: [PATCH 154/189] add graph to show relation between branches and versions of LAMMPS --- doc/graphviz/lammps-releases.dot | 34 +++++++++++++++++++++++++++++++ doc/src/JPG/lammps-releases.png | Bin 0 -> 69780 bytes doc/src/Manual_version.rst | 5 +++++ 3 files changed, 39 insertions(+) create mode 100644 doc/graphviz/lammps-releases.dot create mode 100644 doc/src/JPG/lammps-releases.png diff --git a/doc/graphviz/lammps-releases.dot b/doc/graphviz/lammps-releases.dot new file mode 100644 index 0000000000..f641cac029 --- /dev/null +++ b/doc/graphviz/lammps-releases.dot @@ -0,0 +1,34 @@ +// LAMMPS branches and releases +digraph releases { + rankdir="LR"; + github [shape="box" label="Pull Requests\non GitHub" height=0.75]; + github -> develop [label="Merge commits"]; + { + rank = "same"; + work [shape="none" label="Development branches:"] + develop [label="'develop' branch" height=0.75]; + maintenance [label="'maintenance' branch" height=0.75]; + }; + { + rank = "same"; + upload [shape="none" label="Release branches:"] + release [label="'release' branch" height=0.75]; + stable [label="'stable' branch" height=0.75]; + }; + develop -> release [label="Feature release\n(every 4-8 weeks)"]; + release -> stable [label="Stable release\n(once per year)"]; + stable -> maintenance [label="Reset on stable release" style="setlinewidth(2)"]; + develop -> maintenance [label="Backports of bugfixes" style="dashed"]; + maintenance -> stable [label="Updates to stable release"]; + { + rank = "same"; + tag [shape="none" label="Applied tags:"]; + patchtag [shape="box" label="patch_"]; + stabletag [shape="box" label="stable_"]; + updatetag [shape="box" label="stable__update"]; + }; + release -> patchtag [label="feature release" style="dotted"]; + stable -> stabletag [label="stable release" style="dotted"]; + stable -> updatetag [label="update release" style="dotted"]; +} + diff --git a/doc/src/JPG/lammps-releases.png b/doc/src/JPG/lammps-releases.png new file mode 100644 index 0000000000000000000000000000000000000000..d5c317088f3c247cfc37bc27d1747cb0bc671d93 GIT binary patch literal 69780 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�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. Development ^^^^^^^^^^^ +.. figure:: JPG/lammps-releases.png + :figclass: align-center + + Relationships between main branches and tags in the LAMMPS git repository + Modifications of the LAMMPS source code (like bug fixes, code refactoring, updates to existing features, or addition of new features) are organized into pull requests. Pull requests will be merged into the From 9546fd89e52edce0d4f089e1c91554abcf1db0c5 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 23 Dec 2023 12:16:53 -0500 Subject: [PATCH 155/189] adjust heading levels --- doc/src/Manual_version.rst | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/src/Manual_version.rst b/doc/src/Manual_version.rst index c8ac1ac7d9..1bfaffaf6d 100644 --- a/doc/src/Manual_version.rst +++ b/doc/src/Manual_version.rst @@ -34,13 +34,16 @@ first page of the :doc:`manual `. describe the version you have, which may be older than the online version. -Development -^^^^^^^^^^^ +LAMMPS releases, branches, and tags +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. figure:: JPG/lammps-releases.png :figclass: align-center - Relationships between main branches and tags in the LAMMPS git repository + Relations between releases, main branches, and tags in the LAMMPS git repository + +Development +""""""""""" Modifications of the LAMMPS source code (like bug fixes, code refactoring, updates to existing features, or addition of new features) @@ -49,7 +52,7 @@ are organized into pull requests. Pull requests will be merged into the and code review by the LAMMPS developers. Feature Releases -^^^^^^^^^^^^^^^^ +"""""""""""""""" When a sufficient number of new features and updates have accumulated *and* the LAMMPS version on the *develop* branch passes an extended set @@ -63,7 +66,7 @@ notes are `available on GitHub `_. Stable Releases -^^^^^^^^^^^^^^^ +""""""""""""""" About once a year, we release a *stable release* version of LAMMPS. This is done after a "stabilization period" where we apply only bug @@ -75,7 +78,7 @@ branches are updated and two tags are applied, a ``patch_1May2014`` format and a ``stable_1May2014`` format tag. Stable Release Updates -^^^^^^^^^^^^^^^^^^^^^^ +"""""""""""""""""""""" Between *stable releases*, we collect bug fixes and updates back-ported from the *develop* branch in a branch called *maintenance*. From the From ded160cd41653fb9a6d4835045d46279d1245124 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Sun, 31 Dec 2023 10:49:59 -0700 Subject: [PATCH 156/189] Generalizing fix update/special/bonds for pair hybrid --- src/fix_update_special_bonds.cpp | 59 ++++++++++++++++---------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/src/fix_update_special_bonds.cpp b/src/fix_update_special_bonds.cpp index 159b2a1170..4e8cba47ec 100644 --- a/src/fix_update_special_bonds.cpp +++ b/src/fix_update_special_bonds.cpp @@ -19,6 +19,7 @@ #include "error.h" #include "force.h" #include "modify.h" +#include "neighbor.h" #include "neigh_list.h" #include "pair.h" @@ -72,9 +73,6 @@ void FixUpdateSpecialBonds::setup(int /*vflag*/) force->special_coul[3] != 1.0) error->all(FLERR, "Fix update/special/bonds requires special Coulomb weights = 1,1,1"); // Implies neighbor->special_flag = [X, 2, 1, 1] - - if (utils::strmatch(force->pair_style, "^hybrid")) - error->all(FLERR, "Cannot use fix update/special/bonds with hybrid pair styles"); } /* ---------------------------------------------------------------------- @@ -155,44 +153,47 @@ void FixUpdateSpecialBonds::pre_exchange() void FixUpdateSpecialBonds::pre_force(int /*vflag*/) { - int i1, i2, j, jj, jnum; + int ilist, nlist, i1, i2, j, jj, jnum; int *jlist, *numneigh, **firstneigh; tagint tag1, tag2; + NeighList *list; int nlocal = atom->nlocal; - tagint *tag = atom->tag; - NeighList *list = force->pair->list; // may need to be generalized for pair hybrid* - numneigh = list->numneigh; - firstneigh = list->firstneigh; // In theory could communicate a list of broken bonds to neighboring processors here // to remove restriction that users use Newton bond off - for (auto const &it : new_broken_pairs) { - tag1 = it.first; - tag2 = it.second; - i1 = atom->map(tag1); - i2 = atom->map(tag2); + for (int ilist = 0; ilist < neighbor->nlist; ilist ++) { + list = neighbor->lists[ilist]; + numneigh = list->numneigh; + firstneigh = list->firstneigh; - // Loop through atoms of owned atoms i j - if (i1 < nlocal) { - jlist = firstneigh[i1]; - jnum = numneigh[i1]; - for (jj = 0; jj < jnum; jj++) { - j = jlist[jj]; - j &= SPECIALMASK; // Clear special bond bits - if (tag[j] == tag2) jlist[jj] = j; + for (auto const &it : new_broken_pairs) { + tag1 = it.first; + tag2 = it.second; + i1 = atom->map(tag1); + i2 = atom->map(tag2); + + // Loop through atoms of owned atoms i j + if (i1 < nlocal) { + jlist = firstneigh[i1]; + jnum = numneigh[i1]; + for (jj = 0; jj < jnum; jj++) { + j = jlist[jj]; + j &= SPECIALMASK; // Clear special bond bits + if (tag[j] == tag2) jlist[jj] = j; + } } - } - if (i2 < nlocal) { - jlist = firstneigh[i2]; - jnum = numneigh[i2]; - for (jj = 0; jj < jnum; jj++) { - j = jlist[jj]; - j &= SPECIALMASK; // Clear special bond bits - if (tag[j] == tag1) jlist[jj] = j; + if (i2 < nlocal) { + jlist = firstneigh[i2]; + jnum = numneigh[i2]; + for (jj = 0; jj < jnum; jj++) { + j = jlist[jj]; + j &= SPECIALMASK; // Clear special bond bits + if (tag[j] == tag1) jlist[jj] = j; + } } } } From c1446ddd92882760d1478299ffea8f1be71a04c8 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 2 Jan 2024 11:02:59 -0500 Subject: [PATCH 157/189] improve error messages --- src/compute_pair.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compute_pair.cpp b/src/compute_pair.cpp index 2788b632d2..e789adbc89 100644 --- a/src/compute_pair.cpp +++ b/src/compute_pair.cpp @@ -30,7 +30,7 @@ enum { EPAIR, EVDWL, ECOUL }; ComputePair::ComputePair(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg), pstyle(nullptr), pair(nullptr), one(nullptr) { - if (narg < 4) error->all(FLERR, "Illegal compute pair command"); + if (narg < 4) utils::missing_cmd_args(FLERR, "compute pair", error); scalar_flag = 1; extscalar = 1; @@ -63,7 +63,7 @@ ComputePair::ComputePair(LAMMPS *lmp, int narg, char **arg) : else if (strcmp(arg[iarg], "ecoul") == 0) evalue = ECOUL; else - error->all(FLERR, "Illegal compute pair command"); + error->all(FLERR, "Unknown compute pair keyword {}", arg[iarg]); ++iarg; } @@ -75,7 +75,7 @@ ComputePair::ComputePair(LAMMPS *lmp, int narg, char **arg) : pair = force->pair_match(pstyle, 1, nsub); } - if (!pair) error->all(FLERR, "Unrecognized pair style in compute pair command"); + if (!pair) error->all(FLERR, "Unrecognized pair style {} in compute pair command", pstyle); npair = pair->nextra; if (npair) { @@ -104,7 +104,7 @@ void ComputePair::init() // recheck for pair style in case it has been deleted pair = force->pair_match(pstyle, 1, nsub); - if (!pair) error->all(FLERR, "Unrecognized pair style in compute pair command"); + if (!pair) error->all(FLERR, "Unrecognized pair style {} in compute pair command", pstyle); } /* ---------------------------------------------------------------------- */ From 817c7bac18ab8d126d83a9670a2619e53af1999b Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Wed, 3 Jan 2024 10:11:30 -0700 Subject: [PATCH 158/189] Optimize Kokkos PACE pair style for GPUs --- src/KOKKOS/pair_pace_kokkos.cpp | 669 ++++++++++++++++++++------------ src/KOKKOS/pair_pace_kokkos.h | 52 +-- 2 files changed, 442 insertions(+), 279 deletions(-) diff --git a/src/KOKKOS/pair_pace_kokkos.cpp b/src/KOKKOS/pair_pace_kokkos.cpp index 805d7f68bb..85fd458298 100644 --- a/src/KOKKOS/pair_pace_kokkos.cpp +++ b/src/KOKKOS/pair_pace_kokkos.cpp @@ -104,7 +104,8 @@ void PairPACEKokkos::grow(int natom, int maxneigh) if ((int)A.extent(0) < natom) { - MemKK::realloc_kokkos(A, "pace:A", natom, nelements, nradmax + 1, (lmax + 1) * (lmax + 1)); + MemKK::realloc_kokkos(A_sph, "pace:A_sph", natom, nelements, idx_sph_max, nradmax + 1); + MemKK::realloc_kokkos(A, "pace:A", natom, nelements, (lmax + 1) * (lmax + 1), nradmax + 1); MemKK::realloc_kokkos(A_rank1, "pace:A_rank1", natom, nelements, nradbase); MemKK::realloc_kokkos(A_list, "pace:A_list", natom, idx_rho_max, basis_set->rankmax); @@ -115,7 +116,7 @@ void PairPACEKokkos::grow(int natom, int maxneigh) MemKK::realloc_kokkos(rhos, "pace:rhos", natom, basis_set->ndensitymax + 1); // +1 density for core repulsion MemKK::realloc_kokkos(dF_drho, "pace:dF_drho", natom, basis_set->ndensitymax + 1); // +1 density for core repulsion - MemKK::realloc_kokkos(weights, "pace:weights", natom, nelements, nradmax + 1, (lmax + 1) * (lmax + 1)); + MemKK::realloc_kokkos(weights, "pace:weights", natom, nelements, idx_sph_max, nradmax + 1); MemKK::realloc_kokkos(weights_rank1, "pace:weights_rank1", natom, nelements, nradbase); // hard-core repulsion @@ -129,11 +130,11 @@ void PairPACEKokkos::grow(int natom, int maxneigh) MemKK::realloc_kokkos(dB_flatten, "pace:dB_flatten", natom, idx_rho_max, basis_set->rankmax); } - if (((int)ylm.extent(0) < natom) || ((int)ylm.extent(1) < maxneigh)) { + if (((int)fr.extent(0) < natom) || ((int)fr.extent(1) < maxneigh)) { // radial functions - MemKK::realloc_kokkos(fr, "pace:fr", natom, maxneigh, nradmax, lmax + 1); - MemKK::realloc_kokkos(dfr, "pace:dfr", natom, maxneigh, nradmax, lmax + 1); + MemKK::realloc_kokkos(fr, "pace:fr", natom, maxneigh, lmax + 1, nradmax); + MemKK::realloc_kokkos(dfr, "pace:dfr", natom, maxneigh, lmax + 1, nradmax); MemKK::realloc_kokkos(gr, "pace:gr", natom, maxneigh, nradbase); MemKK::realloc_kokkos(dgr, "pace:dgr", natom, maxneigh, nradbase); const int max_num_functions = MAX(nradbase, nradmax*(lmax + 1)); @@ -144,12 +145,6 @@ void PairPACEKokkos::grow(int natom, int maxneigh) MemKK::realloc_kokkos(cr, "pace:cr", natom, maxneigh); MemKK::realloc_kokkos(dcr, "pace:dcr", natom, maxneigh); - // spherical harmonics - MemKK::realloc_kokkos(plm, "pace:plm", natom, maxneigh, (lmax + 1) * (lmax + 1)); - MemKK::realloc_kokkos(dplm, "pace:dplm", natom, maxneigh, (lmax + 1) * (lmax + 1)); - MemKK::realloc_kokkos(ylm, "pace:ylm", natom, maxneigh, (lmax + 1) * (lmax + 1)); - MemKK::realloc_kokkos(dylm, "pace:dylm", natom, maxneigh, (lmax + 1) * (lmax + 1)); - // short neigh list MemKK::realloc_kokkos(d_ncount, "pace:ncount", natom); MemKK::realloc_kokkos(d_mu, "pace:mu", natom, maxneigh); @@ -443,6 +438,7 @@ void PairPACEKokkos::init_style() // spherical harmonics + MemKK::realloc_kokkos(d_idx_sph, "pace:idx_sph", (lmax + 1) * (lmax + 1)); MemKK::realloc_kokkos(alm, "pace:alm", (lmax + 1) * (lmax + 1)); MemKK::realloc_kokkos(blm, "pace:blm", (lmax + 1) * (lmax + 1)); MemKK::realloc_kokkos(cl, "pace:cl", lmax + 1); @@ -613,7 +609,7 @@ void PairPACEKokkos::compute(int eflag_in, int vflag_in) Kokkos::deep_copy(weights, 0.0); Kokkos::deep_copy(weights_rank1, 0.0); - Kokkos::deep_copy(A, 0.0); + Kokkos::deep_copy(A_sph, 0.0); Kokkos::deep_copy(A_rank1, 0.0); Kokkos::deep_copy(rhos, 0.0); Kokkos::deep_copy(rho_core, 0.0); @@ -646,15 +642,6 @@ void PairPACEKokkos::compute(int eflag_in, int vflag_in) Kokkos::parallel_for("ComputeRadial",policy_radial,*this); } - //ComputeYlm - { - int vector_length = vector_length_default; - int team_size = 16; - check_team_size_for(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); - typename Kokkos::TeamPolicy policy_ylm(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); - Kokkos::parallel_for("ComputeYlm",policy_ylm,*this); - } - //ComputeAi { int vector_length = vector_length_default; @@ -693,7 +680,7 @@ void PairPACEKokkos::compute(int eflag_in, int vflag_in) int vector_length = vector_length_default; int team_size = team_size_default; check_team_size_for(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); - typename Kokkos::TeamPolicy policy_derivative(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); + typename Kokkos::TeamPolicy policy_derivative(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); Kokkos::parallel_for("ComputeDerivative",policy_derivative,*this); } @@ -898,28 +885,6 @@ void PairPACEKokkos::operator() (TagPairPACEComputeRadial, const typ /* ---------------------------------------------------------------------- */ -template -KOKKOS_INLINE_FUNCTION -void PairPACEKokkos::operator() (TagPairPACEComputeYlm, const typename Kokkos::TeamPolicy::member_type& team) const -{ - // Extract the atom number - int ii = team.team_rank() + team.team_size() * (team.league_rank() % - ((chunk_size+team.team_size()-1)/team.team_size())); - if (ii >= chunk_size) return; - - // Extract the neighbor number - const int jj = team.league_rank() / ((chunk_size+team.team_size()-1)/team.team_size()); - const int ncount = d_ncount(ii); - if (jj >= ncount) return; - - const double xn = d_rhats(ii, jj, 0); - const double yn = d_rhats(ii, jj, 1); - const double zn = d_rhats(ii, jj, 2); - compute_ylm(ii,jj,xn,yn,zn,lmax); -} - -/* ---------------------------------------------------------------------- */ - template KOKKOS_INLINE_FUNCTION void PairPACEKokkos::operator() (TagPairPACEComputeAi, const typename Kokkos::TeamPolicy::member_type& team) const @@ -941,13 +906,127 @@ void PairPACEKokkos::operator() (TagPairPACEComputeAi, const typenam Kokkos::atomic_add(&A_rank1(ii, mu_j, n), gr(ii, jj, n) * Y00); // rank > 1 - for (int n = 0; n < nradmax; n++) { - for (int l = 0; l <= lmax; l++) { - for (int m = 0; m <= l; m++) { - const int idx = l * (l + 1) + m; // (l, m) - Kokkos::atomic_add(&A(ii, mu_j, n, idx).re, fr(ii, jj, n, l) * ylm(ii, jj, idx).re); - Kokkos::atomic_add(&A(ii, mu_j, n, idx).im, fr(ii, jj, n, l) * ylm(ii, jj, idx).im); + + // Compute plm and ylm + + // requires rx^2 + ry^2 + rz^2 = 1 , NO CHECKING IS PERFORMED !!!!!!!!! + // requires -1 <= rz <= 1 , NO CHECKING IS PERFORMED !!!!!!!!! + // prefactors include 1/sqrt(2) factor compared to reference + + complex ylm, phase; + complex phasem, mphasem1; + complex dyx, dyy, dyz; + complex rdy; + + const double rx = d_rhats(ii, jj, 0); + const double ry = d_rhats(ii, jj, 1); + const double rz = d_rhats(ii, jj, 2); + + phase.re = rx; + phase.im = ry; + + double plm_idx,plm_idx1,plm_idx2; + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + + int idx_sph = 0; + + // m = 0 + for (int l = 0; l <= lmax; l++) { + // const int idx = l * (l + 1); + + if (l == 0) { + // l=0, m=0 + // plm[0] = Y00/sq1o4pi; //= sq1o4pi; + plm_idx = Y00; //= 1; + } else if (l == 1) { + // l=1, m=0 + plm_idx = Y00 * sq3 * rz; + } else { + // l>=2, m=0 + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + } + + ylm.re = plm_idx; + ylm.im = 0.0; + + for (int n = 0; n < nradmax; n++) { + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).re, fr(ii, jj, l, n) * ylm.re); + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).im, fr(ii, jj, l, n) * ylm.im); + } + + plm_idx2 = plm_idx1; + plm_idx1 = plm_idx; + + idx_sph++; + } + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + + // m = 1 + for (int l = 1; l <= lmax; l++) { + // const int idx = l * (l + 1) + 1; // (l, 1) + + if (l == 1) { + // l=1, m=1 + plm_idx = -sq3o2 * Y00; + } else if (l == 2) { + const double t = dl(l) * plm_idx1; + plm_idx = t * rz; + } else { + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + } + + ylm = phase * plm_idx; + + for (int n = 0; n < nradmax; n++) { + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).re, fr(ii, jj, l, n) * ylm.re); + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).im, fr(ii, jj, l, n) * ylm.im); + } + + plm_idx2 = plm_idx1; + plm_idx1 = plm_idx; + + idx_sph++; + } + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + + double plm_mm1_mm1 = -sq3o2 * Y00; // (1, 1) + + // m > 1 + phasem = phase; + for (int m = 2; m <= lmax; m++) { + + mphasem1.re = phasem.re * double(m); + mphasem1.im = phasem.im * double(m); + phasem = phasem * phase; + + for (int l = m; l <= lmax; l++) { + // const int idx = l * (l + 1) + m; + + if (l == m) { + plm_idx = cl(l) * plm_mm1_mm1; // (m+1, m) + plm_mm1_mm1 = plm_idx; + } else if (l == (m + 1)) { + const double t = dl(l) * plm_mm1_mm1; // (m - 1, m - 1) + plm_idx = t * rz; // (m, m) + } else { + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); } + + ylm.re = phasem.re * plm_idx; + ylm.im = phasem.im * plm_idx; + + for (int n = 0; n < nradmax; n++) { + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).re, fr(ii, jj, l, n) * ylm.re); + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).im, fr(ii, jj, l, n) * ylm.im); + } + + plm_idx2 = plm_idx1; + plm_idx1 = plm_idx; + + idx_sph++; } } @@ -961,17 +1040,35 @@ template KOKKOS_INLINE_FUNCTION void PairPACEKokkos::operator() (TagPairPACEConjugateAi, const int& ii) const { - //complex conjugate A's (for NEGATIVE (-m) terms) - // for rank > 1 for (int mu_j = 0; mu_j < nelements; mu_j++) { - for (int n = 0; n < nradmax; n++) { - for (int l = 0; l <= lmax; l++) { + + // transpose + + int idx_sph = 0; + + for (int m = 0; m <= lmax; m++) { + for (int l = m; l <= lmax; l++) { + const int idx = l * (l + 1) + m; + for (int n = 0; n < nradmax; n++) { + A(ii, mu_j, idx, n) = A_sph(ii, mu_j, idx_sph, n); + } + + idx_sph++; + } + } + + // complex conjugate A's (for NEGATIVE (-m) terms) + // for rank > 1 + + for (int l = 0; l <= lmax; l++) { //fill in -m part in the outer loop using the same m <-> -m symmetry as for Ylm - for (int m = 1; m <= l; m++) { - const int idx = l * (l + 1) + m; // (l, m) - const int idxm = l * (l + 1) - m; // (l, -m) - const int factor = m % 2 == 0 ? 1 : -1; - A(ii, mu_j, n, idxm) = A(ii, mu_j, n, idx).conj() * (double)factor; + for (int m = 1; m <= l; m++) { + const int idx = l * (l + 1) + m; // (l, m) + const int idxm = l * (l + 1) - m; // (l, -m) + const int idx_sph = d_idx_sph(idx); + const int factor = m % 2 == 0 ? 1 : -1; + for (int n = 0; n < nradmax; n++) { + A(ii, mu_j, idxm, n) = A_sph(ii, mu_j, idx_sph, n).conj() * (double)factor; } } } @@ -1021,7 +1118,7 @@ void PairPACEKokkos::operator() (TagPairPACEComputeRho, const int& i const int l = d_ls(mu_i, offset, t); const int m = d_ms_combs(mu_i, idx_rho, t); // current ms-combination (of length = rank) const int idx = l * (l + 1) + m; // (l, m) - A_list(ii, idx_rho, t) = A(ii, mu, n - 1, idx); + A_list(ii, idx_rho, t) = A(ii, mu, idx, n - 1); A_forward_prod(ii, idx_rho, t + 1) = A_forward_prod(ii, idx_rho, t) * A_list(ii, idx_rho, t); } @@ -1142,14 +1239,20 @@ void PairPACEKokkos::operator() (TagPairPACEComputeWeights, const in const int n_t = d_ns(mu_i, offset, t); const int l_t = d_ls(mu_i, offset, t); const int idx = l_t * (l_t + 1) + m_t; // (l, m) - const complex value = theta * dB; - Kokkos::atomic_add(&(weights(ii, mu_t, n_t - 1, idx).re), value.re); - Kokkos::atomic_add(&(weights(ii, mu_t, n_t - 1, idx).im), value.im); + const int idx_sph = d_idx_sph(idx); + if (idx_sph >= 0) { + const complex value = theta * dB; + Kokkos::atomic_add(&(weights(ii, mu_t, idx_sph, n_t - 1).re), value.re); + Kokkos::atomic_add(&(weights(ii, mu_t, idx_sph, n_t - 1).im), value.im); + } // update -m_t (that could also be positive), because the basis is half_basis const int idxm = l_t * (l_t + 1) - m_t; // (l, -m) - const complex valuem = theta * dB.conj() * (double)factor; - Kokkos::atomic_add(&(weights(ii, mu_t, n_t - 1, idxm).re), valuem.re); - Kokkos::atomic_add(&(weights(ii, mu_t, n_t - 1, idxm).im), valuem.im); + const int idxm_sph = d_idx_sph(idxm); + if (idxm_sph >= 0) { + const complex valuem = theta * dB.conj() * (double)factor; + Kokkos::atomic_add(&(weights(ii, mu_t, idxm_sph, n_t - 1).re), valuem.re); + Kokkos::atomic_add(&(weights(ii, mu_t, idxm_sph, n_t - 1).im), valuem.im); + } } } } @@ -1196,37 +1299,239 @@ void PairPACEKokkos::operator() (TagPairPACEComputeDerivative, const } // for rank > 1 - for (int n = 0; n < nradmax; n++) { - for (int l = 0; l <= lmax; l++) { - const double R_over_r = fr(ii, jj, n, l) * rinv; - const double DR = dfr(ii, jj, n, l); - // for m >= 0 - for (int m = 0; m <= l; m++) { - const int idx = l * (l + 1) + m; // (l, m) - complex w = weights(ii, mu_j, n, idx); + // compute plm, dplm, ylm and dylm + // requires rx^2 + ry^2 + rz^2 = 1 , NO CHECKING IS PERFORMED !!!!!!!!! + // requires -1 <= rz <= 1 , NO CHECKING IS PERFORMED !!!!!!!!! + // prefactors include 1/sqrt(2) factor compared to reference + + complex ylm,dylm[3]; + complex phase; + complex phasem, mphasem1; + complex dyx, dyy, dyz; + complex rdy; + + const double rx = d_rhats(ii, jj, 0); + const double ry = d_rhats(ii, jj, 1); + const double rz = d_rhats(ii, jj, 2); + + phase.re = rx; + phase.im = ry; + + double plm_idx,plm_idx1,plm_idx2; + double dplm_idx,dplm_idx1,dplm_idx2; + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + dplm_idx = dplm_idx1 = dplm_idx2 = 0.0; + + int idx_sph = 0; + + // m = 0 + for (int l = 0; l <= lmax; l++) { + // const int idx = l * (l + 1); + + if (l == 0) { + // l=0, m=0 + // plm[0] = Y00/sq1o4pi; //= sq1o4pi; + plm_idx = Y00; //= 1; + dplm_idx = 0.0; + } else if (l == 1) { + // l=1, m=0 + plm_idx = Y00 * sq3 * rz; + dplm_idx = Y00 * sq3; + } else { + // l>=2, m=0 + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + dplm_idx = alm(idx_sph) * (plm_idx1 + rz * dplm_idx1 + blm(idx_sph) * dplm_idx2); + } + + ylm.re = plm_idx; + ylm.im = 0.0; + + dyz.re = dplm_idx; + rdy.re = dyz.re * rz; + + dylm[0].re = -rdy.re * rx; + dylm[0].im = 0.0; + dylm[1].re = -rdy.re * ry; + dylm[1].im = 0.0; + dylm[2].re = dyz.re - rdy.re * rz; + dylm[2].im = 0; + + for (int n = 0; n < nradmax; n++) { + + const double R_over_r = fr(ii, jj, l, n) * rinv; + const double DR = dfr(ii, jj, l, n); + const complex Y_DR = ylm * DR; + + complex w = weights(ii, mu_j, idx_sph, n); + if (w.re == 0.0 && w.im == 0.0) continue; + + complex grad_phi_nlm[3]; + grad_phi_nlm[0] = Y_DR * r_hat[0] + dylm[0] * R_over_r; + grad_phi_nlm[1] = Y_DR * r_hat[1] + dylm[1] * R_over_r; + grad_phi_nlm[2] = Y_DR * r_hat[2] + dylm[2] * R_over_r; + // real-part multiplication only + f_ji[0] += w.real_part_product(grad_phi_nlm[0]); + f_ji[1] += w.real_part_product(grad_phi_nlm[1]); + f_ji[2] += w.real_part_product(grad_phi_nlm[2]); + } + + plm_idx2 = plm_idx1; + dplm_idx2 = dplm_idx1; + + plm_idx1 = plm_idx; + dplm_idx1 = dplm_idx; + + idx_sph++; + } + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + dplm_idx = dplm_idx1 = dplm_idx2 = 0.0; + + // m = 1 + for (int l = 1; l <= lmax; l++) { + // const int idx = l * (l + 1) + 1; // (l, 1) + + if (l == 1) { + // l=1, m=1 + plm_idx = -sq3o2 * Y00; + dplm_idx = 0.0; + } else if (l == 2) { + const double t = dl(l) * plm_idx1; + plm_idx = t * rz; + dplm_idx = t; + } else { + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + dplm_idx = alm(idx_sph) * (plm_idx1 + rz * dplm_idx1 + blm(idx_sph) * dplm_idx2); + } + + ylm = phase * plm_idx; + + dyx.re = plm_idx; + dyx.im = 0.0; + dyy.re = 0.0; + dyy.im = plm_idx; + dyz.re = phase.re * dplm_idx; + dyz.im = phase.im * dplm_idx; + + rdy.re = rx * dyx.re + +rz * dyz.re; + rdy.im = ry * dyy.im + rz * dyz.im; + + dylm[0].re = dyx.re - rdy.re * rx; + dylm[0].im = -rdy.im * rx; + dylm[1].re = -rdy.re * ry; + dylm[1].im = dyy.im - rdy.im * ry; + dylm[2].re = dyz.re - rdy.re * rz; + dylm[2].im = dyz.im - rdy.im * rz; + + for (int n = 0; n < nradmax; n++) { + + const double R_over_r = fr(ii, jj, l, n) * rinv; + const double DR = dfr(ii, jj, l, n); + const complex Y_DR = ylm * DR; + + complex w = weights(ii, mu_j, idx_sph, n); + if (w.re == 0.0 && w.im == 0.0) continue; + // counting for -m cases if m > 0 + w.re *= 2.0; + w.im *= 2.0; + + complex grad_phi_nlm[3]; + grad_phi_nlm[0] = Y_DR * r_hat[0] + dylm[0] * R_over_r; + grad_phi_nlm[1] = Y_DR * r_hat[1] + dylm[1] * R_over_r; + grad_phi_nlm[2] = Y_DR * r_hat[2] + dylm[2] * R_over_r; + // real-part multiplication only + f_ji[0] += w.real_part_product(grad_phi_nlm[0]); + f_ji[1] += w.real_part_product(grad_phi_nlm[1]); + f_ji[2] += w.real_part_product(grad_phi_nlm[2]); + } + + plm_idx2 = plm_idx1; + dplm_idx2 = dplm_idx1; + + plm_idx1 = plm_idx; + dplm_idx1 = dplm_idx; + + idx_sph++; + } + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + dplm_idx = dplm_idx1 = dplm_idx2 = 0.0; + + double plm_mm1_mm1 = -sq3o2 * Y00; // (1, 1) + + // m > 1 + phasem = phase; + for (int m = 2; m <= lmax; m++) { + + mphasem1.re = phasem.re * double(m); + mphasem1.im = phasem.im * double(m); + phasem = phasem * phase; + + for (int l = m; l <= lmax; l++) { + // const int idx = l * (l + 1) + m; + + if (l == m) { + plm_idx = cl(l) * plm_mm1_mm1; // (m+1, m) + dplm_idx = 0.0; + plm_mm1_mm1 = plm_idx; + } else if (l == (m + 1)) { + const double t = dl(l) * plm_mm1_mm1; // (m - 1, m - 1) + plm_idx = t * rz; // (m, m) + dplm_idx = t; + } else { + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + dplm_idx = alm(idx_sph) * (plm_idx1 + rz * dplm_idx1 + blm(idx_sph) * dplm_idx2); + } + + ylm.re = phasem.re * plm_idx; + ylm.im = phasem.im * plm_idx; + + dyx = mphasem1 * plm_idx; + dyy.re = -dyx.im; + dyy.im = dyx.re; + dyz = phasem * dplm_idx; + + rdy.re = rx * dyx.re + ry * dyy.re + rz * dyz.re; + rdy.im = rx * dyx.im + ry * dyy.im + rz * dyz.im; + + dylm[0].re = dyx.re - rdy.re * rx; + dylm[0].im = dyx.im - rdy.im * rx; + dylm[1].re = dyy.re - rdy.re * ry; + dylm[1].im = dyy.im - rdy.im * ry; + dylm[2].re = dyz.re - rdy.re * rz; + dylm[2].im = dyz.im - rdy.im * rz; + + for (int n = 0; n < nradmax; n++) { + + const double R_over_r = fr(ii, jj, l, n) * rinv; + const double DR = dfr(ii, jj, l, n); + const complex Y_DR = ylm * DR; + + complex w = weights(ii, mu_j, idx_sph, n); if (w.re == 0.0 && w.im == 0.0) continue; // counting for -m cases if m > 0 - if (m > 0) { - w.re *= 2.0; - w.im *= 2.0; - } - - complex DY[3]; - DY[0] = dylm(ii, jj, idx, 0); - DY[1] = dylm(ii, jj, idx, 1); - DY[2] = dylm(ii, jj, idx, 2); - const complex Y_DR = ylm(ii, jj, idx) * DR; + w.re *= 2.0; + w.im *= 2.0; complex grad_phi_nlm[3]; - grad_phi_nlm[0] = Y_DR * r_hat[0] + DY[0] * R_over_r; - grad_phi_nlm[1] = Y_DR * r_hat[1] + DY[1] * R_over_r; - grad_phi_nlm[2] = Y_DR * r_hat[2] + DY[2] * R_over_r; + grad_phi_nlm[0] = Y_DR * r_hat[0] + dylm[0] * R_over_r; + grad_phi_nlm[1] = Y_DR * r_hat[1] + dylm[1] * R_over_r; + grad_phi_nlm[2] = Y_DR * r_hat[2] + dylm[2] * R_over_r; // real-part multiplication only f_ji[0] += w.real_part_product(grad_phi_nlm[0]); f_ji[1] += w.real_part_product(grad_phi_nlm[1]); f_ji[2] += w.real_part_product(grad_phi_nlm[2]); } + + plm_idx2 = plm_idx1; + dplm_idx2 = dplm_idx1; + + plm_idx1 = plm_idx; + dplm_idx1 = dplm_idx; + + idx_sph++; } } @@ -1364,31 +1669,46 @@ void PairPACEKokkos::v_tally_xyz(EV_FLOAT &ev, const int &i, const i template void PairPACEKokkos::pre_compute_harmonics(int lmax) { + auto h_idx_sph = Kokkos::create_mirror_view(d_idx_sph); auto h_alm = Kokkos::create_mirror_view(alm); auto h_blm = Kokkos::create_mirror_view(blm); auto h_cl = Kokkos::create_mirror_view(cl); auto h_dl = Kokkos::create_mirror_view(dl); - for (int l = 1; l <= lmax; l++) { - const double lsq = l * l; - const double ld = 2 * l; - const double l1 = (4 * lsq - 1); - const double l2 = lsq - ld + 1; - for (int m = 0; m < l - 1; m++) { - const double msq = m * m; - const double a = sqrt((double(l1)) / (double(lsq - msq))); - const double b = -sqrt((double(l2 - msq)) / (double(4 * l2 - 1))); + Kokkos::deep_copy(h_idx_sph,-1); + + int idx_sph = 0; + for (int m = 0; m <= lmax; m++) { + const double msq = m * m; + for (int l = m; l <= lmax; l++) { const int idx = l * (l + 1) + m; // (l, m) - h_alm(idx) = a; - h_blm(idx) = b; + h_idx_sph(idx) = idx_sph; + + double a = 0.0; + double b = 0.0; + + if (l > 1 && l != m) { + const double lsq = l * l; + const double ld = 2 * l; + const double l1 = (4 * lsq - 1); + const double l2 = lsq - ld + 1; + + a = sqrt((double(l1)) / (double(lsq - msq))); + b = -sqrt((double(l2 - msq)) / (double(4 * l2 - 1))); + } + h_alm(idx_sph) = a; + h_blm(idx_sph) = b; + idx_sph++; } } + idx_sph_max = idx_sph; for (int l = 1; l <= lmax; l++) { h_cl(l) = -sqrt(1.0 + 0.5 / (double(l))); h_dl(l) = sqrt(double(2 * (l - 1) + 3)); } + Kokkos::deep_copy(d_idx_sph, h_idx_sph); Kokkos::deep_copy(alm, h_alm); Kokkos::deep_copy(blm, h_blm); Kokkos::deep_copy(cl, h_cl); @@ -1397,143 +1717,6 @@ void PairPACEKokkos::pre_compute_harmonics(int lmax) /* ---------------------------------------------------------------------- */ -template -KOKKOS_INLINE_FUNCTION -void PairPACEKokkos::compute_barplm(int ii, int jj, double rz, int lmax) const -{ - // requires -1 <= rz <= 1 , NO CHECKING IS PERFORMED !!!!!!!!! - // prefactors include 1/sqrt(2) factor compared to reference - - // l=0, m=0 - // plm(ii, jj, 0, 0) = Y00/sq1o4pi; //= sq1o4pi; - plm(ii, jj, 0) = Y00; //= 1; - dplm(ii, jj, 0) = 0.0; - - if (lmax > 0) { - - // l=1, m=0 - plm(ii, jj, 2) = Y00 * sq3 * rz; - dplm(ii, jj, 2) = Y00 * sq3; - - // l=1, m=1 - plm(ii, jj, 3) = -sq3o2 * Y00; - dplm(ii, jj, 3) = 0.0; - - // loop l = 2, lmax - for (int l = 2; l <= lmax; l++) { - for (int m = 0; m < l - 1; m++) { - const int idx = l * (l + 1) + m; // (l, m) - const int idx1 = (l - 1) * l + m; // (l - 1, m) - const int idx2 = (l - 2) * (l - 1) + m; // (l - 2, m) - plm(ii, jj, idx) = alm(idx) * (rz * plm(ii, jj, idx1) + blm(idx) * plm(ii, jj, idx2)); - dplm(ii, jj, idx) = alm(idx) * (plm(ii, jj, idx1) + rz * dplm(ii, jj, idx1) + blm(idx) * dplm(ii, jj, idx2)); - } - const int idx = l * (l + 1) + l; // (l, l) - const int idx1 = l * (l + 1) + l - 1; // (l, l - 1) - const int idx2 = (l - 1) * l + l - 1; // (l - 1, l - 1) - const double t = dl(l) * plm(ii, jj, idx2); - plm(ii, jj, idx1) = t * rz; - dplm(ii, jj, idx1) = t; - plm(ii, jj, idx) = cl(l) * plm(ii, jj, idx2); - dplm(ii, jj, idx) = 0.0; - } - } -} - -/* ---------------------------------------------------------------------- */ - -template -KOKKOS_INLINE_FUNCTION -void PairPACEKokkos::compute_ylm(int ii, int jj, double rx, double ry, double rz, int lmax) const -{ - // requires rx^2 + ry^2 + rz^2 = 1 , NO CHECKING IS PERFORMED !!!!!!!!! - - complex phase; - complex phasem, mphasem1; - complex dyx, dyy, dyz; - complex rdy; - - phase.re = rx; - phase.im = ry; - - // compute barplm - compute_barplm(ii, jj, rz, lmax); - - // m = 0 - for (int l = 0; l <= lmax; l++) { - const int idx = l * (l + 1); - - ylm(ii, jj, idx).re = plm(ii, jj, idx); - ylm(ii, jj, idx).im = 0.0; - - dyz.re = dplm(ii, jj, idx); - rdy.re = dyz.re * rz; - - dylm(ii, jj, idx, 0).re = -rdy.re * rx; - dylm(ii, jj, idx, 0).im = 0.0; - dylm(ii, jj, idx, 1).re = -rdy.re * ry; - dylm(ii, jj, idx, 1).im = 0.0; - dylm(ii, jj, idx, 2).re = dyz.re - rdy.re * rz; - dylm(ii, jj, idx, 2).im = 0; - } - // m = 1 - for (int l = 1; l <= lmax; l++) { - const int idx = l * (l + 1) + 1; - - ylm(ii, jj, idx) = phase * plm(ii, jj, idx); - - dyx.re = plm(ii, jj, idx); - dyx.im = 0.0; - dyy.re = 0.0; - dyy.im = plm(ii, jj, idx); - dyz.re = phase.re * dplm(ii, jj, idx); - dyz.im = phase.im * dplm(ii, jj, idx); - - rdy.re = rx * dyx.re + +rz * dyz.re; - rdy.im = ry * dyy.im + rz * dyz.im; - - dylm(ii, jj, idx, 0).re = dyx.re - rdy.re * rx; - dylm(ii, jj, idx, 0).im = -rdy.im * rx; - dylm(ii, jj, idx, 1).re = -rdy.re * ry; - dylm(ii, jj, idx, 1).im = dyy.im - rdy.im * ry; - dylm(ii, jj, idx, 2).re = dyz.re - rdy.re * rz; - dylm(ii, jj, idx, 2).im = dyz.im - rdy.im * rz; - } - - // m > 1 - phasem = phase; - for (int m = 2; m <= lmax; m++) { - - mphasem1.re = phasem.re * double(m); - mphasem1.im = phasem.im * double(m); - phasem = phasem * phase; - - for (int l = m; l <= lmax; l++) { - const int idx = l * (l + 1) + m; - - ylm(ii, jj, idx).re = phasem.re * plm(ii, jj, idx); - ylm(ii, jj, idx).im = phasem.im * plm(ii, jj, idx); - - dyx = mphasem1 * plm(ii, jj, idx); - dyy.re = -dyx.im; - dyy.im = dyx.re; - dyz = phasem * dplm(ii, jj, idx); - - rdy.re = rx * dyx.re + ry * dyy.re + rz * dyz.re; - rdy.im = rx * dyx.im + ry * dyy.im + rz * dyz.im; - - dylm(ii, jj, idx, 0).re = dyx.re - rdy.re * rx; - dylm(ii, jj, idx, 0).im = dyx.im - rdy.im * rx; - dylm(ii, jj, idx, 1).re = dyy.re - rdy.re * ry; - dylm(ii, jj, idx, 1).im = dyy.im - rdy.im * ry; - dylm(ii, jj, idx, 2).re = dyz.re - rdy.re * rz; - dylm(ii, jj, idx, 2).im = dyz.im - rdy.im * rz; - } - } -} - -/* ---------------------------------------------------------------------- */ - template KOKKOS_INLINE_FUNCTION void PairPACEKokkos::cutoff_func_poly(const double r, const double r_in, const double delta_in, double &fc, double &dfc) const @@ -1662,11 +1845,11 @@ void PairPACEKokkos::evaluate_splines(const int ii, const int jj, do spline_gk.calcSplines(ii, jj, r, gr, dgr); spline_rnl.calcSplines(ii, jj, r, d_values, d_derivatives); - for (int kk = 0; kk < (int)fr.extent(2); kk++) { - for (int ll = 0; ll < (int)fr.extent(3); ll++) { - const int flatten = kk*fr.extent(3) + ll; - fr(ii, jj, kk, ll) = d_values(ii, jj, flatten); - dfr(ii, jj, kk, ll) = d_derivatives(ii, jj, flatten); + for (int ll = 0; ll < (int)fr.extent(2); ll++) { + for (int kk = 0; kk < (int)fr.extent(3); kk++) { + const int flatten = kk*fr.extent(2) + ll; + fr(ii, jj, ll, kk) = d_values(ii, jj, flatten); + dfr(ii, jj, ll, kk) = d_derivatives(ii, jj, flatten); } } @@ -1686,7 +1869,7 @@ void PairPACEKokkos::SplineInterpolatorKokkos::operator=(const Splin rscalelookup = spline.rscalelookup; num_of_functions = spline.num_of_functions; - lookupTable = t_ace_3d4("lookupTable", ntot+1, num_of_functions); + lookupTable = t_ace_3d4_lr("lookupTable", ntot+1, num_of_functions); auto h_lookupTable = Kokkos::create_mirror_view(lookupTable); for (int i = 0; i < ntot+1; i++) for (int j = 0; j < num_of_functions; j++) @@ -1792,10 +1975,6 @@ double PairPACEKokkos::memory_usage() bytes += MemKK::memory_usage(d_derivatives); bytes += MemKK::memory_usage(cr); bytes += MemKK::memory_usage(dcr); - bytes += MemKK::memory_usage(plm); - bytes += MemKK::memory_usage(dplm); - bytes += MemKK::memory_usage(ylm); - bytes += MemKK::memory_usage(dylm); bytes += MemKK::memory_usage(d_ncount); bytes += MemKK::memory_usage(d_mu); bytes += MemKK::memory_usage(d_rhats); diff --git a/src/KOKKOS/pair_pace_kokkos.h b/src/KOKKOS/pair_pace_kokkos.h index 36486f8628..bb8c5a1f1a 100644 --- a/src/KOKKOS/pair_pace_kokkos.h +++ b/src/KOKKOS/pair_pace_kokkos.h @@ -36,7 +36,6 @@ class PairPACEKokkos : public PairPACE { public: struct TagPairPACEComputeNeigh{}; struct TagPairPACEComputeRadial{}; - struct TagPairPACEComputeYlm{}; struct TagPairPACEComputeAi{}; struct TagPairPACEConjugateAi{}; struct TagPairPACEComputeRho{}; @@ -66,9 +65,6 @@ class PairPACEKokkos : public PairPACE { KOKKOS_INLINE_FUNCTION void operator() (TagPairPACEComputeRadial,const typename Kokkos::TeamPolicy::member_type& team) const; - KOKKOS_INLINE_FUNCTION - void operator() (TagPairPACEComputeYlm,const typename Kokkos::TeamPolicy::member_type& team) const; - KOKKOS_INLINE_FUNCTION void operator() (TagPairPACEComputeAi,const typename Kokkos::TeamPolicy::member_type& team) const; @@ -96,7 +92,7 @@ class PairPACEKokkos : public PairPACE { void operator() (TagPairPACEComputeForce,const int& ii, EV_FLOAT&) const; protected: - int inum, maxneigh, chunk_size, chunk_offset, idx_rho_max; + int inum, maxneigh, chunk_size, chunk_offset, idx_rho_max, idx_sph_max; int host_flag; int eflag, vflag; @@ -157,12 +153,6 @@ class PairPACEKokkos : public PairPACE { const F_FLOAT &fx, const F_FLOAT &fy, const F_FLOAT &fz, const F_FLOAT &delx, const F_FLOAT &dely, const F_FLOAT &delz) const; - KOKKOS_INLINE_FUNCTION - void compute_barplm(int, int, double, int) const; - - KOKKOS_INLINE_FUNCTION - void compute_ylm(int, int, double, double, double, int) const; - KOKKOS_INLINE_FUNCTION void cutoff_func_poly(const double, const double, const double, double &, double &) const; @@ -194,14 +184,18 @@ class PairPACEKokkos : public PairPACE { typedef Kokkos::View t_ace_1i; typedef Kokkos::View t_ace_2i; + typedef Kokkos::View t_ace_2i_lr; typedef Kokkos::View t_ace_3i; + typedef Kokkos::View t_ace_3i_lr; typedef Kokkos::View t_ace_4i; typedef Kokkos::View t_ace_1d; typedef Kokkos::View t_ace_2d; + typedef Kokkos::View t_ace_2d_lr; typedef Kokkos::View t_ace_2d3; typedef Kokkos::View t_ace_3d; typedef Kokkos::View t_ace_3d3; typedef Kokkos::View t_ace_3d4; + typedef Kokkos::View t_ace_3d4_lr; typedef Kokkos::View t_ace_4d; typedef Kokkos::View t_ace_1c; typedef Kokkos::View t_ace_2c; @@ -248,23 +242,13 @@ class PairPACEKokkos : public PairPACE { void pre_compute_harmonics(int); - KOKKOS_INLINE_FUNCTION - void compute_barplm(double rz, int lmaxi); - - KOKKOS_INLINE_FUNCTION - void compute_ylm(double rx, double ry, double rz, int lmaxi); - + t_ace_4c A_sph; + t_ace_1d d_idx_sph; t_ace_1d alm; t_ace_1d blm; t_ace_1d cl; t_ace_1d dl; - t_ace_3d plm; - t_ace_3d dplm; - - t_ace_3c ylm; - t_ace_4c3 dylm; - // short neigh list t_ace_1i d_ncount; t_ace_2d d_mu; @@ -283,18 +267,18 @@ class PairPACEKokkos : public PairPACE { t_ace_1d d_rho_core_cutoff; t_ace_1d d_drho_core_cutoff; t_ace_1d d_E0vals; - t_ace_2d d_wpre; - t_ace_2d d_mexp; + t_ace_2d_lr d_wpre; + t_ace_2d_lr d_mexp; // tilde t_ace_1i d_idx_rho_count; - t_ace_2i d_rank; - t_ace_2i d_num_ms_combs; - t_ace_2i d_offsets; - t_ace_3i d_mus; - t_ace_3i d_ns; - t_ace_3i d_ls; - t_ace_3i d_ms_combs; + t_ace_2i_lr d_rank; + t_ace_2i_lr d_num_ms_combs; + t_ace_2i_lr d_offsets; + t_ace_3i_lr d_mus; + t_ace_3i_lr d_ns; + t_ace_3i_lr d_ls; + t_ace_3i_lr d_ms_combs; t_ace_3d d_ctildes; t_ace_3d3 f_ij; @@ -304,12 +288,12 @@ class PairPACEKokkos : public PairPACE { int ntot, nlut, num_of_functions; double cutoff, deltaSplineBins, invrscalelookup, rscalelookup; - t_ace_3d4 lookupTable; + t_ace_3d4_lr lookupTable; void operator=(const SplineInterpolator &spline); void deallocate() { - lookupTable = t_ace_3d4(); + lookupTable = t_ace_3d4_lr(); } double memory_usage() { From 5020861cbc7cd76fdddd8bafb31373bafccbc51b Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Wed, 3 Jan 2024 13:22:52 -0700 Subject: [PATCH 159/189] Fix compiler warnings --- src/ML-PACE/compute_pace.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/ML-PACE/compute_pace.cpp b/src/ML-PACE/compute_pace.cpp index cf3be8f47f..a3fd989fcd 100644 --- a/src/ML-PACE/compute_pace.cpp +++ b/src/ML-PACE/compute_pace.cpp @@ -73,10 +73,7 @@ ComputePACE::ComputePACE(LAMMPS *lmp, int narg, char **arg) : auto potential_file_name = utils::get_potential_file_path(arg[3]); delete acecimpl -> basis_set; acecimpl -> basis_set = new ACECTildeBasisSet(potential_file_name); - double cut = acecimpl -> basis_set->cutoffmax; cutmax = acecimpl -> basis_set->cutoffmax; - double cuti; - double radelemall = 0.5; //# of rank 1, rank > 1 functions @@ -184,7 +181,6 @@ void ComputePACE::init_list(int /*id*/, NeighList *ptr) void ComputePACE::compute_array() { int ntotal = atom->nlocal + atom->nghost; - double **f = atom->f; invoked_array = update->ntimestep; // grow pace_peratom array if necessary @@ -214,9 +210,6 @@ void ComputePACE::compute_array() // invoke full neighbor list (will copy or build if necessary) neighbor->build_one(list); - SPECIES_TYPE *mus; - NS_TYPE *ns; - LS_TYPE *ls; const int inum = list->inum; const int* const ilist = list->ilist; @@ -240,7 +233,6 @@ void ComputePACE::compute_array() // compute pace derivatives for each atom in group // use full neighbor list to count atoms less than cutoff - double** const x = atom->x; const int* const mask = atom->mask; const int ntypes = atom->ntypes; From 377450882ee20f89827f02c47e1adc190d375f32 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Wed, 3 Jan 2024 14:10:29 -0700 Subject: [PATCH 160/189] Port changes to pair_pace_extrapolation_kokkos, unify code --- src/KOKKOS/pair_pace_extrapolation_kokkos.cpp | 897 ++++++++++-------- src/KOKKOS/pair_pace_extrapolation_kokkos.h | 58 +- src/KOKKOS/pair_pace_kokkos.cpp | 270 +++--- src/KOKKOS/pair_pace_kokkos.h | 6 +- 4 files changed, 675 insertions(+), 556 deletions(-) diff --git a/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp b/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp index 0980ad776d..0597885860 100644 --- a/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp +++ b/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp @@ -106,7 +106,8 @@ void PairPACEExtrapolationKokkos::grow(int natom, int maxneigh) if ((int)A.extent(0) < natom) { - MemKK::realloc_kokkos(A, "pace:A", natom, nelements, nradmax + 1, (lmax + 1) * (lmax + 1)); + MemKK::realloc_kokkos(A_sph, "pace:A_sph", natom, nelements, idx_sph_max, nradmax + 1); + MemKK::realloc_kokkos(A, "pace:A", natom, nelements, (lmax + 1) * (lmax + 1), nradmax + 1); MemKK::realloc_kokkos(A_rank1, "pace:A_rank1", natom, nelements, nradbase); MemKK::realloc_kokkos(A_list, "pace:A_list", natom, idx_ms_combs_max, basis_set->rankmax); @@ -117,7 +118,7 @@ void PairPACEExtrapolationKokkos::grow(int natom, int maxneigh) MemKK::realloc_kokkos(rhos, "pace:rhos", natom, basis_set->ndensitymax + 1); // +1 density for core repulsion MemKK::realloc_kokkos(dF_drho, "pace:dF_drho", natom, basis_set->ndensitymax + 1); // +1 density for core repulsion - MemKK::realloc_kokkos(weights, "pace:weights", natom, nelements, nradmax + 1, (lmax + 1) * (lmax + 1)); + MemKK::realloc_kokkos(weights, "pace:weights", natom, nelements, idx_sph_max, nradmax + 1); MemKK::realloc_kokkos(weights_rank1, "pace:weights_rank1", natom, nelements, nradbase); // hard-core repulsion @@ -130,16 +131,16 @@ void PairPACEExtrapolationKokkos::grow(int natom, int maxneigh) MemKK::realloc_kokkos(dB_flatten, "pace:dB_flatten", natom, idx_ms_combs_max, basis_set->rankmax); - //B-projections + // B-projections MemKK::realloc_kokkos(projections, "pace:projections", natom, total_num_functions_max); // per-atom B-projections MemKK::realloc_kokkos(d_gamma, "pace:gamma", natom); // per-atom gamma } - if (((int)ylm.extent(0) < natom) || ((int)ylm.extent(1) < maxneigh)) { + if (((int)fr.extent(0) < natom) || ((int)fr.extent(1) < maxneigh)) { // radial functions - MemKK::realloc_kokkos(fr, "pace:fr", natom, maxneigh, nradmax, lmax + 1); - MemKK::realloc_kokkos(dfr, "pace:dfr", natom, maxneigh, nradmax, lmax + 1); + MemKK::realloc_kokkos(fr, "pace:fr", natom, maxneigh, lmax + 1, nradmax); + MemKK::realloc_kokkos(dfr, "pace:dfr", natom, maxneigh, lmax + 1, nradmax); MemKK::realloc_kokkos(gr, "pace:gr", natom, maxneigh, nradbase); MemKK::realloc_kokkos(dgr, "pace:dgr", natom, maxneigh, nradbase); const int max_num_functions = MAX(nradbase, nradmax*(lmax + 1)); @@ -150,12 +151,6 @@ void PairPACEExtrapolationKokkos::grow(int natom, int maxneigh) MemKK::realloc_kokkos(cr, "pace:cr", natom, maxneigh); MemKK::realloc_kokkos(dcr, "pace:dcr", natom, maxneigh); - // spherical harmonics - MemKK::realloc_kokkos(plm, "pace:plm", natom, maxneigh, (lmax + 1) * (lmax + 1)); - MemKK::realloc_kokkos(dplm, "pace:dplm", natom, maxneigh, (lmax + 1) * (lmax + 1)); - MemKK::realloc_kokkos(ylm, "pace:ylm", natom, maxneigh, (lmax + 1) * (lmax + 1)); - MemKK::realloc_kokkos(dylm, "pace:dylm", natom, maxneigh, (lmax + 1) * (lmax + 1)); - // short neigh list MemKK::realloc_kokkos(d_ncount, "pace:ncount", natom); MemKK::realloc_kokkos(d_mu, "pace:mu", natom, maxneigh); @@ -224,7 +219,6 @@ void PairPACEExtrapolationKokkos::copy_pertype() Kokkos::deep_copy(d_wpre, h_wpre); Kokkos::deep_copy(d_mexp, h_mexp); - // ZBL core-rep MemKK::realloc_kokkos(d_cut_in, "pace:d_cut_in", nelements, nelements); MemKK::realloc_kokkos(d_dcut_in, "pace:d_dcut_in", nelements, nelements); @@ -266,6 +260,9 @@ void PairPACEExtrapolationKokkos::copy_splines() ACERadialFunctions* radial_functions = dynamic_cast(basis_set->radial_functions); + if (radial_functions == nullptr) + error->all(FLERR,"Chosen radial basis style not supported by pair style pace/kk"); + for (int i = 0; i < nelements; i++) { for (int j = 0; j < nelements; j++) { k_splines_gk.h_view(i, j) = radial_functions->splines_gk(i, j); @@ -297,8 +294,9 @@ void PairPACEExtrapolationKokkos::copy_tilde() total_num_functions_max = 0; MemKK::realloc_kokkos(d_idx_ms_combs_count, "pace:idx_ms_combs_count", nelements); - MemKK::realloc_kokkos(d_total_basis_size, "pace:total_basis_size", nelements); auto h_idx_ms_combs_count = Kokkos::create_mirror_view(d_idx_ms_combs_count); + + MemKK::realloc_kokkos(d_total_basis_size, "pace:total_basis_size", nelements); auto h_total_basis_size = Kokkos::create_mirror_view(d_total_basis_size); for (int mu = 0; mu < nelements; mu++) { @@ -313,8 +311,8 @@ void PairPACEExtrapolationKokkos::copy_tilde() idx_ms_combs++; // rank > 1 - for (int func_ind = 0; func_ind < total_basis_size; ++func_ind) { - ACEBBasisFunction *func = &basis[func_ind]; + for (int idx_func = 0; idx_func < total_basis_size; ++idx_func) { + ACEBBasisFunction *func = &basis[idx_func]; // loop over {ms} combinations in sum for (int ms_ind = 0; ms_ind < func->num_ms_combs; ++ms_ind) @@ -331,7 +329,7 @@ void PairPACEExtrapolationKokkos::copy_tilde() MemKK::realloc_kokkos(d_rank, "pace:rank", nelements, total_num_functions_max); MemKK::realloc_kokkos(d_num_ms_combs, "pace:num_ms_combs", nelements, total_num_functions_max); - MemKK::realloc_kokkos(d_func_inds, "pace:func_inds", nelements, idx_ms_combs_max); + MemKK::realloc_kokkos(d_idx_funcs, "pace:idx_funcs", nelements, idx_ms_combs_max); MemKK::realloc_kokkos(d_mus, "pace:mus", nelements, total_num_functions_max, basis_set->rankmax); MemKK::realloc_kokkos(d_ns, "pace:ns", nelements, total_num_functions_max, basis_set->rankmax); MemKK::realloc_kokkos(d_ls, "pace:ls", nelements, total_num_functions_max, basis_set->rankmax); @@ -344,7 +342,7 @@ void PairPACEExtrapolationKokkos::copy_tilde() auto h_rank = Kokkos::create_mirror_view(d_rank); auto h_num_ms_combs = Kokkos::create_mirror_view(d_num_ms_combs); - auto h_func_inds = Kokkos::create_mirror_view(d_func_inds); + auto h_idx_funcs = Kokkos::create_mirror_view(d_idx_funcs); auto h_mus = Kokkos::create_mirror_view(d_mus); auto h_ns = Kokkos::create_mirror_view(d_ns); auto h_ls = Kokkos::create_mirror_view(d_ls); @@ -365,55 +363,52 @@ void PairPACEExtrapolationKokkos::copy_tilde() const int ndensity = basis_set->map_embedding_specifications.at(mu).ndensity; - int idx_ms_comb = 0; + int idx_ms_combs = 0; // rank=1 - for (int func_ind = 0; func_ind < total_basis_size_rank1; ++func_ind) { - ACEBBasisFunction *func = &basis_rank1[func_ind]; - h_rank(mu, func_ind) = 1; - h_mus(mu, func_ind, 0) = func->mus[0]; - h_ns(mu, func_ind, 0) = func->ns[0]; + for (int idx_func = 0; idx_func < total_basis_size_rank1; ++idx_func) { + ACEBBasisFunction *func = &basis_rank1[idx_func]; + h_rank(mu, idx_func) = 1; + h_mus(mu, idx_func, 0) = func->mus[0]; + h_ns(mu, idx_func, 0) = func->ns[0]; for (int p = 0; p < ndensity; ++p) - h_coeffs(mu, func_ind, p) = func->coeff[p]; + h_coeffs(mu, idx_func, p) = func->coeff[p]; - h_gen_cgs(mu, idx_ms_comb) = func->gen_cgs[0]; + h_gen_cgs(mu, idx_ms_combs) = func->gen_cgs[0]; - h_func_inds(mu, idx_ms_comb) = func_ind; - idx_ms_comb++; + h_idx_funcs(mu, idx_ms_combs) = idx_func; + idx_ms_combs++; } // rank > 1 - for (int func_ind = 0; func_ind < total_basis_size; ++func_ind) { - ACEBBasisFunction *func = &basis[func_ind]; + for (int idx_func = 0; idx_func < total_basis_size; ++idx_func) { + ACEBBasisFunction *func = &basis[idx_func]; // TODO: check if func->ctildes are zero, then skip - const int func_ind_through = total_basis_size_rank1 + func_ind; + const int idx_func_through = total_basis_size_rank1 + idx_func; - const int rank = h_rank(mu, func_ind_through) = func->rank; - h_num_ms_combs(mu, func_ind_through) = func->num_ms_combs; + const int rank = h_rank(mu, idx_func_through) = func->rank; + h_num_ms_combs(mu, idx_func_through) = func->num_ms_combs; for (int t = 0; t < rank; t++) { - h_mus(mu, func_ind_through, t) = func->mus[t]; - h_ns(mu, func_ind_through, t) = func->ns[t]; - h_ls(mu, func_ind_through, t) = func->ls[t]; + h_mus(mu, idx_func_through, t) = func->mus[t]; + h_ns(mu, idx_func_through, t) = func->ns[t]; + h_ls(mu, idx_func_through, t) = func->ls[t]; } for (int p = 0; p < ndensity; ++p) - h_coeffs(mu, func_ind_through, p) = func->coeff[p]; - + h_coeffs(mu, idx_func_through, p) = func->coeff[p]; // loop over {ms} combinations in sum for (int ms_ind = 0; ms_ind < func->num_ms_combs; ++ms_ind) { auto ms = &func->ms_combs[ms_ind * rank]; // current ms-combination (of length = rank) for (int t = 0; t < rank; t++) - h_ms_combs(mu, idx_ms_comb, t) = ms[t]; + h_ms_combs(mu, idx_ms_combs, t) = ms[t]; + h_gen_cgs(mu, idx_ms_combs) = func->gen_cgs[ms_ind]; - h_gen_cgs(mu, idx_ms_comb) = func->gen_cgs[ms_ind]; - - - h_func_inds(mu, idx_ms_comb) = func_ind_through; - idx_ms_comb++; + h_idx_funcs(mu, idx_ms_combs) = idx_func_through; + idx_ms_combs++; } } @@ -427,7 +422,7 @@ void PairPACEExtrapolationKokkos::copy_tilde() Kokkos::deep_copy(d_rank, h_rank); Kokkos::deep_copy(d_num_ms_combs, h_num_ms_combs); - Kokkos::deep_copy(d_func_inds, h_func_inds); + Kokkos::deep_copy(d_idx_funcs, h_idx_funcs); Kokkos::deep_copy(d_mus, h_mus); Kokkos::deep_copy(d_ns, h_ns); Kokkos::deep_copy(d_ls, h_ls); @@ -477,6 +472,7 @@ void PairPACEExtrapolationKokkos::init_style() // spherical harmonics + MemKK::realloc_kokkos(d_idx_sph, "pace:idx_sph", (lmax + 1) * (lmax + 1)); MemKK::realloc_kokkos(alm, "pace:alm", (lmax + 1) * (lmax + 1)); MemKK::realloc_kokkos(blm, "pace:blm", (lmax + 1) * (lmax + 1)); MemKK::realloc_kokkos(cl, "pace:cl", lmax + 1); @@ -575,6 +571,7 @@ void PairPACEExtrapolationKokkos::compute(int eflag_in, int vflag_in atomKK->modified(Host,F_MASK); return; } + eflag = eflag_in; vflag = vflag_in; @@ -602,6 +599,7 @@ void PairPACEExtrapolationKokkos::compute(int eflag_in, int vflag_in //zeroify array memset(extrapolation_grade_gamma, 0, nmax * sizeof(*extrapolation_grade_gamma)); } + if (flag_corerep_factor && atom->nlocal > nmax_corerep) { memory->destroy(corerep_factor); nmax_corerep = atom->nlocal; @@ -647,7 +645,6 @@ void PairPACEExtrapolationKokkos::compute(int eflag_in, int vflag_in chunk_size = MIN(chunksize,inum); // "chunksize" variable is set by user chunk_offset = 0; - grow(chunk_size, maxneigh); EV_FLOAT ev; @@ -656,14 +653,12 @@ void PairPACEExtrapolationKokkos::compute(int eflag_in, int vflag_in Kokkos::deep_copy(weights, 0.0); Kokkos::deep_copy(weights_rank1, 0.0); - Kokkos::deep_copy(A, 0.0); + Kokkos::deep_copy(A_sph, 0.0); Kokkos::deep_copy(A_rank1, 0.0); Kokkos::deep_copy(rhos, 0.0); - Kokkos::deep_copy(rho_core, 0.0); Kokkos::deep_copy(d_d_min, PairPACEExtrapolation::aceimpl->basis_set->cutoffmax); Kokkos::deep_copy(d_jj_min, -1); - Kokkos::deep_copy(projections, 0.0); Kokkos::deep_copy(d_gamma, 0.0); Kokkos::deep_copy(d_corerep, 0.0); @@ -693,15 +688,6 @@ void PairPACEExtrapolationKokkos::compute(int eflag_in, int vflag_in Kokkos::parallel_for("ComputeRadial",policy_radial,*this); } - //ComputeYlm - { - int vector_length = vector_length_default; - int team_size = 16; - check_team_size_for(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); - typename Kokkos::TeamPolicy policy_ylm(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); - Kokkos::parallel_for("ComputeYlm",policy_ylm,*this); - } - //ComputeAi { int vector_length = vector_length_default; @@ -737,7 +723,7 @@ void PairPACEExtrapolationKokkos::compute(int eflag_in, int vflag_in //ComputeWeights { - typename Kokkos::RangePolicy policy_weights(0, chunk_size * idx_ms_combs_max); + typename Kokkos::RangePolicy policy_weights(0,chunk_size * idx_ms_combs_max); Kokkos::parallel_for("ComputeWeights",policy_weights,*this); } @@ -746,7 +732,7 @@ void PairPACEExtrapolationKokkos::compute(int eflag_in, int vflag_in int vector_length = vector_length_default; int team_size = team_size_default; check_team_size_for(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); - typename Kokkos::TeamPolicy policy_derivative(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); + typename Kokkos::TeamPolicy policy_derivative(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); Kokkos::parallel_for("ComputeDerivative",policy_derivative,*this); } @@ -772,16 +758,18 @@ void PairPACEExtrapolationKokkos::compute(int eflag_in, int vflag_in } ev += ev_tmp; - //if flag_compute_extrapolation_grade - copy current d_gamma to extrapolation_grade_gamma + // if flag_compute_extrapolation_grade - copy current d_gamma to extrapolation_grade_gamma + if (flag_compute_extrapolation_grade){ h_gamma = Kokkos::create_mirror_view(d_gamma); Kokkos::deep_copy(h_gamma, d_gamma); memcpy(extrapolation_grade_gamma+chunk_offset, (void *) h_gamma.data(), sizeof(double)*chunk_size); } + if (flag_corerep_factor) { - h_corerep = Kokkos::create_mirror_view(d_corerep); - Kokkos::deep_copy(h_corerep,d_corerep); - memcpy(corerep_factor+chunk_offset, (void *) h_corerep.data(), sizeof(double)*chunk_size); + h_corerep = Kokkos::create_mirror_view(d_corerep); + Kokkos::deep_copy(h_corerep,d_corerep); + memcpy(corerep_factor+chunk_offset, (void *) h_corerep.data(), sizeof(double)*chunk_size); } chunk_offset += chunk_size; @@ -909,18 +897,18 @@ void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeNeig Kokkos::MinLoc reducer_scalar(djjmin); // loop over ncount (actual neighbours withing cutoff) rather than jnum (total number of neigh in cutoff+skin) Kokkos::parallel_reduce(Kokkos::TeamThreadRange(team, ncount), - [&](const int offset, minloc_value_type &min_d_dist) { - int j = d_nearest(ii,offset); - j &= NEIGHMASK; - const int jtype = type(j); - auto r = d_rnorms(ii,offset); - const int mu_j = d_map(type(j)); - const F_FLOAT d = r - (d_cut_in(mu_i, mu_j) - d_dcut_in(mu_i, mu_j)); - if (d < min_d_dist.val) { - min_d_dist.val = d; - min_d_dist.loc = offset; - } - }, reducer_scalar); + [&](const int offset, minloc_value_type &min_d_dist) { + int j = d_nearest(ii,offset); + j &= NEIGHMASK; + const int jtype = type(j); + auto r = d_rnorms(ii,offset); + const int mu_j = d_map(type(j)); + const F_FLOAT d = r - (d_cut_in(mu_i, mu_j) - d_dcut_in(mu_i, mu_j)); + if (d < min_d_dist.val) { + min_d_dist.val = d; + min_d_dist.loc = offset; + } + }, reducer_scalar); d_d_min(ii) = djjmin.val; d_jj_min(ii) = djjmin.loc;// d_jj_min should be NOT in 0..jnum range, but in 0..d_ncount(<=jnum) } else { @@ -956,28 +944,6 @@ void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeRadi /* ---------------------------------------------------------------------- */ -template -KOKKOS_INLINE_FUNCTION -void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeYlm, const typename Kokkos::TeamPolicy::member_type& team) const -{ - // Extract the atom number - int ii = team.team_rank() + team.team_size() * (team.league_rank() % - ((chunk_size+team.team_size()-1)/team.team_size())); - if (ii >= chunk_size) return; - - // Extract the neighbor number - const int jj = team.league_rank() / ((chunk_size+team.team_size()-1)/team.team_size()); - const int ncount = d_ncount(ii); - if (jj >= ncount) return; - - const double xn = d_rhats(ii, jj, 0); - const double yn = d_rhats(ii, jj, 1); - const double zn = d_rhats(ii, jj, 2); - compute_ylm(ii,jj,xn,yn,zn,lmax); -} - -/* ---------------------------------------------------------------------- */ - template KOKKOS_INLINE_FUNCTION void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeAi, const typename Kokkos::TeamPolicy::member_type& team) const @@ -999,13 +965,127 @@ void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeAi, Kokkos::atomic_add(&A_rank1(ii, mu_j, n), gr(ii, jj, n) * Y00); // rank > 1 - for (int n = 0; n < nradmax; n++) { - for (int l = 0; l <= lmax; l++) { - for (int m = 0; m <= l; m++) { - const int idx = l * (l + 1) + m; // (l, m) - Kokkos::atomic_add(&A(ii, mu_j, n, idx).re, fr(ii, jj, n, l) * ylm(ii, jj, idx).re); - Kokkos::atomic_add(&A(ii, mu_j, n, idx).im, fr(ii, jj, n, l) * ylm(ii, jj, idx).im); + + // Compute plm and ylm + + // requires rx^2 + ry^2 + rz^2 = 1 , NO CHECKING IS PERFORMED !!!!!!!!! + // requires -1 <= rz <= 1 , NO CHECKING IS PERFORMED !!!!!!!!! + // prefactors include 1/sqrt(2) factor compared to reference + + complex ylm, phase; + complex phasem, mphasem1; + complex dyx, dyy, dyz; + complex rdy; + + const double rx = d_rhats(ii, jj, 0); + const double ry = d_rhats(ii, jj, 1); + const double rz = d_rhats(ii, jj, 2); + + phase.re = rx; + phase.im = ry; + + double plm_idx,plm_idx1,plm_idx2; + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + + int idx_sph = 0; + + // m = 0 + for (int l = 0; l <= lmax; l++) { + // const int idx = l * (l + 1); + + if (l == 0) { + // l=0, m=0 + // plm[0] = Y00/sq1o4pi; //= sq1o4pi; + plm_idx = Y00; //= 1; + } else if (l == 1) { + // l=1, m=0 + plm_idx = Y00 * sq3 * rz; + } else { + // l>=2, m=0 + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + } + + ylm.re = plm_idx; + ylm.im = 0.0; + + for (int n = 0; n < nradmax; n++) { + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).re, fr(ii, jj, l, n) * ylm.re); + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).im, fr(ii, jj, l, n) * ylm.im); + } + + plm_idx2 = plm_idx1; + plm_idx1 = plm_idx; + + idx_sph++; + } + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + + // m = 1 + for (int l = 1; l <= lmax; l++) { + // const int idx = l * (l + 1) + 1; // (l, 1) + + if (l == 1) { + // l=1, m=1 + plm_idx = -sq3o2 * Y00; + } else if (l == 2) { + const double t = dl(l) * plm_idx1; + plm_idx = t * rz; + } else { + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + } + + ylm = phase * plm_idx; + + for (int n = 0; n < nradmax; n++) { + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).re, fr(ii, jj, l, n) * ylm.re); + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).im, fr(ii, jj, l, n) * ylm.im); + } + + plm_idx2 = plm_idx1; + plm_idx1 = plm_idx; + + idx_sph++; + } + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + + double plm_mm1_mm1 = -sq3o2 * Y00; // (1, 1) + + // m > 1 + phasem = phase; + for (int m = 2; m <= lmax; m++) { + + mphasem1.re = phasem.re * double(m); + mphasem1.im = phasem.im * double(m); + phasem = phasem * phase; + + for (int l = m; l <= lmax; l++) { + // const int idx = l * (l + 1) + m; + + if (l == m) { + plm_idx = cl(l) * plm_mm1_mm1; // (m+1, m) + plm_mm1_mm1 = plm_idx; + } else if (l == (m + 1)) { + const double t = dl(l) * plm_mm1_mm1; // (m - 1, m - 1) + plm_idx = t * rz; // (m, m) + } else { + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); } + + ylm.re = phasem.re * plm_idx; + ylm.im = phasem.im * plm_idx; + + for (int n = 0; n < nradmax; n++) { + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).re, fr(ii, jj, l, n) * ylm.re); + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).im, fr(ii, jj, l, n) * ylm.im); + } + + plm_idx2 = plm_idx1; + plm_idx1 = plm_idx; + + idx_sph++; } } @@ -1019,17 +1099,35 @@ template KOKKOS_INLINE_FUNCTION void PairPACEExtrapolationKokkos::operator() (TagPairPACEConjugateAi, const int& ii) const { - //complex conjugate A's (for NEGATIVE (-m) terms) - // for rank > 1 for (int mu_j = 0; mu_j < nelements; mu_j++) { - for (int n = 0; n < nradmax; n++) { - for (int l = 0; l <= lmax; l++) { + + // transpose + + int idx_sph = 0; + + for (int m = 0; m <= lmax; m++) { + for (int l = m; l <= lmax; l++) { + const int idx = l * (l + 1) + m; + for (int n = 0; n < nradmax; n++) { + A(ii, mu_j, idx, n) = A_sph(ii, mu_j, idx_sph, n); + } + + idx_sph++; + } + } + + // complex conjugate A's (for NEGATIVE (-m) terms) + // for rank > 1 + + for (int l = 0; l <= lmax; l++) { //fill in -m part in the outer loop using the same m <-> -m symmetry as for Ylm - for (int m = 1; m <= l; m++) { - const int idx = l * (l + 1) + m; // (l, m) - const int idxm = l * (l + 1) - m; // (l, -m) - const int factor = m % 2 == 0 ? 1 : -1; - A(ii, mu_j, n, idxm) = A(ii, mu_j, n, idx).conj() * (double)factor; + for (int m = 1; m <= l; m++) { + const int idx = l * (l + 1) + m; // (l, m) + const int idxm = l * (l + 1) - m; // (l, -m) + const int idx_sph = d_idx_sph(idx); + const int factor = m % 2 == 0 ? 1 : -1; + for (int n = 0; n < nradmax; n++) { + A(ii, mu_j, idxm, n) = A_sph(ii, mu_j, idx_sph, n).conj() * (double)factor; } } } @@ -1042,73 +1140,72 @@ template KOKKOS_INLINE_FUNCTION void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeRho, const int& iter) const { - const int idx_ms_comb = iter / chunk_size; + const int idx_ms_combs = iter / chunk_size; const int ii = iter % chunk_size; const int i = d_ilist[ii + chunk_offset]; const int mu_i = d_map(type(i)); - if (idx_ms_comb >= d_idx_ms_combs_count(mu_i)) return; + if (idx_ms_combs >= d_idx_ms_combs_count(mu_i)) return; const int ndensity = d_ndensity(mu_i); - const int func_ind = d_func_inds(mu_i, idx_ms_comb); - const int rank = d_rank(mu_i, func_ind); + const int idx_func = d_idx_funcs(mu_i, idx_ms_combs); + const int rank = d_rank(mu_i, idx_func); const int r = rank - 1; // Basis functions B with iterative product and density rho(p) calculation if (rank == 1) { - const int mu = d_mus(mu_i, func_ind, 0); - const int n = d_ns(mu_i, func_ind, 0); + const int mu = d_mus(mu_i, idx_func, 0); + const int n = d_ns(mu_i, idx_func, 0); double A_cur = A_rank1(ii, mu, n - 1); for (int p = 0; p < ndensity; ++p) { //for rank=1 (r=0) only 1 ms-combination exists (ms_ind=0), so index of func.ctildes is 0..ndensity-1 - Kokkos::atomic_add(&rhos(ii, p), d_coeffs(mu_i, func_ind, p) * d_gen_cgs(mu_i, idx_ms_comb) * A_cur); + Kokkos::atomic_add(&rhos(ii, p), d_coeffs(mu_i, idx_func, p) * d_gen_cgs(mu_i, idx_ms_combs) * A_cur); } - - //gamma_i + // gamma_i if (flag_compute_extrapolation_grade) - Kokkos::atomic_add(&projections(ii, func_ind), d_gen_cgs(mu_i, idx_ms_comb) * A_cur); + Kokkos::atomic_add(&projections(ii, idx_func), d_gen_cgs(mu_i, idx_ms_combs) * A_cur); } else { // rank > 1 // loop over {ms} combinations in sum // loop over m, collect B = product of A with given ms - A_forward_prod(ii, idx_ms_comb, 0) = complex::one(); + A_forward_prod(ii, idx_ms_combs, 0) = complex::one(); // fill forward A-product triangle for (int t = 0; t < rank; t++) { //TODO: optimize ns[t]-1 -> ns[t] during functions construction - const int mu = d_mus(mu_i, func_ind, t); - const int n = d_ns(mu_i, func_ind, t); - const int l = d_ls(mu_i, func_ind, t); - const int m = d_ms_combs(mu_i, idx_ms_comb, t); // current ms-combination (of length = rank) + const int mu = d_mus(mu_i, idx_func, t); + const int n = d_ns(mu_i, idx_func, t); + const int l = d_ls(mu_i, idx_func, t); + const int m = d_ms_combs(mu_i, idx_ms_combs, t); // current ms-combination (of length = rank) const int idx = l * (l + 1) + m; // (l, m) - A_list(ii, idx_ms_comb, t) = A(ii, mu, n - 1, idx); - A_forward_prod(ii, idx_ms_comb, t + 1) = A_forward_prod(ii, idx_ms_comb, t) * A_list(ii, idx_ms_comb, t); + A_list(ii, idx_ms_combs, t) = A(ii, mu, idx, n - 1); + A_forward_prod(ii, idx_ms_combs, t + 1) = A_forward_prod(ii, idx_ms_combs, t) * A_list(ii, idx_ms_combs, t); } complex A_backward_prod = complex::one(); // fill backward A-product triangle for (int t = r; t >= 1; t--) { - const complex dB = A_forward_prod(ii, idx_ms_comb, t) * A_backward_prod; // dB - product of all A's except t-th - dB_flatten(ii, idx_ms_comb, t) = dB; + const complex dB = A_forward_prod(ii, idx_ms_combs, t) * A_backward_prod; // dB - product of all A's except t-th + dB_flatten(ii, idx_ms_combs, t) = dB; - A_backward_prod = A_backward_prod * A_list(ii, idx_ms_comb, t); + A_backward_prod = A_backward_prod * A_list(ii, idx_ms_combs, t); } - dB_flatten(ii, idx_ms_comb, 0) = A_forward_prod(ii, idx_ms_comb, 0) * A_backward_prod; + dB_flatten(ii, idx_ms_combs, 0) = A_forward_prod(ii, idx_ms_combs, 0) * A_backward_prod; - const complex B = A_forward_prod(ii, idx_ms_comb, rank); + const complex B = A_forward_prod(ii, idx_ms_combs, rank); for (int p = 0; p < ndensity; ++p) { // real-part only multiplication - Kokkos::atomic_add(&rhos(ii, p), B.real_part_product(d_coeffs(mu_i, func_ind, p) * d_gen_cgs(mu_i, idx_ms_comb))); + Kokkos::atomic_add(&rhos(ii, p), B.real_part_product(d_coeffs(mu_i, idx_func, p) * d_gen_cgs(mu_i, idx_ms_combs))); } - //gamma_i + // gamma_i if (flag_compute_extrapolation_grade) - Kokkos::atomic_add(&projections(ii, func_ind), B.real_part_product(d_gen_cgs(mu_i, idx_ms_comb))); + Kokkos::atomic_add(&projections(ii, idx_func), B.real_part_product(d_gen_cgs(mu_i, idx_ms_combs))); } } @@ -1129,7 +1226,6 @@ void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeFS, double evdwl_cut; evdwl = fcut = dfcut = 0.0; - inner_cutoff(rho_core(ii), rho_cut, drho_cut, fcut, dfcut); FS_values_and_derivatives(ii, evdwl, mu_i); if (is_zbl) { @@ -1155,7 +1251,6 @@ void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeFS, for (int p = 0; p < ndensity; ++p) dF_drho(ii, p) *= fcut; - // tally energy contribution if (eflag) { // E0 shift @@ -1201,52 +1296,58 @@ template KOKKOS_INLINE_FUNCTION void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeWeights, const int& iter) const { - const int idx_ms_comb = iter / chunk_size; + const int idx_ms_combs = iter / chunk_size; const int ii = iter % chunk_size; const int i = d_ilist[ii + chunk_offset]; const int mu_i = d_map(type(i)); - if (idx_ms_comb >= d_idx_ms_combs_count(mu_i)) return; + if (idx_ms_combs >= d_idx_ms_combs_count(mu_i)) return; const int ndensity = d_ndensity(mu_i); - const int func_ind = d_func_inds(mu_i, idx_ms_comb); - const int rank = d_rank(mu_i, func_ind); + const int idx_func = d_idx_funcs(mu_i, idx_ms_combs); + const int rank = d_rank(mu_i, idx_func); // Weights and theta calculation if (rank == 1) { - const int mu = d_mus(mu_i, func_ind, 0); - const int n = d_ns(mu_i, func_ind, 0); + const int mu = d_mus(mu_i, idx_func, 0); + const int n = d_ns(mu_i, idx_func, 0); double theta = 0.0; for (int p = 0; p < ndensity; ++p) { // for rank=1 (r=0) only 1 ms-combination exists (ms_ind=0), so index of func.ctildes is 0..ndensity-1 - theta += dF_drho(ii, p) * d_coeffs(mu_i, func_ind, p) * d_gen_cgs(mu_i, idx_ms_comb); + theta += dF_drho(ii, p) * d_coeffs(mu_i, idx_func, p) * d_gen_cgs(mu_i, idx_ms_combs); } Kokkos::atomic_add(&weights_rank1(ii, mu, n - 1), theta); } else { // rank > 1 double theta = 0.0; for (int p = 0; p < ndensity; ++p) - theta += dF_drho(ii, p) * d_coeffs(mu_i, func_ind, p) * d_gen_cgs(mu_i, idx_ms_comb); + theta += dF_drho(ii, p) * d_coeffs(mu_i, idx_func, p) * d_gen_cgs(mu_i, idx_ms_combs); theta *= 0.5; // 0.5 factor due to possible double counting ??? for (int t = 0; t < rank; ++t) { - const int m_t = d_ms_combs(mu_i, idx_ms_comb, t); + const int m_t = d_ms_combs(mu_i, idx_ms_combs, t); const int factor = (m_t % 2 == 0 ? 1 : -1); - const complex dB = dB_flatten(ii, idx_ms_comb, t); - const int mu_t = d_mus(mu_i, func_ind, t); - const int n_t = d_ns(mu_i, func_ind, t); - const int l_t = d_ls(mu_i, func_ind, t); + const complex dB = dB_flatten(ii, idx_ms_combs, t); + const int mu_t = d_mus(mu_i, idx_func, t); + const int n_t = d_ns(mu_i, idx_func, t); + const int l_t = d_ls(mu_i, idx_func, t); const int idx = l_t * (l_t + 1) + m_t; // (l, m) - const complex value = theta * dB; - Kokkos::atomic_add(&(weights(ii, mu_t, n_t - 1, idx).re), value.re); - Kokkos::atomic_add(&(weights(ii, mu_t, n_t - 1, idx).im), value.im); + const int idx_sph = d_idx_sph(idx); + if (idx_sph >= 0) { + const complex value = theta * dB; + Kokkos::atomic_add(&(weights(ii, mu_t, idx_sph, n_t - 1).re), value.re); + Kokkos::atomic_add(&(weights(ii, mu_t, idx_sph, n_t - 1).im), value.im); + } // update -m_t (that could also be positive), because the basis is half_basis const int idxm = l_t * (l_t + 1) - m_t; // (l, -m) - const complex valuem = theta * dB.conj() * (double)factor; - Kokkos::atomic_add(&(weights(ii, mu_t, n_t - 1, idxm).re), valuem.re); - Kokkos::atomic_add(&(weights(ii, mu_t, n_t - 1, idxm).im), valuem.im); + const int idxm_sph = d_idx_sph(idxm); + if (idxm_sph >= 0) { + const complex valuem = theta * dB.conj() * (double)factor; + Kokkos::atomic_add(&(weights(ii, mu_t, idxm_sph, n_t - 1).re), valuem.re); + Kokkos::atomic_add(&(weights(ii, mu_t, idxm_sph, n_t - 1).im), valuem.im); + } } } } @@ -1293,37 +1394,239 @@ void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeDeri } // for rank > 1 - for (int n = 0; n < nradmax; n++) { - for (int l = 0; l <= lmax; l++) { - const double R_over_r = fr(ii, jj, n, l) * rinv; - const double DR = dfr(ii, jj, n, l); - // for m >= 0 - for (int m = 0; m <= l; m++) { - const int idx = l * (l + 1) + m; // (l, m) - complex w = weights(ii, mu_j, n, idx); + // compute plm, dplm, ylm and dylm + // requires rx^2 + ry^2 + rz^2 = 1 , NO CHECKING IS PERFORMED !!!!!!!!! + // requires -1 <= rz <= 1 , NO CHECKING IS PERFORMED !!!!!!!!! + // prefactors include 1/sqrt(2) factor compared to reference + + complex ylm,dylm[3]; + complex phase; + complex phasem, mphasem1; + complex dyx, dyy, dyz; + complex rdy; + + const double rx = d_rhats(ii, jj, 0); + const double ry = d_rhats(ii, jj, 1); + const double rz = d_rhats(ii, jj, 2); + + phase.re = rx; + phase.im = ry; + + double plm_idx,plm_idx1,plm_idx2; + double dplm_idx,dplm_idx1,dplm_idx2; + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + dplm_idx = dplm_idx1 = dplm_idx2 = 0.0; + + int idx_sph = 0; + + // m = 0 + for (int l = 0; l <= lmax; l++) { + // const int idx = l * (l + 1); + + if (l == 0) { + // l=0, m=0 + // plm[0] = Y00/sq1o4pi; //= sq1o4pi; + plm_idx = Y00; //= 1; + dplm_idx = 0.0; + } else if (l == 1) { + // l=1, m=0 + plm_idx = Y00 * sq3 * rz; + dplm_idx = Y00 * sq3; + } else { + // l>=2, m=0 + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + dplm_idx = alm(idx_sph) * (plm_idx1 + rz * dplm_idx1 + blm(idx_sph) * dplm_idx2); + } + + ylm.re = plm_idx; + ylm.im = 0.0; + + dyz.re = dplm_idx; + rdy.re = dyz.re * rz; + + dylm[0].re = -rdy.re * rx; + dylm[0].im = 0.0; + dylm[1].re = -rdy.re * ry; + dylm[1].im = 0.0; + dylm[2].re = dyz.re - rdy.re * rz; + dylm[2].im = 0; + + for (int n = 0; n < nradmax; n++) { + + const double R_over_r = fr(ii, jj, l, n) * rinv; + const double DR = dfr(ii, jj, l, n); + const complex Y_DR = ylm * DR; + + complex w = weights(ii, mu_j, idx_sph, n); + if (w.re == 0.0 && w.im == 0.0) continue; + + complex grad_phi_nlm[3]; + grad_phi_nlm[0] = Y_DR * r_hat[0] + dylm[0] * R_over_r; + grad_phi_nlm[1] = Y_DR * r_hat[1] + dylm[1] * R_over_r; + grad_phi_nlm[2] = Y_DR * r_hat[2] + dylm[2] * R_over_r; + // real-part multiplication only + f_ji[0] += w.real_part_product(grad_phi_nlm[0]); + f_ji[1] += w.real_part_product(grad_phi_nlm[1]); + f_ji[2] += w.real_part_product(grad_phi_nlm[2]); + } + + plm_idx2 = plm_idx1; + dplm_idx2 = dplm_idx1; + + plm_idx1 = plm_idx; + dplm_idx1 = dplm_idx; + + idx_sph++; + } + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + dplm_idx = dplm_idx1 = dplm_idx2 = 0.0; + + // m = 1 + for (int l = 1; l <= lmax; l++) { + // const int idx = l * (l + 1) + 1; // (l, 1) + + if (l == 1) { + // l=1, m=1 + plm_idx = -sq3o2 * Y00; + dplm_idx = 0.0; + } else if (l == 2) { + const double t = dl(l) * plm_idx1; + plm_idx = t * rz; + dplm_idx = t; + } else { + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + dplm_idx = alm(idx_sph) * (plm_idx1 + rz * dplm_idx1 + blm(idx_sph) * dplm_idx2); + } + + ylm = phase * plm_idx; + + dyx.re = plm_idx; + dyx.im = 0.0; + dyy.re = 0.0; + dyy.im = plm_idx; + dyz.re = phase.re * dplm_idx; + dyz.im = phase.im * dplm_idx; + + rdy.re = rx * dyx.re + +rz * dyz.re; + rdy.im = ry * dyy.im + rz * dyz.im; + + dylm[0].re = dyx.re - rdy.re * rx; + dylm[0].im = -rdy.im * rx; + dylm[1].re = -rdy.re * ry; + dylm[1].im = dyy.im - rdy.im * ry; + dylm[2].re = dyz.re - rdy.re * rz; + dylm[2].im = dyz.im - rdy.im * rz; + + for (int n = 0; n < nradmax; n++) { + + const double R_over_r = fr(ii, jj, l, n) * rinv; + const double DR = dfr(ii, jj, l, n); + const complex Y_DR = ylm * DR; + + complex w = weights(ii, mu_j, idx_sph, n); + if (w.re == 0.0 && w.im == 0.0) continue; + // counting for -m cases if m > 0 + w.re *= 2.0; + w.im *= 2.0; + + complex grad_phi_nlm[3]; + grad_phi_nlm[0] = Y_DR * r_hat[0] + dylm[0] * R_over_r; + grad_phi_nlm[1] = Y_DR * r_hat[1] + dylm[1] * R_over_r; + grad_phi_nlm[2] = Y_DR * r_hat[2] + dylm[2] * R_over_r; + // real-part multiplication only + f_ji[0] += w.real_part_product(grad_phi_nlm[0]); + f_ji[1] += w.real_part_product(grad_phi_nlm[1]); + f_ji[2] += w.real_part_product(grad_phi_nlm[2]); + } + + plm_idx2 = plm_idx1; + dplm_idx2 = dplm_idx1; + + plm_idx1 = plm_idx; + dplm_idx1 = dplm_idx; + + idx_sph++; + } + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + dplm_idx = dplm_idx1 = dplm_idx2 = 0.0; + + double plm_mm1_mm1 = -sq3o2 * Y00; // (1, 1) + + // m > 1 + phasem = phase; + for (int m = 2; m <= lmax; m++) { + + mphasem1.re = phasem.re * double(m); + mphasem1.im = phasem.im * double(m); + phasem = phasem * phase; + + for (int l = m; l <= lmax; l++) { + // const int idx = l * (l + 1) + m; + + if (l == m) { + plm_idx = cl(l) * plm_mm1_mm1; // (m+1, m) + dplm_idx = 0.0; + plm_mm1_mm1 = plm_idx; + } else if (l == (m + 1)) { + const double t = dl(l) * plm_mm1_mm1; // (m - 1, m - 1) + plm_idx = t * rz; // (m, m) + dplm_idx = t; + } else { + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + dplm_idx = alm(idx_sph) * (plm_idx1 + rz * dplm_idx1 + blm(idx_sph) * dplm_idx2); + } + + ylm.re = phasem.re * plm_idx; + ylm.im = phasem.im * plm_idx; + + dyx = mphasem1 * plm_idx; + dyy.re = -dyx.im; + dyy.im = dyx.re; + dyz = phasem * dplm_idx; + + rdy.re = rx * dyx.re + ry * dyy.re + rz * dyz.re; + rdy.im = rx * dyx.im + ry * dyy.im + rz * dyz.im; + + dylm[0].re = dyx.re - rdy.re * rx; + dylm[0].im = dyx.im - rdy.im * rx; + dylm[1].re = dyy.re - rdy.re * ry; + dylm[1].im = dyy.im - rdy.im * ry; + dylm[2].re = dyz.re - rdy.re * rz; + dylm[2].im = dyz.im - rdy.im * rz; + + for (int n = 0; n < nradmax; n++) { + + const double R_over_r = fr(ii, jj, l, n) * rinv; + const double DR = dfr(ii, jj, l, n); + const complex Y_DR = ylm * DR; + + complex w = weights(ii, mu_j, idx_sph, n); if (w.re == 0.0 && w.im == 0.0) continue; // counting for -m cases if m > 0 - if (m > 0) { - w.re *= 2.0; - w.im *= 2.0; - } - - complex DY[3]; - DY[0] = dylm(ii, jj, idx, 0); - DY[1] = dylm(ii, jj, idx, 1); - DY[2] = dylm(ii, jj, idx, 2); - const complex Y_DR = ylm(ii, jj, idx) * DR; + w.re *= 2.0; + w.im *= 2.0; complex grad_phi_nlm[3]; - grad_phi_nlm[0] = Y_DR * r_hat[0] + DY[0] * R_over_r; - grad_phi_nlm[1] = Y_DR * r_hat[1] + DY[1] * R_over_r; - grad_phi_nlm[2] = Y_DR * r_hat[2] + DY[2] * R_over_r; + grad_phi_nlm[0] = Y_DR * r_hat[0] + dylm[0] * R_over_r; + grad_phi_nlm[1] = Y_DR * r_hat[1] + dylm[1] * R_over_r; + grad_phi_nlm[2] = Y_DR * r_hat[2] + dylm[2] * R_over_r; // real-part multiplication only f_ji[0] += w.real_part_product(grad_phi_nlm[0]); f_ji[1] += w.real_part_product(grad_phi_nlm[1]); f_ji[2] += w.real_part_product(grad_phi_nlm[2]); } + + plm_idx2 = plm_idx1; + dplm_idx2 = dplm_idx1; + + plm_idx1 = plm_idx; + dplm_idx1 = dplm_idx; + + idx_sph++; } } @@ -1461,31 +1764,46 @@ void PairPACEExtrapolationKokkos::v_tally_xyz(EV_FLOAT &ev, const in template void PairPACEExtrapolationKokkos::pre_compute_harmonics(int lmax) { + auto h_idx_sph = Kokkos::create_mirror_view(d_idx_sph); auto h_alm = Kokkos::create_mirror_view(alm); auto h_blm = Kokkos::create_mirror_view(blm); auto h_cl = Kokkos::create_mirror_view(cl); auto h_dl = Kokkos::create_mirror_view(dl); - for (int l = 1; l <= lmax; l++) { - const double lsq = l * l; - const double ld = 2 * l; - const double l1 = (4 * lsq - 1); - const double l2 = lsq - ld + 1; - for (int m = 0; m < l - 1; m++) { - const double msq = m * m; - const double a = sqrt((double(l1)) / (double(lsq - msq))); - const double b = -sqrt((double(l2 - msq)) / (double(4 * l2 - 1))); + Kokkos::deep_copy(h_idx_sph,-1); + + int idx_sph = 0; + for (int m = 0; m <= lmax; m++) { + const double msq = m * m; + for (int l = m; l <= lmax; l++) { const int idx = l * (l + 1) + m; // (l, m) - h_alm(idx) = a; - h_blm(idx) = b; + h_idx_sph(idx) = idx_sph; + + double a = 0.0; + double b = 0.0; + + if (l > 1 && l != m) { + const double lsq = l * l; + const double ld = 2 * l; + const double l1 = (4 * lsq - 1); + const double l2 = lsq - ld + 1; + + a = sqrt((double(l1)) / (double(lsq - msq))); + b = -sqrt((double(l2 - msq)) / (double(4 * l2 - 1))); + } + h_alm(idx_sph) = a; + h_blm(idx_sph) = b; + idx_sph++; } } + idx_sph_max = idx_sph; for (int l = 1; l <= lmax; l++) { h_cl(l) = -sqrt(1.0 + 0.5 / (double(l))); h_dl(l) = sqrt(double(2 * (l - 1) + 3)); } + Kokkos::deep_copy(d_idx_sph, h_idx_sph); Kokkos::deep_copy(alm, h_alm); Kokkos::deep_copy(blm, h_blm); Kokkos::deep_copy(cl, h_cl); @@ -1494,143 +1812,6 @@ void PairPACEExtrapolationKokkos::pre_compute_harmonics(int lmax) /* ---------------------------------------------------------------------- */ -template -KOKKOS_INLINE_FUNCTION -void PairPACEExtrapolationKokkos::compute_barplm(int ii, int jj, double rz, int lmax) const -{ - // requires -1 <= rz <= 1 , NO CHECKING IS PERFORMED !!!!!!!!! - // prefactors include 1/sqrt(2) factor compared to reference - - // l=0, m=0 - // plm(ii, jj, 0, 0) = Y00/sq1o4pi; //= sq1o4pi; - plm(ii, jj, 0) = Y00; //= 1; - dplm(ii, jj, 0) = 0.0; - - if (lmax > 0) { - - // l=1, m=0 - plm(ii, jj, 2) = Y00 * sq3 * rz; - dplm(ii, jj, 2) = Y00 * sq3; - - // l=1, m=1 - plm(ii, jj, 3) = -sq3o2 * Y00; - dplm(ii, jj, 3) = 0.0; - - // loop l = 2, lmax - for (int l = 2; l <= lmax; l++) { - for (int m = 0; m < l - 1; m++) { - const int idx = l * (l + 1) + m; // (l, m) - const int idx1 = (l - 1) * l + m; // (l - 1, m) - const int idx2 = (l - 2) * (l - 1) + m; // (l - 2, m) - plm(ii, jj, idx) = alm(idx) * (rz * plm(ii, jj, idx1) + blm(idx) * plm(ii, jj, idx2)); - dplm(ii, jj, idx) = alm(idx) * (plm(ii, jj, idx1) + rz * dplm(ii, jj, idx1) + blm(idx) * dplm(ii, jj, idx2)); - } - const int idx = l * (l + 1) + l; // (l, l) - const int idx1 = l * (l + 1) + l - 1; // (l, l - 1) - const int idx2 = (l - 1) * l + l - 1; // (l - 1, l - 1) - const double t = dl(l) * plm(ii, jj, idx2); - plm(ii, jj, idx1) = t * rz; - dplm(ii, jj, idx1) = t; - plm(ii, jj, idx) = cl(l) * plm(ii, jj, idx2); - dplm(ii, jj, idx) = 0.0; - } - } -} - -/* ---------------------------------------------------------------------- */ - -template -KOKKOS_INLINE_FUNCTION -void PairPACEExtrapolationKokkos::compute_ylm(int ii, int jj, double rx, double ry, double rz, int lmax) const -{ - // requires rx^2 + ry^2 + rz^2 = 1 , NO CHECKING IS PERFORMED !!!!!!!!! - - complex phase; - complex phasem, mphasem1; - complex dyx, dyy, dyz; - complex rdy; - - phase.re = rx; - phase.im = ry; - - // compute barplm - compute_barplm(ii, jj, rz, lmax); - - // m = 0 - for (int l = 0; l <= lmax; l++) { - const int idx = l * (l + 1); - - ylm(ii, jj, idx).re = plm(ii, jj, idx); - ylm(ii, jj, idx).im = 0.0; - - dyz.re = dplm(ii, jj, idx); - rdy.re = dyz.re * rz; - - dylm(ii, jj, idx, 0).re = -rdy.re * rx; - dylm(ii, jj, idx, 0).im = 0.0; - dylm(ii, jj, idx, 1).re = -rdy.re * ry; - dylm(ii, jj, idx, 1).im = 0.0; - dylm(ii, jj, idx, 2).re = dyz.re - rdy.re * rz; - dylm(ii, jj, idx, 2).im = 0; - } - // m = 1 - for (int l = 1; l <= lmax; l++) { - const int idx = l * (l + 1) + 1; - - ylm(ii, jj, idx) = phase * plm(ii, jj, idx); - - dyx.re = plm(ii, jj, idx); - dyx.im = 0.0; - dyy.re = 0.0; - dyy.im = plm(ii, jj, idx); - dyz.re = phase.re * dplm(ii, jj, idx); - dyz.im = phase.im * dplm(ii, jj, idx); - - rdy.re = rx * dyx.re + +rz * dyz.re; - rdy.im = ry * dyy.im + rz * dyz.im; - - dylm(ii, jj, idx, 0).re = dyx.re - rdy.re * rx; - dylm(ii, jj, idx, 0).im = -rdy.im * rx; - dylm(ii, jj, idx, 1).re = -rdy.re * ry; - dylm(ii, jj, idx, 1).im = dyy.im - rdy.im * ry; - dylm(ii, jj, idx, 2).re = dyz.re - rdy.re * rz; - dylm(ii, jj, idx, 2).im = dyz.im - rdy.im * rz; - } - - // m > 1 - phasem = phase; - for (int m = 2; m <= lmax; m++) { - - mphasem1.re = phasem.re * double(m); - mphasem1.im = phasem.im * double(m); - phasem = phasem * phase; - - for (int l = m; l <= lmax; l++) { - const int idx = l * (l + 1) + m; - - ylm(ii, jj, idx).re = phasem.re * plm(ii, jj, idx); - ylm(ii, jj, idx).im = phasem.im * plm(ii, jj, idx); - - dyx = mphasem1 * plm(ii, jj, idx); - dyy.re = -dyx.im; - dyy.im = dyx.re; - dyz = phasem * dplm(ii, jj, idx); - - rdy.re = rx * dyx.re + ry * dyy.re + rz * dyz.re; - rdy.im = rx * dyx.im + ry * dyy.im + rz * dyz.im; - - dylm(ii, jj, idx, 0).re = dyx.re - rdy.re * rx; - dylm(ii, jj, idx, 0).im = dyx.im - rdy.im * rx; - dylm(ii, jj, idx, 1).re = dyy.re - rdy.re * ry; - dylm(ii, jj, idx, 1).im = dyy.im - rdy.im * ry; - dylm(ii, jj, idx, 2).re = dyz.re - rdy.re * rz; - dylm(ii, jj, idx, 2).im = dyz.im - rdy.im * rz; - } - } -} - -/* ---------------------------------------------------------------------- */ - template KOKKOS_INLINE_FUNCTION void PairPACEExtrapolationKokkos::cutoff_func_poly(const double r, const double r_in, const double delta_in, double &fc, double &dfc) const @@ -1759,11 +1940,11 @@ void PairPACEExtrapolationKokkos::evaluate_splines(const int ii, con spline_gk.calcSplines(ii, jj, r, gr, dgr); spline_rnl.calcSplines(ii, jj, r, d_values, d_derivatives); - for (int kk = 0; kk < (int)fr.extent(2); kk++) { - for (int ll = 0; ll < (int)fr.extent(3); ll++) { - const int flatten = kk*fr.extent(3) + ll; - fr(ii, jj, kk, ll) = d_values(ii, jj, flatten); - dfr(ii, jj, kk, ll) = d_derivatives(ii, jj, flatten); + for (int ll = 0; ll < (int)fr.extent(2); ll++) { + for (int kk = 0; kk < (int)fr.extent(3); kk++) { + const int flatten = kk*fr.extent(2) + ll; + fr(ii, jj, ll, kk) = d_values(ii, jj, flatten); + dfr(ii, jj, ll, kk) = d_derivatives(ii, jj, flatten); } } @@ -1783,7 +1964,7 @@ void PairPACEExtrapolationKokkos::SplineInterpolatorKokkos::operator rscalelookup = spline.rscalelookup; num_of_functions = spline.num_of_functions; - lookupTable = t_ace_3d4("lookupTable", ntot+1, num_of_functions); + lookupTable = t_ace_3d4_lr("lookupTable", ntot+1, num_of_functions); auto h_lookupTable = Kokkos::create_mirror_view(lookupTable); for (int i = 0; i < ntot+1; i++) for (int j = 0; j < num_of_functions; j++) @@ -1889,10 +2070,6 @@ double PairPACEExtrapolationKokkos::memory_usage() bytes += MemKK::memory_usage(d_derivatives); bytes += MemKK::memory_usage(cr); bytes += MemKK::memory_usage(dcr); - bytes += MemKK::memory_usage(plm); - bytes += MemKK::memory_usage(dplm); - bytes += MemKK::memory_usage(ylm); - bytes += MemKK::memory_usage(dylm); bytes += MemKK::memory_usage(d_ncount); bytes += MemKK::memory_usage(d_mu); bytes += MemKK::memory_usage(d_rhats); @@ -1911,7 +2088,7 @@ double PairPACEExtrapolationKokkos::memory_usage() bytes += MemKK::memory_usage(d_idx_ms_combs_count); bytes += MemKK::memory_usage(d_rank); bytes += MemKK::memory_usage(d_num_ms_combs); - bytes += MemKK::memory_usage(d_func_inds); + bytes += MemKK::memory_usage(d_idx_funcs); bytes += MemKK::memory_usage(d_mus); bytes += MemKK::memory_usage(d_ns); bytes += MemKK::memory_usage(d_ls); @@ -1940,47 +2117,6 @@ double PairPACEExtrapolationKokkos::memory_usage() return bytes; } -/* ---------------------------------------------------------------------- - extract method for extracting value of scale variable - ---------------------------------------------------------------------- */ - -template -void *PairPACEExtrapolationKokkos::extract(const char *str, int &dim) -{ - dim = 0; - //check if str=="flag_compute_extrapolation_grade" then compute extrapolation grades on this iteration - if (strcmp(str, "gamma_flag") == 0) return (void *) &flag_compute_extrapolation_grade; - if (strcmp(str, "corerep_flag") == 0) return (void *) &flag_corerep_factor; - - dim = 2; - if (strcmp(str, "scale") == 0) return (void *) scale; - return nullptr; -} - -/* ---------------------------------------------------------------------- - peratom requests from FixPair - return ptr to requested data - also return ncol = # of quantites per atom - 0 = per-atom vector - 1 or more = # of columns in per-atom array - return NULL if str is not recognized ----------------------------------------------------------------------- */ - -template -void *PairPACEExtrapolationKokkos::extract_peratom(const char *str, int &ncol) -{ - if (strcmp(str, "gamma") == 0) { - ncol = 0; - return (void *) extrapolation_grade_gamma; - } - if (strcmp(str, "corerep") == 0) { - ncol = 0; - return (void *) corerep_factor; - } - - return nullptr; -} - /* ---------------------------------------------------------------------- */ namespace LAMMPS_NS { @@ -1989,4 +2125,3 @@ template class PairPACEExtrapolationKokkos; template class PairPACEExtrapolationKokkos; #endif } - diff --git a/src/KOKKOS/pair_pace_extrapolation_kokkos.h b/src/KOKKOS/pair_pace_extrapolation_kokkos.h index aa6c49c36d..df8a0c1740 100644 --- a/src/KOKKOS/pair_pace_extrapolation_kokkos.h +++ b/src/KOKKOS/pair_pace_extrapolation_kokkos.h @@ -36,7 +36,6 @@ class PairPACEExtrapolationKokkos : public PairPACEExtrapolation { public: struct TagPairPACEComputeNeigh{}; struct TagPairPACEComputeRadial{}; - struct TagPairPACEComputeYlm{}; struct TagPairPACEComputeAi{}; struct TagPairPACEConjugateAi{}; struct TagPairPACEComputeRho{}; @@ -67,9 +66,6 @@ class PairPACEExtrapolationKokkos : public PairPACEExtrapolation { KOKKOS_INLINE_FUNCTION void operator() (TagPairPACEComputeRadial,const typename Kokkos::TeamPolicy::member_type& team) const; - KOKKOS_INLINE_FUNCTION - void operator() (TagPairPACEComputeYlm,const typename Kokkos::TeamPolicy::member_type& team) const; - KOKKOS_INLINE_FUNCTION void operator() (TagPairPACEComputeAi,const typename Kokkos::TeamPolicy::member_type& team) const; @@ -99,12 +95,8 @@ class PairPACEExtrapolationKokkos : public PairPACEExtrapolation { KOKKOS_INLINE_FUNCTION void operator() (TagPairPACEComputeForce,const int& ii, EV_FLOAT&) const; - - void *extract(const char *str, int &dim) override; - void *extract_peratom(const char *str, int &ncol) override; - protected: - int inum, maxneigh, chunk_size, chunk_offset, idx_ms_combs_max, total_num_functions_max; + int inum, maxneigh, chunk_size, chunk_offset, idx_ms_combs_max, total_num_functions_max, idx_sph_max; int host_flag; int eflag, vflag; @@ -165,12 +157,6 @@ class PairPACEExtrapolationKokkos : public PairPACEExtrapolation { const F_FLOAT &fx, const F_FLOAT &fy, const F_FLOAT &fz, const F_FLOAT &delx, const F_FLOAT &dely, const F_FLOAT &delz) const; - KOKKOS_INLINE_FUNCTION - void compute_barplm(int, int, double, int) const; - - KOKKOS_INLINE_FUNCTION - void compute_ylm(int, int, double, double, double, int) const; - KOKKOS_INLINE_FUNCTION void cutoff_func_poly(const double, const double, const double, double &, double &) const; @@ -202,15 +188,19 @@ class PairPACEExtrapolationKokkos : public PairPACEExtrapolation { typedef Kokkos::View t_ace_1i; typedef Kokkos::View t_ace_2i; + typedef Kokkos::View t_ace_2i_lr; typedef Kokkos::View t_ace_3i; + typedef Kokkos::View t_ace_3i_lr; typedef Kokkos::View t_ace_4i; typedef Kokkos::View t_ace_1d; typedef Kokkos::View t_ace_2d; + typedef Kokkos::View t_ace_2d_lr; typedef Kokkos::View t_ace_2d3; typedef Kokkos::View t_ace_3d; typedef Kokkos::View tc_ace_3d; typedef Kokkos::View t_ace_3d3; typedef Kokkos::View t_ace_3d4; + typedef Kokkos::View t_ace_3d4_lr; typedef Kokkos::View t_ace_4d; typedef Kokkos::View t_ace_1c; typedef Kokkos::View t_ace_2c; @@ -260,25 +250,16 @@ class PairPACEExtrapolationKokkos : public PairPACEExtrapolation { th_ace_1d h_gamma; // Spherical Harmonics + void pre_compute_harmonics(int); - KOKKOS_INLINE_FUNCTION - void compute_barplm(double rz, int lmaxi); - - KOKKOS_INLINE_FUNCTION - void compute_ylm(double rx, double ry, double rz, int lmaxi); - + t_ace_4c A_sph; + t_ace_1d d_idx_sph; t_ace_1d alm; t_ace_1d blm; t_ace_1d cl; t_ace_1d dl; - t_ace_3d plm; - t_ace_3d dplm; - - t_ace_3c ylm; - t_ace_4c3 dylm; - // short neigh list t_ace_1i d_ncount; t_ace_2d d_mu; @@ -297,20 +278,19 @@ class PairPACEExtrapolationKokkos : public PairPACEExtrapolation { t_ace_1d d_rho_core_cutoff; t_ace_1d d_drho_core_cutoff; t_ace_1d d_E0vals; - t_ace_2d d_wpre; - t_ace_2d d_mexp; + t_ace_2d_lr d_wpre; + t_ace_2d_lr d_mexp; // tilde t_ace_1i d_idx_ms_combs_count; t_ace_1i d_total_basis_size; - t_ace_2i d_rank; - t_ace_2i d_num_ms_combs; - t_ace_2i d_func_inds; - t_ace_3i d_mus; - t_ace_3i d_ns; - t_ace_3i d_ls; - t_ace_3i d_ms_combs; -// t_ace_3d d_ctildes; + t_ace_2i_lr d_rank; + t_ace_2i_lr d_num_ms_combs; + t_ace_2i_lr d_idx_funcs; + t_ace_3i_lr d_mus; + t_ace_3i_lr d_ns; + t_ace_3i_lr d_ls; + t_ace_3i_lr d_ms_combs; t_ace_2d d_gen_cgs; t_ace_3d d_coeffs; @@ -321,12 +301,12 @@ class PairPACEExtrapolationKokkos : public PairPACEExtrapolation { int ntot, nlut, num_of_functions; double cutoff, deltaSplineBins, invrscalelookup, rscalelookup; - t_ace_3d4 lookupTable; + t_ace_3d4_lr lookupTable; void operator=(const SplineInterpolator &spline); void deallocate() { - lookupTable = t_ace_3d4(); + lookupTable = t_ace_3d4_lr(); } double memory_usage() { diff --git a/src/KOKKOS/pair_pace_kokkos.cpp b/src/KOKKOS/pair_pace_kokkos.cpp index 85fd458298..35b7b5ed82 100644 --- a/src/KOKKOS/pair_pace_kokkos.cpp +++ b/src/KOKKOS/pair_pace_kokkos.cpp @@ -29,11 +29,13 @@ #include "neighbor_kokkos.h" #include "neigh_request.h" +#include "ace-evaluator/ace_version.h" +#include "ace-evaluator/ace_radial.h" + #include "ace-evaluator/ace_c_basis.h" #include "ace-evaluator/ace_evaluator.h" #include "ace-evaluator/ace_recursive.h" -#include "ace-evaluator/ace_version.h" -#include "ace-evaluator/ace_radial.h" + #include namespace LAMMPS_NS { @@ -108,9 +110,9 @@ void PairPACEKokkos::grow(int natom, int maxneigh) MemKK::realloc_kokkos(A, "pace:A", natom, nelements, (lmax + 1) * (lmax + 1), nradmax + 1); MemKK::realloc_kokkos(A_rank1, "pace:A_rank1", natom, nelements, nradbase); - MemKK::realloc_kokkos(A_list, "pace:A_list", natom, idx_rho_max, basis_set->rankmax); + MemKK::realloc_kokkos(A_list, "pace:A_list", natom, idx_ms_combs_max, basis_set->rankmax); //size is +1 of max to avoid out-of-boundary array access in double-triangular scheme - MemKK::realloc_kokkos(A_forward_prod, "pace:A_forward_prod", natom, idx_rho_max, basis_set->rankmax + 1); + MemKK::realloc_kokkos(A_forward_prod, "pace:A_forward_prod", natom, idx_ms_combs_max, basis_set->rankmax + 1); MemKK::realloc_kokkos(e_atom, "pace:e_atom", natom); MemKK::realloc_kokkos(rhos, "pace:rhos", natom, basis_set->ndensitymax + 1); // +1 density for core repulsion @@ -127,7 +129,7 @@ void PairPACEKokkos::grow(int natom, int maxneigh) MemKK::realloc_kokkos(d_jj_min, "pace:j_min_pair", natom); MemKK::realloc_kokkos(d_corerep, "pace:corerep", natom); // per-atom corerep - MemKK::realloc_kokkos(dB_flatten, "pace:dB_flatten", natom, idx_rho_max, basis_set->rankmax); + MemKK::realloc_kokkos(dB_flatten, "pace:dB_flatten", natom, idx_ms_combs_max, basis_set->rankmax); } if (((int)fr.extent(0) < natom) || ((int)fr.extent(1) < maxneigh)) { @@ -179,7 +181,7 @@ void PairPACEKokkos::copy_pertype() h_rho_core_cutoff[n] = basis_set->map_embedding_specifications.at(n).rho_core_cutoff; h_drho_core_cutoff[n] = basis_set->map_embedding_specifications.at(n).drho_core_cutoff; - h_E0vals(n)= basis_set->E0vals(n); + h_E0vals(n) = basis_set->E0vals(n); h_ndensity(n) = basis_set->map_embedding_specifications.at(n).ndensity; @@ -220,10 +222,10 @@ void PairPACEKokkos::copy_pertype() auto h_dcut_in = Kokkos::create_mirror_view(d_dcut_in); for (int mu_i = 0; mu_i < nelements; ++mu_i) { - for (int mu_j = 0; mu_j < nelements; ++mu_j) { - h_cut_in(mu_i,mu_j) = basis_set->map_bond_specifications.at({mu_i,mu_j}).rcut_in; - h_dcut_in(mu_i,mu_j) = basis_set->map_bond_specifications.at({mu_i,mu_j}).dcut_in; - } + for (int mu_j = 0; mu_j < nelements; ++mu_j) { + h_cut_in(mu_i,mu_j) = basis_set->map_bond_specifications.at({mu_i,mu_j}).rcut_in; + h_dcut_in(mu_i,mu_j) = basis_set->map_bond_specifications.at({mu_i,mu_j}).dcut_in; + } } Kokkos::deep_copy(d_cut_in, h_cut_in); Kokkos::deep_copy(d_dcut_in, h_dcut_in); @@ -283,50 +285,50 @@ void PairPACEKokkos::copy_tilde() // flatten loops, get per-element count and max - idx_rho_max = 0; + idx_ms_combs_max = 0; int total_basis_size_max = 0; - MemKK::realloc_kokkos(d_idx_rho_count, "pace:idx_rho_count", nelements); - auto h_idx_rho_count = Kokkos::create_mirror_view(d_idx_rho_count); + MemKK::realloc_kokkos(d_idx_ms_combs_count, "pace:idx_ms_combs_count", nelements); + auto h_idx_ms_combs_count = Kokkos::create_mirror_view(d_idx_ms_combs_count); - for (int n = 0; n < nelements; n++) { - int idx_rho = 0; - const int total_basis_size_rank1 = basis_set->total_basis_size_rank1[n]; - const int total_basis_size = basis_set->total_basis_size[n]; + for (int mu = 0; mu < nelements; mu++) { + int idx_ms_combs = 0; + const int total_basis_size_rank1 = basis_set->total_basis_size_rank1[mu]; + const int total_basis_size = basis_set->total_basis_size[mu]; - ACECTildeBasisFunction *basis = basis_set->basis[n]; + ACECTildeBasisFunction *basis = basis_set->basis[mu]; // rank=1 for (int func_rank1_ind = 0; func_rank1_ind < total_basis_size_rank1; ++func_rank1_ind) - idx_rho++; + idx_ms_combs++; // rank > 1 - for (int func_ind = 0; func_ind < total_basis_size; ++func_ind) { - ACECTildeBasisFunction *func = &basis[func_ind]; + for (int idx_func = 0; idx_func < total_basis_size; ++idx_func) { + ACECTildeBasisFunction *func = &basis[idx_func]; // loop over {ms} combinations in sum for (int ms_ind = 0; ms_ind < func->num_ms_combs; ++ms_ind) - idx_rho++; + idx_ms_combs++; } - h_idx_rho_count(n) = idx_rho; - idx_rho_max = MAX(idx_rho_max, idx_rho); + h_idx_ms_combs_count(mu) = idx_ms_combs; + idx_ms_combs_max = MAX(idx_ms_combs_max, idx_ms_combs); total_basis_size_max = MAX(total_basis_size_max, total_basis_size_rank1 + total_basis_size); } - Kokkos::deep_copy(d_idx_rho_count, h_idx_rho_count); + Kokkos::deep_copy(d_idx_ms_combs_count, h_idx_ms_combs_count); MemKK::realloc_kokkos(d_rank, "pace:rank", nelements, total_basis_size_max); MemKK::realloc_kokkos(d_num_ms_combs, "pace:num_ms_combs", nelements, total_basis_size_max); - MemKK::realloc_kokkos(d_offsets, "pace:offsets", nelements, idx_rho_max); + MemKK::realloc_kokkos(d_idx_funcs, "pace:idx_func", nelements, idx_ms_combs_max); MemKK::realloc_kokkos(d_mus, "pace:mus", nelements, total_basis_size_max, basis_set->rankmax); MemKK::realloc_kokkos(d_ns, "pace:ns", nelements, total_basis_size_max, basis_set->rankmax); MemKK::realloc_kokkos(d_ls, "pace:ls", nelements, total_basis_size_max, basis_set->rankmax); - MemKK::realloc_kokkos(d_ms_combs, "pace:ms_combs", nelements, idx_rho_max, basis_set->rankmax); - MemKK::realloc_kokkos(d_ctildes, "pace:ctildes", nelements, idx_rho_max, basis_set->ndensitymax); + MemKK::realloc_kokkos(d_ms_combs, "pace:ms_combs", nelements, idx_ms_combs_max, basis_set->rankmax); + MemKK::realloc_kokkos(d_ctildes, "pace:ctildes", nelements, idx_ms_combs_max, basis_set->ndensitymax); auto h_rank = Kokkos::create_mirror_view(d_rank); auto h_num_ms_combs = Kokkos::create_mirror_view(d_num_ms_combs); - auto h_offsets = Kokkos::create_mirror_view(d_offsets); + auto h_idx_funcs = Kokkos::create_mirror_view(d_idx_funcs); auto h_mus = Kokkos::create_mirror_view(d_mus); auto h_ns = Kokkos::create_mirror_view(d_ns); auto h_ls = Kokkos::create_mirror_view(d_ls); @@ -335,63 +337,66 @@ void PairPACEKokkos::copy_tilde() // copy values on host - for (int n = 0; n < nelements; n++) { - const int total_basis_size_rank1 = basis_set->total_basis_size_rank1[n]; - const int total_basis_size = basis_set->total_basis_size[n]; + for (int mu = 0; mu < nelements; mu++) { + const int total_basis_size_rank1 = basis_set->total_basis_size_rank1[mu]; + const int total_basis_size = basis_set->total_basis_size[mu]; - ACECTildeBasisFunction *basis_rank1 = basis_set->basis_rank1[n]; - ACECTildeBasisFunction *basis = basis_set->basis[n]; + ACECTildeBasisFunction *basis_rank1 = basis_set->basis_rank1[mu]; + ACECTildeBasisFunction *basis = basis_set->basis[mu]; - const int ndensity = basis_set->map_embedding_specifications.at(n).ndensity; + const int ndensity = basis_set->map_embedding_specifications.at(mu).ndensity; - int idx_rho = 0; + int idx_ms_combs = 0; // rank=1 - for (int offset = 0; offset < total_basis_size_rank1; ++offset) { - ACECTildeBasisFunction *func = &basis_rank1[offset]; - h_rank(n, offset) = 1; - h_mus(n, offset, 0) = func->mus[0]; - h_ns(n, offset, 0) = func->ns[0]; - for (int p = 0; p < ndensity; p++) - h_ctildes(n, idx_rho, p) = func->ctildes[p]; - h_offsets(n, idx_rho) = offset; - idx_rho++; + for (int idx_func = 0; idx_func < total_basis_size_rank1; ++idx_func) { + ACECTildeBasisFunction *func = &basis_rank1[idx_func]; + h_rank(mu, idx_func) = 1; + h_mus(mu, idx_func, 0) = func->mus[0]; + h_ns(mu, idx_func, 0) = func->ns[0]; + + for (int p = 0; p < ndensity; ++p) + h_ctildes(mu, idx_ms_combs, p) = func->ctildes[p]; + + h_idx_funcs(mu, idx_ms_combs) = idx_func; + idx_ms_combs++; } // rank > 1 - for (int func_ind = 0; func_ind < total_basis_size; ++func_ind) { - ACECTildeBasisFunction *func = &basis[func_ind]; + for (int idx_func = 0; idx_func < total_basis_size; ++idx_func) { + ACECTildeBasisFunction *func = &basis[idx_func]; // TODO: check if func->ctildes are zero, then skip - const int offset = total_basis_size_rank1 + func_ind; + const int idx_func_through = total_basis_size_rank1 + idx_func; - const int rank = h_rank(n, offset) = func->rank; - h_num_ms_combs(n, offset) = func->num_ms_combs; + const int rank = h_rank(mu, idx_func_through) = func->rank; + h_num_ms_combs(mu, idx_func_through) = func->num_ms_combs; for (int t = 0; t < rank; t++) { - h_mus(n, offset, t) = func->mus[t]; - h_ns(n, offset, t) = func->ns[t]; - h_ls(n, offset, t) = func->ls[t]; + h_mus(mu, idx_func_through, t) = func->mus[t]; + h_ns(mu, idx_func_through, t) = func->ns[t]; + h_ls(mu, idx_func_through, t) = func->ls[t]; } // loop over {ms} combinations in sum for (int ms_ind = 0; ms_ind < func->num_ms_combs; ++ms_ind) { auto ms = &func->ms_combs[ms_ind * rank]; // current ms-combination (of length = rank) for (int t = 0; t < rank; t++) - h_ms_combs(n, idx_rho, t) = ms[t]; + h_ms_combs(mu, idx_ms_combs, t) = ms[t]; for (int p = 0; p < ndensity; ++p) { // real-part only multiplication - h_ctildes(n, idx_rho, p) = func->ctildes[ms_ind * ndensity + p]; + h_ctildes(mu, idx_ms_combs, p) = func->ctildes[ms_ind * ndensity + p]; } - h_offsets(n, idx_rho) = offset; - idx_rho++; + + h_idx_funcs(mu, idx_ms_combs) = idx_func_through; + idx_ms_combs++; } } } Kokkos::deep_copy(d_rank, h_rank); Kokkos::deep_copy(d_num_ms_combs, h_num_ms_combs); - Kokkos::deep_copy(d_offsets, h_offsets); + Kokkos::deep_copy(d_idx_funcs, h_idx_funcs); Kokkos::deep_copy(d_mus, h_mus); Kokkos::deep_copy(d_ns, h_ns); Kokkos::deep_copy(d_ls, h_ls); @@ -659,7 +664,7 @@ void PairPACEKokkos::compute(int eflag_in, int vflag_in) //ComputeRho { - typename Kokkos::RangePolicy policy_rho(0,chunk_size*idx_rho_max); + typename Kokkos::RangePolicy policy_rho(0,chunk_size*idx_ms_combs_max); Kokkos::parallel_for("ComputeRho",policy_rho,*this); } @@ -671,7 +676,7 @@ void PairPACEKokkos::compute(int eflag_in, int vflag_in) //ComputeWeights { - typename Kokkos::RangePolicy policy_weights(0,chunk_size*idx_rho_max); + typename Kokkos::RangePolicy policy_weights(0,chunk_size * idx_ms_combs_max); Kokkos::parallel_for("ComputeWeights",policy_weights,*this); } @@ -713,7 +718,6 @@ void PairPACEKokkos::compute(int eflag_in, int vflag_in) } chunk_offset += chunk_size; - } // end while if (need_dup) @@ -829,15 +833,15 @@ void PairPACEKokkos::operator() (TagPairPACEComputeNeigh,const typen }); if (is_zbl) { - //adapted from https://www.osti.gov/servlets/purl/1429450 - if(ncount>0) { - using minloc_value_type=Kokkos::MinLoc::value_type; - minloc_value_type djjmin; - djjmin.val=1e20; - djjmin.loc=-1; - Kokkos::MinLoc reducer_scalar(djjmin); - // loop over ncount (actual neighbours withing cutoff) rather than jnum (total number of neigh in cutoff+skin) - Kokkos::parallel_reduce(Kokkos::TeamThreadRange(team, ncount), + //adapted from https://www.osti.gov/servlets/purl/1429450 + if (ncount > 0) { + using minloc_value_type=Kokkos::MinLoc::value_type; + minloc_value_type djjmin; + djjmin.val=1e20; + djjmin.loc=-1; + Kokkos::MinLoc reducer_scalar(djjmin); + // loop over ncount (actual neighbours withing cutoff) rather than jnum (total number of neigh in cutoff+skin) + Kokkos::parallel_reduce(Kokkos::TeamThreadRange(team, ncount), [&](const int offset, minloc_value_type &min_d_dist) { int j = d_nearest(ii,offset); j &= NEIGHMASK; @@ -846,8 +850,8 @@ void PairPACEKokkos::operator() (TagPairPACEComputeNeigh,const typen const int mu_j = d_map(type(j)); const F_FLOAT d = r - (d_cut_in(mu_i, mu_j) - d_dcut_in(mu_i, mu_j)); if (d < min_d_dist.val) { - min_d_dist.val = d; - min_d_dist.loc = offset; + min_d_dist.val = d; + min_d_dist.loc = offset; } }, reducer_scalar); d_d_min(ii) = djjmin.val; @@ -1081,70 +1085,69 @@ template KOKKOS_INLINE_FUNCTION void PairPACEKokkos::operator() (TagPairPACEComputeRho, const int& iter) const { - const int idx_rho = iter / chunk_size; + const int idx_ms_combs = iter / chunk_size; const int ii = iter % chunk_size; const int i = d_ilist[ii + chunk_offset]; const int mu_i = d_map(type(i)); - if (idx_rho >= d_idx_rho_count(mu_i)) return; + if (idx_ms_combs >= d_idx_ms_combs_count(mu_i)) return; const int ndensity = d_ndensity(mu_i); - const int offset = d_offsets(mu_i, idx_rho); - const int rank = d_rank(mu_i, offset); + const int idx_func = d_idx_funcs(mu_i, idx_ms_combs); + const int rank = d_rank(mu_i, idx_func); const int r = rank - 1; // Basis functions B with iterative product and density rho(p) calculation if (rank == 1) { - const int mu = d_mus(mu_i, offset, 0); - const int n = d_ns(mu_i, offset, 0); + const int mu = d_mus(mu_i, idx_func, 0); + const int n = d_ns(mu_i, idx_func, 0); double A_cur = A_rank1(ii, mu, n - 1); for (int p = 0; p < ndensity; ++p) { //for rank=1 (r=0) only 1 ms-combination exists (ms_ind=0), so index of func.ctildes is 0..ndensity-1 - Kokkos::atomic_add(&rhos(ii, p), d_ctildes(mu_i, idx_rho, p) * A_cur); + Kokkos::atomic_add(&rhos(ii, p), d_ctildes(mu_i, idx_ms_combs, p) * A_cur); } } else { // rank > 1 // loop over {ms} combinations in sum // loop over m, collect B = product of A with given ms - A_forward_prod(ii, idx_rho, 0) = complex::one(); + A_forward_prod(ii, idx_ms_combs, 0) = complex::one(); // fill forward A-product triangle for (int t = 0; t < rank; t++) { //TODO: optimize ns[t]-1 -> ns[t] during functions construction - const int mu = d_mus(mu_i, offset, t); - const int n = d_ns(mu_i, offset, t); - const int l = d_ls(mu_i, offset, t); - const int m = d_ms_combs(mu_i, idx_rho, t); // current ms-combination (of length = rank) + const int mu = d_mus(mu_i, idx_func, t); + const int n = d_ns(mu_i, idx_func, t); + const int l = d_ls(mu_i, idx_func, t); + const int m = d_ms_combs(mu_i, idx_ms_combs, t); // current ms-combination (of length = rank) const int idx = l * (l + 1) + m; // (l, m) - A_list(ii, idx_rho, t) = A(ii, mu, idx, n - 1); - A_forward_prod(ii, idx_rho, t + 1) = A_forward_prod(ii, idx_rho, t) * A_list(ii, idx_rho, t); + A_list(ii, idx_ms_combs, t) = A(ii, mu, idx, n - 1); + A_forward_prod(ii, idx_ms_combs, t + 1) = A_forward_prod(ii, idx_ms_combs, t) * A_list(ii, idx_ms_combs, t); } complex A_backward_prod = complex::one(); // fill backward A-product triangle for (int t = r; t >= 1; t--) { - const complex dB = A_forward_prod(ii, idx_rho, t) * A_backward_prod; // dB - product of all A's except t-th - dB_flatten(ii, idx_rho, t) = dB; + const complex dB = A_forward_prod(ii, idx_ms_combs, t) * A_backward_prod; // dB - product of all A's except t-th + dB_flatten(ii, idx_ms_combs, t) = dB; - A_backward_prod = A_backward_prod * A_list(ii, idx_rho, t); + A_backward_prod = A_backward_prod * A_list(ii, idx_ms_combs, t); } - dB_flatten(ii, idx_rho, 0) = A_forward_prod(ii, idx_rho, 0) * A_backward_prod; + dB_flatten(ii, idx_ms_combs, 0) = A_forward_prod(ii, idx_ms_combs, 0) * A_backward_prod; - const complex B = A_forward_prod(ii, idx_rho, rank); + const complex B = A_forward_prod(ii, idx_ms_combs, rank); for (int p = 0; p < ndensity; ++p) { // real-part only multiplication - Kokkos::atomic_add(&rhos(ii, p), B.real_part_product(d_ctildes(mu_i, idx_rho, p))); + Kokkos::atomic_add(&rhos(ii, p), B.real_part_product(d_ctildes(mu_i, idx_ms_combs, p))); } } } /* ---------------------------------------------------------------------- */ - template KOKKOS_INLINE_FUNCTION void PairPACEKokkos::operator() (TagPairPACEComputeFS, const int& ii) const @@ -1161,34 +1164,35 @@ void PairPACEKokkos::operator() (TagPairPACEComputeFS, const int& ii evdwl = fcut = dfcut = 0.0; FS_values_and_derivatives(ii, evdwl, mu_i); + if (is_zbl) { - if (d_jj_min(ii) != -1) { - const int mu_jmin = d_mu(ii,d_jj_min(ii)); - F_FLOAT dcutin = d_dcut_in(mu_i, mu_jmin); - F_FLOAT transition_coordinate = dcutin - d_d_min(ii); // == cutin - r_min - cutoff_func_poly(transition_coordinate, dcutin, dcutin, fcut, dfcut); - dfcut = -dfcut; // invert, because rho_core = cutin - r_min - } else { - // no neighbours - fcut = 1; - dfcut = 0; - } - evdwl_cut = evdwl * fcut + rho_core(ii) * (1 - fcut); // evdwl * fcut + rho_core_uncut - rho_core_uncut* fcut - dF_drho_core(ii) = 1 - fcut; - dF_dfcut(ii) = evdwl * dfcut - rho_core(ii) * dfcut; + if (d_jj_min(ii) != -1) { + const int mu_jmin = d_mu(ii,d_jj_min(ii)); + F_FLOAT dcutin = d_dcut_in(mu_i, mu_jmin); + F_FLOAT transition_coordinate = dcutin - d_d_min(ii); // == cutin - r_min + cutoff_func_poly(transition_coordinate, dcutin, dcutin, fcut, dfcut); + dfcut = -dfcut; // invert, because rho_core = cutin - r_min + } else { + // no neighbours + fcut = 1; + dfcut = 0; + } + evdwl_cut = evdwl * fcut + rho_core(ii) * (1 - fcut); // evdwl * fcut + rho_core_uncut - rho_core_uncut* fcut + dF_drho_core(ii) = 1 - fcut; + dF_dfcut(ii) = evdwl * dfcut - rho_core(ii) * dfcut; } else { - inner_cutoff(rho_core(ii), rho_cut, drho_cut, fcut, dfcut); - dF_drho_core(ii) = evdwl * dfcut + 1; - evdwl_cut = evdwl * fcut + rho_core(ii); + inner_cutoff(rho_core(ii), rho_cut, drho_cut, fcut, dfcut); + dF_drho_core(ii) = evdwl * dfcut + 1; + evdwl_cut = evdwl * fcut + rho_core(ii); } for (int p = 0; p < ndensity; ++p) - dF_drho(ii, p) *= fcut; + dF_drho(ii, p) *= fcut; // tally energy contribution if (eflag) { - // E0 shift - evdwl_cut += d_E0vals(mu_i); - e_atom(ii) = evdwl_cut; + // E0 shift + evdwl_cut += d_E0vals(mu_i); + e_atom(ii) = evdwl_cut; } if (flag_corerep_factor) @@ -1201,43 +1205,43 @@ template KOKKOS_INLINE_FUNCTION void PairPACEKokkos::operator() (TagPairPACEComputeWeights, const int& iter) const { - const int idx_rho = iter / chunk_size; + const int idx_ms_combs = iter / chunk_size; const int ii = iter % chunk_size; const int i = d_ilist[ii + chunk_offset]; const int mu_i = d_map(type(i)); - if (idx_rho >= d_idx_rho_count(mu_i)) return; + if (idx_ms_combs >= d_idx_ms_combs_count(mu_i)) return; const int ndensity = d_ndensity(mu_i); - const int offset = d_offsets(mu_i, idx_rho); - const int rank = d_rank(mu_i, offset); + const int idx_func = d_idx_funcs(mu_i, idx_ms_combs); + const int rank = d_rank(mu_i, idx_func); // Weights and theta calculation if (rank == 1) { - const int mu = d_mus(mu_i, offset, 0); - const int n = d_ns(mu_i, offset, 0); + const int mu = d_mus(mu_i, idx_func, 0); + const int n = d_ns(mu_i, idx_func, 0); double theta = 0.0; for (int p = 0; p < ndensity; ++p) { // for rank=1 (r=0) only 1 ms-combination exists (ms_ind=0), so index of func.ctildes is 0..ndensity-1 - theta += dF_drho(ii, p) * d_ctildes(mu_i, idx_rho, p); + theta += dF_drho(ii, p) * d_ctildes(mu_i, idx_ms_combs, p); } Kokkos::atomic_add(&weights_rank1(ii, mu, n - 1), theta); } else { // rank > 1 double theta = 0.0; for (int p = 0; p < ndensity; ++p) - theta += dF_drho(ii, p) * d_ctildes(mu_i, idx_rho, p); + theta += dF_drho(ii, p) * d_ctildes(mu_i, idx_ms_combs, p); theta *= 0.5; // 0.5 factor due to possible double counting ??? for (int t = 0; t < rank; ++t) { - const int m_t = d_ms_combs(mu_i, idx_rho, t); + const int m_t = d_ms_combs(mu_i, idx_ms_combs, t); const int factor = (m_t % 2 == 0 ? 1 : -1); - const complex dB = dB_flatten(ii, idx_rho, t); - const int mu_t = d_mus(mu_i, offset, t); - const int n_t = d_ns(mu_i, offset, t); - const int l_t = d_ls(mu_i, offset, t); + const complex dB = dB_flatten(ii, idx_ms_combs, t); + const int mu_t = d_mus(mu_i, idx_func, t); + const int n_t = d_ns(mu_i, idx_func, t); + const int l_t = d_ls(mu_i, idx_func, t); const int idx = l_t * (l_t + 1) + m_t; // (l, m) const int idx_sph = d_idx_sph(idx); if (idx_sph >= 0) { @@ -1543,10 +1547,10 @@ void PairPACEKokkos::operator() (TagPairPACEComputeDerivative, const if (is_zbl) { if (jj==d_jj_min(ii)) { - // DCRU = 1.0 - f_ij(ii, jj, 0) += dF_dfcut(ii) * r_hat[0]; - f_ij(ii, jj, 1) += dF_dfcut(ii) * r_hat[1]; - f_ij(ii, jj, 2) += dF_dfcut(ii) * r_hat[2]; + // DCRU = 1.0 + f_ij(ii, jj, 0) += dF_dfcut(ii) * r_hat[0]; + f_ij(ii, jj, 1) += dF_dfcut(ii) * r_hat[1]; + f_ij(ii, jj, 2) += dF_dfcut(ii) * r_hat[2]; } } } @@ -1990,10 +1994,10 @@ double PairPACEKokkos::memory_usage() bytes += MemKK::memory_usage(d_npoti); bytes += MemKK::memory_usage(d_wpre); bytes += MemKK::memory_usage(d_mexp); - bytes += MemKK::memory_usage(d_idx_rho_count); + bytes += MemKK::memory_usage(d_idx_ms_combs_count); bytes += MemKK::memory_usage(d_rank); bytes += MemKK::memory_usage(d_num_ms_combs); - bytes += MemKK::memory_usage(d_offsets); + bytes += MemKK::memory_usage(d_idx_funcs); bytes += MemKK::memory_usage(d_mus); bytes += MemKK::memory_usage(d_ns); bytes += MemKK::memory_usage(d_ls); diff --git a/src/KOKKOS/pair_pace_kokkos.h b/src/KOKKOS/pair_pace_kokkos.h index bb8c5a1f1a..e22c61f0ea 100644 --- a/src/KOKKOS/pair_pace_kokkos.h +++ b/src/KOKKOS/pair_pace_kokkos.h @@ -92,7 +92,7 @@ class PairPACEKokkos : public PairPACE { void operator() (TagPairPACEComputeForce,const int& ii, EV_FLOAT&) const; protected: - int inum, maxneigh, chunk_size, chunk_offset, idx_rho_max, idx_sph_max; + int inum, maxneigh, chunk_size, chunk_offset, idx_ms_combs_max, idx_sph_max; int host_flag; int eflag, vflag; @@ -271,10 +271,10 @@ class PairPACEKokkos : public PairPACE { t_ace_2d_lr d_mexp; // tilde - t_ace_1i d_idx_rho_count; + t_ace_1i d_idx_ms_combs_count; t_ace_2i_lr d_rank; t_ace_2i_lr d_num_ms_combs; - t_ace_2i_lr d_offsets; + t_ace_2i_lr d_idx_funcs; t_ace_3i_lr d_mus; t_ace_3i_lr d_ns; t_ace_3i_lr d_ls; From 66a5f566828d16eb31f0e9faafbcf4b4a27d4494 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Wed, 3 Jan 2024 14:13:11 -0700 Subject: [PATCH 161/189] whitespace --- src/KOKKOS/pair_pace_extrapolation_kokkos.cpp | 4 ++-- src/KOKKOS/pair_pace_kokkos.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp b/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp index 0597885860..18ecaf6e69 100644 --- a/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp +++ b/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp @@ -985,7 +985,7 @@ void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeAi, phase.im = ry; double plm_idx,plm_idx1,plm_idx2; - + plm_idx = plm_idx1 = plm_idx2 = 0.0; int idx_sph = 0; @@ -1415,7 +1415,7 @@ void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeDeri double plm_idx,plm_idx1,plm_idx2; double dplm_idx,dplm_idx1,dplm_idx2; - + plm_idx = plm_idx1 = plm_idx2 = 0.0; dplm_idx = dplm_idx1 = dplm_idx2 = 0.0; diff --git a/src/KOKKOS/pair_pace_kokkos.cpp b/src/KOKKOS/pair_pace_kokkos.cpp index 35b7b5ed82..aaed01510a 100644 --- a/src/KOKKOS/pair_pace_kokkos.cpp +++ b/src/KOKKOS/pair_pace_kokkos.cpp @@ -930,7 +930,7 @@ void PairPACEKokkos::operator() (TagPairPACEComputeAi, const typenam phase.im = ry; double plm_idx,plm_idx1,plm_idx2; - + plm_idx = plm_idx1 = plm_idx2 = 0.0; int idx_sph = 0; @@ -1324,7 +1324,7 @@ void PairPACEKokkos::operator() (TagPairPACEComputeDerivative, const double plm_idx,plm_idx1,plm_idx2; double dplm_idx,dplm_idx1,dplm_idx2; - + plm_idx = plm_idx1 = plm_idx2 = 0.0; dplm_idx = dplm_idx1 = dplm_idx2 = 0.0; From 4fbb913425269ac31906e6571823a4e9fd63bd11 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 22 Dec 2023 11:35:35 -0500 Subject: [PATCH 162/189] skip python tests using numpy that fail randomly on macOS --- unittest/python/CMakeLists.txt | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/unittest/python/CMakeLists.txt b/unittest/python/CMakeLists.txt index b4ba281d93..f3b851620c 100644 --- a/unittest/python/CMakeLists.txt +++ b/unittest/python/CMakeLists.txt @@ -84,20 +84,26 @@ if(Python_EXECUTABLE) WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) set_tests_properties(PythonCommands PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") - add_test(NAME PythonNumpy - COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-numpy.py -v - WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) - set_tests_properties(PythonNumpy PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + # randomly failing on macOS with python 3.12 + if(NOT APPLE) + add_test(NAME PythonNumpy + COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-numpy.py -v + WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) + set_tests_properties(PythonNumpy PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + endif() add_test(NAME PythonCapabilities COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-capabilities.py -v WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) set_tests_properties(PythonCapabilities PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") - add_test(NAME PythonPyLammps - COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-pylammps.py -v - WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) - set_tests_properties(PythonPyLammps PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + # randomly failing on macOS with python 3.12 + if(NOT APPLE) + add_test(NAME PythonPyLammps + COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-pylammps.py -v + WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) + set_tests_properties(PythonPyLammps PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + endif() add_test(NAME PythonFormats COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-formats.py -v From e00fc992fcbf899cefb3c409a561ed13e03b3346 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 22 Dec 2023 11:35:35 -0500 Subject: [PATCH 163/189] skip python tests using numpy that fail randomly on macOS --- unittest/python/CMakeLists.txt | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/unittest/python/CMakeLists.txt b/unittest/python/CMakeLists.txt index b4ba281d93..f3b851620c 100644 --- a/unittest/python/CMakeLists.txt +++ b/unittest/python/CMakeLists.txt @@ -84,20 +84,26 @@ if(Python_EXECUTABLE) WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) set_tests_properties(PythonCommands PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") - add_test(NAME PythonNumpy - COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-numpy.py -v - WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) - set_tests_properties(PythonNumpy PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + # randomly failing on macOS with python 3.12 + if(NOT APPLE) + add_test(NAME PythonNumpy + COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-numpy.py -v + WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) + set_tests_properties(PythonNumpy PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + endif() add_test(NAME PythonCapabilities COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-capabilities.py -v WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) set_tests_properties(PythonCapabilities PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") - add_test(NAME PythonPyLammps - COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-pylammps.py -v - WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) - set_tests_properties(PythonPyLammps PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + # randomly failing on macOS with python 3.12 + if(NOT APPLE) + add_test(NAME PythonPyLammps + COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-pylammps.py -v + WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) + set_tests_properties(PythonPyLammps PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + endif() add_test(NAME PythonFormats COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-formats.py -v From 55784019f76c27ce5c3703d379439e123588530b Mon Sep 17 00:00:00 2001 From: Stan Gerald Moore Date: Wed, 3 Jan 2024 16:46:06 -0700 Subject: [PATCH 164/189] Fix CPU issue --- src/KOKKOS/pair_kokkos.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/KOKKOS/pair_kokkos.h b/src/KOKKOS/pair_kokkos.h index eea2cd5316..32f50c3bb5 100644 --- a/src/KOKKOS/pair_kokkos.h +++ b/src/KOKKOS/pair_kokkos.h @@ -84,8 +84,6 @@ struct PairComputeFunctor { // typename KKDevice::value,Kokkos::MemoryTraits::value> > vatom; KKScatterView dup_vatom; - - NeighListKokkos list; PairComputeFunctor(PairStyle* c_ptr, @@ -109,13 +107,15 @@ struct PairComputeFunctor { } void contribute() { - Kokkos::Experimental::contribute(c.f, dup_f); + if constexpr (std::is_same_v,Kokkos::Experimental::ScatterDuplicated>) { + Kokkos::Experimental::contribute(c.f, dup_f); - if (c.eflag_atom) - Kokkos::Experimental::contribute(c.d_eatom, dup_eatom); + if (c.eflag_atom) + Kokkos::Experimental::contribute(c.d_eatom, dup_eatom); - if (c.vflag_atom) - Kokkos::Experimental::contribute(c.d_vatom, dup_vatom); + if (c.vflag_atom) + Kokkos::Experimental::contribute(c.d_vatom, dup_vatom); + } } // Loop over neighbors of one atom without coulomb interaction @@ -988,11 +988,13 @@ EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t<(NEIGHFLAG&P Kokkos::TeamPolicy > policy(num_teams,atoms_per_team,vectorsize); if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(policy,ff,ev); else Kokkos::parallel_for(policy,ff); + ff.contribute(); } else { PairComputeFunctor ff(fpair,list); Kokkos::TeamPolicy > policy(num_teams,atoms_per_team,vectorsize); if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(policy,ff,ev); else Kokkos::parallel_for(policy,ff); + ff.contribute(); } } else { if (fpair->atom->ntypes > MAX_TYPES_STACKPARAMS) { From 9d7582ec1bb86d0b41f9ae6f1de9e62d81e9db01 Mon Sep 17 00:00:00 2001 From: Stan Gerald Moore Date: Wed, 3 Jan 2024 16:59:11 -0700 Subject: [PATCH 165/189] Small tweak for readability --- src/KOKKOS/pair_kokkos.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/KOKKOS/pair_kokkos.h b/src/KOKKOS/pair_kokkos.h index 32f50c3bb5..9521268284 100644 --- a/src/KOKKOS/pair_kokkos.h +++ b/src/KOKKOS/pair_kokkos.h @@ -107,7 +107,9 @@ struct PairComputeFunctor { } void contribute() { - if constexpr (std::is_same_v,Kokkos::Experimental::ScatterDuplicated>) { + int need_dup = std::is_same_v; + + if (need_dup) { Kokkos::Experimental::contribute(c.f, dup_f); if (c.eflag_atom) From e26a762f880964497f500434229163d5e82e674c Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 4 Jan 2024 11:21:38 -0500 Subject: [PATCH 166/189] improve compatibility of oneapi.cmake preset --- cmake/presets/oneapi.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/presets/oneapi.cmake b/cmake/presets/oneapi.cmake index 2aacf1a1f5..393d1d9b68 100644 --- a/cmake/presets/oneapi.cmake +++ b/cmake/presets/oneapi.cmake @@ -18,11 +18,11 @@ set(MPI_CXX_COMPILER "mpicxx" CACHE STRING "" FORCE) unset(HAVE_OMP_H_INCLUDE CACHE) set(OpenMP_C "icx" CACHE STRING "" FORCE) -set(OpenMP_C_FLAGS "-qopenmp -qopenmp-simd" CACHE STRING "" FORCE) +set(OpenMP_C_FLAGS "-qopenmp;-qopenmp-simd" CACHE STRING "" FORCE) set(OpenMP_C_LIB_NAMES "omp" CACHE STRING "" FORCE) set(OpenMP_CXX "icpx" CACHE STRING "" FORCE) -set(OpenMP_CXX_FLAGS "-qopenmp -qopenmp-simd" CACHE STRING "" FORCE) +set(OpenMP_CXX_FLAGS "-qopenmp;-qopenmp-simd" CACHE STRING "" FORCE) set(OpenMP_CXX_LIB_NAMES "omp" CACHE STRING "" FORCE) -set(OpenMP_Fortran_FLAGS "-qopenmp -qopenmp-simd" CACHE STRING "" FORCE) +set(OpenMP_Fortran_FLAGS "-qopenmp;-qopenmp-simd" CACHE STRING "" FORCE) set(OpenMP_omp_LIBRARY "libiomp5.so" CACHE PATH "" FORCE) From 43642a6040243331e4e34856db2d56bd6040ed0d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 4 Jan 2024 22:51:31 -0500 Subject: [PATCH 167/189] Kokkos 4 checks for compatible compilers so this is no longer needed. --- cmake/Modules/Packages/KOKKOS.cmake | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cmake/Modules/Packages/KOKKOS.cmake b/cmake/Modules/Packages/KOKKOS.cmake index 0edd9a3baa..30c46504ed 100644 --- a/cmake/Modules/Packages/KOKKOS.cmake +++ b/cmake/Modules/Packages/KOKKOS.cmake @@ -16,11 +16,6 @@ endif() if(Kokkos_ENABLE_OPENMP) if(NOT BUILD_OMP) message(FATAL_ERROR "Must enable BUILD_OMP with Kokkos_ENABLE_OPENMP") - else() - # NVHPC/(AMD)Clang does not seem to provide a detectable OpenMP version, but is far beyond version 3.1 - if((OpenMP_CXX_VERSION VERSION_LESS 3.1) AND NOT ((CMAKE_CXX_COMPILER_ID STREQUAL "NVHPC") OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang"))) - message(FATAL_ERROR "Compiler must support OpenMP 3.1 or later with Kokkos_ENABLE_OPENMP") - endif() endif() endif() ######################################################################## From 1b6dc1fe8cf1e7063f73fd4544f68f45f7075791 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez Date: Sun, 7 Jan 2024 18:38:47 +0100 Subject: [PATCH 168/189] Small correction to fix/ave/correlate/long to show the right output after a restart --- src/EXTRA-FIX/fix_ave_correlate_long.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/EXTRA-FIX/fix_ave_correlate_long.cpp b/src/EXTRA-FIX/fix_ave_correlate_long.cpp index 7fa57af343..fc1760b353 100644 --- a/src/EXTRA-FIX/fix_ave_correlate_long.cpp +++ b/src/EXTRA-FIX/fix_ave_correlate_long.cpp @@ -503,7 +503,7 @@ void FixAveCorrelateLong::end_of_step() if (overwrite) { bigint fileend = platform::ftell(fp); if ((fileend > 0) && (platform::ftruncate(fp,fileend))) - error->warning(FLERR,"Error while tuncating output: {}", utils::getsyserror()); + error->warning(FLERR,"Error while truncating output: {}", utils::getsyserror()); } } } @@ -728,7 +728,7 @@ double FixAveCorrelateLong::memory_usage() { void FixAveCorrelateLong::write_restart(FILE *fp) { if (comm->me == 0) { int nsize = 3*npair*numcorrelators*p + 2*npair*numcorrelators - + numcorrelators*p + 2*numcorrelators + 6; + + numcorrelators*p + 2*numcorrelators + 7; int n=0; double *list; memory->create(list,nsize,"correlator:list"); @@ -736,6 +736,7 @@ void FixAveCorrelateLong::write_restart(FILE *fp) { list[n++] = numcorrelators; list[n++] = p; list[n++] = m; + list[n++] = kmax; list[n++] = last_accumulated_step; for (int i=0; i < npair; i++) for (int j=0; j < numcorrelators; j++) { @@ -771,6 +772,7 @@ void FixAveCorrelateLong::restart(char *buf) int numcorrelatorsin = static_cast (list[n++]); int pin = static_cast(list[n++]); int min = static_cast(list[n++]); + kmax = static_cast(list[n++]); last_accumulated_step = static_cast(list[n++]); if ((npairin!=npair) || (numcorrelatorsin!=numcorrelators) || (pin!=(int)p) || (min!=(int)m)) From bd0c4f3979c713ff760ffec35f6e0166687ef436 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 5 Jan 2024 03:06:34 -0500 Subject: [PATCH 169/189] remove dead code and silence compiler warning --- src/KOKKOS/min_linesearch_kokkos.cpp | 24 +++++++++--------------- src/min_linesearch.cpp | 17 +++++++---------- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/src/KOKKOS/min_linesearch_kokkos.cpp b/src/KOKKOS/min_linesearch_kokkos.cpp index e8a22f9ddb..2d424957c5 100644 --- a/src/KOKKOS/min_linesearch_kokkos.cpp +++ b/src/KOKKOS/min_linesearch_kokkos.cpp @@ -59,8 +59,8 @@ MinLineSearchKokkos::MinLineSearchKokkos(LAMMPS *lmp) : MinKokkos(lmp) MinLineSearchKokkos::~MinLineSearchKokkos() { - delete [] gextra; - delete [] hextra; + delete[] gextra; + delete[] hextra; } /* ---------------------------------------------------------------------- */ @@ -171,8 +171,8 @@ int MinLineSearchKokkos::linemin_quadratic(double eoriginal, double &alpha) { double fdothall,fdothme,hme,hmaxall; double de_ideal,de; - double delfh,engprev,relerr,alphaprev,fhprev,ff,fh,alpha0; - double dot[2],dotall[2]; + double delfh,engprev,relerr,alphaprev,fhprev,fh,alpha0; + double dot,dotall; double alphamax; fix_minimize_kk->k_vectors.sync(); @@ -280,22 +280,16 @@ int MinLineSearchKokkos::linemin_quadratic(double eoriginal, double &alpha) sdot.d1 += l_fvec[i]*l_h[i]; },sdot); } - dot[0] = sdot.d0; - dot[1] = sdot.d1; + dot = sdot.d1; - MPI_Allreduce(dot,dotall,2,MPI_DOUBLE,MPI_SUM,world); + MPI_Allreduce(&dot,&dotall,1,MPI_DOUBLE,MPI_SUM,world); if (nextra_global) { for (int i = 0; i < nextra_global; i++) { - dotall[0] += fextra[i]*fextra[i]; - dotall[1] += fextra[i]*hextra[i]; + dotall += fextra[i]*hextra[i]; } } - ff = dotall[0]; - fh = dotall[1]; - if (output->thermo->normflag) { - ff /= atom->natoms; - fh /= atom->natoms; - } + fh = dotall; + if (output->thermo->normflag) fh /= atom->natoms; delfh = fh - fhprev; diff --git a/src/min_linesearch.cpp b/src/min_linesearch.cpp index 24ba4c5c23..f875d4249e 100644 --- a/src/min_linesearch.cpp +++ b/src/min_linesearch.cpp @@ -329,7 +329,7 @@ int MinLineSearch::linemin_quadratic(double eoriginal, double &alpha) double fdothall,fdothme,hme,hmax,hmaxall; double de_ideal,de; double delfh,engprev,relerr,alphaprev,fhprev,fh,alpha0; - double dot[2],dotall[2]; + double dot,dotall; double *xatom,*x0atom,*fatom,*hatom; double alphamax; @@ -417,10 +417,9 @@ int MinLineSearch::linemin_quadratic(double eoriginal, double &alpha) // compute new fh, alpha, delfh - dot[0] = dot[1] = 0.0; + dot = 0.0; for (i = 0; i < nvec; i++) { - dot[0] += fvec[i]*fvec[i]; - dot[1] += fvec[i]*h[i]; + dot += fvec[i]*h[i]; } if (nextra_atom) for (m = 0; m < nextra_atom; m++) { @@ -428,18 +427,16 @@ int MinLineSearch::linemin_quadratic(double eoriginal, double &alpha) hatom = hextra_atom[m]; n = extra_nlen[m]; for (i = 0; i < n; i++) { - dot[0] += fatom[i]*fatom[i]; - dot[1] += fatom[i]*hatom[i]; + dot += fatom[i]*hatom[i]; } } - MPI_Allreduce(dot,dotall,2,MPI_DOUBLE,MPI_SUM,world); + MPI_Allreduce(&dot,&dotall,1,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]*hextra[i]; + dotall += fextra[i]*hextra[i]; } } - fh = dotall[1]; + fh = dotall; if (output->thermo->normflag) fh /= atom->natoms; delfh = fh - fhprev; From db45c23d690a0aef35b776fe5f1b963ae8e64509 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 4 Jan 2024 22:51:53 -0500 Subject: [PATCH 170/189] silence compiler warnings --- src/KOKKOS/atom_kokkos.cpp | 2 +- src/KOKKOS/atom_map_kokkos.cpp | 2 +- src/KOKKOS/atom_vec_angle_kokkos.cpp | 11 +-- src/KOKKOS/atom_vec_atomic_kokkos.cpp | 11 +-- src/KOKKOS/atom_vec_bond_kokkos.cpp | 9 +- src/KOKKOS/atom_vec_charge_kokkos.cpp | 11 +-- src/KOKKOS/atom_vec_dipole_kokkos.cpp | 13 ++- src/KOKKOS/atom_vec_full_kokkos.cpp | 12 +-- src/KOKKOS/atom_vec_hybrid_kokkos.cpp | 6 +- src/KOKKOS/atom_vec_molecular_kokkos.cpp | 9 +- src/KOKKOS/atom_vec_sphere_kokkos.cpp | 9 +- src/KOKKOS/atom_vec_spin_kokkos.cpp | 13 ++- src/KOKKOS/comm_kokkos.cpp | 2 +- src/KOKKOS/compute_reaxff_atom_kokkos.cpp | 2 +- src/KOKKOS/fix_acks2_reaxff_kokkos.cpp | 4 +- src/KOKKOS/fix_acks2_reaxff_kokkos.h | 6 +- src/KOKKOS/fix_nvt_sllod_kokkos.cpp | 2 +- src/KOKKOS/fix_spring_self_kokkos.cpp | 2 +- src/KOKKOS/grid3d_kokkos.cpp | 8 +- src/KOKKOS/kokkos.cpp | 2 +- src/KOKKOS/npair_kokkos.h | 7 +- src/KOKKOS/pair_kokkos.h | 2 +- src/KOKKOS/pair_pace_extrapolation_kokkos.cpp | 1 - src/KOKKOS/pair_pace_kokkos.cpp | 1 - src/KOKKOS/pair_reaxff_kokkos.h | 2 +- src/KOKKOS/transpose_helper_kokkos.h | 94 ++++++++----------- 26 files changed, 108 insertions(+), 135 deletions(-) diff --git a/src/KOKKOS/atom_kokkos.cpp b/src/KOKKOS/atom_kokkos.cpp index c55c1d315b..3db8adb5ea 100644 --- a/src/KOKKOS/atom_kokkos.cpp +++ b/src/KOKKOS/atom_kokkos.cpp @@ -300,7 +300,7 @@ void AtomKokkos::grow(unsigned int mask) int AtomKokkos::add_custom(const char *name, int flag, int cols) { - int index; + int index = -1; if (flag == 0 && cols == 0) { index = nivector; diff --git a/src/KOKKOS/atom_map_kokkos.cpp b/src/KOKKOS/atom_map_kokkos.cpp index 06516e4142..b19f87cb3b 100644 --- a/src/KOKKOS/atom_map_kokkos.cpp +++ b/src/KOKKOS/atom_map_kokkos.cpp @@ -143,7 +143,7 @@ void AtomKokkos::map_set() // sort by tag - int nmax = atom->nmax; + unsigned int nmax = atom->nmax; int realloc_flag = 0; if (d_tag_sorted.extent(0) < nmax) { diff --git a/src/KOKKOS/atom_vec_angle_kokkos.cpp b/src/KOKKOS/atom_vec_angle_kokkos.cpp index dd6be164c0..418c2d629d 100644 --- a/src/KOKKOS/atom_vec_angle_kokkos.cpp +++ b/src/KOKKOS/atom_vec_angle_kokkos.cpp @@ -680,7 +680,6 @@ struct AtomVecAngleKokkos_PackExchangeFunctor { const typename AT::tdual_xfloat_2d buf, typename AT::tdual_int_1d sendlist, typename AT::tdual_int_1d copylist): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), @@ -716,7 +715,8 @@ struct AtomVecAngleKokkos_PackExchangeFunctor { _angle_atom2w(atom->k_angle_atom2.view()), _angle_atom3w(atom->k_angle_atom3.view()), _sendlist(sendlist.template view()), - _copylist(copylist.template view()) { + _copylist(copylist.template view()), + _size_exchange(atom->avecKK->size_exchange) { const int maxsendlist = (buf.template view().extent(0)* buf.template view().extent(1))/_size_exchange; buffer_view(_buf,buf,maxsendlist,_size_exchange); @@ -858,7 +858,6 @@ struct AtomVecAngleKokkos_UnpackExchangeFunctor { const typename AT::tdual_xfloat_2d buf, typename AT::tdual_int_1d nlocal, int dim, X_FLOAT lo, X_FLOAT hi): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), @@ -876,8 +875,8 @@ struct AtomVecAngleKokkos_UnpackExchangeFunctor { _angle_atom1(atom->k_angle_atom1.view()), _angle_atom2(atom->k_angle_atom2.view()), _angle_atom3(atom->k_angle_atom3.view()), - _nlocal(nlocal.template view()),_dim(dim), - _lo(lo),_hi(hi) { + _nlocal(nlocal.template view()), + _dim(dim),_lo(lo),_hi(hi),_size_exchange(atom->avecKK->size_exchange) { const int maxsendlist = (buf.template view().extent(0)* buf.template view().extent(1))/_size_exchange; buffer_view(_buf,buf,maxsendlist,_size_exchange); @@ -927,7 +926,7 @@ struct AtomVecAngleKokkos_UnpackExchangeFunctor { int AtomVecAngleKokkos::unpack_exchange_kokkos(DAT::tdual_xfloat_2d &k_buf, int nrecv, int nlocal, int dim, X_FLOAT lo, X_FLOAT hi, ExecutionSpace space, - DAT::tdual_int_1d &k_indices) + DAT::tdual_int_1d &/*k_indices*/) { while (nlocal + nrecv/size_exchange >= nmax) grow(0); diff --git a/src/KOKKOS/atom_vec_atomic_kokkos.cpp b/src/KOKKOS/atom_vec_atomic_kokkos.cpp index 1ea8377a68..973ad2f7f2 100644 --- a/src/KOKKOS/atom_vec_atomic_kokkos.cpp +++ b/src/KOKKOS/atom_vec_atomic_kokkos.cpp @@ -294,7 +294,6 @@ struct AtomVecAtomicKokkos_PackExchangeFunctor { const typename AT::tdual_xfloat_2d buf, typename AT::tdual_int_1d sendlist, typename AT::tdual_int_1d copylist): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), @@ -308,7 +307,8 @@ struct AtomVecAtomicKokkos_PackExchangeFunctor { _maskw(atom->k_mask.view()), _imagew(atom->k_image.view()), _sendlist(sendlist.template view()), - _copylist(copylist.template view()) { + _copylist(copylist.template view()), + _size_exchange(atom->avecKK->size_exchange) { const int maxsendlist = (buf.template view().extent(0)*buf.template view().extent(1))/_size_exchange; buffer_view(_buf,buf,maxsendlist,_size_exchange); @@ -392,16 +392,15 @@ struct AtomVecAtomicKokkos_UnpackExchangeFunctor { typename AT::tdual_int_1d nlocal, typename AT::tdual_int_1d indices, int dim, X_FLOAT lo, X_FLOAT hi): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), _type(atom->k_type.view()), _mask(atom->k_mask.view()), _image(atom->k_image.view()), - _indices(indices.template view()), - _nlocal(nlocal.template view()),_dim(dim), - _lo(lo),_hi(hi) { + _nlocal(nlocal.template view()), + _indices(indices.template view()),_dim(dim), + _lo(lo),_hi(hi),_size_exchange(atom->avecKK->size_exchange) { const int maxsendlist = (buf.template view().extent(0)* buf.template view().extent(1))/_size_exchange; buffer_view(_buf,buf,maxsendlist,_size_exchange); diff --git a/src/KOKKOS/atom_vec_bond_kokkos.cpp b/src/KOKKOS/atom_vec_bond_kokkos.cpp index c45bdedf38..a4fd9ca1b5 100644 --- a/src/KOKKOS/atom_vec_bond_kokkos.cpp +++ b/src/KOKKOS/atom_vec_bond_kokkos.cpp @@ -352,7 +352,6 @@ struct AtomVecBondKokkos_PackExchangeFunctor { const typename AT::tdual_xfloat_2d buf, typename AT::tdual_int_1d sendlist, typename AT::tdual_int_1d copylist): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), @@ -378,7 +377,8 @@ struct AtomVecBondKokkos_PackExchangeFunctor { _bond_typew(atom->k_bond_type.view()), _bond_atomw(atom->k_bond_atom.view()), _sendlist(sendlist.template view()), - _copylist(copylist.template view()) { + _copylist(copylist.template view()), + _size_exchange(atom->avecKK->size_exchange) { const int maxsendlist = (buf.template view().extent(0)* buf.template view().extent(1))/_size_exchange; buffer_view(_buf,buf,maxsendlist,_size_exchange); @@ -503,7 +503,6 @@ struct AtomVecBondKokkos_UnpackExchangeFunctor { typename AT::tdual_int_1d nlocal, typename AT::tdual_int_1d indices, int dim, X_FLOAT lo, X_FLOAT hi): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), @@ -516,9 +515,9 @@ struct AtomVecBondKokkos_UnpackExchangeFunctor { _num_bond(atom->k_num_bond.view()), _bond_type(atom->k_bond_type.view()), _bond_atom(atom->k_bond_atom.view()), + _nlocal(nlocal.template view()), _indices(indices.template view()), - _nlocal(nlocal.template view()),_dim(dim), - _lo(lo),_hi(hi) { + _dim(dim),_lo(lo),_hi(hi),_size_exchange(atom->avecKK->size_exchange) { const int maxsendlist = (buf.template view().extent(0)* buf.template view().extent(1))/_size_exchange; buffer_view(_buf,buf,maxsendlist,_size_exchange); diff --git a/src/KOKKOS/atom_vec_charge_kokkos.cpp b/src/KOKKOS/atom_vec_charge_kokkos.cpp index 22fc63ff91..4fa814f1ac 100644 --- a/src/KOKKOS/atom_vec_charge_kokkos.cpp +++ b/src/KOKKOS/atom_vec_charge_kokkos.cpp @@ -366,7 +366,6 @@ struct AtomVecChargeKokkos_PackExchangeFunctor { const typename AT::tdual_xfloat_2d buf, typename AT::tdual_int_1d sendlist, typename AT::tdual_int_1d copylist): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), @@ -382,7 +381,8 @@ struct AtomVecChargeKokkos_PackExchangeFunctor { _imagew(atom->k_image.view()), _qw(atom->k_q.view()), _sendlist(sendlist.template view()), - _copylist(copylist.template view()) { + _copylist(copylist.template view()), + _size_exchange(atom->avecKK->size_exchange) { const int maxsendlist = (buf.template view().extent(0)* buf.template view().extent(1))/_size_exchange; @@ -474,17 +474,16 @@ struct AtomVecChargeKokkos_UnpackExchangeFunctor { typename AT::tdual_int_1d nlocal, typename AT::tdual_int_1d indices, int dim, X_FLOAT lo, X_FLOAT hi): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), _type(atom->k_type.view()), _mask(atom->k_mask.view()), _image(atom->k_image.view()), - _indices(indices.template view()), _q(atom->k_q.view()), - _nlocal(nlocal.template view()),_dim(dim), - _lo(lo),_hi(hi) { + _nlocal(nlocal.template view()), + _indices(indices.template view()),_dim(dim), + _lo(lo),_hi(hi),_size_exchange(atom->avecKK->size_exchange) { const int maxsendlist = (buf.template view().extent(0)*buf.template view().extent(1))/_size_exchange; buffer_view(_buf,buf,maxsendlist,_size_exchange); diff --git a/src/KOKKOS/atom_vec_dipole_kokkos.cpp b/src/KOKKOS/atom_vec_dipole_kokkos.cpp index ad06570cdc..ecc0f3b497 100644 --- a/src/KOKKOS/atom_vec_dipole_kokkos.cpp +++ b/src/KOKKOS/atom_vec_dipole_kokkos.cpp @@ -398,7 +398,6 @@ struct AtomVecDipoleKokkos_PackExchangeFunctor { const typename AT::tdual_xfloat_2d buf, typename AT::tdual_int_1d sendlist, typename AT::tdual_int_1d copylist): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), @@ -416,7 +415,8 @@ struct AtomVecDipoleKokkos_PackExchangeFunctor { _qw(atom->k_q.view()), _muw(atom->k_mu.view()), _sendlist(sendlist.template view()), - _copylist(copylist.template view()) { + _copylist(copylist.template view()), + _size_exchange(atom->avecKK->size_exchange) { const int maxsendlist = (buf.template view().extent(0)* buf.template view().extent(1))/_size_exchange; @@ -515,7 +515,6 @@ struct AtomVecDipoleKokkos_UnpackExchangeFunctor { const typename AT::tdual_xfloat_2d buf, typename AT::tdual_int_1d nlocal, int dim, X_FLOAT lo, X_FLOAT hi): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), @@ -524,8 +523,8 @@ struct AtomVecDipoleKokkos_UnpackExchangeFunctor { _image(atom->k_image.view()), _q(atom->k_q.view()), _mu(atom->k_mu.view()), - _nlocal(nlocal.template view()),_dim(dim), - _lo(lo),_hi(hi) { + _nlocal(nlocal.template view()), + _dim(dim),_lo(lo),_hi(hi),_size_exchange(atom->avecKK->size_exchange) { const int maxsendlist = (buf.template view().extent(0)*buf.template view().extent(1))/_size_exchange; buffer_view(_buf,buf,maxsendlist,_size_exchange); @@ -557,8 +556,8 @@ struct AtomVecDipoleKokkos_UnpackExchangeFunctor { /* ---------------------------------------------------------------------- */ int AtomVecDipoleKokkos::unpack_exchange_kokkos(DAT::tdual_xfloat_2d &k_buf, int nrecv, int nlocal, - int dim, X_FLOAT lo, X_FLOAT hi, ExecutionSpace space, - DAT::tdual_int_1d &k_indices) + int dim, X_FLOAT lo, X_FLOAT hi, ExecutionSpace space, + DAT::tdual_int_1d &/*k_indices*/) { if (space == Host) { k_count.h_view(0) = nlocal; diff --git a/src/KOKKOS/atom_vec_full_kokkos.cpp b/src/KOKKOS/atom_vec_full_kokkos.cpp index 829ebc75e6..732078a627 100644 --- a/src/KOKKOS/atom_vec_full_kokkos.cpp +++ b/src/KOKKOS/atom_vec_full_kokkos.cpp @@ -501,7 +501,6 @@ struct AtomVecFullKokkos_PackExchangeFunctor { const typename AT::tdual_xfloat_2d buf, typename AT::tdual_int_1d sendlist, typename AT::tdual_int_1d copylist): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), @@ -563,7 +562,8 @@ struct AtomVecFullKokkos_PackExchangeFunctor { _improper_atom3w(atom->k_improper_atom3.view()), _improper_atom4w(atom->k_improper_atom4.view()), _sendlist(sendlist.template view()), - _copylist(copylist.template view()) { + _copylist(copylist.template view()), + _size_exchange(atom->avecKK->size_exchange) { const int maxsendlist = (buf.template view().extent(0)* buf.template view().extent(1))/_size_exchange; buffer_view(_buf,buf,maxsendlist,_size_exchange); @@ -755,14 +755,12 @@ struct AtomVecFullKokkos_UnpackExchangeFunctor { typename AT::tdual_int_1d nlocal, typename AT::tdual_int_1d indices, int dim, X_FLOAT lo, X_FLOAT hi): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), _type(atom->k_type.view()), _mask(atom->k_mask.view()), _image(atom->k_image.view()), - _indices(indices.template view()), _q(atom->k_q.view()), _molecule(atom->k_molecule.view()), _nspecial(atom->k_nspecial.view()), @@ -787,9 +785,9 @@ struct AtomVecFullKokkos_UnpackExchangeFunctor { _improper_atom2(atom->k_improper_atom2.view()), _improper_atom3(atom->k_improper_atom3.view()), _improper_atom4(atom->k_improper_atom4.view()), - _nlocal(nlocal.template view()),_dim(dim), - _lo(lo),_hi(hi) { - + _nlocal(nlocal.template view()), + _indices(indices.template view()), + _dim(dim),_lo(lo),_hi(hi),_size_exchange(atom->avecKK->size_exchange) { const int maxsendlist = (buf.template view().extent(0)* buf.template view().extent(1))/_size_exchange; buffer_view(_buf,buf,maxsendlist,_size_exchange); diff --git a/src/KOKKOS/atom_vec_hybrid_kokkos.cpp b/src/KOKKOS/atom_vec_hybrid_kokkos.cpp index 4e01ab5794..08bcaaef74 100644 --- a/src/KOKKOS/atom_vec_hybrid_kokkos.cpp +++ b/src/KOKKOS/atom_vec_hybrid_kokkos.cpp @@ -66,7 +66,7 @@ void AtomVecHybridKokkos::sort_kokkos(Kokkos::BinSort &Sorte int AtomVecHybridKokkos::pack_comm_kokkos(const int &/*n*/, const DAT::tdual_int_2d &/*k_sendlist*/, const int & /*iswap*/, const DAT::tdual_xfloat_2d &/*buf*/, - const int &/*pbc_flag*/, const int pbc[]) + const int &/*pbc_flag*/, const int /*pbc*/[]) { error->all(FLERR,"AtomVecHybridKokkos doesn't yet support threaded comm"); return 0; @@ -80,7 +80,7 @@ void AtomVecHybridKokkos::unpack_comm_kokkos(const int &/*n*/, const int &/*nfir int AtomVecHybridKokkos::pack_comm_self(const int &/*n*/, const DAT::tdual_int_2d &/*list*/, const int & /*iswap*/, const int /*nfirst*/, - const int &/*pbc_flag*/, const int pbc[]) + const int &/*pbc_flag*/, const int /*pbc*/[]) { error->all(FLERR,"AtomVecHybridKokkos doesn't yet support threaded comm"); return 0; @@ -113,7 +113,7 @@ int AtomVecHybridKokkos::pack_exchange_kokkos(const int &/*nsend*/,DAT::tdual_xf int AtomVecHybridKokkos::unpack_exchange_kokkos(DAT::tdual_xfloat_2d & /*k_buf*/, int /*nrecv*/, int /*nlocal*/, int /*dim*/, X_FLOAT /*lo*/, X_FLOAT /*hi*/, ExecutionSpace /*space*/, - DAT::tdual_int_1d &k_indices) + DAT::tdual_int_1d &/*k_indices*/) { error->all(FLERR,"AtomVecHybridKokkos doesn't yet support threaded comm"); return 0; diff --git a/src/KOKKOS/atom_vec_molecular_kokkos.cpp b/src/KOKKOS/atom_vec_molecular_kokkos.cpp index 471dd0ad58..ec98ff9239 100644 --- a/src/KOKKOS/atom_vec_molecular_kokkos.cpp +++ b/src/KOKKOS/atom_vec_molecular_kokkos.cpp @@ -762,7 +762,6 @@ struct AtomVecMolecularKokkos_PackExchangeFunctor { const typename AT::tdual_xfloat_2d buf, typename AT::tdual_int_1d sendlist, typename AT::tdual_int_1d copylist): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), @@ -822,7 +821,8 @@ struct AtomVecMolecularKokkos_PackExchangeFunctor { _improper_atom3w(atom->k_improper_atom3.view()), _improper_atom4w(atom->k_improper_atom4.view()), _sendlist(sendlist.template view()), - _copylist(copylist.template view()) { + _copylist(copylist.template view()), + _size_exchange(atom->avecKK->size_exchange) { const int maxsendlist = (buf.template view().extent(0)* buf.template view().extent(1))/_size_exchange; buffer_view(_buf,buf,maxsendlist,_size_exchange); @@ -1010,7 +1010,6 @@ struct AtomVecMolecularKokkos_UnpackExchangeFunctor { typename AT::tdual_int_1d nlocal, typename AT::tdual_int_1d indices, int dim, X_FLOAT lo, X_FLOAT hi): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), @@ -1040,9 +1039,9 @@ struct AtomVecMolecularKokkos_UnpackExchangeFunctor { _improper_atom2(atom->k_improper_atom2.view()), _improper_atom3(atom->k_improper_atom3.view()), _improper_atom4(atom->k_improper_atom4.view()), + _nlocal(nlocal.template view()), _indices(indices.template view()), - _nlocal(nlocal.template view()),_dim(dim), - _lo(lo),_hi(hi) { + _dim(dim),_lo(lo),_hi(hi),_size_exchange(atom->avecKK->size_exchange) { const int maxsendlist = (buf.template view().extent(0)* buf.template view().extent(1))/_size_exchange; buffer_view(_buf,buf,maxsendlist,_size_exchange); diff --git a/src/KOKKOS/atom_vec_sphere_kokkos.cpp b/src/KOKKOS/atom_vec_sphere_kokkos.cpp index 5a1c2beee3..3dfb5143cd 100644 --- a/src/KOKKOS/atom_vec_sphere_kokkos.cpp +++ b/src/KOKKOS/atom_vec_sphere_kokkos.cpp @@ -1448,7 +1448,6 @@ struct AtomVecSphereKokkos_PackExchangeFunctor { const typename AT::tdual_xfloat_2d buf, typename AT::tdual_int_1d sendlist, typename AT::tdual_int_1d copylist): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), @@ -1468,7 +1467,8 @@ struct AtomVecSphereKokkos_PackExchangeFunctor { _rmassw(atom->k_rmass.view()), _omegaw(atom->k_omega.view()), _sendlist(sendlist.template view()), - _copylist(copylist.template view()) { + _copylist(copylist.template view()), + _size_exchange(atom->avecKK->size_exchange) { const int maxsend = (buf.template view().extent(0)*buf.template view().extent(1))/_size_exchange; _buf = typename AT::t_xfloat_2d_um(buf.template view().data(),maxsend,_size_exchange); @@ -1572,7 +1572,6 @@ struct AtomVecSphereKokkos_UnpackExchangeFunctor { typename AT::tdual_int_1d nlocal, typename AT::tdual_int_1d indices, int dim, X_FLOAT lo, X_FLOAT hi): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), @@ -1584,9 +1583,7 @@ struct AtomVecSphereKokkos_UnpackExchangeFunctor { _omega(atom->k_omega.view()), _nlocal(nlocal.template view()), _indices(indices.template view()), - _dim(dim), - _lo(lo),_hi(hi) - { + _dim(dim),_lo(lo),_hi(hi),_size_exchange(atom->avecKK->size_exchange) { const size_t size_exchange = 16; const int maxsendlist = (buf.template view().extent(0)*buf.template view().extent(1))/size_exchange; diff --git a/src/KOKKOS/atom_vec_spin_kokkos.cpp b/src/KOKKOS/atom_vec_spin_kokkos.cpp index d2dd3a05ab..72d38a731e 100644 --- a/src/KOKKOS/atom_vec_spin_kokkos.cpp +++ b/src/KOKKOS/atom_vec_spin_kokkos.cpp @@ -410,7 +410,6 @@ struct AtomVecSpinKokkos_PackExchangeFunctor { const typename AT::tdual_xfloat_2d buf, typename AT::tdual_int_1d sendlist, typename AT::tdual_int_1d copylist): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), @@ -426,7 +425,8 @@ struct AtomVecSpinKokkos_PackExchangeFunctor { _imagew(atom->k_image.view()), _spw(atom->k_sp.view()), _sendlist(sendlist.template view()), - _copylist(copylist.template view()) { + _copylist(copylist.template view()), + _size_exchange(atom->avecKK->size_exchange) { const int maxsendlist = (buf.template view().extent(0)* buf.template view().extent(1))/_size_exchange; buffer_view(_buf,buf,maxsendlist,_size_exchange); @@ -521,7 +521,6 @@ struct AtomVecSpinKokkos_UnpackExchangeFunctor { const typename AT::tdual_xfloat_2d buf, typename AT::tdual_int_1d nlocal, int dim, X_FLOAT lo, X_FLOAT hi): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), @@ -529,8 +528,8 @@ struct AtomVecSpinKokkos_UnpackExchangeFunctor { _mask(atom->k_mask.view()), _image(atom->k_image.view()), _sp(atom->k_sp.view()), - _nlocal(nlocal.template view()),_dim(dim), - _lo(lo),_hi(hi) { + _nlocal(nlocal.template view()), + _dim(dim),_lo(lo),_hi(hi),_size_exchange(atom->avecKK->size_exchange) { const int maxsendlist = (buf.template view().extent(0)*buf.template view().extent(1))/_size_exchange; buffer_view(_buf,buf,maxsendlist,_size_exchange); @@ -563,7 +562,7 @@ struct AtomVecSpinKokkos_UnpackExchangeFunctor { int AtomVecSpinKokkos::unpack_exchange_kokkos(DAT::tdual_xfloat_2d &k_buf, int nrecv, int nlocal, int dim, X_FLOAT lo, X_FLOAT hi, ExecutionSpace space, - DAT::tdual_int_1d &k_indices) + DAT::tdual_int_1d &/*k_indices*/) { while (nlocal + nrecv/size_exchange >= nmax) grow(0); @@ -592,7 +591,7 @@ int AtomVecSpinKokkos::unpack_exchange_kokkos(DAT::tdual_xfloat_2d &k_buf, int n include f b/c this is invoked from within SPIN pair styles ------------------------------------------------------------------------- */ -void AtomVecSpinKokkos::force_clear(int n, size_t nbytes) +void AtomVecSpinKokkos::force_clear(int /*n*/, size_t nbytes) { int nzero = (double)nbytes/sizeof(double); diff --git a/src/KOKKOS/comm_kokkos.cpp b/src/KOKKOS/comm_kokkos.cpp index b586dca7a5..5d2a7795fe 100644 --- a/src/KOKKOS/comm_kokkos.cpp +++ b/src/KOKKOS/comm_kokkos.cpp @@ -864,7 +864,7 @@ void CommKokkos::exchange_device() if (nrecv) { if (atom->nextra_grow) { - if (k_indices.extent(0) < nrecv/data_size) + if ((int) k_indices.extent(0) < nrecv/data_size) MemoryKokkos::realloc_kokkos(k_indices,"comm:indices",nrecv/data_size); } else if (k_indices.h_view.data()) k_indices = DAT::tdual_int_1d(); diff --git a/src/KOKKOS/compute_reaxff_atom_kokkos.cpp b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp index 8dbcb9441e..3f6c9242d4 100644 --- a/src/KOKKOS/compute_reaxff_atom_kokkos.cpp +++ b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp @@ -87,7 +87,7 @@ void ComputeReaxFFAtomKokkos::compute_bonds() nbuf = ((store_bonds ? maxnumbonds*2 : 0) + 3)*nlocal; - if (!buf || k_buf.extent(0) < nbuf) { + if (!buf || ((int)k_buf.extent(0) < nbuf)) { memoryKK->destroy_kokkos(k_buf, buf); memoryKK->create_kokkos(k_buf, buf, nbuf, "reaxff/atom:buf"); } diff --git a/src/KOKKOS/fix_acks2_reaxff_kokkos.cpp b/src/KOKKOS/fix_acks2_reaxff_kokkos.cpp index 59ed918729..9c34908d08 100644 --- a/src/KOKKOS/fix_acks2_reaxff_kokkos.cpp +++ b/src/KOKKOS/fix_acks2_reaxff_kokkos.cpp @@ -192,7 +192,7 @@ void FixACKS2ReaxFFKokkos::setup_pre_force(int vflag) /* ---------------------------------------------------------------------- */ template -void FixACKS2ReaxFFKokkos::pre_force(int vflag) +void FixACKS2ReaxFFKokkos::pre_force(int /*vflag*/) { if (update->ntimestep % nevery) return; @@ -298,8 +298,8 @@ void FixACKS2ReaxFFKokkos::pre_force(int vflag) } else { // GPU, use teams Kokkos::deep_copy(d_mfill_offset,0); - int vector_length = 32; int atoms_per_team = 4; + int vector_length = 32; int num_teams = nn / atoms_per_team + (nn % atoms_per_team ? 1 : 0); Kokkos::TeamPolicy policy(num_teams, atoms_per_team, diff --git a/src/KOKKOS/fix_acks2_reaxff_kokkos.h b/src/KOKKOS/fix_acks2_reaxff_kokkos.h index 127c8d0402..c27719c364 100644 --- a/src/KOKKOS/fix_acks2_reaxff_kokkos.h +++ b/src/KOKKOS/fix_acks2_reaxff_kokkos.h @@ -289,8 +289,7 @@ struct FixACKS2ReaxFFKokkosComputeHFunctor { FixACKS2ReaxFFKokkosComputeHFunctor(FixACKS2ReaxFFKokkos *c_ptr, int _atoms_per_team, int _vector_length) - : c(*c_ptr), atoms_per_team(_atoms_per_team), - vector_length(_vector_length) { + : atoms_per_team(_atoms_per_team), vector_length(_vector_length), c(*c_ptr) { c.cleanup_copy(); }; @@ -337,8 +336,7 @@ struct FixACKS2ReaxFFKokkosComputeXFunctor { FixACKS2ReaxFFKokkosComputeXFunctor(FixACKS2ReaxFFKokkos *c_ptr, int _atoms_per_team, int _vector_length) - : c(*c_ptr), atoms_per_team(_atoms_per_team), - vector_length(_vector_length) { + : atoms_per_team(_atoms_per_team), vector_length(_vector_length), c(*c_ptr) { c.cleanup_copy(); }; diff --git a/src/KOKKOS/fix_nvt_sllod_kokkos.cpp b/src/KOKKOS/fix_nvt_sllod_kokkos.cpp index bd65a6965e..948e3b88f6 100644 --- a/src/KOKKOS/fix_nvt_sllod_kokkos.cpp +++ b/src/KOKKOS/fix_nvt_sllod_kokkos.cpp @@ -128,7 +128,7 @@ void FixNVTSllodKokkos::nh_v_temp() d_h_two = Few(h_two); - if (vdelu.extent(0) < atomKK->nmax) + if ((int)vdelu.extent(0) < atomKK->nmax) vdelu = typename AT::t_v_array(Kokkos::NoInit("nvt/sllod/kk:vdelu"), atomKK->nmax); if (!this->psllod_flag) { diff --git a/src/KOKKOS/fix_spring_self_kokkos.cpp b/src/KOKKOS/fix_spring_self_kokkos.cpp index efd8a652ff..2da2fa9f14 100644 --- a/src/KOKKOS/fix_spring_self_kokkos.cpp +++ b/src/KOKKOS/fix_spring_self_kokkos.cpp @@ -184,7 +184,7 @@ void FixSpringSelfKokkos::copy_arrays(int i, int j, int delflag) template KOKKOS_INLINE_FUNCTION -void FixSpringSelfKokkos::pack_exchange_item(const int &mysend, int &offset, const bool &final) const +void FixSpringSelfKokkos::pack_exchange_item(const int &mysend, int &offset, const bool &/*final*/) const { const int i = d_exchange_sendlist(mysend); diff --git a/src/KOKKOS/grid3d_kokkos.cpp b/src/KOKKOS/grid3d_kokkos.cpp index 7b97c417dd..87f2baff84 100644 --- a/src/KOKKOS/grid3d_kokkos.cpp +++ b/src/KOKKOS/grid3d_kokkos.cpp @@ -635,7 +635,7 @@ void Grid3dKokkos::setup_comm_tiled(int &nbuf1, int &nbuf2) ------------------------------------------------------------------------- */ template -void Grid3dKokkos::forward_comm(int caller, void *ptr, int which, int nper, int nbyte, +void Grid3dKokkos::forward_comm(int caller, void *ptr, int which, int nper, int /*nbyte*/, FFT_DAT::tdual_FFT_SCALAR_1d& k_buf1, FFT_DAT::tdual_FFT_SCALAR_1d& k_buf2, MPI_Datatype datatype) { @@ -645,7 +645,7 @@ void Grid3dKokkos::forward_comm(int caller, void *ptr, int which, in else forward_comm_kspace_tiled((KSpace *) ptr,which,nper,k_buf1,k_buf2,datatype); } else - error->all(FLERR,"Kokkos grid comm only supports Kspace"); + error->all(FLERR,"Kokkos grid comm currently only supports Kspace"); } /* ---------------------------------------------------------------------- @@ -775,7 +775,7 @@ forward_comm_kspace_tiled(KSpace *kspace, int which, int nper, ------------------------------------------------------------------------- */ template -void Grid3dKokkos::reverse_comm(int caller, void *ptr, int which, int nper, int nbyte, +void Grid3dKokkos::reverse_comm(int caller, void *ptr, int which, int nper, int /*nbyte*/, FFT_DAT::tdual_FFT_SCALAR_1d& k_buf1, FFT_DAT::tdual_FFT_SCALAR_1d& k_buf2, MPI_Datatype datatype) { @@ -945,7 +945,7 @@ int Grid3dKokkos::indices(DAT::tdual_int_2d &k_list, int index, int xlo, int xhi, int ylo, int yhi, int zlo, int zhi) { int nmax = (xhi-xlo+1) * (yhi-ylo+1) * (zhi-zlo+1); - if (k_list.extent(1) < nmax) + if ((int)k_list.extent(1) < nmax) k_list.resize(k_list.extent(0),nmax); if (nmax == 0) return 0; diff --git a/src/KOKKOS/kokkos.cpp b/src/KOKKOS/kokkos.cpp index 5572f69901..b8bcd80a00 100644 --- a/src/KOKKOS/kokkos.cpp +++ b/src/KOKKOS/kokkos.cpp @@ -622,7 +622,7 @@ void KokkosLMP::accelerator(int narg, char **arg) int KokkosLMP::neigh_count(int m) { - int inum; + int inum = 0; int nneigh = 0; ArrayTypes::t_int_1d h_ilist; diff --git a/src/KOKKOS/npair_kokkos.h b/src/KOKKOS/npair_kokkos.h index fe5484a771..8dd7a1c5ef 100644 --- a/src/KOKKOS/npair_kokkos.h +++ b/src/KOKKOS/npair_kokkos.h @@ -303,7 +303,7 @@ class NeighborKokkosExecute const typename ArrayTypes::t_int_scalar _h_resize, const typename AT::t_int_scalar _new_maxneighs, const typename ArrayTypes::t_int_scalar _h_new_maxneighs): - neigh_list(_neigh_list), cutneighsq(_cutneighsq),delta(_delta),exclude(_exclude), + neigh_list(_neigh_list),delta(_delta),cutneighsq(_cutneighsq),exclude(_exclude), nex_type(_nex_type),ex1_type(_ex1_type),ex2_type(_ex2_type), ex_type(_ex_type),nex_group(_nex_group), ex1_bit(_ex1_bit),ex2_bit(_ex2_bit), @@ -319,10 +319,11 @@ class NeighborKokkosExecute mbinxlo(_mbinxlo),mbinylo(_mbinylo),mbinzlo(_mbinzlo), bininvx(_bininvx),bininvy(_bininvy),bininvz(_bininvz), nlocal(_nlocal),nall(_nall),neigh_transpose(_neigh_transpose), + resize(_resize),new_maxneighs(_new_maxneighs), + h_resize(_h_resize),h_new_maxneighs(_h_new_maxneighs), xperiodic(_xperiodic),yperiodic(_yperiodic),zperiodic(_zperiodic), xprd_half(_xprd_half),yprd_half(_yprd_half),zprd_half(_zprd_half), - skin(_skin),resize(_resize),h_resize(_h_resize), - new_maxneighs(_new_maxneighs),h_new_maxneighs(_h_new_maxneighs) { + skin(_skin) { if (molecular == 2) moltemplate = 1; else moltemplate = 0; diff --git a/src/KOKKOS/pair_kokkos.h b/src/KOKKOS/pair_kokkos.h index 9521268284..398266cb61 100644 --- a/src/KOKKOS/pair_kokkos.h +++ b/src/KOKKOS/pair_kokkos.h @@ -948,9 +948,9 @@ EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t<(NEIGHFLAG&P static int vectorsize = 0; static int atoms_per_team = 0; - static int lastcall = -1; #if defined(LMP_KOKKOS_GPU) + static int lastcall = -1; if (!vectorsize || lastcall < fpair->lmp->neighbor->lastcall) { lastcall = fpair->lmp->update->ntimestep; vectorsize = GetMaxNeighs(list); diff --git a/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp b/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp index 18ecaf6e69..ef747ef95c 100644 --- a/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp +++ b/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp @@ -900,7 +900,6 @@ void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeNeig [&](const int offset, minloc_value_type &min_d_dist) { int j = d_nearest(ii,offset); j &= NEIGHMASK; - const int jtype = type(j); auto r = d_rnorms(ii,offset); const int mu_j = d_map(type(j)); const F_FLOAT d = r - (d_cut_in(mu_i, mu_j) - d_dcut_in(mu_i, mu_j)); diff --git a/src/KOKKOS/pair_pace_kokkos.cpp b/src/KOKKOS/pair_pace_kokkos.cpp index aaed01510a..4046649375 100644 --- a/src/KOKKOS/pair_pace_kokkos.cpp +++ b/src/KOKKOS/pair_pace_kokkos.cpp @@ -845,7 +845,6 @@ void PairPACEKokkos::operator() (TagPairPACEComputeNeigh,const typen [&](const int offset, minloc_value_type &min_d_dist) { int j = d_nearest(ii,offset); j &= NEIGHMASK; - const int jtype = type(j); auto r = d_rnorms(ii,offset); const int mu_j = d_map(type(j)); const F_FLOAT d = r - (d_cut_in(mu_i, mu_j) - d_dcut_in(mu_i, mu_j)); diff --git a/src/KOKKOS/pair_reaxff_kokkos.h b/src/KOKKOS/pair_reaxff_kokkos.h index fba7c03ec4..5010310232 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.h +++ b/src/KOKKOS/pair_reaxff_kokkos.h @@ -526,7 +526,7 @@ struct PairReaxKokkosFindBondFunctor { typedef int value_type; int groupbit; PairReaxFFKokkos c; - PairReaxKokkosFindBondFunctor(PairReaxFFKokkos* c_ptr, int groupbit):c(*c_ptr),groupbit(groupbit) {}; + PairReaxKokkosFindBondFunctor(PairReaxFFKokkos* c_ptr, int groupbit):groupbit(groupbit),c(*c_ptr){}; KOKKOS_INLINE_FUNCTION void join(int &dst, diff --git a/src/KOKKOS/transpose_helper_kokkos.h b/src/KOKKOS/transpose_helper_kokkos.h index e3a4d86f9a..06af0aea91 100644 --- a/src/KOKKOS/transpose_helper_kokkos.h +++ b/src/KOKKOS/transpose_helper_kokkos.h @@ -125,8 +125,7 @@ struct TransposeHelperKokkos { elem[0] = extent_tile_id[0] * tile_size; elem[1] = extent_tile_id[1] * tile_size; - if (elem[0] >= d_dst.extent(0) || - elem[1] >= d_dst.extent(1)) return; + if ((elem[0] >= (int)d_dst.extent(0)) || (elem[1] >= (int)d_dst.extent(1))) return; // determine if a row/column is a full `tile_size` in size or not bool perfect_pad[2]; @@ -135,35 +134,30 @@ struct TransposeHelperKokkos { // load phase if (src_is_layout_right) { - Kokkos::parallel_for(Kokkos::ThreadVectorRange(team_member, tile_size), - [&] (const int j) { - - if (elem[1] + j < d_src.extent(1)) { - if (perfect_pad[0]) { - for (int i = 0; i < tile_size; i++) - buffer[i * (tile_size + bank_pad) + j] = d_src(elem[0] + i, elem[1] + j); - } else { - for (int i = 0; i < (d_src.extent(0) - elem[0]); i++) - buffer[i * (tile_size + bank_pad) + j] = d_src(elem[0] + i, elem[1] + j); + Kokkos::parallel_for(Kokkos::ThreadVectorRange(team_member, tile_size), [&] (const int j) { + if (elem[1] + j < (int)d_src.extent(1)) { + if (perfect_pad[0]) { + for (int i = 0; i < tile_size; i++) + buffer[i * (tile_size + bank_pad) + j] = d_src(elem[0] + i, elem[1] + j); + } else { + for (int i = 0; i < ((int)d_src.extent(0) - elem[0]); i++) + buffer[i * (tile_size + bank_pad) + j] = d_src(elem[0] + i, elem[1] + j); + } } - } - }); - + }); } else { // src is layout left - Kokkos::parallel_for(Kokkos::ThreadVectorRange(team_member, tile_size), - [&] (const int i) { - - if (elem[0] + i < d_src.extent(0)) { - if (perfect_pad[1]) { - for (int j = 0; j < tile_size; j++) - buffer[i * (tile_size + bank_pad) + j] = d_src(elem[0] + i, elem[1] + j); - } else { - for (int j = 0; j < (d_src.extent(1) - elem[1]); j++) - buffer[i * (tile_size + bank_pad) + j] = d_src(elem[0] + i, elem[1] + j); + Kokkos::parallel_for(Kokkos::ThreadVectorRange(team_member, tile_size), [&] (const int i) { + if (elem[0] + i < (int)d_src.extent(0)) { + if (perfect_pad[1]) { + for (int j = 0; j < tile_size; j++) + buffer[i * (tile_size + bank_pad) + j] = d_src(elem[0] + i, elem[1] + j); + } else { + for (int j = 0; j < ((int)d_src.extent(1) - elem[1]); j++) + buffer[i * (tile_size + bank_pad) + j] = d_src(elem[0] + i, elem[1] + j); + } } - } - }); + }); } // No need for an extra sync b/c there is an implicit sync at the end @@ -171,37 +165,31 @@ struct TransposeHelperKokkos { // save phase if (src_is_layout_right) { - Kokkos::parallel_for(Kokkos::ThreadVectorRange(team_member, tile_size), - [&] (const int i) { - - if (elem[0] + i < d_dst.extent(0)) { - if (perfect_pad[1]) { - for (int j = 0; j < tile_size; j++) - d_dst(elem[0] + i, elem[1] + j) = buffer[i * (tile_size + bank_pad) + j]; - } else { - for (int j = 0; j < (d_dst.extent(1) - elem[1]); j++) - d_dst(elem[0] + i, elem[1] + j) = buffer[i * (tile_size + bank_pad) + j]; + Kokkos::parallel_for(Kokkos::ThreadVectorRange(team_member, tile_size), [&] (const int i) { + if (elem[0] + i < (int)d_dst.extent(0)) { + if (perfect_pad[1]) { + for (int j = 0; j < tile_size; j++) + d_dst(elem[0] + i, elem[1] + j) = buffer[i * (tile_size + bank_pad) + j]; + } else { + for (int j = 0; j < ((int)d_dst.extent(1) - elem[1]); j++) + d_dst(elem[0] + i, elem[1] + j) = buffer[i * (tile_size + bank_pad) + j]; + } } - } - }); + }); } else { - // src is layout left - Kokkos::parallel_for(Kokkos::ThreadVectorRange(team_member, tile_size), - [&] (const int j) { - - if (elem[1] + j < d_dst.extent(1)) { - if (perfect_pad[0]) { - for (int i = 0; i < tile_size; i++) - d_dst(elem[0] + i, elem[1] + j) = buffer[i * (tile_size + bank_pad) + j]; - } else { - for (int i = 0; i < (d_dst.extent(0) - elem[0]); i++) - d_dst(elem[0] + i, elem[1] + j) = buffer[i * (tile_size + bank_pad) + j]; + Kokkos::parallel_for(Kokkos::ThreadVectorRange(team_member, tile_size), [&] (const int j) { + if (elem[1] + j < (int)d_dst.extent(1)) { + if (perfect_pad[0]) { + for (int i = 0; i < tile_size; i++) + d_dst(elem[0] + i, elem[1] + j) = buffer[i * (tile_size + bank_pad) + j]; + } else { + for (int i = 0; i < ((int)d_dst.extent(0) - elem[0]); i++) + d_dst(elem[0] + i, elem[1] + j) = buffer[i * (tile_size + bank_pad) + j]; + } } - } - }); + }); } - } }; From 93b7c6e380b4ebae54bead98db3097abe0e3f97c Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Wed, 10 Jan 2024 12:21:22 -0700 Subject: [PATCH 171/189] Fix warnings --- src/KOKKOS/kissfft_kokkos.h | 2 +- src/write_restart.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/KOKKOS/kissfft_kokkos.h b/src/KOKKOS/kissfft_kokkos.h index 265677a21c..e24768f774 100644 --- a/src/KOKKOS/kissfft_kokkos.h +++ b/src/KOKKOS/kissfft_kokkos.h @@ -489,7 +489,7 @@ class KissFFTKokkos { * It can be freed with free(), rather than a kiss_fft-specific function. */ - static kiss_fft_state_kokkos kiss_fft_alloc_kokkos(int nfft, int inverse_fft, void *mem, size_t *lenmem) + static kiss_fft_state_kokkos kiss_fft_alloc_kokkos(int nfft, int inverse_fft, void * /*mem*/, size_t * /*lenmem*/) { kiss_fft_state_kokkos st; int i; diff --git a/src/write_restart.cpp b/src/write_restart.cpp index a996532687..ad279c14f6 100644 --- a/src/write_restart.cpp +++ b/src/write_restart.cpp @@ -545,7 +545,7 @@ void WriteRestart::force_fields() all procs call this method, only proc 0 writes to file ------------------------------------------------------------------------- */ -void WriteRestart::file_layout(int send_size) +void WriteRestart::file_layout(int /*send_size*/) { if (me == 0) write_int(MULTIPROC,multiproc); From 8b89c330e6ce94d69b05ec1d5e317c3d77ec44b7 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 10 Jan 2024 15:32:02 -0500 Subject: [PATCH 172/189] make removed DOF computation large system compatible --- src/EFF/fix_langevin_eff.cpp | 4 ++-- src/KOKKOS/fix_shake_kokkos.cpp | 10 +++++----- src/KOKKOS/fix_shake_kokkos.h | 4 +--- src/LATBOLTZ/fix_lb_fluid.cpp | 6 +++--- src/LATBOLTZ/fix_lb_fluid.h | 2 +- src/MANIFOLD/fix_nve_manifold_rattle.cpp | 10 +++++----- src/MANIFOLD/fix_nve_manifold_rattle.h | 2 +- src/POEMS/fix_poems.cpp | 10 +++++----- src/POEMS/fix_poems.h | 2 +- src/RIGID/fix_rigid.cpp | 4 ++-- src/RIGID/fix_rigid.h | 2 +- src/RIGID/fix_rigid_small.cpp | 8 ++++---- src/RIGID/fix_rigid_small.h | 2 +- src/RIGID/fix_shake.cpp | 8 ++++---- src/RIGID/fix_shake.h | 2 +- src/compute.cpp | 2 +- src/fix.h | 2 +- 17 files changed, 39 insertions(+), 41 deletions(-) diff --git a/src/EFF/fix_langevin_eff.cpp b/src/EFF/fix_langevin_eff.cpp index 8c255e4348..a25b6ac837 100644 --- a/src/EFF/fix_langevin_eff.cpp +++ b/src/EFF/fix_langevin_eff.cpp @@ -137,7 +137,7 @@ void FixLangevinEff::post_force_no_tally() dof = domain->dimension * particles; fix_dof = 0; for (int i = 0; i < modify->nfix; i++) - fix_dof += modify->fix[i]->dof(igroup); + fix_dof += (int)modify->fix[i]->dof(igroup); // extra_dof = domain->dimension dof -= domain->dimension + fix_dof; @@ -306,7 +306,7 @@ void FixLangevinEff::post_force_tally() dof = domain->dimension * particles; fix_dof = 0; for (int i = 0; i < modify->nfix; i++) - fix_dof += modify->fix[i]->dof(igroup); + fix_dof += (int)modify->fix[i]->dof(igroup); // extra_dof = domain->dimension dof -= domain->dimension + fix_dof; diff --git a/src/KOKKOS/fix_shake_kokkos.cpp b/src/KOKKOS/fix_shake_kokkos.cpp index dd6de8f9ec..c31e38a05e 100644 --- a/src/KOKKOS/fix_shake_kokkos.cpp +++ b/src/KOKKOS/fix_shake_kokkos.cpp @@ -525,7 +525,7 @@ void FixShakeKokkos::operator()(TagFixShakePostForce -int FixShakeKokkos::dof(int igroup) +bigint FixShakeKokkos::dof(int igroup) { d_mask = atomKK->k_mask.view(); d_tag = atomKK->k_tag.view(); @@ -538,7 +538,7 @@ int FixShakeKokkos::dof(int igroup) // count dof in a cluster if and only if // the central atom is in group and atom i is the central atom - int n = 0; + bigint n = 0; { // local variables for lambda capture @@ -549,7 +549,7 @@ int FixShakeKokkos::dof(int igroup) auto groupbit = group->bitmask[igroup]; Kokkos::parallel_reduce(Kokkos::RangePolicy(0,nlocal), - LAMMPS_LAMBDA(const int& i, int& n) { + LAMMPS_LAMBDA(const int& i, bigint& n) { if (!(mask[i] & groupbit)) return; if (d_shake_flag[i] == 0) return; if (d_shake_atom(i,0) != tag[i]) return; @@ -560,8 +560,8 @@ int FixShakeKokkos::dof(int igroup) },n); } - int nall; - MPI_Allreduce(&n,&nall,1,MPI_INT,MPI_SUM,world); + bigint nall; + MPI_Allreduce(&n,&nall,1,MPI_LMP_BIGINT,MPI_SUM,world); return nall; } diff --git a/src/KOKKOS/fix_shake_kokkos.h b/src/KOKKOS/fix_shake_kokkos.h index 185e69ce86..7c830e94d8 100644 --- a/src/KOKKOS/fix_shake_kokkos.h +++ b/src/KOKKOS/fix_shake_kokkos.h @@ -44,8 +44,6 @@ struct TagFixShakeUnpackExchange{}; template class FixShakeKokkos : public FixShake, public KokkosBase { - //friend class FixEHEX; - public: typedef DeviceType device_type; typedef EV_FLOAT value_type; @@ -77,7 +75,7 @@ class FixShakeKokkos : public FixShake, public KokkosBase { void shake_end_of_step(int vflag) override; void correct_coordinates(int vflag) override; - int dof(int) override; + bigint dof(int) override; void unconstrained_update() override; diff --git a/src/LATBOLTZ/fix_lb_fluid.cpp b/src/LATBOLTZ/fix_lb_fluid.cpp index f692d28084..f3d8f45142 100644 --- a/src/LATBOLTZ/fix_lb_fluid.cpp +++ b/src/LATBOLTZ/fix_lb_fluid.cpp @@ -4430,9 +4430,9 @@ void FixLbFluid::calc_MPT(double &totalmass, double totalmomentum[3], double &Ta ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */ -int FixLbFluid::adjust_dof_fix() /* Based on same private method in compute class */ -{ /* altered to return fix_dof */ - int fix_dof = 0; +bigint FixLbFluid::adjust_dof_fix() /* Based on same private method in compute class */ +{ /* altered to return fix_dof */ + bigint fix_dof = 0; for (auto &ifix : modify->get_fix_list()) if (ifix->dof_flag) fix_dof += ifix->dof(igroup); return fix_dof; diff --git a/src/LATBOLTZ/fix_lb_fluid.h b/src/LATBOLTZ/fix_lb_fluid.h index 19cd2c6dc3..f134b50901 100644 --- a/src/LATBOLTZ/fix_lb_fluid.h +++ b/src/LATBOLTZ/fix_lb_fluid.h @@ -182,7 +182,7 @@ class FixLbFluid : public Fix { void calc_fluidforceII(void); void calc_fluidforceweight(void); - int adjust_dof_fix(); + bigint adjust_dof_fix(); double dof_compute(); /* nanopit parameters */ diff --git a/src/MANIFOLD/fix_nve_manifold_rattle.cpp b/src/MANIFOLD/fix_nve_manifold_rattle.cpp index b1efea951f..dc0492dbe9 100644 --- a/src/MANIFOLD/fix_nve_manifold_rattle.cpp +++ b/src/MANIFOLD/fix_nve_manifold_rattle.cpp @@ -287,21 +287,21 @@ void FixNVEManifoldRattle::update_var_params() /* ----------------------------------------------------------------------------- ---------------------------------------------------------------------------*/ -int FixNVEManifoldRattle::dof(int /*igroup*/) +bigint FixNVEManifoldRattle::dof(int /*igroup*/) { int *mask = atom->mask; int nlocal = atom->nlocal; - int natoms = 0; + bigint natoms = 0; for (int i = 0; i < nlocal; ++i) { if (mask[i] & groupbit) ++natoms; } - int dofs; - MPI_Allreduce( &natoms, &dofs, 1, MPI_INT, MPI_SUM, world ); + bigint dofs; + MPI_Allreduce( &natoms, &dofs, 1, MPI_LMP_BIGINT, MPI_SUM, world ); // Make sure that, if there is just no or one atom, no dofs are subtracted, // since for the first atom already 3 dofs are subtracted because of the - // centre of mass corrections: + // center of mass corrections: if (dofs <= 1) dofs = 0; stats.dofs_removed = dofs; diff --git a/src/MANIFOLD/fix_nve_manifold_rattle.h b/src/MANIFOLD/fix_nve_manifold_rattle.h index 3eae9c4bc3..7c9e302094 100644 --- a/src/MANIFOLD/fix_nve_manifold_rattle.h +++ b/src/MANIFOLD/fix_nve_manifold_rattle.h @@ -75,7 +75,7 @@ class FixNVEManifoldRattle : public Fix { void init() override; void reset_dt() override; void end_of_step() override; - int dof(int) override; + bigint dof(int) override; void setup(int) override {} // Not needed for fixNVE but is for fixNVT double memory_usage() override; diff --git a/src/POEMS/fix_poems.cpp b/src/POEMS/fix_poems.cpp index f289a939e6..55199a7191 100644 --- a/src/POEMS/fix_poems.cpp +++ b/src/POEMS/fix_poems.cpp @@ -855,7 +855,7 @@ void FixPOEMS::pre_neighbor() {} count # of degrees-of-freedom removed by fix_poems for atoms in igroup ------------------------------------------------------------------------- */ -int FixPOEMS::dof(int igroup) +bigint FixPOEMS::dof(int igroup) { int groupbit = group->bitmask[igroup]; @@ -877,17 +877,17 @@ int FixPOEMS::dof(int igroup) // remove 3N - 6 dof for each rigid body if at least 2 atoms are in igroup - int n = 0; + bigint n = 0; for (int ibody = 0; ibody < nbody; ibody++) if (nall[ibody] > 2) n += 3 * nall[ibody] - 6; // subtract 3 additional dof for each joint if atom is also in igroup - int m = 0; + bigint m = 0; for (int i = 0; i < nlocal; i++) if (natom2body[i] > 1 && (mask[i] & groupbit)) m += 3 * (natom2body[i] - 1); - int mall; - MPI_Allreduce(&m, &mall, 1, MPI_INT, MPI_SUM, world); + bigint mall; + MPI_Allreduce(&m, &mall, 1, MPI_LMP_BIGINT, MPI_SUM, world); n += mall; // delete local memory diff --git a/src/POEMS/fix_poems.h b/src/POEMS/fix_poems.h index 99af171636..6aac4abd8a 100644 --- a/src/POEMS/fix_poems.h +++ b/src/POEMS/fix_poems.h @@ -47,7 +47,7 @@ class FixPOEMS : public Fix { double memory_usage() override; void pre_neighbor() override; - int dof(int) override; + bigint dof(int) override; void deform(int) override; int modify_param(int, char **) override; void reset_dt() override; diff --git a/src/RIGID/fix_rigid.cpp b/src/RIGID/fix_rigid.cpp index 628abb240e..bd3c53e3ec 100644 --- a/src/RIGID/fix_rigid.cpp +++ b/src/RIGID/fix_rigid.cpp @@ -1247,7 +1247,7 @@ void FixRigid::enforce2d() return total count of DOF ------------------------------------------------------------------------- */ -int FixRigid::dof(int tgroup) +bigint FixRigid::dof(int tgroup) { // cannot count DOF correctly unless setup_bodies_static() has been called @@ -1306,7 +1306,7 @@ int FixRigid::dof(int tgroup) // 3d body with any finite-size M should have 6 dof, remove (3N+6M) - 6 // 2d body with any finite-size M should have 3 dof, remove (2N+3M) - 3 - int n = 0; + bigint n = 0; nlinear = 0; if (domain->dimension == 3) { for (int ibody = 0; ibody < nbody; ibody++) diff --git a/src/RIGID/fix_rigid.h b/src/RIGID/fix_rigid.h index 361ddd2720..c2f04ecf1a 100644 --- a/src/RIGID/fix_rigid.h +++ b/src/RIGID/fix_rigid.h @@ -48,7 +48,7 @@ class FixRigid : public Fix { void setup_pre_neighbor() override; void pre_neighbor() override; - int dof(int) override; + bigint dof(int) override; void deform(int) override; void reset_dt() override; void zero_momentum() override; diff --git a/src/RIGID/fix_rigid_small.cpp b/src/RIGID/fix_rigid_small.cpp index bd49834f15..5905e44595 100644 --- a/src/RIGID/fix_rigid_small.cpp +++ b/src/RIGID/fix_rigid_small.cpp @@ -1123,7 +1123,7 @@ void FixRigidSmall::enforce2d() return total count of DOF ------------------------------------------------------------------------- */ -int FixRigidSmall::dof(int tgroup) +bigint FixRigidSmall::dof(int tgroup) { int i,j; @@ -1195,7 +1195,7 @@ int FixRigidSmall::dof(int tgroup) double *inertia; - int n = 0; + bigint n = 0; nlinear = 0; if (domain->dimension == 3) { for (int ibody = 0; ibody < nlocal_body; ibody++) { @@ -1216,8 +1216,8 @@ int FixRigidSmall::dof(int tgroup) memory->destroy(counts); - int nall; - MPI_Allreduce(&n,&nall,1,MPI_INT,MPI_SUM,world); + bigint nall; + MPI_Allreduce(&n,&nall,1,MPI_LMP_BIGINT,MPI_SUM,world); return nall; } diff --git a/src/RIGID/fix_rigid_small.h b/src/RIGID/fix_rigid_small.h index 0070d976df..0508063f05 100644 --- a/src/RIGID/fix_rigid_small.h +++ b/src/RIGID/fix_rigid_small.h @@ -54,7 +54,7 @@ class FixRigidSmall : public Fix { void setup_pre_neighbor() override; void pre_neighbor() override; - int dof(int) override; + bigint dof(int) override; void deform(int) override; void reset_dt() override; void zero_momentum() override; diff --git a/src/RIGID/fix_shake.cpp b/src/RIGID/fix_shake.cpp index b2c65220bc..540ad35a26 100644 --- a/src/RIGID/fix_shake.cpp +++ b/src/RIGID/fix_shake.cpp @@ -755,7 +755,7 @@ void FixShake::min_post_force(int vflag) count # of degrees-of-freedom removed by SHAKE for atoms in igroup ------------------------------------------------------------------------- */ -int FixShake::dof(int igroup) +bigint FixShake::dof(int igroup) { int groupbit = group->bitmask[igroup]; @@ -766,7 +766,7 @@ int FixShake::dof(int igroup) // count dof in a cluster if and only if // the central atom is in group and atom i is the central atom - int n = 0; + bigint n = 0; for (int i = 0; i < nlocal; i++) { if (!(mask[i] & groupbit)) continue; if (shake_flag[i] == 0) continue; @@ -777,8 +777,8 @@ int FixShake::dof(int igroup) else if (shake_flag[i] == 4) n += 3; } - int nall; - MPI_Allreduce(&n,&nall,1,MPI_INT,MPI_SUM,world); + bigint nall; + MPI_Allreduce(&n,&nall,1,MPI_LMP_BIGINT,MPI_SUM,world); return nall; } diff --git a/src/RIGID/fix_shake.h b/src/RIGID/fix_shake.h index 3b04560f09..b728c9d6a5 100644 --- a/src/RIGID/fix_shake.h +++ b/src/RIGID/fix_shake.h @@ -59,7 +59,7 @@ class FixShake : public Fix { virtual void correct_coordinates(int vflag); virtual void correct_velocities(); - int dof(int) override; + bigint dof(int) override; void reset_dt() override; void *extract(const char *, int &) override; double compute_scalar() override; diff --git a/src/compute.cpp b/src/compute.cpp index 2bd1544fd7..d47d1d5292 100644 --- a/src/compute.cpp +++ b/src/compute.cpp @@ -83,7 +83,7 @@ Compute::Compute(LAMMPS *lmp, int narg, char **arg) : extra_dof = domain->dimension; dynamic_user = 0; - fix_dof = 0; + fix_dof = 0.0; // setup list of timesteps diff --git a/src/fix.h b/src/fix.h index 9b595f0c60..5fde4fd8e2 100644 --- a/src/fix.h +++ b/src/fix.h @@ -236,7 +236,7 @@ class Fix : protected Pointers { virtual double compute_vector(int) { return 0.0; } virtual double compute_array(int, int) { return 0.0; } - virtual int dof(int) { return 0; } + virtual bigint dof(int) { return 0; } virtual void deform(int) {} virtual void reset_target(double) {} virtual void reset_dt() {} From 0cfb6a058cdfeeab9c6422e18ccb4bc7bfd00bae Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 10 Jan 2024 16:42:21 -0500 Subject: [PATCH 173/189] make SHAKE stats output 64-bit compatible --- src/RIGID/fix_shake.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/RIGID/fix_shake.cpp b/src/RIGID/fix_shake.cpp index 540ad35a26..99cdc3aa7f 100644 --- a/src/RIGID/fix_shake.cpp +++ b/src/RIGID/fix_shake.cpp @@ -1098,7 +1098,7 @@ void FixShake::find_clusters() // print info on SHAKE clusters // ----------------------------------------------------- - int count1,count2,count3,count4; + bigint count1,count2,count3,count4; count1 = count2 = count3 = count4 = 0; for (i = 0; i < nlocal; i++) { if (shake_flag[i] == 1) count1++; @@ -1107,15 +1107,15 @@ void FixShake::find_clusters() else if (shake_flag[i] == 4) count4++; } - int tmp; + bigint tmp; tmp = count1; - MPI_Allreduce(&tmp,&count1,1,MPI_INT,MPI_SUM,world); + MPI_Allreduce(&tmp,&count1,1,MPI_LMP_BIGINT,MPI_SUM,world); tmp = count2; - MPI_Allreduce(&tmp,&count2,1,MPI_INT,MPI_SUM,world); + MPI_Allreduce(&tmp,&count2,1,MPI_LMP_BIGINT,MPI_SUM,world); tmp = count3; - MPI_Allreduce(&tmp,&count3,1,MPI_INT,MPI_SUM,world); + MPI_Allreduce(&tmp,&count3,1,MPI_LMP_BIGINT,MPI_SUM,world); tmp = count4; - MPI_Allreduce(&tmp,&count4,1,MPI_INT,MPI_SUM,world); + MPI_Allreduce(&tmp,&count4,1,MPI_LMP_BIGINT,MPI_SUM,world); if (comm->me == 0) { utils::logmesg(lmp,"{:>8} = # of size 2 clusters\n" From 4bc77bc4ab7e4b976d7e627a4c371fdd71e7bd2b Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Wed, 10 Jan 2024 16:23:58 -0700 Subject: [PATCH 174/189] Fix potential integer overflow in PPPM --- src/KSPACE/pppm.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/KSPACE/pppm.cpp b/src/KSPACE/pppm.cpp index 2f5b4fc670..328c2c2362 100644 --- a/src/KSPACE/pppm.cpp +++ b/src/KSPACE/pppm.cpp @@ -1944,7 +1944,8 @@ void PPPM::poisson_ik() // global energy and virial contribution - double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); + bigint ngridtotal = (bigint) nx_pppm * ny_pppm * nz_pppm; + double scaleinv = 1.0/(ngridtotal); double s2 = scaleinv*scaleinv; if (eflag_global || vflag_global) { @@ -2145,7 +2146,8 @@ void PPPM::poisson_ad() // global energy and virial contribution - double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); + bigint ngridtotal = (bigint) nx_pppm * ny_pppm * nz_pppm; + double scaleinv = 1.0/(ngridtotal); double s2 = scaleinv*scaleinv; if (eflag_global || vflag_global) { @@ -3259,7 +3261,8 @@ void PPPM::poisson_groups(int AA_flag) // keep everything in reciprocal space so // no inverse FFTs needed - double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); + bigint ngridtotal = (bigint) nx_pppm * ny_pppm * nz_pppm; + double scaleinv = 1.0/(ngridtotal); double s2 = scaleinv*scaleinv; // energy From f6649762e2d2707575f6ac0cff9a0b31f6ac90aa Mon Sep 17 00:00:00 2001 From: jtclemm Date: Wed, 10 Jan 2024 16:58:29 -0700 Subject: [PATCH 175/189] Moving files to BPM dir, skipping copy lists --- src/.gitignore | 4 ++++ src/{ => BPM}/fix_bond_history.cpp | 0 src/{ => BPM}/fix_bond_history.h | 0 src/{ => BPM}/fix_update_special_bonds.cpp | 4 ++++ src/{ => BPM}/fix_update_special_bonds.h | 0 5 files changed, 8 insertions(+) rename src/{ => BPM}/fix_bond_history.cpp (100%) rename src/{ => BPM}/fix_bond_history.h (100%) rename src/{ => BPM}/fix_update_special_bonds.cpp (99%) rename src/{ => BPM}/fix_update_special_bonds.h (100%) diff --git a/src/.gitignore b/src/.gitignore index e2c9fa6d5b..09f7ff8608 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -344,8 +344,12 @@ /bond_bpm_spring.h /compute_nbond_atom.cpp /compute_nbond_atom.h +/fix_bond_history.cpp +/fix_bond_history.h /fix_nve_bpm_sphere.cpp /fix_nve_bpm_sphere.h +/fix_update_special_bonds.cpp +/fix_update_special_bonds.h /pair_bpm_spring.cpp /pair_bpm_spring.h diff --git a/src/fix_bond_history.cpp b/src/BPM/fix_bond_history.cpp similarity index 100% rename from src/fix_bond_history.cpp rename to src/BPM/fix_bond_history.cpp diff --git a/src/fix_bond_history.h b/src/BPM/fix_bond_history.h similarity index 100% rename from src/fix_bond_history.h rename to src/BPM/fix_bond_history.h diff --git a/src/fix_update_special_bonds.cpp b/src/BPM/fix_update_special_bonds.cpp similarity index 99% rename from src/fix_update_special_bonds.cpp rename to src/BPM/fix_update_special_bonds.cpp index 4e8cba47ec..ed323d92e2 100644 --- a/src/fix_update_special_bonds.cpp +++ b/src/BPM/fix_update_special_bonds.cpp @@ -166,6 +166,10 @@ void FixUpdateSpecialBonds::pre_force(int /*vflag*/) for (int ilist = 0; ilist < neighbor->nlist; ilist ++) { list = neighbor->lists[ilist]; + + // Skip copied lists, will update original + if (list->copy) continue; + numneigh = list->numneigh; firstneigh = list->firstneigh; diff --git a/src/fix_update_special_bonds.h b/src/BPM/fix_update_special_bonds.h similarity index 100% rename from src/fix_update_special_bonds.h rename to src/BPM/fix_update_special_bonds.h From 768163865e3574c4d73e892977b91aeaad24e77d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 10 Jan 2024 19:54:29 -0500 Subject: [PATCH 176/189] avoid 32-bit integer overflow for PPPM grid --- src/ELECTRODE/pppm_electrode.cpp | 6 ++++-- src/GPU/pppm_gpu.cpp | 3 ++- src/INTEL/pppm_electrode_intel.cpp | 4 +++- src/KOKKOS/pppm_kokkos.cpp | 3 ++- src/KSPACE/pppm.cpp | 8 ++++---- src/KSPACE/pppm_dipole.cpp | 3 ++- src/KSPACE/pppm_disp.cpp | 18 ++++++++++++------ src/KSPACE/pppm_stagger.cpp | 4 ++-- 8 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/ELECTRODE/pppm_electrode.cpp b/src/ELECTRODE/pppm_electrode.cpp index 6ede0f1f4d..0ae3da6863 100644 --- a/src/ELECTRODE/pppm_electrode.cpp +++ b/src/ELECTRODE/pppm_electrode.cpp @@ -633,7 +633,9 @@ void PPPMElectrode::project_psi(double *vec, int sensor_grpbit) // project u_brick with weight matrix double **x = atom->x; int *mask = atom->mask; - double const scaleinv = 1.0 / (nx_pppm * ny_pppm * nz_pppm); + const bigint ngridtotal = (bigint) nx_pppm * ny_pppm * nz_pppm; + const double scaleinv = 1.0 / ngridtotal; + for (int i = 0; i < atom->nlocal; i++) { if (!(mask[i] & sensor_grpbit)) continue; double v = 0.; @@ -1362,7 +1364,7 @@ double PPPMElectrode::compute_qopt() // each proc calculates contributions from every Pth grid point bigint ngridtotal = (bigint) nx_pppm * ny_pppm * nz_pppm; - int nxy_pppm = nx_pppm * ny_pppm; + bigint nxy_pppm = (bigint) nx_pppm * ny_pppm; double qopt = 0.0; diff --git a/src/GPU/pppm_gpu.cpp b/src/GPU/pppm_gpu.cpp index a2c733e7ed..1959f00865 100644 --- a/src/GPU/pppm_gpu.cpp +++ b/src/GPU/pppm_gpu.cpp @@ -405,7 +405,8 @@ void PPPMGPU::poisson_ik() // if requested, compute energy and virial contribution - double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); + bigint ngridtotal = (bigint) nx_pppm * ny_pppm * nz_pppm; + double scaleinv = 1.0 / ngridtotal; double s2 = scaleinv*scaleinv; if (eflag_global || vflag_global) { diff --git a/src/INTEL/pppm_electrode_intel.cpp b/src/INTEL/pppm_electrode_intel.cpp index 5cb62dc5d2..4d8a0331b8 100644 --- a/src/INTEL/pppm_electrode_intel.cpp +++ b/src/INTEL/pppm_electrode_intel.cpp @@ -420,7 +420,9 @@ void PPPMElectrodeIntel::project_psi(IntelBuffers *buffers, double #endif { int *mask = atom->mask; - const flt_t scaleinv = 1.0 / (nx_pppm * ny_pppm * nz_pppm); + + const bigint ngridtotal = (bigint) nx_pppm * ny_pppm * nz_pppm; + const flt_t scaleinv = 1.0 / ngridtotal; const flt_t lo0 = boxlo[0]; const flt_t lo1 = boxlo[1]; diff --git a/src/KOKKOS/pppm_kokkos.cpp b/src/KOKKOS/pppm_kokkos.cpp index 912ae36f6f..fd84dc7df8 100644 --- a/src/KOKKOS/pppm_kokkos.cpp +++ b/src/KOKKOS/pppm_kokkos.cpp @@ -1383,7 +1383,8 @@ void PPPMKokkos::poisson_ik() // global energy and virial contribution - scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); + bigint ngridtotal = (bigint) nx_pppm * ny_pppm * nz_pppm; + scaleinv = 1.0/ngridtotal; s2 = scaleinv*scaleinv; if (eflag_global || vflag_global) { diff --git a/src/KSPACE/pppm.cpp b/src/KSPACE/pppm.cpp index 328c2c2362..ac516ff18c 100644 --- a/src/KSPACE/pppm.cpp +++ b/src/KSPACE/pppm.cpp @@ -1188,7 +1188,7 @@ double PPPM::compute_qopt() // each proc calculates contributions from every Pth grid point bigint ngridtotal = (bigint) nx_pppm * ny_pppm * nz_pppm; - int nxy_pppm = nx_pppm * ny_pppm; + bigint nxy_pppm = (bigint) nx_pppm * ny_pppm; double qopt = 0.0; @@ -1945,7 +1945,7 @@ void PPPM::poisson_ik() // global energy and virial contribution bigint ngridtotal = (bigint) nx_pppm * ny_pppm * nz_pppm; - double scaleinv = 1.0/(ngridtotal); + double scaleinv = 1.0/ngridtotal; double s2 = scaleinv*scaleinv; if (eflag_global || vflag_global) { @@ -2147,7 +2147,7 @@ void PPPM::poisson_ad() // global energy and virial contribution bigint ngridtotal = (bigint) nx_pppm * ny_pppm * nz_pppm; - double scaleinv = 1.0/(ngridtotal); + double scaleinv = 1.0/ngridtotal; double s2 = scaleinv*scaleinv; if (eflag_global || vflag_global) { @@ -3262,7 +3262,7 @@ void PPPM::poisson_groups(int AA_flag) // no inverse FFTs needed bigint ngridtotal = (bigint) nx_pppm * ny_pppm * nz_pppm; - double scaleinv = 1.0/(ngridtotal); + double scaleinv = 1.0/ngridtotal; double s2 = scaleinv*scaleinv; // energy diff --git a/src/KSPACE/pppm_dipole.cpp b/src/KSPACE/pppm_dipole.cpp index a01ffea1dc..e0d13f2b9a 100644 --- a/src/KSPACE/pppm_dipole.cpp +++ b/src/KSPACE/pppm_dipole.cpp @@ -1338,7 +1338,8 @@ void PPPMDipole::poisson_ik_dipole() // global energy and virial contribution - double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); + bigint ngridtotal = (bigint) nx_pppm * ny_pppm * nz_pppm; + double scaleinv = 1.0/ngridtotal; double s2 = scaleinv*scaleinv; if (eflag_global || vflag_global) { diff --git a/src/KSPACE/pppm_disp.cpp b/src/KSPACE/pppm_disp.cpp index 72424a7330..94fa40b7f2 100644 --- a/src/KSPACE/pppm_disp.cpp +++ b/src/KSPACE/pppm_disp.cpp @@ -4556,7 +4556,8 @@ void PPPMDisp::poisson_ik(FFT_SCALAR* wk1, FFT_SCALAR* wk2, // if requested, compute energy and virial contribution - double scaleinv = 1.0/(nx_p*ny_p*nz_p); + bigint ngridtotal = (bigint) nx_p * ny_p * nz_p; + double scaleinv = 1.0/ngridtotal; double s2 = scaleinv*scaleinv; if (eflag_global || vflag_global) { @@ -4696,7 +4697,8 @@ void PPPMDisp::poisson_ad(FFT_SCALAR* wk1, FFT_SCALAR* wk2, // if requested, compute energy and virial contribution - double scaleinv = 1.0/(nx_p*ny_p*nz_p); + bigint ngridtotal = (bigint) nx_p * ny_p * nz_p; + double scaleinv = 1.0/ngridtotal; double s2 = scaleinv*scaleinv; if (eflag_global || vflag_global) { @@ -4844,7 +4846,8 @@ poisson_2s_ik(FFT_SCALAR* dfft_1, FFT_SCALAR* dfft_2, int i,j,k,n; double eng; - double scaleinv = 1.0/(nx_pppm_6*ny_pppm_6*nz_pppm_6); + bigint ngridtotal = (bigint) nx_pppm_6 * ny_pppm_6 * nz_pppm_6; + double scaleinv = 1.0/ngridtotal; // transform charge/dispersion density (r -> k) // only one transform when energies and pressures not calculated @@ -5017,7 +5020,8 @@ poisson_none_ik(int n1, int n2,FFT_SCALAR* dfft_1, FFT_SCALAR* dfft_2, int i,j,k,n; double eng; - double scaleinv = 1.0/(nx_pppm_6*ny_pppm_6*nz_pppm_6); + bigint ngridtotal = (bigint) nx_pppm_6 * ny_pppm_6 * nz_pppm_6; + double scaleinv = 1.0/ngridtotal; // transform charge/dispersion density (r -> k) // only one transform required when energies and pressures not needed @@ -5191,7 +5195,8 @@ poisson_2s_ad(FFT_SCALAR* dfft_1, FFT_SCALAR* dfft_2, int i,j,k,n; double eng; - double scaleinv = 1.0/(nx_pppm_6*ny_pppm_6*nz_pppm_6); + bigint ngridtotal = (bigint) nx_pppm_6 * ny_pppm_6 * nz_pppm_6; + double scaleinv = 1.0/ngridtotal; // transform charge/dispersion density (r -> k) // only one tansform required when energies and pressures not needed @@ -5289,7 +5294,8 @@ poisson_none_ad(int n1, int n2, FFT_SCALAR* dfft_1, FFT_SCALAR* dfft_2, int i,j,k,n; double eng; - double scaleinv = 1.0/(nx_pppm_6*ny_pppm_6*nz_pppm_6); + bigint ngridtotal = (bigint) nx_pppm_6 * ny_pppm_6 * nz_pppm_6; + double scaleinv = 1.0/ngridtotal; // transform charge/dispersion density (r -> k) // only one tansform required when energies and pressures not needed diff --git a/src/KSPACE/pppm_stagger.cpp b/src/KSPACE/pppm_stagger.cpp index d6f3c9cac6..d44f2428c8 100644 --- a/src/KSPACE/pppm_stagger.cpp +++ b/src/KSPACE/pppm_stagger.cpp @@ -302,7 +302,7 @@ double PPPMStagger::compute_qopt() // each proc calculates contributions from every Pth grid point bigint ngridtotal = (bigint) nx_pppm * ny_pppm * nz_pppm; - int nxy_pppm = nx_pppm * ny_pppm; + bigint nxy_pppm = (bigint) nx_pppm * ny_pppm; double qopt = 0.0; @@ -398,7 +398,7 @@ double PPPMStagger::compute_qopt_ad() // each proc calculates contributions from every Pth grid point bigint ngridtotal = (bigint) nx_pppm * ny_pppm * nz_pppm; - int nxy_pppm = nx_pppm * ny_pppm; + bigint nxy_pppm = (bigint) nx_pppm * ny_pppm; double qopt = 0.0; From c088681bf06d3e189a70455f91aa9c1cdddf6098 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 10 Jan 2024 20:04:47 -0500 Subject: [PATCH 177/189] more large system shake stats fixes --- src/RIGID/fix_shake.cpp | 12 ++++++------ src/RIGID/fix_shake.h | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/RIGID/fix_shake.cpp b/src/RIGID/fix_shake.cpp index 99cdc3aa7f..15bd5d207f 100644 --- a/src/RIGID/fix_shake.cpp +++ b/src/RIGID/fix_shake.cpp @@ -207,8 +207,8 @@ FixShake::FixShake(LAMMPS *lmp, int narg, char **arg) : if (output_every) { int nb = atom->nbondtypes + 1; - b_count = new int[nb]; - b_count_all = new int[nb]; + b_count = new bigint[nb]; + b_count_all = new bigint[nb]; b_ave = new double[nb]; b_ave_all = new double[nb]; b_max = new double[nb]; @@ -217,8 +217,8 @@ FixShake::FixShake(LAMMPS *lmp, int narg, char **arg) : b_min_all = new double[nb]; int na = atom->nangletypes + 1; - a_count = new int[na]; - a_count_all = new int[na]; + a_count = new bigint[na]; + a_count_all = new bigint[na]; a_ave = new double[na]; a_ave_all = new double[na]; a_max = new double[na]; @@ -2682,12 +2682,12 @@ void FixShake::stats() // sum across all procs - MPI_Allreduce(b_count,b_count_all,nb,MPI_INT,MPI_SUM,world); + MPI_Allreduce(b_count,b_count_all,nb,MPI_LMP_BIGINT,MPI_SUM,world); MPI_Allreduce(b_ave,b_ave_all,nb,MPI_DOUBLE,MPI_SUM,world); MPI_Allreduce(b_max,b_max_all,nb,MPI_DOUBLE,MPI_MAX,world); MPI_Allreduce(b_min,b_min_all,nb,MPI_DOUBLE,MPI_MIN,world); - MPI_Allreduce(a_count,a_count_all,na,MPI_INT,MPI_SUM,world); + MPI_Allreduce(a_count,a_count_all,na,MPI_LMP_BIGINT,MPI_SUM,world); MPI_Allreduce(a_ave,a_ave_all,na,MPI_DOUBLE,MPI_SUM,world); MPI_Allreduce(a_max,a_max_all,na,MPI_DOUBLE,MPI_MAX,world); MPI_Allreduce(a_min,a_min_all,na,MPI_DOUBLE,MPI_MIN,world); diff --git a/src/RIGID/fix_shake.h b/src/RIGID/fix_shake.h index b728c9d6a5..d02fdd784a 100644 --- a/src/RIGID/fix_shake.h +++ b/src/RIGID/fix_shake.h @@ -117,10 +117,10 @@ class FixShake : public Fix { int nlist, maxlist; // size and max-size of list // stat quantities - int *b_count, *b_count_all; // counts for each bond type, atoms in bond cluster + bigint *b_count, *b_count_all; // counts for each bond type, atoms in bond cluster double *b_ave, *b_max, *b_min; // ave/max/min dist for each bond type double *b_ave_all, *b_max_all, *b_min_all; // MPI summing arrays - int *a_count, *a_count_all; // ditto for angle types + bigint *a_count, *a_count_all; // ditto for angle types double *a_ave, *a_max, *a_min; double *a_ave_all, *a_max_all, *a_min_all; From 62cf534de0e191fc7090b9ce7ce0b5a51141608b Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 10 Jan 2024 20:07:01 -0500 Subject: [PATCH 178/189] whitespace --- src/KSPACE/pppm_disp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/KSPACE/pppm_disp.cpp b/src/KSPACE/pppm_disp.cpp index 94fa40b7f2..a738db98d2 100644 --- a/src/KSPACE/pppm_disp.cpp +++ b/src/KSPACE/pppm_disp.cpp @@ -4697,7 +4697,7 @@ void PPPMDisp::poisson_ad(FFT_SCALAR* wk1, FFT_SCALAR* wk2, // if requested, compute energy and virial contribution - bigint ngridtotal = (bigint) nx_p * ny_p * nz_p; + bigint ngridtotal = (bigint) nx_p * ny_p * nz_p; double scaleinv = 1.0/ngridtotal; double s2 = scaleinv*scaleinv; From 4d74c623258f07a64cce0dbb4c31c09c99089b5d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 10 Jan 2024 20:17:17 -0500 Subject: [PATCH 179/189] silence some more warnings --- src/KOKKOS/atom_vec_dpd_kokkos.cpp | 11 +++++------ src/KOKKOS/pppm_kokkos.cpp | 4 +--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/KOKKOS/atom_vec_dpd_kokkos.cpp b/src/KOKKOS/atom_vec_dpd_kokkos.cpp index c3430b9f6e..70aedcc931 100644 --- a/src/KOKKOS/atom_vec_dpd_kokkos.cpp +++ b/src/KOKKOS/atom_vec_dpd_kokkos.cpp @@ -746,7 +746,6 @@ struct AtomVecDPDKokkos_PackExchangeFunctor { const typename AT::tdual_xfloat_2d buf, typename AT::tdual_int_1d sendlist, typename AT::tdual_int_1d copylist): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), @@ -772,7 +771,8 @@ struct AtomVecDPDKokkos_PackExchangeFunctor { _uCGw(atom->k_uCG.view()), _uCGneww(atom->k_uCGnew.view()), _sendlist(sendlist.template view()), - _copylist(copylist.template view()) { + _copylist(copylist.template view()), + _size_exchange(atom->avecKK->size_exchange) { const int maxsendlist = (buf.template view().extent(0)*buf.template view().extent(1))/_size_exchange; buffer_view(_buf,buf,maxsendlist,_size_exchange); @@ -875,15 +875,14 @@ struct AtomVecDPDKokkos_UnpackExchangeFunctor { const typename AT::tdual_xfloat_2d buf, typename AT::tdual_int_1d nlocal, int dim, X_FLOAT lo, X_FLOAT hi): - _size_exchange(atom->avecKK->size_exchange), _x(atom->k_x.view()), _v(atom->k_v.view()), _tag(atom->k_tag.view()), _type(atom->k_type.view()), _mask(atom->k_mask.view()), _image(atom->k_image.view()), - _nlocal(nlocal.template view()),_dim(dim), - _lo(lo),_hi(hi) { + _nlocal(nlocal.template view()), + _dim(dim),_lo(lo),_hi(hi),_size_exchange(atom->avecKK->size_exchange) { const int maxsendlist = (buf.template view().extent(0)*buf.template view().extent(1))/_size_exchange; buffer_view(_buf,buf,maxsendlist,_size_exchange); @@ -917,7 +916,7 @@ struct AtomVecDPDKokkos_UnpackExchangeFunctor { /* ---------------------------------------------------------------------- */ int AtomVecDPDKokkos::unpack_exchange_kokkos(DAT::tdual_xfloat_2d &k_buf, int nrecv, int nlocal, int dim, X_FLOAT lo, X_FLOAT hi, ExecutionSpace space, - DAT::tdual_int_1d &k_indices) + DAT::tdual_int_1d &/*k_indices*/) { while (nlocal + nrecv/size_exchange >= nmax) grow(0); diff --git a/src/KOKKOS/pppm_kokkos.cpp b/src/KOKKOS/pppm_kokkos.cpp index 912ae36f6f..beb49639ae 100644 --- a/src/KOKKOS/pppm_kokkos.cpp +++ b/src/KOKKOS/pppm_kokkos.cpp @@ -1371,8 +1371,6 @@ void PPPMKokkos::operator()(TagPPPM_brick2fft, const int &ii) const template void PPPMKokkos::poisson_ik() { - int j; - // transform charge density (r -> k) copymode = 1; @@ -1392,7 +1390,7 @@ void PPPMKokkos::poisson_ik() copymode = 1; Kokkos::parallel_reduce(Kokkos::RangePolicy(0,nfft),*this,ev); copymode = 0; - for (j = 0; j < 6; j++) virial[j] += ev.v[j]; + for (int j = 0; j < 6; j++) virial[j] += ev.v[j]; energy += ev.ecoul; } else { copymode = 1; From 4bda4621bbc9b86ab02a50b8bdcf6a2798208e8f Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Jan 2024 11:12:27 -0500 Subject: [PATCH 180/189] check whether total number of replica is too large for 32-bit int --- src/replicate.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/replicate.cpp b/src/replicate.cpp index f5444b9fa8..e0fd5b0e3d 100644 --- a/src/replicate.cpp +++ b/src/replicate.cpp @@ -57,7 +57,12 @@ void Replicate::command(int narg, char **arg) error->all(FLERR, "Illegal replication grid {}x{}x{}. All replications must be > 0", nx, ny, nz); - int nrep = nx*ny*nz; + bigint nrepbig = (bigint) nx * ny * nz; + if (nrepbig > MAXSMALLINT) + error->all(FLERR, "Total # of replica is too large: {}x{}x{} = {}. " + "Please use replicate multiple times", nx, ny, nz, nrepbig); + + int nrep = (int) nrepbig; if (me == 0) utils::logmesg(lmp, "Replication is creating a {}x{}x{} = {} times larger system...\n", nx, ny, nz, nrep); From b5dbc4ebf67dad505450c24dc342635428338bd6 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Thu, 11 Jan 2024 09:29:54 -0700 Subject: [PATCH 181/189] Add missing dependency --- src/Depend.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Depend.sh b/src/Depend.sh index dbffb2dba0..3df1347e67 100755 --- a/src/Depend.sh +++ b/src/Depend.sh @@ -99,6 +99,7 @@ fi if (test $1 = "EXTRA-PAIR") then depend GPU + depend KOKKOS depend OPENMP fi From 22d50b32e7493d4a66862bdbfb70b4c553001ea9 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Thu, 11 Jan 2024 15:40:45 -0700 Subject: [PATCH 182/189] Fix more warnings and remove deprecated Kokkos code --- src/EXTRA-COMPUTE/compute_rattlers_atom.cpp | 1 - src/KOKKOS/atom_kokkos.cpp | 4 ++- src/KOKKOS/atom_kokkos.h | 3 +- src/KOKKOS/atom_map_kokkos.cpp | 32 ++++++++++----------- src/KOKKOS/kokkos_type.h | 7 ----- 5 files changed, 21 insertions(+), 26 deletions(-) diff --git a/src/EXTRA-COMPUTE/compute_rattlers_atom.cpp b/src/EXTRA-COMPUTE/compute_rattlers_atom.cpp index 602923b58a..9dacf14171 100644 --- a/src/EXTRA-COMPUTE/compute_rattlers_atom.cpp +++ b/src/EXTRA-COMPUTE/compute_rattlers_atom.cpp @@ -144,7 +144,6 @@ void ComputeRattlersAtom::compute_peratom() numneigh = list->numneigh; firstneigh = list->firstneigh; - Pair *pair = force->pair; double **cutsq = force->pair->cutsq; int change_flag = 1; diff --git a/src/KOKKOS/atom_kokkos.cpp b/src/KOKKOS/atom_kokkos.cpp index 3db8adb5ea..501b719ad4 100644 --- a/src/KOKKOS/atom_kokkos.cpp +++ b/src/KOKKOS/atom_kokkos.cpp @@ -31,7 +31,9 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -AtomKokkos::AtomKokkos(LAMMPS *lmp) : Atom(lmp) +AtomKokkos::AtomKokkos(LAMMPS *lmp) : Atom(lmp), +mapBinner(1, 0.0, 1.0), // no default constructor, these values are not used +mapSorter(d_tag_sorted, 0, 1, mapBinner, true) { avecKK = nullptr; diff --git a/src/KOKKOS/atom_kokkos.h b/src/KOKKOS/atom_kokkos.h index 6a3036375d..e6269b5527 100644 --- a/src/KOKKOS/atom_kokkos.h +++ b/src/KOKKOS/atom_kokkos.h @@ -103,7 +103,8 @@ class AtomKokkos : public Atom { using MapKeyViewType = decltype(d_tag_sorted); using BinOpMap = Kokkos::BinOp1D; - Kokkos::BinSort Sorter; + BinOpMap mapBinner; + Kokkos::BinSort mapSorter; class AtomVecKokkos* avecKK; diff --git a/src/KOKKOS/atom_map_kokkos.cpp b/src/KOKKOS/atom_map_kokkos.cpp index b19f87cb3b..c843d88d56 100644 --- a/src/KOKKOS/atom_map_kokkos.cpp +++ b/src/KOKKOS/atom_map_kokkos.cpp @@ -146,7 +146,7 @@ void AtomKokkos::map_set() unsigned int nmax = atom->nmax; int realloc_flag = 0; - if (d_tag_sorted.extent(0) < nmax) { + if (!d_tag_sorted.data() || d_tag_sorted.extent(0) < nmax) { MemKK::realloc_kokkos(d_tag_sorted,"atom:tag_sorted",nmax); MemKK::realloc_kokkos(d_i_sorted,"atom:i_sorted",nmax); realloc_flag = 1; @@ -179,25 +179,25 @@ void AtomKokkos::map_set() using MapKeyViewType = decltype(d_tag_sorted); using BinOpMap = Kokkos::BinOp1D; - auto binner = BinOpMap(nall, min, max); + mapBinner = BinOpMap(nall, min, max); - if (!Sorter.bin_offsets.data() || realloc_flag) { - Sorter = Kokkos::BinSort(d_tag_sorted, 0, nall, binner, true); - MemKK::realloc_kokkos(Sorter.bin_count_atomic,"Kokkos::SortImpl::BinSortFunctor::bin_count",nmax+1); - Kokkos::deep_copy(Sorter.bin_count_atomic,0); - Sorter.bin_count_const = Sorter.bin_count_atomic; - MemKK::realloc_kokkos(Sorter.bin_offsets,"Kokkos::SortImpl::BinSortFunctor::bin_offsets",nmax+1); - MemKK::realloc_kokkos(Sorter.sort_order,"Kokkos::SortImpl::BinSortFunctor::sort_order",nmax); + if (realloc_flag) { + mapSorter = Kokkos::BinSort(d_tag_sorted, 0, nall, mapBinner, true); + MemKK::realloc_kokkos(mapSorter.bin_count_atomic,"Kokkos::SortImpl::BinSortFunctor::bin_count",nmax+1); + Kokkos::deep_copy(mapSorter.bin_count_atomic,0); + mapSorter.bin_count_const = mapSorter.bin_count_atomic; + MemKK::realloc_kokkos(mapSorter.bin_offsets,"Kokkos::SortImpl::BinSortFunctor::bin_offsets",nmax+1); + MemKK::realloc_kokkos(mapSorter.sort_order,"Kokkos::SortImpl::BinSortFunctor::sort_order",nmax); } else { - Kokkos::deep_copy(Sorter.bin_count_atomic,0); - Sorter.bin_op = binner; - Sorter.range_begin = 0; - Sorter.range_end = nall; + Kokkos::deep_copy(mapSorter.bin_count_atomic,0); + mapSorter.bin_op = mapBinner; + mapSorter.range_begin = 0; + mapSorter.range_end = nall; } - Sorter.create_permute_vector(LMPDeviceType()); - Sorter.sort(LMPDeviceType(), d_tag_sorted, 0, nall); - Sorter.sort(LMPDeviceType(), d_i_sorted, 0, nall); + mapSorter.create_permute_vector(LMPDeviceType()); + mapSorter.sort(LMPDeviceType(), d_tag_sorted, 0, nall); + mapSorter.sort(LMPDeviceType(), d_i_sorted, 0, nall); auto d_map_array = k_map_array.d_view; auto d_map_hash = k_map_hash.d_view; diff --git a/src/KOKKOS/kokkos_type.h b/src/KOKKOS/kokkos_type.h index c8ab2198d6..1009e43196 100644 --- a/src/KOKKOS/kokkos_type.h +++ b/src/KOKKOS/kokkos_type.h @@ -453,13 +453,6 @@ struct alignas(2*sizeof(F_FLOAT)) s_FLOAT2 { v[0] = v[1] = 0.0; } - KOKKOS_INLINE_FUNCTION - s_FLOAT2(const s_FLOAT2 & rhs) { - for (int i = 0; i < 2; i++){ - v[i] = rhs.v[i]; - } - } - KOKKOS_INLINE_FUNCTION void operator+=(const s_FLOAT2 &rhs) { v[0] += rhs.v[0]; From 18d140a96ee8da5e96fc01d04e18678e59f31782 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Thu, 11 Jan 2024 15:51:50 -0700 Subject: [PATCH 183/189] Small tweak --- src/KOKKOS/atom_map_kokkos.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/KOKKOS/atom_map_kokkos.cpp b/src/KOKKOS/atom_map_kokkos.cpp index c843d88d56..a266c44a91 100644 --- a/src/KOKKOS/atom_map_kokkos.cpp +++ b/src/KOKKOS/atom_map_kokkos.cpp @@ -143,10 +143,10 @@ void AtomKokkos::map_set() // sort by tag - unsigned int nmax = atom->nmax; + int nmax = atom->nmax; int realloc_flag = 0; - if (!d_tag_sorted.data() || d_tag_sorted.extent(0) < nmax) { + if (!d_tag_sorted.data() || (int)d_tag_sorted.extent(0) < nmax) { MemKK::realloc_kokkos(d_tag_sorted,"atom:tag_sorted",nmax); MemKK::realloc_kokkos(d_i_sorted,"atom:i_sorted",nmax); realloc_flag = 1; From b5aa04c36a87eb6adcba5d35d6d340e9b50767b0 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Jan 2024 18:52:23 -0500 Subject: [PATCH 184/189] remove unused variable --- lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Instance.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Instance.hpp b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Instance.hpp index 03f5fff395..4586406e16 100644 --- a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Instance.hpp +++ b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Instance.hpp @@ -219,8 +219,6 @@ KOKKOS_DEPRECATED void OpenMP::partition_master(F const& f, int num_partitions, Exec::validate_partition_impl(prev_instance->m_pool_size, num_partitions, partition_size); - OpenMP::memory_space space; - #pragma omp parallel num_threads(num_partitions) { Exec thread_local_instance(partition_size); From d4bd385f5e46dd73d867897797d95a4a8c14364f Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Jan 2024 18:52:47 -0500 Subject: [PATCH 185/189] remove unused function parameters and corresponding variables --- src/KOKKOS/pair_reaxff_kokkos.cpp | 16 +++++----------- src/KOKKOS/pair_reaxff_kokkos.h | 2 +- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index 11a40970c2..505681acb3 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -1598,7 +1598,6 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfBlocking< F_FLOAT dDeltap_self_i[3] = {0.0,0.0,0.0}; F_FLOAT total_bo_i = 0.0; - int j_index,i_index; d_bo_first[i] = i*maxbo; const int bo_first_i = d_bo_first[i]; @@ -1675,7 +1674,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfBlocking< int ii_index = -1; int jj_index = -1; - if (build_bo_list(bo_first_i, i, j, i_index, j_index, ii_index, jj_index)) { + if (build_bo_list(bo_first_i, i, j, ii_index, jj_index)) { // from BondOrder1 @@ -1743,7 +1742,6 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfBlockingP F_FLOAT C12, C34, C56, BO_s, BO_pi, BO_pi2, BO, delij[3]; - int j_index,i_index; d_bo_first[i] = i*maxbo; const int bo_first_i = d_bo_first[i]; @@ -1821,7 +1819,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfBlockingP int ii_index = -1; int jj_index = -1; - build_bo_list(bo_first_i, i, j, i_index, j_index, ii_index, jj_index); + build_bo_list(bo_first_i, i, j, ii_index, jj_index); } } } @@ -1842,7 +1840,6 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfPreview::operator()(TagPairReaxBuildListsHalfPreview(bo_first_i, i, j, i_index, j_index, ii_index, jj_index); + build_bo_list(bo_first_i, i, j, ii_index, jj_index); } } @@ -1942,7 +1939,8 @@ void PairReaxFFKokkos::build_hb_list(F_FLOAT rsq, int i, int hb_firs template template KOKKOS_INLINE_FUNCTION -bool PairReaxFFKokkos::build_bo_list(int bo_first_i, int i, int j, int i_index, int j_index, int& ii_index, int& jj_index) const { +bool PairReaxFFKokkos::build_bo_list(int bo_first_i, int i, int j, int& ii_index, int& jj_index) const { + int i_index, j_index; if (NEIGHFLAG == HALF) { j_index = bo_first_i + d_bo_num[i]; @@ -2509,8 +2507,6 @@ void PairReaxFFKokkos::compute_angular_sbo(int i, int itype, int j_s F_FLOAT prod_SBO = 1.0; for (int jj = j_start; jj < j_end; jj++) { - int j = d_bo_list[jj]; - j &= NEIGHMASK; const int j_index = jj - j_start; const F_FLOAT bo_ij = d_BO(i,j_index); @@ -2919,8 +2915,6 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeAngularPreproces a_CdDelta[k] += CEcoa5; for (int ll = j_start; ll < j_end; ll++) { - int l = d_bo_list[ll]; - l &= NEIGHMASK; const int l_index = ll - j_start; temp_bo_jt = d_BO(i,l_index); diff --git a/src/KOKKOS/pair_reaxff_kokkos.h b/src/KOKKOS/pair_reaxff_kokkos.h index 5010310232..5f228ebd19 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.h +++ b/src/KOKKOS/pair_reaxff_kokkos.h @@ -185,7 +185,7 @@ class PairReaxFFKokkos : public PairReaxFF { // Returns if we need to populate d_d* functions or not template KOKKOS_INLINE_FUNCTION - bool build_bo_list(int, int, int, int, int, int&, int&) const; + bool build_bo_list(int, int, int, int&, int&) const; KOKKOS_INLINE_FUNCTION void operator()(TagPairReaxBuildListsFull, const int&) const; From 5ef8e8cf9a7566e61cb2bbd18582537a4da208f2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 11 Jan 2024 18:53:14 -0500 Subject: [PATCH 186/189] remove variables that are set but not used --- src/KOKKOS/dynamical_matrix_kokkos.cpp | 27 -------------------------- src/KOKKOS/third_order_kokkos.cpp | 27 -------------------------- 2 files changed, 54 deletions(-) diff --git a/src/KOKKOS/dynamical_matrix_kokkos.cpp b/src/KOKKOS/dynamical_matrix_kokkos.cpp index 32986025e6..ec2cc17ef2 100644 --- a/src/KOKKOS/dynamical_matrix_kokkos.cpp +++ b/src/KOKKOS/dynamical_matrix_kokkos.cpp @@ -174,72 +174,45 @@ void DynamicalMatrixKokkos::update_force() } bool execute_on_host = false; - unsigned int datamask_read_device = 0; - unsigned int datamask_modify_device = 0; unsigned int datamask_read_host = 0; if (pair_compute_flag) { if (force->pair->execution_space==Host) { execute_on_host = true; datamask_read_host |= force->pair->datamask_read; - datamask_modify_device |= force->pair->datamask_modify; - } else { - datamask_read_device |= force->pair->datamask_read; - datamask_modify_device |= force->pair->datamask_modify; } } if (atomKK->molecular && force->bond) { if (force->bond->execution_space==Host) { execute_on_host = true; datamask_read_host |= force->bond->datamask_read; - datamask_modify_device |= force->bond->datamask_modify; - } else { - datamask_read_device |= force->bond->datamask_read; - datamask_modify_device |= force->bond->datamask_modify; } } if (atomKK->molecular && force->angle) { if (force->angle->execution_space==Host) { execute_on_host = true; datamask_read_host |= force->angle->datamask_read; - datamask_modify_device |= force->angle->datamask_modify; - } else { - datamask_read_device |= force->angle->datamask_read; - datamask_modify_device |= force->angle->datamask_modify; } } if (atomKK->molecular && force->dihedral) { if (force->dihedral->execution_space==Host) { execute_on_host = true; datamask_read_host |= force->dihedral->datamask_read; - datamask_modify_device |= force->dihedral->datamask_modify; - } else { - datamask_read_device |= force->dihedral->datamask_read; - datamask_modify_device |= force->dihedral->datamask_modify; } } if (atomKK->molecular && force->improper) { if (force->improper->execution_space==Host) { execute_on_host = true; datamask_read_host |= force->improper->datamask_read; - datamask_modify_device |= force->improper->datamask_modify; - } else { - datamask_read_device |= force->improper->datamask_read; - datamask_modify_device |= force->improper->datamask_modify; } } if (kspace_compute_flag) { if (force->kspace->execution_space==Host) { execute_on_host = true; datamask_read_host |= force->kspace->datamask_read; - datamask_modify_device |= force->kspace->datamask_modify; - } else { - datamask_read_device |= force->kspace->datamask_read; - datamask_modify_device |= force->kspace->datamask_modify; } } - if (pair_compute_flag) { atomKK->sync(force->pair->execution_space,force->pair->datamask_read); atomKK->sync(force->pair->execution_space,~(~force->pair->datamask_read|(F_MASK | ENERGY_MASK | VIRIAL_MASK))); diff --git a/src/KOKKOS/third_order_kokkos.cpp b/src/KOKKOS/third_order_kokkos.cpp index 6208aa966a..04c467777f 100644 --- a/src/KOKKOS/third_order_kokkos.cpp +++ b/src/KOKKOS/third_order_kokkos.cpp @@ -174,72 +174,45 @@ void ThirdOrderKokkos::update_force() } bool execute_on_host = false; - unsigned int datamask_read_device = 0; - unsigned int datamask_modify_device = 0; unsigned int datamask_read_host = 0; if (pair_compute_flag) { if (force->pair->execution_space==Host) { execute_on_host = true; datamask_read_host |= force->pair->datamask_read; - datamask_modify_device |= force->pair->datamask_modify; - } else { - datamask_read_device |= force->pair->datamask_read; - datamask_modify_device |= force->pair->datamask_modify; } } if (atomKK->molecular && force->bond) { if (force->bond->execution_space==Host) { execute_on_host = true; datamask_read_host |= force->bond->datamask_read; - datamask_modify_device |= force->bond->datamask_modify; - } else { - datamask_read_device |= force->bond->datamask_read; - datamask_modify_device |= force->bond->datamask_modify; } } if (atomKK->molecular && force->angle) { if (force->angle->execution_space==Host) { execute_on_host = true; datamask_read_host |= force->angle->datamask_read; - datamask_modify_device |= force->angle->datamask_modify; - } else { - datamask_read_device |= force->angle->datamask_read; - datamask_modify_device |= force->angle->datamask_modify; } } if (atomKK->molecular && force->dihedral) { if (force->dihedral->execution_space==Host) { execute_on_host = true; datamask_read_host |= force->dihedral->datamask_read; - datamask_modify_device |= force->dihedral->datamask_modify; - } else { - datamask_read_device |= force->dihedral->datamask_read; - datamask_modify_device |= force->dihedral->datamask_modify; } } if (atomKK->molecular && force->improper) { if (force->improper->execution_space==Host) { execute_on_host = true; datamask_read_host |= force->improper->datamask_read; - datamask_modify_device |= force->improper->datamask_modify; - } else { - datamask_read_device |= force->improper->datamask_read; - datamask_modify_device |= force->improper->datamask_modify; } } if (kspace_compute_flag) { if (force->kspace->execution_space==Host) { execute_on_host = true; datamask_read_host |= force->kspace->datamask_read; - datamask_modify_device |= force->kspace->datamask_modify; - } else { - datamask_read_device |= force->kspace->datamask_read; - datamask_modify_device |= force->kspace->datamask_modify; } } - if (pair_compute_flag) { atomKK->sync(force->pair->execution_space,force->pair->datamask_read); atomKK->sync(force->pair->execution_space,~(~force->pair->datamask_read|(F_MASK | ENERGY_MASK | VIRIAL_MASK))); From b041ac745459fa257fedf2a9c2f934ad72febc5e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 12 Jan 2024 13:30:16 -0500 Subject: [PATCH 187/189] backport bugfix for Kokkos with SYCL from upstream --- lib/kokkos/core/src/Kokkos_Printf.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/kokkos/core/src/Kokkos_Printf.hpp b/lib/kokkos/core/src/Kokkos_Printf.hpp index 39f95825c3..af20221a5a 100644 --- a/lib/kokkos/core/src/Kokkos_Printf.hpp +++ b/lib/kokkos/core/src/Kokkos_Printf.hpp @@ -31,7 +31,7 @@ namespace Kokkos { // backends. The GPU backends always return 1 and NVHPC only compiles if we // don't ask for the return value. template -KOKKOS_FUNCTION void printf(const char* format, Args... args) { +KOKKOS_FORCEINLINE_FUNCTION void printf(const char* format, Args... args) { #ifdef KOKKOS_ENABLE_SYCL // Some compilers warn if "args" is empty and format is not a string literal if constexpr (sizeof...(Args) == 0) From dd7ce5fbaa5a0c5168555bda9421981a3a21b2ed Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 12 Jan 2024 14:57:26 -0500 Subject: [PATCH 188/189] remove one more source of 32-bit integer overflow --- src/compute.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compute.h b/src/compute.h index 8ae01a4469..6956c3ae99 100644 --- a/src/compute.h +++ b/src/compute.h @@ -178,7 +178,7 @@ class Compute : protected Pointers { double natoms_temp; // # of atoms used for temperature calculation double extra_dof; // extra DOF for temperature computes - int fix_dof; // DOF due to fixes + double fix_dof; // DOF due to fixes int dynamic; // recount atoms for temperature computes int dynamic_user; // user request for temp compute to be dynamic From 715b030bd64b8639bc31859737dbedc24aac4d88 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 12 Jan 2024 16:12:55 -0500 Subject: [PATCH 189/189] make delete_bonds command code compatible with BPM package not being installed --- cmake/CMakeLists.txt | 11 +++++++--- src/BPM/Install.sh | 49 ++++++++++++++++++++++++++++++++++++++++++++ src/delete_bonds.cpp | 9 +++++++- 3 files changed, 65 insertions(+), 4 deletions(-) create mode 100755 src/BPM/Install.sh diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index f7e9b314bd..8833e55443 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -662,9 +662,14 @@ foreach(PKG_WITH_INCL CORESHELL DPD-SMOOTH MC MISC PHONON QEQ OPENMP KOKKOS OPT endif() endforeach() -if(PKG_PLUGIN) - target_compile_definitions(lammps PRIVATE -DLMP_PLUGIN) -endif() +###################################################################### +# packages with defines to disable package specific code +###################################################################### +foreach(PKG_WITH_DEF BPM PLUGIN) + if(PKG_${PKG_WITH_DEF}) + target_compile_definitions(lammps PRIVATE -DLMP_${PKG_WITH_DEF}) + endif() +endforeach() # link with -ldl or equivalent for plugin loading; except on Windows if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") diff --git a/src/BPM/Install.sh b/src/BPM/Install.sh new file mode 100755 index 0000000000..7f3791a702 --- /dev/null +++ b/src/BPM/Install.sh @@ -0,0 +1,49 @@ +# 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|^PKG_INC =[ \t]*|&-DLMP_BPM |' ../Makefile.package + fi + +elif (test $1 = 0) then + + if (test -e ../Makefile.package) then + sed -i -e 's/[^ \t]*LMP_BPM[^ \t]* //' ../Makefile.package + fi + +fi diff --git a/src/delete_bonds.cpp b/src/delete_bonds.cpp index 1f8fe71bff..5a58da7905 100644 --- a/src/delete_bonds.cpp +++ b/src/delete_bonds.cpp @@ -19,12 +19,15 @@ #include "comm.h" #include "domain.h" #include "error.h" -#include "fix_bond_history.h" #include "force.h" #include "group.h" #include "modify.h" #include "special.h" +#if defined(LMP_BPM) +#include "fix_bond_history.h" +#endif + #include using namespace LAMMPS_NS; @@ -118,9 +121,11 @@ void DeleteBonds::command(int narg, char **arg) iarg++; } +#if defined(LMP_BPM) // find instances of bond history to delete data auto histories = modify->get_fix_by_style("BOND_HISTORY"); int n_histories = histories.size(); +#endif // border swap to ensure type and mask is current for off-proc atoms // enforce PBC before in case atoms are outside box @@ -337,11 +342,13 @@ void DeleteBonds::command(int narg, char **arg) n = atom->num_bond[i]; atom->bond_type[i][m] = atom->bond_type[i][n-1]; atom->bond_atom[i][m] = atom->bond_atom[i][n-1]; +#if defined(LMP_BPM) if (n_histories > 0) for (auto &ihistory: histories) { dynamic_cast(ihistory)->shift_history(i,m,n-1); dynamic_cast(ihistory)->delete_history(i,n-1); } +#endif atom->num_bond[i]--; } else m++; } else m++;