/* ---------------------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator http://lammps.sandia.gov, Sandia National Laboratories Steve Plimpton, sjplimp@sandia.gov Copyright (2003) Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. This software is distributed under the GNU General Public License. See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- Contributing author: Paul Crozier (SNL) ------------------------------------------------------------------------- */ #include "math.h" #include "stdio.h" #include "stdlib.h" #include "string.h" #include "pair_coul_long.h" #include "atom.h" #include "comm.h" #include "force.h" #include "kspace.h" #include "neighbor.h" #include "neigh_list.h" #include "update.h" #include "integrate.h" #include "respa.h" #include "memory.h" #include "error.h" using namespace LAMMPS_NS; #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) #define EWALD_F 1.12837917 #define EWALD_P 0.3275911 #define A1 0.254829592 #define A2 -0.284496736 #define A3 1.421413741 #define A4 -1.453152027 #define A5 1.061405429 /* ---------------------------------------------------------------------- */ PairCoulLong::PairCoulLong(LAMMPS *lmp) : Pair(lmp) { ftable = NULL; } /* ---------------------------------------------------------------------- */ PairCoulLong::~PairCoulLong() { if (allocated) { memory->destroy_2d_int_array(setflag); memory->destroy_2d_double_array(cutsq); } if (ftable) free_tables(); } /* ---------------------------------------------------------------------- */ void PairCoulLong::compute(int eflag, int vflag) { int i,j,ii,jj,inum,jnum,itable; double qtmp,xtmp,ytmp,ztmp,delx,dely,delz,fraction,table; double r,r2inv,forcecoul,fforce,factor_coul; double grij,expm2,prefactor,t,erfc; double phicoul; int *ilist,*jlist,*numneigh,**firstneigh; float rsq; int *int_rsq = (int *) &rsq; eng_coul = 0.0; if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; double **x = atom->x; double **f = atom->f; double *q = atom->q; int nlocal = atom->nlocal; int nall = atom->nlocal + atom->nghost; double *special_coul = force->special_coul; int newton_pair = force->newton_pair; double qqrd2e = force->qqrd2e; 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]; qtmp = q[i]; xtmp = x[i][0]; ytmp = x[i][1]; ztmp = x[i][2]; jlist = firstneigh[i]; jnum = numneigh[i]; for (jj = 0; jj < jnum; jj++) { j = jlist[jj]; if (j < nall) factor_coul = 1.0; else { factor_coul = special_coul[j/nall]; j %= nall; } delx = xtmp - x[j][0]; dely = ytmp - x[j][1]; delz = ztmp - x[j][2]; rsq = delx*delx + dely*dely + delz*delz; if (rsq < cut_coulsq) { r2inv = 1.0/rsq; if (!ncoultablebits || rsq <= tabinnersq) { r = sqrtf(rsq); grij = g_ewald * r; expm2 = exp(-grij*grij); t = 1.0 / (1.0 + EWALD_P*grij); erfc = t * (A1+t*(A2+t*(A3+t*(A4+t*A5)))) * expm2; prefactor = qqrd2e * qtmp*q[j]/r; forcecoul = prefactor * (erfc + EWALD_F*grij*expm2); if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*prefactor; } else { itable = *int_rsq & ncoulmask; itable >>= ncoulshiftbits; fraction = (rsq - rtable[itable]) * drtable[itable]; table = ftable[itable] + fraction*dftable[itable]; forcecoul = qtmp*q[j] * table; if (factor_coul < 1.0) { table = ctable[itable] + fraction*dctable[itable]; prefactor = qtmp*q[j] * table; forcecoul -= (1.0-factor_coul)*prefactor; } } fforce = forcecoul * r2inv; f[i][0] += delx*fforce; f[i][1] += dely*fforce; f[i][2] += delz*fforce; if (newton_pair || j < nlocal) { f[j][0] -= delx*fforce; f[j][1] -= dely*fforce; f[j][2] -= delz*fforce; } if (eflag) { if (!ncoultablebits || rsq <= tabinnersq) phicoul = prefactor*erfc; else { table = etable[itable] + fraction*detable[itable]; phicoul = qtmp*q[j] * table; } if (factor_coul < 1.0) phicoul -= (1.0-factor_coul)*prefactor; if (newton_pair || j < nlocal) eng_coul += phicoul; else eng_coul += 0.5*phicoul; } if (vflag == 1) { if (newton_pair == 0 && j >= nlocal) fforce *= 0.5; virial[0] += delx*delx*fforce; virial[1] += dely*dely*fforce; virial[2] += delz*delz*fforce; virial[3] += delx*dely*fforce; virial[4] += delx*delz*fforce; virial[5] += dely*delz*fforce; } } } } if (vflag == 2) virial_compute(); } /* ---------------------------------------------------------------------- allocate all arrays ------------------------------------------------------------------------- */ void PairCoulLong::allocate() { allocated = 1; int n = atom->ntypes; setflag = memory->create_2d_int_array(n+1,n+1,"pair:setflag"); for (int i = 1; i <= n; i++) for (int j = i; j <= n; j++) setflag[i][j] = 0; cutsq = memory->create_2d_double_array(n+1,n+1,"pair:cutsq"); } /* ---------------------------------------------------------------------- global settings ------------------------------------------------------------------------- */ void PairCoulLong::settings(int narg, char **arg) { if (narg != 1) error->all("Illegal pair_style command"); cut_coul = atof(arg[0]); } /* ---------------------------------------------------------------------- set coeffs for one or more type pairs ------------------------------------------------------------------------- */ void PairCoulLong::coeff(int narg, char **arg) { if (narg != 2) error->all("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); int count = 0; for (int i = ilo; i <= ihi; i++) { for (int j = MAX(jlo,i); j <= jhi; j++) { setflag[i][j] = 1; count++; } } if (count == 0) error->all("Incorrect args for pair coefficients"); } /* ---------------------------------------------------------------------- init specific to this pair style ------------------------------------------------------------------------- */ void PairCoulLong::init_style() { int i,j; if (!atom->q_flag) error->all("Pair style lj/cut/coul/long requires atom attribute q"); int irequest = neighbor->request(this); cut_coulsq = cut_coul * cut_coul; // set & error check interior rRESPA cutoffs if (strcmp(update->integrate_style,"respa") == 0) { if (((Respa *) update->integrate)->level_inner >= 0) { cut_respa = ((Respa *) update->integrate)->cutoff; for (i = 1; i <= atom->ntypes; i++) for (j = i; j <= atom->ntypes; j++) if (cut_coul < cut_respa[3]) error->all("Pair cutoff < Respa interior cutoff"); } } else cut_respa = NULL; // insure use of KSpace long-range solver, set g_ewald if (force->kspace == NULL) error->all("Pair style is incompatible with KSpace style"); g_ewald = force->kspace->g_ewald; // setup force tables if (ncoultablebits) init_tables(); } /* ---------------------------------------------------------------------- init for one type pair i,j and corresponding j,i ------------------------------------------------------------------------- */ double PairCoulLong::init_one(int i, int j) { return cut_coul; } /* ---------------------------------------------------------------------- setup force tables used in compute routines ------------------------------------------------------------------------- */ void PairCoulLong::init_tables() { int masklo,maskhi; double r,grij,expm2,derfc,rsw; double qqrd2e = force->qqrd2e; tabinnersq = tabinner*tabinner; init_bitmap(tabinner,cut_coul,ncoultablebits, masklo,maskhi,ncoulmask,ncoulshiftbits); int ntable = 1; for (int i = 0; i < ncoultablebits; i++) ntable *= 2; // linear lookup tables of length N = 2^ncoultablebits // stored value = value at lower edge of bin // d values = delta from lower edge to upper edge of bin if (ftable) free_tables(); rtable = (double *) memory->smalloc(ntable*sizeof(double),"pair:rtable"); ftable = (double *) memory->smalloc(ntable*sizeof(double),"pair:ftable"); ctable = (double *) memory->smalloc(ntable*sizeof(double),"pair:ctable"); etable = (double *) memory->smalloc(ntable*sizeof(double),"pair:etable"); drtable = (double *) memory->smalloc(ntable*sizeof(double),"pair:drtable"); dftable = (double *) memory->smalloc(ntable*sizeof(double),"pair:dftable"); dctable = (double *) memory->smalloc(ntable*sizeof(double),"pair:dctable"); detable = (double *) memory->smalloc(ntable*sizeof(double),"pair:detable"); if (cut_respa == NULL) { vtable = ptable = dvtable = dptable = NULL; } else { vtable = (double *) memory->smalloc(ntable*sizeof(double),"pair:vtable"); ptable = (double *) memory->smalloc(ntable*sizeof(double),"pair:ptable"); dvtable = (double *) memory->smalloc(ntable*sizeof(double),"pair:dvtable"); dptable = (double *) memory->smalloc(ntable*sizeof(double),"pair:dptable"); } float rsq; int *int_rsq = (int *) &rsq; float minrsq; int *int_minrsq = (int *) &minrsq; int itablemin; *int_minrsq = 0 << ncoulshiftbits; *int_minrsq = *int_minrsq | maskhi; for (int i = 0; i < ntable; i++) { *int_rsq = i << ncoulshiftbits; *int_rsq = *int_rsq | masklo; if (rsq < tabinnersq) { *int_rsq = i << ncoulshiftbits; *int_rsq = *int_rsq | maskhi; } r = sqrtf(rsq); grij = g_ewald * r; expm2 = exp(-grij*grij); derfc = erfc(grij); if (cut_respa == NULL) { rtable[i] = rsq; ftable[i] = qqrd2e/r * (derfc + EWALD_F*grij*expm2); ctable[i] = qqrd2e/r; etable[i] = qqrd2e/r * derfc; } else { rtable[i] = rsq; ftable[i] = qqrd2e/r * (derfc + EWALD_F*grij*expm2 - 1.0); ctable[i] = 0.0; etable[i] = qqrd2e/r * derfc; ptable[i] = qqrd2e/r; vtable[i] = qqrd2e/r * (derfc + EWALD_F*grij*expm2); if (rsq > cut_respa[2]*cut_respa[2]) { if (rsq < cut_respa[3]*cut_respa[3]) { rsw = (r - cut_respa[2])/(cut_respa[3] - cut_respa[2]); ftable[i] += qqrd2e/r * rsw*rsw*(3.0 - 2.0*rsw); ctable[i] = qqrd2e/r * rsw*rsw*(3.0 - 2.0*rsw); } else { ftable[i] = qqrd2e/r * (derfc + EWALD_F*grij*expm2); ctable[i] = qqrd2e/r; } } } minrsq = MIN(minrsq,rsq); } tabinnersq = minrsq; int ntablem1 = ntable - 1; for (int i = 0; i < ntablem1; i++) { drtable[i] = 1.0/(rtable[i+1] - rtable[i]); dftable[i] = ftable[i+1] - ftable[i]; dctable[i] = ctable[i+1] - ctable[i]; detable[i] = etable[i+1] - etable[i]; } if (cut_respa) { for (int i = 0; i < ntablem1; i++) { dvtable[i] = vtable[i+1] - vtable[i]; dptable[i] = ptable[i+1] - ptable[i]; } } // get the delta values for the last table entries // tables are connected periodically between 0 and ntablem1 drtable[ntablem1] = 1.0/(rtable[0] - rtable[ntablem1]); dftable[ntablem1] = ftable[0] - ftable[ntablem1]; dctable[ntablem1] = ctable[0] - ctable[ntablem1]; detable[ntablem1] = etable[0] - etable[ntablem1]; if (cut_respa) { dvtable[ntablem1] = vtable[0] - vtable[ntablem1]; dptable[ntablem1] = ptable[0] - ptable[ntablem1]; } // get the correct delta values at itablemax // smallest r is in bin itablemin // largest r is in bin itablemax, which is itablemin-1, // or ntablem1 if itablemin=0 // deltas at itablemax only needed if corresponding rsq < cut*cut // if so, compute deltas between rsq and cut*cut double f_tmp,c_tmp,e_tmp,p_tmp,v_tmp; itablemin = *int_minrsq & ncoulmask; itablemin >>= ncoulshiftbits; int itablemax = itablemin - 1; if (itablemin == 0) itablemax = ntablem1; *int_rsq = itablemax << ncoulshiftbits; *int_rsq = *int_rsq | maskhi; if (rsq < cut_coulsq) { rsq = cut_coulsq; r = sqrtf(rsq); grij = g_ewald * r; expm2 = exp(-grij*grij); derfc = erfc(grij); if (cut_respa == NULL) { f_tmp = qqrd2e/r * (derfc + EWALD_F*grij*expm2); c_tmp = qqrd2e/r; e_tmp = qqrd2e/r * derfc; } else { f_tmp = qqrd2e/r * (derfc + EWALD_F*grij*expm2 - 1.0); c_tmp = 0.0; e_tmp = qqrd2e/r * derfc; p_tmp = qqrd2e/r; v_tmp = qqrd2e/r * (derfc + EWALD_F*grij*expm2); if (rsq > cut_respa[2]*cut_respa[2]) { if (rsq < cut_respa[3]*cut_respa[3]) { rsw = (r - cut_respa[2])/(cut_respa[3] - cut_respa[2]); f_tmp += qqrd2e/r * rsw*rsw*(3.0 - 2.0*rsw); c_tmp = qqrd2e/r * rsw*rsw*(3.0 - 2.0*rsw); } else { f_tmp = qqrd2e/r * (derfc + EWALD_F*grij*expm2); c_tmp = qqrd2e/r; } } } drtable[itablemax] = 1.0/(rsq - rtable[itablemax]); dftable[itablemax] = f_tmp - ftable[itablemax]; dctable[itablemax] = c_tmp - ctable[itablemax]; detable[itablemax] = e_tmp - etable[itablemax]; if (cut_respa) { dvtable[itablemax] = v_tmp - vtable[itablemax]; dptable[itablemax] = p_tmp - ptable[itablemax]; } } } /* ---------------------------------------------------------------------- proc 0 writes to restart file ------------------------------------------------------------------------- */ void PairCoulLong::write_restart(FILE *fp) { write_restart_settings(fp); } /* ---------------------------------------------------------------------- proc 0 reads from restart file, bcasts ------------------------------------------------------------------------- */ void PairCoulLong::read_restart(FILE *fp) { read_restart_settings(fp); allocate(); } /* ---------------------------------------------------------------------- proc 0 writes to restart file ------------------------------------------------------------------------- */ void PairCoulLong::write_restart_settings(FILE *fp) { fwrite(&cut_coul,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 PairCoulLong::read_restart_settings(FILE *fp) { if (comm->me == 0) { fread(&cut_coul,sizeof(double),1,fp); fread(&offset_flag,sizeof(int),1,fp); fread(&mix_flag,sizeof(int),1,fp); } MPI_Bcast(&cut_coul,1,MPI_DOUBLE,0,world); MPI_Bcast(&offset_flag,1,MPI_INT,0,world); MPI_Bcast(&mix_flag,1,MPI_INT,0,world); } /* ---------------------------------------------------------------------- free memory for tables used in pair computations ------------------------------------------------------------------------- */ void PairCoulLong::free_tables() { memory->sfree(rtable); memory->sfree(drtable); memory->sfree(ftable); memory->sfree(dftable); memory->sfree(ctable); memory->sfree(dctable); memory->sfree(etable); memory->sfree(detable); memory->sfree(vtable); memory->sfree(dvtable); memory->sfree(ptable); memory->sfree(dptable); } /* ---------------------------------------------------------------------- */ void PairCoulLong::single(int i, int j, int itype, int jtype, double rsq, double factor_coul, double factor_lj, int eflag, One &one) { double r2inv,r,grij,expm2,t,erfc,prefactor; double fraction,table,forcecoul,phicoul; int itable; r2inv = 1.0/rsq; if (!ncoultablebits || rsq <= tabinnersq) { r = sqrt(rsq); grij = g_ewald * r; expm2 = exp(-grij*grij); t = 1.0 / (1.0 + EWALD_P*grij); erfc = t * (A1+t*(A2+t*(A3+t*(A4+t*A5)))) * expm2; prefactor = force->qqrd2e * atom->q[i]*atom->q[j]/r; forcecoul = prefactor * (erfc + EWALD_F*grij*expm2); if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*prefactor; } else { float rsq_single = rsq; int *int_rsq = (int *) &rsq_single; itable = *int_rsq & ncoulmask; itable >>= ncoulshiftbits; fraction = (rsq_single - rtable[itable]) * drtable[itable]; table = ftable[itable] + fraction*dftable[itable]; forcecoul = atom->q[i]*atom->q[j] * table; if (factor_coul < 1.0) { table = ctable[itable] + fraction*dctable[itable]; prefactor = atom->q[i]*atom->q[j] * table; forcecoul -= (1.0-factor_coul)*prefactor; } } one.fforce = forcecoul * r2inv; if (eflag) { if (!ncoultablebits || rsq <= tabinnersq) phicoul = prefactor*erfc; else { table = etable[itable] + fraction*detable[itable]; phicoul = atom->q[i]*atom->q[j] * table; } if (factor_coul < 1.0) phicoul -= (1.0-factor_coul)*prefactor; one.eng_coul = phicoul; one.eng_vdwl = 0.0; } } /* ---------------------------------------------------------------------- */ void *PairCoulLong::extract(char *str) { if (strcmp(str,"cut_coul") == 0) return (void *) &cut_coul; return NULL; }