Newer
Older
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
http://lammps.sandia.gov, Sandia National Laboratories
Steve Plimpton, sjplimp@sandia.gov
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: Pablo Piaggi (EPFL Lausanne)
------------------------------------------------------------------------- */
#include <cmath>
#include <cstring>
#include <cstdlib>
#include "atom.h"
#include "update.h"
#include "modify.h"
#include "neighbor.h"
#include "neigh_list.h"
#include "neigh_request.h"
#include "force.h"
#include "pair.h"
#include "comm.h"
#include "math_extra.h"
#include "math_const.h"
#include "memory.h"
#include "error.h"
#include "domain.h"
using namespace LAMMPS_NS;
using namespace MathConst;
/* ---------------------------------------------------------------------- */
ComputeEntropyAtom::
ComputeEntropyAtom(LAMMPS *lmp, int narg, char **arg) :
pair_entropy(NULL), pair_entropy_avg(NULL)
error->all(FLERR,"Illegal compute entropy/atom command; wrong number"
// Arguments are: sigma cutoff avg yes/no cutoff2 local yes/no
// sigma is the gaussian width
// cutoff is the cutoff for the calculation of g(r)
// avg is optional and allows averaging the pair entropy over neighbors
// cutoff2 is the cutoff for the averaging
// local is optional and allows using the local density to normalize
// the g(r)
sigma = force->numeric(FLERR,arg[3]);
if (sigma < 0.0) error->all(FLERR,"Illegal compute entropy/atom"
cutoff = force->numeric(FLERR,arg[4]);
if (cutoff < 0.0) error->all(FLERR,"Illegal compute entropy/atom"
// optional keywords
int iarg = 5;
while (iarg < narg) {
if (strcmp(arg[iarg],"avg") == 0) {
if (iarg+2 > narg)
error->all(FLERR,"Illegal compute entropy/atom;"
if (strcmp(arg[iarg+1],"yes") == 0) avg_flag = 1;
else if (strcmp(arg[iarg+1],"no") == 0) avg_flag = 0;
else error->all(FLERR,"Illegal compute entropy/atom;"
" argument after avg should be yes or no");
cutoff2 = force->numeric(FLERR,arg[iarg+2]);
if (cutoff2 < 0.0) error->all(FLERR,"Illegal compute entropy/atom"
} else if (strcmp(arg[iarg],"local") == 0) {
if (strcmp(arg[iarg+1],"yes") == 0) local_flag = 1;
else if (strcmp(arg[iarg+1],"no") == 0) local_flag = 0;
else error->all(FLERR,"Illegal compute entropy/atom;"
" argument after local should be yes or no");
} else error->all(FLERR,"Illegal compute entropy/atom; argument after"
" sigma and cutoff should be avg or local");
}
cutsq = cutoff*cutoff;
nbin = static_cast<int>(cutoff / sigma) + 1;
nmax = 0;
maxneigh = 0;
// Number of bins above and below the central one that will be
// considered as affected by the gaussian kernel
// 3 seems a good compromise between speed and good mollification
deltabin = 3;
peratom_flag = 1;
size_peratom_cols = 0;
}
/* ---------------------------------------------------------------------- */
{
memory->destroy(pair_entropy);
if (avg_flag) memory->destroy(pair_entropy_avg);
}
/* ---------------------------------------------------------------------- */
error->all(FLERR,"Compute centro/atom requires a pair style be"
" defined");
if ( (cutoff+cutoff2) > (force->pair->cutforce + neighbor->skin) )
error->all(FLERR,"Compute entropy/atom cutoff is longer than the"
" pairwise cutoff. Increase the neighbor list skin"
" distance.");
int count = 0;
for (int i = 0; i < modify->ncompute; i++)
if (strcmp(modify->compute[i]->style,"entropy/atom") == 0) count++;
error->warning(FLERR,"More than one compute entropy/atom");
// need a full neighbor list with neighbors of the ghost atoms
int irequest = neighbor->request(this,instance_me);
neighbor->requests[irequest]->pair = 0;
neighbor->requests[irequest]->compute = 1;
neighbor->requests[irequest]->half = 0;
neighbor->requests[irequest]->full = 1;
neighbor->requests[irequest]->occasional = 0;
neighbor->requests[irequest]->ghost = 1;
}
/* ---------------------------------------------------------------------- */
void ComputeEntropyAtom::init_list(int id, NeighList *ptr)
{
list = ptr;
}
/* ---------------------------------------------------------------------- */
int i,j,ii,jj,inum,jnum;
double xtmp,ytmp,ztmp,delx,dely,delz,rsq;
int *ilist,*jlist,*numneigh,**firstneigh;
double *rbin = new double[nbin];
double *rbinsq = new double[nbin];
invoked_peratom = update->ntimestep;
// Initialize distance vectors
for (int i = 0; i < nbin; i++) {
rbin[i] = i*deltar;
rbinsq[i] = rbin[i]*rbin[i];
}
// grow pair_entropy and pair_entropy_avg array if necessary
if (!avg_flag) {
memory->destroy(pair_entropy);
nmax = atom->nmax;
memory->create(pair_entropy,nmax,"entropy/atom:pair_entropy");
vector_atom = pair_entropy;
} else {
memory->destroy(pair_entropy);
memory->destroy(pair_entropy_avg);
nmax = atom->nmax;
memory->create(pair_entropy,nmax,"entropy/atom:pair_entropy");
memory->create(pair_entropy_avg,nmax,
vector_atom = pair_entropy_avg;
}
inum = list->inum + list->gnum;
ilist = list->ilist;
numneigh = list->numneigh;
firstneigh = list->firstneigh;
// Compute some constants
double sigmasq2=2*sigma*sigma;
double volume = domain->xprd * domain->yprd * domain->zprd;
double density = atom->natoms / volume;
// compute pair entropy for each atom in group
// use full neighbor list
double **x = atom->x;
int *mask = atom->mask;
for (ii = 0; ii < inum; ii++) {
i = ilist[ii];
if (mask[i] & groupbit) {
xtmp = x[i][0];
ytmp = x[i][1];
ztmp = x[i][2];
jlist = firstneigh[i];
jnum = numneigh[i];
// If local density is used, calculate it
if (local_flag) {
double neigh_cutoff = force->pair->cutforce + neighbor->skin;
(4./3.)*MY_PI*neigh_cutoff*neigh_cutoff*neigh_cutoff;
// calculate kernel normalization
double invNormConstantBase = 1./normConstantBase;
// loop over list of all neighbors within force cutoff
double *gofr = new double[nbin];
for(int k=0;k<nbin;++k) gofr[k]=0.;
for (jj = 0; jj < jnum; jj++) {
j = jlist[jj];
j &= NEIGHMASK;
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) {
// contribute to gofr
double r=sqrt(rsq);
int bin=floor(r/deltar);
minbin=bin - deltabin;
if (minbin < 0) minbin=0;
if (minbin > (nbin-1)) minbin=nbin-1;
maxbin=bin + deltabin;
if (maxbin > (nbin-1)) maxbin=nbin-1;
for(int k=minbin;k<maxbin+1;k++) {
double invNormKernel=invNormConstantBase/rbinsq[k];
gofr[k] += invNormKernel*exp(-distance*distance/sigmasq2);
double *integrand = new double[nbin];
for(int k=0;k<nbin;++k){
if (gofr[k]<1.e-10) {
integrand[k] = rbinsq[k];
} else {
integrand[k] = (gofr[k]*log(gofr[k])-gofr[k]+1)*rbinsq[k];
}
}
delete [] gofr;
// Integrate with trapezoid rule
double value = 0.;
for(int k=1;k<nbin-1;++k){
value += integrand[k];
}
value += 0.5*integrand[0];
value += 0.5*integrand[nbin-1];
value *= deltar;
delete [] integrand;
pair_entropy[i] = -2*MY_PI*density*value;
}
if (avg_flag) {
for (ii = 0; ii < inum; ii++) {
i = ilist[ii];
if (mask[i] & groupbit) {
xtmp = x[i][0];
ytmp = x[i][1];
ztmp = x[i][2];
jlist = firstneigh[i];
jnum = numneigh[i];
pair_entropy_avg[i] = pair_entropy[i];
double counter = 1;
// loop over list of all neighbors within force cutoff
for (jj = 0; jj < jnum; jj++) {
j = jlist[jj];
j &= NEIGHMASK;
delx = xtmp - x[j][0];
dely = ytmp - x[j][1];
delz = ztmp - x[j][2];
rsq = delx*delx + dely*dely + delz*delz;
if (rsq < cutsq2) {
pair_entropy_avg[i] += pair_entropy[j];
counter += 1;
}
}
pair_entropy_avg[i] /= counter;
}
}
}
delete [] rbin;
delete [] rbinsq;
}
/* ----------------------------------------------------------------------
memory usage of local atom-based array
------------------------------------------------------------------------- */
double bytes;
if (!avg_flag) {
bytes = nmax * sizeof(double);
} else {
bytes = 2 * nmax * sizeof(double);
}