/* ---------------------------------------------------------------------- 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.h" #include "math_extra.h" #include "atom.h" #include "atom_vec_ellipsoid.h" #include "atom_vec_line.h" #include "atom_vec_tri.h" #include "domain.h" #include "update.h" #include "respa.h" #include "modify.h" #include "group.h" #include "comm.h" #include "random_mars.h" #include "force.h" #include "output.h" #include "math_const.h" #include "memory.h" #include "error.h" using namespace LAMMPS_NS; using namespace FixConst; using namespace MathConst; enum{SINGLE,MOLECULE,GROUP}; enum{NONE,XYZ,XY,YZ,XZ}; enum{ISO,ANISO,TRICLINIC}; #define MAXLINE 256 #define CHUNK 1024 #define ATTRIBUTE_PERBODY 11 #define TOLERANCE 1.0e-6 #define EPSILON 1.0e-7 #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 /* ---------------------------------------------------------------------- */ FixRigid::FixRigid(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { int i,ibody; scalar_flag = 1; extscalar = 0; 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; body = NULL; displace = NULL; eflags = NULL; orient = NULL; dorient = NULL; grow_arrays(atom->nmax); atom->add_callback(0); // parse args for rigid body specification // set nbody and body[i] for each atom if (narg < 4) error->all(FLERR,"Illegal fix rigid command"); int iarg; mol2body = NULL; // single rigid body // nbody = 1 // all atoms in fix group are part of body if (strcmp(arg[3],"single") == 0) { rstyle = SINGLE; iarg = 4; nbody = 1; int *mask = atom->mask; int nlocal = atom->nlocal; for (i = 0; i < nlocal; i++) { body[i] = -1; if (mask[i] & groupbit) body[i] = 0; } // each molecule in fix group is a rigid body // maxmol = largest molecule # // ncount = # of atoms in each molecule (have to sum across procs) // nbody = # of non-zero ncount values // use nall as incremented ptr to set body[] values for each atom } else if (strcmp(arg[3],"molecule") == 0) { rstyle = MOLECULE; iarg = 4; if (atom->molecule_flag == 0) error->all(FLERR,"Fix rigid molecule requires atom attribute molecule"); int *mask = atom->mask; int *molecule = atom->molecule; int nlocal = atom->nlocal; maxmol = -1; for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) maxmol = MAX(maxmol,molecule[i]); int itmp; MPI_Allreduce(&maxmol,&itmp,1,MPI_INT,MPI_MAX,world); maxmol = itmp; int *ncount; memory->create(ncount,maxmol+1,"rigid:ncount"); for (i = 0; i <= maxmol; i++) ncount[i] = 0; for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) ncount[molecule[i]]++; memory->create(mol2body,maxmol+1,"rigid:ncount"); MPI_Allreduce(ncount,mol2body,maxmol+1,MPI_INT,MPI_SUM,world); nbody = 0; for (i = 0; i <= maxmol; i++) if (mol2body[i]) mol2body[i] = nbody++; else mol2body[i] = -1; for (i = 0; i < nlocal; i++) { body[i] = -1; if (mask[i] & groupbit) body[i] = mol2body[molecule[i]]; } memory->destroy(ncount); // each listed group is a rigid body // check if all listed groups exist // an atom must belong to fix group and listed group to be in rigid body // error if atom belongs to more than 1 rigid body } else if (strcmp(arg[3],"group") == 0) { if (narg < 5) error->all(FLERR,"Illegal fix rigid command"); rstyle = GROUP; nbody = atoi(arg[4]); if (nbody <= 0) error->all(FLERR,"Illegal fix rigid command"); if (narg < 5+nbody) error->all(FLERR,"Illegal fix rigid command"); iarg = 5+nbody; int *igroups = new int[nbody]; for (ibody = 0; ibody < nbody; ibody++) { igroups[ibody] = group->find(arg[5+ibody]); if (igroups[ibody] == -1) error->all(FLERR,"Could not find fix rigid group ID"); } int *mask = atom->mask; int nlocal = atom->nlocal; int flag = 0; for (i = 0; i < nlocal; i++) { body[i] = -1; if (mask[i] & groupbit) for (ibody = 0; ibody < nbody; ibody++) if (mask[i] & group->bitmask[igroups[ibody]]) { if (body[i] >= 0) flag = 1; body[i] = ibody; } } int flagall; MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_SUM,world); if (flagall) error->all(FLERR,"One or more atoms belong to multiple rigid bodies"); delete [] igroups; } else error->all(FLERR,"Illegal fix rigid command"); // error check on nbody if (nbody == 0) error->all(FLERR,"No rigid bodies defined"); // create all nbody-length arrays memory->create(nrigid,nbody,"rigid:nrigid"); memory->create(masstotal,nbody,"rigid:masstotal"); memory->create(xcm,nbody,3,"rigid:xcm"); memory->create(vcm,nbody,3,"rigid:vcm"); memory->create(fcm,nbody,3,"rigid:fcm"); memory->create(inertia,nbody,3,"rigid:inertia"); memory->create(ex_space,nbody,3,"rigid:ex_space"); memory->create(ey_space,nbody,3,"rigid:ey_space"); memory->create(ez_space,nbody,3,"rigid:ez_space"); memory->create(angmom,nbody,3,"rigid:angmom"); memory->create(omega,nbody,3,"rigid:omega"); memory->create(torque,nbody,3,"rigid:torque"); memory->create(quat,nbody,4,"rigid:quat"); memory->create(imagebody,nbody,"rigid:imagebody"); memory->create(fflag,nbody,3,"rigid:fflag"); memory->create(tflag,nbody,3,"rigid:tflag"); memory->create(langextra,nbody,6,"rigid:langextra"); memory->create(sum,nbody,6,"rigid:sum"); memory->create(all,nbody,6,"rigid:all"); memory->create(remapflag,nbody,4,"rigid:remapflag"); // initialize force/torque flags to default = 1.0 // for 2d: fz, tx, ty = 0.0 array_flag = 1; size_array_rows = nbody; size_array_cols = 15; global_freq = 1; extarray = 0; for (i = 0; i < nbody; i++) { fflag[i][0] = fflag[i][1] = fflag[i][2] = 1.0; tflag[i][0] = tflag[i][1] = tflag[i][2] = 1.0; if (domain->dimension == 2) fflag[i][2] = tflag[i][0] = tflag[i][1] = 0.0; } // parse optional args int seed; langflag = 0; tstat_flag = 0; pstat_flag = 0; allremap = 1; id_dilate = NULL; t_chain = 10; t_iter = 1; t_order = 3; p_chain = 10; infile = NULL; pcouple = NONE; pstyle = ANISO; dimension = domain->dimension; for (int i = 0; i < 3; i++) { p_start[i] = p_stop[i] = p_period[i] = 0.0; p_flag[i] = 0; } while (iarg < narg) { if (strcmp(arg[iarg],"force") == 0) { if (iarg+5 > narg) error->all(FLERR,"Illegal fix rigid command"); int mlo,mhi; force->bounds(arg[iarg+1],nbody,mlo,mhi); double xflag,yflag,zflag; if (strcmp(arg[iarg+2],"off") == 0) xflag = 0.0; else if (strcmp(arg[iarg+2],"on") == 0) xflag = 1.0; else error->all(FLERR,"Illegal fix rigid command"); if (strcmp(arg[iarg+3],"off") == 0) yflag = 0.0; else if (strcmp(arg[iarg+3],"on") == 0) yflag = 1.0; else error->all(FLERR,"Illegal fix rigid command"); if (strcmp(arg[iarg+4],"off") == 0) zflag = 0.0; else if (strcmp(arg[iarg+4],"on") == 0) zflag = 1.0; else error->all(FLERR,"Illegal fix rigid command"); if (domain->dimension == 2 && zflag == 1.0) error->all(FLERR,"Fix rigid z force cannot be on for 2d simulation"); int count = 0; for (int m = mlo; m <= mhi; m++) { fflag[m-1][0] = xflag; fflag[m-1][1] = yflag; fflag[m-1][2] = zflag; count++; } if (count == 0) error->all(FLERR,"Illegal fix rigid command"); iarg += 5; } else if (strcmp(arg[iarg],"torque") == 0) { if (iarg+5 > narg) error->all(FLERR,"Illegal fix rigid command"); int mlo,mhi; force->bounds(arg[iarg+1],nbody,mlo,mhi); double xflag,yflag,zflag; if (strcmp(arg[iarg+2],"off") == 0) xflag = 0.0; else if (strcmp(arg[iarg+2],"on") == 0) xflag = 1.0; else error->all(FLERR,"Illegal fix rigid command"); if (strcmp(arg[iarg+3],"off") == 0) yflag = 0.0; else if (strcmp(arg[iarg+3],"on") == 0) yflag = 1.0; else error->all(FLERR,"Illegal fix rigid command"); if (strcmp(arg[iarg+4],"off") == 0) zflag = 0.0; else if (strcmp(arg[iarg+4],"on") == 0) zflag = 1.0; else error->all(FLERR,"Illegal fix rigid command"); if (domain->dimension == 2 && (xflag == 1.0 || yflag == 1.0)) error->all(FLERR,"Fix rigid xy torque cannot be on for 2d simulation"); int count = 0; for (int m = mlo; m <= mhi; m++) { tflag[m-1][0] = xflag; tflag[m-1][1] = yflag; tflag[m-1][2] = zflag; count++; } if (count == 0) error->all(FLERR,"Illegal fix rigid command"); iarg += 5; } else if (strcmp(arg[iarg],"langevin") == 0) { if (iarg+5 > narg) error->all(FLERR,"Illegal fix rigid command"); if (strcmp(style,"rigid") != 0 && strcmp(style,"rigid/nve") != 0) error->all(FLERR,"Illegal fix rigid command"); langflag = 1; t_start = atof(arg[iarg+1]); t_stop = atof(arg[iarg+2]); t_period = atof(arg[iarg+3]); seed = atoi(arg[iarg+4]); if (t_period <= 0.0) error->all(FLERR,"Fix rigid langevin period must be > 0.0"); if (seed <= 0) error->all(FLERR,"Illegal fix rigid command"); iarg += 5; } else if (strcmp(arg[iarg],"temp") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal fix rigid command"); if (strcmp(style,"rigid/nvt") != 0 && strcmp(style,"rigid/npt") != 0) error->all(FLERR,"Illegal fix rigid command"); tstat_flag = 1; t_start = atof(arg[iarg+1]); t_stop = atof(arg[iarg+2]); t_period = atof(arg[iarg+3]); iarg += 4; } else if (strcmp(arg[iarg],"iso") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal fix rigid command"); if (strcmp(style,"rigid/npt") != 0 && strcmp(style,"rigid/nph") != 0) error->all(FLERR,"Illegal fix rigid command"); pcouple = XYZ; p_start[0] = p_start[1] = p_start[2] = atof(arg[iarg+1]); p_stop[0] = p_stop[1] = p_stop[2] = atof(arg[iarg+2]); p_period[0] = p_period[1] = p_period[2] = atof(arg[iarg+3]); p_flag[0] = p_flag[1] = p_flag[2] = 1; if (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 command"); if (strcmp(style,"rigid/npt") != 0 && strcmp(style,"rigid/nph") != 0) error->all(FLERR,"Illegal fix rigid command"); p_start[0] = p_start[1] = p_start[2] = atof(arg[iarg+1]); p_stop[0] = p_stop[1] = p_stop[2] = atof(arg[iarg+2]); p_period[0] = p_period[1] = p_period[2] = atof(arg[iarg+3]); p_flag[0] = p_flag[1] = p_flag[2] = 1; if (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 command"); p_start[0] = atof(arg[iarg+1]); p_stop[0] = atof(arg[iarg+2]); p_period[0] = atof(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 command"); p_start[1] = atof(arg[iarg+1]); p_stop[1] = atof(arg[iarg+2]); p_period[1] = atof(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 command"); p_start[2] = atof(arg[iarg+1]); p_stop[2] = atof(arg[iarg+2]); p_period[2] = atof(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 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 command"); iarg += 2; } else if (strcmp(arg[iarg],"dilate") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix rigid 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 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 command"); if (strcmp(style,"rigid/nvt") != 0 && strcmp(style,"rigid/npt") != 0) error->all(FLERR,"Illegal fix rigid command"); t_chain = atoi(arg[iarg+1]); t_iter = atoi(arg[iarg+2]); t_order = atoi(arg[iarg+3]); iarg += 4; } else if (strcmp(arg[iarg],"pchain") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix rigid command"); if (strcmp(style,"rigid/npt") != 0 && strcmp(style,"rigid/nph") != 0) error->all(FLERR,"Illegal fix rigid command"); p_chain = atoi(arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"infile") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix rigid command"); delete [] infile; int n = strlen(arg[iarg+1]) + 1; infile = new char[n]; strcpy(infile,arg[iarg+1]); iarg += 2; } else error->all(FLERR,"Illegal fix rigid command"); } // set pstat_flag pstat_flag = 0; for (int i = 0; i < 3; i++) if (p_flag[i]) pstat_flag = 1; if (pcouple == XYZ || (dimension == 2 && pcouple == XY)) pstyle = ISO; else pstyle = ANISO; // initialize Marsaglia RNG with processor-unique seed if (langflag) random = new RanMars(lmp,seed + me); else random = NULL; // initialize vector output quantities in case accessed before run for (i = 0; i < nbody; i++) { xcm[i][0] = xcm[i][1] = xcm[i][2] = 0.0; vcm[i][0] = vcm[i][1] = vcm[i][2] = 0.0; fcm[i][0] = fcm[i][1] = fcm[i][2] = 0.0; torque[i][0] = torque[i][1] = torque[i][2] = 0.0; } // nrigid[n] = # of atoms in Nth rigid body // error if one or zero atoms int *ncount = new int[nbody]; for (ibody = 0; ibody < nbody; ibody++) ncount[ibody] = 0; int nlocal = atom->nlocal; for (i = 0; i < nlocal; i++) if (body[i] >= 0) ncount[body[i]]++; MPI_Allreduce(ncount,nrigid,nbody,MPI_INT,MPI_SUM,world); delete [] ncount; for (ibody = 0; ibody < nbody; ibody++) if (nrigid[ibody] <= 1) error->all(FLERR,"One or zero atoms in rigid body"); // 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 nsum = 0; for (ibody = 0; ibody < nbody; ibody++) nsum += nrigid[ibody]; if (me == 0) { if (screen) fprintf(screen,"%d rigid bodies with %d atoms\n",nbody,nsum); if (logfile) fprintf(logfile,"%d rigid bodies with %d atoms\n",nbody,nsum); } // firstflag = 1 triggers one-time initialization of rigid body attributes firstflag = 1; } /* ---------------------------------------------------------------------- */ FixRigid::~FixRigid() { // unregister callbacks to this fix from Atom class atom->delete_callback(id,0); delete random; delete [] infile; memory->destroy(mol2body); // delete locally stored arrays memory->destroy(body); memory->destroy(displace); memory->destroy(eflags); memory->destroy(orient); memory->destroy(dorient); // delete nbody-length arrays memory->destroy(nrigid); memory->destroy(masstotal); memory->destroy(xcm); memory->destroy(vcm); memory->destroy(fcm); memory->destroy(inertia); memory->destroy(ex_space); memory->destroy(ey_space); memory->destroy(ez_space); memory->destroy(angmom); memory->destroy(omega); memory->destroy(torque); memory->destroy(quat); memory->destroy(imagebody); memory->destroy(fflag); memory->destroy(tflag); memory->destroy(langextra); memory->destroy(sum); memory->destroy(all); memory->destroy(remapflag); } /* ---------------------------------------------------------------------- */ int FixRigid::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 FixRigid::init() { int i,ibody; 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; // one-time initialization of rigid body attributes // extended flags, masstotal, COM, inertia tensor if (firstflag) setup_bodies(); firstflag = 0; // temperature scale factor double ndof = 0.0; for (ibody = 0; ibody < nbody; ibody++) { ndof += fflag[ibody][0] + fflag[ibody][1] + fflag[ibody][2]; ndof += tflag[ibody][0] + tflag[ibody][1] + tflag[ibody][2]; } if (ndof > 0.0) tfactor = force->mvv2e / (ndof * force->boltz); else tfactor = 0.0; } /* ---------------------------------------------------------------------- */ void FixRigid::setup(int vflag) { int i,n,ibody; double massone,radone; // vcm = velocity of center-of-mass of each rigid body // fcm = force on center-of-mass of each rigid body double **v = atom->v; double **f = atom->f; double *rmass = atom->rmass; double *mass = atom->mass; int *type = atom->type; int nlocal = atom->nlocal; for (ibody = 0; ibody < nbody; ibody++) for (i = 0; i < 6; i++) sum[ibody][i] = 0.0; for (i = 0; i < nlocal; i++) { if (body[i] < 0) continue; ibody = body[i]; if (rmass) massone = rmass[i]; else massone = mass[type[i]]; sum[ibody][0] += v[i][0] * massone; sum[ibody][1] += v[i][1] * massone; sum[ibody][2] += v[i][2] * massone; sum[ibody][3] += f[i][0]; sum[ibody][4] += f[i][1]; sum[ibody][5] += f[i][2]; } MPI_Allreduce(sum[0],all[0],6*nbody,MPI_DOUBLE,MPI_SUM,world); for (ibody = 0; ibody < nbody; ibody++) { vcm[ibody][0] = all[ibody][0]/masstotal[ibody]; vcm[ibody][1] = all[ibody][1]/masstotal[ibody]; vcm[ibody][2] = all[ibody][2]/masstotal[ibody]; fcm[ibody][0] = all[ibody][3]; fcm[ibody][1] = all[ibody][4]; fcm[ibody][2] = all[ibody][5]; } // angmom = angular momentum of each rigid body // torque = torque on each rigid body tagint *image = atom->image; double **x = atom->x; double dx,dy,dz; double unwrap[3]; for (ibody = 0; ibody < nbody; ibody++) for (i = 0; i < 6; i++) sum[ibody][i] = 0.0; for (i = 0; i < nlocal; i++) { if (body[i] < 0) continue; ibody = body[i]; domain->unmap(x[i],image[i],unwrap); dx = unwrap[0] - xcm[ibody][0]; dy = unwrap[1] - xcm[ibody][1]; dz = unwrap[2] - xcm[ibody][2]; if (rmass) massone = rmass[i]; else massone = mass[type[i]]; sum[ibody][0] += dy * massone*v[i][2] - dz * massone*v[i][1]; sum[ibody][1] += dz * massone*v[i][0] - dx * massone*v[i][2]; sum[ibody][2] += dx * massone*v[i][1] - dy * massone*v[i][0]; sum[ibody][3] += dy * f[i][2] - dz * f[i][1]; sum[ibody][4] += dz * f[i][0] - dx * f[i][2]; sum[ibody][5] += dx * f[i][1] - dy * f[i][0]; } // extended particles add their rotation/torque to angmom/torque of body if (extended) { AtomVecLine::Bonus *lbonus; if (avec_line) lbonus = avec_line->bonus; double **omega_one = atom->omega; double **angmom_one = atom->angmom; double **torque_one = atom->torque; double *radius = atom->radius; int *line = atom->line; for (i = 0; i < nlocal; i++) { if (body[i] < 0) continue; ibody = body[i]; if (eflags[i] & OMEGA) { if (eflags[i] & SPHERE) { radone = radius[i]; sum[ibody][0] += SINERTIA*rmass[i] * radone*radone * omega_one[i][0]; sum[ibody][1] += SINERTIA*rmass[i] * radone*radone * omega_one[i][1]; sum[ibody][2] += SINERTIA*rmass[i] * radone*radone * omega_one[i][2]; } else if (eflags[i] & LINE) { radone = lbonus[line[i]].length; sum[ibody][2] += LINERTIA*rmass[i] * radone*radone * omega_one[i][2]; } } if (eflags[i] & ANGMOM) { sum[ibody][0] += angmom_one[i][0]; sum[ibody][1] += angmom_one[i][1]; sum[ibody][2] += angmom_one[i][2]; } if (eflags[i] & TORQUE) { sum[ibody][3] += torque_one[i][0]; sum[ibody][4] += torque_one[i][1]; sum[ibody][5] += torque_one[i][2]; } } } MPI_Allreduce(sum[0],all[0],6*nbody,MPI_DOUBLE,MPI_SUM,world); for (ibody = 0; ibody < nbody; ibody++) { angmom[ibody][0] = all[ibody][0]; angmom[ibody][1] = all[ibody][1]; angmom[ibody][2] = all[ibody][2]; torque[ibody][0] = all[ibody][3]; torque[ibody][1] = all[ibody][4]; torque[ibody][2] = all[ibody][5]; } // zero langextra in case Langevin thermostat not used // no point to calling post_force() here since langextra // is only added to fcm/torque in final_integrate() for (ibody = 0; ibody < nbody; ibody++) for (i = 0; i < 6; i++) langextra[ibody][i] = 0.0; // virial setup before call to set_v if (vflag) v_setup(vflag); else evflag = 0; // set velocities from angmom & omega for (ibody = 0; ibody < nbody; ibody++) MathExtra::angmom_to_omega(angmom[ibody],ex_space[ibody],ey_space[ibody], ez_space[ibody],inertia[ibody],omega[ibody]); 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 FixRigid::initial_integrate(int vflag) { double dtfm; for (int ibody = 0; ibody < nbody; ibody++) { // update vcm by 1/2 step dtfm = dtf / masstotal[ibody]; vcm[ibody][0] += dtfm * fcm[ibody][0] * fflag[ibody][0]; vcm[ibody][1] += dtfm * fcm[ibody][1] * fflag[ibody][1]; vcm[ibody][2] += dtfm * fcm[ibody][2] * fflag[ibody][2]; // update xcm by full step xcm[ibody][0] += dtv * vcm[ibody][0]; xcm[ibody][1] += dtv * vcm[ibody][1]; xcm[ibody][2] += dtv * vcm[ibody][2]; // update angular momentum by 1/2 step angmom[ibody][0] += dtf * torque[ibody][0] * tflag[ibody][0]; angmom[ibody][1] += dtf * torque[ibody][1] * tflag[ibody][1]; angmom[ibody][2] += dtf * torque[ibody][2] * tflag[ibody][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(angmom[ibody],ex_space[ibody],ey_space[ibody], ez_space[ibody],inertia[ibody],omega[ibody]); MathExtra::richardson(quat[ibody],angmom[ibody],omega[ibody], inertia[ibody],dtq); MathExtra::q_to_exyz(quat[ibody], ex_space[ibody],ey_space[ibody],ez_space[ibody]); } // virial setup before call to set_xv if (vflag) v_setup(vflag); else evflag = 0; // set coords/orient and velocity/rotation of atoms in rigid bodies // from quarternion and omega set_xv(); } /* ---------------------------------------------------------------------- apply Langevin thermostat to all 6 DOF of rigid bodies computed by proc 0, broadcast to other procs unlike fix langevin, this stores extra force in extra arrays, which are added in when final_integrate() calculates a new fcm/torque ------------------------------------------------------------------------- */ void FixRigid::post_force(int vflag) { if (me == 0) { double gamma1,gamma2; double delta = update->ntimestep - update->beginstep; delta /= update->endstep - update->beginstep; 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; for (int i = 0; i < nbody; i++) { gamma1 = -masstotal[i] / t_period / ftm2v; gamma2 = sqrt(masstotal[i]) * tsqrt * sqrt(24.0*boltz/t_period/dt/mvv2e) / ftm2v; langextra[i][0] = gamma1*vcm[i][0] + gamma2*(random->uniform()-0.5); langextra[i][1] = gamma1*vcm[i][1] + gamma2*(random->uniform()-0.5); langextra[i][2] = gamma1*vcm[i][2] + gamma2*(random->uniform()-0.5); gamma1 = -1.0 / t_period / ftm2v; gamma2 = tsqrt * sqrt(24.0*boltz/t_period/dt/mvv2e) / ftm2v; langextra[i][3] = inertia[i][0]*gamma1*omega[i][0] + sqrt(inertia[i][0])*gamma2*(random->uniform()-0.5); langextra[i][4] = inertia[i][1]*gamma1*omega[i][1] + sqrt(inertia[i][1])*gamma2*(random->uniform()-0.5); langextra[i][5] = inertia[i][2]*gamma1*omega[i][2] + sqrt(inertia[i][2])*gamma2*(random->uniform()-0.5); } } MPI_Bcast(&langextra[0][0],6*nbody,MPI_DOUBLE,0,world); } /* ---------------------------------------------------------------------- */ void FixRigid::final_integrate() { int i,ibody; double dtfm,xy,xz,yz; // sum over atoms to get force and torque on rigid body tagint *image = atom->image; double **x = atom->x; double **f = atom->f; int nlocal = atom->nlocal; double dx,dy,dz; double unwrap[3]; for (ibody = 0; ibody < nbody; ibody++) for (i = 0; i < 6; i++) sum[ibody][i] = 0.0; for (i = 0; i < nlocal; i++) { if (body[i] < 0) continue; ibody = body[i]; sum[ibody][0] += f[i][0]; sum[ibody][1] += f[i][1]; sum[ibody][2] += f[i][2]; domain->unmap(x[i],image[i],unwrap); dx = unwrap[0] - xcm[ibody][0]; dy = unwrap[1] - xcm[ibody][1]; dz = unwrap[2] - xcm[ibody][2]; sum[ibody][3] += dy*f[i][2] - dz*f[i][1]; sum[ibody][4] += dz*f[i][0] - dx*f[i][2]; sum[ibody][5] += dx*f[i][1] - dy*f[i][0]; } // extended particles add their torque to torque of body if (extended) { double **torque_one = atom->torque; for (i = 0; i < nlocal; i++) { if (body[i] < 0) continue; ibody = body[i]; if (eflags[i] & TORQUE) { sum[ibody][3] += torque_one[i][0]; sum[ibody][4] += torque_one[i][1]; sum[ibody][5] += torque_one[i][2]; } } } MPI_Allreduce(sum[0],all[0],6*nbody,MPI_DOUBLE,MPI_SUM,world); // update vcm and angmom // include Langevin thermostat forces // fflag,tflag = 0 for some dimensions in 2d for (ibody = 0; ibody < nbody; ibody++) { fcm[ibody][0] = all[ibody][0] + langextra[ibody][0]; fcm[ibody][1] = all[ibody][1] + langextra[ibody][1]; fcm[ibody][2] = all[ibody][2] + langextra[ibody][2]; torque[ibody][0] = all[ibody][3] + langextra[ibody][3]; torque[ibody][1] = all[ibody][4] + langextra[ibody][4]; torque[ibody][2] = all[ibody][5] + langextra[ibody][5]; // update vcm by 1/2 step dtfm = dtf / masstotal[ibody]; vcm[ibody][0] += dtfm * fcm[ibody][0] * fflag[ibody][0]; vcm[ibody][1] += dtfm * fcm[ibody][1] * fflag[ibody][1]; vcm[ibody][2] += dtfm * fcm[ibody][2] * fflag[ibody][2]; // update angular momentum by 1/2 step angmom[ibody][0] += dtf * torque[ibody][0] * tflag[ibody][0]; angmom[ibody][1] += dtf * torque[ibody][1] * tflag[ibody][1]; angmom[ibody][2] += dtf * torque[ibody][2] * tflag[ibody][2]; MathExtra::angmom_to_omega(angmom[ibody],ex_space[ibody],ey_space[ibody], ez_space[ibody],inertia[ibody],omega[ibody]); } // set velocity/rotation of atoms in rigid bodies // virial is already setup from initial_integrate set_v(); } /* ---------------------------------------------------------------------- apply evolution operators to quat, quat momentum see Miller paper cited in fix rigid/nvt and fix rigid/npt ------------------------------------------------------------------------- */ void FixRigid::no_squish_rotate(int k, double *p, double *q, double *inertia, double dt) { double phi,c_phi,s_phi,kp[4],kq[4]; // apply permuation operator on p and q, get kp and kq if (k == 1) { kq[0] = -q[1]; kp[0] = -p[1]; kq[1] = q[0]; kp[1] = p[0]; kq[2] = q[3]; kp[2] = p[3]; kq[3] = -q[2]; kp[3] = -p[2]; } else if (k == 2) { kq[0] = -q[2]; kp[0] = -p[2]; kq[1] = -q[3]; kp[1] = -p[3]; kq[2] = q[0]; kp[2] = p[0]; kq[3] = q[1]; kp[3] = p[1]; } else if (k == 3) { kq[0] = -q[3]; kp[0] = -p[3]; kq[1] = q[2]; kp[1] = p[2]; kq[2] = -q[1]; kp[2] = -p[1]; kq[3] = q[0]; kp[3] = p[0]; } // obtain phi, cosines and sines phi = p[0]*kq[0] + p[1]*kq[1] + p[2]*kq[2] + p[3]*kq[3]; if (fabs(inertia[k-1]) < 1e-6) phi *= 0.0; else phi /= 4.0 * inertia[k-1]; c_phi = cos(dt * phi); s_phi = sin(dt * phi); // advance p and q p[0] = c_phi*p[0] + s_phi*kp[0]; p[1] = c_phi*p[1] + s_phi*kp[1]; p[2] = c_phi*p[2] + s_phi*kp[2]; p[3] = c_phi*p[3] + s_phi*kp[3]; q[0] = c_phi*q[0] + s_phi*kq[0]; q[1] = c_phi*q[1] + s_phi*kq[1]; q[2] = c_phi*q[2] + s_phi*kq[2]; q[3] = c_phi*q[3] + s_phi*kq[3]; } /* ---------------------------------------------------------------------- */ void FixRigid::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 FixRigid::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 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 FixRigid::pre_neighbor() { tagint original,oldimage,newimage; for (int ibody = 0; ibody < nbody; ibody++) { original = imagebody[ibody]; domain->remap(xcm[ibody],imagebody[ibody]); if (original == imagebody[ibody]) remapflag[ibody][3] = 0; else { oldimage = original & IMGMASK; newimage = imagebody[ibody] & IMGMASK; remapflag[ibody][0] = newimage - oldimage; oldimage = (original >> IMGBITS) & IMGMASK; newimage = (imagebody[ibody] >> IMGBITS) & IMGMASK; remapflag[ibody][1] = newimage - oldimage; oldimage = original >> IMG2BITS; newimage = imagebody[ibody] >> IMG2BITS; remapflag[ibody][2] = newimage - oldimage; remapflag[ibody][3] = 1; } } // adjust image flags of any atom in a rigid body whose xcm was remapped tagint *image = atom->image; int nlocal = atom->nlocal; int ibody; tagint idim,otherdims; for (int i = 0; i < nlocal; i++) { if (body[i] == -1) continue; if (remapflag[body[i]][3] == 0) continue; ibody = body[i]; if (remapflag[ibody][0]) { idim = image[i] & IMGMASK; otherdims = image[i] ^ idim; idim -= remapflag[ibody][0]; idim &= IMGMASK; image[i] = otherdims | idim; } if (remapflag[ibody][1]) { idim = (image[i] >> IMGBITS) & IMGMASK; otherdims = image[i] ^ (idim << IMGBITS); idim -= remapflag[ibody][1]; idim &= IMGMASK; image[i] = otherdims | (idim << IMGBITS); } if (remapflag[ibody][2]) { idim = image[i] >> IMG2BITS; otherdims = image[i] ^ (idim << IMG2BITS); idim -= remapflag[ibody][2]; idim &= IMGMASK; image[i] = otherdims | (idim << IMG2BITS); } } } /* ---------------------------------------------------------------------- count # of degrees-of-freedom removed by fix_rigid for atoms in igroup ------------------------------------------------------------------------- */ int FixRigid::dof(int igroup) { int groupbit = group->bitmask[igroup]; // nall = # of point particles in each rigid body // mall = # of finite-size particles in each rigid body // particles must also be in temperature group int *mask = atom->mask; int nlocal = atom->nlocal; int *ncount = new int[nbody]; int *mcount = new int[nbody]; for (int ibody = 0; ibody < nbody; ibody++) ncount[ibody] = mcount[ibody] = 0; for (int i = 0; i < nlocal; i++) if (body[i] >= 0 && mask[i] & groupbit) { if (extended && eflags[i]) mcount[body[i]]++; else ncount[body[i]]++; } int *nall = new int[nbody]; int *mall = new int[nbody]; MPI_Allreduce(ncount,nall,nbody,MPI_INT,MPI_SUM,world); MPI_Allreduce(mcount,mall,nbody,MPI_INT,MPI_SUM,world); // warn if nall+mall != nrigid for any body included in temperature group int flag = 0; for (int ibody = 0; ibody < nbody; ibody++) { if (nall[ibody]+mall[ibody] > 0 && nall[ibody]+mall[ibody] != nrigid[ibody]) flag = 1; } if (flag && 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 int n = 0; if (domain->dimension == 3) { for (int ibody = 0; ibody < nbody; ibody++) if (nall[ibody]+mall[ibody] == nrigid[ibody]) { n += 3*nall[ibody] + 6*mall[ibody] - 6; if (inertia[ibody][0] == 0.0 || inertia[ibody][1] == 0.0 || inertia[ibody][2] == 0.0) n++; } } else if (domain->dimension == 2) { for (int ibody = 0; ibody < nbody; ibody++) if (nall[ibody]+mall[ibody] == nrigid[ibody]) n += 2*nall[ibody] + 3*mall[ibody] - 3; } delete [] ncount; delete [] mcount; delete [] nall; delete [] mall; return n; } /* ---------------------------------------------------------------------- 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 FixRigid::deform(int flag) { if (flag == 0) for (int ibody = 0; ibody < nbody; ibody++) domain->x2lamda(xcm[ibody],xcm[ibody]); else for (int ibody = 0; ibody < nbody; ibody++) domain->lamda2x(xcm[ibody],xcm[ibody]); } /* ---------------------------------------------------------------------- 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 FixRigid::set_xv() { int ibody,itype; int xbox,ybox,zbox; double x0,x1,x2,v0,v1,v2,fc0,fc1,fc2,massone; double xy,xz,yz; double ione[3],exone[3],eyone[3],ezone[3],vr[6],p[3][3]; tagint *image = atom->image; double **x = atom->x; double **v = atom->v; double **f = atom->f; double *rmass = atom->rmass; double *mass = atom->mass; int *type = atom->type; int nlocal = atom->nlocal; double xprd = domain->xprd; double yprd = domain->yprd; double zprd = domain->zprd; if (triclinic) { xy = domain->xy; xz = domain->xz; yz = domain->yz; } // set x and v of each atom for (int i = 0; i < nlocal; i++) { if (body[i] < 0) continue; ibody = body[i]; xbox = (image[i] & IMGMASK) - IMGMAX; ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX; zbox = (image[i] >> IMG2BITS) - IMGMAX; // save old positions and velocities for virial if (evflag) { if (triclinic == 0) { x0 = x[i][0] + xbox*xprd; x1 = x[i][1] + ybox*yprd; x2 = x[i][2] + zbox*zprd; } else { x0 = x[i][0] + xbox*xprd + ybox*xy + zbox*xz; x1 = x[i][1] + ybox*yprd + zbox*yz; x2 = x[i][2] + zbox*zprd; } v0 = v[i][0]; v1 = v[i][1]; v2 = v[i][2]; } // x = displacement from center-of-mass, based on body orientation // v = vcm + omega around center-of-mass MathExtra::matvec(ex_space[ibody],ey_space[ibody], ez_space[ibody],displace[i],x[i]); v[i][0] = omega[ibody][1]*x[i][2] - omega[ibody][2]*x[i][1] + vcm[ibody][0]; v[i][1] = omega[ibody][2]*x[i][0] - omega[ibody][0]*x[i][2] + vcm[ibody][1]; v[i][2] = omega[ibody][0]*x[i][1] - omega[ibody][1]*x[i][0] + vcm[ibody][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] += xcm[ibody][0] - xbox*xprd; x[i][1] += xcm[ibody][1] - ybox*yprd; x[i][2] += xcm[ibody][2] - zbox*zprd; } else { x[i][0] += xcm[ibody][0] - xbox*xprd - ybox*xy - zbox*xz; x[i][1] += xcm[ibody][1] - ybox*yprd - zbox*yz; x[i][2] += xcm[ibody][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_one = atom->omega; double **angmom_one = 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 (body[i] < 0) continue; ibody = body[i]; if (eflags[i] & SPHERE) { omega_one[i][0] = omega[ibody][0]; omega_one[i][1] = omega[ibody][1]; omega_one[i][2] = omega[ibody][2]; } else if (eflags[i] & ELLIPSOID) { shape = ebonus[ellipsoid[i]].shape; quatatom = ebonus[ellipsoid[i]].quat; MathExtra::quatquat(quat[ibody],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(omega[ibody],exone,eyone,ezone,ione, angmom_one[i]); } else if (eflags[i] & LINE) { if (quat[ibody][3] >= 0.0) theta_body = 2.0*acos(quat[ibody][0]); else theta_body = -2.0*acos(quat[ibody][0]); theta = orient[i][0] + theta_body; while (theta <= MINUSPI) theta += TWOPI; while (theta > MY_PI) theta -= TWOPI; lbonus[line[i]].theta = theta; omega_one[i][0] = omega[ibody][0]; omega_one[i][1] = omega[ibody][1]; omega_one[i][2] = omega[ibody][2]; } else if (eflags[i] & TRIANGLE) { inertiaatom = tbonus[tri[i]].inertia; quatatom = tbonus[tri[i]].quat; MathExtra::quatquat(quat[ibody],orient[i],quatatom); MathExtra::qnormalize(quatatom); MathExtra::q_to_exyz(quatatom,exone,eyone,ezone); MathExtra::omega_to_angmom(omega[ibody],exone,eyone,ezone, inertiaatom,angmom_one[i]); } if (eflags[i] & DIPOLE) { MathExtra::quat_to_mat(quat[ibody],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 FixRigid::set_v() { int ibody,itype; int xbox,ybox,zbox; double dx,dy,dz; double x0,x1,x2,v0,v1,v2,fc0,fc1,fc2,massone; double xy,xz,yz; double ione[3],exone[3],eyone[3],ezone[3],delta[3],vr[6]; double **x = atom->x; double **v = atom->v; double **f = atom->f; double *rmass = atom->rmass; double *mass = atom->mass; int *type = atom->type; tagint *image = atom->image; int nlocal = atom->nlocal; double xprd = domain->xprd; double yprd = domain->yprd; double zprd = domain->zprd; if (triclinic) { xy = domain->xy; xz = domain->xz; yz = domain->yz; } // set v of each atom for (int i = 0; i < nlocal; i++) { if (body[i] < 0) continue; ibody = body[i]; MathExtra::matvec(ex_space[ibody],ey_space[ibody], ez_space[ibody],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] = omega[ibody][1]*delta[2] - omega[ibody][2]*delta[1] + vcm[ibody][0]; v[i][1] = omega[ibody][2]*delta[0] - omega[ibody][0]*delta[2] + vcm[ibody][1]; v[i][2] = omega[ibody][0]*delta[1] - omega[ibody][1]*delta[0] + vcm[ibody][2]; // virial = unwrapped coords dotted into body constraint force // body constraint force = implied force due to v change minus f external // assume f does not include forces internal to body // 1/2 factor b/c initial_integrate contributes other half // assume per-atom contribution is due to constraint force on that atom if (evflag) { if (rmass) massone = rmass[i]; else massone = mass[type[i]]; fc0 = massone*(v[i][0] - v0)/dtf - f[i][0]; fc1 = massone*(v[i][1] - v1)/dtf - f[i][1]; fc2 = massone*(v[i][2] - v2)/dtf - f[i][2]; xbox = (image[i] & IMGMASK) - IMGMAX; ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX; zbox = (image[i] >> IMG2BITS) - IMGMAX; if (triclinic == 0) { x0 = x[i][0] + xbox*xprd; x1 = x[i][1] + ybox*yprd; x2 = x[i][2] + zbox*zprd; } else { x0 = x[i][0] + xbox*xprd + ybox*xy + zbox*xz; x1 = x[i][1] + ybox*yprd + zbox*yz; x2 = x[i][2] + zbox*zprd; } vr[0] = 0.5*x0*fc0; vr[1] = 0.5*x1*fc1; vr[2] = 0.5*x2*fc2; vr[3] = 0.5*x0*fc1; vr[4] = 0.5*x0*fc2; vr[5] = 0.5*x1*fc2; v_tally(1,&i,1.0,vr); } } // set omega, angmom of each extended particle if (extended) { double *shape,*quatatom,*inertiaatom; AtomVecEllipsoid::Bonus *ebonus; if (avec_ellipsoid) ebonus = avec_ellipsoid->bonus; AtomVecTri::Bonus *tbonus; if (avec_tri) tbonus = avec_tri->bonus; double **omega_one = atom->omega; double **angmom_one = atom->angmom; int *ellipsoid = atom->ellipsoid; int *tri = atom->tri; for (int i = 0; i < nlocal; i++) { if (body[i] < 0) continue; ibody = body[i]; if (eflags[i] & SPHERE) { omega_one[i][0] = omega[ibody][0]; omega_one[i][1] = omega[ibody][1]; omega_one[i][2] = omega[ibody][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(omega[ibody],exone,eyone,ezone,ione, angmom_one[i]); } else if (eflags[i] & LINE) { omega_one[i][0] = omega[ibody][0]; omega_one[i][1] = omega[ibody][1]; omega_one[i][2] = omega[ibody][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(omega[ibody],exone,eyone,ezone, inertiaatom,angmom_one[i]); } } } } /* ---------------------------------------------------------------------- one-time initialization of rigid body attributes extended flags, masstotal, center-of-mass Cartesian and diagonalized inertia tensor read per-body attributes from infile if specified ------------------------------------------------------------------------- */ void FixRigid::setup_bodies() { int i,itype,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 (body[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); } // 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 (body[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; } } // compute masstotal & center-of-mass of each rigid body // error if image flag is not 0 in a non-periodic dim double **x = atom->x; tagint *image = atom->image; int *periodicity = domain->periodicity; double xprd = domain->xprd; double yprd = domain->yprd; double zprd = domain->zprd; double xy = domain->xy; double xz = domain->xz; double yz = domain->yz; for (ibody = 0; ibody < nbody; ibody++) for (i = 0; i < 6; i++) sum[ibody][i] = 0.0; int xbox,ybox,zbox; double massone,xunwrap,yunwrap,zunwrap; for (i = 0; i < nlocal; i++) { if (body[i] < 0) continue; ibody = body[i]; xbox = (image[i] & IMGMASK) - IMGMAX; ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX; zbox = (image[i] >> IMG2BITS) - IMGMAX; if (rmass) massone = rmass[i]; else massone = mass[type[i]]; if ((xbox && !periodicity[0]) || (ybox && !periodicity[1]) || (zbox && !periodicity[2])) error->one(FLERR,"Fix rigid atom has non-zero image flag " "in a non-periodic dimension"); if (triclinic == 0) { xunwrap = x[i][0] + xbox*xprd; yunwrap = x[i][1] + ybox*yprd; zunwrap = x[i][2] + zbox*zprd; } else { xunwrap = x[i][0] + xbox*xprd + ybox*xy + zbox*xz; yunwrap = x[i][1] + ybox*yprd + zbox*yz; zunwrap = x[i][2] + zbox*zprd; } sum[ibody][0] += xunwrap * massone; sum[ibody][1] += yunwrap * massone; sum[ibody][2] += zunwrap * massone; sum[ibody][3] += massone; } MPI_Allreduce(sum[0],all[0],6*nbody,MPI_DOUBLE,MPI_SUM,world); for (ibody = 0; ibody < nbody; ibody++) { masstotal[ibody] = all[ibody][3]; xcm[ibody][0] = all[ibody][0]/masstotal[ibody]; xcm[ibody][1] = all[ibody][1]/masstotal[ibody]; xcm[ibody][2] = all[ibody][2]/masstotal[ibody]; } // 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,nbody,"rigid:inbody"); for (ibody = 0; ibody < nbody; ibody++) inbody[ibody] = 0; readfile(0,masstotal,xcm,inbody); } // set image flags for each rigid body to default values // then remap the xcm of each body back into simulation box if needed for (ibody = 0; ibody < nbody; ibody++) imagebody[ibody] = ((tagint) IMGMAX << IMG2BITS) | ((tagint) 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 double dx,dy,dz,rad; for (ibody = 0; ibody < nbody; ibody++) for (i = 0; i < 6; i++) sum[ibody][i] = 0.0; for (i = 0; i < nlocal; i++) { if (body[i] < 0) continue; ibody = body[i]; xbox = (image[i] & IMGMASK) - IMGMAX; ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX; zbox = (image[i] >> IMG2BITS) - IMGMAX; if (triclinic == 0) { xunwrap = x[i][0] + xbox*xprd; yunwrap = x[i][1] + ybox*yprd; zunwrap = x[i][2] + zbox*zprd; } else { xunwrap = x[i][0] + xbox*xprd + ybox*xy + zbox*xz; yunwrap = x[i][1] + ybox*yprd + zbox*yz; zunwrap = x[i][2] + zbox*zprd; } dx = xunwrap - xcm[ibody][0]; dy = yunwrap - xcm[ibody][1]; dz = zunwrap - xcm[ibody][2]; if (rmass) massone = rmass[i]; else massone = mass[type[i]]; sum[ibody][0] += massone * (dy*dy + dz*dz); sum[ibody][1] += massone * (dx*dx + dz*dz); sum[ibody][2] += massone * (dx*dx + dy*dy); sum[ibody][3] -= massone * dy*dz; sum[ibody][4] -= massone * dx*dz; sum[ibody][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 (body[i] < 0) continue; ibody = body[i]; if (rmass) massone = rmass[i]; else massone = mass[type[i]]; if (eflags[i] & SPHERE) { sum[ibody][0] += SINERTIA*massone * radius[i]*radius[i]; sum[ibody][1] += SINERTIA*massone * radius[i]*radius[i]; sum[ibody][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); sum[ibody][0] += ivec[0]; sum[ibody][1] += ivec[1]; sum[ibody][2] += ivec[2]; sum[ibody][3] += ivec[3]; sum[ibody][4] += ivec[4]; sum[ibody][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); sum[ibody][0] += ivec[0]; sum[ibody][1] += ivec[1]; sum[ibody][2] += ivec[2]; sum[ibody][3] += ivec[3]; sum[ibody][4] += ivec[4]; sum[ibody][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); sum[ibody][0] += ivec[0]; sum[ibody][1] += ivec[1]; sum[ibody][2] += ivec[2]; sum[ibody][3] += ivec[3]; sum[ibody][4] += ivec[4]; sum[ibody][5] += ivec[5]; } } } MPI_Allreduce(sum[0],all[0],6*nbody,MPI_DOUBLE,MPI_SUM,world); // overwrite Cartesian inertia tensor with file values if (infile) readfile(1,NULL,all,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]; for (ibody = 0; ibody < nbody; ibody++) { tensor[0][0] = all[ibody][0]; tensor[1][1] = all[ibody][1]; tensor[2][2] = all[ibody][2]; tensor[1][2] = tensor[2][1] = all[ibody][3]; tensor[0][2] = tensor[2][0] = all[ibody][4]; tensor[0][1] = tensor[1][0] = all[ibody][5]; ierror = MathExtra::jacobi(tensor,inertia[ibody],evectors); if (ierror) error->all(FLERR, "Insufficient Jacobi rotations for rigid body"); ex_space[ibody][0] = evectors[0][0]; ex_space[ibody][1] = evectors[1][0]; ex_space[ibody][2] = evectors[2][0]; ey_space[ibody][0] = evectors[0][1]; ey_space[ibody][1] = evectors[1][1]; ey_space[ibody][2] = evectors[2][1]; ez_space[ibody][0] = evectors[0][2]; ez_space[ibody][1] = evectors[1][2]; ez_space[ibody][2] = evectors[2][2]; // if any principal moment < scaled EPSILON, set to 0.0 double max; max = MAX(inertia[ibody][0],inertia[ibody][1]); max = MAX(max,inertia[ibody][2]); if (inertia[ibody][0] < EPSILON*max) inertia[ibody][0] = 0.0; if (inertia[ibody][1] < EPSILON*max) inertia[ibody][1] = 0.0; if (inertia[ibody][2] < EPSILON*max) inertia[ibody][2] = 0.0; // enforce 3 evectors as a right-handed coordinate system // flip 3rd vector if needed MathExtra::cross3(ex_space[ibody],ey_space[ibody],cross); if (MathExtra::dot3(cross,ez_space[ibody]) < 0.0) MathExtra::negate3(ez_space[ibody]); // create initial quaternion MathExtra::exyz_to_q(ex_space[ibody],ey_space[ibody],ez_space[ibody], quat[ibody]); } // 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 (body[i] < 0) { displace[i][0] = displace[i][1] = displace[i][2] = 0.0; continue; } ibody = body[i]; xbox = (image[i] & IMGMASK) - IMGMAX; ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX; zbox = (image[i] >> IMG2BITS) - IMGMAX; if (triclinic == 0) { xunwrap = x[i][0] + xbox*xprd; yunwrap = x[i][1] + ybox*yprd; zunwrap = x[i][2] + zbox*zprd; } else { xunwrap = x[i][0] + xbox*xprd + ybox*xy + zbox*xz; yunwrap = x[i][1] + ybox*yprd + zbox*yz; zunwrap = x[i][2] + zbox*zprd; } delta[0] = xunwrap - xcm[ibody][0]; delta[1] = yunwrap - xcm[ibody][1]; delta[2] = zunwrap - xcm[ibody][2]; MathExtra::transpose_matvec(ex_space[ibody],ey_space[ibody], ez_space[ibody],delta,displace[i]); if (extended) { if (eflags[i] & ELLIPSOID) { quatatom = ebonus[ellipsoid[i]].quat; MathExtra::qconjugate(quat[ibody],qc); MathExtra::quatquat(qc,quatatom,orient[i]); MathExtra::qnormalize(orient[i]); } else if (eflags[i] & LINE) { if (quat[ibody][3] >= 0.0) theta_body = 2.0*acos(quat[ibody][0]); else theta_body = -2.0*acos(quat[ibody][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(quat[ibody],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(ex_space[ibody],ey_space[ibody], ez_space[ibody],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 < nbody; ibody++) for (i = 0; i < 6; i++) sum[ibody][i] = 0.0; for (i = 0; i < nlocal; i++) { if (body[i] < 0) continue; ibody = body[i]; if (rmass) massone = rmass[i]; else massone = mass[type[i]]; sum[ibody][0] += massone * (displace[i][1]*displace[i][1] + displace[i][2]*displace[i][2]); sum[ibody][1] += massone * (displace[i][0]*displace[i][0] + displace[i][2]*displace[i][2]); sum[ibody][2] += massone * (displace[i][0]*displace[i][0] + displace[i][1]*displace[i][1]); sum[ibody][3] -= massone * displace[i][1]*displace[i][2]; sum[ibody][4] -= massone * displace[i][0]*displace[i][2]; sum[ibody][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 (body[i] < 0) continue; ibody = body[i]; if (rmass) massone = rmass[i]; else massone = mass[type[i]]; if (eflags[i] & SPHERE) { sum[ibody][0] += SINERTIA*massone * radius[i]*radius[i]; sum[ibody][1] += SINERTIA*massone * radius[i]*radius[i]; sum[ibody][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); sum[ibody][0] += ivec[0]; sum[ibody][1] += ivec[1]; sum[ibody][2] += ivec[2]; sum[ibody][3] += ivec[3]; sum[ibody][4] += ivec[4]; sum[ibody][5] += ivec[5]; } else if (eflags[i] & LINE) { length = lbonus[line[i]].length; MathExtra::inertia_line(length,orient[i][0],massone,ivec); sum[ibody][0] += ivec[0]; sum[ibody][1] += ivec[1]; sum[ibody][2] += ivec[2]; sum[ibody][3] += ivec[3]; sum[ibody][4] += ivec[4]; sum[ibody][5] += ivec[5]; } else if (eflags[i] & TRIANGLE) { inertiaatom = tbonus[tri[i]].inertia; MathExtra::inertia_triangle(inertiaatom,orient[i],massone,ivec); sum[ibody][0] += ivec[0]; sum[ibody][1] += ivec[1]; sum[ibody][2] += ivec[2]; sum[ibody][3] += ivec[3]; sum[ibody][4] += ivec[4]; sum[ibody][5] += ivec[5]; } } } MPI_Allreduce(sum[0],all[0],6*nbody,MPI_DOUBLE,MPI_SUM,world); // 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 < nbody; ibody++) { if (infile && inbody[ibody]) continue; if (inertia[ibody][0] == 0.0) { if (fabs(all[ibody][0]) > TOLERANCE) error->all(FLERR,"Fix rigid: Bad principal moments"); } else { if (fabs((all[ibody][0]-inertia[ibody][0])/inertia[ibody][0]) > TOLERANCE) error->all(FLERR,"Fix rigid: Bad principal moments"); } if (inertia[ibody][1] == 0.0) { if (fabs(all[ibody][1]) > TOLERANCE) error->all(FLERR,"Fix rigid: Bad principal moments"); } else { if (fabs((all[ibody][1]-inertia[ibody][1])/inertia[ibody][1]) > TOLERANCE) error->all(FLERR,"Fix rigid: Bad principal moments"); } if (inertia[ibody][2] == 0.0) { if (fabs(all[ibody][2]) > TOLERANCE) error->all(FLERR,"Fix rigid: Bad principal moments"); } else { if (fabs((all[ibody][2]-inertia[ibody][2])/inertia[ibody][2]) > TOLERANCE) error->all(FLERR,"Fix rigid: Bad principal moments"); } norm = (inertia[ibody][0] + inertia[ibody][1] + inertia[ibody][2]) / 3.0; if (fabs(all[ibody][3]/norm) > TOLERANCE || fabs(all[ibody][4]/norm) > TOLERANCE || fabs(all[ibody][5]/norm) > TOLERANCE) error->all(FLERR,"Fix rigid: Bad principal moments"); } if (infile) memory->destroy(inbody); } /* ---------------------------------------------------------------------- read per rigid body info from user-provided file which = 0 to read total mass and center-of-mass, store in vec and array which = 1 to read 6 moments of inertia, store in array flag inbody = 0 for bodies whose info is read from file nlines = # of lines of rigid body info one line = rigid-ID mass xcm ycm zcm ixx iyy izz ixy ixz iyz ------------------------------------------------------------------------- */ void FixRigid::readfile(int which, double *vec, double **array, int *inbody) { int i,j,m,nchunk,id; int nlines; FILE *fp; char *eof,*start,*next,*buf; char line[MAXLINE]; if (me == 0) { fp = fopen(infile,"r"); if (fp == NULL) { char str[128]; sprintf(str,"Cannot open fix rigid 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 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) { if (nlines-nread > CHUNK) nchunk = CHUNK; else nchunk = nlines-nread; if (me == 0) { char *eof; m = 0; for (i = 0; i < nchunk; i++) { eof = fgets(&buffer[m],MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of fix rigid file"); m += strlen(&buffer[m]); } m++; } MPI_Bcast(&m,1,MPI_INT,0,world); MPI_Bcast(buffer,m,MPI_CHAR,0,world); buf = buffer; if (! (next = strchr(buf,'\n'))) next = buf + strlen(buf); *next = '\0'; int nwords = atom->count_words(buf); *next = '\n'; if (nwords != ATTRIBUTE_PERBODY) error->all(FLERR,"Incorrect rigid body format in fix rigid file"); // loop over lines of rigid body attributes // tokenize the line into values // id = rigid body ID // use ID as-is for SINGLE, as mol-ID for MOLECULE, as-is for GROUP // for which = 0, store mass/com in vec/array // for which = 1, store interia tensor array, invert 3,4,5 values to Voigt for (int i = 0; i < nchunk; i++) { if (! (next = strchr(buf,'\n'))) next = buf + strlen(buf); values[0] = strtok(buf," \t\n\r\f"); for (j = 1; j < nwords; j++) values[j] = strtok(NULL," \t\n\r\f"); id = atoi(values[0]); if (rstyle == MOLECULE) { if (id <= 0 || id > maxmol) error->all(FLERR,"Invalid rigid body ID in fix rigid file"); id = mol2body[id]; } else id--; if (id < 0 || id >= nbody) error->all(FLERR,"Invalid rigid body ID in fix rigid file"); inbody[id] = 1; if (which == 0) { vec[id] = atof(values[1]); array[id][0] = atof(values[2]); array[id][1] = atof(values[3]); array[id][2] = atof(values[4]); } else { array[id][0] = atof(values[5]); array[id][1] = atof(values[6]); array[id][2] = atof(values[7]); array[id][5] = atof(values[8]); array[id][4] = atof(values[9]); array[id][3] = atof(values[10]); } buf = next + 1; } nread += nchunk; } if (me == 0) fclose(fp); delete [] buffer; delete [] values; } /* ---------------------------------------------------------------------- memory usage of local atom-based arrays ------------------------------------------------------------------------- */ double FixRigid::memory_usage() { int nmax = atom->nmax; double bytes = nmax * sizeof(int); bytes += nmax*3 * sizeof(double); bytes += maxvatom*6 * sizeof(double); if (extended) { bytes += nmax * sizeof(int); if (orientflag) bytes = nmax*orientflag * sizeof(double); if (dorientflag) bytes = nmax*3 * sizeof(double); } return bytes; } /* ---------------------------------------------------------------------- allocate local atom-based arrays ------------------------------------------------------------------------- */ void FixRigid::grow_arrays(int nmax) { memory->grow(body,nmax,"rigid:body"); memory->grow(displace,nmax,3,"rigid:displace"); if (extended) { memory->grow(eflags,nmax,"rigid:eflags"); if (orientflag) memory->grow(orient,nmax,orientflag,"rigid:orient"); if (dorientflag) memory->grow(dorient,nmax,3,"rigid:dorient"); } } /* ---------------------------------------------------------------------- copy values within local atom-based arrays ------------------------------------------------------------------------- */ void FixRigid::copy_arrays(int i, int j) { body[j] = body[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]; } } } /* ---------------------------------------------------------------------- initialize one atom's array values, called when atom is created ------------------------------------------------------------------------- */ void FixRigid::set_arrays(int i) { body[i] = -1; displace[i][0] = 0.0; displace[i][1] = 0.0; displace[i][2] = 0.0; } /* ---------------------------------------------------------------------- pack values in local atom-based arrays for exchange with another proc ------------------------------------------------------------------------- */ int FixRigid::pack_exchange(int i, double *buf) { buf[0] = body[i]; buf[1] = displace[i][0]; buf[2] = displace[i][1]; buf[3] = displace[i][2]; if (!extended) return 4; int m = 4; 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]; } return m; } /* ---------------------------------------------------------------------- unpack values in local atom-based arrays from exchange with another proc ------------------------------------------------------------------------- */ int FixRigid::unpack_exchange(int nlocal, double *buf) { body[nlocal] = static_cast (buf[0]); displace[nlocal][0] = buf[1]; displace[nlocal][1] = buf[2]; displace[nlocal][2] = buf[3]; if (!extended) return 4; int m = 4; 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++]; } return m; } /* ---------------------------------------------------------------------- */ void FixRigid::reset_dt() { dtv = update->dt; dtf = 0.5 * update->dt * force->ftm2v; dtq = 0.5 * update->dt; } /* ---------------------------------------------------------------------- return temperature of collection of rigid bodies non-active DOF are removed by fflag/tflag and in tfactor ------------------------------------------------------------------------- */ double FixRigid::compute_scalar() { double wbody[3],rot[3][3]; double t = 0.0; for (int i = 0; i < nbody; i++) { t += masstotal[i] * (fflag[i][0]*vcm[i][0]*vcm[i][0] + fflag[i][1]*vcm[i][1]*vcm[i][1] + \ fflag[i][2]*vcm[i][2]*vcm[i][2]); // wbody = angular velocity in body frame MathExtra::quat_to_mat(quat[i],rot); MathExtra::transpose_matvec(rot,angmom[i],wbody); if (inertia[i][0] == 0.0) wbody[0] = 0.0; else wbody[0] /= inertia[i][0]; if (inertia[i][1] == 0.0) wbody[1] = 0.0; else wbody[1] /= inertia[i][1]; if (inertia[i][2] == 0.0) wbody[2] = 0.0; else wbody[2] /= inertia[i][2]; t += tflag[i][0]*inertia[i][0]*wbody[0]*wbody[0] + tflag[i][1]*inertia[i][1]*wbody[1]*wbody[1] + tflag[i][2]*inertia[i][2]*wbody[2]*wbody[2]; } t *= tfactor; return t; } /* ---------------------------------------------------------------------- */ void *FixRigid::extract(const char *str, int &dim) { if (strcmp(str,"body") == 0) { dim = 1; return body; } if (strcmp(str,"masstotal") == 0) { dim = 1; return masstotal; } if (strcmp(str,"t_target") == 0) { dim = 0; return &t_target; } return NULL; } /* ---------------------------------------------------------------------- return attributes of a rigid body 15 values per body xcm = 0,1,2; vcm = 3,4,5; fcm = 6,7,8; torque = 9,10,11; image = 12,13,14 ------------------------------------------------------------------------- */ double FixRigid::compute_array(int i, int j) { if (j < 3) return xcm[i][j]; if (j < 6) return vcm[i][j-3]; if (j < 9) return fcm[i][j-6]; if (j < 12) return torque[i][j-9]; if (j == 12) return (imagebody[i] & IMGMASK) - IMGMAX; if (j == 13) return (imagebody[i] >> IMGBITS & IMGMASK) - IMGMAX; return (imagebody[i] >> IMG2BITS) - IMGMAX; }