diff --git a/src/GRANULAR/fix_pour.cpp b/src/GRANULAR/fix_pour.cpp index 27bf4fc70..9947d9a84 100644 --- a/src/GRANULAR/fix_pour.cpp +++ b/src/GRANULAR/fix_pour.cpp @@ -1,1038 +1,1038 @@ /* ---------------------------------------------------------------------- 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 "math.h" #include "stdlib.h" #include "string.h" #include "fix_pour.h" #include "atom.h" #include "atom_vec.h" #include "force.h" #include "update.h" #include "comm.h" #include "molecule.h" #include "modify.h" #include "fix_gravity.h" #include "domain.h" #include "region.h" #include "region_block.h" #include "region_cylinder.h" #include "random_park.h" #include "math_extra.h" #include "math_const.h" #include "memory.h" #include "error.h" using namespace LAMMPS_NS; using namespace FixConst; using namespace MathConst; enum{ATOM,MOLECULE}; enum{ONE,RANGE,POLY}; enum{LAYOUT_UNIFORM,LAYOUT_NONUNIFORM,LAYOUT_TILED}; // several files #define EPSILON 0.001 /* ---------------------------------------------------------------------- */ FixPour::FixPour(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { if (narg < 6) error->all(FLERR,"Illegal fix pour command"); time_depend = 1; if (!atom->radius_flag || !atom->rmass_flag) error->all(FLERR,"Fix pour requires atom attributes radius, rmass"); // required args ninsert = force->inumeric(FLERR,arg[3]); ntype = force->inumeric(FLERR,arg[4]); seed = force->inumeric(FLERR,arg[5]); if (seed <= 0) error->all(FLERR,"Illegal fix pour command"); // read options from end of input line options(narg-6,&arg[6]); // error check on type if (mode == ATOM && (ntype <= 0 || ntype > atom->ntypes)) error->all(FLERR,"Invalid atom type in fix pour command"); // error checks on region and its extent being inside simulation box if (iregion == -1) error->all(FLERR,"Must specify a region in fix pour"); if (domain->regions[iregion]->bboxflag == 0) error->all(FLERR,"Fix pour region does not support a bounding box"); if (domain->regions[iregion]->dynamic_check()) error->all(FLERR,"Fix pour region cannot be dynamic"); if (strcmp(domain->regions[iregion]->style,"block") == 0) { region_style = 1; xlo = ((RegBlock *) domain->regions[iregion])->xlo; xhi = ((RegBlock *) domain->regions[iregion])->xhi; ylo = ((RegBlock *) domain->regions[iregion])->ylo; yhi = ((RegBlock *) domain->regions[iregion])->yhi; zlo = ((RegBlock *) domain->regions[iregion])->zlo; zhi = ((RegBlock *) domain->regions[iregion])->zhi; if (xlo < domain->boxlo[0] || xhi > domain->boxhi[0] || ylo < domain->boxlo[1] || yhi > domain->boxhi[1] || zlo < domain->boxlo[2] || zhi > domain->boxhi[2]) error->all(FLERR,"Insertion region extends outside simulation box"); } else if (strcmp(domain->regions[iregion]->style,"cylinder") == 0) { region_style = 2; char axis = ((RegCylinder *) domain->regions[iregion])->axis; xc = ((RegCylinder *) domain->regions[iregion])->c1; yc = ((RegCylinder *) domain->regions[iregion])->c2; rc = ((RegCylinder *) domain->regions[iregion])->radius; zlo = ((RegCylinder *) domain->regions[iregion])->lo; zhi = ((RegCylinder *) domain->regions[iregion])->hi; if (axis != 'z') error->all(FLERR,"Must use a z-axis cylinder region with fix pour"); if (xc-rc < domain->boxlo[0] || xc+rc > domain->boxhi[0] || yc-rc < domain->boxlo[1] || yc+rc > domain->boxhi[1] || zlo < domain->boxlo[2] || zhi > domain->boxhi[2]) error->all(FLERR,"Insertion region extends outside simulation box"); } else error->all(FLERR,"Must use a block or cylinder region with fix pour"); if (region_style == 2 && domain->dimension == 2) error->all(FLERR, "Must use a block region with fix pour for 2d simulations"); // error check and further setup for mode = MOLECULE if (atom->tag_enable == 0) error->all(FLERR,"Cannot use fix_pour unless atoms have IDs"); if (mode == MOLECULE) { for (int i = 0; i < nmol; i++) { if (onemols[i]->xflag == 0) error->all(FLERR,"Fix pour molecule must have coordinates"); if (onemols[i]->typeflag == 0) error->all(FLERR,"Fix pour molecule must have atom types"); if (ntype+onemols[i]->ntypes <= 0 || ntype+onemols[i]->ntypes > atom->ntypes) error->all(FLERR,"Invalid atom type in fix pour mol command"); if (atom->molecular == 2 && onemols != atom->avec->onemols) error->all(FLERR,"Fix pour molecule template ID must be same " "as atom style template ID"); onemols[i]->check_attributes(0); // fix pour uses geoemetric center of molecule for insertion onemols[i]->compute_center(); } } if (rigidflag && mode == ATOM) error->all(FLERR,"Cannot use fix pour rigid and not molecule"); if (shakeflag && mode == ATOM) error->all(FLERR,"Cannot use fix pour shake and not molecule"); if (rigidflag && shakeflag) error->all(FLERR,"Cannot use fix pour rigid and shake"); // setup of coords and imageflags array if (mode == ATOM) natom_max = 1; else { natom_max = 0; for (int i = 0; i < nmol; i++) natom_max = MAX(natom_max,onemols[i]->natoms); } memory->create(coords,natom_max,4,"pour:coords"); memory->create(imageflags,natom_max,"pour:imageflags"); // find current max atom and molecule IDs if necessary if (idnext) find_maxid(); // random number generator, same for all procs random = new RanPark(lmp,seed); // allgather arrays MPI_Comm_rank(world,&me); MPI_Comm_size(world,&nprocs); recvcounts = new int[nprocs]; displs = new int[nprocs]; // grav = gravity in distance/time^2 units // assume grav = -magnitude at this point, enforce in init() int ifix; for (ifix = 0; ifix < modify->nfix; ifix++) { if (strcmp(modify->fix[ifix]->style,"gravity") == 0) break; if (strcmp(modify->fix[ifix]->style,"gravity/omp") == 0) break; } if (ifix == modify->nfix) error->all(FLERR,"No fix gravity defined for fix pour"); grav = - ((FixGravity *) modify->fix[ifix])->magnitude * force->ftm2v; // nfreq = timesteps between insertions // should be time for a particle to fall from top of insertion region // to bottom, taking into account that the region may be moving // set these 2 eqs equal to each other, solve for smallest positive t // x = zhi + vz*t + 1/2 grav t^2 // x = zlo + rate*t // gives t = [-(vz-rate) - sqrt((vz-rate)^2 - 2*grav*(zhi-zlo))] / grav // where zhi-zlo > 0, grav < 0, and vz & rate can be either > 0 or < 0 double v_relative,delta; if (domain->dimension == 3) { v_relative = vz - rate; delta = zhi - zlo; } else { v_relative = vy - rate; delta = yhi - ylo; } double t = (-v_relative - sqrt(v_relative*v_relative - 2.0*grav*delta)) / grav; nfreq = static_cast<int> (t/update->dt + 0.5); // 1st insertion on next timestep force_reneighbor = 1; next_reneighbor = update->ntimestep + 1; nfirst = next_reneighbor; ninserted = 0; // nper = # to insert each time // depends on specified volume fraction // volume = volume of insertion region // volume_one = volume of inserted particle (with max possible radius) // in 3d, insure dy >= 1, for quasi-2d simulations - double volume,volume_one; + double volume,volume_one=1.0; molradius_max = 0.0; if (mode == MOLECULE) { for (int i = 0; i < nmol; i++) molradius_max = MAX(molradius_max,onemols[i]->molradius); } if (domain->dimension == 3) { if (region_style == 1) { double dy = yhi - ylo; if (dy < 1.0) dy = 1.0; volume = (xhi-xlo) * dy * (zhi-zlo); } else volume = MY_PI*rc*rc * (zhi-zlo); if (mode == MOLECULE) { volume_one = 4.0/3.0 * MY_PI * molradius_max*molradius_max*molradius_max; } else if (dstyle == ONE || dstyle == RANGE) { volume_one = 4.0/3.0 * MY_PI * radius_max*radius_max*radius_max; } else if (dstyle == POLY) { volume_one = 0.0; for (int i = 0; i < npoly; i++) volume_one += (4.0/3.0 * MY_PI * radius_poly[i]*radius_poly[i]*radius_poly[i]) * frac_poly[i]; } } else { volume = (xhi-xlo) * (yhi-ylo); if (mode == MOLECULE) { volume_one = MY_PI * molradius_max*molradius_max; } else if (dstyle == ONE || dstyle == RANGE) { volume_one = MY_PI * radius_max*radius_max; } else if (dstyle == POLY) { volume_one = 0.0; for (int i = 0; i < npoly; i++) volume_one += (MY_PI * radius_poly[i]*radius_poly[i]) * frac_poly[i]; } } nper = static_cast<int> (volfrac*volume/volume_one); int nfinal = update->ntimestep + 1 + (ninsert-1)/nper * nfreq; // print stats if (me == 0) { if (screen) fprintf(screen, "Particle insertion: %d every %d steps, %d by step %d\n", nper,nfreq,ninsert,nfinal); if (logfile) fprintf(logfile, "Particle insertion: %d every %d steps, %d by step %d\n", nper,nfreq,ninsert,nfinal); } } /* ---------------------------------------------------------------------- */ FixPour::~FixPour() { delete random; delete [] molfrac; delete [] idrigid; delete [] idshake; delete [] radius_poly; delete [] frac_poly; memory->destroy(coords); memory->destroy(imageflags); delete [] recvcounts; delete [] displs; } /* ---------------------------------------------------------------------- */ int FixPour::setmask() { int mask = 0; mask |= PRE_EXCHANGE; return mask; } /* ---------------------------------------------------------------------- */ void FixPour::init() { if (domain->triclinic) error->all(FLERR,"Cannot use fix pour with triclinic box"); // insure gravity fix exists // for 3d must point in -z, for 2d must point in -y // else insertion cannot work int ifix; for (ifix = 0; ifix < modify->nfix; ifix++) { if (strcmp(modify->fix[ifix]->style,"gravity") == 0) break; if (strcmp(modify->fix[ifix]->style,"gravity/omp") == 0) break; } if (ifix == modify->nfix) error->all(FLERR,"No fix gravity defined for fix pour"); double xgrav = ((FixGravity *) modify->fix[ifix])->xgrav; double ygrav = ((FixGravity *) modify->fix[ifix])->ygrav; double zgrav = ((FixGravity *) modify->fix[ifix])->zgrav; if (domain->dimension == 3) { if (fabs(xgrav) > EPSILON || fabs(ygrav) > EPSILON || fabs(zgrav+1.0) > EPSILON) error->all(FLERR,"Gravity must point in -z to use with fix pour in 3d"); } else { if (fabs(xgrav) > EPSILON || fabs(ygrav+1.0) > EPSILON || fabs(zgrav) > EPSILON) error->all(FLERR,"Gravity must point in -y to use with fix pour in 2d"); } double gnew = - ((FixGravity *) modify->fix[ifix])->magnitude * force->ftm2v; if (gnew != grav) error->all(FLERR,"Gravity changed since fix pour was created"); // if rigidflag defined, check for rigid/small fix // its molecule template must be same as this one fixrigid = NULL; if (rigidflag) { int ifix = modify->find_fix(idrigid); if (ifix < 0) error->all(FLERR,"Fix pour rigid fix does not exist"); fixrigid = modify->fix[ifix]; int tmp; if (onemols != (Molecule **) fixrigid->extract("onemol",tmp)) error->all(FLERR, "Fix pour and fix rigid/small not using " "same molecule template ID"); } // if shakeflag defined, check for SHAKE fix // its molecule template must be same as this one fixshake = NULL; if (shakeflag) { int ifix = modify->find_fix(idshake); if (ifix < 0) error->all(FLERR,"Fix pour shake fix does not exist"); fixshake = modify->fix[ifix]; int tmp; if (onemols != (Molecule **) fixshake->extract("onemol",tmp)) error->all(FLERR,"Fix pour and fix shake not using " "same molecule template ID"); } } /* ---------------------------------------------------------------------- perform particle insertion ------------------------------------------------------------------------- */ void FixPour::pre_exchange() { int i,j,m,flag,nlocalprev,imol,natom; double r[3],rotmat[3][3],quat[4],vnew[3]; double *newcoord; // just return if should not be called on this timestep if (next_reneighbor != update->ntimestep) return; // find current max atom and molecule IDs if necessary if (!idnext) find_maxid(); // nnew = # of particles (atoms or molecules) to insert this timestep int nnew = nper; if (ninserted + nnew > ninsert) nnew = ninsert - ninserted; // lo/hi current = z (or y) bounds of insertion region this timestep int dimension = domain->dimension; if (dimension == 3) { lo_current = zlo + (update->ntimestep - nfirst) * update->dt * rate; hi_current = zhi + (update->ntimestep - nfirst) * update->dt * rate; } else { lo_current = ylo + (update->ntimestep - nfirst) * update->dt * rate; hi_current = yhi + (update->ntimestep - nfirst) * update->dt * rate; } // ncount = # of my atoms that overlap the insertion region // nprevious = total of ncount across all procs int ncount = 0; for (i = 0; i < atom->nlocal; i++) if (overlap(i)) ncount++; int nprevious; MPI_Allreduce(&ncount,&nprevious,1,MPI_INT,MPI_SUM,world); // xmine is for my atoms // xnear is for atoms from all procs + atoms to be inserted double **xmine,**xnear; memory->create(xmine,ncount,4,"fix_pour:xmine"); memory->create(xnear,nprevious+nnew*natom_max,4,"fix_pour:xnear"); int nnear = nprevious; // setup for allgatherv int n = 4*ncount; MPI_Allgather(&n,1,MPI_INT,recvcounts,1,MPI_INT,world); displs[0] = 0; for (int iproc = 1; iproc < nprocs; iproc++) displs[iproc] = displs[iproc-1] + recvcounts[iproc-1]; // load up xmine array double **x = atom->x; double *radius = atom->radius; ncount = 0; for (i = 0; i < atom->nlocal; i++) if (overlap(i)) { xmine[ncount][0] = x[i][0]; xmine[ncount][1] = x[i][1]; xmine[ncount][2] = x[i][2]; xmine[ncount][3] = radius[i]; ncount++; } // perform allgatherv to acquire list of nearby particles on all procs double *ptr = NULL; if (ncount) ptr = xmine[0]; MPI_Allgatherv(ptr,4*ncount,MPI_DOUBLE, xnear[0],recvcounts,displs,MPI_DOUBLE,world); // insert new particles into xnear list, one by one // check against all nearby atoms and previously inserted ones // if there is an overlap then try again at same z (3d) or y (2d) coord // else insert by adding to xnear list // max = maximum # of insertion attempts for all particles // h = height, biased to give uniform distribution in time of insertion // for MOLECULE mode: // coords = coords of all atoms in particle // perform random rotation around center pt // apply PBC so final coords are inside box // store image flag modified due to PBC int success; double radtmp,delx,dely,delz,rsq,radsum,rn,h; double coord[3]; int nfix = modify->nfix; Fix **fix = modify->fix; double denstmp; double *sublo = domain->sublo; double *subhi = domain->subhi; int nsuccess = 0; int attempt = 0; int maxiter = nnew * maxattempt; while (nsuccess < nnew) { rn = random->uniform(); h = hi_current - rn*rn * (hi_current-lo_current); if (mode == ATOM) radtmp = radius_sample(); success = 0; while (attempt < maxiter) { attempt++; xyz_random(h,coord); if (mode == ATOM) { natom = 1; coords[0][0] = coord[0]; coords[0][1] = coord[1]; coords[0][2] = coord[2]; coords[0][3] = radtmp; imageflags[0] = ((imageint) IMGMAX << IMG2BITS) | ((imageint) IMGMAX << IMGBITS) | IMGMAX; } else { double rng = random->uniform(); imol = 0; while (rng > molfrac[imol]) imol++; natom = onemols[imol]->natoms; if (dimension == 3) { r[0] = random->uniform() - 0.5; r[1] = random->uniform() - 0.5; r[2] = random->uniform() - 0.5; } else { r[0] = r[1] = 0.0; r[2] = 1.0; } double theta = random->uniform() * MY_2PI; MathExtra::norm3(r); MathExtra::axisangle_to_quat(r,theta,quat); MathExtra::quat_to_mat(quat,rotmat); for (i = 0; i < natom; i++) { MathExtra::matvec(rotmat,onemols[imol]->dx[i],coords[i]); coords[i][0] += coord[0]; coords[i][1] += coord[1]; coords[i][2] += coord[2]; // coords[3] = particle radius // default to 0.5, if radii not defined in Molecule // same as atom->avec->create_atom(), invoked below if (onemols[imol]->radiusflag) coords[i][3] = onemols[imol]->radius[i]; else coords[i][3] = 0.5; imageflags[i] = ((imageint) IMGMAX << IMG2BITS) | ((imageint) IMGMAX << IMGBITS) | IMGMAX; domain->remap(coords[i],imageflags[i]); } } // if any pair of atoms overlap, try again // use minimum_image() to account for PBC for (m = 0; m < natom; m++) { for (i = 0; i < nnear; i++) { delx = coords[m][0] - xnear[i][0]; dely = coords[m][1] - xnear[i][1]; delz = coords[m][2] - xnear[i][2]; domain->minimum_image(delx,dely,delz); rsq = delx*delx + dely*dely + delz*delz; radsum = coords[m][3] + xnear[i][3]; if (rsq <= radsum*radsum) break; } if (i < nnear) break; } if (m == natom) { success = 1; break; } } if (!success) break; // proceed with insertion nsuccess++; nlocalprev = atom->nlocal; // add all atoms in particle to xnear for (m = 0; m < natom; m++) { xnear[nnear][0] = coords[m][0]; xnear[nnear][1] = coords[m][1]; xnear[nnear][2] = coords[m][2]; xnear[nnear][3] = coords[m][3]; nnear++; } // choose random velocity for new particle // used for every atom in molecule // z velocity set to what velocity would be if particle // had fallen from top of insertion region // this gives continuous stream of atoms // solution for v from these 2 eqs, after eliminate t: // v = vz + grav*t // coord[2] = hi_current + vz*t + 1/2 grav t^2 if (dimension == 3) { vnew[0] = vxlo + random->uniform() * (vxhi-vxlo); vnew[1] = vylo + random->uniform() * (vyhi-vylo); vnew[2] = -sqrt(vz*vz + 2.0*grav*(coord[2]-hi_current)); } else { vnew[0] = vxlo + random->uniform() * (vxhi-vxlo); vnew[1] = -sqrt(vy*vy + 2.0*grav*(coord[1]-hi_current)); vnew[2] = 0.0; } // check if new atoms are in my sub-box or above it if I am highest proc // if so, add atom to my list via create_atom() // initialize additional info about the atoms // set group mask to "all" plus fix group for (m = 0; m < natom; m++) { if (mode == ATOM) denstmp = density_lo + random->uniform() * (density_hi-density_lo); newcoord = coords[m]; flag = 0; if (newcoord[0] >= sublo[0] && newcoord[0] < subhi[0] && newcoord[1] >= sublo[1] && newcoord[1] < subhi[1] && newcoord[2] >= sublo[2] && newcoord[2] < subhi[2]) flag = 1; else if (dimension == 3 && newcoord[2] >= domain->boxhi[2]) { if (comm->layout != LAYOUT_TILED) { if (comm->myloc[2] == comm->procgrid[2]-1 && newcoord[0] >= sublo[0] && newcoord[0] < subhi[0] && newcoord[1] >= sublo[1] && newcoord[1] < subhi[1]) flag = 1; } else { if (comm->mysplit[2][1] == 1.0 && newcoord[0] >= sublo[0] && newcoord[0] < subhi[0] && newcoord[1] >= sublo[1] && newcoord[1] < subhi[1]) flag = 1; } } else if (dimension == 2 && newcoord[1] >= domain->boxhi[1]) { if (comm->layout != LAYOUT_TILED) { if (comm->myloc[1] == comm->procgrid[1]-1 && newcoord[0] >= sublo[0] && newcoord[0] < subhi[0]) flag = 1; } else { if (comm->mysplit[1][1] == 1.0 && newcoord[0] >= sublo[0] && newcoord[0] < subhi[0]) flag = 1; } } if (flag) { if (mode == ATOM) atom->avec->create_atom(ntype,coords[m]); else atom->avec->create_atom(ntype+onemols[imol]->type[m],coords[m]); int n = atom->nlocal - 1; atom->tag[n] = maxtag_all + m+1; if (mode == MOLECULE) { if (atom->molecule_flag) atom->molecule[n] = maxmol_all+1; if (atom->molecular == 2) { atom->molindex[n] = 0; atom->molatom[n] = m; } } atom->mask[n] = 1 | groupbit; atom->image[n] = imageflags[m]; atom->v[n][0] = vnew[0]; atom->v[n][1] = vnew[1]; atom->v[n][2] = vnew[2]; if (mode == ATOM) { radtmp = newcoord[3]; atom->radius[n] = radtmp; atom->rmass[n] = 4.0*MY_PI/3.0 * radtmp*radtmp*radtmp * denstmp; } else atom->add_molecule_atom(onemols[imol],m,n,maxtag_all); for (j = 0; j < nfix; j++) if (fix[j]->create_attribute) fix[j]->set_arrays(n); } } // FixRigidSmall::set_molecule stores rigid body attributes // coord is new position of geometric center of mol, not COM // FixShake::set_molecule stores shake info for molecule if (rigidflag) fixrigid->set_molecule(nlocalprev,maxtag_all,imol,coord,vnew,quat); else if (shakeflag) fixshake->set_molecule(nlocalprev,maxtag_all,imol,coord,vnew,quat); maxtag_all += natom; if (mode == MOLECULE && atom->molecule_flag) maxmol_all++; } // warn if not successful with all insertions b/c too many attempts int ninserted_atoms = nnear - nprevious; int ninserted_mols = ninserted_atoms / natom; ninserted += ninserted_mols; if (ninserted_mols < nnew && me == 0) error->warning(FLERR,"Less insertions than requested",0); // reset global natoms,nbonds,etc // increment maxtag_all and maxmol_all if necessary // if global map exists, reset it now instead of waiting for comm // since adding atoms messes up ghosts if (ninserted_atoms) { atom->natoms += ninserted_atoms; if (atom->natoms < 0 || atom->natoms > MAXBIGINT) error->all(FLERR,"Too many total atoms"); if (mode == MOLECULE) { atom->nbonds += onemols[imol]->nbonds * ninserted_mols; atom->nangles += onemols[imol]->nangles * ninserted_mols; atom->ndihedrals += onemols[imol]->ndihedrals * ninserted_mols; atom->nimpropers += onemols[imol]->nimpropers * ninserted_mols; } if (maxtag_all >= MAXTAGINT) error->all(FLERR,"New atom IDs exceed maximum allowed ID"); if (atom->map_style) { atom->nghost = 0; atom->map_init(); atom->map_set(); } } // free local memory memory->destroy(xmine); memory->destroy(xnear); // next timestep to insert if (ninserted < ninsert) next_reneighbor += nfreq; else next_reneighbor = 0; } /* ---------------------------------------------------------------------- maxtag_all = current max atom ID for all atoms maxmol_all = current max molecule ID for all atoms ------------------------------------------------------------------------- */ void FixPour::find_maxid() { tagint *tag = atom->tag; tagint *molecule = atom->molecule; int nlocal = atom->nlocal; tagint max = 0; for (int i = 0; i < nlocal; i++) max = MAX(max,tag[i]); MPI_Allreduce(&max,&maxtag_all,1,MPI_LMP_TAGINT,MPI_MAX,world); if (mode == MOLECULE && molecule) { max = 0; for (int i = 0; i < nlocal; i++) max = MAX(max,molecule[i]); MPI_Allreduce(&max,&maxmol_all,1,MPI_LMP_TAGINT,MPI_MAX,world); } } /* ---------------------------------------------------------------------- check if particle i could overlap with a particle inserted into region return 1 if yes, 0 if no for ATOM mode, use delta with maximum size for inserted atoms for MOLECULE mode, use delta with max radius of inserted molecules account for PBC in overlap decision via outside() and minimum_image() ------------------------------------------------------------------------- */ int FixPour::overlap(int i) { double delta; if (mode == ATOM) delta = atom->radius[i] + radius_max; else delta = atom->radius[i] + molradius_max; double *x = atom->x[i]; if (domain->dimension == 3) { if (region_style == 1) { if (outside(0,x[0],xlo-delta,xhi+delta)) return 0; if (outside(1,x[1],ylo-delta,yhi+delta)) return 0; if (outside(2,x[2],lo_current-delta,hi_current+delta)) return 0; } else { double delx = x[0] - xc; double dely = x[1] - yc; double delz = 0.0; domain->minimum_image(delx,dely,delz); double rsq = delx*delx + dely*dely; double r = rc + delta; if (rsq > r*r) return 0; if (outside(2,x[2],lo_current-delta,hi_current+delta)) return 0; } } else { if (outside(0,x[0],xlo-delta,xhi+delta)) return 0; if (outside(1,x[1],lo_current-delta,hi_current+delta)) return 0; } return 1; } /* ---------------------------------------------------------------------- check if value is inside/outside lo/hi bounds in dimension account for PBC if needed return 1 if value is outside, 0 if inside ------------------------------------------------------------------------- */ int FixPour::outside(int dim, double value, double lo, double hi) { double boxlo = domain->boxlo[dim]; double boxhi = domain->boxhi[dim]; if (domain->periodicity[dim]) { if (lo < boxlo && hi > boxhi) { return 0; } else if (lo < boxlo) { if (value > hi && value < lo + domain->prd[dim]) return 1; } else if (hi > boxhi) { if (value > hi - domain->prd[dim] && value < lo) return 1; } else { if (value < lo || value > hi) return 1; } } if (value < lo || value > hi) return 1; return 0; } /* ---------------------------------------------------------------------- */ void FixPour::xyz_random(double h, double *coord) { if (domain->dimension == 3) { if (region_style == 1) { coord[0] = xlo + random->uniform() * (xhi-xlo); coord[1] = ylo + random->uniform() * (yhi-ylo); coord[2] = h; } else { double r1,r2; while (1) { r1 = random->uniform() - 0.5; r2 = random->uniform() - 0.5; if (r1*r1 + r2*r2 < 0.25) break; } coord[0] = xc + 2.0*r1*rc; coord[1] = yc + 2.0*r2*rc; coord[2] = h; } } else { coord[0] = xlo + random->uniform() * (xhi-xlo); coord[1] = h; coord[2] = 0.0; } } /* ---------------------------------------------------------------------- */ double FixPour::radius_sample() { if (dstyle == ONE) return radius_one; if (dstyle == RANGE) return radius_lo + random->uniform()*(radius_hi-radius_lo); double value = random->uniform(); int i = 0; double sum = 0.0; while (sum < value) { sum += frac_poly[i]; i++; } return radius_poly[i-1]; } /* ---------------------------------------------------------------------- parse optional parameters at end of input line ------------------------------------------------------------------------- */ void FixPour::options(int narg, char **arg) { // defaults iregion = -1; mode = ATOM; molfrac = NULL; rigidflag = 0; idrigid = NULL; shakeflag = 0; idshake = NULL; idnext = 0; dstyle = ONE; radius_max = radius_one = 0.5; radius_poly = frac_poly = NULL; density_lo = density_hi = 1.0; volfrac = 0.25; maxattempt = 50; rate = 0.0; vxlo = vxhi = vylo = vyhi = vy = vz = 0.0; int iarg = 0; while (iarg < narg) { if (strcmp(arg[iarg],"region") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix pour command"); iregion = domain->find_region(arg[iarg+1]); if (iregion == -1) error->all(FLERR,"Fix pour region ID does not exist"); iarg += 2; } else if (strcmp(arg[iarg],"mol") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix pour command"); int imol = atom->find_molecule(arg[iarg+1]); if (imol == -1) error->all(FLERR,"Molecule template ID for fix pour does not exist"); mode = MOLECULE; onemols = &atom->molecules[imol]; nmol = onemols[0]->nset; delete [] molfrac; molfrac = new double[nmol]; molfrac[0] = 1.0/nmol; for (int i = 1; i < nmol-1; i++) molfrac[i] = molfrac[i-1] + 1.0/nmol; molfrac[nmol-1] = 1.0; iarg += 2; } else if (strcmp(arg[iarg],"molfrac") == 0) { if (mode != MOLECULE) error->all(FLERR,"Illegal fix deposit command"); if (iarg+nmol+1 > narg) error->all(FLERR,"Illegal fix deposit command"); molfrac[0] = force->numeric(FLERR,arg[iarg+1]); for (int i = 1; i < nmol; i++) molfrac[i] = molfrac[i-1] + force->numeric(FLERR,arg[iarg+i+1]); if (molfrac[nmol-1] < 1.0-EPSILON || molfrac[nmol-1] > 1.0+EPSILON) error->all(FLERR,"Illegal fix deposit command"); molfrac[nmol-1] = 1.0; iarg += nmol+1; } else if (strcmp(arg[iarg],"rigid") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix pour command"); int n = strlen(arg[iarg+1]) + 1; delete [] idrigid; idrigid = new char[n]; strcpy(idrigid,arg[iarg+1]); rigidflag = 1; iarg += 2; } else if (strcmp(arg[iarg],"shake") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix pour command"); int n = strlen(arg[iarg+1]) + 1; delete [] idshake; idshake = new char[n]; strcpy(idshake,arg[iarg+1]); shakeflag = 1; iarg += 2; } else if (strcmp(arg[iarg],"id") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix pour command"); if (strcmp(arg[iarg+1],"max") == 0) idnext = 0; else if (strcmp(arg[iarg+1],"next") == 0) idnext = 1; else error->all(FLERR,"Illegal fix pour command"); iarg += 2; } else if (strcmp(arg[iarg],"diam") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix pour command"); if (strcmp(arg[iarg+1],"one") == 0) { if (iarg+3 > narg) error->all(FLERR,"Illegal fix pour command"); dstyle = ONE; radius_one = 0.5 * force->numeric(FLERR,arg[iarg+2]); radius_max = radius_one; iarg += 3; } else if (strcmp(arg[iarg+1],"range") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal fix pour command"); dstyle = RANGE; radius_lo = 0.5 * force->numeric(FLERR,arg[iarg+2]); radius_hi = 0.5 * force->numeric(FLERR,arg[iarg+3]); radius_max = radius_hi; iarg += 4; } else if (strcmp(arg[iarg+1],"poly") == 0) { if (iarg+3 > narg) error->all(FLERR,"Illegal fix pour command"); dstyle = POLY; npoly = force->inumeric(FLERR,arg[iarg+2]); if (npoly <= 0) error->all(FLERR,"Illegal fix pour command"); if (iarg+3 + 2*npoly > narg) error->all(FLERR,"Illegal fix pour command"); radius_poly = new double[npoly]; frac_poly = new double[npoly]; iarg += 3; radius_max = 0.0; for (int i = 0; i < npoly; i++) { radius_poly[i] = 0.5 * force->numeric(FLERR,arg[iarg++]); frac_poly[i] = force->numeric(FLERR,arg[iarg++]); if (radius_poly[i] <= 0.0 || frac_poly[i] < 0.0) error->all(FLERR,"Illegal fix pour command"); radius_max = MAX(radius_max,radius_poly[i]); } double sum = 0.0; for (int i = 0; i < npoly; i++) sum += frac_poly[i]; if (sum != 1.0) error->all(FLERR,"Fix pour polydisperse fractions do not sum to 1.0"); } else error->all(FLERR,"Illegal fix pour command"); } else if (strcmp(arg[iarg],"dens") == 0) { if (iarg+3 > narg) error->all(FLERR,"Illegal fix pour command"); density_lo = force->numeric(FLERR,arg[iarg+1]); density_hi = force->numeric(FLERR,arg[iarg+2]); iarg += 3; } else if (strcmp(arg[iarg],"vol") == 0) { if (iarg+3 > narg) error->all(FLERR,"Illegal fix pour command"); volfrac = force->numeric(FLERR,arg[iarg+1]); maxattempt = force->inumeric(FLERR,arg[iarg+2]); iarg += 3; } else if (strcmp(arg[iarg],"rate") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix pour command"); rate = force->numeric(FLERR,arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"vel") == 0) { if (domain->dimension == 3) { if (iarg+6 > narg) error->all(FLERR,"Illegal fix pour command"); vxlo = force->numeric(FLERR,arg[iarg+1]); vxhi = force->numeric(FLERR,arg[iarg+2]); vylo = force->numeric(FLERR,arg[iarg+3]); vyhi = force->numeric(FLERR,arg[iarg+4]); vz = force->numeric(FLERR,arg[iarg+5]); iarg += 6; } else { if (iarg+4 > narg) error->all(FLERR,"Illegal fix pour command"); vxlo = force->numeric(FLERR,arg[iarg+1]); vxhi = force->numeric(FLERR,arg[iarg+2]); vy = force->numeric(FLERR,arg[iarg+3]); vz = 0.0; iarg += 4; } } else error->all(FLERR,"Illegal fix pour command"); } } /* ---------------------------------------------------------------------- */ void FixPour::reset_dt() { error->all(FLERR,"Cannot change timestep with fix pour"); } /* ---------------------------------------------------------------------- extract particle radius for atom type = itype ------------------------------------------------------------------------- */ void *FixPour::extract(const char *str, int &itype) { if (strcmp(str,"radius") == 0) { if (mode == ATOM) { if (itype == ntype) oneradius = radius_max; else oneradius = 0.0; } else { // find a molecule in template with matching type for (int i = 0; i < nmol; i++) { if (itype-ntype > onemols[i]->ntypes) continue; double *radius = onemols[i]->radius; int *type = onemols[i]->type; int natoms = onemols[i]->natoms; // check radii of matching types in Molecule // default to 0.5, if radii not defined in Molecule // same as atom->avec->create_atom(), invoked in pre_exchange() oneradius = 0.0; for (int i = 0; i < natoms; i++) if (type[i] == itype-ntype) { if (radius) oneradius = MAX(oneradius,radius[i]); else oneradius = 0.5; } } } itype = 0; return &oneradius; } return NULL; } diff --git a/src/MC/fix_bond_create.cpp b/src/MC/fix_bond_create.cpp index 7c491fdf2..56dc7d1d9 100755 --- a/src/MC/fix_bond_create.cpp +++ b/src/MC/fix_bond_create.cpp @@ -1,1441 +1,1444 @@ /* ---------------------------------------------------------------------- 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 "math.h" #include "mpi.h" #include "string.h" #include "stdlib.h" #include "fix_bond_create.h" #include "update.h" #include "respa.h" #include "atom.h" #include "atom_vec.h" #include "force.h" #include "pair.h" #include "comm.h" #include "neighbor.h" #include "neigh_list.h" #include "neigh_request.h" #include "random_mars.h" #include "memory.h" #include "error.h" using namespace LAMMPS_NS; using namespace FixConst; #define BIG 1.0e20 #define DELTA 16 /* ---------------------------------------------------------------------- */ FixBondCreate::FixBondCreate(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { if (narg < 8) error->all(FLERR,"Illegal fix bond/create command"); MPI_Comm_rank(world,&me); nevery = force->inumeric(FLERR,arg[3]); if (nevery <= 0) error->all(FLERR,"Illegal fix bond/create command"); force_reneighbor = 1; next_reneighbor = -1; vector_flag = 1; size_vector = 2; global_freq = 1; extvector = 0; iatomtype = force->inumeric(FLERR,arg[4]); jatomtype = force->inumeric(FLERR,arg[5]); double cutoff = force->numeric(FLERR,arg[6]); btype = force->inumeric(FLERR,arg[7]); if (iatomtype < 1 || iatomtype > atom->ntypes || jatomtype < 1 || jatomtype > atom->ntypes) error->all(FLERR,"Invalid atom type in fix bond/create command"); if (cutoff < 0.0) error->all(FLERR,"Illegal fix bond/create command"); if (btype < 1 || btype > atom->nbondtypes) error->all(FLERR,"Invalid bond type in fix bond/create command"); cutsq = cutoff*cutoff; // optional keywords imaxbond = 0; inewtype = iatomtype; jmaxbond = 0; jnewtype = jatomtype; fraction = 1.0; int seed = 12345; atype = dtype = itype = 0; int iarg = 8; while (iarg < narg) { if (strcmp(arg[iarg],"iparam") == 0) { if (iarg+3 > narg) error->all(FLERR,"Illegal fix bond/create command"); imaxbond = force->inumeric(FLERR,arg[iarg+1]); inewtype = force->inumeric(FLERR,arg[iarg+2]); if (imaxbond < 0) error->all(FLERR,"Illegal fix bond/create command"); if (inewtype < 1 || inewtype > atom->ntypes) error->all(FLERR,"Invalid atom type in fix bond/create command"); iarg += 3; } else if (strcmp(arg[iarg],"jparam") == 0) { if (iarg+3 > narg) error->all(FLERR,"Illegal fix bond/create command"); jmaxbond = force->inumeric(FLERR,arg[iarg+1]); jnewtype = force->inumeric(FLERR,arg[iarg+2]); if (jmaxbond < 0) error->all(FLERR,"Illegal fix bond/create command"); if (jnewtype < 1 || jnewtype > atom->ntypes) error->all(FLERR,"Invalid atom type in fix bond/create command"); iarg += 3; } else if (strcmp(arg[iarg],"prob") == 0) { if (iarg+3 > narg) error->all(FLERR,"Illegal fix bond/create command"); fraction = force->numeric(FLERR,arg[iarg+1]); seed = force->inumeric(FLERR,arg[iarg+2]); if (fraction < 0.0 || fraction > 1.0) error->all(FLERR,"Illegal fix bond/create command"); if (seed <= 0) error->all(FLERR,"Illegal fix bond/create command"); iarg += 3; } else if (strcmp(arg[iarg],"atype") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix bond/create command"); atype = force->inumeric(FLERR,arg[iarg+1]); if (atype < 0) error->all(FLERR,"Illegal fix bond/create command"); iarg += 2; } else if (strcmp(arg[iarg],"dtype") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix bond/create command"); dtype = force->inumeric(FLERR,arg[iarg+1]); if (dtype < 0) error->all(FLERR,"Illegal fix bond/create command"); iarg += 2; } else if (strcmp(arg[iarg],"itype") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix bond/create command"); itype = force->inumeric(FLERR,arg[iarg+1]); if (itype < 0) error->all(FLERR,"Illegal fix bond/create command"); iarg += 2; } else error->all(FLERR,"Illegal fix bond/create command"); } // error check if (atom->molecular != 1) error->all(FLERR,"Cannot use fix bond/create with non-molecular systems"); if (iatomtype == jatomtype && ((imaxbond != jmaxbond) || (inewtype != jnewtype))) error->all(FLERR, "Inconsistent iparam/jparam values in fix bond/create command"); // initialize Marsaglia RNG with processor-unique seed random = new RanMars(lmp,seed + me); // perform initial allocation of atom-based arrays // register with Atom class // bondcount values will be initialized in setup() bondcount = NULL; grow_arrays(atom->nmax); atom->add_callback(0); countflag = 0; // set comm sizes needed by this fix // forward is big due to comm of broken bonds and 1-2 neighbors comm_forward = MAX(2,2+atom->maxspecial); comm_reverse = 2; // allocate arrays local to this fix nmax = 0; partner = finalpartner = NULL; distsq = NULL; maxcreate = 0; created = NULL; // copy = special list for one atom // size = ms^2 + ms is sufficient // b/c in rebuild_special() neighs of all 1-2s are added, // then a dedup(), then neighs of all 1-3s are added, then final dedup() // this means intermediate size cannot exceed ms^2 + ms int maxspecial = atom->maxspecial; copy = new tagint[maxspecial*maxspecial + maxspecial]; // zero out stats createcount = 0; createcounttotal = 0; } /* ---------------------------------------------------------------------- */ FixBondCreate::~FixBondCreate() { // unregister callbacks to this fix from Atom class atom->delete_callback(id,0); delete random; // delete locally stored arrays memory->destroy(bondcount); memory->destroy(partner); memory->destroy(finalpartner); memory->destroy(distsq); memory->destroy(created); delete [] copy; } /* ---------------------------------------------------------------------- */ int FixBondCreate::setmask() { int mask = 0; mask |= POST_INTEGRATE; mask |= POST_INTEGRATE_RESPA; return mask; } /* ---------------------------------------------------------------------- */ void FixBondCreate::init() { if (strstr(update->integrate_style,"respa")) nlevels_respa = ((Respa *) update->integrate)->nlevels; // check cutoff for iatomtype,jatomtype if (force->pair == NULL || cutsq > force->pair->cutsq[iatomtype][jatomtype]) error->all(FLERR,"Fix bond/create cutoff is longer than pairwise cutoff"); // enable angle/dihedral/improper creation if atype/dtype/itype // option was used and a force field has been specified if (atype && force->angle) { angleflag = 1; if (atype > atom->nangletypes) error->all(FLERR,"Fix bond/create angle type is invalid"); } else angleflag = 0; if (dtype && force->dihedral) { dihedralflag = 1; if (dtype > atom->ndihedraltypes) error->all(FLERR,"Fix bond/create dihedral type is invalid"); } else dihedralflag = 0; if (itype && force->improper) { improperflag = 1; if (itype > atom->nimpropertypes) error->all(FLERR,"Fix bond/create improper type is invalid"); } else improperflag = 0; if (force->improper) { if (force->improper_match("class2") || force->improper_match("ring")) error->all(FLERR,"Cannot yet use fix bond/create with this " "improper style"); } // need a half neighbor list, built every Nevery steps int irequest = neighbor->request((void *) this); neighbor->requests[irequest]->pair = 0; neighbor->requests[irequest]->fix = 1; neighbor->requests[irequest]->occasional = 1; lastcheck = -1; } /* ---------------------------------------------------------------------- */ void FixBondCreate::init_list(int id, NeighList *ptr) { list = ptr; } /* ---------------------------------------------------------------------- */ void FixBondCreate::setup(int vflag) { int i,j,m; // compute initial bondcount if this is first run // can't do this earlier, in constructor or init, b/c need ghost info if (countflag) return; countflag = 1; // count bonds stored with each bond I own // if newton bond is not set, just increment count on atom I // if newton bond is set, also increment count on atom J even if ghost // bondcount is long enough to tally ghost atom counts int *num_bond = atom->num_bond; int **bond_type = atom->bond_type; tagint **bond_atom = atom->bond_atom; int nlocal = atom->nlocal; int nghost = atom->nghost; int nall = nlocal + nghost; int newton_bond = force->newton_bond; for (i = 0; i < nall; i++) bondcount[i] = 0; for (i = 0; i < nlocal; i++) for (j = 0; j < num_bond[i]; j++) { if (bond_type[i][j] == btype) { bondcount[i]++; if (newton_bond) { m = atom->map(bond_atom[i][j]); if (m < 0) error->one(FLERR,"Fix bond/create needs ghost atoms " "from further away"); bondcount[m]++; } } } // if newton_bond is set, need to sum bondcount commflag = 1; if (newton_bond) comm->reverse_comm_fix(this,1); } /* ---------------------------------------------------------------------- */ void FixBondCreate::post_integrate() { int i,j,k,m,n,ii,jj,inum,jnum,itype,jtype,n1,n2,n3,possible; double xtmp,ytmp,ztmp,delx,dely,delz,rsq; int *ilist,*jlist,*numneigh,**firstneigh; tagint *slist; if (update->ntimestep % nevery) return; // check that all procs have needed ghost atoms within ghost cutoff // only if neighbor list has changed since last check // needs to be <= test b/c neighbor list could have been re-built in // same timestep as last post_integrate() call, but afterwards // NOTE: no longer think is needed, due to error tests on atom->map() // NOTE: if delete, can also delete lastcheck and check_ghosts() //if (lastcheck <= neighbor->lastcall) check_ghosts(); // acquire updated ghost atom positions // necessary b/c are calling this after integrate, but before Verlet comm comm->forward_comm(); // forward comm of bondcount, so ghosts have it commflag = 1; comm->forward_comm_fix(this,1); // resize bond partner list and initialize it // probability array overlays distsq array // needs to be atom->nmax in length if (atom->nmax > nmax) { memory->destroy(partner); memory->destroy(finalpartner); memory->destroy(distsq); nmax = atom->nmax; memory->create(partner,nmax,"bond/create:partner"); memory->create(finalpartner,nmax,"bond/create:finalpartner"); memory->create(distsq,nmax,"bond/create:distsq"); probability = distsq; } int nlocal = atom->nlocal; int nall = atom->nlocal + atom->nghost; for (i = 0; i < nall; i++) { partner[i] = 0; finalpartner[i] = 0; distsq[i] = BIG; } // loop over neighbors of my atoms // each atom sets one closest eligible partner atom ID to bond with double **x = atom->x; tagint *tag = atom->tag; tagint **bond_atom = atom->bond_atom; int *num_bond = atom->num_bond; int **nspecial = atom->nspecial; tagint **special = atom->special; int *mask = atom->mask; int *type = atom->type; neighbor->build_one(list,1); inum = list->inum; ilist = list->ilist; numneigh = list->numneigh; firstneigh = list->firstneigh; for (ii = 0; ii < inum; ii++) { i = ilist[ii]; if (!(mask[i] & groupbit)) continue; itype = type[i]; 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 (!(mask[j] & groupbit)) continue; jtype = type[j]; possible = 0; if (itype == iatomtype && jtype == jatomtype) { if ((imaxbond == 0 || bondcount[i] < imaxbond) && (jmaxbond == 0 || bondcount[j] < jmaxbond)) possible = 1; } else if (itype == jatomtype && jtype == iatomtype) { if ((jmaxbond == 0 || bondcount[i] < jmaxbond) && (imaxbond == 0 || bondcount[j] < imaxbond)) possible = 1; } if (!possible) continue; // do not allow a duplicate bond to be created // check 1-2 neighbors of atom I for (k = 0; k < nspecial[i][0]; k++) if (special[i][k] == tag[j]) possible = 0; if (!possible) 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) continue; if (rsq < distsq[i]) { partner[i] = tag[j]; distsq[i] = rsq; } if (rsq < distsq[j]) { partner[j] = tag[i]; distsq[j] = rsq; } } } // reverse comm of distsq and partner // not needed if newton_pair off since I,J pair was seen by both procs commflag = 2; if (force->newton_pair) comm->reverse_comm_fix(this); // each atom now knows its winning partner // for prob check, generate random value for each atom with a bond partner // forward comm of partner and random value, so ghosts have it if (fraction < 1.0) { for (i = 0; i < nlocal; i++) if (partner[i]) probability[i] = random->uniform(); } commflag = 2; comm->forward_comm_fix(this,2); // create bonds for atoms I own // only if both atoms list each other as winning bond partner // and probability constraint is satisfied // if other atom is owned by another proc, it should do same thing int **bond_type = atom->bond_type; int newton_bond = force->newton_bond; ncreate = 0; for (i = 0; i < nlocal; i++) { if (partner[i] == 0) continue; j = atom->map(partner[i]); if (partner[j] != tag[i]) continue; // apply probability constraint using RN for atom with smallest ID if (fraction < 1.0) { if (tag[i] < tag[j]) { if (probability[i] >= fraction) continue; } else { if (probability[j] >= fraction) continue; } } // if newton_bond is set, only store with I or J // if not newton_bond, store bond with both I and J // atom J will also do this consistently, whatever proc it is on if (!newton_bond || tag[i] < tag[j]) { if (num_bond[i] == atom->bond_per_atom) error->one(FLERR,"New bond exceeded bonds per atom in fix bond/create"); bond_type[i][num_bond[i]] = btype; bond_atom[i][num_bond[i]] = tag[j]; num_bond[i]++; } // add a 1-2 neighbor to special bond list for atom I // atom J will also do this, whatever proc it is on // need to first remove tag[j] from later in list if it appears // prevents list from overflowing, will be rebuilt in rebuild_special() slist = special[i]; n1 = nspecial[i][0]; n2 = nspecial[i][1]; n3 = nspecial[i][2]; for (m = n1; m < n3; m++) if (slist[m] == tag[j]) break; if (m < n3) { for (n = m; n < n3-1; n++) slist[n] = slist[n+1]; n3--; if (m < n2) n2--; } if (n3 == atom->maxspecial) error->one(FLERR, "New bond exceeded special list size in fix bond/create"); for (m = n3; m > n1; m--) slist[m] = slist[m-1]; slist[n1] = tag[j]; nspecial[i][0] = n1+1; nspecial[i][1] = n2+1; nspecial[i][2] = n3+1; // increment bondcount, convert atom to new type if limit reached // atom J will also do this, whatever proc it is on bondcount[i]++; if (type[i] == iatomtype) { if (bondcount[i] == imaxbond) type[i] = inewtype; } else { if (bondcount[i] == jmaxbond) type[i] = jnewtype; } // store final created bond partners and count the created bond once finalpartner[i] = tag[j]; finalpartner[j] = tag[i]; if (tag[i] < tag[j]) ncreate++; } // tally stats MPI_Allreduce(&ncreate,&createcount,1,MPI_INT,MPI_SUM,world); createcounttotal += createcount; atom->nbonds += createcount; // trigger reneighboring if any bonds were formed // this insures neigh lists will immediately reflect the topology changes // done if any bonds created if (createcount) next_reneighbor = update->ntimestep; if (!createcount) return; // communicate final partner and 1-2 special neighbors // 1-2 neighs already reflect created bonds commflag = 3; comm->forward_comm_fix(this); // create list of broken bonds that influence my owned atoms // even if between owned-ghost or ghost-ghost atoms // finalpartner is now set for owned and ghost atoms so loop over nall // OK if duplicates in broken list due to ghosts duplicating owned atoms // check J < 0 to insure a broken bond to unknown atom is included // i.e. a bond partner outside of cutoff length ncreate = 0; for (i = 0; i < nall; i++) { if (finalpartner[i] == 0) continue; j = atom->map(finalpartner[i]); if (j < 0 || tag[i] < tag[j]) { if (ncreate == maxcreate) { maxcreate += DELTA; memory->grow(created,maxcreate,2,"bond/create:created"); } created[ncreate][0] = tag[i]; created[ncreate][1] = finalpartner[i]; ncreate++; } } // update special neigh lists of all atoms affected by any created bond // also add angles/dihedrals/impropers induced by created bonds update_topology(); // DEBUG //print_bb(); } /* ---------------------------------------------------------------------- insure all atoms 2 hops away from owned atoms are in ghost list this allows dihedral 1-2-3-4 to be properly created and special list of 1 to be properly updated if I own atom 1, but not 2,3,4, and bond 3-4 is added then 2,3 will be ghosts and 3 will store 4 as its finalpartner ------------------------------------------------------------------------- */ void FixBondCreate::check_ghosts() { int i,j,n; tagint *slist; int **nspecial = atom->nspecial; tagint **special = atom->special; int nlocal = atom->nlocal; int flag = 0; for (i = 0; i < nlocal; i++) { slist = special[i]; n = nspecial[i][1]; for (j = 0; j < n; j++) if (atom->map(slist[j]) < 0) flag = 1; } int flagall; MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_SUM,world); if (flagall) error->all(FLERR,"Fix3 bond/create needs ghost atoms from further away"); lastcheck = update->ntimestep; } /* ---------------------------------------------------------------------- double loop over my atoms and created bonds influenced = 1 if atom's topology is affected by any created bond yes if is one of 2 atoms in bond yes if either atom ID appears in as 1-2 or 1-3 in atom's special list else no if influenced by any created bond: rebuild the atom's special list of 1-2,1-3,1-4 neighs check for angles/dihedrals/impropers to create due modified special list ------------------------------------------------------------------------- */ void FixBondCreate::update_topology() { int i,j,k,n,influence,influenced; tagint id1,id2; tagint *slist; tagint *tag = atom->tag; int **nspecial = atom->nspecial; tagint **special = atom->special; int nlocal = atom->nlocal; nangles = 0; ndihedrals = 0; nimpropers = 0; overflow = 0; //printf("NCREATE %d: ",ncreate); //for (i = 0; i < ncreate; i++) // printf(" %d %d,",created[i][0],created[i][1]); //printf("\n"); for (i = 0; i < nlocal; i++) { influenced = 0; slist = special[i]; for (j = 0; j < ncreate; j++) { id1 = created[j][0]; id2 = created[j][1]; influence = 0; if (tag[i] == id1 || tag[i] == id2) influence = 1; else { n = nspecial[i][1]; for (k = 0; k < n; k++) if (slist[k] == id1 || slist[k] == id2) { influence = 1; break; } } if (!influence) continue; influenced = 1; } // rebuild_special first, since used by create_angles, etc if (influenced) { rebuild_special(i); if (angleflag) create_angles(i); if (dihedralflag) create_dihedrals(i); if (improperflag) create_impropers(i); } } int overflowall; MPI_Allreduce(&overflow,&overflowall,1,MPI_INT,MPI_SUM,world); if (overflowall) error->all(FLERR,"Fix bond/create induced too many " "angles/dihedrals/impropers per atom"); int newton_bond = force->newton_bond; int all; if (angleflag) { MPI_Allreduce(&nangles,&all,1,MPI_INT,MPI_SUM,world); if (!newton_bond) all /= 3; atom->nangles += all; } if (dihedralflag) { MPI_Allreduce(&ndihedrals,&all,1,MPI_INT,MPI_SUM,world); if (!newton_bond) all /= 4; atom->ndihedrals += all; } if (improperflag) { MPI_Allreduce(&nimpropers,&all,1,MPI_INT,MPI_SUM,world); if (!newton_bond) all /= 4; atom->nimpropers += all; } } /* ---------------------------------------------------------------------- re-build special list of atom M does not affect 1-2 neighs (already include effects of new bond) affects 1-3 and 1-4 neighs due to other atom's augmented 1-2 neighs ------------------------------------------------------------------------- */ void FixBondCreate::rebuild_special(int m) { int i,j,n,n1,cn1,cn2,cn3; tagint *slist; tagint *tag = atom->tag; int **nspecial = atom->nspecial; tagint **special = atom->special; // existing 1-2 neighs of atom M slist = special[m]; n1 = nspecial[m][0]; cn1 = 0; for (i = 0; i < n1; i++) copy[cn1++] = slist[i]; // new 1-3 neighs of atom M, based on 1-2 neighs of 1-2 neighs // exclude self // remove duplicates after adding all possible 1-3 neighs cn2 = cn1; for (i = 0; i < cn1; i++) { n = atom->map(copy[i]); if (n < 0) error->one(FLERR,"Fix bond/create needs ghost atoms from further away"); slist = special[n]; n1 = nspecial[n][0]; for (j = 0; j < n1; j++) if (slist[j] != tag[m]) copy[cn2++] = slist[j]; } cn2 = dedup(cn1,cn2,copy); if (cn2 > atom->maxspecial) error->one(FLERR,"Special list size exceeded in fix bond/create"); // new 1-4 neighs of atom M, based on 1-2 neighs of 1-3 neighs // exclude self // remove duplicates after adding all possible 1-4 neighs cn3 = cn2; for (i = cn1; i < cn2; i++) { n = atom->map(copy[i]); if (n < 0) error->one(FLERR,"Fix bond/create needs ghost atoms from further away"); slist = special[n]; n1 = nspecial[n][0]; for (j = 0; j < n1; j++) if (slist[j] != tag[m]) copy[cn3++] = slist[j]; } cn3 = dedup(cn2,cn3,copy); if (cn3 > atom->maxspecial) error->one(FLERR,"Special list size exceeded in fix bond/create"); // store new special list with atom M nspecial[m][0] = cn1; nspecial[m][1] = cn2; nspecial[m][2] = cn3; memcpy(special[m],copy,cn3*sizeof(int)); } /* ---------------------------------------------------------------------- create any angles owned by atom M induced by newly created bonds walk special list to find all possible angles to create only add an angle if a new bond is one of its 2 bonds (I-J,J-K) for newton_bond on, atom M is central atom for newton_bond off, atom M is any of 3 atoms in angle ------------------------------------------------------------------------- */ void FixBondCreate::create_angles(int m) { int i,j,n,i2local,n1,n2; tagint i1,i2,i3; tagint *s1list,*s2list; tagint *tag = atom->tag; int **nspecial = atom->nspecial; tagint **special = atom->special; int num_angle = atom->num_angle[m]; int *angle_type = atom->angle_type[m]; tagint *angle_atom1 = atom->angle_atom1[m]; tagint *angle_atom2 = atom->angle_atom2[m]; tagint *angle_atom3 = atom->angle_atom3[m]; // atom M is central atom in angle // double loop over 1-2 neighs // avoid double counting by 2nd loop as j = i+1,N not j = 1,N // consider all angles, only add if: // a new bond is in the angle and atom types match i2 = tag[m]; n2 = nspecial[m][0]; s2list = special[m]; for (i = 0; i < n2; i++) { i1 = s2list[i]; for (j = i+1; j < n2; j++) { i3 = s2list[j]; // angle = i1-i2-i3 for (n = 0; n < ncreate; n++) { if (created[n][0] == i1 && created[n][1] == i2) break; if (created[n][0] == i2 && created[n][1] == i1) break; if (created[n][0] == i2 && created[n][1] == i3) break; if (created[n][0] == i3 && created[n][1] == i2) break; } if (n == ncreate) continue; // NOTE: this is place to check atom types of i1,i2,i3 if (num_angle < atom->angle_per_atom) { angle_type[num_angle] = atype; angle_atom1[num_angle] = i1; angle_atom2[num_angle] = i2; angle_atom3[num_angle] = i3; num_angle++; nangles++; } else overflow = 1; } } atom->num_angle[m] = num_angle; if (force->newton_bond) return; // for newton_bond off, also consider atom M as atom 1 in angle i1 = tag[m]; n1 = nspecial[m][0]; s1list = special[m]; for (i = 0; i < n1; i++) { i2 = s1list[i]; i2local = atom->map(i2); if (i2local < 0) error->one(FLERR,"Fix bond/create needs ghost atoms from further away"); s2list = special[i2local]; n2 = nspecial[i2local][0]; for (j = 0; j < n2; j++) { i3 = s2list[j]; if (i3 == i1) continue; // angle = i1-i2-i3 for (n = 0; n < ncreate; n++) { if (created[n][0] == i1 && created[n][1] == i2) break; if (created[n][0] == i2 && created[n][1] == i1) break; if (created[n][0] == i2 && created[n][1] == i3) break; if (created[n][0] == i3 && created[n][1] == i2) break; } if (n == ncreate) continue; // NOTE: this is place to check atom types of i1,i2,i3 if (num_angle < atom->angle_per_atom) { angle_type[num_angle] = atype; angle_atom1[num_angle] = i1; angle_atom2[num_angle] = i2; angle_atom3[num_angle] = i3; num_angle++; nangles++; } else overflow = 1; } } atom->num_angle[m] = num_angle; } /* ---------------------------------------------------------------------- create any dihedrals owned by atom M induced by newly created bonds walk special list to find all possible dihedrals to create only add a dihedral if a new bond is one of its 3 bonds (I-J,J-K,K-L) for newton_bond on, atom M is central atom for newton_bond off, atom M is any of 4 atoms in dihedral ------------------------------------------------------------------------- */ void FixBondCreate::create_dihedrals(int m) { int i,j,k,n,i1local,i2local,i3local,n1,n2,n3; tagint i1,i2,i3,i4; tagint *s1list,*s2list,*s3list; tagint *tag = atom->tag; int **nspecial = atom->nspecial; tagint **special = atom->special; int num_dihedral = atom->num_dihedral[m]; int *dihedral_type = atom->dihedral_type[m]; tagint *dihedral_atom1 = atom->dihedral_atom1[m]; tagint *dihedral_atom2 = atom->dihedral_atom2[m]; tagint *dihedral_atom3 = atom->dihedral_atom3[m]; tagint *dihedral_atom4 = atom->dihedral_atom4[m]; // atom M is 2nd atom in dihedral // double loop over 1-2 neighs // two triple loops: one over neighs at each end of triplet // avoid double counting by 2nd loop as j = i+1,N not j = 1,N // avoid double counting due to another atom being 2nd atom in same dihedral // by requiring ID of 2nd atom < ID of 3rd atom // don't do this if newton bond off since want to double count // consider all dihedrals, only add if: // a new bond is in the dihedral and atom types match i2 = tag[m]; n2 = nspecial[m][0]; s2list = special[m]; for (i = 0; i < n2; i++) { i1 = s2list[i]; for (j = i+1; j < n2; j++) { i3 = s2list[j]; if (force->newton_bond && i2 > i3) continue; i3local = atom->map(i3); if (i3local < 0) error->one(FLERR,"Fix bond/create needs ghost atoms from further away"); s3list = special[i3local]; n3 = nspecial[i3local][0]; for (k = 0; k < n3; k++) { i4 = s3list[k]; if (i4 == i1 || i4 == i2 || i4 == i3) continue; // dihedral = i1-i2-i3-i4 for (n = 0; n < ncreate; n++) { if (created[n][0] == i1 && created[n][1] == i2) break; if (created[n][0] == i2 && created[n][1] == i1) break; if (created[n][0] == i2 && created[n][1] == i3) break; if (created[n][0] == i3 && created[n][1] == i2) break; if (created[n][0] == i3 && created[n][1] == i4) break; if (created[n][0] == i4 && created[n][1] == i3) break; } if (n < ncreate) { // NOTE: this is place to check atom types of i3,i2,i1,i4 if (num_dihedral < atom->dihedral_per_atom) { dihedral_type[num_dihedral] = dtype; dihedral_atom1[num_dihedral] = i1; dihedral_atom2[num_dihedral] = i2; dihedral_atom3[num_dihedral] = i3; dihedral_atom4[num_dihedral] = i4; num_dihedral++; ndihedrals++; } else overflow = 1; } } } } for (i = 0; i < n2; i++) { i1 = s2list[i]; if (force->newton_bond && i2 > i1) continue; i1local = atom->map(i1); if (i1local < 0) error->one(FLERR,"Fix bond/create needs ghost atoms from further away"); s3list = special[i1local]; n3 = nspecial[i1local][0]; for (j = i+1; j < n2; j++) { i3 = s2list[j]; for (k = 0; k < n3; k++) { i4 = s3list[k]; if (i4 == i1 || i4 == i2 || i4 == i3) continue; // dihedral = i3-i2-i1-i4 for (n = 0; n < ncreate; n++) { if (created[n][0] == i3 && created[n][1] == i2) break; if (created[n][0] == i2 && created[n][1] == i3) break; if (created[n][0] == i2 && created[n][1] == i1) break; if (created[n][0] == i1 && created[n][1] == i2) break; if (created[n][0] == i1 && created[n][1] == i4) break; if (created[n][0] == i4 && created[n][1] == i1) break; } if (n < ncreate) { // NOTE: this is place to check atom types of i3,i2,i1,i4 if (num_dihedral < atom->dihedral_per_atom) { dihedral_type[num_dihedral] = dtype; dihedral_atom1[num_dihedral] = i3; dihedral_atom2[num_dihedral] = i2; dihedral_atom3[num_dihedral] = i1; dihedral_atom4[num_dihedral] = i4; num_dihedral++; ndihedrals++; } else overflow = 1; } } } } atom->num_dihedral[m] = num_dihedral; if (force->newton_bond) return; // for newton_bond off, also consider atom M as atom 1 in dihedral i1 = tag[m]; n1 = nspecial[m][0]; s1list = special[m]; for (i = 0; i < n1; i++) { i2 = s1list[i]; i2local = atom->map(i2); if (i2local < 0) error->one(FLERR,"Fix bond/create needs ghost atoms from further away"); s2list = special[i2local]; n2 = nspecial[i2local][0]; for (j = 0; j < n2; j++) { i3 = s2list[j]; if (i3 == i1) continue; i3local = atom->map(i3); if (i3local < 0) error->one(FLERR,"Fix bond/create needs ghost atoms from further away"); s3list = special[i3local]; n3 = nspecial[i3local][0]; for (k = 0; k < n3; k++) { i4 = s3list[k]; if (i4 == i1 || i4 == i2 || i4 == i3) continue; // dihedral = i1-i2-i3-i4 for (n = 0; n < ncreate; n++) { if (created[n][0] == i1 && created[n][1] == i2) break; if (created[n][0] == i2 && created[n][1] == i1) break; if (created[n][0] == i2 && created[n][1] == i3) break; if (created[n][0] == i3 && created[n][1] == i2) break; if (created[n][0] == i3 && created[n][1] == i4) break; if (created[n][0] == i4 && created[n][1] == i3) break; } if (n < ncreate) { // NOTE: this is place to check atom types of i3,i2,i1,i4 if (num_dihedral < atom->dihedral_per_atom) { dihedral_type[num_dihedral] = dtype; dihedral_atom1[num_dihedral] = i1; dihedral_atom2[num_dihedral] = i2; dihedral_atom3[num_dihedral] = i3; dihedral_atom4[num_dihedral] = i4; num_dihedral++; ndihedrals++; } else overflow = 1; } } } } } /* ---------------------------------------------------------------------- create any impropers owned by atom M induced by newly created bonds walk special list to find all possible impropers to create only add an improper if a new bond is one of its 3 bonds (I-J,I-K,I-L) for newton_bond on, atom M is central atom for newton_bond off, atom M is any of 4 atoms in improper ------------------------------------------------------------------------- */ void FixBondCreate::create_impropers(int m) { int i,j,k,n,i1local,n1,n2; tagint i1,i2,i3,i4; tagint *s1list,*s2list; tagint *tag = atom->tag; int **nspecial = atom->nspecial; tagint **special = atom->special; int num_improper = atom->num_improper[m]; int *improper_type = atom->improper_type[m]; tagint *improper_atom1 = atom->improper_atom1[m]; tagint *improper_atom2 = atom->improper_atom2[m]; tagint *improper_atom3 = atom->improper_atom3[m]; tagint *improper_atom4 = atom->improper_atom4[m]; // atom M is central atom in improper // triple loop over 1-2 neighs // avoid double counting by 2nd loop as j = i+1,N not j = 1,N // consider all impropers, only add if: // a new bond is in the improper and atom types match i1 = tag[m]; n1 = nspecial[m][0]; s1list = special[m]; for (i = 0; i < n1; i++) { i2 = s1list[i]; for (j = i+1; j < n1; j++) { i3 = s1list[j]; for (k = j+1; k < n1; k++) { i4 = s1list[k]; // improper = i1-i2-i3-i4 for (n = 0; n < ncreate; n++) { if (created[n][0] == i1 && created[n][1] == i2) break; if (created[n][0] == i2 && created[n][1] == i1) break; if (created[n][0] == i1 && created[n][1] == i3) break; if (created[n][0] == i3 && created[n][1] == i1) break; if (created[n][0] == i1 && created[n][1] == i4) break; if (created[n][0] == i4 && created[n][1] == i1) break; } if (n == ncreate) continue; // NOTE: this is place to check atom types of i1,i2,i3,i4 if (num_improper < atom->improper_per_atom) { improper_type[num_improper] = itype; improper_atom1[num_improper] = i1; improper_atom2[num_improper] = i2; improper_atom3[num_improper] = i3; improper_atom4[num_improper] = i4; num_improper++; nimpropers++; } else overflow = 1; } } } atom->num_improper[m] = num_improper; if (force->newton_bond) return; // for newton_bond off, also consider atom M as atom 2 in improper i2 = tag[m]; n2 = nspecial[m][0]; s2list = special[m]; for (i = 0; i < n2; i++) { i1 = s2list[i]; i1local = atom->map(i1); if (i1local < 0) error->one(FLERR,"Fix bond/create needs ghost atoms from further away"); s1list = special[i1local]; n1 = nspecial[i1local][0]; for (j = 0; j < n1; j++) { i3 = s1list[j]; if (i3 == i1 || i3 == i2) continue; for (k = j+1; k < n1; k++) { i4 = s1list[k]; if (i4 == i1 || i4 == i2) continue; // improper = i1-i2-i3-i4 for (n = 0; n < ncreate; n++) { if (created[n][0] == i1 && created[n][1] == i2) break; if (created[n][0] == i2 && created[n][1] == i1) break; if (created[n][0] == i1 && created[n][1] == i3) break; if (created[n][0] == i3 && created[n][1] == i1) break; if (created[n][0] == i1 && created[n][1] == i4) break; if (created[n][0] == i4 && created[n][1] == i1) break; } if (n < ncreate) { // NOTE: this is place to check atom types of i3,i2,i1,i4 if (num_improper < atom->improper_per_atom) { improper_type[num_improper] = dtype; improper_atom1[num_improper] = i1; improper_atom2[num_improper] = i2; improper_atom3[num_improper] = i3; improper_atom4[num_improper] = i4; num_improper++; nimpropers++; } else overflow = 1; } } } } } /* ---------------------------------------------------------------------- remove all ID duplicates in copy from Nstart:Nstop-1 compare to all previous values in copy return N decremented by any discarded duplicates ------------------------------------------------------------------------- */ int FixBondCreate::dedup(int nstart, int nstop, tagint *copy) { int i; int m = nstart; while (m < nstop) { for (i = 0; i < m; i++) if (copy[i] == copy[m]) { copy[m] = copy[nstop-1]; nstop--; break; } if (i == m) m++; } return nstop; } /* ---------------------------------------------------------------------- */ void FixBondCreate::post_integrate_respa(int ilevel, int iloop) { if (ilevel == nlevels_respa-1) post_integrate(); } /* ---------------------------------------------------------------------- */ int FixBondCreate::pack_forward_comm(int n, int *list, double *buf, int pbc_flag, int *pbc) { int i,j,k,m,ns; m = 0; if (commflag == 1) { for (i = 0; i < n; i++) { j = list[i]; buf[m++] = ubuf(bondcount[j]).d; } return m; } if (commflag == 2) { for (i = 0; i < n; i++) { j = list[i]; buf[m++] = ubuf(partner[j]).d; buf[m++] = probability[j]; } return m; } int **nspecial = atom->nspecial; tagint **special = atom->special; m = 0; for (i = 0; i < n; i++) { j = list[i]; buf[m++] = ubuf(finalpartner[j]).d; ns = nspecial[j][0]; buf[m++] = ubuf(ns).d; for (k = 0; k < ns; k++) buf[m++] = ubuf(special[j][k]).d; } return m; } /* ---------------------------------------------------------------------- */ void FixBondCreate::unpack_forward_comm(int n, int first, double *buf) { int i,j,m,ns,last; m = 0; last = first + n; if (commflag == 1) { for (i = first; i < last; i++) bondcount[i] = (int) ubuf(buf[m++]).i; } else if (commflag == 2) { for (i = first; i < last; i++) { partner[i] = (tagint) ubuf(buf[m++]).i; probability[i] = buf[m++]; } } else { int **nspecial = atom->nspecial; tagint **special = atom->special; m = 0; last = first + n; for (i = first; i < last; i++) { finalpartner[i] = (tagint) ubuf(buf[m++]).i; ns = (int) ubuf(buf[m++]).i; nspecial[i][0] = ns; for (j = 0; j < ns; j++) special[i][j] = (tagint) ubuf(buf[m++]).i; } } } /* ---------------------------------------------------------------------- */ int FixBondCreate::pack_reverse_comm(int n, int first, double *buf) { int i,m,last; m = 0; last = first + n; if (commflag == 1) { for (i = first; i < last; i++) buf[m++] = ubuf(bondcount[i]).d; return m; } for (i = first; i < last; i++) { buf[m++] = ubuf(partner[i]).d; buf[m++] = distsq[i]; } return m; } /* ---------------------------------------------------------------------- */ void FixBondCreate::unpack_reverse_comm(int n, int *list, double *buf) { int i,j,m; m = 0; if (commflag == 1) { for (i = 0; i < n; i++) { j = list[i]; bondcount[j] += (int) ubuf(buf[m++]).i; } } else { for (i = 0; i < n; i++) { j = list[i]; if (buf[m+1] < distsq[j]) { partner[j] = (tagint) ubuf(buf[m++]).i; distsq[j] = buf[m++]; } else m += 2; } } } /* ---------------------------------------------------------------------- allocate local atom-based arrays ------------------------------------------------------------------------- */ void FixBondCreate::grow_arrays(int nmax) { memory->grow(bondcount,nmax,"bond/create:bondcount"); } /* ---------------------------------------------------------------------- copy values within local atom-based arrays ------------------------------------------------------------------------- */ void FixBondCreate::copy_arrays(int i, int j, int delflag) { bondcount[j] = bondcount[i]; } /* ---------------------------------------------------------------------- pack values in local atom-based arrays for exchange with another proc ------------------------------------------------------------------------- */ int FixBondCreate::pack_exchange(int i, double *buf) { buf[0] = bondcount[i]; return 1; } /* ---------------------------------------------------------------------- unpack values in local atom-based arrays from exchange with another proc ------------------------------------------------------------------------- */ int FixBondCreate::unpack_exchange(int nlocal, double *buf) { bondcount[nlocal] = static_cast<int> (buf[0]); return 1; } /* ---------------------------------------------------------------------- */ double FixBondCreate::compute_vector(int n) { if (n == 1) return (double) createcount; return (double) createcounttotal; } /* ---------------------------------------------------------------------- memory usage of local atom-based arrays ------------------------------------------------------------------------- */ double FixBondCreate::memory_usage() { int nmax = atom->nmax; double bytes = nmax * sizeof(int); bytes = 2*nmax * sizeof(tagint); bytes += nmax * sizeof(double); return bytes; } /* ---------------------------------------------------------------------- */ void FixBondCreate::print_bb() { for (int i = 0; i < atom->nlocal; i++) { printf("TAG " TAGINT_FORMAT ": %d nbonds: ",atom->tag[i],atom->num_bond[i]); for (int j = 0; j < atom->num_bond[i]; j++) { - printf(" %d",atom->bond_atom[i][j]); + printf(" " TAGINT_FORMAT,atom->bond_atom[i][j]); } printf("\n"); printf("TAG " TAGINT_FORMAT ": %d nangles: ",atom->tag[i],atom->num_angle[i]); for (int j = 0; j < atom->num_angle[i]; j++) { - printf(" %d %d %d,",atom->angle_atom1[i][j], - atom->angle_atom2[i][j],atom->angle_atom3[i][j]); + printf(" " TAGINT_FORMAT " " TAGINT_FORMAT " " TAGINT_FORMAT ",", + atom->angle_atom1[i][j], atom->angle_atom2[i][j], + atom->angle_atom3[i][j]); } printf("\n"); printf("TAG " TAGINT_FORMAT ": %d ndihedrals: ",atom->tag[i],atom->num_dihedral[i]); for (int j = 0; j < atom->num_dihedral[i]; j++) { - printf(" %d %d %d %d,",atom->dihedral_atom1[i][j], + printf(" " TAGINT_FORMAT " " TAGINT_FORMAT " " TAGINT_FORMAT " " + TAGINT_FORMAT ",", atom->dihedral_atom1[i][j], atom->dihedral_atom2[i][j],atom->dihedral_atom3[i][j], atom->dihedral_atom4[i][j]); } printf("\n"); printf("TAG " TAGINT_FORMAT ": %d nimpropers: ",atom->tag[i],atom->num_improper[i]); for (int j = 0; j < atom->num_improper[i]; j++) { - printf(" %d %d %d %d,",atom->improper_atom1[i][j], + printf(" " TAGINT_FORMAT " " TAGINT_FORMAT " " TAGINT_FORMAT " " + TAGINT_FORMAT ",",atom->improper_atom1[i][j], atom->improper_atom2[i][j],atom->improper_atom3[i][j], atom->improper_atom4[i][j]); } printf("\n"); printf("TAG " TAGINT_FORMAT ": %d %d %d nspecial: ",atom->tag[i], atom->nspecial[i][0],atom->nspecial[i][1],atom->nspecial[i][2]); for (int j = 0; j < atom->nspecial[i][2]; j++) { - printf(" %d",atom->special[i][j]); + printf(" " TAGINT_FORMAT,atom->special[i][j]); } printf("\n"); } } /* ---------------------------------------------------------------------- */ void FixBondCreate::print_copy(const char *str, tagint m, int n1, int n2, int n3, int *v) { - printf("%s %i: %d %d %d nspecial: ",str,m,n1,n2,n3); + printf("%s " TAGINT_FORMAT ": %d %d %d nspecial: ",str,m,n1,n2,n3); for (int j = 0; j < n3; j++) printf(" %d",v[j]); printf("\n"); } diff --git a/src/RIGID/fix_rigid_small.cpp b/src/RIGID/fix_rigid_small.cpp index b014f44ba..9dbf6e826 100644 --- a/src/RIGID/fix_rigid_small.cpp +++ b/src/RIGID/fix_rigid_small.cpp @@ -1,3423 +1,3423 @@ /* ---------------------------------------------------------------------- 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 "math.h" #include "stdio.h" #include "stdlib.h" #include "string.h" #include "fix_rigid_small.h" #include "math_extra.h" #include "atom.h" #include "atom_vec_ellipsoid.h" #include "atom_vec_line.h" #include "atom_vec_tri.h" #include "molecule.h" #include "domain.h" #include "update.h" #include "respa.h" #include "modify.h" #include "group.h" #include "comm.h" #include "force.h" #include "output.h" #include "random_mars.h" #include "math_const.h" #include "memory.h" #include "error.h" #include <map> using namespace LAMMPS_NS; using namespace FixConst; using namespace MathConst; // allocate space for static class variable FixRigidSmall *FixRigidSmall::frsptr; #define MAXLINE 256 #define CHUNK 1024 #define ATTRIBUTE_PERBODY 11 #define TOLERANCE 1.0e-6 #define EPSILON 1.0e-7 #define BIG 1.0e20 #define SINERTIA 0.4 // moment of inertia prefactor for sphere #define EINERTIA 0.4 // moment of inertia prefactor for ellipsoid #define LINERTIA (1.0/12.0) // moment of inertia prefactor for line segment #define DELTA_BODY 10000 enum{NONE,XYZ,XY,YZ,XZ}; // same as in FixRigid enum{ISO,ANISO,TRICLINIC}; // same as in FixRigid enum{FULL_BODY,INITIAL,FINAL,FORCE_TORQUE,VCM_ANGMOM,XCM_MASS,ITENSOR,DOF}; /* ---------------------------------------------------------------------- */ FixRigidSmall::FixRigidSmall(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { int i; scalar_flag = 1; extscalar = 0; global_freq = 1; time_integrate = 1; rigid_flag = 1; virial_flag = 1; create_attribute = 1; MPI_Comm_rank(world,&me); MPI_Comm_size(world,&nprocs); // perform initial allocation of atom-based arrays // register with Atom class extended = orientflag = dorientflag = 0; bodyown = NULL; bodytag = NULL; atom2body = NULL; displace = NULL; eflags = NULL; orient = NULL; dorient = NULL; grow_arrays(atom->nmax); atom->add_callback(0); // parse args for rigid body specification if (narg < 4) error->all(FLERR,"Illegal fix rigid/small command"); if (strcmp(arg[3],"molecule") != 0) error->all(FLERR,"Illegal fix rigid/small command"); if (atom->molecule_flag == 0) error->all(FLERR,"Fix rigid/small requires atom attribute molecule"); if (atom->map_style == 0) error->all(FLERR,"Fix rigid/small requires an atom map, see atom_modify"); // maxmol = largest molecule # int *mask = atom->mask; tagint *molecule = atom->molecule; int nlocal = atom->nlocal; maxmol = -1; for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) maxmol = MAX(maxmol,molecule[i]); tagint itmp; MPI_Allreduce(&maxmol,&itmp,1,MPI_LMP_TAGINT,MPI_MAX,world); maxmol = itmp; // parse optional args int seed; langflag = 0; infile = NULL; onemols = NULL; tstat_flag = 0; pstat_flag = 0; allremap = 1; id_dilate = NULL; t_chain = 10; t_iter = 1; t_order = 3; p_chain = 10; pcouple = NONE; pstyle = ANISO; for (int i = 0; i < 3; i++) { p_start[i] = p_stop[i] = p_period[i] = 0.0; p_flag[i] = 0; } int iarg = 4; while (iarg < narg) { if (strcmp(arg[iarg],"langevin") == 0) { if (iarg+5 > narg) error->all(FLERR,"Illegal fix rigid/small command"); if (strcmp(style,"rigid/small") != 0) error->all(FLERR,"Illegal fix rigid/small command"); langflag = 1; t_start = force->numeric(FLERR,arg[iarg+1]); t_stop = force->numeric(FLERR,arg[iarg+2]); t_period = force->numeric(FLERR,arg[iarg+3]); seed = force->inumeric(FLERR,arg[iarg+4]); if (t_period <= 0.0) error->all(FLERR,"Fix rigid/small langevin period must be > 0.0"); if (seed <= 0) error->all(FLERR,"Illegal fix rigid/small command"); iarg += 5; } else if (strcmp(arg[iarg],"infile") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix rigid/small command"); delete [] infile; int n = strlen(arg[iarg+1]) + 1; infile = new char[n]; strcpy(infile,arg[iarg+1]); restart_file = 1; iarg += 2; } else if (strcmp(arg[iarg],"mol") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix rigid/small command"); int imol = atom->find_molecule(arg[iarg+1]); if (imol == -1) error->all(FLERR,"Molecule template ID for " "fix rigid/small does not exist"); onemols = &atom->molecules[imol]; nmol = onemols[0]->nset; iarg += 2; } else if (strcmp(arg[iarg],"temp") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal fix rigid/small command"); if (strcmp(style,"rigid/nvt/small") != 0 && strcmp(style,"rigid/npt/small") != 0) error->all(FLERR,"Illegal fix rigid command"); tstat_flag = 1; t_start = force->numeric(FLERR,arg[iarg+1]); t_stop = force->numeric(FLERR,arg[iarg+2]); t_period = force->numeric(FLERR,arg[iarg+3]); iarg += 4; } else if (strcmp(arg[iarg],"iso") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal fix rigid/small command"); if (strcmp(style,"rigid/npt/small") != 0 && strcmp(style,"rigid/nph/small") != 0) error->all(FLERR,"Illegal fix rigid/small command"); pcouple = XYZ; p_start[0] = p_start[1] = p_start[2] = force->numeric(FLERR,arg[iarg+1]); p_stop[0] = p_stop[1] = p_stop[2] = force->numeric(FLERR,arg[iarg+2]); p_period[0] = p_period[1] = p_period[2] = force->numeric(FLERR,arg[iarg+3]); p_flag[0] = p_flag[1] = p_flag[2] = 1; if (domain->dimension == 2) { p_start[2] = p_stop[2] = p_period[2] = 0.0; p_flag[2] = 0; } iarg += 4; } else if (strcmp(arg[iarg],"aniso") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal fix rigid/small command"); if (strcmp(style,"rigid/npt/small") != 0 && strcmp(style,"rigid/nph/small") != 0) error->all(FLERR,"Illegal fix rigid/small command"); p_start[0] = p_start[1] = p_start[2] = force->numeric(FLERR,arg[iarg+1]); p_stop[0] = p_stop[1] = p_stop[2] = force->numeric(FLERR,arg[iarg+2]); p_period[0] = p_period[1] = p_period[2] = force->numeric(FLERR,arg[iarg+3]); p_flag[0] = p_flag[1] = p_flag[2] = 1; if (domain->dimension == 2) { p_start[2] = p_stop[2] = p_period[2] = 0.0; p_flag[2] = 0; } iarg += 4; } else if (strcmp(arg[iarg],"x") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal fix rigid/small command"); p_start[0] = force->numeric(FLERR,arg[iarg+1]); p_stop[0] = force->numeric(FLERR,arg[iarg+2]); p_period[0] = force->numeric(FLERR,arg[iarg+3]); p_flag[0] = 1; iarg += 4; } else if (strcmp(arg[iarg],"y") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal fix rigid/small command"); p_start[1] = force->numeric(FLERR,arg[iarg+1]); p_stop[1] = force->numeric(FLERR,arg[iarg+2]); p_period[1] = force->numeric(FLERR,arg[iarg+3]); p_flag[1] = 1; iarg += 4; } else if (strcmp(arg[iarg],"z") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal fix rigid/small command"); p_start[2] = force->numeric(FLERR,arg[iarg+1]); p_stop[2] = force->numeric(FLERR,arg[iarg+2]); p_period[2] = force->numeric(FLERR,arg[iarg+3]); p_flag[2] = 1; iarg += 4; } else if (strcmp(arg[iarg],"couple") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix rigid/small command"); if (strcmp(arg[iarg+1],"xyz") == 0) pcouple = XYZ; else if (strcmp(arg[iarg+1],"xy") == 0) pcouple = XY; else if (strcmp(arg[iarg+1],"yz") == 0) pcouple = YZ; else if (strcmp(arg[iarg+1],"xz") == 0) pcouple = XZ; else if (strcmp(arg[iarg+1],"none") == 0) pcouple = NONE; else error->all(FLERR,"Illegal fix rigid/small command"); iarg += 2; } else if (strcmp(arg[iarg],"dilate") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix rigid/small nvt/npt/nph command"); if (strcmp(arg[iarg+1],"all") == 0) allremap = 1; else { allremap = 0; delete [] id_dilate; int n = strlen(arg[iarg+1]) + 1; id_dilate = new char[n]; strcpy(id_dilate,arg[iarg+1]); int idilate = group->find(id_dilate); if (idilate == -1) error->all(FLERR,"Fix rigid/small nvt/npt/nph dilate group ID " "does not exist"); } iarg += 2; } else if (strcmp(arg[iarg],"tparam") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal fix rigid/small command"); if (strcmp(style,"rigid/nvt/small") != 0 && strcmp(style,"rigid/npt/small") != 0) error->all(FLERR,"Illegal fix rigid/small command"); t_chain = force->numeric(FLERR,arg[iarg+1]); t_iter = force->numeric(FLERR,arg[iarg+2]); t_order = force->numeric(FLERR,arg[iarg+3]); iarg += 4; } else if (strcmp(arg[iarg],"pchain") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix rigid/small command"); if (strcmp(style,"rigid/npt/small") != 0 && strcmp(style,"rigid/nph/small") != 0) error->all(FLERR,"Illegal fix rigid/small command"); p_chain = force->numeric(FLERR,arg[iarg+1]); iarg += 2; } else error->all(FLERR,"Illegal fix rigid/small command"); } // error check and further setup for Molecule template if (onemols) { for (int i = 0; i < nmol; i++) { if (onemols[i]->xflag == 0) error->all(FLERR,"Fix rigid/small molecule must have coordinates"); if (onemols[i]->typeflag == 0) error->all(FLERR,"Fix rigid/small molecule must have atom types"); // fix rigid/small uses center, masstotal, COM, inertia of molecule onemols[i]->compute_center(); onemols[i]->compute_mass(); onemols[i]->compute_com(); onemols[i]->compute_inertia(); } } // set pstat_flag pstat_flag = 0; for (int i = 0; i < 3; i++) if (p_flag[i]) pstat_flag = 1; if (pcouple == XYZ || (domain->dimension == 2 && pcouple == XY)) pstyle = ISO; else pstyle = ANISO; // create rigid bodies based on molecule ID // sets bodytag for owned atoms // body attributes are computed later by setup_bodies() create_bodies(); // set nlocal_body and allocate bodies I own tagint *tag = atom->tag; nlocal_body = nghost_body = 0; for (i = 0; i < nlocal; i++) if (bodytag[i] == tag[i]) nlocal_body++; nmax_body = 0; while (nmax_body < nlocal_body) nmax_body += DELTA_BODY; body = (Body *) memory->smalloc(nmax_body*sizeof(Body), "rigid/small:body"); // set bodyown for owned atoms nlocal_body = 0; for (i = 0; i < nlocal; i++) if (bodytag[i] == tag[i]) { body[nlocal_body].ilocal = i; bodyown[i] = nlocal_body++; } else bodyown[i] = -1; // bodysize = sizeof(Body) in doubles bodysize = sizeof(Body)/sizeof(double); if (bodysize*sizeof(double) != sizeof(Body)) bodysize++; // set max comm sizes needed by this fix comm_forward = 1 + bodysize; comm_reverse = 6; // bitmasks for properties of extended particles POINT = 1; SPHERE = 2; ELLIPSOID = 4; LINE = 8; TRIANGLE = 16; DIPOLE = 32; OMEGA = 64; ANGMOM = 128; TORQUE = 256; MINUSPI = -MY_PI; TWOPI = 2.0*MY_PI; // atom style pointers to particles that store extra info avec_ellipsoid = (AtomVecEllipsoid *) atom->style_match("ellipsoid"); avec_line = (AtomVecLine *) atom->style_match("line"); avec_tri = (AtomVecTri *) atom->style_match("tri"); // print statistics int one = 0; bigint atomone = 0; for (int i = 0; i < nlocal; i++) { if (bodyown[i] >= 0) one++; if (bodytag[i] > 0) atomone++; } MPI_Allreduce(&one,&nbody,1,MPI_INT,MPI_SUM,world); bigint atomall; MPI_Allreduce(&atomone,&atomall,1,MPI_LMP_BIGINT,MPI_SUM,world); if (me == 0) { if (screen) { fprintf(screen,"%d rigid bodies with " BIGINT_FORMAT " atoms\n", nbody,atomall); fprintf(screen," %g = max distance from body owner to body atom\n", maxextent); } if (logfile) { fprintf(logfile,"%d rigid bodies with " BIGINT_FORMAT " atoms\n", nbody,atomall); fprintf(logfile," %g = max distance from body owner to body atom\n", maxextent); } } // initialize Marsaglia RNG with processor-unique seed maxlang = 0; langextra = NULL; random = NULL; if (langflag) random = new RanMars(lmp,seed + comm->me); // mass vector for granular pair styles mass_body = NULL; nmax_mass = 0; staticflag = 1; } /* ---------------------------------------------------------------------- */ FixRigidSmall::~FixRigidSmall() { // unregister callbacks to this fix from Atom class atom->delete_callback(id,0); // delete locally stored arrays memory->sfree(body); memory->destroy(bodyown); memory->destroy(bodytag); memory->destroy(atom2body); memory->destroy(displace); memory->destroy(eflags); memory->destroy(orient); memory->destroy(dorient); delete random; delete [] infile; memory->destroy(langextra); memory->destroy(mass_body); } /* ---------------------------------------------------------------------- */ int FixRigidSmall::setmask() { int mask = 0; mask |= INITIAL_INTEGRATE; mask |= FINAL_INTEGRATE; if (langflag) mask |= POST_FORCE; mask |= PRE_NEIGHBOR; mask |= INITIAL_INTEGRATE_RESPA; mask |= FINAL_INTEGRATE_RESPA; return mask; } /* ---------------------------------------------------------------------- */ void FixRigidSmall::init() { int i; triclinic = domain->triclinic; // warn if more than one rigid fix int count = 0; for (i = 0; i < modify->nfix; i++) if (strcmp(modify->fix[i]->style,"rigid") == 0) count++; if (count > 1 && me == 0) error->warning(FLERR,"More than one fix rigid"); // error if npt,nph fix comes before rigid fix for (i = 0; i < modify->nfix; i++) { if (strcmp(modify->fix[i]->style,"npt") == 0) break; if (strcmp(modify->fix[i]->style,"nph") == 0) break; } if (i < modify->nfix) { for (int j = i; j < modify->nfix; j++) if (strcmp(modify->fix[j]->style,"rigid") == 0) error->all(FLERR,"Rigid fix must come before NPT/NPH fix"); } // timestep info dtv = update->dt; dtf = 0.5 * update->dt * force->ftm2v; dtq = 0.5 * update->dt; if (strstr(update->integrate_style,"respa")) step_respa = ((Respa *) update->integrate)->step; } /* ---------------------------------------------------------------------- setup static/dynamic properties of rigid bodies, using current atom info allows resetting of atom properties like mass between runs only do static initialization once if using an infile cannot do this until now, b/c requires comm->setup() to have setup stencil ------------------------------------------------------------------------- */ void FixRigidSmall::setup_pre_neighbor() { if (!staticflag || !infile) setup_bodies_static(); setup_bodies_dynamic(); } /* ---------------------------------------------------------------------- compute initial fcm and torque on bodies, also initial virial reset all particle velocities to be consistent with vcm and omega ------------------------------------------------------------------------- */ void FixRigidSmall::setup(int vflag) { int i,n,ibody; //check(1); // sum fcm, torque across all rigid bodies // fcm = force on COM // torque = torque around COM double **x = atom->x; double **f = atom->f; imageint *image = atom->image; int nlocal = atom->nlocal; double *xcm,*fcm,*tcm; double dx,dy,dz; double unwrap[3]; for (ibody = 0; ibody < nlocal_body+nghost_body; ibody++) { fcm = body[ibody].fcm; fcm[0] = fcm[1] = fcm[2] = 0.0; tcm = body[ibody].torque; tcm[0] = tcm[1] = tcm[2] = 0.0; } for (i = 0; i < nlocal; i++) { if (atom2body[i] < 0) continue; Body *b = &body[atom2body[i]]; fcm = b->fcm; fcm[0] += f[i][0]; fcm[1] += f[i][1]; fcm[2] += f[i][2]; domain->unmap(x[i],image[i],unwrap); xcm = b->xcm; dx = unwrap[0] - xcm[0]; dy = unwrap[1] - xcm[1]; dz = unwrap[2] - xcm[2]; tcm = b->torque; tcm[0] += dy * f[i][2] - dz * f[i][1]; tcm[1] += dz * f[i][0] - dx * f[i][2]; tcm[2] += dx * f[i][1] - dy * f[i][0]; } // extended particles add their rotation/torque to angmom/torque of body if (extended) { double **torque = atom->torque; for (i = 0; i < nlocal; i++) { if (atom2body[i] < 0) continue; Body *b = &body[atom2body[i]]; if (eflags[i] & TORQUE) { tcm = b->torque; tcm[0] += torque[i][0]; tcm[1] += torque[i][1]; tcm[2] += torque[i][2]; } } } // reverse communicate fcm, torque of all bodies commflag = FORCE_TORQUE; comm->reverse_comm_fix(this,6); // virial setup before call to set_v if (vflag) v_setup(vflag); else evflag = 0; // compute and forward communicate vcm and omega of all bodies for (ibody = 0; ibody < nlocal_body; ibody++) { Body *b = &body[ibody]; MathExtra::angmom_to_omega(b->angmom,b->ex_space,b->ey_space, b->ez_space,b->inertia,b->omega); } commflag = FINAL; comm->forward_comm_fix(this,10); // set velocity/rotation of atoms in rigid bodues set_v(); // guesstimate virial as 2x the set_v contribution if (vflag_global) for (n = 0; n < 6; n++) virial[n] *= 2.0; if (vflag_atom) { for (i = 0; i < nlocal; i++) for (n = 0; n < 6; n++) vatom[i][n] *= 2.0; } } /* ---------------------------------------------------------------------- */ void FixRigidSmall::initial_integrate(int vflag) { double dtfm; //check(2); for (int ibody = 0; ibody < nlocal_body; ibody++) { Body *b = &body[ibody]; // update vcm by 1/2 step dtfm = dtf / b->mass; b->vcm[0] += dtfm * b->fcm[0]; b->vcm[1] += dtfm * b->fcm[1]; b->vcm[2] += dtfm * b->fcm[2]; // update xcm by full step b->xcm[0] += dtv * b->vcm[0]; b->xcm[1] += dtv * b->vcm[1]; b->xcm[2] += dtv * b->vcm[2]; // update angular momentum by 1/2 step b->angmom[0] += dtf * b->torque[0]; b->angmom[1] += dtf * b->torque[1]; b->angmom[2] += dtf * b->torque[2]; // compute omega at 1/2 step from angmom at 1/2 step and current q // update quaternion a full step via Richardson iteration // returns new normalized quaternion, also updated omega at 1/2 step // update ex,ey,ez to reflect new quaternion MathExtra::angmom_to_omega(b->angmom,b->ex_space,b->ey_space, b->ez_space,b->inertia,b->omega); MathExtra::richardson(b->quat,b->angmom,b->omega,b->inertia,dtq); MathExtra::q_to_exyz(b->quat,b->ex_space,b->ey_space,b->ez_space); } // virial setup before call to set_xv if (vflag) v_setup(vflag); else evflag = 0; // forward communicate updated info of all bodies commflag = INITIAL; comm->forward_comm_fix(this,26); // set coords/orient and velocity/rotation of atoms in rigid bodies set_xv(); } /* ---------------------------------------------------------------------- apply Langevin thermostat to all 6 DOF of rigid bodies I own unlike fix langevin, this stores extra force in extra arrays, which are added in when final_integrate() calculates a new fcm/torque ------------------------------------------------------------------------- */ void FixRigidSmall::post_force(int vflag) { double gamma1,gamma2; // grow langextra if needed if (nlocal_body > maxlang) { memory->destroy(langextra); maxlang = nlocal_body + nghost_body; memory->create(langextra,maxlang,6,"rigid/small:langextra"); } double delta = update->ntimestep - update->beginstep; delta /= update->endstep - update->beginstep; double t_target = t_start + delta * (t_stop-t_start); double tsqrt = sqrt(t_target); double boltz = force->boltz; double dt = update->dt; double mvv2e = force->mvv2e; double ftm2v = force->ftm2v; double *vcm,*omega,*inertia; for (int ibody = 0; ibody < nlocal_body; ibody++) { vcm = body[ibody].vcm; omega = body[ibody].omega; inertia = body[ibody].inertia; gamma1 = -body[ibody].mass / t_period / ftm2v; gamma2 = sqrt(body[ibody].mass) * tsqrt * sqrt(24.0*boltz/t_period/dt/mvv2e) / ftm2v; langextra[ibody][0] = gamma1*vcm[0] + gamma2*(random->uniform()-0.5); langextra[ibody][1] = gamma1*vcm[1] + gamma2*(random->uniform()-0.5); langextra[ibody][2] = gamma1*vcm[2] + gamma2*(random->uniform()-0.5); gamma1 = -1.0 / t_period / ftm2v; gamma2 = tsqrt * sqrt(24.0*boltz/t_period/dt/mvv2e) / ftm2v; langextra[ibody][3] = inertia[0]*gamma1*omega[0] + sqrt(inertia[0])*gamma2*(random->uniform()-0.5); langextra[ibody][4] = inertia[1]*gamma1*omega[1] + sqrt(inertia[1])*gamma2*(random->uniform()-0.5); langextra[ibody][5] = inertia[2]*gamma1*omega[2] + sqrt(inertia[2])*gamma2*(random->uniform()-0.5); } } /* ---------------------------------------------------------------------- */ void FixRigidSmall::final_integrate() { int i,ibody; double dtfm; //check(3); // sum over atoms to get force and torque on rigid body imageint *image = atom->image; double **x = atom->x; double **f = atom->f; int nlocal = atom->nlocal; double dx,dy,dz; double unwrap[3]; double *xcm,*fcm,*tcm; for (ibody = 0; ibody < nlocal_body+nghost_body; ibody++) { fcm = body[ibody].fcm; fcm[0] = fcm[1] = fcm[2] = 0.0; tcm = body[ibody].torque; tcm[0] = tcm[1] = tcm[2] = 0.0; } for (i = 0; i < nlocal; i++) { if (atom2body[i] < 0) continue; Body *b = &body[atom2body[i]]; fcm = b->fcm; fcm[0] += f[i][0]; fcm[1] += f[i][1]; fcm[2] += f[i][2]; domain->unmap(x[i],image[i],unwrap); xcm = b->xcm; dx = unwrap[0] - xcm[0]; dy = unwrap[1] - xcm[1]; dz = unwrap[2] - xcm[2]; tcm = b->torque; tcm[0] += dy*f[i][2] - dz*f[i][1]; tcm[1] += dz*f[i][0] - dx*f[i][2]; tcm[2] += dx*f[i][1] - dy*f[i][0]; } // extended particles add their torque to torque of body if (extended) { double **torque = atom->torque; for (i = 0; i < nlocal; i++) { if (atom2body[i] < 0) continue; if (eflags[i] & TORQUE) { tcm = body[atom2body[i]].torque; tcm[0] += torque[i][0]; tcm[1] += torque[i][1]; tcm[2] += torque[i][2]; } } } // reverse communicate fcm, torque of all bodies commflag = FORCE_TORQUE; comm->reverse_comm_fix(this,6); // include Langevin thermostat forces and torques if (langflag) { for (int ibody = 0; ibody < nlocal_body; ibody++) { fcm = body[ibody].fcm; fcm[0] += langextra[ibody][0]; fcm[1] += langextra[ibody][1]; fcm[2] += langextra[ibody][2]; tcm = body[ibody].torque; tcm[0] += langextra[ibody][3]; tcm[1] += langextra[ibody][4]; tcm[2] += langextra[ibody][5]; } } // update vcm and angmom, recompute omega for (int ibody = 0; ibody < nlocal_body; ibody++) { Body *b = &body[ibody]; // update vcm by 1/2 step dtfm = dtf / b->mass; b->vcm[0] += dtfm * b->fcm[0]; b->vcm[1] += dtfm * b->fcm[1]; b->vcm[2] += dtfm * b->fcm[2]; // update angular momentum by 1/2 step b->angmom[0] += dtf * b->torque[0]; b->angmom[1] += dtf * b->torque[1]; b->angmom[2] += dtf * b->torque[2]; MathExtra::angmom_to_omega(b->angmom,b->ex_space,b->ey_space, b->ez_space,b->inertia,b->omega); } // forward communicate updated info of all bodies commflag = FINAL; comm->forward_comm_fix(this,10); // set velocity/rotation of atoms in rigid bodies // virial is already setup from initial_integrate set_v(); } /* ---------------------------------------------------------------------- */ void FixRigidSmall::initial_integrate_respa(int vflag, int ilevel, int iloop) { dtv = step_respa[ilevel]; dtf = 0.5 * step_respa[ilevel] * force->ftm2v; dtq = 0.5 * step_respa[ilevel]; if (ilevel == 0) initial_integrate(vflag); else final_integrate(); } /* ---------------------------------------------------------------------- */ void FixRigidSmall::final_integrate_respa(int ilevel, int iloop) { dtf = 0.5 * step_respa[ilevel] * force->ftm2v; final_integrate(); } /* ---------------------------------------------------------------------- atom to processor assignments have changed, so acquire ghost bodies and setup atom2body remap xcm of each rigid body back into periodic simulation box done during pre_neighbor so will be after call to pbc() and after fix_deform::pre_exchange() may have flipped box use domain->remap() in case xcm is far away from box due to 1st definition of rigid body or due to box flip if don't do this, then atoms of a body which drifts far away from a triclinic box will be remapped back into box with huge displacements when the box tilt changes via set_x() adjust image flag of body and image flags of all atoms in body ------------------------------------------------------------------------- */ void FixRigidSmall::pre_neighbor() { // remap xcm and image flags of each body as needed imageint original,oldimage,newimage; for (int ibody = 0; ibody < nlocal_body; ibody++) { Body *b = &body[ibody]; original = b->image; domain->remap(b->xcm,b->image); if (original == b->image) b->remapflag[3] = 0; else { oldimage = original & IMGMASK; newimage = b->image & IMGMASK; b->remapflag[0] = newimage - oldimage; oldimage = (original >> IMGBITS) & IMGMASK; newimage = (b->image >> IMGBITS) & IMGMASK; b->remapflag[1] = newimage - oldimage; oldimage = original >> IMG2BITS; newimage = b->image >> IMG2BITS; b->remapflag[2] = newimage - oldimage; b->remapflag[3] = 1; } } // acquire ghost bodies via forward comm // also gets new remapflags needed for adjusting atom image flags // reset atom2body for owned atoms // forward comm sets atom2body for ghost atoms nghost_body = 0; commflag = FULL_BODY; comm->forward_comm_fix(this); reset_atom2body(); //check(4); // adjust image flags of any atom in a rigid body whose xcm was remapped imageint *image = atom->image; int nlocal = atom->nlocal; imageint idim,otherdims; for (int i = 0; i < nlocal; i++) { if (atom2body[i] < 0) continue; Body *b = &body[atom2body[i]]; if (b->remapflag[3] == 0) continue; if (b->remapflag[0]) { idim = image[i] & IMGMASK; otherdims = image[i] ^ idim; idim -= b->remapflag[0]; idim &= IMGMASK; image[i] = otherdims | idim; } if (b->remapflag[1]) { idim = (image[i] >> IMGBITS) & IMGMASK; otherdims = image[i] ^ (idim << IMGBITS); idim -= b->remapflag[1]; idim &= IMGMASK; image[i] = otherdims | (idim << IMGBITS); } if (b->remapflag[2]) { idim = image[i] >> IMG2BITS; otherdims = image[i] ^ (idim << IMG2BITS); idim -= b->remapflag[2]; idim &= IMGMASK; image[i] = otherdims | (idim << IMG2BITS); } } } /* ---------------------------------------------------------------------- count # of DOF removed by rigid bodies for atoms in igroup return total count of DOF ------------------------------------------------------------------------- */ int FixRigidSmall::dof(int tgroup) { int i,j; // cannot count DOF correctly unless setup_bodies_static() has been called if (!staticflag) { if (comm->me == 0) error->warning(FLERR,"Cannot count rigid body degrees-of-freedom " "before bodies are fully initialized"); return 0; } int tgroupbit = group->bitmask[tgroup]; // counts = 3 values per rigid body I own // 0 = # of point particles in rigid body and in temperature group // 1 = # of finite-size particles in rigid body and in temperature group // 2 = # of particles in rigid body, disregarding temperature group memory->create(counts,nlocal_body+nghost_body,3,"rigid/small:counts"); for (int i = 0; i < nlocal_body+nghost_body; i++) counts[i][0] = counts[i][1] = counts[i][2] = 0; // tally counts from my owned atoms // 0 = # of point particles in rigid body and in temperature group // 1 = # of finite-size particles in rigid body and in temperature group // 2 = # of particles in rigid body, disregarding temperature group int *mask = atom->mask; int nlocal = atom->nlocal; for (i = 0; i < nlocal; i++) { if (atom2body[i] < 0) continue; j = atom2body[i]; counts[j][2]++; if (mask[i] & tgroupbit) { if (extended && eflags[i]) counts[j][1]++; else counts[j][0]++; } } commflag = DOF; comm->reverse_comm_fix(this,3); // nall = count0 = # of point particles in each rigid body // mall = count1 = # of finite-size particles in each rigid body // warn if nall+mall != nrigid for any body included in temperature group int flag = 0; for (int ibody = 0; ibody < nlocal_body; ibody++) { if (counts[ibody][0]+counts[ibody][1] > 0 && counts[ibody][0]+counts[ibody][1] != counts[ibody][2]) flag = 1; } int flagall; MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_MAX,world); if (flagall && me == 0) error->warning(FLERR,"Computing temperature of portions of rigid bodies"); // remove appropriate DOFs for each rigid body wholly in temperature group // N = # of point particles in body // M = # of finite-size particles in body // 3d body has 3N + 6M dof to start with // 2d body has 2N + 3M dof to start with // 3d point-particle body with all non-zero I should have 6 dof, remove 3N-6 // 3d point-particle body (linear) with a 0 I should have 5 dof, remove 3N-5 // 2d point-particle body should have 3 dof, remove 2N-3 // 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 double *inertia; int n = 0; if (domain->dimension == 3) { for (int ibody = 0; ibody < nlocal_body; ibody++) { if (counts[ibody][0]+counts[ibody][1] == counts[ibody][2]) { n += 3*counts[ibody][0] + 6*counts[ibody][1] - 6; inertia = body[ibody].inertia; if (inertia[0] == 0.0 || inertia[1] == 0.0 || inertia[2] == 0.0) n++; } } } else if (domain->dimension == 2) { for (int ibody = 0; ibody < nlocal_body; ibody++) if (counts[ibody][0]+counts[ibody][1] == counts[ibody][2]) n += 2*counts[ibody][0] + 3*counts[ibody][1] - 3; } memory->destroy(counts); int nall; MPI_Allreduce(&n,&nall,1,MPI_INT,MPI_SUM,world); return nall; } /* ---------------------------------------------------------------------- adjust xcm of each rigid body due to box deformation called by various fixes that change box size/shape flag = 0/1 means map from box to lamda coords or vice versa ------------------------------------------------------------------------- */ void FixRigidSmall::deform(int flag) { if (flag == 0) for (int ibody = 0; ibody < nlocal_body; ibody++) domain->x2lamda(body[ibody].xcm,body[ibody].xcm); else for (int ibody = 0; ibody < nlocal_body; ibody++) domain->lamda2x(body[ibody].xcm,body[ibody].xcm); } /* ---------------------------------------------------------------------- set space-frame coords and velocity of each atom in each rigid body set orientation and rotation of extended particles x = Q displace + Xcm, mapped back to periodic box v = Vcm + (W cross (x - Xcm)) ------------------------------------------------------------------------- */ void FixRigidSmall::set_xv() { int xbox,ybox,zbox; double x0,x1,x2,v0,v1,v2,fc0,fc1,fc2,massone; double ione[3],exone[3],eyone[3],ezone[3],vr[6],p[3][3]; double xprd = domain->xprd; double yprd = domain->yprd; double zprd = domain->zprd; double xy = domain->xy; double xz = domain->xz; double yz = domain->yz; imageint *image = atom->image; double **x = atom->x; double **v = atom->v; double **f = atom->f; double *rmass = atom->rmass; double *mass = atom->mass; int *type = atom->type; int nlocal = atom->nlocal; // set x and v of each atom for (int i = 0; i < nlocal; i++) { if (atom2body[i] < 0) continue; Body *b = &body[atom2body[i]]; xbox = (image[i] & IMGMASK) - IMGMAX; ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX; zbox = (image[i] >> IMG2BITS) - IMGMAX; // save old positions and velocities for virial if (evflag) { if (triclinic == 0) { x0 = x[i][0] + xbox*xprd; x1 = x[i][1] + ybox*yprd; x2 = x[i][2] + zbox*zprd; } else { x0 = x[i][0] + xbox*xprd + ybox*xy + zbox*xz; x1 = x[i][1] + ybox*yprd + zbox*yz; x2 = x[i][2] + zbox*zprd; } v0 = v[i][0]; v1 = v[i][1]; v2 = v[i][2]; } // x = displacement from center-of-mass, based on body orientation // v = vcm + omega around center-of-mass MathExtra::matvec(b->ex_space,b->ey_space,b->ez_space,displace[i],x[i]); v[i][0] = b->omega[1]*x[i][2] - b->omega[2]*x[i][1] + b->vcm[0]; v[i][1] = b->omega[2]*x[i][0] - b->omega[0]*x[i][2] + b->vcm[1]; v[i][2] = b->omega[0]*x[i][1] - b->omega[1]*x[i][0] + b->vcm[2]; // add center of mass to displacement // map back into periodic box via xbox,ybox,zbox // for triclinic, add in box tilt factors as well if (triclinic == 0) { x[i][0] += b->xcm[0] - xbox*xprd; x[i][1] += b->xcm[1] - ybox*yprd; x[i][2] += b->xcm[2] - zbox*zprd; } else { x[i][0] += b->xcm[0] - xbox*xprd - ybox*xy - zbox*xz; x[i][1] += b->xcm[1] - ybox*yprd - zbox*yz; x[i][2] += b->xcm[2] - zbox*zprd; } // virial = unwrapped coords dotted into body constraint force // body constraint force = implied force due to v change minus f external // assume f does not include forces internal to body // 1/2 factor b/c final_integrate contributes other half // assume per-atom contribution is due to constraint force on that atom if (evflag) { if (rmass) massone = rmass[i]; else massone = mass[type[i]]; fc0 = massone*(v[i][0] - v0)/dtf - f[i][0]; fc1 = massone*(v[i][1] - v1)/dtf - f[i][1]; fc2 = massone*(v[i][2] - v2)/dtf - f[i][2]; vr[0] = 0.5*x0*fc0; vr[1] = 0.5*x1*fc1; vr[2] = 0.5*x2*fc2; vr[3] = 0.5*x0*fc1; vr[4] = 0.5*x0*fc2; vr[5] = 0.5*x1*fc2; v_tally(1,&i,1.0,vr); } } // set orientation, omega, angmom of each extended particle if (extended) { double theta_body,theta; double *shape,*quatatom,*inertiaatom; AtomVecEllipsoid::Bonus *ebonus; if (avec_ellipsoid) ebonus = avec_ellipsoid->bonus; AtomVecLine::Bonus *lbonus; if (avec_line) lbonus = avec_line->bonus; AtomVecTri::Bonus *tbonus; if (avec_tri) tbonus = avec_tri->bonus; double **omega = atom->omega; double **angmom = atom->angmom; double **mu = atom->mu; int *ellipsoid = atom->ellipsoid; int *line = atom->line; int *tri = atom->tri; for (int i = 0; i < nlocal; i++) { if (atom2body[i] < 0) continue; Body *b = &body[atom2body[i]]; if (eflags[i] & SPHERE) { omega[i][0] = b->omega[0]; omega[i][1] = b->omega[1]; omega[i][2] = b->omega[2]; } else if (eflags[i] & ELLIPSOID) { shape = ebonus[ellipsoid[i]].shape; quatatom = ebonus[ellipsoid[i]].quat; MathExtra::quatquat(b->quat,orient[i],quatatom); MathExtra::qnormalize(quatatom); ione[0] = EINERTIA*rmass[i] * (shape[1]*shape[1] + shape[2]*shape[2]); ione[1] = EINERTIA*rmass[i] * (shape[0]*shape[0] + shape[2]*shape[2]); ione[2] = EINERTIA*rmass[i] * (shape[0]*shape[0] + shape[1]*shape[1]); MathExtra::q_to_exyz(quatatom,exone,eyone,ezone); MathExtra::omega_to_angmom(b->omega,exone,eyone,ezone,ione,angmom[i]); } else if (eflags[i] & LINE) { if (b->quat[3] >= 0.0) theta_body = 2.0*acos(b->quat[0]); else theta_body = -2.0*acos(b->quat[0]); theta = orient[i][0] + theta_body; while (theta <= MINUSPI) theta += TWOPI; while (theta > MY_PI) theta -= TWOPI; lbonus[line[i]].theta = theta; omega[i][0] = b->omega[0]; omega[i][1] = b->omega[1]; omega[i][2] = b->omega[2]; } else if (eflags[i] & TRIANGLE) { inertiaatom = tbonus[tri[i]].inertia; quatatom = tbonus[tri[i]].quat; MathExtra::quatquat(b->quat,orient[i],quatatom); MathExtra::qnormalize(quatatom); MathExtra::q_to_exyz(quatatom,exone,eyone,ezone); MathExtra::omega_to_angmom(b->omega,exone,eyone,ezone, inertiaatom,angmom[i]); } if (eflags[i] & DIPOLE) { MathExtra::quat_to_mat(b->quat,p); MathExtra::matvec(p,dorient[i],mu[i]); MathExtra::snormalize3(mu[i][3],mu[i],mu[i]); } } } } /* ---------------------------------------------------------------------- set space-frame velocity of each atom in a rigid body set omega and angmom of extended particles v = Vcm + (W cross (x - Xcm)) ------------------------------------------------------------------------- */ void FixRigidSmall::set_v() { int xbox,ybox,zbox; double x0,x1,x2,v0,v1,v2,fc0,fc1,fc2,massone; double ione[3],exone[3],eyone[3],ezone[3],delta[3],vr[6]; double xprd = domain->xprd; double yprd = domain->yprd; double zprd = domain->zprd; double xy = domain->xy; double xz = domain->xz; double yz = domain->yz; double **x = atom->x; double **v = atom->v; double **f = atom->f; double *rmass = atom->rmass; double *mass = atom->mass; int *type = atom->type; imageint *image = atom->image; int nlocal = atom->nlocal; // set v of each atom for (int i = 0; i < nlocal; i++) { if (atom2body[i] < 0) continue; Body *b = &body[atom2body[i]]; MathExtra::matvec(b->ex_space,b->ey_space,b->ez_space,displace[i],delta); // save old velocities for virial if (evflag) { v0 = v[i][0]; v1 = v[i][1]; v2 = v[i][2]; } v[i][0] = b->omega[1]*delta[2] - b->omega[2]*delta[1] + b->vcm[0]; v[i][1] = b->omega[2]*delta[0] - b->omega[0]*delta[2] + b->vcm[1]; v[i][2] = b->omega[0]*delta[1] - b->omega[1]*delta[0] + b->vcm[2]; // virial = unwrapped coords dotted into body constraint force // body constraint force = implied force due to v change minus f external // assume f does not include forces internal to body // 1/2 factor b/c initial_integrate contributes other half // assume per-atom contribution is due to constraint force on that atom if (evflag) { if (rmass) massone = rmass[i]; else massone = mass[type[i]]; fc0 = massone*(v[i][0] - v0)/dtf - f[i][0]; fc1 = massone*(v[i][1] - v1)/dtf - f[i][1]; fc2 = massone*(v[i][2] - v2)/dtf - f[i][2]; xbox = (image[i] & IMGMASK) - IMGMAX; ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX; zbox = (image[i] >> IMG2BITS) - IMGMAX; if (triclinic == 0) { x0 = x[i][0] + xbox*xprd; x1 = x[i][1] + ybox*yprd; x2 = x[i][2] + zbox*zprd; } else { x0 = x[i][0] + xbox*xprd + ybox*xy + zbox*xz; x1 = x[i][1] + ybox*yprd + zbox*yz; x2 = x[i][2] + zbox*zprd; } vr[0] = 0.5*x0*fc0; vr[1] = 0.5*x1*fc1; vr[2] = 0.5*x2*fc2; vr[3] = 0.5*x0*fc1; vr[4] = 0.5*x0*fc2; vr[5] = 0.5*x1*fc2; v_tally(1,&i,1.0,vr); } } // set omega, angmom of each extended particle if (extended) { double *shape,*quatatom,*inertiaatom; AtomVecEllipsoid::Bonus *ebonus; if (avec_ellipsoid) ebonus = avec_ellipsoid->bonus; AtomVecTri::Bonus *tbonus; if (avec_tri) tbonus = avec_tri->bonus; double **omega = atom->omega; double **angmom = atom->angmom; int *ellipsoid = atom->ellipsoid; int *tri = atom->tri; for (int i = 0; i < nlocal; i++) { if (atom2body[i] < 0) continue; Body *b = &body[atom2body[i]]; if (eflags[i] & SPHERE) { omega[i][0] = b->omega[0]; omega[i][1] = b->omega[1]; omega[i][2] = b->omega[2]; } else if (eflags[i] & ELLIPSOID) { shape = ebonus[ellipsoid[i]].shape; quatatom = ebonus[ellipsoid[i]].quat; ione[0] = EINERTIA*rmass[i] * (shape[1]*shape[1] + shape[2]*shape[2]); ione[1] = EINERTIA*rmass[i] * (shape[0]*shape[0] + shape[2]*shape[2]); ione[2] = EINERTIA*rmass[i] * (shape[0]*shape[0] + shape[1]*shape[1]); MathExtra::q_to_exyz(quatatom,exone,eyone,ezone); MathExtra::omega_to_angmom(b->omega,exone,eyone,ezone,ione, angmom[i]); } else if (eflags[i] & LINE) { omega[i][0] = b->omega[0]; omega[i][1] = b->omega[1]; omega[i][2] = b->omega[2]; } else if (eflags[i] & TRIANGLE) { inertiaatom = tbonus[tri[i]].inertia; quatatom = tbonus[tri[i]].quat; MathExtra::q_to_exyz(quatatom,exone,eyone,ezone); MathExtra::omega_to_angmom(b->omega,exone,eyone,ezone, inertiaatom,angmom[i]); } } } } /* ---------------------------------------------------------------------- one-time identification of which atoms are in which rigid bodies set bodytag for all owned atoms ------------------------------------------------------------------------- */ void FixRigidSmall::create_bodies() { int i,m,n; double unwrap[3]; // error check on image flags of atoms in rigid bodies imageint *image = atom->image; int *mask = atom->mask; int nlocal = atom->nlocal; int *periodicity = domain->periodicity; int xbox,ybox,zbox; int flag = 0; for (i = 0; i < nlocal; i++) { if (!(mask[i] & groupbit)) continue; xbox = (image[i] & IMGMASK) - IMGMAX; ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX; zbox = (image[i] >> IMG2BITS) - IMGMAX; if ((xbox && !periodicity[0]) || (ybox && !periodicity[1]) || (zbox && !periodicity[2])) flag = 1; } int flagall; MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_SUM,world); if (flagall) error->all(FLERR,"Fix rigid/small atom has non-zero image flag " "in a non-periodic dimension"); // allocate buffer for passing messages around ring of procs // percount = max number of values to put in buffer for each of ncount int ncount = 0; for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) ncount++; int percount = 5; double *buf; memory->create(buf,ncount*percount,"rigid/small:buf"); // create map hash for storing unique molecule IDs of my atoms // key = molecule ID // value = index into per-body data structure // n = # of entries in hash hash = new std::map<tagint,int>(); hash->clear(); // setup hash // key = body ID // value = index into N-length data structure // n = count of unique bodies my atoms are part of tagint *molecule = atom->molecule; n = 0; for (i = 0; i < nlocal; i++) { if (!(mask[i] & groupbit)) continue; if (hash->find(molecule[i]) == hash->end()) (*hash)[molecule[i]] = n++; } // bbox = bounding box of each rigid body my atoms are part of memory->create(bbox,n,6,"rigid/small:bbox"); for (i = 0; i < n; i++) { bbox[i][0] = bbox[i][2] = bbox[i][4] = BIG; bbox[i][1] = bbox[i][3] = bbox[i][5] = -BIG; } // pack my atoms into buffer as molecule ID, unwrapped coords double **x = atom->x; m = 0; for (i = 0; i < nlocal; i++) { if (!(mask[i] & groupbit)) continue; domain->unmap(x[i],image[i],unwrap); buf[m++] = molecule[i]; buf[m++] = unwrap[0]; buf[m++] = unwrap[1]; buf[m++] = unwrap[2]; } // pass buffer around ring of procs // func = update bbox with atom coords from every proc // when done, have full bbox for every rigid body my atoms are part of frsptr = this; comm->ring(m,sizeof(double),buf,1,ring_bbox,NULL); // ctr = center pt of each rigid body my atoms are part of memory->create(ctr,n,6,"rigid/small:bbox"); for (i = 0; i < n; i++) { ctr[i][0] = 0.5 * (bbox[i][0] + bbox[i][1]); ctr[i][1] = 0.5 * (bbox[i][2] + bbox[i][3]); ctr[i][2] = 0.5 * (bbox[i][4] + bbox[i][5]); } // idclose = ID of atom in body closest to center pt (smaller ID if tied) // rsqclose = distance squared from idclose to center pt memory->create(idclose,n,"rigid/small:idclose"); memory->create(rsqclose,n,"rigid/small:rsqclose"); for (i = 0; i < n; i++) rsqclose[i] = BIG; // pack my atoms into buffer as molecule ID, atom ID, unwrapped coords tagint *tag = atom->tag; m = 0; for (i = 0; i < nlocal; i++) { if (!(mask[i] & groupbit)) continue; domain->unmap(x[i],image[i],unwrap); buf[m++] = molecule[i]; buf[m++] = ubuf(tag[i]).d; buf[m++] = unwrap[0]; buf[m++] = unwrap[1]; buf[m++] = unwrap[2]; } // pass buffer around ring of procs // func = update idclose,rsqclose with atom IDs from every proc // when done, have idclose for every rigid body my atoms are part of frsptr = this; comm->ring(m,sizeof(double),buf,2,ring_nearest,NULL); // set bodytag of all owned atoms, based on idclose // find max value of rsqclose across all procs double rsqmax = 0.0; for (i = 0; i < nlocal; i++) { bodytag[i] = 0; if (!(mask[i] & groupbit)) continue; m = hash->find(molecule[i])->second; bodytag[i] = idclose[m]; rsqmax = MAX(rsqmax,rsqclose[m]); } // pack my atoms into buffer as bodytag of owning atom, unwrapped coords m = 0; for (i = 0; i < nlocal; i++) { if (!(mask[i] & groupbit)) continue; domain->unmap(x[i],image[i],unwrap); buf[m++] = ubuf(bodytag[i]).d; buf[m++] = unwrap[0]; buf[m++] = unwrap[1]; buf[m++] = unwrap[2]; } // pass buffer around ring of procs // func = update rsqfar for atoms belonging to bodies I own // when done, have rsqfar for all atoms in bodies I own rsqfar = 0.0; frsptr = this; comm->ring(m,sizeof(double),buf,3,ring_farthest,NULL); // find maxextent of rsqfar across all procs // if defined, include molecule->maxextent MPI_Allreduce(&rsqfar,&maxextent,1,MPI_DOUBLE,MPI_MAX,world); maxextent = sqrt(maxextent); if (onemols) { for (int i = 0; i < nmol; i++) maxextent = MAX(maxextent,onemols[i]->maxextent); } // clean up delete hash; memory->destroy(buf); memory->destroy(bbox); memory->destroy(ctr); memory->destroy(idclose); memory->destroy(rsqclose); } /* ---------------------------------------------------------------------- process rigid body atoms from another proc update bounding box for rigid bodies my atoms are part of ------------------------------------------------------------------------- */ void FixRigidSmall::ring_bbox(int n, char *cbuf) { std::map<tagint,int> *hash = frsptr->hash; double **bbox = frsptr->bbox; double *buf = (double *) cbuf; int ndatums = n/4; int j,imol; double *x; int m = 0; for (int i = 0; i < ndatums; i++, m += 4) { imol = static_cast<int> (buf[m]); if (hash->find(imol) != hash->end()) { j = hash->find(imol)->second; x = &buf[m+1]; bbox[j][0] = MIN(bbox[j][0],x[0]); bbox[j][1] = MAX(bbox[j][1],x[0]); bbox[j][2] = MIN(bbox[j][2],x[1]); bbox[j][3] = MAX(bbox[j][3],x[1]); bbox[j][4] = MIN(bbox[j][4],x[2]); bbox[j][5] = MAX(bbox[j][5],x[2]); } } } /* ---------------------------------------------------------------------- process rigid body atoms from another proc update nearest atom to body center for rigid bodies my atoms are part of ------------------------------------------------------------------------- */ void FixRigidSmall::ring_nearest(int n, char *cbuf) { std::map<tagint,int> *hash = frsptr->hash; double **ctr = frsptr->ctr; tagint *idclose = frsptr->idclose; double *rsqclose = frsptr->rsqclose; double *buf = (double *) cbuf; int ndatums = n/5; int j,imol; tagint tag; double delx,dely,delz,rsq; double *x; int m = 0; for (int i = 0; i < ndatums; i++, m += 5) { imol = static_cast<int> (buf[m]); if (hash->find(imol) != hash->end()) { j = hash->find(imol)->second; tag = (tagint) ubuf(buf[m+1]).i; x = &buf[m+2]; delx = x[0] - ctr[j][0]; dely = x[1] - ctr[j][1]; delz = x[2] - ctr[j][2]; rsq = delx*delx + dely*dely + delz*delz; if (rsq <= rsqclose[j]) { if (rsq == rsqclose[j] && tag > idclose[j]) continue; idclose[j] = tag; rsqclose[j] = rsq; } } } } /* ---------------------------------------------------------------------- process rigid body atoms from another proc update rsqfar = distance from owning atom to other atom ------------------------------------------------------------------------- */ void FixRigidSmall::ring_farthest(int n, char *cbuf) { double **x = frsptr->atom->x; imageint *image = frsptr->atom->image; int nlocal = frsptr->atom->nlocal; double *buf = (double *) cbuf; int ndatums = n/4; int iowner; tagint tag; double delx,dely,delz,rsq; double *xx; double unwrap[3]; int m = 0; for (int i = 0; i < ndatums; i++, m += 4) { tag = (tagint) ubuf(buf[m]).i; iowner = frsptr->atom->map(tag); if (iowner < 0 || iowner >= nlocal) continue; frsptr->domain->unmap(x[iowner],image[iowner],unwrap); xx = &buf[m+1]; delx = xx[0] - unwrap[0]; dely = xx[1] - unwrap[1]; delz = xx[2] - unwrap[2]; rsq = delx*delx + dely*dely + delz*delz; frsptr->rsqfar = MAX(frsptr->rsqfar,rsq); } } /* ---------------------------------------------------------------------- initialization of rigid body attributes called at setup, so body/atom properties can be changed between runs unless reading from infile, in which case only called once sets extended flags, masstotal, center-of-mass sets Cartesian and diagonalized inertia tensor sets body image flags, but only on first call ------------------------------------------------------------------------- */ void FixRigidSmall::setup_bodies_static() { int i,ibody; // extended = 1 if any particle in a rigid body is finite size // or has a dipole moment extended = orientflag = dorientflag = 0; AtomVecEllipsoid::Bonus *ebonus; if (avec_ellipsoid) ebonus = avec_ellipsoid->bonus; AtomVecLine::Bonus *lbonus; if (avec_line) lbonus = avec_line->bonus; AtomVecTri::Bonus *tbonus; if (avec_tri) tbonus = avec_tri->bonus; double **mu = atom->mu; double *radius = atom->radius; double *rmass = atom->rmass; double *mass = atom->mass; int *ellipsoid = atom->ellipsoid; int *line = atom->line; int *tri = atom->tri; int *type = atom->type; int nlocal = atom->nlocal; if (atom->radius_flag || atom->ellipsoid_flag || atom->line_flag || atom->tri_flag || atom->mu_flag) { int flag = 0; for (i = 0; i < nlocal; i++) { if (bodytag[i] == 0) continue; if (radius && radius[i] > 0.0) flag = 1; if (ellipsoid && ellipsoid[i] >= 0) flag = 1; if (line && line[i] >= 0) flag = 1; if (tri && tri[i] >= 0) flag = 1; if (mu && mu[i][3] > 0.0) flag = 1; } MPI_Allreduce(&flag,&extended,1,MPI_INT,MPI_MAX,world); } // extended = 1 if using molecule template with finite-size particles // require all molecules in template to have consistent radiusflag if (onemols) { int radiusflag = onemols[0]->radiusflag; for (i = 1; i < nmol; i++) { if (onemols[i]->radiusflag != radiusflag) error->all(FLERR,"Inconsistent use of finite-size particles " "by molecule template molecules"); } if (radiusflag) extended = 1; } // grow extended arrays and set extended flags for each particle // orientflag = 4 if any particle stores ellipsoid or tri orientation // orientflag = 1 if any particle stores line orientation // dorientflag = 1 if any particle stores dipole orientation if (extended) { - grow_arrays(atom->nmax); if (atom->ellipsoid_flag) orientflag = 4; if (atom->line_flag) orientflag = 1; if (atom->tri_flag) orientflag = 4; if (atom->mu_flag) dorientflag = 1; + grow_arrays(atom->nmax); for (i = 0; i < nlocal; i++) { eflags[i] = 0; if (bodytag[i] == 0) continue; // set to POINT or SPHERE or ELLIPSOID or LINE if (radius && radius[i] > 0.0) { eflags[i] |= SPHERE; eflags[i] |= OMEGA; eflags[i] |= TORQUE; } else if (ellipsoid && ellipsoid[i] >= 0) { eflags[i] |= ELLIPSOID; eflags[i] |= ANGMOM; eflags[i] |= TORQUE; } else if (line && line[i] >= 0) { eflags[i] |= LINE; eflags[i] |= OMEGA; eflags[i] |= TORQUE; } else if (tri && tri[i] >= 0) { eflags[i] |= TRIANGLE; eflags[i] |= ANGMOM; eflags[i] |= TORQUE; } else eflags[i] |= POINT; // set DIPOLE if atom->mu and mu[3] > 0.0 if (atom->mu_flag && mu[i][3] > 0.0) eflags[i] |= DIPOLE; } } // acquire ghost bodies via forward comm // set atom2body for ghost atoms via forward comm // set atom2body for other owned atoms via reset_atom2body() nghost_body = 0; commflag = FULL_BODY; comm->forward_comm_fix(this); reset_atom2body(); // compute mass & center-of-mass of each rigid body double **x = atom->x; imageint *image = atom->image; double *xcm; for (ibody = 0; ibody < nlocal_body+nghost_body; ibody++) { xcm = body[ibody].xcm; xcm[0] = xcm[1] = xcm[2] = 0.0; body[ibody].mass = 0.0; } double unwrap[3]; double massone; for (i = 0; i < nlocal; i++) { if (atom2body[i] < 0) continue; Body *b = &body[atom2body[i]]; if (rmass) massone = rmass[i]; else massone = mass[type[i]]; domain->unmap(x[i],image[i],unwrap); xcm = b->xcm; xcm[0] += unwrap[0] * massone; xcm[1] += unwrap[1] * massone; xcm[2] += unwrap[2] * massone; b->mass += massone; } // reverse communicate xcm, mass of all bodies commflag = XCM_MASS; comm->reverse_comm_fix(this,4); for (ibody = 0; ibody < nlocal_body; ibody++) { xcm = body[ibody].xcm; xcm[0] /= body[ibody].mass; xcm[1] /= body[ibody].mass; xcm[2] /= body[ibody].mass; } // overwrite masstotal and center-of-mass with file values // inbody[i] = 0/1 if Ith rigid body is initialized by file int *inbody; if (infile) { memory->create(inbody,nlocal_body,"rigid/small:inbody"); for (ibody = 0; ibody < nlocal_body; ibody++) inbody[ibody] = 0; readfile(0,NULL,inbody); } // set image flags for each rigid body to default values // then remap the xcm of each body back into simulation box if needed // staticflag check insures this in only done once, not on successive runs if (!staticflag) for (ibody = 0; ibody < nlocal_body; ibody++) body[ibody].image = ((imageint) IMGMAX << IMG2BITS) | ((imageint) IMGMAX << IMGBITS) | IMGMAX; pre_neighbor(); // compute 6 moments of inertia of each body in Cartesian reference frame // dx,dy,dz = coords relative to center-of-mass // symmetric 3x3 inertia tensor stored in Voigt notation as 6-vector memory->create(itensor,nlocal_body+nghost_body,6,"rigid/small:itensor"); for (ibody = 0; ibody < nlocal_body+nghost_body; ibody++) for (i = 0; i < 6; i++) itensor[ibody][i] = 0.0; double dx,dy,dz; double *inertia; for (i = 0; i < nlocal; i++) { if (atom2body[i] < 0) continue; Body *b = &body[atom2body[i]]; domain->unmap(x[i],image[i],unwrap); xcm = b->xcm; dx = unwrap[0] - xcm[0]; dy = unwrap[1] - xcm[1]; dz = unwrap[2] - xcm[2]; if (rmass) massone = rmass[i]; else massone = mass[type[i]]; inertia = itensor[atom2body[i]]; inertia[0] += massone * (dy*dy + dz*dz); inertia[1] += massone * (dx*dx + dz*dz); inertia[2] += massone * (dx*dx + dy*dy); inertia[3] -= massone * dy*dz; inertia[4] -= massone * dx*dz; inertia[5] -= massone * dx*dy; } // extended particles may contribute extra terms to moments of inertia if (extended) { double ivec[6]; double *shape,*quatatom,*inertiaatom; double length,theta; for (i = 0; i < nlocal; i++) { if (atom2body[i] < 0) continue; inertia = itensor[atom2body[i]]; if (rmass) massone = rmass[i]; else massone = mass[type[i]]; if (eflags[i] & SPHERE) { inertia[0] += SINERTIA*massone * radius[i]*radius[i]; inertia[1] += SINERTIA*massone * radius[i]*radius[i]; inertia[2] += SINERTIA*massone * radius[i]*radius[i]; } else if (eflags[i] & ELLIPSOID) { shape = ebonus[ellipsoid[i]].shape; quatatom = ebonus[ellipsoid[i]].quat; MathExtra::inertia_ellipsoid(shape,quatatom,massone,ivec); inertia[0] += ivec[0]; inertia[1] += ivec[1]; inertia[2] += ivec[2]; inertia[3] += ivec[3]; inertia[4] += ivec[4]; inertia[5] += ivec[5]; } else if (eflags[i] & LINE) { length = lbonus[line[i]].length; theta = lbonus[line[i]].theta; MathExtra::inertia_line(length,theta,massone,ivec); inertia[0] += ivec[0]; inertia[1] += ivec[1]; inertia[2] += ivec[2]; inertia[3] += ivec[3]; inertia[4] += ivec[4]; inertia[5] += ivec[5]; } else if (eflags[i] & TRIANGLE) { inertiaatom = tbonus[tri[i]].inertia; quatatom = tbonus[tri[i]].quat; MathExtra::inertia_triangle(inertiaatom,quatatom,massone,ivec); inertia[0] += ivec[0]; inertia[1] += ivec[1]; inertia[2] += ivec[2]; inertia[3] += ivec[3]; inertia[4] += ivec[4]; inertia[5] += ivec[5]; } } } // reverse communicate inertia tensor of all bodies commflag = ITENSOR; comm->reverse_comm_fix(this,6); // overwrite Cartesian inertia tensor with file values if (infile) readfile(1,itensor,inbody); // diagonalize inertia tensor for each body via Jacobi rotations // inertia = 3 eigenvalues = principal moments of inertia // evectors and exzy_space = 3 evectors = principal axes of rigid body int ierror; double cross[3]; double tensor[3][3],evectors[3][3]; double *ex,*ey,*ez; for (ibody = 0; ibody < nlocal_body; ibody++) { tensor[0][0] = itensor[ibody][0]; tensor[1][1] = itensor[ibody][1]; tensor[2][2] = itensor[ibody][2]; tensor[1][2] = tensor[2][1] = itensor[ibody][3]; tensor[0][2] = tensor[2][0] = itensor[ibody][4]; tensor[0][1] = tensor[1][0] = itensor[ibody][5]; inertia = body[ibody].inertia; ierror = MathExtra::jacobi(tensor,inertia,evectors); if (ierror) error->all(FLERR, "Insufficient Jacobi rotations for rigid body"); ex = body[ibody].ex_space; ex[0] = evectors[0][0]; ex[1] = evectors[1][0]; ex[2] = evectors[2][0]; ey = body[ibody].ey_space; ey[0] = evectors[0][1]; ey[1] = evectors[1][1]; ey[2] = evectors[2][1]; ez = body[ibody].ez_space; ez[0] = evectors[0][2]; ez[1] = evectors[1][2]; ez[2] = evectors[2][2]; // if any principal moment < scaled EPSILON, set to 0.0 double max; max = MAX(inertia[0],inertia[1]); max = MAX(max,inertia[2]); if (inertia[0] < EPSILON*max) inertia[0] = 0.0; if (inertia[1] < EPSILON*max) inertia[1] = 0.0; if (inertia[2] < EPSILON*max) inertia[2] = 0.0; // enforce 3 evectors as a right-handed coordinate system // flip 3rd vector if needed MathExtra::cross3(ex,ey,cross); if (MathExtra::dot3(cross,ez) < 0.0) MathExtra::negate3(ez); // create initial quaternion MathExtra::exyz_to_q(ex,ey,ez,body[ibody].quat); } // forward communicate updated info of all bodies commflag = INITIAL; comm->forward_comm_fix(this,26); // displace = initial atom coords in basis of principal axes // set displace = 0.0 for atoms not in any rigid body // for extended particles, set their orientation wrt to rigid body double qc[4],delta[3]; double *quatatom; double theta_body; for (i = 0; i < nlocal; i++) { if (atom2body[i] < 0) { displace[i][0] = displace[i][1] = displace[i][2] = 0.0; continue; } Body *b = &body[atom2body[i]]; domain->unmap(x[i],image[i],unwrap); xcm = b->xcm; delta[0] = unwrap[0] - xcm[0]; delta[1] = unwrap[1] - xcm[1]; delta[2] = unwrap[2] - xcm[2]; MathExtra::transpose_matvec(b->ex_space,b->ey_space,b->ez_space, delta,displace[i]); if (extended) { if (eflags[i] & ELLIPSOID) { quatatom = ebonus[ellipsoid[i]].quat; MathExtra::qconjugate(b->quat,qc); MathExtra::quatquat(qc,quatatom,orient[i]); MathExtra::qnormalize(orient[i]); } else if (eflags[i] & LINE) { if (b->quat[3] >= 0.0) theta_body = 2.0*acos(b->quat[0]); else theta_body = -2.0*acos(b->quat[0]); orient[i][0] = lbonus[line[i]].theta - theta_body; while (orient[i][0] <= MINUSPI) orient[i][0] += TWOPI; while (orient[i][0] > MY_PI) orient[i][0] -= TWOPI; if (orientflag == 4) orient[i][1] = orient[i][2] = orient[i][3] = 0.0; } else if (eflags[i] & TRIANGLE) { quatatom = tbonus[tri[i]].quat; MathExtra::qconjugate(b->quat,qc); MathExtra::quatquat(qc,quatatom,orient[i]); MathExtra::qnormalize(orient[i]); } else if (orientflag == 4) { orient[i][0] = orient[i][1] = orient[i][2] = orient[i][3] = 0.0; } else if (orientflag == 1) orient[i][0] = 0.0; if (eflags[i] & DIPOLE) { MathExtra::transpose_matvec(b->ex_space,b->ey_space,b->ez_space, mu[i],dorient[i]); MathExtra::snormalize3(mu[i][3],dorient[i],dorient[i]); } else if (dorientflag) dorient[i][0] = dorient[i][1] = dorient[i][2] = 0.0; } } // test for valid principal moments & axes // recompute moments of inertia around new axes // 3 diagonal moments should equal principal moments // 3 off-diagonal moments should be 0.0 // extended particles may contribute extra terms to moments of inertia for (ibody = 0; ibody < nlocal_body+nghost_body; ibody++) for (i = 0; i < 6; i++) itensor[ibody][i] = 0.0; for (i = 0; i < nlocal; i++) { if (atom2body[i] < 0) continue; inertia = itensor[atom2body[i]]; if (rmass) massone = rmass[i]; else massone = mass[type[i]]; inertia[0] += massone * (displace[i][1]*displace[i][1] + displace[i][2]*displace[i][2]); inertia[1] += massone * (displace[i][0]*displace[i][0] + displace[i][2]*displace[i][2]); inertia[2] += massone * (displace[i][0]*displace[i][0] + displace[i][1]*displace[i][1]); inertia[3] -= massone * displace[i][1]*displace[i][2]; inertia[4] -= massone * displace[i][0]*displace[i][2]; inertia[5] -= massone * displace[i][0]*displace[i][1]; } if (extended) { double ivec[6]; double *shape,*inertiaatom; double length; for (i = 0; i < nlocal; i++) { if (atom2body[i] < 0) continue; inertia = itensor[atom2body[i]]; if (rmass) massone = rmass[i]; else massone = mass[type[i]]; if (eflags[i] & SPHERE) { inertia[0] += SINERTIA*massone * radius[i]*radius[i]; inertia[1] += SINERTIA*massone * radius[i]*radius[i]; inertia[2] += SINERTIA*massone * radius[i]*radius[i]; } else if (eflags[i] & ELLIPSOID) { shape = ebonus[ellipsoid[i]].shape; MathExtra::inertia_ellipsoid(shape,orient[i],massone,ivec); inertia[0] += ivec[0]; inertia[1] += ivec[1]; inertia[2] += ivec[2]; inertia[3] += ivec[3]; inertia[4] += ivec[4]; inertia[5] += ivec[5]; } else if (eflags[i] & LINE) { length = lbonus[line[i]].length; MathExtra::inertia_line(length,orient[i][0],massone,ivec); inertia[0] += ivec[0]; inertia[1] += ivec[1]; inertia[2] += ivec[2]; inertia[3] += ivec[3]; inertia[4] += ivec[4]; inertia[5] += ivec[5]; } else if (eflags[i] & TRIANGLE) { inertiaatom = tbonus[tri[i]].inertia; MathExtra::inertia_triangle(inertiaatom,orient[i],massone,ivec); inertia[0] += ivec[0]; inertia[1] += ivec[1]; inertia[2] += ivec[2]; inertia[3] += ivec[3]; inertia[4] += ivec[4]; inertia[5] += ivec[5]; } } } // reverse communicate inertia tensor of all bodies commflag = ITENSOR; comm->reverse_comm_fix(this,6); // error check that re-computed momemts of inertia match diagonalized ones // do not do test for bodies with params read from infile double norm; for (ibody = 0; ibody < nlocal_body; ibody++) { if (infile && inbody[ibody]) continue; inertia = body[ibody].inertia; if (inertia[0] == 0.0) { if (fabs(itensor[ibody][0]) > TOLERANCE) error->all(FLERR,"Fix rigid: Bad principal moments"); } else { if (fabs((itensor[ibody][0]-inertia[0])/inertia[0]) > TOLERANCE) error->all(FLERR,"Fix rigid: Bad principal moments"); } if (inertia[1] == 0.0) { if (fabs(itensor[ibody][1]) > TOLERANCE) error->all(FLERR,"Fix rigid: Bad principal moments"); } else { if (fabs((itensor[ibody][1]-inertia[1])/inertia[1]) > TOLERANCE) error->all(FLERR,"Fix rigid: Bad principal moments"); } if (inertia[2] == 0.0) { if (fabs(itensor[ibody][2]) > TOLERANCE) error->all(FLERR,"Fix rigid: Bad principal moments"); } else { if (fabs((itensor[ibody][2]-inertia[2])/inertia[2]) > TOLERANCE) error->all(FLERR,"Fix rigid: Bad principal moments"); } norm = (inertia[0] + inertia[1] + inertia[2]) / 3.0; if (fabs(itensor[ibody][3]/norm) > TOLERANCE || fabs(itensor[ibody][4]/norm) > TOLERANCE || fabs(itensor[ibody][5]/norm) > TOLERANCE) error->all(FLERR,"Fix rigid: Bad principal moments"); } // clean up memory->destroy(itensor); if (infile) memory->destroy(inbody); // static properties have now been initialized once // used to prevent re-initialization which would re-read infile staticflag = 1; } /* ---------------------------------------------------------------------- one-time initialization of dynamic rigid body attributes Vcm and angmom, computed explicitly from constituent particles even if wrong for overlapping particles, is OK, since is just setting initial time=0 Vcm and angmom of the body which can be estimated value ------------------------------------------------------------------------- */ void FixRigidSmall::setup_bodies_dynamic() { int i,ibody; double massone,radone; // sum vcm, angmom across all rigid bodies // vcm = velocity of COM // angmom = angular momentum around COM double **x = atom->x; double **v = atom->v; double *rmass = atom->rmass; double *mass = atom->mass; int *type = atom->type; imageint *image = atom->image; int nlocal = atom->nlocal; double *xcm,*vcm,*acm; double dx,dy,dz; double unwrap[3]; for (ibody = 0; ibody < nlocal_body+nghost_body; ibody++) { vcm = body[ibody].vcm; vcm[0] = vcm[1] = vcm[2] = 0.0; acm = body[ibody].angmom; acm[0] = acm[1] = acm[2] = 0.0; } for (i = 0; i < nlocal; i++) { if (atom2body[i] < 0) continue; Body *b = &body[atom2body[i]]; if (rmass) massone = rmass[i]; else massone = mass[type[i]]; vcm = b->vcm; vcm[0] += v[i][0] * massone; vcm[1] += v[i][1] * massone; vcm[2] += v[i][2] * massone; domain->unmap(x[i],image[i],unwrap); xcm = b->xcm; dx = unwrap[0] - xcm[0]; dy = unwrap[1] - xcm[1]; dz = unwrap[2] - xcm[2]; acm = b->angmom; acm[0] += dy * massone*v[i][2] - dz * massone*v[i][1]; acm[1] += dz * massone*v[i][0] - dx * massone*v[i][2]; acm[2] += dx * massone*v[i][1] - dy * massone*v[i][0]; } // extended particles add their rotation to angmom of body if (extended) { AtomVecLine::Bonus *lbonus; if (avec_line) lbonus = avec_line->bonus; double **omega = atom->omega; double **angmom = atom->angmom; double *radius = atom->radius; int *line = atom->line; for (i = 0; i < nlocal; i++) { if (atom2body[i] < 0) continue; Body *b = &body[atom2body[i]]; if (eflags[i] & OMEGA) { if (eflags[i] & SPHERE) { radone = radius[i]; acm = b->angmom; acm[0] += SINERTIA*rmass[i] * radone*radone * omega[i][0]; acm[1] += SINERTIA*rmass[i] * radone*radone * omega[i][1]; acm[2] += SINERTIA*rmass[i] * radone*radone * omega[i][2]; } else if (eflags[i] & LINE) { radone = lbonus[line[i]].length; b->angmom[2] += LINERTIA*rmass[i] * radone*radone * omega[i][2]; } } if (eflags[i] & ANGMOM) { acm = b->angmom; acm[0] += angmom[i][0]; acm[1] += angmom[i][1]; acm[2] += angmom[i][2]; } } } // reverse communicate vcm, angmom of all bodies commflag = VCM_ANGMOM; comm->reverse_comm_fix(this,6); // normalize velocity of COM for (ibody = 0; ibody < nlocal_body; ibody++) { vcm = body[ibody].vcm; vcm[0] /= body[ibody].mass; vcm[1] /= body[ibody].mass; vcm[2] /= body[ibody].mass; } } /* ---------------------------------------------------------------------- read per rigid body info from user-provided file which = 0 to read total mass and center-of-mass which = 1 to read 6 moments of inertia, store in array flag inbody = 0 for local bodies whose info is read from file nlines = # of lines of rigid body info, 0 is OK one line = rigid-ID mass xcm ycm zcm ixx iyy izz ixy ixz iyz and rigid-ID = mol-ID for fix rigid/small ------------------------------------------------------------------------- */ void FixRigidSmall::readfile(int which, double **array, int *inbody) { int i,j,m,nchunk,eofflag,nlines; tagint id; FILE *fp; char *eof,*start,*next,*buf; char line[MAXLINE]; // create local hash with key/value pairs // key = mol ID of bodies my atoms own // value = index into local body array int nlocal = atom->nlocal; hash = new std::map<tagint,int>(); for (i = 0; i < nlocal; i++) if (bodyown[i] >= 0) (*hash)[atom->molecule[i]] = bodyown[i]; // open file and read header if (me == 0) { fp = fopen(infile,"r"); if (fp == NULL) { char str[128]; sprintf(str,"Cannot open fix rigid/small infile %s",infile); error->one(FLERR,str); } while (1) { eof = fgets(line,MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of fix rigid/small file"); start = &line[strspn(line," \t\n\v\f\r")]; if (*start != '\0' && *start != '#') break; } sscanf(line,"%d",&nlines); } MPI_Bcast(&nlines,1,MPI_INT,0,world); char *buffer = new char[CHUNK*MAXLINE]; char **values = new char*[ATTRIBUTE_PERBODY]; int nread = 0; while (nread < nlines) { nchunk = MIN(nlines-nread,CHUNK); eofflag = comm->read_lines_from_file(fp,nchunk,MAXLINE,buffer); if (eofflag) error->all(FLERR,"Unexpected end of fix rigid/small file"); buf = buffer; next = strchr(buf,'\n'); *next = '\0'; int nwords = atom->count_words(buf); *next = '\n'; if (nwords != ATTRIBUTE_PERBODY) error->all(FLERR,"Incorrect rigid body format in fix rigid/small file"); // loop over lines of rigid body attributes // tokenize the line into values // id = rigid body ID = mol-ID // for which = 0, store mass/com in vec/array // for which = 1, store interia tensor array, invert 3,4,5 values to Voigt for (int i = 0; i < nchunk; i++) { next = strchr(buf,'\n'); values[0] = strtok(buf," \t\n\r\f"); for (j = 1; j < nwords; j++) values[j] = strtok(NULL," \t\n\r\f"); id = ATOTAGINT(values[0]); if (id <= 0 || id > maxmol) error->all(FLERR,"Invalid rigid body ID in fix rigid/small file"); if (hash->find(id) == hash->end()) { buf = next + 1; continue; } m = (*hash)[id]; inbody[m] = 1; if (which == 0) { body[m].mass = atof(values[1]); body[m].xcm[0] = atof(values[2]); body[m].xcm[1] = atof(values[3]); body[m].xcm[2] = atof(values[4]); } else { array[m][0] = atof(values[5]); array[m][1] = atof(values[6]); array[m][2] = atof(values[7]); array[m][3] = atof(values[10]); array[m][4] = atof(values[9]); array[m][5] = atof(values[8]); } buf = next + 1; } nread += nchunk; } if (me == 0) fclose(fp); delete [] buffer; delete [] values; delete hash; } /* ---------------------------------------------------------------------- write out restart info for mass, COM, inertia tensor to file identical format to infile option, so info can be read in when restarting each proc contributes info for rigid bodies it owns ------------------------------------------------------------------------- */ void FixRigidSmall::write_restart_file(char *file) { FILE *fp; // do not write file if bodies have not yet been intialized if (!staticflag) return; // proc 0 opens file and writes header if (me == 0) { char outfile[128]; sprintf(outfile,"%s.rigid",file); fp = fopen(outfile,"w"); if (fp == NULL) { char str[128]; sprintf(str,"Cannot open fix rigid restart file %s",outfile); error->one(FLERR,str); } fprintf(fp,"# fix rigid mass, COM, inertia tensor info for " "%d bodies on timestep " BIGINT_FORMAT "\n\n", nbody,update->ntimestep); fprintf(fp,"%d\n",nbody); } // communication buffer for all my rigid body info // max_size = largest buffer needed by any proc int ncol = 11; int sendrow = nlocal_body; int maxrow; MPI_Allreduce(&sendrow,&maxrow,1,MPI_INT,MPI_MAX,world); double **buf; if (me == 0) memory->create(buf,MAX(1,maxrow),ncol,"rigid/small:buf"); else memory->create(buf,MAX(1,sendrow),ncol,"rigid/small:buf"); // pack my rigid body info into buf // compute I tensor against xyz axes from diagonalized I and current quat // Ispace = P Idiag P_transpose // P is stored column-wise in exyz_space double p[3][3],pdiag[3][3],ispace[3][3]; for (int i = 0; i < nlocal_body; i++) { MathExtra::col2mat(body[i].ex_space,body[i].ey_space,body[i].ez_space,p); MathExtra::times3_diag(p,body[i].inertia,pdiag); MathExtra::times3_transpose(pdiag,p,ispace); buf[i][0] = atom->molecule[body[i].ilocal]; buf[i][1] = body[i].mass; buf[i][2] = body[i].xcm[0]; buf[i][3] = body[i].xcm[1]; buf[i][4] = body[i].xcm[2]; buf[i][5] = ispace[0][0]; buf[i][6] = ispace[1][1]; buf[i][7] = ispace[2][2]; buf[i][8] = ispace[0][1]; buf[i][9] = ispace[0][2]; buf[i][10] = ispace[1][2]; } // write one chunk of rigid body info per proc to file // proc 0 pings each proc, receives its chunk, writes to file // all other procs wait for ping, send their chunk to proc 0 int tmp,recvrow; MPI_Status status; MPI_Request request; if (me == 0) { for (int iproc = 0; iproc < nprocs; iproc++) { if (iproc) { MPI_Irecv(&buf[0][0],maxrow*ncol,MPI_DOUBLE,iproc,0,world,&request); MPI_Send(&tmp,0,MPI_INT,iproc,0,world); MPI_Wait(&request,&status); MPI_Get_count(&status,MPI_DOUBLE,&recvrow); recvrow /= ncol; } else recvrow = sendrow; for (int i = 0; i < recvrow; i++) fprintf(fp,"%d %-1.16e %-1.16e %-1.16e %-1.16e " "%-1.16e %-1.16e %-1.16e %-1.16e %-1.16e %-1.16e\n", static_cast<int> (buf[i][0]),buf[i][1], buf[i][2],buf[i][3],buf[i][4], buf[i][5],buf[i][6],buf[i][7],buf[i][8],buf[i][9],buf[i][10]); } } else { MPI_Recv(&tmp,0,MPI_INT,0,0,world,&status); MPI_Rsend(&buf[0][0],sendrow*ncol,MPI_DOUBLE,0,0,world); } // clean up and close file memory->destroy(buf); if (me == 0) fclose(fp); } /* ---------------------------------------------------------------------- allocate local atom-based arrays ------------------------------------------------------------------------- */ void FixRigidSmall::grow_arrays(int nmax) { memory->grow(bodyown,nmax,"rigid/small:bodyown"); memory->grow(bodytag,nmax,"rigid/small:bodytag"); memory->grow(atom2body,nmax,"rigid/small:atom2body"); memory->grow(displace,nmax,3,"rigid/small:displace"); if (extended) { memory->grow(eflags,nmax,"rigid/small:eflags"); if (orientflag) memory->grow(orient,nmax,orientflag,"rigid/small:orient"); if (dorientflag) memory->grow(dorient,nmax,3,"rigid/small:dorient"); } } /* ---------------------------------------------------------------------- copy values within local atom-based arrays ------------------------------------------------------------------------- */ void FixRigidSmall::copy_arrays(int i, int j, int delflag) { bodytag[j] = bodytag[i]; displace[j][0] = displace[i][0]; displace[j][1] = displace[i][1]; displace[j][2] = displace[i][2]; if (extended) { eflags[j] = eflags[i]; for (int k = 0; k < orientflag; k++) orient[j][k] = orient[i][k]; if (dorientflag) { dorient[j][0] = dorient[i][0]; dorient[j][1] = dorient[i][1]; dorient[j][2] = dorient[i][2]; } } // if deleting atom J via delflag and J owns a body, then delete it if (delflag && bodyown[j] >= 0) { bodyown[body[nlocal_body-1].ilocal] = bodyown[j]; memcpy(&body[bodyown[j]],&body[nlocal_body-1],sizeof(Body)); nlocal_body--; } // if atom I owns a body, reset I's body.ilocal to loc J // do NOT do this if self-copy (I=J) since I's body is already deleted if (bodyown[i] >= 0 && i != j) body[bodyown[i]].ilocal = j; bodyown[j] = bodyown[i]; } /* ---------------------------------------------------------------------- initialize one atom's array values, called when atom is created ------------------------------------------------------------------------- */ void FixRigidSmall::set_arrays(int i) { bodyown[i] = -1; bodytag[i] = 0; atom2body[i] = -1; displace[i][0] = 0.0; displace[i][1] = 0.0; displace[i][2] = 0.0; } /* ---------------------------------------------------------------------- initialize a molecule inserted by another fix, e.g. deposit or pour called when molecule is created nlocalprev = # of atoms on this proc before molecule inserted tagprev = atom ID previous to new atoms in the molecule xgeom = geometric center of new molecule vcm = COM velocity of new molecule quat = rotation of new molecule (around geometric center) relative to template in Molecule class ------------------------------------------------------------------------- */ void FixRigidSmall::set_molecule(int nlocalprev, tagint tagprev, int imol, double *xgeom, double *vcm, double *quat) { int m; double ctr2com[3],ctr2com_rotate[3]; double rotmat[3][3]; int nlocal = atom->nlocal; if (nlocalprev == nlocal) return; tagint *tag = atom->tag; for (int i = nlocalprev; i < nlocal; i++) { bodytag[i] = tagprev + onemols[imol]->comatom; if (tag[i]-tagprev == onemols[imol]->comatom) bodyown[i] = nlocal_body; m = tag[i] - tagprev-1; displace[i][0] = onemols[imol]->dxbody[m][0]; displace[i][1] = onemols[imol]->dxbody[m][1]; displace[i][2] = onemols[imol]->dxbody[m][2]; eflags[i] = 0; if (onemols[imol]->radiusflag) { eflags[i] |= SPHERE; eflags[i] |= OMEGA; eflags[i] |= TORQUE; } if (bodyown[i] >= 0) { if (nlocal_body == nmax_body) grow_body(); Body *b = &body[nlocal_body]; b->mass = onemols[imol]->masstotal; // new COM = Q (onemols[imol]->xcm - onemols[imol]->center) + xgeom // Q = rotation matrix associated with quat MathExtra::quat_to_mat(quat,rotmat); MathExtra::sub3(onemols[imol]->com,onemols[imol]->center,ctr2com); MathExtra::matvec(rotmat,ctr2com,ctr2com_rotate); MathExtra::add3(ctr2com_rotate,xgeom,b->xcm); b->vcm[0] = vcm[0]; b->vcm[1] = vcm[1]; b->vcm[2] = vcm[2]; b->inertia[0] = onemols[imol]->inertia[0]; b->inertia[1] = onemols[imol]->inertia[1]; b->inertia[2] = onemols[imol]->inertia[2]; // final quat is product of insertion quat and original quat // true even if insertion rotation was not around COM MathExtra::quatquat(quat,onemols[imol]->quat,b->quat); MathExtra::q_to_exyz(b->quat,b->ex_space,b->ey_space,b->ez_space); b->angmom[0] = b->angmom[1] = b->angmom[2] = 0.0; b->omega[0] = b->omega[1] = b->omega[2] = 0.0; b->image = ((imageint) IMGMAX << IMG2BITS) | ((imageint) IMGMAX << IMGBITS) | IMGMAX; b->ilocal = i; nlocal_body++; } } // increment total # of rigid bodies nbody++; } /* ---------------------------------------------------------------------- pack values in local atom-based arrays for exchange with another proc ------------------------------------------------------------------------- */ int FixRigidSmall::pack_exchange(int i, double *buf) { buf[0] = ubuf(bodytag[i]).d; buf[1] = displace[i][0]; buf[2] = displace[i][1]; buf[3] = displace[i][2]; // extended attribute info int m = 4; if (extended) { buf[m++] = eflags[i]; for (int j = 0; j < orientflag; j++) buf[m++] = orient[i][j]; if (dorientflag) { buf[m++] = dorient[i][0]; buf[m++] = dorient[i][1]; buf[m++] = dorient[i][2]; } } // atom not in a rigid body if (!bodytag[i]) return m; // atom does not own its rigid body if (bodyown[i] < 0) { buf[m++] = 0; return m; } // body info for atom that owns a rigid body buf[m++] = 1; memcpy(&buf[m],&body[bodyown[i]],sizeof(Body)); m += bodysize; return m; } /* ---------------------------------------------------------------------- unpack values in local atom-based arrays from exchange with another proc ------------------------------------------------------------------------- */ int FixRigidSmall::unpack_exchange(int nlocal, double *buf) { bodytag[nlocal] = (tagint) ubuf(buf[0]).i; displace[nlocal][0] = buf[1]; displace[nlocal][1] = buf[2]; displace[nlocal][2] = buf[3]; // extended attribute info int m = 4; if (extended) { eflags[nlocal] = static_cast<int> (buf[m++]); for (int j = 0; j < orientflag; j++) orient[nlocal][j] = buf[m++]; if (dorientflag) { dorient[nlocal][0] = buf[m++]; dorient[nlocal][1] = buf[m++]; dorient[nlocal][2] = buf[m++]; } } // atom not in a rigid body if (!bodytag[nlocal]) { bodyown[nlocal] = -1; return m; } // atom does not own its rigid body bodyown[nlocal] = static_cast<int> (buf[m++]); if (bodyown[nlocal] == 0) { bodyown[nlocal] = -1; return m; } // body info for atom that owns a rigid body if (nlocal_body == nmax_body) grow_body(); memcpy(&body[nlocal_body],&buf[m],sizeof(Body)); m += bodysize; body[nlocal_body].ilocal = nlocal; bodyown[nlocal] = nlocal_body++; return m; } /* ---------------------------------------------------------------------- only pack body info if own or ghost atom owns the body for FULL_BODY, send 0/1 flag with every atom ------------------------------------------------------------------------- */ int FixRigidSmall::pack_forward_comm(int n, int *list, double *buf, int pbc_flag, int *pbc) { int i,j; double *xcm,*vcm,*quat,*omega,*ex_space,*ey_space,*ez_space,*conjqm; int m = 0; if (commflag == INITIAL) { for (i = 0; i < n; i++) { j = list[i]; if (bodyown[j] < 0) continue; xcm = body[bodyown[j]].xcm; buf[m++] = xcm[0]; buf[m++] = xcm[1]; buf[m++] = xcm[2]; vcm = body[bodyown[j]].vcm; buf[m++] = vcm[0]; buf[m++] = vcm[1]; buf[m++] = vcm[2]; quat = body[bodyown[j]].quat; buf[m++] = quat[0]; buf[m++] = quat[1]; buf[m++] = quat[2]; buf[m++] = quat[3]; omega = body[bodyown[j]].omega; buf[m++] = omega[0]; buf[m++] = omega[1]; buf[m++] = omega[2]; ex_space = body[bodyown[j]].ex_space; buf[m++] = ex_space[0]; buf[m++] = ex_space[1]; buf[m++] = ex_space[2]; ey_space = body[bodyown[j]].ey_space; buf[m++] = ey_space[0]; buf[m++] = ey_space[1]; buf[m++] = ey_space[2]; ez_space = body[bodyown[j]].ez_space; buf[m++] = ez_space[0]; buf[m++] = ez_space[1]; buf[m++] = ez_space[2]; conjqm = body[bodyown[j]].conjqm; buf[m++] = conjqm[0]; buf[m++] = conjqm[1]; buf[m++] = conjqm[2]; buf[m++] = conjqm[3]; } } else if (commflag == FINAL) { for (i = 0; i < n; i++) { j = list[i]; if (bodyown[j] < 0) continue; vcm = body[bodyown[j]].vcm; buf[m++] = vcm[0]; buf[m++] = vcm[1]; buf[m++] = vcm[2]; omega = body[bodyown[j]].omega; buf[m++] = omega[0]; buf[m++] = omega[1]; buf[m++] = omega[2]; conjqm = body[bodyown[j]].conjqm; buf[m++] = conjqm[0]; buf[m++] = conjqm[1]; buf[m++] = conjqm[2]; buf[m++] = conjqm[3]; } } else if (commflag == FULL_BODY) { for (i = 0; i < n; i++) { j = list[i]; if (bodyown[j] < 0) buf[m++] = 0; else { buf[m++] = 1; memcpy(&buf[m],&body[bodyown[j]],sizeof(Body)); m += bodysize; } } } return m; } /* ---------------------------------------------------------------------- only ghost atoms are looped over for FULL_BODY, store a new ghost body if this atom owns it for other commflag values, only unpack body info if atom owns it ------------------------------------------------------------------------- */ void FixRigidSmall::unpack_forward_comm(int n, int first, double *buf) { int i,j,last; double *xcm,*vcm,*quat,*omega,*ex_space,*ey_space,*ez_space,*conjqm; int m = 0; last = first + n; if (commflag == INITIAL) { for (i = first; i < last; i++) { if (bodyown[i] < 0) continue; xcm = body[bodyown[i]].xcm; xcm[0] = buf[m++]; xcm[1] = buf[m++]; xcm[2] = buf[m++]; vcm = body[bodyown[i]].vcm; vcm[0] = buf[m++]; vcm[1] = buf[m++]; vcm[2] = buf[m++]; quat = body[bodyown[i]].quat; quat[0] = buf[m++]; quat[1] = buf[m++]; quat[2] = buf[m++]; quat[3] = buf[m++]; omega = body[bodyown[i]].omega; omega[0] = buf[m++]; omega[1] = buf[m++]; omega[2] = buf[m++]; ex_space = body[bodyown[i]].ex_space; ex_space[0] = buf[m++]; ex_space[1] = buf[m++]; ex_space[2] = buf[m++]; ey_space = body[bodyown[i]].ey_space; ey_space[0] = buf[m++]; ey_space[1] = buf[m++]; ey_space[2] = buf[m++]; ez_space = body[bodyown[i]].ez_space; ez_space[0] = buf[m++]; ez_space[1] = buf[m++]; ez_space[2] = buf[m++]; conjqm = body[bodyown[i]].conjqm; conjqm[0] = buf[m++]; conjqm[1] = buf[m++]; conjqm[2] = buf[m++]; conjqm[3] = buf[m++]; } } else if (commflag == FINAL) { for (i = first; i < last; i++) { if (bodyown[i] < 0) continue; vcm = body[bodyown[i]].vcm; vcm[0] = buf[m++]; vcm[1] = buf[m++]; vcm[2] = buf[m++]; omega = body[bodyown[i]].omega; omega[0] = buf[m++]; omega[1] = buf[m++]; omega[2] = buf[m++]; conjqm = body[bodyown[i]].conjqm; conjqm[0] = buf[m++]; conjqm[1] = buf[m++]; conjqm[2] = buf[m++]; conjqm[3] = buf[m++]; } } else if (commflag == FULL_BODY) { for (i = first; i < last; i++) { bodyown[i] = static_cast<int> (buf[m++]); if (bodyown[i] == 0) bodyown[i] = -1; else { j = nlocal_body + nghost_body; if (j == nmax_body) grow_body(); memcpy(&body[j],&buf[m],sizeof(Body)); m += bodysize; body[j].ilocal = i; bodyown[i] = j; nghost_body++; } } } } /* ---------------------------------------------------------------------- only ghost atoms are looped over only pack body info if atom owns it ------------------------------------------------------------------------- */ int FixRigidSmall::pack_reverse_comm(int n, int first, double *buf) { int i,j,m,last; double *fcm,*torque,*vcm,*angmom,*xcm; m = 0; last = first + n; if (commflag == FORCE_TORQUE) { for (i = first; i < last; i++) { if (bodyown[i] < 0) continue; fcm = body[bodyown[i]].fcm; buf[m++] = fcm[0]; buf[m++] = fcm[1]; buf[m++] = fcm[2]; torque = body[bodyown[i]].torque; buf[m++] = torque[0]; buf[m++] = torque[1]; buf[m++] = torque[2]; } } else if (commflag == VCM_ANGMOM) { for (i = first; i < last; i++) { if (bodyown[i] < 0) continue; vcm = body[bodyown[i]].vcm; buf[m++] = vcm[0]; buf[m++] = vcm[1]; buf[m++] = vcm[2]; angmom = body[bodyown[i]].angmom; buf[m++] = angmom[0]; buf[m++] = angmom[1]; buf[m++] = angmom[2]; } } else if (commflag == XCM_MASS) { for (i = first; i < last; i++) { if (bodyown[i] < 0) continue; xcm = body[bodyown[i]].xcm; buf[m++] = xcm[0]; buf[m++] = xcm[1]; buf[m++] = xcm[2]; buf[m++] = body[bodyown[i]].mass; } } else if (commflag == ITENSOR) { for (i = first; i < last; i++) { if (bodyown[i] < 0) continue; j = bodyown[i]; buf[m++] = itensor[j][0]; buf[m++] = itensor[j][1]; buf[m++] = itensor[j][2]; buf[m++] = itensor[j][3]; buf[m++] = itensor[j][4]; buf[m++] = itensor[j][5]; } } else if (commflag == DOF) { for (i = first; i < last; i++) { if (bodyown[i] < 0) continue; j = bodyown[i]; buf[m++] = counts[j][0]; buf[m++] = counts[j][1]; buf[m++] = counts[j][2]; } } return m; } /* ---------------------------------------------------------------------- only unpack body info if own or ghost atom owns the body ------------------------------------------------------------------------- */ void FixRigidSmall::unpack_reverse_comm(int n, int *list, double *buf) { int i,j,k; double *fcm,*torque,*vcm,*angmom,*xcm; int m = 0; if (commflag == FORCE_TORQUE) { for (i = 0; i < n; i++) { j = list[i]; if (bodyown[j] < 0) continue; fcm = body[bodyown[j]].fcm; fcm[0] += buf[m++]; fcm[1] += buf[m++]; fcm[2] += buf[m++]; torque = body[bodyown[j]].torque; torque[0] += buf[m++]; torque[1] += buf[m++]; torque[2] += buf[m++]; } } else if (commflag == VCM_ANGMOM) { for (i = 0; i < n; i++) { j = list[i]; if (bodyown[j] < 0) continue; vcm = body[bodyown[j]].vcm; vcm[0] += buf[m++]; vcm[1] += buf[m++]; vcm[2] += buf[m++]; angmom = body[bodyown[j]].angmom; angmom[0] += buf[m++]; angmom[1] += buf[m++]; angmom[2] += buf[m++]; } } else if (commflag == XCM_MASS) { for (i = 0; i < n; i++) { j = list[i]; if (bodyown[j] < 0) continue; xcm = body[bodyown[j]].xcm; xcm[0] += buf[m++]; xcm[1] += buf[m++]; xcm[2] += buf[m++]; body[bodyown[j]].mass += buf[m++]; } } else if (commflag == ITENSOR) { for (i = 0; i < n; i++) { j = list[i]; if (bodyown[j] < 0) continue; k = bodyown[j]; itensor[k][0] += buf[m++]; itensor[k][1] += buf[m++]; itensor[k][2] += buf[m++]; itensor[k][3] += buf[m++]; itensor[k][4] += buf[m++]; itensor[k][5] += buf[m++]; } } else if (commflag == DOF) { for (i = 0; i < n; i++) { j = list[i]; if (bodyown[j] < 0) continue; k = bodyown[j]; counts[k][0] += static_cast<int> (buf[m++]); counts[k][1] += static_cast<int> (buf[m++]); counts[k][2] += static_cast<int> (buf[m++]); } } } /* ---------------------------------------------------------------------- grow body data structure ------------------------------------------------------------------------- */ void FixRigidSmall::grow_body() { nmax_body += DELTA_BODY; body = (Body *) memory->srealloc(body,nmax_body*sizeof(Body), "rigid/small:body"); } /* ---------------------------------------------------------------------- reset atom2body for all owned atoms do this via bodyown of atom that owns the body the owned atom is in atom2body values can point to original body or any image of the body ------------------------------------------------------------------------- */ void FixRigidSmall::reset_atom2body() { int iowner; // iowner = index of atom that owns the body that atom I is in int nlocal = atom->nlocal; for (int i = 0; i < nlocal; i++) { atom2body[i] = -1; if (bodytag[i]) { iowner = atom->map(bodytag[i]); if (iowner == -1) { char str[128]; sprintf(str, "Rigid body atoms " TAGINT_FORMAT " " TAGINT_FORMAT " missing on proc %d at step " BIGINT_FORMAT, atom->tag[i],bodytag[i],comm->me,update->ntimestep); error->one(FLERR,str); } atom2body[i] = bodyown[iowner]; } } } /* ---------------------------------------------------------------------- */ void FixRigidSmall::reset_dt() { dtv = update->dt; dtf = 0.5 * update->dt * force->ftm2v; dtq = 0.5 * update->dt; } /* ---------------------------------------------------------------------- zero linear momentum of each rigid body set Vcm to 0.0, then reset velocities of particles via set_v() ------------------------------------------------------------------------- */ void FixRigidSmall::zero_momentum() { double *vcm; for (int ibody = 0; ibody < nlocal_body+nghost_body; ibody++) { vcm = body[ibody].vcm; vcm[0] = vcm[1] = vcm[2] = 0.0; } // forward communicate of vcm to all ghost copies commflag = FINAL; comm->forward_comm_fix(this,10); // set velocity of atoms in rigid bodues evflag = 0; set_v(); } /* ---------------------------------------------------------------------- zero angular momentum of each rigid body set angmom/omega to 0.0, then reset velocities of particles via set_v() ------------------------------------------------------------------------- */ void FixRigidSmall::zero_rotation() { double *angmom,*omega; for (int ibody = 0; ibody < nlocal_body+nghost_body; ibody++) { angmom = body[ibody].angmom; angmom[0] = angmom[1] = angmom[2] = 0.0; omega = body[ibody].omega; omega[0] = omega[1] = omega[2] = 0.0; } // forward communicate of omega to all ghost copies commflag = FINAL; comm->forward_comm_fix(this,10); // set velocity of atoms in rigid bodues evflag = 0; set_v(); } /* ---------------------------------------------------------------------- */ void *FixRigidSmall::extract(const char *str, int &dim) { if (strcmp(str,"body") == 0) { dim = 1; return atom2body; } if (strcmp(str,"onemol") == 0) { dim = 0; return onemols; } // return vector of rigid body masses, for owned+ghost bodies // used by granular pair styles, indexed by atom2body if (strcmp(str,"masstotal") == 0) { dim = 1; if (nmax_mass < nmax_body) { memory->destroy(mass_body); nmax_mass = nmax_body; memory->create(mass_body,nmax_mass,"rigid:mass_body"); } int n = nlocal_body + nghost_body; for (int i = 0; i < n; i++) mass_body[i] = body[i].mass; return mass_body; } return NULL; } /* ---------------------------------------------------------------------- return translational KE for all rigid bodies KE = 1/2 M Vcm^2 sum local body results across procs ------------------------------------------------------------------------- */ double FixRigidSmall::extract_ke() { double *vcm; double ke = 0.0; for (int i = 0; i < nlocal_body; i++) { vcm = body[i].vcm; ke += body[i].mass * (vcm[0]*vcm[0] + vcm[1]*vcm[1] + vcm[2]*vcm[2]); } double keall; MPI_Allreduce(&ke,&keall,1,MPI_DOUBLE,MPI_SUM,world); return 0.5*keall; } /* ---------------------------------------------------------------------- return rotational KE for all rigid bodies Erotational = 1/2 I wbody^2 ------------------------------------------------------------------------- */ double FixRigidSmall::extract_erotational() { double wbody[3],rot[3][3]; double *inertia; double erotate = 0.0; for (int i = 0; i < nlocal_body; i++) { // for Iw^2 rotational term, need wbody = angular velocity in body frame // not omega = angular velocity in space frame inertia = body[i].inertia; MathExtra::quat_to_mat(body[i].quat,rot); MathExtra::transpose_matvec(rot,body[i].angmom,wbody); if (inertia[0] == 0.0) wbody[0] = 0.0; else wbody[0] /= inertia[0]; if (inertia[1] == 0.0) wbody[1] = 0.0; else wbody[1] /= inertia[1]; if (inertia[2] == 0.0) wbody[2] = 0.0; else wbody[2] /= inertia[2]; erotate += inertia[0]*wbody[0]*wbody[0] + inertia[1]*wbody[1]*wbody[1] + inertia[2]*wbody[2]*wbody[2]; } double erotateall; MPI_Allreduce(&erotate,&erotateall,1,MPI_DOUBLE,MPI_SUM,world); return 0.5*erotateall; } /* ---------------------------------------------------------------------- return temperature of collection of rigid bodies non-active DOF are removed by fflag/tflag and in tfactor ------------------------------------------------------------------------- */ double FixRigidSmall::compute_scalar() { double wbody[3],rot[3][3]; double *vcm,*inertia; double t = 0.0; for (int i = 0; i < nlocal_body; i++) { vcm = body[i].vcm; t += body[i].mass * (vcm[0]*vcm[0] + vcm[1]*vcm[1] + vcm[2]*vcm[2]); // for Iw^2 rotational term, need wbody = angular velocity in body frame // not omega = angular velocity in space frame inertia = body[i].inertia; MathExtra::quat_to_mat(body[i].quat,rot); MathExtra::transpose_matvec(rot,body[i].angmom,wbody); if (inertia[0] == 0.0) wbody[0] = 0.0; else wbody[0] /= inertia[0]; if (inertia[1] == 0.0) wbody[1] = 0.0; else wbody[1] /= inertia[1]; if (inertia[2] == 0.0) wbody[2] = 0.0; else wbody[2] /= inertia[2]; t += inertia[0]*wbody[0]*wbody[0] + inertia[1]*wbody[1]*wbody[1] + inertia[2]*wbody[2]*wbody[2]; } double tall; MPI_Allreduce(&t,&tall,1,MPI_DOUBLE,MPI_SUM,world); double tfactor = force->mvv2e / (6.0*nbody * force->boltz); tall *= tfactor; return tall; } /* ---------------------------------------------------------------------- memory usage of local atom-based arrays ------------------------------------------------------------------------- */ double FixRigidSmall::memory_usage() { int nmax = atom->nmax; double bytes = 2 * nmax * sizeof(int); bytes += nmax*3 * sizeof(double); bytes += maxvatom*6 * sizeof(double); // vatom if (extended) { bytes += nmax * sizeof(int); if (orientflag) bytes = nmax*orientflag * sizeof(double); if (dorientflag) bytes = nmax*3 * sizeof(double); } bytes += nmax_body * sizeof(Body); return bytes; } /* ---------------------------------------------------------------------- debug method for sanity checking of atom/body data pointers ------------------------------------------------------------------------- */ /* void FixRigidSmall::check(int flag) { for (int i = 0; i < atom->nlocal; i++) { if (bodyown[i] >= 0) { if (bodytag[i] != atom->tag[i]) { printf("Proc %d, step %ld, flag %d\n",comm->me,update->ntimestep,flag); errorx->one(FLERR,"BAD AAA"); } if (bodyown[i] < 0 || bodyown[i] >= nlocal_body) { printf("Proc %d, step %ld, flag %d\n",comm->me,update->ntimestep,flag); errorx->one(FLERR,"BAD BBB"); } if (atom2body[i] != bodyown[i]) { printf("Proc %d, step %ld, flag %d\n",comm->me,update->ntimestep,flag); errorx->one(FLERR,"BAD CCC"); } if (body[bodyown[i]].ilocal != i) { printf("Proc %d, step %ld, flag %d\n",comm->me,update->ntimestep,flag); errorx->one(FLERR,"BAD DDD"); } } } for (int i = 0; i < atom->nlocal; i++) { if (bodyown[i] < 0 && bodytag[i] > 0) { if (atom2body[i] < 0 || atom2body[i] >= nlocal_body+nghost_body) { printf("Proc %d, step %ld, flag %d\n",comm->me,update->ntimestep,flag); errorx->one(FLERR,"BAD EEE"); } if (bodytag[i] != atom->tag[body[atom2body[i]].ilocal]) { printf("Proc %d, step %ld, flag %d\n",comm->me,update->ntimestep,flag); errorx->one(FLERR,"BAD FFF"); } } } for (int i = atom->nlocal; i < atom->nlocal + atom->nghost; i++) { if (bodyown[i] >= 0) { if (bodyown[i] < nlocal_body || bodyown[i] >= nlocal_body+nghost_body) { printf("Values %d %d: %d %d %d\n", i,atom->tag[i],bodyown[i],nlocal_body,nghost_body); printf("Proc %d, step %ld, flag %d\n",comm->me,update->ntimestep,flag); errorx->one(FLERR,"BAD GGG"); } if (body[bodyown[i]].ilocal != i) { printf("Proc %d, step %ld, flag %d\n",comm->me,update->ntimestep,flag); errorx->one(FLERR,"BAD HHH"); } } } for (int i = 0; i < nlocal_body; i++) { if (body[i].ilocal < 0 || body[i].ilocal >= atom->nlocal) { printf("Proc %d, step %ld, flag %d\n",comm->me,update->ntimestep,flag); errorx->one(FLERR,"BAD III"); } if (bodytag[body[i].ilocal] != atom->tag[body[i].ilocal] || bodyown[body[i].ilocal] != i) { printf("Proc %d, step %ld, flag %d\n",comm->me,update->ntimestep,flag); errorx->one(FLERR,"BAD JJJ"); } } for (int i = nlocal_body; i < nlocal_body + nghost_body; i++) { if (body[i].ilocal < atom->nlocal || body[i].ilocal >= atom->nlocal + atom->nghost) { printf("Proc %d, step %ld, flag %d\n",comm->me,update->ntimestep,flag); errorx->one(FLERR,"BAD KKK"); } if (bodyown[body[i].ilocal] != i) { printf("Proc %d, step %ld, flag %d\n",comm->me,update->ntimestep,flag); errorx->one(FLERR,"BAD LLL"); } } } */ diff --git a/src/SNAP/pair_snap.cpp b/src/SNAP/pair_snap.cpp index 03bee46a8..e008b0c87 100644 --- a/src/SNAP/pair_snap.cpp +++ b/src/SNAP/pair_snap.cpp @@ -1,1747 +1,1742 @@ /* ---------------------------------------------------------------------- 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 "math.h" #include "stdlib.h" #include "string.h" #include "pair_snap.h" #include "atom.h" #include "atom_vec.h" #include "force.h" #include "comm.h" #include "neighbor.h" #include "neigh_list.h" #include "neigh_request.h" #include "sna.h" #include "memory.h" #include "error.h" #include "openmp_snap.h" #include "domain.h" #include <cmath> using namespace LAMMPS_NS; #define MAXLINE 1024 #define MAXWORD 3 /* ---------------------------------------------------------------------- */ PairSNAP::PairSNAP(LAMMPS *lmp) : Pair(lmp) { single_enable = 0; restartinfo = 0; one_coeff = 1; manybody_flag = 1; nelements = 0; elements = NULL; radelem = NULL; wjelem = NULL; coeffelem = NULL; nmax = 0; nthreads = 1; schedule_user = 0; schedule_time_guided = -1; schedule_time_dynamic = -1; ncalls_neigh =-1; ilistmask_max = 0; ilistmask = NULL; ghostinum = 0; ghostilist_max = 0; ghostilist = NULL; ghostnumneigh_max = 0; ghostnumneigh = NULL; ghostneighs = NULL; ghostfirstneigh = NULL; ghostneighs_total = 0; ghostneighs_max = 0; i_max = 0; i_neighmax = 0; i_numpairs = 0; i_rij = NULL; i_inside = NULL; i_wj = NULL; i_rcutij = NULL; i_ninside = NULL; i_pairs = NULL; i_uarraytot_r = NULL; i_uarraytot_i = NULL; i_zarray_r = NULL; i_zarray_i =NULL; use_shared_arrays = 0; #ifdef TIMING_INFO timers[0] = 0; timers[1] = 0; timers[2] = 0; timers[3] = 0; #endif // Need to set this because restart not handled by PairHybrid sna = NULL; } /* ---------------------------------------------------------------------- */ PairSNAP::~PairSNAP() { if (nelements) { for (int i = 0; i < nelements; i++) delete[] elements[i]; delete[] elements; memory->destroy(radelem); memory->destroy(wjelem); memory->destroy(coeffelem); } // Need to set this because restart not handled by PairHybrid if (sna) { #ifdef TIMING_INFO double time[5]; double timeave[5]; double timeave_mpi[5]; double timemax_mpi[5]; for (int i = 0; i < 5; i++) { time[i] = 0; timeave[i] = 0; for (int tid = 0; tid<nthreads; tid++) { if (sna[tid]->timers[i]>time[i]) time[i] = sna[tid]->timers[i]; timeave[i] += sna[tid]->timers[i]; } timeave[i] /= nthreads; } MPI_Reduce(timeave, timeave_mpi, 5, MPI_DOUBLE, MPI_SUM, 0, world); MPI_Reduce(time, timemax_mpi, 5, MPI_DOUBLE, MPI_MAX, 0, world); #endif for (int tid = 0; tid<nthreads; tid++) delete sna[tid]; delete [] sna; } if (allocated) { memory->destroy(setflag); memory->destroy(cutsq); memory->destroy(map); } } void PairSNAP::compute(int eflag, int vflag) { if (use_optimized) compute_optimized(eflag, vflag); else compute_regular(eflag, vflag); } /* ---------------------------------------------------------------------- This version is a straightforward implementation ---------------------------------------------------------------------- */ void PairSNAP::compute_regular(int eflag, int vflag) { - int i,j,inum,jnum,ninside; + int i,j,jnum,ninside; double delx,dely,delz,evdwl,rsq; double fij[3]; - int *ilist,*jlist,*numneigh,**firstneigh; + int *jlist,*numneigh,**firstneigh; evdwl = 0.0; if (eflag || vflag) ev_setup(eflag,vflag); else evflag = vflag_fdotr = 0; double **x = atom->x; double **f = atom->f; int *type = atom->type; int nlocal = atom->nlocal; int newton_pair = force->newton_pair; class SNA* snaptr = sna[0]; - inum = list->inum; - ilist = list->ilist; numneigh = list->numneigh; firstneigh = list->firstneigh; for (int ii = 0; ii < list->inum; ii++) { i = list->ilist[ii]; const double xtmp = x[i][0]; const double ytmp = x[i][1]; const double ztmp = x[i][2]; const int itype = type[i]; const int ielem = map[itype]; const double radi = radelem[ielem]; jlist = firstneigh[i]; jnum = numneigh[i]; // insure rij, inside, wj, and rcutij are of size jnum snaptr->grow_rij(jnum); // rij[][3] = displacements between atom I and those neighbors // inside = indices of neighbors of I within cutoff // wj = weights for neighbors of I within cutoff // rcutij = cutoffs for neighbors of I within cutoff // note Rij sign convention => dU/dRij = dU/dRj = -dU/dRi ninside = 0; for (int jj = 0; jj < jnum; jj++) { j = jlist[jj]; j &= NEIGHMASK; delx = x[j][0] - xtmp; dely = x[j][1] - ytmp; delz = x[j][2] - ztmp; rsq = delx*delx + dely*dely + delz*delz; int jtype = type[j]; int jelem = map[jtype]; if (rsq < cutsq[itype][jtype]&&rsq>1e-20) { snaptr->rij[ninside][0] = delx; snaptr->rij[ninside][1] = dely; snaptr->rij[ninside][2] = delz; snaptr->inside[ninside] = j; snaptr->wj[ninside] = wjelem[jelem]; snaptr->rcutij[ninside] = (radi + radelem[jelem])*rcutfac; ninside++; } } // compute Ui, Zi, and Bi for atom I snaptr->compute_ui(ninside); snaptr->compute_zi(); if (!gammaoneflag) { snaptr->compute_bi(); snaptr->copy_bi2bvec(); } // for neighbors of I within cutoff: // compute dUi/drj and dBi/drj // Fij = dEi/dRj = -dEi/dRi => add to Fi, subtract from Fj double* coeffi = coeffelem[ielem]; for (int jj = 0; jj < ninside; jj++) { int j = snaptr->inside[jj]; snaptr->compute_duidrj(snaptr->rij[jj], snaptr->wj[jj],snaptr->rcutij[jj]); snaptr->compute_dbidrj(); snaptr->copy_dbi2dbvec(); fij[0] = 0.0; fij[1] = 0.0; fij[2] = 0.0; for (int k = 1; k <= ncoeff; k++) { double bgb; if (gammaoneflag) bgb = coeffi[k]; else bgb = coeffi[k]* gamma*pow(snaptr->bvec[k-1],gamma-1.0); fij[0] += bgb*snaptr->dbvec[k-1][0]; fij[1] += bgb*snaptr->dbvec[k-1][1]; fij[2] += bgb*snaptr->dbvec[k-1][2]; } f[i][0] += fij[0]; f[i][1] += fij[1]; f[i][2] += fij[2]; f[j][0] -= fij[0]; f[j][1] -= fij[1]; f[j][2] -= fij[2]; if (evflag) ev_tally_xyz(i,j,nlocal,newton_pair,0.0,0.0, fij[0],fij[1],fij[2], snaptr->rij[jj][0],snaptr->rij[jj][1], snaptr->rij[jj][2]); } if (eflag) { // evdwl = energy of atom I, sum over coeffs_k * Bi_k evdwl = coeffi[0]; if (gammaoneflag) { snaptr->compute_bi(); snaptr->copy_bi2bvec(); for (int k = 1; k <= ncoeff; k++) evdwl += coeffi[k]*snaptr->bvec[k-1]; } else for (int k = 1; k <= ncoeff; k++) evdwl += coeffi[k]*pow(snaptr->bvec[k-1],gamma); ev_tally_full(i,2.0*evdwl,0.0,0.0,delx,dely,delz); } } if (vflag_fdotr) virial_fdotr_compute(); } /* ---------------------------------------------------------------------- This version is optimized for threading, micro-load balancing ---------------------------------------------------------------------- */ void PairSNAP::compute_optimized(int eflag, int vflag) { // if reneighboring took place do load_balance if requested if (do_load_balance > 0 && (neighbor->ncalls != ncalls_neigh)) { ghostinum = 0; // reset local ghost neighbor lists ncalls_neigh = neighbor->ncalls; if (ilistmask_max < list->inum) { memory->grow(ilistmask,list->inum,"PairSnap::ilistmask"); ilistmask_max = list->inum; } for (int i = 0; i < list->inum; i++) ilistmask[i] = 1; //multiple passes for loadbalancing for (int i = 0; i < do_load_balance; i++) load_balance(); } int numpairs = 0; for (int ii = 0; ii < list->inum; ii++) { if ((do_load_balance <= 0) || ilistmask[ii]) { int i = list->ilist[ii]; int jnum = list->numneigh[i]; numpairs += jnum; } } if (do_load_balance) for (int ii = 0; ii < ghostinum; ii++) { int i = ghostilist[ii]; int jnum = ghostnumneigh[i]; numpairs += jnum; } // optimized schedule setting int time_dynamic = 0; int time_guided = 0; if (schedule_user == 0) schedule_user = 4; switch (schedule_user) { case 1: omp_set_schedule(omp_sched_static,1); break; case 2: omp_set_schedule(omp_sched_dynamic,1); break; case 3: omp_set_schedule(omp_sched_guided,2); break; case 4: omp_set_schedule(omp_sched_auto,0); break; case 5: if (numpairs < 8*nthreads) omp_set_schedule(omp_sched_dynamic,1); else if (schedule_time_guided < 0.0) { omp_set_schedule(omp_sched_guided,2); if (!eflag && !vflag) time_guided = 1; } else if (schedule_time_dynamic<0.0) { omp_set_schedule(omp_sched_dynamic,1); if (!eflag && !vflag) time_dynamic = 1; } else if (schedule_time_guided<schedule_time_dynamic) omp_set_schedule(omp_sched_guided,2); else omp_set_schedule(omp_sched_dynamic,1); break; } if (use_shared_arrays) build_per_atom_arrays(); #if defined(_OPENMP) #pragma omp parallel shared(eflag,vflag,time_dynamic,time_guided) firstprivate(numpairs) default(none) #endif { // begin of pragma omp parallel int tid = omp_get_thread_num(); int** pairs_tid_unique = NULL; int** pairs; if (use_shared_arrays) pairs = i_pairs; else { memory->create(pairs_tid_unique,numpairs,4,"numpairs"); pairs = pairs_tid_unique; } if (!use_shared_arrays) { numpairs = 0; for (int ii = 0; ii < list->inum; ii++) { if ((do_load_balance <= 0) || ilistmask[ii]) { int i = list->ilist[ii]; int jnum = list->numneigh[i]; for (int jj = 0; jj<jnum; jj++) { pairs[numpairs][0] = i; pairs[numpairs][1] = jj; pairs[numpairs][2] = -1; numpairs++; } } } for (int ii = 0; ii < ghostinum; ii++) { int i = ghostilist[ii]; int jnum = ghostnumneigh[i]; for (int jj = 0; jj<jnum; jj++) { pairs[numpairs][0] = i; pairs[numpairs][1] = jj; pairs[numpairs][2] = -1; numpairs++; } } } int ielem; - int jj,k,inum,jnum,jtype,ninside; + int jj,k,jnum,jtype,ninside; double delx,dely,delz,evdwl,rsq; double fij[3]; - int *ilist,*jlist,*numneigh,**firstneigh; + int *jlist,*numneigh,**firstneigh; evdwl = 0.0; #if defined(_OPENMP) #pragma omp master #endif { if (eflag || vflag) ev_setup(eflag,vflag); else evflag = vflag_fdotr = 0; } #if defined(_OPENMP) #pragma omp barrier { ; } #endif double **x = atom->x; double **f = atom->f; int *type = atom->type; int nlocal = atom->nlocal; int newton_pair = force->newton_pair; - inum = list->inum; - ilist = list->ilist; numneigh = list->numneigh; firstneigh = list->firstneigh; #ifdef TIMING_INFO // only update micro timers after setup static int count=0; if (count<2) { sna[tid]->timers[0] = 0; sna[tid]->timers[1] = 0; sna[tid]->timers[2] = 0; sna[tid]->timers[3] = 0; sna[tid]->timers[4] = 0; } count++; #endif // did thread start working on interactions of new atom int iold = -1; double starttime, endtime; if (time_dynamic || time_guided) starttime = MPI_Wtime(); #if defined(_OPENMP) #pragma omp for schedule(runtime) #endif for (int iijj = 0; iijj < numpairs; iijj++) { int i = 0; if (use_shared_arrays) { i = i_pairs[iijj][0]; if (iold != i) { set_sna_to_shared(tid,i_pairs[iijj][3]); ielem = map[type[i]]; } iold = i; } else { i = pairs[iijj][0]; if (iold != i) { iold = i; const double xtmp = x[i][0]; const double ytmp = x[i][1]; const double ztmp = x[i][2]; const int itype = type[i]; ielem = map[itype]; const double radi = radelem[ielem]; if (i < nlocal) { jlist = firstneigh[i]; jnum = numneigh[i]; } else { jlist = ghostneighs+ghostfirstneigh[i]; jnum = ghostnumneigh[i]; } // insure rij, inside, wj, and rcutij are of size jnum sna[tid]->grow_rij(jnum); // rij[][3] = displacements between atom I and those neighbors // inside = indices of neighbors of I within cutoff // wj = weights of neighbors of I within cutoff // rcutij = cutoffs of neighbors of I within cutoff // note Rij sign convention => dU/dRij = dU/dRj = -dU/dRi ninside = 0; for (jj = 0; jj < jnum; jj++) { int j = jlist[jj]; j &= NEIGHMASK; delx = x[j][0] - xtmp; //unitialised dely = x[j][1] - ytmp; delz = x[j][2] - ztmp; rsq = delx*delx + dely*dely + delz*delz; jtype = type[j]; int jelem = map[jtype]; if (rsq < cutsq[itype][jtype]&&rsq>1e-20) { //unitialised sna[tid]->rij[ninside][0] = delx; sna[tid]->rij[ninside][1] = dely; sna[tid]->rij[ninside][2] = delz; sna[tid]->inside[ninside] = j; sna[tid]->wj[ninside] = wjelem[jelem]; sna[tid]->rcutij[ninside] = (radi + radelem[jelem])*rcutfac; ninside++; // update index list with inside index pairs[iijj + (jj - pairs[iijj][1])][2] = ninside-1; //unitialised } } // compute Ui and Zi for atom I sna[tid]->compute_ui(ninside); //unitialised sna[tid]->compute_zi(); } } // for neighbors of I within cutoff: // compute dUi/drj and dBi/drj // Fij = dEi/dRj = -dEi/dRi => add to Fi, subtract from Fj // entry into loop if inside index is set double* coeffi = coeffelem[ielem]; if (pairs[iijj][2] >= 0) { jj = pairs[iijj][2]; int j = sna[tid]->inside[jj]; sna[tid]->compute_duidrj(sna[tid]->rij[jj], sna[tid]->wj[jj],sna[tid]->rcutij[jj]); sna[tid]->compute_dbidrj(); sna[tid]->copy_dbi2dbvec(); if (!gammaoneflag) { sna[tid]->compute_bi(); sna[tid]->copy_bi2bvec(); } fij[0] = 0.0; fij[1] = 0.0; fij[2] = 0.0; for (k = 1; k <= ncoeff; k++) { double bgb; if (gammaoneflag) bgb = coeffi[k]; else bgb = coeffi[k]* gamma*pow(sna[tid]->bvec[k-1],gamma-1.0); fij[0] += bgb*sna[tid]->dbvec[k-1][0]; fij[1] += bgb*sna[tid]->dbvec[k-1][1]; fij[2] += bgb*sna[tid]->dbvec[k-1][2]; } #if defined(_OPENMP) #pragma omp critical #endif { f[i][0] += fij[0]; f[i][1] += fij[1]; f[i][2] += fij[2]; f[j][0] -= fij[0]; f[j][1] -= fij[1]; f[j][2] -= fij[2]; if (evflag) ev_tally_xyz(i,j,nlocal,newton_pair,0.0,0.0, fij[0],fij[1],fij[2], sna[tid]->rij[jj][0],sna[tid]->rij[jj][1], sna[tid]->rij[jj][2]); } } // evdwl = energy of atom I, sum over coeffs_k * Bi_k // only call this for first pair of each atom i // if atom has no pairs, eatom=0, which is wrong if (eflag&&pairs[iijj][1] == 0) { evdwl = coeffi[0]; if (gammaoneflag) { sna[tid]->compute_bi(); sna[tid]->copy_bi2bvec(); for (int k = 1; k <= ncoeff; k++) evdwl += coeffi[k]*sna[tid]->bvec[k-1]; } else for (int k = 1; k <= ncoeff; k++) evdwl += coeffi[k]*pow(sna[tid]->bvec[k-1],gamma); #if defined(_OPENMP) #pragma omp critical #endif ev_tally_full(i,2.0*evdwl,0.0,0.0,delx,dely,delz); } } if (time_dynamic || time_guided) endtime = MPI_Wtime(); if (time_dynamic) schedule_time_dynamic = endtime - starttime; if (time_guided) schedule_time_guided = endtime - starttime; if (!use_shared_arrays) memory->destroy(pairs); }// end of pragma omp parallel if (vflag_fdotr) virial_fdotr_compute(); } inline int PairSNAP::equal(double* x,double* y) { double dist2 = (x[0]-y[0])*(x[0]-y[0]) + (x[1]-y[1])*(x[1]-y[1]) + (x[2]-y[2])*(x[2]-y[2]); if (dist2 < 1e-20) return 1; return 0; } inline double PairSNAP::dist2(double* x,double* y) { return (x[0]-y[0])*(x[0]-y[0]) + (x[1]-y[1])*(x[1]-y[1]) + (x[2]-y[2])*(x[2]-y[2]); } // return extra communication cutoff // extra_cutoff = max(subdomain_length) double PairSNAP::extra_cutoff() { double sublo[3],subhi[3]; if (domain->triclinic == 0) { for (int dim = 0 ; dim < 3 ; dim++) { sublo[dim] = domain->sublo[dim]; subhi[dim] = domain->subhi[dim]; } } else { domain->lamda2x(domain->sublo_lamda,sublo); domain->lamda2x(domain->subhi_lamda,subhi); } double sub_size[3]; for (int dim = 0; dim < 3; dim++) sub_size[dim] = subhi[dim] - sublo[dim]; double max_sub_size = 0; for (int dim = 0; dim < 3; dim++) max_sub_size = MAX(max_sub_size,sub_size[dim]); // note: for triclinic, probably need something different // see Comm::setup() return max_sub_size; } // micro load_balancer: each MPI process will // check with each of its 26 neighbors, // whether an imbalance exists in the number // of atoms to calculate forces for. // If it does it will set ilistmask of one of // its local atoms to zero, and send its Tag // to the neighbor process. The neighboring process // will check its ghost list for the // ghost atom with the same Tag which is closest // to its domain center, and build a // neighborlist for this ghost atom. For this to work, // the communication cutoff has to be // as large as the neighbor cutoff + // maximum subdomain length. // Note that at most one atom is exchanged per processor pair. // Also note that the local atom assignement // doesn't change. This load balancer will cause // some ghost atoms to have full neighborlists // which are unique to PairSNAP. // They are not part of the generally accessible neighborlist. // At the same time corresponding local atoms on // other MPI processes will not be // included in the force computation since // their ilistmask is 0. This does not effect // any other classes which might // access the same general neighborlist. // Reverse communication (newton on) of forces is required. // Currently the load balancer does two passes, // since its exchanging atoms upstream and downstream. void PairSNAP::load_balance() { double sublo[3],subhi[3]; if (domain->triclinic == 0) { double* sublotmp = domain->sublo; double* subhitmp = domain->subhi; for (int dim = 0 ; dim<3 ; dim++) { sublo[dim]=sublotmp[dim]; subhi[dim]=subhitmp[dim]; } } else { double* sublotmp = domain->sublo_lamda; double* subhitmp = domain->subhi_lamda; domain->lamda2x(sublotmp,sublo); domain->lamda2x(subhitmp,subhi); } //if (list->inum==0) list->grow(atom->nmax); int nlocal = ghostinum; for (int i=0; i < list->inum; i++) if (ilistmask[i]) nlocal++; int ***grid2proc = comm->grid2proc; int* procgrid = comm->procgrid; int nlocal_up,nlocal_down; MPI_Status status; MPI_Request request; double sub_mid[3]; for (int dim=0; dim<3; dim++) sub_mid[dim] = (subhi[dim] + sublo[dim])/2; if (comm->cutghostuser < neighbor->cutneighmax+extra_cutoff()) error->all(FLERR,"Communication cutoff is too small " "for SNAP micro load balancing.\n" "Typically this can happen, if you change " "the neighbor skin after your pair_style " "command or if your box dimensions grow " "during the run.\n" "You need to set it via " "'communicate single cutoff NUMBER' " "to the needed length."); int nrecv = ghostinum; int totalsend = 0; int nsend = 0; int depth = 1; for (int dx = -depth; dx < depth+1; dx++) for (int dy = -depth; dy < depth+1; dy++) for (int dz = -depth; dz < depth+1; dz++) { if (dx == dy && dy == dz && dz == 0) continue; int sendloc[3] = {comm->myloc[0], comm->myloc[1], comm->myloc[2] }; sendloc[0] += dx; sendloc[1] += dy; sendloc[2] += dz; for (int dim = 0; dim < 3; dim++) if (sendloc[dim] >= procgrid[dim]) sendloc[dim] = sendloc[dim] - procgrid[dim]; for (int dim = 0; dim < 3; dim++) if (sendloc[dim] < 0) sendloc[dim] = procgrid[dim] + sendloc[dim]; int recvloc[3] = {comm->myloc[0], comm->myloc[1], comm->myloc[2] }; recvloc[0] -= dx; recvloc[1] -= dy; recvloc[2] -= dz; for (int dim = 0; dim < 3; dim++) if (recvloc[dim] < 0) recvloc[dim] = procgrid[dim] + recvloc[dim]; for (int dim = 0; dim < 3; dim++) if (recvloc[dim] >= procgrid[dim]) recvloc[dim] = recvloc[dim] - procgrid[dim]; int sendproc = grid2proc[sendloc[0]][sendloc[1]][sendloc[2]]; int recvproc = grid2proc[recvloc[0]][recvloc[1]][recvloc[2]]; // two stage process, first upstream movement, then downstream MPI_Sendrecv(&nlocal,1,MPI_INT,sendproc,0, &nlocal_up,1,MPI_INT,recvproc,0,world,&status); MPI_Sendrecv(&nlocal,1,MPI_INT,recvproc,0, &nlocal_down,1,MPI_INT,sendproc,0,world,&status); nsend = 0; // send upstream if (nlocal > nlocal_up+1) { int i = totalsend++; while(i < list->inum && ilistmask[i] == 0) i = totalsend++; if (i < list->inum) MPI_Isend(&atom->tag[i],1,MPI_INT,recvproc,0,world,&request); else { int j = -1; MPI_Isend(&j,1,MPI_INT,recvproc,0,world,&request); } if (i < list->inum) { for (int j = 0; j < list->inum; j++) if (list->ilist[j] == i) ilistmask[j] = 0; nsend = 1; } } // recv downstream if (nlocal < nlocal_down-1) { nlocal++; int get_tag = -1; MPI_Recv(&get_tag,1,MPI_INT,sendproc,0,world,&status); // if get_tag -1 the other process didnt have local atoms to send if (get_tag >= 0) { if (ghostinum >= ghostilist_max) { memory->grow(ghostilist,ghostinum+10, "PairSnap::ghostilist"); ghostilist_max = ghostinum+10; } if (atom->nlocal + atom->nghost >= ghostnumneigh_max) { ghostnumneigh_max = atom->nlocal+atom->nghost+100; memory->grow(ghostnumneigh,ghostnumneigh_max, "PairSnap::ghostnumneigh"); memory->grow(ghostfirstneigh,ghostnumneigh_max, "PairSnap::ghostfirstneigh"); } // find closest ghost image of the transfered particle double mindist = 1e200; int closestghost = -1; for (int j = 0; j < atom->nlocal + atom->nghost; j++) if (atom->tag[j] == get_tag) if (dist2(sub_mid, atom->x[j]) < mindist) { closestghost = j; mindist = dist2(sub_mid, atom->x[j]); } // build neighborlist for this particular // ghost atom, and add it to list->ilist if (ghostneighs_max - ghostneighs_total < neighbor->oneatom) { memory->grow(ghostneighs, ghostneighs_total + neighbor->oneatom, "PairSnap::ghostneighs"); ghostneighs_max = ghostneighs_total + neighbor->oneatom; } int j = closestghost; ghostilist[ghostinum] = j; ghostnumneigh[j] = 0; ghostfirstneigh[j] = ghostneighs_total; ghostinum++; int* jlist = ghostneighs + ghostfirstneigh[j]; // find all neighbors by looping // over all local and ghost atoms for (int k = 0; k < atom->nlocal + atom->nghost; k++) if (dist2(atom->x[j],atom->x[k]) < neighbor->cutneighmax*neighbor->cutneighmax) { jlist[ghostnumneigh[j]] = k; ghostnumneigh[j]++; ghostneighs_total++; } } if (get_tag >= 0) nrecv++; } // decrease nlocal later, so that it is the // initial number both for receiving and sending if (nsend) nlocal--; // second pass through the grid MPI_Sendrecv(&nlocal,1,MPI_INT,sendproc,0, &nlocal_up,1,MPI_INT,recvproc,0,world,&status); MPI_Sendrecv(&nlocal,1,MPI_INT,recvproc,0, &nlocal_down,1,MPI_INT,sendproc,0,world,&status); // send downstream nsend=0; if (nlocal > nlocal_down+1) { int i = totalsend++; while(i < list->inum && ilistmask[i]==0) i = totalsend++; if (i < list->inum) MPI_Isend(&atom->tag[i],1,MPI_INT,sendproc,0,world,&request); else { int j =- 1; MPI_Isend(&j,1,MPI_INT,sendproc,0,world,&request); } if (i < list->inum) { for (int j=0; j<list->inum; j++) if (list->ilist[j] == i) ilistmask[j] = 0; nsend = 1; } } // receive upstream if (nlocal < nlocal_up-1) { nlocal++; int get_tag = -1; MPI_Recv(&get_tag,1,MPI_INT,recvproc,0,world,&status); if (get_tag >= 0) { if (ghostinum >= ghostilist_max) { memory->grow(ghostilist,ghostinum+10, "PairSnap::ghostilist"); ghostilist_max = ghostinum+10; } if (atom->nlocal + atom->nghost >= ghostnumneigh_max) { ghostnumneigh_max = atom->nlocal + atom->nghost + 100; memory->grow(ghostnumneigh,ghostnumneigh_max, "PairSnap::ghostnumneigh"); memory->grow(ghostfirstneigh,ghostnumneigh_max, "PairSnap::ghostfirstneigh"); } // find closest ghost image of the transfered particle double mindist = 1e200; int closestghost = -1; for (int j = 0; j < atom->nlocal + atom->nghost; j++) if (atom->tag[j] == get_tag) if (dist2(sub_mid,atom->x[j])<mindist) { closestghost = j; mindist = dist2(sub_mid,atom->x[j]); } // build neighborlist for this particular ghost atom if (ghostneighs_max-ghostneighs_total < neighbor->oneatom) { memory->grow(ghostneighs,ghostneighs_total + neighbor->oneatom, "PairSnap::ghostneighs"); ghostneighs_max = ghostneighs_total + neighbor->oneatom; } int j = closestghost; ghostilist[ghostinum] = j; ghostnumneigh[j] = 0; ghostfirstneigh[j] = ghostneighs_total; ghostinum++; int* jlist = ghostneighs + ghostfirstneigh[j]; for (int k = 0; k < atom->nlocal + atom->nghost; k++) if (dist2(atom->x[j],atom->x[k]) < neighbor->cutneighmax*neighbor->cutneighmax) { jlist[ghostnumneigh[j]] = k; ghostnumneigh[j]++; ghostneighs_total++; } } if (get_tag >= 0) nrecv++; } if (nsend) nlocal--; } } void PairSNAP::set_sna_to_shared(int snaid,int i) { sna[snaid]->rij = i_rij[i]; sna[snaid]->inside = i_inside[i]; sna[snaid]->wj = i_wj[i]; sna[snaid]->rcutij = i_rcutij[i]; sna[snaid]->zarray_r = i_zarray_r[i]; sna[snaid]->zarray_i = i_zarray_i[i]; sna[snaid]->uarraytot_r = i_uarraytot_r[i]; sna[snaid]->uarraytot_i = i_uarraytot_i[i]; } void PairSNAP::build_per_atom_arrays() { #ifdef TIMING_INFO clock_gettime(CLOCK_REALTIME,&starttime); #endif - int i_list[list->inum+ghostinum]; int count = 0; int neighmax = 0; for (int ii = 0; ii < list->inum; ii++) if ((do_load_balance <= 0) || ilistmask[ii]) { neighmax=MAX(neighmax,list->numneigh[list->ilist[ii]]); - i_list[count++]=list->ilist[ii]; + ++count; } for (int ii = 0; ii < ghostinum; ii++) { neighmax=MAX(neighmax,ghostnumneigh[ghostilist[ii]]); - i_list[count++]=ghostilist[ii]; + ++count; } if (i_max < count || i_neighmax < neighmax) { int i_maxt = MAX(count,i_max); i_neighmax = MAX(neighmax,i_neighmax); memory->destroy(i_rij); memory->destroy(i_inside); memory->destroy(i_wj); memory->destroy(i_rcutij); memory->destroy(i_ninside); memory->destroy(i_pairs); memory->create(i_rij,i_maxt,i_neighmax,3,"PairSNAP::i_rij"); memory->create(i_inside,i_maxt,i_neighmax,"PairSNAP::i_inside"); memory->create(i_wj,i_maxt,i_neighmax,"PairSNAP::i_wj"); memory->create(i_rcutij,i_maxt,i_neighmax,"PairSNAP::i_rcutij"); memory->create(i_ninside,i_maxt,"PairSNAP::i_ninside"); memory->create(i_pairs,i_maxt*i_neighmax,4,"PairSNAP::i_pairs"); } if (i_max < count) { int jdim = sna[0]->twojmax+1; memory->destroy(i_uarraytot_r); memory->destroy(i_uarraytot_i); memory->create(i_uarraytot_r,count,jdim,jdim,jdim, "PairSNAP::i_uarraytot_r"); memory->create(i_uarraytot_i,count,jdim,jdim,jdim, "PairSNAP::i_uarraytot_i"); if (i_zarray_r != NULL) for (int i = 0; i < i_max; i++) { memory->destroy(i_zarray_r[i]); memory->destroy(i_zarray_i[i]); } delete [] i_zarray_r; delete [] i_zarray_i; i_zarray_r = new double*****[count]; i_zarray_i = new double*****[count]; for (int i = 0; i < count; i++) { memory->create(i_zarray_r[i],jdim,jdim,jdim,jdim,jdim, "PairSNAP::i_zarray_r"); memory->create(i_zarray_i[i],jdim,jdim,jdim,jdim,jdim, "PairSNAP::i_zarray_i"); } } if (i_max < count) i_max = count; count = 0; i_numpairs = 0; for (int ii = 0; ii < list->inum; ii++) { if ((do_load_balance <= 0) || ilistmask[ii]) { int i = list->ilist[ii]; int jnum = list->numneigh[i]; int* jlist = list->firstneigh[i]; const double xtmp = atom->x[i][0]; const double ytmp = atom->x[i][1]; const double ztmp = atom->x[i][2]; const int itype = atom->type[i]; const int ielem = map[itype]; const double radi = radelem[ielem]; int ninside = 0; for (int jj = 0; jj < jnum; jj++) { int j = jlist[jj]; j &= NEIGHMASK; const double delx = atom->x[j][0] - xtmp; const double dely = atom->x[j][1] - ytmp; const double delz = atom->x[j][2] - ztmp; const double rsq = delx*delx + dely*dely + delz*delz; int jtype = atom->type[j]; int jelem = map[jtype]; i_pairs[i_numpairs][0] = i; i_pairs[i_numpairs][1] = jj; i_pairs[i_numpairs][2] = -1; i_pairs[i_numpairs][3] = count; if (rsq < cutsq[itype][jtype]&&rsq>1e-20) { i_rij[count][ninside][0] = delx; i_rij[count][ninside][1] = dely; i_rij[count][ninside][2] = delz; i_inside[count][ninside] = j; i_wj[count][ninside] = wjelem[jelem]; i_rcutij[count][ninside] = (radi + radelem[jelem])*rcutfac; // update index list with inside index i_pairs[i_numpairs][2] = ninside++; } i_numpairs++; } i_ninside[count] = ninside; count++; } } for (int ii = 0; ii < ghostinum; ii++) { int i = ghostilist[ii]; int jnum = ghostnumneigh[i]; int* jlist = ghostneighs+ghostfirstneigh[i]; const double xtmp = atom->x[i][0]; const double ytmp = atom->x[i][1]; const double ztmp = atom->x[i][2]; const int itype = atom->type[i]; const int ielem = map[itype]; const double radi = radelem[ielem]; int ninside = 0; for (int jj = 0; jj < jnum; jj++) { int j = jlist[jj]; j &= NEIGHMASK; const double delx = atom->x[j][0] - xtmp; const double dely = atom->x[j][1] - ytmp; const double delz = atom->x[j][2] - ztmp; const double rsq = delx*delx + dely*dely + delz*delz; int jtype = atom->type[j]; int jelem = map[jtype]; i_pairs[i_numpairs][0] = i; i_pairs[i_numpairs][1] = jj; i_pairs[i_numpairs][2] = -1; i_pairs[i_numpairs][3] = count; if (rsq < cutsq[itype][jtype]&&rsq>1e-20) { i_rij[count][ninside][0] = delx; i_rij[count][ninside][1] = dely; i_rij[count][ninside][2] = delz; i_inside[count][ninside] = j; i_wj[count][ninside] = wjelem[jelem]; i_rcutij[count][ninside] = (radi + radelem[jelem])*rcutfac; // update index list with inside index i_pairs[i_numpairs][2] = ninside++; } i_numpairs++; } i_ninside[count] = ninside; count++; } #ifdef TIMING_INFO clock_gettime(CLOCK_REALTIME,&endtime); timers[0]+=(endtime.tv_sec-starttime.tv_sec+1.0* (endtime.tv_nsec-starttime.tv_nsec)/1000000000); #endif #ifdef TIMING_INFO clock_gettime(CLOCK_REALTIME,&starttime); #endif #if defined(_OPENMP) #pragma omp parallel for shared(count) default(none) #endif for (int ii=0; ii < count; ii++) { int tid = omp_get_thread_num(); set_sna_to_shared(tid,ii); //sna[tid]->compute_ui(i_ninside[ii]); #ifdef TIMING_INFO clock_gettime(CLOCK_REALTIME,&starttime); #endif sna[tid]->compute_ui_omp(i_ninside[ii],MAX(int(nthreads/count),1)); #ifdef TIMING_INFO clock_gettime(CLOCK_REALTIME,&endtime); sna[tid]->timers[0]+=(endtime.tv_sec-starttime.tv_sec+1.0* (endtime.tv_nsec-starttime.tv_nsec)/1000000000); #endif } #ifdef TIMING_INFO clock_gettime(CLOCK_REALTIME,&starttime); #endif for (int ii=0; ii < count; ii++) { int tid = 0;//omp_get_thread_num(); set_sna_to_shared(tid,ii); sna[tid]->compute_zi_omp(MAX(int(nthreads/count),1)); } #ifdef TIMING_INFO clock_gettime(CLOCK_REALTIME,&endtime); sna[0]->timers[1]+=(endtime.tv_sec-starttime.tv_sec+1.0* (endtime.tv_nsec-starttime.tv_nsec)/1000000000); #endif #ifdef TIMING_INFO clock_gettime(CLOCK_REALTIME,&endtime); timers[1]+=(endtime.tv_sec-starttime.tv_sec+1.0* (endtime.tv_nsec-starttime.tv_nsec)/1000000000); #endif } /* ---------------------------------------------------------------------- allocate all arrays ------------------------------------------------------------------------- */ void PairSNAP::allocate() { allocated = 1; int n = atom->ntypes; memory->create(setflag,n+1,n+1,"pair:setflag"); memory->create(cutsq,n+1,n+1,"pair:cutsq"); memory->create(map,n+1,"pair:map"); } /* ---------------------------------------------------------------------- global settings ------------------------------------------------------------------------- */ void PairSNAP::settings(int narg, char **arg) { // set default values for optional arguments nthreads = -1; use_shared_arrays=-1; do_load_balance = 0; use_optimized = 1; // optional arguments for (int i=0; i < narg; i++) { if (i+2>narg) error->all(FLERR,"Illegal pair_style command." " Too few arguments."); if (strcmp(arg[i],"nthreads")==0) { nthreads=force->inumeric(FLERR,arg[++i]); #if defined(LMP_USER_OMP) error->all(FLERR,"Please set number of threads via package omp command"); #else omp_set_num_threads(nthreads); comm->nthreads=nthreads; #endif continue; } if (strcmp(arg[i],"optimized")==0) { use_optimized=force->inumeric(FLERR,arg[++i]); continue; } if (strcmp(arg[i],"shared")==0) { use_shared_arrays=force->inumeric(FLERR,arg[++i]); continue; } if (strcmp(arg[i],"loadbalance")==0) { do_load_balance = force->inumeric(FLERR,arg[++i]); if (do_load_balance) { double mincutoff = extra_cutoff() + rcutmax + neighbor->skin; if (comm->cutghostuser < mincutoff) { char buffer[255]; //apparently mincutoff is 0 after sprintf command ????? double tmp = mincutoff + 0.1; sprintf(buffer, "Communication cutoff is too small " "for SNAP micro load balancing. " "It will be increased to: %lf.",mincutoff+0.1); if (comm->me==0) error->warning(FLERR,buffer); comm->cutghostuser = tmp; } } continue; } if (strcmp(arg[i],"schedule")==0) { i++; if (strcmp(arg[i],"static")==0) schedule_user = 1; if (strcmp(arg[i],"dynamic")==0) schedule_user = 2; if (strcmp(arg[i],"guided")==0) schedule_user = 3; if (strcmp(arg[i],"auto")==0) schedule_user = 4; if (strcmp(arg[i],"determine")==0) schedule_user = 5; if (schedule_user == 0) error->all(FLERR,"Illegal pair_style command." " Illegal schedule argument."); continue; } char buffer[255]; sprintf(buffer, "Illegal pair_style command." " Unrecognized argument: %s.\n",arg[i]); error->all(FLERR,buffer); } if (nthreads < 0) nthreads = comm->nthreads; if (use_shared_arrays < 0) { if (nthreads > 1 && atom->nlocal <= 2*nthreads) use_shared_arrays = 1; else use_shared_arrays = 0; } // check if running non-optimized code with // optimization flags set if (!use_optimized) if (nthreads > 1 || use_shared_arrays || do_load_balance || schedule_user) error->all(FLERR,"Illegal pair_style command." "Advanced options require setting 'optimized 1'."); } /* ---------------------------------------------------------------------- set coeffs for one or more type pairs ------------------------------------------------------------------------- */ void PairSNAP::coeff(int narg, char **arg) { // read SNAP element names between 2 filenames // nelements = # of SNAP elements // elements = list of unique element names if (narg < 6) error->all(FLERR,"Incorrect args for pair coefficients"); if (!allocated) allocate(); if (nelements) { for (int i = 0; i < nelements; i++) delete[] elements[i]; delete[] elements; memory->destroy(radelem); memory->destroy(wjelem); memory->destroy(coeffelem); } nelements = narg - 4 - atom->ntypes; if (nelements < 1) error->all(FLERR,"Incorrect args for pair coefficients"); char* type1 = arg[0]; char* type2 = arg[1]; char* coefffilename = arg[2]; char** elemlist = &arg[3]; char* paramfilename = arg[3+nelements]; char** elemtypes = &arg[4+nelements]; // insure I,J args are * * if (strcmp(type1,"*") != 0 || strcmp(type2,"*") != 0) error->all(FLERR,"Incorrect args for pair coefficients"); elements = new char*[nelements]; for (int i = 0; i < nelements; i++) { char* elemname = elemlist[i]; int n = strlen(elemname) + 1; elements[i] = new char[n]; strcpy(elements[i],elemname); } // read snapcoeff and snapparam files read_files(coefffilename,paramfilename); // read args that map atom types to SNAP elements // map[i] = which element the Ith atom type is, -1 if not mapped // map[0] is not used for (int i = 1; i <= atom->ntypes; i++) { char* elemname = elemtypes[i-1]; int jelem; for (jelem = 0; jelem < nelements; jelem++) if (strcmp(elemname,elements[jelem]) == 0) break; if (jelem < nelements) map[i] = jelem; else if (strcmp(elemname,"NULL") == 0) map[i] = -1; else error->all(FLERR,"Incorrect args for pair coefficients"); } // clear setflag since coeff() called once with I,J = * * int n = atom->ntypes; for (int i = 1; i <= n; i++) for (int j = i; j <= n; j++) setflag[i][j] = 0; // set setflag i,j for type pairs where both are mapped to elements int count = 0; for (int i = 1; i <= n; i++) for (int j = i; j <= n; j++) if (map[i] >= 0 && map[j] >= 0) { setflag[i][j] = 1; count++; } if (count == 0) error->all(FLERR,"Incorrect args for pair coefficients"); sna = new SNA*[nthreads]; // allocate memory for per OpenMP thread data which // is wrapped into the sna class #if defined(_OPENMP) #pragma omp parallel default(none) #endif { int tid = omp_get_thread_num(); sna[tid] = new SNA(lmp,rfac0,twojmax, diagonalstyle,use_shared_arrays, rmin0,switchflag); if (!use_shared_arrays) sna[tid]->grow_rij(nmax); } if (ncoeff != sna[0]->ncoeff) { printf("ncoeff = %d snancoeff = %d \n",ncoeff,sna[0]->ncoeff); error->all(FLERR,"Incorrect SNAP parameter file"); } // Calculate maximum cutoff for all elements rcutmax = 0.0; for (int ielem = 0; ielem < nelements; ielem++) rcutmax = MAX(2.0*radelem[ielem]*rcutfac,rcutmax); } /* ---------------------------------------------------------------------- init specific to this pair style ------------------------------------------------------------------------- */ void PairSNAP::init_style() { if (force->newton_pair == 0) error->all(FLERR,"Pair style SNAP requires newton pair on"); // need a full neighbor list int irequest = neighbor->request(this); neighbor->requests[irequest]->half = 0; neighbor->requests[irequest]->full = 1; #if defined(_OPENMP) #pragma omp parallel default(none) #endif { int tid = omp_get_thread_num(); sna[tid]->init(); } } /* ---------------------------------------------------------------------- init for one type pair i,j and corresponding j,i ------------------------------------------------------------------------- */ double PairSNAP::init_one(int i, int j) { if (setflag[i][j] == 0) error->all(FLERR,"All pair coeffs are not set"); return (radelem[map[i]] + radelem[map[j]])*rcutfac; } /* ---------------------------------------------------------------------- */ void PairSNAP::read_files(char *coefffilename, char *paramfilename) { // open SNAP ceofficient file on proc 0 FILE *fpcoeff; if (comm->me == 0) { fpcoeff = force->open_potential(coefffilename); if (fpcoeff == NULL) { char str[128]; sprintf(str,"Cannot open SNAP coefficient file %s",coefffilename); error->one(FLERR,str); } } char line[MAXLINE],*ptr; int eof = 0; int n; int nwords = 0; while (nwords == 0) { if (comm->me == 0) { ptr = fgets(line,MAXLINE,fpcoeff); if (ptr == NULL) { eof = 1; fclose(fpcoeff); } else n = strlen(line) + 1; } MPI_Bcast(&eof,1,MPI_INT,0,world); if (eof) break; MPI_Bcast(&n,1,MPI_INT,0,world); MPI_Bcast(line,n,MPI_CHAR,0,world); // strip comment, skip line if blank if ((ptr = strchr(line,'#'))) *ptr = '\0'; nwords = atom->count_words(line); } if (nwords != 2) error->all(FLERR,"Incorrect format in SNAP coefficient file"); // words = ptrs to all words in line // strip single and double quotes from words char* words[MAXWORD]; int iword = 0; words[iword] = strtok(line,"' \t\n\r\f"); iword = 1; words[iword] = strtok(NULL,"' \t\n\r\f"); int nelemfile = atoi(words[0]); ncoeff = atoi(words[1])-1; // Set up element lists memory->create(radelem,nelements,"pair:radelem"); memory->create(wjelem,nelements,"pair:wjelem"); memory->create(coeffelem,nelements,ncoeff+1,"pair:coeffelem"); int *found = new int[nelements]; for (int ielem = 0; ielem < nelements; ielem++) found[ielem] = 0; // Loop over elements in the SNAP coefficient file for (int ielemfile = 0; ielemfile < nelemfile; ielemfile++) { if (comm->me == 0) { ptr = fgets(line,MAXLINE,fpcoeff); if (ptr == NULL) { eof = 1; fclose(fpcoeff); } else n = strlen(line) + 1; } MPI_Bcast(&eof,1,MPI_INT,0,world); if (eof) error->all(FLERR,"Incorrect format in SNAP coefficient file"); MPI_Bcast(&n,1,MPI_INT,0,world); MPI_Bcast(line,n,MPI_CHAR,0,world); nwords = atom->count_words(line); if (nwords != 3) error->all(FLERR,"Incorrect format in SNAP coefficient file"); iword = 0; words[iword] = strtok(line,"' \t\n\r\f"); iword = 1; words[iword] = strtok(NULL,"' \t\n\r\f"); iword = 2; words[iword] = strtok(NULL,"' \t\n\r\f"); char* elemtmp = words[0]; double radtmp = atof(words[1]); double wjtmp = atof(words[2]); // skip if element name isn't in element list int ielem; for (ielem = 0; ielem < nelements; ielem++) if (strcmp(elemtmp,elements[ielem]) == 0) break; if (ielem == nelements) { if (comm->me == 0) for (int icoeff = 0; icoeff <= ncoeff; icoeff++) ptr = fgets(line,MAXLINE,fpcoeff); continue; } // skip if element already appeared if (found[ielem]) { if (comm->me == 0) for (int icoeff = 0; icoeff <= ncoeff; icoeff++) ptr = fgets(line,MAXLINE,fpcoeff); continue; } found[ielem] = 1; radelem[ielem] = radtmp; wjelem[ielem] = wjtmp; if (comm->me == 0) { if (screen) fprintf(screen,"SNAP Element = %s, Radius %g, Weight %g \n", elements[ielem], radelem[ielem], wjelem[ielem]); if (logfile) fprintf(logfile,"SNAP Element = %s, Radius %g, Weight %g \n", elements[ielem], radelem[ielem], wjelem[ielem]); } for (int icoeff = 0; icoeff <= ncoeff; icoeff++) { if (comm->me == 0) { ptr = fgets(line,MAXLINE,fpcoeff); if (ptr == NULL) { eof = 1; fclose(fpcoeff); } else n = strlen(line) + 1; } MPI_Bcast(&eof,1,MPI_INT,0,world); if (eof) error->all(FLERR,"Incorrect format in SNAP coefficient file"); MPI_Bcast(&n,1,MPI_INT,0,world); MPI_Bcast(line,n,MPI_CHAR,0,world); nwords = atom->count_words(line); if (nwords != 1) error->all(FLERR,"Incorrect format in SNAP coefficient file"); iword = 0; words[iword] = strtok(line,"' \t\n\r\f"); coeffelem[ielem][icoeff] = atof(words[0]); } } // set flags for required keywords rcutfacflag = 0; twojmaxflag = 0; // Set defaults for optional keywords gamma = 1.0; gammaoneflag = 1; rfac0 = 0.99363; rmin0 = 0.0; diagonalstyle = 3; switchflag = 1; // open SNAP parameter file on proc 0 FILE *fpparam; if (comm->me == 0) { fpparam = force->open_potential(paramfilename); if (fpparam == NULL) { char str[128]; sprintf(str,"Cannot open SNAP parameter file %s",paramfilename); error->one(FLERR,str); } } eof = 0; while (1) { if (comm->me == 0) { ptr = fgets(line,MAXLINE,fpparam); if (ptr == NULL) { eof = 1; fclose(fpparam); } else n = strlen(line) + 1; } MPI_Bcast(&eof,1,MPI_INT,0,world); if (eof) break; MPI_Bcast(&n,1,MPI_INT,0,world); MPI_Bcast(line,n,MPI_CHAR,0,world); // strip comment, skip line if blank if ((ptr = strchr(line,'#'))) *ptr = '\0'; nwords = atom->count_words(line); if (nwords == 0) continue; if (nwords != 2) error->all(FLERR,"Incorrect format in SNAP parameter file"); // words = ptrs to all words in line // strip single and double quotes from words char* keywd = strtok(line,"' \t\n\r\f"); char* keyval = strtok(NULL,"' \t\n\r\f"); if (comm->me == 0) { if (screen) fprintf(screen,"SNAP keyword %s %s \n",keywd,keyval); if (logfile) fprintf(logfile,"SNAP keyword %s %s \n",keywd,keyval); } if (strcmp(keywd,"rcutfac") == 0) { rcutfac = atof(keyval); rcutfacflag = 1; } else if (strcmp(keywd,"twojmax") == 0) { twojmax = atoi(keyval); twojmaxflag = 1; } else if (strcmp(keywd,"gamma") == 0) gamma = atof(keyval); else if (strcmp(keywd,"rfac0") == 0) rfac0 = atof(keyval); else if (strcmp(keywd,"rmin0") == 0) rmin0 = atof(keyval); else if (strcmp(keywd,"diagonalstyle") == 0) diagonalstyle = atoi(keyval); else if (strcmp(keywd,"switchflag") == 0) switchflag = atoi(keyval); else error->all(FLERR,"Incorrect SNAP parameter file"); } if (rcutfacflag == 0 || twojmaxflag == 0) error->all(FLERR,"Incorrect SNAP parameter file"); if (gamma == 1.0) gammaoneflag = 1; else gammaoneflag = 0; } /* ---------------------------------------------------------------------- memory usage ------------------------------------------------------------------------- */ double PairSNAP::memory_usage() { double bytes = Pair::memory_usage(); int n = atom->ntypes+1; bytes += n*n*sizeof(int); bytes += n*n*sizeof(double); bytes += 3*nmax*sizeof(double); bytes += nmax*sizeof(int); bytes += (2*ncoeff+1)*sizeof(double); bytes += (ncoeff*3)*sizeof(double); bytes += sna[0]->memory_usage()*nthreads; return bytes; } diff --git a/src/citeme.cpp b/src/citeme.cpp index 70b02e83b..66c6eb9dc 100644 --- a/src/citeme.cpp +++ b/src/citeme.cpp @@ -1,75 +1,77 @@ /* ---------------------------------------------------------------------- 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 "citeme.h" #include "version.h" #include "universe.h" #include "error.h" using namespace LAMMPS_NS; static const char cite_header[] = "This LAMMPS simulation made specific use of work described in the\n" "following references. See http://lammps.sandia.gov/cite.html\n" "for details.\n\n"; static const char cite_nagline[] = "\nPlease see the log.cite file " "for references relevant to this simulation\n\n"; /* ---------------------------------------------------------------------- */ CiteMe::CiteMe(LAMMPS *lmp) : Pointers(lmp) { fp = NULL; cs = new citeset(); } /* ---------------------------------------------------------------------- write out nag-line at the end of the regular output and clean up ------------------------------------------------------------------------- */ CiteMe::~CiteMe() { if (universe->me || cs->size() == 0) { delete cs; return; } delete cs; - if (screen) fprintf(screen,cite_nagline); - if (logfile) fprintf(logfile,cite_nagline); + if (fp) { + if (screen) fprintf(screen,cite_nagline); + if (logfile) fprintf(logfile,cite_nagline); - if (fp) fclose(fp); + fclose(fp); + } } /* ---------------------------------------------------------------------- write out and register a citation so it will be written only once ------------------------------------------------------------------------- */ void CiteMe::add(const char *ref) { if (universe->me) return; if (cs->find(ref) != cs->end()) return; cs->insert(ref); if (!fp) { fp = fopen("log.cite","w"); - if (!fp) error->universe_one(FLERR,"Cannot open log.cite file"); + if (!fp) return; fputs(cite_header,fp); fflush(fp); } fputs(ref,fp); fflush(fp); } diff --git a/src/fix_recenter.cpp b/src/fix_recenter.cpp index 2d8ee41f9..5d2e2d5e8 100644 --- a/src/fix_recenter.cpp +++ b/src/fix_recenter.cpp @@ -1,216 +1,232 @@ /* ---------------------------------------------------------------------- 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: Naveen Michaud-Agrawal (Johns Hopkins U) ------------------------------------------------------------------------- */ #include "stdlib.h" #include "string.h" #include "fix_recenter.h" #include "atom.h" #include "group.h" +#include "update.h" #include "domain.h" #include "lattice.h" #include "modify.h" #include "comm.h" +#include "respa.h" #include "error.h" #include "force.h" using namespace LAMMPS_NS; using namespace FixConst; enum{BOX,LATTICE,FRACTION}; /* ---------------------------------------------------------------------- */ FixRecenter::FixRecenter(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { if (narg < 6) error->all(FLERR,"Illegal fix recenter command"); xcom = ycom = zcom = 0.0; xflag = yflag = zflag = 1; xinitflag = yinitflag = zinitflag = 0; shift[0] = shift[1] = shift[2] = 0.0; distance = 0.0; scalar_flag = 1; vector_flag = 1; size_vector = 3; extscalar = 1; extvector = 1; global_freq = 1; /* ---------------------------------------------------------------------- */ if (strcmp(arg[3],"NULL") == 0) xflag = 0; else if (strcmp(arg[3],"INIT") == 0) xinitflag = 1; else xcom = force->numeric(FLERR,arg[3]); if (strcmp(arg[4],"NULL") == 0) yflag = 0; else if (strcmp(arg[4],"INIT") == 0) yinitflag = 1; else ycom = force->numeric(FLERR,arg[4]); if (strcmp(arg[5],"NULL") == 0) zflag = 0; else if (strcmp(arg[5],"INIT") == 0) zinitflag = 1; else zcom = force->numeric(FLERR,arg[5]); // optional args group2bit = groupbit; scaleflag = LATTICE; int iarg = 6; while (iarg < narg) { if (strcmp(arg[iarg],"shift") == 0) { int igroup2 = group->find(arg[iarg+1]); if (igroup2 < 0) error->all(FLERR,"Could not find fix recenter group ID"); group2bit = group->bitmask[igroup2]; iarg += 2; } else if (strcmp(arg[iarg],"units") == 0) { if (strcmp(arg[iarg+1],"box") == 0) scaleflag = BOX; else if (strcmp(arg[iarg+1],"lattice") == 0) scaleflag = LATTICE; else if (strcmp(arg[iarg+1],"fraction") == 0) scaleflag = FRACTION; else error->all(FLERR,"Illegal fix recenter command"); iarg += 2; } else error->all(FLERR,"Illegal fix recenter command"); } // scale xcom,ycom,zcom double xscale,yscale,zscale; if (scaleflag == LATTICE) { xscale = domain->lattice->xlattice; yscale = domain->lattice->ylattice; zscale = domain->lattice->zlattice; } else xscale = yscale = zscale = 1.0; xcom *= xscale; ycom *= yscale; zcom *= zscale; // cannot have 0 atoms in group if (group->count(igroup) == 0) error->all(FLERR,"Fix recenter group has no atoms"); } /* ---------------------------------------------------------------------- */ int FixRecenter::setmask() { int mask = 0; mask |= INITIAL_INTEGRATE; + mask |= INITIAL_INTEGRATE_RESPA; return mask; } /* ---------------------------------------------------------------------- */ void FixRecenter::init() { // warn if any integrate fix comes after this one int after = 0; int flag = 0; for (int i = 0; i < modify->nfix; i++) { if (strcmp(id,modify->fix[i]->id) == 0) after = 1; else if ((modify->fmask[i] & INITIAL_INTEGRATE) && after) flag = 1; } if (flag && comm->me == 0) error->warning(FLERR,"Fix recenter should come after all other integration fixes"); masstotal = group->mass(igroup); // if any components of requested COM were INIT, store initial COM if (xinitflag || yinitflag || zinitflag) { double xcm[3]; group->xcm(igroup,masstotal,xcm); xinit = xcm[0]; yinit = xcm[1]; zinit = xcm[2]; } + + if (strstr(update->integrate_style,"respa")) + nlevels_respa = ((Respa *) update->integrate)->nlevels; } /* ---------------------------------------------------------------------- */ void FixRecenter::initial_integrate(int vflag) { // target COM // bounding box around domain works for both orthogonal and triclinic double xtarget,ytarget,ztarget; double *bboxlo,*bboxhi; if (scaleflag == FRACTION) { if (domain->triclinic == 0) { bboxlo = domain->boxlo; bboxhi = domain->boxhi; } else { bboxlo = domain->boxlo_bound; bboxhi = domain->boxhi_bound; } } if (xinitflag) xtarget = xinit; else if (scaleflag == FRACTION) xtarget = bboxlo[0] + xcom*(bboxhi[0] - bboxlo[0]); else xtarget = xcom; if (yinitflag) ytarget = yinit; else if (scaleflag == FRACTION) ytarget = bboxlo[1] + ycom*(bboxhi[1] - bboxlo[1]); else ytarget = ycom; if (zinitflag) ztarget = zinit; else if (scaleflag == FRACTION) ztarget = bboxlo[2] + zcom*(bboxhi[2] - bboxlo[2]); else ztarget = zcom; // current COM double xcm[3]; group->xcm(igroup,masstotal,xcm); // shift coords by difference between actual COM and requested COM double **x = atom->x; int *mask = atom->mask; int nlocal = atom->nlocal; shift[0] = xflag ? (xtarget - xcm[0]) : 0.0; shift[1] = yflag ? (ytarget - xcm[1]) : 0.0; shift[2] = zflag ? (ztarget - xcm[2]) : 0.0; distance = sqrt(shift[0]*shift[0] + shift[1]*shift[1] + shift[2]*shift[2]); for (int i = 0; i < nlocal; i++) if (mask[i] & group2bit) { x[i][0] += shift[0]; x[i][1] += shift[1]; x[i][2] += shift[2]; } } /* ---------------------------------------------------------------------- */ double FixRecenter::compute_scalar() { return distance; } /* ---------------------------------------------------------------------- */ double FixRecenter::compute_vector(int n) { return shift[n]; } +/* ---------------------------------------------------------------------- */ + +void FixRecenter::initial_integrate_respa(int vflag, int ilevel, int iloop) +{ + // outermost level - operate recenter + // all other levels - nothing + + if (ilevel == nlevels_respa-1) initial_integrate(vflag); +} +