/* ---------------------------------------------------------------------- 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 <cmath> #include <string.h> #include <stdlib.h> #include "compute_pressure_cylinder.h" #include "atom.h" #include "update.h" #include "force.h" #include "pair.h" #include "neighbor.h" #include "neigh_request.h" #include "neigh_list.h" #include "group.h" #include "memory.h" #include "error.h" #include "citeme.h" #include "domain.h" using namespace LAMMPS_NS; static const char cite_compute_pressure_cylinder[] = "compute pressure/cylinder:\n\n" "@Article{Addington,\n" " author = {C. K. Addington, Y. Long, K. E. Gubbins},\n" " title = {The pressure in interfaces having cylindrical geometry},\n" " journal = {J.~Chem.~Phys.},\n" " year = 2018,\n" " volume = 149,\n" " pages = {084109}\n" "}\n\n"; /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Calculate the configurational components of the pressure tensor in cylindrical geometry, according to the formulation of Addington et al. (2018) +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ ComputePressureCyl::ComputePressureCyl(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg), R(NULL), Rinv(NULL), R2(NULL), R2kin(NULL), invVbin(NULL), density_temp(NULL), density_all(NULL), tangent(NULL), ephi_x(NULL), ephi_y(NULL), Pr_temp(NULL), Pr_all(NULL), Pz_temp(NULL), Pz_all(NULL), Pphi_temp(NULL), Pphi_all(NULL), PrAinv(NULL), PzAinv(NULL), binz(NULL) { if (lmp->citeme) lmp->citeme->add(cite_compute_pressure_cylinder); if (narg != 7) error->all(FLERR,"Illegal compute pressure/cylinder command"); zlo=force->numeric(FLERR,arg[3]); zhi=force->numeric(FLERR,arg[4]); Rmax=force->numeric(FLERR,arg[5]); bin_width=force->numeric(FLERR,arg[6]); if (bin_width<0.0 || bin_width<Rmax) error->all(FLERR,"Illegal compute pressure/cylinder command"); if (zhi<zlo || (zhi-zlo)<bin_width) error->all(FLERR,"Illegal compute pressure/cylinder command"); if (zhi>domain->boxhi[2] || zlo<domain->boxlo[2]) error->all(FLERR,"Illegal compute pressure/cylinder command"); nbins=(int)(Rmax/bin_width); nzbins=(int)((zhi-zlo)/bin_width); if (nbins<1 or nzbins<1) error->all(FLERR,"Illegal compute pressure/cylinder command"); array_flag=1; vector_flag=0; extarray=0; size_array_cols = 5; // r, number density, Pr, Pphi, Pz size_array_rows = nbins; Pr_temp = new double[nbins]; Pr_all = new double[nbins]; Pz_temp = new double[nbins]; Pz_all = new double[nbins]; Pphi_temp = new double[nbins]; Pphi_all = new double[nbins]; R = new double[nbins]; R2 = new double[nbins]; PrAinv = new double[nbins]; PzAinv = new double[nbins]; Rinv = new double[nbins]; binz = new double[nzbins]; R2kin = new double[nbins]; density_temp = new double[nbins]; invVbin = new double[nbins]; density_all = new double[nbins]; memory->create(array,nbins,5,"PN:array"); nphi=360; tangent = new double[nphi]; ephi_x = new double[nphi]; ephi_y = new double[nphi]; nktv2p = force->nktv2p; } /* ---------------------------------------------------------------------- */ ComputePressureCyl::~ComputePressureCyl() { // count all of these for memory usage memory->destroy(array); delete [] R; delete [] Rinv; delete [] R2; delete [] R2kin; delete [] invVbin; delete [] density_temp; delete [] density_all; delete [] tangent; delete [] ephi_x; delete [] ephi_y; delete [] Pr_temp; delete [] Pr_all; delete [] Pz_temp; delete [] Pz_all; delete [] Pphi_temp; delete [] Pphi_all; delete [] PrAinv; delete [] PzAinv; delete [] binz; } /* ---------------------------------------------------------------------- */ void ComputePressureCyl::init() { if (force->pair == NULL) error->all(FLERR,"No pair style is defined for compute pressure/cylinder"); if (force->pair->single_enable == 0) error->all(FLERR,"Pair style does not support compute pressure/cylinder"); double phi; for (int iphi=0;iphi<nphi;iphi++) { phi=((double)iphi)*3.14159/180.0; tangent[iphi]=tan(phi); ephi_x[iphi]=-sin(phi); ephi_y[iphi]=cos(phi); } for (int iq=0;iq<nbins;iq++) { R[iq]=((double)iq+0.5)*bin_width; Rinv[iq]=1.0/R[iq]; R2[iq]=R[iq]*R[iq]; R2kin[iq]=(((double)iq)+1.0)*bin_width; R2kin[iq]*=R2kin[iq]; PrAinv[iq]=1.0/(2.0*3.14159*(zhi-zlo)*R[iq]); } PphiAinv=1.0/((zhi-zlo)*bin_width*2.0*(double)nphi); invVbin[0]=1.0/((zhi-zlo)*3.14159*R2kin[0]); PzAinv[0]=1.0/(3.14159*R2kin[0]*((double)nzbins)); for (int jq=1;jq<nbins;jq++) { invVbin[jq]=1.0/((zhi-zlo)*3.14159*(R2kin[jq]-R2kin[jq-1])); PzAinv[jq]=1.0/(3.14159*(R2kin[jq]-R2kin[jq-1])*((double)nzbins)); } // need an occasional half neighbor list int irequest = neighbor->request(this,instance_me); neighbor->requests[irequest]->pair = 0; neighbor->requests[irequest]->compute = 1; neighbor->requests[irequest]->occasional = 1; for (int zzz=0;zzz<nzbins;zzz++) binz[zzz]=(((double)zzz)+0.5)*bin_width+zlo; } /* ---------------------------------------------------------------------- */ void ComputePressureCyl::init_list(int id, NeighList *ptr) { list = ptr; } /* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- count pairs and compute pair info on this proc only count pair once if newton_pair is off both atom I,J must be in group if flag is set, compute requested info about pair ------------------------------------------------------------------------- */ void ComputePressureCyl::compute_array() { invoked_array = update->ntimestep; int ibin; // clear pressures for (ibin=0;ibin<nbins;ibin++) { density_temp[ibin]=0.0; density_all[ibin]=0.0; Pr_temp[ibin]=0.0; Pr_all[ibin]=0.0; Pphi_temp[ibin]=0.0; Pphi_all[ibin]=0.0; Pz_temp[ibin]=0.0; Pz_all[ibin]=0.0; } // what processor am I? int me; MPI_Comm_rank(world,&me); int i,j,n,ii,jj,inum,jnum,itype,jtype; tagint itag,jtag; double xtmp,ytmp,ztmp,delx,dely,delz; double rsq,eng,fpair,factor_coul,factor_lj; int *ilist,*jlist,*numneigh,**firstneigh; double **x = atom->x; tagint *tag = atom->tag; int *type = atom->type; int *mask = atom->mask; int nlocal = atom->nlocal; double *special_coul = force->special_coul; double *special_lj = force->special_lj; int newton_pair = force->newton_pair; // invoke half neighbor list (will copy or build if necessary) neighbor->build_one(list); inum = list->inum; ilist = list->ilist; numneigh = list->numneigh; firstneigh = list->firstneigh; // calculate number density (by radius) double temp_R2; for (i=0;i<nlocal;i++) if (x[i][2]<zhi && x[i][2]>zlo) { temp_R2=x[i][0]*x[i][0]+x[i][1]*x[i][1]; if (temp_R2>R2kin[nbins-1]) continue; // outside of Rmax for (j=0;j<nbins;j++) if (temp_R2<R2kin[j]) break; density_temp[j]+=invVbin[j]; } MPI_Allreduce(density_temp,density_all,nbins,MPI_DOUBLE,MPI_SUM,world); for (i=0;i<nbins;i++) array[i][1]=density_all[i]; // NEW // loop over neighbors of my atoms // skip if I or J are not in group // for newton = 0 and J = ghost atom, // need to insure I,J pair is only output by one proc // use same itag,jtag logic as in Neighbor::neigh_half_nsq() // for flag = 0, just count pair interactions within force cutoff // for flag = 1, calculate requested output fields Pair *pair = force->pair; double **cutsq = force->pair->cutsq; double r1=0.0; double r2=0.0; double risq,rjsq; double ri,rj,rij,fij; double A,B,C,Bsq,A2inv,A4,D; double alpha1,alpha2,aij; double xi,yi,zi,dx,dy,dz; double m,xR,yR,zR,fn; double alpha,xL,yL,zL,L2,ftphi,ftz; double sqrtD; double lower_z,upper_z; for (ii = 0; ii < inum; ii++) { i = ilist[ii]; if (!(mask[i] & groupbit)) continue; xtmp = x[i][0]; ytmp = x[i][1]; ztmp = x[i][2]; itag = tag[i]; itype = type[i]; jlist = firstneigh[i]; jnum = numneigh[i]; r1=x[i][0]*x[i][0]+x[i][1]*x[i][1]; for (jj = 0; jj < jnum; jj++) { j = jlist[jj]; factor_lj = special_lj[sbmask(j)]; factor_coul = special_coul[sbmask(j)]; j &= NEIGHMASK; if (!(mask[j] & groupbit)) continue; // itag = jtag is possible for long cutoffs that include images of self // do calculation only on appropriate processor if (newton_pair == 0 && j >= nlocal) { jtag = tag[j]; if (itag > jtag) { if ((itag+jtag) % 2 == 0) continue; } else if (itag < jtag) { if ((itag+jtag) % 2 == 1) continue; } else { if (x[j][2] < ztmp) continue; if (x[j][2] == ztmp) { if (x[j][1] < ytmp) continue; if (x[j][1] == ytmp && x[j][0] < xtmp) continue; } } } delx = xtmp - x[j][0]; dely = ytmp - x[j][1]; delz = ztmp - x[j][2]; r2=x[j][0]*x[j][0]+x[j][1]*x[j][1]; // ri is smaller of r1 and r2 if (r2<r1) { risq=r2; rjsq=r1; xi=x[j][0]; yi=x[j][1]; zi=x[j][2]; dx=x[i][0]-x[j][0]; dy=x[i][1]-x[j][1]; dz=x[i][2]-x[j][2]; } else { risq=r1; rjsq=r2; xi=x[i][0]; yi=x[i][1]; zi=x[i][2]; dx=x[j][0]-x[i][0]; dy=x[j][1]-x[i][1]; dz=x[j][2]-x[i][2]; } rsq = delx*delx + dely*dely + delz*delz; jtype = type[j]; if (rsq >= cutsq[itype][jtype]) continue; eng = pair->single(i,j,itype,jtype,rsq,factor_coul,factor_lj,fpair); A=dx*dx+dy*dy; B=2.0*(xi*dx+yi*dy); // normal pressure contribution P_rhorho for (ibin=0;ibin<nbins;ibin++) { // completely inside of R if (rjsq<R2[ibin]) continue; C=risq-R2[ibin]; D=B*B-4.0*A*C; // completely outside of R if (D<0.0) continue; sqrtD=sqrt(D); alpha1=0.5*(-B+sqrtD)/A; alpha2=0.5*(-B-sqrtD)/A; if (alpha1>0.0 && alpha1<1.0) { zR=zi+alpha1*dz; if (zR<zhi && zR>zlo) { xR=xi+alpha1*dx; yR=yi+alpha1*dy; fn=fpair*fabs(xR*dx+yR*dy); Pr_temp[ibin]+=fn; } } if (alpha2>0.0 && alpha2<1.0) { zR=zi+alpha2*dz; if (zR<zhi && zR>zlo) { xR=xi+alpha2*dx; yR=yi+alpha2*dy; fn=fpair*fabs(xR*dx+yR*dy); Pr_temp[ibin]+=fn; } } } // azimuthal pressure contribution (P_phiphi) for (int iphi=0;iphi<nphi;iphi++) { alpha=(yi-xi*tangent[iphi])/(dx*tangent[iphi]-dy); // no intersection with phi surface if (alpha>=1.0 || alpha<=0.0) continue; // no contribution (outside of averaging region) zL=zi+alpha*dz; if (zL>zhi || zL<zlo) continue; xL=xi+alpha*dx; yL=yi+alpha*dy; L2=xL*xL+yL*yL; // no intersection (outside of Rmax) if (L2>R2kin[nbins-1]) continue; ftphi=fabs(dx*ephi_x[iphi]+dy*ephi_y[iphi])*fpair; // add to appropriate bin for (ibin=0;ibin<nbins;ibin++) if (L2<R2kin[ibin]) { Pphi_temp[ibin]+=ftphi; break; } } // z pressure contribution (P_zz) for (int zbin=0;zbin<nzbins;zbin++) { // check if interaction contributes if (x[i][2]>binz[zbin] && x[j][2]>binz[zbin]) continue; if (x[i][2]<binz[zbin] && x[j][2]<binz[zbin]) continue; alpha=(binz[zbin]-zi)/dz; xL=xi+alpha*dx; yL=yi+alpha*dy; L2=xL*xL+yL*yL; if (L2>R2kin[nbins-1]) continue; ftz=fabs(dz)*fpair; // add to appropriate bin for (ibin=0;ibin<nbins;ibin++) if (L2<R2kin[ibin]) { Pz_temp[ibin]+=ftz; break; } } } } // calculate pressure (force over area) for (ibin=0;ibin<nbins;ibin++) { Pr_temp[ibin]*=PrAinv[ibin]*Rinv[ibin]; Pphi_temp[ibin]*=PphiAinv; Pz_temp[ibin]*=PzAinv[ibin]; } // communicate these values across processors MPI_Allreduce(Pr_temp,Pr_all,nbins,MPI_DOUBLE,MPI_SUM,world); MPI_Allreduce(Pphi_temp,Pphi_all,nbins,MPI_DOUBLE,MPI_SUM,world); MPI_Allreduce(Pz_temp,Pz_all,nbins,MPI_DOUBLE,MPI_SUM,world); // populate array for (ibin=0;ibin<nbins;ibin++) { array[ibin][0]=R[ibin]; array[ibin][2]=Pr_all[ibin]*nktv2p; array[ibin][3]=Pphi_all[ibin]*nktv2p; array[ibin][4]=Pz_all[ibin]*nktv2p; } } /* ---------------------------------------------------------------------- memory usage of data ------------------------------------------------------------------------- */ double ComputePressureCyl::memory_usage() { double bytes = (3.0*(double)nphi + 16.0*(double)nbins+5.0*(double)nbins) * sizeof(double); return bytes; }