Files
lammps/src/REPLICA/fix_hyper_local.cpp

1438 lines
42 KiB
C++

/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
http://lammps.sandia.gov, Sandia National Laboratories
Steve Plimpton, sjplimp@sandia.gov
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU Gene<ral Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
#include <mpi.h>
#include <cmath>
#include <cstdio>
#include <cstring>
#include "fix_hyper_local.h"
#include "atom.h"
#include "update.h"
#include "force.h"
#include "pair.h"
#include "domain.h"
#include "comm.h"
#include "neighbor.h"
#include "neigh_request.h"
#include "neigh_list.h"
#include "modify.h"
#include "math_extra.h"
#include "memory.h"
#include "error.h"
using namespace LAMMPS_NS;
using namespace FixConst;
#define DELTABOOST 16
#define BOOSTINIT 1.0
#define COEFFMAX 1.2
#define BIG 1.0e20
enum{STRAIN,STRAINREGION,BIASFLAG};
enum{IGNORE,WARN,ERROR};
/* ---------------------------------------------------------------------- */
FixHyperLocal::FixHyperLocal(LAMMPS *lmp, int narg, char **arg) :
FixHyper(lmp, narg, arg), old2now(NULL), xold(NULL), tagold(NULL),
bonds(NULL), numbond(NULL), maxstrain(NULL), maxstrain_region(NULL),
maxstrain_bondindex(NULL), biasflag(NULL), boost(NULL),
histo(NULL), allhisto(NULL)
{
// error checks
// solution for tagint != int is to worry about storing
// local index vs global ID in same variable
// maybe need to declare them all tagint, not int
if (atom->map_style == 0)
error->all(FLERR,"Fix hyper/local command requires atom map");
if (sizeof(tagint) != sizeof(int))
error->all(FLERR,"Fix hyper/local requires tagint = int");
// parse args
if (narg < 10) error->all(FLERR,"Illegal fix hyper/local command");
hyperflag = 2;
scalar_flag = 1;
vector_flag = 1;
size_vector = 23;
global_freq = 1;
extscalar = 0;
extvector = 0;
cutbond = force->numeric(FLERR,arg[3]);
qfactor = force->numeric(FLERR,arg[4]);
vmax = force->numeric(FLERR,arg[5]);
tequil = force->numeric(FLERR,arg[6]);
dcut = force->numeric(FLERR,arg[7]);
alpha_user = force->numeric(FLERR,arg[8]);
boosttarget = force->numeric(FLERR,arg[9]);
if (cutbond < 0.0 || qfactor < 0.0 || vmax < 0.0 ||
tequil <= 0.0 || dcut <= 0.0 || alpha_user <= 0.0 || boosttarget < 1.0)
error->all(FLERR,"Illegal fix hyper/local command");
invqfactorsq = 1.0 / (qfactor*qfactor);
cutbondsq = cutbond*cutbond;
dcutsq = dcut*dcut;
beta = 1.0 / (force->boltz * tequil);
// optional args
histoflag = 0;
lostbond = IGNORE;
checkbias = 0;
checkcoeff = 0;
int iarg = 10;
while (iarg < narg) {
/* NOTE: do not enable this yet, need to think about it differently
if (strcmp(arg[iarg],"histo") == 0) {
if (iarg+5 > narg) error->all(FLERR,"Illegal fix hyper/local command");
histoflag = 1;
histo_every = force->inumeric(FLERR,arg[iarg+1]);
histo_count = force->inumeric(FLERR,arg[iarg+2]);
histo_delta = force->numeric(FLERR,arg[iarg+3]);
histo_print = force->inumeric(FLERR,arg[iarg+4]);
if (histo_every <= 0 || histo_count % 2 ||
histo_delta <= 0.0 || histo_print <= 0)
error->all(FLERR,"Illegal fix hyper/local command");
iarg += 5;
*/
if (strcmp(arg[iarg],"lostbond") == 0) {
if (iarg+2 > narg) error->all(FLERR,"Illegal fix hyper/local command");
if (strcmp(arg[iarg+1],"error") == 0) lostbond = ERROR;
else if (strcmp(arg[iarg+1],"warn") == 0) lostbond = WARN;
else if (strcmp(arg[iarg+1],"ignore") == 0) lostbond = IGNORE;
else error->all(FLERR,"Illegal fix hyper/local command");
iarg += 2;
} else if (strcmp(arg[iarg],"check/bias") == 0) {
if (iarg+3 > narg) error->all(FLERR,"Illegal fix hyper/local command");
checkbias = 1;
checkbias_every = force->inumeric(FLERR,arg[iarg+1]);
if (strcmp(arg[iarg+2],"error") == 0) checkbias_flag = ERROR;
else if (strcmp(arg[iarg+2],"warn") == 0) checkbias_flag = WARN;
else if (strcmp(arg[iarg+2],"ignore") == 0) checkbias_flag = IGNORE;
else error->all(FLERR,"Illegal fix hyper/local command");
iarg += 3;
} else if (strcmp(arg[iarg],"check/coeff") == 0) {
if (iarg+3 > narg) error->all(FLERR,"Illegal fix hyper/local command");
checkcoeff = 1;
checkcoeff_every = force->inumeric(FLERR,arg[iarg+1]);
if (strcmp(arg[iarg+2],"error") == 0) checkcoeff_flag = ERROR;
else if (strcmp(arg[iarg+2],"warn") == 0) checkcoeff_flag = WARN;
else if (strcmp(arg[iarg+2],"ignore") == 0) checkcoeff_flag = IGNORE;
else error->all(FLERR,"Illegal fix hyper/local command");
iarg += 3;
} else error->all(FLERR,"Illegal fix hyper/local command");
}
// per-atom data structs
maxbond = 0;
bonds = NULL;
numbond = NULL;
maxstrain = NULL;
maxstrain_region = NULL;
maxstrain_bondindex = NULL;
biasflag = NULL;
maxold = old_nall = 0;
old2now = NULL;
xold = NULL; // NOTE: don't really need except to monitor drift
tagold = NULL;
nboost = maxboost = 0;
boost = NULL;
// maxbondperatom = max # of bonds any atom is part of
// will be reset in bond_build()
// set comm size needed by this fix
maxbondperatom = 1;
comm_forward = 1;
comm_reverse = 1;
// perform initial allocation of atom-based arrays
// register with Atom class
grow_arrays(atom->nmax);
atom->add_callback(0);
me = comm->me;
firstflag = 1;
allbonds = 0;
allboost = 0.0;
starttime = update->ntimestep;
nostrainyet = 1;
nnewbond = 0;
nevent = 0;
nevent_atom = 0;
mybias = 0.0;
histo = allhisto = NULL;
if (histoflag) {
invhisto_delta = 1.0 / histo_delta;
histo_lo = 1.0 - (histo_count/2 * histo_delta);
histo = new bigint[histo_count+2];
allhisto = new bigint[histo_count+2];
}
}
/* ---------------------------------------------------------------------- */
FixHyperLocal::~FixHyperLocal()
{
memory->destroy(bonds);
memory->destroy(numbond);
memory->destroy(maxstrain);
memory->destroy(maxstrain_region);
memory->destroy(maxstrain_bondindex);
memory->destroy(biasflag);
memory->destroy(old2now);
memory->destroy(xold);
memory->destroy(tagold);
memory->destroy(boost);
delete [] histo;
delete [] allhisto;
}
/* ---------------------------------------------------------------------- */
int FixHyperLocal::setmask()
{
int mask = 0;
mask |= PRE_NEIGHBOR;
mask |= PRE_REVERSE;
mask |= MIN_PRE_NEIGHBOR;
mask |= THERMO_ENERGY;
return mask;
}
/* ---------------------------------------------------------------------- */
void FixHyperLocal::init_hyper()
{
ghost_toofar = 0;
lostbond_partner = 0;
lostbond_coeff = 0.0;
checkbias_count = 0;
checkcoeff_count = 0;
maxdriftsq = 0.0;
maxbondlen = 0.0;
maxboostcoeff = 0.0;
minboostcoeff = BIG;
sumboostcoeff = 0.0;
nboost_running = 0;
nobias_running = 0;
rmaxever = 0.0;
rmaxeverbig = 0.0;
nbondbuild = 0;
time_bondbuild = 0.0;
if (histoflag) histo_steps = 0;
}
/* ---------------------------------------------------------------------- */
void FixHyperLocal::init()
{
// for newton off, bond force bias will not be applied correctly
// bonds that straddle 2 procs
// warn if molecular system, since near-neighbors may not appear in neigh list
// user should not be including bonded atoms as hyper "bonds"
if (force->newton_pair == 0)
error->all(FLERR,"Hyper local requires newton pair on");
if (atom->molecular && me == 0)
error->warning(FLERR,"Hyper local for molecular systems "
"requires care in defining hyperdynamics bonds");
// cutghost = communication cutoff as calculated by Neighbor and Comm
// error if cutghost is smaller than Dcut
// warn if no drift distance added to cutghost
if (firstflag) {
double cutghost;
if (force->pair)
cutghost = MAX(force->pair->cutforce+neighbor->skin,comm->cutghostuser);
else
cutghost = comm->cutghostuser;
if (cutghost < dcut)
error->all(FLERR,"Fix hyper/local bond cutoff exceeds ghost atom range - "
"use comm_modify cutoff command");
if (cutghost < dcut+cutbond/2.0 && me == 0)
error->warning(FLERR,"Fix hyper/local ghost atom range "
"may not allow for atom drift between events");
}
alpha = update->dt / alpha_user;
// need an occasional full neighbor list with cutoff = Dcut
// do not need to include neigh skin in cutoff,
// b/c this list will be built every time bond_build() is called
// NOTE: what if pair style list cutoff > Dcut
// or what if neigh skin is huge?
int irequest = neighbor->request(this,instance_me);
neighbor->requests[irequest]->pair = 0;
neighbor->requests[irequest]->fix = 1;
neighbor->requests[irequest]->half = 0;
neighbor->requests[irequest]->full = 1;
neighbor->requests[irequest]->cut = 1;
neighbor->requests[irequest]->cutoff = dcut;
neighbor->requests[irequest]->occasional = 1;
// DEBUG timing output
timefirst = timesecond = timethird = timefourth = timefifth =
timesixth = timeseventh = timetotal = 0.0;
}
/* ---------------------------------------------------------------------- */
void FixHyperLocal::init_list(int /* id */, NeighList *ptr)
{
list = ptr;
}
/* ---------------------------------------------------------------------- */
void FixHyperLocal::setup_pre_neighbor()
{
// called for dynamics and minimization NOTE: check if min is needed?
pre_neighbor();
}
/* ---------------------------------------------------------------------- */
void FixHyperLocal::setup_pre_reverse(int eflag, int vflag)
{
// only called for dynamics, not minimization
// setupflag prevents boostostat update of boost coeffs in setup
// also prevents increments of nboost_running, nbias_running, sumboostcoeff
setupflag = 1;
pre_reverse(eflag,vflag);
setupflag = 0;
}
/* ---------------------------------------------------------------------- */
void FixHyperLocal::pre_neighbor()
{
int i,m,n,ilocal,jlocal;
// convert global ID bond partners back to local indices
// need to use closest_image() so can calculate bond lengths
// error flag should not happen for a well-behaved system
// b/c are only looking up bond partners inside or near my sub-domain
double **x = atom->x;
int nlocal = atom->nlocal;
int missing = 0;
double missing_coeff = 0.0;
for (i = 0; i < nlocal; i++) {
n = numbond[i];
for (m = 0; m < n; m++) {
jlocal = atom->map(bonds[i][m].jtag);
if (jlocal >= 0) bonds[i][m].j = domain->closest_image(i,jlocal);
else {
bonds[i][m].j = -1;
missing++;
missing_coeff += bonds[i][m].boostcoeff;
if (lostbond != IGNORE) {
char str[128];
sprintf(str,"Fix hyper/local bond info missing for bond "
TAGINT_FORMAT "," TAGINT_FORMAT
" with coeff %g at step " BIGINT_FORMAT,
atom->tag[i],bonds[i][m].jtag,bonds[i][m].boostcoeff,
update->ntimestep);
if (lostbond == ERROR) error->one(FLERR,str);
if (lostbond == WARN) error->warning(FLERR,str);
}
}
}
}
lostbond_partner += missing;
lostbond_coeff += missing_coeff;
// set old2now to point to current local atom indices
// only necessary for tagold entries > 0
// because if tagold = 0, atom is not active in Dcut neighbor list
// must be done after atoms migrate and ghost atoms setup via comm->borders()
// does not matter if there are multiple ghost copies of a global ID
// since only need the ghost atom strain, not its coordinates
// NOTE: maybe need not use closest image, b/c old2now only used to access
// maxstrain values, which will be same for any copy of ghost atom ??
// also need it to compute maxdriftsq correctly when a proc spans a PBC
double distsq;
for (i = 0; i < old_nall; i++) {
if (tagold[i] == 0) continue;
ilocal = atom->map(tagold[i]);
ilocal = domain->closest_image(xold[i],ilocal);
old2now[i] = ilocal;
if (ilocal >= 0) {
distsq = MathExtra::distsq3(x[ilocal],xold[i]);
maxdriftsq = MAX(distsq,maxdriftsq);
} else ghost_toofar++;
}
}
/* ---------------------------------------------------------------------- */
void FixHyperLocal::pre_reverse(int /* eflag */, int /* vflag */)
{
int i,j,m,ii,jj,inum,jnum,iold,jold,nbond,bondindex;
tagint itag,jtag;
double xtmp,ytmp,ztmp,delx,dely,delz;
double r,r0,estrain,emax,vbias,fbias,fbiasr,boostcoeff;
int *ilist,*jlist,*numneigh,**firstneigh;
//double time1,time2,time3,time4,time5,time6,time7,time8;
//time1 = MPI_Wtime();
// compute current maxstrain and maxstrain_bond for each owned atom
// use per-atom full bond list
// this is double-calculating for IJ and JI bonds
// could compute once, but would have to find/store index of JI bond
// order two I,J atoms consistently for IJ and JI calcs
// to insure no round-off issue when comparing maxstrain values of I,J
double **x = atom->x;
tagint *tag = atom->tag;
int nlocal = atom->nlocal;
int mybonds = 0;
nostrainyet = 0;
for (i = 0; i < nlocal; i++) {
xtmp = x[i][0];
ytmp = x[i][1];
ztmp = x[i][2];
itag = tag[i];
emax = 0.0;
bondindex = -1;
jtag = 0;
nbond = numbond[i];
mybonds += nbond;
for (m = 0; m < nbond; m++) {
j = bonds[i][m].j;
if (j < 0) continue;
jtag = bonds[i][m].jtag;
r0 = bonds[i][m].r0;
if (itag < jtag) {
delx = xtmp - x[j][0];
dely = ytmp - x[j][1];
delz = ztmp - x[j][2];
} else {
delx = x[j][0] - xtmp;
dely = x[j][1] - ytmp;
delz = x[j][2] - ztmp;;
}
r = sqrt(delx*delx + dely*dely + delz*delz);
maxbondlen = MAX(r,maxbondlen);
estrain = fabs(r-r0) / r0;
if (estrain > emax) {
emax = estrain;
bondindex = m;
}
}
maxstrain[i] = emax;
maxstrain_bondindex[i] = bondindex;
}
//time2 = MPI_Wtime();
// forward comm to acquire maxstrain of all ghost atoms
commflag = STRAIN;
comm->forward_comm_fix(this);
//time3 = MPI_Wtime();
// use original Dcut neighbor list to check maxstrain of all neighbor atoms
// set maxstrain_region of I atoms = maxstrain of I and all J neighs
// neighbor list has old indices for IJ b/c reneighboring may have occurred
// use old2now[] to convert to current indices
// if neighbor is not currently known (too far away),
// then assume it was part of an event and its strain = qfactor
// this double loop sets maxstrain_region of mostly owned atoms
// but possibly some ghost atoms as well
int nall = nlocal + atom->nghost;
for (i = 0; i < nall; i++) maxstrain_region[i] = 0.0;
inum = list->inum;
ilist = list->ilist;
numneigh = list->numneigh;
firstneigh = list->firstneigh;
// find largest distance from subbox that a ghost atom is with strain < qfactor
double rmax = rmaxever;
double rmaxbig = rmaxeverbig;
double *sublo = domain->sublo;
double *subhi = domain->subhi;
for (ii = 0; ii < inum; ii++) {
iold = ilist[ii];
jlist = firstneigh[iold];
jnum = numneigh[iold];
// I and J may be ghost atoms
// will always know I b/c atoms do not drift that far
// but may no longer know J if hops outside cutghost
// in that case, assume it performed an event, its strain = qfactor
i = old2now[iold];
emax = maxstrain[i];
for (jj = 0; jj < jnum; jj++) {
jold = jlist[jj];
j = old2now[jold];
if (j >= 0) emax = MAX(emax,maxstrain[j]);
else {
emax = MAX(emax,qfactor);
continue;
}
// DEBUG - only good for 2 dims x,y
if (j >= nlocal) {
if (x[j][0] < sublo[0]) rmaxbig = MAX(rmaxbig,sublo[0]-x[j][0]);
if (x[j][1] < sublo[1]) rmaxbig = MAX(rmaxbig,sublo[1]-x[j][1]);
if (x[j][0] > subhi[0]) rmaxbig = MAX(rmaxbig,x[j][0]-subhi[0]);
if (x[j][1] > subhi[1]) rmaxbig = MAX(rmaxbig,x[j][1]-subhi[1]);
if (maxstrain[j] < qfactor) {
if (x[j][0] < sublo[0]) rmax = MAX(rmax,sublo[0]-x[j][0]);
if (x[j][1] < sublo[1]) rmax = MAX(rmax,sublo[1]-x[j][1]);
if (x[j][0] > subhi[0]) rmax = MAX(rmax,x[j][0]-subhi[0]);
if (x[j][1] > subhi[1]) rmax = MAX(rmax,x[j][1]-subhi[1]);
}
}
}
maxstrain_region[i] = emax;
}
double rmax2[2],rmax2all[2];
rmax2[0] = rmax;
rmax2[1] = rmaxbig;
MPI_Allreduce(&rmax2,&rmax2all,2,MPI_DOUBLE,MPI_MAX,world);
rmaxever = rmax2all[0];
rmaxeverbig = rmax2all[1];
MPI_Allreduce(&mybonds,&allbonds,1,MPI_INT,MPI_SUM,world);
//time4 = MPI_Wtime();
// reverse comm to acquire maxstrain_region from ghost atoms
// needed b/c neighbor list referred to old owned atoms,
// so above loop may set maxstrain_region of ghost atoms
// forward comm to acquire maxstrain_region of all ghost atoms
commflag = STRAINREGION;
comm->reverse_comm_fix(this);
comm->forward_comm_fix(this);
//time5 = MPI_Wtime();
// identify biased bonds and add to boost list
// for each max-strain bond IJ of atom I:
// bias this bond only if all these conditions hold:
// itag < jtag, so bond is only biased once
// maxstrain[i] = maxstrain_region[i]
// maxstrain[j] = maxstrain_region[j]
// NOTE: also need to check that maxstrain[i] = maxstrain[j] ??
// don't think so, b/c maxstrain_region[i] includes maxstrain[i]
nboost = 0;
for (i = 0; i < nlocal; i++) {
if (numbond[i] == 0) continue;
itag = tag[i];
j = bonds[i][maxstrain_bondindex[i]].j;
jtag = tag[j];
if (itag > jtag) continue;
if (maxstrain[i] != maxstrain_region[i]) continue;
if (maxstrain[j] != maxstrain_region[j]) continue;
if (nboost == maxboost) {
maxboost += DELTABOOST;
memory->grow(boost,maxboost,"hyper/local:boost");
}
boost[nboost++] = i;
}
//time6 = MPI_Wtime();
// apply boost force to bonds with locally max strain
double **f = atom->f;
int nobias = 0;
mybias = 0.0;
for (int iboost = 0; iboost < nboost; iboost++) {
i = boost[iboost];
emax = maxstrain[i];
if (emax >= qfactor) {
nobias++;
continue;
}
m = maxstrain_bondindex[i];
j = bonds[i][m].j;
r0 = bonds[i][m].r0;
boostcoeff = bonds[i][m].boostcoeff;
vbias = boostcoeff * vmax * (1.0 - emax*emax*invqfactorsq);
fbias = boostcoeff * 2.0 * vmax * emax / (qfactor*qfactor * r0);
delx = x[i][0] - x[j][0];
dely = x[i][1] - x[j][1];
delz = x[i][2] - x[j][2];
r = sqrt(delx*delx + dely*dely + delz*delz);
fbiasr = fbias / r;
f[i][0] += delx*fbiasr;
f[i][1] += dely*fbiasr;
f[i][2] += delz*fbiasr;
f[j][0] -= delx*fbiasr;
f[j][1] -= dely*fbiasr;
f[j][2] -= delz*fbiasr;
mybias += vbias;
}
//time7 = MPI_Wtime();
// no boostostat update of boost coeffs when pre_reverse called from setup()
// nboost_running, nobias_running, sumboostcoeff only incremented on run steps
// NOTE: maybe should also not bias any bonds on firststep of this fix
if (setupflag) return;
nboost_running += nboost;
nobias_running += nobias;
// apply boostostat to boost coefficients of all bonds of all owned atoms
// use per-atom full bond list
// this is double-calculating for IJ and JI bonds
// should be identical for both, b/c emax is the same
// could compute once, but would have to find/store index of JI bond
// delta in boost coeff is function of maxboost_region vs target boost
// maxboost_region is function of two maxstrain_regions for I,J
// NOTE: if J is lost to I but not vice versa, then biascoeff IJ != JI
double myboost = 0.0;
double emaxi,emaxj,maxboost_region;
for (i = 0; i < nlocal; i++) {
emaxi = maxstrain_region[i];
nbond = numbond[i];
for (m = 0; m < nbond; m++) {
j = bonds[i][m].j;
if (j < 0) continue;
emaxj = maxstrain_region[j];
emax = MAX(emaxi,emaxj);
if (emax < qfactor) vbias = vmax * (1.0 - emax*emax*invqfactorsq);
else vbias = 0.0;
boostcoeff = bonds[i][m].boostcoeff;
maxboost_region = exp(beta * boostcoeff*vbias);
boostcoeff -= alpha * (maxboost_region-boosttarget) / boosttarget;
// COMMENT OUT for now - need better way to bound boostcoeff
//boostcoeff = MIN(boostcoeff,COEFFMAX);
myboost += boostcoeff;
maxboostcoeff = MAX(maxboostcoeff,boostcoeff);
minboostcoeff = MIN(minboostcoeff,boostcoeff);
bonds[i][m].boostcoeff = boostcoeff;
}
}
// running stats
MPI_Allreduce(&myboost,&allboost,1,MPI_DOUBLE,MPI_SUM,world);
if (allbonds) sumboostcoeff += allboost/allbonds;
// histogram the bond coeffs and output as requested
// do not double count each bond
if (histoflag && update->ntimestep % histo_every == 0) {
if (histo_steps == 0)
for (i = 0; i < histo_count+2; i++) histo[i] = 0;
histo_steps++;
int ihisto;
for (i = 0; i < nlocal; i++) {
nbond = numbond[i];
for (m = 0; m < nbond; m++) {
if (tag[i] > bonds[i][m].jtag) continue;
boostcoeff = bonds[i][m].boostcoeff;
if (boostcoeff < histo_lo) ihisto = -1;
else ihisto = static_cast<int> ((boostcoeff-histo_lo) * invhisto_delta);
if (ihisto >= histo_count) ihisto = histo_count;
histo[ihisto+1]++;
}
}
if (update->ntimestep % histo_print == 0) {
MPI_Allreduce(histo,allhisto,histo_count+2,MPI_LMP_BIGINT,MPI_SUM,world);
bigint total;
for (i = 0; i < histo_count+2; i++) total += allhisto[i];
if (me == 0) {
if (screen) {
fprintf(screen,"Histogram of bias coeffs:\n");
for (i = 0; i < histo_count+2; i++)
fprintf(screen," %g",1.0*allhisto[i]/total);
fprintf(screen,"\n");
}
if (logfile) {
fprintf(logfile,"Histogram of bias coeffs:\n");
for (i = 0; i < histo_count+2; i++)
fprintf(logfile," %g",1.0*allhisto[i]/total);
fprintf(logfile,"\n");
}
}
}
}
// check for any biased bonds that are too close to each other
// keep a running count for output
if (checkbias && update->ntimestep % checkbias_every == 0) {
// mark each atom in a biased bond with ID of partner
// nboost loop will mark some ghost atoms
for (i = 0; i < nall; i++) biasflag[i] = 0;
for (int iboost = 0; iboost < nboost; iboost++) {
i = boost[iboost];
m = maxstrain_bondindex[i];
j = bonds[i][m].j;
biasflag[i] = tag[j];
biasflag[j] = tag[i];
}
// reverse comm to acquire biasflag from ghost atoms
// needed b/c above loop may set biasflag of ghost atoms
// forward comm to acquire biasflag of all ghost atoms
commflag = BIASFLAG;
comm->reverse_comm_fix(this);
comm->forward_comm_fix(this);
// I and J may be ghost atoms
// only continue if I is a biased atom
// if J is unknonw (drifted ghost) just ignore
// if J is biased and is not bonded to I, then flag as too close
for (ii = 0; ii < inum; ii++) {
iold = ilist[ii];
i = old2now[iold];
if (biasflag[i] == 0) continue;
jlist = firstneigh[iold];
jnum = numneigh[iold];
for (jj = 0; jj < jnum; jj++) {
jold = jlist[jj];
j = old2now[jold];
if (j < 0) continue;
if (biasflag[j] && biasflag[j] != tag[i]) checkbias_count++;
}
}
}
// check for any bond bias coeffcients that do not match
// cannot check unless both atoms IJ are owned by this proc
// keep a running count for output
if (checkcoeff && update->ntimestep % checkcoeff_every == 0) {
int jb,jbonds;
for (i = 0; i < nlocal; i++) {
nbond = numbond[i];
for (m = 0; m < nbond; m++) {
if (tag[i] > bonds[i][m].jtag) continue;
j = bonds[i][m].j;
if (j < 0) continue;
if (j >= nlocal) continue;
itag = tag[i];
jbonds = numbond[j];
for (jb = 0; jb < jbonds; jb++)
if (bonds[j][jb].jtag == itag) break;
if (jb == jbonds)
error->one(FLERR,"Fix hyper/local could not find duplicate bond");
if (bonds[i][m].boostcoeff != bonds[j][jb].boostcoeff)
checkcoeff_count++;
}
}
}
}
/* ---------------------------------------------------------------------- */
void FixHyperLocal::min_pre_neighbor()
{
pre_neighbor();
}
/* ---------------------------------------------------------------------- */
void FixHyperLocal::build_bond_list(int natom)
{
int i,j,ii,jj,m,n,inum,jnum,nbond;
tagint itag,jtag;
double xtmp,ytmp,ztmp,delx,dely,delz,rsq,oldcoeff;
int *ilist,*jlist,*numneigh,**firstneigh;
double time1,time2;
time1 = MPI_Wtime();
if (natom) {
nevent++;
nevent_atom += natom;
}
// trigger Dcut neighbor list build
// NOTE: turn off special bonds in this Dcut neigh list?
neighbor->build_one(list);
// make copy of old bonds to preserve boostcoeffs for bonds that persist
// allocate new numbond
OneBond **old_bonds = bonds;
int *old_numbond = numbond;
int nmax = atom->nmax;
memory->create(numbond,nmax,"hyper/local:numbond");
// old_nall = value of nall at time bonds are built
// reallocate new xold and tagold if necessary
// initialize xold to current coords
// initialize tagold to zero, so atoms not in neighbor list will remain zero
old_nall = atom->nlocal + atom->nghost;
if (old_nall > maxold) {
memory->destroy(xold);
memory->destroy(tagold);
memory->destroy(old2now);
maxold = atom->nmax;
memory->create(xold,maxold,3,"hyper/local:xold");
memory->create(tagold,maxold,"hyper/local:tagold");
memory->create(old2now,maxold,"hyper/local:old2now");
}
double **x = atom->x;
memcpy(&xold[0][0],&x[0][0],3*old_nall*sizeof(double));
for (i = 0; i < old_nall; i++) tagold[i] = 0;
// create and populate new bonds data struct
// while loop allows maxbondperatom to increase once if necessary
// don't know new maxbondperatom value until end of loop
// in practice maxbondperatom will hardly ever increase
// since there is a physical max value
tagint *tag = atom->tag;
int *mask = atom->mask;
int nlocal = atom->nlocal;
inum = list->inum;
ilist = list->ilist;
numneigh = list->numneigh;
firstneigh = list->firstneigh;
while (1) {
bonds = (OneBond **) memory->create(bonds,nmax,maxbondperatom,
"hyper/local:bonds");
if (bonds) memset(bonds[0],0,nmax*sizeof(OneBond));
for (i = 0; i < nlocal; i++) numbond[i] = 0;
// identify bonds assigned to each owned atom
// do not create a bond between two non-group atoms
// set tagold = global ID for all I,J atoms used in neighbor list
// tagold remains 0 for unused atoms, skipped in pre_neighbor
int nbondmax = 0;
for (ii = 0; ii < inum; ii++) {
i = ilist[ii];
xtmp = x[i][0];
ytmp = x[i][1];
ztmp = x[i][2];
itag = tag[i];
tagold[i] = tag[i];
jlist = firstneigh[i];
jnum = numneigh[i];
nbond = 0;
for (jj = 0; jj < jnum; jj++) {
j = jlist[jj];
j &= NEIGHMASK;
jtag = tag[j];
tagold[j] = jtag;
// skip if neither atom I or J are in fix group
// order IJ to insure IJ and JI bonds are stored consistently
if (!(mask[i] & groupbit) && !(mask[j] & groupbit)) continue;
if (itag < jtag) {
delx = xtmp - x[j][0];
dely = ytmp - x[j][1];
delz = ztmp - x[j][2];
} else {
delx = x[j][0] - xtmp;
dely = x[j][1] - ytmp;
delz = x[j][2] - ztmp;
}
rsq = delx*delx + dely*dely + delz*delz;
// NOTE: could create two bonds for IJ both owned from one calc?
// have to skip one of 2 bonds in that case
if (rsq < cutbondsq) {
if (nbond >= maxbondperatom) {
nbond++;
continue;
}
bonds[i][nbond].r0 = sqrt(rsq);
bonds[i][nbond].jtag = tag[j];
bonds[i][nbond].j = j;
if (firstflag) oldcoeff = 0.0;
else {
oldcoeff = 0.0;
jtag = tag[j];
n = old_numbond[i];
for (m = 0; m < n; m++) {
if (old_bonds[i][m].jtag == jtag) {
oldcoeff = old_bonds[i][m].boostcoeff;
break;
}
}
}
if (oldcoeff > 0.0) bonds[i][nbond].boostcoeff = oldcoeff;
else {
bonds[i][nbond].boostcoeff = BOOSTINIT;
nnewbond++;
}
nbond++;
}
}
numbond[i] = nbond;
nbondmax = MAX(nbondmax,nbond);
}
// maxbondperatom must increase uniformly on all procs
// since bonds are comunicated when atoms migrate
int allnbondmax;
MPI_Allreduce(&nbondmax,&allnbondmax,1,MPI_INT,MPI_MAX,world);
if (allnbondmax <= maxbondperatom) break;
maxbondperatom = allnbondmax;
memory->destroy(bonds);
}
// deallocate old_bonds and old_numbond
memory->destroy(old_bonds);
memory->destroy(old_numbond);
time2 = MPI_Wtime();
if (firstflag) nnewbond = 0;
else {
time_bondbuild += time2-time1;
nbondbuild++;
}
firstflag = 0;
}
/* ---------------------------------------------------------------------- */
int FixHyperLocal::pack_forward_comm(int n, int *list, double *buf,
int /* pbc_flag */, int * /* pbc */)
{
int i,j,m;
m = 0;
// STRAIN
// pack maxstrain vector
if (commflag == STRAIN) {
for (i = 0; i < n; i++) {
j = list[i];
buf[m++] = maxstrain[j];
}
// STRAINREGION
// pack maxstrain_region vector
} else if (commflag == STRAINREGION) {
for (i = 0; i < n; i++) {
j = list[i];
buf[m++] = maxstrain_region[j];
}
// BIASFLAG
// pack biasflag vector
} else if (commflag == BIASFLAG) {
for (i = 0; i < n; i++) {
j = list[i];
buf[m++] = ubuf(biasflag[j]).d;
}
}
return m;
}
/* ---------------------------------------------------------------------- */
void FixHyperLocal::unpack_forward_comm(int n, int first, double *buf)
{
int i,m,last;
m = 0;
last = first + n;
// STRAIN
// unpack maxstrain vector
if (commflag == STRAIN) {
for (i = first; i < last; i++) {
maxstrain[i] = buf[m++];
}
// STRAINREGION
// unpack maxstrain_region vector
} else if (commflag == STRAINREGION) {
for (i = first; i < last; i++) {
maxstrain_region[i] = buf[m++];
}
// BIASFLAG
// unpack biasflag vector
} else if (commflag == BIASFLAG) {
for (i = first; i < last; i++) {
biasflag[i] = (tagint) ubuf(buf[m++]).i;
}
}
}
/* ---------------------------------------------------------------------- */
int FixHyperLocal::pack_reverse_comm(int n, int first, double *buf)
{
int i,m,last;
m = 0;
last = first + n;
// STRAINREGION
// pack maxstrain_region vector
if (commflag == STRAINREGION) {
for (i = first; i < last; i++) {
buf[m++] = maxstrain_region[i];
}
// BIASFLAG
// pack biasflag vector
} else if (commflag == BIASFLAG) {
for (i = first; i < last; i++) {
buf[m++] = ubuf(biasflag[i]).d;
}
}
return m;
}
/* ---------------------------------------------------------------------- */
void FixHyperLocal::unpack_reverse_comm(int n, int *list, double *buf)
{
int i,j,m;
m = 0;
// STRAINREGION
// unpack maxstrain_region vector
if (commflag == STRAINREGION) {
for (i = 0; i < n; i++) {
j = list[i];
maxstrain_region[j] += buf[m++];
}
// BIASFLAG
// unpack biasflag vector
} else if (commflag == BIASFLAG) {
for (i = 0; i < n; i++) {
j = list[i];
biasflag[j] = (tagint) ubuf(buf[m++]).i;
}
}
}
/* ----------------------------------------------------------------------
allocate atom-based arrays
------------------------------------------------------------------------- */
void FixHyperLocal::grow_arrays(int nmax)
{
// NOTE: not all of these need to be Nmax in length, could allocate elsewhere
memory->grow(maxstrain,nmax,"hyper/local:maxstrain");
memory->grow(maxstrain_bondindex,nmax,"hyper/local:maxstrain_bondindex");
memory->grow(maxstrain_region,nmax,"hyper/local:maxstrain_region");
if (checkbias) memory->grow(biasflag,nmax,"hyper/local:biasflag");
memory->grow(numbond,nmax,"hyper/local:numbond");
memory->grow(bonds,nmax,maxbondperatom,"hyper/local:bonds");
// zero so valgrind does not complain about memcpy() in copy()
// also so loops in pre_neighbor() are OK before
// bonds are setup for the first time
if (bonds) {
memset(bonds[maxbond],0,(nmax-maxbond)*maxbondperatom*sizeof(OneBond));
memset(&numbond[maxbond],0,(nmax-maxbond)*sizeof(int));
maxbond = nmax;
}
}
/* ----------------------------------------------------------------------
copy values within local atom-based arrays
------------------------------------------------------------------------- */
void FixHyperLocal::copy_arrays(int i, int j, int /* delflag */)
{
// avoid valgrind copy-to-self warning
if (i != j) memcpy(bonds[j],bonds[i],numbond[i]*sizeof(OneBond));
numbond[j] = numbond[i];
}
/* ----------------------------------------------------------------------
pack values in local atom-based array for exchange with another proc
------------------------------------------------------------------------- */
int FixHyperLocal::pack_exchange(int i, double *buf)
{
int m = 1;
int n = numbond[i];
buf[m++] = ubuf(n).d;
for (int j = 0; j < n; j++) {
buf[m++] = bonds[i][j].r0;
buf[m++] = bonds[i][j].boostcoeff;
buf[m++] = ubuf(bonds[i][j].jtag).d;
buf[m++] = ubuf(bonds[i][j].j).d;
}
buf[0] = m;
return m;
}
/* ----------------------------------------------------------------------
unpack values in local atom-based array from exchange with another proc
------------------------------------------------------------------------- */
int FixHyperLocal::unpack_exchange(int nlocal, double *buf)
{
int m = 1;
int n = numbond[nlocal] = (int) ubuf(buf[m++]).i;
for (int j = 0; j < n; j++) {
bonds[nlocal][j].r0 = buf[m++];
bonds[nlocal][j].boostcoeff = buf[m++];
bonds[nlocal][j].jtag = (tagint) ubuf(buf[m++]).i;
bonds[nlocal][j].j = (int) ubuf(buf[m++]).i;
}
return m;
}
/* ---------------------------------------------------------------------- */
double FixHyperLocal::compute_scalar()
{
double allbias;
MPI_Allreduce(&mybias,&allbias,1,MPI_DOUBLE,MPI_SUM,world);
return allbias;
}
/* ---------------------------------------------------------------------- */
double FixHyperLocal::compute_vector(int i)
{
// 23 vector outputs returned for i = 0-22
// i = 0 = # of boosted bonds on this step
// i = 1 = max strain of any bond on this step
// i = 2 = average bias potential for all bonds on this step
// i = 3 = ave bonds/atom on this step
// i = 4 = ave neighbor bonds/bond on this step
// i = 5 = fraction of steps and bonds with no bias during this run
// i = 6 = max drift distance of any atom during this run
// i = 7 = max bond length during this run
// i = 8 = average # of boosted bonds/step during this run
// i = 9 = average bias potential for all bonds during this run
// i = 10 = max bias potential for any bond during this run
// i = 11 = min bias potential for any bond during this run
// i = 12 = max dist from my box of any ghost atom with
// maxstain < qfactor during this run
// i = 13 = max dist from my box of any ghost atom with
// any maxstrain during this run
// i = 14 = count of ghost atoms that could not be found
// by any proc at any reneighbor step during this run
// i = 15 = count of lost bond partners during this run
// i = 16 = average bias coeff for lost bond partners during this run
// i = 17 = count of bias overlaps found during this run
// i = 18 = count of non-matching bias coefficients found during this run
// i = 19 = cummulative hyper time
// i = 20 = cummulative # of event timesteps since fix created
// i = 21 = cummulative # of atoms in events since fix created
// i = 22 = cummulative # of new bonds formed since fix created
if (i == 0) {
int nboostall;
MPI_Allreduce(&nboost,&nboostall,1,MPI_INT,MPI_SUM,world);
return (double) nboostall;
}
if (i == 1) {
if (nostrainyet) return 0.0;
int nlocal = atom->nlocal;
double emax = 0.0;
for (int j = 0; j < nlocal; j++)
emax = MAX(emax,maxstrain[j]);
double eall;
MPI_Allreduce(&emax,&eall,1,MPI_DOUBLE,MPI_MAX,world);
return eall;
}
if (i == 2) {
if (allboost && allbonds) return allboost/allbonds * vmax;
return 1.0;
}
if (i == 3) return 1.0*allbonds/atom->natoms;
if (i == 4) {
int nlocal = atom->nlocal;
int nbonds = 0; // BIGINT?
for (int j = 0; j < nlocal; j++)
nbonds += numbond[j];
int allbonds;
MPI_Allreduce(&nbonds,&allbonds,1,MPI_INT,MPI_SUM,world);
int allneigh; // BIGINT?
MPI_Allreduce(&list->ipage->ndatum,&allneigh,1,MPI_INT,MPI_SUM,world);
double neighsperatom = allneigh/atom->natoms;
double bondsperatom = 0.5*allbonds/atom->natoms;
return neighsperatom * bondsperatom;
}
if (i == 5) {
int allboost_running,allnobias_running;
MPI_Allreduce(&nboost_running,&allboost_running,1,MPI_INT,MPI_SUM,world);
MPI_Allreduce(&nobias_running,&allnobias_running,1,MPI_INT,MPI_SUM,world);
if (allboost_running) return 1.0*allnobias_running / allboost_running;
return 0.0;
}
if (i == 6) {
double alldriftsq;
MPI_Allreduce(&maxdriftsq,&alldriftsq,1,MPI_DOUBLE,MPI_MAX,world);
return (double) sqrt(alldriftsq);
}
if (i == 7) {
double allbondlen;
MPI_Allreduce(&maxbondlen,&allbondlen,1,MPI_DOUBLE,MPI_MAX,world);
return allbondlen;
}
if (i == 8) {
if (update->ntimestep == update->firststep) return 0.0;
int allboost_running;
MPI_Allreduce(&nboost_running,&allboost_running,1,MPI_INT,MPI_SUM,world);
return 1.0*allboost_running / (update->ntimestep - update->firststep);
}
if (i == 9) {
if (update->ntimestep == update->firststep) return 0.0;
return sumboostcoeff * vmax / (update->ntimestep - update->firststep);
}
if (i == 10) {
double allboostcoeff;
MPI_Allreduce(&maxboostcoeff,&allboostcoeff,1,MPI_DOUBLE,MPI_MAX,world);
return allboostcoeff * vmax;
}
if (i == 11) {
double allboostcoeff;
MPI_Allreduce(&minboostcoeff,&allboostcoeff,1,MPI_DOUBLE,MPI_MAX,world);
return allboostcoeff * vmax;
}
if (i == 12) return rmaxever;
if (i == 13) return rmaxeverbig;
if (i == 14) {
int allghost_toofar;
MPI_Allreduce(&ghost_toofar,&allghost_toofar,1,MPI_INT,MPI_SUM,world);
return 1.0*allghost_toofar;
}
if (i == 15) {
int alllost;
MPI_Allreduce(&lostbond_partner,&alllost,1,MPI_INT,MPI_SUM,world);
return 1.0*alllost;
}
if (i == 16) {
int alllost;
MPI_Allreduce(&lostbond_partner,&alllost,1,MPI_INT,MPI_SUM,world);
double allcoeff;
MPI_Allreduce(&lostbond_coeff,&allcoeff,1,MPI_DOUBLE,MPI_SUM,world);
if (alllost == 0) return 0;
return allcoeff/alllost;
}
if (i == 17) {
int allclose;
MPI_Allreduce(&checkbias_count,&allclose,1,MPI_INT,MPI_SUM,world);
return 1.0*allclose;
}
if (i == 18) {
int allcoeff;
MPI_Allreduce(&checkcoeff_count,&allcoeff,1,MPI_INT,MPI_SUM,world);
return 1.0*allcoeff;
}
if (i == 19) {
return boosttarget * update->dt * (update->ntimestep - starttime);
}
if (i == 20) return (double) nevent;
if (i == 21) return (double) nevent_atom;
if (i == 22) {
int allnew;
MPI_Allreduce(&nnewbond,&allnew,1,MPI_INT,MPI_SUM,world);
return (double) 0.5*allnew;
}
return 0.0;
}
/* ----------------------------------------------------------------------
wrapper on compute_vector()
used by hyper.cpp to call FixHyper
------------------------------------------------------------------------- */
double FixHyperLocal::query(int i)
{
if (i == 1) return compute_vector(19); // cummulative hyper time
if (i == 2) return compute_vector(20); // nevent
if (i == 3) return compute_vector(21); // nevent_atom
if (i == 4) return compute_vector(3); // ave bonds/atom
if (i == 5) return compute_vector(6); // maxdrift
if (i == 6) return compute_vector(7); // maxbondlen
if (i == 7) return compute_vector(5); // fraction with zero bias
// unique to local hyper
if (i == 8) return compute_vector(22); // number of new bonds
if (i == 9) return 1.0*maxbondperatom; // max bonds/atom
if (i == 10) return compute_vector(8); // ave # of boosted bonds/step
if (i == 11) return compute_vector(9); // ave boost coeff over all bonds
if (i == 12) return compute_vector(10); // max boost cooef for any bond
if (i == 13) return compute_vector(11); // max boost cooef for any bond
if (i == 14) return compute_vector(4); // neighbor bonds/bond
if (i == 15) return compute_vector(2); // ave boost cooef now
if (i == 16) return time_bondbuild; // CPU time for bond_build calls
if (i == 17) return rmaxever; // ghost atom distance for < maxstrain
if (i == 18) return rmaxeverbig; // ghost atom distance for any strain
if (i == 19) return compute_vector(14); // count of ghost atoms not found
if (i == 20) return compute_vector(15); // count of lost bond partners
if (i == 21) return compute_vector(16); // ave bias coeff of long bonds
if (i == 22) return compute_vector(17); // count of bias overlaps
if (i == 23) return compute_vector(18); // count of non-matching bias coeffs
error->all(FLERR,"Invalid query to fix hyper/local");
return 0.0;
}
/* ----------------------------------------------------------------------
memory usage of per-atom and per-bond arrays
------------------------------------------------------------------------- */
double FixHyperLocal::memory_usage()
{
int nmax = atom->nmax;
double bytes = 2*nmax * sizeof(int); // numbond, maxstrain_bondindex
bytes = 2*nmax * sizeof(double); // maxstrain, maxstrain_region
bytes += maxbondperatom*nmax * sizeof(OneBond); // bonds
bytes += 3*maxold * sizeof(double); // xold
bytes += maxold * sizeof(tagint); // tagold
bytes += maxold * sizeof(int); // old2now
return bytes;
}