/* ---------------------------------------------------------------------- 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. ------------------------------------------------------------------------- */ #include "compute_collision_contact.h" #include #include "atom.h" #include "update.h" #include "modify.h" #include "neighbor.h" #include "neigh_list.h" #include "neigh_request.h" #include "force.h" #include "comm.h" #include "memory.h" #include "error.h" #include #include "group.h" using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ ComputeCollisionContact::ComputeCollisionContact(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg), contact(NULL), contactall(NULL), contactPerAtom(NULL), radiuses(NULL), atomChunkID(NULL), neighbourChunk(NULL) { if (narg < 5) error->all(FLERR,"Illegal compute collision/contact command"); if (narg - 5 != atom->ntypes * 2) error->all(FLERR,"Illegal compute collision/contact command. " "Number of types does not match the given radiuses"); memory->create(radiuses,atom->ntypes,"collision/contact:radiuses"); startstep = 0; int iarg = 3; while (iarg < narg) { if (strcmp(arg[iarg],"start") == 0) { startstep = force->inumeric(FLERR,arg[iarg+1]); startstep--; iarg += 2; } else { radiuses[force->inumeric(FLERR,arg[iarg])-1] = force->numeric(FLERR,arg[iarg+1]); iarg += 2; } } if (startstep == 0) error->all(FLERR,"Start time must be specified for collision/contact compute"); peratom_flag = 1; size_peratom_cols = 0; comm_reverse = 1; nmax = 0; nmol = 0; MPI_Comm_size(world,&nprocs); } /* ---------------------------------------------------------------------- */ ComputeCollisionContact::~ComputeCollisionContact() { memory->destroy(contact); memory->destroy(contactall); memory->destroy(contactPerAtom); memory->destroy(radiuses); memory->destroy(atomChunkID); memory->destroy(neighbourChunk); } /* ---------------------------------------------------------------------- */ void ComputeCollisionContact::init() { if (force->pair == NULL) error->all(FLERR,"Compute coord/atom requires a pair style be defined"); int count = 0; for (int i = 0; i < modify->ncompute; i++) if (strcmp(modify->compute[i]->style,"collision/contact") == 0) count++; if (count > 1 && comm->me == 0) error->warning(FLERR,"More than one compute collision/contact"); int ifix = modify->find_fix_by_style("reax/c/species"); if (ifix < 0) error->all(FLERR,"reax/c/species fix does not exist for " "compute collision/contact"); // need an occasional neighbor list 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 = 1; } /* ---------------------------------------------------------------------- */ void ComputeCollisionContact::init_list(int /*id*/, NeighList *ptr) { list = ptr; } /* ---------------------------------------------------------------------- */ void ComputeCollisionContact::compute_peratom() { int i,j,ii,jj,inum,jnum; double xtmp,ytmp,ztmp,delx,dely,delz,rsq; double radi,radisq; int *ilist,*jlist,*numneigh,**firstneigh; invoked_peratom = update->ntimestep; if (atom->nmax > nmax) { memory->destroy(contactPerAtom); nmax = atom->nmax; memory->create(contactPerAtom,nmax,"c/c:contactPerAtom"); vector_atom = contactPerAtom; memory->create(atomChunkID,nmax,"c/c:atomChunkID"); } for (i = 0; i < nmax; i++) contactPerAtom[i] = 0.0; // avoid memory fault due to uninitialized reax/c/species class if (update->ntimestep > startstep) { // invoke neighbor list (will copy or build if necessary) neighbor->build_one(list); inum = list->inum; ilist = list->ilist; numneigh = list->numneigh; firstneigh = list->firstneigh; // create the chunk arrays reaxc = NULL; reaxc = (PairReaxC *) force->pair_match("^reax/c",0); int *spatomID, *spclusterID; spclusterID = reaxc->ke_temp_compute->compute_clusterID; spatomID = reaxc->ke_temp_compute->compute_atomID; nchunk = reaxc->ke_temp_compute->totalMol; // obtain current nchunk of each atom int natoms = atom->natoms; int ntotal = atom->nlocal + atom->nghost; for (i = 0; i < ntotal; i++) { for (j = 0; j < natoms; j++) { if (atom->tag[i] == spatomID[j]) { atomChunkID[i] = spclusterID[j]; } } } if (nchunk != nmol) { nmol = nchunk; memory->destroy(neighbourChunk); memory->destroy(contact); memory->destroy(contactall); memory->create(contact,nchunk,"c/c:contact"); memory->create(contactall,nchunk,"c/c:contactall"); memory->create(neighbourChunk,nchunk,nchunk,"c/c:neighbourChunk"); } // neighbourChunk stores 1/0 for each chunk of atom i if a collision was/wasn't counted for // the chunk of atom j for (i = 0; i < nchunk; i++) { for (j = 0; j < nchunk; j++) { neighbourChunk[i][j] = 0; } } // compute number of contacts for each atom in group // collision if distance <= sum of radii // tally for both I and J // i,j local indeces double **x = atom->x; int *mask = atom->mask; int chunksumIDnow; for (i = 0; i < nchunk; i++) contact[i] = 0.0; 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]; for (jj = 0; jj < jnum; jj++) { j = jlist[jj]; j &= NEIGHMASK; if (atomChunkID[i] != atomChunkID[j]) { // if atom j does not belong in the same molecule as i if (neighbourChunk[atomChunkID[i]-1][atomChunkID[j]-1] == 0) { // check if collision with neighbour chunk has been already counted radi = radiuses[atom->type[i]-1]+radiuses[atom->type[j]-1]; radisq = radi*radi; delx = xtmp - x[j][0]; dely = ytmp - x[j][1]; delz = ztmp - x[j][2]; rsq = delx*delx + dely*dely + delz*delz; if (rsq <= radisq) { contact[atomChunkID[i]-1] += 1.0; neighbourChunk[atomChunkID[i]-1][atomChunkID[j]-1] = 1; } } } } } } // in case a molecule is located between two processors communicate the information of collisions // between all processors. Therefore, this will count all collisions even if half the molecule is // in ghost atoms for that processor MPI_Allreduce(contact,contactall,nchunk,MPI_DOUBLE,MPI_SUM,world); // chunk values to atoms for (i = 0; i < inum; i++) { contactPerAtom[i] = contactall[atomChunkID[i]-1]; } } // communicate ghost atom counts between neighbor procs if necessary if (force->newton_pair) comm->reverse_comm_compute(this); } /* ---------------------------------------------------------------------- */ int ComputeCollisionContact::pack_reverse_comm(int n, int first, double *buf) { int i,m,last; m = 0; last = first + n; for (i = first; i < last; i++) buf[m++] = contactPerAtom[i]; return m; } /* ---------------------------------------------------------------------- */ void ComputeCollisionContact::unpack_reverse_comm(int n, int *list, double *buf) { int i,j,m; m = 0; for (i = 0; i < n; i++) { j = list[i]; contactPerAtom[j] += buf[m++]; } } /* ---------------------------------------------------------------------- memory usage of local atom-based array ------------------------------------------------------------------------- */ double ComputeCollisionContact::memory_usage() { double bytes = nmax * sizeof(double); return bytes; }