diff --git a/src/ASPHERE/pair_gayberne.cpp b/src/ASPHERE/pair_gayberne.cpp index 4bc4cbc08..f66aefd39 100755 --- a/src/ASPHERE/pair_gayberne.cpp +++ b/src/ASPHERE/pair_gayberne.cpp @@ -1,909 +1,923 @@ /* ---------------------------------------------------------------------- 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: Mike Brown (SNL) ------------------------------------------------------------------------- */ #include "math.h" #include "stdio.h" #include "stdlib.h" #include "string.h" #include "pair_gayberne.h" #include "math_extra.h" #include "atom.h" #include "atom_vec_ellipsoid.h" #include "comm.h" #include "force.h" #include "neighbor.h" #include "neigh_list.h" #include "integrate.h" +#include "citeme.h" #include "memory.h" #include "error.h" using namespace LAMMPS_NS; +static const char cite_pair_gayberne[] = + "pair gayberne command:\n\n" + "@Article{Brown09,\n" + " author = {W. M. Brown, M. K. Petersen, S. J. Plimpton, and G. S. Grest},\n" + " title = {Liquid crystal nanodroplets in solution},\n" + " journal = {J.~Chem.~Phys.},\n" + " year = 2009,\n" + " volume = 130,\n" + " pages = {044901}\n" + "}\n\n"; + /* ---------------------------------------------------------------------- */ PairGayBerne::PairGayBerne(LAMMPS *lmp) : Pair(lmp) { + if (lmp->citeme) lmp->citeme->add(cite_pair_gayberne); + single_enable = 0; } /* ---------------------------------------------------------------------- free all arrays ------------------------------------------------------------------------- */ PairGayBerne::~PairGayBerne() { if (allocated) { memory->destroy(setflag); memory->destroy(cutsq); memory->destroy(form); memory->destroy(epsilon); memory->destroy(sigma); memory->destroy(shape1); memory->destroy(shape2); memory->destroy(well); memory->destroy(cut); memory->destroy(lj1); memory->destroy(lj2); memory->destroy(lj3); memory->destroy(lj4); memory->destroy(offset); delete [] lshape; delete [] setwell; } } /* ---------------------------------------------------------------------- */ void PairGayBerne::compute(int eflag, int vflag) { int i,j,ii,jj,inum,jnum,itype,jtype; double evdwl,one_eng,rsq,r2inv,r6inv,forcelj,factor_lj; double fforce[3],ttor[3],rtor[3],r12[3]; double a1[3][3],b1[3][3],g1[3][3],a2[3][3],b2[3][3],g2[3][3],temp[3][3]; int *ilist,*jlist,*numneigh,**firstneigh; double *iquat,*jquat; evdwl = 0.0; if (eflag || vflag) ev_setup(eflag,vflag); else evflag = vflag_fdotr = 0; AtomVecEllipsoid::Bonus *bonus = avec->bonus; int *ellipsoid = atom->ellipsoid; double **x = atom->x; double **f = atom->f; double **tor = atom->torque; int *type = atom->type; int nlocal = atom->nlocal; double *special_lj = force->special_lj; int newton_pair = force->newton_pair; inum = list->inum; ilist = list->ilist; numneigh = list->numneigh; firstneigh = list->firstneigh; // loop over neighbors of my atoms for (ii = 0; ii < inum; ii++) { i = ilist[ii]; itype = type[i]; if (form[itype][itype] == ELLIPSE_ELLIPSE) { iquat = bonus[ellipsoid[i]].quat; MathExtra::quat_to_mat_trans(iquat,a1); MathExtra::diag_times3(well[itype],a1,temp); MathExtra::transpose_times3(a1,temp,b1); MathExtra::diag_times3(shape2[itype],a1,temp); MathExtra::transpose_times3(a1,temp,g1); } jlist = firstneigh[i]; jnum = numneigh[i]; for (jj = 0; jj < jnum; jj++) { j = jlist[jj]; factor_lj = special_lj[sbmask(j)]; j &= NEIGHMASK; // r12 = center to center vector r12[0] = x[j][0]-x[i][0]; r12[1] = x[j][1]-x[i][1]; r12[2] = x[j][2]-x[i][2]; rsq = MathExtra::dot3(r12,r12); jtype = type[j]; // compute if less than cutoff if (rsq < cutsq[itype][jtype]) { switch (form[itype][jtype]) { case SPHERE_SPHERE: r2inv = 1.0/rsq; r6inv = r2inv*r2inv*r2inv; forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); forcelj *= -r2inv; if (eflag) one_eng = r6inv*(r6inv*lj3[itype][jtype]-lj4[itype][jtype]) - offset[itype][jtype]; fforce[0] = r12[0]*forcelj; fforce[1] = r12[1]*forcelj; fforce[2] = r12[2]*forcelj; ttor[0] = ttor[1] = ttor[2] = 0.0; rtor[0] = rtor[1] = rtor[2] = 0.0; break; case SPHERE_ELLIPSE: jquat = bonus[ellipsoid[j]].quat; MathExtra::quat_to_mat_trans(jquat,a2); MathExtra::diag_times3(well[jtype],a2,temp); MathExtra::transpose_times3(a2,temp,b2); MathExtra::diag_times3(shape2[jtype],a2,temp); MathExtra::transpose_times3(a2,temp,g2); one_eng = gayberne_lj(j,i,a2,b2,g2,r12,rsq,fforce,rtor); ttor[0] = ttor[1] = ttor[2] = 0.0; break; case ELLIPSE_SPHERE: one_eng = gayberne_lj(i,j,a1,b1,g1,r12,rsq,fforce,ttor); rtor[0] = rtor[1] = rtor[2] = 0.0; break; default: jquat = bonus[ellipsoid[j]].quat; MathExtra::quat_to_mat_trans(jquat,a2); MathExtra::diag_times3(well[jtype],a2,temp); MathExtra::transpose_times3(a2,temp,b2); MathExtra::diag_times3(shape2[jtype],a2,temp); MathExtra::transpose_times3(a2,temp,g2); one_eng = gayberne_analytic(i,j,a1,a2,b1,b2,g1,g2,r12,rsq, fforce,ttor,rtor); break; } fforce[0] *= factor_lj; fforce[1] *= factor_lj; fforce[2] *= factor_lj; ttor[0] *= factor_lj; ttor[1] *= factor_lj; ttor[2] *= factor_lj; f[i][0] += fforce[0]; f[i][1] += fforce[1]; f[i][2] += fforce[2]; tor[i][0] += ttor[0]; tor[i][1] += ttor[1]; tor[i][2] += ttor[2]; if (newton_pair || j < nlocal) { rtor[0] *= factor_lj; rtor[1] *= factor_lj; rtor[2] *= factor_lj; f[j][0] -= fforce[0]; f[j][1] -= fforce[1]; f[j][2] -= fforce[2]; tor[j][0] += rtor[0]; tor[j][1] += rtor[1]; tor[j][2] += rtor[2]; } if (eflag) evdwl = factor_lj*one_eng; if (evflag) ev_tally_xyz(i,j,nlocal,newton_pair, evdwl,0.0,fforce[0],fforce[1],fforce[2], -r12[0],-r12[1],-r12[2]); } } } if (vflag_fdotr) virial_fdotr_compute(); } /* ---------------------------------------------------------------------- allocate all arrays ------------------------------------------------------------------------- */ void PairGayBerne::allocate() { allocated = 1; int n = atom->ntypes; memory->create(setflag,n+1,n+1,"pair:setflag"); for (int i = 1; i <= n; i++) for (int j = i; j <= n; j++) setflag[i][j] = 0; memory->create(cutsq,n+1,n+1,"pair:cutsq"); memory->create(form,n+1,n+1,"pair:form"); memory->create(epsilon,n+1,n+1,"pair:epsilon"); memory->create(sigma,n+1,n+1,"pair:sigma"); memory->create(shape1,n+1,3,"pair:shape1"); memory->create(shape2,n+1,3,"pair:shape2"); memory->create(well,n+1,3,"pair:well"); memory->create(cut,n+1,n+1,"pair:cut"); memory->create(lj1,n+1,n+1,"pair:lj1"); memory->create(lj2,n+1,n+1,"pair:lj2"); memory->create(lj3,n+1,n+1,"pair:lj3"); memory->create(lj4,n+1,n+1,"pair:lj4"); memory->create(offset,n+1,n+1,"pair:offset"); lshape = new double[n+1]; setwell = new int[n+1]; for (int i = 1; i <= n; i++) setwell[i] = 0; } /* ---------------------------------------------------------------------- global settings ------------------------------------------------------------------------- */ void PairGayBerne::settings(int narg, char **arg) { if (narg != 4) error->all(FLERR,"Illegal pair_style command"); gamma = force->numeric(FLERR,arg[0]); upsilon = force->numeric(FLERR,arg[1])/2.0; mu = force->numeric(FLERR,arg[2]); cut_global = force->numeric(FLERR,arg[3]); // reset cutoffs that have been explicitly set if (allocated) { int i,j; for (i = 1; i <= atom->ntypes; i++) for (j = i+1; j <= atom->ntypes; j++) if (setflag[i][j]) cut[i][j] = cut_global; } } /* ---------------------------------------------------------------------- set coeffs for one or more type pairs ------------------------------------------------------------------------- */ void PairGayBerne::coeff(int narg, char **arg) { if (narg < 10 || narg > 11) error->all(FLERR,"Incorrect args for pair coefficients"); if (!allocated) allocate(); int ilo,ihi,jlo,jhi; force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); double epsilon_one = force->numeric(FLERR,arg[2]); double sigma_one = force->numeric(FLERR,arg[3]); double eia_one = force->numeric(FLERR,arg[4]); double eib_one = force->numeric(FLERR,arg[5]); double eic_one = force->numeric(FLERR,arg[6]); double eja_one = force->numeric(FLERR,arg[7]); double ejb_one = force->numeric(FLERR,arg[8]); double ejc_one = force->numeric(FLERR,arg[9]); double cut_one = cut_global; if (narg == 11) cut_one = force->numeric(FLERR,arg[10]); int count = 0; for (int i = ilo; i <= ihi; i++) { for (int j = MAX(jlo,i); j <= jhi; j++) { epsilon[i][j] = epsilon_one; sigma[i][j] = sigma_one; cut[i][j] = cut_one; if (eia_one != 0.0 || eib_one != 0.0 || eic_one != 0.0) { well[i][0] = pow(eia_one,-1.0/mu); well[i][1] = pow(eib_one,-1.0/mu); well[i][2] = pow(eic_one,-1.0/mu); if (eia_one == eib_one && eib_one == eic_one) setwell[i] = 2; else setwell[i] = 1; } if (eja_one != 0.0 || ejb_one != 0.0 || ejc_one != 0.0) { well[j][0] = pow(eja_one,-1.0/mu); well[j][1] = pow(ejb_one,-1.0/mu); well[j][2] = pow(ejc_one,-1.0/mu); if (eja_one == ejb_one && ejb_one == ejc_one) setwell[j] = 2; else setwell[j] = 1; } setflag[i][j] = 1; count++; } } if (count == 0) error->all(FLERR,"Incorrect args for pair coefficients"); } /* ---------------------------------------------------------------------- init specific to this pair style ------------------------------------------------------------------------- */ void PairGayBerne::init_style() { avec = (AtomVecEllipsoid *) atom->style_match("ellipsoid"); if (!avec) error->all(FLERR,"Pair gayberne requires atom style ellipsoid"); neighbor->request(this); // per-type shape precalculations // require that atom shapes are identical within each type // if shape = 0 for point particle, set shape = 1 as required by Gay-Berne for (int i = 1; i <= atom->ntypes; i++) { if (!atom->shape_consistency(i,shape1[i][0],shape1[i][1],shape1[i][2])) error->all(FLERR,"Pair gayberne requires atoms with same type have same shape"); if (shape1[i][0] == 0.0) shape1[i][0] = shape1[i][1] = shape1[i][2] = 1.0; shape2[i][0] = shape1[i][0]*shape1[i][0]; shape2[i][1] = shape1[i][1]*shape1[i][1]; shape2[i][2] = shape1[i][2]*shape1[i][2]; lshape[i] = (shape1[i][0]*shape1[i][1]+shape1[i][2]*shape1[i][2]) * sqrt(shape1[i][0]*shape1[i][1]); } } /* ---------------------------------------------------------------------- init for one type pair i,j and corresponding j,i ------------------------------------------------------------------------- */ double PairGayBerne::init_one(int i, int j) { if (setwell[i] == 0 || setwell[j] == 0) error->all(FLERR,"Pair gayberne epsilon a,b,c coeffs are not all set"); if (setflag[i][j] == 0) { epsilon[i][j] = mix_energy(epsilon[i][i],epsilon[j][j], sigma[i][i],sigma[j][j]); sigma[i][j] = mix_distance(sigma[i][i],sigma[j][j]); cut[i][j] = mix_distance(cut[i][i],cut[j][j]); } lj1[i][j] = 48.0 * epsilon[i][j] * pow(sigma[i][j],12.0); lj2[i][j] = 24.0 * epsilon[i][j] * pow(sigma[i][j],6.0); lj3[i][j] = 4.0 * epsilon[i][j] * pow(sigma[i][j],12.0); lj4[i][j] = 4.0 * epsilon[i][j] * pow(sigma[i][j],6.0); if (offset_flag) { double ratio = sigma[i][j] / cut[i][j]; offset[i][j] = 4.0 * epsilon[i][j] * (pow(ratio,12.0) - pow(ratio,6.0)); } else offset[i][j] = 0.0; int ishape = 0; if (shape1[i][0] != shape1[i][1] || shape1[i][0] != shape1[i][2] || shape1[i][1] != shape1[i][2]) ishape = 1; if (setwell[i] == 1) ishape = 1; int jshape = 0; if (shape1[j][0] != shape1[j][1] || shape1[j][0] != shape1[j][2] || shape1[j][1] != shape1[j][2]) jshape = 1; if (setwell[j] == 1) jshape = 1; if (ishape == 0 && jshape == 0) form[i][i] = form[j][j] = form[i][j] = form[j][i] = SPHERE_SPHERE; else if (ishape == 0) { form[i][i] = SPHERE_SPHERE; form[j][j] = ELLIPSE_ELLIPSE; form[i][j] = SPHERE_ELLIPSE; form[j][i] = ELLIPSE_SPHERE; } else if (jshape == 0) { form[j][j] = SPHERE_SPHERE; form[i][i] = ELLIPSE_ELLIPSE; form[j][i] = SPHERE_ELLIPSE; form[i][j] = ELLIPSE_SPHERE; } else form[i][i] = form[j][j] = form[i][j] = form[j][i] = ELLIPSE_ELLIPSE; epsilon[j][i] = epsilon[i][j]; sigma[j][i] = sigma[i][j]; lj1[j][i] = lj1[i][j]; lj2[j][i] = lj2[i][j]; lj3[j][i] = lj3[i][j]; lj4[j][i] = lj4[i][j]; offset[j][i] = offset[i][j]; return cut[i][j]; } /* ---------------------------------------------------------------------- proc 0 writes to restart file ------------------------------------------------------------------------- */ void PairGayBerne::write_restart(FILE *fp) { write_restart_settings(fp); int i,j; for (i = 1; i <= atom->ntypes; i++) { fwrite(&setwell[i],sizeof(int),1,fp); if (setwell[i]) fwrite(&well[i][0],sizeof(double),3,fp); for (j = i; j <= atom->ntypes; j++) { fwrite(&setflag[i][j],sizeof(int),1,fp); if (setflag[i][j]) { fwrite(&epsilon[i][j],sizeof(double),1,fp); fwrite(&sigma[i][j],sizeof(double),1,fp); fwrite(&cut[i][j],sizeof(double),1,fp); } } } } /* ---------------------------------------------------------------------- proc 0 reads from restart file, bcasts ------------------------------------------------------------------------- */ void PairGayBerne::read_restart(FILE *fp) { read_restart_settings(fp); allocate(); int i,j; int me = comm->me; for (i = 1; i <= atom->ntypes; i++) { if (me == 0) fread(&setwell[i],sizeof(int),1,fp); MPI_Bcast(&setwell[i],1,MPI_INT,0,world); if (setwell[i]) { if (me == 0) fread(&well[i][0],sizeof(double),3,fp); MPI_Bcast(&well[i][0],3,MPI_DOUBLE,0,world); } for (j = i; j <= atom->ntypes; j++) { if (me == 0) fread(&setflag[i][j],sizeof(int),1,fp); MPI_Bcast(&setflag[i][j],1,MPI_INT,0,world); if (setflag[i][j]) { if (me == 0) { fread(&epsilon[i][j],sizeof(double),1,fp); fread(&sigma[i][j],sizeof(double),1,fp); fread(&cut[i][j],sizeof(double),1,fp); } MPI_Bcast(&epsilon[i][j],1,MPI_DOUBLE,0,world); MPI_Bcast(&sigma[i][j],1,MPI_DOUBLE,0,world); MPI_Bcast(&cut[i][j],1,MPI_DOUBLE,0,world); } } } } /* ---------------------------------------------------------------------- proc 0 writes to restart file ------------------------------------------------------------------------- */ void PairGayBerne::write_restart_settings(FILE *fp) { fwrite(&gamma,sizeof(double),1,fp); fwrite(&upsilon,sizeof(double),1,fp); fwrite(&mu,sizeof(double),1,fp); fwrite(&cut_global,sizeof(double),1,fp); fwrite(&offset_flag,sizeof(int),1,fp); fwrite(&mix_flag,sizeof(int),1,fp); } /* ---------------------------------------------------------------------- proc 0 reads from restart file, bcasts ------------------------------------------------------------------------- */ void PairGayBerne::read_restart_settings(FILE *fp) { int me = comm->me; if (me == 0) { fread(&gamma,sizeof(double),1,fp); fread(&upsilon,sizeof(double),1,fp); fread(&mu,sizeof(double),1,fp); fread(&cut_global,sizeof(double),1,fp); fread(&offset_flag,sizeof(int),1,fp); fread(&mix_flag,sizeof(int),1,fp); } MPI_Bcast(&gamma,1,MPI_DOUBLE,0,world); MPI_Bcast(&upsilon,1,MPI_DOUBLE,0,world); MPI_Bcast(&mu,1,MPI_DOUBLE,0,world); MPI_Bcast(&cut_global,1,MPI_DOUBLE,0,world); MPI_Bcast(&offset_flag,1,MPI_INT,0,world); MPI_Bcast(&mix_flag,1,MPI_INT,0,world); } /* ---------------------------------------------------------------------- compute analytic energy, force (fforce), and torque (ttor & rtor) based on rotation matrices a and precomputed matrices b and g if newton is off, rtor is not calculated for ghost atoms ------------------------------------------------------------------------- */ double PairGayBerne::gayberne_analytic(const int i,const int j,double a1[3][3], double a2[3][3], double b1[3][3], double b2[3][3], double g1[3][3], double g2[3][3], double *r12, const double rsq, double *fforce, double *ttor, double *rtor) { double tempv[3], tempv2[3]; double temp[3][3]; double temp1,temp2,temp3; int *type = atom->type; int newton_pair = force->newton_pair; int nlocal = atom->nlocal; double r12hat[3]; MathExtra::normalize3(r12,r12hat); double r = sqrt(rsq); // compute distance of closest approach double g12[3][3]; MathExtra::plus3(g1,g2,g12); double kappa[3]; int ierror = MathExtra::mldivide3(g12,r12,kappa); if (ierror) error->all(FLERR,"Bad matrix inversion in mldivide3"); // tempv = G12^-1*r12hat tempv[0] = kappa[0]/r; tempv[1] = kappa[1]/r; tempv[2] = kappa[2]/r; double sigma12 = MathExtra::dot3(r12hat,tempv); sigma12 = pow(0.5*sigma12,-0.5); double h12 = r-sigma12; // energy // compute u_r double varrho = sigma[type[i]][type[j]]/(h12+gamma*sigma[type[i]][type[j]]); double varrho6 = pow(varrho,6.0); double varrho12 = varrho6*varrho6; double u_r = 4.0*epsilon[type[i]][type[j]]*(varrho12-varrho6); // compute eta_12 double eta = 2.0*lshape[type[i]]*lshape[type[j]]; double det_g12 = MathExtra::det3(g12); eta = pow(eta/det_g12,upsilon); // compute chi_12 double b12[3][3]; double iota[3]; MathExtra::plus3(b1,b2,b12); ierror = MathExtra::mldivide3(b12,r12,iota); if (ierror) error->all(FLERR,"Bad matrix inversion in mldivide3"); // tempv = G12^-1*r12hat tempv[0] = iota[0]/r; tempv[1] = iota[1]/r; tempv[2] = iota[2]/r; double chi = MathExtra::dot3(r12hat,tempv); chi = pow(chi*2.0,mu); // force // compute dUr/dr temp1 = (2.0*varrho12*varrho-varrho6*varrho)/sigma[type[i]][type[j]]; temp1 = temp1*24.0*epsilon[type[i]][type[j]]; double u_slj = temp1*pow(sigma12,3.0)/2.0; double dUr[3]; temp2 = MathExtra::dot3(kappa,r12hat); double uslj_rsq = u_slj/rsq; dUr[0] = temp1*r12hat[0]+uslj_rsq*(kappa[0]-temp2*r12hat[0]); dUr[1] = temp1*r12hat[1]+uslj_rsq*(kappa[1]-temp2*r12hat[1]); dUr[2] = temp1*r12hat[2]+uslj_rsq*(kappa[2]-temp2*r12hat[2]); // compute dChi_12/dr double dchi[3]; temp1 = MathExtra::dot3(iota,r12hat); temp2 = -4.0/rsq*mu*pow(chi,(mu-1.0)/mu); dchi[0] = temp2*(iota[0]-temp1*r12hat[0]); dchi[1] = temp2*(iota[1]-temp1*r12hat[1]); dchi[2] = temp2*(iota[2]-temp1*r12hat[2]); temp1 = -eta*u_r; temp2 = eta*chi; fforce[0] = temp1*dchi[0]-temp2*dUr[0]; fforce[1] = temp1*dchi[1]-temp2*dUr[1]; fforce[2] = temp1*dchi[2]-temp2*dUr[2]; // torque for particle 1 and 2 // compute dUr tempv[0] = -uslj_rsq*kappa[0]; tempv[1] = -uslj_rsq*kappa[1]; tempv[2] = -uslj_rsq*kappa[2]; MathExtra::vecmat(kappa,g1,tempv2); MathExtra::cross3(tempv,tempv2,dUr); double dUr2[3]; if (newton_pair || j < nlocal) { MathExtra::vecmat(kappa,g2,tempv2); MathExtra::cross3(tempv,tempv2,dUr2); } // compute d_chi MathExtra::vecmat(iota,b1,tempv); MathExtra::cross3(tempv,iota,dchi); temp1 = -4.0/rsq; dchi[0] *= temp1; dchi[1] *= temp1; dchi[2] *= temp1; double dchi2[3]; if (newton_pair || j < nlocal) { MathExtra::vecmat(iota,b2,tempv); MathExtra::cross3(tempv,iota,dchi2); dchi2[0] *= temp1; dchi2[1] *= temp1; dchi2[2] *= temp1; } // compute d_eta double deta[3]; deta[0] = deta[1] = deta[2] = 0.0; compute_eta_torque(g12,a1,shape2[type[i]],temp); temp1 = -eta*upsilon; for (int m = 0; m < 3; m++) { for (int y = 0; y < 3; y++) tempv[y] = temp1*temp[m][y]; MathExtra::cross3(a1[m],tempv,tempv2); deta[0] += tempv2[0]; deta[1] += tempv2[1]; deta[2] += tempv2[2]; } // compute d_eta for particle 2 double deta2[3]; if (newton_pair || j < nlocal) { deta2[0] = deta2[1] = deta2[2] = 0.0; compute_eta_torque(g12,a2,shape2[type[j]],temp); for (int m = 0; m < 3; m++) { for (int y = 0; y < 3; y++) tempv[y] = temp1*temp[m][y]; MathExtra::cross3(a2[m],tempv,tempv2); deta2[0] += tempv2[0]; deta2[1] += tempv2[1]; deta2[2] += tempv2[2]; } } // torque temp1 = u_r*eta; temp2 = u_r*chi; temp3 = chi*eta; ttor[0] = (temp1*dchi[0]+temp2*deta[0]+temp3*dUr[0]) * -1.0; ttor[1] = (temp1*dchi[1]+temp2*deta[1]+temp3*dUr[1]) * -1.0; ttor[2] = (temp1*dchi[2]+temp2*deta[2]+temp3*dUr[2]) * -1.0; if (newton_pair || j < nlocal) { rtor[0] = (temp1*dchi2[0]+temp2*deta2[0]+temp3*dUr2[0]) * -1.0; rtor[1] = (temp1*dchi2[1]+temp2*deta2[1]+temp3*dUr2[1]) * -1.0; rtor[2] = (temp1*dchi2[2]+temp2*deta2[2]+temp3*dUr2[2]) * -1.0; } return temp1*chi; } /* ---------------------------------------------------------------------- compute analytic energy, force (fforce), and torque (ttor) between ellipsoid and lj particle ------------------------------------------------------------------------- */ double PairGayBerne::gayberne_lj(const int i,const int j,double a1[3][3], double b1[3][3],double g1[3][3], double *r12,const double rsq,double *fforce, double *ttor) { double tempv[3], tempv2[3]; double temp[3][3]; double temp1,temp2,temp3; int *type = atom->type; double r12hat[3]; MathExtra::normalize3(r12,r12hat); double r = sqrt(rsq); // compute distance of closest approach double g12[3][3]; g12[0][0] = g1[0][0]+shape2[type[j]][0]; g12[1][1] = g1[1][1]+shape2[type[j]][0]; g12[2][2] = g1[2][2]+shape2[type[j]][0]; g12[0][1] = g1[0][1]; g12[1][0] = g1[1][0]; g12[0][2] = g1[0][2]; g12[2][0] = g1[2][0]; g12[1][2] = g1[1][2]; g12[2][1] = g1[2][1]; double kappa[3]; int ierror = MathExtra::mldivide3(g12,r12,kappa); if (ierror) error->all(FLERR,"Bad matrix inversion in mldivide3"); // tempv = G12^-1*r12hat tempv[0] = kappa[0]/r; tempv[1] = kappa[1]/r; tempv[2] = kappa[2]/r; double sigma12 = MathExtra::dot3(r12hat,tempv); sigma12 = pow(0.5*sigma12,-0.5); double h12 = r-sigma12; // energy // compute u_r double varrho = sigma[type[i]][type[j]]/(h12+gamma*sigma[type[i]][type[j]]); double varrho6 = pow(varrho,6.0); double varrho12 = varrho6*varrho6; double u_r = 4.0*epsilon[type[i]][type[j]]*(varrho12-varrho6); // compute eta_12 double eta = 2.0*lshape[type[i]]*lshape[type[j]]; double det_g12 = MathExtra::det3(g12); eta = pow(eta/det_g12,upsilon); // compute chi_12 double b12[3][3]; double iota[3]; b12[0][0] = b1[0][0] + well[type[j]][0]; b12[1][1] = b1[1][1] + well[type[j]][0]; b12[2][2] = b1[2][2] + well[type[j]][0]; b12[0][1] = b1[0][1]; b12[1][0] = b1[1][0]; b12[0][2] = b1[0][2]; b12[2][0] = b1[2][0]; b12[1][2] = b1[1][2]; b12[2][1] = b1[2][1]; ierror = MathExtra::mldivide3(b12,r12,iota); if (ierror) error->all(FLERR,"Bad matrix inversion in mldivide3"); // tempv = G12^-1*r12hat tempv[0] = iota[0]/r; tempv[1] = iota[1]/r; tempv[2] = iota[2]/r; double chi = MathExtra::dot3(r12hat,tempv); chi = pow(chi*2.0,mu); // force // compute dUr/dr temp1 = (2.0*varrho12*varrho-varrho6*varrho)/sigma[type[i]][type[j]]; temp1 = temp1*24.0*epsilon[type[i]][type[j]]; double u_slj = temp1*pow(sigma12,3.0)/2.0; double dUr[3]; temp2 = MathExtra::dot3(kappa,r12hat); double uslj_rsq = u_slj/rsq; dUr[0] = temp1*r12hat[0]+uslj_rsq*(kappa[0]-temp2*r12hat[0]); dUr[1] = temp1*r12hat[1]+uslj_rsq*(kappa[1]-temp2*r12hat[1]); dUr[2] = temp1*r12hat[2]+uslj_rsq*(kappa[2]-temp2*r12hat[2]); // compute dChi_12/dr double dchi[3]; temp1 = MathExtra::dot3(iota,r12hat); temp2 = -4.0/rsq*mu*pow(chi,(mu-1.0)/mu); dchi[0] = temp2*(iota[0]-temp1*r12hat[0]); dchi[1] = temp2*(iota[1]-temp1*r12hat[1]); dchi[2] = temp2*(iota[2]-temp1*r12hat[2]); temp1 = -eta*u_r; temp2 = eta*chi; fforce[0] = temp1*dchi[0]-temp2*dUr[0]; fforce[1] = temp1*dchi[1]-temp2*dUr[1]; fforce[2] = temp1*dchi[2]-temp2*dUr[2]; // torque for particle 1 and 2 // compute dUr tempv[0] = -uslj_rsq*kappa[0]; tempv[1] = -uslj_rsq*kappa[1]; tempv[2] = -uslj_rsq*kappa[2]; MathExtra::vecmat(kappa,g1,tempv2); MathExtra::cross3(tempv,tempv2,dUr); // compute d_chi MathExtra::vecmat(iota,b1,tempv); MathExtra::cross3(tempv,iota,dchi); temp1 = -4.0/rsq; dchi[0] *= temp1; dchi[1] *= temp1; dchi[2] *= temp1; // compute d_eta double deta[3]; deta[0] = deta[1] = deta[2] = 0.0; compute_eta_torque(g12,a1,shape2[type[i]],temp); temp1 = -eta*upsilon; for (int m = 0; m < 3; m++) { for (int y = 0; y < 3; y++) tempv[y] = temp1*temp[m][y]; MathExtra::cross3(a1[m],tempv,tempv2); deta[0] += tempv2[0]; deta[1] += tempv2[1]; deta[2] += tempv2[2]; } // torque temp1 = u_r*eta; temp2 = u_r*chi; temp3 = chi*eta; ttor[0] = (temp1*dchi[0]+temp2*deta[0]+temp3*dUr[0]) * -1.0; ttor[1] = (temp1*dchi[1]+temp2*deta[1]+temp3*dUr[1]) * -1.0; ttor[2] = (temp1*dchi[2]+temp2*deta[2]+temp3*dUr[2]) * -1.0; return temp1*chi; } /* ---------------------------------------------------------------------- torque contribution from eta computes trace in the last doc equation for the torque derivative code comes from symbolic solver dump m is g12, m2 is a_i, s is the shape for the particle ------------------------------------------------------------------------- */ void PairGayBerne::compute_eta_torque(double m[3][3], double m2[3][3], double *s, double ans[3][3]) { double den = m[1][0]*m[0][2]*m[2][1]-m[0][0]*m[1][2]*m[2][1]- m[0][2]*m[2][0]*m[1][1]+m[0][1]*m[2][0]*m[1][2]- m[1][0]*m[0][1]*m[2][2]+m[0][0]*m[1][1]*m[2][2]; ans[0][0] = s[0]*(m[1][2]*m[0][1]*m2[0][2]+2.0*m[1][1]*m[2][2]*m2[0][0]- m[1][1]*m2[0][2]*m[0][2]-2.0*m[1][2]*m2[0][0]*m[2][1]+ m2[0][1]*m[0][2]*m[2][1]-m2[0][1]*m[0][1]*m[2][2]- m[1][0]*m[2][2]*m2[0][1]+m[2][0]*m[1][2]*m2[0][1]+ m[1][0]*m2[0][2]*m[2][1]-m2[0][2]*m[2][0]*m[1][1])/den; ans[0][1] = s[0]*(m[0][2]*m2[0][0]*m[2][1]-m[2][2]*m2[0][0]*m[0][1]+ 2.0*m[0][0]*m[2][2]*m2[0][1]-m[0][0]*m2[0][2]*m[1][2]- 2.0*m[2][0]*m[0][2]*m2[0][1]+m2[0][2]*m[1][0]*m[0][2]- m[2][2]*m[1][0]*m2[0][0]+m[2][0]*m2[0][0]*m[1][2]+ m[2][0]*m2[0][2]*m[0][1]-m2[0][2]*m[0][0]*m[2][1])/den; ans[0][2] = s[0]*(m[0][1]*m[1][2]*m2[0][0]-m[0][2]*m2[0][0]*m[1][1]- m[0][0]*m[1][2]*m2[0][1]+m[1][0]*m[0][2]*m2[0][1]- m2[0][1]*m[0][0]*m[2][1]-m[2][0]*m[1][1]*m2[0][0]+ 2.0*m[1][1]*m[0][0]*m2[0][2]-2.0*m[1][0]*m2[0][2]*m[0][1]+ m[1][0]*m[2][1]*m2[0][0]+m[2][0]*m2[0][1]*m[0][1])/den; ans[1][0] = s[1]*(-m[1][1]*m2[1][2]*m[0][2]+2.0*m[1][1]*m[2][2]*m2[1][0]+ m[1][2]*m[0][1]*m2[1][2]-2.0*m[1][2]*m2[1][0]*m[2][1]+ m2[1][1]*m[0][2]*m[2][1]-m2[1][1]*m[0][1]*m[2][2]- m[1][0]*m[2][2]*m2[1][1]+m[2][0]*m[1][2]*m2[1][1]- m2[1][2]*m[2][0]*m[1][1]+m[1][0]*m2[1][2]*m[2][1])/den; ans[1][1] = s[1]*(m[0][2]*m2[1][0]*m[2][1]-m[0][1]*m[2][2]*m2[1][0]+ 2.0*m[2][2]*m[0][0]*m2[1][1]-m2[1][2]*m[0][0]*m[1][2]- 2.0*m[2][0]*m2[1][1]*m[0][2]-m[1][0]*m[2][2]*m2[1][0]+ m[2][0]*m[1][2]*m2[1][0]+m[1][0]*m2[1][2]*m[0][2]- m[0][0]*m2[1][2]*m[2][1]+m2[1][2]*m[0][1]*m[2][0])/den; ans[1][2] = s[1]*(m[0][1]*m[1][2]*m2[1][0]-m[0][2]*m2[1][0]*m[1][1]- m[0][0]*m[1][2]*m2[1][1]+m[1][0]*m[0][2]*m2[1][1]+ 2.0*m[1][1]*m[0][0]*m2[1][2]-m[0][0]*m2[1][1]*m[2][1]+ m[0][1]*m[2][0]*m2[1][1]-m2[1][0]*m[2][0]*m[1][1]- 2.0*m[1][0]*m[0][1]*m2[1][2]+m[1][0]*m2[1][0]*m[2][1])/den; ans[2][0] = s[2]*(-m[1][1]*m[0][2]*m2[2][2]+m[0][1]*m[1][2]*m2[2][2]+ 2.0*m[1][1]*m2[2][0]*m[2][2]-m[0][1]*m2[2][1]*m[2][2]+ m[0][2]*m[2][1]*m2[2][1]-2.0*m2[2][0]*m[2][1]*m[1][2]- m[1][0]*m2[2][1]*m[2][2]+m[1][2]*m[2][0]*m2[2][1]- m[1][1]*m[2][0]*m2[2][2]+m[2][1]*m[1][0]*m2[2][2])/den; ans[2][1] = s[2]*-(m[0][1]*m[2][2]*m2[2][0]-m[0][2]*m2[2][0]*m[2][1]- 2.0*m2[2][1]*m[0][0]*m[2][2]+m[1][2]*m2[2][2]*m[0][0]+ 2.0*m2[2][1]*m[0][2]*m[2][0]+m[1][0]*m2[2][0]*m[2][2]- m[1][0]*m[0][2]*m2[2][2]-m[1][2]*m[2][0]*m2[2][0]+ m[0][0]*m2[2][2]*m[2][1]-m2[2][2]*m[0][1]*m[2][0])/den; ans[2][2] = s[2]*(m[0][1]*m[1][2]*m2[2][0]-m[0][2]*m2[2][0]*m[1][1]- m[0][0]*m[1][2]*m2[2][1]+m[1][0]*m[0][2]*m2[2][1]- m[1][1]*m[2][0]*m2[2][0]-m[2][1]*m2[2][1]*m[0][0]+ 2.0*m[1][1]*m2[2][2]*m[0][0]+m[2][1]*m[1][0]*m2[2][0]+ m[2][0]*m[0][1]*m2[2][1]-2.0*m2[2][2]*m[1][0]*m[0][1])/den; } diff --git a/src/GPU/fix_gpu.cpp b/src/GPU/fix_gpu.cpp index 44f4a5789..6d9d2af6e 100644 --- a/src/GPU/fix_gpu.cpp +++ b/src/GPU/fix_gpu.cpp @@ -1,232 +1,254 @@ /* ---------------------------------------------------------------------- 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 "stdlib.h" #include "fix_gpu.h" #include "atom.h" #include "force.h" #include "pair.h" #include "respa.h" #include "input.h" -#include "error.h" #include "timer.h" #include "modify.h" #include "update.h" #include "domain.h" #include "universe.h" #include "gpu_extra.h" #include "neighbor.h" +#include "citeme.h" +#include "error.h" using namespace LAMMPS_NS; using namespace FixConst; enum{GPU_FORCE, GPU_NEIGH, GPU_HYB_NEIGH}; extern int lmp_init_device(MPI_Comm world, MPI_Comm replica, const int first_gpu, const int last_gpu, const int gpu_mode, const double particle_split, const int nthreads, const int t_per_atom, const double cell_size); extern void lmp_clear_device(); extern double lmp_gpu_forces(double **f, double **tor, double *eatom, double **vatom, double *virial, double &ecoul); +static const char cite_gpu_package[] = + "GPU package (short-range and long-range):\n\n" + "@Article{Brown11,\n" + " author = {W. M. Brown, P. Wang, S. J. Plimpton, A. N. Tharrington},\n" + " title = {Implementing Molecular Dynamics on Hybrid High Performance Computers - Short Range Forces},\n" + " journal = {Comp.~Phys.~Comm.},\n" + " year = 2011,\n" + " volume = 182,\n" + " pages = {898--911}\n" + "}\n\n" + "@Article{Brown12,\n" + " author = {W. M. Brown, A. Kohlmeyer, S. J. Plimpton, A. N. Tharrington},\n" + " title = {Implementing Molecular Dynamics on Hybrid High Performance Computers - Particle-Particle Particle-Mesh},\n" + " journal = {Comp.~Phys.~Comm.},\n" + " year = 2012,\n" + " volume = 183,\n" + " pages = {449--459}\n" + "}\n\n" + /* ---------------------------------------------------------------------- */ FixGPU::FixGPU(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { + if (lmp->citeme) lmp->citeme->add(cite_gpu_package); + if (lmp->cuda) error->all(FLERR,"Cannot use fix GPU with USER-CUDA mode enabled"); if (narg < 7) error->all(FLERR,"Illegal fix GPU command"); if (strcmp(arg[1],"all") != 0) error->all(FLERR,"Illegal fix GPU command"); int first_gpu, last_gpu; if (strcmp(arg[3],"force") == 0) _gpu_mode = GPU_FORCE; else if (strcmp(arg[3],"force/neigh") == 0) { _gpu_mode = GPU_NEIGH; if (domain->triclinic) error->all(FLERR,"Cannot use force/neigh with triclinic box"); } else if (strcmp(arg[3],"force/hybrid_neigh") == 0) { _gpu_mode = GPU_HYB_NEIGH; if (domain->triclinic) error->all(FLERR, "Cannot use force/hybrid_neigh with triclinic box"); } else error->all(FLERR,"Illegal fix GPU command"); first_gpu = force->inumeric(FLERR,arg[4]); last_gpu = force->inumeric(FLERR,arg[5]); _particle_split = force->numeric(FLERR,arg[6]); if (_particle_split==0 || _particle_split>1) error->all(FLERR,"Illegal fix GPU command"); int nthreads = 1; int threads_per_atom = -1; double cell_size = -1; int iarg = 7; while (iarg < narg) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix GPU command"); if (strcmp(arg[iarg],"threads_per_atom") == 0) threads_per_atom = force->inumeric(FLERR,arg[iarg+1]); else if (strcmp(arg[iarg],"nthreads") == 0) nthreads = force->inumeric(FLERR,arg[iarg+1]); else if (strcmp(arg[iarg],"cellsize") == 0) cell_size = force->numeric(FLERR,arg[iarg+1]); else error->all(FLERR,"Illegal fix GPU command"); iarg += 2; } if (nthreads < 1) error->all(FLERR,"Illegal fix GPU command"); #ifndef _OPENMP if (nthreads > 1) error->all(FLERR,"No OpenMP support compiled in"); #endif int gpu_flag = lmp_init_device(universe->uworld, world, first_gpu, last_gpu, _gpu_mode, _particle_split, nthreads, threads_per_atom, cell_size); GPU_EXTRA::check_flag(gpu_flag,error,world); } /* ---------------------------------------------------------------------- */ FixGPU::~FixGPU() { lmp_clear_device(); } /* ---------------------------------------------------------------------- */ int FixGPU::setmask() { int mask = 0; mask |= POST_FORCE; mask |= MIN_POST_FORCE; mask |= POST_FORCE_RESPA; return mask; } /* ---------------------------------------------------------------------- */ void FixGPU::init() { // hybrid cannot be used with force/neigh option if (_gpu_mode == GPU_NEIGH || _gpu_mode == GPU_HYB_NEIGH) if (force->pair_match("hybrid",1) != NULL || force->pair_match("hybrid/overlay",1) != NULL) error->all(FLERR,"Cannot use pair hybrid with GPU neighbor list builds"); if (_particle_split < 0) if (force->pair_match("hybrid",1) != NULL || force->pair_match("hybrid/overlay",1) != NULL) error->all(FLERR,"GPU 'split' must be positive for hybrid pair styles"); // r-RESPA support if (strstr(update->integrate_style,"respa")) { _nlevels_respa = ((Respa *) update->integrate)->nlevels; // need to check that gpu accelerated styles are at the outmost levels if ((force->pair_match("/gpu",0) != NULL) && (((Respa *) update->integrate)->level_pair != _nlevels_respa-1)) error->all(FLERR,"GPU pair style must be at outermost respa level"); if ((force->kspace_match("/gpu",0) != NULL) && (((Respa *) update->integrate)->level_kspace != _nlevels_respa-1)) error->all(FLERR,"GPU Kspace style must be at outermost respa level"); } } /* ---------------------------------------------------------------------- */ void FixGPU::setup(int vflag) { if (_gpu_mode == GPU_NEIGH || _gpu_mode == GPU_HYB_NEIGH) if (neighbor->exclude_setting()!=0) error->all(FLERR, "Cannot use neigh_modify exclude with GPU neighbor builds"); if (strstr(update->integrate_style,"verlet")) post_force(vflag); else { ((Respa *) update->integrate)->copy_flevel_f(_nlevels_respa-1); post_force_respa(vflag,_nlevels_respa-1,0); ((Respa *) update->integrate)->copy_f_flevel(_nlevels_respa-1); } } /* ---------------------------------------------------------------------- */ void FixGPU::min_setup(int vflag) { post_force(vflag); } /* ---------------------------------------------------------------------- */ void FixGPU::post_force(int vflag) { timer->stamp(); double lvirial[6]; for (int i = 0; i < 6; i++) lvirial[i] = 0.0; double my_eng = lmp_gpu_forces(atom->f, atom->torque, force->pair->eatom, force->pair->vatom, lvirial, force->pair->eng_coul); force->pair->eng_vdwl += my_eng; force->pair->virial[0] += lvirial[0]; force->pair->virial[1] += lvirial[1]; force->pair->virial[2] += lvirial[2]; force->pair->virial[3] += lvirial[3]; force->pair->virial[4] += lvirial[4]; force->pair->virial[5] += lvirial[5]; if (force->pair->vflag_fdotr) force->pair->virial_fdotr_compute(); timer->stamp(TIME_PAIR); } /* ---------------------------------------------------------------------- */ void FixGPU::min_post_force(int vflag) { post_force(vflag); } /* ---------------------------------------------------------------------- */ void FixGPU::post_force_respa(int vflag, int ilevel, int iloop) { if (ilevel == _nlevels_respa-1) post_force(vflag); } /* ---------------------------------------------------------------------- */ double FixGPU::memory_usage() { double bytes = 0.0; // Memory usage currently returned by pair routine return bytes; } diff --git a/src/MC/fix_bond_swap.cpp b/src/MC/fix_bond_swap.cpp index 2215d044e..24474052f 100644 --- a/src/MC/fix_bond_swap.cpp +++ b/src/MC/fix_bond_swap.cpp @@ -1,691 +1,705 @@ /* ---------------------------------------------------------------------- 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_bond_swap.h" #include "atom.h" #include "force.h" #include "pair.h" #include "bond.h" #include "angle.h" #include "neighbor.h" #include "neigh_list.h" #include "neigh_request.h" #include "group.h" #include "comm.h" #include "domain.h" #include "modify.h" #include "compute.h" #include "random_mars.h" +#include "citeme.h" #include "memory.h" #include "error.h" #include "update.h" using namespace LAMMPS_NS; using namespace FixConst; +static const char cite_fix_bond_swap[] = + "neighbor multi command:\n\n" + "@Article{Auhl03,\n" + " author = {R. Auhl, R. Everaers, G. S. Grest, K. Kremer, S. J. Plimpton},\n" + " title = {Equilibration of long chain polymer melts in computer simulations},\n" + " journal = {J.~Chem.~Phys.},\n" + " year = 2003,\n" + " volume = 119,\n" + " pages = {12718--12728}\n" + "}\n\n"; + /* ---------------------------------------------------------------------- */ FixBondSwap::FixBondSwap(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { + if (lmp->citeme) lmp->citeme->add(cite_fix_bond_swap); + if (narg != 6) error->all(FLERR,"Illegal fix bond/swap command"); vector_flag = 1; size_vector = 2; global_freq = 1; extvector = 0; fraction = force->numeric(FLERR,arg[3]); double cutoff = force->numeric(FLERR,arg[4]); cutsq = cutoff*cutoff; // initialize Marsaglia RNG with processor-unique seed int seed = force->inumeric(FLERR,arg[5]); random = new RanMars(lmp,seed + comm->me); // create a new compute temp style // id = fix-ID + temp, compute group = fix group 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] = (char *) "all"; newarg[2] = (char *) "temp"; modify->add_compute(3,newarg); delete [] newarg; tflag = 1; // initialize atom list nmax = 0; alist = NULL; naccept = foursome = 0; } /* ---------------------------------------------------------------------- */ FixBondSwap::~FixBondSwap() { delete random; // delete temperature if fix created it if (tflag) modify->delete_compute(id_temp); delete [] id_temp; memory->destroy(alist); } /* ---------------------------------------------------------------------- */ int FixBondSwap::setmask() { int mask = 0; mask |= PRE_NEIGHBOR; return mask; } /* ---------------------------------------------------------------------- */ void FixBondSwap::init() { // require an atom style with molecule IDs if (atom->molecule == NULL) error->all(FLERR,"Must use atom style with molecule IDs with fix bond/swap"); int icompute = modify->find_compute(id_temp); if (icompute < 0) error->all(FLERR,"Temperature ID for fix bond/swap does not exist"); temperature = modify->compute[icompute]; // pair and bonds must be defined // no dihedral or improper potentials allowed // special bonds must be 0 1 1 if (force->pair == NULL || force->bond == NULL) error->all(FLERR,"Fix bond/swap requires pair and bond styles"); if (force->pair->single_enable == 0) error->all(FLERR,"Pair style does not support fix bond/swap"); if (force->angle == NULL && atom->nangles > 0 && comm->me == 0) error->warning(FLERR,"Fix bond/swap will ignore defined angles"); if (force->dihedral || force->improper) error->all(FLERR,"Fix bond/swap cannot use dihedral or improper styles"); if (force->special_lj[1] != 0.0 || force->special_lj[2] != 1.0 || force->special_lj[3] != 1.0) error->all(FLERR,"Fix bond/swap requires special_bonds = 0,1,1"); // need a half neighbor list, built when ever re-neighboring occurs int irequest = neighbor->request((void *) this); neighbor->requests[irequest]->pair = 0; neighbor->requests[irequest]->fix = 1; // zero out stats naccept = foursome = 0; angleflag = 0; if (force->angle) angleflag = 1; } /* ---------------------------------------------------------------------- */ void FixBondSwap::init_list(int id, NeighList *ptr) { list = ptr; } /* ---------------------------------------------------------------------- */ void FixBondSwap::pre_neighbor() { int i,j,ii,jj,m,inum,jnum; int inext,iprev,ilast,jnext,jprev,jlast,ibond,iangle,jbond,jangle; int itag,inexttag,iprevtag,ilasttag,jtag,jnexttag,jprevtag,jlasttag; int ibondtype,jbondtype,iangletype,inextangletype,jangletype,jnextangletype; int i1,i2,i3,j1,j2,j3,tmp; int *ilist,*jlist,*numneigh,**firstneigh; double delta,factor; // compute current temp for Boltzmann factor test double t_current = temperature->compute_scalar(); // local ptrs to atom arrays int *tag = atom->tag; int *mask = atom->mask; int *molecule = atom->molecule; int *num_bond = atom->num_bond; int **bond_atom = atom->bond_atom; int **bond_type = atom->bond_type; int *num_angle = atom->num_angle; int **angle_atom1 = atom->angle_atom1; int **angle_atom2 = atom->angle_atom2; int **angle_atom3 = atom->angle_atom3; int **angle_type = atom->angle_type; int **nspecial = atom->nspecial; int **special = atom->special; int newton_bond = force->newton_bond; int nlocal = atom->nlocal; type = atom->type; x = atom->x; inum = list->inum; ilist = list->ilist; numneigh = list->numneigh; firstneigh = list->firstneigh; // randomize list of my owned atoms that are in fix group // grow atom list if necessary if (nlocal > nmax) { memory->destroy(alist); nmax = atom->nmax; memory->create(alist,nmax,"bondswap:alist"); } int neligible = 0; for (ii = 0; ii < inum; ii++) { i = ilist[ii]; if (mask[i] & groupbit) alist[neligible++] = i; } for (i = 0; i < neligible; i++) { j = static_cast (random->uniform() * neligible); tmp = alist[i]; alist[i] = alist[j]; alist[j] = tmp; } // examine ntest of my eligible atoms for potential swaps // atom i is randomly selected via atom list // look at all j neighbors of atom i // atom j must be on-processor (j < nlocal) // atom j must be in fix group // i and j must be same distance from chain end (mol[i] = mol[j]) // NOTE: must use extra parens in if test on mask[j] & groupbit int ntest = static_cast (fraction * neligible); int accept = 0; for (int itest = 0; itest < ntest; itest++) { i = alist[itest]; jlist = firstneigh[i]; jnum = numneigh[i]; for (jj = 0; jj < jnum; jj++) { j = jlist[jj]; j &= NEIGHMASK; if (j >= nlocal) continue; if ((mask[j] & groupbit) == 0) continue; if (molecule[i] != molecule[j]) continue; // look at all bond partners of atoms i and j // use num_bond for this, not special list, so also find bondtypes // inext,jnext = bonded atoms // inext,jnext must be on-processor (inext,jnext < nlocal) // inext,jnext must be same dist from chain end (mol[inext] = mol[jnext]) // since swaps may occur between two ends of a single chain, insure // the 4 atoms are unique (no duplicates): inext != jnext, inext != j // all 4 old and new bonds must have length < cutoff for (ibond = 0; ibond < num_bond[i]; ibond++) { inext = atom->map(bond_atom[i][ibond]); if (inext >= nlocal || inext < 0) continue; ibondtype = bond_type[i][ibond]; for (jbond = 0; jbond < num_bond[j]; jbond++) { jnext = atom->map(bond_atom[j][jbond]); if (jnext >= nlocal || jnext < 0) continue; jbondtype = bond_type[j][jbond]; if (molecule[inext] != molecule[jnext]) continue; if (inext == jnext || inext == j) continue; if (dist_rsq(i,inext) >= cutsq) continue; if (dist_rsq(j,jnext) >= cutsq) continue; if (dist_rsq(i,jnext) >= cutsq) continue; if (dist_rsq(j,inext) >= cutsq) continue; // if angles are enabled: // find other atoms i,inext,j,jnext are in angles with // and angletypes: i/j angletype, i/j nextangletype // use num_angle for this, not special list, so also find angletypes // 4 atoms consecutively along 1st chain: iprev,i,inext,ilast // 4 atoms consecutively along 2nd chain: jprev,j,jnext,jlast // prev or last atom can be non-existent at end of chain // set prev/last = -1 in this case // if newton bond = 0, then angles are stored by all 4 atoms // so require that iprev,ilast,jprev,jlast be owned by this proc // so all copies of angles can be updated if a swap takes place if (angleflag) { itag = tag[i]; inexttag = tag[inext]; jtag = tag[j]; jnexttag = tag[jnext]; iprev = -1; for (iangle = 0; iangle < num_angle[i]; iangle++) { i1 = angle_atom1[i][iangle]; i2 = angle_atom2[i][iangle]; i3 = angle_atom3[i][iangle]; if (i2 == itag && i3 == inexttag) iprev = atom->map(i1); else if (i1 == inexttag && i2 == itag) iprev = atom->map(i3); if (iprev >= 0) { iangletype = angle_type[i][iangle]; break; } } if (!newton_bond && iprev >= nlocal) continue; ilast = -1; for (iangle = 0; iangle < num_angle[inext]; iangle++) { i1 = angle_atom1[inext][iangle]; i2 = angle_atom2[inext][iangle]; i3 = angle_atom3[inext][iangle]; if (i1 == itag && i2 == inexttag) ilast = atom->map(i3); else if (i2 == inexttag && i3 == itag) ilast = atom->map(i1); if (ilast >= 0) { inextangletype = angle_type[inext][iangle]; break; } } if (!newton_bond && ilast >= nlocal) continue; jprev = -1; for (jangle = 0; jangle < num_angle[j]; jangle++) { j1 = angle_atom1[j][jangle]; j2 = angle_atom2[j][jangle]; j3 = angle_atom3[j][jangle]; if (j2 == jtag && j3 == jnexttag) jprev = atom->map(j1); else if (j1 == jnexttag && j2 == jtag) jprev = atom->map(j3); if (jprev >= 0) { jangletype = angle_type[j][jangle]; break; } } if (!newton_bond && jprev >= nlocal) continue; jlast = -1; for (jangle = 0; jangle < num_angle[jnext]; jangle++) { j1 = angle_atom1[jnext][jangle]; j2 = angle_atom2[jnext][jangle]; j3 = angle_atom3[jnext][jangle]; if (j1 == jtag && j2 == jnexttag) jlast = atom->map(j3); else if (j2 == jnexttag && j3 == jtag) jlast = atom->map(j1); if (jlast >= 0) { jnextangletype = angle_type[jnext][jangle]; break; } } if (!newton_bond && jlast >= nlocal) continue; } // valid foursome found between 2 chains: // chains = iprev-i-inext-ilast and jprev-j-jnext-jlast // prev or last values are -1 if do not exist due to end of chain // OK to call angle_eng with -1 atom, since just return 0.0 // current energy of foursome = // E_nb(i,j) + E_nb(i,jnext) + E_nb(inext,j) + E_nb(inext,jnext) + // E_bond(i,inext) + E_bond(j,jnext) + // E_angle(iprev,i,inext) + E_angle(i,inext,ilast) + // E_angle(jprev,j,jnext) + E_angle(j,jnext,jlast) // new energy of foursome with swapped bonds = // E_nb(i,j) + E_nb(i,inext) + E_nb(j,jnext) + E_nb(inext,jnext) + // E_bond(i,jnext) + E_bond(j,inext) + // E_angle(iprev,i,jnext) + E_angle(i,jnext,jlast) + // E_angle(jprev,j,inext) + E_angle(j,inext,ilast) // energy delta = add/subtract differing terms between 2 formulas foursome++; delta = pair_eng(i,inext) + pair_eng(j,jnext) - pair_eng(i,jnext) - pair_eng(inext,j); delta += bond_eng(ibondtype,i,jnext) + bond_eng(jbondtype,j,inext) - bond_eng(ibondtype,i,inext) - bond_eng(jbondtype,j,jnext); if (angleflag) delta += angle_eng(iangletype,iprev,i,jnext) + angle_eng(jnextangletype,i,jnext,jlast) + angle_eng(jangletype,jprev,j,inext) + angle_eng(inextangletype,j,inext,ilast) - angle_eng(iangletype,iprev,i,inext) - angle_eng(inextangletype,i,inext,ilast) - angle_eng(jangletype,jprev,j,jnext) - angle_eng(jnextangletype,j,jnext,jlast); // if delta <= 0, accept swap // if delta > 0, compute Boltzmann factor with current temperature // only accept if greater than random value // whether accept or not, exit test loop if (delta < 0.0) accept = 1; else { factor = exp(-delta/force->boltz/t_current); if (random->uniform() < factor) accept = 1; } goto done; } } } } done: if (!accept) return; naccept++; // change bond partners of affected atoms // on atom i: bond i-inext changes to i-jnext // on atom j: bond j-jnext changes to j-inext // on atom inext: bond inext-i changes to inext-j // on atom jnext: bond jnext-j changes to jnext-i for (ibond = 0; ibond < num_bond[i]; ibond++) if (bond_atom[i][ibond] == tag[inext]) bond_atom[i][ibond] = tag[jnext]; for (jbond = 0; jbond < num_bond[j]; jbond++) if (bond_atom[j][jbond] == tag[jnext]) bond_atom[j][jbond] = tag[inext]; for (ibond = 0; ibond < num_bond[inext]; ibond++) if (bond_atom[inext][ibond] == tag[i]) bond_atom[inext][ibond] = tag[j]; for (jbond = 0; jbond < num_bond[jnext]; jbond++) if (bond_atom[jnext][jbond] == tag[j]) bond_atom[jnext][jbond] = tag[i]; // set global tags of 4 atoms in bonds itag = tag[i]; inexttag = tag[inext]; jtag = tag[j]; jnexttag = tag[jnext]; // change 1st special neighbors of affected atoms: i,j,inext,jnext // don't need to change 2nd/3rd special neighbors for any atom // since special bonds = 0 1 1 means they are never used for (m = 0; m < nspecial[i][0]; m++) if (special[i][m] == inexttag) special[i][m] = jnexttag; for (m = 0; m < nspecial[j][0]; m++) if (special[j][m] == jnexttag) special[j][m] = inexttag; for (m = 0; m < nspecial[inext][0]; m++) if (special[inext][m] == itag) special[inext][m] = jtag; for (m = 0; m < nspecial[jnext][0]; m++) if (special[jnext][m] == jtag) special[jnext][m] = itag; // done if no angles if (!angleflag) return; // set global tags of 4 additional atoms in angles, 0 if no angle if (iprev >= 0) iprevtag = tag[iprev]; else iprevtag = 0; if (ilast >= 0) ilasttag = tag[ilast]; else ilasttag = 0; if (jprev >= 0) jprevtag = tag[jprev]; else jprevtag = 0; if (jlast >= 0) jlasttag = tag[jlast]; else jlasttag = 0; // change angle partners of affected atoms // must check if each angle is stored as a-b-c or c-b-a // on atom i: // angle iprev-i-inext changes to iprev-i-jnext // angle i-inext-ilast changes to i-jnext-jlast // on atom j: // angle jprev-j-jnext changes to jprev-j-inext // angle j-jnext-jlast changes to j-inext-ilast // on atom inext: // angle iprev-i-inext changes to jprev-j-inext // angle i-inext-ilast changes to j-inext-ilast // on atom jnext: // angle jprev-j-jnext changes to iprev-i-jnext // angle j-jnext-jlast changes to i-jnext-jlast for (iangle = 0; iangle < num_angle[i]; iangle++) { i1 = angle_atom1[i][iangle]; i2 = angle_atom2[i][iangle]; i3 = angle_atom3[i][iangle]; if (i1 == iprevtag && i2 == itag && i3 == inexttag) angle_atom3[i][iangle] = jnexttag; else if (i1 == inexttag && i2 == itag && i3 == iprevtag) angle_atom1[i][iangle] = jnexttag; else if (i1 == itag && i2 == inexttag && i3 == ilasttag) { angle_atom2[i][iangle] = jnexttag; angle_atom3[i][iangle] = jlasttag; } else if (i1 == ilasttag && i2 == inexttag && i3 == itag) { angle_atom1[i][iangle] = jlasttag; angle_atom2[i][iangle] = jnexttag; } } for (jangle = 0; jangle < num_angle[j]; jangle++) { j1 = angle_atom1[j][jangle]; j2 = angle_atom2[j][jangle]; j3 = angle_atom3[j][jangle]; if (j1 == jprevtag && j2 == jtag && j3 == jnexttag) angle_atom3[j][jangle] = inexttag; else if (j1 == jnexttag && j2 == jtag && j3 == jprevtag) angle_atom1[j][jangle] = inexttag; else if (j1 == jtag && j2 == jnexttag && j3 == jlasttag) { angle_atom2[j][jangle] = inexttag; angle_atom3[j][jangle] = ilasttag; } else if (j1 == jlasttag && j2 == jnexttag && j3 == jtag) { angle_atom1[j][jangle] = ilasttag; angle_atom2[j][jangle] = inexttag; } } for (iangle = 0; iangle < num_angle[inext]; iangle++) { i1 = angle_atom1[inext][iangle]; i2 = angle_atom2[inext][iangle]; i3 = angle_atom3[inext][iangle]; if (i1 == iprevtag && i2 == itag && i3 == inexttag) { angle_atom1[inext][iangle] = jprevtag; angle_atom2[inext][iangle] = jtag; } else if (i1 == inexttag && i2 == itag && i3 == iprevtag) { angle_atom2[inext][iangle] = jtag; angle_atom3[inext][iangle] = jprevtag; } else if (i1 == itag && i2 == inexttag && i3 == ilasttag) angle_atom1[inext][iangle] = jtag; else if (i1 == ilasttag && i2 == inexttag && i3 == itag) angle_atom3[inext][iangle] = jtag; } for (jangle = 0; jangle < num_angle[jnext]; jangle++) { j1 = angle_atom1[jnext][jangle]; j2 = angle_atom2[jnext][jangle]; j3 = angle_atom3[jnext][jangle]; if (j1 == jprevtag && j2 == jtag && j3 == jnexttag) { angle_atom1[jnext][jangle] = iprevtag; angle_atom2[jnext][jangle] = itag; } else if (j1 == jnexttag && j2 == jtag && j3 == jprevtag) { angle_atom2[jnext][jangle] = itag; angle_atom3[jnext][jangle] = iprevtag; } else if (j1 == jtag && j2 == jnexttag && j3 == jlasttag) angle_atom1[jnext][jangle] = itag; else if (j1 == jlasttag && j2 == jnexttag && j3 == jtag) angle_atom3[jnext][jangle] = itag; } // done if newton bond set if (newton_bond) return; // change angles stored by iprev,ilast,jprev,jlast // on atom iprev: angle iprev-i-inext changes to iprev-i-jnext // on atom jprev: angle jprev-j-jnext changes to jprev-j-inext // on atom ilast: angle i-inext-ilast changes to j-inext-ilast // on atom jlast: angle j-jnext-jlast changes to i-jnext-jlast for (iangle = 0; iangle < num_angle[iprev]; iangle++) { i1 = angle_atom1[iprev][iangle]; i2 = angle_atom2[iprev][iangle]; i3 = angle_atom3[iprev][iangle]; if (i1 == iprevtag && i2 == itag && i3 == inexttag) angle_atom3[iprev][iangle] = jnexttag; else if (i1 == inexttag && i2 == itag && i3 == iprevtag) angle_atom1[iprev][iangle] = jnexttag; } for (jangle = 0; jangle < num_angle[jprev]; jangle++) { j1 = angle_atom1[jprev][jangle]; j2 = angle_atom2[jprev][jangle]; j3 = angle_atom3[jprev][jangle]; if (j1 == jprevtag && j2 == jtag && j3 == jnexttag) angle_atom3[jprev][jangle] = inexttag; else if (j1 == jnexttag && j2 == jtag && j3 == jprevtag) angle_atom1[jprev][jangle] = inexttag; } for (iangle = 0; iangle < num_angle[ilast]; iangle++) { i1 = angle_atom1[ilast][iangle]; i2 = angle_atom2[ilast][iangle]; i3 = angle_atom3[ilast][iangle]; if (i1 == itag && i2 == inexttag && i3 == ilasttag) angle_atom1[ilast][iangle] = jtag; else if (i1 == ilasttag && i2 == inexttag && i3 == itag) angle_atom3[ilast][iangle] = jtag; } for (jangle = 0; jangle < num_angle[jlast]; jangle++) { j1 = angle_atom1[jlast][jangle]; j2 = angle_atom2[jlast][jangle]; j3 = angle_atom3[jlast][jangle]; if (j1 == jtag && j2 == jnexttag && j3 == jlasttag) angle_atom1[jlast][jangle] = itag; else if (j1 == jlasttag && j2 == jnexttag && j3 == jtag) angle_atom3[jlast][jangle] = itag; } } /* ---------------------------------------------------------------------- */ int FixBondSwap::modify_param(int narg, char **arg) { if (strcmp(arg[0],"temp") == 0) { if (narg < 2) error->all(FLERR,"Illegal fix_modify command"); if (tflag) { modify->delete_compute(id_temp); tflag = 0; } delete [] id_temp; int n = strlen(arg[1]) + 1; id_temp = new char[n]; strcpy(id_temp,arg[1]); int icompute = modify->find_compute(id_temp); if (icompute < 0) error->all(FLERR,"Could not find fix_modify temperature ID"); temperature = modify->compute[icompute]; if (temperature->tempflag == 0) error->all(FLERR,"Fix_modify temperature ID does not compute temperature"); if (temperature->igroup != igroup && comm->me == 0) error->warning(FLERR,"Group for fix_modify temp != fix group"); return 2; } return 0; } /* ---------------------------------------------------------------------- compute squared distance between atoms I,J must use minimum_image since J was found thru atom->map() ------------------------------------------------------------------------- */ double FixBondSwap::dist_rsq(int i, int j) { double delx = x[i][0] - x[j][0]; double dely = x[i][1] - x[j][1]; double delz = x[i][2] - x[j][2]; domain->minimum_image(delx,dely,delz); return (delx*delx + dely*dely + delz*delz); } /* ---------------------------------------------------------------------- return pairwise interaction energy between atoms I,J will always be full non-bond interaction, so factors = 1 in single() call ------------------------------------------------------------------------- */ double FixBondSwap::pair_eng(int i, int j) { double tmp; double rsq = dist_rsq(i,j); return force->pair->single(i,j,type[i],type[j],rsq,1.0,1.0,tmp); } /* ---------------------------------------------------------------------- */ double FixBondSwap::bond_eng(int btype, int i, int j) { double tmp; double rsq = dist_rsq(i,j); return force->bond->single(btype,rsq,i,j,tmp); } /* ---------------------------------------------------------------------- */ double FixBondSwap::angle_eng(int atype, int i, int j, int k) { // test for non-existent angle at end of chain if (i == -1 || k == -1) return 0.0; return force->angle->single(atype,i,j,k); } /* ---------------------------------------------------------------------- return bond swapping stats n = 1 is # of swaps n = 2 is # of attempted swaps ------------------------------------------------------------------------- */ double FixBondSwap::compute_vector(int n) { double one,all; if (n == 0) one = naccept; else one = foursome; MPI_Allreduce(&one,&all,1,MPI_DOUBLE,MPI_SUM,world); return all; } /* ---------------------------------------------------------------------- memory usage of alist ------------------------------------------------------------------------- */ double FixBondSwap::memory_usage() { double bytes = nmax * sizeof(int); return bytes; } diff --git a/src/MISC/fix_orient_fcc.cpp b/src/MISC/fix_orient_fcc.cpp index 361147830..a7fc2ecea 100644 --- a/src/MISC/fix_orient_fcc.cpp +++ b/src/MISC/fix_orient_fcc.cpp @@ -1,593 +1,607 @@ /* ---------------------------------------------------------------------- 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: Koenraad Janssens and David Olmsted (SNL) ------------------------------------------------------------------------- */ #include "math.h" #include "string.h" #include "stdlib.h" #include "mpi.h" #include "fix_orient_fcc.h" #include "atom.h" #include "update.h" #include "respa.h" #include "neighbor.h" #include "neigh_list.h" #include "neigh_request.h" #include "comm.h" #include "output.h" +#include "force.h" #include "math_const.h" +#include "citeme.h" #include "memory.h" #include "error.h" -#include "force.h" using namespace LAMMPS_NS; using namespace FixConst; using namespace MathConst; #define BIG 1000000000 +static const char cite_fix_orient_fcc[] = + "fix orient/fcc command:\n\n" + "@Article{Janssens06,\n" + " author = {K. G. F. Janssens, D. Olmsted, E.A. Holm, S. M. Foiles, S. J. Plimpton, and P. M. Derlet},\n" + " title = {Computing the Mobility of Grain Boundaries},\n" + " journal = {Nature Materials},\n" + " year = 2006,\n" + " volume = 5,\n" + " pages = {124--127}\n" + "}\n\n"; + /* ---------------------------------------------------------------------- */ FixOrientFCC::FixOrientFCC(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { + if (lmp->citeme) lmp->citeme->add(cite_fix_orient_fcc); + MPI_Comm_rank(world,&me); if (narg != 11) error->all(FLERR,"Illegal fix orient/fcc command"); scalar_flag = 1; global_freq = 1; extscalar = 1; peratom_flag = 1; size_peratom_cols = 2; peratom_freq = 1; nstats = force->inumeric(FLERR,arg[3]); direction_of_motion = force->inumeric(FLERR,arg[4]); a = force->numeric(FLERR,arg[5]); Vxi = force->numeric(FLERR,arg[6]); uxif_low = force->numeric(FLERR,arg[7]); uxif_high = force->numeric(FLERR,arg[8]); if (direction_of_motion == 0) { int n = strlen(arg[9]) + 1; chifilename = new char[n]; strcpy(chifilename,arg[9]); n = strlen(arg[10]) + 1; xifilename = new char[n]; strcpy(xifilename,arg[10]); } else if (direction_of_motion == 1) { int n = strlen(arg[9]) + 1; xifilename = new char[n]; strcpy(xifilename,arg[9]); n = strlen(arg[10]) + 1; chifilename = new char[n]; strcpy(chifilename,arg[10]); } else error->all(FLERR,"Illegal fix orient/fcc command"); // initializations half_fcc_nn = 6; use_xismooth = false; double xicutoff = 1.57; xicutoffsq = xicutoff * xicutoff; cutsq = 0.5 * a*a*xicutoffsq; nmax = 0; // read xi and chi reference orientations from files if (me == 0) { char line[IMGMAX]; char *result; int count; FILE *infile = fopen(xifilename,"r"); if (infile == NULL) error->one(FLERR,"Fix orient/fcc file open failed"); for (int i = 0; i < 6; i++) { result = fgets(line,IMGMAX,infile); if (!result) error->one(FLERR,"Fix orient/fcc file read failed"); count = sscanf(line,"%lg %lg %lg",&Rxi[i][0],&Rxi[i][1],&Rxi[i][2]); if (count != 3) error->one(FLERR,"Fix orient/fcc file read failed"); } fclose(infile); infile = fopen(chifilename,"r"); if (infile == NULL) error->one(FLERR,"Fix orient/fcc file open failed"); for (int i = 0; i < 6; i++) { result = fgets(line,IMGMAX,infile); if (!result) error->one(FLERR,"Fix orient/fcc file read failed"); count = sscanf(line,"%lg %lg %lg",&Rchi[i][0],&Rchi[i][1],&Rchi[i][2]); if (count != 3) error->one(FLERR,"Fix orient/fcc file read failed"); } fclose(infile); } MPI_Bcast(&Rxi[0][0],18,MPI_DOUBLE,0,world); MPI_Bcast(&Rchi[0][0],18,MPI_DOUBLE,0,world); // make copy of the reference vectors for (int i = 0; i < 6; i++) for (int j = 0; j < 3; j++) { half_xi_chi_vec[0][i][j] = Rxi[i][j]; half_xi_chi_vec[1][i][j] = Rchi[i][j]; } // compute xiid,xi0,xi1 for all 12 neighbors // xi is the favored crystal // want order parameter when actual is Rchi double xi_sq,dxi[3],rchi[3]; xiid = 0.0; for (int i = 0; i < 6; i++) { rchi[0] = Rchi[i][0]; rchi[1] = Rchi[i][1]; rchi[2] = Rchi[i][2]; find_best_ref(rchi,0,xi_sq,dxi); xiid += sqrt(xi_sq); for (int j = 0; j < 3; j++) rchi[j] = -rchi[j]; find_best_ref(rchi,0,xi_sq,dxi); xiid += sqrt(xi_sq); } xiid /= 12.0; xi0 = uxif_low * xiid; xi1 = uxif_high * xiid; // set comm size needed by this Fix // NOTE: doesn't seem that use_xismooth is ever true if (use_xismooth) comm_forward = 62; else comm_forward = 50; added_energy = 0.0; nmax = atom->nmax; nbr = (Nbr *) memory->smalloc(nmax*sizeof(Nbr),"orient/fcc:nbr"); memory->create(order,nmax,2,"orient/fcc:order"); array_atom = order; // zero the array since a variable may access it before first run int nlocal = atom->nlocal; for (int i = 0; i < nlocal; i++) order[i][0] = order[i][1] = 0.0; } /* ---------------------------------------------------------------------- */ FixOrientFCC::~FixOrientFCC() { delete [] xifilename; delete [] chifilename; memory->sfree(nbr); memory->destroy(order); } /* ---------------------------------------------------------------------- */ int FixOrientFCC::setmask() { int mask = 0; mask |= POST_FORCE; mask |= THERMO_ENERGY; mask |= POST_FORCE_RESPA; return mask; } /* ---------------------------------------------------------------------- */ void FixOrientFCC::init() { if (strstr(update->integrate_style,"respa")) nlevels_respa = ((Respa *) update->integrate)->nlevels; // need a full neighbor list, built whenever re-neighboring occurs int irequest = neighbor->request((void *) this); neighbor->requests[irequest]->pair = 0; neighbor->requests[irequest]->fix = 1; neighbor->requests[irequest]->half = 0; neighbor->requests[irequest]->full = 1; } /* ---------------------------------------------------------------------- */ void FixOrientFCC::init_list(int id, NeighList *ptr) { list = ptr; } /* ---------------------------------------------------------------------- */ void FixOrientFCC::setup(int vflag) { if (strstr(update->integrate_style,"verlet")) post_force(vflag); else { ((Respa *) update->integrate)->copy_flevel_f(nlevels_respa-1); post_force_respa(vflag,nlevels_respa-1,0); ((Respa *) update->integrate)->copy_f_flevel(nlevels_respa-1); } } /* ---------------------------------------------------------------------- */ void FixOrientFCC::post_force(int vflag) { int i,j,k,ii,jj,inum,jnum,m,n,nn,nsort,id_self; int *ilist,*jlist,*numneigh,**firstneigh; double edelta,omega; double dx,dy,dz,rsq,xismooth,xi_sq,duxi,duxi_other; double dxi[3]; double *dxiptr; bool found_myself; // set local ptrs double **x = atom->x; double **f = atom->f; int *mask = atom->mask; int *tag = atom->tag; int nlocal = atom->nlocal; int nall = atom->nlocal + atom->nghost; inum = list->inum; ilist = list->ilist; numneigh = list->numneigh; firstneigh = list->firstneigh; // insure nbr and order data structures are adequate size if (nall > nmax) { nmax = nall; memory->destroy(nbr); memory->destroy(order); nbr = (Nbr *) memory->smalloc(nmax*sizeof(Nbr),"orient/fcc:nbr"); memory->create(order,nmax,2,"orient/fcc:order"); array_atom = order; } // loop over owned atoms and build Nbr data structure of neighbors // use full neighbor list added_energy = 0.0; int count = 0; int mincount = BIG; int maxcount = 0; for (ii = 0; ii < inum; ii++) { i = ilist[ii]; jlist = firstneigh[i]; jnum = numneigh[i]; if (jnum < mincount) mincount = jnum; if (jnum > maxcount) { if (maxcount) delete [] sort; sort = new Sort[jnum]; maxcount = jnum; } // loop over all neighbors of atom i // for those within cutsq, build sort data structure // store local id, rsq, delta vector, xismooth (if included) nsort = 0; for (jj = 0; jj < jnum; jj++) { j = jlist[jj]; j &= NEIGHMASK; count++; dx = x[i][0] - x[j][0]; dy = x[i][1] - x[j][1]; dz = x[i][2] - x[j][2]; rsq = dx*dx + dy*dy + dz*dz; if (rsq < cutsq) { sort[nsort].id = j; sort[nsort].rsq = rsq; sort[nsort].delta[0] = dx; sort[nsort].delta[1] = dy; sort[nsort].delta[2] = dz; if (use_xismooth) { xismooth = (xicutoffsq - 2.0*rsq/(a*a)) / (xicutoffsq - 1.0); sort[nsort].xismooth = 1.0 - fabs(1.0-xismooth); } nsort++; } } // sort neighbors by rsq distance // no need to sort if nsort <= 12 if (nsort > 12) qsort(sort,nsort,sizeof(Sort),compare); // copy up to 12 nearest neighbors into nbr data structure // operate on delta vector via find_best_ref() to compute dxi n = MIN(12,nsort); nbr[i].n = n; if (n == 0) continue; double xi_total = 0.0; for (j = 0; j < n; j++) { find_best_ref(sort[j].delta,0,xi_sq,dxi); xi_total += sqrt(xi_sq); nbr[i].id[j] = sort[j].id; nbr[i].dxi[j][0] = dxi[0]/n; nbr[i].dxi[j][1] = dxi[1]/n; nbr[i].dxi[j][2] = dxi[2]/n; if (use_xismooth) nbr[i].xismooth[j] = sort[j].xismooth; } xi_total /= n; order[i][0] = xi_total; // compute potential derivative to xi if (xi_total < xi0) { nbr[i].duxi = 0.0; edelta = 0.0; order[i][1] = 0.0; } else if (xi_total > xi1) { nbr[i].duxi = 0.0; edelta = Vxi; order[i][1] = 1.0; } else { omega = MY_PI2*(xi_total-xi0) / (xi1-xi0); nbr[i].duxi = MY_PI*Vxi*sin(2.0*omega) / (2.0*(xi1-xi0)); edelta = Vxi*(1 - cos(2.0*omega)) / 2.0; order[i][1] = omega / MY_PI2; } added_energy += edelta; } if (maxcount) delete [] sort; // communicate to acquire nbr data for ghost atoms comm->forward_comm_fix(this); // compute grain boundary force on each owned atom // skip atoms not in group for (ii = 0; ii < inum; ii++) { i = ilist[ii]; if (!(mask[i] & groupbit)) continue; n = nbr[i].n; duxi = nbr[i].duxi; for (j = 0; j < n; j++) { dxiptr = &nbr[i].dxi[j][0]; if (use_xismooth) { xismooth = nbr[i].xismooth[j]; f[i][0] += duxi * dxiptr[0] * xismooth; f[i][1] += duxi * dxiptr[1] * xismooth; f[i][2] += duxi * dxiptr[2] * xismooth; } else { f[i][0] += duxi * dxiptr[0]; f[i][1] += duxi * dxiptr[1]; f[i][2] += duxi * dxiptr[2]; } // m = local index of neighbor // id_self = ID for atom I in atom M's neighbor list // if M is local atom, id_self will be local ID of atom I // if M is ghost atom, id_self will be global ID of atom I m = nbr[i].id[j]; if (m < nlocal) id_self = i; else id_self = tag[i]; found_myself = false; nn = nbr[m].n; for (k = 0; k < nn; k++) { if (id_self == nbr[m].id[k]) { if (found_myself) error->one(FLERR,"Fix orient/fcc found self twice"); found_myself = true; duxi_other = nbr[m].duxi; dxiptr = &nbr[m].dxi[k][0]; if (use_xismooth) { xismooth = nbr[m].xismooth[k]; f[i][0] -= duxi_other * dxiptr[0] * xismooth; f[i][1] -= duxi_other * dxiptr[1] * xismooth; f[i][2] -= duxi_other * dxiptr[2] * xismooth; } else { f[i][0] -= duxi_other * dxiptr[0]; f[i][1] -= duxi_other * dxiptr[1]; f[i][2] -= duxi_other * dxiptr[2]; } } } } } // print statistics every nstats timesteps if (nstats && update->ntimestep % nstats == 0) { int total; MPI_Allreduce(&count,&total,1,MPI_INT,MPI_SUM,world); double ave = total/atom->natoms; int min,max; MPI_Allreduce(&mincount,&min,1,MPI_INT,MPI_MIN,world); MPI_Allreduce(&maxcount,&max,1,MPI_INT,MPI_MAX,world); if (me == 0) { if (screen) fprintf(screen, "orient step " BIGINT_FORMAT ": " BIGINT_FORMAT " atoms have %d neighbors\n", update->ntimestep,atom->natoms,total); if (logfile) fprintf(logfile, "orient step " BIGINT_FORMAT ": " BIGINT_FORMAT " atoms have %d neighbors\n", update->ntimestep,atom->natoms,total); if (screen) fprintf(screen," neighs: min = %d, max = %d, ave = %g\n", min,max,ave); if (logfile) fprintf(logfile," neighs: min = %d, max = %d, ave = %g\n", min,max,ave); } } } /* ---------------------------------------------------------------------- */ void FixOrientFCC::post_force_respa(int vflag, int ilevel, int iloop) { if (ilevel == nlevels_respa-1) post_force(vflag); } /* ---------------------------------------------------------------------- */ double FixOrientFCC::compute_scalar() { double added_energy_total; MPI_Allreduce(&added_energy,&added_energy_total,1,MPI_DOUBLE,MPI_SUM,world); return added_energy_total; } /* ---------------------------------------------------------------------- */ int FixOrientFCC::pack_comm(int n, int *list, double *buf, int pbc_flag, int *pbc) { int i,j,k,id,num; int *tag = atom->tag; int nlocal = atom->nlocal; int m = 0; for (i = 0; i < n; i++) { k = list[i]; num = nbr[k].n; buf[m++] = num; buf[m++] = nbr[k].duxi; for (j = 0; j < num; j++) { if (use_xismooth) buf[m++] = nbr[m].xismooth[j]; buf[m++] = nbr[k].dxi[j][0]; buf[m++] = nbr[k].dxi[j][1]; buf[m++] = nbr[k].dxi[j][2]; // id stored in buf needs to be global ID // if k is a local atom, it stores local IDs, so convert to global // if k is a ghost atom (already comm'd), its IDs are already global id = nbr[k].id[j]; if (k < nlocal) id = tag[id]; buf[m++] = id; } m += (12-num) * 3; if (use_xismooth) m += 12-num; } if (use_xismooth) return 62; return 50; } /* ---------------------------------------------------------------------- */ void FixOrientFCC::unpack_comm(int n, int first, double *buf) { int i,j,num; int last = first + n; int m = 0; for (i = first; i < last; i++) { nbr[i].n = num = static_cast (buf[m++]); nbr[i].duxi = buf[m++]; for (j = 0; j < num; j++) { if (use_xismooth) nbr[i].xismooth[j] = buf[m++]; nbr[i].dxi[j][0] = buf[m++]; nbr[i].dxi[j][1] = buf[m++]; nbr[i].dxi[j][2] = buf[m++]; nbr[i].id[j] = static_cast (buf[m++]); } m += (12-num) * 3; if (use_xismooth) m += 12-num; } } /* ---------------------------------------------------------------------- */ void FixOrientFCC::find_best_ref(double *displs, int which_crystal, double &xi_sq, double *dxi) { int i; double dot,tmp; double best_dot = -1.0; // best is biggest (smallest angle) int best_i = -1; int best_sign = 0; for (i = 0; i < half_fcc_nn; i++) { dot = displs[0] * half_xi_chi_vec[which_crystal][i][0] + displs[1] * half_xi_chi_vec[which_crystal][i][1] + displs[2] * half_xi_chi_vec[which_crystal][i][2]; if (fabs(dot) > best_dot) { best_dot = fabs(dot); best_i = i; if (dot < 0.0) best_sign = -1; else best_sign = 1; } } xi_sq = 0.0; for (i = 0; i < 3; i++) { tmp = displs[i] - best_sign * half_xi_chi_vec[which_crystal][best_i][i]; xi_sq += tmp*tmp; } if (xi_sq > 0.0) { double xi = sqrt(xi_sq); for (i = 0; i < 3; i++) dxi[i] = (best_sign * half_xi_chi_vec[which_crystal][best_i][i] - displs[i]) / xi; } else dxi[0] = dxi[1] = dxi[2] = 0.0; } /* ---------------------------------------------------------------------- compare two neighbors I and J in sort data structure called via qsort in post_force() method is a static method so can't access sort data structure directly return -1 if I < J, 0 if I = J, 1 if I > J do comparison based on rsq distance ------------------------------------------------------------------------- */ int FixOrientFCC::compare(const void *pi, const void *pj) { FixOrientFCC::Sort *ineigh = (FixOrientFCC::Sort *) pi; FixOrientFCC::Sort *jneigh = (FixOrientFCC::Sort *) pj; if (ineigh->rsq < jneigh->rsq) return -1; else if (ineigh->rsq > jneigh->rsq) return 1; return 0; } /* ---------------------------------------------------------------------- memory usage of local atom-based arrays ------------------------------------------------------------------------- */ double FixOrientFCC::memory_usage() { double bytes = nmax * sizeof(Nbr); bytes += 2*nmax * sizeof(double); return bytes; } diff --git a/src/PERI/atom_vec_peri.cpp b/src/PERI/atom_vec_peri.cpp index b4dbb37b9..9d8c6a74d 100644 --- a/src/PERI/atom_vec_peri.cpp +++ b/src/PERI/atom_vec_peri.cpp @@ -1,911 +1,925 @@ /* ---------------------------------------------------------------------- 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: Mike Parks (SNL) ------------------------------------------------------------------------- */ #include "float.h" #include "stdlib.h" #include "atom_vec_peri.h" #include "atom.h" #include "comm.h" #include "domain.h" #include "modify.h" #include "fix.h" +#include "citeme.h" #include "memory.h" #include "error.h" using namespace LAMMPS_NS; #define DELTA 10000 +static const char cite_peri_package[] = + "PERI package for Peridynamics:\n\n" + "@Article{Parks08,\n" + " author = {M. L. Parks, R. B. Lehoucq, S. J. Plimpton, S. A. Silling},\n" + " title = {Implementing peridynamics within a molecular dynamics code},\n" + " journal = {Comp.~Phys.~Comm.},\n" + " year = 2008,\n" + " volume = 179,\n" + " pages = {777--783}\n" + "}\n\n"; + /* ---------------------------------------------------------------------- */ AtomVecPeri::AtomVecPeri(LAMMPS *lmp) : AtomVec(lmp) { + if (lmp->citeme) lmp->citeme->add(cite_peri_package); + molecular = 0; comm_x_only = 0; comm_f_only = 1; size_forward = 4; size_reverse = 3; size_border = 11; size_velocity = 3; size_data_atom = 7; size_data_vel = 4; xcol_data = 5; atom->peri_flag = 1; atom->vfrac_flag = atom->rmass_flag = 1; } /* ---------------------------------------------------------------------- grow atom arrays n = 0 grows arrays by DELTA n > 0 allocates arrays to size n ------------------------------------------------------------------------- */ void AtomVecPeri::grow(int n) { if (n == 0) nmax += DELTA; else nmax = n; atom->nmax = nmax; if (nmax < 0 || nmax > MAXSMALLINT) error->one(FLERR,"Per-processor system is too big"); tag = memory->grow(atom->tag,nmax,"atom:tag"); type = memory->grow(atom->type,nmax,"atom:type"); mask = memory->grow(atom->mask,nmax,"atom:mask"); image = memory->grow(atom->image,nmax,"atom:image"); x = memory->grow(atom->x,nmax,3,"atom:x"); v = memory->grow(atom->v,nmax,3,"atom:v"); f = memory->grow(atom->f,nmax*comm->nthreads,3,"atom:f"); vfrac = memory->grow(atom->vfrac,nmax,"atom:vfrac"); rmass = memory->grow(atom->rmass,nmax,"atom:rmass"); s0 = memory->grow(atom->s0,nmax,"atom:s0"); x0 = memory->grow(atom->x0,nmax,3,"atom:x0"); if (atom->nextra_grow) for (int iextra = 0; iextra < atom->nextra_grow; iextra++) modify->fix[atom->extra_grow[iextra]]->grow_arrays(nmax); } /* ---------------------------------------------------------------------- reset local array ptrs ------------------------------------------------------------------------- */ void AtomVecPeri::grow_reset() { tag = atom->tag; type = atom->type; mask = atom->mask; image = atom->image; x = atom->x; v = atom->v; f = atom->f; vfrac = atom->vfrac; rmass = atom->rmass; s0 = atom->s0; x0 = atom->x0; } /* ---------------------------------------------------------------------- copy atom I info to atom J ------------------------------------------------------------------------- */ void AtomVecPeri::copy(int i, int j, int delflag) { tag[j] = tag[i]; type[j] = type[i]; mask[j] = mask[i]; image[j] = image[i]; x[j][0] = x[i][0]; x[j][1] = x[i][1]; x[j][2] = x[i][2]; v[j][0] = v[i][0]; v[j][1] = v[i][1]; v[j][2] = v[i][2]; vfrac[j] = vfrac[i]; rmass[j] = rmass[i]; s0[j] = s0[i]; x0[j][0] = x0[i][0]; x0[j][1] = x0[i][1]; x0[j][2] = x0[i][2]; if (atom->nextra_grow) for (int iextra = 0; iextra < atom->nextra_grow; iextra++) modify->fix[atom->extra_grow[iextra]]->copy_arrays(i,j,delflag); } /* ---------------------------------------------------------------------- */ int AtomVecPeri::pack_comm(int n, int *list, double *buf, int pbc_flag, int *pbc) { int i,j,m; double dx,dy,dz; m = 0; if (pbc_flag == 0) { for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0]; buf[m++] = x[j][1]; buf[m++] = x[j][2]; buf[m++] = s0[j]; } } else { if (domain->triclinic == 0) { dx = pbc[0]*domain->xprd; dy = pbc[1]*domain->yprd; dz = pbc[2]*domain->zprd; } else { dx = pbc[0]*domain->xprd + pbc[5]*domain->xy + pbc[4]*domain->xz; dy = pbc[1]*domain->yprd + pbc[3]*domain->yz; dz = pbc[2]*domain->zprd; } for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0] + dx; buf[m++] = x[j][1] + dy; buf[m++] = x[j][2] + dz; buf[m++] = s0[j]; } } return m; } /* ---------------------------------------------------------------------- */ int AtomVecPeri::pack_comm_vel(int n, int *list, double *buf, int pbc_flag, int *pbc) { int i,j,m; double dx,dy,dz,dvx,dvy,dvz; m = 0; if (pbc_flag == 0) { for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0]; buf[m++] = x[j][1]; buf[m++] = x[j][2]; buf[m++] = s0[j]; buf[m++] = v[j][0]; buf[m++] = v[j][1]; buf[m++] = v[j][2]; } } else { if (domain->triclinic == 0) { dx = pbc[0]*domain->xprd; dy = pbc[1]*domain->yprd; dz = pbc[2]*domain->zprd; } else { dx = pbc[0]*domain->xprd + pbc[5]*domain->xy + pbc[4]*domain->xz; dy = pbc[1]*domain->yprd + pbc[3]*domain->yz; dz = pbc[2]*domain->zprd; } if (!deform_vremap) { for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0] + dx; buf[m++] = x[j][1] + dy; buf[m++] = x[j][2] + dz; buf[m++] = s0[j]; buf[m++] = v[j][0]; buf[m++] = v[j][1]; buf[m++] = v[j][2]; } } else { dvx = pbc[0]*h_rate[0] + pbc[5]*h_rate[5] + pbc[4]*h_rate[4]; dvy = pbc[1]*h_rate[1] + pbc[3]*h_rate[3]; dvz = pbc[2]*h_rate[2]; for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0] + dx; buf[m++] = x[j][1] + dy; buf[m++] = x[j][2] + dz; buf[m++] = s0[j]; if (mask[i] & deform_groupbit) { buf[m++] = v[j][0] + dvx; buf[m++] = v[j][1] + dvy; buf[m++] = v[j][2] + dvz; } else { buf[m++] = v[j][0]; buf[m++] = v[j][1]; buf[m++] = v[j][2]; } } } } return m; } /* ---------------------------------------------------------------------- */ int AtomVecPeri::pack_comm_hybrid(int n, int *list, double *buf) { int i,j,m; m = 0; for (i = 0; i < n; i++) { j = list[i]; buf[m++] = s0[j]; } return m; } /* ---------------------------------------------------------------------- */ void AtomVecPeri::unpack_comm(int n, int first, double *buf) { int i,m,last; m = 0; last = first + n; for (i = first; i < last; i++) { x[i][0] = buf[m++]; x[i][1] = buf[m++]; x[i][2] = buf[m++]; s0[i] = buf[m++]; } } /* ---------------------------------------------------------------------- */ void AtomVecPeri::unpack_comm_vel(int n, int first, double *buf) { int i,m,last; m = 0; last = first + n; for (i = first; i < last; i++) { x[i][0] = buf[m++]; x[i][1] = buf[m++]; x[i][2] = buf[m++]; s0[i] = buf[m++]; v[i][0] = buf[m++]; v[i][1] = buf[m++]; v[i][2] = buf[m++]; } } /* ---------------------------------------------------------------------- */ int AtomVecPeri::unpack_comm_hybrid(int n, int first, double *buf) { int i,m,last; m = 0; last = first + n; for (i = first; i < last; i++) s0[i] = buf[m++]; return m; } /* ---------------------------------------------------------------------- */ int AtomVecPeri::pack_reverse(int n, int first, double *buf) { int i,m,last; m = 0; last = first + n; for (i = first; i < last; i++) { buf[m++] = f[i][0]; buf[m++] = f[i][1]; buf[m++] = f[i][2]; } return m; } /* ---------------------------------------------------------------------- */ void AtomVecPeri::unpack_reverse(int n, int *list, double *buf) { int i,j,m; m = 0; for (i = 0; i < n; i++) { j = list[i]; f[j][0] += buf[m++]; f[j][1] += buf[m++]; f[j][2] += buf[m++]; } } /* ---------------------------------------------------------------------- */ int AtomVecPeri::pack_border(int n, int *list, double *buf, int pbc_flag, int *pbc) { int i,j,m; double dx,dy,dz; m = 0; if (pbc_flag == 0) { for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0]; buf[m++] = x[j][1]; buf[m++] = x[j][2]; buf[m++] = tag[j]; buf[m++] = type[j]; buf[m++] = mask[j]; buf[m++] = vfrac[j]; buf[m++] = s0[j]; buf[m++] = x0[j][0]; buf[m++] = x0[j][1]; buf[m++] = x0[j][2]; } } else { if (domain->triclinic == 0) { dx = pbc[0]*domain->xprd; dy = pbc[1]*domain->yprd; dz = pbc[2]*domain->zprd; } else { dx = pbc[0]; dy = pbc[1]; dz = pbc[2]; } for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0] + dx; buf[m++] = x[j][1] + dy; buf[m++] = x[j][2] + dz; buf[m++] = tag[j]; buf[m++] = type[j]; buf[m++] = mask[j]; buf[m++] = vfrac[j]; buf[m++] = s0[j]; buf[m++] = x0[j][0]; buf[m++] = x0[j][1]; buf[m++] = x0[j][2]; } } if (atom->nextra_border) for (int iextra = 0; iextra < atom->nextra_border; iextra++) m += modify->fix[atom->extra_border[iextra]]->pack_border(n,list,&buf[m]); return m; } /* ---------------------------------------------------------------------- */ int AtomVecPeri::pack_border_vel(int n, int *list, double *buf, int pbc_flag, int *pbc) { int i,j,m; double dx,dy,dz,dvx,dvy,dvz; m = 0; if (pbc_flag == 0) { for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0]; buf[m++] = x[j][1]; buf[m++] = x[j][2]; buf[m++] = tag[j]; buf[m++] = type[j]; buf[m++] = mask[j]; buf[m++] = vfrac[j]; buf[m++] = s0[j]; buf[m++] = x0[j][0]; buf[m++] = x0[j][1]; buf[m++] = x0[j][2]; buf[m++] = v[j][0]; buf[m++] = v[j][1]; buf[m++] = v[j][2]; } } else { if (domain->triclinic == 0) { dx = pbc[0]*domain->xprd; dy = pbc[1]*domain->yprd; dz = pbc[2]*domain->zprd; } else { dx = pbc[0]; dy = pbc[1]; dz = pbc[2]; } if (!deform_vremap) { for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0] + dx; buf[m++] = x[j][1] + dy; buf[m++] = x[j][2] + dz; buf[m++] = tag[j]; buf[m++] = type[j]; buf[m++] = mask[j]; buf[m++] = vfrac[j]; buf[m++] = s0[j]; buf[m++] = x0[j][0]; buf[m++] = x0[j][1]; buf[m++] = x0[j][2]; buf[m++] = v[j][0]; buf[m++] = v[j][1]; buf[m++] = v[j][2]; } } else { dvx = pbc[0]*h_rate[0] + pbc[5]*h_rate[5] + pbc[4]*h_rate[4]; dvy = pbc[1]*h_rate[1] + pbc[3]*h_rate[3]; dvz = pbc[2]*h_rate[2]; for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0] + dx; buf[m++] = x[j][1] + dy; buf[m++] = x[j][2] + dz; buf[m++] = tag[j]; buf[m++] = type[j]; buf[m++] = mask[j]; buf[m++] = vfrac[j]; buf[m++] = s0[j]; buf[m++] = x0[j][0]; buf[m++] = x0[j][1]; buf[m++] = x0[j][2]; if (mask[i] & deform_groupbit) { buf[m++] = v[j][0] + dvx; buf[m++] = v[j][1] + dvy; buf[m++] = v[j][2] + dvz; } else { buf[m++] = v[j][0]; buf[m++] = v[j][1]; buf[m++] = v[j][2]; } } } } if (atom->nextra_border) for (int iextra = 0; iextra < atom->nextra_border; iextra++) m += modify->fix[atom->extra_border[iextra]]->pack_border(n,list,&buf[m]); return m; } /* ---------------------------------------------------------------------- */ int AtomVecPeri::pack_border_hybrid(int n, int *list, double *buf) { int i,j,m; m = 0; for (i = 0; i < n; i++) { j = list[i]; buf[m++] = vfrac[j]; buf[m++] = s0[j]; buf[m++] = x0[j][0]; buf[m++] = x0[j][1]; buf[m++] = x0[j][2]; } return m; } /* ---------------------------------------------------------------------- */ void AtomVecPeri::unpack_border(int n, int first, double *buf) { int i,m,last; m = 0; last = first + n; for (i = first; i < last; i++) { if (i == nmax) grow(0); x[i][0] = buf[m++]; x[i][1] = buf[m++]; x[i][2] = buf[m++]; tag[i] = static_cast (buf[m++]); type[i] = static_cast (buf[m++]); mask[i] = static_cast (buf[m++]); vfrac[i] = buf[m++]; s0[i] = buf[m++]; x0[i][0] = buf[m++]; x0[i][1] = buf[m++]; x0[i][2] = buf[m++]; } if (atom->nextra_border) for (int iextra = 0; iextra < atom->nextra_border; iextra++) m += modify->fix[atom->extra_border[iextra]]-> unpack_border(n,first,&buf[m]); } /* ---------------------------------------------------------------------- */ void AtomVecPeri::unpack_border_vel(int n, int first, double *buf) { int i,m,last; m = 0; last = first + n; for (i = first; i < last; i++) { if (i == nmax) grow(0); x[i][0] = buf[m++]; x[i][1] = buf[m++]; x[i][2] = buf[m++]; tag[i] = static_cast (buf[m++]); type[i] = static_cast (buf[m++]); mask[i] = static_cast (buf[m++]); vfrac[i] = buf[m++]; s0[i] = buf[m++]; x0[i][0] = buf[m++]; x0[i][1] = buf[m++]; x0[i][2] = buf[m++]; v[i][0] = buf[m++]; v[i][1] = buf[m++]; v[i][2] = buf[m++]; } if (atom->nextra_border) for (int iextra = 0; iextra < atom->nextra_border; iextra++) m += modify->fix[atom->extra_border[iextra]]-> unpack_border(n,first,&buf[m]); } /* ---------------------------------------------------------------------- */ int AtomVecPeri::unpack_border_hybrid(int n, int first, double *buf) { int i,m,last; m = 0; last = first + n; for (i = first; i < last; i++) { vfrac[i] = buf[m++]; s0[i] = buf[m++]; x0[i][0] = buf[m++]; x0[i][1] = buf[m++]; x0[i][2] = buf[m++]; } return m; } /* ---------------------------------------------------------------------- pack data for atom I for sending to another proc xyz must be 1st 3 values, so comm::exchange() can test on them ------------------------------------------------------------------------- */ int AtomVecPeri::pack_exchange(int i, double *buf) { int m = 1; buf[m++] = x[i][0]; buf[m++] = x[i][1]; buf[m++] = x[i][2]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; buf[m++] = v[i][2]; buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = vfrac[i]; buf[m++] = rmass[i]; buf[m++] = s0[i]; buf[m++] = x0[i][0]; buf[m++] = x0[i][1]; buf[m++] = x0[i][2]; if (atom->nextra_grow) for (int iextra = 0; iextra < atom->nextra_grow; iextra++) m += modify->fix[atom->extra_grow[iextra]]->pack_exchange(i,&buf[m]); buf[0] = m; return m; } /* ---------------------------------------------------------------------- */ int AtomVecPeri::unpack_exchange(double *buf) { int nlocal = atom->nlocal; if (nlocal == nmax) grow(0); int m = 1; x[nlocal][0] = buf[m++]; x[nlocal][1] = buf[m++]; x[nlocal][2] = buf[m++]; v[nlocal][0] = buf[m++]; v[nlocal][1] = buf[m++]; v[nlocal][2] = buf[m++]; tag[nlocal] = static_cast (buf[m++]); type[nlocal] = static_cast (buf[m++]); mask[nlocal] = static_cast (buf[m++]); image[nlocal] = *((tagint *) &buf[m++]); vfrac[nlocal] = buf[m++]; rmass[nlocal] = buf[m++]; s0[nlocal] = buf[m++]; x0[nlocal][0] = buf[m++]; x0[nlocal][1] = buf[m++]; x0[nlocal][2] = buf[m++]; if (atom->nextra_grow) for (int iextra = 0; iextra < atom->nextra_grow; iextra++) m += modify->fix[atom->extra_grow[iextra]]-> unpack_exchange(nlocal,&buf[m]); atom->nlocal++; return m; } /* ---------------------------------------------------------------------- size of restart data for all atoms owned by this proc include extra data stored by fixes ------------------------------------------------------------------------- */ int AtomVecPeri::size_restart() { int i; int nlocal = atom->nlocal; int n = 17 * nlocal; if (atom->nextra_restart) for (int iextra = 0; iextra < atom->nextra_restart; iextra++) for (i = 0; i < nlocal; i++) n += modify->fix[atom->extra_restart[iextra]]->size_restart(i); return n; } /* ---------------------------------------------------------------------- pack atom I's data for restart file including extra quantities xyz must be 1st 3 values, so that read_restart can test on them molecular types may be negative, but write as positive ------------------------------------------------------------------------- */ int AtomVecPeri::pack_restart(int i, double *buf) { int m = 1; buf[m++] = x[i][0]; buf[m++] = x[i][1]; buf[m++] = x[i][2]; buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; buf[m++] = v[i][2]; buf[m++] = vfrac[i]; buf[m++] = rmass[i]; buf[m++] = s0[i]; buf[m++] = x0[i][0]; buf[m++] = x0[i][1]; buf[m++] = x0[i][2]; if (atom->nextra_restart) for (int iextra = 0; iextra < atom->nextra_restart; iextra++) m += modify->fix[atom->extra_restart[iextra]]->pack_restart(i,&buf[m]); buf[0] = m; return m; } /* ---------------------------------------------------------------------- unpack data for one atom from restart file including extra quantities ------------------------------------------------------------------------- */ int AtomVecPeri::unpack_restart(double *buf) { int nlocal = atom->nlocal; if (nlocal == nmax) { grow(0); if (atom->nextra_store) memory->grow(atom->extra,nmax,atom->nextra_store,"atom:extra"); } int m = 1; x[nlocal][0] = buf[m++]; x[nlocal][1] = buf[m++]; x[nlocal][2] = buf[m++]; tag[nlocal] = static_cast (buf[m++]); type[nlocal] = static_cast (buf[m++]); mask[nlocal] = static_cast (buf[m++]); image[nlocal] = *((tagint *) &buf[m++]); v[nlocal][0] = buf[m++]; v[nlocal][1] = buf[m++]; v[nlocal][2] = buf[m++]; vfrac[nlocal] = buf[m++]; rmass[nlocal] = buf[m++]; s0[nlocal] = buf[m++]; x0[nlocal][0] = buf[m++]; x0[nlocal][1] = buf[m++]; x0[nlocal][2] = buf[m++]; double **extra = atom->extra; if (atom->nextra_store) { int size = static_cast (buf[0]) - m; for (int i = 0; i < size; i++) extra[nlocal][i] = buf[m++]; } atom->nlocal++; return m; } /* ---------------------------------------------------------------------- create one atom of itype at coord set other values to defaults ------------------------------------------------------------------------- */ void AtomVecPeri::create_atom(int itype, double *coord) { int nlocal = atom->nlocal; if (nlocal == nmax) grow(0); tag[nlocal] = 0; type[nlocal] = itype; x[nlocal][0] = coord[0]; x[nlocal][1] = coord[1]; x[nlocal][2] = coord[2]; mask[nlocal] = 1; image[nlocal] = ((tagint) IMGMAX << IMG2BITS) | ((tagint) IMGMAX << IMGBITS) | IMGMAX; v[nlocal][0] = 0.0; v[nlocal][1] = 0.0; v[nlocal][2] = 0.0; vfrac[nlocal] = 1.0; rmass[nlocal] = 1.0; s0[nlocal] = DBL_MAX; x0[nlocal][0] = coord[0]; x0[nlocal][1] = coord[1]; x0[nlocal][2] = coord[2]; atom->nlocal++; } /* ---------------------------------------------------------------------- unpack one line from Atoms section of data file initialize other atom quantities ------------------------------------------------------------------------- */ void AtomVecPeri::data_atom(double *coord, tagint imagetmp, char **values) { int nlocal = atom->nlocal; if (nlocal == nmax) grow(0); tag[nlocal] = atoi(values[0]); if (tag[nlocal] <= 0) error->one(FLERR,"Invalid atom ID in Atoms section of data file"); type[nlocal] = atoi(values[1]); if (type[nlocal] <= 0 || type[nlocal] > atom->ntypes) error->one(FLERR,"Invalid atom type in Atoms section of data file"); vfrac[nlocal] = atof(values[2]); rmass[nlocal] = atof(values[3]); if (rmass[nlocal] <= 0.0) error->one(FLERR,"Invalid mass value"); x[nlocal][0] = coord[0]; x[nlocal][1] = coord[1]; x[nlocal][2] = coord[2]; image[nlocal] = imagetmp; mask[nlocal] = 1; v[nlocal][0] = 0.0; v[nlocal][1] = 0.0; v[nlocal][2] = 0.0; s0[nlocal] = DBL_MAX; x0[nlocal][0] = coord[0]; x0[nlocal][1] = coord[1]; x0[nlocal][2] = coord[2]; atom->nlocal++; } /* ---------------------------------------------------------------------- unpack hybrid quantities from one line in Atoms section of data file initialize other atom quantities for this sub-style ------------------------------------------------------------------------- */ int AtomVecPeri::data_atom_hybrid(int nlocal, char **values) { vfrac[nlocal] = atof(values[0]); rmass[nlocal] = atof(values[1]); if (rmass[nlocal] <= 0.0) error->one(FLERR,"Invalid mass value"); s0[nlocal] = DBL_MAX; x0[nlocal][0] = x[nlocal][0]; x0[nlocal][1] = x[nlocal][1]; x0[nlocal][2] = x[nlocal][2]; return 2; } /* ---------------------------------------------------------------------- pack atom info for data file including 3 image flags ------------------------------------------------------------------------- */ void AtomVecPeri::pack_data(double **buf) { int nlocal = atom->nlocal; for (int i = 0; i < nlocal; i++) { buf[i][0] = tag[i]; buf[i][1] = type[i]; buf[i][2] = vfrac[i]; buf[i][3] = rmass[i]; buf[i][4] = x[i][0]; buf[i][5] = x[i][1]; buf[i][6] = x[i][2]; buf[i][7] = (image[i] & IMGMASK) - IMGMAX; buf[i][8] = (image[i] >> IMGBITS & IMGMASK) - IMGMAX; buf[i][9] = (image[i] >> IMG2BITS) - IMGMAX; } } /* ---------------------------------------------------------------------- pack hybrid atom info for data file ------------------------------------------------------------------------- */ int AtomVecPeri::pack_data_hybrid(int i, double *buf) { buf[0] = vfrac[i]; buf[1] = rmass[i]; return 2; } /* ---------------------------------------------------------------------- write atom info to data file including 3 image flags ------------------------------------------------------------------------- */ void AtomVecPeri::write_data(FILE *fp, int n, double **buf) { for (int i = 0; i < n; i++) fprintf(fp,"%d %d %-1.16e %-1.16e %-1.16e %-1.16e %-1.16e %d %d %d\n", (int) buf[i][0],(int) buf[i][1], buf[i][2],buf[i][3], buf[i][4],buf[i][5],buf[i][6], (int) buf[i][7],(int) buf[i][8],(int) buf[i][9]); } /* ---------------------------------------------------------------------- write hybrid atom info to data file ------------------------------------------------------------------------- */ int AtomVecPeri::write_data_hybrid(FILE *fp, double *buf) { fprintf(fp," %-1.16e %-1.16e",buf[0],buf[1]); return 2; } /* ---------------------------------------------------------------------- return # of bytes of allocated memory ------------------------------------------------------------------------- */ bigint AtomVecPeri::memory_usage() { bigint bytes = 0; if (atom->memcheck("tag")) bytes += memory->usage(tag,nmax); if (atom->memcheck("type")) bytes += memory->usage(type,nmax); if (atom->memcheck("mask")) bytes += memory->usage(mask,nmax); if (atom->memcheck("image")) bytes += memory->usage(image,nmax); if (atom->memcheck("x")) bytes += memory->usage(x,nmax,3); if (atom->memcheck("v")) bytes += memory->usage(v,nmax,3); if (atom->memcheck("f")) bytes += memory->usage(f,nmax*comm->nthreads,3); if (atom->memcheck("vfrac")) bytes += memory->usage(vfrac,nmax); if (atom->memcheck("rmass")) bytes += memory->usage(rmass,nmax); if (atom->memcheck("s0")) bytes += memory->usage(s0,nmax); if (atom->memcheck("x0")) bytes += memory->usage(x0,nmax,3); return bytes; } diff --git a/src/POEMS/fix_poems.cpp b/src/POEMS/fix_poems.cpp index 93296636b..599680f3d 100644 --- a/src/POEMS/fix_poems.cpp +++ b/src/POEMS/fix_poems.cpp @@ -1,1592 +1,1606 @@ /* ---------------------------------------------------------------------- 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. ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- FixPOEMS is a LAMMPS interface to the POEMS coupled multi-body simulator POEMS authors: Rudranarayan Mukherjee (mukher@rpi.edu) Kurt Anderson (anderk5@rpi.edu) ------------------------------------------------------------------------- */ #include "mpi.h" #include "math.h" #include "stdio.h" #include "string.h" #include "stdlib.h" #include "workspace.h" #include "fix_poems.h" #include "atom.h" #include "domain.h" #include "update.h" #include "respa.h" #include "modify.h" #include "force.h" #include "output.h" #include "group.h" #include "comm.h" +#include "citeme.h" #include "memory.h" #include "error.h" using namespace LAMMPS_NS; using namespace FixConst; #define MAXBODY 2 // currently 2 since only linear chains allowed #define DELTA 128 #define TOLERANCE 1.0e-6 #define EPSILON 1.0e-7 #define MAXJACOBI 50 +static const char cite_fix_poems[] = + "fix poems command:\n\n" + "@Article{Mukherjee08,\n" + " author = {R. M. Mukherjee, P. S. Crozier, S. J. Plimpton, K. S. Anderson},\n" + " title = {Substructured molecular dynamics using multibody dynamics algorithms},\n" + " journal = {Intl.~J.~Non-linear Mechanics},\n" + " year = 2008,\n" + " volume = 43,\n" + " pages = {1045--1055}\n" + "}\n\n"; + /* ---------------------------------------------------------------------- define rigid bodies and joints, initiate POEMS ------------------------------------------------------------------------- */ FixPOEMS::FixPOEMS(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { + if (lmp->citeme) lmp->citeme->add(cite_fix_poems); + int i,j,ibody; time_integrate = 1; rigid_flag = 1; virial_flag = 1; MPI_Comm_rank(world,&me); // perform initial allocation of atom-based arrays // register with atom class natom2body = NULL; atom2body = NULL; displace = NULL; grow_arrays(atom->nmax); atom->add_callback(0); // initialize each atom to belong to no rigid bodies int nlocal = atom->nlocal; for (int i = 0; i < nlocal; i++) natom2body[i] = 0; // create an atom map if one doesn't exist already // readfile() and jointbuild() use global atom IDs int mapflag = 0; if (atom->map_style == 0) { mapflag = 1; atom->map_style = 1; atom->map_init(); atom->map_set(); } // parse command-line args // set natom2body, atom2body for all atoms and nbody = # of rigid bodies // atoms must also be in fix group to be in a body if (narg < 4) error->all(FLERR,"Illegal fix poems command"); // group = arg has list of groups if (strcmp(arg[3],"group") == 0) { nbody = narg-4; if (nbody <= 0) error->all(FLERR,"Illegal fix poems command"); int *igroups = new int[nbody]; for (ibody = 0; ibody < nbody; ibody++) { igroups[ibody] = group->find(arg[ibody+4]); if (igroups[ibody] == -1) error->all(FLERR,"Could not find fix poems group ID"); } int *mask = atom->mask; for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) for (ibody = 0; ibody < nbody; ibody++) if (mask[i] & group->bitmask[igroups[ibody]]) { if (natom2body[i] < MAXBODY) atom2body[i][natom2body[i]] = ibody; natom2body[i]++; } } delete [] igroups; // file = read bodies from file // file read doesn't pay attention to fix group, // so after read, reset natom2body = 0 if atom is not in fix group } else if (strcmp(arg[3],"file") == 0) { readfile(arg[4]); int *mask = atom->mask; for (int i = 0; i < nlocal; i++) if (!(mask[i] & groupbit)) natom2body[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 atom2body[] values for each atom } else if (strcmp(arg[3],"molecule") == 0) { if (narg != 4) error->all(FLERR,"Illegal fix poems command"); if (atom->molecular == 0) error->all(FLERR,"Must use a molecular atom style with fix poems molecule"); int *mask = atom->mask; int *molecule = atom->molecule; int nlocal = atom->nlocal; int 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 + 1; int *ncount = new int[maxmol]; for (i = 0; i < maxmol; i++) ncount[i] = 0; for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) ncount[molecule[i]]++; int *nall = new int[maxmol]; MPI_Allreduce(ncount,nall,maxmol,MPI_INT,MPI_SUM,world); nbody = 0; for (i = 0; i < maxmol; i++) if (nall[i]) nall[i] = nbody++; else nall[i] = -1; for (i = 0; i < nlocal; i++) { natom2body[i] = 0; if (mask[i] & groupbit) { natom2body[i] = 1; atom2body[i][0] = nall[molecule[i]]; } } delete [] ncount; delete [] nall; } else error->all(FLERR,"Illegal fix poems command"); // error if no bodies // error if any atom in too many bodies if (nbody == 0) error->all(FLERR,"No rigid bodies defined"); int flag = 0; for (int i = 0; i < nlocal; i++) if (natom2body[i] > MAXBODY) flag = 1; int flagall; MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_SUM,world); if (flagall) error->all(FLERR,"Atom in too many rigid bodies - boost MAXBODY"); // create all nbody-length arrays nrigid = new int[nbody]; masstotal = new double[nbody]; memory->create(xcm,nbody,3,"poems:xcm"); memory->create(vcm,nbody,3,"poems:vcm"); memory->create(fcm,nbody,3,"poems:fcm"); memory->create(inertia,nbody,3,"poems:inertia"); memory->create(ex_space,nbody,3,"poems:ex_space"); memory->create(ey_space,nbody,3,"poems:ey_space"); memory->create(ez_space,nbody,3,"poems:ez_space"); memory->create(angmom,nbody,3,"poems:angmom"); memory->create(omega,nbody,3,"poems:omega"); memory->create(torque,nbody,3,"poems:torque"); memory->create(sum,nbody,6,"poems:sum"); memory->create(all,nbody,6,"poems:all"); // nrigid[n] = # of atoms in Nth rigid body // double count joint atoms as being in multiple bodies // error if one or zero atoms int *ncount = new int[nbody]; for (ibody = 0; ibody < nbody; ibody++) ncount[ibody] = 0; for (i = 0; i < nlocal; i++) for (j = 0; j < natom2body[i]; j++) ncount[atom2body[i][j]]++; 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"); // build list of joint connections and check for cycles and trees jointbuild(); // delete temporary atom map if (mapflag) { atom->map_delete(); atom->map_style = 0; } // create POEMS instance poems = new Workspace; // print statistics int nsum = 0; for (ibody = 0; ibody < nbody; ibody++) nsum += nrigid[ibody]; nsum -= njoint; if (me == 0) { if (screen) fprintf(screen,"%d clusters, %d bodies, %d joints, %d atoms\n", ncluster,nbody,njoint,nsum); if (logfile) fprintf(logfile,"%d clusters, %d bodies, %d joints, %d atoms\n", ncluster,nbody,njoint,nsum); } } /* ---------------------------------------------------------------------- free all memory for rigid bodies, joints, and POEMS ------------------------------------------------------------------------- */ FixPOEMS::~FixPOEMS() { // if atom class still exists: // unregister this fix so atom class doesn't invoke it any more if (atom) atom->delete_callback(id,0); // delete locally stored arrays memory->destroy(natom2body); memory->destroy(atom2body); memory->destroy(displace); // delete nbody-length arrays delete [] nrigid; delete [] 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(sum); memory->destroy(all); // delete joint arrays memory->destroy(jointbody); memory->destroy(xjoint); delete [] freelist; // delete POEMS object delete poems; } /* ---------------------------------------------------------------------- */ int FixPOEMS::setmask() { int mask = 0; mask |= INITIAL_INTEGRATE; mask |= FINAL_INTEGRATE; mask |= PRE_NEIGHBOR; mask |= POST_FORCE; mask |= INITIAL_INTEGRATE_RESPA; mask |= FINAL_INTEGRATE_RESPA; mask |= POST_FORCE_RESPA; return mask; } /* ---------------------------------------------------------------------- */ void FixPOEMS::init() { int i,ibody; // warn if more than one POEMS fix int count = 0; for (int i = 0; i < modify->nfix; i++) if (strcmp(modify->fix[i]->style,"poems") == 0) count++; if (count > 1 && comm->me == 0) error->warning(FLERR,"More than one fix poems"); // 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,"poems") == 0) error->all(FLERR,"POEMS fix must come before NPT/NPH fix"); } // timestep info dtv = update->dt; dtf = 0.5 * update->dt * force->ftm2v; dthalf = 0.5 * update->dt; // rRESPA info if (strstr(update->integrate_style,"respa")) { step_respa = ((Respa *) update->integrate)->step; nlevels_respa = ((Respa *) update->integrate)->nlevels; } // compute masstotal & center-of-mass xcm of each rigid body // only count joint atoms in 1st body int *type = atom->type; tagint *image = atom->image; double *mass = atom->mass; double **x = atom->x; double **v = atom->v; int nlocal = atom->nlocal; double xprd = domain->xprd; double yprd = domain->yprd; double zprd = domain->zprd; int xbox,ybox,zbox; double massone; for (ibody = 0; ibody < nbody; ibody++) for (i = 0; i < 6; i++) sum[ibody][i] = 0.0; for (i = 0; i < nlocal; i++) { if (natom2body[i]) { ibody = atom2body[i][0]; xbox = (image[i] & IMGMASK) - IMGMAX; ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX; zbox = (image[i] >> IMG2BITS) - IMGMAX; massone = mass[type[i]]; sum[ibody][0] += (x[i][0] + xbox*xprd) * massone; sum[ibody][1] += (x[i][1] + ybox*yprd) * massone; sum[ibody][2] += (x[i][2] + zbox*zprd) * massone; sum[ibody][3] += massone; sum[ibody][4] += massone * (v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]); } } MPI_Allreduce(sum[0],all[0],6*nbody,MPI_DOUBLE,MPI_SUM,world); total_ke = 0.0; 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]; total_ke += 0.5 * all[ibody][4]; } // compute 6 moments of inertia of each body // only count joint atoms in 1st body // dx,dy,dz = coords relative to center-of-mass double dx,dy,dz; for (ibody = 0; ibody < nbody; ibody++) for (i = 0; i < 6; i++) sum[ibody][i] = 0.0; for (i = 0; i < nlocal; i++) { if (natom2body[i]) { ibody = atom2body[i][0]; xbox = (image[i] & IMGMASK) - IMGMAX; ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX; zbox = (image[i] >> IMG2BITS) - IMGMAX; dx = x[i][0] + xbox*xprd - xcm[ibody][0]; dy = x[i][1] + ybox*yprd - xcm[ibody][1]; dz = x[i][2] + zbox*zprd - xcm[ibody][2]; 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 * dx*dy; sum[ibody][4] -= massone * dy*dz; sum[ibody][5] -= massone * dx*dz; } } MPI_Allreduce(sum[0],all[0],6*nbody,MPI_DOUBLE,MPI_SUM,world); // inertia = 3 eigenvalues = principal moments of inertia // ex_space,ey_space,ez_space = 3 eigenvectors = principal axes of rigid body double **tensor,**evectors; memory->create(tensor,3,3,"fix_rigid:tensor"); memory->create(evectors,3,3,"fix_rigid:evectors"); int ierror; double ez0,ez1,ez2; 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[0][1] = tensor[1][0] = all[ibody][3]; tensor[1][2] = tensor[2][1] = all[ibody][4]; tensor[0][2] = tensor[2][0] = all[ibody][5]; ierror = jacobi(tensor,inertia[ibody],evectors); if (ierror) error->all(FLERR,"Insufficient Jacobi rotations for POEMS 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, error // this is b/c POEMS cannot yet handle degenerate bodies double max; max = MAX(inertia[ibody][0],inertia[ibody][1]); max = MAX(max,inertia[ibody][2]); if (inertia[ibody][0] < EPSILON*max || inertia[ibody][1] < EPSILON*max || inertia[ibody][2] < EPSILON*max) error->all(FLERR,"Rigid body has degenerate moment of inertia"); // enforce 3 evectors as a right-handed coordinate system // flip 3rd evector if needed ez0 = ex_space[ibody][1]*ey_space[ibody][2] - ex_space[ibody][2]*ey_space[ibody][1]; ez1 = ex_space[ibody][2]*ey_space[ibody][0] - ex_space[ibody][0]*ey_space[ibody][2]; ez2 = ex_space[ibody][0]*ey_space[ibody][1] - ex_space[ibody][1]*ey_space[ibody][0]; if (ez0*ez_space[ibody][0] + ez1*ez_space[ibody][1] + ez2*ez_space[ibody][2] < 0.0) { ez_space[ibody][0] = -ez_space[ibody][0]; ez_space[ibody][1] = -ez_space[ibody][1]; ez_space[ibody][2] = -ez_space[ibody][2]; } } // free temporary memory memory->destroy(tensor); memory->destroy(evectors); // displace = initial atom coords in basis of principal axes // only set joint atoms relative to 1st body // set displace = 0.0 for atoms not in any rigid body for (i = 0; i < nlocal; i++) { if (natom2body[i]) { ibody = atom2body[i][0]; xbox = (image[i] & IMGMASK) - IMGMAX; ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX; zbox = (image[i] >> IMG2BITS) - IMGMAX; dx = x[i][0] + xbox*xprd - xcm[ibody][0]; dy = x[i][1] + ybox*yprd - xcm[ibody][1]; dz = x[i][2] + zbox*zprd - xcm[ibody][2]; displace[i][0] = dx*ex_space[ibody][0] + dy*ex_space[ibody][1] + dz*ex_space[ibody][2]; displace[i][1] = dx*ey_space[ibody][0] + dy*ey_space[ibody][1] + dz*ey_space[ibody][2]; displace[i][2] = dx*ez_space[ibody][0] + dy*ez_space[ibody][1] + dz*ez_space[ibody][2]; } else displace[i][0] = displace[i][1] = displace[i][2] = 0.0; } // test for valid principal moments & axes // recompute moments of inertia around new axes // only count joint atoms in 1st body // 3 diagonal moments should equal principal moments // 3 off-diagonal moments should be 0.0 // (ddx,ddy,ddz) is projection of atom within rigid body onto principal axes // 6 moments use (ddx,ddy,ddz) displacements from principal axes for (ibody = 0; ibody < nbody; ibody++) for (i = 0; i < 6; i++) sum[ibody][i] = 0.0; double ddx,ddy,ddz; for (i = 0; i < nlocal; i++) { if (natom2body[i]) { ibody = atom2body[i][0]; xbox = (image[i] & IMGMASK) - IMGMAX; ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX; zbox = (image[i] >> IMG2BITS) - IMGMAX; dx = x[i][0] + xbox*xprd - xcm[ibody][0]; dy = x[i][1] + ybox*yprd - xcm[ibody][1]; dz = x[i][2] + zbox*zprd - xcm[ibody][2]; massone = mass[type[i]]; ddx = dx*ex_space[ibody][0] + dy*ex_space[ibody][1] + dz*ex_space[ibody][2]; ddy = dx*ey_space[ibody][0] + dy*ey_space[ibody][1] + dz*ey_space[ibody][2]; ddz = dx*ez_space[ibody][0] + dy*ez_space[ibody][1] + dz*ez_space[ibody][2]; sum[ibody][0] += massone * (ddy*ddy + ddz*ddz); sum[ibody][1] += massone * (ddx*ddx + ddz*ddz); sum[ibody][2] += massone * (ddx*ddx + ddy*ddy); sum[ibody][3] -= massone * ddx*ddy; sum[ibody][4] -= massone * ddy*ddz; sum[ibody][5] -= massone * ddx*ddz; } } MPI_Allreduce(sum[0],all[0],6*nbody,MPI_DOUBLE,MPI_SUM,world); for (ibody = 0; ibody < nbody; ibody++) { if (fabs(all[ibody][0]-inertia[ibody][0]) > TOLERANCE || fabs(all[ibody][1]-inertia[ibody][1]) > TOLERANCE || fabs(all[ibody][2]-inertia[ibody][2]) > TOLERANCE) error->all(FLERR,"Bad principal moments"); if (fabs(all[ibody][3]) > TOLERANCE || fabs(all[ibody][4]) > TOLERANCE || fabs(all[ibody][5]) > TOLERANCE) error->all(FLERR,"Bad principal moments"); } } /* ---------------------------------------------------------------------- compute initial rigid body info make setup call to POEMS ------------------------------------------------------------------------- */ void FixPOEMS::setup(int vflag) { int i,n,ibody; // vcm = velocity of center-of-mass of each rigid body // angmom = angular momentum of each rigid body // only count joint atoms in 1st body int *type = atom->type; tagint *image = atom->image; double *mass = atom->mass; double **x = atom->x; double **v = atom->v; int nlocal = atom->nlocal; double xprd = domain->xprd; double yprd = domain->yprd; double zprd = domain->zprd; int xbox,ybox,zbox; double massone,dx,dy,dz; for (ibody = 0; ibody < nbody; ibody++) for (i = 0; i < 6; i++) sum[ibody][i] = 0.0; for (i = 0; i < nlocal; i++) { if (natom2body[i]) { ibody = atom2body[i][0]; massone = mass[type[i]]; xbox = (image[i] & IMGMASK) - IMGMAX; ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX; zbox = (image[i] >> IMG2BITS) - IMGMAX; dx = x[i][0] + xbox*xprd - xcm[ibody][0]; dy = x[i][1] + ybox*yprd - xcm[ibody][1]; dz = x[i][2] + zbox*zprd - xcm[ibody][2]; sum[ibody][0] += v[i][0] * massone; sum[ibody][1] += v[i][1] * massone; sum[ibody][2] += v[i][2] * massone; sum[ibody][3] += dy * massone*v[i][2] - dz * massone*v[i][1]; sum[ibody][4] += dz * massone*v[i][0] - dx * massone*v[i][2]; sum[ibody][5] += dx * massone*v[i][1] - dy * massone*v[i][0]; } } 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]; angmom[ibody][0] = all[ibody][3]; angmom[ibody][1] = all[ibody][4]; angmom[ibody][2] = all[ibody][5]; } // 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++) omega_from_mq(angmom[ibody],ex_space[ibody],ey_space[ibody], ez_space[ibody],inertia[ibody],omega[ibody]); set_v(); // guestimate 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; } // use post_force() to compute initial fcm & torque post_force(vflag); // setup for POEMS poems->MakeSystem(nbody,masstotal,inertia,xcm,vcm,omega, ex_space,ey_space,ez_space, njoint,jointbody,xjoint,nfree,freelist, dthalf,dtv,force->ftm2v,total_ke); } /* ---------------------------------------------------------------------- update vcm,omega by 1/2 step and xcm,orientation by full step set x,v of body atoms accordingly ---------------------------------------------------------------------- */ void FixPOEMS::initial_integrate(int vflag) { // perform POEMS integration poems->LobattoOne(xcm,vcm,omega,torque,fcm,ex_space,ey_space,ez_space); // virial setup before call to set_xv if (vflag) v_setup(vflag); else evflag = 0; // set coords and velocities of atoms in rigid bodies set_xv(); } /* ---------------------------------------------------------------------- compute fcm,torque on each rigid body only count joint atoms in 1st body ------------------------------------------------------------------------- */ void FixPOEMS::post_force(int vflag) { int i,ibody; int xbox,ybox,zbox; double dx,dy,dz; tagint *image = atom->image; double **x = atom->x; double **f = atom->f; int nlocal = atom->nlocal; double xprd = domain->xprd; double yprd = domain->yprd; double zprd = domain->zprd; for (ibody = 0; ibody < nbody; ibody++) for (i = 0; i < 6; i++) sum[ibody][i] = 0.0; for (i = 0; i < nlocal; i++) { if (natom2body[i]) { ibody = atom2body[i][0]; sum[ibody][0] += f[i][0]; sum[ibody][1] += f[i][1]; sum[ibody][2] += f[i][2]; xbox = (image[i] & IMGMASK) - IMGMAX; ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX; zbox = (image[i] >> IMG2BITS) - IMGMAX; dx = x[i][0] + xbox*xprd - xcm[ibody][0]; dy = x[i][1] + ybox*yprd - xcm[ibody][1]; dz = x[i][2] + zbox*zprd - 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]; } } MPI_Allreduce(sum[0],all[0],6*nbody,MPI_DOUBLE,MPI_SUM,world); for (ibody = 0; ibody < nbody; ibody++) { fcm[ibody][0] = all[ibody][0]; fcm[ibody][1] = all[ibody][1]; fcm[ibody][2] = all[ibody][2]; torque[ibody][0] = all[ibody][3]; torque[ibody][1] = all[ibody][4]; torque[ibody][2] = all[ibody][5]; } } /* ---------------------------------------------------------------------- update vcm,omega by last 1/2 step set v of body atoms accordingly ------------------------------------------------------------------------- */ void FixPOEMS::final_integrate() { // perform POEMS integration poems->LobattoTwo(vcm,omega,torque,fcm); // set velocities of atoms in rigid bodies // virial is already setup from initial_integrate set_v(); } /* ---------------------------------------------------------------------- */ void FixPOEMS::initial_integrate_respa(int vflag, int ilevel, int iloop) { dtv = step_respa[ilevel]; dtf = 0.5 * step_respa[ilevel] * force->ftm2v; dthalf = 0.5 * step_respa[ilevel]; if (ilevel == 0) initial_integrate(vflag); else final_integrate(); } /* ---------------------------------------------------------------------- */ void FixPOEMS::post_force_respa(int vflag, int ilevel, int iloop) { if (ilevel == nlevels_respa-1) post_force(vflag); } /* ---------------------------------------------------------------------- */ void FixPOEMS::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 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() NOTE: cannot do this by changing xcm of each body in cluster or even 1st body in cluster b/c POEMS library does not see xcm but only sets xcm so remap needs to be coordinated with POEMS library thus this routine does nothing for now ------------------------------------------------------------------------- */ void FixPOEMS::pre_neighbor() {} /* ---------------------------------------------------------------------- count # of degrees-of-freedom removed by fix_poems for atoms in igroup ------------------------------------------------------------------------- */ int FixPOEMS::dof(int igroup) { int groupbit = group->bitmask[igroup]; // ncount = # of atoms in each rigid body that are also in group // only count joint atoms as part of first body int *mask = atom->mask; int nlocal = atom->nlocal; int *ncount = new int[nbody]; for (int ibody = 0; ibody < nbody; ibody++) ncount[ibody] = 0; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) if (natom2body[i]) ncount[atom2body[i][0]]++; int *nall = new int[nbody]; MPI_Allreduce(ncount,nall,nbody,MPI_INT,MPI_SUM,world); // remove 3N - 6 dof for each rigid body if at least 2 atoms are in igroup int n = 0; for (int ibody = 0; ibody < nbody; ibody++) if (nall[ibody] > 2) n += 3*nall[ibody] - 6; // subtract 3 additional dof for each joint if atom is also in igroup int m = 0; for (int i = 0; i < nlocal; i++) if (natom2body[i] > 1 && (mask[i] & groupbit)) m += 3*(natom2body[i]-1); int mall; MPI_Allreduce(&m,&mall,1,MPI_INT,MPI_SUM,world); n += mall; // delete local memory delete [] ncount; delete [] nall; return n; } /* ---------------------------------------------------------------------- adjust xcm of each cluster 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 NOTE: cannot do this by changing xcm of each body in cluster or even 1st body in cluster b/c POEMS library does not see xcm but only sets xcm so deform needs to be coordinated with POEMS library thus this routine does nothing for now ------------------------------------------------------------------------- */ void FixPOEMS::deform(int flag) {} /* ---------------------------------------------------------------------- */ void FixPOEMS::readfile(char *file) { FILE *fp; if (me == 0) { fp = fopen(file,"r"); if (fp == NULL) { char str[128]; sprintf(str,"Cannot open fix poems file %s",file); error->one(FLERR,str); } } nbody = 0; char *line = NULL; int maxline = 0; char *ptr; int nlocal = atom->nlocal; int i,id,nlen; while (1) { if (me == 0) nlen = readline(fp,&line,&maxline); MPI_Bcast(&nlen,1,MPI_INT,0,world); if (nlen == 0) break; MPI_Bcast(line,nlen,MPI_CHAR,0,world); ptr = strtok(line," ,\t\n\0"); if (ptr == NULL || ptr[0] == '#') continue; ptr = strtok(NULL," ,\t\n\0"); while (ptr = strtok(NULL," ,\t\n\0")) { id = atoi(ptr); i = atom->map(id); if (i < 0 || i >= nlocal) continue; if (natom2body[i] < MAXBODY) atom2body[i][natom2body[i]] = nbody; natom2body[i]++; } nbody++; } memory->destroy(line); fclose(fp); } /* ---------------------------------------------------------------------- */ int FixPOEMS::readline(FILE *fp, char **pline, int *pmaxline) { int n = 0; char *line = *pline; int maxline = *pmaxline; while (1) { if (n+1 >= maxline) { maxline += DELTA; memory->grow(line,maxline,"fix_poems:line"); } if (fgets(&line[n],maxline-n,fp) == NULL) { n = 0; break; } n = strlen(line); if (n < maxline-1 || line[n-1] == '\n') break; } *pmaxline = maxline; *pline = line; return n; } /* ---------------------------------------------------------------------- build list of joints and error check for cycles and trees ------------------------------------------------------------------------- */ void FixPOEMS::jointbuild() { int i,j; // convert atom2body into list of joint atoms on this proc // mjoint = # of joint atoms in this proc // an atom in N rigid bodies, infers N-1 joints between 1st body and others // mylist = [0],[1] = 2 body indices, [2] = global ID of joint atom int *tag = atom->tag; int nlocal = atom->nlocal; int mjoint = 0; for (i = 0; i < nlocal; i++) { if (natom2body[i] <= 1) continue; mjoint += natom2body[i]-1; } int **mylist = NULL; if (mjoint) memory->create(mylist,mjoint,3,"poems:mylist"); mjoint = 0; for (i = 0; i < nlocal; i++) { if (natom2body[i] <= 1) continue; for (j = 1; j < natom2body[i]; j++) { mylist[mjoint][0] = atom2body[i][0]; mylist[mjoint][1] = atom2body[i][j]; mylist[mjoint][2] = tag[i]; mjoint++; } } // jlist = mylist concatenated across all procs via MPI_Allgatherv MPI_Allreduce(&mjoint,&njoint,1,MPI_INT,MPI_SUM,world); int **jlist = NULL; if (njoint) memory->create(jlist,njoint,3,"poems:jlist"); int nprocs; MPI_Comm_size(world,&nprocs); int *recvcounts = new int[nprocs]; int tmp = 3*mjoint; MPI_Allgather(&tmp,1,MPI_INT,recvcounts,1,MPI_INT,world); int *displs = new int[nprocs]; displs[0] = 0; for (i = 1; i < nprocs; i++) displs[i] = displs[i-1] + recvcounts[i-1]; // allgather the local joint lists // 2 versions in case mjoint is 0 on this proc if (njoint) { if (mjoint) MPI_Allgatherv(mylist[0],3*mjoint,MPI_INT,jlist[0], recvcounts,displs,MPI_INT,world); else MPI_Allgatherv(NULL,3*mjoint,MPI_INT,jlist[0], recvcounts,displs,MPI_INT,world); } delete [] recvcounts; delete [] displs; // warning if no joints if (njoint == 0 && me == 0) error->warning(FLERR,"No joints between rigid bodies, use fix rigid instead"); // sort joint list in ascending order by body indices // check for loops in joint connections between rigid bodies // check for trees = same body in more than 2 joints sortlist(njoint,jlist); if (loopcheck(nbody,njoint,jlist)) error->all(FLERR,"Cyclic loop in joint connections"); int *bodyflag = new int[nbody]; for (i = 0; i < nbody; i++) bodyflag[i] = 0; for (i = 0; i < njoint; i++) { bodyflag[jlist[i][0]]++; bodyflag[jlist[i][1]]++; } for (i = 0; i < nbody; i++) if (bodyflag[i] > 2) error->all(FLERR,"Tree structure in joint connections"); delete [] bodyflag; // allocate and setup joint arrays // jointbody stores body indices from 1 to Nbody to pass to POEMS // each proc sets myjoint if it owns joint atom // MPI_Allreduce gives all procs the xjoint coords jointbody = NULL; xjoint = NULL; double **myjoint = NULL; if (njoint) { memory->create(jointbody,njoint,2,"poems:jointbody"); memory->create(xjoint,njoint,3,"poems:xjoint"); memory->create(myjoint,njoint,3,"poems:myjoint"); } double **x = atom->x; for (i = 0; i < njoint; i++) { jointbody[i][0] = jlist[i][0] + 1; jointbody[i][1] = jlist[i][1] + 1; j = atom->map(jlist[i][2]); if (j >= 0 && j < nlocal) { myjoint[i][0] = x[j][0]; myjoint[i][1] = x[j][1]; myjoint[i][2] = x[j][2]; } else myjoint[i][0] = myjoint[i][1] = myjoint[i][2] = 0.0; } if (njoint) MPI_Allreduce(myjoint[0],xjoint[0],3*njoint,MPI_DOUBLE,MPI_SUM,world); // compute freelist of nfree single unconnected bodies // POEMS could do this itself int *mark = new int[nbody]; for (i = 0; i < nbody; i++) mark[i] = 1; for (i = 0; i < njoint; i++) { mark[jointbody[i][0]-1] = 0; mark[jointbody[i][1]-1] = 0; } nfree = 0; for (i = 0; i < nbody; i++) if (mark[i]) nfree++; if (nfree) freelist = new int[nfree]; else freelist = NULL; nfree = 0; for (i = 0; i < nbody; i++) if (mark[i]) freelist[nfree++] = i + 1; delete [] mark; // free memory local to this routine memory->destroy(mylist); memory->destroy(jlist); memory->destroy(myjoint); } /* ---------------------------------------------------------------------- sort joint list (Numerical Recipes shell sort) sort criterion: sort on 1st body, if equal sort on 2nd body ------------------------------------------------------------------------- */ void FixPOEMS::sortlist(int n, int **list) { int i,j,v0,v1,v2,flag; int inc = 1; while (inc <= n) inc = 3*inc + 1; do { inc /= 3; for (i = inc+1; i <= n; i++) { v0 = list[i-1][0]; v1 = list[i-1][1]; v2 = list[i-1][2]; j = i; flag = 0; if (list[j-inc-1][0] > v0 || (list[j-inc-1][0] == v0 && list[j-inc-1][1] > v1)) flag = 1; while (flag) { list[j-1][0] = list[j-inc-1][0]; list[j-1][1] = list[j-inc-1][1]; list[j-1][2] = list[j-inc-1][2]; j -= inc; if (j <= inc) break; flag = 0; if (list[j-inc-1][0] > v0 || (list[j-inc-1][0] == v0 && list[j-inc-1][1] > v1)) flag = 1; } list[j-1][0] = v0; list[j-1][1] = v1; list[j-1][2] = v2; } } while (inc > 1); } /* ---------------------------------------------------------------------- check for cycles in list of joint connections between rigid bodies treat as graph: vertex = body, edge = joint between 2 bodies ------------------------------------------------------------------------- */ int FixPOEMS::loopcheck(int nvert, int nedge, int **elist) { int i,j,k; // ecount[i] = # of vertices connected to vertex i via edge // elistfull[i][*] = list of vertices connected to vertex i int *ecount = new int[nvert]; for (i = 0; i < nvert; i++) ecount[i] = 0; for (i = 0; i < nedge; i++) { ecount[elist[i][0]]++; ecount[elist[i][1]]++; } int emax = 0; for (i = 0; i < nvert; i++) emax = MAX(emax,ecount[i]); int **elistfull; memory->create(elistfull,nvert,emax,"poems:elistfull"); for (i = 0; i < nvert; i++) ecount[i] = 0; for (i = 0; i < nedge; i++) { elistfull[elist[i][0]][ecount[elist[i][0]]++] = elist[i][1]; elistfull[elist[i][1]][ecount[elist[i][1]]++] = elist[i][0]; } // cycle detection algorithm // mark = 0/1 marking of each vertex, all initially unmarked // outer while loop: // if all vertices are marked, no cycles, exit loop // push an unmarked vertex on stack and mark it, parent is -1 // while stack is not empty: // pop vertex I from stack // loop over vertices J connected to I via edge // if J is parent (vertex that pushed I on stack), skip it // else if J is marked, a cycle is found, return 1 // else push J on stack and mark it, parent is I // increment ncluster each time stack empties since that is new cluster int *parent = new int[nvert]; int *mark = new int[nvert]; for (i = 0; i < nvert; i++) mark[i] = 0; int nstack = 0; int *stack = new int[nvert]; ncluster = 0; while (1) { for (i = 0; i < nvert; i++) if (mark[i] == 0) break; if (i == nvert) break; stack[nstack++] = i; mark[i] = 1; parent[i] = -1; while (nstack) { i = stack[--nstack]; for (k = 0; k < ecount[i]; k++) { j = elistfull[i][k]; if (j == parent[i]) continue; if (mark[j]) return 1; stack[nstack++] = j; mark[j] = 1; parent[j] = i; } } ncluster++; } // free memory local to this routine delete [] ecount; memory->destroy(elistfull); delete [] parent; delete [] mark; delete [] stack; return 0; } /* ---------------------------------------------------------------------- compute evalues and evectors of 3x3 real symmetric matrix based on Jacobi rotations adapted from Numerical Recipes jacobi() function ------------------------------------------------------------------------- */ int FixPOEMS::jacobi(double **matrix, double *evalues, double **evectors) { int i,j,k; double tresh,theta,tau,t,sm,s,h,g,c,b[3],z[3]; for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) evectors[i][j] = 0.0; evectors[i][i] = 1.0; } for (i = 0; i < 3; i++) { b[i] = evalues[i] = matrix[i][i]; z[i] = 0.0; } for (int iter = 1; iter <= MAXJACOBI; iter++) { sm = 0.0; for (i = 0; i < 2; i++) for (j = i+1; j < 3; j++) sm += fabs(matrix[i][j]); if (sm == 0.0) return 0; if (iter < 4) tresh = 0.2*sm/(3*3); else tresh = 0.0; for (i = 0; i < 2; i++) { for (j = i+1; j < 3; j++) { g = 100.0*fabs(matrix[i][j]); if (iter > 4 && fabs(evalues[i])+g == fabs(evalues[i]) && fabs(evalues[j])+g == fabs(evalues[j])) matrix[i][j] = 0.0; else if (fabs(matrix[i][j]) > tresh) { h = evalues[j]-evalues[i]; if (fabs(h)+g == fabs(h)) t = (matrix[i][j])/h; else { theta = 0.5*h/(matrix[i][j]); t = 1.0/(fabs(theta)+sqrt(1.0+theta*theta)); if (theta < 0.0) t = -t; } c = 1.0/sqrt(1.0+t*t); s = t*c; tau = s/(1.0+c); h = t*matrix[i][j]; z[i] -= h; z[j] += h; evalues[i] -= h; evalues[j] += h; matrix[i][j] = 0.0; for (k = 0; k < i; k++) rotate(matrix,k,i,k,j,s,tau); for (k = i+1; k < j; k++) rotate(matrix,i,k,k,j,s,tau); for (k = j+1; k < 3; k++) rotate(matrix,i,k,j,k,s,tau); for (k = 0; k < 3; k++) rotate(evectors,k,i,k,j,s,tau); } } } for (i = 0; i < 3; i++) { evalues[i] = b[i] += z[i]; z[i] = 0.0; } } return 1; } /* ---------------------------------------------------------------------- perform a single Jacobi rotation ------------------------------------------------------------------------- */ void FixPOEMS::rotate(double **matrix, int i, int j, int k, int l, double s, double tau) { double g = matrix[i][j]; double h = matrix[k][l]; matrix[i][j] = g-s*(h+g*tau); matrix[k][l] = h+s*(g-h*tau); } /* ---------------------------------------------------------------------- compute omega from angular momentum w = omega = angular velocity in space frame wbody = angular velocity in body frame set wbody component to 0.0 if inertia component is 0.0 otherwise body can spin easily around that axis project space-frame angular momentum onto body axes and divide by principal moments ------------------------------------------------------------------------- */ void FixPOEMS::omega_from_mq(double *m, double *ex, double *ey, double *ez, double *inertia, double *w) { double wbody[3]; if (inertia[0] == 0.0) wbody[0] = 0.0; else wbody[0] = (m[0]*ex[0] + m[1]*ex[1] + m[2]*ex[2]) / inertia[0]; if (inertia[1] == 0.0) wbody[1] = 0.0; else wbody[1] = (m[0]*ey[0] + m[1]*ey[1] + m[2]*ey[2]) / inertia[1]; if (inertia[2] == 0.0) wbody[2] = 0.0; else wbody[2] = (m[0]*ez[0] + m[1]*ez[1] + m[2]*ez[2]) / inertia[2]; w[0] = wbody[0]*ex[0] + wbody[1]*ey[0] + wbody[2]*ez[0]; w[1] = wbody[0]*ex[1] + wbody[1]*ey[1] + wbody[2]*ez[1]; w[2] = wbody[0]*ex[2] + wbody[1]*ey[2] + wbody[2]*ez[2]; } /* ---------------------------------------------------------------------- set space-frame coords and velocity of each atom in each rigid body x = Q displace + Xcm, mapped back to periodic box v = Vcm + (W cross (x - Xcm)) ------------------------------------------------------------------------- */ void FixPOEMS::set_xv() { int ibody; int xbox,ybox,zbox; double x0,x1,x2,v0,v1,v2,fc0,fc1,fc2,massone; double vr[6]; tagint *image = atom->image; double **x = atom->x; double **v = atom->v; double **f = atom->f; double *mass = atom->mass; int *type = atom->type; int nlocal = atom->nlocal; double xprd = domain->xprd; double yprd = domain->yprd; double zprd = domain->zprd; // set x and v of each atom // only set joint atoms for 1st rigid body they belong to for (int i = 0; i < nlocal; i++) { if (natom2body[i] == 0) continue; ibody = atom2body[i][0]; 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) { x0 = x[i][0] + xbox*xprd; x1 = x[i][1] + ybox*yprd; 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 x[i][0] = ex_space[ibody][0]*displace[i][0] + ey_space[ibody][0]*displace[i][1] + ez_space[ibody][0]*displace[i][2]; x[i][1] = ex_space[ibody][1]*displace[i][0] + ey_space[ibody][1]*displace[i][1] + ez_space[ibody][1]*displace[i][2]; x[i][2] = ex_space[ibody][2]*displace[i][0] + ey_space[ibody][2]*displace[i][1] + ez_space[ibody][2]*displace[i][2]; 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 x[i][0] += xcm[ibody][0] - xbox*xprd; x[i][1] += xcm[ibody][1] - ybox*yprd; 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) { 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*fc0*x0; vr[1] = 0.5*fc1*x1; vr[2] = 0.5*fc2*x2; vr[3] = 0.5*fc1*x0; vr[4] = 0.5*fc2*x0; vr[5] = 0.5*fc2*x1; v_tally(1,&i,1.0,vr); } } } /* ---------------------------------------------------------------------- set space-frame velocity of each atom in a rigid body v = Vcm + (W cross (x - Xcm)) ------------------------------------------------------------------------- */ void FixPOEMS::set_v() { int ibody; int xbox,ybox,zbox; double dx,dy,dz; double x0,x1,x2,v0,v1,v2,fc0,fc1,fc2,massone; double vr[6]; double *mass = atom->mass; double **f = atom->f; double **x = atom->x; double **v = atom->v; int *type = atom->type; tagint *image = atom->image; int nlocal = atom->nlocal; double xprd = domain->xprd; double yprd = domain->yprd; double zprd = domain->zprd; // set v of each atom // only set joint atoms for 1st rigid body they belong to for (int i = 0; i < nlocal; i++) { if (natom2body[i] == 0) continue; ibody = atom2body[i][0]; dx = ex_space[ibody][0]*displace[i][0] + ey_space[ibody][0]*displace[i][1] + ez_space[ibody][0]*displace[i][2]; dy = ex_space[ibody][1]*displace[i][0] + ey_space[ibody][1]*displace[i][1] + ez_space[ibody][1]*displace[i][2]; dz = ex_space[ibody][2]*displace[i][0] + ey_space[ibody][2]*displace[i][1] + ez_space[ibody][2]*displace[i][2]; // 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]*dz - omega[ibody][2]*dy + vcm[ibody][0]; v[i][1] = omega[ibody][2]*dx - omega[ibody][0]*dz + vcm[ibody][1]; v[i][2] = omega[ibody][0]*dy - omega[ibody][1]*dx + 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) { 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; x0 = x[i][0] + xbox*xprd; x1 = x[i][1] + ybox*yprd; x2 = x[i][2] + zbox*zprd; vr[0] = 0.5*fc0*x0; vr[1] = 0.5*fc1*x1; vr[2] = 0.5*fc2*x2; vr[3] = 0.5*fc1*x0; vr[4] = 0.5*fc2*x0; vr[5] = 0.5*fc2*x1; v_tally(1,&i,1.0,vr); } } } /* ---------------------------------------------------------------------- allocate local atom-based arrays ------------------------------------------------------------------------- */ void FixPOEMS::grow_arrays(int nmax) { memory->grow(natom2body,nmax,"fix_poems:natom2body"); memory->grow(atom2body,nmax,MAXBODY,"fix_poems:atom2body"); memory->grow(displace,nmax,3,"fix_poems:displace"); } /* ---------------------------------------------------------------------- copy values within local atom-based arrays ------------------------------------------------------------------------- */ void FixPOEMS::copy_arrays(int i, int j, int delflag) { natom2body[j] = natom2body[i]; for (int k = 0; k < natom2body[j]; k++) atom2body[j][k] = atom2body[i][k]; displace[j][0] = displace[i][0]; displace[j][1] = displace[i][1]; displace[j][2] = displace[i][2]; } /* ---------------------------------------------------------------------- memory usage of local atom-based arrays ------------------------------------------------------------------------- */ double FixPOEMS::memory_usage() { int nmax = atom->nmax; double bytes = nmax * sizeof(int); bytes += nmax*MAXBODY * sizeof(int); bytes += nmax*3 * sizeof(double); return bytes; } /* ---------------------------------------------------------------------- pack values in local atom-based arrays for exchange with another proc ------------------------------------------------------------------------- */ int FixPOEMS::pack_exchange(int i, double *buf) { int m = 0; buf[m++] = static_cast (natom2body[i]); for (int j = 0; j < natom2body[i]; j++) buf[m++] = static_cast (atom2body[i][j]); buf[m++] = displace[i][0]; buf[m++] = displace[i][1]; buf[m++] = displace[i][2]; return m; } /* ---------------------------------------------------------------------- unpack values in local atom-based arrays from exchange with another proc ------------------------------------------------------------------------- */ int FixPOEMS::unpack_exchange(int nlocal, double *buf) { int m = 0; natom2body[nlocal] = static_cast (buf[m++]); for (int i = 0; i < natom2body[nlocal]; i++) atom2body[nlocal][i] = static_cast (buf[m++]); displace[nlocal][0] = buf[m++]; displace[nlocal][1] = buf[m++]; displace[nlocal][2] = buf[m++]; return m; } /* ---------------------------------------------------------------------- */ void FixPOEMS::reset_dt() { dtv = update->dt; dtf = 0.5 * update->dt * force->ftm2v; dthalf = 0.5 * update->dt; } diff --git a/src/SRD/fix_srd.cpp b/src/SRD/fix_srd.cpp index 68651cad1..01ff4e8f2 100644 --- a/src/SRD/fix_srd.cpp +++ b/src/SRD/fix_srd.cpp @@ -1,3950 +1,3964 @@ /* ---------------------------------------------------------------------- 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: Jeremy Lechman (SNL), Pieter in 't Veld (BASF) ------------------------------------------------------------------------- */ #include "math.h" #include "string.h" #include "stdlib.h" #include "fix_srd.h" #include "math_extra.h" #include "atom.h" #include "atom_vec_ellipsoid.h" #include "atom_vec_line.h" #include "atom_vec_tri.h" #include "group.h" #include "update.h" #include "force.h" #include "pair.h" #include "domain.h" #include "neighbor.h" #include "comm.h" #include "modify.h" #include "fix_deform.h" #include "fix_wall_srd.h" #include "random_mars.h" #include "random_park.h" #include "math_const.h" +#include "citeme.h" #include "memory.h" #include "error.h" using namespace LAMMPS_NS; using namespace FixConst; using namespace MathConst; enum{SLIP,NOSLIP}; enum{SPHERE,ELLIPSOID,LINE,TRIANGLE,WALL}; enum{INSIDE_ERROR,INSIDE_WARN,INSIDE_IGNORE}; enum{BIG_MOVE,SRD_MOVE,SRD_ROTATE}; enum{CUBIC_ERROR,CUBIC_WARN}; enum{SHIFT_NO,SHIFT_YES,SHIFT_POSSIBLE}; enum{NO_REMAP,X_REMAP,V_REMAP}; // same as fix_deform.cpp #define EINERTIA 0.2 // moment of inertia prefactor for ellipsoid #define ATOMPERBIN 30 #define BIG 1.0e20 #define VBINSIZE 5 #define TOLERANCE 0.00001 #define MAXITER 20 +static const char cite_fix_srd[] = + "fix srd command:\n\n" + "@Article{Petersen10,\n" + " author = {M. K. Petersen, J. B. Lechman, S. J. Plimpton, G. S. Grest, P. J. in 't Veld, P. R. Schunk},\n" + " title = {Mesoscale Hydrodynamics via Stochastic Rotation Dynamics: Comparison with Lennard-Jones Fluid}," + " journal = {J.~Chem.~Phys.},\n" + " year = 2010,\n" + " volume = 132,\n" + " pages = {174106}\n" + "}\n\n"; + //#define SRD_DEBUG 1 //#define SRD_DEBUG_ATOMID 58 //#define SRD_DEBUG_TIMESTEP 449 /* ---------------------------------------------------------------------- */ FixSRD::FixSRD(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { + if (lmp->citeme) lmp->citeme->add(cite_fix_srd); + if (narg < 8) error->all(FLERR,"Illegal fix srd command"); restart_pbc = 1; vector_flag = 1; size_vector = 12; global_freq = 1; extvector = 0; nevery = force->inumeric(FLERR,arg[3]); bigexist = 1; if (strcmp(arg[4],"NULL") == 0) bigexist = 0; else biggroup = group->find(arg[4]); temperature_srd = force->numeric(FLERR,arg[5]); gridsrd = force->numeric(FLERR,arg[6]); int seed = force->inumeric(FLERR,arg[7]); // parse options lamdaflag = 0; collidestyle = NOSLIP; overlap = 0; insideflag = INSIDE_ERROR; exactflag = 1; radfactor = 1.0; maxbounceallow = 0; gridsearch = gridsrd; cubicflag = CUBIC_ERROR; cubictol = 0.01; shiftuser = SHIFT_NO; shiftseed = 0; tstat = 0; rescale_rotate = rescale_collide = 1; int iarg = 8; while (iarg < narg) { if (strcmp(arg[iarg],"lamda") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command"); lamda = force->numeric(FLERR,arg[iarg+1]); lamdaflag = 1; iarg += 2; } else if (strcmp(arg[iarg],"collision") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command"); if (strcmp(arg[iarg+1],"slip") == 0) collidestyle = SLIP; else if (strcmp(arg[iarg+1],"noslip") == 0) collidestyle = NOSLIP; else error->all(FLERR,"Illegal fix srd command"); iarg += 2; } else if (strcmp(arg[iarg],"overlap") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command"); if (strcmp(arg[iarg+1],"yes") == 0) overlap = 1; else if (strcmp(arg[iarg+1],"no") == 0) overlap = 0; else error->all(FLERR,"Illegal fix srd command"); iarg += 2; } else if (strcmp(arg[iarg],"inside") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command"); if (strcmp(arg[iarg+1],"error") == 0) insideflag = INSIDE_ERROR; else if (strcmp(arg[iarg+1],"warn") == 0) insideflag = INSIDE_WARN; else if (strcmp(arg[iarg+1],"ignore") == 0) insideflag = INSIDE_IGNORE; else error->all(FLERR,"Illegal fix srd command"); iarg += 2; } else if (strcmp(arg[iarg],"exact") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command"); if (strcmp(arg[iarg+1],"yes") == 0) exactflag = 1; else if (strcmp(arg[iarg+1],"no") == 0) exactflag = 0; else error->all(FLERR,"Illegal fix srd command"); iarg += 2; } else if (strcmp(arg[iarg],"radius") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command"); radfactor = force->numeric(FLERR,arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"bounce") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command"); maxbounceallow = force->inumeric(FLERR,arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"search") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command"); gridsearch = force->numeric(FLERR,arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"cubic") == 0) { if (iarg+3 > narg) error->all(FLERR,"Illegal fix srd command"); if (strcmp(arg[iarg+1],"error") == 0) cubicflag = CUBIC_ERROR; else if (strcmp(arg[iarg+1],"warn") == 0) cubicflag = CUBIC_WARN; else error->all(FLERR,"Illegal fix srd command"); cubictol = force->numeric(FLERR,arg[iarg+2]); iarg += 3; } else if (strcmp(arg[iarg],"shift") == 0) { if (iarg+3 > narg) error->all(FLERR,"Illegal fix srd command"); else if (strcmp(arg[iarg+1],"no") == 0) shiftuser = SHIFT_NO; else if (strcmp(arg[iarg+1],"yes") == 0) shiftuser = SHIFT_YES; else if (strcmp(arg[iarg+1],"possible") == 0) shiftuser = SHIFT_POSSIBLE; else error->all(FLERR,"Illegal fix srd command"); shiftseed = force->inumeric(FLERR,arg[iarg+2]); iarg += 3; } else if (strcmp(arg[iarg],"tstat") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command"); if (strcmp(arg[iarg+1],"no") == 0) tstat = 0; else if (strcmp(arg[iarg+1],"yes") == 0) tstat = 1; else error->all(FLERR,"Illegal fix srd command"); iarg += 2; } else if (strcmp(arg[iarg],"rescale") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command"); if (strcmp(arg[iarg+1],"no") == 0) rescale_rotate = rescale_collide = 0; else if (strcmp(arg[iarg+1],"yes") == 0) rescale_rotate = rescale_collide = 1; else if (strcmp(arg[iarg+1],"rotate") == 0) { rescale_rotate = 1; rescale_collide = 0; } else if (strcmp(arg[iarg+1],"collide") == 0) { rescale_rotate = 0; rescale_collide = 1; } else error->all(FLERR,"Illegal fix srd command"); iarg += 2; } else error->all(FLERR,"Illegal fix srd command"); } // error check if (nevery <= 0) error->all(FLERR,"Illegal fix srd command"); if (bigexist && biggroup < 0) error->all(FLERR,"Could not find fix srd group ID"); if (gridsrd <= 0.0) error->all(FLERR,"Illegal fix srd command"); if (temperature_srd <= 0.0) error->all(FLERR,"Illegal fix srd command"); if (seed <= 0) error->all(FLERR,"Illegal fix srd command"); if (radfactor <= 0.0) error->all(FLERR,"Illegal fix srd command"); if (maxbounceallow < 0) error->all(FLERR,"Illegal fix srd command"); if (lamdaflag && lamda <= 0.0) error->all(FLERR,"Illegal fix srd command"); if (gridsearch <= 0.0) error->all(FLERR,"Illegal fix srd command"); if (cubictol < 0.0 || cubictol > 1.0) error->all(FLERR,"Illegal fix srd command"); if ((shiftuser == SHIFT_YES || shiftuser == SHIFT_POSSIBLE) && shiftseed <= 0) error->all(FLERR,"Illegal fix srd command"); // initialize Marsaglia RNG with processor-unique seed me = comm->me; nprocs = comm->nprocs; random = new RanMars(lmp,seed + me); // if requested, initialize shift RNG, same on every proc if (shiftuser == SHIFT_YES || shiftuser == SHIFT_POSSIBLE) randomshift = new RanPark(lmp,shiftseed); else randomshift = NULL; // initialize data structs and flags if (bigexist) biggroupbit = group->bitmask[biggroup]; else biggroupbit = 0; nmax = 0; binhead = NULL; maxbin1 = 0; binnext = NULL; maxbuf = 0; sbuf1 = sbuf2 = rbuf1 = rbuf2 = NULL; shifts[0].maxvbin = shifts[1].maxvbin = 0; shifts[0].vbin = shifts[1].vbin = NULL; shifts[0].maxbinsq = shifts[1].maxbinsq = 0; for (int ishift = 0; ishift < 2; ishift++) for (int iswap = 0; iswap < 6; iswap++) shifts[ishift].bcomm[iswap].sendlist = shifts[ishift].bcomm[iswap].recvlist = NULL; maxbin2 = 0; nbinbig = NULL; binbig = NULL; binsrd = NULL; nstencil = maxstencil = 0; stencil = NULL; maxbig = 0; biglist = NULL; stats_flag = 1; for (int i = 0; i < size_vector; i++) stats_all[i] = 0.0; initflag = 0; srd_bin_temp = 0.0; srd_bin_count = 0; // atom style pointers to particles that store bonus info avec_ellipsoid = (AtomVecEllipsoid *) atom->style_match("ellipsoid"); avec_line = (AtomVecLine *) atom->style_match("line"); avec_tri = (AtomVecTri *) atom->style_match("tri"); // fix parameters if (collidestyle == SLIP) comm_reverse = 3; else comm_reverse = 6; force_reneighbor = 1; } /* ---------------------------------------------------------------------- */ FixSRD::~FixSRD() { delete random; delete randomshift; memory->destroy(binhead); memory->destroy(binnext); memory->destroy(sbuf1); memory->destroy(sbuf2); memory->destroy(rbuf1); memory->destroy(rbuf2); memory->sfree(shifts[0].vbin); memory->sfree(shifts[1].vbin); for (int ishift = 0; ishift < 2; ishift++) for (int iswap = 0; iswap < 6; iswap++) { memory->destroy(shifts[ishift].bcomm[iswap].sendlist); memory->destroy(shifts[ishift].bcomm[iswap].recvlist); } memory->destroy(nbinbig); memory->destroy(binbig); memory->destroy(binsrd); memory->destroy(stencil); memory->sfree(biglist); } /* ---------------------------------------------------------------------- */ int FixSRD::setmask() { int mask = 0; mask |= PRE_NEIGHBOR; mask |= POST_FORCE; return mask; } /* ---------------------------------------------------------------------- */ void FixSRD::init() { // error checks if (force->newton_pair == 0) error->all(FLERR,"Fix srd requires newton pair on"); if (bigexist && comm->ghost_velocity == 0) error->all(FLERR,"Fix srd requires ghost atoms store velocity"); if (bigexist && collidestyle == NOSLIP && !atom->torque_flag) error->all(FLERR,"Fix SRD no-slip requires atom attribute torque"); if (initflag && update->dt != dt_big) error->all(FLERR,"Cannot change timestep once fix srd is setup"); // orthogonal vs triclinic simulation box // could be static or shearing box triclinic = domain->triclinic; // wallexist = 1 if SRD wall(s) are defined wallexist = 0; for (int m = 0; m < modify->nfix; m++) { if (strcmp(modify->fix[m]->style,"wall/srd") == 0) { if (wallexist) error->all(FLERR,"Cannot use fix wall/srd more than once"); wallexist = 1; wallfix = (FixWallSRD *) modify->fix[m]; nwall = wallfix->nwall; wallvarflag = wallfix->varflag; wallwhich = wallfix->wallwhich; xwall = wallfix->xwall; xwallhold = wallfix->xwallhold; vwall = wallfix->vwall; fwall = wallfix->fwall; walltrigger = 0.5 * neighbor->skin; if (wallfix->overlap && overlap == 0 && me == 0) error->warning(FLERR, "Fix SRD walls overlap but fix srd overlap not set"); } } // set change_flags if box size or shape changes change_size = change_shape = deformflag = 0; if (domain->nonperiodic == 2) change_size = 1; for (int i = 0; i < modify->nfix; i++) { if (modify->fix[i]->box_change_size) change_size = 1; if (modify->fix[i]->box_change_shape) change_shape = 1; if (strcmp(modify->fix[i]->style,"deform") == 0) { deformflag = 1; FixDeform *deform = (FixDeform *) modify->fix[i]; if (deform->box_change_shape && deform->remapflag != V_REMAP) error->all(FLERR,"Using fix srd with inconsistent " "fix deform remap option"); } } if (deformflag && tstat == 0 && me == 0) error->warning(FLERR, "Using fix srd with box deformation but no SRD thermostat"); // parameterize based on current box volume dimension = domain->dimension; parameterize(); // limit initial SRD velocities if necessary double **v = atom->v; int *mask = atom->mask; int nlocal = atom->nlocal; double vsq; nrescale = 0; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { vsq = v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]; if (vsq > vmaxsq) { nrescale++; MathExtra::scale3(vmax/sqrt(vsq),v[i]); } } int all; MPI_Allreduce(&nrescale,&all,1,MPI_INT,MPI_SUM,world); if (me == 0) { if (screen) fprintf(screen," # of rescaled SRD velocities = %d\n",all); if (logfile) fprintf(logfile," # of rescaled SRD velocities = %d\n",all); } velocity_stats(igroup); if (bigexist) velocity_stats(biggroup); // zero per-run stats nrescale = 0; bouncemaxnum = 0; bouncemax = 0; reneighcount = 0; initflag = 1; next_reneighbor = -1; } /* ---------------------------------------------------------------------- */ void FixSRD::setup(int vflag) { setup_bounds(); if (dist_srd_reneigh < nevery*dt_big*vmax && me == 0) error->warning(FLERR, "Fix srd SRD moves may trigger frequent reneighboring"); // setup search bins and search stencil based on these distances if (bigexist || wallexist) { setup_search_bins(); setup_search_stencil(); } else nbins2 = 0; // perform first bining of SRD and big particles and walls // set reneighflag to turn off SRD rotation // don't do SRD rotation in setup, only during timestepping reneighflag = BIG_MOVE; pre_neighbor(); } /* ---------------------------------------------------------------------- assign SRD particles to bins assign big particles to all bins they overlap ------------------------------------------------------------------------- */ void FixSRD::pre_neighbor() { int i,j,m,ix,iy,iz,jx,jy,jz,ibin,jbin,lo,hi; double rsq,cutbinsq; double xlamda[3]; // grow SRD per-atom bin arrays if necessary if (atom->nlocal > nmax) { nmax = atom->nmax; memory->destroy(binsrd); memory->destroy(binnext); memory->create(binsrd,nmax,"fix/srd:binsrd"); memory->create(binnext,nmax,"fix/srd:binnext"); } // setup and grow BIG info list if necessary // set index ptrs to BIG particles and to WALLS // big_static() adds static properties to info list if (bigexist || wallexist) { if (bigexist) { if (biggroup == atom->firstgroup) nbig = atom->nfirst + atom->nghost; else { int *mask = atom->mask; int nlocal = atom->nlocal; nbig = atom->nghost; for (i = 0; i < nlocal; i++) if (mask[i] & biggroupbit) nbig++; } } else nbig = 0; int ninfo = nbig; if (wallexist) ninfo += nwall; if (ninfo > maxbig) { maxbig = ninfo; memory->destroy(biglist); biglist = (Big *) memory->smalloc(maxbig*sizeof(Big),"fix/srd:biglist"); } if (bigexist) { int *mask = atom->mask; int nlocal = atom->nlocal; if (biggroup == atom->firstgroup) nlocal = atom->nfirst; nbig = 0; for (i = 0; i < nlocal; i++) if (mask[i] & biggroupbit) biglist[nbig++].index = i; int nall = atom->nlocal + atom->nghost; for (i = atom->nlocal; i < nall; i++) if (mask[i] & biggroupbit) biglist[nbig++].index = i; big_static(); } if (wallexist) { for (m = 0; m < nwall; m++) { biglist[nbig+m].index = m; biglist[nbig+m].type = WALL; } wallfix->wall_params(1); } } // if simulation box size changes, reset velocity bins // if big particles exist, reset search bins if box size or shape changes, // b/c finite-size particles will overlap different bins as the box tilts if (change_size) setup_bounds(); if (change_size) setup_velocity_bins(); if ((change_size || change_shape) && (bigexist || wallexist)) { setup_search_bins(); setup_search_stencil(); } // map each owned & ghost big particle to search bins it overlaps // zero out bin counters for big particles // if firstgroup is defined, only loop over first and ghost particles // for each big particle: loop over stencil to find overlap bins int *mask = atom->mask; double **x = atom->x; int nlocal = atom->nlocal; int nall = nlocal + atom->nghost; int nfirst = nlocal; if (bigexist && biggroup == atom->firstgroup) nfirst = atom->nfirst; if (bigexist || wallexist) for (i = 0; i < nbins2; i++) nbinbig[i] = 0; if (bigexist) { i = nbig = 0; while (i < nall) { if (mask[i] & biggroupbit) { ix = static_cast ((x[i][0]-xblo2)*bininv2x); iy = static_cast ((x[i][1]-yblo2)*bininv2y); iz = static_cast ((x[i][2]-zblo2)*bininv2z); ibin = iz*nbin2y*nbin2x + iy*nbin2x + ix; if (ix < 0 || ix >= nbin2x || iy < 0 || iy >= nbin2y || iz < 0 || iz >= nbin2z) error->one(FLERR,"Fix SRD: bad search bin assignment"); cutbinsq = biglist[nbig].cutbinsq; for (j = 0; j < nstencil; j++) { jx = ix + stencil[j][0]; jy = iy + stencil[j][1]; jz = iz + stencil[j][2]; if (jx < 0 || jx >= nbin2x || jy < 0 || jy >= nbin2y || jz < 0 || jz >= nbin2z) { printf("Big particle %d %d %g %g %g\n", atom->tag[i],i,x[i][0],x[i][1],x[i][2]); printf("Bin indices: %d %d %d, %d %d %d, %d %d %d\n", ix,iy,iz,jx,jy,jz,nbin2x,nbin2y,nbin2z); error->one(FLERR,"Fix SRD: bad stencil bin for big particle"); } rsq = point_bin_distance(x[i],jx,jy,jz); if (rsq < cutbinsq) { jbin = ibin + stencil[j][3]; if (nbinbig[jbin] == ATOMPERBIN) error->one(FLERR,"Fix SRD: too many big particles in bin"); binbig[jbin][nbinbig[jbin]++] = nbig; } } nbig++; } i++; if (i == nfirst) i = nlocal; } } // map each wall to search bins it covers, up to non-periodic boundary // if wall moves, add walltrigger to its position // this insures it is added to all search bins it may move into // may not overlap any of my search bins if (wallexist) { double delta = 0.0; if (wallvarflag) delta = walltrigger; for (m = 0; m < nwall; m++) { int dim = wallwhich[m] / 2; int side = wallwhich[m] % 2; if (dim == 0) { if (side == 0) { hi = static_cast ((xwall[m]+delta-xblo2)*bininv2x); if (hi < 0) continue; if (hi >= nbin2x) error->all(FLERR, "Fix SRD: bad search bin assignment"); lo = 0; } else { lo = static_cast ((xwall[m]-delta-xblo2)*bininv2x); if (lo >= nbin2x) continue; if (lo < 0) error->all(FLERR,"Fix SRD: bad search bin assignment"); hi = nbin2x-1; } for (ix = lo; ix <= hi; ix++) for (iy = 0; iy < nbin2y; iy++) for (iz = 0; iz < nbin2z; iz++) { ibin = iz*nbin2y*nbin2x + iy*nbin2x + ix; if (nbinbig[ibin] == ATOMPERBIN) error->all(FLERR,"Fix SRD: too many walls in bin"); binbig[ibin][nbinbig[ibin]++] = nbig+m; } } else if (dim == 1) { if (side == 0) { hi = static_cast ((xwall[m]+delta-yblo2)*bininv2y); if (hi < 0) continue; if (hi >= nbin2y) error->all(FLERR, "Fix SRD: bad search bin assignment"); lo = 0; } else { lo = static_cast ((xwall[m]-delta-yblo2)*bininv2y); if (lo >= nbin2y) continue; if (lo < 0) error->all(FLERR,"Fix SRD: bad search bin assignment"); hi = nbin2y-1; } for (iy = lo; iy <= hi; iy++) for (ix = 0; ix < nbin2x; ix++) for (iz = 0; iz < nbin2z; iz++) { ibin = iz*nbin2y*nbin2x + iy*nbin2x + ix; if (nbinbig[ibin] == ATOMPERBIN) error->all(FLERR,"Fix SRD: too many walls in bin"); binbig[ibin][nbinbig[ibin]++] = nbig+m; } } else if (dim == 2) { if (side == 0) { hi = static_cast ((xwall[m]+delta-zblo2)*bininv2z); if (hi < 0) continue; if (hi >= nbin2z) error->all(FLERR, "Fix SRD: bad search bin assignment"); lo = 0; } else { lo = static_cast ((xwall[m]-delta-zblo2)*bininv2z); if (lo >= nbin2z) continue; if (lo < 0) error->all(FLERR,"Fix SRD: bad search bin assignment"); hi = nbin2z-1; } for (iz = lo; iz < hi; iz++) for (ix = 0; ix < nbin2x; ix++) for (iy = 0; iy < nbin2y; iy++) { ibin = iz*nbin2y*nbin2x + iy*nbin2x + ix; if (nbinbig[ibin] == ATOMPERBIN) error->all(FLERR,"Fix SRD: too many walls in bin"); binbig[ibin][nbinbig[ibin]++] = nbig+m; } } } } // rotate SRD velocities on SRD timestep // done now since all SRDs are currently inside my sub-domain if (reneighflag == SRD_ROTATE) reset_velocities(); // log stats if reneighboring occurred b/c SRDs moved too far if (reneighflag == SRD_MOVE) reneighcount++; reneighflag = BIG_MOVE; } /* ---------------------------------------------------------------------- advect SRD particles and detect collisions between SRD and BIG particles when collision occurs, change x,v of SRD, force,torque of BIG particle ------------------------------------------------------------------------- */ void FixSRD::post_force(int vflag) { int i,m,ix,iy,iz; double xlamda[3]; // zero per-timestep stats stats_flag = 0; ncheck = ncollide = nbounce = ninside = 0; // zero ghost forces & torques on BIG particles double **f = atom->f; double **torque = atom->torque; int nlocal = atom->nlocal; int nall = nlocal + atom->nghost; if (bigexist == 0) nall = 0; for (i = nlocal; i < nall; i++) f[i][0] = f[i][1] = f[i][2] = 0.0; if (collidestyle == NOSLIP) for (i = nlocal; i < nall; i++) torque[i][0] = torque[i][1] = torque[i][2] = 0.0; // advect SRD particles // assign to search bins if big particles or walls exist int *mask = atom->mask; double **x = atom->x; double **v = atom->v; if (bigexist || wallexist) { for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) { x[i][0] += dt_big*v[i][0]; x[i][1] += dt_big*v[i][1]; x[i][2] += dt_big*v[i][2]; ix = static_cast ((x[i][0]-xblo2)*bininv2x); iy = static_cast ((x[i][1]-yblo2)*bininv2y); iz = static_cast ((x[i][2]-zblo2)*bininv2z); binsrd[i] = iz*nbin2y*nbin2x + iy*nbin2x + ix; if (ix < 0 || ix >= nbin2x || iy < 0 || iy >= nbin2y || iz < 0 || iz >= nbin2z) { if (screen) { fprintf(screen,"SRD particle %d on step " BIGINT_FORMAT "\n", atom->tag[i],update->ntimestep); fprintf(screen,"v = %g %g %g\n",v[i][0],v[i][1],v[i][2]); fprintf(screen,"x = %g %g %g\n",x[i][0],x[i][1],x[i][2]); fprintf(screen,"ix,iy,iz nx,ny,nz = %d %d %d %d %d %d\n", ix,iy,iz,nbin2x,nbin2y,nbin2z); } error->one(FLERR,"Fix SRD: bad bin assignment for SRD advection"); } } } else { for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) { x[i][0] += dt_big*v[i][0]; x[i][1] += dt_big*v[i][1]; x[i][2] += dt_big*v[i][2]; } } // detect collision of SRDs with BIG particles or walls if (bigexist || wallexist) { if (bigexist) big_dynamic(); if (wallexist) wallfix->wall_params(0); if (overlap) collisions_multi(); else collisions_single(); } // reverse communicate forces & torques on BIG particles if (bigexist) { flocal = f; tlocal = torque; comm->reverse_comm_fix(this); } // if any SRD particle has moved too far, trigger reneigh on next step // for triclinic, perform check in lamda units int flag = 0; if (triclinic) domain->x2lamda(nlocal); for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) { if (x[i][0] < srdlo_reneigh[0] || x[i][0] > srdhi_reneigh[0] || x[i][1] < srdlo_reneigh[1] || x[i][1] > srdhi_reneigh[1] || x[i][2] < srdlo_reneigh[2] || x[i][2] > srdhi_reneigh[2]) flag = 1; } if (triclinic) domain->lamda2x(nlocal); int flagall; MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_SUM,world); if (flagall) { next_reneighbor = update->ntimestep + 1; reneighflag = SRD_MOVE; } // if wall has moved too far, trigger reneigh on next step // analagous to neighbor check for big particle moving 1/2 of skin distance if (wallexist) { for (m = 0; m < nwall; m++) if (fabs(xwall[m]-xwallhold[m]) > walltrigger) next_reneighbor = update->ntimestep + 1; } // if next timestep is SRD timestep, trigger reneigh if ((update->ntimestep+1) % nevery == 0) { next_reneighbor = update->ntimestep + 1; reneighflag = SRD_ROTATE; } } /* ---------------------------------------------------------------------- reset SRD velocities may perform random shifting by up to 1/2 bin in each dimension called at pre-neighbor stage when all SRDs are now inside my sub-domain if tstat, then thermostat SRD particles as well, including streaming effects ------------------------------------------------------------------------- */ void FixSRD::reset_velocities() { int i,j,n,ix,iy,iz,ibin,axis,sign,irandom; double u[3],vsum[3]; double vx,vy,vz,vsq,tbin,scale; double *vave,*vnew,*xlamda; double vstream[3]; // if requested, perform a dynamic shift of bin positions if (shiftflag) { double *boxlo; if (triclinic == 0) boxlo = domain->boxlo; else boxlo = domain->boxlo_lamda; shifts[1].corner[0] = boxlo[0] - binsize1x*randomshift->uniform(); shifts[1].corner[1] = boxlo[1] - binsize1y*randomshift->uniform(); if (dimension == 3) shifts[1].corner[2] = boxlo[2] - binsize1z*randomshift->uniform(); else shifts[1].corner[2] = boxlo[2]; setup_velocity_shift(1,1); } double *corner = shifts[shiftflag].corner; int *binlo = shifts[shiftflag].binlo; int *binhi = shifts[shiftflag].binhi; int nbins = shifts[shiftflag].nbins; int nbinx = shifts[shiftflag].nbinx; int nbiny = shifts[shiftflag].nbiny; BinAve *vbin = shifts[shiftflag].vbin; // binhead = 1st SRD particle in each bin // binnext = index of next particle in bin // bin assignment is done in lamda units for triclinic int *mask = atom->mask; double **x = atom->x; double **v = atom->v; int nlocal = atom->nlocal; if (triclinic) domain->x2lamda(nlocal); for (i = 0; i < nbins; i++) binhead[i] = -1; for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) { ix = static_cast ((x[i][0]-corner[0])*bininv1x); ix = MAX(ix,binlo[0]); ix = MIN(ix,binhi[0]); iy = static_cast ((x[i][1]-corner[1])*bininv1y); iy = MAX(iy,binlo[1]); iy = MIN(iy,binhi[1]); iz = static_cast ((x[i][2]-corner[2])*bininv1z); iz = MAX(iz,binlo[2]); iz = MIN(iz,binhi[2]); ibin = (iz-binlo[2])*nbiny*nbinx + (iy-binlo[1])*nbinx + (ix-binlo[0]); binnext[i] = binhead[ibin]; binhead[ibin] = i; } if (triclinic) domain->lamda2x(nlocal); // for each bin I have particles contributing to: // compute summed v of particles in that bin // if I own the bin, set its random value, else set to 0.0 for (i = 0; i < nbins; i++) { n = 0; vsum[0] = vsum[1] = vsum[2] = 0.0; for (j = binhead[i]; j >= 0; j = binnext[j]) { vsum[0] += v[j][0]; vsum[1] += v[j][1]; vsum[2] += v[j][2]; n++; } vbin[i].vsum[0] = vsum[0]; vbin[i].vsum[1] = vsum[1]; vbin[i].vsum[2] = vsum[2]; vbin[i].n = n; if (vbin[i].owner) vbin[i].random = random->uniform(); else vbin[i].random = 0.0; } // communicate bin info for bins which more than 1 proc contribute to if (shifts[shiftflag].commflag) vbin_comm(shiftflag); // for each bin I have particles contributing to: // compute vave over particles in bin // thermal velocity of each particle = v - vave // rotate thermal vel of each particle around one of 6 random axes // add vave back to each particle // thermostat if requested: // if no deformation, rescale thermal vel to temperature // if deformation, rescale thermal vel and change vave to vstream // these are settings for extra dof_temp, dof_tstat to subtract // (not sure why these settings work best) // no deformation, no tstat: dof_temp = 1 // yes deformation, no tstat: doesn't matter, system will not equilibrate // no deformation, yes tstat: dof_temp = dof_tstat = 1 // yes deformation, yes tstat: dof_temp = dof_tstat = 0 // accumulate final T_srd for each bin I own double tfactor = force->mvv2e * mass_srd / (dimension * force->boltz); int dof_temp = 1; int dof_tstat; if (tstat) { if (deformflag) dof_tstat = dof_temp = 0; else dof_tstat = 1; } srd_bin_temp = 0.0; srd_bin_count = 0; if (dimension == 2) axis = 2; double *h_rate = domain->h_rate; double *h_ratelo = domain->h_ratelo; for (i = 0; i < nbins; i++) { n = vbin[i].n; if (n == 0) continue; vave = vbin[i].vsum; vave[0] /= n; vave[1] /= n; vave[2] /= n; irandom = static_cast (6.0*vbin[i].random); sign = irandom % 2; if (dimension == 3) axis = irandom / 2; vsq = 0.0; for (j = binhead[i]; j >= 0; j = binnext[j]) { if (axis == 0) { u[0] = v[j][0]-vave[0]; u[1] = sign ? v[j][2]-vave[2] : vave[2]-v[j][2]; u[2] = sign ? vave[1]-v[j][1] : v[j][1]-vave[1]; } else if (axis == 1) { u[1] = v[j][1]-vave[1]; u[0] = sign ? v[j][2]-vave[2] : vave[2]-v[j][2]; u[2] = sign ? vave[0]-v[j][0] : v[j][0]-vave[0]; } else { u[2] = v[j][2]-vave[2]; u[1] = sign ? v[j][0]-vave[0] : vave[0]-v[j][0]; u[0] = sign ? vave[1]-v[j][1] : v[j][1]-vave[1]; } vsq += u[0]*u[0] + u[1]*u[1] + u[2]*u[2]; v[j][0] = u[0] + vave[0]; v[j][1] = u[1] + vave[1]; v[j][2] = u[2] + vave[2]; } // NOTE: vsq needs to be summed across shared bins in parallel // like vave above via the vbin_comm() call // else the computed scale factor below is incomplete for a shared bin if (tstat && n > 1) { if (deformflag) { xlamda = vbin[i].xctr; vstream[0] = h_rate[0]*xlamda[0] + h_rate[5]*xlamda[1] + h_rate[4]*xlamda[2] + h_ratelo[0]; vstream[1] = h_rate[1]*xlamda[1] + h_rate[3]*xlamda[2] + h_ratelo[1]; vstream[2] = h_rate[2]*xlamda[2] + h_ratelo[2]; } else { vstream[0] = vave[0]; vstream[1] = vave[1]; vstream[2] = vave[2]; } // tbin = thermal temperature of particles in bin // scale = scale factor for thermal velocity tbin = vsq/(n-dof_tstat) * tfactor; scale = sqrt(temperature_srd/tbin); vsq = 0.0; for (j = binhead[i]; j >= 0; j = binnext[j]) { u[0] = (v[j][0] - vave[0]) * scale; u[1] = (v[j][1] - vave[1]) * scale; u[2] = (v[j][2] - vave[2]) * scale; vsq += u[0]*u[0] + u[1]*u[1] + u[2]*u[2]; v[j][0] = u[0] + vstream[0]; v[j][1] = u[1] + vstream[1]; v[j][2] = u[2] + vstream[2]; } } // sum partial contribution of my particles to T even if I don't own bin // but only count bin if I own it, so each bin is counted exactly once if (n > 1) srd_bin_temp += vsq/(n-dof_temp); if (vbin[i].owner) srd_bin_count++; } srd_bin_temp *= tfactor; // rescale any too-large velocities if (rescale_rotate) { for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) { vsq = v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]; if (vsq > vmaxsq) { nrescale++; MathExtra::scale3(vmax/sqrt(vsq),v[i]); } } } } /* ---------------------------------------------------------------------- communicate summed particle info for bins that overlap 1 or more procs ------------------------------------------------------------------------- */ void FixSRD::vbin_comm(int ishift) { BinComm *bcomm1,*bcomm2; MPI_Request request1,request2; MPI_Status status; // send/recv bins in both directions in each dimension // don't send if nsend = 0 // due to static bins aliging with proc boundary // due to dynamic bins across non-periodic global boundary // copy to self if sendproc = me // MPI send to another proc if sendproc != me // don't recv if nrecv = 0 // copy from self if recvproc = me // MPI recv from another proc if recvproc != me BinAve *vbin = shifts[ishift].vbin; int *procgrid = comm->procgrid; int iswap = 0; for (int idim = 0; idim < dimension; idim++) { bcomm1 = &shifts[ishift].bcomm[iswap++]; bcomm2 = &shifts[ishift].bcomm[iswap++]; if (procgrid[idim] == 1) { if (bcomm1->nsend) vbin_pack(vbin,bcomm1->nsend,bcomm1->sendlist,sbuf1); if (bcomm2->nsend) vbin_pack(vbin,bcomm2->nsend,bcomm2->sendlist,sbuf2); if (bcomm1->nrecv) vbin_unpack(sbuf1,vbin,bcomm1->nrecv,bcomm1->recvlist); if (bcomm2->nrecv) vbin_unpack(sbuf2,vbin,bcomm2->nrecv,bcomm2->recvlist); } else { if (bcomm1->nrecv) MPI_Irecv(rbuf1,bcomm1->nrecv*VBINSIZE,MPI_DOUBLE,bcomm1->recvproc,0, world,&request1); if (bcomm2->nrecv) MPI_Irecv(rbuf2,bcomm2->nrecv*VBINSIZE,MPI_DOUBLE,bcomm2->recvproc,0, world,&request2); if (bcomm1->nsend) { vbin_pack(vbin,bcomm1->nsend,bcomm1->sendlist,sbuf1); MPI_Send(sbuf1,bcomm1->nsend*VBINSIZE,MPI_DOUBLE, bcomm1->sendproc,0,world); } if (bcomm2->nsend) { vbin_pack(vbin,bcomm2->nsend,bcomm2->sendlist,sbuf2); MPI_Send(sbuf2,bcomm2->nsend*VBINSIZE,MPI_DOUBLE, bcomm2->sendproc,0,world); } if (bcomm1->nrecv) { MPI_Wait(&request1,&status); vbin_unpack(rbuf1,vbin,bcomm1->nrecv,bcomm1->recvlist); } if (bcomm2->nrecv) { MPI_Wait(&request2,&status); vbin_unpack(rbuf2,vbin,bcomm2->nrecv,bcomm2->recvlist); } } } } /* ---------------------------------------------------------------------- pack velocity bin data into a message buffer for sending ------------------------------------------------------------------------- */ void FixSRD::vbin_pack(BinAve *vbin, int n, int *list, double *buf) { int j; int m = 0; for (int i = 0; i < n; i++) { j = list[i]; buf[m++] = vbin[j].n; buf[m++] = vbin[j].vsum[0]; buf[m++] = vbin[j].vsum[1]; buf[m++] = vbin[j].vsum[2]; buf[m++] = vbin[j].random; } } /* ---------------------------------------------------------------------- unpack velocity bin data from a message buffer and sum values to my bins ------------------------------------------------------------------------- */ void FixSRD::vbin_unpack(double *buf, BinAve *vbin, int n, int *list) { int j; int m = 0; for (int i = 0; i < n; i++) { j = list[i]; vbin[j].n += static_cast (buf[m++]); vbin[j].vsum[0] += buf[m++]; vbin[j].vsum[1] += buf[m++]; vbin[j].vsum[2] += buf[m++]; vbin[j].random += buf[m++]; } } /* ---------------------------------------------------------------------- detect all collisions between SRD and BIG particles or WALLS assume SRD can be inside at most one BIG particle or WALL at a time unoverlap SRDs for each collision ------------------------------------------------------------------------- */ void FixSRD::collisions_single() { int i,j,k,m,type,nbig,ibin,ibounce,inside,collide_flag,lineside; double dt,t_remain; double norm[3],xscoll[3],xbcoll[3],vsnew[3]; Big *big; // outer loop over SRD particles // inner loop over BIG particles or WALLS that overlap SRD particle bin // if overlap between SRD and BIG particle or wall: // for exact, compute collision pt in time // for inexact, push SRD to surf of BIG particle or WALL // update x,v of SRD and f,torque on BIG particle // re-bin SRD particle after collision // iterate until the SRD particle has no overlaps with BIG particles or WALLS double **x = atom->x; double **v = atom->v; double **f = atom->f; double **torque = atom->torque; int *mask = atom->mask; int nlocal = atom->nlocal; for (i = 0; i < nlocal; i++) { if (!(mask[i] & groupbit)) continue; ibin = binsrd[i]; if (nbinbig[ibin] == 0) continue; ibounce = 0; collide_flag = 1; dt = dt_big; while (collide_flag) { nbig = nbinbig[ibin]; if (ibounce == 0) ncheck += nbig; collide_flag = 0; for (m = 0; m < nbig; m++) { k = binbig[ibin][m]; big = &biglist[k]; j = big->index; type = big->type; if (type == SPHERE) inside = inside_sphere(x[i],x[j],big); else if (type == ELLIPSOID) inside = inside_ellipsoid(x[i],x[j],big); else inside = inside_wall(x[i],j); if (inside) { if (exactflag) { if (type == SPHERE) t_remain = collision_sphere_exact(x[i],x[j],v[i],v[j],big, xscoll,xbcoll,norm); else if (type == ELLIPSOID) t_remain = collision_ellipsoid_exact(x[i],x[j],v[i],v[j],big, xscoll,xbcoll,norm); else t_remain = collision_wall_exact(x[i],j,v[i],xscoll,xbcoll,norm); } else { t_remain = 0.5*dt; if (type == SPHERE) collision_sphere_inexact(x[i],x[j],big,xscoll,xbcoll,norm); else if (type == ELLIPSOID) collision_ellipsoid_inexact(x[i],x[j],big,xscoll,xbcoll,norm); else collision_wall_inexact(x[i],j,xscoll,xbcoll,norm); } #ifdef SRD_DEBUG if (update->ntimestep == SRD_DEBUG_TIMESTEP && atom->tag[i] == SRD_DEBUG_ATOMID) print_collision(i,j,ibounce,t_remain,dt,xscoll,xbcoll,norm,type); #endif if (t_remain > dt) { ninside++; if (insideflag == INSIDE_ERROR || insideflag == INSIDE_WARN) { char str[128]; if (type != WALL) sprintf(str, "SRD particle %d started " "inside big particle %d on step " BIGINT_FORMAT " bounce %d", atom->tag[i],atom->tag[j],update->ntimestep,ibounce+1); else sprintf(str, "SRD particle %d started " "inside big particle %d on step " BIGINT_FORMAT " bounce %d", atom->tag[i],atom->tag[j],update->ntimestep,ibounce+1); if (insideflag == INSIDE_ERROR) error->one(FLERR,str); error->warning(FLERR,str); } break; } if (collidestyle == SLIP) { if (type != WALL) slip(v[i],v[j],x[j],big,xscoll,norm,vsnew); else slip_wall(v[i],j,norm,vsnew); } else { if (type != WALL) noslip(v[i],v[j],x[j],big,-1, xscoll,norm,vsnew); else noslip(v[i],NULL,x[j],big,j,xscoll,norm,vsnew); } if (dimension == 2) vsnew[2] = 0.0; // check on rescaling of vsnew if (rescale_collide) { double vsq = vsnew[0]*vsnew[0] + vsnew[1]*vsnew[1] + vsnew[2]*vsnew[2]; if (vsq > vmaxsq) { nrescale++; MathExtra::scale3(vmax/sqrt(vsq),vsnew); } } // update BIG particle and WALL and SRD // BIG particle is not torqued if sphere and SLIP collision if (collidestyle == SLIP && type == SPHERE) force_torque(v[i],vsnew,xscoll,xbcoll,f[j],NULL); else if (type != WALL) force_torque(v[i],vsnew,xscoll,xbcoll,f[j],torque[j]); else if (type == WALL) force_wall(v[i],vsnew,j); ibin = binsrd[i] = update_srd(i,t_remain,xscoll,vsnew,x[i],v[i]); if (ibounce == 0) ncollide++; ibounce++; if (ibounce < maxbounceallow || maxbounceallow == 0) collide_flag = 1; dt = t_remain; break; } } } nbounce += ibounce; if (maxbounceallow && ibounce >= maxbounceallow) bouncemaxnum++; if (ibounce > bouncemax) bouncemax = ibounce; } } /* ---------------------------------------------------------------------- detect all collisions between SRD and big particles an SRD can be inside more than one big particle at a time requires finding which big particle SRD collided with first unoverlap SRDs for each collision ------------------------------------------------------------------------- */ void FixSRD::collisions_multi() { int i,j,k,m,type,nbig,ibin,ibounce,inside,jfirst,typefirst,jlast; double dt,t_remain,t_first; double norm[3],xscoll[3],xbcoll[3],vsnew[3]; double normfirst[3],xscollfirst[3],xbcollfirst[3]; Big *big; // outer loop over SRD particles // inner loop over BIG particles or WALLS that overlap SRD particle bin // loop over all BIG and WALLS to find which one SRD collided with first // if overlap between SRD and BIG particle or wall: // compute collision pt in time // update x,v of SRD and f,torque on BIG particle // re-bin SRD particle after collision // iterate until the SRD particle has no overlaps with BIG particles or WALLS double **x = atom->x; double **v = atom->v; double **f = atom->f; double **torque = atom->torque; int *mask = atom->mask; int nlocal = atom->nlocal; for (i = 0; i < nlocal; i++) { if (!(mask[i] & groupbit)) continue; ibin = binsrd[i]; if (nbinbig[ibin] == 0) continue; ibounce = 0; jlast = -1; dt = dt_big; while (1) { nbig = nbinbig[ibin]; if (ibounce == 0) ncheck += nbig; t_first = 0.0; for (m = 0; m < nbig; m++) { k = binbig[ibin][m]; big = &biglist[k]; j = big->index; if (j == jlast) continue; type = big->type; if (type == SPHERE) inside = inside_sphere(x[i],x[j],big); else if (type == ELLIPSOID) inside = inside_ellipsoid(x[i],x[j],big); else if (type == LINE) inside = inside_line(x[i],x[j],v[i],v[j],big,dt); else if (type == TRIANGLE) inside = inside_tri(x[i],x[j],v[i],v[j],big,dt); else inside = inside_wall(x[i],j); if (inside) { if (type == SPHERE) t_remain = collision_sphere_exact(x[i],x[j],v[i],v[j],big, xscoll,xbcoll,norm); else if (type == ELLIPSOID) t_remain = collision_ellipsoid_exact(x[i],x[j],v[i],v[j],big, xscoll,xbcoll,norm); else if (type == LINE) t_remain = collision_line_exact(x[i],x[j],v[i],v[j],big,dt, xscoll,xbcoll,norm); else if (type == TRIANGLE) t_remain = collision_tri_exact(x[i],x[j],v[i],v[j],big,dt, xscoll,xbcoll,norm); else t_remain = collision_wall_exact(x[i],j,v[i],xscoll,xbcoll,norm); #ifdef SRD_DEBUG if (update->ntimestep == SRD_DEBUG_TIMESTEP && atom->tag[i] == SRD_DEBUG_ATOMID) print_collision(i,j,ibounce,t_remain,dt,xscoll,xbcoll,norm,type); #endif if (t_remain > dt || t_remain < 0.0) { ninside++; if (insideflag == INSIDE_ERROR || insideflag == INSIDE_WARN) { char str[128]; sprintf(str, "SRD particle %d started " "inside big particle %d on step " BIGINT_FORMAT " bounce %d", atom->tag[i],atom->tag[j],update->ntimestep,ibounce+1); if (insideflag == INSIDE_ERROR) error->one(FLERR,str); error->warning(FLERR,str); } t_first = 0.0; break; } if (t_remain > t_first) { t_first = t_remain; jfirst = j; typefirst = type; xscollfirst[0] = xscoll[0]; xscollfirst[1] = xscoll[1]; xscollfirst[2] = xscoll[2]; xbcollfirst[0] = xbcoll[0]; xbcollfirst[1] = xbcoll[1]; xbcollfirst[2] = xbcoll[2]; normfirst[0] = norm[0]; normfirst[1] = norm[1]; normfirst[2] = norm[2]; } } } if (t_first == 0.0) break; j = jlast = jfirst; type = typefirst; xscoll[0] = xscollfirst[0]; xscoll[1] = xscollfirst[1]; xscoll[2] = xscollfirst[2]; xbcoll[0] = xbcollfirst[0]; xbcoll[1] = xbcollfirst[1]; xbcoll[2] = xbcollfirst[2]; norm[0] = normfirst[0]; norm[1] = normfirst[1]; norm[2] = normfirst[2]; if (collidestyle == SLIP) { if (type != WALL) slip(v[i],v[j],x[j],big,xscoll,norm,vsnew); else slip_wall(v[i],j,norm,vsnew); } else { if (type != WALL) noslip(v[i],v[j],x[j],big,-1,xscoll,norm,vsnew); else noslip(v[i],NULL,x[j],big,j,xscoll,norm,vsnew); } if (dimension == 2) vsnew[2] = 0.0; // check on rescaling of vsnew if (rescale_collide) { double vsq = vsnew[0]*vsnew[0] + vsnew[1]*vsnew[1] + vsnew[2]*vsnew[2]; if (vsq > vmaxsq) { nrescale++; MathExtra::scale3(vmax/sqrt(vsq),vsnew); } } // update BIG particle and WALL and SRD // BIG particle is not torqued if sphere and SLIP collision if (collidestyle == SLIP && type == SPHERE) force_torque(v[i],vsnew,xscoll,xbcoll,f[j],NULL); else if (type != WALL) force_torque(v[i],vsnew,xscoll,xbcoll,f[j],torque[j]); else if (type == WALL) force_wall(v[i],vsnew,j); ibin = binsrd[i] = update_srd(i,t_first,xscoll,vsnew,x[i],v[i]); if (ibounce == 0) ncollide++; ibounce++; if (ibounce == maxbounceallow) break; dt = t_first; } nbounce += ibounce; if (maxbounceallow && ibounce >= maxbounceallow) bouncemaxnum++; if (ibounce > bouncemax) bouncemax = ibounce; } } /* ---------------------------------------------------------------------- check if SRD particle S is inside spherical big particle B ------------------------------------------------------------------------- */ int FixSRD::inside_sphere(double *xs, double *xb, Big *big) { double dx,dy,dz; dx = xs[0] - xb[0]; dy = xs[1] - xb[1]; dz = xs[2] - xb[2]; if (dx*dx + dy*dy + dz*dz <= big->radsq) return 1; return 0; } /* ---------------------------------------------------------------------- check if SRD particle S is inside ellipsoidal big particle B ------------------------------------------------------------------------- */ int FixSRD::inside_ellipsoid(double *xs, double *xb, Big *big) { double x,y,z; double *ex = big->ex; double *ey = big->ey; double *ez = big->ez; double xs_xb[3]; xs_xb[0] = xs[0] - xb[0]; xs_xb[1] = xs[1] - xb[1]; xs_xb[2] = xs[2] - xb[2]; x = xs_xb[0]*ex[0] + xs_xb[1]*ex[1] + xs_xb[2]*ex[2]; y = xs_xb[0]*ey[0] + xs_xb[1]*ey[1] + xs_xb[2]*ey[2]; z = xs_xb[0]*ez[0] + xs_xb[1]*ez[1] + xs_xb[2]*ez[2]; if (x*x*big->aradsqinv + y*y*big->bradsqinv + z*z*big->cradsqinv <= 1.0) return 1; return 0; } /* ---------------------------------------------------------------------- check if SRD particle S is inside line big particle B collision only possible if: S starts on positive side of infinite line, which means it will collide with outside of rigid body made of lines since line segments have outward normals, when vector from first to last point is crossed with +z axis S ends on negative side of infinite line unlike most other inside() routines, then calculate exact collision: solve for collision pt along infinite line collision if pt is within endpoints of B ------------------------------------------------------------------------- */ int FixSRD::inside_line(double *xs, double *xb, double *vs, double *vb, Big *big, double dt_step) { double pmc0[2],pmc1[2],n0[2],n1[2]; double n1_n0[2],pmc1_pmc0[2]; // 1 and 2 = start and end of timestep // pmc = P - C, where P = position of S, C = position of B // n = normal to line = [-sin(theta),cos(theta)], theta = orientation of B // (P-C) dot N = side of line that S is on // side0 = -1,0,1 for which side of line B that S is on at start of step // side1 = -1,0,1 for which side of line B that S is on at end of step xs1[0] = xs[0]; xs1[1] = xs[1]; xb1[0] = xb[0]; xb1[1] = xb[1]; xs0[0] = xs1[0] - dt_step*vs[0]; xs0[1] = xs1[1] - dt_step*vs[1]; xb0[0] = xb1[0] - dt_step*vb[0]; xb0[1] = xb1[1] - dt_step*vb[1]; theta1 = big->theta; theta0 = theta1 - dt_step*big->omega[2]; pmc0[0] = xs0[0] - xb0[0]; pmc0[1] = xs0[1] - xb0[1]; n0[0] = sin(theta0); n0[1] = -cos(theta0); pmc1[0] = xs1[0] - xb1[0]; pmc1[1] = xs1[1] - xb1[1]; n1[0] = sin(theta1); n1[1] = -cos(theta1); double side0 = pmc0[0]*n0[0] + pmc0[1]*n0[1]; double side1 = pmc1[0]*n1[0] + pmc1[1]*n1[1]; if (side0 <= 0.0 || side1 >= 0.0) return 0; // solve for time t (0 to 1) at which moving particle // crosses infinite moving/rotating line // Newton-Raphson solve of full non-linear parametric equation tfraction = newton_raphson(0.0,1.0); // quadratic equation solve of approximate parametric equation /* n1_n0[0] = n1[0]-n0[0]; n1_n0[1] = n1[1]-n0[1]; pmc1_pmc0[0] = pmc1[0]-pmc0[0]; pmc1_pmc0[1] = pmc1[1]-pmc0[1]; double a = pmc1_pmc0[0]*n1_n0[0] + pmc1_pmc0[1]*n1_n0[1]; double b = pmc1_pmc0[0]*n0[0] + pmc1_pmc0[1]*n0[1] + n1_n0[0]*pmc0[0] + n1_n0[1]*pmc0[1]; double c = pmc0[0]*n0[0] + pmc0[1]*n0[1]; if (a == 0.0) { double dot0 = pmc0[0]*n0[0] + pmc0[1]*n0[1]; double dot1 = pmc1[0]*n0[0] + pmc1[1]*n0[1]; double root = -dot0 / (dot1 - dot0); //printf("Linear root: %g %g\n",root,tfraction); tfraction = root; } else { double term = sqrt(b*b - 4.0*a*c); double root1 = (-b + term) / (2.0*a); double root2 = (-b - term) / (2.0*a); //printf("ABC vecs: %g %g: %g %g\n", // pmc1_pmc0[0],pmc1_pmc0[1],n1_n0[0],n1_n0[1]); //printf("ABC vecs: %g %g: %g %g: %g %g %g\n", // n0[0],n0[1],n1[0],n1[1],theta0,theta1,big->omega[2]); //printf("ABC root: %g %g %g: %g %g %g\n",a,b,c,root1,root2,tfraction); if (0.0 <= root1 && root1 <= 1.0) tfraction = root1; else if (0.0 <= root2 && root2 <= 1.0) tfraction = root2; else error->one(FLERR,"Bad quadratic solve for particle/line collision"); } */ // check if collision pt is within line segment at collision time xsc[0] = xs0[0] + tfraction*(xs1[0]-xs0[0]); xsc[1] = xs0[1] + tfraction*(xs1[1]-xs0[1]); xbc[0] = xb0[0] + tfraction*(xb1[0]-xb0[0]); xbc[1] = xb0[1] + tfraction*(xb1[1]-xb0[1]); double delx = xsc[0] - xbc[0]; double dely = xsc[1] - xbc[1]; double rsq = delx*delx + dely*dely; if (rsq > 0.25*big->length*big->length) return 0; //nbc[0] = n0[0] + tfraction*(n1[0]-n0[0]); //nbc[1] = n0[1] + tfraction*(n1[1]-n0[1]); nbc[0] = sin(theta0 + tfraction*(theta1-theta0)); nbc[1] = -cos(theta0 + tfraction*(theta1-theta0)); return 1; } /* ---------------------------------------------------------------------- check if SRD particle S is inside triangle big particle B collision only possible if: S starts on positive side of triangle plane, which means it will collide with outside of rigid body made of tris since triangles have outward normals, S ends on negative side of triangle plane unlike most other inside() routines, then calculate exact collision: solve for collision pt on triangle plane collision if pt is inside triangle B ------------------------------------------------------------------------- */ int FixSRD::inside_tri(double *xs, double *xb, double *vs, double *vb, Big *big, double dt_step) { double pmc0[3],pmc1[3],n0[3]; double n1_n0[3],pmc1_pmc0[3]; double excoll[3],eycoll[3],ezcoll[3]; double dc1[3],dc2[3],dc3[3]; double c1[3],c2[3],c3[3]; double c2mc1[3],c3mc2[3],c1mc3[3]; double pvec[3],xproduct[3]; // 1 and 2 = start and end of timestep // pmc = P - C, where P = position of S, C = position of B // n = normal to triangle // (P-C) dot N = side of tri that S is on // side0 = -1,0,1 for which side of tri B that S is on at start of step // side1 = -1,0,1 for which side of tri B that S is on at end of step double *omega = big->omega; double *n1 = big->norm; n0[0] = n1[0] - dt_step*(omega[1]*n1[2] - omega[2]*n1[1]); n0[1] = n1[1] - dt_step*(omega[2]*n1[0] - omega[0]*n1[2]); n0[2] = n1[2] - dt_step*(omega[0]*n1[1] - omega[1]*n1[0]); pmc0[0] = xs[0] - dt_step*vs[0] - xb[0] + dt_step*vb[0]; pmc0[1] = xs[1] - dt_step*vs[1] - xb[1] + dt_step*vb[1]; pmc0[2] = xs[2] - dt_step*vs[2] - xb[2] + dt_step*vb[2]; pmc1[0] = xs[0] - xb[0]; pmc1[1] = xs[1] - xb[1]; pmc1[2] = xs[2] - xb[2]; double side0 = MathExtra::dot3(pmc0,n0); double side1 = MathExtra::dot3(pmc1,n1); if (side0 <= 0.0 || side1 >= 0.0) return 0; // solve for time t (0 to 1) at which moving particle // crosses moving/rotating tri // quadratic equation solve of approximate parametric equation n1_n0[0] = n1[0]-n0[0]; n1_n0[1] = n1[1]-n0[1]; n1_n0[2] = n1[2]-n0[2]; pmc1_pmc0[0] = pmc1[0]-pmc0[0]; pmc1_pmc0[1] = pmc1[1]-pmc0[1]; pmc1_pmc0[2] = pmc1[2]-pmc0[2]; double a = MathExtra::dot3(pmc1_pmc0,n1_n0); double b = MathExtra::dot3(pmc1_pmc0,n0) + MathExtra::dot3(n1_n0,pmc0); double c = MathExtra::dot3(pmc0,n0); if (a == 0.0) { double dot0 = MathExtra::dot3(pmc0,n0); double dot1 = MathExtra::dot3(pmc1,n0); double root = -dot0 / (dot1 - dot0); tfraction = root; } else { double term = sqrt(b*b - 4.0*a*c); double root1 = (-b + term) / (2.0*a); double root2 = (-b - term) / (2.0*a); if (0.0 <= root1 && root1 <= 1.0) tfraction = root1; else if (0.0 <= root2 && root2 <= 1.0) tfraction = root2; else error->one(FLERR,"Bad quadratic solve for particle/tri collision"); } // calculate position/orientation of S and B at collision time // dt = time previous to now at which collision occurs // point = S position in plane of triangle at collision time // Excoll,Eycoll,Ezcoll = orientation of tri at collision time // c1,c2,c3 = corner points of triangle at collision time // nbc = normal to plane of triangle at collision time AtomVecTri::Bonus *tbonus; tbonus = avec_tri->bonus; double *ex = big->ex; double *ey = big->ey; double *ez = big->ez; int index = atom->tri[big->index]; double *c1body = tbonus[index].c1; double *c2body = tbonus[index].c2; double *c3body = tbonus[index].c3; double dt = (1.0-tfraction)*dt_step; xsc[0] = xs[0] - dt*vs[0]; xsc[1] = xs[1] - dt*vs[1]; xsc[2] = xs[2] - dt*vs[2]; xbc[0] = xb[0] - dt*vb[0]; xbc[1] = xb[1] - dt*vb[1]; xbc[2] = xb[2] - dt*vb[2]; excoll[0] = ex[0] - dt*(omega[1]*ex[2] - omega[2]*ex[1]); excoll[1] = ex[1] - dt*(omega[2]*ex[0] - omega[0]*ex[2]); excoll[2] = ex[2] - dt*(omega[0]*ex[1] - omega[1]*ex[0]); eycoll[0] = ey[0] - dt*(omega[1]*ey[2] - omega[2]*ey[1]); eycoll[1] = ey[1] - dt*(omega[2]*ey[0] - omega[0]*ey[2]); eycoll[2] = ey[2] - dt*(omega[0]*ey[1] - omega[1]*ey[0]); ezcoll[0] = ez[0] - dt*(omega[1]*ez[2] - omega[2]*ez[1]); ezcoll[1] = ez[1] - dt*(omega[2]*ez[0] - omega[0]*ez[2]); ezcoll[2] = ez[2] - dt*(omega[0]*ez[1] - omega[1]*ez[0]); MathExtra::matvec(excoll,eycoll,ezcoll,c1body,dc1); MathExtra::matvec(excoll,eycoll,ezcoll,c2body,dc2); MathExtra::matvec(excoll,eycoll,ezcoll,c3body,dc3); MathExtra::add3(xbc,dc1,c1); MathExtra::add3(xbc,dc2,c2); MathExtra::add3(xbc,dc3,c3); MathExtra::sub3(c2,c1,c2mc1); MathExtra::sub3(c3,c2,c3mc2); MathExtra::sub3(c1,c3,c1mc3); MathExtra::cross3(c2mc1,c3mc2,nbc); MathExtra::norm3(nbc); // check if collision pt is within triangle // pvec = vector from tri vertex to intersection point // xproduct = cross product of edge vec with pvec // if dot product of xproduct with nbc < 0.0 for any of 3 edges, // intersection point is outside tri MathExtra::sub3(xsc,c1,pvec); MathExtra::cross3(c2mc1,pvec,xproduct); if (MathExtra::dot3(xproduct,nbc) < 0.0) return 0; MathExtra::sub3(xsc,c2,pvec); MathExtra::cross3(c3mc2,pvec,xproduct); if (MathExtra::dot3(xproduct,nbc) < 0.0) return 0; MathExtra::sub3(xsc,c3,pvec); MathExtra::cross3(c1mc3,pvec,xproduct); if (MathExtra::dot3(xproduct,nbc) < 0.0) return 0; return 1; } /* ---------------------------------------------------------------------- check if SRD particle S is inside wall IWALL ------------------------------------------------------------------------- */ int FixSRD::inside_wall(double *xs, int iwall) { int dim = wallwhich[iwall] / 2; int side = wallwhich[iwall] % 2; if (side == 0 && xs[dim] < xwall[iwall]) return 1; if (side && xs[dim] > xwall[iwall]) return 1; return 0; } /* ---------------------------------------------------------------------- collision of SRD particle S with surface of spherical big particle B exact because compute time of collision dt = time previous to now at which collision occurs xscoll = collision pt = position of SRD at time of collision xbcoll = position of big particle at time of collision norm = surface normal of collision pt at time of collision ------------------------------------------------------------------------- */ double FixSRD::collision_sphere_exact(double *xs, double *xb, double *vs, double *vb, Big *big, double *xscoll, double *xbcoll, double *norm) { double vs_dot_vs,vb_dot_vb,vs_dot_vb; double vs_dot_xb,vb_dot_xs,vs_dot_xs,vb_dot_xb; double xs_dot_xs,xb_dot_xb,xs_dot_xb; double a,b,c,scale; vs_dot_vs = vs[0]*vs[0] + vs[1]*vs[1] + vs[2]*vs[2]; vb_dot_vb = vb[0]*vb[0] + vb[1]*vb[1] + vb[2]*vb[2]; vs_dot_vb = vs[0]*vb[0] + vs[1]*vb[1] + vs[2]*vb[2]; vs_dot_xb = vs[0]*xb[0] + vs[1]*xb[1] + vs[2]*xb[2]; vb_dot_xs = vb[0]*xs[0] + vb[1]*xs[1] + vb[2]*xs[2]; vs_dot_xs = vs[0]*xs[0] + vs[1]*xs[1] + vs[2]*xs[2]; vb_dot_xb = vb[0]*xb[0] + vb[1]*xb[1] + vb[2]*xb[2]; xs_dot_xs = xs[0]*xs[0] + xs[1]*xs[1] + xs[2]*xs[2]; xb_dot_xb = xb[0]*xb[0] + xb[1]*xb[1] + xb[2]*xb[2]; xs_dot_xb = xs[0]*xb[0] + xs[1]*xb[1] + xs[2]*xb[2]; a = vs_dot_vs + vb_dot_vb - 2.0*vs_dot_vb; b = 2.0 * (vs_dot_xb + vb_dot_xs - vs_dot_xs - vb_dot_xb); c = xs_dot_xs + xb_dot_xb - 2.0*xs_dot_xb - big->radsq; double dt = (-b + sqrt(b*b - 4.0*a*c)) / (2.0*a); xscoll[0] = xs[0] - dt*vs[0]; xscoll[1] = xs[1] - dt*vs[1]; xscoll[2] = xs[2] - dt*vs[2]; xbcoll[0] = xb[0] - dt*vb[0]; xbcoll[1] = xb[1] - dt*vb[1]; xbcoll[2] = xb[2] - dt*vb[2]; norm[0] = xscoll[0] - xbcoll[0]; norm[1] = xscoll[1] - xbcoll[1]; norm[2] = xscoll[2] - xbcoll[2]; scale = 1.0/sqrt(norm[0]*norm[0] + norm[1]*norm[1] + norm[2]*norm[2]); norm[0] *= scale; norm[1] *= scale; norm[2] *= scale; return dt; } /* ---------------------------------------------------------------------- collision of SRD particle S with surface of spherical big particle B inexact because just push SRD to surface of big particle at end of step time of collision = end of step xscoll = collision pt = position of SRD at time of collision xbcoll = xb = position of big particle at time of collision norm = surface normal of collision pt at time of collision ------------------------------------------------------------------------- */ void FixSRD::collision_sphere_inexact(double *xs, double *xb, Big *big, double *xscoll, double *xbcoll, double *norm) { double scale; norm[0] = xs[0] - xb[0]; norm[1] = xs[1] - xb[1]; norm[2] = xs[2] - xb[2]; scale = 1.0/sqrt(norm[0]*norm[0] + norm[1]*norm[1] + norm[2]*norm[2]); norm[0] *= scale; norm[1] *= scale; norm[2] *= scale; xscoll[0] = xb[0] + big->radius*norm[0]; xscoll[1] = xb[1] + big->radius*norm[1]; xscoll[2] = xb[2] + big->radius*norm[2]; xbcoll[0] = xb[0]; xbcoll[1] = xb[1]; xbcoll[2] = xb[2]; } /* ---------------------------------------------------------------------- collision of SRD particle S with surface of ellipsoidal big particle B exact because compute time of collision dt = time previous to now at which collision occurs xscoll = collision pt = position of SRD at time of collision xbcoll = position of big particle at time of collision norm = surface normal of collision pt at time of collision ------------------------------------------------------------------------- */ double FixSRD::collision_ellipsoid_exact(double *xs, double *xb, double *vs, double *vb, Big *big, double *xscoll, double *xbcoll, double *norm) { double vs_vb[3],xs_xb[3],omega_ex[3],omega_ey[3],omega_ez[3]; double excoll[3],eycoll[3],ezcoll[3],delta[3],xbody[3],nbody[3]; double ax,bx,cx,ay,by,cy,az,bz,cz; double a,b,c,scale; double *omega = big->omega; double *ex = big->ex; double *ey = big->ey; double *ez = big->ez; vs_vb[0] = vs[0]-vb[0]; vs_vb[1] = vs[1]-vb[1]; vs_vb[2] = vs[2]-vb[2]; xs_xb[0] = xs[0]-xb[0]; xs_xb[1] = xs[1]-xb[1]; xs_xb[2] = xs[2]-xb[2]; MathExtra::cross3(omega,ex,omega_ex); MathExtra::cross3(omega,ey,omega_ey); MathExtra::cross3(omega,ez,omega_ez); ax = vs_vb[0]*omega_ex[0] + vs_vb[1]*omega_ex[1] + vs_vb[2]*omega_ex[2]; bx = -(vs_vb[0]*ex[0] + vs_vb[1]*ex[1] + vs_vb[2]*ex[2]); bx -= xs_xb[0]*omega_ex[0] + xs_xb[1]*omega_ex[1] + xs_xb[2]*omega_ex[2]; cx = xs_xb[0]*ex[0] + xs_xb[1]*ex[1] + xs_xb[2]*ex[2]; ay = vs_vb[0]*omega_ey[0] + vs_vb[1]*omega_ey[1] + vs_vb[2]*omega_ey[2]; by = -(vs_vb[0]*ey[0] + vs_vb[1]*ey[1] + vs_vb[2]*ey[2]); by -= xs_xb[0]*omega_ey[0] + xs_xb[1]*omega_ey[1] + xs_xb[2]*omega_ey[2]; cy = xs_xb[0]*ey[0] + xs_xb[1]*ey[1] + xs_xb[2]*ey[2]; az = vs_vb[0]*omega_ez[0] + vs_vb[1]*omega_ez[1] + vs_vb[2]*omega_ez[2]; bz = -(vs_vb[0]*ez[0] + vs_vb[1]*ez[1] + vs_vb[2]*ez[2]); bz -= xs_xb[0]*omega_ez[0] + xs_xb[1]*omega_ez[1] + xs_xb[2]*omega_ez[2]; cz = xs_xb[0]*ez[0] + xs_xb[1]*ez[1] + xs_xb[2]*ez[2]; a = (bx*bx + 2.0*ax*cx)*big->aradsqinv + (by*by + 2.0*ay*cy)*big->bradsqinv + (bz*bz + 2.0*az*cz)*big->cradsqinv; b = 2.0 * (bx*cx*big->aradsqinv + by*cy*big->bradsqinv + bz*cz*big->cradsqinv); c = cx*cx*big->aradsqinv + cy*cy*big->bradsqinv + cz*cz*big->cradsqinv - 1.0; double dt = (-b + sqrt(b*b - 4.0*a*c)) / (2.0*a); xscoll[0] = xs[0] - dt*vs[0]; xscoll[1] = xs[1] - dt*vs[1]; xscoll[2] = xs[2] - dt*vs[2]; xbcoll[0] = xb[0] - dt*vb[0]; xbcoll[1] = xb[1] - dt*vb[1]; xbcoll[2] = xb[2] - dt*vb[2]; // calculate normal to ellipsoid at collision pt // Excoll,Eycoll,Ezcoll = orientation of ellipsoid at collision time // nbody = normal in body frame of ellipsoid (Excoll,Eycoll,Ezcoll) // norm = normal in space frame // only worry about normalizing final norm vector excoll[0] = ex[0] - dt*(omega[1]*ex[2] - omega[2]*ex[1]); excoll[1] = ex[1] - dt*(omega[2]*ex[0] - omega[0]*ex[2]); excoll[2] = ex[2] - dt*(omega[0]*ex[1] - omega[1]*ex[0]); eycoll[0] = ey[0] - dt*(omega[1]*ey[2] - omega[2]*ey[1]); eycoll[1] = ey[1] - dt*(omega[2]*ey[0] - omega[0]*ey[2]); eycoll[2] = ey[2] - dt*(omega[0]*ey[1] - omega[1]*ey[0]); ezcoll[0] = ez[0] - dt*(omega[1]*ez[2] - omega[2]*ez[1]); ezcoll[1] = ez[1] - dt*(omega[2]*ez[0] - omega[0]*ez[2]); ezcoll[2] = ez[2] - dt*(omega[0]*ez[1] - omega[1]*ez[0]); MathExtra::sub3(xscoll,xbcoll,delta); MathExtra::transpose_matvec(excoll,eycoll,ezcoll,delta,xbody); nbody[0] = xbody[0]*big->aradsqinv; nbody[1] = xbody[1]*big->bradsqinv; nbody[2] = xbody[2]*big->cradsqinv; MathExtra::matvec(excoll,eycoll,ezcoll,nbody,norm); MathExtra::norm3(norm); return dt; } /* ---------------------------------------------------------------------- collision of SRD particle S with surface of ellipsoidal big particle B inexact because just push SRD to surface of big particle at end of step time of collision = end of step xscoll = collision pt = position of SRD at time of collision xbcoll = xb = position of big particle at time of collision norm = surface normal of collision pt at time of collision ------------------------------------------------------------------------- */ void FixSRD::collision_ellipsoid_inexact(double *xs, double *xb, Big *big, double *xscoll, double *xbcoll, double *norm) { double xs_xb[3],delta[3],xbody[3],nbody[3]; double *ex = big->ex; double *ey = big->ey; double *ez = big->ez; MathExtra::sub3(xs,xb,xs_xb); double x = MathExtra::dot3(xs_xb,ex); double y = MathExtra::dot3(xs_xb,ey); double z = MathExtra::dot3(xs_xb,ez); double scale = 1.0/sqrt(x*x*big->aradsqinv + y*y*big->bradsqinv + z*z*big->cradsqinv); x *= scale; y *= scale; z *= scale; xscoll[0] = x*ex[0] + y*ey[0] + z*ez[0] + xb[0]; xscoll[1] = x*ex[1] + y*ey[1] + z*ez[1] + xb[1]; xscoll[2] = x*ex[2] + y*ey[2] + z*ez[2] + xb[2]; xbcoll[0] = xb[0]; xbcoll[1] = xb[1]; xbcoll[2] = xb[2]; // calculate normal to ellipsoid at collision pt // nbody = normal in body frame of ellipsoid // norm = normal in space frame // only worry about normalizing final norm vector MathExtra::sub3(xscoll,xbcoll,delta); MathExtra::transpose_matvec(ex,ey,ez,delta,xbody); nbody[0] = xbody[0]*big->aradsqinv; nbody[1] = xbody[1]*big->bradsqinv; nbody[2] = xbody[2]*big->cradsqinv; MathExtra::matvec(ex,ey,ez,nbody,norm); MathExtra::norm3(norm); } /* ---------------------------------------------------------------------- collision of SRD particle S with surface of line big particle B exact because compute time of collision dt = time previous to now at which collision occurs xscoll = collision pt = position of SRD at time of collision xbcoll = position of big particle at time of collision norm = surface normal of collision pt at time of collision ------------------------------------------------------------------------- */ double FixSRD::collision_line_exact(double *xs, double *xb, double *vs, double *vb, Big *big, double dt_step, double *xscoll, double *xbcoll, double *norm) { xscoll[0] = xsc[0]; xscoll[1] = xsc[1]; xscoll[2] = 0.0; xbcoll[0] = xbc[0]; xbcoll[1] = xbc[1]; xbcoll[2] = 0.0; norm[0] = nbc[0]; norm[1] = nbc[1]; norm[2] = 0.0; return (1.0-tfraction)*dt_step; } /* ---------------------------------------------------------------------- collision of SRD particle S with surface of triangle big particle B exact because compute time of collision dt = time previous to now at which collision occurs xscoll = collision pt = position of SRD at time of collision xbcoll = position of big particle at time of collision norm = surface normal of collision pt at time of collision ------------------------------------------------------------------------- */ double FixSRD::collision_tri_exact(double *xs, double *xb, double *vs, double *vb, Big *big, double dt_step, double *xscoll, double *xbcoll, double *norm) { xscoll[0] = xsc[0]; xscoll[1] = xsc[1]; xscoll[2] = xsc[2]; xbcoll[0] = xbc[0]; xbcoll[1] = xbc[1]; xbcoll[2] = xbc[2]; norm[0] = nbc[0]; norm[1] = nbc[1]; norm[2] = nbc[2]; return (1.0-tfraction)*dt_step; } /* ---------------------------------------------------------------------- collision of SRD particle S with wall IWALL exact because compute time of collision dt = time previous to now at which collision occurs xscoll = collision pt = position of SRD at time of collision xbcoll = position of wall at time of collision norm = surface normal of collision pt at time of collision ------------------------------------------------------------------------- */ double FixSRD::collision_wall_exact(double *xs, int iwall, double *vs, double *xscoll, double *xbcoll, double *norm) { int dim = wallwhich[iwall] / 2; double dt = (xs[dim] - xwall[iwall]) / (vs[dim] - vwall[iwall]); xscoll[0] = xs[0] - dt*vs[0]; xscoll[1] = xs[1] - dt*vs[1]; xscoll[2] = xs[2] - dt*vs[2]; xbcoll[0] = xbcoll[1] = xbcoll[2] = 0.0; xbcoll[dim] = xwall[iwall] - dt*vwall[iwall]; int side = wallwhich[iwall] % 2; norm[0] = norm[1] = norm[2] = 0.0; if (side == 0) norm[dim] = 1.0; else norm[dim] = -1.0; return dt; } /* ---------------------------------------------------------------------- collision of SRD particle S with wall IWALL inexact because just push SRD to surface of wall at end of step time of collision = end of step xscoll = collision pt = position of SRD at time of collision xbcoll = position of wall at time of collision norm = surface normal of collision pt at time of collision ------------------------------------------------------------------------- */ void FixSRD::collision_wall_inexact(double *xs, int iwall, double *xscoll, double *xbcoll, double *norm) { int dim = wallwhich[iwall] / 2; xscoll[0] = xs[0]; xscoll[1] = xs[1]; xscoll[2] = xs[2]; xscoll[dim] = xwall[iwall]; xbcoll[0] = xbcoll[1] = xbcoll[2] = 0.0; xbcoll[dim] = xwall[iwall]; int side = wallwhich[iwall] % 2; norm[0] = norm[1] = norm[2] = 0.0; if (side == 0) norm[dim] = 1.0; else norm[dim] = -1.0; } /* ---------------------------------------------------------------------- SLIP collision with BIG particle with omega vs = velocity of SRD, vb = velocity of BIG xb = position of BIG, omega = rotation of BIG xsurf = collision pt on surf of BIG norm = unit normal from surface of BIG at collision pt v of BIG particle in direction of surf normal is added to v of SRD includes component due to rotation of BIG return vsnew of SRD ------------------------------------------------------------------------- */ void FixSRD::slip(double *vs, double *vb, double *xb, Big *big, double *xsurf, double *norm, double *vsnew) { double r1,r2,vnmag,vs_dot_n,vsurf_dot_n; double tangent[3],vsurf[3]; double *omega = big->omega; while (1) { r1 = sigma * random->gaussian(); r2 = sigma * random->gaussian(); vnmag = sqrt(r1*r1 + r2*r2); if (vnmag*vnmag <= vmaxsq) break; } vs_dot_n = vs[0]*norm[0] + vs[1]*norm[1] + vs[2]*norm[2]; tangent[0] = vs[0] - vs_dot_n*norm[0]; tangent[1] = vs[1] - vs_dot_n*norm[1]; tangent[2] = vs[2] - vs_dot_n*norm[2]; // vsurf = velocity of collision pt = translation/rotation of BIG particle // NOTE: for sphere could just use vsurf = vb, since w x (xsurf-xb) // is orthogonal to norm and thus doesn't contribute to vsurf_dot_n vsurf[0] = vb[0] + omega[1]*(xsurf[2]-xb[2]) - omega[2]*(xsurf[1]-xb[1]); vsurf[1] = vb[1] + omega[2]*(xsurf[0]-xb[0]) - omega[0]*(xsurf[2]-xb[2]); vsurf[2] = vb[2] + omega[0]*(xsurf[1]-xb[1]) - omega[1]*(xsurf[0]-xb[0]); vsurf_dot_n = vsurf[0]*norm[0] + vsurf[1]*norm[1] + vsurf[2]*norm[2]; vsnew[0] = (vnmag+vsurf_dot_n)*norm[0] + tangent[0]; vsnew[1] = (vnmag+vsurf_dot_n)*norm[1] + tangent[1]; vsnew[2] = (vnmag+vsurf_dot_n)*norm[2] + tangent[2]; } /* ---------------------------------------------------------------------- SLIP collision with wall IWALL vs = velocity of SRD norm = unit normal from WALL at collision pt v of WALL in direction of surf normal is added to v of SRD return vsnew of SRD ------------------------------------------------------------------------- */ void FixSRD::slip_wall(double *vs, int iwall, double *norm, double *vsnew) { double vs_dot_n,scale,r1,r2,vnmag,vtmag1,vtmag2; double tangent1[3],tangent2[3]; vs_dot_n = vs[0]*norm[0] + vs[1]*norm[1] + vs[2]*norm[2]; tangent1[0] = vs[0] - vs_dot_n*norm[0]; tangent1[1] = vs[1] - vs_dot_n*norm[1]; tangent1[2] = vs[2] - vs_dot_n*norm[2]; scale = 1.0/sqrt(tangent1[0]*tangent1[0] + tangent1[1]*tangent1[1] + tangent1[2]*tangent1[2]); tangent1[0] *= scale; tangent1[1] *= scale; tangent1[2] *= scale; tangent2[0] = norm[1]*tangent1[2] - norm[2]*tangent1[1]; tangent2[1] = norm[2]*tangent1[0] - norm[0]*tangent1[2]; tangent2[2] = norm[0]*tangent1[1] - norm[1]*tangent1[0]; while (1) { r1 = sigma * random->gaussian(); r2 = sigma * random->gaussian(); vnmag = sqrt(r1*r1 + r2*r2); vtmag1 = sigma * random->gaussian(); vtmag2 = sigma * random->gaussian(); if (vnmag*vnmag + vtmag1*vtmag1 + vtmag2*vtmag2 <= vmaxsq) break; } vsnew[0] = vnmag*norm[0] + vtmag1*tangent1[0] + vtmag2*tangent2[0]; vsnew[1] = vnmag*norm[1] + vtmag1*tangent1[1] + vtmag2*tangent2[1]; vsnew[2] = vnmag*norm[2] + vtmag1*tangent1[2] + vtmag2*tangent2[2]; // add in velocity of collision pt = velocity of wall int dim = wallwhich[iwall] / 2; vsnew[dim] += vwall[iwall]; } /* ---------------------------------------------------------------------- NO-SLIP collision with BIG particle including WALL vs = velocity of SRD, vb = velocity of BIG xb = position of BIG, omega = rotation of BIG xsurf = collision pt on surf of BIG norm = unit normal from surface of BIG at collision pt v of collision pt is added to v of SRD includes component due to rotation of BIG return vsnew of SRD ------------------------------------------------------------------------- */ void FixSRD::noslip(double *vs, double *vb, double *xb, Big *big, int iwall, double *xsurf, double *norm, double *vsnew) { double vs_dot_n,scale,r1,r2,vnmag,vtmag1,vtmag2; double tangent1[3],tangent2[3]; vs_dot_n = vs[0]*norm[0] + vs[1]*norm[1] + vs[2]*norm[2]; tangent1[0] = vs[0] - vs_dot_n*norm[0]; tangent1[1] = vs[1] - vs_dot_n*norm[1]; tangent1[2] = vs[2] - vs_dot_n*norm[2]; scale = 1.0/sqrt(tangent1[0]*tangent1[0] + tangent1[1]*tangent1[1] + tangent1[2]*tangent1[2]); tangent1[0] *= scale; tangent1[1] *= scale; tangent1[2] *= scale; tangent2[0] = norm[1]*tangent1[2] - norm[2]*tangent1[1]; tangent2[1] = norm[2]*tangent1[0] - norm[0]*tangent1[2]; tangent2[2] = norm[0]*tangent1[1] - norm[1]*tangent1[0]; while (1) { r1 = sigma * random->gaussian(); r2 = sigma * random->gaussian(); vnmag = sqrt(r1*r1 + r2*r2); vtmag1 = sigma * random->gaussian(); vtmag2 = sigma * random->gaussian(); if (vnmag*vnmag + vtmag1*vtmag1 + vtmag2*vtmag2 <= vmaxsq) break; } vsnew[0] = vnmag*norm[0] + vtmag1*tangent1[0] + vtmag2*tangent2[0]; vsnew[1] = vnmag*norm[1] + vtmag1*tangent1[1] + vtmag2*tangent2[1]; vsnew[2] = vnmag*norm[2] + vtmag1*tangent1[2] + vtmag2*tangent2[2]; // add in velocity of collision pt // for WALL: velocity of wall in one dim // else: translation/rotation of BIG particle if (big->type == WALL) { int dim = wallwhich[iwall] / 2; vsnew[dim] += vwall[iwall]; } else { double *omega = big->omega; vsnew[0] += vb[0] + omega[1]*(xsurf[2]-xb[2]) - omega[2]*(xsurf[1]-xb[1]); vsnew[1] += vb[1] + omega[2]*(xsurf[0]-xb[0]) - omega[0]*(xsurf[2]-xb[2]); vsnew[2] += vb[2] + omega[0]*(xsurf[1]-xb[1]) - omega[1]*(xsurf[0]-xb[0]); } } /* ---------------------------------------------------------------------- impart force and torque to BIG particle force on BIG particle = -dp/dt of SRD particle torque on BIG particle = r cross (-dp/dt) ------------------------------------------------------------------------- */ void FixSRD::force_torque(double *vsold, double *vsnew, double *xs, double *xb, double *fb, double *tb) { double dpdt[3],xs_xb[3]; double factor = mass_srd / dt_big / force->ftm2v; dpdt[0] = factor * (vsnew[0] - vsold[0]); dpdt[1] = factor * (vsnew[1] - vsold[1]); dpdt[2] = factor * (vsnew[2] - vsold[2]); fb[0] -= dpdt[0]; fb[1] -= dpdt[1]; fb[2] -= dpdt[2]; // no torque if SLIP collision and BIG is a sphere if (tb) { xs_xb[0] = xs[0]-xb[0]; xs_xb[1] = xs[1]-xb[1]; xs_xb[2] = xs[2]-xb[2]; tb[0] -= xs_xb[1]*dpdt[2] - xs_xb[2]*dpdt[1]; tb[1] -= xs_xb[2]*dpdt[0] - xs_xb[0]*dpdt[2]; tb[2] -= xs_xb[0]*dpdt[1] - xs_xb[1]*dpdt[0]; } } /* ---------------------------------------------------------------------- impart force to WALL force on WALL = -dp/dt of SRD particle ------------------------------------------------------------------------- */ void FixSRD::force_wall(double *vsold, double *vsnew, int iwall) { double dpdt[3]; double factor = mass_srd / dt_big / force->ftm2v; dpdt[0] = factor * (vsnew[0] - vsold[0]); dpdt[1] = factor * (vsnew[1] - vsold[1]); dpdt[2] = factor * (vsnew[2] - vsold[2]); fwall[iwall][0] -= dpdt[0]; fwall[iwall][1] -= dpdt[1]; fwall[iwall][2] -= dpdt[2]; } /* ---------------------------------------------------------------------- update SRD particle position & velocity & search bin assignment check if SRD moved outside of valid region if so, may overlap off-processor BIG particle ------------------------------------------------------------------------- */ int FixSRD::update_srd(int i, double dt, double *xscoll, double *vsnew, double *xs, double *vs) { int ix,iy,iz; vs[0] = vsnew[0]; vs[1] = vsnew[1]; vs[2] = vsnew[2]; xs[0] = xscoll[0] + dt*vsnew[0]; xs[1] = xscoll[1] + dt*vsnew[1]; xs[2] = xscoll[2] + dt*vsnew[2]; if (triclinic) domain->x2lamda(xs,xs); if (xs[0] < srdlo[0] || xs[0] > srdhi[0] || xs[1] < srdlo[1] || xs[1] > srdhi[1] || xs[2] < srdlo[2] || xs[2] > srdhi[2]) { if (screen) { error->warning(FLERR,"Fix srd particle moved outside valid domain"); fprintf(screen," particle %d on proc %d at timestep " BIGINT_FORMAT, atom->tag[i],me,update->ntimestep); fprintf(screen," xnew %g %g %g\n",xs[0],xs[1],xs[2]); fprintf(screen," srdlo/hi x %g %g\n",srdlo[0],srdhi[0]); fprintf(screen," srdlo/hi y %g %g\n",srdlo[1],srdhi[1]); fprintf(screen," srdlo/hi z %g %g\n",srdlo[2],srdhi[2]); } } if (triclinic) domain->lamda2x(xs,xs); ix = static_cast ((xs[0]-xblo2)*bininv2x); iy = static_cast ((xs[1]-yblo2)*bininv2y); iz = static_cast ((xs[2]-zblo2)*bininv2z); return iz*nbin2y*nbin2x + iy*nbin2x + ix; } /* ---------------------------------------------------------------------- setup all SRD parameters with big particles ------------------------------------------------------------------------- */ void FixSRD::parameterize() { // timesteps dt_big = update->dt; dt_srd = nevery * update->dt; // maxbigdiam,minbigdiam = max/min diameter of any big particle // big particle must either have radius > 0 or shape > 0 defined // apply radfactor at end 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 *radius = atom->radius; int *ellipsoid = atom->ellipsoid; int *line = atom->line; int *tri = atom->tri; int *mask = atom->mask; int nlocal = atom->nlocal; int any_ellipsoids = 0; int any_lines = 0; int any_tris = 0; maxbigdiam = 0.0; minbigdiam = BIG; for (int i = 0; i < nlocal; i++) if (mask[i] & biggroupbit) { if (radius && radius[i] > 0.0) { maxbigdiam = MAX(maxbigdiam,2.0*radius[i]); minbigdiam = MIN(minbigdiam,2.0*radius[i]); } else if (ellipsoid && ellipsoid[i] >= 0) { any_ellipsoids = 1; double *shape = ebonus[ellipsoid[i]].shape; maxbigdiam = MAX(maxbigdiam,2.0*shape[0]); maxbigdiam = MAX(maxbigdiam,2.0*shape[1]); maxbigdiam = MAX(maxbigdiam,2.0*shape[2]); minbigdiam = MIN(minbigdiam,2.0*shape[0]); minbigdiam = MIN(minbigdiam,2.0*shape[1]); minbigdiam = MIN(minbigdiam,2.0*shape[2]); } else if (line && line[i] >= 0) { any_lines = 1; double length = lbonus[line[i]].length; maxbigdiam = MAX(maxbigdiam,length); minbigdiam = MIN(minbigdiam,length); } else if (tri && tri[i] >= 0) { any_tris = 1; double length1 = MathExtra::len3(tbonus[tri[i]].c1); double length2 = MathExtra::len3(tbonus[tri[i]].c2); double length3 = MathExtra::len3(tbonus[tri[i]].c3); double length = MAX(length1,length2); length = MAX(length,length3); maxbigdiam = MAX(maxbigdiam,length); minbigdiam = MIN(minbigdiam,length); } else error->one(FLERR,"Big particle in fix srd cannot be point particle"); } double tmp = maxbigdiam; MPI_Allreduce(&tmp,&maxbigdiam,1,MPI_DOUBLE,MPI_MAX,world); tmp = minbigdiam; MPI_Allreduce(&tmp,&minbigdiam,1,MPI_DOUBLE,MPI_MIN,world); maxbigdiam *= radfactor; minbigdiam *= radfactor; int itmp = any_ellipsoids; MPI_Allreduce(&itmp,&any_ellipsoids,1,MPI_INT,MPI_MAX,world); itmp = any_lines; MPI_Allreduce(&itmp,&any_lines,1,MPI_INT,MPI_MAX,world); itmp = any_tris; MPI_Allreduce(&itmp,&any_tris,1,MPI_INT,MPI_MAX,world); if (any_lines && overlap == 0) error->all(FLERR,"Cannot use lines with fix srd unless overlap is set"); if (any_tris && overlap == 0) error->all(FLERR,"Cannot use tris with fix srd unless overlap is set"); // big particles are only torqued if ellipsoids/lines/tris or NOSLIP if (any_ellipsoids == 0 && any_lines == 0 && any_tris == 0 && collidestyle == SLIP) torqueflag = 0; else torqueflag = 1; // mass of SRD particles, require monodispersity double *rmass = atom->rmass; double *mass = atom->mass; int *type = atom->type; int flag = 0; mass_srd = 0.0; for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { if (rmass) { if (mass_srd == 0.0) mass_srd = rmass[i]; else if (rmass[i] != mass_srd) flag = 1; } else { if (mass_srd == 0.0) mass_srd = mass[type[i]]; else if (mass[type[i]] != mass_srd) flag = 1; } } int flagall; MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_MAX,world); if (flagall) error->all(FLERR,"Fix srd requires SRD particles all have same mass"); // set temperature and lamda of SRD particles from each other // lamda = dt_srd * sqrt(boltz * temperature_srd / mass_srd); if (lamdaflag == 0) lamda = dt_srd * sqrt(force->boltz*temperature_srd/mass_srd/force->mvv2e); else temperature_srd = force->mvv2e * (lamda/dt_srd)*(lamda/dt_srd) * mass_srd/force->boltz; // vmax = maximum velocity of an SRD particle // dmax = maximum distance an SRD can move = 4*lamda = vmax * dt_srd sigma = lamda/dt_srd; dmax = 4.0*lamda; vmax = dmax/dt_srd; vmaxsq = vmax*vmax; // volbig = total volume of all big particles // LINE/TRI particles have no volume // incorrect if part of rigid particles, so add fudge factor with WIDTH // apply radfactor to reduce final volume double volbig = 0.0; double WIDTH = 1.0; if (dimension == 3) { for (int i = 0; i < nlocal; i++) if (mask[i] & biggroupbit) { if (radius && radius[i] > 0.0) { double r = radfactor * radius[i]; volbig += 4.0/3.0*MY_PI * r*r*r;; } else if (ellipsoid && ellipsoid[i] >= 0) { double *shape = ebonus[ellipsoid[i]].shape; volbig += 4.0/3.0*MY_PI * shape[0]*shape[1]*shape[2] * radfactor*radfactor*radfactor; } else if (tri && tri[i] >= 0) { double *c1 = tbonus[tri[i]].c1; double *c2 = tbonus[tri[i]].c2; double *c3 = tbonus[tri[i]].c3; double c2mc1[3],c3mc1[3],cross[3]; MathExtra::sub3(c2,c1,c2mc1); MathExtra::sub3(c3,c1,c3mc1); MathExtra::cross3(c2mc1,c3mc1,cross); volbig += 0.5 * MathExtra::len3(cross); } } } else { for (int i = 0; i < nlocal; i++) if (mask[i] & biggroupbit) { if (radius && radius[i] > 0.0) { double r = radfactor * radius[i]; volbig += MY_PI * r*r; } else if (ellipsoid && ellipsoid[i] >= 0) { double *shape = ebonus[ellipsoid[i]].shape; volbig += MY_PI * shape[0]*shape[1] * radfactor*radfactor; } else if (line && line[i] >= 0) { double length = lbonus[line[i]].length; volbig += length * WIDTH; } } } tmp = volbig; MPI_Allreduce(&tmp,&volbig,1,MPI_DOUBLE,MPI_SUM,world); // particle counts bigint mbig = 0; if (bigexist) mbig = group->count(biggroup); bigint nsrd = group->count(igroup); // mass_big = total mass of all big particles mass_big = 0.0; for (int i = 0; i < nlocal; i++) if (mask[i] & biggroupbit) { if (rmass) mass_big += rmass[i]; else mass_big += mass[type[i]]; } tmp = mass_big; MPI_Allreduce(&tmp,&mass_big,1,MPI_DOUBLE,MPI_SUM,world); // mass density ratio = big / SRD double density_big = 0.0; if (bigexist) density_big = mass_big / volbig; double volsrd,density_srd; if (dimension == 3) { volsrd = (domain->xprd * domain->yprd * domain->zprd) - volbig; density_srd = nsrd * mass_srd / (domain->xprd*domain->yprd*domain->zprd - volbig); } else { volsrd = (domain->xprd * domain->yprd) - volbig; density_srd = nsrd * mass_srd / (domain->xprd*domain->yprd - volbig); } double mdratio = density_big/density_srd; // create grid for binning/rotating SRD particles from gridsrd setup_velocity_bins(); // binsize3 = binsize1 in box units (not lamda units for triclinic) double binsize3x = binsize1x; double binsize3y = binsize1y; double binsize3z = binsize1z; if (triclinic) { binsize3x = binsize1x * domain->xprd; binsize3y = binsize1y * domain->yprd; binsize3z = binsize1z * domain->zprd; } // srd_per_grid = # of SRD particles per SRD grid cell double ncell; if (dimension == 3) ncell = volsrd / (binsize3x*binsize3y*binsize3z); else ncell = volsrd / (binsize3x*binsize3y); srd_per_cell = (double) nsrd / ncell; // kinematic viscosity of SRD fluid // output in cm^2/sec units, converted by xxt2kmu double viscosity; if (dimension == 3) viscosity = gridsrd*gridsrd/(18.0*dt_srd) * (1.0-(1.0-exp(-srd_per_cell))/srd_per_cell) + (force->boltz*temperature_srd*dt_srd/(4.0*mass_srd*force->mvv2e)) * ((srd_per_cell+2.0)/(srd_per_cell-1.0)); else viscosity = (force->boltz*temperature_srd*dt_srd/(2.0*mass_srd*force->mvv2e)) * (srd_per_cell/(srd_per_cell-1.0 + exp(-srd_per_cell)) - 1.0) + (gridsrd*gridsrd)/(12.0*dt_srd) * ((srd_per_cell-1.0 + exp(-srd_per_cell))/srd_per_cell); viscosity *= force->xxt2kmu; // print SRD parameters if (me == 0) { if (screen) { fprintf(screen,"SRD info:\n"); fprintf(screen, " SRD/big particles = " BIGINT_FORMAT " " BIGINT_FORMAT "\n", nsrd,mbig); fprintf(screen," big particle diameter max/min = %g %g\n", maxbigdiam,minbigdiam); fprintf(screen," SRD temperature & lamda = %g %g\n", temperature_srd,lamda); fprintf(screen," SRD max distance & max velocity = %g %g\n",dmax,vmax); fprintf(screen," SRD grid counts: %d %d %d\n",nbin1x,nbin1y,nbin1z); fprintf(screen," SRD grid size: request, actual (xyz) = %g, %g %g %g\n", gridsrd,binsize3x,binsize3y,binsize3z); fprintf(screen," SRD per actual grid cell = %g\n",srd_per_cell); fprintf(screen," SRD viscosity = %g\n",viscosity); fprintf(screen," big/SRD mass density ratio = %g\n",mdratio); } if (logfile) { fprintf(logfile,"SRD info:\n"); fprintf(logfile, " SRD/big particles = " BIGINT_FORMAT " " BIGINT_FORMAT "\n", nsrd,mbig); fprintf(logfile," big particle diameter max/min = %g %g\n", maxbigdiam,minbigdiam); fprintf(logfile," SRD temperature & lamda = %g %g\n", temperature_srd,lamda); fprintf(logfile," SRD max distance & max velocity = %g %g\n",dmax,vmax); fprintf(logfile," SRD grid counts: %d %d %d\n",nbin1x,nbin1y,nbin1z); fprintf(logfile," SRD grid size: request, actual (xyz) = %g, %g %g %g\n", gridsrd,binsize3x,binsize3y,binsize3z); fprintf(logfile," SRD per actual grid cell = %g\n",srd_per_cell); fprintf(logfile," SRD viscosity = %g\n",viscosity); fprintf(logfile," big/SRD mass density ratio = %g\n",mdratio); } } // error if less than 1 SRD bin per processor in some dim if (nbin1x < comm->procgrid[0] || nbin1y < comm->procgrid[1] || nbin1z < comm->procgrid[2]) error->all(FLERR,"Fewer SRD bins than processors in some dimension"); // check if SRD bins are within tolerance for shape and size int tolflag = 0; if (binsize3y/binsize3x > 1.0+cubictol || binsize3x/binsize3y > 1.0+cubictol) tolflag = 1; if (dimension == 3) { if (binsize3z/binsize3x > 1.0+cubictol || binsize3x/binsize3z > 1.0+cubictol) tolflag = 1; } if (tolflag) { if (cubicflag == CUBIC_ERROR) error->all(FLERR,"SRD bins for fix srd are not cubic enough"); if (me == 0) error->warning(FLERR,"SRD bins for fix srd are not cubic enough"); } tolflag = 0; if (binsize3x/gridsrd > 1.0+cubictol || gridsrd/binsize3x > 1.0+cubictol) tolflag = 1; if (binsize3y/gridsrd > 1.0+cubictol || gridsrd/binsize3y > 1.0+cubictol) tolflag = 1; if (dimension == 3) { if (binsize3z/gridsrd > 1.0+cubictol || gridsrd/binsize3z > 1.0+cubictol) tolflag = 1; } if (tolflag) { if (cubicflag == CUBIC_ERROR) error->all(FLERR,"SRD bin size for fix srd differs from user request"); if (me == 0) error->warning(FLERR, "SRD bin size for fix srd differs from user request"); } // error if lamda < 0.6 of SRD grid size and no shifting allowed // turn on shifting in this case if allowed double maxgridsrd = MAX(binsize3x,binsize3y); if (dimension == 3) maxgridsrd = MAX(maxgridsrd,binsize3z); shiftflag = 0; if (lamda < 0.6*maxgridsrd && shiftuser == SHIFT_NO) error->all(FLERR,"Fix srd lamda must be >= 0.6 of SRD grid size"); else if (lamda < 0.6*maxgridsrd && shiftuser == SHIFT_POSSIBLE) { shiftflag = 1; if (me == 0) error->warning(FLERR,"SRD bin shifting turned on due to small lamda"); } else if (shiftuser == SHIFT_YES) shiftflag = 1; // warnings if (bigexist && maxgridsrd > 0.25 * minbigdiam && me == 0) error->warning(FLERR,"Fix srd grid size > 1/4 of big particle diameter"); if (viscosity < 0.0 && me == 0) error->warning(FLERR,"Fix srd viscosity < 0.0 due to low SRD density"); if (bigexist && dt_big*vmax > minbigdiam && me == 0) error->warning(FLERR,"Fix srd particles may move > big particle diameter"); } /* ---------------------------------------------------------------------- set static parameters of each big particle, owned and ghost called each reneighboring use radfactor in distance parameters as appropriate ------------------------------------------------------------------------- */ void FixSRD::big_static() { int i; double rad,arad,brad,crad,length,length1,length2,length3; double *shape,*c1,*c2,*c3; double c2mc1[3],c3mc1[3]; 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 *radius = atom->radius; int *ellipsoid = atom->ellipsoid; int *line = atom->line; int *tri = atom->tri; int *type = atom->type; double skinhalf = 0.5 * neighbor->skin; for (int k = 0; k < nbig; k++) { i = biglist[k].index; // sphere // set radius and radsq and cutoff based on radius if (radius && radius[i] > 0.0) { biglist[k].type = SPHERE; rad = radfactor*radius[i]; biglist[k].radius = rad; biglist[k].radsq = rad*rad; biglist[k].cutbinsq = (rad+skinhalf) * (rad+skinhalf); // ellipsoid // set abc radsqinv and cutoff based on max radius } else if (ellipsoid && ellipsoid[i] >= 0) { shape = ebonus[ellipsoid[i]].shape; biglist[k].type = ELLIPSOID; arad = radfactor*shape[0]; brad = radfactor*shape[1]; crad = radfactor*shape[2]; biglist[k].aradsqinv = 1.0/(arad*arad); biglist[k].bradsqinv = 1.0/(brad*brad); biglist[k].cradsqinv = 1.0/(crad*crad); rad = MAX(arad,brad); rad = MAX(rad,crad); biglist[k].cutbinsq = (rad+skinhalf) * (rad+skinhalf); // line // set length and cutoff based on 1/2 length } else if (line && line[i] >= 0) { length = lbonus[line[i]].length; biglist[k].type = LINE; biglist[k].length = length; rad = 0.5*length; biglist[k].cutbinsq = (rad+skinhalf) * (rad+skinhalf); // tri // set normbody based on c1,c2,c3 // set cutoff based on point furthest from centroid } else if (tri && tri[i] >= 0) { biglist[k].type = TRIANGLE; c1 = tbonus[tri[i]].c1; c2 = tbonus[tri[i]].c2; c3 = tbonus[tri[i]].c3; MathExtra::sub3(c2,c1,c2mc1); MathExtra::sub3(c3,c1,c3mc1); MathExtra::cross3(c2mc1,c3mc1,biglist[k].normbody); length1 = MathExtra::len3(c1); length2 = MathExtra::len3(c2); length3 = MathExtra::len3(c3); rad = MAX(length1,length2); rad = MAX(rad,length3); biglist[k].cutbinsq = (rad+skinhalf) * (rad+skinhalf); } } } /* ---------------------------------------------------------------------- set dynamic parameters of each big particle, owned and ghost called each timestep ------------------------------------------------------------------------- */ void FixSRD::big_dynamic() { int i; double *shape,*quat,*inertia; double inertiaone[3]; 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 *rmass = atom->rmass; int *ellipsoid = atom->ellipsoid; int *line = atom->line; int *tri = atom->tri; for (int k = 0; k < nbig; k++) { i = biglist[k].index; // sphere // set omega from atom->omega directly if (biglist[k].type == SPHERE) { biglist[k].omega[0] = omega[i][0]; biglist[k].omega[1] = omega[i][1]; biglist[k].omega[2] = omega[i][2]; // ellipsoid // set ex,ey,ez from quaternion // set omega from angmom & ex,ey,ez } else if (biglist[k].type == ELLIPSOID) { quat = ebonus[ellipsoid[i]].quat; MathExtra::q_to_exyz(quat,biglist[k].ex,biglist[k].ey,biglist[k].ez); shape = ebonus[ellipsoid[i]].shape; inertiaone[0] = EINERTIA*rmass[i] * (shape[1]*shape[1]+shape[2]*shape[2]); inertiaone[1] = EINERTIA*rmass[i] * (shape[0]*shape[0]+shape[2]*shape[2]); inertiaone[2] = EINERTIA*rmass[i] * (shape[0]*shape[0]+shape[1]*shape[1]); MathExtra::angmom_to_omega(angmom[i], biglist[k].ex,biglist[k].ey,biglist[k].ez, inertiaone,biglist[k].omega); // line // set omega from atom->omega directly } else if (biglist[k].type == LINE) { biglist[k].theta = lbonus[line[i]].theta; biglist[k].omega[0] = omega[i][0]; biglist[k].omega[1] = omega[i][1]; biglist[k].omega[2] = omega[i][2]; // tri // set ex,ey,ez from quaternion // set omega from angmom & ex,ey,ez // set unit space-frame norm from body-frame norm } else if (biglist[k].type == TRIANGLE) { quat = tbonus[tri[i]].quat; MathExtra::q_to_exyz(quat,biglist[k].ex,biglist[k].ey,biglist[k].ez); inertia = tbonus[tri[i]].inertia; MathExtra::angmom_to_omega(angmom[i], biglist[k].ex,biglist[k].ey,biglist[k].ez, inertia,biglist[k].omega); MathExtra::matvec(biglist[k].ex,biglist[k].ey,biglist[k].ez, biglist[k].normbody,biglist[k].norm); MathExtra::norm3(biglist[k].norm); } } } /* ---------------------------------------------------------------------- set bounds for big and SRD particle movement called at setup() and when box size changes (but not shape) ------------------------------------------------------------------------- */ void FixSRD::setup_bounds() { // triclinic scale factors // convert a real distance (perpendicular to box face) to a lamda distance double length0,length1,length2; if (triclinic) { double *h_inv = domain->h_inv; length0 = sqrt(h_inv[0]*h_inv[0] + h_inv[5]*h_inv[5] + h_inv[4]*h_inv[4]); length1 = sqrt(h_inv[1]*h_inv[1] + h_inv[3]*h_inv[3]); length2 = h_inv[2]; } // collision object = CO = big particle or wall // big particles can be owned or ghost or unknown, walls are all owned // dist_ghost = distance from sub-domain (SD) that // owned/ghost CO may move to before reneigh, // used to bound search bins in setup_search_bins() // dist_srd = distance from SD at which SRD could collide with unknown CO, // used to error check bounds of SRD movement after collisions via srdlo/hi // dist_srd_reneigh = distance from SD at which an SRD should trigger // a reneigh, b/c next SRD move might overlap with unknown CO, // used for SRD triggering of reneighboring via srdlo/hi_reneigh // onemove = max distance an SRD can move in one step // if big_particles (and possibly walls): // dist_ghost = cut + 1/2 skin due to moving away before reneigh // dist_srd = cut - 1/2 skin - 1/2 diam due to ghost CO moving towards // dist_reneigh = dist_srd - onemove // if walls and no big particles: // dist_ghost = 0.0, since not used // if no big particles or walls: // dist_ghost and dist_srd = 0.0, since not used since no search bins // dist_srd_reneigh = subsize - onemove = // max distance to move without being lost during comm->exchange() // subsize = perp distance between sub-domain faces (orthog or triclinic) double cut = MAX(neighbor->cutneighmax,comm->cutghostuser); double onemove = dt_big*vmax; if (bigexist) { dist_ghost = cut + 0.5*neighbor->skin; dist_srd = cut - 0.5*neighbor->skin - 0.5*maxbigdiam; dist_srd_reneigh = dist_srd - onemove; } else if (wallexist) { dist_ghost = 4*onemove; dist_srd = 4*onemove; dist_srd_reneigh = 4*onemove - onemove; } else { dist_ghost = dist_srd = 0.0; double subsize; if (triclinic == 0) { subsize = domain->prd[0]/comm->procgrid[0]; subsize = MIN(subsize,domain->prd[1]/comm->procgrid[1]); if (dimension == 3) subsize = MIN(subsize,domain->prd[2]/comm->procgrid[2]); } else { subsize = 1.0/comm->procgrid[0]/length0; subsize = MIN(subsize,1.0/comm->procgrid[1]/length1); if (dimension == 3) subsize = MIN(subsize,1.0/comm->procgrid[2]/length2); } dist_srd_reneigh = subsize - onemove; } // lo/hi = bbox on this proc which SRD particles must stay inside // lo/hi reneigh = bbox on this proc outside of which SRDs trigger a reneigh // for triclinic, these bbox are in lamda units if (triclinic == 0) { srdlo[0] = domain->sublo[0] - dist_srd; srdhi[0] = domain->subhi[0] + dist_srd; srdlo[1] = domain->sublo[1] - dist_srd; srdhi[1] = domain->subhi[1] + dist_srd; srdlo[2] = domain->sublo[2] - dist_srd; srdhi[2] = domain->subhi[2] + dist_srd; srdlo_reneigh[0] = domain->sublo[0] - dist_srd_reneigh; srdhi_reneigh[0] = domain->subhi[0] + dist_srd_reneigh; srdlo_reneigh[1] = domain->sublo[1] - dist_srd_reneigh; srdhi_reneigh[1] = domain->subhi[1] + dist_srd_reneigh; srdlo_reneigh[2] = domain->sublo[2] - dist_srd_reneigh; srdhi_reneigh[2] = domain->subhi[2] + dist_srd_reneigh; } else { srdlo[0] = domain->sublo_lamda[0] - dist_srd*length0; srdhi[0] = domain->subhi_lamda[0] + dist_srd*length0; srdlo[1] = domain->sublo_lamda[1] - dist_srd*length1; srdhi[1] = domain->subhi_lamda[1] + dist_srd*length1; srdlo[2] = domain->sublo_lamda[2] - dist_srd*length2; srdhi[2] = domain->subhi_lamda[2] + dist_srd*length2; srdlo_reneigh[0] = domain->sublo_lamda[0] - dist_srd_reneigh*length0; srdhi_reneigh[0] = domain->subhi_lamda[0] + dist_srd_reneigh*length0; srdlo_reneigh[1] = domain->sublo_lamda[1] - dist_srd_reneigh*length1; srdhi_reneigh[1] = domain->subhi_lamda[1] + dist_srd_reneigh*length1; srdlo_reneigh[2] = domain->sublo_lamda[2] - dist_srd_reneigh*length2; srdhi_reneigh[2] = domain->subhi_lamda[2] + dist_srd_reneigh*length2; } } /* ---------------------------------------------------------------------- setup bins used for binning SRD particles for velocity reset gridsrd = desired bin size also setup bin shifting parameters also setup comm of bins that straddle processor boundaries called at beginning of each run called every reneighbor if box size changes, but not if box shape changes ------------------------------------------------------------------------- */ void FixSRD::setup_velocity_bins() { // require integer # of bins across global domain nbin1x = static_cast (domain->xprd/gridsrd + 0.5); nbin1y = static_cast (domain->yprd/gridsrd + 0.5); nbin1z = static_cast (domain->zprd/gridsrd + 0.5); if (dimension == 2) nbin1z = 1; if (nbin1x == 0) nbin1x = 1; if (nbin1y == 0) nbin1y = 1; if (nbin1z == 0) nbin1z = 1; if (triclinic == 0) { binsize1x = domain->xprd / nbin1x; binsize1y = domain->yprd / nbin1y; binsize1z = domain->zprd / nbin1z; bininv1x = 1.0/binsize1x; bininv1y = 1.0/binsize1y; bininv1z = 1.0/binsize1z; } else { binsize1x = 1.0 / nbin1x; binsize1y = 1.0 / nbin1y; binsize1z = 1.0 / nbin1z; bininv1x = nbin1x; bininv1y = nbin1y; bininv1z = nbin1z; } nbins1 = nbin1x*nbin1y*nbin1z; // setup two shifts, 0 = no shift, 1 = shift // initialize no shift case since static // shift case is dynamic, has to be initialized each time shift occurs // setup_velocity_shift allocates memory for vbin and sendlist/recvlist double *boxlo; if (triclinic == 0) boxlo = domain->boxlo; else boxlo = domain->boxlo_lamda; shifts[0].corner[0] = boxlo[0]; shifts[0].corner[1] = boxlo[1]; shifts[0].corner[2] = boxlo[2]; setup_velocity_shift(0,0); shifts[1].corner[0] = boxlo[0]; shifts[1].corner[1] = boxlo[1]; shifts[1].corner[2] = boxlo[2]; setup_velocity_shift(1,0); // allocate binhead based on max # of bins in either shift int max = shifts[0].nbins; max = MAX(max,shifts[1].nbins); if (max > maxbin1) { memory->destroy(binhead); maxbin1 = max; memory->create(binhead,max,"fix/srd:binhead"); } // allocate sbuf,rbuf based on biggest bin message max = 0; for (int ishift = 0; ishift < 2; ishift++) for (int iswap = 0; iswap < 2*dimension; iswap++) { max = MAX(max,shifts[ishift].bcomm[iswap].nsend); max = MAX(max,shifts[ishift].bcomm[iswap].nrecv); } if (max > maxbuf) { memory->destroy(sbuf1); memory->destroy(sbuf2); memory->destroy(rbuf1); memory->destroy(rbuf2); maxbuf = max; memory->create(sbuf1,max*VBINSIZE,"fix/srd:sbuf"); memory->create(sbuf2,max*VBINSIZE,"fix/srd:sbuf"); memory->create(rbuf1,max*VBINSIZE,"fix/srd:rbuf"); memory->create(rbuf2,max*VBINSIZE,"fix/srd:rbuf"); } // commflag = 1 if any comm required due to bins overlapping proc boundaries shifts[0].commflag = 0; if (nbin1x % comm->procgrid[0]) shifts[0].commflag = 1; if (nbin1y % comm->procgrid[1]) shifts[0].commflag = 1; if (nbin1z % comm->procgrid[2]) shifts[0].commflag = 1; shifts[1].commflag = 1; } /* ---------------------------------------------------------------------- setup velocity shift parameters set binlo[]/binhi[] and nbins,nbinx,nbiny,nbinz for this proc set bcomm[6] params based on bin overlaps with proc boundaries no comm of bins across non-periodic global boundaries set vbin owner flags for bins I am owner of ishift = 0, dynamic = 0: set all settings since are static allocate and set bcomm params and vbins do not comm bins that align with proc boundaries ishift = 1, dynamic = 0: set max bounds on bin counts and message sizes allocate and set bcomm params and vbins based on max bounds other settings will later change dynamically ishift = 1, dynamic = 1: set actual bin bounds and counts for specific shift set bcomm params and vbins (already allocated) called by setup_velocity_bins() and reset_velocities() ------------------------------------------------------------------------- */ void FixSRD::setup_velocity_shift(int ishift, int dynamic) { int i,j,k,m,id,nsend; int *sendlist; BinComm *first,*second; BinAve *vbin; double *sublo,*subhi; if (triclinic == 0) { sublo = domain->sublo; subhi = domain->subhi; } else { sublo = domain->sublo_lamda; subhi = domain->subhi_lamda; } int *binlo = shifts[ishift].binlo; int *binhi = shifts[ishift].binhi; double *corner = shifts[ishift].corner; int *procgrid = comm->procgrid; int *myloc = comm->myloc; binlo[0] = static_cast ((sublo[0]-corner[0])*bininv1x); binlo[1] = static_cast ((sublo[1]-corner[1])*bininv1y); binlo[2] = static_cast ((sublo[2]-corner[2])*bininv1z); if (dimension == 2) shifts[ishift].binlo[2] = 0; binhi[0] = static_cast ((subhi[0]-corner[0])*bininv1x); binhi[1] = static_cast ((subhi[1]-corner[1])*bininv1y); binhi[2] = static_cast ((subhi[2]-corner[2])*bininv1z); if (dimension == 2) shifts[ishift].binhi[2] = 0; if (ishift == 0) { if (myloc[0]*nbin1x % procgrid[0] == 0) binlo[0] = myloc[0]*nbin1x/procgrid[0]; if (myloc[1]*nbin1y % procgrid[1] == 0) binlo[1] = myloc[1]*nbin1y/procgrid[1]; if (myloc[2]*nbin1z % procgrid[2] == 0) binlo[2] = myloc[2]*nbin1z/procgrid[2]; if ((myloc[0]+1)*nbin1x % procgrid[0] == 0) binhi[0] = (myloc[0]+1)*nbin1x/procgrid[0] - 1; if ((myloc[1]+1)*nbin1y % procgrid[1] == 0) binhi[1] = (myloc[1]+1)*nbin1y/procgrid[1] - 1; if ((myloc[2]+1)*nbin1z % procgrid[2] == 0) binhi[2] = (myloc[2]+1)*nbin1z/procgrid[2] - 1; } int nbinx = binhi[0] - binlo[0] + 1; int nbiny = binhi[1] - binlo[1] + 1; int nbinz = binhi[2] - binlo[2] + 1; // allow for one extra bin if shifting will occur if (ishift == 1 && dynamic == 0) { nbinx++; nbiny++; if (dimension == 3) nbinz++; } int nbins = nbinx*nbiny*nbinz; int nbinxy = nbinx*nbiny; int nbinsq = nbinx*nbiny; nbinsq = MAX(nbiny*nbinz,nbinsq); nbinsq = MAX(nbinx*nbinz,nbinsq); shifts[ishift].nbins = nbins; shifts[ishift].nbinx = nbinx; shifts[ishift].nbiny = nbiny; shifts[ishift].nbinz = nbinz; int reallocflag = 0; if (dynamic == 0 && nbinsq > shifts[ishift].maxbinsq) { shifts[ishift].maxbinsq = nbinsq; reallocflag = 1; } // bcomm neighbors // first = send in lo direction, recv from hi direction // second = send in hi direction, recv from lo direction if (dynamic == 0) { shifts[ishift].bcomm[0].sendproc = comm->procneigh[0][0]; shifts[ishift].bcomm[0].recvproc = comm->procneigh[0][1]; shifts[ishift].bcomm[1].sendproc = comm->procneigh[0][1]; shifts[ishift].bcomm[1].recvproc = comm->procneigh[0][0]; shifts[ishift].bcomm[2].sendproc = comm->procneigh[1][0]; shifts[ishift].bcomm[2].recvproc = comm->procneigh[1][1]; shifts[ishift].bcomm[3].sendproc = comm->procneigh[1][1]; shifts[ishift].bcomm[3].recvproc = comm->procneigh[1][0]; shifts[ishift].bcomm[4].sendproc = comm->procneigh[2][0]; shifts[ishift].bcomm[4].recvproc = comm->procneigh[2][1]; shifts[ishift].bcomm[5].sendproc = comm->procneigh[2][1]; shifts[ishift].bcomm[5].recvproc = comm->procneigh[2][0]; } // set nsend,nrecv and sendlist,recvlist for each swap in x,y,z // set nsend,nrecv = 0 if static bins align with proc boundary // or to prevent dynamic bin swapping across non-periodic global boundary // allocate sendlist,recvlist only for dynamic = 0 first = &shifts[ishift].bcomm[0]; second = &shifts[ishift].bcomm[1]; first->nsend = first->nrecv = second->nsend = second->nrecv = nbiny*nbinz; if (ishift == 0) { if (myloc[0]*nbin1x % procgrid[0] == 0) first->nsend = second->nrecv = 0; if ((myloc[0]+1)*nbin1x % procgrid[0] == 0) second->nsend = first->nrecv = 0; } else { if (domain->xperiodic == 0) { if (myloc[0] == 0) first->nsend = second->nrecv = 0; if (myloc[0] == procgrid[0]-1) second->nsend = first->nrecv = 0; } } if (reallocflag) { memory->destroy(first->sendlist); memory->destroy(first->recvlist); memory->destroy(second->sendlist); memory->destroy(second->recvlist); memory->create(first->sendlist,nbinsq,"fix/srd:sendlist"); memory->create(first->recvlist,nbinsq,"fix/srd:sendlist"); memory->create(second->sendlist,nbinsq,"fix/srd:sendlist"); memory->create(second->recvlist,nbinsq,"fix/srd:sendlist"); } m = 0; i = 0; for (j = 0; j < nbiny; j++) for (k = 0; k < nbinz; k++) { id = k*nbinxy + j*nbinx + i; first->sendlist[m] = second->recvlist[m] = id; m++; } m = 0; i = nbinx-1; for (j = 0; j < nbiny; j++) for (k = 0; k < nbinz; k++) { id = k*nbinxy + j*nbinx + i; second->sendlist[m] = first->recvlist[m] = id; m++; } first = &shifts[ishift].bcomm[2]; second = &shifts[ishift].bcomm[3]; first->nsend = first->nrecv = second->nsend = second->nrecv = nbinx*nbinz; if (ishift == 0) { if (myloc[1]*nbin1y % procgrid[1] == 0) first->nsend = second->nrecv = 0; if ((myloc[1]+1)*nbin1y % procgrid[1] == 0) second->nsend = first->nrecv = 0; } else { if (domain->yperiodic == 0) { if (myloc[1] == 0) first->nsend = second->nrecv = 0; if (myloc[1] == procgrid[1]-1) second->nsend = first->nrecv = 0; } } if (reallocflag) { memory->destroy(first->sendlist); memory->destroy(first->recvlist); memory->destroy(second->sendlist); memory->destroy(second->recvlist); memory->create(first->sendlist,nbinsq,"fix/srd:sendlist"); memory->create(first->recvlist,nbinsq,"fix/srd:sendlist"); memory->create(second->sendlist,nbinsq,"fix/srd:sendlist"); memory->create(second->recvlist,nbinsq,"fix/srd:sendlist"); } m = 0; j = 0; for (i = 0; i < nbinx; i++) for (k = 0; k < nbinz; k++) { id = k*nbinxy + j*nbinx + i; first->sendlist[m] = second->recvlist[m] = id; m++; } m = 0; j = nbiny-1; for (i = 0; i < nbinx; i++) for (k = 0; k < nbinz; k++) { id = k*nbinxy + j*nbinx + i; second->sendlist[m] = first->recvlist[m] = id; m++; } if (dimension == 3) { first = &shifts[ishift].bcomm[4]; second = &shifts[ishift].bcomm[5]; first->nsend = first->nrecv = second->nsend = second->nrecv = nbinx*nbiny; if (ishift == 0) { if (myloc[2]*nbin1z % procgrid[2] == 0) first->nsend = second->nrecv = 0; if ((myloc[2]+1)*nbin1z % procgrid[2] == 0) second->nsend = first->nrecv = 0; } else { if (domain->zperiodic == 0) { if (myloc[2] == 0) first->nsend = second->nrecv = 0; if (myloc[2] == procgrid[2]-1) second->nsend = first->nrecv = 0; } } if (reallocflag) { memory->destroy(first->sendlist); memory->destroy(first->recvlist); memory->destroy(second->sendlist); memory->destroy(second->recvlist); memory->create(first->sendlist,nbinx*nbiny,"fix/srd:sendlist"); memory->create(first->recvlist,nbinx*nbiny,"fix/srd:sendlist"); memory->create(second->sendlist,nbinx*nbiny,"fix/srd:sendlist"); memory->create(second->recvlist,nbinx*nbiny,"fix/srd:sendlist"); } m = 0; k = 0; for (i = 0; i < nbinx; i++) for (j = 0; j < nbiny; j++) { id = k*nbinxy + j*nbinx + i; first->sendlist[m] = second->recvlist[m] = id; m++; } m = 0; k = nbinz-1; for (i = 0; i < nbinx; i++) for (j = 0; j < nbiny; j++) { id = k*nbinxy + j*nbinx + i; second->sendlist[m] = first->recvlist[m] = id; m++; } } // allocate vbins, only for dynamic = 0 if (dynamic == 0 && nbins > shifts[ishift].maxvbin) { memory->destroy(shifts[ishift].vbin); shifts[ishift].maxvbin = nbins; shifts[ishift].vbin = (BinAve *) memory->smalloc(nbins*sizeof(BinAve),"fix/srd:vbin"); } // for vbins I own, set owner = 1 // if bin never sent to anyone, I own it // if bin sent to lower numbered proc, I do not own it // if bin sent to self, I do not own it on even swap (avoids double counting) vbin = shifts[ishift].vbin; for (i = 0; i < nbins; i++) vbin[i].owner = 1; for (int iswap = 0; iswap < 2*dimension; iswap++) { if (shifts[ishift].bcomm[iswap].sendproc > me) continue; if (shifts[ishift].bcomm[iswap].sendproc == me && iswap % 2 == 0) continue; nsend = shifts[ishift].bcomm[iswap].nsend; sendlist = shifts[ishift].bcomm[iswap].sendlist; for (m = 0; m < nsend; m++) vbin[sendlist[m]].owner = 0; } // if tstat and deformflag: // set xctr for all nbins in lamda units so can later compute vstream of bin if (tstat && deformflag) { m = 0; for (k = 0; k < nbinz; k++) for (j = 0; j < nbiny; j++) for (i = 0; i < nbinx; i++) { vbin[m].xctr[0] = corner[0] + (i+binlo[0]+0.5)/nbin1x; vbin[m].xctr[1] = corner[1] + (j+binlo[1]+0.5)/nbin1y; vbin[m].xctr[2] = corner[2] + (k+binlo[2]+0.5)/nbin1z; m++; } } } /* ---------------------------------------------------------------------- setup bins used for big and SRD particle searching gridsearch = desired bin size bins are orthogonal whether simulation box is orthogonal or triclinic for orthogonal boxes, called at each setup since cutghost may change for triclinic boxes, called at every reneigh, since tilt may change sets the following: nbin2 xyz = # of bins in each dim binsize2 and bininv2 xyz = size of bins in each dim xyz blo2 = origin of bins ------------------------------------------------------------------------- */ void FixSRD::setup_search_bins() { // subboxlo/hi = real space bbox which // owned/ghost big particles or walls can be in // start with bounding box for my sub-domain, add dist_ghost // for triclinic, need to: // a) convert dist_ghost to lamda space via length012 // b) lo/hi = sub-domain big particle bbox in lamda space // c) convert lo/hi to real space bounding box via domain->bbox() // similar to neighbor::setup_bins() and comm::cutghost[] calculation double subboxlo[3],subboxhi[3]; if (triclinic == 0) { subboxlo[0] = domain->sublo[0] - dist_ghost; subboxlo[1] = domain->sublo[1] - dist_ghost; subboxlo[2] = domain->sublo[2] - dist_ghost; subboxhi[0] = domain->subhi[0] + dist_ghost; subboxhi[1] = domain->subhi[1] + dist_ghost; subboxhi[2] = domain->subhi[2] + dist_ghost; } else { double *h_inv = domain->h_inv; double length0,length1,length2; length0 = sqrt(h_inv[0]*h_inv[0] + h_inv[5]*h_inv[5] + h_inv[4]*h_inv[4]); length1 = sqrt(h_inv[1]*h_inv[1] + h_inv[3]*h_inv[3]); length2 = h_inv[2]; double lo[3],hi[3]; lo[0] = domain->sublo_lamda[0] - dist_ghost*length0; lo[1] = domain->sublo_lamda[1] - dist_ghost*length1; lo[2] = domain->sublo_lamda[2] - dist_ghost*length2; hi[0] = domain->subhi_lamda[0] + dist_ghost*length0; hi[1] = domain->subhi_lamda[1] + dist_ghost*length1; hi[2] = domain->subhi_lamda[2] + dist_ghost*length2; domain->bbox(lo,hi,subboxlo,subboxhi); } // require integer # of bins for that volume nbin2x = static_cast ((subboxhi[0] - subboxlo[0]) / gridsearch); nbin2y = static_cast ((subboxhi[1] - subboxlo[1]) / gridsearch); nbin2z = static_cast ((subboxhi[2] - subboxlo[2]) / gridsearch); if (dimension == 2) nbin2z = 1; if (nbin2x == 0) nbin2x = 1; if (nbin2y == 0) nbin2y = 1; if (nbin2z == 0) nbin2z = 1; binsize2x = (subboxhi[0] - subboxlo[0]) / nbin2x; binsize2y = (subboxhi[1] - subboxlo[1]) / nbin2y; binsize2z = (subboxhi[2] - subboxlo[2]) / nbin2z; bininv2x = 1.0/binsize2x; bininv2y = 1.0/binsize2y; bininv2z = 1.0/binsize2z; // add bins on either end due to extent of big particles // radmax = max distance from central bin that biggest particle overlaps // includes skin movement // nx,ny,nz = max # of bins to search away from central bin double radmax = 0.5*maxbigdiam + 0.5*neighbor->skin; int nx = static_cast (radmax/binsize2x) + 1; int ny = static_cast (radmax/binsize2y) + 1; int nz = static_cast (radmax/binsize2z) + 1; if (dimension == 2) nz = 0; nbin2x += 2*nx; nbin2y += 2*ny; nbin2z += 2*nz; xblo2 = subboxlo[0] - nx*binsize2x; yblo2 = subboxlo[1] - ny*binsize2y; zblo2 = subboxlo[2] - nz*binsize2z; if (dimension == 2) zblo2 = domain->boxlo[2]; // allocate bins // first deallocate previous bins if necessary nbins2 = nbin2x*nbin2y*nbin2z; if (nbins2 > maxbin2) { memory->destroy(nbinbig); memory->destroy(binbig); maxbin2 = nbins2; memory->create(nbinbig,nbins2,"fix/srd:nbinbig"); memory->create(binbig,nbins2,ATOMPERBIN,"fix/srd:binbig"); } } /* ---------------------------------------------------------------------- compute stencil of bin offsets for a big particle overlapping search bins ------------------------------------------------------------------------- */ void FixSRD::setup_search_stencil() { // radmax = max distance from central bin that any big particle overlaps // includes skin movement // nx,ny,nz = max # of bins to search away from central bin double radmax = 0.5*maxbigdiam + 0.5*neighbor->skin; double radsq = radmax*radmax; int nx = static_cast (radmax/binsize2x) + 1; int ny = static_cast (radmax/binsize2y) + 1; int nz = static_cast (radmax/binsize2z) + 1; if (dimension == 2) nz = 0; // allocate stencil array // deallocate previous stencil if necessary int max = (2*nx+1) * (2*ny+1) * (2*nz+1); if (max > maxstencil) { memory->destroy(stencil); maxstencil = max; memory->create(stencil,max,4,"fix/srd:stencil"); } // loop over all bins // add bin to stencil: // if distance of closest corners of bin and central bin is within cutoff nstencil = 0; for (int k = -nz; k <= nz; k++) for (int j = -ny; j <= ny; j++) for (int i = -nx; i <= nx; i++) if (bin_bin_distance(i,j,k) < radsq) { stencil[nstencil][0] = i; stencil[nstencil][1] = j; stencil[nstencil][2] = k; stencil[nstencil][3] = k*nbin2y*nbin2x + j*nbin2x + i; nstencil++; } } /* ---------------------------------------------------------------------- compute closest squared distance between point x and bin ibin ------------------------------------------------------------------------- */ double FixSRD::point_bin_distance(double *x, int i, int j, int k) { double delx,dely,delz; double xlo = xblo2 + i*binsize2x; double xhi = xlo + binsize2x; double ylo = yblo2 + j*binsize2y; double yhi = ylo + binsize2y; double zlo = zblo2 + k*binsize2z; double zhi = zlo + binsize2z; if (x[0] < xlo) delx = xlo - x[0]; else if (x[0] > xhi) delx = x[0] - xhi; else delx = 0.0; if (x[1] < ylo) dely = ylo - x[1]; else if (x[1] > yhi) dely = x[1] - yhi; else dely = 0.0; if (x[2] < zlo) delz = zlo - x[2]; else if (x[2] > zhi) delz = x[2] - zhi; else delz = 0.0; return (delx*delx + dely*dely + delz*delz); } /* ---------------------------------------------------------------------- compute closest squared distance between 2 bins central bin (0,0,0) and bin (i,j,k) ------------------------------------------------------------------------- */ double FixSRD::bin_bin_distance(int i, int j, int k) { double delx,dely,delz; if (i > 0) delx = (i-1)*binsize2x; else if (i == 0) delx = 0.0; else delx = (i+1)*binsize2x; if (j > 0) dely = (j-1)*binsize2y; else if (j == 0) dely = 0.0; else dely = (j+1)*binsize2y; if (k > 0) delz = (k-1)*binsize2z; else if (k == 0) delz = 0.0; else delz = (k+1)*binsize2z; return (delx*delx + dely*dely + delz*delz); } /* ---------------------------------------------------------------------- */ int FixSRD::pack_reverse_comm(int n, int first, double *buf) { int i,m,last; m = 0; last = first + n; if (torqueflag) { for (i = first; i < last; i++) { buf[m++] = flocal[i][0]; buf[m++] = flocal[i][1]; buf[m++] = flocal[i][2]; buf[m++] = tlocal[i][0]; buf[m++] = tlocal[i][1]; buf[m++] = tlocal[i][2]; } } else { for (i = first; i < last; i++) { buf[m++] = flocal[i][0]; buf[m++] = flocal[i][1]; buf[m++] = flocal[i][2]; } } return comm_reverse; } /* ---------------------------------------------------------------------- */ void FixSRD::unpack_reverse_comm(int n, int *list, double *buf) { int i,j,m; m = 0; if (torqueflag) { for (i = 0; i < n; i++) { j = list[i]; flocal[j][0] += buf[m++]; flocal[j][1] += buf[m++]; flocal[j][2] += buf[m++]; tlocal[j][0] += buf[m++]; tlocal[j][1] += buf[m++]; tlocal[j][2] += buf[m++]; } } else { for (i = 0; i < n; i++) { j = list[i]; flocal[j][0] += buf[m++]; flocal[j][1] += buf[m++]; flocal[j][2] += buf[m++]; } } } /* ---------------------------------------------------------------------- SRD stats ------------------------------------------------------------------------- */ double FixSRD::compute_vector(int n) { // only sum across procs one time if (stats_flag == 0) { stats[0] = ncheck; stats[1] = ncollide; stats[2] = nbounce; stats[3] = ninside; stats[4] = nrescale; stats[5] = nbins2; stats[6] = nbins1; stats[7] = srd_bin_count; stats[8] = srd_bin_temp; stats[9] = bouncemaxnum; stats[10] = bouncemax; stats[11] = reneighcount; MPI_Allreduce(stats,stats_all,10,MPI_DOUBLE,MPI_SUM,world); MPI_Allreduce(&stats[10],&stats_all[10],1,MPI_DOUBLE,MPI_MAX,world); if (stats_all[7] != 0.0) stats_all[8] /= stats_all[7]; stats_all[6] /= nprocs; stats_flag = 1; } return stats_all[n]; } /* ---------------------------------------------------------------------- */ void FixSRD::velocity_stats(int groupnum) { int bitmask = group->bitmask[groupnum]; double **v = atom->v; int *mask = atom->mask; int nlocal = atom->nlocal; double vone; double vave = 0.0; double vmax = 0.0; for (int i = 0; i < nlocal; i++) if (mask[i] & bitmask) { vone = sqrt(v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]); vave += vone; if (vone > vmax) vmax = vone; } double all; MPI_Allreduce(&vave,&all,1,MPI_DOUBLE,MPI_SUM,world); double count = group->count(groupnum); if (count != 0.0) vave = all/count; else vave = 0.0; MPI_Allreduce(&vmax,&all,1,MPI_DOUBLE,MPI_MAX,world); vmax = all; if (me == 0) { if (screen) fprintf(screen," ave/max %s velocity = %g %g\n", group->names[groupnum],vave,vmax); if (logfile) fprintf(logfile," ave/max %s velocity = %g %g\n", group->names[groupnum],vave,vmax); } } /* ---------------------------------------------------------------------- */ double FixSRD::newton_raphson(double t1, double t2) { double f1,f2,df,tlo,thi; lineside(t1,f1,df); if (f1 < 0.0) { tlo = t1; thi = t2; } else { thi = t1; tlo = t2; } double f; double t = 0.5*(t1+t2); double dtold = fabs(t2-t1); double dt = dtold; lineside(t,f,df); double temp; for (int i = 0; i < MAXITER; i++) { if ((((t-thi)*df - f)*((t-tlo)*df - f) > 0.0) || (fabs(2.0*f) > fabs(dtold*df))) { dtold = dt; dt = 0.5 * (thi-tlo); t = tlo + dt; if (tlo == t) return t; } else { dtold = dt; dt = f / df; temp = t; t -= dt; if (temp == t) return t; } if (fabs(dt) < TOLERANCE) return t; lineside(t,f,df); if (f < 0.0) tlo = t; else thi = t; } return t; } /* ---------------------------------------------------------------------- */ void FixSRD::lineside(double t, double &f, double &df) { double p[2],c[2]; p[0] = xs0[0] + (xs1[0]-xs0[0])*t; p[1] = xs0[1] + (xs1[1]-xs0[1])*t; c[0] = xb0[0] + (xb1[0]-xb0[0])*t; c[1] = xb0[1] + (xb1[1]-xb0[1])*t; double dtheta = theta1 - theta0; double theta = theta0 + dtheta*t; double cosT = cos(theta); double sinT = sin(theta); f = (p[1]-c[1]) * cosT - (p[0]-c[0]) * sinT; df = ((xs1[1]-xs0[1]) - (xb1[1]-xb0[1]))*cosT - (p[1]-c[1])*sinT*dtheta - ((xs1[0]-xs0[0]) - (xb1[0]-xb0[0]))*sinT - (p[0]-c[0])*cosT*dtheta; } /* ---------------------------------------------------------------------- */ void FixSRD::triside(double t, double &f, double &df) { double p[2],c[2]; p[0] = xs0[0] + (xs1[0]-xs0[0])*t; p[1] = xs0[1] + (xs1[1]-xs0[1])*t; c[0] = xb0[0] + (xb1[0]-xb0[0])*t; c[1] = xb0[1] + (xb1[1]-xb0[1])*t; double dtheta = theta1 - theta0; double theta = theta0 + dtheta*t; double cosT = cos(theta); double sinT = sin(theta); f = (p[1]-c[1]) * cosT - (p[0]-c[0]) * sinT; df = ((xs1[1]-xs0[1]) - (xb1[1]-xb0[1]))*cosT - (p[1]-c[1])*sinT*dtheta - ((xs1[0]-xs0[0]) - (xb1[0]-xb0[0]))*sinT - (p[0]-c[0])*cosT*dtheta; } /* ---------------------------------------------------------------------- */ double FixSRD::memory_usage() { double bytes = 0.0; bytes += (shifts[0].nbins + shifts[1].nbins) * sizeof(BinAve); bytes += nmax * sizeof(int); if (bigexist) { bytes += nbins2 * sizeof(int); bytes += nbins2*ATOMPERBIN * sizeof(int); } bytes += nmax * sizeof(int); return bytes; } /* ---------------------------------------------------------------------- useful debugging functions ------------------------------------------------------------------------- */ double FixSRD::distance(int i, int j) { double dx = atom->x[i][0] - atom->x[j][0]; double dy = atom->x[i][1] - atom->x[j][1]; double dz = atom->x[i][2] - atom->x[j][2]; return sqrt(dx*dx + dy*dy + dz*dz); } /* ---------------------------------------------------------------------- */ void FixSRD::print_collision(int i, int j, int ibounce, double t_remain, double dt, double *xscoll, double *xbcoll, double *norm, int type) { double xsstart[3],xbstart[3]; double **x = atom->x; double **v = atom->v; if (type != WALL) { printf("COLLISION between SRD %d and BIG %d\n",atom->tag[i],atom->tag[j]); printf(" bounce # = %d\n",ibounce+1); printf(" local indices: %d %d\n",i,j); printf(" timestep = %g\n",dt); printf(" time remaining post-collision = %g\n",t_remain); xsstart[0] = x[i][0] - dt*v[i][0]; xsstart[1] = x[i][1] - dt*v[i][1]; xsstart[2] = x[i][2] - dt*v[i][2]; xbstart[0] = x[j][0] - dt*v[j][0]; xbstart[1] = x[j][1] - dt*v[j][1]; xbstart[2] = x[j][2] - dt*v[j][2]; printf(" SRD start position = %g %g %g\n", xsstart[0],xsstart[1],xsstart[2]); printf(" BIG start position = %g %g %g\n", xbstart[0],xbstart[1],xbstart[2]); printf(" SRD coll position = %g %g %g\n", xscoll[0],xscoll[1],xscoll[2]); printf(" BIG coll position = %g %g %g\n", xbcoll[0],xbcoll[1],xbcoll[2]); printf(" SRD end position = %g %g %g\n",x[i][0],x[i][1],x[i][2]); printf(" BIG end position = %g %g %g\n",x[j][0],x[j][1],x[j][2]); printf(" SRD vel = %g %g %g\n",v[i][0],v[i][1],v[i][2]); printf(" BIG vel = %g %g %g\n",v[j][0],v[j][1],v[j][2]); printf(" surf norm = %g %g %g\n",norm[0],norm[1],norm[2]); double rstart = sqrt((xsstart[0]-xbstart[0])*(xsstart[0]-xbstart[0]) + (xsstart[1]-xbstart[1])*(xsstart[1]-xbstart[1]) + (xsstart[2]-xbstart[2])*(xsstart[2]-xbstart[2])); double rcoll = sqrt((xscoll[0]-xbcoll[0])*(xscoll[0]-xbcoll[0]) + (xscoll[1]-xbcoll[1])*(xscoll[1]-xbcoll[1]) + (xscoll[2]-xbcoll[2])*(xscoll[2]-xbcoll[2])); double rend = sqrt((x[i][0]-x[j][0])*(x[i][0]-x[j][0]) + (x[i][1]-x[j][1])*(x[i][1]-x[j][1]) + (x[i][2]-x[j][2])*(x[i][2]-x[j][2])); printf(" separation at start = %g\n",rstart); printf(" separation at coll = %g\n",rcoll); printf(" separation at end = %g\n",rend); } else { int dim = wallwhich[j] / 2; printf("COLLISION between SRD %d and WALL %d\n",atom->tag[i],j); printf(" bounce # = %d\n",ibounce+1); printf(" local indices: %d %d\n",i,j); printf(" timestep = %g\n",dt); printf(" time remaining post-collision = %g\n",t_remain); xsstart[0] = x[i][0] - dt*v[i][0]; xsstart[1] = x[i][1] - dt*v[i][1]; xsstart[2] = x[i][2] - dt*v[i][2]; xbstart[0] = xbstart[1] = xbstart[2] = 0.0; xbstart[dim] = xwall[j] - dt*vwall[j]; printf(" SRD start position = %g %g %g\n", xsstart[0],xsstart[1],xsstart[2]); printf(" WALL start position = %g\n",xbstart[dim]); printf(" SRD coll position = %g %g %g\n", xscoll[0],xscoll[1],xscoll[2]); printf(" WALL coll position = %g\n",xbcoll[dim]); printf(" SRD end position = %g %g %g\n",x[i][0],x[i][1],x[i][2]); printf(" WALL end position = %g\n",xwall[j]); printf(" SRD vel = %g %g %g\n",v[i][0],v[i][1],v[i][2]); printf(" WALL vel = %g\n",vwall[j]); printf(" surf norm = %g %g %g\n",norm[0],norm[1],norm[2]); double rstart = xsstart[dim]-xbstart[dim]; double rcoll = xscoll[dim]-xbcoll[dim]; double rend = x[dim][0]-xwall[j]; printf(" separation at start = %g\n",rstart); printf(" separation at coll = %g\n",rcoll); printf(" separation at end = %g\n",rend); } } diff --git a/src/USER-EFF/atom_vec_electron.cpp b/src/USER-EFF/atom_vec_electron.cpp index 565d63324..e861414ef 100644 --- a/src/USER-EFF/atom_vec_electron.cpp +++ b/src/USER-EFF/atom_vec_electron.cpp @@ -1,983 +1,997 @@ /* ---------------------------------------------------------------------- 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 "atom_vec_electron.h" #include "atom.h" #include "comm.h" #include "domain.h" #include "modify.h" #include "force.h" #include "fix.h" +#include "citeme.h" #include "memory.h" #include "error.h" using namespace LAMMPS_NS; #define DELTA 10000 +static const char cite_user_eff_package[] = + "USER-EFF package:\n\n" + "@Article{Jaramillo-Botero11,\n" + " author = {A. Jaramillo-Botero, J. Su, A. Qi, W. A. Goddard III},\n" + " title = {Large-Scale, Long-Term Nonadiabatic Electron Molecular Dynamics for Describing Material Properties and Phenomena in Extreme Environments},\n" + " journal = {J.~Comp.~Chem.},\n" + " year = 2011,\n" + " volume = 32,\n" + " pages = {497--512}\n" + "}\n\n"; + /* ---------------------------------------------------------------------- */ AtomVecElectron::AtomVecElectron(LAMMPS *lmp) : AtomVec(lmp) { + if (lmp->citeme) lmp->citeme->add(cite_user_eff_package); + comm_x_only = comm_f_only = 0; mass_type = 1; molecular = 0; size_forward = 4; size_reverse = 4; size_border = 9; size_velocity = 3; size_data_atom = 8; size_data_vel = 5; xcol_data = 6; atom->ecp_flag = 0; atom->electron_flag = 1; atom->q_flag = atom->spin_flag = atom->eradius_flag = atom->ervel_flag = atom->erforce_flag = 1; } /* ---------------------------------------------------------------------- grow atom-electron arrays n = 0 grows arrays by DELTA n > 0 allocates arrays to size n ------------------------------------------------------------------------- */ void AtomVecElectron::grow(int n) { if (n == 0) nmax += DELTA; else nmax = n; atom->nmax = nmax; tag = memory->grow(atom->tag,nmax,"atom:tag"); type = memory->grow(atom->type,nmax,"atom:type"); mask = memory->grow(atom->mask,nmax,"atom:mask"); image = memory->grow(atom->image,nmax,"atom:image"); x = memory->grow(atom->x,nmax,3,"atom:x"); v = memory->grow(atom->v,nmax,3,"atom:v"); f = memory->grow(atom->f,nmax*comm->nthreads,3,"atom:f"); q = memory->grow(atom->q,nmax,"atom:q"); spin = memory->grow(atom->spin,nmax,"atom:spin"); eradius = memory->grow(atom->eradius,nmax,"atom:eradius"); ervel = memory->grow(atom->ervel,nmax,"atom:ervel"); erforce = memory->grow(atom->erforce,nmax*comm->nthreads,"atom:erforce"); if (atom->nextra_grow) for (int iextra = 0; iextra < atom->nextra_grow; iextra++) modify->fix[atom->extra_grow[iextra]]->grow_arrays(nmax); } /* ---------------------------------------------------------------------- reset local array ptrs ------------------------------------------------------------------------- */ void AtomVecElectron::grow_reset() { tag = atom->tag; type = atom->type; mask = atom->mask; image = atom->image; x = atom->x; v = atom->v; f = atom->f; q = atom->q; eradius = atom->eradius; ervel = atom->ervel; erforce = atom->erforce; } /* ---------------------------------------------------------------------- copy atom I info to atom J ------------------------------------------------------------------------- */ void AtomVecElectron::copy(int i, int j, int delflag) { tag[j] = tag[i]; type[j] = type[i]; mask[j] = mask[i]; image[j] = image[i]; x[j][0] = x[i][0]; x[j][1] = x[i][1]; x[j][2] = x[i][2]; v[j][0] = v[i][0]; v[j][1] = v[i][1]; v[j][2] = v[i][2]; q[j] = q[i]; spin[j] = spin[i]; eradius[j] = eradius[i]; ervel[j] = ervel[i]; if (atom->nextra_grow) for (int iextra = 0; iextra < atom->nextra_grow; iextra++) modify->fix[atom->extra_grow[iextra]]->copy_arrays(i,j,delflag); } /* ---------------------------------------------------------------------- */ int AtomVecElectron::pack_comm(int n, int *list, double *buf, int pbc_flag, int *pbc) { int i,j,m; double dx,dy,dz; m = 0; if (pbc_flag == 0) { for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0]; buf[m++] = x[j][1]; buf[m++] = x[j][2]; buf[m++] = eradius[j]; } } else { if (domain->triclinic == 0) { dx = pbc[0]*domain->xprd; dy = pbc[1]*domain->yprd; dz = pbc[2]*domain->zprd; } else { dx = pbc[0]*domain->xprd + pbc[5]*domain->xy + pbc[4]*domain->xz; dy = pbc[1]*domain->yprd + pbc[3]*domain->yz; dz = pbc[2]*domain->zprd; } for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0] + dx; buf[m++] = x[j][1] + dy; buf[m++] = x[j][2] + dz; buf[m++] = eradius[j]; } } return m; } /* ---------------------------------------------------------------------- */ int AtomVecElectron::pack_comm_vel(int n, int *list, double *buf, int pbc_flag, int *pbc) { int i,j,m; double dx,dy,dz,dvx,dvy,dvz; m = 0; if (pbc_flag == 0) { for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0]; buf[m++] = x[j][1]; buf[m++] = x[j][2]; buf[m++] = eradius[j]; buf[m++] = v[j][0]; buf[m++] = v[j][1]; buf[m++] = v[j][2]; } } else { if (domain->triclinic == 0) { dx = pbc[0]*domain->xprd; dy = pbc[1]*domain->yprd; dz = pbc[2]*domain->zprd; } else { dx = pbc[0]*domain->xprd + pbc[5]*domain->xy + pbc[4]*domain->xz; dy = pbc[1]*domain->yprd + pbc[3]*domain->yz; dz = pbc[2]*domain->zprd; } if (!deform_vremap) { for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0] + dx; buf[m++] = x[j][1] + dy; buf[m++] = x[j][2] + dz; buf[m++] = eradius[j]; buf[m++] = v[j][0]; buf[m++] = v[j][1]; buf[m++] = v[j][2]; } } else { dvx = pbc[0]*h_rate[0] + pbc[5]*h_rate[5] + pbc[4]*h_rate[4]; dvy = pbc[1]*h_rate[1] + pbc[3]*h_rate[3]; dvz = pbc[2]*h_rate[2]; for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0] + dx; buf[m++] = x[j][1] + dy; buf[m++] = x[j][2] + dz; buf[m++] = eradius[j]; if (mask[i] & deform_groupbit) { buf[m++] = v[j][0] + dvx; buf[m++] = v[j][1] + dvy; buf[m++] = v[j][2] + dvz; } else { buf[m++] = v[j][0]; buf[m++] = v[j][1]; buf[m++] = v[j][2]; } } } } return m; } /* ---------------------------------------------------------------------- */ int AtomVecElectron::pack_comm_hybrid(int n, int *list, double *buf) { int i,j,m; m = 0; for (i = 0; i < n; i++) { j = list[i]; buf[m++] = eradius[j]; } return m; } /* ---------------------------------------------------------------------- */ void AtomVecElectron::unpack_comm(int n, int first, double *buf) { int i,m,last; m = 0; last = first + n; for (i = first; i < last; i++) { x[i][0] = buf[m++]; x[i][1] = buf[m++]; x[i][2] = buf[m++]; eradius[i] = buf[m++]; } } /* ---------------------------------------------------------------------- */ void AtomVecElectron::unpack_comm_vel(int n, int first, double *buf) { int i,m,last; m = 0; last = first + n; for (i = first; i < last; i++) { x[i][0] = buf[m++]; x[i][1] = buf[m++]; x[i][2] = buf[m++]; eradius[i] = buf[m++]; v[i][0] = buf[m++]; v[i][1] = buf[m++]; v[i][2] = buf[m++]; } } /* ---------------------------------------------------------------------- */ int AtomVecElectron::unpack_comm_hybrid(int n, int first, double *buf) { int i,m,last; m = 0; last = first + n; for (i = first; i < last; i++) eradius[i] = buf[m++]; return m; } /* ---------------------------------------------------------------------- */ int AtomVecElectron::pack_reverse(int n, int first, double *buf) { int i,m,last; m = 0; last = first + n; for (i = first; i < last; i++) { buf[m++] = f[i][0]; buf[m++] = f[i][1]; buf[m++] = f[i][2]; buf[m++] = erforce[i]; } return m; } /* ---------------------------------------------------------------------- */ int AtomVecElectron::pack_reverse_hybrid(int n, int first, double *buf) { int i,m,last; m = 0; last = first + n; for (i = first; i < last; i++) buf[m++] = erforce[i]; return m; } /* ---------------------------------------------------------------------- */ void AtomVecElectron::unpack_reverse(int n, int *list, double *buf) { int i,j,m; m = 0; for (i = 0; i < n; i++) { j = list[i]; f[j][0] += buf[m++]; f[j][1] += buf[m++]; f[j][2] += buf[m++]; erforce[j] += buf[m++]; } } /* ---------------------------------------------------------------------- */ int AtomVecElectron::unpack_reverse_hybrid(int n, int *list, double *buf) { int i,j,m; m = 0; for (i = 0; i < n; i++) { j = list[i]; erforce[j] += buf[m++]; } return m; } /* ---------------------------------------------------------------------- */ int AtomVecElectron::pack_border(int n, int *list, double *buf, int pbc_flag, int *pbc) { int i,j,m; double dx,dy,dz; m = 0; if (pbc_flag == 0) { for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0]; buf[m++] = x[j][1]; buf[m++] = x[j][2]; buf[m++] = tag[j]; buf[m++] = type[j]; buf[m++] = mask[j]; buf[m++] = q[j]; buf[m++] = spin[j]; buf[m++] = eradius[j]; } } else { if (domain->triclinic == 0) { dx = pbc[0]*domain->xprd; dy = pbc[1]*domain->yprd; dz = pbc[2]*domain->zprd; } else { dx = pbc[0]; dy = pbc[1]; dz = pbc[2]; } for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0] + dx; buf[m++] = x[j][1] + dy; buf[m++] = x[j][2] + dz; buf[m++] = tag[j]; buf[m++] = type[j]; buf[m++] = mask[j]; buf[m++] = q[j]; buf[m++] = spin[j]; buf[m++] = eradius[j]; } } if (atom->nextra_border) for (int iextra = 0; iextra < atom->nextra_border; iextra++) m += modify->fix[atom->extra_border[iextra]]->pack_border(n,list,&buf[m]); return m; } /* ---------------------------------------------------------------------- */ int AtomVecElectron::pack_border_vel(int n, int *list, double *buf, int pbc_flag, int *pbc) { int i,j,m; double dx,dy,dz,dvx,dvy,dvz; m = 0; if (pbc_flag == 0) { for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0]; buf[m++] = x[j][1]; buf[m++] = x[j][2]; buf[m++] = tag[j]; buf[m++] = type[j]; buf[m++] = mask[j]; buf[m++] = q[j]; buf[m++] = spin[j]; buf[m++] = eradius[j]; buf[m++] = v[j][0]; buf[m++] = v[j][1]; buf[m++] = v[j][2]; } } else { if (domain->triclinic == 0) { dx = pbc[0]*domain->xprd; dy = pbc[1]*domain->yprd; dz = pbc[2]*domain->zprd; } else { dx = pbc[0]; dy = pbc[1]; dz = pbc[2]; } if (domain->triclinic == 0) { for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0] + dx; buf[m++] = x[j][1] + dy; buf[m++] = x[j][2] + dz; buf[m++] = tag[j]; buf[m++] = type[j]; buf[m++] = mask[j]; buf[m++] = q[j]; buf[m++] = spin[j]; buf[m++] = eradius[j]; buf[m++] = v[j][0]; buf[m++] = v[j][1]; buf[m++] = v[j][2]; } } else { dvx = pbc[0]*h_rate[0] + pbc[5]*h_rate[5] + pbc[4]*h_rate[4]; dvy = pbc[1]*h_rate[1] + pbc[3]*h_rate[3]; dvz = pbc[2]*h_rate[2]; for (i = 0; i < n; i++) { j = list[i]; buf[m++] = x[j][0] + dx; buf[m++] = x[j][1] + dy; buf[m++] = x[j][2] + dz; buf[m++] = tag[j]; buf[m++] = type[j]; buf[m++] = mask[j]; buf[m++] = q[j]; buf[m++] = spin[j]; buf[m++] = eradius[j]; if (mask[i] & deform_groupbit) { buf[m++] = v[j][0] + dvx; buf[m++] = v[j][1] + dvy; buf[m++] = v[j][2] + dvz; } else { buf[m++] = v[j][0]; buf[m++] = v[j][1]; buf[m++] = v[j][2]; } } } } if (atom->nextra_border) for (int iextra = 0; iextra < atom->nextra_border; iextra++) m += modify->fix[atom->extra_border[iextra]]->pack_border(n,list,&buf[m]); return m; } /* ---------------------------------------------------------------------- */ int AtomVecElectron::pack_border_hybrid(int n, int *list, double *buf) { int i,j,m; m = 0; for (i = 0; i < n; i++) { j = list[i]; buf[m++] = q[j]; buf[m++] = spin[j]; buf[m++] = eradius[j]; } return m; } /* ---------------------------------------------------------------------- */ void AtomVecElectron::unpack_border(int n, int first, double *buf) { int i,m,last; m = 0; last = first + n; for (i = first; i < last; i++) { if (i == nmax) grow(0); x[i][0] = buf[m++]; x[i][1] = buf[m++]; x[i][2] = buf[m++]; tag[i] = static_cast (buf[m++]); type[i] = static_cast (buf[m++]); mask[i] = static_cast (buf[m++]); q[i] = buf[m++]; spin[i] = static_cast (buf[m++]); eradius[i] = buf[m++]; } if (atom->nextra_border) for (int iextra = 0; iextra < atom->nextra_border; iextra++) m += modify->fix[atom->extra_border[iextra]]-> unpack_border(n,first,&buf[m]); } /* ---------------------------------------------------------------------- */ void AtomVecElectron::unpack_border_vel(int n, int first, double *buf) { int i,m,last; m = 0; last = first + n; for (i = first; i < last; i++) { if (i == nmax) grow(0); x[i][0] = buf[m++]; x[i][1] = buf[m++]; x[i][2] = buf[m++]; tag[i] = static_cast (buf[m++]); type[i] = static_cast (buf[m++]); mask[i] = static_cast (buf[m++]); q[i] = buf[m++]; spin[i] = static_cast (buf[m++]); eradius[i] = buf[m++]; v[i][0] = buf[m++]; v[i][1] = buf[m++]; v[i][2] = buf[m++]; } if (atom->nextra_border) for (int iextra = 0; iextra < atom->nextra_border; iextra++) m += modify->fix[atom->extra_border[iextra]]-> unpack_border(n,first,&buf[m]); } /* ---------------------------------------------------------------------- */ int AtomVecElectron::unpack_border_hybrid(int n, int first, double *buf) { int i,m,last; m = 0; last = first + n; for (i = first; i < last; i++) { q[i] = buf[m++]; spin[i] = static_cast (buf[m++]); eradius[i] = buf[m++]; } return m; } /* ---------------------------------------------------------------------- pack data for atom I for sending to another proc xyz must be 1st 3 values, so comm::exchange() can test on them ------------------------------------------------------------------------- */ int AtomVecElectron::pack_exchange(int i, double *buf) { int m = 1; buf[m++] = x[i][0]; buf[m++] = x[i][1]; buf[m++] = x[i][2]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; buf[m++] = v[i][2]; buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = q[i]; buf[m++] = spin[i]; buf[m++] = eradius[i]; buf[m++] = ervel[i]; if (atom->nextra_grow) for (int iextra = 0; iextra < atom->nextra_grow; iextra++) m += modify->fix[atom->extra_grow[iextra]]->pack_exchange(i,&buf[m]); buf[0] = m; return m; } /* ---------------------------------------------------------------------- */ int AtomVecElectron::unpack_exchange(double *buf) { int nlocal = atom->nlocal; if (nlocal == nmax) grow(0); int m = 1; x[nlocal][0] = buf[m++]; x[nlocal][1] = buf[m++]; x[nlocal][2] = buf[m++]; v[nlocal][0] = buf[m++]; v[nlocal][1] = buf[m++]; v[nlocal][2] = buf[m++]; tag[nlocal] = static_cast (buf[m++]); type[nlocal] = static_cast (buf[m++]); mask[nlocal] = static_cast (buf[m++]); image[nlocal] = *((tagint *) &buf[m++]); q[nlocal] = buf[m++]; spin[nlocal] = static_cast (buf[m++]); eradius[nlocal] = buf[m++]; ervel[nlocal] = buf[m++]; if (atom->nextra_grow) for (int iextra = 0; iextra < atom->nextra_grow; iextra++) m += modify->fix[atom->extra_grow[iextra]]-> unpack_exchange(nlocal,&buf[m]); atom->nlocal++; return m; } /* ---------------------------------------------------------------------- size of restart data for all atoms owned by this proc include extra data stored by fixes ------------------------------------------------------------------------- */ int AtomVecElectron::size_restart() { int i; int nlocal = atom->nlocal; int n = 15 * nlocal; // Associated with pack_restart if (atom->nextra_restart) for (int iextra = 0; iextra < atom->nextra_restart; iextra++) for (i = 0; i < nlocal; i++) n += modify->fix[atom->extra_restart[iextra]]->size_restart(i); return n; } /* ---------------------------------------------------------------------- pack atom I's data for restart file including extra quantities xyz must be 1st 3 values, so that read_restart can test on them molecular types may be negative, but write as positive ------------------------------------------------------------------------- */ int AtomVecElectron::pack_restart(int i, double *buf) { int m = 1; buf[m++] = x[i][0]; buf[m++] = x[i][1]; buf[m++] = x[i][2]; buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; buf[m++] = v[i][2]; buf[m++] = q[i]; buf[m++] = spin[i]; buf[m++] = eradius[i]; buf[m++] = ervel[i]; if (atom->nextra_restart) for (int iextra = 0; iextra < atom->nextra_restart; iextra++) m += modify->fix[atom->extra_restart[iextra]]->pack_restart(i,&buf[m]); buf[0] = m; return m; } /* ---------------------------------------------------------------------- unpack data for one atom from restart file including extra quantities ------------------------------------------------------------------------- */ int AtomVecElectron::unpack_restart(double *buf) { int nlocal = atom->nlocal; if (nlocal == nmax) { grow(0); if (atom->nextra_store) memory->grow(atom->extra,nmax,atom->nextra_store,"atom:extra"); } int m = 1; x[nlocal][0] = buf[m++]; x[nlocal][1] = buf[m++]; x[nlocal][2] = buf[m++]; tag[nlocal] = static_cast (buf[m++]); type[nlocal] = static_cast (buf[m++]); mask[nlocal] = static_cast (buf[m++]); image[nlocal] = *((tagint *) &buf[m++]); v[nlocal][0] = buf[m++]; v[nlocal][1] = buf[m++]; v[nlocal][2] = buf[m++]; q[nlocal] = buf[m++]; spin[nlocal] = static_cast (buf[m++]); eradius[nlocal] = buf[m++]; ervel[nlocal] = buf[m++]; double **extra = atom->extra; if (atom->nextra_store) { int size = static_cast (buf[0]) - m; for (int i = 0; i < size; i++) extra[nlocal][i] = buf[m++]; } atom->nlocal++; return m; } /* ---------------------------------------------------------------------- create one atom of itype at coord set other values to defaults ------------------------------------------------------------------------- */ void AtomVecElectron::create_atom(int itype, double *coord) { int nlocal = atom->nlocal; if (nlocal == nmax) grow(0); tag[nlocal] = 0; type[nlocal] = itype; x[nlocal][0] = coord[0]; x[nlocal][1] = coord[1]; x[nlocal][2] = coord[2]; mask[nlocal] = 1; image[nlocal] = ((tagint) IMGMAX << IMG2BITS) | ((tagint) IMGMAX << IMGBITS) | IMGMAX; v[nlocal][0] = 0.0; v[nlocal][1] = 0.0; v[nlocal][2] = 0.0; q[nlocal] = 0.0; spin[nlocal] = 1; eradius[nlocal] = 1.0; ervel[nlocal] = 0.0; atom->nlocal++; } /* ---------------------------------------------------------------------- unpack one line from Atoms section of data file initialize other atom quantities ------------------------------------------------------------------------- */ void AtomVecElectron::data_atom(double *coord, tagint imagetmp, char **values) { int nlocal = atom->nlocal; if (nlocal == nmax) grow(0); tag[nlocal] = atoi(values[0]); if (tag[nlocal] <= 0) error->one(FLERR,"Invalid atom ID in Atoms section of data file"); type[nlocal] = atoi(values[1]); if (type[nlocal] <= 0 || type[nlocal] > atom->ntypes) error->one(FLERR,"Invalid atom type in Atoms section of data file"); q[nlocal] = atof(values[2]); spin[nlocal] = atoi(values[3]); if (spin[nlocal] == 3) atom->ecp_flag = 1; eradius[nlocal] = atof(values[4]); x[nlocal][0] = coord[0]; x[nlocal][1] = coord[1]; x[nlocal][2] = coord[2]; image[nlocal] = imagetmp; mask[nlocal] = 1; v[nlocal][0] = 0.0; v[nlocal][1] = 0.0; v[nlocal][2] = 0.0; ervel[nlocal] = 0.0; atom->nlocal++; } /* ---------------------------------------------------------------------- unpack hybrid quantities from one line in Atoms section of data file initialize other atom quantities for this sub-style ------------------------------------------------------------------------- */ int AtomVecElectron::data_atom_hybrid(int nlocal, char **values) { q[nlocal] = atof(values[0]); spin[nlocal] = atoi(values[1]); eradius[nlocal] = atof(values[2]); if (eradius[nlocal] < 0.0) error->one(FLERR,"Invalid eradius in Atoms section of data file"); v[nlocal][0] = 0.0; v[nlocal][1] = 0.0; v[nlocal][2] = 0.0; ervel[nlocal] = 0.0; return 3; } /* ---------------------------------------------------------------------- unpack one line from Velocities section of data file ------------------------------------------------------------------------- */ void AtomVecElectron::data_vel(int m, char **values) { v[m][0] = atof(values[0]); v[m][1] = atof(values[1]); v[m][2] = atof(values[2]); ervel[m] = atof(values[3]); } /* ---------------------------------------------------------------------- unpack hybrid quantities from one line in Velocities section of data file ------------------------------------------------------------------------- */ int AtomVecElectron::data_vel_hybrid(int m, char **values) { ervel[m] = atof(values[0]); return 1; } /* ---------------------------------------------------------------------- pack atom info for data file including 3 image flags ------------------------------------------------------------------------- */ void AtomVecElectron::pack_data(double **buf) { int nlocal = atom->nlocal; for (int i = 0; i < nlocal; i++) { buf[i][0] = tag[i]; buf[i][1] = type[i]; buf[i][2] = q[i]; buf[i][3] = spin[i]; buf[i][4] = eradius[i]; buf[i][5] = x[i][0]; buf[i][6] = x[i][1]; buf[i][7] = x[i][2]; buf[i][8] = (image[i] & IMGMASK) - IMGMAX; buf[i][9] = (image[i] >> IMGBITS & IMGMASK) - IMGMAX; buf[i][10] = (image[i] >> IMG2BITS) - IMGMAX; } } /* ---------------------------------------------------------------------- pack hybrid atom info for data file ------------------------------------------------------------------------- */ int AtomVecElectron::pack_data_hybrid(int i, double *buf) { buf[0] = q[i]; buf[1] = spin[i]; buf[2] = eradius[i]; return 3; } /* ---------------------------------------------------------------------- write atom info to data file including 3 image flags ------------------------------------------------------------------------- */ void AtomVecElectron::write_data(FILE *fp, int n, double **buf) { for (int i = 0; i < n; i++) fprintf(fp,"%d %d %-1.16e %d %-1.16e %-1.16e %-1.16e %-1.16e %d %d %d\n", (int) buf[i][0],(int) buf[i][1],buf[i][2], (int) buf[i][3],buf[i][4], buf[i][5],buf[i][6],buf[i][7], (int) buf[i][8],(int) buf[i][9],(int) buf[i][10]); } /* ---------------------------------------------------------------------- write hybrid atom info to data file ------------------------------------------------------------------------- */ int AtomVecElectron::write_data_hybrid(FILE *fp, double *buf) { fprintf(fp," %-1.16e %d %-1.16e",buf[0],(int) buf[1],buf[2]); return 3; } /* ---------------------------------------------------------------------- pack velocity info for data file ------------------------------------------------------------------------- */ void AtomVecElectron::pack_vel(double **buf) { int nlocal = atom->nlocal; for (int i = 0; i < nlocal; i++) { buf[i][0] = tag[i]; buf[i][1] = v[i][0]; buf[i][2] = v[i][1]; buf[i][3] = v[i][2]; buf[i][4] = ervel[i]; } } /* ---------------------------------------------------------------------- pack velocity info for data file ------------------------------------------------------------------------- */ int AtomVecElectron::pack_vel_hybrid(int i, double *buf) { buf[0] = ervel[i]; return 1; } /* ---------------------------------------------------------------------- write hybrid velocity info to data file ------------------------------------------------------------------------- */ void AtomVecElectron::write_vel(FILE *fp, int n, double **buf) { for (int i = 0; i < n; i++) fprintf(fp,"%d %-1.16e %-1.16e %-1.16e %-1.16e\n", (int) buf[i][0],buf[i][1],buf[i][2],buf[i][3],buf[i][4]); } /* ---------------------------------------------------------------------- write hybrid velocity info to data file ------------------------------------------------------------------------- */ int AtomVecElectron::write_vel_hybrid(FILE *fp, double *buf) { fprintf(fp," %-1.16e",buf[0]); return 1; } /* ---------------------------------------------------------------------- return # of bytes of allocated memory ------------------------------------------------------------------------- */ bigint AtomVecElectron::memory_usage() { bigint bytes = 0; if (atom->memcheck("tag")) bytes += memory->usage(tag,nmax); if (atom->memcheck("type")) bytes += memory->usage(type,nmax); if (atom->memcheck("mask")) bytes += memory->usage(mask,nmax); if (atom->memcheck("image")) bytes += memory->usage(image,nmax); if (atom->memcheck("x")) bytes += memory->usage(x,nmax,3); if (atom->memcheck("v")) bytes += memory->usage(v,nmax,3); if (atom->memcheck("f")) bytes += memory->usage(f,nmax*comm->nthreads,3); if (atom->memcheck("q")) bytes += memory->usage(q,nmax); if (atom->memcheck("spin")) bytes += memory->usage(spin,nmax); if (atom->memcheck("eradius")) bytes += memory->usage(eradius,nmax); if (atom->memcheck("ervel")) bytes += memory->usage(ervel,nmax); if (atom->memcheck("erforce")) bytes += memory->usage(erforce,nmax*comm->nthreads); return bytes; } diff --git a/src/USER-PHONON/fix_phonon.cpp b/src/USER-PHONON/fix_phonon.cpp index ee01f7258..f8290d172 100644 --- a/src/USER-PHONON/fix_phonon.cpp +++ b/src/USER-PHONON/fix_phonon.cpp @@ -1,896 +1,912 @@ /* ---------------------------------------------------------------------- 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: Ling-Ti Kong Contact: School of Materials Science and Engineering, Shanghai Jiao Tong University, 800 Dongchuan Road, Minhang, Shanghai 200240, CHINA konglt@sjtu.edu.cn; konglt@gmail.com ------------------------------------------------------------------------- */ #include "string.h" #include "fix_phonon.h" #include "atom.h" #include "compute.h" #include "domain.h" -#include "error.h" #include "fft3d_wrap.h" #include "force.h" #include "group.h" #include "lattice.h" -#include "memory.h" #include "modify.h" #include "update.h" +#include "citeme.h" +#include "memory.h" +#include "error.h" using namespace LAMMPS_NS; using namespace FixConst; #define INVOKED_SCALAR 1 #define INVOKED_VECTOR 2 #define MAXLINE 256 +static const char cite_fix_phonon[] = + "fix phonon command:\n\n" + "@Article{Kong11,\n" + " author = {L. T. Kong},\n" + " title = {Phonon dispersion measured directly from molecular dynamics simulations},\n" + " journal = {Comp.~Phys.~Comm.},\n" + " year = 2011,\n" + " volume = 182,\n" + " pages = {2201--2207}\n" + "}\n\n"; + +/* ---------------------------------------------------------------------- */ + FixPhonon::FixPhonon(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { + if (lmp->citeme) lmp->citeme->add(cite_fix_phonon); + MPI_Comm_rank(world,&me); MPI_Comm_size(world,&nprocs); if (narg < 8) error->all(FLERR,"Illegal fix phonon command: number of arguments < 8"); nevery = force->inumeric(FLERR,arg[3]); // Calculate this fix every n steps! if (nevery < 1) error->all(FLERR,"Illegal fix phonon command"); nfreq = force->inumeric(FLERR,arg[4]); // frequency to output result if (nfreq < 1) error->all(FLERR,"Illegal fix phonon command"); waitsteps = ATOBIGINT(arg[5]); // Wait this many timesteps before actually measuring if (waitsteps < 0) error->all(FLERR,"Illegal fix phonon command: waitsteps < 0 !"); int n = strlen(arg[6]) + 1; // map file mapfile = new char[n]; strcpy(mapfile, arg[6]); n = strlen(arg[7]) + 1; // prefix of output prefix = new char[n]; strcpy(prefix, arg[7]); logfile = new char[n+4]; sprintf(logfile,"%s.log",prefix); int sdim = 4; int iarg = 8; nasr = 20; // other command line options while (iarg < narg){ if (strcmp(arg[iarg],"sysdim") == 0){ if (++iarg >= narg) error->all(FLERR,"Illegal fix phonon command: incomplete command line options."); sdim = force->inumeric(FLERR,arg[iarg]); } else if (strcmp(arg[iarg],"nasr") == 0){ if (++iarg >= narg) error->all(FLERR,"Illegal fix phonon command: incomplete command line options."); nasr = force->inumeric(FLERR,arg[iarg]); } else { error->all(FLERR,"Illegal fix phonon command: unknown option read!"); } iarg++; } // get the dimension of the simulation; 1D is possible by specifying the option of "sysdim 1" sysdim = domain->dimension; if (sdim < sysdim) sysdim = sdim; nasr = MAX(0,nasr); // get the total number of atoms in group nGFatoms = static_cast(group->count(igroup)); if (nGFatoms<1) error->all(FLERR,"No atom found for fix phonon!"); // MPI gatherv related variables recvcnts = new int[nprocs]; displs = new int[nprocs]; // mapping index tag2surf.clear(); // clear map info surf2tag.clear(); // get the mapping between lattice indices and atom IDs readmap(); delete []mapfile; if (nucell == 1) nasr = MIN(1,nasr); // get the mass matrix for dynamic matrix getmass(); // create FFT and allocate memory for FFT nxlo = 0; int *nx_loc = new int [nprocs]; for (int i=0; ismalloc(MAX(1,mynq)*2*sizeof(double),"fix_phonon:fft_data"); // allocate variables; MAX(1,... is used because NULL buffer will result in error for MPI RIloc = memory->create(RIloc,nGFatoms,(sysdim+1),"fix_phonon:RIloc"); RIall = memory->create(RIall,nGFatoms,(sysdim+1),"fix_phonon:RIall"); Rsort = memory->create(Rsort,nGFatoms, sysdim, "fix_phonon:Rsort"); Rnow = memory->create(Rnow, MAX(1,mynpt),fft_dim,"fix_phonon:Rnow"); Rsum = memory->create(Rsum, MAX(1,mynpt),fft_dim,"fix_phonon:Rsum"); basis = memory->create(basis,nucell, sysdim, "fix_phonon:basis"); // because of hermit, only nearly half of q points are stored Rqnow = memory->create(Rqnow,MAX(1,mynq),fft_dim, "fix_phonon:Rqnow"); Rqsum = memory->create(Rqsum,MAX(1,mynq),fft_dim2,"fix_phonon:Rqsum"); Phi_q = memory->create(Phi_q,MAX(1,mynq),fft_dim2,"fix_phonon:Phi_q"); if (me == 0) // variable to collect all local Phi to root Phi_all = memory->create(Phi_all,ntotal,fft_dim2,"fix_phonon:Phi_all"); else Phi_all = memory->create(Phi_all,1,1,"fix_phonon:Phi_all"); // output some information on the system to log file if (me == 0){ flog = fopen(logfile, "w"); if (flog == NULL) { char str[MAXLINE]; sprintf(str,"Can not open output file %s",logfile); error->one(FLERR,str); } for (int i=0; i<60; i++) fprintf(flog,"#"); fprintf(flog,"\n"); fprintf(flog,"# group name of the atoms under study : %s\n", group->names[igroup]); fprintf(flog,"# total number of atoms in the group : %d\n", nGFatoms); fprintf(flog,"# dimension of the system : %d D\n", sysdim); fprintf(flog,"# number of atoms per unit cell : %d\n", nucell); fprintf(flog,"# dimension of the FFT mesh : %d x %d x %d\n", nx, ny, nz); fprintf(flog,"# number of wait steps before measurement : " BIGINT_FORMAT "\n", waitsteps); fprintf(flog,"# frequency of GFC measurement : %d\n", nevery); fprintf(flog,"# output result after this many measurement: %d\n", nfreq); fprintf(flog,"# number of processors used by this run : %d\n", nprocs); for (int i=0; i<60; i++) fprintf(flog,"#"); fprintf(flog,"\n"); fprintf(flog,"# mapping information between lattice index and atom id\n"); fprintf(flog,"# nx ny nz nucell\n"); fprintf(flog,"%d %d %d %d\n", nx, ny, nz, nucell); fprintf(flog,"# l1 l2 l3 k atom_id\n"); int ix, iy, iz, iu; for (idx =0; idxfind_compute(id_temp); temperature = modify->compute[icompute]; inv_nTemp = 1.0/group->count(temperature->igroup); } // end of constructor /* ---------------------------------------------------------------------- */ void FixPhonon::post_run() { // compute and output final GFC results if (ifreq > 0 && ifreq != nfreq) postprocess(); if (me == 0) fclose(flog); } /* ---------------------------------------------------------------------- */ FixPhonon::~FixPhonon() { // delete locally stored array memory->destroy(RIloc); memory->destroy(RIall); memory->destroy(Rsort); memory->destroy(Rnow); memory->destroy(Rsum); memory->destroy(basis); memory->destroy(Rqnow); memory->destroy(Rqsum); memory->destroy(Phi_q); memory->destroy(Phi_all); delete []recvcnts; delete []displs; delete []prefix; delete []logfile; delete []fft_cnts; delete []fft_disp; delete []id_temp; delete []TempSum; delete []M_inv_sqrt; delete []basetype; // destroy FFT delete fft; memory->sfree(fft_data); // clear map info tag2surf.clear(); surf2tag.clear(); } /* ---------------------------------------------------------------------- */ int FixPhonon::setmask() { int mask = 0; mask |= END_OF_STEP; return mask; } /* ---------------------------------------------------------------------- */ void FixPhonon::init() { // warn if more than one fix-phonon int count = 0; for (int i=0;infix;i++) if (strcmp(modify->fix[i]->style,"gfc") == 0) count++; if (count > 1 && me == 0) error->warning(FLERR,"More than one fix phonon defined"); // just warn, but allowed. } /* ---------------------------------------------------------------------- */ void FixPhonon::setup(int flag) { // initialize accumulating variables for (int i=0; i (0.,0.); } for (int i=0; i<6; i++) hsum[i] = 0.; for (int i=0; intimestep; GFcounter = 0; ifreq = 0; } /* ---------------------------------------------------------------------- */ void FixPhonon::end_of_step() { if ( (update->ntimestep-prev_nstep) <= waitsteps) return; double **x = atom->x; int *mask = atom->mask; int *tag = atom->tag; int *image = atom->image; int nlocal = atom->nlocal; double xprd = domain->xprd; double yprd = domain->yprd; double zprd = domain->zprd; double *h = domain->h; double xbox, ybox, zbox; int i,idim,jdim,ndim; double xcur[3]; // to get the current temperature if (!(temperature->invoked_flag & INVOKED_VECTOR)) temperature->compute_vector(); for (idim=0; idimvector[idim]; // evaluate R(r) on local proc nfind = 0; if (domain->triclinic == 0) { // for orthogonal lattice for (i=0; i> 10 & 1023) - 512; zbox = (image[i] >> 20) - 512; xcur[0] = x[i][0] + xprd*xbox; xcur[1] = x[i][1] + yprd*ybox; xcur[2] = x[i][2] + zprd*zbox; for (idim=0; idim> 10 & 1023) - 512; zbox = (image[i] >> 20) - 512; xcur[0] = x[i][0] + h[0]*xbox + h[5]*ybox + h[4]*zbox; xcur[1] = x[i][1] + h[1]*ybox + h[3]*zbox; xcur[2] = x[i][2] + h[2]*zbox; for (idim=0; idim(RIall[i][sysdim]); for (idim=0; idimcompute(fft_data, fft_data, -1); m = 0; for (idq=0; idq(fft_data[m], fft_data[m+1]); m += 2; } } // to get sum(R(q).R(q)*) for (idq=0; idq sysdim){ for (idx=0; idxminimum_image(dist2orig); for (idim=0; idim)*2*nGFatoms + sizeof(double)*(nGFatoms*(3*sysdim+2)+mynpt*fft_dim*2) + sizeof(std::complex)*MAX(1,mynq)*fft_dim *(1+2*fft_dim) + sizeof(std::complex)*ntotal*fft_dim2 + sizeof(int) * nprocs * 4; return bytes; } /* ---------------------------------------------------------------------- */ int FixPhonon::modify_param(int narg, char **arg) { if (strcmp(arg[0],"temp") == 0) { if (narg < 2) error->all(FLERR,"Illegal fix_modify command"); delete [] id_temp; int n = strlen(arg[1]) + 1; id_temp = new char[n]; strcpy(id_temp,arg[1]); int icompute = modify->find_compute(id_temp); if (icompute < 0) error->all(FLERR,"Could not find fix_modify temp ID"); temperature = modify->compute[icompute]; if (temperature->tempflag == 0) error->all(FLERR,"Fix_modify temp ID does not compute temperature"); inv_nTemp = 1.0/group->count(temperature->igroup); return 2; } return 0; } /* ---------------------------------------------------------------------- * private method, to get the mass matrix for dynamic matrix * --------------------------------------------------------------------*/ void FixPhonon::getmass() { int nlocal = atom->nlocal; int *mask = atom->mask; int *tag = atom->tag; int *type = atom->type; double *rmass = atom->rmass; double *mass = atom->mass; double *mass_one, *mass_all; double *type_one, *type_all; mass_one = new double[nucell]; mass_all = new double[nucell]; type_one = new double[nucell]; type_all = new double[nucell]; for (int i=0; iall(FLERR,str); } if (fgets(strtmp,MAXLINE,fp) == NULL) error->all(FLERR,"Error while reading header of mapping file!"); sscanf(strtmp,"%d %d %d %d", &nx, &ny, &nz, &nucell); ntotal = nx*ny*nz; if (ntotal*nucell != nGFatoms) error->all(FLERR,"FFT mesh and number of atoms in group mismatch!"); if (fgets(strtmp,MAXLINE,fp) == NULL) // second line of mapfile is comment error->all(FLERR,"Error while reading comment of mapping file!"); int ix, iy, iz, iu; for (int i=0; i=nx || iy<0 || iy>=ny || iz<0 || iz>=nz|| iu<0 || iu>=nucell) {info = 2; break;} // check if index is in correct range if (itag<1 || itag>static_cast(atom->natoms)) {info = 3; break;} // 1 <= itag <= natoms idx = ((ix*ny+iy)*nz+iz)*nucell+iu; tag2surf[itag] = idx; surf2tag[idx] = itag; } fclose(fp); if (tag2surf.size() != surf2tag.size() || tag2surf.size() != static_cast(nGFatoms) ) error->all(FLERR,"The mapping is incomplete!"); if (info) error->all(FLERR,"Error while reading mapping file!"); // check the correctness of mapping int *mask = atom->mask; int *tag = atom->tag; int nlocal = atom->nlocal; for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit){ itag = tag[i]; idx = tag2surf[itag]; if (itag != surf2tag[idx]) error->one(FLERR,"The mapping info read is incorrect!"); } } return; } /* ---------------------------------------------------------------------- * private method, to output the force constant matrix * --------------------------------------------------------------------*/ void FixPhonon::postprocess( ) { if (GFcounter<1) return; ifreq =0; int idim, jdim, ndim; double invGFcounter = 1.0 /double(GFcounter); // to get for (idq=0; idq for (idx=0; idxq for (idim=0; idimcompute(fft_data,fft_data,-1); m = 0; for (idq=0; idq(fft_data[m], fft_data[m+1]); m += 2; } } // to get G(q) = - q.q for (idq=0; idqboltz, kbtsqrt[sysdim], TempAve = 0.; double TempFac = invGFcounter*inv_nTemp; double NormFac = TempFac*double(ntotal); for (idim=0; idim sysdim) MPI_Reduce (&basis[1][0], &basis_root[sysdim], fft_dim-sysdim, MPI_DOUBLE, MPI_SUM, 0, world); if (me == 0){ // output dynamic matrix by root // get basis info for (idim=0; idimntimestep); GFC_bin = fopen(fname,"wb"); fwrite(&sysdim, sizeof(int), 1, GFC_bin); fwrite(&nx, sizeof(int), 1, GFC_bin); fwrite(&ny, sizeof(int), 1, GFC_bin); fwrite(&nz, sizeof(int), 1, GFC_bin); fwrite(&nucell, sizeof(int), 1, GFC_bin); fwrite(&boltz, sizeof(double), 1, GFC_bin); fwrite(Phi_all[0],sizeof(double),ntotal*fft_dim2*2,GFC_bin); fwrite(&TempAve, sizeof(double),1, GFC_bin); fwrite(&basevec[0], sizeof(double),9, GFC_bin); fwrite(&basis_root[0],sizeof(double),fft_dim,GFC_bin); fwrite(basetype, sizeof(int), nucell, GFC_bin); fwrite(M_inv_sqrt, sizeof(double),nucell, GFC_bin); fclose(GFC_bin); // write log file, here however, it is the dynamical matrix that is written for (int i=0; i<60; i++) fprintf(flog,"#"); fprintf(flog,"\n"); fprintf(flog, "# Current time step : " BIGINT_FORMAT "\n", update->ntimestep); fprintf(flog, "# Total number of measurements : %d\n", GFcounter); fprintf(flog, "# Average temperature of the measurement : %lg\n", TempAve); fprintf(flog, "# Boltzmann constant under current units : %lg\n", boltz); fprintf(flog, "# basis vector A1 = [%lg %lg %lg]\n", basevec[0], basevec[1], basevec[2]); fprintf(flog, "# basis vector A2 = [%lg %lg %lg]\n", basevec[3], basevec[4], basevec[5]); fprintf(flog, "# basis vector A3 = [%lg %lg %lg]\n", basevec[6], basevec[7], basevec[8]); for (int i=0; i<60; i++) fprintf(flog,"#"); fprintf(flog,"\n"); fprintf(flog, "# qx\t qy \t qz \t\t Phi(q)\n"); EnforceASR(); // to get D = 1/M x Phi for (idq=0; idq *Mat) { int i,icol,irow,j,k,l,ll,idr,idc; int *indxc,*indxr,*ipiv; double big, nmjk; std::complex dum, pivinv; indxc = new int[n]; indxr = new int[n]; ipiv = new int[n]; for (i=0; i= big){ big = nmjk; irow = j; icol = k; } }else if (ipiv[k]>1){ error->one(FLERR,"Singular matrix in complex GaussJordan!"); } } } } ipiv[icol] += 1; if (irow != icol){ for (l=0; l(0.,0.)) error->one(FLERR,"Singular matrix in complex GaussJordan!"); pivinv = 1./ Mat[idr]; Mat[idr] = std::complex(1.,0.); idr = icol*n; for (l=0; l=0; l--){ int rl = indxr[l]; int cl = indxc[l]; if (rl != cl){ for (k=0; kciteme) lmp->citeme->add(cite_fix_qeq_reax); + if (narg != 8) error->all(FLERR,"Illegal fix qeq/reax command"); nevery = force->inumeric(FLERR,arg[3]); swa = force->numeric(FLERR,arg[4]); swb = force->numeric(FLERR,arg[5]); tolerance = force->numeric(FLERR,arg[6]); pertype_parameters(arg[7]); shld = NULL; n = n_cap = 0; N = 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; b_prc = NULL; b_prm = NULL; // CG p = NULL; q = NULL; r = NULL; d = NULL; // H matrix H.firstnbr = NULL; H.numnbrs = NULL; H.jlist = NULL; H.val = NULL; // GMRES //g = NULL; //y = NULL; //hstr = NULL; //v = NULL; //h = NULL; //hc = NULL; //hs = NULL; // 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] = 0; reaxc = NULL; reaxc = (PairReaxC *) force->pair_match("reax/c",1); } /* ---------------------------------------------------------------------- */ FixQEqReax::~FixQEqReax() { // 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); if (!reaxflag) { memory->destroy(chi); memory->destroy(eta); memory->destroy(gamma); } } /* ---------------------------------------------------------------------- */ int FixQEqReax::setmask() { int mask = 0; mask |= PRE_FORCE; mask |= MIN_PRE_FORCE; return mask; } /* ---------------------------------------------------------------------- */ void FixQEqReax::pertype_parameters(char *arg) { if (strcmp(arg,"reax/c") == 0) { reaxflag = 1; Pair *pair = force->pair_match("reax/c",1); if (pair == NULL) error->all(FLERR,"No pair reax/c for fix qeq/reax"); int tmp; chi = (double *) pair->extract("chi",tmp); eta = (double *) pair->extract("eta",tmp); gamma = (double *) pair->extract("gamma",tmp); if (chi == NULL || eta == NULL || gamma == NULL) error->all(FLERR, "Fix qeq/reax could not extract params from pair reax/c"); return; } int i,itype,ntypes; double v1,v2,v3; FILE *pf; reaxflag = 0; ntypes = atom->ntypes; memory->create(chi,ntypes+1,"qeq/reax:chi"); memory->create(eta,ntypes+1,"qeq/reax:eta"); memory->create(gamma,ntypes+1,"qeq/reax:gamma"); if (comm->me == 0) { if ((pf = fopen(arg,"r")) == NULL) error->one(FLERR,"Fix qeq/reax parameter file could not be found"); for (i = 1; i <= ntypes && !feof(pf); i++) { fscanf(pf,"%d %lg %lg %lg",&itype,&v1,&v2,&v3); if (itype < 1 || itype > ntypes) error->one(FLERR,"Fix qeq/reax invalid atom type in param file"); chi[itype] = v1; eta[itype] = v2; gamma[itype] = v3; } if (i <= ntypes) error->one(FLERR,"Invalid param file for fix qeq/reax"); fclose(pf); } MPI_Bcast(&chi[1],ntypes,MPI_DOUBLE,0,world); MPI_Bcast(&eta[1],ntypes,MPI_DOUBLE,0,world); MPI_Bcast(&gamma[1],ntypes,MPI_DOUBLE,0,world); } /* ---------------------------------------------------------------------- */ void FixQEqReax::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(b_prc,nmax,"qeq:b_prc"); memory->create(b_prm,nmax,"qeq:b_prm"); memory->create(p,nmax,"qeq:p"); memory->create(q,nmax,"qeq:q"); memory->create(r,nmax,"qeq:r"); memory->create(d,nmax,"qeq:d"); } /* ---------------------------------------------------------------------- */ void FixQEqReax::deallocate_storage() { memory->destroy(s); memory->destroy(t); memory->destroy( Hdia_inv ); memory->destroy( b_s ); memory->destroy( b_t ); memory->destroy( b_prc ); memory->destroy( b_prm ); memory->destroy( p ); memory->destroy( q ); memory->destroy( r ); memory->destroy( d ); } /* ---------------------------------------------------------------------- */ void FixQEqReax::reallocate_storage() { deallocate_storage(); allocate_storage(); init_storage(); } /* ---------------------------------------------------------------------- */ void FixQEqReax::allocate_matrix() { int i,ii; int mincap; double safezone; if( reaxflag ) { mincap = reaxc->system->mincap; safezone = reaxc->system->safezone; } else { mincap = MIN_CAP; safezone = SAFE_ZONE; } n = atom->nlocal; n_cap = MAX( (int)(n * safezone), mincap ); // determine the total space for the H matrix int m = 0; for( ii = 0; ii < list->inum; ii++ ) { i = list->ilist[ii]; m += list->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 FixQEqReax::deallocate_matrix() { memory->destroy( H.firstnbr ); memory->destroy( H.numnbrs ); memory->destroy( H.jlist ); memory->destroy( H.val ); } /* ---------------------------------------------------------------------- */ void FixQEqReax::reallocate_matrix() { deallocate_matrix(); allocate_matrix(); } /* ---------------------------------------------------------------------- */ void FixQEqReax::init() { if (!atom->q_flag) error->all(FLERR,"Fix qeq/reax requires atom attribute q"); // need a half neighbor list w/ Newton off and ghost neighbors // built whenever re-neighboring occurs int irequest = neighbor->request(this); neighbor->requests[irequest]->pair = 0; neighbor->requests[irequest]->fix = 1; neighbor->requests[irequest]->newton = 2; neighbor->requests[irequest]->ghost = 1; init_shielding(); init_taper(); if (strstr(update->integrate_style,"respa")) nlevels_respa = ((Respa *) update->integrate)->nlevels; } /* ---------------------------------------------------------------------- */ void FixQEqReax::init_list(int id, NeighList *ptr) { list = ptr; } /* ---------------------------------------------------------------------- */ void FixQEqReax::init_shielding() { int i,j; int ntypes; ntypes = atom->ntypes; memory->create(shld,ntypes+1,ntypes+1,"qeq:shileding"); for( i = 1; i <= ntypes; ++i ) for( j = 1; j <= ntypes; ++j ) shld[i][j] = pow( gamma[i] * gamma[j], -1.5 ); } /* ---------------------------------------------------------------------- */ void FixQEqReax::init_taper() { double d7, swa2, swa3, swb2, swb3; if (fabs(swa) > 0.01 && comm->me == 0) error->warning(FLERR,"Fix qeq/reax has non-zero lower Taper radius cutoff"); if (swb < 0) error->all(FLERR, "Fix qeq/reax has negative upper Taper radius cutoff"); else if (swb < 5 && comm->me == 0) error->warning(FLERR,"Fix qeq/reax has very low Taper radius cutoff"); d7 = pow( swb - swa, 7 ); swa2 = SQR( swa ); swa3 = CUBE( swa ); swb2 = SQR( swb ); swb3 = CUBE( swb ); Tap[7] = 20.0 / d7; Tap[6] = -70.0 * (swa + swb) / d7; Tap[5] = 84.0 * (swa2 + 3.0*swa*swb + swb2) / d7; Tap[4] = -35.0 * (swa3 + 9.0*swa2*swb + 9.0*swa*swb2 + swb3 ) / d7; Tap[3] = 140.0 * (swa3*swb + 3.0*swa2*swb2 + swa*swb3 ) / d7; Tap[2] =-210.0 * (swa3*swb2 + swa2*swb3) / d7; Tap[1] = 140.0 * swa3 * swb3 / d7; Tap[0] = (-35.0*swa3*swb2*swb2 + 21.0*swa2*swb3*swb2 + 7.0*swa*swb3*swb3 + swb3*swb3*swb ) / d7; } /* ---------------------------------------------------------------------- */ void FixQEqReax::setup_pre_force(int vflag) { neighbor->build_one(list->index); deallocate_storage(); allocate_storage(); init_storage(); deallocate_matrix(); allocate_matrix(); pre_force(vflag); } /* ---------------------------------------------------------------------- */ void FixQEqReax::setup_pre_force_respa(int vflag, int ilevel) { if (ilevel < nlevels_respa-1) return; setup_pre_force(vflag); } /* ---------------------------------------------------------------------- */ void FixQEqReax::min_setup_pre_force(int vflag) { setup_pre_force(vflag); } /* ---------------------------------------------------------------------- */ void FixQEqReax::init_storage() { N = atom->nlocal + atom->nghost; for( int i = 0; i < N; i++ ) { Hdia_inv[i] = 1. / eta[atom->type[i]]; b_s[i] = -chi[atom->type[i]]; b_t[i] = -1.0; b_prc[i] = 0; b_prm[i] = 0; s[i] = t[i] = 0; } } /* ---------------------------------------------------------------------- */ void FixQEqReax::pre_force(int vflag) { double t_start, t_end; if (update->ntimestep % nevery) return; if( comm->me == 0 ) t_start = MPI_Wtime(); n = atom->nlocal; N = atom->nlocal + atom->nghost; // grow arrays if necessary // need to be atom->nmax in length if( atom->nmax > nmax ) reallocate_storage(); if( n > n_cap*DANGER_ZONE || m_fill > m_cap*DANGER_ZONE ) reallocate_matrix(); init_matvec(); matvecs = CG(b_s, s); // CG on s - parallel matvecs += CG(b_t, t); // CG on t - parallel calculate_Q(); if( comm->me == 0 ) { t_end = MPI_Wtime(); qeq_time = t_end - t_start; } } /* ---------------------------------------------------------------------- */ void FixQEqReax::pre_force_respa(int vflag, int ilevel, int iloop) { if (ilevel == nlevels_respa-1) pre_force(vflag); } /* ---------------------------------------------------------------------- */ void FixQEqReax::min_pre_force(int vflag) { pre_force(vflag); } /* ---------------------------------------------------------------------- */ void FixQEqReax::init_matvec() { /* fill-in H matrix */ compute_H(); for( int i = 0; i < n; ++i ) { /* init pre-conditioner for H and init solution vectors */ Hdia_inv[i] = 1. / eta[ atom->type[i] ]; b_s[i] = -chi[ atom->type[i] ]; b_t[i] = -1.0; /* linear extrapolation for s & t from previous solutions */ //s[i] = 2 * s_hist[i][0] - s_hist[i][1]; //t[i] = 2 * t_hist[i][0] - t_hist[i][1]; /* quadratic extrapolation for s & t from previous solutions */ //s[i] = s_hist[i][2] + 3 * ( s_hist[i][0] - s_hist[i][1] ); t[i] = t_hist[i][2] + 3 * ( t_hist[i][0] - t_hist[i][1] ); /* cubic extrapolation for s & t from previous solutions */ s[i] = 4*(s_hist[i][0]+s_hist[i][2])-(6*s_hist[i][1]+s_hist[i][3]); //t[i] = 4*(t_hist[i][0]+t_hist[i][2])-(6*t_hist[i][1]+t_hist[i][3]); } pack_flag = 2; comm->forward_comm_fix(this); //Dist_vector( s ); pack_flag = 3; comm->forward_comm_fix(this); //Dist_vector( t ); } /* ---------------------------------------------------------------------- */ void FixQEqReax::compute_H() { int inum, jnum, *ilist, *jlist, *numneigh, **firstneigh; int i, j, ii, jj, temp, newnbr, flag; int *type, *tag; double **x, SMALL = 0.0001; double dx, dy, dz, r_sqr; type = atom->type; tag = atom->tag; x = atom->x; inum = list->inum; ilist = list->ilist; numneigh = list->numneigh; firstneigh = list->firstneigh; // fill in the H matrix m_fill = 0; r_sqr = 0; for( ii = 0; ii < inum; ii++ ) { i = ilist[ii]; jlist = firstneigh[i]; jnum = numneigh[i]; H.firstnbr[i] = m_fill; for( jj = 0; jj < jnum; jj++ ) { j = jlist[jj]; dx = x[j][0] - x[i][0]; dy = x[j][1] - x[i][1]; dz = x[j][2] - x[i][2]; r_sqr = SQR(dx) + SQR(dy) + SQR(dz); flag = 0; if (r_sqr <= SQR(swb)) { if (j < n) flag = 1; else if (tag[i] < tag[j]) flag = 1; else if (tag[i] == tag[j]) { if (dz > SMALL) flag = 1; else if (fabs(dz) < SMALL) { if (dy > SMALL) flag = 1; else if (fabs(dy) < SMALL && dx > SMALL) flag = 1; } } } if( flag ) { H.jlist[m_fill] = j; H.val[m_fill] = calculate_H( sqrt(r_sqr), shld[type[i]][type[j]] ); m_fill++; } } H.numnbrs[i] = m_fill - H.firstnbr[i]; } if (m_fill >= H.m) { char str[128]; sprintf(str,"H matrix size has been exceeded: m_fill=%d H.m=%d\n", m_fill, H.m ); error->warning(FLERR,str); error->all(FLERR,"Fix qeq/reax has insufficient QEq matrix size"); } } /* ---------------------------------------------------------------------- */ double FixQEqReax::calculate_H( double r, double gamma ) { double Taper, denom; Taper = Tap[7] * r + Tap[6]; Taper = Taper * r + Tap[5]; Taper = Taper * r + Tap[4]; Taper = Taper * r + Tap[3]; Taper = Taper * r + Tap[2]; Taper = Taper * r + Tap[1]; Taper = Taper * r + Tap[0]; denom = r * r * r + gamma; denom = pow(denom,0.3333333333333); return Taper * EV_TO_KCAL_PER_MOL / denom; } /* ---------------------------------------------------------------------- */ int FixQEqReax::CG( double *b, double *x ) { int i, j, imax; double tmp, alpha, beta, b_norm; double sig_old, sig_new, sig0; imax = 200; pack_flag = 1; sparse_matvec( &H, x, q ); comm->reverse_comm_fix( this ); //Coll_Vector( q ); vector_sum( r , 1., b, -1., q, n ); for( j = 0; j < n; ++j ) d[j] = r[j] * Hdia_inv[j]; //pre-condition b_norm = parallel_norm( b, n ); sig_new = parallel_dot( r, d, n ); sig0 = sig_new; for( i = 1; i < imax && sqrt(sig_new) / b_norm > tolerance; ++i ) { comm->forward_comm_fix(this); //Dist_vector( d ); sparse_matvec( &H, d, q ); comm->reverse_comm_fix(this); //Coll_vector( q ); tmp = parallel_dot( d, q, n ); alpha = sig_new / tmp; // comm->me, i, parallel_norm( d, n ), parallel_norm( q, n ), tmp ); vector_add( x, alpha, d, n ); vector_add( r, -alpha, q, n ); // pre-conditioning for( j = 0; j < n; ++j ) p[j] = r[j] * Hdia_inv[j]; sig_old = sig_new; sig_new = parallel_dot( r, p, n ); beta = sig_new / sig_old; vector_sum( d, 1., p, beta, d, n ); } if (i >= imax && comm->me == 0) { char str[128]; sprintf(str,"Fix qeq/reax CG convergence failed after %d iterations at %d step",i,update->ntimestep); error->warning(FLERR,str); } return i; } /* ---------------------------------------------------------------------- */ void FixQEqReax::sparse_matvec( sparse_matrix *A, double *x, double *b ) { int i, j, itr_j; for( i = 0; i < n; ++i ) b[i] = eta[ atom->type[i] ] * x[i]; for( i = n; i < N; ++i ) b[i] = 0; for( i = 0; i < n; ++i ) { 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 FixQEqReax::calculate_Q() { int i, k; double u, s_sum, t_sum; double *q = atom->q; s_sum = parallel_vector_acc( s, n ); t_sum = parallel_vector_acc( t, n); u = s_sum / t_sum; for( i = 0; i < n; ++i ) { q[i] = s[i] - u * t[i]; /* backup s & t */ 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 FixQEqReax::pack_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 1; } /* ---------------------------------------------------------------------- */ void FixQEqReax::unpack_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 FixQEqReax::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 1; } /* ---------------------------------------------------------------------- */ void FixQEqReax::unpack_reverse_comm(int n, int *list, double *buf) { for(int m = 0; m < n; m++) q[list[m]] += buf[m]; } /* ---------------------------------------------------------------------- memory usage of local atom-based arrays ------------------------------------------------------------------------- */ double FixQEqReax::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 FixQEqReax::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 FixQEqReax::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 FixQEqReax::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 FixQEqReax::unpack_exchange(int nlocal, double *buf) { for (int m = 0; m < nprev; m++) s_hist[nlocal][m] = buf[m]; for (int m = 0; m < nprev; m++) t_hist[nlocal][m] = buf[nprev+m]; return nprev*2; } /* ---------------------------------------------------------------------- */ double FixQEqReax::parallel_norm( double *v, int n ) { int i; double my_sum, norm_sqr; my_sum = 0; for( i = 0; i < n; ++i ) my_sum += SQR( v[i] ); MPI_Allreduce( &my_sum, &norm_sqr, 1, MPI_DOUBLE, MPI_SUM, world ); return sqrt( norm_sqr ); } /* ---------------------------------------------------------------------- */ double FixQEqReax::parallel_dot( double *v1, double *v2, int n ) { int i; double my_dot, res; my_dot = 0; res = 0; for( i = 0; i < n; ++i ) my_dot += v1[i] * v2[i]; MPI_Allreduce( &my_dot, &res, 1, MPI_DOUBLE, MPI_SUM, world ); return res; } /* ---------------------------------------------------------------------- */ double FixQEqReax::parallel_vector_acc( double *v, int n ) { int i; double my_acc, res; my_acc = 0; for( i = 0; i < n; ++i ) my_acc += v[i]; MPI_Allreduce( &my_acc, &res, 1, MPI_DOUBLE, MPI_SUM, world ); return res; } /* ---------------------------------------------------------------------- */ double FixQEqReax::norm( double* v1, int k ) { double ret = 0; for( --k; k>=0; --k ) ret += ( v1[k] * v1[k] ); return sqrt( ret ); } /* ---------------------------------------------------------------------- */ void FixQEqReax::vector_sum( double* dest, double c, double* v, double d, double* y, int k ) { for( --k; k>=0; --k ) dest[k] = c * v[k] + d * y[k]; } /* ---------------------------------------------------------------------- */ void FixQEqReax::vector_scale( double* dest, double c, double* v, int k ) { for( --k; k>=0; --k ) dest[k] = c * v[k]; } /* ---------------------------------------------------------------------- */ double FixQEqReax::dot( double* v1, double* v2, int k ) { double ret = 0; for( --k; k>=0; --k ) ret += v1[k] * v2[k]; return ret; } /* ---------------------------------------------------------------------- */ void FixQEqReax::vector_add( double* dest, double c, double* v, int k ) { for( --k; k>=0; --k ) dest[k] += c * v[k]; } diff --git a/src/USER-REAXC/pair_reax_c.cpp b/src/USER-REAXC/pair_reax_c.cpp index 4c1eea4f5..dd7c0e558 100644 --- a/src/USER-REAXC/pair_reax_c.cpp +++ b/src/USER-REAXC/pair_reax_c.cpp @@ -1,729 +1,738 @@ /* ---------------------------------------------------------------------- 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: Hasan Metin Aktulga, Purdue University (now at Lawrence Berkeley National Laboratory, hmaktulga@lbl.gov) - Please cite the related publication: - H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, - "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. - --- Per-atom energy/virial added by Ray Shan (Sandia) Fix reax/c/bonds and fix reax/c/species for pair_style reax/c added by Ray Shan (Sandia) ------------------------------------------------------------------------- */ #include "pair_reax_c.h" #include "atom.h" #include "update.h" #include "force.h" #include "comm.h" #include "neighbor.h" #include "neigh_list.h" #include "neigh_request.h" #include "modify.h" #include "fix.h" #include "fix_reax_c.h" +#include "citeme.h" #include "memory.h" #include "error.h" #include "reaxc_types.h" #include "reaxc_allocate.h" #include "reaxc_control.h" #include "reaxc_ffield.h" #include "reaxc_forces.h" #include "reaxc_init_md.h" #include "reaxc_io_tools.h" #include "reaxc_list.h" #include "reaxc_lookup.h" #include "reaxc_reset_tools.h" #include "reaxc_traj.h" #include "reaxc_vector.h" #include "fix_reaxc_bonds.h" using namespace LAMMPS_NS; +static const char cite_pair_reax_c[] = + "pair reax/c command:\n\n" + "@Article{Aktulga12,\n" + " author = {H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama},\n" + " title = {Parallel reactive molecular dynamics: Numerical methods and algorithmic techniques},\n" + " journal = {Parallel Computing},\n" + " year = 2012,\n" + " volume = 38,\n" + " pages = {245--259}\n" + "}\n\n"; + /* ---------------------------------------------------------------------- */ PairReaxC::PairReaxC(LAMMPS *lmp) : Pair(lmp) { + if (lmp->citeme) lmp->citeme->add(cite_pair_reax_c); + single_enable = 0; restartinfo = 0; one_coeff = 1; manybody_flag = 1; ghostneigh = 1; system = (reax_system *) memory->smalloc(sizeof(reax_system),"reax:system"); control = (control_params *) memory->smalloc(sizeof(control_params),"reax:control"); data = (simulation_data *) memory->smalloc(sizeof(simulation_data),"reax:data"); workspace = (storage *) memory->smalloc(sizeof(storage),"reax:storage"); lists = (reax_list *) memory->smalloc(LIST_N * sizeof(reax_list),"reax:lists"); out_control = (output_controls *) memory->smalloc(sizeof(output_controls),"reax:out_control"); mpi_data = (mpi_datatypes *) memory->smalloc(sizeof(mpi_datatypes),"reax:mpi"); MPI_Comm_rank(world,&system->my_rank); system->my_coords[0] = 0; system->my_coords[1] = 0; system->my_coords[2] = 0; system->num_nbrs = 0; system->n = 0; // my atoms system->N = 0; // mine + ghosts system->bigN = 0; // all atoms in the system system->local_cap = 0; system->total_cap = 0; system->gcell_cap = 0; system->bndry_cuts.ghost_nonb = 0; system->bndry_cuts.ghost_hbond = 0; system->bndry_cuts.ghost_bond = 0; system->bndry_cuts.ghost_cutoff = 0; system->my_atoms = NULL; system->pair_ptr = this; fix_reax = NULL; nextra = 14; pvector = new double[nextra]; setup_flag = 0; nmax = 0; } /* ---------------------------------------------------------------------- */ PairReaxC::~PairReaxC() { if (fix_reax) modify->delete_fix("REAXC"); if (setup_flag) { Close_Output_Files( system, control, out_control, mpi_data ); // deallocate reax data-structures if( control->tabulate ) Deallocate_Lookup_Tables( system ); if( control->hbond_cut > 0 ) Delete_List( lists+HBONDS, world ); Delete_List( lists+BONDS, world ); Delete_List( lists+THREE_BODIES, world ); Delete_List( lists+FAR_NBRS, world ); DeAllocate_Workspace( control, workspace ); DeAllocate_System( system ); } memory->destroy( system ); memory->destroy( control ); memory->destroy( data ); memory->destroy( workspace ); memory->destroy( lists ); memory->destroy( out_control ); memory->destroy( mpi_data ); // deallocate interface storage if( allocated ) { memory->destroy(setflag); memory->destroy(cutsq); memory->destroy(cutghost); delete [] map; delete [] chi; delete [] eta; delete [] gamma; } delete [] pvector; } /* ---------------------------------------------------------------------- */ void PairReaxC::allocate( ) { allocated = 1; int n = atom->ntypes; memory->create(setflag,n+1,n+1,"pair:setflag"); memory->create(cutsq,n+1,n+1,"pair:cutsq"); memory->create(cutghost,n+1,n+1,"pair:cutghost"); map = new int[n+1]; chi = new double[n+1]; eta = new double[n+1]; gamma = new double[n+1]; } /* ---------------------------------------------------------------------- */ void PairReaxC::settings(int narg, char **arg) { if (narg < 1) error->all(FLERR,"Illegal pair_style command"); // read name of control file or use default controls if (strcmp(arg[0],"NULL") == 0) { strcpy( control->sim_name, "simulate" ); control->ensemble = 0; out_control->energy_update_freq = 0; control->tabulate = 0; control->reneighbor = 1; control->vlist_cut = control->nonb_cut; control->bond_cut = 5.; control->hbond_cut = 7.50; control->thb_cut = 0.001; control->thb_cutsq = 0.00001; control->bg_cut = 0.3; out_control->write_steps = 0; out_control->traj_method = 0; strcpy( out_control->traj_title, "default_title" ); out_control->atom_info = 0; out_control->bond_info = 0; out_control->angle_info = 0; } else Read_Control_File(arg[0], control, out_control); // default values qeqflag = 1; control->lgflag = 0; system->mincap = MIN_CAP; system->safezone = SAFE_ZONE; system->saferzone = SAFER_ZONE; // process optional keywords int iarg = 1; while (iarg < narg) { if (strcmp(arg[iarg],"checkqeq") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal pair_style reax/c command"); if (strcmp(arg[iarg+1],"yes") == 0) qeqflag = 1; else if (strcmp(arg[iarg+1],"no") == 0) qeqflag = 0; else error->all(FLERR,"Illegal pair_style reax/c command"); iarg += 2; } else if (strcmp(arg[iarg],"lgvdw") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal pair_style reax/c command"); if (strcmp(arg[iarg+1],"yes") == 0) control->lgflag = 1; else if (strcmp(arg[iarg+1],"no") == 0) control->lgflag = 0; else error->all(FLERR,"Illegal pair_style reax/c command"); iarg += 2; } else if (strcmp(arg[iarg],"safezone") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal pair_style reax/c command"); system->safezone = force->numeric(FLERR,arg[iarg+1]); if (system->safezone < 0.0) error->all(FLERR,"Illegal pair_style reax/c safezone command"); system->saferzone = system->safezone + 0.2; iarg += 2; } else if (strcmp(arg[iarg],"mincap") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal pair_style reax/c command"); system->mincap = force->inumeric(FLERR,arg[iarg+1]); if (system->mincap < 0) error->all(FLERR,"Illegal pair_style reax/c mincap command"); iarg += 2; } else error->all(FLERR,"Illegal pair_style reax/c command"); } // LAMMPS is responsible for generating nbrs control->reneighbor = 1; } /* ---------------------------------------------------------------------- */ void PairReaxC::coeff( int nargs, char **args ) { if (!allocated) allocate(); if (nargs != 3 + atom->ntypes) error->all(FLERR,"Incorrect args for pair coefficients"); // insure I,J args are * * if (strcmp(args[0],"*") != 0 || strcmp(args[1],"*") != 0) error->all(FLERR,"Incorrect args for pair coefficients"); // read ffield file Read_Force_Field(args[2], &(system->reax_param), control); // read args that map atom types to elements in potential file // map[i] = which element the Ith atom type is, -1 if NULL int itmp; int nreax_types = system->reax_param.num_atom_types; for (int i = 3; i < nargs; i++) { if (strcmp(args[i],"NULL") == 0) { map[i-2] = -1; continue; } } int n = atom->ntypes; // pair_coeff element map itmp = 0; for (int i = 3; i < nargs; i++) for (int j = 0; j < nreax_types; j++) if (strcasecmp(args[i],system->reax_param.sbp[j].name) == 0) { map[i-2] = j; itmp ++; } // error check if (itmp != n) error->all(FLERR,"Non-existent ReaxFF type"); int count = 0; for (int i = 1; i <= n; i++) for (int j = i; j <= n; j++) { setflag[i][j] = 1; count++; } if (count == 0) error->all(FLERR,"Incorrect args for pair coefficients"); } /* ---------------------------------------------------------------------- */ void PairReaxC::init_style( ) { if (!atom->q_flag) error->all(FLERR,"Pair reax/c requires atom attribute q"); // firstwarn = 1; int iqeq; for (iqeq = 0; iqeq < modify->nfix; iqeq++) if (strcmp(modify->fix[iqeq]->style,"qeq/reax") == 0) break; if (iqeq == modify->nfix && qeqflag == 1) error->all(FLERR,"Pair reax/c requires use of fix qeq/reax"); system->n = atom->nlocal; // my atoms system->N = atom->nlocal + atom->nghost; // mine + ghosts system->bigN = static_cast (atom->natoms); // all atoms in the system system->wsize = comm->nprocs; system->big_box.V = 0; system->big_box.box_norms[0] = 0; system->big_box.box_norms[1] = 0; system->big_box.box_norms[2] = 0; if (atom->tag_enable == 0) error->all(FLERR,"Pair style reax/c requires atom IDs"); if (force->newton_pair == 0) error->all(FLERR,"Pair style reax/c requires newton pair on"); // need a half neighbor list w/ Newton off and ghost neighbors // built whenever re-neighboring occurs int irequest = neighbor->request(this); neighbor->requests[irequest]->newton = 2; neighbor->requests[irequest]->ghost = 1; cutmax = MAX3(control->nonb_cut, control->hbond_cut, 2*control->bond_cut); for( int i = 0; i < LIST_N; ++i ) lists[i].allocated = 0; if (fix_reax == NULL) { char **fixarg = new char*[3]; fixarg[0] = (char *) "REAXC"; fixarg[1] = (char *) "all"; fixarg[2] = (char *) "REAXC"; modify->add_fix(3,fixarg); delete [] fixarg; fix_reax = (FixReaxC *) modify->fix[modify->nfix-1]; } } /* ---------------------------------------------------------------------- */ void PairReaxC::setup( ) { int oldN; int mincap = system->mincap; double safezone = system->safezone; system->n = atom->nlocal; // my atoms system->N = atom->nlocal + atom->nghost; // mine + ghosts oldN = system->N; system->bigN = static_cast (atom->natoms); // all atoms in the system if (setup_flag == 0) { setup_flag = 1; int *num_bonds = fix_reax->num_bonds; int *num_hbonds = fix_reax->num_hbonds; control->vlist_cut = neighbor->cutneighmax; // determine the local and total capacity system->local_cap = MAX( (int)(system->n * safezone), mincap ); system->total_cap = MAX( (int)(system->N * safezone), mincap ); // initialize my data structures PreAllocate_Space( system, control, workspace, world ); write_reax_atoms(); int num_nbrs = estimate_reax_lists(); if(!Make_List(system->total_cap, num_nbrs, TYP_FAR_NEIGHBOR, lists+FAR_NBRS, world)) error->all(FLERR,"Pair reax/c problem in far neighbor list"); write_reax_lists(); Initialize( system, control, data, workspace, &lists, out_control, mpi_data, world ); for( int k = 0; k < system->N; ++k ) { num_bonds[k] = system->my_atoms[k].num_bonds; num_hbonds[k] = system->my_atoms[k].num_hbonds; } } else { // fill in reax datastructures write_reax_atoms(); // reset the bond list info for new atoms for(int k = oldN; k < system->N; ++k) Set_End_Index( k, Start_Index( k, lists+BONDS ), lists+BONDS ); // check if I need to shrink/extend my data-structs ReAllocate( system, control, data, workspace, &lists, mpi_data ); } } /* ---------------------------------------------------------------------- */ double PairReaxC::init_one(int i, int j) { cutghost[i][j] = cutghost[j][i] = cutmax; return cutmax; } /* ---------------------------------------------------------------------- */ void PairReaxC::compute(int eflag, int vflag) { double evdwl,ecoul; double t_start, t_end; // communicate num_bonds once every reneighboring // 2 num arrays stored by fix, grab ptr to them if (neighbor->ago == 0) comm->forward_comm_fix(fix_reax); int *num_bonds = fix_reax->num_bonds; int *num_hbonds = fix_reax->num_hbonds; evdwl = ecoul = 0.0; if (eflag || vflag) ev_setup(eflag,vflag); else ev_unset(); if (vflag_global) control->virial = 1; else control->virial = 0; system->n = atom->nlocal; // my atoms system->N = atom->nlocal + atom->nghost; // mine + ghosts system->bigN = static_cast (atom->natoms); // all atoms in the system system->big_box.V = 0; system->big_box.box_norms[0] = 0; system->big_box.box_norms[1] = 0; system->big_box.box_norms[2] = 0; if( comm->me == 0 ) t_start = MPI_Wtime(); // setup data structures setup(); Reset( system, control, data, workspace, &lists, world ); workspace->realloc.num_far = write_reax_lists(); // timing for filling in the reax lists if( comm->me == 0 ) { t_end = MPI_Wtime(); data->timing.nbrs = t_end - t_start; } // forces Compute_Forces(system,control,data,workspace,&lists,out_control,mpi_data); read_reax_forces(); for(int k = 0; k < system->N; ++k) { num_bonds[k] = system->my_atoms[k].num_bonds; num_hbonds[k] = system->my_atoms[k].num_hbonds; } // energies and pressure if (eflag_global) { evdwl += data->my_en.e_bond; evdwl += data->my_en.e_ov; evdwl += data->my_en.e_un; evdwl += data->my_en.e_lp; evdwl += data->my_en.e_ang; evdwl += data->my_en.e_pen; evdwl += data->my_en.e_coa; evdwl += data->my_en.e_hb; evdwl += data->my_en.e_tor; evdwl += data->my_en.e_con; evdwl += data->my_en.e_vdW; ecoul += data->my_en.e_ele; ecoul += data->my_en.e_pol; // eng_vdwl += evdwl; // eng_coul += ecoul; // Store the different parts of the energy // in a list for output by compute pair command pvector[0] = data->my_en.e_bond; pvector[1] = data->my_en.e_ov + data->my_en.e_un; pvector[2] = data->my_en.e_lp; pvector[3] = 0.0; pvector[4] = data->my_en.e_ang; pvector[5] = data->my_en.e_pen; pvector[6] = data->my_en.e_coa; pvector[7] = data->my_en.e_hb; pvector[8] = data->my_en.e_tor; pvector[9] = data->my_en.e_con; pvector[10] = data->my_en.e_vdW; pvector[11] = data->my_en.e_ele; pvector[12] = 0.0; pvector[13] = data->my_en.e_pol; } if (vflag_fdotr) virial_fdotr_compute(); // Set internal timestep counter to that of LAMMPS data->step = update->ntimestep; Output_Results( system, control, data, &lists, out_control, mpi_data ); } /* ---------------------------------------------------------------------- */ void PairReaxC::write_reax_atoms() { int *num_bonds = fix_reax->num_bonds; int *num_hbonds = fix_reax->num_hbonds; if (system->N > system->total_cap) error->all(FLERR,"Too many ghost atoms"); for( int i = 0; i < system->N; ++i ){ system->my_atoms[i].orig_id = atom->tag[i]; system->my_atoms[i].type = map[atom->type[i]]; system->my_atoms[i].x[0] = atom->x[i][0]; system->my_atoms[i].x[1] = atom->x[i][1]; system->my_atoms[i].x[2] = atom->x[i][2]; system->my_atoms[i].q = atom->q[i]; system->my_atoms[i].num_bonds = num_bonds[i]; system->my_atoms[i].num_hbonds = num_hbonds[i]; } } /* ---------------------------------------------------------------------- */ void PairReaxC::get_distance( rvec xj, rvec xi, double *d_sqr, rvec *dvec ) { (*dvec)[0] = xj[0] - xi[0]; (*dvec)[1] = xj[1] - xi[1]; (*dvec)[2] = xj[2] - xi[2]; *d_sqr = SQR((*dvec)[0]) + SQR((*dvec)[1]) + SQR((*dvec)[2]); } /* ---------------------------------------------------------------------- */ void PairReaxC::set_far_nbr( far_neighbor_data *fdest, int j, double d, rvec dvec ) { fdest->nbr = j; fdest->d = d; rvec_Copy( fdest->dvec, dvec ); ivec_MakeZero( fdest->rel_box ); } /* ---------------------------------------------------------------------- */ int PairReaxC::estimate_reax_lists() { int itr_i, itr_j, itr_g, i, j, g; int nlocal, nghost, num_nbrs, num_marked; int *ilist, *jlist, *numneigh, **firstneigh, *marked; double d_sqr, g_d_sqr; rvec dvec, g_dvec; double *dist, **x; reax_list *far_nbrs; far_neighbor_data *far_list; int mincap = system->mincap; double safezone = system->safezone; x = atom->x; nlocal = atom->nlocal; nghost = atom->nghost; ilist = list->ilist; numneigh = list->numneigh; firstneigh = list->firstneigh; far_nbrs = lists + FAR_NBRS; far_list = far_nbrs->select.far_nbr_list; num_nbrs = 0; num_marked = 0; marked = (int*) calloc( system->N, sizeof(int) ); dist = (double*) calloc( system->N, sizeof(double) ); int inum = list->inum; int gnum = list->gnum; int numall = inum + gnum; for( itr_i = 0; itr_i < inum+gnum; ++itr_i ){ i = ilist[itr_i]; marked[i] = 1; ++num_marked; jlist = firstneigh[i]; for( itr_j = 0; itr_j < numneigh[i]; ++itr_j ){ j = jlist[itr_j]; j &= NEIGHMASK; get_distance( x[j], x[i], &d_sqr, &dvec ); if( d_sqr <= SQR(control->nonb_cut) ) ++num_nbrs; } } free( marked ); free( dist ); return static_cast (MAX( num_nbrs*safezone, mincap*MIN_NBRS )); } /* ---------------------------------------------------------------------- */ int PairReaxC::write_reax_lists() { int itr_i, itr_j, itr_g, i, j, g, flag; int nlocal, nghost, num_nbrs; int *ilist, *jlist, *numneigh, **firstneigh, *marked, *tag; double d_sqr, g_d, g_d_sqr; rvec dvec, g_dvec; double *dist, **x, SMALL = 0.0001; reax_list *far_nbrs; far_neighbor_data *far_list; x = atom->x; tag = atom->tag; nlocal = atom->nlocal; nghost = atom->nghost; ilist = list->ilist; numneigh = list->numneigh; firstneigh = list->firstneigh; far_nbrs = lists + FAR_NBRS; far_list = far_nbrs->select.far_nbr_list; num_nbrs = 0; marked = (int*) calloc( system->N, sizeof(int) ); dist = (double*) calloc( system->N, sizeof(double) ); int inum = list->inum; int gnum = list->gnum; int numall = inum + gnum; for( itr_i = 0; itr_i < inum+gnum; ++itr_i ){ i = ilist[itr_i]; marked[i] = 1; jlist = firstneigh[i]; Set_Start_Index( i, num_nbrs, far_nbrs ); for( itr_j = 0; itr_j < numneigh[i]; ++itr_j ){ j = jlist[itr_j]; j &= NEIGHMASK; get_distance( x[j], x[i], &d_sqr, &dvec ); if( d_sqr <= (control->nonb_cut*control->nonb_cut) ){ dist[j] = sqrt( d_sqr ); set_far_nbr( &far_list[num_nbrs], j, dist[j], dvec ); ++num_nbrs; } } Set_End_Index( i, num_nbrs, far_nbrs ); } free( marked ); free( dist ); return num_nbrs; } /* ---------------------------------------------------------------------- */ void PairReaxC::read_reax_forces() { for( int i = 0; i < system->N; ++i ) { system->my_atoms[i].f[0] = workspace->f[i][0]; system->my_atoms[i].f[1] = workspace->f[i][1]; system->my_atoms[i].f[2] = workspace->f[i][2]; atom->f[i][0] = -workspace->f[i][0]; atom->f[i][1] = -workspace->f[i][1]; atom->f[i][2] = -workspace->f[i][2]; } } /* ---------------------------------------------------------------------- */ void *PairReaxC::extract(const char *str, int &dim) { dim = 1; if (strcmp(str,"chi") == 0 && chi) { for (int i = 1; i <= atom->ntypes; i++) if (map[i] >= 0) chi[i] = system->reax_param.sbp[map[i]].chi; else chi[i] = 0.0; return (void *) chi; } if (strcmp(str,"eta") == 0 && eta) { for (int i = 1; i <= atom->ntypes; i++) if (map[i] >= 0) eta[i] = system->reax_param.sbp[map[i]].eta; else eta[i] = 0.0; return (void *) eta; } if (strcmp(str,"gamma") == 0 && gamma) { for (int i = 1; i <= atom->ntypes; i++) if (map[i] >= 0) gamma[i] = system->reax_param.sbp[map[i]].gamma; else gamma[i] = 0.0; return (void *) gamma; } return NULL; } /* ---------------------------------------------------------------------- */ diff --git a/src/neighbor.cpp b/src/neighbor.cpp index 5a7c5312a..b7329d7b0 100644 --- a/src/neighbor.cpp +++ b/src/neighbor.cpp @@ -1,1963 +1,1963 @@ /* ---------------------------------------------------------------------- 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 (triclinic and multi-neigh) : Pieter in 't Veld (SNL) ------------------------------------------------------------------------- */ #include "lmptype.h" #include "mpi.h" #include "math.h" #include "stdlib.h" #include "string.h" #include "neighbor.h" #include "neigh_list.h" #include "neigh_request.h" #include "atom.h" #include "atom_vec.h" #include "comm.h" #include "force.h" #include "pair.h" #include "domain.h" #include "group.h" #include "modify.h" #include "fix.h" #include "compute.h" #include "update.h" #include "respa.h" #include "output.h" #include "citeme.h" #include "memory.h" #include "error.h" using namespace LAMMPS_NS; #define RQDELTA 1 #define EXDELTA 1 #define LB_FACTOR 1.5 #define SMALL 1.0e-6 #define BIG 1.0e20 #define CUT2BIN_RATIO 100 enum{NSQ,BIN,MULTI}; // also in neigh_list.cpp static const char cite_neigh_multi[] = "neighbor multi command:\n\n" - "@Article{intveld08,\n" + "@Article{Intveld08,\n" " author = {P.{\\,}J.~in{\\,}'t~Veld and S.{\\,}J.~Plimpton" " and G.{\\,}S.~Grest},\n" " title = {Accurate and Efficient Methods for Modeling Colloidal\n" " Mixtures in an Explicit Solvent using Molecular Dynamics},\n" " journal = {Comp.~Phys.~Comm.},\n" " year = 2008,\n" " volume = 179,\n" " pages = {320--329}\n" "}\n\n"; //#define NEIGH_LIST_DEBUG 1 /* ---------------------------------------------------------------------- */ Neighbor::Neighbor(LAMMPS *lmp) : Pointers(lmp) { MPI_Comm_rank(world,&me); MPI_Comm_size(world,&nprocs); style = BIN; every = 1; delay = 10; dist_check = 1; pgsize = 100000; oneatom = 2000; binsizeflag = 0; build_once = 0; cluster_check = 0; cutneighsq = NULL; cutneighghostsq = NULL; cuttype = NULL; cuttypesq = NULL; fixchecklist = NULL; // coords at last neighboring maxhold = 0; xhold = NULL; // binning maxhead = 0; binhead = NULL; maxbin = 0; bins = NULL; // pair exclusion list info includegroup = 0; nex_type = maxex_type = 0; ex1_type = ex2_type = NULL; ex_type = NULL; nex_group = maxex_group = 0; ex1_group = ex2_group = ex1_bit = ex2_bit = NULL; nex_mol = maxex_mol = 0; ex_mol_group = ex_mol_bit = NULL; // pair lists maxatom = 0; nblist = nglist = nslist = 0; nlist = 0; lists = NULL; pair_build = NULL; stencil_create = NULL; blist = glist = slist = NULL; anyghostlist = 0; nrequest = maxrequest = 0; requests = NULL; old_style = BIN; old_triclinic = 0; old_pgsize = pgsize; old_oneatom = oneatom; old_nrequest = 0; old_requests = NULL; // bond lists maxbond = 0; bondlist = NULL; maxangle = 0; anglelist = NULL; maxdihedral = 0; dihedrallist = NULL; maximproper = 0; improperlist = NULL; } /* ---------------------------------------------------------------------- */ Neighbor::~Neighbor() { memory->destroy(cutneighsq); memory->destroy(cutneighghostsq); delete [] cuttype; delete [] cuttypesq; delete [] fixchecklist; memory->destroy(xhold); memory->destroy(binhead); memory->destroy(bins); memory->destroy(ex1_type); memory->destroy(ex2_type); memory->destroy(ex_type); memory->destroy(ex1_group); memory->destroy(ex2_group); delete [] ex1_bit; delete [] ex2_bit; memory->destroy(ex_mol_group); delete [] ex_mol_bit; for (int i = 0; i < nlist; i++) delete lists[i]; delete [] lists; delete [] pair_build; delete [] stencil_create; delete [] blist; delete [] glist; delete [] slist; for (int i = 0; i < nrequest; i++) delete requests[i]; memory->sfree(requests); for (int i = 0; i < old_nrequest; i++) delete old_requests[i]; memory->sfree(old_requests); memory->destroy(bondlist); memory->destroy(anglelist); memory->destroy(dihedrallist); memory->destroy(improperlist); } /* ---------------------------------------------------------------------- */ void Neighbor::init() { int i,j,m,n; ncalls = ndanger = 0; dimension = domain->dimension; triclinic = domain->triclinic; newton_pair = force->newton_pair; // error check if (delay > 0 && (delay % every) != 0) error->all(FLERR,"Neighbor delay must be 0 or multiple of every setting"); if (pgsize < 10*oneatom) error->all(FLERR,"Neighbor page size must be >= 10x the one atom setting"); // ------------------------------------------------------------------ // settings // bbox lo/hi = bounding box of entire domain, stored by Domain if (triclinic == 0) { bboxlo = domain->boxlo; bboxhi = domain->boxhi; } else { bboxlo = domain->boxlo_bound; bboxhi = domain->boxhi_bound; } // set neighbor cutoffs (force cutoff + skin) // trigger determines when atoms migrate and neighbor lists are rebuilt // needs to be non-zero for migration distance check // even if pair = NULL and no neighbor lists are used // cutneigh = force cutoff + skin if cutforce > 0, else cutneigh = 0 triggersq = 0.25*skin*skin; boxcheck = 0; if (domain->box_change && (domain->xperiodic || domain->yperiodic || (dimension == 3 && domain->zperiodic))) boxcheck = 1; n = atom->ntypes; if (cutneighsq == NULL) { memory->create(cutneighsq,n+1,n+1,"neigh:cutneighsq"); memory->create(cutneighghostsq,n+1,n+1,"neigh:cutneighghostsq"); cuttype = new double[n+1]; cuttypesq = new double[n+1]; } double cutoff,delta,cut; cutneighmin = BIG; cutneighmax = 0.0; for (i = 1; i <= n; i++) { cuttype[i] = cuttypesq[i] = 0.0; for (j = 1; j <= n; j++) { if (force->pair) cutoff = sqrt(force->pair->cutsq[i][j]); else cutoff = 0.0; if (cutoff > 0.0) delta = skin; else delta = 0.0; cut = cutoff + delta; cutneighsq[i][j] = cut*cut; cuttype[i] = MAX(cuttype[i],cut); cuttypesq[i] = MAX(cuttypesq[i],cut*cut); cutneighmin = MIN(cutneighmin,cut); cutneighmax = MAX(cutneighmax,cut); if (force->pair && force->pair->ghostneigh) { cut = force->pair->cutghost[i][j] + skin; cutneighghostsq[i][j] = cut*cut; } } } cutneighmaxsq = cutneighmax * cutneighmax; // check other classes that can induce reneighboring in decide() // don't check if build_once is set restart_check = 0; if (output->restart_flag) restart_check = 1; delete [] fixchecklist; fixchecklist = NULL; fixchecklist = new int[modify->nfix]; fix_check = 0; for (i = 0; i < modify->nfix; i++) if (modify->fix[i]->force_reneighbor) fixchecklist[fix_check++] = i; must_check = 0; if (restart_check || fix_check) must_check = 1; if (build_once) must_check = 0; // set special_flag for 1-2, 1-3, 1-4 neighbors // flag[0] is not used, flag[1] = 1-2, flag[2] = 1-3, flag[3] = 1-4 // flag = 0 if both LJ/Coulomb special values are 0.0 // flag = 1 if both LJ/Coulomb special values are 1.0 // flag = 2 otherwise or if KSpace solver is enabled // pairwise portion of KSpace solver uses all 1-2,1-3,1-4 neighbors if (force->special_lj[1] == 0.0 && force->special_coul[1] == 0.0) special_flag[1] = 0; else if (force->special_lj[1] == 1.0 && force->special_coul[1] == 1.0) special_flag[1] = 1; else special_flag[1] = 2; if (force->special_lj[2] == 0.0 && force->special_coul[2] == 0.0) special_flag[2] = 0; else if (force->special_lj[2] == 1.0 && force->special_coul[2] == 1.0) special_flag[2] = 1; else special_flag[2] = 2; if (force->special_lj[3] == 0.0 && force->special_coul[3] == 0.0) special_flag[3] = 0; else if (force->special_lj[3] == 1.0 && force->special_coul[3] == 1.0) special_flag[3] = 1; else special_flag[3] = 2; if (force->kspace) special_flag[1] = special_flag[2] = special_flag[3] = 2; // maxwt = max multiplicative factor on atom indices stored in neigh list maxwt = 0; if (special_flag[1] == 2) maxwt = 2; if (special_flag[2] == 2) maxwt = 3; if (special_flag[3] == 2) maxwt = 4; // rRESPA cutoffs int respa = 0; if (update->whichflag == 1 && strstr(update->integrate_style,"respa")) { if (((Respa *) update->integrate)->level_inner >= 0) respa = 1; if (((Respa *) update->integrate)->level_middle >= 0) respa = 2; } if (respa) { double *cut_respa = ((Respa *) update->integrate)->cutoff; cut_inner_sq = (cut_respa[1] + skin) * (cut_respa[1] + skin); cut_middle_sq = (cut_respa[3] + skin) * (cut_respa[3] + skin); cut_middle_inside_sq = (cut_respa[0] - skin) * (cut_respa[0] - skin); if (cut_respa[0]-skin < 0) cut_middle_inside_sq = 0.0; } // ------------------------------------------------------------------ // xhold, bins, exclusion lists // free xhold and bins if not needed for this run if (dist_check == 0) { memory->destroy(xhold); maxhold = 0; xhold = NULL; } if (style == NSQ) { memory->destroy(bins); memory->destroy(binhead); maxbin = maxhead = 0; binhead = NULL; bins = NULL; } // 1st time allocation of xhold and bins if (dist_check) { if (maxhold == 0) { maxhold = atom->nmax; memory->create(xhold,maxhold,3,"neigh:xhold"); } } if (style != NSQ) { if (maxbin == 0) { maxbin = atom->nmax; memory->create(bins,maxbin,"bins"); } } // exclusion lists for type, group, molecule settings from neigh_modify // warn if exclusions used with KSpace solver n = atom->ntypes; if (nex_type == 0 && nex_group == 0 && nex_mol == 0) exclude = 0; else exclude = 1; if (nex_type) { memory->destroy(ex_type); memory->create(ex_type,n+1,n+1,"neigh:ex_type"); for (i = 1; i <= n; i++) for (j = 1; j <= n; j++) ex_type[i][j] = 0; for (i = 0; i < nex_type; i++) { if (ex1_type[i] <= 0 || ex1_type[i] > n || ex2_type[i] <= 0 || ex2_type[i] > n) error->all(FLERR,"Invalid atom type in neighbor exclusion list"); ex_type[ex1_type[i]][ex2_type[i]] = 1; ex_type[ex2_type[i]][ex1_type[i]] = 1; } } if (nex_group) { delete [] ex1_bit; delete [] ex2_bit; ex1_bit = new int[nex_group]; ex2_bit = new int[nex_group]; for (i = 0; i < nex_group; i++) { ex1_bit[i] = group->bitmask[ex1_group[i]]; ex2_bit[i] = group->bitmask[ex2_group[i]]; } } if (nex_mol) { delete [] ex_mol_bit; ex_mol_bit = new int[nex_mol]; for (i = 0; i < nex_mol; i++) ex_mol_bit[i] = group->bitmask[ex_mol_group[i]]; } if (exclude && force->kspace && me == 0) error->warning(FLERR,"Neighbor exclusions used with KSpace solver " "may give inconsistent Coulombic energies"); // ------------------------------------------------------------------ // pairwise lists // test if pairwise lists need to be re-created // no need to re-create if: // neigh style, triclinic, pgsize, oneatom have not changed // current requests = old requests int same = 1; if (style != old_style) same = 0; if (triclinic != old_triclinic) same = 0; if (pgsize != old_pgsize) same = 0; if (oneatom != old_oneatom) same = 0; if (nrequest != old_nrequest) same = 0; else for (i = 0; i < nrequest; i++) if (requests[i]->identical(old_requests[i]) == 0) same = 0; #ifdef NEIGH_LIST_DEBUG if (comm->me == 0) printf("SAME flag %d\n",same); #endif // if old and new are not the same, create new pairwise lists if (!same) { // delete old lists and create new ones for (i = 0; i < nlist; i++) delete lists[i]; delete [] lists; delete [] pair_build; delete [] stencil_create; nlist = nrequest; lists = new NeighList*[nlist]; pair_build = new PairPtr[nlist]; stencil_create = new StencilPtr[nlist]; // create individual lists, one per request // copy dnum setting from request to list // pass list ptr back to requestor (except for Command class) for (i = 0; i < nlist; i++) { lists[i] = new NeighList(lmp); lists[i]->setup_pages(pgsize,oneatom,requests[i]->dnum); lists[i]->index = i; if (requests[i]->pair) { Pair *pair = (Pair *) requests[i]->requestor; pair->init_list(requests[i]->id,lists[i]); } else if (requests[i]->fix) { Fix *fix = (Fix *) requests[i]->requestor; fix->init_list(requests[i]->id,lists[i]); } else if (requests[i]->compute) { Compute *compute = (Compute *) requests[i]->requestor; compute->init_list(requests[i]->id,lists[i]); } } // detect lists that are connected to other lists // if-then-else sequence and processed flag is important // since don't want to re-process skip or copy lists further down // skip list needs to have granhistory or respa info added // copy: point this list at request->otherlist, could be a skip list // skip: point this list at request->otherlist, copy skip info from request // also set granular and respa info if applicable // half_from_full: point this list at preceeding full list // granhistory: set preceeding list's listgranhistory to this list // also set preceeding list's ptr to FixShearHistory // respaouter: point this list at preceeding 1/2 inner/middle lists // pair and half: if there is a full non-occasional non-skip list // change this list to half_from_full and point at the full list // parent could be copy list or pair or fix // fix/compute requests: // whether request is occasional or not doesn't matter // if request = half and non-skip pair half/respaouter exists, // become copy of that list if cudable flag matches // if request = full and non-skip pair full exists, // become copy of that list if cudable flag matches // if request = half and non-skip pair full exists, // become half_from_full of that list if cudable flag matches // if no matches, do nothing, fix/compute list will be built directly // ok if parent is copy list int processed; for (i = 0; i < nlist; i++) { processed = 0; if (requests[i]->copy) { lists[i]->listcopy = lists[requests[i]->otherlist]; processed = 1; } else if (requests[i]->skip) { lists[i]->listskip = lists[requests[i]->otherlist]; lists[i]->copy_skip_info(requests[i]->iskip,requests[i]->ijskip); processed = 1; } else if (requests[i]->half_from_full) { lists[i]->listfull = lists[i-1]; processed = 1; } if (requests[i]->granhistory) { lists[i-1]->listgranhistory = lists[i]; for (int ifix = 0; ifix < modify->nfix; ifix++) if (strcmp(modify->fix[ifix]->style,"SHEAR_HISTORY") == 0) lists[i-1]->fix_history = (FixShearHistory *) modify->fix[ifix]; processed = 1; } else if (requests[i]->respaouter) { if (requests[i-1]->respainner) { lists[i]->respamiddle = 0; lists[i]->listinner = lists[i-1]; } else { lists[i]->respamiddle = 1; lists[i]->listmiddle = lists[i-1]; lists[i]->listinner = lists[i-2]; } processed = 1; } if (processed) continue; if (requests[i]->pair && requests[i]->half) { for (j = 0; j < nlist; j++) if (requests[j]->full && requests[j]->occasional == 0 && requests[j]->skip == 0) break; if (j < nlist) { requests[i]->half = 0; requests[i]->half_from_full = 1; lists[i]->listfull = lists[j]; } } else if (requests[i]->fix || requests[i]->compute) { for (j = 0; j < nlist; j++) { if (requests[i]->half && requests[j]->pair && requests[j]->skip == 0 && requests[j]->half) break; if (requests[i]->full && requests[j]->pair && requests[j]->skip == 0 && requests[j]->full) break; if (requests[i]->gran && requests[j]->pair && requests[j]->skip == 0 && requests[j]->gran) break; if (requests[i]->half && requests[j]->pair && requests[j]->skip == 0 && requests[j]->respaouter) break; } if (j < nlist && requests[j]->cudable != requests[i]->cudable) j = nlist; if (j < nlist) { requests[i]->copy = 1; requests[i]->otherlist = j; lists[i]->listcopy = lists[j]; } else { for (j = 0; j < nlist; j++) { if (requests[i]->half && requests[j]->pair && requests[j]->skip == 0 && requests[j]->full) break; } if (j < nlist && requests[j]->cudable != requests[i]->cudable) j = nlist; if (j < nlist) { requests[i]->half = 0; requests[i]->half_from_full = 1; lists[i]->listfull = lists[j]; } } } } // set ptrs to pair_build and stencil_create functions for each list // ptrs set to NULL if not set explicitly // also set cudable to 0 if any neigh list request is not cudable for (i = 0; i < nlist; i++) { choose_build(i,requests[i]); if (style != NSQ) choose_stencil(i,requests[i]); else stencil_create[i] = NULL; if (!requests[i]->cudable) cudable = 0; } // set each list's build/grow/stencil/ghost flags based on neigh request // buildflag = 1 if its pair_build() invoked every reneighbor // growflag = 1 if it stores atom-based arrays and pages // stencilflag = 1 if it stores stencil arrays // ghostflag = 1 if it stores neighbors of ghosts // anyghostlist = 1 if any non-occasional list stores neighbors of ghosts anyghostlist = 0; for (i = 0; i < nlist; i++) { lists[i]->buildflag = 1; if (pair_build[i] == NULL) lists[i]->buildflag = 0; if (requests[i]->occasional) lists[i]->buildflag = 0; lists[i]->growflag = 1; if (requests[i]->copy) lists[i]->growflag = 0; lists[i]->stencilflag = 1; if (style == NSQ) lists[i]->stencilflag = 0; if (stencil_create[i] == NULL) lists[i]->stencilflag = 0; lists[i]->ghostflag = 0; if (requests[i]->ghost) lists[i]->ghostflag = 1; if (requests[i]->ghost && !requests[i]->occasional) anyghostlist = 1; } #ifdef NEIGH_LIST_DEBUG for (i = 0; i < nlist; i++) lists[i]->print_attributes(); #endif // allocate atom arrays for neighbor lists that store them maxatom = atom->nmax; for (i = 0; i < nlist; i++) if (lists[i]->growflag) lists[i]->grow(maxatom); // setup 3 vectors of pairwise neighbor lists // blist = lists whose pair_build() is invoked every reneighbor // glist = lists who store atom arrays which are used every reneighbor // slist = lists who store stencil arrays which are used every reneighbor // blist and glist vectors are used by neighbor::build() // slist vector is used by neighbor::setup_bins() nblist = nglist = nslist = 0; delete [] blist; delete [] glist; delete [] slist; blist = new int[nlist]; glist = new int[nlist]; slist = new int[nlist]; for (i = 0; i < nlist; i++) { if (lists[i]->buildflag) blist[nblist++] = i; if (lists[i]->growflag && requests[i]->occasional == 0) glist[nglist++] = i; if (lists[i]->stencilflag && requests[i]->occasional == 0) slist[nslist++] = i; } #ifdef NEIGH_LIST_DEBUG print_lists_of_lists(); #endif // reorder build vector if necessary // relevant for lists that copy/skip/half-full from parent // the derived list must appear in blist after the parent list // no occasional lists are in build vector // swap two lists within blist when dependency is mis-ordered // done when entire pass thru blist results in no swaps int done = 0; while (!done) { done = 1; for (i = 0; i < nblist; i++) { NeighList *ptr = NULL; if (lists[blist[i]]->listfull) ptr = lists[blist[i]]->listfull; if (lists[blist[i]]->listcopy) ptr = lists[blist[i]]->listcopy; if (lists[blist[i]]->listskip) ptr = lists[blist[i]]->listskip; if (ptr == NULL) continue; for (m = 0; m < nlist; m++) if (ptr == lists[m]) break; for (j = 0; j < nblist; j++) if (m == blist[j]) break; if (j < i) continue; int tmp = blist[i]; blist[i] = blist[j]; blist[j] = tmp; done = 0; break; } } #ifdef NEIGH_LIST_DEBUG print_lists_of_lists(); #endif } // delete old requests // copy current requests and style to old for next run for (i = 0; i < old_nrequest; i++) delete old_requests[i]; memory->sfree(old_requests); old_nrequest = nrequest; old_requests = requests; nrequest = maxrequest = 0; requests = NULL; old_style = style; old_triclinic = triclinic; // ------------------------------------------------------------------ // topology lists // 1st time allocation of topology lists if (atom->molecular && atom->nbonds && maxbond == 0) { if (nprocs == 1) maxbond = atom->nbonds; else maxbond = static_cast (LB_FACTOR * atom->nbonds / nprocs); memory->create(bondlist,maxbond,3,"neigh:bondlist"); } if (atom->molecular && atom->nangles && maxangle == 0) { if (nprocs == 1) maxangle = atom->nangles; else maxangle = static_cast (LB_FACTOR * atom->nangles / nprocs); memory->create(anglelist,maxangle,4,"neigh:anglelist"); } if (atom->molecular && atom->ndihedrals && maxdihedral == 0) { if (nprocs == 1) maxdihedral = atom->ndihedrals; else maxdihedral = static_cast (LB_FACTOR * atom->ndihedrals / nprocs); memory->create(dihedrallist,maxdihedral,5,"neigh:dihedrallist"); } if (atom->molecular && atom->nimpropers && maximproper == 0) { if (nprocs == 1) maximproper = atom->nimpropers; else maximproper = static_cast (LB_FACTOR * atom->nimpropers / nprocs); memory->create(improperlist,maximproper,5,"neigh:improperlist"); } // set flags that determine which topology neighboring routines to use // SHAKE sets bonds and angles negative // bond_quartic sets bonds to 0 // delete_bonds sets all interactions negative int bond_off = 0; int angle_off = 0; for (i = 0; i < modify->nfix; i++) if (strcmp(modify->fix[i]->style,"shake") == 0) bond_off = angle_off = 1; if (force->bond && force->bond_match("quartic")) bond_off = 1; if (atom->avec->bonds_allow) { for (i = 0; i < atom->nlocal; i++) { if (bond_off) break; for (m = 0; m < atom->num_bond[i]; m++) if (atom->bond_type[i][m] <= 0) bond_off = 1; } } if (atom->avec->angles_allow) { for (i = 0; i < atom->nlocal; i++) { if (angle_off) break; for (m = 0; m < atom->num_angle[i]; m++) if (atom->angle_type[i][m] <= 0) angle_off = 1; } } int dihedral_off = 0; if (atom->avec->dihedrals_allow) { for (i = 0; i < atom->nlocal; i++) { if (dihedral_off) break; for (m = 0; m < atom->num_dihedral[i]; m++) if (atom->dihedral_type[i][m] <= 0) dihedral_off = 1; } } int improper_off = 0; if (atom->avec->impropers_allow) { for (i = 0; i < atom->nlocal; i++) { if (improper_off) break; for (m = 0; m < atom->num_improper[i]; m++) if (atom->improper_type[i][m] <= 0) improper_off = 1; } } // set ptrs to topology build functions if (bond_off) bond_build = &Neighbor::bond_partial; else bond_build = &Neighbor::bond_all; if (angle_off) angle_build = &Neighbor::angle_partial; else angle_build = &Neighbor::angle_all; if (dihedral_off) dihedral_build = &Neighbor::dihedral_partial; else dihedral_build = &Neighbor::dihedral_all; if (improper_off) improper_build = &Neighbor::improper_partial; else improper_build = &Neighbor::improper_all; // set topology neighbor list counts to 0 // in case all are turned off but potential is still defined nbondlist = nanglelist = ndihedrallist = nimproperlist = 0; } /* ---------------------------------------------------------------------- */ int Neighbor::request(void *requestor) { if (nrequest == maxrequest) { maxrequest += RQDELTA; requests = (NeighRequest **) memory->srealloc(requests,maxrequest*sizeof(NeighRequest *), "neighbor:requests"); } requests[nrequest] = new NeighRequest(lmp); requests[nrequest]->requestor = requestor; nrequest++; return nrequest-1; } /* ---------------------------------------------------------------------- determine which pair_build function each neigh list needs based on settings of neigh request copy -> copy_from function skip -> granular function if gran with granhistory, respa function if respaouter, skip_from function for everything else half_from_full, half, full, gran, respaouter -> choose by newton and rq->newton and tri settings style NSQ options = newton off, newton on style BIN options = newton off, newton on and not tri, newton on and tri stlye MULTI options = same options as BIN if none of these, ptr = NULL since pair_build is not invoked for this list use "else if" b/c skip,copy can be set in addition to half,full,etc ------------------------------------------------------------------------- */ void Neighbor::choose_build(int index, NeighRequest *rq) { PairPtr pb = NULL; if (rq->omp == 0) { if (rq->copy) pb = &Neighbor::copy_from; else if (rq->skip) { if (rq->gran && lists[index]->listgranhistory) pb = &Neighbor::skip_from_granular; else if (rq->respaouter) pb = &Neighbor::skip_from_respa; else pb = &Neighbor::skip_from; } else if (rq->half_from_full) { if (newton_pair == 0) pb = &Neighbor::half_from_full_no_newton; else if (newton_pair == 1) pb = &Neighbor::half_from_full_newton; } else if (rq->half) { if (style == NSQ) { if (rq->newton == 0) { if (newton_pair == 0) { if (rq->ghost == 0) pb = &Neighbor::half_nsq_no_newton; else if (includegroup) error->all(FLERR,"Neighbor include group not allowed " "with ghost neighbors"); else pb = &Neighbor::half_nsq_no_newton_ghost; } else if (newton_pair == 1) pb = &Neighbor::half_nsq_newton; } else if (rq->newton == 1) { pb = &Neighbor::half_nsq_newton; } else if (rq->newton == 2) { if (rq->ghost == 0) pb = &Neighbor::half_nsq_no_newton; else if (includegroup) error->all(FLERR,"Neighbor include group not allowed " "with ghost neighbors"); else pb = &Neighbor::half_nsq_no_newton_ghost; } } else if (style == BIN) { if (rq->newton == 0) { if (newton_pair == 0) { if (rq->ghost == 0) pb = &Neighbor::half_bin_no_newton; else if (includegroup) error->all(FLERR,"Neighbor include group not allowed " "with ghost neighbors"); else pb = &Neighbor::half_bin_no_newton_ghost; } else if (triclinic == 0) { pb = &Neighbor::half_bin_newton; } else if (triclinic == 1) pb = &Neighbor::half_bin_newton_tri; } else if (rq->newton == 1) { if (triclinic == 0) pb = &Neighbor::half_bin_newton; else if (triclinic == 1) pb = &Neighbor::half_bin_newton_tri; } else if (rq->newton == 2) { if (rq->ghost == 0) pb = &Neighbor::half_bin_no_newton; else if (includegroup) error->all(FLERR,"Neighbor include group not allowed " "with ghost neighbors"); else pb = &Neighbor::half_bin_no_newton_ghost; } } else if (style == MULTI) { if (rq->ghost == 1) error->all(FLERR, "Neighbor multi not yet enabled for ghost neighbors"); if (rq->newton == 0) { if (newton_pair == 0) pb = &Neighbor::half_multi_no_newton; else if (triclinic == 0) pb = &Neighbor::half_multi_newton; else if (triclinic == 1) pb = &Neighbor::half_multi_newton_tri; } else if (rq->newton == 1) { if (triclinic == 0) pb = &Neighbor::half_multi_newton; else if (triclinic == 1) pb = &Neighbor::half_multi_newton_tri; } else if (rq->newton == 2) pb = &Neighbor::half_multi_no_newton; } } else if (rq->full) { if (style == NSQ) { if (rq->ghost == 0) pb = &Neighbor::full_nsq; else if (includegroup) error->all(FLERR, "Neighbor include group not allowed with ghost neighbors"); else pb = &Neighbor::full_nsq_ghost; } else if (style == BIN) { if (rq->ghost == 0) pb = &Neighbor::full_bin; else if (includegroup) error->all(FLERR, "Neighbor include group not allowed with ghost neighbors"); else pb = &Neighbor::full_bin_ghost; } else if (style == MULTI) { if (rq->ghost == 1) error->all(FLERR, "Neighbor multi not yet enabled for ghost neighbors"); pb = &Neighbor::full_multi; } } else if (rq->gran) { if (style == NSQ) { if (newton_pair == 0) pb = &Neighbor::granular_nsq_no_newton; else if (newton_pair == 1) pb = &Neighbor::granular_nsq_newton; } else if (style == BIN) { if (newton_pair == 0) pb = &Neighbor::granular_bin_no_newton; else if (triclinic == 0) pb = &Neighbor::granular_bin_newton; else if (triclinic == 1) pb = &Neighbor::granular_bin_newton_tri; } else if (style == MULTI) error->all(FLERR,"Neighbor multi not yet enabled for granular"); } else if (rq->respaouter) { if (style == NSQ) { if (newton_pair == 0) pb = &Neighbor::respa_nsq_no_newton; else if (newton_pair == 1) pb = &Neighbor::respa_nsq_newton; } else if (style == BIN) { if (newton_pair == 0) pb = &Neighbor::respa_bin_no_newton; else if (triclinic == 0) pb = &Neighbor::respa_bin_newton; else if (triclinic == 1) pb = &Neighbor::respa_bin_newton_tri; } else if (style == MULTI) error->all(FLERR,"Neighbor multi not yet enabled for rRESPA"); } // OMP versions of build methods } else { if (rq->copy) pb = &Neighbor::copy_from; else if (rq->skip) { if (rq->gran && lists[index]->listgranhistory) pb = &Neighbor::skip_from_granular; else if (rq->respaouter) pb = &Neighbor::skip_from_respa; else pb = &Neighbor::skip_from; } else if (rq->half_from_full) { if (newton_pair == 0) pb = &Neighbor::half_from_full_no_newton_omp; else if (newton_pair == 1) pb = &Neighbor::half_from_full_newton_omp; } else if (rq->half) { if (style == NSQ) { if (rq->newton == 0) { if (newton_pair == 0) { if (rq->ghost == 0) pb = &Neighbor::half_nsq_no_newton_omp; else if (includegroup) error->all(FLERR,"Neighbor include group not allowed " "with ghost neighbors"); else pb = &Neighbor::half_nsq_no_newton_ghost_omp; } else if (newton_pair == 1) pb = &Neighbor::half_nsq_newton_omp; } else if (rq->newton == 1) { pb = &Neighbor::half_nsq_newton_omp; } else if (rq->newton == 2) { if (rq->ghost == 0) pb = &Neighbor::half_nsq_no_newton_omp; else if (includegroup) error->all(FLERR,"Neighbor include group not allowed " "with ghost neighbors"); else pb = &Neighbor::half_nsq_no_newton_ghost_omp; } } else if (style == BIN) { if (rq->newton == 0) { if (newton_pair == 0) { if (rq->ghost == 0) pb = &Neighbor::half_bin_no_newton_omp; else if (includegroup) error->all(FLERR,"Neighbor include group not allowed " "with ghost neighbors"); else pb = &Neighbor::half_bin_no_newton_ghost_omp; } else if (triclinic == 0) { pb = &Neighbor::half_bin_newton_omp; } else if (triclinic == 1) pb = &Neighbor::half_bin_newton_tri_omp; } else if (rq->newton == 1) { if (triclinic == 0) pb = &Neighbor::half_bin_newton_omp; else if (triclinic == 1) pb = &Neighbor::half_bin_newton_tri_omp; } else if (rq->newton == 2) { if (rq->ghost == 0) pb = &Neighbor::half_bin_no_newton_omp; else if (includegroup) error->all(FLERR,"Neighbor include group not allowed " "with ghost neighbors"); else pb = &Neighbor::half_bin_no_newton_ghost_omp; } } else if (style == MULTI) { if (rq->ghost == 1) error->all(FLERR, "Neighbor multi not yet enabled for ghost neighbors"); if (rq->newton == 0) { if (newton_pair == 0) pb = &Neighbor::half_multi_no_newton_omp; else if (triclinic == 0) pb = &Neighbor::half_multi_newton_omp; else if (triclinic == 1) pb = &Neighbor::half_multi_newton_tri_omp; } else if (rq->newton == 1) { if (triclinic == 0) pb = &Neighbor::half_multi_newton_omp; else if (triclinic == 1) pb = &Neighbor::half_multi_newton_tri_omp; } else if (rq->newton == 2) pb = &Neighbor::half_multi_no_newton_omp; } } else if (rq->full) { if (style == NSQ) { if (rq->ghost == 0) pb = &Neighbor::full_nsq_omp; else if (includegroup) error->all(FLERR, "Neighbor include group not allowed with ghost neighbors"); else pb = &Neighbor::full_nsq_ghost_omp; } else if (style == BIN) { if (rq->ghost == 0) pb = &Neighbor::full_bin_omp; else if (includegroup) error->all(FLERR, "Neighbor include group not allowed with ghost neighbors"); else pb = &Neighbor::full_bin_ghost_omp; } else if (style == MULTI) { if (rq->ghost == 1) error->all(FLERR, "Neighbor multi not yet enabled for ghost neighbors"); pb = &Neighbor::full_multi_omp; } } else if (rq->gran) { if (style == NSQ) { if (newton_pair == 0) pb = &Neighbor::granular_nsq_no_newton_omp; else if (newton_pair == 1) pb = &Neighbor::granular_nsq_newton_omp; } else if (style == BIN) { if (newton_pair == 0) pb = &Neighbor::granular_bin_no_newton_omp; else if (triclinic == 0) pb = &Neighbor::granular_bin_newton_omp; else if (triclinic == 1) pb = &Neighbor::granular_bin_newton_tri_omp; } else if (style == MULTI) error->all(FLERR,"Neighbor multi not yet enabled for granular"); } else if (rq->respaouter) { if (style == NSQ) { if (newton_pair == 0) pb = &Neighbor::respa_nsq_no_newton_omp; else if (newton_pair == 1) pb = &Neighbor::respa_nsq_newton_omp; } else if (style == BIN) { if (newton_pair == 0) pb = &Neighbor::respa_bin_no_newton_omp; else if (triclinic == 0) pb = &Neighbor::respa_bin_newton_omp; else if (triclinic == 1) pb = &Neighbor::respa_bin_newton_tri_omp; } else if (style == MULTI) error->all(FLERR,"Neighbor multi not yet enabled for rRESPA"); } } pair_build[index] = pb; } /* ---------------------------------------------------------------------- determine which stencil_create function each neigh list needs based on settings of neigh request, only called if style != NSQ skip or copy or half_from_full -> no stencil half, gran, respaouter, full -> choose by newton and tri and dimension if none of these, ptr = NULL since this list needs no stencils use "else if" b/c skip,copy can be set in addition to half,full,etc ------------------------------------------------------------------------- */ void Neighbor::choose_stencil(int index, NeighRequest *rq) { StencilPtr sc = NULL; if (rq->skip || rq->copy || rq->half_from_full) sc = NULL; else if (rq->half || rq->gran || rq->respaouter) { if (style == BIN) { if (rq->newton == 0) { if (newton_pair == 0) { if (dimension == 2) { if (rq->ghost) sc = &Neighbor::stencil_half_ghost_bin_2d_no_newton; else sc = &Neighbor::stencil_half_bin_2d_no_newton; } else if (dimension == 3) { if (rq->ghost) sc = &Neighbor::stencil_half_ghost_bin_3d_no_newton; else sc = &Neighbor::stencil_half_bin_3d_no_newton; } } else if (triclinic == 0) { if (dimension == 2) sc = &Neighbor::stencil_half_bin_2d_newton; else if (dimension == 3) sc = &Neighbor::stencil_half_bin_3d_newton; } else if (triclinic == 1) { if (dimension == 2) sc = &Neighbor::stencil_half_bin_2d_newton_tri; else if (dimension == 3) sc = &Neighbor::stencil_half_bin_3d_newton_tri; } } else if (rq->newton == 1) { if (triclinic == 0) { if (dimension == 2) sc = &Neighbor::stencil_half_bin_2d_newton; else if (dimension == 3) sc = &Neighbor::stencil_half_bin_3d_newton; } else if (triclinic == 1) { if (dimension == 2) sc = &Neighbor::stencil_half_bin_2d_newton_tri; else if (dimension == 3) sc = &Neighbor::stencil_half_bin_3d_newton_tri; } } else if (rq->newton == 2) { if (dimension == 2) if (rq->ghost) sc = &Neighbor::stencil_half_ghost_bin_2d_no_newton; else sc = &Neighbor::stencil_half_bin_2d_no_newton; else if (dimension == 3) { if (rq->ghost) sc = &Neighbor::stencil_half_ghost_bin_3d_no_newton; else sc = &Neighbor::stencil_half_bin_3d_no_newton; } } } else if (style == MULTI) { if (rq->newton == 0) { if (newton_pair == 0) { if (dimension == 2) sc = &Neighbor::stencil_half_multi_2d_no_newton; else if (dimension == 3) sc = &Neighbor::stencil_half_multi_3d_no_newton; } else if (triclinic == 0) { if (dimension == 2) sc = &Neighbor::stencil_half_multi_2d_newton; else if (dimension == 3) sc = &Neighbor::stencil_half_multi_3d_newton; } else if (triclinic == 1) { if (dimension == 2) sc = &Neighbor::stencil_half_multi_2d_newton_tri; else if (dimension == 3) sc = &Neighbor::stencil_half_multi_3d_newton_tri; } } else if (rq->newton == 1) { if (triclinic == 0) { if (dimension == 2) sc = &Neighbor::stencil_half_multi_2d_newton; else if (dimension == 3) sc = &Neighbor::stencil_half_multi_3d_newton; } else if (triclinic == 1) { if (dimension == 2) sc = &Neighbor::stencil_half_multi_2d_newton_tri; else if (dimension == 3) sc = &Neighbor::stencil_half_multi_3d_newton_tri; } } else if (rq->newton == 2) { if (dimension == 2) sc = &Neighbor::stencil_half_multi_2d_no_newton; else if (dimension == 3) sc = &Neighbor::stencil_half_multi_3d_no_newton; } } } else if (rq->full) { if (style == BIN) { if (dimension == 2) { if (rq->ghost) sc = &Neighbor::stencil_full_ghost_bin_2d; else sc = &Neighbor::stencil_full_bin_2d; } else if (dimension == 3) { if (rq->ghost) sc = &Neighbor::stencil_full_ghost_bin_3d; else sc = &Neighbor::stencil_full_bin_3d; } } else if (style == MULTI) { if (dimension == 2) sc = &Neighbor::stencil_full_multi_2d; else if (dimension == 3) sc = &Neighbor::stencil_full_multi_3d; } } stencil_create[index] = sc; } /* ---------------------------------------------------------------------- */ void Neighbor::print_lists_of_lists() { if (comm->me == 0) { printf("Build lists = %d: ",nblist); for (int i = 0; i < nblist; i++) printf("%d ",blist[i]); printf("\n"); printf("Grow lists = %d: ",nglist); for (int i = 0; i < nglist; i++) printf("%d ",glist[i]); printf("\n"); printf("Stencil lists = %d: ",nslist); for (int i = 0; i < nslist; i++) printf("%d ",slist[i]); printf("\n"); } } /* ---------------------------------------------------------------------- */ int Neighbor::decide() { if (must_check) { int n = update->ntimestep; if (restart_check && n == output->next_restart) return 1; for (int i = 0; i < fix_check; i++) if (n == modify->fix[fixchecklist[i]]->next_reneighbor) return 1; } ago++; if (ago >= delay && ago % every == 0) { if (build_once) return 0; if (dist_check == 0) return 1; return check_distance(); } else return 0; } /* ---------------------------------------------------------------------- if any atom moved trigger distance (half of neighbor skin) return 1 shrink trigger distance if box size has changed conservative shrink procedure: compute distance each of 8 corners of box has moved since last reneighbor reduce skin distance by sum of 2 largest of the 8 values new trigger = 1/2 of reduced skin distance for orthogonal box, only need 2 lo/hi corners for triclinic, need all 8 corners since deformations can displace all 8 ------------------------------------------------------------------------- */ int Neighbor::check_distance() { double delx,dely,delz,rsq; double delta,deltasq,delta1,delta2; if (boxcheck) { if (triclinic == 0) { delx = bboxlo[0] - boxlo_hold[0]; dely = bboxlo[1] - boxlo_hold[1]; delz = bboxlo[2] - boxlo_hold[2]; delta1 = sqrt(delx*delx + dely*dely + delz*delz); delx = bboxhi[0] - boxhi_hold[0]; dely = bboxhi[1] - boxhi_hold[1]; delz = bboxhi[2] - boxhi_hold[2]; delta2 = sqrt(delx*delx + dely*dely + delz*delz); delta = 0.5 * (skin - (delta1+delta2)); deltasq = delta*delta; } else { domain->box_corners(); delta1 = delta2 = 0.0; for (int i = 0; i < 8; i++) { delx = corners[i][0] - corners_hold[i][0]; dely = corners[i][1] - corners_hold[i][1]; delz = corners[i][2] - corners_hold[i][2]; delta = sqrt(delx*delx + dely*dely + delz*delz); if (delta > delta1) delta1 = delta; else if (delta > delta2) delta2 = delta; } delta = 0.5 * (skin - (delta1+delta2)); deltasq = delta*delta; } } else deltasq = triggersq; double **x = atom->x; int nlocal = atom->nlocal; if (includegroup) nlocal = atom->nfirst; int flag = 0; for (int i = 0; i < nlocal; i++) { delx = x[i][0] - xhold[i][0]; dely = x[i][1] - xhold[i][1]; delz = x[i][2] - xhold[i][2]; rsq = delx*delx + dely*dely + delz*delz; if (rsq > deltasq) flag = 1; } int flagall; MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_MAX,world); if (flagall && ago == MAX(every,delay)) ndanger++; return flagall; } /* ---------------------------------------------------------------------- build all perpetual neighbor lists every few timesteps pairwise & topology lists are created as needed topology lists only built if topoflag = 1 ------------------------------------------------------------------------- */ void Neighbor::build(int topoflag) { int i; ago = 0; ncalls++; lastcall = update->ntimestep; // store current atom positions and box size if needed if (dist_check) { double **x = atom->x; int nlocal = atom->nlocal; if (includegroup) nlocal = atom->nfirst; if (nlocal > maxhold) { maxhold = atom->nmax; memory->destroy(xhold); memory->create(xhold,maxhold,3,"neigh:xhold"); } for (i = 0; i < nlocal; i++) { xhold[i][0] = x[i][0]; xhold[i][1] = x[i][1]; xhold[i][2] = x[i][2]; } if (boxcheck) { if (triclinic == 0) { boxlo_hold[0] = bboxlo[0]; boxlo_hold[1] = bboxlo[1]; boxlo_hold[2] = bboxlo[2]; boxhi_hold[0] = bboxhi[0]; boxhi_hold[1] = bboxhi[1]; boxhi_hold[2] = bboxhi[2]; } else { domain->box_corners(); corners = domain->corners; for (i = 0; i < 8; i++) { corners_hold[i][0] = corners[i][0]; corners_hold[i][1] = corners[i][1]; corners_hold[i][2] = corners[i][2]; } } } } // if any lists store neighbors of ghosts: // invoke grow() if nlocal+nghost exceeds previous list size // else only invoke grow() if nlocal exceeds previous list size // only done for lists with growflag set and which are perpetual if (anyghostlist && atom->nlocal+atom->nghost > maxatom) { maxatom = atom->nmax; for (i = 0; i < nglist; i++) lists[glist[i]]->grow(maxatom); } else if (atom->nlocal > maxatom) { maxatom = atom->nmax; for (i = 0; i < nglist; i++) lists[glist[i]]->grow(maxatom); } // extend atom bin list if necessary if (style != NSQ && atom->nmax > maxbin) { maxbin = atom->nmax; memory->destroy(bins); memory->create(bins,maxbin,"bins"); } // check that neighbor list with special bond flags will not overflow if (atom->nlocal+atom->nghost > NEIGHMASK) error->one(FLERR,"Too many local+ghost atoms for neighbor list"); // invoke building of pair and molecular neighbor lists // only for pairwise lists with buildflag set for (i = 0; i < nblist; i++) (this->*pair_build[blist[i]])(lists[blist[i]]); if (atom->molecular && topoflag) build_topology(); } /* ---------------------------------------------------------------------- build all topology neighbor lists every few timesteps normally built with pair lists, but USER-CUDA separates them ------------------------------------------------------------------------- */ void Neighbor::build_topology() { if (force->bond) (this->*bond_build)(); if (force->angle) (this->*angle_build)(); if (force->dihedral) (this->*dihedral_build)(); if (force->improper) (this->*improper_build)(); } /* ---------------------------------------------------------------------- build a single occasional pairwise neighbor list indexed by I called by other classes ------------------------------------------------------------------------- */ void Neighbor::build_one(int i) { // update stencils and grow atom arrays and bins as needed // only for relevant settings of stencilflag and growflag // grow atom array for this list to current size of perpetual lists if (lists[i]->stencilflag) { lists[i]->stencil_allocate(smax,style); (this->*stencil_create[i])(lists[i],sx,sy,sz); } if (lists[i]->growflag) lists[i]->grow(maxatom); if (style != NSQ && atom->nmax > maxbin) { maxbin = atom->nmax; memory->destroy(bins); memory->create(bins,maxbin,"bins"); } // check that neighbor list with special bond flags will not overflow if (atom->nlocal+atom->nghost > NEIGHMASK) error->one(FLERR,"Too many local+ghost atoms for neighbor list"); // when occasional list built, LAMMPS can crash if atoms have moved too far // why is this?, give warning if this is the case // no easy workaround b/c all neighbor lists really need to be rebuilt // solution is for input script to check more often for rebuild // only check_distance if running a simulation, not between simulations int flag = 0; if (dist_check && update->whichflag) flag = check_distance(); if (flag && me == 0) error->warning(FLERR,"Building an occasional neighobr list when " "atoms may have moved too far"); (this->*pair_build[i])(lists[i]); } /* ---------------------------------------------------------------------- setup neighbor binning parameters bin numbering in each dimension is global: 0 = 0.0 to binsize, 1 = binsize to 2*binsize, etc nbin-1,nbin,etc = bbox-binsize to bbox, bbox to bbox+binsize, etc -1,-2,etc = -binsize to 0.0, -2*binsize to -binsize, etc code will work for any binsize since next(xyz) and stencil extend as far as necessary binsize = 1/2 of cutoff is roughly optimal for orthogonal boxes: a dim must be filled exactly by integer # of bins in periodic, procs on both sides of PBC must see same bin boundary in non-periodic, coord2bin() still assumes this by use of nbin xyz for triclinic boxes: tilted simulation box cannot contain integer # of bins stencil & neigh list built differently to account for this mbinlo = lowest global bin any of my ghost atoms could fall into mbinhi = highest global bin any of my ghost atoms could fall into mbin = number of bins I need in a dimension ------------------------------------------------------------------------- */ void Neighbor::setup_bins() { // bbox = size of bbox of entire domain // bsubbox lo/hi = bounding box of my subdomain extended by comm->cutghost // for triclinic: // bbox bounds all 8 corners of tilted box // subdomain is in lamda coords // include dimension-dependent extension via comm->cutghost // domain->bbox() converts lamda extent to box coords and computes bbox double bbox[3],bsubboxlo[3],bsubboxhi[3]; double *cutghost = comm->cutghost; if (triclinic == 0) { bsubboxlo[0] = domain->sublo[0] - cutghost[0]; bsubboxlo[1] = domain->sublo[1] - cutghost[1]; bsubboxlo[2] = domain->sublo[2] - cutghost[2]; bsubboxhi[0] = domain->subhi[0] + cutghost[0]; bsubboxhi[1] = domain->subhi[1] + cutghost[1]; bsubboxhi[2] = domain->subhi[2] + cutghost[2]; } else { double lo[3],hi[3]; lo[0] = domain->sublo_lamda[0] - cutghost[0]; lo[1] = domain->sublo_lamda[1] - cutghost[1]; lo[2] = domain->sublo_lamda[2] - cutghost[2]; hi[0] = domain->subhi_lamda[0] + cutghost[0]; hi[1] = domain->subhi_lamda[1] + cutghost[1]; hi[2] = domain->subhi_lamda[2] + cutghost[2]; domain->bbox(lo,hi,bsubboxlo,bsubboxhi); } bbox[0] = bboxhi[0] - bboxlo[0]; bbox[1] = bboxhi[1] - bboxlo[1]; bbox[2] = bboxhi[2] - bboxlo[2]; // optimal bin size is roughly 1/2 the cutoff // for BIN style, binsize = 1/2 of max neighbor cutoff // for MULTI style, binsize = 1/2 of min neighbor cutoff // special case of all cutoffs = 0.0, binsize = box size double binsize_optimal; if (binsizeflag) binsize_optimal = binsize_user; else if (style == BIN) binsize_optimal = 0.5*cutneighmax; else binsize_optimal = 0.5*cutneighmin; if (binsize_optimal == 0.0) binsize_optimal = bbox[0]; double binsizeinv = 1.0/binsize_optimal; // test for too many global bins in any dimension due to huge global domain if (bbox[0]*binsizeinv > MAXSMALLINT || bbox[1]*binsizeinv > MAXSMALLINT || bbox[2]*binsizeinv > MAXSMALLINT) error->all(FLERR,"Domain too large for neighbor bins"); // create actual bins // always have one bin even if cutoff > bbox // for 2d, nbinz = 1 nbinx = static_cast (bbox[0]*binsizeinv); nbiny = static_cast (bbox[1]*binsizeinv); if (dimension == 3) nbinz = static_cast (bbox[2]*binsizeinv); else nbinz = 1; if (nbinx == 0) nbinx = 1; if (nbiny == 0) nbiny = 1; if (nbinz == 0) nbinz = 1; // compute actual bin size for nbins to fit into box exactly // error if actual bin size << cutoff, since will create a zillion bins // this happens when nbin = 1 and box size << cutoff // typically due to non-periodic, flat system in a particular dim // in that extreme case, should use NSQ not BIN neighbor style binsizex = bbox[0]/nbinx; binsizey = bbox[1]/nbiny; binsizez = bbox[2]/nbinz; bininvx = 1.0 / binsizex; bininvy = 1.0 / binsizey; bininvz = 1.0 / binsizez; if (binsize_optimal*bininvx > CUT2BIN_RATIO || binsize_optimal*bininvy > CUT2BIN_RATIO || binsize_optimal*bininvz > CUT2BIN_RATIO) error->all(FLERR,"Cannot use neighbor bins - box size << cutoff"); // mbinlo/hi = lowest and highest global bins my ghost atoms could be in // coord = lowest and highest values of coords for my ghost atoms // static_cast(-1.5) = -1, so subract additional -1 // add in SMALL for round-off safety int mbinxhi,mbinyhi,mbinzhi; double coord; coord = bsubboxlo[0] - SMALL*bbox[0]; mbinxlo = static_cast ((coord-bboxlo[0])*bininvx); if (coord < bboxlo[0]) mbinxlo = mbinxlo - 1; coord = bsubboxhi[0] + SMALL*bbox[0]; mbinxhi = static_cast ((coord-bboxlo[0])*bininvx); coord = bsubboxlo[1] - SMALL*bbox[1]; mbinylo = static_cast ((coord-bboxlo[1])*bininvy); if (coord < bboxlo[1]) mbinylo = mbinylo - 1; coord = bsubboxhi[1] + SMALL*bbox[1]; mbinyhi = static_cast ((coord-bboxlo[1])*bininvy); if (dimension == 3) { coord = bsubboxlo[2] - SMALL*bbox[2]; mbinzlo = static_cast ((coord-bboxlo[2])*bininvz); if (coord < bboxlo[2]) mbinzlo = mbinzlo - 1; coord = bsubboxhi[2] + SMALL*bbox[2]; mbinzhi = static_cast ((coord-bboxlo[2])*bininvz); } // extend bins by 1 to insure stencil extent is included // if 2d, only 1 bin in z mbinxlo = mbinxlo - 1; mbinxhi = mbinxhi + 1; mbinx = mbinxhi - mbinxlo + 1; mbinylo = mbinylo - 1; mbinyhi = mbinyhi + 1; mbiny = mbinyhi - mbinylo + 1; if (dimension == 3) { mbinzlo = mbinzlo - 1; mbinzhi = mbinzhi + 1; } else mbinzlo = mbinzhi = 0; mbinz = mbinzhi - mbinzlo + 1; // memory for bin ptrs bigint bbin = ((bigint) mbinx) * ((bigint) mbiny) * ((bigint) mbinz); if (bbin > MAXSMALLINT) error->one(FLERR,"Too many neighbor bins"); mbins = bbin; if (mbins > maxhead) { maxhead = mbins; memory->destroy(binhead); memory->create(binhead,maxhead,"neigh:binhead"); } // create stencil of bins to search over in neighbor list construction // sx,sy,sz = max range of stencil in each dim // smax = max possible size of entire 3d stencil // stencil is empty if cutneighmax = 0.0 sx = static_cast (cutneighmax*bininvx); if (sx*binsizex < cutneighmax) sx++; sy = static_cast (cutneighmax*bininvy); if (sy*binsizey < cutneighmax) sy++; sz = static_cast (cutneighmax*bininvz); if (sz*binsizez < cutneighmax) sz++; if (dimension == 2) sz = 0; smax = (2*sx+1) * (2*sy+1) * (2*sz+1); // create stencils for pairwise neighbor lists // only done for lists with stencilflag and buildflag set for (int i = 0; i < nslist; i++) { lists[slist[i]]->stencil_allocate(smax,style); (this->*stencil_create[slist[i]])(lists[slist[i]],sx,sy,sz); } } /* ---------------------------------------------------------------------- compute closest distance between central bin (0,0,0) and bin (i,j,k) ------------------------------------------------------------------------- */ double Neighbor::bin_distance(int i, int j, int k) { double delx,dely,delz; if (i > 0) delx = (i-1)*binsizex; else if (i == 0) delx = 0.0; else delx = (i+1)*binsizex; if (j > 0) dely = (j-1)*binsizey; else if (j == 0) dely = 0.0; else dely = (j+1)*binsizey; if (k > 0) delz = (k-1)*binsizez; else if (k == 0) delz = 0.0; else delz = (k+1)*binsizez; return (delx*delx + dely*dely + delz*delz); } /* ---------------------------------------------------------------------- set neighbor style and skin distance ------------------------------------------------------------------------- */ void Neighbor::set(int narg, char **arg) { if (narg != 2) error->all(FLERR,"Illegal neighbor command"); skin = force->numeric(FLERR,arg[0]); if (skin < 0.0) error->all(FLERR,"Illegal neighbor command"); if (strcmp(arg[1],"nsq") == 0) style = NSQ; else if (strcmp(arg[1],"bin") == 0) style = BIN; else if (strcmp(arg[1],"multi") == 0) style = MULTI; else error->all(FLERR,"Illegal neighbor command"); if (style == MULTI && lmp->citeme) lmp->citeme->add(cite_neigh_multi); } /* ---------------------------------------------------------------------- modify parameters of the pair-wise neighbor build ------------------------------------------------------------------------- */ void Neighbor::modify_params(int narg, char **arg) { int iarg = 0; while (iarg < narg) { if (strcmp(arg[iarg],"every") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command"); every = force->inumeric(FLERR,arg[iarg+1]); if (every <= 0) error->all(FLERR,"Illegal neigh_modify command"); iarg += 2; } else if (strcmp(arg[iarg],"delay") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command"); delay = force->inumeric(FLERR,arg[iarg+1]); if (delay < 0) error->all(FLERR,"Illegal neigh_modify command"); iarg += 2; } else if (strcmp(arg[iarg],"check") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command"); if (strcmp(arg[iarg+1],"yes") == 0) dist_check = 1; else if (strcmp(arg[iarg+1],"no") == 0) dist_check = 0; else error->all(FLERR,"Illegal neigh_modify command"); iarg += 2; } else if (strcmp(arg[iarg],"once") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command"); if (strcmp(arg[iarg+1],"yes") == 0) build_once = 1; else if (strcmp(arg[iarg+1],"no") == 0) build_once = 0; else error->all(FLERR,"Illegal neigh_modify command"); iarg += 2; } else if (strcmp(arg[iarg],"page") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command"); old_pgsize = pgsize; pgsize = force->inumeric(FLERR,arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"one") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command"); old_oneatom = oneatom; oneatom = force->inumeric(FLERR,arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"binsize") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command"); binsize_user = force->numeric(FLERR,arg[iarg+1]); if (binsize_user <= 0.0) binsizeflag = 0; else binsizeflag = 1; iarg += 2; } else if (strcmp(arg[iarg],"cluster") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command"); if (strcmp(arg[iarg+1],"yes") == 0) cluster_check = 1; else if (strcmp(arg[iarg+1],"no") == 0) cluster_check = 0; else error->all(FLERR,"Illegal neigh_modify command"); iarg += 2; } else if (strcmp(arg[iarg],"include") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command"); includegroup = group->find(arg[iarg+1]); if (includegroup < 0) error->all(FLERR,"Invalid group ID in neigh_modify command"); if (includegroup && (atom->firstgroupname == NULL || strcmp(arg[iarg+1],atom->firstgroupname) != 0)) error->all(FLERR, "Neigh_modify include group != atom_modify first group"); iarg += 2; } else if (strcmp(arg[iarg],"exclude") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command"); if (strcmp(arg[iarg+1],"type") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal neigh_modify command"); if (nex_type == maxex_type) { maxex_type += EXDELTA; memory->grow(ex1_type,maxex_type,"neigh:ex1_type"); memory->grow(ex2_type,maxex_type,"neigh:ex2_type"); } ex1_type[nex_type] = force->inumeric(FLERR,arg[iarg+2]); ex2_type[nex_type] = force->inumeric(FLERR,arg[iarg+3]); nex_type++; iarg += 4; } else if (strcmp(arg[iarg+1],"group") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal neigh_modify command"); if (nex_group == maxex_group) { maxex_group += EXDELTA; memory->grow(ex1_group,maxex_group,"neigh:ex1_group"); memory->grow(ex2_group,maxex_group,"neigh:ex2_group"); } ex1_group[nex_group] = group->find(arg[iarg+2]); ex2_group[nex_group] = group->find(arg[iarg+3]); if (ex1_group[nex_group] == -1 || ex2_group[nex_group] == -1) error->all(FLERR,"Invalid group ID in neigh_modify command"); nex_group++; iarg += 4; } else if (strcmp(arg[iarg+1],"molecule") == 0) { if (iarg+3 > narg) error->all(FLERR,"Illegal neigh_modify command"); if (atom->molecule_flag == 0) error->all(FLERR,"Neigh_modify exclude molecule " "requires atom attribute molecule"); if (nex_mol == maxex_mol) { maxex_mol += EXDELTA; memory->grow(ex_mol_group,maxex_mol,"neigh:ex_mol_group"); } ex_mol_group[nex_mol] = group->find(arg[iarg+2]); if (ex_mol_group[nex_mol] == -1) error->all(FLERR,"Invalid group ID in neigh_modify command"); nex_mol++; iarg += 3; } else if (strcmp(arg[iarg+1],"none") == 0) { nex_type = nex_group = nex_mol = 0; iarg += 2; } else error->all(FLERR,"Illegal neigh_modify command"); } else error->all(FLERR,"Illegal neigh_modify command"); } } /* ---------------------------------------------------------------------- bin owned and ghost atoms ------------------------------------------------------------------------- */ void Neighbor::bin_atoms() { int i,ibin; for (i = 0; i < mbins; i++) binhead[i] = -1; // bin in reverse order so linked list will be in forward order // also puts ghost atoms at end of list, which is necessary double **x = atom->x; int *mask = atom->mask; int nlocal = atom->nlocal; int nall = nlocal + atom->nghost; if (includegroup) { int bitmask = group->bitmask[includegroup]; for (i = nall-1; i >= nlocal; i--) { if (mask[i] & bitmask) { ibin = coord2bin(x[i]); bins[i] = binhead[ibin]; binhead[ibin] = i; } } for (i = atom->nfirst-1; i >= 0; i--) { ibin = coord2bin(x[i]); bins[i] = binhead[ibin]; binhead[ibin] = i; } } else { for (i = nall-1; i >= 0; i--) { ibin = coord2bin(x[i]); bins[i] = binhead[ibin]; binhead[ibin] = i; } } } /* ---------------------------------------------------------------------- convert atom coords into local bin # for orthogonal, only ghost atoms will have coord >= bboxhi or coord < bboxlo take special care to insure ghosts are in correct bins even w/ roundoff hi ghost atoms = nbin,nbin+1,etc owned atoms = 0 to nbin-1 lo ghost atoms = -1,-2,etc this is necessary so that both procs on either side of PBC treat a pair of atoms straddling the PBC in a consistent way for triclinic, doesn't matter since stencil & neigh list built differently ------------------------------------------------------------------------- */ int Neighbor::coord2bin(double *x) { int ix,iy,iz; if (x[0] >= bboxhi[0]) ix = static_cast ((x[0]-bboxhi[0])*bininvx) + nbinx; else if (x[0] >= bboxlo[0]) { ix = static_cast ((x[0]-bboxlo[0])*bininvx); ix = MIN(ix,nbinx-1); } else ix = static_cast ((x[0]-bboxlo[0])*bininvx) - 1; if (x[1] >= bboxhi[1]) iy = static_cast ((x[1]-bboxhi[1])*bininvy) + nbiny; else if (x[1] >= bboxlo[1]) { iy = static_cast ((x[1]-bboxlo[1])*bininvy); iy = MIN(iy,nbiny-1); } else iy = static_cast ((x[1]-bboxlo[1])*bininvy) - 1; if (x[2] >= bboxhi[2]) iz = static_cast ((x[2]-bboxhi[2])*bininvz) + nbinz; else if (x[2] >= bboxlo[2]) { iz = static_cast ((x[2]-bboxlo[2])*bininvz); iz = MIN(iz,nbinz-1); } else iz = static_cast ((x[2]-bboxlo[2])*bininvz) - 1; return (iz-mbinzlo)*mbiny*mbinx + (iy-mbinylo)*mbinx + (ix-mbinxlo); } /* ---------------------------------------------------------------------- same as coord2bin, but also return ix,iy,iz offsets in each dim ------------------------------------------------------------------------- */ int Neighbor::coord2bin(double *x, int &ix, int &iy, int &iz) { if (x[0] >= bboxhi[0]) ix = static_cast ((x[0]-bboxhi[0])*bininvx) + nbinx; else if (x[0] >= bboxlo[0]) { ix = static_cast ((x[0]-bboxlo[0])*bininvx); ix = MIN(ix,nbinx-1); } else ix = static_cast ((x[0]-bboxlo[0])*bininvx) - 1; if (x[1] >= bboxhi[1]) iy = static_cast ((x[1]-bboxhi[1])*bininvy) + nbiny; else if (x[1] >= bboxlo[1]) { iy = static_cast ((x[1]-bboxlo[1])*bininvy); iy = MIN(iy,nbiny-1); } else iy = static_cast ((x[1]-bboxlo[1])*bininvy) - 1; if (x[2] >= bboxhi[2]) iz = static_cast ((x[2]-bboxhi[2])*bininvz) + nbinz; else if (x[2] >= bboxlo[2]) { iz = static_cast ((x[2]-bboxlo[2])*bininvz); iz = MIN(iz,nbinz-1); } else iz = static_cast ((x[2]-bboxlo[2])*bininvz) - 1; ix -= mbinxlo; iy -= mbinylo; iz -= mbinzlo; return iz*mbiny*mbinx + iy*mbinx + ix; } /* ---------------------------------------------------------------------- test if atom pair i,j is excluded from neighbor list due to type, group, molecule settings from neigh_modify command return 1 if should be excluded, 0 if included ------------------------------------------------------------------------- */ int Neighbor::exclusion(int i, int j, int itype, int jtype, int *mask, int *molecule) const { int m; if (nex_type && ex_type[itype][jtype]) return 1; if (nex_group) { for (m = 0; m < nex_group; m++) { if (mask[i] & ex1_bit[m] && mask[j] & ex2_bit[m]) return 1; if (mask[i] & ex2_bit[m] && mask[j] & ex1_bit[m]) return 1; } } if (nex_mol) { for (m = 0; m < nex_mol; m++) if (mask[i] & ex_mol_bit[m] && mask[j] & ex_mol_bit[m] && molecule[i] == molecule[j]) return 1; } return 0; } /* ---------------------------------------------------------------------- return # of bytes of allocated memory ------------------------------------------------------------------------- */ bigint Neighbor::memory_usage() { bigint bytes = 0; bytes += memory->usage(xhold,maxhold,3); if (style != NSQ) { bytes += memory->usage(bins,maxbin); bytes += memory->usage(binhead,maxhead); } for (int i = 0; i < nlist; i++) bytes += lists[i]->memory_usage(); bytes += memory->usage(bondlist,maxbond,3); bytes += memory->usage(anglelist,maxangle,4); bytes += memory->usage(dihedrallist,maxdihedral,5); bytes += memory->usage(improperlist,maximproper,5); return bytes; } /* ---------------------------------------------------------------------- return the value of exclude - used to check compatibility with GPU ------------------------------------------------------------------------- */ int Neighbor::exclude_setting() { return exclude; }