Files
lammps/src/nbin_multi.cpp
Axel Kohlmeyer 9ef0395313 whitespace fixes
2021-05-12 06:30:52 -04:00

350 lines
13 KiB
C++

/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://lammps.sandia.gov/, Sandia National Laboratories
Steve Plimpton, sjplimp@sandia.gov
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
#include "nbin_multi.h"
#include "neighbor.h"
#include "atom.h"
#include "group.h"
#include "domain.h"
#include "comm.h"
#include "update.h"
#include "error.h"
#include "memory.h"
using namespace LAMMPS_NS;
#define SMALL 1.0e-6
#define CUT2BIN_RATIO 100
/* ---------------------------------------------------------------------- */
NBinMulti::NBinMulti(LAMMPS *lmp) : NBin(lmp) {}
/* ----------------------------------------------------------------------
setup for bin_atoms()
------------------------------------------------------------------------- */
void NBinMulti::bin_atoms_setup(int nall)
{
// binhead_multi[n] = per-bin vector mbins in length mbins_multi[n]
for (int n = 0; n < maxcollections; n++) {
if (mbins_multi[n] > maxbins_multi[n]) {
maxbins_multi[n] = mbins_multi[n];
memory->destroy(binhead_multi[n]);
memory->create(binhead_multi[n], mbins_multi[n], "neigh:mbins_multi");
}
}
// bins and atom2bin = per-atom vectors
// for both local and ghost atoms
// for multi, bins and atom2bin correspond to different binlists
if (nall > maxatom) {
maxatom = nall;
memory->destroy(bins);
memory->create(bins,maxatom,"neigh:bins");
memory->destroy(atom2bin);
memory->create(atom2bin,maxatom,"neigh:atom2bin");
}
}
/* ----------------------------------------------------------------------
setup neighbor binning geometry
bin numbering in each dimension is global:
0 = 0.0 to binsize, 1 = binsize to 2*binsize, etc
nbin-1,nbin,etc = bbox-binsize to bbox, bbox to bbox+binsize, etc
-1,-2,etc = -binsize to 0.0, -2*binsize to -binsize, etc
code will work for any binsize
since next(xyz) and stencil extend as far as necessary
binsize = 1/2 of cutoff is roughly optimal
for orthogonal boxes:
a dim must be filled exactly by integer # of bins
in periodic, procs on both sides of PBC must see same bin boundary
in non-periodic, coord2bin() still assumes this by use of nbin xyz
for triclinic boxes:
tilted simulation box cannot contain integer # of bins
stencil & neigh list built differently to account for this
mbinlo_multi = lowest global bin any of my ghost atoms could fall into for each collection
mbinhi_multi = highest global bin any of my ghost atoms could fall into for each collection
mbin_multi = number of bins I need in a dimension for each collection
------------------------------------------------------------------------- */
void NBinMulti::setup_bins(int /*style*/)
{
int n;
// Initialize arrays
if (ncollections > maxcollections) {
// Clear any/all memory for existing groupings
for (n = 0; n < maxcollections; n++)
memory->destroy(binhead_multi[n]);
delete [] binhead_multi;
maxcollections = ncollections;
binhead_multi = new int*[maxcollections]();
memory->destroy(nbinx_multi);
memory->destroy(nbiny_multi);
memory->destroy(nbinz_multi);
memory->create(nbinx_multi, maxcollections, "neigh:nbinx_multi");
memory->create(nbiny_multi, maxcollections, "neigh:nbiny_multi");
memory->create(nbinz_multi, maxcollections, "neigh:nbinz_multi");
memory->destroy(mbins_multi);
memory->destroy(mbinx_multi);
memory->destroy(mbiny_multi);
memory->destroy(mbinz_multi);
memory->create(mbins_multi, maxcollections, "neigh:mbins_multi");
memory->create(mbinx_multi, maxcollections, "neigh:mbinx_multi");
memory->create(mbiny_multi, maxcollections, "neigh:mbiny_multi");
memory->create(mbinz_multi, maxcollections, "neigh:mbinz_multi");
memory->destroy(mbinxlo_multi);
memory->destroy(mbinylo_multi);
memory->destroy(mbinzlo_multi);
memory->create(mbinxlo_multi, maxcollections, "neigh:mbinxlo_multi");
memory->create(mbinylo_multi, maxcollections, "neigh:mbinylo_multi");
memory->create(mbinzlo_multi, maxcollections, "neigh:mbinzlo_multi");
memory->destroy(binsizex_multi);
memory->destroy(binsizey_multi);
memory->destroy(binsizez_multi);
memory->create(binsizex_multi, maxcollections, "neigh:binsizex_multi");
memory->create(binsizey_multi, maxcollections, "neigh:binsizey_multi");
memory->create(binsizez_multi, maxcollections, "neigh:binsizez_multi");
memory->destroy(bininvx_multi);
memory->destroy(bininvy_multi);
memory->destroy(bininvz_multi);
memory->create(bininvx_multi, maxcollections, "neigh:bininvx_multi");
memory->create(bininvy_multi, maxcollections, "neigh:bininvy_multi");
memory->create(bininvz_multi, maxcollections, "neigh:bininvz_multi");
memory->destroy(maxbins_multi);
memory->create(maxbins_multi, maxcollections, "neigh:maxbins_multi");
// ensure reallocation occurs in bin_atoms_setup()
for (n = 0; n < maxcollections; n++) {
maxbins_multi[n] = 0;
}
maxatom = 0;
}
// Identify smallest collection
int icollectionmin = 0;
for (n = 0; n < ncollections; n++)
if (cutcollectionsq[n][n] < cutcollectionsq[icollectionmin][icollectionmin])
icollectionmin = n;
// bbox = size of bbox of entire domain
// bsubbox lo/hi = bounding box of my subdomain extended by comm->cutghost
// for triclinic:
// bbox bounds all 8 corners of tilted box
// subdomain is in lamda coords
// include dimension-dependent extension via comm->cutghost
// domain->bbox() converts lamda extent to box coords and computes bbox
double bbox[3],bsubboxlo[3],bsubboxhi[3];
double *cutghost = comm->cutghost;
if (triclinic == 0) {
bsubboxlo[0] = domain->sublo[0] - cutghost[0];
bsubboxlo[1] = domain->sublo[1] - cutghost[1];
bsubboxlo[2] = domain->sublo[2] - cutghost[2];
bsubboxhi[0] = domain->subhi[0] + cutghost[0];
bsubboxhi[1] = domain->subhi[1] + cutghost[1];
bsubboxhi[2] = domain->subhi[2] + cutghost[2];
} else {
double lo[3],hi[3];
lo[0] = domain->sublo_lamda[0] - cutghost[0];
lo[1] = domain->sublo_lamda[1] - cutghost[1];
lo[2] = domain->sublo_lamda[2] - cutghost[2];
hi[0] = domain->subhi_lamda[0] + cutghost[0];
hi[1] = domain->subhi_lamda[1] + cutghost[1];
hi[2] = domain->subhi_lamda[2] + cutghost[2];
domain->bbox(lo,hi,bsubboxlo,bsubboxhi);
}
bbox[0] = bboxhi[0] - bboxlo[0];
bbox[1] = bboxhi[1] - bboxlo[1];
bbox[2] = bboxhi[2] - bboxlo[2];
// For each grouping...
double binsize_optimal, binsizeinv, coord;
int mbinxhi,mbinyhi,mbinzhi;
for (n = 0; n < ncollections; n++) {
// binsize_user only relates to smallest collection
// optimal bin size is roughly 1/2 the collection-collection cutoff
// special case of all cutoffs = 0.0, binsize = box size
if (n == icollectionmin && binsizeflag) binsize_optimal = binsize_user;
else binsize_optimal = 0.5*sqrt(cutcollectionsq[n][n]);
if (binsize_optimal == 0.0) binsize_optimal = bbox[0];
binsizeinv = 1.0/binsize_optimal;
// test for too many global bins in any dimension due to huge global domain
if (bbox[0]*binsizeinv > MAXSMALLINT || bbox[1]*binsizeinv > MAXSMALLINT ||
bbox[2]*binsizeinv > MAXSMALLINT)
error->all(FLERR,"Domain too large for neighbor bins");
// create actual bins
// always have one bin even if cutoff > bbox
// for 2d, nbinz_multi[n] = 1
nbinx_multi[n] = static_cast<int> (bbox[0]*binsizeinv);
nbiny_multi[n] = static_cast<int> (bbox[1]*binsizeinv);
if (dimension == 3) nbinz_multi[n] = static_cast<int> (bbox[2]*binsizeinv);
else nbinz_multi[n] = 1;
if (nbinx_multi[n] == 0) nbinx_multi[n] = 1;
if (nbiny_multi[n] == 0) nbiny_multi[n] = 1;
if (nbinz_multi[n] == 0) nbinz_multi[n] = 1;
// compute actual bin size for nbins to fit into box exactly
// error if actual bin size << cutoff, since will create a zillion bins
// this happens when nbin = 1 and box size << cutoff
// typically due to non-periodic, flat system in a particular dim
// in that extreme case, cannot use multi, should use NSQ not BIN neighbor style
binsizex_multi[n] = bbox[0]/nbinx_multi[n];
binsizey_multi[n] = bbox[1]/nbiny_multi[n];
binsizez_multi[n] = bbox[2]/nbinz_multi[n];
bininvx_multi[n] = 1.0 / binsizex_multi[n];
bininvy_multi[n] = 1.0 / binsizey_multi[n];
bininvz_multi[n] = 1.0 / binsizez_multi[n];
if (binsize_optimal*bininvx_multi[n] > CUT2BIN_RATIO ||
binsize_optimal*bininvy_multi[n] > CUT2BIN_RATIO)
error->all(FLERR,"Cannot use neighbor bins - box size << cutoff");
if ((dimension == 3) && (binsize_optimal*bininvz_multi[n] > CUT2BIN_RATIO))
error->all(FLERR,"Cannot use neighbor bins - box size << cutoff");
// mbinlo/hi = lowest and highest global bins my ghost atoms could be in
// coord = lowest and highest values of coords for my ghost atoms
// static_cast(-1.5) = -1, so subract additional -1
// add in SMALL for round-off safety
coord = bsubboxlo[0] - SMALL*bbox[0];
mbinxlo_multi[n] = static_cast<int> ((coord-bboxlo[0])*bininvx_multi[n]);
if (coord < bboxlo[0]) mbinxlo_multi[n] = mbinxlo_multi[n] - 1;
coord = bsubboxhi[0] + SMALL*bbox[0];
mbinxhi = static_cast<int> ((coord-bboxlo[0])*bininvx_multi[n]);
coord = bsubboxlo[1] - SMALL*bbox[1];
mbinylo_multi[n] = static_cast<int> ((coord-bboxlo[1])*bininvy_multi[n]);
if (coord < bboxlo[1]) mbinylo_multi[n] = mbinylo_multi[n] - 1;
coord = bsubboxhi[1] + SMALL*bbox[1];
mbinyhi = static_cast<int> ((coord-bboxlo[1])*bininvy_multi[n]);
if (dimension == 3) {
coord = bsubboxlo[2] - SMALL*bbox[2];
mbinzlo_multi[n] = static_cast<int> ((coord-bboxlo[2])*bininvz_multi[n]);
if (coord < bboxlo[2]) mbinzlo_multi[n] = mbinzlo_multi[n] - 1;
coord = bsubboxhi[2] + SMALL*bbox[2];
mbinzhi = static_cast<int> ((coord-bboxlo[2])*bininvz_multi[n]);
}
// extend bins by 1 to insure stencil extent is included
// for 2d, only 1 bin in z
mbinxlo_multi[n] = mbinxlo_multi[n] - 1;
mbinxhi = mbinxhi + 1;
mbinx_multi[n] = mbinxhi - mbinxlo_multi[n] + 1;
mbinylo_multi[n] = mbinylo_multi[n] - 1;
mbinyhi = mbinyhi + 1;
mbiny_multi[n] = mbinyhi - mbinylo_multi[n] + 1;
if (dimension == 3) {
mbinzlo_multi[n] = mbinzlo_multi[n] - 1;
mbinzhi = mbinzhi + 1;
} else mbinzlo_multi[n] = mbinzhi = 0;
mbinz_multi[n] = mbinzhi - mbinzlo_multi[n] + 1;
bigint bbin = ((bigint) mbinx_multi[n])
* ((bigint) mbiny_multi[n]) * ((bigint) mbinz_multi[n]) + 1;
if (bbin > MAXSMALLINT) error->one(FLERR,"Too many neighbor bins");
mbins_multi[n] = bbin;
}
}
/* ----------------------------------------------------------------------
bin owned and ghost atoms by type
------------------------------------------------------------------------- */
void NBinMulti::bin_atoms()
{
int i,ibin,n;
last_bin = update->ntimestep;
for (n = 0; n < ncollections; n++) {
for (i = 0; i < mbins_multi[n]; i++) binhead_multi[n][i] = -1;
}
// bin in reverse order so linked list will be in forward order
// also puts ghost atoms at end of list, which is necessary
int *collection = neighbor->collection;
double **x = atom->x;
int *mask = atom->mask;
int nlocal = atom->nlocal;
int nall = nlocal + atom->nghost;
if (includegroup) {
int bitmask = group->bitmask[includegroup];
for (i = nall-1; i >= nlocal; i--) {
if (mask[i] & bitmask) {
n = collection[i];
ibin = coord2bin_multi(x[i], n);
atom2bin[i] = ibin;
bins[i] = binhead_multi[n][ibin];
binhead_multi[n][ibin] = i;
}
}
for (i = atom->nfirst-1; i >= 0; i--) {
n = collection[i];
ibin = coord2bin_multi(x[i], n);
atom2bin[i] = ibin;
bins[i] = binhead_multi[n][ibin];
binhead_multi[n][ibin] = i;
}
} else {
for (i = nall-1; i >= 0; i--) {
n = collection[i];
ibin = coord2bin_multi(x[i], n);
atom2bin[i] = ibin;
bins[i] = binhead_multi[n][ibin];
binhead_multi[n][ibin] = i;
}
}
}
/* ---------------------------------------------------------------------- */
double NBinMulti::memory_usage()
{
double bytes = 0;
for (int m = 0; m < maxcollections; m++)
bytes += (double)maxbins_multi[m]*sizeof(int);
bytes += (double)2*maxatom*sizeof(int);
return bytes;
}