diff --git a/src/GRANULAR/fix_pour.cpp b/src/GRANULAR/fix_pour.cpp index fa14c58ee..6ef6759f7 100644 --- a/src/GRANULAR/fix_pour.cpp +++ b/src/GRANULAR/fix_pour.cpp @@ -1,1035 +1,1035 @@ /* ---------------------------------------------------------------------- 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 (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=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 (volfrac*volume/volume_one); if (nper == 0) error->all(FLERR,"Fix pour insertion count per timestep is 0"); 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; + int i,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]; 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); modify->create_attribute(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_atom_swap.cpp b/src/MC/fix_atom_swap.cpp index 8419eef05..7fafca1b1 100644 --- a/src/MC/fix_atom_swap.cpp +++ b/src/MC/fix_atom_swap.cpp @@ -1,766 +1,765 @@ /* ---------------------------------------------------------------------- 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 authors: Paul Crozier (SNL) Alexander Stukowski ------------------------------------------------------------------------- */ #include "math.h" #include "stdlib.h" #include "string.h" #include "fix_atom_swap.h" #include "atom.h" #include "atom_vec.h" #include "atom_vec_hybrid.h" #include "update.h" #include "modify.h" #include "fix.h" #include "comm.h" #include "compute.h" #include "group.h" #include "domain.h" #include "region.h" #include "random_park.h" #include "force.h" #include "pair.h" #include "bond.h" #include "angle.h" #include "dihedral.h" #include "improper.h" #include "kspace.h" #include "math_const.h" #include "memory.h" #include "error.h" #include "thermo.h" #include "output.h" #include "neighbor.h" #include using namespace std; using namespace LAMMPS_NS; using namespace FixConst; using namespace MathConst; /* ---------------------------------------------------------------------- */ FixAtomSwap::FixAtomSwap(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { if (narg < 10) error->all(FLERR,"Illegal fix atom/swap command"); dynamic_group_allow = 1; vector_flag = 1; size_vector = 2; global_freq = 1; extvector = 0; restart_global = 1; time_depend = 1; // required args nevery = force->inumeric(FLERR,arg[3]); ncycles = force->inumeric(FLERR,arg[4]); seed = force->inumeric(FLERR,arg[5]); double temperature = force->numeric(FLERR,arg[6]); beta = 1.0/(force->boltz*temperature); if (ncycles < 0) error->all(FLERR,"Illegal fix atom/swap command"); if (seed <= 0) error->all(FLERR,"Illegal fix atom/swap command"); memory->create(delta_mu,atom->ntypes+1,"atom/swap:delta_mu"); for (int i = 1; i <= atom->ntypes; i++) delta_mu[i] = 0.0; // read options from end of input line options(narg-7,&arg[7]); // random number generator, same for all procs random_equal = new RanPark(lmp,seed); // random number generator, not the same for all procs random_unequal = new RanPark(lmp,seed); // set up reneighboring force_reneighbor = 1; next_reneighbor = update->ntimestep + 1; // zero out counters nswap_attempts = 0.0; nswap_successes = 0.0; atom_swap_nmax = 0; local_swap_atom_list = NULL; local_swap_iatom_list = NULL; local_swap_jatom_list = NULL; // set comm size needed by this Fix if (atom->q_flag) comm_forward = 2; else comm_forward = 1; } /* ---------------------------------------------------------------------- parse optional parameters at end of input line ------------------------------------------------------------------------- */ void FixAtomSwap::options(int narg, char **arg) { if (narg < 0) error->all(FLERR,"Illegal fix atom/swap command"); regionflag = 0; conserve_ke_flag = 1; semi_grand_flag = 0; ndeltamutypes = 0; nswaptypes = 0; iregion = -1; int iarg = 0; while (iarg < narg) { if (strcmp(arg[iarg],"region") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix atom/swap command"); iregion = domain->find_region(arg[iarg+1]); if (iregion == -1) error->all(FLERR,"Region ID for fix atom/swap does not exist"); int n = strlen(arg[iarg+1]) + 1; idregion = new char[n]; strcpy(idregion,arg[iarg+1]); regionflag = 1; iarg += 2; } else if (strcmp(arg[iarg],"ke") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix atom/swap command"); if (strcmp(arg[iarg+1],"no") == 0) conserve_ke_flag = 0; else if (strcmp(arg[iarg+1],"yes") == 0) conserve_ke_flag = 1; else error->all(FLERR,"Illegal fix atom/swap command"); iarg += 2; } else if (strcmp(arg[iarg],"semi-grand") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix atom/swap command"); if (strcmp(arg[iarg+1],"no") == 0) semi_grand_flag = 0; else if (strcmp(arg[iarg+1],"yes") == 0) semi_grand_flag = 1; else error->all(FLERR,"Illegal fix atom/swap command"); iarg += 2; } else if (strcmp(arg[iarg],"types") == 0) { if (iarg+3 > narg) error->all(FLERR,"Illegal fix atom/swap command"); iarg++; memory->create(type_list,atom->ntypes,"atom/swap:type_list"); while (iarg < narg) { if (isalpha(arg[iarg][0])) break; type_list[nswaptypes++] = force->numeric(FLERR,arg[iarg]); iarg++; } } else if (strcmp(arg[iarg],"delta_mu") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix atom/swap command"); iarg++; while (iarg < narg) { if (isalpha(arg[iarg][0])) break; delta_mu[ndeltamutypes+2] = force->numeric(FLERR,arg[iarg]); ndeltamutypes++; iarg++; } } else error->all(FLERR,"Illegal fix atom/swap command"); } } /* ---------------------------------------------------------------------- */ FixAtomSwap::~FixAtomSwap() { memory->destroy(type_list); memory->destroy(delta_mu); memory->destroy(qtype); memory->destroy(sqrt_mass_ratio); if (regionflag) delete [] idregion; delete random_equal; delete random_unequal; } /* ---------------------------------------------------------------------- */ int FixAtomSwap::setmask() { int mask = 0; mask |= PRE_EXCHANGE; return mask; } /* ---------------------------------------------------------------------- */ void FixAtomSwap::init() { char *id_pe = (char *) "thermo_pe"; int ipe = modify->find_compute(id_pe); c_pe = modify->compute[ipe]; int *type = atom->type; if (nswaptypes < 2) error->all(FLERR,"Must specify at least 2 types in fix atom/swap command"); if (semi_grand_flag) { if (atom->ntypes-1 != ndeltamutypes) error->all(FLERR,"Need ntypes-1 delta_mu values in fix atom/swap command"); } else { if (nswaptypes != 2) error->all(FLERR,"Only 2 types allowed when not using semi-grand in fix atom/swap command"); if (ndeltamutypes != 0) error->all(FLERR,"Delta_mu not allowed when not using semi-grand in fix atom/swap command"); } for (int iswaptype = 0; iswaptype < nswaptypes; iswaptype++) if (type_list[iswaptype] <= 0 || type_list[iswaptype] > atom->ntypes) error->all(FLERR,"Invalid atom type in fix atom/swap command"); memory->create(qtype,nswaptypes,"atom/swap:qtype"); for (int iswaptype = 0; iswaptype < nswaptypes; iswaptype++) { if (atom->q_flag) { bool first = true; for (int i = 0; i < atom->nlocal; i++) { if (type[i] == type_list[iswaptype]) { if (first) qtype[iswaptype] = atom->q[i]; first = false; if (qtype[iswaptype] != atom->q[i]) error->all(FLERR,"All atoms of a swapped type must have the same charge."); } } } } memory->create(sqrt_mass_ratio,atom->ntypes+1,atom->ntypes+1,"atom/swap:sqrt_mass_ratio"); for (int itype = 1; itype <= atom->ntypes; itype++) for (int jtype = 1; jtype <= atom->ntypes; jtype++) sqrt_mass_ratio[itype][jtype] = sqrt(atom->mass[itype]/atom->mass[jtype]); // check to see if itype and jtype cutoffs are the same // if not, reneighboring will be needed between swaps double **cutsq = force->pair->cutsq; unequal_cutoffs = false; for (int iswaptype = 0; iswaptype < nswaptypes; iswaptype++) for (int jswaptype = 0; jswaptype < nswaptypes; jswaptype++) for (int ktype = 1; ktype <= atom->ntypes; ktype++) if (cutsq[type_list[iswaptype]][ktype] != cutsq[type_list[jswaptype]][ktype]) unequal_cutoffs = true; // check that no swappable atoms are in atom->firstgroup // swapping such an atom might not leave firstgroup atoms first if (atom->firstgroup >= 0) { int *mask = atom->mask; int firstgroupbit = group->bitmask[atom->firstgroup]; int flag = 0; for (int i = 0; i < atom->nlocal; i++) if ((mask[i] == groupbit) && (mask[i] && firstgroupbit)) flag = 1; int flagall; MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_SUM,world); if (flagall) error->all(FLERR,"Cannot do atom/swap on atoms in atom_modify first group"); } } /* ---------------------------------------------------------------------- attempt Monte Carlo swaps ------------------------------------------------------------------------- */ void FixAtomSwap::pre_exchange() { // just return if should not be called on this timestep if (next_reneighbor != update->ntimestep) return; if (domain->triclinic) domain->x2lamda(atom->nlocal); domain->pbc(); comm->exchange(); comm->borders(); if (domain->triclinic) domain->lamda2x(atom->nlocal+atom->nghost); if (modify->n_pre_neighbor) modify->pre_neighbor(); neighbor->build(); energy_stored = energy_full(); int nsuccess = 0; if (semi_grand_flag) { update_semi_grand_atoms_list(); for (int i = 0; i < ncycles; i++) nsuccess += attempt_semi_grand(); } else { update_swap_atoms_list(); for (int i = 0; i < ncycles; i++) nsuccess += attempt_swap(); } nswap_attempts += ncycles; nswap_successes += nsuccess; energy_full(); next_reneighbor = update->ntimestep + nevery; } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ int FixAtomSwap::attempt_semi_grand() { if (nswap == 0) return 0; double energy_before = energy_stored; int itype,jtype,jswaptype; double qtmp; int i = pick_semi_grand_atom(); if (i >= 0) { jswaptype = static_cast (nswaptypes*random_unequal->uniform()); jtype = type_list[jswaptype]; itype = atom->type[i]; while (itype == jtype) { jswaptype = static_cast (nswaptypes*random_unequal->uniform()); jtype = type_list[jswaptype]; } atom->type[i] = jtype; if (atom->q_flag) { qtmp = atom->q[i]; atom->q[i] = qtype[jswaptype]; } } if (unequal_cutoffs) { if (domain->triclinic) domain->x2lamda(atom->nlocal); domain->pbc(); comm->exchange(); comm->borders(); if (domain->triclinic) domain->lamda2x(atom->nlocal+atom->nghost); if (modify->n_pre_neighbor) modify->pre_neighbor(); neighbor->build(); } else { comm->forward_comm_fix(this); } double energy_after = energy_full(); int success = 0; if (i >= 0) if (random_unequal->uniform() < exp(-beta*(energy_after - energy_before + delta_mu[jtype] - delta_mu[itype]))) success = 1; int success_all = 0; MPI_Allreduce(&success,&success_all,1,MPI_INT,MPI_MAX,world); if (success_all) { update_semi_grand_atoms_list(); energy_stored = energy_after; if (conserve_ke_flag) { if (i >= 0) { atom->v[i][0] *= sqrt_mass_ratio[itype][jtype]; atom->v[i][1] *= sqrt_mass_ratio[itype][jtype]; atom->v[i][2] *= sqrt_mass_ratio[itype][jtype]; } } return 1; } else { if (i >= 0) { atom->type[i] = itype; if (atom->q_flag) atom->q[i] = qtmp; } energy_stored = energy_before; if (unequal_cutoffs) { if (domain->triclinic) domain->x2lamda(atom->nlocal); domain->pbc(); comm->exchange(); comm->borders(); if (domain->triclinic) domain->lamda2x(atom->nlocal+atom->nghost); if (modify->n_pre_neighbor) modify->pre_neighbor(); neighbor->build(); } else { comm->forward_comm_fix(this); } } return 0; } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ int FixAtomSwap::attempt_swap() { if ((niswap == 0) || (njswap == 0)) return 0; double energy_before = energy_stored; int i = pick_i_swap_atom(); int j = pick_j_swap_atom(); int itype = type_list[0]; int jtype = type_list[1]; if (i >= 0) { atom->type[i] = jtype; if (atom->q_flag) atom->q[i] = qtype[1]; } if (j >= 0) { atom->type[j] = itype; if (atom->q_flag) atom->q[j] = qtype[0]; } if (unequal_cutoffs) { if (domain->triclinic) domain->x2lamda(atom->nlocal); domain->pbc(); comm->exchange(); comm->borders(); if (domain->triclinic) domain->lamda2x(atom->nlocal+atom->nghost); if (modify->n_pre_neighbor) modify->pre_neighbor(); neighbor->build(); } else { comm->forward_comm_fix(this); } double energy_after = energy_full(); if (random_equal->uniform() < exp(beta*(energy_before - energy_after))) { update_swap_atoms_list(); energy_stored = energy_after; if (conserve_ke_flag) { if (i >= 0) { atom->v[i][0] *= sqrt_mass_ratio[itype][jtype]; atom->v[i][1] *= sqrt_mass_ratio[itype][jtype]; atom->v[i][2] *= sqrt_mass_ratio[itype][jtype]; } if (j >= 0) { atom->v[j][0] *= sqrt_mass_ratio[jtype][itype]; atom->v[j][1] *= sqrt_mass_ratio[jtype][itype]; atom->v[j][2] *= sqrt_mass_ratio[jtype][itype]; } } return 1; } else { if (i >= 0) { atom->type[i] = type_list[0]; if (atom->q_flag) atom->q[i] = qtype[0]; } if (j >= 0) { atom->type[j] = type_list[1]; if (atom->q_flag) atom->q[j] = qtype[1]; } energy_stored = energy_before; if (unequal_cutoffs) { if (domain->triclinic) domain->x2lamda(atom->nlocal); domain->pbc(); comm->exchange(); comm->borders(); if (domain->triclinic) domain->lamda2x(atom->nlocal+atom->nghost); if (modify->n_pre_neighbor) modify->pre_neighbor(); neighbor->build(); } else { comm->forward_comm_fix(this); } } return 0; } /* ---------------------------------------------------------------------- compute system potential energy ------------------------------------------------------------------------- */ double FixAtomSwap::energy_full() { int eflag = 1; int vflag = 0; if (force->pair) force->pair->compute(eflag,vflag); if (atom->molecular) { if (force->bond) force->bond->compute(eflag,vflag); if (force->angle) force->angle->compute(eflag,vflag); if (force->dihedral) force->dihedral->compute(eflag,vflag); if (force->improper) force->improper->compute(eflag,vflag); } if (force->kspace) force->kspace->compute(eflag,vflag); update->eflag_global = update->ntimestep; double total_energy = c_pe->compute_scalar(); return total_energy; } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ int FixAtomSwap::pick_semi_grand_atom() { int i = -1; int iwhichglobal = static_cast (nswap*random_equal->uniform()); if ((iwhichglobal >= nswap_before) && (iwhichglobal < nswap_before + nswap_local)) { int iwhichlocal = iwhichglobal - nswap_before; i = local_swap_atom_list[iwhichlocal]; } return i; } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ int FixAtomSwap::pick_i_swap_atom() { int i = -1; int iwhichglobal = static_cast (niswap*random_equal->uniform()); if ((iwhichglobal >= niswap_before) && (iwhichglobal < niswap_before + niswap_local)) { int iwhichlocal = iwhichglobal - niswap_before; i = local_swap_iatom_list[iwhichlocal]; } return i; } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ int FixAtomSwap::pick_j_swap_atom() { int j = -1; int jwhichglobal = static_cast (njswap*random_equal->uniform()); if ((jwhichglobal >= njswap_before) && (jwhichglobal < njswap_before + njswap_local)) { int jwhichlocal = jwhichglobal - njswap_before; j = local_swap_jatom_list[jwhichlocal]; } return j; } /* ---------------------------------------------------------------------- update the list of gas atoms ------------------------------------------------------------------------- */ void FixAtomSwap::update_semi_grand_atoms_list() { int nlocal = atom->nlocal; - int *type = atom->type; double **x = atom->x; if (nlocal > atom_swap_nmax) { memory->sfree(local_swap_atom_list); atom_swap_nmax = atom->nmax; local_swap_atom_list = (int *) memory->smalloc(atom_swap_nmax*sizeof(int), "MCSWAP:local_swap_atom_list"); } nswap_local = 0; if (regionflag) { for (int i = 0; i < nlocal; i++) { if (domain->regions[iregion]->match(x[i][0],x[i][1],x[i][2]) == 1) { if (atom->mask[i] & groupbit) { local_swap_atom_list[nswap_local] = i; nswap_local++; } } } } else { for (int i = 0; i < nlocal; i++) { if (atom->mask[i] & groupbit) { local_swap_atom_list[nswap_local] = i; nswap_local++; } } } MPI_Allreduce(&nswap_local,&nswap,1,MPI_INT,MPI_SUM,world); MPI_Scan(&nswap_local,&nswap_before,1,MPI_INT,MPI_SUM,world); nswap_before -= nswap_local; } /* ---------------------------------------------------------------------- update the list of gas atoms ------------------------------------------------------------------------- */ void FixAtomSwap::update_swap_atoms_list() { int nlocal = atom->nlocal; int *type = atom->type; double **x = atom->x; if (nlocal > atom_swap_nmax) { memory->sfree(local_swap_iatom_list); memory->sfree(local_swap_jatom_list); atom_swap_nmax = atom->nmax; local_swap_iatom_list = (int *) memory->smalloc(atom_swap_nmax*sizeof(int), "MCSWAP:local_swap_iatom_list"); local_swap_jatom_list = (int *) memory->smalloc(atom_swap_nmax*sizeof(int), "MCSWAP:local_swap_jatom_list"); } niswap_local = 0; njswap_local = 0; if (regionflag) { for (int i = 0; i < nlocal; i++) { if (domain->regions[iregion]->match(x[i][0],x[i][1],x[i][2]) == 1) { if (atom->mask[i] & groupbit) { if (type[i] == type_list[0]) { local_swap_iatom_list[niswap_local] = i; niswap_local++; } else if (type[i] == type_list[1]) { local_swap_jatom_list[njswap_local] = i; njswap_local++; } } } } } else { for (int i = 0; i < nlocal; i++) { if (atom->mask[i] & groupbit) { if (type[i] == type_list[0]) { local_swap_iatom_list[niswap_local] = i; niswap_local++; } else if (type[i] == type_list[1]) { local_swap_jatom_list[njswap_local] = i; njswap_local++; } } } } MPI_Allreduce(&niswap_local,&niswap,1,MPI_INT,MPI_SUM,world); MPI_Scan(&niswap_local,&niswap_before,1,MPI_INT,MPI_SUM,world); niswap_before -= niswap_local; MPI_Allreduce(&njswap_local,&njswap,1,MPI_INT,MPI_SUM,world); MPI_Scan(&njswap_local,&njswap_before,1,MPI_INT,MPI_SUM,world); njswap_before -= njswap_local; } /* ---------------------------------------------------------------------- */ int FixAtomSwap::pack_forward_comm(int n, int *list, double *buf, int pbc_flag, int *pbc) { int i,j,m; int *type = atom->type; double *q = atom->q; m = 0; if (atom->q_flag) { for (i = 0; i < n; i++) { j = list[i]; buf[m++] = type[j]; buf[m++] = q[j]; } } else { for (i = 0; i < n; i++) { j = list[i]; buf[m++] = type[j]; } } return m; } /* ---------------------------------------------------------------------- */ void FixAtomSwap::unpack_forward_comm(int n, int first, double *buf) { int i,m,last; int *type = atom->type; double *q = atom->q; m = 0; last = first + n; if (atom->q_flag) { for (i = first; i < last; i++) { type[i] = static_cast (buf[m++]); q[i] = buf[m++]; } } else { for (i = first; i < last; i++) type[i] = static_cast (buf[m++]); } } /* ---------------------------------------------------------------------- return acceptance ratio ------------------------------------------------------------------------- */ double FixAtomSwap::compute_vector(int n) { if (n == 0) return nswap_attempts; if (n == 1) return nswap_successes; return 0.0; } /* ---------------------------------------------------------------------- memory usage of local atom-based arrays ------------------------------------------------------------------------- */ double FixAtomSwap::memory_usage() { double bytes = atom_swap_nmax * sizeof(int); return bytes; } /* ---------------------------------------------------------------------- pack entire state of Fix into one write ------------------------------------------------------------------------- */ void FixAtomSwap::write_restart(FILE *fp) { int n = 0; double list[4]; list[n++] = random_equal->state(); list[n++] = next_reneighbor; if (comm->me == 0) { int size = n * sizeof(double); fwrite(&size,sizeof(int),1,fp); fwrite(list,sizeof(double),n,fp); } } /* ---------------------------------------------------------------------- use state info from restart file to restart the Fix ------------------------------------------------------------------------- */ void FixAtomSwap::restart(char *buf) { int n = 0; double *list = (double *) buf; seed = static_cast (list[n++]); random_equal->reset(seed); seed = static_cast (list[n++]); random_unequal->reset(seed); next_reneighbor = static_cast (list[n++]); } diff --git a/src/MC/fix_gcmc.cpp b/src/MC/fix_gcmc.cpp index 16979195c..e4af71faf 100644 --- a/src/MC/fix_gcmc.cpp +++ b/src/MC/fix_gcmc.cpp @@ -1,1974 +1,1968 @@ /* ---------------------------------------------------------------------- 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: Paul Crozier (SNL) ------------------------------------------------------------------------- */ #include "math.h" #include "stdlib.h" #include "string.h" #include "fix_gcmc.h" #include "atom.h" #include "atom_vec.h" #include "atom_vec_hybrid.h" #include "molecule.h" #include "update.h" #include "modify.h" #include "fix.h" #include "comm.h" #include "compute.h" #include "group.h" #include "domain.h" #include "region.h" #include "random_park.h" #include "force.h" #include "pair.h" #include "bond.h" #include "angle.h" #include "dihedral.h" #include "improper.h" #include "kspace.h" #include "math_extra.h" #include "math_const.h" #include "memory.h" #include "error.h" #include "thermo.h" #include "output.h" #include "neighbor.h" #include using namespace std; using namespace LAMMPS_NS; using namespace FixConst; using namespace MathConst; enum{ATOM,MOLECULE}; /* ---------------------------------------------------------------------- */ FixGCMC::FixGCMC(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { if (narg < 11) error->all(FLERR,"Illegal fix gcmc command"); if (atom->molecular == 2) error->all(FLERR,"Fix gcmc does not (yet) work with atom_style template"); dynamic_group_allow = 1; vector_flag = 1; size_vector = 8; global_freq = 1; extvector = 0; restart_global = 1; time_depend = 1; // required args nevery = force->inumeric(FLERR,arg[3]); nexchanges = force->inumeric(FLERR,arg[4]); nmcmoves = force->inumeric(FLERR,arg[5]); ngcmc_type = force->inumeric(FLERR,arg[6]); seed = force->inumeric(FLERR,arg[7]); reservoir_temperature = force->numeric(FLERR,arg[8]); chemical_potential = force->numeric(FLERR,arg[9]); displace = force->numeric(FLERR,arg[10]); if (nexchanges < 0) error->all(FLERR,"Illegal fix gcmc command"); if (nmcmoves < 0) error->all(FLERR,"Illegal fix gcmc command"); if (seed <= 0) error->all(FLERR,"Illegal fix gcmc command"); if (reservoir_temperature < 0.0) error->all(FLERR,"Illegal fix gcmc command"); if (displace < 0.0) error->all(FLERR,"Illegal fix gcmc command"); // read options from end of input line options(narg-11,&arg[11]); // random number generator, same for all procs random_equal = new RanPark(lmp,seed); // random number generator, not the same for all procs random_unequal = new RanPark(lmp,seed); // error checks on region and its extent being inside simulation box region_xlo = region_xhi = region_ylo = region_yhi = region_zlo = region_zhi = 0.0; if (regionflag) { if (domain->regions[iregion]->bboxflag == 0) error->all(FLERR,"Fix gcmc region does not support a bounding box"); if (domain->regions[iregion]->dynamic_check()) error->all(FLERR,"Fix gcmc region cannot be dynamic"); region_xlo = domain->regions[iregion]->extent_xlo; region_xhi = domain->regions[iregion]->extent_xhi; region_ylo = domain->regions[iregion]->extent_ylo; region_yhi = domain->regions[iregion]->extent_yhi; region_zlo = domain->regions[iregion]->extent_zlo; region_zhi = domain->regions[iregion]->extent_zhi; if (region_xlo < domain->boxlo[0] || region_xhi > domain->boxhi[0] || region_ylo < domain->boxlo[1] || region_yhi > domain->boxhi[1] || region_zlo < domain->boxlo[2] || region_zhi > domain->boxhi[2]) error->all(FLERR,"Fix gcmc region extends outside simulation box"); // estimate region volume using MC trials double coord[3]; int inside = 0; int attempts = 10000000; for (int i = 0; i < attempts; i++) { coord[0] = region_xlo + random_equal->uniform() * (region_xhi-region_xlo); coord[1] = region_ylo + random_equal->uniform() * (region_yhi-region_ylo); coord[2] = region_zlo + random_equal->uniform() * (region_zhi-region_zlo); if (domain->regions[iregion]->match(coord[0],coord[1],coord[2]) != 0) inside++; } double max_region_volume = (region_xhi - region_xlo)* (region_yhi - region_ylo)*(region_zhi - region_zlo); region_volume = max_region_volume*static_cast (inside)/ static_cast (attempts); } // error check and further setup for mode = MOLECULE if (mode == MOLECULE) { if (onemols[imol]->xflag == 0) error->all(FLERR,"Fix gcmc molecule must have coordinates"); if (onemols[imol]->typeflag == 0) error->all(FLERR,"Fix gcmc molecule must have atom types"); if (ngcmc_type+onemols[imol]->ntypes <= 0 || ngcmc_type+onemols[imol]->ntypes > atom->ntypes) error->all(FLERR,"Invalid atom type in fix gcmc mol command"); if (atom->molecular == 2 && onemols != atom->avec->onemols) error->all(FLERR,"Fix gcmc molecule template ID must be same " "as atom_style template ID"); onemols[imol]->check_attributes(0); } if (shakeflag && mode == ATOM) error->all(FLERR,"Cannot use fix gcmc shake and not molecule"); // setup of coords and imageflags array if (mode == ATOM) natoms_per_molecule = 1; else natoms_per_molecule = onemols[imol]->natoms; memory->create(coords,natoms_per_molecule,3,"gcmc:coords"); memory->create(imageflags,natoms_per_molecule,"gcmc:imageflags"); memory->create(atom_coord,natoms_per_molecule,3,"gcmc:atom_coord"); // compute the number of MC cycles that occur nevery timesteps ncycles = nexchanges + nmcmoves; // set up reneighboring force_reneighbor = 1; next_reneighbor = update->ntimestep + 1; // zero out counters ntranslation_attempts = 0.0; ntranslation_successes = 0.0; nrotation_attempts = 0.0; nrotation_successes = 0.0; ndeletion_attempts = 0.0; ndeletion_successes = 0.0; ninsertion_attempts = 0.0; ninsertion_successes = 0.0; gcmc_nmax = 0; local_gas_list = NULL; } /* ---------------------------------------------------------------------- parse optional parameters at end of input line ------------------------------------------------------------------------- */ void FixGCMC::options(int narg, char **arg) { if (narg < 0) error->all(FLERR,"Illegal fix gcmc command"); // defaults mode = ATOM; max_rotation_angle = 10*MY_PI/180; regionflag = 0; iregion = -1; region_volume = 0; max_region_attempts = 1000; molecule_group = 0; molecule_group_bit = 0; molecule_group_inversebit = 0; exclusion_group = 0; exclusion_group_bit = 0; exclusion_group_inversebit = 0; pressure_flag = false; pressure = 0.0; fugacity_coeff = 1.0; shakeflag = 0; charge = 0.0; charge_flag = false; full_flag = false; idshake = NULL; int iarg = 0; while (iarg < narg) { if (strcmp(arg[iarg],"mol") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix gcmc command"); imol = atom->find_molecule(arg[iarg+1]); if (imol == -1) error->all(FLERR,"Molecule template ID for fix gcmc does not exist"); if (atom->molecules[imol]->nset > 1 && comm->me == 0) error->warning(FLERR,"Molecule template for " "fix gcmc has multiple molecules"); mode = MOLECULE; onemols = &atom->molecules[imol]; nmol = onemols[0]->nset; iarg += 2; } else if (strcmp(arg[iarg],"region") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix gcmc command"); iregion = domain->find_region(arg[iarg+1]); if (iregion == -1) error->all(FLERR,"Region ID for fix gcmc does not exist"); int n = strlen(arg[iarg+1]) + 1; idregion = new char[n]; strcpy(idregion,arg[iarg+1]); regionflag = 1; iarg += 2; } else if (strcmp(arg[iarg],"maxangle") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix gcmc command"); max_rotation_angle = force->numeric(FLERR,arg[iarg+1]); max_rotation_angle *= MY_PI/180; iarg += 2; } else if (strcmp(arg[iarg],"pressure") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix gcmc command"); pressure = force->numeric(FLERR,arg[iarg+1]); pressure_flag = true; iarg += 2; } else if (strcmp(arg[iarg],"fugacity_coeff") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix gcmc command"); fugacity_coeff = force->numeric(FLERR,arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"charge") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix gcmc command"); charge = force->numeric(FLERR,arg[iarg+1]); charge_flag = true; iarg += 2; } else if (strcmp(arg[iarg],"shake") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix gcmc 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],"full_energy") == 0) { full_flag = true; iarg += 1; } else error->all(FLERR,"Illegal fix gcmc command"); } } /* ---------------------------------------------------------------------- */ FixGCMC::~FixGCMC() { if (regionflag) delete [] idregion; delete random_equal; delete random_unequal; // remove groups this fix defined // only do it if the groups exists and group itself exists if (molecule_group && (strcmp(group->names[0],"all") == 0)) { char **group_arg = new char*[2]; group_arg[0] = group->names[molecule_group]; group_arg[1] = (char *) "delete"; group->assign(2,group_arg); delete [] group_arg; } if (exclusion_group && (strcmp(group->names[0],"all") == 0)) { char **group_arg = new char*[2]; group_arg[0] = group->names[exclusion_group]; group_arg[1] = (char *) "delete"; group->assign(2,group_arg); delete [] group_arg; } memory->destroy(local_gas_list); memory->destroy(atom_coord); memory->destroy(coords); memory->destroy(imageflags); delete [] idshake; } /* ---------------------------------------------------------------------- */ int FixGCMC::setmask() { int mask = 0; mask |= PRE_EXCHANGE; return mask; } /* ---------------------------------------------------------------------- */ void FixGCMC::init() { // decide whether to switch to the full_energy option if (!full_flag) { if ((force->kspace) || (force->pair == NULL) || (force->pair->single_enable == 0) || (force->pair_match("hybrid",0)) || (force->pair_match("eam",0)) || (domain->triclinic == 1)) { full_flag = true; if (comm->me == 0) error->warning(FLERR,"fix gcmc using full_energy option"); } } if (full_flag) { char *id_pe = (char *) "thermo_pe"; int ipe = modify->find_compute(id_pe); c_pe = modify->compute[ipe]; } int *type = atom->type; if (mode == ATOM) { if (ngcmc_type <= 0 || ngcmc_type > atom->ntypes) error->all(FLERR,"Invalid atom type in fix gcmc command"); } // if mode == ATOM, warn if any deletable atom has a mol ID if ((mode == ATOM) && atom->molecule_flag) { tagint *molecule = atom->molecule; int flag = 0; for (int i = 0; i < atom->nlocal; i++) if (type[i] == ngcmc_type) if (molecule[i]) flag = 1; int flagall; MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_SUM,world); if (flagall && comm->me == 0) error->all(FLERR, "Fix gcmc cannot exchange individual atoms belonging to a molecule"); } // if mode == MOLECULE, check for unset mol IDs if (mode == MOLECULE) { tagint *molecule = atom->molecule; int *mask = atom->mask; int flag = 0; for (int i = 0; i < atom->nlocal; i++) if (mask[i] == groupbit) if (molecule[i] == 0) flag = 1; int flagall; MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_SUM,world); if (flagall && comm->me == 0) error->all(FLERR, "All mol IDs should be set for fix gcmc group atoms"); } if (((mode == MOLECULE) && (atom->molecule_flag == 0)) || ((mode == MOLECULE) && (!atom->tag_enable || !atom->map_style))) error->all(FLERR, "Fix gcmc molecule command requires that " "atoms have molecule attributes"); // 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 gcmc shake fix does not exist"); fixshake = modify->fix[ifix]; int tmp; if (onemols != (Molecule **) fixshake->extract("onemol",tmp)) error->all(FLERR,"Fix gcmc and fix shake not using " "same molecule template ID"); } if (domain->dimension == 2) error->all(FLERR,"Cannot use fix gcmc in a 2d simulation"); // create a new group for interaction exclusions if (full_flag) { char **group_arg = new char*[4]; // create unique group name for atoms to be excluded int len = strlen(id) + 30; group_arg[0] = new char[len]; sprintf(group_arg[0],"FixGCMC:gcmc_exclusion_group:%s",id); group_arg[1] = (char *) "subtract"; group_arg[2] = (char *) "all"; group_arg[3] = (char *) "all"; group->assign(4,group_arg); exclusion_group = group->find(group_arg[0]); if (exclusion_group == -1) error->all(FLERR,"Could not find fix gcmc exclusion group ID"); exclusion_group_bit = group->bitmask[exclusion_group]; exclusion_group_inversebit = exclusion_group_bit ^ ~0; // neighbor list exclusion setup // turn off interactions between group all and the exclusion group int narg = 4; char **arg = new char*[narg];; arg[0] = (char *) "exclude"; arg[1] = (char *) "group"; arg[2] = group_arg[0]; arg[3] = (char *) "all"; neighbor->modify_params(narg,arg); delete [] group_arg[0]; } // create a new group for temporary use with selected molecules if (mode == MOLECULE) { char **group_arg = new char*[3]; // create unique group name for atoms to be rotated int len = strlen(id) + 30; group_arg[0] = new char[len]; sprintf(group_arg[0],"FixGCMC:rotation_gas_atoms:%s",id); group_arg[1] = (char *) "molecule"; char digits[12]; sprintf(digits,"%d",ngcmc_type); group_arg[2] = digits; group->assign(3,group_arg); molecule_group = group->find(group_arg[0]); if (molecule_group == -1) error->all(FLERR,"Could not find fix gcmc rotation group ID"); molecule_group_bit = group->bitmask[molecule_group]; molecule_group_inversebit = molecule_group_bit ^ ~0; delete [] group_arg[0]; } // get all of the needed molecule data if mode == MOLECULE, // otherwise just get the gas mass if (mode == MOLECULE) { onemols[imol]->compute_mass(); onemols[imol]->compute_com(); gas_mass = onemols[imol]->masstotal; for (int i = 0; i < onemols[imol]->natoms; i++) { onemols[imol]->x[i][0] -= onemols[imol]->com[0]; onemols[imol]->x[i][1] -= onemols[imol]->com[1]; onemols[imol]->x[i][2] -= onemols[imol]->com[2]; } } else gas_mass = atom->mass[ngcmc_type]; if (gas_mass <= 0.0) error->all(FLERR,"Illegal fix gcmc gas mass <= 0"); // check that no deletable atoms are in atom->firstgroup // deleting such an atom would not leave firstgroup atoms first if (atom->firstgroup >= 0) { int *mask = atom->mask; int firstgroupbit = group->bitmask[atom->firstgroup]; int flag = 0; for (int i = 0; i < atom->nlocal; i++) if ((mask[i] == groupbit) && (mask[i] && firstgroupbit)) flag = 1; int flagall; MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_SUM,world); if (flagall) error->all(FLERR,"Cannot do GCMC on atoms in atom_modify first group"); } // compute beta, lambda, sigma, and the zz factor beta = 1.0/(force->boltz*reservoir_temperature); double lambda = sqrt(force->hplanck*force->hplanck/ (2.0*MY_PI*gas_mass*force->mvv2e* force->boltz*reservoir_temperature)); sigma = sqrt(force->boltz*reservoir_temperature/gas_mass/force->mvv2e); zz = exp(beta*chemical_potential)/(pow(lambda,3.0)); if (pressure_flag) zz = pressure*fugacity_coeff*beta/force->nktv2p; imagetmp = ((imageint) IMGMAX << IMG2BITS) | ((imageint) IMGMAX << IMGBITS) | IMGMAX; } /* ---------------------------------------------------------------------- attempt Monte Carlo translations, rotations, insertions, and deletions done before exchange, borders, reneighbor so that ghost atoms and neighbor lists will be correct ------------------------------------------------------------------------- */ void FixGCMC::pre_exchange() { // just return if should not be called on this timestep if (next_reneighbor != update->ntimestep) return; xlo = domain->boxlo[0]; xhi = domain->boxhi[0]; ylo = domain->boxlo[1]; yhi = domain->boxhi[1]; zlo = domain->boxlo[2]; zhi = domain->boxhi[2]; sublo = domain->sublo; subhi = domain->subhi; if (regionflag) volume = region_volume; else volume = domain->xprd * domain->yprd * domain->zprd; domain->pbc(); comm->exchange(); atom->nghost = 0; comm->borders(); update_gas_atoms_list(); if (full_flag) { energy_stored = energy_full(); if (mode == MOLECULE) { for (int i = 0; i < ncycles; i++) { int random_int_fraction = static_cast(random_equal->uniform()*ncycles) + 1; if (random_int_fraction <= nmcmoves) { if (random_equal->uniform() < 0.5) attempt_molecule_translation_full(); else attempt_molecule_rotation_full(); } else { if (random_equal->uniform() < 0.5) attempt_molecule_deletion_full(); else attempt_molecule_insertion_full(); } } } else { for (int i = 0; i < ncycles; i++) { int random_int_fraction = static_cast(random_equal->uniform()*ncycles) + 1; if (random_int_fraction <= nmcmoves) { attempt_atomic_translation_full(); } else { if (random_equal->uniform() < 0.5) attempt_atomic_deletion_full(); else attempt_atomic_insertion_full(); } } } domain->pbc(); comm->exchange(); atom->nghost = 0; comm->borders(); } else { if (mode == MOLECULE) { for (int i = 0; i < ncycles; i++) { int random_int_fraction = static_cast(random_equal->uniform()*ncycles) + 1; if (random_int_fraction <= nmcmoves) { if (random_equal->uniform() < 0.5) attempt_molecule_translation(); else attempt_molecule_rotation(); } else { if (random_equal->uniform() < 0.5) attempt_molecule_deletion(); else attempt_molecule_insertion(); } } } else { for (int i = 0; i < ncycles; i++) { int random_int_fraction = static_cast(random_equal->uniform()*ncycles) + 1; if (random_int_fraction <= nmcmoves) { attempt_atomic_translation(); } else { if (random_equal->uniform() < 0.5) attempt_atomic_deletion(); else attempt_atomic_insertion(); } } } } next_reneighbor = update->ntimestep + nevery; } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ void FixGCMC::attempt_atomic_translation() { ntranslation_attempts += 1.0; if (ngas == 0) return; int i = pick_random_gas_atom(); int success = 0; if (i >= 0) { double **x = atom->x; double energy_before = energy(i,ngcmc_type,-1,x[i]); double rsq = 1.1; double rx,ry,rz; rx = ry = rz = 0.0; double coord[3]; while (rsq > 1.0) { rx = 2*random_unequal->uniform() - 1.0; ry = 2*random_unequal->uniform() - 1.0; rz = 2*random_unequal->uniform() - 1.0; rsq = rx*rx + ry*ry + rz*rz; } coord[0] = x[i][0] + displace*rx; coord[1] = x[i][1] + displace*ry; coord[2] = x[i][2] + displace*rz; if (regionflag) { while (domain->regions[iregion]->match(coord[0],coord[1],coord[2]) == 0) { rsq = 1.1; while (rsq > 1.0) { rx = 2*random_unequal->uniform() - 1.0; ry = 2*random_unequal->uniform() - 1.0; rz = 2*random_unequal->uniform() - 1.0; rsq = rx*rx + ry*ry + rz*rz; } coord[0] = x[i][0] + displace*rx; coord[1] = x[i][1] + displace*ry; coord[2] = x[i][2] + displace*rz; } } double energy_after = energy(i,ngcmc_type,-1,coord); if (random_unequal->uniform() < exp(beta*(energy_before - energy_after))) { x[i][0] = coord[0]; x[i][1] = coord[1]; x[i][2] = coord[2]; success = 1; } } int success_all = 0; MPI_Allreduce(&success,&success_all,1,MPI_INT,MPI_MAX,world); if (success_all) { domain->pbc(); comm->exchange(); atom->nghost = 0; comm->borders(); update_gas_atoms_list(); ntranslation_successes += 1.0; } } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ void FixGCMC::attempt_atomic_deletion() { ndeletion_attempts += 1.0; if (ngas == 0) return; int i = pick_random_gas_atom(); int success = 0; if (i >= 0) { double deletion_energy = energy(i,ngcmc_type,-1,atom->x[i]); if (random_unequal->uniform() < ngas*exp(beta*deletion_energy)/(zz*volume)) { atom->avec->copy(atom->nlocal-1,i,1); atom->nlocal--; success = 1; } } int success_all = 0; MPI_Allreduce(&success,&success_all,1,MPI_INT,MPI_MAX,world); if (success_all) { atom->natoms--; if (atom->tag_enable) { if (atom->map_style) atom->map_init(); } atom->nghost = 0; comm->borders(); update_gas_atoms_list(); ndeletion_successes += 1.0; } } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ void FixGCMC::attempt_atomic_insertion() { ninsertion_attempts += 1.0; double coord[3]; if (regionflag) { int region_attempt = 0; coord[0] = region_xlo + random_equal->uniform() * (region_xhi-region_xlo); coord[1] = region_ylo + random_equal->uniform() * (region_yhi-region_ylo); coord[2] = region_zlo + random_equal->uniform() * (region_zhi-region_zlo); while (domain->regions[iregion]->match(coord[0],coord[1],coord[2]) == 0) { coord[0] = region_xlo + random_equal->uniform() * (region_xhi-region_xlo); coord[1] = region_ylo + random_equal->uniform() * (region_yhi-region_ylo); coord[2] = region_zlo + random_equal->uniform() * (region_zhi-region_zlo); region_attempt++; if (region_attempt >= max_region_attempts) return; } } else { coord[0] = xlo + random_equal->uniform() * (xhi-xlo); coord[1] = ylo + random_equal->uniform() * (yhi-ylo); coord[2] = zlo + random_equal->uniform() * (zhi-zlo); } int proc_flag = 0; if (coord[0] >= sublo[0] && coord[0] < subhi[0] && coord[1] >= sublo[1] && coord[1] < subhi[1] && coord[2] >= sublo[2] && coord[2] < subhi[2]) proc_flag = 1; int success = 0; if (proc_flag) { double insertion_energy = energy(-1,ngcmc_type,-1,coord); if (random_unequal->uniform() < zz*volume*exp(-beta*insertion_energy)/(ngas+1)) { atom->avec->create_atom(ngcmc_type,coord); int m = atom->nlocal - 1; atom->mask[m] = 1 | groupbit; atom->v[m][0] = random_unequal->gaussian()*sigma; atom->v[m][1] = random_unequal->gaussian()*sigma; atom->v[m][2] = random_unequal->gaussian()*sigma; modify->create_attribute(m); success = 1; } } int success_all = 0; MPI_Allreduce(&success,&success_all,1,MPI_INT,MPI_MAX,world); if (success_all) { atom->natoms++; if (atom->tag_enable) { atom->tag_extend(); if (atom->map_style) atom->map_init(); } atom->nghost = 0; comm->borders(); update_gas_atoms_list(); ninsertion_successes += 1.0; } } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ void FixGCMC::attempt_molecule_translation() { ntranslation_attempts += 1.0; if (ngas == 0) return; tagint translation_molecule = pick_random_gas_molecule(); if (translation_molecule == -1) return; double energy_before_sum = molecule_energy(translation_molecule); double **x = atom->x; double rx,ry,rz; double com_displace[3],coord[3]; double rsq = 1.1; while (rsq > 1.0) { rx = 2*random_equal->uniform() - 1.0; ry = 2*random_equal->uniform() - 1.0; rz = 2*random_equal->uniform() - 1.0; rsq = rx*rx + ry*ry + rz*rz; } com_displace[0] = displace*rx; com_displace[1] = displace*ry; com_displace[2] = displace*rz; int nlocal = atom->nlocal; if (regionflag) { int *mask = atom->mask; for (int i = 0; i < nlocal; i++) { if (atom->molecule[i] == translation_molecule) { mask[i] |= molecule_group_bit; } else { mask[i] &= molecule_group_inversebit; } } double com[3]; com[0] = com[1] = com[2] = 0.0; group->xcm(molecule_group,gas_mass,com); coord[0] = com[0] + displace*rx; coord[1] = com[1] + displace*ry; coord[2] = com[2] + displace*rz; while (domain->regions[iregion]->match(coord[0],coord[1],coord[2]) == 0) { rsq = 1.1; while (rsq > 1.0) { rx = 2*random_equal->uniform() - 1.0; ry = 2*random_equal->uniform() - 1.0; rz = 2*random_equal->uniform() - 1.0; rsq = rx*rx + ry*ry + rz*rz; } coord[0] = com[0] + displace*rx; coord[1] = com[1] + displace*ry; coord[2] = com[2] + displace*rz; } com_displace[0] = displace*rx; com_displace[1] = displace*ry; com_displace[2] = displace*rz; } double energy_after = 0.0; for (int i = 0; i < nlocal; i++) { if (atom->molecule[i] == translation_molecule) { coord[0] = x[i][0] + com_displace[0]; coord[1] = x[i][1] + com_displace[1]; coord[2] = x[i][2] + com_displace[2]; energy_after += energy(i,atom->type[i],translation_molecule,coord); } } double energy_after_sum = 0.0; MPI_Allreduce(&energy_after,&energy_after_sum,1,MPI_DOUBLE,MPI_SUM,world); if (random_equal->uniform() < exp(beta*(energy_before_sum - energy_after_sum))) { for (int i = 0; i < nlocal; i++) { if (atom->molecule[i] == translation_molecule) { x[i][0] += com_displace[0]; x[i][1] += com_displace[1]; x[i][2] += com_displace[2]; } } domain->pbc(); comm->exchange(); atom->nghost = 0; comm->borders(); update_gas_atoms_list(); ntranslation_successes += 1.0; } } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ void FixGCMC::attempt_molecule_rotation() { nrotation_attempts += 1.0; if (ngas == 0) return; tagint rotation_molecule = pick_random_gas_molecule(); if (rotation_molecule == -1) return; double energy_before_sum = molecule_energy(rotation_molecule); int nlocal = atom->nlocal; int *mask = atom->mask; for (int i = 0; i < nlocal; i++) { if (atom->molecule[i] == rotation_molecule) { mask[i] |= molecule_group_bit; } else { mask[i] &= molecule_group_inversebit; } } double com[3]; com[0] = com[1] = com[2] = 0.0; group->xcm(molecule_group,gas_mass,com); double r[3],rotmat[3][3],quat[4]; r[0] = random_equal->uniform() - 0.5; r[1] = random_equal->uniform() - 0.5; r[2] = random_equal->uniform() - 0.5; double theta = random_equal->uniform() * max_rotation_angle; MathExtra::norm3(r); MathExtra::axisangle_to_quat(r,theta,quat); MathExtra::quat_to_mat(quat,rotmat); double **x = atom->x; imageint *image = atom->image; double energy_after = 0.0; int n = 0; for (int i = 0; i < nlocal; i++) { if (mask[i] & molecule_group_bit) { double xtmp[3]; domain->unmap(x[i],image[i],xtmp); xtmp[0] -= com[0]; xtmp[1] -= com[1]; xtmp[2] -= com[2]; MathExtra::matvec(rotmat,xtmp,atom_coord[n]); atom_coord[n][0] += com[0]; atom_coord[n][1] += com[1]; atom_coord[n][2] += com[2]; xtmp[0] = atom_coord[n][0]; xtmp[1] = atom_coord[n][1]; xtmp[2] = atom_coord[n][2]; domain->remap(xtmp); energy_after += energy(i,atom->type[i],rotation_molecule,xtmp); n++; } } double energy_after_sum = 0.0; MPI_Allreduce(&energy_after,&energy_after_sum,1,MPI_DOUBLE,MPI_SUM,world); if (random_equal->uniform() < exp(beta*(energy_before_sum - energy_after_sum))) { int n = 0; for (int i = 0; i < nlocal; i++) { if (mask[i] & molecule_group_bit) { image[i] = imagetmp; x[i][0] = atom_coord[n][0]; x[i][1] = atom_coord[n][1]; x[i][2] = atom_coord[n][2]; domain->remap(x[i],image[i]); n++; } } domain->pbc(); comm->exchange(); atom->nghost = 0; comm->borders(); update_gas_atoms_list(); nrotation_successes += 1.0; } } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ void FixGCMC::attempt_molecule_deletion() { ndeletion_attempts += 1.0; if (ngas == 0) return; tagint deletion_molecule = pick_random_gas_molecule(); if (deletion_molecule == -1) return; double deletion_energy_sum = molecule_energy(deletion_molecule); if (random_equal->uniform() < ngas*exp(beta*deletion_energy_sum)/(zz*volume*natoms_per_molecule)) { int i = 0; while (i < atom->nlocal) { if (atom->molecule[i] == deletion_molecule) { atom->avec->copy(atom->nlocal-1,i,1); atom->nlocal--; } else i++; } atom->natoms -= natoms_per_molecule; if (atom->map_style) atom->map_init(); atom->nghost = 0; comm->borders(); update_gas_atoms_list(); ndeletion_successes += 1.0; } } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ void FixGCMC::attempt_molecule_insertion() { ninsertion_attempts += 1.0; double com_coord[3]; if (regionflag) { int region_attempt = 0; com_coord[0] = region_xlo + random_equal->uniform() * (region_xhi-region_xlo); com_coord[1] = region_ylo + random_equal->uniform() * (region_yhi-region_ylo); com_coord[2] = region_zlo + random_equal->uniform() * (region_zhi-region_zlo); while (domain->regions[iregion]->match(com_coord[0],com_coord[1], com_coord[2]) == 0) { com_coord[0] = region_xlo + random_equal->uniform() * (region_xhi-region_xlo); com_coord[1] = region_ylo + random_equal->uniform() * (region_yhi-region_ylo); com_coord[2] = region_zlo + random_equal->uniform() * (region_zhi-region_zlo); region_attempt++; if (region_attempt >= max_region_attempts) return; } } else { com_coord[0] = xlo + random_equal->uniform() * (xhi-xlo); com_coord[1] = ylo + random_equal->uniform() * (yhi-ylo); com_coord[2] = zlo + random_equal->uniform() * (zhi-zlo); } double r[3],rotmat[3][3],quat[4]; r[0] = random_equal->uniform() - 0.5; r[1] = random_equal->uniform() - 0.5; r[2] = random_equal->uniform() - 0.5; double theta = random_equal->uniform() * MY_2PI; MathExtra::norm3(r); MathExtra::axisangle_to_quat(r,theta,quat); MathExtra::quat_to_mat(quat,rotmat); double insertion_energy = 0.0; bool procflag[natoms_per_molecule]; for (int i = 0; i < natoms_per_molecule; i++) { MathExtra::matvec(rotmat,onemols[imol]->x[i],atom_coord[i]); atom_coord[i][0] += com_coord[0]; atom_coord[i][1] += com_coord[1]; atom_coord[i][2] += com_coord[2]; double xtmp[3]; xtmp[0] = atom_coord[i][0]; xtmp[1] = atom_coord[i][1]; xtmp[2] = atom_coord[i][2]; domain->remap(xtmp); procflag[i] = false; if (xtmp[0] >= sublo[0] && xtmp[0] < subhi[0] && xtmp[1] >= sublo[1] && xtmp[1] < subhi[1] && xtmp[2] >= sublo[2] && xtmp[2] < subhi[2]) { procflag[i] = true; insertion_energy += energy(-1,onemols[imol]->type[i],-1,xtmp); } } double insertion_energy_sum = 0.0; MPI_Allreduce(&insertion_energy,&insertion_energy_sum,1, MPI_DOUBLE,MPI_SUM,world); if (random_equal->uniform() < zz*volume*natoms_per_molecule* exp(-beta*insertion_energy_sum)/(ngas + natoms_per_molecule)) { tagint maxmol = 0; for (int i = 0; i < atom->nlocal; i++) maxmol = MAX(maxmol,atom->molecule[i]); tagint maxmol_all; MPI_Allreduce(&maxmol,&maxmol_all,1,MPI_LMP_TAGINT,MPI_MAX,world); maxmol_all++; if (maxmol_all >= MAXTAGINT) error->all(FLERR,"Fix gcmc ran out of available molecule IDs"); tagint maxtag = 0; for (int i = 0; i < atom->nlocal; i++) maxtag = MAX(maxtag,atom->tag[i]); tagint maxtag_all; MPI_Allreduce(&maxtag,&maxtag_all,1,MPI_LMP_TAGINT,MPI_MAX,world); - int nfix = modify->nfix; - Fix **fix = modify->fix; - int nlocalprev = atom->nlocal; double vnew[3]; vnew[0] = random_unequal->gaussian()*sigma; vnew[1] = random_unequal->gaussian()*sigma; vnew[2] = random_unequal->gaussian()*sigma; for (int i = 0; i < natoms_per_molecule; i++) { if (procflag[i]) { atom->avec->create_atom(ngcmc_type+onemols[imol]->type[i],atom_coord[i]); int m = atom->nlocal - 1; atom->mask[m] = 1 | groupbit; atom->image[m] = imagetmp; domain->remap(atom->x[m],atom->image[m]); atom->molecule[m] = maxmol_all; if (maxtag_all+i+1 >= MAXTAGINT) error->all(FLERR,"Fix gcmc ran out of available atom IDs"); atom->tag[m] = maxtag_all + i + 1; atom->v[m][0] = vnew[0]; atom->v[m][1] = vnew[1]; atom->v[m][2] = vnew[2]; atom->add_molecule_atom(onemols[imol],i,m,maxtag_all); modify->create_attribute(m); } } if (shakeflag) fixshake->set_molecule(nlocalprev,maxtag_all,imol,com_coord,vnew,quat); atom->natoms += natoms_per_molecule; if (atom->natoms < 0 || atom->natoms > MAXBIGINT) error->all(FLERR,"Too many total atoms"); atom->nbonds += onemols[imol]->nbonds; atom->nangles += onemols[imol]->nangles; atom->ndihedrals += onemols[imol]->ndihedrals; atom->nimpropers += onemols[imol]->nimpropers; if (atom->map_style) atom->map_init(); atom->nghost = 0; comm->borders(); update_gas_atoms_list(); ninsertion_successes += 1.0; } } /* ---------------------------------------------------------------------- compute particle's interaction energy with the rest of the system ------------------------------------------------------------------------- */ double FixGCMC::energy(int i, int itype, tagint imolecule, double *coord) { double delx,dely,delz,rsq; double **x = atom->x; int *type = atom->type; tagint *molecule = atom->molecule; int nall = atom->nlocal + atom->nghost; pair = force->pair; cutsq = force->pair->cutsq; double fpair = 0.0; double factor_coul = 1.0; double factor_lj = 1.0; double total_energy = 0.0; for (int j = 0; j < nall; j++) { if (i == j) continue; if (mode == MOLECULE) if (imolecule == molecule[j]) continue; delx = coord[0] - x[j][0]; dely = coord[1] - x[j][1]; delz = coord[2] - x[j][2]; rsq = delx*delx + dely*dely + delz*delz; int jtype = type[j]; if (rsq < cutsq[itype][jtype]) total_energy += pair->single(i,j,itype,jtype,rsq,factor_coul,factor_lj,fpair); } return total_energy; } /* ---------------------------------------------------------------------- compute the energy of the given gas molecule in its current position sum across all procs that own atoms of the given molecule ------------------------------------------------------------------------- */ double FixGCMC::molecule_energy(tagint gas_molecule_id) { double mol_energy = 0.0; for (int i = 0; i < atom->nlocal; i++) if (atom->molecule[i] == gas_molecule_id) { mol_energy += energy(i,atom->type[i],gas_molecule_id,atom->x[i]); } double mol_energy_sum = 0.0; MPI_Allreduce(&mol_energy,&mol_energy_sum,1,MPI_DOUBLE,MPI_SUM,world); return mol_energy_sum; } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ void FixGCMC::attempt_atomic_translation_full() { ntranslation_attempts += 1.0; if (ngas == 0) return; double energy_before = energy_stored; int i = pick_random_gas_atom(); double **x = atom->x; double xtmp[3]; xtmp[0] = xtmp[1] = xtmp[2] = 0.0; tagint tmptag = -1; if (i >= 0) { double rsq = 1.1; double rx,ry,rz; rx = ry = rz = 0.0; double coord[3]; while (rsq > 1.0) { rx = 2*random_unequal->uniform() - 1.0; ry = 2*random_unequal->uniform() - 1.0; rz = 2*random_unequal->uniform() - 1.0; rsq = rx*rx + ry*ry + rz*rz; } coord[0] = x[i][0] + displace*rx; coord[1] = x[i][1] + displace*ry; coord[2] = x[i][2] + displace*rz; if (regionflag) { while (domain->regions[iregion]->match(coord[0],coord[1],coord[2]) == 0) { rsq = 1.1; while (rsq > 1.0) { rx = 2*random_unequal->uniform() - 1.0; ry = 2*random_unequal->uniform() - 1.0; rz = 2*random_unequal->uniform() - 1.0; rsq = rx*rx + ry*ry + rz*rz; } coord[0] = x[i][0] + displace*rx; coord[1] = x[i][1] + displace*ry; coord[2] = x[i][2] + displace*rz; } } xtmp[0] = x[i][0]; xtmp[1] = x[i][1]; xtmp[2] = x[i][2]; x[i][0] = coord[0]; x[i][1] = coord[1]; x[i][2] = coord[2]; tmptag = atom->tag[i]; } double energy_after = energy_full(); if (random_equal->uniform() < exp(beta*(energy_before - energy_after))) { energy_stored = energy_after; ntranslation_successes += 1.0; } else { tagint tmptag_all; MPI_Allreduce(&tmptag,&tmptag_all,1,MPI_LMP_TAGINT,MPI_MAX,world); double xtmp_all[3]; MPI_Allreduce(&xtmp,&xtmp_all,3,MPI_DOUBLE,MPI_SUM,world); for (int i = 0; i < atom->nlocal; i++) { if (tmptag_all == atom->tag[i]) { x[i][0] = xtmp_all[0]; x[i][1] = xtmp_all[1]; x[i][2] = xtmp_all[2]; } } energy_stored = energy_before; } update_gas_atoms_list(); } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ void FixGCMC::attempt_atomic_deletion_full() { double q_tmp; ndeletion_attempts += 1.0; if (ngas == 0) return; double energy_before = energy_stored; int i = pick_random_gas_atom(); if (i >= 0) { atom->mask[i] |= exclusion_group_bit; if (atom->q_flag) { q_tmp = atom->q[i]; atom->q[i] = 0.0; } } double energy_after = energy_full(); if (random_equal->uniform() < ngas*exp(beta*(energy_before - energy_after))/(zz*volume)) { if (i >= 0) { atom->avec->copy(atom->nlocal-1,i,1); atom->nlocal--; } atom->natoms--; if (atom->map_style) atom->map_init(); ndeletion_successes += 1.0; energy_stored = energy_after; } else { if (i >= 0) { atom->mask[i] &= exclusion_group_inversebit; if (atom->q_flag) atom->q[i] = q_tmp; } energy_stored = energy_before; } update_gas_atoms_list(); } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ void FixGCMC::attempt_atomic_insertion_full() { ninsertion_attempts += 1.0; double energy_before = energy_stored; double coord[3]; if (regionflag) { int region_attempt = 0; coord[0] = region_xlo + random_equal->uniform() * (region_xhi-region_xlo); coord[1] = region_ylo + random_equal->uniform() * (region_yhi-region_ylo); coord[2] = region_zlo + random_equal->uniform() * (region_zhi-region_zlo); while (domain->regions[iregion]->match(coord[0],coord[1],coord[2]) == 0) { coord[0] = region_xlo + random_equal->uniform() * (region_xhi-region_xlo); coord[1] = region_ylo + random_equal->uniform() * (region_yhi-region_ylo); coord[2] = region_zlo + random_equal->uniform() * (region_zhi-region_zlo); region_attempt++; if (region_attempt >= max_region_attempts) return; } } else { coord[0] = xlo + random_equal->uniform() * (xhi-xlo); coord[1] = ylo + random_equal->uniform() * (yhi-ylo); coord[2] = zlo + random_equal->uniform() * (zhi-zlo); } int proc_flag = 0; if (coord[0] >= sublo[0] && coord[0] < subhi[0] && coord[1] >= sublo[1] && coord[1] < subhi[1] && coord[2] >= sublo[2] && coord[2] < subhi[2]) { proc_flag = 1; atom->avec->create_atom(ngcmc_type,coord); int m = atom->nlocal - 1; atom->mask[m] = 1 | groupbit; atom->v[m][0] = random_unequal->gaussian()*sigma; atom->v[m][1] = random_unequal->gaussian()*sigma; atom->v[m][2] = random_unequal->gaussian()*sigma; if (charge_flag) atom->q[m] = charge; modify->create_attribute(m); } atom->natoms++; if (atom->tag_enable) { atom->tag_extend(); if (atom->map_style) atom->map_init(); } atom->nghost = 0; comm->borders(); double energy_after = energy_full(); if (random_equal->uniform() < zz*volume*exp(beta*(energy_before - energy_after))/(ngas+1)) { ninsertion_successes += 1.0; energy_stored = energy_after; } else { atom->natoms--; if (proc_flag) atom->nlocal--; energy_stored = energy_before; } update_gas_atoms_list(); } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ void FixGCMC::attempt_molecule_translation_full() { ntranslation_attempts += 1.0; if (ngas == 0) return; tagint translation_molecule = pick_random_gas_molecule(); if (translation_molecule == -1) return; double energy_before = energy_stored; double **x = atom->x; double rx,ry,rz; double com_displace[3],coord[3]; double rsq = 1.1; while (rsq > 1.0) { rx = 2*random_equal->uniform() - 1.0; ry = 2*random_equal->uniform() - 1.0; rz = 2*random_equal->uniform() - 1.0; rsq = rx*rx + ry*ry + rz*rz; } com_displace[0] = displace*rx; com_displace[1] = displace*ry; com_displace[2] = displace*rz; int nlocal = atom->nlocal; if (regionflag) { int *mask = atom->mask; for (int i = 0; i < nlocal; i++) { if (atom->molecule[i] == translation_molecule) { mask[i] |= molecule_group_bit; } else { mask[i] &= molecule_group_inversebit; } } double com[3]; com[0] = com[1] = com[2] = 0.0; group->xcm(molecule_group,gas_mass,com); coord[0] = com[0] + displace*rx; coord[1] = com[1] + displace*ry; coord[2] = com[2] + displace*rz; while (domain->regions[iregion]->match(coord[0],coord[1],coord[2]) == 0) { rsq = 1.1; while (rsq > 1.0) { rx = 2*random_equal->uniform() - 1.0; ry = 2*random_equal->uniform() - 1.0; rz = 2*random_equal->uniform() - 1.0; rsq = rx*rx + ry*ry + rz*rz; } coord[0] = com[0] + displace*rx; coord[1] = com[1] + displace*ry; coord[2] = com[2] + displace*rz; } com_displace[0] = displace*rx; com_displace[1] = displace*ry; com_displace[2] = displace*rz; } for (int i = 0; i < nlocal; i++) { if (atom->molecule[i] == translation_molecule) { x[i][0] += com_displace[0]; x[i][1] += com_displace[1]; x[i][2] += com_displace[2]; } } double energy_after = energy_full(); if (random_equal->uniform() < exp(beta*(energy_before - energy_after))) { ntranslation_successes += 1.0; energy_stored = energy_after; } else { energy_stored = energy_before; for (int i = 0; i < nlocal; i++) { if (atom->molecule[i] == translation_molecule) { x[i][0] -= com_displace[0]; x[i][1] -= com_displace[1]; x[i][2] -= com_displace[2]; } } } update_gas_atoms_list(); } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ void FixGCMC::attempt_molecule_rotation_full() { nrotation_attempts += 1.0; if (ngas == 0) return; tagint rotation_molecule = pick_random_gas_molecule(); if (rotation_molecule == -1) return; double energy_before = energy_stored; int nlocal = atom->nlocal; int *mask = atom->mask; for (int i = 0; i < nlocal; i++) { if (atom->molecule[i] == rotation_molecule) { mask[i] |= molecule_group_bit; } else { mask[i] &= molecule_group_inversebit; } } double com[3]; com[0] = com[1] = com[2] = 0.0; group->xcm(molecule_group,gas_mass,com); double r[3],rotmat[3][3],quat[4]; r[0] = random_equal->uniform() - 0.5; r[1] = random_equal->uniform() - 0.5; r[2] = random_equal->uniform() - 0.5; double theta = random_equal->uniform() * max_rotation_angle; MathExtra::norm3(r); MathExtra::axisangle_to_quat(r,theta,quat); MathExtra::quat_to_mat(quat,rotmat); double **x = atom->x; imageint *image = atom->image; imageint image_orig[natoms_per_molecule]; int n = 0; for (int i = 0; i < nlocal; i++) { if (mask[i] & molecule_group_bit) { atom_coord[n][0] = x[i][0]; atom_coord[n][1] = x[i][1]; atom_coord[n][2] = x[i][2]; image_orig[n] = image[i]; double xtmp[3]; domain->unmap(x[i],image[i],xtmp); xtmp[0] -= com[0]; xtmp[1] -= com[1]; xtmp[2] -= com[2]; MathExtra::matvec(rotmat,xtmp,x[i]); x[i][0] += com[0]; x[i][1] += com[1]; x[i][2] += com[2]; image[i] = imagetmp; domain->remap(x[i],image[i]); n++; } } double energy_after = energy_full(); if (random_equal->uniform() < exp(beta*(energy_before - energy_after))) { nrotation_successes += 1.0; energy_stored = energy_after; } else { energy_stored = energy_before; int n = 0; for (int i = 0; i < nlocal; i++) { if (mask[i] & molecule_group_bit) { x[i][0] = atom_coord[n][0]; x[i][1] = atom_coord[n][1]; x[i][2] = atom_coord[n][2]; image[i] = image_orig[n]; n++; } } } update_gas_atoms_list(); } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ void FixGCMC::attempt_molecule_deletion_full() { ndeletion_attempts += 1.0; if (ngas == 0) return; tagint deletion_molecule = pick_random_gas_molecule(); if (deletion_molecule == -1) return; double energy_before = energy_stored; int m = 0; double q_tmp[natoms_per_molecule]; for (int i = 0; i < atom->nlocal; i++) { if (atom->molecule[i] == deletion_molecule) { atom->mask[i] |= exclusion_group_bit; if (atom->q_flag) { q_tmp[m] = atom->q[i]; m++; atom->q[i] = 0.0; } } } double energy_after = energy_full(); if (random_equal->uniform() < ngas*exp(beta*(energy_before - energy_after))/(zz*volume*natoms_per_molecule)) { int i = 0; while (i < atom->nlocal) { if (atom->molecule[i] == deletion_molecule) { atom->avec->copy(atom->nlocal-1,i,1); atom->nlocal--; } else i++; } atom->natoms -= natoms_per_molecule; if (atom->map_style) atom->map_init(); ndeletion_successes += 1.0; energy_stored = energy_after; } else { energy_stored = energy_before; int m = 0; for (int i = 0; i < atom->nlocal; i++) { if (atom->molecule[i] == deletion_molecule) { atom->mask[i] &= exclusion_group_inversebit; if (atom->q_flag) { atom->q[i] = q_tmp[m]; m++; } } } } update_gas_atoms_list(); } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ void FixGCMC::attempt_molecule_insertion_full() { ninsertion_attempts += 1.0; double energy_before = energy_stored; tagint maxmol = 0; for (int i = 0; i < atom->nlocal; i++) maxmol = MAX(maxmol,atom->molecule[i]); tagint maxmol_all; MPI_Allreduce(&maxmol,&maxmol_all,1,MPI_LMP_TAGINT,MPI_MAX,world); maxmol_all++; if (maxmol_all >= MAXTAGINT) error->all(FLERR,"Fix gcmc ran out of available molecule IDs"); int insertion_molecule = maxmol_all; tagint maxtag = 0; for (int i = 0; i < atom->nlocal; i++) maxtag = MAX(maxtag,atom->tag[i]); tagint maxtag_all; MPI_Allreduce(&maxtag,&maxtag_all,1,MPI_LMP_TAGINT,MPI_MAX,world); - int nfix = modify->nfix; - Fix **fix = modify->fix; - int nlocalprev = atom->nlocal; double com_coord[3]; if (regionflag) { int region_attempt = 0; com_coord[0] = region_xlo + random_equal->uniform() * (region_xhi-region_xlo); com_coord[1] = region_ylo + random_equal->uniform() * (region_yhi-region_ylo); com_coord[2] = region_zlo + random_equal->uniform() * (region_zhi-region_zlo); while (domain->regions[iregion]->match(com_coord[0],com_coord[1], com_coord[2]) == 0) { com_coord[0] = region_xlo + random_equal->uniform() * (region_xhi-region_xlo); com_coord[1] = region_ylo + random_equal->uniform() * (region_yhi-region_ylo); com_coord[2] = region_zlo + random_equal->uniform() * (region_zhi-region_zlo); region_attempt++; if (region_attempt >= max_region_attempts) return; } } else { com_coord[0] = xlo + random_equal->uniform() * (xhi-xlo); com_coord[1] = ylo + random_equal->uniform() * (yhi-ylo); com_coord[2] = zlo + random_equal->uniform() * (zhi-zlo); } double r[3],rotmat[3][3],quat[4]; r[0] = random_equal->uniform() - 0.5; r[1] = random_equal->uniform() - 0.5; r[2] = random_equal->uniform() - 0.5; double theta = random_equal->uniform() * MY_2PI; MathExtra::norm3(r); MathExtra::axisangle_to_quat(r,theta,quat); MathExtra::quat_to_mat(quat,rotmat); double vnew[3]; vnew[0] = random_unequal->gaussian()*sigma; vnew[1] = random_unequal->gaussian()*sigma; vnew[2] = random_unequal->gaussian()*sigma; for (int i = 0; i < natoms_per_molecule; i++) { double xtmp[3]; MathExtra::matvec(rotmat,onemols[imol]->x[i],xtmp); xtmp[0] += com_coord[0]; xtmp[1] += com_coord[1]; xtmp[2] += com_coord[2]; domain->remap(xtmp); if (xtmp[0] >= sublo[0] && xtmp[0] < subhi[0] && xtmp[1] >= sublo[1] && xtmp[1] < subhi[1] && xtmp[2] >= sublo[2] && xtmp[2] < subhi[2]) { atom->avec->create_atom(ngcmc_type+onemols[imol]->type[i],xtmp); int m = atom->nlocal - 1; atom->mask[m] = 1 | groupbit; atom->image[m] = imagetmp; domain->remap(atom->x[m],atom->image[m]); atom->molecule[m] = insertion_molecule; if (maxtag_all+i+1 >= MAXTAGINT) error->all(FLERR,"Fix gcmc ran out of available atom IDs"); atom->tag[m] = maxtag_all + i + 1; atom->v[m][0] = vnew[0]; atom->v[m][1] = vnew[1]; atom->v[m][2] = vnew[2]; atom->add_molecule_atom(onemols[imol],i,m,maxtag_all); modify->create_attribute(m); } } if (shakeflag) fixshake->set_molecule(nlocalprev,maxtag_all,imol,com_coord,vnew,quat); atom->natoms += natoms_per_molecule; if (atom->natoms < 0 || atom->natoms > MAXBIGINT) error->all(FLERR,"Too many total atoms"); atom->nbonds += onemols[imol]->nbonds; atom->nangles += onemols[imol]->nangles; atom->ndihedrals += onemols[imol]->ndihedrals; atom->nimpropers += onemols[imol]->nimpropers; if (atom->map_style) atom->map_init(); atom->nghost = 0; comm->borders(); double energy_after = energy_full(); if (random_equal->uniform() < zz*volume*natoms_per_molecule* exp(beta*(energy_before - energy_after))/(ngas + natoms_per_molecule)) { ninsertion_successes += 1.0; energy_stored = energy_after; } else { atom->nbonds -= onemols[imol]->nbonds; atom->nangles -= onemols[imol]->nangles; atom->ndihedrals -= onemols[imol]->ndihedrals; atom->nimpropers -= onemols[imol]->nimpropers; atom->natoms -= natoms_per_molecule; energy_stored = energy_before; int i = 0; while (i < atom->nlocal) { if (atom->molecule[i] == insertion_molecule) { atom->avec->copy(atom->nlocal-1,i,1); atom->nlocal--; } else i++; } } update_gas_atoms_list(); } /* ---------------------------------------------------------------------- compute system potential energy ------------------------------------------------------------------------- */ double FixGCMC::energy_full() { if (domain->triclinic) domain->x2lamda(atom->nlocal); domain->pbc(); comm->exchange(); comm->borders(); if (domain->triclinic) domain->lamda2x(atom->nlocal+atom->nghost); if (modify->n_pre_neighbor) modify->pre_neighbor(); neighbor->build(); int eflag = 1; int vflag = 0; if (modify->n_pre_force) modify->pre_force(vflag); if (force->pair) force->pair->compute(eflag,vflag); if (atom->molecular) { if (force->bond) force->bond->compute(eflag,vflag); if (force->angle) force->angle->compute(eflag,vflag); if (force->dihedral) force->dihedral->compute(eflag,vflag); if (force->improper) force->improper->compute(eflag,vflag); } if (force->kspace) force->kspace->compute(eflag,vflag); if (modify->n_post_force) modify->post_force(vflag); if (modify->n_end_of_step) modify->end_of_step(); update->eflag_global = update->ntimestep; double total_energy = c_pe->compute_scalar(); return total_energy; } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ int FixGCMC::pick_random_gas_atom() { int i = -1; int iwhichglobal = static_cast (ngas*random_equal->uniform()); if ((iwhichglobal >= ngas_before) && (iwhichglobal < ngas_before + ngas_local)) { int iwhichlocal = iwhichglobal - ngas_before; i = local_gas_list[iwhichlocal]; } return i; } /* ---------------------------------------------------------------------- ------------------------------------------------------------------------- */ tagint FixGCMC::pick_random_gas_molecule() { int iwhichglobal = static_cast (ngas*random_equal->uniform()); tagint gas_molecule_id = 0; if ((iwhichglobal >= ngas_before) && (iwhichglobal < ngas_before + ngas_local)) { int iwhichlocal = iwhichglobal - ngas_before; int i = local_gas_list[iwhichlocal]; gas_molecule_id = atom->molecule[i]; } tagint gas_molecule_id_all = 0; MPI_Allreduce(&gas_molecule_id,&gas_molecule_id_all,1, MPI_LMP_TAGINT,MPI_MAX,world); return gas_molecule_id_all; } /* ---------------------------------------------------------------------- update the list of gas atoms ------------------------------------------------------------------------- */ void FixGCMC::update_gas_atoms_list() { int nlocal = atom->nlocal; int *mask = atom->mask; tagint *molecule = atom->molecule; double **x = atom->x; if (nlocal > gcmc_nmax) { memory->sfree(local_gas_list); gcmc_nmax = atom->nmax; local_gas_list = (int *) memory->smalloc(gcmc_nmax*sizeof(int), "GCMC:local_gas_list"); } ngas_local = 0; if (regionflag) { if (mode == MOLECULE) { tagint maxmol = 0; for (int i = 0; i < nlocal; i++) maxmol = MAX(maxmol,molecule[i]); tagint maxmol_all; MPI_Allreduce(&maxmol,&maxmol_all,1,MPI_LMP_TAGINT,MPI_MAX,world); double comx[maxmol_all]; double comy[maxmol_all]; double comz[maxmol_all]; for (int imolecule = 0; imolecule < maxmol_all; imolecule++) { for (int i = 0; i < nlocal; i++) { if (molecule[i] == imolecule) { mask[i] |= molecule_group_bit; } else { mask[i] &= molecule_group_inversebit; } } double com[3]; com[0] = com[1] = com[2] = 0.0; group->xcm(molecule_group,gas_mass,com); comx[imolecule] = com[0]; comy[imolecule] = com[1]; comz[imolecule] = com[2]; } for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { if (domain->regions[iregion]->match(comx[molecule[i]], comy[molecule[i]],comz[molecule[i]]) == 1) { local_gas_list[ngas_local] = i; ngas_local++; } } } } else { for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { if (domain->regions[iregion]->match(x[i][0],x[i][1],x[i][2]) == 1) { local_gas_list[ngas_local] = i; ngas_local++; } } } } } else { for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { local_gas_list[ngas_local] = i; ngas_local++; } } } MPI_Allreduce(&ngas_local,&ngas,1,MPI_INT,MPI_SUM,world); MPI_Scan(&ngas_local,&ngas_before,1,MPI_INT,MPI_SUM,world); ngas_before -= ngas_local; } /* ---------------------------------------------------------------------- return acceptance ratios ------------------------------------------------------------------------- */ double FixGCMC::compute_vector(int n) { if (n == 0) return ntranslation_attempts; if (n == 1) return ntranslation_successes; if (n == 2) return ninsertion_attempts; if (n == 3) return ninsertion_successes; if (n == 4) return ndeletion_attempts; if (n == 5) return ndeletion_successes; if (n == 6) return nrotation_attempts; if (n == 7) return nrotation_successes; return 0.0; } /* ---------------------------------------------------------------------- memory usage of local atom-based arrays ------------------------------------------------------------------------- */ double FixGCMC::memory_usage() { double bytes = gcmc_nmax * sizeof(int); return bytes; } /* ---------------------------------------------------------------------- pack entire state of Fix into one write ------------------------------------------------------------------------- */ void FixGCMC::write_restart(FILE *fp) { int n = 0; double list[4]; list[n++] = random_equal->state(); list[n++] = random_unequal->state(); list[n++] = next_reneighbor; if (comm->me == 0) { int size = n * sizeof(double); fwrite(&size,sizeof(int),1,fp); fwrite(list,sizeof(double),n,fp); } } /* ---------------------------------------------------------------------- use state info from restart file to restart the Fix ------------------------------------------------------------------------- */ void FixGCMC::restart(char *buf) { int n = 0; double *list = (double *) buf; seed = static_cast (list[n++]); random_equal->reset(seed); seed = static_cast (list[n++]); random_unequal->reset(seed); next_reneighbor = static_cast (list[n++]); } diff --git a/src/MISC/fix_deposit.cpp b/src/MISC/fix_deposit.cpp index c9f29844b..1cf78d2dc 100644 --- a/src/MISC/fix_deposit.cpp +++ b/src/MISC/fix_deposit.cpp @@ -1,803 +1,803 @@ /* ---------------------------------------------------------------------- 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_deposit.h" #include "atom.h" #include "atom_vec.h" #include "molecule.h" #include "force.h" #include "update.h" #include "modify.h" #include "fix.h" #include "comm.h" #include "domain.h" #include "lattice.h" #include "region.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{LAYOUT_UNIFORM,LAYOUT_NONUNIFORM,LAYOUT_TILED}; // several files #define EPSILON 1.0e6 /* ---------------------------------------------------------------------- */ FixDeposit::FixDeposit(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { if (narg < 7) error->all(FLERR,"Illegal fix deposit command"); restart_global = 1; time_depend = 1; // required args ninsert = force->inumeric(FLERR,arg[3]); ntype = force->inumeric(FLERR,arg[4]); nfreq = force->inumeric(FLERR,arg[5]); seed = force->inumeric(FLERR,arg[6]); if (seed <= 0) error->all(FLERR,"Illegal fix deposit command"); // read options from end of input line options(narg-7,&arg[7]); // error check on type if (mode == ATOM && (ntype <= 0 || ntype > atom->ntypes)) error->all(FLERR,"Invalid atom type in fix deposit command"); // error checks on region and its extent being inside simulation box if (iregion == -1) error->all(FLERR,"Must specify a region in fix deposit"); if (domain->regions[iregion]->bboxflag == 0) error->all(FLERR,"Fix deposit region does not support a bounding box"); if (domain->regions[iregion]->dynamic_check()) error->all(FLERR,"Fix deposit region cannot be dynamic"); xlo = domain->regions[iregion]->extent_xlo; xhi = domain->regions[iregion]->extent_xhi; ylo = domain->regions[iregion]->extent_ylo; yhi = domain->regions[iregion]->extent_yhi; zlo = domain->regions[iregion]->extent_zlo; zhi = domain->regions[iregion]->extent_zhi; if (domain->triclinic == 0) { 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,"Deposition region extends outside simulation box"); } else { if (xlo < domain->boxlo_bound[0] || xhi > domain->boxhi_bound[0] || ylo < domain->boxlo_bound[1] || yhi > domain->boxhi_bound[1] || zlo < domain->boxlo_bound[2] || zhi > domain->boxhi_bound[2]) error->all(FLERR,"Deposition region extends outside simulation box"); } // error check and further setup for mode = MOLECULE if (atom->tag_enable == 0) error->all(FLERR,"Cannot use fix_deposit unless atoms have IDs"); if (mode == MOLECULE) { for (int i = 0; i < nmol; i++) { if (onemols[i]->xflag == 0) error->all(FLERR,"Fix deposit molecule must have coordinates"); if (onemols[i]->typeflag == 0) error->all(FLERR,"Fix deposit 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 deposit mol command"); if (atom->molecular == 2 && onemols != atom->avec->onemols) error->all(FLERR,"Fix deposit molecule template ID must be same " "as atom_style template ID"); onemols[i]->check_attributes(0); // fix deposit uses geoemetric center of molecule for insertion onemols[i]->compute_center(); } } if (rigidflag && mode == ATOM) error->all(FLERR,"Cannot use fix deposit rigid and not molecule"); if (shakeflag && mode == ATOM) error->all(FLERR,"Cannot use fix deposit shake and not molecule"); if (rigidflag && shakeflag) error->all(FLERR,"Cannot use fix deposit 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,3,"deposit:coords"); memory->create(imageflags,natom_max,"deposit:imageflags"); // setup scaling double xscale,yscale,zscale; if (scaleflag) { xscale = domain->lattice->xlattice; yscale = domain->lattice->ylattice; zscale = domain->lattice->zlattice; } else xscale = yscale = zscale = 1.0; // apply scaling to all input parameters with dist/vel units if (domain->dimension == 2) { lo *= yscale; hi *= yscale; rate *= yscale; } else { lo *= zscale; hi *= zscale; rate *= zscale; } deltasq *= xscale*xscale; nearsq *= xscale*xscale; vxlo *= xscale; vxhi *= xscale; vylo *= yscale; vyhi *= yscale; vzlo *= zscale; vzhi *= zscale; tx *= xscale; ty *= yscale; tz *= zscale; // 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); // set up reneighboring force_reneighbor = 1; next_reneighbor = update->ntimestep + 1; nfirst = next_reneighbor; ninserted = 0; } /* ---------------------------------------------------------------------- */ FixDeposit::~FixDeposit() { delete random; delete [] molfrac; delete [] idrigid; delete [] idshake; delete [] idregion; memory->destroy(coords); memory->destroy(imageflags); } /* ---------------------------------------------------------------------- */ int FixDeposit::setmask() { int mask = 0; mask |= PRE_EXCHANGE; return mask; } /* ---------------------------------------------------------------------- */ void FixDeposit::init() { // set index and check validity of region iregion = domain->find_region(idregion); if (iregion == -1) error->all(FLERR,"Region ID for fix deposit does not exist"); // 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 deposit 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 deposit shake fix does not exist"); fixshake = modify->fix[ifix]; int tmp; if (onemols != (Molecule **) fixshake->extract("onemol",tmp)) error->all(FLERR,"Fix deposit and fix shake not using " "same molecule template ID"); } } /* ---------------------------------------------------------------------- perform particle insertion ------------------------------------------------------------------------- */ void FixDeposit::pre_exchange() { - int i,j,m,n,nlocalprev,imol,natom,flag,flagall; + int i,m,n,nlocalprev,imol,natom,flag,flagall; double coord[3],lamda[3],delx,dely,delz,rsq; double r[3],vnew[3],rotmat[3][3],quat[4]; double *newcoord; // just return if should not be called on this timestep if (next_reneighbor != update->ntimestep) return; // compute current offset = bottom of insertion volume double offset = 0.0; if (rateflag) offset = (update->ntimestep - nfirst) * update->dt * rate; double *sublo,*subhi; if (domain->triclinic == 0) { sublo = domain->sublo; subhi = domain->subhi; } else { sublo = domain->sublo_lamda; subhi = domain->subhi_lamda; } // find current max atom and molecule IDs if necessary if (!idnext) find_maxid(); // attempt an insertion until successful int dimension = domain->dimension; int success = 0; int attempt = 0; while (attempt < maxattempt) { attempt++; // choose random position for new particle within region coord[0] = xlo + random->uniform() * (xhi-xlo); coord[1] = ylo + random->uniform() * (yhi-ylo); coord[2] = zlo + random->uniform() * (zhi-zlo); while (domain->regions[iregion]->match(coord[0],coord[1],coord[2]) == 0) { coord[0] = xlo + random->uniform() * (xhi-xlo); coord[1] = ylo + random->uniform() * (yhi-ylo); coord[2] = zlo + random->uniform() * (zhi-zlo); } // adjust vertical coord by offset if (dimension == 2) coord[1] += offset; else coord[2] += offset; // if global, reset vertical coord to be lo-hi above highest atom // if local, reset vertical coord to be lo-hi above highest "nearby" atom // local computation computes lateral distance between 2 particles w/ PBC // when done, have final coord of atom or center pt of molecule if (globalflag || localflag) { int dim; double max,maxall,delx,dely,delz,rsq; if (dimension == 2) { dim = 1; max = domain->boxlo[1]; } else { dim = 2; max = domain->boxlo[2]; } double **x = atom->x; int nlocal = atom->nlocal; for (i = 0; i < nlocal; i++) { if (localflag) { delx = coord[0] - x[i][0]; dely = coord[1] - x[i][1]; delz = 0.0; domain->minimum_image(delx,dely,delz); if (dimension == 2) rsq = delx*delx; else rsq = delx*delx + dely*dely; if (rsq > deltasq) continue; } if (x[i][dim] > max) max = x[i][dim]; } MPI_Allreduce(&max,&maxall,1,MPI_DOUBLE,MPI_MAX,world); if (dimension == 2) coord[1] = maxall + lo + random->uniform()*(hi-lo); else coord[2] = maxall + lo + random->uniform()*(hi-lo); } // coords = coords of all atoms // for molecule, perform random rotation around center pt // apply PBC so final coords are inside box // also modify image flags due to PBC if (mode == ATOM) { natom = 1; coords[0][0] = coord[0]; coords[0][1] = coord[1]; coords[0][2] = coord[2]; 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]; imageflags[i] = ((imageint) IMGMAX << IMG2BITS) | ((imageint) IMGMAX << IMGBITS) | IMGMAX; domain->remap(coords[i],imageflags[i]); } } // if distance to any inserted atom is less than near, try again // use minimum_image() to account for PBC double **x = atom->x; int nlocal = atom->nlocal; flag = 0; for (m = 0; m < natom; m++) { for (i = 0; i < nlocal; i++) { delx = coords[m][0] - x[i][0]; dely = coords[m][1] - x[i][1]; delz = coords[m][2] - x[i][2]; domain->minimum_image(delx,dely,delz); rsq = delx*delx + dely*dely + delz*delz; if (rsq < nearsq) flag = 1; } } MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_MAX,world); if (flagall) continue; // proceed with insertion nlocalprev = atom->nlocal; // choose random velocity for new particle // used for every atom in molecule vnew[0] = vxlo + random->uniform() * (vxhi-vxlo); vnew[1] = vylo + random->uniform() * (vyhi-vylo); vnew[2] = vzlo + random->uniform() * (vzhi-vzlo); // if target specified, change velocity vector accordingly if (targetflag) { double vel = sqrt(vnew[0]*vnew[0] + vnew[1]*vnew[1] + vnew[2]*vnew[2]); delx = tx - coord[0]; dely = ty - coord[1]; delz = tz - coord[2]; double rsq = delx*delx + dely*dely + delz*delz; if (rsq > 0.0) { double rinv = sqrt(1.0/rsq); vnew[0] = delx*rinv*vel; vnew[1] = dely*rinv*vel; vnew[2] = delz*rinv*vel; } } // 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 (domain->triclinic) { domain->x2lamda(coords[m],lamda); newcoord = lamda; } else 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]); 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 == MOLECULE) atom->add_molecule_atom(onemols[imol],m,n,maxtag_all); modify->create_attribute(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); // old code: unsuccessful if no proc performed insertion of an atom // don't think that check is necessary // if get this far, should always be succesful // would be hard to undo partial insertion for a molecule // better to check how many atoms could be inserted (w/out inserting) // then sum to insure all are inserted, before doing actual insertion // MPI_Allreduce(&flag,&success,1,MPI_INT,MPI_MAX,world); success = 1; break; } // warn if not successful b/c too many attempts if (!success && comm->me == 0) error->warning(FLERR,"Particle deposition was unsuccessful",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 (success) { atom->natoms += natom; if (atom->natoms < 0 || atom->natoms > MAXBIGINT) error->all(FLERR,"Too many total atoms"); if (mode == MOLECULE) { atom->nbonds += onemols[imol]->nbonds; atom->nangles += onemols[imol]->nangles; atom->ndihedrals += onemols[imol]->ndihedrals; atom->nimpropers += onemols[imol]->nimpropers; } maxtag_all += natom; if (maxtag_all >= MAXTAGINT) error->all(FLERR,"New atom IDs exceed maximum allowed ID"); if (mode == MOLECULE && atom->molecule_flag) maxmol_all++; if (atom->map_style) { atom->nghost = 0; atom->map_init(); atom->map_set(); } } // next timestep to insert // next_reneighbor = 0 if done if (success) ninserted++; 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 FixDeposit::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); } } /* ---------------------------------------------------------------------- parse optional parameters at end of input line ------------------------------------------------------------------------- */ void FixDeposit::options(int narg, char **arg) { // defaults iregion = -1; idregion = NULL; mode = ATOM; molfrac = NULL; rigidflag = 0; idrigid = NULL; shakeflag = 0; idshake = NULL; idnext = 0; globalflag = localflag = 0; lo = hi = deltasq = 0.0; nearsq = 0.0; maxattempt = 10; rateflag = 0; vxlo = vxhi = vylo = vyhi = vzlo = vzhi = 0.0; scaleflag = 1; targetflag = 0; int iarg = 0; while (iarg < narg) { if (strcmp(arg[iarg],"region") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix deposit command"); iregion = domain->find_region(arg[iarg+1]); if (iregion == -1) error->all(FLERR,"Region ID for fix deposit does not exist"); int n = strlen(arg[iarg+1]) + 1; idregion = new char[n]; strcpy(idregion,arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"mol") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix deposit command"); int imol = atom->find_molecule(arg[iarg+1]); if (imol == -1) error->all(FLERR,"Molecule template ID for fix deposit 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 deposit 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 deposit 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 deposit 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 deposit command"); iarg += 2; } else if (strcmp(arg[iarg],"global") == 0) { if (iarg+3 > narg) error->all(FLERR,"Illegal fix deposit command"); globalflag = 1; localflag = 0; lo = force->numeric(FLERR,arg[iarg+1]); hi = force->numeric(FLERR,arg[iarg+2]); iarg += 3; } else if (strcmp(arg[iarg],"local") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal fix deposit command"); localflag = 1; globalflag = 0; lo = force->numeric(FLERR,arg[iarg+1]); hi = force->numeric(FLERR,arg[iarg+2]); deltasq = force->numeric(FLERR,arg[iarg+3]) * force->numeric(FLERR,arg[iarg+3]); iarg += 4; } else if (strcmp(arg[iarg],"near") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix deposit command"); nearsq = force->numeric(FLERR,arg[iarg+1]) * force->numeric(FLERR,arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"attempt") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix deposit command"); maxattempt = force->inumeric(FLERR,arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"rate") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix deposit command"); rateflag = 1; rate = force->numeric(FLERR,arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"vx") == 0) { if (iarg+3 > narg) error->all(FLERR,"Illegal fix deposit command"); vxlo = force->numeric(FLERR,arg[iarg+1]); vxhi = force->numeric(FLERR,arg[iarg+2]); iarg += 3; } else if (strcmp(arg[iarg],"vy") == 0) { if (iarg+3 > narg) error->all(FLERR,"Illegal fix deposit command"); vylo = force->numeric(FLERR,arg[iarg+1]); vyhi = force->numeric(FLERR,arg[iarg+2]); iarg += 3; } else if (strcmp(arg[iarg],"vz") == 0) { if (iarg+3 > narg) error->all(FLERR,"Illegal fix deposit command"); vzlo = force->numeric(FLERR,arg[iarg+1]); vzhi = force->numeric(FLERR,arg[iarg+2]); iarg += 3; } else if (strcmp(arg[iarg],"units") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix deposit command"); if (strcmp(arg[iarg+1],"box") == 0) scaleflag = 0; else if (strcmp(arg[iarg+1],"lattice") == 0) scaleflag = 1; else error->all(FLERR,"Illegal fix deposit command"); iarg += 2; } else if (strcmp(arg[iarg],"target") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal fix deposit command"); tx = force->numeric(FLERR,arg[iarg+1]); ty = force->numeric(FLERR,arg[iarg+2]); tz = force->numeric(FLERR,arg[iarg+3]); targetflag = 1; iarg += 4; } else error->all(FLERR,"Illegal fix deposit command"); } } /* ---------------------------------------------------------------------- pack entire state of Fix into one write ------------------------------------------------------------------------- */ void FixDeposit::write_restart(FILE *fp) { int n = 0; double list[4]; list[n++] = random->state(); list[n++] = ninserted; list[n++] = nfirst; list[n++] = next_reneighbor; if (comm->me == 0) { int size = n * sizeof(double); fwrite(&size,sizeof(int),1,fp); fwrite(list,sizeof(double),n,fp); } } /* ---------------------------------------------------------------------- use state info from restart file to restart the Fix ------------------------------------------------------------------------- */ void FixDeposit::restart(char *buf) { int n = 0; double *list = (double *) buf; seed = static_cast (list[n++]); ninserted = static_cast (list[n++]); nfirst = static_cast (list[n++]); next_reneighbor = static_cast (list[n++]); random->reset(seed); } /* ---------------------------------------------------------------------- extract particle radius for atom type = itype ------------------------------------------------------------------------- */ void *FixDeposit::extract(const char *str, int &itype) { if (strcmp(str,"radius") == 0) { if (mode == ATOM) { if (itype == ntype) oneradius = 0.5; 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/QEQ/fix_qeq.cpp b/src/QEQ/fix_qeq.cpp index 9d5b82b54..b22136efd 100644 --- a/src/QEQ/fix_qeq.cpp +++ b/src/QEQ/fix_qeq.cpp @@ -1,750 +1,751 @@ /* ---------------------------------------------------------------------- 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: Ray Shan (Sandia) Based on fix qeq/reax by H. Metin Aktulga ------------------------------------------------------------------------- */ #include "math.h" #include "stdio.h" #include "stdlib.h" #include "string.h" #include "fix_qeq.h" #include "atom.h" #include "comm.h" #include "domain.h" #include "neighbor.h" #include "neigh_list.h" #include "neigh_request.h" #include "update.h" #include "force.h" #include "kspace.h" #include "group.h" #include "pair.h" #include "respa.h" #include "math_const.h" #include "memory.h" #include "error.h" using namespace LAMMPS_NS; using namespace MathConst; using namespace FixConst; #define MAXLINE 1024 /* ---------------------------------------------------------------------- */ FixQEq::FixQEq(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { if (narg < 8) error->all(FLERR,"Illegal fix qeq command"); nevery = force->inumeric(FLERR,arg[3]); cutoff = force->numeric(FLERR,arg[4]); tolerance = force->numeric(FLERR,arg[5]); maxiter = force->inumeric(FLERR,arg[6]); alpha = 0.20; swa = 0.0; swb = cutoff; shld = NULL; nlocal = n_cap = 0; nall = nmax = 0; m_fill = m_cap = 0; pack_flag = 0; s = NULL; t = NULL; nprev = 5; Hdia_inv = NULL; b_s = NULL; b_t = NULL; // CG p = NULL; q = NULL; r = NULL; d = NULL; // H matrix H.firstnbr = NULL; H.numnbrs = NULL; H.jlist = NULL; H.val = NULL; // others cutoff_sq = cutoff*cutoff; chizj = NULL; qf = NULL; q1 = NULL; q2 = NULL; comm_forward = comm_reverse = 1; // perform initial allocation of atom-based arrays // register with Atom class s_hist = t_hist = NULL; grow_arrays(atom->nmax); atom->add_callback(0); for( int i = 0; i < atom->nmax; i++ ) for (int j = 0; j < nprev; ++j ) s_hist[i][j] = t_hist[i][j] = atom->q[i]; read_file(arg[7]); } /* ---------------------------------------------------------------------- */ FixQEq::~FixQEq() { // unregister callbacks to this fix from Atom class atom->delete_callback(id,0); memory->destroy(s_hist); memory->destroy(t_hist); deallocate_storage(); deallocate_matrix(); memory->destroy(shld); memory->destroy(chi); memory->destroy(eta); memory->destroy(gamma); memory->destroy(zeta); memory->destroy(zcore); } /* ---------------------------------------------------------------------- */ int FixQEq::setmask() { int mask = 0; mask |= PRE_FORCE; mask |= MIN_PRE_FORCE; return mask; } /* ---------------------------------------------------------------------- */ void FixQEq::allocate_storage() { nmax = atom->nmax; memory->create(s,nmax,"qeq:s"); memory->create(t,nmax,"qeq:t"); memory->create(Hdia_inv,nmax,"qeq:Hdia_inv"); memory->create(b_s,nmax,"qeq:b_s"); memory->create(b_t,nmax,"qeq:b_t"); memory->create(p,nmax,"qeq:p"); memory->create(q,nmax,"qeq:q"); memory->create(r,nmax,"qeq:r"); memory->create(d,nmax,"qeq:d"); memory->create(chizj,nmax,"qeq:chizj"); memory->create(qf,nmax,"qeq:qf"); memory->create(q1,nmax,"qeq:q1"); memory->create(q2,nmax,"qeq:q2"); } /* ---------------------------------------------------------------------- */ void FixQEq::deallocate_storage() { memory->destroy(s); memory->destroy(t); memory->destroy( Hdia_inv ); memory->destroy( b_s ); memory->destroy( b_t ); memory->destroy( p ); memory->destroy( q ); memory->destroy( r ); memory->destroy( d ); memory->destroy( chizj ); memory->destroy( qf ); memory->destroy( q1 ); memory->destroy( q2 ); } /* ---------------------------------------------------------------------- */ void FixQEq::reallocate_storage() { deallocate_storage(); allocate_storage(); init_storage(); } /* ---------------------------------------------------------------------- */ void FixQEq::allocate_matrix() { int i,ii,inum,m; int *ilist, *numneigh; int mincap; double safezone; mincap = MIN_CAP; safezone = SAFE_ZONE; nlocal = atom->nlocal; n_cap = MAX( (int)(nlocal * safezone), mincap ); nall = atom->nlocal + atom->nghost; // determine the total space for the H matrix inum = list->inum; ilist = list->ilist; numneigh = list->numneigh; m = 0; for( ii = 0; ii < inum; ii++ ) { i = ilist[ii]; m += numneigh[i]; } m_cap = MAX( (int)(m * safezone), mincap * MIN_NBRS ); H.n = n_cap; H.m = m_cap; memory->create(H.firstnbr,n_cap,"qeq:H.firstnbr"); memory->create(H.numnbrs,n_cap,"qeq:H.numnbrs"); memory->create(H.jlist,m_cap,"qeq:H.jlist"); memory->create(H.val,m_cap,"qeq:H.val"); } /* ---------------------------------------------------------------------- */ void FixQEq::deallocate_matrix() { memory->destroy( H.firstnbr ); memory->destroy( H.numnbrs ); memory->destroy( H.jlist ); memory->destroy( H.val ); } /* ---------------------------------------------------------------------- */ void FixQEq::reallocate_matrix() { deallocate_matrix(); allocate_matrix(); } /* ---------------------------------------------------------------------- */ void FixQEq::init_list(int id, NeighList *ptr) { list = ptr; } /* ---------------------------------------------------------------------- */ void FixQEq::setup_pre_force(int vflag) { if (force->newton_pair == 0) error->all(FLERR,"QEQ with 'newton pair off' not supported"); neighbor->build_one(list); deallocate_storage(); allocate_storage(); init_storage(); deallocate_matrix(); allocate_matrix(); pre_force(vflag); } /* ---------------------------------------------------------------------- */ void FixQEq::setup_pre_force_respa(int vflag, int ilevel) { if (ilevel < nlevels_respa-1) return; setup_pre_force(vflag); } /* ---------------------------------------------------------------------- */ void FixQEq::min_setup_pre_force(int vflag) { setup_pre_force(vflag); } /* ---------------------------------------------------------------------- */ void FixQEq::init_storage() { nlocal = atom->nlocal; nall = atom->nlocal + atom->nghost; for( int i = 0; i < nall; i++ ) { Hdia_inv[i] = 1. / eta[atom->type[i]]; b_s[i] = -chi[atom->type[i]]; b_t[i] = -1.0; s[i] = t[i] = atom->q[i]; chizj[i] = 0.0; qf[i] = 0.0; q1[i] = 0.0; q2[i] = 0.0; } } /* ---------------------------------------------------------------------- */ void FixQEq::pre_force_respa(int vflag, int ilevel, int iloop) { if (ilevel == nlevels_respa-1) pre_force(vflag); } /* ---------------------------------------------------------------------- */ void FixQEq::min_pre_force(int vflag) { pre_force(vflag); } /* ---------------------------------------------------------------------- */ int FixQEq::CG( double *b, double *x ) { int loop, i, ii, inum, *ilist; double tmp, alfa, beta, b_norm; double sig_old, sig_new; inum = list->inum; ilist = list->ilist; pack_flag = 1; sparse_matvec( &H, x, q ); comm->reverse_comm_fix( this ); vector_sum( r , 1., b, -1., q, inum ); for( ii = 0; ii < inum; ++ii ) { i = ilist[ii]; if (atom->mask[i] & groupbit) d[i] = r[i] * Hdia_inv[i]; } b_norm = parallel_norm( b, inum ); sig_new = parallel_dot( r, d, inum); for( loop = 1; loop < maxiter && sqrt(sig_new)/b_norm > tolerance; ++loop ) { comm->forward_comm_fix(this); sparse_matvec( &H, d, q ); comm->reverse_comm_fix(this); tmp = parallel_dot( d, q, inum); alfa = sig_new / tmp; vector_add( x, alfa, d, inum ); vector_add( r, -alfa, q, inum ); for( ii = 0; ii < inum; ++ii ) { i = ilist[ii]; if (atom->mask[i] & groupbit) p[i] = r[i] * Hdia_inv[i]; } sig_old = sig_new; sig_new = parallel_dot( r, p, inum); beta = sig_new / sig_old; vector_sum( d, 1., p, beta, d, inum ); } if (loop >= maxiter && comm->me == 0) { char str[128]; sprintf(str,"Fix qeq CG convergence failed (%g) after %d iterations " "at " BIGINT_FORMAT " step",sqrt(sig_new)/b_norm,loop,update->ntimestep); error->warning(FLERR,str); } return loop; } /* ---------------------------------------------------------------------- */ void FixQEq::sparse_matvec( sparse_matrix *A, double *x, double *b ) { int i, j, itr_j; nlocal = atom->nlocal; nall = atom->nlocal + atom->nghost; for( i = 0; i < nlocal; ++i ) { if (atom->mask[i] & groupbit) b[i] = eta[ atom->type[i] ] * x[i]; } for( i = nlocal; i < nall; ++i ) { if (atom->mask[i] & groupbit) b[i] = 0; } for( i = 0; i < nlocal; ++i ) { if (atom->mask[i] & groupbit) { for( itr_j=A->firstnbr[i]; itr_jfirstnbr[i]+A->numnbrs[i]; itr_j++) { j = A->jlist[itr_j]; b[i] += A->val[itr_j] * x[j]; b[j] += A->val[itr_j] * x[i]; } } } } /* ---------------------------------------------------------------------- */ void FixQEq::calculate_Q() { int i, k, inum, ii; int *ilist; double u, s_sum, t_sum; double *q = atom->q; inum = list->inum; ilist = list->ilist; s_sum = parallel_vector_acc( s, inum ); t_sum = parallel_vector_acc( t, inum); u = s_sum / t_sum; for( ii = 0; ii < inum; ++ii ) { i = ilist[ii]; if (atom->mask[i] & groupbit) { q[i] = s[i] - u * t[i]; for( k = 4; k > 0; --k ) { s_hist[i][k] = s_hist[i][k-1]; t_hist[i][k] = t_hist[i][k-1]; } s_hist[i][0] = s[i]; t_hist[i][0] = t[i]; } } pack_flag = 4; comm->forward_comm_fix( this ); //Dist_vector( atom->q ); } /* ---------------------------------------------------------------------- */ int FixQEq::pack_forward_comm(int n, int *list, double *buf, int pbc_flag, int *pbc) { int m; if( pack_flag == 1) for(m = 0; m < n; m++) buf[m] = d[list[m]]; else if( pack_flag == 2 ) for(m = 0; m < n; m++) buf[m] = s[list[m]]; else if( pack_flag == 3 ) for(m = 0; m < n; m++) buf[m] = t[list[m]]; else if( pack_flag == 4 ) for(m = 0; m < n; m++) buf[m] = atom->q[list[m]]; return m; } /* ---------------------------------------------------------------------- */ void FixQEq::unpack_forward_comm(int n, int first, double *buf) { int i, m; if( pack_flag == 1) for(m = 0, i = first; m < n; m++, i++) d[i] = buf[m]; else if( pack_flag == 2) for(m = 0, i = first; m < n; m++, i++) s[i] = buf[m]; else if( pack_flag == 3) for(m = 0, i = first; m < n; m++, i++) t[i] = buf[m]; else if( pack_flag == 4) for(m = 0, i = first; m < n; m++, i++) atom->q[i] = buf[m]; } /* ---------------------------------------------------------------------- */ int FixQEq::pack_reverse_comm(int n, int first, double *buf) { int i, m; for(m = 0, i = first; m < n; m++, i++) buf[m] = q[i]; return m; } /* ---------------------------------------------------------------------- */ void FixQEq::unpack_reverse_comm(int n, int *list, double *buf) { int m; for(m = 0; m < n; m++) q[list[m]] += buf[m]; } /* ---------------------------------------------------------------------- memory usage of local atom-based arrays ------------------------------------------------------------------------- */ double FixQEq::memory_usage() { double bytes; bytes = atom->nmax*nprev*2 * sizeof(double); // s_hist & t_hist bytes += atom->nmax*11 * sizeof(double); // storage bytes += n_cap*2 * sizeof(int); // matrix... bytes += m_cap * sizeof(int); bytes += m_cap * sizeof(double); return bytes; } /* ---------------------------------------------------------------------- allocate fictitious charge arrays ------------------------------------------------------------------------- */ void FixQEq::grow_arrays(int nmax) { memory->grow(s_hist,nmax,nprev,"qeq:s_hist"); memory->grow(t_hist,nmax,nprev,"qeq:t_hist"); } /* ---------------------------------------------------------------------- copy values within fictitious charge arrays ------------------------------------------------------------------------- */ void FixQEq::copy_arrays(int i, int j, int delflag) { for (int m = 0; m < nprev; m++) { s_hist[j][m] = s_hist[i][m]; t_hist[j][m] = t_hist[i][m]; } } /* ---------------------------------------------------------------------- pack values in local atom-based array for exchange with another proc ------------------------------------------------------------------------- */ int FixQEq::pack_exchange(int i, double *buf) { for (int m = 0; m < nprev; m++) buf[m] = s_hist[i][m]; for (int m = 0; m < nprev; m++) buf[nprev+m] = t_hist[i][m]; return nprev*2; } /* ---------------------------------------------------------------------- unpack values in local atom-based array from exchange with another proc ------------------------------------------------------------------------- */ int FixQEq::unpack_exchange(int n, double *buf) { for (int m = 0; m < nprev; m++) s_hist[n][m] = buf[m]; for (int m = 0; m < nprev; m++) t_hist[n][m] = buf[nprev+m]; return nprev*2; } /* ---------------------------------------------------------------------- */ double FixQEq::parallel_norm( double *v, int n ) { int i; double my_sum, norm_sqr; int ii; int *ilist; ilist = list->ilist; + my_sum = 0.0; for( ii = 0; ii < n; ++ii ) { i = ilist[ii]; if (atom->mask[i] & groupbit) my_sum += v[i]*v[i]; } - MPI_Allreduce( &my_sum, &norm_sqr, 1, MPI_DOUBLE, MPI_SUM, world ); + MPI_Allreduce(&my_sum, &norm_sqr, 1, MPI_DOUBLE, MPI_SUM, world); return sqrt( norm_sqr ); } /* ---------------------------------------------------------------------- */ double FixQEq::parallel_dot( double *v1, double *v2, int n) { int i; double my_dot, res; int ii; int *ilist; ilist = list->ilist; my_dot = 0.0; res = 0.0; for( ii = 0; ii < n; ++ii ) { i = ilist[ii]; if (atom->mask[i] & groupbit) my_dot += v1[i] * v2[i]; } MPI_Allreduce( &my_dot, &res, 1, MPI_DOUBLE, MPI_SUM, world ); return res; } /* ---------------------------------------------------------------------- */ double FixQEq::parallel_vector_acc( double *v, int n ) { int i; double my_acc, res; int ii; int *ilist; ilist = list->ilist; my_acc = 0.0; res = 0.0; for( ii = 0; ii < n; ++ii ) { i = ilist[ii]; if (atom->mask[i] & groupbit) my_acc += v[i]; } MPI_Allreduce( &my_acc, &res, 1, MPI_DOUBLE, MPI_SUM, world ); return res; } /* ---------------------------------------------------------------------- */ void FixQEq::vector_sum( double* dest, double c, double* v, double d, double* y, int k ) { int kk; int *ilist; ilist = list->ilist; for( --k; k>=0; --k ) { kk = ilist[k]; if (atom->mask[kk] & groupbit) dest[kk] = c * v[kk] + d * y[kk]; } } /* ---------------------------------------------------------------------- */ void FixQEq::vector_add( double* dest, double c, double* v, int k ) { int kk; int *ilist; ilist = list->ilist; for( --k; k>=0; --k ) { kk = ilist[k]; if (atom->mask[kk] & groupbit) dest[kk] += c * v[kk]; } } /* ---------------------------------------------------------------------- */ void FixQEq::read_file(char *file) { int itype,ntypes; int params_per_line = 6; char **words = new char*[params_per_line+1]; ntypes = atom->ntypes; memory->create(chi,ntypes+1,"qeq:chi"); memory->create(eta,ntypes+1,"qeq:eta"); memory->create(gamma,ntypes+1,"qeq:gamma"); memory->create(zeta,ntypes+1,"qeq:zeta"); memory->create(zcore,ntypes+1,"qeq:zcore"); // open file on proc 0 FILE *fp; if (comm->me == 0) { fp = force->open_potential(file); if (fp == NULL) { char str[128]; sprintf(str,"Cannot open fix qeq parameter file %s",file); error->one(FLERR,str); } } // read each line out of file, skipping blank lines or leading '#' // store line of params if all 3 element tags are in element list int n,nwords,ielement,eof; char line[MAXLINE],*ptr; eof = ielement = 0; while (1) { if (comm->me == 0) { ptr = fgets(line,MAXLINE,fp); if (ptr == NULL) { eof = 1; fclose(fp); } 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); ielement ++; if (ielement > ntypes) error->all(FLERR,"Invalid fix qeq parameter file"); // strip comment, skip line if blank if ((ptr = strchr(line,'#'))) *ptr = '\0'; nwords = atom->count_words(line); if (nwords == 0) continue; // words = ptrs to all words in line nwords = 0; words[nwords++] = strtok(line," \t\n\r\f"); while ((words[nwords++] = strtok(NULL," \t\n\r\f"))) continue; itype = atoi(words[0]); chi[itype] = atof(words[1]); eta[itype] = atof(words[2]); gamma[itype] = atof(words[3]); zeta[itype] = atof(words[4]); zcore[itype] = atof(words[5]); } delete [] words; } diff --git a/src/RIGID/fix_rigid_small.cpp b/src/RIGID/fix_rigid_small.cpp index 3561322ff..b0f3d5652 100644 --- a/src/RIGID/fix_rigid_small.cpp +++ b/src/RIGID/fix_rigid_small.cpp @@ -1,3437 +1,3436 @@ /* ---------------------------------------------------------------------- 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 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; xcmimage = 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 = 0; } /* ---------------------------------------------------------------------- */ 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(xcmimage); 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 invoke pre_neighbor() to insure body xcmimage flags are reset needed if Verlet::setup::pbc() has remapped/migrated atoms for 2nd run setup_bodies_static() invokes pre_neighbor itself ------------------------------------------------------------------------- */ void FixRigidSmall::setup_pre_neighbor() { if (!staticflag || !infile) setup_bodies_static(); else pre_neighbor(); 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; 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],xcmimage[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 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],xcmimage[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(); } /* ---------------------------------------------------------------------- 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 first-time definition of rigid body in setup_bodies_static() or due to box flip also adjust imagebody = rigid body image flags, due to xcm remap also adjust rgdimage flags of all atoms in bodies for two effects (1) change in true image flags due to pbc() call during exchange (2) change in imagebody due to xcm remap rgdimage flags are always -1,0,-1 so that body can be unwrapped around in-box xcm and stay close to simulation box if don't do this, then a body could end up very far away when unwrapped by true image flags, and set_xv() will compute huge displacements every step to reset coords of all the body atoms to be back inside the box, ditto for triclinic box flip note: so just want to avoid that numeric probem? ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- 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() { // 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); for (int ibody = 0; ibody < nlocal_body; ibody++) { Body *b = &body[ibody]; domain->remap(b->xcm,b->image); } image_shift(); } /* ---------------------------------------------------------------------- reset body xcmimage flags of atoms in bodies xcmimage flags are relative to xcm so that body can be unwrapped xcmimage = true image flag - imagebody flag ------------------------------------------------------------------------- */ void FixRigidSmall::image_shift() { - int ibody; imageint tdim,bdim,xdim[3]; imageint *image = atom->image; int nlocal = atom->nlocal; for (int i = 0; i < nlocal; i++) { if (atom2body[i] < 0) continue; Body *b = &body[atom2body[i]]; tdim = image[i] & IMGMASK; bdim = b->image & IMGMASK; xdim[0] = IMGMAX + tdim - bdim; tdim = (image[i] >> IMGBITS) & IMGMASK; bdim = (b->image >> IMGBITS) & IMGMASK; xdim[1] = IMGMAX + tdim - bdim; tdim = image[i] >> IMG2BITS; bdim = b->image >> IMG2BITS; xdim[2] = IMGMAX + tdim - bdim; xcmimage[i] = (xdim[2] << IMG2BITS) | (xdim[1] << IMGBITS) | xdim[0]; } } /* ---------------------------------------------------------------------- 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; 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 = (xcmimage[i] & IMGMASK) - IMGMAX; ybox = (xcmimage[i] >> IMGBITS & IMGMASK) - IMGMAX; zbox = (xcmimage[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; 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 = (xcmimage[i] & IMGMASK) - IMGMAX; ybox = (xcmimage[i] >> IMGBITS & IMGMASK) - IMGMAX; zbox = (xcmimage[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(); 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 *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 (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 *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 (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) { 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; } } // first-time setting of body xcmimage flags = true image flags if (!staticflag) { imageint *image = atom->image; for (i = 0; i < nlocal; i++) if (bodytag[i] >= 0) xcmimage[i] = image[i]; else xcmimage[i] = 0; } // 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; 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],xcmimage[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); } // one-time set of rigid body image flags to default values // staticflag insures this is only done once, not on successive runs // then remap the xcm of each body back into simulation box // and reset body xcmimage flags via pre_neighbor() 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],xcmimage[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],xcmimage[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; 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],xcmimage[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(); 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 inertia 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; if (me == 0) { MPI_Status status; MPI_Request request; 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 (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,MPI_STATUS_IGNORE); 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(xcmimage,nmax,"rigid/small:xcmimage"); 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]; xcmimage[j] = xcmimage[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; xcmimage[i] = 0; 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] = ubuf(xcmimage[i]).d; buf[2] = displace[i][0]; buf[3] = displace[i][1]; buf[4] = displace[i][2]; // extended attribute info int m = 5; 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; xcmimage[nlocal] = (imageint) ubuf(buf[1]).i; displace[nlocal][0] = buf[2]; displace[nlocal][1] = buf[3]; displace[nlocal][2] = buf[4]; // extended attribute info int m = 5; if (extended) { eflags[nlocal] = static_cast (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 (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 (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 (buf[m++]); counts[k][1] += static_cast (buf[m++]); counts[k][2] += static_cast (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 = nmax*2 * sizeof(int); bytes += nmax * sizeof(imageint); 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/USER-CUDA/cuda.cpp b/src/USER-CUDA/cuda.cpp index 3e3d54f79..909b1259e 100644 --- a/src/USER-CUDA/cuda.cpp +++ b/src/USER-CUDA/cuda.cpp @@ -1,1067 +1,1067 @@ /* ---------------------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator Original Version: http://lammps.sandia.gov, Sandia National Laboratories Steve Plimpton, sjplimp@sandia.gov See the README file in the top-level LAMMPS directory. ----------------------------------------------------------------------- USER-CUDA Package and associated modifications: https://sourceforge.net/projects/lammpscuda/ Christian Trott, christian.trott@tu-ilmenau.de Lars Winterfeld, lars.winterfeld@tu-ilmenau.de Theoretical Physics II, University of Technology Ilmenau, Germany See the README file in the USER-CUDA directory. This software is distributed under the GNU General Public License. ------------------------------------------------------------------------- */ #include #include #include #include "user_cuda.h" #include "atom.h" #include "domain.h" #include "force.h" #include "pair.h" #include "update.h" #include "neighbor.h" #include "neigh_list.h" #include "universe.h" #include "input.h" #include "atom_masks.h" #include "error.h" #include "cuda_neigh_list.h" //#include "pre_binning_cu.h" //#include "reverse_binning_cu.h" #include #include #include "cuda_pair_cu.h" #include "cuda_cu.h" using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ Cuda::Cuda(LAMMPS* lmp) : Pointers(lmp) { cuda_exists = true; lmp->cuda = this; if (universe->me == 0) printf("# Using LAMMPS_CUDA \n"); shared_data.me = universe->me; device_set = false; devicelist = NULL; Cuda_Cuda_GetCompileSettings(&shared_data); if (universe->me == 0) { if(shared_data.compile_settings.prec_glob != sizeof(CUDA_CFLOAT) / 4) printf("\n\n # CUDA WARNING: Compile Settings of cuda and cpp code differ! \n" " # CUDA WARNING: Global Precision: cuda %i cpp %i\n\n", shared_data.compile_settings.prec_glob, (int) sizeof(CUDA_CFLOAT) / 4); if(shared_data.compile_settings.prec_x != sizeof(X_CFLOAT) / 4) printf("\n\n # CUDA WARNING: Compile Settings of cuda and cpp code differ! \n" " # CUDA WARNING: X Precision: cuda %i cpp %i\n\n", shared_data.compile_settings.prec_x, (int) sizeof(X_CFLOAT) / 4); if(shared_data.compile_settings.prec_v != sizeof(V_CFLOAT) / 4) printf("\n\n # CUDA WARNING: Compile Settings of cuda and cpp code differ! \n" " # CUDA WARNING: V Precision: cuda %i cpp %i\n\n", shared_data.compile_settings.prec_v, (int) sizeof(V_CFLOAT) / 4); if(shared_data.compile_settings.prec_f != sizeof(F_CFLOAT) / 4) printf("\n\n # CUDA WARNING: Compile Settings of cuda and cpp code differ! \n" " # CUDA WARNING: F Precision: cuda %i cpp %i\n\n", shared_data.compile_settings.prec_f, (int) sizeof(F_CFLOAT) / 4); if(shared_data.compile_settings.prec_pppm != sizeof(PPPM_CFLOAT) / 4) printf("\n\n # CUDA WARNING: Compile Settings of cuda and cpp code differ! \n" " # CUDA WARNING: PPPM Precision: cuda %i cpp %i\n\n", shared_data.compile_settings.prec_pppm, (int) sizeof(PPPM_CFLOAT) / 4); if(shared_data.compile_settings.prec_fft != sizeof(FFT_CFLOAT) / 4) printf("\n\n # CUDA WARNING: Compile Settings of cuda and cpp code differ! \n" " # CUDA WARNING: FFT Precision: cuda %i cpp %i\n\n", shared_data.compile_settings.prec_fft, (int) sizeof(FFT_CFLOAT) / 4); #ifdef FFT_CUFFT if(shared_data.compile_settings.cufft != 1) printf("\n\n # CUDA WARNING: Compile Settings of cuda and cpp code differ! \n" " # CUDA WARNING: cufft: cuda %i cpp %i\n\n", shared_data.compile_settings.cufft, 1); #else if(shared_data.compile_settings.cufft != 0) printf("\n\n # CUDA WARNING: Compile Settings of cuda and cpp code differ! \n" " # CUDA WARNING: cufft: cuda %i cpp %i\n\n", shared_data.compile_settings.cufft, 0); #endif if(shared_data.compile_settings.arch != CUDA_ARCH) printf("\n\n # CUDA WARNING: Compile Settings of cuda and cpp code differ! \n" " # CUDA WARNING: arch: cuda %i cpp %i\n\n", shared_data.compile_settings.cufft, CUDA_ARCH); } cu_x = 0; cu_v = 0; cu_f = 0; cu_tag = 0; cu_type = 0; cu_mask = 0; cu_image = 0; cu_xhold = 0; cu_q = 0; cu_rmass = 0; cu_mass = 0; cu_virial = 0; cu_eatom = 0; cu_vatom = 0; cu_radius = 0; cu_density = 0; cu_omega = 0; cu_torque = 0; cu_special = 0; cu_nspecial = 0; cu_molecule = 0; cu_x_type = 0; x_type = 0; cu_v_radius = 0; v_radius = 0; cu_omega_rmass = 0; omega_rmass = 0; binned_id = 0; cu_binned_id = 0; binned_idnew = 0; cu_binned_idnew = 0; cu_map_array = 0; copy_buffer = 0; copy_buffersize = 0; neighbor_decide_by_integrator = 0; pinned = true; debugdata = 0; finished_setup = false; begin_setup = false; finished_run = false; setSharedDataZero(); uploadtime = 0; downloadtime = 0; dotiming = false; dotestatom = false; testatom = 0; oncpu = true; self_comm = 0; MYDBG(printf("# CUDA: Cuda::Cuda Done...\n");) //cCudaData } /* ---------------------------------------------------------------------- */ Cuda::~Cuda() { print_timings(); if (universe->me == 0) printf("# CUDA: Free memory...\n"); delete [] devicelist; delete cu_q; delete cu_x; delete cu_v; delete cu_f; delete cu_tag; delete cu_type; delete cu_mask; delete cu_image; delete cu_xhold; delete cu_mass; delete cu_rmass; delete cu_virial; delete cu_eng_vdwl; delete cu_eng_coul; delete cu_extent; delete cu_eatom; delete cu_vatom; delete cu_radius; delete cu_density; delete cu_omega; delete cu_torque; delete cu_molecule; delete cu_x_type; delete [] x_type; delete cu_v_radius; delete [] v_radius; delete cu_omega_rmass; delete [] omega_rmass; delete cu_debugdata; delete[] debugdata; delete cu_map_array; std::map::iterator p = neigh_lists.begin(); while(p != neigh_lists.end()) { delete p->second; ++p; } } /* ---------------------------------------------------------------------- package cuda command can be invoked multiple times: -c on, -pk, package command can only init GPUs once in activate(), so just store params here ------------------------------------------------------------------------- */ void Cuda::accelerator(int narg, char **arg) { // this error should not happen if (device_set) error->all(FLERR,"USER-CUDA device is already activated"); // pppn = # of GPUs/node pppn = force->inumeric(FLERR,arg[0]); if (pppn <= 0) error->all(FLERR,"Illegal package cuda command"); // optional args delete [] devicelist; devicelist = NULL; int newtonflag = 0; int iarg = 1; while (iarg < narg) { if (strcmp(arg[iarg],"newton") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal package cuda command"); if (strcmp(arg[iarg+1],"off") == 0) newtonflag = 0; else if (strcmp(arg[iarg+1],"on") == 0) newtonflag = 1; else error->all(FLERR,"Illegal package cuda command"); } else if (strcmp(arg[iarg],"gpuID") == 0) { if (iarg+pppn+1 > narg) error->all(FLERR,"Illegal package cuda command"); devicelist = new int[pppn]; for (int k = 0; k < pppn; k++) devicelist[k] = force->inumeric(FLERR,arg[iarg+k+1]); iarg += pppn + 1; } else if (strcmp(arg[iarg],"timing") == 0) { dotiming = true; iarg++; } else if (strcmp(arg[iarg],"test") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal package cuda command"); testatom = force->numeric(FLERR,arg[iarg+1]); dotestatom = true; iarg += 2; } else if (strcmp(arg[iarg],"thread") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal package cuda command"); if (strcmp(arg[iarg+1],"auto") == 0) shared_data.pair.override_block_per_atom = -1; else if (strcmp(arg[iarg+1],"tpa") == 0) shared_data.pair.override_block_per_atom = 0; else if (strcmp(arg[iarg+1],"bpa") == 0) shared_data.pair.override_block_per_atom = 1; else error->all(FLERR,"Illegal package cuda command"); iarg += 2; } // undocumented options else if (strcmp(arg[iarg],"suffix") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal package cuda command"); strcpy(lmp->suffix,arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"overlap_comm") == 0) { shared_data.overlap_comm = 1; iarg++; } else if (strcmp(arg[iarg],"pinned") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal package cuda command"); pinned = force->inumeric(FLERR,arg[iarg+1]) == 0 ? false : true; if ((pinned == false) && (universe->me == 0)) printf(" #CUDA: Pinned memory is not used for communication\n"); iarg += 2; } else error->all(FLERR,"Illegal package cuda command"); } // set newton flags force->newton = force->newton_pair = force->newton_bond = newtonflag; } /* ---------------------------------------------------------------------- activate the GPUs only done once with whatever settings used by the last package command ------------------------------------------------------------------------- */ void Cuda::activate() { if (device_set) return; device_set = true; if (universe->me == 0) printf("# CUDA: Activate GPU \n"); CudaWrapper_Init(0, (char**)0, universe->me, pppn, devicelist); //if(shared_data.overlap_comm) CudaWrapper_AddStreams(3); cu_x = 0; cu_v = 0; cu_f = 0; cu_tag = 0; cu_type = 0; cu_mask = 0; cu_image = 0; cu_xhold = 0; cu_q = 0; cu_rmass = 0; cu_mass = 0; cu_virial = 0; cu_eatom = 0; cu_vatom = 0; cu_radius = 0; cu_density = 0; cu_omega = 0; cu_torque = 0; cu_special = 0; cu_nspecial = 0; cu_molecule = 0; cu_x_type = 0; cu_v_radius = 0; cu_omega_rmass = 0; cu_binned_id = 0; cu_binned_idnew = 0; allocate(); } /* ---------------------------------------------------------------------- */ void Cuda::setSharedDataZero() { MYDBG(printf("# CUDA: Cuda::setSharedDataZero ...\n");) shared_data.atom.nlocal = 0; shared_data.atom.nghost = 0; shared_data.atom.nall = 0; shared_data.atom.nmax = 0; shared_data.atom.ntypes = 0; shared_data.atom.q_flag = 0; shared_data.atom.need_eatom = 0; shared_data.atom.need_vatom = 0; shared_data.atom.update_nmax = 1; shared_data.atom.update_nlocal = 1; shared_data.atom.update_neigh = 1; shared_data.pair.cudable_force = 0; shared_data.pair.collect_forces_later = 0; shared_data.pair.use_block_per_atom = 0; shared_data.pair.override_block_per_atom = -1; shared_data.pair.cut = 0; shared_data.pair.cutsq = 0; shared_data.pair.cut_inner = 0; shared_data.pair.cut_coul = 0; shared_data.pair.special_lj = 0; shared_data.pair.special_coul = 0; shared_data.pair.neighall = false; shared_data.pppm.cudable_force = 0; shared_data.buffersize = 0; shared_data.buffer_new = 1; shared_data.buffer = NULL; shared_data.comm.comm_phase = 0; shared_data.overlap_comm = 0; shared_data.comm.buffer = NULL; shared_data.comm.buffer_size = 0; shared_data.comm.overlap_split_ratio = 0; // setTimingsZero(); } void Cuda::allocate() { MYDBG(printf("# CUDA: Cuda::allocate ...\n");) if(not cu_virial) { cu_virial = new cCudaData (NULL, & shared_data.pair.virial , 6); cu_eng_vdwl = new cCudaData (NULL, & shared_data.pair.eng_vdwl , 1); cu_eng_coul = new cCudaData (NULL, & shared_data.pair.eng_coul , 1); cu_extent = new cCudaData (extent, 6); shared_data.flag = CudaWrapper_AllocCudaData(sizeof(int)); int size = 2 * CUDA_MAX_DEBUG_SIZE; debugdata = new int[size]; cu_debugdata = new cCudaData (debugdata , size); shared_data.debugdata = cu_debugdata->dev_data(); } checkResize(); setSystemParams(); MYDBG(printf("# CUDA: Cuda::allocate done...\n");) } void Cuda::setSystemParams() { MYDBG(printf("# CUDA: Cuda::setSystemParams ...\n");) shared_data.atom.nlocal = atom->nlocal; shared_data.atom.nghost = atom->nghost; shared_data.atom.nall = atom->nlocal + atom->nghost; shared_data.atom.ntypes = atom->ntypes; shared_data.atom.q_flag = atom->q_flag; shared_data.atom.rmass_flag = atom->rmass_flag; MYDBG(printf("# CUDA: Cuda::setSystemParams done ...\n");) } void Cuda::setDomainParams() { MYDBG(printf("# CUDA: Cuda::setDomainParams ...\n");) cuda_shared_domain* cu_domain = &shared_data.domain; cu_domain->triclinic = domain->triclinic; for(short i = 0; i < 3; ++i) { cu_domain->periodicity[i] = domain->periodicity[i]; cu_domain->sublo[i] = domain->sublo[i]; cu_domain->subhi[i] = domain->subhi[i]; cu_domain->boxlo[i] = domain->boxlo[i]; cu_domain->boxhi[i] = domain->boxhi[i]; cu_domain->prd[i] = domain->prd[i]; } if(domain->triclinic) { for(short i = 0; i < 3; ++i) { cu_domain->boxlo_lamda[i] = domain->boxlo_lamda[i]; cu_domain->boxhi_lamda[i] = domain->boxhi_lamda[i]; cu_domain->prd_lamda[i] = domain->prd_lamda[i]; cu_domain->sublo[i] = domain->sublo_lamda[i]; cu_domain->subhi[i] = domain->subhi_lamda[i]; } cu_domain->xy = domain->xy; cu_domain->xz = domain->xz; cu_domain->yz = domain->yz; } for(int i = 0; i < 6; i++) { cu_domain->h[i] = domain->h[i]; cu_domain->h_inv[i] = domain->h_inv[i]; cu_domain->h_rate[i] = domain->h_rate[i]; } cu_domain->update = 2; MYDBG(printf("# CUDA: Cuda::setDomainParams done ...\n");) } void Cuda::checkResize() { MYDBG(printf("# CUDA: Cuda::checkResize ...\n");) cuda_shared_atom* cu_atom = & shared_data.atom; cu_atom->q_flag = atom->q_flag; cu_atom->rmass_flag = atom->rmass ? 1 : 0; cu_atom->nall = atom->nlocal + atom->nghost; cu_atom->nlocal = atom->nlocal; cu_atom->nghost = atom->nghost; // do we have more atoms to upload than currently allocated memory on device? (also true if nothing yet allocated) if(atom->nmax > cu_atom->nmax || cu_tag == NULL) { delete cu_x; cu_x = new cCudaData ((double*)atom->x , & cu_atom->x , atom->nmax, 3, 0, true); //cu_x->set_buffer(&(shared_data.buffer),&(shared_data.buffersize),true); delete cu_v; cu_v = new cCudaData ((double*)atom->v, & cu_atom->v , atom->nmax, 3); delete cu_f; cu_f = new cCudaData ((double*)atom->f, & cu_atom->f , atom->nmax, 3, 0, true); delete cu_tag; cu_tag = new cCudaData (atom->tag , & cu_atom->tag , atom->nmax, 0, true); delete cu_type; cu_type = new cCudaData (atom->type , & cu_atom->type , atom->nmax, 0, true); delete cu_mask; cu_mask = new cCudaData (atom->mask , & cu_atom->mask , atom->nmax, 0, true); delete cu_image; cu_image = new cCudaData (atom->image , & cu_atom->image , atom->nmax, 0, true); if(atom->rmass) { delete cu_rmass; cu_rmass = new cCudaData (atom->rmass , & cu_atom->rmass , atom->nmax); } if(cu_atom->q_flag) { delete cu_q; cu_q = new cCudaData ((double*)atom->q, & cu_atom->q , atom->nmax, 0 , true); }// cu_q->set_buffer(&(copy_buffer),&(copy_buffersize),true);} if(atom->radius) { delete cu_radius; cu_radius = new cCudaData (atom->radius , & cu_atom->radius , atom->nmax); delete cu_v_radius; cu_v_radius = new cCudaData (v_radius , & cu_atom->v_radius , atom->nmax * 4); delete cu_omega_rmass; cu_omega_rmass = new cCudaData (omega_rmass , & cu_atom->omega_rmass , atom->nmax * 4); } if(atom->omega) { delete cu_omega; cu_omega = new cCudaData (((double*) atom->omega) , & cu_atom->omega , atom->nmax, 3); } if(atom->torque) { delete cu_torque; cu_torque = new cCudaData (((double*) atom->torque) , & cu_atom->torque , atom->nmax, 3); } if(atom->special) { delete cu_special; cu_special = new cCudaData (((int*) & (atom->special[0][0])) , & cu_atom->special , atom->nmax, atom->maxspecial, 0 , true); shared_data.atom.maxspecial = atom->maxspecial; } if(atom->nspecial) { delete cu_nspecial; cu_nspecial = new cCudaData (((int*) atom->nspecial) , & cu_atom->nspecial , atom->nmax, 3, 0, true); } if(atom->molecule) { delete cu_molecule; cu_molecule = new cCudaData (((int*) atom->molecule) , & cu_atom->molecule , atom->nmax, 0 , true); } shared_data.atom.special_flag = neighbor->special_flag; shared_data.atom.molecular = atom->molecular; cu_atom->update_nmax = 2; cu_atom->nmax = atom->nmax; delete cu_x_type; cu_x_type = new cCudaData (x_type , & cu_atom->x_type , atom->nmax * 4); } if(((cu_xhold == NULL) || (cu_xhold->get_dim()[0] < neighbor->maxhold)) && neighbor->xhold) { delete cu_xhold; cu_xhold = new cCudaData ((double*)neighbor->xhold, & cu_atom->xhold , neighbor->maxhold, 3); shared_data.atom.maxhold = neighbor->maxhold; } if(atom->mass && !cu_mass) { cu_mass = new cCudaData (atom->mass , & cu_atom->mass , atom->ntypes + 1); } cu_atom->mass_host = atom->mass; if(atom->map_style == 1) { - if((cu_map_array == NULL)) { + if(cu_map_array == NULL) { cu_map_array = new cCudaData (atom->get_map_array() , & cu_atom->map_array , atom->get_map_size()); } else if(cu_map_array->dev_size() / sizeof(int) < atom->get_map_size()) { delete cu_map_array; cu_map_array = new cCudaData (atom->get_map_array() , & cu_atom->map_array , atom->get_map_size()); } } // if any of the host pointers have changed (e.g. re-allocated somewhere else), set to correct pointer if(cu_x ->get_host_data() != atom->x) cu_x ->set_host_data((double*)(atom->x)); if(cu_v ->get_host_data() != atom->v) cu_v ->set_host_data((double*)(atom->v)); if(cu_f ->get_host_data() != atom->f) cu_f ->set_host_data((double*)(atom->f)); if(cu_tag ->get_host_data() != atom->tag) cu_tag ->set_host_data(atom->tag); if(cu_type->get_host_data() != atom->type) cu_type->set_host_data(atom->type); if(cu_mask->get_host_data() != atom->mask) cu_mask->set_host_data(atom->mask); if(cu_image->get_host_data() != atom->image) cu_mask->set_host_data(atom->image); if(cu_xhold) if(cu_xhold->get_host_data() != neighbor->xhold) cu_xhold->set_host_data((double*)(neighbor->xhold)); if(atom->rmass) if(cu_rmass->get_host_data() != atom->rmass) cu_rmass->set_host_data((double*)(atom->rmass)); if(cu_atom->q_flag) if(cu_q->get_host_data() != atom->q) cu_q->set_host_data((double*)(atom->q)); if(atom->radius) if(cu_radius->get_host_data() != atom->radius) cu_radius->set_host_data((double*)(atom->radius)); if(atom->omega) if(cu_omega->get_host_data() != atom->omega) cu_omega->set_host_data((double*)(atom->omega)); if(atom->torque) if(cu_torque->get_host_data() != atom->torque) cu_torque->set_host_data((double*)(atom->torque)); if(atom->special) if(cu_special->get_host_data() != atom->special) { delete cu_special; cu_special = new cCudaData (((int*) atom->special) , & cu_atom->special , atom->nmax, atom->maxspecial); shared_data.atom.maxspecial = atom->maxspecial; } if(atom->nspecial) if(cu_nspecial->get_host_data() != atom->nspecial) cu_nspecial->set_host_data((int*)(atom->nspecial)); if(atom->molecule) if(cu_molecule->get_host_data() != atom->molecule) cu_molecule->set_host_data((int*)(atom->molecule)); if(force) if(cu_virial ->get_host_data() != force->pair->virial) cu_virial ->set_host_data(force->pair->virial); if(force) if(cu_eng_vdwl ->get_host_data() != &force->pair->eng_vdwl) cu_eng_vdwl ->set_host_data(&force->pair->eng_vdwl); if(force) if(cu_eng_coul ->get_host_data() != &force->pair->eng_coul) cu_eng_coul ->set_host_data(&force->pair->eng_coul); cu_atom->update_nlocal = 2; MYDBG(printf("# CUDA: Cuda::checkResize done...\n");) } void Cuda::evsetup_eatom_vatom(int eflag_atom, int vflag_atom) { if(eflag_atom) { if(not cu_eatom) cu_eatom = new cCudaData (force->pair->eatom, & (shared_data.atom.eatom) , atom->nmax); // cu_eatom->set_buffer(&(copy_buffer),&(copy_buffersize),true);} if(cu_eatom->get_dim()[0] != atom->nmax) { //delete cu_eatom; //cu_eatom = new cCudaData (force->pair->eatom, & (shared_data.atom.eatom) , atom->nmax );// cu_eatom->set_buffer(&(copy_buffer),&(copy_buffersize),true);} shared_data.atom.update_nmax = 2; } cu_eatom->set_host_data(force->pair->eatom); cu_eatom->memset_device(0); } if(vflag_atom) { if(not cu_vatom) cu_vatom = new cCudaData ((double*)force->pair->vatom, & (shared_data.atom.vatom) , atom->nmax , 6);// cu_vatom->set_buffer(&(copy_buffer),&(copy_buffersize),true);} if(cu_vatom->get_dim()[0] != atom->nmax) { //delete cu_vatom; //cu_vatom = new cCudaData ((double*)force->pair->vatom, & (shared_data.atom.vatom) , atom->nmax ,6 );// cu_vatom->set_buffer(&(copy_buffer),&(copy_buffersize),true);} shared_data.atom.update_nmax = 2; } cu_vatom->set_host_data((double*)force->pair->vatom); cu_vatom->memset_device(0); } } void Cuda::uploadAll() { MYDBG(printf("# CUDA: Cuda::uploadAll() ... start\n");) my_times starttime; my_times endtime; if(atom->nmax != shared_data.atom.nmax) checkResize(); my_gettime(CLOCK_REALTIME, &starttime); cu_x ->upload(); cu_v ->upload(); cu_f ->upload(); cu_tag ->upload(); cu_type->upload(); cu_mask->upload(); cu_image->upload(); if(shared_data.atom.q_flag) cu_q ->upload(); if(atom->rmass) cu_rmass->upload(); if(atom->radius) cu_radius->upload(); if(atom->omega) cu_omega->upload(); if(atom->torque) cu_torque->upload(); if(atom->special) cu_special->upload(); if(atom->nspecial) cu_nspecial->upload(); if(atom->molecule) cu_molecule->upload(); if(cu_eatom) cu_eatom->upload(); if(cu_vatom) cu_vatom->upload(); my_gettime(CLOCK_REALTIME, &endtime); uploadtime += (endtime.tv_sec - starttime.tv_sec + 1.0 * (endtime.tv_nsec - starttime.tv_nsec) / 1000000000); CUDA_IF_BINNING(Cuda_PreBinning(& shared_data);) CUDA_IF_BINNING(Cuda_Binning(& shared_data);) shared_data.atom.triggerneighsq = neighbor->triggersq; MYDBG(printf("# CUDA: Cuda::uploadAll() ... end\n");) } void Cuda::downloadAll() { MYDBG(printf("# CUDA: Cuda::downloadAll() ... start\n");) my_times starttime; my_times endtime; if(atom->nmax != shared_data.atom.nmax) checkResize(); CUDA_IF_BINNING(Cuda_ReverseBinning(& shared_data);) my_gettime(CLOCK_REALTIME, &starttime); cu_x ->download(); cu_v ->download(); cu_f ->download(); cu_type->download(); cu_tag ->download(); cu_mask->download(); cu_image->download(); //if(shared_data.atom.need_eatom) cu_eatom->download(); //if(shared_data.atom.need_vatom) cu_vatom->download(); if(shared_data.atom.q_flag) cu_q ->download(); if(atom->rmass) cu_rmass->download(); if(atom->radius) cu_radius->download(); if(atom->omega) cu_omega->download(); if(atom->torque) cu_torque->download(); if(atom->special) cu_special->download(); if(atom->nspecial) cu_nspecial->download(); if(atom->molecule) cu_molecule->download(); if(cu_eatom) cu_eatom->download(); if(cu_vatom) cu_vatom->download(); my_gettime(CLOCK_REALTIME, &endtime); downloadtime += (endtime.tv_sec - starttime.tv_sec + 1.0 * (endtime.tv_nsec - starttime.tv_nsec) / 1000000000); MYDBG(printf("# CUDA: Cuda::downloadAll() ... end\n");) } void Cuda::upload(int datamask) { MYDBG(printf("# CUDA: Cuda::upload() ... start\n");) my_times starttime; my_times endtime; if(atom->nmax != shared_data.atom.nmax) checkResize(); my_gettime(CLOCK_REALTIME, &starttime); if(X_MASK & datamask) cu_x ->upload(); if(V_MASK & datamask) cu_v ->upload(); if(F_MASK & datamask) cu_f ->upload(); if(TYPE_MASK & datamask) cu_type->upload(); if(TAG_MASK & datamask) cu_tag ->upload(); if(MASK_MASK & datamask) cu_mask->upload(); if(IMAGE_MASK & datamask) cu_image->upload(); //if(shared_data.atom.need_eatom) cu_eatom->upload(); //if(shared_data.atom.need_vatom) cu_vatom->upload(); if(shared_data.atom.q_flag) if(Q_MASK & datamask) cu_q ->upload(); if(atom->rmass) if(RMASS_MASK & datamask) cu_rmass->upload(); if(atom->radius) if(RADIUS_MASK & datamask) cu_radius->upload(); if(atom->omega) if(OMEGA_MASK & datamask) cu_omega->upload(); if(atom->torque) if(TORQUE_MASK & datamask) cu_torque->upload(); if(atom->special) if(SPECIAL_MASK & datamask) cu_special->upload(); if(atom->nspecial) if(SPECIAL_MASK & datamask) cu_nspecial->upload(); if(atom->molecule) if(MOLECULE_MASK & datamask) cu_molecule->upload(); if(cu_eatom) cu_eatom->upload(); if(cu_vatom) cu_vatom->upload(); my_gettime(CLOCK_REALTIME, &endtime); uploadtime += (endtime.tv_sec - starttime.tv_sec + 1.0 * (endtime.tv_nsec - starttime.tv_nsec) / 1000000000); MYDBG(printf("# CUDA: Cuda::upload() ... end\n");) } void Cuda::download(int datamask) { MYDBG(printf("# CUDA: Cuda::download() ... start\n");) my_times starttime; my_times endtime; if(atom->nmax != shared_data.atom.nmax) checkResize(); CUDA_IF_BINNING(Cuda_ReverseBinning(& shared_data);) my_gettime(CLOCK_REALTIME, &starttime); if(X_MASK & datamask) cu_x ->download(); if(V_MASK & datamask) cu_v ->download(); if(F_MASK & datamask) cu_f ->download(); if(TYPE_MASK & datamask) cu_type->download(); if(TAG_MASK & datamask) cu_tag ->download(); if(MASK_MASK & datamask) cu_mask->download(); if(IMAGE_MASK & datamask) cu_image->download(); //if(shared_data.atom.need_eatom) cu_eatom->download(); //if(shared_data.atom.need_vatom) cu_vatom->download(); if(shared_data.atom.q_flag) if(Q_MASK & datamask) cu_q ->download(); if(atom->rmass) if(RMASS_MASK & datamask) cu_rmass->download(); if(atom->radius) if(RADIUS_MASK & datamask) cu_radius->download(); if(atom->omega) if(OMEGA_MASK & datamask) cu_omega->download(); if(atom->torque) if(TORQUE_MASK & datamask) cu_torque->download(); if(atom->special) if(SPECIAL_MASK & datamask) cu_special->download(); if(atom->nspecial) if(SPECIAL_MASK & datamask) cu_nspecial->download(); if(atom->molecule) if(MOLECULE_MASK & datamask) cu_molecule->download(); if(cu_eatom) cu_eatom->download(); if(cu_vatom) cu_vatom->download(); my_gettime(CLOCK_REALTIME, &endtime); downloadtime += (endtime.tv_sec - starttime.tv_sec + 1.0 * (endtime.tv_nsec - starttime.tv_nsec) / 1000000000); MYDBG(printf("# CUDA: Cuda::download() ... end\n");) } void Cuda::downloadX() { Cuda_Pair_RevertXType(& this->shared_data); cu_x->download(); } CudaNeighList* Cuda::registerNeighborList(class NeighList* neigh_list) { MYDBG(printf("# CUDA: Cuda::registerNeighborList() ... start a\n");) std::map::iterator p = neigh_lists.find(neigh_list); if(p != neigh_lists.end()) return p->second; else { CudaNeighList* neigh_list_cuda = new CudaNeighList(lmp, neigh_list); neigh_lists.insert(std::pair(neigh_list, neigh_list_cuda)); return neigh_list_cuda; } MYDBG(printf("# CUDA: Cuda::registerNeighborList() ... end b\n");) } void Cuda::uploadAllNeighborLists() { MYDBG(printf("# CUDA: Cuda::uploadAllNeighborList() ... start\n");) std::map::iterator p = neigh_lists.begin(); while(p != neigh_lists.end()) { p->second->nl_upload(); if(not(p->second->neigh_list->cuda_list->build_cuda)) for(int i = 0; i < atom->nlocal; i++) p->second->sneighlist.maxneighbors = MAX(p->second->neigh_list->numneigh[i], p->second->sneighlist.maxneighbors) ; ++p; } MYDBG(printf("# CUDA: Cuda::uploadAllNeighborList() ... done\n");) } void Cuda::downloadAllNeighborLists() { MYDBG(printf("# CUDA: Cuda::downloadAllNeighborList() ... start\n");) std::map::iterator p = neigh_lists.begin(); while(p != neigh_lists.end()) { p->second->nl_download(); ++p; } } void Cuda::update_xhold(int &maxhold, double* xhold) { if(this->shared_data.atom.maxhold < atom->nmax) { maxhold = atom->nmax; delete this->cu_xhold; this->cu_xhold = new cCudaData ((double*)xhold, & this->shared_data.atom.xhold , maxhold, 3); } this->shared_data.atom.maxhold = maxhold; CudaWrapper_CopyData(this->cu_xhold->dev_data(), this->cu_x->dev_data(), 3 * atom->nmax * sizeof(X_CFLOAT)); } void Cuda::setTimingsZero() { shared_data.cuda_timings.test1 = 0; shared_data.cuda_timings.test2 = 0; //communication shared_data.cuda_timings.comm_forward_total = 0; shared_data.cuda_timings.comm_forward_mpi_upper = 0; shared_data.cuda_timings.comm_forward_mpi_lower = 0; shared_data.cuda_timings.comm_forward_kernel_pack = 0; shared_data.cuda_timings.comm_forward_kernel_unpack = 0; shared_data.cuda_timings.comm_forward_upload = 0; shared_data.cuda_timings.comm_forward_download = 0; shared_data.cuda_timings.comm_exchange_total = 0; shared_data.cuda_timings.comm_exchange_mpi = 0; shared_data.cuda_timings.comm_exchange_kernel_pack = 0; shared_data.cuda_timings.comm_exchange_kernel_unpack = 0; shared_data.cuda_timings.comm_exchange_kernel_fill = 0; shared_data.cuda_timings.comm_exchange_cpu_pack = 0; shared_data.cuda_timings.comm_exchange_upload = 0; shared_data.cuda_timings.comm_exchange_download = 0; shared_data.cuda_timings.comm_border_total = 0; shared_data.cuda_timings.comm_border_mpi = 0; shared_data.cuda_timings.comm_border_kernel_pack = 0; shared_data.cuda_timings.comm_border_kernel_unpack = 0; shared_data.cuda_timings.comm_border_kernel_buildlist = 0; shared_data.cuda_timings.comm_border_kernel_self = 0; shared_data.cuda_timings.comm_border_upload = 0; shared_data.cuda_timings.comm_border_download = 0; //pair forces shared_data.cuda_timings.pair_xtype_conversion = 0; shared_data.cuda_timings.pair_kernel = 0; shared_data.cuda_timings.pair_virial = 0; shared_data.cuda_timings.pair_force_collection = 0; //neighbor shared_data.cuda_timings.neigh_bin = 0; shared_data.cuda_timings.neigh_build = 0; shared_data.cuda_timings.neigh_special = 0; //PPPM shared_data.cuda_timings.pppm_particle_map = 0; shared_data.cuda_timings.pppm_make_rho = 0; shared_data.cuda_timings.pppm_brick2fft = 0; shared_data.cuda_timings.pppm_poisson = 0; shared_data.cuda_timings.pppm_fillbrick = 0; shared_data.cuda_timings.pppm_fieldforce = 0; shared_data.cuda_timings.pppm_compute = 0; CudaWrapper_CheckUploadTime(true); CudaWrapper_CheckDownloadTime(true); CudaWrapper_CheckCPUBufUploadTime(true); CudaWrapper_CheckCPUBufDownloadTime(true); } void Cuda::print_timings() { if(universe->me != 0) return; if(not dotiming) return; printf("\n # CUDA: Special timings\n\n"); printf("\n Transfer Times\n"); printf(" PCIe Upload: \t %lf s\n", CudaWrapper_CheckUploadTime()); printf(" PCIe Download:\t %lf s\n", CudaWrapper_CheckDownloadTime()); printf(" CPU Tempbbuf Upload: \t %lf \n", CudaWrapper_CheckCPUBufUploadTime()); printf(" CPU Tempbbuf Download: \t %lf \n", CudaWrapper_CheckCPUBufDownloadTime()); printf("\n Communication \n"); printf(" Forward Total \t %lf \n", shared_data.cuda_timings.comm_forward_total); printf(" Forward MPI Upper Bound \t %lf \n", shared_data.cuda_timings.comm_forward_mpi_upper); printf(" Forward MPI Lower Bound \t %lf \n", shared_data.cuda_timings.comm_forward_mpi_lower); printf(" Forward Kernel Pack \t %lf \n", shared_data.cuda_timings.comm_forward_kernel_pack); printf(" Forward Kernel Unpack \t %lf \n", shared_data.cuda_timings.comm_forward_kernel_unpack); printf(" Forward Kernel Self \t %lf \n", shared_data.cuda_timings.comm_forward_kernel_self); printf(" Forward Upload \t %lf \n", shared_data.cuda_timings.comm_forward_upload); printf(" Forward Download \t %lf \n", shared_data.cuda_timings.comm_forward_download); printf(" Forward Overlap Split Ratio\t %lf \n", shared_data.comm.overlap_split_ratio); printf("\n"); printf(" Exchange Total \t %lf \n", shared_data.cuda_timings.comm_exchange_total); printf(" Exchange MPI \t %lf \n", shared_data.cuda_timings.comm_exchange_mpi); printf(" Exchange Kernel Pack \t %lf \n", shared_data.cuda_timings.comm_exchange_kernel_pack); printf(" Exchange Kernel Unpack \t %lf \n", shared_data.cuda_timings.comm_exchange_kernel_unpack); printf(" Exchange Kernel Fill \t %lf \n", shared_data.cuda_timings.comm_exchange_kernel_fill); printf(" Exchange CPU Pack \t %lf \n", shared_data.cuda_timings.comm_exchange_cpu_pack); printf(" Exchange Upload \t %lf \n", shared_data.cuda_timings.comm_exchange_upload); printf(" Exchange Download \t %lf \n", shared_data.cuda_timings.comm_exchange_download); printf("\n"); printf(" Border Total \t %lf \n", shared_data.cuda_timings.comm_border_total); printf(" Border MPI \t %lf \n", shared_data.cuda_timings.comm_border_mpi); printf(" Border Kernel Pack \t %lf \n", shared_data.cuda_timings.comm_border_kernel_pack); printf(" Border Kernel Unpack \t %lf \n", shared_data.cuda_timings.comm_border_kernel_unpack); printf(" Border Kernel Self \t %lf \n", shared_data.cuda_timings.comm_border_kernel_self); printf(" Border Kernel BuildList \t %lf \n", shared_data.cuda_timings.comm_border_kernel_buildlist); printf(" Border Upload \t %lf \n", shared_data.cuda_timings.comm_border_upload); printf(" Border Download \t %lf \n", shared_data.cuda_timings.comm_border_download); printf("\n"); //pair forces printf(" Pair XType Conversion \t %lf \n", shared_data.cuda_timings.pair_xtype_conversion); printf(" Pair Kernel \t %lf \n", shared_data.cuda_timings.pair_kernel); printf(" Pair Virial \t %lf \n", shared_data.cuda_timings.pair_virial); printf(" Pair Force Collection \t %lf \n", shared_data.cuda_timings.pair_force_collection); printf("\n"); //neighbor printf(" Neighbor Binning \t %lf \n", shared_data.cuda_timings.neigh_bin); printf(" Neighbor Build \t %lf \n", shared_data.cuda_timings.neigh_build); printf(" Neighbor Special \t %lf \n", shared_data.cuda_timings.neigh_special); printf("\n"); //pppm if(force->kspace) { printf(" PPPM Total \t %lf \n", shared_data.cuda_timings.pppm_compute); printf(" PPPM Particle Map \t %lf \n", shared_data.cuda_timings.pppm_particle_map); printf(" PPPM Make Rho \t %lf \n", shared_data.cuda_timings.pppm_make_rho); printf(" PPPM Brick2fft \t %lf \n", shared_data.cuda_timings.pppm_brick2fft); printf(" PPPM Poisson \t %lf \n", shared_data.cuda_timings.pppm_poisson); printf(" PPPM Fillbrick \t %lf \n", shared_data.cuda_timings.pppm_fillbrick); printf(" PPPM Fieldforce \t %lf \n", shared_data.cuda_timings.pppm_fieldforce); printf("\n"); } printf(" Debug Test 1 \t %lf \n", shared_data.cuda_timings.test1); printf(" Debug Test 2 \t %lf \n", shared_data.cuda_timings.test2); printf("\n"); } diff --git a/src/USER-CUDA/fix_aveforce_cuda.h b/src/USER-CUDA/fix_aveforce_cuda.h index 07dfdb4e5..c22e702ee 100644 --- a/src/USER-CUDA/fix_aveforce_cuda.h +++ b/src/USER-CUDA/fix_aveforce_cuda.h @@ -1,69 +1,68 @@ /* -*- c++ -*- ---------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator Original Version: http://lammps.sandia.gov, Sandia National Laboratories Steve Plimpton, sjplimp@sandia.gov See the README file in the top-level LAMMPS directory. ----------------------------------------------------------------------- USER-CUDA Package and associated modifications: https://sourceforge.net/projects/lammpscuda/ Christian Trott, christian.trott@tu-ilmenau.de Lars Winterfeld, lars.winterfeld@tu-ilmenau.de Theoretical Physics II, University of Technology Ilmenau, Germany See the README file in the USER-CUDA directory. This software is distributed under the GNU General Public License. ------------------------------------------------------------------------- */ #ifdef FIX_CLASS FixStyle(aveforce/cuda,FixAveForceCuda) #else #ifndef LMP_FIX_AVE_FORCE_CUDA_H #define LMP_FIX_AVE_FORCE_CUDA_H #include "fix.h" #include "cuda_data.h" namespace LAMMPS_NS { class FixAveForceCuda : public Fix { public: FixAveForceCuda(class LAMMPS *, int, char **); ~FixAveForceCuda(); int setmask(); void init(); void setup(int); void min_setup(int); void post_force(int); void post_force_respa(int, int, int); void min_post_force(int); double compute_vector(int); private: class Cuda *cuda; char *xstr,*ystr,*zstr; int iregion; double xvalue,yvalue,zvalue; double foriginal_all[4]; double foriginal[4]; cCudaData* cu_foriginal; - int nlevels_respa; int varflag; int xvar,yvar,zvar,xstyle,ystyle,zstyle; }; } #endif #endif diff --git a/src/USER-CUDA/fix_shake_cuda.h b/src/USER-CUDA/fix_shake_cuda.h index 03b2d79da..577ea1daa 100644 --- a/src/USER-CUDA/fix_shake_cuda.h +++ b/src/USER-CUDA/fix_shake_cuda.h @@ -1,132 +1,130 @@ /* -*- c++ -*- ---------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator http://lammps.sandia.gov, Sandia National Laboratories Steve Plimpton, sjplimp@sandia.gov Copyright (2003) Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. This software is distributed under the GNU General Public License. See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ #ifdef FIX_CLASS FixStyle(shake/cuda,FixShakeCuda) #else #ifndef LMP_FIX_SHAKE_CUDA_H #define LMP_FIX_SHAKE_CUDA_H #include "fix.h" #include "cuda_data.h" #include "cuda_precision.h" namespace LAMMPS_NS { class FixShakeCuda : public Fix { public: FixShakeCuda(class LAMMPS *, int, char **); ~FixShakeCuda(); int setmask(); void init(); void setup(int); void pre_neighbor(); void post_force(int); //void post_force_respa(int, int, int); double memory_usage(); void grow_arrays(int); void copy_arrays(int, int, int); void set_arrays(int); int pack_exchange(int, double *); int unpack_exchange(int, double *); int pack_forward_comm(int, int *, double *, int, int *); void unpack_forward_comm(int, int, double *); int dof(int); void reset_dt(); double time_postforce; private: class Cuda *cuda; int me,nprocs; double tolerance; // SHAKE tolerance int max_iter; // max # of SHAKE iterations int output_every; // SHAKE stat output every so often int next_output; // timestep for next output // settings from input command int *bond_flag,*angle_flag; // bond/angle types to constrain int *type_flag; // constrain bonds to these types double *mass_list; // constrain bonds to these masses int nmass; // # of masses in mass_list bool neighbor_step; // was neighboring done in this step -> need to run the Cuda_FixShake_Init double *bond_distance,*angle_distance; // constraint distances cCudaData* cu_bond_distance; cCudaData* cu_angle_distance; int ifix_respa; // rRESPA fix needed by SHAKE int nlevels_respa; // copies of needed rRESPA variables int *loop_respa; double *step_respa; double **x,**v,**f; // local ptrs to atom class quantities double *mass,*rmass; int *type; int nlocal; // atom-based arrays int *shake_flag; // 0 if atom not in SHAKE cluster // 1 = size 3 angle cluster // 2,3,4 = size of bond-only cluster int **shake_atom; // global IDs of atoms in cluster // central atom is 1st // lowest global ID is 1st for size 2 int **shake_type; // bondtype of each bond in cluster // for angle cluster, 3rd value // is angletype double **xshake; // unconstrained atom coords cCudaData* cu_shake_flag; cCudaData* cu_shake_atom; cCudaData* cu_shake_type; cCudaData* cu_xshake; cCudaData* cu_list; cCudaData* cu_virial; - int* countoccur; - int vflag; // virial flag double dtv,dtfsq; // timesteps for trial move double dtf_inner,dtf_innerhalf; // timesteps for rRESPA trial move int *list; // list of clusters to SHAKE int nlist,maxlist; // size and max-size of list // stat quantities int *b_count,*b_count_all; // counts for each bond type double *b_ave,*b_max,*b_min; // ave/max/min dist for each bond type double *b_ave_all,*b_max_all,*b_min_all; // MPI summing arrays int *a_count,*a_count_all; // ditto for angle types double *a_ave,*a_max,*a_min; double *a_ave_all,*a_max_all,*a_min_all; void find_clusters(); void swap_clusters(int i,int j); int masscheck(double); void unconstrained_update(); void shake2(int); void shake3(int); void shake4(int); void shake3angle(int); void stats(); int bondfind(int, int, int); int anglefind(int, int, int); }; } #endif #endif diff --git a/src/USER-EFF/compute_ke_atom_eff.cpp b/src/USER-EFF/compute_ke_atom_eff.cpp index 165a3c401..3c29e801c 100644 --- a/src/USER-EFF/compute_ke_atom_eff.cpp +++ b/src/USER-EFF/compute_ke_atom_eff.cpp @@ -1,115 +1,116 @@ /* ---------------------------------------------------------------------- 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: Andres Jaramillo-Botero ------------------------------------------------------------------------- */ #include "math.h" #include "string.h" +#include "stdlib.h" #include "compute_ke_atom_eff.h" #include "atom.h" #include "update.h" #include "modify.h" #include "comm.h" #include "force.h" #include "memory.h" #include "error.h" #include "domain.h" using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ ComputeKEAtomEff::ComputeKEAtomEff(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg) { if (narg != 3) error->all(FLERR,"Illegal compute ke/atom/eff command"); peratom_flag = 1; size_peratom_cols = 0; nmax = 0; ke = NULL; // error check if (!atom->electron_flag) error->all(FLERR,"Compute ke/atom/eff requires atom style electron"); } /* ---------------------------------------------------------------------- */ ComputeKEAtomEff::~ComputeKEAtomEff() { memory->destroy(ke); } /* ---------------------------------------------------------------------- */ void ComputeKEAtomEff::init() { int count = 0; for (int i = 0; i < modify->ncompute; i++) if (strcmp(modify->compute[i]->style,"ke/atom/eff") == 0) count++; if (count > 1 && comm->me == 0) error->warning(FLERR,"More than one compute ke/atom/eff"); } /* ---------------------------------------------------------------------- */ void ComputeKEAtomEff::compute_peratom() { invoked_peratom = update->ntimestep; // grow ke array if necessary if (atom->nlocal > nmax) { memory->destroy(ke); nmax = atom->nmax; memory->create(ke,nmax,"compute/ke/atom/eff:ke"); vector_atom = ke; } // compute kinetic energy for each atom in group double mvv2e = force->mvv2e; double **v = atom->v; double *ervel = atom->ervel; double *mass = atom->mass; int *spin = atom->spin; int *mask = atom->mask; int *type = atom->type; int nlocal = atom->nlocal; double mefactor = domain->dimension/4.0; if (mass) for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { ke[i] = 0.5 * mvv2e * mass[type[i]] * (v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]); - if (fabs(spin[i])==1) + if (abs(spin[i])==1) ke[i] += 0.5 * mvv2e * mefactor * mass[type[i]] * ervel[i]*ervel[i]; } else ke[i] = 0.0; } } /* ---------------------------------------------------------------------- memory usage of local atom-based array ------------------------------------------------------------------------- */ double ComputeKEAtomEff::memory_usage() { double bytes = nmax * sizeof(double); return bytes; } diff --git a/src/USER-EFF/compute_ke_eff.cpp b/src/USER-EFF/compute_ke_eff.cpp index 2549ca93a..7d3487b69 100644 --- a/src/USER-EFF/compute_ke_eff.cpp +++ b/src/USER-EFF/compute_ke_eff.cpp @@ -1,82 +1,83 @@ /* ---------------------------------------------------------------------- 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: Andres Jaramillo-Botero ------------------------------------------------------------------------- */ #include "mpi.h" #include "math.h" +#include "stdlib.h" #include "compute_ke_eff.h" #include "atom.h" #include "update.h" #include "force.h" #include "domain.h" #include "group.h" #include "error.h" using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ ComputeKEEff::ComputeKEEff(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg) { if (narg != 3) error->all(FLERR,"Illegal compute ke/eff command"); scalar_flag = 1; extscalar = 1; // error check if (!atom->electron_flag) error->all(FLERR,"Compute ke/eff requires atom style electron"); } /* ---------------------------------------------------------------------- */ void ComputeKEEff::init() { pfactor = 0.5 * force->mvv2e; } /* ---------------------------------------------------------------------- */ double ComputeKEEff::compute_scalar() { invoked_scalar = update->ntimestep; double **v = atom->v; double *ervel = atom->ervel; double *mass = atom->mass; int *spin = atom->spin; int *mask = atom->mask; int *type = atom->type; int nlocal = atom->nlocal; double mefactor = domain->dimension/4.0; double ke = 0.0; if (mass) { for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { ke += mass[type[i]]*(v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]); - if (fabs(spin[i])==1) ke += mefactor*mass[type[i]]*ervel[i]*ervel[i]; + if (abs(spin[i])==1) ke += mefactor*mass[type[i]]*ervel[i]*ervel[i]; } } MPI_Allreduce(&ke,&scalar,1,MPI_DOUBLE,MPI_SUM,world); scalar *= pfactor; return scalar; } diff --git a/src/USER-EFF/compute_temp_deform_eff.cpp b/src/USER-EFF/compute_temp_deform_eff.cpp index 45639145c..30729097a 100644 --- a/src/USER-EFF/compute_temp_deform_eff.cpp +++ b/src/USER-EFF/compute_temp_deform_eff.cpp @@ -1,323 +1,324 @@ /* ---------------------------------------------------------------------- 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: Andres Jaramillo-Botero (Caltech) ------------------------------------------------------------------------- */ #include "mpi.h" #include "math.h" #include "string.h" +#include "stdlib.h" #include "compute_temp_deform_eff.h" #include "domain.h" #include "atom.h" #include "update.h" #include "force.h" #include "modify.h" #include "fix.h" #include "fix_deform.h" #include "group.h" #include "comm.h" #include "memory.h" #include "error.h" using namespace LAMMPS_NS; enum{NO_REMAP,X_REMAP,V_REMAP}; // same as fix_deform.cpp /* ---------------------------------------------------------------------- */ ComputeTempDeformEff::ComputeTempDeformEff(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg) { if (narg != 3) error->all(FLERR,"Illegal compute temp/deform/eff command"); if (!atom->electron_flag) error->all(FLERR,"Compute temp/deform/eff requires atom style electron"); scalar_flag = vector_flag = 1; size_vector = 6; extscalar = 0; extvector = 1; tempflag = 1; tempbias = 1; maxbias = 0; vbiasall = NULL; vector = new double[6]; } /* ---------------------------------------------------------------------- */ ComputeTempDeformEff::~ComputeTempDeformEff() { memory->destroy(vbiasall); delete [] vector; } /* ---------------------------------------------------------------------- */ void ComputeTempDeformEff::init() { // check fix deform remap settings int i; for (i = 0; i < modify->nfix; i++) if (strcmp(modify->fix[i]->style,"deform") == 0) { if (((FixDeform *) modify->fix[i])->remapflag == X_REMAP && comm->me == 0) error->warning(FLERR,"Using compute temp/deform/eff with inconsistent " "fix deform remap option"); break; } if (i == modify->nfix && comm->me == 0) error->warning(FLERR, "Using compute temp/deform/eff with no fix deform defined"); } /* ---------------------------------------------------------------------- */ void ComputeTempDeformEff::setup() { dynamic = 0; if (dynamic_user || group->dynamic[igroup]) dynamic = 1; fix_dof = -1; dof_compute(); } /* ---------------------------------------------------------------------- */ void ComputeTempDeformEff::dof_compute() { double natoms = group->count(igroup); dof = domain->dimension * natoms; dof -= extra_dof + fix_dof; // just include nuclear dof int *spin = atom->spin; int *mask = atom->mask; int nlocal = atom->nlocal; int one = 0; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { - if (fabs(spin[i]) == 1) one++; + if (abs(spin[i]) == 1) one++; } int nelectrons; MPI_Allreduce(&one,&nelectrons,1,MPI_INT,MPI_SUM,world); // Assume 3/2 k T per nucleus dof -= domain->dimension * nelectrons; if (dof > 0) tfactor = force->mvv2e / (dof * force->boltz); else tfactor = 0.0; } /* ---------------------------------------------------------------------- */ double ComputeTempDeformEff::compute_scalar() { double lamda[3],vstream[3],vthermal[3]; invoked_scalar = update->ntimestep; double **x = atom->x; double **v = atom->v; double *ervel = atom->ervel; double *mass = atom->mass; int *spin = atom->spin; int *type = atom->type; int *mask = atom->mask; int nlocal = atom->nlocal; double mefactor = domain->dimension/4.0; // lamda = 0-1 triclinic lamda coords // vstream = streaming velocity = Hrate*lamda + Hratelo // vthermal = thermal velocity = v - vstream double *h_rate = domain->h_rate; double *h_ratelo = domain->h_ratelo; double t = 0.0; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { domain->x2lamda(x[i],lamda); vstream[0] = h_rate[0]*lamda[0] + h_rate[5]*lamda[1] + h_rate[4]*lamda[2] + h_ratelo[0]; vstream[1] = h_rate[1]*lamda[1] + h_rate[3]*lamda[2] + h_ratelo[1]; vstream[2] = h_rate[2]*lamda[2] + h_ratelo[2]; vthermal[0] = v[i][0] - vstream[0]; vthermal[1] = v[i][1] - vstream[1]; vthermal[2] = v[i][2] - vstream[2]; if (mass) { t += (vthermal[0]*vthermal[0] + vthermal[1]*vthermal[1] + vthermal[2]*vthermal[2])* mass[type[i]]; - if (fabs(spin[i])==1) t += mefactor*mass[type[i]]*ervel[i]*ervel[i]; + if (abs(spin[i])==1) t += mefactor*mass[type[i]]*ervel[i]*ervel[i]; } } MPI_Allreduce(&t,&scalar,1,MPI_DOUBLE,MPI_SUM,world); if (dynamic) dof_compute(); scalar *= tfactor; return scalar; } /* ---------------------------------------------------------------------- */ void ComputeTempDeformEff::compute_vector() { double lamda[3],vstream[3],vthermal[3]; invoked_vector = update->ntimestep; double **x = atom->x; double **v = atom->v; double *ervel = atom->ervel; double *mass = atom->mass; int *spin = atom->spin; int *type = atom->type; int *mask = atom->mask; int nlocal = atom->nlocal; double mefactor = domain->dimension/4.0; double *h_rate = domain->h_rate; double *h_ratelo = domain->h_ratelo; double massone,t[6]; for (int i = 0; i < 6; i++) t[i] = 0.0; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { domain->x2lamda(x[i],lamda); vstream[0] = h_rate[0]*lamda[0] + h_rate[5]*lamda[1] + h_rate[4]*lamda[2] + h_ratelo[0]; vstream[1] = h_rate[1]*lamda[1] + h_rate[3]*lamda[2] + h_ratelo[1]; vstream[2] = h_rate[2]*lamda[2] + h_ratelo[2]; vthermal[0] = v[i][0] - vstream[0]; vthermal[1] = v[i][1] - vstream[1]; vthermal[2] = v[i][2] - vstream[2]; massone = mass[type[i]]; t[0] += massone * vthermal[0]*vthermal[0]; t[1] += massone * vthermal[1]*vthermal[1]; t[2] += massone * vthermal[2]*vthermal[2]; t[3] += massone * vthermal[0]*vthermal[1]; t[4] += massone * vthermal[0]*vthermal[2]; t[5] += massone * vthermal[1]*vthermal[2]; - if (fabs(spin[i])==1) { + if (abs(spin[i])==1) { t[0] += mefactor * massone * ervel[i]*ervel[i]; t[1] += mefactor * massone * ervel[i]*ervel[i]; t[2] += mefactor * massone * ervel[i]*ervel[i]; } } MPI_Allreduce(t,vector,6,MPI_DOUBLE,MPI_SUM,world); for (int i = 0; i < 6; i++) vector[i] *= force->mvv2e; } /* ---------------------------------------------------------------------- remove velocity bias from atom I to leave thermal velocity ------------------------------------------------------------------------- */ void ComputeTempDeformEff::remove_bias(int i, double *v) { double lamda[3]; double *h_rate = domain->h_rate; double *h_ratelo = domain->h_ratelo; domain->x2lamda(atom->x[i],lamda); vbias[0] = h_rate[0]*lamda[0] + h_rate[5]*lamda[1] + h_rate[4]*lamda[2] + h_ratelo[0]; vbias[1] = h_rate[1]*lamda[1] + h_rate[3]*lamda[2] + h_ratelo[1]; vbias[2] = h_rate[2]*lamda[2] + h_ratelo[2]; v[0] -= vbias[0]; v[1] -= vbias[1]; v[2] -= vbias[2]; } /* ---------------------------------------------------------------------- remove velocity bias from all atoms to leave thermal velocity NOTE: only removes translational velocity bias from electrons ------------------------------------------------------------------------- */ void ComputeTempDeformEff::remove_bias_all() { double **v = atom->v; int *mask = atom->mask; int nlocal = atom->nlocal; if (nlocal > maxbias) { memory->destroy(vbiasall); maxbias = atom->nmax; memory->create(vbiasall,maxbias,3,"temp/deform/eff:vbiasall"); } double lamda[3]; double *h_rate = domain->h_rate; double *h_ratelo = domain->h_ratelo; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { domain->x2lamda(atom->x[i],lamda); vbiasall[i][0] = h_rate[0]*lamda[0] + h_rate[5]*lamda[1] + h_rate[4]*lamda[2] + h_ratelo[0]; vbiasall[i][1] = h_rate[1]*lamda[1] + h_rate[3]*lamda[2] + h_ratelo[1]; vbiasall[i][2] = h_rate[2]*lamda[2] + h_ratelo[2]; v[i][0] -= vbiasall[i][0]; v[i][1] -= vbiasall[i][1]; v[i][2] -= vbiasall[i][2]; } } /* ---------------------------------------------------------------------- add back in velocity bias to atom I removed by remove_bias() assume remove_bias() was previously called ------------------------------------------------------------------------- */ void ComputeTempDeformEff::restore_bias(int i, double *v) { v[0] += vbias[0]; v[1] += vbias[1]; v[2] += vbias[2]; } /* ---------------------------------------------------------------------- add back in velocity bias to all atoms removed by remove_bias_all() assume remove_bias_all() was previously called ------------------------------------------------------------------------- */ void ComputeTempDeformEff::restore_bias_all() { double **v = atom->v; int *mask = atom->mask; int nlocal = atom->nlocal; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { v[i][0] += vbiasall[i][0]; v[i][1] += vbiasall[i][1]; v[i][2] += vbiasall[i][2]; } } /* ---------------------------------------------------------------------- */ double ComputeTempDeformEff::memory_usage() { double bytes = maxbias * sizeof(double); return bytes; } diff --git a/src/USER-EFF/compute_temp_eff.cpp b/src/USER-EFF/compute_temp_eff.cpp index 4dcf7720c..634bd83c0 100644 --- a/src/USER-EFF/compute_temp_eff.cpp +++ b/src/USER-EFF/compute_temp_eff.cpp @@ -1,165 +1,166 @@ /* ---------------------------------------------------------------------- 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: Andres Jaramillo-Botero (Caltech) ------------------------------------------------------------------------- */ #include "mpi.h" #include "math.h" #include "string.h" +#include "stdlib.h" #include "compute_temp_eff.h" #include "atom.h" #include "update.h" #include "force.h" #include "domain.h" #include "group.h" #include "error.h" using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ ComputeTempEff::ComputeTempEff(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg) { if (!atom->electron_flag) error->all(FLERR,"Compute temp/eff requires atom style electron"); scalar_flag = vector_flag = 1; size_vector = 6; extscalar = 0; extvector = 1; tempflag = 1; vector = new double[size_vector]; } /* ---------------------------------------------------------------------- */ ComputeTempEff::~ComputeTempEff() { delete [] vector; } /* ---------------------------------------------------------------------- */ void ComputeTempEff::setup() { dynamic = 0; if (dynamic_user || group->dynamic[igroup]) dynamic = 1; fix_dof = -1; dof_compute(); } /* ---------------------------------------------------------------------- */ void ComputeTempEff::dof_compute() { if (fix_dof) adjust_dof_fix(); double natoms = group->count(igroup); dof = domain->dimension * natoms; dof -= extra_dof + fix_dof; int *spin = atom->spin; int *mask = atom->mask; int nlocal = atom->nlocal; int one = 0; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { - if (fabs(spin[i])==1) one++; + if (abs(spin[i])==1) one++; } int nelectrons; MPI_Allreduce(&one,&nelectrons,1,MPI_INT,MPI_SUM,world); // Assume 3/2 k T per nucleus dof -= domain->dimension * nelectrons; if (dof > 0.0) tfactor = force->mvv2e / (dof * force->boltz); else tfactor = 0.0; } /* ---------------------------------------------------------------------- */ double ComputeTempEff::compute_scalar() { invoked_scalar = update->ntimestep; double **v = atom->v; double *ervel = atom->ervel; double *mass = atom->mass; int *spin = atom->spin; int *type = atom->type; int *mask = atom->mask; int nlocal = atom->nlocal; double mefactor = domain->dimension/4.0; double t = 0.0; if (mass) { for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { t += (v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]) * mass[type[i]]; - if (fabs(spin[i])==1) t += mefactor*mass[type[i]]*ervel[i]*ervel[i]; + if (abs(spin[i])==1) t += mefactor*mass[type[i]]*ervel[i]*ervel[i]; } } } MPI_Allreduce(&t,&scalar,1,MPI_DOUBLE,MPI_SUM,world); if (dynamic) dof_compute(); scalar *= tfactor; return scalar; } /* ---------------------------------------------------------------------- */ void ComputeTempEff::compute_vector() { int i; invoked_vector = update->ntimestep; double **v = atom->v; double *ervel = atom->ervel; double *mass = atom->mass; int *spin = atom->spin; int *type = atom->type; int *mask = atom->mask; int nlocal = atom->nlocal; double mefactor = domain->dimension/4.0; double massone,t[6]; for (i = 0; i < 6; i++) t[i] = 0.0; for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) { massone = mass[type[i]]; t[0] += massone * v[i][0]*v[i][0]; t[1] += massone * v[i][1]*v[i][1]; t[2] += massone * v[i][2]*v[i][2]; t[3] += massone * v[i][0]*v[i][1]; t[4] += massone * v[i][0]*v[i][2]; t[5] += massone * v[i][1]*v[i][2]; - if (fabs(spin[i])==1) { + if (abs(spin[i])==1) { t[0] += mefactor*massone*ervel[i]*ervel[i]; t[1] += mefactor*massone*ervel[i]*ervel[i]; t[2] += mefactor*massone*ervel[i]*ervel[i]; } } MPI_Allreduce(t,vector,6,MPI_DOUBLE,MPI_SUM,world); for (i = 0; i < 6; i++) vector[i] *= force->mvv2e; } diff --git a/src/USER-EFF/compute_temp_region_eff.cpp b/src/USER-EFF/compute_temp_region_eff.cpp index 6c86256eb..d2517d879 100644 --- a/src/USER-EFF/compute_temp_region_eff.cpp +++ b/src/USER-EFF/compute_temp_region_eff.cpp @@ -1,295 +1,296 @@ /* ---------------------------------------------------------------------- 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: Andres Jaramillo-Botero (Caltech) ------------------------------------------------------------------------- */ #include "mpi.h" #include "math.h" #include "string.h" +#include "stdlib.h" #include "compute_temp_region_eff.h" #include "atom.h" #include "update.h" #include "force.h" #include "modify.h" #include "domain.h" #include "region.h" #include "group.h" #include "memory.h" #include "error.h" using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ ComputeTempRegionEff::ComputeTempRegionEff(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg) { if (!atom->electron_flag) error->all(FLERR,"Compute temp/region/eff requires atom style electron"); if (narg != 4) error->all(FLERR,"Illegal compute temp/region/eff command"); iregion = domain->find_region(arg[3]); if (iregion == -1) error->all(FLERR,"Region ID for compute temp/region/eff does not exist"); int n = strlen(arg[3]) + 1; idregion = new char[n]; strcpy(idregion,arg[3]); scalar_flag = vector_flag = 1; size_vector = 6; extscalar = 0; extvector = 1; tempflag = 1; tempbias = 1; maxbias = 0; vbiasall = NULL; vector = new double[6]; } /* ---------------------------------------------------------------------- */ ComputeTempRegionEff::~ComputeTempRegionEff() { delete [] idregion; memory->destroy(vbiasall); delete [] vector; } /* ---------------------------------------------------------------------- */ void ComputeTempRegionEff::init() { // set index and check validity of region iregion = domain->find_region(idregion); if (iregion == -1) error->all(FLERR,"Region ID for compute temp/region/eff does not exist"); } /* ---------------------------------------------------------------------- */ void ComputeTempRegionEff::setup() { dynamic = 0; if (dynamic_user || group->dynamic[igroup]) dynamic = 1; dof = 0.0; } /* ---------------------------------------------------------------------- */ void ComputeTempRegionEff::dof_remove_pre() { domain->regions[iregion]->prematch(); } /* ---------------------------------------------------------------------- */ int ComputeTempRegionEff::dof_remove(int i) { double *x = atom->x[i]; if (domain->regions[iregion]->match(x[0],x[1],x[2])) return 0; return 1; } /* ---------------------------------------------------------------------- */ double ComputeTempRegionEff::compute_scalar() { invoked_scalar = update->ntimestep; double **x = atom->x; double **v = atom->v; double *ervel = atom->ervel; double *mass = atom->mass; int *spin = atom->spin; int *type = atom->type; int *mask = atom->mask; int nlocal = atom->nlocal; double mefactor = domain->dimension/4.0; Region *region = domain->regions[iregion]; region->prematch(); int count = 0; int ecount = 0; double t = 0.0; if (mass) { for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit && region->match(x[i][0],x[i][1],x[i][2])) { count++; t += (v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]) * mass[type[i]]; - if (fabs(spin[i])==1) { + if (abs(spin[i])==1) { t += mefactor*mass[type[i]]*ervel[i]*ervel[i]; ecount++; } } } double tarray[2],tarray_all[2]; // Assume 3/2 k T per nucleus tarray[0] = count-ecount; tarray[1] = t; MPI_Allreduce(tarray,tarray_all,2,MPI_DOUBLE,MPI_SUM,world); dof = domain->dimension * tarray_all[0] - extra_dof; int one = 0; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit && region->match(x[i][0],x[i][1],x[i][2])) { - if (fabs(spin[i])==1) one++; + if (abs(spin[i])==1) one++; } if (dof > 0) scalar = force->mvv2e * tarray_all[1] / (dof * force->boltz); else scalar = 0.0; return scalar; } /* ---------------------------------------------------------------------- */ void ComputeTempRegionEff::compute_vector() { int i; invoked_vector = update->ntimestep; double **x = atom->x; double **v = atom->v; double *ervel = atom->ervel; double *mass = atom->mass; int *spin = atom->spin; int *type = atom->type; int *mask = atom->mask; int nlocal = atom->nlocal; double mefactor = domain->dimension/4.0; Region *region = domain->regions[iregion]; region->prematch(); double massone,t[6]; for (i = 0; i < 6; i++) t[i] = 0.0; for (i = 0; i < nlocal; i++) if (mask[i] & groupbit && region->match(x[i][0],x[i][1],x[i][2])) { massone = mass[type[i]]; t[0] += massone * v[i][0]*v[i][0]; t[1] += massone * v[i][1]*v[i][1]; t[2] += massone * v[i][2]*v[i][2]; t[3] += massone * v[i][0]*v[i][1]; t[4] += massone * v[i][0]*v[i][2]; t[5] += massone * v[i][1]*v[i][2]; - if (fabs(spin[i])==1) { + if (abs(spin[i])==1) { t[0] += mefactor * massone * ervel[i]*ervel[i]; t[1] += mefactor * massone * ervel[i]*ervel[i]; t[2] += mefactor * massone * ervel[i]*ervel[i]; } } MPI_Allreduce(t,vector,6,MPI_DOUBLE,MPI_SUM,world); for (i = 0; i < 6; i++) vector[i] *= force->mvv2e; } /* ---------------------------------------------------------------------- remove velocity bias from atom I to leave thermal velocity NOTE: the following commands do not bias the radial electron velocities ------------------------------------------------------------------------- */ void ComputeTempRegionEff::remove_bias(int i, double *v) { double *x = atom->x[i]; if (domain->regions[iregion]->match(x[0],x[1],x[2])) vbias[0] = vbias[1] = vbias[2] = 0.0; else { vbias[0] = v[0]; vbias[1] = v[1]; vbias[2] = v[2]; v[0] = v[1] = v[2] = 0.0; } } /* ---------------------------------------------------------------------- remove velocity bias from all atoms to leave thermal velocity ------------------------------------------------------------------------- */ void ComputeTempRegionEff::remove_bias_all() { double **x = atom->x; double **v = atom->v; int *mask = atom->mask; int nlocal = atom->nlocal; if (nlocal > maxbias) { memory->destroy(vbiasall); maxbias = atom->nmax; memory->create(vbiasall,maxbias,3,"temp/region:vbiasall"); } Region *region = domain->regions[iregion]; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { if (region->match(x[i][0],x[i][1],x[i][2])) vbiasall[i][0] = vbiasall[i][1] = vbiasall[i][2] = 0.0; else { vbiasall[i][0] = v[i][0]; vbiasall[i][1] = v[i][1]; vbiasall[i][2] = v[i][2]; v[i][0] = v[i][1] = v[i][2] = 0.0; } } } /* ---------------------------------------------------------------------- add back in velocity bias to atom I removed by remove_bias() assume remove_bias() was previously called ------------------------------------------------------------------------- */ void ComputeTempRegionEff::restore_bias(int i, double *v) { v[0] += vbias[0]; v[1] += vbias[1]; v[2] += vbias[2]; } /* ---------------------------------------------------------------------- add back in velocity bias to all atoms removed by remove_bias_all() assume remove_bias_all() was previously called ------------------------------------------------------------------------- */ void ComputeTempRegionEff::restore_bias_all() { double **v = atom->v; int *mask = atom->mask; int nlocal = atom->nlocal; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { v[i][0] += vbiasall[i][0]; v[i][1] += vbiasall[i][1]; v[i][2] += vbiasall[i][2]; } } /* ---------------------------------------------------------------------- */ double ComputeTempRegionEff::memory_usage() { double bytes = maxbias * sizeof(double); return bytes; } diff --git a/src/USER-EFF/fix_langevin_eff.cpp b/src/USER-EFF/fix_langevin_eff.cpp index 826c60681..dec460da9 100644 --- a/src/USER-EFF/fix_langevin_eff.cpp +++ b/src/USER-EFF/fix_langevin_eff.cpp @@ -1,433 +1,433 @@ /* ---------------------------------------------------------------------- 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: Andres Jaramillo-Botero ------------------------------------------------------------------------- */ #include "mpi.h" #include "math.h" #include "string.h" #include "stdlib.h" #include "fix_langevin_eff.h" #include "math_extra.h" #include "atom.h" #include "force.h" #include "update.h" #include "modify.h" #include "compute.h" #include "domain.h" #include "region.h" #include "respa.h" #include "comm.h" #include "input.h" #include "variable.h" #include "random_mars.h" #include "memory.h" #include "error.h" #include "group.h" using namespace LAMMPS_NS; using namespace FixConst; enum{NOBIAS,BIAS}; enum{CONSTANT,EQUAL,ATOM}; #define SINERTIA 0.4 // moment of inertia prefactor for sphere #define EINERTIA 0.2 // moment of inertia prefactor for ellipsoid /* ---------------------------------------------------------------------- */ FixLangevinEff::FixLangevinEff(LAMMPS *lmp, int narg, char **arg) : FixLangevin(lmp, narg, arg) { erforcelangevin = NULL; } /* ---------------------------------------------------------------------- */ FixLangevinEff::~FixLangevinEff() { memory->destroy(erforcelangevin); } /* ---------------------------------------------------------------------- */ void FixLangevinEff::post_force(int vflag) { if (tallyflag) post_force_tally(); else post_force_no_tally(); } /* ---------------------------------------------------------------------- */ void FixLangevinEff::post_force_no_tally() { double gamma1,gamma2,t_target; double **v = atom->v; double **f = atom->f; double *ervel = atom->ervel; double *erforce = atom->erforce; int *spin = atom->spin; int *type = atom->type; int *mask = atom->mask; int nlocal = atom->nlocal; double mefactor = domain->dimension/4.0; double sqrtmefactor = sqrt(mefactor); double delta = update->ntimestep - update->beginstep; delta /= update->endstep - update->beginstep; // set current t_target and t_sqrt // if variable temp, evaluate variable, wrap with clear/add // reallocate tforce array if necessary if (tstyle == CONSTANT) { t_target = t_start + delta * (t_stop-t_start); tsqrt = sqrt(t_target); } else { modify->clearstep_compute(); if (tstyle == EQUAL) { t_target = input->variable->compute_equal(tvar); if (t_target < 0.0) error->one(FLERR,"Fix langevin/eff variable returned negative temperature"); tsqrt = sqrt(t_target); } else { if (nlocal > maxatom2) { maxatom2 = atom->nmax; memory->destroy(tforce); memory->create(tforce,maxatom2,"langevin/eff:tforce"); } input->variable->compute_atom(tvar,igroup,tforce,1,0); for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) if (tforce[i] < 0.0) error->one(FLERR, "Fix langevin/eff variable returned negative temperature"); } modify->addstep_compute(update->ntimestep + 1); } // apply damping and thermostat to atoms in group // for BIAS: // calculate temperature since some computes require temp // computed on current nlocal atoms to remove bias // test v = 0 since some computes mask non-participating atoms via v = 0 // and added force has extra term not multiplied by v = 0 // for ZEROFLAG: // sum random force over all atoms in group // subtract sum/particles from each atom in group double fran[4],fsum[4],fsumall[4]; fsum[0] = fsum[1] = fsum[2] = fsum[3] = 0.0; int particles = group->count(igroup); if (zeroflag) { if (particles == 0) error->all(FLERR,"Cannot zero Langevin force of 0 atoms/electrons"); } // find number of electrons in group int dof,fix_dof; dof = domain->dimension * particles; fix_dof = 0; for (int i = 0; i < modify->nfix; i++) fix_dof += modify->fix[i]->dof(igroup); // extra_dof = domain->dimension dof -= domain->dimension + fix_dof; int one = 0; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { - if (fabs(spin[i])==1) one++; + if (abs(spin[i])==1) one++; } int nelectrons, dofelectrons, dofnuclei; MPI_Allreduce(&one,&nelectrons,1,MPI_INT,MPI_SUM,world); dofelectrons = domain->dimension*nelectrons; dofnuclei = dof-dofelectrons; // thermal partitioning factor between nuclei and electrons // extra dof from electron size double gfactor3=(double) (dof+nelectrons)/dofnuclei; if (tbiasflag == NOBIAS) { for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { if (tstyle == ATOM) tsqrt = sqrt(tforce[i]); gamma1 = gfactor1[type[i]] * gfactor3; gamma2 = gfactor2[type[i]] * tsqrt; fran[0] = gamma2*(random->uniform()-0.5); fran[1] = gamma2*(random->uniform()-0.5); fran[2] = gamma2*(random->uniform()-0.5); f[i][0] += gamma1*v[i][0] + fran[0]; f[i][1] += gamma1*v[i][1] + fran[1]; f[i][2] += gamma1*v[i][2] + fran[2]; fsum[0] += fran[0]; fsum[1] += fran[1]; fsum[2] += fran[2]; - if (fabs(spin[i])==1) { + if (abs(spin[i])==1) { fran[3] = sqrtmefactor*gamma2*(random->uniform()-0.5); erforce[i] += mefactor*gamma1*ervel[i]+fran[3]; fsum[3] += fran[3]; } } } } else if (tbiasflag == BIAS) { temperature->compute_scalar(); for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { if (tstyle == ATOM) tsqrt = sqrt(tforce[i]); gamma1 = gfactor1[type[i]] * gfactor3; gamma2 = gfactor2[type[i]] * tsqrt; temperature->remove_bias(i,v[i]); fran[0] = gamma2*(random->uniform()-0.5); fran[1] = gamma2*(random->uniform()-0.5); fran[2] = gamma2*(random->uniform()-0.5); if (v[i][0] != 0.0) f[i][0] += gamma1*v[i][0] + fran[0]; if (v[i][1] != 0.0) f[i][1] += gamma1*v[i][1] + fran[1]; if (v[i][2] != 0.0) f[i][2] += gamma1*v[i][2] + fran[2]; fsum[0] += fran[0]; fsum[1] += fran[1]; fsum[2] += fran[2]; - if (fabs(spin[i])==1) { + if (abs(spin[i])==1) { fran[3] = sqrtmefactor*gamma2*(random->uniform()-0.5); if (ervel[i] != 0.0) erforce[i] += mefactor*gamma1*ervel[i]+fran[3]; fsum[3] += fran[3]; } temperature->restore_bias(i,v[i]); } } } // set total force to zero if (zeroflag) { MPI_Allreduce(fsum,fsumall,3,MPI_DOUBLE,MPI_SUM,world); fsumall[0] /= particles; fsumall[1] /= particles; fsumall[2] /= particles; fsumall[3] /= nelectrons; for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { f[i][0] -= fsumall[0]; f[i][1] -= fsumall[1]; f[i][2] -= fsumall[2]; - if (fabs(spin[i])==1) erforce[i] -= fsumall[3]; + if (abs(spin[i])==1) erforce[i] -= fsumall[3]; } } } } /* ---------------------------------------------------------------------- */ void FixLangevinEff::post_force_tally() { double gamma1,gamma2,t_target; // reallocate flangevin and erforcelangevin if necessary if (atom->nlocal > maxatom1) { memory->destroy(flangevin); memory->destroy(erforcelangevin); maxatom1 = atom->nmax; memory->create(flangevin,maxatom1,3,"langevin/eff:flangevin"); memory->create(erforcelangevin,maxatom1,"langevin/eff:erforcelangevin"); } double **v = atom->v; double **f = atom->f; double *erforce = atom->erforce; double *ervel = atom->ervel; int *spin = atom->spin; double mefactor = domain->dimension/4.0; double sqrtmefactor = sqrt(mefactor); int *type = atom->type; int *mask = atom->mask; int nlocal = atom->nlocal; double delta = update->ntimestep - update->beginstep; delta /= update->endstep - update->beginstep; // set current t_target and t_sqrt // if variable temp, evaluate variable, wrap with clear/add // reallocate tforce array if necessary if (tstyle == CONSTANT) { t_target = t_start + delta * (t_stop-t_start); tsqrt = sqrt(t_target); } else { modify->clearstep_compute(); if (tstyle == EQUAL) { t_target = input->variable->compute_equal(tvar); if (t_target < 0.0) error->one(FLERR,"Fix langevin/eff variable returned negative temperature"); tsqrt = sqrt(t_target); } else { if (nlocal > maxatom2) { maxatom2 = atom->nmax; memory->destroy(tforce); memory->create(tforce,maxatom2,"langevin/eff:tforce"); } input->variable->compute_atom(tvar,igroup,tforce,1,0); for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) if (tforce[i] < 0.0) error->one(FLERR, "Fix langevin/eff variable returned negative temperature"); } modify->addstep_compute(update->ntimestep + 1); } // apply damping and thermostat to appropriate atoms // for BIAS: // calculate temperature since some computes require temp // computed on current nlocal atoms to remove bias // test v = 0 since some computes mask non-participating atoms via v = 0 // and added force has extra term not multiplied by v = 0 int particles = group->count(igroup); if (zeroflag) { if (particles == 0) error->all(FLERR,"Cannot zero Langevin force of 0 atoms/electrons"); } // find number of electrons in group int dof,fix_dof; dof = domain->dimension * particles; fix_dof = 0; for (int i = 0; i < modify->nfix; i++) fix_dof += modify->fix[i]->dof(igroup); // extra_dof = domain->dimension dof -= domain->dimension + fix_dof; int one = 0; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { - if (fabs(spin[i])==1) one++; + if (abs(spin[i])==1) one++; } int nelectrons, dofelectrons, dofnuclei; MPI_Allreduce(&one,&nelectrons,1,MPI_INT,MPI_SUM,world); dofelectrons = domain->dimension*nelectrons; dofnuclei = dof-dofelectrons; // thermal partitioning factor between nuclei and electrons // extra dof from electron size double gfactor3=(double) (dof+nelectrons)/dofnuclei; if (tbiasflag == NOBIAS) { for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { if (tstyle == ATOM) tsqrt = sqrt(tforce[i]); gamma1 = gfactor1[type[i]] * gfactor3; gamma2 = gfactor2[type[i]] * tsqrt; flangevin[i][0] = gamma1*v[i][0] + gamma2*(random->uniform()-0.5); flangevin[i][1] = gamma1*v[i][1] + gamma2*(random->uniform()-0.5); flangevin[i][2] = gamma1*v[i][2] + gamma2*(random->uniform()-0.5); f[i][0] += flangevin[i][0]; f[i][1] += flangevin[i][1]; f[i][2] += flangevin[i][2]; - if (fabs(spin[i])==1) { + if (abs(spin[i])==1) { erforcelangevin[i] = mefactor*gamma1*ervel[i]+sqrtmefactor*gamma2*(random->uniform()-0.5); erforce[i] += erforcelangevin[i]; } } } } else if (tbiasflag == BIAS) { temperature->compute_scalar(); for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { if (tstyle == ATOM) tsqrt = sqrt(tforce[i]); gamma1 = gfactor1[type[i]] * gfactor3; gamma2 = gfactor2[type[i]] * tsqrt; temperature->remove_bias(i,v[i]); flangevin[i][0] = gamma1*v[i][0] + gamma2*(random->uniform()-0.5); flangevin[i][1] = gamma1*v[i][1] + gamma2*(random->uniform()-0.5); flangevin[i][2] = gamma1*v[i][2] + gamma2*(random->uniform()-0.5); if (v[i][0] != 0.0) f[i][0] += flangevin[i][0]; else flangevin[i][0] = 0.0; if (v[i][1] != 0.0) f[i][1] += flangevin[i][1]; else flangevin[i][1] = 0.0; if (v[i][2] != 0.0) f[i][2] += flangevin[i][2]; else flangevin[i][2] = 0.0; - if (fabs(spin[i])==1) { + if (abs(spin[i])==1) { erforcelangevin[i] = mefactor*gamma1*ervel[i]+sqrtmefactor*gamma2*(random->uniform()-0.5); if (ervel[i] != 0.0) erforce[i] += erforcelangevin[i]; else erforcelangevin[i] = 0.0; } temperature->restore_bias(i,v[i]); } } } } /* ---------------------------------------------------------------------- tally energy transfer to thermal reservoir ------------------------------------------------------------------------- */ void FixLangevinEff::end_of_step() { if (!tallyflag) return; double **v = atom->v; int *mask = atom->mask; int nlocal = atom->nlocal; int *spin = atom->spin; energy_onestep = 0.0; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { energy_onestep += flangevin[i][0]*v[i][0] + flangevin[i][1]*v[i][1] + flangevin[i][2]*v[i][2]; - if (fabs(spin[i])==1) energy_onestep += erforcelangevin[i]; + if (abs(spin[i])==1) energy_onestep += erforcelangevin[i]; } energy += energy_onestep*update->dt; } /* ---------------------------------------------------------------------- */ double FixLangevinEff::compute_scalar() { if (!tallyflag || flangevin == NULL || erforcelangevin == NULL) return 0.0; // capture the very first energy transfer to thermal reservoir double **v = atom->v; int *mask = atom->mask; int nlocal = atom->nlocal; int *spin = atom->spin; if (update->ntimestep == update->beginstep) { energy_onestep = 0.0; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { energy_onestep += flangevin[i][0]*v[i][0] + flangevin[i][1]*v[i][1] + flangevin[i][2]*v[i][2]; - if (fabs(spin[i])==1) energy_onestep += erforcelangevin[i]; + if (abs(spin[i])==1) energy_onestep += erforcelangevin[i]; } energy = 0.5*energy_onestep*update->dt; } double energy_me = energy - 0.5*energy_onestep*update->dt; double energy_all; MPI_Allreduce(&energy_me,&energy_all,1,MPI_DOUBLE,MPI_SUM,world); return -energy_all; } diff --git a/src/USER-EFF/fix_nh_eff.cpp b/src/USER-EFF/fix_nh_eff.cpp index 1f996a565..ca38fc7e7 100644 --- a/src/USER-EFF/fix_nh_eff.cpp +++ b/src/USER-EFF/fix_nh_eff.cpp @@ -1,112 +1,113 @@ /* ---------------------------------------------------------------------- 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: Andres Jaramillo-Botero (Caltech) ------------------------------------------------------------------------- */ #include "math.h" +#include "stdlib.h" #include "fix_nh_eff.h" #include "atom.h" #include "atom_vec.h" #include "group.h" #include "error.h" #include "domain.h" using namespace LAMMPS_NS; using namespace FixConst; enum{NOBIAS,BIAS}; /* ---------------------------------------------------------------------- */ FixNHEff::FixNHEff(LAMMPS *lmp, int narg, char **arg) : FixNH(lmp, narg, arg) { if (!atom->electron_flag) error->all(FLERR,"Fix nvt/nph/npt/eff requires atom style electron"); } /* ---------------------------------------------------------------------- perform half-step update of electron radial velocities -----------------------------------------------------------------------*/ void FixNHEff::nve_v() { // standard nve_v velocity update FixNH::nve_v(); double *erforce = atom->erforce; double *ervel = atom->ervel; double *mass = atom->mass; int *spin = atom->spin; double mefactor = domain->dimension/4.0; int *type = atom->type; int *mask = atom->mask; int nlocal = atom->nlocal; if (igroup == atom->firstgroup) nlocal = atom->nfirst; double dtfm; for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { - if (fabs(spin[i])==1) { + if (abs(spin[i])==1) { dtfm = dtf / mass[type[i]]; ervel[i] = dtfm * erforce[i] / mefactor; } } } } /* ---------------------------------------------------------------------- perform full-step update of electron radii -----------------------------------------------------------------------*/ void FixNHEff::nve_x() { // standard nve_x position update FixNH::nve_x(); double *eradius = atom->eradius; double *ervel = atom->ervel; int *spin = atom->spin; int *mask = atom->mask; int nlocal = atom->nlocal; if (igroup == atom->firstgroup) nlocal = atom->nfirst; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) - if (fabs(spin[i])==1) eradius[i] += dtv * ervel[i]; + if (abs(spin[i])==1) eradius[i] += dtv * ervel[i]; } /* ---------------------------------------------------------------------- perform half-step scaling of electron radial velocities -----------------------------------------------------------------------*/ void FixNHEff::nh_v_temp() { // standard nh_v_temp velocity scaling FixNH::nh_v_temp(); double *ervel = atom->ervel; int *spin = atom->spin; int *mask = atom->mask; int nlocal = atom->nlocal; if (igroup == atom->firstgroup) nlocal = atom->nfirst; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) - if (fabs(spin[i])==1) ervel[i] *= factor_eta; + if (abs(spin[i])==1) ervel[i] *= factor_eta; } diff --git a/src/USER-EFF/fix_nve_eff.cpp b/src/USER-EFF/fix_nve_eff.cpp index afe442ba5..d1aefaa76 100644 --- a/src/USER-EFF/fix_nve_eff.cpp +++ b/src/USER-EFF/fix_nve_eff.cpp @@ -1,174 +1,175 @@ /* ---------------------------------------------------------------------- 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: Andres Jaramillo-Botero (Caltech) ------------------------------------------------------------------------- */ #include "math.h" #include "stdio.h" #include "string.h" +#include "stdlib.h" #include "fix_nve_eff.h" #include "atom.h" #include "force.h" #include "update.h" #include "respa.h" #include "error.h" #include "math.h" #include "domain.h" using namespace LAMMPS_NS; using namespace FixConst; /* ---------------------------------------------------------------------- */ FixNVEEff::FixNVEEff(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { if (!atom->electron_flag) error->all(FLERR,"Fix nve/eff requires atom style electron"); time_integrate = 1; } /* ---------------------------------------------------------------------- */ int FixNVEEff::setmask() { int mask = 0; mask |= INITIAL_INTEGRATE; mask |= FINAL_INTEGRATE; mask |= INITIAL_INTEGRATE_RESPA; mask |= FINAL_INTEGRATE_RESPA; return mask; } /* ---------------------------------------------------------------------- */ void FixNVEEff::init() { dtv = update->dt; dtf = 0.5 * update->dt * force->ftm2v; if (strstr(update->integrate_style,"respa")) step_respa = ((Respa *) update->integrate)->step; } /* ---------------------------------------------------------------------- allow for both per-type and per-atom mass ------------------------------------------------------------------------- */ void FixNVEEff::initial_integrate(int vflag) { double dtfm; // update v,vr and x,radius of atoms in group double **x = atom->x; double *eradius = atom->eradius; double **v = atom->v; double *ervel = atom->ervel; double **f = atom->f; double *erforce = atom->erforce; double *mass = atom->mass; int *spin = atom->spin; double mefactor = domain->dimension/4.0; int *type = atom->type; int *mask = atom->mask; int nlocal = atom->nlocal; if (igroup == atom->firstgroup) nlocal = atom->nfirst; // x + dt * [v + 0.5 * dt * (f / m)]; if (mass) { for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { dtfm = dtf / mass[type[i]]; v[i][0] += dtfm * f[i][0]; v[i][1] += dtfm * f[i][1]; v[i][2] += dtfm * f[i][2]; x[i][0] += dtv * v[i][0]; x[i][1] += dtv * v[i][1]; x[i][2] += dtv * v[i][2]; - if (fabs(spin[i])==1) { + if (abs(spin[i])==1) { ervel[i] += dtfm * erforce[i] / mefactor; eradius[i] += dtv * ervel[i]; } } } } } /* ---------------------------------------------------------------------- */ void FixNVEEff::final_integrate() { double dtfm; double **v = atom->v; double *ervel = atom->ervel; double *erforce = atom->erforce; double **f = atom->f; double *mass = atom->mass; int *spin = atom->spin; double mefactor = domain->dimension/4.0; int *type = atom->type; int *mask = atom->mask; int nlocal = atom->nlocal; if (igroup == atom->firstgroup) nlocal = atom->nfirst; // dyn_v[i] += m * dt * dyn_f[i]; if (mass) { for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { dtfm = dtf / mass[type[i]]; v[i][0] += dtfm * f[i][0]; v[i][1] += dtfm * f[i][1]; v[i][2] += dtfm * f[i][2]; - if (fabs(spin[i])==1) + if (abs(spin[i])==1) ervel[i] += dtfm * erforce[i] / mefactor; } } } } /* ---------------------------------------------------------------------- */ void FixNVEEff::initial_integrate_respa(int vflag, int ilevel, int iloop) { dtv = step_respa[ilevel]; dtf = 0.5 * step_respa[ilevel] * force->ftm2v; // innermost level - NVE update of v and x // all other levels - NVE update of v if (ilevel == 0) initial_integrate(vflag); else final_integrate(); } /* ---------------------------------------------------------------------- */ void FixNVEEff::final_integrate_respa(int ilevel, int iloop) { dtf = 0.5 * step_respa[ilevel] * force->ftm2v; final_integrate(); } /* ---------------------------------------------------------------------- */ void FixNVEEff::reset_dt() { dtv = update->dt; dtf = 0.5 * update->dt * force->ftm2v; } diff --git a/src/USER-EFF/fix_nvt_sllod_eff.cpp b/src/USER-EFF/fix_nvt_sllod_eff.cpp index 95e52c140..46b49331b 100644 --- a/src/USER-EFF/fix_nvt_sllod_eff.cpp +++ b/src/USER-EFF/fix_nvt_sllod_eff.cpp @@ -1,130 +1,131 @@ /* ---------------------------------------------------------------------- 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 "fix_nvt_sllod_eff.h" #include "math_extra.h" #include "atom.h" #include "domain.h" #include "group.h" #include "modify.h" #include "fix.h" #include "fix_deform.h" #include "compute.h" #include "error.h" using namespace LAMMPS_NS; using namespace FixConst; enum{NO_REMAP,X_REMAP,V_REMAP}; // same as fix_deform.cpp /* ---------------------------------------------------------------------- */ FixNVTSllodEff::FixNVTSllodEff(LAMMPS *lmp, int narg, char **arg) : FixNHEff(lmp, narg, arg) { if (!tstat_flag) error->all(FLERR,"Temperature control must be used with fix nvt/sllod/eff"); if (pstat_flag) error->all(FLERR,"Pressure control can not be used with fix nvt/sllod/eff"); // default values if (mtchain_default_flag) mtchain = 1; // create a new compute temp style // id = fix-ID + temp int n = strlen(id) + 6; id_temp = new char[n]; strcpy(id_temp,id); strcat(id_temp,"_temp"); char **newarg = new char*[3]; newarg[0] = id_temp; newarg[1] = group->names[igroup]; newarg[2] = (char *) "temp/deform/eff"; modify->add_compute(3,newarg); delete [] newarg; tflag = 1; } /* ---------------------------------------------------------------------- */ void FixNVTSllodEff::init() { FixNHEff::init(); if (!temperature->tempbias) error->all(FLERR,"Temperature for fix nvt/sllod/eff does not have a bias"); nondeformbias = 0; if (strcmp(temperature->style,"temp/deform/eff") != 0) nondeformbias = 1; // check fix deform remap settings int i; for (i = 0; i < modify->nfix; i++) if (strcmp(modify->fix[i]->style,"deform") == 0) { if (((FixDeform *) modify->fix[i])->remapflag != V_REMAP) error->all(FLERR,"Using fix nvt/sllod/eff with inconsistent fix deform " "remap option"); break; } if (i == modify->nfix) error->all(FLERR,"Using fix nvt/sllod/eff with no fix deform defined"); } /* ---------------------------------------------------------------------- perform half-step scaling of velocities -----------------------------------------------------------------------*/ void FixNVTSllodEff::nh_v_temp() { // remove and restore bias = streaming velocity = Hrate*lamda + Hratelo // thermostat thermal velocity only // vdelu = SLLOD correction = Hrate*Hinv*vthermal // for non temp/deform BIAS: // calculate temperature since some computes require temp // computed on current nlocal atoms to remove bias if (nondeformbias) temperature->compute_scalar(); double **v = atom->v; double *ervel = atom->ervel; int *spin = atom->spin; int *mask = atom->mask; int nlocal = atom->nlocal; if (igroup == atom->firstgroup) nlocal = atom->nfirst; double h_two[6],vdelu[3]; MathExtra::multiply_shape_shape(domain->h_rate,domain->h_inv,h_two); for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { temperature->remove_bias(i,v[i]); vdelu[0] = h_two[0]*v[i][0] + h_two[5]*v[i][1] + h_two[4]*v[i][2]; vdelu[1] = h_two[1]*v[i][1] + h_two[3]*v[i][2]; vdelu[2] = h_two[2]*v[i][2]; v[i][0] = v[i][0]*factor_eta - dthalf*vdelu[0]; v[i][1] = v[i][1]*factor_eta - dthalf*vdelu[1]; v[i][2] = v[i][2]*factor_eta - dthalf*vdelu[2]; temperature->restore_bias(i,v[i]); - if (fabs(spin[i])==1) + if (abs(spin[i])==1) ervel[i] = ervel[i]*factor_eta - dthalf*sqrt(vdelu[0]*vdelu[0]+vdelu[1]*vdelu[1]+vdelu[2]*vdelu[2]); } } } diff --git a/src/USER-MISC/fix_ttm_mod.cpp b/src/USER-MISC/fix_ttm_mod.cpp index c72c2c97d..dcbca2deb 100644 --- a/src/USER-MISC/fix_ttm_mod.cpp +++ b/src/USER-MISC/fix_ttm_mod.cpp @@ -1,871 +1,869 @@ /* ---------------------------------------------------------------------- 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 authors: (in addition to authors of original fix ttm) Sergey Starikov (Joint Institute for High Temperatures of RAS) Vasily Pisarev (Joint Institute for High Temperatures of RAS) ------------------------------------------------------------------------- */ #include "lmptype.h" #include "mpi.h" #include "math.h" #include "string.h" #include "stdlib.h" #include "fix_ttm_mod.h" #include "atom.h" #include "force.h" #include "update.h" #include "domain.h" #include "region.h" #include "respa.h" #include "comm.h" #include "random_mars.h" #include "memory.h" #include "error.h" #include "math_const.h" using namespace LAMMPS_NS; using namespace FixConst; using namespace MathConst; #define MAXLINE 1024 /* ---------------------------------------------------------------------- */ FixTTMMod::FixTTMMod(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { if (narg < 9) error->all(FLERR,"Illegal fix ttm/mod command"); vector_flag = 1; size_vector = 2; global_freq = 1; extvector = 1; nevery = 1; restart_peratom = 1; restart_global = 1; seed = atoi(arg[3]); nxnodes = atoi(arg[4]); nynodes = atoi(arg[5]); nznodes = atoi(arg[6]); fpr = fopen(arg[7],"r"); if (fpr == NULL) { char str[128]; sprintf(str,"Cannot open file %s",arg[7]); error->one(FLERR,str); } fpr_2 = fopen(arg[8],"r"); if (fpr == NULL) { char str[128]; sprintf(str,"Cannot open file %s",arg[8]); error->one(FLERR,str); } nfileevery = atoi(arg[9]); if (nfileevery) { if (narg != 11) error->all(FLERR,"Illegal fix ttm/mod command"); MPI_Comm_rank(world,&me); if (me == 0) { fp = fopen(arg[10],"w"); if (fp == NULL) { char str[128]; sprintf(str,"Cannot open fix ttm/mod file %s",arg[10]); error->one(FLERR,str); } } } char linee[MAXLINE]; double tresh_d; int tresh_i; // C0 (metal) fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%lg",&tresh_d); esheat_0 = tresh_d; // C1 (metal*10^3) fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%lg",&tresh_d); esheat_1 = tresh_d; // C2 (metal*10^6) fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%lg",&tresh_d); esheat_2 = tresh_d; // C3 (metal*10^9) fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%lg",&tresh_d); esheat_3 = tresh_d; // C4 (metal*10^12) fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%lg",&tresh_d); esheat_4 = tresh_d; // C_limit fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%lg",&tresh_d); C_limit = tresh_d; //Temperature damping factor fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%lg",&tresh_d); T_damp = tresh_d; // rho_e fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%lg",&tresh_d); electronic_density = tresh_d; //thermal_diffusion fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%lg",&tresh_d); el_th_diff = tresh_d; // gamma_p fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%lg",&tresh_d); gamma_p = tresh_d; // gamma_s fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%lg",&tresh_d); gamma_s = tresh_d; // v0 fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%lg",&tresh_d); v_0 = tresh_d; // average intensity of pulse (source of energy) (metal units) fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%lg",&tresh_d); intensity = tresh_d; // coordinate of 1st surface in x-direction (in box units) - constant fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%d",&tresh_i); surface_l = tresh_i; // coordinate of 2nd surface in x-direction (in box units) - constant fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%d",&tresh_i); surface_r = tresh_i; // skin_layer = intensity is reduced (I=I0*exp[-x/skin_layer]) fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%d",&tresh_i); skin_layer = tresh_i; // width of pulse (picoseconds) fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%lg",&tresh_d); width = tresh_d; // factor of electronic pressure (PF) Pe = PF*Ce*Te fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%lg",&tresh_d); pres_factor = tresh_d; // effective free path of electrons (angstrom) fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%lg",&tresh_d); free_path = tresh_d; // ionic density (ions*angstrom^{-3}) fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%lg",&tresh_d); ionic_density = tresh_d; // if movsur = 0: surface is freezed fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%d",&tresh_i); movsur = tresh_i; // electron_temperature_min fgets(linee,MAXLINE,fpr_2); fgets(linee,MAXLINE,fpr_2); sscanf(linee,"%lg",&tresh_d); electron_temperature_min = tresh_d; fclose(fpr_2); //t_surface is determined by electronic temperature (not constant) t_surface_l = surface_l; mult_factor = intensity; duration = 0.0; surface_double = double(t_surface_l)*(domain->xprd/nxnodes); if ((C_limit+esheat_0) < 0.0) error->all(FLERR,"Fix ttm electronic_specific_heat must be >= 0.0"); if (electronic_density <= 0.0) error->all(FLERR,"Fix ttm electronic_density must be > 0.0"); if (gamma_p < 0.0) error->all(FLERR,"Fix ttm gamma_p must be >= 0.0"); if (gamma_s < 0.0) error->all(FLERR,"Fix ttm gamma_s must be >= 0.0"); if (v_0 < 0.0) error->all(FLERR,"Fix ttm v_0 must be >= 0.0"); if (ionic_density <= 0.0) error->all(FLERR,"Fix ttm ionic_density must be > 0.0"); v_0_sq = v_0*v_0; // error check if (nxnodes <= 0 || nynodes <= 0 || nznodes <= 0) error->all(FLERR,"Fix ttm number of nodes must be > 0"); if (seed <= 0) error->all(FLERR,"Invalid random number seed in fix ttm command"); if (surface_l < 0) error->all(FLERR,"Surface coordinates must be >= 0"); if (surface_l >= surface_r) error->all(FLERR, "Left surface coordinate must be less than right surface coordinate"); // initialize Marsaglia RNG with processor-unique seed random = new RanMars(lmp,seed + comm->me); // allocate per-type arrays for force prefactors gfactor1 = new double[atom->ntypes+1]; gfactor2 = new double[atom->ntypes+1]; // allocate 3d grid variables total_nnodes = nxnodes*nynodes*nznodes; memory->create(nsum,nxnodes,nynodes,nznodes,"ttm/mod:nsum"); memory->create(nsum_all,nxnodes,nynodes,nznodes,"ttm/mod:nsum_all"); memory->create(T_initial_set,nxnodes,nynodes,nznodes,"ttm/mod:T_initial_set"); memory->create(sum_vsq,nxnodes,nynodes,nznodes,"ttm/mod:sum_vsq"); memory->create(sum_mass_vsq,nxnodes,nynodes,nznodes,"ttm/mod:sum_mass_vsq"); memory->create(sum_vsq_all,nxnodes,nynodes,nznodes,"ttm/mod:sum_vsq_all"); memory->create(sum_mass_vsq_all,nxnodes,nynodes,nznodes, "ttm/mod:sum_mass_vsq_all"); memory->create(T_electron_old,nxnodes,nynodes,nznodes,"ttm/mod:T_electron_old"); memory->create(T_electron,nxnodes,nynodes,nznodes,"ttm/mod:T_electron"); memory->create(net_energy_transfer,nxnodes,nynodes,nznodes, "ttm/mod:net_energy_transfer"); memory->create(net_energy_transfer_all,nxnodes,nynodes,nznodes, "ttm/mod:net_energy_transfer_all"); flangevin = NULL; grow_arrays(atom->nmax); // zero out the flangevin array for (int i = 0; i < atom->nmax; i++) { flangevin[i][0] = 0; flangevin[i][1] = 0; flangevin[i][2] = 0; } atom->add_callback(0); atom->add_callback(1); // set initial electron temperatures from user input file if (me == 0) read_initial_electron_temperatures(); MPI_Bcast(&T_electron[0][0][0],total_nnodes,MPI_DOUBLE,0,world); } /* ---------------------------------------------------------------------- */ FixTTMMod::~FixTTMMod() { if (nfileevery && me == 0) fclose(fp); delete random; delete [] gfactor1; delete [] gfactor2; memory->destroy(nsum); memory->destroy(nsum_all); memory->destroy(T_initial_set); memory->destroy(sum_vsq); memory->destroy(sum_mass_vsq); memory->destroy(sum_vsq_all); memory->destroy(sum_mass_vsq_all); memory->destroy(T_electron_first); memory->destroy(T_electron_old); memory->destroy(T_electron); memory->destroy(flangevin); memory->destroy(net_energy_transfer); memory->destroy(net_energy_transfer_all); } /* ---------------------------------------------------------------------- */ int FixTTMMod::setmask() { int mask = 0; mask |= POST_FORCE; mask |= POST_FORCE_RESPA; mask |= END_OF_STEP; return mask; } /* ---------------------------------------------------------------------- */ void FixTTMMod::init() { if (domain->dimension == 2) error->all(FLERR,"Cannot use fix ttm with 2d simulation"); if (domain->nonperiodic != 0) error->all(FLERR,"Cannot use nonperiodic boundares with fix ttm"); if (domain->triclinic) error->all(FLERR,"Cannot use fix ttm with triclinic box"); // set force prefactors for (int i = 1; i <= atom->ntypes; i++) { gfactor1[i] = - gamma_p / force->ftm2v; gfactor2[i] = sqrt(24.0*force->boltz*gamma_p/update->dt/force->mvv2e) / force->ftm2v; } for (int ixnode = 0; ixnode < nxnodes; ixnode++) for (int iynode = 0; iynode < nynodes; iynode++) for (int iznode = 0; iznode < nznodes; iznode++) net_energy_transfer_all[ixnode][iynode][iznode] = 0; if (strstr(update->integrate_style,"respa")) nlevels_respa = ((Respa *) update->integrate)->nlevels; } /* ---------------------------------------------------------------------- */ void FixTTMMod::setup(int vflag) { if (strstr(update->integrate_style,"verlet")) post_force_setup(vflag); else { ((Respa *) update->integrate)->copy_flevel_f(nlevels_respa-1); post_force_respa_setup(vflag,nlevels_respa-1,0); ((Respa *) update->integrate)->copy_f_flevel(nlevels_respa-1); } } /* ---------------------------------------------------------------------- */ void FixTTMMod::post_force(int vflag) { double **x = atom->x; double **v = atom->v; double **f = atom->f; int *type = atom->type; int *mask = atom->mask; int nlocal = atom->nlocal; double dx = domain->xprd/nxnodes; double dy = domain->yprd/nynodes; double dz = domain->zprd/nynodes; double gamma1,gamma2; // apply damping and thermostat to all atoms in fix group for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { double xscale = (x[i][0] - domain->boxlo[0])/domain->xprd; double yscale = (x[i][1] - domain->boxlo[1])/domain->yprd; double zscale = (x[i][2] - domain->boxlo[2])/domain->zprd; int ixnode = static_cast(xscale*nxnodes); int iynode = static_cast(yscale*nynodes); int iznode = static_cast(zscale*nznodes); while (ixnode > nxnodes-1) ixnode -= nxnodes; while (iynode > nynodes-1) iynode -= nynodes; while (iznode > nznodes-1) iznode -= nznodes; while (ixnode < 0) ixnode += nxnodes; while (iynode < 0) iynode += nynodes; while (iznode < 0) iznode += nznodes; if (T_electron[ixnode][iynode][iznode] < 0) error->all(FLERR,"Electronic temperature dropped below zero"); double tsqrt = sqrt(T_electron[ixnode][iynode][iznode]); gamma1 = gfactor1[type[i]]; double vsq = v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]; if (vsq > v_0_sq) gamma1 *= (gamma_p + gamma_s)/gamma_p; gamma2 = gfactor2[type[i]] * tsqrt; if (ixnode >= surface_l){ if (ixnode < surface_r){ flangevin[i][0] = gamma1*v[i][0] + gamma2*(random->uniform()-0.5); flangevin[i][1] = gamma1*v[i][1] + gamma2*(random->uniform()-0.5); flangevin[i][2] = gamma1*v[i][2] + gamma2*(random->uniform()-0.5); double x_surf = dx*double(surface_l)+dx; double x_at = x[i][0] - domain->boxlo[0]; int right_xnode = ixnode + 1; int right_ynode = iynode + 1; int right_znode = iznode + 1; if (right_xnode == nxnodes) right_xnode = 0; if (right_ynode == nynodes) right_ynode = 0; if (right_znode == nznodes) right_znode = 0; int left_xnode = ixnode - 1; int left_ynode = iynode - 1; int left_znode = iznode - 1; if (left_xnode == -1) left_xnode = nxnodes - 1; if (left_ynode == -1) left_ynode = nynodes - 1; if (left_znode == -1) left_znode = nznodes - 1; double T_i = T_electron[ixnode][iynode][iznode]; - double T_il = T_electron[left_xnode][iynode][iznode]; double T_ir = T_electron[right_xnode][iynode][iznode]; double T_iu = T_electron[ixnode][right_ynode][iznode]; double T_if = T_electron[ixnode][iynode][right_znode]; double C_i = el_properties(T_electron[ixnode][iynode][iznode]).el_heat_capacity; - double C_il = el_properties(T_electron[left_xnode][iynode][iznode]).el_heat_capacity; double C_ir = el_properties(T_electron[right_xnode][iynode][iznode]).el_heat_capacity; double C_iu = el_properties(T_electron[ixnode][right_ynode][iznode]).el_heat_capacity; double C_if = el_properties(T_electron[ixnode][iynode][right_znode]).el_heat_capacity; double diff_x = (x_at - x_surf)*(x_at - x_surf); diff_x = pow(diff_x,0.5); double len_factor = diff_x/(diff_x+free_path); if (movsur == 1){ if (x_at >= x_surf){ flangevin[i][0] -= pres_factor/ionic_density*((C_ir*T_ir*free_path/(diff_x+free_path)/(diff_x+free_path)) + (len_factor/dx)*(C_ir*T_ir-C_i*T_i)); flangevin[i][1] -= pres_factor/ionic_density/dy*(C_iu*T_iu-C_i*T_i); flangevin[i][2] -= pres_factor/ionic_density/dz*(C_if*T_if-C_i*T_i); } } else{ flangevin[i][0] -= pres_factor/ionic_density/dx*(C_ir*T_ir-C_i*T_i); flangevin[i][1] -= pres_factor/ionic_density/dy*(C_iu*T_iu-C_i*T_i); flangevin[i][2] -= pres_factor/ionic_density/dz*(C_if*T_if-C_i*T_i); } f[i][0] += flangevin[i][0]; f[i][1] += flangevin[i][1]; f[i][2] += flangevin[i][2]; } } if (movsur == 1){ if (ixnode < surface_l){ t_surface_l = ixnode; } } } } MPI_Allreduce(&t_surface_l,&surface_l,1,MPI_INT,MPI_MIN,world); } /* ---------------------------------------------------------------------- */ void FixTTMMod::post_force_setup(int vflag) { double **f = atom->f; int *mask = atom->mask; int nlocal = atom->nlocal; // apply langevin forces that have been stored from previous run for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { f[i][0] += flangevin[i][0]; f[i][1] += flangevin[i][1]; f[i][2] += flangevin[i][2]; } } } /* ---------------------------------------------------------------------- */ void FixTTMMod::post_force_respa(int vflag, int ilevel, int iloop) { if (ilevel == nlevels_respa-1) post_force(vflag); } /* ---------------------------------------------------------------------- */ void FixTTMMod::post_force_respa_setup(int vflag, int ilevel, int iloop) { if (ilevel == nlevels_respa-1) post_force_setup(vflag); } /* ---------------------------------------------------------------------- */ void FixTTMMod::reset_dt() { for (int i = 1; i <= atom->ntypes; i++) gfactor2[i] = sqrt(24.0*force->boltz*gamma_p/update->dt/force->mvv2e) / force->ftm2v; } /* ---------------------------------------------------------------------- read in initial electron temperatures from a user-specified file only called by proc 0 ------------------------------------------------------------------------- */ void FixTTMMod::read_initial_electron_temperatures() { char line[MAXLINE]; for (int ixnode = 0; ixnode < nxnodes; ixnode++) for (int iynode = 0; iynode < nynodes; iynode++) for (int iznode = 0; iznode < nznodes; iznode++) T_initial_set[ixnode][iynode][iznode] = 0; // read initial electron temperature values from file int ixnode,iynode,iznode; double T_tmp; while (1) { if (fgets(line,MAXLINE,fpr) == NULL) break; sscanf(line,"%d %d %d %lg",&ixnode,&iynode,&iznode,&T_tmp); if (T_tmp < 0.0) error->one(FLERR,"Fix ttm electron temperatures must be >= 0.0"); T_electron[ixnode][iynode][iznode] = T_tmp; T_initial_set[ixnode][iynode][iznode] = 1; } for (int ixnode = 0; ixnode < nxnodes; ixnode++) for (int iynode = 0; iynode < nynodes; iynode++) for (int iznode = 0; iznode < nznodes; iznode++) if (T_initial_set[ixnode][iynode][iznode] == 0) error->one(FLERR,"Initial temperatures not all set in fix ttm"); // close file fclose(fpr); } /* ---------------------------------------------------------------------- */ el_heat_capacity_thermal_conductivity FixTTMMod::el_properties(double T_e) { el_heat_capacity_thermal_conductivity properties; double T_temp = T_e/1000.0, T_reduced = T_damp*T_temp; double T2 = T_temp*T_temp; double T3 = T2*T_temp; double T4 = T3*T_temp; double poly = esheat_0 + esheat_1*T_temp + esheat_2*T2 + esheat_3*T3 + esheat_4*T4; properties.el_heat_capacity = electronic_density*(poly*exp(-T_reduced*T_reduced) + C_limit); // heat capacity properties.el_thermal_conductivity = el_th_diff*properties.el_heat_capacity; // thermal conductivity return properties; } double FixTTMMod::el_sp_heat_integral(double T_e) { double T_temp = T_e/1000.0, T_reduced = T_damp*T_temp; if (T_damp != 0) return electronic_density*(MY_PIS*(3*esheat_4/pow(T_damp,5)+2*esheat_2/pow(T_damp,3)+4*esheat_0/T_damp)*erf(T_reduced)+ 4*esheat_3/pow(T_damp,4)+4*esheat_1/T_damp/T_damp- ((6*esheat_4*T_temp+4*esheat_3)/pow(T_damp,4)+ (4*esheat_1+4*esheat_4*pow(T_temp,3)+4*esheat_3*T_temp*T_temp+4*esheat_2*T_temp)/T_damp/T_damp)*exp(-T_reduced*T_reduced))*125.0+electronic_density*C_limit*T_e; else return electronic_density*((esheat_0 + C_limit)*T_e + esheat_1*T_temp*T_e/2.0 + esheat_2*T_temp*T_temp*T_e/3.0 + esheat_3*pow(T_temp,3)*T_e/4.0 + esheat_4*pow(T_temp,4)*T_e/5.0); } void FixTTMMod::end_of_step() { double **x = atom->x; double **v = atom->v; double *mass = atom->mass; double *rmass = atom->rmass; int *type = atom->type; int *mask = atom->mask; int nlocal = atom->nlocal; if (movsur == 1){ for (int ixnode = 0; ixnode < nxnodes; ixnode++) for (int iynode = 0; iynode < nynodes; iynode++) for (int iznode = 0; iznode < nznodes; iznode++){ double TTT = T_electron[ixnode][iynode][iznode]; if (TTT > 0){ if (ixnode < t_surface_l) t_surface_l = ixnode; } } } for (int ixnode = 0; ixnode < nxnodes; ixnode++) for (int iynode = 0; iynode < nynodes; iynode++) for (int iznode = 0; iznode < nznodes; iznode++) net_energy_transfer[ixnode][iynode][iznode] = 0; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { double xscale = (x[i][0] - domain->boxlo[0])/domain->xprd; double yscale = (x[i][1] - domain->boxlo[1])/domain->yprd; double zscale = (x[i][2] - domain->boxlo[2])/domain->zprd; int ixnode = static_cast(xscale*nxnodes); int iynode = static_cast(yscale*nynodes); int iznode = static_cast(zscale*nznodes); while (ixnode > nxnodes-1) ixnode -= nxnodes; while (iynode > nynodes-1) iynode -= nynodes; while (iznode > nznodes-1) iznode -= nznodes; while (ixnode < 0) ixnode += nxnodes; while (iynode < 0) iynode += nynodes; while (iznode < 0) iznode += nznodes; if (ixnode >= t_surface_l){ if (ixnode < surface_r) net_energy_transfer[ixnode][iynode][iznode] += (flangevin[i][0]*v[i][0] + flangevin[i][1]*v[i][1] + flangevin[i][2]*v[i][2]); } } MPI_Allreduce(&net_energy_transfer[0][0][0], &net_energy_transfer_all[0][0][0], total_nnodes,MPI_DOUBLE,MPI_SUM,world); double dx = domain->xprd/nxnodes; double dy = domain->yprd/nynodes; double dz = domain->zprd/nznodes; double del_vol = dx*dy*dz; double el_specific_heat = 0.0; double el_thermal_conductivity = el_properties(electron_temperature_min).el_thermal_conductivity; for (int ixnode = 0; ixnode < nxnodes; ixnode++) for (int iynode = 0; iynode < nynodes; iynode++) for (int iznode = 0; iznode < nznodes; iznode++) { if (el_properties(T_electron[ixnode][iynode][iznode]).el_thermal_conductivity > el_thermal_conductivity) el_thermal_conductivity = el_properties(T_electron[ixnode][iynode][iznode]).el_thermal_conductivity; if (el_specific_heat > 0.0) { if ((T_electron[ixnode][iynode][iznode] > 0.0) && (el_properties(T_electron[ixnode][iynode][iznode]).el_heat_capacity < el_specific_heat)) el_specific_heat = el_properties(T_electron[ixnode][iynode][iznode]).el_heat_capacity; } else if (T_electron[ixnode][iynode][iznode] > 0.0) el_specific_heat = el_properties(T_electron[ixnode][iynode][iznode]).el_heat_capacity; } // num_inner_timesteps = # of inner steps (thermal solves) // required this MD step to maintain a stable explicit solve int num_inner_timesteps = 1; double inner_dt = update->dt; double stability_criterion = 1.0 - 2.0*inner_dt/el_specific_heat * (el_thermal_conductivity*(1.0/dx/dx + 1.0/dy/dy + 1.0/dz/dz)); if (stability_criterion < 0.0) { inner_dt = 0.25*el_specific_heat / (el_thermal_conductivity*(1.0/dx/dx + 1.0/dy/dy + 1.0/dz/dz)); } num_inner_timesteps = static_cast(update->dt/inner_dt) + 1; inner_dt = update->dt/double(num_inner_timesteps); if (num_inner_timesteps > 1000000) error->warning(FLERR,"Too many inner timesteps in fix ttm",0); for (int ith_inner_timestep = 0; ith_inner_timestep < num_inner_timesteps; ith_inner_timestep++) { for (int ixnode = 0; ixnode < nxnodes; ixnode++) for (int iynode = 0; iynode < nynodes; iynode++) for (int iznode = 0; iznode < nznodes; iznode++) T_electron_old[ixnode][iynode][iznode] = T_electron[ixnode][iynode][iznode]; // compute new electron T profile duration = duration + inner_dt; for (int ixnode = 0; ixnode < nxnodes; ixnode++) for (int iynode = 0; iynode < nynodes; iynode++) for (int iznode = 0; iznode < nznodes; iznode++) { int right_xnode = ixnode + 1; int right_ynode = iynode + 1; int right_znode = iznode + 1; if (right_xnode == nxnodes) right_xnode = 0; if (right_ynode == nynodes) right_ynode = 0; if (right_znode == nznodes) right_znode = 0; int left_xnode = ixnode - 1; int left_ynode = iynode - 1; int left_znode = iznode - 1; if (left_xnode == -1) left_xnode = nxnodes - 1; if (left_ynode == -1) left_ynode = nynodes - 1; if (left_znode == -1) left_znode = nznodes - 1; double skin_layer_d = double(skin_layer); double ixnode_d = double(ixnode); double surface_d = double(t_surface_l); mult_factor = 0.0; if (duration < width){ if (ixnode >= t_surface_l) mult_factor = (intensity/(dx*skin_layer_d))*exp((-1.0)*(ixnode_d - surface_d)/skin_layer_d); } if (ixnode < t_surface_l) net_energy_transfer_all[ixnode][iynode][iznode] = 0.0; double cr_vac = 1; if (T_electron_old[ixnode][iynode][iznode] == 0) cr_vac = 0; double cr_v_l_x = 1; if (T_electron_old[left_xnode][iynode][iznode] == 0) cr_v_l_x = 0; double cr_v_r_x = 1; if (T_electron_old[right_xnode][iynode][iznode] == 0) cr_v_r_x = 0; double cr_v_l_y = 1; if (T_electron_old[ixnode][left_ynode][iznode] == 0) cr_v_l_y = 0; double cr_v_r_y = 1; if (T_electron_old[ixnode][right_ynode][iznode] == 0) cr_v_r_y = 0; double cr_v_l_z = 1; if (T_electron_old[ixnode][iynode][left_znode] == 0) cr_v_l_z = 0; double cr_v_r_z = 1; if (T_electron_old[ixnode][iynode][right_znode] == 0) cr_v_r_z = 0; if (cr_vac != 0) { T_electron[ixnode][iynode][iznode] = T_electron_old[ixnode][iynode][iznode] + inner_dt/el_properties(T_electron_old[ixnode][iynode][iznode]).el_heat_capacity * ((cr_v_r_x*el_properties(T_electron_old[ixnode][iynode][iznode]/2.0+T_electron_old[right_xnode][iynode][iznode]/2.0).el_thermal_conductivity* (T_electron_old[right_xnode][iynode][iznode]-T_electron_old[ixnode][iynode][iznode])/dx - cr_v_l_x*el_properties(T_electron_old[ixnode][iynode][iznode]/2.0+T_electron_old[left_xnode][iynode][iznode]/2.0).el_thermal_conductivity* (T_electron_old[ixnode][iynode][iznode]-T_electron_old[left_xnode][iynode][iznode])/dx)/dx + (cr_v_r_y*el_properties(T_electron_old[ixnode][iynode][iznode]/2.0+T_electron_old[ixnode][right_ynode][iznode]/2.0).el_thermal_conductivity* (T_electron_old[ixnode][right_ynode][iznode]-T_electron_old[ixnode][iynode][iznode])/dy - cr_v_l_y*el_properties(T_electron_old[ixnode][iynode][iznode]/2.0+T_electron_old[ixnode][left_ynode][iznode]/2.0).el_thermal_conductivity* (T_electron_old[ixnode][iynode][iznode]-T_electron_old[ixnode][left_ynode][iznode])/dy)/dy + (cr_v_r_z*el_properties(T_electron_old[ixnode][iynode][iznode]/2.0+T_electron_old[ixnode][iynode][right_znode]/2.0).el_thermal_conductivity* (T_electron_old[ixnode][iynode][right_znode]-T_electron_old[ixnode][iynode][iznode])/dz - cr_v_l_z*el_properties(T_electron_old[ixnode][iynode][iznode]/2.0+T_electron_old[ixnode][iynode][left_znode]/2.0).el_thermal_conductivity* (T_electron_old[ixnode][iynode][iznode]-T_electron_old[ixnode][iynode][left_znode])/dz)/dz); T_electron[ixnode][iynode][iznode]+=inner_dt/el_properties(T_electron[ixnode][iynode][iznode]).el_heat_capacity* (mult_factor - net_energy_transfer_all[ixnode][iynode][iznode]/del_vol); } else T_electron[ixnode][iynode][iznode] = T_electron_old[ixnode][iynode][iznode]; if ((T_electron[ixnode][iynode][iznode] > 0.0) && (T_electron[ixnode][iynode][iznode] < electron_temperature_min)) T_electron[ixnode][iynode][iznode] = T_electron[ixnode][iynode][iznode] + 0.5*(electron_temperature_min - T_electron[ixnode][iynode][iznode]); } } // output nodal temperatures for current timestep if ((nfileevery) && !(update->ntimestep % nfileevery)) { // compute atomic Ta for each grid point for (int ixnode = 0; ixnode < nxnodes; ixnode++) for (int iynode = 0; iynode < nynodes; iynode++) for (int iznode = 0; iznode < nznodes; iznode++) { nsum[ixnode][iynode][iznode] = 0; nsum_all[ixnode][iynode][iznode] = 0; sum_vsq[ixnode][iynode][iznode] = 0.0; sum_mass_vsq[ixnode][iynode][iznode] = 0.0; sum_vsq_all[ixnode][iynode][iznode] = 0.0; sum_mass_vsq_all[ixnode][iynode][iznode] = 0.0; } double massone; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { if (rmass) massone = rmass[i]; else massone = mass[type[i]]; double xscale = (x[i][0] - domain->boxlo[0])/domain->xprd; double yscale = (x[i][1] - domain->boxlo[1])/domain->yprd; double zscale = (x[i][2] - domain->boxlo[2])/domain->zprd; int ixnode = static_cast(xscale*nxnodes); int iynode = static_cast(yscale*nynodes); int iznode = static_cast(zscale*nznodes); while (ixnode > nxnodes-1) ixnode -= nxnodes; while (iynode > nynodes-1) iynode -= nynodes; while (iznode > nznodes-1) iznode -= nznodes; while (ixnode < 0) ixnode += nxnodes; while (iynode < 0) iynode += nynodes; while (iznode < 0) iznode += nznodes; double vsq = v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]; nsum[ixnode][iynode][iznode] += 1; sum_vsq[ixnode][iynode][iznode] += vsq; sum_mass_vsq[ixnode][iynode][iznode] += massone*vsq; } MPI_Allreduce(&nsum[0][0][0],&nsum_all[0][0][0],total_nnodes, MPI_INT,MPI_SUM,world); MPI_Allreduce(&sum_vsq[0][0][0],&sum_vsq_all[0][0][0],total_nnodes, MPI_DOUBLE,MPI_SUM,world); MPI_Allreduce(&sum_mass_vsq[0][0][0],&sum_mass_vsq_all[0][0][0], total_nnodes,MPI_DOUBLE,MPI_SUM,world); MPI_Allreduce(&t_surface_l,&surface_l, 1,MPI_INT,MPI_MIN,world); if (me == 0) { fprintf(fp,BIGINT_FORMAT,update->ntimestep); double T_a; for (int ixnode = 0; ixnode < nxnodes; ixnode++) for (int iynode = 0; iynode < nynodes; iynode++) for (int iznode = 0; iznode < nznodes; iznode++) { T_a = 0; if (nsum_all[ixnode][iynode][iznode] > 0){ T_a = sum_mass_vsq_all[ixnode][iynode][iznode]/ (3.0*force->boltz*nsum_all[ixnode][iynode][iznode]/force->mvv2e); if (movsur == 1){ if (T_electron[ixnode][iynode][iznode]==0.0) T_electron[ixnode][iynode][iznode] = electron_temperature_min; } } fprintf(fp," %f",T_a); } fprintf(fp,"\t"); for (int ixnode = 0; ixnode < nxnodes; ixnode++) for (int iynode = 0; iynode < nynodes; iynode++) for (int iznode = 0; iznode < nznodes; iznode++) fprintf(fp,"%f ",T_electron[ixnode][iynode][iznode]); fprintf(fp,"\n"); } } } /* ---------------------------------------------------------------------- memory usage of 3d grid ------------------------------------------------------------------------- */ double FixTTMMod::memory_usage() { double bytes = 0.0; bytes += 5*total_nnodes * sizeof(int); bytes += 14*total_nnodes * sizeof(double); return bytes; } /* ---------------------------------------------------------------------- */ void FixTTMMod::grow_arrays(int ngrow) { memory->grow(flangevin,ngrow,3,"ttm/mod:flangevin"); } /* ---------------------------------------------------------------------- return the energy of the electronic subsystem or the net_energy transfer between the subsystems ------------------------------------------------------------------------- */ double FixTTMMod::compute_vector(int n) { double e_energy = 0.0; double transfer_energy = 0.0; double dx = domain->xprd/nxnodes; double dy = domain->yprd/nynodes; double dz = domain->zprd/nznodes; double del_vol = dx*dy*dz; for (int ixnode = 0; ixnode < nxnodes; ixnode++) for (int iynode = 0; iynode < nynodes; iynode++) for (int iznode = 0; iznode < nznodes; iznode++) { e_energy += el_sp_heat_integral(T_electron[ixnode][iynode][iznode])*del_vol; transfer_energy += net_energy_transfer_all[ixnode][iynode][iznode]*update->dt; } if (n == 0) return e_energy; if (n == 1) return transfer_energy; return 0.0; } /* ---------------------------------------------------------------------- pack entire state of Fix into one write ------------------------------------------------------------------------- */ void FixTTMMod::write_restart(FILE *fp) { double *rlist; memory->create(rlist,nxnodes*nynodes*nznodes+1,"ttm/mod:rlist"); int n = 0; rlist[n++] = seed; for (int ixnode = 0; ixnode < nxnodes; ixnode++) for (int iynode = 0; iynode < nynodes; iynode++) for (int iznode = 0; iznode < nznodes; iznode++) rlist[n++] = T_electron[ixnode][iynode][iznode]; if (comm->me == 0) { int size = n * sizeof(double); fwrite(&size,sizeof(int),1,fp); fwrite(rlist,sizeof(double),n,fp); } memory->destroy(rlist); } /* ---------------------------------------------------------------------- use state info from restart file to restart the Fix ------------------------------------------------------------------------- */ void FixTTMMod::restart(char *buf) { int n = 0; double *rlist = (double *) buf; // the seed must be changed from the initial seed seed = static_cast (0.5*rlist[n++]); for (int ixnode = 0; ixnode < nxnodes; ixnode++) for (int iynode = 0; iynode < nynodes; iynode++) for (int iznode = 0; iznode < nznodes; iznode++) T_electron[ixnode][iynode][iznode] = rlist[n++]; delete random; random = new RanMars(lmp,seed+comm->me); } /* ---------------------------------------------------------------------- pack values in local atom-based arrays for restart file ------------------------------------------------------------------------- */ int FixTTMMod::pack_restart(int i, double *buf) { buf[0] = 4; buf[1] = flangevin[i][0]; buf[2] = flangevin[i][1]; buf[3] = flangevin[i][2]; return 4; } /* ---------------------------------------------------------------------- unpack values from atom->extra array to restart the fix ------------------------------------------------------------------------- */ void FixTTMMod::unpack_restart(int nlocal, int nth) { double **extra = atom->extra; // skip to Nth set of extra values int m = 0; for (int i = 0; i < nth; i++) m += static_cast (extra[nlocal][m]); m++; flangevin[nlocal][0] = extra[nlocal][m++]; flangevin[nlocal][1] = extra[nlocal][m++]; flangevin[nlocal][2] = extra[nlocal][m++]; } /* ---------------------------------------------------------------------- maxsize of any atom's restart data ------------------------------------------------------------------------- */ int FixTTMMod::maxsize_restart() { return 4; } /* ---------------------------------------------------------------------- size of atom nlocal's restart data ------------------------------------------------------------------------- */ int FixTTMMod::size_restart(int nlocal) { return 4; } diff --git a/src/compute_chunk_atom.cpp b/src/compute_chunk_atom.cpp index 4ffe9c4c1..7197767b4 100644 --- a/src/compute_chunk_atom.cpp +++ b/src/compute_chunk_atom.cpp @@ -1,1557 +1,1548 @@ /* ---------------------------------------------------------------------- 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 "mpi.h" #include "string.h" #include "stdlib.h" #include "compute_chunk_atom.h" #include "atom.h" #include "update.h" #include "force.h" #include "domain.h" #include "region.h" #include "lattice.h" #include "modify.h" #include "fix_store.h" #include "comm.h" #include "group.h" #include "input.h" #include "variable.h" #include "memory.h" #include "error.h" #include using namespace LAMMPS_NS; enum{BIN1D,BIN2D,BIN3D,TYPE,MOLECULE,COMPUTE,FIX,VARIABLE}; enum{LOWER,CENTER,UPPER,COORD}; enum{BOX,LATTICE,REDUCED}; enum{NODISCARD,MIXED,YESDISCARD}; enum{ONCE,NFREQ,EVERY}; // used in several files enum{LIMITMAX,LIMITEXACT}; #define IDMAX 1024*1024 #define INVOKED_PERATOM 8 // allocate space for static class variable ComputeChunkAtom *ComputeChunkAtom::cptr; /* ---------------------------------------------------------------------- */ ComputeChunkAtom::ComputeChunkAtom(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg) { if (narg < 4) error->all(FLERR,"Illegal compute chunk/atom command"); peratom_flag = 1; size_peratom_cols = 0; create_attribute = 1; // chunk style and its args int iarg; binflag = 0; ncoord = 0; cfvid = NULL; if (strcmp(arg[3],"bin/1d") == 0) { binflag = 1; which = BIN1D; ncoord = 1; iarg = 4; readdim(narg,arg,iarg,0); iarg += 3; } else if (strcmp(arg[3],"bin/2d") == 0) { binflag = 1; which = BIN2D; ncoord = 2; iarg = 4; readdim(narg,arg,iarg,0); readdim(narg,arg,iarg+3,1); iarg += 6; } else if (strcmp(arg[3],"bin/3d") == 0) { binflag = 1; which = BIN3D; ncoord = 3; iarg = 4; readdim(narg,arg,iarg,0); readdim(narg,arg,iarg+3,1); readdim(narg,arg,iarg+6,2); iarg += 9; } else if (strcmp(arg[3],"type") == 0) { which = TYPE; iarg = 4; } else if (strcmp(arg[3],"molecule") == 0) { which = MOLECULE; iarg = 4; } else if (strstr(arg[3],"c_") == arg[3] || strstr(arg[3],"f_") == arg[3] || strstr(arg[3],"v_") == arg[3]) { if (arg[3][0] == 'c') which = COMPUTE; else if (arg[3][0] == 'f') which = FIX; else if (arg[3][0] == 'v') which = VARIABLE; iarg = 4; int n = strlen(arg[3]); char *suffix = new char[n]; strcpy(suffix,&arg[3][2]); char *ptr = strchr(suffix,'['); if (ptr) { if (suffix[strlen(suffix)-1] != ']') error->all(FLERR,"Illegal fix ave/atom command"); argindex = atoi(ptr+1); *ptr = '\0'; } else argindex = 0; n = strlen(suffix) + 1; cfvid = new char[n]; strcpy(cfvid,suffix); delete [] suffix; } else error->all(FLERR,"Illegal compute chunk/atom command"); // optional args regionflag = 0; idregion = NULL; nchunksetflag = 0; nchunkflag = EVERY; limit = 0; limitstyle = LIMITMAX; limitfirst = 0; idsflag = EVERY; compress = 0; int discardsetflag = 0; discard = MIXED; minflag[0] = LOWER; minflag[1] = LOWER; minflag[2] = LOWER; maxflag[0] = UPPER; maxflag[1] = UPPER; maxflag[2] = UPPER; scaleflag = LATTICE; while (iarg < narg) { if (strcmp(arg[iarg],"region") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal compute chunk/atom command"); int iregion = domain->find_region(arg[iarg+1]); if (iregion == -1) error->all(FLERR,"Region ID for compute chunk/atom does not exist"); int n = strlen(arg[iarg+1]) + 1; idregion = new char[n]; strcpy(idregion,arg[iarg+1]); regionflag = 1; iarg += 2; } else if (strcmp(arg[iarg],"nchunk") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal compute chunk/atom command"); if (strcmp(arg[iarg+1],"once") == 0) nchunkflag = ONCE; else if (strcmp(arg[iarg+1],"every") == 0) nchunkflag = EVERY; else error->all(FLERR,"Illegal compute chunk/atom command"); nchunksetflag = 1; iarg += 2; } else if (strcmp(arg[iarg],"limit") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal compute chunk/atom command"); limit = force->inumeric(FLERR,arg[iarg+1]); if (limit < 0) error->all(FLERR,"Illegal compute chunk/atom command"); if (limit && !compress) limitfirst = 1; iarg += 2; if (limit) { if (iarg+1 > narg) error->all(FLERR,"Illegal compute chunk/atom command"); if (strcmp(arg[iarg+1],"max") == 0) limitstyle = LIMITMAX; else if (strcmp(arg[iarg+1],"exact") == 0) limitstyle = LIMITEXACT; else error->all(FLERR,"Illegal compute chunk/atom command"); iarg++; } } else if (strcmp(arg[iarg],"ids") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal compute chunk/atom command"); if (strcmp(arg[iarg+1],"once") == 0) idsflag = ONCE; else if (strcmp(arg[iarg+1],"nfreq") == 0) idsflag = NFREQ; else if (strcmp(arg[iarg+1],"every") == 0) idsflag = EVERY; else error->all(FLERR,"Illegal compute chunk/atom command"); iarg += 2; } else if (strcmp(arg[iarg],"compress") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal compute chunk/atom command"); else if (strcmp(arg[iarg+1],"no") == 0) compress = 0; else if (strcmp(arg[iarg+1],"yes") == 0) compress = 1; else error->all(FLERR,"Illegal compute chunk/atom command"); iarg += 2; } else if (strcmp(arg[iarg],"discard") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal compute chunk/atom command"); if (strcmp(arg[iarg+1],"mixed") == 0) discard = MIXED; else if (strcmp(arg[iarg+1],"no") == 0) discard = NODISCARD; else if (strcmp(arg[iarg+1],"yes") == 0) discard = YESDISCARD; else error->all(FLERR,"Illegal compute chunk/atom command"); discardsetflag = 1; iarg += 2; } else if (strcmp(arg[iarg],"bound") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal compute chunk/atom command"); int idim; if (strcmp(arg[iarg+1],"x") == 0) idim = 0; else if (strcmp(arg[iarg+1],"y") == 0) idim = 1; else if (strcmp(arg[iarg+1],"z") == 0) idim = 2; else error->all(FLERR,"Illegal compute chunk/atom command"); if (strcmp(arg[iarg+2],"lower") == 0) minflag[idim] = LOWER; else minflag[idim] = COORD; if (minflag[idim] == COORD) minvalue[idim] = force->numeric(FLERR,arg[iarg+2]); if (strcmp(arg[iarg+3],"upper") == 0) maxflag[idim] = UPPER; else maxflag[idim] = COORD; if (maxflag[idim] == COORD) maxvalue[idim] = force->numeric(FLERR,arg[iarg+3]); else error->all(FLERR,"Illegal compute chunk/atom command"); iarg += 4; } else if (strcmp(arg[iarg],"units") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal compute chunk/atom command"); 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],"reduced") == 0) scaleflag = REDUCED; else error->all(FLERR,"Illegal compute chunk/atom command"); iarg += 2; } else error->all(FLERR,"Illegal compute chunk/atom command"); } // set nchunkflag and discard to default values if not explicitly set // for binning style, also check in init() if simulation box is static, // which sets nchunkflag = ONCE if (!nchunksetflag) { if (binflag) { if (scaleflag == REDUCED) nchunkflag = ONCE; else nchunkflag = EVERY; } if (which == TYPE) nchunkflag = ONCE; if (which == MOLECULE) { if (regionflag) nchunkflag = EVERY; else nchunkflag = ONCE; } if (compress) nchunkflag = EVERY; } if (!discardsetflag) { if (binflag) discard = MIXED; else discard = YESDISCARD; } // error checks if (which == MOLECULE && !atom->molecule_flag) error->all(FLERR,"Compute chunk/atom molecule for non-molecular system"); if (!binflag && discard == MIXED) error->all(FLERR,"Compute chunk/atom without bins " "cannot use discard mixed"); if (which == BIN1D && delta[0] <= 0.0) error->all(FLERR,"Illegal compute chunk/atom command"); if (which == BIN2D && (delta[0] <= 0.0 || delta[1] <= 0.0)) error->all(FLERR,"Illegal compute chunk/atom command"); if (which == BIN3D && (delta[0] <= 0.0 || delta[1] <= 0.0 || delta[2] <= 0.0)) error->all(FLERR,"Illegal compute chunk/atom command"); if (which == COMPUTE) { int icompute = modify->find_compute(cfvid); if (icompute < 0) error->all(FLERR,"Compute ID for compute chunk/atom does not exist"); if (modify->compute[icompute]->peratom_flag == 0) error->all(FLERR, "Compute chunk/atom compute does not calculate " "per-atom values"); if (argindex == 0 && modify->compute[icompute]->size_peratom_cols != 0) error->all(FLERR,"Compute chunk/atom compute does not " "calculate a per-atom vector"); if (argindex && modify->compute[icompute]->size_peratom_cols == 0) error->all(FLERR,"Compute chunk/atom compute does not " "calculate a per-atom array"); if (argindex && argindex > modify->compute[icompute]->size_peratom_cols) error->all(FLERR,"Compute chunk/atom compute array is " "accessed out-of-range"); } if (which == FIX) { int ifix = modify->find_fix(cfvid); if (ifix < 0) error->all(FLERR,"Fix ID for compute chunk/atom does not exist"); if (modify->fix[ifix]->peratom_flag == 0) error->all(FLERR,"Compute chunk/atom fix does not calculate " "per-atom values"); if (argindex == 0 && modify->fix[ifix]->size_peratom_cols != 0) error->all(FLERR, "Compute chunk/atom fix does not calculate a per-atom vector"); if (argindex && modify->fix[ifix]->size_peratom_cols == 0) error->all(FLERR, "Compute chunk/atom fix does not calculate a per-atom array"); if (argindex && argindex > modify->fix[ifix]->size_peratom_cols) error->all(FLERR,"Compute chunk/atom fix array is accessed out-of-range"); } if (which == VARIABLE) { int ivariable = input->variable->find(cfvid); if (ivariable < 0) error->all(FLERR,"Variable name for compute chunk/atom does not exist"); if (input->variable->atomstyle(ivariable) == 0) error->all(FLERR,"Compute chunk/atom variable is not " "atom-style variable"); } // setup scaling if (binflag) { if (domain->triclinic == 1 && scaleflag != REDUCED) error->all(FLERR,"Compute chunk/atom for triclinic boxes " "requires units reduced"); } if (scaleflag == LATTICE) { xscale = domain->lattice->xlattice; yscale = domain->lattice->ylattice; zscale = domain->lattice->zlattice; } else xscale = yscale = zscale = 1.0; // apply scaling factors if (binflag) { double scale; if (which == BIN1D) ndim = 1; if (which == BIN2D) ndim = 2; if (which == BIN3D) ndim = 3; for (int idim = 0; idim < ndim; idim++) { if (dim[idim] == 0) scale = xscale; else if (dim[idim] == 1) scale = yscale; else if (dim[idim] == 2) scale = zscale; delta[idim] *= scale; invdelta[idim] = 1.0/delta[idim]; if (originflag[idim] == COORD) origin[idim] *= scale; if (minflag[idim] == COORD) minvalue[idim] *= scale; if (maxflag[idim] == COORD) maxvalue[idim] *= scale; } } // initialize chunk vector and per-chunk info nmax = 0; chunk = NULL; nmaxint = 0; ichunk = NULL; exclude = NULL; nchunk = 0; chunk_volume_scalar = 1.0; chunk_volume_vec = NULL; coord = NULL; chunkID = NULL; // computeflag = 1 if this compute might invoke another compute // during assign_chunk_ids() if (which == COMPUTE || which == FIX || which == VARIABLE) computeflag = 1; else computeflag = 0; // other initializations invoked_setup = -1; invoked_ichunk = -1; id_fix = NULL; fixstore = NULL; if (compress) hash = new std::map(); else hash = NULL; maxvar = 0; varatom = NULL; lockcount = 0; lockfix = NULL; if (which == MOLECULE) molcheck = 1; else molcheck = 0; } /* ---------------------------------------------------------------------- */ ComputeChunkAtom::~ComputeChunkAtom() { // check nfix in case all fixes have already been deleted if (modify->nfix) modify->delete_fix(id_fix); delete [] id_fix; memory->destroy(chunk); memory->destroy(ichunk); memory->destroy(exclude); memory->destroy(chunk_volume_vec); memory->destroy(coord); memory->destroy(chunkID); delete [] idregion; delete [] cfvid; delete hash; memory->destroy(varatom); } /* ---------------------------------------------------------------------- */ void ComputeChunkAtom::init() { // set and check validity of region if (regionflag) { int iregion = domain->find_region(idregion); if (iregion == -1) error->all(FLERR,"Region ID for compute chunk/atom does not exist"); region = domain->regions[iregion]; } // set compute,fix,variable if (which == COMPUTE) { int icompute = modify->find_compute(cfvid); if (icompute < 0) error->all(FLERR,"Compute ID for compute chunk/atom does not exist"); cchunk = modify->compute[icompute]; } else if (which == FIX) { int ifix = modify->find_fix(cfvid); if (ifix < 0) error->all(FLERR,"Fix ID for compute chunk/atom does not exist"); fchunk = modify->fix[ifix]; } else if (which == VARIABLE) { int ivariable = input->variable->find(cfvid); if (ivariable < 0) error->all(FLERR,"Variable name for compute chunk/atom does not exist"); vchunk = ivariable; } // for style MOLECULE, check that no mol IDs exceed MAXSMALLINT // don't worry about group or optional region if (which == MOLECULE) { tagint *molecule = atom->molecule; int nlocal = atom->nlocal; tagint maxone = -1; for (int i = 0; i < nlocal; i++) if (molecule[i] > maxone) maxone = molecule[i]; tagint maxall; MPI_Allreduce(&maxone,&maxall,1,MPI_LMP_TAGINT,MPI_MAX,world); if (maxall > MAXSMALLINT) error->all(FLERR,"Molecule IDs too large for compute chunk/atom"); } // for binning, if nchunkflag not already set, set it to ONCE or EVERY // depends on whether simulation box size is static or dynamic // reset invoked_setup if this is not first run and box just became static if (binflag && !nchunksetflag && !compress && scaleflag != REDUCED) { if (domain->box_change_size == 0) { if (nchunkflag == EVERY && invoked_setup >= 0) invoked_setup = -1; nchunkflag = ONCE; } else nchunkflag = EVERY; } // require nchunkflag = ONCE if idsflag = ONCE // b/c nchunk cannot change if chunk IDs are frozen // can't check until now since nchunkflag may have been adjusted in init() if (idsflag == ONCE && nchunkflag != ONCE) error->all(FLERR,"Compute chunk/atom ids once but nchunk is not once"); // create/destroy fix STORE for persistent chunk IDs as needed // need to wait until init() so that fix ave/chunk command(s) are in place // they increment lockcount if they lock this compute // fixstore ID = compute-ID + COMPUTE_STORE, fix group = compute group // fixstore initializes all values to 0.0 if (lockcount && !fixstore) { int n = strlen(id) + strlen("_COMPUTE_STORE") + 1; id_fix = new char[n]; strcpy(id_fix,id); strcat(id_fix,"_COMPUTE_STORE"); char **newarg = new char*[5]; newarg[0] = id_fix; newarg[1] = group->names[igroup]; newarg[2] = (char *) "STORE"; newarg[3] = (char *) "1"; newarg[4] = (char *) "1"; modify->add_fix(5,newarg); fixstore = (FixStore *) modify->fix[modify->nfix-1]; delete [] newarg; } if (!lockcount && fixstore) { delete fixstore; fixstore = NULL; } } /* ---------------------------------------------------------------------- invoke setup_chunks and/or compute_ichunk if only done ONCE so that nchunks or chunk IDs are assigned when this compute was specified as opposed to first times compute_peratom() or compute_ichunk() is called ------------------------------------------------------------------------- */ void ComputeChunkAtom::setup() { if (nchunkflag == ONCE) setup_chunks(); if (idsflag == ONCE) compute_ichunk(); } /* ---------------------------------------------------------------------- only called by classes that use per-atom computes in standard way dump, variable, thermo output, other computes, etc not called by fix ave/chunk or compute chunk commands they invoke setup_chunks() and compute_ichunk() directly ------------------------------------------------------------------------- */ void ComputeChunkAtom::compute_peratom() { invoked_peratom = update->ntimestep; // grow floating point chunk vector if necessary if (atom->nlocal > nmax) { memory->destroy(chunk); nmax = atom->nmax; memory->create(chunk,nmax,"chunk/atom:chunk"); vector_atom = chunk; } setup_chunks(); compute_ichunk(); // copy integer indices into floating-point chunk vector int nlocal = atom->nlocal; for (int i = 0; i < nlocal; i++) chunk[i] = ichunk[i]; } /* ---------------------------------------------------------------------- set lock, so that nchunk will not change from startstep to stopstep called by fix ave/chunk for duration of its Nfreq epoch OK if called by multiple fix ave/chunk commands error if all callers do not have same duration last caller holds the lock, so it can also unlock lockstop can be positive for final step of finite-size time window or can be -1 for infinite-size time window ------------------------------------------------------------------------- */ void ComputeChunkAtom::lock(Fix *fixptr, bigint startstep, bigint stopstep) { if (lockfix == NULL) { lockfix = fixptr; lockstart = startstep; lockstop = stopstep; return; } if (startstep != lockstart || stopstep != lockstop) error->all(FLERR,"Two fix ave/chunk commands using " "same compute chunk/atom command in incompatible ways"); // set lock to last calling Fix, since it will be last to unlock() lockfix = fixptr; } /* ---------------------------------------------------------------------- unset lock can only be done by fix ave/chunk command that holds the lock ------------------------------------------------------------------------- */ void ComputeChunkAtom::unlock(Fix *fixptr) { if (fixptr != lockfix) return; lockfix = NULL; } /* ---------------------------------------------------------------------- assign chunk IDs from 1 to Nchunk to every atom, or 0 if not in chunk ------------------------------------------------------------------------- */ void ComputeChunkAtom::compute_ichunk() { int i; // skip if already done on this step if (invoked_ichunk == update->ntimestep) return; // if old IDs persist via storage in fixstore, then just retrieve them // yes if idsflag = ONCE, and already done once // or if idsflag = NFREQ and lock is in place and are on later timestep // else proceed to recalculate per-atom chunk assignments int restore = 0; if (idsflag == ONCE && invoked_ichunk >= 0) restore = 1; if (idsflag == NFREQ && lockfix && update->ntimestep > lockstart) restore = 1; if (restore) { invoked_ichunk = update->ntimestep; double *vstore = fixstore->vstore; int nlocal = atom->nlocal; for (i = 0; i < nlocal; i++) ichunk[i] = static_cast (vstore[i]); return; } invoked_ichunk = update->ntimestep; // assign chunk IDs to atoms // will exclude atoms not in group or in optional region // already invoked if this is same timestep as last setup_chunks() if (update->ntimestep > invoked_setup) assign_chunk_ids(); // compress chunk IDs via hash of the original uncompressed IDs // also apply discard rule except for binning styles which already did int nlocal = atom->nlocal; if (compress) { if (binflag) { for (i = 0; i < nlocal; i++) { if (exclude[i]) continue; - int old = ichunk[i]; if (hash->find(ichunk[i]) == hash->end()) exclude[i] = 1; else ichunk[i] = hash->find(ichunk[i])->second; } } else if (discard == NODISCARD) { for (i = 0; i < nlocal; i++) { if (exclude[i]) continue; if (hash->find(ichunk[i]) == hash->end()) ichunk[i] = nchunk; else ichunk[i] = hash->find(ichunk[i])->second; } } else { for (i = 0; i < nlocal; i++) { if (exclude[i]) continue; if (hash->find(ichunk[i]) == hash->end()) exclude[i] = 1; else ichunk[i] = hash->find(ichunk[i])->second; } } // else if no compression apply discard rule by itself } else { if (discard == NODISCARD) { for (i = 0; i < nlocal; i++) { if (exclude[i]) continue; if (ichunk[i] < 1 || ichunk[i] > nchunk) ichunk[i] = nchunk;; } } else { for (i = 0; i < nlocal; i++) { if (exclude[i]) continue; if (ichunk[i] < 1 || ichunk[i] > nchunk) exclude[i] = 1; } } } // set ichunk = 0 for excluded atoms // this should set any ichunk values which have not yet been set for (i = 0; i < nlocal; i++) if (exclude[i]) ichunk[i] = 0; // if newly calculated IDs need to persist, store them in fixstore // yes if idsflag = ONCE or idsflag = NFREQ and lock is in place - int save = 0; if (idsflag == ONCE || (idsflag == NFREQ && lockfix)) { double *vstore = fixstore->vstore; int nlocal = atom->nlocal; for (int i = 0; i < nlocal; i++) vstore[i] = ichunk[i]; } // one-time check if which = MOLECULE and // any chunks do not contain all atoms in the molecule if (molcheck) { check_molecules(); molcheck = 0; } } /* ---------------------------------------------------------------------- setup chunks return nchunk = # of chunks all atoms will be assigned a chunk ID from 1 to Nchunk, or 0 also setup any internal state needed to quickly assign atoms to chunks called from compute_peratom() and also directly from fix ave/chunk and compute chunk commands ------------------------------------------------------------------------- */ int ComputeChunkAtom::setup_chunks() { if (invoked_setup == update->ntimestep) return nchunk; // check if setup needs to be done // no if lock is in place // no if nchunkflag = ONCE, and already done once // otherwise yes // even if no, check if need to re-compute bin volumes // so that fix ave/chunk can do proper density normalization int flag = 0; if (lockfix) flag = 1; if (nchunkflag == ONCE && invoked_setup >= 0) flag = 1; if (flag) { if (binflag && scaleflag == REDUCED && domain->box_change_size) bin_volumes(); return nchunk; } invoked_setup = update->ntimestep; // assign chunk IDs to atoms // will exclude atoms not in group or in optional region // for binning styles, need to setup bins and their volume first // IDs are needed to scan for max ID and for compress() if (binflag) { nchunk = setup_bins(); bin_volumes(); } assign_chunk_ids(); // set nchunk for chunk styles other than binning // for styles other than TYPE, scan for max ID if (which == TYPE) nchunk = atom->ntypes; else if (!binflag) { int nlocal = atom->nlocal; int hi = -1; for (int i = 0; i < nlocal; i++) { if (exclude[i]) continue; if (ichunk[i] > hi) hi = ichunk[i]; } MPI_Allreduce(&hi,&nchunk,1,MPI_INT,MPI_MAX,world); if (nchunk <= 0) nchunk = 1; } // apply limit setting as well as compression of chunks with no atoms // if limit is set, there are 3 cases: // no compression, limit specified before compression, or vice versa if (limit && !binflag) { if (!compress) { if (limitstyle == LIMITMAX) nchunk = MIN(nchunk,limit); else if (limitstyle == LIMITEXACT) nchunk = limit; } else if (limitfirst) { nchunk = MIN(nchunk,limit); } } if (compress) compress_chunk_ids(); if (limit && !binflag && compress) { if (limitstyle == LIMITMAX) nchunk = MIN(nchunk,limit); else if (limitstyle == LIMITEXACT) nchunk = limit; } return nchunk; } /* ---------------------------------------------------------------------- assign chunk IDs for all atoms, via ichunk vector except excluded atoms, their chunk IDs are set to 0 later also set exclude vector to 0/1 for all atoms excluded atoms are those not in group or in optional region called from compute_ichunk() and setup_chunks() ------------------------------------------------------------------------- */ void ComputeChunkAtom::assign_chunk_ids() { int i; // grow integer chunk index vector if necessary if (atom->nlocal > nmaxint) { memory->destroy(ichunk); memory->destroy(exclude); nmaxint = atom->nmax; memory->create(ichunk,nmaxint,"chunk/atom:ichunk"); memory->create(exclude,nmaxint,"chunk/atom:exclude"); } // update region if necessary if (regionflag) region->prematch(); // exclude = 1 if atom is not assigned to a chunk // exclude atoms not in group or not in optional region double **x = atom->x; int *mask = atom->mask; int nlocal = atom->nlocal; if (regionflag) { for (i = 0; i < nlocal; i++) { if (mask[i] & groupbit && region->match(x[i][0],x[i][1],x[i][2])) exclude[i] = 0; else exclude[i] = 1; } } else { for (i = 0; i < nlocal; i++) { if (mask[i] & groupbit) exclude[i] = 0; else exclude[i] = 1; } } // set ichunk to style value for included atoms // binning styles apply discard rule, others do not yet if (binflag) { if (which == BIN1D) atom2bin1d(); else if (which == BIN2D) atom2bin2d(); else if (which == BIN3D) atom2bin3d(); } else if (which == TYPE) { int *type = atom->type; for (i = 0; i < nlocal; i++) { if (exclude[i]) continue; ichunk[i] = type[i]; } } else if (which == MOLECULE) { tagint *molecule = atom->molecule; for (i = 0; i < nlocal; i++) { if (exclude[i]) continue; ichunk[i] = static_cast (molecule[i]); } } else if (which == COMPUTE) { if (!(cchunk->invoked_flag & INVOKED_PERATOM)) { cchunk->compute_peratom(); cchunk->invoked_flag |= INVOKED_PERATOM; } if (argindex == 0) { double *vec = cchunk->vector_atom; for (i = 0; i < nlocal; i++) { if (exclude[i]) continue; ichunk[i] = static_cast (vec[i]); } } else { double **array = cchunk->array_atom; int argm1 = argindex - 1; for (i = 0; i < nlocal; i++) { if (exclude[i]) continue; ichunk[i] = static_cast (array[i][argm1]); } } } else if (which == FIX) { if (update->ntimestep % fchunk->peratom_freq) error->all(FLERR,"Fix used in compute chunk/atom not " "computed at compatible time"); if (argindex == 0) { double *vec = fchunk->vector_atom; - int n = nlocal; for (i = 0; i < nlocal; i++) { if (exclude[i]) continue; ichunk[i] = static_cast (vec[i]); } } else { double **array = fchunk->array_atom; int argm1 = argindex - 1; for (i = 0; i < nlocal; i++) { if (exclude[i]) continue; ichunk[i] = static_cast (array[i][argm1]); } } } else if (which == VARIABLE) { if (nlocal > maxvar) { maxvar = atom->nmax; memory->destroy(varatom); memory->create(varatom,maxvar,"chunk/atom:varatom"); } input->variable->compute_atom(vchunk,igroup,varatom,1,0); for (i = 0; i < nlocal; i++) { if (exclude[i]) continue; ichunk[i] = static_cast (varatom[i]); } } } /* ---------------------------------------------------------------------- compress chunk IDs currently assigned to atoms across all processors by removing those with no atoms assigned current assignment excludes atoms not in group or in optional region current Nchunk = max ID operation: use hash to store list of populated IDs that I own add new IDs to populated lists communicated from all other procs final hash has global list of populated ideas reset Nchunk = length of global list called by setup_chunks() when setting Nchunk remapping of chunk IDs to smaller Nchunk occurs later in compute_ichunk() ------------------------------------------------------------------------- */ void ComputeChunkAtom::compress_chunk_ids() { hash->clear(); // put my IDs into hash int nlocal = atom->nlocal; for (int i = 0; i < nlocal; i++) { if (exclude[i]) continue; if (hash->find(ichunk[i]) == hash->end()) (*hash)[ichunk[i]] = 0; } // n = # of my populated IDs // nall = n summed across all procs int n = hash->size(); bigint nbone = n; bigint nball; MPI_Allreduce(&nbone,&nball,1,MPI_LMP_BIGINT,MPI_SUM,world); // create my list of populated IDs int *list = NULL; memory->create(list,n,"chunk/atom:list"); n = 0; std::map::iterator pos; for (pos = hash->begin(); pos != hash->end(); ++pos) list[n++] = pos->first; // if nall < 1M, just allgather all ID lists on every proc // else perform ring comm // add IDs from all procs to my hash if (nball <= IDMAX) { // setup for allgatherv int nprocs = comm->nprocs; int nall = nball; int *recvcounts,*displs,*listall; memory->create(recvcounts,nprocs,"chunk/atom:recvcounts"); memory->create(displs,nprocs,"chunk/atom:displs"); memory->create(listall,nall,"chunk/atom:listall"); 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]; // allgatherv acquires list of populated IDs from all procs MPI_Allgatherv(list,n,MPI_INT,listall,recvcounts,displs,MPI_INT,world); // add all unique IDs in listall to my hash for (int i = 0; i < nall; i++) if (hash->find(listall[i]) == hash->end()) (*hash)[listall[i]] = 0; // clean up memory->destroy(recvcounts); memory->destroy(displs); memory->destroy(listall); } else { cptr = this; comm->ring(n,sizeof(int),list,1,idring,NULL,0); } memory->destroy(list); // nchunk = length of hash containing populated IDs from all procs nchunk = hash->size(); // reset hash value of each original chunk ID to ordered index // ordered index = new compressed chunk ID (1 to Nchunk) // leverages fact that map stores keys in ascending order // also allocate and set chunkID = list of original chunk IDs // used by fix ave/chunk and compute property/chunk memory->destroy(chunkID); memory->create(chunkID,nchunk,"chunk/atom:chunkID"); n = 0; for (pos = hash->begin(); pos != hash->end(); ++pos) { chunkID[n] = pos->first; (*hash)[pos->first] = ++n; } } /* ---------------------------------------------------------------------- callback from comm->ring() cbuf = list of N chunk IDs from another proc loop over the list, add each to my hash hash ends up storing all unique IDs across all procs ------------------------------------------------------------------------- */ void ComputeChunkAtom::idring(int n, char *cbuf) { tagint *list = (tagint *) cbuf; std::map *hash = cptr->hash; for (int i = 0; i < n; i++) (*hash)[list[i]] = 0; } /* ---------------------------------------------------------------------- one-time check for which = MOLECULE to check if each chunk contains all atoms in the molecule issue warning if not note that this check is without regard to discard rule if discard == NODISCARD, there is no easy way to check that all atoms in an out-of-bounds molecule were added to a chunk, some could have been excluded by group or region, others not ------------------------------------------------------------------------- */ void ComputeChunkAtom::check_molecules() { int *molecule = atom->molecule; int nlocal = atom->nlocal; int flag = 0; if (!compress) { for (int i = 0; i < nlocal; i++) { if (molecule[i] > 0 && molecule[i] <= nchunk && ichunk[i] == 0) flag = 1; } } else { int molid; for (int i = 0; i < nlocal; i++) { molid = static_cast (molecule[i]); if (hash->find(molid) != hash->end() && ichunk[i] == 0) flag = 1; } } int flagall; MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_SUM,world); if (flagall && comm->me == 0) error->warning(FLERR, "One or more chunks do not contain all atoms in molecule"); } /* ---------------------------------------------------------------------- setup spatial bins and their extent and coordinates return nbins = # of bins, will become # of chunks called from setup_chunks() ------------------------------------------------------------------------- */ int ComputeChunkAtom::setup_bins() { int i,j,k,m,n,idim; double lo,hi,coord1,coord2; // lo = bin boundary immediately below boxlo or minvalue // hi = bin boundary immediately above boxhi or maxvalue // allocate and initialize arrays based on new bin count double binlo[3],binhi[3]; double *prd; if (scaleflag == REDUCED) { binlo[0] = domain->boxlo_lamda[0]; binlo[1] = domain->boxlo_lamda[1]; binlo[2] = domain->boxlo_lamda[2]; binhi[0] = domain->boxhi_lamda[0]; binhi[1] = domain->boxhi_lamda[1]; binhi[2] = domain->boxhi_lamda[2]; prd = domain->prd_lamda; } else { binlo[0] = domain->boxlo[0]; binlo[1] = domain->boxlo[1]; binlo[2] = domain->boxlo[2]; binhi[0] = domain->boxhi[0]; binhi[1] = domain->boxhi[1]; binhi[2] = domain->boxhi[2]; prd = domain->prd; } if (minflag[0] == COORD) binlo[0] = minvalue[0]; if (minflag[1] == COORD) binlo[1] = minvalue[1]; if (minflag[2] == COORD) binlo[2] = minvalue[2]; if (maxflag[0] == COORD) binhi[0] = maxvalue[0]; if (maxflag[1] == COORD) binhi[1] = maxvalue[1]; if (maxflag[2] == COORD) binhi[2] = maxvalue[2]; int nbins = 1; for (m = 0; m < ndim; m++) { idim = dim[m]; if (originflag[m] == LOWER) origin[m] = binlo[idim]; else if (originflag[m] == UPPER) origin[m] = binhi[idim]; else if (originflag[m] == CENTER) origin[m] = 0.5 * (binlo[idim] + binhi[idim]); if (origin[m] < binlo[idim]) { n = static_cast ((binlo[idim] - origin[m]) * invdelta[m]); lo = origin[m] + n*delta[m]; } else { n = static_cast ((origin[m] - binlo[idim]) * invdelta[m]); lo = origin[m] - n*delta[m]; if (lo > binlo[idim]) lo -= delta[m]; } if (origin[m] < binhi[idim]) { n = static_cast ((binhi[idim] - origin[m]) * invdelta[m]); hi = origin[m] + n*delta[m]; if (hi < binhi[idim]) hi += delta[m]; } else { n = static_cast ((origin[m] - binhi[idim]) * invdelta[m]); hi = origin[m] - n*delta[m]; } if (lo > hi) error->all(FLERR,"Invalid bin bounds in fix ave/spatial"); offset[m] = lo; nlayers[m] = static_cast ((hi-lo) * invdelta[m] + 0.5); nbins *= nlayers[m]; chunk_volume_scalar *= delta[m]/prd[idim]; } // allocate and set bin coordinates memory->destroy(coord); memory->create(coord,nbins,ndim,"chunk/atom:coord"); if (ndim == 1) { for (i = 0; i < nlayers[0]; i++) coord[i][0] = offset[0] + (i+0.5)*delta[0]; } else if (ndim == 2) { m = 0; for (i = 0; i < nlayers[0]; i++) { coord1 = offset[0] + (i+0.5)*delta[0]; for (j = 0; j < nlayers[1]; j++) { coord[m][0] = coord1; coord[m][1] = offset[1] + (j+0.5)*delta[1]; m++; } } } else if (ndim == 3) { m = 0; for (i = 0; i < nlayers[0]; i++) { coord1 = offset[0] + (i+0.5)*delta[0]; for (j = 0; j < nlayers[1]; j++) { coord2 = offset[1] + (j+0.5)*delta[1]; for (k = 0; k < nlayers[2]; k++) { coord[m][0] = coord1; coord[m][1] = coord2; coord[m][2] = offset[2] + (k+0.5)*delta[2]; m++; } } } } return nbins; } /* ---------------------------------------------------------------------- calculate chunk volumes = bin volumes scalar if all bins have same volume vector if per-bin volumes are different ------------------------------------------------------------------------- */ void ComputeChunkAtom::bin_volumes() { if (which == BIN1D || which == BIN2D || which == BIN3D) { if (domain->dimension == 3) chunk_volume_scalar = domain->xprd * domain->yprd * domain->zprd; else chunk_volume_scalar = domain->xprd * domain->yprd; double *prd; if (scaleflag == REDUCED) prd = domain->prd_lamda; else prd = domain->prd; for (int m = 0; m < ndim; m++) chunk_volume_scalar *= delta[m]/prd[dim[m]]; } else { memory->destroy(chunk_volume_vec); memory->create(chunk_volume_vec,nchunk,"chunk/atom:chunk_volume_vec"); // fill in the vector values } } /* ---------------------------------------------------------------------- assign each atom to a 1d spatial bin (layer) ------------------------------------------------------------------------- */ void ComputeChunkAtom::atom2bin1d() { int i,ibin; double *boxlo,*boxhi,*prd; double xremap; - double lamda[3]; double **x = atom->x; - int *mask = atom->mask; int nlocal = atom->nlocal; int idim = dim[0]; int nlayer1m1 = nlayers[0] - 1; int periodicity = domain->periodicity[idim]; if (periodicity) { if (scaleflag == REDUCED) { boxlo = domain->boxlo_lamda; boxhi = domain->boxhi_lamda; prd = domain->prd_lamda; } else { boxlo = domain->boxlo; boxhi = domain->boxhi; prd = domain->prd; } } // remap each atom's relevant coord back into box via PBC if necessary // if scaleflag = REDUCED, box coords -> lamda coords // apply discard rule if (scaleflag == REDUCED) domain->x2lamda(nlocal); for (i = 0; i < nlocal; i++) { if (exclude[i]) continue; xremap = x[i][idim]; if (periodicity) { if (xremap < boxlo[idim]) xremap += prd[idim]; if (xremap >= boxhi[idim]) xremap -= prd[idim]; } ibin = static_cast ((xremap - offset[0]) * invdelta[0]); if (xremap < offset[0]) ibin--; if (discard == MIXED) { if (!minflag[idim]) ibin = MAX(ibin,0); else if (ibin < 0) { exclude[i] = 1; continue; } if (!maxflag[idim]) ibin = MIN(ibin,nlayer1m1); else if (ibin > nlayer1m1) { exclude[i] = 1; continue; } } else if (discard == NODISCARD) { ibin = MAX(ibin,0); ibin = MIN(ibin,nlayer1m1); } else if (ibin < 0 || ibin > nlayer1m1) { exclude[i] = 1; continue; } ichunk[i] = ibin+1; } if (scaleflag == REDUCED) domain->lamda2x(nlocal); } /* ---------------------------------------------------------------------- assign each atom to a 2d spatial bin (pencil) ------------------------------------------------------------------------- */ void ComputeChunkAtom::atom2bin2d() { int i,ibin,i1bin,i2bin; double *boxlo,*boxhi,*prd; double xremap,yremap; - double lamda[3]; double **x = atom->x; - int *mask = atom->mask; int nlocal = atom->nlocal; int idim = dim[0]; int jdim = dim[1]; int nlayer1m1 = nlayers[0] - 1; int nlayer2m1 = nlayers[1] - 1; int *periodicity = domain->periodicity; if (periodicity[idim] || periodicity[jdim]) { if (scaleflag == REDUCED) { boxlo = domain->boxlo_lamda; boxhi = domain->boxhi_lamda; prd = domain->prd_lamda; } else { boxlo = domain->boxlo; boxhi = domain->boxhi; prd = domain->prd; } } // remap each atom's relevant coord back into box via PBC if necessary // if scaleflag = REDUCED, box coords -> lamda coords // apply discard rule if (scaleflag == REDUCED) domain->x2lamda(nlocal); for (i = 0; i < nlocal; i++) { if (exclude[i]) continue; xremap = x[i][idim]; if (periodicity[idim]) { if (xremap < boxlo[idim]) xremap += prd[idim]; if (xremap >= boxhi[idim]) xremap -= prd[idim]; } i1bin = static_cast ((xremap - offset[0]) * invdelta[0]); if (xremap < offset[0]) i1bin--; if (discard == MIXED) { if (!minflag[idim]) i1bin = MAX(i1bin,0); else if (i1bin < 0) { exclude[i] = 1; continue; } if (!maxflag[idim]) i1bin = MIN(i1bin,nlayer1m1); else if (i1bin > nlayer1m1) { exclude[i] = 1; continue; } } else if (discard == NODISCARD) { i1bin = MAX(i1bin,0); i1bin = MIN(i1bin,nlayer1m1); } else if (i1bin < 0 || i1bin > nlayer1m1) { exclude[i] = 1; continue; } yremap = x[i][jdim]; if (periodicity[jdim]) { if (yremap < boxlo[jdim]) yremap += prd[jdim]; if (yremap >= boxhi[jdim]) yremap -= prd[jdim]; } i2bin = static_cast ((yremap - offset[1]) * invdelta[1]); if (yremap < offset[1]) i2bin--; if (discard == MIXED) { if (!minflag[jdim]) i2bin = MAX(i2bin,0); else if (i2bin < 0) { exclude[i] = 1; continue; } if (!maxflag[jdim]) i2bin = MIN(i2bin,nlayer2m1); else if (i2bin > nlayer1m1) { exclude[i] = 1; continue; } } else if (discard == NODISCARD) { i2bin = MAX(i2bin,0); i2bin = MIN(i2bin,nlayer2m1); } else if (i2bin < 0 || i2bin > nlayer2m1) { exclude[i] = 1; continue; } ibin = i1bin*nlayers[1] + i2bin; ichunk[i] = ibin+1; } if (scaleflag == REDUCED) domain->lamda2x(nlocal); } /* ---------------------------------------------------------------------- assign each atom to a 3d spatial bin (brick) ------------------------------------------------------------------------- */ void ComputeChunkAtom::atom2bin3d() { int i,ibin,i1bin,i2bin,i3bin; double *boxlo,*boxhi,*prd; double xremap,yremap,zremap; - double lamda[3]; double **x = atom->x; - int *mask = atom->mask; int nlocal = atom->nlocal; int idim = dim[0]; int jdim = dim[1]; int kdim = dim[2]; int nlayer1m1 = nlayers[0] - 1; int nlayer2m1 = nlayers[1] - 1; int nlayer3m1 = nlayers[2] - 1; int *periodicity = domain->periodicity; if (periodicity[idim] || periodicity[jdim] || periodicity[kdim]) { if (scaleflag == REDUCED) { boxlo = domain->boxlo_lamda; boxhi = domain->boxhi_lamda; prd = domain->prd_lamda; } else { boxlo = domain->boxlo; boxhi = domain->boxhi; prd = domain->prd; } } // remap each atom's relevant coord back into box via PBC if necessary // if scaleflag = REDUCED, box coords -> lamda coords // apply discard rule if (scaleflag == REDUCED) domain->x2lamda(nlocal); for (i = 0; i < nlocal; i++) { if (exclude[i]) continue; xremap = x[i][idim]; if (periodicity[idim]) { if (xremap < boxlo[idim]) xremap += prd[idim]; if (xremap >= boxhi[idim]) xremap -= prd[idim]; } i1bin = static_cast ((xremap - offset[0]) * invdelta[0]); if (xremap < offset[0]) i1bin--; if (discard == MIXED) { if (!minflag[idim]) i1bin = MAX(i1bin,0); else if (i1bin < 0) { exclude[i] = 1; continue; } if (!maxflag[idim]) i1bin = MIN(i1bin,nlayer1m1); else if (i1bin > nlayer1m1) { exclude[i] = 1; continue; } } else if (discard == NODISCARD) { i1bin = MAX(i1bin,0); i1bin = MIN(i1bin,nlayer1m1); } else if (i1bin < 0 || i1bin > nlayer1m1) { exclude[i] = 1; continue; } yremap = x[i][jdim]; if (periodicity[jdim]) { if (yremap < boxlo[jdim]) yremap += prd[jdim]; if (yremap >= boxhi[jdim]) yremap -= prd[jdim]; } i2bin = static_cast ((yremap - offset[1]) * invdelta[1]); if (yremap < offset[1]) i2bin--; if (discard == MIXED) { if (!minflag[jdim]) i2bin = MAX(i2bin,0); else if (i2bin < 0) { exclude[i] = 1; continue; } if (!maxflag[jdim]) i2bin = MIN(i2bin,nlayer2m1); else if (i2bin > nlayer1m1) { exclude[i] = 1; continue; } } else if (discard == NODISCARD) { i2bin = MAX(i2bin,0); i2bin = MIN(i2bin,nlayer2m1); } else if (i2bin < 0 || i2bin > nlayer2m1) { exclude[i] = 1; continue; } zremap = x[i][kdim]; if (periodicity[kdim]) { if (zremap < boxlo[kdim]) zremap += prd[kdim]; if (zremap >= boxhi[kdim]) zremap -= prd[kdim]; } i3bin = static_cast ((zremap - offset[2]) * invdelta[2]); if (zremap < offset[2]) i3bin--; if (discard == MIXED) { if (!minflag[kdim]) i3bin = MAX(i3bin,0); else if (i3bin < 0) { exclude[i] = 1; continue; } if (!maxflag[kdim]) i3bin = MIN(i3bin,nlayer3m1); else if (i3bin > nlayer3m1) { exclude[i] = 1; continue; } } else if (discard == NODISCARD) { i3bin = MAX(i3bin,0); i3bin = MIN(i3bin,nlayer3m1); } else if (i3bin < 0 || i3bin > nlayer3m1) { exclude[i] = 1; continue; } ibin = i1bin*nlayers[1]*nlayers[2] + i2bin*nlayers[2] + i3bin; ichunk[i] = ibin+1; } if (scaleflag == REDUCED) domain->lamda2x(nlocal); } /* ---------------------------------------------------------------------- process args for one dimension of binning info ------------------------------------------------------------------------- */ void ComputeChunkAtom::readdim(int narg, char **arg, int iarg, int idim) { if (narg < iarg+3) error->all(FLERR,"Illegal compute chunk/atom command"); if (strcmp(arg[iarg],"x") == 0) dim[idim] = 0; else if (strcmp(arg[iarg],"y") == 0) dim[idim] = 1; else if (strcmp(arg[iarg],"z") == 0) dim[idim] = 2; if (dim[idim] == 2 && domain->dimension == 2) error->all(FLERR,"Cannot use compute chunk/atom bin z for 2d model"); if (strcmp(arg[iarg+1],"lower") == 0) originflag[idim] = LOWER; else if (strcmp(arg[iarg+1],"center") == 0) originflag[idim] = CENTER; else if (strcmp(arg[iarg+1],"upper") == 0) originflag[idim] = UPPER; else originflag[idim] = COORD; if (originflag[idim] == COORD) origin[idim] = force->numeric(FLERR,arg[iarg+1]); delta[idim] = force->numeric(FLERR,arg[iarg+2]); } /* ---------------------------------------------------------------------- initialize one atom's storage values, called when atom is created just set chunkID to 0 for new atom ------------------------------------------------------------------------- */ void ComputeChunkAtom::set_arrays(int i) { if (!fixstore) return; double *vstore = fixstore->vstore; vstore[i] = 0.0; } /* ---------------------------------------------------------------------- memory usage of local atom-based arrays and per-chunk arrays note: nchunk is actually 0 until first call ------------------------------------------------------------------------- */ double ComputeChunkAtom::memory_usage() { double bytes = 2*nmaxint * sizeof(int); // ichunk,exclude bytes += nmax * sizeof(double); // chunk bytes += ncoord*nchunk * sizeof(double); // coord if (compress) bytes += nchunk * sizeof(int); // chunkID return bytes; } diff --git a/src/compute_temp_chunk.cpp b/src/compute_temp_chunk.cpp index fdd800f3a..2d36bdf0b 100644 --- a/src/compute_temp_chunk.cpp +++ b/src/compute_temp_chunk.cpp @@ -1,858 +1,854 @@ /* ---------------------------------------------------------------------- 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 "string.h" #include "compute_temp_chunk.h" #include "atom.h" #include "update.h" #include "force.h" #include "modify.h" #include "compute_chunk_atom.h" #include "domain.h" #include "memory.h" #include "error.h" using namespace LAMMPS_NS; enum{TEMP,KECOM,INTERNAL}; /* ---------------------------------------------------------------------- */ ComputeTempChunk::ComputeTempChunk(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg) { if (narg < 4) error->all(FLERR,"Illegal compute temp/chunk command"); scalar_flag = vector_flag = 1; size_vector = 6; extscalar = 0; extvector = 1; tempflag = 1; // ID of compute chunk/atom int n = strlen(arg[3]) + 1; idchunk = new char[n]; strcpy(idchunk,arg[3]); biasflag = 0; init(); // optional per-chunk values nvalues = narg-4; which = new int[nvalues]; nvalues = 0; int iarg = 4; while (iarg < narg) { if (strcmp(arg[iarg],"temp") == 0) which[nvalues] = TEMP; else if (strcmp(arg[iarg],"kecom") == 0) which[nvalues] = KECOM; else if (strcmp(arg[iarg],"internal") == 0) which[nvalues] = INTERNAL; else break; iarg++; nvalues++; } // optional args comflag = 0; biasflag = 0; id_bias = NULL; adof = domain->dimension; cdof = 0.0; while (iarg < narg) { if (strcmp(arg[iarg],"com") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal compute temp/chunk command"); if (strcmp(arg[iarg+1],"yes") == 0) comflag = 1; else if (strcmp(arg[iarg+1],"no") == 0) comflag = 0; else error->all(FLERR,"Illegal compute temp/chunk command"); iarg += 2; } else if (strcmp(arg[iarg],"bias") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal compute temp/chunk command"); biasflag = 1; int n = strlen(arg[iarg+1]) + 1; id_bias = new char[n]; strcpy(id_bias,arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"adof") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal compute temp/chunk command"); adof = force->numeric(FLERR,arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"cdof") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal compute temp/chunk command"); cdof = force->numeric(FLERR,arg[iarg+1]); iarg += 2; } else error->all(FLERR,"Illegal compute temp/chunk command"); } // error check on bias compute if (biasflag) { int i = modify->find_compute(id_bias); if (i < 0) error->all(FLERR,"Could not find compute ID for temperature bias"); tbias = modify->compute[i]; if (tbias->tempflag == 0) error->all(FLERR,"Bias compute does not calculate temperature"); if (tbias->tempbias == 0) error->all(FLERR,"Bias compute does not calculate a velocity bias"); } // this compute only calculates a bias, if comflag is set // won't be two biases since comflag and biasflag cannot both be set if (comflag && biasflag) error->all(FLERR,"Cannot use both com and bias with compute temp/chunk"); if (comflag) tempbias = 1; // vector data vector = new double[6]; // chunk-based data nchunk = 1; maxchunk = 0; sum = sumall = NULL; count = countall = NULL; massproc = masstotal = NULL; vcm = vcmall = NULL; array = NULL; if (nvalues) { array_flag = 1; size_array_cols = nvalues; size_array_rows = 0; size_array_rows_variable = 1; extarray = 0; } allocate(); comstep = -1; } /* ---------------------------------------------------------------------- */ ComputeTempChunk::~ComputeTempChunk() { delete [] idchunk; delete [] which; delete [] id_bias; delete [] vector; memory->destroy(sum); memory->destroy(sumall); memory->destroy(count); memory->destroy(countall); memory->destroy(array); memory->destroy(massproc); memory->destroy(masstotal); memory->destroy(vcm); memory->destroy(vcmall); } /* ---------------------------------------------------------------------- */ void ComputeTempChunk::init() { int icompute = modify->find_compute(idchunk); if (icompute < 0) error->all(FLERR,"Chunk/atom compute does not exist for " "compute temp/chunk"); cchunk = (ComputeChunkAtom *) modify->compute[icompute]; if (strcmp(cchunk->style,"chunk/atom") != 0) error->all(FLERR,"Compute temp/chunk does not use chunk/atom compute"); if (biasflag) { int i = modify->find_compute(id_bias); if (i < 0) error->all(FLERR,"Could not find compute ID for temperature bias"); tbias = modify->compute[i]; } } /* ---------------------------------------------------------------------- */ double ComputeTempChunk::compute_scalar() { int i,index; invoked_scalar = update->ntimestep; // calculate chunk assignments, // since only atoms in chunks contribute to global temperature // compute chunk/atom assigns atoms to chunk IDs // extract ichunk index vector from compute // ichunk = 1 to Nchunk for included atoms, 0 for excluded atoms nchunk = cchunk->setup_chunks(); cchunk->compute_ichunk(); int *ichunk = cchunk->ichunk; // remove velocity bias if (biasflag) { if (tbias->invoked_scalar != update->ntimestep) tbias->compute_scalar(); tbias->remove_bias_all(); } // calculate COM velocity for each chunk // won't be invoked with bias also removed = 2 biases if (comflag && comstep != update->ntimestep) vcm_compute(); // calculate global temperature, optionally removing COM velocity double **v = atom->v; double *mass = atom->mass; double *rmass = atom->rmass; int *type = atom->type; int *mask = atom->mask; int nlocal = atom->nlocal; double t = 0.0; int mycount = 0; if (!comflag) { if (rmass) { for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) { index = ichunk[i]-1; if (index < 0) continue; t += (v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]) * rmass[i]; mycount++; } } else { for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) { index = ichunk[i]-1; if (index < 0) continue; t += (v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]) * mass[type[i]]; mycount++; } } } else { double vx,vy,vz; if (rmass) { for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) { index = ichunk[i]-1; if (index < 0) continue; vx = v[i][0] - vcmall[index][0]; vy = v[i][1] - vcmall[index][1]; vz = v[i][2] - vcmall[index][2]; t += (vx*vx + vy*vy + vz*vz) * rmass[i]; mycount++; } } else { for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) { index = ichunk[i]-1; if (index < 0) continue; vx = v[i][0] - vcmall[index][0]; vy = v[i][1] - vcmall[index][1]; vz = v[i][2] - vcmall[index][2]; t += (vx*vx + vy*vy + vz*vz) * mass[type[i]]; mycount++; } } } // restore velocity bias if (biasflag) tbias->restore_bias_all(); // final temperature MPI_Allreduce(&t,&scalar,1,MPI_DOUBLE,MPI_SUM,world); double rcount = mycount; double allcount; MPI_Allreduce(&rcount,&allcount,1,MPI_DOUBLE,MPI_SUM,world); double dof = nchunk*cdof + adof*allcount; double tfactor = 0.0; if (dof > 0.0) tfactor = force->mvv2e / (dof * force->boltz); scalar *= tfactor; return scalar; } /* ---------------------------------------------------------------------- */ void ComputeTempChunk::compute_vector() { int i,index; invoked_vector = update->ntimestep; // calculate chunk assignments, // since only atoms in chunks contribute to global temperature // compute chunk/atom assigns atoms to chunk IDs // extract ichunk index vector from compute // ichunk = 1 to Nchunk for included atoms, 0 for excluded atoms nchunk = cchunk->setup_chunks(); cchunk->compute_ichunk(); int *ichunk = cchunk->ichunk; // remove velocity bias if (biasflag) { if (tbias->invoked_scalar != update->ntimestep) tbias->compute_scalar(); tbias->remove_bias_all(); } // calculate COM velocity for each chunk // won't be invoked with bias also removed = 2 biases if (comflag && comstep != update->ntimestep) vcm_compute(); // calculate KE tensor, optionally removing COM velocity double **v = atom->v; double *mass = atom->mass; double *rmass = atom->rmass; int *type = atom->type; int *mask = atom->mask; int nlocal = atom->nlocal; double massone,t[6]; for (i = 0; i < 6; i++) t[i] = 0.0; if (!comflag) { for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) { index = ichunk[i]-1; if (index < 0) continue; if (rmass) massone = rmass[i]; else massone = mass[type[i]]; t[0] += massone * v[i][0]*v[i][0]; t[1] += massone * v[i][1]*v[i][1]; t[2] += massone * v[i][2]*v[i][2]; t[3] += massone * v[i][0]*v[i][1]; t[4] += massone * v[i][0]*v[i][2]; t[5] += massone * v[i][1]*v[i][2]; } } else { double vx,vy,vz; for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) { index = ichunk[i]-1; if (index < 0) continue; if (rmass) massone = rmass[i]; else massone = mass[type[i]]; vx = v[i][0] - vcmall[index][0]; vy = v[i][1] - vcmall[index][1]; vz = v[i][2] - vcmall[index][2]; t[0] += massone * vx*vx; t[1] += massone * vy*vy; t[2] += massone * vz*vz; t[3] += massone * vx*vy; t[4] += massone * vx*vz; t[5] += massone * vy*vz; } } // restore velocity bias if (biasflag) tbias->restore_bias_all(); // final KE MPI_Allreduce(t,vector,6,MPI_DOUBLE,MPI_SUM,world); for (i = 0; i < 6; i++) vector[i] *= force->mvv2e; } /* ---------------------------------------------------------------------- */ void ComputeTempChunk::compute_array() { - int index; - invoked_array = update->ntimestep; // compute chunk/atom assigns atoms to chunk IDs // extract ichunk index vector from compute // ichunk = 1 to Nchunk for included atoms, 0 for excluded atoms nchunk = cchunk->setup_chunks(); cchunk->compute_ichunk(); - int *ichunk = cchunk->ichunk; if (nchunk > maxchunk) allocate(); size_array_rows = nchunk; // remove velocity bias if (biasflag) { if (tbias->invoked_scalar != update->ntimestep) tbias->compute_scalar(); tbias->remove_bias_all(); } // calculate COM velocity for each chunk whether comflag set or not // needed by some values even if comflag not set // important to do this after velocity bias is removed // otherwise per-chunk values that use both v and vcm will be inconsistent if (comstep != update->ntimestep) vcm_compute(); // compute each value for (int i = 0; i < nvalues; i++) { if (which[i] == TEMP) temperature(i); else if (which[i] == KECOM) kecom(i); else if (which[i] == INTERNAL) internal(i); } // restore velocity bias if (biasflag) tbias->restore_bias_all(); } /* ---------------------------------------------------------------------- calculate velocity of COM for each chunk ------------------------------------------------------------------------- */ void ComputeTempChunk::vcm_compute() { int i,index; double massone; // avoid re-computing VCM more than once per step comstep = update->ntimestep; int *ichunk = cchunk->ichunk; for (int i = 0; i < nchunk; i++) { vcm[i][0] = vcm[i][1] = vcm[i][2] = 0.0; massproc[i] = 0.0; } double **v = atom->v; int *mask = atom->mask; int *type = atom->type; double *mass = atom->mass; double *rmass = atom->rmass; int nlocal = atom->nlocal; for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) { index = ichunk[i]-1; if (index < 0) continue; if (rmass) massone = rmass[i]; else massone = mass[type[i]]; vcm[index][0] += v[i][0] * massone; vcm[index][1] += v[i][1] * massone; vcm[index][2] += v[i][2] * massone; massproc[index] += massone; } MPI_Allreduce(&vcm[0][0],&vcmall[0][0],3*nchunk,MPI_DOUBLE,MPI_SUM,world); MPI_Allreduce(massproc,masstotal,nchunk,MPI_DOUBLE,MPI_SUM,world); for (i = 0; i < nchunk; i++) { vcmall[i][0] /= masstotal[i]; vcmall[i][1] /= masstotal[i]; vcmall[i][2] /= masstotal[i]; } } /* ---------------------------------------------------------------------- temperature of each chunk ------------------------------------------------------------------------- */ void ComputeTempChunk::temperature(int icol) { int i,index; int *ichunk = cchunk->ichunk; // zero local per-chunk values for (int i = 0; i < nchunk; i++) { count[i] = 0; sum[i] = 0.0; } // per-chunk temperature, option for removing COM velocity double **v = atom->v; double *mass = atom->mass; double *rmass = atom->rmass; int *mask = atom->mask; int *type = atom->type; int nlocal = atom->nlocal; if (!comflag) { if (rmass) { for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) { index = ichunk[i]-1; if (index < 0) continue; sum[index] += (v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]) * rmass[i]; count[index]++; } } else { for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) { index = ichunk[i]-1; if (index < 0) continue; sum[index] += (v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]) * mass[type[i]]; count[index]++; } } } else { double vx,vy,vz; if (rmass) { for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) { index = ichunk[i]-1; if (index < 0) continue; vx = v[i][0] - vcmall[index][0]; vy = v[i][1] - vcmall[index][1]; vz = v[i][2] - vcmall[index][2]; sum[index] += (vx*vx + vy*vy + vz*vz) * rmass[i]; count[index]++; } } else { for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) { index = ichunk[i]-1; if (index < 0) continue; vx = v[i][0] - vcmall[index][0]; vy = v[i][1] - vcmall[index][1]; vz = v[i][2] - vcmall[index][2]; sum[index] += (vx*vx + vy*vy + vz*vz) * mass[type[i]]; count[index]++; } } } // sum across procs MPI_Allreduce(sum,sumall,nchunk,MPI_DOUBLE,MPI_SUM,world); MPI_Allreduce(count,countall,nchunk,MPI_INT,MPI_SUM,world); // normalize temperatures by per-chunk DOF double dof,tfactor; double mvv2e = force->mvv2e; double boltz = force->boltz; for (int i = 0; i < nchunk; i++) { dof = cdof + adof*countall[i]; if (dof > 0.0) tfactor = mvv2e / (dof * boltz); else tfactor = 0.0; array[i][icol] = tfactor * sumall[i]; } } /* ---------------------------------------------------------------------- KE of entire chunk moving at VCM ------------------------------------------------------------------------- */ void ComputeTempChunk::kecom(int icol) { - int i,index; + int index; int *ichunk = cchunk->ichunk; // zero local per-chunk values for (int i = 0; i < nchunk; i++) sum[i] = 0.0; // per-chunk COM KE - double **v = atom->v; double *mass = atom->mass; double *rmass = atom->rmass; int *mask = atom->mask; int *type = atom->type; int nlocal = atom->nlocal; double vx,vy,vz; if (rmass) { for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { index = ichunk[i]-1; if (index < 0) continue; vx = vcmall[index][0]; vy = vcmall[index][1]; vz = vcmall[index][2]; sum[index] += (vx*vx + vy*vy + vz*vz) * rmass[i]; } } else { for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { index = ichunk[i]-1; if (index < 0) continue; vx = vcmall[index][0]; vy = vcmall[index][1]; vz = vcmall[index][2]; sum[index] += (vx*vx + vy*vy + vz*vz) * mass[type[i]]; } } // sum across procs MPI_Allreduce(sum,sumall,nchunk,MPI_DOUBLE,MPI_SUM,world); double mvv2e = force->mvv2e; for (int i = 0; i < nchunk; i++) array[i][icol] = 0.5 * mvv2e * sumall[i]; } /* ---------------------------------------------------------------------- internal KE of each chunk around its VCM computed using per-atom velocities with chunk VCM subtracted off ------------------------------------------------------------------------- */ void ComputeTempChunk::internal(int icol) { - int i,index; + int index; int *ichunk = cchunk->ichunk; // zero local per-chunk values for (int i = 0; i < nchunk; i++) sum[i] = 0.0; // per-chunk internal KE double **v = atom->v; double *mass = atom->mass; double *rmass = atom->rmass; int *mask = atom->mask; int *type = atom->type; int nlocal = atom->nlocal; double vx,vy,vz; if (rmass) { for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { index = ichunk[i]-1; if (index < 0) continue; vx = v[i][0] - vcmall[index][0]; vy = v[i][1] - vcmall[index][1]; vz = v[i][2] - vcmall[index][2]; sum[index] += (vx*vx + vy*vy + vz*vz) * rmass[i]; } } else { for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { index = ichunk[i]-1; if (index < 0) continue; vx = v[i][0] - vcmall[index][0]; vy = v[i][1] - vcmall[index][1]; vz = v[i][2] - vcmall[index][2]; sum[index] += (vx*vx + vy*vy + vz*vz) * mass[type[i]]; } } // sum across procs MPI_Allreduce(sum,sumall,nchunk,MPI_DOUBLE,MPI_SUM,world); double mvv2e = force->mvv2e; for (int i = 0; i < nchunk; i++) array[i][icol] = 0.5 * mvv2e * sumall[i]; } /* ---------------------------------------------------------------------- bias methods: called by thermostats ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- remove velocity bias from atom I to leave thermal velocity ------------------------------------------------------------------------- */ void ComputeTempChunk::remove_bias(int i, double *v) { int index = cchunk->ichunk[i]; if (index < 0) return; v[0] -= vcmall[index][0]; v[1] -= vcmall[index][1]; v[2] -= vcmall[index][2]; } /* ---------------------------------------------------------------------- remove velocity bias from all atoms to leave thermal velocity ------------------------------------------------------------------------- */ void ComputeTempChunk::remove_bias_all() { int index; int *ichunk = cchunk->ichunk; double **v = atom->v; int *mask = atom->mask; int nlocal = atom->nlocal; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { index = ichunk[i]; if (index < 0) continue; v[i][0] -= vbias[0]; v[i][1] -= vbias[1]; v[i][2] -= vbias[2]; } } /* ---------------------------------------------------------------------- add back in velocity bias to atom I removed by remove_bias() assume remove_bias() was previously called ------------------------------------------------------------------------- */ void ComputeTempChunk::restore_bias(int i, double *v) { int index = cchunk->ichunk[i]; if (index < 0) return; v[0] += vcmall[index][0]; v[1] += vcmall[index][1]; v[2] += vcmall[index][2]; } /* ---------------------------------------------------------------------- add back in velocity bias to all atoms removed by remove_bias_all() assume remove_bias_all() was previously called ------------------------------------------------------------------------- */ void ComputeTempChunk::restore_bias_all() { int index; int *ichunk = cchunk->ichunk; double **v = atom->v; int *mask = atom->mask; int nlocal = atom->nlocal; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { index = ichunk[i]; if (index < 0) continue; v[i][0] += vbias[0]; v[i][1] += vbias[1]; v[i][2] += vbias[2]; } } /* ---------------------------------------------------------------------- lock methods: called by fix ave/time these methods insure vector/array size is locked for Nfreq epoch by passing lock info along to compute chunk/atom ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- increment lock counter ------------------------------------------------------------------------- */ void ComputeTempChunk::lock_enable() { cchunk->lockcount++; } /* ---------------------------------------------------------------------- decrement lock counter in compute chunk/atom, it if still exists ------------------------------------------------------------------------- */ void ComputeTempChunk::lock_disable() { int icompute = modify->find_compute(idchunk); if (icompute >= 0) { cchunk = (ComputeChunkAtom *) modify->compute[icompute]; cchunk->lockcount--; } } /* ---------------------------------------------------------------------- calculate and return # of chunks = length of vector/array ------------------------------------------------------------------------- */ int ComputeTempChunk::lock_length() { nchunk = cchunk->setup_chunks(); return nchunk; } /* ---------------------------------------------------------------------- set the lock from startstep to stopstep ------------------------------------------------------------------------- */ void ComputeTempChunk::lock(Fix *fixptr, bigint startstep, bigint stopstep) { cchunk->lock(fixptr,startstep,stopstep); } /* ---------------------------------------------------------------------- unset the lock ------------------------------------------------------------------------- */ void ComputeTempChunk::unlock(Fix *fixptr) { cchunk->unlock(fixptr); } /* ---------------------------------------------------------------------- free and reallocate per-chunk arrays ------------------------------------------------------------------------- */ void ComputeTempChunk::allocate() { memory->destroy(sum); memory->destroy(sumall); memory->destroy(count); memory->destroy(countall); memory->destroy(array); maxchunk = nchunk; memory->create(sum,maxchunk,"temp/chunk:sum"); memory->create(sumall,maxchunk,"temp/chunk:sumall"); memory->create(count,maxchunk,"temp/chunk:count"); memory->create(countall,maxchunk,"temp/chunk:countall"); memory->create(array,maxchunk,nvalues,"temp/chunk:array"); if (comflag || nvalues) { memory->destroy(massproc); memory->destroy(masstotal); memory->destroy(vcm); memory->destroy(vcmall); memory->create(massproc,maxchunk,"vcm/chunk:massproc"); memory->create(masstotal,maxchunk,"vcm/chunk:masstotal"); memory->create(vcm,maxchunk,3,"vcm/chunk:vcm"); memory->create(vcmall,maxchunk,3,"vcm/chunk:vcmall"); } } /* ---------------------------------------------------------------------- memory usage of local data ------------------------------------------------------------------------- */ double ComputeTempChunk::memory_usage() { double bytes = (bigint) maxchunk * 2 * sizeof(double); bytes = (bigint) maxchunk * 2 * sizeof(int); bytes = (bigint) maxchunk * nvalues * sizeof(double); if (comflag || nvalues) { bytes += (bigint) maxchunk * 2 * sizeof(double); bytes += (bigint) maxchunk * 2*3 * sizeof(double); } return bytes; } diff --git a/src/compute_torque_chunk.cpp b/src/compute_torque_chunk.cpp index 220f1e9da..6b49d98f0 100644 --- a/src/compute_torque_chunk.cpp +++ b/src/compute_torque_chunk.cpp @@ -1,253 +1,253 @@ /* ---------------------------------------------------------------------- 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 "string.h" #include "compute_torque_chunk.h" #include "atom.h" #include "update.h" #include "modify.h" #include "compute_chunk_atom.h" #include "domain.h" #include "memory.h" #include "error.h" using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ ComputeTorqueChunk::ComputeTorqueChunk(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg) { if (narg != 4) error->all(FLERR,"Illegal compute inertia/chunk command"); array_flag = 1; size_array_cols = 3; size_array_rows = 0; size_array_rows_variable = 1; extarray = 0; // ID of compute chunk/atom int n = strlen(arg[3]) + 1; idchunk = new char[n]; strcpy(idchunk,arg[3]); init(); // chunk-based data nchunk = 1; maxchunk = 0; massproc = masstotal = NULL; com = comall = NULL; torque = torqueall = NULL; allocate(); } /* ---------------------------------------------------------------------- */ ComputeTorqueChunk::~ComputeTorqueChunk() { delete [] idchunk; memory->destroy(massproc); memory->destroy(masstotal); memory->destroy(com); memory->destroy(comall); memory->destroy(torque); memory->destroy(torqueall); } /* ---------------------------------------------------------------------- */ void ComputeTorqueChunk::init() { int icompute = modify->find_compute(idchunk); if (icompute < 0) error->all(FLERR,"Chunk/atom compute does not exist for " "compute torque/chunk"); cchunk = (ComputeChunkAtom *) modify->compute[icompute]; if (strcmp(cchunk->style,"chunk/atom") != 0) error->all(FLERR,"Compute torque/chunk does not use chunk/atom compute"); } /* ---------------------------------------------------------------------- */ void ComputeTorqueChunk::compute_array() { - int i,j,index; + int i,index; double dx,dy,dz,massone; double unwrap[3]; invoked_array = update->ntimestep; // compute chunk/atom assigns atoms to chunk IDs // extract ichunk index vector from compute // ichunk = 1 to Nchunk for included atoms, 0 for excluded atoms nchunk = cchunk->setup_chunks(); cchunk->compute_ichunk(); int *ichunk = cchunk->ichunk; if (nchunk > maxchunk) allocate(); size_array_rows = nchunk; // zero local per-chunk values for (int i = 0; i < nchunk; i++) { massproc[i] = 0.0; com[i][0] = com[i][1] = com[i][2] = 0.0; torque[i][0] = torque[i][1] = torque[i][2] = 0.0; } // compute COM for each chunk double **x = atom->x; int *mask = atom->mask; int *type = atom->type; imageint *image = atom->image; double *mass = atom->mass; double *rmass = atom->rmass; int nlocal = atom->nlocal; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { index = ichunk[i]-1; if (index < 0) continue; if (rmass) massone = rmass[i]; else massone = mass[type[i]]; domain->unmap(x[i],image[i],unwrap); massproc[index] += massone; com[index][0] += unwrap[0] * massone; com[index][1] += unwrap[1] * massone; com[index][2] += unwrap[2] * massone; } MPI_Allreduce(massproc,masstotal,nchunk,MPI_DOUBLE,MPI_SUM,world); MPI_Allreduce(&com[0][0],&comall[0][0],3*nchunk,MPI_DOUBLE,MPI_SUM,world); for (int i = 0; i < nchunk; i++) { comall[i][0] /= masstotal[i]; comall[i][1] /= masstotal[i]; comall[i][2] /= masstotal[i]; } // compute torque on each chunk double **f = atom->f; for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) { index = ichunk[i]-1; if (index < 0) continue; domain->unmap(x[i],image[i],unwrap); dx = unwrap[0] - comall[index][0]; dy = unwrap[1] - comall[index][1]; dz = unwrap[2] - comall[index][2]; torque[i][0] += dy*f[i][2] - dz*f[i][1]; torque[i][1] += dz*f[i][0] - dx*f[i][2]; torque[i][2] += dx*f[i][1] - dy*f[i][0]; } MPI_Allreduce(&torque[0][0],&torqueall[0][0],3*nchunk, MPI_DOUBLE,MPI_SUM,world); } /* ---------------------------------------------------------------------- lock methods: called by fix ave/time these methods insure vector/array size is locked for Nfreq epoch by passing lock info along to compute chunk/atom ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- increment lock counter ------------------------------------------------------------------------- */ void ComputeTorqueChunk::lock_enable() { cchunk->lockcount++; } /* ---------------------------------------------------------------------- decrement lock counter in compute chunk/atom, it if still exists ------------------------------------------------------------------------- */ void ComputeTorqueChunk::lock_disable() { int icompute = modify->find_compute(idchunk); if (icompute >= 0) { cchunk = (ComputeChunkAtom *) modify->compute[icompute]; cchunk->lockcount--; } } /* ---------------------------------------------------------------------- calculate and return # of chunks = length of vector/array ------------------------------------------------------------------------- */ int ComputeTorqueChunk::lock_length() { nchunk = cchunk->setup_chunks(); return nchunk; } /* ---------------------------------------------------------------------- set the lock from startstep to stopstep ------------------------------------------------------------------------- */ void ComputeTorqueChunk::lock(Fix *fixptr, bigint startstep, bigint stopstep) { cchunk->lock(fixptr,startstep,stopstep); } /* ---------------------------------------------------------------------- unset the lock ------------------------------------------------------------------------- */ void ComputeTorqueChunk::unlock(Fix *fixptr) { cchunk->unlock(fixptr); } /* ---------------------------------------------------------------------- free and reallocate per-chunk arrays ------------------------------------------------------------------------- */ void ComputeTorqueChunk::allocate() { memory->destroy(massproc); memory->destroy(masstotal); memory->destroy(com); memory->destroy(comall); memory->destroy(torque); memory->destroy(torqueall); maxchunk = nchunk; memory->create(massproc,maxchunk,"torque/chunk:massproc"); memory->create(masstotal,maxchunk,"torque/chunk:masstotal"); memory->create(com,maxchunk,3,"torque/chunk:com"); memory->create(comall,maxchunk,3,"torque/chunk:comall"); memory->create(torque,maxchunk,6,"torque/chunk:torque"); memory->create(torqueall,maxchunk,6,"torque/chunk:torqueall"); array = torqueall; } /* ---------------------------------------------------------------------- memory usage of local data ------------------------------------------------------------------------- */ double ComputeTorqueChunk::memory_usage() { double bytes = (bigint) maxchunk * 2 * sizeof(double); bytes += (bigint) maxchunk * 2*3 * sizeof(double); bytes += (bigint) maxchunk * 2*3 * sizeof(double); return bytes; } diff --git a/src/create_bonds.cpp b/src/create_bonds.cpp index c56adbfd1..a47128572 100644 --- a/src/create_bonds.cpp +++ b/src/create_bonds.cpp @@ -1,229 +1,228 @@ /* ---------------------------------------------------------------------- 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 "stdlib.h" #include "string.h" #include "create_bonds.h" #include "atom.h" #include "domain.h" #include "force.h" #include "neighbor.h" #include "neigh_request.h" #include "neigh_list.h" #include "comm.h" #include "group.h" #include "special.h" #include "error.h" using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ CreateBonds::CreateBonds(LAMMPS *lmp) : Pointers(lmp) {} /* ---------------------------------------------------------------------- */ void CreateBonds::command(int narg, char **arg) { if (domain->box_exist == 0) error->all(FLERR,"Create_bonds command before simulation box is defined"); if (atom->tag_enable == 0) error->all(FLERR,"Cannot use create_bonds unless atoms have IDs"); if (atom->molecular != 1) error->all(FLERR,"Cannot use create_bonds with non-molecular system"); if (narg != 5) error->all(FLERR,"Illegal create_bonds command"); // parse args int igroup = group->find(arg[0]); if (igroup == -1) error->all(FLERR,"Cannot find create_bonds group ID"); int group1bit = group->bitmask[igroup]; igroup = group->find(arg[1]); if (igroup == -1) error->all(FLERR,"Cannot find create_bonds group ID"); int group2bit = group->bitmask[igroup]; int btype = force->inumeric(FLERR,arg[2]); double rmin = force->numeric(FLERR,arg[3]); double rmax = force->numeric(FLERR,arg[4]); if (btype <= 0 || btype > atom->nbondtypes) error->all(FLERR,"Invalid bond type in create_bonds command"); if (rmin > rmax) error->all(FLERR,"Illegal create_bonds command"); double rminsq = rmin*rmin; double rmaxsq = rmax*rmax; // store state before bond creation bigint nbonds_previous = atom->nbonds; // request a full neighbor list for use by this command int irequest = neighbor->request(this); neighbor->requests[irequest]->pair = 0; neighbor->requests[irequest]->command = 1; neighbor->requests[irequest]->half = 0; neighbor->requests[irequest]->full = 1; neighbor->requests[irequest]->occasional = 1; // init entire system since comm->borders and neighbor->build is done // comm::init needs neighbor::init needs pair::init needs kspace::init, etc lmp->init(); // error check on cutoff // if no pair style, neighbor list will be empty if (force->pair == NULL) error->all(FLERR,"Create_bonds requires a pair style be defined"); if (rmax > neighbor->cutneighmax) error->all(FLERR,"Create_bonds max distance > neighbor cutoff"); if (rmax > neighbor->cutneighmin && comm->me == 0) error->warning(FLERR,"Create_bonds max distance > minimum neighbor cutoff"); // require special_bonds 1-2 weights = 0.0 and KSpace = NULL // so that already bonded atom pairs do not appear in neighbor list // otherwise with newton_bond = 1, // would be hard to check if I-J bond already existed // note that with KSpace, pair with weight = 0 could still be in neigh list if (force->special_lj[1] != 0.0 || force->special_coul[1] != 0.0) error->all(FLERR,"Create_bonds command requires " "special_bonds 1-2 weights be 0.0"); if (force->kspace) error->all(FLERR,"Create_bonds command requires " "no kspace_style be defined"); // setup domain, communication and neighboring // acquire ghosts and build standard neighbor lists if (domain->triclinic) domain->x2lamda(atom->nlocal); domain->pbc(); domain->reset_box(); comm->setup(); if (neighbor->style) neighbor->setup_bins(); comm->exchange(); comm->borders(); if (domain->triclinic) domain->lamda2x(atom->nlocal+atom->nghost); neighbor->build(); // build neighbor list this command needs based on earlier request NeighList *list = neighbor->lists[irequest]; neighbor->build_one(list); // loop over all neighs of each atom // compute distance between two atoms consistently on both procs // add bond if group and distance criteria are met // check that bond list does not overflow tagint *tag = atom->tag; int *mask = atom->mask; double **x = atom->x; int *num_bond = atom->num_bond; int **bond_type = atom->bond_type; tagint **bond_atom = atom->bond_atom; double newton_bond = force->newton_bond; int nlocal = atom->nlocal; int i,j,ii,jj,inum,jnum,flag; double xtmp,ytmp,ztmp,delx,dely,delz,rsq; int *ilist,*jlist,*numneigh,**firstneigh; - double factor_lj,factor_coul; inum = list->inum; ilist = list->ilist; numneigh = list->numneigh; firstneigh = list->firstneigh; for (ii = 0; ii < inum; ii++) { i = ilist[ii]; 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; // only consider bond creation if I,J distance between 2 cutoffs // compute rsq identically on both I,J loop iterations // if I,J tags equal, do not bond atom to itself if (tag[i] < tag[j]) { delx = xtmp - x[j][0]; dely = ytmp - x[j][1]; delz = ztmp - x[j][2]; } else if (tag[i] > tag[j]) { delx = x[j][0] - xtmp; dely = x[j][1] - ytmp; delz = x[j][2] - ztmp; } else continue; rsq = delx*delx + dely*dely + delz*delz; if (rsq < rminsq || rsq > rmaxsq) continue; // only consider bond creation if igroup and jgroup match I,J atoms flag = 0; if ((mask[i] & group1bit) && (mask[j] & group2bit)) flag = 1; if ((mask[i] & group2bit) && (mask[j] & group1bit)) flag = 1; if (!flag) continue; // create bond, check for overflow // on I,J loop iterations, store with 1 or 2 atoms based on newton_bond 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 create_bonds"); bond_type[i][num_bond[i]] = btype; bond_atom[i][num_bond[i]] = tag[j]; num_bond[i]++; } } } // recount bonds bigint nbonds = 0; for (int i = 0; i < nlocal; i++) nbonds += num_bond[i]; MPI_Allreduce(&nbonds,&atom->nbonds,1,MPI_LMP_BIGINT,MPI_SUM,world); if (!force->newton_bond) atom->nbonds /= 2; // print new bond count bigint nadd_bonds = atom->nbonds - nbonds_previous; if (comm->me == 0) { if (screen) { fprintf(screen,"Added " BIGINT_FORMAT " bonds, new total = " BIGINT_FORMAT "\n", nadd_bonds,atom->nbonds); } if (logfile) { fprintf(logfile,"Added " BIGINT_FORMAT " bonds, new total = " BIGINT_FORMAT "\n", nadd_bonds,atom->nbonds); } } // re-trigger special list build Special special(lmp); special.build(); } diff --git a/src/delete_bonds.cpp b/src/delete_bonds.cpp index c4823cc01..6ed4d211d 100644 --- a/src/delete_bonds.cpp +++ b/src/delete_bonds.cpp @@ -1,591 +1,593 @@ /* ---------------------------------------------------------------------- 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 "mpi.h" #include "stdlib.h" #include "string.h" #include "delete_bonds.h" #include "atom.h" #include "atom_vec.h" #include "domain.h" #include "neighbor.h" #include "comm.h" #include "force.h" #include "group.h" #include "special.h" #include "error.h" +#include + using namespace LAMMPS_NS; enum{MULTI,ATOM,BOND,ANGLE,DIHEDRAL,IMPROPER,STATS}; /* ---------------------------------------------------------------------- */ DeleteBonds::DeleteBonds(LAMMPS *lmp) : Pointers(lmp) {} /* ---------------------------------------------------------------------- */ void DeleteBonds::command(int narg, char **arg) { if (domain->box_exist == 0) error->all(FLERR,"Delete_bonds command before simulation box is defined"); if (atom->natoms == 0) error->all(FLERR,"Delete_bonds command with no atoms existing"); if (atom->molecular != 1) error->all(FLERR,"Cannot use delete_bonds with non-molecular system"); if (narg < 2) error->all(FLERR,"Illegal delete_bonds command"); // init entire system since comm->borders is done // comm::init needs neighbor::init needs pair::init needs kspace::init, etc if (comm->me == 0 && screen) fprintf(screen,"System init for delete_bonds ...\n"); lmp->init(); if (comm->me == 0 && screen) fprintf(screen,"Deleting bonds ...\n"); // identify group int igroup = group->find(arg[0]); if (igroup == -1) error->all(FLERR,"Cannot find delete_bonds group ID"); int groupbit = group->bitmask[igroup]; // set style and which = type value int style = -1; if (strcmp(arg[1],"multi") == 0) style = MULTI; else if (strcmp(arg[1],"atom") == 0) style = ATOM; else if (strcmp(arg[1],"bond") == 0) style = BOND; else if (strcmp(arg[1],"angle") == 0) style = ANGLE; else if (strcmp(arg[1],"dihedral") == 0) style = DIHEDRAL; else if (strcmp(arg[1],"improper") == 0) style = IMPROPER; else if (strcmp(arg[1],"stats") == 0) style = STATS; else error->all(FLERR,"Illegal delete_bonds command"); // setup list of types (atom,bond,etc) to consider // use force->bounds() to allow setting of range of types // range can be 0 to ntypes inclusive int *tlist = NULL; int iarg = 2; if (style != MULTI && style != STATS) { if (narg < 3) error->all(FLERR,"Illegal delete_bonds command"); int n = -1; if (style == ATOM) n = atom->ntypes; if (style == BOND) n = atom->nbondtypes; if (style == ANGLE) n = atom->nangletypes; if (style == DIHEDRAL) n = atom->ndihedraltypes; if (style == IMPROPER) n = atom->nimpropertypes; tlist = new int[n+1]; for (int i = 0; i <= n; i++) tlist[i] = 0; int nlo,nhi; force->bounds(arg[2],n,nlo,nhi,0); for (int i = nlo; i <= nhi; i++) tlist[i] = 1; iarg++; } // grab optional keywords int any_flag = 0; int undo_flag = 0; int remove_flag = 0; int special_flag = 0; int induce_flag = 0; while (iarg < narg) { if (strcmp(arg[iarg],"any") == 0) any_flag = 1; else if (strcmp(arg[iarg],"undo") == 0) undo_flag = 1; else if (strcmp(arg[iarg],"remove") == 0) remove_flag = 1; else if (strcmp(arg[iarg],"special") == 0) special_flag = 1; else if (strcmp(arg[iarg],"induce") == 0) induce_flag = 1; else error->all(FLERR,"Illegal delete_bonds command"); iarg++; } // border swap to insure type and mask is current for off-proc atoms // enforce PBC before in case atoms are outside box if (domain->triclinic) domain->x2lamda(atom->nlocal); domain->pbc(); domain->reset_box(); comm->setup(); comm->exchange(); comm->borders(); if (domain->triclinic) domain->lamda2x(atom->nlocal+atom->nghost); // set topology interactions either off or on // criteria for an interaction to potentially be changed (set flag = 1) // all atoms or any atom in interaction must be in group, based on any_flag // for style = MULTI, all bond/angle/dihedral/improper, no other criteria // for style = ATOM, same as MULTI, plus at least one atom is specified type // for style = BOND/ANGLE/DIHEDRAL/IMPROPER, interaction is specified type // for style = STATS only compute stats, flag is always 0 // if flag = 1 // set interaction type negative if undo_flag = 0 // set interaction type positive if undo_flag = 1 int *mask = atom->mask; int *type = atom->type; int nlocal = atom->nlocal; int i,m,n,consider,flag,itype; int atom1,atom2,atom3,atom4; if (atom->avec->bonds_allow && (style == BOND || style == MULTI || style == ATOM)) { int *num_bond = atom->num_bond; int **bond_type = atom->bond_type; for (i = 0; i < nlocal; i++) { for (m = 0; m < num_bond[i]; m++) { atom1 = atom->map(atom->bond_atom[i][m]); if (atom1 == -1) error->one(FLERR,"Bond atom missing in delete_bonds"); consider = 0; if (!any_flag && mask[i] & groupbit && mask[atom1] & groupbit) consider = 1; if (any_flag && (mask[i] & groupbit || mask[atom1] & groupbit)) consider = 1; if (consider) { flag = 0; if (style == MULTI) flag = 1; else if (style == ATOM) { if (tlist[type[i]] || tlist[type[atom1]]) flag = 1; } else if (style == BOND) { itype = abs(bond_type[i][m]); if (tlist[itype]) flag = 1; } if (flag) { if (undo_flag == 0 && bond_type[i][m] > 0) bond_type[i][m] = -bond_type[i][m]; if (undo_flag == 1 && bond_type[i][m] < 0) bond_type[i][m] = -bond_type[i][m]; } } } } } if (atom->avec->angles_allow && (style == ANGLE || style == MULTI || style == ATOM)) { int *num_angle = atom->num_angle; int **angle_type = atom->angle_type; for (i = 0; i < nlocal; i++) { for (m = 0; m < num_angle[i]; m++) { atom1 = atom->map(atom->angle_atom1[i][m]); atom2 = atom->map(atom->angle_atom2[i][m]); atom3 = atom->map(atom->angle_atom3[i][m]); if (atom1 == -1 || atom2 == -1 || atom3 == -1) error->one(FLERR,"Angle atom missing in delete_bonds"); consider = 0; if (!any_flag && mask[atom1] & groupbit && mask[atom2] & groupbit && mask[atom3] & groupbit) consider = 1; if (any_flag && (mask[atom1] & groupbit || mask[atom2] & groupbit || mask[atom3] & groupbit)) consider = 1; if (consider) { flag = 0; if (style == MULTI) flag = 1; else if (style == ATOM) { if (tlist[type[atom1]] || tlist[type[atom2]] || tlist[type[atom3]]) flag = 1; } else if (style == ANGLE) { itype = abs(angle_type[i][m]); if (tlist[itype]) flag = 1; } if (flag) { if (undo_flag == 0 && angle_type[i][m] > 0) angle_type[i][m] = -angle_type[i][m]; if (undo_flag == 1 && angle_type[i][m] < 0) angle_type[i][m] = -angle_type[i][m]; } } } } } if (atom->avec->dihedrals_allow && (style == DIHEDRAL || style == MULTI || style == ATOM)) { int *num_dihedral = atom->num_dihedral; int **dihedral_type = atom->dihedral_type; for (i = 0; i < nlocal; i++) { for (m = 0; m < num_dihedral[i]; m++) { atom1 = atom->map(atom->dihedral_atom1[i][m]); atom2 = atom->map(atom->dihedral_atom2[i][m]); atom3 = atom->map(atom->dihedral_atom3[i][m]); atom4 = atom->map(atom->dihedral_atom4[i][m]); if (atom1 == -1 || atom2 == -1 || atom3 == -1 || atom4 == -1) error->one(FLERR,"Dihedral atom missing in delete_bonds"); consider = 0; if (!any_flag && mask[atom1] & groupbit && mask[atom2] & groupbit && mask[atom3] & groupbit && mask[atom4] & groupbit) consider = 1; if (any_flag && (mask[atom1] & groupbit || mask[atom2] & groupbit || mask[atom3] & groupbit || mask[atom4] & groupbit)) consider = 1; if (consider) { flag = 0; if (style == MULTI) flag = 1; else if (style == ATOM) { if (tlist[type[atom1]] || tlist[type[atom2]] || tlist[type[atom3]] || tlist[type[atom4]]) flag = 1; } else if (style == DIHEDRAL) { itype = abs(dihedral_type[i][m]); if (tlist[itype]) flag = 1; } if (flag) { if (undo_flag == 0 && dihedral_type[i][m] > 0) dihedral_type[i][m] = -dihedral_type[i][m]; if (undo_flag == 1 && dihedral_type[i][m] < 0) dihedral_type[i][m] = -dihedral_type[i][m]; } } } } } if (atom->avec->impropers_allow && (style == IMPROPER || style == MULTI || style == ATOM)) { int *num_improper = atom->num_improper; int **improper_type = atom->improper_type; for (i = 0; i < nlocal; i++) { for (m = 0; m < num_improper[i]; m++) { atom1 = atom->map(atom->improper_atom1[i][m]); atom2 = atom->map(atom->improper_atom2[i][m]); atom3 = atom->map(atom->improper_atom3[i][m]); atom4 = atom->map(atom->improper_atom4[i][m]); if (atom1 == -1 || atom2 == -1 || atom3 == -1 || atom4 == -1) error->one(FLERR,"Improper atom missing in delete_bonds"); consider = 0; if (!any_flag && mask[atom1] & groupbit && mask[atom2] & groupbit && mask[atom3] & groupbit && mask[atom4] & groupbit) consider = 1; if (any_flag && (mask[atom1] & groupbit || mask[atom2] & groupbit || mask[atom3] & groupbit || mask[atom4] & groupbit)) consider = 1; if (consider) { flag = 0; if (style == MULTI) flag = 1; else if (style == ATOM) { if (tlist[type[atom1]] || tlist[type[atom2]] || tlist[type[atom3]] || tlist[type[atom4]]) flag = 1; } else if (style == IMPROPER) { itype = abs(improper_type[i][m]); if (tlist[itype]) flag = 1; } if (flag) { if (undo_flag == 0 && improper_type[i][m] > 0) improper_type[i][m] = -improper_type[i][m]; if (undo_flag == 1 && improper_type[i][m] < 0) improper_type[i][m] = -improper_type[i][m]; } } } } } delete [] tlist; // induce turn off of angles, dihedral, impropers due to turned off bonds // induce turn off of dihedrals due to turned off angles // all atoms or any atom in interaction must be in group, based on any_flag if (induce_flag) { // circulate list of turned off bonds around ring of procs // circulate list of turned off angles around ring of procs } // remove interactions if requested // all atoms or any atom in interaction must be in group, based on any_flag if (remove_flag) { if (atom->avec->bonds_allow) { for (i = 0; i < nlocal; i++) { m = 0; while (m < atom->num_bond[i]) { if (atom->bond_type[i][m] <= 0) { atom1 = atom->map(atom->bond_atom[i][m]); flag = 0; if (!any_flag && mask[i] & groupbit && mask[atom1] & groupbit) flag = 1; if (any_flag && (mask[i] & groupbit || mask[atom1] & groupbit)) flag = 1; if (flag) { n = atom->num_bond[i]; atom->bond_type[i][m] = atom->bond_type[i][n-1]; atom->bond_atom[i][m] = atom->bond_atom[i][n-1]; atom->num_bond[i]--; } else m++; } else m++; } } } if (atom->avec->angles_allow) { for (i = 0; i < nlocal; i++) { m = 0; while (m < atom->num_angle[i]) { if (atom->angle_type[i][m] <= 0) { atom1 = atom->map(atom->angle_atom1[i][m]); atom2 = atom->map(atom->angle_atom2[i][m]); atom3 = atom->map(atom->angle_atom3[i][m]); flag = 0; if (!any_flag && mask[atom1] & groupbit && mask[atom2] & groupbit && mask[atom3] & groupbit) flag = 1; if (any_flag && (mask[atom1] & groupbit || mask[atom2] & groupbit || mask[atom3] & groupbit)) flag = 1; if (flag) { n = atom->num_angle[i]; atom->angle_type[i][m] = atom->angle_type[i][n-1]; atom->angle_atom1[i][m] = atom->angle_atom1[i][n-1]; atom->angle_atom2[i][m] = atom->angle_atom2[i][n-1]; atom->angle_atom3[i][m] = atom->angle_atom3[i][n-1]; atom->num_angle[i]--; } else m++; } else m++; } } } if (atom->avec->dihedrals_allow) { for (i = 0; i < nlocal; i++) { m = 0; while (m < atom->num_dihedral[i]) { if (atom->dihedral_type[i][m] <= 0) { atom1 = atom->map(atom->dihedral_atom1[i][m]); atom2 = atom->map(atom->dihedral_atom2[i][m]); atom3 = atom->map(atom->dihedral_atom3[i][m]); atom4 = atom->map(atom->dihedral_atom4[i][m]); flag = 0; if (!any_flag && mask[atom1] & groupbit && mask[atom2] & groupbit && mask[atom3] & groupbit && mask[atom4] & groupbit) flag = 1; if (any_flag && (mask[atom1] & groupbit || mask[atom2] & groupbit || mask[atom3] & groupbit || mask[atom4] & groupbit)) flag = 1; if (flag) { n = atom->num_dihedral[i]; atom->dihedral_type[i][m] = atom->dihedral_type[i][n-1]; atom->dihedral_atom1[i][m] = atom->dihedral_atom1[i][n-1]; atom->dihedral_atom2[i][m] = atom->dihedral_atom2[i][n-1]; atom->dihedral_atom3[i][m] = atom->dihedral_atom3[i][n-1]; atom->dihedral_atom4[i][m] = atom->dihedral_atom4[i][n-1]; atom->num_dihedral[i]--; } else m++; } else m++; } } } if (atom->avec->impropers_allow) { for (i = 0; i < nlocal; i++) { m = 0; while (m < atom->num_improper[i]) { if (atom->improper_type[i][m] <= 0) { atom1 = atom->map(atom->improper_atom1[i][m]); atom2 = atom->map(atom->improper_atom2[i][m]); atom3 = atom->map(atom->improper_atom3[i][m]); atom4 = atom->map(atom->improper_atom4[i][m]); flag = 0; if (!any_flag && mask[atom1] & groupbit && mask[atom2] & groupbit && mask[atom3] & groupbit && mask[atom4] & groupbit) flag = 1; if (any_flag && (mask[atom1] & groupbit || mask[atom2] & groupbit || mask[atom3] & groupbit || mask[atom4] & groupbit)) flag = 1; if (flag) { n = atom->num_improper[i]; atom->improper_type[i][m] = atom->improper_type[i][n-1]; atom->improper_atom1[i][m] = atom->improper_atom1[i][n-1]; atom->improper_atom2[i][m] = atom->improper_atom2[i][n-1]; atom->improper_atom3[i][m] = atom->improper_atom3[i][n-1]; atom->improper_atom4[i][m] = atom->improper_atom4[i][n-1]; atom->num_improper[i]--; } else m++; } else m++; } } } } // if interactions were removed, recompute global counts if (remove_flag) { if (atom->avec->bonds_allow) { bigint nbonds = 0; for (i = 0; i < nlocal; i++) nbonds += atom->num_bond[i]; MPI_Allreduce(&nbonds,&atom->nbonds,1,MPI_LMP_BIGINT, MPI_SUM,world); if (force->newton_bond == 0) atom->nbonds /= 2; } if (atom->avec->angles_allow) { bigint nangles = 0; for (i = 0; i < nlocal; i++) nangles += atom->num_angle[i]; MPI_Allreduce(&nangles,&atom->nangles,1,MPI_LMP_BIGINT, MPI_SUM,world); if (force->newton_bond == 0) atom->nangles /= 3; } if (atom->avec->dihedrals_allow) { bigint ndihedrals = 0; for (i = 0; i < nlocal; i++) ndihedrals += atom->num_dihedral[i]; MPI_Allreduce(&ndihedrals,&atom->ndihedrals, 1,MPI_LMP_BIGINT,MPI_SUM,world); if (force->newton_bond == 0) atom->ndihedrals /= 4; } if (atom->avec->impropers_allow) { bigint nimpropers = 0; for (i = 0; i < nlocal; i++) nimpropers += atom->num_improper[i]; MPI_Allreduce(&nimpropers,&atom->nimpropers, 1,MPI_LMP_BIGINT,MPI_SUM,world); if (force->newton_bond == 0) atom->nimpropers /= 4; } } // compute and print stats bigint tmp; bigint bond_on,bond_off; bigint angle_on,angle_off; bigint dihedral_on,dihedral_off; bigint improper_on,improper_off; if (atom->avec->bonds_allow) { bond_on = bond_off = 0; for (i = 0; i < nlocal; i++) for (m = 0; m < atom->num_bond[i]; m++) if (atom->bond_type[i][m] > 0) bond_on++; else bond_off++; MPI_Allreduce(&bond_on,&tmp,1,MPI_LMP_BIGINT,MPI_SUM,world); bond_on = tmp; MPI_Allreduce(&bond_off,&tmp,1,MPI_LMP_BIGINT,MPI_SUM,world); bond_off = tmp; if (force->newton_bond == 0) { bond_on /= 2; bond_off /= 2; } } if (atom->avec->angles_allow) { angle_on = angle_off = 0; for (i = 0; i < nlocal; i++) for (m = 0; m < atom->num_angle[i]; m++) if (atom->angle_type[i][m] > 0) angle_on++; else angle_off++; MPI_Allreduce(&angle_on,&tmp,1,MPI_LMP_BIGINT,MPI_SUM,world); angle_on = tmp; MPI_Allreduce(&angle_off,&tmp,1,MPI_LMP_BIGINT,MPI_SUM,world); angle_off = tmp; if (force->newton_bond == 0) { angle_on /= 3; angle_off /= 3; } } if (atom->avec->dihedrals_allow) { dihedral_on = dihedral_off = 0; for (i = 0; i < nlocal; i++) for (m = 0; m < atom->num_dihedral[i]; m++) if (atom->dihedral_type[i][m] > 0) dihedral_on++; else dihedral_off++; MPI_Allreduce(&dihedral_on,&tmp,1,MPI_LMP_BIGINT,MPI_SUM,world); dihedral_on = tmp; MPI_Allreduce(&dihedral_off,&tmp,1,MPI_LMP_BIGINT,MPI_SUM,world); dihedral_off = tmp; if (force->newton_bond == 0) { dihedral_on /= 4; dihedral_off /= 4; } } if (atom->avec->impropers_allow) { improper_on = improper_off = 0; for (i = 0; i < nlocal; i++) for (m = 0; m < atom->num_improper[i]; m++) if (atom->improper_type[i][m] > 0) improper_on++; else improper_off++; MPI_Allreduce(&improper_on,&tmp,1,MPI_LMP_BIGINT,MPI_SUM,world); improper_on = tmp; MPI_Allreduce(&improper_off,&tmp,1,MPI_LMP_BIGINT,MPI_SUM,world); improper_off = tmp; if (force->newton_bond == 0) { improper_on /= 4; improper_off /= 4; } } if (comm->me == 0) { if (atom->avec->bonds_allow) { if (screen) fprintf(screen, " " BIGINT_FORMAT " total bonds, " BIGINT_FORMAT " turned on, " BIGINT_FORMAT " turned off\n", atom->nbonds,bond_on,bond_off); if (logfile) fprintf(logfile, " " BIGINT_FORMAT " total bonds, " BIGINT_FORMAT " turned on, " BIGINT_FORMAT " turned off\n", atom->nbonds,bond_on,bond_off); } if (atom->avec->angles_allow) { if (screen) fprintf(screen, " " BIGINT_FORMAT " total angles, " BIGINT_FORMAT " turned on, " BIGINT_FORMAT " turned off\n", atom->nangles,angle_on,angle_off); if (logfile) fprintf(logfile, " " BIGINT_FORMAT " total angles, " BIGINT_FORMAT " turned on, " BIGINT_FORMAT " turned off\n", atom->nangles,angle_on,angle_off); } if (atom->avec->dihedrals_allow) { if (screen) fprintf(screen, " " BIGINT_FORMAT " total dihedrals, " BIGINT_FORMAT " turned on, " BIGINT_FORMAT " turned off\n", atom->ndihedrals,dihedral_on,dihedral_off); if (logfile) fprintf(logfile, " " BIGINT_FORMAT " total dihedrals, " BIGINT_FORMAT " turned on, " BIGINT_FORMAT " turned off\n", atom->ndihedrals,dihedral_on,dihedral_off); } if (atom->avec->impropers_allow) { if (screen) fprintf(screen, " " BIGINT_FORMAT " total impropers, " BIGINT_FORMAT " turned on, " BIGINT_FORMAT " turned off\n", atom->nimpropers,improper_on,improper_off); if (logfile) fprintf(logfile, " " BIGINT_FORMAT " total impropers, " BIGINT_FORMAT " turned on, " BIGINT_FORMAT " turned off\n", atom->nimpropers,improper_on,improper_off); } } // re-compute special list if requested if (special_flag) { Special special(lmp); special.build(); } } diff --git a/src/neighbor.h b/src/neighbor.h index 01402ab33..8224a97e1 100644 --- a/src/neighbor.h +++ b/src/neighbor.h @@ -1,426 +1,420 @@ /* -*- c++ -*- ---------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator http://lammps.sandia.gov, Sandia National Laboratories Steve Plimpton, sjplimp@sandia.gov Copyright (2003) Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. This software is distributed under the GNU General Public License. See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ #ifndef LMP_NEIGHBOR_H #define LMP_NEIGHBOR_H #include "pointers.h" namespace LAMMPS_NS { class Neighbor : protected Pointers { friend class Cuda; public: int style; // 0,1,2 = nsq, bin, multi int every; // build every this many steps int delay; // delay build for this many steps int dist_check; // 0 = always build, 1 = only if 1/2 dist int ago; // how many steps ago neighboring occurred int pgsize; // size of neighbor page int oneatom; // max # of neighbors for one atom int includegroup; // only build pairwise lists for this group int build_once; // 1 if only build lists once per run int cudable; // GPU <-> CPU communication flag for CUDA double skin; // skin distance double cutneighmin; // min neighbor cutoff for all type pairs double cutneighmax; // max neighbor cutoff for all type pairs double *cuttype; // for each type, max neigh cut w/ others int binsizeflag; // user-chosen bin size double binsize_user; // set externally by some accelerator pkgs bigint ncalls; // # of times build has been called bigint ndanger; // # of dangerous builds bigint lastcall; // timestep of last neighbor::build() call int nrequest; // requests for pairwise neighbor lists class NeighRequest **requests; // from Pair, Fix, Compute, Command classes int maxrequest; int old_style,old_nrequest; // previous run info to avoid int old_triclinic,old_pgsize; // re-creation of pairwise neighbor lists int old_oneatom,old_every; int old_delay,old_check; double old_cutoff; class NeighRequest **old_requests; int nlist; // pairwise neighbor lists class NeighList **lists; int nbondlist; // list of bonds to compute int **bondlist; int nanglelist; // list of angles to compute int **anglelist; int ndihedrallist; // list of dihedrals to compute int **dihedrallist; int nimproperlist; // list of impropers to compute int **improperlist; Neighbor(class LAMMPS *); virtual ~Neighbor(); virtual void init(); int request(void *, int instance=0); // another class requests a neigh list void print_lists_of_lists(); // debug print out int decide(); // decide whether to build or not virtual int check_distance(); // check max distance moved since last build void setup_bins(); // setup bins based on box and cutoff virtual void build(int topoflag=1); // create all neighbor lists (pair,bond) virtual void build_topology(); // create all topology neighbor lists void build_one(class NeighList *list, int preflag=0); // create a single neighbor list void set(int, char **); // set neighbor style and skin distance void modify_params(int, char**); // modify parameters that control builds bigint memory_usage(); int exclude_setting(); protected: int me,nprocs; int maxatom; // size of atom-based NeighList arrays int maxbond,maxangle,maxdihedral,maximproper; // size of bond lists int maxwt; // max weighting factor applied + 1 int must_check; // 1 if must check other classes to reneigh int restart_check; // 1 if restart enabled, 0 if no int fix_check; // # of fixes that induce reneigh int *fixchecklist; // which fixes to check double **cutneighsq; // neighbor cutneigh sq for each type pair double **cutneighghostsq; // neighbor cutnsq for each ghost type pair double cutneighmaxsq; // cutneighmax squared double *cuttypesq; // cuttype squared double triggersq; // trigger = build when atom moves this dist int cluster_check; // 1 if check bond/angle/etc satisfies minimg double **xhold; // atom coords at last neighbor build int maxhold; // size of xhold array int boxcheck; // 1 if need to store box size double boxlo_hold[3],boxhi_hold[3]; // box size at last neighbor build double corners_hold[8][3]; // box corners at last neighbor build int binatomflag; // bin atoms or not when build neigh list // turned off by build_one() int nbinx,nbiny,nbinz; // # of global bins int *bins; // ptr to next atom in each bin int maxbin; // size of bins array int *binhead; // ptr to 1st atom in each bin int maxhead; // size of binhead array int mbins; // # of local bins and offset int mbinx,mbiny,mbinz; int mbinxlo,mbinylo,mbinzlo; double binsizex,binsizey,binsizez; // actual bin sizes and inverse sizes double bininvx,bininvy,bininvz; int sx,sy,sz,smax; // bin stencil extents int dimension; // 2/3 for 2d/3d int triclinic; // 0 if domain is orthog, 1 if triclinic int newton_pair; // 0 if newton off, 1 if on for pairwise double *bboxlo,*bboxhi; // ptrs to full domain bounding box double (*corners)[3]; // ptr to 8 corners of triclinic box double inner[2],middle[2]; // rRESPA cutoffs for extra lists double cut_inner_sq; // outer cutoff for inner neighbor list double cut_middle_sq; // outer cutoff for middle neighbor list double cut_middle_inside_sq; // inner cutoff for middle neighbor list int special_flag[4]; // flags for 1-2, 1-3, 1-4 neighbors int anyghostlist; // 1 if any non-occasional list // stores neighbors of ghosts int exclude; // 0 if no type/group exclusions, 1 if yes int nex_type; // # of entries in type exclusion list int maxex_type; // max # in type list int *ex1_type,*ex2_type; // pairs of types to exclude int **ex_type; // 2d array of excluded type pairs int nex_group; // # of entries in group exclusion list int maxex_group; // max # in group list int *ex1_group,*ex2_group; // pairs of group #'s to exclude int *ex1_bit,*ex2_bit; // pairs of group bits to exclude int nex_mol; // # of entries in molecule exclusion list int maxex_mol; // max # in molecule list int *ex_mol_group; // molecule group #'s to exclude int *ex_mol_bit; // molecule group bits to exclude int nblist,nglist,nslist; // # of pairwise neigh lists of various kinds int *blist; // lists to build every reneighboring int *glist; // lists to grow atom arrays every reneigh int *slist; // lists to grow stencil arrays every reneigh void bin_atoms(); // bin all atoms double bin_distance(int, int, int); // distance between binx int coord2bin(double *); // mapping atom coord to a bin int coord2bin(double *, int &, int &, int&); // ditto int exclusion(int, int, int, int, int *, tagint *) const; // test for pair exclusion virtual void choose_build(int, class NeighRequest *); void choose_stencil(int, class NeighRequest *); // dummy functions provided by NeighborKokkos virtual void init_cutneighsq_kokkos(int) {} virtual int init_lists_kokkos() {return 0;} virtual void init_list_flags1_kokkos(int) {} virtual void init_list_flags2_kokkos(int) {} virtual void init_list_grow_kokkos(int) {} virtual void build_kokkos(int) {} virtual void setup_bins_kokkos(int) {} // pairwise build functions typedef void (Neighbor::*PairPtr)(class NeighList *); PairPtr *pair_build; void half_nsq_no_newton(class NeighList *); void half_nsq_no_newton_ghost(class NeighList *); void half_nsq_newton(class NeighList *); void half_bin_no_newton(class NeighList *); void half_bin_no_newton_ghost(class NeighList *); void half_bin_newton(class NeighList *); void half_bin_newton_tri(class NeighList *); void half_multi_no_newton(class NeighList *); void half_multi_newton(class NeighList *); void half_multi_newton_tri(class NeighList *); void full_nsq(class NeighList *); void full_nsq_ghost(class NeighList *); void full_bin(class NeighList *); void full_bin_ghost(class NeighList *); void full_multi(class NeighList *); void half_from_full_no_newton(class NeighList *); void half_from_full_newton(class NeighList *); void skip_from(class NeighList *); void skip_from_granular(class NeighList *); void skip_from_respa(class NeighList *); void copy_from(class NeighList *); void granular_nsq_no_newton(class NeighList *); void granular_nsq_newton(class NeighList *); void granular_bin_no_newton(class NeighList *); void granular_bin_newton(class NeighList *); void granular_bin_newton_tri(class NeighList *); void respa_nsq_no_newton(class NeighList *); void respa_nsq_newton(class NeighList *); void respa_bin_no_newton(class NeighList *); void respa_bin_newton(class NeighList *); void respa_bin_newton_tri(class NeighList *); // include prototypes for multi-threaded neighbor lists // builds or their corresponding dummy versions #define LMP_INSIDE_NEIGHBOR_H #include "accelerator_omp.h" #include "accelerator_intel.h" #undef LMP_INSIDE_NEIGHBOR_H // pairwise stencil creation functions typedef void (Neighbor::*StencilPtr)(class NeighList *, int, int, int); StencilPtr *stencil_create; void stencil_half_bin_2d_no_newton(class NeighList *, int, int, int); void stencil_half_ghost_bin_2d_no_newton(class NeighList *, int, int, int); void stencil_half_bin_3d_no_newton(class NeighList *, int, int, int); void stencil_half_ghost_bin_3d_no_newton(class NeighList *, int, int, int); void stencil_half_bin_2d_newton(class NeighList *, int, int, int); void stencil_half_bin_3d_newton(class NeighList *, int, int, int); void stencil_half_bin_2d_newton_tri(class NeighList *, int, int, int); void stencil_half_bin_3d_newton_tri(class NeighList *, int, int, int); void stencil_half_multi_2d_no_newton(class NeighList *, int, int, int); void stencil_half_multi_3d_no_newton(class NeighList *, int, int, int); void stencil_half_multi_2d_newton(class NeighList *, int, int, int); void stencil_half_multi_3d_newton(class NeighList *, int, int, int); void stencil_half_multi_2d_newton_tri(class NeighList *, int, int, int); void stencil_half_multi_3d_newton_tri(class NeighList *, int, int, int); void stencil_full_bin_2d(class NeighList *, int, int, int); void stencil_full_ghost_bin_2d(class NeighList *, int, int, int); void stencil_full_bin_3d(class NeighList *, int, int, int); void stencil_full_ghost_bin_3d(class NeighList *, int, int, int); void stencil_full_multi_2d(class NeighList *, int, int, int); void stencil_full_multi_3d(class NeighList *, int, int, int); // topology build functions typedef void (Neighbor::*BondPtr)(); // ptrs to topology build functions BondPtr bond_build; // ptr to bond list functions void bond_all(); // bond list with all bonds void bond_template(); // bond list with templated bonds void bond_partial(); // exclude certain bonds void bond_check(); BondPtr angle_build; // ptr to angle list functions void angle_all(); // angle list with all angles void angle_template(); // angle list with templated bonds void angle_partial(); // exclude certain angles void angle_check(); BondPtr dihedral_build; // ptr to dihedral list functions void dihedral_all(); // dihedral list with all dihedrals void dihedral_template(); // dihedral list with templated bonds void dihedral_partial(); // exclude certain dihedrals void dihedral_check(int, int **); BondPtr improper_build; // ptr to improper list functions void improper_all(); // improper list with all impropers void improper_template(); // improper list with templated bonds void improper_partial(); // exclude certain impropers // find_special: determine if atom j is in special list of atom i // if it is not, return 0 // if it is and special flag is 0 (both coeffs are 0.0), return -1 // if it is and special flag is 1 (both coeffs are 1.0), return 0 // if it is and special flag is 2 (otherwise), return 1,2,3 // for which level of neighbor it is (and which coeff it maps to) inline int find_special(const tagint *list, const int *nspecial, const tagint tag) const { const int n1 = nspecial[0]; const int n2 = nspecial[1]; const int n3 = nspecial[2]; for (int i = 0; i < n3; i++) { if (list[i] == tag) { if (i < n1) { if (special_flag[1] == 0) return -1; else if (special_flag[1] == 1) return 0; else return 1; } else if (i < n2) { if (special_flag[2] == 0) return -1; else if (special_flag[2] == 1) return 0; else return 2; } else { if (special_flag[3] == 0) return -1; else if (special_flag[3] == 1) return 0; else return 3; } } } return 0; }; }; } #endif /* ERROR/WARNING messages: E: Neighbor delay must be 0 or multiple of every setting The delay and every parameters set via the neigh_modify command are inconsistent. If the delay setting is non-zero, then it must be a multiple of the every setting. E: Neighbor page size must be >= 10x the one atom setting This is required to prevent wasting too much memory. E: Invalid atom type in neighbor exclusion list Atom types must range from 1 to Ntypes inclusive. W: Neighbor exclusions used with KSpace solver may give inconsistent Coulombic energies This is because excluding specific pair interactions also excludes them from long-range interactions which may not be the desired effect. The special_bonds command handles this consistently by insuring excluded (or weighted) 1-2, 1-3, 1-4 interactions are treated consistently by both the short-range pair style and the long-range solver. This is not done for exclusions of charged atom pairs via the neigh_modify exclude command. E: Neighbor include group not allowed with ghost neighbors This is a current restriction within LAMMPS. E: Neighbor multi not yet enabled for ghost neighbors This is a current restriction within LAMMPS. E: Neighbor multi not yet enabled for granular Self-explanatory. E: Neighbor multi not yet enabled for rRESPA Self-explanatory. E: Too many local+ghost atoms for neighbor list The number of nlocal + nghost atoms on a processor is limited by the size of a 32-bit integer with 2 bits removed for masking 1-2, 1-3, 1-4 neighbors. -W: Building an occasional neighobr list when atoms may have moved too far - -This can cause LAMMPS to crash when the neighbor list is built. -The solution is to check for building the regular neighbor lists -more frequently. - E: Domain too large for neighbor bins The domain has become extremely large so that neighbor bins cannot be used. Most likely, one or more atoms have been blown out of the simulation box to a great distance. E: Cannot use neighbor bins - box size << cutoff Too many neighbor bins will be created. This typically happens when the simulation box is very small in some dimension, compared to the neighbor cutoff. Use the "nsq" style instead of "bin" style. E: Too many neighbor bins This is likely due to an immense simulation box that has blown up to a large size. E: Illegal ... command Self-explanatory. Check the input script syntax and compare to the documentation for the command. You can use -echo screen as a command-line option when running LAMMPS to see the offending line. E: Invalid group ID in neigh_modify command A group ID used in the neigh_modify command does not exist. E: Neigh_modify include group != atom_modify first group Self-explanatory. E: Neigh_modify exclude molecule requires atom attribute molecule Self-explanatory. */