diff --git a/src/CLASS2/Install.csh b/src/CLASS2/Install.csh new file mode 100644 index 0000000000000000000000000000000000000000..2a9e23653f852046bb97f8a1e7b324b475c5509b --- /dev/null +++ b/src/CLASS2/Install.csh @@ -0,0 +1,50 @@ +# Install/unInstall package classes in LAMMPS + +# pair_lj_class2_coul_long.h must always be in src + +if ($1 == 1) then + + cp style_class2.h .. + + cp bond_class2.cpp .. + cp angle_class2.cpp .. + cp dihedral_class2.cpp .. + cp improper_class2.cpp .. + + cp pair_lj_class2.cpp .. + cp pair_lj_class2_coul_cut.cpp .. + cp pair_lj_class2_coul_long.cpp .. + + cp bond_class2.h .. + cp angle_class2.h .. + cp dihedral_class2.h .. + cp improper_class2.h .. + + cp pair_lj_class2.h .. + cp pair_lj_class2_coul_cut.h .. +# cp pair_lj_class2_coul_long.h .. + +else if ($1 == 0) then + + rm ../style_class2.h + touch ../style_class2.h + + rm ../bond_class2.cpp + rm ../angle_class2.cpp + rm ../dihedral_class2.cpp + rm ../improper_class2.cpp + + rm ../pair_lj_class2.cpp + rm ../pair_lj_class2_coul_cut.cpp + rm ../pair_lj_class2_coul_long.cpp + + rm ../bond_class2.h + rm ../angle_class2.h + rm ../dihedral_class2.h + rm ../improper_class2.h + + rm ../pair_lj_class2.h + rm ../pair_lj_class2_coul_cut.h +# rm ../pair_lj_class2_coul_long.h + +endif diff --git a/src/CLASS2/angle_class2.cpp b/src/CLASS2/angle_class2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8267c9951f7608e057b0dcee31d2f347857e0559 --- /dev/null +++ b/src/CLASS2/angle_class2.cpp @@ -0,0 +1,421 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Eric Simon (Cray) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "angle_class2.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +AngleClass2::~AngleClass2() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(setflag_a); + memory->sfree(setflag_bb); + memory->sfree(setflag_ba); + + memory->sfree(theta0); + memory->sfree(k2); + memory->sfree(k3); + memory->sfree(k4); + + memory->sfree(bb_k); + memory->sfree(bb_r1); + memory->sfree(bb_r2); + + memory->sfree(ba_k1); + memory->sfree(ba_k2); + memory->sfree(ba_r1); + memory->sfree(ba_r2); + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleClass2::compute(int eflag, int vflag) +{ + int i1,i2,i3,n,type,factor; + double delx1,dely1,delz1,delx2,dely2,delz2,rfactor; + double dtheta,dtheta2,dtheta3,dtheta4,de_angle; + double dr1,dr2,tk1,tk2,aa1,aa2,aa11,aa12,aa21,aa22; + double rsq1,rsq2,r1,r2,c,s,a,a11,a12,a22,b1,b2,vx1,vx2,vy1,vy2,vz1,vz2; + double vx11,vx12,vy11,vy12,vz11,vz12,vx21,vx22,vy21,vy22,vz21,vz22; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **anglelist = neighbor->anglelist; + int nanglelist = neighbor->nanglelist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nanglelist; n++) { + + i1 = anglelist[n][0]; + i2 = anglelist[n][1]; + i3 = anglelist[n][2]; + type = anglelist[n][3]; + + if (newton_bond) factor = 3; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + } + rfactor = factor/3.0; + + // 1st bond + + delx1 = x[i1][0] - x[i2][0]; + dely1 = x[i1][1] - x[i2][1]; + delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + + rsq1 = delx1*delx1 + dely1*dely1 + delz1*delz1; + r1 = sqrt(rsq1); + + // 2nd bond + + delx2 = x[i3][0] - x[i2][0]; + dely2 = x[i3][1] - x[i2][1]; + delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + + rsq2 = delx2*delx2 + dely2*dely2 + delz2*delz2; + r2 = sqrt(rsq2); + + // angle (cos and sin) + + c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + s = sqrt(1.0 - c*c); + if (s < SMALL) s = SMALL; + s = 1.0/s; + + // force & energy for angle term + + dtheta = acos(c) - theta0[type]; + dtheta2 = dtheta*dtheta; + dtheta3 = dtheta2*dtheta; + dtheta4 = dtheta3*dtheta; + + de_angle = 2.0*k2[type]*dtheta + 3.0*k3[type]*dtheta2 + + 4.0*k4[type]*dtheta3; + + a = de_angle*s; + + a11 = a*c / rsq1; + a12 = -a / (r1*r2); + a22 = a*c / rsq2; + + vx1 = a11*delx1 + a12*delx2; + vy1 = a11*dely1 + a12*dely2; + vz1 = a11*delz1 + a12*delz2; + + vx2 = a22*delx2 + a12*delx1; + vy2 = a22*dely2 + a12*dely1; + vz2 = a22*delz2 + a12*delz1; + + if (eflag) energy += rfactor * + (k2[type]*dtheta2 + k3[type]*dtheta3 + k4[type]*dtheta4); + + // force & energy for bond-bond term + + dr1 = r1 - bb_r1[type]; + dr2 = r2 - bb_r2[type]; + tk1 = bb_k[type] * dr1; + tk2 = bb_k[type] * dr2; + + vx1 += delx1*tk2/r1; + vy1 += dely1*tk2/r1; + vz1 += delz1*tk2/r1; + + vx2 += delx2*tk1/r2; + vy2 += dely2*tk1/r2; + vz2 += delz2*tk1/r2; + + if (eflag) energy += rfactor * bb_k[type]*dr1*dr2; + + // force & energy for bond-angle term + + aa1 = s * dr1 * ba_k1[type]; + aa2 = s * dr2 * ba_k2[type]; + + aa11 = aa1 * c / rsq1; + aa12 = -aa1 / (r1 * r2); + aa21 = aa2 * c / rsq1; + aa22 = -aa2 / (r1 * r2); + + vx11 = (aa11 * delx1) + (aa12 * delx2); + vx12 = (aa21 * delx1) + (aa22 * delx2); + vy11 = (aa11 * dely1) + (aa12 * dely2); + vy12 = (aa21 * dely1) + (aa22 * dely2); + vz11 = (aa11 * delz1) + (aa12 * delz2); + vz12 = (aa21 * delz1) + (aa22 * delz2); + + aa11 = aa1 * c / rsq2; + aa21 = aa2 * c / rsq2; + + vx21 = (aa11 * delx2) + (aa12 * delx1); + vx22 = (aa21 * delx2) + (aa22 * delx1); + vy21 = (aa11 * dely2) + (aa12 * dely1); + vy22 = (aa21 * dely2) + (aa22 * dely1); + vz21 = (aa11 * delz2) + (aa12 * delz1); + vz22 = (aa21 * delz2) + (aa22 * delz1); + + b1 = ba_k1[type] * dtheta / r1; + b2 = ba_k2[type] * dtheta / r2; + + vx1 += vx11 + b1*delx1 + vx12; + vy1 += vy11 + b1*dely1 + vy12; + vz1 += vz11 + b1*delz1 + vz12; + + vx2 += vx21 + b2*delx2 + vx22; + vy2 += vy21 + b2*dely2 + vy22; + vz2 += vz21 + b2*delz2 + vz22; + + if (eflag) energy += rfactor * + ((ba_k1[type]*dr1*dtheta) + (ba_k2[type]*dr2*dtheta)); + + // apply force to each of 3 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= vx1; + f[i1][1] -= vy1; + f[i1][2] -= vz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += vx1 + vx2; + f[i2][1] += vy1 + vy2; + f[i2][2] += vz1 + vz2; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] -= vx2; + f[i3][1] -= vy2; + f[i3][2] -= vz2; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (delx1*vx1 + delx2*vx2); + virial[1] -= rfactor * (dely1*vy1 + dely2*vy2); + virial[2] -= rfactor * (delz1*vz1 + delz2*vz2); + virial[3] -= rfactor * (delx1*vy1 + delx2*vy2); + virial[4] -= rfactor * (delx1*vz1 + delx2*vz2); + virial[5] -= rfactor * (dely1*vz1 + dely2*vz2); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleClass2::allocate() +{ + allocated = 1; + int n = atom->nangletypes; + + theta0 = (double *) memory->smalloc((n+1)*sizeof(double),"angle:theta0"); + k2 = (double *) memory->smalloc((n+1)*sizeof(double),"angle:k2"); + k3 = (double *) memory->smalloc((n+1)*sizeof(double),"angle:k3"); + k4 = (double *) memory->smalloc((n+1)*sizeof(double),"angle:k4"); + + bb_k = (double *) memory->smalloc((n+1)*sizeof(double),"angle:bb_k"); + bb_r1 = (double *) memory->smalloc((n+1)*sizeof(double),"angle:bb_r1"); + bb_r2 = (double *) memory->smalloc((n+1)*sizeof(double),"angle:bb_r2"); + + ba_k1 = (double *) memory->smalloc((n+1)*sizeof(double),"angle:ba_k1"); + ba_k2 = (double *) memory->smalloc((n+1)*sizeof(double),"angle:ba_k2"); + ba_r1 = (double *) memory->smalloc((n+1)*sizeof(double),"angle:ba_r1"); + ba_r2 = (double *) memory->smalloc((n+1)*sizeof(double),"angle:ba_r2"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"angle:setflag"); + setflag_a = (int *) memory->smalloc((n+1)*sizeof(int),"angle:setflag_a"); + setflag_bb = (int *) memory->smalloc((n+1)*sizeof(int),"angle:setflag_bb"); + setflag_ba = (int *) memory->smalloc((n+1)*sizeof(int),"angle:setflag_ba"); + for (int i = 1; i <= n; i++) + setflag[i] = setflag_a[i] = setflag_bb[i] = setflag_ba[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more types + which = 0 -> Angle coeffs + which = 1 -> BondBond coeffs + which = 2 -> BondAngle coeffs +------------------------------------------------------------------------- */ + +void AngleClass2::coeff(int which, int narg, char **arg) +{ + if (which < 0 || which > 2) + error->all("Invalid coeffs for this angle style"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nangletypes,ilo,ihi); + + int count = 0; + + if (which == 0) { + if (narg != 5) error->all("Incorrect args for angle coefficients"); + + double theta0_one = atof(arg[1]); + double k2_one = atof(arg[2]); + double k3_one = atof(arg[3]); + double k4_one = atof(arg[4]); + + // convert theta0 from degrees to radians + + for (int i = ilo; i <= ihi; i++) { + theta0[i] = theta0_one/180.0 * PI; + k2[i] = k2_one; + k3[i] = k3_one; + k4[i] = k4_one; + setflag_a[i] = 1; + count++; + } + } + + if (which == 1) { + if (narg != 4) error->all("Incorrect args for angle coefficients"); + + double bb_k_one = atof(arg[1]); + double bb_r1_one = atof(arg[2]); + double bb_r2_one = atof(arg[3]); + + for (int i = ilo; i <= ihi; i++) { + bb_k[i] = bb_k_one; + bb_r1[i] = bb_r1_one; + bb_r2[i] = bb_r2_one; + setflag_bb[i] = 1; + count++; + } + } + + if (which == 2) { + if (narg != 5) error->all("Incorrect args for angle coefficients"); + + double ba_k1_one = atof(arg[1]); + double ba_k2_one = atof(arg[2]); + double ba_r1_one = atof(arg[3]); + double ba_r2_one = atof(arg[4]); + + for (int i = ilo; i <= ihi; i++) { + ba_k1[i] = ba_k1_one; + ba_k2[i] = ba_k2_one; + ba_r1[i] = ba_r1_one; + ba_r2[i] = ba_r2_one; + setflag_ba[i] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for angle coefficients"); + + for (int i = ilo; i <= ihi; i++) + if (setflag_a[i] == 1 && setflag_bb[i] == 1 && setflag_ba[i] == 1) + setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +double AngleClass2::equilibrium_angle(int i) +{ + return theta0[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void AngleClass2::write_restart(FILE *fp) +{ + fwrite(&theta0[1],sizeof(double),atom->nangletypes,fp); + fwrite(&k2[1],sizeof(double),atom->nangletypes,fp); + fwrite(&k3[1],sizeof(double),atom->nangletypes,fp); + fwrite(&k4[1],sizeof(double),atom->nangletypes,fp); + + fwrite(&bb_k[1],sizeof(double),atom->nangletypes,fp); + fwrite(&bb_r1[1],sizeof(double),atom->nangletypes,fp); + fwrite(&bb_r2[1],sizeof(double),atom->nangletypes,fp); + + fwrite(&ba_k1[1],sizeof(double),atom->nangletypes,fp); + fwrite(&ba_k2[1],sizeof(double),atom->nangletypes,fp); + fwrite(&ba_r1[1],sizeof(double),atom->nangletypes,fp); + fwrite(&ba_r2[1],sizeof(double),atom->nangletypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void AngleClass2::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&theta0[1],sizeof(double),atom->nangletypes,fp); + fread(&k2[1],sizeof(double),atom->nangletypes,fp); + fread(&k3[1],sizeof(double),atom->nangletypes,fp); + fread(&k4[1],sizeof(double),atom->nangletypes,fp); + + fread(&bb_k[1],sizeof(double),atom->nangletypes,fp); + fread(&bb_r1[1],sizeof(double),atom->nangletypes,fp); + fread(&bb_r2[1],sizeof(double),atom->nangletypes,fp); + + fread(&ba_k1[1],sizeof(double),atom->nangletypes,fp); + fread(&ba_k2[1],sizeof(double),atom->nangletypes,fp); + fread(&ba_r1[1],sizeof(double),atom->nangletypes,fp); + fread(&ba_r2[1],sizeof(double),atom->nangletypes,fp); + } + + MPI_Bcast(&theta0[1],atom->nangletypes,MPI_DOUBLE,0,world); + MPI_Bcast(&k2[1],atom->nangletypes,MPI_DOUBLE,0,world); + MPI_Bcast(&k3[1],atom->nangletypes,MPI_DOUBLE,0,world); + MPI_Bcast(&k4[1],atom->nangletypes,MPI_DOUBLE,0,world); + + MPI_Bcast(&bb_k[1],atom->nangletypes,MPI_DOUBLE,0,world); + MPI_Bcast(&bb_r1[1],atom->nangletypes,MPI_DOUBLE,0,world); + MPI_Bcast(&bb_r2[1],atom->nangletypes,MPI_DOUBLE,0,world); + + MPI_Bcast(&ba_k1[1],atom->nangletypes,MPI_DOUBLE,0,world); + MPI_Bcast(&ba_k2[1],atom->nangletypes,MPI_DOUBLE,0,world); + MPI_Bcast(&ba_r1[1],atom->nangletypes,MPI_DOUBLE,0,world); + MPI_Bcast(&ba_r2[1],atom->nangletypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nangletypes; i++) setflag[i] = 1; +} diff --git a/src/CLASS2/angle_class2.h b/src/CLASS2/angle_class2.h new file mode 100644 index 0000000000000000000000000000000000000000..f5a36a3dc8438865e0dae6ff4c7687750f77a01f --- /dev/null +++ b/src/CLASS2/angle_class2.h @@ -0,0 +1,39 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ANGLE_CLASS2_H +#define ANGLE_CLASS2_H + +#include "stdio.h" +#include "angle.h" + +class AngleClass2 : public Angle { + public: + AngleClass2() {} + ~AngleClass2(); + void compute(int, int); + void coeff(int, int, char **); + double equilibrium_angle(int); + void write_restart(FILE *); + void read_restart(FILE *); + + private: + double *theta0,*k2,*k3,*k4; + double *bb_k,*bb_r1,*bb_r2; + double *ba_k1,*ba_k2,*ba_r1,*ba_r2; + int *setflag_a,*setflag_bb,*setflag_ba; + + void allocate(); +}; + +#endif diff --git a/src/CLASS2/bond_class2.cpp b/src/CLASS2/bond_class2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..326e14ea24fb7717899d5fc449c01980b29339d6 --- /dev/null +++ b/src/CLASS2/bond_class2.cpp @@ -0,0 +1,230 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Eric Simon (Cray) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "bond_class2.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +BondClass2::~BondClass2() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(r0); + memory->sfree(k2); + memory->sfree(k3); + memory->sfree(k4); + } +} + +/* ---------------------------------------------------------------------- */ + +void BondClass2::compute(int eflag, int vflag) +{ + int i1,i2,n,type,factor; + double delx,dely,delz,rsq,r,dr,dr2,dr3,dr4,de_bond,fforce,rfactor; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **bondlist = neighbor->bondlist; + int nbondlist = neighbor->nbondlist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nbondlist; n++) { + + i1 = bondlist[n][0]; + i2 = bondlist[n][1]; + type = bondlist[n][2]; + + if (newton_bond) factor = 2; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + } + rfactor = 0.5 * factor; + + delx = x[i1][0] - x[i2][0]; + dely = x[i1][1] - x[i2][1]; + delz = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx,&dely,&delz); + + rsq = delx*delx + dely*dely + delz*delz; + r = sqrt(rsq); + dr = r - r0[type]; + dr2 = dr*dr; + dr3 = dr2*dr; + dr4 = dr3*dr; + + // force & energy + + de_bond = 2.0*k2[type]*dr + 3.0*k3[type]*dr2 + 4.0*k4[type]*dr3; + if (r > 0.0) fforce = -de_bond/r; + else fforce = 0.0; + + if (eflag) + energy += rfactor * (k2[type]*dr2 + k3[type]*dr3 + k4[type]*dr4); + + // apply force to each of 2 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] += delx*fforce; + f[i1][1] += dely*fforce; + f[i1][2] += delz*fforce; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] -= delx*fforce; + f[i2][1] -= dely*fforce; + f[i2][2] -= delz*fforce; + } + + // virial contribution + + if (vflag) { + virial[0] += rfactor*delx*delx*fforce; + virial[1] += rfactor*dely*dely*fforce; + virial[2] += rfactor*delz*delz*fforce; + virial[3] += rfactor*delx*dely*fforce; + virial[4] += rfactor*delx*delz*fforce; + virial[5] += rfactor*dely*delz*fforce; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void BondClass2::allocate() +{ + allocated = 1; + int n = atom->nbondtypes; + + r0 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:r0"); + k2 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:k2"); + k3 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:k3"); + k4 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:k4"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"bond:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs from one line in input script or data file +------------------------------------------------------------------------- */ + +void BondClass2::coeff(int narg, char **arg) +{ + if (narg != 5) error->all("Incorrect args for bond coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nbondtypes,ilo,ihi); + + double r0_one = atof(arg[1]); + double k2_one = atof(arg[2]); + double k3_one = atof(arg[3]); + double k4_one = atof(arg[4]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + r0[i] = r0_one; + k2[i] = k2_one; + k3[i] = k3_one; + k4[i] = k4_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for bond coefficients"); +} + +/* ---------------------------------------------------------------------- + return an equilbrium bond length +------------------------------------------------------------------------- */ + +double BondClass2::equilibrium_distance(int i) +{ + return r0[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void BondClass2::write_restart(FILE *fp) +{ + fwrite(&r0[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&k2[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&k3[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&k4[1],sizeof(double),atom->nbondtypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void BondClass2::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&r0[1],sizeof(double),atom->nbondtypes,fp); + fread(&k2[1],sizeof(double),atom->nbondtypes,fp); + fread(&k3[1],sizeof(double),atom->nbondtypes,fp); + fread(&k4[1],sizeof(double),atom->nbondtypes,fp); + } + MPI_Bcast(&r0[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&k2[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&k3[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&k4[1],atom->nbondtypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nbondtypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +void BondClass2::single(int type, double rsq, int i, int j, double rfactor, + int eflag, double &fforce, double &eng) +{ + double r = sqrt(rsq); + double dr = r - r0[type]; + double dr2 = dr*dr; + double dr3 = dr2*dr; + double dr4 = dr3*dr; + + // force & energy + + double de_bond = 2.0*k2[type]*dr + 3.0*k3[type]*dr2 + 4.0*k4[type]*dr3; + if (r > 0.0) fforce = -de_bond/r; + else fforce = 0.0; + + if (eflag) eng = rfactor * (k2[type]*dr2 + k3[type]*dr3 + k4[type]*dr4); +} diff --git a/src/CLASS2/bond_class2.h b/src/CLASS2/bond_class2.h new file mode 100644 index 0000000000000000000000000000000000000000..fe250a3104795582fde7c2406a8cbbafbfe3e364 --- /dev/null +++ b/src/CLASS2/bond_class2.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef BOND_CLASS2_H +#define BOND_CLASS2_H + +#include "stdio.h" +#include "bond.h" + +class BondClass2 : public Bond { + public: + BondClass2() {} + ~BondClass2(); + void compute(int, int); + void coeff(int, char **); + double equilibrium_distance(int); + void write_restart(FILE *); + void read_restart(FILE *); + void single(int, double, int, int, double, int, double &, double &); + + private: + double *r0,*k2,*k3,*k4; + + void allocate(); +}; + +#endif diff --git a/src/CLASS2/dihedral_class2.cpp b/src/CLASS2/dihedral_class2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..09c38609dd7cd034a47c74e70fe123c19ad71b2e --- /dev/null +++ b/src/CLASS2/dihedral_class2.cpp @@ -0,0 +1,977 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Eric Simon (Cray) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "dihedral_class2.h" +#include "atom.h" +#include "neighbor.h" +#include "update.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +#define TOLERANCE 0.05 +#define SMALL 0.0000001 + +/* ---------------------------------------------------------------------- + set all global defaults +------------------------------------------------------------------------- */ + +DihedralClass2::DihedralClass2() +{ + PI = 4.0*atan(1.0); +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +DihedralClass2::~DihedralClass2() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(setflag_d); + memory->sfree(setflag_mbt); + memory->sfree(setflag_ebt); + memory->sfree(setflag_at); + memory->sfree(setflag_aat); + memory->sfree(setflag_bb13t); + + memory->sfree(k1); + memory->sfree(k2); + memory->sfree(k3); + memory->sfree(phi1); + memory->sfree(phi2); + memory->sfree(phi3); + + memory->sfree(mbt_f1); + memory->sfree(mbt_f2); + memory->sfree(mbt_f3); + memory->sfree(mbt_r0); + + memory->sfree(ebt_f1_1); + memory->sfree(ebt_f2_1); + memory->sfree(ebt_f3_1); + memory->sfree(ebt_r0_1); + + memory->sfree(ebt_f1_2); + memory->sfree(ebt_f2_2); + memory->sfree(ebt_f3_2); + memory->sfree(ebt_r0_2); + + memory->sfree(at_f1_1); + memory->sfree(at_f2_1); + memory->sfree(at_f3_1); + memory->sfree(at_theta0_1); + + memory->sfree(at_f1_2); + memory->sfree(at_f2_2); + memory->sfree(at_f3_2); + memory->sfree(at_theta0_2); + + memory->sfree(aat_k); + memory->sfree(aat_theta0_1); + memory->sfree(aat_theta0_2); + + memory->sfree(bb13t_k); + memory->sfree(bb13t_r10); + memory->sfree(bb13t_r30); + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralClass2::compute(int eflag, int vflag) +{ + int i1,i2,i3,i4,i,j,k,n,type,factor; + double rfactor; + double delx1,dely1,delz1,dely2,delz2,delx2m,dely2m,delz2m; + double delx2,dely3,delz3,r1mag2,r1,r2mag2,r2,r3mag2,r3; + double sb1,rb1,sb2,rb2,sb3,rb3,c0,r12c1; + double r12c2,costh12,costh13,costh23,sc1,sc2,s1,s2,c; + double cosphi,phi,sinphi,a11,a22,a33,a12,a13,a23,sx1,sx2; + double sx12,sy1,sy2,sy12,sz1,sz2,sz12,dphi1,dphi2,dphi3; + double de_dihedral,t1,t2,t3,t4,cos2phi,cos3phi,bt1,bt2; + double bt3,sumbte,db,sumbtf,at1,at2,at3,da,da1,da2,r1_0; + double r3_0,dr1,dr2,tk1,tk2,vx1,vx2,vx3,vy1,vy2,vy3,vz1; + double vz2,vz3,delx3,s12,sin2; + double dcosphidr[4][3],dphidr[4][3],dbonddr[3][4][3],dthetadr[2][4][3]; + double fabcd[4][3]; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **dihedrallist = neighbor->dihedrallist; + int ndihedrallist = neighbor->ndihedrallist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < ndihedrallist; n++) { + + i1 = dihedrallist[n][0]; + i2 = dihedrallist[n][1]; + i3 = dihedrallist[n][2]; + i4 = dihedrallist[n][3]; + type = dihedrallist[n][4]; + + if (newton_bond) factor = 4; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + if (i4 < nlocal) factor++; + } + rfactor = 0.25 * factor; + + // 1st bond + + delx1 = x[i1][0] - x[i2][0]; + dely1 = x[i1][1] - x[i2][1]; + delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + + // 2nd bond + + delx2 = x[i3][0] - x[i2][0]; + dely2 = x[i3][1] - x[i2][1]; + delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + + delx2m = -delx2; + dely2m = -dely2; + delz2m = -delz2; + domain->minimum_image(&delx2m,&dely2m,&delz2m); + + // 3rd bond + + delx3 = x[i4][0] - x[i3][0]; + dely3 = x[i4][1] - x[i3][1]; + delz3 = x[i4][2] - x[i3][2]; + domain->minimum_image(&delx3,&dely3,&delz3); + + // distances + + r1mag2 = delx1*delx1 + dely1*dely1 + delz1*delz1; + r1 = sqrt(r1mag2); + r2mag2 = delx2*delx2 + dely2*dely2 + delz2*delz2; + r2 = sqrt(r2mag2); + r3mag2 = delx3*delx3 + dely3*dely3 + delz3*delz3; + r3 = sqrt(r3mag2); + + sb1 = 1.0/r1mag2; + rb1 = 1.0/r1; + + sb2 = 1.0/r2mag2; + rb2 = 1.0/r2; + + sb3 = 1.0/r3mag2; + rb3 = 1.0/r3; + + c0 = (delx1*delx3 + dely1*dely3 + delz1*delz3) * rb1*rb3; + + // angles + + r12c1 = rb1*rb2; + r12c2 = rb2*rb3; + costh12 = (delx1*delx2 + dely1*dely2 + delz1*delz2) * r12c1; + costh13 = c0; + costh23 = (delx2m*delx3 + dely2m*dely3 + delz2m*delz3) * r12c2; + + // cos and sin of 2 angles and final c + + sin2 = MAX(1.0 - costh12*costh12,0.0); + sc1 = sqrt(sin2); + if (sc1 < SMALL) sc1 = SMALL; + sc1 = 1.0/sc1; + + sin2 = MAX(1.0 - costh23*costh23,0.0); + sc2 = sqrt(sin2); + if (sc2 < SMALL) sc2 = SMALL; + sc2 = 1.0/sc2; + + s1 = sc1 * sc1; + s2 = sc2 * sc2; + s12 = sc1 * sc2; + c = (c0 + costh12*costh23) * s12; + + // error check + + if (c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) { + int me; + MPI_Comm_rank(world,&me); + if (screen) { + fprintf(screen,"Dihedral problem: %d %d %d %d %d %d\n", + me,update->ntimestep, + atom->tag[i1],atom->tag[i2],atom->tag[i3],atom->tag[i4]); + fprintf(screen," 1st atom: %d %g %g %g\n", + me,x[i1][0],x[i1][1],x[i1][2]); + fprintf(screen," 2nd atom: %d %g %g %g\n", + me,x[i2][0],x[i2][1],x[i2][2]); + fprintf(screen," 3rd atom: %d %g %g %g\n", + me,x[i3][0],x[i3][1],x[i3][2]); + fprintf(screen," 4th atom: %d %g %g %g\n", + me,x[i4][0],x[i4][1],x[i4][2]); + } + } + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + cosphi = c; + phi = acos(c); + + sinphi = sqrt(1.0 - c*c); + sinphi = MAX(sinphi,SMALL); + + a11 = -c*sb1*s1; + a22 = sb2 * (2.0*costh13*s12 - c*(s1+s2)); + a33 = -c*sb3*s2; + a12 = r12c1 * (costh12*c*s1 + costh23*s12); + a13 = rb1*rb3*s12; + a23 = r12c2 * (-costh23*c*s2 - costh12*s12); + + sx1 = a11*delx1 + a12*delx2 + a13*delx3; + sx2 = a12*delx1 + a22*delx2 + a23*delx3; + sx12 = a13*delx1 + a23*delx2 + a33*delx3; + sy1 = a11*dely1 + a12*dely2 + a13*dely3; + sy2 = a12*dely1 + a22*dely2 + a23*dely3; + sy12 = a13*dely1 + a23*dely2 + a33*dely3; + sz1 = a11*delz1 + a12*delz2 + a13*delz3; + sz2 = a12*delz1 + a22*delz2 + a23*delz3; + sz12 = a13*delz1 + a23*delz2 + a33*delz3; + + // set up d(cos(phi))/d(r) and dphi/dr arrays + + dcosphidr[0][0] = -sx1; + dcosphidr[0][1] = -sy1; + dcosphidr[0][2] = -sz1; + dcosphidr[1][0] = sx2 + sx1; + dcosphidr[1][1] = sy2 + sy1; + dcosphidr[1][2] = sz2 + sz1; + dcosphidr[2][0] = sx12 - sx2; + dcosphidr[2][1] = sy12 - sy2; + dcosphidr[2][2] = sz12 - sz2; + dcosphidr[3][0] = -sx12; + dcosphidr[3][1] = -sy12; + dcosphidr[3][2] = -sz12; + + for (i = 0; i < 4; i++) + for (j = 0; j < 3; j++) + dphidr[i][j] = -dcosphidr[i][j] / sinphi; + + // energy + + dphi1 = phi - phi1[type]; + dphi2 = 2.0*phi - phi2[type]; + dphi3 = 3.0*phi - phi3[type]; + + if (eflag) energy += rfactor * (k1[type]*(1.0 - cos(dphi1)) + + k2[type]*(1.0 - cos(dphi2)) + + k3[type]*(1.0 - cos(dphi3))); + + de_dihedral = k1[type]*sin(dphi1) + 2.0*k2[type]*sin(dphi2) + + 3.0*k3[type]*sin(dphi3); + + // torsion forces on all 4 atoms + + for (i = 0; i < 4; i++) + for (j = 0; j < 3; j++) + fabcd[i][j] = de_dihedral*dphidr[i][j]; + + // set up d(bond)/d(r) array + // dbonddr(i,j,k) = bond i, atom j, coordinate k + + for (i = 0; i < 3; i++) + for (j = 0; j < 4; j++) + for (k = 0; k < 3; k++) + dbonddr[i][j][k] = 0.0; + + // bond1 + + dbonddr[0][0][0] = delx1 / r1; + dbonddr[0][0][1] = dely1 / r1; + dbonddr[0][0][2] = delz1 / r1; + dbonddr[0][1][0] = -delx1 / r1; + dbonddr[0][1][1] = -dely1 / r1; + dbonddr[0][1][2] = -delz1 / r1; + + // bond2 + + dbonddr[1][1][0] = delx2 / r2; + dbonddr[1][1][1] = dely2 / r2; + dbonddr[1][1][2] = delz2 / r2; + dbonddr[1][2][0] = -delx2 / r2; + dbonddr[1][2][1] = -dely2 / r2; + dbonddr[1][2][2] = -delz2 / r2; + + // bond3 + + dbonddr[2][2][0] = delx3 / r3; + dbonddr[2][2][1] = dely3 / r3; + dbonddr[2][2][2] = delz3 / r3; + dbonddr[2][3][0] = -delx3 / r3; + dbonddr[2][3][1] = -dely3 / r3; + dbonddr[2][3][2] = -delz3 / r3; + + // set up d(theta)/d(r) array + // dthetadr(i,j,k) = angle i, atom j, coordinate k + + for (i = 0; i < 2; i++) + for (j = 0; j < 4; j++) + for (k = 0; k < 3; k++) + dthetadr[i][j][k] = 0.0; + + t1 = costh12 / r1mag2; + t2 = costh23 / r2mag2; + t3 = costh12 / r2mag2; + t4 = costh23 / r3mag2; + + // angle12 + + dthetadr[0][0][0] = sc1 * ((t1 * delx1) - (delx2 * r12c1)); + dthetadr[0][0][1] = sc1 * ((t1 * dely1) - (dely2 * r12c1)); + dthetadr[0][0][2] = sc1 * ((t1 * delz1) - (delz2 * r12c1)); + + dthetadr[0][1][0] = sc1 * ((-t1 * delx1) + (delx2 * r12c1) + + (-t3 * delx2) + (delx1 * r12c1)); + dthetadr[0][1][1] = sc1 * ((-t1 * dely1) + (dely2 * r12c1) + + (-t3 * dely2) + (dely1 * r12c1)); + dthetadr[0][1][2] = sc1 * ((-t1 * delz1) + (delz2 * r12c1) + + (-t3 * delz2) + (delz1 * r12c1)); + + dthetadr[0][2][0] = sc1 * ((t3 * delx2) - (delx1 * r12c1)); + dthetadr[0][2][1] = sc1 * ((t3 * dely2) - (dely1 * r12c1)); + dthetadr[0][2][2] = sc1 * ((t3 * delz2) - (delz1 * r12c1)); + + // angle23 + + dthetadr[1][1][0] = sc2 * ((t2 * delx2) + (delx3 * r12c2)); + dthetadr[1][1][1] = sc2 * ((t2 * dely2) + (dely3 * r12c2)); + dthetadr[1][1][2] = sc2 * ((t2 * delz2) + (delz3 * r12c2)); + + dthetadr[1][2][0] = sc2 * ((-t2 * delx2) - (delx3 * r12c2) + + (t4 * delx3) + (delx2 * r12c2)); + dthetadr[1][2][1] = sc2 * ((-t2 * dely2) - (dely3 * r12c2) + + (t4 * dely3) + (dely2 * r12c2)); + dthetadr[1][2][2] = sc2 * ((-t2 * delz2) - (delz3 * r12c2) + + (t4 * delz3) + (delz2 * r12c2)); + + dthetadr[1][3][0] = -sc2 * ((t4 * delx3) + (delx2 * r12c2)); + dthetadr[1][3][1] = -sc2 * ((t4 * dely3) + (dely2 * r12c2)); + dthetadr[1][3][2] = -sc2 * ((t4 * delz3) + (delz2 * r12c2)); + + // mid-bond/torsion coupling + // energy on bond2 (middle bond) + + cos2phi = cos(2.0*phi); + cos3phi = cos(3.0*phi); + + bt1 = mbt_f1[type] * cosphi; + bt2 = mbt_f2[type] * cos2phi; + bt3 = mbt_f3[type] * cos3phi; + sumbte = bt1 + bt2 + bt3; + db = r2 - mbt_r0[type]; + if (eflag) energy += rfactor * db * sumbte; + + // force on bond2 + + bt1 = -mbt_f1[type] * sinphi; + bt2 = -2.0 * mbt_f2[type] * sin(2.0*phi); + bt3 = -3.0 * mbt_f3[type] * sin(3.0*phi); + sumbtf = bt1 + bt2 + bt3; + + for (i = 0; i < 4; i++) + for (j = 0; j < 3; j++) + fabcd[i][j] += db*sumbtf*dphidr[i][j] + sumbte*dbonddr[1][i][j]; + + // end-bond/torsion coupling + // energy on bond1 (first bond) + + bt1 = ebt_f1_1[type] * cosphi; + bt2 = ebt_f2_1[type] * cos2phi; + bt3 = ebt_f3_1[type] * cos3phi; + sumbte = bt1 + bt2 + bt3; + + db = r1 - ebt_r0_1[type]; + if (eflag) energy += rfactor * db * (bt1+bt2+bt3); + + // force on bond1 + + bt1 = ebt_f1_1[type] * sinphi; + bt2 = 2.0 * ebt_f2_1[type] * sin(2.0*phi); + bt3 = 3.0 * ebt_f3_1[type] * sin(3.0*phi); + sumbtf = bt1 + bt2 + bt3; + + for (i = 0; i < 4; i++) + for (j = 0; j < 3; j++) + fabcd[i][j] -= db*sumbtf*dphidr[i][j] + sumbte*dbonddr[0][i][j]; + + // end-bond/torsion coupling + // energy on bond3 (last bond) + + bt1 = ebt_f1_2[type] * cosphi; + bt2 = ebt_f2_2[type] * cos2phi; + bt3 = ebt_f3_2[type] * cos3phi; + sumbte = bt1 + bt2 + bt3; + + db = r3 - ebt_r0_2[type]; + if (eflag) energy += rfactor * db * (bt1+bt2+bt3); + + // force on bond3 + + bt1 = -ebt_f1_2[type] * sinphi; + bt2 = -2.0 * ebt_f2_2[type] * sin(2.0*phi); + bt3 = -3.0 * ebt_f3_2[type] * sin(3.0*phi); + sumbtf = bt1 + bt2 + bt3; + + for (i = 0; i < 4; i++) + for (j = 0; j < 3; j++) + fabcd[i][j] += db*sumbtf*dphidr[i][j] + sumbte*dbonddr[2][i][j]; + + // angle/torsion coupling + // energy on angle1 + + at1 = at_f1_1[type] * cosphi; + at2 = at_f2_1[type] * cos2phi; + at3 = at_f3_1[type] * cos3phi; + sumbte = at1 + at2 + at3; + + da = acos(costh12) - at_theta0_1[type]; + if (eflag) energy += rfactor * da * (at1+at2+at3); + + // force on angle1 + + bt1 = at_f1_1[type] * sinphi; + bt2 = 2.0 * at_f2_1[type] * sin(2.0*phi); + bt3 = 3.0 * at_f3_1[type] * sin(3.0*phi); + sumbtf = bt1 + bt2 + bt3; + + for (i = 0; i < 4; i++) + for (j = 0; j < 3; j++) + fabcd[i][j] -= da*sumbtf*dphidr[i][j] + sumbte*dthetadr[0][i][j]; + + // energy on angle2 + + at1 = at_f1_2[type] * cosphi; + at2 = at_f2_2[type] * cos2phi; + at3 = at_f3_2[type] * cos3phi; + sumbte = at1 + at2 + at3; + + da = acos(costh23) - at_theta0_2[type]; + if (eflag) energy += rfactor *da * (at1+at2+at3); + + // force on angle2 + + bt1 = -at_f1_2[type] * sinphi; + bt2 = -2.0 * at_f2_2[type] * sin(2.0*phi); + bt3 = -3.0 * at_f3_2[type] * sin(3.0*phi); + sumbtf = bt1 + bt2 + bt3; + + for (i = 0; i < 4; i++) + for (j = 0; j < 3; j++) + fabcd[i][j] += da*sumbtf*dphidr[i][j] + sumbte*dthetadr[1][i][j]; + + // angle/angle/torsion coupling + + da1 = acos(costh12) - aat_theta0_1[type]; + da2 = acos(costh23) - aat_theta0_2[type]; + + // energy + + if (eflag) energy += rfactor * aat_k[type]*da1*da2*cosphi; + + // force + + for (i = 0; i < 4; i++) + for (j = 0; j < 3; j++) + fabcd[i][j] -= aat_k[type] * + (cosphi * (da2*dthetadr[0][i][j] - da1*dthetadr[1][i][j]) + + sinphi * da1*da2*dphidr[i][j]); + + // bond1/bond3 coupling + + if (fabs(bb13t_k[type]) > SMALL) { + + r1_0 = bb13t_r10[type]; + r3_0 = bb13t_r30[type]; + dr1 = r1 - r1_0; + dr2 = r3 - r3_0; + tk1 = -bb13t_k[type] * dr1 / r3; + tk2 = -bb13t_k[type] * dr2 / r1; + + if (eflag) energy += rfactor * bb13t_k[type]*dr1*dr2; + + fabcd[0][0] += tk2 * delx1; + fabcd[0][1] += tk2 * dely1; + fabcd[0][2] += tk2 * delz1; + + fabcd[1][0] -= tk2 * delx1; + fabcd[1][1] -= tk2 * dely1; + fabcd[1][2] -= tk2 * delz1; + + fabcd[2][0] -= tk1 * delx3; + fabcd[2][1] -= tk1 * dely3; + fabcd[2][2] -= tk1 * delz3; + + fabcd[3][0] += tk1 * delx3; + fabcd[3][1] += tk1 * dely3; + fabcd[3][2] += tk1 * delz3; + } + + // apply force to each of 4 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] += fabcd[0][0]; + f[i1][1] += fabcd[0][1]; + f[i1][2] += fabcd[0][2]; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += fabcd[1][0]; + f[i2][1] += fabcd[1][1]; + f[i2][2] += fabcd[1][2]; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] += fabcd[2][0]; + f[i3][1] += fabcd[2][1]; + f[i3][2] += fabcd[2][2]; + } + + if (newton_bond || i4 < nlocal) { + f[i4][0] += fabcd[3][0]; + f[i4][1] += fabcd[3][1]; + f[i4][2] += fabcd[3][2]; + } + + // virial contribution + + if (vflag) { + vx1 = fabcd[0][0]; + vx2 = fabcd[2][0] + fabcd[3][0]; + vx3 = fabcd[3][0]; + + vy1 = fabcd[0][1]; + vy2 = fabcd[2][1] + fabcd[3][1]; + vy3 = fabcd[3][1]; + + vz1 = fabcd[0][2]; + vz2 = fabcd[2][2] + fabcd[3][2]; + vz3 = fabcd[3][2]; + + virial[0] += rfactor * (delx1*vx1 + delx2*vx2 + delx3*vx3); + virial[1] += rfactor * (dely1*vy1 + dely2*vy2 + dely3*vy3); + virial[2] += rfactor * (delz1*vz1 + delz2*vz2 + delz3*vz3); + virial[3] += rfactor * (delx1*vy1 + delx2*vy2 + delx3*vy3); + virial[4] += rfactor * (delx1*vz1 + delx2*vz2 + delx3*vz3); + virial[5] += rfactor * (dely1*vz1 + dely2*vz2 + dely3*vz3); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralClass2::allocate() +{ + allocated = 1; + int n = atom->ndihedraltypes; + + k1 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:k1"); + k2 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:k2"); + k3 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:k3"); + phi1 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:phi1"); + phi2 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:phi2"); + phi3 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:phi3"); + + mbt_f1 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:mbt_f1"); + mbt_f2 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:mbt_f2"); + mbt_f3 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:mbt_f3"); + mbt_r0 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:mbt_r0"); + + ebt_f1_1 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:ebt_f1_1"); + ebt_f2_1 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:ebt_f2_1"); + ebt_f3_1 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:ebt_f3_1"); + ebt_r0_1 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:ebt_r0_1"); + + ebt_f1_2 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:ebt_f1_2"); + ebt_f2_2 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:ebt_f2_2"); + ebt_f3_2 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:ebt_f3_2"); + ebt_r0_2 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:ebt_r0_2"); + + at_f1_1 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:at_f1_1"); + at_f2_1 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:at_f2_1"); + at_f3_1 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:at_f3_1"); + at_theta0_1 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:at_theta0_1"); + + at_f1_2 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:at_f1_2"); + at_f2_2 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:at_f2_2"); + at_f3_2 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:at_f3_2"); + at_theta0_2 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:at_theta0_2"); + + aat_k = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:aat_k"); + aat_theta0_1 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:aat_theta0_1"); + aat_theta0_2 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:aat_theta0_2"); + + bb13t_k = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:bb13t_k"); + bb13t_r10 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:bb13t_r10"); + bb13t_r30 = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:bb13t_r30"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"dihedral:setflag"); + setflag_d = (int *) + memory->smalloc((n+1)*sizeof(int),"dihedral:setflag_d"); + setflag_mbt = (int *) + memory->smalloc((n+1)*sizeof(int),"dihedral:setflag_mbt"); + setflag_ebt = (int *) + memory->smalloc((n+1)*sizeof(int),"dihedral:setflag_ebt"); + setflag_at = (int *) + memory->smalloc((n+1)*sizeof(int),"dihedral:setflag_at"); + setflag_aat = (int *) + memory->smalloc((n+1)*sizeof(int),"dihedral:setflag_aat"); + setflag_bb13t = (int *) + memory->smalloc((n+1)*sizeof(int),"dihedral:setflag_bb13t"); + for (int i = 1; i <= n; i++) + setflag[i] = setflag_d[i] = setflag_mbt[i] = setflag_ebt[i] = + setflag_at[i] = setflag_aat[i] = setflag_bb13t[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more types + which = 0 -> Dihedral coeffs + which = 1 -> MiddleBondTorsion coeffs + which = 2 -> EndBondTorsion coeffs + which = 3 -> AngleTorsion coeffs + which = 4 -> AngleAngleTorsion coeffs + which = 5 -> BondBond13Torsion coeffs +------------------------------------------------------------------------- */ + +void DihedralClass2::coeff(int which, int narg, char **arg) +{ + if (which < 0 || which > 5) + error->all("Invalid coeffs for this dihedral style"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->ndihedraltypes,ilo,ihi); + + int count = 0; + + if (which == 0) { + if (narg != 7) error->all("Incorrect args for dihedral coefficients"); + + double k1_one = atof(arg[1]); + double phi1_one = atof(arg[2]); + double k2_one = atof(arg[3]); + double phi2_one = atof(arg[4]); + double k3_one = atof(arg[5]); + double phi3_one = atof(arg[6]); + + // convert phi's from degrees to radians + + for (int i = ilo; i <= ihi; i++) { + k1[i] = k1_one; + phi1[i] = phi1_one/180.0 * PI; + k2[i] = k2_one; + phi2[i] = phi2_one/180.0 * PI; + k3[i] = k3_one; + phi3[i] = phi3_one/180.0 * PI; + setflag_d[i] = 1; + count++; + } + } + + if (which == 1) { + if (narg != 5) error->all("Incorrect args for dihedral coefficients"); + + double f1_one = atof(arg[1]); + double f2_one = atof(arg[2]); + double f3_one = atof(arg[3]); + double r0_one = atof(arg[4]); + + for (int i = ilo; i <= ihi; i++) { + mbt_f1[i] = f1_one; + mbt_f2[i] = f2_one; + mbt_f3[i] = f3_one; + mbt_r0[i] = r0_one; + setflag_mbt[i] = 1; + count++; + } + } + + if (which == 2) { + if (narg != 9) error->all("Incorrect args for dihedral coefficients"); + + double f1_1_one = atof(arg[1]); + double f2_1_one = atof(arg[2]); + double f3_1_one = atof(arg[3]); + double f1_2_one = atof(arg[4]); + double f2_2_one = atof(arg[5]); + double f3_2_one = atof(arg[6]); + double r0_1_one = atof(arg[7]); + double r0_2_one = atof(arg[8]); + + for (int i = ilo; i <= ihi; i++) { + ebt_f1_1[i] = f1_1_one; + ebt_f2_1[i] = f2_1_one; + ebt_f3_1[i] = f3_1_one; + ebt_f1_2[i] = f1_2_one; + ebt_f2_2[i] = f2_2_one; + ebt_f3_2[i] = f3_2_one; + ebt_r0_1[i] = r0_1_one; + ebt_r0_2[i] = r0_2_one; + setflag_ebt[i] = 1; + count++; + } + } + + if (which == 3) { + if (narg != 9) error->all("Incorrect args for dihedral coefficients"); + + double f1_1_one = atof(arg[1]); + double f2_1_one = atof(arg[2]); + double f3_1_one = atof(arg[3]); + double f1_2_one = atof(arg[4]); + double f2_2_one = atof(arg[5]); + double f3_2_one = atof(arg[6]); + double theta0_1_one = atof(arg[7]); + double theta0_2_one = atof(arg[8]); + + // convert theta0's from degrees to radians + + for (int i = ilo; i <= ihi; i++) { + at_f1_1[i] = f1_1_one; + at_f2_1[i] = f2_1_one; + at_f3_1[i] = f3_1_one; + at_f1_2[i] = f1_2_one; + at_f2_2[i] = f2_2_one; + at_f3_2[i] = f3_2_one; + at_theta0_1[i] = theta0_1_one/180.0 * PI; + at_theta0_2[i] = theta0_2_one/180.0 * PI; + setflag_at[i] = 1; + count++; + } + } + + if (which == 4) { + if (narg != 4) error->all("Incorrect args for dihedral coefficients"); + + double k_one = atof(arg[1]); + double theta0_1_one = atof(arg[2]); + double theta0_2_one = atof(arg[3]); + + // convert theta0's from degrees to radians + + for (int i = ilo; i <= ihi; i++) { + aat_k[i] = k_one; + aat_theta0_1[i] = theta0_1_one/180.0 * PI; + aat_theta0_2[i] = theta0_2_one/180.0 * PI; + setflag_aat[i] = 1; + count++; + } + } + + if (which == 5) { + if (narg != 4) error->all("Incorrect args for dihedral coefficients"); + + double k_one = atof(arg[1]); + double r10_one = atof(arg[2]); + double r30_one = atof(arg[3]); + + for (int i = ilo; i <= ihi; i++) { + bb13t_k[i] = k_one; + bb13t_r10[i] = r10_one; + bb13t_r30[i] = r30_one; + setflag_bb13t[i] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for dihedral coefficients"); + + for (int i = ilo; i <= ihi; i++) + if (setflag_d[i] == 1 && setflag_mbt[i] == 1 && setflag_ebt[i] == 1 && + setflag_at[i] == 1 && setflag_aat[i] == 1 && setflag_bb13t[i] == 1) + setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void DihedralClass2::write_restart(FILE *fp) +{ + fwrite(&k1[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&k2[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&k3[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&phi1[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&phi2[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&phi3[1],sizeof(double),atom->ndihedraltypes,fp); + + fwrite(&mbt_f1[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&mbt_f2[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&mbt_f3[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&mbt_r0[1],sizeof(double),atom->ndihedraltypes,fp); + + fwrite(&ebt_f1_1[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&ebt_f2_1[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&ebt_f3_1[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&ebt_r0_1[1],sizeof(double),atom->ndihedraltypes,fp); + + fwrite(&ebt_f1_2[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&ebt_f2_2[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&ebt_f3_2[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&ebt_r0_2[1],sizeof(double),atom->ndihedraltypes,fp); + + fwrite(&at_f1_1[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&at_f2_1[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&at_f3_1[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&at_theta0_1[1],sizeof(double),atom->ndihedraltypes,fp); + + fwrite(&at_f1_2[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&at_f2_2[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&at_f3_2[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&at_theta0_2[1],sizeof(double),atom->ndihedraltypes,fp); + + fwrite(&aat_k[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&aat_theta0_1[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&aat_theta0_2[1],sizeof(double),atom->ndihedraltypes,fp); + + fwrite(&bb13t_k[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&bb13t_r10[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&bb13t_r30[1],sizeof(double),atom->ndihedraltypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void DihedralClass2::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k1[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&k2[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&k3[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&phi1[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&phi2[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&phi3[1],sizeof(double),atom->ndihedraltypes,fp); + + fread(&mbt_f1[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&mbt_f2[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&mbt_f3[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&mbt_r0[1],sizeof(double),atom->ndihedraltypes,fp); + + fread(&ebt_f1_1[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&ebt_f2_1[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&ebt_f3_1[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&ebt_r0_1[1],sizeof(double),atom->ndihedraltypes,fp); + + fread(&ebt_f1_2[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&ebt_f2_2[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&ebt_f3_2[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&ebt_r0_2[1],sizeof(double),atom->ndihedraltypes,fp); + + fread(&at_f1_1[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&at_f2_1[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&at_f3_1[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&at_theta0_1[1],sizeof(double),atom->ndihedraltypes,fp); + + fread(&at_f1_2[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&at_f2_2[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&at_f3_2[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&at_theta0_2[1],sizeof(double),atom->ndihedraltypes,fp); + + fread(&aat_k[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&aat_theta0_1[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&aat_theta0_2[1],sizeof(double),atom->ndihedraltypes,fp); + + fread(&bb13t_k[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&bb13t_r10[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&bb13t_r30[1],sizeof(double),atom->ndihedraltypes,fp); + } + + MPI_Bcast(&k1[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&k2[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&k3[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&phi1[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&phi2[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&phi3[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + + MPI_Bcast(&mbt_f1[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&mbt_f2[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&mbt_f3[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&mbt_r0[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + + MPI_Bcast(&ebt_f1_1[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&ebt_f2_1[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&ebt_f3_1[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&ebt_r0_1[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + + MPI_Bcast(&ebt_f1_2[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&ebt_f2_2[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&ebt_f3_2[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&ebt_r0_2[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + + MPI_Bcast(&at_f1_1[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&at_f2_1[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&at_f3_1[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&at_theta0_1[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + + MPI_Bcast(&at_f1_2[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&at_f2_2[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&at_f3_2[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&at_theta0_2[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + + MPI_Bcast(&aat_k[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&aat_theta0_1[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&aat_theta0_2[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + + MPI_Bcast(&bb13t_k[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&bb13t_r10[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&bb13t_r30[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->ndihedraltypes; i++) setflag[i] = 1; +} diff --git a/src/CLASS2/dihedral_class2.h b/src/CLASS2/dihedral_class2.h new file mode 100644 index 0000000000000000000000000000000000000000..7a38262665a4186c730e3bddbe1c3decc7db0fde --- /dev/null +++ b/src/CLASS2/dihedral_class2.h @@ -0,0 +1,46 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DIHEDRAL_CLASS2_H +#define DIHEDRAL_CLASS2_H + +#include "stdio.h" +#include "dihedral.h" + +class DihedralClass2 : public Dihedral { + public: + DihedralClass2(); + ~DihedralClass2(); + void compute(int, int); + void coeff(int, int, char **); + void write_restart(FILE *); + void read_restart(FILE *); + + private: + double *k1,*k2,*k3; + double *phi1,*phi2,*phi3; + double *mbt_f1,*mbt_f2,*mbt_f3,*mbt_r0; + double *ebt_f1_1,*ebt_f2_1,*ebt_f3_1,*ebt_r0_1; + double *ebt_f1_2,*ebt_f2_2,*ebt_f3_2,*ebt_r0_2; + double *at_f1_1,*at_f2_1,*at_f3_1,*at_theta0_1; + double *at_f1_2,*at_f2_2,*at_f3_2,*at_theta0_2; + double *aat_k,*aat_theta0_1,*aat_theta0_2; + double *bb13t_k,*bb13t_r10,*bb13t_r30; + int *setflag_d,*setflag_mbt,*setflag_ebt; + int *setflag_at,*setflag_aat,*setflag_bb13t; + double PI; + + void allocate(); +}; + +#endif diff --git a/src/CLASS2/improper_class2.cpp b/src/CLASS2/improper_class2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c1530a2e5043c00a2184f84febaed6354538561d --- /dev/null +++ b/src/CLASS2/improper_class2.cpp @@ -0,0 +1,891 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Eric Simon (Cray) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "improper_class2.h" +#include "atom.h" +#include "neighbor.h" +#include "update.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + set all global defaults +------------------------------------------------------------------------- */ + +ImproperClass2::ImproperClass2() +{ + PI = 4.0*atan(1.0); +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +ImproperClass2::~ImproperClass2() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(setflag_i); + memory->sfree(setflag_aa); + + memory->sfree(k0); + memory->sfree(chi0); + + memory->sfree(aa_k1); + memory->sfree(aa_k2); + memory->sfree(aa_k3); + memory->sfree(aa_theta0_1); + memory->sfree(aa_theta0_2); + memory->sfree(aa_theta0_3); + } +} + +/* ---------------------------------------------------------------------- */ + +void ImproperClass2::compute(int eflag, int vflag) +{ + int i1,i2,i3,i4,i,j,k,n,type,factor; + double rfactor; + double delr[3][3],rmag[3],rinvmag[3],rmag2[3]; + double theta[3],costheta[3],sintheta[3]; + double cossqtheta[3],sinsqtheta[3],invstheta[3]; + double rABxrCB[3],rDBxrAB[3],rCBxrDB[3]; + double ddelr[3][4],dr[3][4][3],dinvr[3][4][3]; + double dthetadr[3][4][3],dinvsth[3][4][3]; + double dinv3r[4][3],dinvs3r[3][4][3]; + double drCBxrDB[3],rCBxdrDB[3],drDBxrAB[3],rDBxdrAB[3]; + double drABxrCB[3],rABxdrCB[3]; + double dot1,dot2,dd[3]; + double fdot[3][4][3],f2[3][4][3],invs3r[3],inv3r; + double t,tt1,tt3,sc1; + double dotCBDBAB,dotDBABCB,dotABCBDB; + double chi,deltachi,d2chi,cossin2; + double drAB[3][4][3],drCB[3][4][3],drDB[3][4][3]; + double dchi[3][4][3],dtotalchi[4][3]; + double schiABCD,chiABCD,schiCBDA,chiCBDA,schiDBAC,chiDBAC; + double fabcd[4][3]; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + for (i = 0; i < 3; i++) + for (j = 0; j < 4; j++) + for (k = 0; k < 3; k++) { + dthetadr[i][j][k] = 0.0; + drAB[i][j][k] = 0.0; + drCB[i][j][k] = 0.0; + drDB[i][j][k] = 0.0; + } + + double **x = atom->x; + double **f = atom->f; + int **improperlist = neighbor->improperlist; + int nimproperlist = neighbor->nimproperlist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nimproperlist; n++) { + + i1 = improperlist[n][0]; + i2 = improperlist[n][1]; + i3 = improperlist[n][2]; + i4 = improperlist[n][3]; + type = improperlist[n][4]; + + if (k0[type] == 0.0) continue; + + if (newton_bond) factor = 4; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + if (i4 < nlocal) factor++; + } + rfactor = 0.25 * factor; + + // difference vectors + + delr[0][0] = x[i1][0] - x[i2][0]; + delr[0][1] = x[i1][1] - x[i2][1]; + delr[0][2] = x[i1][2] - x[i2][2]; + domain->minimum_image(&delr[0][0],&delr[0][1],&delr[0][2]); + + delr[1][0] = x[i3][0] - x[i2][0]; + delr[1][1] = x[i3][1] - x[i2][1]; + delr[1][2] = x[i3][2] - x[i2][2]; + domain->minimum_image(&delr[1][0],&delr[1][1],&delr[1][2]); + + delr[2][0] = x[i4][0] - x[i2][0]; + delr[2][1] = x[i4][1] - x[i2][1]; + delr[2][2] = x[i4][2] - x[i2][2]; + domain->minimum_image(&delr[2][0],&delr[2][1],&delr[2][2]); + + // bond lengths and associated values + + for (i = 0; i < 3; i++) { + rmag2[i] = delr[i][0]*delr[i][0] + delr[i][1]*delr[i][1] + + delr[i][2]*delr[i][2]; + rmag[i] = sqrt(rmag2[i]); + rinvmag[i] = 1.0/rmag[i]; + } + + // angle ABC, CBD, ABD + + costheta[0] = (delr[0][0]*delr[1][0] + delr[0][1]*delr[1][1] + + delr[0][2]*delr[1][2]) / (rmag[0]*rmag[1]); + costheta[1] = (delr[1][0]*delr[2][0] + delr[1][1]*delr[2][1] + + delr[1][2]*delr[2][2]) / (rmag[1]*rmag[2]); + costheta[2] = (delr[0][0]*delr[2][0] + delr[0][1]*delr[2][1] + + delr[0][2]*delr[2][2]) / (rmag[0]*rmag[2]); + + // angle error check + + for (i = 0; i < 3; i++) { + if (costheta[i] == -1.0) { + int me; + MPI_Comm_rank(world,&me); + if (screen) { + fprintf(screen,"Improper problem: %d %d %d %d %d %d\n", + me,update->ntimestep, + atom->tag[i1],atom->tag[i2],atom->tag[i3],atom->tag[i4]); + fprintf(screen," 1st atom: %d %g %g %g\n", + me,x[i1][0],x[i1][1],x[i1][2]); + fprintf(screen," 2nd atom: %d %g %g %g\n", + me,x[i2][0],x[i2][1],x[i2][2]); + fprintf(screen," 3rd atom: %d %g %g %g\n", + me,x[i3][0],x[i3][1],x[i3][2]); + fprintf(screen," 4th atom: %d %g %g %g\n", + me,x[i4][0],x[i4][1],x[i4][2]); + } + } + } + + for (i = 0; i < 3; i++) { + if (costheta[i] > 1.0) costheta[i] = 1.0; + if (costheta[i] < -1.0) costheta[i] = -1.0; + theta[i] = acos(costheta[i]); + cossqtheta[i] = costheta[i]*costheta[i]; + sintheta[i] = sin(theta[i]); + invstheta[i] = 1.0/sintheta[i]; + sinsqtheta[i] = sintheta[i]*sintheta[i]; + } + + // cross & dot products + + cross(delr[0],delr[1],rABxrCB); + cross(delr[2],delr[0],rDBxrAB); + cross(delr[1],delr[2],rCBxrDB); + + dotCBDBAB = dot(rCBxrDB,delr[0]); + dotDBABCB = dot(rDBxrAB,delr[1]); + dotABCBDB = dot(rABxrCB,delr[2]); + + t = rmag[0] * rmag[1] * rmag[2]; + inv3r = 1.0/t; + invs3r[0] = invstheta[1] * inv3r; + invs3r[1] = invstheta[2] * inv3r; + invs3r[2] = invstheta[0] * inv3r; + + // chi ABCD, CBDA, DBAC + // final chi is average of three + + schiABCD = dotCBDBAB * invs3r[0]; + chiABCD = asin(schiABCD); + schiCBDA = dotDBABCB * invs3r[1]; + chiCBDA = asin(schiCBDA); + schiDBAC = dotABCBDB * invs3r[2]; + chiDBAC = asin(schiDBAC); + + chi = (chiABCD + chiCBDA + chiDBAC) / 3.0; + deltachi = chi - chi0[type]; + d2chi = deltachi * deltachi; + + // energy + + if (eflag) energy += rfactor * k0[type] * d2chi; + + // forces + // define d(delr) + // i = bond AB/CB/DB, j = atom A/B/C/D + + for (i = 0; i < 3; i++) + for (j = 0; j < 4; j++) + ddelr[i][j] = 0.0; + + ddelr[0][0] = 1.0; + ddelr[0][1] = -1.0; + ddelr[1][1] = -1.0; + ddelr[1][2] = 1.0; + ddelr[2][1] = -1.0; + ddelr[2][3] = 1.0; + + // compute d(|r|)/dr and d(1/|r|)/dr for each direction, bond and atom + // define d(r) for each r + // i = bond AB/CB/DB, j = atom A/B/C/D, k = X/Y/Z + + for (i = 0; i < 3; i++) + for (j = 0; j < 4; j++) + for (k = 0; k < 3; k++) { + dr[i][j][k] = delr[i][k] * ddelr[i][j] / rmag[i]; + dinvr[i][j][k] = -dr[i][j][k] / rmag2[i]; + } + + // compute d(1 / (|r_AB| * |r_CB| * |r_DB|) / dr + // i = atom A/B/C/D, j = X/Y/Z + + for (i = 0; i < 4; i++) + for (j = 0; j < 3; j++) + dinv3r[i][j] = rinvmag[1] * (rinvmag[2] * dinvr[0][i][j] + + rinvmag[0] * dinvr[2][i][j]) + + rinvmag[2] * rinvmag[0] * dinvr[1][i][j]; + + // compute d(theta)/d(r) for 3 angles + // angleABC + + tt1 = costheta[0] / rmag2[0]; + tt3 = costheta[0] / rmag2[1]; + sc1 = 1.0 / sqrt(1.0 - cossqtheta[0]); + + dthetadr[0][0][0] = sc1 * ((tt1 * delr[0][0]) - + (delr[1][0] * rinvmag[0] * rinvmag[1])); + dthetadr[0][0][1] = sc1 * ((tt1 * delr[0][1]) - + (delr[1][1] * rinvmag[0] * rinvmag[1])); + dthetadr[0][0][2] = sc1 * ((tt1 * delr[0][2]) - + (delr[1][2] * rinvmag[0] * rinvmag[1])); + dthetadr[0][1][0] = -sc1 * ((tt1 * delr[0][0]) - + (delr[1][0] * rinvmag[0] * rinvmag[1]) + + (tt3 * delr[1][0]) - + (delr[0][0] * rinvmag[0] * rinvmag[1])); + dthetadr[0][1][1] = -sc1 * ((tt1 * delr[0][1]) - + (delr[1][1] * rinvmag[0] * rinvmag[1]) + + (tt3 * delr[1][1]) - + (delr[0][1] * rinvmag[0] * rinvmag[1])); + dthetadr[0][1][2] = -sc1 * ((tt1 * delr[0][2]) - + (delr[1][2] * rinvmag[0] * rinvmag[1]) + + (tt3 * delr[1][2]) - + (delr[0][2] * rinvmag[0] * rinvmag[1])); + dthetadr[0][2][0] = sc1 * ((tt3 * delr[1][0]) - + (delr[0][0] * rinvmag[0] * rinvmag[1])); + dthetadr[0][2][1] = sc1 * ((tt3 * delr[1][1]) - + (delr[0][1] * rinvmag[0] * rinvmag[1])); + dthetadr[0][2][2] = sc1 * ((tt3 * delr[1][2]) - + (delr[0][2] * rinvmag[0] * rinvmag[1])); + + // angleCBD + + tt1 = costheta[1] / rmag2[1]; + tt3 = costheta[1] / rmag2[2]; + sc1 = 1.0 / sqrt(1.0 - cossqtheta[1]); + + dthetadr[1][2][0] = sc1 * ((tt1 * delr[1][0]) - + (delr[2][0] * rinvmag[1] * rinvmag[2])); + dthetadr[1][2][1] = sc1 * ((tt1 * delr[1][1]) - + (delr[2][1] * rinvmag[1] * rinvmag[2])); + dthetadr[1][2][2] = sc1 * ((tt1 * delr[1][2]) - + (delr[2][2] * rinvmag[1] * rinvmag[2])); + dthetadr[1][1][0] = -sc1 * ((tt1 * delr[1][0]) - + (delr[2][0] * rinvmag[1] * rinvmag[2]) + + (tt3 * delr[2][0]) - + (delr[1][0] * rinvmag[2] * rinvmag[1])); + dthetadr[1][1][1] = -sc1 * ((tt1 * delr[1][1]) - + (delr[2][1] * rinvmag[1] * rinvmag[2]) + + (tt3 * delr[2][1]) - + (delr[1][1] * rinvmag[2] * rinvmag[1])); + dthetadr[1][1][2] = -sc1 * ((tt1 * delr[1][2]) - + (delr[2][2] * rinvmag[1] * rinvmag[2]) + + (tt3 * delr[2][2]) - + (delr[1][2] * rinvmag[2] * rinvmag[1])); + dthetadr[1][3][0] = sc1 * ((tt3 * delr[2][0]) - + (delr[1][0] * rinvmag[2] * rinvmag[1])); + dthetadr[1][3][1] = sc1 * ((tt3 * delr[2][1]) - + (delr[1][1] * rinvmag[2] * rinvmag[1])); + dthetadr[1][3][2] = sc1 * ((tt3 * delr[2][2]) - + (delr[1][2] * rinvmag[2] * rinvmag[1])); + + // angleABD + + tt1 = costheta[2] / rmag2[0]; + tt3 = costheta[2] / rmag2[2]; + sc1 = 1.0 / sqrt(1.0 - cossqtheta[2]); + + dthetadr[2][0][0] = sc1 * ((tt1 * delr[0][0]) - + (delr[2][0] * rinvmag[0] * rinvmag[2])); + dthetadr[2][0][1] = sc1 * ((tt1 * delr[0][1]) - + (delr[2][1] * rinvmag[0] * rinvmag[2])); + dthetadr[2][0][2] = sc1 * ((tt1 * delr[0][2]) - + (delr[2][2] * rinvmag[0] * rinvmag[2])); + dthetadr[2][1][0] = -sc1 * ((tt1 * delr[0][0]) - + (delr[2][0] * rinvmag[0] * rinvmag[2]) + + (tt3 * delr[2][0]) - + (delr[0][0] * rinvmag[2] * rinvmag[0])); + dthetadr[2][1][1] = -sc1 * ((tt1 * delr[0][1]) - + (delr[2][1] * rinvmag[0] * rinvmag[2]) + + (tt3 * delr[2][1]) - + (delr[0][1] * rinvmag[2] * rinvmag[0])); + dthetadr[2][1][2] = -sc1 * ((tt1 * delr[0][2]) - + (delr[2][2] * rinvmag[0] * rinvmag[2]) + + (tt3 * delr[2][2]) - + (delr[0][2] * rinvmag[2] * rinvmag[0])); + dthetadr[2][3][0] = sc1 * ((tt3 * delr[2][0]) - + (delr[0][0] * rinvmag[2] * rinvmag[0])); + dthetadr[2][3][1] = sc1 * ((tt3 * delr[2][1]) - + (delr[0][1] * rinvmag[2] * rinvmag[0])); + dthetadr[2][3][2] = sc1 * ((tt3 * delr[2][2]) - + (delr[0][2] * rinvmag[2] * rinvmag[0])); + + // compute d( 1 / sin(theta))/dr + // i = angle, j = atom, k = direction + + for (i = 0; i < 3; i++) { + cossin2 = -costheta[i] / sinsqtheta[i]; + for (j = 0; j < 4; j++) + for (k = 0; k < 3; k++) + dinvsth[i][j][k] = cossin2 * dthetadr[i][j][k]; + } + + // compute d(1 / sin(theta) * |r_AB| * |r_CB| * |r_DB|)/dr + // i = angle, j = atom + + for (i = 0; i < 4; i++) + for (j = 0; j < 3; j++) { + dinvs3r[0][i][j] = (invstheta[1] * dinv3r[i][j]) + + (inv3r * dinvsth[1][i][j]); + dinvs3r[1][i][j] = (invstheta[2] * dinv3r[i][j]) + + (inv3r * dinvsth[2][i][j]); + dinvs3r[2][i][j] = (invstheta[0] * dinv3r[i][j]) + + (inv3r * dinvsth[0][i][j]); + } + + // drCB(i,j,k), etc + // i = vector X'/Y'/Z', j = atom A/B/C/D, k = direction X/Y/Z + + for (i = 0; i < 3; i++) { + drCB[i][1][i] = -1.0; + drAB[i][1][i] = -1.0; + drDB[i][1][i] = -1.0; + drDB[i][3][i] = 1.0; + drCB[i][2][i] = 1.0; + drAB[i][0][i] = 1.0; + } + + // d((r_CB x r_DB) dot r_AB) + // r_CB x d(r_DB) + // d(r_CB) x r_DB + // (r_CB x d(r_DB)) + (d(r_CB) x r_DB) + // (r_CB x d(r_DB)) + (d(r_CB) x r_DB) dot r_AB + // d(r_AB) dot (r_CB x r_DB) + + for (i = 0; i < 3; i++) + for (j = 0; j < 4; j++) { + cross(delr[1],drDB[i][j],rCBxdrDB); + cross(drCB[i][j],delr[2],drCBxrDB); + for (k = 0; k < 3; k++) dd[k] = rCBxdrDB[k] + drCBxrDB[k]; + dot1 = dot(dd,delr[0]); + dot2 = dot(rCBxrDB,drAB[i][j]); + fdot[0][j][i] = dot1 + dot2; + } + + // d((r_DB x r_AB) dot r_CB) + // r_DB x d(r_AB) + // d(r_DB) x r_AB + // (r_DB x d(r_AB)) + (d(r_DB) x r_AB) + // (r_DB x d(r_AB)) + (d(r_DB) x r_AB) dot r_CB + // d(r_CB) dot (r_DB x r_AB) + + for (i = 0; i < 3; i++) + for (j = 0; j < 4; j++) { + cross(delr[2],drAB[i][j],rDBxdrAB); + cross(drDB[i][j],delr[0],drDBxrAB); + for (k = 0; k < 3; k++) dd[k] = rDBxdrAB[k] + drDBxrAB[k]; + dot1 = dot(dd,delr[1]); + dot2 = dot(rDBxrAB,drCB[i][j]); + fdot[1][j][i] = dot1 + dot2; + } + + // d((r_AB x r_CB) dot r_DB) + // r_AB x d(r_CB) + // d(r_AB) x r_CB + // (r_AB x d(r_CB)) + (d(r_AB) x r_CB) + // (r_AB x d(r_CB)) + (d(r_AB) x r_CB) dot r_DB + // d(r_DB) dot (r_AB x r_CB) + + for (i = 0; i < 3; i++) + for (j = 0; j < 4; j++) { + cross(delr[0],drCB[i][j],rABxdrCB); + cross(drAB[i][j],delr[1],drABxrCB); + for (k = 0; k < 3; k++) dd[k] = rABxdrCB[k] + drABxrCB[k]; + dot1 = dot(dd,delr[2]); + dot2 = dot(rABxrCB,drDB[i][j]); + fdot[2][j][i] = dot1 + dot2; + } + + // force on each atom + + for (i = 0; i < 4; i++) + for (j = 0; j < 3; j++) { + f2[0][i][j] = (fdot[0][i][j] * invs3r[0]) + + (dinvs3r[0][i][j] * dotCBDBAB); + dchi[0][i][j] = f2[0][i][j] / cos(chiABCD); + f2[1][i][j] = (fdot[1][i][j] * invs3r[1]) + + (dinvs3r[1][i][j] * dotDBABCB); + dchi[1][i][j] = f2[1][i][j] / cos(chiCBDA); + f2[2][i][j] = (fdot[2][i][j] * invs3r[2]) + + (dinvs3r[2][i][j] * dotABCBDB); + dchi[2][i][j] = f2[2][i][j] / cos(chiDBAC); + dtotalchi[i][j] = (dchi[0][i][j]+dchi[1][i][j]+dchi[2][i][j]) / 3.0; + } + + for (i = 0; i < 4; i++) + for (j = 0; j < 3; j++) + fabcd[i][j] = -2.0*k0[type] * deltachi*dtotalchi[i][j]; + + // apply force to each of 4 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] += fabcd[0][0]; + f[i1][1] += fabcd[0][1]; + f[i1][2] += fabcd[0][2]; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += fabcd[1][0]; + f[i2][1] += fabcd[1][1]; + f[i2][2] += fabcd[1][2]; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] += fabcd[2][0]; + f[i3][1] += fabcd[2][1]; + f[i3][2] += fabcd[2][2]; + } + + if (newton_bond || i4 < nlocal) { + f[i4][0] += fabcd[3][0]; + f[i4][1] += fabcd[3][1]; + f[i4][2] += fabcd[3][2]; + } + + // virial contribution + + if (vflag) { + virial[0] += rfactor * (delr[0][0]*fabcd[0][0] + + delr[1][0]*fabcd[2][0] + delr[2][0]*fabcd[3][0]); + virial[1] += rfactor * (delr[0][1]*fabcd[0][1] + + delr[1][1]*fabcd[2][1] + delr[2][1]*fabcd[3][1]); + virial[2] += rfactor * (delr[0][2]*fabcd[0][2] + + delr[1][2]*fabcd[2][2] + delr[2][2]*fabcd[3][2]); + virial[3] += rfactor * (delr[0][0]*fabcd[0][1] + + delr[1][0]*fabcd[2][1] + delr[2][0]*fabcd[3][1]); + virial[4] += rfactor * (delr[0][0]*fabcd[0][2] + + delr[1][0]*fabcd[2][2] + delr[2][0]*fabcd[3][2]); + virial[5] += rfactor * (delr[0][1]*fabcd[0][2] + + delr[1][1]*fabcd[2][2] + delr[2][1]*fabcd[3][2]); + } + } + + // compute angle-angle interactions + + angleangle(eflag,vflag); +} + +/* ---------------------------------------------------------------------- */ + +void ImproperClass2::allocate() +{ + allocated = 1; + int n = atom->nimpropertypes; + + k0 = (double *) memory->smalloc((n+1)*sizeof(double),"improper:k0"); + chi0 = (double *) memory->smalloc((n+1)*sizeof(double),"improper:chi0"); + + aa_k1 = (double *) memory->smalloc((n+1)*sizeof(double),"improper:aa_k1"); + aa_k2 = (double *) memory->smalloc((n+1)*sizeof(double),"improper:aa_k2"); + aa_k3 = (double *) memory->smalloc((n+1)*sizeof(double),"improper:aa_k3"); + aa_theta0_1 = (double *) + memory->smalloc((n+1)*sizeof(double),"improper:aa_theta0_1"); + aa_theta0_2 = (double *) + memory->smalloc((n+1)*sizeof(double),"improper:aa_theta0_2"); + aa_theta0_3 = (double *) + memory->smalloc((n+1)*sizeof(double),"improper:aa_theta0_3"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"improper:setflag"); + setflag_i = (int *) + memory->smalloc((n+1)*sizeof(int),"improper:setflag_i"); + setflag_aa = (int *) + memory->smalloc((n+1)*sizeof(int),"improper:setflag_aa"); + for (int i = 1; i <= n; i++) + setflag[i] = setflag_i[i] = setflag_aa[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more types + which = 0 -> improper coeffs + which = 1 -> AngleAngle coeffs +------------------------------------------------------------------------- */ + +void ImproperClass2::coeff(int which, int narg, char **arg) +{ + if (which < 0 || which > 1) + error->all("Invalid coeffs for this improper style"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nimpropertypes,ilo,ihi); + + int count = 0; + + if (which == 0) { + if (narg != 3) error->all("Incorrect args for improper coefficients"); + + double k0_one = atof(arg[1]); + double chi0_one = atof(arg[2]); + + // convert chi0 from degrees to radians + + for (int i = ilo; i <= ihi; i++) { + k0[i] = k0_one; + chi0[i] = chi0_one/180.0 * PI; + setflag_i[i] = 1; + count++; + } + } + + if (which == 1) { + if (narg != 7) error->all("Incorrect args for improper coefficients"); + + double k1_one = atof(arg[1]); + double k2_one = atof(arg[2]); + double k3_one = atof(arg[3]); + double theta0_1_one = atof(arg[4]); + double theta0_2_one = atof(arg[5]); + double theta0_3_one = atof(arg[6]); + + // convert theta0's from degrees to radians + + for (int i = ilo; i <= ihi; i++) { + aa_k1[i] = k1_one; + aa_k2[i] = k2_one; + aa_k3[i] = k3_one; + aa_theta0_1[i] = theta0_1_one/180.0 * PI; + aa_theta0_2[i] = theta0_2_one/180.0 * PI; + aa_theta0_3[i] = theta0_3_one/180.0 * PI; + setflag_aa[i] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for improper coefficients"); + + for (int i = ilo; i <= ihi; i++) + if (setflag_i[i] == 1 && setflag_aa[i] == 1) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void ImproperClass2::write_restart(FILE *fp) +{ + fwrite(&k0[1],sizeof(double),atom->nimpropertypes,fp); + fwrite(&chi0[1],sizeof(double),atom->nimpropertypes,fp); + + fwrite(&aa_k1[1],sizeof(double),atom->nimpropertypes,fp); + fwrite(&aa_k2[1],sizeof(double),atom->nimpropertypes,fp); + fwrite(&aa_k3[1],sizeof(double),atom->nimpropertypes,fp); + fwrite(&aa_theta0_1[1],sizeof(double),atom->nimpropertypes,fp); + fwrite(&aa_theta0_2[1],sizeof(double),atom->nimpropertypes,fp); + fwrite(&aa_theta0_3[1],sizeof(double),atom->nimpropertypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void ImproperClass2::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k0[1],sizeof(double),atom->nimpropertypes,fp); + fread(&chi0[1],sizeof(double),atom->nimpropertypes,fp); + + fread(&aa_k1[1],sizeof(double),atom->nimpropertypes,fp); + fread(&aa_k2[1],sizeof(double),atom->nimpropertypes,fp); + fread(&aa_k3[1],sizeof(double),atom->nimpropertypes,fp); + fread(&aa_theta0_1[1],sizeof(double),atom->nimpropertypes,fp); + fread(&aa_theta0_2[1],sizeof(double),atom->nimpropertypes,fp); + fread(&aa_theta0_3[1],sizeof(double),atom->nimpropertypes,fp); + } + MPI_Bcast(&k0[1],atom->nimpropertypes,MPI_DOUBLE,0,world); + MPI_Bcast(&chi0[1],atom->nimpropertypes,MPI_DOUBLE,0,world); + + MPI_Bcast(&aa_k1[1],atom->nimpropertypes,MPI_DOUBLE,0,world); + MPI_Bcast(&aa_k2[1],atom->nimpropertypes,MPI_DOUBLE,0,world); + MPI_Bcast(&aa_k3[1],atom->nimpropertypes,MPI_DOUBLE,0,world); + MPI_Bcast(&aa_theta0_1[1],atom->nimpropertypes,MPI_DOUBLE,0,world); + MPI_Bcast(&aa_theta0_2[1],atom->nimpropertypes,MPI_DOUBLE,0,world); + MPI_Bcast(&aa_theta0_3[1],atom->nimpropertypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nimpropertypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- + angle-angle interactions within improper +------------------------------------------------------------------------- */ + +void ImproperClass2::angleangle(int eflag, int vflag) +{ + int i1,i2,i3,i4,i,j,k,n,type,factor; + double rfactor; + double delxAB,delyAB,delzAB,rABmag2,rAB; + double delxBC,delyBC,delzBC,rBCmag2,rBC; + double delxBD,delyBD,delzBD,rBDmag2,rBD; + double costhABC,thetaABC,costhABD; + double thetaABD,costhCBD,thetaCBD,dthABC,dthCBD,dthABD; + double sc1,t1,t3,r12; + double dthetadr[3][4][3],fabcd[4][3]; + + double **x = atom->x; + double **f = atom->f; + int **improperlist = neighbor->improperlist; + int nimproperlist = neighbor->nimproperlist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nimproperlist; n++) { + + i1 = improperlist[n][0]; + i2 = improperlist[n][1]; + i3 = improperlist[n][2]; + i4 = improperlist[n][3]; + type = improperlist[n][4]; + + if (newton_bond) factor = 4; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + if (i4 < nlocal) factor++; + } + rfactor = 0.25 * factor; + + // difference vectors + + delxAB = x[i1][0] - x[i2][0]; + delyAB = x[i1][1] - x[i2][1]; + delzAB = x[i1][2] - x[i2][2]; + domain->minimum_image(&delxAB,&delyAB,&delzAB); + + delxBC = x[i3][0] - x[i2][0]; + delyBC = x[i3][1] - x[i2][1]; + delzBC = x[i3][2] - x[i2][2]; + domain->minimum_image(&delxBC,&delyBC,&delzBC); + + delxBD = x[i4][0] - x[i2][0]; + delyBD = x[i4][1] - x[i2][1]; + delzBD = x[i4][2] - x[i2][2]; + domain->minimum_image(&delxBD,&delyBD,&delzBD); + + // bond lengths + + rABmag2 = delxAB*delxAB + delyAB*delyAB + delzAB*delzAB; + rAB = sqrt(rABmag2); + rBCmag2 = delxBC*delxBC + delyBC*delyBC + delzBC*delzBC; + rBC = sqrt(rBCmag2); + rBDmag2 = delxBD*delxBD + delyBD*delyBD + delzBD*delzBD; + rBD = sqrt(rBDmag2); + + // angle ABC, ABD, CBD + + costhABC = (delxAB*delxBC + delyAB*delyBC + delzAB*delzBC) / (rAB * rBC); + if (costhABC > 1.0) costhABC = 1.0; + if (costhABC < -1.0) costhABC = -1.0; + thetaABC = acos(costhABC); + + costhABD = (delxAB*delxBD + delyAB*delyBD + delzAB*delzBD) / (rAB * rBD); + if (costhABD > 1.0) costhABD = 1.0; + if (costhABD < -1.0) costhABD = -1.0; + thetaABD = acos(costhABD); + + costhCBD = (delxBC*delxBD + delyBC*delyBD + delzBC*delzBD) /(rBC * rBD); + if (costhCBD > 1.0) costhCBD = 1.0; + if (costhCBD < -1.0) costhCBD = -1.0; + thetaCBD = acos(costhCBD); + + dthABC = thetaABC - aa_theta0_1[type]; + dthCBD = thetaCBD - aa_theta0_2[type]; + dthABD = thetaABD - aa_theta0_3[type]; + + // energy + + if (eflag) energy += rfactor * ((aa_k2[type] * dthABC * dthABD) + + (aa_k1[type] * dthABC * dthCBD) + + (aa_k3[type] * dthABD * dthCBD)); + + // d(theta)/d(r) array + // angle i, atom j, coordinate k + + for (i = 0; i < 3; i++) + for (j = 0; j < 4; j++) + for (k = 0; k < 3; k++) + dthetadr[i][j][k] = 0.0; + + // angle ABC + + sc1 = sqrt(1.0/(1.0 - costhABC*costhABC)); + t1 = costhABC / rABmag2; + t3 = costhABC / rBCmag2; + r12 = 1.0 / (rAB * rBC); + + dthetadr[0][0][0] = sc1 * ((t1 * delxAB) - (delxBC * r12)); + dthetadr[0][0][1] = sc1 * ((t1 * delyAB) - (delyBC * r12)); + dthetadr[0][0][2] = sc1 * ((t1 * delzAB) - (delzBC * r12)); + dthetadr[0][1][0] = sc1 * ((-t1 * delxAB) + (delxBC * r12) + + (-t3 * delxBC) + (delxAB * r12)); + dthetadr[0][1][1] = sc1 * ((-t1 * delyAB) + (delyBC * r12) + + (-t3 * delyBC) + (delyAB * r12)); + dthetadr[0][1][2] = sc1 * ((-t1 * delzAB) + (delzBC * r12) + + (-t3 * delzBC) + (delzAB * r12)); + dthetadr[0][2][0] = sc1 * ((t3 * delxBC) - (delxAB * r12)); + dthetadr[0][2][1] = sc1 * ((t3 * delyBC) - (delyAB * r12)); + dthetadr[0][2][2] = sc1 * ((t3 * delzBC) - (delzAB * r12)); + + // angle CBD + + sc1 = sqrt(1.0/(1.0 - costhCBD*costhCBD)); + t1 = costhCBD / rBCmag2; + t3 = costhCBD / rBDmag2; + r12 = 1.0 / (rBC * rBD); + + dthetadr[1][2][0] = sc1 * ((t1 * delxBC) - (delxBD * r12)); + dthetadr[1][2][1] = sc1 * ((t1 * delyBC) - (delyBD * r12)); + dthetadr[1][2][2] = sc1 * ((t1 * delzBC) - (delzBD * r12)); + dthetadr[1][1][0] = sc1 * ((-t1 * delxBC) + (delxBD * r12) + + (-t3 * delxBD) + (delxBC * r12)); + dthetadr[1][1][1] = sc1 * ((-t1 * delyBC) + (delyBD * r12) + + (-t3 * delyBD) + (delyBC * r12)); + dthetadr[1][1][2] = sc1 * ((-t1 * delzBC) + (delzBD * r12) + + (-t3 * delzBD) + (delzBC * r12)); + dthetadr[1][3][0] = sc1 * ((t3 * delxBD) - (delxBC * r12)); + dthetadr[1][3][1] = sc1 * ((t3 * delyBD) - (delyBC * r12)); + dthetadr[1][3][2] = sc1 * ((t3 * delzBD) - (delzBC * r12)); + + // angle ABD + + sc1 = sqrt(1.0/(1.0 - costhABD*costhABD)); + t1 = costhABD / rABmag2; + t3 = costhABD / rBDmag2; + r12 = 1.0 / (rAB * rBD); + + dthetadr[2][0][0] = sc1 * ((t1 * delxAB) - (delxBD * r12)); + dthetadr[2][0][1] = sc1 * ((t1 * delyAB) - (delyBD * r12)); + dthetadr[2][0][2] = sc1 * ((t1 * delzAB) - (delzBD * r12)); + dthetadr[2][1][0] = sc1 * ((-t1 * delxAB) + (delxBD * r12) + + (-t3 * delxBD) + (delxAB * r12)); + dthetadr[2][1][1] = sc1 * ((-t1 * delyAB) + (delyBD * r12) + + (-t3 * delyBD) + (delyAB * r12)); + dthetadr[2][1][2] = sc1 * ((-t1 * delzAB) + (delzBD * r12) + + (-t3 * delzBD) + (delzAB * r12)); + dthetadr[2][3][0] = sc1 * ((t3 * delxBD) - (delxAB * r12)); + dthetadr[2][3][1] = sc1 * ((t3 * delyBD) - (delyAB * r12)); + dthetadr[2][3][2] = sc1 * ((t3 * delzBD) - (delzAB * r12)); + + // angleangle forces + + for (i = 0; i < 4; i++) + for (j = 0; j < 3; j++) + fabcd[i][j] = - + ((aa_k1[type] * + (dthABC*dthetadr[1][i][j] + dthCBD*dthetadr[0][i][j])) + + (aa_k2[type] * + (dthABC*dthetadr[2][i][j] + dthABD*dthetadr[0][i][j])) + + (aa_k3[type] * + (dthABD*dthetadr[1][i][j] + dthCBD*dthetadr[2][i][j]))); + + // apply force to each of 4 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] += fabcd[0][0]; + f[i1][1] += fabcd[0][1]; + f[i1][2] += fabcd[0][2]; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += fabcd[1][0]; + f[i2][1] += fabcd[1][1]; + f[i2][2] += fabcd[1][2]; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] += fabcd[2][0]; + f[i3][1] += fabcd[2][1]; + f[i3][2] += fabcd[2][2]; + } + + if (newton_bond || i4 < nlocal) { + f[i4][0] += fabcd[3][0]; + f[i4][1] += fabcd[3][1]; + f[i4][2] += fabcd[3][2]; + } + + // virial contribution + + if (vflag) { + virial[0] += rfactor * (delxAB*fabcd[0][0] + + delxBC*fabcd[2][0] + delxBD*fabcd[3][0]); + virial[1] += rfactor * (delyAB*fabcd[0][1] + + delyBC*fabcd[2][1] + delyBD*fabcd[3][1]); + virial[2] += rfactor * (delzAB*fabcd[0][2] + + delzBC*fabcd[2][2] + delzBD*fabcd[3][2]); + virial[3] += rfactor * (delxAB*fabcd[0][1] + + delxBC*fabcd[2][1] + delxBD*fabcd[3][1]); + virial[4] += rfactor * (delxAB*fabcd[0][2] + + delxBC*fabcd[2][2] + delxBD*fabcd[3][2]); + virial[5] += rfactor * (delyAB*fabcd[0][2] + + delyBC*fabcd[2][2] + delyBD*fabcd[3][2]); + } + } +} + +/* ---------------------------------------------------------------------- + cross product: c = a x b +------------------------------------------------------------------------- */ + +void ImproperClass2::cross(double *a, double *b, double *c) +{ + c[0] = a[1]*b[2] - a[2]*b[1]; + c[1] = a[2]*b[0] - a[0]*b[2]; + c[2] = a[0]*b[1] - a[1]*b[0]; +} + +/* ---------------------------------------------------------------------- + dot product of a dot b +------------------------------------------------------------------------- */ + +double ImproperClass2::dot(double *a, double *b) +{ + return (a[0]*b[0] + a[1]*b[1] + a[2]*b[2]); +} diff --git a/src/CLASS2/improper_class2.h b/src/CLASS2/improper_class2.h new file mode 100644 index 0000000000000000000000000000000000000000..fa7521aef4e187bebc07dc7111229a3f280881f5 --- /dev/null +++ b/src/CLASS2/improper_class2.h @@ -0,0 +1,41 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef IMPROPER_CLASS2_H +#define IMPROPER_CLASS2_H + +#include "stdio.h" +#include "improper.h" + +class ImproperClass2 : public Improper { + public: + ImproperClass2(); + ~ImproperClass2(); + void compute(int, int); + void coeff(int, int, char **); + void write_restart(FILE *); + void read_restart(FILE *); + + private: + double *k0,*chi0; + double *aa_k1,*aa_k2,*aa_k3,*aa_theta0_1,*aa_theta0_2,*aa_theta0_3; + int *setflag_i,*setflag_aa; + double PI; + + void allocate(); + void angleangle(int, int); + void cross(double *, double *, double *); + double dot(double *, double *); +}; + +#endif diff --git a/src/CLASS2/pair_lj_class2.cpp b/src/CLASS2/pair_lj_class2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4564ec2a8353ce0abda7230fe28083c032bdc94d --- /dev/null +++ b/src/CLASS2/pair_lj_class2.cpp @@ -0,0 +1,383 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "pair_lj_class2.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +PairLJClass2::~PairLJClass2() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(cut); + memory->destroy_2d_double_array(epsilon); + memory->destroy_2d_double_array(sigma); + memory->destroy_2d_double_array(lj1); + memory->destroy_2d_double_array(lj2); + memory->destroy_2d_double_array(lj3); + memory->destroy_2d_double_array(lj4); + memory->destroy_2d_double_array(offset); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairLJClass2::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,rinv,r2inv,r3inv,r6inv,forcelj,fforce,factor_lj,philj; + int *neighs; + double **f; + + eng_vdwl = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_lj = 1.0; + else { + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r2inv = 1.0/rsq; + rinv = sqrt(r2inv); + r3inv = r2inv*rinv; + r6inv = r3inv*r3inv; + forcelj = r6inv * (lj1[itype][jtype]*r3inv - lj2[itype][jtype]); + fforce = factor_lj*forcelj*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) { + philj = r6inv*(lj3[itype][jtype]*r3inv-lj4[itype][jtype]) - + offset[itype][jtype]; + if (newton_pair || j < nlocal) eng_vdwl += factor_lj*philj; + else eng_vdwl += 0.5*factor_lj*philj; + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairLJClass2::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"); + + cut = memory->create_2d_double_array(n+1,n+1,"pair:cut"); + epsilon = memory->create_2d_double_array(n+1,n+1,"pair:epsilon"); + sigma = memory->create_2d_double_array(n+1,n+1,"pair:sigma"); + lj1 = memory->create_2d_double_array(n+1,n+1,"pair:lj1"); + lj2 = memory->create_2d_double_array(n+1,n+1,"pair:lj2"); + lj3 = memory->create_2d_double_array(n+1,n+1,"pair:lj3"); + lj4 = memory->create_2d_double_array(n+1,n+1,"pair:lj4"); + offset = memory->create_2d_double_array(n+1,n+1,"pair:offset"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairLJClass2::settings(int narg, char **arg) +{ + if (narg != 1) error->all("Illegal pair_style command"); + + cut_global = atof(arg[0]); + + // 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 PairLJClass2::coeff(int narg, char **arg) +{ + if (narg < 4 || narg > 5) 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); + + double epsilon_one = atof(arg[2]); + double sigma_one = atof(arg[3]); + + double cut_one = cut_global; + if (narg == 5) cut_one = atof(arg[4]); + + 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; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairLJClass2::init_one(int i, int j) +{ + // always mix epsilon,sigma via sixthpower rules + // mix distance via user-defined rule + + if (setflag[i][j] == 0) { + epsilon[i][j] = 2.0 * sqrt(epsilon[i][i]*epsilon[j][j]) * + pow(sigma[i][i],3.0) * pow(sigma[j][j],3.0) / + (pow(sigma[i][i],6.0) + pow(sigma[j][j],6.0)); + sigma[i][j] = + pow((0.5 * (pow(sigma[i][i],6.0) + pow(sigma[j][j],6.0))),1.0/6.0); + cut[i][j] = mix_distance(cut[i][i],cut[j][j]); + } + + lj1[i][j] = 18.0 * epsilon[i][j] * pow(sigma[i][j],9.0); + lj2[i][j] = 18.0 * epsilon[i][j] * pow(sigma[i][j],6.0); + lj3[i][j] = 2.0 * epsilon[i][j] * pow(sigma[i][j],9.0); + lj4[i][j] = 3.0 * epsilon[i][j] * pow(sigma[i][j],6.0); + + if (offset_flag) { + double ratio = sigma[i][j] / cut[i][j]; + offset[i][j] = epsilon[i][j] * (2.0*pow(ratio,9.0) - 3.0*pow(ratio,6.0)); + } else offset[i][j] = 0.0; + + 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]; + + // compute I,J contribution to long-range tail correction + // count total # of atoms of type I and J via Allreduce + + if (tail_flag) { + int *type = atom->type; + int nlocal = atom->nlocal; + + double count[2],all[2]; + count[0] = count[1] = 0.0; + for (int k = 0; k < nlocal; k++) { + if (type[k] == i) count[0] += 1.0; + if (type[k] == j) count[1] += 1.0; + } + MPI_Allreduce(count,all,2,MPI_DOUBLE,MPI_SUM,world); + + double PI = 4.0*atan(1.0); + double sig3 = sigma[i][j]*sigma[i][j]*sigma[i][j]; + double sig6 = sig3*sig3; + double rc3 = cut[i][j]*cut[i][j]*cut[i][j]; + double rc6 = rc3*rc3; + etail_ij = 2.0*PI*all[0]*all[1]*epsilon[i][j] * + sig6 * (sig3 - 3.0*rc3) / (3.0*rc6); + ptail_ij = 2.0*PI*all[0]*all[1]*epsilon[i][j] * + sig6 * (sig3 - 2.0*rc3) / rc6; + } + + return cut[i][j]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJClass2::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + 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 PairLJClass2::read_restart(FILE *fp) +{ + read_restart_settings(fp); + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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 PairLJClass2::write_restart_settings(FILE *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 PairLJClass2::read_restart_settings(FILE *fp) +{ + int me = comm->me; + if (me == 0) { + fread(&cut_global,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + 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); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJClass2::single(int i, int j, int itype, int jtype, double rsq, + double factor_coul, double factor_lj, int eflag, + One &one) +{ + double r2inv,rinv,r3inv,r6inv,forcelj,philj; + + r2inv = 1.0/rsq; + rinv = sqrt(r2inv); + r3inv = r2inv*rinv; + r6inv = r3inv*r3inv; + forcelj = r6inv * (lj1[itype][jtype]*r3inv - lj2[itype][jtype]); + one.fforce = factor_lj*forcelj*r2inv; + + if (eflag) { + philj = r6inv*(lj3[itype][jtype]*r3inv-lj4[itype][jtype]) - + offset[itype][jtype]; + one.eng_vdwl = factor_lj*philj; + one.eng_coul = 0.0; + } +} diff --git a/src/CLASS2/pair_lj_class2.h b/src/CLASS2/pair_lj_class2.h new file mode 100644 index 0000000000000000000000000000000000000000..00db7754eedfa251451b90af608fc59a076a708a --- /dev/null +++ b/src/CLASS2/pair_lj_class2.h @@ -0,0 +1,42 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_LJ_CLASS2_H +#define PAIR_LJ_CLASS2_H + +#include "pair.h" + +class PairLJClass2 : public Pair { + public: + PairLJClass2() {} + ~PairLJClass2(); + void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + void single(int, int, int, int, double, double, double, int, One &); + + private: + double cut_global; + double **cut; + double **epsilon,**sigma; + double **lj1,**lj2,**lj3,**lj4,**offset; + + void allocate(); +}; + +#endif diff --git a/src/CLASS2/pair_lj_class2_coul_cut.cpp b/src/CLASS2/pair_lj_class2_coul_cut.cpp new file mode 100644 index 0000000000000000000000000000000000000000..78e1d649ae00fc3dd4e21d691fe40568db801622 --- /dev/null +++ b/src/CLASS2/pair_lj_class2_coul_cut.cpp @@ -0,0 +1,452 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "pair_lj_class2_coul_cut.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +PairLJClass2CoulCut::~PairLJClass2CoulCut() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(cut_lj); + memory->destroy_2d_double_array(cut_ljsq); + memory->destroy_2d_double_array(cut_coul); + memory->destroy_2d_double_array(cut_coulsq); + memory->destroy_2d_double_array(epsilon); + memory->destroy_2d_double_array(sigma); + memory->destroy_2d_double_array(lj1); + memory->destroy_2d_double_array(lj2); + memory->destroy_2d_double_array(lj3); + memory->destroy_2d_double_array(lj4); + memory->destroy_2d_double_array(offset); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairLJClass2CoulCut::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,rinv,r2inv,r3inv,r6inv,forcecoul,forcelj,fforce; + double factor_coul,factor_lj,factor,phicoul,philj; + int *neighs; + double **f; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r2inv = 1.0/rsq; + + if (rsq < cut_coulsq[itype][jtype]) + forcecoul = qqrd2e * qtmp*q[j]*sqrt(r2inv); + else forcecoul = 0.0; + + if (rsq < cut_ljsq[itype][jtype]) { + rinv = sqrt(r2inv); + r3inv = r2inv*rinv; + r6inv = r3inv*r3inv; + forcelj = r6inv * (lj1[itype][jtype]*r3inv - lj2[itype][jtype]); + } else forcelj = 0.0; + + fforce = (factor_coul*forcecoul + factor_lj*forcelj) * 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 (newton_pair || j < nlocal) factor = 1.0; + else factor = 0.5; + if (rsq < cut_coulsq[itype][jtype]) { + phicoul = qqrd2e * qtmp*q[j]*sqrt(r2inv); + eng_coul += factor*factor_coul*phicoul; + } + if (rsq < cut_ljsq[itype][jtype]) { + philj = r6inv*(lj3[itype][jtype]*r3inv-lj4[itype][jtype]) - + offset[itype][jtype]; + eng_vdwl += factor*factor_lj*philj; + } + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairLJClass2CoulCut::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"); + + cut_lj = memory->create_2d_double_array(n+1,n+1,"pair:cut_lj"); + cut_ljsq = memory->create_2d_double_array(n+1,n+1,"pair:cut_ljsq"); + cut_coul = memory->create_2d_double_array(n+1,n+1,"pair:cut_coul"); + cut_coulsq = memory->create_2d_double_array(n+1,n+1,"pair:cut_coulsq"); + epsilon = memory->create_2d_double_array(n+1,n+1,"pair:epsilon"); + sigma = memory->create_2d_double_array(n+1,n+1,"pair:sigma"); + lj1 = memory->create_2d_double_array(n+1,n+1,"pair:lj1"); + lj2 = memory->create_2d_double_array(n+1,n+1,"pair:lj2"); + lj3 = memory->create_2d_double_array(n+1,n+1,"pair:lj3"); + lj4 = memory->create_2d_double_array(n+1,n+1,"pair:lj4"); + offset = memory->create_2d_double_array(n+1,n+1,"pair:offset"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairLJClass2CoulCut::settings(int narg, char **arg) +{ + if (narg < 1 || narg > 2) error->all("Illegal pair_style command"); + + cut_lj_global = atof(arg[0]); + if (narg == 1) cut_coul_global = cut_lj_global; + else cut_coul_global = atof(arg[1]); + + // 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_lj[i][j] = cut_lj_global; + cut_coul[i][j] = cut_coul_global; + } + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairLJClass2CoulCut::coeff(int narg, char **arg) +{ + if (narg < 4 || narg > 6) 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); + + double epsilon_one = atof(arg[2]); + double sigma_one = atof(arg[3]); + + double cut_lj_one = cut_lj_global; + double cut_coul_one = cut_coul_global; + if (narg >= 5) cut_coul_one = cut_lj_one = atof(arg[4]); + if (narg == 6) cut_coul_one = atof(arg[5]); + + 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_lj[i][j] = cut_lj_one; + cut_coul[i][j] = cut_coul_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairLJClass2CoulCut::init_one(int i, int j) +{ + // always mix epsilon,sigma via sixthpower rules + // mix distance via user-defined rule + + if (setflag[i][j] == 0) { + epsilon[i][j] = 2.0 * sqrt(epsilon[i][i]*epsilon[j][j]) * + pow(sigma[i][i],3.0) * pow(sigma[j][j],3.0) / + (pow(sigma[i][i],6.0) + pow(sigma[j][j],6.0)); + sigma[i][j] = + pow((0.5 * (pow(sigma[i][i],6.0) + pow(sigma[j][j],6.0))),1.0/6.0); + cut_lj[i][j] = mix_distance(cut_lj[i][i],cut_lj[j][j]); + cut_coul[i][j] = mix_distance(cut_coul[i][i],cut_coul[j][j]); + } + + double cut = MAX(cut_lj[i][j],cut_coul[i][j]); + cut_ljsq[i][j] = cut_lj[i][j] * cut_lj[i][j]; + cut_coulsq[i][j] = cut_coul[i][j] * cut_coul[i][j]; + + lj1[i][j] = 18.0 * epsilon[i][j] * pow(sigma[i][j],9.0); + lj2[i][j] = 18.0 * epsilon[i][j] * pow(sigma[i][j],6.0); + lj3[i][j] = 2.0 * epsilon[i][j] * pow(sigma[i][j],9.0); + lj4[i][j] = 3.0 * epsilon[i][j] * pow(sigma[i][j],6.0); + + if (offset_flag) { + double ratio = sigma[i][j] / cut_lj[i][j]; + offset[i][j] = epsilon[i][j] * (2.0*pow(ratio,9.0) - 3.0*pow(ratio,6.0)); + } else offset[i][j] = 0.0; + + cut_ljsq[j][i] = cut_ljsq[i][j]; + cut_coulsq[j][i] = cut_coulsq[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]; + + // compute I,J contribution to long-range tail correction + // count total # of atoms of type I and J via Allreduce + + if (tail_flag) { + int *type = atom->type; + int nlocal = atom->nlocal; + + double count[2],all[2]; + count[0] = count[1] = 0.0; + for (int k = 0; k < nlocal; k++) { + if (type[k] == i) count[0] += 1.0; + if (type[k] == j) count[1] += 1.0; + } + MPI_Allreduce(count,all,2,MPI_DOUBLE,MPI_SUM,world); + + double PI = 4.0*atan(1.0); + double sig3 = sigma[i][j]*sigma[i][j]*sigma[i][j]; + double sig6 = sig3*sig3; + double rc3 = cut_lj[i][j]*cut_lj[i][j]*cut_lj[i][j]; + double rc6 = rc3*rc3; + etail_ij = 2.0*PI*all[0]*all[1]*epsilon[i][j] * + sig6 * (sig3 - 3.0*rc3) / (3.0*rc6); + ptail_ij = 2.0*PI*all[0]*all[1]*epsilon[i][j] * + sig6 * (sig3 - 2.0*rc3) / rc6; + } + + return cut; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairLJClass2CoulCut::init_style() +{ + // require an atom style with charge defined + + if (atom->charge_allow == 0) + error->all("Must use charged atom style with this pair style"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJClass2CoulCut::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + 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_lj[i][j],sizeof(double),1,fp); + fwrite(&cut_coul[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairLJClass2CoulCut::read_restart(FILE *fp) +{ + read_restart_settings(fp); + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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_lj[i][j],sizeof(double),1,fp); + fread(&cut_coul[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_lj[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_coul[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJClass2CoulCut::write_restart_settings(FILE *fp) +{ + fwrite(&cut_lj_global,sizeof(double),1,fp); + fwrite(&cut_coul_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 PairLJClass2CoulCut::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&cut_lj_global,sizeof(double),1,fp); + fread(&cut_coul_global,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + MPI_Bcast(&cut_lj_global,1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_coul_global,1,MPI_DOUBLE,0,world); + MPI_Bcast(&offset_flag,1,MPI_INT,0,world); + MPI_Bcast(&mix_flag,1,MPI_INT,0,world); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJClass2CoulCut::single(int i, int j, int itype, int jtype, + double rsq, double factor_coul, + double factor_lj, int eflag, One &one) +{ + double r2inv,rinv,r3inv,r6inv,forcecoul,forcelj,phicoul,philj; + + r2inv = 1.0/rsq; + if (rsq < cut_coulsq[itype][jtype]) + forcecoul = force->qqrd2e * atom->q[i]*atom->q[j]*sqrt(r2inv); + else forcecoul = 0.0; + if (rsq < cut_ljsq[itype][jtype]) { + rinv = sqrt(r2inv); + r3inv = r2inv*rinv; + r6inv = r3inv*r3inv; + forcelj = r6inv * (lj1[itype][jtype]*r3inv - lj2[itype][jtype]); + } else forcelj = 0.0; + one.fforce = (factor_coul*forcecoul + factor_lj*forcelj) * r2inv; + + if (eflag) { + if (rsq < cut_coulsq[itype][jtype]) { + phicoul = force->qqrd2e * atom->q[i]*atom->q[j]*sqrt(r2inv); + one.eng_coul = factor_coul*phicoul; + } else one.eng_coul = 0.0; + if (rsq < cut_ljsq[itype][jtype]) { + philj = r6inv*(lj3[itype][jtype]*r3inv-lj4[itype][jtype]) - + offset[itype][jtype]; + one.eng_vdwl = factor_lj*philj; + } else one.eng_vdwl = 0.0; + } +} diff --git a/src/CLASS2/pair_lj_class2_coul_cut.h b/src/CLASS2/pair_lj_class2_coul_cut.h new file mode 100644 index 0000000000000000000000000000000000000000..f7475703a02bc64309d703cde7cc54e3ba0adb8e --- /dev/null +++ b/src/CLASS2/pair_lj_class2_coul_cut.h @@ -0,0 +1,44 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_LJ_CLASS2_COUL_CUT_H +#define PAIR_LJ_CLASS2_COUL_CUT_H + +#include "pair.h" + +class PairLJClass2CoulCut : public Pair { + public: + PairLJClass2CoulCut() {} + ~PairLJClass2CoulCut(); + void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void init_style(); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + void single(int, int, int, int, double, double, double, int, One &); + + private: + double cut_lj_global,cut_coul_global; + double **cut_lj,**cut_ljsq; + double **cut_coul,**cut_coulsq; + double **epsilon,**sigma; + double **lj1,**lj2,**lj3,**lj4,**offset; + + void allocate(); +}; + +#endif diff --git a/src/CLASS2/pair_lj_class2_coul_long.cpp b/src/CLASS2/pair_lj_class2_coul_long.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2e540745a81a6327f132e983bbbd100fe1e54922 --- /dev/null +++ b/src/CLASS2/pair_lj_class2_coul_long.cpp @@ -0,0 +1,476 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "pair_lj_class2_coul_long.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "kspace.h" +#include "update.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#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 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +PairLJClass2CoulLong::~PairLJClass2CoulLong() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(cut_lj); + memory->destroy_2d_double_array(cut_ljsq); + memory->destroy_2d_double_array(epsilon); + memory->destroy_2d_double_array(sigma); + memory->destroy_2d_double_array(lj1); + memory->destroy_2d_double_array(lj2); + memory->destroy_2d_double_array(lj3); + memory->destroy_2d_double_array(lj4); + memory->destroy_2d_double_array(offset); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairLJClass2CoulLong::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r,rinv,r2inv,r3inv,r6inv,forcecoul,forcelj,fforce; + double grij,expm2,prefactor,t,erfc; + double factor_coul,factor_lj,factor,phicoul,philj; + int *neighs; + double **f; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r2inv = 1.0/rsq; + + if (rsq < cut_coulsq) { + 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 = qqrd2e * qtmp*q[j]/r; + forcecoul = prefactor * (erfc + EWALD_F*grij*expm2); + if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*prefactor; + } else forcecoul = 0.0; + + if (rsq < cut_ljsq[itype][jtype]) { + rinv = sqrt(r2inv); + r3inv = r2inv*rinv; + r6inv = r3inv*r3inv; + forcelj = r6inv * (lj1[itype][jtype]*r3inv - lj2[itype][jtype]); + } else forcelj = 0.0; + + fforce = (forcecoul + factor_lj*forcelj) * 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 (newton_pair || j < nlocal) factor = 1.0; + else factor = 0.5; + if (rsq < cut_coulsq) { + phicoul = prefactor*erfc; + if (factor_coul < 1.0) phicoul -= (1.0-factor_coul)*prefactor; + eng_coul += factor*phicoul; + } + if (rsq < cut_ljsq[itype][jtype]) { + philj = r6inv*(lj3[itype][jtype]*r3inv-lj4[itype][jtype]) - + offset[itype][jtype]; + eng_vdwl += factor*factor_lj*philj; + } + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairLJClass2CoulLong::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"); + + cut_lj = memory->create_2d_double_array(n+1,n+1,"pair:cut_lj"); + cut_ljsq = memory->create_2d_double_array(n+1,n+1,"pair:cut_ljsq"); + epsilon = memory->create_2d_double_array(n+1,n+1,"pair:epsilon"); + sigma = memory->create_2d_double_array(n+1,n+1,"pair:sigma"); + lj1 = memory->create_2d_double_array(n+1,n+1,"pair:lj1"); + lj2 = memory->create_2d_double_array(n+1,n+1,"pair:lj2"); + lj3 = memory->create_2d_double_array(n+1,n+1,"pair:lj3"); + lj4 = memory->create_2d_double_array(n+1,n+1,"pair:lj4"); + offset = memory->create_2d_double_array(n+1,n+1,"pair:offset"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairLJClass2CoulLong::settings(int narg, char **arg) +{ + if (narg < 1 || narg > 2) error->all("Illegal pair_style command"); + + cut_lj_global = atof(arg[0]); + if (narg == 1) cut_coul = cut_lj_global; + else cut_coul = atof(arg[1]); + + // 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_lj[i][j] = cut_lj_global; + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairLJClass2CoulLong::coeff(int narg, char **arg) +{ + if (narg < 4 || narg > 6) 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); + + double epsilon_one = atof(arg[2]); + double sigma_one = atof(arg[3]); + + double cut_lj_one = cut_lj_global; + if (narg == 5) cut_lj_one = atof(arg[4]); + + 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_lj[i][j] = cut_lj_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairLJClass2CoulLong::init_one(int i, int j) +{ + // always mix epsilon,sigma via sixthpower rules + // mix distance via user-defined rule + + if (setflag[i][j] == 0) { + epsilon[i][j] = 2.0 * sqrt(epsilon[i][i]*epsilon[j][j]) * + pow(sigma[i][i],3.0) * pow(sigma[j][j],3.0) / + (pow(sigma[i][i],6.0) + pow(sigma[j][j],6.0)); + sigma[i][j] = + pow((0.5 * (pow(sigma[i][i],6.0) + pow(sigma[j][j],6.0))),1.0/6.0); + cut_lj[i][j] = mix_distance(cut_lj[i][i],cut_lj[j][j]); + } + + double cut = MAX(cut_lj[i][j],cut_coul); + cut_ljsq[i][j] = cut_lj[i][j] * cut_lj[i][j]; + + lj1[i][j] = 18.0 * epsilon[i][j] * pow(sigma[i][j],9.0); + lj2[i][j] = 18.0 * epsilon[i][j] * pow(sigma[i][j],6.0); + lj3[i][j] = 2.0 * epsilon[i][j] * pow(sigma[i][j],9.0); + lj4[i][j] = 3.0 * epsilon[i][j] * pow(sigma[i][j],6.0); + + if (offset_flag) { + double ratio = sigma[i][j] / cut_lj[i][j]; + offset[i][j] = epsilon[i][j] * (2.0*pow(ratio,9.0) - 3.0*pow(ratio,6.0)); + } else offset[i][j] = 0.0; + + cut_ljsq[j][i] = cut_ljsq[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]; + + // compute I,J contribution to long-range tail correction + // count total # of atoms of type I and J via Allreduce + + if (tail_flag) { + int *type = atom->type; + int nlocal = atom->nlocal; + + double count[2],all[2]; + count[0] = count[1] = 0.0; + for (int k = 0; k < nlocal; k++) { + if (type[k] == i) count[0] += 1.0; + if (type[k] == j) count[1] += 1.0; + } + MPI_Allreduce(count,all,2,MPI_DOUBLE,MPI_SUM,world); + + double PI = 4.0*atan(1.0); + double sig3 = sigma[i][j]*sigma[i][j]*sigma[i][j]; + double sig6 = sig3*sig3; + double rc3 = cut_lj[i][j]*cut_lj[i][j]*cut_lj[i][j]; + double rc6 = rc3*rc3; + etail_ij = 2.0*PI*all[0]*all[1]*epsilon[i][j] * + sig6 * (sig3 - 3.0*rc3) / (3.0*rc6); + ptail_ij = 2.0*PI*all[0]*all[1]*epsilon[i][j] * + sig6 * (sig3 - 2.0*rc3) / rc6; + } + + return cut; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairLJClass2CoulLong::init_style() +{ + // require an atom style with charge defined + + if (atom->charge_allow == 0) + error->all("Must use charged atom style with this pair style"); + + cut_coulsq = cut_coul * cut_coul; + + // insure use of KSpace long-range solver, set g_ewald + + if (force->kspace == NULL) + error->all("Pair style is incompatible with KSpace style"); + else if (strcmp(force->kspace_style,"ewald") == 0) + g_ewald = force->kspace->g_ewald; + else if (strcmp(force->kspace_style,"pppm") == 0) + g_ewald = force->kspace->g_ewald; + else error->all("Pair style is incompatible with KSpace style"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJClass2CoulLong::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + 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_lj[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairLJClass2CoulLong::read_restart(FILE *fp) +{ + read_restart_settings(fp); + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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_lj[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_lj[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJClass2CoulLong::write_restart_settings(FILE *fp) +{ + fwrite(&cut_lj_global,sizeof(double),1,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 PairLJClass2CoulLong::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&cut_lj_global,sizeof(double),1,fp); + fread(&cut_coul,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + MPI_Bcast(&cut_lj_global,1,MPI_DOUBLE,0,world); + 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); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJClass2CoulLong::single(int i, int j, int itype, int jtype, + double rsq, double factor_coul, + double factor_lj, int eflag, One &one) +{ + double r2inv,r,rinv,r3inv,r6inv,grij,expm2,t,erfc,prefactor; + double forcecoul,forcelj,phicoul,philj; + + r2inv = 1.0/rsq; + if (rsq < cut_coulsq) { + 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 forcecoul = 0.0; + if (rsq < cut_ljsq[itype][jtype]) { + rinv = sqrt(r2inv); + r3inv = r2inv*rinv; + r6inv = r3inv*r3inv; + forcelj = r6inv * (lj1[itype][jtype]*r3inv - lj2[itype][jtype]); + } else forcelj = 0.0; + one.fforce = (forcecoul + factor_lj*forcelj) * r2inv; + + if (eflag) { + if (rsq < cut_coulsq) { + phicoul = prefactor*erfc; + if (factor_coul < 1.0) phicoul -= (1.0-factor_coul)*prefactor; + one.eng_coul = phicoul; + } else one.eng_coul = 0.0; + if (rsq < cut_ljsq[itype][jtype]) { + philj = r6inv*(lj3[itype][jtype]*r3inv-lj4[itype][jtype]) - + offset[itype][jtype]; + one.eng_vdwl = factor_lj*philj; + } else one.eng_vdwl = 0.0; + } +} diff --git a/src/CLASS2/style_class2.h b/src/CLASS2/style_class2.h new file mode 100644 index 0000000000000000000000000000000000000000..517070d25f468e8dda6dbfa60986e7e985eb4350 --- /dev/null +++ b/src/CLASS2/style_class2.h @@ -0,0 +1,56 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifdef AngleInclude +#include "angle_class2.h" +#endif + +#ifdef AngleClass +AngleStyle(class2,AngleClass2) +#endif + +#ifdef BondInclude +#include "bond_class2.h" +#endif + +#ifdef BondClass +BondStyle(class2,BondClass2) +#endif + +#ifdef DihedralInclude +#include "dihedral_class2.h" +#endif + +#ifdef DihedralClass +DihedralStyle(class2,DihedralClass2) +#endif + +#ifdef ImproperInclude +#include "improper_class2.h" +#endif + +#ifdef ImproperClass +ImproperStyle(class2,ImproperClass2) +#endif + +#ifdef PairInclude +#include "pair_lj_class2.h" +#include "pair_lj_class2_coul_cut.h" +#include "pair_lj_class2_coul_long.h" +#endif + +#ifdef PairClass +PairStyle(lj/class2,PairLJClass2) +PairStyle(lj/class2/coul/cut,PairLJClass2CoulCut) +PairStyle(lj/class2/coul/long,PairLJClass2CoulLong) +#endif diff --git a/src/DPD/Install.csh b/src/DPD/Install.csh new file mode 100644 index 0000000000000000000000000000000000000000..1c5a909e8d970b8a87e11e6ae47cacd49cb8931b --- /dev/null +++ b/src/DPD/Install.csh @@ -0,0 +1,24 @@ +# Install/unInstall package classes in LAMMPS + +if ($1 == 1) then + + cp style_dpd.h .. + + cp atom_dpd.cpp .. + cp pair_dpd.cpp .. + + cp atom_dpd.h .. + cp pair_dpd.h .. + +else if ($1 == 0) then + + rm ../style_dpd.h + touch ../style_dpd.h + + rm ../atom_dpd.cpp + rm ../pair_dpd.cpp + + rm ../atom_dpd.h + rm ../pair_dpd.h + +endif diff --git a/src/DPD/atom_dpd.cpp b/src/DPD/atom_dpd.cpp new file mode 100644 index 0000000000000000000000000000000000000000..867fb93239b27ec390c94409fa34ad35a55193da --- /dev/null +++ b/src/DPD/atom_dpd.cpp @@ -0,0 +1,236 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "atom_dpd.h" +#include "domain.h" +#include "modify.h" +#include "fix.h" + +/* ---------------------------------------------------------------------- */ + +AtomDPD::AtomDPD(int narg, char **arg) : Atom(narg, arg) {} + +/* ---------------------------------------------------------------------- */ + +void AtomDPD::copy(int i, int j) +{ + 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]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + modify->fix[extra_grow[iextra]]->copy_arrays(i,j); +} + +/* ---------------------------------------------------------------------- */ + +void AtomDPD::pack_comm(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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++] = v[j][0]; + buf[m++] = v[j][1]; + buf[m++] = v[j][2]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + buf[m++] = v[j][0]; + buf[m++] = v[j][1]; + buf[m++] = v[j][2]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomDPD::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++]; + v[i][0] = buf[m++]; + v[i][1] = buf[m++]; + v[i][2] = buf[m++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomDPD::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]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomDPD::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomDPD::pack_border(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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++] = v[j][0]; + buf[m++] = v[j][1]; + buf[m++] = v[j][2]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + buf[m++] = tag[j]; + buf[m++] = type[j]; + buf[m++] = mask[j]; + buf[m++] = v[j][0]; + buf[m++] = v[j][1]; + buf[m++] = v[j][2]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomDPD::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<int> (buf[m++]); + type[i] = static_cast<int> (buf[m++]); + mask[i] = static_cast<int> (buf[m++]); + v[i][0] = buf[m++]; + v[i][1] = buf[m++]; + v[i][2] = buf[m++]; + } +} + +/* ---------------------------------------------------------------------- + pack all atom quantities for shipping to another proc + xyz must be 1st 3 values, so that comm::exchange can test on them +------------------------------------------------------------------------- */ + +int AtomDPD::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++] = image[i]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->pack_exchange(i,&buf[m]); + + buf[0] = m; + return m; +} + +/* ---------------------------------------------------------------------- */ + +int AtomDPD::unpack_exchange(double *buf) +{ + 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<int> (buf[m++]); + type[nlocal] = static_cast<int> (buf[m++]); + mask[nlocal] = static_cast<int> (buf[m++]); + image[nlocal] = static_cast<int> (buf[m++]); + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->unpack_exchange(nlocal,&buf[m]); + + nlocal++; + return m; +} diff --git a/src/DPD/atom_dpd.h b/src/DPD/atom_dpd.h new file mode 100644 index 0000000000000000000000000000000000000000..30af38bd302dbded1c8cef3bb474e86375ffa282 --- /dev/null +++ b/src/DPD/atom_dpd.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ATOM_DPD_H +#define ATOM_DPD_H + +#include "atom.h" + +class AtomDPD : public Atom { + public: + AtomDPD(int, char **); + ~AtomDPD() {} + void copy(int, int); + void pack_comm(int, int *, double *, int *); + void unpack_comm(int, int, double *); + void pack_reverse(int, int, double *); + void unpack_reverse(int, int *, double *); + void pack_border(int, int *, double *, int *); + void unpack_border(int, int, double *); + int pack_exchange(int, double *); + int unpack_exchange(double *); +}; + +#endif diff --git a/src/DPD/pair_dpd.cpp b/src/DPD/pair_dpd.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7dbfee4212162103ad7aaa2c65927a1c16169e5a --- /dev/null +++ b/src/DPD/pair_dpd.cpp @@ -0,0 +1,413 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Kurt Smith (U Pittsburgh) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "pair_dpd.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "update.h" +#include "random_mars.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +#define EPSILON 1.0e-10 + +/* ---------------------------------------------------------------------- */ + +PairDPD::PairDPD() +{ + random = NULL; +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +PairDPD::~PairDPD() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(cut); + memory->destroy_2d_double_array(a0); + memory->destroy_2d_double_array(gamma); + memory->destroy_2d_double_array(sigma); + } + + if (random) delete random; +} + +/* ---------------------------------------------------------------------- */ + +void PairDPD::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double xtmp,ytmp,ztmp,delx,dely,delz,vxtmp,vytmp,vztmp,delvx,delvy,delvz; + double rsq,r,rinv,dot,wd,randnum,fforce,factor_dpd,phi; + int *neighs; + double **f; + + eng_vdwl = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + double **v = atom->v; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double dtinvsqrt = 1.0/sqrt(update->dt); + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + vxtmp = v[i][0]; + vytmp = v[i][1]; + vztmp = v[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_dpd = 1.0; + else { + factor_dpd = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r = sqrt(rsq); + if (r < EPSILON) continue; // r can be 0.0 in DPD systems + rinv = 1.0/r; + delvx = vxtmp - v[j][0]; + delvy = vytmp - v[j][1]; + delvz = vztmp - v[j][2]; + dot = delx*delvx + dely*delvy + delz*delvz; + wd = 1.0 - r/cut[itype][jtype]; + randnum = random->gaussian(); + + // conservative force = a0 * wd + // drag force = -gamma * wd^2 * (delx dot delv) / r + // random force = sigma * wd * rnd * dtinvsqrt; + + fforce = a0[itype][jtype]*wd; + fforce -= gamma[itype][jtype]*wd*wd*dot*rinv; + fforce += sigma[itype][jtype]*wd*randnum*dtinvsqrt; + fforce *= factor_dpd*rinv; + + 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) { + phi = a0[itype][jtype] * r * (1.0 - 0.5*r/cut[itype][jtype]); + if (newton_pair || j < nlocal) eng_vdwl += factor_dpd*phi; + else eng_vdwl += 0.5*factor_dpd*phi; + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairDPD::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"); + + cut = memory->create_2d_double_array(n+1,n+1,"pair:cut"); + a0 = memory->create_2d_double_array(n+1,n+1,"pair:a0"); + gamma = memory->create_2d_double_array(n+1,n+1,"pair:gamma"); + sigma = memory->create_2d_double_array(n+1,n+1,"pair:sigma"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairDPD::settings(int narg, char **arg) +{ + if (narg != 3) error->all("Illegal pair_style command"); + + temperature = atof(arg[0]); + cut_global = atof(arg[1]); + seed = atoi(arg[2]); + + // initialize Marsaglia RNG with processor-unique seed + + if (seed <= 0 || seed > 900000000) + error->all("Illegal fix pair_style command"); + if (random) delete random; + random = new RanMars(seed + comm->me); + + // 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 PairDPD::coeff(int narg, char **arg) +{ + if (narg < 4 || narg > 5) 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); + + double a0_one = atof(arg[2]); + double gamma_one = atof(arg[3]); + + double cut_one = cut_global; + if (narg == 5) cut_one = atof(arg[4]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + for (int j = MAX(jlo,i); j <= jhi; j++) { + a0[i][j] = a0_one; + gamma[i][j] = gamma_one; + cut[i][j] = cut_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairDPD::init_one(int i, int j) +{ + if (setflag[i][j] == 0) error->all("All pair coeffs are not set"); + + sigma[i][j] = sqrt(2.0*temperature*gamma[i][j]); + + cut[j][i] = cut[i][j]; + a0[j][i] = a0[i][j]; + gamma[j][i] = gamma[i][j]; + sigma[j][i] = sigma[i][j]; + + return cut[i][j]; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairDPD::init_style() +{ + // check that atom style is dpd + // else compute() will not have ghost atom velocities + + if (atom->check_style("dpd") == 0) + error->all("Must use atom style dpd with pair style dpd"); + + // if newton off, forces between atoms ij will be double computed + // using different random numbers + + if (force->newton_pair == 0 && comm->me == 0) error->warning( + "DPD potential needs newton pair on for momentum conservation"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairDPD::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + for (j = i; j <= atom->ntypes; j++) { + fwrite(&setflag[i][j],sizeof(int),1,fp); + if (setflag[i][j]) { + fwrite(&a0[i][j],sizeof(double),1,fp); + fwrite(&gamma[i][j],sizeof(double),1,fp); + fwrite(&cut[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairDPD::read_restart(FILE *fp) +{ + read_restart_settings(fp); + + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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(&a0[i][j],sizeof(double),1,fp); + fread(&gamma[i][j],sizeof(double),1,fp); + fread(&cut[i][j],sizeof(double),1,fp); + } + MPI_Bcast(&a0[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&gamma[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairDPD::write_restart_settings(FILE *fp) +{ + fwrite(&temperature,sizeof(double),1,fp); + fwrite(&cut_global,sizeof(double),1,fp); + fwrite(&seed,sizeof(int),1,fp); + fwrite(&mix_flag,sizeof(int),1,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairDPD::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&temperature,sizeof(double),1,fp); + fread(&cut_global,sizeof(double),1,fp); + fread(&seed,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + MPI_Bcast(&temperature,1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_global,1,MPI_DOUBLE,0,world); + MPI_Bcast(&seed,1,MPI_INT,0,world); + MPI_Bcast(&mix_flag,1,MPI_INT,0,world); + + // initialize Marsaglia RNG with processor-unique seed + // same seed that pair_style command initially specified + + if (random) delete random; + random = new RanMars(seed + comm->me); +} + +/* ---------------------------------------------------------------------- */ + +void PairDPD::single(int i, int j, int itype, int jtype, double rsq, + double factor_coul, double factor_dpd, int eflag, + One &one) +{ + double r,rinv,dot,wd,randnum,phi; + + double delx = atom->x[i][0] - atom->x[j][0]; + double dely = atom->x[i][1] - atom->x[j][1]; + double delz = atom->x[i][2] - atom->x[j][2]; + double delvx = atom->v[i][0] - atom->v[j][0]; + double delvy = atom->v[i][1] - atom->v[j][1]; + double delvz = atom->v[i][2] - atom->v[j][2]; + double dtinvsqrt = 1.0/sqrt(update->dt); + + r = sqrt(rsq); + if (r < EPSILON) { + one.fforce = 0.0; + if (eflag) one.eng_vdwl = one.eng_coul = 0.0; + return; + } + + rinv = 1.0/r; + dot = delx*delvx + dely*delvy + delz*delvz; + wd = 1.0 - r/cut[itype][jtype]; + randnum = random->gaussian(); + + one.fforce = a0[itype][jtype]*wd * factor_dpd*rinv; + + if (eflag) { + phi = a0[itype][jtype] * r * (1.0 - 0.5*r/cut[itype][jtype]); + one.eng_vdwl = factor_dpd*phi; + one.eng_coul = 0.0; + } +} diff --git a/src/DPD/pair_dpd.h b/src/DPD/pair_dpd.h new file mode 100644 index 0000000000000000000000000000000000000000..a5c106bcf7518cb6f22960c0e182852ddf00d614 --- /dev/null +++ b/src/DPD/pair_dpd.h @@ -0,0 +1,47 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_DPD_H +#define PAIR_DPD_H + +#include "pair.h" + +class RanMars; + +class PairDPD : public Pair { + public: + PairDPD(); + ~PairDPD(); + void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void init_style(); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + void single(int, int, int, int, double, double, double, int, One &); + + private: + double cut_global,temperature; + int seed; + double **cut; + double **a0,**gamma; + double **sigma; + RanMars *random; + + void allocate(); +}; + +#endif diff --git a/src/DPD/style_dpd.h b/src/DPD/style_dpd.h new file mode 100644 index 0000000000000000000000000000000000000000..0b5d131f2f0e63bac92ed16dfad8f01ad8d1dc87 --- /dev/null +++ b/src/DPD/style_dpd.h @@ -0,0 +1,28 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifdef AtomInclude +#include "atom_dpd.h" +#endif + +#ifdef AtomClass +AtomStyle(dpd,AtomDPD) +#endif + +#ifdef PairInclude +#include "pair_dpd.h" +#endif + +#ifdef PairClass +PairStyle(dpd,PairDPD) +#endif diff --git a/src/GRANULAR/Install.csh b/src/GRANULAR/Install.csh new file mode 100644 index 0000000000000000000000000000000000000000..104514fed4986d442ce9e2de9c1d7adbb3badce6 --- /dev/null +++ b/src/GRANULAR/Install.csh @@ -0,0 +1,58 @@ +# Install/unInstall package classes in LAMMPS + +# fix_shear_history.h must always be in src + +if ($1 == 1) then + + cp style_granular.h .. + + cp atom_granular.cpp .. + cp fix_freeze.cpp .. + cp fix_gran_diag.cpp .. + cp fix_insert.cpp .. + cp fix_nve_gran.cpp .. + cp fix_shear_history.cpp .. + cp fix_wall_gran.cpp .. + cp pair_gran_hertzian.cpp .. + cp pair_gran_history.cpp .. + cp pair_gran_no_history.cpp .. + + cp atom_granular.h .. + cp fix_freeze.h .. + cp fix_gran_diag.h .. + cp fix_insert.h .. + cp fix_nve_gran.h .. +# cp fix_shear_history.h .. + cp fix_wall_gran.h .. + cp pair_gran_hertzian.h .. + cp pair_gran_history.h .. + cp pair_gran_no_history.h .. + +else if ($1 == 0) then + + rm ../style_granular.h + touch ../style_granular.h + + rm ../atom_granular.cpp + rm ../fix_freeze.cpp + rm ../fix_gran_diag.cpp + rm ../fix_insert.cpp + rm ../fix_nve_gran.cpp + rm ../fix_shear_history.cpp + rm ../fix_wall_gran.cpp + rm ../pair_gran_hertzian.cpp + rm ../pair_gran_history.cpp + rm ../pair_gran_no_history.cpp + + rm ../atom_granular.h + rm ../fix_freeze.h + rm ../fix_gran_diag.h + rm ../fix_insert.h + rm ../fix_nve_gran.h +# rm ../fix_shear_history.h + rm ../fix_wall_gran.h + rm ../pair_gran_hertzian.h + rm ../pair_gran_history.h + rm ../pair_gran_no_history.h + +endif diff --git a/src/GRANULAR/atom_granular.cpp b/src/GRANULAR/atom_granular.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6d183dc9fc4158d9cc9499b16fc3babac12996e5 --- /dev/null +++ b/src/GRANULAR/atom_granular.cpp @@ -0,0 +1,322 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "atom_granular.h" +#include "domain.h" +#include "modify.h" +#include "fix.h" + +/* ---------------------------------------------------------------------- */ + +AtomGranular::AtomGranular(int narg, char **arg) : Atom(narg, arg) {} + +/* ---------------------------------------------------------------------- */ + +void AtomGranular::copy(int i, int j) +{ + 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]; + phix[j][0] = phix[i][0]; + phix[j][1] = phix[i][1]; + phix[j][2] = phix[i][2]; + phiv[j][0] = phiv[i][0]; + phiv[j][1] = phiv[i][1]; + phiv[j][2] = phiv[i][2]; + radius[j] = radius[i]; + density[j] = density[i]; + rmass[j] = rmass[i]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + modify->fix[extra_grow[iextra]]->copy_arrays(i,j); +} + +/* ---------------------------------------------------------------------- */ + +void AtomGranular::pack_comm(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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++] = v[j][0]; + buf[m++] = v[j][1]; + buf[m++] = v[j][2]; + buf[m++] = phiv[j][0]; + buf[m++] = phiv[j][1]; + buf[m++] = phiv[j][2]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + buf[m++] = v[j][0]; + buf[m++] = v[j][1]; + buf[m++] = v[j][2]; + buf[m++] = phiv[j][0]; + buf[m++] = phiv[j][1]; + buf[m++] = phiv[j][2]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomGranular::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++]; + v[i][0] = buf[m++]; + v[i][1] = buf[m++]; + v[i][2] = buf[m++]; + phiv[i][0] = buf[m++]; + phiv[i][1] = buf[m++]; + phiv[i][2] = buf[m++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomGranular::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++] = phia[i][0]; + buf[m++] = phia[i][1]; + buf[m++] = phia[i][2]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomGranular::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++]; + phia[j][0] += buf[m++]; + phia[j][1] += buf[m++]; + phia[j][2] += buf[m++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomGranular::pack_border(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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++] = v[j][0]; + buf[m++] = v[j][1]; + buf[m++] = v[j][2]; + buf[m++] = phiv[j][0]; + buf[m++] = phiv[j][1]; + buf[m++] = phiv[j][2]; + buf[m++] = radius[j]; + buf[m++] = rmass[j]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + buf[m++] = tag[j]; + buf[m++] = type[j]; + buf[m++] = mask[j]; + buf[m++] = v[j][0]; + buf[m++] = v[j][1]; + buf[m++] = v[j][2]; + buf[m++] = phiv[j][0]; + buf[m++] = phiv[j][1]; + buf[m++] = phiv[j][2]; + buf[m++] = radius[j]; + buf[m++] = rmass[j]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomGranular::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<int> (buf[m++]); + type[i] = static_cast<int> (buf[m++]); + mask[i] = static_cast<int> (buf[m++]); + v[i][0] = buf[m++]; + v[i][1] = buf[m++]; + v[i][2] = buf[m++]; + phiv[i][0] = buf[m++]; + phiv[i][1] = buf[m++]; + phiv[i][2] = buf[m++]; + radius[i] = buf[m++]; + rmass[i] = buf[m++]; + } +} + +/* ---------------------------------------------------------------------- + pack all atom quantities for shipping to another proc + xyz must be 1st 3 values, so that comm::exchange can test on them +------------------------------------------------------------------------- */ + +int AtomGranular::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++] = phix[i][0]; + buf[m++] = phix[i][1]; + buf[m++] = phix[i][2]; + buf[m++] = phiv[i][0]; + buf[m++] = phiv[i][1]; + buf[m++] = phiv[i][2]; + buf[m++] = radius[i]; + buf[m++] = density[i]; + buf[m++] = rmass[i]; + buf[m++] = tag[i]; + buf[m++] = type[i]; + buf[m++] = mask[i]; + buf[m++] = image[i]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->pack_exchange(i,&buf[m]); + + buf[0] = m; + return m; +} + +/* ---------------------------------------------------------------------- */ + +int AtomGranular::unpack_exchange(double *buf) +{ + 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++]; + phix[nlocal][0] = buf[m++]; + phix[nlocal][1] = buf[m++]; + phix[nlocal][2] = buf[m++]; + phiv[nlocal][0] = buf[m++]; + phiv[nlocal][1] = buf[m++]; + phiv[nlocal][2] = buf[m++]; + radius[nlocal] = buf[m++]; + density[nlocal] = buf[m++]; + rmass[nlocal] = buf[m++]; + tag[nlocal] = static_cast<int> (buf[m++]); + type[nlocal] = static_cast<int> (buf[m++]); + mask[nlocal] = static_cast<int> (buf[m++]); + image[nlocal] = static_cast<int> (buf[m++]); + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->unpack_exchange(nlocal,&buf[m]); + + nlocal++; + return m; +} + +/* ---------------------------------------------------------------------- + unpack vels & phis specific to granular data file +------------------------------------------------------------------------- */ + +void AtomGranular::unpack_vels(int n, char *buf) +{ + int m,tagtmp; + double vxtmp,vytmp,vztmp,phivxtmp,phivytmp,phivztmp; + char *next; + + for (int i = 0; i < n; i++) { + next = strchr(buf,'\n'); + *next = '\0'; + sscanf(buf,"%d %lg %lg %lg %lg %lg %lg", + &tagtmp,&vxtmp,&vytmp,&vztmp, + &phivxtmp,&phivytmp,&phivztmp); + if ((m = map(tagtmp)) >= 0) { + v[m][0] = vxtmp; + v[m][1] = vytmp; + v[m][2] = vztmp; + phiv[m][0] = phivxtmp; + phiv[m][1] = phivytmp; + phiv[m][2] = phivztmp; + } + buf = next + 1; + } +} diff --git a/src/GRANULAR/atom_granular.h b/src/GRANULAR/atom_granular.h new file mode 100644 index 0000000000000000000000000000000000000000..04a84f56e7d47806294bc7a6f756e5ae6b916a5e --- /dev/null +++ b/src/GRANULAR/atom_granular.h @@ -0,0 +1,36 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ATOM_GRANULAR_H +#define ATOM_GRANULAR_H + +#include "atom.h" + +class AtomGranular : public Atom { + public: + AtomGranular(int, char **); + ~AtomGranular() {} + void copy(int, int); + void pack_comm(int, int *, double *, int *); + void unpack_comm(int, int, double *); + void pack_reverse(int, int, double *); + void unpack_reverse(int, int *, double *); + void pack_border(int, int *, double *, int *); + void unpack_border(int, int, double *); + int pack_exchange(int, double *); + int unpack_exchange(double *); + + void unpack_vels(int, char *); +}; + +#endif diff --git a/src/GRANULAR/fix_freeze.cpp b/src/GRANULAR/fix_freeze.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0d146eb88c4facb48ab8f236dc64ced8f101b885 --- /dev/null +++ b/src/GRANULAR/fix_freeze.cpp @@ -0,0 +1,77 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "fix_freeze.h" +#include "atom.h" +#include "modify.h" +#include "comm.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixFreeze::FixFreeze(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 3) error->all("Illegal fix freeze command"); + + if (atom->check_style("granular") == 0) + error->all("Must use fix freeze with atom style granular"); +} + +/* ---------------------------------------------------------------------- */ + +int FixFreeze::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixFreeze::init() +{ + // error if more than one freeze fix + + int count = 0; + for (int i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"freeze") == 0) count++; + if (count > 1) error->all("More than one freeze fix"); +} + +/* ---------------------------------------------------------------------- */ + +void FixFreeze::setup() +{ + post_force(1); +} + +/* ---------------------------------------------------------------------- */ + +void FixFreeze::post_force(int vflag) +{ + double **f = atom->f; + double **phia = atom->phia; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + f[i][0] = 0.0; + f[i][1] = 0.0; + f[i][2] = 0.0; + phia[i][0] = 0.0; + phia[i][1] = 0.0; + phia[i][2] = 0.0; + } +} diff --git a/src/GRANULAR/fix_freeze.h b/src/GRANULAR/fix_freeze.h new file mode 100644 index 0000000000000000000000000000000000000000..b2985c76960609e9d32f3ba920667dc00c7da8f2 --- /dev/null +++ b/src/GRANULAR/fix_freeze.h @@ -0,0 +1,29 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_FREEZE_H +#define FIX_FREEZE_H + +#include "fix.h" + +class FixFreeze : public Fix { + public: + FixFreeze(int, char **); + ~FixFreeze() {} + int setmask(); + void init(); + void setup(); + void post_force(int); +}; + +#endif diff --git a/src/GRANULAR/fix_gran_diag.cpp b/src/GRANULAR/fix_gran_diag.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1c5a32ffdc74493d791ea090b5df57cf26d65cd4 --- /dev/null +++ b/src/GRANULAR/fix_gran_diag.cpp @@ -0,0 +1,969 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Leo Silbert (SNL), Gary Grest (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "fix_gran_diag.h" +#include "update.h" +#include "force.h" +#include "pair_gran_no_history.h" +#include "pair_gran_history.h" +#include "pair_gran_hertzian.h" +#include "atom.h" +#include "neighbor.h" +#include "comm.h" +#include "domain.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +#define NO_HISTORY 1 +#define HISTORY 2 +#define HERTZIAN 3 + +/* ---------------------------------------------------------------------- */ + +FixGranDiag::FixGranDiag(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 6) error->all("Illegal fix gran/diag command"); + nevery = atoi(arg[3]); + if (nevery <= 0) error->all("Illegal fix gran/diag command"); + first = 1; + + if (atom->check_style("granular") == 0) + error->all("Must use fix gran/diag with atom style granular"); + + MPI_Comm_rank(world,&me); + if (me == 0) { + char *file = new char[128]; + + sprintf(file,"%s.den",arg[4]); + fpden = fopen(file,"w"); + if (fpden == NULL) { + char str[128]; + sprintf(str,"Cannot open fix gran/diag file %s",file); + error->one(str); + } + + sprintf(file,"%s.vel",arg[4]); + fpvel = fopen(file,"w"); + if (fpvel == NULL) { + char str[128]; + sprintf(str,"Cannot open fix gran/diag file %s",file); + error->one(str); + } + + sprintf(file,"%s.str",arg[4]); + fpstr = fopen(file,"w"); + if (fpstr == NULL) { + char str[128]; + sprintf(str,"Cannot open fix gran/diag file %s",file); + error->one(str); + } + + delete [] file; + } + + stepz = atof(arg[5]); + stepinv = 1.0/stepz; + PI = 4.0*atan(1.0); + + maxlayers = 0; +} + +/* ---------------------------------------------------------------------- */ + +FixGranDiag::~FixGranDiag() +{ + deallocate(); + + if (me == 0) { + fclose(fpden); + fclose(fpvel); + fclose(fpstr); + } +} + +/* ---------------------------------------------------------------------- */ + +int FixGranDiag::setmask() +{ + int mask = 0; + mask |= END_OF_STEP; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixGranDiag::init() +{ + dt = update->dt; + + // set constants from pair style + + Pair *anypair; + if (anypair = force->pair_match("gran/no_history")) { + pairstyle = NO_HISTORY; + xkk = ((PairGranNoHistory *) anypair)->xkk; + xkkt = ((PairGranNoHistory *) anypair)->xkkt; + xmu = ((PairGranNoHistory *) anypair)->xmu; + gamman_dl = ((PairGranNoHistory *) anypair)->gamman_dl; + gammas_dl = ((PairGranNoHistory *) anypair)->gammas_dl; + freeze_group_bit = ((PairGranNoHistory *) anypair)->freeze_group_bit; + } else if (anypair = force->pair_match("gran/history")) { + pairstyle = HISTORY; + xkk = ((PairGranHistory *) anypair)->xkk; + xkkt = ((PairGranHistory *) anypair)->xkkt; + xmu = ((PairGranHistory *) anypair)->xmu; + gamman_dl = ((PairGranHistory *) anypair)->gamman_dl; + gammas_dl = ((PairGranHistory *) anypair)->gammas_dl; + freeze_group_bit = ((PairGranNoHistory *) anypair)->freeze_group_bit; + } else if (anypair = force->pair_match("gran/hertzian")) { + pairstyle = HERTZIAN; + xkk = ((PairGranHertzian *) anypair)->xkk; + xkkt = ((PairGranHertzian *) anypair)->xkkt; + xmu = ((PairGranHertzian *) anypair)->xmu; + gamman_dl = ((PairGranHertzian *) anypair)->gamman_dl; + gammas_dl = ((PairGranHertzian *) anypair)->gammas_dl; + freeze_group_bit = ((PairGranNoHistory *) anypair)->freeze_group_bit; + } else + error->all("Must use fix gran/diag with granular pair style"); +} + +/* ---------------------------------------------------------------------- */ + +void FixGranDiag::setup() +{ + if (first) end_of_step(); + first = 0; +} + +/* ---------------------------------------------------------------------- */ + +void FixGranDiag::end_of_step() +{ + int i,m; + + // set bottom of box for binning purposes + + boxzlo = domain->boxzlo; + + // update ghost atom info + // else ghost x/v is out-of-date at end of timestep + + comm->communicate(); + + // insure accumulator arrays are correct length + // add 10 for buffer + + nlayers = static_cast<int> (domain->zprd / stepz) + 10; + if (nlayers > maxlayers) { + deallocate(); + maxlayers = nlayers; + allocate(); + } + + // zero all accumulators + + for (i = 0; i < nlayers; i++) { + numdens[i] = 0; + dendens[i] = 0.0; + velx[i] = vely[i] = velz[i] = 0.0; + velxx[i] = velyy[i] = velzz[i] = velxy[i] = velxz[i] = velyz[i] = 0.0; + sigxx[i] = sigyy[i] = sigzz[i] = sigxy[i] = sigxz[i] = sigyz[i] = 0.0; + } + + // density/velocity accumulation by atom + // assign to layer based on atom distance from z bottom of domain + + int overflow = 0; + + double **x = atom->x; + double **v = atom->v; + double *rmass = atom->rmass; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + m = static_cast<int> ((x[i][2]-boxzlo) * stepinv); + if (m >= 0 && m < nlayers) { + numdens[m]++; + dendens[m] += rmass[i]; + velx[m] += v[i][0]; + vely[m] += v[i][1]; + velz[m] += v[i][2]; + velxx[m] += v[i][0]*v[i][0]; + velyy[m] += v[i][1]*v[i][1]; + velzz[m] += v[i][2]*v[i][2]; + velxy[m] += v[i][0]*v[i][1]; + velxz[m] += v[i][0]*v[i][2]; + velyz[m] += v[i][1]*v[i][2]; + } else overflow++; + } + + // m = largest layer # with any counts + // nmax = # of layers up to m + + for (m = nlayers-1; m >= 0; m--) if (numdens[m]) break; + int nmax = m + 1; + + int tmp = nmax; + MPI_Allreduce(&tmp,&nmax,1,MPI_INT,MPI_MAX,world); + + // overflow = total # of atoms out-of-bounds of layer arrays + + tmp = overflow; + MPI_Allreduce(&tmp,&overflow,1,MPI_INT,MPI_SUM,world); + + // sum contributions across procs + + int *isum = new int[nmax]; + double *dsum = new double[nmax]; + + MPI_Allreduce(numdens,isum,nmax,MPI_INT,MPI_SUM,world); + for (i = 0; i < nmax; i++) numdens[i] = isum[i]; + + MPI_Allreduce(dendens,dsum,nmax,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < nmax; i++) dendens[i] = dsum[i]; + + MPI_Allreduce(velx,dsum,nmax,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < nmax; i++) velx[i] = dsum[i]; + MPI_Allreduce(vely,dsum,nmax,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < nmax; i++) vely[i] = dsum[i]; + MPI_Allreduce(velz,dsum,nmax,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < nmax; i++) velz[i] = dsum[i]; + + MPI_Allreduce(velxx,dsum,nmax,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < nmax; i++) velxx[i] = dsum[i]; + MPI_Allreduce(velyy,dsum,nmax,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < nmax; i++) velyy[i] = dsum[i]; + MPI_Allreduce(velzz,dsum,nmax,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < nmax; i++) velzz[i] = dsum[i]; + MPI_Allreduce(velxy,dsum,nmax,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < nmax; i++) velxy[i] = dsum[i]; + MPI_Allreduce(velxz,dsum,nmax,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < nmax; i++) velxz[i] = dsum[i]; + MPI_Allreduce(velyz,dsum,nmax,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < nmax; i++) velyz[i] = dsum[i]; + + // compute contribution to stress by every atom pair + + if (pairstyle == NO_HISTORY) stress_no_history(); + else if (pairstyle == HISTORY) stress_history(); + else if (pairstyle == HERTZIAN) stress_hertzian(); + + // sum contributions across procs + + MPI_Allreduce(sigxx,dsum,nmax,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < nmax; i++) sigxx[i] = dsum[i]; + MPI_Allreduce(sigyy,dsum,nmax,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < nmax; i++) sigyy[i] = dsum[i]; + MPI_Allreduce(sigzz,dsum,nmax,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < nmax; i++) sigzz[i] = dsum[i]; + + MPI_Allreduce(sigxy,dsum,nmax,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < nmax; i++) sigxy[i] = dsum[i]; + MPI_Allreduce(sigxz,dsum,nmax,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < nmax; i++) sigxz[i] = dsum[i]; + MPI_Allreduce(sigyz,dsum,nmax,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < nmax; i++) sigyz[i] = dsum[i]; + + delete [] isum; + delete [] dsum; + + // density/velocity/stress by layer + + double velxxd1,velyyd1,velzzd1,velxyd1,velxzd1,velyzd1; + double volxy = domain->xprd * domain->yprd; + + for (m = 0; m < nmax; m++) { + if (numdens[m] == 0) numdens[m] = 1; + dendens[m] = stepinv*dendens[m]/volxy; + + velx11[m] = velx[m]/numdens[m]; + vely11[m] = vely[m]/numdens[m]; + velz11[m] = velz[m]/numdens[m]; + velxx11[m] = velxx[m]/numdens[m]; + velyy11[m] = velyy[m]/numdens[m]; + velzz11[m] = velzz[m]/numdens[m]; + velxy11[m] = velxy[m]/numdens[m]; + velxz11[m] = velxz[m]/numdens[m]; + velyz11[m] = velyz[m]/numdens[m]; + + velxxd1 = velxx11[m] - velx11[m]*velx11[m]; + velyyd1 = velyy11[m] - vely11[m]*vely11[m]; + velzzd1 = velzz11[m] - velz11[m]*velz11[m]; + velxyd1 = velxy11[m] - velx11[m]*vely11[m]; + velxzd1 = velxz11[m] - velx11[m]*velz11[m]; + velyzd1 = velyz11[m] - vely11[m]*velz11[m]; + + velfxx[m] = velxxd1 * dendens[m]; + velfyy[m] = velyyd1 * dendens[m]; + velfzz[m] = velzzd1 * dendens[m]; + velfxy[m] = velxyd1 * dendens[m]; + velfxz[m] = velxzd1 * dendens[m]; + velfyz[m] = velyzd1 * dendens[m]; + + sigx2[m] = sigxx[m]/(2.0*volxy*stepz) + velxxd1*dendens[m]; + sigy2[m] = sigyy[m]/(2.0*volxy*stepz) + velyyd1*dendens[m]; + sigz2[m] = sigzz[m]/(2.0*volxy*stepz) + velzzd1*dendens[m]; + sigxy2[m] = sigxy[m]/(2.0*volxy*stepz) + velxyd1*dendens[m]; + sigxz2[m] = sigxz[m]/(2.0*volxy*stepz) + velxzd1*dendens[m]; + sigyz2[m] = sigyz[m]/(2.0*volxy*stepz) + velyzd1*dendens[m]; + } + + // write out density profile + + if (me == 0) { + fprintf(fpden,"ITEM: TIMESTEP\n"); + fprintf(fpden,"%d\n",update->ntimestep); + fprintf(fpden,"ITEM: NUMBER OF LAYERS / OVERFLOWS\n"); + fprintf(fpden,"%d %d\n",nmax,overflow); + fprintf(fpden,"ITEM: DENSITY BY LAYER\n"); + for (m = 0; m < nmax; m++) + fprintf(fpden,"%d %g %g\n",m+1,(m+1)*stepz+boxzlo,dendens[m]*PI/6.0); + } + + // write out velocity profile + + if (me == 0) { + fprintf(fpvel,"ITEM: TIMESTEP\n"); + fprintf(fpvel,"%d\n",update->ntimestep); + fprintf(fpvel,"ITEM: NUMBER OF LAYERS / OVERFLOWS\n"); + fprintf(fpvel,"%d %d\n",nmax,overflow); + fprintf(fpvel,"ITEM: VELOCITY BY LAYER\n"); + for (m = 0; m < nmax; m++) + fprintf(fpvel,"%d %g %g %g %g %g %g %g %g %g %g\n", + m+1,(m+1)*stepz+boxzlo, + velx11[m],vely11[m],velz11[m], + velxx11[m],velyy11[m],velzz11[m], + velxy11[m],velxz11[m],velyz11[m]); + } + + // write out stress profile + + if (me == 0) { + fprintf(fpstr,"ITEM: TIMESTEP\n"); + fprintf(fpstr,"%d\n",update->ntimestep); + fprintf(fpstr,"ITEM: NUMBER OF LAYERS / OVERFLOWS\n"); + fprintf(fpstr,"%d %d\n",nmax,overflow); + fprintf(fpstr,"ITEM: STRESS BY LAYER\n"); + for (m = 0; m < nmax; m++) + fprintf(fpstr,"%d %g %g %g %g %g %g %g %g %g %g %g %g %g\n", + m+1,(m+1)*stepz+boxzlo, + sigx2[m],sigy2[m],sigz2[m], + sigxy2[m],sigxz2[m],sigyz2[m], + velfxx[m],velfyy[m],velfzz[m], + velfxy[m],velfxz[m],velfyz[m]); + } +} + +/* ---------------------------------------------------------------------- */ + +void FixGranDiag::allocate() +{ + numdens = new int[maxlayers]; + dendens = new double[maxlayers]; + + velx = new double[maxlayers]; + vely = new double[maxlayers]; + velz = new double[maxlayers]; + velxx = new double[maxlayers]; + velyy = new double[maxlayers]; + velzz = new double[maxlayers]; + velxy = new double[maxlayers]; + velxz = new double[maxlayers]; + velyz = new double[maxlayers]; + + velx11 = new double[maxlayers]; + vely11 = new double[maxlayers]; + velz11 = new double[maxlayers]; + velxx11 = new double[maxlayers]; + velyy11 = new double[maxlayers]; + velzz11 = new double[maxlayers]; + velxy11 = new double[maxlayers]; + velxz11 = new double[maxlayers]; + velyz11 = new double[maxlayers]; + + sigxx = new double[maxlayers]; + sigyy = new double[maxlayers]; + sigzz = new double[maxlayers]; + sigxy = new double[maxlayers]; + sigxz = new double[maxlayers]; + sigyz = new double[maxlayers]; + + sigx2 = new double[maxlayers]; + sigy2 = new double[maxlayers]; + sigz2 = new double[maxlayers]; + sigxy2 = new double[maxlayers]; + sigxz2 = new double[maxlayers]; + sigyz2 = new double[maxlayers]; + + velfxx = new double[maxlayers]; + velfyy = new double[maxlayers]; + velfzz = new double[maxlayers]; + velfxy = new double[maxlayers]; + velfxz = new double[maxlayers]; + velfyz = new double[maxlayers]; +} + +/* ---------------------------------------------------------------------- */ + +void FixGranDiag::deallocate() +{ + if (maxlayers == 0) return; + + delete [] numdens; delete [] dendens; + + delete [] velx; delete [] vely; delete [] velz; + delete [] velxx; delete [] velyy; delete [] velzz; + delete [] velxy; delete [] velxz; delete [] velyz; + + delete [] velx11; delete [] vely11; delete [] velz11; + delete [] velxx11; delete [] velyy11; delete [] velzz11; + delete [] velxy11; delete [] velxz11; delete [] velyz11; + + delete [] sigxx; delete [] sigyy; delete [] sigzz; + delete [] sigxy; delete [] sigxz; delete [] sigyz; + + delete [] sigx2; delete [] sigy2; delete [] sigz2; + delete [] sigxy2; delete [] sigxz2; delete [] sigyz2; + + delete [] velfxx; delete [] velfyy; delete [] velfzz; + delete [] velfxy; delete [] velfxz; delete [] velfyz; +} + +/* ---------------------------------------------------------------------- */ + +void FixGranDiag::stress_no_history() +{ + int i,j,k,m,numneigh; + double xtmp,ytmp,ztmp,delx,dely,delz; + double radi,radj,radsum,rsq,r; + double vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3; + double wr1,wr2,wr3; + double vtr1,vtr2,vtr3,vrel; + double xmeff,damp,ccel,ccelx,ccely,ccelz; + double fn,fs,ft,fs1,fs2,fs3; + int *neighs; + + double **x = atom->x; + double **v = atom->v; + double **phiv = atom->phiv; + double *radius = atom->radius; + double *rmass = atom->rmass; + int *mask = atom->mask; + int nlocal = atom->nlocal; + int newton_pair = force->newton_pair; + + // loop over all neighbors of my atoms + // store stress for both atoms i and j + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + radi = radius[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + // skip if neither atom is in fix group + + if (!(mask[i] & groupbit) && !(mask[j] & groupbit)) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + radj = radius[j]; + radsum = radi + radj; + + if (rsq < radsum*radsum) { + + r = sqrt(rsq); + + // relative translational velocity + + vr1 = v[i][0] - v[j][0]; + vr2 = v[i][1] - v[j][1]; + vr3 = v[i][2] - v[j][2]; + + vr1 *= dt; + vr2 *= dt; + vr3 *= dt; + + // normal component + + vnnr = vr1*delx + vr2*dely + vr3*delz; + vn1 = delx*vnnr / rsq; + vn2 = dely*vnnr / rsq; + vn3 = delz*vnnr / rsq; + + // tangential component + + vt1 = vr1 - vn1; + vt2 = vr2 - vn2; + vt3 = vr3 - vn3; + + // relative rotational velocity + + wr1 = radi*phiv[i][0] + radj*phiv[j][0]; + wr2 = radi*phiv[i][1] + radj*phiv[j][1]; + wr3 = radi*phiv[i][2] + radj*phiv[j][2]; + + wr1 *= dt/r; + wr2 *= dt/r; + wr3 *= dt/r; + + // normal damping term + // this definition of DAMP includes the extra 1/r term + + xmeff = rmass[i]*rmass[j] / (rmass[i]+rmass[j]); + if (mask[i] & freeze_group_bit) xmeff = rmass[j]; + if (mask[j] & freeze_group_bit) xmeff = rmass[i]; + damp = xmeff*gamman_dl*vnnr/rsq; + ccel = xkk*(radsum-r)/r - damp; + + // relative velocities + + vtr1 = vt1 - (delz*wr2-dely*wr3); + vtr2 = vt2 - (delx*wr3-delz*wr1); + vtr3 = vt3 - (dely*wr1-delx*wr2); + vrel = vtr1*vtr1 + vtr2*vtr2 + vtr3*vtr3; + vrel = sqrt(vrel); + + // force normalization + + fn = xmu * fabs(ccel*r); + fs = xmeff*gammas_dl*vrel; + if (vrel != 0.0) ft = MIN(fn,fs) / vrel; + else ft = 0.0; + + // shear friction forces + + fs1 = -ft*vtr1; + fs2 = -ft*vtr2; + fs3 = -ft*vtr3; + + // forces + + ccelx = delx*ccel + fs1; + ccely = dely*ccel + fs2; + ccelz = delz*ccel + fs3; + + // stress contribution of atom pair to z-layers + // atom i always contributes + // atom j contributes if newton_pair is on or if owned by this proc + + m = static_cast<int> ((x[i][2]-boxzlo) * stepinv); + if (m >= 0 && m < nlayers) { + sigxx[m] += delx*ccelx; + sigyy[m] += dely*ccely; + sigzz[m] += delz*ccelz; + sigxy[m] += delx*ccely; + sigxz[m] += delx*ccelz; + sigyz[m] += dely*ccelz; + } + + if (newton_pair || j < nlocal) { + m = static_cast<int> ((x[j][2]-boxzlo) * stepinv); + if (m >= 0 && m < nlayers) { + sigxx[m] += delx*ccelx; + sigyy[m] += dely*ccely; + sigzz[m] += delz*ccelz; + sigxy[m] += delx*ccely; + sigxz[m] += delx*ccelz; + sigyz[m] += dely*ccelz; + } + } + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixGranDiag::stress_history() +{ + int i,j,k,m,numneigh; + double xtmp,ytmp,ztmp,delx,dely,delz; + double radi,radj,radsum,rsq,r; + double vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3; + double wr1,wr2,wr3; + double vtr1,vtr2,vtr3,vrel,shrx,shry,shrz; + double xmeff,damp,ccel,ccelx,ccely,ccelz; + double fn,fs,fs1,fs2,fs3; + double shrmag,rsht; + int *neighs; + double *firstshear,*shear; + + double **x = atom->x; + double **v = atom->v; + double **phiv = atom->phiv; + double *radius = atom->radius; + double *rmass = atom->rmass; + int *mask = atom->mask; + int nlocal = atom->nlocal; + int newton_pair = force->newton_pair; + + // loop over all neighbors of my atoms + // store stress on both atoms i and j + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + radi = radius[i]; + neighs = neighbor->firstneigh[i]; + firstshear = neighbor->firstshear[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + // skip if neither atom is in fix group + + if (!(mask[i] & groupbit) && !(mask[j] & groupbit)) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + radj = radius[j]; + radsum = radi + radj; + + if (rsq < radsum*radsum) { + + r = sqrt(rsq); + + // relative translational velocity + + vr1 = v[i][0] - v[j][0]; + vr2 = v[i][1] - v[j][1]; + vr3 = v[i][2] - v[j][2]; + + vr1 *= dt; + vr2 *= dt; + vr3 *= dt; + + // normal component + + vnnr = vr1*delx + vr2*dely + vr3*delz; + vn1 = delx*vnnr / rsq; + vn2 = dely*vnnr / rsq; + vn3 = delz*vnnr / rsq; + + // tangential component + + vt1 = vr1 - vn1; + vt2 = vr2 - vn2; + vt3 = vr3 - vn3; + + // relative rotational velocity + + wr1 = radi*phiv[i][0] + radj*phiv[j][0]; + wr2 = radi*phiv[i][1] + radj*phiv[j][1]; + wr3 = radi*phiv[i][2] + radj*phiv[j][2]; + + wr1 *= dt/r; + wr2 *= dt/r; + wr3 *= dt/r; + + // normal damping term + // this definition of DAMP includes the extra 1/r term + + xmeff = rmass[i]*rmass[j] / (rmass[i]+rmass[j]); + if (mask[i] & freeze_group_bit) xmeff = rmass[j]; + if (mask[j] & freeze_group_bit) xmeff = rmass[i]; + damp = xmeff*gamman_dl*vnnr/rsq; + ccel = xkk*(radsum-r)/r - damp; + + // relative velocities + + vtr1 = vt1 - (delz*wr2-dely*wr3); + vtr2 = vt2 - (delx*wr3-delz*wr1); + vtr3 = vt3 - (dely*wr1-delx*wr2); + vrel = vtr1*vtr1 + vtr2*vtr2 + vtr3*vtr3; + vrel = sqrt(vrel); + + // shear history effects + // shrmag = magnitude of shear + // do not update shear history since not timestepping + + shear = &firstshear[3*k]; + shrx = shear[0] + vtr1; + shry = shear[1] + vtr2; + shrz = shear[2] + vtr3; + shrmag = sqrt(shrx*shrx + shry*shry + shrz*shrz); + + // rotate shear displacements correctly + + rsht = shrx*delx + shry*dely + shrz*delz; + rsht /= rsq; + shrx -= rsht*delx; + shry -= rsht*dely; + shrz -= rsht*delz; + + // tangential forces + + fs1 = - (xkkt*shrx + xmeff*gammas_dl*vtr1); + fs2 = - (xkkt*shry + xmeff*gammas_dl*vtr2); + fs3 = - (xkkt*shrz + xmeff*gammas_dl*vtr3); + + // force normalization + // rescale frictional displacements and forces if needed + + fs = sqrt(fs1*fs1 + fs2*fs2 + fs3*fs3); + fn = xmu * fabs(ccel*r); + + if (fs > fn) { + if (shrmag != 0.0) { + fs1 *= fn/fs; + fs2 *= fn/fs; + fs3 *= fn/fs; + } else { + fs1 = 0.0; + fs2 = 0.0; + fs3 = 0.0; + } + } + + // forces + + ccelx = delx*ccel + fs1; + ccely = dely*ccel + fs2; + ccelz = delz*ccel + fs3; + + // stress contribution of atom pair to z-layers + // atom i always contributes + // atom j contributes if newton_pair is on or if owned by this proc + + m = static_cast<int> ((x[i][2]-boxzlo) * stepinv); + if (m >= 0 && m < nlayers) { + sigxx[m] += delx*ccelx; + sigyy[m] += dely*ccely; + sigzz[m] += delz*ccelz; + sigxy[m] += delx*ccely; + sigxz[m] += delx*ccelz; + sigyz[m] += dely*ccelz; + } + + if (newton_pair || j < nlocal) { + m = static_cast<int> ((x[j][2]-boxzlo) * stepinv); + if (m >= 0 && m < nlayers) { + sigxx[m] += delx*ccelx; + sigyy[m] += dely*ccely; + sigzz[m] += delz*ccelz; + sigxy[m] += delx*ccely; + sigxz[m] += delx*ccelz; + sigyz[m] += dely*ccelz; + } + } + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixGranDiag::stress_hertzian() +{ + int i,j,k,m,numneigh; + double xtmp,ytmp,ztmp,delx,dely,delz; + double radi,radj,radsum,rsq,r; + double vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3; + double wr1,wr2,wr3; + double vtr1,vtr2,vtr3,vrel,shrx,shry,shrz; + double xmeff,damp,ccel,ccelx,ccely,ccelz; + double fn,fs,fs1,fs2,fs3; + double shrmag,rsht,rhertz; + int *neighs; + double *firstshear,*shear; + + double **x = atom->x; + double **v = atom->v; + double **phiv = atom->phiv; + double *radius = atom->radius; + double *rmass = atom->rmass; + int *mask = atom->mask; + int nlocal = atom->nlocal; + int newton_pair = force->newton_pair; + + // loop over all neighbors of my atoms + // store stress on both atoms i and j + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + radi = radius[i]; + neighs = neighbor->firstneigh[i]; + firstshear = neighbor->firstshear[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + // skip if neither atom is in fix group + + if (!(mask[i] & groupbit) && !(mask[j] & groupbit)) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + radj = radius[j]; + radsum = radi + radj; + + if (rsq < radsum*radsum) { + + r = sqrt(rsq); + + // relative translational velocity + + vr1 = v[i][0] - v[j][0]; + vr2 = v[i][1] - v[j][1]; + vr3 = v[i][2] - v[j][2]; + + vr1 *= dt; + vr2 *= dt; + vr3 *= dt; + + // normal component + + vnnr = vr1*delx + vr2*dely + vr3*delz; + vn1 = delx*vnnr / rsq; + vn2 = dely*vnnr / rsq; + vn3 = delz*vnnr / rsq; + + // tangential component + + vt1 = vr1 - vn1; + vt2 = vr2 - vn2; + vt3 = vr3 - vn3; + + // relative rotational velocity + + wr1 = radi*phiv[i][0] + radj*phiv[j][0]; + wr2 = radi*phiv[i][1] + radj*phiv[j][1]; + wr3 = radi*phiv[i][2] + radj*phiv[j][2]; + + wr1 *= dt/r; + wr2 *= dt/r; + wr3 *= dt/r; + + // normal damping term + // this definition of DAMP includes the extra 1/r term + + xmeff = rmass[i]*rmass[j] / (rmass[i]+rmass[j]); + if (mask[i] & freeze_group_bit) xmeff = rmass[j]; + if (mask[j] & freeze_group_bit) xmeff = rmass[i]; + damp = xmeff*gamman_dl*vnnr/rsq; + ccel = xkk*(radsum-r)/r - damp; + rhertz = sqrt(radsum - r); + ccel = rhertz * ccel; + + // relative velocities + + vtr1 = vt1 - (delz*wr2-dely*wr3); + vtr2 = vt2 - (delx*wr3-delz*wr1); + vtr3 = vt3 - (dely*wr1-delx*wr2); + vrel = vtr1*vtr1 + vtr2*vtr2 + vtr3*vtr3; + vrel = sqrt(vrel); + + // shear history effects + // shrmag = magnitude of shear + // do not update shear history since not timestepping + + shear = &firstshear[3*k]; + shrx = shear[0] + vtr1; + shry = shear[1] + vtr2; + shrz = shear[2] + vtr3; + shrmag = sqrt(shrx*shrx + shry*shry + shrz*shrz); + + // rotate shear displacements correctly + + rsht = shrx*delx + shry*dely + shrz*delz; + rsht /= rsq; + shrx -= rsht*delx; + shry -= rsht*dely; + shrz -= rsht*delz; + + // tangential forces + + fs1 = -rhertz * (xkkt*shrx + xmeff*gammas_dl*vtr1); + fs2 = -rhertz * (xkkt*shry + xmeff*gammas_dl*vtr2); + fs3 = -rhertz * (xkkt*shrz + xmeff*gammas_dl*vtr3); + + // force normalization + // rescale frictional displacements and forces if needed + + fs = sqrt(fs1*fs1 + fs2*fs2 + fs3*fs3); + fn = xmu * fabs(ccel*r); + + if (fs > fn) { + if (shrmag != 0.0) { + fs1 *= fn/fs; + fs2 *= fn/fs; + fs3 *= fn/fs; + } else { + fs1 = 0.0; + fs2 = 0.0; + fs3 = 0.0; + } + } + + // forces + + ccelx = delx*ccel + fs1; + ccely = dely*ccel + fs2; + ccelz = delz*ccel + fs3; + + // stress contribution of atom pair to z-layers + // atom i always contributes + // atom j contributes if newton_pair is on or if owned by this proc + + m = static_cast<int> ((x[i][2]-boxzlo) * stepinv); + if (m >= 0 && m < nlayers) { + sigxx[m] += delx*ccelx; + sigyy[m] += dely*ccely; + sigzz[m] += delz*ccelz; + sigxy[m] += delx*ccely; + sigxz[m] += delx*ccelz; + sigyz[m] += dely*ccelz; + } + + if (newton_pair || j < nlocal) { + m = static_cast<int> ((x[j][2]-boxzlo) * stepinv); + if (m >= 0 && m < nlayers) { + sigxx[m] += delx*ccelx; + sigyy[m] += dely*ccely; + sigzz[m] += delz*ccelz; + sigxy[m] += delx*ccely; + sigxz[m] += delx*ccelz; + sigyz[m] += dely*ccelz; + } + } + } + } + } +} diff --git a/src/GRANULAR/fix_gran_diag.h b/src/GRANULAR/fix_gran_diag.h new file mode 100644 index 0000000000000000000000000000000000000000..970e076552ee10218ffd4cba17a72e597fedb113 --- /dev/null +++ b/src/GRANULAR/fix_gran_diag.h @@ -0,0 +1,53 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_GRAN_DIAG_H +#define FIX_GRAN_DIAG_H + +#include "stdio.h" +#include "fix.h" + +class FixGranDiag : public Fix { + public: + FixGranDiag(int, char **); + ~FixGranDiag(); + int setmask(); + void init(); + void setup(); + void end_of_step(); + + private: + int me,first,pairstyle,nlayers,maxlayers; + FILE *fpden,*fpvel,*fpstr; + double stepz,stepinv,PI,boxzlo; + double dt,xkk,xkkt,xmu,gamman_dl,gammas_dl; + int freeze_group_bit; + + int *numdens; + double *dendens; + double *velx,*vely,*velz; + double *velxx,*velyy,*velzz,*velxy,*velxz,*velyz; + double *sigxx,*sigyy,*sigzz,*sigxy,*sigxz,*sigyz; + double *velx11,*vely11,*velz11; + double *velxx11,*velyy11,*velzz11,*velxy11,*velxz11,*velyz11; + double *velfxx,*velfyy,*velfzz,*velfxy,*velfxz,*velfyz; + double *sigx2,*sigy2,*sigz2,*sigxy2,*sigxz2,*sigyz2; + + void allocate(); + void deallocate(); + void stress_no_history(); + void stress_history(); + void stress_hertzian(); +}; + +#endif diff --git a/src/GRANULAR/fix_insert.cpp b/src/GRANULAR/fix_insert.cpp new file mode 100644 index 0000000000000000000000000000000000000000..30367ba2e4a1fc098513c0f258c9ac4dd1f00d5e --- /dev/null +++ b/src/GRANULAR/fix_insert.cpp @@ -0,0 +1,534 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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_insert.h" +#include "atom.h" +#include "force.h" +#include "update.h" +#include "modify.h" +#include "fix_gravity.h" +#include "fix_shear_history.h" +#include "neighbor.h" +#include "domain.h" +#include "region.h" +#include "region_block.h" +#include "region_cylinder.h" +#include "random_park.h" +#include "memory.h" +#include "error.h" + +#define EPSILON 0.001 + +/* ---------------------------------------------------------------------- */ + +FixInsert::FixInsert(int narg, char **arg) : Fix(narg, arg) +{ + if (narg < 6) error->all("Illegal fix insert command"); + + if (atom->check_style("granular") == 0) + error->all("Must use fix insert with atom style granular"); + + // required args + + ninsert = atoi(arg[3]); + ntype = atoi(arg[4]); + seed = atoi(arg[5]); + + PI = 4.0*atan(1.0); + + // option defaults + + int iregion = -1; + radius_lo = radius_hi = 0.5; + density_lo = density_hi = 1.0; + volfrac = 0.25; + maxattempt = 50; + rate = 0.0; + vxlo = vxhi = vylo = vyhi = vy = vz = 0.0; + + // optional args + + int iarg = 6; + while (iarg < narg) { + if (strcmp(arg[iarg],"region") == 0) { + if (iarg+2 > narg) error->all("Illegal fix insert command"); + for (iregion = 0; iregion < domain->nregion; iregion++) + if (strcmp(arg[iarg+1],domain->regions[iregion]->id) == 0) break; + if (iregion == domain->nregion) + error->all("Fix insert region ID does not exist"); + iarg += 2; + } else if (strcmp(arg[iarg],"diam") == 0) { + if (iarg+3 > narg) error->all("Illegal fix insert command"); + radius_lo = 0.5 * atof(arg[iarg+1]); + radius_hi = 0.5 * atof(arg[iarg+2]); + iarg += 3; + } else if (strcmp(arg[iarg],"dens") == 0) { + if (iarg+3 > narg) error->all("Illegal fix insert command"); + density_lo = atof(arg[iarg+1]); + density_hi = atof(arg[iarg+2]); + iarg += 3; + } else if (strcmp(arg[iarg],"vol") == 0) { + if (iarg+3 > narg) error->all("Illegal fix insert command"); + volfrac = atof(arg[iarg+1]); + maxattempt = atoi(arg[iarg+2]); + iarg += 3; + } else if (strcmp(arg[iarg],"rate") == 0) { + if (iarg+2 > narg) error->all("Illegal fix insert command"); + rate = atof(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"vel") == 0) { + if (force->dimension == 3) { + if (iarg+6 > narg) error->all("Illegal fix insert command"); + vxlo = atof(arg[iarg+1]); + vxhi = atof(arg[iarg+2]); + vylo = atof(arg[iarg+3]); + vyhi = atof(arg[iarg+4]); + vz = atof(arg[iarg+5]); + iarg += 6; + } else { + if (iarg+4 > narg) error->all("Illegal fix insert command"); + vxlo = atof(arg[iarg+1]); + vxhi = atof(arg[iarg+2]); + vy = atof(arg[iarg+3]); + vz = 0.0; + iarg += 4; + } + } else error->all("Illegal fix insert command"); + } + + // error check that a valid region was specified + + if (iregion == -1) error->all("Must specify a region in fix insert"); + + // error checks on region + + if (domain->regions[iregion]->interior == 0) + error->all("Must use region with side = in with fix insert"); + + if (strcmp(domain->regions[iregion]->style,"block") == 0) { + region_style = 1; + xlo = ((RegBlock *) domain->regions[iregion])->xlo; + xhi = ((RegBlock *) domain->regions[iregion])->xhi; + ylo = ((RegBlock *) domain->regions[iregion])->ylo; + yhi = ((RegBlock *) domain->regions[iregion])->yhi; + zlo = ((RegBlock *) domain->regions[iregion])->zlo; + zhi = ((RegBlock *) domain->regions[iregion])->zhi; + if (xlo < domain->boxxlo || xhi > domain->boxxhi || + ylo < domain->boxylo || yhi > domain->boxyhi || + zlo < domain->boxzlo || zhi > domain->boxzhi) + error->all("Insertion region extends outside simulation box"); + } else if (strcmp(domain->regions[iregion]->style,"cylinder") == 0) { + region_style = 2; + char axis = ((RegCylinder *) domain->regions[iregion])->axis; + xc = ((RegCylinder *) domain->regions[iregion])->c1; + yc = ((RegCylinder *) domain->regions[iregion])->c2; + rc = ((RegCylinder *) domain->regions[iregion])->radius; + zlo = ((RegCylinder *) domain->regions[iregion])->lo; + zhi = ((RegCylinder *) domain->regions[iregion])->hi; + if (axis != 'z') + error->all("Must use a z-axis cylinder with fix insert"); + if (xc-rc < domain->boxxlo || xc+rc > domain->boxxhi || + yc-rc < domain->boxylo || yc+rc > domain->boxyhi || + zlo < domain->boxzlo || zhi > domain->boxzhi) + error->all("Insertion region extends outside simulation box"); + } else error->all("Must use a block or cylinder region with fix insert"); + + if (region_style == 2 && force->dimension == 2) + error->all("Must use a block region with fix insert for 2d simulations"); + + // random number generator, same for all procs + + random = new RanPark(seed); + + // allgather arrays + + MPI_Comm_rank(world,&me); + MPI_Comm_size(world,&nprocs); + recvcounts = new int[nprocs]; + displs = new int[nprocs]; + + // nfreq = timesteps between insertions + // should be time for a particle to fall from top of insertion region + // to bottom, taking into account that the region may be moving + // 1st insertion on next timestep + + double v_relative,delta; + double g = 1.0; + if (force->dimension == 3) { + v_relative = vz - rate; + delta = v_relative + sqrt(v_relative*v_relative + 2.0*g*(zhi-zlo)) / g; + } else { + v_relative = vy - rate; + delta = v_relative + sqrt(v_relative*v_relative + 2.0*g*(yhi-ylo)) / g; + } + nfreq = static_cast<int> (delta/update->dt + 0.5); + + force_reneighbor = 1; + next_reneighbor = update->ntimestep + 1; + nfirst = next_reneighbor; + ninserted = 0; + + // nper = # to insert each time + // depends on specified volume fraction + // volume = volume of insertion region + // volume_one = volume of inserted particle (with max possible radius) + // in 3d, insure dy >= 1, for quasi-2d simulations + + double volume,volume_one; + if (force->dimension == 3) { + if (region_style == 1) { + double dy = yhi - ylo; + if (dy < 1.0) dy = 1.0; + volume = (xhi-xlo) * dy * (zhi-zlo); + } else volume = PI*rc*rc * (zhi-zlo); + volume_one = 4.0/3.0 * PI * radius_hi*radius_hi*radius_hi; + } else { + volume = (xhi-xlo) * (yhi-ylo); + volume_one = PI * radius_hi*radius_hi; + } + + nper = static_cast<int> (volfrac*volume/volume_one); + int nfinal = update->ntimestep + 1 + (ninsert-1)/nper * nfreq; + + // print stats + + if (me == 0) { + if (screen) + fprintf(screen, + "Particle insertion: %d every %d steps, %d by step %d\n", + nper,nfreq,ninsert,nfinal); + if (logfile) + fprintf(logfile, + "Particle insertion: %d every %d steps, %d by step %d\n", + nper,nfreq,ninsert,nfinal); + } +} + +/* ---------------------------------------------------------------------- */ + +FixInsert::~FixInsert() +{ + delete random; + delete [] recvcounts; + delete [] displs; +} + +/* ---------------------------------------------------------------------- */ + +int FixInsert::setmask() +{ + int mask = 0; + mask |= PRE_EXCHANGE; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixInsert::init() +{ + // insure gravity fix exists + // for 3d must point in -z, for 2d must point in -y + // else insertion cannot work + + int ifix; + for (ifix = 0; ifix < modify->nfix; ifix++) + if (strcmp(modify->fix[ifix]->style,"gravity") == 0) break; + if (ifix == modify->nfix) + error->all("Must use fix gravity with fix insert"); + + double phi = ((FixGravity *) modify->fix[ifix])->phi; + double theta = ((FixGravity *) modify->fix[ifix])->theta; + double PI = 2.0 * asin(1.0); + double degree2rad = 2.0*PI / 360.0; + double xgrav = sin(degree2rad * theta) * cos(degree2rad * phi); + double ygrav = sin(degree2rad * theta) * sin(degree2rad * phi); + double zgrav = cos(degree2rad * theta); + + if (force->dimension == 3) { + if (fabs(xgrav) > EPSILON || fabs(ygrav) > EPSILON || + fabs(zgrav+1.0) > EPSILON) + error->all("Gravity must point in -z to use with fix insert in 3d"); + } else { + if (fabs(xgrav) > EPSILON || fabs(ygrav+1.0) > EPSILON || + fabs(zgrav) > EPSILON) + error->all("Gravity must point in -y to use with fix insert in 2d"); + } + + // check if a shear history fix exists + + ifix_history = -1; + if (force->pair_match("gran/history") || force->pair_match("gran/hertzian")) + for (int i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"SHEAR_HISTORY") == 0) ifix_history = i; +} + +/* ---------------------------------------------------------------------- + perform particle insertion +------------------------------------------------------------------------- */ + +void FixInsert::pre_exchange() +{ + int i; + + // just return if should not be called on this timestep + + if (next_reneighbor != update->ntimestep) return; + + // nnew = # to insert this timestep + + int nnew = nper; + if (ninserted + nnew > ninsert) nnew = ninsert - ninserted; + + // lo/hi current = z bounds of insertion region this timestep + + if (force->dimension == 3) { + lo_current = zlo + (update->ntimestep - nfirst) * update->dt * rate; + hi_current = zhi + (update->ntimestep - nfirst) * update->dt * rate; + } else { + lo_current = ylo + (update->ntimestep - nfirst) * update->dt * rate; + hi_current = yhi + (update->ntimestep - nfirst) * update->dt * rate; + } + + // ncount = # of my atoms that overlap the insertion region + // nprevious = total of ncount across all procs + + int ncount = 0; + for (i = 0; i < atom->nlocal; i++) + if (overlap(i)) ncount++; + + int nprevious; + MPI_Allreduce(&ncount,&nprevious,1,MPI_INT,MPI_SUM,world); + + // xmine is for my atoms + // xnear is for atoms from all procs + atoms to be inserted + + double **xmine = + memory->create_2d_double_array(ncount,4,"fix_insert:xmine"); + double **xnear = + memory->create_2d_double_array(nprevious+nnew,4,"fix_insert:xnear"); + int nnear = nprevious; + + // setup for allgatherv + + int n = 4*ncount; + MPI_Allgather(&n,1,MPI_INT,recvcounts,1,MPI_INT,world); + + displs[0] = 0; + for (int iproc = 1; iproc < nprocs; iproc++) + displs[iproc] = displs[iproc-1] + recvcounts[iproc-1]; + + // load up xmine array + + double **x = atom->x; + double *radius = atom->radius; + + ncount = 0; + for (i = 0; i < atom->nlocal; i++) + if (overlap(i)) { + xmine[ncount][0] = x[i][0]; + xmine[ncount][1] = x[i][1]; + xmine[ncount][2] = x[i][2]; + xmine[ncount][3] = radius[i]; + ncount++; + } + + // perform allgatherv to acquire list of nearby particles on all procs + + double *ptr = NULL; + if (ncount) ptr = xmine[0]; + MPI_Allgatherv(ptr,4*ncount,MPI_DOUBLE, + xnear[0],recvcounts,displs,MPI_DOUBLE,world); + + // insert new atoms into xnear list, one by one + // check against all nearby atoms and previously inserted ones + // if there is an overlap then try again at same z (3d) or y (2d) coord + // else insert by adding to xnear list + // max = maximum # of insertion attempts for all particles + // h = height, biased to give uniform distribution in time of insertion + + int success; + double xtmp,ytmp,ztmp,radtmp,delx,dely,delz,rsq,radsum,rn,h; + + int attempt = 0; + int max = nnew * maxattempt; + int ntotal = nprevious+nnew; + + while (nnear < ntotal) { + rn = random->uniform(); + h = hi_current - rn*rn * (hi_current-lo_current); + radtmp = radius_lo + random->uniform() * (radius_hi-radius_lo); + success = 0; + while (attempt < max) { + attempt++; + xyz_random(h,xtmp,ytmp,ztmp); + for (i = 0; i < nnear; i++) { + delx = xtmp - xnear[i][0]; + dely = ytmp - xnear[i][1]; + delz = ztmp - xnear[i][2]; + rsq = delx*delx + dely*dely + delz*delz; + radsum = radtmp + xnear[i][3]; + if (rsq <= radsum*radsum) break; + } + if (i == nnear) { + success = 1; + break; + } + } + if (success) { + xnear[nnear][0] = xtmp; + xnear[nnear][1] = ytmp; + xnear[nnear][2] = ztmp; + xnear[nnear][3] = radtmp; + nnear++; + } else break; + } + + // warn if not all insertions were performed + + ninserted += nnear-nprevious; + if (nnear - nprevious < nnew && me == 0) + error->warning("Less insertions than requested"); + + // add new atoms in my sub-box to my arrays + // initialize info about the atoms + // type, diameter, density set from fix parameters + // group mask set to "all" plus fix group + // z velocity set to what velocity would be if particle + // had fallen from top of insertion region + // this gives continuous stream of atoms + // set npartner for new atoms to 0 (assume not touching any others) + + int m; + double denstmp,vxtmp,vytmp,vztmp; + double g = 1.0; + + for (i = nprevious; i < nnear; i++) { + xtmp = xnear[i][0]; + ytmp = xnear[i][1]; + ztmp = xnear[i][2]; + radtmp = xnear[i][3]; + denstmp = density_lo + random->uniform() * (density_hi-density_lo); + if (force->dimension == 3) { + vxtmp = vxlo + random->uniform() * (vxhi-vxlo); + vytmp = vylo + random->uniform() * (vyhi-vylo); + vztmp = vz - sqrt(2.0*g*(hi_current-ztmp)); + } else { + vxtmp = vxlo + random->uniform() * (vxhi-vxlo); + vytmp = vy - sqrt(2.0*g*(hi_current-ytmp)); + vztmp = 0.0; + } + + if (xtmp >= domain->subxlo && xtmp < domain->subxhi && + ytmp >= domain->subylo && ytmp < domain->subyhi && + ztmp >= domain->subzlo && ztmp < domain->subzhi) { + atom->create_one(ntype,xtmp,ytmp,ztmp); + m = atom->nlocal - 1; + atom->type[m] = ntype; + atom->radius[m] = radtmp; + atom->density[m] = denstmp; + if (force->dimension == 3) + atom->rmass[m] = 4.0*PI/3.0 * radtmp*radtmp*radtmp * denstmp; + else + atom->rmass[m] = PI * radtmp*radtmp * denstmp; + atom->mask[m] = 1 | groupbit; + atom->v[m][0] = vxtmp; + atom->v[m][1] = vytmp; + atom->v[m][2] = vztmp; + if (ifix_history >= 0) + ((FixShearHistory *) modify->fix[ifix_history])->npartner[m] = 0; + } + } + + // tag # of new particles grow beyond all previous atoms + // reset global natoms + // if global map exists, reset it + + atom->tag_extend(); + atom->natoms += nnear - nprevious; + if (atom->map_style) { + atom->map_init(); + atom->map_set(); + } + + // free local memory + + memory->destroy_2d_double_array(xmine); + memory->destroy_2d_double_array(xnear); + + // next timestep to insert + + if (ninserted < ninsert) next_reneighbor += nfreq; + else next_reneighbor = 0; +} + +/* ---------------------------------------------------------------------- + check if particle i could overlap with a particle inserted into region + return 1 if yes, 0 if no + use maximum diameter for inserted particle +------------------------------------------------------------------------- */ + +int FixInsert::overlap(int i) +{ + double delta = radius_hi + atom->radius[i]; + double **x = atom->x; + + if (force->dimension == 3) { + if (region_style == 1) { + if (x[i][0] < xlo-delta || x[i][0] > xhi+delta || + x[i][1] < ylo-delta || x[i][1] > yhi+delta || + x[i][2] < lo_current-delta || x[i][2] > hi_current+delta) return 0; + } else { + if (x[i][2] < lo_current-delta || x[i][2] > hi_current+delta) return 0; + double delx = x[i][0] - xc; + double dely = x[i][1] - yc; + double rsq = delx*delx + dely*dely; + double r = rc + delta; + if (rsq > r*r) return 0; + } + } else { + if (x[i][0] < xlo-delta || x[i][0] > xhi+delta || + x[i][1] < lo_current-delta || x[i][1] > hi_current+delta) return 0; + } + + return 1; +} + +/* ---------------------------------------------------------------------- */ + +void FixInsert::xyz_random(double h, double &x, double &y, double &z) +{ + if (force->dimension == 3) { + if (region_style == 1) { + x = xlo + random->uniform() * (xhi-xlo); + y = ylo + random->uniform() * (yhi-ylo); + z = h; + } else { + double r1,r2; + while (1) { + r1 = random->uniform() - 0.5; + r2 = random->uniform() - 0.5; + if (r1*r1 + r2*r2 < 0.25) break; + } + x = xc + 2.0*r1*rc; + y = yc + 2.0*r2*rc; + z = h; + } + } else { + x = xlo + random->uniform() * (xhi-xlo); + y = h; + z = 0.0; + } +} diff --git a/src/GRANULAR/fix_insert.h b/src/GRANULAR/fix_insert.h new file mode 100644 index 0000000000000000000000000000000000000000..f8b89157a1eb79c76254866cb0c42257d6cc0cf2 --- /dev/null +++ b/src/GRANULAR/fix_insert.h @@ -0,0 +1,58 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_INSERT_H +#define FIX_INSERT_H + +#include "fix.h" + +class RanPark; + +class FixInsert : public Fix { + friend class PairGranHistory; + friend class PairGranHertzian; + friend class PairGranNoHistory; + + public: + FixInsert(int, char **); + ~FixInsert(); + int setmask(); + void init(); + void pre_exchange(); + + private: + int ninsert,ntype,seed; + double radius_lo,radius_hi; + double density_lo,density_hi; + double volfrac; + int maxattempt; + int region_style; + double rate; + double vxlo,vxhi,vylo,vyhi,vy,vz; + double xlo,xhi,ylo,yhi,zlo,zhi; + double xc,yc,rc; + + int me,nprocs; + int *recvcounts,*displs; + double PI; + int nfreq,nfirst,ninserted,nper; + double lo_current,hi_current; + int ifix_history; + + RanPark *random; + + int overlap(int); + void xyz_random(double, double &, double &, double &); +}; + +#endif diff --git a/src/GRANULAR/fix_nve_gran.cpp b/src/GRANULAR/fix_nve_gran.cpp new file mode 100644 index 0000000000000000000000000000000000000000..24e09d51d2f48358909c7d629c763c5a1d335275 --- /dev/null +++ b/src/GRANULAR/fix_nve_gran.cpp @@ -0,0 +1,123 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "stdio.h" +#include "string.h" +#include "fix_nve_gran.h" +#include "atom.h" +#include "update.h" +#include "force.h" +#include "error.h" + +// moments of inertia for sphere and disk + +#define INERTIA3D 0.4 +#define INERTIA2D 0.5 + +/* ---------------------------------------------------------------------- */ + +FixNVEGran::FixNVEGran(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 3) error->all("Illegal fix nve/gran command"); + + if (atom->check_style("granular") == 0) + error->all("Must use fix nve/gran with atom style granular"); +} + +/* ---------------------------------------------------------------------- */ + +int FixNVEGran::setmask() +{ + int mask = 0; + mask |= INITIAL_INTEGRATE; + mask |= FINAL_INTEGRATE; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixNVEGran::init() +{ + dtv = update->dt; + dtf = 0.5 * update->dt * force->ftm2v; + if (force->dimension == 3) + dtfphi = 0.5 * update->dt * force->ftm2v / INERTIA3D; + else + dtfphi = 0.5 * update->dt * force->ftm2v / INERTIA2D; +} + +/* ---------------------------------------------------------------------- */ + +void FixNVEGran::initial_integrate() +{ + double dtfm; + + double **x = atom->x; + double **v = atom->v; + double **f = atom->f; + double **phix = atom->phix; + double **phiv = atom->phiv; + double **phia = atom->phia; + double *rmass = atom->rmass; + double *radius = atom->radius; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / rmass[i]; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + x[i][0] += dtv * v[i][0]; + x[i][1] += dtv * v[i][1]; + x[i][2] += dtv * v[i][2]; + dtfm = dtfphi / (radius[i]*radius[i]*rmass[i]); + phiv[i][0] += dtfm * phia[i][0]; + phiv[i][1] += dtfm * phia[i][1]; + phiv[i][2] += dtfm * phia[i][2]; + phix[i][0] += dtv * phiv[i][0]; + phix[i][1] += dtv * phiv[i][1]; + phix[i][2] += dtv * phiv[i][2]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixNVEGran::final_integrate() +{ + double dtfm; + + double **v = atom->v; + double **f = atom->f; + double **phiv = atom->phiv; + double **phia = atom->phia; + double *rmass = atom->rmass; + double *radius = atom->radius; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / rmass[i]; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + dtfm = dtfphi / (radius[i]*radius[i]*rmass[i]); + phiv[i][0] += dtfm * phia[i][0]; + phiv[i][1] += dtfm * phia[i][1]; + phiv[i][2] += dtfm * phia[i][2]; + } + } +} diff --git a/src/GRANULAR/fix_nve_gran.h b/src/GRANULAR/fix_nve_gran.h new file mode 100644 index 0000000000000000000000000000000000000000..2da678aa94ba059477eae89146651853d000dfe3 --- /dev/null +++ b/src/GRANULAR/fix_nve_gran.h @@ -0,0 +1,32 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_NVE_GRAN_H +#define FIX_NVE_GRAN_H + +#include "fix.h" + +class FixNVEGran : public Fix { + public: + FixNVEGran(int, char **); + ~FixNVEGran() {} + int setmask(); + void init(); + void initial_integrate(); + void final_integrate(); + + private: + double dtv,dtf,dtfphi; +}; + +#endif diff --git a/src/GRANULAR/fix_shear_history.cpp b/src/GRANULAR/fix_shear_history.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0c509a952237b0769335d8067eb9053f90123eac --- /dev/null +++ b/src/GRANULAR/fix_shear_history.cpp @@ -0,0 +1,282 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "stdio.h" +#include "fix_shear_history.h" +#include "atom.h" +#include "neighbor.h" +#include "force.h" +#include "update.h" +#include "modify.h" +#include "memory.h" +#include "error.h" + +#define MAXTOUCH 15 + +/* ---------------------------------------------------------------------- */ + +FixShearHistory::FixShearHistory(int narg, char **arg) : Fix(narg, arg) +{ + restart_peratom = 1; + + // perform initial allocation of atom-based arrays + // register with atom class + + npartner = NULL; + partner = NULL; + shearpartner = NULL; + grow_arrays(atom->nmax); + atom->add_callback(0); + atom->add_callback(1); + + // initialize npartner to 0 so neighbor list creation is OK the 1st time + + int nlocal = atom->nlocal; + for (int i = 0; i < nlocal; i++) npartner[i] = 0; +} + +/* ---------------------------------------------------------------------- */ + +FixShearHistory::~FixShearHistory() +{ + // if atom class still exists: + // unregister this fix so atom class doesn't invoke it any more + + if (atom) atom->delete_callback(id,0); + if (atom) atom->delete_callback(id,1); + + // delete locally stored arrays + + memory->sfree(npartner); + memory->destroy_2d_int_array(partner); + memory->destroy_3d_double_array(shearpartner); +} + +/* ---------------------------------------------------------------------- */ + +int FixShearHistory::setmask() +{ + int mask = 0; + mask |= PRE_EXCHANGE; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixShearHistory::init() +{ + if (atom->tag_enable == 0) + error->all("Pair style granular with history requires atoms have IDs"); +} + +/* ---------------------------------------------------------------------- + copy shear partner info from neighbor lists to atom arrays + so can be exchanged with atoms +------------------------------------------------------------------------- */ + +void FixShearHistory::pre_exchange() +{ + int i,j,k,m; + + // zero npartners for all current atoms + + int nlocal = atom->nlocal; + for (i = 0; i < nlocal; i++) npartner[i] = 0; + + // copy shear info from neighbor list atoms to atom arrays + // nlocal = nlocal_neighbor = nlocal when neighbor list last built, + // which might be pre-insert on this step + + int numneigh; + int *neighs,*touch; + double *firstshear,*shear; + int *tag = atom->tag; + nlocal = neighbor->nlocal_neighbor; + + for (i = 0; i < nlocal; i++) { + neighs = neighbor->firstneigh[i]; + touch = neighbor->firsttouch[i]; + firstshear = neighbor->firstshear[i]; + numneigh = neighbor->numneigh[i]; + for (k = 0; k < numneigh; k++) { + if (touch[k]) { + shear = &firstshear[3*k]; + j = neighs[k]; + if (npartner[i] < MAXTOUCH) { + m = npartner[i]; + partner[i][m] = tag[j]; + shearpartner[i][m][0] = shear[0]; + shearpartner[i][m][1] = shear[1]; + shearpartner[i][m][2] = shear[2]; + } + npartner[i]++; + if (j < nlocal) { + if (npartner[j] < MAXTOUCH) { + m = npartner[j]; + partner[j][m] = tag[i]; + shearpartner[j][m][0] = -shear[0]; + shearpartner[j][m][1] = -shear[1]; + shearpartner[j][m][2] = -shear[2]; + } + npartner[j]++; + } + } + } + } + + // test for too many touching neighbors + + int flag = 0; + for (i = 0; i < nlocal; i++) + if (npartner[i] >= MAXTOUCH) flag = 1; + int flag_all; + MPI_Allreduce(&flag,&flag_all,1,MPI_INT,MPI_SUM,world); + if (flag_all) error->all("Too many touching neighbors - boost MAXTOUCH"); +} + +/* ---------------------------------------------------------------------- + memory usage of local atom-based arrays +------------------------------------------------------------------------- */ + +int FixShearHistory::memory_usage() +{ + int nmax = atom->nmax; + int bytes = nmax * sizeof(int); + bytes += nmax*MAXTOUCH * sizeof(int); + bytes += nmax*MAXTOUCH*3 * sizeof(double); + return bytes; +} + +/* ---------------------------------------------------------------------- + allocate local atom-based arrays +------------------------------------------------------------------------- */ + +void FixShearHistory::grow_arrays(int nmax) +{ + npartner = (int *) memory->srealloc(npartner,nmax*sizeof(int), + "shear_history:npartner"); + partner = memory->grow_2d_int_array(partner,nmax,MAXTOUCH, + "shear_history:partner"); + shearpartner = + memory->grow_3d_double_array(shearpartner,nmax,MAXTOUCH,3, + "shear_history:shearpartner"); +} + +/* ---------------------------------------------------------------------- + copy values within local atom-based arrays +------------------------------------------------------------------------- */ + +void FixShearHistory::copy_arrays(int i, int j) +{ + npartner[j] = npartner[i]; + for (int m = 0; m < npartner[j]; m++) { + partner[j][m] = partner[i][m]; + shearpartner[j][m][0] = shearpartner[i][m][0]; + shearpartner[j][m][1] = shearpartner[i][m][1]; + shearpartner[j][m][2] = shearpartner[i][m][2]; + } +} + +/* ---------------------------------------------------------------------- + pack values in local atom-based arrays for exchange with another proc +------------------------------------------------------------------------- */ + +int FixShearHistory::pack_exchange(int i, double *buf) +{ + int m = 0; + buf[m++] = npartner[i]; + for (int n = 0; n < npartner[i]; n++) { + buf[m++] = partner[i][n]; + buf[m++] = shearpartner[i][n][0]; + buf[m++] = shearpartner[i][n][1]; + buf[m++] = shearpartner[i][n][2]; + } + return m; +} + +/* ---------------------------------------------------------------------- + unpack values in local atom-based arrays from exchange with another proc +------------------------------------------------------------------------- */ + +int FixShearHistory::unpack_exchange(int nlocal, double *buf) +{ + int m = 0; + npartner[nlocal] = static_cast<int> (buf[m++]); + for (int n = 0; n < npartner[nlocal]; n++) { + partner[nlocal][n] = static_cast<int> (buf[m++]); + shearpartner[nlocal][n][0] = buf[m++]; + shearpartner[nlocal][n][1] = buf[m++]; + shearpartner[nlocal][n][2] = buf[m++]; + } + return m; +} + +/* ---------------------------------------------------------------------- + pack values in local atom-based arrays for restart file +------------------------------------------------------------------------- */ + +int FixShearHistory::pack_restart(int i, double *buf) +{ + int m = 0; + buf[m++] = 4*npartner[i] + 2; + buf[m++] = npartner[i]; + for (int n = 0; n < npartner[i]; n++) { + buf[m++] = partner[i][n]; + buf[m++] = shearpartner[i][n][0]; + buf[m++] = shearpartner[i][n][1]; + buf[m++] = shearpartner[i][n][2]; + } + return m; +} + +/* ---------------------------------------------------------------------- + unpack values from atom->extra array to restart the fix +------------------------------------------------------------------------- */ + +void FixShearHistory::unpack_restart(int nlocal, int nth) +{ + double **extra = atom->extra; + + // skip to Nth set of extra values + + int m = 0; + for (int i = 0; i < nth; i++) m += static_cast<int> (extra[nlocal][m]); + m++; + + npartner[nlocal] = static_cast<int> (extra[nlocal][m++]); + for (int n = 0; n < npartner[nlocal]; n++) { + partner[nlocal][n] = static_cast<int> (extra[nlocal][m++]); + shearpartner[nlocal][n][0] = extra[nlocal][m++]; + shearpartner[nlocal][n][1] = extra[nlocal][m++]; + shearpartner[nlocal][n][2] = extra[nlocal][m++]; + } +} + +/* ---------------------------------------------------------------------- + maxsize of any atom's restart data +------------------------------------------------------------------------- */ + +int FixShearHistory::maxsize_restart() +{ + return 4*MAXTOUCH + 2; +} + +/* ---------------------------------------------------------------------- + size of atom nlocal's restart data +------------------------------------------------------------------------- */ + +int FixShearHistory::size_restart(int nlocal) +{ + return 4*npartner[nlocal] + 2; +} diff --git a/src/GRANULAR/fix_wall_gran.cpp b/src/GRANULAR/fix_wall_gran.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8c1012f14aa526fb4e3a2405df21fabd02da5abd --- /dev/null +++ b/src/GRANULAR/fix_wall_gran.cpp @@ -0,0 +1,750 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Leo Silbert (SNL), Gary Grest (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "string.h" +#include "fix_wall_gran.h" +#include "atom.h" +#include "update.h" +#include "force.h" +#include "modify.h" +#include "pair_gran_no_history.h" +#include "pair_gran_history.h" +#include "pair_gran_hertzian.h" +#include "memory.h" +#include "error.h" + +#define XPLANE 1 +#define YPLANE 2 +#define ZPLANE 3 +#define ZCYLINDER 4 + +#define NO_HISTORY 1 +#define HISTORY 2 +#define HERTZIAN 3 + +#define BIG 1.0e20 + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +/* ---------------------------------------------------------------------- */ + +FixWallGran::FixWallGran(int narg, char **arg) : Fix(narg, arg) +{ + if (narg < 4) error->all("Illegal fix wall/gran command"); + + if (atom->check_style("granular") == 0) + error->all("Must use fix wall/gran with atom style granular"); + + restart_peratom = 1; + + int iarg; + if (strcmp(arg[3],"xplane") == 0) { + iarg = 8; + if (narg < iarg) error->all("Illegal fix wall/gran command"); + wallstyle = XPLANE; + if (strcmp(arg[4],"NULL") == 0) lo = -BIG; + else lo = atof(arg[4]); + if (strcmp(arg[5],"NULL") == 0) hi = BIG; + else hi = atof(arg[5]); + gamman = atof(arg[6]); + xmu = atof(arg[7]); + } else if (strcmp(arg[3],"yplane") == 0) { + iarg = 8; + if (narg < iarg) error->all("Illegal fix wall/gran command"); + wallstyle = YPLANE; + if (strcmp(arg[4],"NULL") == 0) lo = -BIG; + else lo = atof(arg[4]); + if (strcmp(arg[5],"NULL") == 0) hi = BIG; + else hi = atof(arg[5]); + gamman = atof(arg[6]); + xmu = atof(arg[7]); + } else if (strcmp(arg[3],"zplane") == 0) { + iarg = 8; + if (narg < iarg) error->all("Illegal fix wall/gran command"); + wallstyle = ZPLANE; + if (strcmp(arg[4],"NULL") == 0) lo = -BIG; + else lo = atof(arg[4]); + if (strcmp(arg[5],"NULL") == 0) hi = BIG; + else hi = atof(arg[5]); + gamman = atof(arg[6]); + xmu = atof(arg[7]); + } else if (strcmp(arg[3],"zcylinder") == 0) { + iarg = 7; + if (narg < iarg) error->all("Illegal fix wall/gran command"); + wallstyle = ZCYLINDER; + lo = hi = 0.0; + cylradius = atof(arg[4]); + gamman = atof(arg[5]); + xmu = atof(arg[6]); + } + + // check for trailing keyword/values + + wiggle = 0; + + while (iarg < narg) { + if (strcmp(arg[iarg],"wiggle") == 0) { + if (iarg+4 > narg) error->all("Illegal fix wall/gran command"); + if (strcmp(arg[iarg+1],"x") == 0) axis = 0; + else if (strcmp(arg[iarg+1],"y") == 0) axis = 1; + else if (strcmp(arg[iarg+1],"z") == 0) axis = 2; + else error->all("Illegal fix wall/gran command"); + amplitude = atof(arg[iarg+2]); + period = atof(arg[iarg+3]); + wiggle = 1; + iarg += 4; + } else error->all("Illegal fix wall/gran command"); + } + + if (wallstyle == ZCYLINDER && wiggle) + if (axis != 2) error->all("Can only wiggle zcylinder wall in z dim"); + + // setup oscillations + + if (wiggle) { + double PI = 4.0 * atan(1.0); + omega = 2.0*PI / period; + time_origin = update->ntimestep; + } + + // perform initial allocation of atom-based arrays + // register with atom class + + shear = NULL; + grow_arrays(atom->nmax); + atom->add_callback(0); + atom->add_callback(1); + + // initialize as if particle is not touching wall + + int nlocal = atom->nlocal; + for (int i = 0; i < nlocal; i++) + shear[i][0] = shear[i][1] = shear[i][2] = 0.0; +} + +/* ---------------------------------------------------------------------- */ + +FixWallGran::~FixWallGran() +{ + // if atom class still exists: + // unregister this fix so atom class doesn't invoke it any more + + if (atom) atom->delete_callback(id,0); + if (atom) atom->delete_callback(id,1); + + // delete locally stored arrays + + memory->destroy_2d_double_array(shear); +} + +/* ---------------------------------------------------------------------- */ + +int FixWallGran::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixWallGran::init() +{ + // set constants that depend on pair style + + Pair *anypair; + if (anypair = force->pair_match("gran/no_history")) { + historystyle = 0; + pairstyle = NO_HISTORY; + xkk = ((PairGranNoHistory *) anypair)->xkk; + xkkt = ((PairGranNoHistory *) anypair)->xkkt; + } else if (anypair = force->pair_match("gran/history")) { + historystyle = 1; + pairstyle = HISTORY; + xkk = ((PairGranHistory *) anypair)->xkk; + xkkt = ((PairGranHistory *) anypair)->xkkt; + } else if (anypair = force->pair_match("gran/hertzian")) { + historystyle = 1; + pairstyle = HERTZIAN; + xkk = ((PairGranHertzian *) anypair)->xkk; + xkkt = ((PairGranHertzian *) anypair)->xkkt; + } else + error->all("Fix wall/gran can only be used with granular pair style"); + + // friction coeffs + + dt = update->dt; + gamman_dl = gamman/dt; + gammas_dl = 0.5*gamman_dl; +} + +/* ---------------------------------------------------------------------- */ + +void FixWallGran::setup() +{ + post_force(1); +} + +/* ---------------------------------------------------------------------- */ + +void FixWallGran::post_force(int vflag) +{ + double vwall[3],dx,dy,dz,del1,del2,delxy,delr,rsq; + + // set position of wall to initial settings and velocity to 0.0 + // if wiggle, set wall position and velocity accordingly + + double wlo = lo; + double whi = hi; + vwall[0] = vwall[1] = vwall[2] = 0.0; + if (wiggle) { + double arg = omega * (update->ntimestep - time_origin) * dt; + wlo = lo + amplitude - amplitude*cos(arg); + whi = hi + amplitude - amplitude*cos(arg); + vwall[axis] = dt * amplitude*omega*sin(arg); + } + + // loop over all my atoms + // rsq = distance from wall + // dx,dy,dz = signed distance from wall + // in cylinder case + // skip atom if not close enough to wall + // if wall was set to NULL, it's skipped since lo/hi are infinity + // compute force and torque on atom if close enough to wall + // via wall potential matched to pair potential + // set shear if pair potential stores history + + double **x = atom->x; + double **v = atom->v; + double **f = atom->f; + double **phiv = atom->phiv; + double **phia = atom->phia; + double *radius = atom->radius; + double *rmass = atom->rmass; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + + dx = dy = dz = 0.0; + + if (wallstyle == XPLANE) { + del1 = x[i][0] - wlo; + del2 = whi - x[i][0]; + if (del1 < del2) dx = del1; + else dx = -del2; + } else if (wallstyle == YPLANE) { + del1 = x[i][1] - wlo; + del2 = whi - x[i][1]; + if (del1 < del2) dy = del1; + else dy = -del2; + } else if (wallstyle == ZPLANE) { + del1 = x[i][2] - wlo; + del2 = whi - x[i][2]; + if (del1 < del2) dz = del1; + else dz = -del2; + } else if (wallstyle == ZCYLINDER) { + delxy = sqrt(x[i][0]*x[i][0] + x[i][1]*x[i][1]); + delr = cylradius - delxy; + if (delr > radius[i]) dz = cylradius; + else { + dx = -delr/delxy * x[i][0]; + dy = -delr/delxy * x[i][1]; + } + } + + rsq = dx*dx + dy*dy + dz*dz; + + if (rsq > radius[i]*radius[i]) { + if (historystyle) { + shear[i][0] = 0.0; + shear[i][1] = 0.0; + shear[i][2] = 0.0; + } + } else { + if (pairstyle == NO_HISTORY) + no_history(rsq,dx,dy,dz,vwall,v[i],f[i],phiv[i],phia[i], + radius[i],rmass[i]); + else if (pairstyle == HISTORY) + history(rsq,dx,dy,dz,vwall,v[i],f[i],phiv[i],phia[i], + radius[i],rmass[i],shear[i]); + else if (pairstyle == HERTZIAN) + hertzian(rsq,dx,dy,dz,vwall,v[i],f[i],phiv[i],phia[i], + radius[i],rmass[i],shear[i]); + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixWallGran::no_history(double rsq, double dx, double dy, double dz, + double *vwall, double *v, + double *f, double *phiv, double *phia, + double radius, double mass) +{ + double r,vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3; + double wr1,wr2,wr3,xmeff,damp,ccel,vtr1,vtr2,vtr3,vrel; + double fn,fs,ft,fs1,fs2,fs3,ccelx,ccely,ccelz,tor1,tor2,tor3; + + r = sqrt(rsq); + + // relative translational velocity + + vr1 = v[0] - vwall[0]; + vr2 = v[1] - vwall[1]; + vr3 = v[2] - vwall[2]; + + vr1 *= dt; + vr2 *= dt; + vr3 *= dt; + + // normal component + + vnnr = vr1*dx + vr2*dy + vr3*dz; + vn1 = dx*vnnr / rsq; + vn2 = dy*vnnr / rsq; + vn3 = dz*vnnr / rsq; + + // tangential component + + vt1 = vr1 - vn1; + vt2 = vr2 - vn2; + vt3 = vr3 - vn3; + + // relative rotational velocity + + wr1 = radius*phiv[0]; + wr2 = radius*phiv[1]; + wr3 = radius*phiv[2]; + + wr1 *= dt/r; + wr2 *= dt/r; + wr3 *= dt/r; + + // normal damping term + // this definition of DAMP includes the extra 1/r term + + xmeff = mass; + damp = xmeff*gamman_dl*vnnr/rsq; + ccel = xkk*(radius-r)/r - damp; + + // relative velocities + + vtr1 = vt1 - (dz*wr2-dy*wr3); + vtr2 = vt2 - (dx*wr3-dz*wr1); + vtr3 = vt3 - (dy*wr1-dx*wr2); + vrel = vtr1*vtr1 + vtr2*vtr2 + vtr3*vtr3; + vrel = sqrt(vrel); + + // force normalization + + fn = xmu * fabs(ccel*r); + fs = xmeff*gammas_dl*vrel; + if (vrel != 0.0) ft = MIN(fn,fs) / vrel; + else ft = 0.0; + + // shear friction forces + + fs1 = -ft*vtr1; + fs2 = -ft*vtr2; + fs3 = -ft*vtr3; + + // force components + + ccelx = dx*ccel + fs1; + ccely = dy*ccel + fs2; + ccelz = dz*ccel + fs3; + + // forces + + f[0] += ccelx; + f[1] += ccely; + f[2] += ccelz; + + // torques + + tor1 = dy*fs3 - dz*fs2; + tor2 = dz*fs1 - dx*fs3; + tor3 = dx*fs2 - dy*fs1; + phia[0] -= radius*tor1; + phia[1] -= radius*tor2; + phia[2] -= radius*tor3; +} + +/* ---------------------------------------------------------------------- */ + +void FixWallGran::history(double rsq, double dx, double dy, double dz, + double *vwall, double *v, + double *f, double *phiv, double *phia, + double radius, double mass, double *shear) +{ + double r,vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3; + double wr1,wr2,wr3,xmeff,damp,ccel,vtr1,vtr2,vtr3,vrel; + double fn,fs,fs1,fs2,fs3,ccelx,ccely,ccelz,tor1,tor2,tor3; + double shrmag,rsht; + + r = sqrt(rsq); + + // relative translational velocity + + vr1 = v[0] - vwall[0]; + vr2 = v[1] - vwall[1]; + vr3 = v[2] - vwall[2]; + + vr1 *= dt; + vr2 *= dt; + vr3 *= dt; + + // normal component + + vnnr = vr1*dx + vr2*dy + vr3*dz; + vn1 = dx*vnnr / rsq; + vn2 = dy*vnnr / rsq; + vn3 = dz*vnnr / rsq; + + // tangential component + + vt1 = vr1 - vn1; + vt2 = vr2 - vn2; + vt3 = vr3 - vn3; + + // relative rotational velocity + + wr1 = radius*phiv[0]; + wr2 = radius*phiv[1]; + wr3 = radius*phiv[2]; + + wr1 *= dt/r; + wr2 *= dt/r; + wr3 *= dt/r; + + // normal damping term + // this definition of DAMP includes the extra 1/r term + + xmeff = mass; + damp = xmeff*gamman_dl*vnnr/rsq; + ccel = xkk*(radius-r)/r - damp; + + // relative velocities + + vtr1 = vt1 - (dz*wr2-dy*wr3); + vtr2 = vt2 - (dx*wr3-dz*wr1); + vtr3 = vt3 - (dy*wr1-dx*wr2); + vrel = vtr1*vtr1 + vtr2*vtr2 + vtr3*vtr3; + vrel = sqrt(vrel); + + // shear history effects + + shear[0] += vtr1; + shear[1] += vtr2; + shear[2] += vtr3; + shrmag = sqrt(shear[0]*shear[0] + shear[1]*shear[1] + shear[2]*shear[2]); + + // rotate shear displacements correctly + + rsht = shear[0]*dx + shear[1]*dy + shear[2]*dz; + rsht = rsht/rsq; + shear[0] -= rsht*dx; + shear[1] -= rsht*dy; + shear[2] -= rsht*dz; + + // tangential forces + + fs1 = - (xkkt*shear[0] + xmeff*gammas_dl*vtr1); + fs2 = - (xkkt*shear[1] + xmeff*gammas_dl*vtr2); + fs3 = - (xkkt*shear[2] + xmeff*gammas_dl*vtr3); + + // force normalization + + fs = sqrt(fs1*fs1 + fs2*fs2 + fs3*fs3); + fn = xmu * fabs(ccel*r); + + // shrmag is magnitude of shearwall + // rescale frictional displacements and forces if needed + + if (fs > fn) { + if (shrmag != 0.0) { + shear[0] = (fn/fs) * (shear[0] + xmeff*gammas_dl*vtr1/xkkt) - + xmeff*gammas_dl*vtr1/xkkt; + shear[1] = (fn/fs) * (shear[1] + xmeff*gammas_dl*vtr2/xkkt) - + xmeff*gammas_dl*vtr2/xkkt; + shear[2] = (fn/fs) * (shear[2] + xmeff*gammas_dl*vtr3/xkkt) - + xmeff*gammas_dl*vtr3/xkkt; + fs1 = fs1 * fn / fs ; + fs2 = fs2 * fn / fs; + fs3 = fs3 * fn / fs; + } else fs1 = fs2 = fs3 = 0.0; + } + + ccelx = dx*ccel + fs1; + ccely = dy*ccel + fs2; + ccelz = dz*ccel + fs3; + + // forces + + f[0] += ccelx; + f[1] += ccely; + f[2] += ccelz; + + // torques + + tor1 = dy*fs3 - dz*fs2; + tor2 = dz*fs1 - dx*fs3; + tor3 = dx*fs2 - dy*fs1; + phia[0] -= radius*tor1; + phia[1] -= radius*tor2; + phia[2] -= radius*tor3; +} + +/* ---------------------------------------------------------------------- */ + +void FixWallGran::hertzian(double rsq, double dx, double dy, double dz, + double *vwall, double *v, + double *f, double *phiv, double *phia, + double radius, double mass, double *shear) +{ + double r,vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3; + double wr1,wr2,wr3,xmeff,damp,ccel,vtr1,vtr2,vtr3,vrel; + double fn,fs,fs1,fs2,fs3,ccelx,ccely,ccelz,tor1,tor2,tor3; + double shrmag,rsht,rhertz; + + r = sqrt(rsq); + + // relative translational velocity + + vr1 = v[0] - vwall[0]; + vr2 = v[1] - vwall[1]; + vr3 = v[2] - vwall[2]; + + vr1 *= dt; + vr2 *= dt; + vr3 *= dt; + + // normal component + + vnnr = vr1*dx + vr2*dy + vr3*dz; + vn1 = dx*vnnr / rsq; + vn2 = dy*vnnr / rsq; + vn3 = dz*vnnr / rsq; + + // tangential component + + vt1 = vr1 - vn1; + vt2 = vr2 - vn2; + vt3 = vr3 - vn3; + + // relative rotational velocity + + wr1 = radius*phiv[0]; + wr2 = radius*phiv[1]; + wr3 = radius*phiv[2]; + + wr1 *= dt/r; + wr2 *= dt/r; + wr3 *= dt/r; + + // normal damping term + // this definition of DAMP includes the extra 1/r term + + xmeff = mass; + damp = xmeff*gamman_dl*vnnr/rsq; + ccel = xkk*(radius-r)/r - damp; + rhertz = sqrt(radius - r); + ccel = rhertz * ccel; + + // relative velocities + + vtr1 = vt1 - (dz*wr2-dy*wr3); + vtr2 = vt2 - (dx*wr3-dz*wr1); + vtr3 = vt3 - (dy*wr1-dx*wr2); + vrel = vtr1*vtr1 + vtr2*vtr2 + vtr3*vtr3; + vrel = sqrt(vrel); + + // shear history effects + + shear[0] += vtr1; + shear[1] += vtr2; + shear[2] += vtr3; + shrmag = sqrt(shear[0]*shear[0] + shear[1]*shear[1] + shear[2]*shear[2]); + + // rotate shear displacements correctly + + rsht = shear[0]*dx + shear[1]*dy + shear[2]*dz; + rsht = rsht/rsq; + shear[0] -= rsht*dx; + shear[1] -= rsht*dy; + shear[2] -= rsht*dz; + + // tangential forces + + fs1 = -rhertz * (xkkt*shear[0] + xmeff*gammas_dl*vtr1); + fs2 = -rhertz * (xkkt*shear[1] + xmeff*gammas_dl*vtr2); + fs3 = -rhertz * (xkkt*shear[2] + xmeff*gammas_dl*vtr3); + + // force normalization + + fs = sqrt(fs1*fs1 + fs2*fs2 + fs3*fs3); + fn = xmu * fabs(ccel*r); + + // shrmag is magnitude of shearwall + // rescale frictional displacements and forces if needed + + if (fs > fn) { + if (shrmag != 0.0) { + shear[0] = (fn/fs) * (shear[0] + xmeff*gammas_dl*vtr1/xkkt) - + xmeff*gammas_dl*vtr1/xkkt; + shear[1] = (fn/fs) * (shear[1] + xmeff*gammas_dl*vtr2/xkkt) - + xmeff*gammas_dl*vtr2/xkkt; + shear[2] = (fn/fs) * (shear[2] + xmeff*gammas_dl*vtr3/xkkt) - + xmeff*gammas_dl*vtr3/xkkt; + fs1 = fs1 * fn / fs ; + fs2 = fs2 * fn / fs; + fs3 = fs3 * fn / fs; + } else fs1 = fs2 = fs3 = 0.0; + } + + ccelx = dx*ccel + fs1; + ccely = dy*ccel + fs2; + ccelz = dz*ccel + fs3; + + // forces + + f[0] += ccelx; + f[1] += ccely; + f[2] += ccelz; + + // torques + + tor1 = dy*fs3 - dz*fs2; + tor2 = dz*fs1 - dx*fs3; + tor3 = dx*fs2 - dy*fs1; + phia[0] -= radius*tor1; + phia[1] -= radius*tor2; + phia[2] -= radius*tor3; +} + +/* ---------------------------------------------------------------------- + memory usage of local atom-based arrays +------------------------------------------------------------------------- */ + +int FixWallGran::memory_usage() +{ + int nmax = atom->nmax; + int bytes = nmax * sizeof(int); + bytes += 3*nmax * sizeof(double); + return bytes; +} + +/* ---------------------------------------------------------------------- + allocate local atom-based arrays +------------------------------------------------------------------------- */ + +void FixWallGran::grow_arrays(int nmax) +{ + shear = memory->grow_2d_double_array(shear,nmax,3,"fix_wall_gran:shear"); +} + +/* ---------------------------------------------------------------------- + copy values within local atom-based arrays +------------------------------------------------------------------------- */ + +void FixWallGran::copy_arrays(int i, int j) +{ + shear[j][0] = shear[i][0]; + shear[j][1] = shear[i][1]; + shear[j][2] = shear[i][2]; +} + +/* ---------------------------------------------------------------------- + pack values in local atom-based arrays for exchange with another proc +------------------------------------------------------------------------- */ + +int FixWallGran::pack_exchange(int i, double *buf) +{ + buf[0] = shear[i][0]; + buf[1] = shear[i][1]; + buf[2] = shear[i][2]; + return 3; +} + +/* ---------------------------------------------------------------------- + unpack values into local atom-based arrays after exchange +------------------------------------------------------------------------- */ + +int FixWallGran::unpack_exchange(int nlocal, double *buf) +{ + shear[nlocal][0] = buf[0]; + shear[nlocal][1] = buf[1]; + shear[nlocal][2] = buf[2]; + return 3; +} + +/* ---------------------------------------------------------------------- + pack values in local atom-based arrays for restart file +------------------------------------------------------------------------- */ + +int FixWallGran::pack_restart(int i, double *buf) +{ + int m = 0; + buf[m++] = 4; + buf[m++] = shear[i][0]; + buf[m++] = shear[i][1]; + buf[m++] = shear[i][2]; + return m; +} + +/* ---------------------------------------------------------------------- + unpack values from atom->extra array to restart the fix +------------------------------------------------------------------------- */ + +void FixWallGran::unpack_restart(int nlocal, int nth) +{ + double **extra = atom->extra; + + // skip to Nth set of extra values + + int m = 0; + for (int i = 0; i < nth; i++) m += static_cast<int> (extra[nlocal][m]); + m++; + + shear[nlocal][0] = extra[nlocal][m++]; + shear[nlocal][1] = extra[nlocal][m++]; + shear[nlocal][2] = extra[nlocal][m++]; +} + +/* ---------------------------------------------------------------------- + maxsize of any atom's restart data +------------------------------------------------------------------------- */ + +int FixWallGran::maxsize_restart() +{ + return 4; +} + +/* ---------------------------------------------------------------------- + size of atom nlocal's restart data +------------------------------------------------------------------------- */ + +int FixWallGran::size_restart(int nlocal) +{ + return 4; +} diff --git a/src/GRANULAR/fix_wall_gran.h b/src/GRANULAR/fix_wall_gran.h new file mode 100644 index 0000000000000000000000000000000000000000..5b7d2b3307384eeb748a788c280981e15170a2cc --- /dev/null +++ b/src/GRANULAR/fix_wall_gran.h @@ -0,0 +1,58 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_WALL_GRAN_H +#define FIX_WALL_GRAN_H + +#include "fix.h" + +class FixWallGran : public Fix { + public: + FixWallGran(int, char **); + ~FixWallGran(); + int setmask(); + void init(); + void setup(); + void post_force(int); + + int memory_usage(); + void grow_arrays(int); + void copy_arrays(int, int); + int pack_exchange(int, double *); + int unpack_exchange(int, double *); + int pack_restart(int, double *); + void unpack_restart(int, int); + int size_restart(int); + int maxsize_restart(); + + private: + int wallstyle,pairstyle,historystyle,wiggle,axis; + double xkk,xkkt,gamman,xmu; + double lo,hi,cylradius; + double dt,gamman_dl,gammas_dl; + double amplitude,period,omega,time_origin; + + int *touch; + double **shear; + + void no_history(double, double, double, double, double *, + double *, double *, double *, double *, double, double); + void history(double, double, double, double, double *, + double *, double *, double *, double *, double, double, + double *); + void hertzian(double, double, double, double, double *, + double *, double *, double *, double *, double, double, + double *); +}; + +#endif diff --git a/src/GRANULAR/pair_gran_hertzian.cpp b/src/GRANULAR/pair_gran_hertzian.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5180397114d84c7dffab803b2f16eb833767e531 --- /dev/null +++ b/src/GRANULAR/pair_gran_hertzian.cpp @@ -0,0 +1,220 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Leo Silbert (SNL), Gary Grest (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "string.h" +#include "pair_gran_hertzian.h" +#include "atom.h" +#include "force.h" +#include "neighbor.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +void PairGranHertzian::compute(int eflag, int vflag) +{ + int i,j,k,numneigh; + double xtmp,ytmp,ztmp,delx,dely,delz; + double radi,radj,radsum,rsq,r,rinv; + double vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3; + double wr1,wr2,wr3; + double vtr1,vtr2,vtr3,vrel; + double xmeff,damp,ccel,ccelx,ccely,ccelz,tor1,tor2,tor3; + double fn,fs,fs1,fs2,fs3; + double shrmag,rsht,rhertz; + int *neighs,*touch; + double *firstshear,*shear; + + double **f = atom->f; + double **x = atom->x; + double **v = atom->v; + double **phiv = atom->phiv; + double **phia = atom->phia; + double *radius = atom->radius; + double *rmass = atom->rmass; + int *mask = atom->mask; + int nlocal = atom->nlocal; + int newton_pair = force->newton_pair; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + radi = radius[i]; + neighs = neighbor->firstneigh[i]; + touch = neighbor->firsttouch[i]; + firstshear = neighbor->firstshear[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + radj = radius[j]; + radsum = radi + radj; + + if (rsq >= radsum*radsum) { + + // unset touching neighbors + + touch[k] = 0; + shear = &firstshear[3*k]; + shear[0] = 0.0; + shear[1] = 0.0; + shear[2] = 0.0; + + } else { + r = sqrt(rsq); + + // relative translational velocity + + vr1 = v[i][0] - v[j][0]; + vr2 = v[i][1] - v[j][1]; + vr3 = v[i][2] - v[j][2]; + + vr1 *= dt; + vr2 *= dt; + vr3 *= dt; + + // normal component + + vnnr = vr1*delx + vr2*dely + vr3*delz; + vn1 = delx*vnnr / rsq; + vn2 = dely*vnnr / rsq; + vn3 = delz*vnnr / rsq; + + // tangential component + + vt1 = vr1 - vn1; + vt2 = vr2 - vn2; + vt3 = vr3 - vn3; + + // relative rotational velocity + + wr1 = radi*phiv[i][0] + radj*phiv[j][0]; + wr2 = radi*phiv[i][1] + radj*phiv[j][1]; + wr3 = radi*phiv[i][2] + radj*phiv[j][2]; + + wr1 *= dt/r; + wr2 *= dt/r; + wr3 *= dt/r; + + // normal damping term + // this definition of DAMP includes the extra 1/r term + + xmeff = rmass[i]*rmass[j] / (rmass[i]+rmass[j]); + if (mask[i] & freeze_group_bit) xmeff = rmass[j]; + if (mask[j] & freeze_group_bit) xmeff = rmass[i]; + damp = xmeff*gamman_dl*vnnr/rsq; + ccel = xkk*(radsum-r)/r - damp; + rhertz = sqrt(radsum - r); + ccel = rhertz * ccel; + + // relative velocities + + vtr1 = vt1 - (delz*wr2-dely*wr3); + vtr2 = vt2 - (delx*wr3-delz*wr1); + vtr3 = vt3 - (dely*wr1-delx*wr2); + vrel = vtr1*vtr1 + vtr2*vtr2 + vtr3*vtr3; + vrel = sqrt(vrel); + + // shear history effects + // shrmag = magnitude of shear + + touch[k] = 1; + shear = &firstshear[3*k]; + shear[0] += vtr1; + shear[1] += vtr2; + shear[2] += vtr3; + shrmag = sqrt(shear[0]*shear[0] + shear[1]*shear[1] + + shear[2]*shear[2]); + + // rotate shear displacements correctly + + rsht = shear[0]*delx + shear[1]*dely + shear[2]*delz; + rsht /= rsq; + shear[0] -= rsht*delx; + shear[1] -= rsht*dely; + shear[2] -= rsht*delz; + + // tangential forces + + fs1 = -rhertz * (xkkt*shear[0] + xmeff*gammas_dl*vtr1); + fs2 = -rhertz * (xkkt*shear[1] + xmeff*gammas_dl*vtr2); + fs3 = -rhertz * (xkkt*shear[2] + xmeff*gammas_dl*vtr3); + + // force normalization + // rescale frictional displacements and forces if needed + + fs = sqrt(fs1*fs1 + fs2*fs2 + fs3*fs3); + fn = xmu * fabs(ccel*r); + + if (fs > fn) { + if (shrmag != 0.0) { + shear[0] = (fn/fs) * (shear[0] + xmeff*gammas_dl*vtr1/xkkt) - + xmeff*gammas_dl*vtr1/xkkt; + shear[1] = (fn/fs) * (shear[1] + xmeff*gammas_dl*vtr2/xkkt) - + xmeff*gammas_dl*vtr2/xkkt; + shear[2] = (fn/fs) * (shear[2] + xmeff*gammas_dl*vtr3/xkkt) - + xmeff*gammas_dl*vtr3/xkkt; + fs1 *= fn/fs; + fs2 *= fn/fs; + fs3 *= fn/fs; + } else { + fs1 = 0.0; + fs2 = 0.0; + fs3 = 0.0; + } + } + + // forces & torques + + ccelx = delx*ccel + fs1; + ccely = dely*ccel + fs2; + ccelz = delz*ccel + fs3; + f[i][0] += ccelx; + f[i][1] += ccely; + f[i][2] += ccelz; + + rinv = 1/r; + tor1 = rinv * (dely*fs3 - delz*fs2); + tor2 = rinv * (delz*fs1 - delx*fs3); + tor3 = rinv * (delx*fs2 - dely*fs1); + phia[i][0] -= radi*tor1; + phia[i][1] -= radi*tor2; + phia[i][2] -= radi*tor3; + + if (newton_pair || j < nlocal) { + f[j][0] -= ccelx; + f[j][1] -= ccely; + f[j][2] -= ccelz; + phia[j][0] -= radj*tor1; + phia[j][1] -= radj*tor2; + phia[j][2] -= radj*tor3; + } + } + } + } +} diff --git a/src/GRANULAR/pair_gran_hertzian.h b/src/GRANULAR/pair_gran_hertzian.h new file mode 100644 index 0000000000000000000000000000000000000000..9a7504fe613f01805f11c14f6e110dc5d7e6b4e2 --- /dev/null +++ b/src/GRANULAR/pair_gran_hertzian.h @@ -0,0 +1,24 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_GRAN_HERTZIAN_H +#define PAIR_GRAN_HERTZIAN_H + +#include "pair_gran_history.h" + +class PairGranHertzian : public PairGranHistory { + public: + void compute(int, int); +}; + +#endif diff --git a/src/GRANULAR/pair_gran_history.cpp b/src/GRANULAR/pair_gran_history.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f3a44b7c0524ac7d62add4136ae3ae37fb890d9c --- /dev/null +++ b/src/GRANULAR/pair_gran_history.cpp @@ -0,0 +1,454 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Leo Silbert (SNL), Gary Grest (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "pair_gran_history.h" +#include "atom.h" +#include "domain.h" +#include "force.h" +#include "update.h" +#include "modify.h" +#include "fix.h" +#include "fix_insert.h" +#include "comm.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +PairGranHistory::PairGranHistory() +{ + single_enable = 0; + + for (int i = 0; i < 6; i++) virial[i] = 0.0; + ifix_history = -1; +} + +/* ---------------------------------------------------------------------- */ + +PairGranHistory::~PairGranHistory() +{ + if (ifix_history >= 0) modify->delete_fix("SHEAR_HISTORY"); + + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairGranHistory::compute(int eflag, int vflag) +{ + int i,j,k,numneigh; + double xtmp,ytmp,ztmp,delx,dely,delz; + double radi,radj,radsum,rsq,r,rinv; + double vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3; + double wr1,wr2,wr3; + double vtr1,vtr2,vtr3,vrel; + double xmeff,damp,ccel,ccelx,ccely,ccelz,tor1,tor2,tor3; + double fn,fs,fs1,fs2,fs3; + double shrmag,rsht; + int *neighs,*touch; + double *firstshear,*shear; + + double **f = atom->f; + double **x = atom->x; + double **v = atom->v; + double **phiv = atom->phiv; + double **phia = atom->phia; + double *radius = atom->radius; + double *rmass = atom->rmass; + int *mask = atom->mask; + int nlocal = atom->nlocal; + int newton_pair = force->newton_pair; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + radi = radius[i]; + neighs = neighbor->firstneigh[i]; + touch = neighbor->firsttouch[i]; + firstshear = neighbor->firstshear[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + radj = radius[j]; + radsum = radi + radj; + + if (rsq >= radsum*radsum) { + + // unset touching neighbors + + touch[k] = 0; + shear = &firstshear[3*k]; + shear[0] = 0.0; + shear[1] = 0.0; + shear[2] = 0.0; + + } else { + r = sqrt(rsq); + + // relative translational velocity + + vr1 = v[i][0] - v[j][0]; + vr2 = v[i][1] - v[j][1]; + vr3 = v[i][2] - v[j][2]; + + vr1 *= dt; + vr2 *= dt; + vr3 *= dt; + + // normal component + + vnnr = vr1*delx + vr2*dely + vr3*delz; + vn1 = delx*vnnr / rsq; + vn2 = dely*vnnr / rsq; + vn3 = delz*vnnr / rsq; + + // tangential component + + vt1 = vr1 - vn1; + vt2 = vr2 - vn2; + vt3 = vr3 - vn3; + + // relative rotational velocity + + wr1 = radi*phiv[i][0] + radj*phiv[j][0]; + wr2 = radi*phiv[i][1] + radj*phiv[j][1]; + wr3 = radi*phiv[i][2] + radj*phiv[j][2]; + + wr1 *= dt/r; + wr2 *= dt/r; + wr3 *= dt/r; + + // normal damping term + // this definition of DAMP includes the extra 1/r term + + xmeff = rmass[i]*rmass[j] / (rmass[i]+rmass[j]); + if (mask[i] & freeze_group_bit) xmeff = rmass[j]; + if (mask[j] & freeze_group_bit) xmeff = rmass[i]; + damp = xmeff*gamman_dl*vnnr/rsq; + ccel = xkk*(radsum-r)/r - damp; + + // relative velocities + + vtr1 = vt1 - (delz*wr2-dely*wr3); + vtr2 = vt2 - (delx*wr3-delz*wr1); + vtr3 = vt3 - (dely*wr1-delx*wr2); + vrel = vtr1*vtr1 + vtr2*vtr2 + vtr3*vtr3; + vrel = sqrt(vrel); + + // shear history effects + // shrmag = magnitude of shear + + touch[k] = 1; + shear = &firstshear[3*k]; + shear[0] += vtr1; + shear[1] += vtr2; + shear[2] += vtr3; + shrmag = sqrt(shear[0]*shear[0] + shear[1]*shear[1] + + shear[2]*shear[2]); + + // rotate shear displacements correctly + + rsht = shear[0]*delx + shear[1]*dely + shear[2]*delz; + rsht /= rsq; + shear[0] -= rsht*delx; + shear[1] -= rsht*dely; + shear[2] -= rsht*delz; + + // tangential forces + + fs1 = - (xkkt*shear[0] + xmeff*gammas_dl*vtr1); + fs2 = - (xkkt*shear[1] + xmeff*gammas_dl*vtr2); + fs3 = - (xkkt*shear[2] + xmeff*gammas_dl*vtr3); + + // force normalization + // rescale frictional displacements and forces if needed + + fs = sqrt(fs1*fs1 + fs2*fs2 + fs3*fs3); + fn = xmu * fabs(ccel*r); + + if (fs > fn) { + if (shrmag != 0.0) { + shear[0] = (fn/fs) * (shear[0] + xmeff*gammas_dl*vtr1/xkkt) - + xmeff*gammas_dl*vtr1/xkkt; + shear[1] = (fn/fs) * (shear[1] + xmeff*gammas_dl*vtr2/xkkt) - + xmeff*gammas_dl*vtr2/xkkt; + shear[2] = (fn/fs) * (shear[2] + xmeff*gammas_dl*vtr3/xkkt) - + xmeff*gammas_dl*vtr3/xkkt; + fs1 *= fn/fs; + fs2 *= fn/fs; + fs3 *= fn/fs; + } else { + fs1 = 0.0; + fs2 = 0.0; + fs3 = 0.0; + } + } + + // forces & torques + + ccelx = delx*ccel + fs1; + ccely = dely*ccel + fs2; + ccelz = delz*ccel + fs3; + f[i][0] += ccelx; + f[i][1] += ccely; + f[i][2] += ccelz; + + rinv = 1/r; + tor1 = rinv * (dely*fs3 - delz*fs2); + tor2 = rinv * (delz*fs1 - delx*fs3); + tor3 = rinv * (delx*fs2 - dely*fs1); + phia[i][0] -= radi*tor1; + phia[i][1] -= radi*tor2; + phia[i][2] -= radi*tor3; + + if (newton_pair || j < nlocal) { + f[j][0] -= ccelx; + f[j][1] -= ccely; + f[j][2] -= ccelz; + phia[j][0] -= radj*tor1; + phia[j][1] -= radj*tor2; + phia[j][2] -= radj*tor3; + } + } + } + } +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairGranHistory::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 PairGranHistory::settings(int narg, char **arg) +{ + if (domain->box_exist == 0) + error->all("Pair_style granular command before simulation box is defined"); + if (narg != 4) error->all("Illegal pair_style command"); + + xkk = atof(arg[0]); + gamman = atof(arg[1]); + xmu = atof(arg[2]); + dampflag = atoi(arg[3]); + + // granular styles do not use pair_coeff, so set setflag for everything now + + if (!allocated) allocate(); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + for (j = i; j <= atom->ntypes; j++) + setflag[i][j] = 1; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairGranHistory::coeff(int narg, char **arg) +{ + error->all("Granular pair styles do not use pair_coeff settings"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairGranHistory::init_one(int i, int j) +{ + if (!allocated) allocate(); + + // return dummy value used in neighbor setup, + // but not in actual neighbor calculation + // since particles have variable radius + + return 1.0; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairGranHistory::init_style() +{ + int i; + + xkkt = xkk * 2.0/7.0; + dt = update->dt; + double gammas = 0.5*gamman; + if (dampflag == 0) gammas = 0.0; + gamman_dl = gamman/dt; + gammas_dl = gammas/dt; + + // check that atom style is granular + // else compute() will update illegal arrays + + if (atom->check_style("granular") == 0) + error->all("Must use atom style granular with pair style granular"); + + // for pair choices with shear history: + // check if newton flag is valid + // if first init, create Fix needed for storing shear history + + int history = 0; + if (force->pair_match("gran/history") || force->pair_match("gran/hertzian")) + history = 1; + + if (history && force->newton_pair == 1) + error->all("Potential with shear history requires newton pair off"); + + if (history && ifix_history == -1) { + char **fixarg = new char*[3]; + fixarg[0] = "SHEAR_HISTORY"; + fixarg[1] = "all"; + fixarg[2] = "SHEAR_HISTORY"; + modify->add_fix(3,fixarg); + delete [] fixarg; + } + + // find associated SHEAR_HISTORY fix that must exist + // could have changed locations in fix list since created + + if (history) { + for (i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"SHEAR_HISTORY") == 0) ifix_history = i; + } + + // check for freeze Fix and set freeze_group_bit + + for (i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"freeze") == 0) break; + if (i < modify->nfix) freeze_group_bit = modify->fix[i]->groupbit; + else freeze_group_bit = 0; + + // set cutoff by largest particles + // maxrad_dynamic = radius of largest dynamic particle, including inserted + // maxrad_frozen = radius of largest dynamic particle + // include frozen-dynamic interactions + // do not include frozen-frozen interactions + // include future inserted particles as dynamic + // cutforce was already set in pair::init(), but this sets it correctly + + double *radius = atom->radius; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double maxrad_dynamic = 0.0; + for (i = 0; i < nlocal; i++) + if (!(mask[i] & freeze_group_bit)) + maxrad_dynamic = MAX(maxrad_dynamic,radius[i]); + double mine = maxrad_dynamic; + MPI_Allreduce(&mine,&maxrad_dynamic,1,MPI_DOUBLE,MPI_MAX,world); + + for (i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"insert") == 0) + maxrad_dynamic = + MAX(maxrad_dynamic,((FixInsert *) modify->fix[i])->radius_hi); + + double maxrad_frozen = 0.0; + for (i = 0; i < nlocal; i++) + if (mask[i] & freeze_group_bit) + maxrad_frozen = MAX(maxrad_frozen,radius[i]); + mine = maxrad_frozen; + MPI_Allreduce(&mine,&maxrad_frozen,1,MPI_DOUBLE,MPI_MAX,world); + + cutforce = maxrad_dynamic + MAX(maxrad_dynamic,maxrad_frozen); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairGranHistory::write_restart(FILE *fp) +{ + write_restart_settings(fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairGranHistory::read_restart(FILE *fp) +{ + read_restart_settings(fp); + allocate(); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairGranHistory::write_restart_settings(FILE *fp) +{ + fwrite(&xkk,sizeof(double),1,fp); + fwrite(&gamman,sizeof(double),1,fp); + fwrite(&xmu,sizeof(double),1,fp); + fwrite(&dampflag,sizeof(int),1,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairGranHistory::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&xkk,sizeof(double),1,fp); + fread(&gamman,sizeof(double),1,fp); + fread(&xmu,sizeof(double),1,fp); + fread(&dampflag,sizeof(int),1,fp); + } + MPI_Bcast(&xkk,1,MPI_DOUBLE,0,world); + MPI_Bcast(&gamman,1,MPI_DOUBLE,0,world); + MPI_Bcast(&xmu,1,MPI_DOUBLE,0,world); + MPI_Bcast(&dampflag,1,MPI_INT,0,world); +} diff --git a/src/GRANULAR/pair_gran_history.h b/src/GRANULAR/pair_gran_history.h new file mode 100644 index 0000000000000000000000000000000000000000..d8be75d2582e62595c900d0a96cd32b6f26565c0 --- /dev/null +++ b/src/GRANULAR/pair_gran_history.h @@ -0,0 +1,48 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_GRAN_HISTORY_H +#define PAIR_GRAN_HISTORY_H + +#include "pair.h" + +class PairGranHistory : public Pair { + friend class Neighbor; + friend class FixWallGran; + friend class FixGranDiag; + friend class FixInsert; + + public: + PairGranHistory(); + ~PairGranHistory(); + virtual void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void init_style(); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + + protected: + double xkk,xkkt,xmu; + int dampflag; + double gamman; + double dt,gamman_dl,gammas_dl; + int ifix_history,freeze_group_bit; + + void allocate(); +}; + +#endif diff --git a/src/GRANULAR/pair_gran_no_history.cpp b/src/GRANULAR/pair_gran_no_history.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0e2a7a94e85607d5035da234e4a72c11554ce295 --- /dev/null +++ b/src/GRANULAR/pair_gran_no_history.cpp @@ -0,0 +1,168 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Leo Silbert (SNL), Gary Grest (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "string.h" +#include "pair_gran_no_history.h" +#include "atom.h" +#include "force.h" +#include "neighbor.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +void PairGranNoHistory::compute(int eflag, int vflag) +{ + int i,j,k,numneigh; + double xtmp,ytmp,ztmp,delx,dely,delz; + double radi,radj,radsum,rsq,r,rinv; + double vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3; + double wr1,wr2,wr3; + double vtr1,vtr2,vtr3,vrel; + double xmeff,damp,ccel,ccelx,ccely,ccelz,tor1,tor2,tor3; + double fn,fs,ft,fs1,fs2,fs3; + int *neighs; + + double **f = atom->f; + double **x = atom->x; + double **v = atom->v; + double **phiv = atom->phiv; + double **phia = atom->phia; + double *radius = atom->radius; + double *rmass = atom->rmass; + int *mask = atom->mask; + int nlocal = atom->nlocal; + int newton_pair = force->newton_pair; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + radi = radius[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + radj = radius[j]; + radsum = radi + radj; + + if (rsq < radsum*radsum) { + r = sqrt(rsq); + + // relative translational velocity + + vr1 = v[i][0] - v[j][0]; + vr2 = v[i][1] - v[j][1]; + vr3 = v[i][2] - v[j][2]; + + vr1 *= dt; + vr2 *= dt; + vr3 *= dt; + + // normal component + + vnnr = vr1*delx + vr2*dely + vr3*delz; + vn1 = delx*vnnr / rsq; + vn2 = dely*vnnr / rsq; + vn3 = delz*vnnr / rsq; + + // tangential component + + vt1 = vr1 - vn1; + vt2 = vr2 - vn2; + vt3 = vr3 - vn3; + + // relative rotational velocity + + wr1 = radi*phiv[i][0] + radj*phiv[j][0]; + wr2 = radi*phiv[i][1] + radj*phiv[j][1]; + wr3 = radi*phiv[i][2] + radj*phiv[j][2]; + + wr1 *= dt/r; + wr2 *= dt/r; + wr3 *= dt/r; + + // normal damping term + // this definition of DAMP includes the extra 1/r term + + xmeff = rmass[i]*rmass[j] / (rmass[i]+rmass[j]); + if (mask[i] & freeze_group_bit) xmeff = rmass[j]; + if (mask[j] & freeze_group_bit) xmeff = rmass[i]; + damp = xmeff*gamman_dl*vnnr/rsq; + ccel = xkk*(radsum-r)/r - damp; + + // relative velocities + + vtr1 = vt1 - (delz*wr2-dely*wr3); + vtr2 = vt2 - (delx*wr3-delz*wr1); + vtr3 = vt3 - (dely*wr1-delx*wr2); + vrel = vtr1*vtr1 + vtr2*vtr2 + vtr3*vtr3; + vrel = sqrt(vrel); + + // force normalization + + fn = xmu * fabs(ccel*r); + fs = xmeff*gammas_dl*vrel; + if (vrel != 0.0) ft = MIN(fn,fs) / vrel; + else ft = 0.0; + + // shear friction forces + + fs1 = -ft*vtr1; + fs2 = -ft*vtr2; + fs3 = -ft*vtr3; + + // forces & torques + + ccelx = delx*ccel + fs1; + ccely = dely*ccel + fs2; + ccelz = delz*ccel + fs3; + f[i][0] += ccelx; + f[i][1] += ccely; + f[i][2] += ccelz; + + rinv = 1/r; + tor1 = rinv * (dely*fs3 - delz*fs2); + tor2 = rinv * (delz*fs1 - delx*fs3); + tor3 = rinv * (delx*fs2 - dely*fs1); + phia[i][0] -= radi*tor1; + phia[i][1] -= radi*tor2; + phia[i][2] -= radi*tor3; + + if (newton_pair || j < nlocal) { + f[j][0] -= ccelx; + f[j][1] -= ccely; + f[j][2] -= ccelz; + phia[j][0] -= radj*tor1; + phia[j][1] -= radj*tor2; + phia[j][2] -= radj*tor3; + } + } + } + } +} diff --git a/src/GRANULAR/pair_gran_no_history.h b/src/GRANULAR/pair_gran_no_history.h new file mode 100644 index 0000000000000000000000000000000000000000..d3d2e74c1a1176c668b7a6272cb386b5185be084 --- /dev/null +++ b/src/GRANULAR/pair_gran_no_history.h @@ -0,0 +1,24 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_GRAN_NO_HISTORY_H +#define PAIR_GRAN_NO_HISTORY_H + +#include "pair_gran_history.h" + +class PairGranNoHistory : public PairGranHistory { + public: + void compute(int, int); +}; + +#endif diff --git a/src/GRANULAR/style_granular.h b/src/GRANULAR/style_granular.h new file mode 100644 index 0000000000000000000000000000000000000000..5b571b79a666dc4de7c5b632061c93054e33d76c --- /dev/null +++ b/src/GRANULAR/style_granular.h @@ -0,0 +1,50 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifdef AtomInclude +#include "atom_granular.h" +#endif + +#ifdef AtomClass +AtomStyle(granular,AtomGranular) +# endif + +#ifdef FixInclude +#include "fix_freeze.h" +#include "fix_gran_diag.h" +#include "fix_insert.h" +#include "fix_nve_gran.h" +#include "fix_shear_history.h" +#include "fix_wall_gran.h" +#endif + +#ifdef FixClass +FixStyle(freeze,FixFreeze) +FixStyle(gran/diag,FixGranDiag) +FixStyle(insert,FixInsert) +FixStyle(nve/gran,FixNVEGran) +FixStyle(SHEAR_HISTORY,FixShearHistory) +FixStyle(wall/gran,FixWallGran) +#endif + +#ifdef PairInclude +#include "pair_gran_hertzian.h" +#include "pair_gran_history.h" +#include "pair_gran_no_history.h" +#endif + +#ifdef PairClass +PairStyle(gran/hertzian,PairGranHertzian) +PairStyle(gran/history,PairGranHistory) +PairStyle(gran/no_history,PairGranNoHistory) +#endif diff --git a/src/KSPACE/Install.csh b/src/KSPACE/Install.csh new file mode 100644 index 0000000000000000000000000000000000000000..8a58067c7c5cc100352a641d074071fdc56c5d4a --- /dev/null +++ b/src/KSPACE/Install.csh @@ -0,0 +1,62 @@ +# Install/unInstall package classes in LAMMPS + +# pair_lj_charmm_coul_long.h must always be in src + +if ($1 == 1) then + + cp style_kspace.h .. + + cp ewald.cpp .. + cp pppm.cpp .. + cp pppm_tip4p.cpp .. + cp pair_buck_coul_long.cpp .. + cp pair_lj_cut_coul_long.cpp .. + cp pair_lj_cut_coul_long_tip4p.cpp .. + cp pair_lj_charmm_coul_long.cpp .. + cp fft3d.cpp .. + cp fft3d_wrap.cpp .. + cp remap.cpp .. + cp remap_wrap.cpp .. + + cp ewald.h .. + cp pppm.h .. + cp pppm_tip4p.h .. + cp pair_buck_coul_long.h .. + cp pair_lj_cut_coul_long.h .. + cp pair_lj_cut_coul_long_tip4p.h .. +# cp pair_lj_charmm_coul_long.h .. + cp fft3d.h .. + cp fft3d_wrap.h .. + cp remap.h .. + cp remap_wrap.h .. + +else if ($1 == 0) then + + rm ../style_kspace.h + touch ../style_kspace.h + + rm ../ewald.cpp + rm ../pppm.cpp + rm ../pppm_tip4p.cpp + rm ../pair_buck_coul_long.cpp + rm ../pair_lj_cut_coul_long.cpp + rm ../pair_lj_cut_coul_long_tip4p.cpp + rm ../pair_lj_charmm_coul_long.cpp + rm ../fft3d.cpp + rm ../fft3d_wrap.cpp + rm ../remap.cpp + rm ../remap_wrap.cpp + + rm ../ewald.h + rm ../pppm.h + rm ../pppm_tip4p.h + rm ../pair_buck_coul_long.h + rm ../pair_lj_cut_coul_long.h + rm ../pair_lj_cut_coul_long_tip4p.h +# rm ../pair_lj_charmm_coul_long.h + rm ../fft3d.h + rm ../fft3d_wrap.h + rm ../remap.h + rm ../remap_wrap.h + +endif diff --git a/src/KSPACE/ewald.cpp b/src/KSPACE/ewald.cpp new file mode 100644 index 0000000000000000000000000000000000000000..202a6779997a58428af1c0c2c8245160de56d986 --- /dev/null +++ b/src/KSPACE/ewald.cpp @@ -0,0 +1,846 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Roy Pollock (LLNL), Paul Crozier (SNL) +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "stdlib.h" +#include "stdio.h" +#include "math.h" +#include "ewald.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "pair_buck_coul_long.h" +#include "pair_lj_cut_coul_long.h" +#include "pair_lj_charmm_coul_long.h" +#include "pair_lj_class2_coul_long.h" +#include "pair_table.h" +#include "domain.h" +#include "memory.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +Ewald::Ewald(int narg, char **arg) : KSpace(narg, arg) +{ + if (narg != 1) error->all("Illegal kspace_style ewald command"); + + precision = atof(arg[0]); + PI = 4.0*atan(1.0); + + kmax = 0; + kxvecs = kyvecs = kzvecs = NULL; + ug = NULL; + eg = vg = NULL; + sfacrl = sfacim = sfacrl_all = sfacim_all = NULL; + + nmax = 0; + ek = NULL; + cs = sn = NULL; + + kcount = 0; +} + +/* ---------------------------------------------------------------------- + free all memory +------------------------------------------------------------------------- */ + +Ewald::~Ewald() +{ + deallocate(); + memory->destroy_2d_double_array(ek); + memory->destroy_3d_double_array(cs,-kmax_created); + memory->destroy_3d_double_array(sn,-kmax_created); +} + +/* ---------------------------------------------------------------------- */ + +void Ewald::init() +{ + if (comm->me == 0) { + if (screen) fprintf(screen,"Ewald initialization ...\n"); + if (logfile) fprintf(logfile,"Ewald initialization ...\n"); + } + + // error check + + if (force->dimension == 2) error->all("Cannot use Ewald with 2d simulation"); + + if (slabflag == 0 && domain->nonperiodic > 0) + error->all("Cannot use nonperiodic boundaries with Ewald"); + if (slabflag == 1) { + if (domain->xperiodic != 1 || domain->yperiodic != 1 || + domain->boundary[2][0] != 1 || domain->boundary[2][1] != 1) + error->all("Incorrect boundaries with slab Ewald"); + } + + // insure use of pair_style with long-range Coulombics + // set cutoff to short-range Coulombic cutoff + + qqrd2e = force->qqrd2e; + + double cutoff; + + Pair *anypair; + if (force->pair == NULL) + error->all("KSpace style is incompatible with Pair style"); + else if (anypair = force->pair_match("buck/coul/long")) + cutoff = ((PairBuckCoulLong *) anypair)->cut_coul; + else if (anypair = force->pair_match("lj/cut/coul/long")) + cutoff = ((PairLJCutCoulLong *) anypair)->cut_coul; + else if (anypair = force->pair_match("lj/charmm/coul/long")) + cutoff = ((PairLJCharmmCoulLong *) anypair)->cut_coul; + else if (anypair = force->pair_match("lj/class2/coul/long")) + cutoff = ((PairLJClass2CoulLong *) anypair)->cut_coul; + else if (anypair = force->pair_match("table")) + cutoff = ((PairTable *) anypair)->cut_coul(); + else error->all("KSpace style is incompatible with Pair style"); + + // compute qsum & qsqsum + + double tmp; + + qsum = 0.0; + for (int i = 0; i < atom->nlocal; i++) qsum += atom->q[i]; + MPI_Allreduce(&qsum,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + qsum = tmp; + + qsqsum = 0.0; + for (int i = 0; i < atom->nlocal; i++) qsqsum += atom->q[i]*atom->q[i]; + MPI_Allreduce(&qsqsum,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + qsqsum = tmp; + + // setup K-space resolution + + g_ewald = (1.35 - 0.15*log(precision))/cutoff; + gsqmx = -4.0*g_ewald*g_ewald*log(precision); + + if (comm->me == 0) { + if (screen) fprintf(screen," G vector = %g\n",g_ewald); + if (logfile) fprintf(logfile," G vector = %g\n",g_ewald); + } +} + +/* ---------------------------------------------------------------------- + adjust Ewald coeffs, called initially and whenever volume has changed +------------------------------------------------------------------------- */ + +void Ewald::setup() +{ + // volume-dependent factors + + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + + // adjustment of z dimension for 2d slab Ewald + // 3d Ewald just uses zprd since slab_volfactor = 1.0 + + double zprd_slab = zprd*slab_volfactor; + volume = xprd * yprd * zprd_slab; + + unitk[0] = 2.0*PI/xprd; + unitk[1] = 2.0*PI/yprd; + unitk[2] = 2.0*PI/zprd_slab; + + // determine kmax + // function of current box size, precision, G_ewald (short-range cutoff) + + int nkxmx = static_cast<int> ((g_ewald*xprd/PI) * sqrt(-log(precision))); + int nkymx = static_cast<int> ((g_ewald*yprd/PI) * sqrt(-log(precision))); + int nkzmx = static_cast<int> ((g_ewald*zprd_slab/PI) * sqrt(-log(precision))); + + int kmax_old = kmax; + kmax = MAX(nkxmx,nkymx); + kmax = MAX(kmax,nkzmx); + kmax3d = 4*kmax*kmax*kmax + 6*kmax*kmax + 3*kmax; + + // if size has grown, reallocate k-dependent and nlocal-dependent arrays + + if (kmax > kmax_old) { + deallocate(); + allocate(); + + memory->destroy_2d_double_array(ek); + memory->destroy_3d_double_array(cs,-kmax_created); + memory->destroy_3d_double_array(sn,-kmax_created); + nmax = atom->nmax; + ek = memory->create_2d_double_array(nmax,3,"ewald:ek"); + cs = memory->create_3d_double_array(-kmax,kmax,3,nmax,"ewald:cs"); + sn = memory->create_3d_double_array(-kmax,kmax,3,nmax,"ewald:sn"); + kmax_created = kmax; + } + + // pre-compute Ewald coefficients + + int kcount_old = kcount; + coeffs(); + + // if array sizes changed, print out new sizes + + if (kmax != kmax_old || kcount != kcount_old) { + if (comm->me == 0) { + if (screen) fprintf(screen," vectors: actual 1d max = %d %d %d\n", + kcount,kmax,kmax3d); + if (logfile) fprintf(logfile," vectors: actual 1d max = %d %d %d\n", + kcount,kmax,kmax3d); + } + } +} + +/* ---------------------------------------------------------------------- + compute the Ewald long-range force, energy, virial +------------------------------------------------------------------------- */ + +void Ewald::compute(int eflag, int vflag) +{ + int i,k,n; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + // extend size of nlocal-dependent arrays if necessary + + int nlocal = atom->nlocal; + if (nlocal > nmax) { + memory->destroy_2d_double_array(ek); + memory->destroy_3d_double_array(cs,-kmax_created); + memory->destroy_3d_double_array(sn,-kmax_created); + nmax = atom->nmax; + ek = memory->create_2d_double_array(nmax,3,"ewald:ek"); + cs = memory->create_3d_double_array(-kmax,kmax,3,nmax,"ewald:cs"); + sn = memory->create_3d_double_array(-kmax,kmax,3,nmax,"ewald:sn"); + kmax_created = kmax; + } + + // partial structure factors on each processor + // total structure factor by summing over procs + + eik_dot_r(); + MPI_Allreduce(sfacrl,sfacrl_all,kcount,MPI_DOUBLE,MPI_SUM,world); + MPI_Allreduce(sfacim,sfacim_all,kcount,MPI_DOUBLE,MPI_SUM,world); + + // K-space portion of electric field + // double loop over K-vectors and local atoms + + double **f = atom->f; + double *q = atom->q; + + int kx,ky,kz; + double cypz,sypz,exprl,expim,partial; + + for (i = 0; i < nlocal; i++) { + ek[i][0] = 0.0; + ek[i][1] = 0.0; + ek[i][2] = 0.0; + } + + for (k = 0; k < kcount; k++) { + kx = kxvecs[k]; + ky = kyvecs[k]; + kz = kzvecs[k]; + + for (i = 0; i < nlocal; i++) { + cypz = cs[ky][1][i]*cs[kz][2][i] - sn[ky][1][i]*sn[kz][2][i]; + sypz = sn[ky][1][i]*cs[kz][2][i] + cs[ky][1][i]*sn[kz][2][i]; + exprl = cs[kx][0][i]*cypz - sn[kx][0][i]*sypz; + expim = sn[kx][0][i]*cypz + cs[kx][0][i]*sypz; + partial = expim*sfacrl_all[k] - exprl*sfacim_all[k]; + ek[i][0] += partial*eg[k][0]; + ek[i][1] += partial*eg[k][1]; + ek[i][2] += partial*eg[k][2]; + } + } + + // convert E-field to force + + for (i = 0; i < nlocal; i++) { + f[i][0] += qqrd2e*q[i]*ek[i][0]; + f[i][1] += qqrd2e*q[i]*ek[i][1]; + f[i][2] += qqrd2e*q[i]*ek[i][2]; + } + + // energy if requested + + if (eflag) { + for (k = 0; k < kcount; k++) + energy += ug[k] * (sfacrl_all[k]*sfacrl_all[k] + + sfacim_all[k]*sfacim_all[k]); + PI = 4.0*atan(1.0); + energy -= g_ewald*qsqsum/1.772453851 + + 0.5*PI*qsum*qsum / (g_ewald*g_ewald*volume); + energy *= qqrd2e; + } + + // virial if requested + + if (vflag) { + double uk; + for (k = 0; k < kcount; k++) { + uk = ug[k] * (sfacrl_all[k]*sfacrl_all[k] + sfacim_all[k]*sfacim_all[k]); + for (n = 0; n < 6; n++) virial[n] += uk*vg[k][n]; + } + for (n = 0; n < 6; n++) virial[n] *= qqrd2e; + } + + if (slabflag) slabcorr(eflag); + +} + +/* ---------------------------------------------------------------------- */ + +void Ewald::eik_dot_r() +{ + int i,k,l,m,n,ic; + double cstr1,sstr1,cstr2,sstr2,cstr3,sstr3,cstr4,sstr4; + double sqk,clpm,slpm; + + double **x = atom->x; + double *q = atom->q; + int nlocal = atom->nlocal; + + n = 0; + + // (k,0,0), (0,l,0), (0,0,m) + + for (ic = 0; ic < 3; ic++) { + sqk = unitk[ic]*unitk[ic]; + if (sqk <= gsqmx) { + cstr1 = 0.0; + sstr1 = 0.0; + for (i = 0; i < nlocal; i++) { + cs[0][ic][i] = 1.0; + sn[0][ic][i] = 0.0; + cs[1][ic][i] = cos(unitk[ic]*x[i][ic]); + sn[1][ic][i] = sin(unitk[ic]*x[i][ic]); + cs[-1][ic][i] = cs[1][ic][i]; + sn[-1][ic][i] = -sn[1][ic][i]; + cstr1 += q[i]*cs[1][ic][i]; + sstr1 += q[i]*sn[1][ic][i]; + } + sfacrl[n] = cstr1; + sfacim[n++] = sstr1; + } + } + + for (m = 2; m <= kmax; m++) { + for (ic = 0; ic < 3; ic++) { + sqk = m*unitk[ic] * m*unitk[ic]; + if (sqk <= gsqmx) { + cstr1 = 0.0; + sstr1 = 0.0; + for (i = 0; i < nlocal; i++) { + cs[m][ic][i] = cs[m-1][ic][i]*cs[1][ic][i] - + sn[m-1][ic][i]*sn[1][ic][i]; + sn[m][ic][i] = sn[m-1][ic][i]*cs[1][ic][i] + + cs[m-1][ic][i]*sn[1][ic][i]; + cs[-m][ic][i] = cs[m][ic][i]; + sn[-m][ic][i] = -sn[m][ic][i]; + cstr1 += q[i]*cs[m][ic][i]; + sstr1 += q[i]*sn[m][ic][i]; + } + sfacrl[n] = cstr1; + sfacim[n++] = sstr1; + } + } + } + + // 1 = (k,l,0), 2 = (k,-l,0) + + for (k = 1; k <= kmax; k++) { + for (l = 1; l <= kmax; l++) { + sqk = (k*unitk[0] * k*unitk[0]) + (l*unitk[1] * l*unitk[1]); + if (sqk <= gsqmx) { + cstr1 = 0.0; + sstr1 = 0.0; + cstr2 = 0.0; + sstr2 = 0.0; + for (i = 0; i < nlocal; i++) { + cstr1 += q[i]*(cs[k][0][i]*cs[l][1][i] - sn[k][0][i]*sn[l][1][i]); + sstr1 += q[i]*(sn[k][0][i]*cs[l][1][i] + cs[k][0][i]*sn[l][1][i]); + cstr2 += q[i]*(cs[k][0][i]*cs[l][1][i] + sn[k][0][i]*sn[l][1][i]); + sstr2 += q[i]*(sn[k][0][i]*cs[l][1][i] - cs[k][0][i]*sn[l][1][i]); + } + sfacrl[n] = cstr1; + sfacim[n++] = sstr1; + sfacrl[n] = cstr2; + sfacim[n++] = sstr2; + } + } + } + + // 1 = (0,l,m), 2 = (0,l,-m) + + for (l = 1; l <= kmax; l++) { + for (m = 1; m <= kmax; m++) { + sqk = (l*unitk[1] * l*unitk[1]) + (m*unitk[2] * m*unitk[2]); + if (sqk <= gsqmx) { + cstr1 = 0.0; + sstr1 = 0.0; + cstr2 = 0.0; + sstr2 = 0.0; + for (i = 0; i < nlocal; i++) { + cstr1 += q[i]*(cs[l][1][i]*cs[m][2][i] - sn[l][1][i]*sn[m][2][i]); + sstr1 += q[i]*(sn[l][1][i]*cs[m][2][i] + cs[l][1][i]*sn[m][2][i]); + cstr2 += q[i]*(cs[l][1][i]*cs[m][2][i] + sn[l][1][i]*sn[m][2][i]); + sstr2 += q[i]*(sn[l][1][i]*cs[m][2][i] - cs[l][1][i]*sn[m][2][i]); + } + sfacrl[n] = cstr1; + sfacim[n++] = sstr1; + sfacrl[n] = cstr2; + sfacim[n++] = sstr2; + } + } + } + + // 1 = (k,0,m), 2 = (k,0,-m) + + for (k = 1; k <= kmax; k++) { + for (m = 1; m <= kmax; m++) { + sqk = (k*unitk[0] * k*unitk[0]) + (m*unitk[2] * m*unitk[2]); + if (sqk <= gsqmx) { + cstr1 = 0.0; + sstr1 = 0.0; + cstr2 = 0.0; + sstr2 = 0.0; + for (i = 0; i < nlocal; i++) { + cstr1 += q[i]*(cs[k][0][i]*cs[m][2][i] - sn[k][0][i]*sn[m][2][i]); + sstr1 += q[i]*(sn[k][0][i]*cs[m][2][i] + cs[k][0][i]*sn[m][2][i]); + cstr2 += q[i]*(cs[k][0][i]*cs[m][2][i] + sn[k][0][i]*sn[m][2][i]); + sstr2 += q[i]*(sn[k][0][i]*cs[m][2][i] - cs[k][0][i]*sn[m][2][i]); + } + sfacrl[n] = cstr1; + sfacim[n++] = sstr1; + sfacrl[n] = cstr2; + sfacim[n++] = sstr2; + } + } + } + + // 1 = (k,l,m), 2 = (k,-l,m), 3 = (k,l,-m), 4 = (k,-l,-m) + + for (k = 1; k <= kmax; k++) { + for (l = 1; l <= kmax; l++) { + for (m = 1; m <= kmax; m++) { + sqk = (k*unitk[0] * k*unitk[0]) + (l*unitk[1] * l*unitk[1]) + + (m*unitk[2] * m*unitk[2]); + if (sqk <= gsqmx) { + cstr1 = 0.0; + sstr1 = 0.0; + cstr2 = 0.0; + sstr2 = 0.0; + cstr3 = 0.0; + sstr3 = 0.0; + cstr4 = 0.0; + sstr4 = 0.0; + for (i = 0; i < nlocal; i++) { + clpm = cs[l][1][i]*cs[m][2][i] - sn[l][1][i]*sn[m][2][i]; + slpm = sn[l][1][i]*cs[m][2][i] + cs[l][1][i]*sn[m][2][i]; + cstr1 += q[i]*(cs[k][0][i]*clpm - sn[k][0][i]*slpm); + sstr1 += q[i]*(sn[k][0][i]*clpm + cs[k][0][i]*slpm); + + clpm = cs[l][1][i]*cs[m][2][i] + sn[l][1][i]*sn[m][2][i]; + slpm = -sn[l][1][i]*cs[m][2][i] + cs[l][1][i]*sn[m][2][i]; + cstr2 += q[i]*(cs[k][0][i]*clpm - sn[k][0][i]*slpm); + sstr2 += q[i]*(sn[k][0][i]*clpm + cs[k][0][i]*slpm); + + clpm = cs[l][1][i]*cs[m][2][i] + sn[l][1][i]*sn[m][2][i]; + slpm = sn[l][1][i]*cs[m][2][i] - cs[l][1][i]*sn[m][2][i]; + cstr3 += q[i]*(cs[k][0][i]*clpm - sn[k][0][i]*slpm); + sstr3 += q[i]*(sn[k][0][i]*clpm + cs[k][0][i]*slpm); + + clpm = cs[l][1][i]*cs[m][2][i] - sn[l][1][i]*sn[m][2][i]; + slpm = -sn[l][1][i]*cs[m][2][i] - cs[l][1][i]*sn[m][2][i]; + cstr4 += q[i]*(cs[k][0][i]*clpm - sn[k][0][i]*slpm); + sstr4 += q[i]*(sn[k][0][i]*clpm + cs[k][0][i]*slpm); + } + sfacrl[n] = cstr1; + sfacim[n++] = sstr1; + sfacrl[n] = cstr2; + sfacim[n++] = sstr2; + sfacrl[n] = cstr3; + sfacim[n++] = sstr3; + sfacrl[n] = cstr4; + sfacim[n++] = sstr4; + } + } + } + } +} + +/* ---------------------------------------------------------------------- + pre-compute coefficients for each Ewald K-vector +------------------------------------------------------------------------- */ + +void Ewald::coeffs() +{ + int k,l,m; + double sqk,vterm; + + double unitkx = unitk[0]; + double unitky = unitk[1]; + double unitkz = unitk[2]; + double g_ewald_sq_inv = 1.0 / (g_ewald*g_ewald); + double preu = 4.0*PI/volume; + + kcount = 0; + + // (k,0,0), (0,l,0), (0,0,m) + + for (m = 1; m <= kmax; m++) { + sqk = (m*unitkx) * (m*unitkx); + if (sqk <= gsqmx) { + kxvecs[kcount] = m; + kyvecs[kcount] = 0; + kzvecs[kcount] = 0; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 2.0*unitkx*m*ug[kcount]; + eg[kcount][1] = 0.0; + eg[kcount][2] = 0.0; + vterm = -2.0*(1.0/sqk + 0.25*g_ewald_sq_inv); + vg[kcount][0] = 1.0 + vterm*(unitkx*m)*(unitkx*m); + vg[kcount][1] = 1.0; + vg[kcount][2] = 1.0; + vg[kcount][3] = 0.0; + vg[kcount][4] = 0.0; + vg[kcount][5] = 0.0; + kcount++; + } + sqk = (m*unitky) * (m*unitky); + if (sqk <= gsqmx) { + kxvecs[kcount] = 0; + kyvecs[kcount] = m; + kzvecs[kcount] = 0; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 0.0; + eg[kcount][1] = 2.0*unitky*m*ug[kcount]; + eg[kcount][2] = 0.0; + vterm = -2.0*(1.0/sqk + 0.25*g_ewald_sq_inv); + vg[kcount][0] = 1.0; + vg[kcount][1] = 1.0 + vterm*(unitky*m)*(unitky*m); + vg[kcount][2] = 1.0; + vg[kcount][3] = 0.0; + vg[kcount][4] = 0.0; + vg[kcount][5] = 0.0; + kcount++; + } + sqk = (m*unitkz) * (m*unitkz); + if (sqk <= gsqmx) { + kxvecs[kcount] = 0; + kyvecs[kcount] = 0; + kzvecs[kcount] = m; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 0.0; + eg[kcount][1] = 0.0; + eg[kcount][2] = 2.0*unitkz*m*ug[kcount]; + vterm = -2.0*(1.0/sqk + 0.25*g_ewald_sq_inv); + vg[kcount][0] = 1.0; + vg[kcount][1] = 1.0; + vg[kcount][2] = 1.0 + vterm*(unitkz*m)*(unitkz*m); + vg[kcount][3] = 0.0; + vg[kcount][4] = 0.0; + vg[kcount][5] = 0.0; + kcount++; + } + } + + // 1 = (k,l,0), 2 = (k,-l,0) + + for (k = 1; k <= kmax; k++) { + for (l = 1; l <= kmax; l++) { + sqk = (unitkx*k) * (unitkx*k) + (unitky*l) * (unitky*l); + if (sqk <= gsqmx) { + kxvecs[kcount] = k; + kyvecs[kcount] = l; + kzvecs[kcount] = 0; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 2.0*unitkx*k*ug[kcount]; + eg[kcount][1] = 2.0*unitky*l*ug[kcount]; + eg[kcount][2] = 0.0; + vterm = -2.0*(1.0/sqk + 0.25*g_ewald_sq_inv); + vg[kcount][0] = 1.0 + vterm*(unitkx*k)*(unitkx*k); + vg[kcount][1] = 1.0 + vterm*(unitky*l)*(unitky*l); + vg[kcount][2] = 1.0; + vg[kcount][3] = vterm*unitkx*k*unitky*l; + vg[kcount][4] = 0.0; + vg[kcount][5] = 0.0; + kcount++; + + kxvecs[kcount] = k; + kyvecs[kcount] = -l; + kzvecs[kcount] = 0; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 2.0*unitkx*k*ug[kcount]; + eg[kcount][1] = -2.0*unitky*l*ug[kcount]; + eg[kcount][2] = 0.0; + vg[kcount][0] = 1.0 + vterm*(unitkx*k)*(unitkx*k); + vg[kcount][1] = 1.0 + vterm*(unitky*l)*(unitky*l); + vg[kcount][2] = 1.0; + vg[kcount][3] = -vterm*unitkx*k*unitky*l; + vg[kcount][4] = 0.0; + vg[kcount][5] = 0.0; + kcount++;; + } + } + } + + // 1 = (0,l,m), 2 = (0,l,-m) + + for (l = 1; l <= kmax; l++) { + for (m = 1; m <= kmax; m++) { + sqk = (unitky*l) * (unitky*l) + (unitkz*m) * (unitkz*m); + if (sqk <= gsqmx) { + kxvecs[kcount] = 0; + kyvecs[kcount] = l; + kzvecs[kcount] = m; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 0.0; + eg[kcount][1] = 2.0*unitky*l*ug[kcount]; + eg[kcount][2] = 2.0*unitkz*m*ug[kcount]; + vterm = -2.0*(1.0/sqk + 0.25*g_ewald_sq_inv); + vg[kcount][0] = 1.0; + vg[kcount][1] = 1.0 + vterm*(unitky*l)*(unitky*l); + vg[kcount][2] = 1.0 + vterm*(unitkz*m)*(unitkz*m); + vg[kcount][3] = 0.0; + vg[kcount][4] = 0.0; + vg[kcount][5] = vterm*unitky*l*unitkz*m; + kcount++; + + kxvecs[kcount] = 0; + kyvecs[kcount] = l; + kzvecs[kcount] = -m; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 0.0; + eg[kcount][1] = 2.0*unitky*l*ug[kcount]; + eg[kcount][2] = -2.0*unitkz*m*ug[kcount]; + vg[kcount][0] = 1.0; + vg[kcount][1] = 1.0 + vterm*(unitky*l)*(unitky*l); + vg[kcount][2] = 1.0 + vterm*(unitkz*m)*(unitkz*m); + vg[kcount][3] = 0.0; + vg[kcount][4] = 0.0; + vg[kcount][5] = -vterm*unitky*l*unitkz*m; + kcount++; + } + } + } + + // 1 = (k,0,m), 2 = (k,0,-m) + + for (k = 1; k <= kmax; k++) { + for (m = 1; m <= kmax; m++) { + sqk = (unitkx*k) * (unitkx*k) + (unitkz*m) * (unitkz*m); + if (sqk <= gsqmx) { + kxvecs[kcount] = k; + kyvecs[kcount] = 0; + kzvecs[kcount] = m; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 2.0*unitkx*k*ug[kcount]; + eg[kcount][1] = 0.0; + eg[kcount][2] = 2.0*unitkz*m*ug[kcount]; + vterm = -2.0*(1.0/sqk + 0.25*g_ewald_sq_inv); + vg[kcount][0] = 1.0 + vterm*(unitkx*k)*(unitkx*k); + vg[kcount][1] = 1.0; + vg[kcount][2] = 1.0 + vterm*(unitkz*m)*(unitkz*m); + vg[kcount][3] = 0.0; + vg[kcount][4] = vterm*unitkx*k*unitkz*m; + vg[kcount][5] = 0.0; + kcount++; + + kxvecs[kcount] = k; + kyvecs[kcount] = 0; + kzvecs[kcount] = -m; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 2.0*unitkx*k*ug[kcount]; + eg[kcount][1] = 0.0; + eg[kcount][2] = -2.0*unitkz*m*ug[kcount]; + vg[kcount][0] = 1.0 + vterm*(unitkx*k)*(unitkx*k); + vg[kcount][1] = 1.0; + vg[kcount][2] = 1.0 + vterm*(unitkz*m)*(unitkz*m); + vg[kcount][3] = 0.0; + vg[kcount][4] = -vterm*unitkx*k*unitkz*m; + vg[kcount][5] = 0.0; + kcount++; + } + } + } + + // 1 = (k,l,m), 2 = (k,-l,m), 3 = (k,l,-m), 4 = (k,-l,-m) + + for (k = 1; k <= kmax; k++) { + for (l = 1; l <= kmax; l++) { + for (m = 1; m <= kmax; m++) { + sqk = (unitkx*k) * (unitkx*k) + (unitky*l) * (unitky*l) + + (unitkz*m) * (unitkz*m); + if (sqk <= gsqmx) { + kxvecs[kcount] = k; + kyvecs[kcount] = l; + kzvecs[kcount] = m; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 2.0*unitkx*k*ug[kcount]; + eg[kcount][1] = 2.0*unitky*l*ug[kcount]; + eg[kcount][2] = 2.0*unitkz*m*ug[kcount]; + vterm = -2.0*(1.0/sqk + 0.25*g_ewald_sq_inv); + vg[kcount][0] = 1.0 + vterm*(unitkx*k)*(unitkx*k); + vg[kcount][1] = 1.0 + vterm*(unitky*l)*(unitky*l); + vg[kcount][2] = 1.0 + vterm*(unitkz*m)*(unitkz*m); + vg[kcount][3] = vterm*unitkx*k*unitky*l; + vg[kcount][4] = vterm*unitkx*k*unitkz*m; + vg[kcount][5] = vterm*unitky*l*unitkz*m; + kcount++; + + kxvecs[kcount] = k; + kyvecs[kcount] = -l; + kzvecs[kcount] = m; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 2.0*unitkx*k*ug[kcount]; + eg[kcount][1] = -2.0*unitky*l*ug[kcount]; + eg[kcount][2] = 2.0*unitkz*m*ug[kcount]; + vg[kcount][0] = 1.0 + vterm*(unitkx*k)*(unitkx*k); + vg[kcount][1] = 1.0 + vterm*(unitky*l)*(unitky*l); + vg[kcount][2] = 1.0 + vterm*(unitkz*m)*(unitkz*m); + vg[kcount][3] = -vterm*unitkx*k*unitky*l; + vg[kcount][4] = vterm*unitkx*k*unitkz*m; + vg[kcount][5] = -vterm*unitky*l*unitkz*m; + kcount++; + + kxvecs[kcount] = k; + kyvecs[kcount] = l; + kzvecs[kcount] = -m; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 2.0*unitkx*k*ug[kcount]; + eg[kcount][1] = 2.0*unitky*l*ug[kcount]; + eg[kcount][2] = -2.0*unitkz*m*ug[kcount]; + vg[kcount][0] = 1.0 + vterm*(unitkx*k)*(unitkx*k); + vg[kcount][1] = 1.0 + vterm*(unitky*l)*(unitky*l); + vg[kcount][2] = 1.0 + vterm*(unitkz*m)*(unitkz*m); + vg[kcount][3] = vterm*unitkx*k*unitky*l; + vg[kcount][4] = -vterm*unitkx*k*unitkz*m; + vg[kcount][5] = -vterm*unitky*l*unitkz*m; + kcount++; + + kxvecs[kcount] = k; + kyvecs[kcount] = -l; + kzvecs[kcount] = -m; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 2.0*unitkx*k*ug[kcount]; + eg[kcount][1] = -2.0*unitky*l*ug[kcount]; + eg[kcount][2] = -2.0*unitkz*m*ug[kcount]; + vg[kcount][0] = 1.0 + vterm*(unitkx*k)*(unitkx*k); + vg[kcount][1] = 1.0 + vterm*(unitky*l)*(unitky*l); + vg[kcount][2] = 1.0 + vterm*(unitkz*m)*(unitkz*m); + vg[kcount][3] = -vterm*unitkx*k*unitky*l; + vg[kcount][4] = -vterm*unitkx*k*unitkz*m; + vg[kcount][5] = vterm*unitky*l*unitkz*m; + kcount++;; + } + } + } + } +} + +/* ---------------------------------------------------------------------- + allocate memory that depends on # of K-vectors +------------------------------------------------------------------------- */ + +void Ewald::allocate() +{ + kxvecs = new int[kmax3d]; + kyvecs = new int[kmax3d]; + kzvecs = new int[kmax3d]; + + ug = new double[kmax3d]; + eg = memory->create_2d_double_array(kmax3d,3,"ewald:eg"); + vg = memory->create_2d_double_array(kmax3d,6,"ewald:vg"); + + sfacrl = new double[kmax3d]; + sfacim = new double[kmax3d]; + sfacrl_all = new double[kmax3d]; + sfacim_all = new double[kmax3d]; +} + +/* ---------------------------------------------------------------------- + deallocate memory that depends on # of K-vectors +------------------------------------------------------------------------- */ + +void Ewald::deallocate() +{ + delete [] kxvecs; + delete [] kyvecs; + delete [] kzvecs; + + delete [] ug; + memory->destroy_2d_double_array(eg); + memory->destroy_2d_double_array(vg); + + delete [] sfacrl; + delete [] sfacim; + delete [] sfacrl_all; + delete [] sfacim_all; +} + +/* ---------------------------------------------------------------------- + Slab-geometry correction term to dampen inter-slab interactions between + periodically repeating slabs. Yields good approximation to 2-D Ewald if + adequate empty space is left between repeating slabs (J. Chem. Phys. + 111, 3155). Slabs defined here to be parallel to the xy plane. +------------------------------------------------------------------------- */ + +void Ewald::slabcorr(int eflag) +{ + // compute local contribution to global dipole moment + + double *q = atom->q; + double **x = atom->x; + int nlocal = atom->nlocal; + + double dipole = 0.0; + for (int i = 0; i < nlocal; i++) dipole += q[i]*x[i][2]; + + // sum local contributions to get global dipole moment + + double dipole_all; + MPI_Allreduce(&dipole,&dipole_all,1,MPI_DOUBLE,MPI_SUM,world); + + // compute corrections + + double e_slabcorr = 2.0*PI*dipole_all*dipole_all/volume; + + if (eflag) energy += qqrd2e*e_slabcorr; + + // add on force corrections + + double ffact = -4.0*PI*dipole_all/volume; + double **f = atom->f; + + for (int i = 0; i < nlocal; i++) f[i][2] += qqrd2e*q[i]*ffact; +} + +/* ---------------------------------------------------------------------- + memory usage of local arrays +------------------------------------------------------------------------- */ + +int Ewald::memory_usage() +{ + int bytes = 3 * kmax3d * sizeof(int); + bytes += (1 + 3 + 6) * kmax3d * sizeof(double); + bytes += 4 * kmax3d * sizeof(double); + bytes += nmax*3 * sizeof(double); + bytes += 2 * (2*kmax+1)*3*nmax * sizeof(double); + return bytes; +} diff --git a/src/KSPACE/ewald.h b/src/KSPACE/ewald.h new file mode 100644 index 0000000000000000000000000000000000000000..cbd28c5693d0ff847d19ab91a427acb576dc020d --- /dev/null +++ b/src/KSPACE/ewald.h @@ -0,0 +1,52 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef EWALD_H +#define EWALD_H + +#include "kspace.h" + +class Ewald : public KSpace { + public: + Ewald(int, char **); + ~Ewald(); + void init(); + void setup(); + void compute(int, int); + int memory_usage(); + + private: + double PI; + double precision; + int kcount,kmax,kmax3d,kmax_created; + double qqrd2e; + double gsqmx,qsum,qsqsum,volume; + int nmax; + + double unitk[3]; + int *kxvecs,*kyvecs,*kzvecs; + double *ug; + double **eg,**vg; + double **ek; + double *sfacrl,*sfacim,*sfacrl_all,*sfacim_all; + double ***cs,***sn; + + void eik_dot_r(); + void coeffs(); + void allocate(); + void deallocate(); + void slabcorr(int); +}; + +#endif + diff --git a/src/KSPACE/fft3d.cpp b/src/KSPACE/fft3d.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3af1923d1524a21e43b8fe2bfb7dfbd71dd8c5f9 --- /dev/null +++ b/src/KSPACE/fft3d.cpp @@ -0,0 +1,999 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Jim Shepherd (GA Tech) added SGI SCSL support +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "stdio.h" +#include "stdlib.h" +#include "math.h" +#include "fft3d.h" +#include "remap.h" + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +/* ---------------------------------------------------------------------- + Data layout for 3d FFTs: + + data set of Nfast x Nmid x Nslow elements is owned by P procs + on input, each proc owns a subsection of the elements + on output, each proc will own a (possibly different) subsection + my subsection must not overlap with any other proc's subsection, + i.e. the union of all proc's input (or output) subsections must + exactly tile the global Nfast x Nmid x Nslow data set + when called from C, all subsection indices are + C-style from 0 to N-1 where N = Nfast or Nmid or Nslow + when called from F77, all subsection indices are + F77-style from 1 to N where N = Nfast or Nmid or Nslow + a proc can own 0 elements on input or output + by specifying hi index < lo index + on both input and output, data is stored contiguously on a processor + with a fast-varying, mid-varying, and slow-varying index +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Perform 3d FFT + + Arguments: + in starting address of input data on this proc + out starting address of where output data for this proc + will be placed (can be same as in) + flag 1 for forward FFT, -1 for inverse FFT + plan plan returned by previous call to fft_3d_create_plan +------------------------------------------------------------------------- */ + +void fft_3d(FFT_DATA *in, FFT_DATA *out, int flag, struct fft_plan_3d *plan) +{ + int i,total,length,offset,num; + double norm; + FFT_DATA *data,*copy; + + // system specific constants + +#ifdef FFT_SCSL + int isys = 0; + FFT_PREC scalef = 1.0; +#endif +#ifdef FFT_DEC + char c = 'C'; + char f = 'F'; + char b = 'B'; + int one = 1; +#endif +#ifdef FFT_T3E + int isys = 0; + double scalef = 1.0; +#endif + + // pre-remap to prepare for 1st FFTs if needed + // copy = loc for remap result + + if (plan->pre_plan) { + if (plan->pre_target == 0) copy = out; + else copy = plan->copy; + remap_3d((double *) in, (double *) copy, (double *) plan->scratch, + plan->pre_plan); + data = copy; + } + else + data = in; + + // 1d FFTs along fast axis + + total = plan->total1; + length = plan->length1; + +#ifdef FFT_SGI + for (offset = 0; offset < total; offset += length) + FFT_1D(flag,length,&data[offset],1,plan->coeff1); +#endif +#ifdef FFT_SCSL + for (offset = 0; offset < total; offset += length) + FFT_1D(flag,length,scalef,&data[offset],&data[offset],plan->coeff1, + plan->work1,&isys); +#endif +#ifdef FFT_INTEL + for (offset = 0; offset < total; offset += length) + FFT_1D(&data[offset],&length,&flag,plan->coeff1); +#endif +#ifdef FFT_DEC + if (flag == -1) + for (offset = 0; offset < total; offset += length) + FFT_1D(&c,&c,&f,&data[offset],&data[offset],&length,&one); + else + for (offset = 0; offset < total; offset += length) + FFT_1D(&c,&c,&b,&data[offset],&data[offset],&length,&one); +#endif +#ifdef FFT_T3E + for (offset = 0; offset < total; offset += length) + FFT_1D(&flag,&length,&scalef,&data[offset],&data[offset],plan->coeff1, + plan->work1,&isys); +#endif +#ifdef FFT_FFTW + if (flag == -1) + fftw(plan->plan_fast_forward,total/length,data,1,length,NULL,0,0); + else + fftw(plan->plan_fast_backward,total/length,data,1,length,NULL,0,0); +#endif + + // 1st mid-remap to prepare for 2nd FFTs + // copy = loc for remap result + + if (plan->mid1_target == 0) copy = out; + else copy = plan->copy; + remap_3d((double *) data, (double *) copy, (double *) plan->scratch, + plan->mid1_plan); + data = copy; + + // 1d FFTs along mid axis + + total = plan->total2; + length = plan->length2; + +#ifdef FFT_SGI + for (offset = 0; offset < total; offset += length) + FFT_1D(flag,length,&data[offset],1,plan->coeff2); +#endif +#ifdef FFT_SCSL + for (offset = 0; offset < total; offset += length) + FFT_1D(flag,length,scalef,&data[offset],&data[offset],plan->coeff2, + plan->work2,&isys); +#endif +#ifdef FFT_INTEL + for (offset = 0; offset < total; offset += length) + FFT_1D(&data[offset],&length,&flag,plan->coeff2); +#endif +#ifdef FFT_DEC + if (flag == -1) + for (offset = 0; offset < total; offset += length) + FFT_1D(&c,&c,&f,&data[offset],&data[offset],&length,&one); + else + for (offset = 0; offset < total; offset += length) + FFT_1D(&c,&c,&b,&data[offset],&data[offset],&length,&one); +#endif +#ifdef FFT_T3E + for (offset = 0; offset < total; offset += length) + FFT_1D(&flag,&length,&scalef,&data[offset],&data[offset],plan->coeff2, + plan->work2,&isys); +#endif +#ifdef FFT_FFTW + if (flag == -1) + fftw(plan->plan_mid_forward,total/length,data,1,length,NULL,0,0); + else + fftw(plan->plan_mid_backward,total/length,data,1,length,NULL,0,0); +#endif + + // 2nd mid-remap to prepare for 3rd FFTs + // copy = loc for remap result + + if (plan->mid2_target == 0) copy = out; + else copy = plan->copy; + remap_3d((double *) data, (double *) copy, (double *) plan->scratch, + plan->mid2_plan); + data = copy; + + // 1d FFTs along slow axis + + total = plan->total3; + length = plan->length3; + +#ifdef FFT_SGI + for (offset = 0; offset < total; offset += length) + FFT_1D(flag,length,&data[offset],1,plan->coeff3); +#endif +#ifdef FFT_SCSL + for (offset = 0; offset < total; offset += length) + FFT_1D(flag,length,scalef,&data[offset],&data[offset],plan->coeff3, + plan->work3,&isys); +#endif +#ifdef FFT_INTEL + for (offset = 0; offset < total; offset += length) + FFT_1D(&data[offset],&length,&flag,plan->coeff3); +#endif +#ifdef FFT_DEC + if (flag == -1) + for (offset = 0; offset < total; offset += length) + FFT_1D(&c,&c,&f,&data[offset],&data[offset],&length,&one); + else + for (offset = 0; offset < total; offset += length) + FFT_1D(&c,&c,&b,&data[offset],&data[offset],&length,&one); +#endif +#ifdef FFT_T3E + for (offset = 0; offset < total; offset += length) + FFT_1D(&flag,&length,&scalef,&data[offset],&data[offset],plan->coeff3, + plan->work3,&isys); +#endif +#ifdef FFT_FFTW + if (flag == -1) + fftw(plan->plan_slow_forward,total/length,data,1,length,NULL,0,0); + else + fftw(plan->plan_slow_backward,total/length,data,1,length,NULL,0,0); +#endif + + // post-remap to put data in output format if needed + // destination is always out + + if (plan->post_plan) + remap_3d((double *) data, (double *) out, (double *) plan->scratch, + plan->post_plan); + + // scaling if required + +#ifndef FFT_T3E + if (flag == 1 && plan->scaled) { + norm = plan->norm; + num = plan->normnum; + for (i = 0; i < num; i++) { + out[i].re *= norm; + out[i].im *= norm; + } + } +#endif + +#ifdef FFT_T3E + if (flag == 1 && plan->scaled) { + norm = plan->norm; + num = plan->normnum; + for (i = 0; i < num; i++) out[i] *= (norm,norm); + } +#endif +} + +/* ---------------------------------------------------------------------- + Create plan for performing a 3d FFT + + Arguments: + comm MPI communicator for the P procs which own the data + nfast,nmid,nslow size of global 3d matrix + in_ilo,in_ihi input bounds of data I own in fast index + in_jlo,in_jhi input bounds of data I own in mid index + in_klo,in_khi input bounds of data I own in slow index + out_ilo,out_ihi output bounds of data I own in fast index + out_jlo,out_jhi output bounds of data I own in mid index + out_klo,out_khi output bounds of data I own in slow index + scaled 0 = no scaling of result, 1 = scaling + permute permutation in storage order of indices on output + 0 = no permutation + 1 = permute once = mid->fast, slow->mid, fast->slow + 2 = permute twice = slow->fast, fast->mid, mid->slow + nbuf returns size of internal storage buffers used by FFT +------------------------------------------------------------------------- */ + +struct fft_plan_3d *fft_3d_create_plan( + MPI_Comm comm, int nfast, int nmid, int nslow, + int in_ilo, int in_ihi, int in_jlo, int in_jhi, + int in_klo, int in_khi, + int out_ilo, int out_ihi, int out_jlo, int out_jhi, + int out_klo, int out_khi, + int scaled, int permute, int *nbuf) +{ + struct fft_plan_3d *plan; + int me,nprocs; + int i,num,flag,remapflag,fftflag; + int first_ilo,first_ihi,first_jlo,first_jhi,first_klo,first_khi; + int second_ilo,second_ihi,second_jlo,second_jhi,second_klo,second_khi; + int third_ilo,third_ihi,third_jlo,third_jhi,third_klo,third_khi; + int out_size,first_size,second_size,third_size,copy_size,scratch_size; + int np1,np2,ip1,ip2; + int list[50]; + + // system specific variables + +#ifdef FFT_SCSL + FFT_DATA dummy_d[5]; + FFT_PREC dummy_p[5]; + int isign,isys; + FFT_PREC scalef; +#endif +#ifdef FFT_INTEL + FFT_DATA dummy; +#endif +#ifdef FFT_T3E + FFT_DATA dummy[5]; + int isign,isys; + double scalef; +#endif + + // query MPI info + + MPI_Comm_rank(comm,&me); + MPI_Comm_size(comm,&nprocs); + +#ifdef FFT_NONE + if (me == 0) { + printf("ERROR: Cannot use FFTs with FFT_NONE set\n"); + return NULL; + } +#endif + + // compute division of procs in 2 dimensions not on-processor + + bifactor(nprocs,&np1,&np2); + ip1 = me % np1; + ip2 = me/np1; + + // allocate memory for plan data struct + + plan = (struct fft_plan_3d *) malloc(sizeof(struct fft_plan_3d)); + if (plan == NULL) return NULL; + + // remap from initial distribution to layout needed for 1st set of 1d FFTs + // not needed if all procs own entire fast axis initially + // first indices = distribution after 1st set of FFTs + + if (in_ilo == 0 && in_ihi == nfast-1) + flag = 0; + else + flag = 1; + + MPI_Allreduce(&flag,&remapflag,1,MPI_INT,MPI_MAX,comm); + + if (remapflag == 0) { + first_ilo = in_ilo; + first_ihi = in_ihi; + first_jlo = in_jlo; + first_jhi = in_jhi; + first_klo = in_klo; + first_khi = in_khi; + plan->pre_plan = NULL; + } + else { + first_ilo = 0; + first_ihi = nfast - 1; + first_jlo = ip1*nmid/np1; + first_jhi = (ip1+1)*nmid/np1 - 1; + first_klo = ip2*nslow/np2; + first_khi = (ip2+1)*nslow/np2 - 1; + plan->pre_plan = + remap_3d_create_plan(comm,in_ilo,in_ihi,in_jlo,in_jhi,in_klo,in_khi, + first_ilo,first_ihi,first_jlo,first_jhi, + first_klo,first_khi, + FFT_PRECISION,0,0,2); + if (plan->pre_plan == NULL) return NULL; + } + + // 1d FFTs along fast axis + + plan->length1 = nfast; + plan->total1 = nfast * (first_jhi-first_jlo+1) * (first_khi-first_klo+1); + + // remap from 1st to 2nd FFT + // choose which axis is split over np1 vs np2 to minimize communication + // second indices = distribution after 2nd set of FFTs + + second_ilo = ip1*nfast/np1; + second_ihi = (ip1+1)*nfast/np1 - 1; + second_jlo = 0; + second_jhi = nmid - 1; + second_klo = ip2*nslow/np2; + second_khi = (ip2+1)*nslow/np2 - 1; + plan->mid1_plan = + remap_3d_create_plan(comm, + first_ilo,first_ihi,first_jlo,first_jhi, + first_klo,first_khi, + second_ilo,second_ihi,second_jlo,second_jhi, + second_klo,second_khi, + FFT_PRECISION,1,0,2); + if (plan->mid1_plan == NULL) return NULL; + + // 1d FFTs along mid axis + + plan->length2 = nmid; + plan->total2 = (second_ihi-second_ilo+1) * nmid * (second_khi-second_klo+1); + + // remap from 2nd to 3rd FFT + // if final distribution is permute=2 with all procs owning entire slow axis + // then this remapping goes directly to final distribution + // third indices = distribution after 3rd set of FFTs + + if (permute == 2 && out_klo == 0 && out_khi == nslow-1) + flag = 0; + else + flag = 1; + + MPI_Allreduce(&flag,&remapflag,1,MPI_INT,MPI_MAX,comm); + + if (remapflag == 0) { + third_ilo = out_ilo; + third_ihi = out_ihi; + third_jlo = out_jlo; + third_jhi = out_jhi; + third_klo = out_klo; + third_khi = out_khi; + } + else { + third_ilo = ip1*nfast/np1; + third_ihi = (ip1+1)*nfast/np1 - 1; + third_jlo = ip2*nmid/np2; + third_jhi = (ip2+1)*nmid/np2 - 1; + third_klo = 0; + third_khi = nslow - 1; + } + + plan->mid2_plan = + remap_3d_create_plan(comm, + second_jlo,second_jhi,second_klo,second_khi, + second_ilo,second_ihi, + third_jlo,third_jhi,third_klo,third_khi, + third_ilo,third_ihi, + FFT_PRECISION,1,0,2); + if (plan->mid2_plan == NULL) return NULL; + + // 1d FFTs along slow axis + + plan->length3 = nslow; + plan->total3 = (third_ihi-third_ilo+1) * (third_jhi-third_jlo+1) * nslow; + + // remap from 3rd FFT to final distribution + // not needed if permute = 2 and third indices = out indices on all procs + + if (permute == 2 && + out_ilo == third_ilo && out_ihi == third_ihi && + out_jlo == third_jlo && out_jhi == third_jhi && + out_klo == third_klo && out_khi == third_khi) + flag = 0; + else + flag = 1; + + MPI_Allreduce(&flag,&remapflag,1,MPI_INT,MPI_MAX,comm); + + if (remapflag == 0) + plan->post_plan = NULL; + else { + plan->post_plan = + remap_3d_create_plan(comm, + third_klo,third_khi,third_ilo,third_ihi, + third_jlo,third_jhi, + out_klo,out_khi,out_ilo,out_ihi, + out_jlo,out_jhi, + FFT_PRECISION,(permute+1)%3,0,2); + if (plan->post_plan == NULL) return NULL; + } + + // configure plan memory pointers and allocate work space + // out_size = amount of memory given to FFT by user + // first/second/third_size = amount of memory needed after pre,mid1,mid2 remaps + // copy_size = amount needed internally for extra copy of data + // scratch_size = amount needed internally for remap scratch space + // for each remap: + // out space used for result if big enough, else require copy buffer + // accumulate largest required remap scratch space + + out_size = (out_ihi-out_ilo+1) * (out_jhi-out_jlo+1) * (out_khi-out_klo+1); + first_size = (first_ihi-first_ilo+1) * (first_jhi-first_jlo+1) * + (first_khi-first_klo+1); + second_size = (second_ihi-second_ilo+1) * (second_jhi-second_jlo+1) * + (second_khi-second_klo+1); + third_size = (third_ihi-third_ilo+1) * (third_jhi-third_jlo+1) * + (third_khi-third_klo+1); + + copy_size = 0; + scratch_size = 0; + + if (plan->pre_plan) { + if (first_size <= out_size) + plan->pre_target = 0; + else { + plan->pre_target = 1; + copy_size = MAX(copy_size,first_size); + } + scratch_size = MAX(scratch_size,first_size); + } + + if (plan->mid1_plan) { + if (second_size <= out_size) + plan->mid1_target = 0; + else { + plan->mid1_target = 1; + copy_size = MAX(copy_size,second_size); + } + scratch_size = MAX(scratch_size,second_size); + } + + if (plan->mid2_plan) { + if (third_size <= out_size) + plan->mid2_target = 0; + else { + plan->mid2_target = 1; + copy_size = MAX(copy_size,third_size); + } + scratch_size = MAX(scratch_size,third_size); + } + + if (plan->post_plan) + scratch_size = MAX(scratch_size,out_size); + + *nbuf = copy_size + scratch_size; + + if (copy_size) { + plan->copy = (FFT_DATA *) malloc(copy_size*sizeof(FFT_DATA)); + if (plan->copy == NULL) return NULL; + } + else plan->copy = NULL; + + if (scratch_size) { + plan->scratch = (FFT_DATA *) malloc(scratch_size*sizeof(FFT_DATA)); + if (plan->scratch == NULL) return NULL; + } + else plan->scratch = NULL; + + // system specific pre-computation of 1d FFT coeffs + // and scaling normalization + +#ifdef FFT_SGI + + plan->coeff1 = (FFT_DATA *) malloc((nfast+15)*sizeof(FFT_DATA)); + plan->coeff2 = (FFT_DATA *) malloc((nmid+15)*sizeof(FFT_DATA)); + plan->coeff3 = (FFT_DATA *) malloc((nslow+15)*sizeof(FFT_DATA)); + + if (plan->coeff1 == NULL || plan->coeff2 == NULL || + plan->coeff3 == NULL) return NULL; + + FFT_1D_INIT(nfast,plan->coeff1); + FFT_1D_INIT(nmid,plan->coeff2); + FFT_1D_INIT(nslow,plan->coeff3); + + if (scaled == 0) + plan->scaled = 0; + else { + plan->scaled = 1; + plan->norm = 1.0/(nfast*nmid*nslow); + plan->normnum = (out_ihi-out_ilo+1) * (out_jhi-out_jlo+1) * + (out_khi-out_klo+1); + } + +#endif + +#ifdef FFT_SCSL + + plan->coeff1 = (FFT_PREC *) malloc((2*nfast+30)*sizeof(FFT_PREC)); + plan->coeff2 = (FFT_PREC *) malloc((2*nmid+30)*sizeof(FFT_PREC)); + plan->coeff3 = (FFT_PREC *) malloc((2*nslow+30)*sizeof(FFT_PREC)); + + if (plan->coeff1 == NULL || plan->coeff2 == NULL || + plan->coeff3 == NULL) return NULL; + + plan->work1 = (FFT_PREC *) malloc((2*nfast)*sizeof(FFT_PREC)); + plan->work2 = (FFT_PREC *) malloc((2*nmid)*sizeof(FFT_PREC)); + plan->work3 = (FFT_PREC *) malloc((2*nslow)*sizeof(FFT_PREC)); + + if (plan->work1 == NULL || plan->work2 == NULL || + plan->work3 == NULL) return NULL; + + isign = 0; + scalef = 1.0; + isys = 0; + + FFT_1D_INIT(isign,nfast,scalef,dummy_d,dummy_d,plan->coeff1,dummy_p,&isys); + FFT_1D_INIT(isign,nmid,scalef,dummy_d,dummy_d,plan->coeff2,dummy_p,&isys); + FFT_1D_INIT(isign,nslow,scalef,dummy_d,dummy_d,plan->coeff3,dummy_p,&isys); + + if (scaled == 0) + plan->scaled = 0; + else { + plan->scaled = 1; + plan->norm = 1.0/(nfast*nmid*nslow); + plan->normnum = (out_ihi-out_ilo+1) * (out_jhi-out_jlo+1) * + (out_khi-out_klo+1); + } + +#endif + +#ifdef FFT_INTEL + + flag = 0; + + num = 0; + factor(nfast,&num,list); + for (i = 0; i < num; i++) + if (list[i] != 2 && list[i] != 3 && list[i] != 5) flag = 1; + num = 0; + factor(nmid,&num,list); + for (i = 0; i < num; i++) + if (list[i] != 2 && list[i] != 3 && list[i] != 5) flag = 1; + num = 0; + factor(nslow,&num,list); + for (i = 0; i < num; i++) + if (list[i] != 2 && list[i] != 3 && list[i] != 5) flag = 1; + + MPI_Allreduce(&flag,&fftflag,1,MPI_INT,MPI_MAX,comm); + if (fftflag) { + if (me == 0) printf("ERROR: FFTs are not power of 2,3,5\n"); + return NULL; + } + + plan->coeff1 = (FFT_DATA *) malloc((3*nfast/2+1)*sizeof(FFT_DATA)); + plan->coeff2 = (FFT_DATA *) malloc((3*nmid/2+1)*sizeof(FFT_DATA)); + plan->coeff3 = (FFT_DATA *) malloc((3*nslow/2+1)*sizeof(FFT_DATA)); + + if (plan->coeff1 == NULL || plan->coeff2 == NULL || + plan->coeff3 == NULL) return NULL; + + flag = 0; + FFT_1D_INIT(&dummy,&nfast,&flag,plan->coeff1); + FFT_1D_INIT(&dummy,&nmid,&flag,plan->coeff2); + FFT_1D_INIT(&dummy,&nslow,&flag,plan->coeff3); + + if (scaled == 0) { + plan->scaled = 1; + plan->norm = nfast*nmid*nslow; + plan->normnum = (out_ihi-out_ilo+1) * (out_jhi-out_jlo+1) * + (out_khi-out_klo+1); + } + else + plan->scaled = 0; + +#endif + +#ifdef FFT_DEC + + if (scaled == 0) { + plan->scaled = 1; + plan->norm = nfast*nmid*nslow; + plan->normnum = (out_ihi-out_ilo+1) * (out_jhi-out_jlo+1) * + (out_khi-out_klo+1); + } + else + plan->scaled = 0; + +#endif + +#ifdef FFT_T3E + + plan->coeff1 = (double *) malloc((12*nfast)*sizeof(double)); + plan->coeff2 = (double *) malloc((12*nmid)*sizeof(double)); + plan->coeff3 = (double *) malloc((12*nslow)*sizeof(double)); + + if (plan->coeff1 == NULL || plan->coeff2 == NULL || + plan->coeff3 == NULL) return NULL; + + plan->work1 = (double *) malloc((8*nfast)*sizeof(double)); + plan->work2 = (double *) malloc((8*nmid)*sizeof(double)); + plan->work3 = (double *) malloc((8*nslow)*sizeof(double)); + + if (plan->work1 == NULL || plan->work2 == NULL || + plan->work3 == NULL) return NULL; + + isign = 0; + scalef = 1.0; + isys = 0; + + FFT_1D_INIT(&isign,&nfast,&scalef,dummy,dummy,plan->coeff1,dummy,&isys); + FFT_1D_INIT(&isign,&nmid,&scalef,dummy,dummy,plan->coeff2,dummy,&isys); + FFT_1D_INIT(&isign,&nslow,&scalef,dummy,dummy,plan->coeff3,dummy,&isys); + + if (scaled == 0) + plan->scaled = 0; + else { + plan->scaled = 1; + plan->norm = 1.0/(nfast*nmid*nslow); + plan->normnum = (out_ihi-out_ilo+1) * (out_jhi-out_jlo+1) * + (out_khi-out_klo+1); + } + +#endif + +#ifdef FFT_FFTW + + plan->plan_fast_forward = + fftw_create_plan(nfast,FFTW_FORWARD,FFTW_ESTIMATE | FFTW_IN_PLACE); + plan->plan_fast_backward = + fftw_create_plan(nfast,FFTW_BACKWARD,FFTW_ESTIMATE | FFTW_IN_PLACE); + + if (nmid == nfast) { + plan->plan_mid_forward = plan->plan_fast_forward; + plan->plan_mid_backward = plan->plan_fast_backward; + } + else { + plan->plan_mid_forward = + fftw_create_plan(nmid,FFTW_FORWARD,FFTW_ESTIMATE | FFTW_IN_PLACE); + plan->plan_mid_backward = + fftw_create_plan(nmid,FFTW_BACKWARD,FFTW_ESTIMATE | FFTW_IN_PLACE); + } + + if (nslow == nfast) { + plan->plan_slow_forward = plan->plan_fast_forward; + plan->plan_slow_backward = plan->plan_fast_backward; + } + else if (nslow == nmid) { + plan->plan_slow_forward = plan->plan_mid_forward; + plan->plan_slow_backward = plan->plan_mid_backward; + } + else { + plan->plan_slow_forward = + fftw_create_plan(nslow,FFTW_FORWARD,FFTW_ESTIMATE | FFTW_IN_PLACE); + plan->plan_slow_backward = + fftw_create_plan(nslow,FFTW_BACKWARD,FFTW_ESTIMATE | FFTW_IN_PLACE); + } + + if (scaled == 0) + plan->scaled = 0; + else { + plan->scaled = 1; + plan->norm = 1.0/(nfast*nmid*nslow); + plan->normnum = (out_ihi-out_ilo+1) * (out_jhi-out_jlo+1) * + (out_khi-out_klo+1); + } + +#endif + + return plan; +} + +/* ---------------------------------------------------------------------- + Destroy a 3d fft plan +------------------------------------------------------------------------- */ + +void fft_3d_destroy_plan(struct fft_plan_3d *plan) +{ + if (plan->pre_plan) remap_3d_destroy_plan(plan->pre_plan); + if (plan->mid1_plan) remap_3d_destroy_plan(plan->mid1_plan); + if (plan->mid2_plan) remap_3d_destroy_plan(plan->mid2_plan); + if (plan->post_plan) remap_3d_destroy_plan(plan->post_plan); + + if (plan->copy) free(plan->copy); + if (plan->scratch) free(plan->scratch); + +#ifdef FFT_SGI + free(plan->coeff1); + free(plan->coeff2); + free(plan->coeff3); +#endif +#ifdef FFT_SCSL + free(plan->coeff1); + free(plan->coeff2); + free(plan->coeff3); + free(plan->work1); + free(plan->work2); + free(plan->work3); +#endif +#ifdef FFT_INTEL + free(plan->coeff1); + free(plan->coeff2); + free(plan->coeff3); +#endif +#ifdef FFT_T3E + free(plan->coeff1); + free(plan->coeff2); + free(plan->coeff3); + free(plan->work1); + free(plan->work2); + free(plan->work3); +#endif +#ifdef FFT_FFTW + if (plan->plan_slow_forward != plan->plan_mid_forward && + plan->plan_slow_forward != plan->plan_fast_forward) { + fftw_destroy_plan(plan->plan_slow_forward); + fftw_destroy_plan(plan->plan_slow_backward); + } + if (plan->plan_mid_forward != plan->plan_fast_forward) { + fftw_destroy_plan(plan->plan_mid_forward); + fftw_destroy_plan(plan->plan_mid_backward); + } + fftw_destroy_plan(plan->plan_fast_forward); + fftw_destroy_plan(plan->plan_fast_backward); +#endif + + free(plan); +} + +/* ---------------------------------------------------------------------- + recursively divide n into small factors, return them in list +------------------------------------------------------------------------- */ + +void factor(int n, int *num, int *list) +{ + if (n == 1) { + return; + } + else if (n % 2 == 0) { + *list = 2; + (*num)++; + factor(n/2,num,list+1); + } + else if (n % 3 == 0) { + *list = 3; + (*num)++; + factor(n/3,num,list+1); + } + else if (n % 5 == 0) { + *list = 5; + (*num)++; + factor(n/5,num,list+1); + } + else if (n % 7 == 0) { + *list = 7; + (*num)++; + factor(n/7,num,list+1); + } + else if (n % 11 == 0) { + *list = 11; + (*num)++; + factor(n/11,num,list+1); + } + else if (n % 13 == 0) { + *list = 13; + (*num)++; + factor(n/13,num,list+1); + } + else { + *list = n; + (*num)++; + return; + } +} + +/* ---------------------------------------------------------------------- + divide n into 2 factors of as equal size as possible +------------------------------------------------------------------------- */ + +void bifactor(int n, int *factor1, int *factor2) +{ + int n1,n2,facmax; + + facmax = static_cast<int> (sqrt((double) n)); + + for (n1 = facmax; n1 > 0; n1--) { + n2 = n/n1; + if (n1*n2 == n) { + *factor1 = n1; + *factor2 = n2; + return; + } + } +} + +/* ---------------------------------------------------------------------- + perform just the 1d FFTs needed by a 3d FFT, no data movement + used for timing purposes + + Arguments: + in starting address of input data on this proc, all set to 0.0 + nsize size of in + flag 1 for forward FFT, -1 for inverse FFT + plan plan returned by previous call to fft_3d_create_plan +------------------------------------------------------------------------- */ + +void fft_1d_only(FFT_DATA *data, int nsize, int flag, struct fft_plan_3d *plan) +{ + int i,total,length,offset,num; + double norm; + + // system specific constants + +#ifdef FFT_SCSL + int isys = 0; + FFT_PREC scalef = 1.0; +#endif +#ifdef FFT_DEC + char c = 'C'; + char f = 'F'; + char b = 'B'; + int one = 1; +#endif +#ifdef FFT_T3E + int isys = 0; + double scalef = 1.0; +#endif + + // total = size of data needed in each dim + // length = length of 1d FFT in each dim + // total/length = # of 1d FFTs in each dim + // if total > nsize, limit # of 1d FFTs to available size of data + + int total1 = plan->total1; + int length1 = plan->length1; + int total2 = plan->total2; + int length2 = plan->length2; + int total3 = plan->total3; + int length3 = plan->length3; + + if (total1 > nsize) total1 = (nsize/length1) * length1; + if (total2 > nsize) total2 = (nsize/length2) * length2; + if (total3 > nsize) total3 = (nsize/length3) * length3; + + // perform 1d FFTs in each of 3 dimensions + // data is just an array of 0.0 + +#ifdef FFT_SGI + for (offset = 0; offset < total1; offset += length1) + FFT_1D(flag,length1,&data[offset],1,plan->coeff1); + for (offset = 0; offset < total2; offset += length2) + FFT_1D(flag,length2,&data[offset],1,plan->coeff2); + for (offset = 0; offset < total3; offset += length3) + FFT_1D(flag,length3,&data[offset],1,plan->coeff3); +#endif +#ifdef FFT_SCSL + for (offset = 0; offset < total1; offset += length1) + FFT_1D(flag,length1,scalef,&data[offset],&data[offset],plan->coeff1, + plan->work1,&isys); + for (offset = 0; offset < total2; offset += length2) + FFT_1D(flag,length2,scalef,&data[offset],&data[offset],plan->coeff2, + plan->work2,&isys); + for (offset = 0; offset < total3; offset += length3) + FFT_1D(flag,length3,scalef,&data[offset],&data[offset],plan->coeff3, + plan->work3,&isys); +#endif +#ifdef FFT_INTEL + for (offset = 0; offset < total1; offset += length1) + FFT_1D(&data[offset],&length1,&flag,plan->coeff1); + for (offset = 0; offset < total2; offset += length2) + FFT_1D(&data[offset],&length2,&flag,plan->coeff2); + for (offset = 0; offset < total3; offset += length3) + FFT_1D(&data[offset],&length3,&flag,plan->coeff3); +#endif +#ifdef FFT_DEC + if (flag == -1) { + for (offset = 0; offset < total1; offset += length1) + FFT_1D(&c,&c,&f,&data[offset],&data[offset],&length1,&one); + for (offset = 0; offset < total2; offset += length2) + FFT_1D(&c,&c,&f,&data[offset],&data[offset],&length2,&one); + for (offset = 0; offset < total3; offset += length3) + FFT_1D(&c,&c,&f,&data[offset],&data[offset],&length3,&one); + } else { + for (offset = 0; offset < total1; offset += length1) + FFT_1D(&c,&c,&b,&data[offset],&data[offset],&length1,&one); + for (offset = 0; offset < total2; offset += length2) + FFT_1D(&c,&c,&b,&data[offset],&data[offset],&length2,&one); + for (offset = 0; offset < total3; offset += length3) + FFT_1D(&c,&c,&b,&data[offset],&data[offset],&length3,&one); + } +#endif +#ifdef FFT_T3E + for (offset = 0; offset < total1; offset += length1) + FFT_1D(&flag,&length1,&scalef,&data[offset],&data[offset],plan->coeff1, + plan->work1,&isys); + for (offset = 0; offset < total2; offset += length2) + FFT_1D(&flag,&length2,&scalef,&data[offset],&data[offset],plan->coeff2, + plan->work2,&isys); + for (offset = 0; offset < total3; offset += length3) + FFT_1D(&flag,&length3,&scalef,&data[offset],&data[offset],plan->coeff3, + plan->work3,&isys); +#endif +#ifdef FFT_FFTW + if (flag == -1) { + fftw(plan->plan_fast_forward,total1/length1,data,1,0,NULL,0,0); + fftw(plan->plan_mid_forward,total2/length2,data,1,0,NULL,0,0); + fftw(plan->plan_slow_forward,total3/length3,data,1,0,NULL,0,0); + } else { + fftw(plan->plan_fast_backward,total1/length1,data,1,0,NULL,0,0); + fftw(plan->plan_mid_backward,total2/length2,data,1,0,NULL,0,0); + fftw(plan->plan_slow_backward,total3/length3,data,1,0,NULL,0,0); + } +#endif + + // scaling if required + // limit num to size of data + +#ifndef FFT_T3E + if (flag == 1 && plan->scaled) { + norm = plan->norm; + num = MIN(plan->normnum,nsize); + for (i = 0; i < num; i++) { + data[i].re *= norm; + data[i].im *= norm; + } + } +#endif + +#ifdef FFT_T3E + if (flag == 1 && plan->scaled) { + norm = plan->norm; + num = MIN(plan->normnum,nsize); + for (i = 0; i < num; i++) data[i] *= (norm,norm); + } +#endif +} diff --git a/src/KSPACE/fft3d.h b/src/KSPACE/fft3d.h new file mode 100644 index 0000000000000000000000000000000000000000..eb4a179db3140f07dc1221386d0cde206e5f1aa4 --- /dev/null +++ b/src/KSPACE/fft3d.h @@ -0,0 +1,242 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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. +------------------------------------------------------------------------- */ + +// User-settable FFT precision + +// FFT_PRECISION = 1 is single-precision complex (4-byte real, 4-byte imag) +// FFT_PRECISION = 2 is double-precision complex (8-byte real, 8-byte imag) + +#define FFT_PRECISION 2 + +// ------------------------------------------------------------------------- + +// Data types for single-precision complex + +#if FFT_PRECISION == 1 + +#ifdef FFT_SGI +#include "fft.h" +typedef complex FFT_DATA; +#define FFT_1D cfft1d +#define FFT_1D_INIT cfft1di +extern "C" { + int cfft1d(int, int, FFT_DATA *, int, FFT_DATA *); + FFT_DATA *cfft1di(int, FFT_DATA *); +} + +#endif + +#ifdef FFT_SCSL +#include <scsl_fft.h> +typedef scsl_complex FFT_DATA; +typedef float FFT_PREC; +#define FFT_1D ccfft +#define FFT_1D_INIT ccfft +extern "C" { + int ccfft(int, int, FFT_PREC, FFT_DATA *, FFT_DATA *, + FFT_PREC *, FFT_PREC *, int *); +} + +#endif + +#ifdef FFT_INTEL +typedef struct { + float re; + float im; +} FFT_DATA; +#define FFT_1D cfft1d_ +#define FFT_1D_INIT cfft1d_ +extern "C" { + void cfft1d_(FFT_DATA *, int *, int *, FFT_DATA *); +} +#endif + +#ifdef FFT_DEC +typedef struct { + float re; + float im; +} FFT_DATA; +#define FFT_1D cfft_ +extern "C" { + void cfft_(char *, char *, char *, FFT_DATA *, FFT_DATA *, int *, int *); +} +#endif + +#ifdef FFT_T3E +#include <complex.h> +typedef complex single FFT_DATA; +#define FFT_1D GGFFT +#define FFT_1D_INIT GGFFT +extern "C" { + void GGFFT(int *, int *, double *, FFT_DATA *, FFT_DATA *, + double *, double *, int *); +} +#endif + +#ifdef FFT_FFTW +#include "fftw.h" +typedef FFTW_COMPLEX FFT_DATA; +#endif + +#ifdef FFT_NONE +typedef struct { + float re; + float im; +} FFT_DATA; +#endif + +#endif + +// ------------------------------------------------------------------------- + +// Data types for double-precision complex + +#if FFT_PRECISION == 2 + +#ifdef FFT_SGI +#include "fft.h" +typedef zomplex FFT_DATA; +#define FFT_1D zfft1d +#define FFT_1D_INIT zfft1di +extern "C" { + int zfft1d(int, int, FFT_DATA *, int, FFT_DATA *); + FFT_DATA *zfft1di(int, FFT_DATA *); +} +#endif + +#ifdef FFT_SCSL +#include <scsl_fft.h> +typedef scsl_zomplex FFT_DATA; +typedef double FFT_PREC; +#define FFT_1D zzfft +#define FFT_1D_INIT zzfft +extern "C" { + int zzfft(int, int, FFT_PREC, FFT_DATA *, FFT_DATA *, + FFT_PREC *, FFT_PREC *, int *); +} +#endif + +#ifdef FFT_INTEL +typedef struct { + double re; + double im; +} FFT_DATA; +#define FFT_1D zfft1d_ +#define FFT_1D_INIT zfft1d_ +extern "C" { + void zfft1d_(FFT_DATA *, int *, int *, FFT_DATA *); +} +#endif + +#ifdef FFT_DEC +typedef struct { + double re; + double im; +} FFT_DATA; +#define FFT_1D zfft_ +extern "C" { + void zfft_(char *, char *, char *, FFT_DATA *, FFT_DATA *, int *, int *); +} +#endif + +#ifdef FFT_T3E +#include <complex.h> +typedef complex double FFT_DATA; +#define FFT_1D CCFFT +#define FFT_1D_INIT CCFFT +extern "C" { + void CCFFT(int *, int *, double *, FFT_DATA *, FFT_DATA *, + double *, double *, int *); +} +#endif + +#ifdef FFT_FFTW +#include "fftw.h" +typedef FFTW_COMPLEX FFT_DATA; +#endif + +#ifdef FFT_NONE +typedef struct { + double re; + double im; +} FFT_DATA; +#endif + +#endif + +// ------------------------------------------------------------------------- + +// details of how to do a 3d FFT + +struct fft_plan_3d { + struct remap_plan_3d *pre_plan; // remap from input -> 1st FFTs + struct remap_plan_3d *mid1_plan; // remap from 1st -> 2nd FFTs + struct remap_plan_3d *mid2_plan; // remap from 2nd -> 3rd FFTs + struct remap_plan_3d *post_plan; // remap from 3rd FFTs -> output + FFT_DATA *copy; // memory for remap results (if needed) + FFT_DATA *scratch; // scratch space for remaps + int total1,total2,total3; // # of 1st,2nd,3rd FFTs (times length) + int length1,length2,length3; // length of 1st,2nd,3rd FFTs + int pre_target; // where to put remap results + int mid1_target,mid2_target; + int scaled; // whether to scale FFT results + int normnum; // # of values to rescale + double norm; // normalization factor for rescaling + + // system specific 1d FFT info +#ifdef FFT_SGI + FFT_DATA *coeff1; + FFT_DATA *coeff2; + FFT_DATA *coeff3; +#endif +#ifdef FFT_SCSL + FFT_PREC *coeff1; + FFT_PREC *coeff2; + FFT_PREC *coeff3; + FFT_PREC *work1; + FFT_PREC *work2; + FFT_PREC *work3; +#endif +#ifdef FFT_INTEL + FFT_DATA *coeff1; + FFT_DATA *coeff2; + FFT_DATA *coeff3; +#endif +#ifdef FFT_T3E + double *coeff1; + double *coeff2; + double *coeff3; + double *work1; + double *work2; + double *work3; +#endif +#ifdef FFT_FFTW + fftw_plan plan_fast_forward; + fftw_plan plan_fast_backward; + fftw_plan plan_mid_forward; + fftw_plan plan_mid_backward; + fftw_plan plan_slow_forward; + fftw_plan plan_slow_backward; +#endif +}; + +// function prototypes + +void fft_3d(FFT_DATA *, FFT_DATA *, int, struct fft_plan_3d *); +struct fft_plan_3d *fft_3d_create_plan(MPI_Comm, int, int, int, + int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int *); +void fft_3d_destroy_plan(struct fft_plan_3d *); +void factor(int, int *, int *); +void bifactor(int, int *, int *); +void fft_1d_only(FFT_DATA *, int, int, struct fft_plan_3d *); diff --git a/src/KSPACE/fft3d_wrap.cpp b/src/KSPACE/fft3d_wrap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..af72fdf4a23ef741b2cbfefc44203d16d49ce23b --- /dev/null +++ b/src/KSPACE/fft3d_wrap.cpp @@ -0,0 +1,53 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "fft3d_wrap.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FFT3d::FFT3d(MPI_Comm comm, int nfast, int nmid, int nslow, + int in_ilo, int in_ihi, int in_jlo, int in_jhi, + int in_klo, int in_khi, + int out_ilo, int out_ihi, int out_jlo, int out_jhi, + int out_klo, int out_khi, + int scaled, int permute, int *nbuf) +{ + plan = fft_3d_create_plan(comm,nfast,nmid,nslow, + in_ilo,in_ihi,in_jlo,in_jhi,in_klo,in_khi, + out_ilo,out_ihi,out_jlo,out_jhi,out_klo,out_khi, + scaled,permute,nbuf); + if (plan == NULL) error->one("Could not create 3d FFT plan"); +} + +/* ---------------------------------------------------------------------- */ + +FFT3d::~FFT3d() +{ + fft_3d_destroy_plan(plan); +} + +/* ---------------------------------------------------------------------- */ + +void FFT3d::compute(double *in, double *out, int flag) +{ + fft_3d((FFT_DATA *) in,(FFT_DATA *) out,flag,plan); +} + +/* ---------------------------------------------------------------------- */ + +void FFT3d::timing1d(double *in, int nsize, int flag) +{ + fft_1d_only((FFT_DATA *) in,nsize,flag,plan); +} diff --git a/src/KSPACE/fft3d_wrap.h b/src/KSPACE/fft3d_wrap.h new file mode 100644 index 0000000000000000000000000000000000000000..2d32e346266b030e4eb4b5bae17106206958e0de --- /dev/null +++ b/src/KSPACE/fft3d_wrap.h @@ -0,0 +1,32 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FFT3D_WRAP_H +#define FFT3D_WRAP_H + +#include "lammps.h" +#include "fft3d.h" + +class FFT3d : public LAMMPS { + public: + FFT3d(MPI_Comm,int,int,int,int,int,int,int,int,int, + int,int,int,int,int,int,int,int,int *); + ~FFT3d(); + void compute(double *, double *, int); + void timing1d(double *, int, int); + + private: + struct fft_plan_3d *plan; +}; + +#endif diff --git a/src/KSPACE/pair_buck_coul_long.cpp b/src/KSPACE/pair_buck_coul_long.cpp new file mode 100644 index 0000000000000000000000000000000000000000..450fcb5efa52c6e88ad1c333f0c0af15206d9182 --- /dev/null +++ b/src/KSPACE/pair_buck_coul_long.cpp @@ -0,0 +1,446 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "pair_buck_coul_long.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "kspace.h" +#include "update.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#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 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +PairBuckCoulLong::~PairBuckCoulLong() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(cut_lj); + memory->destroy_2d_double_array(cut_ljsq); + memory->destroy_2d_double_array(a); + memory->destroy_2d_double_array(rho); + memory->destroy_2d_double_array(c); + memory->destroy_2d_double_array(rhoinv); + memory->destroy_2d_double_array(buck1); + memory->destroy_2d_double_array(buck2); + memory->destroy_2d_double_array(offset); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairBuckCoulLong::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcecoul,forcebuck,fforce,factor_coul,factor_lj; + double grij,expm2,prefactor,t,erfc; + double factor,phicoul,phibuck,r,rexp; + int *neighs; + double **f; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r2inv = 1.0/rsq; + + if (rsq < cut_coulsq) { + 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 = qqrd2e * qtmp*q[j]/r; + forcecoul = prefactor * (erfc + EWALD_F*grij*expm2); + if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*prefactor; + } else forcecoul = 0.0; + + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + r = sqrt(rsq); + rexp = exp(-r*rhoinv[itype][jtype]); + forcebuck = buck1[itype][jtype]*r*rexp - buck2[itype][jtype]*r6inv; + } else forcebuck = 0.0; + + fforce = (forcecoul + factor_lj*forcebuck) * 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 (newton_pair || j < nlocal) factor = 1.0; + else factor = 0.5; + if (rsq < cut_coulsq) { + phicoul = prefactor*erfc; + if (factor_coul < 1.0) phicoul -= (1.0-factor_coul)*prefactor; + eng_coul += factor*phicoul; + } + if (rsq < cut_ljsq[itype][jtype]) { + phibuck = a[itype][jtype]*rexp - c[itype][jtype]*r6inv - + offset[itype][jtype]; + eng_vdwl += factor*factor_lj*phibuck; + } + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairBuckCoulLong::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"); + + cut_lj = memory->create_2d_double_array(n+1,n+1,"pair:cut_lj"); + cut_ljsq = memory->create_2d_double_array(n+1,n+1,"pair:cut_ljsq"); + a = memory->create_2d_double_array(n+1,n+1,"pair:a"); + rho = memory->create_2d_double_array(n+1,n+1,"pair:rho"); + c = memory->create_2d_double_array(n+1,n+1,"pair:c"); + rhoinv = memory->create_2d_double_array(n+1,n+1,"pair:rhoinv"); + buck1 = memory->create_2d_double_array(n+1,n+1,"pair:buck1"); + buck2 = memory->create_2d_double_array(n+1,n+1,"pair:buck2"); + offset = memory->create_2d_double_array(n+1,n+1,"pair:offset"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairBuckCoulLong::settings(int narg, char **arg) +{ + if (narg < 1 || narg > 2) error->all("Illegal pair_style command"); + + cut_lj_global = atof(arg[0]); + if (narg == 1) cut_coul = cut_lj_global; + else cut_coul = atof(arg[1]); + + // 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_lj[i][j] = cut_lj_global; + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairBuckCoulLong::coeff(int narg, char **arg) +{ + if (narg < 5 || narg > 6) 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); + + double a_one = atof(arg[2]); + double rho_one = atof(arg[3]); + double c_one = atof(arg[4]); + + double cut_lj_one = cut_lj_global; + if (narg == 6) cut_lj_one = atof(arg[5]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + for (int j = MAX(jlo,i); j <= jhi; j++) { + a[i][j] = a_one; + rho[i][j] = rho_one; + c[i][j] = c_one; + cut_lj[i][j] = cut_lj_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairBuckCoulLong::init_one(int i, int j) +{ + if (setflag[i][j] == 0) error->all("All pair coeffs are not set"); + + double cut = MAX(cut_lj[i][j],cut_coul); + cut_ljsq[i][j] = cut_lj[i][j] * cut_lj[i][j]; + + rhoinv[i][j] = 1.0/rho[i][j]; + buck1[i][j] = a[i][j]/rho[i][j]; + buck2[i][j] = 6.0*c[i][j]; + + if (offset_flag) { + double rexp = exp(-cut_lj[i][j]/rho[i][j]); + offset[i][j] = a[i][j]*rexp - c[i][j]/pow(cut_lj[i][j],6.0); + } else offset[i][j] = 0.0; + + cut_ljsq[j][i] = cut_ljsq[i][j]; + a[j][i] = a[i][j]; + c[j][i] = c[i][j]; + rhoinv[j][i] = rhoinv[i][j]; + buck1[j][i] = buck1[i][j]; + buck2[j][i] = buck2[i][j]; + offset[j][i] = offset[i][j]; + + return cut; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairBuckCoulLong::init_style() +{ + // require an atom style with charge defined + + if (atom->charge_allow == 0) + error->all("Must use charged atom style with this pair style"); + + cut_coulsq = cut_coul * cut_coul; + + // insure use of KSpace long-range solver, set g_ewald + + if (force->kspace == NULL) + error->all("Pair style is incompatible with KSpace style"); + else if (strcmp(force->kspace_style,"ewald") == 0) + g_ewald = force->kspace->g_ewald; + else if (strcmp(force->kspace_style,"pppm") == 0) + g_ewald = force->kspace->g_ewald; + else error->all("Pair style is incompatible with KSpace style"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairBuckCoulLong::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + for (j = i; j <= atom->ntypes; j++) { + fwrite(&setflag[i][j],sizeof(int),1,fp); + if (setflag[i][j]) { + fwrite(&a[i][j],sizeof(double),1,fp); + fwrite(&rho[i][j],sizeof(double),1,fp); + fwrite(&c[i][j],sizeof(double),1,fp); + fwrite(&cut_lj[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairBuckCoulLong::read_restart(FILE *fp) +{ + read_restart_settings(fp); + + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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(&a[i][j],sizeof(double),1,fp); + fread(&rho[i][j],sizeof(double),1,fp); + fread(&c[i][j],sizeof(double),1,fp); + fread(&cut_lj[i][j],sizeof(double),1,fp); + } + MPI_Bcast(&a[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&rho[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&c[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_lj[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairBuckCoulLong::write_restart_settings(FILE *fp) +{ + fwrite(&cut_lj_global,sizeof(double),1,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 PairBuckCoulLong::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&cut_lj_global,sizeof(double),1,fp); + fread(&cut_coul,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + MPI_Bcast(&cut_lj_global,1,MPI_DOUBLE,0,world); + 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); +} + +/* ---------------------------------------------------------------------- */ + +void PairBuckCoulLong::single(int i, int j, int itype, int jtype, + double rsq, double factor_coul, double factor_lj, + int eflag, One &one) +{ + double r2inv,r6inv,r,rexp,grij,expm2,t,erfc,prefactor; + double forcecoul,forcebuck,phicoul,phibuck; + + r2inv = 1.0/rsq; + if (rsq < cut_coulsq) { + 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 forcecoul = 0.0; + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + r = sqrt(rsq); + rexp = exp(-r*rhoinv[itype][jtype]); + forcebuck = buck1[itype][jtype]*r*rexp - buck2[itype][jtype]*r6inv; + } else forcebuck = 0.0; + one.fforce = (forcecoul + factor_lj*forcebuck) * r2inv; + + if (eflag) { + if (rsq < cut_coulsq) { + phicoul = prefactor*erfc; + if (factor_coul < 1.0) phicoul -= (1.0-factor_coul)*prefactor; + one.eng_coul = phicoul; + } else one.eng_coul = 0.0; + if (rsq < cut_ljsq[itype][jtype]) { + phibuck = a[itype][jtype]*rexp - c[itype][jtype]*r6inv - + offset[itype][jtype]; + one.eng_vdwl = factor_lj*phibuck; + } else one.eng_vdwl = 0.0; + } +} diff --git a/src/KSPACE/pair_buck_coul_long.h b/src/KSPACE/pair_buck_coul_long.h new file mode 100644 index 0000000000000000000000000000000000000000..0f87844fa8d22fb4d737ac25acdc3370f494fff0 --- /dev/null +++ b/src/KSPACE/pair_buck_coul_long.h @@ -0,0 +1,47 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_BUCK_COUL_LONG_H +#define PAIR_BUCK_COUL_LONG_H + +#include "pair.h" + +class PairBuckCoulLong : public Pair { + public: + double cut_coul; + + PairBuckCoulLong() {} + ~PairBuckCoulLong(); + void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void init_style(); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + void single(int, int, int, int, double, double, double, int, One &); + + private: + double cut_lj_global; + double **cut_lj,**cut_ljsq; + double cut_coulsq; + double **a,**rho,**c; + double **rhoinv,**buck1,**buck2,**offset; + double g_ewald; + + void allocate(); +}; + +#endif diff --git a/src/KSPACE/pair_lj_charmm_coul_long.cpp b/src/KSPACE/pair_lj_charmm_coul_long.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d9e8d1713d163832018f07f4eac5c7a3ea57fdc0 --- /dev/null +++ b/src/KSPACE/pair_lj_charmm_coul_long.cpp @@ -0,0 +1,1160 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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_lj_charmm_coul_long.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "kspace.h" +#include "update.h" +#include "integrate.h" +#include "respa.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#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 + +/* ---------------------------------------------------------------------- */ + +PairLJCharmmCoulLong::PairLJCharmmCoulLong() +{ + respa_enable = 1; + ftable = NULL; +} + +/* ---------------------------------------------------------------------- */ + +PairLJCharmmCoulLong::~PairLJCharmmCoulLong() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(epsilon); + memory->destroy_2d_double_array(sigma); + memory->destroy_2d_double_array(eps14); + memory->destroy_2d_double_array(sigma14); + memory->destroy_2d_double_array(lj1); + memory->destroy_2d_double_array(lj2); + memory->destroy_2d_double_array(lj3); + memory->destroy_2d_double_array(lj4); + memory->destroy_2d_double_array(lj14_1); + memory->destroy_2d_double_array(lj14_2); + memory->destroy_2d_double_array(lj14_3); + memory->destroy_2d_double_array(lj14_4); + } + if (ftable) free_tables(); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype,itable; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz,fraction,table; + double r,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double grij,expm2,prefactor,t,erfc; + double factor,phicoul,philj,switch1,switch2; + int *neighs; + double **f; + float rsq; + int *int_rsq = (int *) &rsq; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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_bothsq) { + r2inv = 1.0/rsq; + + if (rsq < cut_coulsq) { + 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; + } + } + } else forcecoul = 0.0; + + if (rsq < cut_ljsq) { + r6inv = r2inv*r2inv*r2inv; + jtype = type[j]; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + } else forcelj = 0.0; + + fforce = (forcecoul + factor_lj*forcelj) * 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 (newton_pair || j < nlocal) factor = 1.0; + else factor = 0.5; + if (rsq < cut_coulsq) { + 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; + eng_coul += factor*phicoul; + } + if (rsq < cut_ljsq) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + philj *= switch1; + } + eng_vdwl += factor*factor_lj*philj; + } + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::compute_inner() +{ + int i,j,k,numneigh,itype,jtype; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double rsw; + int *neighs; + + double **f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + double cut_out_on = cut_respa[0]; + double cut_out_off = cut_respa[1]; + + double cut_out_diff = cut_out_off - cut_out_on; + double cut_out_on_sq = cut_out_on*cut_out_on; + double cut_out_off_sq = cut_out_off*cut_out_off; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh_inner[i]; + numneigh = neighbor->numneigh_inner[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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_out_off_sq) { + r2inv = 1.0/rsq; + forcecoul = qqrd2e * qtmp*q[j]*sqrt(r2inv); + if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*forcecoul; + + r6inv = r2inv*r2inv*r2inv; + jtype = type[j]; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + + fforce = (forcecoul + factor_lj*forcelj) * r2inv; + + if (rsq > cut_out_on_sq) { + rsw = (sqrt(rsq) - cut_out_on)/cut_out_diff; + fforce *= 1.0 + rsw*rsw*(2.0*rsw-3.0); + } + + 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; + } + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::compute_middle() +{ + int i,j,k,numneigh,itype,jtype; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double philj,switch1,switch2; + double rsw; + int *neighs; + + double **f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + double cut_in_off = cut_respa[0]; + double cut_in_on = cut_respa[1]; + double cut_out_on = cut_respa[2]; + double cut_out_off = cut_respa[3]; + + double cut_in_diff = cut_in_on - cut_in_off; + double cut_out_diff = cut_out_off - cut_out_on; + double cut_in_off_sq = cut_in_off*cut_in_off; + double cut_in_on_sq = cut_in_on*cut_in_on; + double cut_out_on_sq = cut_out_on*cut_out_on; + double cut_out_off_sq = cut_out_off*cut_out_off; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh_middle[i]; + numneigh = neighbor->numneigh_middle[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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_out_off_sq && rsq > cut_in_off_sq) { + r2inv = 1.0/rsq; + forcecoul = qqrd2e * qtmp*q[j]*sqrt(r2inv); + if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*forcecoul; + + r6inv = r2inv*r2inv*r2inv; + jtype = type[j]; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + + fforce = (forcecoul + factor_lj*forcelj) * r2inv; + if (rsq < cut_in_on_sq) { + rsw = (sqrt(rsq) - cut_in_off)/cut_in_diff; + fforce *= rsw*rsw*(3.0 - 2.0*rsw); + } + if (rsq > cut_out_on_sq) { + rsw = (sqrt(rsq) - cut_out_on)/cut_out_diff; + fforce *= 1.0 + rsw*rsw*(2.0*rsw - 3.0); + } + + 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; + } + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::compute_outer(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype,itable; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz,fraction,table; + double r,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double grij,expm2,prefactor,t,erfc; + double factor,phicoul,philj,switch1,switch2; + double rsw; + int *neighs; + float rsq; + int *int_rsq = (int *) &rsq; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + double **f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + double cut_in_off = cut_respa[2]; + double cut_in_on = cut_respa[3]; + + double cut_in_diff = cut_in_on - cut_in_off; + double cut_in_off_sq = cut_in_off*cut_in_off; + double cut_in_on_sq = cut_in_on*cut_in_on; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cut_bothsq) { + r2inv = 1.0/rsq; + + if (rsq < cut_coulsq) { + 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 - 1.0); + if (rsq > cut_in_off_sq) { + if (rsq < cut_in_on_sq) { + rsw = (r - cut_in_off)/cut_in_diff; + forcecoul += prefactor*rsw*rsw*(3.0 - 2.0*rsw); + if (factor_coul < 1.0) + forcecoul -= + (1.0-factor_coul)*prefactor*rsw*rsw*(3.0 - 2.0*rsw); + } else { + forcecoul += prefactor; + 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; + } + } + } else forcecoul = 0.0; + + if (rsq < cut_ljsq && rsq > cut_in_off_sq) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + if (rsq < cut_in_on_sq) { + rsw = (sqrtf(rsq) - cut_in_off)/cut_in_diff; + forcelj *= rsw*rsw*(3.0 - 2.0*rsw); + } + } else forcelj = 0.0; + + fforce = (forcecoul + forcelj) * 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 (newton_pair || j < nlocal) factor = 1.0; + else factor = 0.5; + if (rsq < cut_coulsq) { + if (!ncoultablebits || rsq <= tabinnersq) { + phicoul = prefactor*erfc; + if (factor_coul < 1.0) phicoul -= (1.0-factor_coul)*prefactor; + } else { + table = etable[itable] + fraction*detable[itable]; + phicoul = qtmp*q[j] * table; + if (factor_coul < 1.0) { + table = ptable[itable] + fraction*dptable[itable]; + prefactor = qtmp*q[j] * table; + phicoul -= (1.0-factor_coul)*prefactor; + } + } + eng_coul += factor*phicoul; + } + if (rsq < cut_ljsq) { + r6inv = r2inv*r2inv*r2inv; + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + philj *= switch1; + } + eng_vdwl += factor*factor_lj*philj; + } + } + + if (vflag) { + if (rsq < cut_coulsq) { + if (!ncoultablebits || rsq <= tabinnersq) { + forcecoul = prefactor * (erfc + EWALD_F*grij*expm2); + if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*prefactor; + } else { + table = vtable[itable] + fraction*dvtable[itable]; + forcecoul = qtmp*q[j] * table; + if (factor_coul < 1.0) { + table = ptable[itable] + fraction*dptable[itable]; + prefactor = qtmp*q[j] * table; + phicoul -= (1.0-factor_coul)*prefactor; + } + } + } else forcecoul = 0.0; + + if (rsq <= cut_in_off_sq) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + } else if (rsq <= cut_in_on_sq) { + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + } + + fforce = (forcecoul + factor_lj*forcelj) * r2inv; + + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::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"); + + epsilon = memory->create_2d_double_array(n+1,n+1,"pair:epsilon"); + sigma = memory->create_2d_double_array(n+1,n+1,"pair:sigma"); + eps14 = memory->create_2d_double_array(n+1,n+1,"pair:eps14"); + sigma14 = memory->create_2d_double_array(n+1,n+1,"pair:sigma14"); + lj1 = memory->create_2d_double_array(n+1,n+1,"pair:lj1"); + lj2 = memory->create_2d_double_array(n+1,n+1,"pair:lj2"); + lj3 = memory->create_2d_double_array(n+1,n+1,"pair:lj3"); + lj4 = memory->create_2d_double_array(n+1,n+1,"pair:lj4"); + lj14_1 = memory->create_2d_double_array(n+1,n+1,"pair:lj14_1"); + lj14_2 = memory->create_2d_double_array(n+1,n+1,"pair:lj14_2"); + lj14_3 = memory->create_2d_double_array(n+1,n+1,"pair:lj14_3"); + lj14_4 = memory->create_2d_double_array(n+1,n+1,"pair:lj14_4"); +} + +/* ---------------------------------------------------------------------- + global settings + unlike other pair styles, + there are no individual pair settings that these override +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::settings(int narg, char **arg) +{ + if (narg != 2 && narg != 3) error->all("Illegal pair_style command"); + + cut_lj_inner = atof(arg[0]); + cut_lj = atof(arg[1]); + if (narg == 2) cut_coul = cut_lj; + else cut_coul = atof(arg[2]); +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::coeff(int narg, char **arg) +{ + if (narg != 4 && narg != 6) error->all("Illegal pair_coeff command"); + 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 = atof(arg[2]); + double sigma_one = atof(arg[3]); + double eps14_one = epsilon_one; + double sigma14_one = sigma_one; + if (narg == 6) { + eps14_one = atof(arg[4]); + sigma14_one = atof(arg[5]); + } + + 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; + eps14[i][j] = eps14_one; + sigma14[i][j] = sigma14_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairLJCharmmCoulLong::init_one(int i, int j) +{ + // always mix arithmetically + + if (setflag[i][j] == 0) { + epsilon[i][j] = sqrt(epsilon[i][i]*epsilon[j][j]); + sigma[i][j] = 0.5 * (sigma[i][i] + sigma[j][j]); + eps14[i][j] = sqrt(eps14[i][i]*eps14[j][j]); + sigma14[i][j] = 0.5 * (sigma14[i][i] + sigma14[j][j]); + } + + double cut = MAX(cut_lj,cut_coul); + + 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); + lj14_1[i][j] = 48.0 * eps14[i][j] * pow(sigma14[i][j],12.0); + lj14_2[i][j] = 24.0 * eps14[i][j] * pow(sigma14[i][j],6.0); + lj14_3[i][j] = 4.0 * eps14[i][j] * pow(sigma14[i][j],12.0); + lj14_4[i][j] = 4.0 * eps14[i][j] * pow(sigma14[i][j],6.0); + + lj1[j][i] = lj1[i][j]; + lj2[j][i] = lj2[i][j]; + lj3[j][i] = lj3[i][j]; + lj4[j][i] = lj4[i][j]; + lj14_1[j][i] = lj14_1[i][j]; + lj14_2[j][i] = lj14_2[i][j]; + lj14_3[j][i] = lj14_3[i][j]; + lj14_4[j][i] = lj14_4[i][j]; + + return cut; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::init_style() +{ + // require an atom style with charge defined + + if (atom->charge_allow == 0) + error->all("Must use charged atom style with this pair style"); + + // require cut_lj_inner < cut_lj + + if (cut_lj_inner >= cut_lj) + error->all("Pair inner cutoff >= Pair outer cutoff"); + + cut_lj_innersq = cut_lj_inner * cut_lj_inner; + cut_ljsq = cut_lj * cut_lj; + cut_coulsq = cut_coul * cut_coul; + cut_bothsq = MAX(cut_ljsq,cut_coulsq); + + denom_lj = (cut_ljsq-cut_lj_innersq) * (cut_ljsq-cut_lj_innersq) * + (cut_ljsq-cut_lj_innersq); + + // set & error check interior rRESPA cutoffs + + cut_respa = NULL; + if (strcmp(update->integrate_style,"respa") == 0) { + if (((Respa *) update->integrate)->level_inner >= 0) { + cut_respa = ((Respa *) update->integrate)->cutoff; + if (MIN(cut_lj,cut_coul) < cut_respa[3]) + error->all("Pair cutoff < Respa interior cutoff"); + if (cut_lj_inner < cut_respa[1]) + error->all("Pair inner 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"); + else if (strcmp(force->kspace_style,"ewald") == 0) + g_ewald = force->kspace->g_ewald; + else if (strcmp(force->kspace_style,"pppm") == 0) + g_ewald = force->kspace->g_ewald; + else if (strcmp(force->kspace_style,"pppm2") == 0) + g_ewald = force->kspace->g_ewald; + else error->all("Pair style is incompatible with KSpace style"); + + // setup force tables + + if (ncoultablebits) init_tables(); +} + +/* ---------------------------------------------------------------------- + setup force tables used in compute routines +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::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 PairLJCharmmCoulLong::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + 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(&eps14[i][j],sizeof(double),1,fp); + fwrite(&sigma14[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::read_restart(FILE *fp) +{ + read_restart_settings(fp); + + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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(&eps14[i][j],sizeof(double),1,fp); + fread(&sigma14[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(&eps14[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&sigma14[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::write_restart_settings(FILE *fp) +{ + fwrite(&cut_lj_inner,sizeof(double),1,fp); + fwrite(&cut_lj,sizeof(double),1,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 PairLJCharmmCoulLong::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&cut_lj_inner,sizeof(double),1,fp); + fread(&cut_lj,sizeof(double),1,fp); + fread(&cut_coul,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + MPI_Bcast(&cut_lj_inner,1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_lj,1,MPI_DOUBLE,0,world); + 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 PairLJCharmmCoulLong::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 PairLJCharmmCoulLong::single(int i, int j, int itype, int jtype, + double rsq, + double factor_coul, double factor_lj, + int eflag, One &one) +{ + double r2inv,r6inv,r,grij,expm2,t,erfc,prefactor; + double switch1,switch2,fraction,table,forcecoul,forcelj,phicoul,philj; + int itable; + + r2inv = 1.0/rsq; + if (rsq < cut_coulsq) { + 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; + } + } + } else forcecoul = 0.0; + if (rsq < cut_ljsq) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + } else forcelj = 0.0; + one.fforce = (forcecoul + factor_lj*forcelj) * r2inv; + + if (eflag) { + if (rsq < cut_coulsq) { + 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; + } else one.eng_coul = 0.0; + if (rsq < cut_ljsq) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + philj *= switch1; + } + one.eng_vdwl = factor_lj*philj; + } else one.eng_vdwl = 0.0; + } +} diff --git a/src/KSPACE/pair_lj_cut_coul_long.cpp b/src/KSPACE/pair_lj_cut_coul_long.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1b6a3649760ce87b1ecbafdd1f3aa181ba141d5b --- /dev/null +++ b/src/KSPACE/pair_lj_cut_coul_long.cpp @@ -0,0 +1,1104 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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_lj_cut_coul_long.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "kspace.h" +#include "update.h" +#include "integrate.h" +#include "respa.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#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 + +/* ---------------------------------------------------------------------- */ + +PairLJCutCoulLong::PairLJCutCoulLong() +{ + respa_enable = 1; + ftable = NULL; +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +PairLJCutCoulLong::~PairLJCutCoulLong() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(cut_lj); + memory->destroy_2d_double_array(cut_ljsq); + memory->destroy_2d_double_array(epsilon); + memory->destroy_2d_double_array(sigma); + memory->destroy_2d_double_array(lj1); + memory->destroy_2d_double_array(lj2); + memory->destroy_2d_double_array(lj3); + memory->destroy_2d_double_array(lj4); + memory->destroy_2d_double_array(offset); + } + if (ftable) free_tables(); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCutCoulLong::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype,itable; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz,fraction,table; + double r,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double grij,expm2,prefactor,t,erfc; + double factor,phicoul,philj; + int *neighs; + double **f; + float rsq; + int *int_rsq = (int *) &rsq; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r2inv = 1.0/rsq; + + if (rsq < cut_coulsq) { + 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; + } + } + } else forcecoul = 0.0; + + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + } else forcelj = 0.0; + + fforce = (forcecoul + factor_lj*forcelj) * 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 (newton_pair || j < nlocal) factor = 1.0; + else factor = 0.5; + if (rsq < cut_coulsq) { + 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; + eng_coul += factor*phicoul; + } + if (rsq < cut_ljsq[itype][jtype]) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]) - + offset[itype][jtype]; + eng_vdwl += factor*factor_lj*philj; + } + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCutCoulLong::compute_inner() +{ + int i,j,k,numneigh,itype,jtype; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double rsw; + int *neighs; + + double **f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + double cut_out_on = cut_respa[0]; + double cut_out_off = cut_respa[1]; + + double cut_out_diff = cut_out_off - cut_out_on; + double cut_out_on_sq = cut_out_on*cut_out_on; + double cut_out_off_sq = cut_out_off*cut_out_off; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh_inner[i]; + numneigh = neighbor->numneigh_inner[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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_out_off_sq) { + r2inv = 1.0/rsq; + forcecoul = qqrd2e * qtmp*q[j]*sqrt(r2inv); + if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*forcecoul; + + jtype = type[j]; + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + } else forcelj = 0.0; + + fforce = (forcecoul + factor_lj*forcelj) * r2inv; + if (rsq > cut_out_on_sq) { + rsw = (sqrt(rsq) - cut_out_on)/cut_out_diff; + fforce *= 1.0 + rsw*rsw*(2.0*rsw-3.0); + } + + 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; + } + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCutCoulLong::compute_middle() +{ + int i,j,k,numneigh,itype,jtype; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double rsw; + int *neighs; + + double **f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + double cut_in_off = cut_respa[0]; + double cut_in_on = cut_respa[1]; + double cut_out_on = cut_respa[2]; + double cut_out_off = cut_respa[3]; + + double cut_in_diff = cut_in_on - cut_in_off; + double cut_out_diff = cut_out_off - cut_out_on; + double cut_in_off_sq = cut_in_off*cut_in_off; + double cut_in_on_sq = cut_in_on*cut_in_on; + double cut_out_on_sq = cut_out_on*cut_out_on; + double cut_out_off_sq = cut_out_off*cut_out_off; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh_middle[i]; + numneigh = neighbor->numneigh_middle[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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_out_off_sq && rsq > cut_in_off_sq) { + r2inv = 1.0/rsq; + forcecoul = qqrd2e * qtmp*q[j]*sqrt(r2inv); + if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*forcecoul; + + jtype = type[j]; + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + } else forcelj = 0.0; + + fforce = (forcecoul + factor_lj*forcelj) * r2inv; + if (rsq < cut_in_on_sq) { + rsw = (sqrt(rsq) - cut_in_off)/cut_in_diff; + fforce *= rsw*rsw*(3.0 - 2.0*rsw); + } + if (rsq > cut_out_on_sq) { + rsw = (sqrt(rsq) - cut_out_on)/cut_out_diff; + fforce *= 1.0 + rsw*rsw*(2.0*rsw - 3.0); + } + + 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; + } + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCutCoulLong::compute_outer(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype,itable; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz,fraction,table; + double r,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double grij,expm2,prefactor,t,erfc; + double factor,phicoul,philj; + double rsw; + int *neighs; + float rsq; + int *int_rsq = (int *) &rsq; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + double **f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + double cut_in_off = cut_respa[2]; + double cut_in_on = cut_respa[3]; + + double cut_in_diff = cut_in_on - cut_in_off; + double cut_in_off_sq = cut_in_off*cut_in_off; + double cut_in_on_sq = cut_in_on*cut_in_on; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r2inv = 1.0/rsq; + + if (rsq < cut_coulsq) { + 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 - 1.0); + if (rsq > cut_in_off_sq) { + if (rsq < cut_in_on_sq) { + rsw = (r - cut_in_off)/cut_in_diff; + forcecoul += prefactor*rsw*rsw*(3 - 2*rsw); + if (factor_coul < 1.0) + forcecoul -= (1.0-factor_coul)*prefactor*rsw*rsw*(3 - 2*rsw); + } else { + forcecoul += prefactor; + 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; + } + } + } else forcecoul = 0.0; + + if (rsq < cut_ljsq[itype][jtype] && rsq > cut_in_off_sq) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq < cut_in_on_sq) { + rsw = (sqrtf(rsq) - cut_in_off)/cut_in_diff; + forcelj *= rsw*rsw*(3 - 2*rsw); + } + } else forcelj = 0.0; + + fforce = (forcecoul + forcelj) * 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 (newton_pair || j < nlocal) factor = 1.0; + else factor = 0.5; + if (rsq < cut_coulsq) { + if (!ncoultablebits || rsq <= tabinnersq) { + phicoul = prefactor*erfc; + if (factor_coul < 1.0) phicoul -= (1.0-factor_coul)*prefactor; + } else { + table = etable[itable] + fraction*detable[itable]; + phicoul = qtmp*q[j] * table; + if (factor_coul < 1.0) { + table = ptable[itable] + fraction*dptable[itable]; + prefactor = qtmp*q[j] * table; + phicoul -= (1.0-factor_coul)*prefactor; + } + } + eng_coul += factor*phicoul; + } + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]) - + offset[itype][jtype]; + eng_vdwl += factor*factor_lj*philj; + } + } + + if (vflag) { + if (rsq < cut_coulsq) { + if (!ncoultablebits || rsq <= tabinnersq) { + forcecoul = prefactor * (erfc + EWALD_F*grij*expm2); + if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*prefactor; + } else { + table = vtable[itable] + fraction*dvtable[itable]; + forcecoul = qtmp*q[j] * table; + if (factor_coul < 1.0) { + table = ptable[itable] + fraction*dptable[itable]; + prefactor = qtmp*q[j] * table; + phicoul -= (1.0-factor_coul)*prefactor; + } + } + } else forcecoul = 0.0; + + if (rsq <= cut_in_off_sq) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + } else if (rsq <= cut_in_on_sq) + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + + fforce = (forcecoul + factor_lj*forcelj) * r2inv; + + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairLJCutCoulLong::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"); + + cut_lj = memory->create_2d_double_array(n+1,n+1,"pair:cut_lj"); + cut_ljsq = memory->create_2d_double_array(n+1,n+1,"pair:cut_ljsq"); + epsilon = memory->create_2d_double_array(n+1,n+1,"pair:epsilon"); + sigma = memory->create_2d_double_array(n+1,n+1,"pair:sigma"); + lj1 = memory->create_2d_double_array(n+1,n+1,"pair:lj1"); + lj2 = memory->create_2d_double_array(n+1,n+1,"pair:lj2"); + lj3 = memory->create_2d_double_array(n+1,n+1,"pair:lj3"); + lj4 = memory->create_2d_double_array(n+1,n+1,"pair:lj4"); + offset = memory->create_2d_double_array(n+1,n+1,"pair:offset"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairLJCutCoulLong::settings(int narg, char **arg) +{ + if (narg < 1 || narg > 2) error->all("Illegal pair_style command"); + + cut_lj_global = atof(arg[0]); + if (narg == 1) cut_coul = cut_lj_global; + else cut_coul = atof(arg[1]); + + // 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_lj[i][j] = cut_lj_global; + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairLJCutCoulLong::coeff(int narg, char **arg) +{ + if (narg < 4 || narg > 5) 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); + + double epsilon_one = atof(arg[2]); + double sigma_one = atof(arg[3]); + + double cut_lj_one = cut_lj_global; + if (narg == 5) cut_lj_one = atof(arg[4]); + + 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_lj[i][j] = cut_lj_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairLJCutCoulLong::init_one(int i, int j) +{ + 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_lj[i][j] = mix_distance(cut_lj[i][i],cut_lj[j][j]); + } + + double cut = MAX(cut_lj[i][j],cut_coul); + cut_ljsq[i][j] = cut_lj[i][j] * cut_lj[i][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_lj[i][j]; + offset[i][j] = 4.0 * epsilon[i][j] * (pow(ratio,12.0) - pow(ratio,6.0)); + } else offset[i][j] = 0.0; + + cut_ljsq[j][i] = cut_ljsq[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]; + + // compute I,J contribution to long-range tail correction + // count total # of atoms of type I and J via Allreduce + + if (tail_flag) { + int *type = atom->type; + int nlocal = atom->nlocal; + + double count[2],all[2]; + count[0] = count[1] = 0.0; + for (int k = 0; k < nlocal; k++) { + if (type[k] == i) count[0] += 1.0; + if (type[k] == j) count[1] += 1.0; + } + MPI_Allreduce(count,all,2,MPI_DOUBLE,MPI_SUM,world); + + double PI = 4.0*atan(1.0); + double sig2 = sigma[i][j]*sigma[i][j]; + double sig6 = sig2*sig2*sig2; + double rc3 = cut_lj[i][j]*cut_lj[i][j]*cut_lj[i][j]; + double rc6 = rc3*rc3; + double rc9 = rc3*rc6; + etail_ij = 8.0*PI*all[0]*all[1]*epsilon[i][j] * + sig6 * (sig6 - 3.0*rc6) / (9.0*rc9); + ptail_ij = 16.0*PI*all[0]*all[1]*epsilon[i][j] * + sig6 * (2.0*sig6 - 3.0*rc6) / (9.0*rc9); + } + + return cut; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairLJCutCoulLong::init_style() +{ + int i,j; + + // require an atom style with charge defined + + if (atom->charge_allow == 0) + error->all("Must use charged atom style with this pair style"); + + 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 (MIN(cut_lj[i][j],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"); + else if (strcmp(force->kspace_style,"ewald") == 0) + g_ewald = force->kspace->g_ewald; + else if (strcmp(force->kspace_style,"pppm") == 0) + g_ewald = force->kspace->g_ewald; + else error->all("Pair style is incompatible with KSpace style"); + + // setup force tables + + if (ncoultablebits) init_tables(); +} + +/* ---------------------------------------------------------------------- + setup force tables used in compute routines +------------------------------------------------------------------------- */ + +void PairLJCutCoulLong::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 PairLJCutCoulLong::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + 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_lj[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairLJCutCoulLong::read_restart(FILE *fp) +{ + read_restart_settings(fp); + + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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_lj[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_lj[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJCutCoulLong::write_restart_settings(FILE *fp) +{ + fwrite(&cut_lj_global,sizeof(double),1,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 PairLJCutCoulLong::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&cut_lj_global,sizeof(double),1,fp); + fread(&cut_coul,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + MPI_Bcast(&cut_lj_global,1,MPI_DOUBLE,0,world); + 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 PairLJCutCoulLong::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 PairLJCutCoulLong::single(int i, int j, int itype, int jtype, + double rsq, + double factor_coul, double factor_lj, + int eflag, One &one) +{ + double r2inv,r6inv,r,grij,expm2,t,erfc,prefactor; + double fraction,table,forcecoul,forcelj,phicoul,philj; + int itable; + + r2inv = 1.0/rsq; + if (rsq < cut_coulsq) { + 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; + } + } + } else forcecoul = 0.0; + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + } else forcelj = 0.0; + one.fforce = (forcecoul + factor_lj*forcelj) * r2inv; + + if (eflag) { + if (rsq < cut_coulsq) { + 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; + } else one.eng_coul = 0.0; + if (rsq < cut_ljsq[itype][jtype]) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]) - + offset[itype][jtype]; + one.eng_vdwl = factor_lj*philj; + } else one.eng_vdwl = 0.0; + } +} diff --git a/src/KSPACE/pair_lj_cut_coul_long.h b/src/KSPACE/pair_lj_cut_coul_long.h new file mode 100644 index 0000000000000000000000000000000000000000..bb5827489b66a460f74739653d239e3695299cd1 --- /dev/null +++ b/src/KSPACE/pair_lj_cut_coul_long.h @@ -0,0 +1,59 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_LJ_CUT_COUL_LONG_H +#define PAIR_LJ_CUT_COUL_LONG_H + +#include "pair.h" + +class PairLJCutCoulLong : public Pair { + public: + double cut_coul; + + PairLJCutCoulLong(); + ~PairLJCutCoulLong(); + virtual void compute(int, int); + virtual void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + virtual void init_style(); + void write_restart(FILE *); + void read_restart(FILE *); + virtual void write_restart_settings(FILE *); + virtual void read_restart_settings(FILE *); + virtual void single(int, int, int, int, double, double, double, int, One &); + + void compute_inner(); + void compute_middle(); + void compute_outer(int, int); + + protected: + double cut_lj_global; + double **cut_lj,**cut_ljsq; + double cut_coulsq; + double **epsilon,**sigma; + double **lj1,**lj2,**lj3,**lj4,**offset; + double *cut_respa; + double g_ewald; + + double tabinnersq; + double *rtable,*drtable,*ftable,*dftable,*ctable,*dctable; + double *etable,*detable,*ptable,*dptable,*vtable,*dvtable; + int ncoulshiftbits,ncoulmask; + + void allocate(); + void init_tables(); + void free_tables(); +}; + +#endif diff --git a/src/KSPACE/pair_lj_cut_coul_long_tip4p.cpp b/src/KSPACE/pair_lj_cut_coul_long_tip4p.cpp new file mode 100644 index 0000000000000000000000000000000000000000..151573bed7d5671a44d38b1bd77f91609a52a866 --- /dev/null +++ b/src/KSPACE/pair_lj_cut_coul_long_tip4p.cpp @@ -0,0 +1,528 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Amalie Frischknecht and Ahmed Ismail (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "pair_lj_cut_coul_long_tip4p.h" +#include "angle.h" +#include "atom.h" +#include "bond.h" +#include "comm.h" +#include "domain.h" +#include "force.h" +#include "kspace.h" +#include "update.h" +#include "respa.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#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 + +/* ---------------------------------------------------------------------- */ + +PairLJCutCoulLongTIP4P::PairLJCutCoulLongTIP4P() +{ + single_enable = 0; +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCutCoulLongTIP4P::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype,itable; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz,fraction,table; + double delx1,dely1,delz1,delx2,dely2,delz2,delx3,dely3,delz3; + double r,r2inv,r6inv,forcecoul,forcelj,cforce,negforce; + double factor_coul,factor_lj; + double grij,expm2,prefactor,t,erfc; + double phicoul,philj; + int iH1,iH2,jH1,jH2; + double xiM[3],xjM[3]; + double *x1,*x2; + double fO[3],fH[3]; + int *neighs; + double **f; + float rsq; + int *int_rsq = (int *) &rsq; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = tvirial[i] = 0.0; + + if (vflag == 2) { + f = update->f_pair; + tf = atom->f; + } + else f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + double qqrd2e = force->qqrd2e; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + if (itype == typeO) { + find_M(i,iH1,iH2,xiM); + x1 = xiM; + } else x1 = x[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + + r2inv = 1.0/rsq; + + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + forcelj *= factor_lj * r2inv; + + f[i][0] += delx*forcelj; + f[i][1] += dely*forcelj; + f[i][2] += delz*forcelj; + f[j][0] -= delx*forcelj; + f[j][1] -= dely*forcelj; + f[j][2] -= delz*forcelj; + + if (eflag) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]) - + offset[itype][jtype]; + eng_vdwl += factor_lj*philj; + } + } + + // adjust rsq for off-site O charge(s) + + if (itype == typeO || jtype == typeO) { + if (jtype == typeO) { + find_M(j,jH1,jH2,xjM); + x2 = xjM; + } else x2 = x[j]; + delx = x1[0] - x2[0]; + dely = x1[1] - x2[1]; + delz = x1[2] - x2[2]; + rsq = delx*delx + dely*dely + delz*delz; + } + + // test current rsq against cutoff and compute Coulombic force + + if (rsq < cut_coulsq) { + if (!ncoultablebits || rsq <= tabinnersq) { + r = sqrtf(rsq); + r2inv = 1 / 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 { + r2inv = 1 / rsq; + 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; + } + } + + cforce = forcecoul * r2inv; + + // if i,j are not O atoms, force is applied directly + // if i or j are O atoms, force is on fictitious atoms + // spread force to all 3 atoms in water molecule + // formulas due to Feenstra et al, J Comp Chem, 20, 786 (1999) + + if (itype != typeO) { + if (vflag == 0) { + f[i][0] += delx * cforce; + f[i][1] += dely * cforce; + f[i][2] += delz * cforce; + } else { + tf[i][0] += delx * cforce; + tf[i][1] += dely * cforce; + tf[i][2] += delz * cforce; + + tvirial[0] += 0.5 * delx * delx * cforce; + tvirial[1] += 0.5 * dely * dely * cforce; + tvirial[2] += 0.5 * delz * delz * cforce; + tvirial[3] += 0.5 * dely * delx * cforce; + tvirial[4] += 0.5 * delz * delx * cforce; + tvirial[5] += 0.5 * delz * dely * cforce; + } + + } else { + fO[0] = delx*cforce*(1.0-2.0*alpha); + fO[1] = dely*cforce*(1.0-2.0*alpha); + fO[2] = delz*cforce*(1.0-2.0*alpha); + + fH[0] = alpha * (delx*cforce); + fH[1] = alpha * (dely*cforce); + fH[2] = alpha * (delz*cforce); + + if (vflag == 0) { + f[i][0] += fO[0]; + f[i][1] += fO[1]; + f[i][2] += fO[2]; + + f[iH1][0] += fH[0]; + f[iH1][1] += fH[1]; + f[iH1][2] += fH[2]; + + f[iH2][0] += fH[0]; + f[iH2][1] += fH[1]; + f[iH2][2] += fH[2]; + + } else { + tf[i][0] += fO[0]; + tf[i][1] += fO[1]; + tf[i][2] += fO[2]; + + tf[iH1][0] += fH[0]; + tf[iH1][1] += fH[1]; + tf[iH1][2] += fH[2]; + + tf[iH2][0] += fH[0]; + tf[iH2][1] += fH[1]; + tf[iH2][2] += fH[2]; + + delx1 = x[i][0] - x2[0]; + dely1 = x[i][1] - x2[1]; + delz1 = x[i][2] - x2[2]; + domain->minimum_image(&delx1,&dely1,&delz1); + + delx2 = x[iH1][0] - x2[0]; + dely2 = x[iH1][1] - x2[1]; + delz2 = x[iH1][2] - x2[2]; + domain->minimum_image(&delx2,&dely2,&delz2); + + delx3 = x[iH2][0] - x2[0]; + dely3 = x[iH2][1] - x2[1]; + delz3 = x[iH2][2] - x2[2]; + domain->minimum_image(&delx3,&dely3,&delz3); + + tvirial[0] += 0.5 * (delx1 * fO[0] + (delx2 + delx3) * fH[0]); + tvirial[1] += 0.5 * (dely1 * fO[1] + (dely2 + dely3) * fH[1]); + tvirial[2] += 0.5 * (delz1 * fO[2] + (delz2 + delz3) * fH[2]); + tvirial[3] += 0.5 * (dely1 * fO[0] + (dely2 + dely3) * fH[0]); + tvirial[4] += 0.5 * (delz1 * fO[0] + (delz2 + delz3) * fH[0]); + tvirial[5] += 0.5 * (delz1 * fO[1] + (delz2 + delz3) * fH[1]); + } + } + + if (jtype != typeO) { + if (vflag == 0) { + f[j][0] -= delx * cforce; + f[j][1] -= dely * cforce; + f[j][2] -= delz * cforce; + } else { + tf[j][0] -= delx * cforce; + tf[j][1] -= dely * cforce; + tf[j][2] -= delz * cforce; + + tvirial[0] += 0.5 * (delx * delx * cforce); + tvirial[1] += 0.5 * (dely * dely * cforce); + tvirial[2] += 0.5 * (delz * delz * cforce); + tvirial[3] += 0.5 * (dely * delx * cforce); + tvirial[4] += 0.5 * (delz * delx * cforce); + tvirial[5] += 0.5 * (delz * dely * cforce); + } + + } else { + negforce = -cforce; + + fO[0] = delx*negforce*(1.0-2.0*alpha); + fO[1] = dely*negforce*(1.0-2.0*alpha); + fO[2] = delz*negforce*(1.0-2.0*alpha); + + fH[0] = alpha * (delx*negforce); + fH[1] = alpha * (dely*negforce); + fH[2] = alpha * (delz*negforce); + + if (vflag != 2) { + f[j][0] += fO[0]; + f[j][1] += fO[1]; + f[j][2] += fO[2]; + + f[jH1][0] += fH[0]; + f[jH1][1] += fH[1]; + f[jH1][2] += fH[2]; + + f[jH2][0] += fH[0]; + f[jH2][1] += fH[1]; + f[jH2][2] += fH[2]; + + } else { + tf[j][0] += fO[0]; + tf[j][1] += fO[1]; + tf[j][2] += fO[2]; + + tf[jH1][0] += fH[0]; + tf[jH1][1] += fH[1]; + tf[jH1][2] += fH[2]; + + tf[jH2][0] += fH[0]; + tf[jH2][1] += fH[1]; + tf[jH2][2] += fH[2]; + + delx1 = x[j][0] - x1[0]; + dely1 = x[j][1] - x1[1]; + delz1 = x[j][2] - x1[2]; + domain->minimum_image(&delx1,&dely1,&delz1); + + delx2 = x[jH1][0] - x1[0]; + dely2 = x[jH1][1] - x1[1]; + delz2 = x[jH1][2] - x1[2]; + domain->minimum_image(&delx2,&dely2,&delz2); + + delx3 = x[jH2][0] - x1[0]; + dely3 = x[jH2][1] - x1[1]; + delz3 = x[jH2][2] - x1[2]; + domain->minimum_image(&delx3,&dely3,&delz3); + + tvirial[0] += 0.5 * (delx1 * fO[0] + (delx2 + delx3) * fH[0]); + tvirial[1] += 0.5 * (dely1 * fO[1] + (dely2 + dely3) * fH[1]); + tvirial[2] += 0.5 * (delz1 * fO[2] + (delz2 + delz3) * fH[2]); + tvirial[3] += 0.5 * (dely1 * fO[0] + (dely2 + dely3) * fH[0]); + tvirial[4] += 0.5 * (delz1 * fO[0] + (delz2 + delz3) * fH[0]); + tvirial[5] += 0.5 * (delz1 * fO[1] + (delz2 + delz3) * fH[1]); + } + } + + 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; + eng_coul += phicoul; + } + } + } + } + } + if (vflag == 2) { + virial_compute(); + for (int i = 0; i < 6; i++) virial[i] += tvirial[i]; + } +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairLJCutCoulLongTIP4P::settings(int narg, char **arg) +{ + if (narg < 6 || narg > 7) error->all("Illegal pair_style command"); + + typeO = atoi(arg[0]); + typeH = atoi(arg[1]); + typeB = atoi(arg[2]); + typeA = atoi(arg[3]); + qdist = atof(arg[4]); + + cut_lj_global = atof(arg[5]); + if (narg == 6) cut_coul = cut_lj_global; + else cut_coul = atof(arg[6]); + + // 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_lj[i][j] = cut_lj_global; + } +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairLJCutCoulLongTIP4P::init_style() +{ + int i,j; + + if (atom->tag_enable == 0) + error->all("Pair style lj/cut/coul/long/tip4p requires atom IDs"); + if (!force->newton_pair) + error->all("Pair style lj/cut/coul/long/tip4p requires newton pair on"); + if (atom->charge_allow == 0) + error->all("Must use charged atom style with this pair style"); + + 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 (MIN(cut_lj[i][j],cut_coul) < cut_respa[3]) + error->all("Pair cutoff < Respa interior cutoff"); + } + } else cut_respa = NULL; + + // insure use of correct KSpace long-range solver, set g_ewald + + if (force->kspace == NULL) + error->all("Pair style is incompatible with KSpace style"); + if (strcmp(force->kspace_style,"pppm/tip4p") == 0) + g_ewald = force->kspace->g_ewald; + else error->all("Pair style is incompatible with KSpace style"); + + // setup force tables + + if (ncoultablebits) init_tables(); + + // set alpha parameter + + double theta = force->angle->equilibrium_angle(typeA); + double blen = force->bond->equilibrium_distance(typeB); + alpha = qdist / (2.0 * cos(0.5*theta) * blen); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJCutCoulLongTIP4P::write_restart_settings(FILE *fp) +{ + fwrite(&typeO,sizeof(int),1,fp); + fwrite(&typeH,sizeof(int),1,fp); + fwrite(&typeB,sizeof(int),1,fp); + fwrite(&typeA,sizeof(int),1,fp); + fwrite(&qdist,sizeof(double),1,fp); + + fwrite(&cut_lj_global,sizeof(double),1,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 PairLJCutCoulLongTIP4P::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&typeO,sizeof(int),1,fp); + fread(&typeH,sizeof(int),1,fp); + fread(&typeB,sizeof(int),1,fp); + fread(&typeA,sizeof(int),1,fp); + fread(&qdist,sizeof(double),1,fp); + + fread(&cut_lj_global,sizeof(double),1,fp); + fread(&cut_coul,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + + MPI_Bcast(&typeO,1,MPI_INT,0,world); + MPI_Bcast(&typeH,1,MPI_INT,0,world); + MPI_Bcast(&typeB,1,MPI_INT,0,world); + MPI_Bcast(&typeA,1,MPI_INT,0,world); + MPI_Bcast(&qdist,1,MPI_DOUBLE,0,world); + + MPI_Bcast(&cut_lj_global,1,MPI_DOUBLE,0,world); + 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); + +} + +/* ---------------------------------------------------------------------- + find 2 H atoms bonded to O atom i + compute position xM of fictitious charge site for O atom + also return local indices iH1,iH2 of H atoms +------------------------------------------------------------------------- */ + +void PairLJCutCoulLongTIP4P::find_M(int i, int &iH1, int &iH2, double *xM) +{ + // test that O is correctly bonded to 2 succesive H atoms + + iH1 = atom->map(atom->tag[i] + 1); + iH2 = atom->map(atom->tag[i] + 2); + + if (iH1 == -1 || iH2 == -1) error->one("TIP4P hydrogen is missing"); + if (atom->type[iH1] != typeH || atom->type[iH2] != typeH) + error->one("TIP4P hydrogen has incorrect atom type"); + + double **x = atom->x; + + double delx1 = x[iH1][0] - x[i][0]; + double dely1 = x[iH1][1] - x[i][1]; + double delz1 = x[iH1][2] - x[i][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + + double delx2 = x[iH2][0] - x[i][0]; + double dely2 = x[iH2][1] - x[i][1]; + double delz2 = x[iH2][2] - x[i][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + + xM[0] = x[i][0] + alpha * (delx1 + delx2); + xM[1] = x[i][1] + alpha * (dely1 + dely2); + xM[2] = x[i][2] + alpha * (delz1 + delz2); +} diff --git a/src/KSPACE/pair_lj_cut_coul_long_tip4p.h b/src/KSPACE/pair_lj_cut_coul_long_tip4p.h new file mode 100644 index 0000000000000000000000000000000000000000..e7873ebe98c161b8b0aca1ea487b70894ff16ef1 --- /dev/null +++ b/src/KSPACE/pair_lj_cut_coul_long_tip4p.h @@ -0,0 +1,41 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_LJ_CUT_COUL_LONG_TIP4P_H +#define PAIR_LJ_CUT_COUL_LONG_TIP4P_H + +#include "pair_lj_cut_coul_long.h" + +class PairLJCutCoulLongTIP4P : public PairLJCutCoulLong { + friend class PPPM; + + public: + PairLJCutCoulLongTIP4P(); + void compute(int, int); + void settings(int, char **); + void init_style(); + void write_restart_settings(FILE *fp); + void read_restart_settings(FILE *fp); + + private: + int typeH,typeO; // atom types of TIP4P water H and O atoms + int typeA,typeB; // angle and bond types of TIP4P water + double qdist; // distance from O site to negative charge + double alpha; // geometric constraint parameter for TIP4P + double **tf; + double tvirial[6]; + + void find_M(int, int &, int &, double *); +}; + +#endif diff --git a/src/KSPACE/pppm.cpp b/src/KSPACE/pppm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8f486a58a6283ba467522b9bbd34a7a24008efc7 --- /dev/null +++ b/src/KSPACE/pppm.cpp @@ -0,0 +1,1867 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Roy Pollock (LLNL), Paul Crozier (SNL) +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "string.h" +#include "stdio.h" +#include "math.h" +#include "pppm.h" +#include "atom.h" +#include "comm.h" +#include "neighbor.h" +#include "force.h" +#include "pair_buck_coul_long.h" +#include "pair_lj_cut_coul_long.h" +#include "pair_lj_charmm_coul_long.h" +#include "pair_lj_class2_coul_long.h" +#include "pair_lj_cut_coul_long_tip4p.h" +#include "pair_table.h" +#include "bond.h" +#include "angle.h" +#include "domain.h" +#include "fft3d_wrap.h" +#include "remap_wrap.h" +#include "memory.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +#define MAXORDER 7 +#define OFFSET 4096 +#define SMALL 0.00001 +#define LARGE 10000.0 +#define EPS_HOC 1.0e-7 + +/* ---------------------------------------------------------------------- */ + +PPPM::PPPM(int narg, char **arg) : KSpace(narg, arg) +{ + if (narg != 1) error->all("Illegal kspace_style pppm command"); + + precision = atof(arg[0]); + PI = 4.0*atan(1.0); + + nfactors = 3; + factors = new int[nfactors]; + factors[0] = 2; + factors[1] = 3; + factors[2] = 5; + + MPI_Comm_rank(world,&me); + MPI_Comm_size(world,&nprocs); + + density_brick = vdx_brick = vdy_brick = vdz_brick = NULL; + density_fft = NULL; + greensfn = NULL; + work1 = work2 = NULL; + vg = NULL; + fkx = fky = fkz = NULL; + buf1 = buf2 = NULL; + + gf_b = NULL; + rho1d = rho_coeff = NULL; + + fft1 = fft2 = NULL; + remap = NULL; + + nmax = 0; + part2grid = NULL; +} + +/* ---------------------------------------------------------------------- + free all memory +------------------------------------------------------------------------- */ + +PPPM::~PPPM() +{ + delete [] factors; + deallocate(); + memory->destroy_2d_int_array(part2grid); +} + +/* ---------------------------------------------------------------------- + called once before run +------------------------------------------------------------------------- */ + +void PPPM::init() +{ + if (me == 0) { + if (screen) fprintf(screen,"PPPM initialization ...\n"); + if (logfile) fprintf(logfile,"PPPM initialization ...\n"); + } + + // error check + + if (force->dimension == 2) error->all("Cannot use PPPM with 2d simulation"); + + if (slabflag == 0 && domain->nonperiodic > 0) + error->all("Cannot use nonperiodic boundaries with PPPM"); + if (slabflag == 1) { + if (domain->xperiodic != 1 || domain->yperiodic != 1 || + domain->boundary[2][0] != 1 || domain->boundary[2][1] != 1) + error->all("Incorrect boundaries with slab PPPM"); + } + + if (order > MAXORDER) { + char str[128]; + sprintf(str,"PPPM order cannot be greater than %d",MAXORDER); + error->all(str); + } + + // free all arrays previously allocated + + deallocate(); + + // insure use of valid pair_style with long-range Coulombics + // set cutoff to short-range Coulombic cutoff + + qqrd2e = force->qqrd2e; + qdist = 0.0; + + Pair *anypair; + if (force->pair == NULL) + error->all("Pair style is incompatible with KSpace style"); + else if (anypair = force->pair_match("buck/coul/long")) + cutoff = ((PairBuckCoulLong *) anypair)->cut_coul; + else if (anypair = force->pair_match("lj/cut/coul/long")) + cutoff = ((PairLJCutCoulLong *) anypair)->cut_coul; + else if (anypair = force->pair_match("lj/charmm/coul/long")) + cutoff = ((PairLJCharmmCoulLong *) anypair)->cut_coul; + else if (anypair = force->pair_match("lj/class2/coul/long")) + cutoff = ((PairLJClass2CoulLong *) anypair)->cut_coul; + else if (anypair = force->pair_match("lj/cut/coul/long/tip4p")) { + if (strcmp(force->kspace_style,"pppm/tip4p") != 0) + error->all("Pair style is incompatible with KSpace style"); + cutoff = ((PairLJCutCoulLongTIP4P *) anypair)->cut_coul; + qdist = ((PairLJCutCoulLongTIP4P *) anypair)->qdist; + typeO = ((PairLJCutCoulLongTIP4P *) anypair)->typeO; + typeH = ((PairLJCutCoulLongTIP4P *) anypair)->typeH; + int typeA = ((PairLJCutCoulLongTIP4P *) anypair)->typeA; + int typeB = ((PairLJCutCoulLongTIP4P *) anypair)->typeB; + if (force->angle == NULL || force->bond == NULL) + error->all("Bond and angle potentials must be defined for TIP4P"); + double theta = force->angle->equilibrium_angle(typeA); + double blen = force->bond->equilibrium_distance(typeB); + alpha = qdist / (2.0 * cos(0.5*theta) * blen); + } + else if (anypair = force->pair_match("table")) + cutoff = ((PairTable *) anypair)->cut_coul(); + else error->all("Pair style is incompatible with KSpace style"); + + // compute qsum & qsqsum and warn if not charge-neutral + + qsum = qsqsum = 0.0; + for (int i = 0; i < atom->nlocal; i++) { + qsum += atom->q[i]; + qsqsum += atom->q[i]*atom->q[i]; + } + + double tmp; + MPI_Allreduce(&qsum,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + qsum = tmp; + MPI_Allreduce(&qsqsum,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + qsqsum = tmp; + + if (fabs(qsum) > SMALL && me == 0) { + char str[128]; + sprintf(str,"System is not charge neutral, net charge = %g",qsum); + error->warning(str); + } + + // setup FFT grid resolution and g_ewald + + set_grid(); + + if (nx_pppm >= OFFSET || ny_pppm >= OFFSET || nz_pppm >= OFFSET) + error->all("PPPM grid is too large"); + + // global indices of PPPM grid range from 0 to N-1 + // nlo_in,nhi_in = lower/upper limits of the 3d sub-brick of + // global PPPM grid that I own without ghost cells + // for slab PPPM, assign z grid as if it were not extended + + nxlo_in = comm->myloc[0]*nx_pppm / comm->procgrid[0]; + nxhi_in = (comm->myloc[0]+1)*nx_pppm / comm->procgrid[0] - 1; + nylo_in = comm->myloc[1]*ny_pppm / comm->procgrid[1]; + nyhi_in = (comm->myloc[1]+1)*ny_pppm / comm->procgrid[1] - 1; + nzlo_in = comm->myloc[2] * + (static_cast<int> (nz_pppm/slab_volfactor)) / comm->procgrid[2]; + nzhi_in = (comm->myloc[2]+1) * + (static_cast<int> (nz_pppm/slab_volfactor)) / comm->procgrid[2] - 1; + + // nlower,nupper = stencil size for mapping particles to PPPM grid + + nlower = -(order-1)/2; + nupper = order/2; + + // shift values for particle <-> grid mapping + // add/subtract OFFSET to avoid int(-0.75) = 0 when want it to be -1 + + if (order % 2) shift = OFFSET + 0.5; + else shift = OFFSET; + if (order % 2) shiftone = 0.0; + else shiftone = 0.5; + + // nlo_out,nhi_out = lower/upper limits of the 3d sub-brick of + // global PPPM grid that my particles can contribute charge to + // effectively nlo_in,nhi_in + ghost cells + // nlo,nhi = global coords of grid pt to "lower left" of smallest/largest + // position a particle in my box can be at + // particle position bound = subbox + skin/2.0 + qdist + // qdist = offset due to TIP4P fictitious charge + // nlo_out,nhi_out = nlo,nhi + stencil size for particle mapping + // for slab PPPM, assign z grid as if it were not extended + + int nlo,nhi; + double cuthalf = 0.5 * neighbor->skin + qdist; + + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + double zprd_slab = zprd*slab_volfactor; + + nlo = static_cast<int> ((domain->subxlo-cuthalf-domain->boxxlo) * + nx_pppm/xprd + shift) - OFFSET; + nhi = static_cast<int> ((domain->subxhi+cuthalf-domain->boxxlo) * + nx_pppm/xprd + shift) - OFFSET; + nxlo_out = nlo + nlower; + nxhi_out = nhi + nupper; + + nlo = static_cast<int> ((domain->subylo-cuthalf-domain->boxylo) * + ny_pppm/yprd + shift) - OFFSET; + nhi = static_cast<int> ((domain->subyhi+cuthalf-domain->boxylo) * + ny_pppm/yprd + shift) - OFFSET; + nylo_out = nlo + nlower; + nyhi_out = nhi + nupper; + + nlo = static_cast<int> ((domain->subzlo-cuthalf-domain->boxzlo) * + nz_pppm/zprd_slab + shift) - OFFSET; + nhi = static_cast<int> ((domain->subzhi+cuthalf-domain->boxzlo) * + nz_pppm/zprd_slab + shift) - OFFSET; + nzlo_out = nlo + nlower; + nzhi_out = nhi + nupper; + + // for slab PPPM, change the grid boundary for processors at +z end + // to include the empty volume between periodically repeating slabs + // for slab PPPM, want charge data communicated from -z proc to +z proc, + // but not vice versa, also want field data communicated from +z proc to + // -z proc, but not vice versa + // this is accomplished by nzhi_in = nzhi_out on +z end (no ghost cells) + + if (slabflag && ((comm->myloc[2]+1) == (comm->procgrid[2]))) { + nzhi_in = nz_pppm - 1; + nzhi_out = nz_pppm - 1; + } + + // nlo_ghost,nhi_ghost = # of planes I will recv from 6 directions + // that overlay domain I own + // proc in that direction tells me via sendrecv() + // if no neighbor proc, value comes from self since I have ghosts regardless + + int nplanes; + MPI_Status status; + + nplanes = nxlo_in - nxlo_out; + if (comm->procneigh[0][0] != me) + MPI_Sendrecv(&nplanes,1,MPI_INT,comm->procneigh[0][0],0, + &nxhi_ghost,1,MPI_INT,comm->procneigh[0][1],0,world,&status); + else nxhi_ghost = nplanes; + + nplanes = nxhi_out - nxhi_in; + if (comm->procneigh[0][1] != me) + MPI_Sendrecv(&nplanes,1,MPI_INT,comm->procneigh[0][1],0, + &nxlo_ghost,1,MPI_INT,comm->procneigh[0][0],0,world,&status); + else nxlo_ghost = nplanes; + + nplanes = nylo_in - nylo_out; + if (comm->procneigh[1][0] != me) + MPI_Sendrecv(&nplanes,1,MPI_INT,comm->procneigh[1][0],0, + &nyhi_ghost,1,MPI_INT,comm->procneigh[1][1],0,world,&status); + else nyhi_ghost = nplanes; + + nplanes = nyhi_out - nyhi_in; + if (comm->procneigh[1][1] != me) + MPI_Sendrecv(&nplanes,1,MPI_INT,comm->procneigh[1][1],0, + &nylo_ghost,1,MPI_INT,comm->procneigh[1][0],0,world,&status); + else nylo_ghost = nplanes; + + nplanes = nzlo_in - nzlo_out; + if (comm->procneigh[2][0] != me) + MPI_Sendrecv(&nplanes,1,MPI_INT,comm->procneigh[2][0],0, + &nzhi_ghost,1,MPI_INT,comm->procneigh[2][1],0,world,&status); + else nzhi_ghost = nplanes; + + nplanes = nzhi_out - nzhi_in; + if (comm->procneigh[2][1] != me) + MPI_Sendrecv(&nplanes,1,MPI_INT,comm->procneigh[2][1],0, + &nzlo_ghost,1,MPI_INT,comm->procneigh[2][0],0,world,&status); + else nzlo_ghost = nplanes; + + // test that ghost overlap is not bigger than my sub-domain + + int flag = 0; + if (nxlo_ghost > nxhi_in-nxlo_in+1) flag = 1; + if (nxhi_ghost > nxhi_in-nxlo_in+1) flag = 1; + if (nylo_ghost > nyhi_in-nylo_in+1) flag = 1; + if (nyhi_ghost > nyhi_in-nylo_in+1) flag = 1; + if (nzlo_ghost > nzhi_in-nzlo_in+1) flag = 1; + if (nzhi_ghost > nzhi_in-nzlo_in+1) flag = 1; + + int flag_all; + MPI_Allreduce(&flag,&flag_all,1,MPI_INT,MPI_SUM,world); + + if (flag_all) + error->all("PPPM stencil extends too far, reduce PPPM order"); + + // decomposition of FFT mesh + // global indices range from 0 to N-1 + // proc owns entire x-dimension, clump of columns in y,z dimensions + // npey_fft,npez_fft = # of procs in y,z dims + // if nprocs is small enough, proc can own 1 or more entire xy planes, + // else proc owns 2d sub-blocks of yz plane + // me_y,me_z = which proc (0-npe_fft-1) I am in y,z dimensions + // nlo_fft,nhi_fft = lower/upper limit of the section + // of the global FFT mesh that I own + + int npey_fft,npez_fft; + if (nz_pppm >= nprocs) { + npey_fft = 1; + npez_fft = nprocs; + } else procs2grid2d(nprocs,ny_pppm,nz_pppm,&npey_fft,&npez_fft); + + int me_y = me % npey_fft; + int me_z = me / npey_fft; + + nxlo_fft = 0; + nxhi_fft = nx_pppm - 1; + nylo_fft = me_y*ny_pppm/npey_fft; + nyhi_fft = (me_y+1)*ny_pppm/npey_fft - 1; + nzlo_fft = me_z*nz_pppm/npez_fft; + nzhi_fft = (me_z+1)*nz_pppm/npez_fft - 1; + + // PPPM grid for this proc, including ghosts + + ngrid = (nxhi_out-nxlo_out+1) * (nyhi_out-nylo_out+1) * + (nzhi_out-nzlo_out+1); + + // FFT arrays on this proc, without ghosts + // nfft = FFT points in FFT decomposition on this proc + // nfft_brick = FFT points in 3d brick-decomposition on this proc + // nfft_both = greater of 2 values + + nfft = (nxhi_fft-nxlo_fft+1) * (nyhi_fft-nylo_fft+1) * + (nzhi_fft-nzlo_fft+1); + int nfft_brick = (nxhi_in-nxlo_in+1) * (nyhi_in-nylo_in+1) * + (nzhi_in-nzlo_in+1); + nfft_both = MAX(nfft,nfft_brick); + + // buffer space for use in brick2fft and fillbrick + // idel = max # of ghost planes to send or recv in +/- dir of each dim + // nx,ny,nz = owned planes (including ghosts) in each dim + // nxx,nyy,nzz = max # of grid cells to send in each dim + // nbuf = max in any dim, augment by 3x for components of vd_xyz in fillbrick + + int idelx,idely,idelz,nx,ny,nz,nxx,nyy,nzz; + + idelx = MAX(nxlo_ghost,nxhi_ghost); + idelx = MAX(idelx,nxhi_out-nxhi_in); + idelx = MAX(idelx,nxlo_in-nxlo_out); + + idely = MAX(nylo_ghost,nyhi_ghost); + idely = MAX(idely,nyhi_out-nyhi_in); + idely = MAX(idely,nylo_in-nylo_out); + + idelz = MAX(nzlo_ghost,nzhi_ghost); + idelz = MAX(idelz,nzhi_out-nzhi_in); + idelz = MAX(idelz,nzlo_in-nzlo_out); + + nx = nxhi_out - nxlo_out + 1; + ny = nyhi_out - nylo_out + 1; + nz = nzhi_out - nzlo_out + 1; + + nxx = idelx * ny * nz; + nyy = idely * nx * nz; + nzz = idelz * nx * ny; + + nbuf = MAX(nxx,nyy); + nbuf = MAX(nbuf,nzz); + nbuf *= 3; + + // print stats + + int ngrid_max,nfft_both_max,nbuf_max; + MPI_Allreduce(&ngrid,&ngrid_max,1,MPI_INT,MPI_MAX,world); + MPI_Allreduce(&nfft_both,&nfft_both_max,1,MPI_INT,MPI_MAX,world); + MPI_Allreduce(&nbuf,&nbuf_max,1,MPI_INT,MPI_MAX,world); + + if (me == 0) { + if (screen) fprintf(screen," brick FFT buffer size/proc = %d %d %d\n", + ngrid_max,nfft_both_max,nbuf_max); + if (logfile) fprintf(logfile," brick FFT buffer size/proc = %d %d %d\n", + ngrid_max,nfft_both_max,nbuf_max); + } + + // allocate K-space dependent memory + + allocate(); + + // pre-compute Green's function denomiator expansion + // pre-compute 1d charge distribution coefficients + + compute_gf_denom(); + compute_rho_coeff(); +} + +/* ---------------------------------------------------------------------- + adjust PPPM coeffs, called initially and whenever volume has changed +------------------------------------------------------------------------- */ + +void PPPM::setup() +{ + int i,j,k,l,m,n; + + // volume-dependent factors + + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + + // adjustment of z dimension for 2d slab PPPM + // 3d PPPM just uses zprd since slab_volfactor = 1.0 + + double zprd_slab = zprd*slab_volfactor; + + volume = xprd * yprd * zprd_slab; + + delxinv = nx_pppm/xprd; + delyinv = ny_pppm/yprd; + delzinv = nz_pppm/zprd_slab; + + delvolinv = delxinv*delyinv*delzinv; + + double unitkx = (2.0*PI/xprd); + double unitky = (2.0*PI/yprd); + double unitkz = (2.0*PI/zprd_slab); + + // fkx,fky,fkz for my FFT grid pts + + double per; + + for (i = nxlo_fft; i <= nxhi_fft; i++) { + per = i - nx_pppm*(2*i/nx_pppm); + fkx[i] = unitkx*per; + } + + for (i = nylo_fft; i <= nyhi_fft; i++) { + per = i - ny_pppm*(2*i/ny_pppm); + fky[i] = unitky*per; + } + + for (i = nzlo_fft; i <= nzhi_fft; i++) { + per = i - nz_pppm*(2*i/nz_pppm); + fkz[i] = unitkz*per; + } + + // virial coefficients + + double sqk,vterm; + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) { + for (j = nylo_fft; j <= nyhi_fft; j++) { + for (i = nxlo_fft; i <= nxhi_fft; i++) { + sqk = fkx[i]*fkx[i] + fky[j]*fky[j] + fkz[k]*fkz[k]; + if (sqk == 0.0) { + vg[n][0] = 0.0; + vg[n][1] = 0.0; + vg[n][2] = 0.0; + vg[n][3] = 0.0; + vg[n][4] = 0.0; + vg[n][5] = 0.0; + } else { + vterm = -2.0 * (1.0/sqk + 0.25/(g_ewald*g_ewald)); + vg[n][0] = 1.0 + vterm*fkx[i]*fkx[i]; + vg[n][1] = 1.0 + vterm*fky[j]*fky[j]; + vg[n][2] = 1.0 + vterm*fkz[k]*fkz[k]; + vg[n][3] = vterm*fkx[i]*fky[j]; + vg[n][4] = vterm*fkx[i]*fkz[k]; + vg[n][5] = vterm*fky[j]*fkz[k]; + } + n++; + } + } + } + + // modified (Hockney-Eastwood) Coulomb Green's function + + int nx,ny,nz,kper,lper,mper; + double snx,sny,snz,snx2,sny2,snz2; + double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; + double sum1,dot1,dot2; + double numerator,denominator; + + int nbx = static_cast<int> ((g_ewald*xprd/(PI*nx_pppm)) * + pow(-log(EPS_HOC),0.25)); + int nby = static_cast<int> ((g_ewald*yprd/(PI*ny_pppm)) * + pow(-log(EPS_HOC),0.25)); + int nbz = static_cast<int> ((g_ewald*zprd_slab/(PI*nz_pppm)) * + pow(-log(EPS_HOC),0.25)); + + double form = 1.0; + + n = 0; + for (m = nzlo_fft; m <= nzhi_fft; m++) { + mper = m - nz_pppm*(2*m/nz_pppm); + snz = sin(0.5*unitkz*mper*zprd_slab/nz_pppm); + snz2 = snz*snz; + + for (l = nylo_fft; l <= nyhi_fft; l++) { + lper = l - ny_pppm*(2*l/ny_pppm); + sny = sin(0.5*unitky*lper*yprd/ny_pppm); + sny2 = sny*sny; + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + kper = k - nx_pppm*(2*k/nx_pppm); + snx = sin(0.5*unitkx*kper*xprd/nx_pppm); + snx2 = snx*snx; + + sqk = pow(unitkx*kper,2.0) + pow(unitky*lper,2.0) + + pow(unitkz*mper,2.0); + + if (sqk != 0.0) { + numerator = form*12.5663706/sqk; + denominator = gf_denom(snx2,sny2,snz2); + sum1 = 0.0; + for (nx = -nbx; nx <= nbx; nx++) { + qx = unitkx*(kper+nx_pppm*nx); + sx = exp(-.25*pow(qx/g_ewald,2.0)); + wx = 1.0; + argx = 0.5*qx*xprd/nx_pppm; + if (argx != 0.0) wx = pow(sin(argx)/argx,order); + for (ny = -nby; ny <= nby; ny++) { + qy = unitky*(lper+ny_pppm*ny); + sy = exp(-.25*pow(qy/g_ewald,2.0)); + wy = 1.0; + argy = 0.5*qy*yprd/ny_pppm; + if (argy != 0.0) wy = pow(sin(argy)/argy,order); + for (nz = -nbz; nz <= nbz; nz++) { + qz = unitkz*(mper+nz_pppm*nz); + sz = exp(-.25*pow(qz/g_ewald,2.0)); + wz = 1.0; + argz = 0.5*qz*zprd_slab/nz_pppm; + if (argz != 0.0) wz = pow(sin(argz)/argz,order); + + dot1 = unitkx*kper*qx + unitky*lper*qy + unitkz*mper*qz; + dot2 = qx*qx+qy*qy+qz*qz; + sum1 += (dot1/dot2) * sx*sy*sz * pow(wx*wy*wz,2.0); + } + } + } + greensfn[n++] = numerator*sum1/denominator; + } else greensfn[n++] = 0.0; + } + } + } +} + +/* ---------------------------------------------------------------------- + compute the PPPM long-range force, energy, virial +------------------------------------------------------------------------- */ + +void PPPM::compute(int eflag, int vflag) +{ + int i; + + // extend size of nlocal-dependent arrays if necessary + + if (atom->nlocal > nmax) { + memory->destroy_2d_int_array(part2grid); + nmax = atom->nmax; + part2grid = memory->create_2d_int_array(nmax,3,"pppm:part2grid"); + } + + energy = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + // find grid points for all my particles + // map my particle charge onto my local 3d density grid + + particle_map(); + make_rho(); + + // all procs communicate density values from their ghost cells + // to fully sum contribution in their 3d bricks + // remap from 3d decomposition to FFT decomposition + + brick2fft(); + + // compute potential gradient on my FFT grid and + // portion of e_long on this proc's FFT grid + // return gradients (electric fields) in 3d brick decomposition + + poisson(eflag,vflag); + + // all procs communicate E-field values to fill ghost cells + // surrounding their 3d bricks + + fillbrick(); + + // calculate the force on my particles + + fieldforce(); + + // sum energy across procs and add in volume-dependent term + + if (eflag) { + double energy_all; + MPI_Allreduce(&energy,&energy_all,1,MPI_DOUBLE,MPI_SUM,world); + energy = energy_all; + + energy *= 0.5*volume; + energy -= g_ewald*qsqsum/1.772453851 + + 0.5*PI*qsum*qsum / (g_ewald*g_ewald*volume); + energy *= qqrd2e; + } + + // sum virial across procs + + if (vflag) { + double virial_all[6]; + MPI_Allreduce(virial,virial_all,6,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < 6; i++) virial[i] = 0.5*qqrd2e*volume*virial_all[i]; + } + + // 2d slab correction + + if (slabflag) slabcorr(eflag); +} + +/* ---------------------------------------------------------------------- + allocate memory that depends on # of K-vectors and order +------------------------------------------------------------------------- */ + +void PPPM::allocate() +{ + density_brick = + memory->create_3d_double_array(nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:density_brick"); + vdx_brick = + memory->create_3d_double_array(nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:vdx_brick"); + vdy_brick = + memory->create_3d_double_array(nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:vdy_brick"); + vdz_brick = + memory->create_3d_double_array(nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:vdz_brick"); + + density_fft = new double[nfft_both]; + greensfn = new double[nfft_both]; + work1 = new double[2*nfft_both]; + work2 = new double[2*nfft_both]; + vg = memory->create_2d_double_array(nfft_both,6,"pppm:vg"); + + fkx = memory->create_1d_double_array(nxlo_fft,nxhi_fft,"pppm:fkx"); + fky = memory->create_1d_double_array(nylo_fft,nyhi_fft,"pppm:fky"); + fkz = memory->create_1d_double_array(nzlo_fft,nzhi_fft,"pppm:fkz"); + + buf1 = new double[nbuf]; + buf2 = new double[nbuf]; + + // summation coeffs + + gf_b = new double[order]; + rho1d = memory->create_2d_double_array(3,-order/2,order/2,"pppm:rho1d"); + rho_coeff = memory->create_2d_double_array(order,(1-order)/2,order/2, + "pppm:rho_coeff"); + + // create 2 FFTs and a Remap + // 1st FFT keeps data in FFT decompostion + // 2nd FFT returns data in 3d brick decomposition + // remap takes data from 3d brick to FFT decomposition + + int tmp; + + fft1 = new FFT3d(world,nx_pppm,ny_pppm,nz_pppm, + nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, + nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, + 0,0,&tmp); + + fft2 = new FFT3d(world,nx_pppm,ny_pppm,nz_pppm, + nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, + nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, + 0,0,&tmp); + + remap = new Remap(world, + nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, + nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, + 1,0,0,2); +} + +/* ---------------------------------------------------------------------- + deallocate memory that depends on # of K-vectors and order +------------------------------------------------------------------------- */ + +void PPPM::deallocate() +{ + memory->destroy_3d_double_array(density_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy_3d_double_array(vdx_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy_3d_double_array(vdy_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy_3d_double_array(vdz_brick,nzlo_out,nylo_out,nxlo_out); + + delete [] density_fft; + delete [] greensfn; + delete [] work1; + delete [] work2; + memory->destroy_2d_double_array(vg); + + memory->destroy_1d_double_array(fkx,nxlo_fft); + memory->destroy_1d_double_array(fky,nylo_fft); + memory->destroy_1d_double_array(fkz,nzlo_fft); + + delete [] buf1; + delete [] buf2; + + delete [] gf_b; + memory->destroy_2d_double_array(rho1d,-order/2); + memory->destroy_2d_double_array(rho_coeff,(1-order)/2); + + delete fft1; + delete fft2; + delete remap; +} + +/* ---------------------------------------------------------------------- + set size of FFT grid (nx,ny,nz_pppm) and g_ewald +------------------------------------------------------------------------- */ + +void PPPM::set_grid() +{ + // see JCP 109, pg. 7698 for derivation of coefficients + // higher order coefficients may be computed if needed + + double **acons = memory->create_2d_double_array(8,7,"pppm:acons"); + + acons[1][0] = 2.0 / 3.0; + acons[2][0] = 1.0 / 50.0; + acons[2][1] = 5.0 / 294.0; + acons[3][0] = 1.0 / 588.0; + acons[3][1] = 7.0 / 1440.0; + acons[3][2] = 21.0 / 3872.0; + acons[4][0] = 1.0 / 4320.0; + acons[4][1] = 3.0 / 1936.0; + acons[4][2] = 7601.0 / 2271360.0; + acons[4][3] = 143.0 / 28800.0; + acons[5][0] = 1.0 / 23232.0; + acons[5][1] = 7601.0 / 13628160.0; + acons[5][2] = 143.0 / 69120.0; + acons[5][3] = 517231.0 / 106536960.0; + acons[5][4] = 106640677.0 / 11737571328.0; + acons[6][0] = 691.0 / 68140800.0; + acons[6][1] = 13.0 / 57600.0; + acons[6][2] = 47021.0 / 35512320.0; + acons[6][3] = 9694607.0 / 2095994880.0; + acons[6][4] = 733191589.0 / 59609088000.0; + acons[6][5] = 326190917.0 / 11700633600.0; + acons[7][0] = 1.0 / 345600.0; + acons[7][1] = 3617.0 / 35512320.0; + acons[7][2] = 745739.0 / 838397952.0; + acons[7][3] = 56399353.0 / 12773376000.0; + acons[7][4] = 25091609.0 / 1560084480.0; + acons[7][5] = 1755948832039.0 / 36229939200000.0; + acons[7][6] = 4887769399.0 / 37838389248.0; + + double q2 = qsqsum / force->dielectric; + double natoms = atom->natoms; + + // adjustment of z dimension for 2d slab PPPM + // 3d PPPM just uses zprd since slab_volfactor = 1.0 + + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + double zprd_slab = zprd*slab_volfactor; + + // make initial g_ewald estimate + // based on desired error and real space cutoff + // fluid-occupied volume used to estimate real-space error + // zprd used rather than zprd_slab + + if (!gewaldflag) + g_ewald = sqrt(-log(precision*sqrt(natoms*cutoff*xprd*yprd*zprd) / + (2.0*q2))) / cutoff; + + // set optimal nx_pppm,ny_pppm,nz_pppm based on order and precision + // nz_pppm uses extended zprd_slab instead of zprd + + double h,h1,h2,err,er1,er2,lpr; + int ncount; + + if (!gridflag) { + h = 1.0; + h1 = 2.0; + + ncount = 0; + err = LARGE; + er1 = 0.0; + while (fabs(err) > SMALL) { + lpr = rms(h,xprd,natoms,q2,acons); + err = log(lpr) - log(precision); + er2 = er1; + er1 = err; + h2 = h1; + h1 = h; + if ((er1 - er2) == 0.0) h = h1 + er1; + else h = h1 + er1*(h2 - h1)/(er1 - er2); + ncount++; + if (ncount > LARGE) error->all("Cannot compute PPPM X grid spacing"); + } + nx_pppm = static_cast<int> (xprd/h + 1); + + ncount = 0; + err = LARGE; + er1 = 0.0; + while (fabs(err) > SMALL) { + lpr = rms(h,yprd,natoms,q2,acons); + err = log(lpr) - log(precision); + er2 = er1; + er1 = err; + h2 = h1; + h1 = h; + if ((er1 - er2) == 0.0) h = h1 + er1; + else h = h1 + er1*(h2 - h1)/(er1 - er2); + ncount++; + if (ncount > LARGE) error->all("Cannot compute PPPM Y grid spacing"); + } + ny_pppm = static_cast<int> (yprd/h + 1); + + ncount = 0; + err = LARGE; + er1 = 0.0; + while (fabs(err) > SMALL) { + lpr = rms(h,zprd_slab,natoms,q2,acons); + err = log(lpr) - log(precision); + er2 = er1; + er1 = err; + h2 = h1; + h1 = h; + if ((er1 - er2) == 0.0) h = h1 + er1; + else h = h1 + er1*(h2 - h1)/(er1 - er2); + ncount++; + if (ncount > LARGE) error->all("Cannot compute PPPM Z grid spacing"); + } + nz_pppm = static_cast<int> (zprd_slab/h + 1); + + } + + // convert grid into sizes that are factorable + + while (!factorable(nx_pppm)) nx_pppm++; + while (!factorable(ny_pppm)) ny_pppm++; + while (!factorable(nz_pppm)) nz_pppm++; + + // adjust g_ewald for new grid + + double dx = xprd/nx_pppm; + double dy = yprd/ny_pppm; + double dz = zprd_slab/nz_pppm; + + if (!gewaldflag) { + double gew1,gew2,lprx,lpry,lprz,spr; + + gew1 = g_ewald + 0.01; + ncount = 0; + err = LARGE; + er1 = 0.0; + + while (fabs(err) > SMALL) { + lprx = rms(dx,xprd,natoms,q2,acons); + lpry = rms(dy,yprd,natoms,q2,acons); + lprz = rms(dz,zprd_slab,natoms,q2,acons); + lpr = sqrt(lprx*lprx + lpry*lpry + lprz*lprz) / sqrt(3.0); + spr = 2.0*q2 * exp(-g_ewald*g_ewald*cutoff*cutoff) / + sqrt(natoms*cutoff*xprd*yprd*zprd_slab); + + err = log(lpr) - log(spr); + er2 = er1; + er1 = err; + gew2 = gew1; + gew1 = g_ewald; + if ((er1 - er2) == 0.0) g_ewald = gew1 + er1; + else g_ewald = gew1 + er1*(gew2 - gew1)/(er1 - er2); + ncount++; + if (ncount > LARGE) error->all("Cannot compute PPPM G"); + } + } + + // compute final RMS precision + + double lprx = rms(dx,xprd,natoms,q2,acons); + double lpry = rms(dy,yprd,natoms,q2,acons); + double lprz = rms(dz,zprd_slab,natoms,q2,acons); + lpr = sqrt(lprx*lprx + lpry*lpry + lprz*lprz) / sqrt(3.0); + double spr = 2.0*q2 * exp(-g_ewald*g_ewald*cutoff*cutoff) / + sqrt(natoms*cutoff*xprd*yprd*zprd_slab); + + // free local memory + + memory->destroy_2d_double_array(acons); + + // print info + + if (me == 0) { + if (screen) { + fprintf(screen," G vector = %g\n",g_ewald); + fprintf(screen," grid = %d %d %d\n",nx_pppm,ny_pppm,nz_pppm); + fprintf(screen," RMS precision = %g\n",MAX(lpr,spr)); + } + if (logfile) { + fprintf(logfile," G vector = %g\n",g_ewald); + fprintf(logfile," grid = %d %d %d\n",nx_pppm,ny_pppm,nz_pppm); + fprintf(logfile," RMS precision = %g\n",MAX(lpr,spr)); + } + } +} + +/* ---------------------------------------------------------------------- + check if all factors of n are in list of factors + return 1 if yes, 0 if no +------------------------------------------------------------------------- */ + +int PPPM::factorable(int n) +{ + int i; + + while (n > 1) { + for (i = 0; i < nfactors; i++) { + if (n % factors[i] == 0) { + n /= factors[i]; + break; + } + } + if (i == nfactors) return 0; + } + + return 1; +} + +/* ---------------------------------------------------------------------- + compute RMS precision for a dimension +------------------------------------------------------------------------- */ + +double PPPM::rms(double h, double prd, double natoms, + double q2, double **acons) +{ + double sum = 0.0; + for (int m = 0; m < order; m++) + sum += acons[order][m] * pow(h*g_ewald,2.0*m); + double value = q2 * pow(h*g_ewald,order) * + sqrt(g_ewald*prd*sqrt(2.0*PI)*sum/natoms) / (prd*prd); + return value; +} + +/* ---------------------------------------------------------------------- + denominator for Hockney-Eastwood Green's function + of x,y,z = sin(kx*deltax/2), etc + + inf n-1 + S(n,k) = Sum W(k+pi*j)**2 = Sum b(l)*(z*z)**l + j=-inf l=0 + + = -(z*z)**n /(2n-1)! * (d/dx)**(2n-1) cot(x) at z = sin(x) + gf_b = denominator expansion coeffs +------------------------------------------------------------------------- */ + +double PPPM::gf_denom(double x, double y, double z) +{ + double sx,sy,sz; + sz = sy = sx = 0.0; + for (int l = order-1; l >= 0; l--) { + sx = gf_b[l] + sx*x; + sy = gf_b[l] + sy*y; + sz = gf_b[l] + sz*z; + } + double s = sx*sy*sz; + return s*s; +} + +/* ---------------------------------------------------------------------- + pre-compute Green's function denominator expansion coeffs, Gamma(2n) +------------------------------------------------------------------------- */ + +void PPPM::compute_gf_denom() +{ + int k,l,m; + + for (l = 1; l < order; l++) gf_b[l] = 0.0; + gf_b[0] = 1.0; + + for (m = 1; m < order; m++) { + for (l = m; l > 0; l--) + gf_b[l] = 4.0 * (gf_b[l]*(l-m)*(l-m-0.5)-gf_b[l-1]*(l-m-1)*(l-m-1)); + gf_b[0] = 4.0 * (gf_b[0]*(l-m)*(l-m-0.5)); + } + + int ifact = 1; + for (k = 1; k < 2*order; k++) ifact *= k; + double gaminv = 1.0/ifact; + for (l = 0; l < order; l++) gf_b[l] *= gaminv; +} + +/* ---------------------------------------------------------------------- + ghost-swap to accumulate full density in brick decomposition + remap density from 3d brick decomposition to FFT decomposition +------------------------------------------------------------------------- */ + +void PPPM::brick2fft() +{ + int i,n,ix,iy,iz; + MPI_Request request; + MPI_Status status; + + // pack my ghosts for +x processor + // pass data to self or +x processor + // unpack and sum recv data into my real cells + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy <= nyhi_out; iy++) + for (ix = nxhi_in+1; ix <= nxhi_out; ix++) + buf1[n++] = density_brick[iz][iy][ix]; + + if (comm->procneigh[0][1] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[0][0],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[0][1],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy <= nyhi_out; iy++) + for (ix = nxlo_in; ix < nxlo_in+nxlo_ghost; ix++) + density_brick[iz][iy][ix] += buf2[n++]; + + // pack my ghosts for -x processor + // pass data to self or -x processor + // unpack and sum recv data into my real cells + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy <= nyhi_out; iy++) + for (ix = nxlo_out; ix < nxlo_in; ix++) + buf1[n++] = density_brick[iz][iy][ix]; + + if (comm->procneigh[0][0] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[0][1],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[0][0],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy <= nyhi_out; iy++) + for (ix = nxhi_in-nxhi_ghost+1; ix <= nxhi_in; ix++) + density_brick[iz][iy][ix] += buf2[n++]; + + // pack my ghosts for +y processor + // pass data to self or +y processor + // unpack and sum recv data into my real cells + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nyhi_in+1; iy <= nyhi_out; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + buf1[n++] = density_brick[iz][iy][ix]; + + if (comm->procneigh[1][1] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[1][0],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[1][1],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_in; iy < nylo_in+nylo_ghost; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + density_brick[iz][iy][ix] += buf2[n++]; + + // pack my ghosts for -y processor + // pass data to self or -y processor + // unpack and sum recv data into my real cells + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy < nylo_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + buf1[n++] = density_brick[iz][iy][ix]; + + if (comm->procneigh[1][0] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[1][1],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[1][0],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nyhi_in-nyhi_ghost+1; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + density_brick[iz][iy][ix] += buf2[n++]; + + // pack my ghosts for +z processor + // pass data to self or +z processor + // unpack and sum recv data into my real cells + + n = 0; + for (iz = nzhi_in+1; iz <= nzhi_out; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + buf1[n++] = density_brick[iz][iy][ix]; + + if (comm->procneigh[2][1] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[2][0],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[2][1],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_in; iz < nzlo_in+nzlo_ghost; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + density_brick[iz][iy][ix] += buf2[n++]; + + // pack my ghosts for -z processor + // pass data to self or -z processor + // unpack and sum recv data into my real cells + + n = 0; + for (iz = nzlo_out; iz < nzlo_in; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + buf1[n++] = density_brick[iz][iy][ix]; + + if (comm->procneigh[2][0] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[2][1],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[2][0],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzhi_in-nzhi_ghost+1; iz <= nzhi_in; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + density_brick[iz][iy][ix] += buf2[n++]; + + // remap from 3d brick decomposition to FFT decomposition + // copy grabs inner portion of density from 3d brick + // remap could be done as pre-stage of FFT, + // but this works optimally on only double values, not complex values + + n = 0; + for (iz = nzlo_in; iz <= nzhi_in; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + density_fft[n++] = density_brick[iz][iy][ix]; + + remap->perform(density_fft,density_fft,work1); +} + +/* ---------------------------------------------------------------------- + ghost-swap to fill ghost cells of my brick with field values +------------------------------------------------------------------------- */ + +void PPPM::fillbrick() +{ + int i,n,ix,iy,iz; + MPI_Request request; + MPI_Status status; + + // pack my real cells for +z processor + // pass data to self or +z processor + // unpack and sum recv data into my ghost cells + + n = 0; + for (iz = nzhi_in-nzhi_ghost+1; iz <= nzhi_in; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) { + buf1[n++] = vdx_brick[iz][iy][ix]; + buf1[n++] = vdy_brick[iz][iy][ix]; + buf1[n++] = vdz_brick[iz][iy][ix]; + } + + if (comm->procneigh[2][1] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[2][0],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[2][1],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_out; iz < nzlo_in; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) { + vdx_brick[iz][iy][ix] = buf2[n++]; + vdy_brick[iz][iy][ix] = buf2[n++]; + vdz_brick[iz][iy][ix] = buf2[n++]; + } + + // pack my real cells for -z processor + // pass data to self or -z processor + // unpack and sum recv data into my ghost cells + + n = 0; + for (iz = nzlo_in; iz < nzlo_in+nzlo_ghost; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) { + buf1[n++] = vdx_brick[iz][iy][ix]; + buf1[n++] = vdy_brick[iz][iy][ix]; + buf1[n++] = vdz_brick[iz][iy][ix]; + } + + if (comm->procneigh[2][0] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[2][1],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[2][0],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzhi_in+1; iz <= nzhi_out; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) { + vdx_brick[iz][iy][ix] = buf2[n++]; + vdy_brick[iz][iy][ix] = buf2[n++]; + vdz_brick[iz][iy][ix] = buf2[n++]; + } + + // pack my real cells for +y processor + // pass data to self or +y processor + // unpack and sum recv data into my ghost cells + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nyhi_in-nyhi_ghost+1; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) { + buf1[n++] = vdx_brick[iz][iy][ix]; + buf1[n++] = vdy_brick[iz][iy][ix]; + buf1[n++] = vdz_brick[iz][iy][ix]; + } + + if (comm->procneigh[1][1] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[1][0],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[1][1],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy < nylo_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) { + vdx_brick[iz][iy][ix] = buf2[n++]; + vdy_brick[iz][iy][ix] = buf2[n++]; + vdz_brick[iz][iy][ix] = buf2[n++]; + } + + // pack my real cells for -y processor + // pass data to self or -y processor + // unpack and sum recv data into my ghost cells + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_in; iy < nylo_in+nylo_ghost; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) { + buf1[n++] = vdx_brick[iz][iy][ix]; + buf1[n++] = vdy_brick[iz][iy][ix]; + buf1[n++] = vdz_brick[iz][iy][ix]; + } + + if (comm->procneigh[1][0] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[1][1],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[1][0],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nyhi_in+1; iy <= nyhi_out; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) { + vdx_brick[iz][iy][ix] = buf2[n++]; + vdy_brick[iz][iy][ix] = buf2[n++]; + vdz_brick[iz][iy][ix] = buf2[n++]; + } + + // pack my real cells for +x processor + // pass data to self or +x processor + // unpack and sum recv data into my ghost cells + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy <= nyhi_out; iy++) + for (ix = nxhi_in-nxhi_ghost+1; ix <= nxhi_in; ix++) { + buf1[n++] = vdx_brick[iz][iy][ix]; + buf1[n++] = vdy_brick[iz][iy][ix]; + buf1[n++] = vdz_brick[iz][iy][ix]; + } + + if (comm->procneigh[0][1] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[0][0],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[0][1],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy <= nyhi_out; iy++) + for (ix = nxlo_out; ix < nxlo_in; ix++) { + vdx_brick[iz][iy][ix] = buf2[n++]; + vdy_brick[iz][iy][ix] = buf2[n++]; + vdz_brick[iz][iy][ix] = buf2[n++]; + } + + // pack my real cells for -x processor + // pass data to self or -x processor + // unpack and sum recv data into my ghost cells + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy <= nyhi_out; iy++) + for (ix = nxlo_in; ix < nxlo_in+nxlo_ghost; ix++) { + buf1[n++] = vdx_brick[iz][iy][ix]; + buf1[n++] = vdy_brick[iz][iy][ix]; + buf1[n++] = vdz_brick[iz][iy][ix]; + } + + if (comm->procneigh[0][0] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[0][1],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[0][0],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy <= nyhi_out; iy++) + for (ix = nxhi_in+1; ix <= nxhi_out; ix++) { + vdx_brick[iz][iy][ix] = buf2[n++]; + vdy_brick[iz][iy][ix] = buf2[n++]; + vdz_brick[iz][iy][ix] = buf2[n++]; + } +} + +/* ---------------------------------------------------------------------- + find center grid pt for each of my particles + check that full stencil for the particle will fit in my 3d brick + store central grid pt indices in part2grid array +------------------------------------------------------------------------- */ + +void PPPM::particle_map() +{ + int nx,ny,nz; + + double **x = atom->x; + int nlocal = atom->nlocal; + double boxxlo = domain->boxxlo; + double boxylo = domain->boxylo; + double boxzlo = domain->boxzlo; + + int flag = 0; + for (int i = 0; i < nlocal; i++) { + + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // current particle coord can be outside global and local box + // add/subtract OFFSET to avoid int(-0.75) = 0 when want it to be -1 + + nx = static_cast<int> ((x[i][0]-boxxlo)*delxinv+shift) - OFFSET; + ny = static_cast<int> ((x[i][1]-boxylo)*delyinv+shift) - OFFSET; + nz = static_cast<int> ((x[i][2]-boxzlo)*delzinv+shift) - OFFSET; + + part2grid[i][0] = nx; + part2grid[i][1] = ny; + part2grid[i][2] = nz; + + // check that entire stencil around nx,ny,nz will fit in my 3d brick + + if (nx+nlower < nxlo_out || nx+nupper > nxhi_out || + ny+nlower < nylo_out || ny+nupper > nyhi_out || + nz+nlower < nzlo_out || nz+nupper > nzhi_out) flag++; + } + + int flag_all; + MPI_Allreduce(&flag,&flag_all,1,MPI_INT,MPI_SUM,world); + if (flag_all) error->all("Out of range atoms - cannot compute PPPM"); +} + +/* ---------------------------------------------------------------------- + create discretized "density" on section of global grid due to my particles + density(x,y,z) = charge "density" at grid points of my 3d brick + (nxlo:nxhi,nylo:nyhi,nzlo:nzhi) is extent of my brick (including ghosts) + in global grid +------------------------------------------------------------------------- */ + +void PPPM::make_rho() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz; + double dx,dy,dz,x0,y0,z0; + + // clear 3d density array + + double *vec = &density_brick[nzlo_out][nylo_out][nxlo_out]; + for (i = 0; i < ngrid; i++) vec[i] = 0.0; + + // loop over my charges, add their contribution to nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + + double *q = atom->q; + double **x = atom->x; + int nlocal = atom->nlocal; + double boxxlo = domain->boxxlo; + double boxylo = domain->boxylo; + double boxzlo = domain->boxzlo; + + for (int i = 0; i < nlocal; i++) { + + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxxlo)*delxinv; + dy = ny+shiftone - (x[i][1]-boxylo)*delyinv; + dz = nz+shiftone - (x[i][2]-boxzlo)*delzinv; + + compute_rho1d(dx,dy,dz); + + z0 = delvolinv * q[i]; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + y0 = z0*rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + x0 = y0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + density_brick[mz][my][mx] += x0*rho1d[0][l]; + } + } + } + } +} + +/* ---------------------------------------------------------------------- + FFT-based Poisson solver +------------------------------------------------------------------------- */ + +void PPPM::poisson(int eflag, int vflag) +{ + int i,j,k,n; + double eng; + + // transform charge density (r -> k) + + n = 0; + for (i = 0; i < nfft; i++) { + work1[n++] = density_fft[i]; + work1[n++] = 0.0; + } + + fft1->compute(work1,work1,1); + + // if requested, compute energy and virial contribution + + double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); + double s2 = scaleinv*scaleinv; + + if (eflag || vflag) { + if (vflag) { + n = 0; + for (i = 0; i < nfft; i++) { + eng = s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); + for (j = 0; j < 6; j++) virial[j] += eng*vg[i][j]; + energy += eng; + n += 2; + } + } else { + n = 0; + for (i = 0; i < nfft; i++) { + energy += + s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); + n += 2; + } + } + } + + // scale by 1/total-grid-pts to get rho(k) + // multiply by Green's function to get V(k) + + n = 0; + for (i = 0; i < nfft; i++) { + work1[n++] *= scaleinv * greensfn[i]; + work1[n++] *= scaleinv * greensfn[i]; + } + + // compute gradients of V(r) in each of 3 dims by transformimg -ik*V(k) + // FFT leaves data in 3d brick decomposition + // copy it into inner portion of vdx,vdy,vdz arrays + + // x direction gradient + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) + for (j = nylo_fft; j <= nyhi_fft; j++) + for (i = nxlo_fft; i <= nxhi_fft; i++) { + work2[n] = fkx[i]*work1[n+1]; + work2[n+1] = -fkx[i]*work1[n]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + vdx_brick[k][j][i] = work2[n]; + n += 2; + } + + // y direction gradient + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) + for (j = nylo_fft; j <= nyhi_fft; j++) + for (i = nxlo_fft; i <= nxhi_fft; i++) { + work2[n] = fky[j]*work1[n+1]; + work2[n+1] = -fky[j]*work1[n]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + vdy_brick[k][j][i] = work2[n]; + n += 2; + } + + // z direction gradient + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) + for (j = nylo_fft; j <= nyhi_fft; j++) + for (i = nxlo_fft; i <= nxhi_fft; i++) { + work2[n] = fkz[k]*work1[n+1]; + work2[n+1] = -fkz[k]*work1[n]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + vdz_brick[k][j][i] = work2[n]; + n += 2; + } +} + +/* ---------------------------------------------------------------------- + interpolate from grid to get electric field & force on my particles +------------------------------------------------------------------------- */ + +void PPPM::fieldforce() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz; + double dx,dy,dz,x0,y0,z0; + double ek[3]; + + // loop over my charges, interpolate electric field from nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + // ek = 3 components of E-field on particle + + double *q = atom->q; + double **x = atom->x; + double **f = atom->f; + int nlocal = atom->nlocal; + double boxxlo = domain->boxxlo; + double boxylo = domain->boxylo; + double boxzlo = domain->boxzlo; + + for (i = 0; i < nlocal; i++) { + + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxxlo)*delxinv; + dy = ny+shiftone - (x[i][1]-boxylo)*delyinv; + dz = nz+shiftone - (x[i][2]-boxzlo)*delzinv; + + compute_rho1d(dx,dy,dz); + + ek[0] = ek[1] = ek[2] = 0.0; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + z0 = rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + y0 = z0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + x0 = y0*rho1d[0][l]; + ek[0] -= x0*vdx_brick[mz][my][mx];; + ek[1] -= x0*vdy_brick[mz][my][mx];; + ek[2] -= x0*vdz_brick[mz][my][mx];; + } + } + } + + // convert E-field to force + + f[i][0] += qqrd2e*q[i]*ek[0]; + f[i][1] += qqrd2e*q[i]*ek[1]; + f[i][2] += qqrd2e*q[i]*ek[2]; + } +} + +/* ---------------------------------------------------------------------- + map nprocs to NX by NY grid as PX by PY procs - return optimal px,py +------------------------------------------------------------------------- */ + +void PPPM::procs2grid2d(int nprocs, int nx, int ny, int *px, int *py) +{ + // loop thru all possible factorizations of nprocs + // surf = surface area of largest proc sub-domain + // innermost if test minimizes surface area and surface/volume ratio + + int bestsurf = 2 * (nx + ny); + int bestboxx = 0; + int bestboxy = 0; + + int boxx,boxy,surf,ipx,ipy; + + ipx = 1; + while (ipx <= nprocs) { + if (nprocs % ipx == 0) { + ipy = nprocs/ipx; + boxx = nx/ipx; + if (nx % ipx) boxx++; + boxy = ny/ipy; + if (ny % ipy) boxy++; + surf = boxx + boxy; + if (surf < bestsurf || + (surf == bestsurf && boxx*boxy > bestboxx*bestboxy)) { + bestsurf = surf; + bestboxx = boxx; + bestboxy = boxy; + *px = ipx; + *py = ipy; + } + } + ipx++; + } +} + +/* ---------------------------------------------------------------------- + charge assignment into rho1d + dx,dy,dz = distance of particle from "lower left" grid point +------------------------------------------------------------------------- */ + +void PPPM::compute_rho1d(double dx, double dy, double dz) +{ + int k,l; + + for (k = (1-order)/2; k <= order/2; k++) { + rho1d[0][k] = 0.0; + rho1d[1][k] = 0.0; + rho1d[2][k] = 0.0; + for (l = order-1; l >= 0; l--) { + rho1d[0][k] = rho_coeff[l][k] + rho1d[0][k]*dx; + rho1d[1][k] = rho_coeff[l][k] + rho1d[1][k]*dy; + rho1d[2][k] = rho_coeff[l][k] + rho1d[2][k]*dz; + } + } +} + +/* ---------------------------------------------------------------------- + generate coeffients for the weight function of order n + + (n-1) + Wn(x) = Sum wn(k,x) , Sum is over every other integer + k=-(n-1) + For k=-(n-1),-(n-1)+2, ....., (n-1)-2,n-1 + k is odd integers if n is even and even integers if n is odd + --- + | n-1 + | Sum a(l,j)*(x-k/2)**l if abs(x-k/2) < 1/2 + wn(k,x) = < l=0 + | + | 0 otherwise + --- + a coeffients are packed into the array rho_coeff to eliminate zeros + rho_coeff(l,((k+mod(n+1,2))/2) = a(l,k) +------------------------------------------------------------------------- */ + +void PPPM::compute_rho_coeff() +{ + int j,k,l,m; + double s; + + double **a = memory->create_2d_double_array(order,-order,order,"pppm:a"); + + for (k = -order; k <= order; k++) + for (l = 0; l < order; l++) + a[l][k] = 0.0; + + a[0][0] = 1.0; + for (j = 1; j < order; j++) { + for (k = -j; k <= j; k += 2) { + s = 0.0; + for (l = 0; l < j; l++) { + a[l+1][k] = (a[l][k+1]-a[l][k-1]) / (l+1); + s += pow(0.5,(double) l+1) * + (a[l][k-1] + pow(-1.0,(double) l) * a[l][k+1]) / (l+1); + } + a[0][k] = s; + } + } + + m = (1-order)/2; + for (k = -(order-1); k < order; k += 2) { + for (l = 0; l < order; l++) + rho_coeff[l][m] = a[l][k]; + m++; + } + + memory->destroy_2d_double_array(a,-order); +} + +/* ---------------------------------------------------------------------- + Slab-geometry correction term to dampen inter-slab interactions between + periodically repeating slabs. Yields good approximation to 2D Ewald if + adequate empty space is left between repeating slabs (J. Chem. Phys. + 111, 3155). Slabs defined here to be parallel to the xy plane. +------------------------------------------------------------------------- */ + +void PPPM::slabcorr(int eflag) +{ + // compute local contribution to global dipole moment + + double *q = atom->q; + double **x = atom->x; + int nlocal = atom->nlocal; + + double dipole = 0.0; + for (int i = 0; i < nlocal; i++) dipole += q[i]*x[i][2]; + + // sum local contributions to get global dipole moment + + double dipole_all; + MPI_Allreduce(&dipole,&dipole_all,1,MPI_DOUBLE,MPI_SUM,world); + + // compute corrections + + double e_slabcorr = 2.0*PI*dipole_all*dipole_all/volume; + + if (eflag) energy += qqrd2e*e_slabcorr; + + // add on force corrections + + double ffact = -4.0*PI*dipole_all/volume; + double **f = atom->f; + + for (int i = 0; i < nlocal; i++) f[i][2] += qqrd2e*q[i]*ffact; +} + +/* ---------------------------------------------------------------------- + perform and time the 4 FFTs required for N timesteps +------------------------------------------------------------------------- */ + +void PPPM::timing(int n, double &time3d, double &time1d) +{ + double time1,time2; + + for (int i = 0; i < 2*nfft_both; i++) work1[i] = 0.0; + + MPI_Barrier(world); + time1 = MPI_Wtime(); + + for (int i = 0; i < n; i++) { + fft1->compute(work1,work1,1); + fft2->compute(work1,work1,-1); + fft2->compute(work1,work1,-1); + fft2->compute(work1,work1,-1); + } + + MPI_Barrier(world); + time2 = MPI_Wtime(); + time3d = time2 - time1; + + MPI_Barrier(world); + time1 = MPI_Wtime(); + + for (int i = 0; i < n; i++) { + fft1->timing1d(work1,nfft_both,1); + fft2->timing1d(work1,nfft_both,-1); + fft2->timing1d(work1,nfft_both,-1); + fft2->timing1d(work1,nfft_both,-1); + } + + MPI_Barrier(world); + time2 = MPI_Wtime(); + time1d = time2 - time1; +} + +/* ---------------------------------------------------------------------- + memory usage of local arrays +------------------------------------------------------------------------- */ + +int PPPM::memory_usage() +{ + int bytes = nmax*3 * sizeof(double); + int nbrick = (nxhi_out-nxlo_out+1) * (nyhi_out-nylo_out+1) * + (nzhi_out-nzlo_out+1); + bytes += 4 * nbrick * sizeof(double); + bytes += 6 * nfft_both * sizeof(double); + bytes += nfft_both*6 * sizeof(double); + bytes += 2 * nbuf * sizeof(double); + return bytes; +} diff --git a/src/KSPACE/pppm.h b/src/KSPACE/pppm.h new file mode 100644 index 0000000000000000000000000000000000000000..f75009842684b16e334c5f63d88c2eed7a342c82 --- /dev/null +++ b/src/KSPACE/pppm.h @@ -0,0 +1,94 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PPPM_H +#define PPPM_H + +#include "kspace.h" + +class FFT3d; +class Remap; + +class PPPM : public KSpace { + public: + PPPM(int, char **); + ~PPPM(); + void init(); + void setup(); + void compute(int, int); + void timing(int, double &, double &); + int memory_usage(); + + protected: + int me,nprocs; + double PI; + double precision; + int nfactors; + int *factors; + double qsum,qsqsum; + double qqrd2e; + double cutoff; + double volume; + double delxinv,delyinv,delzinv,delvolinv; + double shift,shiftone; + + int nxlo_in,nylo_in,nzlo_in,nxhi_in,nyhi_in,nzhi_in; + int nxlo_out,nylo_out,nzlo_out,nxhi_out,nyhi_out,nzhi_out; + int nxlo_ghost,nxhi_ghost,nylo_ghost,nyhi_ghost,nzlo_ghost,nzhi_ghost; + int nxlo_fft,nylo_fft,nzlo_fft,nxhi_fft,nyhi_fft,nzhi_fft; + int nlower,nupper; + int ngrid,nfft,nbuf,nfft_both; + + double ***density_brick; + double ***vdx_brick,***vdy_brick,***vdz_brick; + double *greensfn; + double **vg; + double *fkx,*fky,*fkz; + double *density_fft; + double *work1,*work2; + double *buf1,*buf2; + + double *gf_b; + double **rho1d,**rho_coeff; + + FFT3d *fft1,*fft2; + Remap *remap; + + int **part2grid; // storage for particle -> grid mapping + int nmax; + + // TIP4P settings + int typeH,typeO; // atom types of TIP4P water H and O atoms + double qdist; // distance from O site to negative charge + double alpha; // geometric factor + + void set_grid(); + void allocate(); + void deallocate(); + int factorable(int); + double rms(double, double, double, double, double **); + void compute_gf_denom(); + double gf_denom(double, double, double); + virtual void particle_map(); + virtual void make_rho(); + void brick2fft(); + void fillbrick(); + void poisson(int, int); + virtual void fieldforce(); + void procs2grid2d(int,int,int,int *, int*); + void compute_rho1d(double, double, double); + void compute_rho_coeff(); + void slabcorr(int); +}; + +#endif diff --git a/src/KSPACE/pppm_tip4p.cpp b/src/KSPACE/pppm_tip4p.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b677629255a33040cb3ce3b9eb6010f9327a1fd3 --- /dev/null +++ b/src/KSPACE/pppm_tip4p.cpp @@ -0,0 +1,261 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Amalie Frischknecht and Ahmed Ismail (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "pppm_tip4p.h" +#include "atom.h" +#include "domain.h" +#include "memory.h" +#include "error.h" + +#define OFFSET 4096 + +/* ---------------------------------------------------------------------- */ + +PPPMTIP4P::PPPMTIP4P(int narg, char **arg) : PPPM(narg, arg) {} + +/* ---------------------------------------------------------------------- + find center grid pt for each of my particles + check that full stencil for the particle will fit in my 3d brick + store central grid pt indices in part2grid array +------------------------------------------------------------------------- */ + +void PPPMTIP4P::particle_map() +{ + int nx,ny,nz,iH1,iH2; + double *xi,xM[3]; + + int *type = atom->type; + double **x = atom->x; + int nlocal = atom->nlocal; + double boxxlo = domain->boxxlo; + double boxylo = domain->boxylo; + double boxzlo = domain->boxzlo; + + int flag = 0; + for (int i = 0; i < nlocal; i++) { + if (type[i] == typeO) { + find_M(i,iH1,iH2,xM); + xi = xM; + } else xi = x[i]; + + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // current particle coord can be outside global and local box + // add/subtract OFFSET to avoid int(-0.75) = 0 when want it to be -1 + + nx = static_cast<int> ((xi[0]-boxxlo)*delxinv+shift) - OFFSET; + ny = static_cast<int> ((xi[1]-boxylo)*delyinv+shift) - OFFSET; + nz = static_cast<int> ((xi[2]-boxzlo)*delzinv+shift) - OFFSET; + + part2grid[i][0] = nx; + part2grid[i][1] = ny; + part2grid[i][2] = nz; + + // check that entire stencil around nx,ny,nz will fit in my 3d brick + + if (nx+nlower < nxlo_out || nx+nupper > nxhi_out || + ny+nlower < nylo_out || ny+nupper > nyhi_out || + nz+nlower < nzlo_out || nz+nupper > nzhi_out) flag++; + } + + int flag_all; + MPI_Allreduce(&flag,&flag_all,1,MPI_INT,MPI_SUM,world); + if (flag_all) error->all("Out of range atoms - cannot compute PPPM"); +} + +/* ---------------------------------------------------------------------- + create discretized "density" on section of global grid due to my particles + density(x,y,z) = charge "density" at grid points of my 3d brick + (nxlo:nxhi,nylo:nyhi,nzlo:nzhi) is extent of my brick (including ghosts) + in global grid +------------------------------------------------------------------------- */ + +void PPPMTIP4P::make_rho() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz,iH1,iH2; + double dx,dy,dz,x0,y0,z0; + double *xi,xM[3]; + + // clear 3d density array + + double *vec = &density_brick[nzlo_out][nylo_out][nxlo_out]; + for (i = 0; i < ngrid; i++) vec[i] = 0.0; + + // loop over my charges, add their contribution to nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + + int *type = atom->type; + double *q = atom->q; + double **x = atom->x; + int nlocal = atom->nlocal; + double boxxlo = domain->boxxlo; + double boxylo = domain->boxylo; + double boxzlo = domain->boxzlo; + + for (int i = 0; i < nlocal; i++) { + if (type[i] == typeO) { + find_M(i,iH1,iH2,xM); + xi = xM; + } else xi = x[i]; + + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (xi[0]-boxxlo)*delxinv; + dy = ny+shiftone - (xi[1]-boxylo)*delyinv; + dz = nz+shiftone - (xi[2]-boxzlo)*delzinv; + + compute_rho1d(dx,dy,dz); + + z0 = delvolinv * q[i]; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + y0 = z0*rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + x0 = y0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + density_brick[mz][my][mx] += x0*rho1d[0][l]; + } + } + } + } +} + +/* ---------------------------------------------------------------------- + interpolate from grid to get electric field & force on my particles +------------------------------------------------------------------------- */ + +void PPPMTIP4P::fieldforce() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz; + double dx,dy,dz,x0,y0,z0; + double ek[3]; + double *xi; + int iH1,iH2; + double xM[3]; + double fx,fy,fz; + + // loop over my charges, interpolate electric field from nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + // ek = 3 components of E-field on particle + + double *q = atom->q; + double **x = atom->x; + double **f = atom->f; + int *type = atom->type; + int nlocal = atom->nlocal; + double boxxlo = domain->boxxlo; + double boxylo = domain->boxylo; + double boxzlo = domain->boxzlo; + + for (i = 0; i < nlocal; i++) { + if (type[i] == typeO) { + find_M(i,iH1,iH2,xM); + xi = xM; + } else xi = x[i]; + + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (xi[0]-boxxlo)*delxinv; + dy = ny+shiftone - (xi[1]-boxylo)*delyinv; + dz = nz+shiftone - (xi[2]-boxzlo)*delzinv; + + compute_rho1d(dx,dy,dz); + + ek[0] = ek[1] = ek[2] = 0.0; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + z0 = rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + y0 = z0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + x0 = y0*rho1d[0][l]; + ek[0] -= x0*vdx_brick[mz][my][mx]; + ek[1] -= x0*vdy_brick[mz][my][mx]; + ek[2] -= x0*vdz_brick[mz][my][mx]; + } + } + } + + // convert E-field to force + + if (type[i] != typeO) { + f[i][0] += qqrd2e*q[i]*ek[0]; + f[i][1] += qqrd2e*q[i]*ek[1]; + f[i][2] += qqrd2e*q[i]*ek[2]; + } else { + + fx = qqrd2e * q[i] * ek[0]; + fy = qqrd2e * q[i] * ek[1]; + fz = qqrd2e * q[i] * ek[2]; + find_M(i,iH1,iH2,xM); + + f[i][0] += fx*(1.0-2.0*alpha); + f[i][1] += fy*(1.0-2.0*alpha); + f[i][2] += fz*(1.0-2.0*alpha); + + f[iH1][0] += alpha*(fx); + f[iH1][1] += alpha*(fy); + f[iH1][2] += alpha*(fz); + + f[iH2][0] += alpha*(fx); + f[iH2][1] += alpha*(fy); + f[iH2][2] += alpha*(fz); + } + } +} + +/* ---------------------------------------------------------------------- + find 2 H atoms bonded to O atom i + compute position xM of fictitious charge site for O atom + also return local indices iH1,iH2 of H atoms +------------------------------------------------------------------------- */ + +void PPPMTIP4P::find_M(int i, int &iH1, int &iH2, double *xM) +{ + iH1 = atom->map(atom->tag[i] + 1); + iH2 = atom->map(atom->tag[i] + 2); + + if (iH1 == -1 || iH2 == -1) error->one("TIP4P hydrogen is missing"); + if (atom->type[iH1] != typeH || atom->type[iH2] != typeH) + error->one("TIP4P hydrogen has incorrect atom type"); + + double **x = atom->x; + + double delx1 = x[iH1][0] - x[i][0]; + double dely1 = x[iH1][1] - x[i][1]; + double delz1 = x[iH1][2] - x[i][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + + double delx2 = x[iH2][0] - x[i][0]; + double dely2 = x[iH2][1] - x[i][1]; + double delz2 = x[iH2][2] - x[i][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + + xM[0] = x[i][0] + alpha * (delx1 + delx2); + xM[1] = x[i][1] + alpha * (dely1 + dely2); + xM[2] = x[i][2] + alpha * (delz1 + delz2); +} diff --git a/src/KSPACE/pppm_tip4p.h b/src/KSPACE/pppm_tip4p.h new file mode 100644 index 0000000000000000000000000000000000000000..c23ca062aeaa50b9f9b8b6ecbcaa13ee21236483 --- /dev/null +++ b/src/KSPACE/pppm_tip4p.h @@ -0,0 +1,31 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PPPM_TIP4P_H +#define PPPM_TIP4P_H + +#include "pppm.h" + +class PPPMTIP4P : public PPPM { + public: + PPPMTIP4P(int, char **); + + private: + void particle_map(); + void make_rho(); + void fieldforce(); + + void find_M(int, int &, int &, double *); +}; + +#endif diff --git a/src/KSPACE/remap.cpp b/src/KSPACE/remap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..215dc5b6d392942903de59611a2a98b3000b5437 --- /dev/null +++ b/src/KSPACE/remap.cpp @@ -0,0 +1,506 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "stdio.h" +#include "stdlib.h" +#include "remap.h" +#include "pack.h" + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +/* ---------------------------------------------------------------------- + Data layout for 3d remaps: + + data set of Nfast x Nmid x Nslow elements is owned by P procs + each element = nqty contiguous datums + on input, each proc owns a subsection of the elements + on output, each proc will own a (presumably different) subsection + my subsection must not overlap with any other proc's subsection, + i.e. the union of all proc's input (or output) subsections must + exactly tile the global Nfast x Nmid x Nslow data set + when called from C, all subsection indices are + C-style from 0 to N-1 where N = Nfast or Nmid or Nslow + when called from F77, all subsection indices are + F77-style from 1 to N where N = Nfast or Nmid or Nslow + a proc can own 0 elements on input or output + by specifying hi index < lo index + on both input and output, data is stored contiguously on a processor + with a fast-varying, mid-varying, and slow-varying index +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Perform 3d remap + + Arguments: + in starting address of input data on this proc + out starting address of where output data for this proc + will be placed (can be same as in) + buf extra memory required for remap + if memory=0 was used in call to remap_3d_create_plan + then buf must be big enough to hold output result + i.e. nqty * (out_ihi-out_ilo+1) * (out_jhi-out_jlo+1) * + (out_khi-out_klo+1) + if memory=1 was used in call to remap_3d_create_plan + then buf is not used, can just be a dummy pointer + plan plan returned by previous call to remap_3d_create_plan +------------------------------------------------------------------------- */ + +void remap_3d(double *in, double *out, double *buf, + struct remap_plan_3d *plan) + +{ + MPI_Status status; + int i,isend,irecv; + double *scratch; + + if (plan->memory == 0) + scratch = buf; + else + scratch = plan->scratch; + + // post all recvs into scratch space + + for (irecv = 0; irecv < plan->nrecv; irecv++) + MPI_Irecv(&scratch[plan->recv_bufloc[irecv]],plan->recv_size[irecv], + MPI_DOUBLE,plan->recv_proc[irecv],0, + plan->comm,&plan->request[irecv]); + + // send all messages to other procs + + for (isend = 0; isend < plan->nsend; isend++) { + plan->pack(&in[plan->send_offset[isend]], + plan->sendbuf,&plan->packplan[isend]); + MPI_Send(plan->sendbuf,plan->send_size[isend],MPI_DOUBLE, + plan->send_proc[isend],0,plan->comm); + } + + // copy in -> scratch -> out for self data + + if (plan->self) { + isend = plan->nsend; + irecv = plan->nrecv; + plan->pack(&in[plan->send_offset[isend]], + &scratch[plan->recv_bufloc[irecv]], + &plan->packplan[isend]); + plan->unpack(&scratch[plan->recv_bufloc[irecv]], + &out[plan->recv_offset[irecv]],&plan->unpackplan[irecv]); + } + + // unpack all messages from scratch -> out + + for (i = 0; i < plan->nrecv; i++) { + MPI_Waitany(plan->nrecv,plan->request,&irecv,&status); + plan->unpack(&scratch[plan->recv_bufloc[irecv]], + &out[plan->recv_offset[irecv]],&plan->unpackplan[irecv]); + } +} + +/* ---------------------------------------------------------------------- + Create plan for performing a 3d remap + + Arguments: + comm MPI communicator for the P procs which own the data + in_ilo,in_ihi input bounds of data I own in fast index + in_jlo,in_jhi input bounds of data I own in mid index + in_klo,in_khi input bounds of data I own in slow index + out_ilo,out_ihi output bounds of data I own in fast index + out_jlo,out_jhi output bounds of data I own in mid index + out_klo,out_khi output bounds of data I own in slow index + nqty # of datums per element + permute permutation in storage order of indices on output + 0 = no permutation + 1 = permute once = mid->fast, slow->mid, fast->slow + 2 = permute twice = slow->fast, fast->mid, mid->slow + memory user provides buffer memory for remap or system does + 0 = user provides memory + 1 = system provides memory + precision precision of data + 1 = single precision (4 bytes per datum) + 2 = double precision (8 bytes per datum) +------------------------------------------------------------------------- */ + +struct remap_plan_3d *remap_3d_create_plan( + MPI_Comm comm, + int in_ilo, int in_ihi, int in_jlo, int in_jhi, + int in_klo, int in_khi, + int out_ilo, int out_ihi, int out_jlo, int out_jhi, + int out_klo, int out_khi, + int nqty, int permute, int memory, int precision) + +{ + struct remap_plan_3d *plan; + struct extent_3d *array; + struct extent_3d in,out,overlap; + int i,iproc,nsend,nrecv,ibuf,size,me,nprocs; + + // query MPI info + + MPI_Comm_rank(comm,&me); + MPI_Comm_size(comm,&nprocs); + + // single precision not yet supported + + if (precision == 1) { + if (me == 0) printf("Single precision not supported\n"); + return NULL; + } + + // allocate memory for plan data struct + + plan = (struct remap_plan_3d *) malloc(sizeof(struct remap_plan_3d)); + if (plan == NULL) return NULL; + + // store parameters in local data structs + + in.ilo = in_ilo; + in.ihi = in_ihi; + in.isize = in.ihi - in.ilo + 1; + + in.jlo = in_jlo; + in.jhi = in_jhi; + in.jsize = in.jhi - in.jlo + 1; + + in.klo = in_klo; + in.khi = in_khi; + in.ksize = in.khi - in.klo + 1; + + out.ilo = out_ilo; + out.ihi = out_ihi; + out.isize = out.ihi - out.ilo + 1; + + out.jlo = out_jlo; + out.jhi = out_jhi; + out.jsize = out.jhi - out.jlo + 1; + + out.klo = out_klo; + out.khi = out_khi; + out.ksize = out.khi - out.klo + 1; + + // combine output extents across all procs + + array = (struct extent_3d *) malloc(nprocs*sizeof(struct extent_3d)); + if (array == NULL) return NULL; + + MPI_Allgather(&out,sizeof(struct extent_3d),MPI_BYTE, + array,sizeof(struct extent_3d),MPI_BYTE,comm); + + // count send collides, including self + + nsend = 0; + iproc = me; + for (i = 0; i < nprocs; i++) { + iproc++; + if (iproc == nprocs) iproc = 0; + nsend += remap_3d_collide(&in,&array[iproc],&overlap); + } + + // malloc space for send info + + if (nsend) { + if (precision == 1) + plan->pack = NULL; + else + plan->pack = pack_3d; + + plan->send_offset = (int *) malloc(nsend*sizeof(int)); + plan->send_size = (int *) malloc(nsend*sizeof(int)); + plan->send_proc = (int *) malloc(nsend*sizeof(int)); + plan->packplan = (struct pack_plan_3d *) + malloc(nsend*sizeof(struct pack_plan_3d)); + + if (plan->send_offset == NULL || plan->send_size == NULL || + plan->send_proc == NULL || plan->packplan == NULL) return NULL; + } + + // store send info, with self as last entry + + nsend = 0; + iproc = me; + for (i = 0; i < nprocs; i++) { + iproc++; + if (iproc == nprocs) iproc = 0; + if (remap_3d_collide(&in,&array[iproc],&overlap)) { + plan->send_proc[nsend] = iproc; + plan->send_offset[nsend] = nqty * + ((overlap.klo-in.klo)*in.jsize*in.isize + + ((overlap.jlo-in.jlo)*in.isize + overlap.ilo-in.ilo)); + plan->packplan[nsend].nfast = nqty*overlap.isize; + plan->packplan[nsend].nmid = overlap.jsize; + plan->packplan[nsend].nslow = overlap.ksize; + plan->packplan[nsend].nstride_line = nqty*in.isize; + plan->packplan[nsend].nstride_plane = nqty*in.jsize*in.isize; + plan->packplan[nsend].nqty = nqty; + plan->send_size[nsend] = nqty*overlap.isize*overlap.jsize*overlap.ksize; + nsend++; + } + } + + // plan->nsend = # of sends not including self + + if (nsend && plan->send_proc[nsend-1] == me) + plan->nsend = nsend - 1; + else + plan->nsend = nsend; + + // combine input extents across all procs + + MPI_Allgather(&in,sizeof(struct extent_3d),MPI_BYTE, + array,sizeof(struct extent_3d),MPI_BYTE,comm); + + // count recv collides, including self + + nrecv = 0; + iproc = me; + for (i = 0; i < nprocs; i++) { + iproc++; + if (iproc == nprocs) iproc = 0; + nrecv += remap_3d_collide(&out,&array[iproc],&overlap); + } + + // malloc space for recv info + + if (nrecv) { + if (precision == 1) { + if (permute == 0) + plan->unpack = NULL; + else if (permute == 1) { + if (nqty == 1) + plan->unpack = NULL; + else if (nqty == 2) + plan->unpack = NULL; + else + plan->unpack = NULL; + } + else if (permute == 2) { + if (nqty == 1) + plan->unpack = NULL; + else if (nqty == 2) + plan->unpack = NULL; + else + plan->unpack = NULL; + } + } + else if (precision == 2) { + if (permute == 0) + plan->unpack = unpack_3d; + else if (permute == 1) { + if (nqty == 1) + plan->unpack = unpack_3d_permute1_1; + else if (nqty == 2) + plan->unpack = unpack_3d_permute1_2; + else + plan->unpack = unpack_3d_permute1_n; + } + else if (permute == 2) { + if (nqty == 1) + plan->unpack = unpack_3d_permute2_1; + else if (nqty == 2) + plan->unpack = unpack_3d_permute2_2; + else + plan->unpack = unpack_3d_permute2_n; + } + } + + plan->recv_offset = (int *) malloc(nrecv*sizeof(int)); + plan->recv_size = (int *) malloc(nrecv*sizeof(int)); + plan->recv_proc = (int *) malloc(nrecv*sizeof(int)); + plan->recv_bufloc = (int *) malloc(nrecv*sizeof(int)); + plan->request = (MPI_Request *) malloc(nrecv*sizeof(MPI_Request)); + plan->unpackplan = (struct pack_plan_3d *) + malloc(nrecv*sizeof(struct pack_plan_3d)); + + if (plan->recv_offset == NULL || plan->recv_size == NULL || + plan->recv_proc == NULL || plan->recv_bufloc == NULL || + plan->request == NULL || plan->unpackplan == NULL) return NULL; + } + + // store recv info, with self as last entry + + ibuf = 0; + nrecv = 0; + iproc = me; + + for (i = 0; i < nprocs; i++) { + iproc++; + if (iproc == nprocs) iproc = 0; + if (remap_3d_collide(&out,&array[iproc],&overlap)) { + plan->recv_proc[nrecv] = iproc; + plan->recv_bufloc[nrecv] = ibuf; + + if (permute == 0) { + plan->recv_offset[nrecv] = nqty * + ((overlap.klo-out.klo)*out.jsize*out.isize + + (overlap.jlo-out.jlo)*out.isize + (overlap.ilo-out.ilo)); + plan->unpackplan[nrecv].nfast = nqty*overlap.isize; + plan->unpackplan[nrecv].nmid = overlap.jsize; + plan->unpackplan[nrecv].nslow = overlap.ksize; + plan->unpackplan[nrecv].nstride_line = nqty*out.isize; + plan->unpackplan[nrecv].nstride_plane = nqty*out.jsize*out.isize; + plan->unpackplan[nrecv].nqty = nqty; + } + else if (permute == 1) { + plan->recv_offset[nrecv] = nqty * + ((overlap.ilo-out.ilo)*out.ksize*out.jsize + + (overlap.klo-out.klo)*out.jsize + (overlap.jlo-out.jlo)); + plan->unpackplan[nrecv].nfast = overlap.isize; + plan->unpackplan[nrecv].nmid = overlap.jsize; + plan->unpackplan[nrecv].nslow = overlap.ksize; + plan->unpackplan[nrecv].nstride_line = nqty*out.jsize; + plan->unpackplan[nrecv].nstride_plane = nqty*out.ksize*out.jsize; + plan->unpackplan[nrecv].nqty = nqty; + } + else { + plan->recv_offset[nrecv] = nqty * + ((overlap.jlo-out.jlo)*out.isize*out.ksize + + (overlap.ilo-out.ilo)*out.ksize + (overlap.klo-out.klo)); + plan->unpackplan[nrecv].nfast = overlap.isize; + plan->unpackplan[nrecv].nmid = overlap.jsize; + plan->unpackplan[nrecv].nslow = overlap.ksize; + plan->unpackplan[nrecv].nstride_line = nqty*out.ksize; + plan->unpackplan[nrecv].nstride_plane = nqty*out.isize*out.ksize; + plan->unpackplan[nrecv].nqty = nqty; + } + + plan->recv_size[nrecv] = nqty*overlap.isize*overlap.jsize*overlap.ksize; + ibuf += plan->recv_size[nrecv]; + nrecv++; + } + } + + // plan->nrecv = # of recvs not including self + + if (nrecv && plan->recv_proc[nrecv-1] == me) + plan->nrecv = nrecv - 1; + else + plan->nrecv = nrecv; + + // init remaining fields in remap plan + + plan->memory = memory; + + if (nrecv == plan->nrecv) + plan->self = 0; + else + plan->self = 1; + + // free locally malloced space + + free(array); + + // find biggest send message (not including self) and malloc space for it + + plan->sendbuf = NULL; + + size = 0; + for (nsend = 0; nsend < plan->nsend; nsend++) + size = MAX(size,plan->send_size[nsend]); + + if (size) { + if (precision == 1) + plan->sendbuf = NULL; + else + plan->sendbuf = (double *) malloc(size*sizeof(double)); + if (plan->sendbuf == NULL) return NULL; + } + + // if requested, allocate internal scratch space for recvs, + // only need it if I will receive any data (including self) + + plan->scratch = NULL; + + if (memory == 1) { + if (nrecv > 0) { + if (precision == 1) + plan->scratch = NULL; + else + plan->scratch = + (double *) malloc(nqty*out.isize*out.jsize*out.ksize*sizeof(double)); + if (plan->scratch == NULL) return NULL; + } + } + + // create new MPI communicator for remap + + MPI_Comm_dup(comm,&plan->comm); + + // return pointer to plan + + return plan; +} + +/* ---------------------------------------------------------------------- + Destroy a 3d remap plan +------------------------------------------------------------------------- */ + +void remap_3d_destroy_plan(struct remap_plan_3d *plan) + +{ + // free MPI communicator + + MPI_Comm_free(&plan->comm); + + // free internal arrays + + if (plan->nsend || plan->self) { + free(plan->send_offset); + free(plan->send_size); + free(plan->send_proc); + free(plan->packplan); + if (plan->sendbuf) free(plan->sendbuf); + } + + if (plan->nrecv || plan->self) { + free(plan->recv_offset); + free(plan->recv_size); + free(plan->recv_proc); + free(plan->recv_bufloc); + free(plan->request); + free(plan->unpackplan); + if (plan->scratch) free(plan->scratch); + } + + // free plan itself + + free(plan); +} + +/* ---------------------------------------------------------------------- + collide 2 sets of indices to determine overlap + compare bounds of block1 with block2 to see if they overlap + return 1 if they do and put bounds of overlapping section in overlap + return 0 if they do not overlap +------------------------------------------------------------------------- */ + +int remap_3d_collide(struct extent_3d *block1, struct extent_3d *block2, + struct extent_3d *overlap) + +{ + overlap->ilo = MAX(block1->ilo,block2->ilo); + overlap->ihi = MIN(block1->ihi,block2->ihi); + overlap->jlo = MAX(block1->jlo,block2->jlo); + overlap->jhi = MIN(block1->jhi,block2->jhi); + overlap->klo = MAX(block1->klo,block2->klo); + overlap->khi = MIN(block1->khi,block2->khi); + + if (overlap->ilo > overlap->ihi || + overlap->jlo > overlap->jhi || + overlap->klo > overlap->khi) return 0; + + overlap->isize = overlap->ihi - overlap->ilo + 1; + overlap->jsize = overlap->jhi - overlap->jlo + 1; + overlap->ksize = overlap->khi - overlap->klo + 1; + + return 1; +} diff --git a/src/KSPACE/remap.h b/src/KSPACE/remap.h new file mode 100644 index 0000000000000000000000000000000000000000..15bcdbe41ea8d9eb19ccf3613eb4b5b535d7a399 --- /dev/null +++ b/src/KSPACE/remap.h @@ -0,0 +1,56 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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. +------------------------------------------------------------------------- */ + +// details of how to do a 3d remap + +struct remap_plan_3d { + double *sendbuf; // buffer for MPI sends + double *scratch; // scratch buffer for MPI recvs + void (*pack)(double *, double *, struct pack_plan_3d *); + // which pack function to use + void (*unpack)(double *, double *, struct pack_plan_3d *); + // which unpack function to use + int *send_offset; // extraction loc for each send + int *send_size; // size of each send message + int *send_proc; // proc to send each message to + struct pack_plan_3d *packplan; // pack plan for each send message + int *recv_offset; // insertion loc for each recv + int *recv_size; // size of each recv message + int *recv_proc; // proc to recv each message from + int *recv_bufloc; // offset in scratch buf for each recv + MPI_Request *request; // MPI request for each posted recv + struct pack_plan_3d *unpackplan; // unpack plan for each recv message + int nrecv; // # of recvs from other procs + int nsend; // # of sends to other procs + int self; // whether I send/recv with myself + int memory; // user provides scratch space or not + MPI_Comm comm; // group of procs performing remap +}; + +// collision between 2 regions + +struct extent_3d { + int ilo,ihi,isize; + int jlo,jhi,jsize; + int klo,khi,ksize; +}; + +// function prototypes + +void remap_3d(double *, double *, double *, struct remap_plan_3d *); +struct remap_plan_3d *remap_3d_create_plan(MPI_Comm, + int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int, int); +void remap_3d_destroy_plan(struct remap_plan_3d *); +int remap_3d_collide(struct extent_3d *, + struct extent_3d *, struct extent_3d *); diff --git a/src/KSPACE/remap_wrap.cpp b/src/KSPACE/remap_wrap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c652ab0f4c150ce96bf628a0d60f5aa5d9bc921 --- /dev/null +++ b/src/KSPACE/remap_wrap.cpp @@ -0,0 +1,46 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "remap_wrap.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +Remap::Remap(MPI_Comm comm, + int in_ilo, int in_ihi, int in_jlo, int in_jhi, + int in_klo, int in_khi, + int out_ilo, int out_ihi, int out_jlo, int out_jhi, + int out_klo, int out_khi, + int nqty, int permute, int memory, int precision) +{ + plan = remap_3d_create_plan(comm, + in_ilo,in_ihi,in_jlo,in_jhi,in_klo,in_khi, + out_ilo,out_ihi,out_jlo,out_jhi,out_klo,out_khi, + nqty,permute,memory,precision); + if (plan == NULL) error->one("Could not create 3d remap plan"); +} + +/* ---------------------------------------------------------------------- */ + +Remap::~Remap() +{ + remap_3d_destroy_plan(plan); +} + +/* ---------------------------------------------------------------------- */ + +void Remap::perform(double *in, double *out, double *buf) +{ + remap_3d(in,out,buf,plan); +} diff --git a/src/KSPACE/remap_wrap.h b/src/KSPACE/remap_wrap.h new file mode 100644 index 0000000000000000000000000000000000000000..b27a3593228e6f1b2a971650c6ea146c29d4e9d0 --- /dev/null +++ b/src/KSPACE/remap_wrap.h @@ -0,0 +1,31 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef REMAP_WRAP_H +#define REMAP_WRAP_H + +#include "lammps.h" +#include "remap.h" + +class Remap : public LAMMPS { + public: + Remap(MPI_Comm,int,int,int,int,int,int, + int,int,int,int,int,int,int,int,int,int); + ~Remap(); + void perform(double *, double *, double *); + + private: + struct remap_plan_3d *plan; +}; + +#endif diff --git a/src/KSPACE/style_kspace.h b/src/KSPACE/style_kspace.h new file mode 100644 index 0000000000000000000000000000000000000000..c6ea29b73ba6eed64bbff0f68bafbfe53a3682af --- /dev/null +++ b/src/KSPACE/style_kspace.h @@ -0,0 +1,38 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifdef KSpaceInclude +#include "ewald.h" +#include "pppm.h" +#include "pppm_tip4p.h" +#endif + +#ifdef KSpaceClass +KSpaceStyle(ewald,Ewald) +KSpaceStyle(pppm,PPPM) +KSpaceStyle(pppm/tip4p,PPPMTIP4P) +#endif + +#ifdef PairInclude +#include "pair_buck_coul_long.h" +#include "pair_lj_cut_coul_long.h" +#include "pair_lj_cut_coul_long_tip4p.h" +#include "pair_lj_charmm_coul_long.h" +#endif + +#ifdef PairClass +PairStyle(buck/coul/long,PairBuckCoulLong) +PairStyle(lj/cut/coul/long,PairLJCutCoulLong) +PairStyle(lj/cut/coul/long/tip4p,PairLJCutCoulLongTIP4P) +PairStyle(lj/charmm/coul/long,PairLJCharmmCoulLong) +#endif diff --git a/src/MAKE/Makefile.altix b/src/MAKE/Makefile.altix new file mode 100644 index 0000000000000000000000000000000000000000..84f5aae9cfa72686ad8869f465ad331b0e46676d --- /dev/null +++ b/src/MAKE/Makefile.altix @@ -0,0 +1,36 @@ +# altix = SGI Altix, Intel icc, MPI, FFTs from SGI SCSL library + +SHELL = /bin/sh +#.IGNORE: + +# System-specific settings + +CC = icc +CCFLAGS = -O2 -DFFT_SCSL -w +DEPFLAGS = -M +# one user needed icpc to link +LINK = icc +LINKFLAGS = -O2 +USRLIB = +SYSLIB = -lmpi -lscs_mp +SIZE = size + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) + diff --git a/src/MAKE/Makefile.bgl b/src/MAKE/Makefile.bgl new file mode 100644 index 0000000000000000000000000000000000000000..3f152f4f0b99a197038ce08f1e7ae2fb3dba6675 --- /dev/null +++ b/src/MAKE/Makefile.bgl @@ -0,0 +1,42 @@ +# bgl = LLNL Blue Gene Light machine, xlC, native MPI, FFTW + +SHELL = /bin/sh +.SUFFIXES: .cpp .u +.IGNORE: + +# System-specific settings + +CC = /opt/ibmcmp/vacpp/7.0/bin/blrts_xlC \ + -I/bgl/BlueLight/ppcfloor/bglsys/include \ + -I/bgl/local/bglfftwgel-2.1.5.pre5/include +CCFLAGS = -O3 -DFFT_FFTW -DMPICH_IGNORE_CXX_SEEK +DEPFLAGS = -M +LINK = /opt/ibmcmp/vacpp/7.0/bin/blrts_xlC +LINKFLAGS = -O3 -L/bgl/BlueLight/ppcfloor/bglsys/lib \ + -L/opt/ibmcmp/xlf/9.1/blrts_lib \ + -L/opt/ibmcmp/vacpp/7.0/blrts_lib \ + -L/bgl/local/lib \ + -L/bgl/local/bglfftwgel-2.1.5.pre5/lib +USRLIB = -lxlopt -lxlomp_ser -lxl -lxlfmath -lm -lfftw \ + -lmpich.rts -lmsglayer.rts -lrts.rts -ldevices.rts -lmassv +SYSLIB = +SIZE = size + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.u:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) -c $< + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.u) +include $(DEPENDS) diff --git a/src/MAKE/Makefile.cheetah b/src/MAKE/Makefile.cheetah new file mode 100755 index 0000000000000000000000000000000000000000..bc33d1badbd8c1c6a3745a70299ca0381dbbb17b --- /dev/null +++ b/src/MAKE/Makefile.cheetah @@ -0,0 +1,48 @@ +# cheetah = ORNL IBM machine, mpCC, native MPI, FFTW + +SHELL = /bin/sh +.SUFFIXES: .cpp .u +.IGNORE: + +# System-specific settings + +CC = mpCC_r +CCFLAGS = -O4 -qnoipa -I/usr/apps/include -DFFT_FFTW +DEPFLAGS = -M +LINK = mpCC_r +LINKFLAGS = -O -L/usr/apps/lib +USRLIB = -lfftw +SYSLIB = -lm +SIZE = size + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# --------- old section ------------- + +# Compilation rules + +#.cpp.o: +# $(CC) $(CCFLAGS) -c $< + +# Individual dependencies + +#$(OBJ): $(INC) + +# --------- new section ------------- + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.u:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) -c $< + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.u) +include $(DEPENDS) diff --git a/src/MAKE/Makefile.crockett b/src/MAKE/Makefile.crockett new file mode 100755 index 0000000000000000000000000000000000000000..a0615c2736a36a258d12fe851a458ae1525cee4e --- /dev/null +++ b/src/MAKE/Makefile.crockett @@ -0,0 +1,30 @@ +# crockett = RedHat Linux box, mpiCC, LAM MPI, no FFTs + +SHELL = /bin/sh +#.IGNORE: + +# System-specific settings + +CC = mpiCC +CCFLAGS = -g -O -DFFT_NONE -DGZIP +DEPFLAGS = -M +LINK = mpiCC +LINKFLAGS = -g -O +USRLIB = +SYSLIB = +SIZE = size + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Compilation rules + +.cpp.o: + $(CC) $(CCFLAGS) -c $< + +# Individual dependencies + +$(OBJ): $(INC) diff --git a/src/MAKE/Makefile.cygwin b/src/MAKE/Makefile.cygwin new file mode 100644 index 0000000000000000000000000000000000000000..92734357cbeddf496e1a7f8be05f4e709a1921a1 --- /dev/null +++ b/src/MAKE/Makefile.cygwin @@ -0,0 +1,40 @@ +# cygwin = RedHat Linux box, g++, no MPI, no FFTs + +SHELL = /bin/sh + +# System-specific settings + +CC = g++ +CCFLAGS = -O -I../STUBS -DFFT_NONE +DEPFLAGS = -M +LINK = g++ +LINKFLAGS = -O -L../STUBS +USRLIB = -lmpi +SYSLIB = +ARCHIVE = ar +ARFLAGS = -rc +SIZE = size + +# Link target + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE).exe + +# Library target + +lib: $(OBJ) + $(ARCHIVE) $(ARFLAGS) $(EXE) $(OBJ) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) diff --git a/src/MAKE/Makefile.debian b/src/MAKE/Makefile.debian new file mode 100644 index 0000000000000000000000000000000000000000..40d290a99e049b79f64dd9dbec16d6913a8c117a --- /dev/null +++ b/src/MAKE/Makefile.debian @@ -0,0 +1,41 @@ +# debian = Debian, g++, MPICH, FFTW + +SHELL = /bin/sh +#.IGNORE: + +# System-specific settings + +CC = g++ +CCFLAGS = -g -O -I/usr/lib/mpich/include/ -DFFT_FFTW -DGZIP +DEPFLAGS = -M +LINK = g++ +LINKFLAGS = -g -O -L/usr/lib/mpich/lib +USRLIB = -lfftw -lmpich +SYSLIB = +ARCHIVE = ar +ARFLAGS = -rc +SIZE = size + +# Link target + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Library target + +lib: $(OBJ) + $(ARCHIVE) $(ARFLAGS) $(EXE) $(OBJ) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) diff --git a/src/MAKE/Makefile.diesel b/src/MAKE/Makefile.diesel new file mode 100644 index 0000000000000000000000000000000000000000..d9cf913bd86bec62862a92ca3602ee7c42da89c9 --- /dev/null +++ b/src/MAKE/Makefile.diesel @@ -0,0 +1,39 @@ +# diesel = SGI Origin 350, 64-bit, SGI MIPSpro CC, SGI MPT, SGI SCSL MP FFTs + +SHELL = /bin/sh +#.IGNORE: + +# System-specific settings + +CC = CC +CCFLAGS = -64 -O -mp -DFFT_SCSL +DEPFLAGS = -M +LINK = CC +LINKFLAGS = -64 +USRLIB = +SYSLIB = -lm -lscs_mp -lmpi -lmpi++ +SIZE = size + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Library target + +lib: $(OBJ) + $(ARCHIVE) $(ARFLAGS) $(EXE) $(OBJ) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) diff --git a/src/MAKE/Makefile.fink b/src/MAKE/Makefile.fink new file mode 100644 index 0000000000000000000000000000000000000000..a5cde047142947550c1f3d9f2410d211f5e13b03 --- /dev/null +++ b/src/MAKE/Makefile.fink @@ -0,0 +1,35 @@ +# fink = Mac OS-X w/ fink installed libraries, c++, no MPI, FFTW 2.1.5 + +SHELL = /bin/sh + +# System-specific settings + +CC = c++ +CCFLAGS = -O -I../STUBS -I/sw/include -DFFT_FFTW +DEPFLAGS = -M +LINK = c++ +LINKFLAGS = -O -L../STUBS -L/sw/lib +USRLIB = -lfftw -lmpi +SYSLIB = +SIZE = size + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Compilation rules + + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) + diff --git a/src/MAKE/Makefile.g++ b/src/MAKE/Makefile.g++ new file mode 100755 index 0000000000000000000000000000000000000000..4ca8d5dba0284290a8cccd6efb1bb8ba656e5ae5 --- /dev/null +++ b/src/MAKE/Makefile.g++ @@ -0,0 +1,43 @@ +# g++ = RedHat Linux box, g++, MPICH, FFTW + +SHELL = /bin/sh +#.IGNORE: + +# System-specific settings + +CC = g++ +CCFLAGS = -g -O -I/home/sjplimp/tools/mpich/include \ + -I/home/sjplimp/tools/fftw/include -DFFT_FFTW -DGZIP +DEPFLAGS = -M +LINK = g++ +LINKFLAGS = -g -O -L/home/sjplimp/tools/mpich/lib \ + -L/home/sjplimp/tools/fftw/lib +USRLIB = -lfftw -lmpich +SYSLIB = +ARCHIVE = ar +ARFLAGS = -rc +SIZE = size + +# Link target + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Library target + +lib: $(OBJ) + $(ARCHIVE) $(ARFLAGS) $(EXE) $(OBJ) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) diff --git a/src/MAKE/Makefile.g++_poems b/src/MAKE/Makefile.g++_poems new file mode 100755 index 0000000000000000000000000000000000000000..1ab2bc3d11c36a462a775220d461b81afa6d89ac --- /dev/null +++ b/src/MAKE/Makefile.g++_poems @@ -0,0 +1,45 @@ +# g++_poems = RedHat Linux box, g++, MPICH, FFTW, POEMS library + +SHELL = /bin/sh +#.IGNORE: + +# System-specific settings + +CC = g++ +CCFLAGS = -g -O -I/home/sjplimp/tools/mpich/include \ + -I/home/sjplimp/lammps/lib/poems \ + -I/home/sjplimp/tools/fftw/include -DFFT_FFTW -DGZIP +DEPFLAGS = -M +LINK = g++ +LINKFLAGS = -g -O -L/home/sjplimp/tools/mpich/lib \ + -L/home/sjplimp/lammps/lib/poems \ + -L/home/sjplimp/tools/fftw/lib +USRLIB = -lfftw -lmpich -lpoems +SYSLIB = +ARCHIVE = ar +ARFLAGS = -rc +SIZE = size + +# Link target + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Library target + +lib: $(OBJ) + $(ARCHIVE) $(ARFLAGS) $(EXE) $(OBJ) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) diff --git a/src/MAKE/Makefile.liberty b/src/MAKE/Makefile.liberty new file mode 100755 index 0000000000000000000000000000000000000000..f1aa02ce43e8fb0dcacb29d1c37699478594715d --- /dev/null +++ b/src/MAKE/Makefile.liberty @@ -0,0 +1,36 @@ +# liberty = HP cluster with dual 3.0 GHz Xeons, mpiCC, native MPI, FFTW + +SHELL = /bin/sh +.IGNORE: + +# System-specific settings + +FFTW = /apps/libraries/fftw/icc + +CC = mpiCC +CCFLAGS = -O -DFFT_FFTW -I${FFTW}/fftw +DEPFLAGS = -M +LINK = mpiCC +LINKFLAGS = -O -L${FFTW}/fftw/.libs +USRLIB = -lfftw +SYSLIB = -lstdc++ -lm +SIZE = size + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) diff --git a/src/MAKE/Makefile.liberty_poems b/src/MAKE/Makefile.liberty_poems new file mode 100755 index 0000000000000000000000000000000000000000..a8970fbd621e6d1e0ac0a654ce19406fde4cc007 --- /dev/null +++ b/src/MAKE/Makefile.liberty_poems @@ -0,0 +1,36 @@ +# liberty_poems = HP clust w/ dual Xeons, mpiCC, native MPI, FFTW, POEMS lib + +SHELL = /bin/sh +.IGNORE: + +# System-specific settings + +FFTW = /apps/libraries/fftw/icc + +CC = mpiCC +CCFLAGS = -O -DFFT_FFTW -I${FFTW}/fftw -I/home/sjplimp/lammps/lib/poems +DEPFLAGS = -M +LINK = mpiCC +LINKFLAGS = -O -L${FFTW}/fftw/.libs -L/home/sjplimp/lammps/lib/poems +USRLIB = -lfftw -lpoems +SYSLIB = -lstdc++ -lm +SIZE = size + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) diff --git a/src/MAKE/Makefile.linux b/src/MAKE/Makefile.linux new file mode 100755 index 0000000000000000000000000000000000000000..c164a657808c58496df1e32921f7b7cbbea275cf --- /dev/null +++ b/src/MAKE/Makefile.linux @@ -0,0 +1,43 @@ +# linux = RedHat Linux box, Intel icc, MPICH, FFTW + +SHELL = /bin/sh +#.IGNORE: + +# System-specific settings + +CC = icc +CCFLAGS = -O -I/home/sjplimp/tools/mpich/include \ + -I/home/sjplimp/tools/fftw/include -DFFT_FFTW -DGZIP +DEPFLAGS = -M +LINK = icc +LINKFLAGS = -O -L/home/sjplimp/tools/mpich/lib \ + -L/home/sjplimp/tools/fftw/lib +USRLIB = -lfftw -lmpich +SYSLIB = -lcxa -lunwind -lstdc++ +ARCHIVE = ar +ARFLAGS = -rc +SIZE = size + +# Link target + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Library target + +lib: $(OBJ) + $(ARCHIVE) $(ARFLAGS) $(EXE) $(OBJ) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) diff --git a/src/MAKE/Makefile.linux_poems b/src/MAKE/Makefile.linux_poems new file mode 100755 index 0000000000000000000000000000000000000000..bf97e31ff272b4263b5f47728d7f97cb5ffd9ad2 --- /dev/null +++ b/src/MAKE/Makefile.linux_poems @@ -0,0 +1,45 @@ +# linux_poems = RedHat Linux box, Intel icc, MPICH, FFTW, POEMS library + +SHELL = /bin/sh +#.IGNORE: + +# System-specific settings + +CC = icc +CCFLAGS = -O -I/home/sjplimp/tools/mpich/include \ + -I/home/sjplimp/lammps/lib/poems \ + -I/home/sjplimp/tools/fftw/include -DFFT_FFTW -DGZIP +DEPFLAGS = -M +LINK = icc +LINKFLAGS = -O -L/home/sjplimp/tools/mpich/lib \ + -L/home/sjplimp/lammps/lib/poems \ + -L/home/sjplimp/tools/fftw/lib +USRLIB = -lfftw -lmpich -lpoems +SYSLIB = -lstdc++ -lcxa -lunwind +ARCHIVE = ar +ARFLAGS = -rc +SIZE = size + +# Link target + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Library target + +lib: $(OBJ) + $(ARCHIVE) $(ARFLAGS) $(EXE) $(OBJ) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) diff --git a/src/MAKE/Makefile.mac b/src/MAKE/Makefile.mac new file mode 100755 index 0000000000000000000000000000000000000000..e5d695535e49f165b447a734b706c130ef6a8fa6 --- /dev/null +++ b/src/MAKE/Makefile.mac @@ -0,0 +1,34 @@ +# mac = Apple PowerBook G4 laptop, c++, no MPI, FFTW 2.1.5 + +SHELL = /bin/sh + +# System-specific settings + +CC = c++ +CCFLAGS = -O -I../STUBS -I/Users/sjplimp/tools/fftw/include -DFFT_FFTW +DEPFLAGS = -M +LINK = c++ +LINKFLAGS = -O -L../STUBS -L/Users/sjplimp/tools/fftw/lib +USRLIB = -lfftw -lmpi +SYSLIB = +SIZE = size + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Compilation rules + + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) diff --git a/src/MAKE/Makefile.odin b/src/MAKE/Makefile.odin new file mode 100755 index 0000000000000000000000000000000000000000..c49301c230135c35470f928e132a5ffdae8c3ce0 --- /dev/null +++ b/src/MAKE/Makefile.odin @@ -0,0 +1,41 @@ +# odin = 1400 cluster, g++, MPICH, no FFTs + +SHELL = /bin/sh +#.IGNORE: + +# System-specific settings + +CC = g++ +CCFLAGS = -O -I/opt/mpich-mx/include -DFFT_NONE -DGZIP +DEPFLAGS = -M +LINK = g++ +LINKFLAGS = -O -L/opt/mpich-mx/lib -L/opt/mx/lib +USRLIB = -lmpich -lmyriexpress +SYSLIB = +ARCHIVE = ar +ARFLAGS = -rc +SIZE = size + +# Link target + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Library target + +lib: $(OBJ) + $(ARCHIVE) $(ARFLAGS) $(EXE) $(OBJ) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) diff --git a/src/MAKE/Makefile.ross b/src/MAKE/Makefile.ross new file mode 100755 index 0000000000000000000000000000000000000000..cbdd92f35a7556eee0a037c253db122d3904c614 --- /dev/null +++ b/src/MAKE/Makefile.ross @@ -0,0 +1,34 @@ +# ross = CPlant cluster (compile on taylor), c++, native MPI, DEC FFTs + +SHELL = /bin/sh +.IGNORE: + +# System-specific settings + +CC = /usr/local/cplant/ross/current/bin/c++ +CCFLAGS = -O -DFFT_DEC +DEPFLAGS = -M +LINK = /usr/local/cplant/ross/current/bin/c++ +LINKFLAGS = -O +USRLIB = -lmpi -lcxml +SYSLIB = -lm +SIZE = size + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) diff --git a/src/MAKE/Makefile.seaborg b/src/MAKE/Makefile.seaborg new file mode 100644 index 0000000000000000000000000000000000000000..3350d1a0c494ee58ae4d548e86b1adcf8c36c409 --- /dev/null +++ b/src/MAKE/Makefile.seaborg @@ -0,0 +1,49 @@ +# seaborg = NERSC IBM machine, mpCC, native MPI, FFTW + +SHELL = /bin/sh +.SUFFIXES: .cpp .u +.IGNORE: + +# System-specific settings + +CC = mpCC_r +CCFLAGS = -O2 -qnoipa -I/usr/common/usg/fftw/2.1.5/include -DFFT_FFTW +DEPFLAGS = -M +LINK = mpCC_r +LINKFLAGS = -O -L/usr/lib -L/usr/common/usg/fftw/2.1.5/lib +USRLIB = -lfftw -lfftw_mpi +SYSLIB = -lm +SIZE = size + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# --------- old section ------------- + +# Compilation rules + +#.cpp.o: +# $(CC) $(CCFLAGS) -c $< + +# Individual dependencies + +#$(OBJ): $(INC) + +# --------- new section ------------- + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.u:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) -c $< + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.u) +include $(DEPENDS) + diff --git a/src/MAKE/Makefile.serial b/src/MAKE/Makefile.serial new file mode 100755 index 0000000000000000000000000000000000000000..689675cf8fe49b1506656150c313406fa43bd035 --- /dev/null +++ b/src/MAKE/Makefile.serial @@ -0,0 +1,40 @@ +# serial = RedHat Linux box, g++, no MPI, no FFTs + +SHELL = /bin/sh + +# System-specific settings + +CC = g++ +CCFLAGS = -O -I../STUBS -DFFT_NONE +DEPFLAGS = -M +LINK = g++ +LINKFLAGS = -O -L../STUBS +USRLIB = -lmpi +SYSLIB = +ARCHIVE = ar +ARFLAGS = -rc +SIZE = size + +# Link target + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Library target + +lib: $(OBJ) + $(ARCHIVE) $(ARFLAGS) $(EXE) $(OBJ) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) diff --git a/src/MAKE/Makefile.spirit b/src/MAKE/Makefile.spirit new file mode 100644 index 0000000000000000000000000000000000000000..29793bf2ee92ccb56b4d1e504c3c3c79b69e11dc --- /dev/null +++ b/src/MAKE/Makefile.spirit @@ -0,0 +1,36 @@ +# spirit = HP cluster with dual 3.4 GHz EM64T (64 bit), mpiCC, native MPI, FFTW + +SHELL = /bin/sh +.IGNORE: + +# System-specific settings + +FFTW = /apps/libraries/fftw/nwcc + +CC = mpiCC +CCFLAGS = -O -DFFT_FFTW -I${FFTW}/include +DEPFLAGS = -M +LINK = mpiCC +LINKFLAGS = -O -L${FFTW}/lib +USRLIB = -lfftw -lstdc++ +SYSLIB = -lm +SIZE = size + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) diff --git a/src/MAKE/Makefile.squall b/src/MAKE/Makefile.squall new file mode 100755 index 0000000000000000000000000000000000000000..9e9ec6c655a9830df0213bda146ba8e6bcea6f5a --- /dev/null +++ b/src/MAKE/Makefile.squall @@ -0,0 +1,34 @@ +# squall = Red Squall (compile on reddish), pgCC, native MPI, FFTW + +SHELL = /bin/sh + +# System-specific settings + +CC = mpiCC +CCFLAGS = -fastsse -tp k8-64 -DGZIP -DFFT_FFTW \ + -I/home/sjplimp/tools/fftw/include +DEPFLAGS = -M +LINK = mpiCC +LINKFLAGS = -O -L/home/sjplimp/tools/fftw/lib +USRLIB = -lfftw -lmpi +SYSLIB = +SIZE = size + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) diff --git a/src/MAKE/Makefile.storm b/src/MAKE/Makefile.storm new file mode 100644 index 0000000000000000000000000000000000000000..185ce1390b3047df3ca329403c72c88274a7c417 --- /dev/null +++ b/src/MAKE/Makefile.storm @@ -0,0 +1,37 @@ +# storm = Cray Red Storm, Cray mpicxx, native MPI, FFTW + +SHELL = /bin/sh +.SUFFIXES: .cpp .d +.IGNORE: + +# System-specific settings + +CC = CC +CCFLAGS = -fastsse -DFFT_FFTW -DMPICH_IGNORE_CXX_SEEK \ + -I/projects/fftw/fftw-2.1.5/include +DEPFLAGS = -M +LINK = CC +LINKFLAGS = -O -L/projects/fftw/fftw-2.1.5/lib +USRLIB = -lfftw +SYSLIB = +SIZE = size + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Library target + +lib: $(OBJ) + $(ARCHIVE) $(ARFLAGS) $(EXE) $(OBJ) + +# Compilation rules + +.cpp.o: + $(CC) $(CCFLAGS) -c $< + +# Individual dependencies + +$(OBJ): $(INC) diff --git a/src/MAKE/Makefile.storm_poems b/src/MAKE/Makefile.storm_poems new file mode 100644 index 0000000000000000000000000000000000000000..1cc9f5e52877f71dc576cedc90948e354ccffb07 --- /dev/null +++ b/src/MAKE/Makefile.storm_poems @@ -0,0 +1,39 @@ +# storm_poems = Cray Red Storm, Cray mpicxx, native MPI, FFTW, POEMS library + +SHELL = /bin/sh +.SUFFIXES: .cpp .d +.IGNORE: + +# System-specific settings + +CC = CC +CCFLAGS = -fastsse -DFFT_FFTW -DMPICH_IGNORE_CXX_SEEK \ + -I/projects/fftw/fftw-2.1.5/include \ + -I/home/sjplimp/lammps/lib/poems +DEPFLAGS = -M +LINK = CC +LINKFLAGS = -O -L/projects/fftw/fftw-2.1.5/lib \ + -L/home/sjplimp/lammps/lib/poems +USRLIB = -lfftw -lpoems +SYSLIB = +SIZE = size + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Library target + +lib: $(OBJ) + $(ARCHIVE) $(ARFLAGS) $(EXE) $(OBJ) + +# Compilation rules + +.cpp.o: + $(CC) $(CCFLAGS) -c $< + +# Individual dependencies + +$(OBJ): $(INC) diff --git a/src/MAKE/Makefile.tbird b/src/MAKE/Makefile.tbird new file mode 100644 index 0000000000000000000000000000000000000000..2732d893eab8eb2f60f6b9897c50beea5184a316 --- /dev/null +++ b/src/MAKE/Makefile.tbird @@ -0,0 +1,36 @@ +# tbird = Dell cluster with dual 3.6 GHz Xeons, Intel mpicxx, native MPI, FFTW + +SHELL = /bin/sh +.IGNORE: + +# System-specific settings + +FFTW = /apps/libraries/fftw/nwcc + +CC = mpicxx +CCFLAGS = -O -DFFT_FFTW -I${FFTW}/include +DEPFLAGS = -M +LINK = mpicxx +LINKFLAGS = -O -L${FFTW}/lib +USRLIB = -lfftw -lstdc++ +SYSLIB = -lm +SIZE = size + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) diff --git a/src/MAKE/Makefile.tesla b/src/MAKE/Makefile.tesla new file mode 100755 index 0000000000000000000000000000000000000000..60180da58932ad03edd82ff29086f1aaeb8df834 --- /dev/null +++ b/src/MAKE/Makefile.tesla @@ -0,0 +1,35 @@ +# tesla = 16-proc SGI Onyx3, g++, no MPI, SGI FFTs + +SHELL = /bin/sh +#.IGNORE: + +# System-specific settings + +CC = g++ +CCFLAGS = -O -I../STUBS -DFFT_SGI +DEPFLAGS = -M +LINK = g++ +LINKFLAGS = -O -L../STUBS +USRLIB = -lmpi +SYSLIB = -lm -lcomplib.sgimath +SIZE = size + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) + diff --git a/src/MAKE/Makefile.tflop b/src/MAKE/Makefile.tflop new file mode 100755 index 0000000000000000000000000000000000000000..842e2feb751a2c5d2e296130913ed0c105154b05 --- /dev/null +++ b/src/MAKE/Makefile.tflop @@ -0,0 +1,31 @@ +# tflop = Intel Tflops (compile on sasn100), ciCC, native MPI, Intel FFTs + +SHELL = /bin/sh +.SUFFIXES: .cpp .d +.IGNORE: + +# System-specific settings + +CC = ciCC +CCFLAGS = -O4 -Knoieee -DFFT_INTEL +DEPFLAGS = -M +LINK = ciCC +LINKFLAGS = -Knoieee +USRLIB = -lmpi -lkmath +SYSLIB = +SIZE = xsize + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Compilation rules + +.cpp.o: + $(CC) $(CCFLAGS) -c $< + +# Individual dependencies + +$(OBJ): $(INC) diff --git a/src/MAKE/Makefile.valor b/src/MAKE/Makefile.valor new file mode 100644 index 0000000000000000000000000000000000000000..00a5495380ec6de130a8d97932f73047b110f749 --- /dev/null +++ b/src/MAKE/Makefile.valor @@ -0,0 +1,36 @@ +# valor = HP cluster with dual Xeons, mpiCC, native MPI, FFTW + +SHELL = /bin/sh +.IGNORE: + +# System-specific settings + +FFTW = /apps/libraries/fftw-2.1.5 + +CC = mpiCC +CCFLAGS = -O -DFFT_FFTW -I${FFTW}/include +DEPFLAGS = -M +LINK = mpiCC +LINKFLAGS = -O -L${FFTW}/lib +USRLIB = -lfftw -lstdc++ +SYSLIB = -lm +SIZE = size + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) diff --git a/src/MAKE/Makefile.ydl b/src/MAKE/Makefile.ydl new file mode 100644 index 0000000000000000000000000000000000000000..06f7a2c47fcd27c37f26bf394e482f8d3ac590e3 --- /dev/null +++ b/src/MAKE/Makefile.ydl @@ -0,0 +1,42 @@ +# ydl = Yellow Dog Linux box, xlc -q64, MPICH, FFTW + +SHELL = /bin/sh +#.IGNORE: + +# System-specific settings + +CC = xlc -q64 +CCFLAGS = -g -O -I/opt/mpich/include \ + -I/usr/local/include -L/opt/mpich/lib64 \ + -DFFT_FFTW -DGZIP +DEPFLAGS = -M +LINK = xlc -q64 +LINKFLAGS = -g -O -L/opt/mpich/lib64 \ + -L/usr/local/lib -lstdc++ -lc +USRLIB = -lfftw -lmpich +SYSLIB = +SIZE = size + +# Link rule + +$(EXE): $(OBJ) + $(LINK) $(LINKFLAGS) $(OBJ) $(USRLIB) $(SYSLIB) -o $(EXE) + $(SIZE) $(EXE) + +# Library target + +lib: $(OBJ) + $(ARCHIVE) $(ARFLAGS) $(EXE) $(OBJ) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< + +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# Individual dependencies + +DEPENDS = $(OBJ:.o=.d) +include $(DEPENDS) diff --git a/src/MAKE/Windows/erfc.cpp b/src/MAKE/Windows/erfc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bacb62b167f190deb4840eddf2928ea583dff744 --- /dev/null +++ b/src/MAKE/Windows/erfc.cpp @@ -0,0 +1,51 @@ +//This code was written by Philip Nicoletti +//http://www.codeguru.com/forum/archive/index.php/t-129990.html +// +//Modified by Jin Ma, Oklahoma State University for LAMMPS +//erfc() is defined in GNU libraries. This code is a simplified +//version for implementation with Visual C++. +// +//Warning: these functions are not fully tested. +// +#include "erfc.h" +#include "math.h" + +double erf(double x) +{ + // + // Computation of the error function erf(x). + // + return (1-erfc(x)); +} + +// +// +double erfc(double x) +{ + // + // Computation of the complementary error function erfc(x). + // + // The algorithm is based on a Chebyshev fit as denoted in + // Numerical Recipes 2nd ed. on p. 214 (W.H.Press et al.). + // + // The fractional error is always less than 1.2e-7. + // + // + // The parameters of the Chebyshev fit + // + const double a1 = -1.26551223, a2 = 1.00002368, + a3 = 0.37409196, a4 = 0.09678418, + a5 = -0.18628806, a6 = 0.27886807, + a7 = -1.13520398, a8 = 1.48851587, + a9 = -0.82215223, a10 = 0.17087277; + // + double v = 1; // The return value + double z = fabs(x); + // + if (z == 0) return v; // erfc(0)=1 + double t = 1/(1+0.5*z); + v = t*exp((-z*z) +a1+t*(a2+t*(a3+t*(a4+t*(a5+t*(a6+ + t*(a7+t*(a8+t*(a9+t*a10))))))))); + if (x < 0) v = 2-v; // erfc(-x)=2-erfc(x) + return v; +} \ No newline at end of file diff --git a/src/MAKE/Windows/erfc.h b/src/MAKE/Windows/erfc.h new file mode 100644 index 0000000000000000000000000000000000000000..ec22ee62f1d186464ff59bc9ec5f7e6b16dcc903 --- /dev/null +++ b/src/MAKE/Windows/erfc.h @@ -0,0 +1,5 @@ +// + +double erf(double x); + +double erfc(double x); \ No newline at end of file diff --git a/src/MAKE/Windows/notes.1 b/src/MAKE/Windows/notes.1 new file mode 100644 index 0000000000000000000000000000000000000000..317e42bdd91f553c75e896de4e504ca33447ae8c --- /dev/null +++ b/src/MAKE/Windows/notes.1 @@ -0,0 +1,52 @@ +Compiling LAMMPS under MS Windows: + +Tips from Jin Ma at Oklahoma State Univerisity +jin.ma@okstate.edu +November 20, 2004 + +compiled without MPI and FFT in Viusal C++ 6.0 + +------------------- + +0. Create an empty workspace (Win32 console), add all .h and .cpp +files into the project. + +1. At about 80 places in the code, variables are redefined. Most of +these variables are loop counters, which can be easily fixed. + +Code looks like this: + + for (int i=0; i<5; i++) { + something; + } + for (int i=0; i<5; i++) { + something else; + } + +This is ok with g++ compiler. But VC thinks the i is redefined in the +second loop. So the variable scope is different. This happens many times +in the code. It can be fixed easily based on the compiling error. + +2. At the beginning of fft3d.h, added: +#ifndef FFT_NONE +#define FFT_NONE +#endif + +3. In input.cpp, changed the two header files +//#include "unistd.h" +#include "direct.h" + +4. Added mpi.h and mpi.cpp (in STUBS folder) to the workspace +In mpi.cpp, commented the time.h header file +//#include <sys/time.h> +commented the original code in MPI_Wtime(), just make it return 0; + +5. In system.cpp, two changes due to difference in the input argument +list +Line 82: int iarg = 2; +Line 171: inflag=1; + +The number of input arguments (nargs) is different in g++ and VC when +you give arguments to run a program. This might be related to MPI as +well. The difference is one. Once the above changes are made, the +program is taking the correct argument. diff --git a/src/MAKE/Windows/notes.2 b/src/MAKE/Windows/notes.2 new file mode 100644 index 0000000000000000000000000000000000000000..763ca0647a917f6eb736c7fb6ef11cf3b8c6c9fa --- /dev/null +++ b/src/MAKE/Windows/notes.2 @@ -0,0 +1,128 @@ +/* +//This is instruction for the modification of LAMMPS for MS Windows +//LAMMPS version: Jan 2005 +// + +compiled without MPI and FFT in Viusal C++ 6.0 +(All packages except for XTC appear to work.) +------------------- + +1. Create an empty workspace (Win32 console), add all .h and .cpp +files into the project. + +2. At about 80 places in the code, variables are redefined. Most of +these variables are loop counters, which can be easily fixed. + +Code looks like this: + + for (int i=0; i<5; i++) { + something; + } + for (int i=0; i<5; i++) { + something else; + } + +This is ok with g++ compiler. But VC thinks the i is redefined in the +second loop. So the variable scope is different. This happens many times +in the code. It can be fixed easily based on the compiling error. + +3. At the beginning of fft3d.h, added: +#ifndef FFT_NONE +#define FFT_NONE +#endif + +4. In input.cpp, changed the two header files +//#include "unistd.h" +#include "direct.h" + +4A. (added by Tim Lau, MIT, ttl@mit.edu) + +In variable.cpp, change the header files +//#include "unistd.h" +#include "direct.h" +#include "windows.h" + +Change usleep(100000) to Sleep(100) + +Note that the value is divided by 1000 since usleep takes in +microseconds while Sleep takes in milliseconds. + +4B. (added by Tim Lau, MIT, ttl@mit.edu) + +In shell.cpp, change the header file: +//#include "unistd.h" +#include "direct.h" + +Change the line in shell.cpp: +mkdir(arg[i], S_IRWXU | S_IRGRP | S_IXGRP); +to: +mkdir(arg[i]); +since Windows obviously does not use UNIX file permissions. + +It's also possible that the line has to be changed to: +_mkdir(arg[i]); +depending on the version of the Visual C++ compiler used. + +5. Added mpi.h and mpi.cpp (in STUBS folder) to the workspace +In mpi.cpp, commented the time.h header file +//#include <sys/time.h> +commented the original code in MPI_Wtime(), just make it return 0; + +6. In system.cpp, two changes due to difference in the input argument +list + +Line 83: int iarg = 2; +Line 172: inflag=1; //add this line + +The number of input arguments (nargs) is different in g++ and VC when +you give arguments to run a program. This might be related to MPI as +well. The difference is one. Once the above changes are made, the +program is taking the correct argument. + +However, it has been observed in the latest versions of sytem.cpp that +no modification needs be made to the file as distributed from the +LAMMPS website to work. The user however, instead of starting LAMMPS +by the command: + +lammps in.file + +as he would if he implemented the changes detailed here, would launch +in the Unix style: + +lammps < in.file + +7. The new version LAMMPS calls the error function: + double erfc(double) + This function is in the GNU C library. However, it's not found for + VC++. + Three options: + a. One can try to find erfc() from other libraries. + b. The erfc() is called for pair_modify table option. One can set + the table option to be 0 to avoid calling this function. + c. Write your own functions. + + In this code, two files erfc.h, erfc.cpp are created and added to the project. + Files that call erfc() all add + #include "erfc.h" at the beginning. + Note: the functions are not fully tested, use with caution. + +8. MSVC does not have a inttypes.h file. The simplest way + to deal with this problem is to download inttypes.h from the + following site: + http://www.koders.com/c/fidDE7D6EFFD475FAB1B7F6A2BBA791401CFA88FFA3.aspx + and add this file into the workspace. + +9. MSVC does not have dirent.h. The problem is solved by downloading +a version of it for Windows from the following website: + +http://www.softagalleria.net/dirent/index.en.html + +10. Build the project. Specify appropriate input file to run the code. + The Windows result might be different from Unix results. Be Cautious. + +--------------------------------------------------------- + Jin Ma + Email: jin.ma@okstate.edu + Oklahoma State University + March 7, 2005 +--------------------------------------------------------- diff --git a/src/MAKE/Windows/notes.3 b/src/MAKE/Windows/notes.3 new file mode 100644 index 0000000000000000000000000000000000000000..6940365073203a9e8d927868193383a5beb778c8 --- /dev/null +++ b/src/MAKE/Windows/notes.3 @@ -0,0 +1,33 @@ +Using MPI and FFTW with LAMMPS under Windows +from Timothy Lau <ttl@MIT.EDU> +(the referenced step #'s refer to the notes.2 document) + +------- + +If the user would like to use FFT with LAMMPS, he can download the source code +for FFTW 2.1.5 and dump all the files into the same directory as LAMMPS. Then +he can add to the project all the .c and .h files of FFTW as though those were +LAMMPS files. Instead of following step 3 of the instructions, however, the +following should be added to fft3d.h: + +#ifndef FFT_FFTW +#define FFT_FFTW +#endif + +The user must take care to check for a Visual Studio compile that the "WIN32" +variable is defined although it is likely that Visual Studio would +automatically define this. Refer to line 137 of fftw.h that comes with FFTW +2.1.5. + +If the user would like to use MPI with his Microsoft Visual Studio compile for +use on a multicore processor or for use on a Windows cluster, it has been +observed that MPICH 2 (at least the IA32 version) is known to compile with +LAMMPS in Visual Studio. Instead of following step 5 of the instructions, the +user could add the MPICH2\include as an additional include directory for MSVS +to find "mpi.h" and also add the MPICH2\lib as an additional link directory. He +should add mpi.lib to be specifically linked to. + +------- + +To compile LAMMPS with MPI-2 (e.g. MPICH 2) on Windows, you need +to use the MPICH_IGNORE_CXX_SEEK preprocessor definition. diff --git a/src/MANYBODY/Install.csh b/src/MANYBODY/Install.csh new file mode 100644 index 0000000000000000000000000000000000000000..d5bce21f06f04672c5ab9c83ca669ea71d158479 --- /dev/null +++ b/src/MANYBODY/Install.csh @@ -0,0 +1,30 @@ +# Install/unInstall package classes in LAMMPS + +# pair_eam.h must always be in src + +if ($1 == 1) then + + cp style_manybody.h .. + + cp pair_eam.cpp .. + cp pair_eam_alloy.cpp .. + cp pair_eam_fs.cpp .. + +# cp pair_eam.h .. + cp pair_eam_alloy.h .. + cp pair_eam_fs.h .. + +else if ($1 == 0) then + + rm ../style_manybody.h + touch ../style_manybody.h + + rm ../pair_eam.cpp + rm ../pair_eam_alloy.cpp + rm ../pair_eam_fs.cpp + +# rm ../pair_eam.h + rm ../pair_eam_alloy.h + rm ../pair_eam_fs.h + +endif diff --git a/src/MANYBODY/pair_eam.cpp b/src/MANYBODY/pair_eam.cpp new file mode 100644 index 0000000000000000000000000000000000000000..651d32dde8db2533eb1add31c940fa9bfdf34a06 --- /dev/null +++ b/src/MANYBODY/pair_eam.cpp @@ -0,0 +1,927 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Stephen Foiles (SNL), Murray Daw (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "pair_eam.h" +#include "atom.h" +#include "force.h" +#include "update.h" +#include "comm.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +#define MAXLINE 1024 + +/* ---------------------------------------------------------------------- */ + +PairEAM::PairEAM() +{ + nmax = 0; + rho = NULL; + fp = NULL; + + ntables = 0; + tables = NULL; + frho = NULL; + frho_0 = NULL; + + // set rhor to NULL so memory deallocation will work + // even from derived classes that don't use rhor + + rhor = NULL; +} + +/* ---------------------------------------------------------------------- + free all arrays + check if allocated, since class can be destructed when incomplete +------------------------------------------------------------------------- */ + +PairEAM::~PairEAM() +{ + memory->sfree(rho); + memory->sfree(fp); + + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + memory->destroy_2d_int_array(tabindex); + } + + for (int m = 0; m < ntables; m++) { + delete [] tables[m].filename; + delete [] tables[m].frho; + delete [] tables[m].rhor; + delete [] tables[m].zr; + delete [] tables[m].z2r; + } + memory->sfree(tables); + + if (frho) { + memory->destroy_2d_double_array(frho); + memory->destroy_2d_double_array(rhor); + memory->destroy_2d_double_array(zrtmp); + memory->destroy_3d_double_array(z2r); + } + + if (frho_0) interpolate_deallocate(); +} + +/* ---------------------------------------------------------------------- */ + +void PairEAM::compute(int eflag, int vflag) +{ + int i,j,k,m,numneigh,itype,jtype; + double xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r,p,fforce,rhoip,rhojp,z2,z2p,recip,phi,phip,psip; + int *neighs; + double **f; + + // grow energy array if necessary + + if (atom->nmax > nmax) { + memory->sfree(rho); + memory->sfree(fp); + nmax = atom->nmax; + rho = (double *) memory->smalloc(nmax*sizeof(double),"eam:rho"); + fp = (double *) memory->smalloc(nmax*sizeof(double),"eam:fp"); + } + + eng_vdwl = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + int *type = atom->type; + int nlocal = atom->nlocal; + int newton_pair = force->newton_pair; + + // zero out density + + if (newton_pair) { + m = nlocal + atom->nghost; + for (i = 0; i < m; i++) rho[i] = 0.0; + } else for (i = 0; i < nlocal; i++) rho[i] = 0.0; + + // rho = density at each atom + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq < cutforcesq) { + jtype = type[j]; + p = sqrt(rsq)*rdr + 1.0; + m = static_cast<int> (p); + m = MIN(m,nr-1); + p -= m; + p = MIN(p,1.0); + rho[i] += ((rhor_3[jtype][m]*p + rhor_2[jtype][m])*p + + rhor_1[jtype][m])*p + rhor_0[jtype][m]; + if (newton_pair || j < nlocal) + rho[j] += ((rhor_3[itype][m]*p + rhor_2[itype][m])*p + + rhor_1[itype][m])*p + rhor_0[itype][m]; + } + } + } + + // communicate and sum densities + + if (newton_pair) comm->reverse_comm_pair(this); + + // fp = derivative of embedding energy at each atom + // phi = embedding energy at each atom + + for (i = 0; i < nlocal; i++) { + itype = type[i]; + p = rho[i]*rdrho + 1.0; + m = static_cast<int> (p); + m = MAX(1,MIN(m,nrho-1)); + p -= m; + p = MIN(p,1.0); + fp[i] = (frho_6[itype][m]*p + frho_5[itype][m])*p + frho_4[itype][m]; + if (eflag) { + phi = ((frho_3[itype][m]*p + frho_2[itype][m])*p + + frho_1[itype][m])*p + frho_0[itype][m]; + eng_vdwl += phi; + } + } + + // communicate derivative of embedding function + + comm->comm_pair(this); + + // compute forces on each atom + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq < cutforcesq) { + jtype = type[j]; + r = sqrt(rsq); + p = r*rdr + 1.0; + m = static_cast<int> (p); + m = MIN(m,nr-1); + p -= m; + p = MIN(p,1.0); + + // rhoip = derivative of (density at atom j due to atom i) + // rhojp = derivative of (density at atom i due to atom j) + // phi = pair potential energy + // phip = phi' + // z2 = phi * r + // z2p = (phi * r)' = (phi' r) + phi + // psip needs both fp[i] and fp[j] terms since r_ij appears in two + // terms of embed eng: Fi(sum rho_ij) and Fj(sum rho_ji) + // hence embed' = Fi(sum rho_ij) rhojp + Fj(sum rho_ji) rhoip + + rhoip = (rhor_6[itype][m]*p + rhor_5[itype][m])*p + + rhor_4[itype][m]; + rhojp = (rhor_6[jtype][m]*p + rhor_5[jtype][m])*p + + rhor_4[jtype][m]; + z2 = ((z2r_3[itype][jtype][m]*p + z2r_2[itype][jtype][m])*p + + z2r_1[itype][jtype][m])*p + z2r_0[itype][jtype][m]; + z2p = (z2r_6[itype][jtype][m]*p + z2r_5[itype][jtype][m])*p + + z2r_4[itype][jtype][m]; + + recip = 1.0/r; + phi = z2*recip; + phip = z2p*recip - phi*recip; + psip = fp[i]*rhojp + fp[j]*rhoip + phip; + fforce = psip*recip; + 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 (newton_pair || j < nlocal) eng_vdwl += phi; + else eng_vdwl += 0.5*phi; + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] -= 0.5*delx*delx*fforce; + virial[1] -= 0.5*dely*dely*fforce; + virial[2] -= 0.5*delz*delz*fforce; + virial[3] -= 0.5*delx*dely*fforce; + virial[4] -= 0.5*delx*delz*fforce; + virial[5] -= 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairEAM::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"); + tabindex = memory->create_2d_int_array(n+1,n+1,"pair:tabindex"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairEAM::settings(int narg, char **arg) +{ + if (narg > 0) error->all("Illegal pair_style command"); +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs + reading multiple funcfl files defines a funcfl alloy simulation +------------------------------------------------------------------------- */ + +void PairEAM::coeff(int narg, char **arg) +{ + if (!allocated) allocate(); + + if (narg != 3) error->all("Incorrect args for pair coefficients"); + + // parse pair of atom types + + int ilo,ihi,jlo,jhi; + force->bounds(arg[0],atom->ntypes,ilo,ihi); + force->bounds(arg[1],atom->ntypes,jlo,jhi); + + // read funcfl file only for i,i pairs + // only setflag i,i will be set + // set mass of each atom type + + int itable; + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + for (int j = MAX(jlo,i); j <= jhi; j++) { + if (i == j) { + itable = read_funcfl(arg[2]); + atom->set_mass(i,tables[itable].mass); + tabindex[i][i] = itable; + setflag[i][i] = 1; + count++; + } + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairEAM::init_one(int i, int j) +{ + // only setflag I,I was set by coeff + // mixing will occur in init_style if both I,I and J,J were set + + if (setflag[i][i] == 0 || setflag[j][j] == 0) + error->all("All EAM pair coeffs are not set"); + + // EAM has only one cutoff = max of all pairwise cutoffs + // determine max by checking table assigned to all type pairs + // only setflag[i][j] = 1 is relevant (if hybrid, some may not be set) + + cutmax = 0.0; + for (int ii = 1; ii <= atom->ntypes; ii++) { + for (int jj = ii; jj <= atom->ntypes; jj++) { + if (setflag[ii][jj] == 0) continue; + cutmax = MAX(cutmax,tables[tabindex[ii][jj]].cut); + } + } + + return cutmax; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairEAM::init_style() +{ + // set communication sizes in comm class + + comm->maxforward_pair = MAX(comm->maxforward_pair,1); + comm->maxreverse_pair = MAX(comm->maxreverse_pair,1); + + // convert read-in funcfl tables to multi-type setfl format and mix I,J + // interpolate final spline coeffs + + convert_funcfl(); + interpolate(); + + cutforcesq = cutmax*cutmax; +} + +/* ---------------------------------------------------------------------- + read potential values from a single element EAM file + read values into table and bcast values +------------------------------------------------------------------------- */ + +int PairEAM::read_funcfl(char *file) +{ + // check if same file has already been read + // if yes, return index of table entry + // if no, extend table list + + for (int i = 0; i < ntables; i++) + if (strcmp(file,tables->filename) == 0) return i; + + tables = (Table *) + memory->srealloc(tables,(ntables+1)*sizeof(Table),"pair:tables"); + + Table *tb = &tables[ntables]; + int n = strlen(file) + 1; + tb->filename = new char[n]; + strcpy(tb->filename,file); + tb->ith = tb->jth = 0; + + // open potential file + + int me = comm->me; + FILE *fp; + char line[MAXLINE]; + + if (me == 0) { + fp = fopen(file,"r"); + if (fp == NULL) { + char str[128]; + sprintf(str,"Cannot open EAM potential file %s",file); + error->one(str); + } + } + + // read and broadcast header + + int tmp; + if (me == 0) { + fgets(line,MAXLINE,fp); + fgets(line,MAXLINE,fp); + sscanf(line,"%d %lg",&tmp,&tb->mass); + fgets(line,MAXLINE,fp); + sscanf(line,"%d %lg %d %lg %lg", + &tb->nrho,&tb->drho,&tb->nr,&tb->dr,&tb->cut); + } + + MPI_Bcast(&tb->mass,1,MPI_DOUBLE,0,world); + MPI_Bcast(&tb->nrho,1,MPI_INT,0,world); + MPI_Bcast(&tb->drho,1,MPI_DOUBLE,0,world); + MPI_Bcast(&tb->nr,1,MPI_INT,0,world); + MPI_Bcast(&tb->dr,1,MPI_DOUBLE,0,world); + MPI_Bcast(&tb->cut,1,MPI_DOUBLE,0,world); + + // allocate potential arrays and read/bcast them + // set z2r to NULL (setfl array) so it can be deallocated + + tb->frho = new double[tb->nrho+1]; + tb->zr = new double[tb->nr+1]; + tb->rhor = new double[tb->nr+1]; + tb->z2r = NULL; + + if (me == 0) grab(fp,tb->nrho,&tb->frho[1]); + MPI_Bcast(&tb->frho[1],tb->nrho,MPI_DOUBLE,0,world); + + if (me == 0) grab(fp,tb->nr,&tb->zr[1]); + MPI_Bcast(&tb->zr[1],tb->nr,MPI_DOUBLE,0,world); + + if (me == 0) grab(fp,tb->nr,&tb->rhor[1]); + MPI_Bcast(&tb->rhor[1],tb->nr,MPI_DOUBLE,0,world); + + // close the potential file + + if (me == 0) fclose(fp); + + ntables++; + return ntables-1; +} + +/* ---------------------------------------------------------------------- + convert read-in funcfl potentials to multi-type setfl format +------------------------------------------------------------------------- */ + +void PairEAM::convert_funcfl() +{ + int i,j,k,m; + + int ntypes = atom->ntypes; + + // determine max values for all i,i type pairs + // skip if setflag = 0 (if hybrid, some may not be set) + + double rmax,rhomax; + dr = drho = rmax = rhomax = 0.0; + + for (int i = 1; i <= ntypes; i++) { + if (setflag[i][i] == 0) continue; + Table *tb = &tables[tabindex[i][i]]; + dr = MAX(dr,tb->dr); + drho = MAX(drho,tb->drho); + rmax = MAX(rmax,(tb->nr-1) * tb->dr); + rhomax = MAX(rhomax,(tb->nrho-1) * tb->drho); + } + + // set nr,nrho from cutoff and spacings + // 0.5 is for round-off in divide + + nr = static_cast<int> (rmax/dr + 0.5); + nrho = static_cast<int> (rhomax/drho + 0.5); + + // allocate multi-type setfl arrays + + if (frho) { + memory->destroy_2d_double_array(frho); + memory->destroy_2d_double_array(rhor); + memory->destroy_2d_double_array(zrtmp); + memory->destroy_3d_double_array(z2r); + } + + frho = (double **) + memory->create_2d_double_array(ntypes+1,nrho+1,"eam:frho"); + rhor = (double **) + memory->create_2d_double_array(ntypes+1,nr+1,"eam:rhor"); + zrtmp = (double **) + memory->create_2d_double_array(ntypes+1,nr+1,"eam:zrtmp"); + z2r = (double ***) + memory->create_3d_double_array(ntypes+1,ntypes+1,nr+1,"eam:frho"); + + // interpolate all potentials to a single grid and cutoff for all atom types + // frho,rhor are 1:ntypes, z2r is 1:ntypes,1:ntypes + // skip if setflag i,i or j,j = 0 (if hybrid, some may not be set) + + double r,p,cof1,cof2,cof3,cof4; + + for (i = 1; i <= ntypes; i++) { + if (setflag[i][i] == 0) continue; + Table *tb = &tables[tabindex[i][i]]; + for (m = 1; m <= nrho; m++) { + r = (m-1)*drho; + p = r/tb->drho + 1.0; + k = static_cast<int> (p); + k = MIN(k,tb->nrho-2); + k = MAX(k,2); + p -= k; + p = MIN(p,2.0); + cof1 = -0.166666667*p*(p-1.0)*(p-2.0); + cof2 = 0.5*(p*p-1.0)*(p-2.0); + cof3 = -0.5*p*(p+1.0)*(p-2.0); + cof4 = 0.166666667*p*(p*p-1.0); + frho[i][m] = cof1*tb->frho[k-1] + cof2*tb->frho[k] + + cof3*tb->frho[k+1] + cof4*tb->frho[k+2]; + } + } + + for (i = 1; i <= ntypes; i++) { + if (setflag[i][i] == 0) continue; + Table *tb = &tables[tabindex[i][i]]; + for (m = 1; m <= nr; m++) { + r = (m-1)*dr; + p = r/tb->dr + 1.0; + k = static_cast<int> (p); + k = MIN(k,tb->nr-2); + k = MAX(k,2); + p -= k; + p = MIN(p,2.0); + cof1 = -0.166666667*p*(p-1.0)*(p-2.0); + cof2 = 0.5*(p*p-1.0)*(p-2.0); + cof3 = -0.5*p*(p+1.0)*(p-2.0); + cof4 = 0.166666667*p*(p*p-1.0); + rhor[i][m] = cof1*tb->rhor[k-1] + cof2*tb->rhor[k] + + cof3*tb->rhor[k+1] + cof4*tb->rhor[k+2]; + zrtmp[i][m] = cof1*tb->zr[k-1] + cof2*tb->zr[k] + + cof3*tb->zr[k+1] + cof4*tb->zr[k+2]; + } + } + + for (i = 1; i <= ntypes; i++) + for (j = i; j <= ntypes; j++) { + if (setflag[i][i] == 0 || setflag[j][j] == 0) continue; + for (m = 1; m <= nr; m++) + z2r[i][j][m] = 27.2*0.529 * zrtmp[i][m]*zrtmp[j][m]; + } +} + +/* ---------------------------------------------------------------------- + interpolate EAM potentials +------------------------------------------------------------------------- */ + +void PairEAM::interpolate() +{ + // free memory from previous interpolation + + if (frho_0) interpolate_deallocate(); + + // interpolation spacings + + rdr = 1.0/dr; + rdrho = 1.0/drho; + + // allocate coeff arrays + + int n = atom->ntypes; + + frho_0 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_0"); + frho_1 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_1"); + frho_2 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_2"); + frho_3 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_3"); + frho_4 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_4"); + frho_5 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_5"); + frho_6 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_6"); + + rhor_0 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_0"); + rhor_1 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_1"); + rhor_2 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_2"); + rhor_3 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_3"); + rhor_4 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_4"); + rhor_5 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_5"); + rhor_6 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_6"); + + z2r_0 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_0"); + z2r_1 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_1"); + z2r_2 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_2"); + z2r_3 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_3"); + z2r_4 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_4"); + z2r_5 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_5"); + z2r_6 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_6"); + + // frho interpolation for 1:ntypes + // skip if setflag = 0 (if hybrid, some may not be set) + // if skip, set frho arrays to 0.0, since they will still be accessed + // for non-EAM atoms when compute() calculates embedding function + + int i,j,m; + + for (i = 1; i <= atom->ntypes; i++) { + if (setflag[i][i] == 0) { + for (j = 1; j <= n; j++) + for (m = 1; m <= nrho; m++) + frho_0[j][m] = frho_1[j][m] = frho_2[j][m] = frho_3[j][m] = + frho_4[j][m] = frho_5[j][m] = frho_6[j][m] = 0.0; + continue; + } + + for (m = 1; m <= nrho; m++) frho_0[i][m] = frho[i][m]; + + frho_1[i][1] = frho_0[i][2]-frho_0[i][1]; + frho_1[i][2] = 0.5*(frho_0[i][3]-frho_0[i][1]); + frho_1[i][nrho-1] = 0.5*(frho_0[i][nrho]-frho_0[i][nrho-2]); + frho_1[i][nrho] = frho_0[i][nrho]-frho_0[i][nrho-1]; + + for (m = 3; m <= nrho-2; m++) + frho_1[i][m] = ((frho_0[i][m-2]-frho_0[i][m+2]) + + 8.0*(frho_0[i][m+1]-frho_0[i][m-1]))/12.0; + + for (m = 1; m <= nrho-1; m++) { + frho_2[i][m] = 3.*(frho_0[i][m+1]-frho_0[i][m]) - + 2.0*frho_1[i][m] - frho_1[i][m+1]; + frho_3[i][m] = frho_1[i][m] + frho_1[i][m+1] - + 2.0*(frho_0[i][m+1]-frho_0[i][m]); + } + + frho_2[i][nrho] = 0.0; + frho_3[i][nrho] = 0.0; + + for (m = 1; m <= nrho; m++) { + frho_4[i][m] = frho_1[i][m]/drho; + frho_5[i][m] = 2.0*frho_2[i][m]/drho; + frho_6[i][m] = 3.0*frho_3[i][m]/drho; + } + } + + // rhor interpolation for 1:ntypes + // skip if setflag = 0 (if hybrid, some may not be set) + + for (i = 1; i <= atom->ntypes; i++) { + if (setflag[i][i] == 0) continue; + + for (m = 1; m <= nr; m++) rhor_0[i][m] = rhor[i][m]; + + rhor_1[i][1] = rhor_0[i][2]-rhor_0[i][1]; + rhor_1[i][2] = 0.5*(rhor_0[i][3]-rhor_0[i][1]); + rhor_1[i][nr-1] = 0.5*(rhor_0[i][nr]-rhor_0[i][nr-2]); + rhor_1[i][nr] = 0.0; + + for (m = 3; m <= nr-2; m++) + rhor_1[i][m] = ((rhor_0[i][m-2]-rhor_0[i][m+2]) + + 8.0*(rhor_0[i][m+1]-rhor_0[i][m-1]))/12.; + + for (m = 1; m <= nr-1; m++) { + rhor_2[i][m] = 3.0*(rhor_0[i][m+1]-rhor_0[i][m]) - + 2.0*rhor_1[i][m] - rhor_1[i][m+1]; + rhor_3[i][m] = rhor_1[i][m] + rhor_1[i][m+1] - + 2.0*(rhor_0[i][m+1]-rhor_0[i][m]); + } + + rhor_2[i][nr] = 0.0; + rhor_3[i][nr] = 0.0; + + for (m = 1; m <= nr; m++) { + rhor_4[i][m] = rhor_1[i][m]/dr; + rhor_5[i][m] = 2.0*rhor_2[i][m]/dr; + rhor_6[i][m] = 3.0*rhor_3[i][m]/dr; + } + } + + // z2r interpolation for 1:ntypes,1:ntypes + // skip if setflag i,i or j,j = 0 (if hybrid, some may not be set) + // set j,i coeffs = i,j coeffs + + for (i = 1; i <= atom->ntypes; i++) { + for (j = i; j <= atom->ntypes; j++) { + if (setflag[i][i] == 0 || setflag[j][j] == 0) continue; + + for (m = 1; m <= nr; m++) z2r_0[i][j][m] = z2r[i][j][m]; + + z2r_1[i][j][1] = z2r_0[i][j][2]-z2r_0[i][j][1]; + z2r_1[i][j][2] = 0.5*(z2r_0[i][j][3]-z2r_0[i][j][1]); + z2r_1[i][j][nr-1] = 0.5*(z2r_0[i][j][nr]-z2r_0[i][j][nr-2]); + z2r_1[i][j][nr] = 0.0; + + for (m = 3; m <= nr-2; m++) + z2r_1[i][j][m] = ((z2r_0[i][j][m-2]-z2r_0[i][j][m+2]) + + 8.0*(z2r_0[i][j][m+1]-z2r_0[i][j][m-1]))/12.; + + for (m = 1; m <= nr-1; m++) { + z2r_2[i][j][m] = 3.0*(z2r_0[i][j][m+1]-z2r_0[i][j][m]) - + 2.0*z2r_1[i][j][m] - z2r_1[i][j][m+1]; + z2r_3[i][j][m] = z2r_1[i][j][m] + z2r_1[i][j][m+1] - + 2.0*(z2r_0[i][j][m+1]-z2r_0[i][j][m]); + } + + z2r_2[i][j][nr] = 0.0; + z2r_3[i][j][nr] = 0.0; + + for (m = 1; m <= nr; m++) { + z2r_4[i][j][m] = z2r_1[i][j][m]/dr; + z2r_5[i][j][m] = 2.0*z2r_2[i][j][m]/dr; + z2r_6[i][j][m] = 3.0*z2r_3[i][j][m]/dr; + } + + for (m = 1; m <= nr; m++) { + z2r_0[j][i][m] = z2r_0[i][j][m]; + z2r_1[j][i][m] = z2r_1[i][j][m]; + z2r_2[j][i][m] = z2r_2[i][j][m]; + z2r_3[j][i][m] = z2r_3[i][j][m]; + z2r_4[j][i][m] = z2r_4[i][j][m]; + z2r_5[j][i][m] = z2r_5[i][j][m]; + z2r_6[j][i][m] = z2r_6[i][j][m]; + } + } + } +} + +/* ---------------------------------------------------------------------- + grab n values from file fp and put them in list + values can be several to a line + only called by proc 0 +------------------------------------------------------------------------- */ + +void PairEAM::grab(FILE *fp, int n, double *list) +{ + char *ptr; + char line[MAXLINE]; + + int i = 0; + while (i < n) { + fgets(line,MAXLINE,fp); + ptr = strtok(line," \t\n\r\f"); + list[i++] = atof(ptr); + while (ptr = strtok(NULL," \t\n\r\f")) list[i++] = atof(ptr); + } +} + +/* ---------------------------------------------------------------------- + skip n values from file fp + values can be several to a line + only called by proc 0 +------------------------------------------------------------------------- */ + +void PairEAM::skip(FILE *fp, int n) +{ + char line[MAXLINE]; + + int i = 0; + while (i < n) { + fgets(line,MAXLINE,fp); + strtok(line," \t\n\r\f"); + i++; + while (strtok(NULL," \t\n\r\f")) i++; + } +} + +/* ---------------------------------------------------------------------- + deallocate spline interpolation arrays +------------------------------------------------------------------------- */ + +void PairEAM::interpolate_deallocate() +{ + memory->destroy_2d_double_array(frho_0); + memory->destroy_2d_double_array(frho_1); + memory->destroy_2d_double_array(frho_2); + memory->destroy_2d_double_array(frho_3); + memory->destroy_2d_double_array(frho_4); + memory->destroy_2d_double_array(frho_5); + memory->destroy_2d_double_array(frho_6); + + memory->destroy_2d_double_array(rhor_0); + memory->destroy_2d_double_array(rhor_1); + memory->destroy_2d_double_array(rhor_2); + memory->destroy_2d_double_array(rhor_3); + memory->destroy_2d_double_array(rhor_4); + memory->destroy_2d_double_array(rhor_5); + memory->destroy_2d_double_array(rhor_6); + + memory->destroy_3d_double_array(z2r_0); + memory->destroy_3d_double_array(z2r_1); + memory->destroy_3d_double_array(z2r_2); + memory->destroy_3d_double_array(z2r_3); + memory->destroy_3d_double_array(z2r_4); + memory->destroy_3d_double_array(z2r_5); + memory->destroy_3d_double_array(z2r_6); +} + +/* ---------------------------------------------------------------------- */ + +void PairEAM::single(int i, int j, int itype, int jtype, + double rsq, double factor_coul, double factor_lj, + int eflag, One &one) +{ + double r,p,rhoip,rhojp,z2,z2p,recip,phi,phip,psip; + int m; + + r = sqrt(rsq); + p = r*rdr + 1.0; + m = static_cast<int> (p); + m = MIN(m,nr-1); + p -= m; + p = MIN(p,1.0); + + rhoip = (rhor_6[itype][m]*p + rhor_5[itype][m])*p + + rhor_4[itype][m]; + rhojp = (rhor_6[jtype][m]*p + rhor_5[jtype][m])*p + + rhor_4[jtype][m]; + z2 = ((z2r_3[itype][jtype][m]*p + z2r_2[itype][jtype][m])*p + + z2r_1[itype][jtype][m])*p + z2r_0[itype][jtype][m]; + z2p = (z2r_6[itype][jtype][m]*p + z2r_5[itype][jtype][m])*p + + z2r_4[itype][jtype][m]; + + recip = 1.0/r; + phi = z2*recip; + phip = z2p*recip - phi*recip; + psip = fp[i]*rhojp + fp[j]*rhoip + phip; + one.fforce = -psip*recip; + + if (eflag) { + one.eng_vdwl = phi; + one.eng_coul = 0.0; + } +} + +/* ---------------------------------------------------------------------- */ + +void PairEAM::single_embed(int i, int itype, double &fpi, + int eflag, double &phi) +{ + double p = rho[i]*rdrho + 1.0; + int m = static_cast<int> (p); + m = MAX(1,MIN(m,nrho-1)); + p -= m; + + fpi = (frho_6[itype][m]*p + frho_5[itype][m])*p + frho_4[itype][m]; + if (eflag) + phi = ((frho_3[itype][m]*p + frho_2[itype][m])*p + + frho_1[itype][m])*p + frho_0[itype][m]; +} + +/* ---------------------------------------------------------------------- */ + +int PairEAM::pack_comm(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = fp[j]; + } + return 1; +} + +/* ---------------------------------------------------------------------- */ + +void PairEAM::unpack_comm(int n, int first, double *buf) +{ + int i,m,last; + + m = 0; + last = first + n; + for (i = first; i < last; i++) fp[i] = buf[m++]; +} + +/* ---------------------------------------------------------------------- */ + +int PairEAM::pack_reverse_comm(int n, int first, double *buf) +{ + int i,m,last; + + m = 0; + last = first + n; + for (i = first; i < last; i++) buf[m++] = rho[i]; + return 1; +} + +/* ---------------------------------------------------------------------- */ + +void PairEAM::unpack_reverse_comm(int n, int *list, double *buf) +{ + int i,j,m; + + m = 0; + for (i = 0; i < n; i++) { + j = list[i]; + rho[j] += buf[m++]; + } +} + +/* ---------------------------------------------------------------------- + memory usage of local atom-based arrays +------------------------------------------------------------------------- */ + +int PairEAM::memory_usage() +{ + int bytes = 2 * nmax * sizeof(double); + return bytes; +} diff --git a/src/MANYBODY/pair_eam_alloy.cpp b/src/MANYBODY/pair_eam_alloy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ccd832c6ffb1ce63d6fceb89e6301a86013ae833 --- /dev/null +++ b/src/MANYBODY/pair_eam_alloy.cpp @@ -0,0 +1,472 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Stephen Foiles (SNL), Murray Daw (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "pair_eam_alloy.h" +#include "atom.h" +#include "force.h" +#include "comm.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +#define MAXLINE 1024 + +/* ---------------------------------------------------------------------- */ + +PairEAMAlloy::PairEAMAlloy() +{ + one_coeff = 1; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairEAMAlloy::coeff(int narg, char **arg) +{ + if (!allocated) allocate(); + + if (narg != 3 + atom->ntypes) + error->all("Incorrect args for pair coefficients"); + + // insure I,J args are * * + + if (strcmp(arg[0],"*") != 0 || strcmp(arg[1],"*") != 0) + error->all("Incorrect args for pair coefficients"); + + // read EAM setfl file, possibly multiple times + // first clear setflag since are doing this once for I,J = *,* + // read for all i,j pairs where ith,jth mapping is non-zero + // set setflag i,j for non-zero pairs + // set mass of atom type if i = j + + int n = atom->ntypes; + for (int i = 1; i <= n; i++) + for (int j = i; j <= n; j++) + setflag[i][j] = 0; + + int itable,ith,jth; + int ilo,ihi,jlo,jhi; + ilo = jlo = 1; + ihi = jhi = n; + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + for (int j = MAX(jlo,i); j <= jhi; j++) { + ith = atoi(arg[2+i]); + jth = atoi(arg[2+j]); + if (ith > 0 && jth > 0) { + itable = read_setfl(arg[2],ith,jth); + if (i == j) atom->set_mass(i,tables[itable].mass); + tabindex[i][j] = itable; + setflag[i][j] = 1; + count++; + } + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairEAMAlloy::init_one(int i, int j) +{ + if (setflag[i][j] == 0) + error->all("All EAM pair coeffs are not set"); + + // EAM has only one cutoff = max of all pairwise cutoffs + // determine max by checking table assigned to all type pairs + // only setflag[i][j] = 1 is relevant (if hybrid, some may not be set) + + cutmax = 0.0; + for (int ii = 1; ii <= atom->ntypes; ii++) { + for (int jj = ii; jj <= atom->ntypes; jj++) { + if (setflag[ii][jj] == 0) continue; + cutmax = MAX(cutmax,tables[tabindex[ii][jj]].cut); + } + } + + return cutmax; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairEAMAlloy::init_style() +{ + // set communication sizes in comm class + + comm->maxforward_pair = MAX(comm->maxforward_pair,1); + comm->maxreverse_pair = MAX(comm->maxreverse_pair,1); + + // copy read-in-tables to multi-type setfl format + // interpolate final spline coeffs + + store_setfl(); + interpolate(); + + cutforcesq = cutmax*cutmax; +} + +/* ---------------------------------------------------------------------- + read ith,jth potential values from a multi-element alloy EAM file + read values into table and bcast values +------------------------------------------------------------------------- */ + +int PairEAMAlloy::read_setfl(char *file, int ith, int jth) +{ + // check if ith,jth portion of same file has already been read + // if yes, return index of table entry + // if no, extend table list + + for (int i = 0; i < ntables; i++) + if (ith == tables[i].ith && jth == tables[i].jth) return i; + + tables = (Table *) + memory->srealloc(tables,(ntables+1)*sizeof(Table),"pair:tables"); + + Table *tb = &tables[ntables]; + int n = strlen(file) + 1; + tb->filename = new char[n]; + strcpy(tb->filename,file); + tb->ith = ith; + tb->jth = jth; + + // open potential file + + int me = comm->me; + FILE *fp; + char line[MAXLINE]; + + if (me == 0) { + fp = fopen(file,"r"); + if (fp == NULL) { + char str[128]; + sprintf(str,"Cannot open EAM potential file %s",file); + error->one(str); + } + } + + // read and broadcast header + + int ntypes; + if (me == 0) { + fgets(line,MAXLINE,fp); + fgets(line,MAXLINE,fp); + fgets(line,MAXLINE,fp); + fgets(line,MAXLINE,fp); + sscanf(line,"%d",&ntypes); + fgets(line,MAXLINE,fp); + sscanf(line,"%d %lg %d %lg %lg", + &tb->nrho,&tb->drho,&tb->nr,&tb->dr,&tb->cut); + } + + MPI_Bcast(&ntypes,1,MPI_INT,0,world); + MPI_Bcast(&tb->nrho,1,MPI_INT,0,world); + MPI_Bcast(&tb->drho,1,MPI_DOUBLE,0,world); + MPI_Bcast(&tb->nr,1,MPI_INT,0,world); + MPI_Bcast(&tb->dr,1,MPI_DOUBLE,0,world); + MPI_Bcast(&tb->cut,1,MPI_DOUBLE,0,world); + + // check if ith,jth are consistent with ntypes + + if (ith > ntypes || jth > ntypes) + error->all("Requested atom types in EAM setfl file do not exist"); + + // allocate potential arrays and read/bcast them + // skip sections of file that don't correspond to ith,jth + // extract mass, frho, rhor for i,i from ith element section + // extract z2r for i,j from ith,jth array of z2r section + // note that ith can be < or > than jth + // set zr to NULL (funcl array) so it can be deallocated + + tb->frho = new double[tb->nrho+1]; + tb->rhor = new double[tb->nr+1]; + tb->z2r = new double[tb->nr+1]; + tb->zr = NULL; + + int i,j,tmp; + double mass; + + for (i = 1; i <= ntypes; i++) { + if (me == 0) { + fgets(line,MAXLINE,fp); + sscanf(line,"%d %lg",&tmp,&mass); + } + MPI_Bcast(&mass,1,MPI_DOUBLE,0,world); + + if (i == ith && ith == jth) { + tb->mass = mass; + if (me == 0) grab(fp,tb->nrho,&tb->frho[1]); + MPI_Bcast(&tb->frho[1],tb->nrho,MPI_DOUBLE,0,world); + if (me == 0) grab(fp,tb->nr,&tb->rhor[1]); + MPI_Bcast(&tb->rhor[1],tb->nr,MPI_DOUBLE,0,world); + } else { + if (me == 0) skip(fp,tb->nrho); + if (me == 0) skip(fp,tb->nr); + } + } + + for (i = 1; i <= ntypes; i++) { + for (j = 1; j <= i; j++) { + if ((i == ith && j == jth) || (j == ith && i == jth)) { + if (me == 0) grab(fp,tb->nr,&tb->z2r[1]); + MPI_Bcast(&tb->z2r[1],tb->nr,MPI_DOUBLE,0,world); + } else if (me == 0) skip(fp,tb->nr); + } + } + + // close the potential file + + if (me == 0) fclose(fp); + + ntables++; + return ntables-1; +} + +/* ---------------------------------------------------------------------- + store read-in setfl values in multi-type setfl format +------------------------------------------------------------------------- */ + +void PairEAMAlloy::store_setfl() +{ + int i,j,m; + + int ntypes = atom->ntypes; + + // set nrho,nr,drho,dr from any i,i table entry since all the same + + for (i = 1; i <= ntypes; i++) + if (setflag[i][i]) break; + + nrho = tables[tabindex[i][i]].nrho; + nr = tables[tabindex[i][i]].nr; + drho = tables[tabindex[i][i]].drho; + dr = tables[tabindex[i][i]].dr; + + // allocate multi-type setfl arrays + + if (frho) { + memory->destroy_2d_double_array(frho); + memory->destroy_2d_double_array(rhor); + memory->destroy_2d_double_array(zrtmp); + memory->destroy_3d_double_array(z2r); + } + + frho = (double **) + memory->create_2d_double_array(ntypes+1,nrho+1,"eam:frho"); + rhor = (double **) + memory->create_2d_double_array(ntypes+1,nr+1,"eam:rhor"); + zrtmp = (double **) + memory->create_2d_double_array(ntypes+1,nr+1,"eam:zrtmp"); + z2r = (double ***) + memory->create_3d_double_array(ntypes+1,ntypes+1,nr+1,"eam:frho"); + + // copy from read-in tables to multi-type setfl arrays + // frho,rhor are 1:ntypes, z2r is 1:ntypes,1:ntypes + // skip if setflag i,j = 0 (if hybrid, some may not be set) + + for (i = 1; i <= ntypes; i++) + for (j = i; j <= ntypes; j++) { + if (setflag[i][j] == 0) continue; + Table *tb = &tables[tabindex[i][j]]; + if (i == j) { + for (m = 1; m <= nrho; m++) frho[i][m] = tb->frho[m]; + for (m = 1; m <= nr; m++) rhor[i][m] = tb->rhor[m]; + } + for (m = 1; m <= nr; m++) z2r[i][j][m] = tb->z2r[m]; + } +} + +/* ---------------------------------------------------------------------- + interpolate EAM potentials +------------------------------------------------------------------------- */ + +void PairEAMAlloy::interpolate() +{ + // free memory from previous interpolation + + if (frho_0) interpolate_deallocate(); + + // interpolation spacings + + rdr = 1.0/dr; + rdrho = 1.0/drho; + + // allocate coeff arrays + + int n = atom->ntypes; + + frho_0 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_0"); + frho_1 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_1"); + frho_2 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_2"); + frho_3 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_3"); + frho_4 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_4"); + frho_5 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_5"); + frho_6 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_6"); + + rhor_0 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_0"); + rhor_1 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_1"); + rhor_2 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_2"); + rhor_3 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_3"); + rhor_4 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_4"); + rhor_5 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_5"); + rhor_6 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_6"); + + z2r_0 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_0"); + z2r_1 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_1"); + z2r_2 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_2"); + z2r_3 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_3"); + z2r_4 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_4"); + z2r_5 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_5"); + z2r_6 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_6"); + + // frho interpolation for 1:ntypes + // skip if setflag = 0 (if hybrid, some may not be set) + // if skip, set frho arrays to 0.0, since they will still be accessed + // for non-EAM atoms when compute() calculates embedding function + + int i,j,m; + + for (i = 1; i <= atom->ntypes; i++) { + if (setflag[i][i] == 0) { + for (m = 1; m <= nrho; m++) + frho_0[i][m] = frho_1[i][m] = frho_2[i][m] = frho_3[i][m] = + frho_4[i][m] = frho_5[i][m] = frho_6[i][m] = 0.0; + continue; + } + + for (m = 1; m <= nrho; m++) frho_0[i][m] = frho[i][m]; + + frho_1[i][1] = frho_0[i][2]-frho_0[i][1]; + frho_1[i][2] = 0.5*(frho_0[i][3]-frho_0[i][1]); + frho_1[i][nrho-1] = 0.5*(frho_0[i][nrho]-frho_0[i][nrho-2]); + frho_1[i][nrho] = frho_0[i][nrho]-frho_0[i][nrho-1]; + + for (m = 3; m <= nrho-2; m++) + frho_1[i][m] = ((frho_0[i][m-2]-frho_0[i][m+2]) + + 8.0*(frho_0[i][m+1]-frho_0[i][m-1]))/12.0; + + for (m = 1; m <= nrho-1; m++) { + frho_2[i][m] = 3.*(frho_0[i][m+1]-frho_0[i][m]) - + 2.0*frho_1[i][m] - frho_1[i][m+1]; + frho_3[i][m] = frho_1[i][m] + frho_1[i][m+1] - + 2.0*(frho_0[i][m+1]-frho_0[i][m]); + } + + frho_2[i][nrho] = 0.0; + frho_3[i][nrho] = 0.0; + + for (m = 1; m <= nrho; m++) { + frho_4[i][m] = frho_1[i][m]/drho; + frho_5[i][m] = 2.0*frho_2[i][m]/drho; + frho_6[i][m] = 3.0*frho_3[i][m]/drho; + } + } + + // rhor interpolation for 1:ntypes + // skip if setflag = 0 (if hybrid, some may not be set) + + for (i = 1; i <= atom->ntypes; i++) { + if (setflag[i][i] == 0) continue; + + for (m = 1; m <= nr; m++) rhor_0[i][m] = rhor[i][m]; + + rhor_1[i][1] = rhor_0[i][2]-rhor_0[i][1]; + rhor_1[i][2] = 0.5*(rhor_0[i][3]-rhor_0[i][1]); + rhor_1[i][nr-1] = 0.5*(rhor_0[i][nr]-rhor_0[i][nr-2]); + rhor_1[i][nr] = 0.0; + + for (m = 3; m <= nr-2; m++) + rhor_1[i][m] = ((rhor_0[i][m-2]-rhor_0[i][m+2]) + + 8.0*(rhor_0[i][m+1]-rhor_0[i][m-1]))/12.; + + for (m = 1; m <= nr-1; m++) { + rhor_2[i][m] = 3.0*(rhor_0[i][m+1]-rhor_0[i][m]) - + 2.0*rhor_1[i][m] - rhor_1[i][m+1]; + rhor_3[i][m] = rhor_1[i][m] + rhor_1[i][m+1] - + 2.0*(rhor_0[i][m+1]-rhor_0[i][m]); + } + + rhor_2[i][nr] = 0.0; + rhor_3[i][nr] = 0.0; + + for (m = 1; m <= nr; m++) { + rhor_4[i][m] = rhor_1[i][m]/dr; + rhor_5[i][m] = 2.0*rhor_2[i][m]/dr; + rhor_6[i][m] = 3.0*rhor_3[i][m]/dr; + } + } + + // z2r interpolation for 1:ntypes,1:ntypes + // skip if setflag i,j = 0 (if hybrid, some may not be set) + // set j,i coeffs = i,j coeffs + + for (i = 1; i <= atom->ntypes; i++) { + for (j = i; j <= atom->ntypes; j++) { + if (setflag[i][j] == 0) continue; + + for (m = 1; m <= nr; m++) z2r_0[i][j][m] = z2r[i][j][m]; + + z2r_1[i][j][1] = z2r_0[i][j][2]-z2r_0[i][j][1]; + z2r_1[i][j][2] = 0.5*(z2r_0[i][j][3]-z2r_0[i][j][1]); + z2r_1[i][j][nr-1] = 0.5*(z2r_0[i][j][nr]-z2r_0[i][j][nr-2]); + z2r_1[i][j][nr] = 0.0; + + for (m = 3; m <= nr-2; m++) + z2r_1[i][j][m] = ((z2r_0[i][j][m-2]-z2r_0[i][j][m+2]) + + 8.0*(z2r_0[i][j][m+1]-z2r_0[i][j][m-1]))/12.; + + for (m = 1; m <= nr-1; m++) { + z2r_2[i][j][m] = 3.0*(z2r_0[i][j][m+1]-z2r_0[i][j][m]) - + 2.0*z2r_1[i][j][m] - z2r_1[i][j][m+1]; + z2r_3[i][j][m] = z2r_1[i][j][m] + z2r_1[i][j][m+1] - + 2.0*(z2r_0[i][j][m+1]-z2r_0[i][j][m]); + } + + z2r_2[i][j][nr] = 0.0; + z2r_3[i][j][nr] = 0.0; + + for (m = 1; m <= nr; m++) { + z2r_4[i][j][m] = z2r_1[i][j][m]/dr; + z2r_5[i][j][m] = 2.0*z2r_2[i][j][m]/dr; + z2r_6[i][j][m] = 3.0*z2r_3[i][j][m]/dr; + } + + for (m = 1; m <= nr; m++) { + z2r_0[j][i][m] = z2r_0[i][j][m]; + z2r_1[j][i][m] = z2r_1[i][j][m]; + z2r_2[j][i][m] = z2r_2[i][j][m]; + z2r_3[j][i][m] = z2r_3[i][j][m]; + z2r_4[j][i][m] = z2r_4[i][j][m]; + z2r_5[j][i][m] = z2r_5[i][j][m]; + z2r_6[j][i][m] = z2r_6[i][j][m]; + } + } + } +} diff --git a/src/MANYBODY/pair_eam_alloy.h b/src/MANYBODY/pair_eam_alloy.h new file mode 100644 index 0000000000000000000000000000000000000000..9296baff48d30cf548624b489f9d8eb2e198b22d --- /dev/null +++ b/src/MANYBODY/pair_eam_alloy.h @@ -0,0 +1,32 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_EAM_ALLOY_H +#define PAIR_EAM_ALLOY_H + +#include "pair_eam.h" + +class PairEAMAlloy : public PairEAM { + public: + PairEAMAlloy(); + void coeff(int, char **); + double init_one(int, int); + void init_style(); + + private: + int read_setfl(char *, int, int); + void store_setfl(); + void interpolate(); +}; + +#endif diff --git a/src/MANYBODY/pair_eam_fs.cpp b/src/MANYBODY/pair_eam_fs.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e1cf0cc78bd26b957125db57e35f142f2c75eab3 --- /dev/null +++ b/src/MANYBODY/pair_eam_fs.cpp @@ -0,0 +1,771 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Tim Lau (MIT) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "pair_eam_fs.h" +#include "atom.h" +#include "force.h" +#include "update.h" +#include "comm.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +#define MAXLINE 1024 + +/* ---------------------------------------------------------------------- */ + +PairEAMFS::PairEAMFS() +{ + one_coeff = 1; +} + +/* ---------------------------------------------------------------------- */ + +PairEAMFS::~PairEAMFS() { + + // deallocate array unique to derived class, parent will deallocate the rest + + if (frho) memory->destroy_3d_double_array(rhor_fs); + + // insure derived-class's deallocate is called + // set ptr to NULL to prevent parent class from calling it's deallocate + + if (frho_0) interpolate_deallocate(); + frho_0 = NULL; +} + +/* ---------------------------------------------------------------------- */ + +void PairEAMFS::compute(int eflag, int vflag) +{ + int i,j,k,m,numneigh,itype,jtype; + double xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r,p,fforce,rhoip,rhojp,z2,z2p,recip,phi,phip,psip; + int *neighs; + double **f; + + // grow energy array if necessary + + if (atom->nmax > nmax) { + memory->sfree(rho); + memory->sfree(fp); + nmax = atom->nmax; + rho = (double *) memory->smalloc(nmax*sizeof(double),"eam:rho"); + fp = (double *) memory->smalloc(nmax*sizeof(double),"eam:fp"); + } + + eng_vdwl = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + int *type = atom->type; + int nlocal = atom->nlocal; + int newton_pair = force->newton_pair; + + // zero out density + + if (newton_pair) { + m = nlocal + atom->nghost; + for (i = 0; i < m; i++) rho[i] = 0.0; + } else for (i = 0; i < nlocal; i++) rho[i] = 0.0; + + // rho = density at each atom + // loop over neighbors of my atoms + // FS has type-specific rho functional + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq < cutforcesq) { + jtype = type[j]; + p = sqrt(rsq)*rdr + 1.0; + m = static_cast<int> (p); + m = MIN(m,nr-1); + p -= m; + p = MIN(p,1.0); + rho[i] += + ((rhor_fs_3[jtype][itype][m]*p + rhor_fs_2[jtype][itype][m])*p + + rhor_fs_1[jtype][itype][m])*p + rhor_fs_0[jtype][itype][m]; + if (newton_pair || j < nlocal) + rho[j] += + ((rhor_fs_3[itype][jtype][m]*p + rhor_fs_2[itype][jtype][m])*p + + rhor_fs_1[itype][jtype][m])*p + rhor_fs_0[itype][jtype][m]; + } + } + } + + // communicate and sum densities + + if (newton_pair) comm->reverse_comm_pair(this); + + // fp = derivative of embedding energy at each atom + // phi = embedding energy at each atom + // FS is same as standard EAM + + for (i = 0; i < nlocal; i++) { + itype = type[i]; + p = rho[i]*rdrho + 1.0; + m = static_cast<int> (p); + m = MAX(1,MIN(m,nrho-1)); + p -= m; + p = MIN(p,1.0); + fp[i] = (frho_6[itype][m]*p + frho_5[itype][m])*p + frho_4[itype][m]; + if (eflag) { + phi = ((frho_3[itype][m]*p + frho_2[itype][m])*p + + frho_1[itype][m])*p + frho_0[itype][m]; + eng_vdwl += phi; + } + } + + // communicate derivative of embedding function + + comm->comm_pair(this); + + // compute forces on each atom + // loop over neighbors of my atoms + // FS has type-specific rhoip and rhojp + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq < cutforcesq) { + jtype = type[j]; + r = sqrt(rsq); + p = r*rdr + 1.0; + m = static_cast<int> (p); + m = MIN(m,nr-1); + p -= m; + p = MIN(p,1.0); + + // rhoip = derivative of (density at atom j due to atom i) + // rhojp = derivative of (density at atom i due to atom j) + // phi = pair potential energy + // phip = phi' + // z2 = phi * r + // z2p = (phi * r)' = (phi' r) + phi + // psip needs both fp[i] and fp[j] terms since r_ij appears in two + // terms of embed eng: Fi(sum rho_ij) and Fj(sum rho_ji) + // hence embed' = Fi(sum rho_ij) rhojp + Fj(sum rho_ji) rhoip + + rhoip = + (rhor_fs_6[itype][jtype][m]*p + rhor_fs_5[itype][jtype][m])*p + + rhor_fs_4[itype][jtype][m]; + rhojp = + (rhor_fs_6[jtype][itype][m]*p + rhor_fs_5[jtype][itype][m])*p + + rhor_fs_4[jtype][itype][m]; + z2 = ((z2r_3[itype][jtype][m]*p + z2r_2[itype][jtype][m])*p + + z2r_1[itype][jtype][m])*p + z2r_0[itype][jtype][m]; + z2p = (z2r_6[itype][jtype][m]*p + z2r_5[itype][jtype][m])*p + + z2r_4[itype][jtype][m]; + + recip = 1.0/r; + phi = z2*recip; + phip = z2p*recip - phi*recip; + psip = fp[i]*rhojp + fp[j]*rhoip + phip; + fforce = psip*recip; + 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 (newton_pair || j < nlocal) eng_vdwl += phi; + else eng_vdwl += 0.5*phi; + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] -= 0.5*delx*delx*fforce; + virial[1] -= 0.5*dely*dely*fforce; + virial[2] -= 0.5*delz*delz*fforce; + virial[3] -= 0.5*delx*dely*fforce; + virial[4] -= 0.5*delx*delz*fforce; + virial[5] -= 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairEAMFS::coeff(int narg, char **arg) +{ + if (!allocated) allocate(); + + if (narg != 3 + atom->ntypes) + error->all("Incorrect args for pair coefficients"); + + // insure I,J args are * * + // can only have one file, so clear all setflag settings + + if (strcmp(arg[0],"*") != 0 || strcmp(arg[1],"*") != 0) + error->all("Incorrect args for pair coefficients"); + + // read Finnis/Sinclair modified setfl file + // first clear setflag since are doing this once for I,J = *,* + // read for all i,j pairs where ith,jth mapping is non-zero + // set setflag i,j for non-zero pairs + // set mass of atom type if i = j + + int n = atom->ntypes; + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) + setflag[i][j] = 0; + + int itable,ith,jth; + int ilo,ihi,jlo,jhi; + ilo = jlo = 1; + ihi = jhi = n; + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + for (int j = jlo; j <= jhi; j++) { + ith = atoi(arg[2+i]); + jth = atoi(arg[2+j]); + if (ith > 0 && jth > 0) { + itable = read_setfl(arg[2],ith,jth); + if (i == j) atom->set_mass(i,tables[itable].mass); + tabindex[i][j] = itable; + setflag[i][j] = 1; + count++; + } + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairEAMFS::init_one(int i, int j) +{ + if (setflag[i][j] == 0) + error->all("All EAM pair coeffs are not set"); + + // EAM has only one cutoff = max of all pairwise cutoffs + // determine max by checking table assigned to all type pairs + // only setflag[i][j] = 1 is relevant (if hybrid, some may not be set) + + cutmax = 0.0; + for (int ii = 1; ii <= atom->ntypes; ii++) { + for (int jj = ii; jj <= atom->ntypes; jj++) { + if (setflag[ii][jj] == 0) continue; + cutmax = MAX(cutmax,tables[tabindex[ii][jj]].cut); + } + } + + return cutmax; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairEAMFS::init_style() +{ + // set communication sizes in comm class + + comm->maxforward_pair = MAX(comm->maxforward_pair,1); + comm->maxreverse_pair = MAX(comm->maxreverse_pair,1); + + // copy read-in-tables to multi-type setfl format + // interpolate final spline coeffs + + store_setfl(); + interpolate(); + + cutforcesq = cutmax*cutmax; +} + +/* ---------------------------------------------------------------------- + read ith,jth potential values from a multi-element alloy EAM/FS file + read values into table and bcast values + this file has different format than standard EAM setfl file +------------------------------------------------------------------------- */ + +int PairEAMFS::read_setfl(char *file, int ith, int jth) +{ + // check if ith,jth portion of same file has already been read + // if yes, return index of table entry + // if no, extend table list + + for (int i = 0; i < ntables; i++) + if (ith == tables[i].ith && jth == tables[i].jth) return i; + + tables = (Table *) + memory->srealloc(tables,(ntables+1)*sizeof(Table),"pair:tables"); + + Table *tb = &tables[ntables]; + int n = strlen(file) + 1; + tb->filename = new char[n]; + strcpy(tb->filename,file); + tb->ith = ith; + tb->jth = jth; + + // open potential file + + int me = comm->me; + FILE *fp; + char line[MAXLINE]; + + if (me == 0) { + fp = fopen(file,"r"); + if (fp == NULL) { + char str[128]; + sprintf(str,"Cannot open EAM potential file %s",file); + error->one(str); + } + } + + // read and broadcast header + + int ntypes; + if (me == 0) { + fgets(line,MAXLINE,fp); + fgets(line,MAXLINE,fp); + fgets(line,MAXLINE,fp); + fgets(line,MAXLINE,fp); + sscanf(line,"%d",&ntypes); + fgets(line,MAXLINE,fp); + sscanf(line,"%d %lg %d %lg %lg", + &tb->nrho,&tb->drho,&tb->nr,&tb->dr,&tb->cut); + } + + MPI_Bcast(&ntypes,1,MPI_INT,0,world); + MPI_Bcast(&tb->nrho,1,MPI_INT,0,world); + MPI_Bcast(&tb->drho,1,MPI_DOUBLE,0,world); + MPI_Bcast(&tb->nr,1,MPI_INT,0,world); + MPI_Bcast(&tb->dr,1,MPI_DOUBLE,0,world); + MPI_Bcast(&tb->cut,1,MPI_DOUBLE,0,world); + + // check if ith,jth are consistent with ntypes + + if (ith > ntypes || jth > ntypes) + error->all("Requested atom types in EAM setfl file do not exist"); + + // allocate potential arrays and read/bcast them + // skip sections of file that don't correspond to ith,jth + // extract mass, frho for i,i from ith element section + // extract rhor for i,j from jth array of ith element section + // extract z2r for i,j from ith,jth array of z2r section + // note that ith can be < or > than jth + // set zr to NULL (funcl array) so it can be deallocated + + tb->frho = new double[tb->nrho+1]; + tb->rhor = new double[tb->nr+1]; + tb->z2r = new double[tb->nr+1]; + tb->zr = NULL; + + int i,j,tmp; + double mass; + + for (i = 1; i <= ntypes; i++) { + if (me == 0) { + fgets(line,MAXLINE,fp); + sscanf(line,"%d %lg",&tmp,&mass); + } + MPI_Bcast(&mass,1,MPI_DOUBLE,0,world); + + if (i == ith && ith == jth) { + tb->mass = mass; + if (me == 0) grab(fp,tb->nrho,&tb->frho[1]); + MPI_Bcast(&tb->frho[1],tb->nrho,MPI_DOUBLE,0,world); + + for (j = 1; j <= ntypes; j++) { + if (j == jth) { + if (me == 0) grab(fp,tb->nr,&tb->rhor[1]); + MPI_Bcast(&tb->rhor[1],tb->nr,MPI_DOUBLE,0,world); + } else if (me == 0) skip(fp,tb->nr); + } + + } else if (i == ith && ith != jth) { + if (me == 0) skip(fp,tb->nrho); + for (j = 1; j <= ntypes; j++) { + if (j == jth) { + if (me == 0) grab(fp,tb->nr,&tb->rhor[1]); + MPI_Bcast(&tb->rhor[1],tb->nr,MPI_DOUBLE,0,world); + } else if (me == 0) skip(fp,tb->nr); + } + + } else { + if (me == 0) skip(fp,tb->nrho); + if (me == 0) for (j = 1; j <= ntypes; j = j + 1) skip(fp,tb->nr); + } + } + + for (i = 1; i <= ntypes; i++) { + for (j = 1; j <= i; j++) { + if ((i == ith && j == jth) || (j == ith && i == jth)) { + if (me == 0) grab(fp,tb->nr,&tb->z2r[1]); + MPI_Bcast(&tb->z2r[1],tb->nr,MPI_DOUBLE,0,world); + } else if (me == 0) skip(fp,tb->nr); + } + } + + // close the potential file + + if (me == 0) fclose(fp); + + ntables++; + return ntables-1; +} + +/* ---------------------------------------------------------------------- + store read-in setfl values in multi-type setfl format +------------------------------------------------------------------------- */ + +void PairEAMFS::store_setfl() +{ + int i,j,m; + + int ntypes = atom->ntypes; + + // set nrho,nr,drho,dr from any i,i table entry since all the same + + for (i = 1; i <= ntypes; i++) + if (setflag[i][i]) break; + + nrho = tables[tabindex[i][i]].nrho; + nr = tables[tabindex[i][i]].nr; + drho = tables[tabindex[i][i]].drho; + dr = tables[tabindex[i][i]].dr; + + // allocate multi-type setfl arrays + + if (frho) { + memory->destroy_2d_double_array(frho); + memory->destroy_3d_double_array(rhor_fs); + memory->destroy_2d_double_array(zrtmp); + memory->destroy_3d_double_array(z2r); + } + + frho = (double **) + memory->create_2d_double_array(ntypes+1,nrho+1,"eam/fs:frho"); + rhor_fs = (double ***) + memory->create_3d_double_array(ntypes+1,ntypes+1,nr+1,"eam/fs:rhor_fs"); + zrtmp = (double **) + memory->create_2d_double_array(ntypes+1,nr+1,"eam/fs:zrtmp"); + z2r = (double ***) + memory->create_3d_double_array(ntypes+1,ntypes+1,nr+1,"eam/fs:frho"); + + // copy from read-in tables to multi-type setfl arrays + // frho,rhor are 1:ntypes, z2r is 1:ntypes,1:ntypes + // skip if setflag i,j = 0 (if hybrid, some may not be set) + + for (i = 1; i <= ntypes; i++) + for (j = 1; j <= ntypes; j++) { + if (setflag[i][j] == 0) continue; + Table *tb = &tables[tabindex[i][j]]; + if (i == j) for (m = 1; m <= nrho; m++) frho[i][m] = tb->frho[m]; + for (m = 1; m <= nr; m++) { + rhor_fs[i][j][m] = tb->rhor[m]; + z2r[i][j][m] = tb->z2r[m]; + } + } +} + +/* ---------------------------------------------------------------------- + interpolate EAM potentials +------------------------------------------------------------------------- */ + +void PairEAMFS::interpolate() +{ + // free memory from previous interpolation + + if (frho_0) interpolate_deallocate(); + + // interpolation spacings + + rdr = 1.0/dr; + rdrho = 1.0/drho; + + // allocate coeff arrays + + int n = atom->ntypes; + + frho_0 = memory->create_2d_double_array(n+1,nrho+1,"eam/fs:frho_0"); + frho_1 = memory->create_2d_double_array(n+1,nrho+1,"eam/fs:frho_1"); + frho_2 = memory->create_2d_double_array(n+1,nrho+1,"eam/fs:frho_2"); + frho_3 = memory->create_2d_double_array(n+1,nrho+1,"eam/fs:frho_3"); + frho_4 = memory->create_2d_double_array(n+1,nrho+1,"eam/fs:frho_4"); + frho_5 = memory->create_2d_double_array(n+1,nrho+1,"eam/fs:frho_5"); + frho_6 = memory->create_2d_double_array(n+1,nrho+1,"eam/fs:frho_6"); + + rhor_fs_0 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:rhor_fs_0"); + rhor_fs_1 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:rhor_fs_1"); + rhor_fs_2 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:rhor_fs_2"); + rhor_fs_3 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:rhor_fs_3"); + rhor_fs_4 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:rhor_fs_4"); + rhor_fs_5 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:rhor_fs_5"); + rhor_fs_6 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:rhor_fs_6"); + + z2r_0 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:z2r_0"); + z2r_1 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:z2r_1"); + z2r_2 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:z2r_2"); + z2r_3 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:z2r_3"); + z2r_4 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:z2r_4"); + z2r_5 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:z2r_5"); + z2r_6 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:z2r_6"); + + // frho interpolation for 1:ntypes + // skip if setflag = 0 (if hybrid, some may not be set) + // if skip, set frho arrays to 0.0, since they will still be accessed + // for non-EAM atoms when compute() calculates embedding function + + int i,j,m; + + for (i = 1; i <= atom->ntypes; i++) { + if (setflag[i][i] == 0) { + for (j = 1; j <= n; j++) + for (m = 1; m <= nrho; m++) + frho_0[j][m] = frho_1[j][m] = frho_2[j][m] = frho_3[j][m] = + frho_4[j][m] = frho_5[j][m] = frho_6[j][m] = 0.0; + continue; + } + + for (m = 1; m <= nrho; m++) frho_0[i][m] = frho[i][m]; + + frho_1[i][1] = frho_0[i][2]-frho_0[i][1]; + frho_1[i][2] = 0.5*(frho_0[i][3]-frho_0[i][1]); + frho_1[i][nrho-1] = 0.5*(frho_0[i][nrho]-frho_0[i][nrho-2]); + frho_1[i][nrho] = frho_0[i][nrho]-frho_0[i][nrho-1]; + + for (m = 3; m <= nrho-2; m++) + frho_1[i][m] = ((frho_0[i][m-2]-frho_0[i][m+2]) + + 8.0*(frho_0[i][m+1]-frho_0[i][m-1]))/12.0; + + for (m = 1; m <= nrho-1; m++) { + frho_2[i][m] = 3.*(frho_0[i][m+1]-frho_0[i][m]) - + 2.0*frho_1[i][m] - frho_1[i][m+1]; + frho_3[i][m] = frho_1[i][m] + frho_1[i][m+1] - + 2.0*(frho_0[i][m+1]-frho_0[i][m]); + } + + frho_2[i][nrho] = 0.0; + frho_3[i][nrho] = 0.0; + + for (m = 1; m <= nrho; m++) { + frho_4[i][m] = frho_1[i][m]/drho; + frho_5[i][m] = 2.0*frho_2[i][m]/drho; + frho_6[i][m] = 3.0*frho_3[i][m]/drho; + } + } + + // rhor interpolation for 1:ntypes + // skip if setflag = 0 (if hybrid, some may not be set) + + for (i = 1; i <= atom->ntypes; i++) { + for (j = 1; j <= atom->ntypes; j++) { + if (setflag[i][j] == 0) continue; + + for (m = 1; m <= nr; m++) rhor_fs_0[i][j][m] = rhor_fs[i][j][m]; + + rhor_fs_1[i][j][1] = rhor_fs_0[i][j][2]-rhor_fs_0[i][j][1]; + rhor_fs_1[i][j][2] = 0.5*(rhor_fs_0[i][j][3]-rhor_fs_0[i][j][1]); + rhor_fs_1[i][j][nr-1] = 0.5*(rhor_fs_0[i][j][nr]-rhor_fs_0[i][j][nr-2]); + rhor_fs_1[i][j][nr] = 0.0; + + for (m = 3; m <= nr-2; m++) + rhor_fs_1[i][j][m] = ((rhor_fs_0[i][j][m-2]-rhor_fs_0[i][j][m+2]) + + 8.0*(rhor_fs_0[i][j][m+1]-rhor_fs_0[i][j][m-1]))/12.; + + for (m = 1; m <= nr-1; m++) { + rhor_fs_2[i][j][m] = 3.0*(rhor_fs_0[i][j][m+1]-rhor_fs_0[i][j][m]) - + 2.0*rhor_fs_1[i][j][m] - rhor_fs_1[i][j][m+1]; + rhor_fs_3[i][j][m] = rhor_fs_1[i][j][m] + rhor_fs_1[i][j][m+1] - + 2.0*(rhor_fs_0[i][j][m+1]-rhor_fs_0[i][j][m]); + } + + rhor_fs_2[i][j][nr] = 0.0; + rhor_fs_3[i][j][nr] = 0.0; + + for (m = 1; m <= nr; m++) { + rhor_fs_4[i][j][m] = rhor_fs_1[i][j][m]/dr; + rhor_fs_5[i][j][m] = 2.0*rhor_fs_2[i][j][m]/dr; + rhor_fs_6[i][j][m] = 3.0*rhor_fs_3[i][j][m]/dr; + } + } + } + + // z2r interpolation for 1:ntypes,1:ntypes + // skip if setflag i,j = 0 (if hybrid, some may not be set) + // set j,i coeffs = i,j coeffs + + for (i = 1; i <= atom->ntypes; i++) { + for (j = i; j <= atom->ntypes; j++) { + if (setflag[i][j] == 0) continue; + + for (m = 1; m <= nr; m++) z2r_0[i][j][m] = z2r[i][j][m]; + + z2r_1[i][j][1] = z2r_0[i][j][2]-z2r_0[i][j][1]; + z2r_1[i][j][2] = 0.5*(z2r_0[i][j][3]-z2r_0[i][j][1]); + z2r_1[i][j][nr-1] = 0.5*(z2r_0[i][j][nr]-z2r_0[i][j][nr-2]); + z2r_1[i][j][nr] = 0.0; + + for (m = 3; m <= nr-2; m++) + z2r_1[i][j][m] = ((z2r_0[i][j][m-2]-z2r_0[i][j][m+2]) + + 8.0*(z2r_0[i][j][m+1]-z2r_0[i][j][m-1]))/12.; + + for (m = 1; m <= nr-1; m++) { + z2r_2[i][j][m] = 3.0*(z2r_0[i][j][m+1]-z2r_0[i][j][m]) - + 2.0*z2r_1[i][j][m] - z2r_1[i][j][m+1]; + z2r_3[i][j][m] = z2r_1[i][j][m] + z2r_1[i][j][m+1] - + 2.0*(z2r_0[i][j][m+1]-z2r_0[i][j][m]); + } + + z2r_2[i][j][nr] = 0.0; + z2r_3[i][j][nr] = 0.0; + + for (m = 1; m <= nr; m++) { + z2r_4[i][j][m] = z2r_1[i][j][m]/dr; + z2r_5[i][j][m] = 2.0*z2r_2[i][j][m]/dr; + z2r_6[i][j][m] = 3.0*z2r_3[i][j][m]/dr; + } + + for (m = 1; m <= nr; m++) { + z2r_0[j][i][m] = z2r_0[i][j][m]; + z2r_1[j][i][m] = z2r_1[i][j][m]; + z2r_2[j][i][m] = z2r_2[i][j][m]; + z2r_3[j][i][m] = z2r_3[i][j][m]; + z2r_4[j][i][m] = z2r_4[i][j][m]; + z2r_5[j][i][m] = z2r_5[i][j][m]; + z2r_6[j][i][m] = z2r_6[i][j][m]; + } + } + } +} + +/* ---------------------------------------------------------------------- + deallocate spline interpolation arrays +------------------------------------------------------------------------- */ + +void PairEAMFS::interpolate_deallocate() +{ + memory->destroy_2d_double_array(frho_0); + memory->destroy_2d_double_array(frho_1); + memory->destroy_2d_double_array(frho_2); + memory->destroy_2d_double_array(frho_3); + memory->destroy_2d_double_array(frho_4); + memory->destroy_2d_double_array(frho_5); + memory->destroy_2d_double_array(frho_6); + + memory->destroy_3d_double_array(rhor_fs_0); + memory->destroy_3d_double_array(rhor_fs_1); + memory->destroy_3d_double_array(rhor_fs_2); + memory->destroy_3d_double_array(rhor_fs_3); + memory->destroy_3d_double_array(rhor_fs_4); + memory->destroy_3d_double_array(rhor_fs_5); + memory->destroy_3d_double_array(rhor_fs_6); + + memory->destroy_3d_double_array(z2r_0); + memory->destroy_3d_double_array(z2r_1); + memory->destroy_3d_double_array(z2r_2); + memory->destroy_3d_double_array(z2r_3); + memory->destroy_3d_double_array(z2r_4); + memory->destroy_3d_double_array(z2r_5); + memory->destroy_3d_double_array(z2r_6); +} + +/* ---------------------------------------------------------------------- */ + +void PairEAMFS::single(int i, int j, int itype, int jtype, + double rsq, double factor_coul, double factor_lj, + int eflag, One &one) +{ + double r,p,rhoip,rhojp,z2,z2p,recip,phi,phip,psip; + int m; + + r = sqrt(rsq); + p = r*rdr + 1.0; + m = static_cast<int> (p); + m = MIN(m,nr-1); + p -= m; + p = MIN(p,1.0); + + rhoip = (rhor_fs_6[itype][jtype][m]*p + rhor_fs_5[itype][jtype][m])*p + + rhor_fs_4[itype][jtype][m]; + rhojp = (rhor_fs_6[jtype][itype][m]*p + rhor_fs_5[jtype][itype][m])*p + + rhor_fs_4[jtype][itype][m]; + z2 = ((z2r_3[itype][jtype][m]*p + z2r_2[itype][jtype][m])*p + + z2r_1[itype][jtype][m])*p + z2r_0[itype][jtype][m]; + z2p = (z2r_6[itype][jtype][m]*p + z2r_5[itype][jtype][m])*p + + z2r_4[itype][jtype][m]; + + recip = 1.0/r; + phi = z2*recip; + phip = z2p*recip - phi*recip; + psip = fp[i]*rhojp + fp[j]*rhoip + phip; + one.fforce = -psip*recip; + + if (eflag) { + one.eng_vdwl = phi; + one.eng_coul = 0.0; + } +} diff --git a/src/MANYBODY/pair_eam_fs.h b/src/MANYBODY/pair_eam_fs.h new file mode 100644 index 0000000000000000000000000000000000000000..1787d1beabf5946c8cdcc3bb4fcb0054d9394b1f --- /dev/null +++ b/src/MANYBODY/pair_eam_fs.h @@ -0,0 +1,40 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_EAM_FS_H +#define PAIR_EAM_FS_H + +#include "pair_eam.h" + +class PairEAMFS : public PairEAM { + public: + PairEAMFS(); + ~PairEAMFS(); + void compute(int, int); + void coeff(int, char **); + double init_one(int, int); + void init_style(); + void single(int, int, int, int, double, double, double, int, One &); + + private: + double ***rhor_fs; + double ***rhor_fs_0,***rhor_fs_1,***rhor_fs_2,***rhor_fs_3; + double ***rhor_fs_4,***rhor_fs_5,***rhor_fs_6; + + int read_setfl(char *, int, int); + void store_setfl(); + void interpolate(); + void interpolate_deallocate(); +}; + +#endif diff --git a/src/MANYBODY/style_manybody.h b/src/MANYBODY/style_manybody.h new file mode 100644 index 0000000000000000000000000000000000000000..659f63ca83f23addc633e3ddc1159f7c910240a0 --- /dev/null +++ b/src/MANYBODY/style_manybody.h @@ -0,0 +1,24 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifdef PairInclude +#include "pair_eam.h" +#include "pair_eam_alloy.h" +#include "pair_eam_fs.h" +#endif + +#ifdef PairClass +PairStyle(eam,PairEAM) +PairStyle(eam/alloy,PairEAMAlloy) +PairStyle(eam/fs,PairEAMFS) +#endif diff --git a/src/MOLECULE/Install.csh b/src/MOLECULE/Install.csh new file mode 100644 index 0000000000000000000000000000000000000000..2e59b8d9f4bfe734f8c8c9a4744e8f1f64f5ec87 --- /dev/null +++ b/src/MOLECULE/Install.csh @@ -0,0 +1,147 @@ +# Install/unInstall package classes in LAMMPS + +# bond.h, angle.h, dihedal.h, improper.h must always be in src +# bond_hybrid.h must always be in src + +if ($1 == 1) then + + cp style_molecule.h .. + + cp angle.cpp .. + cp angle_charmm.cpp .. + cp angle_cosine.cpp .. + cp angle_cosine_squared.cpp .. + cp angle_harmonic.cpp .. + cp angle_hybrid.cpp .. + cp atom_angle.cpp .. + cp atom_bond.cpp .. + cp atom_full.cpp .. + cp atom_molecular.cpp .. + cp bond.cpp .. + cp bond_fene.cpp .. + cp bond_fene_expand.cpp .. + cp bond_harmonic.cpp .. + cp bond_hybrid.cpp .. + cp bond_morse.cpp .. + cp bond_nonlinear.cpp .. + cp bond_quartic.cpp .. + cp dihedral.cpp .. + cp dihedral_charmm.cpp .. + cp dihedral_harmonic.cpp .. + cp dihedral_helix.cpp .. + cp dihedral_hybrid.cpp .. + cp dihedral_multi_harmonic.cpp .. + cp dihedral_opls.cpp .. + cp dump_bond.cpp .. + cp improper.cpp .. + cp improper_cvff.cpp .. + cp improper_harmonic.cpp .. + cp improper_hybrid.cpp .. + cp pair_lj_charmm_coul_charmm.cpp .. + cp pair_lj_charmm_coul_charmm_implicit.cpp .. + +# cp angle.h .. + cp angle_charmm.h .. + cp angle_cosine.h .. + cp angle_cosine_squared.h .. + cp angle_harmonic.h .. + cp angle_hybrid.h .. + cp atom_angle.h .. + cp atom_bond.h .. + cp atom_full.h .. + cp atom_molecular.h .. +# cp bond.h .. + cp bond_fene.h .. + cp bond_fene_expand.h .. + cp bond_harmonic.h .. + cp bond_morse.h .. +# cp bond_hybrid.h .. + cp bond_nonlinear.h .. + cp bond_quartic.h .. +# cp dihedral.h .. + cp dihedral_charmm.h .. + cp dihedral_harmonic.h .. + cp dihedral_helix.h .. + cp dihedral_hybrid.h .. + cp dihedral_multi_harmonic.h .. + cp dihedral_opls.h .. + cp dump_bond.h .. +# cp improper.h .. + cp improper_cvff.h .. + cp improper_harmonic.h .. + cp improper_hybrid.h .. + cp pair_lj_charmm_coul_charmm.h .. + cp pair_lj_charmm_coul_charmm_implicit.h .. + +else if ($1 == 0) then + + rm ../style_molecule.h + touch ../style_molecule.h + + rm ../angle.cpp + rm ../angle_charmm.cpp + rm ../angle_cosine.cpp + rm ../angle_cosine_squared.cpp + rm ../angle_harmonic.cpp + rm ../angle_hybrid.cpp + rm ../atom_angle.cpp + rm ../atom_bond.cpp + rm ../atom_full.cpp + rm ../atom_molecular.cpp + rm ../bond.cpp + rm ../bond_fene.cpp + rm ../bond_fene_expand.cpp + rm ../bond_harmonic.cpp + rm ../bond_hybrid.cpp + rm ../bond_morse.cpp + rm ../bond_nonlinear.cpp + rm ../bond_quartic.cpp + rm ../dihedral.cpp + rm ../dihedral_charmm.cpp + rm ../dihedral_harmonic.cpp + rm ../dihedral_helix.cpp + rm ../dihedral_hybrid.cpp + rm ../dihedral_multi_harmonic.cpp + rm ../dihedral_opls.cpp + rm ../dump_bond.cpp + rm ../improper.cpp + rm ../improper_cvff.cpp + rm ../improper_harmonic.cpp + rm ../improper_hybrid.cpp + rm ../pair_lj_charmm_coul_charmm.cpp + rm ../pair_lj_charmm_coul_charmm_implicit.cpp + +# rm ../angle.h + rm ../angle_charmm.h + rm ../angle_cosine.h + rm ../angle_cosine_squared.h + rm ../angle_harmonic.h + rm ../angle_hybrid.h + rm ../atom_angle.h + rm ../atom_bond.h + rm ../atom_full.h + rm ../atom_molecular.h +# rm ../bond.h + rm ../bond_fene.h + rm ../bond_fene_expand.h + rm ../bond_harmonic.h +# rm ../bond_hybrid.h + rm ../bond_morse.h + rm ../bond_nonlinear.h + rm ../bond_quartic.h +# rm ../dihedral.h + rm ../dihedral_charmm.h + rm ../dihedral_harmonic.h + rm ../dihedral_helix.h + rm ../dihedral_hybrid.h + rm ../dihedral_multi_harmonic.h + rm ../dihedral_opls.h + rm ../dump_bond.h +# rm ../improper.h + rm ../improper_cvff.h + rm ../improper_harmonic.h + rm ../improper_hybrid.h + rm ../pair_lj_charmm_coul_charmm.h + rm ../pair_lj_charmm_coul_charmm_implicit.h + +endif diff --git a/src/MOLECULE/angle.cpp b/src/MOLECULE/angle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3b689700f9a124496d374bc2a288e0407f7d7b72 --- /dev/null +++ b/src/MOLECULE/angle.cpp @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "angle.h" +#include "atom.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +Angle::Angle() +{ + allocated = 0; + PI = 4.0*atan(1.0); +} + +/* ---------------------------------------------------------------------- + check if all coeffs are set +------------------------------------------------------------------------- */ + +void Angle::init() +{ + if (!allocated) error->all("Angle coeffs are not set"); + for (int i = 1; i <= atom->nangletypes; i++) + if (setflag[i] == 0) error->all("All angle coeffs are not set"); +} + diff --git a/src/MOLECULE/angle_charmm.cpp b/src/MOLECULE/angle_charmm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e23cd9684a08614915e031e41e6664ae44b10a81 --- /dev/null +++ b/src/MOLECULE/angle_charmm.cpp @@ -0,0 +1,310 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Paul Crozier (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "angle_charmm.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +AngleCharmm::~AngleCharmm() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(theta0); + memory->sfree(k_ub); + memory->sfree(r_ub); + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleCharmm::compute(int eflag, int vflag) +{ + int i1,i2,i3,n,type,factor; + double delx1,dely1,delz1,delx2,dely2,delz2,rfactor,dtheta,tk; + double rsq1,rsq2,r1,r2,c,s,a,a11,a12,a22,vx1,vx2,vy1,vy2,vz1,vz2; + double delxUB,delyUB,delzUB,rsqUB,rUB,dr,rk,forceUB; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **anglelist = neighbor->anglelist; + int nanglelist = neighbor->nanglelist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nanglelist; n++) { + + i1 = anglelist[n][0]; + i2 = anglelist[n][1]; + i3 = anglelist[n][2]; + type = anglelist[n][3]; + + if (newton_bond) factor = 3; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + } + rfactor = factor/3.0; + + // 1st bond + + delx1 = x[i1][0] - x[i2][0]; + dely1 = x[i1][1] - x[i2][1]; + delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + + rsq1 = delx1*delx1 + dely1*dely1 + delz1*delz1; + r1 = sqrt(rsq1); + + // 2nd bond + + delx2 = x[i3][0] - x[i2][0]; + dely2 = x[i3][1] - x[i2][1]; + delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + + rsq2 = delx2*delx2 + dely2*dely2 + delz2*delz2; + r2 = sqrt(rsq2); + + // Urey-Bradley bond + + delxUB = x[i3][0] - x[i1][0]; + delyUB = x[i3][1] - x[i1][1]; + delzUB = x[i3][2] - x[i1][2]; + domain->minimum_image(&delxUB,&delyUB,&delzUB); + + rsqUB = delxUB*delxUB + delyUB*delyUB + delzUB*delzUB; + rUB = sqrt(rsqUB); + + // angle (cos and sin) + + c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + s = sqrt(1.0 - c*c); + if (s < SMALL) s = SMALL; + s = 1.0/s; + + // harmonic force & energy + + dtheta = acos(c) - theta0[type]; + tk = k[type] * dtheta; + + if (eflag) energy += rfactor * tk*dtheta; + + a = 2.0 * tk * s; + + a11 = a*c / rsq1; + a12 = -a / (r1*r2); + a22 = a*c / rsq2; + + vx1 = a11*delx1 + a12*delx2; + vx2 = a22*delx2 + a12*delx1; + vy1 = a11*dely1 + a12*dely2; + vy2 = a22*dely2 + a12*dely1; + vz1 = a11*delz1 + a12*delz2; + vz2 = a22*delz2 + a12*delz1; + + // Urey-Bradley force & energy + + dr = rUB - r_ub[type]; + rk = k_ub[type] * dr; + + if (rUB > 0.0) forceUB = -2.0*rk/rUB; + else forceUB = 0.0; + + if (eflag) energy += rfactor * rk*dr; + + // apply force to each of 3 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= vx1 + delxUB*forceUB; + f[i1][1] -= vy1 + delyUB*forceUB; + f[i1][2] -= vz1 + delzUB*forceUB; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += vx1 + vx2; + f[i2][1] += vy1 + vy2; + f[i2][2] += vz1 + vz2; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] -= vx2 - delxUB*forceUB; + f[i3][1] -= vy2 - delyUB*forceUB; + f[i3][2] -= vz2 - delzUB*forceUB; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (delx1*vx1 + delx2*vx2 - delxUB*delxUB*forceUB); + virial[1] -= rfactor * (dely1*vy1 + dely2*vy2 - delyUB*delyUB*forceUB); + virial[2] -= rfactor * (delz1*vz1 + delz2*vz2 - delzUB*delzUB*forceUB); + virial[3] -= rfactor * (delx1*vy1 + delx2*vy2 - delxUB*delyUB*forceUB); + virial[4] -= rfactor * (delx1*vz1 + delx2*vz2 - delxUB*delzUB*forceUB); + virial[5] -= rfactor * (dely1*vz1 + dely2*vz2 - delyUB*delzUB*forceUB); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleCharmm::allocate() +{ + allocated = 1; + int n = atom->nangletypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"angle:k"); + theta0 = (double *) memory->smalloc((n+1)*sizeof(double),"angle:theta0"); + k_ub = (double *) memory->smalloc((n+1)*sizeof(double),"angle:k_ub"); + r_ub = (double *) memory->smalloc((n+1)*sizeof(double),"angle:r_ub"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"angle:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void AngleCharmm::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this angle style"); + if (narg != 5) error->all("Incorrect args for angle coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nangletypes,ilo,ihi); + + double k_one = atof(arg[1]); + double theta0_one = atof(arg[2]); + double k_ub_one = atof(arg[3]); + double r_ub_one = atof(arg[4]); + + // convert theta0 from degrees to radians + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + theta0[i] = theta0_one/180.0 * PI; + k_ub[i] = k_ub_one; + r_ub[i] = r_ub_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for angle coefficients"); +} + +/* ---------------------------------------------------------------------- */ + +double AngleCharmm::equilibrium_angle(int i) +{ + return theta0[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void AngleCharmm::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nangletypes,fp); + fwrite(&theta0[1],sizeof(double),atom->nangletypes,fp); + fwrite(&k_ub[1],sizeof(double),atom->nangletypes,fp); + fwrite(&r_ub[1],sizeof(double),atom->nangletypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void AngleCharmm::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->nangletypes,fp); + fread(&theta0[1],sizeof(double),atom->nangletypes,fp); + fread(&k_ub[1],sizeof(double),atom->nangletypes,fp); + fread(&r_ub[1],sizeof(double),atom->nangletypes,fp); + } + MPI_Bcast(&k[1],atom->nangletypes,MPI_DOUBLE,0,world); + MPI_Bcast(&theta0[1],atom->nangletypes,MPI_DOUBLE,0,world); + MPI_Bcast(&k_ub[1],atom->nangletypes,MPI_DOUBLE,0,world); + MPI_Bcast(&r_ub[1],atom->nangletypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nangletypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +double AngleCharmm::single(int type, int i1, int i2, int i3, double rfactor) +{ + double **x = atom->x; + + double delx1 = x[i1][0] - x[i2][0]; + double dely1 = x[i1][1] - x[i2][1]; + double delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + double r1 = sqrt(delx1*delx1 + dely1*dely1 + delz1*delz1); + + double delx2 = x[i3][0] - x[i2][0]; + double dely2 = x[i3][1] - x[i2][1]; + double delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + double r2 = sqrt(delx2*delx2 + dely2*dely2 + delz2*delz2); + + double delxUB = x[i3][0] - x[i1][0]; + double delyUB = x[i3][1] - x[i1][1]; + double delzUB = x[i3][2] - x[i1][2]; + domain->minimum_image(&delxUB,&delyUB,&delzUB); + double rUB = sqrt(delxUB*delxUB + delyUB*delyUB + delzUB*delzUB); + + double c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + double dtheta = acos(c) - theta0[type]; + double tk = k[type] * dtheta; + double dr = rUB - r_ub[type]; + double rk = k_ub[type] * dr; + + return (rfactor * (tk*dtheta + rk*dr)); +} diff --git a/src/MOLECULE/angle_charmm.h b/src/MOLECULE/angle_charmm.h new file mode 100644 index 0000000000000000000000000000000000000000..278e8e2493da7e84245a38216d85b5983dba25a4 --- /dev/null +++ b/src/MOLECULE/angle_charmm.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ANGLE_CHARMM_H +#define ANGLE_CHARMM_H + +#include "stdio.h" +#include "angle.h" + +class AngleCharmm : public Angle { + public: + AngleCharmm() {} + ~AngleCharmm(); + void compute(int, int); + void coeff(int, int, char **); + double equilibrium_angle(int); + void write_restart(FILE *); + void read_restart(FILE *); + double single(int, int, int, int, double); + + private: + double *k,*theta0,*k_ub,*r_ub; + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/angle_cosine.cpp b/src/MOLECULE/angle_cosine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6f33f0e7bac712619007137b8d4c120d323b827a --- /dev/null +++ b/src/MOLECULE/angle_cosine.cpp @@ -0,0 +1,241 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "angle_cosine.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +AngleCosine::~AngleCosine() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleCosine::compute(int eflag, int vflag) +{ + int i1,i2,i3,n,type,factor; + double delx1,dely1,delz1,delx2,dely2,delz2,rfactor; + double rsq1,rsq2,r1,r2,c,a,a11,a12,a22,vx1,vx2,vy1,vy2,vz1,vz2; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **anglelist = neighbor->anglelist; + int nanglelist = neighbor->nanglelist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nanglelist; n++) { + + i1 = anglelist[n][0]; + i2 = anglelist[n][1]; + i3 = anglelist[n][2]; + type = anglelist[n][3]; + + if (newton_bond) factor = 3; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + } + rfactor = factor/3.0; + + // 1st bond + + delx1 = x[i1][0] - x[i2][0]; + dely1 = x[i1][1] - x[i2][1]; + delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + + rsq1 = delx1*delx1 + dely1*dely1 + delz1*delz1; + r1 = sqrt(rsq1); + + // 2nd bond + + delx2 = x[i3][0] - x[i2][0]; + dely2 = x[i3][1] - x[i2][1]; + delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + + rsq2 = delx2*delx2 + dely2*dely2 + delz2*delz2; + r2 = sqrt(rsq2); + + // c = cosine of angle + + c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + // force & energy + + if (eflag) energy += rfactor * k[type]*(1.0+c); + + a = -k[type]; + + a11 = a*c / rsq1; + a12 = -a / (r1*r2); + a22 = a*c / rsq2; + + vx1 = a11*delx1 + a12*delx2; + vx2 = a22*delx2 + a12*delx1; + vy1 = a11*dely1 + a12*dely2; + vy2 = a22*dely2 + a12*dely1; + vz1 = a11*delz1 + a12*delz2; + vz2 = a22*delz2 + a12*delz1; + + // apply force to each of 3 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= vx1; + f[i1][1] -= vy1; + f[i1][2] -= vz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += vx1 + vx2; + f[i2][1] += vy1 + vy2; + f[i2][2] += vz1 + vz2; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] -= vx2; + f[i3][1] -= vy2; + f[i3][2] -= vz2; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (delx1*vx1 + delx2*vx2); + virial[1] -= rfactor * (dely1*vy1 + dely2*vy2); + virial[2] -= rfactor * (delz1*vz1 + delz2*vz2); + virial[3] -= rfactor * (delx1*vy1 + delx2*vy2); + virial[4] -= rfactor * (delx1*vz1 + delx2*vz2); + virial[5] -= rfactor * (dely1*vz1 + dely2*vz2); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleCosine::allocate() +{ + allocated = 1; + int n = atom->nangletypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"angle:k"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"angle:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void AngleCosine::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this angle style"); + if (narg != 2) error->all("Incorrect args for angle coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nangletypes,ilo,ihi); + + double k_one = atof(arg[1]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for angle coefficients"); +} + +/* ---------------------------------------------------------------------- */ + +double AngleCosine::equilibrium_angle(int i) +{ + return PI; +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void AngleCosine::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nangletypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void AngleCosine::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) fread(&k[1],sizeof(double),atom->nangletypes,fp); + MPI_Bcast(&k[1],atom->nangletypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nangletypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +double AngleCosine::single(int type, int i1, int i2, int i3, double rfactor) +{ + double **x = atom->x; + + double delx1 = x[i1][0] - x[i2][0]; + double dely1 = x[i1][1] - x[i2][1]; + double delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + double r1 = sqrt(delx1*delx1 + dely1*dely1 + delz1*delz1); + + double delx2 = x[i3][0] - x[i2][0]; + double dely2 = x[i3][1] - x[i2][1]; + double delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + double r2 = sqrt(delx2*delx2 + dely2*dely2 + delz2*delz2); + + double c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + return (rfactor * k[type]*(1.0+c)); +} diff --git a/src/MOLECULE/angle_cosine.h b/src/MOLECULE/angle_cosine.h new file mode 100644 index 0000000000000000000000000000000000000000..6adf7e684662668dbc8d9c5f498b3879d28c29b6 --- /dev/null +++ b/src/MOLECULE/angle_cosine.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ANGLE_COSINE_H +#define ANGLE_COSINE_H + +#include "stdio.h" +#include "angle.h" + +class AngleCosine : public Angle { + public: + AngleCosine() {} + ~AngleCosine(); + void compute(int, int); + void coeff(int, int, char **); + double equilibrium_angle(int); + void write_restart(FILE *); + void read_restart(FILE *); + double single(int, int, int, int, double); + + private: + double *k; + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/angle_cosine_squared.cpp b/src/MOLECULE/angle_cosine_squared.cpp new file mode 100644 index 0000000000000000000000000000000000000000..581204cee44a181034ef6adfd01aea8091540e88 --- /dev/null +++ b/src/MOLECULE/angle_cosine_squared.cpp @@ -0,0 +1,264 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Naveen Michaud-Agrawal (Johns Hopkins U) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "angle_cosine_squared.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +AngleCosineSquared::~AngleCosineSquared() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(theta0); + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleCosineSquared::compute(int eflag, int vflag) +{ + int i1,i2,i3,n,type,factor; + double delx1,dely1,delz1,delx2,dely2,delz2,rfactor,dcostheta,tk; + double rsq1,rsq2,r1,r2,c,a,a11,a12,a22,vx1,vx2,vy1,vy2,vz1,vz2; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **anglelist = neighbor->anglelist; + int nanglelist = neighbor->nanglelist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nanglelist; n++) { + + i1 = anglelist[n][0]; + i2 = anglelist[n][1]; + i3 = anglelist[n][2]; + type = anglelist[n][3]; + + if (newton_bond) factor = 3; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + } + rfactor = factor/3.0; + + // 1st bond + + delx1 = x[i1][0] - x[i2][0]; + dely1 = x[i1][1] - x[i2][1]; + delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + + rsq1 = delx1*delx1 + dely1*dely1 + delz1*delz1; + r1 = sqrt(rsq1); + + // 2nd bond + + delx2 = x[i3][0] - x[i2][0]; + dely2 = x[i3][1] - x[i2][1]; + delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + + rsq2 = delx2*delx2 + dely2*dely2 + delz2*delz2; + r2 = sqrt(rsq2); + + // angle (cos and sin) + + c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + // force & energy + + dcostheta = c - cos(theta0[type]); + tk = k[type] * dcostheta; + + if (eflag) energy += rfactor * tk*dcostheta; + + a = -2.0 * tk; + + a11 = a*c / rsq1; + a12 = -a / (r1*r2); + a22 = a*c / rsq2; + + vx1 = a11*delx1 + a12*delx2; + vx2 = a22*delx2 + a12*delx1; + vy1 = a11*dely1 + a12*dely2; + vy2 = a22*dely2 + a12*dely1; + vz1 = a11*delz1 + a12*delz2; + vz2 = a22*delz2 + a12*delz1; + + // apply force to each of 3 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= vx1; + f[i1][1] -= vy1; + f[i1][2] -= vz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += vx1 + vx2; + f[i2][1] += vy1 + vy2; + f[i2][2] += vz1 + vz2; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] -= vx2; + f[i3][1] -= vy2; + f[i3][2] -= vz2; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (delx1*vx1 + delx2*vx2); + virial[1] -= rfactor * (dely1*vy1 + dely2*vy2); + virial[2] -= rfactor * (delz1*vz1 + delz2*vz2); + virial[3] -= rfactor * (delx1*vy1 + delx2*vy2); + virial[4] -= rfactor * (delx1*vz1 + delx2*vz2); + virial[5] -= rfactor * (dely1*vz1 + dely2*vz2); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleCosineSquared::allocate() +{ + allocated = 1; + int n = atom->nangletypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"angle:k"); + theta0 = (double *) memory->smalloc((n+1)*sizeof(double),"angle:theta0"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"angle:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more types +------------------------------------------------------------------------- */ + +void AngleCosineSquared::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this angle style"); + if (narg != 3) error->all("Incorrect args for angle coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nangletypes,ilo,ihi); + + double k_one = atof(arg[1]); + double theta0_one = atof(arg[2]); + + // convert theta0 from degrees to radians + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + theta0[i] = theta0_one/180.0 * PI; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for angle coefficients"); +} + +/* ---------------------------------------------------------------------- */ + +double AngleCosineSquared::equilibrium_angle(int i) +{ + return theta0[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void AngleCosineSquared::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nangletypes,fp); + fwrite(&theta0[1],sizeof(double),atom->nangletypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void AngleCosineSquared::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->nangletypes,fp); + fread(&theta0[1],sizeof(double),atom->nangletypes,fp); + } + MPI_Bcast(&k[1],atom->nangletypes,MPI_DOUBLE,0,world); + MPI_Bcast(&theta0[1],atom->nangletypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nangletypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +double AngleCosineSquared::single(int type, int i1, int i2, int i3, + double rfactor) +{ + double **x = atom->x; + + double delx1 = x[i1][0] - x[i2][0]; + double dely1 = x[i1][1] - x[i2][1]; + double delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + double r1 = sqrt(delx1*delx1 + dely1*dely1 + delz1*delz1); + + double delx2 = x[i3][0] - x[i2][0]; + double dely2 = x[i3][1] - x[i2][1]; + double delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + double r2 = sqrt(delx2*delx2 + dely2*dely2 + delz2*delz2); + + double c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + double dcostheta = c - cos(theta0[type]); + double tk = k[type] * dcostheta; + return (rfactor * tk*dcostheta); +} diff --git a/src/MOLECULE/angle_cosine_squared.h b/src/MOLECULE/angle_cosine_squared.h new file mode 100644 index 0000000000000000000000000000000000000000..cc7ce9b14f42d4a93178091f6a593be2f5c3b19c --- /dev/null +++ b/src/MOLECULE/angle_cosine_squared.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ANGLE_COSINE_SQUARED_H +#define ANGLE_COSINE_SQUARED_H + +#include "stdio.h" +#include "angle.h" + +class AngleCosineSquared : public Angle { + public: + AngleCosineSquared() {} + ~AngleCosineSquared(); + void compute(int, int); + void coeff(int, int, char **); + double equilibrium_angle(int); + void write_restart(FILE *); + void read_restart(FILE *); + double single(int, int, int, int, double); + + private: + double *k,*theta0; + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/angle_harmonic.cpp b/src/MOLECULE/angle_harmonic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b9b2a9ccf2b27c4994594751fc92f4ca18a68ef --- /dev/null +++ b/src/MOLECULE/angle_harmonic.cpp @@ -0,0 +1,263 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "angle_harmonic.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +AngleHarmonic::~AngleHarmonic() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(theta0); + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleHarmonic::compute(int eflag, int vflag) +{ + int i1,i2,i3,n,type,factor; + double delx1,dely1,delz1,delx2,dely2,delz2,rfactor,dtheta,tk; + double rsq1,rsq2,r1,r2,c,s,a,a11,a12,a22,vx1,vx2,vy1,vy2,vz1,vz2; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **anglelist = neighbor->anglelist; + int nanglelist = neighbor->nanglelist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nanglelist; n++) { + + i1 = anglelist[n][0]; + i2 = anglelist[n][1]; + i3 = anglelist[n][2]; + type = anglelist[n][3]; + + if (newton_bond) factor = 3; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + } + rfactor = factor/3.0; + + // 1st bond + + delx1 = x[i1][0] - x[i2][0]; + dely1 = x[i1][1] - x[i2][1]; + delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + + rsq1 = delx1*delx1 + dely1*dely1 + delz1*delz1; + r1 = sqrt(rsq1); + + // 2nd bond + + delx2 = x[i3][0] - x[i2][0]; + dely2 = x[i3][1] - x[i2][1]; + delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + + rsq2 = delx2*delx2 + dely2*dely2 + delz2*delz2; + r2 = sqrt(rsq2); + + // angle (cos and sin) + + c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + s = sqrt(1.0 - c*c); + if (s < SMALL) s = SMALL; + s = 1.0/s; + + // force & energy + + dtheta = acos(c) - theta0[type]; + tk = k[type] * dtheta; + + if (eflag) energy += rfactor * tk*dtheta; + + a = 2.0 * tk * s; + + a11 = a*c / rsq1; + a12 = -a / (r1*r2); + a22 = a*c / rsq2; + + vx1 = a11*delx1 + a12*delx2; + vx2 = a22*delx2 + a12*delx1; + vy1 = a11*dely1 + a12*dely2; + vy2 = a22*dely2 + a12*dely1; + vz1 = a11*delz1 + a12*delz2; + vz2 = a22*delz2 + a12*delz1; + + // apply force to each of 3 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= vx1; + f[i1][1] -= vy1; + f[i1][2] -= vz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += vx1 + vx2; + f[i2][1] += vy1 + vy2; + f[i2][2] += vz1 + vz2; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] -= vx2; + f[i3][1] -= vy2; + f[i3][2] -= vz2; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (delx1*vx1 + delx2*vx2); + virial[1] -= rfactor * (dely1*vy1 + dely2*vy2); + virial[2] -= rfactor * (delz1*vz1 + delz2*vz2); + virial[3] -= rfactor * (delx1*vy1 + delx2*vy2); + virial[4] -= rfactor * (delx1*vz1 + delx2*vz2); + virial[5] -= rfactor * (dely1*vz1 + dely2*vz2); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleHarmonic::allocate() +{ + allocated = 1; + int n = atom->nangletypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"angle:k"); + theta0 = (double *) memory->smalloc((n+1)*sizeof(double),"angle:theta0"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"angle:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more types +------------------------------------------------------------------------- */ + +void AngleHarmonic::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this angle style"); + if (narg != 3) error->all("Incorrect args for angle coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nangletypes,ilo,ihi); + + double k_one = atof(arg[1]); + double theta0_one = atof(arg[2]); + + // convert theta0 from degrees to radians + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + theta0[i] = theta0_one/180.0 * PI; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for angle coefficients"); +} + +/* ---------------------------------------------------------------------- */ + +double AngleHarmonic::equilibrium_angle(int i) +{ + return theta0[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void AngleHarmonic::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nangletypes,fp); + fwrite(&theta0[1],sizeof(double),atom->nangletypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void AngleHarmonic::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->nangletypes,fp); + fread(&theta0[1],sizeof(double),atom->nangletypes,fp); + } + MPI_Bcast(&k[1],atom->nangletypes,MPI_DOUBLE,0,world); + MPI_Bcast(&theta0[1],atom->nangletypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nangletypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +double AngleHarmonic::single(int type, int i1, int i2, int i3, double rfactor) +{ + double **x = atom->x; + + double delx1 = x[i1][0] - x[i2][0]; + double dely1 = x[i1][1] - x[i2][1]; + double delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + double r1 = sqrt(delx1*delx1 + dely1*dely1 + delz1*delz1); + + double delx2 = x[i3][0] - x[i2][0]; + double dely2 = x[i3][1] - x[i2][1]; + double delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + double r2 = sqrt(delx2*delx2 + dely2*dely2 + delz2*delz2); + + double c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + double dtheta = acos(c) - theta0[type]; + double tk = k[type] * dtheta; + return (rfactor * tk*dtheta); +} diff --git a/src/MOLECULE/angle_harmonic.h b/src/MOLECULE/angle_harmonic.h new file mode 100644 index 0000000000000000000000000000000000000000..8b097f9e1af2214a08ea72ffc1e1567b4560476d --- /dev/null +++ b/src/MOLECULE/angle_harmonic.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ANGLE_HARMONIC_H +#define ANGLE_HARMONIC_H + +#include "stdio.h" +#include "angle.h" + +class AngleHarmonic : public Angle { + public: + AngleHarmonic() {} + ~AngleHarmonic(); + void compute(int, int); + void coeff(int, int, char **); + double equilibrium_angle(int); + void write_restart(FILE *); + void read_restart(FILE *); + double single(int, int, int, int, double); + + private: + double *k,*theta0; + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/angle_hybrid.cpp b/src/MOLECULE/angle_hybrid.cpp new file mode 100644 index 0000000000000000000000000000000000000000..12d7a0093e9cfe28200b0adfbbebb5201537ce5b --- /dev/null +++ b/src/MOLECULE/angle_hybrid.cpp @@ -0,0 +1,262 @@ +/* ----------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "string.h" +#include "angle_hybrid.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define EXTRA 1000 + +/* ---------------------------------------------------------------------- + set all global defaults +------------------------------------------------------------------------- */ + +AngleHybrid::AngleHybrid() +{ + nstyles = 0; +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +AngleHybrid::~AngleHybrid() +{ + if (nstyles) { + for (int i = 0; i < nstyles; i++) delete styles[i]; + delete [] styles; + for (int i = 0; i < nstyles; i++) delete [] keywords[i]; + delete [] keywords; + } + + if (allocated) { + memory->sfree(setflag); + memory->sfree(map); + delete [] nanglelist; + delete [] maxangle; + for (int i = 0; i < nstyles; i++) + memory->destroy_2d_int_array(anglelist[i]); + delete [] anglelist; + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleHybrid::compute(int eflag, int vflag) +{ + int i,m,n; + + // save ptrs to original anglelist + + int nanglelist_orig = neighbor->nanglelist; + int **anglelist_orig = neighbor->anglelist; + + // if this is re-neighbor step, create sub-style anglelists + // nanglelist[] = length of each sub-style list + // realloc sub-style anglelist if necessary + // load sub-style anglelist with 4 values from original anglelist + + if (neighbor->ago == 0) { + for (m = 0; m < nstyles; m++) nanglelist[m] = 0; + for (i = 0; i < nanglelist_orig; i++) + nanglelist[map[anglelist_orig[i][3]]]++; + for (m = 0; m < nstyles; m++) { + if (nanglelist[m] > maxangle[m]) { + memory->destroy_2d_int_array(anglelist[m]); + maxangle[m] = nanglelist[m] + EXTRA; + anglelist[m] = (int **) + memory->create_2d_int_array(maxangle[m],4,"angle_hybrid:anglelist"); + } + nanglelist[m] = 0; + } + for (i = 0; i < nanglelist_orig; i++) { + m = map[anglelist_orig[i][3]]; + n = nanglelist[m]; + anglelist[m][n][0] = anglelist_orig[i][0]; + anglelist[m][n][1] = anglelist_orig[i][1]; + anglelist[m][n][2] = anglelist_orig[i][2]; + anglelist[m][n][3] = anglelist_orig[i][3]; + nanglelist[m]++; + } + } + + // call each sub-style's compute function + // must set neighbor->anglelist to sub-style anglelist before call + // accumulate sub-style energy,virial in hybrid's energy,virial + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + for (m = 0; m < nstyles; m++) { + if (styles[m] == NULL) continue; + neighbor->nanglelist = nanglelist[m]; + neighbor->anglelist = anglelist[m]; + styles[m]->compute(eflag,vflag); + if (eflag) energy += styles[m]->energy; + if (vflag) for (n = 0; n < 6; n++) virial[n] += styles[m]->virial[n]; + } + + // restore ptrs to original anglelist + + neighbor->nanglelist = nanglelist_orig; + neighbor->anglelist = anglelist_orig; +} + +/* ---------------------------------------------------------------------- */ + +void AngleHybrid::allocate() +{ + allocated = 1; + int n = atom->nangletypes; + + map = (int *) memory->smalloc((n+1)*sizeof(int),"angle:map"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"angle:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; + + nanglelist = new int[nstyles]; + maxangle = new int[nstyles]; + anglelist = new int**[nstyles]; + for (int m = 0; m < nstyles; m++) maxangle[m] = 0; + for (int m = 0; m < nstyles; m++) anglelist[m] = NULL; +} + +/* ---------------------------------------------------------------------- + create one angle style for each arg in list +------------------------------------------------------------------------- */ + +void AngleHybrid::settings(int narg, char **arg) +{ + nstyles = narg; + styles = new Angle*[nstyles]; + keywords = new char*[nstyles]; + + for (int m = 0; m < nstyles; m++) { + for (int i = 0; i < m; i++) + if (strcmp(arg[m],arg[i]) == 0) + error->all("Angle style hybrid cannot use same angle style twice"); + if (strcmp(arg[m],"hybrid") == 0) + error->all("Angle style hybrid cannot have hybrid as an argument"); + styles[m] = force->new_angle(arg[m]); + keywords[m] = new char[strlen(arg[m])+1]; + strcpy(keywords[m],arg[m]); + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +---------------------------------------------------------------------- */ + +void AngleHybrid::coeff(int which, int narg, char **arg) +{ + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nangletypes,ilo,ihi); + + // 2nd arg = angle style name (harmonic, etc) + + int m; + for (m = 0; m < nstyles; m++) + if (strcmp(arg[1],keywords[m]) == 0) break; + if (m == nstyles) error->all("Angle coeff for hybrid has invalid style"); + + // set low-level coefficients for each angletype + // replace 2nd arg with i, call coeff() with no 1st arg + // if sub-style is NULL for "none", still set setflag + + for (int i = ilo; i <= ihi; i++) { + sprintf(arg[1],"%d",i); + map[i] = m; + if (styles[m]) styles[m]->coeff(which,narg-1,&arg[1]); + setflag[i] = 1; + } +} + +/* ---------------------------------------------------------------------- + return an equilbrium angle length +------------------------------------------------------------------------- */ + +double AngleHybrid::equilibrium_angle(int i) +{ + return styles[map[i]]->equilibrium_angle(i); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void AngleHybrid::write_restart(FILE *fp) +{ + fwrite(&nstyles,sizeof(int),1,fp); + + int n; + for (int m = 0; m < nstyles; m++) { + n = strlen(keywords[m]) + 1; + fwrite(&n,sizeof(int),1,fp); + fwrite(keywords[m],sizeof(char),n,fp); + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void AngleHybrid::read_restart(FILE *fp) +{ + allocate(); + + int me = comm->me; + if (me == 0) fread(&nstyles,sizeof(int),1,fp); + MPI_Bcast(&nstyles,1,MPI_INT,0,world); + styles = new Angle*[nstyles]; + keywords = new char*[nstyles]; + + int n; + for (int m = 0; m < nstyles; m++) { + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + keywords[m] = new char[n]; + if (me == 0) fread(keywords[m],sizeof(char),n,fp); + MPI_Bcast(keywords[m],n,MPI_CHAR,0,world); + styles[m] = force->new_angle(keywords[m]); + } +} + +/* ---------------------------------------------------------------------- */ + +double AngleHybrid::single(int type, int i1, int i2, int i3, double rfactor) +{ + if (styles[map[type]]) + return styles[map[type]]->single(type,i1,i2,i3,rfactor); + else return 0.0; +} + +/* ---------------------------------------------------------------------- + memory usage +------------------------------------------------------------------------- */ + +int AngleHybrid::memory_usage() +{ + int bytes = 0; + for (int m = 0; m < nstyles; m++) bytes += maxangle[m]*4 * sizeof(int); + for (int m = 0; m < nstyles; m++) + if (styles[m]) bytes += styles[m]->memory_usage(); + return bytes; +} diff --git a/src/MOLECULE/angle_hybrid.h b/src/MOLECULE/angle_hybrid.h new file mode 100644 index 0000000000000000000000000000000000000000..250238cfc3f47b4e7fb133cb4f01c1730d1d88a0 --- /dev/null +++ b/src/MOLECULE/angle_hybrid.h @@ -0,0 +1,46 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ANGLE_HYBRID_H +#define ANGLE_HYBRID_H + +#include "stdio.h" +#include "angle.h" + +class AngleHybrid : public Angle { + public: + AngleHybrid(); + ~AngleHybrid(); + void compute(int, int); + void settings(int, char **); + void coeff(int, int, char **); + double equilibrium_angle(int); + void write_restart(FILE *); + void read_restart(FILE *); + double single(int, int, int, int, double); + int memory_usage(); + + private: + int nstyles; // # of different angle styles + Angle **styles; // class list for each Angle style + char **keywords; // keyword for each Angle style + int *map; // which style each angle type points to + + int *nanglelist; // # of angles in sub-style anglelists + int *maxangle; // max # of angles sub-style lists can store + int ***anglelist; // anglelist for each sub-style + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/atom_angle.cpp b/src/MOLECULE/atom_angle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f8d9716571b86885bc7c51a34ef20d71bad5ff23 --- /dev/null +++ b/src/MOLECULE/atom_angle.cpp @@ -0,0 +1,290 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "atom_angle.h" +#include "domain.h" +#include "modify.h" +#include "fix.h" + +/* ---------------------------------------------------------------------- */ + +AtomAngle::AtomAngle(int narg, char **arg) : Atom(narg, arg) {} + +/* ---------------------------------------------------------------------- */ + +void AtomAngle::copy(int i, int j) +{ + int k; + + 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]; + + molecule[j] = molecule[i]; + num_bond[j] = num_bond[i]; + num_angle[j] = num_angle[i]; + nspecial[j][0] = nspecial[i][0]; + nspecial[j][1] = nspecial[i][1]; + nspecial[j][2] = nspecial[i][2]; + + for (k = 0; k < num_bond[j]; k++) { + bond_type[j][k] = bond_type[i][k]; + bond_atom[j][k] = bond_atom[i][k]; + } + + for (k = 0; k < num_angle[j]; k++) { + angle_type[j][k] = angle_type[i][k]; + angle_atom1[j][k] = angle_atom1[i][k]; + angle_atom2[j][k] = angle_atom2[i][k]; + angle_atom3[j][k] = angle_atom3[i][k]; + } + + for (k = 0; k < nspecial[j][2]; k++) special[j][k] = special[i][k]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + modify->fix[extra_grow[iextra]]->copy_arrays(i,j); +} + +/* ---------------------------------------------------------------------- */ + +void AtomAngle::pack_comm(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomAngle::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomAngle::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]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomAngle::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomAngle::pack_border(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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++] = molecule[j]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + buf[m++] = tag[j]; + buf[m++] = type[j]; + buf[m++] = mask[j]; + buf[m++] = molecule[j]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomAngle::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<int> (buf[m++]); + type[i] = static_cast<int> (buf[m++]); + mask[i] = static_cast<int> (buf[m++]); + molecule[i] = static_cast<int> (buf[m++]); + } +} + +/* ---------------------------------------------------------------------- + pack all atom quantities for shipping to another proc + xyz must be 1st 3 values, so that comm::exchange can test on them +------------------------------------------------------------------------- */ + +int AtomAngle::pack_exchange(int i, double *buf) +{ + int k; + + 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++] = image[i]; + buf[m++] = v[i][0]; + buf[m++] = v[i][1]; + buf[m++] = v[i][2]; + + buf[m++] = molecule[i]; + + buf[m++] = num_bond[i]; + for (k = 0; k < num_bond[i]; k++) { + buf[m++] = bond_type[i][k]; + buf[m++] = bond_atom[i][k]; + } + + buf[m++] = num_angle[i]; + for (k = 0; k < num_angle[i]; k++) { + buf[m++] = angle_type[i][k]; + buf[m++] = angle_atom1[i][k]; + buf[m++] = angle_atom2[i][k]; + buf[m++] = angle_atom3[i][k]; + } + + buf[m++] = nspecial[i][0]; + buf[m++] = nspecial[i][1]; + buf[m++] = nspecial[i][2]; + for (k = 0; k < nspecial[i][2]; k++) buf[m++] = special[i][k]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->pack_exchange(i,&buf[m]); + + buf[0] = m; + return m; +} + +/* ---------------------------------------------------------------------- */ + +int AtomAngle::unpack_exchange(double *buf) +{ + int k; + if (nlocal == nmax) grow(0); + + int m = 1; + x[nlocal][0] = buf[m++]; + x[nlocal][1] = buf[m++]; + x[nlocal][2] = buf[m++]; + tag[nlocal] = static_cast<int> (buf[m++]); + type[nlocal] = static_cast<int> (buf[m++]); + mask[nlocal] = static_cast<int> (buf[m++]); + image[nlocal] = static_cast<int> (buf[m++]); + v[nlocal][0] = buf[m++]; + v[nlocal][1] = buf[m++]; + v[nlocal][2] = buf[m++]; + + molecule[nlocal] = static_cast<int> (buf[m++]); + + num_bond[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_bond[nlocal]; k++) { + bond_type[nlocal][k] = static_cast<int> (buf[m++]); + bond_atom[nlocal][k] = static_cast<int> (buf[m++]); + } + + num_angle[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_angle[nlocal]; k++) { + angle_type[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom1[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom2[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom3[nlocal][k] = static_cast<int> (buf[m++]); + } + + nspecial[nlocal][0] = static_cast<int> (buf[m++]); + nspecial[nlocal][1] = static_cast<int> (buf[m++]); + nspecial[nlocal][2] = static_cast<int> (buf[m++]); + for (k = 0; k < nspecial[nlocal][2]; k++) + special[nlocal][k] = static_cast<int> (buf[m++]); + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->unpack_exchange(nlocal,&buf[m]); + + nlocal++; + return m; +} diff --git a/src/MOLECULE/atom_angle.h b/src/MOLECULE/atom_angle.h new file mode 100644 index 0000000000000000000000000000000000000000..a395c577a8ca49be919340cd0ca4f93f9cc45e6e --- /dev/null +++ b/src/MOLECULE/atom_angle.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ATOM_ANGLE_H +#define ATOM_ANGLE_H + +#include "atom.h" + +class AtomAngle : public Atom { + public: + AtomAngle(int, char **); + ~AtomAngle() {} + void copy(int, int); + void pack_comm(int, int *, double *, int *); + void unpack_comm(int, int, double *); + void pack_reverse(int, int, double *); + void unpack_reverse(int, int *, double *); + void pack_border(int, int *, double *, int *); + void unpack_border(int, int, double *); + int pack_exchange(int, double *); + int unpack_exchange(double *); +}; + +#endif diff --git a/src/MOLECULE/atom_bond.cpp b/src/MOLECULE/atom_bond.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fe3c5fe82e80b5fd7c09ec7253b052da66aa3cac --- /dev/null +++ b/src/MOLECULE/atom_bond.cpp @@ -0,0 +1,266 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "atom_bond.h" +#include "domain.h" +#include "modify.h" +#include "fix.h" + +/* ---------------------------------------------------------------------- */ + +AtomBond::AtomBond(int narg, char **arg) : Atom(narg, arg) {} + +/* ---------------------------------------------------------------------- */ + +void AtomBond::copy(int i, int j) +{ + int k; + + 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]; + + molecule[j] = molecule[i]; + num_bond[j] = num_bond[i]; + nspecial[j][0] = nspecial[i][0]; + nspecial[j][1] = nspecial[i][1]; + nspecial[j][2] = nspecial[i][2]; + + for (k = 0; k < num_bond[j]; k++) { + bond_type[j][k] = bond_type[i][k]; + bond_atom[j][k] = bond_atom[i][k]; + } + + for (k = 0; k < nspecial[j][2]; k++) special[j][k] = special[i][k]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + modify->fix[extra_grow[iextra]]->copy_arrays(i,j); +} + +/* ---------------------------------------------------------------------- */ + +void AtomBond::pack_comm(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomBond::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomBond::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]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomBond::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomBond::pack_border(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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++] = molecule[j]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + buf[m++] = tag[j]; + buf[m++] = type[j]; + buf[m++] = mask[j]; + buf[m++] = molecule[j]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomBond::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<int> (buf[m++]); + type[i] = static_cast<int> (buf[m++]); + mask[i] = static_cast<int> (buf[m++]); + molecule[i] = static_cast<int> (buf[m++]); + } +} + +/* ---------------------------------------------------------------------- + pack all atom quantities for shipping to another proc + xyz must be 1st 3 values, so that comm::exchange can test on them +------------------------------------------------------------------------- */ + +int AtomBond::pack_exchange(int i, double *buf) +{ + int k; + + 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++] = image[i]; + buf[m++] = v[i][0]; + buf[m++] = v[i][1]; + buf[m++] = v[i][2]; + + buf[m++] = molecule[i]; + + buf[m++] = num_bond[i]; + for (k = 0; k < num_bond[i]; k++) { + buf[m++] = bond_type[i][k]; + buf[m++] = bond_atom[i][k]; + } + + buf[m++] = nspecial[i][0]; + buf[m++] = nspecial[i][1]; + buf[m++] = nspecial[i][2]; + for (k = 0; k < nspecial[i][2]; k++) buf[m++] = special[i][k]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->pack_exchange(i,&buf[m]); + + buf[0] = m; + return m; +} + +/* ---------------------------------------------------------------------- */ + +int AtomBond::unpack_exchange(double *buf) +{ + int k; + if (nlocal == nmax) grow(0); + + int m = 1; + x[nlocal][0] = buf[m++]; + x[nlocal][1] = buf[m++]; + x[nlocal][2] = buf[m++]; + tag[nlocal] = static_cast<int> (buf[m++]); + type[nlocal] = static_cast<int> (buf[m++]); + mask[nlocal] = static_cast<int> (buf[m++]); + image[nlocal] = static_cast<int> (buf[m++]); + v[nlocal][0] = buf[m++]; + v[nlocal][1] = buf[m++]; + v[nlocal][2] = buf[m++]; + + molecule[nlocal] = static_cast<int> (buf[m++]); + + num_bond[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_bond[nlocal]; k++) { + bond_type[nlocal][k] = static_cast<int> (buf[m++]); + bond_atom[nlocal][k] = static_cast<int> (buf[m++]); + } + + nspecial[nlocal][0] = static_cast<int> (buf[m++]); + nspecial[nlocal][1] = static_cast<int> (buf[m++]); + nspecial[nlocal][2] = static_cast<int> (buf[m++]); + for (k = 0; k < nspecial[nlocal][2]; k++) + special[nlocal][k] = static_cast<int> (buf[m++]); + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->unpack_exchange(nlocal,&buf[m]); + + nlocal++; + return m; +} diff --git a/src/MOLECULE/atom_bond.h b/src/MOLECULE/atom_bond.h new file mode 100644 index 0000000000000000000000000000000000000000..f44f7d073f44631a834fbadadfb18adc6dcb903f --- /dev/null +++ b/src/MOLECULE/atom_bond.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ATOM_BOND_H +#define ATOM_BOND_H + +#include "atom.h" + +class AtomBond : public Atom { + public: + AtomBond(int, char **); + ~AtomBond() {} + void copy(int, int); + void pack_comm(int, int *, double *, int *); + void unpack_comm(int, int, double *); + void pack_reverse(int, int, double *); + void unpack_reverse(int, int *, double *); + void pack_border(int, int *, double *, int *); + void unpack_border(int, int, double *); + int pack_exchange(int, double *); + int unpack_exchange(double *); +}; + +#endif diff --git a/src/MOLECULE/atom_full.cpp b/src/MOLECULE/atom_full.cpp new file mode 100644 index 0000000000000000000000000000000000000000..935108e770aace516e52b7ca4606cf3078caefda --- /dev/null +++ b/src/MOLECULE/atom_full.cpp @@ -0,0 +1,353 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "atom_full.h" +#include "domain.h" +#include "modify.h" +#include "fix.h" + +/* ---------------------------------------------------------------------- */ + +AtomFull::AtomFull(int narg, char **arg) : Atom(narg, arg) {} + +/* ---------------------------------------------------------------------- */ + +void AtomFull::copy(int i, int j) +{ + int k; + + 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]; + + molecule[j] = molecule[i]; + num_bond[j] = num_bond[i]; + num_angle[j] = num_angle[i]; + num_dihedral[j] = num_dihedral[i]; + num_improper[j] = num_improper[i]; + nspecial[j][0] = nspecial[i][0]; + nspecial[j][1] = nspecial[i][1]; + nspecial[j][2] = nspecial[i][2]; + + for (k = 0; k < num_bond[j]; k++) { + bond_type[j][k] = bond_type[i][k]; + bond_atom[j][k] = bond_atom[i][k]; + } + + for (k = 0; k < num_angle[j]; k++) { + angle_type[j][k] = angle_type[i][k]; + angle_atom1[j][k] = angle_atom1[i][k]; + angle_atom2[j][k] = angle_atom2[i][k]; + angle_atom3[j][k] = angle_atom3[i][k]; + } + + for (k = 0; k < num_dihedral[j]; k++) { + dihedral_type[j][k] = dihedral_type[i][k]; + dihedral_atom1[j][k] = dihedral_atom1[i][k]; + dihedral_atom2[j][k] = dihedral_atom2[i][k]; + dihedral_atom3[j][k] = dihedral_atom3[i][k]; + dihedral_atom4[j][k] = dihedral_atom4[i][k]; + } + + for (k = 0; k < num_improper[j]; k++) { + improper_type[j][k] = improper_type[i][k]; + improper_atom1[j][k] = improper_atom1[i][k]; + improper_atom2[j][k] = improper_atom2[i][k]; + improper_atom3[j][k] = improper_atom3[i][k]; + improper_atom4[j][k] = improper_atom4[i][k]; + } + + for (k = 0; k < nspecial[j][2]; k++) special[j][k] = special[i][k]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + modify->fix[extra_grow[iextra]]->copy_arrays(i,j); +} + +/* ---------------------------------------------------------------------- */ + +void AtomFull::pack_comm(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomFull::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomFull::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]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomFull::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomFull::pack_border(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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++] = molecule[j]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + buf[m++] = tag[j]; + buf[m++] = type[j]; + buf[m++] = mask[j]; + buf[m++] = q[j]; + buf[m++] = molecule[j]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomFull::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<int> (buf[m++]); + type[i] = static_cast<int> (buf[m++]); + mask[i] = static_cast<int> (buf[m++]); + q[i] = buf[m++]; + molecule[i] = static_cast<int> (buf[m++]); + } +} + +/* ---------------------------------------------------------------------- + pack all atom quantities for shipping to another proc + xyz must be 1st 3 values, so that comm::exchange can test on them +------------------------------------------------------------------------- */ + +int AtomFull::pack_exchange(int i, double *buf) +{ + int k; + + 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++] = image[i]; + buf[m++] = v[i][0]; + buf[m++] = v[i][1]; + buf[m++] = v[i][2]; + + buf[m++] = q[i]; + + buf[m++] = molecule[i]; + + buf[m++] = num_bond[i]; + for (k = 0; k < num_bond[i]; k++) { + buf[m++] = bond_type[i][k]; + buf[m++] = bond_atom[i][k]; + } + + buf[m++] = num_angle[i]; + for (k = 0; k < num_angle[i]; k++) { + buf[m++] = angle_type[i][k]; + buf[m++] = angle_atom1[i][k]; + buf[m++] = angle_atom2[i][k]; + buf[m++] = angle_atom3[i][k]; + } + + buf[m++] = num_dihedral[i]; + for (k = 0; k < num_dihedral[i]; k++) { + buf[m++] = dihedral_type[i][k]; + buf[m++] = dihedral_atom1[i][k]; + buf[m++] = dihedral_atom2[i][k]; + buf[m++] = dihedral_atom3[i][k]; + buf[m++] = dihedral_atom4[i][k]; + } + + buf[m++] = num_improper[i]; + for (k = 0; k < num_improper[i]; k++) { + buf[m++] = improper_type[i][k]; + buf[m++] = improper_atom1[i][k]; + buf[m++] = improper_atom2[i][k]; + buf[m++] = improper_atom3[i][k]; + buf[m++] = improper_atom4[i][k]; + } + + buf[m++] = nspecial[i][0]; + buf[m++] = nspecial[i][1]; + buf[m++] = nspecial[i][2]; + for (k = 0; k < nspecial[i][2]; k++) buf[m++] = special[i][k]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->pack_exchange(i,&buf[m]); + + buf[0] = m; + return m; +} + +/* ---------------------------------------------------------------------- */ + +int AtomFull::unpack_exchange(double *buf) +{ + int k; + if (nlocal == nmax) grow(0); + + int m = 1; + x[nlocal][0] = buf[m++]; + x[nlocal][1] = buf[m++]; + x[nlocal][2] = buf[m++]; + tag[nlocal] = static_cast<int> (buf[m++]); + type[nlocal] = static_cast<int> (buf[m++]); + mask[nlocal] = static_cast<int> (buf[m++]); + image[nlocal] = static_cast<int> (buf[m++]); + v[nlocal][0] = buf[m++]; + v[nlocal][1] = buf[m++]; + v[nlocal][2] = buf[m++]; + + q[nlocal] = buf[m++]; + + molecule[nlocal] = static_cast<int> (buf[m++]); + + num_bond[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_bond[nlocal]; k++) { + bond_type[nlocal][k] = static_cast<int> (buf[m++]); + bond_atom[nlocal][k] = static_cast<int> (buf[m++]); + } + + num_angle[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_angle[nlocal]; k++) { + angle_type[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom1[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom2[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom3[nlocal][k] = static_cast<int> (buf[m++]); + } + + num_dihedral[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_dihedral[nlocal]; k++) { + dihedral_type[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom1[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom2[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom3[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom4[nlocal][k] = static_cast<int> (buf[m++]); + } + + num_improper[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_improper[nlocal]; k++) { + improper_type[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom1[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom2[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom3[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom4[nlocal][k] = static_cast<int> (buf[m++]); + } + + nspecial[nlocal][0] = static_cast<int> (buf[m++]); + nspecial[nlocal][1] = static_cast<int> (buf[m++]); + nspecial[nlocal][2] = static_cast<int> (buf[m++]); + for (k = 0; k < nspecial[nlocal][2]; k++) + special[nlocal][k] = static_cast<int> (buf[m++]); + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->unpack_exchange(nlocal,&buf[m]); + + nlocal++; + return m; +} diff --git a/src/MOLECULE/atom_full.h b/src/MOLECULE/atom_full.h new file mode 100644 index 0000000000000000000000000000000000000000..71c5a806db57840de0f4a9a2e141ba4cd3a3aa6c --- /dev/null +++ b/src/MOLECULE/atom_full.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ATOM_FULL_H +#define ATOM_FULL_H + +#include "atom.h" + +class AtomFull : public Atom { + public: + AtomFull(int, char **); + ~AtomFull() {} + void copy(int, int); + void pack_comm(int, int *, double *, int *); + void unpack_comm(int, int, double *); + void pack_reverse(int, int, double *); + void unpack_reverse(int, int *, double *); + void pack_border(int, int *, double *, int *); + void unpack_border(int, int, double *); + int pack_exchange(int, double *); + int unpack_exchange(double *); +}; + +#endif diff --git a/src/MOLECULE/atom_molecular.cpp b/src/MOLECULE/atom_molecular.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5c180619308803cd4bf205b3eea3a12cb443fab8 --- /dev/null +++ b/src/MOLECULE/atom_molecular.cpp @@ -0,0 +1,344 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "atom_molecular.h" +#include "domain.h" +#include "modify.h" +#include "fix.h" + +/* ---------------------------------------------------------------------- */ + +AtomMolecular::AtomMolecular(int narg, char **arg) : Atom(narg, arg) {} + +/* ---------------------------------------------------------------------- */ + +void AtomMolecular::copy(int i, int j) +{ + int k; + + 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]; + + molecule[j] = molecule[i]; + num_bond[j] = num_bond[i]; + num_angle[j] = num_angle[i]; + num_dihedral[j] = num_dihedral[i]; + num_improper[j] = num_improper[i]; + nspecial[j][0] = nspecial[i][0]; + nspecial[j][1] = nspecial[i][1]; + nspecial[j][2] = nspecial[i][2]; + + for (k = 0; k < num_bond[j]; k++) { + bond_type[j][k] = bond_type[i][k]; + bond_atom[j][k] = bond_atom[i][k]; + } + + for (k = 0; k < num_angle[j]; k++) { + angle_type[j][k] = angle_type[i][k]; + angle_atom1[j][k] = angle_atom1[i][k]; + angle_atom2[j][k] = angle_atom2[i][k]; + angle_atom3[j][k] = angle_atom3[i][k]; + } + + for (k = 0; k < num_dihedral[j]; k++) { + dihedral_type[j][k] = dihedral_type[i][k]; + dihedral_atom1[j][k] = dihedral_atom1[i][k]; + dihedral_atom2[j][k] = dihedral_atom2[i][k]; + dihedral_atom3[j][k] = dihedral_atom3[i][k]; + dihedral_atom4[j][k] = dihedral_atom4[i][k]; + } + + for (k = 0; k < num_improper[j]; k++) { + improper_type[j][k] = improper_type[i][k]; + improper_atom1[j][k] = improper_atom1[i][k]; + improper_atom2[j][k] = improper_atom2[i][k]; + improper_atom3[j][k] = improper_atom3[i][k]; + improper_atom4[j][k] = improper_atom4[i][k]; + } + + for (k = 0; k < nspecial[j][2]; k++) special[j][k] = special[i][k]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + modify->fix[extra_grow[iextra]]->copy_arrays(i,j); +} + +/* ---------------------------------------------------------------------- */ + +void AtomMolecular::pack_comm(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomMolecular::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomMolecular::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]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomMolecular::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomMolecular::pack_border(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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++] = molecule[j]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + buf[m++] = tag[j]; + buf[m++] = type[j]; + buf[m++] = mask[j]; + buf[m++] = molecule[j]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomMolecular::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<int> (buf[m++]); + type[i] = static_cast<int> (buf[m++]); + mask[i] = static_cast<int> (buf[m++]); + molecule[i] = static_cast<int> (buf[m++]); + } +} + +/* ---------------------------------------------------------------------- + pack all atom quantities for shipping to another proc + xyz must be 1st 3 values, so that comm::exchange can test on them +------------------------------------------------------------------------- */ + +int AtomMolecular::pack_exchange(int i, double *buf) +{ + int k; + + 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++] = image[i]; + buf[m++] = v[i][0]; + buf[m++] = v[i][1]; + buf[m++] = v[i][2]; + + buf[m++] = molecule[i]; + + buf[m++] = num_bond[i]; + for (k = 0; k < num_bond[i]; k++) { + buf[m++] = bond_type[i][k]; + buf[m++] = bond_atom[i][k]; + } + + buf[m++] = num_angle[i]; + for (k = 0; k < num_angle[i]; k++) { + buf[m++] = angle_type[i][k]; + buf[m++] = angle_atom1[i][k]; + buf[m++] = angle_atom2[i][k]; + buf[m++] = angle_atom3[i][k]; + } + + buf[m++] = num_dihedral[i]; + for (k = 0; k < num_dihedral[i]; k++) { + buf[m++] = dihedral_type[i][k]; + buf[m++] = dihedral_atom1[i][k]; + buf[m++] = dihedral_atom2[i][k]; + buf[m++] = dihedral_atom3[i][k]; + buf[m++] = dihedral_atom4[i][k]; + } + + buf[m++] = num_improper[i]; + for (k = 0; k < num_improper[i]; k++) { + buf[m++] = improper_type[i][k]; + buf[m++] = improper_atom1[i][k]; + buf[m++] = improper_atom2[i][k]; + buf[m++] = improper_atom3[i][k]; + buf[m++] = improper_atom4[i][k]; + } + + buf[m++] = nspecial[i][0]; + buf[m++] = nspecial[i][1]; + buf[m++] = nspecial[i][2]; + for (k = 0; k < nspecial[i][2]; k++) buf[m++] = special[i][k]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->pack_exchange(i,&buf[m]); + + buf[0] = m; + return m; +} + +/* ---------------------------------------------------------------------- */ + +int AtomMolecular::unpack_exchange(double *buf) +{ + int k; + if (nlocal == nmax) grow(0); + + int m = 1; + x[nlocal][0] = buf[m++]; + x[nlocal][1] = buf[m++]; + x[nlocal][2] = buf[m++]; + tag[nlocal] = static_cast<int> (buf[m++]); + type[nlocal] = static_cast<int> (buf[m++]); + mask[nlocal] = static_cast<int> (buf[m++]); + image[nlocal] = static_cast<int> (buf[m++]); + v[nlocal][0] = buf[m++]; + v[nlocal][1] = buf[m++]; + v[nlocal][2] = buf[m++]; + + molecule[nlocal] = static_cast<int> (buf[m++]); + + num_bond[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_bond[nlocal]; k++) { + bond_type[nlocal][k] = static_cast<int> (buf[m++]); + bond_atom[nlocal][k] = static_cast<int> (buf[m++]); + } + + num_angle[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_angle[nlocal]; k++) { + angle_type[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom1[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom2[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom3[nlocal][k] = static_cast<int> (buf[m++]); + } + + num_dihedral[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_dihedral[nlocal]; k++) { + dihedral_type[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom1[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom2[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom3[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom4[nlocal][k] = static_cast<int> (buf[m++]); + } + + num_improper[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_improper[nlocal]; k++) { + improper_type[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom1[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom2[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom3[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom4[nlocal][k] = static_cast<int> (buf[m++]); + } + + nspecial[nlocal][0] = static_cast<int> (buf[m++]); + nspecial[nlocal][1] = static_cast<int> (buf[m++]); + nspecial[nlocal][2] = static_cast<int> (buf[m++]); + for (k = 0; k < nspecial[nlocal][2]; k++) + special[nlocal][k] = static_cast<int> (buf[m++]); + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->unpack_exchange(nlocal,&buf[m]); + + nlocal++; + return m; +} diff --git a/src/MOLECULE/atom_molecular.h b/src/MOLECULE/atom_molecular.h new file mode 100644 index 0000000000000000000000000000000000000000..f31caf8926453f82c8c47574f52279831e686198 --- /dev/null +++ b/src/MOLECULE/atom_molecular.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ATOM_MOLECULAR_H +#define ATOM_MOLECULAR_H + +#include "atom.h" + +class AtomMolecular : public Atom { + public: + AtomMolecular(int, char **); + ~AtomMolecular() {} + void copy(int, int); + void pack_comm(int, int *, double *, int *); + void unpack_comm(int, int, double *); + void pack_reverse(int, int, double *); + void unpack_reverse(int, int *, double *); + void pack_border(int, int *, double *, int *); + void unpack_border(int, int, double *); + int pack_exchange(int, double *); + int unpack_exchange(double *); +}; + +#endif diff --git a/src/MOLECULE/bond.cpp b/src/MOLECULE/bond.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c12665806306e88175edc2b99c120ad826d77213 --- /dev/null +++ b/src/MOLECULE/bond.cpp @@ -0,0 +1,40 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "bond.h" +#include "atom.h" +#include "error.h" + +/* ----------------------------------------------------------------------- + set bond contribution to Vdwl energy to 0.0 + a particular bond style can override this +------------------------------------------------------------------------- */ + +Bond::Bond() +{ + allocated = 0; + eng_vdwl = 0.0; +} + +/* ---------------------------------------------------------------------- + check if all coeffs are set +------------------------------------------------------------------------- */ + +void Bond::init() +{ + if (!allocated) error->all("Bond coeffs are not set"); + for (int i = 1; i <= atom->nbondtypes; i++) + if (setflag[i] == 0) error->all("All bond coeffs are not set"); + init_style(); +} diff --git a/src/MOLECULE/bond_fene.cpp b/src/MOLECULE/bond_fene.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0c516341a43ce101513a26945994dc27c7296e9d --- /dev/null +++ b/src/MOLECULE/bond_fene.cpp @@ -0,0 +1,272 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "bond_fene.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "update.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +BondFENE::BondFENE() +{ + TWO_1_3 = pow(2.0,(1.0/3.0)); +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +BondFENE::~BondFENE() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(r0); + memory->sfree(epsilon); + memory->sfree(sigma); + } +} + +/* ---------------------------------------------------------------------- */ + +void BondFENE::compute(int eflag, int vflag) +{ + int i1,i2,n,type,factor; + double delx,dely,delz,rsq,r0sq,rlogarg,fforce,sr2,sr6,rfactor; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **bondlist = neighbor->bondlist; + int nbondlist = neighbor->nbondlist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nbondlist; n++) { + + i1 = bondlist[n][0]; + i2 = bondlist[n][1]; + type = bondlist[n][2]; + + if (newton_bond) factor = 2; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + } + rfactor = 0.5*factor; + + delx = x[i1][0] - x[i2][0]; + dely = x[i1][1] - x[i2][1]; + delz = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx,&dely,&delz); + + // force from log term + + rsq = delx*delx + dely*dely + delz*delz; + r0sq = r0[type] * r0[type]; + rlogarg = 1.0 - rsq/r0sq; + + // if r -> r0, then rlogarg < 0.0 which is an error + // issue a warning and reset rlogarg = epsilon + // if r > 2*r0 something serious is wrong, abort + + if (rlogarg < 0.1) { + char str[128]; + sprintf(str,"FENE bond too long: %d %d %d %g", + update->ntimestep,atom->tag[i1],atom->tag[i2],sqrt(rsq)); + error->warning(str); + if (rlogarg <= -3.0) error->one("Bad FENE bond"); + rlogarg = 0.1; + } + + fforce = -k[type]/rlogarg; + + // force from LJ term + + if (rsq < TWO_1_3*sigma[type]*sigma[type]) { + sr2 = sigma[type]*sigma[type]/rsq; + sr6 = sr2*sr2*sr2; + fforce += 48.0*epsilon[type]*sr6*(sr6-0.5)/rsq; + } + + // energy + + if (eflag) { + energy -= 0.5*rfactor * k[type]*r0sq*log(rlogarg); + if (rsq < TWO_1_3*sigma[type]*sigma[type]) + energy += rfactor * (4.0*epsilon[type]*sr6*(sr6-1.0) + epsilon[type]); + } + + // apply force to each of 2 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] += delx*fforce; + f[i1][1] += dely*fforce; + f[i1][2] += delz*fforce; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] -= delx*fforce; + f[i2][1] -= dely*fforce; + f[i2][2] -= delz*fforce; + } + + // virial contribution + + if (vflag) { + virial[0] += rfactor * delx*delx*fforce; + virial[1] += rfactor * dely*dely*fforce; + virial[2] += rfactor * delz*delz*fforce; + virial[3] += rfactor * delx*dely*fforce; + virial[4] += rfactor * delx*delz*fforce; + virial[5] += rfactor * dely*delz*fforce; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void BondFENE::allocate() +{ + allocated = 1; + int n = atom->nbondtypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"bond:k"); + r0 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:r0"); + epsilon = (double *) memory->smalloc((n+1)*sizeof(double),"bond:epsilon"); + sigma = (double *) memory->smalloc((n+1)*sizeof(double),"bond:sigma"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"bond:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void BondFENE::coeff(int narg, char **arg) +{ + if (narg != 5) error->all("Incorrect args for bond coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nbondtypes,ilo,ihi); + + double k_one = atof(arg[1]); + double r0_one = atof(arg[2]); + double epsilon_one = atof(arg[3]); + double sigma_one = atof(arg[4]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + r0[i] = r0_one; + epsilon[i] = epsilon_one; + sigma[i] = sigma_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for bond coefficients"); +} + +/* ---------------------------------------------------------------------- */ + +double BondFENE::equilibrium_distance(int i) +{ + return 0.97*sigma[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void BondFENE::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&r0[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&epsilon[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&sigma[1],sizeof(double),atom->nbondtypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void BondFENE::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->nbondtypes,fp); + fread(&r0[1],sizeof(double),atom->nbondtypes,fp); + fread(&epsilon[1],sizeof(double),atom->nbondtypes,fp); + fread(&sigma[1],sizeof(double),atom->nbondtypes,fp); + } + MPI_Bcast(&k[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&r0[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&epsilon[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&sigma[1],atom->nbondtypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nbondtypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +void BondFENE::single(int type, double rsq, int i, int j, double rfactor, + int eflag, double &fforce, double &eng) +{ + double r0sq = r0[type] * r0[type]; + double rlogarg = 1.0 - rsq/r0sq; + + // if r -> r0, then rlogarg < 0.0 which is an error + // issue a warning and reset rlogarg = epsilon + // if r > 2*r0 something serious is wrong, abort + + if (rlogarg < 0.1) { + char str[128]; + sprintf(str,"FENE bond too long: %d %g",update->ntimestep,sqrt(rsq)); + error->warning(str); + if (rlogarg <= -3.0) error->one("Bad FENE bond"); + rlogarg = 0.1; + } + + fforce = -k[type]/rlogarg; + + // force from LJ term + + double sr2,sr6; + if (rsq < TWO_1_3*sigma[type]*sigma[type]) { + sr2 = sigma[type]*sigma[type]/rsq; + sr6 = sr2*sr2*sr2; + fforce += 48.0*epsilon[type]*sr6*(sr6-0.5)/rsq; + } + + // energy + + if (eflag) { + eng = -0.5*rfactor * k[type]*r0sq*log(rlogarg); + if (rsq < TWO_1_3*sigma[type]*sigma[type]) + eng += rfactor * (4.0*epsilon[type]*sr6*(sr6-1.0) + epsilon[type]); + } +} diff --git a/src/MOLECULE/bond_fene.h b/src/MOLECULE/bond_fene.h new file mode 100644 index 0000000000000000000000000000000000000000..43c997f92dcbc543fcf841e55bcfd86ed7517911 --- /dev/null +++ b/src/MOLECULE/bond_fene.h @@ -0,0 +1,38 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef BOND_FENE_H +#define BOND_FENE_H + +#include "stdio.h" +#include "bond.h" + +class BondFENE : public Bond { + public: + BondFENE(); + ~BondFENE(); + void compute(int, int); + void coeff(int, char **); + double equilibrium_distance(int); + void write_restart(FILE *); + void read_restart(FILE *); + void single(int, double, int, int, double, int, double &, double &); + + private: + double TWO_1_3; + double *k,*r0,*epsilon,*sigma; + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/bond_fene_expand.cpp b/src/MOLECULE/bond_fene_expand.cpp new file mode 100644 index 0000000000000000000000000000000000000000..801ae110f87aa9f1170c4d2c6c5e194e6565cfd3 --- /dev/null +++ b/src/MOLECULE/bond_fene_expand.cpp @@ -0,0 +1,292 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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. +------------------------------------------------------------------------- */ + +// FENE bond potential, with repulsive LJ, with shift + +#include "math.h" +#include "stdlib.h" +#include "bond_fene_expand.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "update.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- + set all global defaults +------------------------------------------------------------------------- */ + +BondFENEExpand::BondFENEExpand() +{ + TWO_1_3 = pow(2.0,(1.0/3.0)); +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +BondFENEExpand::~BondFENEExpand() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(r0); + memory->sfree(epsilon); + memory->sfree(sigma); + memory->sfree(shift); + } +} + +/* ---------------------------------------------------------------------- */ + +void BondFENEExpand::compute(int eflag, int vflag) +{ + int i1,i2,n,type,factor; + double delx,dely,delz,rsq,r0sq,rlogarg,fforce,sr2,sr6,rfactor; + double r,rshift,rshiftsq; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **bondlist = neighbor->bondlist; + int nbondlist = neighbor->nbondlist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nbondlist; n++) { + + i1 = bondlist[n][0]; + i2 = bondlist[n][1]; + type = bondlist[n][2]; + + if (newton_bond) factor = 2; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + } + rfactor = 0.5*factor; + + delx = x[i1][0] - x[i2][0]; + dely = x[i1][1] - x[i2][1]; + delz = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx,&dely,&delz); + + // force from log term + + rsq = delx*delx + dely*dely + delz*delz; + r = sqrt(rsq); + rshift = r - shift[type]; + rshiftsq = rshift*rshift; + r0sq = r0[type] * r0[type]; + rlogarg = 1.0 - rshiftsq/r0sq; + + // if r -> r0, then rlogarg < 0.0 which is an error + // issue a warning and reset rlogarg = epsilon + // if r > 2*r0 something serious is wrong, abort + + if (rlogarg < 0.1) { + char str[128]; + sprintf(str,"FENE bond too long: %d %d %d %g", + update->ntimestep,atom->tag[i1],atom->tag[i2],sqrt(rsq)); + error->warning(str); + if (rlogarg <= -3.0) error->one("Bad FENE bond"); + rlogarg = 0.1; + } + + fforce = -k[type]*rshift/rlogarg/r; + + // force from LJ term + + if (rshiftsq < TWO_1_3*sigma[type]*sigma[type]) { + sr2 = sigma[type]*sigma[type]/rshiftsq; + sr6 = sr2*sr2*sr2; + fforce += 48.0*epsilon[type]*sr6*(sr6-0.5)/rshift/r; + } + + // energy + + if (eflag) { + energy -= 0.5*rfactor * k[type]*r0sq*log(rlogarg); + if (rshiftsq < TWO_1_3*sigma[type]*sigma[type]) + energy += 0.5*factor * + (4.0*epsilon[type]*sr6*(sr6-1.0) + epsilon[type]); + } + + + // apply force to each of 2 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] += delx*fforce; + f[i1][1] += dely*fforce; + f[i1][2] += delz*fforce; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] -= delx*fforce; + f[i2][1] -= dely*fforce; + f[i2][2] -= delz*fforce; + } + + // virial contribution + + if (vflag) { + virial[0] += rfactor * delx*delx*fforce; + virial[1] += rfactor * dely*dely*fforce; + virial[2] += rfactor * delz*delz*fforce; + virial[3] += rfactor * delx*dely*fforce; + virial[4] += rfactor * delx*delz*fforce; + virial[5] += rfactor * dely*delz*fforce; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void BondFENEExpand::allocate() +{ + allocated = 1; + int n = atom->nbondtypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"bond:k"); + r0 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:r0"); + epsilon = (double *) memory->smalloc((n+1)*sizeof(double),"bond:epsilon"); + sigma = (double *) memory->smalloc((n+1)*sizeof(double),"bond:sigma"); + shift = (double *) memory->smalloc((n+1)*sizeof(double),"bond:shift"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"bond:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void BondFENEExpand::coeff(int narg, char **arg) +{ + if (narg != 6) error->all("Incorrect args for bond coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nbondtypes,ilo,ihi); + + double k_one = atof(arg[1]); + double r0_one = atof(arg[2]); + double epsilon_one = atof(arg[3]); + double sigma_one = atof(arg[4]); + double shift_one = atof(arg[5]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + r0[i] = r0_one; + epsilon[i] = epsilon_one; + sigma[i] = sigma_one; + shift[i] = shift_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for bond coefficients"); +} + +/* ---------------------------------------------------------------------- */ + +double BondFENEExpand::equilibrium_distance(int i) +{ + return 0.97*sigma[i] + shift[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void BondFENEExpand::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&r0[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&epsilon[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&sigma[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&shift[1],sizeof(double),atom->nbondtypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void BondFENEExpand::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->nbondtypes,fp); + fread(&r0[1],sizeof(double),atom->nbondtypes,fp); + fread(&epsilon[1],sizeof(double),atom->nbondtypes,fp); + fread(&sigma[1],sizeof(double),atom->nbondtypes,fp); + fread(&shift[1],sizeof(double),atom->nbondtypes,fp); + } + MPI_Bcast(&k[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&r0[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&epsilon[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&sigma[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&shift[1],atom->nbondtypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nbondtypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +void BondFENEExpand::single(int type, double rsq, int i, int j, double rfactor, + int eflag, double &fforce, double &eng) +{ + double r = sqrt(rsq); + double rshift = r - shift[type]; + double rshiftsq = rshift*rshift; + double r0sq = r0[type] * r0[type]; + double rlogarg = 1.0 - rshiftsq/r0sq; + + // if r -> r0, then rlogarg < 0.0 which is an error + // issue a warning and reset rlogarg = epsilon + // if r > 2*r0 something serious is wrong, abort + + if (rlogarg < 0.1) { + char str[128]; + sprintf(str,"FENE bond too long: %d %g",update->ntimestep,sqrt(rsq)); + error->warning(str); + if (rlogarg <= -3.0) error->one("Bad FENE bond"); + rlogarg = 0.1; + } + + fforce = -k[type]*rshift/rlogarg/r; + + // force from LJ term + + double sr2,sr6; + if (rshiftsq < TWO_1_3*sigma[type]*sigma[type]) { + sr2 = sigma[type]*sigma[type]/rshiftsq; + sr6 = sr2*sr2*sr2; + fforce += 48.0*epsilon[type]*sr6*(sr6-0.5)/rshift/r; + } + + // energy + + if (eflag) { + eng = -0.5*rfactor * k[type]*r0sq*log(rlogarg); + if (rshiftsq < TWO_1_3*sigma[type]*sigma[type]) + eng += rfactor * (4.0*epsilon[type]*sr6*(sr6-1.0) + epsilon[type]); + } +} diff --git a/src/MOLECULE/bond_fene_expand.h b/src/MOLECULE/bond_fene_expand.h new file mode 100644 index 0000000000000000000000000000000000000000..0e7167b1c68abf519ff5b6e6c979de23408cd124 --- /dev/null +++ b/src/MOLECULE/bond_fene_expand.h @@ -0,0 +1,38 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef BOND_FENE_EXPAND_H +#define BOND_FENE_EXPAND_H + +#include "stdio.h" +#include "bond.h" + +class BondFENEExpand : public Bond { + public: + BondFENEExpand(); + ~BondFENEExpand(); + void compute(int, int); + void coeff(int, char **); + double equilibrium_distance(int); + void write_restart(FILE *); + void read_restart(FILE *); + void single(int, double, int, int, double, int, double &, double &); + + private: + double TWO_1_3; + double *k,*r0,*epsilon,*sigma,*shift; + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/bond_harmonic.cpp b/src/MOLECULE/bond_harmonic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4520ec0dfb7288a98ec728ce54fc04cb3c1691bf --- /dev/null +++ b/src/MOLECULE/bond_harmonic.cpp @@ -0,0 +1,204 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "bond_harmonic.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +BondHarmonic::~BondHarmonic() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(r0); + } +} + +/* ---------------------------------------------------------------------- */ + +void BondHarmonic::compute(int eflag, int vflag) +{ + int i1,i2,n,type,factor; + double delx,dely,delz,rsq,r,dr,rk,fforce,rfactor; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **bondlist = neighbor->bondlist; + int nbondlist = neighbor->nbondlist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nbondlist; n++) { + + i1 = bondlist[n][0]; + i2 = bondlist[n][1]; + type = bondlist[n][2]; + + if (newton_bond) factor = 2; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + } + rfactor = 0.5 * factor; + + delx = x[i1][0] - x[i2][0]; + dely = x[i1][1] - x[i2][1]; + delz = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx,&dely,&delz); + + rsq = delx*delx + dely*dely + delz*delz; + r = sqrt(rsq); + dr = r - r0[type]; + rk = k[type] * dr; + + // force & energy + + if (r > 0.0) fforce = -2.0*rk/r; + else fforce = 0.0; + + if (eflag) energy += rfactor * rk*dr; + + // apply force to each of 2 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] += delx*fforce; + f[i1][1] += dely*fforce; + f[i1][2] += delz*fforce; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] -= delx*fforce; + f[i2][1] -= dely*fforce; + f[i2][2] -= delz*fforce; + } + + // virial contribution + + if (vflag) { + virial[0] += rfactor*delx*delx*fforce; + virial[1] += rfactor*dely*dely*fforce; + virial[2] += rfactor*delz*delz*fforce; + virial[3] += rfactor*delx*dely*fforce; + virial[4] += rfactor*delx*delz*fforce; + virial[5] += rfactor*dely*delz*fforce; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void BondHarmonic::allocate() +{ + allocated = 1; + int n = atom->nbondtypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"bond:k"); + r0 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:r0"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"bond:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more types +------------------------------------------------------------------------- */ + +void BondHarmonic::coeff(int narg, char **arg) +{ + if (narg != 3) error->all("Incorrect args for bond coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nbondtypes,ilo,ihi); + + double k_one = atof(arg[1]); + double r0_one = atof(arg[2]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + r0[i] = r0_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for bond coefficients"); +} + +/* ---------------------------------------------------------------------- + return an equilbrium bond length +------------------------------------------------------------------------- */ + +double BondHarmonic::equilibrium_distance(int i) +{ + return r0[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void BondHarmonic::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&r0[1],sizeof(double),atom->nbondtypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void BondHarmonic::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->nbondtypes,fp); + fread(&r0[1],sizeof(double),atom->nbondtypes,fp); + } + MPI_Bcast(&k[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&r0[1],atom->nbondtypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nbondtypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +void BondHarmonic::single(int type, double rsq, int i, int j, double rfactor, + int eflag, double &fforce, double &eng) +{ + double r = sqrt(rsq); + double dr = r - r0[type]; + double rk = k[type] * dr; + + // force & energy + + if (r > 0.0) fforce = -2.0*rk/r; + else fforce = 0.0; + if (eflag) eng = rfactor * rk*dr; +} diff --git a/src/MOLECULE/bond_harmonic.h b/src/MOLECULE/bond_harmonic.h new file mode 100644 index 0000000000000000000000000000000000000000..438df27e0b3119f2efdfa82c16630c7e4e9ac9e1 --- /dev/null +++ b/src/MOLECULE/bond_harmonic.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef BOND_HARMONIC_H +#define BOND_HARMONIC_H + +#include "stdio.h" +#include "bond.h" + +class BondHarmonic : public Bond { + public: + BondHarmonic() {} + ~BondHarmonic(); + void compute(int, int); + void coeff(int, char **); + double equilibrium_distance(int); + void write_restart(FILE *); + void read_restart(FILE *); + void single(int, double, int, int, double, int, double &, double &); + + private: + double *k,*r0; + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/bond_hybrid.cpp b/src/MOLECULE/bond_hybrid.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ff9f7b4f3f8cbfc59fc53106a98a281845061021 --- /dev/null +++ b/src/MOLECULE/bond_hybrid.cpp @@ -0,0 +1,273 @@ +/* ----------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "string.h" +#include "bond_hybrid.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define EXTRA 1000 + +/* ---------------------------------------------------------------------- + set all global defaults +------------------------------------------------------------------------- */ + +BondHybrid::BondHybrid() +{ + nstyles = 0; +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +BondHybrid::~BondHybrid() +{ + if (nstyles) { + for (int i = 0; i < nstyles; i++) delete styles[i]; + delete [] styles; + for (int i = 0; i < nstyles; i++) delete [] keywords[i]; + delete [] keywords; + } + + if (allocated) { + memory->sfree(setflag); + memory->sfree(map); + delete [] nbondlist; + delete [] maxbond; + for (int i = 0; i < nstyles; i++) + memory->destroy_2d_int_array(bondlist[i]); + delete [] bondlist; + } +} + +/* ---------------------------------------------------------------------- */ + +void BondHybrid::compute(int eflag, int vflag) +{ + int i,m,n; + + // save ptrs to original bondlist + + int nbondlist_orig = neighbor->nbondlist; + int **bondlist_orig = neighbor->bondlist; + + // if this is re-neighbor step, create sub-style bondlists + // nbondlist[] = length of each sub-style list + // realloc sub-style bondlist if necessary + // load sub-style bondlist with 3 values from original bondlist + + if (neighbor->ago == 0) { + for (m = 0; m < nstyles; m++) nbondlist[m] = 0; + for (i = 0; i < nbondlist_orig; i++) + nbondlist[map[bondlist_orig[i][2]]]++; + for (m = 0; m < nstyles; m++) { + if (nbondlist[m] > maxbond[m]) { + memory->destroy_2d_int_array(bondlist[m]); + maxbond[m] = nbondlist[m] + EXTRA; + bondlist[m] = (int **) + memory->create_2d_int_array(maxbond[m],3,"bond_hybrid:bondlist"); + } + nbondlist[m] = 0; + } + for (i = 0; i < nbondlist_orig; i++) { + m = map[bondlist_orig[i][2]]; + n = nbondlist[m]; + bondlist[m][n][0] = bondlist_orig[i][0]; + bondlist[m][n][1] = bondlist_orig[i][1]; + bondlist[m][n][2] = bondlist_orig[i][2]; + nbondlist[m]++; + } + } + + // call each sub-style's compute function + // must set neighbor->bondlist to sub-style bondlist before call + // accumulate sub-style energy,virial in hybrid's energy,virial + + energy = 0.0; + eng_vdwl = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + for (m = 0; m < nstyles; m++) { + if (styles[m] == NULL) continue; + neighbor->nbondlist = nbondlist[m]; + neighbor->bondlist = bondlist[m]; + styles[m]->compute(eflag,vflag); + if (eflag) { + energy += styles[m]->energy; + eng_vdwl += styles[m]->eng_vdwl; + } + if (vflag) for (n = 0; n < 6; n++) virial[n] += styles[m]->virial[n]; + } + + // restore ptrs to original bondlist + + neighbor->nbondlist = nbondlist_orig; + neighbor->bondlist = bondlist_orig; +} + +/* ---------------------------------------------------------------------- */ + +void BondHybrid::allocate() +{ + allocated = 1; + int n = atom->nbondtypes; + + map = (int *) memory->smalloc((n+1)*sizeof(int),"bond:map"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"bond:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; + + nbondlist = new int[nstyles]; + maxbond = new int[nstyles]; + bondlist = new int**[nstyles]; + for (int m = 0; m < nstyles; m++) maxbond[m] = 0; + for (int m = 0; m < nstyles; m++) bondlist[m] = NULL; +} + +/* ---------------------------------------------------------------------- + create one bond style for each arg in list +------------------------------------------------------------------------- */ + +void BondHybrid::settings(int narg, char **arg) +{ + nstyles = narg; + styles = new Bond*[nstyles]; + keywords = new char*[nstyles]; + + for (int m = 0; m < nstyles; m++) { + for (int i = 0; i < m; i++) + if (strcmp(arg[m],arg[i]) == 0) + error->all("Bond style hybrid cannot use same bond style twice"); + if (strcmp(arg[m],"hybrid") == 0) + error->all("Bond style hybrid cannot have hybrid as an argument"); + styles[m] = force->new_bond(arg[m]); + keywords[m] = new char[strlen(arg[m])+1]; + strcpy(keywords[m],arg[m]); + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +---------------------------------------------------------------------- */ + +void BondHybrid::coeff(int narg, char **arg) +{ + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nbondtypes,ilo,ihi); + + // 2nd arg = bond style name (harmonic, fene, etc) + + int m; + for (m = 0; m < nstyles; m++) + if (strcmp(arg[1],keywords[m]) == 0) break; + if (m == nstyles) error->all("Bond coeff for hybrid has invalid style"); + + // set low-level coefficients for each bondtype + // replace 2nd arg with i, call coeff() with no 1st arg + // if sub-style is NULL for "none", still set setflag + + for (int i = ilo; i <= ihi; i++) { + sprintf(arg[1],"%d",i); + map[i] = m; + if (styles[m]) styles[m]->coeff(narg-1,&arg[1]); + setflag[i] = 1; + } +} + +/* ---------------------------------------------------------------------- */ + +void BondHybrid::init_style() +{ + for (int m = 0; m < nstyles; m++) + if (styles[m]) styles[m]->init_style(); +} + +/* ---------------------------------------------------------------------- + return an equilbrium bond length +------------------------------------------------------------------------- */ + +double BondHybrid::equilibrium_distance(int i) +{ + return styles[map[i]]->equilibrium_distance(i); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void BondHybrid::write_restart(FILE *fp) +{ + fwrite(&nstyles,sizeof(int),1,fp); + + int n; + for (int m = 0; m < nstyles; m++) { + n = strlen(keywords[m]) + 1; + fwrite(&n,sizeof(int),1,fp); + fwrite(keywords[m],sizeof(char),n,fp); + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void BondHybrid::read_restart(FILE *fp) +{ + allocate(); + + int me = comm->me; + if (me == 0) fread(&nstyles,sizeof(int),1,fp); + MPI_Bcast(&nstyles,1,MPI_INT,0,world); + styles = new Bond*[nstyles]; + keywords = new char*[nstyles]; + + int n; + for (int m = 0; m < nstyles; m++) { + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + keywords[m] = new char[n]; + if (me == 0) fread(keywords[m],sizeof(char),n,fp); + MPI_Bcast(keywords[m],n,MPI_CHAR,0,world); + styles[m] = force->new_bond(keywords[m]); + } +} + +/* ---------------------------------------------------------------------- */ + +void BondHybrid::single(int type, double rsq, int i, int j, double rfactor, + int eflag, double &fforce, double &eng) +{ + if (styles[map[type]]) + styles[map[type]]->single(type,rsq,i,j,rfactor,eflag,fforce,eng); +} + +/* ---------------------------------------------------------------------- + memory usage +------------------------------------------------------------------------- */ + +int BondHybrid::memory_usage() +{ + int bytes = 0; + for (int m = 0; m < nstyles; m++) bytes += maxbond[m]*3 * sizeof(int); + for (int m = 0; m < nstyles; m++) + if (styles[m]) bytes += styles[m]->memory_usage(); + return bytes; +} diff --git a/src/MOLECULE/bond_morse.cpp b/src/MOLECULE/bond_morse.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5cd83b8a32ad2de537e1cfc5e55f9aad051fa0be --- /dev/null +++ b/src/MOLECULE/bond_morse.cpp @@ -0,0 +1,214 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Jeff Greathouse (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "bond_morse.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +BondMorse::~BondMorse() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(d0); + memory->sfree(alpha); + memory->sfree(r0); + } +} + +/* ---------------------------------------------------------------------- */ + +void BondMorse::compute(int eflag, int vflag) +{ + int i1,i2,n,type,factor; + double delx,dely,delz,rsq,r,dr,fforce,rfactor,ralpha; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **bondlist = neighbor->bondlist; + int nbondlist = neighbor->nbondlist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nbondlist; n++) { + + i1 = bondlist[n][0]; + i2 = bondlist[n][1]; + type = bondlist[n][2]; + + if (newton_bond) factor = 2; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + } + rfactor = 0.5 * factor; + + delx = x[i1][0] - x[i2][0]; + dely = x[i1][1] - x[i2][1]; + delz = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx,&dely,&delz); + + rsq = delx*delx + dely*dely + delz*delz; + r = sqrt(rsq); + dr = r - r0[type]; + ralpha = exp(-alpha[type]*dr); + + // force & energy + + if (r > 0.0) fforce = -2.0*d0[type]*alpha[type]*(1-ralpha)*ralpha/r; + else fforce = 0.0; + + if (eflag) energy += rfactor * d0[type]*(1-ralpha)*(1-ralpha); + + // apply force to each of 2 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] += delx*fforce; + f[i1][1] += dely*fforce; + f[i1][2] += delz*fforce; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] -= delx*fforce; + f[i2][1] -= dely*fforce; + f[i2][2] -= delz*fforce; + } + + // virial contribution + + if (vflag) { + virial[0] += rfactor*delx*delx*fforce; + virial[1] += rfactor*dely*dely*fforce; + virial[2] += rfactor*delz*delz*fforce; + virial[3] += rfactor*delx*dely*fforce; + virial[4] += rfactor*delx*delz*fforce; + virial[5] += rfactor*dely*delz*fforce; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void BondMorse::allocate() +{ + allocated = 1; + int n = atom->nbondtypes; + + d0 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:d0"); + alpha = (double *) memory->smalloc((n+1)*sizeof(double),"bond:alpha"); + r0 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:r0"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"bond:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void BondMorse::coeff(int narg, char **arg) +{ + if (narg != 4) error->all("Incorrect args for bond coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nbondtypes,ilo,ihi); + + double d0_one = atof(arg[1]); + double alpha_one = atof(arg[2]); + double r0_one = atof(arg[3]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + d0[i] = d0_one; + alpha[i] = alpha_one; + r0[i] = r0_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for bond coefficients"); +} + +/* ---------------------------------------------------------------------- + return an equilbrium bond length +------------------------------------------------------------------------- */ + +double BondMorse::equilibrium_distance(int i) +{ + return r0[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void BondMorse::write_restart(FILE *fp) +{ + fwrite(&d0[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&alpha[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&r0[1],sizeof(double),atom->nbondtypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void BondMorse::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&d0[1],sizeof(double),atom->nbondtypes,fp); + fread(&alpha[1],sizeof(double),atom->nbondtypes,fp); + fread(&r0[1],sizeof(double),atom->nbondtypes,fp); + } + MPI_Bcast(&d0[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&alpha[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&r0[1],atom->nbondtypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nbondtypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +void BondMorse::single(int type, double rsq, int i, int j, double rfactor, + int eflag, double &fforce, double &eng) +{ + double r = sqrt(rsq); + double dr = r - r0[type]; + double ralpha = exp(-alpha[type]*dr); + + // force & energy + + if (r > 0.0) fforce = -2.0*d0[type]*alpha[type]*(1-ralpha)*ralpha/r; + else fforce = 0.0; + if (eflag) eng = rfactor * d0[type]*(1-ralpha)*(1-ralpha); +} diff --git a/src/MOLECULE/bond_morse.h b/src/MOLECULE/bond_morse.h new file mode 100644 index 0000000000000000000000000000000000000000..9ac1d4cbf32eaaaabfda6bc4226fd8f35104a3dc --- /dev/null +++ b/src/MOLECULE/bond_morse.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef BOND_MORSE_H +#define BOND_MORSE_H + +#include "stdio.h" +#include "bond.h" + +class BondMorse : public Bond { + public: + BondMorse() {} + ~BondMorse(); + void compute(int, int); + void coeff(int, char **); + double equilibrium_distance(int); + void write_restart(FILE *); + void read_restart(FILE *); + void single(int, double, int, int, double, int, double &, double &); + + private: + double *d0,*alpha,*r0; + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/bond_nonlinear.cpp b/src/MOLECULE/bond_nonlinear.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f39e61f6651ccb9ff50004dcea9ae84c8f78d376 --- /dev/null +++ b/src/MOLECULE/bond_nonlinear.cpp @@ -0,0 +1,211 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "bond_nonlinear.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +BondNonlinear::~BondNonlinear() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(epsilon); + memory->sfree(r0); + memory->sfree(lamda); + } +} + +/* ---------------------------------------------------------------------- */ + +void BondNonlinear::compute(int eflag, int vflag) +{ + int i1,i2,n,type,factor; + double delx,dely,delz,rsq,r,dr,drsq,lamdasq,denom,denomsq,fforce,rfactor; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **bondlist = neighbor->bondlist; + int nbondlist = neighbor->nbondlist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nbondlist; n++) { + + i1 = bondlist[n][0]; + i2 = bondlist[n][1]; + type = bondlist[n][2]; + + if (newton_bond) factor = 2; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + } + rfactor = 0.5 * factor; + + delx = x[i1][0] - x[i2][0]; + dely = x[i1][1] - x[i2][1]; + delz = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx,&dely,&delz); + + rsq = delx*delx + dely*dely + delz*delz; + r = sqrt(rsq); + dr = r - r0[type]; + drsq = dr*dr; + lamdasq = lamda[type]*lamda[type]; + denom = lamdasq - drsq; + denomsq = denom*denom; + + // force & energy + + fforce = -epsilon[type]/r * 2.0*dr*lamdasq/denomsq; + if (eflag) energy += rfactor * epsilon[type] * drsq / denom; + + // apply force to each of 2 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] += delx*fforce; + f[i1][1] += dely*fforce; + f[i1][2] += delz*fforce; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] -= delx*fforce; + f[i2][1] -= dely*fforce; + f[i2][2] -= delz*fforce; + } + + // virial contribution + + if (vflag) { + virial[0] += rfactor*delx*delx*fforce; + virial[1] += rfactor*dely*dely*fforce; + virial[2] += rfactor*delz*delz*fforce; + virial[3] += rfactor*delx*dely*fforce; + virial[4] += rfactor*delx*delz*fforce; + virial[5] += rfactor*dely*delz*fforce; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void BondNonlinear::allocate() +{ + allocated = 1; + int n = atom->nbondtypes; + + epsilon = (double *) memory->smalloc((n+1)*sizeof(double),"bond:epsilon"); + r0 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:r0"); + lamda = (double *) memory->smalloc((n+1)*sizeof(double),"bond:lamda"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"bond:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void BondNonlinear::coeff(int narg, char **arg) +{ + if (narg != 4) error->all("Incorrect args for bond coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nbondtypes,ilo,ihi); + + double epsilon_one = atof(arg[1]); + double r0_one = atof(arg[2]); + double lamda_one = atof(arg[3]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + epsilon[i] = epsilon_one; + r0[i] = r0_one; + lamda[i] = lamda_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for bond coefficients"); +} + +/* ---------------------------------------------------------------------- */ + +double BondNonlinear::equilibrium_distance(int i) +{ + return r0[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void BondNonlinear::write_restart(FILE *fp) +{ + fwrite(&epsilon[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&r0[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&lamda[1],sizeof(double),atom->nbondtypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void BondNonlinear::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&epsilon[1],sizeof(double),atom->nbondtypes,fp); + fread(&r0[1],sizeof(double),atom->nbondtypes,fp); + fread(&lamda[1],sizeof(double),atom->nbondtypes,fp); + } + MPI_Bcast(&epsilon[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&r0[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&lamda[1],atom->nbondtypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nbondtypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +void BondNonlinear::single(int type, double rsq, int i, int j, double rfactor, + int eflag, double &fforce, double &eng) +{ + double r = sqrt(rsq); + double dr = r - r0[type]; + double drsq = dr*dr; + double lamdasq = lamda[type]*lamda[type]; + double denom = lamdasq - drsq; + double denomsq = denom*denom; + + // force & energy + + fforce = -epsilon[type]/r * 2.0*dr*lamdasq/denomsq; + if (eflag) eng = rfactor * epsilon[type] * drsq / denom; +} diff --git a/src/MOLECULE/bond_nonlinear.h b/src/MOLECULE/bond_nonlinear.h new file mode 100644 index 0000000000000000000000000000000000000000..952ddf1661460603b281df75d54d07198f0fda21 --- /dev/null +++ b/src/MOLECULE/bond_nonlinear.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef BOND_NONLINEAR_H +#define BOND_NONLINEAR_H + +#include "stdio.h" +#include "bond.h" + +class BondNonlinear : public Bond { + public: + BondNonlinear() {} + ~BondNonlinear(); + void compute(int, int); + void coeff(int, char **); + double equilibrium_distance(int); + void write_restart(FILE *); + void read_restart(FILE *); + void single(int, double, int, int, double, int, double &, double &); + + private: + double *epsilon,*r0,*lamda; + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/bond_quartic.cpp b/src/MOLECULE/bond_quartic.cpp new file mode 100755 index 0000000000000000000000000000000000000000..6889837bddfd840317ddf006d5b32caba82c24ef --- /dev/null +++ b/src/MOLECULE/bond_quartic.cpp @@ -0,0 +1,340 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Chris Lorenz and Mark Stevens (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "bond_quartic.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "update.h" +#include "force.h" +#include "pair.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +BondQuartic::BondQuartic() +{ + TWO_1_3 = pow(2.0,(1.0/3.0)); +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +BondQuartic::~BondQuartic() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(b1); + memory->sfree(b2); + memory->sfree(rc); + memory->sfree(u0); + } +} + +/* ---------------------------------------------------------------------- */ + +void BondQuartic::compute(int eflag, int vflag) +{ + int i1,i2,n,m,type,factor,itype,jtype; + double delx,dely,delz,r,rsq,dr,r2,ra,rb,fforce,sr2,sr6,rfactor; + Pair::One one; + + energy = 0.0; + eng_vdwl = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **cutsq = force->pair->cutsq; + double **x = atom->x; + double **f = atom->f; + int **bondlist = neighbor->bondlist; + int nbondlist = neighbor->nbondlist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nbondlist; n++) { + + // skip bond if already broken + + if (bondlist[n][2] <= 0) continue; + + i1 = bondlist[n][0]; + i2 = bondlist[n][1]; + type = bondlist[n][2]; + + if (newton_bond) factor = 2; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + } + rfactor = 0.5*factor; + + delx = x[i1][0] - x[i2][0]; + dely = x[i1][1] - x[i2][1]; + delz = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx,&dely,&delz); + + rsq = delx*delx + dely*dely + delz*delz; + + // if bond breaks, set type to 0 + // both in temporary bondlist and permanent bond_type + // if this proc owns both atoms, + // negate bond_type twice if other atom stores it + // if other proc owns 2nd atom, other proc will also break bond + + if (rsq > rc[type]*rc[type]) { + bondlist[n][2] = 0; + for (m = 0; m < atom->num_bond[i1]; m++) + if (atom->bond_atom[i1][m] == atom->tag[i2]) + atom->bond_type[i1][m] = 0; + if (i2 < atom->nlocal) + for (m = 0; m < atom->num_bond[i2]; m++) + if (atom->bond_atom[i2][m] == atom->tag[i1]) + atom->bond_type[i2][m] = 0; + continue; + } + + // subtract out pairwise contribution from 2 atoms via pair->single() + // required since special_bond = 1,1,1 + + itype = atom->type[i1]; + jtype = atom->type[i2]; + + if (rsq < cutsq[itype][jtype]) { + force->pair->single(i1,i2,itype,jtype,rsq,1.0,1.0,eflag,one); + fforce = -one.fforce; + if (eflag) eng_vdwl -= one.eng_vdwl + one.eng_coul; + } else fforce = 0.0; + + // quartic bond + // 1st portion is from quartic term + // 2nd portion is from LJ term cut at 2^(1/6) with eps = sigma = 1.0 + + r = sqrt(rsq); + dr = r - rc[type]; + r2 = dr*dr; + ra = dr - b1[type]; + rb = dr - b2[type]; + fforce += -k[type]/r * (r2*(ra+rb) + 2.0*dr*ra*rb); + + if (rsq < TWO_1_3) { + sr2 = 1.0/rsq; + sr6 = sr2*sr2*sr2; + fforce += 48.0*sr6*(sr6-0.5)/rsq; + } + + if (eflag) { + energy += rfactor*(k[type]*r2*ra*rb + u0[type]); + if (rsq < TWO_1_3) energy += rfactor * (4.0*sr6*(sr6-1.0) + 1.0); + } + + // apply force to each of 2 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] += delx*fforce; + f[i1][1] += dely*fforce; + f[i1][2] += delz*fforce; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] -= delx*fforce; + f[i2][1] -= dely*fforce; + f[i2][2] -= delz*fforce; + } + + // virial contribution + + if (vflag) { + virial[0] += rfactor * delx*delx*fforce; + virial[1] += rfactor * dely*dely*fforce; + virial[2] += rfactor * delz*delz*fforce; + virial[3] += rfactor * delx*dely*fforce; + virial[4] += rfactor * delx*delz*fforce; + virial[5] += rfactor * dely*delz*fforce; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void BondQuartic::allocate() +{ + allocated = 1; + int n = atom->nbondtypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"bond:k"); + b1 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:b1"); + b2 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:b2"); + rc = (double *) memory->smalloc((n+1)*sizeof(double),"bond:rc"); + u0 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:u0"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"bond:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more types +------------------------------------------------------------------------- */ + +void BondQuartic::coeff(int narg, char **arg) +{ + if (narg != 6) error->all("Incorrect args for bond coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nbondtypes,ilo,ihi); + + double k_one = atof(arg[1]); + double b1_one = atof(arg[2]); + double b2_one = atof(arg[3]); + double rc_one = atof(arg[4]); + double u0_one = atof(arg[5]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + b1[i] = b1_one; + b2[i] = b2_one; + rc[i] = rc_one; + u0[i] = u0_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for bond coefficients"); +} + +/* ---------------------------------------------------------------------- + check if pair defined and special_bond settings are valid +------------------------------------------------------------------------- */ + +void BondQuartic::init_style() +{ + if (force->pair == NULL || force->pair->single_enable == 0) + error->all("Pair style does not support bond_style quartic"); + if (force->angle) + error->all("Bond style quartic cannot be used with 3,4-body interactions"); + if (force->dihedral) + error->all("Bond style quartic cannot be used with 3,4-body interactions"); + if (force->improper) + error->all("Bond style quartic cannot be used with 3,4-body interactions"); + + // special bonds must be 1 1 1 + + if (force->special_lj[1] != 1.0 || force->special_lj[2] != 1.0 || + force->special_lj[3] != 1.0) + error->all("Must use special bonds = 1,1,1 with bond style quartic"); +} + +/* ---------------------------------------------------------------------- + return an equilbrium bond length +------------------------------------------------------------------------- */ + +double BondQuartic::equilibrium_distance(int i) +{ + return 0.97; +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void BondQuartic::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&b1[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&b2[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&rc[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&u0[1],sizeof(double),atom->nbondtypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void BondQuartic::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->nbondtypes,fp); + fread(&b1[1],sizeof(double),atom->nbondtypes,fp); + fread(&b2[1],sizeof(double),atom->nbondtypes,fp); + fread(&rc[1],sizeof(double),atom->nbondtypes,fp); + fread(&u0[1],sizeof(double),atom->nbondtypes,fp); + } + MPI_Bcast(&k[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&b1[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&b2[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&rc[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&u0[1],atom->nbondtypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nbondtypes; i++) setflag[i] = 1; +} + + +/* ---------------------------------------------------------------------- */ + +void BondQuartic::single(int type, double rsq, int i, int j, double rfactor, + int eflag, double &fforce, double &eng) +{ + double r,dr,r2,ra,rb,sr2,sr6; + + fforce = eng = 0.0; + if (type <= 0) return; + + // subtract out pairwise contribution from 2 atoms via pair->single() + // required since special_bond = 1,1,1 + + int itype = atom->type[i]; + int jtype = atom->type[j]; + + if (rsq < force->pair->cutsq[itype][jtype]) { + Pair::One one; + force->pair->single(i,j,itype,jtype,rsq,1.0,1.0,eflag,one); + fforce = -one.fforce; + if (eflag) eng = -one.eng_coul - one.eng_vdwl; + } + + // quartic bond + // 1st portion is from quartic term + // 2nd portion is from LJ term cut at 2^(1/6) with eps = sigma = 1.0 + + r = sqrt(rsq); + dr = r - rc[type]; + r2 = dr*dr; + ra = dr - b1[type]; + rb = dr - b2[type]; + fforce += -k[type]/r * (r2*(ra+rb) + 2.0*dr*ra*rb); + + if (rsq < TWO_1_3) { + sr2 = 1.0/rsq; + sr6 = sr2*sr2*sr2; + fforce += 48.0*sr6*(sr6-0.5)/rsq; + } + + if (eflag) { + eng += rfactor*(k[type]*r2*ra*rb + u0[type]); + if (rsq < TWO_1_3) eng += rfactor * (4.0*sr6*(sr6-1.0) + 1.0); + } +} diff --git a/src/MOLECULE/bond_quartic.h b/src/MOLECULE/bond_quartic.h new file mode 100644 index 0000000000000000000000000000000000000000..7bc7f55f3a761d2c8252346d4cd3ff099992b5ac --- /dev/null +++ b/src/MOLECULE/bond_quartic.h @@ -0,0 +1,39 @@ +/* ----------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------ */ + +#ifndef BOND_QUARTIC_H +#define BOND_QUARTIC_H + +#include "stdio.h" +#include "bond.h" + +class BondQuartic : public Bond { + public: + BondQuartic(); + ~BondQuartic(); + void compute(int, int); + void coeff(int, char **); + void init_style(); + double equilibrium_distance(int); + void write_restart(FILE *); + void read_restart(FILE *); + void single(int, double, int, int, double, int, double &, double &); + + private: + double TWO_1_3; + double *k,*b1,*b2,*rc,*u0; + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/dihedral.cpp b/src/MOLECULE/dihedral.cpp new file mode 100644 index 0000000000000000000000000000000000000000..49de307e3673f82c53909426421cb5610ef6b34f --- /dev/null +++ b/src/MOLECULE/dihedral.cpp @@ -0,0 +1,42 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "dihedral.h" +#include "atom.h" +#include "error.h" + +/* ---------------------------------------------------------------------- + set dihedral contribution to Vdwl and Coulombic energy to 0.0 + DihedralCharmm will override this +------------------------------------------------------------------------- */ + +Dihedral::Dihedral() +{ + allocated = 0; + eng_vdwl = eng_coul = 0.0; + PI = 4.0*atan(1.0); +} + +/* ---------------------------------------------------------------------- + check if all coeffs are set +------------------------------------------------------------------------- */ + +void Dihedral::init() +{ + if (!allocated) error->all("Dihedral coeffs are not set"); + for (int i = 1; i <= atom->ndihedraltypes; i++) + if (setflag[i] == 0) error->all("All dihedral coeffs are not set"); + init_style(); +} + diff --git a/src/MOLECULE/dihedral_charmm.cpp b/src/MOLECULE/dihedral_charmm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..24198df7f1bb22b5d79bbfdef63a36e2cfda37ed --- /dev/null +++ b/src/MOLECULE/dihedral_charmm.cpp @@ -0,0 +1,450 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "mpi.h" +#include "math.h" +#include "stdlib.h" +#include "dihedral_charmm.h" +#include "atom.h" +#include "comm.h" +#include "neighbor.h" +#include "domain.h" +#include "force.h" +#include "pair_lj_charmm_coul_charmm.h" +#include "pair_lj_charmm_coul_charmm_implicit.h" +#include "pair_lj_charmm_coul_long.h" +#include "update.h" +#include "memory.h" +#include "error.h" + +#define TOLERANCE 0.05 +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +DihedralCharmm::~DihedralCharmm() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(multiplicity); + memory->sfree(shift); + memory->sfree(cos_shift); + memory->sfree(sin_shift); + memory->sfree(weight); + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralCharmm::compute(int eflag, int vflag) +{ + int i,m,n,i1,i2,i3,i4,type,factor; + double rfactor; + double vb1x,vb1y,vb1z,vb2x,vb2y; + double vb2z,vb2xm,vb2ym,vb2zm,vb3x,vb3y,vb3z; + double ax,ay,az,bx,by,bz,rasq,rbsq,rgsq,rg,rginv,ra2inv,rb2inv,rabinv; + double df,df1,ddf1,fg,hg,fga,hgb,gaa,gbb; + double dtfx,dtfy,dtfz,dtgx,dtgy,dtgz,dthx,dthy,dthz; + double c,s,p,sx1,sx2,sx12,sy1,sy2,sy12,sz1,sz2,sz12; + int itype,jtype; + double delx,dely,delz,rsq,r2inv,r6inv; + double fforce,forcecoul,forcelj,phicoul,philj; + + energy = 0.0; + eng_coul = eng_vdwl = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + double *q = atom->q; + int *atomtype = atom->type; + int **dihedrallist = neighbor->dihedrallist; + int ndihedrallist = neighbor->ndihedrallist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + double qqrd2e = force->qqrd2e; + + for (n = 0; n < ndihedrallist; n++) { + + i1 = dihedrallist[n][0]; + i2 = dihedrallist[n][1]; + i3 = dihedrallist[n][2]; + i4 = dihedrallist[n][3]; + type = dihedrallist[n][4]; + + if (newton_bond) factor = 4; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + if (i4 < nlocal) factor++; + } + rfactor = 0.25 * factor; + + // 1st bond + + vb1x = x[i1][0] - x[i2][0]; + vb1y = x[i1][1] - x[i2][1]; + vb1z = x[i1][2] - x[i2][2]; + domain->minimum_image(&vb1x,&vb1y,&vb1z); + + // 2nd bond + + vb2x = x[i3][0] - x[i2][0]; + vb2y = x[i3][1] - x[i2][1]; + vb2z = x[i3][2] - x[i2][2]; + domain->minimum_image(&vb2x,&vb2y,&vb2z); + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + domain->minimum_image(&vb2xm,&vb2ym,&vb2zm); + + // 3rd bond + + vb3x = x[i4][0] - x[i3][0]; + vb3y = x[i4][1] - x[i3][1]; + vb3z = x[i4][2] - x[i3][2]; + domain->minimum_image(&vb3x,&vb3y,&vb3z); + + ax = vb1y*vb2zm - vb1z*vb2ym; + ay = vb1z*vb2xm - vb1x*vb2zm; + az = vb1x*vb2ym - vb1y*vb2xm; + bx = vb3y*vb2zm - vb3z*vb2ym; + by = vb3z*vb2xm - vb3x*vb2zm; + bz = vb3x*vb2ym - vb3y*vb2xm; + + rasq = ax*ax + ay*ay + az*az; + rbsq = bx*bx + by*by + bz*bz; + rgsq = vb2xm*vb2xm + vb2ym*vb2ym + vb2zm*vb2zm; + rg = sqrt(rgsq); + + rginv = ra2inv = rb2inv = 0.0; + if (rg > 0) rginv = 1.0/rg; + if (rasq > 0) ra2inv = 1.0/rasq; + if (rbsq > 0) rb2inv = 1.0/rbsq; + rabinv = sqrt(ra2inv*rb2inv); + + c = (ax*bx + ay*by + az*bz)*rabinv; + s = rg*rabinv*(ax*vb3x + ay*vb3y + az*vb3z); + + // error check + + if (c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) { + int me; + MPI_Comm_rank(world,&me); + if (screen) { + fprintf(screen,"Dihedral problem: %d %d %d %d %d %d\n", + me,update->ntimestep, + atom->tag[i1],atom->tag[i2],atom->tag[i3],atom->tag[i4]); + fprintf(screen," 1st atom: %d %g %g %g\n", + me,x[i1][0],x[i1][1],x[i1][2]); + fprintf(screen," 2nd atom: %d %g %g %g\n", + me,x[i2][0],x[i2][1],x[i2][2]); + fprintf(screen," 3rd atom: %d %g %g %g\n", + me,x[i3][0],x[i3][1],x[i3][2]); + fprintf(screen," 4th atom: %d %g %g %g\n", + me,x[i4][0],x[i4][1],x[i4][2]); + } + } + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + m = multiplicity[type]; + p = 1.0; + df1 = 0.0; + + for (i = 0; i < m; i++) { + ddf1 = p*c - df1*s; + df1 = p*s + df1*c; + p = ddf1; + } + + p = p*cos_shift[type] + df1*sin_shift[type]; + df1 = df1*cos_shift[type] - ddf1*sin_shift[type]; + df1 *= -m; + p += 1.0; + + if (m == 0) { + p = 1.0 + cos_shift[type]; + df1 = 0.0; + } + + if (eflag) energy += rfactor * k[type] * p; + + fg = vb1x*vb2xm + vb1y*vb2ym + vb1z*vb2zm; + hg = vb3x*vb2xm + vb3y*vb2ym + vb3z*vb2zm; + fga = fg*ra2inv*rginv; + hgb = hg*rb2inv*rginv; + gaa = -ra2inv*rg; + gbb = rb2inv*rg; + + dtfx = gaa*ax; + dtfy = gaa*ay; + dtfz = gaa*az; + dtgx = fga*ax - hgb*bx; + dtgy = fga*ay - hgb*by; + dtgz = fga*az - hgb*bz; + dthx = gbb*bx; + dthy = gbb*by; + dthz = gbb*bz; + + df = k[type] * df1; + + sx1 = df*dtfx; + sy1 = df*dtfy; + sz1 = df*dtfz; + sx2 = -df*dtgx; + sy2 = -df*dtgy; + sz2 = -df*dtgz; + sx12 = df*dthx; + sy12 = df*dthy; + sz12 = df*dthz; + + // apply force to each of 4 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= sx1; + f[i1][1] -= sy1; + f[i1][2] -= sz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += sx2 + sx1; + f[i2][1] += sy2 + sy1; + f[i2][2] += sz2 + sz1; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] += sx12 - sx2; + f[i3][1] += sy12 - sy2; + f[i3][2] += sz12 - sz2; + } + + if (newton_bond || i4 < nlocal) { + f[i4][0] -= sx12; + f[i4][1] -= sy12; + f[i4][2] -= sz12; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (vb1x*sx1 + vb2x*sx2 + vb3x*sx12); + virial[1] -= rfactor * (vb1y*sy1 + vb2y*sy2 + vb3y*sy12); + virial[2] -= rfactor * (vb1z*sz1 + vb2z*sz2 + vb3z*sz12); + virial[3] -= rfactor * (vb1x*sy1 + vb2x*sy2 + vb3x*sy12); + virial[4] -= rfactor * (vb1x*sz1 + vb2x*sz2 + vb3x*sz12); + virial[5] -= rfactor * (vb1y*sz1 + vb2y*sz2 + vb3y*sz12); + } + + // 1-4 LJ and Coulomb interactions + // force, energy, and virial + + if (weight[type] > 0.0) { + + itype = atomtype[i1]; + jtype = atomtype[i4]; + + delx = x[i1][0] - x[i4][0]; + dely = x[i1][1] - x[i4][1]; + delz = x[i1][2] - x[i4][2]; + domain->minimum_image(&delx,&dely,&delz); + rsq = delx*delx + dely*dely + delz*delz; + r2inv = 1.0/rsq; + r6inv = r2inv*r2inv*r2inv; + + if (implicitflag) forcecoul = qqrd2e * q[i1]*q[i4]*r2inv; + else forcecoul = qqrd2e * q[i1]*q[i4]*sqrt(r2inv); + forcelj = r6inv * (lj14_1[itype][jtype]*r6inv - lj14_2[itype][jtype]); + fforce = weight[type] * (forcelj+forcecoul)*r2inv; + + if (eflag) { + phicoul = weight[type] * rfactor * forcecoul; + philj = r6inv * (lj14_3[itype][jtype]*r6inv - lj14_4[itype][jtype]); + philj = weight[type] * rfactor * philj; + eng_coul += phicoul; + eng_vdwl += philj; + } + + if (newton_bond || i1 < nlocal) { + f[i1][0] += delx*fforce; + f[i1][1] += dely*fforce; + f[i1][2] += delz*fforce; + } + if (newton_bond || i4 < nlocal) { + f[i4][0] -= delx*fforce; + f[i4][1] -= dely*fforce; + f[i4][2] -= delz*fforce; + } + + if (vflag) { + virial[0] += rfactor * delx*delx*fforce; + virial[1] += rfactor * dely*dely*fforce; + virial[2] += rfactor * delz*delz*fforce; + virial[3] += rfactor * delx*dely*fforce; + virial[4] += rfactor * delx*delz*fforce; + virial[5] += rfactor * dely*delz*fforce; + } + } + + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralCharmm::allocate() +{ + allocated = 1; + int n = atom->ndihedraltypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:k"); + multiplicity = (int *) + memory->smalloc((n+1)*sizeof(double),"dihedral:multiplicity"); + shift = (int *) + memory->smalloc((n+1)*sizeof(double),"dihedral:shift"); + cos_shift = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:cos_shift"); + sin_shift = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:sin_shift"); + weight = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:weight"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"dihedral:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void DihedralCharmm::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this dihedral style"); + if (narg != 5) error->all("Incorrect args for dihedral coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->ndihedraltypes,ilo,ihi); + + // require integer values of shift for backwards compatibility + // arbitrary phase angle shift could be allowed, but would break + // backwards compatibility and is probably not needed + + double k_one = atof(arg[1]); + int multiplicity_one = atoi(arg[2]); + int shift_one = atoi(arg[3]); + double weight_one = atof(arg[4]); + + if (multiplicity_one < 0) + error->all("Incorrect multiplicity arg for dihedral coefficients"); + if (weight_one < 0.0 || weight_one > 1.0) + error->all("Incorrect weight arg for dihedral coefficients"); + + double PI = 4.0*atan(1.0); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + shift[i] = shift_one; + cos_shift[i] = cos(PI*shift_one/180.0); + sin_shift[i] = sin(PI*shift_one/180.0); + multiplicity[i] = multiplicity_one; + weight[i] = weight_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for dihedral coefficients"); +} + +/* ---------------------------------------------------------------------- + error check and initialize all values needed for force computation +------------------------------------------------------------------------- */ + +void DihedralCharmm::init_style() +{ + // set local ptrs to LJ 14 arrays setup by pair + + Pair *anypair; + if (anypair = force->pair_match("lj/charmm/coul/charmm")) { + PairLJCharmmCoulCharmm *pair = (PairLJCharmmCoulCharmm *) anypair; + lj14_1 = pair->lj14_1; + lj14_2 = pair->lj14_2; + lj14_3 = pair->lj14_3; + lj14_4 = pair->lj14_4; + implicitflag = 0; + } else if (anypair = force->pair_match("lj/charmm/coul/charmm/implicit")) { + PairLJCharmmCoulCharmmImplicit *pair = + (PairLJCharmmCoulCharmmImplicit *) anypair; + lj14_1 = pair->lj14_1; + lj14_2 = pair->lj14_2; + lj14_3 = pair->lj14_3; + lj14_4 = pair->lj14_4; + implicitflag = 1; + } else if (anypair = force->pair_match("lj/charmm/coul/long")) { + PairLJCharmmCoulLong *pair = (PairLJCharmmCoulLong *) anypair; + lj14_1 = pair->lj14_1; + lj14_2 = pair->lj14_2; + lj14_3 = pair->lj14_3; + lj14_4 = pair->lj14_4; + implicitflag = 0; + } else error->all("Pair style is incompatible with DihedralCharmm"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void DihedralCharmm::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&multiplicity[1],sizeof(int),atom->ndihedraltypes,fp); + fwrite(&shift[1],sizeof(int),atom->ndihedraltypes,fp); + fwrite(&weight[1],sizeof(double),atom->ndihedraltypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void DihedralCharmm::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&multiplicity[1],sizeof(int),atom->ndihedraltypes,fp); + fread(&shift[1],sizeof(int),atom->ndihedraltypes,fp); + fread(&weight[1],sizeof(double),atom->ndihedraltypes,fp); + } + MPI_Bcast(&k[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&multiplicity[1],atom->ndihedraltypes,MPI_INT,0,world); + MPI_Bcast(&shift[1],atom->ndihedraltypes,MPI_INT,0,world); + MPI_Bcast(&weight[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + + double PI = 4.0*atan(1.0); + for (int i = 1; i <= atom->ndihedraltypes; i++) { + setflag[i] = 1; + cos_shift[i] = cos(PI*shift[i]/180.0); + sin_shift[i] = sin(PI*shift[i]/180.0); + } +} diff --git a/src/MOLECULE/dihedral_charmm.h b/src/MOLECULE/dihedral_charmm.h new file mode 100644 index 0000000000000000000000000000000000000000..107818633d2cfb9110d2e532405a641236b984d2 --- /dev/null +++ b/src/MOLECULE/dihedral_charmm.h @@ -0,0 +1,39 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DIHEDRAL_CHARMM_H +#define DIHEDRAL_CHARMM_H + +#include "stdio.h" +#include "dihedral.h" + +class DihedralCharmm : public Dihedral { + public: + DihedralCharmm() {} + ~DihedralCharmm(); + void compute(int, int); + void coeff(int, int, char **); + void init_style(); + void write_restart(FILE *); + void read_restart(FILE *); + + private: + double *k,*weight,*cos_shift,*sin_shift; + int *multiplicity,*shift; + double **lj14_1,**lj14_2,**lj14_3,**lj14_4; + int implicitflag; + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/dihedral_harmonic.cpp b/src/MOLECULE/dihedral_harmonic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8762262bdf632db45a35ad192ea238ce6cfa05ee --- /dev/null +++ b/src/MOLECULE/dihedral_harmonic.cpp @@ -0,0 +1,356 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "mpi.h" +#include "math.h" +#include "stdlib.h" +#include "dihedral_harmonic.h" +#include "atom.h" +#include "comm.h" +#include "neighbor.h" +#include "domain.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "error.h" + +#define TOLERANCE 0.05 +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +DihedralHarmonic::~DihedralHarmonic() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(sign); + memory->sfree(multiplicity); + memory->sfree(cos_shift); + memory->sfree(sin_shift); + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralHarmonic::compute(int eflag, int vflag) +{ + int i,m,n,i1,i2,i3,i4,type,factor; + double rfactor; + double vb1x,vb1y,vb1z,vb2x,vb2y; + double vb2z,vb2xm,vb2ym,vb2zm,vb3x,vb3y,vb3z; + double ax,ay,az,bx,by,bz,rasq,rbsq,rgsq,rg,rginv,ra2inv,rb2inv,rabinv; + double df,df1,ddf1,fg,hg,fga,hgb,gaa,gbb; + double dtfx,dtfy,dtfz,dtgx,dtgy,dtgz,dthx,dthy,dthz; + double c,s,p,sx1,sx2,sx12,sy1,sy2,sy12,sz1,sz2,sz12; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **dihedrallist = neighbor->dihedrallist; + int ndihedrallist = neighbor->ndihedrallist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < ndihedrallist; n++) { + + i1 = dihedrallist[n][0]; + i2 = dihedrallist[n][1]; + i3 = dihedrallist[n][2]; + i4 = dihedrallist[n][3]; + type = dihedrallist[n][4]; + + if (newton_bond) factor = 4; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + if (i4 < nlocal) factor++; + } + rfactor = 0.25 * factor; + + // 1st bond + + vb1x = x[i1][0] - x[i2][0]; + vb1y = x[i1][1] - x[i2][1]; + vb1z = x[i1][2] - x[i2][2]; + domain->minimum_image(&vb1x,&vb1y,&vb1z); + + // 2nd bond + + vb2x = x[i3][0] - x[i2][0]; + vb2y = x[i3][1] - x[i2][1]; + vb2z = x[i3][2] - x[i2][2]; + domain->minimum_image(&vb2x,&vb2y,&vb2z); + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + domain->minimum_image(&vb2xm,&vb2ym,&vb2zm); + + // 3rd bond + + vb3x = x[i4][0] - x[i3][0]; + vb3y = x[i4][1] - x[i3][1]; + vb3z = x[i4][2] - x[i3][2]; + domain->minimum_image(&vb3x,&vb3y,&vb3z); + + // c,s calculation + + ax = vb1y*vb2zm - vb1z*vb2ym; + ay = vb1z*vb2xm - vb1x*vb2zm; + az = vb1x*vb2ym - vb1y*vb2xm; + bx = vb3y*vb2zm - vb3z*vb2ym; + by = vb3z*vb2xm - vb3x*vb2zm; + bz = vb3x*vb2ym - vb3y*vb2xm; + + rasq = ax*ax + ay*ay + az*az; + rbsq = bx*bx + by*by + bz*bz; + rgsq = vb2xm*vb2xm + vb2ym*vb2ym + vb2zm*vb2zm; + rg = sqrt(rgsq); + + rginv = ra2inv = rb2inv = 0.0; + if (rg > 0) rginv = 1.0/rg; + if (rasq > 0) ra2inv = 1.0/rasq; + if (rbsq > 0) rb2inv = 1.0/rbsq; + rabinv = sqrt(ra2inv*rb2inv); + + c = (ax*bx + ay*by + az*bz)*rabinv; + s = rg*rabinv*(ax*vb3x + ay*vb3y + az*vb3z); + + // error check + + if (c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) { + int me; + MPI_Comm_rank(world,&me); + if (screen) { + fprintf(screen,"Dihedral problem: %d %d %d %d %d %d\n", + me,update->ntimestep, + atom->tag[i1],atom->tag[i2],atom->tag[i3],atom->tag[i4]); + fprintf(screen," 1st atom: %d %g %g %g\n", + me,x[i1][0],x[i1][1],x[i1][2]); + fprintf(screen," 2nd atom: %d %g %g %g\n", + me,x[i2][0],x[i2][1],x[i2][2]); + fprintf(screen," 3rd atom: %d %g %g %g\n", + me,x[i3][0],x[i3][1],x[i3][2]); + fprintf(screen," 4th atom: %d %g %g %g\n", + me,x[i4][0],x[i4][1],x[i4][2]); + } + } + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + m = multiplicity[type]; + p = 1.0; + df1 = 0.0; + + for (i = 0; i < m; i++) { + ddf1 = p*c - df1*s; + df1 = p*s + df1*c; + p = ddf1; + } + + p = p*cos_shift[type] + df1*sin_shift[type]; + df1 = df1*cos_shift[type] - ddf1*sin_shift[type]; + df1 *= -m; + p += 1.0; + + if (m == 0) { + p = 1.0 + cos_shift[type]; + df1 = 0.0; + } + + if (eflag) energy += rfactor * k[type] * p; + + fg = vb1x*vb2xm + vb1y*vb2ym + vb1z*vb2zm; + hg = vb3x*vb2xm + vb3y*vb2ym + vb3z*vb2zm; + fga = fg*ra2inv*rginv; + hgb = hg*rb2inv*rginv; + gaa = -ra2inv*rg; + gbb = rb2inv*rg; + + dtfx = gaa*ax; + dtfy = gaa*ay; + dtfz = gaa*az; + dtgx = fga*ax - hgb*bx; + dtgy = fga*ay - hgb*by; + dtgz = fga*az - hgb*bz; + dthx = gbb*bx; + dthy = gbb*by; + dthz = gbb*bz; + + df = k[type] * df1; + + sx1 = df*dtfx; + sy1 = df*dtfy; + sz1 = df*dtfz; + sx2 = -df*dtgx; + sy2 = -df*dtgy; + sz2 = -df*dtgz; + sx12 = df*dthx; + sy12 = df*dthy; + sz12 = df*dthz; + + // apply force to each of 4 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= sx1; + f[i1][1] -= sy1; + f[i1][2] -= sz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += sx2 + sx1; + f[i2][1] += sy2 + sy1; + f[i2][2] += sz2 + sz1; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] += sx12 - sx2; + f[i3][1] += sy12 - sy2; + f[i3][2] += sz12 - sz2; + } + + if (newton_bond || i4 < nlocal) { + f[i4][0] -= sx12; + f[i4][1] -= sy12; + f[i4][2] -= sz12; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (vb1x*sx1 + vb2x*sx2 + vb3x*sx12); + virial[1] -= rfactor * (vb1y*sy1 + vb2y*sy2 + vb3y*sy12); + virial[2] -= rfactor * (vb1z*sz1 + vb2z*sz2 + vb3z*sz12); + virial[3] -= rfactor * (vb1x*sy1 + vb2x*sy2 + vb3x*sy12); + virial[4] -= rfactor * (vb1x*sz1 + vb2x*sz2 + vb3x*sz12); + virial[5] -= rfactor * (vb1y*sz1 + vb2y*sz2 + vb3y*sz12); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralHarmonic::allocate() +{ + allocated = 1; + int n = atom->ndihedraltypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:k"); + sign = (int *) memory->smalloc((n+1)*sizeof(double),"dihedral:sign"); + multiplicity = (int *) + memory->smalloc((n+1)*sizeof(double),"dihedral:multiplicity"); + cos_shift = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:cos_shift"); + sin_shift = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:sin_shift"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"dihedral:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void DihedralHarmonic::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this dihedral style"); + if (narg != 4) error->all("Incorrect args for dihedral coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->ndihedraltypes,ilo,ihi); + + double k_one = atof(arg[1]); + int sign_one = atoi(arg[2]); + int multiplicity_one = atoi(arg[3]); + + // require sign = +/- 1 for backwards compatibility + // arbitrary phase angle shift could be allowed, but would break + // backwards compatibility and is probably not needed + + if (sign_one != -1 && sign_one != 1) + error->all("Incorrect sign arg for dihedral coefficients"); + if (multiplicity_one < 0) + error->all("Incorrect multiplicity arg for dihedral coefficients"); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + sign[i] = sign_one; + if (sign[i] == 1) { + cos_shift[i] = 1; + sin_shift[i] = 0; + } else { + cos_shift[i] = -1; + sin_shift[i] = 0; + } + multiplicity[i] = multiplicity_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for dihedral coefficients"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void DihedralHarmonic::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&sign[1],sizeof(int),atom->ndihedraltypes,fp); + fwrite(&multiplicity[1],sizeof(int),atom->ndihedraltypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void DihedralHarmonic::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&sign[1],sizeof(int),atom->ndihedraltypes,fp); + fread(&multiplicity[1],sizeof(int),atom->ndihedraltypes,fp); + } + MPI_Bcast(&k[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&sign[1],atom->ndihedraltypes,MPI_INT,0,world); + MPI_Bcast(&multiplicity[1],atom->ndihedraltypes,MPI_INT,0,world); + + for (int i = 1; i <= atom->ndihedraltypes; i++) { + setflag[i] = 1; + if (sign[i] == 1) { + cos_shift[i] = 1; + sin_shift[i] = 0; + } else { + cos_shift[i] = -1; + sin_shift[i] = 0; + } + } +} diff --git a/src/MOLECULE/dihedral_harmonic.h b/src/MOLECULE/dihedral_harmonic.h new file mode 100644 index 0000000000000000000000000000000000000000..e0ddac0882a06249e69d49e3e86e934d733ab52b --- /dev/null +++ b/src/MOLECULE/dihedral_harmonic.h @@ -0,0 +1,36 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DIHEDRAL_HARMONIC_H +#define DIHEDRAL_HARMONIC_H + +#include "stdio.h" +#include "dihedral.h" + +class DihedralHarmonic : public Dihedral { + public: + DihedralHarmonic() {} + ~DihedralHarmonic(); + void compute(int, int); + void coeff(int, int, char **); + void write_restart(FILE *); + void read_restart(FILE *); + + private: + double *k,*cos_shift,*sin_shift; + int *sign,*multiplicity; + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/dihedral_helix.cpp b/src/MOLECULE/dihedral_helix.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ba6f7c7e332fb017c49ddb9fc739d26711b47813 --- /dev/null +++ b/src/MOLECULE/dihedral_helix.cpp @@ -0,0 +1,340 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Naveen Michaud-Agrawal (Johns Hopkins U) and + Mark Stevens (Sandia) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "mpi.h" +#include "dihedral_helix.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "error.h" + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +#define TOLERANCE 0.05 +#define SMALL 0.001 +#define SMALLER 0.00001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +DihedralHelix::~DihedralHelix() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(aphi); + memory->sfree(bphi); + memory->sfree(cphi); + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralHelix::compute(int eflag, int vflag) +{ + int n,i1,i2,i3,i4,type,factor; + double rfactor; + double vb1x,vb1y,vb1z,vb2x,vb2y; + double vb2z,vb2xm,vb2ym,vb2zm,vb3x,vb3y,vb3z,sb1; + double sb2,sb3,rb1,rb3,c0,b1mag2,b1mag,b2mag2; + double b2mag,b3mag2,b3mag,ctmp,r12c1,c1mag,r12c2; + double c2mag,sc1,sc2,s1,s12,c,p,pd,a,a11,a22; + double a33,a12,a13,a23,sx1,sx2,sx12,sy1,sy2,sy12; + double sz1,sz2,sz12,s2,cx,cy,cz,cmag,dx,phi,si,siinv,sin2; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **dihedrallist = neighbor->dihedrallist; + int ndihedrallist = neighbor->ndihedrallist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < ndihedrallist; n++) { + + i1 = dihedrallist[n][0]; + i2 = dihedrallist[n][1]; + i3 = dihedrallist[n][2]; + i4 = dihedrallist[n][3]; + type = dihedrallist[n][4]; + + if (newton_bond) factor = 4; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + if (i4 < nlocal) factor++; + } + rfactor = 0.25 * factor; + + // 1st bond + + vb1x = x[i1][0] - x[i2][0]; + vb1y = x[i1][1] - x[i2][1]; + vb1z = x[i1][2] - x[i2][2]; + domain->minimum_image(&vb1x,&vb1y,&vb1z); + + // 2nd bond + + vb2x = x[i3][0] - x[i2][0]; + vb2y = x[i3][1] - x[i2][1]; + vb2z = x[i3][2] - x[i2][2]; + domain->minimum_image(&vb2x,&vb2y,&vb2z); + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + domain->minimum_image(&vb2xm,&vb2ym,&vb2zm); + + // 3rd bond + + vb3x = x[i4][0] - x[i3][0]; + vb3y = x[i4][1] - x[i3][1]; + vb3z = x[i4][2] - x[i3][2]; + domain->minimum_image(&vb3x,&vb3y,&vb3z); + + // c0 calculation + + sb1 = 1.0 / (vb1x*vb1x + vb1y*vb1y + vb1z*vb1z); + sb2 = 1.0 / (vb2x*vb2x + vb2y*vb2y + vb2z*vb2z); + sb3 = 1.0 / (vb3x*vb3x + vb3y*vb3y + vb3z*vb3z); + + rb1 = sqrt(sb1); + rb3 = sqrt(sb3); + + c0 = (vb1x*vb3x + vb1y*vb3y + vb1z*vb3z) * rb1*rb3; + + // 1st and 2nd angle + + b1mag2 = vb1x*vb1x + vb1y*vb1y + vb1z*vb1z; + b1mag = sqrt(b1mag2); + b2mag2 = vb2x*vb2x + vb2y*vb2y + vb2z*vb2z; + b2mag = sqrt(b2mag2); + b3mag2 = vb3x*vb3x + vb3y*vb3y + vb3z*vb3z; + b3mag = sqrt(b3mag2); + + ctmp = vb1x*vb2x + vb1y*vb2y + vb1z*vb2z; + r12c1 = 1.0 / (b1mag*b2mag); + c1mag = ctmp * r12c1; + + ctmp = vb2xm*vb3x + vb2ym*vb3y + vb2zm*vb3z; + r12c2 = 1.0 / (b2mag*b3mag); + c2mag = ctmp * r12c2; + + // cos and sin of 2 angles and final c + + sin2 = MAX(1.0 - c1mag*c1mag,0.0); + sc1 = sqrt(sin2); + if (sc1 < SMALL) sc1 = SMALL; + sc1 = 1.0/sc1; + + sin2 = MAX(1.0 - c2mag*c2mag,0.0); + sc2 = sqrt(sin2); + if (sc2 < SMALL) sc2 = SMALL; + sc2 = 1.0/sc2; + + s1 = sc1 * sc1; + s2 = sc2 * sc2; + s12 = sc1 * sc2; + c = (c0 + c1mag*c2mag) * s12; + + cx = vb1y*vb2z - vb1z*vb2y; + cy = vb1z*vb2x - vb1x*vb2z; + cz = vb1x*vb2y - vb1y*vb2x; + cmag = sqrt(cx*cx + cy*cy + cz*cz); + dx = (cx*vb3x + cy*vb3y + cz*vb3z)/cmag/b3mag; + + // error check + + if (c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) { + int me; + MPI_Comm_rank(world,&me); + if (screen) { + fprintf(screen,"Dihedral problem: %d %d %d %d %d %d\n", + me,update->ntimestep, + atom->tag[i1],atom->tag[i2],atom->tag[i3],atom->tag[i4]); + fprintf(screen," 1st atom: %d %g %g %g\n", + me,x[i1][0],x[i1][1],x[i1][2]); + fprintf(screen," 2nd atom: %d %g %g %g\n", + me,x[i2][0],x[i2][1],x[i2][2]); + fprintf(screen," 3rd atom: %d %g %g %g\n", + me,x[i3][0],x[i3][1],x[i3][2]); + fprintf(screen," 4th atom: %d %g %g %g\n", + me,x[i4][0],x[i4][1],x[i4][2]); + } + } + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + phi = acos(c); + if (dx < 0.0) phi *= -1.0; + si = sin(phi); + if (fabs(si) < SMALLER) si = SMALLER; + siinv = 1.0/si; + + p = aphi[type]*(1.0 - c) + bphi[type]*(1.0 + cos(3.0*phi)) + + cphi[type]*(1.0 + cos(phi + 0.25*PI)); + pd = -aphi[type] + 3.0*bphi[type]*sin(3.0*phi)*siinv + + cphi[type]*sin(phi + 0.25*PI)*siinv; + + if (eflag) energy += rfactor * p; + + a = pd; + c = c * a; + s12 = s12 * a; + a11 = -c*sb1*s1; + a22 = sb2 * (2.0*c0*s12 - c*(s1+s2)); + a33 = -c*sb3*s2; + a12 = r12c1 * (c1mag*c*s1 + c2mag*s12); + a13 = rb1*rb3*s12; + a23 = r12c2 * (-c2mag*c*s2 - c1mag*s12); + + sx1 = a11*vb1x + a12*vb2x + a13*vb3x; + sx2 = a12*vb1x + a22*vb2x + a23*vb3x; + sx12 = a13*vb1x + a23*vb2x + a33*vb3x; + sy1 = a11*vb1y + a12*vb2y + a13*vb3y; + sy2 = a12*vb1y + a22*vb2y + a23*vb3y; + sy12 = a13*vb1y + a23*vb2y + a33*vb3y; + sz1 = a11*vb1z + a12*vb2z + a13*vb3z; + sz2 = a12*vb1z + a22*vb2z + a23*vb3z; + sz12 = a13*vb1z + a23*vb2z + a33*vb3z; + + // apply force to each of 4 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= sx1; + f[i1][1] -= sy1; + f[i1][2] -= sz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += sx2 + sx1; + f[i2][1] += sy2 + sy1; + f[i2][2] += sz2 + sz1; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] += sx12 - sx2; + f[i3][1] += sy12 - sy2; + f[i3][2] += sz12 - sz2; + } + + if (newton_bond || i4 < nlocal) { + f[i4][0] -= sx12; + f[i4][1] -= sy12; + f[i4][2] -= sz12; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (vb1x*sx1 + vb2x*sx2 + vb3x*sx12); + virial[1] -= rfactor * (vb1y*sy1 + vb2y*sy2 + vb3y*sy12); + virial[2] -= rfactor * (vb1z*sz1 + vb2z*sz2 + vb3z*sz12); + virial[3] -= rfactor * (vb1x*sy1 + vb2x*sy2 + vb3x*sy12); + virial[4] -= rfactor * (vb1x*sz1 + vb2x*sz2 + vb3x*sz12); + virial[5] -= rfactor * (vb1y*sz1 + vb2y*sz2 + vb3y*sz12); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralHelix::allocate() +{ + allocated = 1; + int n = atom->ndihedraltypes; + + aphi = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:aphi"); + bphi = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:bphi"); + cphi = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:cphi"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"dihedral:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs from one line in input script +------------------------------------------------------------------------- */ + +void DihedralHelix::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this dihedral style"); + if (narg != 4) error->all("Incorrect args for dihedral coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->ndihedraltypes,ilo,ihi); + + double aphi_one = atof(arg[1]); + double bphi_one = atof(arg[2]); + double cphi_one = atof(arg[3]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + aphi[i] = aphi_one; + bphi[i] = bphi_one; + cphi[i] = cphi_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for dihedral coefficients"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void DihedralHelix::write_restart(FILE *fp) +{ + fwrite(&aphi[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&bphi[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&cphi[1],sizeof(double),atom->ndihedraltypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void DihedralHelix::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&aphi[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&bphi[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&cphi[1],sizeof(double),atom->ndihedraltypes,fp); + } + MPI_Bcast(&aphi[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&bphi[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&cphi[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->ndihedraltypes; i++) setflag[i] = 1; +} diff --git a/src/MOLECULE/dihedral_helix.h b/src/MOLECULE/dihedral_helix.h new file mode 100644 index 0000000000000000000000000000000000000000..85481b4318e489f0df403b2e27b82e1e2f7a6d49 --- /dev/null +++ b/src/MOLECULE/dihedral_helix.h @@ -0,0 +1,35 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DIHEDRAL_HELIX_H +#define DIHEDRAL_HELIX_H + +#include "stdio.h" +#include "dihedral.h" + +class DihedralHelix : public Dihedral { + public: + DihedralHelix() {} + ~DihedralHelix(); + void compute(int, int); + void coeff(int, int, char **); + void write_restart(FILE *); + void read_restart(FILE *); + + private: + double *aphi,*bphi,*cphi; + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/dihedral_hybrid.cpp b/src/MOLECULE/dihedral_hybrid.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dea4ce72fcbe71762a14bd4db678e689fb4ea291 --- /dev/null +++ b/src/MOLECULE/dihedral_hybrid.cpp @@ -0,0 +1,259 @@ +/* ----------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "string.h" +#include "dihedral_hybrid.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define EXTRA 1000 + +/* ---------------------------------------------------------------------- + set all global defaults +------------------------------------------------------------------------- */ + +DihedralHybrid::DihedralHybrid() +{ + nstyles = 0; +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +DihedralHybrid::~DihedralHybrid() +{ + if (nstyles) { + for (int i = 0; i < nstyles; i++) delete styles[i]; + delete [] styles; + for (int i = 0; i < nstyles; i++) delete [] keywords[i]; + delete [] keywords; + } + + if (allocated) { + memory->sfree(setflag); + memory->sfree(map); + delete [] ndihedrallist; + delete [] maxdihedral; + for (int i = 0; i < nstyles; i++) + memory->destroy_2d_int_array(dihedrallist[i]); + delete [] dihedrallist; + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralHybrid::compute(int eflag, int vflag) +{ + int i,m,n; + + // save ptrs to original dihedrallist + + int ndihedrallist_orig = neighbor->ndihedrallist; + int **dihedrallist_orig = neighbor->dihedrallist; + + // if this is re-neighbor step, create sub-style dihedrallists + // ndihedrallist[] = length of each sub-style list + // realloc sub-style dihedrallist if necessary + // load sub-style dihedrallist with 5 values from original dihedrallist + + if (neighbor->ago == 0) { + for (m = 0; m < nstyles; m++) ndihedrallist[m] = 0; + for (i = 0; i < ndihedrallist_orig; i++) + ndihedrallist[map[dihedrallist_orig[i][4]]]++; + for (m = 0; m < nstyles; m++) { + if (ndihedrallist[m] > maxdihedral[m]) { + memory->destroy_2d_int_array(dihedrallist[m]); + maxdihedral[m] = ndihedrallist[m] + EXTRA; + dihedrallist[m] = (int **) + memory->create_2d_int_array(maxdihedral[m],5, + "dihedral_hybrid:dihedrallist"); + } + ndihedrallist[m] = 0; + } + for (i = 0; i < ndihedrallist_orig; i++) { + m = map[dihedrallist_orig[i][4]]; + n = ndihedrallist[m]; + dihedrallist[m][n][0] = dihedrallist_orig[i][0]; + dihedrallist[m][n][1] = dihedrallist_orig[i][1]; + dihedrallist[m][n][2] = dihedrallist_orig[i][2]; + dihedrallist[m][n][3] = dihedrallist_orig[i][3]; + dihedrallist[m][n][4] = dihedrallist_orig[i][4]; + ndihedrallist[m]++; + } + } + + // call each sub-style's compute function + // must set neighbor->dihedrallist to sub-style dihedrallist before call + // accumulate sub-style energy,virial in hybrid's energy,virial + + energy = 0.0; + eng_vdwl = eng_coul = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + for (m = 0; m < nstyles; m++) { + if (styles[m] == NULL) continue; + neighbor->ndihedrallist = ndihedrallist[m]; + neighbor->dihedrallist = dihedrallist[m]; + styles[m]->compute(eflag,vflag); + if (eflag) { + energy += styles[m]->energy; + eng_vdwl += styles[m]->eng_vdwl; + eng_coul += styles[m]->eng_coul; + } + if (vflag) for (n = 0; n < 6; n++) virial[n] += styles[m]->virial[n]; + } + + // restore ptrs to original dihedrallist + + neighbor->ndihedrallist = ndihedrallist_orig; + neighbor->dihedrallist = dihedrallist_orig; +} + +/* ---------------------------------------------------------------------- */ + +void DihedralHybrid::allocate() +{ + allocated = 1; + int n = atom->ndihedraltypes; + + map = (int *) memory->smalloc((n+1)*sizeof(int),"dihedral:map"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"dihedral:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; + + ndihedrallist = new int[nstyles]; + maxdihedral = new int[nstyles]; + dihedrallist = new int**[nstyles]; + for (int m = 0; m < nstyles; m++) maxdihedral[m] = 0; + for (int m = 0; m < nstyles; m++) dihedrallist[m] = NULL; +} + +/* ---------------------------------------------------------------------- + create one dihedral style for each arg in list +------------------------------------------------------------------------- */ + +void DihedralHybrid::settings(int narg, char **arg) +{ + nstyles = narg; + styles = new Dihedral*[nstyles]; + keywords = new char*[nstyles]; + + for (int m = 0; m < nstyles; m++) { + for (int i = 0; i < m; i++) + if (strcmp(arg[m],arg[i]) == 0) + error->all("Dihedral style hybrid cannot use same dihedral style twice"); + if (strcmp(arg[m],"hybrid") == 0) + error->all("Dihedral style hybrid cannot have hybrid as an argument"); + styles[m] = force->new_dihedral(arg[m]); + keywords[m] = new char[strlen(arg[m])+1]; + strcpy(keywords[m],arg[m]); + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +---------------------------------------------------------------------- */ + +void DihedralHybrid::coeff(int which, int narg, char **arg) +{ + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->ndihedraltypes,ilo,ihi); + + // 2nd arg = dihedral style name (harmonic, etc) + + int m; + for (m = 0; m < nstyles; m++) + if (strcmp(arg[1],keywords[m]) == 0) break; + if (m == nstyles) error->all("Dihedral coeff for hybrid has invalid style"); + + // set low-level coefficients for each dihedraltype + // replace 2nd arg with i, call coeff() with no 1st arg + // if sub-style is NULL for "none", still set setflag + + for (int i = ilo; i <= ihi; i++) { + sprintf(arg[1],"%d",i); + map[i] = m; + if (styles[m]) styles[m]->coeff(which,narg-1,&arg[1]); + setflag[i] = 1; + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralHybrid::init_style() +{ + for (int m = 0; m < nstyles; m++) + if (styles[m]) styles[m]->init_style(); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void DihedralHybrid::write_restart(FILE *fp) +{ + fwrite(&nstyles,sizeof(int),1,fp); + + int n; + for (int m = 0; m < nstyles; m++) { + n = strlen(keywords[m]) + 1; + fwrite(&n,sizeof(int),1,fp); + fwrite(keywords[m],sizeof(char),n,fp); + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void DihedralHybrid::read_restart(FILE *fp) +{ + allocate(); + + int me = comm->me; + if (me == 0) fread(&nstyles,sizeof(int),1,fp); + MPI_Bcast(&nstyles,1,MPI_INT,0,world); + styles = new Dihedral*[nstyles]; + keywords = new char*[nstyles]; + + int n; + for (int m = 0; m < nstyles; m++) { + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + keywords[m] = new char[n]; + if (me == 0) fread(keywords[m],sizeof(char),n,fp); + MPI_Bcast(keywords[m],n,MPI_CHAR,0,world); + styles[m] = force->new_dihedral(keywords[m]); + } +} + +/* ---------------------------------------------------------------------- + memory usage +------------------------------------------------------------------------- */ + +int DihedralHybrid::memory_usage() +{ + int bytes = 0; + for (int m = 0; m < nstyles; m++) bytes += maxdihedral[m]*5 * sizeof(int); + for (int m = 0; m < nstyles; m++) + if (styles[m]) bytes += styles[m]->memory_usage(); + return bytes; +} diff --git a/src/MOLECULE/dihedral_hybrid.h b/src/MOLECULE/dihedral_hybrid.h new file mode 100644 index 0000000000000000000000000000000000000000..e6cc4713425f3a953e177d9b7e9eb8ef6cfa4021 --- /dev/null +++ b/src/MOLECULE/dihedral_hybrid.h @@ -0,0 +1,45 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DIHEDRAL_HYBRID_H +#define DIHEDRAL_HYBRID_H + +#include "stdio.h" +#include "dihedral.h" + +class DihedralHybrid : public Dihedral { + public: + DihedralHybrid(); + ~DihedralHybrid(); + void compute(int, int); + void settings(int, char **); + void coeff(int, int, char **); + void init_style(); + void write_restart(FILE *); + void read_restart(FILE *); + int memory_usage(); + + private: + int nstyles; // # of different dihedral styles + Dihedral **styles; // class list for each Dihedral style + char **keywords; // keyword for each dihedral style + int *map; // which style each dihedral type points to + + int *ndihedrallist; // # of dihedrals in sub-style dihedrallists + int *maxdihedral; // max # of dihedrals sub-style lists can store + int ***dihedrallist; // dihedrallist for each sub-style + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/dihedral_multi_harmonic.cpp b/src/MOLECULE/dihedral_multi_harmonic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b3541f95210019d906f887e7930e50c04053f751 --- /dev/null +++ b/src/MOLECULE/dihedral_multi_harmonic.cpp @@ -0,0 +1,339 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Mathias Puetz (SNL) and friends +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "dihedral_multi_harmonic.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "error.h" + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +#define TOLERANCE 0.05 +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +DihedralMultiHarmonic::~DihedralMultiHarmonic() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(a1); + memory->sfree(a2); + memory->sfree(a3); + memory->sfree(a4); + memory->sfree(a5); + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralMultiHarmonic::compute(int eflag, int vflag) +{ + int n,i1,i2,i3,i4,type,factor; + double rfactor; + double vb1x,vb1y,vb1z,vb2x,vb2y; + double vb2z,vb2xm,vb2ym,vb2zm,vb3x,vb3y,vb3z,sb1; + double sb2,sb3,rb1,rb3,c0,b1mag2,b1mag,b2mag2; + double b2mag,b3mag2,b3mag,ctmp,r12c1,c1mag,r12c2; + double c2mag,sc1,sc2,s1,s12,c,p,pd,a,a11,a22; + double a33,a12,a13,a23,sx1,sx2,sx12,sy1,sy2,sy12; + double sz1,sz2,sz12,s2,sin2; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **dihedrallist = neighbor->dihedrallist; + int ndihedrallist = neighbor->ndihedrallist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < ndihedrallist; n++) { + + i1 = dihedrallist[n][0]; + i2 = dihedrallist[n][1]; + i3 = dihedrallist[n][2]; + i4 = dihedrallist[n][3]; + type = dihedrallist[n][4]; + + if (newton_bond) factor = 4; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + if (i4 < nlocal) factor++; + } + rfactor = 0.25 * factor; + + // 1st bond + + vb1x = x[i1][0] - x[i2][0]; + vb1y = x[i1][1] - x[i2][1]; + vb1z = x[i1][2] - x[i2][2]; + domain->minimum_image(&vb1x,&vb1y,&vb1z); + + // 2nd bond + + vb2x = x[i3][0] - x[i2][0]; + vb2y = x[i3][1] - x[i2][1]; + vb2z = x[i3][2] - x[i2][2]; + domain->minimum_image(&vb2x,&vb2y,&vb2z); + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + domain->minimum_image(&vb2xm,&vb2ym,&vb2zm); + + // 3rd bond + + vb3x = x[i4][0] - x[i3][0]; + vb3y = x[i4][1] - x[i3][1]; + vb3z = x[i4][2] - x[i3][2]; + domain->minimum_image(&vb3x,&vb3y,&vb3z); + + // c0 calculation + + sb1 = 1.0 / (vb1x*vb1x + vb1y*vb1y + vb1z*vb1z); + sb2 = 1.0 / (vb2x*vb2x + vb2y*vb2y + vb2z*vb2z); + sb3 = 1.0 / (vb3x*vb3x + vb3y*vb3y + vb3z*vb3z); + + rb1 = sqrt(sb1); + rb3 = sqrt(sb3); + + c0 = (vb1x*vb3x + vb1y*vb3y + vb1z*vb3z) * rb1*rb3; + + // 1st and 2nd angle + + b1mag2 = vb1x*vb1x + vb1y*vb1y + vb1z*vb1z; + b1mag = sqrt(b1mag2); + b2mag2 = vb2x*vb2x + vb2y*vb2y + vb2z*vb2z; + b2mag = sqrt(b2mag2); + b3mag2 = vb3x*vb3x + vb3y*vb3y + vb3z*vb3z; + b3mag = sqrt(b3mag2); + + ctmp = vb1x*vb2x + vb1y*vb2y + vb1z*vb2z; + r12c1 = 1.0 / (b1mag*b2mag); + c1mag = ctmp * r12c1; + + ctmp = vb2xm*vb3x + vb2ym*vb3y + vb2zm*vb3z; + r12c2 = 1.0 / (b2mag*b3mag); + c2mag = ctmp * r12c2; + + // cos and sin of 2 angles and final c + + sin2 = MAX(1.0 - c1mag*c1mag,0.0); + sc1 = sqrt(sin2); + if (sc1 < SMALL) sc1 = SMALL; + sc1 = 1.0/sc1; + + sin2 = MAX(1.0 - c2mag*c2mag,0.0); + sc2 = sqrt(sin2); + if (sc2 < SMALL) sc2 = SMALL; + sc2 = 1.0/sc2; + + s1 = sc1 * sc1; + s2 = sc2 * sc2; + s12 = sc1 * sc2; + c = (c0 + c1mag*c2mag) * s12; + + // error check + + if (c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) { + if (screen) { + fprintf(screen,"Dihedral problem: %d %d %d %d %d %d\n", + comm->me,update->ntimestep, + atom->tag[i1],atom->tag[i2],atom->tag[i3],atom->tag[i4]); + fprintf(screen," 1st atom: %d %g %g %g\n", + comm->me,x[i1][0],x[i1][1],x[i1][2]); + fprintf(screen," 2nd atom: %d %g %g %g\n", + comm->me,x[i2][0],x[i2][1],x[i2][2]); + fprintf(screen," 3rd atom: %d %g %g %g\n", + comm->me,x[i3][0],x[i3][1],x[i3][2]); + fprintf(screen," 4th atom: %d %g %g %g\n", + comm->me,x[i4][0],x[i4][1],x[i4][2]); + } + } + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + // force & energy + // p = sum (i=1,5) a_i * c**(i-1) + // pd = dp/dc + + p = a1[type] + c*(a2[type] + c*(a3[type] + c*(a4[type] + c*a5[type]))); + pd = a2[type] + c*(2.0*a3[type] + c*(3.0*a4[type] + c*4.0*a5[type])); + + if (eflag) energy += rfactor * p; + + a = pd; + c = c * a; + s12 = s12 * a; + a11 = (-c*sb1*s1); + a22 = sb2*(2.0*c0*s12 - c*(s1+s2)); + a33 = (-c*sb3*s2); + a12 = r12c1*(c1mag*c*s1 + c2mag*s12); + a13 = rb1*rb3*s12; + a23 = r12c2*(-c2mag*c*s2 - c1mag*s12); + + sx1 = a11*vb1x + a12*vb2x + a13*vb3x; + sx2 = a12*vb1x + a22*vb2x + a23*vb3x; + sx12 = a13*vb1x + a23*vb2x + a33*vb3x; + sy1 = a11*vb1y + a12*vb2y + a13*vb3y; + sy2 = a12*vb1y + a22*vb2y + a23*vb3y; + sy12 = a13*vb1y + a23*vb2y + a33*vb3y; + sz1 = a11*vb1z + a12*vb2z + a13*vb3z; + sz2 = a12*vb1z + a22*vb2z + a23*vb3z; + sz12 = a13*vb1z + a23*vb2z + a33*vb3z; + + // apply force to each of 4 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= sx1; + f[i1][1] -= sy1; + f[i1][2] -= sz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += sx2 + sx1; + f[i2][1] += sy2 + sy1; + f[i2][2] += sz2 + sz1; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] += sx12 - sx2; + f[i3][1] += sy12 - sy2; + f[i3][2] += sz12 - sz2; + } + + if (newton_bond || i4 < nlocal) { + f[i4][0] -= sx12; + f[i4][1] -= sy12; + f[i4][2] -= sz12; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (vb1x*sx1 + vb2x*sx2 + vb3x*sx12); + virial[1] -= rfactor * (vb1y*sy1 + vb2y*sy2 + vb3y*sy12); + virial[2] -= rfactor * (vb1z*sz1 + vb2z*sz2 + vb3z*sz12); + virial[3] -= rfactor * (vb1x*sy1 + vb2x*sy2 + vb3x*sy12); + virial[4] -= rfactor * (vb1x*sz1 + vb2x*sz2 + vb3x*sz12); + virial[5] -= rfactor * (vb1y*sz1 + vb2y*sz2 + vb3y*sz12); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralMultiHarmonic::allocate() +{ + allocated = 1; + int n = atom->ndihedraltypes; + + a1 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:a1"); + a2 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:a2"); + a3 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:a3"); + a4 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:a4"); + a5 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:a5"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"dihedral:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void DihedralMultiHarmonic::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this dihedral style"); + if (narg != 6) error->all("Incorrect args for dihedral coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->ndihedraltypes,ilo,ihi); + + double a1_one = atof(arg[1]); + double a2_one = atof(arg[2]); + double a3_one = atof(arg[3]); + double a4_one = atof(arg[4]); + double a5_one = atof(arg[5]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + a1[i] = a1_one; + a2[i] = a2_one; + a3[i] = a3_one; + a4[i] = a4_one; + a5[i] = a5_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for dihedral coefficients"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void DihedralMultiHarmonic::write_restart(FILE *fp) +{ + fwrite(&a1[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&a2[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&a3[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&a4[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&a5[1],sizeof(double),atom->ndihedraltypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void DihedralMultiHarmonic::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&a1[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&a2[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&a3[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&a4[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&a5[1],sizeof(double),atom->ndihedraltypes,fp); + } + MPI_Bcast(&a1[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&a2[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&a3[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&a4[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&a5[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->ndihedraltypes; i++) setflag[i] = 1; +} diff --git a/src/MOLECULE/dihedral_multi_harmonic.h b/src/MOLECULE/dihedral_multi_harmonic.h new file mode 100644 index 0000000000000000000000000000000000000000..695cdfe1afde7864d1ea1b00316903db17a5ed84 --- /dev/null +++ b/src/MOLECULE/dihedral_multi_harmonic.h @@ -0,0 +1,35 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DIHEDRAL_MULTI_HARMONIC_H +#define DIHEDRAL_MULTI_HARMONIC_H + +#include "stdio.h" +#include "dihedral.h" + +class DihedralMultiHarmonic : public Dihedral { + public: + DihedralMultiHarmonic() {} + ~DihedralMultiHarmonic(); + void compute(int, int); + void coeff(int, int, char **); + void write_restart(FILE *); + void read_restart(FILE *); + + private: + double *a1,*a2,*a3,*a4,*a5; + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/dihedral_opls.cpp b/src/MOLECULE/dihedral_opls.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eff17571fc56dcf716c61dec4094d1814ef5c216 --- /dev/null +++ b/src/MOLECULE/dihedral_opls.cpp @@ -0,0 +1,349 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Mark Stevens (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "dihedral_opls.h" +#include "atom.h" +#include "comm.h" +#include "neighbor.h" +#include "domain.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "error.h" + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +#define TOLERANCE 0.05 +#define SMALL 0.001 +#define SMALLER 0.00001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +DihedralOPLS::~DihedralOPLS() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k1); + memory->sfree(k2); + memory->sfree(k3); + memory->sfree(k4); + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralOPLS::compute(int eflag, int vflag) +{ + int i,m,n,i1,i2,i3,i4,type,factor; + double rfactor; + double vb1x,vb1y,vb1z,vb2x,vb2y; + double vb2z,vb2xm,vb2ym,vb2zm,vb3x,vb3y,vb3z,sb1; + double sb2,sb3,rb1,rb3,c0,b1mag2,b1mag,b2mag2; + double b2mag,b3mag2,b3mag,ctmp,r12c1,c1mag,r12c2; + double c2mag,sc1,sc2,s1,s12,c,p,pd,a,a11,a22; + double a33,a12,a13,a23,sx1,sx2,sx12,sy1,sy2,sy12; + double sz1,sz2,sz12,s2,cx,cy,cz,cmag,dx,phi,si,siinv,sin2; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **dihedrallist = neighbor->dihedrallist; + int ndihedrallist = neighbor->ndihedrallist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < ndihedrallist; n++) { + + i1 = dihedrallist[n][0]; + i2 = dihedrallist[n][1]; + i3 = dihedrallist[n][2]; + i4 = dihedrallist[n][3]; + type = dihedrallist[n][4]; + + if (newton_bond) factor = 4; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + if (i4 < nlocal) factor++; + } + rfactor = 0.25 * factor; + + // 1st bond + + vb1x = x[i1][0] - x[i2][0]; + vb1y = x[i1][1] - x[i2][1]; + vb1z = x[i1][2] - x[i2][2]; + domain->minimum_image(&vb1x,&vb1y,&vb1z); + + // 2nd bond + + vb2x = x[i3][0] - x[i2][0]; + vb2y = x[i3][1] - x[i2][1]; + vb2z = x[i3][2] - x[i2][2]; + domain->minimum_image(&vb2x,&vb2y,&vb2z); + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + domain->minimum_image(&vb2xm,&vb2ym,&vb2zm); + + // 3rd bond + + vb3x = x[i4][0] - x[i3][0]; + vb3y = x[i4][1] - x[i3][1]; + vb3z = x[i4][2] - x[i3][2]; + domain->minimum_image(&vb3x,&vb3y,&vb3z); + + // c0 calculation + + sb1 = 1.0 / (vb1x*vb1x + vb1y*vb1y + vb1z*vb1z); + sb2 = 1.0 / (vb2x*vb2x + vb2y*vb2y + vb2z*vb2z); + sb3 = 1.0 / (vb3x*vb3x + vb3y*vb3y + vb3z*vb3z); + + rb1 = sqrt(sb1); + rb3 = sqrt(sb3); + + c0 = (vb1x*vb3x + vb1y*vb3y + vb1z*vb3z) * rb1*rb3; + + // 1st and 2nd angle + + b1mag2 = vb1x*vb1x + vb1y*vb1y + vb1z*vb1z; + b1mag = sqrt(b1mag2); + b2mag2 = vb2x*vb2x + vb2y*vb2y + vb2z*vb2z; + b2mag = sqrt(b2mag2); + b3mag2 = vb3x*vb3x + vb3y*vb3y + vb3z*vb3z; + b3mag = sqrt(b3mag2); + + ctmp = vb1x*vb2x + vb1y*vb2y + vb1z*vb2z; + r12c1 = 1.0 / (b1mag*b2mag); + c1mag = ctmp * r12c1; + + ctmp = vb2xm*vb3x + vb2ym*vb3y + vb2zm*vb3z; + r12c2 = 1.0 / (b2mag*b3mag); + c2mag = ctmp * r12c2; + + // cos and sin of 2 angles and final c + + sin2 = MAX(1.0 - c1mag*c1mag,0.0); + sc1 = sqrt(sin2); + if (sc1 < SMALL) sc1 = SMALL; + sc1 = 1.0/sc1; + + sin2 = MAX(1.0 - c2mag*c2mag,0.0); + sc2 = sqrt(sin2); + if (sc2 < SMALL) sc2 = SMALL; + sc2 = 1.0/sc2; + + s1 = sc1 * sc1; + s2 = sc2 * sc2; + s12 = sc1 * sc2; + c = (c0 + c1mag*c2mag) * s12; + + cx = vb1y*vb2z - vb1z*vb2y; + cy = vb1z*vb2x - vb1x*vb2z; + cz = vb1x*vb2y - vb1y*vb2x; + cmag = sqrt(cx*cx + cy*cy + cz*cz); + dx = (cx*vb3x + cy*vb3y + cz*vb3z)/cmag/b3mag; + + // error check + + if (c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) { + if (screen) { + fprintf(screen,"Dihedral problem: %d %d %d %d %d %d\n", + comm->me,update->ntimestep, + atom->tag[i1],atom->tag[i2],atom->tag[i3],atom->tag[i4]); + fprintf(screen," 1st atom: %d %g %g %g\n", + comm->me,x[i1][0],x[i1][1],x[i1][2]); + fprintf(screen," 2nd atom: %d %g %g %g\n", + comm->me,x[i2][0],x[i2][1],x[i2][2]); + fprintf(screen," 3rd atom: %d %g %g %g\n", + comm->me,x[i3][0],x[i3][1],x[i3][2]); + fprintf(screen," 4th atom: %d %g %g %g\n", + comm->me,x[i4][0],x[i4][1],x[i4][2]); + } + } + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + // force & energy + // p = sum (i=1,4) k_i * (1 + (-1)**(i+1)*cos(i*phi) ) + // pd = dp/dc + + phi = acos(c); + if (dx < 0.0) phi *= -1.0; + si = sin(phi); + if (fabs(si) < SMALLER) si = SMALLER; + siinv = 1.0/si; + + p = k1[type]*(1.0 + c) + k2[type]*(1.0 - cos(2.0*phi)) + + k3[type]*(1.0 + cos(3.0*phi)) + k4[type]*(1.0 - cos(4.0*phi)) ; + pd = k1[type] - 2.0*k2[type]*sin(2.0*phi)*siinv + + 3.0*k3[type]*sin(3.0*phi)*siinv - 4.0*k4[type]*sin(4.0*phi)*siinv; + + if (eflag) energy += rfactor * p; + + a = pd; + c = c * a; + s12 = s12 * a; + a11 = -c*sb1*s1; + a22 = sb2 * (2.0*c0*s12 - c*(s1+s2)); + a33 = -c*sb3*s2; + a12 = r12c1 * (c1mag*c*s1 + c2mag*s12); + a13 = rb1*rb3*s12; + a23 = r12c2 * (-c2mag*c*s2 - c1mag*s12); + + sx1 = a11*vb1x + a12*vb2x + a13*vb3x; + sx2 = a12*vb1x + a22*vb2x + a23*vb3x; + sx12 = a13*vb1x + a23*vb2x + a33*vb3x; + sy1 = a11*vb1y + a12*vb2y + a13*vb3y; + sy2 = a12*vb1y + a22*vb2y + a23*vb3y; + sy12 = a13*vb1y + a23*vb2y + a33*vb3y; + sz1 = a11*vb1z + a12*vb2z + a13*vb3z; + sz2 = a12*vb1z + a22*vb2z + a23*vb3z; + sz12 = a13*vb1z + a23*vb2z + a33*vb3z; + + // apply force to each of 4 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= sx1; + f[i1][1] -= sy1; + f[i1][2] -= sz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += sx2 + sx1; + f[i2][1] += sy2 + sy1; + f[i2][2] += sz2 + sz1; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] += sx12 - sx2; + f[i3][1] += sy12 - sy2; + f[i3][2] += sz12 - sz2; + } + + if (newton_bond || i4 < nlocal) { + f[i4][0] -= sx12; + f[i4][1] -= sy12; + f[i4][2] -= sz12; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (vb1x*sx1 + vb2x*sx2 + vb3x*sx12); + virial[1] -= rfactor * (vb1y*sy1 + vb2y*sy2 + vb3y*sy12); + virial[2] -= rfactor * (vb1z*sz1 + vb2z*sz2 + vb3z*sz12); + virial[3] -= rfactor * (vb1x*sy1 + vb2x*sy2 + vb3x*sy12); + virial[4] -= rfactor * (vb1x*sz1 + vb2x*sz2 + vb3x*sz12); + virial[5] -= rfactor * (vb1y*sz1 + vb2y*sz2 + vb3y*sz12); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralOPLS::allocate() +{ + allocated = 1; + int n = atom->ndihedraltypes; + + k1 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:k1"); + k2 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:k2"); + k3 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:k3"); + k4 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:k4"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"dihedral:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void DihedralOPLS::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this dihedral style"); + if (narg != 5) error->all("Incorrect args for dihedral coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->ndihedraltypes,ilo,ihi); + + double k1_one = atof(arg[1]); + double k2_one = atof(arg[2]); + double k3_one = atof(arg[3]); + double k4_one = atof(arg[4]); + + // store 1/2 factor with prefactor + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k1[i] = 0.5*k1_one; + k2[i] = 0.5*k2_one; + k3[i] = 0.5*k3_one; + k4[i] = 0.5*k4_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for dihedral coefficients"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void DihedralOPLS::write_restart(FILE *fp) +{ + fwrite(&k1[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&k2[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&k3[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&k4[1],sizeof(double),atom->ndihedraltypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void DihedralOPLS::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k1[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&k2[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&k3[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&k4[1],sizeof(double),atom->ndihedraltypes,fp); + } + MPI_Bcast(&k1[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&k2[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&k3[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&k4[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->ndihedraltypes; i++) setflag[i] = 1; +} diff --git a/src/MOLECULE/dihedral_opls.h b/src/MOLECULE/dihedral_opls.h new file mode 100644 index 0000000000000000000000000000000000000000..d65363286c9aa1903f0a1acdd433b2d5565fc87f --- /dev/null +++ b/src/MOLECULE/dihedral_opls.h @@ -0,0 +1,35 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DIHEDRAL_OPLS_H +#define DIHEDRAL_OPLS_H + +#include "stdio.h" +#include "dihedral.h" + +class DihedralOPLS : public Dihedral { + public: + DihedralOPLS() {} + ~DihedralOPLS(); + void compute(int, int); + void coeff(int, int, char **); + void write_restart(FILE *); + void read_restart(FILE *); + + private: + double *k1,*k2,*k3,*k4; + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/dump_bond.cpp b/src/MOLECULE/dump_bond.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eb88e2478099760fb7033251f78b43d850c8b6b3 --- /dev/null +++ b/src/MOLECULE/dump_bond.cpp @@ -0,0 +1,136 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "dump_bond.h" +#include "atom.h" +#include "domain.h" +#include "update.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +DumpBond::DumpBond(int narg, char **arg) : Dump(narg, arg) +{ + if (narg != 5) error->all("Illegal dump bond command"); + if (atom->molecular == 0) + error->all("Cannot use dump bond with non-molecular system"); + + size_one = 3; + + char *str = "%d %d %d %d"; + int n = strlen(str) + 1; + format_default = new char[n]; + strcpy(format_default,str); + + // one-time file open + + if (multifile == 0) openfile(); +} + +/* ---------------------------------------------------------------------- */ + +void DumpBond::init() +{ + delete [] format; + char *str; + if (format_user) str = format_user; + else str = format_default; + + int n = strlen(str) + 2; + format = new char[n]; + strcpy(format,str); + strcat(format,"\n"); +} + +/* ---------------------------------------------------------------------- */ + +void DumpBond::write_header(int ndump) +{ + fprintf(fp,"ITEM: TIMESTEP\n"); + fprintf(fp,"%d\n",update->ntimestep); + fprintf(fp,"ITEM: NUMBER OF BONDS\n"); + fprintf(fp,"%d\n",ndump); + fprintf(fp,"ITEM: BONDS\n"); +} + +/* ---------------------------------------------------------------------- */ + +int DumpBond::count() +{ + index = 0; + + int *num_bond = atom->num_bond; + int **bond_type = atom->bond_type; + int **bond_atom = atom->bond_atom; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + int i,j,k; + + int m = 0; + for (i = 0; i < nlocal; i++) { + if (!(mask[i] & groupbit)) continue; + for (j = 0; j < num_bond[i]; j++) { + k = atom->map(bond_atom[i][j]); + if (k >= 0 && !(mask[k] & groupbit)) continue; + if (bond_type[i][j] == 0) continue; + m++; + } + } + return m; +} + +/* ---------------------------------------------------------------------- */ + +int DumpBond::pack() +{ + int *num_bond = atom->num_bond; + int **bond_type = atom->bond_type; + int **bond_atom = atom->bond_atom; + int *tag = atom->tag; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + int i,j,k,type,iatom; + + int m = 0; + for (i = 0; i < nlocal; i++) { + if (!(mask[i] & groupbit)) continue; + for (j = 0; j < num_bond[i]; j++) { + iatom = bond_atom[i][j]; + k = atom->map(iatom); + if (k >= 0 && !(mask[k] & groupbit)) continue; + type = bond_type[i][j]; + if (type == 0) continue; + buf[m++] = type; + buf[m++] = tag[i]; + buf[m++] = iatom; + } + } + return m; +} + +/* ---------------------------------------------------------------------- */ + +void DumpBond::write_data(int n, double *buf) +{ + int m = 0; + for (int i = 0; i < n; i++) { + index++; + fprintf(fp,format, + index, static_cast<int> (buf[m]), + static_cast<int> (buf[m+1]), static_cast<int> (buf[m+2])); + m += size_one; + } +} diff --git a/src/MOLECULE/dump_bond.h b/src/MOLECULE/dump_bond.h new file mode 100644 index 0000000000000000000000000000000000000000..6af31b8a9a773df8f414450f2d869410b05c989a --- /dev/null +++ b/src/MOLECULE/dump_bond.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DUMP_BOND_H +#define DUMP_BOND_H + +#include "dump.h" + +class DumpBond : public Dump { + public: + DumpBond(int, char **); + ~DumpBond() {} + void init(); + + private: + int index; // counter for bond output + + void write_header(int); + int count(); + int pack(); + void write_data(int, double *); +}; + +#endif diff --git a/src/MOLECULE/improper.cpp b/src/MOLECULE/improper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bf428a62c643ecdc1c4fb71c9ec6114740e4f389 --- /dev/null +++ b/src/MOLECULE/improper.cpp @@ -0,0 +1,36 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "improper.h" +#include "atom.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +Improper::Improper() +{ + allocated = 0; + PI = 4.0*atan(1.0); +} + +/* ---------------------------------------------------------------------- + check if all coeffs are set +------------------------------------------------------------------------- */ + +void Improper::init() +{ + if (!allocated) error->all("Improper coeffs are not set"); + for (int i = 1; i <= atom->nimpropertypes; i++) + if (setflag[i] == 0) error->all("All improper coeffs are not set"); +} diff --git a/src/MOLECULE/improper_cvff.cpp b/src/MOLECULE/improper_cvff.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2b7a0d111f7d2e339a73eadac643152232f37e9f --- /dev/null +++ b/src/MOLECULE/improper_cvff.cpp @@ -0,0 +1,352 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "math.h" +#include "stdlib.h" +#include "improper_cvff.h" +#include "atom.h" +#include "comm.h" +#include "neighbor.h" +#include "domain.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "error.h" + +#define TOLERANCE 0.05 +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +ImproperCvff::~ImproperCvff() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(sign); + memory->sfree(multiplicity); + } +} + +/* ---------------------------------------------------------------------- */ + +void ImproperCvff::compute(int eflag, int vflag) +{ + int m,n,i1,i2,i3,i4,type,factor; + double rfactor; + double vb1x,vb1y,vb1z,vb2x,vb2y; + double vb2z,vb2xm,vb2ym,vb2zm,vb3x,vb3y,vb3z,sb1; + double sb2,sb3,rb1,rb3,c0,b1mag2,b1mag,b2mag2; + double b2mag,b3mag2,b3mag,ctmp,r12c1,c1mag,r12c2; + double c2mag,sc1,sc2,s1,s12,c,p,pd,rc2,a,a11,a22; + double a33,a12,a13,a23,sx1,sx2,sx12,sy1,sy2,sy12; + double sz1,sz2,sz12,s2; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **improperlist = neighbor->improperlist; + int nimproperlist = neighbor->nimproperlist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nimproperlist; n++) { + + i1 = improperlist[n][0]; + i2 = improperlist[n][1]; + i3 = improperlist[n][2]; + i4 = improperlist[n][3]; + type = improperlist[n][4]; + + if (newton_bond) factor = 4; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + if (i4 < nlocal) factor++; + } + rfactor = 0.25 * factor; + + // 1st bond + + vb1x = x[i1][0] - x[i2][0]; + vb1y = x[i1][1] - x[i2][1]; + vb1z = x[i1][2] - x[i2][2]; + domain->minimum_image(&vb1x,&vb1y,&vb1z); + + // 2nd bond + + vb2x = x[i3][0] - x[i2][0]; + vb2y = x[i3][1] - x[i2][1]; + vb2z = x[i3][2] - x[i2][2]; + domain->minimum_image(&vb2x,&vb2y,&vb2z); + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + domain->minimum_image(&vb2xm,&vb2ym,&vb2zm); + + // 3rd bond + + vb3x = x[i4][0] - x[i3][0]; + vb3y = x[i4][1] - x[i3][1]; + vb3z = x[i4][2] - x[i3][2]; + domain->minimum_image(&vb3x,&vb3y,&vb3z); + + // c0 calculation + + sb1 = 1.0 / (vb1x*vb1x + vb1y*vb1y + vb1z*vb1z); + sb2 = 1.0 / (vb2x*vb2x + vb2y*vb2y + vb2z*vb2z); + sb3 = 1.0 / (vb3x*vb3x + vb3y*vb3y + vb3z*vb3z); + + rb1 = sqrt(sb1); + rb3 = sqrt(sb3); + + c0 = (vb1x*vb3x + vb1y*vb3y + vb1z*vb3z) * rb1*rb3; + + // 1st and 2nd angle + + b1mag2 = vb1x*vb1x + vb1y*vb1y + vb1z*vb1z; + b1mag = sqrt(b1mag2); + b2mag2 = vb2x*vb2x + vb2y*vb2y + vb2z*vb2z; + b2mag = sqrt(b2mag2); + b3mag2 = vb3x*vb3x + vb3y*vb3y + vb3z*vb3z; + b3mag = sqrt(b3mag2); + + ctmp = vb1x*vb2x + vb1y*vb2y + vb1z*vb2z; + r12c1 = 1.0 / (b1mag*b2mag); + c1mag = ctmp * r12c1; + + ctmp = vb2xm*vb3x + vb2ym*vb3y + vb2zm*vb3z; + r12c2 = 1.0 / (b2mag*b3mag); + c2mag = ctmp * r12c2; + + // cos and sin of 2 angles and final c + + sc1 = sqrt(1.0 - c1mag*c1mag); + if (sc1 < SMALL) sc1 = SMALL; + sc1 = 1.0/sc1; + + sc2 = sqrt(1.0 - c2mag*c2mag); + if (sc2 < SMALL) sc2 = SMALL; + sc2 = 1.0/sc2; + + s1 = sc1 * sc1; + s2 = sc2 * sc2; + s12 = sc1 * sc2; + c = (c0 + c1mag*c2mag) * s12; + + // error check + + if (c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) { + int me; + MPI_Comm_rank(world,&me); + if (screen) { + fprintf(screen,"Improper problem: %d %d %d %d %d %d\n", + me,update->ntimestep, + atom->tag[i1],atom->tag[i2],atom->tag[i3],atom->tag[i4]); + fprintf(screen," 1st atom: %d %g %g %g\n", + me,x[i1][0],x[i1][1],x[i1][2]); + fprintf(screen," 2nd atom: %d %g %g %g\n", + me,x[i2][0],x[i2][1],x[i2][2]); + fprintf(screen," 3rd atom: %d %g %g %g\n", + me,x[i3][0],x[i3][1],x[i3][2]); + fprintf(screen," 4th atom: %d %g %g %g\n", + me,x[i4][0],x[i4][1],x[i4][2]); + } + } + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + // force & energy + // p = 1 + cos(n*phi) for d = 1 + // p = 1 - cos(n*phi) for d = -1 + // pd = dp/dc / 2 + + m = multiplicity[type]; + + if (m == 2) { + p = 2.0*c*c; + pd = 2.0*c; + } else if (m == 3) { + rc2 = c*c; + p = (4.0*rc2-3.0)*c + 1.0; + pd = 6.0*rc2 - 1.5; + } else if (m == 4) { + rc2 = c*c; + p = 8.0*(rc2-1)*rc2 + 2.0; + pd = (16.0*rc2-8.0)*c; + } else if (m == 6) { + rc2 = c*c; + p = ((32.0*rc2-48.0)*rc2 + 18.0)*rc2; + pd = (96.0*(rc2-1.0)*rc2 + 18.0)*c; + } else if (m == 1) { + p = c + 1.0; + pd = 0.5; + } else if (m == 5) { + rc2 = c*c; + p = ((16.0*rc2-20.0)*rc2 + 5.0)*c + 1.0; + pd = (40.0*rc2-30.0)*rc2 + 2.5; + } else if (m == 0) { + p = 2.0; + pd = 0.0; + } + + if (sign[type] == -1) { + p = 2.0 - p; + pd = -pd; + } + + if (eflag) energy += rfactor * k[type] * p; + + a = 2.0 * k[type] * pd; + c = c * a; + s12 = s12 * a; + a11 = (-c*sb1*s1); + a22 = sb2*(2.0*c0*s12 - c*(s1+s2)); + a33 = (-c*sb3*s2); + a12 = r12c1*(c1mag*c*s1 + c2mag*s12); + a13 = rb1*rb3*s12; + a23 = r12c2*(-c2mag*c*s2 - c1mag*s12); + + sx1 = a11*vb1x + a12*vb2x + a13*vb3x; + sx2 = a12*vb1x + a22*vb2x + a23*vb3x; + sx12 = a13*vb1x + a23*vb2x + a33*vb3x; + sy1 = a11*vb1y + a12*vb2y + a13*vb3y; + sy2 = a12*vb1y + a22*vb2y + a23*vb3y; + sy12 = a13*vb1y + a23*vb2y + a33*vb3y; + sz1 = a11*vb1z + a12*vb2z + a13*vb3z; + sz2 = a12*vb1z + a22*vb2z + a23*vb3z; + sz12 = a13*vb1z + a23*vb2z + a33*vb3z; + + // apply force to each of 4 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= sx1; + f[i1][1] -= sy1; + f[i1][2] -= sz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += sx2 + sx1; + f[i2][1] += sy2 + sy1; + f[i2][2] += sz2 + sz1; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] += sx12 - sx2; + f[i3][1] += sy12 - sy2; + f[i3][2] += sz12 - sz2; + } + + if (newton_bond || i4 < nlocal) { + f[i4][0] -= sx12; + f[i4][1] -= sy12; + f[i4][2] -= sz12; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (vb1x*sx1 + vb2x*sx2 + vb3x*sx12); + virial[1] -= rfactor * (vb1y*sy1 + vb2y*sy2 + vb3y*sy12); + virial[2] -= rfactor * (vb1z*sz1 + vb2z*sz2 + vb3z*sz12); + virial[3] -= rfactor * (vb1x*sy1 + vb2x*sy2 + vb3x*sy12); + virial[4] -= rfactor * (vb1x*sz1 + vb2x*sz2 + vb3x*sz12); + virial[5] -= rfactor * (vb1y*sz1 + vb2y*sz2 + vb3y*sz12); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void ImproperCvff::allocate() +{ + allocated = 1; + int n = atom->nimpropertypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"improper:k"); + sign = (int *) memory->smalloc((n+1)*sizeof(int),"improper:sign"); + multiplicity = (int *) + memory->smalloc((n+1)*sizeof(int),"improper:multiplicity"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"improper:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void ImproperCvff::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this improper style"); + if (narg != 4) error->all("Incorrect args for improper coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nimpropertypes,ilo,ihi); + + double k_one = atof(arg[1]); + int sign_one = atoi(arg[2]); + int multiplicity_one = atoi(arg[3]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + sign[i] = sign_one; + multiplicity[i] = multiplicity_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for improper coefficients"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void ImproperCvff::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nimpropertypes,fp); + fwrite(&sign[1],sizeof(int),atom->nimpropertypes,fp); + fwrite(&multiplicity[1],sizeof(int),atom->nimpropertypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void ImproperCvff::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->nimpropertypes,fp); + fread(&sign[1],sizeof(int),atom->nimpropertypes,fp); + fread(&multiplicity[1],sizeof(int),atom->nimpropertypes,fp); + } + MPI_Bcast(&k[1],atom->nimpropertypes,MPI_DOUBLE,0,world); + MPI_Bcast(&sign[1],atom->nimpropertypes,MPI_INT,0,world); + MPI_Bcast(&multiplicity[1],atom->nimpropertypes,MPI_INT,0,world); + + for (int i = 1; i <= atom->nimpropertypes; i++) setflag[i] = 1; +} diff --git a/src/MOLECULE/improper_cvff.h b/src/MOLECULE/improper_cvff.h new file mode 100644 index 0000000000000000000000000000000000000000..37225ee5011f1e4cd2d3c81b583270f92fa3be8e --- /dev/null +++ b/src/MOLECULE/improper_cvff.h @@ -0,0 +1,36 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef IMPROPER_CVFF_H +#define IMPROPER_CVFF_H + +#include "stdio.h" +#include "improper.h" + +class ImproperCvff : public Improper { + public: + ImproperCvff() {} + ~ImproperCvff(); + void compute(int, int); + void coeff(int, int, char **); + void write_restart(FILE *); + void read_restart(FILE *); + + private: + double *k; + int *sign,*multiplicity; + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/improper_harmonic.cpp b/src/MOLECULE/improper_harmonic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..58b0c58531bd75d1d5b35aa37aa9b5cb9b03a7c3 --- /dev/null +++ b/src/MOLECULE/improper_harmonic.cpp @@ -0,0 +1,286 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "math.h" +#include "stdlib.h" +#include "improper_harmonic.h" +#include "atom.h" +#include "comm.h" +#include "neighbor.h" +#include "domain.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "error.h" + +#define TOLERANCE 0.05 +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +ImproperHarmonic::~ImproperHarmonic() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(chi); + } +} + +/* ---------------------------------------------------------------------- */ + +void ImproperHarmonic::compute(int eflag, int vflag) +{ + int n,i1,i2,i3,i4,type,factor; + double rfactor; + double v1x,v1y,v1z,v2x,v2y,v2z,v3x; + double v3y,v3z,ss1,ss2,ss3,r1,r2,r3,c0,c1,c2,s1,s2; + double s12,c,s,domega,a,a11,a22,a33,a12,a13,a23,sx1; + double sx2,sx12,sy1,sy2,sy12,sz1,sz2,sz12; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **improperlist = neighbor->improperlist; + int nimproperlist = neighbor->nimproperlist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nimproperlist; n++) { + + i1 = improperlist[n][0]; + i2 = improperlist[n][1]; + i3 = improperlist[n][2]; + i4 = improperlist[n][3]; + type = improperlist[n][4]; + + if (newton_bond) factor = 4; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + if (i4 < nlocal) factor++; + } + rfactor = 0.25 * factor; + + // geometry of 4-body + + v1x = x[i2][0] - x[i1][0]; + v1y = x[i2][1] - x[i1][1]; + v1z = x[i2][2] - x[i1][2]; + domain->minimum_image(&v1x,&v1y,&v1z); + + v2x = x[i3][0] - x[i2][0]; + v2y = x[i3][1] - x[i2][1]; + v2z = x[i3][2] - x[i2][2]; + domain->minimum_image(&v2x,&v2y,&v2z); + + v3x = x[i4][0] - x[i3][0]; + v3y = x[i4][1] - x[i3][1]; + v3z = x[i4][2] - x[i3][2]; + domain->minimum_image(&v3x,&v3y,&v3z); + + ss1 = 1.0 / (v1x*v1x + v1y*v1y + v1z*v1z); + ss2 = 1.0 / (v2x*v2x + v2y*v2y + v2z*v2z); + ss3 = 1.0 / (v3x*v3x + v3y*v3y + v3z*v3z); + + r1 = sqrt(ss1); + r2 = sqrt(ss2); + r3 = sqrt(ss3); + + // sin and cos of angle + + c0 = -(v1x * v3x + v1y * v3y + v1z * v3z) * r1 * r3; + c1 = -(v1x * v2x + v1y * v2y + v1z * v2z) * r1 * r2; + c2 = -(v3x * v2x + v3y * v2y + v3z * v2z) * r3 * r2; + + s1 = 1.0 - c1*c1; + if (s1 < SMALL) s1 = SMALL; + s1 = 1.0 / s1; + + s2 = 1.0 - c2*c2; + if (s2 < SMALL) s2 = SMALL; + s2 = 1.0 / s2; + + s12 = sqrt(s1*s2); + c = (c1*c2 + c0) * s12; + + // error check + + if (c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) { + int me; + MPI_Comm_rank(world,&me); + if (screen) { + fprintf(screen,"Improper problem: %d %d %d %d %d %d\n", + me,update->ntimestep, + atom->tag[i1],atom->tag[i2],atom->tag[i3],atom->tag[i4]); + fprintf(screen," 1st atom: %d %g %g %g\n", + me,x[i1][0],x[i1][1],x[i1][2]); + fprintf(screen," 2nd atom: %d %g %g %g\n", + me,x[i2][0],x[i2][1],x[i2][2]); + fprintf(screen," 3rd atom: %d %g %g %g\n", + me,x[i3][0],x[i3][1],x[i3][2]); + fprintf(screen," 4th atom: %d %g %g %g\n", + me,x[i4][0],x[i4][1],x[i4][2]); + } + } + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + s = sqrt(1.0 - c*c); + if (s < SMALL) s = SMALL; + + // force & energy + + domega = acos(c) - chi[type]; + a = k[type] * domega; + + if (eflag) energy += rfactor * a * domega; + + a = -a * 2.0/s; + c = c * a; + + s12 = s12 * a; + a11 = (-c * ss1 * s1); + a22 = ss2 * (2.0 * c0 * s12 - c * (s1 + s2)); + a33 = (-c * ss3 * s2); + a12 = r1 * r2 * (c1 * c * s1 + c2 * s12); + a13 = r1 * r3 * s12; + a23 = r2 * r3 * (-c2 * c * s2 - c1 * s12); + + sx1 = a12*v2x + a13*v3x - a11*v1x; + sx2 = a22*v2x + a23*v3x - a12*v1x; + sx12 = a23*v2x + a33*v3x - a13*v1x; + sy1 = a12*v2y + a13*v3y - a11*v1y; + sy2 = a22*v2y + a23*v3y - a12*v1y; + sy12 = a23*v2y + a33*v3y - a13*v1y; + sz1 = a12*v2z + a13*v3z - a11*v1z; + sz2 = a22*v2z + a23*v3z - a12*v1z; + sz12 = a23*v2z + a33*v3z - a13*v1z; + + // apply force to each of 4 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= sx1; + f[i1][1] -= sy1; + f[i1][2] -= sz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += sx2 + sx1; + f[i2][1] += sy2 + sy1; + f[i2][2] += sz2 + sz1; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] += sx12 - sx2; + f[i3][1] += sy12 - sy2; + f[i3][2] += sz12 - sz2; + } + + if (newton_bond || i4 < nlocal) { + f[i4][0] -= sx12; + f[i4][1] -= sy12; + f[i4][2] -= sz12; + } + + // virial contribution + + if (vflag) { + virial[0] += rfactor * (v1x*sx1 - v2x*sx2 - v3x*sx12); + virial[1] += rfactor * (v1y*sy1 - v2y*sy2 - v3y*sy12); + virial[2] += rfactor * (v1z*sz1 - v2z*sz2 - v3z*sz12); + virial[3] += rfactor * (v1x*sy1 - v2x*sy2 - v3x*sy12); + virial[4] += rfactor * (v1x*sz1 - v2x*sz2 - v3x*sz12); + virial[5] += rfactor * (v1y*sz1 - v2y*sz2 - v3y*sz12); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void ImproperHarmonic::allocate() +{ + allocated = 1; + int n = atom->nimpropertypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"improper:k"); + chi = (double *) memory->smalloc((n+1)*sizeof(double),"improper:chi"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"improper:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void ImproperHarmonic::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this improper style"); + if (narg != 3) error->all("Incorrect args for improper coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nimpropertypes,ilo,ihi); + + double k_one = atof(arg[1]); + double chi_one = atof(arg[2]); + + // convert chi from degrees to radians + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + chi[i] = chi_one/180.0 * PI; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for improper coefficients"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void ImproperHarmonic::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nimpropertypes,fp); + fwrite(&chi[1],sizeof(double),atom->nimpropertypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void ImproperHarmonic::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->nimpropertypes,fp); + fread(&chi[1],sizeof(double),atom->nimpropertypes,fp); + } + MPI_Bcast(&k[1],atom->nimpropertypes,MPI_DOUBLE,0,world); + MPI_Bcast(&chi[1],atom->nimpropertypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nimpropertypes; i++) setflag[i] = 1; +} diff --git a/src/MOLECULE/improper_harmonic.h b/src/MOLECULE/improper_harmonic.h new file mode 100644 index 0000000000000000000000000000000000000000..e21ac2e6b7f1d71c50018e47e4768868e8700bd7 --- /dev/null +++ b/src/MOLECULE/improper_harmonic.h @@ -0,0 +1,35 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef IMPROPER_HARMONIC_H +#define IMPROPER_HARMONIC_H + +#include "stdio.h" +#include "improper.h" + +class ImproperHarmonic : public Improper { + public: + ImproperHarmonic() {} + ~ImproperHarmonic(); + void compute(int, int); + void coeff(int, int, char **); + void write_restart(FILE *); + void read_restart(FILE *); + + private: + double *k,*chi; + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/improper_hybrid.cpp b/src/MOLECULE/improper_hybrid.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c911f589500d8283eb2198cd3d2362509fab40b1 --- /dev/null +++ b/src/MOLECULE/improper_hybrid.cpp @@ -0,0 +1,246 @@ +/* ----------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "string.h" +#include "improper_hybrid.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define EXTRA 1000 + +/* ---------------------------------------------------------------------- + set all global defaults +------------------------------------------------------------------------- */ + +ImproperHybrid::ImproperHybrid() +{ + nstyles = 0; +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +ImproperHybrid::~ImproperHybrid() +{ + if (nstyles) { + for (int i = 0; i < nstyles; i++) delete styles[i]; + delete [] styles; + for (int i = 0; i < nstyles; i++) delete [] keywords[i]; + delete [] keywords; + } + + if (allocated) { + memory->sfree(setflag); + memory->sfree(map); + delete [] nimproperlist; + delete [] maximproper; + for (int i = 0; i < nstyles; i++) + memory->destroy_2d_int_array(improperlist[i]); + delete [] improperlist; + } +} + +/* ---------------------------------------------------------------------- */ + +void ImproperHybrid::compute(int eflag, int vflag) +{ + int i,m,n; + + // save ptrs to original improperlist + + int nimproperlist_orig = neighbor->nimproperlist; + int **improperlist_orig = neighbor->improperlist; + + // if this is re-neighbor step, create sub-style improperlists + // nimproperlist[] = length of each sub-style list + // realloc sub-style improperlist if necessary + // load sub-style improperlist with 5 values from original improperlist + + if (neighbor->ago == 0) { + for (m = 0; m < nstyles; m++) nimproperlist[m] = 0; + for (i = 0; i < nimproperlist_orig; i++) + nimproperlist[map[improperlist_orig[i][4]]]++; + for (m = 0; m < nstyles; m++) { + if (nimproperlist[m] > maximproper[m]) { + memory->destroy_2d_int_array(improperlist[m]); + maximproper[m] = nimproperlist[m] + EXTRA; + improperlist[m] = (int **) + memory->create_2d_int_array(maximproper[m],5, + "improper_hybrid:improperlist"); + } + nimproperlist[m] = 0; + } + for (i = 0; i < nimproperlist_orig; i++) { + m = map[improperlist_orig[i][4]]; + n = nimproperlist[m]; + improperlist[m][n][0] = improperlist_orig[i][0]; + improperlist[m][n][1] = improperlist_orig[i][1]; + improperlist[m][n][2] = improperlist_orig[i][2]; + improperlist[m][n][3] = improperlist_orig[i][3]; + improperlist[m][n][4] = improperlist_orig[i][4]; + nimproperlist[m]++; + } + } + + // call each sub-style's compute function + // must set neighbor->improperlist to sub-style improperlist before call + // accumulate sub-style energy,virial in hybrid's energy,virial + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + for (m = 0; m < nstyles; m++) { + if (styles[m] == NULL) continue; + neighbor->nimproperlist = nimproperlist[m]; + neighbor->improperlist = improperlist[m]; + styles[m]->compute(eflag,vflag); + if (eflag) energy += styles[m]->energy; + if (vflag) for (n = 0; n < 6; n++) virial[n] += styles[m]->virial[n]; + } + + // restore ptrs to original improperlist + + neighbor->nimproperlist = nimproperlist_orig; + neighbor->improperlist = improperlist_orig; +} + +/* ---------------------------------------------------------------------- */ + +void ImproperHybrid::allocate() +{ + allocated = 1; + int n = atom->nimpropertypes; + + map = (int *) memory->smalloc((n+1)*sizeof(int),"improper:map"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"improper:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; + + nimproperlist = new int[nstyles]; + maximproper = new int[nstyles]; + improperlist = new int**[nstyles]; + for (int m = 0; m < nstyles; m++) maximproper[m] = 0; + for (int m = 0; m < nstyles; m++) improperlist[m] = NULL; +} + +/* ---------------------------------------------------------------------- + create one improper style for each arg in list +------------------------------------------------------------------------- */ + +void ImproperHybrid::settings(int narg, char **arg) +{ + nstyles = narg; + styles = new Improper*[nstyles]; + keywords = new char*[nstyles]; + + for (int m = 0; m < nstyles; m++) { + for (int i = 0; i < m; i++) + if (strcmp(arg[m],arg[i]) == 0) + error->all("Improper style hybrid cannot use same improper style twice"); + if (strcmp(arg[m],"hybrid") == 0) + error->all("Improper style hybrid cannot have hybrid as an argument"); + styles[m] = force->new_improper(arg[m]); + keywords[m] = new char[strlen(arg[m])+1]; + strcpy(keywords[m],arg[m]); + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +---------------------------------------------------------------------- */ + +void ImproperHybrid::coeff(int which, int narg, char **arg) +{ + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nimpropertypes,ilo,ihi); + + // 2nd arg = improper style name (harmonic, etc) + + int m; + for (m = 0; m < nstyles; m++) + if (strcmp(arg[1],keywords[m]) == 0) break; + if (m == nstyles) error->all("Improper coeff for hybrid has invalid style"); + + // set low-level coefficients for each impropertype + // replace 2nd arg with i, call coeff() with no 1st arg + // if sub-style is NULL for "none", still set setflag + + for (int i = ilo; i <= ihi; i++) { + sprintf(arg[1],"%d",i); + map[i] = m; + if (styles[m]) styles[m]->coeff(which,narg-1,&arg[1]); + setflag[i] = 1; + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void ImproperHybrid::write_restart(FILE *fp) +{ + fwrite(&nstyles,sizeof(int),1,fp); + + int n; + for (int m = 0; m < nstyles; m++) { + n = strlen(keywords[m]) + 1; + fwrite(&n,sizeof(int),1,fp); + fwrite(keywords[m],sizeof(char),n,fp); + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void ImproperHybrid::read_restart(FILE *fp) +{ + allocate(); + + int me = comm->me; + if (me == 0) fread(&nstyles,sizeof(int),1,fp); + MPI_Bcast(&nstyles,1,MPI_INT,0,world); + styles = new Improper*[nstyles]; + keywords = new char*[nstyles]; + + int n; + for (int m = 0; m < nstyles; m++) { + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + keywords[m] = new char[n]; + if (me == 0) fread(keywords[m],sizeof(char),n,fp); + MPI_Bcast(keywords[m],n,MPI_CHAR,0,world); + styles[m] = force->new_improper(keywords[m]); + } +} + +/* ---------------------------------------------------------------------- + memory usage +------------------------------------------------------------------------- */ + +int ImproperHybrid::memory_usage() +{ + int bytes = 0; + for (int m = 0; m < nstyles; m++) bytes += maximproper[m]*5 * sizeof(int); + for (int m = 0; m < nstyles; m++) + if (styles[m]) bytes += styles[m]->memory_usage(); + return bytes; +} diff --git a/src/MOLECULE/improper_hybrid.h b/src/MOLECULE/improper_hybrid.h new file mode 100644 index 0000000000000000000000000000000000000000..1aca2401e59001f49695c51847985e3f6bffae71 --- /dev/null +++ b/src/MOLECULE/improper_hybrid.h @@ -0,0 +1,44 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef IMPROPER_HYBRID_H +#define IMPROPER_HYBRID_H + +#include "stdio.h" +#include "improper.h" + +class ImproperHybrid : public Improper { + public: + ImproperHybrid(); + ~ImproperHybrid(); + void compute(int, int); + void settings(int, char **); + void coeff(int, int, char **); + void write_restart(FILE *); + void read_restart(FILE *); + int memory_usage(); + + private: + int nstyles; // # of different improper styles + Improper **styles; // class list for each Improper style + char **keywords; // keyword for each improper style + int *map; // which style each improper type points to + + int *nimproperlist; // # of impropers in sub-style improperlists + int *maximproper; // max # of impropers sub-style lists can store + int ***improperlist; // improperlist for each sub-style + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/pair_lj_charmm_coul_charmm.cpp b/src/MOLECULE/pair_lj_charmm_coul_charmm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..101043e9977df744ffa5a04dd7e1f8d5b49fe812 --- /dev/null +++ b/src/MOLECULE/pair_lj_charmm_coul_charmm.cpp @@ -0,0 +1,493 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "pair_lj_charmm_coul_charmm.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +PairLJCharmmCoulCharmm::~PairLJCharmmCoulCharmm() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(epsilon); + memory->destroy_2d_double_array(sigma); + memory->destroy_2d_double_array(eps14); + memory->destroy_2d_double_array(sigma14); + memory->destroy_2d_double_array(lj1); + memory->destroy_2d_double_array(lj2); + memory->destroy_2d_double_array(lj3); + memory->destroy_2d_double_array(lj4); + memory->destroy_2d_double_array(lj14_1); + memory->destroy_2d_double_array(lj14_2); + memory->destroy_2d_double_array(lj14_3); + memory->destroy_2d_double_array(lj14_4); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmm::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double factor,phicoul,philj,switch1,switch2; + int *neighs; + double **f; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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_bothsq) { + r2inv = 1.0/rsq; + + if (rsq < cut_coulsq) { + forcecoul = qqrd2e * qtmp*q[j]*sqrt(r2inv); + if (rsq > cut_coul_innersq) { + switch1 = (cut_coulsq-rsq) * (cut_coulsq-rsq) * + (cut_coulsq + 2.0*rsq - 3.0*cut_coul_innersq) / denom_coul; + switch2 = 12.0*rsq * (cut_coulsq-rsq) * + (rsq-cut_coul_innersq) / denom_coul; + forcecoul *= switch1 + switch2; + } + } else forcecoul = 0.0; + + if (rsq < cut_ljsq) { + r6inv = r2inv*r2inv*r2inv; + jtype = type[j]; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + } else forcelj = 0.0; + + fforce = (factor_coul*forcecoul + factor_lj*forcelj) * 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 (newton_pair || j < nlocal) factor = 1.0; + else factor = 0.5; + if (rsq < cut_coulsq) { + phicoul = qqrd2e * qtmp*q[j]*sqrt(r2inv); + if (rsq > cut_coul_innersq) { + switch1 = (cut_coulsq-rsq) * (cut_coulsq-rsq) * + (cut_coulsq + 2.0*rsq - 3.0*cut_coul_innersq) / + denom_coul; + phicoul *= switch1; + } + eng_coul += factor*factor_coul*phicoul; + } + if (rsq < cut_ljsq) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + philj *= switch1; + } + eng_vdwl += factor*factor_lj*philj; + } + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmm::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"); + + epsilon = memory->create_2d_double_array(n+1,n+1,"pair:epsilon"); + sigma = memory->create_2d_double_array(n+1,n+1,"pair:sigma"); + eps14 = memory->create_2d_double_array(n+1,n+1,"pair:eps14"); + sigma14 = memory->create_2d_double_array(n+1,n+1,"pair:sigma14"); + lj1 = memory->create_2d_double_array(n+1,n+1,"pair:lj1"); + lj2 = memory->create_2d_double_array(n+1,n+1,"pair:lj2"); + lj3 = memory->create_2d_double_array(n+1,n+1,"pair:lj3"); + lj4 = memory->create_2d_double_array(n+1,n+1,"pair:lj4"); + lj14_1 = memory->create_2d_double_array(n+1,n+1,"pair:lj14_1"); + lj14_2 = memory->create_2d_double_array(n+1,n+1,"pair:lj14_2"); + lj14_3 = memory->create_2d_double_array(n+1,n+1,"pair:lj14_3"); + lj14_4 = memory->create_2d_double_array(n+1,n+1,"pair:lj14_4"); +} + +/* ---------------------------------------------------------------------- + global settings + unlike other pair styles, + there are no individual pair settings that these override +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmm::settings(int narg, char **arg) +{ + if (narg != 2 && narg != 4) + error->all("Illegal pair_style command"); + + cut_lj_inner = atof(arg[0]); + cut_lj = atof(arg[1]); + if (narg == 2) { + cut_coul_inner = cut_lj_inner; + cut_coul = cut_lj; + } else { + cut_coul_inner = atof(arg[2]); + cut_coul = atof(arg[3]); + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmm::coeff(int narg, char **arg) +{ + if (narg != 4 && narg != 6) + 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); + + double epsilon_one = atof(arg[2]); + double sigma_one = atof(arg[3]); + double eps14_one = epsilon_one; + double sigma14_one = sigma_one; + if (narg == 6) { + eps14_one = atof(arg[4]); + sigma14_one = atof(arg[5]); + } + + 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; + eps14[i][j] = eps14_one; + sigma14[i][j] = sigma14_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairLJCharmmCoulCharmm::init_one(int i, int j) +{ + // always mix arithmetically + + if (setflag[i][j] == 0) { + epsilon[i][j] = sqrt(epsilon[i][i]*epsilon[j][j]); + sigma[i][j] = 0.5 * (sigma[i][i] + sigma[j][j]); + eps14[i][j] = sqrt(eps14[i][i]*eps14[j][j]); + sigma14[i][j] = 0.5 * (sigma14[i][i] + sigma14[j][j]); + } + + double cut = MAX(cut_lj,cut_coul); + + 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); + lj14_1[i][j] = 48.0 * eps14[i][j] * pow(sigma14[i][j],12.0); + lj14_2[i][j] = 24.0 * eps14[i][j] * pow(sigma14[i][j],6.0); + lj14_3[i][j] = 4.0 * eps14[i][j] * pow(sigma14[i][j],12.0); + lj14_4[i][j] = 4.0 * eps14[i][j] * pow(sigma14[i][j],6.0); + + lj1[j][i] = lj1[i][j]; + lj2[j][i] = lj2[i][j]; + lj3[j][i] = lj3[i][j]; + lj4[j][i] = lj4[i][j]; + lj14_1[j][i] = lj14_1[i][j]; + lj14_2[j][i] = lj14_2[i][j]; + lj14_3[j][i] = lj14_3[i][j]; + lj14_4[j][i] = lj14_4[i][j]; + + return cut; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmm::init_style() +{ + // require an atom style with charge defined + + if (atom->charge_allow == 0) + error->all("Must use charged atom style with this pair style"); + + // require cut_lj_inner < cut_lj, cut_coul_inner < cut_coul + + if (cut_lj_inner >= cut_lj || cut_coul_inner >= cut_coul) + error->all("Pair inner cutoff >= Pair outer cutoff"); + + cut_lj_innersq = cut_lj_inner * cut_lj_inner; + cut_ljsq = cut_lj * cut_lj; + cut_coul_innersq = cut_coul_inner * cut_coul_inner; + cut_coulsq = cut_coul * cut_coul; + cut_bothsq = MAX(cut_ljsq,cut_coulsq); + + denom_lj = (cut_ljsq-cut_lj_innersq) * (cut_ljsq-cut_lj_innersq) * + (cut_ljsq-cut_lj_innersq); + denom_coul = (cut_coulsq-cut_coul_innersq) * (cut_coulsq-cut_coul_innersq) * + (cut_coulsq-cut_coul_innersq); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmm::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + 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(&eps14[i][j],sizeof(double),1,fp); + fwrite(&sigma14[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmm::read_restart(FILE *fp) +{ + read_restart_settings(fp); + + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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(&eps14[i][j],sizeof(double),1,fp); + fread(&sigma14[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(&eps14[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&sigma14[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmm::write_restart_settings(FILE *fp) +{ + fwrite(&cut_lj_inner,sizeof(double),1,fp); + fwrite(&cut_lj,sizeof(double),1,fp); + fwrite(&cut_coul_inner,sizeof(double),1,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 PairLJCharmmCoulCharmm::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&cut_lj_inner,sizeof(double),1,fp); + fread(&cut_lj,sizeof(double),1,fp); + fread(&cut_coul_inner,sizeof(double),1,fp); + fread(&cut_coul,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + MPI_Bcast(&cut_lj_inner,1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_lj,1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_coul_inner,1,MPI_DOUBLE,0,world); + 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); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmm::single(int i, int j, int itype, int jtype, + double rsq, double factor_coul, + double factor_lj, + int eflag, One &one) +{ + double r2inv,r6inv,switch1,switch2,forcecoul,forcelj,phicoul,philj; + + r2inv = 1.0/rsq; + if (rsq < cut_coulsq) { + forcecoul = force->qqrd2e * atom->q[i]*atom->q[j]*sqrt(r2inv); + if (rsq > cut_coul_innersq) { + switch1 = (cut_coulsq-rsq) * (cut_coulsq-rsq) * + (cut_coulsq + 2.0*rsq - 3.0*cut_coul_innersq) / denom_coul; + switch2 = 12.0*rsq * (cut_coulsq-rsq) * + (rsq-cut_coul_innersq) / denom_coul; + forcecoul *= switch1 + switch2; + } + } else forcecoul = 0.0; + if (rsq < cut_ljsq) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + } else forcelj = 0.0; + one.fforce = (factor_coul*forcecoul + factor_lj*forcelj) * r2inv; + + if (eflag) { + if (rsq < cut_coulsq) { + phicoul = force->qqrd2e * atom->q[i]*atom->q[j]*sqrt(r2inv); + if (rsq > cut_coul_innersq) { + switch1 = (cut_coulsq-rsq) * (cut_coulsq-rsq) * + (cut_coulsq + 2.0*rsq - 3.0*cut_coul_innersq) / + denom_coul; + phicoul *= switch1; + } + one.eng_coul = factor_coul*phicoul; + } else one.eng_coul = 0.0; + if (rsq < cut_ljsq) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + philj *= switch1; + } + one.eng_vdwl = factor_lj*philj; + } else one.eng_vdwl = 0.0; + } +} diff --git a/src/MOLECULE/pair_lj_charmm_coul_charmm.h b/src/MOLECULE/pair_lj_charmm_coul_charmm.h new file mode 100644 index 0000000000000000000000000000000000000000..d6a6c63d29422a2415d6ad93b4a370305c74ca65 --- /dev/null +++ b/src/MOLECULE/pair_lj_charmm_coul_charmm.h @@ -0,0 +1,47 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_LJ_CHARMM_COUL_CHARMM_H +#define PAIR_LJ_CHARMM_COUL_CHARMM_H + +#include "pair.h" + +class PairLJCharmmCoulCharmm : public Pair { + public: + // these variables are public so DihedralCharmm can see them + double **lj14_1,**lj14_2,**lj14_3,**lj14_4; + + PairLJCharmmCoulCharmm() {} + ~PairLJCharmmCoulCharmm(); + virtual void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void init_style(); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + virtual void single(int, int, int, int, double, double, double, int, One &); + + protected: + double cut_lj_inner,cut_lj,cut_coul_inner,cut_coul; + double cut_lj_innersq,cut_ljsq,cut_coul_innersq,cut_coulsq,cut_bothsq; + double denom_lj,denom_coul; + double **epsilon,**sigma,**eps14,**sigma14; + double **lj1,**lj2,**lj3,**lj4; + + void allocate(); +}; + +#endif diff --git a/src/MOLECULE/pair_lj_charmm_coul_charmm_implicit.cpp b/src/MOLECULE/pair_lj_charmm_coul_charmm_implicit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d4bc75b172c3b5eda2cd6084e1f8db38a05094e2 --- /dev/null +++ b/src/MOLECULE/pair_lj_charmm_coul_charmm_implicit.cpp @@ -0,0 +1,214 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "pair_lj_charmm_coul_charmm_implicit.h" +#include "atom.h" +#include "force.h" +#include "update.h" +#include "neighbor.h" + +/* ---------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmmImplicit::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double factor,phicoul,philj,switch1,switch2; + int *neighs; + double **f; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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_bothsq) { + r2inv = 1.0/rsq; + + if (rsq < cut_coulsq) { + forcecoul = 2.0 * qqrd2e * qtmp*q[j]*r2inv; + if (rsq > cut_coul_innersq) { + switch1 = (cut_coulsq-rsq) * (cut_coulsq-rsq) * + (cut_coulsq + 2.0*rsq - 3.0*cut_coul_innersq) / denom_coul; + switch2 = 12.0*rsq * (cut_coulsq-rsq) * + (rsq-cut_coul_innersq) / denom_coul; + forcecoul *= switch1 + switch2; + } + } else forcecoul = 0.0; + + if (rsq < cut_ljsq) { + r6inv = r2inv*r2inv*r2inv; + jtype = type[j]; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + } else forcelj = 0.0; + + fforce = (factor_coul*forcecoul + factor_lj*forcelj) * 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 (newton_pair || j < nlocal) factor = 1.0; + else factor = 0.5; + if (rsq < cut_coulsq) { + phicoul = qqrd2e * qtmp*q[j]*r2inv; + if (rsq > cut_coul_innersq) { + switch1 = (cut_coulsq-rsq) * (cut_coulsq-rsq) * + (cut_coulsq + 2.0*rsq - 3.0*cut_coul_innersq) / + denom_coul; + phicoul *= switch1; + } + eng_coul += factor*factor_coul*phicoul; + } + if (rsq < cut_ljsq) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + philj *= switch1; + } + eng_vdwl += factor*factor_lj*philj; + } + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmmImplicit::single(int i, int j, int itype, int jtype, + double rsq, double factor_coul, + double factor_lj, + int eflag, One &one) +{ + double r2inv,r6inv,switch1,switch2,forcecoul,forcelj,phicoul,philj; + + r2inv = 1.0/rsq; + if (rsq < cut_coulsq) { + forcecoul = 2.0 * force->qqrd2e * atom->q[i]*atom->q[j]*r2inv; + if (rsq > cut_coul_innersq) { + switch1 = (cut_coulsq-rsq) * (cut_coulsq-rsq) * + (cut_coulsq + 2.0*rsq - 3.0*cut_coul_innersq) / denom_coul; + switch2 = 12.0*rsq * (cut_coulsq-rsq) * + (rsq-cut_coul_innersq) / denom_coul; + forcecoul *= switch1 + switch2; + } + } else forcecoul = 0.0; + if (rsq < cut_ljsq) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + } else forcelj = 0.0; + one.fforce = (factor_coul*forcecoul + factor_lj*forcelj) * r2inv; + + if (eflag) { + if (rsq < cut_coulsq) { + phicoul = force->qqrd2e * atom->q[i]*atom->q[j]*r2inv; + if (rsq > cut_coul_innersq) { + switch1 = (cut_coulsq-rsq) * (cut_coulsq-rsq) * + (cut_coulsq + 2.0*rsq - 3.0*cut_coul_innersq) / + denom_coul; + phicoul *= switch1; + } + one.eng_coul = factor_coul*phicoul; + } else one.eng_coul = 0.0; + if (rsq < cut_ljsq) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + philj *= switch1; + } + one.eng_vdwl = factor_lj*philj; + } else one.eng_vdwl = 0.0; + } +} diff --git a/src/MOLECULE/pair_lj_charmm_coul_charmm_implicit.h b/src/MOLECULE/pair_lj_charmm_coul_charmm_implicit.h new file mode 100644 index 0000000000000000000000000000000000000000..2ef707551f754b70853e893b4594e748e0519c82 --- /dev/null +++ b/src/MOLECULE/pair_lj_charmm_coul_charmm_implicit.h @@ -0,0 +1,25 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_LJ_CHARMM_COUL_CHARMM_IMPLICIT_H +#define PAIR_LJ_CHARMM_COUL_CHARMM_IMPLICIT_H + +#include "pair_lj_charmm_coul_charmm.h" + +class PairLJCharmmCoulCharmmImplicit : public PairLJCharmmCoulCharmm { + public: + void compute(int, int); + void single(int, int, int, int, double, double, double, int, One &); +}; + +#endif diff --git a/src/MOLECULE/style_molecule.h b/src/MOLECULE/style_molecule.h new file mode 100644 index 0000000000000000000000000000000000000000..1da512819deb46125711b59dfbab8f93311e48fc --- /dev/null +++ b/src/MOLECULE/style_molecule.h @@ -0,0 +1,116 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifdef AngleInclude +#include "angle_charmm.h" +#include "angle_cosine.h" +#include "angle_cosine_squared.h" +#include "angle_harmonic.h" +#include "angle_hybrid.h" +#endif + +#ifdef AngleClass +AngleStyle(charmm,AngleCharmm) +AngleStyle(cosine,AngleCosine) +AngleStyle(cosine/squared,AngleCosineSquared) +AngleStyle(harmonic,AngleHarmonic) +AngleStyle(hybrid,AngleHybrid) +#endif + +#ifdef AtomInclude +#include "atom_angle.h" +#include "atom_bond.h" +#include "atom_full.h" +#include "atom_molecular.h" +#endif + +#ifdef AtomClass +AtomStyle(angle,AtomAngle) +AtomStyle(bond,AtomBond) +AtomStyle(full,AtomFull) +AtomStyle(molecular,AtomMolecular) +#endif + +#ifdef BondInclude +#include "bond_fene.h" +#include "bond_fene_expand.h" +#include "bond_harmonic.h" +#include "bond_hybrid.h" +#include "bond_morse.h" +#include "bond_nonlinear.h" +#include "bond_quartic.h" +#endif + +#ifdef BondClass +BondStyle(fene,BondFENE) +BondStyle(fene/expand,BondFENEExpand) +BondStyle(harmonic,BondHarmonic) +BondStyle(hybrid,BondHybrid) +BondStyle(morse,BondMorse) +BondStyle(nonlinear,BondNonlinear) +BondStyle(quartic,BondQuartic) +#endif + +#ifdef DihedralInclude +#include "dihedral_charmm.h" +#include "dihedral_harmonic.h" +#include "dihedral_helix.h" +#include "dihedral_hybrid.h" +#include "dihedral_multi_harmonic.h" +#include "dihedral_opls.h" +#endif + +#ifdef DihedralClass +DihedralStyle(charmm,DihedralCharmm) +DihedralStyle(harmonic,DihedralHarmonic) +DihedralStyle(helix,DihedralHelix) +DihedralStyle(hybrid,DihedralHybrid) +DihedralStyle(multi/harmonic,DihedralMultiHarmonic) +DihedralStyle(opls,DihedralOPLS) +#endif + +#ifdef DumpInclude +#include "dump_bond.h" +#endif + +#ifdef DumpClass +DumpStyle(bond,DumpBond) +#endif + +#ifdef FixInclude +#endif + +#ifdef FixClass +#endif + +#ifdef ImproperInclude +#include "improper_cvff.h" +#include "improper_harmonic.h" +#include "improper_hybrid.h" +#endif + +#ifdef ImproperClass +ImproperStyle(cvff,ImproperCvff) +ImproperStyle(harmonic,ImproperHarmonic) +ImproperStyle(hybrid,ImproperHybrid) +#endif + +#ifdef PairInclude +#include "pair_lj_charmm_coul_charmm.h" +#include "pair_lj_charmm_coul_charmm_implicit.h" +#endif + +#ifdef PairClass +PairStyle(lj/charmm/coul/charmm,PairLJCharmmCoulCharmm) +PairStyle(lj/charmm/coul/charmm/implicit,PairLJCharmmCoulCharmmImplicit) +#endif diff --git a/src/Make.csh b/src/Make.csh new file mode 100644 index 0000000000000000000000000000000000000000..d5dda6e40da18583996ec07dc80b901eec9de768 --- /dev/null +++ b/src/Make.csh @@ -0,0 +1,18 @@ +# Make.csh = update Makefile.lib and Makefile.list +# use current list of *.cpp and *.h files in src dir + +if ($1 == "Makefile.lib") then + + set list = `ls --width=10000 *.cpp | sed s/main\.cpp//` + sed -i -e "s/SRC =\t.*/SRC =\t$list/" Makefile.lib + set list = `ls --width=10000 *.h` + sed -i -e "s/INC =\t.*/INC =\t$list/" Makefile.lib + +else if ($1 == "Makefile.list") then + + set list = `ls --width=10000 *.cpp` + sed -i -e "s/SRC =\t.*/SRC =\t$list/" Makefile.list + set list = `ls --width=10000 *.h` + sed -i -e "s/INC =\t.*/INC =\t$list/" Makefile.list + +endif diff --git a/src/Makefile b/src/Makefile new file mode 100755 index 0000000000000000000000000000000000000000..377f4affaa87b85f41e6b7bdae53f22c329f7e35 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,158 @@ +# LAMMPS multiple-machine Makefile + +SHELL = /bin/sh +#.IGNORE: + +# Definitions + +ROOT = lmp +EXE = $(ROOT)_$@ +SRC = $(wildcard *.cpp) +INC = $(wildcard *.h) +OBJ = $(SRC:.cpp=.o) + +# Targets + +help: + @echo 'Type "make target" where target is one of:' + @echo '' + @files="`ls MAKE/Makefile.*`"; \ + for file in $$files; do head -1 $$file; done + +clean: + rm -r Obj_* + +.DEFAULT: + @test -f MAKE/Makefile.$@ + @if [ ! -d Obj_$@ ]; then mkdir Obj_$@; fi + @cp -p $(SRC) $(INC) Obj_$@ + @cp MAKE/Makefile.$@ Obj_$@/Makefile + @cd Obj_$@; \ + $(MAKE) "OBJ = $(OBJ)" "INC = $(INC)" "EXE = ../$(EXE)" ../$(EXE) + @if [ -d Obj_$@ ]; then cd Obj_$@; rm $(SRC) $(INC) Makefile*; fi + +# Update Makefile.lib and Makefile.list + +makelib: + @csh Make.csh Makefile.lib + +makelist: + @csh Make.csh Makefile.list + +# Packages + +package: + @echo 'Available packages: class2, dpd, granular, kspace' + @echo ' molecule, poems, xtc' + @echo ' "make yes-name" include a package' + @echo ' "make no-name" exclude a package' + @echo ' "make yes-all" include all packages' + @echo ' "make no-all" exclude all packages' + +yes-all: + make yes-class2 yes-dpd yes-granular yes-kspace \ + yes-manybody yes-molecule yes-poems yes-xtc + +no-all: + @echo 'Removing files, ignore any rm errors ...' + @cd CLASS2; csh -f Install.csh 0 + @cd DPD; csh -f Install.csh 0 + @cd GRANULAR; csh -f Install.csh 0 + @cd KSPACE; csh -f Install.csh 0 + @cd MANYBODY; csh -f Install.csh 0 + @cd MOLECULE; csh -f Install.csh 0 + @cd POEMS; csh -f Install.csh 0 + @cd XTC; csh -f Install.csh 0 + @make clean + +yes-class2: + @cd CLASS2; csh -f Install.csh 1 +no-class2: + @echo 'Removing files, ignore any rm errors ...' + @cd CLASS2; csh -f Install.csh 0 + @make clean + +yes-dpd: + @cd DPD; csh -f Install.csh 1 +no-dpd: + @echo 'Removing files, ignore any rm errors ...' + @cd DPD; csh -f Install.csh 0 + @make clean + +yes-granular: + @cd GRANULAR; csh -f Install.csh 1 +no-granular: + @echo 'Removing files, ignore any rm errors ...' + @cd GRANULAR; csh -f Install.csh 0 + @make clean + +yes-kspace: + @cd KSPACE; csh -f Install.csh 1 +no-kspace: + @echo 'Removing files, ignore any rm errors ...' + @cd KSPACE; csh -f Install.csh 0 + @make clean + +yes-manybody: + @cd MANYBODY; csh -f Install.csh 1 +no-manybody: + @echo 'Removing files, ignore any rm errors ...' + @cd MANYBODY; csh -f Install.csh 0 + @make clean + +yes-molecule: + @cd MOLECULE; csh -f Install.csh 1 +no-molecule: + @echo 'Removing files, ignore any rm errors ...' + @cd MOLECULE; csh -f Install.csh 0 + @make clean + +yes-poems: + @cd POEMS; csh -f Install.csh 1 +no-poems: + @echo 'Removing files, ignore any rm errors ...' + @cd POEMS; csh -f Install.csh 0 + @make clean + +yes-xtc: + @cd XTC; csh -f Install.csh 1 +no-xtc: + @echo 'Removing files, ignore any rm errors ...' + @cd XTC; csh -f Install.csh 0 + @make clean + +# update src files with package files + +package-update: + @csh -f Package.csh CLASS2 update + @csh -f Package.csh DPD update + @csh -f Package.csh GRANULAR update + @csh -f Package.csh KSPACE update + @csh -f Package.csh MANYBODY update + @csh -f Package.csh MOLECULE update + @csh -f Package.csh POEMS update + @csh -f Package.csh XTC update + +# overwrite package files with src files + +package-overwrite: + @csh -f Package.csh CLASS2 overwrite + @csh -f Package.csh DPD overwrite + @csh -f Package.csh GRANULAR overwrite + @csh -f Package.csh KSPACE overwrite + @csh -f Package.csh MANYBODY overwrite + @csh -f Package.csh MOLECULE overwrite + @csh -f Package.csh POEMS overwrite + @csh -f Package.csh XTC overwrite + +# check differences between src and pacakge files + +package-check: + @csh -f Package.csh CLASS2 check + @csh -f Package.csh DPD check + @csh -f Package.csh GRANULAR check + @csh -f Package.csh KSPACE check + @csh -f Package.csh MANYBODY check + @csh -f Package.csh MOLECULE check + @csh -f Package.csh POEMS check + @csh -f Package.csh XTC check diff --git a/src/Makefile.lib b/src/Makefile.lib new file mode 100644 index 0000000000000000000000000000000000000000..4c38138e1858464e95a406562f694706c7383f9d --- /dev/null +++ b/src/Makefile.lib @@ -0,0 +1,34 @@ +# LAMMPS library multiple-machine Makefile + +SHELL = /bin/sh + +# Definitions + +ROOT = lmp +EXE = lib$(ROOT)_$@.a + +SRC = angle.cpp angle_charmm.cpp angle_cosine.cpp angle_cosine_squared.cpp angle_harmonic.cpp angle_hybrid.cpp atom.cpp atom_angle.cpp atom_atomic.cpp atom_bond.cpp atom_charge.cpp atom_full.cpp atom_hybrid.cpp atom_molecular.cpp bond.cpp bond_fene.cpp bond_fene_expand.cpp bond_harmonic.cpp bond_hybrid.cpp bond_morse.cpp bond_nonlinear.cpp bond_quartic.cpp comm.cpp create_atoms.cpp create_box.cpp delete_atoms.cpp delete_bonds.cpp dihedral.cpp dihedral_charmm.cpp dihedral_harmonic.cpp dihedral_helix.cpp dihedral_hybrid.cpp dihedral_multi_harmonic.cpp dihedral_opls.cpp displace_atoms.cpp do dump.cpp dump_atom.cpp dump_bond.cpp dump_custom.cpp dump_dcd.cpp dump_xyz.cpp error.cpp ewald.cpp fft3d.cpp fft3d_wrap.cpp finish.cpp fix.cpp fix_add_force.cpp fix_ave_force.cpp fix_centro.cpp fix_com.cpp fix_drag.cpp fix_efield.cpp fix_energy.cpp fix_enforce2d.cpp fix_gravity.cpp fix_gyration.cpp fix_indent.cpp fix_langevin.cpp fix_line_force.cpp fix_minimize.cpp fix_momentum.cpp fix_msd.cpp fix_nph.cpp fix_npt.cpp fix_nve.cpp fix_nvt.cpp fix_orient_fcc.cpp fix_plane_force.cpp fix_print.cpp fix_rdf.cpp fix_recenter.cpp fix_respa.cpp fix_rigid.cpp fix_set_force.cpp fix_shake.cpp fix_spring.cpp fix_spring_rg.cpp fix_spring_self.cpp fix_stress.cpp fix_temp_rescale.cpp fix_tmd.cpp fix_uniaxial.cpp fix_viscous.cpp fix_volume_rescale.cpp fix_wall_lj93.cpp fix_wall_reflect.cpp fix_wiggle.cpp force.cpp group.cpp improper.cpp improper_cvff.cpp improper_harmonic.cpp improper_hybrid.cpp input.cpp kspace.cpp lammps.cpp library.cpp memory.cpp min.cpp min_cg.cpp min_cg_fr.cpp min_sd.cpp minimize.cpp modify.cpp neigh_bond.cpp neigh_full.cpp neigh_gran.cpp neigh_half.cpp neigh_respa.cpp neighbor.cpp output.cpp pack.cpp pair.cpp pair_buck.cpp pair_buck_coul_cut.cpp pair_buck_coul_long.cpp pair_eam.cpp pair_eam_alloy.cpp pair_eam_fs.cpp pair_hybrid.cpp pair_lj_charmm_coul_charmm.cpp pair_lj_charmm_coul_charmm_implicit.cpp pair_lj_charmm_coul_long.cpp pair_lj_cut.cpp pair_lj_cut_coul_cut.cpp pair_lj_cut_coul_debye.cpp pair_lj_cut_coul_long.cpp pair_lj_cut_coul_long_tip4p.cpp pair_lj_expand.cpp pair_lj_smooth.cpp pair_morse.cpp pair_soft.cpp pair_table.cpp pair_yukawa.cpp pppm.cpp pppm_tip4p.cpp pressure.cpp random_mars.cpp random_park.cpp read_data.cpp read_restart.cpp region.cpp region_block.cpp region_cylinder.cpp region_intersect.cpp region_sphere.cpp region_union.cpp remap.cpp remap_wrap.cpp replicate.cpp respa.cpp run.cpp set.cpp shell.cpp special.cpp system.cpp temp_full.cpp temp_partial.cpp temp_ramp.cpp temp_region.cpp temper.cpp temperature.cpp thermo.cpp timer.cpp universe.cpp update.cpp variable.cpp velocity.cpp verlet.cpp write_restart.cpp + +INC = angle.h angle_charmm.h angle_cosine.h angle_cosine_squared.h angle_harmonic.h angle_hybrid.h atom.h atom_angle.h atom_atomic.h atom_bond.h atom_charge.h atom_full.h atom_hybrid.h atom_molecular.h bond.h bond_fene.h bond_fene_expand.h bond_harmonic.h bond_hybrid.h bond_morse.h bond_nonlinear.h bond_quartic.h comm.h create_atoms.h create_box.h delete_atoms.h delete_bonds.h dihedral.h dihedral_charmm.h dihedral_harmonic.h dihedral_helix.h dihedral_hybrid.h dihedral_multi_harmonic.h dihedral_opls.h displace_atoms.h domain.h dump.h dump_atom.h dump_bond.h dump_custom.h dump_dcd.h dump_xyz.h error.h ewald.h fft3d.h fft3d_wrap.h finish.h fix.h fix_add_force.h fix_ave_force.h fix_centro.h fix_com.h fix_drag.h fix_efield.h fix_energy.h fix_enforce2d.h fix_gravity.h fix_gyration.h fix_indent.h fix_langevin.h fix_line_force.h fix_minimize.h fix_momentum.h fix_msd.h fix_nph.h fix_npt.h fix_nve.h fix_nvt.h fix_orient_fcc.h fix_plane_force.h fix_print.h fix_rdf.h fix_recenter.h fix_respa.h fix_rigid.h fix_set_force.h fix_shake.h fix_shear_history.h fix_spring.h fix_spring_rg.h fix_spring_self.h fix_stress.h fix_temp_rescale.h fix_tmd.h fix_uniaxial.h fix_viscous.h fix_volume_rescale.h fix_wall_lj93.h fix_wall_reflect.h fix_wiggle.h force.h group.h improper.h improper_cvff.h improper_harmonic.h improper_hybrid.h input.h integrate.h kspace.h lammps.h library.h memory.h memory.hold.h min.h min_cg.h min_cg_fr.h min_sd.h minimize.h modify.h neighbor.h output.h pack.h pair.h pair_buck.h pair_buck_coul_cut.h pair_buck_coul_long.h pair_eam.h pair_eam_alloy.h pair_eam_fs.h pair_hybrid.h pair_lj_charmm_coul_charmm.h pair_lj_charmm_coul_charmm_implicit.h pair_lj_charmm_coul_long.h pair_lj_class2_coul_long.h pair_lj_cut.h pair_lj_cut_coul_cut.h pair_lj_cut_coul_debye.h pair_lj_cut_coul_long.h pair_lj_cut_coul_long_tip4p.h pair_lj_expand.h pair_lj_smooth.h pair_morse.h pair_soft.h pair_table.h pair_yukawa.h pppm.h pppm_tip4p.h pressure.h random_mars.h random_park.h read_data.h read_restart.h region.h region_block.h region_cylinder.h region_intersect.h region_sphere.h region_union.h remap.h remap_wrap.h replicate.h respa.h run.h set.h shell.h special.h style.h style_class2.h style_dpd.h style_granular.h style_kspace.h style_manybody.h style_molecule.h style_poems.h style_user.h style_xtc.h system.h temp_full.h temp_partial.h temp_ramp.h temp_region.h temper.h temperature.h thermo.h timer.h universe.h update.h variable.h velocity.h verlet.h write_restart.h + +OBJ = $(SRC:.cpp=.o) + +# Targets + +help: + @echo 'Type "make target" where target is one of:' + @echo '' + @files="`ls MAKE/Makefile.*`"; \ + for file in $$files; do head -1 $$file; done + +clean: + rm -r Obj_* + +.DEFAULT: + @test -f MAKE/Makefile.$@ + @if [ ! -d Obj_$@ ]; then mkdir Obj_$@; fi + @cp -p $(SRC) $(INC) Obj_$@ + @cp MAKE/Makefile.$@ Obj_$@/Makefile + @cd Obj_$@; \ + $(MAKE) "OBJ = $(OBJ)" "INC = $(INC)" "EXE = ../$(EXE)" lib + @if [ -d Obj_$@ ]; then cd Obj_$@; rm $(SRC) $(INC) Makefile*; fi diff --git a/src/Makefile.list b/src/Makefile.list new file mode 100644 index 0000000000000000000000000000000000000000..719c5bf9819ec5730c03c6fe74fee395383af207 --- /dev/null +++ b/src/Makefile.list @@ -0,0 +1,34 @@ +# LAMMPS multiple-machine Makefile with explicit file list + +SHELL = /bin/sh + +# Definitions + +ROOT = lmp +EXE = $(ROOT)_$@ + +SRC = angle.cpp angle_charmm.cpp angle_cosine.cpp angle_cosine_squared.cpp angle_harmonic.cpp angle_hybrid.cpp atom.cpp atom_angle.cpp atom_atomic.cpp atom_bond.cpp atom_charge.cpp atom_full.cpp atom_hybrid.cpp atom_molecular.cpp bond.cpp bond_fene.cpp bond_fene_expand.cpp bond_harmonic.cpp bond_hybrid.cpp bond_morse.cpp bond_nonlinear.cpp bond_quartic.cpp comm.cpp create_atoms.cpp create_box.cpp delete_atoms.cpp delete_bonds.cpp dihedral.cpp dihedral_charmm.cpp dihedral_harmonic.cpp dihedral_helix.cpp dihedral_hybrid.cpp dihedral_multi_harmonic.cpp dihedral_opls.cpp displace_atoms.cpp domain.cpp dump.cpp dump_atom.cpp dump_bond.cpp dump_custom.cpp dump_dcd.cpp dump_xyz.cpp error.cpp ewald.cpp fft3d.cpp fft3d_wrap.cpp finish.cpp fix.cpp fix_add_force.cpp fix_ave_force.cpp fix_centro.cpp fix_com.cpp fix_drag.cpp fix_efield.cpp fix_energy.cpp fix_enforce2d.cpp fix_gravity.cpp fix_gyration.cpp fix_indent.cpp fix_langevin.cpp fix_line_force.cpp fix_minimize.cpp fix_momentum.cpp fix_msd.cpp fix_nph.cpp fix_npt.cpp fix_nve.cpp fix_nvt.cpp fix_orient_fcc.cpp fix_plane_force.cpp fix_print.cpp fix_rdf.cpp fix_recenter.cpp fix_respa.cpp fix_rigid.cpp fix_set_force.cpp fix_shake.cpp fix_spring.cpp fix_spring_rg.cpp fix_spring_self.cpp fix_stress.cpp fix_temp_rescale.cpp fix_tmd.cpp fix_uniaxial.cpp fix_viscous.cpp fix_volume_rescale.cpp fix_wall_lj93.cpp fix_wall_reflect.cpp fix_wiggle.cpp force.cpp group.cpp improper.cpp improper_cvff.cpp improper_harmonic.cpp improper_hybrid.cpp input.cpp kspace.cpp lammps.cpp library.cpp main.cpp memory.cpp min.cpp min_cg.cpp min_cg_fr.cpp min_sd.cpp minimize.cpp modify.cpp neigh_bond.cpp neigh_full.cpp neigh_gran.cpp neigh_half.cpp neigh_respa.cpp neighbor.cpp output.cpp pack.cpp pair.cpp pair_buck.cpp pair_buck_coul_cut.cpp pair_buck_coul_long.cpp pair_eam.cpp pair_eam_alloy.cpp pair_eam_fs.cpp pair_hybrid.cpp pair_lj_charmm_coul_charmm.cpp pair_lj_charmm_coul_charmm_implicit.cpp pair_lj_charmm_coul_long.cpp pair_lj_cut.cpp pair_lj_cut_coul_cut.cpp pair_lj_cut_coul_debye.cpp pair_lj_cut_coul_long.cpp pair_lj_cut_coul_long_tip4p.cpp pair_lj_expand.cpp pair_lj_smooth.cpp pair_morse.cpp pair_soft.cpp pair_table.cpp pair_yukawa.cpp pppm.cpp pppm_tip4p.cpp pressure.cpp random_mars.cpp random_park.cpp read_data.cpp read_restart.cpp region.cpp region_block.cpp region_cylinder.cpp region_intersect.cpp region_sphere.cpp region_union.cpp remap.cpp remap_wrap.cpp replicate.cpp respa.cpp run.cpp set.cpp shell.cpp special.cpp system.cpp temp_full.cpp temp_partial.cpp temp_ramp.cpp temp_region.cpp temper.cpp temperature.cpp thermo.cpp timer.cpp universe.cpp update.cpp variable.cpp velocity.cpp verlet.cpp write_restart.cpp + +INC = angle.h angle_charmm.h angle_cosine.h angle_cosine_squared.h angle_harmonic.h angle_hybrid.h atom.h atom_angle.h atom_atomic.h atom_bond.h atom_charge.h atom_full.h atom_hybrid.h atom_molecular.h bond.h bond_fene.h bond_fene_expand.h bond_harmonic.h bond_hybrid.h bond_morse.h bond_nonlinear.h bond_quartic.h comm.h create_atoms.h create_box.h delete_atoms.h delete_bonds.h dihedral.h dihedral_charmm.h dihedral_harmonic.h dihedral_helix.h dihedral_hybrid.h dihedral_multi_harmonic.h dihedral_opls.h displace_atoms.h domain.h dump.h dump_atom.h dump_bond.h dump_custom.h dump_dcd.h dump_xyz.h error.h ewald.h fft3d.h fft3d_wrap.h finish.h fix.h fix_add_force.h fix_ave_force.h fix_centro.h fix_com.h fix_drag.h fix_efield.h fix_energy.h fix_enforce2d.h fix_gravity.h fix_gyration.h fix_indent.h fix_langevin.h fix_line_force.h fix_minimize.h fix_momentum.h fix_msd.h fix_nph.h fix_npt.h fix_nve.h fix_nvt.h fix_orient_fcc.h fix_plane_force.h fix_print.h fix_rdf.h fix_recenter.h fix_respa.h fix_rigid.h fix_set_force.h fix_shake.h fix_shear_history.h fix_spring.h fix_spring_rg.h fix_spring_self.h fix_stress.h fix_temp_rescale.h fix_tmd.h fix_uniaxial.h fix_viscous.h fix_volume_rescale.h fix_wall_lj93.h fix_wall_reflect.h fix_wiggle.h force.h group.h improper.h improper_cvff.h improper_harmonic.h improper_hybrid.h input.h integrate.h kspace.h lammps.h library.h memory.h memory.hold.h min.h min_cg.h min_cg_fr.h min_sd.h minimize.h modify.h neighbor.h output.h pack.h pair.h pair_buck.h pair_buck_coul_cut.h pair_buck_coul_long.h pair_eam.h pair_eam_alloy.h pair_eam_fs.h pair_hybrid.h pair_lj_charmm_coul_charmm.h pair_lj_charmm_coul_charmm_implicit.h pair_lj_charmm_coul_long.h pair_lj_class2_coul_long.h pair_lj_cut.h pair_lj_cut_coul_cut.h pair_lj_cut_coul_debye.h pair_lj_cut_coul_long.h pair_lj_cut_coul_long_tip4p.h pair_lj_expand.h pair_lj_smooth.h pair_morse.h pair_soft.h pair_table.h pair_yukawa.h pppm.h pppm_tip4p.h pressure.h random_mars.h random_park.h read_data.h read_restart.h region.h region_block.h region_cylinder.h region_intersect.h region_sphere.h region_union.h remap.h remap_wrap.h replicate.h respa.h run.h set.h shell.h special.h style.h style_class2.h style_dpd.h style_granular.h style_kspace.h style_manybody.h style_molecule.h style_poems.h style_user.h style_xtc.h system.h temp_full.h temp_partial.h temp_ramp.h temp_region.h temper.h temperature.h thermo.h timer.h universe.h update.h variable.h velocity.h verlet.h write_restart.h + +OBJ = $(SRC:.cpp=.o) + +# Targets + +help: + @echo 'Type "make target" where target is one of:' + @echo '' + @files="`ls MAKE/Makefile.*`"; \ + for file in $$files; do head -1 $$file; done + +clean: + rm -r Obj_* + +.DEFAULT: + @test -f MAKE/Makefile.$@ + @if [ ! -d Obj_$@ ]; then mkdir Obj_$@; fi + @cp -p $(SRC) $(INC) Obj_$@ + @cp MAKE/Makefile.$@ Obj_$@/Makefile + @cd Obj_$@; \ + $(MAKE) "OBJ = $(OBJ)" "INC = $(INC)" "EXE = ../$(EXE)" ../$(EXE) + @if [ -d Obj_$@ ]; then cd Obj_$@; rm $(SRC) $(INC) Makefile*; fi diff --git a/src/POEMS/Install.csh b/src/POEMS/Install.csh new file mode 100644 index 0000000000000000000000000000000000000000..00fdf2503be632ce4e7d875a6db80c3886bce397 --- /dev/null +++ b/src/POEMS/Install.csh @@ -0,0 +1,18 @@ +# Install/unInstall package classes in LAMMPS + +if ($1 == 1) then + + cp style_poems.h .. + + cp fix_poems.cpp .. + cp fix_poems.h .. + +else if ($1 == 0) then + + rm ../style_poems.h + touch ../style_poems.h + + rm ../fix_poems.cpp + rm ../fix_poems.h + +endif diff --git a/src/POEMS/fix_poems.cpp b/src/POEMS/fix_poems.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8dbc778a83e89baf038a7be73593c7c805205354 --- /dev/null +++ b/src/POEMS/fix_poems.cpp @@ -0,0 +1,1573 @@ +/* ----------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "math.h" +#include "stdio.h" +#include "string.h" +#include "mpi.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 "memory.h" +#include "error.h" + +#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 + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +/* ---------------------------------------------------------------------- + constructor - define rigid bodies and joints, initiate POEMS +------------------------------------------------------------------------- */ + +FixPOEMS::FixPOEMS(int narg, char **arg) : Fix(narg, arg) +{ + int i,j,ibody; + + MPI_Comm_rank(world,&me); + + // can't use with pure granular style since mass arrays are different + // hybrid granular style would be OK if fix were on non-granular atoms + + if (strcmp(atom->style,"granular") == 0) + error->all("Cannot use fix poems with atom style granular"); + + // 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("Illegal fix poems command"); + + // group = arg has list of groups + + if (strcmp(arg[3],"group") == 0) { + nbody = narg-4; + if (nbody <= 0) error->all("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("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("Illegal fix poems command"); + if (atom->molecular == 0) + error->all("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("Illegal fix poems command"); + + // error if no bodies + // error if any atom in too many bodies + + if (nbody == 0) error->all("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("Atom in too many rigid bodies - boost MAXBODY"); + + // create all nbody-length arrays + + nrigid = new int[nbody]; + masstotal = new double[nbody]; + xcm = memory->create_2d_double_array(nbody,3,"poems:xcm"); + vcm = memory->create_2d_double_array(nbody,3,"poems:vcm"); + fcm = memory->create_2d_double_array(nbody,3,"poems:fcm"); + inertia = memory->create_2d_double_array(nbody,3,"poems:inertia"); + ex_space = memory->create_2d_double_array(nbody,3,"poems:ex_space"); + ey_space = memory->create_2d_double_array(nbody,3,"poems:ey_space"); + ez_space = memory->create_2d_double_array(nbody,3,"poems:ez_space"); + angmom = memory->create_2d_double_array(nbody,3,"poems:angmom"); + omega = memory->create_2d_double_array(nbody,3,"poems:omega"); + torque = memory->create_2d_double_array(nbody,3,"poems:torque"); + + sum = memory->create_2d_double_array(nbody,6,"poems:sum"); + all = memory->create_2d_double_array(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("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); + } + + // zero fix_poems virial in case pressure uses it before 1st fix_poems call + + for (int n = 0; n < 6; n++) virial[n] = 0.0; +} + +/* ---------------------------------------------------------------------- + destructor - 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->sfree(natom2body); + memory->destroy_2d_int_array(atom2body); + memory->destroy_2d_double_array(displace); + + // delete nbody-length arrays + + delete [] nrigid; + delete [] masstotal; + memory->destroy_2d_double_array(xcm); + memory->destroy_2d_double_array(vcm); + memory->destroy_2d_double_array(fcm); + memory->destroy_2d_double_array(inertia); + memory->destroy_2d_double_array(ex_space); + memory->destroy_2d_double_array(ey_space); + memory->destroy_2d_double_array(ez_space); + memory->destroy_2d_double_array(angmom); + memory->destroy_2d_double_array(omega); + memory->destroy_2d_double_array(torque); + + memory->destroy_2d_double_array(sum); + memory->destroy_2d_double_array(all); + + // delete joint arrays + + memory->destroy_2d_int_array(jointbody); + memory->destroy_2d_double_array(xjoint); + delete [] freelist; + + // delete POEMS object + + delete poems; +} + +/* ---------------------------------------------------------------------- */ + +int FixPOEMS::setmask() +{ + int mask = 0; + mask |= INITIAL_INTEGRATE; + mask |= FINAL_INTEGRATE; + mask |= POST_FORCE; + mask |= INITIAL_INTEGRATE_RESPA; + mask |= FINAL_INTEGRATE_RESPA; + mask |= POST_FORCE_RESPA; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixPOEMS::init() +{ + int i,j,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("More than one poems fix"); + + // 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("POEMS fix must come before NPT/NPH fix"); + } + + // compute poems contribution to virial every step if fix NPT,NPH exists + + pressure_flag = 0; + for (int i = 0; i < modify->nfix; i++) { + if (strcmp(modify->fix[i]->style,"npt") == 0) pressure_flag = 1; + if (strcmp(modify->fix[i]->style,"nph") == 0) pressure_flag = 1; + } + + // timestep info + + dtv = update->dt; + dtf = 0.5 * update->dt * force->ftm2v; + dthalf = 0.5 * update->dt; + + // rRESPA info + + if (strcmp(update->integrate_style,"respa") == 0) { + 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; + int *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] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + 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] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + 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 = memory->create_2d_double_array(3,3,"fix_rigid:tensor"); + double **evectors = memory->create_2d_double_array(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("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("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_2d_double_array(tensor); + memory->destroy_2d_double_array(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] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + 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] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + 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("Bad principal moments"); + if (fabs(all[ibody][3]) > TOLERANCE || + fabs(all[ibody][4]) > TOLERANCE || + fabs(all[ibody][5]) > TOLERANCE) + error->all("Bad principal moments"); + } +} + +/* ---------------------------------------------------------------------- + compute initial rigid body info + make setup call to POEMS +------------------------------------------------------------------------- */ + +void FixPOEMS::setup() +{ + int i,j,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; + int *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] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + 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]; + } + + // set omega from angmom + + for (ibody = 0; ibody < nbody; ibody++) + omega_from_mq(angmom[ibody],ex_space[ibody],ey_space[ibody], + ez_space[ibody],inertia[ibody],omega[ibody]); + + // reset velocities from omega + // guestimate virial as 2x the set_v contribution + // since post_force doesn't compute virial + + for (int n = 0; n < 6; n++) virial[n] = 0.0; + set_v(1); + for (int n = 0; n < 6; n++) virial[n] *= 2.0; + + double ke = 0.0; + for (i = 0; i < nlocal; i++) + if (natom2body[i]) + ke += mass[atom->type[i]] * + (v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]); + + // use post_force() to compute initial fcm & torque + + post_force(1); + + // 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() +{ + // perform POEMS integration + + poems->LobattoOne(xcm,vcm,omega,torque,fcm,ex_space,ey_space,ez_space); + + // set coords and velocities of atoms in rigid bodies + + int vflag = 0; + if (pressure_flag || output->next_thermo == update->ntimestep) vflag = 1; + set_xv(vflag); +} + +/* ---------------------------------------------------------------------- + compute fcm,torque on each rigid body + only count joint atoms in 1st body +------------------------------------------------------------------------- */ + +void FixPOEMS::post_force(int vflag) +{ + int i,j,ibody; + + int *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; + + int xbox,ybox,zbox; + 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]; + + sum[ibody][0] += f[i][0]; + sum[ibody][1] += f[i][1]; + sum[ibody][2] += f[i][2]; + + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + 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 + + int vflag = 0; + if (pressure_flag || output->next_thermo == update->ntimestep) vflag = 1; + set_v(vflag); +} + +/* ---------------------------------------------------------------------- */ + +void FixPOEMS::initial_integrate_respa(int ilevel, int flag) +{ + if (flag) return; // only used by NPT,NPH + + dtv = step_respa[ilevel]; + dtf = 0.5 * step_respa[ilevel] * force->ftm2v; + dthalf = 0.5 * step_respa[ilevel]; + + if (ilevel == 0) initial_integrate(); + 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) +{ + dtf = 0.5 * step_respa[ilevel] * force->ftm2v; + final_integrate(); +} + +/* ---------------------------------------------------------------------- + count # of degrees-of-freedom removed by fix_poems for atoms in igroup +------------------------------------------------------------------------- */ + +int FixPOEMS::dof(int igroup) +{ + int i,j; + + 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 (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 (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 dilation in idim + called by various fixes that change box + NOTE: cannot do this by changing xcm of each body in cluster + or even 1st body in cluster since POEMS does not observe xcm + but only sets xcm + so dilation needs to be coordinated with POEMS + this routine does nothing for now +------------------------------------------------------------------------- */ + +void FixPOEMS::dilate(int idim, + double oldlo, double oldhi, double newlo, double newhi) +{ + // double ratio; + // for (int ibody = 0; ibody < nbody; ibody++) { + // ratio = (xcm[ibody][idim] - oldlo) / (oldhi - oldlo); + // xcm[ibody][idim] = newlo + ratio*(newhi - newlo); + // } +} + +/* ---------------------------------------------------------------------- */ + +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(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->sfree(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; + line = (char *) + memory->srealloc(line,maxline*sizeof(char),"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) mylist = memory->create_2d_int_array(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) jlist = memory->create_2d_int_array(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("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("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("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) { + jointbody = memory->create_2d_int_array(njoint,2,"poems:jointbody"); + xjoint = memory->create_2d_double_array(njoint,3,"poems:xjoint"); + myjoint = memory->create_2d_double_array(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_2d_int_array(mylist); + memory->destroy_2d_int_array(jlist); + memory->destroy_2d_double_array(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_2d_int_array(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_2d_int_array(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 vflag) +{ + int *image = atom->image; + 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 ibody; + int xbox,ybox,zbox; + + double vold0,vold1,vold2,fc0,fc1,fc2,massone,x0,x1,x2; + double *mass = atom->mass; + double **f = atom->f; + int *type = atom->type; + + // zero out fix_poems virial + + if (vflag) for (int n = 0; n < 6; n++) virial[n] = 0.0; + + // set x and v + // 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] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + + // save old positions and velocities for virial contribution + + if (vflag) { + x0 = x[i][0] + xbox*xprd; + x1 = x[i][1] + ybox*yprd; + x2 = x[i][2] + zbox*zprd; + + vold0 = v[i][0]; + vold1 = v[i][1]; + vold2 = v[i][2]; + } + + 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]; + + x[i][0] += xcm[ibody][0] - xbox*xprd; + x[i][1] += xcm[ibody][1] - ybox*yprd; + x[i][2] += xcm[ibody][2] - zbox*zprd; + + // compute body constraint forces for virial + + if (vflag) { + massone = mass[type[i]]; + fc0 = massone*(v[i][0] - vold0)/dtf - f[i][0]; + fc1 = massone*(v[i][1] - vold1)/dtf - f[i][1]; + fc2 = massone*(v[i][2] - vold2)/dtf - f[i][2]; + + virial[0] += 0.5*fc0*x0; + virial[1] += 0.5*fc1*x1; + virial[2] += 0.5*fc2*x2; + virial[3] += 0.5*fc1*x0; + virial[4] += 0.5*fc2*x0; + virial[5] += 0.5*fc2*x1; + } + } +} + +/* ---------------------------------------------------------------------- + set space-frame velocity of each atom in a rigid body + v = Vcm + (W cross (x - Xcm)) +------------------------------------------------------------------------- */ + +void FixPOEMS::set_v(int vflag) +{ + double **v = atom->v; + int nlocal = atom->nlocal; + + int ibody; + double dx,dy,dz; + + double vold0,vold1,vold2,fc0,fc1,fc2,massone,x0,x1,x2; + double *mass = atom->mass; + double **f = atom->f; + double **x = atom->x; + int *type = atom->type; + int *image = atom->image; + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + int xbox,ybox,zbox; + + // set v + // 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 (vflag) { + vold0 = v[i][0]; + vold1 = v[i][1]; + vold2 = 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]; + + // compute body constraint forces for virial + // use unwrapped atom positions + + if (vflag) { + massone = mass[type[i]]; + fc0 = massone*(v[i][0] - vold0)/dtf - f[i][0]; + fc1 = massone*(v[i][1] - vold1)/dtf - f[i][1]; + fc2 = massone*(v[i][2] - vold2)/dtf - f[i][2]; + + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + + x0 = x[i][0] + xbox*xprd; + x1 = x[i][1] + ybox*yprd; + x2 = x[i][2] + zbox*zprd; + + virial[0] += 0.5*fc0*x0; + virial[1] += 0.5*fc1*x1; + virial[2] += 0.5*fc2*x2; + virial[3] += 0.5*fc1*x0; + virial[4] += 0.5*fc2*x0; + virial[5] += 0.5*fc2*x1; + } + } +} + +/* ---------------------------------------------------------------------- + allocate local atom-based arrays +------------------------------------------------------------------------- */ + +void FixPOEMS::grow_arrays(int nmax) +{ + natom2body = (int *) + memory->srealloc(natom2body,nmax*sizeof(int),"fix_poems:natom2body"); + atom2body = + memory->grow_2d_int_array(atom2body,nmax,MAXBODY,"fix_poems:atom2body"); + displace = + memory->grow_2d_double_array(displace,nmax,3,"fix_poems:displace"); +} + +/* ---------------------------------------------------------------------- + copy values within local atom-based arrays +------------------------------------------------------------------------- */ + +void FixPOEMS::copy_arrays(int i, int j) +{ + 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 +------------------------------------------------------------------------- */ + +int FixPOEMS::memory_usage() +{ + int nmax = atom->nmax; + int 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<double> (natom2body[i]); + for (int j = 0; j < natom2body[i]; j++) + buf[m++] = static_cast<double> (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<int> (buf[m++]); + for (int i = 0; i < natom2body[nlocal]; i++) + atom2body[nlocal][i] = static_cast<int> (buf[m++]); + displace[nlocal][0] = buf[m++]; + displace[nlocal][1] = buf[m++]; + displace[nlocal][2] = buf[m++]; + return m; +} diff --git a/src/POEMS/fix_poems.h b/src/POEMS/fix_poems.h new file mode 100644 index 0000000000000000000000000000000000000000..da2a82c16c9ccbcc5bfca14cea57d5062452e97a --- /dev/null +++ b/src/POEMS/fix_poems.h @@ -0,0 +1,105 @@ +/* ----------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------ */ + +#ifndef FIX_POEMS_H +#define FIX_POEMS_H + +#include "fix.h" + +class Workspace; + +class FixPOEMS : public Fix { + public: + FixPOEMS(int narg, char **arg); + ~FixPOEMS(); + int setmask(); + void init(); + void setup(); + void initial_integrate(); + void post_force(int); + void final_integrate(); + void initial_integrate_respa(int, int); + void post_force_respa(int, int, int); + void final_integrate_respa(int); + + void grow_arrays(int); + void copy_arrays(int, int); + int memory_usage(); + int pack_exchange(int, double *); + int unpack_exchange(int, double *); + + int dof(int); + void dilate(int, double, double, double, double); + + private: + int me; + double dtv,dtf,dthalf; + double *step_respa; + int nlevels_respa; + int pressure_flag; + double total_ke; + + // atom assignment to rigid bodies + // double count joint atoms as being in multiple bodies + + int *natom2body; // # of bodies each atom is part of + int **atom2body; // list of bodies each atom is part of + double **displace; // atom displace in body coords for 1st body it's in + + // rigid body properties + // only nrigid double counts joint atoms as being in multiple bodies + // other quantities only count a joint atom as being in 1st body + + int nbody; // # of rigid bodies + int *nrigid; // # of atoms in each rigid body + double *masstotal; // total mass of each rigid body + double **xcm; // coords of center-of-mass of each rigid body + double **vcm; // velocity of center-of-mass of each + double **fcm; // force on center-of-mass of each + double **inertia; // 6 inertia components of each (xx,yy,zz,xy,yz,xz) + double **ex_space,**ey_space,**ez_space; + // orientation of each body in space coords + double **angmom; // angular momentum of each in space coords + double **omega; // angular velocity of each in space coords + double **torque; // torque on each rigid body in space coords + double **sum,**all; // work vectors + + // joint attributes between pairs of rigid bodies + + int ncluster; // # of independent clusters of coupled bodies + int njoint; // # of interbody joints + int **jointbody; // indices of 2 rigid bodies in each joint (1-N) + double **xjoint; // coords of each joint point + int nfree; // # of isolated unconnected bodies + int *freelist; // indices of isolated bodies (1-N) + + // POEMS object + + Workspace *poems; + + // internal class functions + + void readfile(char *); + int readline(FILE *, char **, int *); + void jointbuild(); + void sortlist(int, int **); + int loopcheck(int, int, int **); + int jacobi(double **, double *, double **); + void rotate(double **, int, int, int, int, double, double); + void omega_from_mq(double *, double *, double *, double *, + double *, double *); + void set_v(int); + void set_xv(int); +}; + +#endif diff --git a/src/POEMS/style_poems.h b/src/POEMS/style_poems.h new file mode 100644 index 0000000000000000000000000000000000000000..28509dc307d1f64220df201fbfba2cd330275ccb --- /dev/null +++ b/src/POEMS/style_poems.h @@ -0,0 +1,20 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifdef FixInclude +#include "fix_poems.h" +#endif + +#ifdef FixClass +FixStyle(poems,FixPOEMS) +#endif diff --git a/src/Package.csh b/src/Package.csh new file mode 100644 index 0000000000000000000000000000000000000000..606da3978a17475bf7136c801f1c2aafc7c8b4a9 --- /dev/null +++ b/src/Package.csh @@ -0,0 +1,101 @@ +# Package.csh = copy src files to/from package directories +# called from Makefile +# Syntax: csh Package.csh DIR update/overwrite/check + +# if last arg = "update": +# if 0-length src/style file doesn't exist, create src/style file +# via touch command, since LAMMPS needs it to do a build, +# but don't copy any more files since don't want to install package +# if 0-length src/style file already exists, do nothing +# if non-0-length src/style file exists, +# copy any package file into src if file doesn't exist or is different + +# if last arg = "overwrite": +# if package not installed (0-length src/style file), do nothing besides +# check that no package files are in src (except style file) +# flag src files that do not exist or are 0-length +# overwrite package files that are different than src version + +# if last arg = "check": +# if package not installed (0-length src/style file), do nothing besides +# check that no package files are in src (except style file) +# flag src files that do not exist or are 0-length +# list package files that are different than src version + +# use diff to compare files +# tried using cmp, but it doesn't satisfy if test if one file is +# just longer than the other (has new stuff added) + +set glob +set style = `echo $1 | sed 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'` +cd $1 + +if ($2 == "update") then + + echo "Updating src from $1 package" + + if (! -e ../style_$style.h) then + echo " $1 package is not installed, but creating dummy style file" + touch ../style_$style.h + else if (-z ../style_$style.h) then + echo " $1 package is not installed, no action" + else + foreach file (*.cpp *.h) + if (! -e ../$file) then + echo " updating src/$file" + cp $file .. + else if (`diff --brief $file ../$file` != "") then + echo " updating src/$file" + cp $file .. + endif + end + endif + +else if ($2 == "overwrite") then + + echo "Overwriting $1 package with src" + + if (-z ../style_$style.h) then + echo " $1 package is not installed, no action" + foreach file (*.cpp *.h) + if (-e ../$file && $file != "style_$style.h") then + echo " src/$file exists but should not" + endif + end + else + foreach file (*.cpp *.h) + if (! -e ../$file) then + echo " src/$file does not exist" + else if (-z ../$file) then + echo " src/$file is empty file" + else if (`diff --brief $file ../$file` != "") then + echo " overwriting $1/$file" + cp ../$file . + endif + end + endif + +else if ($2 == "check") then + + echo "Checking src versus $1 package" + + if (-z ../style_$style.h) then + echo " $1 package is not installed, no action" + foreach file (*.cpp *.h) + if (-e ../$file && $file != "style_$style.h") then + echo " src/$file exists but should not" + endif + end + else + foreach file (*.cpp *.h) + if (! -e ../$file) then + echo " src/$file does not exist" + else if (-z ../$file) then + echo " src/$file is empty file" + else if (`diff --brief $file ../$file` != "") then + echo " src/$file and $1/$file are different" + endif + end + endif + +endif diff --git a/src/STUBS/Makefile b/src/STUBS/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..38d013cbb8b9e0a49bdf92b4f9e3bf4f427d8796 --- /dev/null +++ b/src/STUBS/Makefile @@ -0,0 +1,40 @@ +# Makefile for MPI stubs - edit this for your platform + +SHELL = /bin/sh +.IGNORE: + +# Files + +SRC = mpi.c +INC = mpi.h + +# Definitions + +EXE = libmpi.a +OBJ = $(SRC:.c=.o) + +# System-specific settings + +CC = g++ +CCFLAGS = -O +ARCHIVE = ar +ARCHFLAG = rs + +# Target + +$(EXE): $(OBJ) + $(ARCHIVE) $(ARCHFLAG) $(EXE) $(OBJ) + +# Clean + +clean: + rm *.o libmpi.a + +# Compilation rules + +.cpp.o: + $(CC) $(CCFLAGS) -c $< + +# Individual dependencies + +$(OBJ): $(INC) diff --git a/src/STUBS/mpi.cpp b/src/STUBS/mpi.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5fd6ad77954fbfe5d7334bb66b7b4ac8d208848f --- /dev/null +++ b/src/STUBS/mpi.cpp @@ -0,0 +1,294 @@ +/* ----------------------------------------------------------------------- + LAMMPS 2003 (July 31) - Molecular Dynamics Simulator + Sandia National Laboratories, www.cs.sandia.gov/~sjplimp/lammps.html + 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. +------------------------------------------------------------------------ */ + +/* Single-processor "stub" versions of MPI routines */ + +#include "stdlib.h" +#include "stdio.h" +#include <sys/time.h> +#include "mpi.h" + +/* lo-level function prototypes */ + +void mpi_copy_int(void *, void *, int); +void mpi_copy_float(void *, void *, int); +void mpi_copy_double(void *, void *, int); +void mpi_copy_char(void *, void *, int); +void mpi_copy_byte(void *, void *, int); + +/* MPI Functions */ + +void MPI_Init(int *argc, char ***argv) {} + +void MPI_Comm_rank(MPI_Comm comm, int *me) +{ + *me = 0; +} + +void MPI_Comm_size(MPI_Comm comm, int *nprocs) +{ + *nprocs = 1; +} + +void MPI_Abort(MPI_Comm comm, int errorcode) +{ + exit(1); +} + +void MPI_Finalize() {} + +double MPI_Wtime() +{ + double time; + struct timeval tv; + + gettimeofday(&tv,NULL); + time = 1.0 * tv.tv_sec + 1.0e-6 * tv.tv_usec; + return time; +} + +void MPI_Send(void *buf, int count, MPI_Datatype datatype, + int dest, int tag, MPI_Comm comm) +{ + printf("MPI Stub WARNING: Should not send message to self\n"); +} + +void MPI_Rsend(void *buf, int count, MPI_Datatype datatype, + int dest, int tag, MPI_Comm comm) +{ + printf("MPI Stub WARNING: Should not rsend message to self\n"); +} + +void MPI_Recv(void *buf, int count, MPI_Datatype datatype, + int source, int tag, MPI_Comm comm, MPI_Status *status) +{ + printf("MPI Stub WARNING: Should not recv message from self\n"); +} + +void MPI_Irecv(void *buf, int count, MPI_Datatype datatype, + int source, int tag, MPI_Comm comm, MPI_Request *request) +{ + printf("MPI Stub WARNING: Should not recv message from self\n"); +} + +void MPI_Wait(MPI_Request *request, MPI_Status *status) +{ + printf("MPI Stub WARNING: Should not wait on message from self\n"); +} + +void MPI_Waitany(int count, MPI_Request *request, int *index, + MPI_Status *status) +{ + printf("MPI Stub WARNING: Should not wait on message from self\n"); +} + +void MPI_Sendrecv(void *sbuf, int scount, MPI_Datatype sdatatype, + int dest, int stag, void *rbuf, int rcount, + MPI_Datatype rdatatype, int source, int rtag, + MPI_Comm comm, MPI_Status *status) +{ + printf("MPI Stub WARNING: Should not send message to self\n"); +} + +void MPI_Get_count(MPI_Status *status, MPI_Datatype datatype, int *count) +{ + printf("MPI Stub WARNING: Should not get count of message to self\n"); +} + +void MPI_Comm_split(MPI_Comm comm, int color, int key, MPI_Comm *comm_out) +{ + *comm_out = comm; +} + +void MPI_Comm_dup(MPI_Comm comm, MPI_Comm *comm_out) +{ + *comm_out = comm; +} + +void MPI_Comm_free(MPI_Comm *comm) { } + +void MPI_Cart_create(MPI_Comm comm_old, int ndims, int *dims, int *periods, + int reorder, MPI_Comm *comm_cart) +{ + *comm_cart = comm_old; +} + +void MPI_Cart_get(MPI_Comm comm, int maxdims, int *dims, int *periods, + int *coords) +{ + dims[0] = dims[1] = dims[2] = 1; + periods[0] = periods[1] = periods[2] = 1; + coords[0] = coords[1] = coords[2] = 0; +} + +void MPI_Cart_shift(MPI_Comm comm, int direction, int displ, + int *source, int *dest) +{ + *source = *dest = 0; +} + +void MPI_Barrier(MPI_Comm comm) {} + +void MPI_Bcast(void *buf, int count, MPI_Datatype datatype, + int root, MPI_Comm comm) {} + +/* copy values from data1 to data2 */ + +void MPI_Allreduce(void *sendbuf, void *recvbuf, int count, + MPI_Datatype datatype, MPI_Op op, MPI_Comm comm) +{ + if (datatype == MPI_INT) + mpi_copy_int(sendbuf,recvbuf,count); + else if (datatype == MPI_FLOAT) + mpi_copy_float(sendbuf,recvbuf,count); + else if (datatype == MPI_DOUBLE) + mpi_copy_double(sendbuf,recvbuf,count); + else if (datatype == MPI_CHAR) + mpi_copy_char(sendbuf,recvbuf,count); + else if (datatype == MPI_BYTE) + mpi_copy_byte(sendbuf,recvbuf,count); +} + +void MPI_Scan(void *sendbuf, void *recvbuf, int count, + MPI_Datatype datatype, MPI_Op op, MPI_Comm comm) +{ + if (datatype == MPI_INT) + mpi_copy_int(sendbuf,recvbuf,count); + else if (datatype == MPI_FLOAT) + mpi_copy_float(sendbuf,recvbuf,count); + else if (datatype == MPI_DOUBLE) + mpi_copy_double(sendbuf,recvbuf,count); + else if (datatype == MPI_CHAR) + mpi_copy_char(sendbuf,recvbuf,count); + else if (datatype == MPI_BYTE) + mpi_copy_byte(sendbuf,recvbuf,count); +} + +/* copy values from data1 to data2 */ + +void MPI_Allgather(void *sendbuf, int sendcount, MPI_Datatype sendtype, + void *recvbuf, int recvcount, MPI_Datatype recvtype, + MPI_Comm comm) +{ + if (sendtype == MPI_INT) + mpi_copy_int(sendbuf,recvbuf,sendcount); + else if (sendtype == MPI_FLOAT) + mpi_copy_float(sendbuf,recvbuf,sendcount); + else if (sendtype == MPI_DOUBLE) + mpi_copy_double(sendbuf,recvbuf,sendcount); + else if (sendtype == MPI_CHAR) + mpi_copy_char(sendbuf,recvbuf,sendcount); + else if (sendtype == MPI_BYTE) + mpi_copy_byte(sendbuf,recvbuf,sendcount); +} + +/* copy values from data1 to data2 */ + +void MPI_Allgatherv(void *sendbuf, int sendcount, MPI_Datatype sendtype, + void *recvbuf, int *recvcounts, int *displs, + MPI_Datatype recvtype, MPI_Comm comm) +{ + if (sendtype == MPI_INT) + mpi_copy_int(sendbuf,recvbuf,sendcount); + else if (sendtype == MPI_FLOAT) + mpi_copy_float(sendbuf,recvbuf,sendcount); + else if (sendtype == MPI_DOUBLE) + mpi_copy_double(sendbuf,recvbuf,sendcount); + else if (sendtype == MPI_CHAR) + mpi_copy_char(sendbuf,recvbuf,sendcount); + else if (sendtype == MPI_BYTE) + mpi_copy_byte(sendbuf,recvbuf,sendcount); +} + +/* copy values from data1 to data2 */ + +void MPI_Reduce_scatter(void *sendbuf, void *recvbuf, int *recvcounts, + MPI_Datatype datatype, MPI_Op op, MPI_Comm comm) +{ + if (datatype == MPI_INT) + mpi_copy_int(sendbuf,recvbuf,*recvcounts); + else if (datatype == MPI_FLOAT) + mpi_copy_float(sendbuf,recvbuf,*recvcounts); + else if (datatype == MPI_DOUBLE) + mpi_copy_double(sendbuf,recvbuf,*recvcounts); + else if (datatype == MPI_CHAR) + mpi_copy_char(sendbuf,recvbuf,*recvcounts); + else if (datatype == MPI_BYTE) + mpi_copy_byte(sendbuf,recvbuf,*recvcounts); +} + +/* copy values from data1 to data2 */ + +void MPI_Gather(void *sendbuf, int sendcount, MPI_Datatype sendtype, + void *recvbuf, int recvcount, MPI_Datatype recvtype, + int root, MPI_Comm comm) +{ + if (sendtype == MPI_INT) + mpi_copy_int(sendbuf,recvbuf,sendcount); + else if (sendtype == MPI_FLOAT) + mpi_copy_float(sendbuf,recvbuf,sendcount); + else if (sendtype == MPI_DOUBLE) + mpi_copy_double(sendbuf,recvbuf,sendcount); + else if (sendtype == MPI_CHAR) + mpi_copy_char(sendbuf,recvbuf,sendcount); + else if (sendtype == MPI_BYTE) + mpi_copy_byte(sendbuf,recvbuf,sendcount); +} + +/* ------------------------------------------------------------------------ */ +/* Added routines for data copying */ + +void mpi_copy_int(void *data1, void *data2, int n) +{ + int i; + int *pdata1 = (int *) data1; + int *pdata2 = (int *) data2; + + for (i = 0; i < n; i++) pdata2[i] = pdata1[i]; +} + +void mpi_copy_float(void *data1, void *data2, int n) +{ + int i; + float *pdata1 = (float *) data1; + float *pdata2 = (float *) data2; + + for (i = 0; i < n; i++) pdata2[i] = pdata1[i]; +} + +void mpi_copy_double(void *data1, void *data2, int n) +{ + int i; + double *pdata1 = (double *) data1; + double *pdata2 = (double *) data2; + + for (i = 0; i < n; i++) pdata2[i] = pdata1[i]; +} + +void mpi_copy_char(void *data1, void *data2, int n) +{ + int i; + char *pdata1 = (char *) data1; + char *pdata2 = (char *) data2; + + for (i = 0; i < n; i++) pdata2[i] = pdata1[i]; +} + +void mpi_copy_byte(void *data1, void *data2, int n) +{ + int i; + char *pdata1 = (char *) data1; + char *pdata2 = (char *) data2; + + for (i = 0; i < n; i++) pdata2[i] = pdata1[i]; +} diff --git a/src/STUBS/mpi.h b/src/STUBS/mpi.h new file mode 100644 index 0000000000000000000000000000000000000000..2da314a7c07eca3213f3da6faa21d6d7291021d1 --- /dev/null +++ b/src/STUBS/mpi.h @@ -0,0 +1,88 @@ +/* ----------------------------------------------------------------------- + LAMMPS 2003 (July 31) - Molecular Dynamics Simulator + Sandia National Laboratories, www.cs.sandia.gov/~sjplimp/lammps.html + 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. +------------------------------------------------------------------------ */ + +/* Dummy defs for MPI stubs */ + +#define MPI_COMM_WORLD 0 + +#define MPI_INT 1 +#define MPI_FLOAT 2 +#define MPI_DOUBLE 3 +#define MPI_CHAR 4 +#define MPI_BYTE 5 + +#define MPI_SUM 1 +#define MPI_MAX 2 +#define MPI_MIN 3 + +#define MPI_Comm int +#define MPI_Request int +#define MPI_Status int +#define MPI_Datatype int +#define MPI_Op int + +/* Function prototypes for MPI stubs */ + +void MPI_Init(int *argc, char ***argv); +void MPI_Comm_rank(MPI_Comm comm, int *me); +void MPI_Comm_size(MPI_Comm comm, int *nprocs); +void MPI_Abort(MPI_Comm comm, int errorcode); +void MPI_Finalize(); +double MPI_Wtime(); + +void MPI_Send(void *buf, int count, MPI_Datatype datatype, + int dest, int tag, MPI_Comm comm); +void MPI_Rsend(void *buf, int count, MPI_Datatype datatype, + int dest, int tag, MPI_Comm comm); +void MPI_Recv(void *buf, int count, MPI_Datatype datatype, + int source, int tag, MPI_Comm comm, MPI_Status *status); +void MPI_Irecv(void *buf, int count, MPI_Datatype datatype, + int source, int tag, MPI_Comm comm, MPI_Request *request); +void MPI_Wait(MPI_Request *request, MPI_Status *status); +void MPI_Waitany(int count, MPI_Request *request, int *index, + MPI_Status *status); +void MPI_Sendrecv(void *sbuf, int scount, MPI_Datatype sdatatype, + int dest, int stag, void *rbuf, int rcount, + MPI_Datatype rdatatype, int source, int rtag, + MPI_Comm comm, MPI_Status *status); +void MPI_Get_count(MPI_Status *status, MPI_Datatype datatype, int *count); + +void MPI_Comm_split(MPI_Comm comm, int color, int key, MPI_Comm *comm_out); +void MPI_Comm_dup(MPI_Comm comm, MPI_Comm *comm_out); +void MPI_Comm_free(MPI_Comm *comm); + +void MPI_Cart_create(MPI_Comm comm_old, int ndims, int *dims, int *periods, + int reorder, MPI_Comm *comm_cart); +void MPI_Cart_get(MPI_Comm comm, int maxdims, int *dims, int *periods, + int *coords); +void MPI_Cart_shift(MPI_Comm comm, int direction, int displ, + int *source, int *dest); + +void MPI_Barrier(MPI_Comm comm); +void MPI_Bcast(void *buf, int count, MPI_Datatype datatype, + int root, MPI_Comm comm); +void MPI_Allreduce(void *sendbuf, void *recvbuf, int count, + MPI_Datatype datatype, MPI_Op op, MPI_Comm comm); +void MPI_Scan(void *sendbuf, void *recvbuf, int count, + MPI_Datatype datatype, MPI_Op op, MPI_Comm comm); +void MPI_Allgather(void *sendbuf, int sendcount, MPI_Datatype sendtype, + void *recvbuf, int recvcount, MPI_Datatype recvtype, + MPI_Comm comm); +void MPI_Allgatherv(void *sendbuf, int sendcount, MPI_Datatype sendtype, + void *recvbuf, int *recvcounts, int *displs, + MPI_Datatype recvtype, MPI_Comm comm); +void MPI_Reduce_scatter(void *sendbuf, void *recvbuf, int *recvcounts, + MPI_Datatype datatype, MPI_Op op, MPI_Comm comm); +void MPI_Gather(void *sendbuf, int sendcount, MPI_Datatype sendtype, + void *recvbuf, int recvcount, MPI_Datatype recvtype, + int root, MPI_Comm comm); diff --git a/src/XTC/Install.csh b/src/XTC/Install.csh new file mode 100644 index 0000000000000000000000000000000000000000..319ec056edd2a3cd4f23b6b198123084e9d0eb63 --- /dev/null +++ b/src/XTC/Install.csh @@ -0,0 +1,20 @@ +# Install/unInstall package classes in LAMMPS + +if ($1 == 1) then + + cp style_xtc.h .. + + cp dump_xtc.cpp .. + + cp dump_xtc.h .. + +else if ($1 == 0) then + + rm ../style_xtc.h + touch ../style_xtc.h + + rm ../dump_xtc.cpp + + rm ../dump_xtc.h + +endif diff --git a/src/XTC/dump_xtc.cpp b/src/XTC/dump_xtc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..193c337f092d58e2339367e4f467eb3a11d9b880 --- /dev/null +++ b/src/XTC/dump_xtc.cpp @@ -0,0 +1,1106 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Naveen Michaud-Agrawal (Johns Hopkins U) + open-source XDR routines from + Frans van Hoesel (http://md.chem.rug.nl/hoesel) + are also included in this file +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "rpc/rpc.h" +#include "rpc/xdr.h" +#include "dump_xtc.h" +#include "domain.h" +#include "atom.h" +#include "update.h" +#include "group.h" +#include "error.h" +#include "memory.h" + +#define EPS 1e-5 +#define XTC_MAGIC 1995 + +int xdropen(XDR *, const char *, const char *); +int xdrclose(XDR *) ; +void xdrfreebuf(); +int xdr3dfcoord(XDR *, float *, int *, float *); + +/* ---------------------------------------------------------------------- */ + +DumpXTC::DumpXTC(int narg, char **arg) : Dump(narg, arg) +{ + if (narg != 5 && narg != 6) error->all("Illegal dump xtc command"); + if (igroup != group->find("all")) error->all("Dump xtc must use group all"); + if (binary || compressed || multifile || multiproc) + error->all("Invalid dump xtc filename"); + + size_one = 4; + format_default = NULL; + flush_flag = 0; + + // parse extra argument + + precision = 1000.0; + if (narg == 6) { + precision = atof(arg[5]); + if ((fabs(precision-10.0) > EPS) && (fabs(precision-100.0) > EPS) && + (fabs(precision-1000.0) > EPS) && (fabs(precision-10000.0) > EPS) && + (fabs(precision-100000.0) > EPS) && (fabs(precision-1000000.0) > EPS)) + error->all("Illegal dump xtc command"); + } + + // allocate global array for atom coords + + natoms = static_cast<int> (atom->natoms); + if (natoms <= 0) error->all("Invalid natoms for dump xtc"); + if (atom->tag_consecutive() == 0) + error->all("Atom IDs must be consecutive for dump xtc"); + coords = (float *) memory->smalloc(3*natoms*sizeof(float),"dump:coords"); + + // sfactor = conversion of coords to XTC units + + sfactor = 0.1; + if (strcmp(update->unit_style,"lj") == 0) sfactor = 1.0; + + openfile(); + ntotal = 0; +} + +/* ---------------------------------------------------------------------- */ + +DumpXTC::~DumpXTC() +{ + memory->sfree(coords); + if (me == 0) { + xdrclose(&xd); + xdrfreebuf(); + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpXTC::init() +{ + // check that flush_flag is not set since dump::write() will use it + + if (flush_flag) error->all("Cannot set dump_modify flush for dump xtc"); +} + +/* ---------------------------------------------------------------------- + return # of bytes of allocated memory in buf and global coords array +------------------------------------------------------------------------- */ + +int DumpXTC::memory_usage() +{ + int bytes = maxbuf * sizeof(double); + bytes += 3*natoms * sizeof(float); + return bytes; +} + +/* ---------------------------------------------------------------------- */ + +void DumpXTC::openfile() +{ + // XTC maintains it's own XDR file ptr + // set fp to NULL so parent dump class will not use it + + fp = NULL; + if (me == 0) + if (xdropen(&xd,filename,"w") == 0) error->one("Cannot open dump file"); +} + +/* ---------------------------------------------------------------------- */ + +void DumpXTC::write_header(int n) +{ + // realloc coords if necessary + + if (n != natoms) { + memory->sfree(coords); + if (atom->tag_consecutive() == 0) + error->all("Atom IDs must be consecutive for dump xtc"); + natoms = n; + coords = (float *) memory->smalloc(3*natoms*sizeof(float),"dump:coords"); + } + + int tmp = XTC_MAGIC; + xdr_int(&xd,&tmp); + xdr_int(&xd,&n); + xdr_int(&xd,&update->ntimestep); + float time_value = update->ntimestep * update->dt; + xdr_float(&xd,&time_value); + + // cell basis vectors + + float zero = 0.0; + float xdim = domain->boxxhi - domain->boxxlo; + float ydim = domain->boxyhi - domain->boxylo; + float zdim = domain->boxzhi - domain->boxzlo; + + xdr_float(&xd,&xdim); xdr_float(&xd,&zero); xdr_float(&xd,&zero); + xdr_float(&xd,&zero); xdr_float(&xd,&ydim); xdr_float(&xd,&zero); + xdr_float(&xd,&zero); xdr_float(&xd,&zero); xdr_float(&xd,&zdim); +} + +/* ---------------------------------------------------------------------- */ + +int DumpXTC::count() +{ + return atom->nlocal; +} + +/* ---------------------------------------------------------------------- */ + +int DumpXTC::pack() +{ + int *tag = atom->tag; + double **x = atom->x; + int nlocal = atom->nlocal; + + // assume group all, so no need to perform mask check + + int m = 0; + for (int i = 0; i < nlocal; i++) { + buf[m++] = tag[i]; + buf[m++] = sfactor*x[i][0]; + buf[m++] = sfactor*x[i][1]; + buf[m++] = sfactor*x[i][2]; + } + + return m; +} + +/* ---------------------------------------------------------------------- */ + +void DumpXTC::write_data(int n, double *buf) +{ + float *xyz; + int j,tag; + + int m = 0; + for (int i = 0; i < n; i++) { + tag = static_cast<int> (buf[m]) - 1; + j = 3*tag; + coords[j++] = buf[m+1]; + coords[j++] = buf[m+2]; + coords[j] = buf[m+3]; + m += size_one; + } + + // if last chunk of atoms in this snapshot, write global arrays to file + + ntotal += n; + if (ntotal == natoms) { + write_frame(); + ntotal = 0; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpXTC::write_frame() +{ + xdr3dfcoord(&xd,coords,&natoms,&precision); +} + +// ---------------------------------------------------------------------- +// C functions that create GROMOS-compatible XDR files +// open-source +// (c) 1995 Frans van Hoesel, hoesel@chem.rug.nl +// ---------------------------------------------------------------------- + +/*____________________________________________________________________________ + | + | Below are the routines to be used by C programmers. Use the 'normal' + | xdr routines to write integers, floats, etc (see man xdr) + | + | int xdropen(XDR *xdrs, const char *filename, const char *type) + | This will open the file with the given filename and the + | given mode. You should pass it an allocated XDR struct + | in xdrs, to be used in all other calls to xdr routines. + | Mode is 'w' to create, or update an file, and for all + | other values of mode the file is opened for reading. + | You need to call xdrclose to flush the output and close + | the file. + | + | Note that you should not use xdrstdio_create, which + | comes with the standard xdr library. + | + | int xdrclose(XDR *xdrs) + | Flush the data to the file, and close the file; + | You should not use xdr_destroy (which comes standard + | with the xdr libraries). + | + | int xdr3dfcoord(XDR *xdrs, float *fp, int *size, float *precision) + | This is \fInot\fR a standard xdr routine. I named it this + | way, because it invites people to use the other xdr + | routines. + | + | (c) 1995 Frans van Hoesel, hoesel@chem.rug.nl +*/ + +#define MAXID 20 +static FILE *xdrfiles[MAXID]; +static XDR *xdridptr[MAXID]; +static char xdrmodes[MAXID]; +static unsigned int cnt; +static int *ip = NULL; +static int *buf = NULL; + +/*___________________________________________________________________________ + | + | what follows are the C routines for opening, closing xdr streams + | and the routine to read/write compressed coordinates together + | with some routines to assist in this task (those are marked + | static and cannot be called from user programs) +*/ +#define MAXABS INT_MAX-2 + +#ifndef SQR +#define SQR(x) ((x)*(x)) +#endif +static int magicints[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 10, 12, 16, 20, 25, 32, 40, 50, 64, + 80, 101, 128, 161, 203, 256, 322, 406, 512, 645, + 812, 1024, 1290, 1625, 2048, 2580, 3250, 4096, 5060, 6501, + 8192, 10321, 13003, 16384, 20642, 26007, 32768, 41285, 52015, 65536, + 82570, 104031, 131072, 165140, 208063, 262144, 330280, 416127, + 524287, 660561, + 832255, 1048576, 1321122, 1664510, 2097152, 2642245, 3329021, + 4194304, 5284491, 6658042, + 8388607, 10568983, 13316085, 16777216 }; + +#define FIRSTIDX 9 +/* note that magicints[FIRSTIDX-1] == 0 */ +#define LASTIDX (sizeof(magicints) / sizeof(*magicints)) + +/*__________________________________________________________________________ + | + | xdropen - open xdr file + | + | This versions differs from xdrstdio_create, because I need to know + | the state of the file (read or write) so I can use xdr3dfcoord + | in eigther read or write mode, and the file descriptor + | so I can close the file (something xdr_destroy doesn't do). + | +*/ + +int xdropen(XDR *xdrs, const char *filename, const char *type) +{ + static int init_done = 0; + enum xdr_op lmode; + int xdrid; + + if (init_done == 0) { + for (xdrid = 1; xdrid < MAXID; xdrid++) { + xdridptr[xdrid] = NULL; + } + init_done = 1; + } + xdrid = 1; + while (xdrid < MAXID && xdridptr[xdrid] != NULL) { + xdrid++; + } + if (xdrid == MAXID) { + return 0; + } + if (*type == 'w' || *type == 'W') { + type = "w+"; + lmode = XDR_ENCODE; + } else { + type = "r"; + lmode = XDR_DECODE; + } + xdrfiles[xdrid] = fopen(filename, type); + if (xdrfiles[xdrid] == NULL) { + xdrs = NULL; + return 0; + } + xdrmodes[xdrid] = *type; + + /* next test isn't usefull in the case of C language + * but is used for the Fortran interface + * (C users are expected to pass the address of an already allocated + * XDR staructure) + */ + if (xdrs == NULL) { + xdridptr[xdrid] = (XDR *) malloc(sizeof(XDR)); + xdrstdio_create(xdridptr[xdrid], xdrfiles[xdrid], lmode); + } else { + xdridptr[xdrid] = xdrs; + xdrstdio_create(xdrs, xdrfiles[xdrid], lmode); + } + return xdrid; +} + +/*_________________________________________________________________________ + | + | xdrclose - close a xdr file + | + | This will flush the xdr buffers, and destroy the xdr stream. + | It also closes the associated file descriptor (this is *not* + | done by xdr_destroy). + | +*/ + +int xdrclose(XDR *xdrs) +{ + int xdrid; + + if (xdrs == NULL) { + fprintf(stderr, "xdrclose: passed a NULL pointer\n"); + exit(1); + } + for (xdrid = 1; xdrid < MAXID; xdrid++) { + if (xdridptr[xdrid] == xdrs) { + + xdr_destroy(xdrs); + fclose(xdrfiles[xdrid]); + xdridptr[xdrid] = NULL; + return 1; + } + } + fprintf(stderr, "xdrclose: no such open xdr file\n"); + exit(1); +} + +/*_________________________________________________________________________ + | + | xdrfreebuf - free the buffers used by xdr3dfcoord + | +*/ +void xdrfreebuf() +{ + if (ip) free(ip); + if (buf) free(buf); +} + + +/*____________________________________________________________________________ + | + | sendbits - encode num into buf using the specified number of bits + | + | This routines appends the value of num to the bits already present in + | the array buf. You need to give it the number of bits to use and you + | better make sure that this number of bits is enough to hold the value + | Also num must be positive. + | +*/ + +static void sendbits(int buf[], int num_of_bits, int num) +{ + unsigned int cnt, lastbyte; + int lastbits; + unsigned char * cbuf; + + cbuf = ((unsigned char *)buf) + 3 * sizeof(*buf); + cnt = (unsigned int) buf[0]; + lastbits = buf[1]; + lastbyte =(unsigned int) buf[2]; + while (num_of_bits >= 8) { + lastbyte = (lastbyte << 8) | ((num >> (num_of_bits -8)) /* & 0xff*/); + cbuf[cnt++] = lastbyte >> lastbits; + num_of_bits -= 8; + } + if (num_of_bits > 0) { + lastbyte = (lastbyte << num_of_bits) | num; + lastbits += num_of_bits; + if (lastbits >= 8) { + lastbits -= 8; + cbuf[cnt++] = lastbyte >> lastbits; + } + } + buf[0] = cnt; + buf[1] = lastbits; + buf[2] = lastbyte; + if (lastbits>0) { + cbuf[cnt] = lastbyte << (8 - lastbits); + } +} + +/*_________________________________________________________________________ + | + | sizeofint - calculate bitsize of an integer + | + | return the number of bits needed to store an integer with given max size + | +*/ + +static int sizeofint(const int size) +{ + unsigned int num = 1; + int num_of_bits = 0; + + while (size >= num && num_of_bits < 32) { + num_of_bits++; + num <<= 1; + } + return num_of_bits; +} + +/*___________________________________________________________________________ + | + | sizeofints - calculate 'bitsize' of compressed ints + | + | given the number of small unsigned integers and the maximum value + | return the number of bits needed to read or write them with the + | routines receiveints and sendints. You need this parameter when + | calling these routines. Note that for many calls I can use + | the variable 'smallidx' which is exactly the number of bits, and + | So I don't need to call 'sizeofints for those calls. +*/ + +static int sizeofints( const int num_of_ints, unsigned int sizes[]) +{ + int i, num; + unsigned int num_of_bytes, num_of_bits, bytes[32], bytecnt, tmp; + num_of_bytes = 1; + bytes[0] = 1; + num_of_bits = 0; + for (i=0; i < num_of_ints; i++) { + tmp = 0; + for (bytecnt = 0; bytecnt < num_of_bytes; bytecnt++) { + tmp = bytes[bytecnt] * sizes[i] + tmp; + bytes[bytecnt] = tmp & 0xff; + tmp >>= 8; + } + while (tmp != 0) { + bytes[bytecnt++] = tmp & 0xff; + tmp >>= 8; + } + num_of_bytes = bytecnt; + } + num = 1; + num_of_bytes--; + while (bytes[num_of_bytes] >= num) { + num_of_bits++; + num *= 2; + } + return num_of_bits + num_of_bytes * 8; +} + +/*____________________________________________________________________________ + | + | sendints - send a small set of small integers in compressed + | + | this routine is used internally by xdr3dfcoord, to send a set of + | small integers to the buffer. + | Multiplication with fixed (specified maximum ) sizes is used to get + | to one big, multibyte integer. Allthough the routine could be + | modified to handle sizes bigger than 16777216, or more than just + | a few integers, this is not done, because the gain in compression + | isn't worth the effort. Note that overflowing the multiplication + | or the byte buffer (32 bytes) is unchecked and causes bad results. + | + */ + +static void sendints(int buf[], const int num_of_ints, const int num_of_bits, + unsigned int sizes[], unsigned int nums[]) +{ + int i; + unsigned int bytes[32], num_of_bytes, bytecnt, tmp; + + tmp = nums[0]; + num_of_bytes = 0; + do { + bytes[num_of_bytes++] = tmp & 0xff; + tmp >>= 8; + } while (tmp != 0); + + for (i = 1; i < num_of_ints; i++) { + if (nums[i] >= sizes[i]) { + fprintf(stderr,"major breakdown in sendints num %d doesn't " + "match size %d\n", nums[i], sizes[i]); + exit(1); + } + /* use one step multiply */ + tmp = nums[i]; + for (bytecnt = 0; bytecnt < num_of_bytes; bytecnt++) { + tmp = bytes[bytecnt] * sizes[i] + tmp; + bytes[bytecnt] = tmp & 0xff; + tmp >>= 8; + } + while (tmp != 0) { + bytes[bytecnt++] = tmp & 0xff; + tmp >>= 8; + } + num_of_bytes = bytecnt; + } + if (num_of_bits >= num_of_bytes * 8) { + for (i = 0; i < num_of_bytes; i++) { + sendbits(buf, 8, bytes[i]); + } + sendbits(buf, num_of_bits - num_of_bytes * 8, 0); + } else { + for (i = 0; i < num_of_bytes-1; i++) { + sendbits(buf, 8, bytes[i]); + } + sendbits(buf, num_of_bits- (num_of_bytes -1) * 8, bytes[i]); + } +} + +/*___________________________________________________________________________ + | + | receivebits - decode number from buf using specified number of bits + | + | extract the number of bits from the array buf and construct an integer + | from it. Return that value. + | +*/ + +static int receivebits(int buf[], int num_of_bits) +{ + int cnt, num; + unsigned int lastbits, lastbyte; + unsigned char * cbuf; + int mask = (1 << num_of_bits) -1; + + cbuf = ((unsigned char *)buf) + 3 * sizeof(*buf); + cnt = buf[0]; + lastbits = (unsigned int) buf[1]; + lastbyte = (unsigned int) buf[2]; + + num = 0; + while (num_of_bits >= 8) { + lastbyte = ( lastbyte << 8 ) | cbuf[cnt++]; + num |= (lastbyte >> lastbits) << (num_of_bits - 8); + num_of_bits -=8; + } + if (num_of_bits > 0) { + if (lastbits < num_of_bits) { + lastbits += 8; + lastbyte = (lastbyte << 8) | cbuf[cnt++]; + } + lastbits -= num_of_bits; + num |= (lastbyte >> lastbits) & ((1 << num_of_bits) -1); + } + num &= mask; + buf[0] = cnt; + buf[1] = lastbits; + buf[2] = lastbyte; + return num; +} + +/*____________________________________________________________________________ + | + | receiveints - decode 'small' integers from the buf array + | + | this routine is the inverse from sendints() and decodes the small integers + | written to buf by calculating the remainder and doing divisions with + | the given sizes[]. You need to specify the total number of bits to be + | used from buf in num_of_bits. + | +*/ + +static void receiveints(int buf[], const int num_of_ints, int num_of_bits, + unsigned int sizes[], int nums[]) +{ + int bytes[32]; + int i, j, num_of_bytes, p, num; + + bytes[1] = bytes[2] = bytes[3] = 0; + num_of_bytes = 0; + while (num_of_bits > 8) { + bytes[num_of_bytes++] = receivebits(buf, 8); + num_of_bits -= 8; + } + if (num_of_bits > 0) { + bytes[num_of_bytes++] = receivebits(buf, num_of_bits); + } + for (i = num_of_ints-1; i > 0; i--) { + num = 0; + for (j = num_of_bytes-1; j >=0; j--) { + num = (num << 8) | bytes[j]; + p = num / sizes[i]; + bytes[j] = p; + num = num - p * sizes[i]; + } + nums[i] = num; + } + nums[0] = bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24); +} + +/*____________________________________________________________________________ + | + | xdr3dfcoord - read or write compressed 3d coordinates to xdr file. + | + | this routine reads or writes (depending on how you opened the file with + | xdropen() ) a large number of 3d coordinates (stored in *fp). + | The number of coordinates triplets to write is given by *size. On + | read this number may be zero, in which case it reads as many as were written + | or it may specify the number if triplets to read (which should match the + | number written). + | Compression is achieved by first converting all floating numbers to integer + | using multiplication by *precision and rounding to the nearest integer. + | Then the minimum and maximum value are calculated to determine the range. + | The limited range of integers so found, is used to compress the coordinates. + | In addition the differences between succesive coordinates is calculated. + | If the difference happens to be 'small' then only the difference is saved, + | compressing the data even more. The notion of 'small' is changed dynamically + | and is enlarged or reduced whenever needed or possible. + | Extra compression is achieved in the case of GROMOS and coordinates of + | water molecules. GROMOS first writes out the Oxygen position, followed by + | the two hydrogens. In order to make the differences smaller (and thereby + | compression the data better) the order is changed into first one hydrogen + | then the oxygen, followed by the other hydrogen. This is rather special, but + | it shouldn't harm in the general case. + | + */ + +int xdr3dfcoord(XDR *xdrs, float *fp, int *size, float *precision) +{ + static int oldsize; + + int minint[3], maxint[3], mindiff, *lip, diff; + int lint1, lint2, lint3, oldlint1, oldlint2, oldlint3, smallidx; + int minidx, maxidx; + unsigned sizeint[3], sizesmall[3], bitsizeint[3], size3, *luip; + int flag, k; + int small, smaller, larger, i, is_small, is_smaller, run, prevrun; + float *lfp, lf; + int tmp, *thiscoord, prevcoord[3]; + unsigned int tmpcoord[30]; + + int bufsize, xdrid, lsize; + unsigned int bitsize; + float inv_precision; + int errval = 1; + + /* find out if xdrs is opened for reading or for writing */ + xdrid = 0; + while (xdridptr[xdrid] != xdrs) { + xdrid++; + if (xdrid >= MAXID) { + fprintf(stderr, "xdr error. no open xdr stream\n"); + exit (1); + } + } + if (xdrmodes[xdrid] == 'w') { + + /* xdrs is open for writing */ + + if (xdr_int(xdrs, size) == 0) + return 0; + size3 = *size * 3; + /* when the number of coordinates is small, don't try to compress; just + * write them as floats using xdr_vector + */ + if (*size <= 9 ) { + return (xdr_vector(xdrs, (char *) fp, size3, sizeof(*fp), + (xdrproc_t)xdr_float)); + } + + xdr_float(xdrs, precision); + if (ip == NULL) { + ip = (int *) malloc(size3 * sizeof(*ip)); + if (ip == NULL) { + fprintf(stderr,"malloc failed\n"); + exit(1); + } + bufsize = (int) (size3 * 1.2); + buf = (int *) malloc(bufsize * sizeof(*buf)); + if (buf == NULL) { + fprintf(stderr,"malloc failed\n"); + exit(1); + } + oldsize = *size; + } else if (*size > oldsize) { + ip = (int *) realloc(ip, size3 * sizeof(*ip)); + if (ip == NULL) { + fprintf(stderr,"malloc failed\n"); + exit(1); + } + bufsize = (int) (size3 * 1.2); + buf = (int *) realloc(buf, bufsize * sizeof(*buf)); + if (buf == NULL) { + fprintf(stderr,"malloc failed\n"); + exit(1); + } + oldsize = *size; + } + /* buf[0-2] are special and do not contain actual data */ + buf[0] = buf[1] = buf[2] = 0; + minint[0] = minint[1] = minint[2] = INT_MAX; + maxint[0] = maxint[1] = maxint[2] = INT_MIN; + prevrun = -1; + lfp = fp; + lip = ip; + mindiff = INT_MAX; + oldlint1 = oldlint2 = oldlint3 = 0; + while(lfp < fp + size3 ) { + /* find nearest integer */ + if (*lfp >= 0.0) + lf = *lfp * *precision + 0.5; + else + lf = *lfp * *precision - 0.5; + if (fabs(lf) > MAXABS) { + /* scaling would cause overflow */ + errval = 0; + } + lint1 = (int) lf; + if (lint1 < minint[0]) minint[0] = lint1; + if (lint1 > maxint[0]) maxint[0] = lint1; + *lip++ = lint1; + lfp++; + if (*lfp >= 0.0) + lf = *lfp * *precision + 0.5; + else + lf = *lfp * *precision - 0.5; + if (fabs(lf) > MAXABS) { + /* scaling would cause overflow */ + errval = 0; + } + lint2 = (int) lf; + if (lint2 < minint[1]) minint[1] = lint2; + if (lint2 > maxint[1]) maxint[1] = lint2; + *lip++ = lint2; + lfp++; + if (*lfp >= 0.0) + lf = *lfp * *precision + 0.5; + else + lf = *lfp * *precision - 0.5; + if (fabs(lf) > MAXABS) { + /* scaling would cause overflow */ + errval = 0; + } + lint3 = (int) lf; + if (lint3 < minint[2]) minint[2] = lint3; + if (lint3 > maxint[2]) maxint[2] = lint3; + *lip++ = lint3; + lfp++; + diff = abs(oldlint1-lint1)+abs(oldlint2-lint2)+abs(oldlint3-lint3); + if (diff < mindiff && lfp > fp + 3) + mindiff = diff; + oldlint1 = lint1; + oldlint2 = lint2; + oldlint3 = lint3; + } + xdr_int(xdrs, &(minint[0])); + xdr_int(xdrs, &(minint[1])); + xdr_int(xdrs, &(minint[2])); + + xdr_int(xdrs, &(maxint[0])); + xdr_int(xdrs, &(maxint[1])); + xdr_int(xdrs, &(maxint[2])); + + if ((float)maxint[0] - (float)minint[0] >= MAXABS || + (float)maxint[1] - (float)minint[1] >= MAXABS || + (float)maxint[2] - (float)minint[2] >= MAXABS) { + /* turning value in unsigned by subtracting minint + * would cause overflow + */ + errval = 0; + } + sizeint[0] = maxint[0] - minint[0]+1; + sizeint[1] = maxint[1] - minint[1]+1; + sizeint[2] = maxint[2] - minint[2]+1; + + /* check if one of the sizes is to big to be multiplied */ + if ((sizeint[0] | sizeint[1] | sizeint[2] ) > 0xffffff) { + bitsizeint[0] = sizeofint(sizeint[0]); + bitsizeint[1] = sizeofint(sizeint[1]); + bitsizeint[2] = sizeofint(sizeint[2]); + bitsize = 0; /* flag the use of large sizes */ + } else { + bitsize = sizeofints(3, sizeint); + } + lip = ip; + luip = (unsigned int *) ip; + smallidx = FIRSTIDX; + while (smallidx < LASTIDX && magicints[smallidx] < mindiff) { + smallidx++; + } + xdr_int(xdrs, &smallidx); + maxidx = MIN(LASTIDX, smallidx + 8) ; + minidx = maxidx - 8; /* often this equal smallidx */ + smaller = magicints[MAX(FIRSTIDX, smallidx-1)] / 2; + small = magicints[smallidx] / 2; + sizesmall[0] = sizesmall[1] = sizesmall[2] = magicints[smallidx]; + larger = magicints[maxidx] / 2; + i = 0; + while (i < *size) { + is_small = 0; + thiscoord = (int *)(luip) + i * 3; + if (smallidx < maxidx && i >= 1 && + abs(thiscoord[0] - prevcoord[0]) < larger && + abs(thiscoord[1] - prevcoord[1]) < larger && + abs(thiscoord[2] - prevcoord[2]) < larger) { + is_smaller = 1; + } else if (smallidx > minidx) { + is_smaller = -1; + } else { + is_smaller = 0; + } + if (i + 1 < *size) { + if (abs(thiscoord[0] - thiscoord[3]) < small && + abs(thiscoord[1] - thiscoord[4]) < small && + abs(thiscoord[2] - thiscoord[5]) < small) { + /* interchange first with second atom for better + * compression of water molecules + */ + tmp = thiscoord[0]; thiscoord[0] = thiscoord[3]; + thiscoord[3] = tmp; + tmp = thiscoord[1]; thiscoord[1] = thiscoord[4]; + thiscoord[4] = tmp; + tmp = thiscoord[2]; thiscoord[2] = thiscoord[5]; + thiscoord[5] = tmp; + is_small = 1; + } + + } + tmpcoord[0] = thiscoord[0] - minint[0]; + tmpcoord[1] = thiscoord[1] - minint[1]; + tmpcoord[2] = thiscoord[2] - minint[2]; + if (bitsize == 0) { + sendbits(buf, bitsizeint[0], tmpcoord[0]); + sendbits(buf, bitsizeint[1], tmpcoord[1]); + sendbits(buf, bitsizeint[2], tmpcoord[2]); + } else { + sendints(buf, 3, bitsize, sizeint, tmpcoord); + } + prevcoord[0] = thiscoord[0]; + prevcoord[1] = thiscoord[1]; + prevcoord[2] = thiscoord[2]; + thiscoord = thiscoord + 3; + i++; + + run = 0; + if (is_small == 0 && is_smaller == -1) + is_smaller = 0; + while (is_small && run < 8*3) { + if (is_smaller == -1 && (SQR(thiscoord[0] - prevcoord[0]) + + SQR(thiscoord[1] - prevcoord[1]) + + SQR(thiscoord[2] - prevcoord[2]) >= + smaller * smaller)) { + is_smaller = 0; + } + + tmpcoord[run++] = thiscoord[0] - prevcoord[0] + small; + tmpcoord[run++] = thiscoord[1] - prevcoord[1] + small; + tmpcoord[run++] = thiscoord[2] - prevcoord[2] + small; + + prevcoord[0] = thiscoord[0]; + prevcoord[1] = thiscoord[1]; + prevcoord[2] = thiscoord[2]; + + i++; + thiscoord = thiscoord + 3; + is_small = 0; + if (i < *size && + abs(thiscoord[0] - prevcoord[0]) < small && + abs(thiscoord[1] - prevcoord[1]) < small && + abs(thiscoord[2] - prevcoord[2]) < small) { + is_small = 1; + } + } + if (run != prevrun || is_smaller != 0) { + prevrun = run; + sendbits(buf, 1, 1); /* flag the change in run-length */ + sendbits(buf, 5, run+is_smaller+1); + } else { + sendbits(buf, 1, 0); /* flag the fact that runlength did not change */ + } + for (k=0; k < run; k+=3) { + sendints(buf, 3, smallidx, sizesmall, &tmpcoord[k]); + } + if (is_smaller != 0) { + smallidx += is_smaller; + if (is_smaller < 0) { + small = smaller; + smaller = magicints[smallidx-1] / 2; + } else { + smaller = small; + small = magicints[smallidx] / 2; + } + sizesmall[0] = sizesmall[1] = sizesmall[2] = magicints[smallidx]; + } + } + if (buf[1] != 0) buf[0]++;; + xdr_int(xdrs, &(buf[0])); /* buf[0] holds the length in bytes */ + return errval * (xdr_opaque(xdrs, (caddr_t)&(buf[3]), (u_int)buf[0])); + } else { + + /* xdrs is open for reading */ + + if (xdr_int(xdrs, &lsize) == 0) + return 0; + if (*size != 0 && lsize != *size) { + fprintf(stderr, "wrong number of coordinates in xdr3dfcoor; " + "%d arg vs %d in file", *size, lsize); + } + *size = lsize; + size3 = *size * 3; + if (*size <= 9) { + return (xdr_vector(xdrs, (char *) fp, size3, sizeof(*fp), + (xdrproc_t)xdr_float)); + } + xdr_float(xdrs, precision); + if (ip == NULL) { + ip = (int *) malloc(size3 * sizeof(*ip)); + if (ip == NULL) { + fprintf(stderr,"malloc failed\n"); + exit(1); + } + bufsize = (int) (size3 * 1.2); + buf = (int *) malloc(bufsize * sizeof(*buf)); + if (buf == NULL) { + fprintf(stderr,"malloc failed\n"); + exit(1); + } + oldsize = *size; + } else if (*size > oldsize) { + ip = (int *)realloc(ip, size3 * sizeof(*ip)); + if (ip == NULL) { + fprintf(stderr,"malloc failed\n"); + exit(1); + } + bufsize = (int) (size3 * 1.2); + buf = (int *)realloc(buf, bufsize * sizeof(*buf)); + if (buf == NULL) { + fprintf(stderr,"malloc failed\n"); + exit(1); + } + oldsize = *size; + } + buf[0] = buf[1] = buf[2] = 0; + + xdr_int(xdrs, &(minint[0])); + xdr_int(xdrs, &(minint[1])); + xdr_int(xdrs, &(minint[2])); + + xdr_int(xdrs, &(maxint[0])); + xdr_int(xdrs, &(maxint[1])); + xdr_int(xdrs, &(maxint[2])); + + sizeint[0] = maxint[0] - minint[0]+1; + sizeint[1] = maxint[1] - minint[1]+1; + sizeint[2] = maxint[2] - minint[2]+1; + + /* check if one of the sizes is to big to be multiplied */ + if ((sizeint[0] | sizeint[1] | sizeint[2] ) > 0xffffff) { + bitsizeint[0] = sizeofint(sizeint[0]); + bitsizeint[1] = sizeofint(sizeint[1]); + bitsizeint[2] = sizeofint(sizeint[2]); + bitsize = 0; /* flag the use of large sizes */ + } else { + bitsize = sizeofints(3, sizeint); + } + + xdr_int(xdrs, &smallidx); + maxidx = MIN(LASTIDX, smallidx + 8) ; + minidx = maxidx - 8; /* often this equal smallidx */ + smaller = magicints[MAX(FIRSTIDX, smallidx-1)] / 2; + small = magicints[smallidx] / 2; + sizesmall[0] = sizesmall[1] = sizesmall[2] = magicints[smallidx] ; + larger = magicints[maxidx]; + + /* buf[0] holds the length in bytes */ + + if (xdr_int(xdrs, &(buf[0])) == 0) + return 0; + if (xdr_opaque(xdrs, (caddr_t)&(buf[3]), (u_int)buf[0]) == 0) + return 0; + buf[0] = buf[1] = buf[2] = 0; + + lfp = fp; + inv_precision = 1.0 / * precision; + run = 0; + i = 0; + lip = ip; + while ( i < lsize ) { + thiscoord = (int *)(lip) + i * 3; + + if (bitsize == 0) { + thiscoord[0] = receivebits(buf, bitsizeint[0]); + thiscoord[1] = receivebits(buf, bitsizeint[1]); + thiscoord[2] = receivebits(buf, bitsizeint[2]); + } else { + receiveints(buf, 3, bitsize, sizeint, thiscoord); + } + + i++; + thiscoord[0] += minint[0]; + thiscoord[1] += minint[1]; + thiscoord[2] += minint[2]; + + prevcoord[0] = thiscoord[0]; + prevcoord[1] = thiscoord[1]; + prevcoord[2] = thiscoord[2]; + + + flag = receivebits(buf, 1); + is_smaller = 0; + if (flag == 1) { + run = receivebits(buf, 5); + is_smaller = run % 3; + run -= is_smaller; + is_smaller--; + } + if (run > 0) { + thiscoord += 3; + for (k = 0; k < run; k+=3) { + receiveints(buf, 3, smallidx, sizesmall, thiscoord); + i++; + thiscoord[0] += prevcoord[0] - small; + thiscoord[1] += prevcoord[1] - small; + thiscoord[2] += prevcoord[2] - small; + if (k == 0) { + /* interchange first with second atom for better + * compression of water molecules + */ + tmp = thiscoord[0]; thiscoord[0] = prevcoord[0]; + prevcoord[0] = tmp; + tmp = thiscoord[1]; thiscoord[1] = prevcoord[1]; + prevcoord[1] = tmp; + tmp = thiscoord[2]; thiscoord[2] = prevcoord[2]; + prevcoord[2] = tmp; + *lfp++ = prevcoord[0] * inv_precision; + *lfp++ = prevcoord[1] * inv_precision; + *lfp++ = prevcoord[2] * inv_precision; + } else { + prevcoord[0] = thiscoord[0]; + prevcoord[1] = thiscoord[1]; + prevcoord[2] = thiscoord[2]; + } + *lfp++ = thiscoord[0] * inv_precision; + *lfp++ = thiscoord[1] * inv_precision; + *lfp++ = thiscoord[2] * inv_precision; + } + } else { + *lfp++ = thiscoord[0] * inv_precision; + *lfp++ = thiscoord[1] * inv_precision; + *lfp++ = thiscoord[2] * inv_precision; + } + smallidx += is_smaller; + if (is_smaller < 0) { + small = smaller; + if (smallidx > FIRSTIDX) { + smaller = magicints[smallidx - 1] /2; + } else { + smaller = 0; + } + } else if (is_smaller > 0) { + smaller = small; + small = magicints[smallidx] / 2; + } + sizesmall[0] = sizesmall[1] = sizesmall[2] = magicints[smallidx] ; + } + } + return 1; +} diff --git a/src/XTC/dump_xtc.h b/src/XTC/dump_xtc.h new file mode 100644 index 0000000000000000000000000000000000000000..e9968d29f15e1585662871a38a8b6093f7d156a1 --- /dev/null +++ b/src/XTC/dump_xtc.h @@ -0,0 +1,44 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DUMP_XTC_H +#define DUMP_XTC_H + +#include "dump.h" +#include "rpc/rpc.h" +#include "rpc/xdr.h" + +class DumpXTC : public Dump { + public: + DumpXTC(int, char**); + ~DumpXTC(); + void init(); + int memory_usage(); + + private: + int natoms,ntotal; + float precision; + float *coords; + double sfactor; + XDR xd; + + void openfile(); + void write_header(int); + int count(); + int pack(); + void write_data(int, double *); + + void write_frame(); +}; + +#endif diff --git a/src/XTC/style_xtc.h b/src/XTC/style_xtc.h new file mode 100644 index 0000000000000000000000000000000000000000..13c21825d9e75be516fe3d81081bedafb29a4d8b --- /dev/null +++ b/src/XTC/style_xtc.h @@ -0,0 +1,20 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifdef DumpInclude +#include "dump_xtc.h" +#endif + +#ifdef DumpClass +DumpStyle(xtc,DumpXTC) +#endif diff --git a/src/angle.cpp b/src/angle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3b689700f9a124496d374bc2a288e0407f7d7b72 --- /dev/null +++ b/src/angle.cpp @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "angle.h" +#include "atom.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +Angle::Angle() +{ + allocated = 0; + PI = 4.0*atan(1.0); +} + +/* ---------------------------------------------------------------------- + check if all coeffs are set +------------------------------------------------------------------------- */ + +void Angle::init() +{ + if (!allocated) error->all("Angle coeffs are not set"); + for (int i = 1; i <= atom->nangletypes; i++) + if (setflag[i] == 0) error->all("All angle coeffs are not set"); +} + diff --git a/src/angle.h b/src/angle.h new file mode 100644 index 0000000000000000000000000000000000000000..3e6e9d659c1d8981fb8cbb8accc2523d54d9cabc --- /dev/null +++ b/src/angle.h @@ -0,0 +1,41 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ANGLE_H +#define ANGLE_H + +#include "stdio.h" +#include "lammps.h" + +class Angle : public LAMMPS { + public: + int allocated; + int *setflag; + double energy; + double virial[6]; + double PI; + + Angle(); + virtual ~Angle() {} + virtual void init(); + virtual void compute(int, int) = 0; + virtual void settings(int, char **) {} + virtual void coeff(int, int, char **) = 0; + virtual double equilibrium_angle(int) = 0; + virtual void write_restart(FILE *) = 0; + virtual void read_restart(FILE *) = 0; + virtual double single(int, int, int, int, double) {return 0.0;} + virtual int memory_usage() {return 0;} +}; + +#endif diff --git a/src/angle_charmm.cpp b/src/angle_charmm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e23cd9684a08614915e031e41e6664ae44b10a81 --- /dev/null +++ b/src/angle_charmm.cpp @@ -0,0 +1,310 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Paul Crozier (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "angle_charmm.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +AngleCharmm::~AngleCharmm() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(theta0); + memory->sfree(k_ub); + memory->sfree(r_ub); + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleCharmm::compute(int eflag, int vflag) +{ + int i1,i2,i3,n,type,factor; + double delx1,dely1,delz1,delx2,dely2,delz2,rfactor,dtheta,tk; + double rsq1,rsq2,r1,r2,c,s,a,a11,a12,a22,vx1,vx2,vy1,vy2,vz1,vz2; + double delxUB,delyUB,delzUB,rsqUB,rUB,dr,rk,forceUB; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **anglelist = neighbor->anglelist; + int nanglelist = neighbor->nanglelist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nanglelist; n++) { + + i1 = anglelist[n][0]; + i2 = anglelist[n][1]; + i3 = anglelist[n][2]; + type = anglelist[n][3]; + + if (newton_bond) factor = 3; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + } + rfactor = factor/3.0; + + // 1st bond + + delx1 = x[i1][0] - x[i2][0]; + dely1 = x[i1][1] - x[i2][1]; + delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + + rsq1 = delx1*delx1 + dely1*dely1 + delz1*delz1; + r1 = sqrt(rsq1); + + // 2nd bond + + delx2 = x[i3][0] - x[i2][0]; + dely2 = x[i3][1] - x[i2][1]; + delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + + rsq2 = delx2*delx2 + dely2*dely2 + delz2*delz2; + r2 = sqrt(rsq2); + + // Urey-Bradley bond + + delxUB = x[i3][0] - x[i1][0]; + delyUB = x[i3][1] - x[i1][1]; + delzUB = x[i3][2] - x[i1][2]; + domain->minimum_image(&delxUB,&delyUB,&delzUB); + + rsqUB = delxUB*delxUB + delyUB*delyUB + delzUB*delzUB; + rUB = sqrt(rsqUB); + + // angle (cos and sin) + + c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + s = sqrt(1.0 - c*c); + if (s < SMALL) s = SMALL; + s = 1.0/s; + + // harmonic force & energy + + dtheta = acos(c) - theta0[type]; + tk = k[type] * dtheta; + + if (eflag) energy += rfactor * tk*dtheta; + + a = 2.0 * tk * s; + + a11 = a*c / rsq1; + a12 = -a / (r1*r2); + a22 = a*c / rsq2; + + vx1 = a11*delx1 + a12*delx2; + vx2 = a22*delx2 + a12*delx1; + vy1 = a11*dely1 + a12*dely2; + vy2 = a22*dely2 + a12*dely1; + vz1 = a11*delz1 + a12*delz2; + vz2 = a22*delz2 + a12*delz1; + + // Urey-Bradley force & energy + + dr = rUB - r_ub[type]; + rk = k_ub[type] * dr; + + if (rUB > 0.0) forceUB = -2.0*rk/rUB; + else forceUB = 0.0; + + if (eflag) energy += rfactor * rk*dr; + + // apply force to each of 3 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= vx1 + delxUB*forceUB; + f[i1][1] -= vy1 + delyUB*forceUB; + f[i1][2] -= vz1 + delzUB*forceUB; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += vx1 + vx2; + f[i2][1] += vy1 + vy2; + f[i2][2] += vz1 + vz2; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] -= vx2 - delxUB*forceUB; + f[i3][1] -= vy2 - delyUB*forceUB; + f[i3][2] -= vz2 - delzUB*forceUB; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (delx1*vx1 + delx2*vx2 - delxUB*delxUB*forceUB); + virial[1] -= rfactor * (dely1*vy1 + dely2*vy2 - delyUB*delyUB*forceUB); + virial[2] -= rfactor * (delz1*vz1 + delz2*vz2 - delzUB*delzUB*forceUB); + virial[3] -= rfactor * (delx1*vy1 + delx2*vy2 - delxUB*delyUB*forceUB); + virial[4] -= rfactor * (delx1*vz1 + delx2*vz2 - delxUB*delzUB*forceUB); + virial[5] -= rfactor * (dely1*vz1 + dely2*vz2 - delyUB*delzUB*forceUB); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleCharmm::allocate() +{ + allocated = 1; + int n = atom->nangletypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"angle:k"); + theta0 = (double *) memory->smalloc((n+1)*sizeof(double),"angle:theta0"); + k_ub = (double *) memory->smalloc((n+1)*sizeof(double),"angle:k_ub"); + r_ub = (double *) memory->smalloc((n+1)*sizeof(double),"angle:r_ub"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"angle:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void AngleCharmm::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this angle style"); + if (narg != 5) error->all("Incorrect args for angle coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nangletypes,ilo,ihi); + + double k_one = atof(arg[1]); + double theta0_one = atof(arg[2]); + double k_ub_one = atof(arg[3]); + double r_ub_one = atof(arg[4]); + + // convert theta0 from degrees to radians + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + theta0[i] = theta0_one/180.0 * PI; + k_ub[i] = k_ub_one; + r_ub[i] = r_ub_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for angle coefficients"); +} + +/* ---------------------------------------------------------------------- */ + +double AngleCharmm::equilibrium_angle(int i) +{ + return theta0[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void AngleCharmm::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nangletypes,fp); + fwrite(&theta0[1],sizeof(double),atom->nangletypes,fp); + fwrite(&k_ub[1],sizeof(double),atom->nangletypes,fp); + fwrite(&r_ub[1],sizeof(double),atom->nangletypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void AngleCharmm::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->nangletypes,fp); + fread(&theta0[1],sizeof(double),atom->nangletypes,fp); + fread(&k_ub[1],sizeof(double),atom->nangletypes,fp); + fread(&r_ub[1],sizeof(double),atom->nangletypes,fp); + } + MPI_Bcast(&k[1],atom->nangletypes,MPI_DOUBLE,0,world); + MPI_Bcast(&theta0[1],atom->nangletypes,MPI_DOUBLE,0,world); + MPI_Bcast(&k_ub[1],atom->nangletypes,MPI_DOUBLE,0,world); + MPI_Bcast(&r_ub[1],atom->nangletypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nangletypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +double AngleCharmm::single(int type, int i1, int i2, int i3, double rfactor) +{ + double **x = atom->x; + + double delx1 = x[i1][0] - x[i2][0]; + double dely1 = x[i1][1] - x[i2][1]; + double delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + double r1 = sqrt(delx1*delx1 + dely1*dely1 + delz1*delz1); + + double delx2 = x[i3][0] - x[i2][0]; + double dely2 = x[i3][1] - x[i2][1]; + double delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + double r2 = sqrt(delx2*delx2 + dely2*dely2 + delz2*delz2); + + double delxUB = x[i3][0] - x[i1][0]; + double delyUB = x[i3][1] - x[i1][1]; + double delzUB = x[i3][2] - x[i1][2]; + domain->minimum_image(&delxUB,&delyUB,&delzUB); + double rUB = sqrt(delxUB*delxUB + delyUB*delyUB + delzUB*delzUB); + + double c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + double dtheta = acos(c) - theta0[type]; + double tk = k[type] * dtheta; + double dr = rUB - r_ub[type]; + double rk = k_ub[type] * dr; + + return (rfactor * (tk*dtheta + rk*dr)); +} diff --git a/src/angle_charmm.h b/src/angle_charmm.h new file mode 100644 index 0000000000000000000000000000000000000000..278e8e2493da7e84245a38216d85b5983dba25a4 --- /dev/null +++ b/src/angle_charmm.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ANGLE_CHARMM_H +#define ANGLE_CHARMM_H + +#include "stdio.h" +#include "angle.h" + +class AngleCharmm : public Angle { + public: + AngleCharmm() {} + ~AngleCharmm(); + void compute(int, int); + void coeff(int, int, char **); + double equilibrium_angle(int); + void write_restart(FILE *); + void read_restart(FILE *); + double single(int, int, int, int, double); + + private: + double *k,*theta0,*k_ub,*r_ub; + + void allocate(); +}; + +#endif diff --git a/src/angle_cosine.cpp b/src/angle_cosine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6f33f0e7bac712619007137b8d4c120d323b827a --- /dev/null +++ b/src/angle_cosine.cpp @@ -0,0 +1,241 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "angle_cosine.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +AngleCosine::~AngleCosine() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleCosine::compute(int eflag, int vflag) +{ + int i1,i2,i3,n,type,factor; + double delx1,dely1,delz1,delx2,dely2,delz2,rfactor; + double rsq1,rsq2,r1,r2,c,a,a11,a12,a22,vx1,vx2,vy1,vy2,vz1,vz2; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **anglelist = neighbor->anglelist; + int nanglelist = neighbor->nanglelist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nanglelist; n++) { + + i1 = anglelist[n][0]; + i2 = anglelist[n][1]; + i3 = anglelist[n][2]; + type = anglelist[n][3]; + + if (newton_bond) factor = 3; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + } + rfactor = factor/3.0; + + // 1st bond + + delx1 = x[i1][0] - x[i2][0]; + dely1 = x[i1][1] - x[i2][1]; + delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + + rsq1 = delx1*delx1 + dely1*dely1 + delz1*delz1; + r1 = sqrt(rsq1); + + // 2nd bond + + delx2 = x[i3][0] - x[i2][0]; + dely2 = x[i3][1] - x[i2][1]; + delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + + rsq2 = delx2*delx2 + dely2*dely2 + delz2*delz2; + r2 = sqrt(rsq2); + + // c = cosine of angle + + c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + // force & energy + + if (eflag) energy += rfactor * k[type]*(1.0+c); + + a = -k[type]; + + a11 = a*c / rsq1; + a12 = -a / (r1*r2); + a22 = a*c / rsq2; + + vx1 = a11*delx1 + a12*delx2; + vx2 = a22*delx2 + a12*delx1; + vy1 = a11*dely1 + a12*dely2; + vy2 = a22*dely2 + a12*dely1; + vz1 = a11*delz1 + a12*delz2; + vz2 = a22*delz2 + a12*delz1; + + // apply force to each of 3 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= vx1; + f[i1][1] -= vy1; + f[i1][2] -= vz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += vx1 + vx2; + f[i2][1] += vy1 + vy2; + f[i2][2] += vz1 + vz2; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] -= vx2; + f[i3][1] -= vy2; + f[i3][2] -= vz2; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (delx1*vx1 + delx2*vx2); + virial[1] -= rfactor * (dely1*vy1 + dely2*vy2); + virial[2] -= rfactor * (delz1*vz1 + delz2*vz2); + virial[3] -= rfactor * (delx1*vy1 + delx2*vy2); + virial[4] -= rfactor * (delx1*vz1 + delx2*vz2); + virial[5] -= rfactor * (dely1*vz1 + dely2*vz2); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleCosine::allocate() +{ + allocated = 1; + int n = atom->nangletypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"angle:k"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"angle:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void AngleCosine::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this angle style"); + if (narg != 2) error->all("Incorrect args for angle coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nangletypes,ilo,ihi); + + double k_one = atof(arg[1]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for angle coefficients"); +} + +/* ---------------------------------------------------------------------- */ + +double AngleCosine::equilibrium_angle(int i) +{ + return PI; +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void AngleCosine::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nangletypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void AngleCosine::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) fread(&k[1],sizeof(double),atom->nangletypes,fp); + MPI_Bcast(&k[1],atom->nangletypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nangletypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +double AngleCosine::single(int type, int i1, int i2, int i3, double rfactor) +{ + double **x = atom->x; + + double delx1 = x[i1][0] - x[i2][0]; + double dely1 = x[i1][1] - x[i2][1]; + double delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + double r1 = sqrt(delx1*delx1 + dely1*dely1 + delz1*delz1); + + double delx2 = x[i3][0] - x[i2][0]; + double dely2 = x[i3][1] - x[i2][1]; + double delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + double r2 = sqrt(delx2*delx2 + dely2*dely2 + delz2*delz2); + + double c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + return (rfactor * k[type]*(1.0+c)); +} diff --git a/src/angle_cosine.h b/src/angle_cosine.h new file mode 100644 index 0000000000000000000000000000000000000000..6adf7e684662668dbc8d9c5f498b3879d28c29b6 --- /dev/null +++ b/src/angle_cosine.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ANGLE_COSINE_H +#define ANGLE_COSINE_H + +#include "stdio.h" +#include "angle.h" + +class AngleCosine : public Angle { + public: + AngleCosine() {} + ~AngleCosine(); + void compute(int, int); + void coeff(int, int, char **); + double equilibrium_angle(int); + void write_restart(FILE *); + void read_restart(FILE *); + double single(int, int, int, int, double); + + private: + double *k; + + void allocate(); +}; + +#endif diff --git a/src/angle_cosine_squared.cpp b/src/angle_cosine_squared.cpp new file mode 100644 index 0000000000000000000000000000000000000000..581204cee44a181034ef6adfd01aea8091540e88 --- /dev/null +++ b/src/angle_cosine_squared.cpp @@ -0,0 +1,264 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Naveen Michaud-Agrawal (Johns Hopkins U) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "angle_cosine_squared.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +AngleCosineSquared::~AngleCosineSquared() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(theta0); + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleCosineSquared::compute(int eflag, int vflag) +{ + int i1,i2,i3,n,type,factor; + double delx1,dely1,delz1,delx2,dely2,delz2,rfactor,dcostheta,tk; + double rsq1,rsq2,r1,r2,c,a,a11,a12,a22,vx1,vx2,vy1,vy2,vz1,vz2; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **anglelist = neighbor->anglelist; + int nanglelist = neighbor->nanglelist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nanglelist; n++) { + + i1 = anglelist[n][0]; + i2 = anglelist[n][1]; + i3 = anglelist[n][2]; + type = anglelist[n][3]; + + if (newton_bond) factor = 3; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + } + rfactor = factor/3.0; + + // 1st bond + + delx1 = x[i1][0] - x[i2][0]; + dely1 = x[i1][1] - x[i2][1]; + delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + + rsq1 = delx1*delx1 + dely1*dely1 + delz1*delz1; + r1 = sqrt(rsq1); + + // 2nd bond + + delx2 = x[i3][0] - x[i2][0]; + dely2 = x[i3][1] - x[i2][1]; + delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + + rsq2 = delx2*delx2 + dely2*dely2 + delz2*delz2; + r2 = sqrt(rsq2); + + // angle (cos and sin) + + c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + // force & energy + + dcostheta = c - cos(theta0[type]); + tk = k[type] * dcostheta; + + if (eflag) energy += rfactor * tk*dcostheta; + + a = -2.0 * tk; + + a11 = a*c / rsq1; + a12 = -a / (r1*r2); + a22 = a*c / rsq2; + + vx1 = a11*delx1 + a12*delx2; + vx2 = a22*delx2 + a12*delx1; + vy1 = a11*dely1 + a12*dely2; + vy2 = a22*dely2 + a12*dely1; + vz1 = a11*delz1 + a12*delz2; + vz2 = a22*delz2 + a12*delz1; + + // apply force to each of 3 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= vx1; + f[i1][1] -= vy1; + f[i1][2] -= vz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += vx1 + vx2; + f[i2][1] += vy1 + vy2; + f[i2][2] += vz1 + vz2; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] -= vx2; + f[i3][1] -= vy2; + f[i3][2] -= vz2; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (delx1*vx1 + delx2*vx2); + virial[1] -= rfactor * (dely1*vy1 + dely2*vy2); + virial[2] -= rfactor * (delz1*vz1 + delz2*vz2); + virial[3] -= rfactor * (delx1*vy1 + delx2*vy2); + virial[4] -= rfactor * (delx1*vz1 + delx2*vz2); + virial[5] -= rfactor * (dely1*vz1 + dely2*vz2); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleCosineSquared::allocate() +{ + allocated = 1; + int n = atom->nangletypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"angle:k"); + theta0 = (double *) memory->smalloc((n+1)*sizeof(double),"angle:theta0"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"angle:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more types +------------------------------------------------------------------------- */ + +void AngleCosineSquared::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this angle style"); + if (narg != 3) error->all("Incorrect args for angle coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nangletypes,ilo,ihi); + + double k_one = atof(arg[1]); + double theta0_one = atof(arg[2]); + + // convert theta0 from degrees to radians + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + theta0[i] = theta0_one/180.0 * PI; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for angle coefficients"); +} + +/* ---------------------------------------------------------------------- */ + +double AngleCosineSquared::equilibrium_angle(int i) +{ + return theta0[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void AngleCosineSquared::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nangletypes,fp); + fwrite(&theta0[1],sizeof(double),atom->nangletypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void AngleCosineSquared::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->nangletypes,fp); + fread(&theta0[1],sizeof(double),atom->nangletypes,fp); + } + MPI_Bcast(&k[1],atom->nangletypes,MPI_DOUBLE,0,world); + MPI_Bcast(&theta0[1],atom->nangletypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nangletypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +double AngleCosineSquared::single(int type, int i1, int i2, int i3, + double rfactor) +{ + double **x = atom->x; + + double delx1 = x[i1][0] - x[i2][0]; + double dely1 = x[i1][1] - x[i2][1]; + double delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + double r1 = sqrt(delx1*delx1 + dely1*dely1 + delz1*delz1); + + double delx2 = x[i3][0] - x[i2][0]; + double dely2 = x[i3][1] - x[i2][1]; + double delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + double r2 = sqrt(delx2*delx2 + dely2*dely2 + delz2*delz2); + + double c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + double dcostheta = c - cos(theta0[type]); + double tk = k[type] * dcostheta; + return (rfactor * tk*dcostheta); +} diff --git a/src/angle_cosine_squared.h b/src/angle_cosine_squared.h new file mode 100644 index 0000000000000000000000000000000000000000..cc7ce9b14f42d4a93178091f6a593be2f5c3b19c --- /dev/null +++ b/src/angle_cosine_squared.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ANGLE_COSINE_SQUARED_H +#define ANGLE_COSINE_SQUARED_H + +#include "stdio.h" +#include "angle.h" + +class AngleCosineSquared : public Angle { + public: + AngleCosineSquared() {} + ~AngleCosineSquared(); + void compute(int, int); + void coeff(int, int, char **); + double equilibrium_angle(int); + void write_restart(FILE *); + void read_restart(FILE *); + double single(int, int, int, int, double); + + private: + double *k,*theta0; + + void allocate(); +}; + +#endif diff --git a/src/angle_harmonic.cpp b/src/angle_harmonic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b9b2a9ccf2b27c4994594751fc92f4ca18a68ef --- /dev/null +++ b/src/angle_harmonic.cpp @@ -0,0 +1,263 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "angle_harmonic.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +AngleHarmonic::~AngleHarmonic() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(theta0); + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleHarmonic::compute(int eflag, int vflag) +{ + int i1,i2,i3,n,type,factor; + double delx1,dely1,delz1,delx2,dely2,delz2,rfactor,dtheta,tk; + double rsq1,rsq2,r1,r2,c,s,a,a11,a12,a22,vx1,vx2,vy1,vy2,vz1,vz2; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **anglelist = neighbor->anglelist; + int nanglelist = neighbor->nanglelist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nanglelist; n++) { + + i1 = anglelist[n][0]; + i2 = anglelist[n][1]; + i3 = anglelist[n][2]; + type = anglelist[n][3]; + + if (newton_bond) factor = 3; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + } + rfactor = factor/3.0; + + // 1st bond + + delx1 = x[i1][0] - x[i2][0]; + dely1 = x[i1][1] - x[i2][1]; + delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + + rsq1 = delx1*delx1 + dely1*dely1 + delz1*delz1; + r1 = sqrt(rsq1); + + // 2nd bond + + delx2 = x[i3][0] - x[i2][0]; + dely2 = x[i3][1] - x[i2][1]; + delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + + rsq2 = delx2*delx2 + dely2*dely2 + delz2*delz2; + r2 = sqrt(rsq2); + + // angle (cos and sin) + + c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + s = sqrt(1.0 - c*c); + if (s < SMALL) s = SMALL; + s = 1.0/s; + + // force & energy + + dtheta = acos(c) - theta0[type]; + tk = k[type] * dtheta; + + if (eflag) energy += rfactor * tk*dtheta; + + a = 2.0 * tk * s; + + a11 = a*c / rsq1; + a12 = -a / (r1*r2); + a22 = a*c / rsq2; + + vx1 = a11*delx1 + a12*delx2; + vx2 = a22*delx2 + a12*delx1; + vy1 = a11*dely1 + a12*dely2; + vy2 = a22*dely2 + a12*dely1; + vz1 = a11*delz1 + a12*delz2; + vz2 = a22*delz2 + a12*delz1; + + // apply force to each of 3 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= vx1; + f[i1][1] -= vy1; + f[i1][2] -= vz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += vx1 + vx2; + f[i2][1] += vy1 + vy2; + f[i2][2] += vz1 + vz2; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] -= vx2; + f[i3][1] -= vy2; + f[i3][2] -= vz2; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (delx1*vx1 + delx2*vx2); + virial[1] -= rfactor * (dely1*vy1 + dely2*vy2); + virial[2] -= rfactor * (delz1*vz1 + delz2*vz2); + virial[3] -= rfactor * (delx1*vy1 + delx2*vy2); + virial[4] -= rfactor * (delx1*vz1 + delx2*vz2); + virial[5] -= rfactor * (dely1*vz1 + dely2*vz2); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleHarmonic::allocate() +{ + allocated = 1; + int n = atom->nangletypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"angle:k"); + theta0 = (double *) memory->smalloc((n+1)*sizeof(double),"angle:theta0"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"angle:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more types +------------------------------------------------------------------------- */ + +void AngleHarmonic::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this angle style"); + if (narg != 3) error->all("Incorrect args for angle coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nangletypes,ilo,ihi); + + double k_one = atof(arg[1]); + double theta0_one = atof(arg[2]); + + // convert theta0 from degrees to radians + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + theta0[i] = theta0_one/180.0 * PI; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for angle coefficients"); +} + +/* ---------------------------------------------------------------------- */ + +double AngleHarmonic::equilibrium_angle(int i) +{ + return theta0[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void AngleHarmonic::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nangletypes,fp); + fwrite(&theta0[1],sizeof(double),atom->nangletypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void AngleHarmonic::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->nangletypes,fp); + fread(&theta0[1],sizeof(double),atom->nangletypes,fp); + } + MPI_Bcast(&k[1],atom->nangletypes,MPI_DOUBLE,0,world); + MPI_Bcast(&theta0[1],atom->nangletypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nangletypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +double AngleHarmonic::single(int type, int i1, int i2, int i3, double rfactor) +{ + double **x = atom->x; + + double delx1 = x[i1][0] - x[i2][0]; + double dely1 = x[i1][1] - x[i2][1]; + double delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + double r1 = sqrt(delx1*delx1 + dely1*dely1 + delz1*delz1); + + double delx2 = x[i3][0] - x[i2][0]; + double dely2 = x[i3][1] - x[i2][1]; + double delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + double r2 = sqrt(delx2*delx2 + dely2*dely2 + delz2*delz2); + + double c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + double dtheta = acos(c) - theta0[type]; + double tk = k[type] * dtheta; + return (rfactor * tk*dtheta); +} diff --git a/src/angle_harmonic.h b/src/angle_harmonic.h new file mode 100644 index 0000000000000000000000000000000000000000..8b097f9e1af2214a08ea72ffc1e1567b4560476d --- /dev/null +++ b/src/angle_harmonic.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ANGLE_HARMONIC_H +#define ANGLE_HARMONIC_H + +#include "stdio.h" +#include "angle.h" + +class AngleHarmonic : public Angle { + public: + AngleHarmonic() {} + ~AngleHarmonic(); + void compute(int, int); + void coeff(int, int, char **); + double equilibrium_angle(int); + void write_restart(FILE *); + void read_restart(FILE *); + double single(int, int, int, int, double); + + private: + double *k,*theta0; + + void allocate(); +}; + +#endif diff --git a/src/angle_hybrid.cpp b/src/angle_hybrid.cpp new file mode 100644 index 0000000000000000000000000000000000000000..12d7a0093e9cfe28200b0adfbbebb5201537ce5b --- /dev/null +++ b/src/angle_hybrid.cpp @@ -0,0 +1,262 @@ +/* ----------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "string.h" +#include "angle_hybrid.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define EXTRA 1000 + +/* ---------------------------------------------------------------------- + set all global defaults +------------------------------------------------------------------------- */ + +AngleHybrid::AngleHybrid() +{ + nstyles = 0; +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +AngleHybrid::~AngleHybrid() +{ + if (nstyles) { + for (int i = 0; i < nstyles; i++) delete styles[i]; + delete [] styles; + for (int i = 0; i < nstyles; i++) delete [] keywords[i]; + delete [] keywords; + } + + if (allocated) { + memory->sfree(setflag); + memory->sfree(map); + delete [] nanglelist; + delete [] maxangle; + for (int i = 0; i < nstyles; i++) + memory->destroy_2d_int_array(anglelist[i]); + delete [] anglelist; + } +} + +/* ---------------------------------------------------------------------- */ + +void AngleHybrid::compute(int eflag, int vflag) +{ + int i,m,n; + + // save ptrs to original anglelist + + int nanglelist_orig = neighbor->nanglelist; + int **anglelist_orig = neighbor->anglelist; + + // if this is re-neighbor step, create sub-style anglelists + // nanglelist[] = length of each sub-style list + // realloc sub-style anglelist if necessary + // load sub-style anglelist with 4 values from original anglelist + + if (neighbor->ago == 0) { + for (m = 0; m < nstyles; m++) nanglelist[m] = 0; + for (i = 0; i < nanglelist_orig; i++) + nanglelist[map[anglelist_orig[i][3]]]++; + for (m = 0; m < nstyles; m++) { + if (nanglelist[m] > maxangle[m]) { + memory->destroy_2d_int_array(anglelist[m]); + maxangle[m] = nanglelist[m] + EXTRA; + anglelist[m] = (int **) + memory->create_2d_int_array(maxangle[m],4,"angle_hybrid:anglelist"); + } + nanglelist[m] = 0; + } + for (i = 0; i < nanglelist_orig; i++) { + m = map[anglelist_orig[i][3]]; + n = nanglelist[m]; + anglelist[m][n][0] = anglelist_orig[i][0]; + anglelist[m][n][1] = anglelist_orig[i][1]; + anglelist[m][n][2] = anglelist_orig[i][2]; + anglelist[m][n][3] = anglelist_orig[i][3]; + nanglelist[m]++; + } + } + + // call each sub-style's compute function + // must set neighbor->anglelist to sub-style anglelist before call + // accumulate sub-style energy,virial in hybrid's energy,virial + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + for (m = 0; m < nstyles; m++) { + if (styles[m] == NULL) continue; + neighbor->nanglelist = nanglelist[m]; + neighbor->anglelist = anglelist[m]; + styles[m]->compute(eflag,vflag); + if (eflag) energy += styles[m]->energy; + if (vflag) for (n = 0; n < 6; n++) virial[n] += styles[m]->virial[n]; + } + + // restore ptrs to original anglelist + + neighbor->nanglelist = nanglelist_orig; + neighbor->anglelist = anglelist_orig; +} + +/* ---------------------------------------------------------------------- */ + +void AngleHybrid::allocate() +{ + allocated = 1; + int n = atom->nangletypes; + + map = (int *) memory->smalloc((n+1)*sizeof(int),"angle:map"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"angle:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; + + nanglelist = new int[nstyles]; + maxangle = new int[nstyles]; + anglelist = new int**[nstyles]; + for (int m = 0; m < nstyles; m++) maxangle[m] = 0; + for (int m = 0; m < nstyles; m++) anglelist[m] = NULL; +} + +/* ---------------------------------------------------------------------- + create one angle style for each arg in list +------------------------------------------------------------------------- */ + +void AngleHybrid::settings(int narg, char **arg) +{ + nstyles = narg; + styles = new Angle*[nstyles]; + keywords = new char*[nstyles]; + + for (int m = 0; m < nstyles; m++) { + for (int i = 0; i < m; i++) + if (strcmp(arg[m],arg[i]) == 0) + error->all("Angle style hybrid cannot use same angle style twice"); + if (strcmp(arg[m],"hybrid") == 0) + error->all("Angle style hybrid cannot have hybrid as an argument"); + styles[m] = force->new_angle(arg[m]); + keywords[m] = new char[strlen(arg[m])+1]; + strcpy(keywords[m],arg[m]); + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +---------------------------------------------------------------------- */ + +void AngleHybrid::coeff(int which, int narg, char **arg) +{ + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nangletypes,ilo,ihi); + + // 2nd arg = angle style name (harmonic, etc) + + int m; + for (m = 0; m < nstyles; m++) + if (strcmp(arg[1],keywords[m]) == 0) break; + if (m == nstyles) error->all("Angle coeff for hybrid has invalid style"); + + // set low-level coefficients for each angletype + // replace 2nd arg with i, call coeff() with no 1st arg + // if sub-style is NULL for "none", still set setflag + + for (int i = ilo; i <= ihi; i++) { + sprintf(arg[1],"%d",i); + map[i] = m; + if (styles[m]) styles[m]->coeff(which,narg-1,&arg[1]); + setflag[i] = 1; + } +} + +/* ---------------------------------------------------------------------- + return an equilbrium angle length +------------------------------------------------------------------------- */ + +double AngleHybrid::equilibrium_angle(int i) +{ + return styles[map[i]]->equilibrium_angle(i); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void AngleHybrid::write_restart(FILE *fp) +{ + fwrite(&nstyles,sizeof(int),1,fp); + + int n; + for (int m = 0; m < nstyles; m++) { + n = strlen(keywords[m]) + 1; + fwrite(&n,sizeof(int),1,fp); + fwrite(keywords[m],sizeof(char),n,fp); + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void AngleHybrid::read_restart(FILE *fp) +{ + allocate(); + + int me = comm->me; + if (me == 0) fread(&nstyles,sizeof(int),1,fp); + MPI_Bcast(&nstyles,1,MPI_INT,0,world); + styles = new Angle*[nstyles]; + keywords = new char*[nstyles]; + + int n; + for (int m = 0; m < nstyles; m++) { + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + keywords[m] = new char[n]; + if (me == 0) fread(keywords[m],sizeof(char),n,fp); + MPI_Bcast(keywords[m],n,MPI_CHAR,0,world); + styles[m] = force->new_angle(keywords[m]); + } +} + +/* ---------------------------------------------------------------------- */ + +double AngleHybrid::single(int type, int i1, int i2, int i3, double rfactor) +{ + if (styles[map[type]]) + return styles[map[type]]->single(type,i1,i2,i3,rfactor); + else return 0.0; +} + +/* ---------------------------------------------------------------------- + memory usage +------------------------------------------------------------------------- */ + +int AngleHybrid::memory_usage() +{ + int bytes = 0; + for (int m = 0; m < nstyles; m++) bytes += maxangle[m]*4 * sizeof(int); + for (int m = 0; m < nstyles; m++) + if (styles[m]) bytes += styles[m]->memory_usage(); + return bytes; +} diff --git a/src/angle_hybrid.h b/src/angle_hybrid.h new file mode 100644 index 0000000000000000000000000000000000000000..250238cfc3f47b4e7fb133cb4f01c1730d1d88a0 --- /dev/null +++ b/src/angle_hybrid.h @@ -0,0 +1,46 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ANGLE_HYBRID_H +#define ANGLE_HYBRID_H + +#include "stdio.h" +#include "angle.h" + +class AngleHybrid : public Angle { + public: + AngleHybrid(); + ~AngleHybrid(); + void compute(int, int); + void settings(int, char **); + void coeff(int, int, char **); + double equilibrium_angle(int); + void write_restart(FILE *); + void read_restart(FILE *); + double single(int, int, int, int, double); + int memory_usage(); + + private: + int nstyles; // # of different angle styles + Angle **styles; // class list for each Angle style + char **keywords; // keyword for each Angle style + int *map; // which style each angle type points to + + int *nanglelist; // # of angles in sub-style anglelists + int *maxangle; // max # of angles sub-style lists can store + int ***anglelist; // anglelist for each sub-style + + void allocate(); +}; + +#endif diff --git a/src/atom.cpp b/src/atom.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a2560d669fa273ba3799b7b815ea8732eb477985 --- /dev/null +++ b/src/atom.cpp @@ -0,0 +1,1857 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "modify.h" +#include "fix.h" +#include "output.h" +#include "thermo.h" +#include "update.h" +#include "domain.h" +#include "group.h" +#include "memory.h" +#include "error.h" + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +#define DELTA 10000 +#define DELTA_CALLBACK 1 + +/* ---------------------------------------------------------------------- */ + +Atom::Atom(int narg, char **arg) +{ + int n = strlen(arg[0]) + 1; + style = new char[n]; + strcpy(style,arg[0]); + + // arg[0] sets one style flag to 1 + + style_angle = style_atomic = style_bond = style_charge = style_dipole = + style_dpd = style_full = style_granular = style_molecular = + style_peri = style_hybrid = 0; + + set_style(arg[0]); + + // if hybrid style, set additional style for each additional arg + + if (style_hybrid) { + if (narg < 2) error->all("Illegal atom style hybrid command"); + for (int i = 1; i < narg; i++) { + if (strcmp(arg[i],"hybrid") == 0) + error->all("Atom style hybrid cannot have hybrid as an argument"); + set_style(arg[i]); + } + } + + // set low-level flags from style flags + + mass_require = 0; + if (style_angle || style_atomic || style_bond || style_charge || + style_dipole || style_dpd || style_full || + style_molecular || style_hybrid) mass_require = 1; + + mass_allow = 0; + if (style_granular || style_peri) mass_allow = 1; + + charge_allow = 0; + if (style_charge || style_full || style_dipole) charge_allow = 1; + + dipole_require = 0; + if (style_dipole) dipole_require = 1; + + molecular = 0; + if (style_angle || style_bond || style_full || style_molecular) + molecular = 1; + + bonds_allow = 0; + if (style_angle || style_bond || style_full || style_molecular) + bonds_allow = 1; + + angles_allow = 0; + if (style_angle || style_full || style_molecular) angles_allow = 1; + + dihedrals_allow = 0; + if (style_full || style_molecular) dihedrals_allow = 1; + + impropers_allow = 0; + if (style_full || style_molecular) impropers_allow = 1; + + // set size variables from styles + // if size_comm and size_reverse are changed, + // must also change direct_flag in comm::init() + + size_comm = 3; + if (style_dpd) size_comm += 3; // v + if (style_dipole) size_comm += 3; // mu + if (style_granular) size_comm += 6; // v,phiv + + size_reverse = 3; + if (style_dipole) size_reverse += 3; // torque + if (style_granular) size_reverse += 3; // phia + + size_border = 6; + if (charge_allow) size_border += 1; // q + if (style_dpd) size_border += 3; // v + if (style_dipole) size_border += 3; // mu + if (style_granular) size_border += 8; // v,phiv,radius,rmass + if (molecular) size_border += 1; // molecule + + size_atom_valid = 5; + if (molecular) size_atom_valid += 1; // molecule + if (charge_allow) size_atom_valid += 1; // q + if (style_granular) size_atom_valid += 2; // radius,density + if (style_peri) size_atom_valid += 2; // vfrac,rmass + if (style_dipole) size_atom_valid += 3; // mu + + // initialize atom arrays to empty + + tag = type = mask = image = NULL; + x = v = f = NULL; + + q = NULL; + mu = omega = torque = NULL; + phix = phiv = phia = NULL; + radius = density = rmass = vfrac = NULL; + + molecule = NULL; + + maxspecial = 1; + nspecial = NULL; + special = NULL; + + num_bond = NULL; + bond_type = bond_atom = NULL; + + num_angle = NULL; + angle_type = angle_atom1 = angle_atom2 = angle_atom3 = NULL; + + num_dihedral = NULL; + dihedral_type = dihedral_atom1 = dihedral_atom2 = NULL; + dihedral_atom3 = dihedral_atom4 = NULL; + + num_improper = NULL; + improper_type = improper_atom1 = improper_atom2 = NULL; + improper_atom3 = improper_atom4 = NULL; + + // set data and restart file header values to defaults + + natoms = 0; + nbonds = nangles = ndihedrals = nimpropers = 0; + ntypes = 0; + nbondtypes = nangletypes = ndihedraltypes = nimpropertypes = 0; + bond_per_atom = angle_per_atom = dihedral_per_atom = improper_per_atom = 0; + + // no atoms initially + + nlocal = 0; + nghost = 0; + nmax = 0; + + // ntype length arrays + + mass = NULL; + mass_setflag = NULL; + dipole = NULL; + dipole_setflag = NULL; + + // callback lists & extra restart info + + nextra_grow = nextra_restart = 0; + extra_grow = extra_restart = NULL; + nextra_grow_max = nextra_restart_max = 0; + nextra_store = 0; + extra = NULL; + + // set default mapping values and hash table primes + + tag_enable = 1; + if (molecular) map_style = 1; + else map_style = 0; + map_tag_max = 0; + map_nhash = 0; + + nprimes = 38; + primes = new int[nprimes]; + int plist[] = {5041,10007,20011,30011,40009,50021,60013,70001,80021, + 90001,100003,110017,120011,130003,140009,150001,160001, + 170003,180001,190027,200003,210011,220009,230003,240007, + 250007,260003,270001,280001,290011,300007,310019,320009, + 330017,340007,350003,362881,3628801}; + for (int i = 0; i < nprimes; i++) primes[i] = plist[i]; + + // constant + + PI = 4.0*atan(1.0); +} + +/* ---------------------------------------------------------------------- */ + +Atom::~Atom() +{ + // delete atom arrays + + memory->sfree(tag); + memory->sfree(type); + memory->sfree(mask); + memory->sfree(image); + memory->destroy_2d_double_array(x); + memory->destroy_2d_double_array(v); + memory->destroy_2d_double_array(f); + + memory->sfree(q); + memory->destroy_2d_double_array(mu); + memory->destroy_2d_double_array(omega); + memory->destroy_2d_double_array(torque); + memory->destroy_2d_double_array(phix); + memory->destroy_2d_double_array(phiv); + memory->destroy_2d_double_array(phia); + memory->sfree(radius); + memory->sfree(density); + memory->sfree(rmass); + memory->sfree(vfrac); + + memory->sfree(molecule); + + memory->destroy_2d_int_array(nspecial); + memory->destroy_2d_int_array(special); + + memory->sfree(num_bond); + memory->destroy_2d_int_array(bond_type); + memory->destroy_2d_int_array(bond_atom); + + memory->sfree(num_angle); + memory->destroy_2d_int_array(angle_type); + memory->destroy_2d_int_array(angle_atom1); + memory->destroy_2d_int_array(angle_atom2); + memory->destroy_2d_int_array(angle_atom3); + + memory->sfree(num_dihedral); + memory->destroy_2d_int_array(dihedral_type); + memory->destroy_2d_int_array(dihedral_atom1); + memory->destroy_2d_int_array(dihedral_atom2); + memory->destroy_2d_int_array(dihedral_atom3); + memory->destroy_2d_int_array(dihedral_atom4); + + memory->sfree(num_improper); + memory->destroy_2d_int_array(improper_type); + memory->destroy_2d_int_array(improper_atom1); + memory->destroy_2d_int_array(improper_atom2); + memory->destroy_2d_int_array(improper_atom3); + memory->destroy_2d_int_array(improper_atom4); + + // delete auxiliary arrays + + delete [] style; + + delete [] mass; + delete [] mass_setflag; + delete [] dipole; + delete [] dipole_setflag; + + memory->sfree(extra_grow); + memory->sfree(extra_restart); + memory->destroy_2d_double_array(extra); + + map_delete(); + delete [] primes; +} + +/* ---------------------------------------------------------------------- */ + +void Atom::set_style(char *name) +{ + if (strcmp(name,"angle") == 0) style_angle = 1; + else if (strcmp(name,"atomic") == 0) style_atomic = 1; + else if (strcmp(name,"bond") == 0) style_bond = 1; + else if (strcmp(name,"charge") == 0) style_charge = 1; + else if (strcmp(name,"dipole") == 0) style_dipole = 1; + else if (strcmp(name,"dpd") == 0) style_dpd = 1; + else if (strcmp(name,"full") == 0) style_full = 1; + else if (strcmp(name,"granular") == 0) style_granular = 1; + else if (strcmp(name,"molecular") == 0) style_molecular = 1; + else if (strcmp(name,"peri") == 0) style_peri = 1; + else if (strcmp(name,"hybrid") == 0) style_hybrid = 1; + else error->all("Illegal atom_style command"); +} + +/* ---------------------------------------------------------------------- + return 1 if named atom style is set as pure style or sub-class of hybrid + else return 0 +------------------------------------------------------------------------- */ + +int Atom::check_style(char *name) +{ + if (!strcmp(name,"angle") && style_angle) return 1; + else if (!strcmp(name,"atomic") && style_atomic) return 1; + else if (!strcmp(name,"bond") && style_bond) return 1; + else if (!strcmp(name,"charge") && style_charge) return 1; + else if (!strcmp(name,"dipole") && style_dipole) return 1; + else if (!strcmp(name,"dpd") && style_dpd) return 1; + else if (!strcmp(name,"full") && style_full) return 1; + else if (!strcmp(name,"granular") && style_granular) return 1; + else if (!strcmp(name,"molecular") && style_molecular) return 1; + else if (!strcmp(name,"peri") && style_peri) return 1; + else if (!strcmp(name,"hybrid") && style_hybrid) return 1; + return 0; +} + +/* ---------------------------------------------------------------------- + convert style settings to list of keywords + put "hybrid" first if it is set + return list and number of words in list +------------------------------------------------------------------------- */ + +int Atom::style2arg(char **&name) +{ + int n = style_angle + style_atomic + style_bond + style_charge + + style_dipole + style_dpd + style_full + + style_granular + style_molecular + style_peri + style_hybrid; + + name = new char*[n]; + + n = 0; + if (style_hybrid) name[n++] = style2word("hybrid"); + if (style_angle) name[n++] = style2word("angle"); + if (style_atomic) name[n++] = style2word("atomic"); + if (style_bond) name[n++] = style2word("bond"); + if (style_charge) name[n++] = style2word("charge"); + if (style_dipole) name[n++] = style2word("dipole"); + if (style_dpd) name[n++] = style2word("dpd"); + if (style_full) name[n++] = style2word("full"); + if (style_granular) name[n++] = style2word("granular"); + if (style_molecular) name[n++] = style2word("molecular"); + if (style_peri) name[n++] = style2word("peri"); + + return n; +} + +/* ---------------------------------------------------------------------- + copy modify settings from old Atom class to current Atom class +------------------------------------------------------------------------- */ + +void Atom::settings(Atom *old) +{ + map_style = old->map_style; +} + +/* ---------------------------------------------------------------------- + copy name into new word string +------------------------------------------------------------------------- */ + +char *Atom::style2word(char *name) +{ + int n = strlen(name) + 1; + char *word = new char[n]; + strcpy(word,name); + return word; +} + +/* ---------------------------------------------------------------------- */ + +void Atom::init() +{ + // delete extra array since it doesn't persist past first run + + if (nextra_store) { + memory->destroy_2d_double_array(extra); + extra = NULL; + nextra_store = 0; + } + + // check arrays that are atom type in length + + check_mass(); + check_dipole(); + + // for dipole style: + // for dipole type atoms, check that dipole moment is set, normalize it + // for non-dipole type atoms, check that dipole moment is 0.0 + + if (style_dipole) { + double msq,scale; + int flag = 0; + for (int i = 0; i < nlocal; i++) { + msq = mu[i][0]*mu[i][0] + mu[i][1]*mu[i][1] + mu[i][2]*mu[i][2]; + if (dipole[type[i]] > 0.0 && msq == 0.0) flag++; + else if (dipole[type[i]] > 0.0) { + scale = dipole[type[i]]/sqrt(msq); + mu[i][0] *= scale; + mu[i][1] *= scale; + mu[i][2] *= scale; + } + else if (dipole[type[i]] == 0.0 && msq > 0.0) flag++; + } + int flag_all; + MPI_Allreduce(&flag,&flag_all,1,MPI_INT,MPI_SUM,world); + if (flag) error->all("Inconsistent dipole settings for some atoms"); + } + + // for granular style: + // insure LJ units and thermo style granular or custom is used + + if (style_granular) { + if (strcmp(update->unit_style,"lj") != 0) + error->all("Must use lj units with atom style granular"); + if ((strcmp(output->thermo->style,"granular") != 0) && + (strcmp(output->thermo->style,"custom") != 0)) + error->all("Must use atom style granular with granular thermo output"); + } + + // don't allow granular and dpd styles together + // only reason is that they both communicate v in pack_comm + // ok if use better logic for setting size_comm and atom_hybrid::pack_comm + + if (style_granular && style_dpd) + error->all("Atom style granular and dpd cannot be used together"); +} + +/* ---------------------------------------------------------------------- + grow atom arrays + n = 0 grows arrays by DELTA + n > 0 allocates arrays to size n +------------------------------------------------------------------------- */ + +void Atom::grow(int n) +{ + if (n == 0) nmax += DELTA; + else nmax = n; + + tag = (int *) memory->srealloc(tag,nmax*sizeof(int),"atom:tag"); + type = (int *) memory->srealloc(type,nmax*sizeof(int),"atom:type"); + mask = (int *) memory->srealloc(mask,nmax*sizeof(int),"atom:mask"); + image = (int *) memory->srealloc(image,nmax*sizeof(int),"atom:image"); + x = memory->grow_2d_double_array(x,nmax,3,"atom:x"); + v = memory->grow_2d_double_array(v,nmax,3,"atom:v"); + f = memory->grow_2d_double_array(f,nmax,3,"atom:f"); + + if (charge_allow) + q = (double *) memory->srealloc(q,nmax*sizeof(double),"atom:q"); + if (mass_allow) + rmass = (double *) memory->srealloc(rmass,nmax*sizeof(double), + "atom:rmass"); + + if (style_dipole) { + mu = memory->grow_2d_double_array(mu,nmax,3,"atom:mu"); + omega = memory->grow_2d_double_array(omega,nmax,3,"atom:omega"); + torque = memory->grow_2d_double_array(torque,nmax,3,"atom:torque"); + } + + if (style_granular) { + phix = memory->grow_2d_double_array(phix,nmax,3,"atom:phix"); + phiv = memory->grow_2d_double_array(phiv,nmax,3,"atom:phiv"); + phia = memory->grow_2d_double_array(phia,nmax,3,"atom:phia"); + radius = (double *) memory->srealloc(radius,nmax*sizeof(double), + "atom:radius"); + density = (double *) memory->srealloc(density,nmax*sizeof(double), + "atom:density"); + } + + if (style_peri) + vfrac = (double *) memory->srealloc(vfrac,nmax*sizeof(double), + "atom:vfrac"); + + if (molecular) { + molecule = (int *) + memory->srealloc(molecule,nmax*sizeof(int),"atom:molecule"); + + nspecial = memory->grow_2d_int_array(nspecial,nmax,3,"atom:nspecial"); + special = + memory->grow_2d_int_array(special,nmax,maxspecial,"atom:special"); + + if (bonds_allow) { + num_bond = (int *) + memory->srealloc(num_bond,nmax*sizeof(int),"atom:num_bond"); + bond_type = memory->grow_2d_int_array(bond_type,nmax,bond_per_atom, + "atom:bond_type"); + bond_atom = memory->grow_2d_int_array(bond_atom,nmax,bond_per_atom, + "atom:bond_atom"); + } + + if (angles_allow) { + num_angle = (int *) + memory->srealloc(num_angle,nmax*sizeof(int),"atom:num_angle"); + angle_type = memory->grow_2d_int_array(angle_type,nmax,angle_per_atom, + "atom:angle_type"); + angle_atom1 = memory->grow_2d_int_array(angle_atom1,nmax,angle_per_atom, + "atom:angle_atom1"); + angle_atom2 = memory->grow_2d_int_array(angle_atom2,nmax,angle_per_atom, + "atom:angle_atom2"); + angle_atom3 = memory->grow_2d_int_array(angle_atom3,nmax,angle_per_atom, + "atom:angle_atom3"); + } + + if (dihedrals_allow) { + num_dihedral = (int *) + memory->srealloc(num_dihedral,nmax*sizeof(int),"atom:num_dihedral"); + dihedral_type = + memory->grow_2d_int_array(dihedral_type,nmax,dihedral_per_atom, + "atom:dihedral_type"); + dihedral_atom1 = + memory->grow_2d_int_array(dihedral_atom1,nmax,dihedral_per_atom, + "atom:dihedral_atom1"); + dihedral_atom2 = + memory->grow_2d_int_array(dihedral_atom2,nmax,dihedral_per_atom, + "atom:dihedral_atom2"); + dihedral_atom3 = + memory->grow_2d_int_array(dihedral_atom3,nmax,dihedral_per_atom, + "atom:dihedral_atom3"); + dihedral_atom4 = + memory->grow_2d_int_array(dihedral_atom4,nmax,dihedral_per_atom, + "atom:dihedral_atom4"); + } + + if (impropers_allow) { + num_improper = (int *) + memory->srealloc(num_improper,nmax*sizeof(int),"atom:num_improper"); + improper_type = + memory->grow_2d_int_array(improper_type,nmax,improper_per_atom, + "atom:improper_type"); + improper_atom1 = + memory->grow_2d_int_array(improper_atom1,nmax,improper_per_atom, + "atom:improper_atom1"); + improper_atom2 = + memory->grow_2d_int_array(improper_atom2,nmax,improper_per_atom, + "atom:improper_atom2"); + improper_atom3 = + memory->grow_2d_int_array(improper_atom3,nmax,improper_per_atom, + "atom:improper_atom3"); + improper_atom4 = + memory->grow_2d_int_array(improper_atom4,nmax,improper_per_atom, + "atom:improper_atom4"); + } + } + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + modify->fix[extra_grow[iextra]]->grow_arrays(nmax); +} + +/* ---------------------------------------------------------------------- + modify parameters of the atom style +------------------------------------------------------------------------- */ + +void Atom::modify_params(int narg, char **arg) +{ + if (narg == 0) error->all("Illegal atom_modify command"); + + int iarg = 0; + while (iarg < narg) { + if (strcmp(arg[iarg],"map") == 0) { + if (iarg+2 > narg) error->all("Illegal atom_modify command"); + if (strcmp(arg[iarg+1],"array") == 0) map_style = 1; + else if (strcmp(arg[iarg+1],"hash") == 0) map_style = 2; + else error->all("Illegal atom_modify command"); + iarg += 2; + } else error->all("Illegal atom_modify command"); + } +} + +/* ---------------------------------------------------------------------- + allocate and initialize array or hash table for global -> local map + set map_tag_max = largest atom ID (may be larger than natoms) + for array option: + array length = 1 to largest tag of any atom + set entire array to -1 as initial values + for hash option: + map_nhash = length of hash table + map_nbucket = # of hash buckets, prime larger than map_nhash + so buckets will only be filled with 0 or 1 atoms on average +------------------------------------------------------------------------- */ + +void Atom::map_init() +{ + map_delete(); + + if (tag_enable == 0) + error->all("Cannot create an atom map unless atoms have IDs"); + + int max = 0; + for (int i = 0; i < nlocal; i++) max = MAX(max,tag[i]); + MPI_Allreduce(&max,&map_tag_max,1,MPI_INT,MPI_MAX,world); + + if (map_style == 1) { + map_array = (int *) + memory->smalloc((map_tag_max+1)*sizeof(int),"atom:map_array"); + for (int i = 0; i <= map_tag_max; i++) map_array[i] = -1; + + } else { + + // map_nhash = max of atoms/proc or total atoms, times 2, at least 1000 + + int nper = static_cast<int> (natoms/comm->nprocs); + map_nhash = MAX(nper,nmax); + if (map_nhash > natoms) map_nhash = static_cast<int> (natoms); + if (comm->nprocs > 1) map_nhash *= 2; + map_nhash = MAX(map_nhash,1000); + + // map_nbucket = prime just larger than map_nhash + + int n = map_nhash/10000; + n = MIN(n,nprimes-1); + map_nbucket = primes[n]; + if (map_nbucket < map_nhash && n < nprimes-1) map_nbucket = primes[n+1]; + + // set all buckets to empty + // set hash to map_nhash in length + // put all hash entries in free list and point them to each other + + map_bucket = new int[map_nbucket]; + for (int i = 0; i < map_nbucket; i++) map_bucket[i] = -1; + + map_hash = new HashElem[map_nhash]; + map_nused = 0; + map_free = 0; + for (int i = 0; i < map_nhash; i++) map_hash[i].next = i+1; + map_hash[map_nhash-1].next = -1; + } +} + +/* ---------------------------------------------------------------------- + clear global -> local map for all of my own and ghost atoms + for hash table option: + global ID may not be in table if image atom was already cleared +------------------------------------------------------------------------- */ + +void Atom::map_clear() +{ + if (map_style == 1) { + int nall = nlocal + nghost; + for (int i = 0; i < nall; i++) map_array[tag[i]] = -1; + + } else { + int previous,global,ibucket,index; + int nall = nlocal + nghost; + for (int i = 0; i < nall; i++) { + + // search for key + // if don't find it, done + + previous = -1; + global = tag[i]; + ibucket = global % map_nbucket; + index = map_bucket[ibucket]; + while (index > -1) { + if (map_hash[index].global == global) break; + previous = index; + index = map_hash[index].next; + } + if (index == -1) continue; + + // delete the hash entry and add it to free list + // special logic if entry is 1st in the bucket + + if (previous == -1) map_bucket[ibucket] = map_hash[index].next; + else map_hash[previous].next = map_hash[index].next; + + map_hash[index].next = map_free; + map_free = index; + map_nused--; + } + } +} + +/* ---------------------------------------------------------------------- + set global -> local map for all of my own and ghost atoms + loop in reverse order so that nearby images take precedence over far ones + and owned atoms take precedence over images + this enables valid lookups of bond topology atoms + for hash table option: + if hash table too small, re-init + global ID may already be in table if image atom was set +------------------------------------------------------------------------- */ + +void Atom::map_set() +{ + if (map_style == 1) { + int nall = nlocal + nghost; + for (int i = nall-1; i >= 0 ; i--) map_array[tag[i]] = i; + + } else { + int previous,global,ibucket,index; + int nall = nlocal + nghost; + if (nall > map_nhash) map_init(); + + for (int i = nall-1; i >= 0 ; i--) { + + // search for key + // if found it, just overwrite local value with index + + previous = -1; + global = tag[i]; + ibucket = global % map_nbucket; + index = map_bucket[ibucket]; + while (index > -1) { + if (map_hash[index].global == global) break; + previous = index; + index = map_hash[index].next; + } + if (index > -1) { + map_hash[index].local = i; + continue; + } + + // take one entry from free list + // add the new global/local pair as entry at end of bucket list + // special logic if this entry is 1st in bucket + + index = map_free; + map_free = map_hash[map_free].next; + if (previous == -1) map_bucket[ibucket] = index; + else map_hash[previous].next = index; + map_hash[index].global = global; + map_hash[index].local = i; + map_hash[index].next = -1; + map_nused++; + } + } +} + +/* ---------------------------------------------------------------------- + set global to local map for one atom + for hash table option: + global ID may already be in table if atom was already set +------------------------------------------------------------------------- */ + +void Atom::map_one(int global, int local) +{ + if (map_style == 1) map_array[global] = local; + + else { + // search for key + // if found it, just overwrite local value with index + + int previous = -1; + int ibucket = global % map_nbucket; + int index = map_bucket[ibucket]; + while (index > -1) { + if (map_hash[index].global == global) break; + previous = index; + index = map_hash[index].next; + } + if (index > -1) { + map_hash[index].local = local; + return; + } + + // take one entry from free list + // add the new global/local pair as entry at end of bucket list + // special logic if this entry is 1st in bucket + + index = map_free; + map_free = map_hash[map_free].next; + if (previous == -1) map_bucket[ibucket] = index; + else map_hash[previous].next = index; + map_hash[index].global = global; + map_hash[index].local = local; + map_hash[index].next = -1; + map_nused++; + } +} + +/* ---------------------------------------------------------------------- + free the array or hash table for global to local mapping +------------------------------------------------------------------------- */ + +void Atom::map_delete() +{ + if (map_style == 1) { + if (map_tag_max) memory->sfree(map_array); + } else { + if (map_nhash) { + delete [] map_bucket; + delete [] map_hash; + } + map_nhash = 0; + } + map_tag_max = 0; +} + +/* ---------------------------------------------------------------------- + lookup global ID in hash table, return local index +------------------------------------------------------------------------- */ + +int Atom::map_find_hash(int global) +{ + int local = -1; + int index = map_bucket[global % map_nbucket]; + while (index > -1) { + if (map_hash[index].global == global) { + local = map_hash[index].local; + break; + } + index = map_hash[index].next; + } + return local; +} + +/* ---------------------------------------------------------------------- + add unique tags to any atoms with tag = 0 + new tags are grouped by proc and start after max current tag + called after creating new atoms +------------------------------------------------------------------------- */ + +void Atom::tag_extend() +{ + // maxtag_all = max tag for all atoms + + int maxtag = 0; + for (int i = 0; i < nlocal; i++) maxtag = MAX(maxtag,tag[i]); + int maxtag_all; + MPI_Allreduce(&maxtag,&maxtag_all,1,MPI_INT,MPI_MAX,world); + + // notag = # of atoms I own with no tag (tag = 0) + // notag_sum = # of total atoms on procs <= me with no tag + + int notag = 0; + for (int i = 0; i < nlocal; i++) if (tag[i] == 0) notag++; + int notag_sum; + MPI_Scan(¬ag,¬ag_sum,1,MPI_INT,MPI_SUM,world); + + // itag = 1st new tag that my untagged atoms should use + + int itag = maxtag_all + notag_sum - notag + 1; + for (int i = 0; i < nlocal; i++) if (tag[i] == 0) tag[i] = itag++; +} + +/* ---------------------------------------------------------------------- + check if atom tags are consecutive from 1 to Natoms + return 0 if any tag <= 0 or maxtag > Natoms + return 1 if OK (doesn't actually check if all tag values are used) +------------------------------------------------------------------------- */ + +int Atom::tag_consecutive() +{ + // check[0] = flagged if any tag <= 0 + // check[1] = max tag of any atom + + int check[2],check_all[2]; + + check[0] = check[1] = 0; + for (int i = 0; i < nlocal; i++) { + if (tag[i] <= 0) check[0] = 1; + if (tag[i] > check[1]) check[1] = tag[i]; + } + MPI_Allreduce(check,check_all,2,MPI_INT,MPI_MAX,world); + + if (check_all[0] || check_all[1] > natoms) return 0; + return 1; +} + +/* ---------------------------------------------------------------------- + parse one atom line from data file, check if valid for atom style +------------------------------------------------------------------------- */ + +int Atom::parse_data(char *line) +{ + size_atom_actual = count_words(line); + if (size_atom_actual == size_atom_valid || + size_atom_actual == size_atom_valid + 3) return 0; + else return 1; +} + +/* ---------------------------------------------------------------------- + count and return words in a single line + make copy of line before using strtok so as not to change line + trim anything from '#' onward +------------------------------------------------------------------------- */ + +int Atom::count_words(char *line) +{ + int n = strlen(line) + 1; + char *copy = (char *) memory->smalloc(n*sizeof(char),"copy"); + strcpy(copy,line); + + char *ptr; + if (ptr = strchr(copy,'#')) *ptr = '\0'; + + if (strtok(copy," \t\n\r\f") == NULL) { + memory->sfree(copy); + return 0; + } + n = 1; + while (strtok(NULL," \t\n\r\f")) n++; + + memory->sfree(copy); + return n; +} + +/* ---------------------------------------------------------------------- + check that atom IDs are > 0 and <= map_tag_max +------------------------------------------------------------------------- */ + +void Atom::unpack_vels(int n, char *buf) +{ + int m,tagtmp; + double vxtmp,vytmp,vztmp; + char *next; + + for (int i = 0; i < n; i++) { + next = strchr(buf,'\n'); + *next = '\0'; + sscanf(buf,"%d %lg %lg %lg",&tagtmp,&vxtmp,&vytmp,&vztmp); + if (tagtmp <= 0 || tagtmp > map_tag_max) + error->one("Invalid atom ID in Velocities section of data file"); + if ((m = map(tagtmp)) >= 0) { + v[m][0] = vxtmp; + v[m][1] = vytmp; + v[m][2] = vztmp; + } + buf = next + 1; + } +} + +/* ---------------------------------------------------------------------- + check that atom IDs are > 0 and <= map_tag_max +------------------------------------------------------------------------- */ + +void Atom::unpack_bonds(int n, char *buf) +{ + int m,tmp,itype,atom1,atom2; + char *next; + int newton_bond = force->newton_bond; + + for (int i = 0; i < n; i++) { + next = strchr(buf,'\n'); + *next = '\0'; + sscanf(buf,"%d %d %d %d",&tmp,&itype,&atom1,&atom2); + if (atom1 <= 0 || atom1 > map_tag_max || + atom2 <= 0 || atom2 > map_tag_max) + error->one("Invalid atom ID in Bonds section of data file"); + if (itype <= 0 || itype > nbondtypes) + error->one("Invalid bond type in Bonds section of data file"); + if ((m = map(atom1)) >= 0) { + bond_type[m][num_bond[m]] = itype; + bond_atom[m][num_bond[m]] = atom2; + num_bond[m]++; + } + if (newton_bond == 0) { + if ((m = map(atom2)) >= 0) { + bond_type[m][num_bond[m]] = itype; + bond_atom[m][num_bond[m]] = atom1; + num_bond[m]++; + } + } + buf = next + 1; + } +} + +/* ---------------------------------------------------------------------- + check that atom IDs are > 0 and <= map_tag_max +------------------------------------------------------------------------- */ + +void Atom::unpack_angles(int n, char *buf) +{ + int m,tmp,itype,atom1,atom2,atom3; + char *next; + int newton_bond = force->newton_bond; + + for (int i = 0; i < n; i++) { + next = strchr(buf,'\n'); + *next = '\0'; + sscanf(buf,"%d %d %d %d %d",&tmp,&itype,&atom1,&atom2,&atom3); + if (atom1 <= 0 || atom1 > map_tag_max || + atom2 <= 0 || atom2 > map_tag_max || + atom3 <= 0 || atom3 > map_tag_max) + error->one("Invalid atom ID in Angles section of data file"); + if (itype <= 0 || itype > nangletypes) + error->one("Invalid angle type in Angles section of data file"); + if ((m = map(atom2)) >= 0) { + angle_type[m][num_angle[m]] = itype; + angle_atom1[m][num_angle[m]] = atom1; + angle_atom2[m][num_angle[m]] = atom2; + angle_atom3[m][num_angle[m]] = atom3; + num_angle[m]++; + } + if (newton_bond == 0) { + if ((m = map(atom1)) >= 0) { + angle_type[m][num_angle[m]] = itype; + angle_atom1[m][num_angle[m]] = atom1; + angle_atom2[m][num_angle[m]] = atom2; + angle_atom3[m][num_angle[m]] = atom3; + num_angle[m]++; + } + if ((m = map(atom3)) >= 0) { + angle_type[m][num_angle[m]] = itype; + angle_atom1[m][num_angle[m]] = atom1; + angle_atom2[m][num_angle[m]] = atom2; + angle_atom3[m][num_angle[m]] = atom3; + num_angle[m]++; + } + } + buf = next + 1; + } +} + +/* ---------------------------------------------------------------------- + check that atom IDs are > 0 and <= map_tag_max +------------------------------------------------------------------------- */ + +void Atom::unpack_dihedrals(int n, char *buf) +{ + int m,tmp,itype,atom1,atom2,atom3,atom4; + char *next; + int newton_bond = force->newton_bond; + + for (int i = 0; i < n; i++) { + next = strchr(buf,'\n'); + *next = '\0'; + sscanf(buf,"%d %d %d %d %d %d",&tmp,&itype,&atom1,&atom2,&atom3,&atom4); + if (atom1 <= 0 || atom1 > map_tag_max || + atom2 <= 0 || atom2 > map_tag_max || + atom3 <= 0 || atom3 > map_tag_max || + atom4 <= 0 || atom4 > map_tag_max) + error->one("Invalid atom ID in Dihedrals section of data file"); + if (itype <= 0 || itype > ndihedraltypes) + error->one("Invalid dihedral type in Dihedrals section of data file"); + if ((m = map(atom2)) >= 0) { + dihedral_type[m][num_dihedral[m]] = itype; + dihedral_atom1[m][num_dihedral[m]] = atom1; + dihedral_atom2[m][num_dihedral[m]] = atom2; + dihedral_atom3[m][num_dihedral[m]] = atom3; + dihedral_atom4[m][num_dihedral[m]] = atom4; + num_dihedral[m]++; + } + if (newton_bond == 0) { + if ((m = map(atom1)) >= 0) { + dihedral_type[m][num_dihedral[m]] = itype; + dihedral_atom1[m][num_dihedral[m]] = atom1; + dihedral_atom2[m][num_dihedral[m]] = atom2; + dihedral_atom3[m][num_dihedral[m]] = atom3; + dihedral_atom4[m][num_dihedral[m]] = atom4; + num_dihedral[m]++; + } + if ((m = map(atom3)) >= 0) { + dihedral_type[m][num_dihedral[m]] = itype; + dihedral_atom1[m][num_dihedral[m]] = atom1; + dihedral_atom2[m][num_dihedral[m]] = atom2; + dihedral_atom3[m][num_dihedral[m]] = atom3; + dihedral_atom4[m][num_dihedral[m]] = atom4; + num_dihedral[m]++; + } + if ((m = map(atom4)) >= 0) { + dihedral_type[m][num_dihedral[m]] = itype; + dihedral_atom1[m][num_dihedral[m]] = atom1; + dihedral_atom2[m][num_dihedral[m]] = atom2; + dihedral_atom3[m][num_dihedral[m]] = atom3; + dihedral_atom4[m][num_dihedral[m]] = atom4; + num_dihedral[m]++; + } + } + buf = next + 1; + } +} + +/* ---------------------------------------------------------------------- + check that atom IDs are > 0 and <= map_tag_max +------------------------------------------------------------------------- */ + +void Atom::unpack_impropers(int n, char *buf) +{ + int m,tmp,itype,atom1,atom2,atom3,atom4; + char *next; + int newton_bond = force->newton_bond; + + for (int i = 0; i < n; i++) { + next = strchr(buf,'\n'); + *next = '\0'; + sscanf(buf,"%d %d %d %d %d %d",&tmp,&itype,&atom1,&atom2,&atom3,&atom4); + if (atom1 <= 0 || atom1 > map_tag_max || + atom2 <= 0 || atom2 > map_tag_max || + atom3 <= 0 || atom3 > map_tag_max || + atom4 <= 0 || atom4 > map_tag_max) + error->one("Invalid atom ID in Impropers section of data file"); + if (itype <= 0 || itype > nimpropertypes) + error->one("Invalid improper type in Impropers section of data file"); + if ((m = map(atom2)) >= 0) { + improper_type[m][num_improper[m]] = itype; + improper_atom1[m][num_improper[m]] = atom1; + improper_atom2[m][num_improper[m]] = atom2; + improper_atom3[m][num_improper[m]] = atom3; + improper_atom4[m][num_improper[m]] = atom4; + num_improper[m]++; + } + if (newton_bond == 0) { + if ((m = map(atom1)) >= 0) { + improper_type[m][num_improper[m]] = itype; + improper_atom1[m][num_improper[m]] = atom1; + improper_atom2[m][num_improper[m]] = atom2; + improper_atom3[m][num_improper[m]] = atom3; + improper_atom4[m][num_improper[m]] = atom4; + num_improper[m]++; + } + if ((m = map(atom3)) >= 0) { + improper_type[m][num_improper[m]] = itype; + improper_atom1[m][num_improper[m]] = atom1; + improper_atom2[m][num_improper[m]] = atom2; + improper_atom3[m][num_improper[m]] = atom3; + improper_atom4[m][num_improper[m]] = atom4; + num_improper[m]++; + } + if ((m = map(atom4)) >= 0) { + improper_type[m][num_improper[m]] = itype; + improper_atom1[m][num_improper[m]] = atom1; + improper_atom2[m][num_improper[m]] = atom2; + improper_atom3[m][num_improper[m]] = atom3; + improper_atom4[m][num_improper[m]] = atom4; + num_improper[m]++; + } + } + buf = next + 1; + } +} + +/* ---------------------------------------------------------------------- + allocate arrays of length ntypes + only done after ntypes is set +------------------------------------------------------------------------- */ + +void Atom::allocate_type_arrays() +{ + if (mass_require) { + mass = new double[ntypes+1]; + mass_setflag = new int[ntypes+1]; + for (int itype = 1; itype <= ntypes; itype++) mass_setflag[itype] = 0; + } + if (dipole_require) { + dipole = new double[ntypes+1]; + dipole_setflag = new int[ntypes+1]; + for (int itype = 1; itype <= ntypes; itype++) dipole_setflag[itype] = 0; + } +} + +/* ---------------------------------------------------------------------- + set a mass and flag it as set + called from reading of data file +------------------------------------------------------------------------- */ + +void Atom::set_mass(char *str) +{ + if (mass_require == 0) error->all("Cannot set mass for this atom style"); + + int itype; + double mass_one; + sscanf(str,"%d %lg",&itype,&mass_one); + + if (itype < 1 || itype > ntypes) error->all("Invalid type for mass set"); + + mass[itype] = mass_one; + mass_setflag[itype] = 1; +} + +/* ---------------------------------------------------------------------- + set a mass and flag it as set + called from EAM pair routine +------------------------------------------------------------------------- */ + +void Atom::set_mass(int itype, double value) +{ + if (mass_require == 0) error->all("Cannot set mass for this atom style"); + if (itype < 1 || itype > ntypes) error->all("Invalid type for mass set"); + + mass[itype] = value; + mass_setflag[itype] = 1; +} + +/* ---------------------------------------------------------------------- + set one or more masses and flag them as set + called from reading of input script +------------------------------------------------------------------------- */ + +void Atom::set_mass(int narg, char **arg) +{ + if (mass_require == 0) error->all("Cannot set mass for this atom style"); + + int lo,hi; + force->bounds(arg[0],ntypes,lo,hi); + if (lo < 1 || hi > ntypes) error->all("Invalid type for mass set"); + + for (int itype = lo; itype <= hi; itype++) { + mass[itype] = atof(arg[1]); + mass_setflag[itype] = 1; + } +} + +/* ---------------------------------------------------------------------- + set all masses as read in from restart file +------------------------------------------------------------------------- */ + +void Atom::set_mass(double *values) +{ + for (int itype = 1; itype <= ntypes; itype++) { + mass[itype] = values[itype]; + mass_setflag[itype] = 1; + } +} + +/* ---------------------------------------------------------------------- + check that all masses have been set +------------------------------------------------------------------------- */ + +void Atom::check_mass() +{ + if (!mass_require) return; + for (int itype = 1; itype <= ntypes; itype++) + if (mass_setflag[itype] == 0) error->all("All masses are not set"); +} + +/* ---------------------------------------------------------------------- + set a dipole moment and flag it as set + called from reading of data file +------------------------------------------------------------------------- */ + +void Atom::set_dipole(char *str) +{ + if (dipole_require == 0) + error->all("Cannot set dipole for this atom style"); + + int i; + double dipole_one; + sscanf(str,"%d %lg",&i,&dipole_one); + + dipole[i] = dipole_one; + dipole_setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- + set one or more dipole moments and flag them as set + called from reading of input script +------------------------------------------------------------------------- */ + +void Atom::set_dipole(int narg, char **arg) +{ + if (dipole_require == 0) + error->all("Cannot set dipole for this atom style"); + + int lo,hi; + force->bounds(arg[0],ntypes,lo,hi); + if (lo < 1 || hi > ntypes) error->all("Invalid type for dipole set"); + + for (int itype = lo; itype <= hi; itype++) { + dipole[itype] = atof(arg[1]); + dipole_setflag[itype] = 1; + } +} + +/* ---------------------------------------------------------------------- + set all dipole moments as read in from restart file +------------------------------------------------------------------------- */ + +void Atom::set_dipole(double *values) +{ + for (int itype = 1; itype <= ntypes; itype++) { + dipole[itype] = values[itype]; + dipole_setflag[itype] = 1; + } +} + +/* ---------------------------------------------------------------------- + check that all dipole moments have been set +------------------------------------------------------------------------- */ + +void Atom::check_dipole() +{ + if (!dipole_require) return; + for (int itype = 1; itype <= ntypes; itype++) + if (dipole_setflag[itype] == 0) + error->all("All dipole moments are not set"); +} + +/* ---------------------------------------------------------------------- + register a callback to a fix so it can manage atom-based arrays + happens when fix is created + flag = 0 for grow, 1 for restart +------------------------------------------------------------------------- */ + +void Atom::add_callback(int flag) +{ + int ifix; + + // find the fix + // if find NULL ptr: + // it's this one, since it is deleted at this point in creation + // if don't find NULL ptr: + // i is set to nfix = new one currently being added at end of list + + for (ifix = 0; ifix < modify->nfix; ifix++) + if (modify->fix[ifix] == NULL) break; + + // add callback to lists, reallocating if necessary + + if (flag == 0) { + if (nextra_grow == nextra_grow_max) { + nextra_grow_max += DELTA_CALLBACK; + extra_grow = (int *) + memory->srealloc(extra_grow,nextra_grow_max*sizeof(int), + "atom:extra_grow"); + } + extra_grow[nextra_grow] = ifix; + nextra_grow++; + } else if (flag == 1) { + if (nextra_restart == nextra_restart_max) { + nextra_restart_max += DELTA_CALLBACK; + extra_restart = (int *) + memory->srealloc(extra_restart,nextra_restart_max*sizeof(int), + "atom:extra_restart"); + } + extra_restart[nextra_restart] = ifix; + nextra_restart++; + } +} + +/* ---------------------------------------------------------------------- + unregister a callback to a fix + happens when fix is deleted + flag = 0 for grow, 1 for restart +------------------------------------------------------------------------- */ + +void Atom::delete_callback(char *id, int flag) +{ + int ifix; + + for (ifix = 0; ifix < modify->nfix; ifix++) + if (strcmp(id,modify->fix[ifix]->id) == 0) break; + + // compact the list of callbacks + + if (flag == 0) { + for (int i = ifix; i < nextra_grow-1; i++) + extra_grow[i] = extra_grow[i+1]; + nextra_grow--; + } else if (flag == 1) { + for (int i = ifix; i < nextra_restart-1; i++) + extra_restart[i] = extra_restart[i+1]; + nextra_restart--; + } +} + +/* ---------------------------------------------------------------------- + decrement ptrs in callback lists to fixes beyond the deleted ifix + happens after fix is deleted +------------------------------------------------------------------------- */ + +void Atom::update_callback(int ifix) +{ + for (int i = 0; i < nextra_grow; i++) + if (extra_grow[i] > ifix) extra_grow[i]--; + for (int i = 0; i < nextra_restart; i++) + if (extra_restart[i] > ifix) extra_restart[i]--; +} + +/* ---------------------------------------------------------------------- + unpack n lines of atoms from buf + set all atom values and defaults +------------------------------------------------------------------------- */ + +void Atom::unpack_data(int n, char *buf) +{ + int m,imagetmp,xptr,iptr; + double xtmp,ytmp,ztmp; + char *next; + + double subxlo = domain->subxlo; + double subxhi = domain->subxhi; + double subylo = domain->subylo; + double subyhi = domain->subyhi; + double subzlo = domain->subzlo; + double subzhi = domain->subzhi; + + char **values = new char*[size_atom_actual]; + + // xptr = which word in line is start of xyz coords + // iptr = which word in line is start of ix,iy,iz image flags + + xptr = 2; + if (molecular) xptr++; + if (charge_allow) xptr++; + if (style_granular) xptr += 2; + if (style_peri) xptr += 2; + + int imageflag = 0; + if (size_atom_actual > size_atom_valid) imageflag = 1; + if (imageflag) iptr = size_atom_actual - 3; + + // loop over lines of atom data + // tokenize the line into values + // extract xyz coords and image flags + // if atom is in my sub-domain, set its values + // scan thru values in ascending order, storing ones appropriate to style + + for (int i = 0; i < n; i++) { + next = strchr(buf,'\n'); + + values[0] = strtok(buf," \t\n\r\f"); + for (m = 1; m < size_atom_actual; m++) + values[m] = strtok(NULL," \t\n\r\f"); + + xtmp = atof(values[xptr]); + ytmp = atof(values[xptr+1]); + ztmp = atof(values[xptr+2]); + if (imageflag) + imagetmp = ((atoi(values[iptr+2]) + 512 & 1023) << 20) | + ((atoi(values[iptr+1]) + 512 & 1023) << 10) | + (atoi(values[iptr]) + 512 & 1023); + else imagetmp = (512 << 20) | (512 << 10) | 512; + + domain->remap(xtmp,ytmp,ztmp,imagetmp); + if (xtmp >= subxlo && xtmp < subxhi && + ytmp >= subylo && ytmp < subyhi && + ztmp >= subzlo && ztmp < subzhi) { + + if (nlocal == nmax) grow(0); + + // parse quantities in prescribed order depending on atom style(s) + + m = 0; + tag[nlocal] = atoi(values[m++]); + if (tag[nlocal] <= 0) + error->one("Invalid atom ID in Atoms section of data file"); + + if (molecular) molecule[nlocal] = atoi(values[m++]); + + type[nlocal] = atoi(values[m++]); + if (type[nlocal] <= 0 || type[nlocal] > ntypes) + error->one("Invalid atom type in Atoms section of data file"); + + if (charge_allow) q[nlocal] = atof(values[m++]); + if (style_granular) { + radius[nlocal] = 0.5 * atof(values[m++]); + density[nlocal] = atof(values[m++]); + if (force->dimension == 3) + rmass[nlocal] = 4.0*PI/3.0 * + radius[nlocal]*radius[nlocal]*radius[nlocal] * density[nlocal]; + else + rmass[nlocal] = PI * radius[nlocal]*radius[nlocal] * density[nlocal]; + } + if (style_peri) { + vfrac[nlocal] = atof(values[m++]); + rmass[nlocal] = atof(values[m++]); + } + + x[nlocal][0] = xtmp; + x[nlocal][1] = ytmp; + x[nlocal][2] = ztmp; + m += 3; + + if (style_dipole) { + mu[nlocal][0] = atof(values[m++]); + mu[nlocal][1] = atof(values[m++]); + mu[nlocal][2] = atof(values[m++]); + } + image[nlocal] = imagetmp; + + // initialize quantities not included in Atom section of data file + + mask[nlocal] = 1; + v[nlocal][0] = 0.0; + v[nlocal][1] = 0.0; + v[nlocal][2] = 0.0; + + if (style_dipole) { + omega[nlocal][0] = 0.0; + omega[nlocal][1] = 0.0; + omega[nlocal][2] = 0.0; + } + + if (style_granular) { + phix[nlocal][0] = 0.0; + phix[nlocal][1] = 0.0; + phix[nlocal][2] = 0.0; + phiv[nlocal][0] = 0.0; + phiv[nlocal][1] = 0.0; + phiv[nlocal][2] = 0.0; + } + + if (bonds_allow) num_bond[nlocal] = 0; + if (angles_allow) num_angle[nlocal] = 0; + if (dihedrals_allow) num_dihedral[nlocal] = 0; + if (impropers_allow) num_improper[nlocal] = 0; + + nlocal++; + } + + buf = next + 1; + } + + delete [] values; +} + +/* ---------------------------------------------------------------------- + create one atom of type itype at x0,y0,z0 + set all other values to defaults +------------------------------------------------------------------------- */ + +void Atom::create_one(int itype, double x0, double y0, double z0) +{ + if (nlocal == nmax) grow(0); + + tag[nlocal] = 0; + type[nlocal] = itype; + x[nlocal][0] = x0; + x[nlocal][1] = y0; + x[nlocal][2] = z0; + mask[nlocal] = 1; + image[nlocal] = (512 << 20) | (512 << 10) | 512; + v[nlocal][0] = 0.0; + v[nlocal][1] = 0.0; + v[nlocal][2] = 0.0; + + if (charge_allow) q[nlocal] = 0.0; + + if (style_dipole) { + mu[nlocal][0] = 0.0; + mu[nlocal][1] = 0.0; + mu[nlocal][2] = 0.0; + omega[nlocal][0] = 0.0; + omega[nlocal][1] = 0.0; + omega[nlocal][2] = 0.0; + } + + if (style_granular) { + radius[nlocal] = 0.5; + density[nlocal] = 1.0; + if (force->dimension == 3) + rmass[nlocal] = 4.0*PI/3.0 * + radius[nlocal]*radius[nlocal]*radius[nlocal] * density[nlocal]; + else + rmass[nlocal] = PI * radius[nlocal]*radius[nlocal] * density[nlocal]; + phix[nlocal][0] = 0.0; + phix[nlocal][1] = 0.0; + phix[nlocal][2] = 0.0; + phiv[nlocal][0] = 0.0; + phiv[nlocal][1] = 0.0; + phiv[nlocal][2] = 0.0; + } + + if (style_peri) { + vfrac[nlocal] = 1.0; + rmass[nlocal] = 1.0; + } + + if (molecular) { + molecule[nlocal] = 0; + if (bonds_allow) num_bond[nlocal] = 0; + if (angles_allow) num_angle[nlocal] = 0; + if (dihedrals_allow) num_dihedral[nlocal] = 0; + if (impropers_allow) num_improper[nlocal] = 0; + } + + nlocal++; +} + +/* ---------------------------------------------------------------------- */ + +int Atom::size_restart() +{ + int i; + + int n = 0; + for (i = 0; i < nlocal; i++) { + n += 11; + if (charge_allow) n++; + if (style_dipole) n += 6; + if (style_granular) n += 8; + if (style_peri) n += 5; + if (molecular) { + n++; + if (bonds_allow) n += 1 + 2*num_bond[i]; + if (angles_allow) n += 1 + 4*num_angle[i]; + if (dihedrals_allow) n += 1 + 5*num_dihedral[i]; + if (impropers_allow) n += 1 + 5*num_improper[i]; + } + } + + if (nextra_restart) + for (int iextra = 0; iextra < nextra_restart; iextra++) + for (i = 0; i < nlocal; i++) + n += modify->fix[extra_restart[iextra]]->size_restart(i); + + return n; +} + +/* ---------------------------------------------------------------------- + pack all atom quantities for restart file including extra quantities + xyz must be 1st 3 values, so that read_restart can test on them + interaction types may be negative, but write as positive +------------------------------------------------------------------------- */ + +int Atom::pack_restart(int i, double *buf) +{ + int k; + + 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++] = image[i]; + buf[m++] = v[i][0]; + buf[m++] = v[i][1]; + buf[m++] = v[i][2]; + + if (charge_allow) buf[m++] = q[i]; + + if (style_dipole) { + buf[m++] = mu[i][0]; + buf[m++] = mu[i][1]; + buf[m++] = mu[i][2]; + buf[m++] = omega[i][0]; + buf[m++] = omega[i][1]; + buf[m++] = omega[i][2]; + } + + if (style_granular) { + buf[m++] = phix[i][0]; + buf[m++] = phix[i][1]; + buf[m++] = phix[i][2]; + buf[m++] = phiv[i][0]; + buf[m++] = phiv[i][1]; + buf[m++] = phiv[i][2]; + buf[m++] = radius[i]; + buf[m++] = density[i]; + } + + if (style_peri) { + buf[m++] = vfrac[i]; + buf[m++] = rmass[i]; + } + + if (molecular) { + buf[m++] = molecule[i]; + + if (bonds_allow) { + buf[m++] = num_bond[i]; + for (k = 0; k < num_bond[i]; k++) { + buf[m++] = MAX(bond_type[i][k],-bond_type[i][k]); + buf[m++] = bond_atom[i][k]; + } + } + + if (angles_allow) { + buf[m++] = num_angle[i]; + for (k = 0; k < num_angle[i]; k++) { + buf[m++] = MAX(angle_type[i][k],-angle_type[i][k]); + buf[m++] = angle_atom1[i][k]; + buf[m++] = angle_atom2[i][k]; + buf[m++] = angle_atom3[i][k]; + } + } + + if (dihedrals_allow) { + buf[m++] = num_dihedral[i]; + for (k = 0; k < num_dihedral[i]; k++) { + buf[m++] = MAX(dihedral_type[i][k],-dihedral_type[i][k]); + buf[m++] = dihedral_atom1[i][k]; + buf[m++] = dihedral_atom2[i][k]; + buf[m++] = dihedral_atom3[i][k]; + buf[m++] = dihedral_atom4[i][k]; + } + } + + if (impropers_allow) { + buf[m++] = num_improper[i]; + for (k = 0; k < num_improper[i]; k++) { + buf[m++] = MAX(improper_type[i][k],-improper_type[i][k]); + buf[m++] = improper_atom1[i][k]; + buf[m++] = improper_atom2[i][k]; + buf[m++] = improper_atom3[i][k]; + buf[m++] = improper_atom4[i][k]; + } + } + } + + if (nextra_restart) + for (int iextra = 0; iextra < nextra_restart; iextra++) + m += modify->fix[extra_restart[iextra]]->pack_restart(i,&buf[m]); + + buf[0] = m; + return m; +} + +/* ---------------------------------------------------------------------- + unpack all atom quantities from restart file including extra quantities +------------------------------------------------------------------------- */ + +int Atom::unpack_restart(double *buf) +{ + int k; + + if (nlocal == nmax) { + grow(0); + if (nextra_store) + extra = memory->grow_2d_double_array(extra,nmax,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<int> (buf[m++]); + type[nlocal] = static_cast<int> (buf[m++]); + mask[nlocal] = static_cast<int> (buf[m++]); + image[nlocal] = static_cast<int> (buf[m++]); + v[nlocal][0] = buf[m++]; + v[nlocal][1] = buf[m++]; + v[nlocal][2] = buf[m++]; + + if (charge_allow) q[nlocal] = buf[m++]; + + if (style_dipole) { + mu[nlocal][0] = buf[m++]; + mu[nlocal][1] = buf[m++]; + mu[nlocal][2] = buf[m++]; + omega[nlocal][0] = buf[m++]; + omega[nlocal][1] = buf[m++]; + omega[nlocal][2] = buf[m++]; + } + + if (style_granular) { + phix[nlocal][0] = buf[m++]; + phix[nlocal][1] = buf[m++]; + phix[nlocal][2] = buf[m++]; + phiv[nlocal][0] = buf[m++]; + phiv[nlocal][1] = buf[m++]; + phiv[nlocal][2] = buf[m++]; + radius[nlocal] = buf[m++]; + density[nlocal] = buf[m++]; + if (force->dimension == 3) + rmass[nlocal] = 4.0*PI/3.0 * + radius[nlocal]*radius[nlocal]*radius[nlocal] * density[nlocal]; + else + rmass[nlocal] = PI * radius[nlocal]*radius[nlocal] * density[nlocal]; + } + + if (style_peri) { + vfrac[nlocal] = buf[m++]; + rmass[nlocal] = buf[m++]; + } + + if (molecular) { + molecule[nlocal] = static_cast<int> (buf[m++]); + + if (bonds_allow) { + num_bond[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_bond[nlocal]; k++) { + bond_type[nlocal][k] = static_cast<int> (buf[m++]); + bond_atom[nlocal][k] = static_cast<int> (buf[m++]); + } + } + + if (angles_allow) { + num_angle[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_angle[nlocal]; k++) { + angle_type[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom1[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom2[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom3[nlocal][k] = static_cast<int> (buf[m++]); + } + } + + if (dihedrals_allow) { + num_dihedral[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_dihedral[nlocal]; k++) { + dihedral_type[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom1[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom2[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom3[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom4[nlocal][k] = static_cast<int> (buf[m++]); + } + } + + if (impropers_allow) { + num_improper[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_improper[nlocal]; k++) { + improper_type[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom1[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom2[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom3[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom4[nlocal][k] = static_cast<int> (buf[m++]); + } + } + } + + if (nextra_store) { + int size = static_cast<int> (buf[0]) - m; + for (int i = 0; i < size; i++) extra[nlocal][i] = buf[m++]; + } + + nlocal++; + return m; +} + +/* ---------------------------------------------------------------------- + return # of bytes of allocated memory +------------------------------------------------------------------------- */ + +int Atom::memory_usage() +{ + int bytes = 0; + + bytes += 4 * nmax * sizeof(int); + bytes += 3 * nmax*3 * sizeof(double); + + if (charge_allow) bytes += 1 * nmax * sizeof(double); // q + if (mass_allow) bytes += 1 * nmax * sizeof(double); // rmass + if (style_dipole) bytes += 3 * nmax*3 * sizeof(double); // mu + if (style_granular) { + bytes += 3 * nmax*3 * sizeof(double); // phix,phiv,phia + bytes += 2 * nmax * sizeof(double); // radius,density + } + if (style_peri) bytes += nmax * sizeof(double); // vfrac + + if (map_style == 1) + bytes += map_tag_max * sizeof(int); + else if (map_style == 2) { + bytes += map_nbucket*sizeof(int); + bytes += map_nhash*sizeof(HashElem); + } + + if (molecular) { + bytes += nmax * sizeof(int); + bytes += nmax*3 * sizeof(int); + bytes += nmax*maxspecial * sizeof(int); + + if (bonds_allow) { + bytes += nmax * sizeof(int); + bytes += 2 * nmax*bond_per_atom * sizeof(int); + } + + if (angles_allow) { + bytes += nmax * sizeof(int); + bytes += 4 * nmax*angle_per_atom * sizeof(int); + } + + if (dihedrals_allow) { + bytes += nmax * sizeof(int); + bytes += 5 * nmax*dihedral_per_atom * sizeof(int); + } + + if (impropers_allow) { + bytes += nmax * sizeof(int); + bytes += 5 * nmax*improper_per_atom * sizeof(int); + } + } + + return bytes; +} diff --git a/src/atom.h b/src/atom.h new file mode 100644 index 0000000000000000000000000000000000000000..21aec0124a3e01688982f466ed8050eddef4a477 --- /dev/null +++ b/src/atom.h @@ -0,0 +1,212 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ATOM_H +#define ATOM_H + +#include "lammps.h" + +class Atom : public LAMMPS { + public: + char *style; + double PI; + + // atom counts + + double natoms; // total # of atoms in system, could be 0 + int nlocal,nghost; // # of owned and ghost atoms on this proc + int nmax; // max # of owned+ghost in arrays on this proc + + // # of atom and topology types + + int ntypes,nbondtypes,nangletypes,ndihedraltypes,nimpropertypes; + + // molecular counts + + int nbonds,nangles,ndihedrals,nimpropers; + int bond_per_atom,angle_per_atom,dihedral_per_atom,improper_per_atom; + + // atom styles + + int style_angle,style_atomic,style_bond,style_charge,style_dipole,style_dpd; + int style_eam,style_full,style_granular,style_molecular,style_peri; + int style_hybrid; + + // flags set by atom styles + + int molecular; // 0 = atomic, 1 = molecular system + int bonds_allow,angles_allow; // 0/1 if bonds, angles are used + int dihedrals_allow,impropers_allow; // 0/1 if dihedrals, impropers used + int charge_allow; // 0/1 if charges used + int mass_allow; // 0/1 if per-atom rmass array + int mass_require; // 0/1 if per-type masses + int dipole_require; // 0/1 if per-type dipole moments + int tag_enable; // 0/1 if atom ID tags are defined + + // communication sizes - set by each atom style + + int size_comm,size_reverse,size_border; + int size_atom_valid,size_atom_actual; + + // arrays of length ntypes and flags if set + + double *mass; + int *mass_setflag; + double *dipole; + int *dipole_setflag; + + // arrays common to all atom classes + + int *tag,*type,*mask,*image; + double **x,**v,**f; + + // arrays specific to some atom classes + + double *q,**mu; + double **omega,**torque; + double *radius,*density,*rmass,*vfrac; + double **phix,**phiv,**phia; + + int *molecule; + + int maxspecial; + int **nspecial,**special; + + int *num_bond; + int **bond_type; + int **bond_atom; + + int *num_angle; + int **angle_type; + int **angle_atom1,**angle_atom2,**angle_atom3; + + int *num_dihedral; + int **dihedral_type; + int **dihedral_atom1,**dihedral_atom2,**dihedral_atom3,**dihedral_atom4; + + int *num_improper; + int **improper_type; + int **improper_atom1,**improper_atom2,**improper_atom3,**improper_atom4; + + // array for extra peratom info in restart file destined for fix & diag + + double **extra; + + // callback ptrs for atom arrays managed by fix classes + + int nextra_grow,nextra_restart; // # of callbacks of each type + int *extra_grow,*extra_restart; // index of fix to callback to + int nextra_grow_max,nextra_restart_max; // size of callback lists + int nextra_store; + + // data for global to local ID mapping + + int map_style; // default or user-specified style of map + // 0 = none, 1 = array, 2 = hash + int map_tag_max; + int *map_array; + + struct HashElem { + int global; // key to search on = global ID + int local; // value associated with key = local index + int next; // next entry in this bucket, -1 if last + }; + int map_nhash; // # of entries hash table can hold + int map_nused; // # of actual entries in hash table + int map_free; // ptr to 1st unused entry in hash table + int map_nbucket; // # of hash buckets + int *map_bucket; // ptr to 1st entry in each bucket + HashElem *map_hash; // hash table + int *primes; // table of prime #s for hashing + int nprimes; // # of primes + + // functions common to all atom styles + + Atom(int, char **); + virtual ~Atom(); + void set_style(char *); + int check_style(char *); + int style2arg(char **&); + char *style2word(char *); + void settings(Atom *); + + void init(); + void grow(int); + + void modify_params(int, char **); + void tag_extend(); + int tag_consecutive(); + + int parse_data(char *); + int count_words(char *); + + void unpack_data(int, char *); + void create_one(int, double, double, double); + virtual void unpack_vels(int, char *); // can be overwritten by child + void unpack_bonds(int, char *); + void unpack_angles(int, char *); + void unpack_dihedrals(int, char *); + void unpack_impropers(int, char *); + + void allocate_type_arrays(); + void set_mass(char *); + void set_mass(int, double); + void set_mass(int, char **); + void set_mass(double *); + void check_mass(); + void set_dipole(char *); + void set_dipole(int, char **); + void set_dipole(double *); + void check_dipole(); + + void add_callback(int); + void delete_callback(char *, int); + void update_callback(int); + + int size_restart(); + int pack_restart(int, double *); + int unpack_restart(double *); + + int memory_usage(); + + // functions for global ID to local index atom mapping + // map lookup function is inlined right here for efficiency + + inline int map(int global) { + if (map_style == 1) return map_array[global]; + else return map_find_hash(global); + }; + + void map_init(); + void map_clear(); + void map_set(); + void map_one(int, int); + void map_delete(); + int map_find_hash(int); + + // pure virtual functions, must be defined in child class + + virtual void copy(int, int) = 0; + + virtual void pack_comm(int, int *, double *, int *) = 0; + virtual void unpack_comm(int, int, double *) = 0; + virtual void pack_reverse(int, int, double *) = 0; + virtual void unpack_reverse(int, int *, double *) = 0; + + virtual void pack_border(int, int *, double *, int *) = 0; + virtual void unpack_border(int, int, double *) = 0; + virtual int pack_exchange(int, double *) = 0; + virtual int unpack_exchange(double *) = 0; +}; + +#endif diff --git a/src/atom_angle.cpp b/src/atom_angle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f8d9716571b86885bc7c51a34ef20d71bad5ff23 --- /dev/null +++ b/src/atom_angle.cpp @@ -0,0 +1,290 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "atom_angle.h" +#include "domain.h" +#include "modify.h" +#include "fix.h" + +/* ---------------------------------------------------------------------- */ + +AtomAngle::AtomAngle(int narg, char **arg) : Atom(narg, arg) {} + +/* ---------------------------------------------------------------------- */ + +void AtomAngle::copy(int i, int j) +{ + int k; + + 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]; + + molecule[j] = molecule[i]; + num_bond[j] = num_bond[i]; + num_angle[j] = num_angle[i]; + nspecial[j][0] = nspecial[i][0]; + nspecial[j][1] = nspecial[i][1]; + nspecial[j][2] = nspecial[i][2]; + + for (k = 0; k < num_bond[j]; k++) { + bond_type[j][k] = bond_type[i][k]; + bond_atom[j][k] = bond_atom[i][k]; + } + + for (k = 0; k < num_angle[j]; k++) { + angle_type[j][k] = angle_type[i][k]; + angle_atom1[j][k] = angle_atom1[i][k]; + angle_atom2[j][k] = angle_atom2[i][k]; + angle_atom3[j][k] = angle_atom3[i][k]; + } + + for (k = 0; k < nspecial[j][2]; k++) special[j][k] = special[i][k]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + modify->fix[extra_grow[iextra]]->copy_arrays(i,j); +} + +/* ---------------------------------------------------------------------- */ + +void AtomAngle::pack_comm(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomAngle::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomAngle::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]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomAngle::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomAngle::pack_border(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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++] = molecule[j]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + buf[m++] = tag[j]; + buf[m++] = type[j]; + buf[m++] = mask[j]; + buf[m++] = molecule[j]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomAngle::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<int> (buf[m++]); + type[i] = static_cast<int> (buf[m++]); + mask[i] = static_cast<int> (buf[m++]); + molecule[i] = static_cast<int> (buf[m++]); + } +} + +/* ---------------------------------------------------------------------- + pack all atom quantities for shipping to another proc + xyz must be 1st 3 values, so that comm::exchange can test on them +------------------------------------------------------------------------- */ + +int AtomAngle::pack_exchange(int i, double *buf) +{ + int k; + + 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++] = image[i]; + buf[m++] = v[i][0]; + buf[m++] = v[i][1]; + buf[m++] = v[i][2]; + + buf[m++] = molecule[i]; + + buf[m++] = num_bond[i]; + for (k = 0; k < num_bond[i]; k++) { + buf[m++] = bond_type[i][k]; + buf[m++] = bond_atom[i][k]; + } + + buf[m++] = num_angle[i]; + for (k = 0; k < num_angle[i]; k++) { + buf[m++] = angle_type[i][k]; + buf[m++] = angle_atom1[i][k]; + buf[m++] = angle_atom2[i][k]; + buf[m++] = angle_atom3[i][k]; + } + + buf[m++] = nspecial[i][0]; + buf[m++] = nspecial[i][1]; + buf[m++] = nspecial[i][2]; + for (k = 0; k < nspecial[i][2]; k++) buf[m++] = special[i][k]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->pack_exchange(i,&buf[m]); + + buf[0] = m; + return m; +} + +/* ---------------------------------------------------------------------- */ + +int AtomAngle::unpack_exchange(double *buf) +{ + int k; + if (nlocal == nmax) grow(0); + + int m = 1; + x[nlocal][0] = buf[m++]; + x[nlocal][1] = buf[m++]; + x[nlocal][2] = buf[m++]; + tag[nlocal] = static_cast<int> (buf[m++]); + type[nlocal] = static_cast<int> (buf[m++]); + mask[nlocal] = static_cast<int> (buf[m++]); + image[nlocal] = static_cast<int> (buf[m++]); + v[nlocal][0] = buf[m++]; + v[nlocal][1] = buf[m++]; + v[nlocal][2] = buf[m++]; + + molecule[nlocal] = static_cast<int> (buf[m++]); + + num_bond[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_bond[nlocal]; k++) { + bond_type[nlocal][k] = static_cast<int> (buf[m++]); + bond_atom[nlocal][k] = static_cast<int> (buf[m++]); + } + + num_angle[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_angle[nlocal]; k++) { + angle_type[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom1[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom2[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom3[nlocal][k] = static_cast<int> (buf[m++]); + } + + nspecial[nlocal][0] = static_cast<int> (buf[m++]); + nspecial[nlocal][1] = static_cast<int> (buf[m++]); + nspecial[nlocal][2] = static_cast<int> (buf[m++]); + for (k = 0; k < nspecial[nlocal][2]; k++) + special[nlocal][k] = static_cast<int> (buf[m++]); + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->unpack_exchange(nlocal,&buf[m]); + + nlocal++; + return m; +} diff --git a/src/atom_angle.h b/src/atom_angle.h new file mode 100644 index 0000000000000000000000000000000000000000..a395c577a8ca49be919340cd0ca4f93f9cc45e6e --- /dev/null +++ b/src/atom_angle.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ATOM_ANGLE_H +#define ATOM_ANGLE_H + +#include "atom.h" + +class AtomAngle : public Atom { + public: + AtomAngle(int, char **); + ~AtomAngle() {} + void copy(int, int); + void pack_comm(int, int *, double *, int *); + void unpack_comm(int, int, double *); + void pack_reverse(int, int, double *); + void unpack_reverse(int, int *, double *); + void pack_border(int, int *, double *, int *); + void unpack_border(int, int, double *); + int pack_exchange(int, double *); + int unpack_exchange(double *); +}; + +#endif diff --git a/src/atom_atomic.cpp b/src/atom_atomic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..97f4488b452fab1508de6145494050cb89d9120a --- /dev/null +++ b/src/atom_atomic.cpp @@ -0,0 +1,218 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "atom_atomic.h" +#include "domain.h" +#include "modify.h" +#include "fix.h" + +/* ---------------------------------------------------------------------- */ + +AtomAtomic::AtomAtomic(int narg, char **arg) : Atom(narg, arg) {} + +/* ---------------------------------------------------------------------- */ + +void AtomAtomic::copy(int i, int j) +{ + 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]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + modify->fix[extra_grow[iextra]]->copy_arrays(i,j); +} + +/* ---------------------------------------------------------------------- */ + +void AtomAtomic::pack_comm(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomAtomic::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomAtomic::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]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomAtomic::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomAtomic::pack_border(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + buf[m++] = tag[j]; + buf[m++] = type[j]; + buf[m++] = mask[j]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomAtomic::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<int> (buf[m++]); + type[i] = static_cast<int> (buf[m++]); + mask[i] = static_cast<int> (buf[m++]); + } +} + +/* ---------------------------------------------------------------------- + pack all atom quantities for shipping to another proc + xyz must be 1st 3 values, so that comm::exchange can test on them +------------------------------------------------------------------------- */ + +int AtomAtomic::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++] = image[i]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->pack_exchange(i,&buf[m]); + + buf[0] = m; + return m; +} + +/* ---------------------------------------------------------------------- */ + +int AtomAtomic::unpack_exchange(double *buf) +{ + 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<int> (buf[m++]); + type[nlocal] = static_cast<int> (buf[m++]); + mask[nlocal] = static_cast<int> (buf[m++]); + image[nlocal] = static_cast<int> (buf[m++]); + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->unpack_exchange(nlocal,&buf[m]); + + nlocal++; + return m; +} diff --git a/src/atom_atomic.h b/src/atom_atomic.h new file mode 100644 index 0000000000000000000000000000000000000000..ba15feda921383cb7db2f47cebd4aa8f1827d173 --- /dev/null +++ b/src/atom_atomic.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ATOM_ATOMIC_H +#define ATOM_ATOMIC_H + +#include "atom.h" + +class AtomAtomic : public Atom { + public: + AtomAtomic(int, char **); + ~AtomAtomic() {} + void copy(int, int); + void pack_comm(int, int *, double *, int *); + void unpack_comm(int, int, double *); + void pack_reverse(int, int, double *); + void unpack_reverse(int, int *, double *); + void pack_border(int, int *, double *, int *); + void unpack_border(int, int, double *); + int pack_exchange(int, double *); + int unpack_exchange(double *); +}; + +#endif diff --git a/src/atom_bond.cpp b/src/atom_bond.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fe3c5fe82e80b5fd7c09ec7253b052da66aa3cac --- /dev/null +++ b/src/atom_bond.cpp @@ -0,0 +1,266 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "atom_bond.h" +#include "domain.h" +#include "modify.h" +#include "fix.h" + +/* ---------------------------------------------------------------------- */ + +AtomBond::AtomBond(int narg, char **arg) : Atom(narg, arg) {} + +/* ---------------------------------------------------------------------- */ + +void AtomBond::copy(int i, int j) +{ + int k; + + 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]; + + molecule[j] = molecule[i]; + num_bond[j] = num_bond[i]; + nspecial[j][0] = nspecial[i][0]; + nspecial[j][1] = nspecial[i][1]; + nspecial[j][2] = nspecial[i][2]; + + for (k = 0; k < num_bond[j]; k++) { + bond_type[j][k] = bond_type[i][k]; + bond_atom[j][k] = bond_atom[i][k]; + } + + for (k = 0; k < nspecial[j][2]; k++) special[j][k] = special[i][k]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + modify->fix[extra_grow[iextra]]->copy_arrays(i,j); +} + +/* ---------------------------------------------------------------------- */ + +void AtomBond::pack_comm(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomBond::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomBond::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]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomBond::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomBond::pack_border(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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++] = molecule[j]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + buf[m++] = tag[j]; + buf[m++] = type[j]; + buf[m++] = mask[j]; + buf[m++] = molecule[j]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomBond::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<int> (buf[m++]); + type[i] = static_cast<int> (buf[m++]); + mask[i] = static_cast<int> (buf[m++]); + molecule[i] = static_cast<int> (buf[m++]); + } +} + +/* ---------------------------------------------------------------------- + pack all atom quantities for shipping to another proc + xyz must be 1st 3 values, so that comm::exchange can test on them +------------------------------------------------------------------------- */ + +int AtomBond::pack_exchange(int i, double *buf) +{ + int k; + + 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++] = image[i]; + buf[m++] = v[i][0]; + buf[m++] = v[i][1]; + buf[m++] = v[i][2]; + + buf[m++] = molecule[i]; + + buf[m++] = num_bond[i]; + for (k = 0; k < num_bond[i]; k++) { + buf[m++] = bond_type[i][k]; + buf[m++] = bond_atom[i][k]; + } + + buf[m++] = nspecial[i][0]; + buf[m++] = nspecial[i][1]; + buf[m++] = nspecial[i][2]; + for (k = 0; k < nspecial[i][2]; k++) buf[m++] = special[i][k]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->pack_exchange(i,&buf[m]); + + buf[0] = m; + return m; +} + +/* ---------------------------------------------------------------------- */ + +int AtomBond::unpack_exchange(double *buf) +{ + int k; + if (nlocal == nmax) grow(0); + + int m = 1; + x[nlocal][0] = buf[m++]; + x[nlocal][1] = buf[m++]; + x[nlocal][2] = buf[m++]; + tag[nlocal] = static_cast<int> (buf[m++]); + type[nlocal] = static_cast<int> (buf[m++]); + mask[nlocal] = static_cast<int> (buf[m++]); + image[nlocal] = static_cast<int> (buf[m++]); + v[nlocal][0] = buf[m++]; + v[nlocal][1] = buf[m++]; + v[nlocal][2] = buf[m++]; + + molecule[nlocal] = static_cast<int> (buf[m++]); + + num_bond[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_bond[nlocal]; k++) { + bond_type[nlocal][k] = static_cast<int> (buf[m++]); + bond_atom[nlocal][k] = static_cast<int> (buf[m++]); + } + + nspecial[nlocal][0] = static_cast<int> (buf[m++]); + nspecial[nlocal][1] = static_cast<int> (buf[m++]); + nspecial[nlocal][2] = static_cast<int> (buf[m++]); + for (k = 0; k < nspecial[nlocal][2]; k++) + special[nlocal][k] = static_cast<int> (buf[m++]); + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->unpack_exchange(nlocal,&buf[m]); + + nlocal++; + return m; +} diff --git a/src/atom_bond.h b/src/atom_bond.h new file mode 100644 index 0000000000000000000000000000000000000000..f44f7d073f44631a834fbadadfb18adc6dcb903f --- /dev/null +++ b/src/atom_bond.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ATOM_BOND_H +#define ATOM_BOND_H + +#include "atom.h" + +class AtomBond : public Atom { + public: + AtomBond(int, char **); + ~AtomBond() {} + void copy(int, int); + void pack_comm(int, int *, double *, int *); + void unpack_comm(int, int, double *); + void pack_reverse(int, int, double *); + void unpack_reverse(int, int *, double *); + void pack_border(int, int *, double *, int *); + void unpack_border(int, int, double *); + int pack_exchange(int, double *); + int unpack_exchange(double *); +}; + +#endif diff --git a/src/atom_charge.cpp b/src/atom_charge.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7b6a0a3ea5a7e59756ec7ccd98f81ae018c8f95b --- /dev/null +++ b/src/atom_charge.cpp @@ -0,0 +1,227 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "atom_charge.h" +#include "domain.h" +#include "modify.h" +#include "fix.h" + +/* ---------------------------------------------------------------------- */ + +AtomCharge::AtomCharge(int narg, char **arg) : Atom(narg, arg) {} + +/* ---------------------------------------------------------------------- */ + +void AtomCharge::copy(int i, int j) +{ + 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]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + modify->fix[extra_grow[iextra]]->copy_arrays(i,j); +} + +/* ---------------------------------------------------------------------- */ + +void AtomCharge::pack_comm(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomCharge::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomCharge::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]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomCharge::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomCharge::pack_border(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + buf[m++] = tag[j]; + buf[m++] = type[j]; + buf[m++] = mask[j]; + buf[m++] = q[j]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomCharge::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<int> (buf[m++]); + type[i] = static_cast<int> (buf[m++]); + mask[i] = static_cast<int> (buf[m++]); + q[i] = buf[m++]; + } +} + +/* ---------------------------------------------------------------------- + pack all atom quantities for shipping to another proc + xyz must be 1st 3 values, so that comm::exchange can test on them +------------------------------------------------------------------------- */ + +int AtomCharge::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++] = tag[i]; + buf[m++] = type[i]; + buf[m++] = mask[i]; + buf[m++] = image[i]; + buf[m++] = v[i][0]; + buf[m++] = v[i][1]; + buf[m++] = v[i][2]; + + buf[m++] = q[i]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->pack_exchange(i,&buf[m]); + + buf[0] = m; + return m; +} + +/* ---------------------------------------------------------------------- */ + +int AtomCharge::unpack_exchange(double *buf) +{ + if (nlocal == nmax) grow(0); + + int m = 1; + x[nlocal][0] = buf[m++]; + x[nlocal][1] = buf[m++]; + x[nlocal][2] = buf[m++]; + tag[nlocal] = static_cast<int> (buf[m++]); + type[nlocal] = static_cast<int> (buf[m++]); + mask[nlocal] = static_cast<int> (buf[m++]); + image[nlocal] = static_cast<int> (buf[m++]); + v[nlocal][0] = buf[m++]; + v[nlocal][1] = buf[m++]; + v[nlocal][2] = buf[m++]; + + q[nlocal] = buf[m++]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->unpack_exchange(nlocal,&buf[m]); + + nlocal++; + return m; +} diff --git a/src/atom_charge.h b/src/atom_charge.h new file mode 100644 index 0000000000000000000000000000000000000000..46b9848c987b7fe7c825c4c2e7df78cbdfe5a439 --- /dev/null +++ b/src/atom_charge.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ATOM_CHARGE_H +#define ATOM_CHARGE_H + +#include "atom.h" + +class AtomCharge : public Atom { + public: + AtomCharge(int, char **); + ~AtomCharge() {} + void copy(int, int); + void pack_comm(int, int *, double *, int *); + void unpack_comm(int, int, double *); + void pack_reverse(int, int, double *); + void unpack_reverse(int, int *, double *); + void pack_border(int, int *, double *, int *); + void unpack_border(int, int, double *); + int pack_exchange(int, double *); + int unpack_exchange(double *); +}; + +#endif diff --git a/src/atom_full.cpp b/src/atom_full.cpp new file mode 100644 index 0000000000000000000000000000000000000000..935108e770aace516e52b7ca4606cf3078caefda --- /dev/null +++ b/src/atom_full.cpp @@ -0,0 +1,353 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "atom_full.h" +#include "domain.h" +#include "modify.h" +#include "fix.h" + +/* ---------------------------------------------------------------------- */ + +AtomFull::AtomFull(int narg, char **arg) : Atom(narg, arg) {} + +/* ---------------------------------------------------------------------- */ + +void AtomFull::copy(int i, int j) +{ + int k; + + 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]; + + molecule[j] = molecule[i]; + num_bond[j] = num_bond[i]; + num_angle[j] = num_angle[i]; + num_dihedral[j] = num_dihedral[i]; + num_improper[j] = num_improper[i]; + nspecial[j][0] = nspecial[i][0]; + nspecial[j][1] = nspecial[i][1]; + nspecial[j][2] = nspecial[i][2]; + + for (k = 0; k < num_bond[j]; k++) { + bond_type[j][k] = bond_type[i][k]; + bond_atom[j][k] = bond_atom[i][k]; + } + + for (k = 0; k < num_angle[j]; k++) { + angle_type[j][k] = angle_type[i][k]; + angle_atom1[j][k] = angle_atom1[i][k]; + angle_atom2[j][k] = angle_atom2[i][k]; + angle_atom3[j][k] = angle_atom3[i][k]; + } + + for (k = 0; k < num_dihedral[j]; k++) { + dihedral_type[j][k] = dihedral_type[i][k]; + dihedral_atom1[j][k] = dihedral_atom1[i][k]; + dihedral_atom2[j][k] = dihedral_atom2[i][k]; + dihedral_atom3[j][k] = dihedral_atom3[i][k]; + dihedral_atom4[j][k] = dihedral_atom4[i][k]; + } + + for (k = 0; k < num_improper[j]; k++) { + improper_type[j][k] = improper_type[i][k]; + improper_atom1[j][k] = improper_atom1[i][k]; + improper_atom2[j][k] = improper_atom2[i][k]; + improper_atom3[j][k] = improper_atom3[i][k]; + improper_atom4[j][k] = improper_atom4[i][k]; + } + + for (k = 0; k < nspecial[j][2]; k++) special[j][k] = special[i][k]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + modify->fix[extra_grow[iextra]]->copy_arrays(i,j); +} + +/* ---------------------------------------------------------------------- */ + +void AtomFull::pack_comm(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomFull::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomFull::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]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomFull::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomFull::pack_border(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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++] = molecule[j]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + buf[m++] = tag[j]; + buf[m++] = type[j]; + buf[m++] = mask[j]; + buf[m++] = q[j]; + buf[m++] = molecule[j]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomFull::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<int> (buf[m++]); + type[i] = static_cast<int> (buf[m++]); + mask[i] = static_cast<int> (buf[m++]); + q[i] = buf[m++]; + molecule[i] = static_cast<int> (buf[m++]); + } +} + +/* ---------------------------------------------------------------------- + pack all atom quantities for shipping to another proc + xyz must be 1st 3 values, so that comm::exchange can test on them +------------------------------------------------------------------------- */ + +int AtomFull::pack_exchange(int i, double *buf) +{ + int k; + + 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++] = image[i]; + buf[m++] = v[i][0]; + buf[m++] = v[i][1]; + buf[m++] = v[i][2]; + + buf[m++] = q[i]; + + buf[m++] = molecule[i]; + + buf[m++] = num_bond[i]; + for (k = 0; k < num_bond[i]; k++) { + buf[m++] = bond_type[i][k]; + buf[m++] = bond_atom[i][k]; + } + + buf[m++] = num_angle[i]; + for (k = 0; k < num_angle[i]; k++) { + buf[m++] = angle_type[i][k]; + buf[m++] = angle_atom1[i][k]; + buf[m++] = angle_atom2[i][k]; + buf[m++] = angle_atom3[i][k]; + } + + buf[m++] = num_dihedral[i]; + for (k = 0; k < num_dihedral[i]; k++) { + buf[m++] = dihedral_type[i][k]; + buf[m++] = dihedral_atom1[i][k]; + buf[m++] = dihedral_atom2[i][k]; + buf[m++] = dihedral_atom3[i][k]; + buf[m++] = dihedral_atom4[i][k]; + } + + buf[m++] = num_improper[i]; + for (k = 0; k < num_improper[i]; k++) { + buf[m++] = improper_type[i][k]; + buf[m++] = improper_atom1[i][k]; + buf[m++] = improper_atom2[i][k]; + buf[m++] = improper_atom3[i][k]; + buf[m++] = improper_atom4[i][k]; + } + + buf[m++] = nspecial[i][0]; + buf[m++] = nspecial[i][1]; + buf[m++] = nspecial[i][2]; + for (k = 0; k < nspecial[i][2]; k++) buf[m++] = special[i][k]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->pack_exchange(i,&buf[m]); + + buf[0] = m; + return m; +} + +/* ---------------------------------------------------------------------- */ + +int AtomFull::unpack_exchange(double *buf) +{ + int k; + if (nlocal == nmax) grow(0); + + int m = 1; + x[nlocal][0] = buf[m++]; + x[nlocal][1] = buf[m++]; + x[nlocal][2] = buf[m++]; + tag[nlocal] = static_cast<int> (buf[m++]); + type[nlocal] = static_cast<int> (buf[m++]); + mask[nlocal] = static_cast<int> (buf[m++]); + image[nlocal] = static_cast<int> (buf[m++]); + v[nlocal][0] = buf[m++]; + v[nlocal][1] = buf[m++]; + v[nlocal][2] = buf[m++]; + + q[nlocal] = buf[m++]; + + molecule[nlocal] = static_cast<int> (buf[m++]); + + num_bond[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_bond[nlocal]; k++) { + bond_type[nlocal][k] = static_cast<int> (buf[m++]); + bond_atom[nlocal][k] = static_cast<int> (buf[m++]); + } + + num_angle[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_angle[nlocal]; k++) { + angle_type[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom1[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom2[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom3[nlocal][k] = static_cast<int> (buf[m++]); + } + + num_dihedral[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_dihedral[nlocal]; k++) { + dihedral_type[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom1[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom2[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom3[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom4[nlocal][k] = static_cast<int> (buf[m++]); + } + + num_improper[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_improper[nlocal]; k++) { + improper_type[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom1[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom2[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom3[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom4[nlocal][k] = static_cast<int> (buf[m++]); + } + + nspecial[nlocal][0] = static_cast<int> (buf[m++]); + nspecial[nlocal][1] = static_cast<int> (buf[m++]); + nspecial[nlocal][2] = static_cast<int> (buf[m++]); + for (k = 0; k < nspecial[nlocal][2]; k++) + special[nlocal][k] = static_cast<int> (buf[m++]); + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->unpack_exchange(nlocal,&buf[m]); + + nlocal++; + return m; +} diff --git a/src/atom_full.h b/src/atom_full.h new file mode 100644 index 0000000000000000000000000000000000000000..71c5a806db57840de0f4a9a2e141ba4cd3a3aa6c --- /dev/null +++ b/src/atom_full.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ATOM_FULL_H +#define ATOM_FULL_H + +#include "atom.h" + +class AtomFull : public Atom { + public: + AtomFull(int, char **); + ~AtomFull() {} + void copy(int, int); + void pack_comm(int, int *, double *, int *); + void unpack_comm(int, int, double *); + void pack_reverse(int, int, double *); + void unpack_reverse(int, int *, double *); + void pack_border(int, int *, double *, int *); + void unpack_border(int, int, double *); + int pack_exchange(int, double *); + int unpack_exchange(double *); +}; + +#endif diff --git a/src/atom_hybrid.cpp b/src/atom_hybrid.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2520632d00d6f6ee1e59b0472d67d61942826901 --- /dev/null +++ b/src/atom_hybrid.cpp @@ -0,0 +1,584 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "atom_hybrid.h" +#include "domain.h" +#include "modify.h" +#include "fix.h" + +/* ---------------------------------------------------------------------- */ + +AtomHybrid::AtomHybrid(int narg, char **arg) : Atom(narg, arg) {} + +/* ---------------------------------------------------------------------- */ + +void AtomHybrid::copy(int i, int j) +{ + int k; + + 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]; + + if (charge_allow) q[j] = q[i]; + if (mass_allow) rmass[j] = rmass[i]; + + if (style_dipole) { + mu[j][0] = mu[i][0]; + mu[j][1] = mu[i][1]; + mu[j][2] = mu[i][2]; + omega[j][0] = omega[i][0]; + omega[j][1] = omega[i][1]; + omega[j][2] = omega[i][2]; + } + + if (style_granular) { + phix[j][0] = phix[i][0]; + phix[j][1] = phix[i][1]; + phix[j][2] = phix[i][2]; + phiv[j][0] = phiv[i][0]; + phiv[j][1] = phiv[i][1]; + phiv[j][2] = phiv[i][2]; + radius[j] = radius[i]; + density[j] = density[i]; + } + + if (style_peri) vfrac[j] = vfrac[i]; + + if (molecular) { + molecule[j] = molecule[i]; + nspecial[j][0] = nspecial[i][0]; + nspecial[j][1] = nspecial[i][1]; + nspecial[j][2] = nspecial[i][2]; + for (k = 0; k < nspecial[j][2]; k++) special[j][k] = special[i][k]; + + if (bonds_allow) { + num_bond[j] = num_bond[i]; + for (k = 0; k < num_bond[j]; k++) { + bond_type[j][k] = bond_type[i][k]; + bond_atom[j][k] = bond_atom[i][k]; + } + } + + if (angles_allow) { + num_angle[j] = num_angle[i]; + for (k = 0; k < num_angle[j]; k++) { + angle_type[j][k] = angle_type[i][k]; + angle_atom1[j][k] = angle_atom1[i][k]; + angle_atom2[j][k] = angle_atom2[i][k]; + angle_atom3[j][k] = angle_atom3[i][k]; + } + } + + if (dihedrals_allow) { + num_dihedral[j] = num_dihedral[i]; + for (k = 0; k < num_dihedral[j]; k++) { + dihedral_type[j][k] = dihedral_type[i][k]; + dihedral_atom1[j][k] = dihedral_atom1[i][k]; + dihedral_atom2[j][k] = dihedral_atom2[i][k]; + dihedral_atom3[j][k] = dihedral_atom3[i][k]; + dihedral_atom4[j][k] = dihedral_atom4[i][k]; + } + } + + if (impropers_allow) { + num_improper[j] = num_improper[i]; + for (k = 0; k < num_improper[j]; k++) { + improper_type[j][k] = improper_type[i][k]; + improper_atom1[j][k] = improper_atom1[i][k]; + improper_atom2[j][k] = improper_atom2[i][k]; + improper_atom3[j][k] = improper_atom3[i][k]; + improper_atom4[j][k] = improper_atom4[i][k]; + } + } + } + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + modify->fix[extra_grow[iextra]]->copy_arrays(i,j); +} + +/* ---------------------------------------------------------------------- */ + +void AtomHybrid::pack_comm(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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]; + if (style_dpd) { + buf[m++] = v[j][0]; + buf[m++] = v[j][1]; + buf[m++] = v[j][2]; + } + if (style_dipole) { + buf[m++] = mu[j][0]; + buf[m++] = mu[j][1]; + buf[m++] = mu[j][2]; + } + if (style_granular) { + buf[m++] = v[j][0]; + buf[m++] = v[j][1]; + buf[m++] = v[j][2]; + buf[m++] = phiv[j][0]; + buf[m++] = phiv[j][1]; + buf[m++] = phiv[j][2]; + } + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + if (style_dpd) { + buf[m++] = v[j][0]; + buf[m++] = v[j][1]; + buf[m++] = v[j][2]; + } + if (style_dipole) { + buf[m++] = mu[j][0]; + buf[m++] = mu[j][1]; + buf[m++] = mu[j][2]; + } + if (style_granular) { + buf[m++] = v[j][0]; + buf[m++] = v[j][1]; + buf[m++] = v[j][2]; + buf[m++] = phiv[j][0]; + buf[m++] = phiv[j][1]; + buf[m++] = phiv[j][2]; + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomHybrid::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++]; + if (style_dpd) { + v[i][0] = buf[m++]; + v[i][1] = buf[m++]; + v[i][2] = buf[m++]; + } + if (style_dipole) { + mu[i][0] = buf[m++]; + mu[i][1] = buf[m++]; + mu[i][2] = buf[m++]; + } + if (style_granular) { + v[i][0] = buf[m++]; + v[i][1] = buf[m++]; + v[i][2] = buf[m++]; + phiv[i][0] = buf[m++]; + phiv[i][1] = buf[m++]; + phiv[i][2] = buf[m++]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomHybrid::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]; + if (style_dipole) { + buf[m++] = torque[i][0]; + buf[m++] = torque[i][1]; + buf[m++] = torque[i][2]; + } + if (style_granular) { + buf[m++] = phia[i][0]; + buf[m++] = phia[i][1]; + buf[m++] = phia[i][2]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomHybrid::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++]; + if (style_dipole) { + torque[j][0] += buf[m++]; + torque[j][1] += buf[m++]; + torque[j][2] += buf[m++]; + } + if (style_granular) { + phia[j][0] += buf[m++]; + phia[j][1] += buf[m++]; + phia[j][2] += buf[m++]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomHybrid::pack_border(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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]; + if (charge_allow) buf[m++] = q[j]; + if (style_dpd) { + buf[m++] = v[j][0]; + buf[m++] = v[j][1]; + buf[m++] = v[j][2]; + } + if (style_dipole) { + buf[m++] = mu[j][0]; + buf[m++] = mu[j][1]; + buf[m++] = mu[j][2]; + } + if (style_granular) { + buf[m++] = v[j][0]; + buf[m++] = v[j][1]; + buf[m++] = v[j][2]; + buf[m++] = phiv[j][0]; + buf[m++] = phiv[j][1]; + buf[m++] = phiv[j][2]; + buf[m++] = radius[j]; + buf[m++] = rmass[j]; + } + if (molecular) buf[m++] = molecule[j]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + buf[m++] = tag[j]; + buf[m++] = type[j]; + buf[m++] = mask[j]; + if (charge_allow) buf[m++] = q[j]; + if (style_dpd) { + buf[m++] = v[j][0]; + buf[m++] = v[j][1]; + buf[m++] = v[j][2]; + } + if (style_dipole) { + buf[m++] = mu[j][0]; + buf[m++] = mu[j][1]; + buf[m++] = mu[j][2]; + } + if (style_granular) { + buf[m++] = v[j][0]; + buf[m++] = v[j][1]; + buf[m++] = v[j][2]; + buf[m++] = phiv[j][0]; + buf[m++] = phiv[j][1]; + buf[m++] = phiv[j][2]; + buf[m++] = radius[j]; + buf[m++] = rmass[j]; + } + if (molecular) buf[m++] = molecule[j]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomHybrid::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<int> (buf[m++]); + type[i] = static_cast<int> (buf[m++]); + mask[i] = static_cast<int> (buf[m++]); + if (charge_allow) q[i] = buf[m++]; + if (style_dpd) { + v[i][0] = buf[m++]; + v[i][1] = buf[m++]; + v[i][2] = buf[m++]; + } + if (style_dipole) { + mu[i][0] = buf[m++]; + mu[i][1] = buf[m++]; + mu[i][2] = buf[m++]; + } + if (style_granular) { + v[i][0] = buf[m++]; + v[i][1] = buf[m++]; + v[i][2] = buf[m++]; + phiv[i][0] = buf[m++]; + phiv[i][1] = buf[m++]; + phiv[i][2] = buf[m++]; + radius[i] = buf[m++]; + rmass[i] = buf[m++]; + } + if (molecular) molecule[i] = static_cast<int> (buf[m++]); + } +} + +/* ---------------------------------------------------------------------- + pack all atom quantities for shipping to another proc + xyz must be 1st 3 values, so that comm::exchange can test on them +------------------------------------------------------------------------- */ + +int AtomHybrid::pack_exchange(int i, double *buf) +{ + int k; + + 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++] = image[i]; + buf[m++] = v[i][0]; + buf[m++] = v[i][1]; + buf[m++] = v[i][2]; + + if (charge_allow) buf[m++] = q[i]; + if (mass_allow) buf[m++] = rmass[i]; + + if (style_dipole) { + buf[m++] = mu[i][0]; + buf[m++] = mu[i][1]; + buf[m++] = mu[i][2]; + buf[m++] = omega[i][0]; + buf[m++] = omega[i][1]; + buf[m++] = omega[i][2]; + } + + if (style_granular) { + buf[m++] = phix[i][0]; + buf[m++] = phix[i][1]; + buf[m++] = phix[i][2]; + buf[m++] = phiv[i][0]; + buf[m++] = phiv[i][1]; + buf[m++] = phiv[i][2]; + buf[m++] = radius[i]; + buf[m++] = density[i]; + } + + if (style_peri) buf[m++] = vfrac[i]; + + if (molecular) { + buf[m++] = molecule[i]; + buf[m++] = nspecial[i][0]; + buf[m++] = nspecial[i][1]; + buf[m++] = nspecial[i][2]; + for (k = 0; k < nspecial[i][2]; k++) buf[m++] = special[i][k]; + + if (bonds_allow) { + buf[m++] = num_bond[i]; + for (k = 0; k < num_bond[i]; k++) { + buf[m++] = bond_type[i][k]; + buf[m++] = bond_atom[i][k]; + } + } + + if (angles_allow) { + buf[m++] = num_angle[i]; + for (k = 0; k < num_angle[i]; k++) { + buf[m++] = angle_type[i][k]; + buf[m++] = angle_atom1[i][k]; + buf[m++] = angle_atom2[i][k]; + buf[m++] = angle_atom3[i][k]; + } + } + + if (dihedrals_allow) { + buf[m++] = num_dihedral[i]; + for (k = 0; k < num_dihedral[i]; k++) { + buf[m++] = dihedral_type[i][k]; + buf[m++] = dihedral_atom1[i][k]; + buf[m++] = dihedral_atom2[i][k]; + buf[m++] = dihedral_atom3[i][k]; + buf[m++] = dihedral_atom4[i][k]; + } + } + + if (impropers_allow) { + buf[m++] = num_improper[i]; + for (k = 0; k < num_improper[i]; k++) { + buf[m++] = improper_type[i][k]; + buf[m++] = improper_atom1[i][k]; + buf[m++] = improper_atom2[i][k]; + buf[m++] = improper_atom3[i][k]; + buf[m++] = improper_atom4[i][k]; + } + } + } + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->pack_exchange(i,&buf[m]); + + buf[0] = m; + return m; +} + +/* ---------------------------------------------------------------------- */ + +int AtomHybrid::unpack_exchange(double *buf) +{ + int k; + if (nlocal == nmax) grow(0); + + int m = 1; + x[nlocal][0] = buf[m++]; + x[nlocal][1] = buf[m++]; + x[nlocal][2] = buf[m++]; + tag[nlocal] = static_cast<int> (buf[m++]); + type[nlocal] = static_cast<int> (buf[m++]); + mask[nlocal] = static_cast<int> (buf[m++]); + image[nlocal] = static_cast<int> (buf[m++]); + v[nlocal][0] = buf[m++]; + v[nlocal][1] = buf[m++]; + v[nlocal][2] = buf[m++]; + + if (charge_allow) q[nlocal] = buf[m++]; + if (mass_allow) rmass[nlocal] = buf[m++]; + + if (style_dipole) { + mu[nlocal][0] = buf[m++]; + mu[nlocal][1] = buf[m++]; + mu[nlocal][2] = buf[m++]; + omega[nlocal][0] = buf[m++]; + omega[nlocal][1] = buf[m++]; + omega[nlocal][2] = buf[m++]; + } + + if (style_granular) { + phix[nlocal][0] = buf[m++]; + phix[nlocal][1] = buf[m++]; + phix[nlocal][2] = buf[m++]; + phiv[nlocal][0] = buf[m++]; + phiv[nlocal][1] = buf[m++]; + phiv[nlocal][2] = buf[m++]; + radius[nlocal] = buf[m++]; + density[nlocal] = buf[m++]; + rmass[nlocal] = buf[m++]; + } + + if (style_peri) vfrac[nlocal] = buf[m++]; + + if (molecular) { + molecule[nlocal] = static_cast<int> (buf[m++]); + nspecial[nlocal][0] = static_cast<int> (buf[m++]); + nspecial[nlocal][1] = static_cast<int> (buf[m++]); + nspecial[nlocal][2] = static_cast<int> (buf[m++]); + for (k = 0; k < nspecial[nlocal][2]; k++) + special[nlocal][k] = static_cast<int> (buf[m++]); + + if (bonds_allow) { + num_bond[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_bond[nlocal]; k++) { + bond_type[nlocal][k] = static_cast<int> (buf[m++]); + bond_atom[nlocal][k] = static_cast<int> (buf[m++]); + } + } + + if (angles_allow) { + num_angle[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_angle[nlocal]; k++) { + angle_type[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom1[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom2[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom3[nlocal][k] = static_cast<int> (buf[m++]); + } + } + + if (dihedrals_allow) { + num_dihedral[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_dihedral[nlocal]; k++) { + dihedral_type[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom1[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom2[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom3[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom4[nlocal][k] = static_cast<int> (buf[m++]); + } + } + + if (impropers_allow) { + num_improper[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_improper[nlocal]; k++) { + improper_type[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom1[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom2[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom3[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom4[nlocal][k] = static_cast<int> (buf[m++]); + } + } + } + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->unpack_exchange(nlocal,&buf[m]); + + nlocal++; + return m; +} diff --git a/src/atom_hybrid.h b/src/atom_hybrid.h new file mode 100644 index 0000000000000000000000000000000000000000..7180cb424be47f738b941a524e6c2770f224c67a --- /dev/null +++ b/src/atom_hybrid.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ATOM_HYBRID_H +#define ATOM_HYBRID_H + +#include "atom.h" + +class AtomHybrid : public Atom { + public: + AtomHybrid(int, char **); + ~AtomHybrid() {} + void copy(int, int); + void pack_comm(int, int *, double *, int *); + void unpack_comm(int, int, double *); + void pack_reverse(int, int, double *); + void unpack_reverse(int, int *, double *); + void pack_border(int, int *, double *, int *); + void unpack_border(int, int, double *); + int pack_exchange(int, double *); + int unpack_exchange(double *); +}; + +#endif diff --git a/src/atom_molecular.cpp b/src/atom_molecular.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5c180619308803cd4bf205b3eea3a12cb443fab8 --- /dev/null +++ b/src/atom_molecular.cpp @@ -0,0 +1,344 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "atom_molecular.h" +#include "domain.h" +#include "modify.h" +#include "fix.h" + +/* ---------------------------------------------------------------------- */ + +AtomMolecular::AtomMolecular(int narg, char **arg) : Atom(narg, arg) {} + +/* ---------------------------------------------------------------------- */ + +void AtomMolecular::copy(int i, int j) +{ + int k; + + 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]; + + molecule[j] = molecule[i]; + num_bond[j] = num_bond[i]; + num_angle[j] = num_angle[i]; + num_dihedral[j] = num_dihedral[i]; + num_improper[j] = num_improper[i]; + nspecial[j][0] = nspecial[i][0]; + nspecial[j][1] = nspecial[i][1]; + nspecial[j][2] = nspecial[i][2]; + + for (k = 0; k < num_bond[j]; k++) { + bond_type[j][k] = bond_type[i][k]; + bond_atom[j][k] = bond_atom[i][k]; + } + + for (k = 0; k < num_angle[j]; k++) { + angle_type[j][k] = angle_type[i][k]; + angle_atom1[j][k] = angle_atom1[i][k]; + angle_atom2[j][k] = angle_atom2[i][k]; + angle_atom3[j][k] = angle_atom3[i][k]; + } + + for (k = 0; k < num_dihedral[j]; k++) { + dihedral_type[j][k] = dihedral_type[i][k]; + dihedral_atom1[j][k] = dihedral_atom1[i][k]; + dihedral_atom2[j][k] = dihedral_atom2[i][k]; + dihedral_atom3[j][k] = dihedral_atom3[i][k]; + dihedral_atom4[j][k] = dihedral_atom4[i][k]; + } + + for (k = 0; k < num_improper[j]; k++) { + improper_type[j][k] = improper_type[i][k]; + improper_atom1[j][k] = improper_atom1[i][k]; + improper_atom2[j][k] = improper_atom2[i][k]; + improper_atom3[j][k] = improper_atom3[i][k]; + improper_atom4[j][k] = improper_atom4[i][k]; + } + + for (k = 0; k < nspecial[j][2]; k++) special[j][k] = special[i][k]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + modify->fix[extra_grow[iextra]]->copy_arrays(i,j); +} + +/* ---------------------------------------------------------------------- */ + +void AtomMolecular::pack_comm(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomMolecular::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomMolecular::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]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomMolecular::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++]; + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomMolecular::pack_border(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 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++] = molecule[j]; + } + } else { + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = x[j][0] + pbc_flags[1]*xprd; + buf[m++] = x[j][1] + pbc_flags[2]*yprd; + buf[m++] = x[j][2] + pbc_flags[3]*zprd; + buf[m++] = tag[j]; + buf[m++] = type[j]; + buf[m++] = mask[j]; + buf[m++] = molecule[j]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void AtomMolecular::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<int> (buf[m++]); + type[i] = static_cast<int> (buf[m++]); + mask[i] = static_cast<int> (buf[m++]); + molecule[i] = static_cast<int> (buf[m++]); + } +} + +/* ---------------------------------------------------------------------- + pack all atom quantities for shipping to another proc + xyz must be 1st 3 values, so that comm::exchange can test on them +------------------------------------------------------------------------- */ + +int AtomMolecular::pack_exchange(int i, double *buf) +{ + int k; + + 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++] = image[i]; + buf[m++] = v[i][0]; + buf[m++] = v[i][1]; + buf[m++] = v[i][2]; + + buf[m++] = molecule[i]; + + buf[m++] = num_bond[i]; + for (k = 0; k < num_bond[i]; k++) { + buf[m++] = bond_type[i][k]; + buf[m++] = bond_atom[i][k]; + } + + buf[m++] = num_angle[i]; + for (k = 0; k < num_angle[i]; k++) { + buf[m++] = angle_type[i][k]; + buf[m++] = angle_atom1[i][k]; + buf[m++] = angle_atom2[i][k]; + buf[m++] = angle_atom3[i][k]; + } + + buf[m++] = num_dihedral[i]; + for (k = 0; k < num_dihedral[i]; k++) { + buf[m++] = dihedral_type[i][k]; + buf[m++] = dihedral_atom1[i][k]; + buf[m++] = dihedral_atom2[i][k]; + buf[m++] = dihedral_atom3[i][k]; + buf[m++] = dihedral_atom4[i][k]; + } + + buf[m++] = num_improper[i]; + for (k = 0; k < num_improper[i]; k++) { + buf[m++] = improper_type[i][k]; + buf[m++] = improper_atom1[i][k]; + buf[m++] = improper_atom2[i][k]; + buf[m++] = improper_atom3[i][k]; + buf[m++] = improper_atom4[i][k]; + } + + buf[m++] = nspecial[i][0]; + buf[m++] = nspecial[i][1]; + buf[m++] = nspecial[i][2]; + for (k = 0; k < nspecial[i][2]; k++) buf[m++] = special[i][k]; + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->pack_exchange(i,&buf[m]); + + buf[0] = m; + return m; +} + +/* ---------------------------------------------------------------------- */ + +int AtomMolecular::unpack_exchange(double *buf) +{ + int k; + if (nlocal == nmax) grow(0); + + int m = 1; + x[nlocal][0] = buf[m++]; + x[nlocal][1] = buf[m++]; + x[nlocal][2] = buf[m++]; + tag[nlocal] = static_cast<int> (buf[m++]); + type[nlocal] = static_cast<int> (buf[m++]); + mask[nlocal] = static_cast<int> (buf[m++]); + image[nlocal] = static_cast<int> (buf[m++]); + v[nlocal][0] = buf[m++]; + v[nlocal][1] = buf[m++]; + v[nlocal][2] = buf[m++]; + + molecule[nlocal] = static_cast<int> (buf[m++]); + + num_bond[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_bond[nlocal]; k++) { + bond_type[nlocal][k] = static_cast<int> (buf[m++]); + bond_atom[nlocal][k] = static_cast<int> (buf[m++]); + } + + num_angle[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_angle[nlocal]; k++) { + angle_type[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom1[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom2[nlocal][k] = static_cast<int> (buf[m++]); + angle_atom3[nlocal][k] = static_cast<int> (buf[m++]); + } + + num_dihedral[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_dihedral[nlocal]; k++) { + dihedral_type[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom1[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom2[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom3[nlocal][k] = static_cast<int> (buf[m++]); + dihedral_atom4[nlocal][k] = static_cast<int> (buf[m++]); + } + + num_improper[nlocal] = static_cast<int> (buf[m++]); + for (k = 0; k < num_improper[nlocal]; k++) { + improper_type[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom1[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom2[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom3[nlocal][k] = static_cast<int> (buf[m++]); + improper_atom4[nlocal][k] = static_cast<int> (buf[m++]); + } + + nspecial[nlocal][0] = static_cast<int> (buf[m++]); + nspecial[nlocal][1] = static_cast<int> (buf[m++]); + nspecial[nlocal][2] = static_cast<int> (buf[m++]); + for (k = 0; k < nspecial[nlocal][2]; k++) + special[nlocal][k] = static_cast<int> (buf[m++]); + + if (nextra_grow) + for (int iextra = 0; iextra < nextra_grow; iextra++) + m += modify->fix[extra_grow[iextra]]->unpack_exchange(nlocal,&buf[m]); + + nlocal++; + return m; +} diff --git a/src/atom_molecular.h b/src/atom_molecular.h new file mode 100644 index 0000000000000000000000000000000000000000..f31caf8926453f82c8c47574f52279831e686198 --- /dev/null +++ b/src/atom_molecular.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ATOM_MOLECULAR_H +#define ATOM_MOLECULAR_H + +#include "atom.h" + +class AtomMolecular : public Atom { + public: + AtomMolecular(int, char **); + ~AtomMolecular() {} + void copy(int, int); + void pack_comm(int, int *, double *, int *); + void unpack_comm(int, int, double *); + void pack_reverse(int, int, double *); + void unpack_reverse(int, int *, double *); + void pack_border(int, int *, double *, int *); + void unpack_border(int, int, double *); + int pack_exchange(int, double *); + int unpack_exchange(double *); +}; + +#endif diff --git a/src/bond.cpp b/src/bond.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c12665806306e88175edc2b99c120ad826d77213 --- /dev/null +++ b/src/bond.cpp @@ -0,0 +1,40 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "bond.h" +#include "atom.h" +#include "error.h" + +/* ----------------------------------------------------------------------- + set bond contribution to Vdwl energy to 0.0 + a particular bond style can override this +------------------------------------------------------------------------- */ + +Bond::Bond() +{ + allocated = 0; + eng_vdwl = 0.0; +} + +/* ---------------------------------------------------------------------- + check if all coeffs are set +------------------------------------------------------------------------- */ + +void Bond::init() +{ + if (!allocated) error->all("Bond coeffs are not set"); + for (int i = 1; i <= atom->nbondtypes; i++) + if (setflag[i] == 0) error->all("All bond coeffs are not set"); + init_style(); +} diff --git a/src/bond.h b/src/bond.h new file mode 100644 index 0000000000000000000000000000000000000000..6d75b0d512ce600c495512b6e8950621938e10ea --- /dev/null +++ b/src/bond.h @@ -0,0 +1,44 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef BOND_H +#define BOND_H + +#include "stdio.h" +#include "lammps.h" + +class Bond : public LAMMPS { + public: + int allocated; + int *setflag; + double energy; + double eng_vdwl; + double virial[6]; + + Bond(); + virtual ~Bond() {} + virtual void init(); + virtual void init_style() {} + + virtual void compute(int, int) = 0; + virtual void settings(int, char **) {} + virtual void coeff(int, char **) = 0; + virtual double equilibrium_distance(int) = 0; + virtual void write_restart(FILE *) = 0; + virtual void read_restart(FILE *) = 0; + virtual void single(int, double, int, int, + double, int, double &, double &) = 0; + virtual int memory_usage() {return 0;} +}; + +#endif diff --git a/src/bond_fene.cpp b/src/bond_fene.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0c516341a43ce101513a26945994dc27c7296e9d --- /dev/null +++ b/src/bond_fene.cpp @@ -0,0 +1,272 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "bond_fene.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "update.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +BondFENE::BondFENE() +{ + TWO_1_3 = pow(2.0,(1.0/3.0)); +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +BondFENE::~BondFENE() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(r0); + memory->sfree(epsilon); + memory->sfree(sigma); + } +} + +/* ---------------------------------------------------------------------- */ + +void BondFENE::compute(int eflag, int vflag) +{ + int i1,i2,n,type,factor; + double delx,dely,delz,rsq,r0sq,rlogarg,fforce,sr2,sr6,rfactor; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **bondlist = neighbor->bondlist; + int nbondlist = neighbor->nbondlist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nbondlist; n++) { + + i1 = bondlist[n][0]; + i2 = bondlist[n][1]; + type = bondlist[n][2]; + + if (newton_bond) factor = 2; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + } + rfactor = 0.5*factor; + + delx = x[i1][0] - x[i2][0]; + dely = x[i1][1] - x[i2][1]; + delz = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx,&dely,&delz); + + // force from log term + + rsq = delx*delx + dely*dely + delz*delz; + r0sq = r0[type] * r0[type]; + rlogarg = 1.0 - rsq/r0sq; + + // if r -> r0, then rlogarg < 0.0 which is an error + // issue a warning and reset rlogarg = epsilon + // if r > 2*r0 something serious is wrong, abort + + if (rlogarg < 0.1) { + char str[128]; + sprintf(str,"FENE bond too long: %d %d %d %g", + update->ntimestep,atom->tag[i1],atom->tag[i2],sqrt(rsq)); + error->warning(str); + if (rlogarg <= -3.0) error->one("Bad FENE bond"); + rlogarg = 0.1; + } + + fforce = -k[type]/rlogarg; + + // force from LJ term + + if (rsq < TWO_1_3*sigma[type]*sigma[type]) { + sr2 = sigma[type]*sigma[type]/rsq; + sr6 = sr2*sr2*sr2; + fforce += 48.0*epsilon[type]*sr6*(sr6-0.5)/rsq; + } + + // energy + + if (eflag) { + energy -= 0.5*rfactor * k[type]*r0sq*log(rlogarg); + if (rsq < TWO_1_3*sigma[type]*sigma[type]) + energy += rfactor * (4.0*epsilon[type]*sr6*(sr6-1.0) + epsilon[type]); + } + + // apply force to each of 2 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] += delx*fforce; + f[i1][1] += dely*fforce; + f[i1][2] += delz*fforce; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] -= delx*fforce; + f[i2][1] -= dely*fforce; + f[i2][2] -= delz*fforce; + } + + // virial contribution + + if (vflag) { + virial[0] += rfactor * delx*delx*fforce; + virial[1] += rfactor * dely*dely*fforce; + virial[2] += rfactor * delz*delz*fforce; + virial[3] += rfactor * delx*dely*fforce; + virial[4] += rfactor * delx*delz*fforce; + virial[5] += rfactor * dely*delz*fforce; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void BondFENE::allocate() +{ + allocated = 1; + int n = atom->nbondtypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"bond:k"); + r0 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:r0"); + epsilon = (double *) memory->smalloc((n+1)*sizeof(double),"bond:epsilon"); + sigma = (double *) memory->smalloc((n+1)*sizeof(double),"bond:sigma"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"bond:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void BondFENE::coeff(int narg, char **arg) +{ + if (narg != 5) error->all("Incorrect args for bond coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nbondtypes,ilo,ihi); + + double k_one = atof(arg[1]); + double r0_one = atof(arg[2]); + double epsilon_one = atof(arg[3]); + double sigma_one = atof(arg[4]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + r0[i] = r0_one; + epsilon[i] = epsilon_one; + sigma[i] = sigma_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for bond coefficients"); +} + +/* ---------------------------------------------------------------------- */ + +double BondFENE::equilibrium_distance(int i) +{ + return 0.97*sigma[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void BondFENE::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&r0[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&epsilon[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&sigma[1],sizeof(double),atom->nbondtypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void BondFENE::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->nbondtypes,fp); + fread(&r0[1],sizeof(double),atom->nbondtypes,fp); + fread(&epsilon[1],sizeof(double),atom->nbondtypes,fp); + fread(&sigma[1],sizeof(double),atom->nbondtypes,fp); + } + MPI_Bcast(&k[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&r0[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&epsilon[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&sigma[1],atom->nbondtypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nbondtypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +void BondFENE::single(int type, double rsq, int i, int j, double rfactor, + int eflag, double &fforce, double &eng) +{ + double r0sq = r0[type] * r0[type]; + double rlogarg = 1.0 - rsq/r0sq; + + // if r -> r0, then rlogarg < 0.0 which is an error + // issue a warning and reset rlogarg = epsilon + // if r > 2*r0 something serious is wrong, abort + + if (rlogarg < 0.1) { + char str[128]; + sprintf(str,"FENE bond too long: %d %g",update->ntimestep,sqrt(rsq)); + error->warning(str); + if (rlogarg <= -3.0) error->one("Bad FENE bond"); + rlogarg = 0.1; + } + + fforce = -k[type]/rlogarg; + + // force from LJ term + + double sr2,sr6; + if (rsq < TWO_1_3*sigma[type]*sigma[type]) { + sr2 = sigma[type]*sigma[type]/rsq; + sr6 = sr2*sr2*sr2; + fforce += 48.0*epsilon[type]*sr6*(sr6-0.5)/rsq; + } + + // energy + + if (eflag) { + eng = -0.5*rfactor * k[type]*r0sq*log(rlogarg); + if (rsq < TWO_1_3*sigma[type]*sigma[type]) + eng += rfactor * (4.0*epsilon[type]*sr6*(sr6-1.0) + epsilon[type]); + } +} diff --git a/src/bond_fene.h b/src/bond_fene.h new file mode 100644 index 0000000000000000000000000000000000000000..43c997f92dcbc543fcf841e55bcfd86ed7517911 --- /dev/null +++ b/src/bond_fene.h @@ -0,0 +1,38 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef BOND_FENE_H +#define BOND_FENE_H + +#include "stdio.h" +#include "bond.h" + +class BondFENE : public Bond { + public: + BondFENE(); + ~BondFENE(); + void compute(int, int); + void coeff(int, char **); + double equilibrium_distance(int); + void write_restart(FILE *); + void read_restart(FILE *); + void single(int, double, int, int, double, int, double &, double &); + + private: + double TWO_1_3; + double *k,*r0,*epsilon,*sigma; + + void allocate(); +}; + +#endif diff --git a/src/bond_fene_expand.cpp b/src/bond_fene_expand.cpp new file mode 100644 index 0000000000000000000000000000000000000000..801ae110f87aa9f1170c4d2c6c5e194e6565cfd3 --- /dev/null +++ b/src/bond_fene_expand.cpp @@ -0,0 +1,292 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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. +------------------------------------------------------------------------- */ + +// FENE bond potential, with repulsive LJ, with shift + +#include "math.h" +#include "stdlib.h" +#include "bond_fene_expand.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "update.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- + set all global defaults +------------------------------------------------------------------------- */ + +BondFENEExpand::BondFENEExpand() +{ + TWO_1_3 = pow(2.0,(1.0/3.0)); +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +BondFENEExpand::~BondFENEExpand() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(r0); + memory->sfree(epsilon); + memory->sfree(sigma); + memory->sfree(shift); + } +} + +/* ---------------------------------------------------------------------- */ + +void BondFENEExpand::compute(int eflag, int vflag) +{ + int i1,i2,n,type,factor; + double delx,dely,delz,rsq,r0sq,rlogarg,fforce,sr2,sr6,rfactor; + double r,rshift,rshiftsq; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **bondlist = neighbor->bondlist; + int nbondlist = neighbor->nbondlist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nbondlist; n++) { + + i1 = bondlist[n][0]; + i2 = bondlist[n][1]; + type = bondlist[n][2]; + + if (newton_bond) factor = 2; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + } + rfactor = 0.5*factor; + + delx = x[i1][0] - x[i2][0]; + dely = x[i1][1] - x[i2][1]; + delz = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx,&dely,&delz); + + // force from log term + + rsq = delx*delx + dely*dely + delz*delz; + r = sqrt(rsq); + rshift = r - shift[type]; + rshiftsq = rshift*rshift; + r0sq = r0[type] * r0[type]; + rlogarg = 1.0 - rshiftsq/r0sq; + + // if r -> r0, then rlogarg < 0.0 which is an error + // issue a warning and reset rlogarg = epsilon + // if r > 2*r0 something serious is wrong, abort + + if (rlogarg < 0.1) { + char str[128]; + sprintf(str,"FENE bond too long: %d %d %d %g", + update->ntimestep,atom->tag[i1],atom->tag[i2],sqrt(rsq)); + error->warning(str); + if (rlogarg <= -3.0) error->one("Bad FENE bond"); + rlogarg = 0.1; + } + + fforce = -k[type]*rshift/rlogarg/r; + + // force from LJ term + + if (rshiftsq < TWO_1_3*sigma[type]*sigma[type]) { + sr2 = sigma[type]*sigma[type]/rshiftsq; + sr6 = sr2*sr2*sr2; + fforce += 48.0*epsilon[type]*sr6*(sr6-0.5)/rshift/r; + } + + // energy + + if (eflag) { + energy -= 0.5*rfactor * k[type]*r0sq*log(rlogarg); + if (rshiftsq < TWO_1_3*sigma[type]*sigma[type]) + energy += 0.5*factor * + (4.0*epsilon[type]*sr6*(sr6-1.0) + epsilon[type]); + } + + + // apply force to each of 2 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] += delx*fforce; + f[i1][1] += dely*fforce; + f[i1][2] += delz*fforce; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] -= delx*fforce; + f[i2][1] -= dely*fforce; + f[i2][2] -= delz*fforce; + } + + // virial contribution + + if (vflag) { + virial[0] += rfactor * delx*delx*fforce; + virial[1] += rfactor * dely*dely*fforce; + virial[2] += rfactor * delz*delz*fforce; + virial[3] += rfactor * delx*dely*fforce; + virial[4] += rfactor * delx*delz*fforce; + virial[5] += rfactor * dely*delz*fforce; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void BondFENEExpand::allocate() +{ + allocated = 1; + int n = atom->nbondtypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"bond:k"); + r0 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:r0"); + epsilon = (double *) memory->smalloc((n+1)*sizeof(double),"bond:epsilon"); + sigma = (double *) memory->smalloc((n+1)*sizeof(double),"bond:sigma"); + shift = (double *) memory->smalloc((n+1)*sizeof(double),"bond:shift"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"bond:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void BondFENEExpand::coeff(int narg, char **arg) +{ + if (narg != 6) error->all("Incorrect args for bond coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nbondtypes,ilo,ihi); + + double k_one = atof(arg[1]); + double r0_one = atof(arg[2]); + double epsilon_one = atof(arg[3]); + double sigma_one = atof(arg[4]); + double shift_one = atof(arg[5]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + r0[i] = r0_one; + epsilon[i] = epsilon_one; + sigma[i] = sigma_one; + shift[i] = shift_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for bond coefficients"); +} + +/* ---------------------------------------------------------------------- */ + +double BondFENEExpand::equilibrium_distance(int i) +{ + return 0.97*sigma[i] + shift[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void BondFENEExpand::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&r0[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&epsilon[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&sigma[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&shift[1],sizeof(double),atom->nbondtypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void BondFENEExpand::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->nbondtypes,fp); + fread(&r0[1],sizeof(double),atom->nbondtypes,fp); + fread(&epsilon[1],sizeof(double),atom->nbondtypes,fp); + fread(&sigma[1],sizeof(double),atom->nbondtypes,fp); + fread(&shift[1],sizeof(double),atom->nbondtypes,fp); + } + MPI_Bcast(&k[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&r0[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&epsilon[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&sigma[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&shift[1],atom->nbondtypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nbondtypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +void BondFENEExpand::single(int type, double rsq, int i, int j, double rfactor, + int eflag, double &fforce, double &eng) +{ + double r = sqrt(rsq); + double rshift = r - shift[type]; + double rshiftsq = rshift*rshift; + double r0sq = r0[type] * r0[type]; + double rlogarg = 1.0 - rshiftsq/r0sq; + + // if r -> r0, then rlogarg < 0.0 which is an error + // issue a warning and reset rlogarg = epsilon + // if r > 2*r0 something serious is wrong, abort + + if (rlogarg < 0.1) { + char str[128]; + sprintf(str,"FENE bond too long: %d %g",update->ntimestep,sqrt(rsq)); + error->warning(str); + if (rlogarg <= -3.0) error->one("Bad FENE bond"); + rlogarg = 0.1; + } + + fforce = -k[type]*rshift/rlogarg/r; + + // force from LJ term + + double sr2,sr6; + if (rshiftsq < TWO_1_3*sigma[type]*sigma[type]) { + sr2 = sigma[type]*sigma[type]/rshiftsq; + sr6 = sr2*sr2*sr2; + fforce += 48.0*epsilon[type]*sr6*(sr6-0.5)/rshift/r; + } + + // energy + + if (eflag) { + eng = -0.5*rfactor * k[type]*r0sq*log(rlogarg); + if (rshiftsq < TWO_1_3*sigma[type]*sigma[type]) + eng += rfactor * (4.0*epsilon[type]*sr6*(sr6-1.0) + epsilon[type]); + } +} diff --git a/src/bond_fene_expand.h b/src/bond_fene_expand.h new file mode 100644 index 0000000000000000000000000000000000000000..0e7167b1c68abf519ff5b6e6c979de23408cd124 --- /dev/null +++ b/src/bond_fene_expand.h @@ -0,0 +1,38 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef BOND_FENE_EXPAND_H +#define BOND_FENE_EXPAND_H + +#include "stdio.h" +#include "bond.h" + +class BondFENEExpand : public Bond { + public: + BondFENEExpand(); + ~BondFENEExpand(); + void compute(int, int); + void coeff(int, char **); + double equilibrium_distance(int); + void write_restart(FILE *); + void read_restart(FILE *); + void single(int, double, int, int, double, int, double &, double &); + + private: + double TWO_1_3; + double *k,*r0,*epsilon,*sigma,*shift; + + void allocate(); +}; + +#endif diff --git a/src/bond_harmonic.cpp b/src/bond_harmonic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4520ec0dfb7288a98ec728ce54fc04cb3c1691bf --- /dev/null +++ b/src/bond_harmonic.cpp @@ -0,0 +1,204 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "bond_harmonic.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +BondHarmonic::~BondHarmonic() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(r0); + } +} + +/* ---------------------------------------------------------------------- */ + +void BondHarmonic::compute(int eflag, int vflag) +{ + int i1,i2,n,type,factor; + double delx,dely,delz,rsq,r,dr,rk,fforce,rfactor; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **bondlist = neighbor->bondlist; + int nbondlist = neighbor->nbondlist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nbondlist; n++) { + + i1 = bondlist[n][0]; + i2 = bondlist[n][1]; + type = bondlist[n][2]; + + if (newton_bond) factor = 2; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + } + rfactor = 0.5 * factor; + + delx = x[i1][0] - x[i2][0]; + dely = x[i1][1] - x[i2][1]; + delz = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx,&dely,&delz); + + rsq = delx*delx + dely*dely + delz*delz; + r = sqrt(rsq); + dr = r - r0[type]; + rk = k[type] * dr; + + // force & energy + + if (r > 0.0) fforce = -2.0*rk/r; + else fforce = 0.0; + + if (eflag) energy += rfactor * rk*dr; + + // apply force to each of 2 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] += delx*fforce; + f[i1][1] += dely*fforce; + f[i1][2] += delz*fforce; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] -= delx*fforce; + f[i2][1] -= dely*fforce; + f[i2][2] -= delz*fforce; + } + + // virial contribution + + if (vflag) { + virial[0] += rfactor*delx*delx*fforce; + virial[1] += rfactor*dely*dely*fforce; + virial[2] += rfactor*delz*delz*fforce; + virial[3] += rfactor*delx*dely*fforce; + virial[4] += rfactor*delx*delz*fforce; + virial[5] += rfactor*dely*delz*fforce; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void BondHarmonic::allocate() +{ + allocated = 1; + int n = atom->nbondtypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"bond:k"); + r0 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:r0"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"bond:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more types +------------------------------------------------------------------------- */ + +void BondHarmonic::coeff(int narg, char **arg) +{ + if (narg != 3) error->all("Incorrect args for bond coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nbondtypes,ilo,ihi); + + double k_one = atof(arg[1]); + double r0_one = atof(arg[2]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + r0[i] = r0_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for bond coefficients"); +} + +/* ---------------------------------------------------------------------- + return an equilbrium bond length +------------------------------------------------------------------------- */ + +double BondHarmonic::equilibrium_distance(int i) +{ + return r0[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void BondHarmonic::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&r0[1],sizeof(double),atom->nbondtypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void BondHarmonic::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->nbondtypes,fp); + fread(&r0[1],sizeof(double),atom->nbondtypes,fp); + } + MPI_Bcast(&k[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&r0[1],atom->nbondtypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nbondtypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +void BondHarmonic::single(int type, double rsq, int i, int j, double rfactor, + int eflag, double &fforce, double &eng) +{ + double r = sqrt(rsq); + double dr = r - r0[type]; + double rk = k[type] * dr; + + // force & energy + + if (r > 0.0) fforce = -2.0*rk/r; + else fforce = 0.0; + if (eflag) eng = rfactor * rk*dr; +} diff --git a/src/bond_harmonic.h b/src/bond_harmonic.h new file mode 100644 index 0000000000000000000000000000000000000000..438df27e0b3119f2efdfa82c16630c7e4e9ac9e1 --- /dev/null +++ b/src/bond_harmonic.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef BOND_HARMONIC_H +#define BOND_HARMONIC_H + +#include "stdio.h" +#include "bond.h" + +class BondHarmonic : public Bond { + public: + BondHarmonic() {} + ~BondHarmonic(); + void compute(int, int); + void coeff(int, char **); + double equilibrium_distance(int); + void write_restart(FILE *); + void read_restart(FILE *); + void single(int, double, int, int, double, int, double &, double &); + + private: + double *k,*r0; + + void allocate(); +}; + +#endif diff --git a/src/bond_hybrid.cpp b/src/bond_hybrid.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ff9f7b4f3f8cbfc59fc53106a98a281845061021 --- /dev/null +++ b/src/bond_hybrid.cpp @@ -0,0 +1,273 @@ +/* ----------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "string.h" +#include "bond_hybrid.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define EXTRA 1000 + +/* ---------------------------------------------------------------------- + set all global defaults +------------------------------------------------------------------------- */ + +BondHybrid::BondHybrid() +{ + nstyles = 0; +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +BondHybrid::~BondHybrid() +{ + if (nstyles) { + for (int i = 0; i < nstyles; i++) delete styles[i]; + delete [] styles; + for (int i = 0; i < nstyles; i++) delete [] keywords[i]; + delete [] keywords; + } + + if (allocated) { + memory->sfree(setflag); + memory->sfree(map); + delete [] nbondlist; + delete [] maxbond; + for (int i = 0; i < nstyles; i++) + memory->destroy_2d_int_array(bondlist[i]); + delete [] bondlist; + } +} + +/* ---------------------------------------------------------------------- */ + +void BondHybrid::compute(int eflag, int vflag) +{ + int i,m,n; + + // save ptrs to original bondlist + + int nbondlist_orig = neighbor->nbondlist; + int **bondlist_orig = neighbor->bondlist; + + // if this is re-neighbor step, create sub-style bondlists + // nbondlist[] = length of each sub-style list + // realloc sub-style bondlist if necessary + // load sub-style bondlist with 3 values from original bondlist + + if (neighbor->ago == 0) { + for (m = 0; m < nstyles; m++) nbondlist[m] = 0; + for (i = 0; i < nbondlist_orig; i++) + nbondlist[map[bondlist_orig[i][2]]]++; + for (m = 0; m < nstyles; m++) { + if (nbondlist[m] > maxbond[m]) { + memory->destroy_2d_int_array(bondlist[m]); + maxbond[m] = nbondlist[m] + EXTRA; + bondlist[m] = (int **) + memory->create_2d_int_array(maxbond[m],3,"bond_hybrid:bondlist"); + } + nbondlist[m] = 0; + } + for (i = 0; i < nbondlist_orig; i++) { + m = map[bondlist_orig[i][2]]; + n = nbondlist[m]; + bondlist[m][n][0] = bondlist_orig[i][0]; + bondlist[m][n][1] = bondlist_orig[i][1]; + bondlist[m][n][2] = bondlist_orig[i][2]; + nbondlist[m]++; + } + } + + // call each sub-style's compute function + // must set neighbor->bondlist to sub-style bondlist before call + // accumulate sub-style energy,virial in hybrid's energy,virial + + energy = 0.0; + eng_vdwl = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + for (m = 0; m < nstyles; m++) { + if (styles[m] == NULL) continue; + neighbor->nbondlist = nbondlist[m]; + neighbor->bondlist = bondlist[m]; + styles[m]->compute(eflag,vflag); + if (eflag) { + energy += styles[m]->energy; + eng_vdwl += styles[m]->eng_vdwl; + } + if (vflag) for (n = 0; n < 6; n++) virial[n] += styles[m]->virial[n]; + } + + // restore ptrs to original bondlist + + neighbor->nbondlist = nbondlist_orig; + neighbor->bondlist = bondlist_orig; +} + +/* ---------------------------------------------------------------------- */ + +void BondHybrid::allocate() +{ + allocated = 1; + int n = atom->nbondtypes; + + map = (int *) memory->smalloc((n+1)*sizeof(int),"bond:map"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"bond:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; + + nbondlist = new int[nstyles]; + maxbond = new int[nstyles]; + bondlist = new int**[nstyles]; + for (int m = 0; m < nstyles; m++) maxbond[m] = 0; + for (int m = 0; m < nstyles; m++) bondlist[m] = NULL; +} + +/* ---------------------------------------------------------------------- + create one bond style for each arg in list +------------------------------------------------------------------------- */ + +void BondHybrid::settings(int narg, char **arg) +{ + nstyles = narg; + styles = new Bond*[nstyles]; + keywords = new char*[nstyles]; + + for (int m = 0; m < nstyles; m++) { + for (int i = 0; i < m; i++) + if (strcmp(arg[m],arg[i]) == 0) + error->all("Bond style hybrid cannot use same bond style twice"); + if (strcmp(arg[m],"hybrid") == 0) + error->all("Bond style hybrid cannot have hybrid as an argument"); + styles[m] = force->new_bond(arg[m]); + keywords[m] = new char[strlen(arg[m])+1]; + strcpy(keywords[m],arg[m]); + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +---------------------------------------------------------------------- */ + +void BondHybrid::coeff(int narg, char **arg) +{ + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nbondtypes,ilo,ihi); + + // 2nd arg = bond style name (harmonic, fene, etc) + + int m; + for (m = 0; m < nstyles; m++) + if (strcmp(arg[1],keywords[m]) == 0) break; + if (m == nstyles) error->all("Bond coeff for hybrid has invalid style"); + + // set low-level coefficients for each bondtype + // replace 2nd arg with i, call coeff() with no 1st arg + // if sub-style is NULL for "none", still set setflag + + for (int i = ilo; i <= ihi; i++) { + sprintf(arg[1],"%d",i); + map[i] = m; + if (styles[m]) styles[m]->coeff(narg-1,&arg[1]); + setflag[i] = 1; + } +} + +/* ---------------------------------------------------------------------- */ + +void BondHybrid::init_style() +{ + for (int m = 0; m < nstyles; m++) + if (styles[m]) styles[m]->init_style(); +} + +/* ---------------------------------------------------------------------- + return an equilbrium bond length +------------------------------------------------------------------------- */ + +double BondHybrid::equilibrium_distance(int i) +{ + return styles[map[i]]->equilibrium_distance(i); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void BondHybrid::write_restart(FILE *fp) +{ + fwrite(&nstyles,sizeof(int),1,fp); + + int n; + for (int m = 0; m < nstyles; m++) { + n = strlen(keywords[m]) + 1; + fwrite(&n,sizeof(int),1,fp); + fwrite(keywords[m],sizeof(char),n,fp); + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void BondHybrid::read_restart(FILE *fp) +{ + allocate(); + + int me = comm->me; + if (me == 0) fread(&nstyles,sizeof(int),1,fp); + MPI_Bcast(&nstyles,1,MPI_INT,0,world); + styles = new Bond*[nstyles]; + keywords = new char*[nstyles]; + + int n; + for (int m = 0; m < nstyles; m++) { + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + keywords[m] = new char[n]; + if (me == 0) fread(keywords[m],sizeof(char),n,fp); + MPI_Bcast(keywords[m],n,MPI_CHAR,0,world); + styles[m] = force->new_bond(keywords[m]); + } +} + +/* ---------------------------------------------------------------------- */ + +void BondHybrid::single(int type, double rsq, int i, int j, double rfactor, + int eflag, double &fforce, double &eng) +{ + if (styles[map[type]]) + styles[map[type]]->single(type,rsq,i,j,rfactor,eflag,fforce,eng); +} + +/* ---------------------------------------------------------------------- + memory usage +------------------------------------------------------------------------- */ + +int BondHybrid::memory_usage() +{ + int bytes = 0; + for (int m = 0; m < nstyles; m++) bytes += maxbond[m]*3 * sizeof(int); + for (int m = 0; m < nstyles; m++) + if (styles[m]) bytes += styles[m]->memory_usage(); + return bytes; +} diff --git a/src/bond_hybrid.h b/src/bond_hybrid.h new file mode 100644 index 0000000000000000000000000000000000000000..fa77bd845ccc8175b4324f96c7d066f80e69ae61 --- /dev/null +++ b/src/bond_hybrid.h @@ -0,0 +1,49 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef BOND_HYBRID_H +#define BOND_HYBRID_H + +#include "stdio.h" +#include "bond.h" + +class BondHybrid : public Bond { + friend class Force; + + public: + BondHybrid(); + ~BondHybrid(); + void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + void init_style(); + double equilibrium_distance(int); + void write_restart(FILE *); + void read_restart(FILE *); + void single(int, double, int, int, double, int, double &, double &); + int memory_usage(); + + private: + int nstyles; // # of different bond styles + Bond **styles; // class list for each Bond style + char **keywords; // keyword for each Bond style + int *map; // which style each bond type points to + + int *nbondlist; // # of bonds in sub-style bondlists + int *maxbond; // max # of bonds sub-style lists can store + int ***bondlist; // bondlist for each sub-style + + void allocate(); +}; + +#endif diff --git a/src/bond_morse.cpp b/src/bond_morse.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5cd83b8a32ad2de537e1cfc5e55f9aad051fa0be --- /dev/null +++ b/src/bond_morse.cpp @@ -0,0 +1,214 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Jeff Greathouse (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "bond_morse.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +BondMorse::~BondMorse() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(d0); + memory->sfree(alpha); + memory->sfree(r0); + } +} + +/* ---------------------------------------------------------------------- */ + +void BondMorse::compute(int eflag, int vflag) +{ + int i1,i2,n,type,factor; + double delx,dely,delz,rsq,r,dr,fforce,rfactor,ralpha; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **bondlist = neighbor->bondlist; + int nbondlist = neighbor->nbondlist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nbondlist; n++) { + + i1 = bondlist[n][0]; + i2 = bondlist[n][1]; + type = bondlist[n][2]; + + if (newton_bond) factor = 2; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + } + rfactor = 0.5 * factor; + + delx = x[i1][0] - x[i2][0]; + dely = x[i1][1] - x[i2][1]; + delz = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx,&dely,&delz); + + rsq = delx*delx + dely*dely + delz*delz; + r = sqrt(rsq); + dr = r - r0[type]; + ralpha = exp(-alpha[type]*dr); + + // force & energy + + if (r > 0.0) fforce = -2.0*d0[type]*alpha[type]*(1-ralpha)*ralpha/r; + else fforce = 0.0; + + if (eflag) energy += rfactor * d0[type]*(1-ralpha)*(1-ralpha); + + // apply force to each of 2 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] += delx*fforce; + f[i1][1] += dely*fforce; + f[i1][2] += delz*fforce; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] -= delx*fforce; + f[i2][1] -= dely*fforce; + f[i2][2] -= delz*fforce; + } + + // virial contribution + + if (vflag) { + virial[0] += rfactor*delx*delx*fforce; + virial[1] += rfactor*dely*dely*fforce; + virial[2] += rfactor*delz*delz*fforce; + virial[3] += rfactor*delx*dely*fforce; + virial[4] += rfactor*delx*delz*fforce; + virial[5] += rfactor*dely*delz*fforce; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void BondMorse::allocate() +{ + allocated = 1; + int n = atom->nbondtypes; + + d0 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:d0"); + alpha = (double *) memory->smalloc((n+1)*sizeof(double),"bond:alpha"); + r0 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:r0"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"bond:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void BondMorse::coeff(int narg, char **arg) +{ + if (narg != 4) error->all("Incorrect args for bond coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nbondtypes,ilo,ihi); + + double d0_one = atof(arg[1]); + double alpha_one = atof(arg[2]); + double r0_one = atof(arg[3]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + d0[i] = d0_one; + alpha[i] = alpha_one; + r0[i] = r0_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for bond coefficients"); +} + +/* ---------------------------------------------------------------------- + return an equilbrium bond length +------------------------------------------------------------------------- */ + +double BondMorse::equilibrium_distance(int i) +{ + return r0[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void BondMorse::write_restart(FILE *fp) +{ + fwrite(&d0[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&alpha[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&r0[1],sizeof(double),atom->nbondtypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void BondMorse::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&d0[1],sizeof(double),atom->nbondtypes,fp); + fread(&alpha[1],sizeof(double),atom->nbondtypes,fp); + fread(&r0[1],sizeof(double),atom->nbondtypes,fp); + } + MPI_Bcast(&d0[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&alpha[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&r0[1],atom->nbondtypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nbondtypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +void BondMorse::single(int type, double rsq, int i, int j, double rfactor, + int eflag, double &fforce, double &eng) +{ + double r = sqrt(rsq); + double dr = r - r0[type]; + double ralpha = exp(-alpha[type]*dr); + + // force & energy + + if (r > 0.0) fforce = -2.0*d0[type]*alpha[type]*(1-ralpha)*ralpha/r; + else fforce = 0.0; + if (eflag) eng = rfactor * d0[type]*(1-ralpha)*(1-ralpha); +} diff --git a/src/bond_morse.h b/src/bond_morse.h new file mode 100644 index 0000000000000000000000000000000000000000..9ac1d4cbf32eaaaabfda6bc4226fd8f35104a3dc --- /dev/null +++ b/src/bond_morse.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef BOND_MORSE_H +#define BOND_MORSE_H + +#include "stdio.h" +#include "bond.h" + +class BondMorse : public Bond { + public: + BondMorse() {} + ~BondMorse(); + void compute(int, int); + void coeff(int, char **); + double equilibrium_distance(int); + void write_restart(FILE *); + void read_restart(FILE *); + void single(int, double, int, int, double, int, double &, double &); + + private: + double *d0,*alpha,*r0; + + void allocate(); +}; + +#endif diff --git a/src/bond_nonlinear.cpp b/src/bond_nonlinear.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f39e61f6651ccb9ff50004dcea9ae84c8f78d376 --- /dev/null +++ b/src/bond_nonlinear.cpp @@ -0,0 +1,211 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "bond_nonlinear.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +BondNonlinear::~BondNonlinear() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(epsilon); + memory->sfree(r0); + memory->sfree(lamda); + } +} + +/* ---------------------------------------------------------------------- */ + +void BondNonlinear::compute(int eflag, int vflag) +{ + int i1,i2,n,type,factor; + double delx,dely,delz,rsq,r,dr,drsq,lamdasq,denom,denomsq,fforce,rfactor; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **bondlist = neighbor->bondlist; + int nbondlist = neighbor->nbondlist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nbondlist; n++) { + + i1 = bondlist[n][0]; + i2 = bondlist[n][1]; + type = bondlist[n][2]; + + if (newton_bond) factor = 2; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + } + rfactor = 0.5 * factor; + + delx = x[i1][0] - x[i2][0]; + dely = x[i1][1] - x[i2][1]; + delz = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx,&dely,&delz); + + rsq = delx*delx + dely*dely + delz*delz; + r = sqrt(rsq); + dr = r - r0[type]; + drsq = dr*dr; + lamdasq = lamda[type]*lamda[type]; + denom = lamdasq - drsq; + denomsq = denom*denom; + + // force & energy + + fforce = -epsilon[type]/r * 2.0*dr*lamdasq/denomsq; + if (eflag) energy += rfactor * epsilon[type] * drsq / denom; + + // apply force to each of 2 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] += delx*fforce; + f[i1][1] += dely*fforce; + f[i1][2] += delz*fforce; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] -= delx*fforce; + f[i2][1] -= dely*fforce; + f[i2][2] -= delz*fforce; + } + + // virial contribution + + if (vflag) { + virial[0] += rfactor*delx*delx*fforce; + virial[1] += rfactor*dely*dely*fforce; + virial[2] += rfactor*delz*delz*fforce; + virial[3] += rfactor*delx*dely*fforce; + virial[4] += rfactor*delx*delz*fforce; + virial[5] += rfactor*dely*delz*fforce; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void BondNonlinear::allocate() +{ + allocated = 1; + int n = atom->nbondtypes; + + epsilon = (double *) memory->smalloc((n+1)*sizeof(double),"bond:epsilon"); + r0 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:r0"); + lamda = (double *) memory->smalloc((n+1)*sizeof(double),"bond:lamda"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"bond:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void BondNonlinear::coeff(int narg, char **arg) +{ + if (narg != 4) error->all("Incorrect args for bond coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nbondtypes,ilo,ihi); + + double epsilon_one = atof(arg[1]); + double r0_one = atof(arg[2]); + double lamda_one = atof(arg[3]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + epsilon[i] = epsilon_one; + r0[i] = r0_one; + lamda[i] = lamda_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for bond coefficients"); +} + +/* ---------------------------------------------------------------------- */ + +double BondNonlinear::equilibrium_distance(int i) +{ + return r0[i]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void BondNonlinear::write_restart(FILE *fp) +{ + fwrite(&epsilon[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&r0[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&lamda[1],sizeof(double),atom->nbondtypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void BondNonlinear::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&epsilon[1],sizeof(double),atom->nbondtypes,fp); + fread(&r0[1],sizeof(double),atom->nbondtypes,fp); + fread(&lamda[1],sizeof(double),atom->nbondtypes,fp); + } + MPI_Bcast(&epsilon[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&r0[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&lamda[1],atom->nbondtypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nbondtypes; i++) setflag[i] = 1; +} + +/* ---------------------------------------------------------------------- */ + +void BondNonlinear::single(int type, double rsq, int i, int j, double rfactor, + int eflag, double &fforce, double &eng) +{ + double r = sqrt(rsq); + double dr = r - r0[type]; + double drsq = dr*dr; + double lamdasq = lamda[type]*lamda[type]; + double denom = lamdasq - drsq; + double denomsq = denom*denom; + + // force & energy + + fforce = -epsilon[type]/r * 2.0*dr*lamdasq/denomsq; + if (eflag) eng = rfactor * epsilon[type] * drsq / denom; +} diff --git a/src/bond_nonlinear.h b/src/bond_nonlinear.h new file mode 100644 index 0000000000000000000000000000000000000000..952ddf1661460603b281df75d54d07198f0fda21 --- /dev/null +++ b/src/bond_nonlinear.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef BOND_NONLINEAR_H +#define BOND_NONLINEAR_H + +#include "stdio.h" +#include "bond.h" + +class BondNonlinear : public Bond { + public: + BondNonlinear() {} + ~BondNonlinear(); + void compute(int, int); + void coeff(int, char **); + double equilibrium_distance(int); + void write_restart(FILE *); + void read_restart(FILE *); + void single(int, double, int, int, double, int, double &, double &); + + private: + double *epsilon,*r0,*lamda; + + void allocate(); +}; + +#endif diff --git a/src/bond_quartic.cpp b/src/bond_quartic.cpp new file mode 100755 index 0000000000000000000000000000000000000000..6889837bddfd840317ddf006d5b32caba82c24ef --- /dev/null +++ b/src/bond_quartic.cpp @@ -0,0 +1,340 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Chris Lorenz and Mark Stevens (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "bond_quartic.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "update.h" +#include "force.h" +#include "pair.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +BondQuartic::BondQuartic() +{ + TWO_1_3 = pow(2.0,(1.0/3.0)); +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +BondQuartic::~BondQuartic() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(b1); + memory->sfree(b2); + memory->sfree(rc); + memory->sfree(u0); + } +} + +/* ---------------------------------------------------------------------- */ + +void BondQuartic::compute(int eflag, int vflag) +{ + int i1,i2,n,m,type,factor,itype,jtype; + double delx,dely,delz,r,rsq,dr,r2,ra,rb,fforce,sr2,sr6,rfactor; + Pair::One one; + + energy = 0.0; + eng_vdwl = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **cutsq = force->pair->cutsq; + double **x = atom->x; + double **f = atom->f; + int **bondlist = neighbor->bondlist; + int nbondlist = neighbor->nbondlist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nbondlist; n++) { + + // skip bond if already broken + + if (bondlist[n][2] <= 0) continue; + + i1 = bondlist[n][0]; + i2 = bondlist[n][1]; + type = bondlist[n][2]; + + if (newton_bond) factor = 2; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + } + rfactor = 0.5*factor; + + delx = x[i1][0] - x[i2][0]; + dely = x[i1][1] - x[i2][1]; + delz = x[i1][2] - x[i2][2]; + domain->minimum_image(&delx,&dely,&delz); + + rsq = delx*delx + dely*dely + delz*delz; + + // if bond breaks, set type to 0 + // both in temporary bondlist and permanent bond_type + // if this proc owns both atoms, + // negate bond_type twice if other atom stores it + // if other proc owns 2nd atom, other proc will also break bond + + if (rsq > rc[type]*rc[type]) { + bondlist[n][2] = 0; + for (m = 0; m < atom->num_bond[i1]; m++) + if (atom->bond_atom[i1][m] == atom->tag[i2]) + atom->bond_type[i1][m] = 0; + if (i2 < atom->nlocal) + for (m = 0; m < atom->num_bond[i2]; m++) + if (atom->bond_atom[i2][m] == atom->tag[i1]) + atom->bond_type[i2][m] = 0; + continue; + } + + // subtract out pairwise contribution from 2 atoms via pair->single() + // required since special_bond = 1,1,1 + + itype = atom->type[i1]; + jtype = atom->type[i2]; + + if (rsq < cutsq[itype][jtype]) { + force->pair->single(i1,i2,itype,jtype,rsq,1.0,1.0,eflag,one); + fforce = -one.fforce; + if (eflag) eng_vdwl -= one.eng_vdwl + one.eng_coul; + } else fforce = 0.0; + + // quartic bond + // 1st portion is from quartic term + // 2nd portion is from LJ term cut at 2^(1/6) with eps = sigma = 1.0 + + r = sqrt(rsq); + dr = r - rc[type]; + r2 = dr*dr; + ra = dr - b1[type]; + rb = dr - b2[type]; + fforce += -k[type]/r * (r2*(ra+rb) + 2.0*dr*ra*rb); + + if (rsq < TWO_1_3) { + sr2 = 1.0/rsq; + sr6 = sr2*sr2*sr2; + fforce += 48.0*sr6*(sr6-0.5)/rsq; + } + + if (eflag) { + energy += rfactor*(k[type]*r2*ra*rb + u0[type]); + if (rsq < TWO_1_3) energy += rfactor * (4.0*sr6*(sr6-1.0) + 1.0); + } + + // apply force to each of 2 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] += delx*fforce; + f[i1][1] += dely*fforce; + f[i1][2] += delz*fforce; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] -= delx*fforce; + f[i2][1] -= dely*fforce; + f[i2][2] -= delz*fforce; + } + + // virial contribution + + if (vflag) { + virial[0] += rfactor * delx*delx*fforce; + virial[1] += rfactor * dely*dely*fforce; + virial[2] += rfactor * delz*delz*fforce; + virial[3] += rfactor * delx*dely*fforce; + virial[4] += rfactor * delx*delz*fforce; + virial[5] += rfactor * dely*delz*fforce; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void BondQuartic::allocate() +{ + allocated = 1; + int n = atom->nbondtypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"bond:k"); + b1 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:b1"); + b2 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:b2"); + rc = (double *) memory->smalloc((n+1)*sizeof(double),"bond:rc"); + u0 = (double *) memory->smalloc((n+1)*sizeof(double),"bond:u0"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"bond:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more types +------------------------------------------------------------------------- */ + +void BondQuartic::coeff(int narg, char **arg) +{ + if (narg != 6) error->all("Incorrect args for bond coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nbondtypes,ilo,ihi); + + double k_one = atof(arg[1]); + double b1_one = atof(arg[2]); + double b2_one = atof(arg[3]); + double rc_one = atof(arg[4]); + double u0_one = atof(arg[5]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + b1[i] = b1_one; + b2[i] = b2_one; + rc[i] = rc_one; + u0[i] = u0_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for bond coefficients"); +} + +/* ---------------------------------------------------------------------- + check if pair defined and special_bond settings are valid +------------------------------------------------------------------------- */ + +void BondQuartic::init_style() +{ + if (force->pair == NULL || force->pair->single_enable == 0) + error->all("Pair style does not support bond_style quartic"); + if (force->angle) + error->all("Bond style quartic cannot be used with 3,4-body interactions"); + if (force->dihedral) + error->all("Bond style quartic cannot be used with 3,4-body interactions"); + if (force->improper) + error->all("Bond style quartic cannot be used with 3,4-body interactions"); + + // special bonds must be 1 1 1 + + if (force->special_lj[1] != 1.0 || force->special_lj[2] != 1.0 || + force->special_lj[3] != 1.0) + error->all("Must use special bonds = 1,1,1 with bond style quartic"); +} + +/* ---------------------------------------------------------------------- + return an equilbrium bond length +------------------------------------------------------------------------- */ + +double BondQuartic::equilibrium_distance(int i) +{ + return 0.97; +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void BondQuartic::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&b1[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&b2[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&rc[1],sizeof(double),atom->nbondtypes,fp); + fwrite(&u0[1],sizeof(double),atom->nbondtypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void BondQuartic::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->nbondtypes,fp); + fread(&b1[1],sizeof(double),atom->nbondtypes,fp); + fread(&b2[1],sizeof(double),atom->nbondtypes,fp); + fread(&rc[1],sizeof(double),atom->nbondtypes,fp); + fread(&u0[1],sizeof(double),atom->nbondtypes,fp); + } + MPI_Bcast(&k[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&b1[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&b2[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&rc[1],atom->nbondtypes,MPI_DOUBLE,0,world); + MPI_Bcast(&u0[1],atom->nbondtypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nbondtypes; i++) setflag[i] = 1; +} + + +/* ---------------------------------------------------------------------- */ + +void BondQuartic::single(int type, double rsq, int i, int j, double rfactor, + int eflag, double &fforce, double &eng) +{ + double r,dr,r2,ra,rb,sr2,sr6; + + fforce = eng = 0.0; + if (type <= 0) return; + + // subtract out pairwise contribution from 2 atoms via pair->single() + // required since special_bond = 1,1,1 + + int itype = atom->type[i]; + int jtype = atom->type[j]; + + if (rsq < force->pair->cutsq[itype][jtype]) { + Pair::One one; + force->pair->single(i,j,itype,jtype,rsq,1.0,1.0,eflag,one); + fforce = -one.fforce; + if (eflag) eng = -one.eng_coul - one.eng_vdwl; + } + + // quartic bond + // 1st portion is from quartic term + // 2nd portion is from LJ term cut at 2^(1/6) with eps = sigma = 1.0 + + r = sqrt(rsq); + dr = r - rc[type]; + r2 = dr*dr; + ra = dr - b1[type]; + rb = dr - b2[type]; + fforce += -k[type]/r * (r2*(ra+rb) + 2.0*dr*ra*rb); + + if (rsq < TWO_1_3) { + sr2 = 1.0/rsq; + sr6 = sr2*sr2*sr2; + fforce += 48.0*sr6*(sr6-0.5)/rsq; + } + + if (eflag) { + eng += rfactor*(k[type]*r2*ra*rb + u0[type]); + if (rsq < TWO_1_3) eng += rfactor * (4.0*sr6*(sr6-1.0) + 1.0); + } +} diff --git a/src/bond_quartic.h b/src/bond_quartic.h new file mode 100644 index 0000000000000000000000000000000000000000..7bc7f55f3a761d2c8252346d4cd3ff099992b5ac --- /dev/null +++ b/src/bond_quartic.h @@ -0,0 +1,39 @@ +/* ----------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------ */ + +#ifndef BOND_QUARTIC_H +#define BOND_QUARTIC_H + +#include "stdio.h" +#include "bond.h" + +class BondQuartic : public Bond { + public: + BondQuartic(); + ~BondQuartic(); + void compute(int, int); + void coeff(int, char **); + void init_style(); + double equilibrium_distance(int); + void write_restart(FILE *); + void read_restart(FILE *); + void single(int, double, int, int, double, int, double &, double &); + + private: + double TWO_1_3; + double *k,*b1,*b2,*rc,*u0; + + void allocate(); +}; + +#endif diff --git a/src/comm.cpp b/src/comm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f3568b86708bfb582a0ef46124908d5d97bdb237 --- /dev/null +++ b/src/comm.cpp @@ -0,0 +1,853 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "string.h" +#include "stdio.h" +#include "stdlib.h" +#include "comm.h" +#include "atom.h" +#include "force.h" +#include "pair.h" +#include "domain.h" +#include "neighbor.h" +#include "modify.h" +#include "fix.h" +#include "error.h" +#include "memory.h" + +#define BUFFACTOR 1.5 +#define BUFMIN 1000 +#define BUFEXTRA 1000 + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define BIG 1.0e20 + +/* ---------------------------------------------------------------------- + setup MPI and allocate buffer space +------------------------------------------------------------------------- */ + +Comm::Comm() +{ + MPI_Comm_rank(world,&me); + MPI_Comm_size(world,&nprocs); + + user_procgrid[0] = user_procgrid[1] = user_procgrid[2] = 0; + + // initialize comm buffers & exchange memory + + maxsend = BUFMIN; + buf_send = (double *) + memory->smalloc((maxsend+BUFEXTRA)*sizeof(double),"comm:buf_send"); + maxrecv = BUFMIN; + buf_recv = (double *) + memory->smalloc(maxrecv*sizeof(double),"comm:buf_recv"); + + maxswap = 6; + allocate_swap(maxswap); + + sendlist = (int **) memory->smalloc(maxswap*sizeof(int *),"sendlist"); + maxsendlist = (int *) memory->smalloc(maxswap*sizeof(int),"maxsendlist"); + for (int i = 0; i < maxswap; i++) { + maxsendlist[i] = BUFMIN; + sendlist[i] = (int *) memory->smalloc(BUFMIN*sizeof(int),"sendlist[i]"); + } + + maxforward_fix = maxreverse_fix = 0; + maxforward_pair = maxreverse_pair = 0; +} + +/* ---------------------------------------------------------------------- + destructor, free all memory +------------------------------------------------------------------------- */ + +Comm::~Comm() +{ + free_swap(); + memory->sfree(maxsendlist); + if (sendlist) for (int i = 0; i < maxswap; i++) memory->sfree(sendlist[i]); + memory->sfree(sendlist); + + memory->sfree(buf_send); + memory->sfree(buf_recv); +} + +/* ---------------------------------------------------------------------- */ + +void Comm::init() +{ + // direct_flag = 1 if only x,f are exchanged in forward/reverse comm + + direct_flag = 1; + if (atom->check_style("dipole") || atom->check_style("dpd") || + atom->check_style("granular")) direct_flag = 0; + + // maxforward = # of datums in largest forward communication + // maxreverse = # of datums in largest reverse communication + // pair,fix values are set by init() of force,modify + + maxforward = MAX(atom->size_comm,atom->size_border); + maxforward = MAX(maxforward,maxforward_pair); + maxforward = MAX(maxforward,maxforward_fix); + + maxreverse = atom->size_reverse; + maxreverse = MAX(maxreverse,maxreverse_pair); + maxreverse = MAX(maxreverse,maxreverse_fix); + if (force->newton == 0) maxreverse = 0; + + map_style = atom->map_style; +} + +/* ---------------------------------------------------------------------- + setup 3d grid of procs based on box size +------------------------------------------------------------------------- */ + +void Comm::set_procs() +{ + if (user_procgrid[0] == 0) procs2box(); + else { + procgrid[0] = user_procgrid[0]; + procgrid[1] = user_procgrid[1]; + procgrid[2] = user_procgrid[2]; + } + + if (procgrid[0]*procgrid[1]*procgrid[2] != nprocs) + error->all("Bad grid of processors"); + if (force->dimension == 2 && procgrid[2] != 1) + error->all("Proc grid in z != 1 for 2d simulation"); + + int reorder = 0; + int periods[3]; + periods[0] = periods[1] = periods[2] = 1; + MPI_Comm cartesian; + + MPI_Cart_create(world,3,procgrid,periods,reorder,&cartesian); + MPI_Cart_get(cartesian,3,procgrid,periods,myloc); + MPI_Cart_shift(cartesian,0,1,&procneigh[0][0],&procneigh[0][1]); + MPI_Cart_shift(cartesian,1,1,&procneigh[1][0],&procneigh[1][1]); + MPI_Cart_shift(cartesian,2,1,&procneigh[2][0],&procneigh[2][1]); + MPI_Comm_free(&cartesian); + + if (me == 0) { + if (screen) fprintf(screen," %d by %d by %d processor grid\n", + procgrid[0],procgrid[1],procgrid[2]); + if (logfile) fprintf(logfile," %d by %d by %d processor grid\n", + procgrid[0],procgrid[1],procgrid[2]); + } +} + +/* ---------------------------------------------------------------------- + setup spatial-decomposition communication patterns + function of neighbor cutoff and current box size +------------------------------------------------------------------------- */ + +void Comm::setup() +{ + // need = # of procs I need atoms from in each dim + + need[0] = static_cast<int> + (neighbor->cutneigh * procgrid[0] / domain->xprd) + 1; + need[1] = static_cast<int> + (neighbor->cutneigh * procgrid[1] / domain->yprd) + 1; + need[2] = static_cast<int> + (neighbor->cutneigh * procgrid[2] / domain->zprd) + 1; + + // for 2d, don't communicate in z + + if (force->dimension == 2) need[2] = 0; + + // if non-periodic, do not communicate further than procgrid-1 away + // this enables very large cutoffs in non-periodic systems + + if (domain->xperiodic == 0) need[0] = MIN(need[0],procgrid[0]-1); + if (domain->yperiodic == 0) need[1] = MIN(need[1],procgrid[1]-1); + if (domain->zperiodic == 0) need[2] = MIN(need[2],procgrid[2]-1); + + // allocate comm memory + + nswap = 2 * (need[0]+need[1]+need[2]); + if (nswap > maxswap) grow_swap(); + + // setup parameters for each exchange: + // sendproc = proc to send to at each swap + // recvproc = proc to recv from at each swap + // slablo/slabhi = boundaries for slab of atoms to send at each swap + // use -BIG/midpt/BIG to insure all atoms included even if round-off occurs + // if round-off, atoms recvd across PBC can be < or > than subbox boundary + // note that borders() only loops over subset of atoms during each swap + // set slablo > slabhi for swaps across non-periodic boundaries + // this insures no atoms are swapped + // only for procs owning sub-box at non-periodic end of global box + // pbc_flags = add-on factor for atoms sent across a periodic global boundary + // 0 = not across a boundary + // 1 = add box-length to coord when sending + // -1 = subtract box-length from coord when sending + // 1st part of if statement is sending to the west/south/down + // 2nd part of if statement is sending to the east/north/up + + int iswap = 0; + for (int dim = 0; dim < 3; dim++) { + for (int ineed = 0; ineed < 2*need[dim]; ineed++) { + pbc_flags[iswap][0] = 0; + pbc_flags[iswap][1] = 0; + pbc_flags[iswap][2] = 0; + pbc_flags[iswap][3] = 0; + + if (ineed % 2 == 0) { + sendproc[iswap] = procneigh[dim][0]; + recvproc[iswap] = procneigh[dim][1]; + if (ineed < 2) slablo[iswap] = -BIG; + else slablo[iswap] = 0.5 * (domain->sublo[dim] + domain->subhi[dim]); + slabhi[iswap] = domain->sublo[dim] + neighbor->cutneigh; + if (myloc[dim] == 0) { + if (domain->periodicity[dim] == 0) + slabhi[iswap] = slablo[iswap] - 1.0; + else { + pbc_flags[iswap][0] = 1; + pbc_flags[iswap][dim+1] = 1; + } + } + } else { + sendproc[iswap] = procneigh[dim][1]; + recvproc[iswap] = procneigh[dim][0]; + slablo[iswap] = domain->subhi[dim] - neighbor->cutneigh; + if (ineed < 2) slabhi[iswap] = BIG; + else slabhi[iswap] = 0.5 * (domain->sublo[dim] + domain->subhi[dim]); + if (myloc[dim] == procgrid[dim]-1) { + if (domain->periodicity[dim] == 0) + slabhi[iswap] = slablo[iswap] - 1.0; + else { + pbc_flags[iswap][0] = 1; + pbc_flags[iswap][dim+1] = -1; + } + } + } + + iswap++; + } + } +} + +/* ---------------------------------------------------------------------- + communication of atom coords every timestep + other stuff may also be sent via pack/unpack routines +------------------------------------------------------------------------- */ + +void Comm::communicate() +{ + MPI_Request request; + MPI_Status status; + double **x = atom->x; + double *buf; + + // exchange data with another proc + // if other proc is self, just copy + // if direct_flag is set, exchange or copy directly to x, don't unpack + + for (int iswap = 0; iswap < nswap; iswap++) { + if (sendproc[iswap] != me) { + if (direct_flag) { + if (size_comm_recv[iswap]) buf = x[firstrecv[iswap]]; + else buf = NULL; + MPI_Irecv(buf,size_comm_recv[iswap],MPI_DOUBLE, + recvproc[iswap],0,world,&request); + atom->pack_comm(sendnum[iswap],sendlist[iswap], + buf_send,pbc_flags[iswap]); + MPI_Send(buf_send,size_comm_send[iswap],MPI_DOUBLE, + sendproc[iswap],0,world); + MPI_Wait(&request,&status); + } else { + MPI_Irecv(buf_recv,size_comm_recv[iswap],MPI_DOUBLE, + recvproc[iswap],0,world,&request); + atom->pack_comm(sendnum[iswap],sendlist[iswap], + buf_send,pbc_flags[iswap]); + MPI_Send(buf_send,size_comm_send[iswap],MPI_DOUBLE, + sendproc[iswap],0,world); + MPI_Wait(&request,&status); + atom->unpack_comm(recvnum[iswap],firstrecv[iswap],buf_recv); + } + + } else { + if (direct_flag) { + if (sendnum[iswap]) + atom->pack_comm(sendnum[iswap],sendlist[iswap], + x[firstrecv[iswap]],pbc_flags[iswap]); + } else { + atom->pack_comm(sendnum[iswap],sendlist[iswap], + buf_send,pbc_flags[iswap]); + atom->unpack_comm(recvnum[iswap],firstrecv[iswap],buf_send); + } + } + } +} + +/* ---------------------------------------------------------------------- + reverse communication of forces on atoms every timestep + other stuff can also be sent via pack/unpack routines +------------------------------------------------------------------------- */ + +void Comm::reverse_communicate() +{ + MPI_Request request; + MPI_Status status; + double **f = atom->f; + double *buf; + + // exchange data with another proc + // if other proc is self, just copy + // if direct_flag is set, exchange or copy directly from f, don't pack + + for (int iswap = nswap-1; iswap >= 0; iswap--) { + if (sendproc[iswap] != me) { + if (direct_flag) { + MPI_Irecv(buf_recv,size_reverse_recv[iswap],MPI_DOUBLE, + sendproc[iswap],0,world,&request); + if (size_reverse_send[iswap]) buf = f[firstrecv[iswap]]; + else buf = NULL; + MPI_Send(buf,size_reverse_send[iswap],MPI_DOUBLE, + recvproc[iswap],0,world); + MPI_Wait(&request,&status); + } else { + MPI_Irecv(buf_recv,size_reverse_recv[iswap],MPI_DOUBLE, + sendproc[iswap],0,world,&request); + atom->pack_reverse(recvnum[iswap],firstrecv[iswap],buf_send); + MPI_Send(buf_send,size_reverse_send[iswap],MPI_DOUBLE, + recvproc[iswap],0,world); + MPI_Wait(&request,&status); + } + atom->unpack_reverse(sendnum[iswap],sendlist[iswap],buf_recv); + + } else { + if (direct_flag) { + if (sendnum[iswap]) + atom->unpack_reverse(sendnum[iswap],sendlist[iswap], + f[firstrecv[iswap]]); + } else { + atom->pack_reverse(recvnum[iswap],firstrecv[iswap],buf_send); + atom->unpack_reverse(sendnum[iswap],sendlist[iswap],buf_send); + } + } + } +} + +/* ---------------------------------------------------------------------- + exchange: + move atoms to correct processors + atoms exchanged with all 6 stencil neighbors + send out atoms that have left my box, receive ones entering my box + atoms will be lost if not inside some proc's box + can happen if atom moves outside of non-periodic bounary + or if atom moves more than one proc away + this routine called before every reneighboring +------------------------------------------------------------------------- */ + +void Comm::exchange() +{ + int i,m,nsend,nrecv,nrecv1,nrecv2,nlocal; + double lo,hi,value; + double **x; + double *buf; + MPI_Request request; + MPI_Status status; + + // clear global->local map since atoms move & new ghosts are created + + if (map_style) atom->map_clear(); + + // loop over dimensions + + for (int dim = 0; dim < 3; dim++) { + + // fill buffer with atoms leaving my box, using < and >= + // when atom is deleted, fill it in with last atom + + x = atom->x; + lo = domain->sublo[dim]; + hi = domain->subhi[dim]; + nlocal = atom->nlocal; + i = nsend = 0; + + while (i < nlocal) { + if (x[i][dim] < lo || x[i][dim] >= hi) { + if (nsend > maxsend) grow_send(nsend,1); + nsend += atom->pack_exchange(i,&buf_send[nsend]); + atom->copy(nlocal-1,i); + nlocal--; + } else i++; + } + atom->nlocal = nlocal; + + // send/recv atoms in both directions + // if 1 proc in dimension, no send/recv, set recv buf to send buf + // if 2 procs in dimension, single send/recv + // if more than 2 procs in dimension, send/recv to both neighbors + + if (procgrid[dim] == 1) { + nrecv = nsend; + buf = buf_send; + + } else { + MPI_Sendrecv(&nsend,1,MPI_INT,procneigh[dim][0],0, + &nrecv1,1,MPI_INT,procneigh[dim][1],0,world,&status); + nrecv = nrecv1; + if (procgrid[dim] > 2) { + MPI_Sendrecv(&nsend,1,MPI_INT,procneigh[dim][1],0, + &nrecv2,1,MPI_INT,procneigh[dim][0],0,world,&status); + nrecv += nrecv2; + } + if (nrecv > maxrecv) grow_recv(nrecv); + + MPI_Irecv(buf_recv,nrecv1,MPI_DOUBLE,procneigh[dim][1],0, + world,&request); + MPI_Send(buf_send,nsend,MPI_DOUBLE,procneigh[dim][0],0,world); + MPI_Wait(&request,&status); + + if (procgrid[dim] > 2) { + MPI_Irecv(&buf_recv[nrecv1],nrecv2,MPI_DOUBLE,procneigh[dim][0],0, + world,&request); + MPI_Send(buf_send,nsend,MPI_DOUBLE,procneigh[dim][1],0,world); + MPI_Wait(&request,&status); + } + + buf = buf_recv; + } + + // check incoming atoms to see if they are in my box + // if so, add to my list + + m = 0; + while (m < nrecv) { + value = buf[m+dim+1]; + if (value >= lo && value < hi) m += atom->unpack_exchange(&buf[m]); + else m += static_cast<int> (buf[m]); + } + } +} + +/* ---------------------------------------------------------------------- + borders: + make lists of nearby atoms to send to neighboring procs at every timestep + one list is created for every swap that will be made + as list is made, actually do swaps + this does equivalent of a communicate (so don't need to explicitly + call communicate routine on reneighboring timestep) + this routine is called before every reneighboring +------------------------------------------------------------------------- */ + +void Comm::borders() +{ + int i,iswap,dim,ineed,maxneed,nsend,nrecv,nfirst,nlast,smax,rmax; + double lo,hi; + double **x; + double *buf; + MPI_Request request; + MPI_Status status; + + // clear old ghosts + + atom->nghost = 0; + + // do swaps over all 3 dimensions + + iswap = 0; + smax = rmax = 0; + + for (dim = 0; dim < 3; dim++) { + nlast = 0; + maxneed = 2*need[dim]; + for (ineed = 0; ineed < maxneed; ineed++) { + + // find atoms within slab boundaries lo/hi using <= and >= + // check atoms between nfirst and nlast + // for first swaps in a dim, check owned and ghost + // for later swaps in a dim, only check newly arrived ghosts + // store sent atom indices in list for use in future timesteps + + lo = slablo[iswap]; + hi = slabhi[iswap]; + x = atom->x; + if (ineed % 2 == 0) { + nfirst = nlast; + nlast = atom->nlocal + atom->nghost; + } + + nsend = 0; + for (i = nfirst; i < nlast; i++) + if (x[i][dim] >= lo && x[i][dim] <= hi) { + if (nsend == maxsendlist[iswap]) grow_list(iswap,nsend); + sendlist[iswap][nsend++] = i; + } + + // pack up list of border atoms + + if (nsend*atom->size_border > maxsend) + grow_send(nsend*atom->size_border,0); + atom->pack_border(nsend,sendlist[iswap],buf_send,pbc_flags[iswap]); + + // swap atoms with other proc + // put incoming ghosts at end of my atom arrays + // if swapping with self, simply copy, no messages + + if (sendproc[iswap] != me) { + MPI_Sendrecv(&nsend,1,MPI_INT,sendproc[iswap],0, + &nrecv,1,MPI_INT,recvproc[iswap],0,world,&status); + if (nrecv*atom->size_border > maxrecv) + grow_recv(nrecv*atom->size_border); + MPI_Irecv(buf_recv,nrecv*atom->size_border,MPI_DOUBLE, + recvproc[iswap],0,world,&request); + MPI_Send(buf_send,nsend*atom->size_border,MPI_DOUBLE, + sendproc[iswap],0,world); + MPI_Wait(&request,&status); + buf = buf_recv; + } else { + nrecv = nsend; + buf = buf_send; + } + + // unpack buffer + + atom->unpack_border(nrecv,atom->nlocal+atom->nghost,buf); + + // set all pointers & counters + + smax = MAX(smax,nsend); + rmax = MAX(rmax,nrecv); + sendnum[iswap] = nsend; + recvnum[iswap] = nrecv; + size_comm_send[iswap] = nsend * atom->size_comm; + size_comm_recv[iswap] = nrecv * atom->size_comm; + size_reverse_send[iswap] = nrecv * atom->size_reverse; + size_reverse_recv[iswap] = nsend * atom->size_reverse; + firstrecv[iswap] = atom->nlocal + atom->nghost; + atom->nghost += nrecv; + iswap++; + } + } + + // insure send/recv buffers are long enough for all forward & reverse comm + + int max = MAX(maxforward*smax,maxreverse*rmax); + if (max > maxsend) grow_send(max,0); + max = MAX(maxforward*rmax,maxreverse*smax); + if (max > maxrecv) grow_recv(max); + + // reset global->local map + + if (map_style) atom->map_set(); +} + +/* ---------------------------------------------------------------------- + realloc the size of the send buffer as needed with BUFFACTOR & BUFEXTRA + if flag = 1, realloc + if flag = 0, don't need to realloc with copy, just free/malloc +------------------------------------------------------------------------- */ + +void Comm::grow_send(int n, int flag) +{ + maxsend = static_cast<int> (BUFFACTOR * n); + if (flag) + buf_send = (double *) + memory->srealloc(buf_send,(maxsend+BUFEXTRA)*sizeof(double), + "comm:buf_send"); + else { + memory->sfree(buf_send); + buf_send = (double *) memory->smalloc((maxsend+BUFEXTRA)*sizeof(double), + "comm:buf_send"); + } +} + +/* ---------------------------------------------------------------------- + free/malloc the size of the recv buffer as needed with BUFFACTOR +------------------------------------------------------------------------- */ + +void Comm::grow_recv(int n) +{ + maxrecv = static_cast<int> (BUFFACTOR * n); + memory->sfree(buf_recv); + buf_recv = (double *) memory->smalloc(maxrecv*sizeof(double), + "comm:buf_recv"); +} + +/* ---------------------------------------------------------------------- + realloc the size of the iswap sendlist as needed with BUFFACTOR +------------------------------------------------------------------------- */ + +void Comm::grow_list(int iswap, int n) +{ + maxsendlist[iswap] = static_cast<int> (BUFFACTOR * n); + sendlist[iswap] = (int *) + memory->srealloc(sendlist[iswap],maxsendlist[iswap]*sizeof(int), + "comm:sendlist[iswap]"); +} + +/* ---------------------------------------------------------------------- + realloc the buffers needed for swaps +------------------------------------------------------------------------- */ + +void Comm::grow_swap() +{ + free_swap(); + allocate_swap(nswap); + + sendlist = (int **) memory->srealloc(sendlist,nswap*sizeof(int *), + "comm:sendlist"); + maxsendlist = (int *) memory->srealloc(maxsendlist,nswap*sizeof(int), + "comm:maxsendlist"); + for (int i = maxswap; i < nswap; i++) { + maxsendlist[i] = BUFMIN; + sendlist[i] = (int *) memory->smalloc(BUFMIN*sizeof(int), + "comm:sendlist[i]"); + } + maxswap = nswap; +} + +/* ---------------------------------------------------------------------- + initial allocation of swap info +------------------------------------------------------------------------- */ + +void Comm::allocate_swap(int n) +{ + sendnum = (int *) memory->smalloc(n*sizeof(int),"comm:sendnum"); + recvnum = (int *) memory->smalloc(n*sizeof(int),"comm:recvnum"); + sendproc = (int *) memory->smalloc(n*sizeof(int),"comm:sendproc"); + recvproc = (int *) memory->smalloc(n*sizeof(int),"comm:recvproc"); + size_comm_send = (int *) memory->smalloc(n*sizeof(int),"comm:size"); + size_comm_recv = (int *) memory->smalloc(n*sizeof(int),"comm:size"); + size_reverse_send = (int *) memory->smalloc(n*sizeof(int),"comm:size"); + size_reverse_recv = (int *) memory->smalloc(n*sizeof(int),"comm:size"); + slablo = (double *) memory->smalloc(n*sizeof(double),"comm:slablo"); + slabhi = (double *) memory->smalloc(n*sizeof(double),"comm:slabhi"); + firstrecv = (int *) memory->smalloc(n*sizeof(int),"comm:firstrecv"); + pbc_flags = (int **) memory->create_2d_int_array(n,4,"comm:pbc_flags"); +} + +/* ---------------------------------------------------------------------- + free memory for swaps +------------------------------------------------------------------------- */ + +void Comm::free_swap() +{ + memory->sfree(sendnum); + memory->sfree(recvnum); + memory->sfree(sendproc); + memory->sfree(recvproc); + memory->sfree(size_comm_send); + memory->sfree(size_comm_recv); + memory->sfree(size_reverse_send); + memory->sfree(size_reverse_recv); + memory->sfree(slablo); + memory->sfree(slabhi); + memory->sfree(firstrecv); + memory->destroy_2d_int_array(pbc_flags); +} + +/* ---------------------------------------------------------------------- + assign nprocs to 3d xprd,yprd,zprd box so as to minimize surface area +------------------------------------------------------------------------- */ + +void Comm::procs2box() +{ + int ipx,ipy,ipz,nremain; + double boxx,boxy,boxz,surf; + + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + + double bestsurf = 2.0 * (xprd*yprd + yprd*zprd + zprd*xprd); + + // loop thru all possible factorizations of nprocs + // surf = surface area of a proc sub-domain + // for 2d, insure ipz = 1 + + ipx = 1; + while (ipx <= nprocs) { + if (nprocs % ipx == 0) { + nremain = nprocs/ipx; + ipy = 1; + while (ipy <= nremain) { + if (nremain % ipy == 0) { + ipz = nremain/ipy; + if (force->dimension == 3 || ipz == 1) { + boxx = xprd/ipx; + boxy = yprd/ipy; + boxz = zprd/ipz; + surf = boxx*boxy + boxy*boxz + boxz*boxx; + if (surf < bestsurf) { + bestsurf = surf; + procgrid[0] = ipx; + procgrid[1] = ipy; + procgrid[2] = ipz; + } + } + } + ipy++; + } + } + ipx++; + } +} + +/* ---------------------------------------------------------------------- + return # of bytes of allocated memory +------------------------------------------------------------------------- */ + +int Comm::memory_usage() +{ + int bytes = 0; + + for (int i = 0; i < nswap; i++) bytes += maxsendlist[i] * sizeof(int); + bytes += maxsend * sizeof(double); + bytes += maxrecv * sizeof(double); + + return bytes; +} + +/* ---------------------------------------------------------------------- + forward communication invoked by a Fix +------------------------------------------------------------------------- */ + +void Comm::comm_fix(Fix *fix) +{ + int iswap,n; + double *buf; + MPI_Request request; + MPI_Status status; + + for (iswap = 0; iswap < nswap; iswap++) { + + // pack buffer + + n = fix->pack_comm(sendnum[iswap],sendlist[iswap], + buf_send,pbc_flags[iswap]); + + // exchange with another proc + // if self, set recv buffer to send buffer + + if (sendproc[iswap] != me) { + MPI_Irecv(buf_recv,n*recvnum[iswap],MPI_DOUBLE,recvproc[iswap],0, + world,&request); + MPI_Send(buf_send,n*sendnum[iswap],MPI_DOUBLE,sendproc[iswap],0,world); + MPI_Wait(&request,&status); + buf = buf_recv; + } else buf = buf_send; + + // unpack buffer + + fix->unpack_comm(recvnum[iswap],firstrecv[iswap],buf); + } +} + +/* ---------------------------------------------------------------------- + reverse communication invoked by a Fix +------------------------------------------------------------------------- */ + +void Comm::reverse_comm_fix(Fix *fix) +{ + int iswap,n; + double *buf; + MPI_Request request; + MPI_Status status; + + for (iswap = nswap-1; iswap >= 0; iswap--) { + + // pack buffer + + n = fix->pack_reverse_comm(recvnum[iswap],firstrecv[iswap],buf_send); + + // exchange with another proc + // if self, set recv buffer to send buffer + + if (sendproc[iswap] != me) { + MPI_Irecv(buf_recv,n*sendnum[iswap],MPI_DOUBLE,sendproc[iswap],0, + world,&request); + MPI_Send(buf_send,n*recvnum[iswap],MPI_DOUBLE,recvproc[iswap],0,world); + MPI_Wait(&request,&status); + buf = buf_recv; + } else buf = buf_send; + + // unpack buffer + + fix->unpack_reverse_comm(sendnum[iswap],sendlist[iswap],buf); + } +} + +/* ---------------------------------------------------------------------- + forward communication invoked by a Pair +------------------------------------------------------------------------- */ + +void Comm::comm_pair(Pair *pair) +{ + int iswap,n; + double *buf; + MPI_Request request; + MPI_Status status; + + for (iswap = 0; iswap < nswap; iswap++) { + + // pack buffer + + n = pair->pack_comm(sendnum[iswap],sendlist[iswap], + buf_send,pbc_flags[iswap]); + + // exchange with another proc + // if self, set recv buffer to send buffer + + if (sendproc[iswap] != me) { + MPI_Irecv(buf_recv,n*recvnum[iswap],MPI_DOUBLE,recvproc[iswap],0, + world,&request); + MPI_Send(buf_send,n*sendnum[iswap],MPI_DOUBLE,sendproc[iswap],0,world); + MPI_Wait(&request,&status); + buf = buf_recv; + } else buf = buf_send; + + // unpack buffer + + pair->unpack_comm(recvnum[iswap],firstrecv[iswap],buf); + } +} + +/* ---------------------------------------------------------------------- + reverse communication invoked by a Pair +------------------------------------------------------------------------- */ + +void Comm::reverse_comm_pair(Pair *pair) +{ + int iswap,n; + double *buf; + MPI_Request request; + MPI_Status status; + + for (iswap = nswap-1; iswap >= 0; iswap--) { + + // pack buffer + + n = pair->pack_reverse_comm(recvnum[iswap],firstrecv[iswap],buf_send); + + // exchange with another proc + // if self, set recv buffer to send buffer + + if (sendproc[iswap] != me) { + MPI_Irecv(buf_recv,n*sendnum[iswap],MPI_DOUBLE,sendproc[iswap],0, + world,&request); + MPI_Send(buf_send,n*recvnum[iswap],MPI_DOUBLE,recvproc[iswap],0,world); + MPI_Wait(&request,&status); + buf = buf_recv; + } else buf = buf_send; + + // unpack buffer + + pair->unpack_reverse_comm(sendnum[iswap],sendlist[iswap],buf); + } +} diff --git a/src/comm.h b/src/comm.h new file mode 100644 index 0000000000000000000000000000000000000000..4abfc5810b30c62b16257713c75e91283e861e68 --- /dev/null +++ b/src/comm.h @@ -0,0 +1,85 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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. +------------------------------------------------------------------------- */ + +// interprocessor communication + +#ifndef COMM_H +#define COMM_H + +#include "lammps.h" + +class Fix; +class Pair; + +class Comm : public LAMMPS { + public: + int me,nprocs; // proc info + int procgrid[3]; // assigned # of procs in each dim + int user_procgrid[3]; // user request for procs in each dim + int myloc[3]; // which proc I am in each dim + int procneigh[3][2]; // my 6 neighboring procs + int nswap; // # of swaps to perform + int need[3]; // procs I need atoms from in each dim + int maxforward_fix,maxreverse_fix; // comm sizes called from Fix,Pair + int maxforward_pair,maxreverse_pair; + + Comm(); + ~Comm(); + + void init(); + void set_procs(); // setup 3d grid of procs + void setup(); // setup 3d communication pattern + void communicate(); // communication of atom coords + void reverse_communicate(); // reverse communication of forces + void exchange(); // move atoms to new procs + void borders(); // setup list of atoms to communicate + int memory_usage(); // tally memory usage + void comm_fix(Fix *); // forward comm from a Fix + void reverse_comm_fix(Fix *); // reverse comm from a Fix + void comm_pair(Pair *); // forward comm from a Pair + void reverse_comm_pair(Pair *); // reverse comm from a Pair + + private: + int maxswap; // max # of swaps memory is allocated for + int *sendnum,*recvnum; // # of atoms to send/recv in each swap + int *sendproc,*recvproc; // proc to send/recv to/from at each swap + int *size_comm_send; // # of values to send in each comm + int *size_comm_recv; // # of values to recv in each comm + int *size_reverse_send; // # to send in each reverse comm + int *size_reverse_recv; // # to recv in each reverse comm + double *slablo,*slabhi; // bounds of slab to send at each swap + int **pbc_flags; // flags for sending atoms thru PBC + // [0] = 1 if any dim is across PBC + // [123] = 1 if dim 012 is across PBC + int direct_flag; // 1 if only x,f are forward/reverse comm + int map_style; // non-0 if global->local mapping is done + + int *firstrecv; // where to put 1st recv atom in each swap + int **sendlist; // list of atoms to send in each swap + int *maxsendlist; // max size of send list for each swap + + double *buf_send; // send buffer for all comm + double *buf_recv; // recv buffer for all comm + int maxsend,maxrecv; // current size of send/recv buffer + int maxforward,maxreverse; // max # of datums in forward/reverse comm + + void procs2box(); // map procs to 3d box + void grow_send(int,int); // reallocate send buffer + void grow_recv(int); // free/allocate recv buffer + void grow_list(int, int); // reallocate one sendlist + void grow_swap(); // grow swap arrays + void allocate_swap(int); // allocate swap arrays + void free_swap(); // free swap arrays +}; + +#endif diff --git a/src/create_atoms.cpp b/src/create_atoms.cpp new file mode 100644 index 0000000000000000000000000000000000000000..584c4cb1f2db624db7b68dd0cbd698414fa9c073 --- /dev/null +++ b/src/create_atoms.cpp @@ -0,0 +1,339 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "create_atoms.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "domain.h" +#include "update.h" +#include "region.h" +#include "error.h" + +#define SC 1 +#define BCC 2 +#define FCC 3 +#define SQ 4 +#define SQ2 5 +#define HEX 6 +#define DIAMOND 7 + +#define MINATOMS 1000 +#define MAXATOMS 0x7FFFFFFF +#define EPSILON 1.0e-6 + +/* ---------------------------------------------------------------------- */ + +void CreateAtoms::command(int narg, char **arg) +{ + if (domain->box_exist == 0) + error->all("Create_atoms command before simulation box is defined"); + + if (narg != 1 && narg != 2) error->all("Illegal create_atoms command"); + + create_type = atoi(arg[0]); + if (create_type > atom->ntypes) + error->all("Too large an atom type in create_atoms command"); + + if (strcmp(domain->lattice_style,"none") == 0) + error->all("Cannot create atoms with undefined lattice"); + if (!domain->orthogonality()) error->all("Non-orthogonal lattice vectors"); + if (!domain->right_handed()) + error->all("Orientation vectors are not right-handed"); + + // iregion = specified region (-1 if not specified) + + iregion = -1; + if (narg == 2) { + for (iregion = 0; iregion < domain->nregion; iregion++) + if (strcmp(arg[1],domain->regions[iregion]->id) == 0) break; + if (iregion == domain->nregion) + error->all("Create_atoms region ID does not exist"); + } + + // local copies of domain properties + + subxlo = domain->subxlo; + subxhi = domain->subxhi; + subylo = domain->subylo; + subyhi = domain->subyhi; + subzlo = domain->subzlo; + subzhi = domain->subzhi; + + boxxhi = domain->boxxhi; + boxyhi = domain->boxyhi; + boxzhi = domain->boxzhi; + + // ilo:ihi,jlo:jhi,klo:khi = loop bounds of simple cubic lattice + // that entirely overlaps my proc's sub-box + + int ilo,ihi,jlo,jhi,klo,khi; + + loop_bounds(0,&ilo,&ihi); + loop_bounds(1,&jlo,&jhi); + loop_bounds(2,&klo,&khi); + + // initialize 3d periodic lattice using overlapping loop bounds + // lattice style determines how many atoms in cubic unit cell + // sc = 1, bcc = 2, fcc = 4, sq = 1, sq2 = 2, hex = 2, diamond = 8 + + double natoms_previous = atom->natoms; + int nlocal_previous = atom->nlocal; + + int style; + if (strcmp(domain->lattice_style,"sc") == 0) style = SC; + else if (strcmp(domain->lattice_style,"bcc") == 0) style = BCC; + else if (strcmp(domain->lattice_style,"fcc") == 0) style = FCC; + else if (strcmp(domain->lattice_style,"sq") == 0) style = SQ; + else if (strcmp(domain->lattice_style,"sq2") == 0) style = SQ2; + else if (strcmp(domain->lattice_style,"hex") == 0) style = HEX; + else if (strcmp(domain->lattice_style,"diamond") == 0) style = DIAMOND; + + double ifull,ihalf,jfull,jhalf,kfull,khalf; + double iquart,i3quart,jquart,j3quart,kquart,k3quart; + int i,j,k; + + for (k = klo; k <= khi; k++) { + kfull = (double) k; + khalf = k + 0.5; + kquart = k + 0.25; + k3quart = k + 0.75; + for (j = jlo; j <= jhi; j++) { + jfull = (double) j; + jhalf = j + 0.5; + jquart = j + 0.25; + j3quart = j + 0.75; + for (i = ilo; i <= ihi; i++) { + ifull = (double) i; + ihalf = i + 0.5; + iquart = i + 0.25; + i3quart = i + 0.75; + + if (style == SC) + add_atom(ifull,jfull,kfull); + else if (style == BCC) { + add_atom(ifull,jfull,kfull); + add_atom(ihalf,jhalf,khalf); + } else if (style == FCC) { + add_atom(ifull,jfull,kfull); + add_atom(ihalf,jhalf,kfull); + add_atom(ihalf,jfull,khalf); + add_atom(ifull,jhalf,khalf); + } else if (style == SQ) { + add_atom(ifull,jfull,kfull); + } else if (style == SQ2) { + add_atom(ifull,jfull,kfull); + add_atom(ihalf,jhalf,kfull); + } else if (style == HEX) { + add_atom(ifull,jfull,kfull); + add_atom(ihalf,jhalf,kfull); + } else if (style == DIAMOND) { + add_atom(ifull,jfull,kfull); + add_atom(ifull,jhalf,khalf); + add_atom(ihalf,jfull,khalf); + add_atom(ihalf,jhalf,kfull); + add_atom(iquart,jquart,kquart); + add_atom(iquart,j3quart,k3quart); + add_atom(i3quart,jquart,k3quart); + add_atom(i3quart,j3quart,kquart); + } + } + } + } + + // new total # of atoms + + double rlocal = atom->nlocal; + MPI_Allreduce(&rlocal,&atom->natoms,1,MPI_DOUBLE,MPI_SUM,world); + + // print status + + if (comm->me == 0) { + if (screen) + fprintf(screen,"Created %.15g atoms\n",atom->natoms-natoms_previous); + if (logfile) + fprintf(logfile,"Created %.15g atoms\n",atom->natoms-natoms_previous); + } + + // reset simulation now that more atoms are defined + // add tags for newly created atoms if possible + // if global map exists, reset it + // if a molecular system, set nspecial to 0 for new atoms + + if (atom->natoms > MAXATOMS) atom->tag_enable = 0; + if (atom->natoms <= MAXATOMS) atom->tag_extend(); + + if (atom->map_style) { + atom->map_init(); + atom->map_set(); + } + if (atom->molecular) { + int **nspecial = atom->nspecial; + for (i = nlocal_previous; i < atom->nlocal; i++) { + nspecial[i][0] = 0; + nspecial[i][1] = 0; + nspecial[i][2] = 0; + } + } +} + +/* ---------------------------------------------------------------------- + add an atom at x,y,z in lattice coords if it meets all criteria +------------------------------------------------------------------------- */ + +void CreateAtoms::add_atom(double x, double y, double z) +{ + // convert from lattice coords to box coords + + domain->lattice2box(&x,&y,&z); + + // if a region was specified, test if atom is in it + + if (iregion >= 0) + if (!domain->regions[iregion]->match(x,y,z)) return; + + // test if atom is in my subbox + + if (x < subxlo || x >= subxhi || + y < subylo || y >= subyhi || + z < subzlo || z >= subzhi) return; + + // don't put atoms within EPSILON of upper periodic boundary + // else may overlap image atom at lower boundary + + if (domain->xperiodic && fabs(x-boxxhi) < EPSILON) return; + if (domain->yperiodic && fabs(y-boxyhi) < EPSILON) return; + if (domain->zperiodic && fabs(z-boxzhi) < EPSILON) return; + + // add the atom to my list of atoms + + atom->create_one(create_type,x,y,z); +} + +/* ---------------------------------------------------------------------- + search for 2 bounding lattice planes that completely enclose my sub-box + do this by testing if all corner points of my sub-box lie on correct side + of a lattice plane via same_side function + dim = 0,1,2 for x,y,z directions + lo,hi = returned indices of 2 bounding lattice planes + a lattice plane is defined by: + point = point in lattice space through which the plane passes + normal = vector normal to lattice plane +------------------------------------------------------------------------- */ + +void CreateAtoms::loop_bounds(int dim, int *lo, int *hi) +{ + int normal[3],point[3]; + + // start search at origin + + point[0] = point[1] = point[2] = 0; + + // set lattice plane direction along positive lattice axis + + normal[0] = normal[1] = normal[2] = 0; + normal[dim] = 1; + + // step down (if needed) until entire box is above the plane + // step up until 1st time entire box is not above the plane + + while (!same_side(point,normal)) point[dim]--; + while (same_side(point,normal)) point[dim]++; + + // lower loop bound = current loc minus 1 (subtract 1 more for safety) + + *lo = point[dim] - 2; + + // flip plane direction + // step up until entire box is below the plane + + normal[dim] = -1; + while (!same_side(point,normal)) point[dim]++; + + // lower loop bound = current loc (add 1 more for safety) + + *hi = point[dim] + 1; +} + +/* ---------------------------------------------------------------------- + test if all 8 corner points of my sub-box are on "correct" side of a plane + plane is defined by point[3] it goes thru and a normal[3] + normal also defines the correct side +------------------------------------------------------------------------- */ + +int CreateAtoms::same_side(int *point, int *normal) +{ + // p1 = plane center point in box coords + // p2 = point on correct side of plane, in box coords + + double p1x = point[0]; + double p1y = point[1]; + double p1z = point[2]; + domain->lattice2box(&p1x,&p1y,&p1z); + + double p2x = point[0] + normal[0]; + double p2y = point[1] + normal[1]; + double p2z = point[2] + normal[2]; + domain->lattice2box(&p2x,&p2y,&p2z); + + // for each of 8 sub-box corner points, dot these 2 vectors: + // v1 = from plane center point to point on correct side of plane + // v2 = from plane center point to box corner point + // negative result = portion of box is on wrong side of plane, return 0 + + double v1[3],v2[3]; + + points2vec(p1x,p1y,p1z,p2x,p2y,p2z,v1); + + points2vec(p1x,p1y,p1z,subxlo,subylo,subzlo,v2); + if (dot(v1,v2) < 0.0) return 0; + points2vec(p1x,p1y,p1z,subxhi,subylo,subzlo,v2); + if (dot(v1,v2) < 0.0) return 0; + points2vec(p1x,p1y,p1z,subxlo,subyhi,subzlo,v2); + if (dot(v1,v2) < 0.0) return 0; + points2vec(p1x,p1y,p1z,subxhi,subyhi,subzlo,v2); + if (dot(v1,v2) < 0.0) return 0; + points2vec(p1x,p1y,p1z,subxlo,subylo,subzhi,v2); + if (dot(v1,v2) < 0.0) return 0; + points2vec(p1x,p1y,p1z,subxhi,subylo,subzhi,v2); + if (dot(v1,v2) < 0.0) return 0; + points2vec(p1x,p1y,p1z,subxlo,subyhi,subzhi,v2); + if (dot(v1,v2) < 0.0) return 0; + points2vec(p1x,p1y,p1z,subxhi,subyhi,subzhi,v2); + if (dot(v1,v2) < 0.0) return 0; + + // all 8 points were on correct side + + return 1; +} + +/* ---------------------------------------------------------------------- */ + +double CreateAtoms::dot(double *vec1, double *vec2) +{ + double sum = vec1[0]*vec2[0] + vec1[1]*vec2[1] + vec1[2]*vec2[2]; + return sum; +} + +/* ---------------------------------------------------------------------- */ + +void CreateAtoms::points2vec(double p1x, double p1y, double p1z, + double p2x, double p2y, double p2z, double *v) +{ + v[0] = p2x - p1x; + v[1] = p2y - p1y; + v[2] = p2z - p1z; +} diff --git a/src/create_atoms.h b/src/create_atoms.h new file mode 100644 index 0000000000000000000000000000000000000000..57d1541e878fd48558d8519138eb339c09b36780 --- /dev/null +++ b/src/create_atoms.h @@ -0,0 +1,38 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef CREATE_ATOMS_H +#define CREATE_ATOMS_H + +#include "lammps.h" + +class CreateAtoms : public LAMMPS { + public: + CreateAtoms() {} + ~CreateAtoms() {} + void command(int, char **); + + private: + int create_type; + double subxlo,subxhi,subylo,subyhi,subzlo,subzhi; + double boxxhi,boxyhi,boxzhi; + int iregion; + + void add_atom(double, double, double); + void loop_bounds(int, int *, int *); + int same_side(int *, int *); + double dot(double *, double *); + void points2vec(double, double, double, double, double, double, double *); +}; + +#endif diff --git a/src/create_box.cpp b/src/create_box.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eee4763fb07e735e438c0b4583cc5d215dcf07cb --- /dev/null +++ b/src/create_box.cpp @@ -0,0 +1,103 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "string.h" +#include "create_box.h" +#include "atom.h" +#include "force.h" +#include "domain.h" +#include "region.h" +#include "comm.h" +#include "update.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +void CreateBox::command(int narg, char **arg) +{ + if (atom == NULL) + error->all("Cannot create_box until atom_style is defined"); + if (domain->box_exist) + error->all("Cannot create_box after simulation box is defined"); + if (narg != 2) error->all("Illegal create_box command"); + + if (force->dimension == 2 && domain->zperiodic == 0) + error->all("Cannot run 2d simulation with nonperiodic Z dimension"); + + // find region ID + + int iregion; + for (iregion = 0; iregion < domain->nregion; iregion++) + if (strcmp(arg[1],domain->regions[iregion]->id) == 0) break; + if (iregion == domain->nregion) + error->all("Create_box region ID does not exist"); + + if (domain->regions[iregion]->interior == 0) + error->all("Create_box region must be of type inside"); + + // set global box from region extent + + domain->boxxlo = domain->regions[iregion]->extent_xlo; + domain->boxxhi = domain->regions[iregion]->extent_xhi; + domain->boxylo = domain->regions[iregion]->extent_ylo; + domain->boxyhi = domain->regions[iregion]->extent_yhi; + domain->boxzlo = domain->regions[iregion]->extent_zlo; + domain->boxzhi = domain->regions[iregion]->extent_zhi; + + domain->box_exist = 1; + + if (comm->me == 0) { + if (screen) + fprintf(screen,"Created box = (%g %g %g) to (%g %g %g)\n", + domain->boxxlo,domain->boxylo,domain->boxzlo, + domain->boxxhi,domain->boxyhi,domain->boxzhi); + if (logfile) + fprintf(logfile,"Created box = (%g %g %g) to (%g %g %g)\n", + domain->boxxlo,domain->boxylo,domain->boxzlo, + domain->boxxhi,domain->boxyhi,domain->boxzhi); + } + + // if molecular, zero out topology info + + if (atom->molecular) { + atom->bond_per_atom = 0; + atom->angle_per_atom = 0; + atom->dihedral_per_atom = 0; + atom->improper_per_atom = 0; + atom->nbonds = 0; + atom->nangles = 0; + atom->ndihedrals = 0; + atom->nimpropers = 0; + } + + // set atom and topology type quantities + + atom->ntypes = atoi(arg[0]); + atom->nbondtypes = 0; + atom->nangletypes = 0; + atom->ndihedraltypes = 0; + atom->nimpropertypes = 0; + + // problem setup using info from header + // no call to atom->grow since create_atoms or insertion will do that + + update->ntimestep = 0; + + atom->allocate_type_arrays(); + + domain->set_initial_box(); + domain->set_global_box(); + comm->set_procs(); + domain->set_local_box(); +} diff --git a/src/create_box.h b/src/create_box.h new file mode 100644 index 0000000000000000000000000000000000000000..31b8f50c1ae55aa79b989084fd4eda25799e9335 --- /dev/null +++ b/src/create_box.h @@ -0,0 +1,26 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef CREATE_BOX_H +#define CREATE_BOX_H + +#include "lammps.h" + +class CreateBox : public LAMMPS { + public: + CreateBox() {} + ~CreateBox() {} + void command(int, char **); +}; + +#endif diff --git a/src/delete_atoms.cpp b/src/delete_atoms.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6579e1e082f8e0735d2d40764bf7560c155e32ad --- /dev/null +++ b/src/delete_atoms.cpp @@ -0,0 +1,303 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "string.h" +#include "delete_atoms.h" +#include "system.h" +#include "atom.h" +#include "comm.h" +#include "domain.h" +#include "force.h" +#include "group.h" +#include "region.h" +#include "neighbor.h" +#include "comm.h" +#include "force.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +void DeleteAtoms::command(int narg, char **arg) +{ + if (domain->box_exist == 0) + error->all("Delete_atoms command before simulation box is defined"); + if (narg < 1) error->all("Illegal delete_atoms command"); + + // store state before delete + + if (atom->tag_enable == 0) + error->all("Cannot use delete_atoms unless atoms have IDs"); + + int natoms_previous = static_cast<int> (atom->natoms); + int tag_lowest = natoms_previous + 1; + + // allocate and initialize deletion list + + int nlocal = atom->nlocal; + int *list = new int[nlocal]; + + for (int i = 0; i < nlocal; i++) list[i] = 0; + + // delete the atoms + // return tag_lowest = min tag of each proc's deleted atoms + + if (strcmp(arg[0],"group") == 0) delete_group(narg,arg,list); + else if (strcmp(arg[0],"region") == 0) delete_region(narg,arg,list); + else if (strcmp(arg[0],"overlap") == 0) delete_overlap(narg,arg,list); + else error->all("Illegal delete_atoms command"); + + // delete local atoms in list + // reset nlocal + // tag_lowest_all = smallest deleted atom tag on any proc + + int *tag = atom->tag; + + int i = 0; + while (i < nlocal) { + if (list[i]) { + tag_lowest = MIN(tag_lowest,tag[i]); + atom->copy(nlocal-1,i); + list[i] = list[nlocal-1]; + nlocal--; + } else i++; + } + atom->nlocal = nlocal; + delete [] list; + + int tag_lowest_all; + MPI_Allreduce(&tag_lowest,&tag_lowest_all,1,MPI_INT,MPI_MIN,world); + + // if non-molecular system, reset atom tags to be contiguous + // set all atom IDs >= tag_lowest_all to 0 + // tag_extend() will reset tags for those atoms + + if (atom->molecular == 0) { + int *tag = atom->tag; + int nlocal = atom->nlocal; + + for (i = 0; i < nlocal; i++) + if (tag[i] >= tag_lowest_all) tag[i] = 0; + atom->tag_extend(); + } + + // reset atom->natoms + // reset atom->map if it exists + // set nghost to 0 so old ghosts of deleted atoms won't be mapped + + double rlocal = atom->nlocal; + MPI_Allreduce(&rlocal,&atom->natoms,1,MPI_DOUBLE,MPI_SUM,world); + if (atom->map_style) { + atom->nghost = 0; + atom->map_init(); + atom->map_set(); + } + + // print before and after atom count + + int ndelete = static_cast<int> (natoms_previous - atom->natoms); + + if (comm->me == 0) { + if (screen) fprintf(screen,"Deleted %d atoms, new total = %.15g\n", + ndelete,atom->natoms); + if (logfile) fprintf(logfile,"Deleted %d atoms, new total = %.15g\n", + ndelete,atom->natoms); + } +} + +/* ---------------------------------------------------------------------- + delete all atoms in group + group will still exist +------------------------------------------------------------------------- */ + +void DeleteAtoms::delete_group(int narg, char **arg, int *list) +{ + if (narg != 2) error->all("Illegal delete_atoms command"); + + int igroup = group->find(arg[1]); + if (igroup == -1) error->all("Could not find delete_atoms group ID"); + + int *mask = atom->mask; + int nlocal = atom->nlocal; + int groupbit = group->bitmask[igroup]; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) list[i] = 1; +} + +/* ---------------------------------------------------------------------- + delete all atoms in region +------------------------------------------------------------------------- */ + +void DeleteAtoms::delete_region(int narg, char **arg, int *list) +{ + if (narg != 2) error->all("Illegal delete_atoms command"); + + int iregion; + for (iregion = 0; iregion < domain->nregion; iregion++) + if (strcmp(arg[1],domain->regions[iregion]->id) == 0) break; + if (iregion == domain->nregion) + error->all("Could not find delete_atoms region ID"); + + double **x = atom->x; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (domain->regions[iregion]->match(x[i][0],x[i][1],x[i][2])) list[i] = 1; +} + +/* ---------------------------------------------------------------------- + delete atoms so there are no pairs within cutoff + which atoms are deleted depends on ordering of atoms within proc + deletions can vary with processor count + no guarantee that minimium number of atoms will be deleted +------------------------------------------------------------------------- */ + +void DeleteAtoms::delete_overlap(int narg, char **arg, int *list) +{ + if (narg < 2) error->all("Illegal delete_atoms command"); + + // read args including optional type info + + double cut = atof(arg[1]); + double cutsq = cut*cut; + + int typeflag,type1,type2; + if (narg == 2) typeflag = 0; + else if (narg == 3) { + typeflag = 1; + type1 = atoi(arg[2]); + } else if (narg == 4) { + typeflag = 2; + type1 = atoi(arg[2]); + type2 = atoi(arg[3]); + } else error->all("Illegal delete_atoms command"); + + // init entire system since comm->borders and neighbor->build is done + // comm::init needs neighbor::init needs pair::init needs kspace::init, etc + + if (comm->me == 0 && screen) + fprintf(screen,"System init for delete_atoms ...\n"); + sys->init(); + + // setup domain, communication and neighboring + // acquire ghosts + // build half neighbor list via explicit call + + domain->pbc(); + domain->reset_box(); + comm->setup(); + if (neighbor->style) neighbor->setup_bins(); + comm->exchange(); + comm->borders(); + + if (!neighbor->half) neighbor->build_half_setup(); + neighbor->build_half(); + + // error check on cutoff + + if (cut > neighbor->cutneigh) + error->all("Delete_atoms cutoff > neighbor cutoff"); + + // create an atom map if one doesn't exist already + + int mapflag = 0; + if (atom->map_style == 0) { + mapflag = 1; + atom->map_style = 1; + atom->map_init(); + atom->map_set(); + } + + // double loop over owned atoms and their neighbors + // at end of loop, there are no more overlaps + // criteria for i,j to undergo a deletion event: + // weighting factor != 0.0 for this pair + // could be 0 and still be in neigh list for long-range Coulombics + // local copy of j (map[tag[j]]) has not already been deleted + // distance between i,j is less than cutoff + // i,j are of valid types + // if all criteria met, delete i and skip to next i in outer loop + // unless j is ghost and newton_pair off and tag[j] < tag[i] + // then rely on other proc to delete + + int *tag = atom->tag; + int *type = atom->type; + double **x = atom->x; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + + int i,j,k,m,itype,jtype,numneigh; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + int *neighs; + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + list[i] = 1; + if (j >= nall) { + if (special_coul[j/nall] == 0.0 && special_lj[j/nall] == 0.0) continue; + j %= nall; + } + + if (j < nlocal) { + if (list[j]) continue; + } else { + m = atom->map(tag[j]); + if (m < nlocal && list[m]) continue; + } + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + if (rsq >= cutsq) continue; + + if (typeflag) { + jtype = type[j]; + if (typeflag == 1 && itype != type1 && jtype != type1) continue; + if (typeflag == 2 && !(itype == type1 && jtype == type2) && + !(itype == type2 && jtype == type1)) continue; + } + + if (j >= nlocal && newton_pair == 0 && tag[j] < tag[i]) continue; + + list[i] = 1; + break; + } + } + + // cleanup after neighbor list + + if (!neighbor->half) neighbor->build_half_cleanup(); + + // delete temporary atom map + + if (mapflag) { + atom->map_delete(); + atom->map_style = 0; + } +} diff --git a/src/delete_atoms.h b/src/delete_atoms.h new file mode 100644 index 0000000000000000000000000000000000000000..5def81bbf62f1f12a851f5ca39f3864219f3bad7 --- /dev/null +++ b/src/delete_atoms.h @@ -0,0 +1,31 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DELETE_ATOMS_H +#define DELETE_ATOMS_H + +#include "lammps.h" + +class DeleteAtoms : public LAMMPS { + public: + DeleteAtoms() {} + ~DeleteAtoms() {} + void command(int, char **); + + private: + void delete_group(int, char **, int *); + void delete_region(int, char **, int *); + void delete_overlap(int, char **, int *); +}; + +#endif diff --git a/src/delete_bonds.cpp b/src/delete_bonds.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9f03f4d63ef2f34cb62945dae96dd557bfdc43cc --- /dev/null +++ b/src/delete_bonds.cpp @@ -0,0 +1,479 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "stdlib.h" +#include "string.h" +#include "delete_bonds.h" +#include "system.h" +#include "atom.h" +#include "domain.h" +#include "neighbor.h" +#include "comm.h" +#include "force.h" +#include "pair.h" +#include "group.h" +#include "special.h" +#include "error.h" + +#define MULTI 1 +#define ATOM 2 +#define BOND 3 +#define ANGLE 4 +#define DIHEDRAL 5 +#define IMPROPER 6 +#define STATS 7 + +/* ---------------------------------------------------------------------- */ + +void DeleteBonds::command(int narg, char **arg) +{ + if (domain->box_exist == 0) + error->all("Delete_bonds command before simulation box is defined"); + if (atom->natoms == 0) + error->all("Delete_bonds command with no atoms existing"); + if (atom->molecular == 0) + error->all("Cannot use delete_bonds with non-molecular system"); + if (narg < 2) error->all("Illegal delete_bonds command"); + + // init entire system since comm->borders is done + // comm::init needs neighbor::init needs pair::init needs kspace::init, etc + + if (comm->me == 0 && screen) + fprintf(screen,"System init for delete_bonds ...\n"); + sys->init(); + + if (comm->me == 0 && screen) fprintf(screen,"Deleting bonds ...\n"); + + // identify group + + int igroup = group->find(arg[0]); + if (igroup == -1) error->all("Cannot find delete_bonds group ID"); + int groupbit = group->bitmask[igroup]; + + // set style and which = type value + + int style; + if (strcmp(arg[1],"multi") == 0) style = MULTI; + else if (strcmp(arg[1],"atom") == 0) style = ATOM; + else if (strcmp(arg[1],"bond") == 0) style = BOND; + else if (strcmp(arg[1],"angle") == 0) style = ANGLE; + else if (strcmp(arg[1],"dihedral") == 0) style = DIHEDRAL; + else if (strcmp(arg[1],"improper") == 0) style = IMPROPER; + else if (strcmp(arg[1],"stats") == 0) style = STATS; + else error->all("Illegal delete_bonds command"); + + int iarg = 2; + int which; + if (style != MULTI && style != STATS) { + if (narg < 3) error->all("Illegal delete_bonds command"); + which = atoi(arg[2]); + iarg++; + } + + // grab optional keywords + + int undo_flag = 0; + int remove_flag = 0; + int special_flag = 0; + + while (iarg < narg) { + if (strcmp(arg[iarg],"undo") == 0) undo_flag = 1; + else if (strcmp(arg[iarg],"remove") == 0) remove_flag = 1; + else if (strcmp(arg[iarg],"special") == 0) special_flag = 1; + else error->all("Illegal delete_bonds command"); + iarg++; + } + + // border swap to insure type and mask is current for off-proc atoms + // enforce PBC before in case atoms are outside box + + domain->pbc(); + domain->reset_box(); + comm->setup(); + comm->exchange(); + comm->borders(); + + // set topology interactions either off or on + // criteria for an interaction to potentially be changed (set flag = 1) + // all atoms in interaction must be in group + // for style = MULTI, no other criteria + // for style = ATOM, at least one atom is specified type + // for style = BOND/ANGLE/DIHEDRAL/IMPROPER, interaction is specified type + // for style = STATS only compute stats, flag is always 0 + // if flag = 1 + // set interaction type negative if undo_flag = 0 + // set interaction type positive if undo_flag = 1 + + int *mask = atom->mask; + int *type = atom->type; + int nlocal = atom->nlocal; + + int i,m,n,flag; + int atom1,atom2,atom3,atom4; + + if (atom->bonds_allow) { + int *num_bond = atom->num_bond; + int **bond_type = atom->bond_type; + + for (i = 0; i < nlocal; i++) { + for (m = 0; m < num_bond[i]; m++) { + atom1 = atom->map(atom->bond_atom[i][m]); + if (atom1 == -1) error->one("Bond atom missing in delete_bonds"); + if (mask[i] & groupbit && mask[atom1] & groupbit) { + flag = 0; + if (style == MULTI) flag = 1; + if (style == ATOM && + (type[i] == which || type[atom1] == which)) flag = 1; + if (style == BOND && (bond_type[i][m] == which)) flag = 1; + if (flag) { + if (undo_flag == 0 && bond_type[i][m] > 0) + bond_type[i][m] = -bond_type[i][m]; + if (undo_flag == 1 && bond_type[i][m] < 0) + bond_type[i][m] = -bond_type[i][m]; + } + } + } + } + } + + if (atom->angles_allow) { + int *num_angle = atom->num_angle; + int **angle_type = atom->angle_type; + + for (i = 0; i < nlocal; i++) { + for (m = 0; m < num_angle[i]; m++) { + atom1 = atom->map(atom->angle_atom1[i][m]); + atom2 = atom->map(atom->angle_atom2[i][m]); + atom3 = atom->map(atom->angle_atom3[i][m]); + if (atom1 == -1 || atom2 == -1 || atom3 == -1) + error->one("Angle atom missing in delete_bonds"); + if (mask[atom1] & groupbit && mask[atom2] & groupbit && + mask[atom3] & groupbit) { + flag = 0; + if (style == MULTI) flag = 1; + if (style == ATOM && + (type[atom1] == which || type[atom2] == which || + type[atom3] == which)) flag = 1; + if (style == ANGLE && (angle_type[i][m] == which)) flag = 1; + if (flag) { + if (undo_flag == 0 && angle_type[i][m] > 0) + angle_type[i][m] = -angle_type[i][m]; + if (undo_flag == 1 && angle_type[i][m] < 0) + angle_type[i][m] = -angle_type[i][m]; + } + } + } + } + } + + if (atom->dihedrals_allow) { + int *num_dihedral = atom->num_dihedral; + int **dihedral_type = atom->dihedral_type; + + for (i = 0; i < nlocal; i++) { + for (m = 0; m < num_dihedral[i]; m++) { + atom1 = atom->map(atom->dihedral_atom1[i][m]); + atom2 = atom->map(atom->dihedral_atom2[i][m]); + atom3 = atom->map(atom->dihedral_atom3[i][m]); + atom4 = atom->map(atom->dihedral_atom4[i][m]); + if (atom1 == -1 || atom2 == -1 || atom3 == -1 || atom4 == -1) + error->one("Dihedral atom missing in delete_bonds"); + if (mask[atom1] & groupbit && mask[atom2] & groupbit && + mask[atom3] & groupbit && mask[atom4] & groupbit) { + flag = 0; + if (style == MULTI) flag = 1; + if (style == ATOM && + (type[atom1] == which || type[atom2] == which || + type[atom3] == which || type[atom4] == which)) flag = 1; + if (style == DIHEDRAL && (dihedral_type[i][m] == which)) flag = 1; + if (flag) { + if (undo_flag == 0 && dihedral_type[i][m] > 0) + dihedral_type[i][m] = -dihedral_type[i][m]; + if (undo_flag == 1 && dihedral_type[i][m] < 0) + dihedral_type[i][m] = -dihedral_type[i][m]; + } + } + } + } + } + + if (atom->impropers_allow) { + int *num_improper = atom->num_improper; + int **improper_type = atom->improper_type; + + for (i = 0; i < nlocal; i++) { + for (m = 0; m < num_improper[i]; m++) { + atom1 = atom->map(atom->improper_atom1[i][m]); + atom2 = atom->map(atom->improper_atom2[i][m]); + atom3 = atom->map(atom->improper_atom3[i][m]); + atom4 = atom->map(atom->improper_atom4[i][m]); + if (atom1 == -1 || atom2 == -1 || atom3 == -1 || atom4 == -1) + error->one("Improper atom missing in delete_bonds"); + if (mask[atom1] & groupbit && mask[atom2] & groupbit && + mask[atom3] & groupbit && mask[atom4] & groupbit) { + flag = 0; + if (style == MULTI) flag = 1; + if (style == ATOM && + (type[atom1] == which || type[atom2] == which || + type[atom3] == which || type[atom4] == which)) flag = 1; + if (style == IMPROPER && (improper_type[i][m] == which)) flag = 1; + if (flag) { + if (undo_flag == 0 && improper_type[i][m] > 0) + improper_type[i][m] = -improper_type[i][m]; + if (undo_flag == 1 && improper_type[i][m] < 0) + improper_type[i][m] = -improper_type[i][m]; + } + } + } + } + } + + // remove interactions if requested + // only if all atoms in bond, angle, etc are in the delete_bonds group + + if (remove_flag) { + + if (atom->bonds_allow) { + for (i = 0; i < nlocal; i++) { + m = 0; + while (m < atom->num_bond[i]) { + if (atom->bond_type[i][m] <= 0) { + atom1 = atom->map(atom->bond_atom[i][m]); + if (mask[i] & groupbit && mask[atom1] & groupbit) { + n = atom->num_bond[i]; + atom->bond_type[i][m] = atom->bond_type[i][n-1]; + atom->bond_atom[i][m] = atom->bond_atom[i][n-1]; + atom->num_bond[i]--; + } else m++; + } else m++; + } + } + } + + if (atom->angles_allow) { + for (i = 0; i < nlocal; i++) { + m = 0; + while (m < atom->num_angle[i]) { + if (atom->angle_type[i][m] <= 0) { + atom1 = atom->map(atom->angle_atom1[i][m]); + atom2 = atom->map(atom->angle_atom2[i][m]); + atom3 = atom->map(atom->angle_atom3[i][m]); + if (mask[atom1] & groupbit && mask[atom2] & groupbit && + mask[atom3] & groupbit) { + n = atom->num_angle[i]; + atom->angle_type[i][m] = atom->angle_type[i][n-1]; + atom->angle_atom1[i][m] = atom->angle_atom1[i][n-1]; + atom->angle_atom2[i][m] = atom->angle_atom2[i][n-1]; + atom->angle_atom3[i][m] = atom->angle_atom3[i][n-1]; + atom->num_angle[i]--; + } else m++; + } else m++; + } + } + } + + if (atom->dihedrals_allow) { + for (i = 0; i < nlocal; i++) { + m = 0; + while (m < atom->num_dihedral[i]) { + if (atom->dihedral_type[i][m] <= 0) { + atom1 = atom->map(atom->dihedral_atom1[i][m]); + atom2 = atom->map(atom->dihedral_atom2[i][m]); + atom3 = atom->map(atom->dihedral_atom3[i][m]); + atom4 = atom->map(atom->dihedral_atom4[i][m]); + if (mask[atom1] & groupbit && mask[atom2] & groupbit && + mask[atom3] & groupbit && mask[atom4] & groupbit) { + n = atom->num_dihedral[i]; + atom->dihedral_type[i][m] = atom->dihedral_type[i][n-1]; + atom->dihedral_atom1[i][m] = atom->dihedral_atom1[i][n-1]; + atom->dihedral_atom2[i][m] = atom->dihedral_atom2[i][n-1]; + atom->dihedral_atom3[i][m] = atom->dihedral_atom3[i][n-1]; + atom->dihedral_atom4[i][m] = atom->dihedral_atom4[i][n-1]; + atom->num_dihedral[i]--; + } else m++; + } else m++; + } + } + } + + if (atom->impropers_allow) { + for (i = 0; i < nlocal; i++) { + m = 0; + while (m < atom->num_improper[i]) { + if (atom->improper_type[i][m] <= 0) { + atom1 = atom->map(atom->improper_atom1[i][m]); + atom2 = atom->map(atom->improper_atom2[i][m]); + atom3 = atom->map(atom->improper_atom3[i][m]); + atom4 = atom->map(atom->improper_atom4[i][m]); + if (mask[atom1] & groupbit && mask[atom2] & groupbit && + mask[atom3] & groupbit && mask[atom4] & groupbit) { + n = atom->num_improper[i]; + atom->improper_type[i][m] = atom->improper_type[i][n-1]; + atom->improper_atom1[i][m] = atom->improper_atom1[i][n-1]; + atom->improper_atom2[i][m] = atom->improper_atom2[i][n-1]; + atom->improper_atom3[i][m] = atom->improper_atom3[i][n-1]; + atom->improper_atom4[i][m] = atom->improper_atom4[i][n-1]; + atom->num_improper[i]--; + } else m++; + } else m++; + } + } + } + + } + + // if interactions were removed, recompute global counts + + if (remove_flag) { + + if (atom->bonds_allow) { + int nbonds = 0; + for (i = 0; i < nlocal; i++) nbonds += atom->num_bond[i]; + MPI_Allreduce(&nbonds,&atom->nbonds,1,MPI_INT,MPI_SUM,world); + if (force->newton_bond == 0) atom->nbonds /= 2; + } + + if (atom->angles_allow) { + int nangles = 0; + for (i = 0; i < nlocal; i++) nangles += atom->num_angle[i]; + MPI_Allreduce(&nangles,&atom->nangles,1,MPI_INT,MPI_SUM,world); + if (force->newton_bond == 0) atom->nangles /= 3; + } + + if (atom->dihedrals_allow) { + int ndihedrals = 0; + for (i = 0; i < nlocal; i++) ndihedrals += atom->num_dihedral[i]; + MPI_Allreduce(&ndihedrals,&atom->ndihedrals,1,MPI_INT,MPI_SUM,world); + if (force->newton_bond == 0) atom->ndihedrals /= 4; + } + + if (atom->impropers_allow) { + int nimpropers = 0; + for (i = 0; i < nlocal; i++) nimpropers += atom->num_improper[i]; + MPI_Allreduce(&nimpropers,&atom->nimpropers,1,MPI_INT,MPI_SUM,world); + if (force->newton_bond == 0) atom->nimpropers /= 4; + } + + } + + // compute and print stats + + int tmp; + int bond_on,bond_off; + int angle_on,angle_off; + int dihedral_on,dihedral_off; + int improper_on,improper_off; + + if (atom->bonds_allow) { + bond_on = bond_off = 0; + for (i = 0; i < nlocal; i++) + for (m = 0; m < atom->num_bond[i]; m++) + if (atom->bond_type[i][m] > 0) bond_on++; + else bond_off++; + MPI_Allreduce(&bond_on,&tmp,1,MPI_INT,MPI_SUM,world); + bond_on = tmp; + MPI_Allreduce(&bond_off,&tmp,1,MPI_INT,MPI_SUM,world); + bond_off = tmp; + if (force->newton_bond == 0) { + bond_on /= 2; + bond_off /= 2; + } + } + + if (atom->angles_allow) { + angle_on = angle_off = 0; + for (i = 0; i < nlocal; i++) + for (m = 0; m < atom->num_angle[i]; m++) + if (atom->angle_type[i][m] > 0) angle_on++; + else angle_off++; + MPI_Allreduce(&angle_on,&tmp,1,MPI_INT,MPI_SUM,world); + angle_on = tmp; + MPI_Allreduce(&angle_off,&tmp,1,MPI_INT,MPI_SUM,world); + angle_off = tmp; + if (force->newton_bond == 0) { + angle_on /= 3; + angle_off /= 3; + } + } + + if (atom->dihedrals_allow) { + dihedral_on = dihedral_off = 0; + for (i = 0; i < nlocal; i++) + for (m = 0; m < atom->num_dihedral[i]; m++) + if (atom->dihedral_type[i][m] > 0) dihedral_on++; + else dihedral_off++; + MPI_Allreduce(&dihedral_on,&tmp,1,MPI_INT,MPI_SUM,world); + dihedral_on = tmp; + MPI_Allreduce(&dihedral_off,&tmp,1,MPI_INT,MPI_SUM,world); + dihedral_off = tmp; + if (force->newton_bond == 0) { + dihedral_on /= 4; + dihedral_off /= 4; + } + } + + if (atom->impropers_allow) { + improper_on = improper_off = 0; + for (i = 0; i < nlocal; i++) + for (m = 0; m < atom->num_improper[i]; m++) + if (atom->improper_type[i][m] > 0) improper_on++; + else improper_off++; + MPI_Allreduce(&improper_on,&tmp,1,MPI_INT,MPI_SUM,world); + improper_on = tmp; + MPI_Allreduce(&improper_off,&tmp,1,MPI_INT,MPI_SUM,world); + improper_off = tmp; + if (force->newton_bond == 0) { + improper_on /= 4; + improper_off /= 4; + } + } + + if (comm->me == 0) { + if (screen) { + if (atom->bonds_allow) + fprintf(screen," %d total bonds, %d turned on, %d turned off\n", + atom->nbonds,bond_on,bond_off); + if (atom->angles_allow) + fprintf(screen," %d total angles, %d turned on, %d turned off\n", + atom->nangles,angle_on,angle_off); + if (atom->dihedrals_allow) + fprintf(screen," %d total dihedrals, %d turned on, %d turned off\n", + atom->ndihedrals,dihedral_on,dihedral_off); + if (atom->impropers_allow) + fprintf(screen," %d total impropers, %d turned on, %d turned off\n", + atom->nimpropers,improper_on,improper_off); + } + if (logfile) { + if (atom->bonds_allow) + fprintf(logfile," %d total bonds, %d turned on, %d turned off\n", + atom->nbonds,bond_on,bond_off); + if (atom->angles_allow) + fprintf(logfile," %d total angles, %d turned on, %d turned off\n", + atom->nangles,angle_on,angle_off); + if (atom->dihedrals_allow) + fprintf(logfile," %d total dihedrals, %d turned on, %d turned off\n", + atom->ndihedrals,dihedral_on,dihedral_off); + if (atom->impropers_allow) + fprintf(logfile," %d total impropers, %d turned on, %d turned off\n", + atom->nimpropers,improper_on,improper_off); + } + } + + // re-compute special list if requested + + if (special_flag) { + Special special; + special.build(); + } +} diff --git a/src/delete_bonds.h b/src/delete_bonds.h new file mode 100644 index 0000000000000000000000000000000000000000..3f57a3fa16c1666bea8093d983fcdea61dda582b --- /dev/null +++ b/src/delete_bonds.h @@ -0,0 +1,26 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DELETE_BONDS_H +#define DELETE_BONDS_H + +#include "lammps.h" + +class DeleteBonds : public LAMMPS { + public: + DeleteBonds() {} + ~DeleteBonds() {} + void command(int, char **); +}; + +#endif diff --git a/src/dihedral.cpp b/src/dihedral.cpp new file mode 100644 index 0000000000000000000000000000000000000000..49de307e3673f82c53909426421cb5610ef6b34f --- /dev/null +++ b/src/dihedral.cpp @@ -0,0 +1,42 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "dihedral.h" +#include "atom.h" +#include "error.h" + +/* ---------------------------------------------------------------------- + set dihedral contribution to Vdwl and Coulombic energy to 0.0 + DihedralCharmm will override this +------------------------------------------------------------------------- */ + +Dihedral::Dihedral() +{ + allocated = 0; + eng_vdwl = eng_coul = 0.0; + PI = 4.0*atan(1.0); +} + +/* ---------------------------------------------------------------------- + check if all coeffs are set +------------------------------------------------------------------------- */ + +void Dihedral::init() +{ + if (!allocated) error->all("Dihedral coeffs are not set"); + for (int i = 1; i <= atom->ndihedraltypes; i++) + if (setflag[i] == 0) error->all("All dihedral coeffs are not set"); + init_style(); +} + diff --git a/src/dihedral.h b/src/dihedral.h new file mode 100644 index 0000000000000000000000000000000000000000..db9296a678228e5146065dc2130132710d080b34 --- /dev/null +++ b/src/dihedral.h @@ -0,0 +1,42 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DIHEDRAL_H +#define DIHEDRAL_H + +#include "stdio.h" +#include "lammps.h" + +class Dihedral : public LAMMPS { + public: + int allocated; + int *setflag; + double energy; + double eng_vdwl,eng_coul; + double virial[6]; + double PI; + + Dihedral(); + virtual ~Dihedral() {} + virtual void init(); + virtual void init_style() {} + + virtual void compute(int, int) = 0; + virtual void settings(int, char **) {} + virtual void coeff(int, int, char **) = 0; + virtual void write_restart(FILE *) = 0; + virtual void read_restart(FILE *) = 0; + virtual int memory_usage() {return 0;} +}; + +#endif diff --git a/src/dihedral_charmm.cpp b/src/dihedral_charmm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..24198df7f1bb22b5d79bbfdef63a36e2cfda37ed --- /dev/null +++ b/src/dihedral_charmm.cpp @@ -0,0 +1,450 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "mpi.h" +#include "math.h" +#include "stdlib.h" +#include "dihedral_charmm.h" +#include "atom.h" +#include "comm.h" +#include "neighbor.h" +#include "domain.h" +#include "force.h" +#include "pair_lj_charmm_coul_charmm.h" +#include "pair_lj_charmm_coul_charmm_implicit.h" +#include "pair_lj_charmm_coul_long.h" +#include "update.h" +#include "memory.h" +#include "error.h" + +#define TOLERANCE 0.05 +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +DihedralCharmm::~DihedralCharmm() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(multiplicity); + memory->sfree(shift); + memory->sfree(cos_shift); + memory->sfree(sin_shift); + memory->sfree(weight); + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralCharmm::compute(int eflag, int vflag) +{ + int i,m,n,i1,i2,i3,i4,type,factor; + double rfactor; + double vb1x,vb1y,vb1z,vb2x,vb2y; + double vb2z,vb2xm,vb2ym,vb2zm,vb3x,vb3y,vb3z; + double ax,ay,az,bx,by,bz,rasq,rbsq,rgsq,rg,rginv,ra2inv,rb2inv,rabinv; + double df,df1,ddf1,fg,hg,fga,hgb,gaa,gbb; + double dtfx,dtfy,dtfz,dtgx,dtgy,dtgz,dthx,dthy,dthz; + double c,s,p,sx1,sx2,sx12,sy1,sy2,sy12,sz1,sz2,sz12; + int itype,jtype; + double delx,dely,delz,rsq,r2inv,r6inv; + double fforce,forcecoul,forcelj,phicoul,philj; + + energy = 0.0; + eng_coul = eng_vdwl = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + double *q = atom->q; + int *atomtype = atom->type; + int **dihedrallist = neighbor->dihedrallist; + int ndihedrallist = neighbor->ndihedrallist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + double qqrd2e = force->qqrd2e; + + for (n = 0; n < ndihedrallist; n++) { + + i1 = dihedrallist[n][0]; + i2 = dihedrallist[n][1]; + i3 = dihedrallist[n][2]; + i4 = dihedrallist[n][3]; + type = dihedrallist[n][4]; + + if (newton_bond) factor = 4; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + if (i4 < nlocal) factor++; + } + rfactor = 0.25 * factor; + + // 1st bond + + vb1x = x[i1][0] - x[i2][0]; + vb1y = x[i1][1] - x[i2][1]; + vb1z = x[i1][2] - x[i2][2]; + domain->minimum_image(&vb1x,&vb1y,&vb1z); + + // 2nd bond + + vb2x = x[i3][0] - x[i2][0]; + vb2y = x[i3][1] - x[i2][1]; + vb2z = x[i3][2] - x[i2][2]; + domain->minimum_image(&vb2x,&vb2y,&vb2z); + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + domain->minimum_image(&vb2xm,&vb2ym,&vb2zm); + + // 3rd bond + + vb3x = x[i4][0] - x[i3][0]; + vb3y = x[i4][1] - x[i3][1]; + vb3z = x[i4][2] - x[i3][2]; + domain->minimum_image(&vb3x,&vb3y,&vb3z); + + ax = vb1y*vb2zm - vb1z*vb2ym; + ay = vb1z*vb2xm - vb1x*vb2zm; + az = vb1x*vb2ym - vb1y*vb2xm; + bx = vb3y*vb2zm - vb3z*vb2ym; + by = vb3z*vb2xm - vb3x*vb2zm; + bz = vb3x*vb2ym - vb3y*vb2xm; + + rasq = ax*ax + ay*ay + az*az; + rbsq = bx*bx + by*by + bz*bz; + rgsq = vb2xm*vb2xm + vb2ym*vb2ym + vb2zm*vb2zm; + rg = sqrt(rgsq); + + rginv = ra2inv = rb2inv = 0.0; + if (rg > 0) rginv = 1.0/rg; + if (rasq > 0) ra2inv = 1.0/rasq; + if (rbsq > 0) rb2inv = 1.0/rbsq; + rabinv = sqrt(ra2inv*rb2inv); + + c = (ax*bx + ay*by + az*bz)*rabinv; + s = rg*rabinv*(ax*vb3x + ay*vb3y + az*vb3z); + + // error check + + if (c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) { + int me; + MPI_Comm_rank(world,&me); + if (screen) { + fprintf(screen,"Dihedral problem: %d %d %d %d %d %d\n", + me,update->ntimestep, + atom->tag[i1],atom->tag[i2],atom->tag[i3],atom->tag[i4]); + fprintf(screen," 1st atom: %d %g %g %g\n", + me,x[i1][0],x[i1][1],x[i1][2]); + fprintf(screen," 2nd atom: %d %g %g %g\n", + me,x[i2][0],x[i2][1],x[i2][2]); + fprintf(screen," 3rd atom: %d %g %g %g\n", + me,x[i3][0],x[i3][1],x[i3][2]); + fprintf(screen," 4th atom: %d %g %g %g\n", + me,x[i4][0],x[i4][1],x[i4][2]); + } + } + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + m = multiplicity[type]; + p = 1.0; + df1 = 0.0; + + for (i = 0; i < m; i++) { + ddf1 = p*c - df1*s; + df1 = p*s + df1*c; + p = ddf1; + } + + p = p*cos_shift[type] + df1*sin_shift[type]; + df1 = df1*cos_shift[type] - ddf1*sin_shift[type]; + df1 *= -m; + p += 1.0; + + if (m == 0) { + p = 1.0 + cos_shift[type]; + df1 = 0.0; + } + + if (eflag) energy += rfactor * k[type] * p; + + fg = vb1x*vb2xm + vb1y*vb2ym + vb1z*vb2zm; + hg = vb3x*vb2xm + vb3y*vb2ym + vb3z*vb2zm; + fga = fg*ra2inv*rginv; + hgb = hg*rb2inv*rginv; + gaa = -ra2inv*rg; + gbb = rb2inv*rg; + + dtfx = gaa*ax; + dtfy = gaa*ay; + dtfz = gaa*az; + dtgx = fga*ax - hgb*bx; + dtgy = fga*ay - hgb*by; + dtgz = fga*az - hgb*bz; + dthx = gbb*bx; + dthy = gbb*by; + dthz = gbb*bz; + + df = k[type] * df1; + + sx1 = df*dtfx; + sy1 = df*dtfy; + sz1 = df*dtfz; + sx2 = -df*dtgx; + sy2 = -df*dtgy; + sz2 = -df*dtgz; + sx12 = df*dthx; + sy12 = df*dthy; + sz12 = df*dthz; + + // apply force to each of 4 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= sx1; + f[i1][1] -= sy1; + f[i1][2] -= sz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += sx2 + sx1; + f[i2][1] += sy2 + sy1; + f[i2][2] += sz2 + sz1; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] += sx12 - sx2; + f[i3][1] += sy12 - sy2; + f[i3][2] += sz12 - sz2; + } + + if (newton_bond || i4 < nlocal) { + f[i4][0] -= sx12; + f[i4][1] -= sy12; + f[i4][2] -= sz12; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (vb1x*sx1 + vb2x*sx2 + vb3x*sx12); + virial[1] -= rfactor * (vb1y*sy1 + vb2y*sy2 + vb3y*sy12); + virial[2] -= rfactor * (vb1z*sz1 + vb2z*sz2 + vb3z*sz12); + virial[3] -= rfactor * (vb1x*sy1 + vb2x*sy2 + vb3x*sy12); + virial[4] -= rfactor * (vb1x*sz1 + vb2x*sz2 + vb3x*sz12); + virial[5] -= rfactor * (vb1y*sz1 + vb2y*sz2 + vb3y*sz12); + } + + // 1-4 LJ and Coulomb interactions + // force, energy, and virial + + if (weight[type] > 0.0) { + + itype = atomtype[i1]; + jtype = atomtype[i4]; + + delx = x[i1][0] - x[i4][0]; + dely = x[i1][1] - x[i4][1]; + delz = x[i1][2] - x[i4][2]; + domain->minimum_image(&delx,&dely,&delz); + rsq = delx*delx + dely*dely + delz*delz; + r2inv = 1.0/rsq; + r6inv = r2inv*r2inv*r2inv; + + if (implicitflag) forcecoul = qqrd2e * q[i1]*q[i4]*r2inv; + else forcecoul = qqrd2e * q[i1]*q[i4]*sqrt(r2inv); + forcelj = r6inv * (lj14_1[itype][jtype]*r6inv - lj14_2[itype][jtype]); + fforce = weight[type] * (forcelj+forcecoul)*r2inv; + + if (eflag) { + phicoul = weight[type] * rfactor * forcecoul; + philj = r6inv * (lj14_3[itype][jtype]*r6inv - lj14_4[itype][jtype]); + philj = weight[type] * rfactor * philj; + eng_coul += phicoul; + eng_vdwl += philj; + } + + if (newton_bond || i1 < nlocal) { + f[i1][0] += delx*fforce; + f[i1][1] += dely*fforce; + f[i1][2] += delz*fforce; + } + if (newton_bond || i4 < nlocal) { + f[i4][0] -= delx*fforce; + f[i4][1] -= dely*fforce; + f[i4][2] -= delz*fforce; + } + + if (vflag) { + virial[0] += rfactor * delx*delx*fforce; + virial[1] += rfactor * dely*dely*fforce; + virial[2] += rfactor * delz*delz*fforce; + virial[3] += rfactor * delx*dely*fforce; + virial[4] += rfactor * delx*delz*fforce; + virial[5] += rfactor * dely*delz*fforce; + } + } + + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralCharmm::allocate() +{ + allocated = 1; + int n = atom->ndihedraltypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:k"); + multiplicity = (int *) + memory->smalloc((n+1)*sizeof(double),"dihedral:multiplicity"); + shift = (int *) + memory->smalloc((n+1)*sizeof(double),"dihedral:shift"); + cos_shift = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:cos_shift"); + sin_shift = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:sin_shift"); + weight = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:weight"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"dihedral:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void DihedralCharmm::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this dihedral style"); + if (narg != 5) error->all("Incorrect args for dihedral coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->ndihedraltypes,ilo,ihi); + + // require integer values of shift for backwards compatibility + // arbitrary phase angle shift could be allowed, but would break + // backwards compatibility and is probably not needed + + double k_one = atof(arg[1]); + int multiplicity_one = atoi(arg[2]); + int shift_one = atoi(arg[3]); + double weight_one = atof(arg[4]); + + if (multiplicity_one < 0) + error->all("Incorrect multiplicity arg for dihedral coefficients"); + if (weight_one < 0.0 || weight_one > 1.0) + error->all("Incorrect weight arg for dihedral coefficients"); + + double PI = 4.0*atan(1.0); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + shift[i] = shift_one; + cos_shift[i] = cos(PI*shift_one/180.0); + sin_shift[i] = sin(PI*shift_one/180.0); + multiplicity[i] = multiplicity_one; + weight[i] = weight_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for dihedral coefficients"); +} + +/* ---------------------------------------------------------------------- + error check and initialize all values needed for force computation +------------------------------------------------------------------------- */ + +void DihedralCharmm::init_style() +{ + // set local ptrs to LJ 14 arrays setup by pair + + Pair *anypair; + if (anypair = force->pair_match("lj/charmm/coul/charmm")) { + PairLJCharmmCoulCharmm *pair = (PairLJCharmmCoulCharmm *) anypair; + lj14_1 = pair->lj14_1; + lj14_2 = pair->lj14_2; + lj14_3 = pair->lj14_3; + lj14_4 = pair->lj14_4; + implicitflag = 0; + } else if (anypair = force->pair_match("lj/charmm/coul/charmm/implicit")) { + PairLJCharmmCoulCharmmImplicit *pair = + (PairLJCharmmCoulCharmmImplicit *) anypair; + lj14_1 = pair->lj14_1; + lj14_2 = pair->lj14_2; + lj14_3 = pair->lj14_3; + lj14_4 = pair->lj14_4; + implicitflag = 1; + } else if (anypair = force->pair_match("lj/charmm/coul/long")) { + PairLJCharmmCoulLong *pair = (PairLJCharmmCoulLong *) anypair; + lj14_1 = pair->lj14_1; + lj14_2 = pair->lj14_2; + lj14_3 = pair->lj14_3; + lj14_4 = pair->lj14_4; + implicitflag = 0; + } else error->all("Pair style is incompatible with DihedralCharmm"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void DihedralCharmm::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&multiplicity[1],sizeof(int),atom->ndihedraltypes,fp); + fwrite(&shift[1],sizeof(int),atom->ndihedraltypes,fp); + fwrite(&weight[1],sizeof(double),atom->ndihedraltypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void DihedralCharmm::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&multiplicity[1],sizeof(int),atom->ndihedraltypes,fp); + fread(&shift[1],sizeof(int),atom->ndihedraltypes,fp); + fread(&weight[1],sizeof(double),atom->ndihedraltypes,fp); + } + MPI_Bcast(&k[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&multiplicity[1],atom->ndihedraltypes,MPI_INT,0,world); + MPI_Bcast(&shift[1],atom->ndihedraltypes,MPI_INT,0,world); + MPI_Bcast(&weight[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + + double PI = 4.0*atan(1.0); + for (int i = 1; i <= atom->ndihedraltypes; i++) { + setflag[i] = 1; + cos_shift[i] = cos(PI*shift[i]/180.0); + sin_shift[i] = sin(PI*shift[i]/180.0); + } +} diff --git a/src/dihedral_charmm.h b/src/dihedral_charmm.h new file mode 100644 index 0000000000000000000000000000000000000000..107818633d2cfb9110d2e532405a641236b984d2 --- /dev/null +++ b/src/dihedral_charmm.h @@ -0,0 +1,39 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DIHEDRAL_CHARMM_H +#define DIHEDRAL_CHARMM_H + +#include "stdio.h" +#include "dihedral.h" + +class DihedralCharmm : public Dihedral { + public: + DihedralCharmm() {} + ~DihedralCharmm(); + void compute(int, int); + void coeff(int, int, char **); + void init_style(); + void write_restart(FILE *); + void read_restart(FILE *); + + private: + double *k,*weight,*cos_shift,*sin_shift; + int *multiplicity,*shift; + double **lj14_1,**lj14_2,**lj14_3,**lj14_4; + int implicitflag; + + void allocate(); +}; + +#endif diff --git a/src/dihedral_harmonic.cpp b/src/dihedral_harmonic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8762262bdf632db45a35ad192ea238ce6cfa05ee --- /dev/null +++ b/src/dihedral_harmonic.cpp @@ -0,0 +1,356 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "mpi.h" +#include "math.h" +#include "stdlib.h" +#include "dihedral_harmonic.h" +#include "atom.h" +#include "comm.h" +#include "neighbor.h" +#include "domain.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "error.h" + +#define TOLERANCE 0.05 +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +DihedralHarmonic::~DihedralHarmonic() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(sign); + memory->sfree(multiplicity); + memory->sfree(cos_shift); + memory->sfree(sin_shift); + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralHarmonic::compute(int eflag, int vflag) +{ + int i,m,n,i1,i2,i3,i4,type,factor; + double rfactor; + double vb1x,vb1y,vb1z,vb2x,vb2y; + double vb2z,vb2xm,vb2ym,vb2zm,vb3x,vb3y,vb3z; + double ax,ay,az,bx,by,bz,rasq,rbsq,rgsq,rg,rginv,ra2inv,rb2inv,rabinv; + double df,df1,ddf1,fg,hg,fga,hgb,gaa,gbb; + double dtfx,dtfy,dtfz,dtgx,dtgy,dtgz,dthx,dthy,dthz; + double c,s,p,sx1,sx2,sx12,sy1,sy2,sy12,sz1,sz2,sz12; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **dihedrallist = neighbor->dihedrallist; + int ndihedrallist = neighbor->ndihedrallist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < ndihedrallist; n++) { + + i1 = dihedrallist[n][0]; + i2 = dihedrallist[n][1]; + i3 = dihedrallist[n][2]; + i4 = dihedrallist[n][3]; + type = dihedrallist[n][4]; + + if (newton_bond) factor = 4; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + if (i4 < nlocal) factor++; + } + rfactor = 0.25 * factor; + + // 1st bond + + vb1x = x[i1][0] - x[i2][0]; + vb1y = x[i1][1] - x[i2][1]; + vb1z = x[i1][2] - x[i2][2]; + domain->minimum_image(&vb1x,&vb1y,&vb1z); + + // 2nd bond + + vb2x = x[i3][0] - x[i2][0]; + vb2y = x[i3][1] - x[i2][1]; + vb2z = x[i3][2] - x[i2][2]; + domain->minimum_image(&vb2x,&vb2y,&vb2z); + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + domain->minimum_image(&vb2xm,&vb2ym,&vb2zm); + + // 3rd bond + + vb3x = x[i4][0] - x[i3][0]; + vb3y = x[i4][1] - x[i3][1]; + vb3z = x[i4][2] - x[i3][2]; + domain->minimum_image(&vb3x,&vb3y,&vb3z); + + // c,s calculation + + ax = vb1y*vb2zm - vb1z*vb2ym; + ay = vb1z*vb2xm - vb1x*vb2zm; + az = vb1x*vb2ym - vb1y*vb2xm; + bx = vb3y*vb2zm - vb3z*vb2ym; + by = vb3z*vb2xm - vb3x*vb2zm; + bz = vb3x*vb2ym - vb3y*vb2xm; + + rasq = ax*ax + ay*ay + az*az; + rbsq = bx*bx + by*by + bz*bz; + rgsq = vb2xm*vb2xm + vb2ym*vb2ym + vb2zm*vb2zm; + rg = sqrt(rgsq); + + rginv = ra2inv = rb2inv = 0.0; + if (rg > 0) rginv = 1.0/rg; + if (rasq > 0) ra2inv = 1.0/rasq; + if (rbsq > 0) rb2inv = 1.0/rbsq; + rabinv = sqrt(ra2inv*rb2inv); + + c = (ax*bx + ay*by + az*bz)*rabinv; + s = rg*rabinv*(ax*vb3x + ay*vb3y + az*vb3z); + + // error check + + if (c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) { + int me; + MPI_Comm_rank(world,&me); + if (screen) { + fprintf(screen,"Dihedral problem: %d %d %d %d %d %d\n", + me,update->ntimestep, + atom->tag[i1],atom->tag[i2],atom->tag[i3],atom->tag[i4]); + fprintf(screen," 1st atom: %d %g %g %g\n", + me,x[i1][0],x[i1][1],x[i1][2]); + fprintf(screen," 2nd atom: %d %g %g %g\n", + me,x[i2][0],x[i2][1],x[i2][2]); + fprintf(screen," 3rd atom: %d %g %g %g\n", + me,x[i3][0],x[i3][1],x[i3][2]); + fprintf(screen," 4th atom: %d %g %g %g\n", + me,x[i4][0],x[i4][1],x[i4][2]); + } + } + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + m = multiplicity[type]; + p = 1.0; + df1 = 0.0; + + for (i = 0; i < m; i++) { + ddf1 = p*c - df1*s; + df1 = p*s + df1*c; + p = ddf1; + } + + p = p*cos_shift[type] + df1*sin_shift[type]; + df1 = df1*cos_shift[type] - ddf1*sin_shift[type]; + df1 *= -m; + p += 1.0; + + if (m == 0) { + p = 1.0 + cos_shift[type]; + df1 = 0.0; + } + + if (eflag) energy += rfactor * k[type] * p; + + fg = vb1x*vb2xm + vb1y*vb2ym + vb1z*vb2zm; + hg = vb3x*vb2xm + vb3y*vb2ym + vb3z*vb2zm; + fga = fg*ra2inv*rginv; + hgb = hg*rb2inv*rginv; + gaa = -ra2inv*rg; + gbb = rb2inv*rg; + + dtfx = gaa*ax; + dtfy = gaa*ay; + dtfz = gaa*az; + dtgx = fga*ax - hgb*bx; + dtgy = fga*ay - hgb*by; + dtgz = fga*az - hgb*bz; + dthx = gbb*bx; + dthy = gbb*by; + dthz = gbb*bz; + + df = k[type] * df1; + + sx1 = df*dtfx; + sy1 = df*dtfy; + sz1 = df*dtfz; + sx2 = -df*dtgx; + sy2 = -df*dtgy; + sz2 = -df*dtgz; + sx12 = df*dthx; + sy12 = df*dthy; + sz12 = df*dthz; + + // apply force to each of 4 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= sx1; + f[i1][1] -= sy1; + f[i1][2] -= sz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += sx2 + sx1; + f[i2][1] += sy2 + sy1; + f[i2][2] += sz2 + sz1; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] += sx12 - sx2; + f[i3][1] += sy12 - sy2; + f[i3][2] += sz12 - sz2; + } + + if (newton_bond || i4 < nlocal) { + f[i4][0] -= sx12; + f[i4][1] -= sy12; + f[i4][2] -= sz12; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (vb1x*sx1 + vb2x*sx2 + vb3x*sx12); + virial[1] -= rfactor * (vb1y*sy1 + vb2y*sy2 + vb3y*sy12); + virial[2] -= rfactor * (vb1z*sz1 + vb2z*sz2 + vb3z*sz12); + virial[3] -= rfactor * (vb1x*sy1 + vb2x*sy2 + vb3x*sy12); + virial[4] -= rfactor * (vb1x*sz1 + vb2x*sz2 + vb3x*sz12); + virial[5] -= rfactor * (vb1y*sz1 + vb2y*sz2 + vb3y*sz12); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralHarmonic::allocate() +{ + allocated = 1; + int n = atom->ndihedraltypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:k"); + sign = (int *) memory->smalloc((n+1)*sizeof(double),"dihedral:sign"); + multiplicity = (int *) + memory->smalloc((n+1)*sizeof(double),"dihedral:multiplicity"); + cos_shift = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:cos_shift"); + sin_shift = (double *) + memory->smalloc((n+1)*sizeof(double),"dihedral:sin_shift"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"dihedral:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void DihedralHarmonic::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this dihedral style"); + if (narg != 4) error->all("Incorrect args for dihedral coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->ndihedraltypes,ilo,ihi); + + double k_one = atof(arg[1]); + int sign_one = atoi(arg[2]); + int multiplicity_one = atoi(arg[3]); + + // require sign = +/- 1 for backwards compatibility + // arbitrary phase angle shift could be allowed, but would break + // backwards compatibility and is probably not needed + + if (sign_one != -1 && sign_one != 1) + error->all("Incorrect sign arg for dihedral coefficients"); + if (multiplicity_one < 0) + error->all("Incorrect multiplicity arg for dihedral coefficients"); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + sign[i] = sign_one; + if (sign[i] == 1) { + cos_shift[i] = 1; + sin_shift[i] = 0; + } else { + cos_shift[i] = -1; + sin_shift[i] = 0; + } + multiplicity[i] = multiplicity_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for dihedral coefficients"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void DihedralHarmonic::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&sign[1],sizeof(int),atom->ndihedraltypes,fp); + fwrite(&multiplicity[1],sizeof(int),atom->ndihedraltypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void DihedralHarmonic::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&sign[1],sizeof(int),atom->ndihedraltypes,fp); + fread(&multiplicity[1],sizeof(int),atom->ndihedraltypes,fp); + } + MPI_Bcast(&k[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&sign[1],atom->ndihedraltypes,MPI_INT,0,world); + MPI_Bcast(&multiplicity[1],atom->ndihedraltypes,MPI_INT,0,world); + + for (int i = 1; i <= atom->ndihedraltypes; i++) { + setflag[i] = 1; + if (sign[i] == 1) { + cos_shift[i] = 1; + sin_shift[i] = 0; + } else { + cos_shift[i] = -1; + sin_shift[i] = 0; + } + } +} diff --git a/src/dihedral_harmonic.h b/src/dihedral_harmonic.h new file mode 100644 index 0000000000000000000000000000000000000000..e0ddac0882a06249e69d49e3e86e934d733ab52b --- /dev/null +++ b/src/dihedral_harmonic.h @@ -0,0 +1,36 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DIHEDRAL_HARMONIC_H +#define DIHEDRAL_HARMONIC_H + +#include "stdio.h" +#include "dihedral.h" + +class DihedralHarmonic : public Dihedral { + public: + DihedralHarmonic() {} + ~DihedralHarmonic(); + void compute(int, int); + void coeff(int, int, char **); + void write_restart(FILE *); + void read_restart(FILE *); + + private: + double *k,*cos_shift,*sin_shift; + int *sign,*multiplicity; + + void allocate(); +}; + +#endif diff --git a/src/dihedral_helix.cpp b/src/dihedral_helix.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ba6f7c7e332fb017c49ddb9fc739d26711b47813 --- /dev/null +++ b/src/dihedral_helix.cpp @@ -0,0 +1,340 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Naveen Michaud-Agrawal (Johns Hopkins U) and + Mark Stevens (Sandia) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "mpi.h" +#include "dihedral_helix.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "error.h" + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +#define TOLERANCE 0.05 +#define SMALL 0.001 +#define SMALLER 0.00001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +DihedralHelix::~DihedralHelix() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(aphi); + memory->sfree(bphi); + memory->sfree(cphi); + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralHelix::compute(int eflag, int vflag) +{ + int n,i1,i2,i3,i4,type,factor; + double rfactor; + double vb1x,vb1y,vb1z,vb2x,vb2y; + double vb2z,vb2xm,vb2ym,vb2zm,vb3x,vb3y,vb3z,sb1; + double sb2,sb3,rb1,rb3,c0,b1mag2,b1mag,b2mag2; + double b2mag,b3mag2,b3mag,ctmp,r12c1,c1mag,r12c2; + double c2mag,sc1,sc2,s1,s12,c,p,pd,a,a11,a22; + double a33,a12,a13,a23,sx1,sx2,sx12,sy1,sy2,sy12; + double sz1,sz2,sz12,s2,cx,cy,cz,cmag,dx,phi,si,siinv,sin2; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **dihedrallist = neighbor->dihedrallist; + int ndihedrallist = neighbor->ndihedrallist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < ndihedrallist; n++) { + + i1 = dihedrallist[n][0]; + i2 = dihedrallist[n][1]; + i3 = dihedrallist[n][2]; + i4 = dihedrallist[n][3]; + type = dihedrallist[n][4]; + + if (newton_bond) factor = 4; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + if (i4 < nlocal) factor++; + } + rfactor = 0.25 * factor; + + // 1st bond + + vb1x = x[i1][0] - x[i2][0]; + vb1y = x[i1][1] - x[i2][1]; + vb1z = x[i1][2] - x[i2][2]; + domain->minimum_image(&vb1x,&vb1y,&vb1z); + + // 2nd bond + + vb2x = x[i3][0] - x[i2][0]; + vb2y = x[i3][1] - x[i2][1]; + vb2z = x[i3][2] - x[i2][2]; + domain->minimum_image(&vb2x,&vb2y,&vb2z); + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + domain->minimum_image(&vb2xm,&vb2ym,&vb2zm); + + // 3rd bond + + vb3x = x[i4][0] - x[i3][0]; + vb3y = x[i4][1] - x[i3][1]; + vb3z = x[i4][2] - x[i3][2]; + domain->minimum_image(&vb3x,&vb3y,&vb3z); + + // c0 calculation + + sb1 = 1.0 / (vb1x*vb1x + vb1y*vb1y + vb1z*vb1z); + sb2 = 1.0 / (vb2x*vb2x + vb2y*vb2y + vb2z*vb2z); + sb3 = 1.0 / (vb3x*vb3x + vb3y*vb3y + vb3z*vb3z); + + rb1 = sqrt(sb1); + rb3 = sqrt(sb3); + + c0 = (vb1x*vb3x + vb1y*vb3y + vb1z*vb3z) * rb1*rb3; + + // 1st and 2nd angle + + b1mag2 = vb1x*vb1x + vb1y*vb1y + vb1z*vb1z; + b1mag = sqrt(b1mag2); + b2mag2 = vb2x*vb2x + vb2y*vb2y + vb2z*vb2z; + b2mag = sqrt(b2mag2); + b3mag2 = vb3x*vb3x + vb3y*vb3y + vb3z*vb3z; + b3mag = sqrt(b3mag2); + + ctmp = vb1x*vb2x + vb1y*vb2y + vb1z*vb2z; + r12c1 = 1.0 / (b1mag*b2mag); + c1mag = ctmp * r12c1; + + ctmp = vb2xm*vb3x + vb2ym*vb3y + vb2zm*vb3z; + r12c2 = 1.0 / (b2mag*b3mag); + c2mag = ctmp * r12c2; + + // cos and sin of 2 angles and final c + + sin2 = MAX(1.0 - c1mag*c1mag,0.0); + sc1 = sqrt(sin2); + if (sc1 < SMALL) sc1 = SMALL; + sc1 = 1.0/sc1; + + sin2 = MAX(1.0 - c2mag*c2mag,0.0); + sc2 = sqrt(sin2); + if (sc2 < SMALL) sc2 = SMALL; + sc2 = 1.0/sc2; + + s1 = sc1 * sc1; + s2 = sc2 * sc2; + s12 = sc1 * sc2; + c = (c0 + c1mag*c2mag) * s12; + + cx = vb1y*vb2z - vb1z*vb2y; + cy = vb1z*vb2x - vb1x*vb2z; + cz = vb1x*vb2y - vb1y*vb2x; + cmag = sqrt(cx*cx + cy*cy + cz*cz); + dx = (cx*vb3x + cy*vb3y + cz*vb3z)/cmag/b3mag; + + // error check + + if (c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) { + int me; + MPI_Comm_rank(world,&me); + if (screen) { + fprintf(screen,"Dihedral problem: %d %d %d %d %d %d\n", + me,update->ntimestep, + atom->tag[i1],atom->tag[i2],atom->tag[i3],atom->tag[i4]); + fprintf(screen," 1st atom: %d %g %g %g\n", + me,x[i1][0],x[i1][1],x[i1][2]); + fprintf(screen," 2nd atom: %d %g %g %g\n", + me,x[i2][0],x[i2][1],x[i2][2]); + fprintf(screen," 3rd atom: %d %g %g %g\n", + me,x[i3][0],x[i3][1],x[i3][2]); + fprintf(screen," 4th atom: %d %g %g %g\n", + me,x[i4][0],x[i4][1],x[i4][2]); + } + } + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + phi = acos(c); + if (dx < 0.0) phi *= -1.0; + si = sin(phi); + if (fabs(si) < SMALLER) si = SMALLER; + siinv = 1.0/si; + + p = aphi[type]*(1.0 - c) + bphi[type]*(1.0 + cos(3.0*phi)) + + cphi[type]*(1.0 + cos(phi + 0.25*PI)); + pd = -aphi[type] + 3.0*bphi[type]*sin(3.0*phi)*siinv + + cphi[type]*sin(phi + 0.25*PI)*siinv; + + if (eflag) energy += rfactor * p; + + a = pd; + c = c * a; + s12 = s12 * a; + a11 = -c*sb1*s1; + a22 = sb2 * (2.0*c0*s12 - c*(s1+s2)); + a33 = -c*sb3*s2; + a12 = r12c1 * (c1mag*c*s1 + c2mag*s12); + a13 = rb1*rb3*s12; + a23 = r12c2 * (-c2mag*c*s2 - c1mag*s12); + + sx1 = a11*vb1x + a12*vb2x + a13*vb3x; + sx2 = a12*vb1x + a22*vb2x + a23*vb3x; + sx12 = a13*vb1x + a23*vb2x + a33*vb3x; + sy1 = a11*vb1y + a12*vb2y + a13*vb3y; + sy2 = a12*vb1y + a22*vb2y + a23*vb3y; + sy12 = a13*vb1y + a23*vb2y + a33*vb3y; + sz1 = a11*vb1z + a12*vb2z + a13*vb3z; + sz2 = a12*vb1z + a22*vb2z + a23*vb3z; + sz12 = a13*vb1z + a23*vb2z + a33*vb3z; + + // apply force to each of 4 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= sx1; + f[i1][1] -= sy1; + f[i1][2] -= sz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += sx2 + sx1; + f[i2][1] += sy2 + sy1; + f[i2][2] += sz2 + sz1; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] += sx12 - sx2; + f[i3][1] += sy12 - sy2; + f[i3][2] += sz12 - sz2; + } + + if (newton_bond || i4 < nlocal) { + f[i4][0] -= sx12; + f[i4][1] -= sy12; + f[i4][2] -= sz12; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (vb1x*sx1 + vb2x*sx2 + vb3x*sx12); + virial[1] -= rfactor * (vb1y*sy1 + vb2y*sy2 + vb3y*sy12); + virial[2] -= rfactor * (vb1z*sz1 + vb2z*sz2 + vb3z*sz12); + virial[3] -= rfactor * (vb1x*sy1 + vb2x*sy2 + vb3x*sy12); + virial[4] -= rfactor * (vb1x*sz1 + vb2x*sz2 + vb3x*sz12); + virial[5] -= rfactor * (vb1y*sz1 + vb2y*sz2 + vb3y*sz12); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralHelix::allocate() +{ + allocated = 1; + int n = atom->ndihedraltypes; + + aphi = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:aphi"); + bphi = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:bphi"); + cphi = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:cphi"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"dihedral:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs from one line in input script +------------------------------------------------------------------------- */ + +void DihedralHelix::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this dihedral style"); + if (narg != 4) error->all("Incorrect args for dihedral coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->ndihedraltypes,ilo,ihi); + + double aphi_one = atof(arg[1]); + double bphi_one = atof(arg[2]); + double cphi_one = atof(arg[3]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + aphi[i] = aphi_one; + bphi[i] = bphi_one; + cphi[i] = cphi_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for dihedral coefficients"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void DihedralHelix::write_restart(FILE *fp) +{ + fwrite(&aphi[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&bphi[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&cphi[1],sizeof(double),atom->ndihedraltypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void DihedralHelix::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&aphi[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&bphi[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&cphi[1],sizeof(double),atom->ndihedraltypes,fp); + } + MPI_Bcast(&aphi[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&bphi[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&cphi[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->ndihedraltypes; i++) setflag[i] = 1; +} diff --git a/src/dihedral_helix.h b/src/dihedral_helix.h new file mode 100644 index 0000000000000000000000000000000000000000..85481b4318e489f0df403b2e27b82e1e2f7a6d49 --- /dev/null +++ b/src/dihedral_helix.h @@ -0,0 +1,35 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DIHEDRAL_HELIX_H +#define DIHEDRAL_HELIX_H + +#include "stdio.h" +#include "dihedral.h" + +class DihedralHelix : public Dihedral { + public: + DihedralHelix() {} + ~DihedralHelix(); + void compute(int, int); + void coeff(int, int, char **); + void write_restart(FILE *); + void read_restart(FILE *); + + private: + double *aphi,*bphi,*cphi; + + void allocate(); +}; + +#endif diff --git a/src/dihedral_hybrid.cpp b/src/dihedral_hybrid.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dea4ce72fcbe71762a14bd4db678e689fb4ea291 --- /dev/null +++ b/src/dihedral_hybrid.cpp @@ -0,0 +1,259 @@ +/* ----------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "string.h" +#include "dihedral_hybrid.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define EXTRA 1000 + +/* ---------------------------------------------------------------------- + set all global defaults +------------------------------------------------------------------------- */ + +DihedralHybrid::DihedralHybrid() +{ + nstyles = 0; +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +DihedralHybrid::~DihedralHybrid() +{ + if (nstyles) { + for (int i = 0; i < nstyles; i++) delete styles[i]; + delete [] styles; + for (int i = 0; i < nstyles; i++) delete [] keywords[i]; + delete [] keywords; + } + + if (allocated) { + memory->sfree(setflag); + memory->sfree(map); + delete [] ndihedrallist; + delete [] maxdihedral; + for (int i = 0; i < nstyles; i++) + memory->destroy_2d_int_array(dihedrallist[i]); + delete [] dihedrallist; + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralHybrid::compute(int eflag, int vflag) +{ + int i,m,n; + + // save ptrs to original dihedrallist + + int ndihedrallist_orig = neighbor->ndihedrallist; + int **dihedrallist_orig = neighbor->dihedrallist; + + // if this is re-neighbor step, create sub-style dihedrallists + // ndihedrallist[] = length of each sub-style list + // realloc sub-style dihedrallist if necessary + // load sub-style dihedrallist with 5 values from original dihedrallist + + if (neighbor->ago == 0) { + for (m = 0; m < nstyles; m++) ndihedrallist[m] = 0; + for (i = 0; i < ndihedrallist_orig; i++) + ndihedrallist[map[dihedrallist_orig[i][4]]]++; + for (m = 0; m < nstyles; m++) { + if (ndihedrallist[m] > maxdihedral[m]) { + memory->destroy_2d_int_array(dihedrallist[m]); + maxdihedral[m] = ndihedrallist[m] + EXTRA; + dihedrallist[m] = (int **) + memory->create_2d_int_array(maxdihedral[m],5, + "dihedral_hybrid:dihedrallist"); + } + ndihedrallist[m] = 0; + } + for (i = 0; i < ndihedrallist_orig; i++) { + m = map[dihedrallist_orig[i][4]]; + n = ndihedrallist[m]; + dihedrallist[m][n][0] = dihedrallist_orig[i][0]; + dihedrallist[m][n][1] = dihedrallist_orig[i][1]; + dihedrallist[m][n][2] = dihedrallist_orig[i][2]; + dihedrallist[m][n][3] = dihedrallist_orig[i][3]; + dihedrallist[m][n][4] = dihedrallist_orig[i][4]; + ndihedrallist[m]++; + } + } + + // call each sub-style's compute function + // must set neighbor->dihedrallist to sub-style dihedrallist before call + // accumulate sub-style energy,virial in hybrid's energy,virial + + energy = 0.0; + eng_vdwl = eng_coul = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + for (m = 0; m < nstyles; m++) { + if (styles[m] == NULL) continue; + neighbor->ndihedrallist = ndihedrallist[m]; + neighbor->dihedrallist = dihedrallist[m]; + styles[m]->compute(eflag,vflag); + if (eflag) { + energy += styles[m]->energy; + eng_vdwl += styles[m]->eng_vdwl; + eng_coul += styles[m]->eng_coul; + } + if (vflag) for (n = 0; n < 6; n++) virial[n] += styles[m]->virial[n]; + } + + // restore ptrs to original dihedrallist + + neighbor->ndihedrallist = ndihedrallist_orig; + neighbor->dihedrallist = dihedrallist_orig; +} + +/* ---------------------------------------------------------------------- */ + +void DihedralHybrid::allocate() +{ + allocated = 1; + int n = atom->ndihedraltypes; + + map = (int *) memory->smalloc((n+1)*sizeof(int),"dihedral:map"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"dihedral:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; + + ndihedrallist = new int[nstyles]; + maxdihedral = new int[nstyles]; + dihedrallist = new int**[nstyles]; + for (int m = 0; m < nstyles; m++) maxdihedral[m] = 0; + for (int m = 0; m < nstyles; m++) dihedrallist[m] = NULL; +} + +/* ---------------------------------------------------------------------- + create one dihedral style for each arg in list +------------------------------------------------------------------------- */ + +void DihedralHybrid::settings(int narg, char **arg) +{ + nstyles = narg; + styles = new Dihedral*[nstyles]; + keywords = new char*[nstyles]; + + for (int m = 0; m < nstyles; m++) { + for (int i = 0; i < m; i++) + if (strcmp(arg[m],arg[i]) == 0) + error->all("Dihedral style hybrid cannot use same dihedral style twice"); + if (strcmp(arg[m],"hybrid") == 0) + error->all("Dihedral style hybrid cannot have hybrid as an argument"); + styles[m] = force->new_dihedral(arg[m]); + keywords[m] = new char[strlen(arg[m])+1]; + strcpy(keywords[m],arg[m]); + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +---------------------------------------------------------------------- */ + +void DihedralHybrid::coeff(int which, int narg, char **arg) +{ + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->ndihedraltypes,ilo,ihi); + + // 2nd arg = dihedral style name (harmonic, etc) + + int m; + for (m = 0; m < nstyles; m++) + if (strcmp(arg[1],keywords[m]) == 0) break; + if (m == nstyles) error->all("Dihedral coeff for hybrid has invalid style"); + + // set low-level coefficients for each dihedraltype + // replace 2nd arg with i, call coeff() with no 1st arg + // if sub-style is NULL for "none", still set setflag + + for (int i = ilo; i <= ihi; i++) { + sprintf(arg[1],"%d",i); + map[i] = m; + if (styles[m]) styles[m]->coeff(which,narg-1,&arg[1]); + setflag[i] = 1; + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralHybrid::init_style() +{ + for (int m = 0; m < nstyles; m++) + if (styles[m]) styles[m]->init_style(); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void DihedralHybrid::write_restart(FILE *fp) +{ + fwrite(&nstyles,sizeof(int),1,fp); + + int n; + for (int m = 0; m < nstyles; m++) { + n = strlen(keywords[m]) + 1; + fwrite(&n,sizeof(int),1,fp); + fwrite(keywords[m],sizeof(char),n,fp); + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void DihedralHybrid::read_restart(FILE *fp) +{ + allocate(); + + int me = comm->me; + if (me == 0) fread(&nstyles,sizeof(int),1,fp); + MPI_Bcast(&nstyles,1,MPI_INT,0,world); + styles = new Dihedral*[nstyles]; + keywords = new char*[nstyles]; + + int n; + for (int m = 0; m < nstyles; m++) { + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + keywords[m] = new char[n]; + if (me == 0) fread(keywords[m],sizeof(char),n,fp); + MPI_Bcast(keywords[m],n,MPI_CHAR,0,world); + styles[m] = force->new_dihedral(keywords[m]); + } +} + +/* ---------------------------------------------------------------------- + memory usage +------------------------------------------------------------------------- */ + +int DihedralHybrid::memory_usage() +{ + int bytes = 0; + for (int m = 0; m < nstyles; m++) bytes += maxdihedral[m]*5 * sizeof(int); + for (int m = 0; m < nstyles; m++) + if (styles[m]) bytes += styles[m]->memory_usage(); + return bytes; +} diff --git a/src/dihedral_hybrid.h b/src/dihedral_hybrid.h new file mode 100644 index 0000000000000000000000000000000000000000..e6cc4713425f3a953e177d9b7e9eb8ef6cfa4021 --- /dev/null +++ b/src/dihedral_hybrid.h @@ -0,0 +1,45 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DIHEDRAL_HYBRID_H +#define DIHEDRAL_HYBRID_H + +#include "stdio.h" +#include "dihedral.h" + +class DihedralHybrid : public Dihedral { + public: + DihedralHybrid(); + ~DihedralHybrid(); + void compute(int, int); + void settings(int, char **); + void coeff(int, int, char **); + void init_style(); + void write_restart(FILE *); + void read_restart(FILE *); + int memory_usage(); + + private: + int nstyles; // # of different dihedral styles + Dihedral **styles; // class list for each Dihedral style + char **keywords; // keyword for each dihedral style + int *map; // which style each dihedral type points to + + int *ndihedrallist; // # of dihedrals in sub-style dihedrallists + int *maxdihedral; // max # of dihedrals sub-style lists can store + int ***dihedrallist; // dihedrallist for each sub-style + + void allocate(); +}; + +#endif diff --git a/src/dihedral_multi_harmonic.cpp b/src/dihedral_multi_harmonic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b3541f95210019d906f887e7930e50c04053f751 --- /dev/null +++ b/src/dihedral_multi_harmonic.cpp @@ -0,0 +1,339 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Mathias Puetz (SNL) and friends +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "dihedral_multi_harmonic.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "error.h" + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +#define TOLERANCE 0.05 +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +DihedralMultiHarmonic::~DihedralMultiHarmonic() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(a1); + memory->sfree(a2); + memory->sfree(a3); + memory->sfree(a4); + memory->sfree(a5); + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralMultiHarmonic::compute(int eflag, int vflag) +{ + int n,i1,i2,i3,i4,type,factor; + double rfactor; + double vb1x,vb1y,vb1z,vb2x,vb2y; + double vb2z,vb2xm,vb2ym,vb2zm,vb3x,vb3y,vb3z,sb1; + double sb2,sb3,rb1,rb3,c0,b1mag2,b1mag,b2mag2; + double b2mag,b3mag2,b3mag,ctmp,r12c1,c1mag,r12c2; + double c2mag,sc1,sc2,s1,s12,c,p,pd,a,a11,a22; + double a33,a12,a13,a23,sx1,sx2,sx12,sy1,sy2,sy12; + double sz1,sz2,sz12,s2,sin2; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **dihedrallist = neighbor->dihedrallist; + int ndihedrallist = neighbor->ndihedrallist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < ndihedrallist; n++) { + + i1 = dihedrallist[n][0]; + i2 = dihedrallist[n][1]; + i3 = dihedrallist[n][2]; + i4 = dihedrallist[n][3]; + type = dihedrallist[n][4]; + + if (newton_bond) factor = 4; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + if (i4 < nlocal) factor++; + } + rfactor = 0.25 * factor; + + // 1st bond + + vb1x = x[i1][0] - x[i2][0]; + vb1y = x[i1][1] - x[i2][1]; + vb1z = x[i1][2] - x[i2][2]; + domain->minimum_image(&vb1x,&vb1y,&vb1z); + + // 2nd bond + + vb2x = x[i3][0] - x[i2][0]; + vb2y = x[i3][1] - x[i2][1]; + vb2z = x[i3][2] - x[i2][2]; + domain->minimum_image(&vb2x,&vb2y,&vb2z); + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + domain->minimum_image(&vb2xm,&vb2ym,&vb2zm); + + // 3rd bond + + vb3x = x[i4][0] - x[i3][0]; + vb3y = x[i4][1] - x[i3][1]; + vb3z = x[i4][2] - x[i3][2]; + domain->minimum_image(&vb3x,&vb3y,&vb3z); + + // c0 calculation + + sb1 = 1.0 / (vb1x*vb1x + vb1y*vb1y + vb1z*vb1z); + sb2 = 1.0 / (vb2x*vb2x + vb2y*vb2y + vb2z*vb2z); + sb3 = 1.0 / (vb3x*vb3x + vb3y*vb3y + vb3z*vb3z); + + rb1 = sqrt(sb1); + rb3 = sqrt(sb3); + + c0 = (vb1x*vb3x + vb1y*vb3y + vb1z*vb3z) * rb1*rb3; + + // 1st and 2nd angle + + b1mag2 = vb1x*vb1x + vb1y*vb1y + vb1z*vb1z; + b1mag = sqrt(b1mag2); + b2mag2 = vb2x*vb2x + vb2y*vb2y + vb2z*vb2z; + b2mag = sqrt(b2mag2); + b3mag2 = vb3x*vb3x + vb3y*vb3y + vb3z*vb3z; + b3mag = sqrt(b3mag2); + + ctmp = vb1x*vb2x + vb1y*vb2y + vb1z*vb2z; + r12c1 = 1.0 / (b1mag*b2mag); + c1mag = ctmp * r12c1; + + ctmp = vb2xm*vb3x + vb2ym*vb3y + vb2zm*vb3z; + r12c2 = 1.0 / (b2mag*b3mag); + c2mag = ctmp * r12c2; + + // cos and sin of 2 angles and final c + + sin2 = MAX(1.0 - c1mag*c1mag,0.0); + sc1 = sqrt(sin2); + if (sc1 < SMALL) sc1 = SMALL; + sc1 = 1.0/sc1; + + sin2 = MAX(1.0 - c2mag*c2mag,0.0); + sc2 = sqrt(sin2); + if (sc2 < SMALL) sc2 = SMALL; + sc2 = 1.0/sc2; + + s1 = sc1 * sc1; + s2 = sc2 * sc2; + s12 = sc1 * sc2; + c = (c0 + c1mag*c2mag) * s12; + + // error check + + if (c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) { + if (screen) { + fprintf(screen,"Dihedral problem: %d %d %d %d %d %d\n", + comm->me,update->ntimestep, + atom->tag[i1],atom->tag[i2],atom->tag[i3],atom->tag[i4]); + fprintf(screen," 1st atom: %d %g %g %g\n", + comm->me,x[i1][0],x[i1][1],x[i1][2]); + fprintf(screen," 2nd atom: %d %g %g %g\n", + comm->me,x[i2][0],x[i2][1],x[i2][2]); + fprintf(screen," 3rd atom: %d %g %g %g\n", + comm->me,x[i3][0],x[i3][1],x[i3][2]); + fprintf(screen," 4th atom: %d %g %g %g\n", + comm->me,x[i4][0],x[i4][1],x[i4][2]); + } + } + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + // force & energy + // p = sum (i=1,5) a_i * c**(i-1) + // pd = dp/dc + + p = a1[type] + c*(a2[type] + c*(a3[type] + c*(a4[type] + c*a5[type]))); + pd = a2[type] + c*(2.0*a3[type] + c*(3.0*a4[type] + c*4.0*a5[type])); + + if (eflag) energy += rfactor * p; + + a = pd; + c = c * a; + s12 = s12 * a; + a11 = (-c*sb1*s1); + a22 = sb2*(2.0*c0*s12 - c*(s1+s2)); + a33 = (-c*sb3*s2); + a12 = r12c1*(c1mag*c*s1 + c2mag*s12); + a13 = rb1*rb3*s12; + a23 = r12c2*(-c2mag*c*s2 - c1mag*s12); + + sx1 = a11*vb1x + a12*vb2x + a13*vb3x; + sx2 = a12*vb1x + a22*vb2x + a23*vb3x; + sx12 = a13*vb1x + a23*vb2x + a33*vb3x; + sy1 = a11*vb1y + a12*vb2y + a13*vb3y; + sy2 = a12*vb1y + a22*vb2y + a23*vb3y; + sy12 = a13*vb1y + a23*vb2y + a33*vb3y; + sz1 = a11*vb1z + a12*vb2z + a13*vb3z; + sz2 = a12*vb1z + a22*vb2z + a23*vb3z; + sz12 = a13*vb1z + a23*vb2z + a33*vb3z; + + // apply force to each of 4 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= sx1; + f[i1][1] -= sy1; + f[i1][2] -= sz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += sx2 + sx1; + f[i2][1] += sy2 + sy1; + f[i2][2] += sz2 + sz1; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] += sx12 - sx2; + f[i3][1] += sy12 - sy2; + f[i3][2] += sz12 - sz2; + } + + if (newton_bond || i4 < nlocal) { + f[i4][0] -= sx12; + f[i4][1] -= sy12; + f[i4][2] -= sz12; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (vb1x*sx1 + vb2x*sx2 + vb3x*sx12); + virial[1] -= rfactor * (vb1y*sy1 + vb2y*sy2 + vb3y*sy12); + virial[2] -= rfactor * (vb1z*sz1 + vb2z*sz2 + vb3z*sz12); + virial[3] -= rfactor * (vb1x*sy1 + vb2x*sy2 + vb3x*sy12); + virial[4] -= rfactor * (vb1x*sz1 + vb2x*sz2 + vb3x*sz12); + virial[5] -= rfactor * (vb1y*sz1 + vb2y*sz2 + vb3y*sz12); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralMultiHarmonic::allocate() +{ + allocated = 1; + int n = atom->ndihedraltypes; + + a1 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:a1"); + a2 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:a2"); + a3 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:a3"); + a4 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:a4"); + a5 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:a5"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"dihedral:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void DihedralMultiHarmonic::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this dihedral style"); + if (narg != 6) error->all("Incorrect args for dihedral coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->ndihedraltypes,ilo,ihi); + + double a1_one = atof(arg[1]); + double a2_one = atof(arg[2]); + double a3_one = atof(arg[3]); + double a4_one = atof(arg[4]); + double a5_one = atof(arg[5]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + a1[i] = a1_one; + a2[i] = a2_one; + a3[i] = a3_one; + a4[i] = a4_one; + a5[i] = a5_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for dihedral coefficients"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void DihedralMultiHarmonic::write_restart(FILE *fp) +{ + fwrite(&a1[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&a2[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&a3[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&a4[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&a5[1],sizeof(double),atom->ndihedraltypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void DihedralMultiHarmonic::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&a1[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&a2[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&a3[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&a4[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&a5[1],sizeof(double),atom->ndihedraltypes,fp); + } + MPI_Bcast(&a1[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&a2[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&a3[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&a4[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&a5[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->ndihedraltypes; i++) setflag[i] = 1; +} diff --git a/src/dihedral_multi_harmonic.h b/src/dihedral_multi_harmonic.h new file mode 100644 index 0000000000000000000000000000000000000000..695cdfe1afde7864d1ea1b00316903db17a5ed84 --- /dev/null +++ b/src/dihedral_multi_harmonic.h @@ -0,0 +1,35 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DIHEDRAL_MULTI_HARMONIC_H +#define DIHEDRAL_MULTI_HARMONIC_H + +#include "stdio.h" +#include "dihedral.h" + +class DihedralMultiHarmonic : public Dihedral { + public: + DihedralMultiHarmonic() {} + ~DihedralMultiHarmonic(); + void compute(int, int); + void coeff(int, int, char **); + void write_restart(FILE *); + void read_restart(FILE *); + + private: + double *a1,*a2,*a3,*a4,*a5; + + void allocate(); +}; + +#endif diff --git a/src/dihedral_opls.cpp b/src/dihedral_opls.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eff17571fc56dcf716c61dec4094d1814ef5c216 --- /dev/null +++ b/src/dihedral_opls.cpp @@ -0,0 +1,349 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Mark Stevens (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "dihedral_opls.h" +#include "atom.h" +#include "comm.h" +#include "neighbor.h" +#include "domain.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "error.h" + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +#define TOLERANCE 0.05 +#define SMALL 0.001 +#define SMALLER 0.00001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +DihedralOPLS::~DihedralOPLS() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k1); + memory->sfree(k2); + memory->sfree(k3); + memory->sfree(k4); + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralOPLS::compute(int eflag, int vflag) +{ + int i,m,n,i1,i2,i3,i4,type,factor; + double rfactor; + double vb1x,vb1y,vb1z,vb2x,vb2y; + double vb2z,vb2xm,vb2ym,vb2zm,vb3x,vb3y,vb3z,sb1; + double sb2,sb3,rb1,rb3,c0,b1mag2,b1mag,b2mag2; + double b2mag,b3mag2,b3mag,ctmp,r12c1,c1mag,r12c2; + double c2mag,sc1,sc2,s1,s12,c,p,pd,a,a11,a22; + double a33,a12,a13,a23,sx1,sx2,sx12,sy1,sy2,sy12; + double sz1,sz2,sz12,s2,cx,cy,cz,cmag,dx,phi,si,siinv,sin2; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **dihedrallist = neighbor->dihedrallist; + int ndihedrallist = neighbor->ndihedrallist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < ndihedrallist; n++) { + + i1 = dihedrallist[n][0]; + i2 = dihedrallist[n][1]; + i3 = dihedrallist[n][2]; + i4 = dihedrallist[n][3]; + type = dihedrallist[n][4]; + + if (newton_bond) factor = 4; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + if (i4 < nlocal) factor++; + } + rfactor = 0.25 * factor; + + // 1st bond + + vb1x = x[i1][0] - x[i2][0]; + vb1y = x[i1][1] - x[i2][1]; + vb1z = x[i1][2] - x[i2][2]; + domain->minimum_image(&vb1x,&vb1y,&vb1z); + + // 2nd bond + + vb2x = x[i3][0] - x[i2][0]; + vb2y = x[i3][1] - x[i2][1]; + vb2z = x[i3][2] - x[i2][2]; + domain->minimum_image(&vb2x,&vb2y,&vb2z); + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + domain->minimum_image(&vb2xm,&vb2ym,&vb2zm); + + // 3rd bond + + vb3x = x[i4][0] - x[i3][0]; + vb3y = x[i4][1] - x[i3][1]; + vb3z = x[i4][2] - x[i3][2]; + domain->minimum_image(&vb3x,&vb3y,&vb3z); + + // c0 calculation + + sb1 = 1.0 / (vb1x*vb1x + vb1y*vb1y + vb1z*vb1z); + sb2 = 1.0 / (vb2x*vb2x + vb2y*vb2y + vb2z*vb2z); + sb3 = 1.0 / (vb3x*vb3x + vb3y*vb3y + vb3z*vb3z); + + rb1 = sqrt(sb1); + rb3 = sqrt(sb3); + + c0 = (vb1x*vb3x + vb1y*vb3y + vb1z*vb3z) * rb1*rb3; + + // 1st and 2nd angle + + b1mag2 = vb1x*vb1x + vb1y*vb1y + vb1z*vb1z; + b1mag = sqrt(b1mag2); + b2mag2 = vb2x*vb2x + vb2y*vb2y + vb2z*vb2z; + b2mag = sqrt(b2mag2); + b3mag2 = vb3x*vb3x + vb3y*vb3y + vb3z*vb3z; + b3mag = sqrt(b3mag2); + + ctmp = vb1x*vb2x + vb1y*vb2y + vb1z*vb2z; + r12c1 = 1.0 / (b1mag*b2mag); + c1mag = ctmp * r12c1; + + ctmp = vb2xm*vb3x + vb2ym*vb3y + vb2zm*vb3z; + r12c2 = 1.0 / (b2mag*b3mag); + c2mag = ctmp * r12c2; + + // cos and sin of 2 angles and final c + + sin2 = MAX(1.0 - c1mag*c1mag,0.0); + sc1 = sqrt(sin2); + if (sc1 < SMALL) sc1 = SMALL; + sc1 = 1.0/sc1; + + sin2 = MAX(1.0 - c2mag*c2mag,0.0); + sc2 = sqrt(sin2); + if (sc2 < SMALL) sc2 = SMALL; + sc2 = 1.0/sc2; + + s1 = sc1 * sc1; + s2 = sc2 * sc2; + s12 = sc1 * sc2; + c = (c0 + c1mag*c2mag) * s12; + + cx = vb1y*vb2z - vb1z*vb2y; + cy = vb1z*vb2x - vb1x*vb2z; + cz = vb1x*vb2y - vb1y*vb2x; + cmag = sqrt(cx*cx + cy*cy + cz*cz); + dx = (cx*vb3x + cy*vb3y + cz*vb3z)/cmag/b3mag; + + // error check + + if (c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) { + if (screen) { + fprintf(screen,"Dihedral problem: %d %d %d %d %d %d\n", + comm->me,update->ntimestep, + atom->tag[i1],atom->tag[i2],atom->tag[i3],atom->tag[i4]); + fprintf(screen," 1st atom: %d %g %g %g\n", + comm->me,x[i1][0],x[i1][1],x[i1][2]); + fprintf(screen," 2nd atom: %d %g %g %g\n", + comm->me,x[i2][0],x[i2][1],x[i2][2]); + fprintf(screen," 3rd atom: %d %g %g %g\n", + comm->me,x[i3][0],x[i3][1],x[i3][2]); + fprintf(screen," 4th atom: %d %g %g %g\n", + comm->me,x[i4][0],x[i4][1],x[i4][2]); + } + } + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + // force & energy + // p = sum (i=1,4) k_i * (1 + (-1)**(i+1)*cos(i*phi) ) + // pd = dp/dc + + phi = acos(c); + if (dx < 0.0) phi *= -1.0; + si = sin(phi); + if (fabs(si) < SMALLER) si = SMALLER; + siinv = 1.0/si; + + p = k1[type]*(1.0 + c) + k2[type]*(1.0 - cos(2.0*phi)) + + k3[type]*(1.0 + cos(3.0*phi)) + k4[type]*(1.0 - cos(4.0*phi)) ; + pd = k1[type] - 2.0*k2[type]*sin(2.0*phi)*siinv + + 3.0*k3[type]*sin(3.0*phi)*siinv - 4.0*k4[type]*sin(4.0*phi)*siinv; + + if (eflag) energy += rfactor * p; + + a = pd; + c = c * a; + s12 = s12 * a; + a11 = -c*sb1*s1; + a22 = sb2 * (2.0*c0*s12 - c*(s1+s2)); + a33 = -c*sb3*s2; + a12 = r12c1 * (c1mag*c*s1 + c2mag*s12); + a13 = rb1*rb3*s12; + a23 = r12c2 * (-c2mag*c*s2 - c1mag*s12); + + sx1 = a11*vb1x + a12*vb2x + a13*vb3x; + sx2 = a12*vb1x + a22*vb2x + a23*vb3x; + sx12 = a13*vb1x + a23*vb2x + a33*vb3x; + sy1 = a11*vb1y + a12*vb2y + a13*vb3y; + sy2 = a12*vb1y + a22*vb2y + a23*vb3y; + sy12 = a13*vb1y + a23*vb2y + a33*vb3y; + sz1 = a11*vb1z + a12*vb2z + a13*vb3z; + sz2 = a12*vb1z + a22*vb2z + a23*vb3z; + sz12 = a13*vb1z + a23*vb2z + a33*vb3z; + + // apply force to each of 4 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= sx1; + f[i1][1] -= sy1; + f[i1][2] -= sz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += sx2 + sx1; + f[i2][1] += sy2 + sy1; + f[i2][2] += sz2 + sz1; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] += sx12 - sx2; + f[i3][1] += sy12 - sy2; + f[i3][2] += sz12 - sz2; + } + + if (newton_bond || i4 < nlocal) { + f[i4][0] -= sx12; + f[i4][1] -= sy12; + f[i4][2] -= sz12; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (vb1x*sx1 + vb2x*sx2 + vb3x*sx12); + virial[1] -= rfactor * (vb1y*sy1 + vb2y*sy2 + vb3y*sy12); + virial[2] -= rfactor * (vb1z*sz1 + vb2z*sz2 + vb3z*sz12); + virial[3] -= rfactor * (vb1x*sy1 + vb2x*sy2 + vb3x*sy12); + virial[4] -= rfactor * (vb1x*sz1 + vb2x*sz2 + vb3x*sz12); + virial[5] -= rfactor * (vb1y*sz1 + vb2y*sz2 + vb3y*sz12); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void DihedralOPLS::allocate() +{ + allocated = 1; + int n = atom->ndihedraltypes; + + k1 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:k1"); + k2 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:k2"); + k3 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:k3"); + k4 = (double *) memory->smalloc((n+1)*sizeof(double),"dihedral:k4"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"dihedral:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void DihedralOPLS::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this dihedral style"); + if (narg != 5) error->all("Incorrect args for dihedral coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->ndihedraltypes,ilo,ihi); + + double k1_one = atof(arg[1]); + double k2_one = atof(arg[2]); + double k3_one = atof(arg[3]); + double k4_one = atof(arg[4]); + + // store 1/2 factor with prefactor + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k1[i] = 0.5*k1_one; + k2[i] = 0.5*k2_one; + k3[i] = 0.5*k3_one; + k4[i] = 0.5*k4_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for dihedral coefficients"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void DihedralOPLS::write_restart(FILE *fp) +{ + fwrite(&k1[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&k2[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&k3[1],sizeof(double),atom->ndihedraltypes,fp); + fwrite(&k4[1],sizeof(double),atom->ndihedraltypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void DihedralOPLS::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k1[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&k2[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&k3[1],sizeof(double),atom->ndihedraltypes,fp); + fread(&k4[1],sizeof(double),atom->ndihedraltypes,fp); + } + MPI_Bcast(&k1[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&k2[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&k3[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + MPI_Bcast(&k4[1],atom->ndihedraltypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->ndihedraltypes; i++) setflag[i] = 1; +} diff --git a/src/dihedral_opls.h b/src/dihedral_opls.h new file mode 100644 index 0000000000000000000000000000000000000000..d65363286c9aa1903f0a1acdd433b2d5565fc87f --- /dev/null +++ b/src/dihedral_opls.h @@ -0,0 +1,35 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DIHEDRAL_OPLS_H +#define DIHEDRAL_OPLS_H + +#include "stdio.h" +#include "dihedral.h" + +class DihedralOPLS : public Dihedral { + public: + DihedralOPLS() {} + ~DihedralOPLS(); + void compute(int, int); + void coeff(int, int, char **); + void write_restart(FILE *); + void read_restart(FILE *); + + private: + double *k1,*k2,*k3,*k4; + + void allocate(); +}; + +#endif diff --git a/src/displace_atoms.cpp b/src/displace_atoms.cpp new file mode 100644 index 0000000000000000000000000000000000000000..803ff5cae0be8484ef379e6b0e51eb1e3fe2d5b4 --- /dev/null +++ b/src/displace_atoms.cpp @@ -0,0 +1,198 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "stdlib.h" +#include "string.h" +#include "displace_atoms.h" +#include "system.h" +#include "atom.h" +#include "domain.h" +#include "comm.h" +#include "group.h" +#include "error.h" + +#define MOVE 1 +#define RAMP 2 + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +void DisplaceAtoms::command(int narg, char **arg) +{ + if (domain->box_exist == 0) + error->all("Displace_atoms command before simulation box is defined"); + if (narg < 2) error->all("Illegal displace_atoms command"); + + // init entire system since comm->exchange is done + // comm::init needs neighbor::init needs pair::init needs kspace::init, etc + + if (comm->me == 0 && screen) + fprintf(screen,"System init for displace_atoms ...\n"); + sys->init(); + + if (comm->me == 0 && screen) fprintf(screen,"Displacing atoms ...\n"); + + // group and style + + int igroup = group->find(arg[0]); + if (igroup == -1) error->all("Could not find displace_atoms group ID"); + int groupbit = group->bitmask[igroup]; + + int style; + if (strcmp(arg[1],"move") == 0) style = MOVE; + else if (strcmp(arg[1],"ramp") == 0) style = RAMP; + else error->all("Illegal displace_atoms command"); + + // set option defaults + + scaleflag = 1; + + // read options from end of input line + + if (style == MOVE) options(narg-5,&arg[5]); + else if (style == RAMP) options(narg-8,&arg[8]); + + // setup scaling + + if (scaleflag && strcmp(domain->lattice_style,"none") == 0) + error->all("Use of displace_atoms with undefined lattice"); + + double xscale,yscale,zscale; + if (scaleflag) { + xscale = domain->xlattice; + yscale = domain->ylattice; + zscale = domain->zlattice; + } + else xscale = yscale = zscale = 1.0; + + // move atoms directly by specified 3-vector distance + + if (style == MOVE) { + + double delx,dely,delz; + delx = xscale*atof(arg[2]); + dely = yscale*atof(arg[3]); + delz = zscale*atof(arg[4]); + + double **x = atom->x; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + x[i][0] += delx; + x[i][1] += dely; + x[i][2] += delz; + } + } + + // move atoms in ramped fashion + + } else if (style == RAMP) { + + int d_dim; + if (strcmp(arg[2],"x") == 0) d_dim = 0; + else if (strcmp(arg[2],"y") == 0) d_dim = 1; + else if (strcmp(arg[2],"z") == 0) d_dim = 2; + else error->all("Illegal displace_atoms ramp command"); + + double d_lo,d_hi; + if (d_dim == 0) { + d_lo = xscale*atof(arg[3]); + d_hi = xscale*atof(arg[4]); + } else if (d_dim == 1) { + d_lo = yscale*atof(arg[3]); + d_hi = yscale*atof(arg[4]); + } else if (d_dim == 2) { + d_lo = zscale*atof(arg[3]); + d_hi = zscale*atof(arg[4]); + } + + int coord_dim; + if (strcmp(arg[5],"x") == 0) coord_dim = 0; + else if (strcmp(arg[5],"y") == 0) coord_dim = 1; + else if (strcmp(arg[5],"z") == 0) coord_dim = 2; + else error->all("Illegal velocity ramp command"); + + double coord_lo,coord_hi; + if (coord_dim == 0) { + coord_lo = xscale*atof(arg[6]); + coord_hi = xscale*atof(arg[7]); + } else if (coord_dim == 1) { + coord_lo = yscale*atof(arg[6]); + coord_hi = yscale*atof(arg[7]); + } else if (coord_dim == 2) { + coord_lo = zscale*atof(arg[6]); + coord_hi = zscale*atof(arg[7]); + } + + double **x = atom->x; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double fraction,dramp; + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + fraction = (x[i][coord_dim] - coord_lo) / (coord_hi - coord_lo); + fraction = MAX(fraction,0.0); + fraction = MIN(fraction,1.0); + dramp = d_lo + fraction*(d_hi - d_lo); + x[i][d_dim] += dramp; + } + } + } + + // move atoms to new processors + // enforce PBC before in case atoms are outside box + + domain->pbc(); + domain->reset_box(); + comm->setup(); + comm->exchange(); + + // check if any atoms were lost + + double natoms; + double rlocal = atom->nlocal; + MPI_Allreduce(&rlocal,&natoms,1,MPI_DOUBLE,MPI_SUM,world); + if (natoms != atom->natoms) { + char str[128]; + sprintf(str,"Lost atoms via displacement: original %.15g current %.15g", + atom->natoms,natoms); + error->all(str); + } +} + +/* ---------------------------------------------------------------------- + parse optional parameters at end of displace_atoms input line +------------------------------------------------------------------------- */ + +void DisplaceAtoms::options(int narg, char **arg) +{ + if (narg < 0) error->all("Illegal displace_atoms command"); + + int iarg = 0; + while (iarg < narg) { + if (strcmp(arg[iarg],"units") == 0) { + if (iarg+2 > narg) error->all("Illegal displace_atoms command"); + if (strcmp(arg[iarg+1],"box") == 0) scaleflag = 0; + else if (strcmp(arg[iarg+1],"lattice") == 0) scaleflag = 1; + else error->all("Illegal displace_atoms command"); + iarg += 2; + } else error->all("Illegal displace_atoms command"); + } +} diff --git a/src/displace_atoms.h b/src/displace_atoms.h new file mode 100644 index 0000000000000000000000000000000000000000..c60f74b22ca71ba0135fc88195f2dc6a27b62f56 --- /dev/null +++ b/src/displace_atoms.h @@ -0,0 +1,30 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DISPLACE_ATOMS_H +#define DISPLACE_ATOMS_H + +#include "lammps.h" + +class DisplaceAtoms : public LAMMPS { + public: + DisplaceAtoms() {} + ~DisplaceAtoms() {} + void command(int, char **); + + private: + int scaleflag; + void options(int, char **); +}; + +#endif diff --git a/src/domain.cpp b/src/domain.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b379a4a5497819bf824220e2d532bdf62f36f22c --- /dev/null +++ b/src/domain.cpp @@ -0,0 +1,688 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "stdlib.h" +#include "string.h" +#include "stdio.h" +#include "math.h" +#include "domain.h" +#include "atom.h" +#include "force.h" +#include "update.h" +#include "modify.h" +#include "fix.h" +#include "region.h" +#include "comm.h" +#include "memory.h" +#include "error.h" + +#define RegionInclude +#include "style.h" +#undef RegionInclude + +#define BIG 1.0e20 +#define SMALL 1.0e-4 +#define DELTA 1 +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- + default is periodic +------------------------------------------------------------------------- */ + +Domain::Domain() +{ + box_exist = 0; + + nonperiodic = 0; + xperiodic = yperiodic = zperiodic = 1; + boundary[0][0] = boundary[0][1] = 0; + boundary[1][0] = boundary[1][1] = 0; + boundary[2][0] = boundary[2][1] = 0; + + boxxlo = boxylo = boxzlo = -0.5; + boxxhi = boxyhi = boxzhi = 0.5; + + char *str = "none"; + int n = strlen(str) + 1; + lattice_style = new char[n]; + strcpy(lattice_style,str); + + origin_x = origin_y = origin_z = 0.0; + orient_x[0] = 1; orient_x[1] = 0; orient_x[2] = 0; + orient_y[0] = 0; orient_y[1] = 1; orient_y[2] = 0; + orient_z[0] = 0; orient_z[1] = 0; orient_z[2] = 1; + + nregion = maxregion = 0; + regions = NULL; +} + +/* ---------------------------------------------------------------------- */ + +Domain::~Domain() +{ + delete [] lattice_style; + for (int i = 0; i < nregion; i++) delete regions[i]; + memory->sfree(regions); +} + +/* ---------------------------------------------------------------------- */ + +void Domain::init() +{ + // set box_change if box dimensions ever change + // due to shrink-wrapping, fix nph, npt, volume/rescale, uniaxial + + box_change = 0; + if (nonperiodic == 2) box_change = 1; + for (int i = 0; i < modify->nfix; i++) { + if (strcmp(modify->fix[i]->style,"nph") == 0) box_change = 1; + if (strcmp(modify->fix[i]->style,"npt") == 0) box_change = 1; + if (strcmp(modify->fix[i]->style,"volume/rescale") == 0) box_change = 1; + if (strcmp(modify->fix[i]->style,"uniaxial") == 0) box_change = 1; + } +} + +/* ---------------------------------------------------------------------- + setup initial global box, boxxlo-boxzhi are already set + adjust for any shrink-wrapped boundaries + store min values if boundary type = 3 = "m" +------------------------------------------------------------------------- */ + +void Domain::set_initial_box() +{ + if (boundary[0][0] == 2) boxxlo -= SMALL; + else if (boundary[0][0] == 3) minxlo = boxxlo; + if (boundary[0][1] == 2) boxxhi += SMALL; + else if (boundary[0][1] == 3) minxhi = boxxhi; + + if (boundary[1][0] == 2) boxylo -= SMALL; + else if (boundary[1][0] == 3) minylo = boxylo; + if (boundary[1][1] == 2) boxyhi += SMALL; + else if (boundary[1][1] == 3) minyhi = boxyhi; + + if (boundary[2][0] == 2) boxzlo -= SMALL; + else if (boundary[2][0] == 3) minzlo = boxzlo; + if (boundary[2][1] == 2) boxzhi += SMALL; + else if (boundary[2][1] == 3) minzhi = boxzhi; +} + +/* ---------------------------------------------------------------------- + setup global box parameters + set prd, prd_half, prd[], boxlo/hi[], periodicity[] +------------------------------------------------------------------------- */ + +void Domain::set_global_box() +{ + xprd = boxxhi - boxxlo; + yprd = boxyhi - boxylo; + zprd = boxzhi - boxzlo; + + xprd_half = 0.5*xprd; + yprd_half = 0.5*yprd; + zprd_half = 0.5*zprd; + + prd[0] = xprd; prd[1] = yprd; prd[2] = zprd; + boxlo[0] = boxxlo; boxlo[1] = boxylo; boxlo[2] = boxzlo; + boxhi[0] = boxxhi; boxhi[1] = boxyhi; boxhi[2] = boxzhi; + periodicity[0] = xperiodic; + periodicity[1] = yperiodic; + periodicity[2] = zperiodic; +} + +/* ---------------------------------------------------------------------- + set local subbox from global boxxlo-boxzhi and proc grid + set subxlo-subzhi, sublo/hi[] + for uppermost proc, insure subhi = boxhi (in case round-off occurs) +------------------------------------------------------------------------- */ + +void Domain::set_local_box() +{ + subxlo = boxxlo + comm->myloc[0] * xprd / comm->procgrid[0]; + if (comm->myloc[0] < comm->procgrid[0]-1) + subxhi = boxxlo + (comm->myloc[0]+1) * xprd / comm->procgrid[0]; + else subxhi = boxxhi; + + subylo = boxylo + comm->myloc[1] * yprd / comm->procgrid[1]; + if (comm->myloc[1] < comm->procgrid[1]-1) + subyhi = boxylo + (comm->myloc[1]+1) * yprd / comm->procgrid[1]; + else subyhi = boxyhi; + + subzlo = boxzlo + comm->myloc[2] * zprd / comm->procgrid[2]; + if (comm->myloc[2] < comm->procgrid[2]-1) + subzhi = boxzlo + (comm->myloc[2]+1) * zprd / comm->procgrid[2]; + else subzhi = boxzhi; + + sublo[0] = subxlo; sublo[1] = subylo; sublo[2] = subzlo; + subhi[0] = subxhi; subhi[1] = subyhi; subhi[2] = subzhi; +} + +/* ---------------------------------------------------------------------- + reset global & local boxes due to global box boundary changes + if shrink-wrapped, determine atom extent and reset boxxlo thru boxzhi + call set_global_box and set_local_box +------------------------------------------------------------------------- */ + +void Domain::reset_box() +{ + if (nonperiodic == 2) { + + // compute extent of atoms on this proc + + double extent[3][2],all[3][2]; + + extent[2][0] = extent[1][0] = extent[0][0] = BIG; + extent[2][1] = extent[1][1] = extent[0][1] = -BIG; + + double **x = atom->x; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) { + extent[0][0] = MIN(extent[0][0],x[i][0]); + extent[0][1] = MAX(extent[0][1],x[i][0]); + extent[1][0] = MIN(extent[1][0],x[i][1]); + extent[1][1] = MAX(extent[1][1],x[i][1]); + extent[2][0] = MIN(extent[2][0],x[i][2]); + extent[2][1] = MAX(extent[2][1],x[i][2]); + } + + // compute extent across all procs + // flip sign of MIN to do it in one Allreduce MAX + // set box by extent in shrink-wrapped dims + + extent[0][0] = -extent[0][0]; + extent[1][0] = -extent[1][0]; + extent[2][0] = -extent[2][0]; + + MPI_Allreduce(extent,all,6,MPI_DOUBLE,MPI_MAX,world); + + // if any of 6 dims is shrink-wrapped, set it to extent of atoms +/- SMALL + // enforce min extent for boundary type = 3 = "m" + + if (xperiodic == 0) { + if (boundary[0][0] == 2) boxxlo = -all[0][0] - SMALL; + else if (boundary[0][0] == 3) boxxlo = MIN(-all[0][0]-SMALL,minxlo); + if (boundary[0][1] == 2) boxxhi = all[0][1] + SMALL; + else if (boundary[0][1] == 3) boxxhi = MAX(all[0][1]+SMALL,minxhi); + } + if (yperiodic == 0) { + if (boundary[1][0] == 2) boxylo = -all[1][0] - SMALL; + else if (boundary[1][0] == 3) boxylo = MIN(-all[1][0]-SMALL,minylo); + if (boundary[1][1] == 2) boxyhi = all[1][1] + SMALL; + else if (boundary[1][1] == 3) boxyhi = MAX(all[1][1]+SMALL,minyhi); + } + if (zperiodic == 0) { + if (boundary[2][0] == 2) boxzlo = -all[2][0] - SMALL; + else if (boundary[2][0] == 3) boxzlo = MIN(-all[2][0]-SMALL,minzlo); + if (boundary[2][1] == 2) boxzhi = all[2][1] + SMALL; + else if (boundary[2][1] == 3) boxzhi = MAX(all[2][1]+SMALL,minzhi); + } + } + + set_global_box(); + set_local_box(); +} + +/* ---------------------------------------------------------------------- + enforce PBC and modify box image flags for each atom + called every reneighboring + resulting coord must satisfy lo <= coord < hi + MAX is important since coord - prd < lo can happen when coord = hi + image = 10 bits for each dimension + increment/decrement in wrap-around fashion +------------------------------------------------------------------------- */ + +void Domain::pbc() +{ + int i,idim,otherdims; + int nlocal = atom->nlocal; + double **x = atom->x; + int *image = atom->image; + + if (xperiodic) { + for (i = 0; i < nlocal; i++) { + if (x[i][0] < boxxlo) { + x[i][0] += xprd; + idim = image[i] & 1023; + otherdims = image[i] ^ idim; + idim--; + idim &= 1023; + image[i] = otherdims | idim; + } + if (x[i][0] >= boxxhi) { + x[i][0] -= xprd; + x[i][0] = MAX(x[i][0],boxxlo); + idim = image[i] & 1023; + otherdims = image[i] ^ idim; + idim++; + idim &= 1023; + image[i] = otherdims | idim; + } + } + } + + if (yperiodic) { + for (i = 0; i < nlocal; i++) { + if (x[i][1] < boxylo) { + x[i][1] += yprd; + idim = (image[i] >> 10) & 1023; + otherdims = image[i] ^ (idim << 10); + idim--; + idim &= 1023; + image[i] = otherdims | (idim << 10); + } + if (x[i][1] >= boxyhi) { + x[i][1] -= yprd; + x[i][1] = MAX(x[i][1],boxylo); + idim = (image[i] >> 10) & 1023; + otherdims = image[i] ^ (idim << 10); + idim++; + idim &= 1023; + image[i] = otherdims | (idim << 10); + } + } + } + + if (zperiodic) { + for (i = 0; i < nlocal; i++) { + if (x[i][2] < boxzlo) { + x[i][2] += zprd; + idim = image[i] >> 20; + otherdims = image[i] ^ (idim << 20); + idim--; + idim &= 1023; + image[i] = otherdims | (idim << 20); + } + if (x[i][2] >= boxzhi) { + x[i][2] -= zprd; + x[i][2] = MAX(x[i][2],boxzlo); + idim = image[i] >> 20; + otherdims = image[i] ^ (idim << 20); + idim++; + idim &= 1023; + image[i] = otherdims | (idim << 20); + } + } + } +} + +/* ---------------------------------------------------------------------- + minimum image convention + use 1/2 of box size as test +------------------------------------------------------------------------- */ + +void Domain::minimum_image(double *dx, double *dy, double *dz) +{ + if (xperiodic) { + if (fabs(*dx) > xprd_half) { + if (*dx < 0.0) *dx += xprd; + else *dx -= xprd; + } + } + if (yperiodic) { + if (fabs(*dy) > yprd_half) { + if (*dy < 0.0) *dy += yprd; + else *dy -= yprd; + } + } + if (zperiodic) { + if (fabs(*dz) > zprd_half) { + if (*dz < 0.0) *dz += zprd; + else *dz -= zprd; + } + } +} + +/* ---------------------------------------------------------------------- + minimum image convention + use 1/2 of box size as test +------------------------------------------------------------------------- */ + +void Domain::minimum_image(double *x) +{ + if (xperiodic) { + if (fabs(x[0]) > xprd_half) { + if (x[0] < 0.0) x[0] += xprd; + else x[0] -= xprd; + } + } + if (yperiodic) { + if (fabs(x[1]) > yprd_half) { + if (x[1] < 0.0) x[1] += yprd; + else x[1] -= yprd; + } + } + if (zperiodic) { + if (fabs(x[2]) > zprd_half) { + if (x[2] < 0.0) x[2] += zprd; + else x[2] -= zprd; + } + } +} + +/* ---------------------------------------------------------------------- + remap the point into the periodic box no matter how far away + adjust image accordingly + resulting coord must satisfy lo <= coord < hi + MAX is important since coord - prd < lo can happen when coord = hi + image = 10 bits for each dimension + increment/decrement in wrap-around fashion +------------------------------------------------------------------------- */ + +void Domain::remap(double &x, double &y, double &z, int &image) +{ + if (xperiodic) { + while (x < boxxlo) { + x += xprd; + int idim = image & 1023; + int otherdims = image ^ idim; + idim--; + idim &= 1023; + image = otherdims | idim; + } + while (x >= boxxhi) { + x -= xprd; + int idim = image & 1023; + int otherdims = image ^ idim; + idim++; + idim &= 1023; + image = otherdims | idim; + } + x = MAX(x,boxxlo); + } + + if (yperiodic) { + while (y < boxylo) { + y += yprd; + int idim = (image >> 10) & 1023; + int otherdims = image ^ (idim << 10); + idim--; + idim &= 1023; + image = otherdims | (idim << 10); + } + while (y >= boxyhi) { + y -= yprd; + int idim = (image >> 10) & 1023; + int otherdims = image ^ (idim << 10); + idim++; + idim &= 1023; + image = otherdims | (idim << 10); + } + y = MAX(y,boxylo); + } + + if (zperiodic) { + while (z < boxzlo) { + z += zprd; + int idim = image >> 20; + int otherdims = image ^ (idim << 20); + idim--; + idim &= 1023; + image = otherdims | (idim << 20); + } + while (z >= boxzhi) { + z -= zprd; + int idim = image >> 20; + int otherdims = image ^ (idim << 20); + idim--; + idim &= 1023; + image = otherdims | (idim << 20); + } + z = MAX(z,boxzlo); + } +} + +/* ---------------------------------------------------------------------- + unmap the point via image flags + don't reset image flag +------------------------------------------------------------------------- */ + +void Domain::unmap(double &x, double &y, double &z, int image) +{ + int xbox = (image & 1023) - 512; + int ybox = (image >> 10 & 1023) - 512; + int zbox = (image >> 20) - 512; + + x = x + xbox*xprd; + y = y + ybox*yprd; + z = z + zbox*zprd; +} + +/* ---------------------------------------------------------------------- + set lattice constant +------------------------------------------------------------------------- */ + +void Domain::set_lattice(int narg, char **arg) +{ + if (narg < 1) error->all("Illegal lattice command"); + + delete [] lattice_style; + int n = strlen(arg[0]) + 1; + lattice_style = new char[n]; + strcpy(lattice_style,arg[0]); + + if (strcmp(arg[0],"none") == 0) return; + + if (narg != 2) error->all("Illegal lattice command"); + + if (strcmp(arg[0],"sc") && strcmp(arg[0],"bcc") && strcmp(arg[0],"fcc") && + strcmp(arg[0],"sq") && strcmp(arg[0],"sq2") && strcmp(arg[0],"hex") && + strcmp(arg[0],"diamond")) + error->all("Illegal lattice command"); + + // check that lattice matches dimension + + int dim = force->dimension; + + if (dim == 2) { + if (strcmp(arg[0],"sq") && strcmp(arg[0],"sq2") && strcmp(arg[0],"hex")) + error->all("Lattice style incompatible with dimension"); + } + + if (dim == 3) { + if (strcmp(arg[0],"sc") && strcmp(arg[0],"bcc") && + strcmp(arg[0],"fcc") && strcmp(arg[0],"diamond")) + error->all("Lattice style incompatible with dimension"); + } + + // set lattice constants depending on # of atoms per unit cell + // hex is only case where xlattice = ylattice = zlattice is not true + + double value = atof(arg[1]); + + if (!strcmp(arg[0],"sc") || !strcmp(arg[0],"sq")) { + if (strcmp(update->unit_style,"lj") == 0) + xlattice = ylattice = zlattice = pow(1.0/value,1.0/dim); + else xlattice = ylattice = zlattice = value; + } + + if (!strcmp(arg[0],"bcc") || !strcmp(arg[0],"sq2")) { + if (strcmp(update->unit_style,"lj") == 0) + xlattice = ylattice = zlattice = pow(2.0/value,1.0/dim); + else xlattice = ylattice = zlattice = value; + } + + if (strcmp(arg[0],"fcc") == 0) { + if (strcmp(update->unit_style,"lj") == 0) + xlattice = ylattice = zlattice = pow(4.0/value,1.0/dim); + else xlattice = ylattice = zlattice = value; + } + + if (strcmp(arg[0],"hex") == 0) { + if (strcmp(update->unit_style,"lj") == 0) + xlattice = zlattice = pow(2.0/sqrt(3.0)/value,1.0/dim); + else xlattice = zlattice = value; + ylattice = xlattice * sqrt(3.0); + } + + if (strcmp(arg[0],"diamond") == 0) { + if (strcmp(update->unit_style,"lj") == 0) + xlattice = ylattice = zlattice = pow(8.0/value,1.0/dim); + else xlattice = ylattice = zlattice = value; + } +} + +/* ---------------------------------------------------------------------- + check if orientation vectors are mutually orthogonal +------------------------------------------------------------------------- */ + +int Domain::orthogonality() +{ + if (orient_x[0]*orient_y[0] + orient_x[1]*orient_y[1] + + orient_x[2]*orient_y[2]) return 0; + + if (orient_y[0]*orient_z[0] + orient_y[1]*orient_z[1] + + orient_y[2]*orient_z[2]) return 0; + + if (orient_x[0]*orient_z[0] + orient_x[1]*orient_z[1] + + orient_x[2]*orient_z[2]) return 0; + + return 1; +} + +/* ---------------------------------------------------------------------- + check righthandedness of orientation vectors + x cross y must be in same direction as z +------------------------------------------------------------------------- */ + +int Domain::right_handed() +{ + int xy0 = orient_x[1]*orient_y[2] - orient_x[2]*orient_y[1]; + int xy1 = orient_x[2]*orient_y[0] - orient_x[0]*orient_y[2]; + int xy2 = orient_x[0]*orient_y[1] - orient_x[1]*orient_y[0]; + if (xy0*orient_z[0] + xy1*orient_z[1] + xy2*orient_z[2] <= 0) return 0; + return 1; +} + +/* ---------------------------------------------------------------------- + convert lattice coords into box coords + x,y,z = point in lattice coords + orient_xyz = lattice vectors that point in positive x,y,z box directions + origin_xyz = origin of lattice in lattice units + return xnew,ynew,znew = point in box coords + method: + compute projection of vector from (0,0,0) to lattice onto each + orient vector via dot product scaled by length of + orient vector in lattice coords + this projection (offset by origin and scaled by lattice constant) + gives x,y,z box coords +------------------------------------------------------------------------- */ + +void Domain::lattice2box(double *x, double *y, double *z) +{ + double length; + double dotprod; + + length = orient_x[0]*orient_x[0] + orient_x[1]*orient_x[1] + + orient_x[2]*orient_x[2]; + length = sqrt(length); + dotprod = *x * orient_x[0] + *y * orient_x[1] + *z * orient_x[2]; + double xnew = (dotprod/length + origin_x) * xlattice; + + length = orient_y[0]*orient_y[0] + orient_y[1]*orient_y[1] + + orient_y[2]*orient_y[2]; + length = sqrt(length); + dotprod = *x * orient_y[0] + *y * orient_y[1] + *z * orient_y[2]; + double ynew = (dotprod/length + origin_y) * ylattice; + + length = orient_z[0]*orient_z[0] + orient_z[1]*orient_z[1] + + orient_z[2]*orient_z[2]; + length = sqrt(length); + dotprod = *x * orient_z[0] + *y * orient_z[1] + *z * orient_z[2]; + double znew = (dotprod/length + origin_z) * zlattice; + + *x = xnew; + *y = ynew; + *z = znew; +} + +/* ---------------------------------------------------------------------- + create a new region +------------------------------------------------------------------------- */ + +void Domain::add_region(int narg, char **arg) +{ + if (narg < 2) error->all("Illegal region command"); + + // error checks + + for (int iregion = 0; iregion < nregion; iregion++) + if (strcmp(arg[0],regions[iregion]->id) == 0) + error->all("Reuse of region ID"); + + // extend Region list if necessary + + if (nregion == maxregion) { + maxregion += DELTA; + regions = (Region **) + memory->srealloc(regions,maxregion*sizeof(Region *),"domain:regions"); + } + + // create the Region + + if (strcmp(arg[1],"none") == 0) error->all("Invalid region style"); + +#define RegionClass +#define RegionStyle(key,Class) \ + else if (strcmp(arg[1],#key) == 0) regions[nregion] = new Class(narg,arg); +#include "style.h" +#undef RegionClass + + else error->all("Invalid region style"); + + nregion++; +} + +/* ---------------------------------------------------------------------- + boundary settings from the input script +------------------------------------------------------------------------- */ + +void Domain::set_boundary(int narg, char **arg) +{ + if (narg != 3) error->all("Illegal boundary command"); + + char c; + for (int idim = 0; idim < 3; idim++) + for (int iside = 0; iside < 2; iside++) { + if (iside == 0) c = arg[idim][0]; + else if (iside == 1 && strlen(arg[idim]) == 1) c = arg[idim][0]; + else c = arg[idim][1]; + + if (c == 'p') boundary[idim][iside] = 0; + else if (c == 'f') boundary[idim][iside] = 1; + else if (c == 's') boundary[idim][iside] = 2; + else if (c == 'm') boundary[idim][iside] = 3; + else error->all("Illegal boundary command"); + } + + for (int idim = 0; idim < 3; idim++) + if ((boundary[idim][0] == 0 && boundary[idim][1]) || + (boundary[idim][0] && boundary[idim][1] == 0)) + error->all("Both sides of boundary must be periodic"); + + if (boundary[0][0] == 0) xperiodic = 1; + else xperiodic = 0; + if (boundary[1][0] == 0) yperiodic = 1; + else yperiodic = 0; + if (boundary[2][0] == 0) zperiodic = 1; + else zperiodic = 0; + + nonperiodic = 0; + if (xperiodic == 0 || yperiodic == 0 || zperiodic == 0) { + nonperiodic = 1; + if (boundary[0][0] >= 2 || boundary[0][1] >= 2 || + boundary[1][0] >= 2 || boundary[1][1] >= 2 || + boundary[2][0] >= 2 || boundary[2][1] >= 2) nonperiodic = 2; + } +} diff --git a/src/domain.h b/src/domain.h new file mode 100644 index 0000000000000000000000000000000000000000..38c70ecc2d4d399947a9d08fb3d3bc0b17cdf50f --- /dev/null +++ b/src/domain.h @@ -0,0 +1,91 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DOMAIN_H +#define DOMAIN_H + +#include "lammps.h" +class Region; + +class Domain : public LAMMPS { + public: + int box_exist; // 0 = not yet created, 1 = exists + + int nonperiodic; // 0 = periodic in all 3 dims + // 1 = periodic or fixed in all 6 + // 2 = shrink-wrap in any of 6 + int xperiodic,yperiodic,zperiodic; // 0 = not periodic, 1 = periodic + int boundary[3][2]; // settings for 6 boundaries + // 0 = periodic + // 1 = fixed non-periodic + // 2 = shrink-wrap non-periodic + // 3 = shrink-wrap non-per w/ min + + double minxlo,minxhi; // minimum size of global box + double minylo,minyhi; // when shrink-wrapping + double minzlo,minzhi; + + double boxxlo,boxxhi; // global box boundaries + double boxylo,boxyhi; + double boxzlo,boxzhi; + + double xprd,yprd,zprd; // global box size + + double subxlo,subxhi; // sub-box boudaries on this proc + double subylo,subyhi; + double subzlo,subzhi; + + double boxlo[3],boxhi[3]; // global box bounds as arrays + double prd[3]; // global box size as array + double sublo[3],subhi[3]; // sub-box bounds as arrays + int periodicity[3]; // xyz periodic as array + + double xprd_half,yprd_half,zprd_half; + + int box_change; // 1 if box bounds ever change, 0 if fixed + + // params for create_atoms command + char *lattice_style; // lattice: none + // 3d = fcc, bcc, sc + // 2d = sq, sq2, hex + double xlattice,ylattice,zlattice; // lattice const in 3 directions + double origin_x,origin_y,origin_z; // lattice origin + int orient_x[3]; // lattice vectors + int orient_y[3]; + int orient_z[3]; + + int nregion; // # of defined Regions + int maxregion; // max # list can hold + Region **regions; // list of defined Regions + + Domain(); + ~Domain(); + void init(); + void set_initial_box(); + void set_global_box(); + void set_local_box(); + void reset_box(); + void pbc(); + void remap(double &, double &, double &, int &); + void unmap(double &, double &, double &, int); + void minimum_image(double *, double *, double *); + void minimum_image(double *); + void set_lattice(int, char **); + int orthogonality(); + int right_handed(); + void lattice2box(double *, double *, double *); + void add_region(int, char **); + void set_boundary(int, char **); +}; + +#endif diff --git a/src/dump.cpp b/src/dump.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8a8aa746319af4e9831d6f5385370a10be98f004 --- /dev/null +++ b/src/dump.cpp @@ -0,0 +1,286 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "stdlib.h" +#include "string.h" +#include "stdio.h" +#include "dump.h" +#include "atom.h" +#include "update.h" +#include "group.h" +#include "output.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +Dump::Dump(int narg, char **arg) +{ + MPI_Comm_rank(world,&me); + MPI_Comm_size(world,&nprocs); + + int n = strlen(arg[0]) + 1; + id = new char[n]; + strcpy(id,arg[0]); + + igroup = group->find(arg[1]); + groupbit = group->bitmask[igroup]; + + n = strlen(arg[2]) + 1; + style = new char[n]; + strcpy(style,arg[2]); + + n = strlen(arg[4]) + 1; + filename = new char[n]; + strcpy(filename,arg[4]); + + flush_flag = 1; + format = NULL; + format_user = NULL; + + maxbuf = 0; + buf = NULL; + + // parse filename for special syntax + // if contains '%', write one file per proc and replace % with proc-ID + // if contains '*', write one file per timestep and replace * with timestep + // check file suffixes + // if ends in .bin = binary file + // else if ends in .gz = gzipped text file + // else ASCII text file + + compressed = 0; + binary = 0; + multifile = 0; + multiproc = 0; + + char *ptr; + if (ptr = strchr(filename,'%')) { + multiproc = 1; + char *extend = new char[strlen(filename) + 16]; + *ptr = '\0'; + sprintf(extend,"%s%d%s",filename,me,ptr+1); + delete [] filename; + n = strlen(extend) + 1; + filename = new char[n]; + strcpy(filename,extend); + delete [] extend; + } + + if (strchr(filename,'*')) multifile = 1; + + char *suffix = filename + strlen(filename) - strlen(".bin"); + if (suffix > filename && strcmp(suffix,".bin") == 0) binary = 1; + suffix = filename + strlen(filename) - strlen(".gz"); + if (suffix > filename && strcmp(suffix,".gz") == 0) compressed = 1; +} + +/* ---------------------------------------------------------------------- */ + +Dump::~Dump() +{ + delete [] id; + delete [] style; + delete [] filename; + + delete [] format; + delete [] format_default; + delete [] format_user; + + memory->sfree(buf); + + // XTC style sets fp to NULL since it closes file in its destructor + + if (multifile == 0 && fp != NULL) { + if (multiproc) fclose(fp); + else if (me == 0) fclose(fp); + } +} + +/* ---------------------------------------------------------------------- */ + +void Dump::write() +{ + // if file per timestep, open new file + + if (multifile) openfile(); + + // nme = # of dump lines this proc will contribute to dump + // ntotal = total # of dump lines + // nmax = max # of dump lines on any proc + + int nme = count(); + + int ntotal,nmax; + if (multiproc) nmax = nme; + else { + MPI_Allreduce(&nme,&ntotal,1,MPI_INT,MPI_SUM,world); + MPI_Allreduce(&nme,&nmax,1,MPI_INT,MPI_MAX,world); + } + + // write timestep header + + if (multiproc) write_header(nme); + else if (me == 0) write_header(ntotal); + + // grow communication buffer if necessary + + if (nmax*size_one > maxbuf) { + maxbuf = nmax*size_one; + memory->sfree(buf); + buf = (double *) memory->smalloc(maxbuf*sizeof(double),"dump:buf"); + } + + // pack my data into buf + // send_size = # of quantities in buf + + int me_size = pack(); + + // multiproc = 1 = each proc writes own data to own file + // multiproc = 0 = all procs write to one file thru proc 0 + // proc 0 pings each proc, receives it's data, writes to file + // all other procs wait for ping, send their data to proc 0 + + if (multiproc) write_data(me_size/size_one,buf); + else { + int tmp,nlines; + MPI_Status status; + MPI_Request request; + + if (me == 0) { + for (int iproc = 0; iproc < nprocs; iproc++) { + if (iproc) { + MPI_Irecv(buf,maxbuf,MPI_DOUBLE,iproc,0,world,&request); + MPI_Send(&tmp,0,MPI_INT,iproc,0,world); + MPI_Wait(&request,&status); + MPI_Get_count(&status,MPI_DOUBLE,&nlines); + nlines /= size_one; + } else nlines = me_size/size_one; + + write_data(nlines,buf); + } + if (flush_flag) fflush(fp); + + } else { + MPI_Recv(&tmp,0,MPI_INT,0,0,world,&status); + MPI_Rsend(buf,me_size,MPI_DOUBLE,0,0,world); + } + } + + // if file per timestep, close file + + if (multifile) { + if (multiproc) fclose(fp); + else if (me == 0) fclose(fp); + } +} + +/* ---------------------------------------------------------------------- + process params common to all dumps here + if unknown param, call modify_param specific to the dump +------------------------------------------------------------------------- */ + +void Dump::modify_params(int narg, char **arg) +{ + if (narg == 0) error->all("Illegal dump_modify command"); + + int iarg = 0; + while (iarg < narg) { + if (strcmp(arg[iarg],"format") == 0) { + if (iarg+2 > narg) error->all("Illegal dump_modify command"); + delete [] format_user; + format_user = NULL; + if (strcmp(arg[iarg+1],"none")) { + int n = strlen(arg[iarg+1]) + 1; + format_user = new char[n]; + strcpy(format_user,arg[iarg+1]); + } + iarg += 2; + } else if (strcmp(arg[iarg],"flush") == 0) { + if (iarg+2 > narg) error->all("Illegal dump_modify command"); + if (strcmp(arg[iarg+1],"yes") == 0) flush_flag = 1; + else if (strcmp(arg[iarg+1],"no") == 0) flush_flag = 0; + else error->all("Illegal dump_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"every") == 0) { + if (iarg+2 > narg) error->all("Illegal dump_modify command"); + int n = atoi(arg[iarg+1]); + if (n <= 0) error->all("Illegal dump_modify command"); + int idump; + for (idump = 0; idump < output->ndump; idump++) + if (strcmp(id,output->dump[idump]->id) == 0) break; + output->dump_every[idump] = n; + iarg += 2; + } else { + int n = modify_param(narg-iarg,&arg[iarg]); + if (n == 0) error->all("Illegal dump_modify command"); + iarg += n; + } + } +} + +/* ---------------------------------------------------------------------- + return # of bytes of allocated memory in buf +------------------------------------------------------------------------- */ + +int Dump::memory_usage() +{ + int bytes = maxbuf * sizeof(double); + return bytes; +} + +/* ---------------------------------------------------------------------- + generic opening of a dump file + ASCII or binary or gzipped + some derived classes override this function +------------------------------------------------------------------------- */ + +void Dump::openfile() +{ + // if one file per timestep, replace '*' with current timestep + + char *filecurrent; + if (multifile == 0) filecurrent = filename; + else { + filecurrent = new char[strlen(filename) + 16]; + char *ptr = strchr(filename,'*'); + *ptr = '\0'; + sprintf(filecurrent,"%s%d%s",filename,update->ntimestep,ptr+1); + *ptr = '*'; + } + + // open one file on proc 0 or file on every proc + + if (me == 0 || multiproc) { + if (compressed) { +#ifdef GZIP + char gzip[128]; + sprintf(gzip,"gzip -6 > %s",filecurrent); + fp = popen(gzip,"w"); +#else + error->one("Cannot open gzipped file"); +#endif + } else if (binary) { + fp = fopen(filecurrent,"wb"); + } else { + fp = fopen(filecurrent,"w"); + } + + if (fp == NULL) error->one("Cannot open dump file"); + } + + // delete string with timestep replaced + + if (multifile) delete [] filecurrent; +} diff --git a/src/dump.h b/src/dump.h new file mode 100644 index 0000000000000000000000000000000000000000..0db98105199afe9c9cce7450f919d41c8b750a11 --- /dev/null +++ b/src/dump.h @@ -0,0 +1,61 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DUMP_H +#define DUMP_H + +#include "stdio.h" +#include "lammps.h" + +class Dump : public LAMMPS { + public: + char *id; // user-defined name of Dump + char *style; // style of Dump + int igroup,groupbit; // group that Dump is performed on + int me,nprocs; // proc info + + char *filename; // user-specified file + int compressed; // 1 if dump file is written compressed, 0 no + int binary; // 1 if dump file is written binary, 0 no + int multifile; // 0 = one big file, 1 = one file per timestep + int multiproc; // 0 = proc 0 writes for all, 1 = one file/proc + + int header_flag; // 0 = item, 2 = xyz + int flush_flag; // 0 if no flush, 1 if flush every dump + + char *format_default; // default format string + char *format_user; // format string set by user + char *format; // format string for the file write + double *buf; // memory for atom quantities + int maxbuf; // size of buf + FILE *fp; // file to write dump to + int size_one; // # of quantities for one atom + + Dump(int, char **); + virtual ~Dump(); + virtual void init() {} + void write(); + void modify_params(int, char **); + virtual int memory_usage(); + + protected: + virtual void openfile(); + virtual int modify_param(int, char **) {return 0;} + + virtual void write_header(int) = 0; + virtual int count() = 0; + virtual int pack() = 0; + virtual void write_data(int, double *) = 0; +}; + +#endif diff --git a/src/dump_atom.cpp b/src/dump_atom.cpp new file mode 100644 index 0000000000000000000000000000000000000000..72d339f2b794bd0f4478173a39c0e4440dbb7026 --- /dev/null +++ b/src/dump_atom.cpp @@ -0,0 +1,319 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "dump_atom.h" +#include "domain.h" +#include "atom.h" +#include "update.h" +#include "group.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +DumpAtom::DumpAtom(int narg, char **arg) : Dump(narg, arg) +{ + if (narg != 5) error->all("Illegal dump atom command"); + + scale_flag = 1; + image_flag = 0; + format_default = NULL; + + // one-time file open + + if (multifile == 0) openfile(); +} + +/* ---------------------------------------------------------------------- */ + +void DumpAtom::init() +{ + if (image_flag == 0) size_one = 5; + else size_one = 8; + + // default format depends on image flags + + delete [] format; + if (format_user) { + int n = strlen(format_user) + 2; + format = new char[n]; + strcpy(format,format_user); + strcat(format,"\n"); + } else { + char *str; + if (image_flag == 0) str = "%d %d %g %g %g"; + else str = "%d %d %g %g %g %d %d %d"; + int n = strlen(str) + 2; + format = new char[n]; + strcpy(format,str); + strcat(format,"\n"); + } + + // setup function ptrs + + if (binary) header_choice = &DumpAtom::header_binary; + else header_choice = &DumpAtom::header_item; + + if (scale_flag == 1 && image_flag == 0) + pack_choice = &DumpAtom::pack_scale_noimage; + else if (scale_flag == 1 && image_flag == 1) + pack_choice = &DumpAtom::pack_scale_image; + else if (scale_flag == 0 && image_flag == 0) + pack_choice = &DumpAtom::pack_noscale_noimage; + else if (scale_flag == 0 && image_flag == 1) + pack_choice = &DumpAtom::pack_noscale_image; + + if (binary) write_choice = &DumpAtom::write_binary; + else if (image_flag == 0) write_choice = &DumpAtom::write_noimage; + else if (image_flag == 1) write_choice = &DumpAtom::write_image; +} + +/* ---------------------------------------------------------------------- */ + +int DumpAtom::modify_param(int narg, char **arg) +{ + if (strcmp(arg[0],"scale") == 0) { + if (narg < 2) error->all("Illegal dump_modify command"); + if (strcmp(arg[1],"yes") == 0) scale_flag = 1; + else if (strcmp(arg[1],"no") == 0) scale_flag = 0; + else error->all("Illegal dump_modify command"); + return 2; + } else if (strcmp(arg[0],"image") == 0) { + if (narg < 2) error->all("Illegal dump_modify command"); + if (strcmp(arg[1],"yes") == 0) image_flag = 1; + else if (strcmp(arg[1],"no") == 0) image_flag = 0; + else error->all("Illegal dump_modify command"); + return 2; + } + return 0; +} + +/* ---------------------------------------------------------------------- */ + +void DumpAtom::write_header(int ndump) +{ + (this->*header_choice)(ndump); +} + +/* ---------------------------------------------------------------------- */ + +int DumpAtom::count() +{ + if (igroup == 0) return atom->nlocal; + + int *mask = atom->mask; + int nlocal = atom->nlocal; + + int m = 0; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) m++; + return m; +} + +/* ---------------------------------------------------------------------- */ + +int DumpAtom::pack() +{ + return (this->*pack_choice)(); +} + +/* ---------------------------------------------------------------------- */ + +void DumpAtom::write_data(int n, double *buf) +{ + (this->*write_choice)(n,buf); +} + +/* ---------------------------------------------------------------------- */ + +void DumpAtom::header_binary(int ndump) +{ + fwrite(&update->ntimestep,sizeof(int),1,fp); + fwrite(&ndump,sizeof(int),1,fp); + fwrite(&domain->boxxlo,sizeof(double),1,fp); + fwrite(&domain->boxxhi,sizeof(double),1,fp); + fwrite(&domain->boxylo,sizeof(double),1,fp); + fwrite(&domain->boxyhi,sizeof(double),1,fp); + fwrite(&domain->boxzlo,sizeof(double),1,fp); + fwrite(&domain->boxzhi,sizeof(double),1,fp); + fwrite(&size_one,sizeof(int),1,fp); + if (multiproc) { + int one = 1; + fwrite(&one,sizeof(int),1,fp); + } else fwrite(&nprocs,sizeof(int),1,fp); +} + +/* ---------------------------------------------------------------------- */ + +void DumpAtom::header_item(int ndump) +{ + fprintf(fp,"ITEM: TIMESTEP\n"); + fprintf(fp,"%d\n",update->ntimestep); + fprintf(fp,"ITEM: NUMBER OF ATOMS\n"); + fprintf(fp,"%d\n",ndump); + fprintf(fp,"ITEM: BOX BOUNDS\n"); + fprintf(fp,"%g %g\n",domain->boxxlo,domain->boxxhi); + fprintf(fp,"%g %g\n",domain->boxylo,domain->boxyhi); + fprintf(fp,"%g %g\n",domain->boxzlo,domain->boxzhi); + fprintf(fp,"ITEM: ATOMS\n"); +} + +/* ---------------------------------------------------------------------- */ + +int DumpAtom::pack_scale_image() +{ + int *tag = atom->tag; + int *type = atom->type; + int *image = atom->image; + int *mask = atom->mask; + double **x = atom->x; + int nlocal = atom->nlocal; + + double boxxlo = domain->boxxlo; + double boxylo = domain->boxylo; + double boxzlo = domain->boxzlo; + double invxprd = 1.0/domain->xprd; + double invyprd = 1.0/domain->yprd; + double invzprd = 1.0/domain->zprd; + + int m = 0; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + buf[m++] = tag[i]; + buf[m++] = type[i]; + buf[m++] = (x[i][0] - boxxlo) * invxprd; + buf[m++] = (x[i][1] - boxylo) * invyprd; + buf[m++] = (x[i][2] - boxzlo) * invzprd; + buf[m++] = (image[i] & 1023) - 512; + buf[m++] = (image[i] >> 10 & 1023) - 512; + buf[m++] = (image[i] >> 20) - 512; + } + + return m; +} + +/* ---------------------------------------------------------------------- */ + +int DumpAtom::pack_scale_noimage() +{ + int *tag = atom->tag; + int *type = atom->type; + int *mask = atom->mask; + double **x = atom->x; + int nlocal = atom->nlocal; + + double boxxlo = domain->boxxlo; + double boxylo = domain->boxylo; + double boxzlo = domain->boxzlo; + double invxprd = 1.0/domain->xprd; + double invyprd = 1.0/domain->yprd; + double invzprd = 1.0/domain->zprd; + + int m = 0; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + buf[m++] = tag[i]; + buf[m++] = type[i]; + buf[m++] = (x[i][0] - boxxlo) * invxprd; + buf[m++] = (x[i][1] - boxylo) * invyprd; + buf[m++] = (x[i][2] - boxzlo) * invzprd; + } + + return m; +} + +/* ---------------------------------------------------------------------- */ + +int DumpAtom::pack_noscale_image() +{ + int *tag = atom->tag; + int *type = atom->type; + int *image = atom->image; + int *mask = atom->mask; + double **x = atom->x; + int nlocal = atom->nlocal; + + int m = 0; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + buf[m++] = tag[i]; + buf[m++] = type[i]; + buf[m++] = x[i][0]; + buf[m++] = x[i][1]; + buf[m++] = x[i][2]; + buf[m++] = (image[i] & 1023) - 512; + buf[m++] = (image[i] >> 10 & 1023) - 512; + buf[m++] = (image[i] >> 20) - 512; + } + + return m; +} + +/* ---------------------------------------------------------------------- */ + +int DumpAtom::pack_noscale_noimage() +{ + int *tag = atom->tag; + int *type = atom->type; + int *mask = atom->mask; + double **x = atom->x; + int nlocal = atom->nlocal; + + int m = 0; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + buf[m++] = tag[i]; + buf[m++] = type[i]; + buf[m++] = x[i][0]; + buf[m++] = x[i][1]; + buf[m++] = x[i][2]; + } + + return m; +} + +/* ---------------------------------------------------------------------- */ + +void DumpAtom::write_binary(int n, double *buf) +{ + n *= size_one; + fwrite(&n,sizeof(int),1,fp); + fwrite(buf,sizeof(double),n,fp); +} + +/* ---------------------------------------------------------------------- */ + +void DumpAtom::write_image(int n, double *buf) +{ + int m = 0; + for (int i = 0; i < n; i++) { + fprintf(fp,format, + static_cast<int> (buf[m]), static_cast<int> (buf[m+1]), + buf[m+2],buf[m+3],buf[m+4], static_cast<int> (buf[m+5]), + static_cast<int> (buf[m+6]), static_cast<int> (buf[m+7])); + m += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpAtom::write_noimage(int n, double *buf) +{ + int m = 0; + for (int i = 0; i < n; i++) { + fprintf(fp,format, + static_cast<int> (buf[m]), static_cast<int> (buf[m+1]), + buf[m+2],buf[m+3],buf[m+4]); + m += size_one; + } +} diff --git a/src/dump_atom.h b/src/dump_atom.h new file mode 100644 index 0000000000000000000000000000000000000000..4a0d1cdbe60991b26bb8759e038bf64f4db140fa --- /dev/null +++ b/src/dump_atom.h @@ -0,0 +1,54 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DUMP_ATOM_H +#define DUMP_ATOM_H + +#include "dump.h" + +class DumpAtom : public Dump { + public: + DumpAtom(int, char**); + ~DumpAtom() {} + void init(); + + private: + int scale_flag; // 1 if atom coords are scaled, 0 if no + int image_flag; // 1 if append box count to atom coords, 0 if no + + int modify_param(int, char **); + void write_header(int); + int count(); + int pack(); + void write_data(int, double *); + + typedef void (DumpAtom::*FnPtrHeader)(int); + FnPtrHeader header_choice; // ptr to write header functions + void header_binary(int); + void header_item(int); + + typedef int (DumpAtom::*FnPtrPack)(); + FnPtrPack pack_choice; // ptr to pack functions + int pack_scale_image(); + int pack_scale_noimage(); + int pack_noscale_image(); + int pack_noscale_noimage(); + + typedef void (DumpAtom::*FnPtrData)(int, double *); + FnPtrData write_choice; // ptr to write data functions + void write_binary(int, double *); + void write_image(int, double *); + void write_noimage(int, double *); +}; + +#endif diff --git a/src/dump_bond.cpp b/src/dump_bond.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eb88e2478099760fb7033251f78b43d850c8b6b3 --- /dev/null +++ b/src/dump_bond.cpp @@ -0,0 +1,136 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "dump_bond.h" +#include "atom.h" +#include "domain.h" +#include "update.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +DumpBond::DumpBond(int narg, char **arg) : Dump(narg, arg) +{ + if (narg != 5) error->all("Illegal dump bond command"); + if (atom->molecular == 0) + error->all("Cannot use dump bond with non-molecular system"); + + size_one = 3; + + char *str = "%d %d %d %d"; + int n = strlen(str) + 1; + format_default = new char[n]; + strcpy(format_default,str); + + // one-time file open + + if (multifile == 0) openfile(); +} + +/* ---------------------------------------------------------------------- */ + +void DumpBond::init() +{ + delete [] format; + char *str; + if (format_user) str = format_user; + else str = format_default; + + int n = strlen(str) + 2; + format = new char[n]; + strcpy(format,str); + strcat(format,"\n"); +} + +/* ---------------------------------------------------------------------- */ + +void DumpBond::write_header(int ndump) +{ + fprintf(fp,"ITEM: TIMESTEP\n"); + fprintf(fp,"%d\n",update->ntimestep); + fprintf(fp,"ITEM: NUMBER OF BONDS\n"); + fprintf(fp,"%d\n",ndump); + fprintf(fp,"ITEM: BONDS\n"); +} + +/* ---------------------------------------------------------------------- */ + +int DumpBond::count() +{ + index = 0; + + int *num_bond = atom->num_bond; + int **bond_type = atom->bond_type; + int **bond_atom = atom->bond_atom; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + int i,j,k; + + int m = 0; + for (i = 0; i < nlocal; i++) { + if (!(mask[i] & groupbit)) continue; + for (j = 0; j < num_bond[i]; j++) { + k = atom->map(bond_atom[i][j]); + if (k >= 0 && !(mask[k] & groupbit)) continue; + if (bond_type[i][j] == 0) continue; + m++; + } + } + return m; +} + +/* ---------------------------------------------------------------------- */ + +int DumpBond::pack() +{ + int *num_bond = atom->num_bond; + int **bond_type = atom->bond_type; + int **bond_atom = atom->bond_atom; + int *tag = atom->tag; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + int i,j,k,type,iatom; + + int m = 0; + for (i = 0; i < nlocal; i++) { + if (!(mask[i] & groupbit)) continue; + for (j = 0; j < num_bond[i]; j++) { + iatom = bond_atom[i][j]; + k = atom->map(iatom); + if (k >= 0 && !(mask[k] & groupbit)) continue; + type = bond_type[i][j]; + if (type == 0) continue; + buf[m++] = type; + buf[m++] = tag[i]; + buf[m++] = iatom; + } + } + return m; +} + +/* ---------------------------------------------------------------------- */ + +void DumpBond::write_data(int n, double *buf) +{ + int m = 0; + for (int i = 0; i < n; i++) { + index++; + fprintf(fp,format, + index, static_cast<int> (buf[m]), + static_cast<int> (buf[m+1]), static_cast<int> (buf[m+2])); + m += size_one; + } +} diff --git a/src/dump_bond.h b/src/dump_bond.h new file mode 100644 index 0000000000000000000000000000000000000000..6af31b8a9a773df8f414450f2d869410b05c989a --- /dev/null +++ b/src/dump_bond.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DUMP_BOND_H +#define DUMP_BOND_H + +#include "dump.h" + +class DumpBond : public Dump { + public: + DumpBond(int, char **); + ~DumpBond() {} + void init(); + + private: + int index; // counter for bond output + + void write_header(int); + int count(); + int pack(); + void write_data(int, double *); +}; + +#endif diff --git a/src/dump_custom.cpp b/src/dump_custom.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c7d3c75365ee610243a1123beb79a9ccd716ac71 --- /dev/null +++ b/src/dump_custom.cpp @@ -0,0 +1,1348 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "string.h" +#include "dump_custom.h" +#include "atom.h" +#include "domain.h" +#include "region.h" +#include "group.h" +#include "update.h" +#include "modify.h" +#include "fix.h" +#include "fix_centro.h" +#include "fix_energy.h" +#include "fix_stress.h" +#include "memory.h" +#include "error.h" + +// customize by adding to 1st enum + +enum{TAG,MOL,TYPE,X,Y,Z,XS,YS,ZS,XU,YU,ZU,IX,IY,IZ,VX,VY,VZ,FX,FY,FZ, + Q,MUX,MUY,MUZ,TQX,TQY,TQZ,CENTRO,ENG,SXX,SYY,SZZ,SXY,SXZ,SYZ}; +enum{LT,LE,GT,GE,EQ,NEQ}; +enum{INT,DOUBLE}; + +/* ---------------------------------------------------------------------- */ + +DumpCustom::DumpCustom(int narg, char **arg) : Dump(narg, arg) +{ + if (narg == 5) error->all("No dump custom arguments specified"); + + nevery = atoi(arg[3]); + + size_one = narg-5; + pack_choice = new FnPtrPack[size_one]; + vtype = new int[size_one]; + + iregion = -1; + nthresh = 0; + thresh_array = NULL; + thresh_op = NULL; + thresh_value = NULL; + + fixflag_centro = 0; + fixflag_energy = 0; + fixflag_stress = 0; + + // customize by adding to if statement + + int i; + for (int iarg = 5; iarg < narg; iarg++) { + i = iarg-5; + if (strcmp(arg[iarg],"tag") == 0) { + pack_choice[i] = &DumpCustom::pack_tag; + vtype[i] = INT; + } else if (strcmp(arg[iarg],"mol") == 0) { + pack_choice[i] = &DumpCustom::pack_molecule; + vtype[i] = INT; + } else if (strcmp(arg[iarg],"type") == 0) { + pack_choice[i] = &DumpCustom::pack_type; + vtype[i] = INT; + } else if (strcmp(arg[iarg],"x") == 0) { + pack_choice[i] = &DumpCustom::pack_x; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"y") == 0) { + pack_choice[i] = &DumpCustom::pack_y; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"z") == 0) { + pack_choice[i] = &DumpCustom::pack_z; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"xs") == 0) { + pack_choice[i] = &DumpCustom::pack_xs; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"ys") == 0) { + pack_choice[i] = &DumpCustom::pack_ys; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"zs") == 0) { + pack_choice[i] = &DumpCustom::pack_zs; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"xu") == 0) { + pack_choice[i] = &DumpCustom::pack_xu; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"yu") == 0) { + pack_choice[i] = &DumpCustom::pack_yu; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"zu") == 0) { + pack_choice[i] = &DumpCustom::pack_zu; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"ix") == 0) { + pack_choice[i] = &DumpCustom::pack_ix; + vtype[i] = INT; + } else if (strcmp(arg[iarg],"iy") == 0) { + pack_choice[i] = &DumpCustom::pack_iy; + vtype[i] = INT; + } else if (strcmp(arg[iarg],"iz") == 0) { + pack_choice[i] = &DumpCustom::pack_iz; + vtype[i] = INT; + } else if (strcmp(arg[iarg],"vx") == 0) { + pack_choice[i] = &DumpCustom::pack_vx; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"vy") == 0) { + pack_choice[i] = &DumpCustom::pack_vy; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"vz") == 0) { + pack_choice[i] = &DumpCustom::pack_vz; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"fx") == 0) { + pack_choice[i] = &DumpCustom::pack_fx; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"fy") == 0) { + pack_choice[i] = &DumpCustom::pack_fy; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"fz") == 0) { + pack_choice[i] = &DumpCustom::pack_fz; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"q") == 0) { + pack_choice[i] = &DumpCustom::pack_q; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"mux") == 0) { + pack_choice[i] = &DumpCustom::pack_mux; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"muy") == 0) { + pack_choice[i] = &DumpCustom::pack_muy; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"muz") == 0) { + pack_choice[i] = &DumpCustom::pack_muz; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"tqx") == 0) { + pack_choice[i] = &DumpCustom::pack_tqx; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"tqy") == 0) { + pack_choice[i] = &DumpCustom::pack_tqy; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"tqz") == 0) { + pack_choice[i] = &DumpCustom::pack_tqz; + vtype[i] = DOUBLE; + } else if (strcmp(arg[iarg],"centro") == 0) { + pack_choice[i] = &DumpCustom::pack_centro; + vtype[i] = DOUBLE; + fixflag_centro = 1; + } else if (strcmp(arg[iarg],"eng") == 0) { + pack_choice[i] = &DumpCustom::pack_energy; + vtype[i] = DOUBLE; + fixflag_energy = 1; + } else if (strcmp(arg[iarg],"sxx") == 0) { + pack_choice[i] = &DumpCustom::pack_sxx; + vtype[i] = DOUBLE; + fixflag_stress = 1; + } else if (strcmp(arg[iarg],"syy") == 0) { + pack_choice[i] = &DumpCustom::pack_syy; + vtype[i] = DOUBLE; + fixflag_stress = 1; + } else if (strcmp(arg[iarg],"szz") == 0) { + pack_choice[i] = &DumpCustom::pack_szz; + vtype[i] = DOUBLE; + fixflag_stress = 1; + } else if (strcmp(arg[iarg],"sxy") == 0) { + pack_choice[i] = &DumpCustom::pack_sxy; + vtype[i] = DOUBLE; + fixflag_stress = 1; + } else if (strcmp(arg[iarg],"sxz") == 0) { + pack_choice[i] = &DumpCustom::pack_sxz; + vtype[i] = DOUBLE; + fixflag_stress = 1; + } else if (strcmp(arg[iarg],"syz") == 0) { + pack_choice[i] = &DumpCustom::pack_syz; + vtype[i] = DOUBLE; + fixflag_stress = 1; + } else error->all("Invalid keyword in dump custom command"); + } + + if (fixflag_centro) fix_create("CENTRO"); + if (fixflag_energy) fix_create("ENERGY"); + if (fixflag_stress) fix_create("STRESS"); + + vformat = new char*[size_one]; + + format_default = new char[3*size_one+1]; + format_default[0] = '\0'; + + for (i = 0; i < size_one; i++) { + if (vtype[i] == INT) format_default = strcat(format_default,"%d "); + else format_default = strcat(format_default,"%g "); + vformat[i] = NULL; + } + + maxchoose = 0; + choose = NULL; + dchoose = NULL; + + // one-time file open + + if (multifile == 0) openfile(); +} + +/* ---------------------------------------------------------------------- + free memory +------------------------------------------------------------------------- */ + +DumpCustom::~DumpCustom() +{ + delete [] vtype; + for (int i = 0; i < size_one; i++) delete [] vformat[i]; + delete [] vformat; + delete [] pack_choice; + + if (nthresh) { + memory->sfree(thresh_array); + memory->sfree(thresh_op); + memory->sfree(thresh_value); + } + + // delete fixes for computing per-atom custom quantities if they exist + + if (fixflag_centro) fix_delete("CENTRO"); + if (fixflag_energy) fix_delete("ENERGY"); + if (fixflag_stress) fix_delete("STRESS"); + + if (maxchoose) { + delete [] choose; + delete [] dchoose; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::init() +{ + delete [] format; + char *str; + if (format_user) str = format_user; + else str = format_default; + + int n = strlen(str) + 1; + format = new char[n]; + strcpy(format,str); + + // tokenize the format string and add space at end of each format element + + char *ptr; + for (int i = 0; i < size_one; i++) { + if (i == 0) ptr = strtok(format," \0"); + else ptr = strtok(NULL," \0"); + delete [] vformat[i]; + vformat[i] = new char[strlen(ptr) + 2]; + strcpy(vformat[i],ptr); + vformat[i] = strcat(vformat[i]," "); + } + + // setup function ptrs + + if (binary) header_choice = &DumpCustom::header_binary; + else header_choice = &DumpCustom::header_item; + + if (binary) write_choice = &DumpCustom::write_binary; + else write_choice = &DumpCustom::write_text; + + // set fixflag if dump invokes any fixes + + fixflag = 0; + if (fixflag_centro || fixflag_energy || fixflag_stress) fixflag = 1; + + // find associated fixes that must exist + // could have changed locations in fix list since created + + if (fixflag_centro) ifix_centro = fix_match("CENTRO"); + if (fixflag_energy) ifix_energy = fix_match("ENERGY"); + if (fixflag_stress) ifix_stress = fix_match("STRESS"); + + // allocate choose arrays if first time + + if (maxchoose == 0) { + maxchoose = atom->nmax; + choose = new int[maxchoose]; + dchoose = new double[maxchoose]; + } +} + +/* ---------------------------------------------------------------------- + return # of bytes of allocated memory in buf and choose arrays +------------------------------------------------------------------------- */ + +int DumpCustom::memory_usage() +{ + int bytes = maxbuf * sizeof(double); + bytes += maxchoose * sizeof(int); + bytes += maxchoose * sizeof(double); + return bytes; +} + +/* ---------------------------------------------------------------------- */ + +int DumpCustom::modify_param(int narg, char **arg) +{ + if (strcmp(arg[0],"region") == 0) { + if (narg < 2) error->all("Illegal dump_modify command"); + if (strcmp(arg[1],"none") == 0) iregion = -1; + else { + for (iregion = 0; iregion < domain->nregion; iregion++) + if (strcmp(arg[1],domain->regions[iregion]->id) == 0) break; + if (iregion == domain->nregion) + error->all("Dump_modify region ID does not exist"); + } + return 2; + + } else if (strcmp(arg[0],"thresh") == 0) { + if (narg < 2) error->all("Illegal dump_modify command"); + if (strcmp(arg[1],"none") == 0) { + if (nthresh) { + memory->sfree(thresh_array); + memory->sfree(thresh_op); + memory->sfree(thresh_value); + thresh_array = NULL; + thresh_op = NULL; + thresh_value = NULL; + } + nthresh = 0; + return 2; + } + + if (narg < 4) error->all("Illegal dump_modify command"); + thresh_array = (int *) + memory->srealloc(thresh_array,(nthresh+1)*sizeof(int), + "dump:thresh_array"); + thresh_op = (int *) + memory->srealloc(thresh_op,(nthresh+1)*sizeof(int), + "dump:thresh_op"); + thresh_value = (double *) + memory->srealloc(thresh_value,(nthresh+1)*sizeof(double), + "dump:thresh_value"); + + // customize by adding to if statement + + if (strcmp(arg[1],"tag") == 0) thresh_array[nthresh] = TAG; + else if (strcmp(arg[1],"mol") == 0) thresh_array[nthresh] = MOL; + else if (strcmp(arg[1],"type") == 0) thresh_array[nthresh] = TYPE; + else if (strcmp(arg[1],"x") == 0) thresh_array[nthresh] = X; + else if (strcmp(arg[1],"y") == 0) thresh_array[nthresh] = Y; + else if (strcmp(arg[1],"z") == 0) thresh_array[nthresh] = Z; + else if (strcmp(arg[1],"xs") == 0) thresh_array[nthresh] = XS; + else if (strcmp(arg[1],"ys") == 0) thresh_array[nthresh] = YS; + else if (strcmp(arg[1],"zs") == 0) thresh_array[nthresh] = ZS; + else if (strcmp(arg[1],"xu") == 0) thresh_array[nthresh] = XU; + else if (strcmp(arg[1],"yu") == 0) thresh_array[nthresh] = YU; + else if (strcmp(arg[1],"zu") == 0) thresh_array[nthresh] = ZU; + else if (strcmp(arg[1],"ix") == 0) thresh_array[nthresh] = IX; + else if (strcmp(arg[1],"iy") == 0) thresh_array[nthresh] = IY; + else if (strcmp(arg[1],"iz") == 0) thresh_array[nthresh] = IZ; + else if (strcmp(arg[1],"vx") == 0) thresh_array[nthresh] = VX; + else if (strcmp(arg[1],"vy") == 0) thresh_array[nthresh] = VY; + else if (strcmp(arg[1],"vz") == 0) thresh_array[nthresh] = VZ; + else if (strcmp(arg[1],"fx") == 0) thresh_array[nthresh] = FX; + else if (strcmp(arg[1],"fy") == 0) thresh_array[nthresh] = FY; + else if (strcmp(arg[1],"fz") == 0) thresh_array[nthresh] = FZ; + else if (strcmp(arg[1],"q") == 0) thresh_array[nthresh] = Q; + else if (strcmp(arg[1],"mux") == 0) thresh_array[nthresh] = MUX; + else if (strcmp(arg[1],"muy") == 0) thresh_array[nthresh] = MUY; + else if (strcmp(arg[1],"muz") == 0) thresh_array[nthresh] = MUZ; + else if (strcmp(arg[1],"tqx") == 0) thresh_array[nthresh] = TQX; + else if (strcmp(arg[1],"tqy") == 0) thresh_array[nthresh] = TQY; + else if (strcmp(arg[1],"tqz") == 0) thresh_array[nthresh] = TQZ; + else if (strcmp(arg[1],"centro") == 0) thresh_array[nthresh] = CENTRO; + else if (strcmp(arg[1],"eng") == 0) thresh_array[nthresh] = ENG; + else if (strcmp(arg[1],"sxx") == 0) thresh_array[nthresh] = SXX; + else if (strcmp(arg[1],"syy") == 0) thresh_array[nthresh] = SYY; + else if (strcmp(arg[1],"szz") == 0) thresh_array[nthresh] = SZZ; + else if (strcmp(arg[1],"sxy") == 0) thresh_array[nthresh] = SXY; + else if (strcmp(arg[1],"sxz") == 0) thresh_array[nthresh] = SXZ; + else if (strcmp(arg[1],"syz") == 0) thresh_array[nthresh] = SYZ; + else error->all("Invalid dump_modify threshhold operator"); + + if (strcmp(arg[2],">=") == 0) thresh_op[nthresh] = LT; + else if (strcmp(arg[2],">") == 0) thresh_op[nthresh] = LE; + else if (strcmp(arg[2],"<=") == 0) thresh_op[nthresh] = GT; + else if (strcmp(arg[2],"<") == 0) thresh_op[nthresh] = GE; + else if (strcmp(arg[2],"=") == 0) thresh_op[nthresh] = NEQ; + else if (strcmp(arg[2],"<>") == 0) thresh_op[nthresh] = EQ; + else error->all("Invalid dump_modify threshhold operator"); + + thresh_value[nthresh] = atof(arg[3]); + + // add new fix if necessary + + if (thresh_array[nthresh] == CENTRO && fixflag_centro == 0) { + fix_create("CENTRO"); + fixflag_centro = 1; + } + if (thresh_array[nthresh] == ENG && fixflag_energy == 0) { + fix_create("ENERGY"); + fixflag_energy = 1; + } + if ((thresh_array[nthresh] == SXX || thresh_array[nthresh] == SYY || + thresh_array[nthresh] == SZZ || thresh_array[nthresh] == SXY || + thresh_array[nthresh] == SXZ || thresh_array[nthresh] == SYZ) && + fixflag_stress == 0) { + fix_create("STRESS"); + fixflag_stress = 1; + } + + nthresh++; + return 4; + } + return 0; +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::write_header(int ndump) +{ + (this->*header_choice)(ndump); +} + +/* ---------------------------------------------------------------------- */ + +int DumpCustom::count() +{ + int i; + + // invoke any fixes that compute dump quantities + + if (fixflag) { + if (fixflag_centro) modify->fix[ifix_centro]->dump(); + if (fixflag_energy) modify->fix[ifix_energy]->dump(); + if (fixflag_stress) modify->fix[ifix_stress]->dump(); + } + + // grow choose and dchoose arrays if needed + + int nlocal = atom->nlocal; + if (nlocal > maxchoose) { + delete [] choose; + delete [] dchoose; + maxchoose = atom->nmax; + choose = new int[maxchoose]; + dchoose = new double[maxchoose]; + } + + // choose all local atoms for output + + for (i = 0; i < nlocal; i++) choose[i] = 1; + nmine = nlocal; + + // un-choose if not in group + + if (igroup) { + int *mask = atom->mask; + for (i = 0; i < nlocal; i++) + if (!(mask[i] & groupbit)) { + choose[i] = 0; + nmine--; + } + } + + // un-choose if not in region + + if (iregion >= 0) { + Region *region = domain->regions[iregion]; + double **x = atom->x; + for (i = 0; i < nlocal; i++) + if (choose[i] && region->match(x[i][0],x[i][1],x[i][2]) == 0) { + choose[i] = 0; + nmine--; + } + } + + // un-choose if any threshhold criterion isn't met + + if (nthresh) { + double *ptr; + double value; + int nstride; + int nlocal = atom->nlocal; + + for (int ithresh = 0; ithresh < nthresh; ithresh++) { + + // customize by adding to if statement + + if (thresh_array[ithresh] == TAG) { + int *tag = atom->tag; + for (i = 0; i < nlocal; i++) dchoose[i] = tag[i]; + ptr = dchoose; + nstride = 1; + } else if (thresh_array[ithresh] == MOL) { + int *molecule = atom->molecule; + for (i = 0; i < nlocal; i++) dchoose[i] = molecule[i]; + ptr = dchoose; + nstride = 1; + } else if (thresh_array[ithresh] == TYPE) { + int *type = atom->type; + for (i = 0; i < nlocal; i++) dchoose[i] = type[i]; + ptr = dchoose; + nstride = 1; + } else if (thresh_array[ithresh] == X) { + ptr = &atom->x[0][0]; + nstride = 3; + } else if (thresh_array[ithresh] == Y) { + ptr = &atom->x[0][1]; + nstride = 3; + } else if (thresh_array[ithresh] == Z) { + ptr = &atom->x[0][2]; + nstride = 3; + } else if (thresh_array[ithresh] == XS) { + double **x = atom->x; + double boxxlo = domain->boxxlo; + double invxprd = 1.0/domain->xprd; + for (i = 0; i < nlocal; i++) + dchoose[i] = (x[i][0] - boxxlo) * invxprd; + ptr = dchoose; + nstride = 1; + } else if (thresh_array[ithresh] == YS) { + double **x = atom->x; + double boxylo = domain->boxylo; + double invyprd = 1.0/domain->yprd; + for (i = 0; i < nlocal; i++) + dchoose[i] = (x[i][1] - boxylo) * invyprd; + ptr = dchoose; + nstride = 1; + } else if (thresh_array[ithresh] == ZS) { + double **x = atom->x; + double boxzlo = domain->boxzlo; + double invzprd = 1.0/domain->zprd; + for (i = 0; i < nlocal; i++) + dchoose[i] = (x[i][2] - boxzlo) * invzprd; + ptr = dchoose; + nstride = 1; + } else if (thresh_array[ithresh] == XU) { + double **x = atom->x; + int *image = atom->image; + double xprd = domain->xprd; + for (i = 0; i < nlocal; i++) + dchoose[i] = x[i][0] + ((image[i] & 1023) - 512) * xprd; + ptr = dchoose; + nstride = 1; + } else if (thresh_array[ithresh] == YU) { + double **x = atom->x; + int *image = atom->image; + double yprd = domain->yprd; + for (i = 0; i < nlocal; i++) + dchoose[i] = x[i][1] + ((image[i] >> 10 & 1023) - 512) * yprd; + ptr = dchoose; + nstride = 1; + } else if (thresh_array[ithresh] == ZU) { + double **x = atom->x; + int *image = atom->image; + double zprd = domain->zprd; + for (i = 0; i < nlocal; i++) + dchoose[i] = x[i][2] + ((image[i] >> 20) - 512) * zprd; + ptr = dchoose; + nstride = 1; + } else if (thresh_array[ithresh] == IX) { + int *image = atom->image; + for (i = 0; i < nlocal; i++) + dchoose[i] = (image[i] & 1023) - 512; + ptr = dchoose; + nstride = 1; + } else if (thresh_array[ithresh] == IY) { + int *image = atom->image; + for (i = 0; i < nlocal; i++) + dchoose[i] = (image[i] >> 10 & 1023) - 512; + ptr = dchoose; + nstride = 1; + } else if (thresh_array[ithresh] == IX) { + int *image = atom->image; + for (i = 0; i < nlocal; i++) + dchoose[i] = (image[i] >> 20) - 512; + ptr = dchoose; + nstride = 1; + } else if (thresh_array[ithresh] == VX) { + ptr = &atom->v[0][0]; + nstride = 3; + } else if (thresh_array[ithresh] == VY) { + ptr = &atom->v[0][1]; + nstride = 3; + } else if (thresh_array[ithresh] == VZ) { + ptr = &atom->v[0][2]; + nstride = 3; + } else if (thresh_array[ithresh] == FX) { + ptr = &atom->f[0][0]; + nstride = 3; + } else if (thresh_array[ithresh] == FY) { + ptr = &atom->f[0][1]; + nstride = 3; + } else if (thresh_array[ithresh] == FZ) { + ptr = &atom->f[0][2]; + nstride = 3; + } else if (thresh_array[ithresh] == Q) { + ptr = atom->q; + nstride = 1; + } else if (thresh_array[ithresh] == MUX) { + ptr = &atom->mu[0][0]; + nstride = 3; + } else if (thresh_array[ithresh] == MUY) { + ptr = &atom->mu[0][1]; + nstride = 3; + } else if (thresh_array[ithresh] == MUZ) { + ptr = &atom->mu[0][2]; + nstride = 3; + } else if (thresh_array[ithresh] == TQX) { + ptr = &atom->torque[0][0]; + nstride = 3; + } else if (thresh_array[ithresh] == TQY) { + ptr = &atom->torque[0][1]; + nstride = 3; + } else if (thresh_array[ithresh] == TQZ) { + ptr = &atom->torque[0][2]; + nstride = 3; + } else if (thresh_array[ithresh] == CENTRO) { + ptr = ((FixCentro *) modify->fix[ifix_centro])->centro; + nstride = 3; + } else if (thresh_array[ithresh] == ENG) { + ptr = ((FixEnergy *) modify->fix[ifix_energy])->energy; + nstride = 3; + } else if (thresh_array[ithresh] == SXX) { + ptr = &((FixStress *) modify->fix[ifix_stress])->stress[0][0]; + nstride = 6; + } else if (thresh_array[ithresh] == SYY) { + ptr = &((FixStress *) modify->fix[ifix_stress])->stress[0][1]; + nstride = 6; + } else if (thresh_array[ithresh] == SZZ) { + ptr = &((FixStress *) modify->fix[ifix_stress])->stress[0][2]; + nstride = 6; + } else if (thresh_array[ithresh] == SXY) { + ptr = &((FixStress *) modify->fix[ifix_stress])->stress[0][3]; + nstride = 6; + } else if (thresh_array[ithresh] == SXZ) { + ptr = &((FixStress *) modify->fix[ifix_stress])->stress[0][4]; + nstride = 6; + } else if (thresh_array[ithresh] == SYZ) { + ptr = &((FixStress *) modify->fix[ifix_stress])->stress[0][5]; + nstride = 6; + } + + value = thresh_value[ithresh]; + + if (thresh_op[ithresh] == GE) { + for (i = 0; i < nlocal; i++, ptr += nstride) + if (choose[i] && *ptr >= value) { + choose[i] = 0; + nmine--; + } + } else if (thresh_op[ithresh] == GT) { + for (i = 0; i < nlocal; i++, ptr += nstride) + if (choose[i] && *ptr > value) { + choose[i] = 0; + nmine--; + } + } else if (thresh_op[ithresh] == LE) { + for (i = 0; i < nlocal; i++, ptr += nstride) + if (choose[i] && *ptr <= value) { + choose[i] = 0; + nmine--; + } + } else if (thresh_op[ithresh] == LT) { + for (i = 0; i < nlocal; i++, ptr += nstride) + if (choose[i] && *ptr < value) { + choose[i] = 0; + nmine--; + } + } else if (thresh_op[ithresh] == EQ) { + for (i = 0; i < nlocal; i++, ptr += nstride) + if (choose[i] && *ptr == value) { + choose[i] = 0; + nmine--; + } + } else if (thresh_op[ithresh] == NEQ) { + for (i = 0; i < nlocal; i++, ptr += nstride) + if (choose[i] && *ptr != value) { + choose[i] = 0; + nmine--; + } + } + } + } + + return nmine; +} + +/* ---------------------------------------------------------------------- */ + +int DumpCustom::pack() +{ + for (int n = 0; n < size_one; n++) (this->*pack_choice[n])(n); + return nmine*size_one; +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::write_data(int n, double *buf) +{ + (this->*write_choice)(n,buf); +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::header_binary(int ndump) +{ + fwrite(&update->ntimestep,sizeof(int),1,fp); + fwrite(&ndump,sizeof(int),1,fp); + fwrite(&domain->boxxlo,sizeof(double),1,fp); + fwrite(&domain->boxxhi,sizeof(double),1,fp); + fwrite(&domain->boxylo,sizeof(double),1,fp); + fwrite(&domain->boxyhi,sizeof(double),1,fp); + fwrite(&domain->boxzlo,sizeof(double),1,fp); + fwrite(&domain->boxzhi,sizeof(double),1,fp); + fwrite(&size_one,sizeof(int),1,fp); + if (multiproc) { + int one = 1; + fwrite(&one,sizeof(int),1,fp); + } else fwrite(&nprocs,sizeof(int),1,fp); +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::header_item(int ndump) +{ + fprintf(fp,"ITEM: TIMESTEP\n"); + fprintf(fp,"%d\n",update->ntimestep); + fprintf(fp,"ITEM: NUMBER OF ATOMS\n"); + fprintf(fp,"%d\n",ndump); + fprintf(fp,"ITEM: BOX BOUNDS\n"); + fprintf(fp,"%g %g\n",domain->boxxlo,domain->boxxhi); + fprintf(fp,"%g %g\n",domain->boxylo,domain->boxyhi); + fprintf(fp,"%g %g\n",domain->boxzlo,domain->boxzhi); + fprintf(fp,"ITEM: ATOMS\n"); +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::write_binary(int n, double *buf) +{ + n *= size_one; + fwrite(&n,sizeof(int),1,fp); + fwrite(buf,sizeof(double),n,fp); +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::write_text(int n, double *buf) +{ + int i,j; + + int m = 0; + for (i = 0; i < n; i++) { + for (j = 0; j < size_one; j++) { + if (vtype[j] == INT) fprintf(fp,vformat[j],static_cast<int> (buf[m])); + else fprintf(fp,vformat[j],buf[m]); + m++; + } + fprintf(fp,"\n"); + } +} + +/* ---------------------------------------------------------------------- + create a fix of style keyword for computing a per-atom custom quantity + fix-ID = dump-ID + keyword +------------------------------------------------------------------------- */ + +void DumpCustom::fix_create(char *keyword) +{ + char **fixarg = new char*[3]; + + int n = strlen(id) + strlen(keyword) + 1; + fixarg[0] = new char[n]; + strcpy(fixarg[0],id); + strcat(fixarg[0],keyword); + + n = strlen(group->names[igroup]) + 1; + fixarg[1] = new char[n]; + strcpy(fixarg[1],group->names[igroup]); + + n = strlen(keyword) + 1; + fixarg[2] = new char[n]; + strcpy(fixarg[2],keyword); + + modify->add_fix(3,fixarg); + for (int i = 0; i < 3; i++) delete [] fixarg[i]; + delete [] fixarg; +} + +/* ---------------------------------------------------------------------- + delete a fix of style keyword for computing a per-atom custom quantity +------------------------------------------------------------------------- */ + +void DumpCustom::fix_delete(char *keyword) +{ + int n = strlen(id) + strlen(keyword) + 1; + char *name = new char[n]; + strcpy(name,id); + strcat(name,keyword); + modify->delete_fix(name); + delete [] name; +} + +/* ---------------------------------------------------------------------- + find which fix matches keyword + fix was created by dump custom, so must exist +------------------------------------------------------------------------- */ + +int DumpCustom::fix_match(char *keyword) +{ + int n = strlen(id) + strlen(keyword) + 1; + char *name = new char[n]; + strcpy(name,id); + strcat(name,keyword); + + int i; + for (i = 0; i < modify->nfix; i++) + if (strcmp(name,modify->fix[i]->id) == 0) break; + + delete [] name; + return i; +} + +// ---------------------------------------------------------------------- +// one routine for every kind of quantity dump custom can output +// the atom quantity is packed into buf starting at n with stride size_one +// customize by adding a method +// ---------------------------------------------------------------------- + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_tag(int n) +{ + int *tag = atom->tag; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = tag[i]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_molecule(int n) +{ + int *molecule = atom->molecule; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = molecule[i]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_type(int n) +{ + int *type = atom->type; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = type[i]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_x(int n) +{ + double **x = atom->x; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = x[i][0]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_y(int n) +{ + double **x = atom->x; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = x[i][1]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_z(int n) +{ + double **x = atom->x; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = x[i][2]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_xs(int n) +{ + double **x = atom->x; + int nlocal = atom->nlocal; + + double boxxlo = domain->boxxlo; + double invxprd = 1.0/domain->xprd; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = (x[i][0] - boxxlo) * invxprd; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_ys(int n) +{ + double **x = atom->x; + int nlocal = atom->nlocal; + + double boxylo = domain->boxylo; + double invyprd = 1.0/domain->yprd; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = (x[i][1] - boxylo) * invyprd; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_zs(int n) +{ + double **x = atom->x; + int nlocal = atom->nlocal; + + double boxzlo = domain->boxzlo; + double invzprd = 1.0/domain->zprd; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = (x[i][2] - boxzlo) * invzprd; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_xu(int n) +{ + double **x = atom->x; + int *image = atom->image; + double xprd = domain->xprd; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = x[i][0] + ((image[i] & 1023) - 512) * xprd; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_yu(int n) +{ + double **x = atom->x; + int *image = atom->image; + double yprd = domain->yprd; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = x[i][1] + ((image[i] >> 10 & 1023) - 512) * yprd; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_zu(int n) +{ + double **x = atom->x; + int *image = atom->image; + double zprd = domain->zprd; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = x[i][2] + ((image[i] >> 20) - 512) * zprd; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_ix(int n) +{ + int *image = atom->image; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = (image[i] & 1023) - 512; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_iy(int n) +{ + int *image = atom->image; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = (image[i] >> 10 & 1023) - 512; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_iz(int n) +{ + int *image = atom->image; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = (image[i] >> 20) - 512; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_vx(int n) +{ + double **v = atom->v; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = v[i][0]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_vy(int n) +{ + double **v = atom->v; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = v[i][1]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_vz(int n) +{ + double **v = atom->v; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = v[i][2]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_fx(int n) +{ + double **f = atom->f; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = f[i][0]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_fy(int n) +{ + double **f = atom->f; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = f[i][1]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_fz(int n) +{ + double **f = atom->f; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = f[i][2]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_q(int n) +{ + double *q = atom->q; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = q[i]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_mux(int n) +{ + double **mu = atom->mu; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = mu[i][0]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_muy(int n) +{ + double **mu = atom->mu; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = mu[i][1]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_muz(int n) +{ + double **mu = atom->mu; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = mu[i][2]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_tqx(int n) +{ + double **torque = atom->torque; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = torque[i][0]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_tqy(int n) +{ + double **torque = atom->torque; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = torque[i][1]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_tqz(int n) +{ + double **torque = atom->torque; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = torque[i][2]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_centro(int n) +{ + double *centro = ((FixCentro *) modify->fix[ifix_centro])->centro; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = centro[i]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_energy(int n) +{ + double *energy = ((FixEnergy *) modify->fix[ifix_energy])->energy; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = energy[i]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_sxx(int n) +{ + double **stress = ((FixStress *) modify->fix[ifix_stress])->stress; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = stress[i][0]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_syy(int n) +{ + double **stress = ((FixStress *) modify->fix[ifix_stress])->stress; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = stress[i][1]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_szz(int n) +{ + double **stress = ((FixStress *) modify->fix[ifix_stress])->stress; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = stress[i][2]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_sxy(int n) +{ + double **stress = ((FixStress *) modify->fix[ifix_stress])->stress; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = stress[i][3]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_sxz(int n) +{ + double **stress = ((FixStress *) modify->fix[ifix_stress])->stress; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = stress[i][4]; + n += size_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpCustom::pack_syz(int n) +{ + double **stress = ((FixStress *) modify->fix[ifix_stress])->stress; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (choose[i]) { + buf[n] = stress[i][5]; + n += size_one; + } +} diff --git a/src/dump_custom.h b/src/dump_custom.h new file mode 100644 index 0000000000000000000000000000000000000000..c58f9653383495012a69d28dffb6f5d0bf1f1a62 --- /dev/null +++ b/src/dump_custom.h @@ -0,0 +1,113 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DUMP_CUSTOM_H +#define DUMP_CUSTOM_H + +#include "dump.h" + +class DumpCustom : public Dump { + public: + DumpCustom(int, char **); + ~DumpCustom(); + void init(); + int memory_usage(); + + private: + int iregion; // -1 if no region, else which region + int nthresh; // # of defined threshholds + int *thresh_array; // array to threshhhold on for each nthresh + int *thresh_op; // threshhold operation for each nthresh + double *thresh_value; // threshhold value for each nthresh + + int nmine; // # of lines I am dumping + int *vtype; // type of each vector (INT, DOUBLE) + char **vformat; // format string for each vector element + + int maxchoose; // size of choose arrays + int *choose; // 1 if output this atom, 0 if no + double *dchoose; // value for each atom to threshhold against + + int nevery; + + int fixflag; // 1 if this dump invokes any fixes, 0 if not + int fixflag_centro; // 1 if this fix is invoked, 0 if not + int fixflag_energy; + int fixflag_stress; + int ifix_centro; // which fix it is (0 to nfixes-1) + int ifix_energy; + int ifix_stress; + + int modify_param(int, char **); + void write_header(int); + int count(); + int pack(); + void write_data(int, double *); + + void fix_create(char *); + void fix_delete(char *); + int fix_match(char *); + + typedef void (DumpCustom::*FnPtrHeader)(int); + FnPtrHeader header_choice; // ptr to write header functions + void header_binary(int); + void header_item(int); + + // customize by adding a method prototype + + typedef void (DumpCustom::*FnPtrPack)(int); + FnPtrPack *pack_choice; // ptrs to pack functions + void pack_tag(int); + void pack_molecule(int); + void pack_type(int); + void pack_x(int); + void pack_y(int); + void pack_z(int); + void pack_xs(int); + void pack_ys(int); + void pack_zs(int); + void pack_xu(int); + void pack_yu(int); + void pack_zu(int); + void pack_ix(int); + void pack_iy(int); + void pack_iz(int); + void pack_vx(int); + void pack_vy(int); + void pack_vz(int); + void pack_fx(int); + void pack_fy(int); + void pack_fz(int); + void pack_q(int); + void pack_mux(int); + void pack_muy(int); + void pack_muz(int); + void pack_tqx(int); + void pack_tqy(int); + void pack_tqz(int); + void pack_centro(int); + void pack_energy(int); + void pack_sxx(int); + void pack_syy(int); + void pack_szz(int); + void pack_sxy(int); + void pack_sxz(int); + void pack_syz(int); + + typedef void (DumpCustom::*FnPtrData)(int, double *); + FnPtrData write_choice; // ptr to write data functions + void write_binary(int, double *); + void write_text(int, double *); +}; + +#endif diff --git a/src/dump_dcd.cpp b/src/dump_dcd.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2a520b44d16a86ce449e8deef8ba136dad39f998 --- /dev/null +++ b/src/dump_dcd.cpp @@ -0,0 +1,286 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Naveen Michaud-Agrawal (Johns Hopkins U) +------------------------------------------------------------------------- */ + +#include "inttypes.h" +#include "stdio.h" +#include "time.h" +#include "string.h" +#include "dump_dcd.h" +#include "domain.h" +#include "atom.h" +#include "update.h" +#include "output.h" +#include "group.h" +#include "memory.h" +#include "error.h" + +#define NFILE_POS 8L +#define NSTEP_POS 20L + +// necessary to set SEEK params b/c MPI-2 messes with these settings + +#ifndef SEEK_SET +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 +#endif + +/* ---------------------------------------------------------------------- */ + +DumpDCD::DumpDCD(int narg, char **arg) : Dump(narg, arg) +{ + if (narg != 5) error->all("Illegal dump dcd command"); + if (igroup != group->find("all")) error->all("Dump dcd must use group all"); + if (binary || compressed || multifile || multiproc) + error->all("Invalid dump dcd filename"); + + size_one = 4; + format_default = NULL; + + // allocate global array for atom coords + + natoms = static_cast<int> (atom->natoms); + if (natoms <= 0) error->all("Invalid natoms for dump dcd"); + if (atom->tag_consecutive() == 0) + error->all("Atom IDs must be consecutive for dump dcd"); + coords = (float *) memory->smalloc(3*natoms*sizeof(float),"dump:coords"); + xf = &coords[0*natoms]; + yf = &coords[1*natoms]; + zf = &coords[2*natoms]; + + openfile(); + headerflag = 0; + nevery_save = 0; + ntotal = 0; +} + +/* ---------------------------------------------------------------------- */ + +DumpDCD::~DumpDCD() +{ + memory->sfree(coords); +} + +/* ---------------------------------------------------------------------- */ + +void DumpDCD::init() +{ + // check that dump frequency has not changed + + if (nevery_save == 0) { + int idump; + for (idump = 0; idump < output->ndump; idump++) + if (strcmp(id,output->dump[idump]->id) == 0) break; + nevery_save = output->dump_every[idump]; + } else { + int idump; + for (idump = 0; idump < output->ndump; idump++) + if (strcmp(id,output->dump[idump]->id) == 0) break; + if (nevery_save != output->dump_every[idump]) + error->all("Cannot change dump_modify every for dump dcd"); + } +} + +/* ---------------------------------------------------------------------- + return # of bytes of allocated memory in buf and global coords array +------------------------------------------------------------------------- */ + +int DumpDCD::memory_usage() +{ + int bytes = maxbuf * sizeof(double); + bytes += 3*natoms * sizeof(float); + return bytes; +} + +/* ---------------------------------------------------------------------- */ + +void DumpDCD::openfile() +{ + if (me == 0) { + fp = fopen(filename,"wb"); + if (fp == NULL) error->one("Cannot open dump file"); + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpDCD::write_header(int n) +{ + if (n != natoms) error->all("Dump dcd of non-matching # of atoms"); + + // first time, write header for entire file + + if (headerflag == 0) { + if (me == 0) write_dcd_header("Written by LAMMPS"); + headerflag = 1; + nframes = 0; + } + + // dim[134] = angle cosines of periodic box + // 48 = 6 doubles + + double dim[6]; + dim[0] = domain->boxxhi - domain->boxxlo; + dim[2] = domain->boxyhi - domain->boxylo; + dim[5] = domain->boxzhi - domain->boxzlo; + dim[1] = dim[3] = dim[4] = 0.0; + + uint32_t out_integer = 48; + fwrite(&out_integer,sizeof(uint32_t),1,fp); + fwrite(dim,out_integer,1,fp); + fwrite(&out_integer,sizeof(uint32_t),1,fp); +} + +/* ---------------------------------------------------------------------- */ + +int DumpDCD::count() +{ + return atom->nlocal; +} + +/* ---------------------------------------------------------------------- */ + +int DumpDCD::pack() +{ + int *tag = atom->tag; + double **x = atom->x; + int nlocal = atom->nlocal; + + // assume group all, so no need to perform mask check + + int m = 0; + for (int i = 0; i < nlocal; i++) { + buf[m++] = tag[i]; + buf[m++] = x[i][0]; + buf[m++] = x[i][1]; + buf[m++] = x[i][2]; + } + + return m; +} + +/* ---------------------------------------------------------------------- */ + +void DumpDCD::write_data(int n, double *buf) +{ + // spread buf atom coords into global arrays + + int tag; + int m = 0; + for (int i = 0; i < n; i++) { + tag = static_cast<int> (buf[m]) - 1; + xf[tag] = buf[m+1]; + yf[tag] = buf[m+2]; + zf[tag] = buf[m+3]; + m += size_one; + } + + // if last chunk of atoms in this snapshot, write global arrays to file + + ntotal += n; + if (ntotal == natoms) { + write_frame(); + ntotal = 0; + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpDCD::write_frame() +{ + // write coords + + uint32_t out_integer = natoms*sizeof(float); + fwrite(&out_integer,sizeof(uint32_t),1,fp); + fwrite(xf,out_integer,1,fp); + fwrite(&out_integer,sizeof(uint32_t),1,fp); + fwrite(&out_integer,sizeof(uint32_t),1,fp); + fwrite(yf,out_integer,1,fp); + fwrite(&out_integer,sizeof(uint32_t),1,fp); + fwrite(&out_integer,sizeof(uint32_t),1,fp); + fwrite(zf,out_integer,1,fp); + fwrite(&out_integer,sizeof(uint32_t),1,fp); + + // update NFILE and NSTEP fields in DCD header + + nframes++; + out_integer = nframes; + fseek(fp,NFILE_POS,SEEK_SET); + fwrite(&out_integer,sizeof(uint32_t),1,fp); + out_integer = update->ntimestep; + fseek(fp,NSTEP_POS,SEEK_SET); + fwrite(&out_integer,sizeof(uint32_t),1,fp); + fseek(fp,0,SEEK_END); +} + +/* ---------------------------------------------------------------------- */ + +void DumpDCD::write_dcd_header(char *remarks) +{ + uint32_t out_integer; + float out_float; + char title_string[200]; + time_t cur_time; + struct tm *tmbuf; + char time_str[81]; + + out_integer = 84; + fwrite(&out_integer,sizeof(uint32_t),1,fp); + strcpy(title_string,"CORD"); + fwrite(title_string,4,1,fp); + fwrite_int32(fp,0); // NFILE = # of snapshots in file + fwrite_int32(fp,update->ntimestep); // START = timestep of first snapshot + fwrite_int32(fp,nevery_save); // SKIP = interval between snapshots + fwrite_int32(fp,update->ntimestep); // NSTEP = timestep of last snapshot + fwrite_int32(fp,0); // NAMD writes NSTEP or ISTART + fwrite_int32(fp,0); + fwrite_int32(fp,0); + fwrite_int32(fp,0); + fwrite_int32(fp,0); + out_float = update->dt; + fwrite(&out_float,sizeof(float),1,fp); + fwrite_int32(fp,1); + fwrite_int32(fp,0); + fwrite_int32(fp,0); + fwrite_int32(fp,0); + fwrite_int32(fp,0); + fwrite_int32(fp,0); + fwrite_int32(fp,0); + fwrite_int32(fp,0); + fwrite_int32(fp,0); + fwrite_int32(fp,24); // pretend to be Charmm version 24 + fwrite_int32(fp,84); + fwrite_int32(fp,164); + fwrite_int32(fp,2); + strncpy(title_string,remarks,80); + title_string[79] = '\0'; + fwrite(title_string,80,1,fp); + cur_time=time(NULL); + tmbuf=localtime(&cur_time); + strftime(time_str,80,"REMARKS Created %d %B,%Y at %R",tmbuf); + fwrite(time_str,80,1,fp); + fwrite_int32(fp,164); + fwrite_int32(fp,4); + fwrite_int32(fp,natoms); // number of atoms in each snapshot + fwrite_int32(fp,4); +} + +/* ---------------------------------------------------------------------- */ + +void DumpDCD::fwrite_int32(FILE* fp, uint32_t i) { + fwrite(&i,4,1,fp); +} diff --git a/src/dump_dcd.h b/src/dump_dcd.h new file mode 100644 index 0000000000000000000000000000000000000000..fa205ef5647d04a08ca81704ed8464dc0f3536e4 --- /dev/null +++ b/src/dump_dcd.h @@ -0,0 +1,43 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DUMP_DCD_H +#define DUMP_DCD_H + +#include "stdio.h" +#include "dump.h" +#include "inttypes.h" + +class DumpDCD : public Dump { + public: + DumpDCD(int, char**); + ~DumpDCD(); + void init(); + int memory_usage(); + + private: + int natoms,ntotal,headerflag,nevery_save,nframes; + float *coords,*xf,*yf,*zf; + + void openfile(); + void write_header(int); + int count(); + int pack(); + void write_data(int, double *); + + void write_frame(); + void write_dcd_header(char *); + void fwrite_int32(FILE *, uint32_t); +}; + +#endif diff --git a/src/dump_xyz.cpp b/src/dump_xyz.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3df2cd984cec32ed422496f55aec14abd2bac687 --- /dev/null +++ b/src/dump_xyz.cpp @@ -0,0 +1,197 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "dump_xyz.h" +#include "atom.h" +#include "group.h" +#include "error.h" +#include "memory.h" + +/* ---------------------------------------------------------------------- */ + +DumpXYZ::DumpXYZ(int narg, char **arg) : Dump(narg, arg) +{ + if (narg != 5) error->all("Illegal dump xyz command"); + if (binary || multiproc) error->all("Invalid dump xyz filename"); + + size_one = 5; + + char *str = "%d %g %g %g"; + int n = strlen(str) + 1; + format_default = new char[n]; + strcpy(format_default,str); + + // allocate global array for atom coords if group is all + + if (igroup == 0) { + natoms = static_cast<int> (atom->natoms); + if (natoms <= 0) error->all("Invalid natoms for dump xyz"); + if (atom->tag_consecutive() == 0) + error->all("Atom IDs must be consecutive for dump xyz"); + types = (int *) memory->smalloc(natoms*sizeof(int),"dump:types"); + coords = (float *) memory->smalloc(3*natoms*sizeof(float),"dump:coords"); + } + + // one-time file open + + if (multifile == 0) openfile(); + ntotal = 0; +} + +/* ---------------------------------------------------------------------- */ + +DumpXYZ::~DumpXYZ() +{ + if (igroup == 0) { + memory->sfree(types); + memory->sfree(coords); + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpXYZ::init() +{ + delete [] format; + char *str; + if (format_user) str = format_user; + else str = format_default; + + int n = strlen(str) + 2; + format = new char[n]; + strcpy(format,str); + strcat(format,"\n"); +} + +/* ---------------------------------------------------------------------- + return # of bytes of allocated memory in buf and global coords array +------------------------------------------------------------------------- */ + +int DumpXYZ::memory_usage() +{ + int bytes = maxbuf * sizeof(double); + if (igroup == 0) { + bytes += natoms * sizeof(int); + bytes += 3*natoms * sizeof(float); + } + return bytes; +} + +/* ---------------------------------------------------------------------- */ + +void DumpXYZ::write_header(int n) +{ + // realloc types & coords if necessary + + if (igroup == 0 && n != natoms) { + memory->sfree(types); + memory->sfree(coords); + if (atom->tag_consecutive() == 0) + error->all("Atom IDs must be consecutive for dump xyz"); + natoms = n; + types = (int *) memory->smalloc(natoms*sizeof(int),"dump:types"); + coords = (float *) memory->smalloc(3*natoms*sizeof(float),"dump:coords"); + } + + fprintf(fp,"%d\n",n); + fprintf(fp,"Atoms\n"); +} + +/* ---------------------------------------------------------------------- */ + +int DumpXYZ::count() +{ + if (igroup == 0) return atom->nlocal; + + int *mask = atom->mask; + int nlocal = atom->nlocal; + + int m = 0; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) m++; + return m; +} + +/* ---------------------------------------------------------------------- */ + +int DumpXYZ::pack() +{ + int *tag = atom->tag; + int *type = atom->type; + int *mask = atom->mask; + double **x = atom->x; + int nlocal = atom->nlocal; + + int m = 0; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + buf[m++] = tag[i]; + buf[m++] = type[i]; + buf[m++] = x[i][0]; + buf[m++] = x[i][1]; + buf[m++] = x[i][2]; + } + + return m; +} + +/* ---------------------------------------------------------------------- */ + +void DumpXYZ::write_data(int n, double *buf) +{ + // for group = all, spread buf atom coords into global arrays + // if last chunk of atoms in this snapshot, write global arrays to file + + if (igroup == 0) { + int j,tag; + + int m = 0; + for (int i = 0; i < n; i++) { + tag = static_cast<int> (buf[m]) - 1; + types[tag] = static_cast<int> (buf[m+1]); + j = 3*tag; + coords[j++] = buf[m+2]; + coords[j++] = buf[m+3]; + coords[j] = buf[m+4]; + m += size_one; + } + + ntotal += n; + if (ntotal == natoms) { + write_frame(); + ntotal = 0; + } + + // for group != all, write unsorted type,x,y,z to file + + } else { + int m = 0; + for (int i = 0; i < n; i++) { + fprintf(fp,format, + static_cast<int> (buf[m+1]),buf[m+2],buf[m+3],buf[m+4]); + m += size_one; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void DumpXYZ::write_frame() +{ + int m = 0; + for (int i = 0; i < natoms; i++) { + fprintf(fp,format,types[i],coords[m],coords[m+1],coords[m+2]); + m += 3; + } +} diff --git a/src/dump_xyz.h b/src/dump_xyz.h new file mode 100644 index 0000000000000000000000000000000000000000..dd0d90ec2904db37d140751359cf283e37bb1553 --- /dev/null +++ b/src/dump_xyz.h @@ -0,0 +1,38 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef DUMP_XYZ_H +#define DUMP_XYZ_H + +#include "dump.h" + +class DumpXYZ : public Dump { + public: + DumpXYZ(int, char**); + ~DumpXYZ(); + void init(); + int memory_usage(); + + private: + int natoms,ntotal; + int *types; + float *coords; + + void write_header(int); + int count(); + int pack(); + void write_data(int, double *); + void write_frame(); +}; + +#endif diff --git a/src/error.cpp b/src/error.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5221ef16fe4c6890cf53746836e9dce5d1072c1d --- /dev/null +++ b/src/error.cpp @@ -0,0 +1,106 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "stdlib.h" +#include "error.h" +#include "universe.h" +#include "memory.h" +#include "output.h" + +/* ---------------------------------------------------------------------- + called by all procs in universe + close all output, screen, and log files in world and universe +------------------------------------------------------------------------- */ + +void Error::universe_all(char *str) +{ + MPI_Barrier(universe->uworld); + + if (universe->me == 0) { + if (universe->uscreen) fprintf(universe->uscreen,"ERROR: %s\n",str); + if (universe->ulogfile) fprintf(universe->ulogfile,"ERROR: %s\n",str); + } + + if (output) delete output; + if (universe->nworlds > 1) { + if (screen && screen != stdout) fclose(screen); + if (logfile) fclose(logfile); + } + if (universe->ulogfile) fclose(universe->ulogfile); + + MPI_Finalize(); + exit(1); +} + +/* ---------------------------------------------------------------------- + called by one proc in universe +------------------------------------------------------------------------- */ + +void Error::universe_one(char *str) +{ + if (universe->uscreen) + fprintf(universe->uscreen,"ERROR on proc %d: %s\n",universe->me,str); + MPI_Abort(universe->uworld,1); +} + +/* ---------------------------------------------------------------------- + called by all procs in one world + close all output, screen, and log files in world +------------------------------------------------------------------------- */ + +void Error::all(char *str) +{ + MPI_Barrier(world); + + int me; + MPI_Comm_rank(world,&me); + + if (me == 0) { + if (screen) fprintf(screen,"ERROR: %s\n",str); + if (logfile) fprintf(logfile,"ERROR: %s\n",str); + } + + if (output) delete output; + if (screen && screen != stdout) fclose(screen); + if (logfile) fclose(logfile); + + MPI_Finalize(); + exit(1); +} + +/* ---------------------------------------------------------------------- + called by one proc in world + write to world screen only if non-NULL on this proc + always write to universe screen +------------------------------------------------------------------------- */ + +void Error::one(char *str) +{ + int me; + MPI_Comm_rank(world,&me); + if (screen) fprintf(screen,"ERROR on proc %d: %s\n",me,str); + if (universe->nworlds > 1) + fprintf(universe->uscreen,"ERROR on proc %d: %s\n",universe->me,str); + MPI_Abort(world,1); +} + +/* ---------------------------------------------------------------------- + called by one proc in world + only write to screen if non-NULL on this proc since could be file +------------------------------------------------------------------------- */ + +void Error::warning(char *str) +{ + if (screen) fprintf(screen,"WARNING: %s\n",str); +} diff --git a/src/error.h b/src/error.h new file mode 100644 index 0000000000000000000000000000000000000000..40c28f7dd545f6d3e0c68d60432eba4e1b640856 --- /dev/null +++ b/src/error.h @@ -0,0 +1,33 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef ERROR_H +#define ERROR_H + +#include "stdio.h" +#include "lammps.h" + +class Error : public LAMMPS { + public: + Error() {} + ~Error() {} + + void universe_all(char *); + void universe_one(char *); + + void all(char *); + void one(char *); + void warning(char *); +}; + +#endif diff --git a/src/ewald.cpp b/src/ewald.cpp new file mode 100644 index 0000000000000000000000000000000000000000..202a6779997a58428af1c0c2c8245160de56d986 --- /dev/null +++ b/src/ewald.cpp @@ -0,0 +1,846 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Roy Pollock (LLNL), Paul Crozier (SNL) +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "stdlib.h" +#include "stdio.h" +#include "math.h" +#include "ewald.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "pair_buck_coul_long.h" +#include "pair_lj_cut_coul_long.h" +#include "pair_lj_charmm_coul_long.h" +#include "pair_lj_class2_coul_long.h" +#include "pair_table.h" +#include "domain.h" +#include "memory.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +Ewald::Ewald(int narg, char **arg) : KSpace(narg, arg) +{ + if (narg != 1) error->all("Illegal kspace_style ewald command"); + + precision = atof(arg[0]); + PI = 4.0*atan(1.0); + + kmax = 0; + kxvecs = kyvecs = kzvecs = NULL; + ug = NULL; + eg = vg = NULL; + sfacrl = sfacim = sfacrl_all = sfacim_all = NULL; + + nmax = 0; + ek = NULL; + cs = sn = NULL; + + kcount = 0; +} + +/* ---------------------------------------------------------------------- + free all memory +------------------------------------------------------------------------- */ + +Ewald::~Ewald() +{ + deallocate(); + memory->destroy_2d_double_array(ek); + memory->destroy_3d_double_array(cs,-kmax_created); + memory->destroy_3d_double_array(sn,-kmax_created); +} + +/* ---------------------------------------------------------------------- */ + +void Ewald::init() +{ + if (comm->me == 0) { + if (screen) fprintf(screen,"Ewald initialization ...\n"); + if (logfile) fprintf(logfile,"Ewald initialization ...\n"); + } + + // error check + + if (force->dimension == 2) error->all("Cannot use Ewald with 2d simulation"); + + if (slabflag == 0 && domain->nonperiodic > 0) + error->all("Cannot use nonperiodic boundaries with Ewald"); + if (slabflag == 1) { + if (domain->xperiodic != 1 || domain->yperiodic != 1 || + domain->boundary[2][0] != 1 || domain->boundary[2][1] != 1) + error->all("Incorrect boundaries with slab Ewald"); + } + + // insure use of pair_style with long-range Coulombics + // set cutoff to short-range Coulombic cutoff + + qqrd2e = force->qqrd2e; + + double cutoff; + + Pair *anypair; + if (force->pair == NULL) + error->all("KSpace style is incompatible with Pair style"); + else if (anypair = force->pair_match("buck/coul/long")) + cutoff = ((PairBuckCoulLong *) anypair)->cut_coul; + else if (anypair = force->pair_match("lj/cut/coul/long")) + cutoff = ((PairLJCutCoulLong *) anypair)->cut_coul; + else if (anypair = force->pair_match("lj/charmm/coul/long")) + cutoff = ((PairLJCharmmCoulLong *) anypair)->cut_coul; + else if (anypair = force->pair_match("lj/class2/coul/long")) + cutoff = ((PairLJClass2CoulLong *) anypair)->cut_coul; + else if (anypair = force->pair_match("table")) + cutoff = ((PairTable *) anypair)->cut_coul(); + else error->all("KSpace style is incompatible with Pair style"); + + // compute qsum & qsqsum + + double tmp; + + qsum = 0.0; + for (int i = 0; i < atom->nlocal; i++) qsum += atom->q[i]; + MPI_Allreduce(&qsum,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + qsum = tmp; + + qsqsum = 0.0; + for (int i = 0; i < atom->nlocal; i++) qsqsum += atom->q[i]*atom->q[i]; + MPI_Allreduce(&qsqsum,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + qsqsum = tmp; + + // setup K-space resolution + + g_ewald = (1.35 - 0.15*log(precision))/cutoff; + gsqmx = -4.0*g_ewald*g_ewald*log(precision); + + if (comm->me == 0) { + if (screen) fprintf(screen," G vector = %g\n",g_ewald); + if (logfile) fprintf(logfile," G vector = %g\n",g_ewald); + } +} + +/* ---------------------------------------------------------------------- + adjust Ewald coeffs, called initially and whenever volume has changed +------------------------------------------------------------------------- */ + +void Ewald::setup() +{ + // volume-dependent factors + + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + + // adjustment of z dimension for 2d slab Ewald + // 3d Ewald just uses zprd since slab_volfactor = 1.0 + + double zprd_slab = zprd*slab_volfactor; + volume = xprd * yprd * zprd_slab; + + unitk[0] = 2.0*PI/xprd; + unitk[1] = 2.0*PI/yprd; + unitk[2] = 2.0*PI/zprd_slab; + + // determine kmax + // function of current box size, precision, G_ewald (short-range cutoff) + + int nkxmx = static_cast<int> ((g_ewald*xprd/PI) * sqrt(-log(precision))); + int nkymx = static_cast<int> ((g_ewald*yprd/PI) * sqrt(-log(precision))); + int nkzmx = static_cast<int> ((g_ewald*zprd_slab/PI) * sqrt(-log(precision))); + + int kmax_old = kmax; + kmax = MAX(nkxmx,nkymx); + kmax = MAX(kmax,nkzmx); + kmax3d = 4*kmax*kmax*kmax + 6*kmax*kmax + 3*kmax; + + // if size has grown, reallocate k-dependent and nlocal-dependent arrays + + if (kmax > kmax_old) { + deallocate(); + allocate(); + + memory->destroy_2d_double_array(ek); + memory->destroy_3d_double_array(cs,-kmax_created); + memory->destroy_3d_double_array(sn,-kmax_created); + nmax = atom->nmax; + ek = memory->create_2d_double_array(nmax,3,"ewald:ek"); + cs = memory->create_3d_double_array(-kmax,kmax,3,nmax,"ewald:cs"); + sn = memory->create_3d_double_array(-kmax,kmax,3,nmax,"ewald:sn"); + kmax_created = kmax; + } + + // pre-compute Ewald coefficients + + int kcount_old = kcount; + coeffs(); + + // if array sizes changed, print out new sizes + + if (kmax != kmax_old || kcount != kcount_old) { + if (comm->me == 0) { + if (screen) fprintf(screen," vectors: actual 1d max = %d %d %d\n", + kcount,kmax,kmax3d); + if (logfile) fprintf(logfile," vectors: actual 1d max = %d %d %d\n", + kcount,kmax,kmax3d); + } + } +} + +/* ---------------------------------------------------------------------- + compute the Ewald long-range force, energy, virial +------------------------------------------------------------------------- */ + +void Ewald::compute(int eflag, int vflag) +{ + int i,k,n; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + // extend size of nlocal-dependent arrays if necessary + + int nlocal = atom->nlocal; + if (nlocal > nmax) { + memory->destroy_2d_double_array(ek); + memory->destroy_3d_double_array(cs,-kmax_created); + memory->destroy_3d_double_array(sn,-kmax_created); + nmax = atom->nmax; + ek = memory->create_2d_double_array(nmax,3,"ewald:ek"); + cs = memory->create_3d_double_array(-kmax,kmax,3,nmax,"ewald:cs"); + sn = memory->create_3d_double_array(-kmax,kmax,3,nmax,"ewald:sn"); + kmax_created = kmax; + } + + // partial structure factors on each processor + // total structure factor by summing over procs + + eik_dot_r(); + MPI_Allreduce(sfacrl,sfacrl_all,kcount,MPI_DOUBLE,MPI_SUM,world); + MPI_Allreduce(sfacim,sfacim_all,kcount,MPI_DOUBLE,MPI_SUM,world); + + // K-space portion of electric field + // double loop over K-vectors and local atoms + + double **f = atom->f; + double *q = atom->q; + + int kx,ky,kz; + double cypz,sypz,exprl,expim,partial; + + for (i = 0; i < nlocal; i++) { + ek[i][0] = 0.0; + ek[i][1] = 0.0; + ek[i][2] = 0.0; + } + + for (k = 0; k < kcount; k++) { + kx = kxvecs[k]; + ky = kyvecs[k]; + kz = kzvecs[k]; + + for (i = 0; i < nlocal; i++) { + cypz = cs[ky][1][i]*cs[kz][2][i] - sn[ky][1][i]*sn[kz][2][i]; + sypz = sn[ky][1][i]*cs[kz][2][i] + cs[ky][1][i]*sn[kz][2][i]; + exprl = cs[kx][0][i]*cypz - sn[kx][0][i]*sypz; + expim = sn[kx][0][i]*cypz + cs[kx][0][i]*sypz; + partial = expim*sfacrl_all[k] - exprl*sfacim_all[k]; + ek[i][0] += partial*eg[k][0]; + ek[i][1] += partial*eg[k][1]; + ek[i][2] += partial*eg[k][2]; + } + } + + // convert E-field to force + + for (i = 0; i < nlocal; i++) { + f[i][0] += qqrd2e*q[i]*ek[i][0]; + f[i][1] += qqrd2e*q[i]*ek[i][1]; + f[i][2] += qqrd2e*q[i]*ek[i][2]; + } + + // energy if requested + + if (eflag) { + for (k = 0; k < kcount; k++) + energy += ug[k] * (sfacrl_all[k]*sfacrl_all[k] + + sfacim_all[k]*sfacim_all[k]); + PI = 4.0*atan(1.0); + energy -= g_ewald*qsqsum/1.772453851 + + 0.5*PI*qsum*qsum / (g_ewald*g_ewald*volume); + energy *= qqrd2e; + } + + // virial if requested + + if (vflag) { + double uk; + for (k = 0; k < kcount; k++) { + uk = ug[k] * (sfacrl_all[k]*sfacrl_all[k] + sfacim_all[k]*sfacim_all[k]); + for (n = 0; n < 6; n++) virial[n] += uk*vg[k][n]; + } + for (n = 0; n < 6; n++) virial[n] *= qqrd2e; + } + + if (slabflag) slabcorr(eflag); + +} + +/* ---------------------------------------------------------------------- */ + +void Ewald::eik_dot_r() +{ + int i,k,l,m,n,ic; + double cstr1,sstr1,cstr2,sstr2,cstr3,sstr3,cstr4,sstr4; + double sqk,clpm,slpm; + + double **x = atom->x; + double *q = atom->q; + int nlocal = atom->nlocal; + + n = 0; + + // (k,0,0), (0,l,0), (0,0,m) + + for (ic = 0; ic < 3; ic++) { + sqk = unitk[ic]*unitk[ic]; + if (sqk <= gsqmx) { + cstr1 = 0.0; + sstr1 = 0.0; + for (i = 0; i < nlocal; i++) { + cs[0][ic][i] = 1.0; + sn[0][ic][i] = 0.0; + cs[1][ic][i] = cos(unitk[ic]*x[i][ic]); + sn[1][ic][i] = sin(unitk[ic]*x[i][ic]); + cs[-1][ic][i] = cs[1][ic][i]; + sn[-1][ic][i] = -sn[1][ic][i]; + cstr1 += q[i]*cs[1][ic][i]; + sstr1 += q[i]*sn[1][ic][i]; + } + sfacrl[n] = cstr1; + sfacim[n++] = sstr1; + } + } + + for (m = 2; m <= kmax; m++) { + for (ic = 0; ic < 3; ic++) { + sqk = m*unitk[ic] * m*unitk[ic]; + if (sqk <= gsqmx) { + cstr1 = 0.0; + sstr1 = 0.0; + for (i = 0; i < nlocal; i++) { + cs[m][ic][i] = cs[m-1][ic][i]*cs[1][ic][i] - + sn[m-1][ic][i]*sn[1][ic][i]; + sn[m][ic][i] = sn[m-1][ic][i]*cs[1][ic][i] + + cs[m-1][ic][i]*sn[1][ic][i]; + cs[-m][ic][i] = cs[m][ic][i]; + sn[-m][ic][i] = -sn[m][ic][i]; + cstr1 += q[i]*cs[m][ic][i]; + sstr1 += q[i]*sn[m][ic][i]; + } + sfacrl[n] = cstr1; + sfacim[n++] = sstr1; + } + } + } + + // 1 = (k,l,0), 2 = (k,-l,0) + + for (k = 1; k <= kmax; k++) { + for (l = 1; l <= kmax; l++) { + sqk = (k*unitk[0] * k*unitk[0]) + (l*unitk[1] * l*unitk[1]); + if (sqk <= gsqmx) { + cstr1 = 0.0; + sstr1 = 0.0; + cstr2 = 0.0; + sstr2 = 0.0; + for (i = 0; i < nlocal; i++) { + cstr1 += q[i]*(cs[k][0][i]*cs[l][1][i] - sn[k][0][i]*sn[l][1][i]); + sstr1 += q[i]*(sn[k][0][i]*cs[l][1][i] + cs[k][0][i]*sn[l][1][i]); + cstr2 += q[i]*(cs[k][0][i]*cs[l][1][i] + sn[k][0][i]*sn[l][1][i]); + sstr2 += q[i]*(sn[k][0][i]*cs[l][1][i] - cs[k][0][i]*sn[l][1][i]); + } + sfacrl[n] = cstr1; + sfacim[n++] = sstr1; + sfacrl[n] = cstr2; + sfacim[n++] = sstr2; + } + } + } + + // 1 = (0,l,m), 2 = (0,l,-m) + + for (l = 1; l <= kmax; l++) { + for (m = 1; m <= kmax; m++) { + sqk = (l*unitk[1] * l*unitk[1]) + (m*unitk[2] * m*unitk[2]); + if (sqk <= gsqmx) { + cstr1 = 0.0; + sstr1 = 0.0; + cstr2 = 0.0; + sstr2 = 0.0; + for (i = 0; i < nlocal; i++) { + cstr1 += q[i]*(cs[l][1][i]*cs[m][2][i] - sn[l][1][i]*sn[m][2][i]); + sstr1 += q[i]*(sn[l][1][i]*cs[m][2][i] + cs[l][1][i]*sn[m][2][i]); + cstr2 += q[i]*(cs[l][1][i]*cs[m][2][i] + sn[l][1][i]*sn[m][2][i]); + sstr2 += q[i]*(sn[l][1][i]*cs[m][2][i] - cs[l][1][i]*sn[m][2][i]); + } + sfacrl[n] = cstr1; + sfacim[n++] = sstr1; + sfacrl[n] = cstr2; + sfacim[n++] = sstr2; + } + } + } + + // 1 = (k,0,m), 2 = (k,0,-m) + + for (k = 1; k <= kmax; k++) { + for (m = 1; m <= kmax; m++) { + sqk = (k*unitk[0] * k*unitk[0]) + (m*unitk[2] * m*unitk[2]); + if (sqk <= gsqmx) { + cstr1 = 0.0; + sstr1 = 0.0; + cstr2 = 0.0; + sstr2 = 0.0; + for (i = 0; i < nlocal; i++) { + cstr1 += q[i]*(cs[k][0][i]*cs[m][2][i] - sn[k][0][i]*sn[m][2][i]); + sstr1 += q[i]*(sn[k][0][i]*cs[m][2][i] + cs[k][0][i]*sn[m][2][i]); + cstr2 += q[i]*(cs[k][0][i]*cs[m][2][i] + sn[k][0][i]*sn[m][2][i]); + sstr2 += q[i]*(sn[k][0][i]*cs[m][2][i] - cs[k][0][i]*sn[m][2][i]); + } + sfacrl[n] = cstr1; + sfacim[n++] = sstr1; + sfacrl[n] = cstr2; + sfacim[n++] = sstr2; + } + } + } + + // 1 = (k,l,m), 2 = (k,-l,m), 3 = (k,l,-m), 4 = (k,-l,-m) + + for (k = 1; k <= kmax; k++) { + for (l = 1; l <= kmax; l++) { + for (m = 1; m <= kmax; m++) { + sqk = (k*unitk[0] * k*unitk[0]) + (l*unitk[1] * l*unitk[1]) + + (m*unitk[2] * m*unitk[2]); + if (sqk <= gsqmx) { + cstr1 = 0.0; + sstr1 = 0.0; + cstr2 = 0.0; + sstr2 = 0.0; + cstr3 = 0.0; + sstr3 = 0.0; + cstr4 = 0.0; + sstr4 = 0.0; + for (i = 0; i < nlocal; i++) { + clpm = cs[l][1][i]*cs[m][2][i] - sn[l][1][i]*sn[m][2][i]; + slpm = sn[l][1][i]*cs[m][2][i] + cs[l][1][i]*sn[m][2][i]; + cstr1 += q[i]*(cs[k][0][i]*clpm - sn[k][0][i]*slpm); + sstr1 += q[i]*(sn[k][0][i]*clpm + cs[k][0][i]*slpm); + + clpm = cs[l][1][i]*cs[m][2][i] + sn[l][1][i]*sn[m][2][i]; + slpm = -sn[l][1][i]*cs[m][2][i] + cs[l][1][i]*sn[m][2][i]; + cstr2 += q[i]*(cs[k][0][i]*clpm - sn[k][0][i]*slpm); + sstr2 += q[i]*(sn[k][0][i]*clpm + cs[k][0][i]*slpm); + + clpm = cs[l][1][i]*cs[m][2][i] + sn[l][1][i]*sn[m][2][i]; + slpm = sn[l][1][i]*cs[m][2][i] - cs[l][1][i]*sn[m][2][i]; + cstr3 += q[i]*(cs[k][0][i]*clpm - sn[k][0][i]*slpm); + sstr3 += q[i]*(sn[k][0][i]*clpm + cs[k][0][i]*slpm); + + clpm = cs[l][1][i]*cs[m][2][i] - sn[l][1][i]*sn[m][2][i]; + slpm = -sn[l][1][i]*cs[m][2][i] - cs[l][1][i]*sn[m][2][i]; + cstr4 += q[i]*(cs[k][0][i]*clpm - sn[k][0][i]*slpm); + sstr4 += q[i]*(sn[k][0][i]*clpm + cs[k][0][i]*slpm); + } + sfacrl[n] = cstr1; + sfacim[n++] = sstr1; + sfacrl[n] = cstr2; + sfacim[n++] = sstr2; + sfacrl[n] = cstr3; + sfacim[n++] = sstr3; + sfacrl[n] = cstr4; + sfacim[n++] = sstr4; + } + } + } + } +} + +/* ---------------------------------------------------------------------- + pre-compute coefficients for each Ewald K-vector +------------------------------------------------------------------------- */ + +void Ewald::coeffs() +{ + int k,l,m; + double sqk,vterm; + + double unitkx = unitk[0]; + double unitky = unitk[1]; + double unitkz = unitk[2]; + double g_ewald_sq_inv = 1.0 / (g_ewald*g_ewald); + double preu = 4.0*PI/volume; + + kcount = 0; + + // (k,0,0), (0,l,0), (0,0,m) + + for (m = 1; m <= kmax; m++) { + sqk = (m*unitkx) * (m*unitkx); + if (sqk <= gsqmx) { + kxvecs[kcount] = m; + kyvecs[kcount] = 0; + kzvecs[kcount] = 0; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 2.0*unitkx*m*ug[kcount]; + eg[kcount][1] = 0.0; + eg[kcount][2] = 0.0; + vterm = -2.0*(1.0/sqk + 0.25*g_ewald_sq_inv); + vg[kcount][0] = 1.0 + vterm*(unitkx*m)*(unitkx*m); + vg[kcount][1] = 1.0; + vg[kcount][2] = 1.0; + vg[kcount][3] = 0.0; + vg[kcount][4] = 0.0; + vg[kcount][5] = 0.0; + kcount++; + } + sqk = (m*unitky) * (m*unitky); + if (sqk <= gsqmx) { + kxvecs[kcount] = 0; + kyvecs[kcount] = m; + kzvecs[kcount] = 0; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 0.0; + eg[kcount][1] = 2.0*unitky*m*ug[kcount]; + eg[kcount][2] = 0.0; + vterm = -2.0*(1.0/sqk + 0.25*g_ewald_sq_inv); + vg[kcount][0] = 1.0; + vg[kcount][1] = 1.0 + vterm*(unitky*m)*(unitky*m); + vg[kcount][2] = 1.0; + vg[kcount][3] = 0.0; + vg[kcount][4] = 0.0; + vg[kcount][5] = 0.0; + kcount++; + } + sqk = (m*unitkz) * (m*unitkz); + if (sqk <= gsqmx) { + kxvecs[kcount] = 0; + kyvecs[kcount] = 0; + kzvecs[kcount] = m; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 0.0; + eg[kcount][1] = 0.0; + eg[kcount][2] = 2.0*unitkz*m*ug[kcount]; + vterm = -2.0*(1.0/sqk + 0.25*g_ewald_sq_inv); + vg[kcount][0] = 1.0; + vg[kcount][1] = 1.0; + vg[kcount][2] = 1.0 + vterm*(unitkz*m)*(unitkz*m); + vg[kcount][3] = 0.0; + vg[kcount][4] = 0.0; + vg[kcount][5] = 0.0; + kcount++; + } + } + + // 1 = (k,l,0), 2 = (k,-l,0) + + for (k = 1; k <= kmax; k++) { + for (l = 1; l <= kmax; l++) { + sqk = (unitkx*k) * (unitkx*k) + (unitky*l) * (unitky*l); + if (sqk <= gsqmx) { + kxvecs[kcount] = k; + kyvecs[kcount] = l; + kzvecs[kcount] = 0; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 2.0*unitkx*k*ug[kcount]; + eg[kcount][1] = 2.0*unitky*l*ug[kcount]; + eg[kcount][2] = 0.0; + vterm = -2.0*(1.0/sqk + 0.25*g_ewald_sq_inv); + vg[kcount][0] = 1.0 + vterm*(unitkx*k)*(unitkx*k); + vg[kcount][1] = 1.0 + vterm*(unitky*l)*(unitky*l); + vg[kcount][2] = 1.0; + vg[kcount][3] = vterm*unitkx*k*unitky*l; + vg[kcount][4] = 0.0; + vg[kcount][5] = 0.0; + kcount++; + + kxvecs[kcount] = k; + kyvecs[kcount] = -l; + kzvecs[kcount] = 0; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 2.0*unitkx*k*ug[kcount]; + eg[kcount][1] = -2.0*unitky*l*ug[kcount]; + eg[kcount][2] = 0.0; + vg[kcount][0] = 1.0 + vterm*(unitkx*k)*(unitkx*k); + vg[kcount][1] = 1.0 + vterm*(unitky*l)*(unitky*l); + vg[kcount][2] = 1.0; + vg[kcount][3] = -vterm*unitkx*k*unitky*l; + vg[kcount][4] = 0.0; + vg[kcount][5] = 0.0; + kcount++;; + } + } + } + + // 1 = (0,l,m), 2 = (0,l,-m) + + for (l = 1; l <= kmax; l++) { + for (m = 1; m <= kmax; m++) { + sqk = (unitky*l) * (unitky*l) + (unitkz*m) * (unitkz*m); + if (sqk <= gsqmx) { + kxvecs[kcount] = 0; + kyvecs[kcount] = l; + kzvecs[kcount] = m; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 0.0; + eg[kcount][1] = 2.0*unitky*l*ug[kcount]; + eg[kcount][2] = 2.0*unitkz*m*ug[kcount]; + vterm = -2.0*(1.0/sqk + 0.25*g_ewald_sq_inv); + vg[kcount][0] = 1.0; + vg[kcount][1] = 1.0 + vterm*(unitky*l)*(unitky*l); + vg[kcount][2] = 1.0 + vterm*(unitkz*m)*(unitkz*m); + vg[kcount][3] = 0.0; + vg[kcount][4] = 0.0; + vg[kcount][5] = vterm*unitky*l*unitkz*m; + kcount++; + + kxvecs[kcount] = 0; + kyvecs[kcount] = l; + kzvecs[kcount] = -m; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 0.0; + eg[kcount][1] = 2.0*unitky*l*ug[kcount]; + eg[kcount][2] = -2.0*unitkz*m*ug[kcount]; + vg[kcount][0] = 1.0; + vg[kcount][1] = 1.0 + vterm*(unitky*l)*(unitky*l); + vg[kcount][2] = 1.0 + vterm*(unitkz*m)*(unitkz*m); + vg[kcount][3] = 0.0; + vg[kcount][4] = 0.0; + vg[kcount][5] = -vterm*unitky*l*unitkz*m; + kcount++; + } + } + } + + // 1 = (k,0,m), 2 = (k,0,-m) + + for (k = 1; k <= kmax; k++) { + for (m = 1; m <= kmax; m++) { + sqk = (unitkx*k) * (unitkx*k) + (unitkz*m) * (unitkz*m); + if (sqk <= gsqmx) { + kxvecs[kcount] = k; + kyvecs[kcount] = 0; + kzvecs[kcount] = m; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 2.0*unitkx*k*ug[kcount]; + eg[kcount][1] = 0.0; + eg[kcount][2] = 2.0*unitkz*m*ug[kcount]; + vterm = -2.0*(1.0/sqk + 0.25*g_ewald_sq_inv); + vg[kcount][0] = 1.0 + vterm*(unitkx*k)*(unitkx*k); + vg[kcount][1] = 1.0; + vg[kcount][2] = 1.0 + vterm*(unitkz*m)*(unitkz*m); + vg[kcount][3] = 0.0; + vg[kcount][4] = vterm*unitkx*k*unitkz*m; + vg[kcount][5] = 0.0; + kcount++; + + kxvecs[kcount] = k; + kyvecs[kcount] = 0; + kzvecs[kcount] = -m; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 2.0*unitkx*k*ug[kcount]; + eg[kcount][1] = 0.0; + eg[kcount][2] = -2.0*unitkz*m*ug[kcount]; + vg[kcount][0] = 1.0 + vterm*(unitkx*k)*(unitkx*k); + vg[kcount][1] = 1.0; + vg[kcount][2] = 1.0 + vterm*(unitkz*m)*(unitkz*m); + vg[kcount][3] = 0.0; + vg[kcount][4] = -vterm*unitkx*k*unitkz*m; + vg[kcount][5] = 0.0; + kcount++; + } + } + } + + // 1 = (k,l,m), 2 = (k,-l,m), 3 = (k,l,-m), 4 = (k,-l,-m) + + for (k = 1; k <= kmax; k++) { + for (l = 1; l <= kmax; l++) { + for (m = 1; m <= kmax; m++) { + sqk = (unitkx*k) * (unitkx*k) + (unitky*l) * (unitky*l) + + (unitkz*m) * (unitkz*m); + if (sqk <= gsqmx) { + kxvecs[kcount] = k; + kyvecs[kcount] = l; + kzvecs[kcount] = m; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 2.0*unitkx*k*ug[kcount]; + eg[kcount][1] = 2.0*unitky*l*ug[kcount]; + eg[kcount][2] = 2.0*unitkz*m*ug[kcount]; + vterm = -2.0*(1.0/sqk + 0.25*g_ewald_sq_inv); + vg[kcount][0] = 1.0 + vterm*(unitkx*k)*(unitkx*k); + vg[kcount][1] = 1.0 + vterm*(unitky*l)*(unitky*l); + vg[kcount][2] = 1.0 + vterm*(unitkz*m)*(unitkz*m); + vg[kcount][3] = vterm*unitkx*k*unitky*l; + vg[kcount][4] = vterm*unitkx*k*unitkz*m; + vg[kcount][5] = vterm*unitky*l*unitkz*m; + kcount++; + + kxvecs[kcount] = k; + kyvecs[kcount] = -l; + kzvecs[kcount] = m; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 2.0*unitkx*k*ug[kcount]; + eg[kcount][1] = -2.0*unitky*l*ug[kcount]; + eg[kcount][2] = 2.0*unitkz*m*ug[kcount]; + vg[kcount][0] = 1.0 + vterm*(unitkx*k)*(unitkx*k); + vg[kcount][1] = 1.0 + vterm*(unitky*l)*(unitky*l); + vg[kcount][2] = 1.0 + vterm*(unitkz*m)*(unitkz*m); + vg[kcount][3] = -vterm*unitkx*k*unitky*l; + vg[kcount][4] = vterm*unitkx*k*unitkz*m; + vg[kcount][5] = -vterm*unitky*l*unitkz*m; + kcount++; + + kxvecs[kcount] = k; + kyvecs[kcount] = l; + kzvecs[kcount] = -m; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 2.0*unitkx*k*ug[kcount]; + eg[kcount][1] = 2.0*unitky*l*ug[kcount]; + eg[kcount][2] = -2.0*unitkz*m*ug[kcount]; + vg[kcount][0] = 1.0 + vterm*(unitkx*k)*(unitkx*k); + vg[kcount][1] = 1.0 + vterm*(unitky*l)*(unitky*l); + vg[kcount][2] = 1.0 + vterm*(unitkz*m)*(unitkz*m); + vg[kcount][3] = vterm*unitkx*k*unitky*l; + vg[kcount][4] = -vterm*unitkx*k*unitkz*m; + vg[kcount][5] = -vterm*unitky*l*unitkz*m; + kcount++; + + kxvecs[kcount] = k; + kyvecs[kcount] = -l; + kzvecs[kcount] = -m; + ug[kcount] = preu*exp(-0.25*sqk*g_ewald_sq_inv)/sqk; + eg[kcount][0] = 2.0*unitkx*k*ug[kcount]; + eg[kcount][1] = -2.0*unitky*l*ug[kcount]; + eg[kcount][2] = -2.0*unitkz*m*ug[kcount]; + vg[kcount][0] = 1.0 + vterm*(unitkx*k)*(unitkx*k); + vg[kcount][1] = 1.0 + vterm*(unitky*l)*(unitky*l); + vg[kcount][2] = 1.0 + vterm*(unitkz*m)*(unitkz*m); + vg[kcount][3] = -vterm*unitkx*k*unitky*l; + vg[kcount][4] = -vterm*unitkx*k*unitkz*m; + vg[kcount][5] = vterm*unitky*l*unitkz*m; + kcount++;; + } + } + } + } +} + +/* ---------------------------------------------------------------------- + allocate memory that depends on # of K-vectors +------------------------------------------------------------------------- */ + +void Ewald::allocate() +{ + kxvecs = new int[kmax3d]; + kyvecs = new int[kmax3d]; + kzvecs = new int[kmax3d]; + + ug = new double[kmax3d]; + eg = memory->create_2d_double_array(kmax3d,3,"ewald:eg"); + vg = memory->create_2d_double_array(kmax3d,6,"ewald:vg"); + + sfacrl = new double[kmax3d]; + sfacim = new double[kmax3d]; + sfacrl_all = new double[kmax3d]; + sfacim_all = new double[kmax3d]; +} + +/* ---------------------------------------------------------------------- + deallocate memory that depends on # of K-vectors +------------------------------------------------------------------------- */ + +void Ewald::deallocate() +{ + delete [] kxvecs; + delete [] kyvecs; + delete [] kzvecs; + + delete [] ug; + memory->destroy_2d_double_array(eg); + memory->destroy_2d_double_array(vg); + + delete [] sfacrl; + delete [] sfacim; + delete [] sfacrl_all; + delete [] sfacim_all; +} + +/* ---------------------------------------------------------------------- + Slab-geometry correction term to dampen inter-slab interactions between + periodically repeating slabs. Yields good approximation to 2-D Ewald if + adequate empty space is left between repeating slabs (J. Chem. Phys. + 111, 3155). Slabs defined here to be parallel to the xy plane. +------------------------------------------------------------------------- */ + +void Ewald::slabcorr(int eflag) +{ + // compute local contribution to global dipole moment + + double *q = atom->q; + double **x = atom->x; + int nlocal = atom->nlocal; + + double dipole = 0.0; + for (int i = 0; i < nlocal; i++) dipole += q[i]*x[i][2]; + + // sum local contributions to get global dipole moment + + double dipole_all; + MPI_Allreduce(&dipole,&dipole_all,1,MPI_DOUBLE,MPI_SUM,world); + + // compute corrections + + double e_slabcorr = 2.0*PI*dipole_all*dipole_all/volume; + + if (eflag) energy += qqrd2e*e_slabcorr; + + // add on force corrections + + double ffact = -4.0*PI*dipole_all/volume; + double **f = atom->f; + + for (int i = 0; i < nlocal; i++) f[i][2] += qqrd2e*q[i]*ffact; +} + +/* ---------------------------------------------------------------------- + memory usage of local arrays +------------------------------------------------------------------------- */ + +int Ewald::memory_usage() +{ + int bytes = 3 * kmax3d * sizeof(int); + bytes += (1 + 3 + 6) * kmax3d * sizeof(double); + bytes += 4 * kmax3d * sizeof(double); + bytes += nmax*3 * sizeof(double); + bytes += 2 * (2*kmax+1)*3*nmax * sizeof(double); + return bytes; +} diff --git a/src/ewald.h b/src/ewald.h new file mode 100644 index 0000000000000000000000000000000000000000..cbd28c5693d0ff847d19ab91a427acb576dc020d --- /dev/null +++ b/src/ewald.h @@ -0,0 +1,52 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef EWALD_H +#define EWALD_H + +#include "kspace.h" + +class Ewald : public KSpace { + public: + Ewald(int, char **); + ~Ewald(); + void init(); + void setup(); + void compute(int, int); + int memory_usage(); + + private: + double PI; + double precision; + int kcount,kmax,kmax3d,kmax_created; + double qqrd2e; + double gsqmx,qsum,qsqsum,volume; + int nmax; + + double unitk[3]; + int *kxvecs,*kyvecs,*kzvecs; + double *ug; + double **eg,**vg; + double **ek; + double *sfacrl,*sfacim,*sfacrl_all,*sfacim_all; + double ***cs,***sn; + + void eik_dot_r(); + void coeffs(); + void allocate(); + void deallocate(); + void slabcorr(int); +}; + +#endif + diff --git a/src/fft3d.cpp b/src/fft3d.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3af1923d1524a21e43b8fe2bfb7dfbd71dd8c5f9 --- /dev/null +++ b/src/fft3d.cpp @@ -0,0 +1,999 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Jim Shepherd (GA Tech) added SGI SCSL support +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "stdio.h" +#include "stdlib.h" +#include "math.h" +#include "fft3d.h" +#include "remap.h" + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +/* ---------------------------------------------------------------------- + Data layout for 3d FFTs: + + data set of Nfast x Nmid x Nslow elements is owned by P procs + on input, each proc owns a subsection of the elements + on output, each proc will own a (possibly different) subsection + my subsection must not overlap with any other proc's subsection, + i.e. the union of all proc's input (or output) subsections must + exactly tile the global Nfast x Nmid x Nslow data set + when called from C, all subsection indices are + C-style from 0 to N-1 where N = Nfast or Nmid or Nslow + when called from F77, all subsection indices are + F77-style from 1 to N where N = Nfast or Nmid or Nslow + a proc can own 0 elements on input or output + by specifying hi index < lo index + on both input and output, data is stored contiguously on a processor + with a fast-varying, mid-varying, and slow-varying index +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Perform 3d FFT + + Arguments: + in starting address of input data on this proc + out starting address of where output data for this proc + will be placed (can be same as in) + flag 1 for forward FFT, -1 for inverse FFT + plan plan returned by previous call to fft_3d_create_plan +------------------------------------------------------------------------- */ + +void fft_3d(FFT_DATA *in, FFT_DATA *out, int flag, struct fft_plan_3d *plan) +{ + int i,total,length,offset,num; + double norm; + FFT_DATA *data,*copy; + + // system specific constants + +#ifdef FFT_SCSL + int isys = 0; + FFT_PREC scalef = 1.0; +#endif +#ifdef FFT_DEC + char c = 'C'; + char f = 'F'; + char b = 'B'; + int one = 1; +#endif +#ifdef FFT_T3E + int isys = 0; + double scalef = 1.0; +#endif + + // pre-remap to prepare for 1st FFTs if needed + // copy = loc for remap result + + if (plan->pre_plan) { + if (plan->pre_target == 0) copy = out; + else copy = plan->copy; + remap_3d((double *) in, (double *) copy, (double *) plan->scratch, + plan->pre_plan); + data = copy; + } + else + data = in; + + // 1d FFTs along fast axis + + total = plan->total1; + length = plan->length1; + +#ifdef FFT_SGI + for (offset = 0; offset < total; offset += length) + FFT_1D(flag,length,&data[offset],1,plan->coeff1); +#endif +#ifdef FFT_SCSL + for (offset = 0; offset < total; offset += length) + FFT_1D(flag,length,scalef,&data[offset],&data[offset],plan->coeff1, + plan->work1,&isys); +#endif +#ifdef FFT_INTEL + for (offset = 0; offset < total; offset += length) + FFT_1D(&data[offset],&length,&flag,plan->coeff1); +#endif +#ifdef FFT_DEC + if (flag == -1) + for (offset = 0; offset < total; offset += length) + FFT_1D(&c,&c,&f,&data[offset],&data[offset],&length,&one); + else + for (offset = 0; offset < total; offset += length) + FFT_1D(&c,&c,&b,&data[offset],&data[offset],&length,&one); +#endif +#ifdef FFT_T3E + for (offset = 0; offset < total; offset += length) + FFT_1D(&flag,&length,&scalef,&data[offset],&data[offset],plan->coeff1, + plan->work1,&isys); +#endif +#ifdef FFT_FFTW + if (flag == -1) + fftw(plan->plan_fast_forward,total/length,data,1,length,NULL,0,0); + else + fftw(plan->plan_fast_backward,total/length,data,1,length,NULL,0,0); +#endif + + // 1st mid-remap to prepare for 2nd FFTs + // copy = loc for remap result + + if (plan->mid1_target == 0) copy = out; + else copy = plan->copy; + remap_3d((double *) data, (double *) copy, (double *) plan->scratch, + plan->mid1_plan); + data = copy; + + // 1d FFTs along mid axis + + total = plan->total2; + length = plan->length2; + +#ifdef FFT_SGI + for (offset = 0; offset < total; offset += length) + FFT_1D(flag,length,&data[offset],1,plan->coeff2); +#endif +#ifdef FFT_SCSL + for (offset = 0; offset < total; offset += length) + FFT_1D(flag,length,scalef,&data[offset],&data[offset],plan->coeff2, + plan->work2,&isys); +#endif +#ifdef FFT_INTEL + for (offset = 0; offset < total; offset += length) + FFT_1D(&data[offset],&length,&flag,plan->coeff2); +#endif +#ifdef FFT_DEC + if (flag == -1) + for (offset = 0; offset < total; offset += length) + FFT_1D(&c,&c,&f,&data[offset],&data[offset],&length,&one); + else + for (offset = 0; offset < total; offset += length) + FFT_1D(&c,&c,&b,&data[offset],&data[offset],&length,&one); +#endif +#ifdef FFT_T3E + for (offset = 0; offset < total; offset += length) + FFT_1D(&flag,&length,&scalef,&data[offset],&data[offset],plan->coeff2, + plan->work2,&isys); +#endif +#ifdef FFT_FFTW + if (flag == -1) + fftw(plan->plan_mid_forward,total/length,data,1,length,NULL,0,0); + else + fftw(plan->plan_mid_backward,total/length,data,1,length,NULL,0,0); +#endif + + // 2nd mid-remap to prepare for 3rd FFTs + // copy = loc for remap result + + if (plan->mid2_target == 0) copy = out; + else copy = plan->copy; + remap_3d((double *) data, (double *) copy, (double *) plan->scratch, + plan->mid2_plan); + data = copy; + + // 1d FFTs along slow axis + + total = plan->total3; + length = plan->length3; + +#ifdef FFT_SGI + for (offset = 0; offset < total; offset += length) + FFT_1D(flag,length,&data[offset],1,plan->coeff3); +#endif +#ifdef FFT_SCSL + for (offset = 0; offset < total; offset += length) + FFT_1D(flag,length,scalef,&data[offset],&data[offset],plan->coeff3, + plan->work3,&isys); +#endif +#ifdef FFT_INTEL + for (offset = 0; offset < total; offset += length) + FFT_1D(&data[offset],&length,&flag,plan->coeff3); +#endif +#ifdef FFT_DEC + if (flag == -1) + for (offset = 0; offset < total; offset += length) + FFT_1D(&c,&c,&f,&data[offset],&data[offset],&length,&one); + else + for (offset = 0; offset < total; offset += length) + FFT_1D(&c,&c,&b,&data[offset],&data[offset],&length,&one); +#endif +#ifdef FFT_T3E + for (offset = 0; offset < total; offset += length) + FFT_1D(&flag,&length,&scalef,&data[offset],&data[offset],plan->coeff3, + plan->work3,&isys); +#endif +#ifdef FFT_FFTW + if (flag == -1) + fftw(plan->plan_slow_forward,total/length,data,1,length,NULL,0,0); + else + fftw(plan->plan_slow_backward,total/length,data,1,length,NULL,0,0); +#endif + + // post-remap to put data in output format if needed + // destination is always out + + if (plan->post_plan) + remap_3d((double *) data, (double *) out, (double *) plan->scratch, + plan->post_plan); + + // scaling if required + +#ifndef FFT_T3E + if (flag == 1 && plan->scaled) { + norm = plan->norm; + num = plan->normnum; + for (i = 0; i < num; i++) { + out[i].re *= norm; + out[i].im *= norm; + } + } +#endif + +#ifdef FFT_T3E + if (flag == 1 && plan->scaled) { + norm = plan->norm; + num = plan->normnum; + for (i = 0; i < num; i++) out[i] *= (norm,norm); + } +#endif +} + +/* ---------------------------------------------------------------------- + Create plan for performing a 3d FFT + + Arguments: + comm MPI communicator for the P procs which own the data + nfast,nmid,nslow size of global 3d matrix + in_ilo,in_ihi input bounds of data I own in fast index + in_jlo,in_jhi input bounds of data I own in mid index + in_klo,in_khi input bounds of data I own in slow index + out_ilo,out_ihi output bounds of data I own in fast index + out_jlo,out_jhi output bounds of data I own in mid index + out_klo,out_khi output bounds of data I own in slow index + scaled 0 = no scaling of result, 1 = scaling + permute permutation in storage order of indices on output + 0 = no permutation + 1 = permute once = mid->fast, slow->mid, fast->slow + 2 = permute twice = slow->fast, fast->mid, mid->slow + nbuf returns size of internal storage buffers used by FFT +------------------------------------------------------------------------- */ + +struct fft_plan_3d *fft_3d_create_plan( + MPI_Comm comm, int nfast, int nmid, int nslow, + int in_ilo, int in_ihi, int in_jlo, int in_jhi, + int in_klo, int in_khi, + int out_ilo, int out_ihi, int out_jlo, int out_jhi, + int out_klo, int out_khi, + int scaled, int permute, int *nbuf) +{ + struct fft_plan_3d *plan; + int me,nprocs; + int i,num,flag,remapflag,fftflag; + int first_ilo,first_ihi,first_jlo,first_jhi,first_klo,first_khi; + int second_ilo,second_ihi,second_jlo,second_jhi,second_klo,second_khi; + int third_ilo,third_ihi,third_jlo,third_jhi,third_klo,third_khi; + int out_size,first_size,second_size,third_size,copy_size,scratch_size; + int np1,np2,ip1,ip2; + int list[50]; + + // system specific variables + +#ifdef FFT_SCSL + FFT_DATA dummy_d[5]; + FFT_PREC dummy_p[5]; + int isign,isys; + FFT_PREC scalef; +#endif +#ifdef FFT_INTEL + FFT_DATA dummy; +#endif +#ifdef FFT_T3E + FFT_DATA dummy[5]; + int isign,isys; + double scalef; +#endif + + // query MPI info + + MPI_Comm_rank(comm,&me); + MPI_Comm_size(comm,&nprocs); + +#ifdef FFT_NONE + if (me == 0) { + printf("ERROR: Cannot use FFTs with FFT_NONE set\n"); + return NULL; + } +#endif + + // compute division of procs in 2 dimensions not on-processor + + bifactor(nprocs,&np1,&np2); + ip1 = me % np1; + ip2 = me/np1; + + // allocate memory for plan data struct + + plan = (struct fft_plan_3d *) malloc(sizeof(struct fft_plan_3d)); + if (plan == NULL) return NULL; + + // remap from initial distribution to layout needed for 1st set of 1d FFTs + // not needed if all procs own entire fast axis initially + // first indices = distribution after 1st set of FFTs + + if (in_ilo == 0 && in_ihi == nfast-1) + flag = 0; + else + flag = 1; + + MPI_Allreduce(&flag,&remapflag,1,MPI_INT,MPI_MAX,comm); + + if (remapflag == 0) { + first_ilo = in_ilo; + first_ihi = in_ihi; + first_jlo = in_jlo; + first_jhi = in_jhi; + first_klo = in_klo; + first_khi = in_khi; + plan->pre_plan = NULL; + } + else { + first_ilo = 0; + first_ihi = nfast - 1; + first_jlo = ip1*nmid/np1; + first_jhi = (ip1+1)*nmid/np1 - 1; + first_klo = ip2*nslow/np2; + first_khi = (ip2+1)*nslow/np2 - 1; + plan->pre_plan = + remap_3d_create_plan(comm,in_ilo,in_ihi,in_jlo,in_jhi,in_klo,in_khi, + first_ilo,first_ihi,first_jlo,first_jhi, + first_klo,first_khi, + FFT_PRECISION,0,0,2); + if (plan->pre_plan == NULL) return NULL; + } + + // 1d FFTs along fast axis + + plan->length1 = nfast; + plan->total1 = nfast * (first_jhi-first_jlo+1) * (first_khi-first_klo+1); + + // remap from 1st to 2nd FFT + // choose which axis is split over np1 vs np2 to minimize communication + // second indices = distribution after 2nd set of FFTs + + second_ilo = ip1*nfast/np1; + second_ihi = (ip1+1)*nfast/np1 - 1; + second_jlo = 0; + second_jhi = nmid - 1; + second_klo = ip2*nslow/np2; + second_khi = (ip2+1)*nslow/np2 - 1; + plan->mid1_plan = + remap_3d_create_plan(comm, + first_ilo,first_ihi,first_jlo,first_jhi, + first_klo,first_khi, + second_ilo,second_ihi,second_jlo,second_jhi, + second_klo,second_khi, + FFT_PRECISION,1,0,2); + if (plan->mid1_plan == NULL) return NULL; + + // 1d FFTs along mid axis + + plan->length2 = nmid; + plan->total2 = (second_ihi-second_ilo+1) * nmid * (second_khi-second_klo+1); + + // remap from 2nd to 3rd FFT + // if final distribution is permute=2 with all procs owning entire slow axis + // then this remapping goes directly to final distribution + // third indices = distribution after 3rd set of FFTs + + if (permute == 2 && out_klo == 0 && out_khi == nslow-1) + flag = 0; + else + flag = 1; + + MPI_Allreduce(&flag,&remapflag,1,MPI_INT,MPI_MAX,comm); + + if (remapflag == 0) { + third_ilo = out_ilo; + third_ihi = out_ihi; + third_jlo = out_jlo; + third_jhi = out_jhi; + third_klo = out_klo; + third_khi = out_khi; + } + else { + third_ilo = ip1*nfast/np1; + third_ihi = (ip1+1)*nfast/np1 - 1; + third_jlo = ip2*nmid/np2; + third_jhi = (ip2+1)*nmid/np2 - 1; + third_klo = 0; + third_khi = nslow - 1; + } + + plan->mid2_plan = + remap_3d_create_plan(comm, + second_jlo,second_jhi,second_klo,second_khi, + second_ilo,second_ihi, + third_jlo,third_jhi,third_klo,third_khi, + third_ilo,third_ihi, + FFT_PRECISION,1,0,2); + if (plan->mid2_plan == NULL) return NULL; + + // 1d FFTs along slow axis + + plan->length3 = nslow; + plan->total3 = (third_ihi-third_ilo+1) * (third_jhi-third_jlo+1) * nslow; + + // remap from 3rd FFT to final distribution + // not needed if permute = 2 and third indices = out indices on all procs + + if (permute == 2 && + out_ilo == third_ilo && out_ihi == third_ihi && + out_jlo == third_jlo && out_jhi == third_jhi && + out_klo == third_klo && out_khi == third_khi) + flag = 0; + else + flag = 1; + + MPI_Allreduce(&flag,&remapflag,1,MPI_INT,MPI_MAX,comm); + + if (remapflag == 0) + plan->post_plan = NULL; + else { + plan->post_plan = + remap_3d_create_plan(comm, + third_klo,third_khi,third_ilo,third_ihi, + third_jlo,third_jhi, + out_klo,out_khi,out_ilo,out_ihi, + out_jlo,out_jhi, + FFT_PRECISION,(permute+1)%3,0,2); + if (plan->post_plan == NULL) return NULL; + } + + // configure plan memory pointers and allocate work space + // out_size = amount of memory given to FFT by user + // first/second/third_size = amount of memory needed after pre,mid1,mid2 remaps + // copy_size = amount needed internally for extra copy of data + // scratch_size = amount needed internally for remap scratch space + // for each remap: + // out space used for result if big enough, else require copy buffer + // accumulate largest required remap scratch space + + out_size = (out_ihi-out_ilo+1) * (out_jhi-out_jlo+1) * (out_khi-out_klo+1); + first_size = (first_ihi-first_ilo+1) * (first_jhi-first_jlo+1) * + (first_khi-first_klo+1); + second_size = (second_ihi-second_ilo+1) * (second_jhi-second_jlo+1) * + (second_khi-second_klo+1); + third_size = (third_ihi-third_ilo+1) * (third_jhi-third_jlo+1) * + (third_khi-third_klo+1); + + copy_size = 0; + scratch_size = 0; + + if (plan->pre_plan) { + if (first_size <= out_size) + plan->pre_target = 0; + else { + plan->pre_target = 1; + copy_size = MAX(copy_size,first_size); + } + scratch_size = MAX(scratch_size,first_size); + } + + if (plan->mid1_plan) { + if (second_size <= out_size) + plan->mid1_target = 0; + else { + plan->mid1_target = 1; + copy_size = MAX(copy_size,second_size); + } + scratch_size = MAX(scratch_size,second_size); + } + + if (plan->mid2_plan) { + if (third_size <= out_size) + plan->mid2_target = 0; + else { + plan->mid2_target = 1; + copy_size = MAX(copy_size,third_size); + } + scratch_size = MAX(scratch_size,third_size); + } + + if (plan->post_plan) + scratch_size = MAX(scratch_size,out_size); + + *nbuf = copy_size + scratch_size; + + if (copy_size) { + plan->copy = (FFT_DATA *) malloc(copy_size*sizeof(FFT_DATA)); + if (plan->copy == NULL) return NULL; + } + else plan->copy = NULL; + + if (scratch_size) { + plan->scratch = (FFT_DATA *) malloc(scratch_size*sizeof(FFT_DATA)); + if (plan->scratch == NULL) return NULL; + } + else plan->scratch = NULL; + + // system specific pre-computation of 1d FFT coeffs + // and scaling normalization + +#ifdef FFT_SGI + + plan->coeff1 = (FFT_DATA *) malloc((nfast+15)*sizeof(FFT_DATA)); + plan->coeff2 = (FFT_DATA *) malloc((nmid+15)*sizeof(FFT_DATA)); + plan->coeff3 = (FFT_DATA *) malloc((nslow+15)*sizeof(FFT_DATA)); + + if (plan->coeff1 == NULL || plan->coeff2 == NULL || + plan->coeff3 == NULL) return NULL; + + FFT_1D_INIT(nfast,plan->coeff1); + FFT_1D_INIT(nmid,plan->coeff2); + FFT_1D_INIT(nslow,plan->coeff3); + + if (scaled == 0) + plan->scaled = 0; + else { + plan->scaled = 1; + plan->norm = 1.0/(nfast*nmid*nslow); + plan->normnum = (out_ihi-out_ilo+1) * (out_jhi-out_jlo+1) * + (out_khi-out_klo+1); + } + +#endif + +#ifdef FFT_SCSL + + plan->coeff1 = (FFT_PREC *) malloc((2*nfast+30)*sizeof(FFT_PREC)); + plan->coeff2 = (FFT_PREC *) malloc((2*nmid+30)*sizeof(FFT_PREC)); + plan->coeff3 = (FFT_PREC *) malloc((2*nslow+30)*sizeof(FFT_PREC)); + + if (plan->coeff1 == NULL || plan->coeff2 == NULL || + plan->coeff3 == NULL) return NULL; + + plan->work1 = (FFT_PREC *) malloc((2*nfast)*sizeof(FFT_PREC)); + plan->work2 = (FFT_PREC *) malloc((2*nmid)*sizeof(FFT_PREC)); + plan->work3 = (FFT_PREC *) malloc((2*nslow)*sizeof(FFT_PREC)); + + if (plan->work1 == NULL || plan->work2 == NULL || + plan->work3 == NULL) return NULL; + + isign = 0; + scalef = 1.0; + isys = 0; + + FFT_1D_INIT(isign,nfast,scalef,dummy_d,dummy_d,plan->coeff1,dummy_p,&isys); + FFT_1D_INIT(isign,nmid,scalef,dummy_d,dummy_d,plan->coeff2,dummy_p,&isys); + FFT_1D_INIT(isign,nslow,scalef,dummy_d,dummy_d,plan->coeff3,dummy_p,&isys); + + if (scaled == 0) + plan->scaled = 0; + else { + plan->scaled = 1; + plan->norm = 1.0/(nfast*nmid*nslow); + plan->normnum = (out_ihi-out_ilo+1) * (out_jhi-out_jlo+1) * + (out_khi-out_klo+1); + } + +#endif + +#ifdef FFT_INTEL + + flag = 0; + + num = 0; + factor(nfast,&num,list); + for (i = 0; i < num; i++) + if (list[i] != 2 && list[i] != 3 && list[i] != 5) flag = 1; + num = 0; + factor(nmid,&num,list); + for (i = 0; i < num; i++) + if (list[i] != 2 && list[i] != 3 && list[i] != 5) flag = 1; + num = 0; + factor(nslow,&num,list); + for (i = 0; i < num; i++) + if (list[i] != 2 && list[i] != 3 && list[i] != 5) flag = 1; + + MPI_Allreduce(&flag,&fftflag,1,MPI_INT,MPI_MAX,comm); + if (fftflag) { + if (me == 0) printf("ERROR: FFTs are not power of 2,3,5\n"); + return NULL; + } + + plan->coeff1 = (FFT_DATA *) malloc((3*nfast/2+1)*sizeof(FFT_DATA)); + plan->coeff2 = (FFT_DATA *) malloc((3*nmid/2+1)*sizeof(FFT_DATA)); + plan->coeff3 = (FFT_DATA *) malloc((3*nslow/2+1)*sizeof(FFT_DATA)); + + if (plan->coeff1 == NULL || plan->coeff2 == NULL || + plan->coeff3 == NULL) return NULL; + + flag = 0; + FFT_1D_INIT(&dummy,&nfast,&flag,plan->coeff1); + FFT_1D_INIT(&dummy,&nmid,&flag,plan->coeff2); + FFT_1D_INIT(&dummy,&nslow,&flag,plan->coeff3); + + if (scaled == 0) { + plan->scaled = 1; + plan->norm = nfast*nmid*nslow; + plan->normnum = (out_ihi-out_ilo+1) * (out_jhi-out_jlo+1) * + (out_khi-out_klo+1); + } + else + plan->scaled = 0; + +#endif + +#ifdef FFT_DEC + + if (scaled == 0) { + plan->scaled = 1; + plan->norm = nfast*nmid*nslow; + plan->normnum = (out_ihi-out_ilo+1) * (out_jhi-out_jlo+1) * + (out_khi-out_klo+1); + } + else + plan->scaled = 0; + +#endif + +#ifdef FFT_T3E + + plan->coeff1 = (double *) malloc((12*nfast)*sizeof(double)); + plan->coeff2 = (double *) malloc((12*nmid)*sizeof(double)); + plan->coeff3 = (double *) malloc((12*nslow)*sizeof(double)); + + if (plan->coeff1 == NULL || plan->coeff2 == NULL || + plan->coeff3 == NULL) return NULL; + + plan->work1 = (double *) malloc((8*nfast)*sizeof(double)); + plan->work2 = (double *) malloc((8*nmid)*sizeof(double)); + plan->work3 = (double *) malloc((8*nslow)*sizeof(double)); + + if (plan->work1 == NULL || plan->work2 == NULL || + plan->work3 == NULL) return NULL; + + isign = 0; + scalef = 1.0; + isys = 0; + + FFT_1D_INIT(&isign,&nfast,&scalef,dummy,dummy,plan->coeff1,dummy,&isys); + FFT_1D_INIT(&isign,&nmid,&scalef,dummy,dummy,plan->coeff2,dummy,&isys); + FFT_1D_INIT(&isign,&nslow,&scalef,dummy,dummy,plan->coeff3,dummy,&isys); + + if (scaled == 0) + plan->scaled = 0; + else { + plan->scaled = 1; + plan->norm = 1.0/(nfast*nmid*nslow); + plan->normnum = (out_ihi-out_ilo+1) * (out_jhi-out_jlo+1) * + (out_khi-out_klo+1); + } + +#endif + +#ifdef FFT_FFTW + + plan->plan_fast_forward = + fftw_create_plan(nfast,FFTW_FORWARD,FFTW_ESTIMATE | FFTW_IN_PLACE); + plan->plan_fast_backward = + fftw_create_plan(nfast,FFTW_BACKWARD,FFTW_ESTIMATE | FFTW_IN_PLACE); + + if (nmid == nfast) { + plan->plan_mid_forward = plan->plan_fast_forward; + plan->plan_mid_backward = plan->plan_fast_backward; + } + else { + plan->plan_mid_forward = + fftw_create_plan(nmid,FFTW_FORWARD,FFTW_ESTIMATE | FFTW_IN_PLACE); + plan->plan_mid_backward = + fftw_create_plan(nmid,FFTW_BACKWARD,FFTW_ESTIMATE | FFTW_IN_PLACE); + } + + if (nslow == nfast) { + plan->plan_slow_forward = plan->plan_fast_forward; + plan->plan_slow_backward = plan->plan_fast_backward; + } + else if (nslow == nmid) { + plan->plan_slow_forward = plan->plan_mid_forward; + plan->plan_slow_backward = plan->plan_mid_backward; + } + else { + plan->plan_slow_forward = + fftw_create_plan(nslow,FFTW_FORWARD,FFTW_ESTIMATE | FFTW_IN_PLACE); + plan->plan_slow_backward = + fftw_create_plan(nslow,FFTW_BACKWARD,FFTW_ESTIMATE | FFTW_IN_PLACE); + } + + if (scaled == 0) + plan->scaled = 0; + else { + plan->scaled = 1; + plan->norm = 1.0/(nfast*nmid*nslow); + plan->normnum = (out_ihi-out_ilo+1) * (out_jhi-out_jlo+1) * + (out_khi-out_klo+1); + } + +#endif + + return plan; +} + +/* ---------------------------------------------------------------------- + Destroy a 3d fft plan +------------------------------------------------------------------------- */ + +void fft_3d_destroy_plan(struct fft_plan_3d *plan) +{ + if (plan->pre_plan) remap_3d_destroy_plan(plan->pre_plan); + if (plan->mid1_plan) remap_3d_destroy_plan(plan->mid1_plan); + if (plan->mid2_plan) remap_3d_destroy_plan(plan->mid2_plan); + if (plan->post_plan) remap_3d_destroy_plan(plan->post_plan); + + if (plan->copy) free(plan->copy); + if (plan->scratch) free(plan->scratch); + +#ifdef FFT_SGI + free(plan->coeff1); + free(plan->coeff2); + free(plan->coeff3); +#endif +#ifdef FFT_SCSL + free(plan->coeff1); + free(plan->coeff2); + free(plan->coeff3); + free(plan->work1); + free(plan->work2); + free(plan->work3); +#endif +#ifdef FFT_INTEL + free(plan->coeff1); + free(plan->coeff2); + free(plan->coeff3); +#endif +#ifdef FFT_T3E + free(plan->coeff1); + free(plan->coeff2); + free(plan->coeff3); + free(plan->work1); + free(plan->work2); + free(plan->work3); +#endif +#ifdef FFT_FFTW + if (plan->plan_slow_forward != plan->plan_mid_forward && + plan->plan_slow_forward != plan->plan_fast_forward) { + fftw_destroy_plan(plan->plan_slow_forward); + fftw_destroy_plan(plan->plan_slow_backward); + } + if (plan->plan_mid_forward != plan->plan_fast_forward) { + fftw_destroy_plan(plan->plan_mid_forward); + fftw_destroy_plan(plan->plan_mid_backward); + } + fftw_destroy_plan(plan->plan_fast_forward); + fftw_destroy_plan(plan->plan_fast_backward); +#endif + + free(plan); +} + +/* ---------------------------------------------------------------------- + recursively divide n into small factors, return them in list +------------------------------------------------------------------------- */ + +void factor(int n, int *num, int *list) +{ + if (n == 1) { + return; + } + else if (n % 2 == 0) { + *list = 2; + (*num)++; + factor(n/2,num,list+1); + } + else if (n % 3 == 0) { + *list = 3; + (*num)++; + factor(n/3,num,list+1); + } + else if (n % 5 == 0) { + *list = 5; + (*num)++; + factor(n/5,num,list+1); + } + else if (n % 7 == 0) { + *list = 7; + (*num)++; + factor(n/7,num,list+1); + } + else if (n % 11 == 0) { + *list = 11; + (*num)++; + factor(n/11,num,list+1); + } + else if (n % 13 == 0) { + *list = 13; + (*num)++; + factor(n/13,num,list+1); + } + else { + *list = n; + (*num)++; + return; + } +} + +/* ---------------------------------------------------------------------- + divide n into 2 factors of as equal size as possible +------------------------------------------------------------------------- */ + +void bifactor(int n, int *factor1, int *factor2) +{ + int n1,n2,facmax; + + facmax = static_cast<int> (sqrt((double) n)); + + for (n1 = facmax; n1 > 0; n1--) { + n2 = n/n1; + if (n1*n2 == n) { + *factor1 = n1; + *factor2 = n2; + return; + } + } +} + +/* ---------------------------------------------------------------------- + perform just the 1d FFTs needed by a 3d FFT, no data movement + used for timing purposes + + Arguments: + in starting address of input data on this proc, all set to 0.0 + nsize size of in + flag 1 for forward FFT, -1 for inverse FFT + plan plan returned by previous call to fft_3d_create_plan +------------------------------------------------------------------------- */ + +void fft_1d_only(FFT_DATA *data, int nsize, int flag, struct fft_plan_3d *plan) +{ + int i,total,length,offset,num; + double norm; + + // system specific constants + +#ifdef FFT_SCSL + int isys = 0; + FFT_PREC scalef = 1.0; +#endif +#ifdef FFT_DEC + char c = 'C'; + char f = 'F'; + char b = 'B'; + int one = 1; +#endif +#ifdef FFT_T3E + int isys = 0; + double scalef = 1.0; +#endif + + // total = size of data needed in each dim + // length = length of 1d FFT in each dim + // total/length = # of 1d FFTs in each dim + // if total > nsize, limit # of 1d FFTs to available size of data + + int total1 = plan->total1; + int length1 = plan->length1; + int total2 = plan->total2; + int length2 = plan->length2; + int total3 = plan->total3; + int length3 = plan->length3; + + if (total1 > nsize) total1 = (nsize/length1) * length1; + if (total2 > nsize) total2 = (nsize/length2) * length2; + if (total3 > nsize) total3 = (nsize/length3) * length3; + + // perform 1d FFTs in each of 3 dimensions + // data is just an array of 0.0 + +#ifdef FFT_SGI + for (offset = 0; offset < total1; offset += length1) + FFT_1D(flag,length1,&data[offset],1,plan->coeff1); + for (offset = 0; offset < total2; offset += length2) + FFT_1D(flag,length2,&data[offset],1,plan->coeff2); + for (offset = 0; offset < total3; offset += length3) + FFT_1D(flag,length3,&data[offset],1,plan->coeff3); +#endif +#ifdef FFT_SCSL + for (offset = 0; offset < total1; offset += length1) + FFT_1D(flag,length1,scalef,&data[offset],&data[offset],plan->coeff1, + plan->work1,&isys); + for (offset = 0; offset < total2; offset += length2) + FFT_1D(flag,length2,scalef,&data[offset],&data[offset],plan->coeff2, + plan->work2,&isys); + for (offset = 0; offset < total3; offset += length3) + FFT_1D(flag,length3,scalef,&data[offset],&data[offset],plan->coeff3, + plan->work3,&isys); +#endif +#ifdef FFT_INTEL + for (offset = 0; offset < total1; offset += length1) + FFT_1D(&data[offset],&length1,&flag,plan->coeff1); + for (offset = 0; offset < total2; offset += length2) + FFT_1D(&data[offset],&length2,&flag,plan->coeff2); + for (offset = 0; offset < total3; offset += length3) + FFT_1D(&data[offset],&length3,&flag,plan->coeff3); +#endif +#ifdef FFT_DEC + if (flag == -1) { + for (offset = 0; offset < total1; offset += length1) + FFT_1D(&c,&c,&f,&data[offset],&data[offset],&length1,&one); + for (offset = 0; offset < total2; offset += length2) + FFT_1D(&c,&c,&f,&data[offset],&data[offset],&length2,&one); + for (offset = 0; offset < total3; offset += length3) + FFT_1D(&c,&c,&f,&data[offset],&data[offset],&length3,&one); + } else { + for (offset = 0; offset < total1; offset += length1) + FFT_1D(&c,&c,&b,&data[offset],&data[offset],&length1,&one); + for (offset = 0; offset < total2; offset += length2) + FFT_1D(&c,&c,&b,&data[offset],&data[offset],&length2,&one); + for (offset = 0; offset < total3; offset += length3) + FFT_1D(&c,&c,&b,&data[offset],&data[offset],&length3,&one); + } +#endif +#ifdef FFT_T3E + for (offset = 0; offset < total1; offset += length1) + FFT_1D(&flag,&length1,&scalef,&data[offset],&data[offset],plan->coeff1, + plan->work1,&isys); + for (offset = 0; offset < total2; offset += length2) + FFT_1D(&flag,&length2,&scalef,&data[offset],&data[offset],plan->coeff2, + plan->work2,&isys); + for (offset = 0; offset < total3; offset += length3) + FFT_1D(&flag,&length3,&scalef,&data[offset],&data[offset],plan->coeff3, + plan->work3,&isys); +#endif +#ifdef FFT_FFTW + if (flag == -1) { + fftw(plan->plan_fast_forward,total1/length1,data,1,0,NULL,0,0); + fftw(plan->plan_mid_forward,total2/length2,data,1,0,NULL,0,0); + fftw(plan->plan_slow_forward,total3/length3,data,1,0,NULL,0,0); + } else { + fftw(plan->plan_fast_backward,total1/length1,data,1,0,NULL,0,0); + fftw(plan->plan_mid_backward,total2/length2,data,1,0,NULL,0,0); + fftw(plan->plan_slow_backward,total3/length3,data,1,0,NULL,0,0); + } +#endif + + // scaling if required + // limit num to size of data + +#ifndef FFT_T3E + if (flag == 1 && plan->scaled) { + norm = plan->norm; + num = MIN(plan->normnum,nsize); + for (i = 0; i < num; i++) { + data[i].re *= norm; + data[i].im *= norm; + } + } +#endif + +#ifdef FFT_T3E + if (flag == 1 && plan->scaled) { + norm = plan->norm; + num = MIN(plan->normnum,nsize); + for (i = 0; i < num; i++) data[i] *= (norm,norm); + } +#endif +} diff --git a/src/fft3d.h b/src/fft3d.h new file mode 100644 index 0000000000000000000000000000000000000000..eb4a179db3140f07dc1221386d0cde206e5f1aa4 --- /dev/null +++ b/src/fft3d.h @@ -0,0 +1,242 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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. +------------------------------------------------------------------------- */ + +// User-settable FFT precision + +// FFT_PRECISION = 1 is single-precision complex (4-byte real, 4-byte imag) +// FFT_PRECISION = 2 is double-precision complex (8-byte real, 8-byte imag) + +#define FFT_PRECISION 2 + +// ------------------------------------------------------------------------- + +// Data types for single-precision complex + +#if FFT_PRECISION == 1 + +#ifdef FFT_SGI +#include "fft.h" +typedef complex FFT_DATA; +#define FFT_1D cfft1d +#define FFT_1D_INIT cfft1di +extern "C" { + int cfft1d(int, int, FFT_DATA *, int, FFT_DATA *); + FFT_DATA *cfft1di(int, FFT_DATA *); +} + +#endif + +#ifdef FFT_SCSL +#include <scsl_fft.h> +typedef scsl_complex FFT_DATA; +typedef float FFT_PREC; +#define FFT_1D ccfft +#define FFT_1D_INIT ccfft +extern "C" { + int ccfft(int, int, FFT_PREC, FFT_DATA *, FFT_DATA *, + FFT_PREC *, FFT_PREC *, int *); +} + +#endif + +#ifdef FFT_INTEL +typedef struct { + float re; + float im; +} FFT_DATA; +#define FFT_1D cfft1d_ +#define FFT_1D_INIT cfft1d_ +extern "C" { + void cfft1d_(FFT_DATA *, int *, int *, FFT_DATA *); +} +#endif + +#ifdef FFT_DEC +typedef struct { + float re; + float im; +} FFT_DATA; +#define FFT_1D cfft_ +extern "C" { + void cfft_(char *, char *, char *, FFT_DATA *, FFT_DATA *, int *, int *); +} +#endif + +#ifdef FFT_T3E +#include <complex.h> +typedef complex single FFT_DATA; +#define FFT_1D GGFFT +#define FFT_1D_INIT GGFFT +extern "C" { + void GGFFT(int *, int *, double *, FFT_DATA *, FFT_DATA *, + double *, double *, int *); +} +#endif + +#ifdef FFT_FFTW +#include "fftw.h" +typedef FFTW_COMPLEX FFT_DATA; +#endif + +#ifdef FFT_NONE +typedef struct { + float re; + float im; +} FFT_DATA; +#endif + +#endif + +// ------------------------------------------------------------------------- + +// Data types for double-precision complex + +#if FFT_PRECISION == 2 + +#ifdef FFT_SGI +#include "fft.h" +typedef zomplex FFT_DATA; +#define FFT_1D zfft1d +#define FFT_1D_INIT zfft1di +extern "C" { + int zfft1d(int, int, FFT_DATA *, int, FFT_DATA *); + FFT_DATA *zfft1di(int, FFT_DATA *); +} +#endif + +#ifdef FFT_SCSL +#include <scsl_fft.h> +typedef scsl_zomplex FFT_DATA; +typedef double FFT_PREC; +#define FFT_1D zzfft +#define FFT_1D_INIT zzfft +extern "C" { + int zzfft(int, int, FFT_PREC, FFT_DATA *, FFT_DATA *, + FFT_PREC *, FFT_PREC *, int *); +} +#endif + +#ifdef FFT_INTEL +typedef struct { + double re; + double im; +} FFT_DATA; +#define FFT_1D zfft1d_ +#define FFT_1D_INIT zfft1d_ +extern "C" { + void zfft1d_(FFT_DATA *, int *, int *, FFT_DATA *); +} +#endif + +#ifdef FFT_DEC +typedef struct { + double re; + double im; +} FFT_DATA; +#define FFT_1D zfft_ +extern "C" { + void zfft_(char *, char *, char *, FFT_DATA *, FFT_DATA *, int *, int *); +} +#endif + +#ifdef FFT_T3E +#include <complex.h> +typedef complex double FFT_DATA; +#define FFT_1D CCFFT +#define FFT_1D_INIT CCFFT +extern "C" { + void CCFFT(int *, int *, double *, FFT_DATA *, FFT_DATA *, + double *, double *, int *); +} +#endif + +#ifdef FFT_FFTW +#include "fftw.h" +typedef FFTW_COMPLEX FFT_DATA; +#endif + +#ifdef FFT_NONE +typedef struct { + double re; + double im; +} FFT_DATA; +#endif + +#endif + +// ------------------------------------------------------------------------- + +// details of how to do a 3d FFT + +struct fft_plan_3d { + struct remap_plan_3d *pre_plan; // remap from input -> 1st FFTs + struct remap_plan_3d *mid1_plan; // remap from 1st -> 2nd FFTs + struct remap_plan_3d *mid2_plan; // remap from 2nd -> 3rd FFTs + struct remap_plan_3d *post_plan; // remap from 3rd FFTs -> output + FFT_DATA *copy; // memory for remap results (if needed) + FFT_DATA *scratch; // scratch space for remaps + int total1,total2,total3; // # of 1st,2nd,3rd FFTs (times length) + int length1,length2,length3; // length of 1st,2nd,3rd FFTs + int pre_target; // where to put remap results + int mid1_target,mid2_target; + int scaled; // whether to scale FFT results + int normnum; // # of values to rescale + double norm; // normalization factor for rescaling + + // system specific 1d FFT info +#ifdef FFT_SGI + FFT_DATA *coeff1; + FFT_DATA *coeff2; + FFT_DATA *coeff3; +#endif +#ifdef FFT_SCSL + FFT_PREC *coeff1; + FFT_PREC *coeff2; + FFT_PREC *coeff3; + FFT_PREC *work1; + FFT_PREC *work2; + FFT_PREC *work3; +#endif +#ifdef FFT_INTEL + FFT_DATA *coeff1; + FFT_DATA *coeff2; + FFT_DATA *coeff3; +#endif +#ifdef FFT_T3E + double *coeff1; + double *coeff2; + double *coeff3; + double *work1; + double *work2; + double *work3; +#endif +#ifdef FFT_FFTW + fftw_plan plan_fast_forward; + fftw_plan plan_fast_backward; + fftw_plan plan_mid_forward; + fftw_plan plan_mid_backward; + fftw_plan plan_slow_forward; + fftw_plan plan_slow_backward; +#endif +}; + +// function prototypes + +void fft_3d(FFT_DATA *, FFT_DATA *, int, struct fft_plan_3d *); +struct fft_plan_3d *fft_3d_create_plan(MPI_Comm, int, int, int, + int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int *); +void fft_3d_destroy_plan(struct fft_plan_3d *); +void factor(int, int *, int *); +void bifactor(int, int *, int *); +void fft_1d_only(FFT_DATA *, int, int, struct fft_plan_3d *); diff --git a/src/fft3d_wrap.cpp b/src/fft3d_wrap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..af72fdf4a23ef741b2cbfefc44203d16d49ce23b --- /dev/null +++ b/src/fft3d_wrap.cpp @@ -0,0 +1,53 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "fft3d_wrap.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FFT3d::FFT3d(MPI_Comm comm, int nfast, int nmid, int nslow, + int in_ilo, int in_ihi, int in_jlo, int in_jhi, + int in_klo, int in_khi, + int out_ilo, int out_ihi, int out_jlo, int out_jhi, + int out_klo, int out_khi, + int scaled, int permute, int *nbuf) +{ + plan = fft_3d_create_plan(comm,nfast,nmid,nslow, + in_ilo,in_ihi,in_jlo,in_jhi,in_klo,in_khi, + out_ilo,out_ihi,out_jlo,out_jhi,out_klo,out_khi, + scaled,permute,nbuf); + if (plan == NULL) error->one("Could not create 3d FFT plan"); +} + +/* ---------------------------------------------------------------------- */ + +FFT3d::~FFT3d() +{ + fft_3d_destroy_plan(plan); +} + +/* ---------------------------------------------------------------------- */ + +void FFT3d::compute(double *in, double *out, int flag) +{ + fft_3d((FFT_DATA *) in,(FFT_DATA *) out,flag,plan); +} + +/* ---------------------------------------------------------------------- */ + +void FFT3d::timing1d(double *in, int nsize, int flag) +{ + fft_1d_only((FFT_DATA *) in,nsize,flag,plan); +} diff --git a/src/fft3d_wrap.h b/src/fft3d_wrap.h new file mode 100644 index 0000000000000000000000000000000000000000..2d32e346266b030e4eb4b5bae17106206958e0de --- /dev/null +++ b/src/fft3d_wrap.h @@ -0,0 +1,32 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FFT3D_WRAP_H +#define FFT3D_WRAP_H + +#include "lammps.h" +#include "fft3d.h" + +class FFT3d : public LAMMPS { + public: + FFT3d(MPI_Comm,int,int,int,int,int,int,int,int,int, + int,int,int,int,int,int,int,int,int *); + ~FFT3d(); + void compute(double *, double *, int); + void timing1d(double *, int, int); + + private: + struct fft_plan_3d *plan; +}; + +#endif diff --git a/src/finish.cpp b/src/finish.cpp new file mode 100644 index 0000000000000000000000000000000000000000..65e6212dc28752734cbdeb1d1645f4a51b8b987e --- /dev/null +++ b/src/finish.cpp @@ -0,0 +1,422 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "math.h" +#include "string.h" +#include "stdio.h" +#include "finish.h" +#include "timer.h" +#include "atom.h" +#include "force.h" +#include "kspace.h" +#include "update.h" +#include "min.h" +#include "neighbor.h" +#include "output.h" +#include "memory.h" + +/* ---------------------------------------------------------------------- */ + +void Finish::end(int flag) +{ + int i; + int histo[10]; + double time,tmp,ave,max,min; + + int me,nprocs; + MPI_Comm_rank(world,&me); + MPI_Comm_size(world,&nprocs); + + // deduce time_other + + double time_other = timer->array[TIME_LOOP] - + (timer->array[TIME_PAIR] + timer->array[TIME_BOND] + + timer->array[TIME_KSPACE] + timer->array[TIME_NEIGHBOR] + + timer->array[TIME_COMM] + timer->array[TIME_OUTPUT]); + + double time_loop = timer->array[TIME_LOOP]; + MPI_Allreduce(&time_loop,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + time_loop = tmp/nprocs; + + // overall loop time + // use actual natoms, in case atoms were lost + + double natoms; + double rlocal = atom->nlocal; + MPI_Allreduce(&rlocal,&natoms,1,MPI_DOUBLE,MPI_SUM,world); + + if (me == 0) { + if (screen) + fprintf(screen, + "Loop time of %g on %d procs for %d steps with %.15g atoms\n", + time_loop,nprocs,update->nsteps,natoms); + if (logfile) + fprintf(logfile, + "Loop time of %g on %d procs for %d steps with %.15g atoms\n", + time_loop,nprocs,update->nsteps,natoms); + } + + if (flag == 0) return; + + if (me == 0) { + if (screen) fprintf(screen,"\n"); + if (logfile) fprintf(logfile,"\n"); + } + + // minimization stats + + if (update->whichflag == 1) { + if (me == 0) { + if (screen) { + fprintf(screen,"Minimization stats:\n"); + fprintf(screen," E initial, next-to-last, final = %g %g %g\n", + update->minimize->einitial,update->minimize->eprevious, + update->minimize->efinal); + fprintf(screen," Gradient 2-norm init/final= %g %g\n", + update->minimize->gnorm2_init,update->minimize->gnorm2_final); + fprintf(screen," Gradient inf-norm init/final= %g %g\n", + update->minimize->gnorminf_init, + update->minimize->gnorminf_final); + fprintf(screen," Iterations = %d\n",update->minimize->niter); + fprintf(screen," Force evaluations = %d\n",update->minimize->neval); + } + if (logfile) { + fprintf(logfile,"Minimization stats:\n"); + fprintf(logfile," E initial, next-to-last, final = %g %g %g\n", + update->minimize->einitial,update->minimize->eprevious, + update->minimize->efinal); + fprintf(logfile," Gradient 2-norm init/final= %g %g\n", + update->minimize->gnorm2_init,update->minimize->gnorm2_final); + fprintf(logfile," Gradient inf-norm init/final= %g %g\n", + update->minimize->gnorminf_init, + update->minimize->gnorminf_final); + fprintf(logfile," Iterations = %d\n",update->minimize->niter); + fprintf(logfile," Force evaluations = %d\n",update->minimize->neval); + } + } + if (me == 0) { + if (screen) fprintf(screen,"\n"); + if (logfile) fprintf(logfile,"\n"); + } + } + + // timing breakdowns + + if (time_loop == 0.0) time_loop = 1.0; + + time = timer->array[TIME_PAIR]; + MPI_Allreduce(&time,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + time = tmp/nprocs; + if (me == 0) { + if (screen) + fprintf(screen,"Pair time (%%) = %g (%g)\n",time,time/time_loop*100.0); + if (logfile) + fprintf(logfile,"Pair time (%%) = %g (%g)\n",time,time/time_loop*100.0); + } + + if (atom->molecular) { + time = timer->array[TIME_BOND]; + MPI_Allreduce(&time,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + time = tmp/nprocs; + if (me == 0) { + if (screen) + fprintf(screen,"Bond time (%%) = %g (%g)\n", + time,time/time_loop*100.0); + if (logfile) + fprintf(logfile,"Bond time (%%) = %g (%g)\n", + time,time/time_loop*100.0); + } + } + + if (force->kspace) { + time = timer->array[TIME_KSPACE]; + MPI_Allreduce(&time,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + time = tmp/nprocs; + if (me == 0) { + if (screen) + fprintf(screen,"Kspce time (%%) = %g (%g)\n", + time,time/time_loop*100.0); + if (logfile) + fprintf(logfile,"Kspce time (%%) = %g (%g)\n", + time,time/time_loop*100.0); + } + } + + time = timer->array[TIME_NEIGHBOR]; + MPI_Allreduce(&time,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + time = tmp/nprocs; + if (me == 0) { + if (screen) + fprintf(screen,"Neigh time (%%) = %g (%g)\n",time,time/time_loop*100.0); + if (logfile) + fprintf(logfile,"Neigh time (%%) = %g (%g)\n",time,time/time_loop*100.0); + } + + time = timer->array[TIME_COMM]; + MPI_Allreduce(&time,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + time = tmp/nprocs; + if (me == 0) { + if (screen) + fprintf(screen,"Comm time (%%) = %g (%g)\n",time,time/time_loop*100.0); + if (logfile) + fprintf(logfile,"Comm time (%%) = %g (%g)\n",time,time/time_loop*100.0); + } + + time = timer->array[TIME_OUTPUT]; + MPI_Allreduce(&time,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + time = tmp/nprocs; + if (me == 0) { + if (screen) + fprintf(screen,"Outpt time (%%) = %g (%g)\n",time,time/time_loop*100.0); + if (logfile) + fprintf(logfile,"Outpt time (%%) = %g (%g)\n",time,time/time_loop*100.0); + } + + time = time_other; + MPI_Allreduce(&time,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + time = tmp/nprocs; + if (me == 0) { + if (screen) + fprintf(screen,"Other time (%%) = %g (%g)\n",time,time/time_loop*100.0); + if (logfile) + fprintf(logfile,"Other time (%%) = %g (%g)\n",time,time/time_loop*100.0); + } + + // FFT timing statistics + // time3d,time1d = total time during run for 3d and 1d FFTs + + if (strstr(force->kspace_style,"pppm")) { + if (me == 0) { + if (screen) fprintf(screen,"\n"); + if (logfile) fprintf(logfile,"\n"); + } + + int nsteps = update->nsteps; + + int nsample = 5; + double time3d,time1d; + force->kspace->timing(nsample,time3d,time1d); + + time3d = nsteps * time3d / nsample; + MPI_Allreduce(&time3d,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + time3d = tmp/nprocs; + + time1d = nsteps * time1d / nsample; + MPI_Allreduce(&time1d,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + time1d = tmp/nprocs; + + double time_kspace = timer->array[TIME_KSPACE]; + MPI_Allreduce(&time_kspace,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + time_kspace = tmp/nprocs; + + double ntotal = 1.0 * force->kspace->nx_pppm * + force->kspace->ny_pppm * force->kspace->nz_pppm; + double nflops = 5.0 * ntotal * log(ntotal); + + double fraction,flop3,flop1; + if (nsteps) { + fraction = time3d/time_kspace*100.0; + flop3 = nflops/1.0e9/(time3d/4.0/nsteps); + flop1 = nflops/1.0e9/(time1d/4.0/nsteps); + } else fraction = flop3 = flop1 = 0.0; + + if (me == 0) { + if (screen) { + fprintf(screen,"FFT time (%% of Kspce) = %g (%g)\n",time3d,fraction); + fprintf(screen,"FFT Gflps 3d 1d-only = %g %g\n",flop3,flop1); + } + if (logfile) { + fprintf(logfile,"FFT time (%% of Kspce) = %g (%g)\n", + time3d,time3d/time_kspace*100.0); + fprintf(logfile,"FFT Gflps 3d 1d-only = %g %g\n", + nflops/1.0e9/(time3d/4.0/nsteps), + nflops/1.0e9/(time1d/4.0/nsteps)); + } + } + } + + if (me == 0) { + if (screen) fprintf(screen,"\n"); + if (logfile) fprintf(logfile,"\n"); + } + + tmp = atom->nlocal; + stats(1,&tmp,&ave,&max,&min,10,histo); + if (me == 0) { + if (screen) { + fprintf(screen,"Nlocal: %g ave %g max %g min\n",ave,max,min); + fprintf(screen,"Histogram:"); + for (i = 0; i < 10; i++) fprintf(screen," %d",histo[i]); + fprintf(screen,"\n"); + } + if (logfile) { + fprintf(logfile,"Nlocal: %g ave %g max %g min\n",ave,max,min); + fprintf(logfile,"Histogram:"); + for (i = 0; i < 10; i++) fprintf(logfile," %d",histo[i]); + fprintf(logfile,"\n"); + } + } + + tmp = atom->nghost; + stats(1,&tmp,&ave,&max,&min,10,histo); + if (me == 0) { + if (screen) { + fprintf(screen,"Nghost: %g ave %g max %g min\n",ave,max,min); + fprintf(screen,"Histogram:"); + for (i = 0; i < 10; i++) fprintf(screen," %d",histo[i]); + fprintf(screen,"\n"); + } + if (logfile) { + fprintf(logfile,"Nghost: %g ave %g max %g min\n",ave,max,min); + fprintf(logfile,"Histogram:"); + for (i = 0; i < 10; i++) fprintf(logfile," %d",histo[i]); + fprintf(logfile,"\n"); + } + } + + int nneigh = 0; + if (neighbor->half_every) + for (i = 0; i < atom->nlocal; i++) nneigh += neighbor->numneigh[i]; + else if (neighbor->full_every) + for (i = 0; i < atom->nlocal; i++) nneigh += neighbor->numneigh_full[i]; + + tmp = nneigh; + stats(1,&tmp,&ave,&max,&min,10,histo); + if (me == 0) { + if (screen) { + fprintf(screen,"Neighs: %g ave %g max %g min\n",ave,max,min); + fprintf(screen,"Histogram:"); + for (i = 0; i < 10; i++) fprintf(screen," %d",histo[i]); + fprintf(screen,"\n"); + } + if (logfile) { + fprintf(logfile,"Neighs: %g ave %g max %g min\n",ave,max,min); + fprintf(logfile,"Histogram:"); + for (i = 0; i < 10; i++) fprintf(logfile," %d",histo[i]); + fprintf(logfile,"\n"); + } + } + + if (neighbor->half_every && neighbor->full_every) { + + nneigh = 0; + for (i = 0; i < atom->nlocal; i++) nneigh += neighbor->numneigh_full[i]; + + tmp = nneigh; + stats(1,&tmp,&ave,&max,&min,10,histo); + if (me == 0) { + if (screen) { + fprintf(screen,"FullNghs: %g ave %g max %g min\n",ave,max,min); + fprintf(screen,"Histogram:"); + for (i = 0; i < 10; i++) fprintf(screen," %d",histo[i]); + fprintf(screen,"\n"); + } + if (logfile) { + fprintf(logfile,"FullNghs: %g ave %g max %g min\n",ave,max,min); + fprintf(logfile,"Histogram:"); + for (i = 0; i < 10; i++) fprintf(logfile," %d",histo[i]); + fprintf(logfile,"\n"); + } + } + } + + if (me == 0) { + if (screen) fprintf(screen,"\n"); + if (logfile) fprintf(logfile,"\n"); + } + + tmp = nneigh; + double nall; + MPI_Allreduce(&tmp,&nall,1,MPI_DOUBLE,MPI_SUM,world); + + int nspec; + double nspec_all; + if (atom->molecular) { + nspec = 0; + for (i = 0; i < atom->nlocal; i++) nspec += atom->nspecial[i][2]; + tmp = nspec; + MPI_Allreduce(&tmp,&nspec_all,1,MPI_DOUBLE,MPI_SUM,world); + } + + if (me == 0) { + if (screen) { + if (nall < 2.0e9) + fprintf(screen,"Total # of neighbors = %d\n",static_cast<int> (nall)); + else fprintf(screen,"Total # of neighbors = %g\n",nall); + if (natoms > 0) fprintf(screen,"Ave neighs/atom = %g\n",nall/natoms); + if (atom->molecular && natoms > 0) + fprintf(screen,"Ave special neighs/atom = %g\n",nspec_all/natoms); + fprintf(screen,"Neighbor list builds = %d\n",neighbor->ncalls); + fprintf(screen,"Dangerous builds = %d\n",neighbor->ndanger); + } + if (logfile) { + if (nall < 2.0e9) + fprintf(logfile,"Total # of neighbors = %d\n",static_cast<int> (nall)); + else fprintf(logfile,"Total # of neighbors = %g\n",nall); + if (natoms > 0) fprintf(logfile,"Ave neighs/atom = %g\n",nall/natoms); + if (atom->molecular && natoms > 0) + fprintf(logfile,"Ave special neighs/atom = %g\n",nspec_all/natoms); + fprintf(logfile,"Neighbor list builds = %d\n",neighbor->ncalls); + fprintf(logfile,"Dangerous builds = %d\n",neighbor->ndanger); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void Finish::stats(int n, double *data, + double *pave, double *pmax, double *pmin, + int nhisto, int *histo) +{ + int i,m; + int *histotmp; + + double min = 1.0e20; + double max = -1.0e20; + double ave = 0.0; + for (i = 0; i < n; i++) { + ave += data[i]; + if (data[i] < min) min = data[i]; + if (data[i] > max) max = data[i]; + } + + int ntotal; + MPI_Allreduce(&n,&ntotal,1,MPI_INT,MPI_SUM,world); + double tmp; + MPI_Allreduce(&ave,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + ave = tmp/ntotal; + MPI_Allreduce(&min,&tmp,1,MPI_DOUBLE,MPI_MIN,world); + min = tmp; + MPI_Allreduce(&max,&tmp,1,MPI_DOUBLE,MPI_MAX,world); + max = tmp; + + for (i = 0; i < nhisto; i++) histo[i] = 0; + + double del = max - min; + for (i = 0; i < n; i++) { + if (del == 0.0) m = 0; + else m = static_cast<int> ((data[i]-min)/del * nhisto); + if (m > nhisto-1) m = nhisto-1; + histo[m]++; + } + + histotmp = (int *) memory->smalloc(nhisto*sizeof(int),"finish:histotmp"); + MPI_Allreduce(histo,histotmp,nhisto,MPI_INT,MPI_SUM,world); + for (i = 0; i < nhisto; i++) histo[i] = histotmp[i]; + memory->sfree(histotmp); + + *pave = ave; + *pmax = max; + *pmin = min; +} diff --git a/src/finish.h b/src/finish.h new file mode 100644 index 0000000000000000000000000000000000000000..f22b427616f0e1777b8b4e0cb88b869e1c40d80f --- /dev/null +++ b/src/finish.h @@ -0,0 +1,29 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FINISH_H +#define FINISH_H + +#include "lammps.h" + +class Finish : public LAMMPS { + public: + Finish() {} + ~Finish() {} + void end(int); + + private: + void stats(int, double *, double *, double *, double *, int, int *); +}; + +#endif diff --git a/src/fix.cpp b/src/fix.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ebc926900c091caef447971ac3319bfb5a1d3171 --- /dev/null +++ b/src/fix.cpp @@ -0,0 +1,96 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "fix.h" +#include "group.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +Fix::Fix(int narg, char **arg) +{ + int n = strlen(arg[0]) + 1; + id = new char[n]; + strcpy(id,arg[0]); + + igroup = group->find(arg[1]); + groupbit = group->bitmask[igroup]; + + n = strlen(arg[2]) + 1; + style = new char[n]; + strcpy(style,arg[2]); + + restart_global = 0; + restart_peratom = 0; + force_reneighbor = 0; + thermo_print = 0; + thermo_energy = 0; + nevery = 1; + + neigh_half_once = neigh_half_every = 0; + neigh_full_once = neigh_full_every = 0; + + // mask settings - same as define settings in modify.cpp + + INITIAL_INTEGRATE = 1; + PRE_EXCHANGE = 2; + PRE_NEIGHBOR = 4; + POST_FORCE = 8; + FINAL_INTEGRATE = 16; + END_OF_STEP = 32; + THERMO = 64; + INITIAL_INTEGRATE_RESPA = 128; + POST_FORCE_RESPA = 256; + FINAL_INTEGRATE_RESPA = 512; + MIN_POST_FORCE = 1024; +} + +/* ---------------------------------------------------------------------- */ + +Fix::~Fix() +{ + delete [] id; + delete [] style; +} + +/* ---------------------------------------------------------------------- + process params common to all fixes here + if unknown param, call modify_param specific to the fix +------------------------------------------------------------------------- */ + +void Fix::modify_params(int narg, char **arg) +{ + if (narg == 0) error->all("Illegal fix_modify command"); + + int iarg = 0; + while (iarg < narg) { + if (strcmp(arg[iarg],"thermo") == 0) { + if (iarg+2 > narg) error->all("Illegal fix_modify command"); + if (strcmp(arg[iarg+1],"no") == 0) thermo_print = 0; + else if (strcmp(arg[iarg+1],"yes") == 0) thermo_print = 1; + else error->all("Illegal fix_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"energy") == 0) { + if (iarg+2 > narg) error->all("Illegal fix_modify command"); + if (strcmp(arg[iarg+1],"no") == 0) thermo_energy = 0; + else if (strcmp(arg[iarg+1],"yes") == 0) thermo_energy = 1; + else error->all("Illegal fix_modify command"); + iarg += 2; + } else { + int n = modify_param(narg-iarg,&arg[iarg]); + if (n == 0) error->all("Illegal fix_modify command"); + iarg += n; + } + } +} diff --git a/src/fix.h b/src/fix.h new file mode 100644 index 0000000000000000000000000000000000000000..6311a8ef6e1b4b9a3c65cde0a4a892e9716720ea --- /dev/null +++ b/src/fix.h @@ -0,0 +1,90 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_H +#define FIX_H + +#include "lammps.h" + +class Fix : public LAMMPS { + public: + char *id,*style; + int igroup,groupbit; + int restart_global; // 1 if Fix saves global state, 0 if not + int restart_peratom; // 1 if Fix saves peratom state, 0 if not + int force_reneighbor; // 1 if Fix forces reneighboring, 0 if not + int next_reneighbor; // next timestep to force a reneighboring + int thermo_print; // 1 if Fix prints info during thermo, 0 no + int thermo_energy; // 1 if Fix adds to thermo energy, 0 if not + int nevery; // how often to call an end_of_step fix + int neigh_half_once; // 0/1 if needs half neigh list occasionally + int neigh_half_every; // 0/1 if needs half neigh list every step + int neigh_full_once; // 0/1 if needs full neigh list occasionally + int neigh_full_every; // 0/1 if needs full neigh list every step + double virial[6]; // fix contribution to pressure virial + + int INITIAL_INTEGRATE,PRE_EXCHANGE,PRE_NEIGHBOR; // mask settings + int POST_FORCE,FINAL_INTEGRATE,END_OF_STEP,THERMO; + int INITIAL_INTEGRATE_RESPA,POST_FORCE_RESPA,FINAL_INTEGRATE_RESPA; + int MIN_POST_FORCE; + + Fix(int, char **); + virtual ~Fix(); + void modify_params(int, char **); + + virtual int setmask() = 0; + + virtual void init() {} + virtual void setup() {} + virtual void min_setup() {} + virtual void initial_integrate() {} + virtual void pre_exchange() {} + virtual void pre_neighbor() {} + virtual void post_force(int) {} + virtual void final_integrate() {} + virtual void end_of_step() {} + virtual void write_restart(FILE *) {} + virtual void restart(char *) {} + + virtual int memory_usage() {return 0;} + virtual void grow_arrays(int) {} + virtual void copy_arrays(int, int) {} + virtual int pack_exchange(int, double *) {return 0;} + virtual int unpack_exchange(int, double *) {return 0;} + virtual int pack_restart(int, double *) {return 0;} + virtual void unpack_restart(int, int) {} + virtual int size_restart(int) {return 0;} + virtual int maxsize_restart() {return 0;} + + virtual void initial_integrate_respa(int, int) {} + virtual void post_force_respa(int, int, int) {} + virtual void final_integrate_respa(int) {} + + virtual void min_post_force(int) {} + + virtual int pack_comm(int, int *, double *, int *) {return 0;} + virtual void unpack_comm(int, int, double *) {} + virtual int pack_reverse_comm(int, int, double *) {return 0;} + virtual void unpack_reverse_comm(int, int *, double *) {} + + virtual int thermo_fields(int, int *, char **) {return 0;} + virtual int thermo_compute(double *) {return 0;} + virtual void dump() {} + + virtual int dof(int) {return 0;} + virtual void dilate(int, double, double, double, double) {} + + virtual int modify_param(int, char **) {return 0;} +}; + +#endif diff --git a/src/fix_add_force.cpp b/src/fix_add_force.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fa40420a3f94c8c822cfe85dfe8d21515e60b47a --- /dev/null +++ b/src/fix_add_force.cpp @@ -0,0 +1,99 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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_add_force.h" +#include "atom.h" +#include "update.h" +#include "respa.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixAddForce::FixAddForce(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 6) error->all("Illegal fix addforce command"); + xvalue = atof(arg[3]); + yvalue = atof(arg[4]); + zvalue = atof(arg[5]); +} + +/* ---------------------------------------------------------------------- */ + +int FixAddForce::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + mask |= POST_FORCE_RESPA; + mask |= MIN_POST_FORCE; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixAddForce::init() +{ + if (strcmp(update->integrate_style,"respa") == 0) + nlevels_respa = ((Respa *) update->integrate)->nlevels; +} + +/* ---------------------------------------------------------------------- */ + +void FixAddForce::setup() +{ + if (strcmp(update->integrate_style,"verlet") == 0) + post_force(1); + else { + ((Respa *) update->integrate)->copy_flevel_f(nlevels_respa-1); + post_force_respa(1,nlevels_respa-1,0); + ((Respa *) update->integrate)->copy_f_flevel(nlevels_respa-1); + } +} + +/* ---------------------------------------------------------------------- */ + +void FixAddForce::min_setup() +{ + post_force(1); +} + +/* ---------------------------------------------------------------------- */ + +void FixAddForce::post_force(int vflag) +{ + double **f = atom->f; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + f[i][0] += xvalue; + f[i][1] += yvalue; + f[i][2] += zvalue; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixAddForce::post_force_respa(int vflag, int ilevel, int iloop) +{ + if (ilevel == nlevels_respa-1) post_force(vflag); +} + +/* ---------------------------------------------------------------------- */ + +void FixAddForce::min_post_force(int vflag) +{ + post_force(vflag); +} diff --git a/src/fix_add_force.h b/src/fix_add_force.h new file mode 100644 index 0000000000000000000000000000000000000000..85075c5c74d00d030c649a5d5f51a0c5aabfadf9 --- /dev/null +++ b/src/fix_add_force.h @@ -0,0 +1,36 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_ADD_FORCE_H +#define FIX_ADD_FORCE_H + +#include "fix.h" + +class FixAddForce : public Fix { + public: + FixAddForce(int, char **); + ~FixAddForce() {} + int setmask(); + void init(); + void setup(); + void min_setup(); + void post_force(int); + void post_force_respa(int, int, int); + void min_post_force(int); + + private: + double xvalue,yvalue,zvalue; + int nlevels_respa; +}; + +#endif diff --git a/src/fix_ave_force.cpp b/src/fix_ave_force.cpp new file mode 100644 index 0000000000000000000000000000000000000000..af44ee70ce79c6d15f6979d4b30233d19d21ca66 --- /dev/null +++ b/src/fix_ave_force.cpp @@ -0,0 +1,171 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "string.h" +#include "stdlib.h" +#include "fix_ave_force.h" +#include "atom.h" +#include "update.h" +#include "respa.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixAveForce::FixAveForce(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 6) error->all("Illegal fix aveforce command"); + + xflag = yflag = zflag = 1; + if (strcmp(arg[3],"NULL") == 0) xflag = 0; + else xvalue = atof(arg[3]); + if (strcmp(arg[4],"NULL") == 0) yflag = 0; + else yvalue = atof(arg[4]); + if (strcmp(arg[5],"NULL") == 0) zflag = 0; + else zvalue = atof(arg[5]); +} + +/* ---------------------------------------------------------------------- */ + +int FixAveForce::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + mask |= POST_FORCE_RESPA; + mask |= MIN_POST_FORCE; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixAveForce::init() +{ + if (strcmp(update->integrate_style,"respa") == 0) + nlevels_respa = ((Respa *) update->integrate)->nlevels; + + // ncount = total # of atoms in group + + int *mask = atom->mask; + int nlocal = atom->nlocal; + + int count = 0; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) count++; + MPI_Allreduce(&count,&ncount,1,MPI_INT,MPI_SUM,world); +} + +/* ---------------------------------------------------------------------- */ + +void FixAveForce::setup() +{ + if (strcmp(update->integrate_style,"verlet") == 0) + post_force(1); + else + for (int ilevel = 0; ilevel < nlevels_respa; ilevel++) { + ((Respa *) update->integrate)->copy_flevel_f(ilevel); + post_force_respa(1,ilevel,0); + ((Respa *) update->integrate)->copy_f_flevel(ilevel); + } +} + +/* ---------------------------------------------------------------------- */ + +void FixAveForce::min_setup() +{ + post_force(1); +} + +/* ---------------------------------------------------------------------- */ + +void FixAveForce::post_force(int vflag) +{ + // sum forces on participating atoms + + double **f = atom->f; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double sum[3]; + sum[0] = sum[1] = sum[2] = 0.0; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + sum[0] += f[i][0]; + sum[1] += f[i][1]; + sum[2] += f[i][2]; + } + + // average the force on participating atoms + // add in requested amount + + double sumall[3]; + MPI_Allreduce(sum,sumall,3,MPI_DOUBLE,MPI_SUM,world); + sumall[0] = sumall[0]/ncount + xvalue; + sumall[1] = sumall[1]/ncount + yvalue; + sumall[2] = sumall[2]/ncount + zvalue; + + // set force of all participating atoms to same value + // only for active dimensions + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + if (xflag) f[i][0] = sumall[0]; + if (yflag) f[i][1] = sumall[1]; + if (zflag) f[i][2] = sumall[2]; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixAveForce::post_force_respa(int vflag, int ilevel, int iloop) +{ + // ave + extra force on outermost level + // just ave on inner levels + + if (ilevel == nlevels_respa-1) post_force(vflag); + else { + double **f = atom->f; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double sum[3]; + sum[0] = sum[1] = sum[2] = 0.0; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + sum[0] += f[i][0]; + sum[1] += f[i][1]; + sum[2] += f[i][2]; + } + + double sumall[3]; + MPI_Allreduce(sum,sumall,3,MPI_DOUBLE,MPI_SUM,world); + sumall[0] = sumall[0]/ncount; + sumall[1] = sumall[1]/ncount; + sumall[2] = sumall[2]/ncount; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + if (xflag) f[i][0] = sumall[0]; + if (yflag) f[i][1] = sumall[1]; + if (zflag) f[i][2] = sumall[2]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixAveForce::min_post_force(int vflag) +{ + post_force(vflag); +} diff --git a/src/fix_ave_force.h b/src/fix_ave_force.h new file mode 100644 index 0000000000000000000000000000000000000000..bcd44c4c49b43be006aa8f5120481d156f2abdd8 --- /dev/null +++ b/src/fix_ave_force.h @@ -0,0 +1,38 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_AVE_FORCE_H +#define FIX_AVE_FORCE_H + +#include "fix.h" + +class FixAveForce : public Fix { + public: + FixAveForce(int, char **); + ~FixAveForce() {} + int setmask(); + void init(); + void setup(); + void min_setup(); + void post_force(int); + void post_force_respa(int, int, int); + void min_post_force(int); + + private: + int xflag,yflag,zflag; + double xvalue,yvalue,zvalue; + int ncount; + int nlevels_respa; +}; + +#endif diff --git a/src/fix_centro.cpp b/src/fix_centro.cpp new file mode 100644 index 0000000000000000000000000000000000000000..410b0f55a7b4cbd42c0cc003767087c193e85028 --- /dev/null +++ b/src/fix_centro.cpp @@ -0,0 +1,292 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "fix_centro.h" +#include "atom.h" +#include "modify.h" +#include "update.h" +#include "neighbor.h" +#include "force.h" +#include "pair.h" +#include "comm.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixCentro::FixCentro(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 3) error->all("Illegal fix centro command"); + + neigh_full_once = 1; + nmax = 0; + centro = NULL; + maxneigh = 0; + distsq = NULL; + nearest = NULL; +} + +/* ---------------------------------------------------------------------- */ + +FixCentro::~FixCentro() +{ + memory->sfree(centro); + memory->sfree(distsq); + memory->sfree(nearest); +} + +/* ---------------------------------------------------------------------- */ + +int FixCentro::setmask() +{ + int mask = 0; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixCentro::init() +{ + // warn if more than one centro fix + + int count = 0; + for (int i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"CENTRO") == 0) count++; + if (count > 1 && comm->me == 0) + error->warning("More than one dump custom with a centro attribute"); +} + +/* ---------------------------------------------------------------------- */ + +void FixCentro::dump() +{ + int j,k,jj,kk,n,numneigh; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq,value; + int *neighs; + double pairs[66]; + + // grow centro array if necessary + + if (atom->nlocal > nmax) { + memory->sfree(centro); + nmax = atom->nmax; + centro = (double *) memory->smalloc(nmax*sizeof(double),"centro:centro"); + } + + // if needed, build a full neighbor list + + if (!neighbor->full_every) neighbor->build_full(); + + // compute centro-symmetry parameter for each atom in group + // use full neighbor list + + double **x = atom->x; + int *mask = atom->mask; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double cutsq = force->pair->cutforce * force->pair->cutforce; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + neighs = neighbor->firstneigh_full[i]; + numneigh = neighbor->numneigh_full[i]; + + // insure distsq and nearest arrays are long enough + + if (numneigh > maxneigh) { + memory->sfree(distsq); + memory->sfree(nearest); + maxneigh = numneigh; + distsq = (double *) memory->smalloc(maxneigh*sizeof(double), + "centro:distsq"); + nearest = (int *) memory->smalloc(maxneigh*sizeof(int), + "centro:nearest"); + } + + // loop over list of all neighbors within force cutoff + // distsq[] = distance sq to each + // nearest[] = atom indices of neighbors + + n = 0; + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + if (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 < cutsq) { + distsq[n] = rsq; + nearest[n++] = j; + } + } + + // if not 12 neighbors, centro = 0.0 + + if (n < 12) { + centro[i] = 0.0; + continue; + } + + // store 12 nearest neighs in 1st 12 locations of distsq and nearest + + select2(12,n,distsq,nearest); + + // R = Ri + Rj for each of 66 i,j pairs among 12 neighbors + // pairs = squared length of each R + + n = 0; + for (j = 0; j < 12; j++) { + jj = nearest[j]; + for (k = j+1; k < 12; k++) { + kk = nearest[k]; + delx = x[jj][0] + x[kk][0] - 2.0*xtmp; + dely = x[jj][1] + x[kk][1] - 2.0*ytmp; + delz = x[jj][2] + x[kk][2] - 2.0*ztmp; + pairs[n++] = delx*delx + dely*dely + delz*delz; + } + } + + // store 6 smallest pair distances in 1st 6 locations of pairs + + select(6,66,pairs); + + // centrosymmetry = sum of 6 smallest squared values + + value = 0.0; + for (j = 0; j < 6; j++) value += pairs[j]; + centro[i] = value; + } +} + +/* ---------------------------------------------------------------------- + 2 select routines from Numerical Recipes (slightly modified) + find k smallest values in array of length n + 2nd routine sorts auxiliary array at same time +------------------------------------------------------------------------- */ + +#define SWAP(a,b) tmp = a; a = b; b = tmp; +#define ISWAP(a,b) itmp = a; a = b; b = itmp; + +void FixCentro::select(int k, int n, double *arr) +{ + int i,ir,j,l,mid; + double a,tmp; + + arr--; + l = 1; + ir = n; + for (;;) { + if (ir <= l+1) { + if (ir == l+1 && arr[ir] < arr[l]) { + SWAP(arr[l],arr[ir]) + } + return; + } else { + mid=(l+ir) >> 1; + SWAP(arr[mid],arr[l+1]) + if (arr[l] > arr[ir]) { + SWAP(arr[l],arr[ir]) + } + if (arr[l+1] > arr[ir]) { + SWAP(arr[l+1],arr[ir]) + } + if (arr[l] > arr[l+1]) { + SWAP(arr[l],arr[l+1]) + } + i = l+1; + j = ir; + a = arr[l+1]; + for (;;) { + do i++; while (arr[i] < a); + do j--; while (arr[j] > a); + if (j < i) break; + SWAP(arr[i],arr[j]) + } + arr[l+1] = arr[j]; + arr[j] = a; + if (j >= k) ir = j-1; + if (j <= k) l = i; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixCentro::select2(int k, int n, double *arr, int *iarr) +{ + int i,ir,j,l,mid,ia,itmp; + double a,tmp; + + arr--; + iarr--; + l = 1; + ir = n; + for (;;) { + if (ir <= l+1) { + if (ir == l+1 && arr[ir] < arr[l]) { + SWAP(arr[l],arr[ir]) + ISWAP(iarr[l],iarr[ir]) + } + return; + } else { + mid=(l+ir) >> 1; + SWAP(arr[mid],arr[l+1]) + ISWAP(iarr[mid],iarr[l+1]) + if (arr[l] > arr[ir]) { + SWAP(arr[l],arr[ir]) + ISWAP(iarr[l],iarr[ir]) + } + if (arr[l+1] > arr[ir]) { + SWAP(arr[l+1],arr[ir]) + ISWAP(iarr[l+1],iarr[ir]) + } + if (arr[l] > arr[l+1]) { + SWAP(arr[l],arr[l+1]) + ISWAP(iarr[l],iarr[l+1]) + } + i = l+1; + j = ir; + a = arr[l+1]; + ia = iarr[l+1]; + for (;;) { + do i++; while (arr[i] < a); + do j--; while (arr[j] > a); + if (j < i) break; + SWAP(arr[i],arr[j]) + ISWAP(iarr[i],iarr[j]) + } + arr[l+1] = arr[j]; + arr[j] = a; + iarr[l+1] = iarr[j]; + iarr[j] = ia; + if (j >= k) ir = j-1; + if (j <= k) l = i; + } + } +} + +/* ---------------------------------------------------------------------- + memory usage of local atom-based array +------------------------------------------------------------------------- */ + +int FixCentro::memory_usage() +{ + int bytes = nmax * sizeof(double); + return bytes; +} diff --git a/src/fix_centro.h b/src/fix_centro.h new file mode 100644 index 0000000000000000000000000000000000000000..b7031955e33e1bd9273b4cecb40bdad58ffa7c12 --- /dev/null +++ b/src/fix_centro.h @@ -0,0 +1,40 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_CENTRO_H +#define FIX_CENTRO_H + +#include "fix.h" + +class FixCentro : public Fix { + friend class DumpCustom; + + public: + FixCentro(int, char **); + ~FixCentro(); + int setmask(); + void init(); + void dump(); + int memory_usage(); + + private: + int nmax,maxneigh; + double *centro; + double *distsq; + int *nearest; + + void select(int, int, double *); + void select2(int, int, double *, int *); +}; + +#endif diff --git a/src/fix_com.cpp b/src/fix_com.cpp new file mode 100644 index 0000000000000000000000000000000000000000..568e8a3eea232649d4876b9a35c947f810efec49 --- /dev/null +++ b/src/fix_com.cpp @@ -0,0 +1,87 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "fix_com.h" +#include "atom.h" +#include "update.h" +#include "domain.h" +#include "group.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixCOM::FixCOM(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 5) error->all("Illegal fix com command"); + nevery = atoi(arg[3]); + if (nevery <= 0) error->all("Illegal fix com command"); + first = 1; + + MPI_Comm_rank(world,&me); + if (me == 0) { + fp = fopen(arg[4],"w"); + if (fp == NULL) { + char str[128]; + sprintf(str,"Cannot open fix com file %s",arg[4]); + error->one(str); + } + } + + if (me == 0) { + fprintf(fp,"Center-of-mass for group %s\n",group->names[igroup]); + fprintf(fp,"TimeStep x y z\n"); + } +} + +/* ---------------------------------------------------------------------- */ + +FixCOM::~FixCOM() +{ + if (me == 0) fclose(fp); +} + +/* ---------------------------------------------------------------------- */ + +int FixCOM::setmask() +{ + int mask = 0; + mask |= END_OF_STEP; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixCOM::init() +{ + masstotal = group->mass(igroup); +} + +/* ---------------------------------------------------------------------- */ + +void FixCOM::setup() +{ + if (first) end_of_step(); + first = 0; +} + +/* ---------------------------------------------------------------------- */ + +void FixCOM::end_of_step() +{ + double xcm[3]; + group->xcm(igroup,masstotal,xcm); + + if (me == 0) fprintf(fp,"%d %g %g %g\n", + update->ntimestep,xcm[0],xcm[1],xcm[2]); +} diff --git a/src/fix_com.h b/src/fix_com.h new file mode 100644 index 0000000000000000000000000000000000000000..ab5205b7d1bc6bd2b814e22cb509df049d0f561b --- /dev/null +++ b/src/fix_com.h @@ -0,0 +1,35 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_COM_H +#define FIX_COM_H + +#include "stdio.h" +#include "fix.h" + +class FixCOM : public Fix { + public: + FixCOM(int, char **); + ~FixCOM(); + int setmask(); + void init(); + void setup(); + void end_of_step(); + + private: + int me,first; + FILE *fp; + double masstotal; +}; + +#endif diff --git a/src/fix_drag.cpp b/src/fix_drag.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4063ce70ba6ba0e8cd81cb532f03276997163100 --- /dev/null +++ b/src/fix_drag.cpp @@ -0,0 +1,112 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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_drag.h" +#include "atom.h" +#include "update.h" +#include "respa.h" +#include "domain.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixDrag::FixDrag(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 8) error->all("Illegal fix drag command"); + + xflag = yflag = zflag = 1; + + if (strcmp(arg[3],"NULL") == 0) xflag = 0; + else xc = atof(arg[3]); + if (strcmp(arg[4],"NULL") == 0) yflag = 0; + else yc = atof(arg[4]); + if (strcmp(arg[5],"NULL") == 0) zflag = 0; + else zc = atof(arg[5]); + + f_mag = atof(arg[6]); + delta = atof(arg[7]); +} + +/* ---------------------------------------------------------------------- */ + +int FixDrag::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + mask |= POST_FORCE_RESPA; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixDrag::init() +{ + if (strcmp(update->integrate_style,"respa") == 0) + nlevels_respa = ((Respa *) update->integrate)->nlevels; +} + +/* ---------------------------------------------------------------------- */ + +void FixDrag::setup() +{ + if (strcmp(update->integrate_style,"verlet") == 0) + post_force(1); + else { + ((Respa *) update->integrate)->copy_flevel_f(nlevels_respa-1); + post_force_respa(1,nlevels_respa-1,0); + ((Respa *) update->integrate)->copy_f_flevel(nlevels_respa-1); + } +} + +/* ---------------------------------------------------------------------- */ + +void FixDrag::post_force(int vflag) +{ + // apply drag force to atoms in group of magnitude f_mag + // apply in direction (r-r0) if atom is further than delta away + + double **x = atom->x; + double **f = atom->f; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double dx,dy,dz,r,prefactor; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + dx = x[i][0] - xc; + dy = x[i][1] - yc; + dz = x[i][2] - zc; + if (!xflag) dx = 0.0; + if (!yflag) dy = 0.0; + if (!zflag) dz = 0.0; + domain->minimum_image(&dx,&dy,&dz); + r = sqrt(dx*dx + dy*dy + dz*dz); + if (r > delta) { + prefactor = f_mag/r; + f[i][0] -= prefactor*dx; + f[i][1] -= prefactor*dy; + f[i][2] -= prefactor*dz; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixDrag::post_force_respa(int vflag, int ilevel, int iloop) +{ + if (ilevel == nlevels_respa-1) post_force(vflag); +} diff --git a/src/fix_drag.h b/src/fix_drag.h new file mode 100644 index 0000000000000000000000000000000000000000..3a46b695db84173f3aef57dacb659e2b7f56308e --- /dev/null +++ b/src/fix_drag.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_DRAG_H +#define FIX_DRAG_H + +#include "fix.h" + +class FixDrag : public Fix { + public: + FixDrag(int, char **); + ~FixDrag() {} + int setmask(); + void init(); + void setup(); + void post_force(int); + void post_force_respa(int, int, int); + + private: + double xc,yc,zc; + double f_mag; + int xflag,yflag,zflag; + double delta; + int nlevels_respa; +}; + +#endif diff --git a/src/fix_efield.cpp b/src/fix_efield.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6fadf4054948d959bfaea79a9211968aed89cb6b --- /dev/null +++ b/src/fix_efield.cpp @@ -0,0 +1,100 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Christina Payne (Vanderbilt U) +------------------------------------------------------------------------- */ + +#include "string.h" +#include "stdlib.h" +#include "fix_efield.h" +#include "atom.h" +#include "update.h" +#include "force.h" +#include "respa.h" +#include "error.h" +#include "math.h" + +/* ---------------------------------------------------------------------- */ + +FixEfield::FixEfield(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 6) error->all("Illegal fix efield command"); + + double factor = force->qe2f; + ex = factor * atof(arg[3]); + ey = factor * atof(arg[4]); + ez = factor * atof(arg[5]); +} + +/* ---------------------------------------------------------------------- */ + +int FixEfield::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + mask |= POST_FORCE_RESPA; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixEfield::init() +{ + // require an atom style with charge defined + + if (atom->charge_allow == 0) + error->all("Must use charged atom style with fix efield"); + + if (strcmp(update->integrate_style,"respa") == 0) + nlevels_respa = ((Respa *) update->integrate)->nlevels; +} + +/* ---------------------------------------------------------------------- */ + +void FixEfield::setup() +{ + if (strcmp(update->integrate_style,"verlet") == 0) + post_force(1); + else { + ((Respa *) update->integrate)->copy_flevel_f(nlevels_respa-1); + post_force_respa(1,nlevels_respa-1,0); + ((Respa *) update->integrate)->copy_f_flevel(nlevels_respa-1); + } +} + +/* ---------------------------------------------------------------------- + apply F = qE +------------------------------------------------------------------------- */ + +void FixEfield::post_force(int vflag) +{ + double **f = atom->f; + double *q = atom->q; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + f[i][0] += q[i]*ex; + f[i][1] += q[i]*ey; + f[i][2] += q[i]*ez; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixEfield::post_force_respa(int vflag, int ilevel, int iloop) +{ + if (ilevel == nlevels_respa-1) post_force(vflag); +} diff --git a/src/fix_efield.h b/src/fix_efield.h new file mode 100644 index 0000000000000000000000000000000000000000..4add204245e84fa19a8257bada6e93253c3466b0 --- /dev/null +++ b/src/fix_efield.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_EFIELD_H +#define FIX_EFIELD_H + +#include "fix.h" + +class FixEfield : public Fix { + public: + FixEfield(int, char **); + ~FixEfield() {} + int setmask(); + void init(); + void setup(); + void post_force(int); + void post_force_respa(int, int, int); + + private: + double ex,ey,ez; + int nlevels_respa; +}; + +#endif diff --git a/src/fix_energy.cpp b/src/fix_energy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..899c198ee5fb0278433cc9914106fa50f2f59c6b --- /dev/null +++ b/src/fix_energy.cpp @@ -0,0 +1,210 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "fix_energy.h" +#include "atom.h" +#include "neighbor.h" +#include "modify.h" +#include "comm.h" +#include "update.h" +#include "force.h" +#include "pair.h" +#include "memory.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +FixEnergy::FixEnergy(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 3) error->all("Illegal fix energy command"); + + neigh_half_once = 1; + nmax = 0; + energy = NULL; +} + +/* ---------------------------------------------------------------------- */ + +FixEnergy::~FixEnergy() +{ + memory->sfree(energy); +} + +/* ---------------------------------------------------------------------- */ + +int FixEnergy::setmask() +{ + int mask = 0; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixEnergy::init() +{ + if (force->pair == NULL || force->pair->single_enable == 0) + error->all("Pair style does not support dumping per-atom energy"); + + eamstyle = 0; + if (force->pair_match("eam")) eamstyle = 1; + else if (force->pair_match("eam/alloy")) eamstyle = 1; + else if (force->pair_match("eam/fs")) eamstyle = 1; + + // set communication size in comm class + + comm->maxreverse_fix = MAX(comm->maxreverse_fix,1); + + // warn if more than one energy fix + + int count = 0; + for (int i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"ENERGY") == 0) count++; + if (count > 1 && comm->me == 0) + error->warning("More than one dump custom with an energy attribute"); +} + +/* ---------------------------------------------------------------------- */ + +void FixEnergy::dump() +{ + int i,j,k,n,itype,jtype,numneigh; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + double factor_coul,factor_lj,e; + int *neighs; + + // grow energy array if necessary + + if (atom->nmax > nmax) { + memory->sfree(energy); + nmax = atom->nmax; + energy = (double *) memory->smalloc(nmax*sizeof(double),"energy:energy"); + } + + // clear energy array + // n includes ghosts only if newton_pair flag is set + + if (force->newton_pair) n = atom->nlocal + atom->nghost; + else n = atom->nlocal; + + for (i = 0; i < n; i++) energy[i] = 0.0; + + // if needed, build a half neighbor list + + if (!neighbor->half_every) neighbor->build_half(); + + // compute pairwise energy for all atoms via pair->single() + // use half neighbor list + + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + double **cutsq = force->pair->cutsq; + + double **x = atom->x; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + + Pair::One one; + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + force->pair->single(i,j,itype,jtype,rsq,factor_coul,factor_lj,1,one); + e = one.eng_coul + one.eng_vdwl; + energy[i] += e; + energy[j] += e; + } + } + } + + // communicate energy between neighbor procs + + if (force->newton_pair) comm->reverse_comm_fix(this); + + // remove double counting of per-atom energy + + for (i = 0; i < nlocal; i++) energy[i] *= 0.5; + + // for EAM, include embedding function contribution to energy + + if (eamstyle) { + int *type = atom->type; + double fptmp,etmp; + + for (i = 0; i < nlocal; i++) { + force->pair->single_embed(i,type[i],fptmp,1,etmp); + energy[i] += etmp; + } + } +} + +/* ---------------------------------------------------------------------- */ + +int FixEnergy::pack_reverse_comm(int n, int first, double *buf) +{ + int i,m,last; + + m = 0; + last = first + n; + for (i = first; i < last; i++) buf[m++] = energy[i]; + return 1; +} + +/* ---------------------------------------------------------------------- */ + +void FixEnergy::unpack_reverse_comm(int n, int *list, double *buf) +{ + int i,j,m; + + m = 0; + for (i = 0; i < n; i++) { + j = list[i]; + energy[j] += buf[m++]; + } +} + +/* ---------------------------------------------------------------------- + memory usage of local atom-based array +------------------------------------------------------------------------- */ + +int FixEnergy::memory_usage() +{ + int bytes = nmax * sizeof(double); + return bytes; +} diff --git a/src/fix_energy.h b/src/fix_energy.h new file mode 100644 index 0000000000000000000000000000000000000000..4882f1af211e96df0b16fb92748d5cde04779a1c --- /dev/null +++ b/src/fix_energy.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_ENERGY_H +#define FIX_ENERGY_H + +#include "fix.h" + +class FixEnergy : public Fix { + friend class DumpCustom; + + public: + FixEnergy(int, char **); + ~FixEnergy(); + int setmask(); + void init(); + void dump(); + int pack_reverse_comm(int, int, double *); + void unpack_reverse_comm(int, int *, double *); + int memory_usage(); + + private: + int nmax,eamstyle; + double *energy; +}; + +#endif diff --git a/src/fix_enforce2d.cpp b/src/fix_enforce2d.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e86787a6105f8ebe41b488d788fe197c10eeb26d --- /dev/null +++ b/src/fix_enforce2d.cpp @@ -0,0 +1,111 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "fix_enforce2d.h" +#include "atom.h" +#include "update.h" +#include "respa.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixEnforce2D::FixEnforce2D(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 3) error->all("Illegal fix enforce2d command"); +} + +/* ---------------------------------------------------------------------- */ + +int FixEnforce2D::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + mask |= POST_FORCE_RESPA; + mask |= MIN_POST_FORCE; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixEnforce2D::init() +{ + granular = atom->check_style("granular"); +} + +/* ---------------------------------------------------------------------- */ + +void FixEnforce2D::setup() +{ + if (strcmp(update->integrate_style,"verlet") == 0) + post_force(1); + else { + int nlevels_respa = ((Respa *) update->integrate)->nlevels; + for (int ilevel = 0; ilevel < nlevels_respa; ilevel++) { + ((Respa *) update->integrate)->copy_flevel_f(ilevel); + post_force_respa(1,ilevel,0); + ((Respa *) update->integrate)->copy_f_flevel(ilevel); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixEnforce2D::min_setup() +{ + post_force(1); +} + +/* ---------------------------------------------------------------------- */ + +void FixEnforce2D::post_force(int vflag) +{ + double **v = atom->v; + double **f = atom->f; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + v[i][2] = 0.0; + f[i][2] = 0.0; + } + + // for granular systems, zero xy rotational componenets + + if (granular) { + double **phiv = atom->phiv; + double **phia = atom->phia; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + phiv[i][0] = 0.0; + phiv[i][1] = 0.0; + phia[i][0] = 0.0; + phia[i][1] = 0.0; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixEnforce2D::post_force_respa(int vflag, int ilevel, int iloop) +{ + post_force(vflag); +} + +/* ---------------------------------------------------------------------- */ + +void FixEnforce2D::min_post_force(int vflag) +{ + post_force(vflag); +} diff --git a/src/fix_enforce2d.h b/src/fix_enforce2d.h new file mode 100644 index 0000000000000000000000000000000000000000..1379a540d75a8b0f3b5941a146aa417237a42a4d --- /dev/null +++ b/src/fix_enforce2d.h @@ -0,0 +1,35 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_ENFORCE2D_H +#define FIX_ENFORCE2D_H + +#include "fix.h" + +class FixEnforce2D : public Fix { + public: + FixEnforce2D(int, char **); + ~FixEnforce2D() {} + int setmask(); + void init(); + void setup(); + void min_setup(); + void post_force(int); + void post_force_respa(int, int, int); + void min_post_force(int); + + private: + int granular; +}; + +#endif diff --git a/src/fix_gravity.cpp b/src/fix_gravity.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b0b4c0f8a5a899858bb80bdca38fa60ff60f9ddc --- /dev/null +++ b/src/fix_gravity.cpp @@ -0,0 +1,146 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "fix_gravity.h" +#include "atom.h" +#include "update.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixGravity::FixGravity(int narg, char **arg) : Fix(narg, arg) +{ + if (narg < 4) error->all("Illegal fix gravity command"); + + if (strcmp(arg[3],"chute") == 0) { + if (narg != 5) error->all("Illegal fix gravity command"); + if (atom->check_style("granular") == 0) + error->all("Must use fix gravity chute with atom style granular"); + dynamic = 0; + granular = 1; + phi = 0.0; + theta = 180.0 - atof(arg[4]); + } else if (strcmp(arg[3],"spherical") == 0) { + if (narg != 6) error->all("Illegal fix gravity command"); + if (atom->check_style("granular") == 0) + error->all("Must use fix gravity spherical with atom style granular"); + dynamic = 0; + granular = 1; + phi = atof(arg[4]); + theta = atof(arg[5]); + } else if (strcmp(arg[3],"gradient") == 0) { + if (narg != 8) error->all("Illegal fix gravity command"); + if (atom->check_style("granular") == 0) + error->all("Must use fix gravity gradient with atom style granular"); + dynamic = 1; + granular = 1; + phi = atof(arg[4]); + theta = atof(arg[5]); + phigrad = atof(arg[6]); + thetagrad = atof(arg[7]); + } else if (strcmp(arg[3],"vector") == 0) { + if (narg != 8) error->all("Illegal fix gravity command"); + if (atom->check_style("granular") != 0) + error->all("Cannot use fix gravity vector with atom style granular"); + dynamic = 0; + granular = 0; + magnitude = atof(arg[4]); + xdir = atof(arg[5]); + ydir = atof(arg[6]); + zdir = atof(arg[7]); + } else error->all("Illegal fix gravity command"); + + double PI = 2.0 * asin(1.0); + degree2rad = 2.0*PI / 360.0; + time_initial = update->ntimestep; +} + +/* ---------------------------------------------------------------------- */ + +int FixGravity::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixGravity::init() +{ + dt = update->dt; + + if (granular) { + xgrav = sin(degree2rad * theta) * cos(degree2rad * phi); + ygrav = sin(degree2rad * theta) * sin(degree2rad * phi); + zgrav = cos(degree2rad * theta); + } else { + double length = sqrt(xdir*xdir + ydir*ydir + zdir*zdir); + xgrav = magnitude * xdir/length; + ygrav = magnitude * ydir/length; + zgrav = magnitude * zdir/length; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixGravity::setup() +{ + post_force(1); +} + +/* ---------------------------------------------------------------------- */ + +void FixGravity::post_force(int vflag) +{ + // update direction of gravity vector if dynamic + + if (dynamic) { + double phi_current = degree2rad * + (phi + (update->ntimestep-time_initial)*dt*phigrad*360.0); + double theta_current = degree2rad * + (theta + (update->ntimestep-time_initial)*dt*thetagrad*360.0); + xgrav = sin(theta_current) * cos(phi_current); + ygrav = sin(theta_current) * sin(phi_current); + zgrav = cos(theta_current); + } + + double **f = atom->f; + double *rmass = atom->rmass; + double *mass = atom->mass; + int *mask = atom->mask; + int *type = atom->type; + int nlocal = atom->nlocal; + double massone; + + if (granular) { + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + f[i][0] += rmass[i]*xgrav; + f[i][1] += rmass[i]*ygrav; + f[i][2] += rmass[i]*zgrav; + } + } else { + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + massone = mass[type[i]]; + f[i][0] += massone*xgrav; + f[i][1] += massone*ygrav; + f[i][2] += massone*zgrav; + } + } +} diff --git a/src/fix_gravity.h b/src/fix_gravity.h new file mode 100644 index 0000000000000000000000000000000000000000..1f597415fd5fd7e0f32b8eee0b55a1cd51fa4c2a --- /dev/null +++ b/src/fix_gravity.h @@ -0,0 +1,40 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_GRAVITY_H +#define FIX_GRAVITY_H + +#include "fix.h" + +class FixGravity : public Fix { + friend class FixInsert; + + public: + FixGravity(int, char **); + ~FixGravity() {} + int setmask(); + void init(); + void setup(); + void post_force(int); + + private: + double phi,theta,phigrad,thetagrad; + int dynamic,time_initial; + int granular; // 0 if non-granular, 1 if granular + double magnitude,xdir,ydir,zdir; + double dt; + double xgrav,ygrav,zgrav; + double degree2rad; +}; + +#endif diff --git a/src/fix_gyration.cpp b/src/fix_gyration.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7d50a7ba018b5b39102d30718f45a6e2f3082421 --- /dev/null +++ b/src/fix_gyration.cpp @@ -0,0 +1,87 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "fix_gyration.h" +#include "atom.h" +#include "update.h" +#include "domain.h" +#include "group.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixGyration::FixGyration(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 5) error->all("Illegal fix gyration command"); + nevery = atoi(arg[3]); + if (nevery <= 0) error->all("Illegal fix gyration command"); + first = 1; + + MPI_Comm_rank(world,&me); + if (me == 0) { + fp = fopen(arg[4],"w"); + if (fp == NULL) { + char str[128]; + sprintf(str,"Cannot open fix gyration file %s",arg[4]); + error->one(str); + } + } + + if (me == 0) { + fprintf(fp,"Radius-of-gyration for group %s\n",group->names[igroup]); + fprintf(fp,"TimeStep Rg\n"); + } +} + +/* ---------------------------------------------------------------------- */ + +FixGyration::~FixGyration() +{ + if (me == 0) fclose(fp); +} + +/* ---------------------------------------------------------------------- */ + +int FixGyration::setmask() +{ + int mask = 0; + mask |= END_OF_STEP; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixGyration::init() +{ + masstotal = group->mass(igroup); +} + +/* ---------------------------------------------------------------------- */ + +void FixGyration::setup() +{ + if (first) end_of_step(); + first = 0; +} + +/* ---------------------------------------------------------------------- */ + +void FixGyration::end_of_step() +{ + double xcm[3]; + group->xcm(igroup,masstotal,xcm); + double rg = group->gyration(igroup,masstotal,xcm); + + if (me == 0) fprintf(fp,"%d %g\n",update->ntimestep,rg); +} diff --git a/src/fix_gyration.h b/src/fix_gyration.h new file mode 100644 index 0000000000000000000000000000000000000000..77d54cf2cd3fc7fbdb3ea14a0a5d963c372c76f1 --- /dev/null +++ b/src/fix_gyration.h @@ -0,0 +1,35 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_GYRATION_H +#define FIX_GYRATION_H + +#include "stdio.h" +#include "fix.h" + +class FixGyration : public Fix { + public: + FixGyration(int, char **); + ~FixGyration(); + int setmask(); + void init(); + void setup(); + void end_of_step(); + + private: + int me,first; + FILE *fp; + double masstotal; +}; + +#endif diff --git a/src/fix_indent.cpp b/src/fix_indent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ed8f9d78daaf36c8457eef8936dc927da782cf5f --- /dev/null +++ b/src/fix_indent.cpp @@ -0,0 +1,339 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Ravi Agrawal (Northwestern U) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "string.h" +#include "stdlib.h" +#include "fix_indent.h" +#include "atom.h" +#include "domain.h" +#include "update.h" +#include "output.h" +#include "respa.h" +#include "error.h" + +#define NONE 0 +#define SPHERE 1 +#define CYLINDER 2 + +/* ---------------------------------------------------------------------- */ + +FixIndent::FixIndent(int narg, char **arg) : Fix(narg, arg) +{ + if (narg < 4) error->all("Illegal fix indent command"); + k = atof(arg[3]); + + // set input line defaults + + istyle = NONE; + vx = vy = vz = 0.0; + scaleflag = 1; + radflag = 0; + r0_start = 0.0; + + // read options from end of input line + + options(narg-4,&arg[4]); + + // setup scaling + + if (scaleflag && strcmp(domain->lattice_style,"none") == 0) + error->all("Use of fix indent with undefined lattice"); + + double xscale,yscale,zscale; + if (scaleflag) { + xscale = domain->xlattice; + yscale = domain->ylattice; + zscale = domain->zlattice; + } + else xscale = yscale = zscale = 1.0; + + // apply scaling to indenter force constant, geometry, and velocity + + k /= xscale; + k3 = k/3.0; + vx *= xscale; + vy *= yscale; + vz *= zscale; + + if (istyle == SPHERE) { + x0 *= xscale; + y0 *= yscale; + z0 *= zscale; + r0_stop *= xscale; + r0_start *= xscale; + } else if (istyle == CYLINDER) { + if (cdim == 0) { + c1 *= yscale; + c2 *= zscale; + r0_stop *= xscale; + r0_start *= xscale; + } else if (cdim == 1) { + c1 *= xscale; + c2 *= zscale; + r0_stop *= yscale; + r0_start *= yscale; + } else if (cdim == 2) { + c1 *= xscale; + c2 *= yscale; + r0_stop *= zscale; + r0_start *= zscale; + } + } else error->all("Illegal fix indent command"); + + // time 0 for the indenter movement + + ntimestep_initial = update->ntimestep; +} + +/* ---------------------------------------------------------------------- */ + +int FixIndent::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + mask |= THERMO; + mask |= POST_FORCE_RESPA; + mask |= MIN_POST_FORCE; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixIndent::init() +{ + if (strcmp(update->integrate_style,"respa") == 0) + nlevels_respa = ((Respa *) update->integrate)->nlevels; + + if (thermo_print || thermo_energy) thermo_flag = 1; + else thermo_flag = 0; +} + +/* ---------------------------------------------------------------------- */ + +void FixIndent::setup() +{ + eflag_on = 1; + if (strcmp(update->integrate_style,"verlet") == 0) + post_force(1); + else { + ((Respa *) update->integrate)->copy_flevel_f(nlevels_respa-1); + post_force_respa(1,nlevels_respa-1,0); + ((Respa *) update->integrate)->copy_f_flevel(nlevels_respa-1); + } + eflag_on = 0; +} + +/* ---------------------------------------------------------------------- */ + +void FixIndent::min_setup() +{ + eflag_on = 1; + post_force(1); +} + +/* ---------------------------------------------------------------------- */ + +void FixIndent::post_force(int vflag) +{ + bool eflag = false; + if (thermo_flag) { + if (eflag_on) eflag = true; + else if (output->next_thermo == update->ntimestep) eflag = true; + } + if (eflag) eng = 0.0; + + // set current r0 + // for minimization, always set to r0_stop + + double r0; + if (!radflag || update->whichflag) r0 = r0_stop; + else { + double delta = update->ntimestep - update->beginstep; + delta /= update->endstep - update->beginstep; + r0 = r0_start + delta * (r0_stop-r0_start); + } + + // spherical indenter + + if (istyle == SPHERE) { + + // x1,y1,z1 = current position of indenter from original x0,y0,z0 + + double delta = (update->ntimestep - ntimestep_initial) * update->dt; + double x1 = x0 + delta*vx; + double y1 = y0 + delta*vy; + double z1 = z0 + delta*vz; + + double **x = atom->x; + double **f = atom->f; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double delx,dely,delz,r,dr,fmag; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + delx = x[i][0] - x1; + dely = x[i][1] - y1; + delz = x[i][2] - z1; + r = sqrt(delx*delx + dely*dely + delz*delz); + dr = r - r0; + if (dr >= 0.0) continue; + fmag = k*dr*dr; + f[i][0] += delx*fmag/r; + f[i][1] += dely*fmag/r; + f[i][2] += delz*fmag/r; + if (eflag) eng -= k3 * dr*dr*dr; + } + + // cylindrical indenter + + } else { + + // c1new,c2new = current coords of indenter axis from original c1,c2 + + double delta = (update->ntimestep - ntimestep_initial) * update->dt; + double c1new,c2new; + if (cdim == 0) { + c1new = c1 + delta*vy; + c2new = c2 + delta*vz; + } else if (cdim == 1) { + c1new = c1 + delta*vx; + c2new = c2 + delta*vz; + } else { + c1new = c1 + delta*vx; + c2new = c2 + delta*vy; + } + + double **x = atom->x; + double **f = atom->f; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double delx,dely,delz,r,dr,fmag; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + if (cdim == 0) { + delx = 0; + dely = x[i][1] - c1new; + delz = x[i][2] - c2new; + } else if (cdim == 1) { + delx = x[i][0] - c1new; + dely = 0; + delz = x[i][2] - c2new; + } else { + delx = x[i][0] - c1new; + dely = x[i][1] - c2new; + delz = 0; + } + r = sqrt(delx*delx + dely*dely + delz*delz); + dr = r - r0; + if (dr >= 0.0) continue; + fmag = k*dr*dr; + f[i][0] += delx*fmag/r; + f[i][1] += dely*fmag/r; + f[i][2] += delz*fmag/r; + if (eflag) eng -= k3 * dr*dr*dr; + } + } + + if (eflag) MPI_Allreduce(&eng,&etotal,1,MPI_DOUBLE,MPI_SUM,world); +} + +/* ---------------------------------------------------------------------- */ + +void FixIndent::post_force_respa(int vflag, int ilevel, int iloop) +{ + if (ilevel == nlevels_respa-1) post_force(vflag); +} + +/* ---------------------------------------------------------------------- */ + +void FixIndent::min_post_force(int vflag) +{ + post_force(vflag); +} + +/* ---------------------------------------------------------------------- */ + +int FixIndent::thermo_fields(int n, int *flags, char **keywords) +{ + if (n == 0) return 1; + flags[0] = 3; + strcpy(keywords[0],"Indent"); + return 1; +} + +/* ---------------------------------------------------------------------- */ + +int FixIndent::thermo_compute(double *values) +{ + values[0] = etotal; + return 1; +} + +/* ---------------------------------------------------------------------- + parse optional parameters at end of fix indent input line +------------------------------------------------------------------------- */ + +void FixIndent::options(int narg, char **arg) +{ + if (narg < 0) error->all("Illegal fix indent command"); + + int iarg = 0; + while (iarg < narg) { + if (strcmp(arg[iarg],"sphere") == 0) { + if (iarg+5 > narg) error->all("Illegal fix indent command"); + x0 = atof(arg[iarg+1]); + y0 = atof(arg[iarg+2]); + z0 = atof(arg[iarg+3]); + r0_stop = atof(arg[iarg+4]); + istyle = SPHERE; + iarg += 5; + } else if (strcmp(arg[iarg],"cylinder") == 0) { + if (iarg+5 > narg) error->all("Illegal fix indent command"); + if (strcmp(arg[iarg+1],"x") == 0) cdim = 0; + else if (strcmp(arg[iarg+1],"y") == 0) cdim = 1; + else if (strcmp(arg[iarg+1],"z") == 0) cdim = 2; + else error->all("Illegal fix indent command"); + c1 = atof(arg[iarg+2]); + c2 = atof(arg[iarg+3]); + r0_stop = atof(arg[iarg+4]); + istyle = CYLINDER; + iarg += 5; + } else if (strcmp(arg[iarg],"vel") == 0) { + if (iarg+4 > narg) error->all("Illegal fix indent command"); + vx = atof(arg[iarg+1]); + vy = atof(arg[iarg+2]); + vz = atof(arg[iarg+3]); + iarg += 4; + } else if (strcmp(arg[iarg],"rstart") == 0) { + if (iarg+2 > narg) error->all("Illegal fix indent command"); + radflag = 1; + r0_start = atof(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"units") == 0) { + if (iarg+2 > narg) error->all("Illegal fix indent command"); + if (strcmp(arg[iarg+1],"box") == 0) scaleflag = 0; + else if (strcmp(arg[iarg+1],"lattice") == 0) scaleflag = 1; + else error->all("Illegal fix indent command"); + iarg += 2; + } else error->all("Illegal fix indent command"); + } +} diff --git a/src/fix_indent.h b/src/fix_indent.h new file mode 100644 index 0000000000000000000000000000000000000000..73f5f1ca1aa89d32877e7cb571bce6a9bcaafa74 --- /dev/null +++ b/src/fix_indent.h @@ -0,0 +1,45 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_INDENT_H +#define FIX_INDENT_H + +#include "fix.h" + +class FixIndent : public Fix { + public: + FixIndent(int, char **); + ~FixIndent() {} + int setmask(); + void init(); + void setup(); + void min_setup(); + void post_force(int); + void post_force_respa(int, int, int); + void min_post_force(int); + int thermo_fields(int, int *, char **); + int thermo_compute(double *); + + private: + int ntimestep_initial,istyle,scaleflag,radflag,thermo_flag,eflag_on; + double k,k3,eng,etotal; + double x0,y0,z0,r0_stop,r0_start; + int cdim; + double c1,c2; + double vx,vy,vz; + int nlevels_respa; + + void options(int, char **); +}; + +#endif diff --git a/src/fix_langevin.cpp b/src/fix_langevin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f5cdb96e0ddcab67fbbd6d8bca9b866fd4c01a2f --- /dev/null +++ b/src/fix_langevin.cpp @@ -0,0 +1,196 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "math.h" +#include "string.h" +#include "stdlib.h" +#include "fix_langevin.h" +#include "atom.h" +#include "force.h" +#include "update.h" +#include "domain.h" +#include "region.h" +#include "respa.h" +#include "comm.h" +#include "random_mars.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixLangevin::FixLangevin(int narg, char **arg) : Fix(narg, arg) +{ + if (narg < 7) error->all("Illegal fix langevin command"); + + t_start = atof(arg[3]); + t_stop = atof(arg[4]); + t_period = atof(arg[5]); + int seed = atoi(arg[6]); + + if (t_period <= 0.0) error->all("Fix langevin period must be > 0.0"); + if (seed <= 0 || seed > 900000000) + error->all("Illegal fix langevin command"); + + // initialize Marsaglia RNG with processor-unique seed + + random = new RanMars(seed + comm->me); + + // allocate per-type arrays for force prefactors + + gfactor1 = new double[atom->ntypes+1]; + gfactor2 = new double[atom->ntypes+1]; + ratio = new double[atom->ntypes+1]; + + // optional args + + flagx = flagy = flagz = 1; + for (int i = 1; i <= atom->ntypes; i++) ratio[i] = 1.0; + iregion = -1; + + int iarg = 7; + while (iarg < narg) { + if (strcmp(arg[iarg],"axes") == 0) { + if (iarg+4 > narg) error->all("Illegal fix langevin command"); + flagx = atoi(arg[iarg+1]); + flagy = atoi(arg[iarg+2]); + flagz = atoi(arg[iarg+3]); + iarg += 4; + } else if (strcmp(arg[iarg],"scale") == 0) { + if (iarg+3 > narg) error->all("Illegal fix langevin command"); + int itype = atoi(arg[iarg+1]); + double scale = atof(arg[iarg+2]); + if (itype <= 0 || itype > atom->ntypes) + error->all("Illegal fix langevin command"); + ratio[itype] = scale; + iarg += 3; + } else if (strcmp(arg[iarg],"region") == 0) { + for (iregion = 0; iregion < domain->nregion; iregion++) + if (strcmp(arg[iarg+1],domain->regions[iregion]->id) == 0) break; + if (iregion == domain->nregion) + error->all("Fix langevin region ID does not exist"); + iarg += 2; + } else error->all("Illegal fix langevin command"); + } +} + +/* ---------------------------------------------------------------------- */ + +FixLangevin::~FixLangevin() +{ + delete random; + delete [] gfactor1; + delete [] gfactor2; + delete [] ratio; +} + +/* ---------------------------------------------------------------------- */ + +int FixLangevin::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + mask |= POST_FORCE_RESPA; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixLangevin::init() +{ + // set force prefactors + + for (int i = 1; i <= atom->ntypes; i++) { + gfactor1[i] = - atom->mass[i] / t_period / force->ftm2v; + gfactor2[i] = sqrt(atom->mass[i]) * + sqrt(24.0*force->boltz/t_period/update->dt/force->mvv2e) / force->ftm2v; + gfactor1[i] *= 1.0/ratio[i]; + gfactor2[i] *= 1.0/sqrt(ratio[i]); + } + + if (strcmp(update->integrate_style,"respa") == 0) + nlevels_respa = ((Respa *) update->integrate)->nlevels; +} + +/* ---------------------------------------------------------------------- */ + +void FixLangevin::setup() +{ + if (strcmp(update->integrate_style,"verlet") == 0) + post_force(1); + else { + ((Respa *) update->integrate)->copy_flevel_f(nlevels_respa-1); + post_force_respa(1,nlevels_respa-1,0); + ((Respa *) update->integrate)->copy_f_flevel(nlevels_respa-1); + } +} + +/* ---------------------------------------------------------------------- */ + +void FixLangevin::post_force(int vflag) +{ + double **x = atom->x; + double **v = atom->v; + double **f = atom->f; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double delta = update->ntimestep - update->beginstep; + delta /= update->endstep - update->beginstep; + double t_target = t_start + delta * (t_stop-t_start); + double tsqrt = sqrt(t_target); + + double gamma1,gamma2; + + // apply damping and thermostat to all atoms in fix group + + if (iregion == -1) { + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + gamma1 = gfactor1[type[i]]; + gamma2 = gfactor2[type[i]] * tsqrt; + if (flagx) f[i][0] += gamma1*v[i][0] + gamma2*(random->uniform()-0.5); + if (flagy) f[i][1] += gamma1*v[i][1] + gamma2*(random->uniform()-0.5); + if (flagz) f[i][2] += gamma1*v[i][2] + gamma2*(random->uniform()-0.5); + } + } + + // apply damping and thermostat to all atoms in fix group and in region + + } else { + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit && + domain->regions[iregion]->match(x[i][0],x[i][1],x[i][2])) { + gamma1 = gfactor1[type[i]]; + gamma2 = gfactor2[type[i]] * tsqrt; + if (flagx) f[i][0] += gamma1*v[i][0] + gamma2*(random->uniform()-0.5); + if (flagy) f[i][1] += gamma1*v[i][1] + gamma2*(random->uniform()-0.5); + if (flagz) f[i][2] += gamma1*v[i][2] + gamma2*(random->uniform()-0.5); + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixLangevin::post_force_respa(int vflag, int ilevel, int iloop) +{ + if (ilevel == nlevels_respa-1) post_force(vflag); +} + +/* ---------------------------------------------------------------------- */ + +void FixLangevin::reset_target(double t_new) +{ + t_start = t_stop = t_new; +} diff --git a/src/fix_langevin.h b/src/fix_langevin.h new file mode 100644 index 0000000000000000000000000000000000000000..44eee526a1d8c55d2d5c3992066b7ebf08ed5730 --- /dev/null +++ b/src/fix_langevin.h @@ -0,0 +1,41 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_LANGEVIN_H +#define FIX_LANGEVIN_H + +#include "fix.h" + +class RanMars; + +class FixLangevin : public Fix { + public: + FixLangevin(int, char **); + ~FixLangevin(); + int setmask(); + void init(); + void setup(); + void post_force(int); + void post_force_respa(int, int, int); + void reset_target(double); + + private: + double t_start,t_stop,t_period; + int flagx,flagy,flagz,iregion; + double *gfactor1,*gfactor2,*ratio; + + int nlevels_respa; + RanMars *random; +}; + +#endif diff --git a/src/fix_line_force.cpp b/src/fix_line_force.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e20d16a99f3997c6adb2fd4961e071222e16cb6a --- /dev/null +++ b/src/fix_line_force.cpp @@ -0,0 +1,98 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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_line_force.h" +#include "atom.h" +#include "update.h" +#include "respa.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixLineForce::FixLineForce(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 6) error->all("Illegal fix lineforce command"); + xdir = atof(arg[3]); + ydir = atof(arg[4]); + zdir = atof(arg[5]); +} + +/* ---------------------------------------------------------------------- */ + +int FixLineForce::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + mask |= POST_FORCE_RESPA; + mask |= MIN_POST_FORCE; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixLineForce::setup() +{ + if (strcmp(update->integrate_style,"verlet") == 0) + post_force(1); + else { + int nlevels_respa = ((Respa *) update->integrate)->nlevels; + for (int ilevel = 0; ilevel < nlevels_respa; ilevel++) { + ((Respa *) update->integrate)->copy_flevel_f(ilevel); + post_force_respa(1,ilevel,0); + ((Respa *) update->integrate)->copy_f_flevel(ilevel); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixLineForce::min_setup() +{ + post_force(1); +} + +/* ---------------------------------------------------------------------- */ + +void FixLineForce::post_force(int vflag) +{ + double **f = atom->f; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double dot; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + dot = f[i][0]*xdir + f[i][1]*ydir + f[i][2]*zdir; + f[i][0] = dot * xdir; + f[i][1] = dot * ydir; + f[i][2] = dot * zdir; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixLineForce::post_force_respa(int vflag, int ilevel, int iloop) +{ + post_force(vflag); +} + +/* ---------------------------------------------------------------------- */ + +void FixLineForce::min_post_force(int vflag) +{ + post_force(vflag); +} + + diff --git a/src/fix_line_force.h b/src/fix_line_force.h new file mode 100644 index 0000000000000000000000000000000000000000..47aa393695232994c38030654f12a4ad6612ffb0 --- /dev/null +++ b/src/fix_line_force.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_LINE_FORCE_H +#define FIX_LINE_FORCE_H + +#include "fix.h" + +class FixLineForce : public Fix { + public: + FixLineForce(int, char **); + ~FixLineForce() {} + int setmask(); + void setup(); + void min_setup(); + void post_force(int); + void post_force_respa(int, int, int); + void min_post_force(int); + + private: + double xdir,ydir,zdir; +}; + +#endif diff --git a/src/fix_minimize.cpp b/src/fix_minimize.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a2531b84c32067eacfff1e2c35498e113504a41e --- /dev/null +++ b/src/fix_minimize.cpp @@ -0,0 +1,118 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "fix_minimize.h" +#include "atom.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixMinimize::FixMinimize(int narg, char **arg) : Fix(narg, arg) +{ + // perform initial allocation of atom-based arrays + // register with atom class + + gradient = NULL; + searchdir = NULL; + grow_arrays(atom->nmax); + atom->add_callback(0); +} + +/* ---------------------------------------------------------------------- */ + +FixMinimize::~FixMinimize() +{ + // 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_2d_double_array(gradient); + memory->destroy_2d_double_array(searchdir); +} + +/* ---------------------------------------------------------------------- */ + +int FixMinimize::setmask() +{ + return 0; +} + +/* ---------------------------------------------------------------------- + memory usage of local atom-based arrays +------------------------------------------------------------------------- */ + +int FixMinimize::memory_usage() +{ + int bytes = 2 * atom->nmax*3 * sizeof(double); + return bytes; +} + +/* ---------------------------------------------------------------------- + allocate local atom-based arrays +------------------------------------------------------------------------- */ + +void FixMinimize::grow_arrays(int nmax) +{ + gradient = + memory->grow_2d_double_array(gradient,nmax,3,"fix_minimize:gradient"); + searchdir = + memory->grow_2d_double_array(searchdir,nmax,3,"fix_minimize:searchdir"); +} + +/* ---------------------------------------------------------------------- + copy values within local atom-based arrays +------------------------------------------------------------------------- */ + +void FixMinimize::copy_arrays(int i, int j) +{ + gradient[j][0] = gradient[i][0]; + gradient[j][1] = gradient[i][1]; + gradient[j][2] = gradient[i][2]; + searchdir[j][0] = searchdir[i][0]; + searchdir[j][1] = searchdir[i][1]; + searchdir[j][2] = searchdir[i][2]; +} + +/* ---------------------------------------------------------------------- + pack values in local atom-based arrays for exchange with another proc +------------------------------------------------------------------------- */ + +int FixMinimize::pack_exchange(int i, double *buf) +{ + buf[0] = gradient[i][0]; + buf[1] = gradient[i][1]; + buf[2] = gradient[i][2]; + buf[3] = searchdir[i][0]; + buf[4] = searchdir[i][1]; + buf[5] = searchdir[i][2]; + return 6; +} + +/* ---------------------------------------------------------------------- + unpack values in local atom-based arrays from exchange with another proc +------------------------------------------------------------------------- */ + +int FixMinimize::unpack_exchange(int nlocal, double *buf) +{ + gradient[nlocal][0] = buf[0]; + gradient[nlocal][1] = buf[1]; + gradient[nlocal][2] = buf[2]; + searchdir[nlocal][0] = buf[3]; + searchdir[nlocal][1] = buf[4]; + searchdir[nlocal][2] = buf[5]; + return 6; +} diff --git a/src/fix_minimize.h b/src/fix_minimize.h new file mode 100644 index 0000000000000000000000000000000000000000..c6adf3bd57e8c695fd56eb56648b2e3d5321e3ac --- /dev/null +++ b/src/fix_minimize.h @@ -0,0 +1,35 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_MINIMIZE_H +#define FIX_MINIMIZE_H + +#include "fix.h" + +class FixMinimize : public Fix { + public: + double **gradient,**searchdir; // gradient vectors + + FixMinimize(int, char **); + ~FixMinimize(); + int setmask(); + void init() {} + + int memory_usage(); + void grow_arrays(int); + void copy_arrays(int, int); + int pack_exchange(int, double *); + int unpack_exchange(int, double *); +}; + +#endif diff --git a/src/fix_momentum.cpp b/src/fix_momentum.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c3fc68f602f05534a3134a989f0b09fe94c6a86d --- /dev/null +++ b/src/fix_momentum.cpp @@ -0,0 +1,139 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "string.h" +#include "fix_momentum.h" +#include "atom.h" +#include "domain.h" +#include "group.h" +#include "error.h" + +/* ---------------------------------------------------------------------- + Contributing author: Naveen Michaud-Agrawal (Johns Hopkins U) +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- */ + +FixMomentum::FixMomentum(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 7) error->all("Illegal fix momentum command"); + nevery = atoi(arg[3]); + if (nevery <= 0) error->all("Illegal fix momentum command"); + + linear = angular = 0; + + int iarg = 4; + while (iarg < narg) { + if (strcmp(arg[iarg],"linear") == 0) { + if (iarg+4 > narg) error->all("Illegal fix momentum command"); + linear = 1; + xflag = atoi(arg[iarg+1]); + yflag = atoi(arg[iarg+2]); + zflag = atoi(arg[iarg+3]); + iarg += 4; + } else if (strcmp(arg[iarg],"angular") == 0) { + angular = 1; + iarg += 1; + } else error->all("Illegal fix momentum command"); + } + + if (linear == 0 && angular == 0) + error->all("Illegal run_style respa command"); + + if (linear) + if (xflag < 0 || xflag > 1 || yflag < 0 || yflag > 1 || + zflag < 0 || zflag > 1) error->all("Illegal fix momentum command"); + + // cannot have 0 atoms in group + + if (group->count(igroup) == 0.0) + error->all("Fix momentum group has no atoms"); +} + +/* ---------------------------------------------------------------------- */ + +int FixMomentum::setmask() +{ + int mask = 0; + mask |= END_OF_STEP; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixMomentum::init() +{ + masstotal = group->mass(igroup); +} + +/* ---------------------------------------------------------------------- */ + +void FixMomentum::end_of_step() +{ + if (linear) { + double vcm[3]; + group->vcm(igroup,masstotal,vcm); + + // adjust velocities by vcm to zero linear momentum + // only adjust a component if flag is set + + double **v = atom->v; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + if (xflag) v[i][0] -= vcm[0]; + if (yflag) v[i][1] -= vcm[1]; + if (zflag) v[i][2] -= vcm[2]; + } + } + + if (angular) { + double xcm[3],angmom[3],inertia[3][3],omega[3]; + group->xcm(igroup,masstotal,xcm); + group->angmom(igroup,xcm,angmom); + group->inertia(igroup,xcm,inertia); + group->omega(igroup,angmom,inertia,omega); + + // adjust velocities to zero omega + // vnew_i = v_i - w x r_i + // must use unwrapped coords to compute r_i correctly + + double **x = atom->x; + double **v = atom->v; + int *mask = atom->mask; + int *image = atom->image; + int nlocal = atom->nlocal; + + int xbox,ybox,zbox; + double dx,dy,dz; + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + dx = (x[i][0] + xbox*xprd) - xcm[0]; + dy = (x[i][1] + ybox*yprd) - xcm[1]; + dz = (x[i][2] + zbox*zprd) - xcm[2]; + v[i][0] -= omega[1]*dz - omega[2]*dy; + v[i][1] -= omega[2]*dx - omega[0]*dy; + v[i][2] -= omega[0]*dy - omega[1]*dx; + } + } +} diff --git a/src/fix_momentum.h b/src/fix_momentum.h new file mode 100644 index 0000000000000000000000000000000000000000..9664052965cf6b6b16c52c9ac18e4c289fcfc417 --- /dev/null +++ b/src/fix_momentum.h @@ -0,0 +1,33 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_MOMENTUM_H +#define FIX_MOMENTUM_H + +#include "fix.h" + +class FixMomentum : public Fix { + public: + FixMomentum(int, char **); + ~FixMomentum() {} + int setmask(); + void init(); + void end_of_step(); + + private: + int linear,angular; + int xflag,yflag,zflag; + double masstotal; +}; + +#endif diff --git a/src/fix_msd.cpp b/src/fix_msd.cpp new file mode 100644 index 0000000000000000000000000000000000000000..986729d5de2191daa1b69b512a9d2b2b25daef4d --- /dev/null +++ b/src/fix_msd.cpp @@ -0,0 +1,286 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "string.h" +#include "fix_msd.h" +#include "atom.h" +#include "update.h" +#include "domain.h" +#include "group.h" +#include "modify.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixMSD::FixMSD(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 5) error->all("Illegal fix msd command"); + nevery = atoi(arg[3]); + if (nevery <= 0) error->all("Illegal fix msd command"); + first = 1; + restart_peratom = 1; + + MPI_Comm_rank(world,&me); + if (me == 0) { + fp = fopen(arg[4],"w"); + if (fp == NULL) { + char str[128]; + sprintf(str,"Cannot open fix msd file %s",arg[4]); + error->one(str); + } + } + + if (me == 0) { + fprintf(fp,"Mean-squared Displacement for group %s\n", + group->names[igroup]); + fprintf(fp,"TimeStep x y z total\n"); + } + + // perform initial allocation of atom-based array + // register with atom class + + xoriginal = NULL; + grow_arrays(atom->nmax); + atom->add_callback(0); + atom->add_callback(1); + + // xoriginal = initial unwrapped positions of atoms + + double **x = atom->x; + int *mask = atom->mask; + int *image = atom->image; + int nlocal = atom->nlocal; + + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + int xbox,ybox,zbox; + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + xoriginal[i][0] = x[i][0] + xbox*xprd; + xoriginal[i][1] = x[i][1] + ybox*yprd; + xoriginal[i][2] = x[i][2] + zbox*zprd; + } else xoriginal[i][0] = xoriginal[i][1] = xoriginal[i][2] = 0.0; + } + + // nmsd = # of atoms in group + + nmsd = 0; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) nmsd++; + + int nmsd_all; + MPI_Allreduce(&nmsd,&nmsd_all,1,MPI_INT,MPI_SUM,world); + nmsd = nmsd_all; + + if (nmsd == 0) error->all("Fix msd group has no atoms"); +} + +/* ---------------------------------------------------------------------- */ + +FixMSD::~FixMSD() +{ + if (me == 0) fclose(fp); + + // if atom class still exists: + // unregister this fix so atom class doesn't invoke it any more + + if (atom) atom->delete_callback(id,0); + if (atom) atom->delete_callback(id,1); + + // delete locally stored array + + memory->destroy_2d_double_array(xoriginal); +} + +/* ---------------------------------------------------------------------- */ + +int FixMSD::setmask() +{ + int mask = 0; + mask |= END_OF_STEP; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixMSD::init() +{ + // warn if more than one msd fix + + int count = 0; + for (int i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"msd") == 0) count++; + if (count > 1 && me == 0) error->warning("More than one msd fix"); +} + +/* ---------------------------------------------------------------------- */ + +void FixMSD::setup() +{ + if (first) end_of_step(); + first = 0; +} + +/* ---------------------------------------------------------------------- */ + +void FixMSD::end_of_step() +{ + double **x = atom->x; + int *mask = atom->mask; + int *image = atom->image; + int nlocal = atom->nlocal; + + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + int xbox,ybox,zbox; + double dx,dy,dz; + double msd[4]; + msd[0] = msd[1] = msd[2] = msd[3] = 0.0; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + dx = x[i][0] + xbox*xprd - xoriginal[i][0]; + dy = x[i][1] + ybox*yprd - xoriginal[i][1]; + dz = x[i][2] + zbox*zprd - xoriginal[i][2]; + msd[0] += dx*dx; + msd[1] += dy*dy; + msd[2] += dz*dz; + msd[3] += dx*dx + dy*dy + dz*dz; + } + + double msd_all[4]; + MPI_Allreduce(msd,msd_all,4,MPI_DOUBLE,MPI_SUM,world); + msd_all[0] /= nmsd; + msd_all[1] /= nmsd; + msd_all[2] /= nmsd; + msd_all[3] /= nmsd; + + if (me == 0) fprintf(fp,"%d %g %g %g %g\n",update->ntimestep, + msd_all[0],msd_all[1],msd_all[2],msd_all[3]); +} + +/* ---------------------------------------------------------------------- + memory usage of local atom-based array +------------------------------------------------------------------------- */ + +int FixMSD::memory_usage() +{ + int bytes = atom->nmax*3 * sizeof(double); + return bytes; +} + +/* ---------------------------------------------------------------------- + allocate atom-based array +------------------------------------------------------------------------- */ + +void FixMSD::grow_arrays(int nmax) +{ + xoriginal = + memory->grow_2d_double_array(xoriginal,nmax,3,"fix_msd:xoriginal"); +} + +/* ---------------------------------------------------------------------- + copy values within local atom-based array +------------------------------------------------------------------------- */ + +void FixMSD::copy_arrays(int i, int j) +{ + xoriginal[j][0] = xoriginal[i][0]; + xoriginal[j][1] = xoriginal[i][1]; + xoriginal[j][2] = xoriginal[i][2]; +} + +/* ---------------------------------------------------------------------- + pack values in local atom-based array for exchange with another proc +------------------------------------------------------------------------- */ + +int FixMSD::pack_exchange(int i, double *buf) +{ + buf[0] = xoriginal[i][0]; + buf[1] = xoriginal[i][1]; + buf[2] = xoriginal[i][2]; + return 3; +} + +/* ---------------------------------------------------------------------- + unpack values in local atom-based array from exchange with another proc +------------------------------------------------------------------------- */ + +int FixMSD::unpack_exchange(int nlocal, double *buf) +{ + xoriginal[nlocal][0] = buf[0]; + xoriginal[nlocal][1] = buf[1]; + xoriginal[nlocal][2] = buf[2]; + return 3; +} + +/* ---------------------------------------------------------------------- + pack values in local atom-based arrays for restart file +------------------------------------------------------------------------- */ + +int FixMSD::pack_restart(int i, double *buf) +{ + buf[0] = 4; + buf[1] = xoriginal[i][0]; + buf[2] = xoriginal[i][1]; + buf[3] = xoriginal[i][2]; + return 4; +} + +/* ---------------------------------------------------------------------- + unpack values from atom->extra array to restart the fix +------------------------------------------------------------------------- */ + +void FixMSD::unpack_restart(int nlocal, int nth) +{ + double **extra = atom->extra; + + // skip to Nth set of extra values + + int m = 0; + for (int i = 0; i < nth; i++) m += static_cast<int> (extra[nlocal][m]); + m++; + + xoriginal[nlocal][0] = extra[nlocal][m++]; + xoriginal[nlocal][1] = extra[nlocal][m++]; + xoriginal[nlocal][2] = extra[nlocal][m++]; +} + +/* ---------------------------------------------------------------------- + maxsize of any atom's restart data +------------------------------------------------------------------------- */ + +int FixMSD::maxsize_restart() +{ + return 4; +} + +/* ---------------------------------------------------------------------- + size of atom nlocal's restart data +------------------------------------------------------------------------- */ + +int FixMSD::size_restart(int nlocal) +{ + return 4; +} diff --git a/src/fix_msd.h b/src/fix_msd.h new file mode 100644 index 0000000000000000000000000000000000000000..88b4345395192cbf61a32a3db4a0551ae70d572d --- /dev/null +++ b/src/fix_msd.h @@ -0,0 +1,46 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_MSD_H +#define FIX_MSD_H + +#include "stdio.h" +#include "fix.h" + +class FixMSD : public Fix { + public: + FixMSD(int, char **); + ~FixMSD(); + int setmask(); + void init(); + void setup(); + void end_of_step(); + + int memory_usage(); + void grow_arrays(int); + void copy_arrays(int, int); + int pack_exchange(int, double *); + int unpack_exchange(int, double *); + int pack_restart(int, double *); + void unpack_restart(int, int); + int size_restart(int); + int maxsize_restart(); + + private: + int me,first; + FILE *fp; + int nmsd; // # of atoms in group + double **xoriginal; // original coords of atoms +}; + +#endif diff --git a/src/fix_nph.cpp b/src/fix_nph.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f7a678a74b0585696e4ae705f0061a9044fab5c5 --- /dev/null +++ b/src/fix_nph.cpp @@ -0,0 +1,696 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Mark Stevens (SNL) +------------------------------------------------------------------------- */ + +#include "string.h" +#include "stdlib.h" +#include "math.h" +#include "fix_nph.h" +#include "atom.h" +#include "force.h" +#include "comm.h" +#include "output.h" +#include "modify.h" +#include "kspace.h" +#include "update.h" +#include "respa.h" +#include "temperature.h" +#include "pressure.h" +#include "domain.h" +#include "error.h" + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +/* ---------------------------------------------------------------------- */ + +FixNPH::FixNPH(int narg, char **arg) : Fix(narg, arg) +{ + if (narg < 4) error->all("Illegal fix nph command"); + + restart_global = 1; + + double p_period[3]; + if (strcmp(arg[3],"xyz") == 0) { + if (narg < 7) error->all("Illegal fix nph command"); + + press_couple = 0; + p_start[0] = p_start[1] = p_start[2] = atof(arg[4]); + p_stop[0] = p_stop[1] = p_stop[2] = atof(arg[5]); + p_period[0] = p_period[1] = p_period[2] = atof(arg[6]); + p_flag[0] = p_flag[1] = p_flag[2] = 1; + + } else { + if (strcmp(arg[3],"xy") == 0) press_couple = 1; + else if (strcmp(arg[3],"yz") == 0) press_couple = 2; + else if (strcmp(arg[3],"xz") == 0) press_couple = 3; + else if (strcmp(arg[3],"aniso") == 0) press_couple = 4; + else error->all("Illegal fix nph command"); + + if (narg < 11) error->all("Illegal fix nph command"); + + if (strcmp(arg[4],"NULL") == 0) { + p_start[0] = p_stop[0] = p_period[0] = 0.0; + p_flag[0] = 0; + } else { + p_start[0] = atof(arg[4]); + p_stop[0] = atof(arg[5]); + p_flag[0] = 1; + } + if (strcmp(arg[6],"NULL") == 0) { + p_start[1] = p_stop[1] = p_period[1] = 0.0; + p_flag[1] = 0; + } else { + p_start[1] = atof(arg[6]); + p_stop[1] = atof(arg[7]); + p_flag[1] = 1; + } + if (strcmp(arg[8],"NULL") == 0) { + p_start[2] = p_stop[2] = p_period[2] = 0.0; + p_flag[2] = 0; + } else { + p_start[2] = atof(arg[8]); + p_stop[2] = atof(arg[9]); + p_flag[2] = 1; + } + + double period = atof(arg[10]); + if (p_flag[0]) p_period[0] = period; + if (p_flag[1]) p_period[1] = period; + if (p_flag[2]) p_period[2] = period; + } + + // process extra keywords + + drag = 0.0; + dilate_partial = 0; + + int iarg; + if (press_couple == 0) iarg = 7; + else iarg = 11; + + while (iarg < narg) { + if (strcmp(arg[iarg],"drag") == 0) { + if (iarg+2 > narg) error->all("Illegal fix nph command"); + drag = atof(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"dilate") == 0) { + if (iarg+2 > narg) error->all("Illegal fix nph command"); + if (strcmp(arg[iarg+1],"all") == 0) dilate_partial = 0; + else if (strcmp(arg[iarg+1],"partial") == 0) dilate_partial = 1; + else error->all("Illegal fix nph command"); + iarg += 2; + } else error->all("Illegal fix nph command"); + } + + // check for periodicity in controlled dimensions + + if (p_flag[0] && domain->xperiodic == 0) + error->all("Cannot fix nph on a non-periodic dimension"); + if (p_flag[1] && domain->yperiodic == 0) + error->all("Cannot fix nph on a non-periodic dimension"); + if (p_flag[2] && domain->zperiodic == 0) + error->all("Cannot fix nph on a non-periodic dimension"); + + // create a new temperature full style with fix ID and group all + // pressure is always global (group all) and thus its + // KE/temperature contribution should use group all + + char **newarg = new char*[3]; + newarg[0] = id; + newarg[1] = "all"; + newarg[2] = "full"; + force->add_temp(3,newarg,1); + delete [] newarg; + + temperature = force->find_temp(id); + + // convert input periods to frequencies + + if ((p_flag[0] && p_period[0] <= 0.0) || + (p_flag[1] && p_period[1] <= 0.0) || (p_flag[2] && p_period[2] <= 0.0)) + error->all("Fix nph periods must be > 0.0"); + + p_freq[0] = p_freq[1] = p_freq[2] = 0.0; + if (p_flag[0]) p_freq[0] = 1.0 / p_period[0]; + if (p_flag[1]) p_freq[1] = 1.0 / p_period[1]; + if (p_flag[2]) p_freq[2] = 1.0 / p_period[2]; + + // pressure init + + pressure = force->pressure; + omega[0] = omega[1] = omega[2] = 0.0; + omega_dot[0] = omega_dot[1] = omega_dot[2] = 0.0; + + nrigid = 0; + rfix = NULL; +} + +/* ---------------------------------------------------------------------- */ + +FixNPH::~FixNPH() +{ + delete [] rfix; +} + +/* ---------------------------------------------------------------------- */ + +int FixNPH::setmask() +{ + int mask = 0; + mask |= INITIAL_INTEGRATE; + mask |= FINAL_INTEGRATE; + mask |= THERMO; + mask |= INITIAL_INTEGRATE_RESPA; + mask |= FINAL_INTEGRATE_RESPA; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixNPH::init() +{ + if (atom->mass_require == 0) + error->all("Cannot use fix nph with no per-type mass defined"); + + // set timesteps and frequencies + // Nkt = initial value for piston mass and energy conservation + // guesstimate a unit-dependent t_initial if actual T = 0.0 + + dtv = update->dt; + dtf = 0.5 * update->dt * force->ftm2v; + dthalf = 0.5 * update->dt; + + double freq = MAX(p_freq[0],p_freq[1]); + freq = MAX(freq,p_freq[2]); + drag_factor = 1.0 - (update->dt * freq * drag); + + boltz = force->boltz; + nktv2p = force->nktv2p; + vol0 = domain->xprd * domain->yprd * domain->zprd; + + double t_initial = temperature->compute(); + if (t_initial == 0.0) { + if (strcmp(update->unit_style,"lj") == 0) t_initial = 1.0; + else t_initial = 300.0; + } + nkt = atom->natoms * boltz * t_initial; + + double mass = 0.0; + for (int i = 0; i < atom->nlocal; i++) mass += atom->mass[atom->type[i]]; + MPI_Allreduce(&mass,&total_mass,1,MPI_DOUBLE,MPI_SUM,world); + + if (force->kspace) kspace_flag = 1; + else kspace_flag = 0; + + if (strcmp(update->integrate_style,"respa") == 0) { + nlevels_respa = ((Respa *) update->integrate)->nlevels; + step_respa = ((Respa *) update->integrate)->step; + } + + // detect if any fix rigid exist so rigid bodies move when box is dilated + // rfix[] = indices to each fix rigid + + delete [] rfix; + nrigid = 0; + rfix = NULL; + + for (int i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"rigid") == 0 || + strcmp(modify->fix[i]->style,"poems") == 0) nrigid++; + if (nrigid) { + rfix = new int[nrigid]; + nrigid = 0; + for (int i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"rigid") == 0 || + strcmp(modify->fix[i]->style,"poems") == 0) rfix[nrigid++] = i; + } +} + +/* ---------------------------------------------------------------------- + compute temp and press before integrator starts +------------------------------------------------------------------------- */ + +void FixNPH::setup() +{ + double t_current = temperature->compute(); + pressure->compute(temperature); + couple(); +} + +/* ---------------------------------------------------------------------- + 1st half of Verlet update +------------------------------------------------------------------------- */ + +void FixNPH::initial_integrate() +{ + int i; + + double delta = update->ntimestep - update->beginstep; + delta /= update->endstep - update->beginstep; + + // update omega_dot + // for non-varying dims, p_freq is 0.0, so omega doesn't change + + double f_omega; + double denskt = nkt / (domain->xprd*domain->yprd*domain->zprd) * nktv2p; + + for (i = 0; i < 3; i++) { + p_target[i] = p_start[i] + delta * (p_stop[i]-p_start[i]); + f_omega = p_freq[i]*p_freq[i] * (p_current[i]-p_target[i])/denskt; + omega_dot[i] += f_omega*dthalf; + omega_dot[i] *= drag_factor; + omega[i] += dtv*omega_dot[i]; + factor[i] = exp(-dthalf*omega_dot[i]); + dilation[i] = exp(dthalf*omega_dot[i]); + } + + // v update only for atoms in NPH group + + double **x = atom->x; + double **v = atom->v; + double **f = atom->f; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double dtfm; + for (i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]]; + v[i][0] = v[i][0]*factor[0] + dtfm*f[i][0]; + v[i][1] = v[i][1]*factor[1] + dtfm*f[i][1]; + v[i][2] = v[i][2]*factor[2] + dtfm*f[i][2]; + } + } + + // rescale simulation box and all owned atoms by 1/2 step + + box_dilate(0); + + // x update by full step only for atoms in NPH group + + for (i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + x[i][0] += dtv * v[i][0]; + x[i][1] += dtv * v[i][1]; + x[i][2] += dtv * v[i][2]; + } + } + + // rescale simulation box and all owned atoms by 1/2 step + // redo KSpace coeffs since volume has changed + + box_dilate(0); + if (kspace_flag) force->kspace->setup(); +} + +/* ---------------------------------------------------------------------- + 2nd half of Verlet update +------------------------------------------------------------------------- */ + +void FixNPH::final_integrate() +{ + int i; + + // v update only for atoms in NPH group + + double **v = atom->v; + double **f = atom->f; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double dtfm; + for (i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]]; + v[i][0] = (v[i][0] + dtfm*f[i][0]) * factor[0]; + v[i][1] = (v[i][1] + dtfm*f[i][1]) * factor[1]; + v[i][2] = (v[i][2] + dtfm*f[i][2]) * factor[2]; + } + } + + // compute new pressure + + double t_current = temperature->compute(); + pressure->compute(temperature); + couple(); + + // update omega_dot + // for non-varying dims, p_freq is 0.0, so omega_dot doesn't change + + double f_omega; + double denskt = nkt / (domain->xprd*domain->yprd*domain->zprd) * nktv2p; + + for (i = 0; i < 3; i++) { + f_omega = p_freq[i]*p_freq[i] * (p_current[i]-p_target[i])/denskt; + omega_dot[i] += f_omega*dthalf; + omega_dot[i] *= drag_factor; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixNPH::initial_integrate_respa(int ilevel, int flag) +{ + // if flag = 1, then is 2nd call at outermost level from rRESPA + // perform 2nd half of box dilate on own + ghost atoms and return + // redo KSpace coeffs since volume has changed + + if (flag == 1) { + box_dilate(1); + if (kspace_flag) force->kspace->setup(); + return; + } + + // set timesteps by level + + double dtfm; + dtv = step_respa[ilevel]; + dtf = 0.5 * step_respa[ilevel] * force->ftm2v; + dthalf = 0.5 * step_respa[ilevel]; + + // atom quantities + + double **x = atom->x; + double **v = atom->v; + double **f = atom->f; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + // outermost level - update omega_dot, apply to v, dilate box + // all other levels - NVE update of v + // x,v updates only performed for atoms in NPH group + + if (ilevel == nlevels_respa-1) { + + double delta = update->ntimestep - update->beginstep; + delta /= update->endstep - update->beginstep; + + // update omega_dot + // for non-varying dims, p_freq is 0.0, so omega doesn't change + + double f_omega; + double denskt = nkt / (domain->xprd*domain->yprd*domain->zprd) * nktv2p; + + for (int i = 0; i < 3; i++) { + p_target[i] = p_start[i] + delta * (p_stop[i]-p_start[i]); + f_omega = p_freq[i]*p_freq[i] * (p_current[i]-p_target[i])/denskt; + omega_dot[i] += f_omega*dthalf; + omega_dot[i] *= drag_factor; + omega[i] += dtv*omega_dot[i]; + factor[i] = exp(-dthalf*omega_dot[i]); + dilation[i] = exp(dthalf*omega_dot[i]); + } + + // v update only for atoms in NPH group + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]]; + v[i][0] = v[i][0]*factor[0] + dtfm*f[i][0]; + v[i][1] = v[i][1]*factor[1] + dtfm*f[i][1]; + v[i][2] = v[i][2]*factor[2] + dtfm*f[i][2]; + } + } + + // rescale simulation box and all owned atoms by 1/2 step + + box_dilate(0); + + } else { + + // v update only for atoms in NPH group + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]]; + v[i][0] += dtfm*f[i][0]; + v[i][1] += dtfm*f[i][1]; + v[i][2] += dtfm*f[i][2]; + } + } + } + + // innermost level - also update x only for atoms in NPH group + + if (ilevel == 0) { + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + x[i][0] += dtv * v[i][0]; + x[i][1] += dtv * v[i][1]; + x[i][2] += dtv * v[i][2]; + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixNPH::final_integrate_respa(int ilevel) +{ + double dtfm; + + // set timesteps by level + + dtf = 0.5 * step_respa[ilevel] * force->ftm2v; + dthalf = 0.5 * step_respa[ilevel]; + + // outermost level - update omega_dot, apply to v via final_integrate() + // all other levels - NVE update of v + // v update only performed for atoms in NPH group + + if (ilevel == nlevels_respa-1) final_integrate(); + else { + + // v update only for atoms in NPH group + + double **v = atom->v; + double **f = atom->f; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]]; + v[i][0] += dtfm*f[i][0]; + v[i][1] += dtfm*f[i][1]; + v[i][2] += dtfm*f[i][2]; + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixNPH::couple() +{ + double *p_tensor = pressure->p_tensor; + + if (press_couple == 0) + p_current[0] = p_current[1] = p_current[2] = pressure->p_total; + else if (press_couple == 1) { + double ave = 0.5 * (p_tensor[0] + p_tensor[1]); + p_current[0] = p_current[1] = ave; + p_current[2] = p_tensor[2]; + } else if (press_couple == 2) { + double ave = 0.5 * (p_tensor[1] + p_tensor[2]); + p_current[1] = p_current[2] = ave; + p_current[0] = p_tensor[0]; + } else if (press_couple == 3) { + double ave = 0.5 * (p_tensor[0] + p_tensor[2]); + p_current[0] = p_current[2] = ave; + p_current[1] = p_tensor[1]; + } if (press_couple == 4) { + p_current[0] = p_tensor[0]; + p_current[1] = p_tensor[1]; + p_current[2] = p_tensor[2]; + } +} + +/* ---------------------------------------------------------------------- + dilate the box around center of box +------------------------------------------------------------------------- */ + +void FixNPH::box_dilate(int flag) +{ + int i,n; + + // ctr = geometric center of box in a dimension + // scale owned or owned+ghost atoms depending on flag + // re-define simulation box via xprd/yprd/zprd + // scale atom coords for all atoms or only for fix group atoms + // if fix rigid exists, scale rigid body centers-of-mass + // don't do anything if non-periodic or press style is constant volume + + double **x = atom->x; + int *mask = atom->mask; + if (flag) n = atom->nlocal + atom->nghost; + else n = atom->nlocal; + + double oldlo,oldhi,ctr; + + if (domain->xperiodic && p_flag[0]) { + oldlo = domain->boxxlo; + oldhi = domain->boxxhi; + ctr = 0.5 * (oldlo + oldhi); + domain->boxxlo = (oldlo-ctr)*dilation[0] + ctr; + domain->boxxhi = (oldhi-ctr)*dilation[0] + ctr; + domain->xprd = domain->boxxhi - domain->boxxlo; + if (dilate_partial) { + for (i = 0; i < n; i++) + if (mask[i] & groupbit) + x[i][0] = ctr + (x[i][0]-ctr)*dilation[0]; + } else { + for (i = 0; i < n; i++) + x[i][0] = ctr + (x[i][0]-ctr)*dilation[0]; + } + if (nrigid) + for (i = 0; i < nrigid; i++) + modify->fix[rfix[i]]-> + dilate(0,oldlo,oldhi,domain->boxxlo,domain->boxxhi); + } + + if (domain->yperiodic && p_flag[1]) { + oldlo = domain->boxylo; + oldhi = domain->boxyhi; + ctr = 0.5 * (oldlo + oldhi); + domain->boxylo = (oldlo-ctr)*dilation[1] + ctr; + domain->boxyhi = (oldhi-ctr)*dilation[1] + ctr; + domain->yprd = domain->boxyhi - domain->boxylo; + if (dilate_partial) { + for (i = 0; i < n; i++) + if (mask[i] & groupbit) + x[i][1] = ctr + (x[i][1]-ctr)*dilation[1]; + } else { + for (i = 0; i < n; i++) + x[i][1] = ctr + (x[i][1]-ctr)*dilation[1]; + } + if (nrigid) + for (i = 0; i < nrigid; i++) + modify->fix[rfix[i]]-> + dilate(1,oldlo,oldhi,domain->boxylo,domain->boxyhi); + } + + if (domain->zperiodic && p_flag[2]) { + oldlo = domain->boxzlo; + oldhi = domain->boxzhi; + ctr = 0.5 * (oldlo + oldhi); + domain->boxzlo = (oldlo-ctr)*dilation[2] + ctr; + domain->boxzhi = (oldhi-ctr)*dilation[2] + ctr; + domain->zprd = domain->boxzhi - domain->boxzlo; + if (dilate_partial) { + for (i = 0; i < n; i++) + if (mask[i] & groupbit) + x[i][2] = ctr + (x[i][2]-ctr)*dilation[2]; + } else { + for (i = 0; i < n; i++) + x[i][2] = ctr + (x[i][2]-ctr)*dilation[2]; + } + if (nrigid) + for (i = 0; i < nrigid; i++) + modify->fix[rfix[i]]-> + dilate(2,oldlo,oldhi,domain->boxzlo,domain->boxzhi); + } +} + +/* ---------------------------------------------------------------------- + pack entire state of Fix into one write +------------------------------------------------------------------------- */ + +void FixNPH::write_restart(FILE *fp) +{ + int n = 0; + double list[6]; + list[n++] = omega[0]; + list[n++] = omega[1]; + list[n++] = omega[2]; + list[n++] = omega_dot[0]; + list[n++] = omega_dot[1]; + list[n++] = omega_dot[2]; + + if (comm->me == 0) { + int size = n * sizeof(double); + fwrite(&size,sizeof(int),1,fp); + fwrite(&list,sizeof(double),n,fp); + } +} + +/* ---------------------------------------------------------------------- + use state info from restart file to restart the Fix +------------------------------------------------------------------------- */ + +void FixNPH::restart(char *buf) +{ + int n = 0; + double *list = (double *) buf; + omega[0] = list[n++]; + omega[1] = list[n++]; + omega[2] = list[n++]; + omega_dot[0] = list[n++]; + omega_dot[1] = list[n++]; + omega_dot[2] = list[n++]; +} + +/* ---------------------------------------------------------------------- */ + +int FixNPH::modify_param(int narg, char **arg) +{ + if (strcmp(arg[0],"temp") == 0) { + if (narg < 2) error->all("Illegal fix_modify command"); + temperature = force->find_temp(arg[1]); + if (temperature == NULL) + error->all("Could not find fix_modify temperature ID"); + if (temperature->igroup != 0 && comm->me == 0) + error->warning("Temperature for NPH is not for group all"); + if (strcmp(temperature->style,"region") == 0 && comm->me == 0) + error->warning("Temperature for NPH is style region"); + return 2; + } + return 0; +} + +/* ---------------------------------------------------------------------- */ + +int FixNPH::thermo_fields(int n, int *flags, char **keywords) +{ + if (n == 0) return 1; + flags[0] = 3; + strcpy(keywords[0],"EngNPH"); + return 1; +} + +/* ---------------------------------------------------------------------- */ + +int FixNPH::thermo_compute(double *values) +{ + double volume = domain->xprd * domain->yprd * domain->zprd; + int pdim = p_flag[0] + p_flag[1] + p_flag[2]; + + values[0] = 0.0; + for (int i = 0; i < 3; i++) + if (p_freq[i] > 0.0) + values[0] += 0.5*nkt*omega_dot[i]*omega_dot[i] / + (p_freq[i]*p_freq[i]) + p_target[i]*(volume-vol0) / (pdim*nktv2p); + return 1; +} diff --git a/src/fix_nph.h b/src/fix_nph.h new file mode 100644 index 0000000000000000000000000000000000000000..5df3a17a7897ea709fe2cd93e7b9bebfac223f11 --- /dev/null +++ b/src/fix_nph.h @@ -0,0 +1,67 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_NPH_H +#define FIX_NPH_H + +#include "fix.h" +class Temperature; +class Pressure; + +class FixNPH : public Fix { + friend class Pressure; // accesses press_couple + + public: + FixNPH(int, char **); + ~FixNPH(); + int setmask(); + void init(); + void setup(); + void initial_integrate(); + void final_integrate(); + void initial_integrate_respa(int,int); + void final_integrate_respa(int); + void write_restart(FILE *); + void restart(char *); + int modify_param(int, char **); + int thermo_fields(int, int *, char **); + int thermo_compute(double *); + + private: + Temperature *temperature; + Pressure *pressure; + + double dtv,dtf,dthalf; + double boltz,nktv2p; + double total_mass,vol0,nkt; + + int press_couple,dilate_partial; + int p_flag[3]; // 1 if control P on this dim, 0 if not + double p_start[3],p_stop[3]; + double p_freq[3],p_target[3]; + double omega[3],omega_dot[3]; + double p_current[3],dilation[3]; + double drag,drag_factor; + double factor[3]; + int kspace_flag; // 1 if KSpace invoked, 0 if not + int nrigid; // number of rigid fixes + int *rfix; // indices of rigid fixes + + int nlevels_respa; + double *step_respa; + + void couple(); + void box_dilate(int); +}; + +#endif diff --git a/src/fix_npt.cpp b/src/fix_npt.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7d3ac83b65731594e2d05a57531c02ad4e15c31a --- /dev/null +++ b/src/fix_npt.cpp @@ -0,0 +1,729 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Mark Stevens (SNL) +------------------------------------------------------------------------- */ + +#include "string.h" +#include "stdlib.h" +#include "math.h" +#include "fix_npt.h" +#include "atom.h" +#include "force.h" +#include "comm.h" +#include "output.h" +#include "modify.h" +#include "kspace.h" +#include "update.h" +#include "respa.h" +#include "temperature.h" +#include "pressure.h" +#include "domain.h" +#include "error.h" + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +/* ---------------------------------------------------------------------- */ + +FixNPT::FixNPT(int narg, char **arg) : Fix(narg, arg) +{ + if (narg < 7) error->all("Illegal fix npt command"); + + restart_global = 1; + + t_start = atof(arg[3]); + t_stop = atof(arg[4]); + double t_period = atof(arg[5]); + + if (t_start < 0.0 || t_stop <= 0.0) + error->all("Target T for fix npt cannot be 0.0"); + + double p_period[3]; + if (strcmp(arg[6],"xyz") == 0) { + if (narg < 10) error->all("Illegal fix npt command"); + + press_couple = 0; + p_start[0] = p_start[1] = p_start[2] = atof(arg[7]); + p_stop[0] = p_stop[1] = p_stop[2] = atof(arg[8]); + p_period[0] = p_period[1] = p_period[2] = atof(arg[9]); + p_flag[0] = p_flag[1] = p_flag[2] = 1; + + } else { + if (strcmp(arg[6],"xy") == 0) press_couple = 1; + else if (strcmp(arg[6],"yz") == 0) press_couple = 2; + else if (strcmp(arg[6],"xz") == 0) press_couple = 3; + else if (strcmp(arg[6],"aniso") == 0) press_couple = 4; + else error->all("Illegal fix npt command"); + + if (narg < 14) error->all("Illegal fix npt command"); + + if (strcmp(arg[7],"NULL") == 0) { + p_start[0] = p_stop[0] = p_period[0] = 0.0; + p_flag[0] = 0; + } else { + p_start[0] = atof(arg[7]); + p_stop[0] = atof(arg[8]); + p_flag[0] = 1; + } + if (strcmp(arg[9],"NULL") == 0) { + p_start[1] = p_stop[1] = p_period[1] = 0.0; + p_flag[1] = 0; + } else { + p_start[1] = atof(arg[9]); + p_stop[1] = atof(arg[10]); + p_flag[1] = 1; + } + if (strcmp(arg[11],"NULL") == 0) { + p_start[2] = p_stop[2] = p_period[2] = 0.0; + p_flag[2] = 0; + } else { + p_start[2] = atof(arg[11]); + p_stop[2] = atof(arg[12]); + p_flag[2] = 1; + } + + double period = atof(arg[13]); + if (p_flag[0]) p_period[0] = period; + if (p_flag[1]) p_period[1] = period; + if (p_flag[2]) p_period[2] = period; + } + + // process extra keywords + + drag = 0.0; + dilate_partial = 0; + + int iarg; + if (press_couple == 0) iarg = 10; + else iarg = 14; + + while (iarg < narg) { + if (strcmp(arg[iarg],"drag") == 0) { + if (iarg+2 > narg) error->all("Illegal fix npt command"); + drag = atof(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"dilate") == 0) { + if (iarg+2 > narg) error->all("Illegal fix npt command"); + if (strcmp(arg[iarg+1],"all") == 0) dilate_partial = 0; + else if (strcmp(arg[iarg+1],"partial") == 0) dilate_partial = 1; + else error->all("Illegal fix npt command"); + iarg += 2; + } else error->all("Illegal fix npt command"); + } + + // check for periodicity in controlled dimensions + + if (p_flag[0] && domain->xperiodic == 0) + error->all("Cannot fix npt on a non-periodic dimension"); + if (p_flag[1] && domain->yperiodic == 0) + error->all("Cannot fix npt on a non-periodic dimension"); + if (p_flag[2] && domain->zperiodic == 0) + error->all("Cannot fix npt on a non-periodic dimension"); + + // create a new temperature full style with fix ID and group all + // pressure is always global (group all) and thus its + // KE/temperature contribution should use group all + + eta = eta_dot = 0.0; + + char **newarg = new char*[3]; + newarg[0] = id; + newarg[1] = "all"; + newarg[2] = "full"; + force->add_temp(3,newarg,1); + delete [] newarg; + + temperature = force->find_temp(id); + + // convert input periods to frequencies + + if (t_period <= 0.0 || (p_flag[0] && p_period[0] <= 0.0) || + (p_flag[1] && p_period[1] <= 0.0) || (p_flag[2] && p_period[2] <= 0.0)) + error->all("Fix npt periods must be > 0.0"); + + t_freq = 1.0 / t_period; + p_freq[0] = p_freq[1] = p_freq[2] = 0.0; + if (p_flag[0]) p_freq[0] = 1.0 / p_period[0]; + if (p_flag[1]) p_freq[1] = 1.0 / p_period[1]; + if (p_flag[2]) p_freq[2] = 1.0 / p_period[2]; + + // pressure init + + pressure = force->pressure; + omega[0] = omega[1] = omega[2] = 0.0; + omega_dot[0] = omega_dot[1] = omega_dot[2] = 0.0; + + nrigid = 0; + rfix = NULL; +} + +/* ---------------------------------------------------------------------- */ + +FixNPT::~FixNPT() +{ + delete [] rfix; +} + +/* ---------------------------------------------------------------------- */ + +int FixNPT::setmask() +{ + int mask = 0; + mask |= INITIAL_INTEGRATE; + mask |= FINAL_INTEGRATE; + mask |= THERMO; + mask |= INITIAL_INTEGRATE_RESPA; + mask |= FINAL_INTEGRATE_RESPA; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixNPT::init() +{ + if (atom->mass_require == 0) + error->all("Cannot use fix npt with no per-type mass defined"); + + // set timesteps and frequencies + + dtv = update->dt; + dtf = 0.5 * update->dt * force->ftm2v; + dthalf = 0.5 * update->dt; + + double freq = MAX(p_freq[0],p_freq[1]); + freq = MAX(freq,p_freq[2]); + drag_factor = 1.0 - (update->dt * freq * drag); + + boltz = force->boltz; + nktv2p = force->nktv2p; + vol0 = domain->xprd * domain->yprd * domain->zprd; + + double mass = 0.0; + for (int i = 0; i < atom->nlocal; i++) mass += atom->mass[atom->type[i]]; + MPI_Allreduce(&mass,&total_mass,1,MPI_DOUBLE,MPI_SUM,world); + + if (force->kspace) kspace_flag = 1; + else kspace_flag = 0; + + if (strcmp(update->integrate_style,"respa") == 0) { + nlevels_respa = ((Respa *) update->integrate)->nlevels; + step_respa = ((Respa *) update->integrate)->step; + } + + // detect if any fix rigid exist so rigid bodies move when box is dilated + // rfix[] = indices to each fix rigid + + delete [] rfix; + nrigid = 0; + rfix = NULL; + + for (int i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"rigid") == 0 || + strcmp(modify->fix[i]->style,"poems") == 0) nrigid++; + if (nrigid) { + rfix = new int[nrigid]; + nrigid = 0; + for (int i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"rigid") == 0 || + strcmp(modify->fix[i]->style,"poems") == 0) rfix[nrigid++] = i; + } +} + +/* ---------------------------------------------------------------------- + compute T,P before integrator starts +------------------------------------------------------------------------- */ + +void FixNPT::setup() +{ + t_current = temperature->compute(); + pressure->compute(temperature); + couple(); +} + +/* ---------------------------------------------------------------------- + 1st half of Verlet update +------------------------------------------------------------------------- */ + +void FixNPT::initial_integrate() +{ + int i; + + double delta = update->ntimestep - update->beginstep; + delta /= update->endstep - update->beginstep; + + // update eta_dot + + t_target = t_start + delta * (t_stop-t_start); + f_eta = t_freq*t_freq * (t_current/t_target - 1.0); + eta_dot += f_eta*dthalf; + eta_dot *= drag_factor; + eta += dtv*eta_dot; + + // update omega_dot + // for non-varying dims, p_freq is 0.0, so omega_dot doesn't change + + double f_omega; + double denskt = (atom->natoms*boltz*t_target) / + (domain->xprd*domain->yprd*domain->zprd) * nktv2p; + + for (i = 0; i < 3; i++) { + p_target[i] = p_start[i] + delta * (p_stop[i]-p_start[i]); + f_omega = p_freq[i]*p_freq[i] * (p_current[i]-p_target[i])/denskt; + omega_dot[i] += f_omega*dthalf; + omega_dot[i] *= drag_factor; + omega[i] += dtv*omega_dot[i]; + factor[i] = exp(-dthalf*(eta_dot+omega_dot[i])); + dilation[i] = exp(dthalf*omega_dot[i]); + } + + // v update only for atoms in NPT group + + double **x = atom->x; + double **v = atom->v; + double **f = atom->f; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double dtfm; + for (i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]]; + v[i][0] = v[i][0]*factor[0] + dtfm*f[i][0]; + v[i][1] = v[i][1]*factor[1] + dtfm*f[i][1]; + v[i][2] = v[i][2]*factor[2] + dtfm*f[i][2]; + } + } + + // rescale simulation box and all owned atoms by 1/2 step + + box_dilate(0); + + // x update by full step only for atoms in NPT group + + for (i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + x[i][0] += dtv * v[i][0]; + x[i][1] += dtv * v[i][1]; + x[i][2] += dtv * v[i][2]; + } + } + + // rescale simulation box and all owned atoms by 1/2 step + // redo KSpace coeffs since volume has changed + + box_dilate(0); + if (kspace_flag) force->kspace->setup(); +} + +/* ---------------------------------------------------------------------- + 2nd half of Verlet update +------------------------------------------------------------------------- */ + +void FixNPT::final_integrate() +{ + int i; + + // v update only for atoms in NPT group + + double **v = atom->v; + double **f = atom->f; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double dtfm; + for (i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]]; + v[i][0] = (v[i][0] + dtfm*f[i][0]) * factor[0]; + v[i][1] = (v[i][1] + dtfm*f[i][1]) * factor[1]; + v[i][2] = (v[i][2] + dtfm*f[i][2]) * factor[2]; + } + } + + // compute new T,P + + t_current = temperature->compute(); + pressure->compute(temperature); + couple(); + + // update eta_dot + + f_eta = t_freq*t_freq * (t_current/t_target - 1.0); + eta_dot += f_eta*dthalf; + eta_dot *= drag_factor; + + // update omega_dot + // for non-varying dims, p_freq is 0.0, so omega_dot doesn't change + + double f_omega; + double denskt = (atom->natoms*boltz*t_target) / + (domain->xprd*domain->yprd*domain->zprd) * nktv2p; + + for (i = 0; i < 3; i++) { + f_omega = p_freq[i]*p_freq[i] * (p_current[i]-p_target[i])/denskt; + omega_dot[i] += f_omega*dthalf; + omega_dot[i] *= drag_factor; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixNPT::initial_integrate_respa(int ilevel, int flag) +{ + // if flag = 1, then is 2nd call at outermost level from rRESPA + // perform 2nd half of box dilate on own + ghost atoms and return + // redo KSpace coeffs since volume has changed + + if (flag == 1) { + box_dilate(1); + if (kspace_flag) force->kspace->setup(); + return; + } + + // set timesteps by level + + double dtfm; + dtv = step_respa[ilevel]; + dtf = 0.5 * step_respa[ilevel] * force->ftm2v; + dthalf = 0.5 * step_respa[ilevel]; + + // atom quantities + + double **x = atom->x; + double **v = atom->v; + double **f = atom->f; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + // outermost level - update eta_dot and omega_dot, apply to v, dilate box + // all other levels - NVE update of v + // x,v updates only performed for atoms in NPT group + + if (ilevel == nlevels_respa-1) { + + double delta = update->ntimestep - update->beginstep; + delta /= update->endstep - update->beginstep; + + // update eta_dot + + t_target = t_start + delta * (t_stop-t_start); + f_eta = t_freq*t_freq * (t_current/t_target - 1.0); + eta_dot += f_eta*dthalf; + eta_dot *= drag_factor; + eta += dtv*eta_dot; + + // update omega_dot + // for non-varying dims, p_freq is 0.0, so omega_dot doesn't change + + double f_omega; + double denskt = (atom->natoms*boltz*t_target) / + (domain->xprd*domain->yprd*domain->zprd) * nktv2p; + + for (int i = 0; i < 3; i++) { + p_target[i] = p_start[i] + delta * (p_stop[i]-p_start[i]); + f_omega = p_freq[i]*p_freq[i] * (p_current[i]-p_target[i])/denskt; + omega_dot[i] += f_omega*dthalf; + omega_dot[i] *= drag_factor; + omega[i] += dtv*omega_dot[i]; + factor[i] = exp(-dthalf*(eta_dot+omega_dot[i])); + dilation[i] = exp(dthalf*omega_dot[i]); + } + + // v update only for atoms in NPT group + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]]; + v[i][0] = v[i][0]*factor[0] + dtfm*f[i][0]; + v[i][1] = v[i][1]*factor[1] + dtfm*f[i][1]; + v[i][2] = v[i][2]*factor[2] + dtfm*f[i][2]; + } + } + + // rescale simulation box and all owned atoms by 1/2 step + + box_dilate(0); + + } else { + + // v update only for atoms in NPT group + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]]; + v[i][0] += dtfm*f[i][0]; + v[i][1] += dtfm*f[i][1]; + v[i][2] += dtfm*f[i][2]; + } + } + } + + // innermost level - also update x only for atoms in NPT group + + if (ilevel == 0) { + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + x[i][0] += dtv * v[i][0]; + x[i][1] += dtv * v[i][1]; + x[i][2] += dtv * v[i][2]; + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixNPT::final_integrate_respa(int ilevel) +{ + double dtfm; + + // set timesteps by level + + dtf = 0.5 * step_respa[ilevel] * force->ftm2v; + dthalf = 0.5 * step_respa[ilevel]; + + // outermost level - update eta_dot and omega_dot, + // apply to v via final_integrate() + // all other levels - NVE update of v + // v update only performed for atoms in NPT group + + if (ilevel == nlevels_respa-1) final_integrate(); + else { + + // v update only for atoms in NPT group + + double **v = atom->v; + double **f = atom->f; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]]; + v[i][0] += dtfm*f[i][0]; + v[i][1] += dtfm*f[i][1]; + v[i][2] += dtfm*f[i][2]; + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixNPT::couple() +{ + double *p_tensor = pressure->p_tensor; + + if (press_couple == 0) + p_current[0] = p_current[1] = p_current[2] = pressure->p_total; + else if (press_couple == 1) { + double ave = 0.5 * (p_tensor[0] + p_tensor[1]); + p_current[0] = p_current[1] = ave; + p_current[2] = p_tensor[2]; + } else if (press_couple == 2) { + double ave = 0.5 * (p_tensor[1] + p_tensor[2]); + p_current[1] = p_current[2] = ave; + p_current[0] = p_tensor[0]; + } else if (press_couple == 3) { + double ave = 0.5 * (p_tensor[0] + p_tensor[2]); + p_current[0] = p_current[2] = ave; + p_current[1] = p_tensor[1]; + } if (press_couple == 4) { + p_current[0] = p_tensor[0]; + p_current[1] = p_tensor[1]; + p_current[2] = p_tensor[2]; + } +} + +/* ---------------------------------------------------------------------- + dilate the box around center of box +------------------------------------------------------------------------- */ + +void FixNPT::box_dilate(int flag) +{ + int i,n; + + // ctr = geometric center of box in a dimension + // scale owned or owned+ghost atoms depending on flag + // re-define simulation box via xprd/yprd/zprd + // scale atom coords for all atoms or only for fix group atoms + // if fix rigid exists, scale rigid body centers-of-mass + // don't do anything if non-periodic or press style is constant volume + + double **x = atom->x; + int *mask = atom->mask; + if (flag) n = atom->nlocal + atom->nghost; + else n = atom->nlocal; + + double oldlo,oldhi,ctr; + + if (domain->xperiodic && p_flag[0]) { + oldlo = domain->boxxlo; + oldhi = domain->boxxhi; + ctr = 0.5 * (oldlo + oldhi); + domain->boxxlo = (oldlo-ctr)*dilation[0] + ctr; + domain->boxxhi = (oldhi-ctr)*dilation[0] + ctr; + domain->xprd = domain->boxxhi - domain->boxxlo; + if (dilate_partial) { + for (i = 0; i < n; i++) + if (mask[i] & groupbit) + x[i][0] = ctr + (x[i][0]-ctr)*dilation[0]; + } else { + for (i = 0; i < n; i++) + x[i][0] = ctr + (x[i][0]-ctr)*dilation[0]; + } + if (nrigid) + for (i = 0; i < nrigid; i++) + modify->fix[rfix[i]]-> + dilate(0,oldlo,oldhi,domain->boxxlo,domain->boxxhi); + } + + if (domain->yperiodic && p_flag[1]) { + oldlo = domain->boxylo; + oldhi = domain->boxyhi; + ctr = 0.5 * (oldlo + oldhi); + domain->boxylo = (oldlo-ctr)*dilation[1] + ctr; + domain->boxyhi = (oldhi-ctr)*dilation[1] + ctr; + domain->yprd = domain->boxyhi - domain->boxylo; + if (dilate_partial) { + for (i = 0; i < n; i++) + if (mask[i] & groupbit) + x[i][1] = ctr + (x[i][1]-ctr)*dilation[1]; + } else { + for (i = 0; i < n; i++) + x[i][1] = ctr + (x[i][1]-ctr)*dilation[1]; + } + if (nrigid) + for (i = 0; i < nrigid; i++) + modify->fix[rfix[i]]-> + dilate(1,oldlo,oldhi,domain->boxylo,domain->boxyhi); + } + + if (domain->zperiodic && p_flag[2]) { + oldlo = domain->boxzlo; + oldhi = domain->boxzhi; + ctr = 0.5 * (oldlo + oldhi); + domain->boxzlo = (oldlo-ctr)*dilation[2] + ctr; + domain->boxzhi = (oldhi-ctr)*dilation[2] + ctr; + domain->zprd = domain->boxzhi - domain->boxzlo; + if (dilate_partial) { + for (i = 0; i < n; i++) + if (mask[i] & groupbit) + x[i][2] = ctr + (x[i][2]-ctr)*dilation[2]; + } else { + for (i = 0; i < n; i++) + x[i][2] = ctr + (x[i][2]-ctr)*dilation[2]; + } + if (nrigid) + for (i = 0; i < nrigid; i++) + modify->fix[rfix[i]]-> + dilate(2,oldlo,oldhi,domain->boxzlo,domain->boxzhi); + } +} + +/* ---------------------------------------------------------------------- + pack entire state of Fix into one write +------------------------------------------------------------------------- */ + +void FixNPT::write_restart(FILE *fp) +{ + int n = 0; + double list[8]; + list[n++] = eta; + list[n++] = eta_dot; + list[n++] = omega[0]; + list[n++] = omega[1]; + list[n++] = omega[2]; + list[n++] = omega_dot[0]; + list[n++] = omega_dot[1]; + list[n++] = omega_dot[2]; + + if (comm->me == 0) { + int size = n * sizeof(double); + fwrite(&size,sizeof(int),1,fp); + fwrite(&list,sizeof(double),n,fp); + } +} + +/* ---------------------------------------------------------------------- + use state info from restart file to restart the Fix +------------------------------------------------------------------------- */ + +void FixNPT::restart(char *buf) +{ + int n = 0; + double *list = (double *) buf; + eta = list[n++]; + eta_dot = list[n++]; + omega[0] = list[n++]; + omega[1] = list[n++]; + omega[2] = list[n++]; + omega_dot[0] = list[n++]; + omega_dot[1] = list[n++]; + omega_dot[2] = list[n++]; +} + +/* ---------------------------------------------------------------------- */ + +int FixNPT::modify_param(int narg, char **arg) +{ + if (strcmp(arg[0],"temp") == 0) { + if (narg < 2) error->all("Illegal fix_modify command"); + temperature = force->find_temp(arg[1]); + if (temperature == NULL) + error->all("Could not find fix_modify temperature ID"); + if (temperature->igroup != 0 && comm->me == 0) + error->warning("Temperature for NPT is not for group all"); + if (strcmp(temperature->style,"region") == 0 && comm->me == 0) + error->warning("Temperature for NPT is style region"); + return 2; + } + return 0; +} + +/* ---------------------------------------------------------------------- */ + +int FixNPT::thermo_fields(int n, int *flags, char **keywords) +{ + if (n == 0) return 1; + flags[0] = 3; + strcpy(keywords[0],"EngNPT"); + return 1; +} + +/* ---------------------------------------------------------------------- */ + +int FixNPT::thermo_compute(double *values) +{ + double ke = temperature->dof * boltz * t_target; + double keplus = atom->natoms * boltz * t_target; + double volume = domain->xprd * domain->yprd * domain->zprd; + int pdim = p_flag[0] + p_flag[1] + p_flag[2]; + + values[0] = ke * (eta + 0.5*eta_dot*eta_dot/(t_freq*t_freq)); + for (int i = 0; i < 3; i++) + if (p_freq[i] > 0.0) + values[0] += 0.5*keplus*omega_dot[i]*omega_dot[i] / + (p_freq[i]*p_freq[i]) + p_target[i]*(volume-vol0) / (pdim*nktv2p); + return 1; +} diff --git a/src/fix_npt.h b/src/fix_npt.h new file mode 100644 index 0000000000000000000000000000000000000000..d9ce157f570d175be55469e494136ea8f5f67340 --- /dev/null +++ b/src/fix_npt.h @@ -0,0 +1,72 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_NPT_H +#define FIX_NPT_H + +#include "fix.h" +class Temperature; +class Pressure; + +class FixNPT : public Fix { + friend class Pressure; // accesses press_couple + + public: + FixNPT(int, char **); + ~FixNPT(); + int setmask(); + void init(); + void setup(); + void initial_integrate(); + void final_integrate(); + void initial_integrate_respa(int, int); + void final_integrate_respa(int); + void write_restart(FILE *); + void restart(char *); + int modify_param(int, char **); + int thermo_fields(int, int *, char **); + int thermo_compute(double *); + + private: + Temperature *temperature; + Pressure *pressure; + + double dtv,dtf,dthalf; + double boltz,nktv2p; + double total_mass,vol0; + + double t_start,t_stop; + double t_current,t_target; + double t_freq; + double f_eta,eta_dot,eta; + + int press_couple,dilate_partial; + int p_flag[3]; // 1 if control P on this dim, 0 if not + double p_start[3],p_stop[3]; + double p_freq[3],p_target[3]; + double omega[3],omega_dot[3]; + double p_current[3],dilation[3]; + double drag,drag_factor; + double factor[3]; + int kspace_flag; // 1 if KSpace invoked, 0 if not + int nrigid; // number of rigid fixes + int *rfix; // indices of rigid fixes + + int nlevels_respa; + double *step_respa; + + void couple(); + void box_dilate(int); +}; + +#endif diff --git a/src/fix_nve.cpp b/src/fix_nve.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9013e2ab8ca1c0cd78f181120f38693490e9cf52 --- /dev/null +++ b/src/fix_nve.cpp @@ -0,0 +1,154 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "stdio.h" +#include "string.h" +#include "fix_nve.h" +#include "atom.h" +#include "force.h" +#include "update.h" +#include "respa.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixNVE::FixNVE(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 3) error->all("Illegal fix nve command"); +} + +/* ---------------------------------------------------------------------- */ + +int FixNVE::setmask() +{ + int mask = 0; + mask |= INITIAL_INTEGRATE; + mask |= FINAL_INTEGRATE; + mask |= INITIAL_INTEGRATE_RESPA; + mask |= FINAL_INTEGRATE_RESPA; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixNVE::init() +{ + dtv = update->dt; + dtf = 0.5 * update->dt * force->ftm2v; + + mass_require = atom->mass_require; + if (strcmp(update->integrate_style,"respa") == 0) + step_respa = ((Respa *) update->integrate)->step; +} + +/* ---------------------------------------------------------------------- + allow for both per-type and per-atom mass +------------------------------------------------------------------------- */ + +void FixNVE::initial_integrate() +{ + double dtfm; + + double **x = atom->x; + double **v = atom->v; + double **f = atom->f; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + if (mass_require) { + double *mass = atom->mass; + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]]; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + x[i][0] += dtv * v[i][0]; + x[i][1] += dtv * v[i][1]; + x[i][2] += dtv * v[i][2]; + } + } + + } else { + double *rmass = atom->rmass; + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / rmass[i]; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + x[i][0] += dtv * v[i][0]; + x[i][1] += dtv * v[i][1]; + x[i][2] += dtv * v[i][2]; + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixNVE::final_integrate() +{ + double dtfm; + + double **v = atom->v; + double **f = atom->f; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + if (mass_require) { + double *mass = atom->mass; + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]]; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + } + } + + } else { + double *rmass = atom->rmass; + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / rmass[i]; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixNVE::initial_integrate_respa(int ilevel, int flag) +{ + if (flag) return; // only used by NPT,NPH + + dtv = step_respa[ilevel]; + dtf = 0.5 * step_respa[ilevel] * force->ftm2v; + + if (ilevel == 0) initial_integrate(); + else final_integrate(); +} + +/* ---------------------------------------------------------------------- */ + +void FixNVE::final_integrate_respa(int ilevel) +{ + dtf = 0.5 * step_respa[ilevel] * force->ftm2v; + final_integrate(); +} diff --git a/src/fix_nve.h b/src/fix_nve.h new file mode 100644 index 0000000000000000000000000000000000000000..3e0f512ef31d2099e6a3bfa4104925df8de8e6d0 --- /dev/null +++ b/src/fix_nve.h @@ -0,0 +1,36 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_NVE_H +#define FIX_NVE_H + +#include "fix.h" + +class FixNVE : public Fix { + public: + FixNVE(int, char **); + ~FixNVE() {} + int setmask(); + void init(); + void initial_integrate(); + void final_integrate(); + void initial_integrate_respa(int, int); + void final_integrate_respa(int); + + protected: + double dtv,dtf; + double *step_respa; + int mass_require; +}; + +#endif diff --git a/src/fix_nvt.cpp b/src/fix_nvt.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aac18cb2f0e61ec2ee8bc99f9bac5aaa27cb0cb4 --- /dev/null +++ b/src/fix_nvt.cpp @@ -0,0 +1,370 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Mark Stevens (SNL) +------------------------------------------------------------------------- */ + +#include "string.h" +#include "stdlib.h" +#include "math.h" +#include "fix_nvt.h" +#include "atom.h" +#include "force.h" +#include "comm.h" +#include "group.h" +#include "update.h" +#include "respa.h" +#include "temperature.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixNVT::FixNVT(int narg, char **arg) : Fix(narg, arg) +{ + if (narg < 6) error->all("Illegal fix nvt command"); + + restart_global = 1; + + t_start = atof(arg[3]); + t_stop = atof(arg[4]); + double t_period = atof(arg[5]); + + if (narg == 6) drag = 0.0; + else if (narg == 8 && strcmp(arg[6],"drag") == 0) drag = atof(arg[7]); + else error->all("Illegal fix nvt command"); + + // error checks + // convert input period to frequency + + if (t_start < 0.0 || t_stop <= 0.0) + error->all("Target T for fix nvt cannot be 0.0"); + if (t_period <= 0.0) error->all("Fix nvt period must be > 0.0"); + t_freq = 1.0 / t_period; + + // create a new temperature full style with fix ID and fix group + + eta = eta_dot = 0.0; + + char **newarg = new char*[3]; + newarg[0] = id; + newarg[1] = group->names[igroup]; + newarg[2] = "full"; + force->add_temp(3,newarg,1); + delete [] newarg; + + temperature = force->find_temp(id); +} + +/* ---------------------------------------------------------------------- */ + +int FixNVT::setmask() +{ + int mask = 0; + mask |= INITIAL_INTEGRATE; + mask |= FINAL_INTEGRATE; + mask |= THERMO; + mask |= INITIAL_INTEGRATE_RESPA; + mask |= FINAL_INTEGRATE_RESPA; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixNVT::init() +{ + if (atom->mass_require == 0) + error->all("Cannot use fix nvt with no per-type mass defined"); + + dtv = update->dt; + dtf = 0.5 * update->dt * force->ftm2v; + dthalf = 0.5 * update->dt; + + drag_factor = 1.0 - (update->dt * t_freq * drag); + + if (strcmp(update->integrate_style,"respa") == 0) { + nlevels_respa = ((Respa *) update->integrate)->nlevels; + step_respa = ((Respa *) update->integrate)->step; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixNVT::setup() +{ + t_current = temperature->compute(); +} + +/* ---------------------------------------------------------------------- */ + +void FixNVT::initial_integrate() +{ + double dtfm; + + double delta = update->ntimestep - update->beginstep; + delta /= update->endstep - update->beginstep; + t_target = t_start + delta * (t_stop-t_start); + + // update eta_dot + + f_eta = t_freq*t_freq * (t_current/t_target - 1.0); + eta_dot += f_eta*dthalf; + eta_dot *= drag_factor; + eta += dtv*eta_dot; + factor = exp(-dthalf*eta_dot); + + // update v and x of only atoms in NVT group + + double **x = atom->x; + double **v = atom->v; + double **f = atom->f; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]]; + v[i][0] = v[i][0]*factor + dtfm*f[i][0]; + v[i][1] = v[i][1]*factor + dtfm*f[i][1]; + v[i][2] = v[i][2]*factor + dtfm*f[i][2]; + x[i][0] += dtv * v[i][0]; + x[i][1] += dtv * v[i][1]; + x[i][2] += dtv * v[i][2]; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixNVT::final_integrate() +{ + double dtfm; + + // update v of only atoms in NVT group + + double **v = atom->v; + double **f = atom->f; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]] * factor; + v[i][0] = v[i][0]*factor + dtfm*f[i][0]; + v[i][1] = v[i][1]*factor + dtfm*f[i][1]; + v[i][2] = v[i][2]*factor + dtfm*f[i][2]; + } + } + + // compute current T + + t_current = temperature->compute(); + + // update eta_dot + + f_eta = t_freq*t_freq * (t_current/t_target - 1.0); + eta_dot += f_eta*dthalf; + eta_dot *= drag_factor; +} + +/* ---------------------------------------------------------------------- */ + +void FixNVT::initial_integrate_respa(int ilevel, int flag) +{ + if (flag) return; // only used by NPT,NPH + + // set timesteps by level + + double dtfm; + dtv = step_respa[ilevel]; + dtf = 0.5 * step_respa[ilevel] * force->ftm2v; + dthalf = 0.5 * step_respa[ilevel]; + + // atom quantities + + double **x = atom->x; + double **v = atom->v; + double **f = atom->f; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + // outermost level - update eta_dot and apply to v + // all other levels - NVE update of v + + if (ilevel == nlevels_respa-1) { + double delta = update->ntimestep - update->beginstep; + delta /= update->endstep - update->beginstep; + t_target = t_start + delta * (t_stop-t_start); + + // update eta_dot + + f_eta = t_freq*t_freq * (t_current/t_target - 1.0); + eta_dot += f_eta*dthalf; + eta_dot *= drag_factor; + eta += dtv*eta_dot; + factor = exp(-dthalf*eta_dot); + + // update v of only atoms in NVT group + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]]; + v[i][0] = v[i][0]*factor + dtfm*f[i][0]; + v[i][1] = v[i][1]*factor + dtfm*f[i][1]; + v[i][2] = v[i][2]*factor + dtfm*f[i][2]; + } + } + + } else { + + // update v of only atoms in NVT group + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]]; + v[i][0] += dtfm*f[i][0]; + v[i][1] += dtfm*f[i][1]; + v[i][2] += dtfm*f[i][2]; + } + } + } + + // innermost level - also update x of only atoms in NVT group + + if (ilevel == 0) { + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + x[i][0] += dtv * v[i][0]; + x[i][1] += dtv * v[i][1]; + x[i][2] += dtv * v[i][2]; + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixNVT::final_integrate_respa(int ilevel) +{ + double dtfm; + + // set timesteps by level + + dtf = 0.5 * step_respa[ilevel] * force->ftm2v; + dthalf = 0.5 * step_respa[ilevel]; + + // outermost level - update eta_dot and apply to v via final_integrate() + // all other levels - NVE update of v + + if (ilevel == nlevels_respa-1) final_integrate(); + else { + + // update v of only atoms in NVT group + + double **v = atom->v; + double **f = atom->f; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]]; + v[i][0] += dtfm*f[i][0]; + v[i][1] += dtfm*f[i][1]; + v[i][2] += dtfm*f[i][2]; + } + } + } +} + +/* ---------------------------------------------------------------------- + pack entire state of Fix into one write +------------------------------------------------------------------------- */ + +void FixNVT::write_restart(FILE *fp) +{ + int n = 0; + double list[2]; + list[n++] = eta; + list[n++] = eta_dot; + + if (comm->me == 0) { + int size = n * sizeof(double); + fwrite(&size,sizeof(int),1,fp); + fwrite(&list,sizeof(double),n,fp); + } +} + +/* ---------------------------------------------------------------------- + use state info from restart file to restart the Fix +------------------------------------------------------------------------- */ + +void FixNVT::restart(char *buf) +{ + int n = 0; + double *list = (double *) buf; + eta = list[n++]; + eta_dot = list[n++]; +} + +/* ---------------------------------------------------------------------- */ + +int FixNVT::modify_param(int narg, char **arg) +{ + if (strcmp(arg[0],"temp") == 0) { + if (narg < 2) error->all("Illegal fix_modify command"); + temperature = force->find_temp(arg[1]); + if (temperature == NULL) + error->all("Could not find fix_modify temperature ID"); + if (temperature->igroup != igroup && comm->me == 0) + error->warning("Group for fix_modify temp != fix group"); + if (strcmp(temperature->style,"region") == 0 && comm->me == 0) + error->warning("Temperature for NVT is style region"); + return 2; + } + return 0; +} + +/* ---------------------------------------------------------------------- */ + +void FixNVT::reset_target(double t_new) +{ + t_start = t_stop = t_new; +} + +/* ---------------------------------------------------------------------- */ + +int FixNVT::thermo_fields(int n, int *flags, char **keywords) +{ + if (n == 0) return 1; + flags[0] = 3; + strcpy(keywords[0],"EngNVT"); + return 1; +} + +/* ---------------------------------------------------------------------- */ + +int FixNVT::thermo_compute(double *values) +{ + double ke = temperature->dof * force->boltz * t_target; + values[0] = ke * (eta + 0.5*eta_dot*eta_dot/(t_freq*t_freq)); + return 1; +} diff --git a/src/fix_nvt.h b/src/fix_nvt.h new file mode 100644 index 0000000000000000000000000000000000000000..7cb14a3684e02eac8c0a17a184a7bdd9fbb8efda --- /dev/null +++ b/src/fix_nvt.h @@ -0,0 +1,50 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_NVT_H +#define FIX_NVT_H + +#include "fix.h" +class Temperature; + +class FixNVT : public Fix { + public: + FixNVT(int, char **); + ~FixNVT() {} + int setmask(); + void init(); + void setup(); + void initial_integrate(); + void final_integrate(); + void initial_integrate_respa(int,int); + void final_integrate_respa(int); + void write_restart(FILE *); + void restart(char *); + int modify_param(int, char **); + void reset_target(double); + int thermo_fields(int, int *, char **); + int thermo_compute(double *); + + private: + double t_start,t_stop; + double t_current,t_target; + double t_freq,drag,drag_factor; + double f_eta,eta_dot,eta,factor; + double dtv,dtf,dthalf; + Temperature *temperature; + + int nlevels_respa; + double *step_respa; +}; + +#endif diff --git a/src/fix_orient_fcc.cpp b/src/fix_orient_fcc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bb039e1f4efa90f42412c780ac34597e21a6be80 --- /dev/null +++ b/src/fix_orient_fcc.cpp @@ -0,0 +1,556 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "comm.h" +#include "output.h" +#include "error.h" + +#define BIG 1000000000 + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +/* ---------------------------------------------------------------------- */ + +FixOrientFCC::FixOrientFCC(int narg, char **arg) : Fix(narg, arg) +{ + MPI_Comm_rank(world,&me); + + if (narg != 11) error->all("Illegal fix orient/fcc command"); + + neigh_full_every = 1; + + nstats = atoi(arg[3]); + direction_of_motion = atoi(arg[4]); + a = atof(arg[5]); + Vxi = atof(arg[6]); + uxif_low = atof(arg[7]); + uxif_high = atof(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("Illegal fix orient/fcc command"); + + // initializations + + PI = 4.0*atan(1.0); + 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[512]; + char *result; + int count; + + FILE *infile = fopen(xifilename,"r"); + if (infile == NULL) error->one("Fix orient/fcc file open failed"); + for (int i = 0; i < 6; i++) { + result = fgets(line,512,infile); + if (!result) error->one("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("Fix orient/fcc file read failed"); + } + fclose(infile); + + infile = fopen(chifilename,"r"); + if (infile == NULL) error->one("Fix orient/fcc file open failed"); + for (int i = 0; i < 6; i++) { + result = fgets(line,512,infile); + if (!result) error->one("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("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; +} + +/* ---------------------------------------------------------------------- */ + +FixOrientFCC::~FixOrientFCC() +{ + delete [] xifilename; + delete [] chifilename; + if (nmax) delete [] nbr; +} + +/* ---------------------------------------------------------------------- */ + +int FixOrientFCC::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + mask |= POST_FORCE_RESPA; + mask |= THERMO; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixOrientFCC::init() +{ + if (use_xismooth) comm->maxforward_fix = MAX(comm->maxforward_fix,62); + else comm->maxforward_fix = MAX(comm->maxforward_fix,50); + + if (strcmp(update->integrate_style,"respa") == 0) + nlevels_respa = ((Respa *) update->integrate)->nlevels; + + if (thermo_print || thermo_energy) thermo_flag = 1; + else thermo_flag = 0; +} + +/* ---------------------------------------------------------------------- */ + +void FixOrientFCC::setup() +{ + eflag_on = 1; + if (strcmp(update->integrate_style,"verlet") == 0) + post_force(1); + else { + ((Respa *) update->integrate)->copy_flevel_f(nlevels_respa-1); + post_force_respa(1,nlevels_respa-1,0); + ((Respa *) update->integrate)->copy_f_flevel(nlevels_respa-1); + } + eflag_on = 0; +} + +/* ---------------------------------------------------------------------- */ + +void FixOrientFCC::post_force(int vflag) +{ + int i,j,k,m,n,nn,nsort,id_self; + int *neighs; + double edelta,added_energy,omega; + double dx,dy,dz,rsq,xismooth,xi_sq,duxi,duxi_other; + double dxi[3]; + double *dxiptr; + bool found_myself; + + int eflag = false; + if (thermo_flag) { + if (eflag_on) eflag = true; + else if (output->next_thermo == update->ntimestep) eflag = true; + } + + // 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; + + // insure nbr data structure is adequate size + + if (nall > nmax) { + if (nmax) delete [] nbr; + nbr = new Nbr[nall]; + nmax = nall; + } + + // loop over owned atoms and build Nbr data structure of neighbors + // use full neighbor list + + if (eflag) added_energy = 0.0; + int count = 0; + int mincount = BIG; + int maxcount = 0; + + for (i = 0; i < nlocal; i++) { + neighs = neighbor->firstneigh_full[i]; + n = neighbor->numneigh_full[i]; + + if (n < mincount) mincount = n; + if (n > maxcount) { + if (maxcount) delete [] sort; + sort = new Sort[n]; + maxcount = n; + } + + // 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 (k = 0; k < n; k++) { + j = neighs[k]; + 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; + + // compute potential derivative to xi + + if (xi_total < xi0) { + nbr[i].duxi = 0.0; + if (eflag) edelta = 0.0; + } else if (xi_total > xi1) { + nbr[i].duxi = 0.0; + if (eflag) edelta = Vxi; + } else { + omega = (0.5*PI)*(xi_total-xi0) / (xi1-xi0); + nbr[i].duxi = PI*Vxi*sin(2.0*omega) / (2.0*(xi1-xi0)); + if (eflag) edelta = Vxi*(1 - cos(2.0*omega)) / 2.0; + } + if (eflag) added_energy += edelta; + } + + if (maxcount) delete [] sort; + + // communicate to acquire nbr data for ghost atoms + + comm->comm_fix(this); + + // compute grain boundary force on each owned atom + // skip atoms not in group + + for (i = 0; i < nlocal; i++) { + 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("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]; + } + } + } + } + } + + // sum energy across procs + + if (eflag) + MPI_Allreduce(&added_energy,&total_added_e,1,MPI_DOUBLE,MPI_SUM,world); + + // 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 %d: %g atoms have %d neighbors\n", + update->ntimestep,atom->natoms,total); + fprintf(screen," neighs: min = %d, max = %d, ave = %g\n", + min,max,ave); + } + if (logfile) { + fprintf(logfile,"orient step %d: %g atoms have %d neighbors\n", + update->ntimestep,atom->natoms,total); + 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); +} + +/* ---------------------------------------------------------------------- */ + +int FixOrientFCC::thermo_fields(int n, int *flags, char **keywords) +{ + if (n == 0) return 1; + flags[0] = 3; + strcpy(keywords[0],"Orient"); + return 1; +} + +/* ---------------------------------------------------------------------- */ + +int FixOrientFCC::thermo_compute(double *values) +{ + values[0] = total_added_e; + return 1; +} + +/* ---------------------------------------------------------------------- */ + +int FixOrientFCC::pack_comm(int n, int *list, double *buf, int *pbc_flags) +{ + 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++] = static_cast<double> (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++] = static_cast<double> (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<int> (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<int> (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 +------------------------------------------------------------------------- */ + +int FixOrientFCC::memory_usage() +{ + int bytes = atom->nmax * sizeof(Nbr); + return bytes; +} diff --git a/src/fix_orient_fcc.h b/src/fix_orient_fcc.h new file mode 100644 index 0000000000000000000000000000000000000000..decfeb67641c1373018b349813eba316030ac16a --- /dev/null +++ b/src/fix_orient_fcc.h @@ -0,0 +1,77 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_ORIENT_FCC_H +#define FIX_ORIENT_FCC_H + +#include "fix.h" + +class FixOrientFCC : public Fix { + public: + struct Nbr { // neighbor info for each owned and ghost atom + int n; // # of closest neighbors (up to 12) + int id[12]; // IDs of each neighbor + // if center atom is owned, these are local IDs + // if center atom is ghost, these are global IDs + double xismooth[12]; // distance weighting factor for each neighbors + double dxi[12][3]; // d order-parameter / dx for each neighbor + double duxi; // d Energy / d order-parameter for atom + }; + + struct Sort { // data structure for sorting to find 12 closest + int id; // ID of neighbor atom + double rsq; // distance between center and neighbor atom + double delta[3]; // displacement between center and neighbor atom + double xismooth; // distance weighting factor + }; + + FixOrientFCC(int, char **); + ~FixOrientFCC(); + int setmask(); + void init(); + void setup(); + void post_force(int); + void post_force_respa(int, int, int); + int pack_comm(int, int *, double *, int *); + void unpack_comm(int, int, double *); + int thermo_fields(int, int *, char **); + int thermo_compute(double *); + int memory_usage(); + + private: + int me; + double PI; + int nlevels_respa; + + int direction_of_motion; // 1 = center shrinks, 0 = center grows + int nstats; // stats output every this many steps + double a; // lattice parameter + double Vxi; // potential value + double uxif_low; // cut-off fraction, low order parameter + double uxif_high; // cut-off fraction, high order parameter + char *xifilename, *chifilename; // file names for 2 crystal orientations + + bool use_xismooth; + int thermo_flag,eflag_on; + double Rxi[12][3],Rchi[12][3],half_xi_chi_vec[2][6][3]; + double xiid,xi0,xi1,xicutoffsq,cutsq,total_added_e; + int half_fcc_nn,nmax; + + Nbr *nbr; + Sort *sort; + + void find_best_ref(double *, int, double &, double *); + static int compare(const void *, const void *); +}; + +#endif diff --git a/src/fix_plane_force.cpp b/src/fix_plane_force.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1dea08c3b1ffff70c299007a2cce584c08531606 --- /dev/null +++ b/src/fix_plane_force.cpp @@ -0,0 +1,96 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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_plane_force.h" +#include "atom.h" +#include "update.h" +#include "respa.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixPlaneForce::FixPlaneForce(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 6) error->all("Illegal fix planeforce command"); + xdir = atof(arg[3]); + ydir = atof(arg[4]); + zdir = atof(arg[5]); +} + +/* ---------------------------------------------------------------------- */ + +int FixPlaneForce::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + mask |= POST_FORCE_RESPA; + mask |= MIN_POST_FORCE; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixPlaneForce::setup() +{ + if (strcmp(update->integrate_style,"verlet") == 0) + post_force(1); + else { + int nlevels_respa = ((Respa *) update->integrate)->nlevels; + for (int ilevel = 0; ilevel < nlevels_respa; ilevel++) { + ((Respa *) update->integrate)->copy_flevel_f(ilevel); + post_force_respa(1,ilevel,0); + ((Respa *) update->integrate)->copy_f_flevel(ilevel); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixPlaneForce::min_setup() +{ + post_force(1); +} + +/* ---------------------------------------------------------------------- */ + +void FixPlaneForce::post_force(int vflag) +{ + double **f = atom->f; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double dot; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + dot = f[i][0]*xdir + f[i][1]*ydir + f[i][2]*zdir; + f[i][0] -= dot * xdir; + f[i][1] -= dot * ydir; + f[i][2] -= dot * zdir; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixPlaneForce::post_force_respa(int vflag, int ilevel, int iloop) +{ + post_force(vflag); +} + +/* ---------------------------------------------------------------------- */ + +void FixPlaneForce::min_post_force(int vflag) +{ + post_force(vflag); +} diff --git a/src/fix_plane_force.h b/src/fix_plane_force.h new file mode 100644 index 0000000000000000000000000000000000000000..5485ce612d4bcf827e2739e224bc8d968b3523f0 --- /dev/null +++ b/src/fix_plane_force.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_PLANE_FORCE_H +#define FIX_PLANE_FORCE_H + +#include "fix.h" + +class FixPlaneForce : public Fix { + public: + FixPlaneForce(int, char **); + ~FixPlaneForce() {} + int setmask(); + void setup(); + void min_setup(); + void post_force(int); + void post_force_respa(int, int, int); + void min_post_force(int); + + private: + double xdir,ydir,zdir; +}; + +#endif diff --git a/src/fix_print.cpp b/src/fix_print.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f1263a823407801da854a6b9ddf8c5818381138f --- /dev/null +++ b/src/fix_print.cpp @@ -0,0 +1,76 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "string.h" +#include "fix_print.h" +#include "update.h" +#include "input.h" +#include "variable.h" +#include "error.h" + +#define MAXLINE 1024 + +/* ---------------------------------------------------------------------- */ + +FixPrint::FixPrint(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 5) error->all("Illegal fix print command"); + nevery = atoi(arg[3]); + if (nevery <= 0) error->all("Illegal fix print command"); + + MPI_Comm_rank(world,&me); + + int n = strlen(arg[4]) + 1; + line = new char[n]; + strcpy(line,arg[4]); + + copy = new char[MAXLINE]; + work = new char[MAXLINE]; +} + +/* ---------------------------------------------------------------------- */ + +FixPrint::~FixPrint() +{ + delete [] line; + delete [] copy; + delete [] work; +} + +/* ---------------------------------------------------------------------- */ + +int FixPrint::setmask() +{ + int mask = 0; + mask |= END_OF_STEP; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixPrint::end_of_step() +{ + // make a copy of line to work on + // substitute for $ variables (no printing) + // append a newline and print final copy + + strcpy(copy,line); + input->substitute(copy,0); + strcat(copy,"\n"); + + if (me == 0) { + if (screen) fprintf(screen,copy); + if (logfile) fprintf(logfile,copy); + } +} diff --git a/src/fix_print.h b/src/fix_print.h new file mode 100644 index 0000000000000000000000000000000000000000..07099ebea0963527360233a4a989d9b0817c76fe --- /dev/null +++ b/src/fix_print.h @@ -0,0 +1,31 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_PRINT_H +#define FIX_PRINT_H + +#include "fix.h" + +class FixPrint : public Fix { + public: + FixPrint(int, char **); + ~FixPrint(); + int setmask(); + void end_of_step(); + + private: + int me; + char *line,*copy,*work; +}; + +#endif diff --git a/src/fix_rdf.cpp b/src/fix_rdf.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d68ffe6938caefca617f91b3bc8dac96fc12bcea --- /dev/null +++ b/src/fix_rdf.cpp @@ -0,0 +1,303 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing authors: Paul Crozier (SNL), Jeff Greathouse (SNL) +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "math.h" +#include "stdlib.h" +#include "fix_rdf.h" +#include "atom.h" +#include "update.h" +#include "force.h" +#include "domain.h" +#include "output.h" +#include "group.h" +#include "modify.h" +#include "neighbor.h" +#include "pair.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixRDF::FixRDF(int narg, char **arg) : Fix(narg, arg) +{ + if (narg < 8 || (narg-6) % 2) error->all("Illegal fix rdf command"); + + neigh_half_once = 1; + + nevery = atoi(arg[3]); + if (nevery <= 0) error->all("Illegal fix rdf command"); + first = 1; + + MPI_Comm_rank(world,&me); + if (me == 0) { + fp = fopen(arg[4],"w"); + if (fp == NULL) { + char str[128]; + sprintf(str,"Cannot open fix rdf file %s",arg[4]); + error->one(str); + } + } + + maxbin = atoi(arg[5]); + + n_rdf_pairs = 0; + rdfpair = memory->create_2d_int_array(atom->ntypes+1,atom->ntypes+1, + "rdf:rdfpair"); + + for (int i = 1; i <= atom->ntypes; i++) + for (int j = 1; j <= atom->ntypes; j++) + rdfpair[i][j] = 0; + + int itype,jtype; + for (int i = 6; i < narg; i+=2) { + itype = atoi(arg[i]); + jtype = atoi(arg[i+1]); + if (itype < 1 || jtype < 1 || itype > atom->ntypes || jtype > atom->ntypes) + error->all("Invalid atom types in fix rdf command"); + n_rdf_pairs++; + rdfpair[itype][jtype] = n_rdf_pairs; + } + + hist = memory->create_2d_int_array(n_rdf_pairs,maxbin,"rdf:hist"); + hist_all = memory->create_2d_int_array(n_rdf_pairs,maxbin,"rdf:hist_all"); + + int *nrdfatom = new int[atom->ntypes+1]; + for (int i = 1; i <= atom->ntypes; i++) nrdfatom[i] = 0; + + int *mask = atom->mask; + int *type = atom->type; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) nrdfatom[type[i]]++; + + nrdfatoms = new int[atom->ntypes+1]; + MPI_Allreduce(&nrdfatom[1],&nrdfatoms[1],atom->ntypes,MPI_INT,MPI_SUM,world); + delete [] nrdfatom; + + gr_ave = memory->create_2d_double_array(n_rdf_pairs,maxbin,"rdf:gr_ave"); + ncoord_ave = memory->create_2d_double_array(n_rdf_pairs,maxbin, + "rdf:nccord_ave"); +} + +/* ---------------------------------------------------------------------- */ + +FixRDF::~FixRDF() +{ + if (me == 0) fclose(fp); + + memory->destroy_2d_int_array(rdfpair); + memory->destroy_2d_int_array(hist); + memory->destroy_2d_int_array(hist_all); + delete [] nrdfatoms; + memory->destroy_2d_double_array(gr_ave); + memory->destroy_2d_double_array(ncoord_ave); +} + +/* ---------------------------------------------------------------------- */ + +int FixRDF::setmask() +{ + int mask = 0; + mask |= END_OF_STEP; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixRDF::init() +{ + if (force->pair) delr = force->pair->cutforce / maxbin; + else error->all("Fix rdf requires a pair style be defined"); + + delrinv = 1.0/delr; + nframes = 0; + + // set running averages to 0.0 + + int i,j,bin,irdf; + + for (i = 1; i <= atom->ntypes; i++) + for (j = 1; j <= atom->ntypes; j++) + if (rdfpair[i][j]) { + irdf = rdfpair[i][j] - 1; + for (bin = 0; bin < maxbin; bin++) + gr_ave[irdf][bin] = ncoord_ave[irdf][bin] = 0.0; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixRDF::setup() +{ + if (first) end_of_step(); + first = 0; +} + +/* ---------------------------------------------------------------------- */ + +void FixRDF::end_of_step() +{ + double **x = atom->x; + int *mask = atom->mask; + int nlocal = atom->nlocal; + int *type = atom->type; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int nall = atom->nlocal + atom->nghost; + int newton_pair = force->newton_pair; + + int i,j,k,numneigh,itype,jtype,ipair,jpair,bin; + double xtmp,ytmp,ztmp,delx,dely,delz,r; + int *neighs; + + // if needed, build a half neighbor list + + if (!neighbor->half_every) neighbor->build_half(); + + // zero the histogram counts + + for (int i = 0; i < n_rdf_pairs; i++) + for (int j = 0; j < maxbin; j++) + hist[i][j] = 0; + + // tally the RDF + // both atom i and j must be in fix group + // itype,jtype must have been specified by user + // weighting factor must be != 0.0 for this pair + // could be 0 and still be in neigh list for long-range Coulombics + // count the interaction once even if neighbor pair is stored on 2 procs + // if itype = jtype, count the interaction twice + + for (i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + if (j >= nall) { + if (special_coul[j/nall] == 0.0 && special_lj[j/nall] == 0.0) + continue; + j %= nall; + } + if (mask[j] & groupbit) { + jtype = type[j]; + ipair = rdfpair[itype][jtype]; + jpair = rdfpair[jtype][itype]; + if (!ipair && !jpair) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + r = sqrt(delx*delx + dely*dely + delz*delz); + bin = static_cast<int> (r * delrinv); + if (bin >= maxbin) continue; + + if (ipair) hist[ipair-1][bin]++; + if (newton_pair || j < nlocal) + if (jpair) hist[jpair-1][bin]++; + } + } + } + } + + // sum histogram across procs + + MPI_Allreduce(hist[0],hist_all[0],n_rdf_pairs*maxbin,MPI_INT,MPI_SUM,world); + nframes++; + + if (me == 0) { + + // print RDF for current snapshot + + double rlower,rcenter,rupper,nideal,gr; + double PI = 4.0*atan(1.0); + double constant = 4.0*PI / (3.0*domain->xprd*domain->yprd*domain->zprd); + int irdf; + + fprintf(fp,"%s %d \n","TIMESTEP", update->ntimestep); + fprintf(fp,"%s","r"); + + for (int i = 1; i <= atom->ntypes; i++) + for (int j = 1; j <= atom->ntypes; j++) + if (rdfpair[i][j]) + fprintf(fp,", %s%d,%d%s, %s%d,%d%s", + "g(",i,j,",r)","ncoord(",i,j,",r)"); + + double *ncoord = new double[n_rdf_pairs]; + for (int i = 0; i < n_rdf_pairs; i++) ncoord[i] = 0; + + for (bin = 0; bin < maxbin; bin++) { + rlower = bin*delr; + rcenter = rlower + 0.5*delr; + rupper = rlower + delr; + fprintf(fp,"\n%f ",rcenter); + for (int i = 1; i <= atom->ntypes; i++) + for (int j = 1; j <= atom->ntypes; j++) + if (rdfpair[i][j]) { + nideal = constant*nrdfatoms[j] * + (rupper*rupper*rupper - rlower*rlower*rlower); + irdf = rdfpair[i][j] - 1; + gr = hist_all[irdf][bin] / (nrdfatoms[i]*nideal); + ncoord[irdf] += gr*nideal; + fprintf(fp,"%f %f ",gr,ncoord[irdf]); + gr_ave[irdf][bin] = + ((nframes-1)*gr_ave[irdf][bin] + gr) / nframes; + ncoord_ave[irdf][bin] = + ((nframes-1)*ncoord_ave[irdf][bin] + ncoord[irdf]) / nframes; + } + } + + fprintf(fp,"\n"); + delete [] ncoord; + + // if last time in run that RDF is computed, print running averages + + if (update->ntimestep + nevery > update->endstep) { + + fprintf(fp,"%s \n","RUN AVERAGE"); + fprintf(fp,"%s","r"); + + for (int i = 1; i <= atom->ntypes; i++) + for (int j = 1; j <= atom->ntypes; j++) + if (rdfpair[i][j]) + fprintf(fp,", %s%d,%d%s, %s%d,%d%s", + "g(",i,j,",r)","ncoord(",i,j,",r)"); + + for (bin = 0; bin < maxbin; bin++) { + rlower = bin*delr; + rcenter = rlower + 0.5*delr; + rupper = rlower + delr; + fprintf(fp,"\n%f ",rcenter); + for (int i = 1; i <= atom->ntypes; i++) + for (int j = 1; j <= atom->ntypes; j++) + if (rdfpair[i][j]) { + irdf = rdfpair[i][j] - 1; + fprintf(fp,"%f %f ",gr_ave[irdf][bin],ncoord_ave[irdf][bin]); + } + } + fprintf(fp,"\n"); + } + } +} diff --git a/src/fix_rdf.h b/src/fix_rdf.h new file mode 100644 index 0000000000000000000000000000000000000000..eae2d8add87cf986aa43419ffa632e7de70e5314 --- /dev/null +++ b/src/fix_rdf.h @@ -0,0 +1,42 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_RDF_H +#define FIX_RDF_H + +#include "stdio.h" +#include "fix.h" + +class FixRDF : public Fix { + public: + FixRDF(int, char **); + ~FixRDF(); + int setmask(); + void init(); + void setup(); + void end_of_step(); + + private: + int me,first; + FILE *fp; + int maxbin; // # of rdf bins + int n_rdf_pairs; // # of rdf pairs + int nframes; // # of rdf snapshots taken + double delr,delrinv; // bin width and its inverse + int **rdfpair; // mapping from 2 types to rdf pair + int **hist,**hist_all; // histogram bins + int *nrdfatoms; // # of atoms of each type in the group + double **gr_ave,**ncoord_ave; // accumulators for average rdf statistics +}; + +#endif diff --git a/src/fix_recenter.cpp b/src/fix_recenter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b369f23f3b3a29ddd0407e5023fed33c4d800061 --- /dev/null +++ b/src/fix_recenter.cpp @@ -0,0 +1,166 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Naveen Michaud-Agrawal (Johns Hopkins U) +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "string.h" +#include "fix_recenter.h" +#include "atom.h" +#include "group.h" +#include "domain.h" +#include "modify.h" +#include "comm.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixRecenter::FixRecenter(int narg, char **arg) : Fix(narg, arg) +{ + if (narg < 6) error->all("Illegal fix recenter command"); + + xcom = ycom = zcom = 0.0; + xflag = yflag = zflag = 1; + xinitflag = yinitflag = zinitflag = 0; + + if (strcmp(arg[3],"NULL") == 0) xflag = 0; + else if (strcmp(arg[3],"INIT") == 0) xinitflag = 1; + else xcom = atof(arg[3]); + if (strcmp(arg[4],"NULL") == 0) yflag = 0; + else if (strcmp(arg[4],"INIT") == 0) yinitflag = 1; + else ycom = atof(arg[4]); + if (strcmp(arg[5],"NULL") == 0) zflag = 0; + else if (strcmp(arg[5],"INIT") == 0) zinitflag = 1; + else zcom = atof(arg[5]); + + // optional args + + group2bit = groupbit; + scaleflag = 1; + + int iarg = 6; + while (iarg < narg) { + if (strcmp(arg[iarg],"shift") == 0) { + int igroup2 = group->find(arg[iarg]); + if (igroup2 < 0) error->all("Could not find fix recenter group ID"); + group2bit = group->bitmask[igroup2]; + iarg += 2; + } else if (strcmp(arg[iarg],"units") == 0) { + if (strcmp(arg[iarg+1],"box") == 0) scaleflag = 0; + else if (strcmp(arg[iarg+1],"lattice") == 0) scaleflag = 1; + else if (strcmp(arg[iarg+1],"fraction") == 0) scaleflag = 2; + else error->all("Illegal fix indent command"); + iarg += 2; + } else error->all("Illegal fix recenter command"); + } + + // scale xcom,ycom,zcom + + double xscale,yscale,zscale; + if (scaleflag == 1) { + xscale = domain->xlattice; + yscale = domain->ylattice; + zscale = domain->zlattice; + } + else xscale = yscale = zscale = 1.0; + + xcom *= xscale; + ycom *= yscale; + zcom *= zscale; + + // cannot have 0 atoms in group + + if (group->count(igroup) == 0.0) + error->all("Fix recenter group has no atoms"); +} + +/* ---------------------------------------------------------------------- */ + +int FixRecenter::setmask() +{ + int mask = 0; + mask |= INITIAL_INTEGRATE; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixRecenter::init() +{ + // warn if any integrate fix comes after this one + + int after = 0; + int flag = 0; + for (int i = 0; i < modify->nfix; i++) { + if (strcmp(id,modify->fix[i]->id) == 0) after = 1; + else if ((modify->fmask[i] & INITIAL_INTEGRATE) && after) flag = 1; + } + if (flag && comm->me == 0) + error->warning("Fix recenter should come after all other integration fixes"); + + masstotal = group->mass(igroup); + + // if any components of requested COM were INIT, store initial COM + + if (xinitflag || yinitflag || zinitflag) { + double xcm[3]; + group->xcm(igroup,masstotal,xcm); + xinit = xcm[0]; + yinit = xcm[1]; + zinit = xcm[2]; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixRecenter::initial_integrate() +{ + // target COM + + double xtarget,ytarget,ztarget; + + if (xinitflag) xtarget = xinit; + else if (scaleflag == 2) + xtarget = domain->boxxlo + xcom*(domain->boxxhi - domain->boxxlo); + else xtarget = xcom; + + if (yinitflag) ytarget = yinit; + else if (scaleflag == 2) + ytarget = domain->boxylo + ycom*(domain->boxyhi - domain->boxylo); + else ytarget = ycom; + + if (zinitflag) ztarget = zinit; + else if (scaleflag == 2) + ztarget = domain->boxzlo + zcom*(domain->boxzhi - domain->boxzlo); + else ztarget = zcom; + + // current COM + + double xcm[3]; + group->xcm(igroup,masstotal,xcm); + + // shift coords by difference between actual COM and requested COM + + double **x = atom->x; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & group2bit) { + if (xflag) x[i][0] += xtarget - xcm[0]; + if (yflag) x[i][1] += ytarget - xcm[1]; + if (zflag) x[i][2] += ztarget - xcm[2]; + } +} diff --git a/src/fix_recenter.h b/src/fix_recenter.h new file mode 100644 index 0000000000000000000000000000000000000000..da33a6884461834bc1ec199b00a201ebaf85af85 --- /dev/null +++ b/src/fix_recenter.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_RECENTER_H +#define FIX_RECENTER_H + +#include "fix.h" + +class FixRecenter : public Fix { + public: + FixRecenter(int, char **); + ~FixRecenter() {} + int setmask(); + void init(); + void initial_integrate(); + + private: + int group2bit,scaleflag; + int xflag,yflag,zflag; + int xinitflag,yinitflag,zinitflag; + double xcom,ycom,zcom,xinit,yinit,zinit,masstotal; +}; + +#endif diff --git a/src/fix_respa.cpp b/src/fix_respa.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6215c114bf05c8d972a09f8e81cb0e647d4de6a6 --- /dev/null +++ b/src/fix_respa.cpp @@ -0,0 +1,118 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "fix_respa.h" +#include "atom.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixRespa::FixRespa(int narg, char **arg) : Fix(narg, arg) +{ + // nlevels = # of rRESPA levels + + nlevels = atoi(arg[3]); + + // perform initial allocation of atom-based arrays + // register with atom class + + f_level = NULL; + grow_arrays(atom->nmax); + atom->add_callback(0); +} + +/* ---------------------------------------------------------------------- */ + +FixRespa::~FixRespa() +{ + // 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_3d_double_array(f_level); +} + +/* ---------------------------------------------------------------------- */ + +int FixRespa::setmask() +{ + return 0; +} + +/* ---------------------------------------------------------------------- + memory usage of local atom-based arrays +------------------------------------------------------------------------- */ + +int FixRespa::memory_usage() +{ + int bytes = atom->nmax*nlevels*3 * sizeof(double); + return bytes; +} + +/* ---------------------------------------------------------------------- + allocate local atom-based arrays +------------------------------------------------------------------------- */ + +void FixRespa::grow_arrays(int nmax) +{ + f_level = + memory->grow_3d_double_array(f_level,nmax,nlevels,3,"fix_respa:f_level"); +} + +/* ---------------------------------------------------------------------- + copy values within local atom-based arrays +------------------------------------------------------------------------- */ + +void FixRespa::copy_arrays(int i, int j) +{ + for (int k = 0; k < nlevels; k++) { + f_level[j][k][0] = f_level[i][k][0]; + f_level[j][k][1] = f_level[i][k][1]; + f_level[j][k][2] = f_level[i][k][2]; + } +} + +/* ---------------------------------------------------------------------- + pack values in local atom-based arrays for exchange with another proc +------------------------------------------------------------------------- */ + +int FixRespa::pack_exchange(int i, double *buf) +{ + int m = 0; + for (int k = 0; k < nlevels; k++) { + buf[m++] = f_level[i][k][0]; + buf[m++] = f_level[i][k][1]; + buf[m++] = f_level[i][k][2]; + } + return m; +} + +/* ---------------------------------------------------------------------- + unpack values in local atom-based arrays from exchange with another proc +------------------------------------------------------------------------- */ + +int FixRespa::unpack_exchange(int nlocal, double *buf) +{ + int m = 0; + for (int k = 0; k < nlevels; k++) { + f_level[nlocal][k][0] = buf[m++]; + f_level[nlocal][k][1] = buf[m++]; + f_level[nlocal][k][2] = buf[m++]; + } + return m; +} diff --git a/src/fix_respa.h b/src/fix_respa.h new file mode 100644 index 0000000000000000000000000000000000000000..acc2fb81c2a201ef22ab76a93d8474f909a8eb59 --- /dev/null +++ b/src/fix_respa.h @@ -0,0 +1,40 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_RESPA_H +#define FIX_RESPA_H + +#include "fix.h" + +class FixRespa : public Fix { + friend class Respa; + friend class FixShake; + + public: + FixRespa(int, char **); + ~FixRespa(); + int setmask(); + void init() {} + + int memory_usage(); + void grow_arrays(int); + void copy_arrays(int, int); + int pack_exchange(int, double *); + int unpack_exchange(int, double *); + + private: + int nlevels; + double ***f_level; // force at each rRESPA level +}; + +#endif diff --git a/src/fix_rigid.cpp b/src/fix_rigid.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4f6a1a721aa8ea7fc0f959a50a8d776f35fbe5fb --- /dev/null +++ b/src/fix_rigid.cpp @@ -0,0 +1,1282 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "string.h" +#include "fix_rigid.h" +#include "atom.h" +#include "domain.h" +#include "update.h" +#include "respa.h" +#include "modify.h" +#include "group.h" +#include "comm.h" +#include "force.h" +#include "output.h" +#include "memory.h" +#include "error.h" + +#define TOLERANCE 1.0e-6 +#define EPSILON 1.0e-7 +#define MAXJACOBI 50 + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +/* ---------------------------------------------------------------------- */ + +FixRigid::FixRigid(int narg, char **arg) : Fix(narg, arg) +{ + int i,ibody; + + // can't use with pure granular style since mass arrays are different + // hybrid granular style would be OK if fix were on non-granular atoms + + if (strcmp(atom->style,"granular") == 0) + error->all("Cannot use fix rigid with atom style granular"); + + // perform initial allocation of atom-based arrays + // register with atom class + + body = NULL; + displace = NULL; + grow_arrays(atom->nmax); + atom->add_callback(0); + + // parse command-line args + // set nbody and body[i] for each atom + + if (narg < 4) error->all("Illegal fix rigid command"); + + // single rigid body + // nbody = 1 + // all atoms in fix group are part of body + + if (strcmp(arg[3],"single") == 0) { + if (narg != 4) error->all("Illegal fix rigid command"); + + nbody = 1; + + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (i = 0; i < nlocal; i++) { + body[i] = -1; + if (mask[i] & groupbit) body[i] = 0; + } + + // each molecule in fix group is a rigid body + // maxmol = largest molecule # + // ncount = # of atoms in each molecule (have to sum across procs) + // nbody = # of non-zero ncount values + // use nall as incremented ptr to set body[] values for each atom + + } else if (strcmp(arg[3],"molecule") == 0) { + if (narg != 4) error->all("Illegal fix rigid command"); + if (atom->molecular == 0) + error->all("Must use a molecular atom style with fix rigid 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++) { + body[i] = -1; + if (mask[i] & groupbit) body[i] = nall[molecule[i]]; + } + + delete [] ncount; + delete [] nall; + + // each listed group is a rigid body + // check if all listed groups exist + // an atom must belong to fix group and listed group to be in rigid body + // error if atom belongs to more than 1 rigid body + + } else if (strcmp(arg[3],"group") == 0) { + nbody = narg-4; + if (nbody <= 0) error->all("Illegal fix rigid 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("Could not find fix rigid group ID"); + } + + int *mask = atom->mask; + int nlocal = atom->nlocal; + + int flag = 0; + for (i = 0; i < nlocal; i++) { + body[i] = -1; + if (mask[i] & groupbit) + for (ibody = 0; ibody < nbody; ibody++) + if (mask[i] & group->bitmask[igroups[ibody]]) { + if (body[i] >= 0) flag = 1; + body[i] = ibody; + } + } + + int flagall; + MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_SUM,world); + if (flagall) + error->all("One or more atoms belong to multiple rigid bodies"); + + delete [] igroups; + + } else error->all("Illegal fix rigid command"); + + // error check on nbody + + if (nbody == 0) error->all("No rigid bodies defined by fix rigid"); + + // create all nbody-length arrays + + nrigid = (int *) memory->smalloc(nbody*sizeof(int),"rigid:nrigid"); + masstotal = (double *) + memory->smalloc(nbody*sizeof(double),"rigid:masstotal"); + xcm = memory->create_2d_double_array(nbody,3,"rigid:xcm"); + vcm = memory->create_2d_double_array(nbody,3,"rigid:vcm"); + fcm = memory->create_2d_double_array(nbody,3,"rigid:fcm"); + inertia = memory->create_2d_double_array(nbody,3,"rigid:inertia"); + ex_space = memory->create_2d_double_array(nbody,3,"rigid:ex_space"); + ey_space = memory->create_2d_double_array(nbody,3,"rigid:ey_space"); + ez_space = memory->create_2d_double_array(nbody,3,"rigid:ez_space"); + angmom = memory->create_2d_double_array(nbody,3,"rigid:angmom"); + omega = memory->create_2d_double_array(nbody,3,"rigid:omega"); + torque = memory->create_2d_double_array(nbody,3,"rigid:torque"); + quat = memory->create_2d_double_array(nbody,4,"rigid:quat"); + + sum = memory->create_2d_double_array(nbody,6,"rigid:sum"); + all = memory->create_2d_double_array(nbody,6,"rigid:all"); + + // nrigid[n] = # of atoms in Nth rigid body + // error if one or zero atoms + + int *ncount = new int[nbody]; + for (ibody = 0; ibody < nbody; ibody++) ncount[ibody] = 0; + + int nlocal = atom->nlocal; + + for (i = 0; i < nlocal; i++) + if (body[i] >= 0) ncount[body[i]]++; + + MPI_Allreduce(ncount,nrigid,nbody,MPI_INT,MPI_SUM,world); + delete [] ncount; + + for (ibody = 0; ibody < nbody; ibody++) + if (nrigid[ibody] <= 1) error->all("One or zero atoms in rigid body"); + + // print statistics + + int nsum = 0; + for (ibody = 0; ibody < nbody; ibody++) nsum += nrigid[ibody]; + + if (comm->me == 0) { + if (screen) fprintf(screen,"%d rigid bodies with %d atoms\n",nbody,nsum); + if (logfile) fprintf(logfile,"%d rigid bodies with %d atoms\n",nbody,nsum); + } + + // zero fix_rigid virial in case pressure uses it before 1st fix_rigid call + + for (int n = 0; n < 6; n++) virial[n] = 0.0; +} + +/* ---------------------------------------------------------------------- */ + +FixRigid::~FixRigid() +{ + // 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->sfree(body); + memory->destroy_2d_double_array(displace); + + // delete nbody-length arrays + + memory->sfree(nrigid); + memory->sfree(masstotal); + memory->destroy_2d_double_array(xcm); + memory->destroy_2d_double_array(vcm); + memory->destroy_2d_double_array(fcm); + memory->destroy_2d_double_array(inertia); + memory->destroy_2d_double_array(ex_space); + memory->destroy_2d_double_array(ey_space); + memory->destroy_2d_double_array(ez_space); + memory->destroy_2d_double_array(angmom); + memory->destroy_2d_double_array(omega); + memory->destroy_2d_double_array(torque); + memory->destroy_2d_double_array(quat); + + memory->destroy_2d_double_array(sum); + memory->destroy_2d_double_array(all); +} + +/* ---------------------------------------------------------------------- */ + +int FixRigid::setmask() +{ + int mask = 0; + mask |= INITIAL_INTEGRATE; + mask |= FINAL_INTEGRATE; + mask |= INITIAL_INTEGRATE_RESPA; + mask |= FINAL_INTEGRATE_RESPA; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixRigid::init() +{ + int i,ibody; + + // warn if more than one rigid fix + + int count = 0; + for (int i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"rigid") == 0) count++; + if (count > 1 && comm->me == 0) error->warning("More than one rigid fix"); + + // error if npt,nph fix comes before rigid fix + + for (i = 0; i < modify->nfix; i++) { + if (strcmp(modify->fix[i]->style,"npt") == 0) break; + if (strcmp(modify->fix[i]->style,"nph") == 0) break; + } + if (i < modify->nfix) { + for (int j = i; j < modify->nfix; j++) + if (strcmp(modify->fix[j]->style,"rigid") == 0) + error->all("Rigid fix must come before NPT/NPH fix"); + } + + // compute rigid contribution to virial every step if fix NPT,NPH exists + + pressure_flag = 0; + for (int i = 0; i < modify->nfix; i++) { + if (strcmp(modify->fix[i]->style,"npt") == 0) pressure_flag = 1; + if (strcmp(modify->fix[i]->style,"nph") == 0) pressure_flag = 1; + } + + // timestep info + + dtv = update->dt; + dtf = 0.5 * update->dt * force->ftm2v; + dtq = 0.5 * update->dt; + + if (strcmp(update->integrate_style,"respa") == 0) + step_respa = ((Respa *) update->integrate)->step; + + // compute masstotal & center-of-mass of each rigid body + + int *type = atom->type; + int *image = atom->image; + double *mass = atom->mass; + double **x = atom->x; + 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; + int xbox,ybox,zbox; + double massone; + + for (i = 0; i < nlocal; i++) { + if (body[i] < 0) continue; + ibody = body[i]; + + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + 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; + } + + MPI_Allreduce(sum[0],all[0],6*nbody,MPI_DOUBLE,MPI_SUM,world); + + for (ibody = 0; ibody < nbody; ibody++) { + masstotal[ibody] = all[ibody][3]; + xcm[ibody][0] = all[ibody][0]/masstotal[ibody]; + xcm[ibody][1] = all[ibody][1]/masstotal[ibody]; + xcm[ibody][2] = all[ibody][2]/masstotal[ibody]; + } + + // compute 6 moments of inertia of each body + // dx,dy,dz = coords relative to center-of-mass + + for (ibody = 0; ibody < nbody; ibody++) + for (i = 0; i < 6; i++) sum[ibody][i] = 0.0; + double dx,dy,dz; + + for (i = 0; i < nlocal; i++) { + if (body[i] < 0) continue; + ibody = body[i]; + + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + 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 = memory->create_2d_double_array(3,3,"fix_rigid:tensor"); + double **evectors = memory->create_2d_double_array(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("Insufficient Jacobi rotations for rigid body"); + + ex_space[ibody][0] = evectors[0][0]; + ex_space[ibody][1] = evectors[1][0]; + ex_space[ibody][2] = evectors[2][0]; + + ey_space[ibody][0] = evectors[0][1]; + ey_space[ibody][1] = evectors[1][1]; + ey_space[ibody][2] = evectors[2][1]; + + ez_space[ibody][0] = evectors[0][2]; + ez_space[ibody][1] = evectors[1][2]; + ez_space[ibody][2] = evectors[2][2]; + + // if any principal moment < scaled EPSILON, set to 0.0 + + double max; + max = MAX(inertia[ibody][0],inertia[ibody][1]); + max = MAX(max,inertia[ibody][2]); + + if (inertia[ibody][0] < EPSILON*max) inertia[ibody][0] = 0.0; + if (inertia[ibody][1] < EPSILON*max) inertia[ibody][1] = 0.0; + if (inertia[ibody][2] < EPSILON*max) inertia[ibody][2] = 0.0; + + // enforce 3 evectors as a right-handed coordinate system + // flip 3rd 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]; + } + + // create initial quaternion + + qcreate(evectors,quat[ibody]); + } + + // free temporary memory + + memory->destroy_2d_double_array(tensor); + memory->destroy_2d_double_array(evectors); + + // displace = initial atom coords in basis of principal axes + // set displace = 0.0 for atoms not in any rigid body + + for (i = 0; i < nlocal; i++) { + if (body[i] < 0) displace[i][0] = displace[i][1] = displace[i][2] = 0.0; + else { + ibody = body[i]; + + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + 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]; + } + } + + // test for valid principal moments & axes + // recompute moments of inertia around new axes + // 3 diagonal moments should equal principal moments + // 3 off-diagonal moments should be 0.0 + + for (ibody = 0; ibody < nbody; ibody++) + for (i = 0; i < 6; i++) sum[ibody][i] = 0.0; + + for (i = 0; i < nlocal; i++) { + if (body[i] < 0) continue; + ibody = body[i]; + massone = mass[type[i]]; + + sum[ibody][0] += massone * + (displace[i][1]*displace[i][1] + displace[i][2]*displace[i][2]); + sum[ibody][1] += massone * + (displace[i][0]*displace[i][0] + displace[i][2]*displace[i][2]); + sum[ibody][2] += massone * + (displace[i][0]*displace[i][0] + displace[i][1]*displace[i][1]); + sum[ibody][3] -= massone * displace[i][0]*displace[i][1]; + sum[ibody][4] -= massone * displace[i][1]*displace[i][2]; + sum[ibody][5] -= massone * displace[i][0]*displace[i][2]; + } + + 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("Bad principal moments"); + if (fabs(all[ibody][3]) > TOLERANCE || + fabs(all[ibody][4]) > TOLERANCE || + fabs(all[ibody][5]) > TOLERANCE) + error->all("Bad principal moments"); + } +} + +/* ---------------------------------------------------------------------- */ + +void FixRigid::setup() +{ + int i,ibody; + + // vcm = velocity of center-of-mass of each rigid body + // fcm = force on center-of-mass of each rigid body + + int *type = atom->type; + double **v = atom->v; + double **f = atom->f; + double *mass = atom->mass; + int nlocal = atom->nlocal; + + for (ibody = 0; ibody < nbody; ibody++) + for (i = 0; i < 6; i++) sum[ibody][i] = 0.0; + double massone; + + for (i = 0; i < nlocal; i++) { + if (body[i] < 0) continue; + ibody = body[i]; + massone = mass[type[i]]; + sum[ibody][0] += v[i][0] * massone; + sum[ibody][1] += v[i][1] * massone; + sum[ibody][2] += v[i][2] * massone; + sum[ibody][3] += f[i][0]; + sum[ibody][4] += f[i][1]; + sum[ibody][5] += f[i][2]; + } + + MPI_Allreduce(sum[0],all[0],6*nbody,MPI_DOUBLE,MPI_SUM,world); + + for (ibody = 0; ibody < nbody; ibody++) { + vcm[ibody][0] = all[ibody][0]/masstotal[ibody]; + vcm[ibody][1] = all[ibody][1]/masstotal[ibody]; + vcm[ibody][2] = all[ibody][2]/masstotal[ibody]; + fcm[ibody][0] = all[ibody][3]; + fcm[ibody][1] = all[ibody][4]; + fcm[ibody][2] = all[ibody][5]; + } + + // angmom = angular momentum of each rigid body + // torque = torque on each rigid body + + int *image = atom->image; + double **x = atom->x; + + 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; + int xbox,ybox,zbox; + double dx,dy,dz; + + for (i = 0; i < nlocal; i++) { + if (body[i] < 0) continue; + ibody = body[i]; + + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + 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] += dy * massone*v[i][2] - dz * massone*v[i][1]; + sum[ibody][1] += dz * massone*v[i][0] - dx * massone*v[i][2]; + sum[ibody][2] += dx * massone*v[i][1] - dy * massone*v[i][0]; + sum[ibody][3] += dy * f[i][2] - dz * f[i][1]; + sum[ibody][4] += dz * f[i][0] - dx * f[i][2]; + sum[ibody][5] += dx * f[i][1] - dy * f[i][0]; + } + + MPI_Allreduce(sum[0],all[0],6*nbody,MPI_DOUBLE,MPI_SUM,world); + + for (ibody = 0; ibody < nbody; ibody++) { + angmom[ibody][0] = all[ibody][0]; + angmom[ibody][1] = all[ibody][1]; + angmom[ibody][2] = all[ibody][2]; + torque[ibody][0] = all[ibody][3]; + torque[ibody][1] = all[ibody][4]; + torque[ibody][2] = all[ibody][5]; + } + + // set velocities from angmom & omega + // guestimate virial as 2x the set_v contribution + + for (ibody = 0; ibody < nbody; ibody++) + omega_from_mq(angmom[ibody],ex_space[ibody],ey_space[ibody], + ez_space[ibody],inertia[ibody],omega[ibody]); + + for (int n = 0; n < 6; n++) virial[n] = 0.0; + set_v(1); + for (int n = 0; n < 6; n++) virial[n] *= 2.0; +} + +/* ---------------------------------------------------------------------- */ + +void FixRigid::initial_integrate() +{ + double dtfm; + + for (int ibody = 0; ibody < nbody; ibody++) { + + // update vcm by 1/2 step + + dtfm = dtf / masstotal[ibody]; + vcm[ibody][0] += dtfm * fcm[ibody][0]; + vcm[ibody][1] += dtfm * fcm[ibody][1]; + vcm[ibody][2] += dtfm * fcm[ibody][2]; + + // update xcm by full step + + xcm[ibody][0] += dtv * vcm[ibody][0]; + xcm[ibody][1] += dtv * vcm[ibody][1]; + xcm[ibody][2] += dtv * vcm[ibody][2]; + + // update angular momentum by 1/2 step + + angmom[ibody][0] += dtf * torque[ibody][0]; + angmom[ibody][1] += dtf * torque[ibody][1]; + angmom[ibody][2] += dtf * torque[ibody][2]; + + // update quaternion a full step + // returns new normalized quat + // returns ex_space,ey_space,ez_space for new quat + // returns omega at 1/2 step (depends on angmom and quat) + + richardson(quat[ibody],omega[ibody],angmom[ibody],inertia[ibody], + ex_space[ibody],ey_space[ibody],ez_space[ibody]); + } + + // set coords and velocities if atoms in rigid bodies + // from quarternion and omega + + int vflag = 0; + if (pressure_flag || output->next_thermo == update->ntimestep) vflag = 1; + set_xv(vflag); +} + +/* ---------------------------------------------------------------------- */ + +void FixRigid::richardson(double *q, double *w, + double *m, double *moments, + double *ex, double *ey, double *ez) +{ + // compute omega at 1/2 step from m at 1/2 step and q at 0 + + omega_from_mq(m,ex,ey,ez,moments,w); + + // full update from dq/dt = 1/2 w q + + double wq[4]; + multiply(w,q,wq); + + double qfull[4]; + qfull[0] = q[0] + dtq * wq[0]; + qfull[1] = q[1] + dtq * wq[1]; + qfull[2] = q[2] + dtq * wq[2]; + qfull[3] = q[3] + dtq * wq[3]; + + normalize(qfull); + + // 1st half update from dq/dt = 1/2 w q + + double qhalf[4]; + qhalf[0] = q[0] + 0.5*dtq * wq[0]; + qhalf[1] = q[1] + 0.5*dtq * wq[1]; + qhalf[2] = q[2] + 0.5*dtq * wq[2]; + qhalf[3] = q[3] + 0.5*dtq * wq[3]; + + normalize(qhalf); + + // udpate ex,ey,ez from qhalf + // re-compute omega at 1/2 step from m at 1/2 step and q at 1/2 step + // recompute wq + + exyz_from_q(qhalf,ex,ey,ez); + omega_from_mq(m,ex,ey,ez,moments,w); + multiply(w,qhalf,wq); + + // 2nd half update from dq/dt = 1/2 w q + + qhalf[0] += 0.5*dtq * wq[0]; + qhalf[1] += 0.5*dtq * wq[1]; + qhalf[2] += 0.5*dtq * wq[2]; + qhalf[3] += 0.5*dtq * wq[3]; + + normalize(qhalf); + + // corrected Richardson update + + q[0] = 2.0*qhalf[0] - qfull[0]; + q[1] = 2.0*qhalf[1] - qfull[1]; + q[2] = 2.0*qhalf[2] - qfull[2]; + q[3] = 2.0*qhalf[3] - qfull[3]; + + normalize(q); + exyz_from_q(q,ex,ey,ez); +} + +/* ---------------------------------------------------------------------- */ + +void FixRigid::final_integrate() +{ + int i,ibody; + double dtfm; + + // sum forces and torques on atoms in rigid body + + int *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; + + int xbox,ybox,zbox; + 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 (body[i] < 0) continue; + ibody = body[i]; + + sum[ibody][0] += f[i][0]; + sum[ibody][1] += f[i][1]; + sum[ibody][2] += f[i][2]; + + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + 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 by 1/2 step + + dtfm = dtf / masstotal[ibody]; + vcm[ibody][0] += dtfm * fcm[ibody][0]; + vcm[ibody][1] += dtfm * fcm[ibody][1]; + vcm[ibody][2] += dtfm * fcm[ibody][2]; + + // update angular momentum by 1/2 step + + angmom[ibody][0] += dtf * torque[ibody][0]; + angmom[ibody][1] += dtf * torque[ibody][1]; + angmom[ibody][2] += dtf * torque[ibody][2]; + } + + // 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]); + + int vflag = 0; + if (pressure_flag || output->next_thermo == update->ntimestep) vflag = 1; + set_v(vflag); +} + +/* ---------------------------------------------------------------------- */ + +void FixRigid::initial_integrate_respa(int ilevel, int flag) +{ + if (flag) return; // only used by NPT,NPH + + dtv = step_respa[ilevel]; + dtf = 0.5 * step_respa[ilevel] * force->ftm2v; + dtq = 0.5 * step_respa[ilevel]; + + if (ilevel == 0) initial_integrate(); + else final_integrate(); +} + +/* ---------------------------------------------------------------------- */ + +void FixRigid::final_integrate_respa(int ilevel) +{ + dtf = 0.5 * step_respa[ilevel] * force->ftm2v; + final_integrate(); +} + +/* ---------------------------------------------------------------------- + count # of degrees-of-freedom removed by fix_rigid for atoms in igroup +------------------------------------------------------------------------- */ + +int FixRigid::dof(int igroup) +{ + int groupbit = group->bitmask[igroup]; + + // ncount = # of atoms in each rigid body that are also in group + + 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 (body[i] >= 0 && mask[i] & groupbit) ncount[body[i]]++; + + 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; + + delete [] ncount; + delete [] nall; + + return n; +} + +/* ---------------------------------------------------------------------- + adjust xcm of each rigid body due to box dilation in idim + called by various fixes that change box +------------------------------------------------------------------------- */ + +void FixRigid::dilate(int idim, + double oldlo, double oldhi, double newlo, double newhi) +{ + double ratio; + for (int ibody = 0; ibody < nbody; ibody++) { + ratio = (xcm[ibody][idim] - oldlo) / (oldhi - oldlo); + xcm[ibody][idim] = newlo + ratio*(newhi - newlo); + } +} + +/* ---------------------------------------------------------------------- + compute evalues and evectors of 3x3 real symmetric matrix + based on Jacobi rotations + adapted from Numerical Recipes jacobi() function +------------------------------------------------------------------------- */ + +int FixRigid::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 FixRigid::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); +} + +/* ---------------------------------------------------------------------- + create quaternion from rotation matrix (evectors) +------------------------------------------------------------------------- */ + +void FixRigid::qcreate(double **a, double *q) +{ + // squares of quaternion components + + double q0sq = 0.25 * (a[0][0] + a[1][1] + a[2][2] + 1.0); + double q1sq = q0sq - 0.5 * (a[1][1] + a[2][2]); + double q2sq = q0sq - 0.5 * (a[0][0] + a[2][2]); + double q3sq = q0sq - 0.5 * (a[0][0] + a[1][1]); + + // some component must be greater than 1/4 since they sum to 1 + // compute other components from it + + if (q0sq >= 0.25) { + q[0] = sqrt(q0sq); + q[1] = (a[2][1] - a[1][2]) / (4.0*q[0]); + q[2] = (a[0][2] - a[2][0]) / (4.0*q[0]); + q[3] = (a[1][0] - a[0][1]) / (4.0*q[0]); + } else if (q1sq >= 0.25) { + q[1] = sqrt(q1sq); + q[0] = (a[2][1] - a[1][2]) / (4.0*q[1]); + q[2] = (a[0][1] + a[1][0]) / (4.0*q[1]); + q[3] = (a[2][0] + a[0][2]) / (4.0*q[1]); + } else if (q2sq >= 0.25) { + q[2] = sqrt(q2sq); + q[0] = (a[0][2] - a[2][0]) / (4.0*q[2]); + q[1] = (a[0][1] + a[1][0]) / (4.0*q[2]); + q[2] = (a[1][2] + a[2][1]) / (4.0*q[2]); + } else if (q3sq >= 0.25) { + q[3] = sqrt(q3sq); + q[0] = (a[1][0] - a[0][1]) / (4.0*q[3]); + q[1] = (a[0][2] + a[2][0]) / (4.0*q[3]); + q[2] = (a[1][2] + a[2][1]) / (4.0*q[3]); + } else + error->all("Quaternion creation numeric error"); + + normalize(q); +} + +/* ---------------------------------------------------------------------- + quaternion multiply: c = a*b where a = (0,a) +------------------------------------------------------------------------- */ + +void FixRigid::multiply(double *a, double *b, double *c) +{ + c[0] = -(a[0]*b[1] + a[1]*b[2] + a[2]*b[3]); + c[1] = b[0]*a[0] + a[1]*b[3] - a[2]*b[2]; + c[2] = b[0]*a[1] + a[2]*b[1] - a[0]*b[3]; + c[3] = b[0]*a[2] + a[0]*b[2] - a[1]*b[1]; +} + +/* ---------------------------------------------------------------------- + normalize a quaternion +------------------------------------------------------------------------- */ + +void FixRigid::normalize(double *q) +{ + double norm = 1.0 / sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]); + q[0] *= norm; + q[1] *= norm; + q[2] *= norm; + q[3] *= norm; +} + +/* ---------------------------------------------------------------------- + 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 FixRigid::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]; +} + +/* ---------------------------------------------------------------------- + compute space-frame ex,ey,ez from current quaternion q + ex,ey,ez = space-frame coords of 1st,2nd,3rd principal axis + operation is ex = q' d q = Q d, where d is (1,0,0) = 1st axis in body frame +------------------------------------------------------------------------- */ + +void FixRigid::exyz_from_q(double *q, double *ex, double *ey, double *ez) +{ + ex[0] = q[0]*q[0] + q[1]*q[1] - q[2]*q[2] - q[3]*q[3]; + ex[1] = 2.0 * (q[1]*q[2] + q[0]*q[3]); + ex[2] = 2.0 * (q[1]*q[3] - q[0]*q[2]); + + ey[0] = 2.0 * (q[1]*q[2] - q[0]*q[3]); + ey[1] = q[0]*q[0] - q[1]*q[1] + q[2]*q[2] - q[3]*q[3]; + ey[2] = 2.0 * (q[2]*q[3] + q[0]*q[1]); + + ez[0] = 2.0 * (q[1]*q[3] + q[0]*q[2]); + ez[1] = 2.0 * (q[2]*q[3] - q[0]*q[1]); + ez[2] = q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3]; +} + +/* ---------------------------------------------------------------------- + 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 FixRigid::set_xv(int vflag) +{ + int *image = atom->image; + 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 ibody; + int xbox,ybox,zbox; + + double vold0,vold1,vold2,fc0,fc1,fc2,massone,x0,x1,x2; + double *mass = atom->mass; + double **f = atom->f; + int *type = atom->type; + + // zero out fix_rigid virial + + if (vflag) for (int n = 0; n < 6; n++) virial[n] = 0.0; + + for (int i = 0; i < nlocal; i++) { + if (body[i] < 0) continue; + ibody = body[i]; + + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + + // save old positions and velocities for virial contribution + + if (vflag) { + x0 = x[i][0] + xbox*xprd; + x1 = x[i][1] + ybox*yprd; + x2 = x[i][2] + zbox*zprd; + + vold0 = v[i][0]; + vold1 = v[i][1]; + vold2 = v[i][2]; + } + + 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]; + + x[i][0] += xcm[ibody][0] - xbox*xprd; + x[i][1] += xcm[ibody][1] - ybox*yprd; + x[i][2] += xcm[ibody][2] - zbox*zprd; + + // compute body constraint forces for virial + + if (vflag) { + massone = mass[type[i]]; + fc0 = massone*(v[i][0] - vold0)/dtf - f[i][0]; + fc1 = massone*(v[i][1] - vold1)/dtf - f[i][1]; + fc2 = massone*(v[i][2] - vold2)/dtf - f[i][2]; + + virial[0] += 0.5*fc0*x0; + virial[1] += 0.5*fc1*x1; + virial[2] += 0.5*fc2*x2; + virial[3] += 0.5*fc1*x0; + virial[4] += 0.5*fc2*x0; + virial[5] += 0.5*fc2*x1; + } + } +} + +/* ---------------------------------------------------------------------- + set space-frame velocity of each atom in rigid body + v = Vcm + (W cross (x - Xcm)) +------------------------------------------------------------------------- */ + +void FixRigid::set_v(int vflag) +{ + double **v = atom->v; + int nlocal = atom->nlocal; + + int ibody; + double dx,dy,dz; + + double vold0,vold1,vold2,fc0,fc1,fc2,massone,x0,x1,x2; + double *mass = atom->mass; + double **f = atom->f; + double **x = atom->x; + int *type = atom->type; + int *image = atom->image; + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + int xbox,ybox,zbox; + + for (int i = 0; i < nlocal; i++) { + if (body[i] < 0) continue; + ibody = body[i]; + + 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 (vflag) { + vold0 = v[i][0]; + vold1 = v[i][1]; + vold2 = 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]; + + // compute body constraint forces for virial + // use unwrapped atom positions + + if (vflag) { + massone = mass[type[i]]; + fc0 = massone*(v[i][0] - vold0)/dtf - f[i][0]; + fc1 = massone*(v[i][1] - vold1)/dtf - f[i][1]; + fc2 = massone*(v[i][2] - vold2)/dtf - f[i][2]; + + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + + x0 = x[i][0] + xbox*xprd; + x1 = x[i][1] + ybox*yprd; + x2 = x[i][2] + zbox*zprd; + + virial[0] += 0.5*fc0*x0; + virial[1] += 0.5*fc1*x1; + virial[2] += 0.5*fc2*x2; + virial[3] += 0.5*fc1*x0; + virial[4] += 0.5*fc2*x0; + virial[5] += 0.5*fc2*x1; + } + } +} + +/* ---------------------------------------------------------------------- + memory usage of local atom-based arrays +------------------------------------------------------------------------- */ + +int FixRigid::memory_usage() +{ + int nmax = atom->nmax; + int bytes = nmax * sizeof(int); + bytes += nmax*3 * sizeof(double); + return bytes; +} + +/* ---------------------------------------------------------------------- + allocate local atom-based arrays +------------------------------------------------------------------------- */ + +void FixRigid::grow_arrays(int nmax) +{ + body = (int *) memory->srealloc(body,nmax*sizeof(int),"rigid:body"); + displace = memory->grow_2d_double_array(displace,nmax,3,"rigid:displace"); +} + +/* ---------------------------------------------------------------------- + copy values within local atom-based arrays +------------------------------------------------------------------------- */ + +void FixRigid::copy_arrays(int i, int j) +{ + body[j] = body[i]; + displace[j][0] = displace[i][0]; + displace[j][1] = displace[i][1]; + displace[j][2] = displace[i][2]; +} + +/* ---------------------------------------------------------------------- + pack values in local atom-based arrays for exchange with another proc +------------------------------------------------------------------------- */ + +int FixRigid::pack_exchange(int i, double *buf) +{ + buf[0] = body[i]; + buf[1] = displace[i][0]; + buf[2] = displace[i][1]; + buf[3] = displace[i][2]; + return 4; +} + +/* ---------------------------------------------------------------------- + unpack values in local atom-based arrays from exchange with another proc +------------------------------------------------------------------------- */ + +int FixRigid::unpack_exchange(int nlocal, double *buf) +{ + body[nlocal] = static_cast<int> (buf[0]); + displace[nlocal][0] = buf[1]; + displace[nlocal][1] = buf[2]; + displace[nlocal][2] = buf[3]; + return 4; +} diff --git a/src/fix_rigid.h b/src/fix_rigid.h new file mode 100644 index 0000000000000000000000000000000000000000..51fb1044fd6c9d7be565906875d3c0ee44b0ff1c --- /dev/null +++ b/src/fix_rigid.h @@ -0,0 +1,78 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_RIGID_H +#define FIX_RIGID_H + +#include "fix.h" + +class FixRigid : public Fix { + public: + FixRigid(int, char **); + ~FixRigid(); + int setmask(); + void init(); + void setup(); + void initial_integrate(); + void final_integrate(); + void initial_integrate_respa(int, int); + void final_integrate_respa(int); + + int memory_usage(); + void grow_arrays(int); + void copy_arrays(int, int); + int pack_exchange(int, double *); + int unpack_exchange(int, double *); + + int dof(int); + void dilate(int, double, double, double, double); + + private: + double dtv,dtf,dtq; + double *step_respa; + int pressure_flag; + + int nbody; // # of rigid bodies + int *nrigid; // # of atoms in each rigid body + double *masstotal; // total mass of each rigid body + double **xcm; // coords of center-of-mass of each rigid body + double **vcm; // velocity of center-of-mass of each + double **fcm; // force on center-of-mass of each + double **inertia; // 3 principal components of inertia of each + double **ex_space,**ey_space,**ez_space; + // principal axes of each in space coords + double **angmom; // angular momentum of each in space coords + double **omega; // angular velocity of each in space coords + double **torque; // torque on each rigid body in space coords + double **quat; // quaternion of each rigid body + + int *body; // which body each atom is part of (-1 if none) + double **displace; // displacement of each atom in body coords + + double **sum,**all; // work vectors for each rigid body + + int jacobi(double **, double *, double **); + void rotate(double **, int, int, int, int, double, double); + void qcreate(double **, double *); + void multiply(double *, double *, double *); + void normalize(double *); + void richardson(double *, double *, double *, double *, + double *, double *, double *); + void omega_from_mq(double *, double *, double *, + double *, double *, double *); + void exyz_from_q(double *, double *, double *, double *); + void set_xv(int); + void set_v(int); +}; + +#endif diff --git a/src/fix_set_force.cpp b/src/fix_set_force.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0a1602b3777a917af538176eac1c4682251e8aae --- /dev/null +++ b/src/fix_set_force.cpp @@ -0,0 +1,119 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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_set_force.h" +#include "atom.h" +#include "update.h" +#include "respa.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixSetForce::FixSetForce(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 6) error->all("Illegal fix setforce command"); + + flagx = flagy = flagz = 1; + if (strcmp(arg[3],"NULL") == 0) flagx = 0; + else xvalue = atof(arg[3]); + if (strcmp(arg[4],"NULL") == 0) flagy = 0; + else yvalue = atof(arg[4]); + if (strcmp(arg[5],"NULL") == 0) flagz = 0; + else zvalue = atof(arg[5]); +} + +/* ---------------------------------------------------------------------- */ + +int FixSetForce::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + mask |= POST_FORCE_RESPA; + mask |= MIN_POST_FORCE; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixSetForce::init() +{ + if (strcmp(update->integrate_style,"respa") == 0) + nlevels_respa = ((Respa *) update->integrate)->nlevels; +} + +/* ---------------------------------------------------------------------- */ + +void FixSetForce::setup() +{ + if (strcmp(update->integrate_style,"verlet") == 0) + post_force(1); + else + for (int ilevel = 0; ilevel < nlevels_respa; ilevel++) { + ((Respa *) update->integrate)->copy_flevel_f(ilevel); + post_force_respa(1,ilevel,0); + ((Respa *) update->integrate)->copy_f_flevel(ilevel); + } +} + +/* ---------------------------------------------------------------------- */ + +void FixSetForce::min_setup() +{ + post_force(1); +} + +/* ---------------------------------------------------------------------- */ + +void FixSetForce::post_force(int vflag) +{ + double **f = atom->f; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + if (flagx) f[i][0] = xvalue; + if (flagy) f[i][1] = yvalue; + if (flagz) f[i][2] = zvalue; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixSetForce::post_force_respa(int vflag, int ilevel, int iloop) +{ + // set force to desired value on outermost level, 0.0 on other levels + + if (ilevel == nlevels_respa-1) post_force(vflag); + else { + double **f = atom->f; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + if (flagx) f[i][0] = 0.0; + if (flagy) f[i][1] = 0.0; + if (flagz) f[i][2] = 0.0; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixSetForce::min_post_force(int vflag) +{ + post_force(vflag); +} diff --git a/src/fix_set_force.h b/src/fix_set_force.h new file mode 100644 index 0000000000000000000000000000000000000000..f149fe4457b1d305a0788b5773e0b5e7ae35b694 --- /dev/null +++ b/src/fix_set_force.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_SET_FORCE_H +#define FIX_SET_FORCE_H + +#include "fix.h" + +class FixSetForce : public Fix { + public: + FixSetForce(int, char **); + ~FixSetForce() {} + int setmask(); + void init(); + void setup(); + void min_setup(); + void post_force(int); + void post_force_respa(int, int, int); + void min_post_force(int); + + private: + int flagx,flagy,flagz; + double xvalue,yvalue,zvalue; + int nlevels_respa; +}; + +#endif diff --git a/src/fix_shake.cpp b/src/fix_shake.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f1767798a7deb22743fea7d527ab8f1157792809 --- /dev/null +++ b/src/fix_shake.cpp @@ -0,0 +1,2290 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "math.h" +#include "stdlib.h" +#include "string.h" +#include "stdio.h" +#include "fix_shake.h" +#include "atom.h" +#include "update.h" +#include "respa.h" +#include "modify.h" +#include "domain.h" +#include "force.h" +#include "bond.h" +#include "angle.h" +#include "comm.h" +#include "group.h" +#include "fix_respa.h" +#include "memory.h" +#include "error.h" + +#define BIG 1.0e20 +#define MASSDELTA 0.1 +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +FixShake::FixShake(int narg, char **arg) : Fix(narg, arg) +{ + MPI_Comm_rank(world,&me); + MPI_Comm_size(world,&nprocs); + + PI = 4.0*atan(1.0); + + // error check + + if (atom->molecular == 0) + error->all("Cannot use fix shake with non-molecular system"); + + // perform initial allocation of atom-based arrays + // register with atom class + + shake_flag = NULL; + shake_atom = shake_type = NULL; + xshake = NULL; + + grow_arrays(atom->nmax); + atom->add_callback(0); + + // parse SHAKE args + + if (narg < 8) error->all("Illegal fix shake command"); + + tolerance = atof(arg[3]); + max_iter = atoi(arg[4]); + output_every = atoi(arg[5]); + + // parse SHAKE args for bond and angle types + // will be used by find_clusters + // store args for "b" "a" "t" as flags in (1:n) list for fast access + // store args for "m" in list of length nmass for looping over + // for "m" verify that atom masses have been set + + bond_flag = new int[atom->nbondtypes+1]; + for (int i = 1; i <= atom->nbondtypes; i++) bond_flag[i] = 0; + angle_flag = new int[atom->nangletypes+1]; + for (int i = 1; i <= atom->nangletypes; i++) angle_flag[i] = 0; + type_flag = new int[atom->ntypes+1]; + for (int i = 1; i <= atom->ntypes; i++) type_flag[i] = 0; + mass_list = new double[atom->ntypes]; + nmass = 0; + + char mode = '\0'; + int next = 6; + while (next < narg) { + + if (strcmp(arg[next],"b") == 0) mode = 'b'; + else if (strcmp(arg[next],"a") == 0) mode = 'a'; + else if (strcmp(arg[next],"t") == 0) mode = 't'; + else if (strcmp(arg[next],"m") == 0) { + mode = 'm'; + atom->check_mass(); + + } else if (mode == 'b') { + int i = atoi(arg[next]); + if (i < 1 || i > atom->nbondtypes) + error->all("Invalid bond type index for fix shake"); + bond_flag[i] = 1; + + } else if (mode == 'a') { + int i = atoi(arg[next]); + if (i < 1 || i > atom->nangletypes) + error->all("Invalid angle type index for fix shake"); + angle_flag[i] = 1; + + } else if (mode == 't') { + int i = atoi(arg[next]); + if (i < 1 || i > atom->ntypes) + error->all("Invalid atom type index for fix shake"); + type_flag[i] = 1; + + } else if (mode == 'm') { + double rmass = atof(arg[next]); + if (rmass == 0.0) error->all("Invalid atom mass for fix shake"); + if (nmass == atom->ntypes) error->all("Too many masses for fix shake"); + mass_list[nmass++] = rmass; + + } else error->all("Illegal fix shake command"); + next++; + } + + // allocate bond and angle distance arrays, indexed from 1 to n + + bond_distance = new double[atom->nbondtypes+1]; + angle_distance = new double[atom->nangletypes+1]; + + // allocate statistics arrays + + if (output_every) { + int nb = atom->nbondtypes + 1; + b_count = new int[nb]; + b_count_all = new int[nb]; + b_ave = new double[nb]; + b_ave_all = new double[nb]; + b_max = new double[nb]; + b_max_all = new double[nb]; + b_min = new double[nb]; + b_min_all = new double[nb]; + + int na = atom->nangletypes + 1; + a_count = new int[na]; + a_count_all = new int[na]; + a_ave = new double[na]; + a_ave_all = new double[na]; + a_max = new double[na]; + a_max_all = new double[na]; + a_min = new double[na]; + a_min_all = new double[na]; + } + + // identify all SHAKE clusters + + find_clusters(); + + // initialize list of SHAKE clusters to constrain + + maxlist = 0; + list = NULL; +} + +/* ---------------------------------------------------------------------- */ + +FixShake::~FixShake() +{ + // if atom class still exists: + // unregister this fix so atom class doesn't invoke it any more + // set bond_type and angle_type back to positive for SHAKE clusters + // must set for all SHAKE bonds and angles stored by each atom + + if (atom) { + atom->delete_callback(id,0); + + int **bond_type = atom->bond_type; + int **angle_type = atom->angle_type; + int nlocal = atom->nlocal; + + int n; + for (int i = 0; i < nlocal; i++) { + if (shake_flag[i] == 0) continue; + else if (shake_flag[i] == 1) { + n = bondfind(i,shake_atom[i][0],shake_atom[i][1]); + if (n >= 0) bond_type[i][n] = -bond_type[i][n]; + n = bondfind(i,shake_atom[i][0],shake_atom[i][2]); + if (n >= 0) bond_type[i][n] = -bond_type[i][n]; + n = anglefind(i,shake_atom[i][1],shake_atom[i][2]); + if (n >= 0) angle_type[i][n] = -angle_type[i][n]; + } else if (shake_flag[i] == 2) { + n = bondfind(i,shake_atom[i][0],shake_atom[i][1]); + if (n >= 0) bond_type[i][n] = -bond_type[i][n]; + } else if (shake_flag[i] == 3) { + n = bondfind(i,shake_atom[i][0],shake_atom[i][1]); + if (n >= 0) bond_type[i][n] = -bond_type[i][n]; + n = bondfind(i,shake_atom[i][0],shake_atom[i][2]); + if (n >= 0) bond_type[i][n] = -bond_type[i][n]; + } else if (shake_flag[i] == 4) { + n = bondfind(i,shake_atom[i][0],shake_atom[i][1]); + if (n >= 0) bond_type[i][n] = -bond_type[i][n]; + n = bondfind(i,shake_atom[i][0],shake_atom[i][2]); + if (n >= 0) bond_type[i][n] = -bond_type[i][n]; + n = bondfind(i,shake_atom[i][0],shake_atom[i][3]); + if (n >= 0) bond_type[i][n] = -bond_type[i][n]; + } + } + } + + // delete locally stored arrays + + memory->sfree(shake_flag); + memory->destroy_2d_int_array(shake_atom); + memory->destroy_2d_int_array(shake_type); + memory->destroy_2d_double_array(xshake); + + delete [] bond_flag; + delete [] angle_flag; + delete [] type_flag; + delete [] mass_list; + + delete [] bond_distance; + delete [] angle_distance; + + if (output_every) { + delete [] b_count; + delete [] b_count_all; + delete [] b_ave; + delete [] b_ave_all; + delete [] b_max; + delete [] b_max_all; + delete [] b_min; + delete [] b_min_all; + + delete [] a_count; + delete [] a_count_all; + delete [] a_ave; + delete [] a_ave_all; + delete [] a_max; + delete [] a_max_all; + delete [] a_min; + delete [] a_min_all; + } + + memory->sfree(list); +} + +/* ---------------------------------------------------------------------- */ + +int FixShake::setmask() +{ + int mask = 0; + mask |= PRE_NEIGHBOR; + mask |= POST_FORCE; + mask |= POST_FORCE_RESPA; + return mask; +} + +/* ---------------------------------------------------------------------- + set bond and angle distances + this init must happen after force->bond and force->angle inits +------------------------------------------------------------------------- */ + +void FixShake::init() +{ + int i,m,flag,flag_all,type1,type2,bond1_type,bond2_type; + double rsq,angle; + + // error if more than one shake fix + + int count = 0; + for (i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"shake") == 0) count++; + if (count > 1) error->all("More than one shake fix"); + + // error if npt,nph fix comes before shake 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,"shake") == 0) + error->all("Shake fix must come before NPT/NPH fix"); + } + + // if rRESPA, find associated fix that must exist + // could have changed locations in fix list since created + // set ptrs to rRESPA variables + + if (strcmp(update->integrate_style,"respa") == 0) { + for (i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"RESPA") == 0) ifix_respa = i; + nlevels_respa = ((Respa *) update->integrate)->nlevels; + loop_respa = ((Respa *) update->integrate)->loop; + step_respa = ((Respa *) update->integrate)->step; + } + + // set communication size in comm class + + comm->maxforward_fix = MAX(comm->maxforward_fix,3); + + // set equilibrium bond distances + + if (force->bond == NULL) + error->all("Bond potential must be defined for SHAKE"); + for (i = 1; i <= atom->nbondtypes; i++) + bond_distance[i] = force->bond->equilibrium_distance(i); + + // set equilibrium angle distances + + int nlocal = atom->nlocal; + + for (i = 1; i <= atom->nangletypes; i++) { + if (angle_flag[i] == 0) continue; + + // scan all atoms for a SHAKE angle cluster + // extract bond types for the 2 bonds in the cluster + // bond types must be same in all clusters of this angle type, + // else set error flag + + flag = 0; + bond1_type = bond2_type = 0; + for (m = 0; m < nlocal; m++) { + if (shake_flag[m] != 1) continue; + if (shake_type[m][2] != i) continue; + type1 = MIN(shake_type[m][0],shake_type[m][1]); + type2 = MAX(shake_type[m][0],shake_type[m][1]); + if (bond1_type > 0) { + if (type1 != bond1_type || type2 != bond2_type) { + flag = 1; + break; + } + } + bond1_type = type1; + bond2_type = type2; + } + + // error check for any bond types that are not the same + + MPI_Allreduce(&flag,&flag_all,1,MPI_INT,MPI_MAX,world); + if (flag_all) error->all("Shake angles have different bond types"); + + // insure all procs have bond types + + MPI_Allreduce(&bond1_type,&flag_all,1,MPI_INT,MPI_MAX,world); + bond1_type = flag_all; + MPI_Allreduce(&bond2_type,&flag_all,1,MPI_INT,MPI_MAX,world); + bond2_type = flag_all; + + // if bond types are 0, no SHAKE angles of this type exist + // just skip this angle + + if (bond1_type == 0) { + angle_distance[i] = 0.0; + continue; + } + + // compute the angle distance as a function of 2 bond distances + + angle = force->angle->equilibrium_angle(i); + rsq = 2.0*bond_distance[bond1_type]*bond_distance[bond2_type] * + (1.0-cos(angle)); + angle_distance[i] = sqrt(rsq); + } +} + +/* ---------------------------------------------------------------------- + SHAKE as pre-integrator constraint +------------------------------------------------------------------------- */ + +void FixShake::setup() +{ + pre_neighbor(); + + if (output_every) stats(); + + // setup SHAKE output + + int ntimestep = update->ntimestep; + next_output = ntimestep + output_every; + if (output_every == 0) next_output = update->laststep + 1; + if (output_every && ntimestep % output_every != 0) + next_output = (ntimestep/output_every)*output_every + output_every; + + // half timestep constraint on pre-step, full timestep thereafter + + if (strcmp(update->integrate_style,"verlet") == 0) { + dtv = update->dt; + dtfsq = 0.5 * update->dt * update->dt * force->ftm2v; + post_force(1); + dtfsq = update->dt * update->dt * force->ftm2v; + } else { + dtv = step_respa[0]; + dtf_innerhalf = 0.5 * step_respa[0] * force->ftm2v; + dtf_inner = dtf_innerhalf; + ((Respa *) update->integrate)->copy_flevel_f(nlevels_respa-1); + post_force_respa(1,nlevels_respa-1,0); + ((Respa *) update->integrate)->copy_f_flevel(nlevels_respa-1); + dtf_inner = step_respa[0] * force->ftm2v; + } +} + +/* ---------------------------------------------------------------------- + build list of SHAKE clusters to constrain + if one or more atoms in cluster are on this proc, + this proc lists the cluster exactly once +------------------------------------------------------------------------- */ + +void FixShake::pre_neighbor() +{ + int atom1,atom2,atom3,atom4; + + // local copies of atom quantities + // used by SHAKE until next re-neighboring + + x = atom->x; + v = atom->v; + f = atom->f; + mass = atom->mass; + type = atom->type; + nlocal = atom->nlocal; + + // extend size of SHAKE list if necessary + + if (nlocal > maxlist) { + maxlist = nlocal; + memory->sfree(list); + list = (int *) memory->smalloc(maxlist*sizeof(int),"shake:list"); + } + + // build list of SHAKE clusters I compute + + nlist = 0; + + for (int i = 0; i < nlocal; i++) + if (shake_flag[i]) { + if (shake_flag[i] == 2) { + atom1 = atom->map(shake_atom[i][0]); + atom2 = atom->map(shake_atom[i][1]); + if (atom1 == -1 || atom2 == -1) { + char str[128]; + sprintf(str,"Shake atoms %d %d missing on proc %d at step %d", + shake_atom[i][0],shake_atom[i][1],me,update->ntimestep); + error->one(str); + } + if (i <= atom1 && i <= atom2) list[nlist++] = i; + } else if (shake_flag[i] % 2 == 1) { + atom1 = atom->map(shake_atom[i][0]); + atom2 = atom->map(shake_atom[i][1]); + atom3 = atom->map(shake_atom[i][2]); + if (atom1 == -1 || atom2 == -1 || atom3 == -1) { + char str[128]; + sprintf(str,"Shake atoms %d %d %d missing on proc %d at step %d", + shake_atom[i][0],shake_atom[i][1],shake_atom[i][2], + me,update->ntimestep); + error->one(str); + } + if (i <= atom1 && i <= atom2 && i <= atom3) list[nlist++] = i; + } else { + atom1 = atom->map(shake_atom[i][0]); + atom2 = atom->map(shake_atom[i][1]); + atom3 = atom->map(shake_atom[i][2]); + atom4 = atom->map(shake_atom[i][3]); + if (atom1 == -1 || atom2 == -1 || atom3 == -1 || atom4 == -1) { + char str[128]; + sprintf(str,"Shake atoms %d %d %d %d missing on proc %d at step %d", + shake_atom[i][0],shake_atom[i][1], + shake_atom[i][2],shake_atom[i][3], + me,update->ntimestep); + error->one(str); + } + if (i <= atom1 && i <= atom2 && i <= atom3 && i <= atom4) + list[nlist++] = i; + } + } +} + +/* ---------------------------------------------------------------------- + compute the force adjustment for SHAKE constraint +------------------------------------------------------------------------- */ + +void FixShake::post_force(int vflag_in) +{ + if (update->ntimestep == next_output) stats(); + + // xshake = unconstrained move with current v,f + + unconstrained_update(); + + // communicate results if necessary + + if (nprocs > 1) comm->comm_fix(this); + + // zero out SHAKE contribution to virial + + vflag = vflag_in; + if (vflag) for (int n = 0; n < 6; n++) virial[n] = 0.0; + + // loop over clusters + + int m; + for (int i = 0; i < nlist; i++) { + m = list[i]; + if (shake_flag[m] == 2) shake2(m); + else if (shake_flag[m] == 3) shake3(m); + else if (shake_flag[m] == 4) shake4(m); + else shake3angle(m); + } +} + +/* ---------------------------------------------------------------------- + count # of degrees-of-freedom removed by SHAKE for atoms in igroup +------------------------------------------------------------------------- */ + +int FixShake::dof(int igroup) +{ + int groupbit = group->bitmask[igroup]; + + int *mask = atom->mask; + int *tag = atom->tag; + int nlocal = atom->nlocal; + + // count dof in a cluster if and only if the central atom is in group + // and this atom i is the central atom + + int n = 0; + for (int i = 0; i < nlocal; i++) { + if (!(mask[i] & groupbit)) continue; + if (shake_flag[i] == 0) continue; + if (shake_atom[i][0] != tag[i]) continue; + if (shake_flag[i] == 1) n += 3; + else if (shake_flag[i] == 2) n += 1; + else if (shake_flag[i] == 3) n += 2; + else if (shake_flag[i] == 4) n += 3; + } + + int nall; + MPI_Allreduce(&n,&nall,1,MPI_INT,MPI_SUM,world); + return nall; +} + +/* ---------------------------------------------------------------------- + identify whether each atom is in a SHAKE cluster + only include atoms in fix group and those bonds/angles specified in input + test whether all clusters are valid + set shake_flag, shake_atom, shake_type values + set bond,angle types negative so will be ignored in neighbor lists +------------------------------------------------------------------------- */ + +void FixShake::find_clusters() +{ + int i,j,m,n; + int flag,flag_all,messtag,loop,nbuf,nbufmax,size; + double imass,jmass,rmass; + int *buf,*bufcopy; + MPI_Request request; + MPI_Status status; + + if (me == 0 && screen) fprintf(screen,"Finding SHAKE clusters ...\n"); + + // local copies of atom ptrs + + int *tag = atom->tag; + int *type = atom->type; + int *mask = atom->mask; + double *mass = atom->mass; + int **bond_type = atom->bond_type; + int **angle_type = atom->angle_type; + int **nspecial = atom->nspecial; + int **special = atom->special; + int nlocal = atom->nlocal; + + // setup ring of procs + + int next = me + 1; + int prev = me -1; + if (next == nprocs) next = 0; + if (prev < 0) prev = nprocs - 1; + + // ----------------------------------------------------- + // allocate arrays for self (1d) and bond partners (2d) + // max = max # of bond partners for owned atoms = 2nd dim of partner arrays + // npartner[i] = # of bonds attached to atom i + // nshake[i] = # of SHAKE bonds attached to atom i + // partner_tag[i][] = global IDs of each partner + // partner_mask[i][] = mask of each partner + // partner_type[i][] = type of each partner + // partner_bondtype[i][] = type of bond attached to each partner + // partner_shake[i][] = 1 if SHAKE bonded to partner, 0 if not + // partner_nshake[i][] = nshake value for each partner + // ----------------------------------------------------- + + int max = 0; + for (i = 0; i < nlocal; i++) max = MAX(max,nspecial[i][0]); + + int *npartner = (int *) + memory->smalloc(nlocal*sizeof(double),"shake:npartner"); + int *nshake = (int *) + memory->smalloc(nlocal*sizeof(double),"shake:nshake"); + int **partner_tag = + memory->create_2d_int_array(nlocal,max,"shake:partner_tag"); + int **partner_mask = + memory->create_2d_int_array(nlocal,max,"shake:partner_mask"); + int **partner_type = + memory->create_2d_int_array(nlocal,max,"shake:partner_type"); + int **partner_bondtype = + memory->create_2d_int_array(nlocal,max,"shake:partner_bondtype"); + int **partner_shake = + memory->create_2d_int_array(nlocal,max,"shake:partner_shake"); + int **partner_nshake = + memory->create_2d_int_array(nlocal,max,"shake:partner_nshake"); + + // ----------------------------------------------------- + // set npartner and partner_tag from special arrays + // ----------------------------------------------------- + + for (i = 0; i < nlocal; i++) { + npartner[i] = nspecial[i][0]; + for (j = 0; j < npartner[i]; j++) partner_tag[i][j] = special[i][j]; + } + + // ----------------------------------------------------- + // set partner_mask, partner_type, partner_bondtype for bonded partners + // requires communication for off-proc partners + // ----------------------------------------------------- + + // fill in mask, type, bondtype if own bond partner + // info to store in buf for each off-proc bond = + // 2 atoms IDs in bond, space for mask, type, bondtype + // nbufmax = largest buffer needed to hold info from any proc + + nbuf = 0; + for (i = 0; i < nlocal; i++) { + for (j = 0; j < npartner[i]; j++) { + partner_mask[i][j] = 0; + partner_type[i][j] = 0; + partner_bondtype[i][j] = 0; + + m = atom->map(partner_tag[i][j]); + if (m >= 0 && m < nlocal) { + partner_mask[i][j] = mask[m]; + partner_type[i][j] = type[m]; + n = bondfind(i,tag[i],partner_tag[i][j]); + if (n >= 0) partner_bondtype[i][j] = bond_type[i][n]; + else { + n = bondfind(m,tag[i],partner_tag[i][j]); + if (n >= 0) partner_bondtype[i][j] = bond_type[m][n]; + } + } else nbuf += 5; + } + } + + MPI_Allreduce(&nbuf,&nbufmax,1,MPI_INT,MPI_MAX,world); + + buf = new int[nbufmax]; + bufcopy = new int[nbufmax]; + + // fill buffer with info + + size = 0; + for (i = 0; i < nlocal; i++) { + for (j = 0; j < npartner[i]; j++) { + m = atom->map(partner_tag[i][j]); + if (m < 0 || m >= nlocal) { + buf[size] = tag[i]; + buf[size+1] = partner_tag[i][j]; + buf[size+2] = 0; + buf[size+3] = 0; + n = bondfind(i,tag[i],partner_tag[i][j]); + if (n >= 0) buf[size+4] = bond_type[i][n]; + else buf[size+4] = 0; + size += 5; + } + } + } + + // cycle buffer around ring of procs back to self + // when receive buffer, scan bond partner IDs for atoms I own + // if I own partner, fill in mask and type, search for bond with 1st atom + // and fill in bondtype + + messtag = 1; + for (loop = 0; loop < nprocs; loop++) { + i = 0; + while (i < size) { + m = atom->map(buf[i+1]); + if (m >= 0 && m < nlocal) { + buf[i+2] = mask[m]; + buf[i+3] = type[m]; + if (buf[i+4] == 0) { + n = bondfind(m,buf[i],buf[i+1]); + if (n >= 0) buf[i+4] = bond_type[m][n]; + } + } + i += 5; + } + if (me != next) { + MPI_Irecv(bufcopy,nbufmax,MPI_INT,prev,messtag,world,&request); + MPI_Send(buf,size,MPI_INT,next,messtag,world); + MPI_Wait(&request,&status); + MPI_Get_count(&status,MPI_INT,&size); + for (j = 0; j < size; j++) buf[j] = bufcopy[j]; + } + } + + // store partner info returned to me + + m = 0; + while (m < size) { + i = atom->map(buf[m]); + for (j = 0; j < npartner[i]; j++) + if (buf[m+1] == partner_tag[i][j]) break; + partner_mask[i][j] = buf[m+2]; + partner_type[i][j] = buf[m+3]; + partner_bondtype[i][j] = buf[m+4]; + m += 5; + } + + delete [] buf; + delete [] bufcopy; + + // error check for unfilled partner info + // if partner_type not set, is an error + // partner_bondtype may not be set if special list is not consistent + // with bondatom (e.g. due to delete_bonds command) + // this is OK if one or both atoms are not in fix group, since + // bond won't be SHAKEn anyway + // else it's an error + + flag = 0; + for (i = 0; i < nlocal; i++) + for (j = 0; j < npartner[i]; j++) { + if (partner_type[i][j] == 0) flag = 1; + if (!(mask[i] & groupbit)) continue; + if (!(partner_mask[i][j] & groupbit)) continue; + if (partner_bondtype[i][j] == 0) flag = 1; + } + + MPI_Allreduce(&flag,&flag_all,1,MPI_INT,MPI_SUM,world); + if (flag_all) error->all("Did not find fix shake partner info"); + + // ----------------------------------------------------- + // identify SHAKEable bonds + // set nshake[i] = # of SHAKE bonds attached to atom i + // set partner_shake[i][] = 1 if SHAKE bonded to partner, 0 if not + // both atoms must be in group, bondtype must be > 0 + // check if bondtype is in input bond_flag + // check if type of either atom is in input type_flag + // check if mass of either atom is in input mass_list + // ----------------------------------------------------- + + for (i = 0; i < nlocal; i++) { + nshake[i] = 0; + for (j = 0; j < npartner[i]; j++) { + partner_shake[i][j] = 0; + + if (!(mask[i] & groupbit)) continue; + if (!(partner_mask[i][j] & groupbit)) continue; + if (partner_bondtype[i][j] <= 0) continue; + + if (bond_flag[partner_bondtype[i][j]]) { + partner_shake[i][j] = 1; + nshake[i]++; + continue; + } + if (type_flag[type[i]] || type_flag[partner_type[i][j]]) { + partner_shake[i][j] = 1; + nshake[i]++; + continue; + } + if (nmass) { + imass = mass[type[i]]; + jmass = mass[partner_type[i][j]]; + for (m = 0; m < nmass; m++) { + rmass = mass_list[m]; + if (fabs(rmass-imass) <= MASSDELTA || + fabs(rmass-jmass) <= MASSDELTA) { + partner_shake[i][j] = 1; + nshake[i]++; + break; + } + } + } + } + } + + // ----------------------------------------------------- + // set partner_nshake for bonded partners + // requires communication for off-proc partners + // ----------------------------------------------------- + + // fill in partner_nshake if own bond partner + // info to store in buf for each off-proc bond = + // 2 atoms IDs in bond, space for nshake value + // nbufmax = largest buffer needed to hold info from any proc + + nbuf = 0; + for (i = 0; i < nlocal; i++) { + for (j = 0; j < npartner[i]; j++) { + m = atom->map(partner_tag[i][j]); + if (m >= 0 && m < nlocal) partner_nshake[i][j] = nshake[m]; + else nbuf += 3; + } + } + + MPI_Allreduce(&nbuf,&nbufmax,1,MPI_INT,MPI_MAX,world); + + buf = new int[nbufmax]; + bufcopy = new int[nbufmax]; + + // fill buffer with info + + size = 0; + for (i = 0; i < nlocal; i++) { + for (j = 0; j < npartner[i]; j++) { + m = atom->map(partner_tag[i][j]); + if (m < 0 || m >= nlocal) { + buf[size] = tag[i]; + buf[size+1] = partner_tag[i][j]; + size += 3; + } + } + } + + // cycle buffer around ring of procs back to self + // when receive buffer, scan bond partner IDs for atoms I own + // if I own partner, fill in nshake value + + messtag = 2; + for (loop = 0; loop < nprocs; loop++) { + i = 0; + while (i < size) { + m = atom->map(buf[i+1]); + if (m >= 0 && m < nlocal) buf[i+2] = nshake[m]; + i += 3; + } + if (me != next) { + MPI_Irecv(bufcopy,nbufmax,MPI_INT,prev,messtag,world,&request); + MPI_Send(buf,size,MPI_INT,next,messtag,world); + MPI_Wait(&request,&status); + MPI_Get_count(&status,MPI_INT,&size); + for (j = 0; j < size; j++) buf[j] = bufcopy[j]; + } + } + + // store partner info returned to me + + m = 0; + while (m < size) { + i = atom->map(buf[m]); + for (j = 0; j < npartner[i]; j++) + if (buf[m+1] == partner_tag[i][j]) break; + partner_nshake[i][j] = buf[m+2]; + m += 3; + } + + delete [] buf; + delete [] bufcopy; + + // ----------------------------------------------------- + // error checks + // no atom with nshake > 3 + // no connected atoms which both have nshake > 1 + // ----------------------------------------------------- + + flag = 0; + for (i = 0; i < nlocal; i++) if (nshake[i] > 3) flag = 1; + MPI_Allreduce(&flag,&flag_all,1,MPI_INT,MPI_SUM,world); + if (flag_all) error->all("Shake cluster of more than 4 atoms"); + + flag = 0; + for (i = 0; i < nlocal; i++) { + if (nshake[i] <= 1) continue; + for (j = 0; j < npartner[i]; j++) + if (partner_shake[i][j] && partner_nshake[i][j] > 1) flag = 1; + } + MPI_Allreduce(&flag,&flag_all,1,MPI_INT,MPI_SUM,world); + if (flag_all) error->all("Shake clusters are connected"); + + // ----------------------------------------------------- + // set SHAKE arrays that are stored with atoms & add angle constraints + // zero shake arrays for all owned atoms + // if I am central atom set shake_flag & shake_atom & shake_type + // for 2-atom clusters, I am central atom if my atom ID < partner ID + // for 3-atom clusters, test for angle constraint + // angle will be stored by this atom if it exists + // if angle type matches angle_flag, then it is angle-constrained + // shake_flag[] = 0 if atom not in SHAKE cluster + // 2,3,4 = size of bond-only cluster + // 1 = 3-atom angle cluster + // shake_atom[][] = global IDs of 2,3,4 atoms in cluster + // central atom is 1st + // for 2-atom cluster, lowest ID is 1st + // shake_type[][] = bondtype of each bond in cluster + // for 3-atom angle cluster, 3rd value is angletype + // ----------------------------------------------------- + + for (i = 0; i < nlocal; i++) { + shake_flag[i] = 0; + shake_atom[i][0] = 0; + shake_atom[i][1] = 0; + shake_atom[i][2] = 0; + shake_atom[i][3] = 0; + shake_type[i][0] = 0; + shake_type[i][1] = 0; + shake_type[i][2] = 0; + + if (nshake[i] == 1) { + for (j = 0; j < npartner[i]; j++) + if (partner_shake[i][j]) break; + if (partner_nshake[i][j] == 1 && tag[i] < partner_tag[i][j]) { + shake_flag[i] = 2; + shake_atom[i][0] = tag[i]; + shake_atom[i][1] = partner_tag[i][j]; + shake_type[i][0] = partner_bondtype[i][j]; + } + } + + if (nshake[i] > 1) { + shake_flag[i] = 1; + shake_atom[i][0] = tag[i]; + for (j = 0; j < npartner[i]; j++) + if (partner_shake[i][j]) { + m = shake_flag[i]; + shake_atom[i][m] = partner_tag[i][j]; + shake_type[i][m-1] = partner_bondtype[i][j]; + shake_flag[i]++; + } + } + + if (nshake[i] == 2) { + n = anglefind(i,shake_atom[i][1],shake_atom[i][2]); + if (n < 0) continue; + if (angle_type[i][n] < 0) continue; + if (angle_flag[angle_type[i][n]]) { + shake_flag[i] = 1; + shake_type[i][2] = angle_type[i][n]; + } + } + } + + // ----------------------------------------------------- + // set shake_flag,shake_atom,shake_type for non-central atoms + // requires communication for off-proc atoms + // ----------------------------------------------------- + + // fill in shake arrays for each bond partner I own + // info to store in buf for each off-proc bond = + // all values from shake_flag, shake_atom, shake_type + // nbufmax = largest buffer needed to hold info from any proc + + nbuf = 0; + for (i = 0; i < nlocal; i++) { + if (shake_flag[i] == 0) continue; + for (j = 0; j < npartner[i]; j++) { + if (partner_shake[i][j] == 0) continue; + m = atom->map(partner_tag[i][j]); + if (m >= 0 && m < nlocal) { + shake_flag[m] = shake_flag[i]; + shake_atom[m][0] = shake_atom[i][0]; + shake_atom[m][1] = shake_atom[i][1]; + shake_atom[m][2] = shake_atom[i][2]; + shake_atom[m][3] = shake_atom[i][3]; + shake_type[m][0] = shake_type[i][0]; + shake_type[m][1] = shake_type[i][1]; + shake_type[m][2] = shake_type[i][2]; + } else nbuf += 9; + } + } + + MPI_Allreduce(&nbuf,&nbufmax,1,MPI_INT,MPI_MAX,world); + + buf = new int[nbufmax]; + bufcopy = new int[nbufmax]; + + // fill buffer with info + + size = 0; + for (i = 0; i < nlocal; i++) { + if (shake_flag[i] == 0) continue; + for (j = 0; j < npartner[i]; j++) { + if (partner_shake[i][j] == 0) continue; + m = atom->map(partner_tag[i][j]); + if (m < 0 || m >= nlocal) { + buf[size] = partner_tag[i][j]; + buf[size+1] = shake_flag[i]; + buf[size+2] = shake_atom[i][0]; + buf[size+3] = shake_atom[i][1]; + buf[size+4] = shake_atom[i][2]; + buf[size+5] = shake_atom[i][3]; + buf[size+6] = shake_type[i][0]; + buf[size+7] = shake_type[i][1]; + buf[size+8] = shake_type[i][2]; + size += 9; + } + } + } + + // cycle buffer around ring of procs back to self + // when receive buffer, scan for ID that I own + // if I own ID, fill in shake array values + + messtag = 3; + for (loop = 0; loop < nprocs; loop++) { + i = 0; + while (i < size) { + m = atom->map(buf[i]); + if (m >= 0 && m < nlocal) { + shake_flag[m] = buf[i+1]; + shake_atom[m][0] = buf[i+2]; + shake_atom[m][1] = buf[i+3]; + shake_atom[m][2] = buf[i+4]; + shake_atom[m][3] = buf[i+5]; + shake_type[m][0] = buf[i+6]; + shake_type[m][1] = buf[i+7]; + shake_type[m][2] = buf[i+8]; + } + i += 9; + } + if (me != next) { + MPI_Irecv(bufcopy,nbufmax,MPI_INT,prev,messtag,world,&request); + MPI_Send(buf,size,MPI_INT,next,messtag,world); + MPI_Wait(&request,&status); + MPI_Get_count(&status,MPI_INT,&size); + for (j = 0; j < size; j++) buf[j] = bufcopy[j]; + } + } + + delete [] buf; + delete [] bufcopy; + + // ----------------------------------------------------- + // free local memory + // ----------------------------------------------------- + + memory->sfree(npartner); + memory->sfree(nshake); + memory->destroy_2d_int_array(partner_tag); + memory->destroy_2d_int_array(partner_mask); + memory->destroy_2d_int_array(partner_type); + memory->destroy_2d_int_array(partner_bondtype); + memory->destroy_2d_int_array(partner_shake); + memory->destroy_2d_int_array(partner_nshake); + + // ----------------------------------------------------- + // set bond_type and angle_type negative for SHAKE clusters + // must set for all SHAKE bonds and angles stored by each atom + // ----------------------------------------------------- + + for (i = 0; i < nlocal; i++) { + if (shake_flag[i] == 0) continue; + else if (shake_flag[i] == 1) { + n = bondfind(i,shake_atom[i][0],shake_atom[i][1]); + if (n >= 0) bond_type[i][n] = -bond_type[i][n]; + n = bondfind(i,shake_atom[i][0],shake_atom[i][2]); + if (n >= 0) bond_type[i][n] = -bond_type[i][n]; + n = anglefind(i,shake_atom[i][1],shake_atom[i][2]); + if (n >= 0) angle_type[i][n] = -angle_type[i][n]; + } else if (shake_flag[i] == 2) { + n = bondfind(i,shake_atom[i][0],shake_atom[i][1]); + if (n >= 0) bond_type[i][n] = -bond_type[i][n]; + } else if (shake_flag[i] == 3) { + n = bondfind(i,shake_atom[i][0],shake_atom[i][1]); + if (n >= 0) bond_type[i][n] = -bond_type[i][n]; + n = bondfind(i,shake_atom[i][0],shake_atom[i][2]); + if (n >= 0) bond_type[i][n] = -bond_type[i][n]; + } else if (shake_flag[i] == 4) { + n = bondfind(i,shake_atom[i][0],shake_atom[i][1]); + if (n >= 0) bond_type[i][n] = -bond_type[i][n]; + n = bondfind(i,shake_atom[i][0],shake_atom[i][2]); + if (n >= 0) bond_type[i][n] = -bond_type[i][n]; + n = bondfind(i,shake_atom[i][0],shake_atom[i][3]); + if (n >= 0) bond_type[i][n] = -bond_type[i][n]; + } + } + + // ----------------------------------------------------- + // print info on SHAKE clusters + // ----------------------------------------------------- + + int count1,count2,count3,count4; + count1 = count2 = count3 = count4 = 0; + for (i = 0; i < nlocal; i++) { + if (shake_flag[i] == 1) count1++; + else if (shake_flag[i] == 2) count2++; + else if (shake_flag[i] == 3) count3++; + else if (shake_flag[i] == 4) count4++; + } + + int tmp; + tmp = count1; + MPI_Allreduce(&tmp,&count1,1,MPI_INT,MPI_SUM,world); + tmp = count2; + MPI_Allreduce(&tmp,&count2,1,MPI_INT,MPI_SUM,world); + tmp = count3; + MPI_Allreduce(&tmp,&count3,1,MPI_INT,MPI_SUM,world); + tmp = count4; + MPI_Allreduce(&tmp,&count4,1,MPI_INT,MPI_SUM,world); + + if (me == 0) { + if (screen) { + fprintf(screen," %d = # of size 2 clusters\n",count2/2); + fprintf(screen," %d = # of size 3 clusters\n",count3/3); + fprintf(screen," %d = # of size 4 clusters\n",count4/4); + fprintf(screen," %d = # of frozen angles\n",count1/3); + } + if (logfile) { + fprintf(logfile," %d = # of size 2 clusters\n",count2/2); + fprintf(logfile," %d = # of size 3 clusters\n",count3/3); + fprintf(logfile," %d = # of size 4 clusters\n",count4/4); + fprintf(logfile," %d = # of frozen angles\n",count1/3); + } + } +} + +/* ---------------------------------------------------------------------- + update the unconstrained position of each atom + only for SHAKE clusters, else set to 0.0 + assumes NVE update, seems to be accurate enough for NVT,NPT,NPH as well +------------------------------------------------------------------------- */ + +void FixShake::unconstrained_update() +{ + double dtfmsq; + + for (int i = 0; i < nlocal; i++) { + if (shake_flag[i]) { + dtfmsq = dtfsq / mass[type[i]]; + xshake[i][0] = x[i][0] + dtv*v[i][0] + dtfmsq*f[i][0]; + xshake[i][1] = x[i][1] + dtv*v[i][1] + dtfmsq*f[i][1]; + xshake[i][2] = x[i][2] + dtv*v[i][2] + dtfmsq*f[i][2]; + } else xshake[i][2] = xshake[i][1] = xshake[i][0] = 0.0; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixShake::shake2(int m) +{ + // local atom IDs and constraint distances + + int i0 = atom->map(shake_atom[m][0]); + int i1 = atom->map(shake_atom[m][1]); + double bond1 = bond_distance[shake_type[m][0]]; + + // r01 = distance vec between atoms, with PBC + + double r01[3]; + r01[0] = x[i0][0] - x[i1][0]; + r01[1] = x[i0][1] - x[i1][1]; + r01[2] = x[i0][2] - x[i1][2]; + domain->minimum_image(r01); + + // s01 = distance vec after unconstrained update, with PBC + + double s01[3]; + s01[0] = xshake[i0][0] - xshake[i1][0]; + s01[1] = xshake[i0][1] - xshake[i1][1]; + s01[2] = xshake[i0][2] - xshake[i1][2]; + domain->minimum_image(s01); + + // scalar distances between atoms + + double r01sq = r01[0]*r01[0] + r01[1]*r01[1] + r01[2]*r01[2]; + double s01sq = s01[0]*s01[0] + s01[1]*s01[1] + s01[2]*s01[2]; + + // a,b,c = coeffs in quadratic equation for lamda + + double invmass0 = 1.0/mass[type[i0]]; + double invmass1 = 1.0/mass[type[i1]]; + + double a = (invmass0+invmass1)*(invmass0+invmass1) * r01sq; + double b = 2.0 * (invmass0+invmass1) * + (s01[0]*r01[0] + s01[1]*r01[1] + s01[2]*r01[2]); + double c = s01sq - bond1*bond1; + + // error check + + double determ = b*b - 4.0*a*c; + if (determ < 0.0) { + error->warning("Shake determinant < 0.0"); + determ = 0.0; + } + + // exact quadratic solution for lamda + + double lamda,lamda1,lamda2; + lamda1 = (-b+sqrt(determ)) / (2.0*a); + lamda2 = (-b-sqrt(determ)) / (2.0*a); + + if (fabs(lamda1) <= fabs(lamda2)) lamda = lamda1; + else lamda = lamda2; + + // update forces if atom is owned by this processor + + lamda /= dtfsq; + + if (i0 < nlocal) { + f[i0][0] += lamda*r01[0]; + f[i0][1] += lamda*r01[1]; + f[i0][2] += lamda*r01[2]; + } + + if (i1 < nlocal) { + f[i1][0] -= lamda*r01[0]; + f[i1][1] -= lamda*r01[1]; + f[i1][2] -= lamda*r01[2]; + } + + if (vflag) { + int factor = 0; + if (i0 < nlocal) factor++; + if (i1 < nlocal) factor++; + double rfactor = 0.5 * factor; + + virial[0] += rfactor*lamda*r01[0]*r01[0]; + virial[1] += rfactor*lamda*r01[1]*r01[1]; + virial[2] += rfactor*lamda*r01[2]*r01[2]; + virial[3] += rfactor*lamda*r01[0]*r01[1]; + virial[4] += rfactor*lamda*r01[0]*r01[2]; + virial[5] += rfactor*lamda*r01[1]*r01[2]; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixShake::shake3(int m) +{ + // local atom IDs and constraint distances + + int i0 = atom->map(shake_atom[m][0]); + int i1 = atom->map(shake_atom[m][1]); + int i2 = atom->map(shake_atom[m][2]); + double bond1 = bond_distance[shake_type[m][0]]; + double bond2 = bond_distance[shake_type[m][1]]; + + // r01,r02 = distance vec between atoms, with PBC + + double r01[3]; + r01[0] = x[i0][0] - x[i1][0]; + r01[1] = x[i0][1] - x[i1][1]; + r01[2] = x[i0][2] - x[i1][2]; + domain->minimum_image(r01); + + double r02[3]; + r02[0] = x[i0][0] - x[i2][0]; + r02[1] = x[i0][1] - x[i2][1]; + r02[2] = x[i0][2] - x[i2][2]; + domain->minimum_image(r02); + + // s01,s02 = distance vec after unconstrained update, with PBC + + double s01[3]; + s01[0] = xshake[i0][0] - xshake[i1][0]; + s01[1] = xshake[i0][1] - xshake[i1][1]; + s01[2] = xshake[i0][2] - xshake[i1][2]; + domain->minimum_image(s01); + + double s02[3]; + s02[0] = xshake[i0][0] - xshake[i2][0]; + s02[1] = xshake[i0][1] - xshake[i2][1]; + s02[2] = xshake[i0][2] - xshake[i2][2]; + domain->minimum_image(s02); + + // scalar distances between atoms + + double r01sq = r01[0]*r01[0] + r01[1]*r01[1] + r01[2]*r01[2]; + double r02sq = r02[0]*r02[0] + r02[1]*r02[1] + r02[2]*r02[2]; + double s01sq = s01[0]*s01[0] + s01[1]*s01[1] + s01[2]*s01[2]; + double s02sq = s02[0]*s02[0] + s02[1]*s02[1] + s02[2]*s02[2]; + + // matrix coeffs and rhs for lamda equations + + double invmass0 = 1.0/mass[type[i0]]; + double invmass1 = 1.0/mass[type[i1]]; + double invmass2 = 1.0/mass[type[i2]]; + + double a11 = 2.0 * (invmass0+invmass1) * + (s01[0]*r01[0] + s01[1]*r01[1] + s01[2]*r01[2]); + double a12 = 2.0 * invmass0 * + (s01[0]*r02[0] + s01[1]*r02[1] + s01[2]*r02[2]); + double a21 = 2.0 * invmass0 * + (s02[0]*r01[0] + s02[1]*r01[1] + s02[2]*r01[2]); + double a22 = 2.0 * (invmass0+invmass2) * + (s02[0]*r02[0] + s02[1]*r02[1] + s02[2]*r02[2]); + + // inverse of matrix + + double determ = a11*a22 - a12*a21; + if (determ == 0.0) error->one("Shake determinant = 0.0"); + double determinv = 1.0/determ; + + double a11inv = a22*determinv; + double a12inv = -a12*determinv; + double a21inv = -a21*determinv; + double a22inv = a11*determinv; + + // quadratic correction coeffs + + double r0102 = (r01[0]*r02[0] + r01[1]*r02[1] + r01[2]*r02[2]); + + double quad1_0101 = (invmass0+invmass1)*(invmass0+invmass1) * r01sq; + double quad1_0202 = invmass0*invmass0 * r02sq; + double quad1_0102 = 2.0 * (invmass0+invmass1)*invmass0 * r0102; + + double quad2_0202 = (invmass0+invmass2)*(invmass0+invmass2) * r02sq; + double quad2_0101 = invmass0*invmass0 * r01sq; + double quad2_0102 = 2.0 * (invmass0+invmass2)*invmass0 * r0102; + + // iterate until converged + + double lamda01 = 0.0; + double lamda02 = 0.0; + int niter = 0; + int done = 0; + + double quad1,quad2,b1,b2,lamda01_new,lamda02_new; + + while (!done && niter < max_iter) { + quad1 = quad1_0101 * lamda01*lamda01 + quad1_0202 * lamda02*lamda02 + + quad1_0102 * lamda01*lamda02; + quad2 = quad2_0101 * lamda01*lamda01 + quad2_0202 * lamda02*lamda02 + + quad2_0102 * lamda01*lamda02; + + b1 = bond1*bond1 - s01sq - quad1; + b2 = bond2*bond2 - s02sq - quad2; + + lamda01_new = a11inv*b1 + a12inv*b2; + lamda02_new = a21inv*b1 + a22inv*b2; + + done = 1; + if (fabs(lamda01_new-lamda01) > tolerance) done = 0; + if (fabs(lamda02_new-lamda02) > tolerance) done = 0; + + lamda01 = lamda01_new; + lamda02 = lamda02_new; + niter++; + } + + // update forces if atom is owned by this processor + + lamda01 = lamda01/dtfsq; + lamda02 = lamda02/dtfsq; + + if (i0 < nlocal) { + f[i0][0] += lamda01*r01[0] + lamda02*r02[0]; + f[i0][1] += lamda01*r01[1] + lamda02*r02[1]; + f[i0][2] += lamda01*r01[2] + lamda02*r02[2]; + } + + if (i1 < nlocal) { + f[i1][0] -= lamda01*r01[0]; + f[i1][1] -= lamda01*r01[1]; + f[i1][2] -= lamda01*r01[2]; + } + + if (i2 < nlocal) { + f[i2][0] -= lamda02*r02[0]; + f[i2][1] -= lamda02*r02[1]; + f[i2][2] -= lamda02*r02[2]; + } + + if (vflag) { + int factor = 0; + if (i0 < nlocal) factor++; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + double rfactor = factor/3.0; + + virial[0] += rfactor*lamda01*r01[0]*r01[0]; + virial[1] += rfactor*lamda01*r01[1]*r01[1]; + virial[2] += rfactor*lamda01*r01[2]*r01[2]; + virial[3] += rfactor*lamda01*r01[0]*r01[1]; + virial[4] += rfactor*lamda01*r01[0]*r01[2]; + virial[5] += rfactor*lamda01*r01[1]*r01[2]; + + virial[0] += rfactor*lamda02*r02[0]*r02[0]; + virial[1] += rfactor*lamda02*r02[1]*r02[1]; + virial[2] += rfactor*lamda02*r02[2]*r02[2]; + virial[3] += rfactor*lamda02*r02[0]*r02[1]; + virial[4] += rfactor*lamda02*r02[0]*r02[2]; + virial[5] += rfactor*lamda02*r02[1]*r02[2]; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixShake::shake4(int m) +{ + // local atom IDs and constraint distances + + int i0 = atom->map(shake_atom[m][0]); + int i1 = atom->map(shake_atom[m][1]); + int i2 = atom->map(shake_atom[m][2]); + int i3 = atom->map(shake_atom[m][3]); + double bond1 = bond_distance[shake_type[m][0]]; + double bond2 = bond_distance[shake_type[m][1]]; + double bond3 = bond_distance[shake_type[m][2]]; + + // r01,r02,r03 = distance vec between atoms, with PBC + + double r01[3]; + r01[0] = x[i0][0] - x[i1][0]; + r01[1] = x[i0][1] - x[i1][1]; + r01[2] = x[i0][2] - x[i1][2]; + domain->minimum_image(r01); + + double r02[3]; + r02[0] = x[i0][0] - x[i2][0]; + r02[1] = x[i0][1] - x[i2][1]; + r02[2] = x[i0][2] - x[i2][2]; + domain->minimum_image(r02); + + double r03[3]; + r03[0] = x[i0][0] - x[i3][0]; + r03[1] = x[i0][1] - x[i3][1]; + r03[2] = x[i0][2] - x[i3][2]; + domain->minimum_image(r03); + + // s01,s02,s03 = distance vec after unconstrained update, with PBC + + double s01[3]; + s01[0] = xshake[i0][0] - xshake[i1][0]; + s01[1] = xshake[i0][1] - xshake[i1][1]; + s01[2] = xshake[i0][2] - xshake[i1][2]; + domain->minimum_image(s01); + + double s02[3]; + s02[0] = xshake[i0][0] - xshake[i2][0]; + s02[1] = xshake[i0][1] - xshake[i2][1]; + s02[2] = xshake[i0][2] - xshake[i2][2]; + domain->minimum_image(s02); + + double s03[3]; + s03[0] = xshake[i0][0] - xshake[i3][0]; + s03[1] = xshake[i0][1] - xshake[i3][1]; + s03[2] = xshake[i0][2] - xshake[i3][2]; + domain->minimum_image(s03); + + // scalar distances between atoms + + double r01sq = r01[0]*r01[0] + r01[1]*r01[1] + r01[2]*r01[2]; + double r02sq = r02[0]*r02[0] + r02[1]*r02[1] + r02[2]*r02[2]; + double r03sq = r03[0]*r03[0] + r03[1]*r03[1] + r03[2]*r03[2]; + double s01sq = s01[0]*s01[0] + s01[1]*s01[1] + s01[2]*s01[2]; + double s02sq = s02[0]*s02[0] + s02[1]*s02[1] + s02[2]*s02[2]; + double s03sq = s03[0]*s03[0] + s03[1]*s03[1] + s03[2]*s03[2]; + + // matrix coeffs and rhs for lamda equations + + double invmass0 = 1.0/mass[type[i0]]; + double invmass1 = 1.0/mass[type[i1]]; + double invmass2 = 1.0/mass[type[i2]]; + double invmass3 = 1.0/mass[type[i3]]; + + double a11 = 2.0 * (invmass0+invmass1) * + (s01[0]*r01[0] + s01[1]*r01[1] + s01[2]*r01[2]); + double a12 = 2.0 * invmass0 * + (s01[0]*r02[0] + s01[1]*r02[1] + s01[2]*r02[2]); + double a13 = 2.0 * invmass0 * + (s01[0]*r03[0] + s01[1]*r03[1] + s01[2]*r03[2]); + double a21 = 2.0 * invmass0 * + (s02[0]*r01[0] + s02[1]*r01[1] + s02[2]*r01[2]); + double a22 = 2.0 * (invmass0+invmass2) * + (s02[0]*r02[0] + s02[1]*r02[1] + s02[2]*r02[2]); + double a23 = 2.0 * invmass0 * + (s02[0]*r03[0] + s02[1]*r03[1] + s02[2]*r03[2]); + double a31 = 2.0 * invmass0 * + (s03[0]*r01[0] + s03[1]*r01[1] + s03[2]*r01[2]); + double a32 = 2.0 * invmass0 * + (s03[0]*r02[0] + s03[1]*r02[1] + s03[2]*r02[2]); + double a33 = 2.0 * (invmass0+invmass3) * + (s03[0]*r03[0] + s03[1]*r03[1] + s03[2]*r03[2]); + + // inverse of matrix; + + double determ = a11*a22*a33 + a12*a23*a31 + a13*a21*a32 - + a11*a23*a32 - a12*a21*a33 - a13*a22*a31; + if (determ == 0.0) error->one("Shake determinant = 0.0"); + double determinv = 1.0/determ; + + double a11inv = determinv * (a22*a33 - a23*a32); + double a12inv = -determinv * (a12*a33 - a13*a32); + double a13inv = determinv * (a12*a23 - a13*a22); + double a21inv = -determinv * (a21*a33 - a23*a31); + double a22inv = determinv * (a11*a33 - a13*a31); + double a23inv = -determinv * (a11*a23 - a13*a21); + double a31inv = determinv * (a21*a32 - a22*a31); + double a32inv = -determinv * (a11*a32 - a12*a31); + double a33inv = determinv * (a11*a22 - a12*a21); + + // quadratic correction coeffs + + double r0102 = (r01[0]*r02[0] + r01[1]*r02[1] + r01[2]*r02[2]); + double r0103 = (r01[0]*r03[0] + r01[1]*r03[1] + r01[2]*r03[2]); + double r0203 = (r02[0]*r03[0] + r02[1]*r03[1] + r02[2]*r03[2]); + + double quad1_0101 = (invmass0+invmass1)*(invmass0+invmass1) * r01sq; + double quad1_0202 = invmass0*invmass0 * r02sq; + double quad1_0303 = invmass0*invmass0 * r03sq; + double quad1_0102 = 2.0 * (invmass0+invmass1)*invmass0 * r0102; + double quad1_0103 = 2.0 * (invmass0+invmass1)*invmass0 * r0103; + double quad1_0203 = 2.0 * invmass0*invmass0 * r0203; + + double quad2_0101 = invmass0*invmass0 * r01sq; + double quad2_0202 = (invmass0+invmass2)*(invmass0+invmass2) * r02sq; + double quad2_0303 = invmass0*invmass0 * r03sq; + double quad2_0102 = 2.0 * (invmass0+invmass2)*invmass0 * r0102; + double quad2_0103 = 2.0 * invmass0*invmass0 * r0103; + double quad2_0203 = 2.0 * (invmass0+invmass2)*invmass0 * r0203; + + double quad3_0101 = invmass0*invmass0 * r01sq; + double quad3_0202 = invmass0*invmass0 * r02sq; + double quad3_0303 = (invmass0+invmass3)*(invmass0+invmass3) * r03sq; + double quad3_0102 = 2.0 * invmass0*invmass0 * r0102; + double quad3_0103 = 2.0 * (invmass0+invmass3)*invmass0 * r0103; + double quad3_0203 = 2.0 * (invmass0+invmass3)*invmass0 * r0203; + + // iterate until converged + + double lamda01 = 0.0; + double lamda02 = 0.0; + double lamda03 = 0.0; + int niter = 0; + int done = 0; + + double quad1,quad2,quad3,b1,b2,b3,lamda01_new,lamda02_new,lamda03_new; + + while (!done && niter < max_iter) { + quad1 = quad1_0101 * lamda01*lamda01 + + quad1_0202 * lamda02*lamda02 + + quad1_0303 * lamda03*lamda03 + + quad1_0102 * lamda01*lamda02 + + quad1_0103 * lamda01*lamda03 + + quad1_0203 * lamda02*lamda03; + + quad2 = quad2_0101 * lamda01*lamda01 + + quad2_0202 * lamda02*lamda02 + + quad2_0303 * lamda03*lamda03 + + quad2_0102 * lamda01*lamda02 + + quad2_0103 * lamda01*lamda03 + + quad2_0203 * lamda02*lamda03; + + quad3 = quad3_0101 * lamda01*lamda01 + + quad3_0202 * lamda02*lamda02 + + quad3_0303 * lamda03*lamda03 + + quad3_0102 * lamda01*lamda02 + + quad3_0103 * lamda01*lamda03 + + quad3_0203 * lamda02*lamda03; + + b1 = bond1*bond1 - s01sq - quad1; + b2 = bond2*bond2 - s02sq - quad2; + b3 = bond3*bond3 - s03sq - quad3; + + lamda01_new = a11inv*b1 + a12inv*b2 + a13inv*b3; + lamda02_new = a21inv*b1 + a22inv*b2 + a23inv*b3; + lamda03_new = a31inv*b1 + a32inv*b2 + a33inv*b3; + + done = 1; + if (fabs(lamda01_new-lamda01) > tolerance) done = 0; + if (fabs(lamda02_new-lamda02) > tolerance) done = 0; + if (fabs(lamda03_new-lamda03) > tolerance) done = 0; + + lamda01 = lamda01_new; + lamda02 = lamda02_new; + lamda03 = lamda03_new; + niter++; + } + + // update forces if atom is owned by this processor + + lamda01 = lamda01/dtfsq; + lamda02 = lamda02/dtfsq; + lamda03 = lamda03/dtfsq; + + if (i0 < nlocal) { + f[i0][0] += lamda01*r01[0] + lamda02*r02[0] + lamda03*r03[0]; + f[i0][1] += lamda01*r01[1] + lamda02*r02[1] + lamda03*r03[1]; + f[i0][2] += lamda01*r01[2] + lamda02*r02[2] + lamda03*r03[2]; + } + + if (i1 < nlocal) { + f[i1][0] -= lamda01*r01[0]; + f[i1][1] -= lamda01*r01[1]; + f[i1][2] -= lamda01*r01[2]; + } + + if (i2 < nlocal) { + f[i2][0] -= lamda02*r02[0]; + f[i2][1] -= lamda02*r02[1]; + f[i2][2] -= lamda02*r02[2]; + } + + if (i3 < nlocal) { + f[i3][0] -= lamda03*r03[0]; + f[i3][1] -= lamda03*r03[1]; + f[i3][2] -= lamda03*r03[2]; + } + + if (vflag) { + int factor = 0; + if (i0 < nlocal) factor++; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + double rfactor = 0.25*factor; + + virial[0] += rfactor*lamda01*r01[0]*r01[0]; + virial[1] += rfactor*lamda01*r01[1]*r01[1]; + virial[2] += rfactor*lamda01*r01[2]*r01[2]; + virial[3] += rfactor*lamda01*r01[0]*r01[1]; + virial[4] += rfactor*lamda01*r01[0]*r01[2]; + virial[5] += rfactor*lamda01*r01[1]*r01[2]; + + virial[0] += rfactor*lamda02*r02[0]*r02[0]; + virial[1] += rfactor*lamda02*r02[1]*r02[1]; + virial[2] += rfactor*lamda02*r02[2]*r02[2]; + virial[3] += rfactor*lamda02*r02[0]*r02[1]; + virial[4] += rfactor*lamda02*r02[0]*r02[2]; + virial[5] += rfactor*lamda02*r02[1]*r02[2]; + + virial[0] += rfactor*lamda03*r03[0]*r03[0]; + virial[1] += rfactor*lamda03*r03[1]*r03[1]; + virial[2] += rfactor*lamda03*r03[2]*r03[2]; + virial[3] += rfactor*lamda03*r03[0]*r03[1]; + virial[4] += rfactor*lamda03*r03[0]*r03[2]; + virial[5] += rfactor*lamda03*r03[1]*r03[2]; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixShake::shake3angle(int m) +{ + // local atom IDs and constraint distances + + int i0 = atom->map(shake_atom[m][0]); + int i1 = atom->map(shake_atom[m][1]); + int i2 = atom->map(shake_atom[m][2]); + double bond1 = bond_distance[shake_type[m][0]]; + double bond2 = bond_distance[shake_type[m][1]]; + double bond12 = angle_distance[shake_type[m][2]]; + + // r01,r02,r12 = distance vec between atoms, with PBC + + double r01[3]; + r01[0] = x[i0][0] - x[i1][0]; + r01[1] = x[i0][1] - x[i1][1]; + r01[2] = x[i0][2] - x[i1][2]; + domain->minimum_image(r01); + + double r02[3]; + r02[0] = x[i0][0] - x[i2][0]; + r02[1] = x[i0][1] - x[i2][1]; + r02[2] = x[i0][2] - x[i2][2]; + domain->minimum_image(r02); + + double r12[3]; + r12[0] = x[i1][0] - x[i2][0]; + r12[1] = x[i1][1] - x[i2][1]; + r12[2] = x[i1][2] - x[i2][2]; + domain->minimum_image(r12); + + // s01,s02,s12 = distance vec after unconstrained update, with PBC + + double s01[3]; + s01[0] = xshake[i0][0] - xshake[i1][0]; + s01[1] = xshake[i0][1] - xshake[i1][1]; + s01[2] = xshake[i0][2] - xshake[i1][2]; + domain->minimum_image(s01); + + double s02[3]; + s02[0] = xshake[i0][0] - xshake[i2][0]; + s02[1] = xshake[i0][1] - xshake[i2][1]; + s02[2] = xshake[i0][2] - xshake[i2][2]; + domain->minimum_image(s02); + + double s12[3]; + s12[0] = xshake[i1][0] - xshake[i2][0]; + s12[1] = xshake[i1][1] - xshake[i2][1]; + s12[2] = xshake[i1][2] - xshake[i2][2]; + domain->minimum_image(s12); + + // scalar distances between atoms + + double r01sq = r01[0]*r01[0] + r01[1]*r01[1] + r01[2]*r01[2]; + double r02sq = r02[0]*r02[0] + r02[1]*r02[1] + r02[2]*r02[2]; + double r12sq = r12[0]*r12[0] + r12[1]*r12[1] + r12[2]*r12[2]; + double s01sq = s01[0]*s01[0] + s01[1]*s01[1] + s01[2]*s01[2]; + double s02sq = s02[0]*s02[0] + s02[1]*s02[1] + s02[2]*s02[2]; + double s12sq = s12[0]*s12[0] + s12[1]*s12[1] + s12[2]*s12[2]; + + // matrix coeffs and rhs for lamda equations + + double invmass0 = 1.0/mass[type[i0]]; + double invmass1 = 1.0/mass[type[i1]]; + double invmass2 = 1.0/mass[type[i2]]; + + double a11 = 2.0 * (invmass0+invmass1) * + (s01[0]*r01[0] + s01[1]*r01[1] + s01[2]*r01[2]); + double a12 = 2.0 * invmass0 * + (s01[0]*r02[0] + s01[1]*r02[1] + s01[2]*r02[2]); + double a13 = - 2.0 * invmass1 * + (s01[0]*r12[0] + s01[1]*r12[1] + s01[2]*r12[2]); + double a21 = 2.0 * invmass0 * + (s02[0]*r01[0] + s02[1]*r01[1] + s02[2]*r01[2]); + double a22 = 2.0 * (invmass0+invmass2) * + (s02[0]*r02[0] + s02[1]*r02[1] + s02[2]*r02[2]); + double a23 = 2.0 * invmass2 * + (s02[0]*r12[0] + s02[1]*r12[1] + s02[2]*r12[2]); + double a31 = - 2.0 * invmass1 * + (s12[0]*r01[0] + s12[1]*r01[1] + s12[2]*r01[2]); + double a32 = 2.0 * invmass2 * + (s12[0]*r02[0] + s12[1]*r02[1] + s12[2]*r02[2]); + double a33 = 2.0 * (invmass1+invmass2) * + (s12[0]*r12[0] + s12[1]*r12[1] + s12[2]*r12[2]); + + // inverse of matrix + + double determ = a11*a22*a33 + a12*a23*a31 + a13*a21*a32 - + a11*a23*a32 - a12*a21*a33 - a13*a22*a31; + if (determ == 0.0) error->one("Shake determinant = 0.0"); + double determinv = 1.0/determ; + + double a11inv = determinv * (a22*a33 - a23*a32); + double a12inv = -determinv * (a12*a33 - a13*a32); + double a13inv = determinv * (a12*a23 - a13*a22); + double a21inv = -determinv * (a21*a33 - a23*a31); + double a22inv = determinv * (a11*a33 - a13*a31); + double a23inv = -determinv * (a11*a23 - a13*a21); + double a31inv = determinv * (a21*a32 - a22*a31); + double a32inv = -determinv * (a11*a32 - a12*a31); + double a33inv = determinv * (a11*a22 - a12*a21); + + // quadratic correction coeffs + + double r0102 = (r01[0]*r02[0] + r01[1]*r02[1] + r01[2]*r02[2]); + double r0112 = (r01[0]*r12[0] + r01[1]*r12[1] + r01[2]*r12[2]); + double r0212 = (r02[0]*r12[0] + r02[1]*r12[1] + r02[2]*r12[2]); + + double quad1_0101 = (invmass0+invmass1)*(invmass0+invmass1) * r01sq; + double quad1_0202 = invmass0*invmass0 * r02sq; + double quad1_1212 = invmass1*invmass1 * r12sq; + double quad1_0102 = 2.0 * (invmass0+invmass1)*invmass0 * r0102; + double quad1_0112 = - 2.0 * (invmass0+invmass1)*invmass1 * r0112; + double quad1_0212 = - 2.0 * invmass0*invmass1 * r0212; + + double quad2_0101 = invmass0*invmass0 * r01sq; + double quad2_0202 = (invmass0+invmass2)*(invmass0+invmass2) * r02sq; + double quad2_1212 = invmass2*invmass2 * r12sq; + double quad2_0102 = 2.0 * (invmass0+invmass2)*invmass0 * r0102; + double quad2_0112 = 2.0 * invmass0*invmass2 * r0112; + double quad2_0212 = 2.0 * (invmass0+invmass2)*invmass2 * r0212; + + double quad3_0101 = invmass1*invmass1 * r01sq; + double quad3_0202 = invmass2*invmass2 * r02sq; + double quad3_1212 = (invmass1+invmass2)*(invmass1+invmass2) * r12sq; + double quad3_0102 = - 2.0 * invmass1*invmass2 * r0102; + double quad3_0112 = - 2.0 * (invmass1+invmass2)*invmass1 * r0112; + double quad3_0212 = 2.0 * (invmass1+invmass2)*invmass2 * r0212; + + // iterate until converged + + double lamda01 = 0.0; + double lamda02 = 0.0; + double lamda12 = 0.0; + int niter = 0; + int done = 0; + + double quad1,quad2,quad3,b1,b2,b3,lamda01_new,lamda02_new,lamda12_new; + + while (!done && niter < max_iter) { + quad1 = quad1_0101 * lamda01*lamda01 + + quad1_0202 * lamda02*lamda02 + + quad1_1212 * lamda12*lamda12 + + quad1_0102 * lamda01*lamda02 + + quad1_0112 * lamda01*lamda12 + + quad1_0212 * lamda02*lamda12; + + quad2 = quad2_0101 * lamda01*lamda01 + + quad2_0202 * lamda02*lamda02 + + quad2_1212 * lamda12*lamda12 + + quad2_0102 * lamda01*lamda02 + + quad2_0112 * lamda01*lamda12 + + quad2_0212 * lamda02*lamda12; + + quad3 = quad3_0101 * lamda01*lamda01 + + quad3_0202 * lamda02*lamda02 + + quad3_1212 * lamda12*lamda12 + + quad3_0102 * lamda01*lamda02 + + quad3_0112 * lamda01*lamda12 + + quad3_0212 * lamda02*lamda12; + + b1 = bond1*bond1 - s01sq - quad1; + b2 = bond2*bond2 - s02sq - quad2; + b3 = bond12*bond12 - s12sq - quad3; + + lamda01_new = a11inv*b1 + a12inv*b2 + a13inv*b3; + lamda02_new = a21inv*b1 + a22inv*b2 + a23inv*b3; + lamda12_new = a31inv*b1 + a32inv*b2 + a33inv*b3; + + done = 1; + if (fabs(lamda01_new-lamda01) > tolerance) done = 0; + if (fabs(lamda02_new-lamda02) > tolerance) done = 0; + if (fabs(lamda12_new-lamda12) > tolerance) done = 0; + + lamda01 = lamda01_new; + lamda02 = lamda02_new; + lamda12 = lamda12_new; + niter++; + } + + // update forces if atom is owned by this processor + + lamda01 = lamda01/dtfsq; + lamda02 = lamda02/dtfsq; + lamda12 = lamda12/dtfsq; + + if (i0 < nlocal) { + f[i0][0] += lamda01*r01[0] + lamda02*r02[0]; + f[i0][1] += lamda01*r01[1] + lamda02*r02[1]; + f[i0][2] += lamda01*r01[2] + lamda02*r02[2]; + } + + if (i1 < nlocal) { + f[i1][0] -= lamda01*r01[0] - lamda12*r12[0]; + f[i1][1] -= lamda01*r01[1] - lamda12*r12[1]; + f[i1][2] -= lamda01*r01[2] - lamda12*r12[2]; + } + + if (i2 < nlocal) { + f[i2][0] -= lamda02*r02[0] + lamda12*r12[0]; + f[i2][1] -= lamda02*r02[1] + lamda12*r12[1]; + f[i2][2] -= lamda02*r02[2] + lamda12*r12[2]; + } + + if (vflag) { + int factor = 0; + if (i0 < nlocal) factor++; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + double rfactor = factor/3.0; + + virial[0] += rfactor*lamda01*r01[0]*r01[0]; + virial[1] += rfactor*lamda01*r01[1]*r01[1]; + virial[2] += rfactor*lamda01*r01[2]*r01[2]; + virial[3] += rfactor*lamda01*r01[0]*r01[1]; + virial[4] += rfactor*lamda01*r01[0]*r01[2]; + virial[5] += rfactor*lamda01*r01[1]*r01[2]; + + virial[0] += rfactor*lamda02*r02[0]*r02[0]; + virial[1] += rfactor*lamda02*r02[1]*r02[1]; + virial[2] += rfactor*lamda02*r02[2]*r02[2]; + virial[3] += rfactor*lamda02*r02[0]*r02[1]; + virial[4] += rfactor*lamda02*r02[0]*r02[2]; + virial[5] += rfactor*lamda02*r02[1]*r02[2]; + + virial[0] += rfactor*lamda12*r12[0]*r12[0]; + virial[1] += rfactor*lamda12*r12[1]*r12[1]; + virial[2] += rfactor*lamda12*r12[2]*r12[2]; + virial[3] += rfactor*lamda12*r12[0]*r12[1]; + virial[4] += rfactor*lamda12*r12[0]*r12[2]; + virial[5] += rfactor*lamda12*r12[1]*r12[2]; + } +} + +/* ---------------------------------------------------------------------- + print-out bond & angle statistics +------------------------------------------------------------------------- */ + +void FixShake::stats() +{ + int i,j,m,n,iatom,jatom,katom; + double delx,dely,delz; + double r,r1,r2,r3,angle; + + // zero out accumulators + + int nb = atom->nbondtypes + 1; + int na = atom->nangletypes + 1; + + for (i = 0; i < nb; i++) { + b_count[i] = 0; + b_ave[i] = b_max[i] = 0.0; + b_min[i] = BIG; + } + for (i = 0; i < na; i++) { + a_count[i] = 0; + a_ave[i] = a_max[i] = 0.0; + a_min[i] = BIG; + } + + // log stats for each bond & angle + // OK to double count since are just averaging + + double **x = atom->x; + int nlocal = atom->nlocal; + + for (i = 0; i < nlocal; i++) { + if (shake_flag[i] == 0) continue; + + // bond stats + + n = shake_flag[i]; + if (n == 1) n = 3; + iatom = atom->map(shake_atom[i][0]); + for (j = 1; j < n; j++) { + jatom = atom->map(shake_atom[i][j]); + delx = x[iatom][0] - x[jatom][0]; + dely = x[iatom][1] - x[jatom][1]; + delz = x[iatom][2] - x[jatom][2]; + domain->minimum_image(&delx,&dely,&delz); + r = sqrt(delx*delx + dely*dely + delz*delz); + + m = shake_type[i][j-1]; + b_count[m]++; + b_ave[m] += r; + b_max[m] = MAX(b_max[m],r); + b_min[m] = MIN(b_min[m],r); + } + + // angle stats + + if (shake_flag[i] == 1) { + iatom = atom->map(shake_atom[i][0]); + jatom = atom->map(shake_atom[i][1]); + katom = atom->map(shake_atom[i][2]); + + delx = x[iatom][0] - x[jatom][0]; + dely = x[iatom][1] - x[jatom][1]; + delz = x[iatom][2] - x[jatom][2]; + domain->minimum_image(&delx,&dely,&delz); + r1 = sqrt(delx*delx + dely*dely + delz*delz); + + delx = x[iatom][0] - x[katom][0]; + dely = x[iatom][1] - x[katom][1]; + delz = x[iatom][2] - x[katom][2]; + domain->minimum_image(&delx,&dely,&delz); + r2 = sqrt(delx*delx + dely*dely + delz*delz); + + delx = x[jatom][0] - x[katom][0]; + dely = x[jatom][1] - x[katom][1]; + delz = x[jatom][2] - x[katom][2]; + domain->minimum_image(&delx,&dely,&delz); + r3 = sqrt(delx*delx + dely*dely + delz*delz); + + angle = acos((r1*r1 + r2*r2 - r3*r3) / (2.0*r1*r2)); + angle *= 180.0/PI; + m = shake_type[i][2]; + a_count[m]++; + a_ave[m] += angle; + a_max[m] = MAX(a_max[m],angle); + a_min[m] = MIN(a_min[m],angle); + } + } + + // sum across all procs + + MPI_Allreduce(b_count,b_count_all,nb,MPI_INT,MPI_SUM,world); + MPI_Allreduce(b_ave,b_ave_all,nb,MPI_DOUBLE,MPI_SUM,world); + MPI_Allreduce(b_max,b_max_all,nb,MPI_DOUBLE,MPI_MAX,world); + MPI_Allreduce(b_min,b_min_all,nb,MPI_DOUBLE,MPI_MIN,world); + + MPI_Allreduce(a_count,a_count_all,na,MPI_INT,MPI_SUM,world); + MPI_Allreduce(a_ave,a_ave_all,na,MPI_DOUBLE,MPI_SUM,world); + MPI_Allreduce(a_max,a_max_all,na,MPI_DOUBLE,MPI_MAX,world); + MPI_Allreduce(a_min,a_min_all,na,MPI_DOUBLE,MPI_MIN,world); + + // print stats only for non-zero counts + + if (me == 0) { + if (screen) { + fprintf(screen,"SHAKE stats (type/ave/delta) on step %d\n", + update->ntimestep); + for (i = 1; i < nb; i++) + if (b_count_all[i]) + fprintf(screen," %d %g %g\n",i, + b_ave_all[i]/b_count_all[i],b_max_all[i]-b_min_all[i]); + for (i = 1; i < na; i++) + if (a_count_all[i]) + fprintf(screen," %d %g %g\n",i, + a_ave_all[i]/a_count_all[i],a_max_all[i]-a_min_all[i]); + } + if (logfile) { + fprintf(logfile,"SHAKE stats (type/ave/delta) on step %d\n", + update->ntimestep); + for (i = 0; i < nb; i++) + if (b_count_all[i]) + fprintf(logfile," %d %g %g\n",i, + b_ave_all[i]/b_count_all[i],b_max_all[i]-b_min_all[i]); + for (i = 0; i < na; i++) + if (a_count_all[i]) + fprintf(logfile," %d %g %g\n",i, + a_ave_all[i]/a_count_all[i],a_max_all[i]-a_min_all[i]); + } + } + + // next timestep for stats + + next_output += output_every; +} + +/* ---------------------------------------------------------------------- + find a bond between global tags n1 and n2 stored with local atom i + return -1 if don't find it + return bond index if do find it +------------------------------------------------------------------------- */ + +int FixShake::bondfind(int i, int n1, int n2) +{ + int *tag = atom->tag; + int **bond_atom = atom->bond_atom; + int nbonds = atom->num_bond[i]; + + int m; + for (m = 0; m < nbonds; m++) { + if (n1 == tag[i] && n2 == bond_atom[i][m]) break; + if (n1 == bond_atom[i][m] && n2 == tag[i]) break; + } + if (m < nbonds) return m; + return -1; +} + +/* ---------------------------------------------------------------------- + find an angle with global end atoms n1 and n2 stored with local atom i + return -1 if don't find it + return angle index if do find it +------------------------------------------------------------------------- */ + +int FixShake::anglefind(int i, int n1, int n2) +{ + int **angle_atom1 = atom->angle_atom1; + int **angle_atom3 = atom->angle_atom3; + int nangles = atom->num_angle[i]; + + int m; + for (m = 0; m < nangles; m++) { + if (n1 == angle_atom1[i][m] && n2 == angle_atom3[i][m]) break; + if (n1 == angle_atom3[i][m] && n2 == angle_atom1[i][m]) break; + } + if (m < nangles) return m; + return -1; +} + +/* ---------------------------------------------------------------------- + memory usage of local atom-based arrays +------------------------------------------------------------------------- */ + +int FixShake::memory_usage() +{ + int nmax = atom->nmax; + int bytes = nmax * sizeof(int); + bytes += nmax*4 * sizeof(int); + bytes += nmax*3 * sizeof(int); + bytes += nmax*3 * sizeof(double); + return bytes; +} + +/* ---------------------------------------------------------------------- + allocate local atom-based arrays +------------------------------------------------------------------------- */ + +void FixShake::grow_arrays(int nmax) +{ + shake_flag = (int *) + memory->srealloc(shake_flag,nmax*sizeof(int),"shake:shake_flag"); + shake_atom = + memory->grow_2d_int_array(shake_atom,nmax,4,"shake:shake_atom"); + shake_type = + memory->grow_2d_int_array(shake_type,nmax,3,"shake:shake_type"); + memory->destroy_2d_double_array(xshake); + xshake = memory->create_2d_double_array(nmax,3,"shake:xshake"); +} + +/* ---------------------------------------------------------------------- + copy values within local atom-based arrays +------------------------------------------------------------------------- */ + +void FixShake::copy_arrays(int i, int j) +{ + int flag = shake_flag[j] = shake_flag[i]; + if (flag == 1) { + shake_atom[j][0] = shake_atom[i][0]; + shake_atom[j][1] = shake_atom[i][1]; + shake_atom[j][2] = shake_atom[i][2]; + shake_type[j][0] = shake_type[i][0]; + shake_type[j][1] = shake_type[i][1]; + shake_type[j][2] = shake_type[i][2]; + } else if (flag == 2) { + shake_atom[j][0] = shake_atom[i][0]; + shake_atom[j][1] = shake_atom[i][1]; + shake_type[j][0] = shake_type[i][0]; + } else if (flag == 3) { + shake_atom[j][0] = shake_atom[i][0]; + shake_atom[j][1] = shake_atom[i][1]; + shake_atom[j][2] = shake_atom[i][2]; + shake_type[j][0] = shake_type[i][0]; + shake_type[j][1] = shake_type[i][1]; + } else if (flag == 4) { + shake_atom[j][0] = shake_atom[i][0]; + shake_atom[j][1] = shake_atom[i][1]; + shake_atom[j][2] = shake_atom[i][2]; + shake_atom[j][3] = shake_atom[i][3]; + shake_type[j][0] = shake_type[i][0]; + shake_type[j][1] = shake_type[i][1]; + shake_type[j][2] = shake_type[i][2]; + } +} + +/* ---------------------------------------------------------------------- + pack values in local atom-based arrays for exchange with another proc +------------------------------------------------------------------------- */ + +int FixShake::pack_exchange(int i, double *buf) +{ + int m = 0; + buf[m++] = shake_flag[i]; + int flag = shake_flag[i]; + if (flag == 1) { + buf[m++] = shake_atom[i][0]; + buf[m++] = shake_atom[i][1]; + buf[m++] = shake_atom[i][2]; + buf[m++] = shake_type[i][0]; + buf[m++] = shake_type[i][1]; + buf[m++] = shake_type[i][2]; + } else if (flag == 2) { + buf[m++] = shake_atom[i][0]; + buf[m++] = shake_atom[i][1]; + buf[m++] = shake_type[i][0]; + } else if (flag == 3) { + buf[m++] = shake_atom[i][0]; + buf[m++] = shake_atom[i][1]; + buf[m++] = shake_atom[i][2]; + buf[m++] = shake_type[i][0]; + buf[m++] = shake_type[i][1]; + } else if (flag == 4) { + buf[m++] = shake_atom[i][0]; + buf[m++] = shake_atom[i][1]; + buf[m++] = shake_atom[i][2]; + buf[m++] = shake_atom[i][3]; + buf[m++] = shake_type[i][0]; + buf[m++] = shake_type[i][1]; + buf[m++] = shake_type[i][2]; + } + return m; +} + +/* ---------------------------------------------------------------------- + unpack values in local atom-based arrays from exchange with another proc +------------------------------------------------------------------------- */ + +int FixShake::unpack_exchange(int nlocal, double *buf) +{ + int m = 0; + int flag = shake_flag[nlocal] = static_cast<int> (buf[m++]); + if (flag == 1) { + shake_atom[nlocal][0] = static_cast<int> (buf[m++]); + shake_atom[nlocal][1] = static_cast<int> (buf[m++]); + shake_atom[nlocal][2] = static_cast<int> (buf[m++]); + shake_type[nlocal][0] = static_cast<int> (buf[m++]); + shake_type[nlocal][1] = static_cast<int> (buf[m++]); + shake_type[nlocal][2] = static_cast<int> (buf[m++]); + } else if (flag == 2) { + shake_atom[nlocal][0] = static_cast<int> (buf[m++]); + shake_atom[nlocal][1] = static_cast<int> (buf[m++]); + shake_type[nlocal][0] = static_cast<int> (buf[m++]); + } else if (flag == 3) { + shake_atom[nlocal][0] = static_cast<int> (buf[m++]); + shake_atom[nlocal][1] = static_cast<int> (buf[m++]); + shake_atom[nlocal][2] = static_cast<int> (buf[m++]); + shake_type[nlocal][0] = static_cast<int> (buf[m++]); + shake_type[nlocal][1] = static_cast<int> (buf[m++]); + } else if (flag == 4) { + shake_atom[nlocal][0] = static_cast<int> (buf[m++]); + shake_atom[nlocal][1] = static_cast<int> (buf[m++]); + shake_atom[nlocal][2] = static_cast<int> (buf[m++]); + shake_atom[nlocal][3] = static_cast<int> (buf[m++]); + shake_type[nlocal][0] = static_cast<int> (buf[m++]); + shake_type[nlocal][1] = static_cast<int> (buf[m++]); + shake_type[nlocal][2] = static_cast<int> (buf[m++]); + } + return m; +} + +/* ---------------------------------------------------------------------- + enforce SHAKE constraints from rRESPA + prediction portion is different than Verlet + rRESPA updating of atom coords is done with full v, but only portions of f +------------------------------------------------------------------------- */ + +void FixShake::post_force_respa(int vflag_in, int ilevel, int iloop) +{ + // call stats only on outermost level + + if (ilevel == nlevels_respa-1 && update->ntimestep == next_output) stats(); + + // perform SHAKE on every loop iteration of every rRESPA level + // except last loop iteration of inner levels + + if (ilevel < nlevels_respa-1 && iloop == loop_respa[ilevel]-1) return; + + // xshake = atom coords after next x update in innermost loop + // depends on rRESPA level + // for levels > 0 this includes more than one velocity update + // xshake = predicted position from call to this routine at level N = + // x + dt0 (v + dtN/m fN + 1/2 dt(N-1)/m f(N-1) + ... + 1/2 dt0/m f0) + + double ***f_level = ((FixRespa *) modify->fix[ifix_respa])->f_level; + dtfsq = dtf_inner * step_respa[ilevel]; + + double invmass,dtfmsq; + int jlevel; + + for (int i = 0; i < nlocal; i++) { + if (shake_flag[i]) { + invmass = 1.0 / mass[type[i]]; + dtfmsq = dtfsq * invmass; + xshake[i][0] = x[i][0] + dtv*v[i][0] + dtfmsq*f[i][0]; + xshake[i][1] = x[i][1] + dtv*v[i][1] + dtfmsq*f[i][1]; + xshake[i][2] = x[i][2] + dtv*v[i][2] + dtfmsq*f[i][2]; + for (jlevel = 0; jlevel < ilevel; jlevel++) { + dtfmsq = dtf_innerhalf * step_respa[jlevel] * invmass; + xshake[i][0] += dtfmsq*f_level[i][jlevel][0]; + xshake[i][1] += dtfmsq*f_level[i][jlevel][1]; + xshake[i][2] += dtfmsq*f_level[i][jlevel][2]; + } + } else xshake[i][2] = xshake[i][1] = xshake[i][0] = 0.0; + } + + // communicate results if necessary + + if (nprocs > 1) comm->comm_fix(this); + + // zero out SHAKE contribution to virial + + vflag = vflag_in; + if (vflag) for (int n = 0; n < 6; n++) virial[n] = 0.0; + + // loop over clusters + + int m; + for (int i = 0; i < nlist; i++) { + m = list[i]; + if (shake_flag[m] == 2) shake2(m); + else if (shake_flag[m] == 3) shake3(m); + else if (shake_flag[m] == 4) shake4(m); + else shake3angle(m); + } +} + +/* ---------------------------------------------------------------------- */ + +int FixShake::pack_comm(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + if (pbc_flags[0] == 0) { + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = xshake[j][0]; + buf[m++] = xshake[j][1]; + buf[m++] = xshake[j][2]; + } + } else { + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = xshake[j][0] + pbc_flags[1]*domain->xprd; + buf[m++] = xshake[j][1] + pbc_flags[2]*domain->yprd; + buf[m++] = xshake[j][2] + pbc_flags[3]*domain->zprd; + } + } + return 3; +} + +/* ---------------------------------------------------------------------- */ + +void FixShake::unpack_comm(int n, int first, double *buf) +{ + int i,m,last; + + m = 0; + last = first + n; + for (i = first; i < last; i++) { + xshake[i][0] = buf[m++]; + xshake[i][1] = buf[m++]; + xshake[i][2] = buf[m++]; + } +} diff --git a/src/fix_shake.h b/src/fix_shake.h new file mode 100644 index 0000000000000000000000000000000000000000..fdde440ffe74a0842d000cf9b8b7785db123070c --- /dev/null +++ b/src/fix_shake.h @@ -0,0 +1,103 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_SHAKE_H +#define FIX_SHAKE_H + +#include "fix.h" + +class FixShake : public Fix { + public: + FixShake(int, char **); + ~FixShake(); + int setmask(); + void init(); + void setup(); + void pre_neighbor(); + void post_force(int); + void post_force_respa(int, int, int); + + int memory_usage(); + void grow_arrays(int); + void copy_arrays(int, int); + int pack_exchange(int, double *); + int unpack_exchange(int, double *); + int pack_comm(int, int *, double *, int *); + void unpack_comm(int, int, double *); + + int dof(int); + + private: + int me,nprocs; + double PI; + double tolerance; // SHAKE tolerance + int max_iter; // max # of SHAKE iterations + int output_every; // SHAKE stat output every so often + int next_output; // timestep for next output + + // settings from input command + int *bond_flag,*angle_flag; // bond/angle types to constrain + int *type_flag; // constrain bonds to these types + double *mass_list; // constrain bonds to these masses + int nmass; + + double *bond_distance,*angle_distance; // constraint distances + + int ifix_respa; // rRESPA fix needed by SHAKE + int nlevels_respa; // copies of needed rRESPA variables + int *loop_respa; + double *step_respa; + + double **x,**v,**f; // local ptrs to atom class quantities + double *mass; + int *type; + int nlocal; + // atom-based arrays + int *shake_flag; // 0 if atom not in SHAKE cluster + // 1 = size 3 angle cluster + // 2,3,4 = size of bond-only cluster + int **shake_atom; // global IDs of atoms in cluster + // central atom is 1st + // lowest global ID is 1st for size 2 + int **shake_type; // bondtype of each bond in cluster + // for angle cluster, 3rd value + // is angletype + double **xshake; // unconstrained atom coords + + int vflag; // virial flag + double dtv,dtfsq; // timesteps for trial move + double dtf_inner,dtf_innerhalf; // timesteps for rRESPA trial move + + int *list; // list of clusters to SHAKE + int nlist,maxlist; // size and max-size of list + + // stat quantities + int *b_count,*b_count_all; // counts for each bond type + double *b_ave,*b_max,*b_min; // ave/max/min dist for each bond type + double *b_ave_all,*b_max_all,*b_min_all; // MPI summing arrays + int *a_count,*a_count_all; // ditto for angle types + double *a_ave,*a_max,*a_min; + double *a_ave_all,*a_max_all,*a_min_all; + + void find_clusters(); + void unconstrained_update(); + void shake2(int); + void shake3(int); + void shake4(int); + void shake3angle(int); + void stats(); + int bondfind(int, int, int); + int anglefind(int, int, int); +}; + +#endif diff --git a/src/fix_shear_history.h b/src/fix_shear_history.h new file mode 100644 index 0000000000000000000000000000000000000000..17a375027ccc5cc20c3ce17068440b29cd3b2755 --- /dev/null +++ b/src/fix_shear_history.h @@ -0,0 +1,46 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_SHEAR_HISTORY_H +#define FIX_SHEAR_HISTORY_H + +#include "fix.h" + +class FixShearHistory : public Fix { + friend class Neighbor; + friend class FixInsert; + + public: + FixShearHistory(int, char **); + ~FixShearHistory(); + int setmask(); + void init(); + void pre_exchange(); + + int memory_usage(); + void grow_arrays(int); + void copy_arrays(int, int); + int pack_exchange(int, double *); + int unpack_exchange(int, double *); + int pack_restart(int, double *); + void unpack_restart(int, int); + int size_restart(int); + int maxsize_restart(); + + private: + int *npartner; // # of touching partners of each atom + int **partner; // tags for the partners + double ***shearpartner; // 3 shear values with the partner +}; + +#endif diff --git a/src/fix_spring.cpp b/src/fix_spring.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fa3c15ebe19af7d61c2412c4c3e76b2768836a65 --- /dev/null +++ b/src/fix_spring.cpp @@ -0,0 +1,213 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Paul Crozier (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "string.h" +#include "fix_spring.h" +#include "atom.h" +#include "update.h" +#include "respa.h" +#include "domain.h" +#include "error.h" +#include "group.h" + +#define TETHER 0 +#define COUPLE 1 + +/* ---------------------------------------------------------------------- */ + +FixSpring::FixSpring(int narg, char **arg) : Fix(narg, arg) +{ + if (narg < 9) error->all("Illegal fix spring command"); + + if (strcmp(arg[3],"tether") == 0) { + if (narg != 9) error->all("Illegal fix spring command"); + styleflag = TETHER; + k_spring = atof(arg[4]); + xflag = yflag = zflag = 1; + if (strcmp(arg[5],"NULL") == 0) xflag = 0; + else xc = atof(arg[5]); + if (strcmp(arg[6],"NULL") == 0) yflag = 0; + else yc = atof(arg[6]); + if (strcmp(arg[7],"NULL") == 0) zflag = 0; + else zc = atof(arg[7]); + r0 = atof(arg[8]); + if (r0 < 0) error->all("R0 < 0 for fix spring command"); + + } else if (strcmp(arg[3],"couple") == 0) { + if (narg != 10) error->all("Illegal fix spring command"); + styleflag = COUPLE; + igroup2 = group->find(arg[4]); + if (igroup2 == -1) + error->all("Could not find fix spring couple group ID"); + group2bit = group->bitmask[igroup2]; + k_spring = atof(arg[5]); + xflag = yflag = zflag = 1; + if (strcmp(arg[6],"NULL") == 0) xflag = 0; + else xc = atof(arg[6]); + if (strcmp(arg[7],"NULL") == 0) yflag = 0; + else yc = atof(arg[7]); + if (strcmp(arg[8],"NULL") == 0) zflag = 0; + else zc = atof(arg[8]); + r0 = atof(arg[9]); + if (r0 < 0) error->all("R0 < 0 for fix spring command"); + + } else error->all("Illegal fix spring command"); +} + +/* ---------------------------------------------------------------------- */ + +int FixSpring::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + mask |= POST_FORCE_RESPA; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixSpring::init() +{ + masstotal = group->mass(igroup); + if (styleflag == COUPLE) masstotal2 = group->mass(igroup2); + + if (strcmp(update->integrate_style,"respa") == 0) + nlevels_respa = ((Respa *) update->integrate)->nlevels; +} + +/* ---------------------------------------------------------------------- */ + +void FixSpring::setup() +{ + if (strcmp(update->integrate_style,"verlet") == 0) + post_force(1); + else { + ((Respa *) update->integrate)->copy_flevel_f(nlevels_respa-1); + post_force_respa(1,nlevels_respa-1,0); + ((Respa *) update->integrate)->copy_f_flevel(nlevels_respa-1); + } +} + +/* ---------------------------------------------------------------------- */ + +void FixSpring::post_force(int vflag) +{ + if (styleflag == TETHER) spring_tether(); + else spring_couple(); +} + +/* ---------------------------------------------------------------------- */ + +void FixSpring::spring_tether() +{ + double xcm[3]; + group->xcm(igroup,masstotal,xcm); + + // fx,fy,fz = components of k * (r-r0) + + double dx,dy,dz,fx,fy,fz,r,dr; + + dx = xcm[0] - xc; + dy = xcm[1] - yc; + dz = xcm[2] - zc; + if (!xflag) dx = 0.0; + if (!yflag) dy = 0.0; + if (!zflag) dz = 0.0; + r = sqrt(dx*dx + dy*dy + dz*dz); + dr = r - r0; + fx = k_spring*dx*dr/r; + fy = k_spring*dy*dr/r; + fz = k_spring*dz*dr/r; + + // apply restoring force to atoms in group + // f = -k*(r-r0)*mass/masstotal + + double **f = atom->f; + int *mask = atom->mask; + int *type = atom->type; + double *mass = atom->mass; + int nlocal = atom->nlocal; + + double massfrac; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + massfrac = mass[type[i]]/masstotal; + f[i][0] -= fx*massfrac; + f[i][1] -= fy*massfrac; + f[i][2] -= fz*massfrac; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixSpring::spring_couple() +{ + double xcm[3],xcm2[3]; + group->xcm(igroup,masstotal,xcm); + group->xcm(igroup2,masstotal2,xcm2); + + // fx,fy,fz = components of k * (r-r0) + + double dx,dy,dz,fx,fy,fz,r,dr; + + dx = xcm2[0] - xcm[0] - xc; + dy = xcm2[1] - xcm[1] - yc; + dz = xcm2[2] - xcm[2] - zc; + if (!xflag) dx = 0.0; + if (!yflag) dy = 0.0; + if (!zflag) dz = 0.0; + r = sqrt(dx*dx + dy*dy + dz*dz); + dr = r - r0; + + fx = k_spring*dx*dr/r; + fy = k_spring*dy*dr/r; + fz = k_spring*dz*dr/r; + + // apply restoring force to atoms in group + // f = -k*(r-r0)*mass/masstotal + + double **f = atom->f; + int *mask = atom->mask; + int *type = atom->type; + double *mass = atom->mass; + int nlocal = atom->nlocal; + + double massfrac; + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + massfrac = mass[type[i]]/masstotal; + f[i][0] += fx*massfrac; + f[i][1] += fy*massfrac; + f[i][2] += fz*massfrac; + } + if (mask[i] & group2bit) { + massfrac = mass[type[i]]/masstotal2; + f[i][0] -= fx*massfrac; + f[i][1] -= fy*massfrac; + f[i][2] -= fz*massfrac; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixSpring::post_force_respa(int vflag, int ilevel, int iloop) +{ + if (ilevel == nlevels_respa-1) post_force(vflag); +} diff --git a/src/fix_spring.h b/src/fix_spring.h new file mode 100644 index 0000000000000000000000000000000000000000..18fc8dab0dddab1e65dfb8b9ef99c4b003e9e441 --- /dev/null +++ b/src/fix_spring.h @@ -0,0 +1,42 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_SPRING_H +#define FIX_SPRING_H + +#include "fix.h" + +class FixSpring : public Fix { + public: + FixSpring(int, char **); + ~FixSpring() {} + int setmask(); + void init(); + void setup(); + void post_force(int); + void post_force_respa(int, int, int); + + private: + double xc,yc,zc,r0; + double k_spring; + int xflag,yflag,zflag; + int styleflag; + int igroup2,group2bit; + double masstotal,masstotal2; + int nlevels_respa; + + void spring_tether(); + void spring_couple(); +}; + +#endif diff --git a/src/fix_spring_rg.cpp b/src/fix_spring_rg.cpp new file mode 100644 index 0000000000000000000000000000000000000000..72e2dfc9b227fdb1d356306640f95f0fb178295d --- /dev/null +++ b/src/fix_spring_rg.cpp @@ -0,0 +1,135 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Naveen Michaud-Agrawal (Johns Hopkins U) + Paul Crozier (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "string.h" +#include "fix_spring_rg.h" +#include "atom.h" +#include "update.h" +#include "group.h" +#include "respa.h" +#include "domain.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixSpringRG::FixSpringRG(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 5) error->all("Illegal fix spring/rg command"); + + k = atof(arg[3]); + rg0_flag = 0; + if (strcmp(arg[4],"NULL") == 0) rg0_flag = 1; + else rg0 = atof(arg[4]); +} + +/* ---------------------------------------------------------------------- */ + +int FixSpringRG::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + mask |= POST_FORCE_RESPA; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixSpringRG::init() +{ + masstotal = group->mass(igroup); + + // if rg0 was specified as NULL, compute current Rg + // only occurs on 1st run + + if (rg0_flag) { + double xcm[3]; + group->xcm(igroup,masstotal,xcm); + rg0 = group->gyration(igroup,masstotal,xcm); + rg0_flag = 0; + } + + if (strcmp(update->integrate_style,"respa") == 0) + nlevels_respa = ((Respa *) update->integrate)->nlevels; +} + +/* ---------------------------------------------------------------------- */ + +void FixSpringRG::setup() +{ + if (strcmp(update->integrate_style,"verlet") == 0) + post_force(1); + else { + ((Respa *) update->integrate)->copy_flevel_f(nlevels_respa-1); + post_force_respa(1,nlevels_respa-1,0); + ((Respa *) update->integrate)->copy_f_flevel(nlevels_respa-1); + } +} + +/* ---------------------------------------------------------------------- */ + +void FixSpringRG::post_force(int vflag) +{ + // compute current Rg and center-of-mass + + double xcm[3]; + group->xcm(igroup,masstotal,xcm); + double rg = group->gyration(igroup,masstotal,xcm); + + // apply restoring force to atoms in group + // f = -k*(r-r0)*mass/masstotal + + double dx,dy,dz,term1; + + double **f = atom->f; + double **x = atom->x; + int *mask = atom->mask; + int *type = atom->type; + int *image = atom->image; + double *mass = atom->mass; + int nlocal = atom->nlocal; + + double massfrac; + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + + int xbox,ybox,zbox; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + term1 = 2.0 * k * (1.0 - rg0/rg); + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + dx = (x[i][0] + xbox*xprd) - xcm[0]; + dy = (x[i][1] + ybox*yprd) - xcm[1]; + dz = (x[i][2] + zbox*zprd) - xcm[2]; + massfrac = mass[type[i]]/masstotal; + f[i][0] -= term1*dx*massfrac; + f[i][1] -= term1*dy*massfrac; + f[i][2] -= term1*dz*massfrac; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixSpringRG::post_force_respa(int vflag, int ilevel, int iloop) +{ + if (ilevel == nlevels_respa-1) post_force(vflag); +} diff --git a/src/fix_spring_rg.h b/src/fix_spring_rg.h new file mode 100644 index 0000000000000000000000000000000000000000..b4083589df6ccd4745dbf770f6c20a0eb956f114 --- /dev/null +++ b/src/fix_spring_rg.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_SPRING_RG_H +#define FIX_SPRING_RG_H + +#include "fix.h" + +class FixSpringRG : public Fix { + public: + FixSpringRG(int, char **); + ~FixSpringRG() {} + int setmask(); + void init(); + void setup(); + void post_force(int); + void post_force_respa(int, int, int); + + private: + int nlevels_respa,rg0_flag; + double rg0,k,masstotal; +}; + +#endif diff --git a/src/fix_spring_self.cpp b/src/fix_spring_self.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5259a82b6930b8283e318f2211cfe5a2e8d304ef --- /dev/null +++ b/src/fix_spring_self.cpp @@ -0,0 +1,257 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Naveen Michaud-Agrawal (Johns Hopkins University) +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "string.h" +#include "fix_spring_self.h" +#include "atom.h" +#include "update.h" +#include "domain.h" +#include "respa.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixSpringSelf::FixSpringSelf(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 4) error->all("Illegal fix spring/self command"); + + restart_peratom = 1; + + k = atof(arg[3]); + if (k <= 0.0) error->all("Illegal fix spring/self command"); + + // perform initial allocation of atom-based array + // register with atom class + + xoriginal = NULL; + grow_arrays(atom->nmax); + atom->add_callback(0); + atom->add_callback(1); + + // xoriginal = initial unwrapped positions of atoms + + double **x = atom->x; + int *mask = atom->mask; + int *image = atom->image; + int nlocal = atom->nlocal; + + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + int xbox,ybox,zbox; + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + xoriginal[i][0] = x[i][0] + xbox*xprd; + xoriginal[i][1] = x[i][1] + ybox*yprd; + xoriginal[i][2] = x[i][2] + zbox*zprd; + } else xoriginal[i][0] = xoriginal[i][1] = xoriginal[i][2] = 0.0; + } +} + +/* ---------------------------------------------------------------------- */ + +FixSpringSelf::~FixSpringSelf() +{ + // if atom class still exists: + // unregister this fix so atom class doesn't invoke it any more + + if (atom) atom->delete_callback(id,0); + if (atom) atom->delete_callback(id,1); + + // delete locally stored array + + memory->destroy_2d_double_array(xoriginal); +} + +/* ---------------------------------------------------------------------- */ + +int FixSpringSelf::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + mask |= POST_FORCE_RESPA; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixSpringSelf::init() +{ + if (strcmp(update->integrate_style,"respa") == 0) + nlevels_respa = ((Respa *) update->integrate)->nlevels; +} + +/* ---------------------------------------------------------------------- */ + +void FixSpringSelf::setup() +{ + if (strcmp(update->integrate_style,"verlet") == 0) + post_force(1); + else { + ((Respa *) update->integrate)->copy_flevel_f(nlevels_respa-1); + post_force_respa(1,nlevels_respa-1,0); + ((Respa *) update->integrate)->copy_f_flevel(nlevels_respa-1); + } +} + +/* ---------------------------------------------------------------------- */ + +void FixSpringSelf::post_force(int vflag) +{ + double **x = atom->x; + double **f = atom->f; + int *mask = atom->mask; + int *image = atom->image; + int nlocal = atom->nlocal; + + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + int xbox,ybox,zbox; + double dx,dy,dz; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + dx = x[i][0] + xbox*xprd - xoriginal[i][0]; + dy = x[i][1] + ybox*yprd - xoriginal[i][1]; + dz = x[i][2] + zbox*zprd - xoriginal[i][2]; + f[i][0] -= k*dx; + f[i][1] -= k*dy; + f[i][2] -= k*dz; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixSpringSelf::post_force_respa(int vflag, int ilevel, int iloop) +{ + if (ilevel == nlevels_respa-1) post_force(vflag); +} + +/* ---------------------------------------------------------------------- + memory usage of local atom-based array +------------------------------------------------------------------------- */ + +int FixSpringSelf::memory_usage() +{ + int bytes = atom->nmax*3 * sizeof(double); + return bytes; +} + +/* ---------------------------------------------------------------------- + allocate atom-based array +------------------------------------------------------------------------- */ + +void FixSpringSelf::grow_arrays(int nmax) +{ + xoriginal = + memory->grow_2d_double_array(xoriginal,nmax,3,"fix_spring/self:xoriginal"); +} + +/* ---------------------------------------------------------------------- + copy values within local atom-based array +------------------------------------------------------------------------- */ + +void FixSpringSelf::copy_arrays(int i, int j) +{ + xoriginal[j][0] = xoriginal[i][0]; + xoriginal[j][1] = xoriginal[i][1]; + xoriginal[j][2] = xoriginal[i][2]; +} + +/* ---------------------------------------------------------------------- + pack values in local atom-based array for exchange with another proc +------------------------------------------------------------------------- */ + +int FixSpringSelf::pack_exchange(int i, double *buf) +{ + buf[0] = xoriginal[i][0]; + buf[1] = xoriginal[i][1]; + buf[2] = xoriginal[i][2]; + return 3; +} + +/* ---------------------------------------------------------------------- + unpack values in local atom-based array from exchange with another proc +------------------------------------------------------------------------- */ + +int FixSpringSelf::unpack_exchange(int nlocal, double *buf) +{ + xoriginal[nlocal][0] = buf[0]; + xoriginal[nlocal][1] = buf[1]; + xoriginal[nlocal][2] = buf[2]; + return 3; +} + +/* ---------------------------------------------------------------------- + pack values in local atom-based arrays for restart file +------------------------------------------------------------------------- */ + +int FixSpringSelf::pack_restart(int i, double *buf) +{ + buf[0] = 4; + buf[1] = xoriginal[i][0]; + buf[2] = xoriginal[i][1]; + buf[3] = xoriginal[i][2]; + return 4; +} + +/* ---------------------------------------------------------------------- + unpack values from atom->extra array to restart the fix +------------------------------------------------------------------------- */ + +void FixSpringSelf::unpack_restart(int nlocal, int nth) +{ + double **extra = atom->extra; + + // skip to Nth set of extra values + + int m = 0; + for (int i = 0; i < nth; i++) m += static_cast<int> (extra[nlocal][m]); + m++; + + xoriginal[nlocal][0] = extra[nlocal][m++]; + xoriginal[nlocal][1] = extra[nlocal][m++]; + xoriginal[nlocal][2] = extra[nlocal][m++]; +} + +/* ---------------------------------------------------------------------- + maxsize of any atom's restart data +------------------------------------------------------------------------- */ + +int FixSpringSelf::maxsize_restart() +{ + return 4; +} + +/* ---------------------------------------------------------------------- + size of atom nlocal's restart data +------------------------------------------------------------------------- */ + +int FixSpringSelf::size_restart(int nlocal) +{ + return 4; +} diff --git a/src/fix_spring_self.h b/src/fix_spring_self.h new file mode 100644 index 0000000000000000000000000000000000000000..ad590272e7d2fd8f7ffe9a0fbb857a9ca14499c5 --- /dev/null +++ b/src/fix_spring_self.h @@ -0,0 +1,45 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_SPRING_SELF_H +#define FIX_SPRING_SELF_H + +#include "fix.h" + +class FixSpringSelf : public Fix { + public: + FixSpringSelf(int, char **); + ~FixSpringSelf(); + int setmask(); + void init(); + void setup(); + void post_force(int); + void post_force_respa(int, int, int); + + int memory_usage(); + void grow_arrays(int); + void copy_arrays(int, int); + int pack_exchange(int, double *); + int unpack_exchange(int, double *); + int pack_restart(int, double *); + void unpack_restart(int, int); + int size_restart(int); + int maxsize_restart(); + + private: + double k; + double **xoriginal; // original coords of atoms + int nlevels_respa; +}; + +#endif diff --git a/src/fix_stress.cpp b/src/fix_stress.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8549bd18a83c094ea28d58c2a36d531e8eb5c0c9 --- /dev/null +++ b/src/fix_stress.cpp @@ -0,0 +1,260 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "fix_stress.h" +#include "atom.h" +#include "neighbor.h" +#include "modify.h" +#include "comm.h" +#include "update.h" +#include "force.h" +#include "pair.h" +#include "memory.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +FixStress::FixStress(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 3) error->all("Illegal fix stress command"); + + neigh_half_once = 1; + nmax = 0; + stress = NULL; +} + +/* ---------------------------------------------------------------------- */ + +FixStress::~FixStress() +{ + memory->destroy_2d_double_array(stress); +} + +/* ---------------------------------------------------------------------- */ + +int FixStress::setmask() +{ + int mask = 0; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixStress::init() +{ + if (force->pair == NULL || force->pair->single_enable == 0) + error->all("Pair style does not support dumping per-atom stress"); + + // set communication size in comm class + + comm->maxreverse_fix = MAX(comm->maxreverse_fix,6); + + // warn if more than one stress fix + + int count = 0; + for (int i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"STRESS") == 0) count++; + if (count > 1 && comm->me == 0) + error->warning("More than one dump custom with a stress attribute"); +} + +/* ---------------------------------------------------------------------- */ + +void FixStress::dump() +{ + int i,j,k,n,itype,jtype,numneigh; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + double factor_coul,factor_lj,fforce,rmass; + int *neighs; + Pair::One one; + + // grow stress array if necessary + + if (atom->nmax > nmax) { + memory->destroy_2d_double_array(stress); + nmax = atom->nmax; + stress = memory->create_2d_double_array(nmax,6,"stress:stress"); + } + + // clear stress array + // n includes ghosts only if newton_pair flag is set + + if (force->newton_pair) n = atom->nlocal + atom->nghost; + else n = atom->nlocal; + + for (i = 0; i < n; i++) { + stress[i][0] = 0.0; + stress[i][1] = 0.0; + stress[i][2] = 0.0; + stress[i][3] = 0.0; + stress[i][4] = 0.0; + stress[i][5] = 0.0; + } + + // if needed, build a half neighbor list + + if (!neighbor->half_every) neighbor->build_half(); + + // compute pairwise stress for all atoms via pair->single() + // use half neighbor list + + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + double **cutsq = force->pair->cutsq; + + double **x = atom->x; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + force->pair->single(i,j,itype,jtype,rsq,factor_coul,factor_lj,0,one); + fforce = one.fforce; + + stress[i][0] -= delx*delx*fforce; + stress[i][1] -= dely*dely*fforce; + stress[i][2] -= delz*delz*fforce; + stress[i][3] -= delx*dely*fforce; + stress[i][4] -= delx*delz*fforce; + stress[i][5] -= dely*delz*fforce; + if (force->newton_pair || j < nlocal) { + stress[j][0] -= delx*delx*fforce; + stress[j][1] -= dely*dely*fforce; + stress[j][2] -= delz*delz*fforce; + stress[j][3] -= delx*dely*fforce; + stress[j][4] -= delx*delz*fforce; + stress[j][5] -= dely*delz*fforce; + } + } + } + } + + // communicate stress between neighbor procs + + if (force->newton_pair) comm->reverse_comm_fix(this); + + // remove double counting of per-atom stress + + for (i = 0; i < nlocal; i++) { + stress[i][0] *= 0.5; + stress[i][1] *= 0.5; + stress[i][2] *= 0.5; + stress[i][3] *= 0.5; + stress[i][4] *= 0.5; + stress[i][5] *= 0.5; + } + + // include kinetic energy term for each atom + // mvv2e converts mv^2 to energy + + double **v = atom->v; + double *mass = atom->mass; + double mvv2e = force->mvv2e; + + for (i = 0; i < nlocal; i++) { + rmass = mvv2e * mass[type[i]]; + stress[i][0] -= rmass*v[i][0]*v[i][0]; + stress[i][1] -= rmass*v[i][1]*v[i][1]; + stress[i][2] -= rmass*v[i][2]*v[i][2]; + stress[i][3] -= rmass*v[i][0]*v[i][1]; + stress[i][4] -= rmass*v[i][0]*v[i][2]; + stress[i][5] -= rmass*v[i][1]*v[i][2]; + } + + // convert to pressure units (actually stress/volume = pressure) + + double nktv2p = force->nktv2p; + for (i = 0; i < nlocal; i++) { + stress[i][0] *= nktv2p; + stress[i][1] *= nktv2p; + stress[i][2] *= nktv2p; + stress[i][3] *= nktv2p; + stress[i][4] *= nktv2p; + stress[i][5] *= nktv2p; + } +} + +/* ---------------------------------------------------------------------- */ + +int FixStress::pack_reverse_comm(int n, int first, double *buf) +{ + int i,m,last; + + m = 0; + last = first + n; + for (i = first; i < last; i++) { + buf[m++] = stress[i][0]; + buf[m++] = stress[i][1]; + buf[m++] = stress[i][2]; + buf[m++] = stress[i][3]; + buf[m++] = stress[i][4]; + buf[m++] = stress[i][5]; + } + return 6; +} + +/* ---------------------------------------------------------------------- */ + +void FixStress::unpack_reverse_comm(int n, int *list, double *buf) +{ + int i,j,m; + + m = 0; + for (i = 0; i < n; i++) { + j = list[i]; + stress[j][0] += buf[m++]; + stress[j][1] += buf[m++]; + stress[j][2] += buf[m++]; + stress[j][3] += buf[m++]; + stress[j][4] += buf[m++]; + stress[j][5] += buf[m++]; + } +} + +/* ---------------------------------------------------------------------- + memory usage of local atom-based array +------------------------------------------------------------------------- */ + +int FixStress::memory_usage() +{ + int bytes = nmax*6 * sizeof(double); + return bytes; +} diff --git a/src/fix_stress.h b/src/fix_stress.h new file mode 100644 index 0000000000000000000000000000000000000000..2e9ce043ebc05ab57e8785a912fe38fd64764b31 --- /dev/null +++ b/src/fix_stress.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_STRESS_H +#define FIX_STRESS_H + +#include "fix.h" + +class FixStress : public Fix { + friend class DumpCustom; + + public: + FixStress(int, char **); + ~FixStress(); + int setmask(); + void init(); + void dump(); + int pack_reverse_comm(int, int, double *); + void unpack_reverse_comm(int, int *, double *); + int memory_usage(); + + private: + int nmax; + double **stress; +}; + +#endif diff --git a/src/fix_temp_rescale.cpp b/src/fix_temp_rescale.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2c6455548e3d624f0473ca45eb5557525b3ef633 --- /dev/null +++ b/src/fix_temp_rescale.cpp @@ -0,0 +1,170 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "math.h" +#include "fix_temp_rescale.h" +#include "atom.h" +#include "force.h" +#include "group.h" +#include "update.h" +#include "domain.h" +#include "region.h" +#include "comm.h" +#include "temperature.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixTempRescale::FixTempRescale(int narg, char **arg) : Fix(narg, arg) +{ + if (narg < 8) error->all("Illegal fix temp/rescale command"); + nevery = atoi(arg[3]); + if (nevery <= 0) error->all("Illegal fix temp/rescale command"); + + t_start = atof(arg[4]); + t_end = atof(arg[5]); + t_window = atof(arg[6]); + fraction = atof(arg[7]); + + // optional args + + iregion = -1; + + int iarg = 8; + while (iarg < narg) { + if (strcmp(arg[iarg],"region") == 0) { + for (iregion = 0; iregion < domain->nregion; iregion++) + if (strcmp(arg[iarg+1],domain->regions[iregion]->id) == 0) break; + if (iregion == domain->nregion) + error->all("Fix temp/rescale region ID does not exist"); + iarg += 2; + } else error->all("Illegal fix temp/rescale command"); + } + + // create a new temperature full or region style with fix ID and fix group + + char **newarg = new char*[4]; + newarg[0] = id; + newarg[1] = group->names[igroup]; + if (iregion == -1) { + newarg[2] = "full"; + force->add_temp(3,newarg,1); + } else { + newarg[2] = "region"; + newarg[3] = domain->regions[iregion]->id; + force->add_temp(4,newarg,1); + } + delete [] newarg; + + temperature = force->find_temp(id); +} + +/* ---------------------------------------------------------------------- */ + +int FixTempRescale::setmask() +{ + int mask = 0; + mask |= END_OF_STEP; + mask |= THERMO; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixTempRescale::init() +{ + energy = 0.0; + efactor = (0.5 * force->boltz * temperature->dof); +} + +/* ---------------------------------------------------------------------- */ + +void FixTempRescale::end_of_step() +{ + double t_current = temperature->compute(); + double delta = update->ntimestep - update->beginstep; + delta /= update->endstep - update->beginstep; + double t_target = t_start + delta * (t_end-t_start); + + if (fabs(t_current-t_target) > t_window) { + t_target = t_current - fraction*(t_current-t_target); + double factor = sqrt(t_target/t_current); + + double **x = atom->x; + double **v = atom->v; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + if (iregion == -1) { + energy += (t_current-t_target) * efactor; + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + v[i][0] *= factor; + v[i][1] *= factor; + v[i][2] *= factor; + } + } + + } else { + efactor = (0.5 * force->boltz * temperature->dof); + energy += (t_current-t_target) * efactor; + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit && + domain->regions[iregion]->match(x[i][0],x[i][1],x[i][2])) { + v[i][0] *= factor; + v[i][1] *= factor; + v[i][2] *= factor; + } + } + } + } else energy = 0.0; +} + +/* ---------------------------------------------------------------------- */ + +int FixTempRescale::modify_param(int narg, char **arg) +{ + if (strcmp(arg[0],"temp") == 0) { + if (narg < 2) error->all("Illegal fix_modify command"); + temperature = force->find_temp(arg[1]); + if (temperature == NULL) + error->all("Could not find fix_modify temperature ID"); + if (temperature->igroup != igroup && comm->me == 0) + error->warning("Group for fix_modify temp != fix group"); + if (strcmp(temperature->style,"region") == 0 && iregion == -1 && + comm->me == 0) + error->warning("Temperature for temp/rescale is style region"); + return 2; + } + return 0; +} + +/* ---------------------------------------------------------------------- */ + +int FixTempRescale::thermo_fields(int n, int *flags, char **keywords) +{ + if (n == 0) return 1; + flags[0] = 3; + strcpy(keywords[0],"EngTRscl"); + return 1; +} + +/* ---------------------------------------------------------------------- */ + +int FixTempRescale::thermo_compute(double *values) +{ + values[0] = energy; + return 1; +} diff --git a/src/fix_temp_rescale.h b/src/fix_temp_rescale.h new file mode 100644 index 0000000000000000000000000000000000000000..2dd21ff0a42138021112edb2b8fe0d118be4e7eb --- /dev/null +++ b/src/fix_temp_rescale.h @@ -0,0 +1,39 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_TEMP_RESCALE_H +#define FIX_TEMP_RESCALE_H + +#include "fix.h" + +class Temperature; + +class FixTempRescale : public Fix { + public: + FixTempRescale(int, char **); + ~FixTempRescale() {} + int setmask(); + void init(); + void end_of_step(); + int modify_param(int, char **); + int thermo_fields(int, int *, char **); + int thermo_compute(double *); + + private: + int iregion; + double t_start,t_end,t_window; + double fraction,energy,efactor; + Temperature *temperature; +}; + +#endif diff --git a/src/fix_tmd.cpp b/src/fix_tmd.cpp new file mode 100644 index 0000000000000000000000000000000000000000..388d75e53262880c91e8e51658524fd8c71c655a --- /dev/null +++ b/src/fix_tmd.cpp @@ -0,0 +1,538 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing authors: Paul Crozier (SNL) + Christian Burisch (Bochum Univeristy, Germany) +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "math.h" +#include "stdlib.h" +#include "string.h" +#include "fix_tmd.h" +#include "atom.h" +#include "update.h" +#include "modify.h" +#include "domain.h" +#include "group.h" +#include "respa.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define CHUNK 1000 +#define MAXLINE 256 + +/* ---------------------------------------------------------------------- */ + +FixTMD::FixTMD(int narg, char **arg) : Fix(narg, arg) +{ + if (narg < 6) error->all("Illegal fix tmd command"); + + rho_stop = atof(arg[3]); + nfileevery = atoi(arg[5]); + if (rho_stop < 0 || nfileevery < 0) error->all("Illegal fix tmd command"); + if (nfileevery && narg != 7) error->all("Illegal fix tmd command"); + + MPI_Comm_rank(world,&me); + + // perform initial allocation of atom-based arrays + // register with atom class + + xf = NULL; + xold = NULL; + grow_arrays(atom->nmax); + atom->add_callback(0); + + // read from arg[4] and store coordinates of final target in xf + + readfile(arg[4]); + + // open arg[6] statistics file and write header + + if (nfileevery) { + if (narg != 7) error->all("Illegal fix tmd command"); + if (me == 0) { + fp = fopen(arg[6],"w"); + if (fp == NULL) { + char str[128]; + sprintf(str,"Cannot open fix tmd file %s",arg[6]); + error->one(str); + } + fprintf(fp,"%s %s\n","Step rho_target rho_old gamma_back", + "gamma_forward lambda work_lambda work_analytical"); + } + } + + masstotal = group->mass(igroup); + + // rho_start = initial rho + // xold = initial x or 0.0 if not in group + + int *mask = atom->mask; + int *type = atom->type; + int *image = atom->image; + double **x = atom->x; + double *mass = atom->mass; + int nlocal = atom->nlocal; + + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + + double dx,dy,dz; + int xbox,ybox,zbox; + rho_start = 0.0; + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + dx = x[i][0] + xbox*xprd - xf[i][0]; + dy = x[i][1] + ybox*yprd - xf[i][1]; + dz = x[i][2] + zbox*zprd - xf[i][2]; + rho_start += mass[type[i]]*(dx*dx + dy*dy + dz*dz); + xold[i][0] = x[i][0] + xbox*xprd; + xold[i][1] = x[i][1] + ybox*yprd; + xold[i][2] = x[i][2] + zbox*zprd; + } else xold[i][0] = xold[i][1] = xold[i][2] = 0.0; + } + + double rho_start_total; + MPI_Allreduce(&rho_start,&rho_start_total,1,MPI_DOUBLE,MPI_SUM,world); + rho_start = sqrt(rho_start_total/masstotal); + rho_old = rho_start; + + work_lambda = 0.0; + work_analytical = 0.0; + previous_stat = 0; +} + +/* ---------------------------------------------------------------------- */ + +FixTMD::~FixTMD() +{ + if (nfileevery && me == 0) fclose(fp); + + // 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_2d_double_array(xf); + memory->destroy_2d_double_array(xold); +} + +/* ---------------------------------------------------------------------- */ + +int FixTMD::setmask() +{ + int mask = 0; + mask |= INITIAL_INTEGRATE; + mask |= INITIAL_INTEGRATE_RESPA; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixTMD::init() +{ + // check that no integrator fix comes after a TMD fix + + int flag = 0; + for (int i = 0; i < modify->nfix; i++) { + if (strcmp(modify->fix[i]->style,"tmd") == 0) flag = 1; + if (flag && strcmp(modify->fix[i]->style,"nve") == 0) flag = 2; + if (flag && strcmp(modify->fix[i]->style,"nvt") == 0) flag = 2; + if (flag && strcmp(modify->fix[i]->style,"npt") == 0) flag = 2; + if (flag && strcmp(modify->fix[i]->style,"nph") == 0) flag = 2; + } + if (flag == 2) error->all("Fix tmd must come after integration fixes"); + + // timesteps + + dtv = update->dt; + dtf = update->dt * force->ftm2v; + if (strcmp(update->integrate_style,"respa") == 0) { + double *step_respa = ((Respa *) update->integrate)->step; + dtv = step_respa[0]; + dtf = step_respa[0] * force->ftm2v; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixTMD::initial_integrate() +{ + double a,b,c,d,e; + double dx,dy,dz,dxkt,dykt,dzkt; + double dxold,dyold,dzold,xback,yback,zback; + double gamma_forward,gamma_back,gamma_max,lambda; + double kt,fr,kttotal,frtotal; + + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + double **x = atom->x; + double **v = atom->v; + double **f = atom->f; + double *mass = atom->mass; + int *image = atom->image; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + int xbox,ybox,zbox; + + double delta = update->ntimestep - update->beginstep; + delta /= update->endstep - update->beginstep; + double rho_target = rho_start + delta * (rho_stop - rho_start); + + // compute the Lagrange multiplier + + a = b = e = 0.0; + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dxold = xold[i][0] - xf[i][0]; + dyold = xold[i][1] - xf[i][1]; + dzold = xold[i][2] - xf[i][2]; + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + dx = x[i][0] + xbox*xprd - xf[i][0]; + dy = x[i][1] + ybox*yprd - xf[i][1]; + dz = x[i][2] + zbox*zprd - xf[i][2]; + a += mass[type[i]]*(dxold*dxold + dyold*dyold + dzold*dzold); + b += mass[type[i]]*(dx *dxold + dy *dyold + dz *dzold); + e += mass[type[i]]*(dx *dx + dy *dy + dz *dz); + } + } + + double abe[3],abetotal[3]; + abe[0] = a; abe[1] = b; abe[2] = e; + MPI_Allreduce(abe,abetotal,3,MPI_DOUBLE,MPI_SUM,world); + + a = abetotal[0]/masstotal; + b = 2.0*abetotal[1]/masstotal; + e = abetotal[2]/masstotal; + c = e - rho_old*rho_old; + d = b*b - 4*a*c; + + if (d < 0) d = 0; + if (b >= 0) gamma_max = (-b - sqrt(d))/(2*a); + else gamma_max = (-b + sqrt(d))/(2*a); + gamma_back = c/(a*gamma_max); + if (a == 0.0) gamma_back = 0; + + c = e - rho_target*rho_target; + d = b*b - 4*a*c; + if (d < 0) d = 0; + if (b >= 0) gamma_max = (-b - sqrt(d))/(2*a); + else gamma_max = (-b + sqrt(d))/(2*a); + gamma_forward = c/(a*gamma_max); + if (a == 0.0) gamma_forward = 0; + + fr = kt = 0.0; + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dxold = xold[i][0] - xf[i][0]; + dyold = xold[i][1] - xf[i][1]; + dzold = xold[i][2] - xf[i][2]; + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + xback = x[i][0] + xbox*xprd + gamma_back*dxold; + yback = x[i][1] + ybox*yprd + gamma_back*dyold; + zback = x[i][2] + zbox*zprd + gamma_back*dzold; + dxkt = xback - xold[i][0]; + dykt = yback - xold[i][1]; + dzkt = zback - xold[i][2]; + kt += mass[type[i]]*(dxkt*dxkt + dykt*dykt + dzkt*dzkt); + fr += f[i][0]*dxold + f[i][1]*dyold + f[i][2]*dzold; + } + } + + double r[2],rtotal[2]; + r[0] = fr; r[1] = kt; + MPI_Allreduce(r,rtotal,2,MPI_DOUBLE,MPI_SUM,world); + frtotal = rtotal[0]; + kttotal = rtotal[1]; + + // stat write of mean constraint force based on previous time step constraint + + if (nfileevery && me == 0) { + work_analytical += + (-frtotal - kttotal/dtv/dtf)*(rho_target - rho_old)/rho_old; + lambda = gamma_back*rho_old*masstotal/dtv/dtf; + work_lambda += lambda*(rho_target - rho_old); + if (!(update->ntimestep % nfileevery) && + (previous_stat != update->ntimestep)) { + fprintf(fp,"%d %g %g %g %g %g %g %g\n", + update->ntimestep,rho_target,rho_old, + gamma_back,gamma_forward,lambda,work_lambda,work_analytical); + previous_stat = update->ntimestep; + } + } + rho_old = rho_target; + + // apply the constraint and save constrained positions for next step + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + dtfm = dtf / mass[type[i]]; + dxold = xold[i][0] - xf[i][0]; + x[i][0] += gamma_forward*dxold; + v[i][0] += gamma_forward*dxold/dtv; + f[i][0] += gamma_forward*dxold/dtv/dtfm; + dyold = xold[i][1] - xf[i][1]; + x[i][1] += gamma_forward*dyold; + v[i][1] += gamma_forward*dyold/dtv; + f[i][1] += gamma_forward*dyold/dtv/dtfm; + dzold = xold[i][2] - xf[i][2]; + x[i][2] += gamma_forward*dzold; + v[i][2] += gamma_forward*dzold/dtv; + f[i][2] += gamma_forward*dzold/dtv/dtfm; + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + xold[i][0] = x[i][0] + xbox*xprd; + xold[i][1] = x[i][1] + ybox*yprd; + xold[i][2] = x[i][2] + zbox*zprd; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixTMD::initial_integrate_respa(int ilevel, int flag) +{ + if (flag) return; // only used by NPT,NPH + if (ilevel == 0) initial_integrate(); +} + +/* ---------------------------------------------------------------------- + memory usage of local atom-based arrays +------------------------------------------------------------------------- */ + +int FixTMD::memory_usage() +{ + int bytes = 2*atom->nmax*3 * sizeof(double); + return bytes; +} + +/* ---------------------------------------------------------------------- + allocate atom-based arrays +------------------------------------------------------------------------- */ + +void FixTMD::grow_arrays(int nmax) +{ + xf = memory->grow_2d_double_array(xf,nmax,3,"fix_tmd:xf"); + xold = memory->grow_2d_double_array(xold,nmax,3,"fix_tmd:xold"); +} + +/* ---------------------------------------------------------------------- + copy values within local atom-based arrays +------------------------------------------------------------------------- */ + +void FixTMD::copy_arrays(int i, int j) +{ + xf[j][0] = xf[i][0]; + xf[j][1] = xf[i][1]; + xf[j][2] = xf[i][2]; + xold[j][0] = xold[i][0]; + xold[j][1] = xold[i][1]; + xold[j][2] = xold[i][2]; +} + +/* ---------------------------------------------------------------------- + pack values in local atom-based arrays for exchange with another proc +------------------------------------------------------------------------- */ + +int FixTMD::pack_exchange(int i, double *buf) +{ + buf[0] = xf[i][0]; + buf[1] = xf[i][1]; + buf[2] = xf[i][2]; + buf[3] = xold[i][0]; + buf[4] = xold[i][1]; + buf[5] = xold[i][2]; + return 6; +} + +/* ---------------------------------------------------------------------- + unpack values in local atom-based arrays from exchange with another proc +------------------------------------------------------------------------- */ + +int FixTMD::unpack_exchange(int nlocal, double *buf) +{ + xf[nlocal][0] = buf[0]; + xf[nlocal][1] = buf[1]; + xf[nlocal][2] = buf[2]; + xold[nlocal][0] = buf[3]; + xold[nlocal][1] = buf[4]; + xold[nlocal][2] = buf[5]; + return 6; +} + +/* ---------------------------------------------------------------------- + read target coordinates from file, store with appropriate atom +------------------------------------------------------------------------- */ + +void FixTMD::readfile(char *file) +{ + if (me == 0) { + if (screen) fprintf(screen,"Reading TMD target file %s ...\n",file); + open(file); + } + + int *mask = atom->mask; + int nlocal = atom->nlocal; + + char *buffer = new char[CHUNK*MAXLINE]; + char *ptr,*next,*bufptr; + int i,m,nlines,tag,imageflag,ix,iy,iz; + double x,y,z,xprd,yprd,zprd; + + int firstline = 1; + int ncount = 0; + int eof = 0; + xprd = yprd = zprd = -1.0; + + while (!eof) { + if (me == 0) { + m = 0; + for (nlines = 0; nlines < CHUNK; nlines++) { + ptr = fgets(&buffer[m],MAXLINE,fp); + if (ptr == NULL) break; + m += strlen(&buffer[m]); + } + if (ptr == NULL) eof = 1; + buffer[m++] = '\n'; + } + + MPI_Bcast(&eof,1,MPI_INT,0,world); + MPI_Bcast(&nlines,1,MPI_INT,0,world); + MPI_Bcast(&m,1,MPI_INT,0,world); + MPI_Bcast(buffer,m,MPI_CHAR,0,world); + + bufptr = buffer; + for (i = 0; i < nlines; i++) { + next = strchr(bufptr,'\n'); + *next = '\0'; + + if (firstline) { + if (strstr(bufptr,"xlo xhi")) { + double lo,hi; + sscanf(bufptr,"%lg %lg",&lo,&hi); + xprd = hi - lo; + bufptr = next + 1; + continue; + } else if (strstr(bufptr,"ylo yhi")) { + double lo,hi; + sscanf(bufptr,"%lg %lg",&lo,&hi); + yprd = hi - lo; + bufptr = next + 1; + continue; + } else if (strstr(bufptr,"zlo zhi")) { + double lo,hi; + sscanf(bufptr,"%lg %lg",&lo,&hi); + zprd = hi - lo; + bufptr = next + 1; + continue; + } else if (atom->count_words(bufptr) == 4) { + if (xprd >= 0.0 || yprd >= 0.0 || zprd >= 0.0) + error->all("Incorrect format in TMD target file"); + imageflag = 0; + firstline = 0; + } else if (atom->count_words(bufptr) == 7) { + if (xprd < 0.0 || yprd < 0.0 || zprd < 0.0) + error->all("Incorrect format in TMD target file"); + imageflag = 1; + firstline = 0; + } else error->all("Incorrect format in TMD target file"); + } + + if (imageflag) + sscanf(bufptr,"%d %lg %lg %lg %d %d %d",&tag,&x,&y,&z,&ix,&iy,&iz); + else + sscanf(bufptr,"%d %lg %lg %lg",&tag,&x,&y,&z); + + m = atom->map(tag); + if (m >= 0 && m < nlocal && mask[m] & groupbit) { + if (imageflag) { + xf[m][0] = x + ix*xprd; + xf[m][1] = y + iy*yprd; + xf[m][2] = z + iz*zprd; + } else { + xf[m][0] = x; + xf[m][1] = y; + xf[m][2] = z; + } + ncount++; + } + + bufptr = next + 1; + } + } + + // clean up + + delete [] buffer; + if (me == 0) fclose(fp); + + // check that all atoms in group were listed in target file + // set xf = 0.0 for atoms not in group + + int gcount = 0; + for (i = 0; i < nlocal; i++) + if (mask[i] & groupbit) gcount++; + else xf[i][0] = xf[i][1] = xf[i][2] = 0.0; + + int flag = 0; + if (gcount != ncount) flag = 1; + + int flagall; + MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_SUM,world); + if (flagall) error->all("TMD target file did not list all group atoms"); +} + +/* ---------------------------------------------------------------------- + proc 0 opens TMD data file + test if gzipped +------------------------------------------------------------------------- */ + +void FixTMD::open(char *file) +{ + int compressed = 0; + char *suffix = file + strlen(file) - 3; + if (suffix > file && strcmp(suffix,".gz") == 0) compressed = 1; + if (!compressed) fp = fopen(file,"r"); + else { +#ifdef GZIP + char gunzip[128]; + sprintf(gunzip,"gunzip -c %s",file); + fp = popen(gunzip,"r"); +#else + error->one("Cannot open gzipped file"); +#endif + } + + if (fp == NULL) { + char str[128]; + sprintf(str,"Cannot open file %s",file); + error->one(str); + } +} diff --git a/src/fix_tmd.h b/src/fix_tmd.h new file mode 100644 index 0000000000000000000000000000000000000000..adf140a0067aea4344f0ac6aada459f2c70dcc36 --- /dev/null +++ b/src/fix_tmd.h @@ -0,0 +1,48 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_TMD_H +#define FIX_TMD_H + +#include "stdio.h" +#include "fix.h" + +class FixTMD : public Fix { + public: + FixTMD(int, char **); + ~FixTMD(); + int setmask(); + void init(); + void initial_integrate(); + void initial_integrate_respa(int,int); + + int memory_usage(); + void grow_arrays(int); + void copy_arrays(int, int); + int pack_exchange(int, double *); + int unpack_exchange(int, double *); + + private: + int me; + int nfileevery,previous_stat; + FILE *fp; + double rho_start,rho_stop,rho_old,masstotal; + double dtv,dtf,dtfm; + double work_lambda,work_analytical; + double **xf,**xold; + + void readfile(char *); + void open(char *); +}; + +#endif diff --git a/src/fix_uniaxial.cpp b/src/fix_uniaxial.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b51c1a351e1cf44ecfa607eab5fdde171e0c4ae4 --- /dev/null +++ b/src/fix_uniaxial.cpp @@ -0,0 +1,204 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Carsten Svaneborg + (Max Planck Institute for Complex Systems, Dresden, Germany) +------------------------------------------------------------------------- */ + +#include "string.h" +#include "stdlib.h" +#include "math.h" +#include "fix_uniaxial.h" +#include "atom.h" +#include "update.h" +#include "force.h" +#include "domain.h" +#include "modify.h" +#include "comm.h" +#include "kspace.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixUniaxial::FixUniaxial(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 6) error->all("Illegal fix uniaxial command"); + nevery = atoi(arg[3]); + if (nevery <= 0) error->all("Illegal fix uniaxial command"); + + if (strcmp(arg[4],"x") == 0) dir = 0; + else if (strcmp(arg[4],"y") == 0) dir = 1; + else if (strcmp(arg[4],"z") == 0) dir = 2; + else error->all("Illegal fix uniaxial command"); + lambda_final = atof(arg[5]); + + if (lambda_final <= 0) error->all("Illegal fix uniaxial command"); + if (domain->nonperiodic) + error->all("Cannot fix uniaxial on non-periodic system"); + + nrigid = 0; + rfix = NULL; +} + +/* ---------------------------------------------------------------------- */ + +FixUniaxial::~FixUniaxial() +{ + delete [] rfix; +} + +/* ---------------------------------------------------------------------- */ + +int FixUniaxial::setmask() +{ + int mask = 0; + mask |= END_OF_STEP; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixUniaxial::init() +{ + // store pointers to domain variable so can loop over dimensions + + domainlo[0] = &domain->boxxlo; + domainlo[1] = &domain->boxylo; + domainlo[2] = &domain->boxzlo; + + domainhi[0] = &domain->boxxhi; + domainhi[1] = &domain->boxyhi; + domainhi[2] = &domain->boxzhi; + + domainprd[0] = &domain->xprd; + domainprd[1] = &domain->yprd; + domainprd[2] = &domain->zprd; + + double L = pow((domain->boxxhi-domain->boxxlo)* + (domain->boxyhi-domain->boxylo)* + (domain->boxzhi-domain->boxzlo) ,1.0/3.0); + + // save box sizes for coordinate rescaling + // calculate strains and asymmetry parameter + // alpha=lampdai[first]/lampbdai[second] for the two perp directions + + alpha0 = 1.0; + for (int m = 0; m < 3; m++) { + domainloi[m] = *domainlo[m]; + domainhii[m] = *domainhi[m]; + lambdai[m] = (*domainhi[m] - *domainlo[m])/L; + lambdaf[m] = ( m==dir ? lambda_final : 1.0/sqrt(lambda_final) ) ; + if (m != dir) { + alpha0*= lambdai[m]; + alpha0=1.0/alpha0; + } + } + + if (comm->me == 0) { + if (screen) { + fprintf(screen,"Initial strain = %g %g %g\n", + lambdai[0],lambdai[1],lambdai[2]); + fprintf(screen,"Target strain = %g %g %g\n", + lambdaf[0],lambdaf[1],lambdaf[2]); + } + if (logfile) { + fprintf(logfile,"Initial strain = %g %g %g\n", + lambdai[0],lambdai[1],lambdai[2]); + fprintf(logfile,"Target strain = %g %g %g\n", + lambdaf[0],lambdaf[1],lambdaf[2]); + } + } + + if (force->kspace) kspace_flag = 1; + else kspace_flag = 0; + + // detect if any fix rigid exist so rigid bodies can be re-scaled + // rfix[] = indices to each fix rigid + + delete [] rfix; + nrigid = 0; + rfix = NULL; + + for (int i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"rigid") == 0 || + strcmp(modify->fix[i]->style,"poems") == 0) nrigid++; + if (nrigid) { + rfix = new int[nrigid]; + nrigid = 0; + for (int i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"rigid") == 0 || + strcmp(modify->fix[i]->style,"poems") == 0) rfix[nrigid++] = i; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixUniaxial::end_of_step() +{ + int i,m; + double oldlo,oldhi,newlo,newhi,ratio; + + double delta = update->ntimestep - update->beginstep; + delta /= update->endstep - update->beginstep; + + double lvalue[3]; + + // linear interpolation of strain in specified direction + + lvalue[dir] = lambdai[dir]*(1.0-delta) + lambdaf[dir]*delta; + + // linear interpolation of asymmetry parameter in the perp direction + + double alpha = alpha0*(1-delta) + delta; + + // calculate strains perpendicular to dir + + for (m = 0; m < 3; m++) + if (m != dir) { + lvalue[m] = sqrt(alpha/lvalue[dir]); + alpha=1.0/alpha; + } + + // apply appropriate rescaling in each dimension + + double **x = atom->x; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (m = 0; m < 3; m++) { + oldlo = *domainlo[m]; + oldhi = *domainhi[m]; + + newlo = domainloi[m] * lvalue[m]/lambdai[m]; + newhi = domainhii[m] * lvalue[m]/lambdai[m]; + ratio = (newhi - newlo) / *domainprd[m]; + + for (i = 0; i < nlocal; i++) + if (mask[i] & groupbit) + x[i][m] = newlo + (x[i][m] - oldlo) * ratio; + + *domainlo[m] = newlo; + *domainhi[m] = newhi; + *domainprd[m] = newhi - newlo; + + if (nrigid) + for (i = 0; i < nrigid; i++) + modify->fix[rfix[i]]->dilate(m,oldlo,oldhi,newlo,newhi); + } + + // redo KSpace coeffs since volume has changed + + if (kspace_flag) force->kspace->setup(); +} + diff --git a/src/fix_uniaxial.h b/src/fix_uniaxial.h new file mode 100644 index 0000000000000000000000000000000000000000..156d3568044980f309e39ea64dfabb102ca3e909 --- /dev/null +++ b/src/fix_uniaxial.h @@ -0,0 +1,47 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_UNIAXIAL_H +#define FIX_UNIAXIAL_H + +#include "fix.h" + +class FixUniaxial : public Fix { + public: + FixUniaxial(int, char **); + ~FixUniaxial(); + int setmask(); + void init(); + void end_of_step(); + + private: + int dir; + double lambda_final; + double alpha0; // asymmetry parameter + + double lambdai[3]; // initial + double lambdaf[3]; // final + double domainloi[3]; // initial box lo/hi + double domainhii[3]; + double *domainlo[3]; // pointers to make loop over dims possible + double *domainhi[3]; + double *domainprd[3]; + + int kspace_flag; // 1 if KSpace invoked, 0 if not + int nrigid; // number of rigid fixes + int *rfix; // indices of rigid fixes +}; + +#endif + + diff --git a/src/fix_viscous.cpp b/src/fix_viscous.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4c676dd1c336b480fcd5089bc4689946f41106af --- /dev/null +++ b/src/fix_viscous.cpp @@ -0,0 +1,117 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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_viscous.h" +#include "atom.h" +#include "update.h" +#include "respa.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixViscous::FixViscous(int narg, char **arg) : Fix(narg, arg) +{ + if (narg < 4) error->all("Illegal fix viscous command"); + + double gamma_one = atof(arg[3]); + gamma = new double[atom->ntypes+1]; + for (int i = 1; i <= atom->ntypes; i++) gamma[i] = gamma_one; + + // optional args + + int iarg = 4; + while (iarg < narg) { + if (strcmp(arg[iarg],"scale") == 0) { + if (iarg+3 > narg) error->all("Illegal fix viscous command"); + int itype = atoi(arg[iarg+1]); + double scale = atof(arg[iarg+2]); + if (itype <= 0 || itype > atom->ntypes) + error->all("Illegal fix viscous command"); + gamma[itype] = gamma_one * scale; + iarg += 3; + } else error->all("Illegal fix viscous command"); + } +} + +/* ---------------------------------------------------------------------- */ + +FixViscous::~FixViscous() +{ + delete [] gamma; +} + +/* ---------------------------------------------------------------------- */ + +int FixViscous::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + mask |= POST_FORCE_RESPA; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixViscous::init() +{ + if (strcmp(update->integrate_style,"respa") == 0) + nlevels_respa = ((Respa *) update->integrate)->nlevels; +} + +/* ---------------------------------------------------------------------- */ + +void FixViscous::setup() +{ + if (strcmp(update->integrate_style,"verlet") == 0) + post_force(1); + else { + ((Respa *) update->integrate)->copy_flevel_f(nlevels_respa-1); + post_force_respa(1,nlevels_respa-1,0); + ((Respa *) update->integrate)->copy_f_flevel(nlevels_respa-1); + } +} + +/* ---------------------------------------------------------------------- */ + +void FixViscous::post_force(int vflag) +{ + // apply drag force to atoms in group + // direction is opposed to velocity vector + // magnitude depends on atom type + + double **v = atom->v; + double **f = atom->f; + int *mask = atom->mask; + int *type = atom->type; + int nlocal = atom->nlocal; + + double drag; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + drag = gamma[type[i]]; + f[i][0] -= drag*v[i][0]; + f[i][1] -= drag*v[i][1]; + f[i][2] -= drag*v[i][2]; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixViscous::post_force_respa(int vflag, int ilevel, int iloop) +{ + if (ilevel == nlevels_respa-1) post_force(vflag); +} diff --git a/src/fix_viscous.h b/src/fix_viscous.h new file mode 100644 index 0000000000000000000000000000000000000000..c6a63a8cd2221ac91b4dacef4833dffaa13cd838 --- /dev/null +++ b/src/fix_viscous.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_VISCOUS_H +#define FIX_VISCOUS_H + +#include "fix.h" + +class FixViscous : public Fix { + public: + FixViscous(int, char **); + ~FixViscous(); + int setmask(); + void init(); + void setup(); + void post_force(int); + void post_force_respa(int, int, int); + + private: + double *gamma; + int nlevels_respa; +}; + +#endif diff --git a/src/fix_volume_rescale.cpp b/src/fix_volume_rescale.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d2dc96013b8752ad9d2299c9edbb307d60bf638e --- /dev/null +++ b/src/fix_volume_rescale.cpp @@ -0,0 +1,185 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "math.h" +#include "fix_volume_rescale.h" +#include "update.h" +#include "force.h" +#include "modify.h" +#include "kspace.h" +#include "domain.h" +#include "atom.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixVolRescale::FixVolRescale(int narg, char **arg) : Fix(narg, arg) +{ + if (narg < 7) error->all("Illegal fix volume/rescale command"); + nevery = atoi(arg[3]); + if (nevery <= 0) error->all("Illegal fix volume/rescale command"); + + xflag = yflag = zflag = 0; + int iarg = 4; + while (iarg < narg) { + if (strcmp(arg[iarg],"x") == 0) { + if (iarg+3 > narg) error->all("Illegal fix volume/rescale command"); + if (domain->xperiodic == 0) + error->all("Cannot fix volume/rescale on a non-periodic boundary"); + xflag = 1; + xlo_stop = atof(arg[iarg+1]); + xhi_stop = atof(arg[iarg+2]); + iarg += 3; + } else if (strcmp(arg[iarg],"y") == 0) { + if (iarg+3 > narg) error->all("Illegal fix volume/rescale command"); + if (domain->yperiodic == 0) + error->all("Cannot fix volume/rescale on a non-periodic boundary"); + yflag = 1; + ylo_stop = atof(arg[iarg+1]); + yhi_stop = atof(arg[iarg+2]); + iarg += 3; + } else if (strcmp(arg[iarg],"z") == 0) { + if (iarg+3 > narg) error->all("Illegal fix volume/rescale command"); + if (domain->zperiodic == 0) + error->all("Cannot fix volume/rescale on a non-periodic boundary"); + zflag = 1; + zlo_stop = atof(arg[iarg+1]); + zhi_stop = atof(arg[iarg+2]); + iarg += 3; + } else error->all("Illegal fix volume/rescale command"); + } + + nrigid = 0; + rfix = NULL; +} + +/* ---------------------------------------------------------------------- */ + +FixVolRescale::~FixVolRescale() +{ + delete [] rfix; +} + +/* ---------------------------------------------------------------------- */ + +int FixVolRescale::setmask() +{ + int mask = 0; + mask |= END_OF_STEP; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixVolRescale::init() +{ + xlo_start = domain->boxxlo; + xhi_start = domain->boxxhi; + ylo_start = domain->boxylo; + yhi_start = domain->boxyhi; + zlo_start = domain->boxzlo; + zhi_start = domain->boxzhi; + + if (force->kspace) kspace_flag = 1; + else kspace_flag = 0; + + // detect if any fix rigid exist so rigid bodies can be rescaled + // rfix[] = indices to each fix rigid + + delete [] rfix; + nrigid = 0; + rfix = NULL; + + for (int i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"rigid") == 0 || + strcmp(modify->fix[i]->style,"poems") == 0) nrigid++; + if (nrigid) { + rfix = new int[nrigid]; + nrigid = 0; + for (int i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"rigid") == 0 || + strcmp(modify->fix[i]->style,"poems") == 0) rfix[nrigid++] = i; + } +} + +/* ---------------------------------------------------------------------- */ + +void FixVolRescale::end_of_step() +{ + int i; + double oldlo,oldhi,newlo,newhi,ratio; + + double delta = update->ntimestep - update->beginstep; + delta /= update->endstep - update->beginstep; + + double **x = atom->x; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + if (xflag) { + oldlo = domain->boxxlo; + oldhi = domain->boxxhi; + newlo = xlo_start + delta * (xlo_stop-xlo_start); + newhi = xhi_start + delta * (xhi_stop-xhi_start); + ratio = (newhi - newlo) / domain->xprd; + for (i = 0; i < nlocal; i++) + if (mask[i] & groupbit) + x[i][0] = newlo + (x[i][0] - oldlo) * ratio; + domain->boxxlo = newlo; + domain->boxxhi = newhi; + domain->xprd = newhi - newlo; + if (nrigid) + for (i = 0; i < nrigid; i++) + modify->fix[rfix[i]]->dilate(0,oldlo,oldhi,newlo,newhi); + } + + if (yflag) { + oldlo = domain->boxylo; + oldhi = domain->boxyhi; + newlo = ylo_start + delta * (ylo_stop-ylo_start); + newhi = yhi_start + delta * (yhi_stop-yhi_start); + ratio = (newhi - newlo) / domain->yprd; + for (i = 0; i < nlocal; i++) + if (mask[i] & groupbit) + x[i][1] = newlo + (x[i][1] - oldlo) * ratio; + domain->boxylo = newlo; + domain->boxyhi = newhi; + domain->yprd = newhi - newlo; + if (nrigid) + for (i = 0; i < nrigid; i++) + modify->fix[rfix[i]]->dilate(1,oldlo,oldhi,newlo,newhi); + } + + if (zflag) { + oldlo = domain->boxzlo; + oldhi = domain->boxzhi; + newlo = zlo_start + delta * (zlo_stop-zlo_start); + newhi = zhi_start + delta * (zhi_stop-zhi_start); + ratio = (newhi - newlo) / domain->zprd; + for (i = 0; i < nlocal; i++) + if (mask[i] & groupbit) + x[i][2] = newlo + (x[i][2] - oldlo) * ratio; + domain->boxzlo = newlo; + domain->boxzhi = newhi; + domain->zprd = newhi - newlo; + if (nrigid) + for (i = 0; i < nrigid; i++) + modify->fix[rfix[i]]->dilate(2,oldlo,oldhi,newlo,newhi); + } + + // redo KSpace coeffs since volume has changed + + if (kspace_flag) force->kspace->setup(); +} diff --git a/src/fix_volume_rescale.h b/src/fix_volume_rescale.h new file mode 100644 index 0000000000000000000000000000000000000000..7714fdac1b24a907d28d7e82fe9fecd8d9b2a234 --- /dev/null +++ b/src/fix_volume_rescale.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_VOL_RESCALE_H +#define FIX_VOL_RESCALE_H + +#include "fix.h" + +class FixVolRescale : public Fix { + public: + FixVolRescale(int, char **); + ~FixVolRescale(); + int setmask(); + void init(); + void end_of_step(); + + private: + int xflag,yflag,zflag; + double xlo_start,xlo_stop,xhi_start,xhi_stop; + double ylo_start,ylo_stop,yhi_start,yhi_stop; + double zlo_start,zlo_stop,zhi_start,zhi_stop; + int kspace_flag; // 1 if KSpace invoked, 0 if not + int nrigid; // number of rigid fixes + int *rfix; // indices of rigid fixes +}; + +#endif diff --git a/src/fix_wall_lj93.cpp b/src/fix_wall_lj93.cpp new file mode 100644 index 0000000000000000000000000000000000000000..24b4b8a47412d7095672496ffe46c8b3390d4a41 --- /dev/null +++ b/src/fix_wall_lj93.cpp @@ -0,0 +1,172 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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_wall_lj93.h" +#include "atom.h" +#include "update.h" +#include "output.h" +#include "respa.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixWallLJ93::FixWallLJ93(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 8) error->all("Illegal fix wall/lj93 command"); + + if (strcmp(arg[3],"xlo") == 0) { + dim = 0; + side = -1; + } else if (strcmp(arg[3],"xhi") == 0) { + dim = 0; + side = 1; + } else if (strcmp(arg[3],"ylo") == 0) { + dim = 1; + side = -1; + } else if (strcmp(arg[3],"yhi") == 0) { + dim = 1; + side = 1; + } else if (strcmp(arg[3],"zlo") == 0) { + dim = 2; + side = -1; + } else if (strcmp(arg[3],"zhi") == 0) { + dim = 2; + side = 1; + } else error->all("Illegal fix wall/lj93 command"); + + coord = atof(arg[4]); + epsilon = atof(arg[5]); + sigma = atof(arg[6]); + cutoff = atof(arg[7]); + + coeff1 = 6.0/5.0 * epsilon * pow(sigma,9.0); + coeff2 = 3.0 * epsilon * pow(sigma,3.0); + coeff3 = 2.0/15.0 * epsilon * pow(sigma,9.0); + coeff4 = epsilon * pow(sigma,3.0); +} + +/* ---------------------------------------------------------------------- */ + +int FixWallLJ93::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + mask |= THERMO; + mask |= POST_FORCE_RESPA; + mask |= MIN_POST_FORCE; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixWallLJ93::init() +{ + if (strcmp(update->integrate_style,"respa") == 0) + nlevels_respa = ((Respa *) update->integrate)->nlevels; + + if (thermo_print || thermo_energy) thermo_flag = 1; + else thermo_flag = 0; +} + +/* ---------------------------------------------------------------------- */ + +void FixWallLJ93::setup() +{ + eflag_on = 1; + if (strcmp(update->integrate_style,"verlet") == 0) + post_force(1); + else { + ((Respa *) update->integrate)->copy_flevel_f(nlevels_respa-1); + post_force_respa(1,nlevels_respa-1,0); + ((Respa *) update->integrate)->copy_f_flevel(nlevels_respa-1); + } + eflag_on = 0; +} + +/* ---------------------------------------------------------------------- */ + +void FixWallLJ93::min_setup() +{ + eflag_on = 1; + post_force(1); +} + +/* ---------------------------------------------------------------------- */ + +void FixWallLJ93::post_force(int vflag) +{ + bool eflag = false; + if (thermo_flag) { + if (eflag_on) eflag = true; + else if (output->next_thermo == update->ntimestep) eflag = true; + } + + double **x = atom->x; + double **f = atom->f; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double delta,rinv,r2inv,r4inv,r10inv; + if (eflag) eng = 0.0; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + if (side == -1) delta = x[i][dim] - coord; + else delta = coord - x[i][dim]; + if (delta <= 0.0) continue; + if (delta > cutoff) continue; + rinv = 1.0/delta; + r2inv = rinv*rinv; + r4inv = r2inv*r2inv; + r10inv = r4inv*r4inv*r2inv; + f[i][dim] -= (coeff1*r10inv - coeff2*r4inv) * side; + if (eflag) eng += coeff3*r4inv*r4inv*rinv - coeff4*r2inv*rinv; + } + + if (eflag) MPI_Allreduce(&eng,&etotal,1,MPI_DOUBLE,MPI_SUM,world); +} + +/* ---------------------------------------------------------------------- */ + +void FixWallLJ93::post_force_respa(int vflag, int ilevel, int iloop) +{ + if (ilevel == nlevels_respa-1) post_force(vflag); +} + +/* ---------------------------------------------------------------------- */ + +void FixWallLJ93::min_post_force(int vflag) +{ + post_force(vflag); +} + +/* ---------------------------------------------------------------------- */ + +int FixWallLJ93::thermo_fields(int n, int *flags, char **keywords) +{ + if (n == 0) return 1; + flags[0] = 3; + strcpy(keywords[0],"Wall"); + return 1; +} + +/* ---------------------------------------------------------------------- */ + +int FixWallLJ93::thermo_compute(double *values) +{ + values[0] = etotal; + return 1; +} diff --git a/src/fix_wall_lj93.h b/src/fix_wall_lj93.h new file mode 100644 index 0000000000000000000000000000000000000000..c8cc09af00e72ec148f946994c666d0de203061f --- /dev/null +++ b/src/fix_wall_lj93.h @@ -0,0 +1,41 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_WALL_LJ93_H +#define FIX_WALL_LJ93_H + +#include "fix.h" + +class FixWallLJ93 : public Fix { + public: + FixWallLJ93(int, char **); + ~FixWallLJ93() {} + int setmask(); + void init(); + void setup(); + void min_setup(); + void post_force(int); + void post_force_respa(int, int, int); + void min_post_force(int); + int thermo_fields(int, int *, char **); + int thermo_compute(double *); + + private: + int dim,side,thermo_flag,eflag_on; + double coord,epsilon,sigma,cutoff; + double coeff1,coeff2,coeff3,coeff4; + double eng,etotal; + int nlevels_respa; +}; + +#endif diff --git a/src/fix_wall_reflect.cpp b/src/fix_wall_reflect.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e0ad276ea3ec87489a6ba266b97d160c2b4fcdfe --- /dev/null +++ b/src/fix_wall_reflect.cpp @@ -0,0 +1,91 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "fix_wall_reflect.h" +#include "atom.h" +#include "domain.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixWallReflect::FixWallReflect(int narg, char **arg) : Fix(narg, arg) +{ + if (narg < 4) error->all("Illegal fix wall/reflect command"); + + xloflag = xhiflag = yloflag = yhiflag = zloflag = zhiflag = 0; + for (int iarg = 3; iarg < narg; iarg++) { + if (strcmp(arg[iarg],"xlo") == 0) xloflag = 1; + else if (strcmp(arg[iarg],"xhi") == 0) xhiflag = 1; + else if (strcmp(arg[iarg],"ylo") == 0) yloflag = 1; + else if (strcmp(arg[iarg],"yhi") == 0) yhiflag = 1; + else if (strcmp(arg[iarg],"zlo") == 0) zloflag = 1; + else if (strcmp(arg[iarg],"zhi") == 0) zhiflag = 1; + else error->all("Illegal fix wall/reflect command"); + } +} + +/* ---------------------------------------------------------------------- */ + +int FixWallReflect::setmask() +{ + int mask = 0; + mask |= INITIAL_INTEGRATE; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixWallReflect::initial_integrate() +{ + double xlo = domain->boxxlo; + double xhi = domain->boxxhi; + double ylo = domain->boxylo; + double yhi = domain->boxyhi; + double zlo = domain->boxzlo; + double zhi = domain->boxzhi; + + double **x = atom->x; + double **v = atom->v; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + if (xloflag && x[i][0] < xlo) { + x[i][0] = xlo + (xlo - x[i][0]); + v[i][0] = -v[i][0]; + } + if (xhiflag && x[i][0] > xhi) { + x[i][0] = xhi - (x[i][0] - xhi); + v[i][0] = -v[i][0]; + } + if (yloflag && x[i][1] < ylo) { + x[i][1] = ylo + (ylo - x[i][1]); + v[i][1] = -v[i][1]; + } + if (yhiflag && x[i][1] > yhi) { + x[i][1] = yhi - (x[i][1] - yhi); + v[i][1] = -v[i][1]; + } + if (zloflag && x[i][2] < zlo) { + x[i][2] = zlo + (zlo - x[i][2]); + v[i][2] = -v[i][2]; + } + if (zhiflag && x[i][2] > zhi) { + x[i][2] = zhi - (x[i][2] - zhi); + v[i][2] = -v[i][2]; + } + } + } +} diff --git a/src/fix_wall_reflect.h b/src/fix_wall_reflect.h new file mode 100644 index 0000000000000000000000000000000000000000..175d914df45dcf72b60579abbb48bbf200bb4656 --- /dev/null +++ b/src/fix_wall_reflect.h @@ -0,0 +1,30 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_WALL_REFLECT_H +#define FIX_WALL_REFELCT_H + +#include "fix.h" + +class FixWallReflect : public Fix { + public: + FixWallReflect(int, char **); + ~FixWallReflect() {} + int setmask(); + void initial_integrate(); + + private: + int xloflag,xhiflag,yloflag,yhiflag,zloflag,zhiflag; +}; + +#endif diff --git a/src/fix_wiggle.cpp b/src/fix_wiggle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d2e95f7f283d5cd98f0848a86589e5547bce3ca3 --- /dev/null +++ b/src/fix_wiggle.cpp @@ -0,0 +1,175 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "string.h" +#include "stdlib.h" +#include "fix_wiggle.h" +#include "atom.h" +#include "update.h" +#include "respa.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +FixWiggle::FixWiggle(int narg, char **arg) : Fix(narg, arg) +{ + if (narg != 6) error->all("Illegal fix wiggle command"); + + // parse command-line args + + if (strcmp(arg[3],"x") == 0) axis = 0; + else if (strcmp(arg[3],"y") == 0) axis = 1; + else if (strcmp(arg[3],"z") == 0) axis = 2; + else error->all("Illegal fix wiggle command"); + + amplitude = atof(arg[4]); + period = atof(arg[5]); + + // perform initial allocation of atom-based array + // register with atom class + + original = NULL; + grow_arrays(atom->nmax); + atom->add_callback(0); + + // oscillation parameters + + double PI = 4.0 * atan(1.0); + omega = 2.0*PI / period; + time_origin = update->ntimestep; + + // original = initial atom coord in wiggled direction + + double **x = atom->x; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) original[i] = x[i][axis]; + else original[i] = 0.0; +} + +/* ---------------------------------------------------------------------- */ + +FixWiggle::~FixWiggle() +{ + // 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 array + + memory->sfree(original); +} + +/* ---------------------------------------------------------------------- */ + +int FixWiggle::setmask() +{ + int mask = 0; + mask |= POST_FORCE; + mask |= POST_FORCE_RESPA; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixWiggle::init() +{ + dt = update->dt; + + if (strcmp(update->integrate_style,"respa") == 0) + nlevels_respa = ((Respa *) update->integrate)->nlevels; +} + +/* ---------------------------------------------------------------------- */ + +void FixWiggle::post_force(int vflag) +{ + double arg = omega * (update->ntimestep - time_origin) * dt; + double cosine = cos(arg); + double sine = sin(arg); + + double **x = atom->x; + double **v = atom->v; + double **f = atom->f; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + x[i][axis] = original[i] + amplitude - amplitude*cosine; + v[i][axis] = dt * amplitude*omega*sine; + f[i][axis] = 0.0; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FixWiggle::post_force_respa(int vflag, int ilevel, int iloop) +{ + if (ilevel == nlevels_respa-1) post_force(vflag); +} + +/* ---------------------------------------------------------------------- + memory usage of local atom-based array +------------------------------------------------------------------------- */ + +int FixWiggle::memory_usage() +{ + int bytes = atom->nmax * sizeof(double); + return bytes; +} + +/* ---------------------------------------------------------------------- + allocate local atom-based array +------------------------------------------------------------------------- */ + +void FixWiggle::grow_arrays(int nmax) +{ + original = (double *) + memory->srealloc(original,nmax*sizeof(double),"wiggle:original"); +} + +/* ---------------------------------------------------------------------- + copy values within local atom-based array +------------------------------------------------------------------------- */ + +void FixWiggle::copy_arrays(int i, int j) +{ + original[j] = original[i]; +} + +/* ---------------------------------------------------------------------- + pack values in local atom-based array for exchange with another proc +------------------------------------------------------------------------- */ + +int FixWiggle::pack_exchange(int i, double *buf) +{ + buf[0] = original[i]; + return 1; +} + +/* ---------------------------------------------------------------------- + unpack values in local atom-based array from exchange with another proc +------------------------------------------------------------------------- */ + +int FixWiggle::unpack_exchange(int nlocal, double *buf) +{ + original[nlocal] = buf[0]; + return 1; +} diff --git a/src/fix_wiggle.h b/src/fix_wiggle.h new file mode 100644 index 0000000000000000000000000000000000000000..09ead6223962d2ace3d49d5a4b16724f6413d741 --- /dev/null +++ b/src/fix_wiggle.h @@ -0,0 +1,42 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIX_WIGGLE_H +#define FIX_WIGGLE_H + +#include "fix.h" + +class FixWiggle : public Fix { + public: + FixWiggle(int, char **); + ~FixWiggle(); + int setmask(); + void init(); + void post_force(int); + void post_force_respa(int, int, int); + + int memory_usage(); + void grow_arrays(int); + void copy_arrays(int, int); + int pack_exchange(int, double *); + int unpack_exchange(int, double *); + + private: + double dt; + double *original; + double amplitude,period,omega; + int axis,time_origin; + int nlevels_respa; +}; + +#endif diff --git a/src/force.cpp b/src/force.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2f7e7edc6ae1d83f04efa93e191c1146863a4742 --- /dev/null +++ b/src/force.cpp @@ -0,0 +1,526 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "string.h" +#include "force.h" +#include "atom.h" +#include "comm.h" +#include "pair.h" +#include "pair_hybrid.h" +#include "bond.h" +#include "bond_hybrid.h" +#include "angle.h" +#include "dihedral.h" +#include "improper.h" +#include "kspace.h" +#include "temperature.h" +#include "pressure.h" +#include "group.h" +#include "memory.h" +#include "error.h" + +#define BondInclude +#define AngleInclude +#define DihedralInclude +#define ImproperInclude +#define PairInclude +#define KSpaceInclude +#define TempInclude +#include "style.h" +#undef BondInclude +#undef AngleInclude +#undef DihedralInclude +#undef ImproperInclude +#undef PairInclude +#undef KSpaceInclude +#undef TempInclude + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +#define DELTA 1 + +/* ---------------------------------------------------------------------- */ + +Force::Force() +{ + dimension = 3; + newton = newton_pair = newton_bond = 1; + + special_lj[1] = special_lj[2] = special_lj[3] = 0.0; + special_coul[1] = special_coul[2] = special_coul[3] = 0.0; + + dielectric = 1.0; + + pair = NULL; + bond = NULL; + angle = NULL; + dihedral = NULL; + improper = NULL; + kspace = NULL; + + char *str = "none"; + int n = strlen(str) + 1; + pair_style = new char[n]; + strcpy(pair_style,str); + bond_style = new char[n]; + strcpy(bond_style,str); + angle_style = new char[n]; + strcpy(angle_style,str); + dihedral_style = new char[n]; + strcpy(dihedral_style,str); + improper_style = new char[n]; + strcpy(improper_style,str); + kspace_style = new char[n]; + strcpy(kspace_style,str); + + // default temperature = group all and style full + + ntemp = maxtemp = 0; + templist = NULL; + + char **arg = new char*[3]; + arg[0] = "default"; + arg[1] = "all"; + arg[2] = "full"; + add_temp(3,arg,0); + delete [] arg; + + pressure = new Pressure; +} + +/* ---------------------------------------------------------------------- */ + +Force::~Force() +{ + delete [] pair_style; + delete [] bond_style; + delete [] angle_style; + delete [] dihedral_style; + delete [] improper_style; + delete [] kspace_style; + + if (pair) delete pair; + if (bond) delete bond; + if (angle) delete angle; + if (dihedral) delete dihedral; + if (improper) delete improper; + if (kspace) delete kspace; + + for (int i = 0; i < ntemp; i++) delete templist[i]; + memory->sfree(templist); + delete pressure; +} + +/* ---------------------------------------------------------------------- */ + +void Force::init() +{ + qqrd2e = qqr2e/dielectric; + + comm->maxforward_pair = comm->maxreverse_pair = 0; + + if (kspace) kspace->init(); // kspace must come before pair + if (pair) pair->init(); // so g_ewald is defined + if (bond) bond->init(); + if (angle) angle->init(); + if (dihedral) dihedral->init(); + if (improper) improper->init(); + + for (int itemp = 0; itemp < ntemp; itemp++) templist[itemp]->init(); + pressure->init(); +} + +/* ---------------------------------------------------------------------- + create a pair style, called from input script or restart file +------------------------------------------------------------------------- */ + +void Force::create_pair(char *style) +{ + delete [] pair_style; + if (pair) delete pair; + + pair = new_pair(style); + int n = strlen(style) + 1; + pair_style = new char[n]; + strcpy(pair_style,style); +} + +/* ---------------------------------------------------------------------- + generate a pair class +------------------------------------------------------------------------- */ + +Pair *Force::new_pair(char *style) +{ + if (strcmp(style,"none") == 0) return NULL; + +#define PairClass +#define PairStyle(key,Class) \ + else if (strcmp(style,#key) == 0) return new Class(); +#include "style.h" +#undef PairClass + + else error->all("Invalid pair style"); + return NULL; +} + +/* ---------------------------------------------------------------------- + return ptr to current pair class or hybrid sub-class if matches style +------------------------------------------------------------------------- */ + +Pair *Force::pair_match(char *style) +{ + if (strcmp(pair_style,style) == 0) return pair; + else if (strcmp(pair_style,"hybrid") == 0) { + PairHybrid *hpair = (PairHybrid *) pair; + for (int i = 0; i < hpair->nstyles; i++) + if (strcmp(hpair->keywords[i],style) == 0) return hpair->styles[i]; + } + return NULL; +} + +/* ---------------------------------------------------------------------- + create a bond style, called from input script or restart file +------------------------------------------------------------------------- */ + +void Force::create_bond(char *style) +{ + delete [] bond_style; + if (bond) delete bond; + + bond = new_bond(style); + int n = strlen(style) + 1; + bond_style = new char[n]; + strcpy(bond_style,style); +} + +/* ---------------------------------------------------------------------- + generate a bond class +------------------------------------------------------------------------- */ + +Bond *Force::new_bond(char *style) +{ + if (strcmp(style,"none") == 0) return NULL; + +#define BondClass +#define BondStyle(key,Class) \ + else if (strcmp(style,#key) == 0) return new Class(); +#include "style.h" +#undef BondClass + + else error->all("Invalid bond style"); + return NULL; +} + +/* ---------------------------------------------------------------------- + return ptr to current bond class or hybrid sub-class if matches style +------------------------------------------------------------------------- */ + +Bond *Force::bond_match(char *style) +{ + if (strcmp(bond_style,style) == 0) return bond; + else if (strcmp(bond_style,"hybrid") == 0) { + BondHybrid *hbond = (BondHybrid *) bond; + for (int i = 0; i < hbond->nstyles; i++) + if (strcmp(hbond->keywords[i],style) == 0) return hbond->styles[i]; + } + return NULL; +} + +/* ---------------------------------------------------------------------- + create an angle style, called from input script or restart file +------------------------------------------------------------------------- */ + +void Force::create_angle(char *style) +{ + delete [] angle_style; + if (angle) delete angle; + + angle = new_angle(style); + int n = strlen(style) + 1; + angle_style = new char[n]; + strcpy(angle_style,style); +} + +/* ---------------------------------------------------------------------- + generate an angle class +------------------------------------------------------------------------- */ + +Angle *Force::new_angle(char *style) +{ + if (strcmp(style,"none") == 0) return NULL; + +#define AngleClass +#define AngleStyle(key,Class) \ + else if (strcmp(style,#key) == 0) return new Class(); +#include "style.h" +#undef AngleClass + + else error->all("Invalid angle style"); + return NULL; +} + +/* ---------------------------------------------------------------------- + create a dihedral style, called from input script or restart file +------------------------------------------------------------------------- */ + +void Force::create_dihedral(char *style) +{ + delete [] dihedral_style; + if (dihedral) delete dihedral; + + dihedral = new_dihedral(style); + int n = strlen(style) + 1; + dihedral_style = new char[n]; + strcpy(dihedral_style,style); +} + +/* ---------------------------------------------------------------------- + generate a dihedral class +------------------------------------------------------------------------- */ + +Dihedral *Force::new_dihedral(char *style) +{ + if (strcmp(style,"none") == 0) return NULL; + +#define DihedralClass +#define DihedralStyle(key,Class) \ + else if (strcmp(style,#key) == 0) return new Class(); +#include "style.h" +#undef DihedralClass + + else error->all("Invalid dihedral style"); + return NULL; +} + +/* ---------------------------------------------------------------------- + create an improper style, called from input script or restart file +------------------------------------------------------------------------- */ + +void Force::create_improper(char *style) +{ + delete [] improper_style; + if (improper) delete improper; + + improper = new_improper(style); + int n = strlen(style) + 1; + improper_style = new char[n]; + strcpy(improper_style,style); +} + +/* ---------------------------------------------------------------------- + generate a improper class +------------------------------------------------------------------------- */ + +Improper *Force::new_improper(char *style) +{ + if (strcmp(style,"none") == 0) return NULL; + +#define ImproperClass +#define ImproperStyle(key,Class) \ + else if (strcmp(style,#key) == 0) return new Class(); +#include "style.h" +#undef ImproperClass + + else error->all("Invalid improper style"); + return NULL; +} + +/* ---------------------------------------------------------------------- + new kspace style +------------------------------------------------------------------------- */ + +void Force::create_kspace(int narg, char **arg) +{ + delete [] kspace_style; + if (kspace) delete kspace; + + if (strcmp(arg[0],"none") == 0) kspace = NULL; + +#define KSpaceClass +#define KSpaceStyle(key,Class) \ + else if (strcmp(arg[0],#key) == 0) kspace = new Class(narg-1,&arg[1]); +#include "style.h" +#undef KSpaceClass + + else error->all("Invalid kspace style"); + + int n = strlen(arg[0]) + 1; + kspace_style = new char[n]; + strcpy(kspace_style,arg[0]); +} + +/* ---------------------------------------------------------------------- + set special bond values +------------------------------------------------------------------------- */ + +void Force::set_special(int narg, char **arg) +{ + if (narg == 0) error->all("Illegal special_bonds command"); + + if (narg == 1 && strcmp(arg[0],"charmm") == 0) { + special_lj[1] = 0.0; + special_lj[2] = 0.0; + special_lj[3] = 0.0; + special_coul[1] = 0.0; + special_coul[2] = 0.0; + special_coul[3] = 0.0; + } else if (narg == 1 && strcmp(arg[0],"amber") == 0) { + special_lj[1] = 0.0; + special_lj[2] = 0.0; + special_lj[3] = 0.5; + special_coul[1] = 0.0; + special_coul[2] = 0.0; + special_coul[3] = 5.0/6.0; + } else if (narg == 3) { + special_lj[1] = special_coul[1] = atof(arg[0]); + special_lj[2] = special_coul[2] = atof(arg[1]); + special_lj[3] = special_coul[3] = atof(arg[2]); + } else if (narg == 6) { + special_lj[1] = atof(arg[0]); + special_lj[2] = atof(arg[1]); + special_lj[3] = atof(arg[2]); + special_coul[1] = atof(arg[3]); + special_coul[2] = atof(arg[4]); + special_coul[3] = atof(arg[5]); + } else error->all("Illegal special_bonds command"); +} + +/* ---------------------------------------------------------------------- + create a new temperature + called from input script or fixes that create temperature + if flag, then allow overwrite of existing temperature with same ID +------------------------------------------------------------------------- */ + +void Force::add_temp(int narg, char **arg, int flag) +{ + if (narg < 3) error->all("Illegal temperature command"); + + // error checks + + int itemp; + for (itemp = 0; itemp < ntemp; itemp++) + if (strcmp(arg[0],templist[itemp]->id) == 0) break; + if (flag == 0 && itemp < ntemp) error->all("Reuse of temperature ID"); + + int igroup = group->find(arg[1]); + if (igroup == -1) error->all("Could not find temperature group ID"); + + // if resetting existing temperature, delete it first + + int newflag = 1; + if (itemp < ntemp) { + newflag = 0; + delete templist[itemp]; + } + + // extend Temperature list if necessary + + if (itemp == maxtemp) { + maxtemp += DELTA; + templist = (Temperature **) + memory->srealloc(templist,maxtemp*sizeof(Temperature *), + "modify:templist"); + } + + // create the Temperature class + + if (0) return; // dummy line to enable else-if macro expansion + +#define TempClass +#define TempStyle(key,Class) \ + else if (strcmp(arg[2],#key) == 0) templist[itemp] = new Class(narg,arg); +#include "style.h" +#undef TempClass + + else error->all("Invalid temperature style"); + + if (newflag) ntemp++; +} + +/* ---------------------------------------------------------------------- + return ptr to temperature that matches ID + else return NULL +------------------------------------------------------------------------- */ + +Temperature *Force::find_temp(char *id) +{ + for (int itemp = 0; itemp < ntemp; itemp++) + if (strcmp(id,templist[itemp]->id) == 0) return templist[itemp]; + return NULL; +} + +/* ---------------------------------------------------------------------- + modify temperature parameters +------------------------------------------------------------------------- */ + +void Force::modify_temp(int narg, char **arg) +{ + if (narg < 2) error->all("Illegal temperature_modify command"); + + // lookup Temperature ID + + int itemp; + for (itemp = 0; itemp < ntemp; itemp++) + if (strcmp(arg[0],templist[itemp]->id) == 0) break; + if (itemp == ntemp) error->all("Could not find temp_modify ID"); + + templist[itemp]->modify_params(narg-1,&arg[1]); +} + +/* ---------------------------------------------------------------------- + compute bounds implied by numeric str with a possible wildcard asterik + nmax = upper bound + 5 possibilities: (1) i = i to i, (2) * = 1 to nmax, + (3) i* = i to nmax, (4) *j = 1 to j, (5) i*j = i to j + return nlo,nhi +------------------------------------------------------------------------- */ + +void Force::bounds(char *str, int nmax, int &nlo, int &nhi) +{ + char *ptr = strchr(str,'*'); + + if (ptr == NULL) { + nlo = MAX(atoi(str),1); + nhi = MIN(atoi(str),nmax); + } else if (strlen(str) == 1) { + nlo = 1; + nhi = nmax; + } else if (ptr == str) { + nlo = 1; + nhi = MIN(atoi(ptr+1),nmax); + } else if (strlen(ptr+1) == 0) { + nlo = MAX(atoi(str),1); + nhi = nmax; + } else { + nlo = MAX(atoi(str),1); + nhi = MIN(atoi(ptr+1),nmax); + } +} + +/* ---------------------------------------------------------------------- + memory usage of force classes +------------------------------------------------------------------------- */ + +int Force::memory_usage() +{ + int bytes = 0; + if (pair) bytes += pair->memory_usage(); + if (bond) bytes += bond->memory_usage(); + if (angle) bytes += angle->memory_usage(); + if (dihedral) bytes += dihedral->memory_usage(); + if (improper) bytes += improper->memory_usage(); + if (kspace) bytes += kspace->memory_usage(); + return bytes; +} diff --git a/src/force.h b/src/force.h new file mode 100644 index 0000000000000000000000000000000000000000..0e6127ba266900c2dfe8ef00c7681d2c350a8e6a --- /dev/null +++ b/src/force.h @@ -0,0 +1,100 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FORCE_H +#define FORCE_H + +#include "lammps.h" + +class Pair; +class Bond; +class Angle; +class Dihedral; +class Improper; +class KSpace; +class Temperature; +class Pressure; + +class Force : public LAMMPS { + public: + double boltz; // Boltzmann constant (eng/degree-K) + double mvv2e; // conversion of mv^2 to energy + double ftm2v; // conversion of ft/m to velocity + double nktv2p; // conversion of NkT/V to pressure + double qqr2e; // conversion of q^2/r to energy + double qe2f; // conversion of qE to force + double dielectric; // dielectric constant + double qqrd2e; // q^2/r to energy w/ dielectric constant + + int dimension; // 2 = 2d, 3 = 3d + int newton,newton_pair,newton_bond; // Newton's 3rd law settings + + Pair *pair; + char *pair_style; + + Bond *bond; + char *bond_style; + + Angle *angle; + char *angle_style; + + Dihedral *dihedral; + char *dihedral_style; + + Improper *improper; + char *improper_style; + + KSpace *kspace; + char *kspace_style; + // index [0] is not used in these arrays + double special_lj[4]; // 1-2, 1-3, 1-4 prefactors for LJ + double special_coul[4]; // 1-2, 1-3, 1-4 prefactors for Coulombics + + int ntemp; // # of defined Temperatures + int maxtemp; // max number list can hold + Temperature **templist; // list of defined Temperatures + + Pressure *pressure; // single defined Pressure + + Force(); + ~Force(); + void init(); + + void create_pair(char *); + Pair *new_pair(char *); + Pair *pair_match(char *); + + void create_bond(char *); + Bond *new_bond(char *); + Bond *bond_match(char *); + + void create_angle(char *); + Angle *new_angle(char *); + + void create_dihedral(char *); + Dihedral *new_dihedral(char *); + + void create_improper(char *); + Improper *new_improper(char *); + + void create_kspace(int, char **); + + void set_special(int, char **); + void add_temp(int, char **, int); + Temperature *find_temp(char *); + void modify_temp(int, char **); + void bounds(char *, int, int &, int &); + int memory_usage(); +}; + +#endif diff --git a/src/group.cpp b/src/group.cpp new file mode 100644 index 0000000000000000000000000000000000000000..606d910969d2858c255bad586b789bbd7d5e5f3f --- /dev/null +++ b/src/group.cpp @@ -0,0 +1,806 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "mpi.h" +#include "stdio.h" +#include "string.h" +#include "stdlib.h" +#include "group.h" +#include "domain.h" +#include "region.h" +#include "memory.h" +#include "error.h" +#include "atom.h" + +#define MAX_GROUP 32 + +#define TYPE 1 +#define MOLECULE 2 +#define ID 3 + +#define LESS_THAN 1 +#define GREATER_THAN 2 +#define LESS_EQUAL 3 +#define GREATER_EQUAL 4 +#define BETWEEN 5 + +#define BIG 1.0e20 + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- + initialize group memory +------------------------------------------------------------------------- */ + +Group::Group() +{ + MPI_Comm_rank(world,&me); + + names = (char **) memory->smalloc(MAX_GROUP*sizeof(char *),"group:names"); + bitmask = new int[MAX_GROUP]; + inversemask = new int[MAX_GROUP]; + for (int i = 0; i < MAX_GROUP; i++) bitmask[i] = 1 << i; + for (int i = 0; i < MAX_GROUP; i++) inversemask[i] = bitmask[i] ^ ~0; + + // create "all" group + + char *str = "all"; + int n = strlen(str) + 1; + names[0] = (char *) memory->smalloc(n*sizeof(char),"group:names[]"); + strcpy(names[0],str); + ngroup = 1; +} + +/* ---------------------------------------------------------------------- + free all memory +------------------------------------------------------------------------- */ + +Group::~Group() +{ + for (int i = 0; i < ngroup; i++) memory->sfree(names[i]); + memory->sfree(names); + delete [] bitmask; + delete [] inversemask; +} + +/* ---------------------------------------------------------------------- + assign atoms to a new or existing group +------------------------------------------------------------------------- */ + +void Group::assign(int narg, char **arg) +{ + int i; + + if (domain->box_exist == 0) + error->all("Group command before simulation box is defined"); + if (narg < 2) error->all("Illegal group command"); + + // find group in existing list + // igroup = -1 is a new group name, add it + + int igroup = find(arg[0]); + + if (igroup == -1) { + if (ngroup == MAX_GROUP) error->all("Too many groups"); + igroup = ngroup; + ngroup++; + int n = strlen(arg[0]) + 1; + names[igroup] = (char *) memory->smalloc(n*sizeof(char),"group:names[]"); + strcpy(names[igroup],arg[0]); + } + + double **x = atom->x; + int *mask = atom->mask; + int nlocal = atom->nlocal; + int bit = bitmask[igroup]; + + // style = region + + if (strcmp(arg[1],"region") == 0) { + + if (narg != 3) error->all("Illegal group command"); + + int iregion; + for (iregion = 0; iregion < domain->nregion; iregion++) + if (strcmp(arg[2],domain->regions[iregion]->id) == 0) break; + if (iregion == domain->nregion) + error->all("Group region ID does not exist"); + + // add to group if atom is in region + + for (i = 0; i < nlocal; i++) + if (domain->regions[iregion]->match(x[i][0],x[i][1],x[i][2])) + mask[i] |= bit; + + // style = logical condition + + } else if (strcmp(arg[2],"<") == 0 || strcmp(arg[2],">") == 0 || + strcmp(arg[2],"<=") == 0 || strcmp(arg[2],">=") == 0 || + strcmp(arg[2],"<>") == 0) { + + if (narg < 4 || narg > 5) error->all("Illegal group command"); + int category,condition,bound1,bound2; + + if (strcmp(arg[1],"type") == 0) category = TYPE; + else if (strcmp(arg[1],"molecule") == 0) category = MOLECULE; + else if (strcmp(arg[1],"id") == 0) category = ID; + else error->all("Illegal group command"); + + if (strcmp(arg[2],"<") == 0) condition = LESS_THAN; + else if (strcmp(arg[2],">") == 0) condition = GREATER_THAN; + else if (strcmp(arg[2],"<=") == 0) condition = LESS_EQUAL; + else if (strcmp(arg[2],">=") == 0) condition = GREATER_EQUAL; + else if (strcmp(arg[2],"<>") == 0) condition = BETWEEN; + else error->all("Illegal group command"); + + bound1 = atoi(arg[3]); + bound2 = -1; + + if (condition == BETWEEN) { + if (narg != 5) error->all("Illegal group command"); + bound2 = atoi(arg[4]); + } + + int *attribute; + if (category == TYPE) attribute = atom->type; + else if (category == MOLECULE) attribute = atom->molecule; + else if (category == ID) attribute = atom->tag; + + // add to group if meets condition + + if (condition == LESS_THAN) { + for (i = 0; i < nlocal; i++) if (attribute[i] < bound1) mask[i] |= bit; + } else if (condition == GREATER_THAN) { + for (i = 0; i < nlocal; i++) if (attribute[i] > bound1) mask[i] |= bit; + } else if (condition == LESS_EQUAL) { + for (i = 0; i < nlocal; i++) if (attribute[i] <= bound1) mask[i] |= bit; + } else if (condition == GREATER_EQUAL) { + for (i = 0; i < nlocal; i++) if (attribute[i] >= bound1) mask[i] |= bit; + } else if (condition == BETWEEN) { + for (i = 0; i < nlocal; i++) + if (attribute[i] >= bound1 && attribute[i] <= bound2) mask[i] |= bit; + } + + // style = list of values + + } else if (strcmp(arg[1],"type") == 0 || strcmp(arg[1],"molecule") == 0 || + strcmp(arg[1],"id") == 0) { + + if (narg < 3) error->all("Illegal group command"); + + int length = narg-2; + int *list = new int[length]; + + int category; + if (strcmp(arg[1],"type") == 0) category = TYPE; + else if (strcmp(arg[1],"molecule") == 0) category = MOLECULE; + else if (strcmp(arg[1],"id") == 0) category = ID; + else error->all("Illegal group command"); + + length = narg - 2; + for (int iarg = 2; iarg < narg; iarg++) list[iarg-2] = atoi(arg[iarg]); + + int *attribute; + if (category == TYPE) attribute = atom->type; + else if (category == MOLECULE) attribute = atom->molecule; + else if (category == ID) attribute = atom->tag; + + // add to group if attribute is any in list + + for (int ilist = 0; ilist < length; ilist++) + for (i = 0; i < nlocal; i++) + if (attribute[i] == list[ilist]) mask[i] |= bit; + + delete [] list; + + // style = subtract + + } else if (strcmp(arg[1],"subtract") == 0) { + + if (narg < 4) error->all("Illegal group command"); + + int length = narg-2; + int *list = new int[length]; + + int jgroup; + for (int iarg = 2; iarg < narg; iarg++) { + jgroup = find(arg[iarg]); + if (jgroup == -1) error->all("Group ID does not exist"); + list[iarg-2] = jgroup; + } + + // add to group if in 1st group in list + + int otherbit = bitmask[list[0]]; + + for (i = 0; i < nlocal; i++) + if (mask[i] & otherbit) mask[i] |= bit; + + // remove atoms if they are in any of the other groups + // AND with inverse mask removes the atom from group + + int inverse = inversemask[igroup]; + + for (int ilist = 1; ilist < length; ilist++) { + otherbit = bitmask[list[ilist]]; + for (i = 0; i < nlocal; i++) + if (mask[i] & otherbit) mask[i] &= inverse; + } + + delete [] list; + + // style = union + + } else if (strcmp(arg[1],"union") == 0) { + + if (narg < 3) error->all("Illegal group command"); + + int length = narg-2; + int *list = new int[length]; + + int jgroup; + for (int iarg = 2; iarg < narg; iarg++) { + jgroup = find(arg[iarg]); + if (jgroup == -1) error->all("Group ID does not exist"); + list[iarg-2] = jgroup; + } + + // add to group if in any other group in list + + int otherbit; + + for (int ilist = 0; ilist < length; ilist++) { + otherbit = bitmask[list[ilist]]; + for (i = 0; i < nlocal; i++) + if (mask[i] & otherbit) mask[i] |= bit; + } + + delete [] list; + + // style = intersect + + } else if (strcmp(arg[1],"intersect") == 0) { + + if (narg < 4) error->all("Illegal group command"); + + int length = narg-2; + int *list = new int[length]; + + int jgroup; + for (int iarg = 2; iarg < narg; iarg++) { + jgroup = find(arg[iarg]); + if (jgroup == -1) error->all("Group ID does not exist"); + list[iarg-2] = jgroup; + } + + // add to group if in all groups in list + + int otherbit,ok,ilist; + + for (i = 0; i < nlocal; i++) { + ok = 1; + for (ilist = 0; ilist < length; ilist++) { + otherbit = bitmask[list[ilist]]; + if ((mask[i] & otherbit) == 0) ok = 0; + } + if (ok) mask[i] |= bit; + } + + delete [] list; + + // not a valid group style + + } else error->all("Illegal group command"); + + // print stats for changed group + + int n,all; + n = 0; + for (i = 0; i < nlocal; i++) if (mask[i] & bit) n++; + MPI_Allreduce(&n,&all,1,MPI_INT,MPI_SUM,world); + + if (me == 0) { + if (screen) fprintf(screen,"%d atoms in group %s\n",all,names[igroup]); + if (logfile) fprintf(logfile,"%d atoms in group %s\n",all,names[igroup]); + } +} + +/* ---------------------------------------------------------------------- + add flagged atoms to a new or existing group +------------------------------------------------------------------------- */ + +void Group::create(char *name, int *flag) +{ + int i; + + // find group in existing list + // igroup = -1 is a new group name, add it + + int igroup = find(name); + + if (igroup == -1) { + if (ngroup == MAX_GROUP) error->all("Too many groups"); + igroup = ngroup; + ngroup++; + int n = strlen(name) + 1; + names[igroup] = (char *) memory->smalloc(n*sizeof(char),"group:names[]"); + strcpy(names[igroup],name); + } + + // add atom to group if flag is set + + int *mask = atom->mask; + int nlocal = atom->nlocal; + int bit = bitmask[igroup]; + + for (i = 0; i < nlocal; i++) + if (flag[i]) mask[i] |= bit; +} + +/* ---------------------------------------------------------------------- + return group index if name matches existing group, -1 if no such group +------------------------------------------------------------------------- */ + +int Group::find(char *name) +{ + for (int igroup = 0; igroup < ngroup; igroup++) + if (strcmp(name,names[igroup]) == 0) return igroup; + return -1; +} + +/* ---------------------------------------------------------------------- + write group info to a restart file + only called by proc 0 +------------------------------------------------------------------------- */ + +void Group::write_restart(FILE *fp) +{ + fwrite(&ngroup,sizeof(int),1,fp); + + int n; + for (int i = 0; i < ngroup; i++) { + n = strlen(names[i]) + 1; + fwrite(&n,sizeof(int),1,fp); + fwrite(names[i],sizeof(char),n,fp); + } +} + +/* ---------------------------------------------------------------------- + read group info from a restart file + proc 0 reads, bcast to all procs +------------------------------------------------------------------------- */ + +void Group::read_restart(FILE *fp) +{ + int i,n; + + for (i = 0; i < ngroup; i++) memory->sfree(names[i]); + + if (me == 0) fread(&ngroup,sizeof(int),1,fp); + MPI_Bcast(&ngroup,1,MPI_INT,0,world); + + for (i = 0; i < ngroup; i++) { + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + names[i] = (char *) memory->smalloc(n*sizeof(char),"group:names[]"); + if (me == 0) fread(names[i],sizeof(char),n,fp); + MPI_Bcast(names[i],n,MPI_CHAR,0,world); + } +} + +// ---------------------------------------------------------------------- +// computations on a group of atoms +// ---------------------------------------------------------------------- + +/* ---------------------------------------------------------------------- + count atoms in group + compute in double precision in case system is huge +------------------------------------------------------------------------- */ + +double Group::count(int igroup) +{ + int groupbit = bitmask[igroup]; + + int *mask = atom->mask; + int nlocal = atom->nlocal; + + int n = 0; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) n++; + + double nsingle = n; + double nall; + MPI_Allreduce(&nsingle,&nall,1,MPI_DOUBLE,MPI_SUM,world); + return nall; +} + +/* ---------------------------------------------------------------------- + compute the total mass of group of atoms + use either per-type mass or per-atom rmass +------------------------------------------------------------------------- */ + +double Group::mass(int igroup) +{ + int groupbit = bitmask[igroup]; + + int *mask = atom->mask; + int *type = atom->type; + double *mass = atom->mass; + double *rmass = atom->rmass; + int nlocal = atom->nlocal; + + double m = 0.0; + + if (atom->mass_require) { + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) m += mass[type[i]]; + } else { + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) m += rmass[i]; + } + + double mall; + MPI_Allreduce(&m,&mall,1,MPI_DOUBLE,MPI_SUM,world); + return mall; +} + +/* ---------------------------------------------------------------------- + compute the total charge of group of atoms +------------------------------------------------------------------------- */ + +double Group::charge(int igroup) +{ + int groupbit = bitmask[igroup]; + + int *mask = atom->mask; + double *q = atom->q; + int nlocal = atom->nlocal; + + double qone = 0.0; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) qone += q[i]; + + double qall; + MPI_Allreduce(&qone,&qall,1,MPI_DOUBLE,MPI_SUM,world); + return qall; +} + +/* ---------------------------------------------------------------------- + compute the coordinate bounds of the group of atoms + periodic images are not considered, so atoms are NOT unwrapped +------------------------------------------------------------------------- */ + +void Group::bounds(int igroup, double *minmax) +{ + int groupbit = bitmask[igroup]; + + double extent[6]; + extent[0] = extent[2] = extent[4] = BIG; + extent[1] = extent[3] = extent[5] = -BIG; + + double **x = atom->x; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + extent[0] = MIN(extent[0],x[i][0]); + extent[1] = MAX(extent[1],x[i][0]); + extent[2] = MIN(extent[2],x[i][1]); + extent[3] = MAX(extent[3],x[i][1]); + extent[4] = MIN(extent[4],x[i][2]); + extent[5] = MAX(extent[5],x[i][2]); + } + } + + // compute extent across all procs + // flip sign of MIN to do it in one Allreduce MAX + // set box by extent in shrink-wrapped dims + + extent[0] = -extent[0]; + extent[2] = -extent[2]; + extent[4] = -extent[4]; + + MPI_Allreduce(extent,minmax,6,MPI_DOUBLE,MPI_MAX,world); + + extent[0] = -extent[0]; + extent[2] = -extent[2]; + extent[4] = -extent[4]; +} + +/* ---------------------------------------------------------------------- + compute the center-of-mass coords of group with total mass = masstotal + return center-of-mass coords in cm[] + must unwrap atoms to compute center-of-mass correctly +------------------------------------------------------------------------- */ + +void Group::xcm(int igroup, double masstotal, double *cm) +{ + int groupbit = bitmask[igroup]; + + double **x = atom->x; + int *mask = atom->mask; + int *type = atom->type; + int *image = atom->image; + double *mass = atom->mass; + double *rmass = atom->rmass; + int nlocal = atom->nlocal; + + double cmone[3]; + cmone[0] = cmone[1] = cmone[2] = 0.0; + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + + int xbox,ybox,zbox; + double massone; + + if (atom->mass_require) { + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + massone = mass[type[i]]; + cmone[0] += (x[i][0] + xbox*xprd) * massone; + cmone[1] += (x[i][1] + ybox*yprd) * massone; + cmone[2] += (x[i][2] + zbox*zprd) * massone; + } + } else { + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + massone = rmass[i]; + cmone[0] += (x[i][0] + xbox*xprd) * massone; + cmone[1] += (x[i][1] + ybox*yprd) * massone; + cmone[2] += (x[i][2] + zbox*zprd) * massone; + } + } + + MPI_Allreduce(cmone,cm,3,MPI_DOUBLE,MPI_SUM,world); + cm[0] /= masstotal; + cm[1] /= masstotal; + cm[2] /= masstotal; +} + +/* ---------------------------------------------------------------------- + compute the center-of-mass velocity of group with total mass = masstotal + return center-of-mass velocity in cm[] +------------------------------------------------------------------------- */ + +void Group::vcm(int igroup, double masstotal, double *cm) +{ + int groupbit = bitmask[igroup]; + + double **v = atom->v; + int *mask = atom->mask; + int *type = atom->type; + double *mass = atom->mass; + double *rmass = atom->rmass; + int nlocal = atom->nlocal; + + double p[3],massone; + p[0] = p[1] = p[2] = 0.0; + + if (atom->mass_require) { + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + massone = mass[type[i]]; + p[0] += v[i][0]*massone; + p[1] += v[i][1]*massone; + p[2] += v[i][2]*massone; + } + } else { + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + massone = rmass[i]; + p[0] += v[i][0]*massone; + p[1] += v[i][1]*massone; + p[2] += v[i][2]*massone; + } + } + + MPI_Allreduce(p,cm,3,MPI_DOUBLE,MPI_SUM,world); + cm[0] /= masstotal; + cm[1] /= masstotal; + cm[2] /= masstotal; +} + +/* ---------------------------------------------------------------------- + compute the radius-of-gyration of group around center-of-mass cm + must unwrap atoms to compute Rg correctly +------------------------------------------------------------------------- */ + +double Group::gyration(int igroup, double masstotal, double *cm) +{ + int groupbit = bitmask[igroup]; + + double **x = atom->x; + int *mask = atom->mask; + int *type = atom->type; + int *image = atom->image; + double *mass = atom->mass; + double *rmass = atom->rmass; + int nlocal = atom->nlocal; + int mass_require = atom->mass_require; + + int xbox,ybox,zbox; + double dx,dy,dz,massone; + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + double rg = 0.0; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + dx = (x[i][0] + xbox*xprd) - cm[0]; + dy = (x[i][1] + ybox*yprd) - cm[1]; + dz = (x[i][2] + zbox*zprd) - cm[2]; + if (mass_require) massone = mass[type[i]]; + else massone = rmass[i]; + rg += (dx*dx + dy*dy + dz*dz) * massone; + } + double rg_all; + MPI_Allreduce(&rg,&rg_all,1,MPI_DOUBLE,MPI_SUM,world); + + return sqrt(rg_all/masstotal); +} + +/* ---------------------------------------------------------------------- + compute the angular momentum L (lmom) of group around center-of-mass cm + must unwrap atoms to compute L correctly +------------------------------------------------------------------------- */ + +void Group::angmom(int igroup, double *cm, double *lmom) +{ + int groupbit = bitmask[igroup]; + + double **x = atom->x; + double **v = atom->v; + int *mask = atom->mask; + int *type = atom->type; + int *image = atom->image; + double *mass = atom->mass; + double *rmass = atom->rmass; + int nlocal = atom->nlocal; + int mass_require = atom->mass_require; + + int xbox,ybox,zbox; + double dx,dy,dz,massone; + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + double p[3]; + p[0] = p[1] = p[2] = 0.0; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + dx = (x[i][0] + xbox*xprd) - cm[0]; + dy = (x[i][1] + ybox*yprd) - cm[1]; + dz = (x[i][2] + zbox*zprd) - cm[2]; + if (mass_require) massone = mass[type[i]]; + massone = rmass[i]; + p[0] += massone * (dy*v[i][2] - dz*v[i][1]); + p[1] += massone * (dz*v[i][0] - dx*v[i][2]); + p[2] += massone * (dx*v[i][1] - dy*v[i][0]); + } + + MPI_Allreduce(p,lmom,3,MPI_DOUBLE,MPI_SUM,world); +} + +/* ---------------------------------------------------------------------- + compute moment of inertia tensor around center-of-mass cm + must unwrap atoms to compute itensor correctly +------------------------------------------------------------------------- */ + +void Group::inertia(int igroup, double *cm, double itensor[3][3]) +{ + int i,j; + + int groupbit = bitmask[igroup]; + + double **x = atom->x; + int *mask = atom->mask; + int *type = atom->type; + int *image = atom->image; + double *mass = atom->mass; + double *rmass = atom->rmass; + int nlocal = atom->nlocal; + int mass_require = atom->mass_require; + + int xbox,ybox,zbox; + double dx,dy,dz,massone; + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + double ione[3][3]; + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + ione[i][j] = 0.0; + + for (i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + dx = (x[i][0] + xbox*xprd) - cm[0]; + dy = (x[i][1] + ybox*yprd) - cm[1]; + dz = (x[i][2] + zbox*zprd) - cm[2]; + if (mass_require) massone = mass[type[i]]; + else massone = rmass[i]; + ione[0][0] += massone * (dy*dy + dz*dz); + ione[1][1] += massone * (dx*dx + dz*dz); + ione[2][2] += massone * (dx*dx + dy*dy); + ione[0][1] -= massone * dx*dy; + ione[1][2] -= massone * dy*dz; + ione[0][2] -= massone * dx*dz; + } + ione[1][0] = ione[0][1]; + ione[2][1] = ione[1][2]; + ione[2][0] = ione[0][2]; + + MPI_Allreduce(&ione[0][0],&itensor[0][0],9,MPI_DOUBLE,MPI_SUM,world); +} + +/* ---------------------------------------------------------------------- + compute angular velocity omega from L = Iw, inverting I to solve for w +------------------------------------------------------------------------- */ + +void Group::omega(int igroup, double *angmom, double inertia[3][3], double *w) +{ + int i,j; + + int groupbit = bitmask[igroup]; + + double inverse[3][3]; + + inverse[0][0] = inertia[1][1]*inertia[2][2] - inertia[1][2]*inertia[2][1]; + inverse[0][1] = -(inertia[0][1]*inertia[2][2] - inertia[0][2]*inertia[2][1]); + inverse[0][2] = inertia[0][1]*inertia[1][2] - inertia[0][2]*inertia[1][1]; + + inverse[1][0] = -(inertia[1][0]*inertia[2][2] - inertia[1][2]*inertia[2][0]); + inverse[1][1] = inertia[0][0]*inertia[2][2] - inertia[0][2]*inertia[2][0]; + inverse[1][2] = -(inertia[0][0]*inertia[1][2] - inertia[0][2]*inertia[1][0]); + + inverse[2][0] = inertia[1][0]*inertia[2][1] - inertia[1][1]*inertia[2][0]; + inverse[2][1] = -(inertia[0][0]*inertia[2][1] - inertia[0][1]*inertia[2][0]); + inverse[2][2] = inertia[0][0]*inertia[1][1] - inertia[0][1]*inertia[1][0]; + + double determinant = inertia[0][0]*inertia[1][1]*inertia[2][2] + + inertia[0][1]*inertia[1][2]*inertia[2][0] + + inertia[0][2]*inertia[1][0]*inertia[2][1] - + inertia[0][0]*inertia[1][2]*inertia[2][1] - + inertia[0][1]*inertia[1][0]*inertia[2][2] - + inertia[2][0]*inertia[1][1]*inertia[0][2]; + + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + inverse[i][j] /= determinant; + + w[0] = inverse[0][0]*angmom[0] + inverse[0][1]*angmom[1] + + inverse[0][2]*angmom[2]; + w[1] = inverse[1][0]*angmom[0] + inverse[1][1]*angmom[1] + + inverse[1][2]*angmom[2]; + w[2] = inverse[2][0]*angmom[0] + inverse[2][1]*angmom[1] + + inverse[2][2]*angmom[2]; +} diff --git a/src/group.h b/src/group.h new file mode 100644 index 0000000000000000000000000000000000000000..ed24b62726f766086d3a970f259f745bb3fd85bc --- /dev/null +++ b/src/group.h @@ -0,0 +1,49 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef GROUP_H +#define GROUP_H + +#include "stdio.h" +#include "lammps.h" + +class Group : public LAMMPS { + public: + int me; + int ngroup; // # of defined groups + char **names; // name of each group + int *bitmask; // one-bit mask for each group + int *inversemask; // inverse mask for each group + + Group(); + ~Group(); + void assign(int, char **); // assign atoms to a group + void create(char *, int *); // add flagged atoms to a group + int find(char *); // lookup name in list of groups + void write_restart(FILE *); + void read_restart(FILE *); + + double count(int); // count atoms in group + double mass(int); // total mass of atoms in group + double charge(int); // total charge of atoms in group + void bounds(int, double *); // bounds of atoms in group + void xcm(int, double, double *); // center-of-mass coords of group + void vcm(int, double, double *); // center-of-mass velocity of group + double gyration(int, double, double *); // radius-of-gyration of group + void angmom(int, double *, double *); // angular momentum of group + void inertia(int, double *, double [3][3]); // inertia tensor + void omega(int, double *, double [3][3], double *); // angular velocity +}; + +#endif + diff --git a/src/improper.cpp b/src/improper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bf428a62c643ecdc1c4fb71c9ec6114740e4f389 --- /dev/null +++ b/src/improper.cpp @@ -0,0 +1,36 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "improper.h" +#include "atom.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +Improper::Improper() +{ + allocated = 0; + PI = 4.0*atan(1.0); +} + +/* ---------------------------------------------------------------------- + check if all coeffs are set +------------------------------------------------------------------------- */ + +void Improper::init() +{ + if (!allocated) error->all("Improper coeffs are not set"); + for (int i = 1; i <= atom->nimpropertypes; i++) + if (setflag[i] == 0) error->all("All improper coeffs are not set"); +} diff --git a/src/improper.h b/src/improper.h new file mode 100644 index 0000000000000000000000000000000000000000..118d295eeedd9fab010a1ff219793c98f892a678 --- /dev/null +++ b/src/improper.h @@ -0,0 +1,39 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef IMPROPER_H +#define IMPROPER_H + +#include "stdio.h" +#include "lammps.h" + +class Improper : public LAMMPS { + public: + int allocated; + int *setflag; + double energy; + double virial[6]; + double PI; + + Improper(); + virtual ~Improper() {} + virtual void init(); + virtual void compute(int, int) = 0; + virtual void settings(int, char **) {} + virtual void coeff(int, int, char **) = 0; + virtual void write_restart(FILE *) = 0; + virtual void read_restart(FILE *) = 0; + virtual int memory_usage() {return 0;} +}; + +#endif diff --git a/src/improper_cvff.cpp b/src/improper_cvff.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2b7a0d111f7d2e339a73eadac643152232f37e9f --- /dev/null +++ b/src/improper_cvff.cpp @@ -0,0 +1,352 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "math.h" +#include "stdlib.h" +#include "improper_cvff.h" +#include "atom.h" +#include "comm.h" +#include "neighbor.h" +#include "domain.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "error.h" + +#define TOLERANCE 0.05 +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +ImproperCvff::~ImproperCvff() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(sign); + memory->sfree(multiplicity); + } +} + +/* ---------------------------------------------------------------------- */ + +void ImproperCvff::compute(int eflag, int vflag) +{ + int m,n,i1,i2,i3,i4,type,factor; + double rfactor; + double vb1x,vb1y,vb1z,vb2x,vb2y; + double vb2z,vb2xm,vb2ym,vb2zm,vb3x,vb3y,vb3z,sb1; + double sb2,sb3,rb1,rb3,c0,b1mag2,b1mag,b2mag2; + double b2mag,b3mag2,b3mag,ctmp,r12c1,c1mag,r12c2; + double c2mag,sc1,sc2,s1,s12,c,p,pd,rc2,a,a11,a22; + double a33,a12,a13,a23,sx1,sx2,sx12,sy1,sy2,sy12; + double sz1,sz2,sz12,s2; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **improperlist = neighbor->improperlist; + int nimproperlist = neighbor->nimproperlist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nimproperlist; n++) { + + i1 = improperlist[n][0]; + i2 = improperlist[n][1]; + i3 = improperlist[n][2]; + i4 = improperlist[n][3]; + type = improperlist[n][4]; + + if (newton_bond) factor = 4; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + if (i4 < nlocal) factor++; + } + rfactor = 0.25 * factor; + + // 1st bond + + vb1x = x[i1][0] - x[i2][0]; + vb1y = x[i1][1] - x[i2][1]; + vb1z = x[i1][2] - x[i2][2]; + domain->minimum_image(&vb1x,&vb1y,&vb1z); + + // 2nd bond + + vb2x = x[i3][0] - x[i2][0]; + vb2y = x[i3][1] - x[i2][1]; + vb2z = x[i3][2] - x[i2][2]; + domain->minimum_image(&vb2x,&vb2y,&vb2z); + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + domain->minimum_image(&vb2xm,&vb2ym,&vb2zm); + + // 3rd bond + + vb3x = x[i4][0] - x[i3][0]; + vb3y = x[i4][1] - x[i3][1]; + vb3z = x[i4][2] - x[i3][2]; + domain->minimum_image(&vb3x,&vb3y,&vb3z); + + // c0 calculation + + sb1 = 1.0 / (vb1x*vb1x + vb1y*vb1y + vb1z*vb1z); + sb2 = 1.0 / (vb2x*vb2x + vb2y*vb2y + vb2z*vb2z); + sb3 = 1.0 / (vb3x*vb3x + vb3y*vb3y + vb3z*vb3z); + + rb1 = sqrt(sb1); + rb3 = sqrt(sb3); + + c0 = (vb1x*vb3x + vb1y*vb3y + vb1z*vb3z) * rb1*rb3; + + // 1st and 2nd angle + + b1mag2 = vb1x*vb1x + vb1y*vb1y + vb1z*vb1z; + b1mag = sqrt(b1mag2); + b2mag2 = vb2x*vb2x + vb2y*vb2y + vb2z*vb2z; + b2mag = sqrt(b2mag2); + b3mag2 = vb3x*vb3x + vb3y*vb3y + vb3z*vb3z; + b3mag = sqrt(b3mag2); + + ctmp = vb1x*vb2x + vb1y*vb2y + vb1z*vb2z; + r12c1 = 1.0 / (b1mag*b2mag); + c1mag = ctmp * r12c1; + + ctmp = vb2xm*vb3x + vb2ym*vb3y + vb2zm*vb3z; + r12c2 = 1.0 / (b2mag*b3mag); + c2mag = ctmp * r12c2; + + // cos and sin of 2 angles and final c + + sc1 = sqrt(1.0 - c1mag*c1mag); + if (sc1 < SMALL) sc1 = SMALL; + sc1 = 1.0/sc1; + + sc2 = sqrt(1.0 - c2mag*c2mag); + if (sc2 < SMALL) sc2 = SMALL; + sc2 = 1.0/sc2; + + s1 = sc1 * sc1; + s2 = sc2 * sc2; + s12 = sc1 * sc2; + c = (c0 + c1mag*c2mag) * s12; + + // error check + + if (c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) { + int me; + MPI_Comm_rank(world,&me); + if (screen) { + fprintf(screen,"Improper problem: %d %d %d %d %d %d\n", + me,update->ntimestep, + atom->tag[i1],atom->tag[i2],atom->tag[i3],atom->tag[i4]); + fprintf(screen," 1st atom: %d %g %g %g\n", + me,x[i1][0],x[i1][1],x[i1][2]); + fprintf(screen," 2nd atom: %d %g %g %g\n", + me,x[i2][0],x[i2][1],x[i2][2]); + fprintf(screen," 3rd atom: %d %g %g %g\n", + me,x[i3][0],x[i3][1],x[i3][2]); + fprintf(screen," 4th atom: %d %g %g %g\n", + me,x[i4][0],x[i4][1],x[i4][2]); + } + } + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + // force & energy + // p = 1 + cos(n*phi) for d = 1 + // p = 1 - cos(n*phi) for d = -1 + // pd = dp/dc / 2 + + m = multiplicity[type]; + + if (m == 2) { + p = 2.0*c*c; + pd = 2.0*c; + } else if (m == 3) { + rc2 = c*c; + p = (4.0*rc2-3.0)*c + 1.0; + pd = 6.0*rc2 - 1.5; + } else if (m == 4) { + rc2 = c*c; + p = 8.0*(rc2-1)*rc2 + 2.0; + pd = (16.0*rc2-8.0)*c; + } else if (m == 6) { + rc2 = c*c; + p = ((32.0*rc2-48.0)*rc2 + 18.0)*rc2; + pd = (96.0*(rc2-1.0)*rc2 + 18.0)*c; + } else if (m == 1) { + p = c + 1.0; + pd = 0.5; + } else if (m == 5) { + rc2 = c*c; + p = ((16.0*rc2-20.0)*rc2 + 5.0)*c + 1.0; + pd = (40.0*rc2-30.0)*rc2 + 2.5; + } else if (m == 0) { + p = 2.0; + pd = 0.0; + } + + if (sign[type] == -1) { + p = 2.0 - p; + pd = -pd; + } + + if (eflag) energy += rfactor * k[type] * p; + + a = 2.0 * k[type] * pd; + c = c * a; + s12 = s12 * a; + a11 = (-c*sb1*s1); + a22 = sb2*(2.0*c0*s12 - c*(s1+s2)); + a33 = (-c*sb3*s2); + a12 = r12c1*(c1mag*c*s1 + c2mag*s12); + a13 = rb1*rb3*s12; + a23 = r12c2*(-c2mag*c*s2 - c1mag*s12); + + sx1 = a11*vb1x + a12*vb2x + a13*vb3x; + sx2 = a12*vb1x + a22*vb2x + a23*vb3x; + sx12 = a13*vb1x + a23*vb2x + a33*vb3x; + sy1 = a11*vb1y + a12*vb2y + a13*vb3y; + sy2 = a12*vb1y + a22*vb2y + a23*vb3y; + sy12 = a13*vb1y + a23*vb2y + a33*vb3y; + sz1 = a11*vb1z + a12*vb2z + a13*vb3z; + sz2 = a12*vb1z + a22*vb2z + a23*vb3z; + sz12 = a13*vb1z + a23*vb2z + a33*vb3z; + + // apply force to each of 4 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= sx1; + f[i1][1] -= sy1; + f[i1][2] -= sz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += sx2 + sx1; + f[i2][1] += sy2 + sy1; + f[i2][2] += sz2 + sz1; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] += sx12 - sx2; + f[i3][1] += sy12 - sy2; + f[i3][2] += sz12 - sz2; + } + + if (newton_bond || i4 < nlocal) { + f[i4][0] -= sx12; + f[i4][1] -= sy12; + f[i4][2] -= sz12; + } + + // virial contribution + + if (vflag) { + virial[0] -= rfactor * (vb1x*sx1 + vb2x*sx2 + vb3x*sx12); + virial[1] -= rfactor * (vb1y*sy1 + vb2y*sy2 + vb3y*sy12); + virial[2] -= rfactor * (vb1z*sz1 + vb2z*sz2 + vb3z*sz12); + virial[3] -= rfactor * (vb1x*sy1 + vb2x*sy2 + vb3x*sy12); + virial[4] -= rfactor * (vb1x*sz1 + vb2x*sz2 + vb3x*sz12); + virial[5] -= rfactor * (vb1y*sz1 + vb2y*sz2 + vb3y*sz12); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void ImproperCvff::allocate() +{ + allocated = 1; + int n = atom->nimpropertypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"improper:k"); + sign = (int *) memory->smalloc((n+1)*sizeof(int),"improper:sign"); + multiplicity = (int *) + memory->smalloc((n+1)*sizeof(int),"improper:multiplicity"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"improper:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void ImproperCvff::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this improper style"); + if (narg != 4) error->all("Incorrect args for improper coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nimpropertypes,ilo,ihi); + + double k_one = atof(arg[1]); + int sign_one = atoi(arg[2]); + int multiplicity_one = atoi(arg[3]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + sign[i] = sign_one; + multiplicity[i] = multiplicity_one; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for improper coefficients"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void ImproperCvff::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nimpropertypes,fp); + fwrite(&sign[1],sizeof(int),atom->nimpropertypes,fp); + fwrite(&multiplicity[1],sizeof(int),atom->nimpropertypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void ImproperCvff::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->nimpropertypes,fp); + fread(&sign[1],sizeof(int),atom->nimpropertypes,fp); + fread(&multiplicity[1],sizeof(int),atom->nimpropertypes,fp); + } + MPI_Bcast(&k[1],atom->nimpropertypes,MPI_DOUBLE,0,world); + MPI_Bcast(&sign[1],atom->nimpropertypes,MPI_INT,0,world); + MPI_Bcast(&multiplicity[1],atom->nimpropertypes,MPI_INT,0,world); + + for (int i = 1; i <= atom->nimpropertypes; i++) setflag[i] = 1; +} diff --git a/src/improper_cvff.h b/src/improper_cvff.h new file mode 100644 index 0000000000000000000000000000000000000000..37225ee5011f1e4cd2d3c81b583270f92fa3be8e --- /dev/null +++ b/src/improper_cvff.h @@ -0,0 +1,36 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef IMPROPER_CVFF_H +#define IMPROPER_CVFF_H + +#include "stdio.h" +#include "improper.h" + +class ImproperCvff : public Improper { + public: + ImproperCvff() {} + ~ImproperCvff(); + void compute(int, int); + void coeff(int, int, char **); + void write_restart(FILE *); + void read_restart(FILE *); + + private: + double *k; + int *sign,*multiplicity; + + void allocate(); +}; + +#endif diff --git a/src/improper_harmonic.cpp b/src/improper_harmonic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..58b0c58531bd75d1d5b35aa37aa9b5cb9b03a7c3 --- /dev/null +++ b/src/improper_harmonic.cpp @@ -0,0 +1,286 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "math.h" +#include "stdlib.h" +#include "improper_harmonic.h" +#include "atom.h" +#include "comm.h" +#include "neighbor.h" +#include "domain.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "error.h" + +#define TOLERANCE 0.05 +#define SMALL 0.001 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +ImproperHarmonic::~ImproperHarmonic() +{ + if (allocated) { + memory->sfree(setflag); + memory->sfree(k); + memory->sfree(chi); + } +} + +/* ---------------------------------------------------------------------- */ + +void ImproperHarmonic::compute(int eflag, int vflag) +{ + int n,i1,i2,i3,i4,type,factor; + double rfactor; + double v1x,v1y,v1z,v2x,v2y,v2z,v3x; + double v3y,v3z,ss1,ss2,ss3,r1,r2,r3,c0,c1,c2,s1,s2; + double s12,c,s,domega,a,a11,a22,a33,a12,a13,a23,sx1; + double sx2,sx12,sy1,sy2,sy12,sz1,sz2,sz12; + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + double **x = atom->x; + double **f = atom->f; + int **improperlist = neighbor->improperlist; + int nimproperlist = neighbor->nimproperlist; + int nlocal = atom->nlocal; + int newton_bond = force->newton_bond; + + for (n = 0; n < nimproperlist; n++) { + + i1 = improperlist[n][0]; + i2 = improperlist[n][1]; + i3 = improperlist[n][2]; + i4 = improperlist[n][3]; + type = improperlist[n][4]; + + if (newton_bond) factor = 4; + else { + factor = 0; + if (i1 < nlocal) factor++; + if (i2 < nlocal) factor++; + if (i3 < nlocal) factor++; + if (i4 < nlocal) factor++; + } + rfactor = 0.25 * factor; + + // geometry of 4-body + + v1x = x[i2][0] - x[i1][0]; + v1y = x[i2][1] - x[i1][1]; + v1z = x[i2][2] - x[i1][2]; + domain->minimum_image(&v1x,&v1y,&v1z); + + v2x = x[i3][0] - x[i2][0]; + v2y = x[i3][1] - x[i2][1]; + v2z = x[i3][2] - x[i2][2]; + domain->minimum_image(&v2x,&v2y,&v2z); + + v3x = x[i4][0] - x[i3][0]; + v3y = x[i4][1] - x[i3][1]; + v3z = x[i4][2] - x[i3][2]; + domain->minimum_image(&v3x,&v3y,&v3z); + + ss1 = 1.0 / (v1x*v1x + v1y*v1y + v1z*v1z); + ss2 = 1.0 / (v2x*v2x + v2y*v2y + v2z*v2z); + ss3 = 1.0 / (v3x*v3x + v3y*v3y + v3z*v3z); + + r1 = sqrt(ss1); + r2 = sqrt(ss2); + r3 = sqrt(ss3); + + // sin and cos of angle + + c0 = -(v1x * v3x + v1y * v3y + v1z * v3z) * r1 * r3; + c1 = -(v1x * v2x + v1y * v2y + v1z * v2z) * r1 * r2; + c2 = -(v3x * v2x + v3y * v2y + v3z * v2z) * r3 * r2; + + s1 = 1.0 - c1*c1; + if (s1 < SMALL) s1 = SMALL; + s1 = 1.0 / s1; + + s2 = 1.0 - c2*c2; + if (s2 < SMALL) s2 = SMALL; + s2 = 1.0 / s2; + + s12 = sqrt(s1*s2); + c = (c1*c2 + c0) * s12; + + // error check + + if (c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) { + int me; + MPI_Comm_rank(world,&me); + if (screen) { + fprintf(screen,"Improper problem: %d %d %d %d %d %d\n", + me,update->ntimestep, + atom->tag[i1],atom->tag[i2],atom->tag[i3],atom->tag[i4]); + fprintf(screen," 1st atom: %d %g %g %g\n", + me,x[i1][0],x[i1][1],x[i1][2]); + fprintf(screen," 2nd atom: %d %g %g %g\n", + me,x[i2][0],x[i2][1],x[i2][2]); + fprintf(screen," 3rd atom: %d %g %g %g\n", + me,x[i3][0],x[i3][1],x[i3][2]); + fprintf(screen," 4th atom: %d %g %g %g\n", + me,x[i4][0],x[i4][1],x[i4][2]); + } + } + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + s = sqrt(1.0 - c*c); + if (s < SMALL) s = SMALL; + + // force & energy + + domega = acos(c) - chi[type]; + a = k[type] * domega; + + if (eflag) energy += rfactor * a * domega; + + a = -a * 2.0/s; + c = c * a; + + s12 = s12 * a; + a11 = (-c * ss1 * s1); + a22 = ss2 * (2.0 * c0 * s12 - c * (s1 + s2)); + a33 = (-c * ss3 * s2); + a12 = r1 * r2 * (c1 * c * s1 + c2 * s12); + a13 = r1 * r3 * s12; + a23 = r2 * r3 * (-c2 * c * s2 - c1 * s12); + + sx1 = a12*v2x + a13*v3x - a11*v1x; + sx2 = a22*v2x + a23*v3x - a12*v1x; + sx12 = a23*v2x + a33*v3x - a13*v1x; + sy1 = a12*v2y + a13*v3y - a11*v1y; + sy2 = a22*v2y + a23*v3y - a12*v1y; + sy12 = a23*v2y + a33*v3y - a13*v1y; + sz1 = a12*v2z + a13*v3z - a11*v1z; + sz2 = a22*v2z + a23*v3z - a12*v1z; + sz12 = a23*v2z + a33*v3z - a13*v1z; + + // apply force to each of 4 atoms + + if (newton_bond || i1 < nlocal) { + f[i1][0] -= sx1; + f[i1][1] -= sy1; + f[i1][2] -= sz1; + } + + if (newton_bond || i2 < nlocal) { + f[i2][0] += sx2 + sx1; + f[i2][1] += sy2 + sy1; + f[i2][2] += sz2 + sz1; + } + + if (newton_bond || i3 < nlocal) { + f[i3][0] += sx12 - sx2; + f[i3][1] += sy12 - sy2; + f[i3][2] += sz12 - sz2; + } + + if (newton_bond || i4 < nlocal) { + f[i4][0] -= sx12; + f[i4][1] -= sy12; + f[i4][2] -= sz12; + } + + // virial contribution + + if (vflag) { + virial[0] += rfactor * (v1x*sx1 - v2x*sx2 - v3x*sx12); + virial[1] += rfactor * (v1y*sy1 - v2y*sy2 - v3y*sy12); + virial[2] += rfactor * (v1z*sz1 - v2z*sz2 - v3z*sz12); + virial[3] += rfactor * (v1x*sy1 - v2x*sy2 - v3x*sy12); + virial[4] += rfactor * (v1x*sz1 - v2x*sz2 - v3x*sz12); + virial[5] += rfactor * (v1y*sz1 - v2y*sz2 - v3y*sz12); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void ImproperHarmonic::allocate() +{ + allocated = 1; + int n = atom->nimpropertypes; + + k = (double *) memory->smalloc((n+1)*sizeof(double),"improper:k"); + chi = (double *) memory->smalloc((n+1)*sizeof(double),"improper:chi"); + + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"improper:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +------------------------------------------------------------------------- */ + +void ImproperHarmonic::coeff(int which, int narg, char **arg) +{ + if (which != 0) error->all("Invalid coeffs for this improper style"); + if (narg != 3) error->all("Incorrect args for improper coefficients"); + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nimpropertypes,ilo,ihi); + + double k_one = atof(arg[1]); + double chi_one = atof(arg[2]); + + // convert chi from degrees to radians + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + k[i] = k_one; + chi[i] = chi_one/180.0 * PI; + setflag[i] = 1; + count++; + } + + if (count == 0) error->all("Incorrect args for improper coefficients"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out coeffs to restart file +------------------------------------------------------------------------- */ + +void ImproperHarmonic::write_restart(FILE *fp) +{ + fwrite(&k[1],sizeof(double),atom->nimpropertypes,fp); + fwrite(&chi[1],sizeof(double),atom->nimpropertypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +void ImproperHarmonic::read_restart(FILE *fp) +{ + allocate(); + + if (comm->me == 0) { + fread(&k[1],sizeof(double),atom->nimpropertypes,fp); + fread(&chi[1],sizeof(double),atom->nimpropertypes,fp); + } + MPI_Bcast(&k[1],atom->nimpropertypes,MPI_DOUBLE,0,world); + MPI_Bcast(&chi[1],atom->nimpropertypes,MPI_DOUBLE,0,world); + + for (int i = 1; i <= atom->nimpropertypes; i++) setflag[i] = 1; +} diff --git a/src/improper_harmonic.h b/src/improper_harmonic.h new file mode 100644 index 0000000000000000000000000000000000000000..e21ac2e6b7f1d71c50018e47e4768868e8700bd7 --- /dev/null +++ b/src/improper_harmonic.h @@ -0,0 +1,35 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef IMPROPER_HARMONIC_H +#define IMPROPER_HARMONIC_H + +#include "stdio.h" +#include "improper.h" + +class ImproperHarmonic : public Improper { + public: + ImproperHarmonic() {} + ~ImproperHarmonic(); + void compute(int, int); + void coeff(int, int, char **); + void write_restart(FILE *); + void read_restart(FILE *); + + private: + double *k,*chi; + + void allocate(); +}; + +#endif diff --git a/src/improper_hybrid.cpp b/src/improper_hybrid.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c911f589500d8283eb2198cd3d2362509fab40b1 --- /dev/null +++ b/src/improper_hybrid.cpp @@ -0,0 +1,246 @@ +/* ----------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "string.h" +#include "improper_hybrid.h" +#include "atom.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define EXTRA 1000 + +/* ---------------------------------------------------------------------- + set all global defaults +------------------------------------------------------------------------- */ + +ImproperHybrid::ImproperHybrid() +{ + nstyles = 0; +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +ImproperHybrid::~ImproperHybrid() +{ + if (nstyles) { + for (int i = 0; i < nstyles; i++) delete styles[i]; + delete [] styles; + for (int i = 0; i < nstyles; i++) delete [] keywords[i]; + delete [] keywords; + } + + if (allocated) { + memory->sfree(setflag); + memory->sfree(map); + delete [] nimproperlist; + delete [] maximproper; + for (int i = 0; i < nstyles; i++) + memory->destroy_2d_int_array(improperlist[i]); + delete [] improperlist; + } +} + +/* ---------------------------------------------------------------------- */ + +void ImproperHybrid::compute(int eflag, int vflag) +{ + int i,m,n; + + // save ptrs to original improperlist + + int nimproperlist_orig = neighbor->nimproperlist; + int **improperlist_orig = neighbor->improperlist; + + // if this is re-neighbor step, create sub-style improperlists + // nimproperlist[] = length of each sub-style list + // realloc sub-style improperlist if necessary + // load sub-style improperlist with 5 values from original improperlist + + if (neighbor->ago == 0) { + for (m = 0; m < nstyles; m++) nimproperlist[m] = 0; + for (i = 0; i < nimproperlist_orig; i++) + nimproperlist[map[improperlist_orig[i][4]]]++; + for (m = 0; m < nstyles; m++) { + if (nimproperlist[m] > maximproper[m]) { + memory->destroy_2d_int_array(improperlist[m]); + maximproper[m] = nimproperlist[m] + EXTRA; + improperlist[m] = (int **) + memory->create_2d_int_array(maximproper[m],5, + "improper_hybrid:improperlist"); + } + nimproperlist[m] = 0; + } + for (i = 0; i < nimproperlist_orig; i++) { + m = map[improperlist_orig[i][4]]; + n = nimproperlist[m]; + improperlist[m][n][0] = improperlist_orig[i][0]; + improperlist[m][n][1] = improperlist_orig[i][1]; + improperlist[m][n][2] = improperlist_orig[i][2]; + improperlist[m][n][3] = improperlist_orig[i][3]; + improperlist[m][n][4] = improperlist_orig[i][4]; + nimproperlist[m]++; + } + } + + // call each sub-style's compute function + // must set neighbor->improperlist to sub-style improperlist before call + // accumulate sub-style energy,virial in hybrid's energy,virial + + energy = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + for (m = 0; m < nstyles; m++) { + if (styles[m] == NULL) continue; + neighbor->nimproperlist = nimproperlist[m]; + neighbor->improperlist = improperlist[m]; + styles[m]->compute(eflag,vflag); + if (eflag) energy += styles[m]->energy; + if (vflag) for (n = 0; n < 6; n++) virial[n] += styles[m]->virial[n]; + } + + // restore ptrs to original improperlist + + neighbor->nimproperlist = nimproperlist_orig; + neighbor->improperlist = improperlist_orig; +} + +/* ---------------------------------------------------------------------- */ + +void ImproperHybrid::allocate() +{ + allocated = 1; + int n = atom->nimpropertypes; + + map = (int *) memory->smalloc((n+1)*sizeof(int),"improper:map"); + setflag = (int *) memory->smalloc((n+1)*sizeof(int),"improper:setflag"); + for (int i = 1; i <= n; i++) setflag[i] = 0; + + nimproperlist = new int[nstyles]; + maximproper = new int[nstyles]; + improperlist = new int**[nstyles]; + for (int m = 0; m < nstyles; m++) maximproper[m] = 0; + for (int m = 0; m < nstyles; m++) improperlist[m] = NULL; +} + +/* ---------------------------------------------------------------------- + create one improper style for each arg in list +------------------------------------------------------------------------- */ + +void ImproperHybrid::settings(int narg, char **arg) +{ + nstyles = narg; + styles = new Improper*[nstyles]; + keywords = new char*[nstyles]; + + for (int m = 0; m < nstyles; m++) { + for (int i = 0; i < m; i++) + if (strcmp(arg[m],arg[i]) == 0) + error->all("Improper style hybrid cannot use same improper style twice"); + if (strcmp(arg[m],"hybrid") == 0) + error->all("Improper style hybrid cannot have hybrid as an argument"); + styles[m] = force->new_improper(arg[m]); + keywords[m] = new char[strlen(arg[m])+1]; + strcpy(keywords[m],arg[m]); + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one type +---------------------------------------------------------------------- */ + +void ImproperHybrid::coeff(int which, int narg, char **arg) +{ + if (!allocated) allocate(); + + int ilo,ihi; + force->bounds(arg[0],atom->nimpropertypes,ilo,ihi); + + // 2nd arg = improper style name (harmonic, etc) + + int m; + for (m = 0; m < nstyles; m++) + if (strcmp(arg[1],keywords[m]) == 0) break; + if (m == nstyles) error->all("Improper coeff for hybrid has invalid style"); + + // set low-level coefficients for each impropertype + // replace 2nd arg with i, call coeff() with no 1st arg + // if sub-style is NULL for "none", still set setflag + + for (int i = ilo; i <= ihi; i++) { + sprintf(arg[1],"%d",i); + map[i] = m; + if (styles[m]) styles[m]->coeff(which,narg-1,&arg[1]); + setflag[i] = 1; + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void ImproperHybrid::write_restart(FILE *fp) +{ + fwrite(&nstyles,sizeof(int),1,fp); + + int n; + for (int m = 0; m < nstyles; m++) { + n = strlen(keywords[m]) + 1; + fwrite(&n,sizeof(int),1,fp); + fwrite(keywords[m],sizeof(char),n,fp); + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void ImproperHybrid::read_restart(FILE *fp) +{ + allocate(); + + int me = comm->me; + if (me == 0) fread(&nstyles,sizeof(int),1,fp); + MPI_Bcast(&nstyles,1,MPI_INT,0,world); + styles = new Improper*[nstyles]; + keywords = new char*[nstyles]; + + int n; + for (int m = 0; m < nstyles; m++) { + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + keywords[m] = new char[n]; + if (me == 0) fread(keywords[m],sizeof(char),n,fp); + MPI_Bcast(keywords[m],n,MPI_CHAR,0,world); + styles[m] = force->new_improper(keywords[m]); + } +} + +/* ---------------------------------------------------------------------- + memory usage +------------------------------------------------------------------------- */ + +int ImproperHybrid::memory_usage() +{ + int bytes = 0; + for (int m = 0; m < nstyles; m++) bytes += maximproper[m]*5 * sizeof(int); + for (int m = 0; m < nstyles; m++) + if (styles[m]) bytes += styles[m]->memory_usage(); + return bytes; +} diff --git a/src/improper_hybrid.h b/src/improper_hybrid.h new file mode 100644 index 0000000000000000000000000000000000000000..1aca2401e59001f49695c51847985e3f6bffae71 --- /dev/null +++ b/src/improper_hybrid.h @@ -0,0 +1,44 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef IMPROPER_HYBRID_H +#define IMPROPER_HYBRID_H + +#include "stdio.h" +#include "improper.h" + +class ImproperHybrid : public Improper { + public: + ImproperHybrid(); + ~ImproperHybrid(); + void compute(int, int); + void settings(int, char **); + void coeff(int, int, char **); + void write_restart(FILE *); + void read_restart(FILE *); + int memory_usage(); + + private: + int nstyles; // # of different improper styles + Improper **styles; // class list for each Improper style + char **keywords; // keyword for each improper style + int *map; // which style each improper type points to + + int *nimproperlist; // # of impropers in sub-style improperlists + int *maximproper; // max # of impropers sub-style lists can store + int ***improperlist; // improperlist for each sub-style + + void allocate(); +}; + +#endif diff --git a/src/input.cpp b/src/input.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b0d1494fdb7d040b15d759e6df3c103673f7da84 --- /dev/null +++ b/src/input.cpp @@ -0,0 +1,1124 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "input.h" +#include "system.h" +#include "atom.h" +#include "comm.h" +#include "group.h" +#include "domain.h" +#include "output.h" +#include "thermo.h" +#include "force.h" +#include "pair.h" +#include "min.h" +#include "modify.h" +#include "bond.h" +#include "angle.h" +#include "dihedral.h" +#include "improper.h" +#include "kspace.h" +#include "update.h" +#include "neighbor.h" +#include "special.h" +#include "variable.h" +#include "error.h" +#include "memory.h" + +#define AtomInclude +#define CommandInclude +#include "style.h" +#undef AtomInclude +#undef CommandInclude + +#define MAXLINE 1024 +#define DELTA 4 + +/* ---------------------------------------------------------------------- */ + +Input::Input(int argc, char **argv) +{ + MPI_Comm_rank(world,&me); + + line = new char[MAXLINE]; + copy = new char[MAXLINE]; + work = new char[MAXLINE]; + narg = maxarg = 0; + arg = NULL; + + echo_screen = 0; + echo_log = 1; + + label_active = 0; + labelstr = NULL; + jump_skip = 0; + + if (me == 0) { + nfile = maxfile = 1; + infiles = (FILE **) memory->smalloc(sizeof(FILE *),"input:infiles"); + infiles[0] = infile; + } else infiles = NULL; + + variable = new Variable; + + // process command-line args + // check for args "-var" and "-echo" + // caller has already checked that sufficient arguments exist + + int iarg = 0; + while (iarg < argc) { + if (strcmp(argv[iarg],"-var") == 0) { + variable->set(argv[iarg+1],argv[iarg+2]); + iarg += 2; + } else if (strcmp(argv[iarg],"-echo") == 0) { + narg = 1; + char **tmp = arg; // trick echo() into using argv instead of arg + arg = &argv[iarg+1]; + echo(); + arg = tmp; + iarg += 3; + } else iarg++; + } +} + +/* ---------------------------------------------------------------------- */ + +Input::~Input() +{ + // don't free command and arg strings + // they just point to other allocated memory + + delete variable; + delete [] line; + delete [] copy; + delete [] work; + if (labelstr) delete [] labelstr; + if (arg) memory->sfree(arg); + if (infiles) memory->sfree(infiles); +} + +/* ---------------------------------------------------------------------- + process all input from infile + infile = stdin or file if command-line arg "-in" was used +------------------------------------------------------------------------- */ + +void Input::file() +{ + int n,flag; + + while (1) { + + // read one line from input script + // if line ends in continuation char '&', concatenate next line(s) + // n = str length of line + + if (me == 0) { + if (fgets(line,MAXLINE,infile) == NULL) n = 0; + else n = strlen(line) + 1; + while (n >= 3 && line[n-3] == '&') { + if (fgets(&line[n-3],MAXLINE-n+3,infile) == NULL) n = 0; + else n = strlen(line) + 1; + } + } + + // bcast the line + // if n = 0, end-of-file + // error if label_active is set, since label wasn't encountered + // if original input file, code is done + // else go back to previous input file + + MPI_Bcast(&n,1,MPI_INT,0,world); + if (n == 0) { + if (label_active) error->all("Label wasn't found in input script"); + if (me == 0) { + if (infile != stdin) fclose(infile); + nfile--; + } + MPI_Bcast(&nfile,1,MPI_INT,0,world); + if (nfile == 0) break; + if (me == 0) infile = infiles[nfile-1]; + continue; + } + + MPI_Bcast(line,n,MPI_CHAR,0,world); + + // echo the command unless scanning for label + + if (me == 0 && label_active == 0) { + if (echo_screen && screen) fprintf(screen,"%s",line); + if (echo_log && logfile) fprintf(logfile,"%s",line); + } + + // parse the line + // if no command, skip to next line in input script + + parse(); + if (command == NULL) continue; + + // if scanning for label, skip command unless it's a label command + + if (label_active && strcmp(command,"label") != 0) continue; + + // execute the command + + if (execute_command()) { + char str[MAXLINE]; + sprintf(str,"Unknown command: %s",line); + error->all(str); + } + } +} + +/* ---------------------------------------------------------------------- + process all input from filename +------------------------------------------------------------------------- */ + +void Input::file(char *filename) +{ + // error if another nested file still open + // if single open file is not stdin, close it + // open new filename and set infile, infiles[0] + + if (me == 0) { + if (nfile > 1) + error->one("Another input script is already being processed"); + if (infile != stdin) fclose(infile); + infile = fopen(filename,"r"); + if (infile == NULL) { + char str[128]; + sprintf(str,"Cannot open input script %s",filename); + error->one(str); + } + infiles[0] = infile; + } else infile = NULL; + + file(); +} + +/* ---------------------------------------------------------------------- + parse the command in single and execute it + return command name to caller +------------------------------------------------------------------------- */ + +char *Input::one(char *single) +{ + strcpy(line,single); + + // echo the command unless scanning for label + + if (me == 0 && label_active == 0) { + if (echo_screen && screen) fprintf(screen,"%s",line); + if (echo_log && logfile) fprintf(logfile,"%s",line); + } + + // parse the line + // if no command, just return NULL + + parse(); + if (command == NULL) return NULL; + + // if scanning for label, skip command unless it's a label command + + if (label_active && strcmp(command,"label") != 0) return NULL; + + // execute the command and return its name + + if (execute_command()) { + char str[MAXLINE]; + sprintf(str,"Unknown command: %s",line); + error->all(str); + } + + return command; +} + +/* ---------------------------------------------------------------------- + parse copy of command line + strip comment = all chars from # on + replace all $ via variable substitution + command = first word + narg = # of args + arg[] = individual args + treat multiple args between double quotes as one arg +------------------------------------------------------------------------- */ + +void Input::parse() +{ + // make a copy to work on + + strcpy(copy,line); + + // strip any # comment by resetting string terminator + // do not strip # inside double quotes + + int level = 0; + char *ptr = copy; + while (*ptr) { + if (*ptr == '#' && level == 0) { + *ptr = '\0'; + break; + } + if (*ptr == '"') { + if (level == 0) level = 1; + else level = 0; + } + ptr++; + } + + // perform $ variable substitution (print changes) + + substitute(copy,1); + + // command = 1st arg + + command = strtok(copy," \t\n\r\f"); + if (command == NULL) return; + + // point arg[] at each subsequent arg + // treat text between double quotes as one arg + // insert string terminators in copy to delimit args + + narg = 0; + while (1) { + if (narg == maxarg) { + maxarg += DELTA; + arg = (char **) memory->srealloc(arg,maxarg*sizeof(char *),"input:arg"); + } + arg[narg] = strtok(NULL," \t\n\r\f"); + if (arg[narg] && arg[narg][0] == '\"') { + arg[narg] = &arg[narg][1]; + if (strchr(arg[narg],'\"')) error->all("Quotes in a single arg"); + arg[narg][strlen(arg[narg])] = ' '; + ptr = strtok(NULL,"\""); + if (ptr == NULL) error->all("Unbalanced quotes in input line"); + } + if (arg[narg]) narg++; + else break; + } +} + +/* ---------------------------------------------------------------------- + substitute for $ variables in str + print updated string if flag is set and not searching for label +------------------------------------------------------------------------- */ + +void Input::substitute(char *str, int flag) +{ + // use work[] as scratch space to expand str + // do not replace $ inside double quotes as flagged by level + // var = pts at variable name, ended by NULL + // if $ is followed by '{', trailing '}' becomes NULL + // else $x becomes x followed by NULL + // beyond = pts at text following variable + + char *var,*value,*beyond; + int level = 0; + char *ptr = str; + + while (*ptr) { + if (*ptr == '$' && level == 0) { + if (*(ptr+1) == '{') { + var = ptr+2; + int i = 0; + while (var[i] != '\0' && var[i] != '}') i++; + if (var[i] == '\0') error->one("Invalid variable name"); + var[i] = '\0'; + beyond = ptr + strlen(var) + 3; + } else { + var = ptr; + var[0] = var[1]; + var[1] = '\0'; + beyond = ptr + strlen(var) + 1; + } + value = variable->retrieve(var); + if (value == NULL) error->one("Substitution for undefined variable"); + + *ptr = '\0'; + strcpy(work,str); + strcat(work,value); + strcat(work,beyond); + strcpy(str,work); + ptr += strlen(value); + if (flag && me == 0 && label_active == 0) { + if (echo_screen && screen) fprintf(screen,"%s",str); + if (echo_log && logfile) fprintf(logfile,"%s",str); + } + continue; + } + if (*ptr == '"') { + if (level == 0) level = 1; + else level = 0; + } + ptr++; + } +} + +/* ---------------------------------------------------------------------- + process a single parsed command + return 0 if successful, -1 if did not recognize command +------------------------------------------------------------------------- */ + +int Input::execute_command() +{ + int flag = 1; + + if (!strcmp(command,"angle_coeff")) angle_coeff(); + else if (!strcmp(command,"angle_style")) angle_style(); + else if (!strcmp(command,"atom_modify")) atom_modify(); + else if (!strcmp(command,"atom_style")) atom_style(); + else if (!strcmp(command,"bond_coeff")) bond_coeff(); + else if (!strcmp(command,"bond_style")) bond_style(); + else if (!strcmp(command,"boundary")) boundary(); + else if (!strcmp(command,"clear")) clear(); + else if (!strcmp(command,"dielectric")) dielectric(); + else if (!strcmp(command,"dihedral_coeff")) dihedral_coeff(); + else if (!strcmp(command,"dihedral_style")) dihedral_style(); + else if (!strcmp(command,"dimension")) dimension(); + else if (!strcmp(command,"dipole")) dipole(); + else if (!strcmp(command,"dump")) dump(); + else if (!strcmp(command,"dump_modify")) dump_modify(); + else if (!strcmp(command,"echo")) echo(); + else if (!strcmp(command,"fix")) fix(); + else if (!strcmp(command,"fix_modify")) fix_modify(); + else if (!strcmp(command,"group")) group_command(); + else if (!strcmp(command,"improper_coeff")) improper_coeff(); + else if (!strcmp(command,"improper_style")) improper_style(); + else if (!strcmp(command,"include")) include(); + else if (!strcmp(command,"jump")) jump(); + else if (!strcmp(command,"kspace_modify")) kspace_modify(); + else if (!strcmp(command,"kspace_style")) kspace_style(); + else if (!strcmp(command,"label")) label(); + else if (!strcmp(command,"lattice")) lattice(); + else if (!strcmp(command,"log")) log(); + else if (!strcmp(command,"mass")) mass(); + else if (!strcmp(command,"min_modify")) min_modify(); + else if (!strcmp(command,"min_style")) min_style(); + else if (!strcmp(command,"neigh_modify")) neigh_modify(); + else if (!strcmp(command,"neighbor")) neighbor_command(); + else if (!strcmp(command,"newton")) newton(); + else if (!strcmp(command,"next")) next_command(); + else if (!strcmp(command,"orient")) orient(); + else if (!strcmp(command,"origin")) origin(); + else if (!strcmp(command,"pair_coeff")) pair_coeff(); + else if (!strcmp(command,"pair_modify")) pair_modify(); + else if (!strcmp(command,"pair_style")) pair_style(); + else if (!strcmp(command,"pair_write")) pair_write(); + else if (!strcmp(command,"print")) print(); + else if (!strcmp(command,"processors")) processors(); + else if (!strcmp(command,"region")) region(); + else if (!strcmp(command,"reset_timestep")) reset_timestep(); + else if (!strcmp(command,"restart")) restart(); + else if (!strcmp(command,"run_style")) run_style(); + else if (!strcmp(command,"special_bonds")) special_bonds(); + else if (!strcmp(command,"temp_modify")) temp_modify(); + else if (!strcmp(command,"temperature")) temperature(); + else if (!strcmp(command,"thermo")) thermo(); + else if (!strcmp(command,"thermo_modify")) thermo_modify(); + else if (!strcmp(command,"thermo_style")) thermo_style(); + else if (!strcmp(command,"timestep")) timestep(); + else if (!strcmp(command,"undump")) undump(); + else if (!strcmp(command,"unfix")) unfix(); + else if (!strcmp(command,"units")) units(); + else if (!strcmp(command,"variable")) variable_command(); + else flag = 0; + + if (flag) return 0; + + if (0) return 0; // dummy line to enable else-if macro expansion + +#define CommandClass +#define CommandStyle(key,Class) \ + else if (strcmp(command,#key) == 0) { \ + Class key; \ + key.command(narg,arg); \ + return 0; \ + } +#include "style.h" +#undef CommandClass + + return -1; +} + +/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + one function for each input command executed here +------------------------------------------------------------------------- */ + +void Input::angle_coeff() +{ + if (domain->box_exist == 0) + error->all("Angle_coeff command before simulation box is defined"); + if (force->angle == NULL) + error->all("Angle_coeff command before angle_style is defined"); + if (atom->angles_allow == 0) + error->all("Angle_coeff command when no angles allowed"); + force->angle->coeff(0,narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::angle_style() +{ + if (narg < 1) error->all("Illegal angle_style command"); + force->create_angle(arg[0]); + if (force->angle) force->angle->settings(narg-1,&arg[1]); +} + +/* ---------------------------------------------------------------------- */ + +void Input::atom_modify() +{ + if (domain->box_exist) + error->all("Atom_modify command after simulation box is defined"); + if (!atom) error->all("Atom_modify command before atom_style command"); + atom->modify_params(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::atom_style() +{ + if (domain->box_exist) + error->all("Atom_style command after simulation box is defined"); + if (narg < 1) error->all("Illegal atom_style command"); + + Atom *old = atom; + + if (strcmp(arg[0],"none") == 0) error->all("Invalid atom style"); + +#define AtomClass +#define AtomStyle(key,Class) \ + else if (strcmp(arg[0],#key) == 0) atom = new Class(narg,arg); +#include "style.h" +#undef AtomClass + + else error->all("Invalid atom style"); + + if (old) { + atom->settings(old); + delete old; + } +} + +/* ---------------------------------------------------------------------- */ + +void Input::bond_coeff() +{ + if (domain->box_exist == 0) + error->all("Bond_coeff command before simulation box is defined"); + if (force->bond == NULL) + error->all("Bond_coeff command before bond_style is defined"); + if (atom->bonds_allow == 0) + error->all("Bond_coeff command when no bonds allowed"); + force->bond->coeff(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::bond_style() +{ + if (narg < 1) error->all("Illegal bond_style command"); + force->create_bond(arg[0]); + if (force->bond) force->bond->settings(narg-1,&arg[1]); +} + +/* ---------------------------------------------------------------------- */ + +void Input::boundary() +{ + if (domain->box_exist) + error->all("Boundary command after simulation box is defined"); + domain->set_boundary(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::clear() +{ + if (narg > 0) error->all("Illegal clear command"); + sys->destroy(); + sys->create(); +} + +/* ---------------------------------------------------------------------- */ + +void Input::dielectric() +{ + if (narg != 1) error->all("Illegal dielectric command"); + force->dielectric = atof(arg[0]); +} + +/* ---------------------------------------------------------------------- */ + +void Input::dihedral_coeff() +{ + if (domain->box_exist == 0) + error->all("Dihedral_coeff command before simulation box is defined"); + if (force->dihedral == NULL) + error->all("Dihedral_coeff command before dihedral_style is defined"); + if (atom->dihedrals_allow == 0) + error->all("Dihedral_coeff command when no dihedrals allowed"); + force->dihedral->coeff(0,narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::dihedral_style() +{ + if (narg < 1) error->all("Illegal dihedral_style command"); + force->create_dihedral(arg[0]); + if (force->dihedral) force->dihedral->settings(narg-1,&arg[1]); +} + +/* ---------------------------------------------------------------------- */ + +void Input::dimension() +{ + if (domain->box_exist) + error->all("Dimension command after simulation box is defined"); + if (narg != 1) error->all("Illegal dimension command"); + force->dimension = atoi(arg[0]); +} + +/* ---------------------------------------------------------------------- */ + +void Input::dipole() +{ + if (domain->box_exist == 0) + error->all("Dipole command before simulation box is defined"); + if (narg != 2) error->all("Illegal dipole command"); + atom->set_dipole(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::dump() +{ + output->add_dump(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::dump_modify() +{ + output->modify_dump(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::echo() +{ + if (narg != 1) error->all("Illegal echo command"); + + if (strcmp(arg[0],"none") == 0) { + echo_screen = 0; + echo_log = 0; + } else if (strcmp(arg[0],"screen") == 0) { + echo_screen = 1; + echo_log = 0; + } else if (strcmp(arg[0],"log") == 0) { + echo_screen = 0; + echo_log = 1; + } else if (strcmp(arg[0],"both") == 0) { + echo_screen = 1; + echo_log = 1; + } else error->all("Illegal echo command"); +} + +/* ---------------------------------------------------------------------- */ + +void Input::fix() +{ + modify->add_fix(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::fix_modify() +{ + modify->modify_fix(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::group_command() +{ + group->assign(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::improper_coeff() +{ + if (domain->box_exist == 0) + error->all("Improper_coeff command before simulation box is defined"); + if (force->improper == NULL) + error->all("Improper_coeff command before improper_style is defined"); + if (atom->impropers_allow == 0) + error->all("Improper_coeff command when no impropers allowed"); + force->improper->coeff(0,narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::improper_style() +{ + if (narg < 1) error->all("Illegal improper_style command"); + force->create_improper(arg[0]); + if (force->improper) force->improper->settings(narg-1,&arg[1]); +} + +/* ---------------------------------------------------------------------- */ + +void Input::include() +{ + if (narg != 1) error->all("Illegal include command"); + + if (me == 0) { + if (nfile == maxfile) { + maxfile++; + infiles = (FILE **) + memory->srealloc(infiles,maxfile*sizeof(FILE *),"input:infiles"); + } + infile = fopen(arg[0],"r"); + if (infile == NULL) { + char str[128]; + sprintf(str,"Cannot open input script %s",arg[0]); + error->one(str); + } + infiles[nfile++] = infile; + } +} + +/* ---------------------------------------------------------------------- */ + +void Input::jump() +{ + if (narg < 1 || narg > 2) error->all("Illegal jump command"); + + if (jump_skip) { + jump_skip = 0; + return; + } + + if (me == 0) { + if (infile != stdin) fclose(infile); + infile = fopen(arg[0],"r"); + if (infile == NULL) { + char str[128]; + sprintf(str,"Cannot open input script %s",arg[0]); + error->one(str); + } + infiles[nfile-1] = infile; + } + + if (narg == 2) { + label_active = 1; + if (labelstr) delete [] labelstr; + int n = strlen(arg[1]) + 1; + labelstr = new char[n]; + strcpy(labelstr,arg[1]); + } +} + +/* ---------------------------------------------------------------------- */ + +void Input::kspace_modify() +{ + if (force->kspace == NULL) error->all("KSpace style has not yet been set"); + force->kspace->modify_params(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::kspace_style() +{ + force->create_kspace(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::label() +{ + if (narg != 1) error->all("Illegal label command"); + if (label_active && strcmp(labelstr,arg[0]) == 0) label_active = 0; +} + +/* ---------------------------------------------------------------------- */ + +void Input::lattice() +{ + domain->set_lattice(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::log() +{ + if (narg != 1) error->all("Illegal log command"); + + if (me == 0) { + if (logfile) fclose(logfile); + if (strcmp(arg[0],"none") == 0) logfile = NULL; + else { + logfile = fopen(arg[0],"w"); + if (logfile == NULL) { + char str[128]; + sprintf(str,"Cannot open logfile %s",arg[0]); + error->one(str); + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void Input::mass() +{ + if (domain->box_exist == 0) + error->all("Mass command before simulation box is defined"); + if (narg != 2) error->all("Illegal mass command"); + atom->set_mass(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::min_modify() +{ + update->minimize->modify_params(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::min_style() +{ + if (domain->box_exist == 0) + error->all("Min_style command before simulation box is defined"); + update->create_minimize(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::neigh_modify() +{ + neighbor->modify_params(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::neighbor_command() +{ + if (narg != 2) error->all("Illegal neighbor command"); + + neighbor->skin = atof(arg[0]); + if (strcmp(arg[1],"nsq") == 0) neighbor->style = 0; + else if (strcmp(arg[1],"bin") == 0) neighbor->style = 1; + else error->all("Illegal neighbor command"); +} + +/* ---------------------------------------------------------------------- */ + +void Input::newton() +{ + int newton_pair,newton_bond; + + if (narg == 1) { + if (strcmp(arg[0],"off") == 0) newton_pair = newton_bond = 0; + else if (strcmp(arg[0],"on") == 0) newton_pair = newton_bond = 1; + else error->all("Illegal newton command"); + } else if (narg == 2) { + if (strcmp(arg[0],"off") == 0) newton_pair = 0; + else if (strcmp(arg[0],"on") == 0) newton_pair= 1; + else error->all("Illegal newton command"); + if (strcmp(arg[1],"off") == 0) newton_bond = 0; + else if (strcmp(arg[1],"on") == 0) newton_bond = 1; + else error->all("Illegal newton command"); + } else error->all("Illegal newton command"); + + force->newton_pair = newton_pair; + + if (newton_bond == 0) { + if (domain->box_exist && force->newton_bond == 1) + error->all("Newton bond change after simulation box is defined"); + force->newton_bond = 0; + } else { + if (domain->box_exist && force->newton_bond == 0) + error->all("Newton bond change after simulation box is defined"); + force->newton_bond = 1; + } + + if (newton_pair || newton_bond) force->newton = 1; + else force->newton = 0; +} + +/* ---------------------------------------------------------------------- */ + +void Input::next_command() +{ + if (variable->next(narg,arg)) jump_skip = 1; +} + +/* ---------------------------------------------------------------------- */ + +void Input::orient() +{ + if (narg != 4) error->all("Illegal orient command"); + + if (strcmp(arg[0],"x") == 0) { + domain->orient_x[0] = atoi(arg[1]); + domain->orient_x[1] = atoi(arg[2]); + domain->orient_x[2] = atoi(arg[3]); + } else if (strcmp(arg[0],"y") == 0) { + domain->orient_y[0] = atoi(arg[1]); + domain->orient_y[1] = atoi(arg[2]); + domain->orient_y[2] = atoi(arg[3]); + } else if (strcmp(arg[0],"z") == 0) { + domain->orient_z[0] = atoi(arg[1]); + domain->orient_z[1] = atoi(arg[2]); + domain->orient_z[2] = atoi(arg[3]); + } else error->all("Illegal orient command"); +} + +/* ---------------------------------------------------------------------- */ + +void Input::origin() +{ + if (narg != 3) error->all("Illegal origin command"); + + domain->origin_x = atof(arg[0]); + domain->origin_y = atof(arg[1]); + domain->origin_z = atof(arg[2]); + + if (domain->origin_x < 0.0 || domain->origin_x > 1.0 || + domain->origin_y < 0.0 || domain->origin_y > 1.0 || + domain->origin_x < 0.0 || domain->origin_z > 1.0) + error->all("Illegal origin command"); +} + +/* ---------------------------------------------------------------------- */ + +void Input::pair_coeff() +{ + if (domain->box_exist == 0) + error->all("Pair_coeff command before simulation box is defined"); + if (force->pair == NULL) + error->all("Pair_coeff command before pair_style is defined"); + force->pair->coeff(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::pair_modify() +{ + if (force->pair == NULL) + error->all("Pair_modify command before pair_style is defined"); + force->pair->modify_params(narg,arg); +} + +/* ---------------------------------------------------------------------- + if old pair style exists and new style is same, just change settings + else create new pair class +------------------------------------------------------------------------- */ + +void Input::pair_style() +{ + if (narg < 1) error->all("Illegal pair_style command"); + if (force->pair && strcmp(arg[0],force->pair_style) == 0) { + force->pair->settings(narg-1,&arg[1]); + return; + } + force->create_pair(arg[0]); + if (force->pair) force->pair->settings(narg-1,&arg[1]); +} + +/* ---------------------------------------------------------------------- */ + +void Input::pair_write() +{ + if (force->pair == NULL) + error->all("Pair_write command before pair_style is defined"); + force->pair->write_file(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::print() +{ + if (narg < 1) error->all("Illegal print command"); + + char *str = new char[MAXLINE]; + str[0] = '\0'; + for (int iarg = 0; iarg < narg; iarg++) { + strcat(str,arg[iarg]); + strcat(str," "); + } + + if (me == 0) { + if (screen) fprintf(screen,"%s\n",str); + if (logfile) fprintf(logfile,"%s\n",str); + } + + delete [] str; +} + +/* ---------------------------------------------------------------------- */ + +void Input::processors() +{ + if (domain->box_exist) + error->all("Processors command after simulation box is defined"); + if (narg != 3) error->all("Illegal processors command"); + comm->user_procgrid[0] = atoi(arg[0]); + comm->user_procgrid[1] = atoi(arg[1]); + comm->user_procgrid[2] = atoi(arg[2]); +} + +/* ---------------------------------------------------------------------- */ + +void Input::region() +{ + domain->add_region(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::reset_timestep() +{ + if (narg != 1) error->all("Illegal reset_timestep command"); + update->ntimestep = atoi(arg[0]); + if (update->ntimestep < 0) error->all("Timestep must be >= 0"); +} + +/* ---------------------------------------------------------------------- */ + +void Input::restart() +{ + output->create_restart(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::run_style() +{ + if (domain->box_exist == 0) + error->all("Run_style command before simulation box is defined"); + update->create_integrate(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::special_bonds() +{ + // store 1-3,1-4 values before change + + double lj2 = force->special_lj[2]; + double lj3 = force->special_lj[3]; + double coul2 = force->special_coul[2]; + double coul3 = force->special_coul[3]; + + force->set_special(narg,arg); + + // if simulation box defined and 1-3,1-4 values changed, redo special list + + if (domain->box_exist && atom->molecular) { + if (lj2 != force->special_lj[2] || lj3 != force->special_lj[3] || + coul2 != force->special_coul[2] || coul3 != force->special_coul[3]) { + Special special; + special.build(); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void Input::temp_modify() +{ + force->modify_temp(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::temperature() +{ + force->add_temp(narg,arg,0); +} + +/* ---------------------------------------------------------------------- */ + +void Input::thermo() +{ + if (narg != 1) error->all("Illegal thermo command"); + output->thermo_every = atoi(arg[0]); +} + +/* ---------------------------------------------------------------------- */ + +void Input::thermo_modify() +{ + output->thermo->modify_params(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::thermo_style() +{ + output->create_thermo(narg,arg); +} + +/* ---------------------------------------------------------------------- */ + +void Input::timestep() +{ + if (narg != 1) error->all("Illegal timestep command"); + update->dt = atof(arg[0]); +} + +/* ---------------------------------------------------------------------- */ + +void Input::undump() +{ + if (narg != 1) error->all("Illegal undump command"); + output->delete_dump(arg[0]); +} + +/* ---------------------------------------------------------------------- */ + +void Input::unfix() +{ + if (narg != 1) error->all("Illegal unfix command"); + modify->delete_fix(arg[0]); +} + +/* ---------------------------------------------------------------------- */ + +void Input::units() +{ + if (domain->box_exist) + error->all("Units command after simulation box is defined"); + if (narg != 1) error->all("Illegal units command"); + update->set_units(arg[0]); +} + +/* ---------------------------------------------------------------------- */ + +void Input::variable_command() +{ + variable->set(narg,arg); +} diff --git a/src/input.h b/src/input.h new file mode 100644 index 0000000000000000000000000000000000000000..80077f360e99cd139d0c36b18d72f771565433b4 --- /dev/null +++ b/src/input.h @@ -0,0 +1,112 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef INPUT_H +#define INPUT_H + +#include "stdio.h" +#include "lammps.h" + +class Variable; + +class Input : public LAMMPS { + public: + int narg; // # of command args + char **arg; // parsed args for command + Variable *variable; // defined variables + + Input(int, char **); + ~Input(); + void file(); // process all input + void file(char *); // process an input script + char *one(char *); // process a single command + void substitute(char *, int); // substitute for variables in a string + + private: + int me; // proc ID + char *command; // ptr to current command + int maxarg; // max # of args in arg + char *line,*copy,*work; // input line & copy of it + int echo_screen; // 0 = no, 1 = yes + int echo_log; // 0 = no, 1 = yes + int nfile,maxfile; // current # and max # of open input files + int label_active; // 0 = no label, 1 = looking for label + char *labelstr; // label string being looked for + int jump_skip; // 1 if skipping next jump, 0 otherwise + + FILE **infiles; // list of open input files + + void parse(); // parse an input text line + int execute_command(); // execute a single command + + void angle_coeff(); // individual commands + void angle_style(); + void atom_modify(); + void atom_style(); + void bond_coeff(); + void bond_style(); + void boundary(); + void clear(); + void dielectric(); + void dihedral_coeff(); + void dihedral_style(); + void dimension(); + void dipole(); + void dump(); + void dump_modify(); + void echo(); + void fix(); + void fix_modify(); + void group_command(); + void improper_coeff(); + void improper_style(); + void include(); + void jump(); + void kspace_modify(); + void kspace_style(); + void label(); + void lattice(); + void log(); + void mass(); + void min_modify(); + void min_style(); + void neigh_modify(); + void neighbor_command(); + void newton(); + void next_command(); + void orient(); + void origin(); + void pair_coeff(); + void pair_modify(); + void pair_style(); + void pair_write(); + void print(); + void processors(); + void region(); + void reset_timestep(); + void restart(); + void run_style(); + void special_bonds(); + void temp_modify(); + void temperature(); + void thermo(); + void thermo_modify(); + void thermo_style(); + void timestep(); + void undump(); + void unfix(); + void units(); + void variable_command(); +}; + +#endif diff --git a/src/integrate.h b/src/integrate.h new file mode 100644 index 0000000000000000000000000000000000000000..beab2d88acc604b12c74b25c12eb7f12b40817d0 --- /dev/null +++ b/src/integrate.h @@ -0,0 +1,30 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef INTEGRATE_H +#define INTEGRATE_H + +#include "lammps.h" + +class Integrate : public LAMMPS { + public: + Integrate(int, char **) {} + virtual ~Integrate() {} + virtual void init() = 0; + virtual void setup() = 0; + virtual void iterate(int) = 0; + virtual void cleanup() {} + virtual int memory_usage() {return 0;} +}; + +#endif diff --git a/src/kspace.cpp b/src/kspace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5c90fa394ef65e37229ea751ba23f4b2798d949a --- /dev/null +++ b/src/kspace.cpp @@ -0,0 +1,65 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "string.h" +#include "kspace.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +KSpace::KSpace(int narg, char **arg) +{ + energy = 0.0; + order = 5; + gridflag = 0; + gewaldflag = 0; + slabflag = 0; + slab_volfactor = 1; +} + +/* ---------------------------------------------------------------------- + modify parameters of the KSpace style +------------------------------------------------------------------------- */ + +void KSpace::modify_params(int narg, char **arg) +{ + int iarg = 0; + while (iarg < narg) { + if (strcmp(arg[iarg],"mesh") == 0) { + if (iarg+4 > narg) error->all("Illegal kspace_modify command"); + nx_pppm = atoi(arg[iarg+1]); + ny_pppm = atoi(arg[iarg+2]); + nz_pppm = atoi(arg[iarg+3]); + if (nx_pppm == 0 && ny_pppm == 0 && nz_pppm == 0) gridflag = 0; + else gridflag = 1; + iarg += 4; + } else if (strcmp(arg[iarg],"order") == 0) { + if (iarg+2 > narg) error->all("Illegal kspace_modify command"); + order = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"gewald") == 0) { + if (iarg+2 > narg) error->all("Illegal kspace_modify command"); + g_ewald = atof(arg[iarg+1]); + if (g_ewald == 0.0) gewaldflag = 0; + else gewaldflag = 1; + iarg += 2; + } else if (strcmp(arg[iarg],"slab") == 0) { + if (iarg+2 > narg) error->all("Illegal kspace_modify command"); + slab_volfactor = atof(arg[iarg+1]); + iarg += 2; + if (slab_volfactor < 2.0) error->all("Bad slab parameter"); + slabflag = 1; + } else error->all("Illegal kspace_modify command"); + } +} diff --git a/src/kspace.h b/src/kspace.h new file mode 100644 index 0000000000000000000000000000000000000000..500a318ddbf02d260afd4612adb671c170b5fef9 --- /dev/null +++ b/src/kspace.h @@ -0,0 +1,41 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef KSPACE_H +#define KSPACE_H + +#include "lammps.h" + +class KSpace : public LAMMPS { + public: + double energy; + double virial[6]; + + double g_ewald; + double slab_volfactor; + int gridflag,gewaldflag; + int nx_pppm,ny_pppm,nz_pppm; + int order; + int slabflag; + + KSpace(int, char **); + virtual ~KSpace() {} + void modify_params(int, char **); + virtual void init() = 0; + virtual void setup() = 0; + virtual void compute(int, int) = 0; + virtual void timing(int, double &, double &) {} + virtual int memory_usage() {return 0;} +}; + +#endif diff --git a/src/lammps.cpp b/src/lammps.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b3b95afedc65c97ad17ae7e282ac6ee815d753d2 --- /dev/null +++ b/src/lammps.cpp @@ -0,0 +1,64 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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. +------------------------------------------------------------------------- */ + +// parent class for all of LAMMPS +// all other classes inherit from this +// contains static ptrs to single instance of other classes +// contains MPI communicator and file handles for my world of procs + +#include "mpi.h" +#include "stdlib.h" +#include "lammps.h" +#include "system.h" + +// set static ptrs to NULL + +System *LAMMPS::sys = NULL; +Universe *LAMMPS::universe = NULL; +Input *LAMMPS::input = NULL; +Memory *LAMMPS::memory = NULL; +Error *LAMMPS::error = NULL; + +Atom *LAMMPS::atom = NULL; +Update *LAMMPS::update = NULL; +Neighbor *LAMMPS::neighbor = NULL; +Comm *LAMMPS::comm = NULL; +Domain *LAMMPS::domain = NULL; +Force *LAMMPS::force = NULL; +Modify *LAMMPS::modify = NULL; +Group *LAMMPS::group = NULL; +Output *LAMMPS::output = NULL; +Timer *LAMMPS::timer = NULL; + +MPI_Comm LAMMPS::world = 0; +FILE *LAMMPS::infile = NULL; +FILE *LAMMPS::screen = NULL; +FILE *LAMMPS::logfile = NULL; + +/* ---------------------------------------------------------------------- */ + +void LAMMPS::open(int narg, char **arg, MPI_Comm communicator) +{ + sys = new System(); + sys->open(narg,arg,MPI_COMM_WORLD); + sys->create(); +} + +/* ---------------------------------------------------------------------- */ + +void LAMMPS::close() +{ + sys->destroy(); + sys->close(); + delete sys; +} diff --git a/src/lammps.h b/src/lammps.h new file mode 100644 index 0000000000000000000000000000000000000000..cec8db9122158a1feaa17af26b7d53a4067f453b --- /dev/null +++ b/src/lammps.h @@ -0,0 +1,65 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef LAMMPS_H +#define LAMMPS_H + +#include "mpi.h" +#include "stdio.h" + +class System; +class Universe; +class Input; +class Memory; +class Error; + +class Atom; +class Update; +class Neighbor; +class Comm; +class Domain; +class Force; +class Modify; +class Group; +class Output; +class Timer; + +class LAMMPS { + public: + static System *sys; // simulation system + static Universe *universe; // universe of processors + static Input *input; // input script processing + static Memory *memory; // memory allocation functions + static Error *error; // error handling + + static Atom *atom; // atom-based quantities + static Update *update; // integrators/minimizers + static Neighbor *neighbor; // neighbor lists + static Comm *comm; // inter-processor communication + static Domain *domain; // simulation box + static Force *force; // inter-particle forces + static Modify *modify; // fixes + static Group *group; // groups of atoms + static Output *output; // thermo/dump/restart + static Timer *timer; // CPU timing info + + static MPI_Comm world; // communicator for my world of procs + static FILE *infile; // infile for my world + static FILE *screen; // screen output for my world + static FILE *logfile; // logfile for my world + + void open(int, char **, MPI_Comm); + void close(); +}; + +#endif diff --git a/src/library.cpp b/src/library.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6203ae94daa07623298fc4f6c1ae9ff60dd13505 --- /dev/null +++ b/src/library.cpp @@ -0,0 +1,112 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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. +------------------------------------------------------------------------- */ + +// LAMMPS as a library that can be called from another program +// C-style interface + +#include "mpi.h" +#include "library.h" +#include "lammps.h" +#include "input.h" +#include "atom.h" + +// variable visible to all library functions + +LAMMPS *lammps; + +/* ---------------------------------------------------------------------- */ + +void lammps_open(int argc, char **argv, MPI_Comm communicator) +{ + lammps = new LAMMPS(); + lammps->open(argc,argv,communicator); +} + +/* ---------------------------------------------------------------------- */ + +void lammps_close() +{ + lammps->close(); + delete lammps; +} + +/* ---------------------------------------------------------------------- */ + +void lammps_file(char *str) +{ + lammps->input->file(str); +} + +/* ---------------------------------------------------------------------- */ + +char *lammps_command(char *str) +{ + return lammps->input->one(str); +} + +/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- */ + +int lammps_get_natoms() +{ + int natoms = static_cast<int> (lammps->atom->natoms); + return natoms; +} + +/* ---------------------------------------------------------------------- */ + +void lammps_get_coords(double *coords) +{ + int natoms = static_cast<int> (lammps->atom->natoms); + double *copy = new double[3*natoms]; + for (int i = 0; i < 3*natoms; i++) copy[i] = 0.0; + + double **x = lammps->atom->x; + int *tag = lammps->atom->tag; + int nlocal = lammps->atom->nlocal; + + int id,offset; + for (int i = 0; i < nlocal; i++) { + id = tag[i]; + offset = 3*(id-1); + copy[offset+0] = x[i][0]; + copy[offset+1] = x[i][1]; + copy[offset+2] = x[i][2]; + } + + MPI_Allreduce(copy,coords,3*natoms,MPI_DOUBLE,MPI_SUM,lammps->world); + delete [] copy; +} + +/* ---------------------------------------------------------------------- */ + +void lammps_put_coords(const double *coords) +{ + int natoms = static_cast<int> (lammps->atom->natoms); + + double **x = lammps->atom->x; + int nlocal = lammps->atom->nlocal; + + int m,offset; + for (int i = 0; i < natoms; i++) { + if ((m = lammps->atom->map(i+1)) >= 0) { + offset = 3*i; + x[m][0] = coords[offset+0]; + x[m][1] = coords[offset+1]; + x[m][2] = coords[offset+2]; + } + } +} diff --git a/src/library.h b/src/library.h new file mode 100644 index 0000000000000000000000000000000000000000..520eb5950781a1fc58953058fcd7dfce4b452ef7 --- /dev/null +++ b/src/library.h @@ -0,0 +1,25 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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. +------------------------------------------------------------------------- */ + +// prototypes for calling LAMMPS as a library + +#include "mpi.h" + +void lammps_open(int, char **, MPI_Comm); // start LAMMPS w/ command-line args +void lammps_close(); // shut-down LAMMPS +void lammps_file(char *); // execute an input script +char *lammps_command(char *); // execute a single LAMMPS command + +int lammps_get_natoms(); // return # of atoms +void lammps_get_coords(double *); // retrieve list of coords on all procs +void lammps_put_coords(double *); // overwrite list of coords on all procs diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..034a864e4e53da4ac26bf6cafaaba1af8cd734e2 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,33 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "lammps.h" +#include "input.h" + +/* ---------------------------------------------------------------------- + main program to drive LAMMPS +------------------------------------------------------------------------- */ + +int main(int argc, char **argv) +{ + MPI_Init(&argc,&argv); + + LAMMPS *lammps = new LAMMPS(); + lammps->open(argc,argv,MPI_COMM_WORLD); + lammps->input->file(); + lammps->close(); + delete lammps; + + MPI_Finalize(); +} diff --git a/src/memory.cpp b/src/memory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0ff88da3495e18935d30c76aced936230a476e7d --- /dev/null +++ b/src/memory.cpp @@ -0,0 +1,432 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- + safe malloc +------------------------------------------------------------------------- */ + +void *Memory::smalloc(int n, char *name) +{ + if (n == 0) return NULL; + void *ptr = malloc(n); + if (ptr == NULL) { + char str[128]; + sprintf(str,"Failed to allocate %d bytes for array %s",n,name); + error->one(str); + } + return ptr; +} + +/* ---------------------------------------------------------------------- + safe free +------------------------------------------------------------------------- */ + +void Memory::sfree(void *ptr) +{ + if (ptr == NULL) return; + free(ptr); +} + +/* ---------------------------------------------------------------------- + safe realloc +------------------------------------------------------------------------- */ + +void *Memory::srealloc(void *ptr, int n, char *name) +{ + if (n == 0) return NULL; + ptr = realloc(ptr,n); + if (ptr == NULL) { + char str[128]; + sprintf(str,"Failed to reallocate %d bytes for array %s",n,name); + error->one(str); + } + return ptr; +} + +/* ---------------------------------------------------------------------- + create a 1d double array with index from nlo to nhi inclusive +------------------------------------------------------------------------- */ + +double *Memory::create_1d_double_array(int nlo, int nhi, char *name) +{ + int n = nhi - nlo + 1; + double *array = (double *) smalloc(n*sizeof(double),name); + return array-nlo; +} + +/* ---------------------------------------------------------------------- + free a 1d double array with index offset +------------------------------------------------------------------------- */ + +void Memory::destroy_1d_double_array(double *array, int offset) +{ + if (array == NULL) return; + sfree(array + offset); +} + +/* ---------------------------------------------------------------------- + create a 2d double array +------------------------------------------------------------------------- */ + +double **Memory::create_2d_double_array(int n1, int n2, char *name) + +{ + double *data = (double *) smalloc(n1*n2*sizeof(double),name); + double **array = (double **) smalloc(n1*sizeof(double *),name); + + int n = 0; + for (int i = 0; i < n1; i++) { + array[i] = &data[n]; + n += n2; + } + + return array; +} + +/* ---------------------------------------------------------------------- + free a 2d double array +------------------------------------------------------------------------- */ + +void Memory::destroy_2d_double_array(double **array) + +{ + if (array == NULL) return; + sfree(array[0]); + sfree(array); +} + +/* ---------------------------------------------------------------------- + grow or shrink 1st dim of a 2d double array + last dim must stay the same + if either dim is 0, return NULL +------------------------------------------------------------------------- */ + +double **Memory::grow_2d_double_array(double **array, + int n1, int n2, char *name) + +{ + if (array == NULL) return create_2d_double_array(n1,n2,name); + + double *data = (double *) srealloc(array[0],n1*n2*sizeof(double),name); + sfree(array); + array = (double **) smalloc(n1*sizeof(double *),name); + + int n = 0; + for (int i = 0; i < n1; i++) { + array[i] = &data[n]; + n += n2; + } + + return array; +} + +/* ---------------------------------------------------------------------- + create a 2d int array + if either dim is 0, return NULL +------------------------------------------------------------------------- */ + +int **Memory::create_2d_int_array(int n1, int n2, char *name) + +{ + if (n1 == 0 || n2 == 0) return NULL; + + int *data = (int *) smalloc(n1*n2*sizeof(int),name); + int **array = (int **) smalloc(n1*sizeof(int *),name); + + int n = 0; + for (int i = 0; i < n1; i++) { + array[i] = &data[n]; + n += n2; + } + + return array; +} + +/* ---------------------------------------------------------------------- + free a 2d int array +------------------------------------------------------------------------- */ + +void Memory::destroy_2d_int_array(int **array) + +{ + if (array == NULL) return; + sfree(array[0]); + sfree(array); +} + +/* ---------------------------------------------------------------------- + grow or shrink 1st dim of a 2d int array + last dim must stay the same + if either dim is 0, return NULL +------------------------------------------------------------------------- */ + +int **Memory::grow_2d_int_array(int **array, int n1, int n2, char *name) + +{ + if (n1 == 0 || n2 == 0) { + destroy_2d_int_array(array); + return NULL; + } + + if (array == NULL) return create_2d_int_array(n1,n2,name); + + int *data = (int *) srealloc(array[0],n1*n2*sizeof(int),name); + sfree(array); + array = (int **) smalloc(n1*sizeof(int *),name); + + int n = 0; + for (int i = 0; i < n1; i++) { + array[i] = &data[n]; + n += n2; + } + + return array; +} + +/* ---------------------------------------------------------------------- + create a 2d double array with 2nd index from n2lo to n2hi inclusive +------------------------------------------------------------------------- */ + +double **Memory::create_2d_double_array(int n1, int n2lo, int n2hi, char *name) +{ + int n2 = n2hi - n2lo + 1; + double **array = create_2d_double_array(n1,n2,name); + + for (int i = 0; i < n1; i++) array[i] -= n2lo; + return array; +} + +/* ---------------------------------------------------------------------- + free a 2d double array with 2nd index offset +------------------------------------------------------------------------- */ + +void Memory::destroy_2d_double_array(double **array, int offset) +{ + if (array == NULL) return; + sfree(&array[0][offset]); + sfree(array); +} + +/* ---------------------------------------------------------------------- + create a 3d double array +------------------------------------------------------------------------- */ + +double ***Memory::create_3d_double_array(int n1, int n2, int n3, char *name) +{ + int i,j; + + double *data = (double *) smalloc(n1*n2*n3*sizeof(double),name); + double **plane = (double **) smalloc(n1*n2*sizeof(double *),name); + double ***array = (double ***) smalloc(n1*sizeof(double **),name); + + int n = 0; + for (i = 0; i < n1; i++) { + array[i] = &plane[i*n2]; + for (j = 0; j < n2; j++) { + plane[i*n2+j] = &data[n]; + n += n3; + } + } + + return array; +} + +/* ---------------------------------------------------------------------- + free a 3d double array +------------------------------------------------------------------------- */ + +void Memory::destroy_3d_double_array(double ***array) +{ + if (array == NULL) return; + sfree(array[0][0]); + sfree(array[0]); + sfree(array); +} + +/* ---------------------------------------------------------------------- + grow or shrink 1st dim of a 3d double array + last 2 dims must stay the same + if any dim is 0, return NULL +------------------------------------------------------------------------- */ + +double ***Memory::grow_3d_double_array(double ***array, + int n1, int n2, int n3, char *name) +{ + int i,j; + + if (n1 == 0 || n2 == 0 || n3 == 0) { + destroy_3d_double_array(array); + return NULL; + } + + if (array == NULL) return create_3d_double_array(n1,n2,n3,name); + + double *data = (double *) srealloc(array[0][0],n1*n2*n3*sizeof(double),name); + sfree(array[0]); + double **plane = (double **) smalloc(n1*n2*sizeof(double *),name); + sfree(array); + array = (double ***) smalloc(n1*sizeof(double **),name); + + int n = 0; + for (i = 0; i < n1; i++) { + array[i] = &plane[i*n2]; + for (j = 0; j < n2; j++) { + plane[i*n2+j] = &data[n]; + n += n3; + } + } + + return array; +} + +/* ---------------------------------------------------------------------- + create a 3d double array with 1st index from n1lo to n1hi inclusive +------------------------------------------------------------------------- */ + +double ***Memory::create_3d_double_array(int n1lo, int n1hi, + int n2, int n3, char *name) +{ + int n1 = n1hi - n1lo + 1; + double ***array = create_3d_double_array(n1,n2,n3,name); + return array-n1lo; +} + +/* ---------------------------------------------------------------------- + free a 3d double array with 1st index offset +------------------------------------------------------------------------- */ + +void Memory::destroy_3d_double_array(double ***array, int offset) +{ + if (array) destroy_3d_double_array(array + offset); +} + +/* ---------------------------------------------------------------------- + create a 3d double array with + 1st index from n1lo to n1hi inclusive, + 2nd index from n2lo to n2hi inclusive, + 3rd index from n3lo to n3hi inclusive +------------------------------------------------------------------------- */ + +double ***Memory::create_3d_double_array(int n1lo, int n1hi, + int n2lo, int n2hi, + int n3lo, int n3hi, char *name) +{ + int n1 = n1hi - n1lo + 1; + int n2 = n2hi - n2lo + 1; + int n3 = n3hi - n3lo + 1; + double ***array = create_3d_double_array(n1,n2,n3,name); + + for (int i = 0; i < n1*n2; i++) array[0][i] -= n3lo; + for (int i = 0; i < n1; i++) array[i] -= n2lo; + return array-n1lo; +} + +/* ---------------------------------------------------------------------- + free a 3d double array with all 3 indices offset +------------------------------------------------------------------------- */ + +void Memory::destroy_3d_double_array(double ***array, int n1_offset, + int n2_offset, int n3_offset) +{ + if (array == NULL) return; + sfree(&array[n1_offset][n2_offset][n3_offset]); + sfree(&array[n1_offset][n2_offset]); + sfree(array + n1_offset); +} + + +/* ---------------------------------------------------------------------- + create a 3d int array +------------------------------------------------------------------------- */ + +int ***Memory::create_3d_int_array(int n1, int n2, int n3, char *name) +{ + int i,j; + + int *data = (int *) smalloc(n1*n2*n3*sizeof(int),name); + int **plane = (int **) smalloc(n1*n2*sizeof(int *),name); + int ***array = (int ***) smalloc(n1*sizeof(int **),name); + + int n = 0; + for (i = 0; i < n1; i++) { + array[i] = &plane[i*n2]; + for (j = 0; j < n2; j++) { + plane[i*n2+j] = &data[n]; + n += n3; + } + } + + return array; +} + +/* ---------------------------------------------------------------------- + free a 3d int array +------------------------------------------------------------------------- */ + +void Memory::destroy_3d_int_array(int ***array) +{ + if (array == NULL) return; + sfree(array[0][0]); + sfree(array[0]); + sfree(array); +} + +/* ---------------------------------------------------------------------- + create a 4d double array +------------------------------------------------------------------------- */ + +double ****Memory::create_4d_double_array(int n1, int n2, int n3, int n4, char *name) +{ + int i,j,k; + + double *data = (double *) smalloc(n1*n2*n3*n4*sizeof(double),name); + double **cube = (double **) smalloc(n1*n2*n3*sizeof(double *),name); + double ***plane = (double ***) smalloc(n1*n2*sizeof(double **),name); + double ****array = (double ****) smalloc(n1*sizeof(double ***),name); + + int n = 0; + for (i = 0; i < n1; i++) { + array[i] = &plane[i*n2]; + for (j = 0; j < n2; j++) { + plane[i*n2+j] = &cube[i*n2*n3+j*n3]; + for (k = 0; k < n3; k++) { + cube[i*n2*n3+j*n3+k] = &data[n]; + n += n4; + } + } + } + + return array; +} + +/* ---------------------------------------------------------------------- + free a 4d double array +------------------------------------------------------------------------- */ + +void Memory::destroy_4d_double_array(double ****array) +{ + if (array == NULL) return; + sfree(array[0][0][0]); + sfree(array[0][0]); + sfree(array[0]); + sfree(array); +} + + diff --git a/src/memory.h b/src/memory.h new file mode 100644 index 0000000000000000000000000000000000000000..62cf2484e2f336995fe31cc287e076aec62d7e59 --- /dev/null +++ b/src/memory.h @@ -0,0 +1,60 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef MEMORY_H +#define MEMORY_H + +#include "lammps.h" + +class Memory : public LAMMPS { + public: + Memory() {} + ~Memory() {} + + void *smalloc(int n, char *); + void sfree(void *); + void *srealloc(void *, int n, char *name); + + double *create_1d_double_array(int, int, char *); + void destroy_1d_double_array(double *, int); + + double **create_2d_double_array(int, int, char *); + void destroy_2d_double_array(double **); + double **grow_2d_double_array(double **, int, int, char *); + + int **create_2d_int_array(int, int, char *); + void destroy_2d_int_array(int **); + int **grow_2d_int_array(int **, int, int, char *); + + double **create_2d_double_array(int, int, int, char *); + void destroy_2d_double_array(double **, int); + + double ***create_3d_double_array(int, int, int, char *); + void destroy_3d_double_array(double ***); + double ***grow_3d_double_array(double ***, int, int, int, char *); + + double ***create_3d_double_array(int, int, int, int, char *); + void destroy_3d_double_array(double ***, int); + + double ***create_3d_double_array(int, int, int, int, int, int, char *); + void destroy_3d_double_array(double ***, int, int, int); + + int ***create_3d_int_array(int, int, int, char *); + void destroy_3d_int_array(int ***); + + double ****create_4d_double_array(int, int, int, int, char *); + void destroy_4d_double_array(double ****); +}; + +#endif + diff --git a/src/memory.hold.h b/src/memory.hold.h new file mode 100644 index 0000000000000000000000000000000000000000..596b0bf0af32908a4fbc55fb7866285e8338447c --- /dev/null +++ b/src/memory.hold.h @@ -0,0 +1,53 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef MEMORY_H +#define MEMORY_H + +#include "lammps.h" + +class Memory : public LAMMPS { + public: + Memory() {} + ~Memory() {} + + void *smalloc(int n, char *); + void sfree(void *); + void *srealloc(void *, int n, char *name); + + double *create_1d_double_array(int, int, char *); + void destroy_1d_double_array(double *, int); + + double **create_2d_double_array(int, int, char *); + void destroy_2d_double_array(double **); + double **grow_2d_double_array(double **, int, int, char *); + + int **create_2d_int_array(int, int, char *); + void destroy_2d_int_array(int **); + int **grow_2d_int_array(int **, int, int, char *); + + double **create_2d_double_array(int, int, int, char *); + void destroy_2d_double_array(double **, int); + + double ***create_3d_double_array(int, int, int, char *); + void destroy_3d_double_array(double ***); + double ***grow_3d_double_array(double ***, int, int, int, char *); + + double ***create_3d_double_array(int, int, int, int, char *); + void destroy_3d_double_array(double ***, int); + + double ***create_3d_double_array(int, int, int, int, int, int, char *); + void destroy_3d_double_array(double ***, int, int, int); +}; + +#endif diff --git a/src/min.cpp b/src/min.cpp new file mode 100644 index 0000000000000000000000000000000000000000..745a68bc107fd4e4c79fa4c1159fe36a303bd54d --- /dev/null +++ b/src/min.cpp @@ -0,0 +1,60 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "string.h" +#include "min.h" +#include "error.h" + +#define SCAN 0 // same as in min_cg.cpp +#define SECANT 1 + +/* ---------------------------------------------------------------------- */ + +Min::Min() +{ + linestyle = SECANT; + dmin = 1.0e-5; + dmax = 0.1; + lineiter = 10; +} + +/* ---------------------------------------------------------------------- */ + +void Min::modify_params(int narg, char **arg) +{ + if (narg == 0) error->all("Illegal min_modify command"); + + int iarg = 0; + while (iarg < narg) { + if (strcmp(arg[iarg],"linestyle") == 0) { + if (iarg+2 > narg) error->all("Illegal min_modify command"); + if (strcmp(arg[iarg+1],"scan") == 0) linestyle = SCAN; + else if (strcmp(arg[iarg+1],"secant") == 0) linestyle = SECANT; + else error->all("Illegal min_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"dmin") == 0) { + if (iarg+2 > narg) error->all("Illegal min_modify command"); + dmin = atof(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"dmax") == 0) { + if (iarg+2 > narg) error->all("Illegal min_modify command"); + dmax = atof(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"lineiter") == 0) { + if (iarg+2 > narg) error->all("Illegal min_modify command"); + lineiter = atoi(arg[iarg+1]); + iarg += 2; + } else error->all("Illegal min_modify command"); + } +} diff --git a/src/min.h b/src/min.h new file mode 100644 index 0000000000000000000000000000000000000000..26d1c147db4e81683da56c79ba7db5092175f060 --- /dev/null +++ b/src/min.h @@ -0,0 +1,36 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef MIN_H +#define MIN_H + +#include "lammps.h" + +class Min : public LAMMPS { + public: + double einitial,efinal,eprevious; + double gnorm2_init,gnorminf_init,gnorm2_final,gnorminf_final; + int niter,neval; + double dmin,dmax; + int linestyle,lineiter; + + Min(); + virtual ~Min() {} + virtual void init() = 0; + virtual void run() = 0; + virtual int memory_usage() {return 0;} + + void modify_params(int, char **); +}; + +#endif diff --git a/src/min_cg.cpp b/src/min_cg.cpp new file mode 100644 index 0000000000000000000000000000000000000000..434d2ae1164efff7b588655ce252653df77bdb6e --- /dev/null +++ b/src/min_cg.cpp @@ -0,0 +1,647 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Sources: Numerical Recipes frprmn routine + "Conjugate Gradient Method Without the Agonizing Pain" by + JR Shewchuk, http://www-2.cs.cmu.edu/~jrs/jrspapers.html#cg +------------------------------------------------------------------------- */ + +#include "math.h" +#include "string.h" +#include "mpi.h" +#include "min_cg.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "atom.h" +#include "force.h" +#include "pair.h" +#include "bond.h" +#include "angle.h" +#include "dihedral.h" +#include "improper.h" +#include "kspace.h" +#include "output.h" +#include "thermo.h" +#include "update.h" +#include "modify.h" +#include "fix.h" +#include "fix_minimize.h" +#include "timer.h" +#include "memory.h" +#include "error.h" + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +#define EPS 1.0e-6 +#define SCAN_FACTOR 2.0 +#define SECANT_EPS 1.0e-3 + +#define SCAN 0 // same as in min.cpp +#define SECANT 1 + +/* ---------------------------------------------------------------------- + initialization before run +------------------------------------------------------------------------- */ + +void MinCG::init() +{ + // create fix needed for storing atom-based gradient vectors + // will delete it at end of run + + char **fixarg = new char*[3]; + fixarg[0] = "MINIMIZE"; + fixarg[1] = "all"; + fixarg[2] = "MINIMIZE"; + modify->add_fix(3,fixarg); + delete [] fixarg; + + for (int i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"MINIMIZE") == 0) ifix_minimize = i; + + // zero gradient vectors before first atom exchange + + setup_vectors(); + for (int i = 0; i < ndof; i++) h[i] = g[i] = 0.0; + + // virial_thermo is how virial should be computed on thermo timesteps + // 1 = computed explicity by pair, 2 = computed implicitly by pair + + if (force->newton_pair) virial_thermo = 2; + else virial_thermo = 1; + + // set flags for what arrays to clear in force_clear() + // need to clear torques if atom_style is dipole + // need to clear phia if atom_style is granular + // don't need to clear f_pair if atom_style is only granular (no virial) + + torqueflag = 0; + if (atom->check_style("dipole")) torqueflag = 1; + granflag = 0; + if (atom->check_style("granular")) granflag = 1; + pairflag = 1; + if (strcmp(atom->style,"granular") == 0) pairflag = 0; + + // reset reneighboring criteria if necessary + + neigh_every = neighbor->every; + neigh_delay = neighbor->delay; + neigh_dist_check = neighbor->dist_check; + + if (neigh_every != 1 || neigh_delay != 0 || neigh_dist_check != 1) { + if (comm->me == 0) + error->warning("Resetting reneighboring criteria during minimization"); + } + + neighbor->every = 1; + neighbor->delay = 0; + neighbor->dist_check = 1; + + // set ptr to linemin function + + if (linestyle == SCAN) linemin = &MinCG::linemin_scan; + else if (linestyle == SECANT) linemin = &MinCG::linemin_secant; + + // local versions of Update quantities + + maxpair = update->maxpair; + f_pair = update->f_pair; +} + +/* ---------------------------------------------------------------------- + perform minimization, with setup first +------------------------------------------------------------------------- */ + +void MinCG::run() +{ + double tmp,*f; + + // set initial force & energy + + setup(); + setup_vectors(); + output->thermo->fix_compute_pe(); + output->thermo->compute_pe(); + ecurrent = output->thermo->potential_energy; + + // stats for Finish to print + + einitial = ecurrent; + + f = atom->f[0]; + tmp = 0.0; + for (int i = 0; i < ndof; i++) tmp += f[i]*f[i]; + MPI_Allreduce(&tmp,&gnorm2_init,1,MPI_DOUBLE,MPI_SUM,world); + gnorm2_init = sqrt(gnorm2_init); + + tmp = 0.0; + for (int i = 0; i < ndof; i++) tmp = MAX(fabs(f[i]),tmp); + MPI_Allreduce(&tmp,&gnorminf_init,1,MPI_DOUBLE,MPI_MAX,world); + + // minimizer iterations + + timer->barrier_start(TIME_LOOP); + iterate(update->nsteps); + + // account for early exit from iterate loop due to convergence + // set niter/nsteps for Finish stats to print + // set output->next values to this timestep + // call eng_force to insure vflag is set when forces computed + // output->write does final output for thermo, dump, restart files + + if (niter < update->nsteps) { + niter++; + update->nsteps = niter; + for (int idump = 0; idump < output->ndump; idump++) + output->next_dump[idump] = update->ntimestep; + output->next_dump_any = update->ntimestep; + if (output->restart_every) output->next_restart = update->ntimestep; + output->next_thermo = update->ntimestep; + int ntmp; + double *xtmp,*htmp,etmp; + eng_force(&ntmp,&xtmp,&htmp,&etmp); + output->write(update->ntimestep); + } + timer->barrier_stop(TIME_LOOP); + + // delete fix at end of run, so its atom arrays won't persist + + modify->delete_fix("MINIMIZE"); + + // reset reneighboring criteria + + neighbor->every = neigh_every; + neighbor->delay = neigh_delay; + neighbor->dist_check = neigh_dist_check; + + // stats for Finish to print + + efinal = ecurrent; + + f = atom->f[0]; + tmp = 0.0; + for (int i = 0; i < ndof; i++) tmp += f[i]*f[i]; + MPI_Allreduce(&tmp,&gnorm2_final,1,MPI_DOUBLE,MPI_SUM,world); + gnorm2_final = sqrt(gnorm2_final); + + tmp = 0.0; + for (int i = 0; i < ndof; i++) tmp = MAX(fabs(f[i]),tmp); + MPI_Allreduce(&tmp,&gnorminf_final,1,MPI_DOUBLE,MPI_MAX,world); +} + +/* ---------------------------------------------------------------------- + setup before run +------------------------------------------------------------------------- */ + +void MinCG::setup() +{ + if (comm->me == 0 && screen) fprintf(screen,"Setting up minimization ...\n"); + + // setup domain, communication and neighboring + // acquire ghosts + // build neighbor lists + // reset gradient vector ptrs + + domain->pbc(); + domain->reset_box(); + comm->setup(); + if (neighbor->style) neighbor->setup_bins(); + comm->exchange(); + comm->borders(); + neighbor->build(); + neighbor->ncalls = 0; + setup_vectors(); + + // compute all forces + + int eflag = 1; + int vflag = virial_thermo; + force_clear(vflag); + + if (atom->molecular) { + if (force->bond) force->bond->compute(eflag,vflag); + if (force->angle) force->angle->compute(eflag,vflag); + if (force->dihedral) force->dihedral->compute(eflag,vflag); + if (force->improper) force->improper->compute(eflag,vflag); + } + + if (force->pair) force->pair->compute(eflag,vflag); + + if (force->kspace) { + force->kspace->setup(); + force->kspace->compute(eflag,vflag); + } + + if (force->newton) comm->reverse_communicate(); + + modify->setup(); + output->setup(1); +} + +/* ---------------------------------------------------------------------- + minimization via conjugate gradient iterations + Polak-Ribiere formulation +------------------------------------------------------------------------- */ + +void MinCG::iterate(int n) +{ + int i,gradsearch,fail; + double alpha,beta,gg,dot[2],dotall[2]; + double *f; + + f = atom->f[0]; + for (int i = 0; i < ndof; i++) h[i] = g[i] = f[i]; + + dot[0] = 0.0; + for (i = 0; i < ndof; i++) dot[0] += f[i]*f[i]; + MPI_Allreduce(dot,&gg,1,MPI_DOUBLE,MPI_SUM,world); + + neval = 0; + gradsearch = 1; + + for (niter = 0; niter < n; niter++) { + + update->ntimestep++; + + // line minimization along direction h from current atom->x + + eprevious = ecurrent; + fail = (this->*linemin)(ndof,atom->x[0],h,ecurrent,dmin,dmax,alpha,neval); + + // if max_eval exceeded, all done + // if linemin failed or energy did not decrease sufficiently: + // all done if searched in grad direction + // else force next search to be in grad direction (CG restart) + + if (neval >= update->max_eval) break; + + if (fail || fabs(ecurrent-eprevious) <= + update->tolerance * 0.5*(fabs(ecurrent) + fabs(eprevious) + EPS)) { + if (gradsearch == 1) break; + gradsearch = -1; + } + + // update h from new f = -Grad(x) and old g + // old g,h must have migrated with atoms to do this correctly + // done if size sq of grad vector < EPS + // force new search dir to be grad dir if need to restart CG + // set gradsearch to 1 if will search in grad dir on next iteration + + f = atom->f[0]; + dot[0] = dot[1] = 0.0; + for (i = 0; i < ndof; i++) { + dot[0] += f[i]*f[i]; + dot[1] += f[i]*g[i]; + } + MPI_Allreduce(dot,dotall,2,MPI_DOUBLE,MPI_SUM,world); + + beta = MAX(0.0,(dotall[0] - dotall[1])/gg); + gg = dotall[0]; + if (gg < EPS) break; + + if (gradsearch == -1) beta = 0.0; + if (beta == 0.0) gradsearch = 1; + else gradsearch = 0; + + for (i = 0; i < ndof; i++) { + g[i] = f[i]; + h[i] = g[i] + beta*h[i]; + } + + // output for thermo, dump, restart files + + if (output->next == update->ntimestep) { + timer->stamp(); + output->write(update->ntimestep); + timer->stamp(TIME_OUTPUT); + } + } +} + +/* ---------------------------------------------------------------------- + set ndof and vector pointers after atoms have migrated +------------------------------------------------------------------------- */ + +void MinCG::setup_vectors() +{ + ndof = 3 * atom->nlocal; + g = ((FixMinimize *) modify->fix[ifix_minimize])->gradient[0]; + h = ((FixMinimize *) modify->fix[ifix_minimize])->searchdir[0]; +} + +/* ---------------------------------------------------------------------- + evaluate potential energy and forces + may migrate atoms + new energy stored in ecurrent and returned (in case caller not in class) + negative gradient will be stored in atom->f +------------------------------------------------------------------------- */ + +void MinCG::eng_force(int *pndof, double **px, double **ph, double *peng) +{ + // check for reneighboring + // always communicate since minimizer moved atoms + // if reneighbor, have to setup_vectors() since atoms migrated + + int nflag = neighbor->decide(); + + if (nflag == 0) { + timer->stamp(); + comm->communicate(); + timer->stamp(TIME_COMM); + } else { + domain->pbc(); + if (domain->box_change) { + domain->reset_box(); + comm->setup(); + if (neighbor->style) neighbor->setup_bins(); + } + timer->stamp(); + comm->exchange(); + comm->borders(); + timer->stamp(TIME_COMM); + neighbor->build(); + timer->stamp(TIME_NEIGHBOR); + setup_vectors(); + } + + // eflag is always set, since minimizer needs potential energy + + int eflag = 1; + int vflag = 0; + if (output->next_thermo == update->ntimestep) vflag = virial_thermo; + force_clear(vflag); + + timer->stamp(); + if (atom->molecular) { + if (force->bond) force->bond->compute(eflag,vflag); + if (force->angle) force->angle->compute(eflag,vflag); + if (force->dihedral) force->dihedral->compute(eflag,vflag); + if (force->improper) force->improper->compute(eflag,vflag); + timer->stamp(TIME_BOND); + } + + if (force->pair) { + force->pair->compute(eflag,vflag); + timer->stamp(TIME_PAIR); + } + + if (force->kspace) { + force->kspace->compute(eflag,vflag); + timer->stamp(TIME_KSPACE); + } + + if (force->newton) { + comm->reverse_communicate(); + timer->stamp(TIME_COMM); + } + + // fixes that affect minimization + + if (modify->n_min_post_force) modify->min_post_force(vflag); + + // compute potential energy of system via Thermo + + output->thermo->fix_compute_pe(); + output->thermo->compute_pe(); + ecurrent = output->thermo->potential_energy; + + // return updated ptrs to caller since atoms may have migrated + + *pndof = ndof; + *px = atom->x[0]; + *ph = h; + *peng = ecurrent; +} + +/* ---------------------------------------------------------------------- + clear force on own & ghost atoms + setup and clear other arrays as needed +------------------------------------------------------------------------- */ + +void MinCG::force_clear(int vflag) +{ + int i; + + // clear global force array + // nall includes ghosts only if either newton flag is set + + int nall; + if (force->newton) nall = atom->nlocal + atom->nghost; + else nall = atom->nlocal; + + double **f = atom->f; + for (i = 0; i < nall; i++) { + f[i][0] = 0.0; + f[i][1] = 0.0; + f[i][2] = 0.0; + } + + if (torqueflag) { + double **torque = atom->torque; + for (i = 0; i < nall; i++) { + torque[i][0] = 0.0; + torque[i][1] = 0.0; + torque[i][2] = 0.0; + } + } + + if (granflag) { + double **phia = atom->phia; + for (i = 0; i < nall; i++) { + phia[i][0] = 0.0; + phia[i][1] = 0.0; + phia[i][2] = 0.0; + } + } + + // clear f_pair array if using it this timestep to compute virial + + if (vflag == 2 && pairflag) { + if (atom->nmax > maxpair) { + maxpair = atom->nmax; + memory->destroy_2d_double_array(f_pair); + f_pair = memory->create_2d_double_array(maxpair,3,"min:f_pair"); + update->maxpair = maxpair; + update->f_pair = f_pair; + } + for (i = 0; i < nall; i++) { + f_pair[i][0] = 0.0; + f_pair[i][1] = 0.0; + f_pair[i][2] = 0.0; + } + } +} + +/* ---------------------------------------------------------------------- + line minimization methods + find minimum-energy starting at x along dir direction + input: n = # of degrees of freedom on this proc + x = ptr to atom->x[0] as vector + dir = search direction as vector + eng = current energy at initial x + min/max dist = min/max distance to move any atom coord + output: return 0 if successful move, set alpha + return 1 if failed, no move, no need to set alpha + alpha = distance moved along dir to set x to min-eng config + caller has several quantities set via last call to eng_force() + INSURE last call to eng_force() is consistent with returns + if fail, eng_force() of original x + if succeed, eng_force() at x + alpha*dir + atom->x = coords at new configuration + atom->f = force (-Grad) is evaulated at new configuration + ecurrent = energy of new configuration + NOTE: when call eng_force: n,x,dir,eng may change due to atom migration + updated values are returned by eng_force() + this routine CANNOT store atom-based quantities b/c of migration +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + linemin: scan forward by larger and larger steps (SCAN_FACTOR) + uses no gradient info, but should be very robust + start at mindist, continue until maxdist + quit as soon as energy starts to rise +------------------------------------------------------------------------- */ + +int MinCG::linemin_scan(int n, double *x, double *dir, double eng, + double mindist, double maxdist, + double &alpha, int &nfunc) +{ + int i; + double fmax,fme,elowest,alphamin,alphamax,alphalast; + + // alphamin = step that moves some atom coord by mindist + // alphamax = step that moves some atom coord by maxdist + + fme = 0.0; + for (i = 0; i < n; i++) fme = MAX(fme,fabs(dir[i])); + MPI_Allreduce(&fme,&fmax,1,MPI_DOUBLE,MPI_MAX,world); + if (fmax == 0.0) return 1; + + alphamin = mindist/fmax; + alphamax = maxdist/fmax; + + // if minstep is already uphill, fail + // if eng increases, stop and return previous alpha + // if alphamax, stop and return alphamax + + elowest = eng; + alpha = alphamin; + + while (1) { + for (i = 0; i < n; i++) x[i] += alpha*dir[i]; + eng_force(&n,&x,&dir,&eng); + nfunc++; + + if (alpha == alphamin && eng >= elowest) { + for (i = 0; i < n; i++) x[i] -= alpha*dir[i]; + eng_force(&n,&x,&dir,&eng); + nfunc++; + return 1; + } + if (eng > elowest) { + for (i = 0; i < n; i++) x[i] += (alphalast-alpha)*dir[i]; + eng_force(&n,&x,&dir,&eng); + nfunc++; + alpha = alphalast; + return 0; + } + if (alpha == alphamax) return 0; + + elowest = eng; + alphalast = alpha; + alpha *= SCAN_FACTOR; + if (alpha > alphamax) alpha = alphamax; + } +} + +/* ---------------------------------------------------------------------- + linemin: use secant approximation to estimate parabola minimum at each step + should converge more quickly/accurately than "scan", but may be less robust + how to prevent func evals at new x bigger than maxdist? + initial secant from two points: 0 and sigma0 = mindist +------------------------------------------------------------------------- */ + +int MinCG::linemin_secant(int n, double *x, double *dir, double eng, + double mindist, double maxdist, + double &alpha, int &nfunc) +{ + int i,iter; + double eta,eta_prev,sigma0,alphadelta,fme,fmax,dsq,e0,tmp; + double *f; + double epssq = SECANT_EPS * SECANT_EPS; + + // stopping criterion for secant iterations + // change this + + fme = 0.0; + for (i = 0; i < n; i++) fme += dir[i]*dir[i]; + MPI_Allreduce(&fme,&dsq,1,MPI_DOUBLE,MPI_SUM,world); + + // sigma0 = smallest allowed step of mindist + // eval func at sigma0 + // test if minstep is already uphill + + fme = 0.0; + for (i = 0; i < n; i++) fme = MAX(fme,fabs(dir[i])); + MPI_Allreduce(&fme,&fmax,1,MPI_DOUBLE,MPI_MAX,world); + if (fmax == 0.0) return 1; + + sigma0 = mindist/fmax; + + e0 = eng; + for (i = 0; i < n; i++) x[i] += sigma0*dir[i]; + eng_force(&n,&x,&dir,&eng); + nfunc++; + + if (eng >= e0) { + for (i = 0; i < n; i++) x[i] -= sigma0*dir[i]; + eng_force(&n,&x,&dir,&eng); + nfunc++; + return 1; + } + + f = atom->f[0]; + tmp = 0.0; + for (i = 0; i < n; i++) tmp -= f[i]*dir[i]; + MPI_Allreduce(&tmp,&eta_prev,1,MPI_DOUBLE,MPI_SUM,world); + + // secant iterations + // alphadelta = new increment to move, alpha = accumulated move + + alpha = sigma0; + alphadelta = -sigma0; + + for (iter = 0; iter < lineiter; iter++) { + alpha += alphadelta; + for (i = 0; i < n; i++) x[i] += alphadelta*dir[i]; + eng_force(&n,&x,&dir,&eng); + nfunc++; + + f = atom->f[0]; + tmp = 0.0; + for (i = 0; i < n; i++) tmp -= f[i]*dir[i]; + MPI_Allreduce(&tmp,&eta,1,MPI_DOUBLE,MPI_SUM,world); + + alphadelta *= eta / (eta_prev - eta); + eta_prev = eta; + if (alphadelta*alphadelta*dsq <= epssq) break; + } + + // if exited loop on first iteration, func eval was at alpha = 0.0 + // else successful line search + + if (iter == 0) return 1; + return 0; +} diff --git a/src/min_cg.h b/src/min_cg.h new file mode 100644 index 0000000000000000000000000000000000000000..5d7e57be4d195c239f1f56ed0f617c8100b8a3f4 --- /dev/null +++ b/src/min_cg.h @@ -0,0 +1,58 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef MIN_CG_H +#define MIN_CG_H + +#include "min.h" + +class MinCG : public Min { + public: + MinCG() {} + virtual ~MinCG() {} + void init(); + void run(); + + virtual void iterate(int); + + protected: + int virial_thermo; // what vflag should be on thermo steps (1,2) + int pairflag,torqueflag,granflag; + int neigh_every,neigh_delay,neigh_dist_check; // copies of reneigh criteria + + int maxpair; // copies of Update quantities + double **f_pair; + + int ifix_minimize; // fix that stores gradient vecs + double ecurrent; // current potential energy + double mindist,maxdist; // min/max dist for any coord delta in line search + + int ndof; // # of degrees-of-freedom on this proc + double *g,*h; // local portion of gradient, searchdir vectors + + typedef int (MinCG::*FnPtr)(int, double *, double *, double, + double, double, double &, int &); + FnPtr linemin; // ptr to linemin functions + + int linemin_scan(int, double *, double *, double, + double, double, double &, int &); + int linemin_secant(int, double *, double *, double, + double, double, double &, int &); + + void setup(); + void setup_vectors(); + void eng_force(int *, double **, double **, double *); + void force_clear(int); +}; + +#endif diff --git a/src/min_cg_fr.cpp b/src/min_cg_fr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5c14525ab3a41768118f29f05952915c98031107 --- /dev/null +++ b/src/min_cg_fr.cpp @@ -0,0 +1,102 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "mpi.h" +#include "min_cg_fr.h" +#include "atom.h" +#include "update.h" +#include "output.h" +#include "timer.h" + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +#define EPS 1.0e-6 + +/* ---------------------------------------------------------------------- + minimization via conjugate gradient iterations + Fletcher-Reeves formulation +------------------------------------------------------------------------- */ + +void MinCGFR::iterate(int n) +{ + int i,gradsearch,fail; + double alpha,beta,gg,dot,dotall; + double *f; + + f = atom->f[0]; + for (int i = 0; i < ndof; i++) h[i] = g[i] = f[i]; + + dot = 0.0; + for (i = 0; i < ndof; i++) dot += f[i]*f[i]; + MPI_Allreduce(&dot,&gg,1,MPI_DOUBLE,MPI_SUM,world); + + neval = 0; + gradsearch = 1; + + for (niter = 0; niter < n; niter++) { + + update->ntimestep++; + + // line minimization along direction h from current atom->x + + eprevious = ecurrent; + fail = (this->*linemin)(ndof,atom->x[0],h,ecurrent,dmin,dmax,alpha,neval); + + // if max_eval exceeded, all done + // if linemin failed or energy did not decrease sufficiently: + // all done if searched in grad direction + // else force next search to be in grad direction (CG restart) + + if (neval >= update->max_eval) break; + + if (fail || fabs(ecurrent-eprevious) <= + update->tolerance * 0.5*(fabs(ecurrent) + fabs(eprevious) + EPS)) { + if (gradsearch == 1) break; + gradsearch = -1; + } + + // update h from new f = -Grad(x) and old g + // old g,h must have migrated with atoms to do this correctly + // done if size sq of grad vector < EPS + // force new search dir to be grad dir if need to restart CG + // set gradsesarch to 1 if will search in grad dir on next iteration + + f = atom->f[0]; + dot = 0.0; + for (i = 0; i < ndof; i++) dot += f[i]*f[i]; + MPI_Allreduce(&dot,&dotall,1,MPI_DOUBLE,MPI_SUM,world); + + beta = dotall/gg; + gg = dotall; + if (gg < EPS) break; + + if (gradsearch == -1) beta = 0.0; + if (beta == 0.0) gradsearch = 1; + else gradsearch = 0; + + for (i = 0; i < ndof; i++) { + g[i] = f[i]; + h[i] = g[i] + beta*h[i]; + } + + // output for thermo, dump, restart files + + if (output->next == update->ntimestep) { + timer->stamp(); + output->write(update->ntimestep); + timer->stamp(TIME_OUTPUT); + } + } +} diff --git a/src/min_cg_fr.h b/src/min_cg_fr.h new file mode 100644 index 0000000000000000000000000000000000000000..09be190f2920813c5ce35dc0d4a90d123a48635a --- /dev/null +++ b/src/min_cg_fr.h @@ -0,0 +1,24 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef MIN_CG_FR_H +#define MIN_CG_FR_H + +#include "min_cg.h" + +class MinCGFR : public MinCG { + public: + void iterate(int); +}; + +#endif diff --git a/src/min_sd.cpp b/src/min_sd.cpp new file mode 100644 index 0000000000000000000000000000000000000000..db7db43e661d9679d0bced74aa0c246493b6cc92 --- /dev/null +++ b/src/min_sd.cpp @@ -0,0 +1,76 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "mpi.h" +#include "min_sd.h" +#include "atom.h" +#include "update.h" +#include "output.h" +#include "timer.h" + +#define EPS 1.0e-6 + +/* ---------------------------------------------------------------------- + minimization via steepest descent +------------------------------------------------------------------------- */ + +void MinSD::iterate(int n) +{ + int i,fail; + double alpha,dot,dotall; + double *f; + + f = atom->f[0]; + for (int i = 0; i < ndof; i++) h[i] = f[i]; + + neval = 0; + + for (niter = 0; niter < n; niter++) { + + update->ntimestep++; + + // line minimization along direction h from current atom->x + + eprevious = ecurrent; + fail = (this->*linemin)(ndof,atom->x[0],h,ecurrent,dmin,dmax,alpha,neval); + + // if max_eval exceeded, all done + // if linemin failed or energy did not decrease sufficiently, all done + + if (neval >= update->max_eval) break; + + if (fail || fabs(ecurrent-eprevious) <= + update->tolerance * 0.5*(fabs(ecurrent) + fabs(eprevious) + EPS)) + break; + + // set h to new f = -Grad(x) + // done if size sq of grad vector < EPS + + f = atom->f[0]; + dot = 0.0; + for (i = 0; i < ndof; i++) dot += f[i]*f[i]; + MPI_Allreduce(&dot,&dotall,1,MPI_DOUBLE,MPI_SUM,world); + if (dotall < EPS) break; + + for (i = 0; i < ndof; i++) h[i] = f[i]; + + // output for thermo, dump, restart files + + if (output->next == update->ntimestep) { + timer->stamp(); + output->write(update->ntimestep); + timer->stamp(TIME_OUTPUT); + } + } +} diff --git a/src/min_sd.h b/src/min_sd.h new file mode 100644 index 0000000000000000000000000000000000000000..84f4fd04c24c5eca42c335e3d11deaaa7a171b33 --- /dev/null +++ b/src/min_sd.h @@ -0,0 +1,24 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef MIN_SD_H +#define MIN_SD_H + +#include "min_cg.h" + +class MinSD : public MinCG { + public: + void iterate(int); +}; + +#endif diff --git a/src/minimize.cpp b/src/minimize.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0af5dc78e290744bb8e2635ee766a33b864cf608 --- /dev/null +++ b/src/minimize.cpp @@ -0,0 +1,47 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "minimize.h" +#include "system.h" +#include "domain.h" +#include "update.h" +#include "min.h" +#include "finish.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +void Minimize::command(int narg, char **arg) +{ + if (narg != 3) error->all("Illegal minimize command"); + + if (domain->box_exist == 0) + error->all("Minimize command before simulation box is defined"); + + update->tolerance = atof(arg[0]); + update->nsteps = atoi(arg[1]); + update->max_eval = atoi(arg[2]); + + update->beginstep = update->firststep = update->ntimestep; + update->endstep = update->laststep = update->firststep + update->nsteps; + + update->whichflag = 1; + + sys->init(); + update->minimize->run(); + + Finish finish; + finish.end(1); + update->whichflag = -1; +} diff --git a/src/minimize.h b/src/minimize.h new file mode 100644 index 0000000000000000000000000000000000000000..e177d95a4acb4b40008cd02664a090e888e709f3 --- /dev/null +++ b/src/minimize.h @@ -0,0 +1,26 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef MINIMIZE_H +#define MINIMIZE_H + +#include "lammps.h" + +class Minimize : public LAMMPS { + public: + Minimize() {} + ~Minimize() {} + void command(int, char **); +}; + +#endif diff --git a/src/modify.cpp b/src/modify.cpp new file mode 100644 index 0000000000000000000000000000000000000000..48b6cbe220e003b446f68be850cca605f402ee4a --- /dev/null +++ b/src/modify.cpp @@ -0,0 +1,678 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "stdio.h" +#include "string.h" +#include "modify.h" +#include "atom.h" +#include "comm.h" +#include "fix.h" +#include "group.h" +#include "update.h" +#include "domain.h" +#include "memory.h" +#include "error.h" + +#define FixInclude +#include "style.h" +#undef FixInclude + +#define DELTA 1 + +// mask settings - same as mask settings in fix.cpp + +#define INITIAL_INTEGRATE 1 +#define PRE_EXCHANGE 2 +#define PRE_NEIGHBOR 4 +#define POST_FORCE 8 +#define FINAL_INTEGRATE 16 +#define END_OF_STEP 32 +#define THERMO 64 +#define INITIAL_INTEGRATE_RESPA 128 +#define POST_FORCE_RESPA 256 +#define FINAL_INTEGRATE_RESPA 512 +#define MIN_POST_FORCE 1024 + +enum {NEITHER,PRINT,ENERGY,BOTH}; // same as thermo.cpp + +/* ---------------------------------------------------------------------- */ + +Modify::Modify() +{ + nfix = maxfix = 0; + n_initial_integrate = 0; + n_pre_exchange = n_pre_neighbor = 0; + n_post_force = n_final_integrate = n_end_of_step = n_thermo = 0; + n_initial_integrate_respa = n_post_force_respa = n_final_integrate_respa = 0; + n_min_post_force = 0; + + fix = NULL; + fmask = NULL; + list_initial_integrate = NULL; + list_pre_exchange = list_pre_neighbor = NULL; + list_post_force = list_final_integrate = list_end_of_step = NULL; + list_thermo = NULL; + list_initial_integrate_respa = list_post_force_respa = NULL; + list_final_integrate_respa = NULL; + list_min_post_force = NULL; + + end_of_step_every = NULL; + + nfix_restart_global = 0; + id_restart_global = style_restart_global = state_restart_global = NULL; + nfix_restart_peratom = 0; + id_restart_peratom = style_restart_peratom = NULL; + index_restart_peratom = NULL; +} + +/* ---------------------------------------------------------------------- */ + +Modify::~Modify() +{ + // delete all fixes + // don't invoke atom->update_callback() since atom has already been deleted + + for (int i = 0; i < nfix; i++) delete fix[i]; + memory->sfree(fix); + memory->sfree(fmask); + + delete [] list_initial_integrate; + delete [] list_pre_exchange; + delete [] list_pre_neighbor; + delete [] list_post_force; + delete [] list_final_integrate; + delete [] list_end_of_step; + delete [] list_thermo; + delete [] list_initial_integrate_respa; + delete [] list_post_force_respa; + delete [] list_final_integrate_respa; + delete [] list_min_post_force; + + delete [] end_of_step_every; + + restart_deallocate(); +} + +/* ---------------------------------------------------------------------- + initialize all fixes and lists of fixes +------------------------------------------------------------------------- */ + +void Modify::init() +{ + int i; + + // delete storage of restart info since it is not valid after 1st run + + restart_deallocate(); + + // init each fix + + comm->maxforward_fix = comm->maxreverse_fix = 0; + for (i = 0; i < nfix; i++) fix[i]->init(); + + // create lists of fixes to call at each stage of run + + list_init(INITIAL_INTEGRATE,n_initial_integrate,list_initial_integrate); + list_init(PRE_EXCHANGE,n_pre_exchange,list_pre_exchange); + list_init(PRE_NEIGHBOR,n_pre_neighbor,list_pre_neighbor); + list_init(POST_FORCE,n_post_force,list_post_force); + list_init(FINAL_INTEGRATE,n_final_integrate,list_final_integrate); + list_init_end_of_step(END_OF_STEP,n_end_of_step,list_end_of_step); + list_init_thermo(THERMO,n_thermo,list_thermo); + + list_init(INITIAL_INTEGRATE_RESPA, + n_initial_integrate_respa,list_initial_integrate_respa); + list_init(POST_FORCE_RESPA, + n_post_force_respa,list_post_force_respa); + list_init(FINAL_INTEGRATE_RESPA, + n_final_integrate_respa,list_final_integrate_respa); + + list_init(MIN_POST_FORCE,n_min_post_force,list_min_post_force); +} + +/* ---------------------------------------------------------------------- + setup for run, calls setup() of all fixes +------------------------------------------------------------------------- */ + +void Modify::setup() +{ + if (update->whichflag == 0) + for (int i = 0; i < nfix; i++) fix[i]->setup(); + else + for (int i = 0; i < nfix; i++) fix[i]->min_setup(); +} + +/* ---------------------------------------------------------------------- + 1st half of integrate call only for relevant fixes +------------------------------------------------------------------------- */ + +void Modify::initial_integrate() +{ + for (int i = 0; i < n_initial_integrate; i++) + fix[list_initial_integrate[i]]->initial_integrate(); +} + +/* ---------------------------------------------------------------------- + pre_exchange call only for relevant fixes +------------------------------------------------------------------------- */ + +void Modify::pre_exchange() +{ + for (int i = 0; i < n_pre_exchange; i++) + fix[list_pre_exchange[i]]->pre_exchange(); +} + +/* ---------------------------------------------------------------------- + pre_neighbor call only for relevant fixes +------------------------------------------------------------------------- */ + +void Modify::pre_neighbor() +{ + for (int i = 0; i < n_pre_neighbor; i++) + fix[list_pre_neighbor[i]]->pre_neighbor(); +} + +/* ---------------------------------------------------------------------- + force adjustment call only for relevant fixes +------------------------------------------------------------------------- */ + +void Modify::post_force(int vflag) +{ + for (int i = 0; i < n_post_force; i++) + fix[list_post_force[i]]->post_force(vflag); +} + +/* ---------------------------------------------------------------------- + 2nd half of integrate call only for relevant fixes +------------------------------------------------------------------------- */ + +void Modify::final_integrate() +{ + for (int i = 0; i < n_final_integrate; i++) + fix[list_final_integrate[i]]->final_integrate(); +} + +/* ---------------------------------------------------------------------- + end-of-timestep call only for relevant fixes + only call fix->end_of_step() on timesteps that are multiples of nevery +------------------------------------------------------------------------- */ + +void Modify::end_of_step() +{ + for (int i = 0; i < n_end_of_step; i++) + if (update->ntimestep % end_of_step_every[i] == 0) + fix[list_end_of_step[i]]->end_of_step(); +} + +/* ---------------------------------------------------------------------- + thermo_fields call only for relevant fixes + called with n = 0, just query how many fields each fix returns + called with n > 0, get the field names +------------------------------------------------------------------------- */ + +int Modify::thermo_fields(int n, int *flags, char **keywords) +{ + int i,j,nfirst,flag_print,flag_energy; + + int m = 0; + if (n == 0) + for (i = 0; i < n_thermo; i++) + m += fix[list_thermo[i]]->thermo_fields(0,NULL,NULL); + else + for (i = 0; i < n_thermo; i++) { + nfirst = m; + m += fix[list_thermo[i]]->thermo_fields(n,&flags[m],&keywords[m]); + for (j = nfirst; j < m; j++) { + if (fix[list_thermo[i]]->thermo_print && + (flags[j] == PRINT || flags[j] == BOTH)) flag_print = PRINT; + else flag_print = NEITHER; + if (fix[list_thermo[i]]->thermo_energy && + (flags[j] == ENERGY || flags[j] == BOTH)) flag_energy = ENERGY; + else flag_energy = NEITHER; + flags[j] = flag_print + flag_energy; + } + } + return m; +} + +/* ---------------------------------------------------------------------- + thermo_compute call only for relevant fixes +------------------------------------------------------------------------- */ + +void Modify::thermo_compute(double *values) +{ + int m = 0; + for (int i = 0; i < n_thermo; i++) + m += fix[list_thermo[i]]->thermo_compute(&values[m]); +} + +/* ---------------------------------------------------------------------- + 1st half of rRESPA integrate call only for relevant fixes +------------------------------------------------------------------------- */ + +void Modify::initial_integrate_respa(int ilevel, int flag) +{ + for (int i = 0; i < n_initial_integrate_respa; i++) + fix[list_initial_integrate_respa[i]]->initial_integrate_respa(ilevel,flag); +} + +/* ---------------------------------------------------------------------- + rRESPA force adjustment call only for relevant fixes +------------------------------------------------------------------------- */ + +void Modify::post_force_respa(int vflag, int ilevel, int iloop) +{ + for (int i = 0; i < n_post_force_respa; i++) + fix[list_post_force_respa[i]]->post_force_respa(vflag,ilevel,iloop); +} + +/* ---------------------------------------------------------------------- + 2nd half of rRESPA integrate call only for relevant fixes +------------------------------------------------------------------------- */ + +void Modify::final_integrate_respa(int ilevel) +{ + for (int i = 0; i < n_final_integrate_respa; i++) + fix[list_final_integrate_respa[i]]->final_integrate_respa(ilevel); +} + +/* ---------------------------------------------------------------------- + minimizer force adjustment call only for relevant fixes +------------------------------------------------------------------------- */ + +void Modify::min_post_force(int vflag) +{ + for (int i = 0; i < n_min_post_force; i++) + fix[list_min_post_force[i]]->min_post_force(vflag); +} + +/* ---------------------------------------------------------------------- + add a new fix or replace one with same ID +------------------------------------------------------------------------- */ + +void Modify::add_fix(int narg, char **arg) +{ + if (domain->box_exist == 0) + error->all("Fix command before simulation box is defined"); + if (narg < 3) error->all("Illegal fix command"); + + // find group ID + + int igroup = group->find(arg[1]); + if (igroup == -1) error->all("Could not find fix group ID"); + + // if fix ID exists: + // set newflag = 0 so create new fix in same location in fix list + // error if new style does not match old style + // since can't replace it (all when-to-invoke ptrs would be invalid) + // warn if new group != old group + // delete old fix + // set ptr to NULL in case new fix scans list of fixes + // if fix ID does not exist: + // set newflag = 1 so create new fix + // extend fix and fmask lists as necessary + + int ifix,newflag; + for (ifix = 0; ifix < nfix; ifix++) + if (strcmp(arg[0],fix[ifix]->id) == 0) break; + + if (ifix < nfix) { + newflag = 0; + if (strcmp(arg[2],fix[ifix]->style) != 0) + error->all("Replacing a fix, but new style != old style"); + if (fix[ifix]->igroup != igroup && comm->me == 0) + error->warning("Replacing a fix, but new group != old group"); + delete fix[ifix]; + atom->update_callback(ifix); + fix[ifix] = NULL; + } else { + newflag = 1; + if (nfix == maxfix) { + maxfix += DELTA; + fix = (Fix **) memory->srealloc(fix,maxfix*sizeof(Fix *),"modify:fix"); + fmask = (int *) + memory->srealloc(fmask,maxfix*sizeof(int),"modify:fmask"); + } + } + + // create the Fix + + if (0) return; // dummy line to enable else-if macro expansion + +#define FixClass +#define FixStyle(key,Class) \ + else if (strcmp(arg[2],#key) == 0) fix[ifix] = new Class(narg,arg); +#include "style.h" +#undef FixClass + + else error->all("Invalid fix style"); + + // if fix is new, set it's mask values and increment nfix + + if (newflag) { + fmask[ifix] = fix[ifix]->setmask(); + nfix++; + } + + // check if Fix is in restart_global list + // if yes, pass state info to the Fix so it can reset itself + + for (int i = 0; i < nfix_restart_global; i++) + if (strcmp(id_restart_global[i],fix[ifix]->id) == 0 && + strcmp(style_restart_global[i],fix[ifix]->style) == 0) { + fix[ifix]->restart(state_restart_global[i]); + if (comm->me == 0) { + char *str = "Resetting global state of Fix %s Style %s " + "from restart file info\n"; + if (screen) fprintf(screen,str,fix[ifix]->id,fix[ifix]->style); + if (logfile) fprintf(logfile,str,fix[ifix]->id,fix[ifix]->style); + } + } + + // check if Fix is in restart_peratom list + // if yes, loop over atoms so they can extract info from atom->extra array + + for (int i = 0; i < nfix_restart_peratom; i++) + if (strcmp(id_restart_peratom[i],fix[ifix]->id) == 0 && + strcmp(style_restart_peratom[i],fix[ifix]->style) == 0) { + for (int j = 0; j < atom->nlocal; j++) + fix[ifix]->unpack_restart(j,index_restart_peratom[i]); + if (comm->me == 0) { + char *str = "Resetting per-atom state of Fix %s Style %s " + "from restart file info\n"; + if (screen) fprintf(screen,str,fix[ifix]->id,fix[ifix]->style); + if (logfile) fprintf(logfile,str,fix[ifix]->id,fix[ifix]->style); + } + } +} + +/* ---------------------------------------------------------------------- + modify a Fix's parameters +------------------------------------------------------------------------- */ + +void Modify::modify_fix(int narg, char **arg) +{ + if (narg < 2) error->all("Illegal fix_modify command"); + + // lookup Fix ID + + int ifix; + for (ifix = 0; ifix < nfix; ifix++) + if (strcmp(arg[0],fix[ifix]->id) == 0) break; + if (ifix == nfix) error->all("Could not find fix_modify ID"); + + fix[ifix]->modify_params(narg-1,&arg[1]); +} + +/* ---------------------------------------------------------------------- + delete a Fix from list of Fixes +------------------------------------------------------------------------- */ + +void Modify::delete_fix(char *id) +{ + int ifix; + + // find which fix it is and delete it + + for (ifix = 0; ifix < nfix; ifix++) + if (strcmp(id,fix[ifix]->id) == 0) break; + if (ifix == nfix) error->all("Could not find unfix ID"); + + delete fix[ifix]; + if (atom) atom->update_callback(ifix); + + // move other Fixes and fmask down in list one slot + + for (int i = ifix+1; i < nfix; i++) fix[i-1] = fix[i]; + for (int i = ifix+1; i < nfix; i++) fmask[i-1] = fmask[i]; + nfix--; +} + +/* ---------------------------------------------------------------------- + write to restart file for all Fixes with restart info + (1) fixes that have global state + (2) fixes that store per-atom quantities +------------------------------------------------------------------------- */ + +void Modify::write_restart(FILE *fp) +{ + int me = comm->me; + + int count = 0; + for (int i = 0; i < nfix; i++) + if (fix[i]->restart_global) count++; + + if (me == 0) fwrite(&count,sizeof(int),1,fp); + + int n; + for (int i = 0; i < nfix; i++) + if (fix[i]->restart_global) { + if (me == 0) { + n = strlen(fix[i]->id) + 1; + fwrite(&n,sizeof(int),1,fp); + fwrite(fix[i]->id,sizeof(char),n,fp); + n = strlen(fix[i]->style) + 1; + fwrite(&n,sizeof(int),1,fp); + fwrite(fix[i]->style,sizeof(char),n,fp); + } + fix[i]->write_restart(fp); + } + + count = 0; + for (int i = 0; i < nfix; i++) + if (fix[i]->restart_peratom) count++; + + if (me == 0) fwrite(&count,sizeof(int),1,fp); + + for (int i = 0; i < nfix; i++) + if (fix[i]->restart_peratom) { + if (me == 0) { + n = strlen(fix[i]->id) + 1; + fwrite(&n,sizeof(int),1,fp); + fwrite(fix[i]->id,sizeof(char),n,fp); + n = strlen(fix[i]->style) + 1; + fwrite(&n,sizeof(int),1,fp); + fwrite(fix[i]->style,sizeof(char),n,fp); + n = fix[i]->maxsize_restart(); + fwrite(&n,sizeof(int),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + read in restart file data on all previously defined Fixes with restart info + (1) fixes that have global state + (2) fixes that store per-atom quantities + return maxsize of extra info that will be stored with any atom +------------------------------------------------------------------------- */ + +int Modify::read_restart(FILE *fp) +{ + // nfix_restart_global = # of restart entries with global state info + + int me = comm->me; + if (me == 0) fread(&nfix_restart_global,sizeof(int),1,fp); + MPI_Bcast(&nfix_restart_global,1,MPI_INT,0,world); + + // allocate space for each entry + + if (nfix_restart_global) { + id_restart_global = new char*[nfix_restart_global]; + style_restart_global = new char*[nfix_restart_global]; + state_restart_global = new char*[nfix_restart_global]; + } + + // read each entry and Bcast to all procs + // each entry has id string, style string, chunk of state data + + int n; + for (int i = 0; i < nfix_restart_global; i++) { + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + id_restart_global[i] = new char[n]; + if (me == 0) fread(id_restart_global[i],sizeof(char),n,fp); + MPI_Bcast(id_restart_global[i],n,MPI_CHAR,0,world); + + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + style_restart_global[i] = new char[n]; + if (me == 0) fread(style_restart_global[i],sizeof(char),n,fp); + MPI_Bcast(style_restart_global[i],n,MPI_CHAR,0,world); + + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + state_restart_global[i] = new char[n]; + if (me == 0) fread(state_restart_global[i],sizeof(char),n,fp); + MPI_Bcast(state_restart_global[i],n,MPI_CHAR,0,world); + } + + // nfix_restart_peratom = # of restart entries with peratom info + + int maxsize = 0; + + if (me == 0) fread(&nfix_restart_peratom,sizeof(int),1,fp); + MPI_Bcast(&nfix_restart_peratom,1,MPI_INT,0,world); + + // allocate space for each entry + + if (nfix_restart_peratom) { + id_restart_peratom = new char*[nfix_restart_peratom]; + style_restart_peratom = new char*[nfix_restart_peratom]; + index_restart_peratom = new int[nfix_restart_peratom]; + } + + // read each entry and Bcast to all procs + // each entry has id string, style string, maxsize of one atom's data + // set index = which set of extra data this fix represents + + for (int i = 0; i < nfix_restart_peratom; i++) { + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + id_restart_peratom[i] = new char[n]; + if (me == 0) fread(id_restart_peratom[i],sizeof(char),n,fp); + MPI_Bcast(id_restart_peratom[i],n,MPI_CHAR,0,world); + + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + style_restart_peratom[i] = new char[n]; + if (me == 0) fread(style_restart_peratom[i],sizeof(char),n,fp); + MPI_Bcast(style_restart_peratom[i],n,MPI_CHAR,0,world); + + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + maxsize += n; + + index_restart_peratom[i] = i; + } + + return maxsize; +} + +/* ---------------------------------------------------------------------- + delete all lists of restart file Fix info +------------------------------------------------------------------------- */ + +void Modify::restart_deallocate() +{ + if (nfix_restart_global) { + for (int i = 0; i < nfix_restart_global; i++) { + delete [] id_restart_global[i]; + delete [] style_restart_global[i]; + delete [] state_restart_global[i]; + } + delete [] id_restart_global; + delete [] style_restart_global; + delete [] state_restart_global; + } + + if (nfix_restart_peratom) { + for (int i = 0; i < nfix_restart_peratom; i++) { + delete [] id_restart_peratom[i]; + delete [] style_restart_peratom[i]; + } + delete [] id_restart_peratom; + delete [] style_restart_peratom; + delete [] index_restart_peratom; + } + + nfix_restart_global = nfix_restart_peratom = 0; +} + +/* ---------------------------------------------------------------------- + create list of fix indices for fixes which match mask +------------------------------------------------------------------------- */ + +void Modify::list_init(int mask, int &n, int *&list) +{ + delete [] list; + + n = 0; + for (int i = 0; i < nfix; i++) if (fmask[i] & mask) n++; + list = new int[n]; + + n = 0; + for (int i = 0; i < nfix; i++) if (fmask[i] & mask) list[n++] = i; +} + +/* ---------------------------------------------------------------------- + create list of fix indices for end_of_step fixes + also create end_of_step_every[] +------------------------------------------------------------------------- */ + +void Modify::list_init_end_of_step(int mask, int &n, int *&list) +{ + delete [] list; + delete [] end_of_step_every; + + n = 0; + for (int i = 0; i < nfix; i++) if (fmask[i] & mask) n++; + list = new int[n]; + end_of_step_every = new int[n]; + + n = 0; + for (int i = 0; i < nfix; i++) + if (fmask[i] & mask) { + list[n] = i; + end_of_step_every[n++] = fix[i]->nevery; + } +} + +/* ---------------------------------------------------------------------- + create list of fix indices for thermo fixes + also must have thermo print/energy flag set via fix_modify +------------------------------------------------------------------------- */ + +void Modify::list_init_thermo(int mask, int &n, int *&list) +{ + delete [] list; + n = 0; + for (int i = 0; i < nfix; i++) + if (fmask[i] & mask && (fix[i]->thermo_print || fix[i]->thermo_energy)) + n++; + list = new int[n]; + n = 0; + for (int i = 0; i < nfix; i++) + if (fmask[i] & mask && (fix[i]->thermo_print || fix[i]->thermo_energy)) + list[n++] = i; +} + +/* ---------------------------------------------------------------------- + return # of bytes of allocated memory from all fixes +------------------------------------------------------------------------- */ + +int Modify::memory_usage() +{ + int bytes = 0; + for (int i = 0; i < nfix; i++) bytes += fix[i]->memory_usage(); + return bytes; +} diff --git a/src/modify.h b/src/modify.h new file mode 100644 index 0000000000000000000000000000000000000000..612998ff2ef3d9963e26b245c2b757e41f89c6e5 --- /dev/null +++ b/src/modify.h @@ -0,0 +1,90 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef MODIFY_H +#define MODIFY_H + +#include "stdio.h" +#include "lammps.h" + +class Fix; + +class Modify : public LAMMPS { + public: + int nfix; + int maxfix; + int n_initial_integrate,n_pre_decide,n_pre_exchange,n_pre_neighbor; + int n_post_force,n_final_integrate,n_end_of_step,n_thermo; + int n_initial_integrate_respa,n_post_force_respa,n_final_integrate_respa; + int n_min_post_force; + int nfix_restart_peratom; + + Fix **fix; // list of fixes + int *fmask; // bit mask for when each fix is applied + + Modify(); + ~Modify(); + void init(); + void setup(); + void initial_integrate(); + void pre_decide(); + void pre_exchange(); + void pre_neighbor(); + void post_force(int); + void final_integrate(); + void end_of_step(); + int thermo_fields(int, int *, char **); + void thermo_compute(double *); + + void initial_integrate_respa(int,int); + void post_force_respa(int,int,int); + void final_integrate_respa(int); + + void min_post_force(int); + + void add_fix(int, char **); + void modify_fix(int, char **); + void delete_fix(char *); + + void write_restart(FILE *); + int read_restart(FILE *); + void restart_deallocate(); + + int memory_usage(); + + private: + // lists of fixes to apply at different times + int *list_initial_integrate,*list_pre_decide; + int *list_pre_exchange,*list_pre_neighbor; + int *list_post_force,*list_final_integrate,*list_end_of_step,*list_thermo; + int *list_initial_integrate_respa,*list_post_force_respa; + int *list_final_integrate_respa; + int *list_min_post_force; + + int *end_of_step_every; + + int nfix_restart_global; + char **id_restart_global; + char **style_restart_global; + char **state_restart_global; + + char **id_restart_peratom; + char **style_restart_peratom; + int *index_restart_peratom; + + void list_init(int, int &, int *&); + void list_init_end_of_step(int, int &, int *&); + void list_init_thermo(int, int &, int *&); +}; + +#endif diff --git a/src/neigh_bond.cpp b/src/neigh_bond.cpp new file mode 100644 index 0000000000000000000000000000000000000000..71704476aed631278a070bdf3167ad7d578d59e4 --- /dev/null +++ b/src/neigh_bond.cpp @@ -0,0 +1,383 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "neighbor.h" +#include "atom.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "error.h" + +#define BONDDELTA 10000 + +/* ---------------------------------------------------------------------- */ + +void Neighbor::bond_all() +{ + int i,m,atom1; + + int nlocal = atom->nlocal; + int *num_bond = atom->num_bond; + int **bond_atom = atom->bond_atom; + int **bond_type = atom->bond_type; + int *tag = atom->tag; + int newton_bond = force->newton_bond; + + nbondlist = 0; + + for (i = 0; i < nlocal; i++) + for (m = 0; m < num_bond[i]; m++) { + atom1 = atom->map(bond_atom[i][m]); + if (atom1 == -1) { + char str[128]; + sprintf(str,"Bond atoms %d %d missing on proc %d at step %d", + tag[i],bond_atom[i][m],me,update->ntimestep); + error->one(str); + } + if (newton_bond || i < atom1) { + if (nbondlist == maxbond) { + maxbond += BONDDELTA; + bondlist = memory->grow_2d_int_array(bondlist,maxbond,3, + "neighbor:bondlist"); + } + bondlist[nbondlist][0] = i; + bondlist[nbondlist][1] = atom1; + bondlist[nbondlist][2] = bond_type[i][m]; + nbondlist++; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void Neighbor::bond_partial() +{ + int i,m,atom1; + + int nlocal = atom->nlocal; + int *num_bond = atom->num_bond; + int **bond_atom = atom->bond_atom; + int **bond_type = atom->bond_type; + int *tag = atom->tag; + int newton_bond = force->newton_bond; + + nbondlist = 0; + + for (i = 0; i < nlocal; i++) + for (m = 0; m < num_bond[i]; m++) { + if (bond_type[i][m] <= 0) continue; + atom1 = atom->map(bond_atom[i][m]); + if (atom1 == -1) { + char str[128]; + sprintf(str,"Bond atoms %d %d missing on proc %d at step %d", + tag[i],bond_atom[i][m],me,update->ntimestep); + error->one(str); + } + if (newton_bond || i < atom1) { + if (nbondlist == maxbond) { + maxbond += BONDDELTA; + bondlist = memory->grow_2d_int_array(bondlist,maxbond,3, + "neighbor:bondlist"); + } + bondlist[nbondlist][0] = i; + bondlist[nbondlist][1] = atom1; + bondlist[nbondlist][2] = bond_type[i][m]; + nbondlist++; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void Neighbor::angle_all() +{ + int i,m,atom1,atom2,atom3; + + int nlocal = atom->nlocal; + 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 newton_bond = force->newton_bond; + + nanglelist = 0; + + for (i = 0; i < nlocal; i++) + for (m = 0; m < num_angle[i]; m++) { + atom1 = atom->map(angle_atom1[i][m]); + atom2 = atom->map(angle_atom2[i][m]); + atom3 = atom->map(angle_atom3[i][m]); + if (atom1 == -1 || atom2 == -1 || atom3 == -1) { + char str[128]; + sprintf(str,"Angle atoms %d %d %d missing on proc %d at step %d", + angle_atom1[i][m],angle_atom2[i][m],angle_atom3[i][m], + me,update->ntimestep); + error->one(str); + } + if (newton_bond || (i <= atom1 && i <= atom2 && i <= atom3)) { + if (nanglelist == maxangle) { + maxangle += BONDDELTA; + anglelist = memory->grow_2d_int_array(anglelist,maxangle,4, + "neighbor:anglelist"); + } + anglelist[nanglelist][0] = atom1; + anglelist[nanglelist][1] = atom2; + anglelist[nanglelist][2] = atom3; + anglelist[nanglelist][3] = angle_type[i][m]; + nanglelist++; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void Neighbor::angle_partial() +{ + int i,m,atom1,atom2,atom3; + + int nlocal = atom->nlocal; + 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 newton_bond = force->newton_bond; + + nanglelist = 0; + + for (i = 0; i < nlocal; i++) + for (m = 0; m < num_angle[i]; m++) { + if (angle_type[i][m] <= 0) continue; + atom1 = atom->map(angle_atom1[i][m]); + atom2 = atom->map(angle_atom2[i][m]); + atom3 = atom->map(angle_atom3[i][m]); + if (atom1 == -1 || atom2 == -1 || atom3 == -1) { + char str[128]; + sprintf(str,"Angle atoms %d %d %d missing on proc %d at step %d", + angle_atom1[i][m],angle_atom2[i][m],angle_atom3[i][m], + me,update->ntimestep); + error->one(str); + } + if (newton_bond || (i <= atom1 && i <= atom2 && i <= atom3)) { + if (nanglelist == maxangle) { + maxangle += BONDDELTA; + anglelist = memory->grow_2d_int_array(anglelist,maxangle,4, + "neighbor:anglelist"); + } + anglelist[nanglelist][0] = atom1; + anglelist[nanglelist][1] = atom2; + anglelist[nanglelist][2] = atom3; + anglelist[nanglelist][3] = angle_type[i][m]; + nanglelist++; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void Neighbor::dihedral_all() +{ + int i,m,atom1,atom2,atom3,atom4; + + int nlocal = atom->nlocal; + int *num_dihedral = atom->num_dihedral; + int **dihedral_atom1 = atom->dihedral_atom1; + int **dihedral_atom2 = atom->dihedral_atom2; + int **dihedral_atom3 = atom->dihedral_atom3; + int **dihedral_atom4 = atom->dihedral_atom4; + int **dihedral_type = atom->dihedral_type; + int newton_bond = force->newton_bond; + + ndihedrallist = 0; + + for (i = 0; i < nlocal; i++) + for (m = 0; m < num_dihedral[i]; m++) { + atom1 = atom->map(dihedral_atom1[i][m]); + atom2 = atom->map(dihedral_atom2[i][m]); + atom3 = atom->map(dihedral_atom3[i][m]); + atom4 = atom->map(dihedral_atom4[i][m]); + if (atom1 == -1 || atom2 == -1 || atom3 == -1 || atom4 == -1) { + char str[128]; + sprintf(str,"Dihedral atoms %d %d %d %d missing on proc %d at step %d", + dihedral_atom1[i][m],dihedral_atom2[i][m], + dihedral_atom3[i][m],dihedral_atom4[i][m], + me,update->ntimestep); + error->one(str); + } + if (newton_bond || + (i <= atom1 && i <= atom2 && i <= atom3 && i <= atom4)) { + if (ndihedrallist == maxdihedral) { + maxdihedral += BONDDELTA; + dihedrallist = + memory->grow_2d_int_array(dihedrallist,maxdihedral,5, + "neighbor:dihedrallist"); + } + dihedrallist[ndihedrallist][0] = atom1; + dihedrallist[ndihedrallist][1] = atom2; + dihedrallist[ndihedrallist][2] = atom3; + dihedrallist[ndihedrallist][3] = atom4; + dihedrallist[ndihedrallist][4] = dihedral_type[i][m]; + ndihedrallist++; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void Neighbor::dihedral_partial() +{ + int i,m,atom1,atom2,atom3,atom4; + + int nlocal = atom->nlocal; + int *num_dihedral = atom->num_dihedral; + int **dihedral_atom1 = atom->dihedral_atom1; + int **dihedral_atom2 = atom->dihedral_atom2; + int **dihedral_atom3 = atom->dihedral_atom3; + int **dihedral_atom4 = atom->dihedral_atom4; + int **dihedral_type = atom->dihedral_type; + int newton_bond = force->newton_bond; + + ndihedrallist = 0; + + for (i = 0; i < nlocal; i++) + for (m = 0; m < num_dihedral[i]; m++) { + if (dihedral_type[i][m] <= 0) continue; + atom1 = atom->map(dihedral_atom1[i][m]); + atom2 = atom->map(dihedral_atom2[i][m]); + atom3 = atom->map(dihedral_atom3[i][m]); + atom4 = atom->map(dihedral_atom4[i][m]); + if (atom1 == -1 || atom2 == -1 || atom3 == -1 || atom4 == -1) { + char str[128]; + sprintf(str,"Dihedral atoms %d %d %d %d missing on proc %d at step %d", + dihedral_atom1[i][m],dihedral_atom2[i][m], + dihedral_atom3[i][m],dihedral_atom4[i][m], + me,update->ntimestep); + error->one(str); + } + if (newton_bond || + (i <= atom1 && i <= atom2 && i <= atom3 && i <= atom4)) { + if (ndihedrallist == maxdihedral) { + maxdihedral += BONDDELTA; + dihedrallist = + memory->grow_2d_int_array(dihedrallist,maxdihedral,5, + "neighbor:dihedrallist"); + } + dihedrallist[ndihedrallist][0] = atom1; + dihedrallist[ndihedrallist][1] = atom2; + dihedrallist[ndihedrallist][2] = atom3; + dihedrallist[ndihedrallist][3] = atom4; + dihedrallist[ndihedrallist][4] = dihedral_type[i][m]; + ndihedrallist++; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void Neighbor::improper_all() +{ + int i,m,atom1,atom2,atom3,atom4; + + int nlocal = atom->nlocal; + int *num_improper = atom->num_improper; + int **improper_atom1 = atom->improper_atom1; + int **improper_atom2 = atom->improper_atom2; + int **improper_atom3 = atom->improper_atom3; + int **improper_atom4 = atom->improper_atom4; + int **improper_type = atom->improper_type; + int newton_bond = force->newton_bond; + + nimproperlist = 0; + + for (i = 0; i < nlocal; i++) + for (m = 0; m < num_improper[i]; m++) { + atom1 = atom->map(improper_atom1[i][m]); + atom2 = atom->map(improper_atom2[i][m]); + atom3 = atom->map(improper_atom3[i][m]); + atom4 = atom->map(improper_atom4[i][m]); + if (atom1 == -1 || atom2 == -1 || atom3 == -1 || atom4 == -1) { + char str[128]; + sprintf(str,"Improper atoms %d %d %d %d missing on proc %d at step %d", + improper_atom1[i][m],improper_atom2[i][m], + improper_atom3[i][m],improper_atom4[i][m], + me,update->ntimestep); + error->one(str); + } + if (newton_bond || + (i <= atom1 && i <= atom2 && i <= atom3 && i <= atom4)) { + if (nimproperlist == maximproper) { + maximproper += BONDDELTA; + improperlist = + memory->grow_2d_int_array(improperlist,maximproper,5, + "neighbor:improperlist"); + } + improperlist[nimproperlist][0] = atom1; + improperlist[nimproperlist][1] = atom2; + improperlist[nimproperlist][2] = atom3; + improperlist[nimproperlist][3] = atom4; + improperlist[nimproperlist][4] = improper_type[i][m]; + nimproperlist++; + } + } +} + +/* ---------------------------------------------------------------------- */ + +void Neighbor::improper_partial() +{ + int i,m,atom1,atom2,atom3,atom4; + + int nlocal = atom->nlocal; + int *num_improper = atom->num_improper; + int **improper_atom1 = atom->improper_atom1; + int **improper_atom2 = atom->improper_atom2; + int **improper_atom3 = atom->improper_atom3; + int **improper_atom4 = atom->improper_atom4; + int **improper_type = atom->improper_type; + int newton_bond = force->newton_bond; + + nimproperlist = 0; + + for (i = 0; i < nlocal; i++) + for (m = 0; m < num_improper[i]; m++) { + if (improper_type[i][m] <= 0) continue; + atom1 = atom->map(improper_atom1[i][m]); + atom2 = atom->map(improper_atom2[i][m]); + atom3 = atom->map(improper_atom3[i][m]); + atom4 = atom->map(improper_atom4[i][m]); + if (atom1 == -1 || atom2 == -1 || atom3 == -1 || atom4 == -1) { + char str[128]; + sprintf(str,"Improper atoms %d %d %d %d missing on proc %d at step %d", + improper_atom1[i][m],improper_atom2[i][m], + improper_atom3[i][m],improper_atom4[i][m], + me,update->ntimestep); + error->one(str); + } + if (newton_bond || + (i <= atom1 && i <= atom2 && i <= atom3 && i <= atom4)) { + if (nimproperlist == maximproper) { + maximproper += BONDDELTA; + improperlist = + memory->grow_2d_int_array(improperlist,maximproper,5, + "neighbor:improperlist"); + } + improperlist[nimproperlist][0] = atom1; + improperlist[nimproperlist][1] = atom2; + improperlist[nimproperlist][2] = atom3; + improperlist[nimproperlist][3] = atom4; + improperlist[nimproperlist][4] = improper_type[i][m]; + nimproperlist++; + } + } +} diff --git a/src/neigh_full.cpp b/src/neigh_full.cpp new file mode 100644 index 0000000000000000000000000000000000000000..762e2366522929ba5922da6dd4562e7e0ac9d6dc --- /dev/null +++ b/src/neigh_full.cpp @@ -0,0 +1,166 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "neighbor.h" +#include "atom.h" +#include "error.h" + +/* ---------------------------------------------------------------------- + N^2 search for all neighbors + every neighbor pair appears in list of both atoms i and j +------------------------------------------------------------------------- */ + +void Neighbor::full_nsq() +{ + int i,j,n,itype,jtype,which; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + int *neighptr; + + double **x = atom->x; + int *type = atom->type; + int *mask = atom->mask; + int *molecule = atom->molecule; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + int molecular = atom->molecular; + + int npage = 0; + int npnt = 0; + + for (i = 0; i < nlocal; i++) { + + if (pgsize - npnt < oneatom) { + npnt = 0; + npage++; + if (npage == maxpage_full) add_pages_full(npage); + } + + neighptr = &pages_full[npage][npnt]; + n = 0; + + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + + // loop over all atoms, owned and ghost, only skip i = j + + for (j = 0; j < nall; j++) { + if (i == j) continue; + if (exclude && exclusion(i,j,type,mask,molecule)) continue; + + jtype = type[j]; + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular) which = find_special(i,j); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (which > 0) neighptr[n++] = which*nall + j; + } + } + + firstneigh_full[i] = neighptr; + numneigh_full[i] = n; + npnt += n; + if (npnt >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + } +} + +/* ---------------------------------------------------------------------- + binned search for all neighbors + every neighbor pair appears in list of both atoms i and j +------------------------------------------------------------------------- */ + +void Neighbor::full_bin() +{ + int i,j,k,n,itype,jtype,ibin,which; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + int *neighptr; + + // bin local & ghost atoms + + bin_atoms(); + + // loop over each atom, storing neighbors + + double **x = atom->x; + int *type = atom->type; + int *mask = atom->mask; + int *molecule = atom->molecule; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + int molecular = atom->molecular; + + int npage = 0; + int npnt = 0; + + for (i = 0; i < nlocal; i++) { + + if (pgsize - npnt < oneatom) { + npnt = 0; + npage++; + if (npage == maxpage_full) add_pages_full(npage); + } + + neighptr = &pages_full[npage][npnt]; + n = 0; + + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + ibin = coord2bin(x[i]); + + // loop over all atoms in surrounding bins in stencil including self + // only skip i = j + + for (k = 0; k < nstencil_full; k++) { + j = binhead[ibin+stencil_full[k]]; + while (j >= 0) { + if (i == j) { + j = bins[j]; + continue; + } + if (exclude && exclusion(i,j,type,mask,molecule)) { + j = bins[j]; + continue; + } + + jtype = type[j]; + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular) which = find_special(i,j); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (which > 0) neighptr[n++] = which*nall + j; + } + + j = bins[j]; + } + } + + firstneigh_full[i] = neighptr; + numneigh_full[i] = n; + npnt += n; + if (npnt >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + } +} diff --git a/src/neigh_gran.cpp b/src/neigh_gran.cpp new file mode 100644 index 0000000000000000000000000000000000000000..19d4d26e7885893e25f0852f5aeb6143f18f58b1 --- /dev/null +++ b/src/neigh_gran.cpp @@ -0,0 +1,465 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "neighbor.h" +#include "atom.h" +#include "modify.h" +#include "fix_shear_history.h" +#include "error.h" + +/* ---------------------------------------------------------------------- + granular particles + N^2 / 2 search for neighbor pairs with partial Newton's 3rd law + shear history must be accounted for when a neighbor pair is added + pair added to list if atoms i and j are both owned and i < j + pair added if j is ghost (also stored by proc owning j) +------------------------------------------------------------------------- */ + +void Neighbor::granular_nsq_no_newton() +{ + int i,j,m,n,nn; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + double radi,radsum,cutsq; + int *neighptr,*touchptr; + double *shearptr; + int *npartner; + int **partner; + double ***shearpartner; + + if (history >= 0) { + npartner = ((FixShearHistory *) modify->fix[history])->npartner; + partner = ((FixShearHistory *) modify->fix[history])->partner; + shearpartner = + ((FixShearHistory *) modify->fix[history])->shearpartner; + } + + double **x = atom->x; + double *radius = atom->radius; + int *tag = atom->tag; + int *type = atom->type; + int *mask = atom->mask; + int *molecule = atom->molecule; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + + int npage = 0; + int npnt = 0; + + for (i = 0; i < nlocal; i++) { + + if (pgsize - npnt < oneatom) { + npnt = 0; + npage++; + if (npage == maxpage) { + add_pages(npage); + if (history >= 0) add_pages_history(npage); + } + } + + n = 0; + neighptr = &pages[npage][npnt]; + if (history >= 0) { + nn = 0; + touchptr = &pages_touch[npage][npnt]; + shearptr = &pages_shear[npage][3*npnt]; + } + + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + radi = radius[i]; + + // loop over remaining atoms, owned and ghost + + for (j = i+1; j < nall; j++) { + if (exclude && exclusion(i,j,type,mask,molecule)) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + radsum = radi + radius[j]; + cutsq = (radsum+skin) * (radsum+skin); + + if (rsq <= cutsq) { + neighptr[n] = j; + + if (history >= 0) { + if (rsq < radsum*radsum) { + for (m = 0; m < npartner[i]; m++) + if (partner[i][m] == tag[j]) break; + if (m < npartner[i]) { + touchptr[n] = 1; + shearptr[nn++] = shearpartner[i][m][0]; + shearptr[nn++] = shearpartner[i][m][1]; + shearptr[nn++] = shearpartner[i][m][2]; + } else { + touchptr[n] = 0; + shearptr[nn++] = 0.0; + shearptr[nn++] = 0.0; + shearptr[nn++] = 0.0; + } + } else { + touchptr[n] = 0; + shearptr[nn++] = 0.0; + shearptr[nn++] = 0.0; + shearptr[nn++] = 0.0; + } + } + + n++; + } + } + + if (history >= 0) { + firsttouch[i] = touchptr; + firstshear[i] = shearptr; + } + firstneigh[i] = neighptr; + numneigh[i] = n; + npnt += n; + if (npnt >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + } +} + +/* ---------------------------------------------------------------------- + granular particles + N^2 / 2 search for neighbor pairs with full Newton's 3rd law + no shear history is allowed for this option + pair added to list if atoms i and j are both owned and i < j + if j is ghost only me or other proc adds pair + decision based on itag,jtag tests +------------------------------------------------------------------------- */ + +void Neighbor::granular_nsq_newton() +{ + int i,j,n,itag,jtag; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + double radi,radsum,cutsq; + int *neighptr; + + double **x = atom->x; + double *radius = atom->radius; + int *tag = atom->tag; + int *type = atom->type; + int *mask = atom->mask; + int *molecule = atom->molecule; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + + int npage = 0; + int npnt = 0; + + for (i = 0; i < nlocal; i++) { + + if (pgsize - npnt < oneatom) { + npnt = 0; + npage++; + if (npage == maxpage) add_pages(npage); + } + + n = 0; + neighptr = &pages[npage][npnt]; + + itag = tag[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + radi = radius[i]; + + // loop over remaining atoms, owned and ghost + + for (j = i+1; j < nall; j++) { + if (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; + else if (x[j][2] == ztmp && x[j][1] < ytmp) continue; + else if (x[j][2] == ztmp && x[j][1] == ytmp && x[j][0] < xtmp) + continue; + } + } + + if (exclude && exclusion(i,j,type,mask,molecule)) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + radsum = radi + radius[j]; + cutsq = (radsum+skin) * (radsum+skin); + + if (rsq <= cutsq) neighptr[n++] = j; + } + + firstneigh[i] = neighptr; + numneigh[i] = n; + npnt += n; + if (npnt >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + } +} + +/* ---------------------------------------------------------------------- + granular particles + binned neighbor list construction with partial Newton's 3rd law + shear history must be accounted for when a neighbor pair is added + each owned atom i checks own bin and surrounding bins in non-Newton stencil + pair stored once if i,j are both owned and i < j + pair stored by me if j is ghost (also stored by proc owning j) +------------------------------------------------------------------------- */ + +void Neighbor::granular_bin_no_newton() +{ + int i,j,k,m,n,nn,ibin; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + double radi,radsum,cutsq; + int *neighptr,*touchptr; + double *shearptr; + int *npartner; + int **partner; + double ***shearpartner; + + if (history >= 0) { + npartner = ((FixShearHistory *) modify->fix[history])->npartner; + partner = ((FixShearHistory *) modify->fix[history])->partner; + shearpartner = + ((FixShearHistory *) modify->fix[history])->shearpartner; + } + + // bin local & ghost atoms + + bin_atoms(); + + // loop over each atom, storing neighbors + + double **x = atom->x; + double *radius = atom->radius; + int *tag = atom->tag; + int *type = atom->type; + int *mask = atom->mask; + int *molecule = atom->molecule; + int nlocal = atom->nlocal; + + int npage = 0; + int npnt = 0; + + for (i = 0; i < nlocal; i++) { + + if (pgsize - npnt < oneatom) { + npnt = 0; + npage++; + if (npage == maxpage) { + add_pages(npage); + if (history >= 0) add_pages_history(npage); + } + } + + n = 0; + neighptr = &pages[npage][npnt]; + if (history >= 0) { + nn = 0; + touchptr = &pages_touch[npage][npnt]; + shearptr = &pages_shear[npage][3*npnt]; + } + + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + radi = radius[i]; + ibin = coord2bin(x[i]); + + // loop over all atoms in surrounding bins in stencil including self + // only store pair if i < j + // stores own/own pairs only once + // stores own/ghost pairs on both procs + + for (k = 0; k < nstencil; k++) { + j = binhead[ibin+stencil[k]]; + while (j >= 0) { + if (j <= i) { + j = bins[j]; + continue; + } + + if (exclude && exclusion(i,j,type,mask,molecule)) { + j = bins[j]; + continue; + } + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + radsum = radi + radius[j]; + cutsq = (radsum+skin) * (radsum+skin); + + if (rsq <= cutsq) { + neighptr[n] = j; + + if (history >= 0) { + if (rsq < radsum*radsum) { + for (m = 0; m < npartner[i]; m++) + if (partner[i][m] == tag[j]) break; + if (m < npartner[i]) { + touchptr[n] = 1; + shearptr[nn++] = shearpartner[i][m][0]; + shearptr[nn++] = shearpartner[i][m][1]; + shearptr[nn++] = shearpartner[i][m][2]; + } else { + touchptr[n] = 0; + shearptr[nn++] = 0.0; + shearptr[nn++] = 0.0; + shearptr[nn++] = 0.0; + } + } else { + touchptr[n] = 0; + shearptr[nn++] = 0.0; + shearptr[nn++] = 0.0; + shearptr[nn++] = 0.0; + } + } + + n++; + } + + j = bins[j]; + } + } + + if (history >= 0) { + firsttouch[i] = touchptr; + firstshear[i] = shearptr; + } + firstneigh[i] = neighptr; + numneigh[i] = n; + npnt += n; + if (npnt >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + } +} + +/* ---------------------------------------------------------------------- + granular particles + binned neighbor list construction with full Newton's 3rd law + no shear history is allowed for this option + every pair stored exactly once by some processor + each owned atom i checks its own bin and other bins in Newton stencil +------------------------------------------------------------------------- */ + +void Neighbor::granular_bin_newton() +{ + int i,j,k,n,ibin; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + double radi,radsum,cutsq; + int *neighptr; + + // bin local & ghost atoms + + bin_atoms(); + + // loop over each atom, storing neighbors + + double **x = atom->x; + double *radius = atom->radius; + int *type = atom->type; + int *mask = atom->mask; + int *molecule = atom->molecule; + int nlocal = atom->nlocal; + + int npage = 0; + int npnt = 0; + + for (i = 0; i < nlocal; i++) { + + if (pgsize - npnt < oneatom) { + npnt = 0; + npage++; + if (npage == maxpage) add_pages(npage); + } + + n = 0; + neighptr = &pages[npage][npnt]; + + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + radi = radius[i]; + + // loop over rest of atoms in i's bin, ghosts are at end of linked list + // if j is owned atom, store it, since j is beyond i in linked list + // if j is ghost, only store if j coords are "above and to the right" of i + + j = bins[i]; + while (j >= 0) { + if (j >= nlocal) { + if ((x[j][2] < ztmp) || (x[j][2] == ztmp && x[j][1] < ytmp) || + (x[j][2] == ztmp && x[j][1] == ytmp && x[j][0] < xtmp)) { + j = bins[j]; + continue; + } + } + + if (exclude && exclusion(i,j,type,mask,molecule)) { + j = bins[j]; + continue; + } + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + radsum = radi + radius[j]; + cutsq = (radsum+skin) * (radsum+skin); + + if (rsq <= cutsq) neighptr[n++] = j; + + j = bins[j]; + } + + // loop over all atoms in other bins in stencil, store every pair + + ibin = coord2bin(x[i]); + for (k = 0; k < nstencil; k++) { + j = binhead[ibin+stencil[k]]; + while (j >= 0) { + if (exclude && exclusion(i,j,type,mask,molecule)) { + j = bins[j]; + continue; + } + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + radsum = radi + radius[j]; + cutsq = (radsum+skin) * (radsum+skin); + + if (rsq <= cutsq) neighptr[n++] = j; + + j = bins[j]; + } + } + + firstneigh[i] = neighptr; + numneigh[i] = n; + npnt += n; + if (npnt >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + } +} diff --git a/src/neigh_half.cpp b/src/neigh_half.cpp new file mode 100644 index 0000000000000000000000000000000000000000..38dd6c1d7d1daebf7845ad903fed60cbdc78414f --- /dev/null +++ b/src/neigh_half.cpp @@ -0,0 +1,476 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "neighbor.h" +#include "atom.h" +#include "error.h" + +/* ---------------------------------------------------------------------- + N^2 / 2 search for neighbor pairs with partial Newton's 3rd law + pair stored once if i,j are both owned and i < j + pair stored by me if j is ghost (also stored by proc owning j) +------------------------------------------------------------------------- */ + +void Neighbor::half_nsq_no_newton() +{ + int i,j,n,itype,jtype,which; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + int *neighptr; + + double **x = atom->x; + int *type = atom->type; + int *mask = atom->mask; + int *molecule = atom->molecule; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + int molecular = atom->molecular; + + int npage = 0; + int npnt = 0; + + for (i = 0; i < nlocal; i++) { + + if (pgsize - npnt < oneatom) { + npnt = 0; + npage++; + if (npage == maxpage) add_pages(npage); + } + + neighptr = &pages[npage][npnt]; + n = 0; + + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + + // loop over remaining atoms, owned and ghost + + for (j = i+1; j < nall; j++) { + if (exclude && exclusion(i,j,type,mask,molecule)) continue; + + jtype = type[j]; + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular) which = find_special(i,j); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (which > 0) neighptr[n++] = which*nall + j; + } + } + + firstneigh[i] = neighptr; + numneigh[i] = n; + npnt += n; + if (npnt >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + } +} + +/* ---------------------------------------------------------------------- + N^2 / 2 search for neighbor pairs with full Newton's 3rd law + every pair stored exactly once by some processor + decision on ghost atoms based on itag,jtag tests +------------------------------------------------------------------------- */ + +void Neighbor::half_nsq_newton() +{ + int i,j,n,itype,jtype,itag,jtag,which; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + int *neighptr; + + double **x = atom->x; + int *tag = atom->tag; + int *type = atom->type; + int *mask = atom->mask; + int *molecule = atom->molecule; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + int molecular = atom->molecular; + + int npage = 0; + int npnt = 0; + + for (i = 0; i < nlocal; i++) { + + if (pgsize - npnt < oneatom) { + npnt = 0; + npage++; + if (npage == maxpage) add_pages(npage); + } + + neighptr = &pages[npage][npnt]; + n = 0; + + itag = tag[i]; + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + + // loop over remaining atoms, owned and ghost + // itag = jtag is possible for long cutoffs that include images of self + + for (j = i+1; j < nall; j++) { + if (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; + else if (x[j][2] == ztmp && x[j][1] < ytmp) continue; + else if (x[j][2] == ztmp && x[j][1] == ytmp && x[j][0] < xtmp) + continue; + } + } + + if (exclude && exclusion(i,j,type,mask,molecule)) continue; + + jtype = type[j]; + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular) which = find_special(i,j); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (which > 0) neighptr[n++] = which*nall + j; + } + } + + firstneigh[i] = neighptr; + numneigh[i] = n; + npnt += n; + if (npnt >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + } +} + +/* ---------------------------------------------------------------------- + binned neighbor list construction with partial Newton's 3rd law + each owned atom i checks own bin and surrounding bins in non-Newton stencil + pair stored once if i,j are both owned and i < j + pair stored by me if j is ghost (also stored by proc owning j) +------------------------------------------------------------------------- */ + +void Neighbor::half_bin_no_newton() +{ + int i,j,k,n,itype,jtype,ibin,which; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + int *neighptr; + + // bin local & ghost atoms + + bin_atoms(); + + // loop over each atom, storing neighbors + + double **x = atom->x; + int *type = atom->type; + int *mask = atom->mask; + int *molecule = atom->molecule; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + int molecular = atom->molecular; + + int npage = 0; + int npnt = 0; + + for (i = 0; i < nlocal; i++) { + + if (pgsize - npnt < oneatom) { + npnt = 0; + npage++; + if (npage == maxpage) add_pages(npage); + } + + neighptr = &pages[npage][npnt]; + n = 0; + + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + ibin = coord2bin(x[i]); + + // loop over all atoms in surrounding bins in stencil including self + // only store pair if i < j + // stores own/own pairs only once + // stores own/ghost pairs on both procs + + for (k = 0; k < nstencil; k++) { + j = binhead[ibin+stencil[k]]; + while (j >= 0) { + if (j <= i) { + j = bins[j]; + continue; + } + + if (exclude && exclusion(i,j,type,mask,molecule)) { + j = bins[j]; + continue; + } + + jtype = type[j]; + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular) which = find_special(i,j); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (which > 0) neighptr[n++] = which*nall + j; + } + + j = bins[j]; + } + } + + firstneigh[i] = neighptr; + numneigh[i] = n; + npnt += n; + if (npnt >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + } +} + +/* ---------------------------------------------------------------------- + binned neighbor list construction with full Newton's 3rd law + each owned atom i checks its own bin and other bins in Newton stencil + every pair stored exactly once by some processor +------------------------------------------------------------------------- */ + +void Neighbor::half_bin_newton() +{ + int i,j,k,n,itype,jtype,ibin,which; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + int *neighptr; + + // bin local & ghost atoms + + bin_atoms(); + + // loop over each atom, storing neighbors + + double **x = atom->x; + int *type = atom->type; + int *mask = atom->mask; + int *molecule = atom->molecule; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + int molecular = atom->molecular; + + int npage = 0; + int npnt = 0; + + for (i = 0; i < nlocal; i++) { + + if (pgsize - npnt < oneatom) { + npnt = 0; + npage++; + if (npage == maxpage) add_pages(npage); + } + + neighptr = &pages[npage][npnt]; + n = 0; + + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + + // loop over rest of atoms in i's bin, ghosts are at end of linked list + // if j is owned atom, store it, since j is beyond i in linked list + // if j is ghost, only store if j coords are "above and to the right" of i + + j = bins[i]; + while (j >= 0) { + if (j >= nlocal) { + if ((x[j][2] < ztmp) || (x[j][2] == ztmp && x[j][1] < ytmp) || + (x[j][2] == ztmp && x[j][1] == ytmp && x[j][0] < xtmp)) { + j = bins[j]; + continue; + } + } + + if (exclude && exclusion(i,j,type,mask,molecule)) { + j = bins[j]; + continue; + } + + jtype = type[j]; + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular) which = find_special(i,j); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (which > 0) neighptr[n++] = which*nall + j; + } + + j = bins[j]; + } + + // loop over all atoms in other bins in stencil, store every pair + + ibin = coord2bin(x[i]); + for (k = 0; k < nstencil; k++) { + j = binhead[ibin+stencil[k]]; + while (j >= 0) { + if (exclude && exclusion(i,j,type,mask,molecule)) { + j = bins[j]; + continue; + } + + jtype = type[j]; + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular) which = find_special(i,j); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (which > 0) neighptr[n++] = which*nall + j; + } + + j = bins[j]; + } + } + + firstneigh[i] = neighptr; + numneigh[i] = n; + npnt += n; + if (npnt >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + } +} + +/* ---------------------------------------------------------------------- + build half list from full list + pair stored once if i,j are both owned and i < j + pair stored by me if j is ghost (also stored by proc owning j) +------------------------------------------------------------------------- */ + +void Neighbor::half_full_no_newton() +{ + int i,j,k,n,nfull; + int *neighptr,*neighs; + + int nlocal = atom->nlocal; + + int npage = 0; + int npnt = 0; + + for (i = 0; i < nlocal; i++) { + + if (pgsize - npnt < oneatom) { + npnt = 0; + npage++; + if (npage == maxpage) add_pages(npage); + } + + neighptr = &pages[npage][npnt]; + n = 0; + + // loop over full neighbor list + + neighs = firstneigh_full[i]; + nfull = numneigh_full[i]; + + for (k = 0; k < nfull; k++) { + j = neighs[k]; + if (j > i) neighptr[n++] = j; + } + + firstneigh[i] = neighptr; + numneigh[i] = n; + npnt += n; + if (npnt >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + } +} + +/* ---------------------------------------------------------------------- + build half list from full list + pair stored once if i,j are both owned and i < j + pair stored by me if j is ghost (also stored by proc owning j) +------------------------------------------------------------------------- */ + +void Neighbor::half_full_newton() +{ + int i,j,k,n,nfull; + int *neighptr,*neighs; + double xtmp,ytmp,ztmp,delx,dely,delz; + + double **x = atom->x; + int nlocal = atom->nlocal; + + int npage = 0; + int npnt = 0; + + for (i = 0; i < nlocal; i++) { + + if (pgsize - npnt < oneatom) { + npnt = 0; + npage++; + if (npage == maxpage) add_pages(npage); + } + + neighptr = &pages[npage][npnt]; + n = 0; + + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + + // loop over full neighbor list + + neighs = firstneigh_full[i]; + nfull = numneigh_full[i]; + + for (k = 0; k < nfull; k++) { + j = neighs[k]; + if (j < nlocal) { + if (i > j) continue; + } else { + if ((x[j][2] < ztmp) || (x[j][2] == ztmp && x[j][1] < ytmp) || + (x[j][2] == ztmp && x[j][1] == ytmp && x[j][0] < xtmp)) { + j = bins[j]; + continue; + } + } + neighptr[n++] = j; + } + + firstneigh[i] = neighptr; + numneigh[i] = n; + npnt += n; + if (npnt >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + } +} diff --git a/src/neigh_respa.cpp b/src/neigh_respa.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0a516ca8e8c641e364da243281859392cd9be294 --- /dev/null +++ b/src/neigh_respa.cpp @@ -0,0 +1,577 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "neighbor.h" +#include "atom.h" +#include "error.h" + +/* ---------------------------------------------------------------------- + multiple respa lists + N^2 / 2 search for neighbor pairs with partial Newton's 3rd law + pair added to list if atoms i and j are both owned and i < j + pair added if j is ghost (also stored by proc owning j) +------------------------------------------------------------------------- */ + +void Neighbor::respa_nsq_no_newton() +{ + int i,j,itype,jtype,which; + int n_inner,n_middle,n; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + int *neighptr_inner; + int *neighptr_middle; + int *neighptr; + + double **x = atom->x; + int *type = atom->type; + int *mask = atom->mask; + int *molecule = atom->molecule; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + int molecular = atom->molecular; + + int npage = 0; + int npnt = 0; + int npage_inner = 0; + int npnt_inner = 0; + int npage_middle = 0; + int npnt_middle = 0; + + for (i = 0; i < nlocal; i++) { + + if (pgsize - npnt < oneatom) { + npnt = 0; + npage++; + if (npage == maxpage) add_pages(npage); + } + neighptr = &pages[npage][npnt]; + n = 0; + + if (pgsize - npnt_inner < oneatom) { + npnt_inner = 0; + npage_inner++; + if (npage_inner == maxpage_inner) add_pages_inner(npage_inner); + } + neighptr_inner = &pages_inner[npage_inner][npnt_inner]; + n_inner = 0; + + if (respa == 2) { + if (pgsize - npnt_middle < oneatom) { + npnt_middle = 0; + npage_middle++; + if (npage_middle == maxpage_middle) add_pages_middle(npage_middle); + } + neighptr_middle = &pages_middle[npage_middle][npnt_middle]; + n_middle = 0; + } + + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + + // loop over remaining atoms, owned and ghost + + for (j = i+1; j < nall; j++) { + if (exclude && exclusion(i,j,type,mask,molecule)) continue; + + jtype = type[j]; + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular) which = find_special(i,j); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (which > 0) neighptr[n++] = which*nall + j; + + if (rsq < cut_inner_sq) { + if (which == 0) neighptr_inner[n_inner++] = j; + else if (which > 0) neighptr_inner[n_inner++] = which*nall + j; + } + + if (respa == 2 && rsq < cut_middle_sq && rsq > cut_middle_inside_sq) { + if (which == 0) neighptr_middle[n_middle++] = j; + else if (which > 0) neighptr_middle[n_middle++] = which*nall + j; + } + } + } + + firstneigh[i] = neighptr; + numneigh[i] = n; + npnt += n; + if (npnt >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + + firstneigh_inner[i] = neighptr_inner; + numneigh_inner[i] = n_inner; + npnt_inner += n_inner; + if (npnt_inner >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + + if (respa == 2) { + firstneigh_middle[i] = neighptr_middle; + numneigh_middle[i] = n_middle; + npnt_middle += n_middle; + if (npnt_middle >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + } + } +} + +/* ---------------------------------------------------------------------- + multiple respa lists + N^2 / 2 search for neighbor pairs with full Newton's 3rd law + pair added to list if atoms i and j are both owned and i < j + if j is ghost only me or other proc adds pair + decision based on itag,jtag tests +------------------------------------------------------------------------- */ + +void Neighbor::respa_nsq_newton() +{ + int i,j,itype,jtype,itag,jtag,which; + int n_inner,n_middle,n; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + int *neighptr_inner; + int *neighptr_middle; + int *neighptr; + + double **x = atom->x; + int *tag = atom->tag; + int *type = atom->type; + int *mask = atom->mask; + int *molecule = atom->molecule; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + int molecular = atom->molecular; + + int npage = 0; + int npnt = 0; + int npage_inner = 0; + int npnt_inner = 0; + int npage_middle = 0; + int npnt_middle = 0; + + for (i = 0; i < nlocal; i++) { + + if (pgsize - npnt < oneatom) { + npnt = 0; + npage++; + if (npage == maxpage) add_pages(npage); + } + neighptr = &pages[npage][npnt]; + n = 0; + + if (pgsize - npnt_inner < oneatom) { + npnt_inner = 0; + npage_inner++; + if (npage_inner == maxpage_inner) add_pages_inner(npage_inner); + } + neighptr_inner = &pages_inner[npage_inner][npnt_inner]; + n_inner = 0; + + if (respa == 2) { + if (pgsize - npnt_middle < oneatom) { + npnt_middle = 0; + npage_middle++; + if (npage_middle == maxpage_middle) add_pages_middle(npage_middle); + } + neighptr_middle = &pages_middle[npage_middle][npnt_middle]; + n_middle = 0; + } + + itag = tag[i]; + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + + // loop over remaining atoms, owned and ghost + + for (j = i+1; j < nall; j++) { + if (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; + else if (x[j][2] == ztmp && x[j][1] < ytmp) continue; + else if (x[j][2] == ztmp && x[j][1] == ytmp && x[j][0] < xtmp) + continue; + } + } + + if (exclude && exclusion(i,j,type,mask,molecule)) continue; + + jtype = type[j]; + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular) which = find_special(i,j); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (which > 0) neighptr[n++] = which*nall + j; + + if (rsq < cut_inner_sq) { + if (which == 0) neighptr_inner[n_inner++] = j; + else if (which > 0) neighptr_inner[n_inner++] = which*nall + j; + } + + if (respa == 2 && rsq < cut_middle_sq && rsq > cut_middle_inside_sq) { + if (which == 0) neighptr_middle[n_middle++] = j; + else if (which > 0) neighptr_middle[n_middle++] = which*nall + j; + } + } + } + + firstneigh[i] = neighptr; + numneigh[i] = n; + npnt += n; + if (npnt >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + + firstneigh_inner[i] = neighptr_inner; + numneigh_inner[i] = n_inner; + npnt_inner += n_inner; + if (npnt_inner >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + + if (respa == 2) { + firstneigh_middle[i] = neighptr_middle; + numneigh_middle[i] = n_middle; + npnt_middle += n_middle; + if (npnt_middle >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + } + } +} + +/* ---------------------------------------------------------------------- + multiple respa lists + binned neighbor list construction with partial Newton's 3rd law + each owned atom i checks own bin and surrounding bins in non-Newton stencil + pair stored once if i,j are both owned and i < j + pair stored by me if j is ghost (also stored by proc owning j) +------------------------------------------------------------------------- */ + +void Neighbor::respa_bin_no_newton() +{ + int i,j,k,itype,jtype,ibin,which; + int n_inner,n_middle,n; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + int *neighptr_inner; + int *neighptr_middle; + int *neighptr; + + // bin local & ghost atoms + + bin_atoms(); + + // loop over each atom, storing neighbors + + double **x = atom->x; + int *type = atom->type; + int *mask = atom->mask; + int *molecule = atom->molecule; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + int molecular = atom->molecular; + + int npage = 0; + int npnt = 0; + int npage_inner = 0; + int npnt_inner = 0; + int npage_middle = 0; + int npnt_middle = 0; + + for (i = 0; i < nlocal; i++) { + + if (pgsize - npnt < oneatom) { + npnt = 0; + npage++; + if (npage == maxpage) add_pages(npage); + } + neighptr = &pages[npage][npnt]; + n = 0; + + if (pgsize - npnt_inner < oneatom) { + npnt_inner = 0; + npage_inner++; + if (npage_inner == maxpage_inner) add_pages_inner(npage_inner); + } + neighptr_inner = &pages_inner[npage_inner][npnt_inner]; + n_inner = 0; + + if (respa == 2) { + if (pgsize - npnt_middle < oneatom) { + npnt_middle = 0; + npage_middle++; + if (npage_middle == maxpage_middle) add_pages_middle(npage_middle); + } + neighptr_middle = &pages_middle[npage_middle][npnt_middle]; + n_middle = 0; + } + + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + ibin = coord2bin(x[i]); + + // loop over all atoms in surrounding bins in stencil including self + // only store pair if i < j + // stores own/own pairs only once + // stores own/ghost pairs on both procs + + for (k = 0; k < nstencil; k++) { + j = binhead[ibin+stencil[k]]; + while (j >= 0) { + if (j <= i) { + j = bins[j]; + continue; + } + + if (exclude && exclusion(i,j,type,mask,molecule)) { + j = bins[j]; + continue; + } + + jtype = type[j]; + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular) which = find_special(i,j); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (which > 0) neighptr[n++] = which*nall + j; + + if (rsq < cut_inner_sq) { + if (which == 0) neighptr_inner[n_inner++] = j; + else if (which > 0) neighptr_inner[n_inner++] = which*nall + j; + } + + if (respa == 2 && rsq < cut_middle_sq && rsq > cut_middle_inside_sq) { + if (which == 0) neighptr_middle[n_middle++] = j; + else if (which > 0) neighptr_middle[n_middle++] = which*nall + j; + } + } + + j = bins[j]; + } + } + + firstneigh[i] = neighptr; + numneigh[i] = n; + npnt += n; + if (npnt >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + + firstneigh_inner[i] = neighptr_inner; + numneigh_inner[i] = n_inner; + npnt_inner += n_inner; + if (npnt_inner >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + + if (respa == 2) { + firstneigh_middle[i] = neighptr_middle; + numneigh_middle[i] = n_middle; + npnt_middle += n_middle; + if (npnt_middle >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + } + } +} + +/* ---------------------------------------------------------------------- + multiple respa lists + binned neighbor list construction with full Newton's 3rd law + every pair stored exactly once by some processor + each owned atom i checks its own bin and other bins in Newton stencil +------------------------------------------------------------------------- */ + +void Neighbor::respa_bin_newton() +{ + int i,j,k,itype,jtype,ibin,which; + int n_inner,n_middle,n; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + int *neighptr_inner; + int *neighptr_middle; + int *neighptr; + + // bin local & ghost atoms + + bin_atoms(); + + // loop over each atom, storing neighbors + + double **x = atom->x; + int *type = atom->type; + int *mask = atom->mask; + int *molecule = atom->molecule; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + int molecular = atom->molecular; + + int npage = 0; + int npnt = 0; + int npage_inner = 0; + int npnt_inner = 0; + int npage_middle = 0; + int npnt_middle = 0; + + for (i = 0; i < nlocal; i++) { + + if (pgsize - npnt < oneatom) { + npnt = 0; + npage++; + if (npage == maxpage) add_pages(npage); + } + neighptr = &pages[npage][npnt]; + n = 0; + + if (pgsize - npnt_inner < oneatom) { + npnt_inner = 0; + npage_inner++; + if (npage_inner == maxpage_inner) add_pages_inner(npage_inner); + } + neighptr_inner = &pages_inner[npage_inner][npnt_inner]; + n_inner = 0; + + if (respa == 2) { + if (pgsize - npnt_middle < oneatom) { + npnt_middle = 0; + npage_middle++; + if (npage_middle == maxpage_middle) add_pages_middle(npage_middle); + } + neighptr_middle = &pages_middle[npage_middle][npnt_middle]; + n_middle = 0; + } + + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + + // loop over rest of atoms in i's bin, ghosts are at end of linked list + // if j is owned atom, store it, since j is beyond i in linked list + // if j is ghost, only store if j coords are "above and to the right" of i + + j = bins[i]; + while (j >= 0) { + if (j >= nlocal) { + if ((x[j][2] < ztmp) || (x[j][2] == ztmp && x[j][1] < ytmp) || + (x[j][2] == ztmp && x[j][1] == ytmp && x[j][0] < xtmp)) { + j = bins[j]; + continue; + } + } + + if (exclude && exclusion(i,j,type,mask,molecule)) { + j = bins[j]; + continue; + } + + jtype = type[j]; + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular) which = find_special(i,j); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (which > 0) neighptr[n++] = which*nall + j; + + if (rsq < cut_inner_sq) { + if (which == 0) neighptr_inner[n_inner++] = j; + else if (which > 0) neighptr_inner[n_inner++] = which*nall + j; + } + + if (respa == 2 && rsq < cut_middle_sq && rsq > cut_middle_inside_sq) { + if (which == 0) neighptr_middle[n_middle++] = j; + else if (which > 0) neighptr_middle[n_middle++] = which*nall + j; + } + } + + j = bins[j]; + } + + // loop over all atoms in other bins in stencil, store every pair + + ibin = coord2bin(x[i]); + for (k = 0; k < nstencil; k++) { + j = binhead[ibin+stencil[k]]; + while (j >= 0) { + if (exclude && exclusion(i,j,type,mask,molecule)) { + j = bins[j]; + continue; + } + + jtype = type[j]; + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq <= cutneighsq[itype][jtype]) { + if (molecular) which = find_special(i,j); + else which = 0; + if (which == 0) neighptr[n++] = j; + else if (which > 0) neighptr[n++] = which*nall + j; + + if (rsq < cut_inner_sq) { + if (which == 0) neighptr_inner[n_inner++] = j; + else if (which > 0) neighptr_inner[n_inner++] = which*nall + j; + } + + if (respa == 2 && rsq < cut_middle_sq && rsq > cut_middle_inside_sq) { + if (which == 0) neighptr_middle[n_middle++] = j; + else if (which > 0) neighptr_middle[n_middle++] = which*nall + j; + } + } + + j = bins[j]; + } + } + + firstneigh[i] = neighptr; + numneigh[i] = n; + npnt += n; + if (npnt >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + + firstneigh_inner[i] = neighptr_inner; + numneigh_inner[i] = n_inner; + npnt_inner += n_inner; + if (npnt_inner >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + + if (respa == 2) { + firstneigh_middle[i] = neighptr_middle; + numneigh_middle[i] = n_middle; + npnt_middle += n_middle; + if (npnt_middle >= pgsize) + error->one("Neighbor list overflow, boost neigh_modify one or page"); + } + } +} diff --git a/src/neighbor.cpp b/src/neighbor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..36464b26d2d0d8debc298c72469f9f56b2c45161 --- /dev/null +++ b/src/neighbor.cpp @@ -0,0 +1,1406 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "math.h" +#include "stdlib.h" +#include "string.h" +#include "neighbor.h" +#include "atom.h" +#include "force.h" +#include "pair.h" +#include "domain.h" +#include "group.h" +#include "modify.h" +#include "fix.h" +#include "update.h" +#include "respa.h" +#include "output.h" +#include "memory.h" +#include "error.h" + +#define PGDELTA 1 +#define LB_FACTOR 1.5 +#define SMALL 1.0e-6 +#define EXDELTA 1 + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +Neighbor::Neighbor() +{ + MPI_Comm_rank(world,&me); + MPI_Comm_size(world,&nprocs); + + style = 1; + every = 1; + delay = 10; + dist_check = 1; + pgsize = 10000; + oneatom = 2000; + + maxlocal = 0; + + cutneighsq = NULL; + fixchecklist = NULL; + + // last neighbor info + + maxhold = 0; + xhold = NULL; + + // pair exclusion list info + + 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; + + // bin info + + maxhead = 0; + binhead = NULL; + maxbin = 0; + bins = NULL; + + maxstencil = 0; + stencil = NULL; + maxstencil_full = 0; + stencil_full = NULL; + + // half neighbor list info + + half = 0; + maxpage = 0; + numneigh = NULL; + firstneigh = NULL; + pages = NULL; + + // full neighbor list info + + full = 0; + maxpage_full = 0; + numneigh_full = NULL; + firstneigh_full = NULL; + pages_full = NULL; + + // shear history neighbor list info + + history = -1; + firsttouch = NULL; + firstshear = NULL; + pages_touch = NULL; + pages_shear = NULL; + + // multiple respa neighbor list info + + respa = 0; + maxpage_inner = 0; + maxpage_middle = 0; + numneigh_inner = NULL; + firstneigh_inner = NULL; + pages_inner = NULL; + numneigh_middle = NULL; + firstneigh_middle = NULL; + pages_middle = NULL; + + // bond list info + + maxbond = 0; + bondlist = NULL; + maxangle = 0; + anglelist = NULL; + maxdihedral = 0; + dihedrallist = NULL; + maximproper = 0; + improperlist = NULL; +} + +/* ---------------------------------------------------------------------- */ + +Neighbor::~Neighbor() +{ + memory->destroy_2d_double_array(cutneighsq); + delete [] fixchecklist; + memory->destroy_2d_double_array(xhold); + + memory->sfree(ex1_type); + memory->sfree(ex2_type); + memory->destroy_2d_int_array(ex_type); + + memory->sfree(ex1_group); + memory->sfree(ex2_group); + delete [] ex1_bit; + delete [] ex2_bit; + + memory->sfree(ex_mol_group); + delete [] ex_mol_bit; + + memory->sfree(binhead); + memory->sfree(bins); + memory->sfree(stencil); + memory->sfree(stencil_full); + + memory->destroy_2d_int_array(bondlist); + memory->destroy_2d_int_array(anglelist); + memory->destroy_2d_int_array(dihedrallist); + memory->destroy_2d_int_array(improperlist); + + memory->sfree(numneigh); + memory->sfree(firstneigh); + for (int i = 0; i < maxpage; i++) memory->sfree(pages[i]); + memory->sfree(pages); + + memory->sfree(numneigh_full); + memory->sfree(firstneigh_full); + for (int i = 0; i < maxpage_full; i++) memory->sfree(pages_full[i]); + memory->sfree(pages_full); + + if (history >= 0) { + memory->sfree(firsttouch); + memory->sfree(firstshear); + for (int i = 0; i < maxpage; i++) memory->sfree(pages_touch[i]); + for (int i = 0; i < maxpage; i++) memory->sfree(pages_shear[i]); + memory->sfree(pages_touch); + memory->sfree(pages_shear); + } + + if (respa) { + memory->sfree(numneigh_inner); + memory->sfree(firstneigh_inner); + for (int i = 0; i < maxpage_inner; i++) memory->sfree(pages_inner[i]); + memory->sfree(pages_inner); + memory->sfree(numneigh_middle); + memory->sfree(firstneigh_middle); + for (int i = 0; i < maxpage_middle; i++) memory->sfree(pages_middle[i]); + memory->sfree(pages_middle); + } +} + +/* ---------------------------------------------------------------------- */ + +void Neighbor::init() +{ + int i,j,m,n; + + ncalls = ndanger = 0; + + // error check + + if (delay > 0 && (delay % every) != 0) + error->all("Neighbor delay must be 0 or multiple of every setting"); + + // ------------------------------------------------------------------ + // settings + + // set cutneigh and trigger distance for reneighboring + + if (force->pair) cutneigh = force->pair->cutforce + skin; + else cutneigh = skin; + triggersq = 0.25*skin*skin; + if (cutneighsq == NULL) + cutneighsq = memory->create_2d_double_array(atom->ntypes+1,atom->ntypes+1, + "neigh:cutneighsq"); + + // set neighbor cutoffs with skin included + // if no pair defined, cutneigh is just skin + + n = atom->ntypes; + + if (force->pair) { + double cutoff; + double **cutsq = force->pair->cutsq; + for (i = 1; i <= n; i++) + for (j = i; j <= n; j++) { + cutoff = sqrt(cutsq[i][j]); + cutneighsq[i][j] = (cutoff+skin) * (cutoff+skin); + cutneighsq[j][i] = cutneighsq[i][j]; + } + } else { + for (i = 1; i <= n; i++) + for (j = i; j <= n; j++) { + cutneighsq[i][j] = skin*skin; + cutneighsq[j][i] = cutneighsq[i][j]; + } + } + + // check other classes that can induce reneighboring in decide() + + restart_check = 0; + if (output->restart_every) 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; + + // 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; + + // ------------------------------------------------------------------ + // memory management + + // free xhold and bins if not needed for this run + + if (dist_check == 0) { + memory->destroy_2d_double_array(xhold); + maxhold = 0; + } + + if (style == 0) { + memory->sfree(bins); + memory->sfree(binhead); + memory->sfree(stencil); + memory->sfree(stencil_full); + maxbin = maxhead = maxstencil = maxstencil_full = 0; + } + + // 1st time allocation of xhold and bins + + if (dist_check) { + if (maxhold == 0) { + maxhold = atom->nmax; + xhold = memory->create_2d_double_array(maxhold,3,"neigh:xhold"); + } + } + + if (style == 1) { + if (maxbin == 0) { + maxbin = atom->nmax; + bins = (int *) memory->smalloc(maxbin*sizeof(int),"bins"); + } + } + + // exclusion lists for type, group, molecule settings from neigh_modify + + n = atom->ntypes; + + if (nex_type == 0 && nex_group == 0 && nex_mol == 0) exclude = 0; + else exclude = 1; + + if (nex_type) { + memory->destroy_2d_int_array(ex_type); + ex_type = (int **) memory->create_2d_int_array(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("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]]; + } + + // ------------------------------------------------------------------ + // half and full pairwise neighbor lists + + // determine whether to build half and full lists + + maxlocal = atom->nmax; + int half_previous = half; + int full_previous = full; + + half_once = full_once = 0; + half_every = full_every = 0; + if (force->pair) half_every = force->pair->neigh_half_every; + if (force->pair) full_every = force->pair->neigh_full_every; + + for (i = 0; i < modify->nfix; i++) { + if (modify->fix[i]->neigh_half_every) half_every = 1; + if (modify->fix[i]->neigh_full_every) full_every = 1; + if (modify->fix[i]->neigh_half_once) half_once = 1; + if (modify->fix[i]->neigh_full_once) full_once = 1; + } + + half = full = 0; + if (half_every || half_once) half = 1; + if (full_every || full_once) full = 1; + + // setup/delete memory for half and full lists + + if (half == 0 && half_previous) { + memory->sfree(numneigh); + memory->sfree(firstneigh); + for (i = 0; i < maxpage; i++) memory->sfree(pages[i]); + memory->sfree(pages); + pages = NULL; + maxpage = 0; + } else if (half && half_previous == 0) { + numneigh = + (int *) memory->smalloc(maxlocal*sizeof(int),"neigh:numneigh"); + firstneigh = + (int **) memory->smalloc(maxlocal*sizeof(int *),"neigh:firstneigh"); + add_pages(0); + } + + if (full == 0 && full_previous) { + memory->sfree(numneigh_full); + memory->sfree(firstneigh_full); + for (i = 0; i < maxpage_full; i++) memory->sfree(pages_full[i]); + memory->sfree(pages_full); + pages_full = NULL; + maxpage_full = 0; + } else if (full && full_previous == 0) { + numneigh_full = + (int *) memory->smalloc(maxlocal*sizeof(int),"neigh:numneigh_full"); + firstneigh_full = + (int **) memory->smalloc(maxlocal*sizeof(int *),"neigh:firstneigh_full"); + add_pages_full(0); + } + + // setup/delete memory for shear history neighbor lists + // history = index of granular shear history fix if it exists + + int history_previous = history; + history = -1; + if (force->pair_match("gran/history") || force->pair_match("gran/hertzian")) + for (i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"SHEAR_HISTORY") == 0) history = i; + + if (history == -1 && history_previous >= 0) { + memory->sfree(firsttouch); + memory->sfree(firstshear); + for (i = 0; i < maxpage; i++) memory->sfree(pages_touch[i]); + for (i = 0; i < maxpage; i++) memory->sfree(pages_shear[i]); + memory->sfree(pages_touch); + memory->sfree(pages_shear); + pages_touch = NULL; + pages_shear = NULL; + } else if (history >= 0 && history_previous == -1) { + firsttouch = (int **) memory->smalloc(maxlocal*sizeof(int *),"firsttouch"); + firstshear = (double **) + memory->smalloc(maxlocal*sizeof(double *),"firstshear"); + add_pages_history(0); + } + + // setup/delete memory for rRESPA neighbor lists + // respa = 1 if rRESPA requires extra neighbor lists + // set neighbor cutoffs for multiple lists + + int respa_previous = respa; + respa = 0; + if (update->whichflag == 0 && strcmp(update->integrate_style,"respa") == 0) { + if (((Respa *) update->integrate)->level_inner >= 0) respa = 1; + if (((Respa *) update->integrate)->level_middle >= 0) respa = 2; + } + + if (respa && half_every == 0) + error->all("Cannot use rRESPA with full neighbor lists"); + + if (respa == 0 && respa_previous) { + memory->sfree(numneigh_inner); + memory->sfree(firstneigh_inner); + for (i = 0; i < maxpage; i++) memory->sfree(pages_inner[i]); + memory->sfree(pages_inner); + pages_inner = NULL; + maxpage_inner = 0; + if (respa_previous == 2) { + memory->sfree(numneigh_middle); + memory->sfree(firstneigh_middle); + for (i = 0; i < maxpage; i++) memory->sfree(pages_middle[i]); + memory->sfree(pages_middle); + pages_middle = NULL; + maxpage_middle = 0; + } + } else if (respa && respa_previous == 0) { + numneigh_inner = (int *) + memory->smalloc(maxlocal*sizeof(int),"neigh:numneigh_inner"); + firstneigh_inner = (int **) + memory->smalloc(maxlocal*sizeof(int *),"neigh:firstneigh_inner"); + add_pages_inner(0); + if (respa == 2) { + numneigh_middle = (int *) + memory->smalloc(maxlocal*sizeof(int),"neigh:numneigh_middle"); + firstneigh_middle = (int **) + memory->smalloc(maxlocal*sizeof(int *),"neigh:firstneigh_middle"); + add_pages_middle(0); + } + } + + 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); + } + + // set ptrs to correct half and full build functions + // cannot combine granular and rRESPA + + if (half) { + if (strcmp(atom->style,"granular") == 0) { + if (style == 0) { + if (force->newton_pair == 0) + half_build = &Neighbor::granular_nsq_no_newton; + else half_build = &Neighbor::granular_nsq_newton; + } else if (style == 1) { + if (force->newton_pair == 0) + half_build = &Neighbor::granular_bin_no_newton; + else half_build = &Neighbor::granular_bin_newton; + } + } else if (respa) { + if (style == 0) { + if (force->newton_pair == 0) + half_build = &Neighbor::respa_nsq_no_newton; + else half_build = &Neighbor::respa_nsq_newton; + } else if (style == 1) { + if (force->newton_pair == 0) + half_build = &Neighbor::respa_bin_no_newton; + else half_build = &Neighbor::respa_bin_newton; + } + } else { + if (style == 0) { + if (force->newton_pair == 0) { + if (full_every) half_build = &Neighbor::half_full_no_newton; + else half_build = &Neighbor::half_nsq_no_newton; + } else { + if (full_every) half_build = &Neighbor::half_full_newton; + else half_build = &Neighbor::half_nsq_newton; + } + } else if (style == 1) { + if (force->newton_pair == 0) { + if (full_every) half_build = &Neighbor::half_full_no_newton; + else half_build = &Neighbor::half_bin_no_newton; + } else { + if (full_every) half_build = &Neighbor::half_full_newton; + else half_build = &Neighbor::half_bin_newton; + } + } + } + } else half_build = NULL; + + if (full) { + if (style == 0) full_build = &Neighbor::full_nsq; + else full_build = &Neighbor::full_bin; + } else full_build = NULL; + + // ------------------------------------------------------------------ + // bond neighbor lists + + // 1st time allocation of bond lists + + if (atom->molecular && atom->nbonds && maxbond == 0) { + if (nprocs == 1) maxbond = atom->nbonds; + else maxbond = static_cast<int> (LB_FACTOR * atom->nbonds / nprocs); + bondlist = memory->create_2d_int_array(maxbond,3,"neigh:bondlist"); + } + + if (atom->molecular && atom->nangles && maxangle == 0) { + if (nprocs == 1) maxangle = atom->nangles; + else maxangle = static_cast<int> (LB_FACTOR * atom->nangles / nprocs); + anglelist = memory->create_2d_int_array(maxangle,4,"neigh:anglelist"); + } + + if (atom->molecular && atom->ndihedrals && maxdihedral == 0) { + if (nprocs == 1) maxdihedral = atom->ndihedrals; + else maxdihedral = static_cast<int> + (LB_FACTOR * atom->ndihedrals / nprocs); + dihedrallist = + memory->create_2d_int_array(maxdihedral,5,"neigh:dihedrallist"); + } + + if (atom->molecular && atom->nimpropers && maximproper == 0) { + if (nprocs == 1) maximproper = atom->nimpropers; + else maximproper = static_cast<int> + (LB_FACTOR * atom->nimpropers / nprocs); + improperlist = + memory->create_2d_int_array(maximproper,5,"neigh:improperlist"); + } + + // set flags that determine which bond 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->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->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->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->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 correct intra-molecular 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 intra-molecular neighbor list counts to 0 + // in case all are turned off but potential is still defined + + nbondlist = nanglelist = ndihedrallist = nimproperlist = 0; +} + +/* ---------------------------------------------------------------------- */ + +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 (dist_check == 0) return 1; + else return check_distance(); + } else return 0; +} + +/* ---------------------------------------------------------------------- */ + +int Neighbor::check_distance() +{ + double delx,dely,delz,rsq; + + int nlocal = atom->nlocal; + double **x = atom->x; + 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 > triggersq) 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 needed neighbor lists every few timesteps + half, full, bond lists are created as needed +------------------------------------------------------------------------- */ + +void Neighbor::build() +{ + ago = 0; + ncalls++; + + // store current nlocal used on this build (used by fix shear/history) + + nlocal_neighbor = atom->nlocal; + + // store current atom positions if needed + + if (dist_check) { + double **x = atom->x; + int nlocal = atom->nlocal; + if (nlocal > maxhold) { + maxhold = atom->nmax; + memory->destroy_2d_double_array(xhold); + xhold = memory->create_2d_double_array(maxhold,3,"neigh:xhold"); + } + for (int i = 0; i < nlocal; i++) { + xhold[i][0] = x[i][0]; + xhold[i][1] = x[i][1]; + xhold[i][2] = x[i][2]; + } + } + + // extend atom arrays if necessary + // check half/full instead of half_every/full_every so memory will be + // allocated correctly when build_half() and build_full() are called + + if (atom->nlocal > maxlocal) { + maxlocal = atom->nmax; + + if (half) { + memory->sfree(numneigh); + memory->sfree(firstneigh); + numneigh = (int *) + memory->smalloc(maxlocal*sizeof(int),"neigh:numneigh"); + firstneigh = (int **) + memory->smalloc(maxlocal*sizeof(int *),"neigh:firstneigh"); + } + + if (full) { + memory->sfree(numneigh_full); + memory->sfree(firstneigh_full); + numneigh_full = (int *) + memory->smalloc(maxlocal*sizeof(int),"neigh:numneigh_full"); + firstneigh_full = (int **) + memory->smalloc(maxlocal*sizeof(int *),"neigh:firstneigh_full"); + } + + if (history >= 0) { + memory->sfree(firsttouch); + memory->sfree(firstshear); + firsttouch = (int **) + memory->smalloc(maxlocal*sizeof(int *),"neigh:firsttouch"); + firstshear = (double **) + memory->smalloc(maxlocal*sizeof(double *),"neigh:firstshear"); + } + + if (respa) { + memory->sfree(numneigh_inner); + memory->sfree(firstneigh_inner); + numneigh_inner = (int *) + memory->smalloc(maxlocal*sizeof(int),"neigh:numneigh_inner"); + firstneigh_inner = (int **) + memory->smalloc(maxlocal*sizeof(int *),"neigh:firstneigh_inner"); + if (respa == 2) { + memory->sfree(numneigh_middle); + memory->sfree(firstneigh_middle); + numneigh_middle = (int *) + memory->smalloc(maxlocal*sizeof(int),"neigh:numneigh_middle"); + firstneigh_middle = (int **) + memory->smalloc(maxlocal*sizeof(int *),"neigh:firstneigh_middle"); + } + } + } + + // extend bin list if necessary + + if (style && atom->nmax > maxbin) { + maxbin = atom->nmax; + memory->sfree(bins); + bins = (int *) memory->smalloc(maxbin*sizeof(int),"bins"); + } + + // list construction for pairs and bonds + // full comes first in case half is built from full + + if (full_every) (this->*full_build)(); + if (half_every) (this->*half_build)(); + + if (atom->molecular) { + if (atom->nbonds) (this->*bond_build)(); + if (atom->nangles) (this->*angle_build)(); + if (atom->ndihedrals) (this->*dihedral_build)(); + if (atom->nimpropers) (this->*improper_build)(); + } +} + +/* ---------------------------------------------------------------------- + prepare for one-time call to build a half neighbor list + only needs to be called if Neighbor::init() set half = 0 +------------------------------------------------------------------------- */ + +void Neighbor::build_half_setup() +{ + numneigh = (int *) memory->smalloc(maxlocal*sizeof(int),"neigh:numneigh"); + firstneigh = + (int **) memory->smalloc(maxlocal*sizeof(int *),"neigh:firstneigh"); + add_pages(0); + + if (style == 0) { + if (force->newton_pair == 0) half_build = &Neighbor::half_nsq_no_newton; + else half_build = &Neighbor::half_nsq_newton; + } else if (style == 1) { + if (force->newton_pair == 0) half_build = &Neighbor::half_bin_no_newton; + else half_build = &Neighbor::half_bin_newton; + } +} + +/* ---------------------------------------------------------------------- + one-time call to build a half neighbor list made by other classes +------------------------------------------------------------------------- */ + +void Neighbor::build_half() +{ + (this->*half_build)(); +} + +/* ---------------------------------------------------------------------- + clean-up from one-time call to build a half neighbor list + only needs to be called if Neighbor::init() set half = 0 +------------------------------------------------------------------------- */ + +void Neighbor::build_half_cleanup() +{ + memory->sfree(numneigh); + memory->sfree(firstneigh); + for (int i = 0; i < maxpage; i++) memory->sfree(pages[i]); + memory->sfree(pages); + pages = NULL; + maxpage = 0; + half_build = NULL; +} + +/* ---------------------------------------------------------------------- + build a full neighbor list, called occasionally by a fix +------------------------------------------------------------------------- */ + +void Neighbor::build_full() +{ + (this->*full_build)(); +} + +/* ---------------------------------------------------------------------- + setup neighbor binning parameters + bin numbering is global: 0 = 0.0 to binsize, 1 = binsize to 2*binsize + nbin-1 = prd-binsize to binsize + nbin = prd to prd+binsize + -1 = -binsize to 0.0 + code will work for any binsize + since next(xyz) and stencil extend as far as necessary + binsize = 1/2 of cutoff is roughly optimal + prd must be filled exactly by integer # of bins + so procs on both sides of PBC see same bin boundary + 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 + stencil() = bin offsets in 1d sense for stencil of surrounding bins + stencil_full() = bin offsets in 1d sense for stencil for full neighbor list +------------------------------------------------------------------------- */ + +void Neighbor::setup_bins() +{ + // divide box into bins + // optimal size is roughly 1/2 the cutoff + + nbinx = static_cast<int> (2.0 * domain->xprd / cutneigh); + nbiny = static_cast<int> (2.0 * domain->yprd / cutneigh); + if (force->dimension == 3) + nbinz = static_cast<int> (2.0 * domain->zprd / cutneigh); + else nbinz = 1; + + if (nbinx == 0) nbinx = 1; + if (nbiny == 0) nbiny = 1; + if (nbinz == 0) nbinz = 1; + + binsizex = domain->xprd/nbinx; + binsizey = domain->yprd/nbiny; + binsizez = domain->zprd/nbinz; + + bininvx = 1.0 / binsizex; + bininvy = 1.0 / binsizey; + bininvz = 1.0 / binsizez; + + // mbinlo/hi = lowest and highest global bins my ghost atoms could be in + // coord = lowest and highest values of coords for my ghost atoms + // add in SMALL for round-off safety + + double coord; + int mbinxhi,mbinyhi,mbinzhi; + + coord = domain->subxlo - cutneigh - SMALL*domain->xprd; + mbinxlo = static_cast<int> ((coord-domain->boxxlo)*bininvx); + if (coord < 0.0) mbinxlo = mbinxlo - 1; + coord = domain->subxhi + cutneigh + SMALL*domain->xprd; + mbinxhi = static_cast<int> ((coord-domain->boxxlo)*bininvx); + + coord = domain->subylo - cutneigh - SMALL*domain->yprd; + mbinylo = static_cast<int> ((coord-domain->boxylo)*bininvy); + if (coord < 0.0) mbinylo = mbinylo - 1; + coord = domain->subyhi + cutneigh + SMALL*domain->yprd; + mbinyhi = static_cast<int> ((coord-domain->boxylo)*bininvy); + + coord = domain->subzlo - cutneigh - SMALL*domain->zprd; + mbinzlo = static_cast<int> ((coord-domain->boxzlo)*bininvz); + if (coord < 0.0) mbinzlo = mbinzlo - 1; + coord = domain->subzhi + cutneigh + SMALL*domain->zprd; + mbinzhi = static_cast<int> ((coord-domain->boxzlo)*bininvz); + + // extend bins by 1 to insure stencil extent is included + + mbinxlo = mbinxlo - 1; + mbinxhi = mbinxhi + 1; + mbinx = mbinxhi - mbinxlo + 1; + + mbinylo = mbinylo - 1; + mbinyhi = mbinyhi + 1; + mbiny = mbinyhi - mbinylo + 1; + + mbinzlo = mbinzlo - 1; + mbinzhi = mbinzhi + 1; + mbinz = mbinzhi - mbinzlo + 1; + + // memory for bin ptrs + + mbins = mbinx*mbiny*mbinz; + if (mbins > maxhead) { + maxhead = mbins; + memory->sfree(binhead); + binhead = (int *) memory->smalloc(maxhead*sizeof(int),"neigh:binhead"); + } + + // create stencil of bins whose closest corner to central bin + // is within neighbor cutoff + // next(xyz) = how far the stencil could possibly extend + // for partial Newton (newton = 0) + // stencil is all surrounding bins including self + // for full Newton (newton = 1) + // stencil is bins to the "upper right" of central bin + // stencil does NOT include self + // for full neighbor list (full = 1) + // stencil is all surrounding bins including self, regardless of Newton + // stored in stencil_full + // 3d creates xyz stencil, 2d is only xy + + int nextx = static_cast<int> (cutneigh*bininvx); + if (nextx*binsizex < cutneigh) nextx++; + int nexty = static_cast<int> (cutneigh*bininvy); + if (nexty*binsizey < cutneigh) nexty++; + int nextz = static_cast<int> (cutneigh*bininvz); + if (nextz*binsizez < cutneigh) nextz++; + + int nmax = (2*nextz+1) * (2*nexty+1) * (2*nextx+1); + if (nmax > maxstencil) { + maxstencil = nmax; + memory->sfree(stencil); + stencil = (int *) memory->smalloc(maxstencil*sizeof(int),"neigh:stencil"); + } + + int i,j,k; + nstencil = 0; + double cutsq = cutneigh*cutneigh; + + if (force->dimension == 3) { + if (force->newton_pair == 0) { + for (k = -nextz; k <= nextz; k++) + for (j = -nexty; j <= nexty; j++) + for (i = -nextx; i <= nextx; i++) + if (bin_distance(i,j,k) < cutsq) + stencil[nstencil++] = k*mbiny*mbinx + j*mbinx + i; + } else { + for (k = 0; k <= nextz; k++) + for (j = -nexty; j <= nexty; j++) + for (i = -nextx; i <= nextx; i++) + if (k > 0 || j > 0 || (j == 0 && i > 0)) + if (bin_distance(i,j,k) < cutsq) + stencil[nstencil++] = k*mbiny*mbinx + j*mbinx + i; + } + } else { + if (force->newton_pair == 0) { + for (j = -nexty; j <= nexty; j++) + for (i = -nextx; i <= nextx; i++) + if (bin_distance(i,j,0) < cutsq) + stencil[nstencil++] = j*mbinx + i; + } else { + for (j = 0; j <= nexty; j++) + for (i = -nextx; i <= nextx; i++) + if (j > 0 || (j == 0 && i > 0)) + if (bin_distance(i,j,0) < cutsq) + stencil[nstencil++] = j*mbinx + i; + } + } + + if (full) { + if (nmax > maxstencil_full) { + maxstencil_full = nmax; + memory->sfree(stencil_full); + stencil_full = (int *) memory->smalloc(maxstencil_full*sizeof(int), + "neigh:stencil_full"); + } + nstencil_full = 0; + if (force->dimension == 3) { + for (k = -nextz; k <= nextz; k++) + for (j = -nexty; j <= nexty; j++) + for (i = -nextx; i <= nextx; i++) + if (bin_distance(i,j,k) < cutsq) + stencil_full[nstencil_full++] = k*mbiny*mbinx + j*mbinx + i; + } else { + for (j = -nexty; j <= nexty; j++) + for (i = -nextx; i <= nextx; i++) + if (bin_distance(i,j,0) < cutsq) + stencil_full[nstencil_full++] = j*mbinx + i; + } + } +} + +/* ---------------------------------------------------------------------- + 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); +} + +/* ---------------------------------------------------------------------- + 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("Illegal neigh_modify command"); + every = atoi(arg[iarg+1]); + if (every <= 0) error->all("Illegal neigh_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"delay") == 0) { + if (iarg+2 > narg) error->all("Illegal neigh_modify command"); + delay = atoi(arg[iarg+1]); + if (delay < 0) error->all("Illegal neigh_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"check") == 0) { + if (iarg+2 > narg) error->all("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("Illegal neigh_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"page") == 0) { + if (iarg+2 > narg) error->all("Illegal neigh_modify command"); + pgsize = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"one") == 0) { + if (iarg+2 > narg) error->all("Illegal neigh_modify command"); + oneatom = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"exclude") == 0) { + if (iarg+2 > narg) error->all("Illegal neigh_modify command"); + + if (strcmp(arg[iarg+1],"type") == 0) { + if (iarg+4 > narg) error->all("Illegal neigh_modify command"); + if (nex_type == maxex_type) { + maxex_type += EXDELTA; + ex1_type = (int *) memory->srealloc(ex1_type,maxex_type*sizeof(int), + "neigh:ex1_type"); + ex2_type = (int *) memory->srealloc(ex2_type,maxex_type*sizeof(int), + "neigh:ex2_type"); + } + ex1_type[nex_type] = atoi(arg[iarg+2]); + ex2_type[nex_type] = atoi(arg[iarg+3]); + nex_type++; + iarg += 4; + + } else if (strcmp(arg[iarg+1],"group") == 0) { + if (iarg+4 > narg) error->all("Illegal neigh_modify command"); + if (nex_group == maxex_group) { + maxex_group += EXDELTA; + ex1_group = + (int *) memory->srealloc(ex1_group,maxex_group*sizeof(int), + "neigh:ex1_group"); + ex2_group = + (int *) memory->srealloc(ex2_group,maxex_group*sizeof(int), + "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("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("Illegal neigh_modify command"); + if (atom->molecular == 0) { + char *str = "Must use molecular atom style with neigh_modify exclude molecule"; + error->all(str); + } + if (nex_mol == maxex_mol) { + maxex_mol += EXDELTA; + ex_mol_group = + (int *) memory->srealloc(ex_mol_group,maxex_mol*sizeof(int), + "neigh:ex_mol_group"); + } + ex_mol_group[nex_mol] = group->find(arg[iarg+2]); + if (ex_mol_group[nex_mol] == -1) + error->all("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("Illegal neigh_modify command"); + + } else error->all("Illegal neigh_modify command"); + } +} + +/* ---------------------------------------------------------------------- + return # of bytes of allocated memory +------------------------------------------------------------------------- */ + +int Neighbor::memory_usage() +{ + int bytes = 0; + + bytes += maxhold*3 * sizeof(double); + + if (style == 0) { + bytes += maxbin * sizeof(int); + bytes += maxhead * sizeof(int); + bytes += maxstencil * sizeof(int); + bytes += maxstencil_full * sizeof(int); + } + + if (half) { + bytes += maxlocal * sizeof(int); + bytes += maxlocal * sizeof(int *); + bytes += maxpage*pgsize * sizeof(int); + } + + if (full) { + bytes += maxlocal * sizeof(int); + bytes += maxlocal * sizeof(int *); + bytes += maxpage_full*pgsize * sizeof(int); + } + + if (history >= 0) { + bytes += maxlocal * sizeof(int *); + bytes += maxlocal * sizeof(double *); + bytes += maxpage*pgsize * sizeof(int); + bytes += maxpage*pgsize*3 * sizeof(double); + } + + if (respa) { + bytes += maxlocal * sizeof(int); + bytes += maxlocal * sizeof(int *); + bytes += maxpage_inner*pgsize * sizeof(int); + if (respa == 2) { + bytes += maxlocal * sizeof(int); + bytes += maxlocal * sizeof(int *); + bytes += maxpage_middle*pgsize * sizeof(int); + } + } + + bytes += maxbond*3 * sizeof(int); + bytes += maxangle*4 * sizeof(int); + bytes += maxdihedral*5 * sizeof(int); + bytes += maximproper*5 * sizeof(int); + + return bytes; +} + +/* ---------------------------------------------------------------------- + add pages to half or full neighbor list, starting at npage +------------------------------------------------------------------------- */ + +void Neighbor::add_pages(int npage) +{ + maxpage += PGDELTA; + pages = (int **) + memory->srealloc(pages,maxpage*sizeof(int *),"neigh:pages"); + for (int i = npage; i < maxpage; i++) + pages[i] = (int *) memory->smalloc(pgsize*sizeof(int),"neigh:pages[i]"); +} + +void Neighbor::add_pages_full(int npage) +{ + maxpage_full += PGDELTA; + pages_full = (int **) + memory->srealloc(pages_full,maxpage_full*sizeof(int *),"neigh:pages_full"); + for (int i = npage; i < maxpage_full; i++) + pages_full[i] = + (int *) memory->smalloc(pgsize*sizeof(int),"neigh:pages_full[i]"); +} + +/* ---------------------------------------------------------------------- + add pages to granular neighbor list, starting at npage +------------------------------------------------------------------------- */ + +void Neighbor::add_pages_history(int npage) +{ + pages_touch = (int **) + memory->srealloc(pages_touch,maxpage*sizeof(int *),"neigh:pages_touch"); + pages_shear = (double **) + memory->srealloc(pages_shear,maxpage*sizeof(double *), + "neigh:pages_shear"); + for (int i = npage; i < maxpage; i++) { + pages_touch[i] = (int *) + memory->smalloc(pgsize*sizeof(int),"neigh:pages_touch[i]"); + pages_shear[i] = (double *) + memory->smalloc(3*pgsize*sizeof(double),"neigh:pages_shear[i]"); + } +} + +/* ---------------------------------------------------------------------- + add pages to rRESPA inner neighbor list, starting at npage_inner +------------------------------------------------------------------------- */ + +void Neighbor::add_pages_inner(int npage_inner) +{ + maxpage_inner += PGDELTA; + pages_inner = (int **) + memory->srealloc(pages_inner,maxpage_inner*sizeof(int *), + "neigh:pages_inner"); + for (int i = npage_inner; i < maxpage_inner; i++) + pages_inner[i] = + (int *) memory->smalloc(pgsize*sizeof(int),"neigh:pages_inner[i]"); +} + +/* ---------------------------------------------------------------------- + add pages to rRESPA middle neighbor list, starting at npage_middle +------------------------------------------------------------------------- */ + +void Neighbor::add_pages_middle(int npage_middle) +{ + maxpage_middle += PGDELTA; + pages_middle = (int **) + memory->srealloc(pages_middle,maxpage_middle*sizeof(int *), + "neigh:pages_middle"); + for (int i = npage_middle; i < maxpage_middle; i++) + pages_middle[i] = + (int *) memory->smalloc(pgsize*sizeof(int),"neigh:pages_middle[i]"); +} + +/* ---------------------------------------------------------------------- + determine if atom j is in special list of atom i + if it is not, return 0 + if it is and special flag is 0 (both coeffs are 0.0), return -1 + if it is and special flag is 1 (both coeffs are 1.0), return 0 + if it is and special flag is 2 (otherwise), return 1,2,3 + for which neighbor it is (and which coeff it maps to) +------------------------------------------------------------------------- */ + +int Neighbor::find_special(int i, int j) +{ + int *list = atom->special[i]; + int n1 = atom->nspecial[i][0]; + int n2 = atom->nspecial[i][1]; + int n3 = atom->nspecial[i][2]; + int tag = atom->tag[j]; + + for (int i = 0; i < n3; i++) { + if (list[i] == tag) { + if (i < n1) { + if (special_flag[1] == 0) return -1; + else if (special_flag[1] == 1) return 0; + else return 1; + } else if (i < n2) { + if (special_flag[2] == 0) return -1; + else if (special_flag[2] == 1) return 0; + else return 2; + } else { + if (special_flag[3] == 0) return -1; + else if (special_flag[3] == 1) return 0; + else return 3; + } + } + } + return 0; +} + +/* ---------------------------------------------------------------------- + bin owned and ghost atoms +------------------------------------------------------------------------- */ + +void Neighbor::bin_atoms() +{ + int i,ibin,nlocal,nall; + double **x; + + nlocal = atom->nlocal; + nall = atom->nlocal + atom->nghost; + x = atom->x; + + for (i = 0; i < mbins; i++) binhead[i] = -1; + + // bin ghost atoms 1st, so will be at end of linked list + // then bin owned atoms + + for (i = nlocal; i < nall; i++) { + ibin = coord2bin(x[i]); + bins[i] = binhead[ibin]; + binhead[ibin] = i; + } + + for (i = 0; i < nlocal; i++) { + ibin = coord2bin(x[i]); + bins[i] = binhead[ibin]; + binhead[ibin] = i; + } +} + +/* ---------------------------------------------------------------------- + convert atom coords into local bin # + only ghost atoms will have coord >= boxhi or coord < boxlo + take special care to insure ghosts are put in correct bins + this is necessary so that both procs on either side of PBC + treat a pair of atoms straddling the PBC in a consistent way +------------------------------------------------------------------------- */ + +int Neighbor::coord2bin(double *x) +{ + int ix,iy,iz; + + if (x[0] >= domain->boxxhi) + ix = static_cast<int> ((x[0]-domain->boxxhi)*bininvx) + nbinx - mbinxlo; + else if (x[0] >= domain->boxxlo) + ix = static_cast<int> ((x[0]-domain->boxxlo)*bininvx) - mbinxlo; + else + ix = static_cast<int> ((x[0]-domain->boxxlo)*bininvx) - mbinxlo - 1; + + if (x[1] >= domain->boxyhi) + iy = static_cast<int> ((x[1]-domain->boxyhi)*bininvy) + nbiny - mbinylo; + else if (x[1] >= domain->boxylo) + iy = static_cast<int> ((x[1]-domain->boxylo)*bininvy) - mbinylo; + else + iy = static_cast<int> ((x[1]-domain->boxylo)*bininvy) - mbinylo - 1; + + if (x[2] >= domain->boxzhi) + iz = static_cast<int> ((x[2]-domain->boxzhi)*bininvz) + nbinz - mbinzlo; + else if (x[2] >= domain->boxzlo) + iz = static_cast<int> ((x[2]-domain->boxzlo)*bininvz) - mbinzlo; + else + iz = static_cast<int> ((x[2]-domain->boxzlo)*bininvz) - mbinzlo - 1; + + return (iz*mbiny*mbinx + iy*mbinx + ix + 1); +} + +/* ---------------------------------------------------------------------- + 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 *type, int *mask, int *molecule) +{ + int m; + + if (nex_type && ex_type[type[i]][type[j]]) 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; +} diff --git a/src/neighbor.h b/src/neighbor.h new file mode 100644 index 0000000000000000000000000000000000000000..8ee4111d98476fcb8fc67614c5ef13522ee46bb3 --- /dev/null +++ b/src/neighbor.h @@ -0,0 +1,221 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef NEIGHBOR_H +#define NEIGHBOR_H + +#include "lammps.h" + +class Neighbor : public LAMMPS { + public: + int style; // 0 = nsq, 1 = binned + int every; // build every this many steps + int delay; // delay build for this many steps + int dist_check; // 0 = always build, 1 = only if 1/2 dist + int ago; // how many steps ago neighboring occurred + int pgsize; // size of neighbor page + int oneatom; // max # of neighbors for one atom + + double skin; // skin distance + double cutneigh; // neighbor cutoff + + int ncalls; // # of times build has been called + int ndanger; // # of dangerous builds + int nlocal_neighbor; // nlocal at last build + + int half; // 0/1 if half pair list ever built + int full; // 0/1 if full pair list ever built + int half_every; // 0/1 if half list built every step + int full_every; // 0/1 if full list built every step + int half_once; // 0/1 if half pair list built occasionally + int full_once; // 0/1 if full pair list built occasionally + + int *numneigh; // # of half neighbors for each atom + int **firstneigh; // ptr to 1st half neighbor of each atom + + int *numneigh_full; // # of full neighbors for each atom + int **firstneigh_full; // ptr to 1st full neighbor of each atom + + int nbondlist; // list of bonds to compute + int **bondlist; + + int nanglelist; // list of angles to compute + int **anglelist; + + int ndihedrallist; // list of dihedrals to compute + int **dihedrallist; + + int nimproperlist; // list of impropers to compute + int **improperlist; + // granular neighbor list + int **firsttouch; // ptr to first touch flag for each atom + double **firstshear; // ptr to first shear values for each atom + + // rRESPA neighbor lists + int *numneigh_inner; // # of inner pair neighbors for each atom + int **firstneigh_inner; // ptr to inner 1st neigh of each atom + int *numneigh_middle; // # of middle pair neighbors for each atom + int **firstneigh_middle; // ptr to middle 1st neigh of each atom + + Neighbor(); + ~Neighbor(); + void init(); + int decide(); // decide whether to build or not + int check_distance(); // check max distance moved since last build + void setup_bins(); // setup bins based on box and cutoff + void build(); // create all neighbor lists (half,full,bond) + void build_half_setup(); // setup for one-time creation of half list + void build_half(); // one-time creation of half neighbor list + void build_half_cleanup(); // cleanup of one-time creation of half list + void build_full(); // one-time creation of full neighbor list + void modify_params(int, char**); // modify parameters of neighbor build + int memory_usage(); // tally memory usage + + private: + int me,nprocs; + + int maxlocal; // size of numneigh, firstneigh arrays + int maxbond,maxangle,maxdihedral,maximproper; // size of bond lists + + int must_check; // 1 if must check other classes to reneigh + int restart_check; // 1 if restart enabled, 0 if no + int fix_check; // # of fixes that induce reneigh + int *fixchecklist; // which fixes to check + + double **cutneighsq; // neighbor cutoff squared for type pairs + double triggersq; // sq distance to trigger build on + + double **xhold; // atom coords at last neighbor build + int maxhold; // size of xhold array + + int nbinx,nbiny,nbinz; // # of global bins + int *bins; // ptr to next atom in each bin + int maxbin; // size of bins array + + int *binhead; // ptr to 1st atom in each bin + int maxhead; // size of binhead array + + int nstencil; // # of bins in stencil + int *stencil; // stencil list of bin offsets + int maxstencil; // size of stencil + + int mbins; // # of local bins and offset + int mbinx,mbiny,mbinz; + int mbinxlo,mbinylo,mbinzlo; + + double binsizex,binsizey,binsizez; // bin sizes and inverse sizes + double bininvx,bininvy,bininvz; + + int **pages; // half neighbor list pages + int maxpage; // # of half pages currently allocated + + int **pages_full; // full neighbor list pages + int maxpage_full; // # of full pages currently allocated + + int nstencil_full; // # of bins in full neighbor stencil + int *stencil_full; // full neighbor stencil list of bin offsets + int maxstencil_full; // size of full neighbor stencil + + // granular neighbor list + int history; // -1 if history not needed + // else stores fix shear history index + int **pages_touch; // pages of touch flags + double **pages_shear; // pages of shear values + + // rRESPA neighbor lists + int respa; // 0 = single neighbor list + // 1 = additional inner list + // 2 = additional inner and middle list + double inner[2],middle[2]; // rRESPA cutoffs for extra lists + double cut_inner_sq; // outer cutoff for inner neighbor list + double cut_middle_sq; // outer cutoff for middle neighbor list + double cut_middle_inside_sq; // inner cutoff for middle neighbor list + + int **pages_inner; // inner neighbor list pages + int maxpage_inner; // # of inner pages currently allocated + int **pages_middle; // middle neighbor list pages + int maxpage_middle; // # of middle pages currently allocated + + int special_flag[4]; // flags for 1-2, 1-3, 1-4 neighbors + + int exclude; // 0 if no type/group exclusions, 1 if yes + + int nex_type; // # of entries in type exclusion list + int maxex_type; // max # in type list + int *ex1_type,*ex2_type; // pairs of types to exclude + int **ex_type; // 2d array of excluded type pairs + + int nex_group; // # of entries in group exclusion list + int maxex_group; // max # in group list + int *ex1_group,*ex2_group; // pairs of group #'s to exclude + int *ex1_bit,*ex2_bit; // pairs of group bits to exclude + + int nex_mol; // # of entries in molecule exclusion list + int maxex_mol; // max # in molecule list + int *ex_mol_group; // molecule group #'s to exclude + int *ex_mol_bit; // molecule group bits to exclude + + typedef void (Neighbor::*FnPtr)(); + FnPtr half_build; // ptr to half pair list functions + FnPtr full_build; // ptr to full pair list functions + + void half_nsq_no_newton(); // via nsq w/ pair newton off + void half_nsq_newton(); // via nsq w/ pair newton on + void half_bin_no_newton(); // via binning w/ pair newton off + void half_bin_newton(); // via binning w/ pair newton on + void half_full_no_newton(); // via full list w/ pair newton off + void half_full_newton(); // via full list w/ pair newton on + + void full_nsq(); // 2 fns for full neighbor lists + void full_bin(); + + void granular_nsq_no_newton(); // 4 fns for granular systems + void granular_nsq_newton(); + void granular_bin_no_newton(); + void granular_bin_newton(); + + void respa_nsq_no_newton(); // 4 fns for multiple respa lists + void respa_nsq_newton(); + void respa_bin_no_newton(); + void respa_bin_newton(); + + FnPtr bond_build; // ptr to bond list functions + void bond_all(); // bond list with all bonds + void bond_partial(); // exclude certain bonds + + FnPtr angle_build; // ptr to angle list functions + void angle_all(); // angle list with all angles + void angle_partial(); // exclude certain angles + + FnPtr dihedral_build; // ptr to dihedral list functions + void dihedral_all(); // dihedral list with all dihedrals + void dihedral_partial(); // exclude certain dihedrals + + FnPtr improper_build; // ptr to improper list functions + void improper_all(); // improper list with all impropers + void improper_partial(); // exclude certain impropers + + void add_pages(int); // add pages to half neigh list + void add_pages_full(int); // add pages to full neigh list + void add_pages_history(int); // add pages to granular neigh list + void add_pages_inner(int); // add pages to respa inner list + void add_pages_middle(int); // add pages to respa middle list + + void bin_atoms(); // bin all atoms + double bin_distance(int, int, int); // distance between binx + int coord2bin(double *); // mapping atom coord to a bin + int find_special(int, int); // look for special neighbor + int exclusion(int, int, int *, int *, int *); // test for pair exclusion +}; + +#endif diff --git a/src/output.cpp b/src/output.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e0291633c893175d95c6f14cddc5f123d2e74dcb --- /dev/null +++ b/src/output.cpp @@ -0,0 +1,391 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "output.h" +#include "atom.h" +#include "neighbor.h" +#include "comm.h" +#include "update.h" +#include "group.h" +#include "domain.h" +#include "thermo.h" +#include "modify.h" +#include "force.h" +#include "dump.h" +#include "write_restart.h" +#include "memory.h" +#include "error.h" + +#define DumpInclude +#include "style.h" +#undef DumpInclude + +#define DELTA 1 +#define MYMIN(a,b) ((a) < (b) ? (a) : (b)) +#define MYMAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- + initialize all output +------------------------------------------------------------------------- */ + +Output::Output() +{ + thermo = NULL; + char **thermoarg = new char*[1]; + thermoarg[0] = new char[strlen("one") + 1]; + strcpy(thermoarg[0],"one"); + thermo = new Thermo(1,thermoarg); + delete [] thermoarg[0]; + delete [] thermoarg; + + thermo_every = 0; + + ndump = 0; + max_dump = 0; + next_dump = NULL; + last_dump = NULL; + dump_every = NULL; + dump = NULL; + + restart = NULL; + restart1 = restart2 = NULL; + restart_every = 0; +} + +/* ---------------------------------------------------------------------- + free all memory +------------------------------------------------------------------------- */ + +Output::~Output() +{ + if (thermo) delete thermo; + + memory->sfree(next_dump); + memory->sfree(last_dump); + memory->sfree(dump_every); + for (int i = 0; i < ndump; i++) delete dump[i]; + memory->sfree(dump); + + delete restart; + delete [] restart1; + delete [] restart2; +} + +/* ---------------------------------------------------------------------- */ + +void Output::init() +{ + thermo->init(); + for (int i = 0; i < ndump; i++) dump[i]->init(); +} + +/* ---------------------------------------------------------------------- + perform output for setup of run/min + do dump first, so memory_usage will include dump allocation + do thermo last, so will print after memory_usage +------------------------------------------------------------------------- */ + +void Output::setup(int flag) +{ + int ntimestep = update->ntimestep; + + // perform dump at start of run if multiple of every + // and last dump was not on this timestep + // set next_dump to multiple of every + // will not write on last step of run unless multiple of every + // set next_dump_any to smallest next_dump + // if no dumps, set next_dump_any to last+1 so will not influence next + + if (ndump) { + for (int idump = 0; idump < ndump; idump++) { + if (ntimestep % dump_every[idump] == 0 && + last_dump[idump] != ntimestep) { + dump[idump]->write(); + last_dump[idump] = ntimestep; + } + next_dump[idump] = + (ntimestep/dump_every[idump])*dump_every[idump] + dump_every[idump]; + if (idump) next_dump_any = MYMIN(next_dump_any,next_dump[idump]); + else next_dump_any = next_dump[0]; + } + } else next_dump_any = update->laststep + 1; + + // do not write a restart file at start of run + // set next_restart to multiple of every + // will not write on last step of run unless multiple of every + // if every = 0, set next_restart to last+1 so will not influence next + + if (restart_every) + next_restart = (ntimestep/restart_every)*restart_every + restart_every; + else next_restart = update->laststep + 1; + + // print memory usage unless being called between multiple runs + + if (flag) print_memory_usage(); + + // always do thermo with header at start of run + // set next_thermo to multiple of every or last step of run (if smaller) + // if every = 0, set next_thermo to last step of run + + thermo->header(); + thermo->compute(0); + last_thermo = ntimestep; + + if (thermo_every) { + next_thermo = (ntimestep/thermo_every)*thermo_every + thermo_every; + next_thermo = MYMIN(next_thermo,update->laststep); + } else next_thermo = update->laststep; + + // next = next timestep any output will be done + + next = MYMIN(next_dump_any,next_restart); + next = MYMIN(next,next_thermo); +} + +/* ---------------------------------------------------------------------- + perform all output for this timestep + only perform output if next matches current step and last doesn't + insure next values force output on last step of run + do dump/restart before thermo so thermo CPU time will include them +------------------------------------------------------------------------- */ + +void Output::write(int ntimestep) +{ + if (next_dump_any == ntimestep) { + for (int idump = 0; idump < ndump; idump++) { + if (next_dump[idump] == ntimestep && last_dump[idump] != ntimestep) { + dump[idump]->write(); + last_dump[idump] = ntimestep; + next_dump[idump] += dump_every[idump]; + next_dump[idump] = MYMIN(next_dump[idump],update->laststep); + } + if (idump) next_dump_any = MYMIN(next_dump_any,next_dump[idump]); + else next_dump_any = next_dump[0]; + } + } + + // for toggle = 0, replace "*" with current timestep in restart filename + + if (next_restart == ntimestep && last_restart != ntimestep) { + if (restart_toggle == 0) { + char *file = new char[strlen(restart1) + 16]; + char *ptr = strchr(restart1,'*'); + *ptr = '\0'; + sprintf(file,"%s%d%s",restart1,ntimestep,ptr+1); + *ptr = '*'; + restart->write(file); + delete [] file; + } else if (restart_toggle == 1) { + restart->write(restart1); + restart_toggle = 2; + } else if (restart_toggle == 2) { + restart->write(restart2); + restart_toggle = 1; + } + last_restart = ntimestep; + next_restart += restart_every; + next_restart = MYMIN(next_restart,update->laststep); + } + + if (next_thermo == ntimestep && last_thermo != ntimestep) { + thermo->compute(1); + last_thermo = ntimestep; + next_thermo += thermo_every; + next_thermo = MYMIN(next_thermo,update->laststep); + } + + // next = next timestep any output will be done + + next = MYMIN(next_dump_any,next_restart); + next = MYMIN(next,next_thermo); +} + +/* ---------------------------------------------------------------------- + add a Dump to list of Dumps +------------------------------------------------------------------------- */ + +void Output::add_dump(int narg, char **arg) +{ + if (narg < 5) error->all("Illegal dump command"); + + // error checks + + for (int idump = 0; idump < ndump; idump++) + if (strcmp(arg[0],dump[idump]->id) == 0) error->all("Reuse of dump ID"); + int igroup = group->find(arg[1]); + if (igroup == -1) error->all("Could not find dump group ID"); + if (atoi(arg[3]) <= 0) error->all("Invalid dump frequency"); + + // extend Dump list if necessary + + if (ndump == max_dump) { + max_dump += DELTA; + dump = (Dump **) + memory->srealloc(dump,max_dump*sizeof(Dump *),"output:dump"); + dump_every = (int *) + memory->srealloc(dump_every,max_dump*sizeof(int *),"output:dump_every"); + next_dump = (int *) + memory->srealloc(next_dump,max_dump*sizeof(int *),"output:next_dump"); + last_dump = (int *) + memory->srealloc(last_dump,max_dump*sizeof(int *),"output:last_dump"); + } + + // create the Dump + + if (strcmp(arg[2],"none") == 0) error->all("Invalid dump style"); + +#define DumpClass +#define DumpStyle(key,Class) \ + else if (strcmp(arg[2],#key) == 0) dump[ndump] = new Class(narg,arg); +#include "style.h" +#undef DumpClass + + else error->all("Invalid dump style"); + + dump_every[ndump] = atoi(arg[3]); + if (dump_every[ndump] <= 0) error->all("Illegal dump command"); + last_dump[ndump] = -1; + ndump++; +} + +/* ---------------------------------------------------------------------- + modify parameters of a Dump +------------------------------------------------------------------------- */ + +void Output::modify_dump(int narg, char **arg) +{ + if (narg < 1) error->all("Illegal dump_modify command"); + + // find which dump it is + + int idump; + for (idump = 0; idump < ndump; idump++) + if (strcmp(arg[0],dump[idump]->id) == 0) break; + if (idump == ndump) error->all("Cound not find dump_modify ID"); + + dump[idump]->modify_params(narg-1,&arg[1]); +} + +/* ---------------------------------------------------------------------- + delete a Dump from list of Dumps +------------------------------------------------------------------------- */ + +void Output::delete_dump(char *id) +{ + // find which dump it is and delete it + + int idump; + for (idump = 0; idump < ndump; idump++) + if (strcmp(id,dump[idump]->id) == 0) break; + if (idump == ndump) error->all("Could not find undump ID"); + + delete dump[idump]; + + // move other dumps down in list one slot + + for (int i = idump+1; i < ndump; i++) { + dump[i-1] = dump[i]; + dump_every[i-1] = dump_every[i]; + next_dump[i-1] = next_dump[i]; + last_dump[i-1] = last_dump[i]; + } + ndump--; +} + +/* ---------------------------------------------------------------------- + new Thermo style +------------------------------------------------------------------------- */ + +void Output::create_thermo(int narg, char **arg) +{ + if (narg < 1) error->all("Illegal thermo_style command"); + + // don't allow this so that dipole style can safely allocate inertia vector + + if (domain->box_exist == 0) + error->all("Thermo_style command before simulation box is defined"); + + // set thermo = NULL in case new Thermo throws an error + + delete thermo; + thermo = NULL; + thermo = new Thermo(narg,arg); +} + +/* ---------------------------------------------------------------------- + setup restart capability + if only one filename, append ".*" if not "*" in filename +------------------------------------------------------------------------- */ + +void Output::create_restart(int narg, char **arg) +{ + if (narg < 1) error->all("Illegal restart command"); + + if (restart) delete restart; + delete [] restart1; + delete [] restart2; + + restart_every = atoi(arg[0]); + if (restart_every == 0) { + if (narg != 1) error->all("Illegal restart command"); + return; + } + + restart = new WriteRestart; + last_restart = -1; + + int n = strlen(arg[1]) + 3; + restart1 = new char[n]; + strcpy(restart1,arg[1]); + + if (narg == 2) { + restart_toggle = 0; + restart2 = NULL; + if (strchr(restart1,'*') == NULL) strcat(restart1,".*"); + } else if (narg == 3) { + restart_toggle = 1; + n = strlen(arg[2]) + 1; + restart2 = new char[n]; + strcpy(restart2,arg[2]); + } else error->all("Illegal restart command"); +} + +/* ---------------------------------------------------------------------- + sum and print memory usage + is only memory on proc 0, not averaged across procs +------------------------------------------------------------------------- */ + +void Output::print_memory_usage() +{ + int bytes = 0; + + bytes += atom->memory_usage(); + bytes += neighbor->memory_usage(); + bytes += comm->memory_usage(); + bytes += update->memory_usage(); + bytes += force->memory_usage(); + bytes += modify->memory_usage(); + for (int i = 0; i < ndump; i++) bytes += dump[i]->memory_usage(); + + double mbytes = bytes/1024.0/1024.0; + + if (comm->me == 0) { + if (screen) + fprintf(screen,"Memory usage per processor = %g Mbytes\n",mbytes); + if (logfile) + fprintf(logfile,"Memory usage per processor = %g Mbytes\n",mbytes); + } +} diff --git a/src/output.h b/src/output.h new file mode 100644 index 0000000000000000000000000000000000000000..c70ddc32bd783c4d893808c4e3758aca943f2b16 --- /dev/null +++ b/src/output.h @@ -0,0 +1,64 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef OUTPUT_H +#define OUTPUT_H + +#include "lammps.h" + +class Thermo; +class Dump; +class WriteRestart; + +class Output : public LAMMPS { + public: + int next; // next timestep for any kind of output + + int next_thermo; // next timestep for thermo output + int thermo_every; // thermo output every this many steps + int last_thermo; // last timestep thermo was output + Thermo *thermo; // Thermodynamic computations + + int ndump; // # of Dumps defined + int max_dump; // max size of Dump list + int next_dump_any; // next timestep for any Dump + int *next_dump; // next timestep to do each Dump + int *dump_every; // output of each Dump every this many steps + int *last_dump; // last timestep each a snapshot was output + Dump **dump; // list of defined Dumps + + int next_restart; // next timestep to write a restart file + int restart_every; // write a restart file every this many steps + int last_restart; // last timestep a restart file was output + int restart_toggle; // 0 if use restart1 as prefix + // 1 if use restart1 as file, 2 for restart2 + char *restart1,*restart2; // names for restart files + WriteRestart *restart; // Restart output + + Output(); + ~Output(); + void init(); + void setup(int); // initial output before run/min + void write(int); // output for current timestep + + void add_dump(int, char **); // add a Dump to Dump list + void modify_dump(int, char **); // modify a Dump + void delete_dump(char *); // delete a Dump from Dump list + + void create_thermo(int, char **); // create a thermo style + void create_restart(int, char **); // create Restart and restart files + + void print_memory_usage(); // print out memory usage +}; + +#endif diff --git a/src/pack.cpp b/src/pack.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e92d976a1175f44238fa70575de982762e799329 --- /dev/null +++ b/src/pack.cpp @@ -0,0 +1,757 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "pack.h" + +#if !defined(PACK_POINTER) && !defined(PACK_MEMCPY) +#define PACK_ARRAY +#endif + +/* ---------------------------------------------------------------------- + Pack and unpack functions: + + pack routines copy strided values from data into contiguous locs in buf + unpack routines copy contiguous values from buf into strided locs in data + different versions of unpack depending on permutation + and # of values/element + PACK_ARRAY routines work via array indices (default) + PACK_POINTER routines work via pointers + PACK_MEMCPY routines work via pointers and memcpy function +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + pack/unpack with array indices +------------------------------------------------------------------------- */ + +#ifdef PACK_ARRAY + +/* ---------------------------------------------------------------------- + pack from data -> buf +------------------------------------------------------------------------- */ + +void pack_3d(double *data, double *buf, struct pack_plan_3d *plan) + +{ + register int in,out,fast,mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane,plane; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + + in = 0; + for (slow = 0; slow < nslow; slow++) { + plane = slow*nstride_plane; + for (mid = 0; mid < nmid; mid++) { + out = plane + mid*nstride_line; + for (fast = 0; fast < nfast; fast++) + buf[in++] = data[out++]; + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data +------------------------------------------------------------------------- */ + +void unpack_3d(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register int in,out,fast,mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane,plane; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + + out = 0; + for (slow = 0; slow < nslow; slow++) { + plane = slow*nstride_plane; + for (mid = 0; mid < nmid; mid++) { + in = plane + mid*nstride_line; + for (fast = 0; fast < nfast; fast++) + data[in++] = buf[out++]; + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data, one axis permutation, 1 value/element +------------------------------------------------------------------------- */ + +void unpack_3d_permute1_1(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register int in,out,fast,mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane,plane; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + + out = 0; + for (slow = 0; slow < nslow; slow++) { + plane = slow*nstride_line; + for (mid = 0; mid < nmid; mid++) { + in = plane + mid; + for (fast = 0; fast < nfast; fast++, in += nstride_plane) + data[in] = buf[out++]; + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data, one axis permutation, 2 values/element +------------------------------------------------------------------------- */ + +void unpack_3d_permute1_2(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register int in,out,fast,mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane,plane; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + + out = 0; + for (slow = 0; slow < nslow; slow++) { + plane = slow*nstride_line; + for (mid = 0; mid < nmid; mid++) { + in = plane + 2*mid; + for (fast = 0; fast < nfast; fast++, in += nstride_plane) { + data[in] = buf[out++]; + data[in+1] = buf[out++]; + } + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data, one axis permutation, nqty values/element +------------------------------------------------------------------------- */ + +void unpack_3d_permute1_n(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register int in,out,iqty,instart,fast,mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane,plane,nqty; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + nqty = plan->nqty; + + out = 0; + for (slow = 0; slow < nslow; slow++) { + plane = slow*nstride_line; + for (mid = 0; mid < nmid; mid++) { + instart = plane + nqty*mid; + for (fast = 0; fast < nfast; fast++, instart += nstride_plane) { + in = instart; + for (iqty = 0; iqty < nqty; iqty++) data[in++] = buf[out++]; + } + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data, two axis permutation, 1 value/element +------------------------------------------------------------------------- */ + +void unpack_3d_permute2_1(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register int in,out,fast,mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + + out = 0; + for (slow = 0; slow < nslow; slow++) { + for (mid = 0; mid < nmid; mid++) { + in = slow + mid*nstride_plane; + for (fast = 0; fast < nfast; fast++, in += nstride_line) + data[in] = buf[out++]; + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data, two axis permutation, 2 values/element +------------------------------------------------------------------------- */ + +void unpack_3d_permute2_2(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register int in,out,fast,mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + + out = 0; + for (slow = 0; slow < nslow; slow++) { + for (mid = 0; mid < nmid; mid++) { + in = 2*slow + mid*nstride_plane; + for (fast = 0; fast < nfast; fast++, in += nstride_line) { + data[in] = buf[out++]; + data[in+1] = buf[out++]; + } + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data, two axis permutation, nqty values/element +------------------------------------------------------------------------- */ + +void unpack_3d_permute2_n(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register int in,out,iqty,instart,fast,mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane,nqty; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + nqty = plan->nqty; + + out = 0; + for (slow = 0; slow < nslow; slow++) { + for (mid = 0; mid < nmid; mid++) { + instart = nqty*slow + mid*nstride_plane; + for (fast = 0; fast < nfast; fast++, instart += nstride_line) { + in = instart; + for (iqty = 0; iqty < nqty; iqty++) data[in++] = buf[out++]; + } + } + } +} + +#endif + +/* ---------------------------------------------------------------------- + pack/unpack with pointers +------------------------------------------------------------------------- */ + +#ifdef PACK_POINTER + +/* ---------------------------------------------------------------------- + pack from data -> buf +------------------------------------------------------------------------- */ + +void pack_3d(double *data, double *buf, struct pack_plan_3d *plan) + +{ + register double *in,*out,*begin,*end; + register int mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane,plane; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + + in = buf; + for (slow = 0; slow < nslow; slow++) { + plane = slow*nstride_plane; + for (mid = 0; mid < nmid; mid++) { + begin = &(data[plane+mid*nstride_line]); + end = begin + nfast; + for (out = begin; out < end; out++) + *(in++) = *out; + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data +------------------------------------------------------------------------- */ + +void unpack_3d(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register double *in,*out,*begin,*end; + register int mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane,plane; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + + out = buf; + for (slow = 0; slow < nslow; slow++) { + plane = slow*nstride_plane; + for (mid = 0; mid < nmid; mid++) { + begin = &(data[plane+mid*nstride_line]); + end = begin + nfast; + for (in = begin; in < end; in++) + *in = *(out++); + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data, one axis permutation, 1 value/element +------------------------------------------------------------------------- */ + +void unpack_3d_permute1_1(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register double *in,*out,*begin,*end; + register int mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane,plane; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + + out = buf; + for (slow = 0; slow < nslow; slow++) { + plane = slow*nstride_line; + for (mid = 0; mid < nmid; mid++) { + begin = &(data[plane+mid]); + end = begin + nfast*nstride_plane; + for (in = begin; in < end; in += nstride_plane) + *in = *(out++); + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data, one axis permutation, 2 values/element +------------------------------------------------------------------------- */ + +void unpack_3d_permute1_2(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register double *in,*out,*begin,*end; + register int mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane,plane; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + + out = buf; + for (slow = 0; slow < nslow; slow++) { + plane = slow*nstride_line; + for (mid = 0; mid < nmid; mid++) { + begin = &(data[plane+2*mid]); + end = begin + nfast*nstride_plane; + for (in = begin; in < end; in += nstride_plane) { + *in = *(out++); + *(in+1) = *(out++); + } + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data, one axis permutation, nqty values/element +------------------------------------------------------------------------- */ + +void unpack_3d_permute1_n(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register double *in,*out,*instart,*begin,*end; + register int iqty,mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane,plane,nqty; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + nqty = plan->nqty; + + out = buf; + for (slow = 0; slow < nslow; slow++) { + plane = slow*nstride_line; + for (mid = 0; mid < nmid; mid++) { + begin = &(data[plane+nqty*mid]); + end = begin + nfast*nstride_plane; + for (instart = begin; instart < end; instart += nstride_plane) { + in = instart; + for (iqty = 0; iqty < nqty; iqty++) *(in++) = *(out++); + } + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data, two axis permutation, 1 value/element +------------------------------------------------------------------------- */ + +void unpack_3d_permute2_1(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register double *in,*out,*begin,*end; + register int mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + + out = buf; + for (slow = 0; slow < nslow; slow++) { + for (mid = 0; mid < nmid; mid++) { + begin = &(data[slow+mid*nstride_plane]); + end = begin + nfast*nstride_line; + for (in = begin; in < end; in += nstride_line) + *in = *(out++); + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data, two axis permutation, 2 values/element +------------------------------------------------------------------------- */ + +void unpack_3d_permute2_2(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register double *in,*out,*begin,*end; + register int mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + + out = buf; + for (slow = 0; slow < nslow; slow++) { + for (mid = 0; mid < nmid; mid++) { + begin = &(data[2*slow+mid*nstride_plane]); + end = begin + nfast*nstride_line; + for (in = begin; in < end; in += nstride_line) { + *in = *(out++); + *(in+1) = *(out++); + } + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data, two axis permutation, nqty values/element +------------------------------------------------------------------------- */ + +void unpack_3d_permute2_n(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register double *in,*out,*instart,*begin,*end; + register int iqty,mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane,nqty; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + nqty = plan->nqty; + + out = buf; + for (slow = 0; slow < nslow; slow++) { + for (mid = 0; mid < nmid; mid++) { + begin = &(data[nqty*slow+mid*nstride_plane]); + end = begin + nfast*nstride_line; + for (instart = begin; instart < end; instart += nstride_line) { + in = instart; + for (iqty = 0; iqty < nqty; iqty++) *(in++) = *(out++); + } + } + } +} + +#endif + +/* ---------------------------------------------------------------------- + pack/unpack with pointers and memcpy function + no memcpy version of unpack_permute routines, + just use PACK_POINTER versions +------------------------------------------------------------------------- */ + +#ifdef PACK_MEMCPY + +/* ---------------------------------------------------------------------- + pack from data -> buf +------------------------------------------------------------------------- */ + +void pack_3d(double *data, double *buf, struct pack_plan_3d *plan) + +{ + register double *in,*out; + register int mid,slow,size; + register int nfast,nmid,nslow,nstride_line,nstride_plane,plane,upto; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + + size = nfast*sizeof(double); + for (slow = 0; slow < nslow; slow++) { + plane = slow*nstride_plane; + upto = slow*nmid*nfast; + for (mid = 0; mid < nmid; mid++) { + in = &(buf[upto+mid*nfast]); + out = &(data[plane+mid*nstride_line]); + memcpy(in,out,size); + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data +------------------------------------------------------------------------- */ + +void unpack_3d(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register double *in,*out; + register int mid,slow,size; + register int nfast,nmid,nslow,nstride_line,nstride_plane,plane,upto; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + + size = nfast*sizeof(double); + for (slow = 0; slow < nslow; slow++) { + plane = slow*nstride_plane; + upto = slow*nmid*nfast; + for (mid = 0; mid < nmid; mid++) { + in = &(data[plane+mid*nstride_line]); + out = &(buf[upto+mid*nfast]); + memcpy(in,out,size); + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data, one axis permutation, 1 value/element +------------------------------------------------------------------------- */ + +void unpack_3d_permute1_1(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register double *in,*out,*begin,*end; + register int mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane,plane; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + + out = buf; + for (slow = 0; slow < nslow; slow++) { + plane = slow*nstride_line; + for (mid = 0; mid < nmid; mid++) { + begin = &(data[plane+mid]); + end = begin + nfast*nstride_plane; + for (in = begin; in < end; in += nstride_plane) + *in = *(out++); + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data, one axis permutation, 2 values/element +------------------------------------------------------------------------- */ + +void unpack_3d_permute1_2(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register double *in,*out,*begin,*end; + register int mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane,plane; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + + out = buf; + for (slow = 0; slow < nslow; slow++) { + plane = slow*nstride_line; + for (mid = 0; mid < nmid; mid++) { + begin = &(data[plane+2*mid]); + end = begin + nfast*nstride_plane; + for (in = begin; in < end; in += nstride_plane) { + *in = *(out++); + *(in+1) = *(out++); + } + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data, one axis permutation, nqty values/element +------------------------------------------------------------------------- */ + +void unpack_3d_permute1_n(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register double *in,*out,*instart,*begin,*end; + register int iqty,mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane,plane,nqty; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + nqty = plan->nqty; + + out = buf; + for (slow = 0; slow < nslow; slow++) { + plane = slow*nstride_line; + for (mid = 0; mid < nmid; mid++) { + begin = &(data[plane+nqty*mid]); + end = begin + nfast*nstride_plane; + for (instart = begin; instart < end; instart += nstride_plane) { + in = instart; + for (iqty = 0; iqty < nqty; iqty++) *(in++) = *(out++); + } + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data, two axis permutation, 1 value/element +------------------------------------------------------------------------- */ + +void unpack_3d_permute2_1(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register double *in,*out,*begin,*end; + register int mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + + out = buf; + for (slow = 0; slow < nslow; slow++) { + for (mid = 0; mid < nmid; mid++) { + begin = &(data[slow+mid*nstride_plane]); + end = begin + nfast*nstride_line; + for (in = begin; in < end; in += nstride_line) + *in = *(out++); + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data, two axis permutation, 2 values/element +------------------------------------------------------------------------- */ + +void unpack_3d_permute2_2(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register double *in,*out,*begin,*end; + register int mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + + out = buf; + for (slow = 0; slow < nslow; slow++) { + for (mid = 0; mid < nmid; mid++) { + begin = &(data[2*slow+mid*nstride_plane]); + end = begin + nfast*nstride_line; + for (in = begin; in < end; in += nstride_line) { + *in = *(out++); + *(in+1) = *(out++); + } + } + } +} + +/* ---------------------------------------------------------------------- + unpack from buf -> data, two axis permutation, nqty values/element +------------------------------------------------------------------------- */ + +void unpack_3d_permute2_n(double *buf, double *data, struct pack_plan_3d *plan) + +{ + register double *in,*out,*instart,*begin,*end; + register int iqty,mid,slow; + register int nfast,nmid,nslow,nstride_line,nstride_plane,nqty; + + nfast = plan->nfast; + nmid = plan->nmid; + nslow = plan->nslow; + nstride_line = plan->nstride_line; + nstride_plane = plan->nstride_plane; + nqty = plan->nqty; + + out = buf; + for (slow = 0; slow < nslow; slow++) { + for (mid = 0; mid < nmid; mid++) { + begin = &(data[nqty*slow+mid*nstride_plane]); + end = begin + nfast*nstride_line; + for (instart = begin; instart < end; instart += nstride_line) { + in = instart; + for (iqty = 0; iqty < nqty; iqty++) *(in++) = *(out++); + } + } + } +} + +#endif diff --git a/src/pack.h b/src/pack.h new file mode 100644 index 0000000000000000000000000000000000000000..123691d4d8007f20045d1fb7591cd4bed5e422e5 --- /dev/null +++ b/src/pack.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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. +------------------------------------------------------------------------- */ + +// loop counters for doing a pack/unpack + +struct pack_plan_3d { + int nfast; // # of elements in fast index + int nmid; // # of elements in mid index + int nslow; // # of elements in slow index + int nstride_line; // stride between successive mid indices + int nstride_plane; // stride between successive slow indices + int nqty; // # of values/element +}; + +// function prototypes + +void pack_3d(double *, double *, struct pack_plan_3d *); +void unpack_3d(double *, double *, struct pack_plan_3d *); +void unpack_3d_permute1_1(double *, double *, struct pack_plan_3d *); +void unpack_3d_permute1_2(double *, double *, struct pack_plan_3d *); +void unpack_3d_permute1_n(double *, double *, struct pack_plan_3d *); +void unpack_3d_permute2_1(double *, double *, struct pack_plan_3d *); +void unpack_3d_permute2_2(double *, double *, struct pack_plan_3d *); +void unpack_3d_permute2_n(double *, double *, struct pack_plan_3d *); diff --git a/src/pair.cpp b/src/pair.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eb796343f44e5f34659badfaabd9a292e61ba783 --- /dev/null +++ b/src/pair.cpp @@ -0,0 +1,403 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "mpi.h" +#include "float.h" +#include "limits.h" +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "pair.h" +#include "pair_soft.h" +#include "pair_eam.h" +#include "atom.h" +#include "domain.h" +#include "comm.h" +#include "force.h" +#include "update.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +#define GEOMETRIC 0 +#define ARITHMETIC 1 +#define SIXTHPOWER 2 + +#define R 1 +#define RSQ 2 +#define BMP 3 + +/* ---------------------------------------------------------------------- */ + +Pair::Pair() +{ + eng_vdwl = eng_coul = 0.0; + allocated = 0; + offset_flag = 0; + mix_flag = GEOMETRIC; + ncoultablebits = 12; + tabinner = sqrt(2.0); + tail_flag = 0; + etail = ptail = etail_ij = ptail_ij = 0.0; + neigh_half_every = 1; + neigh_full_every = 0; + single_enable = 1; + respa_enable = 0; + one_coeff = 0; +} + +/* ---------------------------------------------------------------------- + modify parameters of the pair style + pair_hybrid has its own version of this routine for its sub-styles +------------------------------------------------------------------------- */ + +void Pair::modify_params(int narg, char **arg) +{ + if (narg == 0) error->all("Illegal pair_modify command"); + + int iarg = 0; + while (iarg < narg) { + if (strcmp(arg[iarg],"mix") == 0) { + if (iarg+2 > narg) error->all("Illegal pair_modify command"); + if (strcmp(arg[iarg+1],"geometric") == 0) mix_flag = GEOMETRIC; + else if (strcmp(arg[iarg+1],"arithmetic") == 0) mix_flag = ARITHMETIC; + else if (strcmp(arg[iarg+1],"sixthpower") == 0) mix_flag = SIXTHPOWER; + else error->all("Illegal pair_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"shift") == 0) { + if (iarg+2 > narg) error->all("Illegal pair_modify command"); + if (strcmp(arg[iarg+1],"yes") == 0) offset_flag = 1; + else if (strcmp(arg[iarg+1],"no") == 0) offset_flag = 0; + else error->all("Illegal pair_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"table") == 0) { + if (iarg+2 > narg) error->all("Illegal pair_modify command"); + ncoultablebits = atoi(arg[iarg+1]); + if (ncoultablebits > sizeof(float)*CHAR_BIT) + error->all("Too many total bits for bitmapped lookup table"); + iarg += 2; + } else if (strcmp(arg[iarg],"tabinner") == 0) { + if (iarg+2 > narg) error->all("Illegal pair_modify command"); + tabinner = atof(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"tail") == 0) { + if (iarg+2 > narg) error->all("Illegal pair_modify command"); + if (strcmp(arg[iarg+1],"yes") == 0) tail_flag = 1; + else if (strcmp(arg[iarg+1],"no") == 0) tail_flag = 0; + else error->all("Illegal pair_modify command"); + iarg += 2; + } else error->all("Illegal pair_modify command"); + } +} + +/* ---------------------------------------------------------------------- */ + +void Pair::init() +{ + int i,j; + + if (offset_flag && tail_flag) + error->all("Cannot have both pair_modify shift and tail set to yes"); + if (tail_flag && force->dimension == 2) + error->all("Cannot use pair tail corrections with 2d simulations"); + if (tail_flag && domain->nonperiodic && comm->me == 0) + error->warning("Using pair tail corrections with nonperiodic system"); + + if (!allocated) error->all("All pair coeffs are not set"); + + for (i = 1; i <= atom->ntypes; i++) + if (setflag[i][i] == 0) error->all("All pair coeffs are not set"); + + double cut; + cutforce = 0.0; + etail = ptail = 0.0; + for (i = 1; i <= atom->ntypes; i++) + for (j = i; j <= atom->ntypes; j++) { + cut = init_one(i,j); + cutsq[i][j] = cutsq[j][i] = cut*cut; + cutforce = MAX(cutforce,cut); + if (tail_flag) { + etail += etail_ij; + ptail += ptail_ij; + if (i != j) { + etail += etail_ij; + ptail += ptail_ij; + } + } + } + + init_style(); +} + +/* ---------------------------------------------------------------------- + mixing of pair potential prefactors (epsilon) +------------------------------------------------------------------------- */ + +double Pair::mix_energy(double eps1, double eps2, double sig1, double sig2) +{ + double value; + if (mix_flag == GEOMETRIC) + value = sqrt(eps1*eps2); + else if (mix_flag == ARITHMETIC) + value = sqrt(eps1*eps2); + else if (mix_flag == SIXTHPOWER) + value = 2.0 * sqrt(eps1*eps2) * + pow(sig1,3.0) * pow(sig2,3.0) / (pow(sig1,6.0) * pow(sig2,6.0)); + return value; +} + +/* ---------------------------------------------------------------------- + mixing of pair potential distances (sigma, cutoff) +------------------------------------------------------------------------- */ + +double Pair::mix_distance(double sig1, double sig2) +{ + double value; + if (mix_flag == GEOMETRIC) + value = sqrt(sig1*sig2); + else if (mix_flag == ARITHMETIC) + value = 0.5 * (sig1+sig2); + else if (mix_flag == SIXTHPOWER) + value = pow((0.5 * (pow(sig1,6.0) + pow(sig2,6.0))),1.0/6.0); + return value; +} + +/* ---------------------------------------------------------------------- + compute pair virial via pair own/ghost forces +------------------------------------------------------------------------- */ + +void Pair::virial_compute() +{ + double **x = atom->x; + double **f_pair = update->f_pair; + int nall = atom->nlocal + atom->nghost; + + // sum over own & ghost atoms + + for (int i = 0; i < nall; i++) { + virial[0] += f_pair[i][0]*x[i][0]; + virial[1] += f_pair[i][1]*x[i][1]; + virial[2] += f_pair[i][2]*x[i][2]; + virial[3] += f_pair[i][1]*x[i][0]; + virial[4] += f_pair[i][2]*x[i][0]; + virial[5] += f_pair[i][2]*x[i][1]; + } + + // add pair forces into total force + + double **f = atom->f; + + for (int i = 0; i < nall; i++) { + f[i][0] += f_pair[i][0]; + f[i][1] += f_pair[i][1]; + f[i][2] += f_pair[i][2]; + } +} + +/* ---------------------------------------------------------------------- + write a table of pair potential energy/force vs distance to a file +------------------------------------------------------------------------- */ + +void Pair::write_file(int narg, char **arg) +{ + if (narg < 8) error->all("Illegal pair_write command"); + if (single_enable == 0) error->all("Pair style does not support pair_write"); + + // parse arguments + + int itype = atoi(arg[0]); + int jtype = atoi(arg[1]); + if (itype < 1 || itype > atom->ntypes || jtype < 1 || jtype > atom->ntypes) + error->all("Invalid atom types in pair_write command"); + + int n = atoi(arg[2]); + + int style; + if (strcmp(arg[3],"r") == 0) style = R; + else if (strcmp(arg[3],"rsq") == 0) style = RSQ; + else if (strcmp(arg[3],"bitmap") == 0) style = BMP; + else error->all("Invalid style in pair_write command"); + + double inner = atof(arg[4]); + double outer = atof(arg[5]); + if (inner <= 0.0 || inner >= outer) + error->all("Invalid cutoffs in pair_write command"); + + // open file in append mode + // print header in format used by pair_style table + + int me; + MPI_Comm_rank(world,&me); + FILE *fp; + if (me == 0) { + fp = fopen(arg[6],"a"); + if (fp == NULL) error->one("Cannot open pair_write file"); + fprintf(fp,"# Pair potential %s for atom types %d %d: i,r,energy,force\n", + force->pair_style,itype,jtype); + if (style == R) + fprintf(fp,"\n%s\nN %d R %g %g\n\n",arg[7],n,inner,outer); + if (style == RSQ) + fprintf(fp,"\n%s\nN %d RSQ %g %g\n\n",arg[7],n,inner,outer); + } + + // setup dummy atom vecs for use by single() + + double q[2]; + q[0] = q[1] = 1.0; + if (narg == 10) { + q[0] = atof(arg[8]); + q[1] = atof(arg[9]); + } + double *qhold; + + double eamfp[2]; + eamfp[0] = eamfp[1] = 0.0; + double *eamfphold; + + // atom and pair styles that need dummy atom vectors + + int qflag = atom->charge_allow; + PairEAM *eampair = NULL; + if (eampair = (PairEAM *) force->pair_match("eam")); + else if (eampair = (PairEAM *) force->pair_match("eam/alloy")); + else if (eampair = (PairEAM *) force->pair_match("eam/fs")); + + // initialize potentials before evaluating pair potential + // insures all pair coeffs are set and force constants + + force->init(); + + // if pair style = soft, set prefactor used by single() + + Pair *anypair; + if (anypair = force->pair_match("soft")) { + ((PairSoft *) anypair)->prefactor[itype][jtype] = + ((PairSoft *) anypair)->prestop[itype][jtype]; + } + + // evaluate energy and force at each of N distances + // swap in dummy vecs before, swap them out after + + if (qflag) { + qhold = atom->q; + atom->q = q; + } + if (eampair) { + eamfphold = eampair->fp; + eampair->fp = eamfp; + } + + int masklo,maskhi,nmask,nshiftbits; + if (style == BMP) { + init_bitmap(inner,outer,n,masklo,maskhi,nmask,nshiftbits); + int ntable = 1 << n; + if (me == 0) + fprintf(fp,"\n%s\nN %d BITMAP %g %g\n\n",arg[7],ntable,inner,outer); + n = ntable; + } + + double r,e,f,rsq; + float rsq_float; + int *int_rsq = (int *) &rsq_float; + One one; + + for (int i = 0; i < n; i++) { + if (style == R) { + r = inner + (outer-inner) * i/(n-1); + rsq = r*r; + } else if (style == RSQ) { + rsq = inner*inner + (outer*outer - inner*inner) * i/(n-1); + r = sqrt(rsq); + } else if (style == BMP) { + *int_rsq = i << nshiftbits; + *int_rsq = *int_rsq | masklo; + if (rsq_float < inner*inner) { + *int_rsq = i << nshiftbits; + *int_rsq = *int_rsq | maskhi; + } + rsq = rsq_float; + r = sqrt(rsq); + } + + if (rsq < cutsq[itype][jtype]) { + single(0,1,itype,jtype,rsq,1.0,1.0,1,one); + e = one.eng_coul + one.eng_vdwl; + f = r * one.fforce; + } else e = f = 0.0; + if (me == 0) fprintf(fp,"%d %g %g %g\n",i+1,r,e,f); + } + + if (qflag) atom->q = qhold; + if (eampair) eampair->fp = eamfphold; + + if (me == 0) fclose(fp); +} + +/* ---------------------------------------------------------------------- + define bitmap parameters based on inner and outer cutoffs +------------------------------------------------------------------------- */ + +void Pair::init_bitmap(double inner, double outer, int ntablebits, + int &masklo, int &maskhi, int &nmask, int &nshiftbits) +{ + if (sizeof(int) != sizeof(float)) + error->all("Bitmapped lookup tables require int/float be same size"); + + if (ntablebits > sizeof(float)*CHAR_BIT) + error->all("Too many total bits for bitmapped lookup table"); + + if (inner >= outer) error->warning("Table inner cutoff >= outer cutoff"); + + int nlowermin = 1; + while (!((pow(double(2),nlowermin) <= inner*inner) && + (pow(double(2),nlowermin+1) > inner*inner))) { + if (pow(double(2),nlowermin) <= inner*inner) nlowermin++; + else nlowermin--; + } + + int nexpbits = 0; + double required_range = outer*outer / pow(double(2),nlowermin); + double available_range = 2.0; + + while (available_range < required_range) { + nexpbits++; + available_range = pow(double(2),pow(double(2),nexpbits)); + } + + int nmantbits = ntablebits - nexpbits; + + if (nexpbits > sizeof(float)*CHAR_BIT - FLT_MANT_DIG) + error->all("Too many exponent bits for lookup table"); + if (nmantbits+1 > FLT_MANT_DIG) + error->all("Too many mantissa bits for lookup table"); + if (nmantbits < 3) error->all("Too few bits for lookup table"); + + nshiftbits = FLT_MANT_DIG - (nmantbits+1); + + nmask = 1; + for (int j = 0; j < ntablebits+nshiftbits; j++) nmask *= 2; + nmask -= 1; + + float rsq; + int *int_rsq = (int *) &rsq; + rsq = outer*outer; + maskhi = *int_rsq & ~(nmask); + rsq = inner*inner; + masklo = *int_rsq & ~(nmask); +} diff --git a/src/pair.h b/src/pair.h new file mode 100644 index 0000000000000000000000000000000000000000..a0748e09f00d50e01d3fbf6a244245b9201e45a6 --- /dev/null +++ b/src/pair.h @@ -0,0 +1,91 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_H +#define PAIR_H + +#include "lammps.h" + +class Pair : public LAMMPS { + public: + // used by other classes + double cutforce; // max cutoff for all atom pairs + double eng_vdwl,eng_coul; // accumulated energies + double virial[6]; // accumulated virial + + // used by neighbor class + int neigh_half_every; // 0/1 = if needs half neighbor list + int neigh_full_every; // 0/1 = if needs full neighbor list + + // common to all pair classes + int allocated; // 0/1 = whether arrays are allocated + int **setflag; // 0/1 = whether each i,j has been set + double **cutsq; // max cutoff sq for each atom pair + + // pair modify settings + int offset_flag,mix_flag; // flags for offset and mixing + int tail_flag; // flag for LJ tail correction + double etail,ptail; // energy/pressure tail corrections + double etail_ij,ptail_ij; + int ncoultablebits; // size of Coulomb table + double tabinner; // inner cutoff for Coulomb table + + int single_enable; // 0/1 if single() routine exists + int respa_enable; // 0/1 if inner/middle/outer routines + int one_coeff; // 0/1 if allows only one coeff * * call + + struct One { // single interaction between 2 atoms + double fforce; + double eng_vdwl,eng_coul; + double fx,fy,fz; + double tix,tiy,tiz,tjx,tjy,tjz; + }; + + Pair(); + virtual ~Pair() {} + + void init(); + double mix_energy(double, double, double, double); + double mix_distance(double, double); + void virial_compute(); + void write_file(int, char **); + void init_bitmap(double, double, int, int &, int &, int &, int &); + + virtual void modify_params(int, char **); + + virtual void compute(int, int) = 0; + virtual void settings(int, char **) = 0; + virtual void coeff(int, char **) = 0; + + virtual double init_one(int, int) {return 0.0;} + virtual void init_style() {} + virtual void compute_inner() {} + virtual void compute_middle() {} + virtual void compute_outer(int, int) {} + virtual void single(int, int, int, int, + double, double, double, int, One &) {} + virtual void single_embed(int, int, double &, int, double &) {} + + virtual void write_restart(FILE *) {} + virtual void read_restart(FILE *) {} + virtual void write_restart_settings(FILE *) {} + virtual void read_restart_settings(FILE *) {} + + virtual int pack_comm(int, int *, double *, int *) {return 0;} + virtual void unpack_comm(int, int, double *) {} + virtual int pack_reverse_comm(int, int, double *) {return 0;} + virtual void unpack_reverse_comm(int, int *, double *) {} + virtual int memory_usage() {return 0;} +}; + +#endif diff --git a/src/pair_buck.cpp b/src/pair_buck.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c94a6a95a3d6a12f935b39e6604a2950cb7839f --- /dev/null +++ b/src/pair_buck.cpp @@ -0,0 +1,353 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "pair_buck.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +PairBuck::~PairBuck() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(cut); + memory->destroy_2d_double_array(a); + memory->destroy_2d_double_array(rho); + memory->destroy_2d_double_array(c); + memory->destroy_2d_double_array(rhoinv); + memory->destroy_2d_double_array(buck1); + memory->destroy_2d_double_array(buck2); + memory->destroy_2d_double_array(offset); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairBuck::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcebuck,fforce,factor_lj; + double phibuck,r,rexp; + int *neighs; + double **f; + + eng_vdwl = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_lj = 1.0; + else { + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r2inv = 1.0/rsq; + r6inv = r2inv*r2inv*r2inv; + r = sqrt(rsq); + rexp = exp(-r*rhoinv[itype][jtype]); + forcebuck = buck1[itype][jtype]*r*rexp - buck2[itype][jtype]*r6inv; + fforce = factor_lj*forcebuck*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) { + phibuck = a[itype][jtype]*rexp - c[itype][jtype]*r6inv - + offset[itype][jtype]; + if (newton_pair || j < nlocal) eng_vdwl += factor_lj*phibuck; + else eng_vdwl += 0.5*factor_lj*phibuck; + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairBuck::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"); + + cut = memory->create_2d_double_array(n+1,n+1,"pair:cut_lj"); + a = memory->create_2d_double_array(n+1,n+1,"pair:a"); + rho = memory->create_2d_double_array(n+1,n+1,"pair:rho"); + c = memory->create_2d_double_array(n+1,n+1,"pair:c"); + rhoinv = memory->create_2d_double_array(n+1,n+1,"pair:rhoinv"); + buck1 = memory->create_2d_double_array(n+1,n+1,"pair:buck1"); + buck2 = memory->create_2d_double_array(n+1,n+1,"pair:buck2"); + offset = memory->create_2d_double_array(n+1,n+1,"pair:offset"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairBuck::settings(int narg, char **arg) +{ + if (narg != 1) error->all("Illegal pair_style command"); + + cut_global = atof(arg[0]); + + // 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 PairBuck::coeff(int narg, char **arg) +{ + if (narg < 5 || narg > 6) 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); + + double a_one = atof(arg[2]); + double rho_one = atof(arg[3]); + double c_one = atof(arg[4]); + + double cut_one = cut_global; + if (narg == 6) cut_one = atof(arg[5]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + for (int j = MAX(jlo,i); j <= jhi; j++) { + a[i][j] = a_one; + rho[i][j] = rho_one; + c[i][j] = c_one; + cut[i][j] = cut_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairBuck::init_one(int i, int j) +{ + if (setflag[i][j] == 0) error->all("All pair coeffs are not set"); + + rhoinv[i][j] = 1.0/rho[i][j]; + buck1[i][j] = a[i][j]/rho[i][j]; + buck2[i][j] = 6.0*c[i][j]; + + if (offset_flag) { + double rexp = exp(-cut[i][j]/rho[i][j]); + offset[i][j] = a[i][j]*rexp - c[i][j]/pow(cut[i][j],6.0); + } else offset[i][j] = 0.0; + + a[j][i] = a[i][j]; + c[j][i] = c[i][j]; + rhoinv[j][i] = rhoinv[i][j]; + buck1[j][i] = buck1[i][j]; + buck2[j][i] = buck2[i][j]; + offset[j][i] = offset[i][j]; + + return cut[i][j]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairBuck::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + for (j = i; j <= atom->ntypes; j++) { + fwrite(&setflag[i][j],sizeof(int),1,fp); + if (setflag[i][j]) { + fwrite(&a[i][j],sizeof(double),1,fp); + fwrite(&rho[i][j],sizeof(double),1,fp); + fwrite(&c[i][j],sizeof(double),1,fp); + fwrite(&cut[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairBuck::read_restart(FILE *fp) +{ + read_restart_settings(fp); + + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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(&a[i][j],sizeof(double),1,fp); + fread(&rho[i][j],sizeof(double),1,fp); + fread(&c[i][j],sizeof(double),1,fp); + fread(&cut[i][j],sizeof(double),1,fp); + } + MPI_Bcast(&a[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&rho[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&c[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairBuck::write_restart_settings(FILE *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 PairBuck::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&cut_global,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + 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); +} + +/* ---------------------------------------------------------------------- */ + +void PairBuck::single(int i, int j, int itype, int jtype, + double rsq, double factor_coul, double factor_lj, + int eflag, One &one) +{ + double r2inv,r6inv,r,rexp,forcebuck,phibuck; + + r2inv = 1.0/rsq; + r6inv = r2inv*r2inv*r2inv; + r = sqrt(rsq); + rexp = exp(-r*rhoinv[itype][jtype]); + forcebuck = buck1[itype][jtype]*r*rexp - buck2[itype][jtype]*r6inv; + one.fforce = factor_lj*forcebuck*r2inv; + + if (eflag) { + phibuck = a[itype][jtype]*rexp - c[itype][jtype]*r6inv - + offset[itype][jtype]; + one.eng_vdwl = factor_lj*phibuck; + one.eng_coul = 0.0; + } +} diff --git a/src/pair_buck.h b/src/pair_buck.h new file mode 100644 index 0000000000000000000000000000000000000000..c512f23abb905ae17f2a12363a357e172a6afd18 --- /dev/null +++ b/src/pair_buck.h @@ -0,0 +1,42 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_BUCK_H +#define PAIR_BUCK_H + +#include "pair.h" + +class PairBuck : public Pair { + public: + PairBuck() {} + ~PairBuck(); + void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + void single(int, int, int, int, double, double, double, int, One &); + + private: + double cut_global; + double **cut; + double **a,**rho,**c; + double **rhoinv,**buck1,**buck2,**offset; + + void allocate(); +}; + +#endif diff --git a/src/pair_buck_coul_cut.cpp b/src/pair_buck_coul_cut.cpp new file mode 100644 index 0000000000000000000000000000000000000000..87212bdede0f667e3f822fd11d4bdbf8f7888a3e --- /dev/null +++ b/src/pair_buck_coul_cut.cpp @@ -0,0 +1,425 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Eduardo Bringa (LLNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "pair_buck_coul_cut.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +PairBuckCoulCut::~PairBuckCoulCut() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(cut_lj); + memory->destroy_2d_double_array(cut_ljsq); + memory->destroy_2d_double_array(cut_coul); + memory->destroy_2d_double_array(cut_coulsq); + memory->destroy_2d_double_array(a); + memory->destroy_2d_double_array(rho); + memory->destroy_2d_double_array(c); + memory->destroy_2d_double_array(rhoinv); + memory->destroy_2d_double_array(buck1); + memory->destroy_2d_double_array(buck2); + memory->destroy_2d_double_array(offset); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairBuckCoulCut::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcecoul,forcebuck,fforce,factor_coul,factor_lj; + double factor,phicoul,phibuck,r,rexp; + int *neighs; + double **f; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r2inv = 1.0/rsq; + + if (rsq < cut_coulsq[itype][jtype]) + forcecoul = qqrd2e * qtmp*q[j]*sqrt(r2inv); + else forcecoul = 0.0; + + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + r = sqrt(rsq); + rexp = exp(-r*rhoinv[itype][jtype]); + forcebuck = buck1[itype][jtype]*r*rexp - buck2[itype][jtype]*r6inv; + } else forcebuck = 0.0; + + fforce = (factor_coul*forcecoul + factor_lj*forcebuck) * 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 (newton_pair || j < nlocal) factor = 1.0; + else factor = 0.5; + if (rsq < cut_coulsq[itype][jtype]) { + phicoul = qqrd2e * qtmp*q[j]*sqrt(r2inv); + eng_coul += factor*factor_coul*phicoul; + } + if (rsq < cut_ljsq[itype][jtype]) { + phibuck = a[itype][jtype]*rexp - c[itype][jtype]*r6inv - + offset[itype][jtype]; + eng_vdwl += factor*factor_lj*phibuck; + } + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairBuckCoulCut::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"); + + cut_lj = memory->create_2d_double_array(n+1,n+1,"pair:cut_lj"); + cut_ljsq = memory->create_2d_double_array(n+1,n+1,"pair:cut_ljsq"); + cut_coul = memory->create_2d_double_array(n+1,n+1,"pair:cut_coul"); + cut_coulsq = memory->create_2d_double_array(n+1,n+1,"pair:cut_coulsq"); + a = memory->create_2d_double_array(n+1,n+1,"pair:a"); + rho = memory->create_2d_double_array(n+1,n+1,"pair:rho"); + c = memory->create_2d_double_array(n+1,n+1,"pair:c"); + rhoinv = memory->create_2d_double_array(n+1,n+1,"pair:rhoinv"); + buck1 = memory->create_2d_double_array(n+1,n+1,"pair:buck1"); + buck2 = memory->create_2d_double_array(n+1,n+1,"pair:buck2"); + offset = memory->create_2d_double_array(n+1,n+1,"pair:offset"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairBuckCoulCut::settings(int narg, char **arg) +{ + if (narg < 1 || narg > 2) error->all("Illegal pair_style command"); + + cut_lj_global = atof(arg[0]); + if (narg == 1) cut_coul_global = cut_lj_global; + else cut_coul_global = atof(arg[1]); + + // 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_lj[i][j] = cut_lj_global; + cut_coul[i][j] = cut_coul_global; + } + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairBuckCoulCut::coeff(int narg, char **arg) +{ + if (narg < 5 || narg > 7) 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); + + double a_one = atof(arg[2]); + double rho_one = atof(arg[3]); + double c_one = atof(arg[4]); + + double cut_lj_one = cut_lj_global; + double cut_coul_one = cut_coul_global; + if (narg >= 6) cut_coul_one = cut_lj_one = atof(arg[5]); + if (narg == 7) cut_coul_one = atof(arg[6]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + for (int j = MAX(jlo,i); j <= jhi; j++) { + a[i][j] = a_one; + rho[i][j] = rho_one; + c[i][j] = c_one; + cut_lj[i][j] = cut_lj_one; + cut_coul[i][j] = cut_coul_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairBuckCoulCut::init_one(int i, int j) +{ + if (setflag[i][j] == 0) error->all("All pair coeffs are not set"); + + double cut = MAX(cut_lj[i][j],cut_coul[i][j]); + cut_ljsq[i][j] = cut_lj[i][j] * cut_lj[i][j]; + cut_coulsq[i][j] = cut_coul[i][j] * cut_coul[i][j]; + + rhoinv[i][j] = 1.0/rho[i][j]; + buck1[i][j] = a[i][j]/rho[i][j]; + buck2[i][j] = 6.0*c[i][j]; + + if (offset_flag) { + double rexp = exp(-cut_lj[i][j]/rho[i][j]); + offset[i][j] = a[i][j]*rexp - c[i][j]/pow(cut_lj[i][j],6.0); + } else offset[i][j] = 0.0; + + cut_ljsq[j][i] = cut_ljsq[i][j]; + cut_coulsq[j][i] = cut_coulsq[i][j]; + a[j][i] = a[i][j]; + c[j][i] = c[i][j]; + rhoinv[j][i] = rhoinv[i][j]; + buck1[j][i] = buck1[i][j]; + buck2[j][i] = buck2[i][j]; + offset[j][i] = offset[i][j]; + + return cut; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairBuckCoulCut::init_style() +{ + // require an atom style with charge defined + + if (atom->charge_allow == 0) + error->all("Must use charged atom style with this pair style"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairBuckCoulCut::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + for (j = i; j <= atom->ntypes; j++) { + fwrite(&setflag[i][j],sizeof(int),1,fp); + if (setflag[i][j]) { + fwrite(&a[i][j],sizeof(double),1,fp); + fwrite(&rho[i][j],sizeof(double),1,fp); + fwrite(&c[i][j],sizeof(double),1,fp); + fwrite(&cut_lj[i][j],sizeof(double),1,fp); + fwrite(&cut_coul[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairBuckCoulCut::read_restart(FILE *fp) +{ + read_restart_settings(fp); + + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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(&a[i][j],sizeof(double),1,fp); + fread(&rho[i][j],sizeof(double),1,fp); + fread(&c[i][j],sizeof(double),1,fp); + fread(&cut_lj[i][j],sizeof(double),1,fp); + fread(&cut_coul[i][j],sizeof(double),1,fp); + } + MPI_Bcast(&a[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&rho[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&c[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_lj[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_coul[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairBuckCoulCut::write_restart_settings(FILE *fp) +{ + fwrite(&cut_lj_global,sizeof(double),1,fp); + fwrite(&cut_coul_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 PairBuckCoulCut::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&cut_lj_global,sizeof(double),1,fp); + fread(&cut_coul_global,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + MPI_Bcast(&cut_lj_global,1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_coul_global,1,MPI_DOUBLE,0,world); + MPI_Bcast(&offset_flag,1,MPI_INT,0,world); + MPI_Bcast(&mix_flag,1,MPI_INT,0,world); +} + +/* ---------------------------------------------------------------------- */ + +void PairBuckCoulCut::single(int i, int j, int itype, int jtype, + double rsq, double factor_coul, double factor_lj, + int eflag, One &one) +{ + double r2inv,r6inv,r,rexp,forcecoul,forcebuck,phicoul,phibuck; + + r2inv = 1.0/rsq; + if (rsq < cut_coulsq[itype][jtype]) + forcecoul = force->qqrd2e * atom->q[i]*atom->q[j]*sqrt(r2inv); + else forcecoul = 0.0; + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + r = sqrt(rsq); + rexp = exp(-r*rhoinv[itype][jtype]); + forcebuck = buck1[itype][jtype]*r*rexp - buck2[itype][jtype]*r6inv; + } else forcebuck = 0.0; + one.fforce = (factor_coul*forcecoul + factor_lj*forcebuck) * r2inv; + + if (eflag) { + if (rsq < cut_coulsq[itype][jtype]) { + phicoul = force->qqrd2e * atom->q[i]*atom->q[j]*sqrt(r2inv); + one.eng_coul = factor_coul*phicoul; + } else one.eng_coul = 0.0; + if (rsq < cut_ljsq[itype][jtype]) { + phibuck = a[itype][jtype]*rexp - c[itype][jtype]*r6inv - + offset[itype][jtype]; + one.eng_vdwl = factor_lj*phibuck; + } else one.eng_vdwl = 0.0; + } +} diff --git a/src/pair_buck_coul_cut.h b/src/pair_buck_coul_cut.h new file mode 100644 index 0000000000000000000000000000000000000000..4482fec8679791aaaeaba750d3c006327b693f43 --- /dev/null +++ b/src/pair_buck_coul_cut.h @@ -0,0 +1,44 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_BUCK_COUL_CUT_H +#define PAIR_BUCK_COUL_CUT_H + +#include "pair.h" + +class PairBuckCoulCut : public Pair { + public: + PairBuckCoulCut() {} + ~PairBuckCoulCut(); + void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void init_style(); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + void single(int, int, int, int, double, double, double, int, One &); + + private: + double cut_lj_global,cut_coul_global; + double **cut_lj,**cut_ljsq; + double **cut_coul,**cut_coulsq; + double **a,**rho,**c; + double **rhoinv,**buck1,**buck2,**offset; + + void allocate(); +}; + +#endif diff --git a/src/pair_buck_coul_long.cpp b/src/pair_buck_coul_long.cpp new file mode 100644 index 0000000000000000000000000000000000000000..450fcb5efa52c6e88ad1c333f0c0af15206d9182 --- /dev/null +++ b/src/pair_buck_coul_long.cpp @@ -0,0 +1,446 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "pair_buck_coul_long.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "kspace.h" +#include "update.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#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 + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +PairBuckCoulLong::~PairBuckCoulLong() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(cut_lj); + memory->destroy_2d_double_array(cut_ljsq); + memory->destroy_2d_double_array(a); + memory->destroy_2d_double_array(rho); + memory->destroy_2d_double_array(c); + memory->destroy_2d_double_array(rhoinv); + memory->destroy_2d_double_array(buck1); + memory->destroy_2d_double_array(buck2); + memory->destroy_2d_double_array(offset); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairBuckCoulLong::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcecoul,forcebuck,fforce,factor_coul,factor_lj; + double grij,expm2,prefactor,t,erfc; + double factor,phicoul,phibuck,r,rexp; + int *neighs; + double **f; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r2inv = 1.0/rsq; + + if (rsq < cut_coulsq) { + 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 = qqrd2e * qtmp*q[j]/r; + forcecoul = prefactor * (erfc + EWALD_F*grij*expm2); + if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*prefactor; + } else forcecoul = 0.0; + + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + r = sqrt(rsq); + rexp = exp(-r*rhoinv[itype][jtype]); + forcebuck = buck1[itype][jtype]*r*rexp - buck2[itype][jtype]*r6inv; + } else forcebuck = 0.0; + + fforce = (forcecoul + factor_lj*forcebuck) * 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 (newton_pair || j < nlocal) factor = 1.0; + else factor = 0.5; + if (rsq < cut_coulsq) { + phicoul = prefactor*erfc; + if (factor_coul < 1.0) phicoul -= (1.0-factor_coul)*prefactor; + eng_coul += factor*phicoul; + } + if (rsq < cut_ljsq[itype][jtype]) { + phibuck = a[itype][jtype]*rexp - c[itype][jtype]*r6inv - + offset[itype][jtype]; + eng_vdwl += factor*factor_lj*phibuck; + } + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairBuckCoulLong::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"); + + cut_lj = memory->create_2d_double_array(n+1,n+1,"pair:cut_lj"); + cut_ljsq = memory->create_2d_double_array(n+1,n+1,"pair:cut_ljsq"); + a = memory->create_2d_double_array(n+1,n+1,"pair:a"); + rho = memory->create_2d_double_array(n+1,n+1,"pair:rho"); + c = memory->create_2d_double_array(n+1,n+1,"pair:c"); + rhoinv = memory->create_2d_double_array(n+1,n+1,"pair:rhoinv"); + buck1 = memory->create_2d_double_array(n+1,n+1,"pair:buck1"); + buck2 = memory->create_2d_double_array(n+1,n+1,"pair:buck2"); + offset = memory->create_2d_double_array(n+1,n+1,"pair:offset"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairBuckCoulLong::settings(int narg, char **arg) +{ + if (narg < 1 || narg > 2) error->all("Illegal pair_style command"); + + cut_lj_global = atof(arg[0]); + if (narg == 1) cut_coul = cut_lj_global; + else cut_coul = atof(arg[1]); + + // 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_lj[i][j] = cut_lj_global; + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairBuckCoulLong::coeff(int narg, char **arg) +{ + if (narg < 5 || narg > 6) 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); + + double a_one = atof(arg[2]); + double rho_one = atof(arg[3]); + double c_one = atof(arg[4]); + + double cut_lj_one = cut_lj_global; + if (narg == 6) cut_lj_one = atof(arg[5]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + for (int j = MAX(jlo,i); j <= jhi; j++) { + a[i][j] = a_one; + rho[i][j] = rho_one; + c[i][j] = c_one; + cut_lj[i][j] = cut_lj_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairBuckCoulLong::init_one(int i, int j) +{ + if (setflag[i][j] == 0) error->all("All pair coeffs are not set"); + + double cut = MAX(cut_lj[i][j],cut_coul); + cut_ljsq[i][j] = cut_lj[i][j] * cut_lj[i][j]; + + rhoinv[i][j] = 1.0/rho[i][j]; + buck1[i][j] = a[i][j]/rho[i][j]; + buck2[i][j] = 6.0*c[i][j]; + + if (offset_flag) { + double rexp = exp(-cut_lj[i][j]/rho[i][j]); + offset[i][j] = a[i][j]*rexp - c[i][j]/pow(cut_lj[i][j],6.0); + } else offset[i][j] = 0.0; + + cut_ljsq[j][i] = cut_ljsq[i][j]; + a[j][i] = a[i][j]; + c[j][i] = c[i][j]; + rhoinv[j][i] = rhoinv[i][j]; + buck1[j][i] = buck1[i][j]; + buck2[j][i] = buck2[i][j]; + offset[j][i] = offset[i][j]; + + return cut; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairBuckCoulLong::init_style() +{ + // require an atom style with charge defined + + if (atom->charge_allow == 0) + error->all("Must use charged atom style with this pair style"); + + cut_coulsq = cut_coul * cut_coul; + + // insure use of KSpace long-range solver, set g_ewald + + if (force->kspace == NULL) + error->all("Pair style is incompatible with KSpace style"); + else if (strcmp(force->kspace_style,"ewald") == 0) + g_ewald = force->kspace->g_ewald; + else if (strcmp(force->kspace_style,"pppm") == 0) + g_ewald = force->kspace->g_ewald; + else error->all("Pair style is incompatible with KSpace style"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairBuckCoulLong::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + for (j = i; j <= atom->ntypes; j++) { + fwrite(&setflag[i][j],sizeof(int),1,fp); + if (setflag[i][j]) { + fwrite(&a[i][j],sizeof(double),1,fp); + fwrite(&rho[i][j],sizeof(double),1,fp); + fwrite(&c[i][j],sizeof(double),1,fp); + fwrite(&cut_lj[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairBuckCoulLong::read_restart(FILE *fp) +{ + read_restart_settings(fp); + + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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(&a[i][j],sizeof(double),1,fp); + fread(&rho[i][j],sizeof(double),1,fp); + fread(&c[i][j],sizeof(double),1,fp); + fread(&cut_lj[i][j],sizeof(double),1,fp); + } + MPI_Bcast(&a[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&rho[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&c[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_lj[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairBuckCoulLong::write_restart_settings(FILE *fp) +{ + fwrite(&cut_lj_global,sizeof(double),1,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 PairBuckCoulLong::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&cut_lj_global,sizeof(double),1,fp); + fread(&cut_coul,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + MPI_Bcast(&cut_lj_global,1,MPI_DOUBLE,0,world); + 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); +} + +/* ---------------------------------------------------------------------- */ + +void PairBuckCoulLong::single(int i, int j, int itype, int jtype, + double rsq, double factor_coul, double factor_lj, + int eflag, One &one) +{ + double r2inv,r6inv,r,rexp,grij,expm2,t,erfc,prefactor; + double forcecoul,forcebuck,phicoul,phibuck; + + r2inv = 1.0/rsq; + if (rsq < cut_coulsq) { + 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 forcecoul = 0.0; + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + r = sqrt(rsq); + rexp = exp(-r*rhoinv[itype][jtype]); + forcebuck = buck1[itype][jtype]*r*rexp - buck2[itype][jtype]*r6inv; + } else forcebuck = 0.0; + one.fforce = (forcecoul + factor_lj*forcebuck) * r2inv; + + if (eflag) { + if (rsq < cut_coulsq) { + phicoul = prefactor*erfc; + if (factor_coul < 1.0) phicoul -= (1.0-factor_coul)*prefactor; + one.eng_coul = phicoul; + } else one.eng_coul = 0.0; + if (rsq < cut_ljsq[itype][jtype]) { + phibuck = a[itype][jtype]*rexp - c[itype][jtype]*r6inv - + offset[itype][jtype]; + one.eng_vdwl = factor_lj*phibuck; + } else one.eng_vdwl = 0.0; + } +} diff --git a/src/pair_buck_coul_long.h b/src/pair_buck_coul_long.h new file mode 100644 index 0000000000000000000000000000000000000000..0f87844fa8d22fb4d737ac25acdc3370f494fff0 --- /dev/null +++ b/src/pair_buck_coul_long.h @@ -0,0 +1,47 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_BUCK_COUL_LONG_H +#define PAIR_BUCK_COUL_LONG_H + +#include "pair.h" + +class PairBuckCoulLong : public Pair { + public: + double cut_coul; + + PairBuckCoulLong() {} + ~PairBuckCoulLong(); + void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void init_style(); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + void single(int, int, int, int, double, double, double, int, One &); + + private: + double cut_lj_global; + double **cut_lj,**cut_ljsq; + double cut_coulsq; + double **a,**rho,**c; + double **rhoinv,**buck1,**buck2,**offset; + double g_ewald; + + void allocate(); +}; + +#endif diff --git a/src/pair_eam.cpp b/src/pair_eam.cpp new file mode 100644 index 0000000000000000000000000000000000000000..651d32dde8db2533eb1add31c940fa9bfdf34a06 --- /dev/null +++ b/src/pair_eam.cpp @@ -0,0 +1,927 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Stephen Foiles (SNL), Murray Daw (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "pair_eam.h" +#include "atom.h" +#include "force.h" +#include "update.h" +#include "comm.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +#define MAXLINE 1024 + +/* ---------------------------------------------------------------------- */ + +PairEAM::PairEAM() +{ + nmax = 0; + rho = NULL; + fp = NULL; + + ntables = 0; + tables = NULL; + frho = NULL; + frho_0 = NULL; + + // set rhor to NULL so memory deallocation will work + // even from derived classes that don't use rhor + + rhor = NULL; +} + +/* ---------------------------------------------------------------------- + free all arrays + check if allocated, since class can be destructed when incomplete +------------------------------------------------------------------------- */ + +PairEAM::~PairEAM() +{ + memory->sfree(rho); + memory->sfree(fp); + + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + memory->destroy_2d_int_array(tabindex); + } + + for (int m = 0; m < ntables; m++) { + delete [] tables[m].filename; + delete [] tables[m].frho; + delete [] tables[m].rhor; + delete [] tables[m].zr; + delete [] tables[m].z2r; + } + memory->sfree(tables); + + if (frho) { + memory->destroy_2d_double_array(frho); + memory->destroy_2d_double_array(rhor); + memory->destroy_2d_double_array(zrtmp); + memory->destroy_3d_double_array(z2r); + } + + if (frho_0) interpolate_deallocate(); +} + +/* ---------------------------------------------------------------------- */ + +void PairEAM::compute(int eflag, int vflag) +{ + int i,j,k,m,numneigh,itype,jtype; + double xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r,p,fforce,rhoip,rhojp,z2,z2p,recip,phi,phip,psip; + int *neighs; + double **f; + + // grow energy array if necessary + + if (atom->nmax > nmax) { + memory->sfree(rho); + memory->sfree(fp); + nmax = atom->nmax; + rho = (double *) memory->smalloc(nmax*sizeof(double),"eam:rho"); + fp = (double *) memory->smalloc(nmax*sizeof(double),"eam:fp"); + } + + eng_vdwl = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + int *type = atom->type; + int nlocal = atom->nlocal; + int newton_pair = force->newton_pair; + + // zero out density + + if (newton_pair) { + m = nlocal + atom->nghost; + for (i = 0; i < m; i++) rho[i] = 0.0; + } else for (i = 0; i < nlocal; i++) rho[i] = 0.0; + + // rho = density at each atom + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq < cutforcesq) { + jtype = type[j]; + p = sqrt(rsq)*rdr + 1.0; + m = static_cast<int> (p); + m = MIN(m,nr-1); + p -= m; + p = MIN(p,1.0); + rho[i] += ((rhor_3[jtype][m]*p + rhor_2[jtype][m])*p + + rhor_1[jtype][m])*p + rhor_0[jtype][m]; + if (newton_pair || j < nlocal) + rho[j] += ((rhor_3[itype][m]*p + rhor_2[itype][m])*p + + rhor_1[itype][m])*p + rhor_0[itype][m]; + } + } + } + + // communicate and sum densities + + if (newton_pair) comm->reverse_comm_pair(this); + + // fp = derivative of embedding energy at each atom + // phi = embedding energy at each atom + + for (i = 0; i < nlocal; i++) { + itype = type[i]; + p = rho[i]*rdrho + 1.0; + m = static_cast<int> (p); + m = MAX(1,MIN(m,nrho-1)); + p -= m; + p = MIN(p,1.0); + fp[i] = (frho_6[itype][m]*p + frho_5[itype][m])*p + frho_4[itype][m]; + if (eflag) { + phi = ((frho_3[itype][m]*p + frho_2[itype][m])*p + + frho_1[itype][m])*p + frho_0[itype][m]; + eng_vdwl += phi; + } + } + + // communicate derivative of embedding function + + comm->comm_pair(this); + + // compute forces on each atom + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq < cutforcesq) { + jtype = type[j]; + r = sqrt(rsq); + p = r*rdr + 1.0; + m = static_cast<int> (p); + m = MIN(m,nr-1); + p -= m; + p = MIN(p,1.0); + + // rhoip = derivative of (density at atom j due to atom i) + // rhojp = derivative of (density at atom i due to atom j) + // phi = pair potential energy + // phip = phi' + // z2 = phi * r + // z2p = (phi * r)' = (phi' r) + phi + // psip needs both fp[i] and fp[j] terms since r_ij appears in two + // terms of embed eng: Fi(sum rho_ij) and Fj(sum rho_ji) + // hence embed' = Fi(sum rho_ij) rhojp + Fj(sum rho_ji) rhoip + + rhoip = (rhor_6[itype][m]*p + rhor_5[itype][m])*p + + rhor_4[itype][m]; + rhojp = (rhor_6[jtype][m]*p + rhor_5[jtype][m])*p + + rhor_4[jtype][m]; + z2 = ((z2r_3[itype][jtype][m]*p + z2r_2[itype][jtype][m])*p + + z2r_1[itype][jtype][m])*p + z2r_0[itype][jtype][m]; + z2p = (z2r_6[itype][jtype][m]*p + z2r_5[itype][jtype][m])*p + + z2r_4[itype][jtype][m]; + + recip = 1.0/r; + phi = z2*recip; + phip = z2p*recip - phi*recip; + psip = fp[i]*rhojp + fp[j]*rhoip + phip; + fforce = psip*recip; + 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 (newton_pair || j < nlocal) eng_vdwl += phi; + else eng_vdwl += 0.5*phi; + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] -= 0.5*delx*delx*fforce; + virial[1] -= 0.5*dely*dely*fforce; + virial[2] -= 0.5*delz*delz*fforce; + virial[3] -= 0.5*delx*dely*fforce; + virial[4] -= 0.5*delx*delz*fforce; + virial[5] -= 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairEAM::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"); + tabindex = memory->create_2d_int_array(n+1,n+1,"pair:tabindex"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairEAM::settings(int narg, char **arg) +{ + if (narg > 0) error->all("Illegal pair_style command"); +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs + reading multiple funcfl files defines a funcfl alloy simulation +------------------------------------------------------------------------- */ + +void PairEAM::coeff(int narg, char **arg) +{ + if (!allocated) allocate(); + + if (narg != 3) error->all("Incorrect args for pair coefficients"); + + // parse pair of atom types + + int ilo,ihi,jlo,jhi; + force->bounds(arg[0],atom->ntypes,ilo,ihi); + force->bounds(arg[1],atom->ntypes,jlo,jhi); + + // read funcfl file only for i,i pairs + // only setflag i,i will be set + // set mass of each atom type + + int itable; + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + for (int j = MAX(jlo,i); j <= jhi; j++) { + if (i == j) { + itable = read_funcfl(arg[2]); + atom->set_mass(i,tables[itable].mass); + tabindex[i][i] = itable; + setflag[i][i] = 1; + count++; + } + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairEAM::init_one(int i, int j) +{ + // only setflag I,I was set by coeff + // mixing will occur in init_style if both I,I and J,J were set + + if (setflag[i][i] == 0 || setflag[j][j] == 0) + error->all("All EAM pair coeffs are not set"); + + // EAM has only one cutoff = max of all pairwise cutoffs + // determine max by checking table assigned to all type pairs + // only setflag[i][j] = 1 is relevant (if hybrid, some may not be set) + + cutmax = 0.0; + for (int ii = 1; ii <= atom->ntypes; ii++) { + for (int jj = ii; jj <= atom->ntypes; jj++) { + if (setflag[ii][jj] == 0) continue; + cutmax = MAX(cutmax,tables[tabindex[ii][jj]].cut); + } + } + + return cutmax; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairEAM::init_style() +{ + // set communication sizes in comm class + + comm->maxforward_pair = MAX(comm->maxforward_pair,1); + comm->maxreverse_pair = MAX(comm->maxreverse_pair,1); + + // convert read-in funcfl tables to multi-type setfl format and mix I,J + // interpolate final spline coeffs + + convert_funcfl(); + interpolate(); + + cutforcesq = cutmax*cutmax; +} + +/* ---------------------------------------------------------------------- + read potential values from a single element EAM file + read values into table and bcast values +------------------------------------------------------------------------- */ + +int PairEAM::read_funcfl(char *file) +{ + // check if same file has already been read + // if yes, return index of table entry + // if no, extend table list + + for (int i = 0; i < ntables; i++) + if (strcmp(file,tables->filename) == 0) return i; + + tables = (Table *) + memory->srealloc(tables,(ntables+1)*sizeof(Table),"pair:tables"); + + Table *tb = &tables[ntables]; + int n = strlen(file) + 1; + tb->filename = new char[n]; + strcpy(tb->filename,file); + tb->ith = tb->jth = 0; + + // open potential file + + int me = comm->me; + FILE *fp; + char line[MAXLINE]; + + if (me == 0) { + fp = fopen(file,"r"); + if (fp == NULL) { + char str[128]; + sprintf(str,"Cannot open EAM potential file %s",file); + error->one(str); + } + } + + // read and broadcast header + + int tmp; + if (me == 0) { + fgets(line,MAXLINE,fp); + fgets(line,MAXLINE,fp); + sscanf(line,"%d %lg",&tmp,&tb->mass); + fgets(line,MAXLINE,fp); + sscanf(line,"%d %lg %d %lg %lg", + &tb->nrho,&tb->drho,&tb->nr,&tb->dr,&tb->cut); + } + + MPI_Bcast(&tb->mass,1,MPI_DOUBLE,0,world); + MPI_Bcast(&tb->nrho,1,MPI_INT,0,world); + MPI_Bcast(&tb->drho,1,MPI_DOUBLE,0,world); + MPI_Bcast(&tb->nr,1,MPI_INT,0,world); + MPI_Bcast(&tb->dr,1,MPI_DOUBLE,0,world); + MPI_Bcast(&tb->cut,1,MPI_DOUBLE,0,world); + + // allocate potential arrays and read/bcast them + // set z2r to NULL (setfl array) so it can be deallocated + + tb->frho = new double[tb->nrho+1]; + tb->zr = new double[tb->nr+1]; + tb->rhor = new double[tb->nr+1]; + tb->z2r = NULL; + + if (me == 0) grab(fp,tb->nrho,&tb->frho[1]); + MPI_Bcast(&tb->frho[1],tb->nrho,MPI_DOUBLE,0,world); + + if (me == 0) grab(fp,tb->nr,&tb->zr[1]); + MPI_Bcast(&tb->zr[1],tb->nr,MPI_DOUBLE,0,world); + + if (me == 0) grab(fp,tb->nr,&tb->rhor[1]); + MPI_Bcast(&tb->rhor[1],tb->nr,MPI_DOUBLE,0,world); + + // close the potential file + + if (me == 0) fclose(fp); + + ntables++; + return ntables-1; +} + +/* ---------------------------------------------------------------------- + convert read-in funcfl potentials to multi-type setfl format +------------------------------------------------------------------------- */ + +void PairEAM::convert_funcfl() +{ + int i,j,k,m; + + int ntypes = atom->ntypes; + + // determine max values for all i,i type pairs + // skip if setflag = 0 (if hybrid, some may not be set) + + double rmax,rhomax; + dr = drho = rmax = rhomax = 0.0; + + for (int i = 1; i <= ntypes; i++) { + if (setflag[i][i] == 0) continue; + Table *tb = &tables[tabindex[i][i]]; + dr = MAX(dr,tb->dr); + drho = MAX(drho,tb->drho); + rmax = MAX(rmax,(tb->nr-1) * tb->dr); + rhomax = MAX(rhomax,(tb->nrho-1) * tb->drho); + } + + // set nr,nrho from cutoff and spacings + // 0.5 is for round-off in divide + + nr = static_cast<int> (rmax/dr + 0.5); + nrho = static_cast<int> (rhomax/drho + 0.5); + + // allocate multi-type setfl arrays + + if (frho) { + memory->destroy_2d_double_array(frho); + memory->destroy_2d_double_array(rhor); + memory->destroy_2d_double_array(zrtmp); + memory->destroy_3d_double_array(z2r); + } + + frho = (double **) + memory->create_2d_double_array(ntypes+1,nrho+1,"eam:frho"); + rhor = (double **) + memory->create_2d_double_array(ntypes+1,nr+1,"eam:rhor"); + zrtmp = (double **) + memory->create_2d_double_array(ntypes+1,nr+1,"eam:zrtmp"); + z2r = (double ***) + memory->create_3d_double_array(ntypes+1,ntypes+1,nr+1,"eam:frho"); + + // interpolate all potentials to a single grid and cutoff for all atom types + // frho,rhor are 1:ntypes, z2r is 1:ntypes,1:ntypes + // skip if setflag i,i or j,j = 0 (if hybrid, some may not be set) + + double r,p,cof1,cof2,cof3,cof4; + + for (i = 1; i <= ntypes; i++) { + if (setflag[i][i] == 0) continue; + Table *tb = &tables[tabindex[i][i]]; + for (m = 1; m <= nrho; m++) { + r = (m-1)*drho; + p = r/tb->drho + 1.0; + k = static_cast<int> (p); + k = MIN(k,tb->nrho-2); + k = MAX(k,2); + p -= k; + p = MIN(p,2.0); + cof1 = -0.166666667*p*(p-1.0)*(p-2.0); + cof2 = 0.5*(p*p-1.0)*(p-2.0); + cof3 = -0.5*p*(p+1.0)*(p-2.0); + cof4 = 0.166666667*p*(p*p-1.0); + frho[i][m] = cof1*tb->frho[k-1] + cof2*tb->frho[k] + + cof3*tb->frho[k+1] + cof4*tb->frho[k+2]; + } + } + + for (i = 1; i <= ntypes; i++) { + if (setflag[i][i] == 0) continue; + Table *tb = &tables[tabindex[i][i]]; + for (m = 1; m <= nr; m++) { + r = (m-1)*dr; + p = r/tb->dr + 1.0; + k = static_cast<int> (p); + k = MIN(k,tb->nr-2); + k = MAX(k,2); + p -= k; + p = MIN(p,2.0); + cof1 = -0.166666667*p*(p-1.0)*(p-2.0); + cof2 = 0.5*(p*p-1.0)*(p-2.0); + cof3 = -0.5*p*(p+1.0)*(p-2.0); + cof4 = 0.166666667*p*(p*p-1.0); + rhor[i][m] = cof1*tb->rhor[k-1] + cof2*tb->rhor[k] + + cof3*tb->rhor[k+1] + cof4*tb->rhor[k+2]; + zrtmp[i][m] = cof1*tb->zr[k-1] + cof2*tb->zr[k] + + cof3*tb->zr[k+1] + cof4*tb->zr[k+2]; + } + } + + for (i = 1; i <= ntypes; i++) + for (j = i; j <= ntypes; j++) { + if (setflag[i][i] == 0 || setflag[j][j] == 0) continue; + for (m = 1; m <= nr; m++) + z2r[i][j][m] = 27.2*0.529 * zrtmp[i][m]*zrtmp[j][m]; + } +} + +/* ---------------------------------------------------------------------- + interpolate EAM potentials +------------------------------------------------------------------------- */ + +void PairEAM::interpolate() +{ + // free memory from previous interpolation + + if (frho_0) interpolate_deallocate(); + + // interpolation spacings + + rdr = 1.0/dr; + rdrho = 1.0/drho; + + // allocate coeff arrays + + int n = atom->ntypes; + + frho_0 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_0"); + frho_1 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_1"); + frho_2 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_2"); + frho_3 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_3"); + frho_4 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_4"); + frho_5 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_5"); + frho_6 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_6"); + + rhor_0 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_0"); + rhor_1 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_1"); + rhor_2 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_2"); + rhor_3 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_3"); + rhor_4 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_4"); + rhor_5 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_5"); + rhor_6 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_6"); + + z2r_0 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_0"); + z2r_1 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_1"); + z2r_2 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_2"); + z2r_3 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_3"); + z2r_4 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_4"); + z2r_5 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_5"); + z2r_6 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_6"); + + // frho interpolation for 1:ntypes + // skip if setflag = 0 (if hybrid, some may not be set) + // if skip, set frho arrays to 0.0, since they will still be accessed + // for non-EAM atoms when compute() calculates embedding function + + int i,j,m; + + for (i = 1; i <= atom->ntypes; i++) { + if (setflag[i][i] == 0) { + for (j = 1; j <= n; j++) + for (m = 1; m <= nrho; m++) + frho_0[j][m] = frho_1[j][m] = frho_2[j][m] = frho_3[j][m] = + frho_4[j][m] = frho_5[j][m] = frho_6[j][m] = 0.0; + continue; + } + + for (m = 1; m <= nrho; m++) frho_0[i][m] = frho[i][m]; + + frho_1[i][1] = frho_0[i][2]-frho_0[i][1]; + frho_1[i][2] = 0.5*(frho_0[i][3]-frho_0[i][1]); + frho_1[i][nrho-1] = 0.5*(frho_0[i][nrho]-frho_0[i][nrho-2]); + frho_1[i][nrho] = frho_0[i][nrho]-frho_0[i][nrho-1]; + + for (m = 3; m <= nrho-2; m++) + frho_1[i][m] = ((frho_0[i][m-2]-frho_0[i][m+2]) + + 8.0*(frho_0[i][m+1]-frho_0[i][m-1]))/12.0; + + for (m = 1; m <= nrho-1; m++) { + frho_2[i][m] = 3.*(frho_0[i][m+1]-frho_0[i][m]) - + 2.0*frho_1[i][m] - frho_1[i][m+1]; + frho_3[i][m] = frho_1[i][m] + frho_1[i][m+1] - + 2.0*(frho_0[i][m+1]-frho_0[i][m]); + } + + frho_2[i][nrho] = 0.0; + frho_3[i][nrho] = 0.0; + + for (m = 1; m <= nrho; m++) { + frho_4[i][m] = frho_1[i][m]/drho; + frho_5[i][m] = 2.0*frho_2[i][m]/drho; + frho_6[i][m] = 3.0*frho_3[i][m]/drho; + } + } + + // rhor interpolation for 1:ntypes + // skip if setflag = 0 (if hybrid, some may not be set) + + for (i = 1; i <= atom->ntypes; i++) { + if (setflag[i][i] == 0) continue; + + for (m = 1; m <= nr; m++) rhor_0[i][m] = rhor[i][m]; + + rhor_1[i][1] = rhor_0[i][2]-rhor_0[i][1]; + rhor_1[i][2] = 0.5*(rhor_0[i][3]-rhor_0[i][1]); + rhor_1[i][nr-1] = 0.5*(rhor_0[i][nr]-rhor_0[i][nr-2]); + rhor_1[i][nr] = 0.0; + + for (m = 3; m <= nr-2; m++) + rhor_1[i][m] = ((rhor_0[i][m-2]-rhor_0[i][m+2]) + + 8.0*(rhor_0[i][m+1]-rhor_0[i][m-1]))/12.; + + for (m = 1; m <= nr-1; m++) { + rhor_2[i][m] = 3.0*(rhor_0[i][m+1]-rhor_0[i][m]) - + 2.0*rhor_1[i][m] - rhor_1[i][m+1]; + rhor_3[i][m] = rhor_1[i][m] + rhor_1[i][m+1] - + 2.0*(rhor_0[i][m+1]-rhor_0[i][m]); + } + + rhor_2[i][nr] = 0.0; + rhor_3[i][nr] = 0.0; + + for (m = 1; m <= nr; m++) { + rhor_4[i][m] = rhor_1[i][m]/dr; + rhor_5[i][m] = 2.0*rhor_2[i][m]/dr; + rhor_6[i][m] = 3.0*rhor_3[i][m]/dr; + } + } + + // z2r interpolation for 1:ntypes,1:ntypes + // skip if setflag i,i or j,j = 0 (if hybrid, some may not be set) + // set j,i coeffs = i,j coeffs + + for (i = 1; i <= atom->ntypes; i++) { + for (j = i; j <= atom->ntypes; j++) { + if (setflag[i][i] == 0 || setflag[j][j] == 0) continue; + + for (m = 1; m <= nr; m++) z2r_0[i][j][m] = z2r[i][j][m]; + + z2r_1[i][j][1] = z2r_0[i][j][2]-z2r_0[i][j][1]; + z2r_1[i][j][2] = 0.5*(z2r_0[i][j][3]-z2r_0[i][j][1]); + z2r_1[i][j][nr-1] = 0.5*(z2r_0[i][j][nr]-z2r_0[i][j][nr-2]); + z2r_1[i][j][nr] = 0.0; + + for (m = 3; m <= nr-2; m++) + z2r_1[i][j][m] = ((z2r_0[i][j][m-2]-z2r_0[i][j][m+2]) + + 8.0*(z2r_0[i][j][m+1]-z2r_0[i][j][m-1]))/12.; + + for (m = 1; m <= nr-1; m++) { + z2r_2[i][j][m] = 3.0*(z2r_0[i][j][m+1]-z2r_0[i][j][m]) - + 2.0*z2r_1[i][j][m] - z2r_1[i][j][m+1]; + z2r_3[i][j][m] = z2r_1[i][j][m] + z2r_1[i][j][m+1] - + 2.0*(z2r_0[i][j][m+1]-z2r_0[i][j][m]); + } + + z2r_2[i][j][nr] = 0.0; + z2r_3[i][j][nr] = 0.0; + + for (m = 1; m <= nr; m++) { + z2r_4[i][j][m] = z2r_1[i][j][m]/dr; + z2r_5[i][j][m] = 2.0*z2r_2[i][j][m]/dr; + z2r_6[i][j][m] = 3.0*z2r_3[i][j][m]/dr; + } + + for (m = 1; m <= nr; m++) { + z2r_0[j][i][m] = z2r_0[i][j][m]; + z2r_1[j][i][m] = z2r_1[i][j][m]; + z2r_2[j][i][m] = z2r_2[i][j][m]; + z2r_3[j][i][m] = z2r_3[i][j][m]; + z2r_4[j][i][m] = z2r_4[i][j][m]; + z2r_5[j][i][m] = z2r_5[i][j][m]; + z2r_6[j][i][m] = z2r_6[i][j][m]; + } + } + } +} + +/* ---------------------------------------------------------------------- + grab n values from file fp and put them in list + values can be several to a line + only called by proc 0 +------------------------------------------------------------------------- */ + +void PairEAM::grab(FILE *fp, int n, double *list) +{ + char *ptr; + char line[MAXLINE]; + + int i = 0; + while (i < n) { + fgets(line,MAXLINE,fp); + ptr = strtok(line," \t\n\r\f"); + list[i++] = atof(ptr); + while (ptr = strtok(NULL," \t\n\r\f")) list[i++] = atof(ptr); + } +} + +/* ---------------------------------------------------------------------- + skip n values from file fp + values can be several to a line + only called by proc 0 +------------------------------------------------------------------------- */ + +void PairEAM::skip(FILE *fp, int n) +{ + char line[MAXLINE]; + + int i = 0; + while (i < n) { + fgets(line,MAXLINE,fp); + strtok(line," \t\n\r\f"); + i++; + while (strtok(NULL," \t\n\r\f")) i++; + } +} + +/* ---------------------------------------------------------------------- + deallocate spline interpolation arrays +------------------------------------------------------------------------- */ + +void PairEAM::interpolate_deallocate() +{ + memory->destroy_2d_double_array(frho_0); + memory->destroy_2d_double_array(frho_1); + memory->destroy_2d_double_array(frho_2); + memory->destroy_2d_double_array(frho_3); + memory->destroy_2d_double_array(frho_4); + memory->destroy_2d_double_array(frho_5); + memory->destroy_2d_double_array(frho_6); + + memory->destroy_2d_double_array(rhor_0); + memory->destroy_2d_double_array(rhor_1); + memory->destroy_2d_double_array(rhor_2); + memory->destroy_2d_double_array(rhor_3); + memory->destroy_2d_double_array(rhor_4); + memory->destroy_2d_double_array(rhor_5); + memory->destroy_2d_double_array(rhor_6); + + memory->destroy_3d_double_array(z2r_0); + memory->destroy_3d_double_array(z2r_1); + memory->destroy_3d_double_array(z2r_2); + memory->destroy_3d_double_array(z2r_3); + memory->destroy_3d_double_array(z2r_4); + memory->destroy_3d_double_array(z2r_5); + memory->destroy_3d_double_array(z2r_6); +} + +/* ---------------------------------------------------------------------- */ + +void PairEAM::single(int i, int j, int itype, int jtype, + double rsq, double factor_coul, double factor_lj, + int eflag, One &one) +{ + double r,p,rhoip,rhojp,z2,z2p,recip,phi,phip,psip; + int m; + + r = sqrt(rsq); + p = r*rdr + 1.0; + m = static_cast<int> (p); + m = MIN(m,nr-1); + p -= m; + p = MIN(p,1.0); + + rhoip = (rhor_6[itype][m]*p + rhor_5[itype][m])*p + + rhor_4[itype][m]; + rhojp = (rhor_6[jtype][m]*p + rhor_5[jtype][m])*p + + rhor_4[jtype][m]; + z2 = ((z2r_3[itype][jtype][m]*p + z2r_2[itype][jtype][m])*p + + z2r_1[itype][jtype][m])*p + z2r_0[itype][jtype][m]; + z2p = (z2r_6[itype][jtype][m]*p + z2r_5[itype][jtype][m])*p + + z2r_4[itype][jtype][m]; + + recip = 1.0/r; + phi = z2*recip; + phip = z2p*recip - phi*recip; + psip = fp[i]*rhojp + fp[j]*rhoip + phip; + one.fforce = -psip*recip; + + if (eflag) { + one.eng_vdwl = phi; + one.eng_coul = 0.0; + } +} + +/* ---------------------------------------------------------------------- */ + +void PairEAM::single_embed(int i, int itype, double &fpi, + int eflag, double &phi) +{ + double p = rho[i]*rdrho + 1.0; + int m = static_cast<int> (p); + m = MAX(1,MIN(m,nrho-1)); + p -= m; + + fpi = (frho_6[itype][m]*p + frho_5[itype][m])*p + frho_4[itype][m]; + if (eflag) + phi = ((frho_3[itype][m]*p + frho_2[itype][m])*p + + frho_1[itype][m])*p + frho_0[itype][m]; +} + +/* ---------------------------------------------------------------------- */ + +int PairEAM::pack_comm(int n, int *list, double *buf, int *pbc_flags) +{ + int i,j,m; + + m = 0; + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = fp[j]; + } + return 1; +} + +/* ---------------------------------------------------------------------- */ + +void PairEAM::unpack_comm(int n, int first, double *buf) +{ + int i,m,last; + + m = 0; + last = first + n; + for (i = first; i < last; i++) fp[i] = buf[m++]; +} + +/* ---------------------------------------------------------------------- */ + +int PairEAM::pack_reverse_comm(int n, int first, double *buf) +{ + int i,m,last; + + m = 0; + last = first + n; + for (i = first; i < last; i++) buf[m++] = rho[i]; + return 1; +} + +/* ---------------------------------------------------------------------- */ + +void PairEAM::unpack_reverse_comm(int n, int *list, double *buf) +{ + int i,j,m; + + m = 0; + for (i = 0; i < n; i++) { + j = list[i]; + rho[j] += buf[m++]; + } +} + +/* ---------------------------------------------------------------------- + memory usage of local atom-based arrays +------------------------------------------------------------------------- */ + +int PairEAM::memory_usage() +{ + int bytes = 2 * nmax * sizeof(double); + return bytes; +} diff --git a/src/pair_eam.h b/src/pair_eam.h new file mode 100644 index 0000000000000000000000000000000000000000..f5a2f6c5a06b62ac2735a2d53a92ab19dad42a46 --- /dev/null +++ b/src/pair_eam.h @@ -0,0 +1,84 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_EAM_H +#define PAIR_EAM_H + +#include "pair.h" + +class PairEAM : public Pair { + friend class FixEnergy; + + public: + double *rho,*fp; + int nmax; + + PairEAM(); + virtual ~PairEAM(); + virtual void compute(int, int); + void settings(int, char **); + virtual void coeff(int, char **); + virtual double init_one(int, int); + virtual void init_style(); + virtual void single(int, int, int, int, double, double, double, int, One &); + + int pack_comm(int, int *, double *, int *); + void unpack_comm(int, int, double *); + int pack_reverse_comm(int, int, double *); + void unpack_reverse_comm(int, int *, double *); + int memory_usage(); + + protected: + double cutforcesq,cutmax; + int **tabindex; + + // potential as read in from file + + struct Table { + char *filename; // file it was read from + int ith,jth; // for setfl, which i,j entry in file + int nrho,nr; // array lengths + double drho,dr,cut,mass; // array spacings and cutoff, mass + double *frho,*rhor; // array values + double *zr,*z2r; // zr set for funcfl, z2r set for setfl + }; + int ntables; + Table *tables; + + // potential stored in multi-type setfl format + // created from read-in tables + + int nrho,nr; + double drho,dr; + double **frho,**rhor,**zrtmp; + double ***z2r; + + // potential in spline form used for force computation + // created from multi-type setfl format by interpolate() + + double rdr,rdrho; + double **rhor_0,**rhor_1,**rhor_2,**rhor_3,**rhor_4,**rhor_5,**rhor_6; + double **frho_0,**frho_1,**frho_2,**frho_3,**frho_4,**frho_5,**frho_6; + double ***z2r_0,***z2r_1,***z2r_2,***z2r_3,***z2r_4,***z2r_5,***z2r_6; + + void allocate(); + int read_funcfl(char *); + void convert_funcfl(); + virtual void interpolate(); + virtual void interpolate_deallocate(); + void grab(FILE *, int, double *); + void skip(FILE *, int); + void single_embed(int, int, double &, int, double &); +}; + +#endif diff --git a/src/pair_eam_alloy.cpp b/src/pair_eam_alloy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ccd832c6ffb1ce63d6fceb89e6301a86013ae833 --- /dev/null +++ b/src/pair_eam_alloy.cpp @@ -0,0 +1,472 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Stephen Foiles (SNL), Murray Daw (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "pair_eam_alloy.h" +#include "atom.h" +#include "force.h" +#include "comm.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +#define MAXLINE 1024 + +/* ---------------------------------------------------------------------- */ + +PairEAMAlloy::PairEAMAlloy() +{ + one_coeff = 1; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairEAMAlloy::coeff(int narg, char **arg) +{ + if (!allocated) allocate(); + + if (narg != 3 + atom->ntypes) + error->all("Incorrect args for pair coefficients"); + + // insure I,J args are * * + + if (strcmp(arg[0],"*") != 0 || strcmp(arg[1],"*") != 0) + error->all("Incorrect args for pair coefficients"); + + // read EAM setfl file, possibly multiple times + // first clear setflag since are doing this once for I,J = *,* + // read for all i,j pairs where ith,jth mapping is non-zero + // set setflag i,j for non-zero pairs + // set mass of atom type if i = j + + int n = atom->ntypes; + for (int i = 1; i <= n; i++) + for (int j = i; j <= n; j++) + setflag[i][j] = 0; + + int itable,ith,jth; + int ilo,ihi,jlo,jhi; + ilo = jlo = 1; + ihi = jhi = n; + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + for (int j = MAX(jlo,i); j <= jhi; j++) { + ith = atoi(arg[2+i]); + jth = atoi(arg[2+j]); + if (ith > 0 && jth > 0) { + itable = read_setfl(arg[2],ith,jth); + if (i == j) atom->set_mass(i,tables[itable].mass); + tabindex[i][j] = itable; + setflag[i][j] = 1; + count++; + } + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairEAMAlloy::init_one(int i, int j) +{ + if (setflag[i][j] == 0) + error->all("All EAM pair coeffs are not set"); + + // EAM has only one cutoff = max of all pairwise cutoffs + // determine max by checking table assigned to all type pairs + // only setflag[i][j] = 1 is relevant (if hybrid, some may not be set) + + cutmax = 0.0; + for (int ii = 1; ii <= atom->ntypes; ii++) { + for (int jj = ii; jj <= atom->ntypes; jj++) { + if (setflag[ii][jj] == 0) continue; + cutmax = MAX(cutmax,tables[tabindex[ii][jj]].cut); + } + } + + return cutmax; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairEAMAlloy::init_style() +{ + // set communication sizes in comm class + + comm->maxforward_pair = MAX(comm->maxforward_pair,1); + comm->maxreverse_pair = MAX(comm->maxreverse_pair,1); + + // copy read-in-tables to multi-type setfl format + // interpolate final spline coeffs + + store_setfl(); + interpolate(); + + cutforcesq = cutmax*cutmax; +} + +/* ---------------------------------------------------------------------- + read ith,jth potential values from a multi-element alloy EAM file + read values into table and bcast values +------------------------------------------------------------------------- */ + +int PairEAMAlloy::read_setfl(char *file, int ith, int jth) +{ + // check if ith,jth portion of same file has already been read + // if yes, return index of table entry + // if no, extend table list + + for (int i = 0; i < ntables; i++) + if (ith == tables[i].ith && jth == tables[i].jth) return i; + + tables = (Table *) + memory->srealloc(tables,(ntables+1)*sizeof(Table),"pair:tables"); + + Table *tb = &tables[ntables]; + int n = strlen(file) + 1; + tb->filename = new char[n]; + strcpy(tb->filename,file); + tb->ith = ith; + tb->jth = jth; + + // open potential file + + int me = comm->me; + FILE *fp; + char line[MAXLINE]; + + if (me == 0) { + fp = fopen(file,"r"); + if (fp == NULL) { + char str[128]; + sprintf(str,"Cannot open EAM potential file %s",file); + error->one(str); + } + } + + // read and broadcast header + + int ntypes; + if (me == 0) { + fgets(line,MAXLINE,fp); + fgets(line,MAXLINE,fp); + fgets(line,MAXLINE,fp); + fgets(line,MAXLINE,fp); + sscanf(line,"%d",&ntypes); + fgets(line,MAXLINE,fp); + sscanf(line,"%d %lg %d %lg %lg", + &tb->nrho,&tb->drho,&tb->nr,&tb->dr,&tb->cut); + } + + MPI_Bcast(&ntypes,1,MPI_INT,0,world); + MPI_Bcast(&tb->nrho,1,MPI_INT,0,world); + MPI_Bcast(&tb->drho,1,MPI_DOUBLE,0,world); + MPI_Bcast(&tb->nr,1,MPI_INT,0,world); + MPI_Bcast(&tb->dr,1,MPI_DOUBLE,0,world); + MPI_Bcast(&tb->cut,1,MPI_DOUBLE,0,world); + + // check if ith,jth are consistent with ntypes + + if (ith > ntypes || jth > ntypes) + error->all("Requested atom types in EAM setfl file do not exist"); + + // allocate potential arrays and read/bcast them + // skip sections of file that don't correspond to ith,jth + // extract mass, frho, rhor for i,i from ith element section + // extract z2r for i,j from ith,jth array of z2r section + // note that ith can be < or > than jth + // set zr to NULL (funcl array) so it can be deallocated + + tb->frho = new double[tb->nrho+1]; + tb->rhor = new double[tb->nr+1]; + tb->z2r = new double[tb->nr+1]; + tb->zr = NULL; + + int i,j,tmp; + double mass; + + for (i = 1; i <= ntypes; i++) { + if (me == 0) { + fgets(line,MAXLINE,fp); + sscanf(line,"%d %lg",&tmp,&mass); + } + MPI_Bcast(&mass,1,MPI_DOUBLE,0,world); + + if (i == ith && ith == jth) { + tb->mass = mass; + if (me == 0) grab(fp,tb->nrho,&tb->frho[1]); + MPI_Bcast(&tb->frho[1],tb->nrho,MPI_DOUBLE,0,world); + if (me == 0) grab(fp,tb->nr,&tb->rhor[1]); + MPI_Bcast(&tb->rhor[1],tb->nr,MPI_DOUBLE,0,world); + } else { + if (me == 0) skip(fp,tb->nrho); + if (me == 0) skip(fp,tb->nr); + } + } + + for (i = 1; i <= ntypes; i++) { + for (j = 1; j <= i; j++) { + if ((i == ith && j == jth) || (j == ith && i == jth)) { + if (me == 0) grab(fp,tb->nr,&tb->z2r[1]); + MPI_Bcast(&tb->z2r[1],tb->nr,MPI_DOUBLE,0,world); + } else if (me == 0) skip(fp,tb->nr); + } + } + + // close the potential file + + if (me == 0) fclose(fp); + + ntables++; + return ntables-1; +} + +/* ---------------------------------------------------------------------- + store read-in setfl values in multi-type setfl format +------------------------------------------------------------------------- */ + +void PairEAMAlloy::store_setfl() +{ + int i,j,m; + + int ntypes = atom->ntypes; + + // set nrho,nr,drho,dr from any i,i table entry since all the same + + for (i = 1; i <= ntypes; i++) + if (setflag[i][i]) break; + + nrho = tables[tabindex[i][i]].nrho; + nr = tables[tabindex[i][i]].nr; + drho = tables[tabindex[i][i]].drho; + dr = tables[tabindex[i][i]].dr; + + // allocate multi-type setfl arrays + + if (frho) { + memory->destroy_2d_double_array(frho); + memory->destroy_2d_double_array(rhor); + memory->destroy_2d_double_array(zrtmp); + memory->destroy_3d_double_array(z2r); + } + + frho = (double **) + memory->create_2d_double_array(ntypes+1,nrho+1,"eam:frho"); + rhor = (double **) + memory->create_2d_double_array(ntypes+1,nr+1,"eam:rhor"); + zrtmp = (double **) + memory->create_2d_double_array(ntypes+1,nr+1,"eam:zrtmp"); + z2r = (double ***) + memory->create_3d_double_array(ntypes+1,ntypes+1,nr+1,"eam:frho"); + + // copy from read-in tables to multi-type setfl arrays + // frho,rhor are 1:ntypes, z2r is 1:ntypes,1:ntypes + // skip if setflag i,j = 0 (if hybrid, some may not be set) + + for (i = 1; i <= ntypes; i++) + for (j = i; j <= ntypes; j++) { + if (setflag[i][j] == 0) continue; + Table *tb = &tables[tabindex[i][j]]; + if (i == j) { + for (m = 1; m <= nrho; m++) frho[i][m] = tb->frho[m]; + for (m = 1; m <= nr; m++) rhor[i][m] = tb->rhor[m]; + } + for (m = 1; m <= nr; m++) z2r[i][j][m] = tb->z2r[m]; + } +} + +/* ---------------------------------------------------------------------- + interpolate EAM potentials +------------------------------------------------------------------------- */ + +void PairEAMAlloy::interpolate() +{ + // free memory from previous interpolation + + if (frho_0) interpolate_deallocate(); + + // interpolation spacings + + rdr = 1.0/dr; + rdrho = 1.0/drho; + + // allocate coeff arrays + + int n = atom->ntypes; + + frho_0 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_0"); + frho_1 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_1"); + frho_2 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_2"); + frho_3 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_3"); + frho_4 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_4"); + frho_5 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_5"); + frho_6 = memory->create_2d_double_array(n+1,nrho+1,"eam:frho_6"); + + rhor_0 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_0"); + rhor_1 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_1"); + rhor_2 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_2"); + rhor_3 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_3"); + rhor_4 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_4"); + rhor_5 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_5"); + rhor_6 = memory->create_2d_double_array(n+1,nr+1,"eam:rhor_6"); + + z2r_0 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_0"); + z2r_1 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_1"); + z2r_2 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_2"); + z2r_3 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_3"); + z2r_4 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_4"); + z2r_5 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_5"); + z2r_6 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam:z2r_6"); + + // frho interpolation for 1:ntypes + // skip if setflag = 0 (if hybrid, some may not be set) + // if skip, set frho arrays to 0.0, since they will still be accessed + // for non-EAM atoms when compute() calculates embedding function + + int i,j,m; + + for (i = 1; i <= atom->ntypes; i++) { + if (setflag[i][i] == 0) { + for (m = 1; m <= nrho; m++) + frho_0[i][m] = frho_1[i][m] = frho_2[i][m] = frho_3[i][m] = + frho_4[i][m] = frho_5[i][m] = frho_6[i][m] = 0.0; + continue; + } + + for (m = 1; m <= nrho; m++) frho_0[i][m] = frho[i][m]; + + frho_1[i][1] = frho_0[i][2]-frho_0[i][1]; + frho_1[i][2] = 0.5*(frho_0[i][3]-frho_0[i][1]); + frho_1[i][nrho-1] = 0.5*(frho_0[i][nrho]-frho_0[i][nrho-2]); + frho_1[i][nrho] = frho_0[i][nrho]-frho_0[i][nrho-1]; + + for (m = 3; m <= nrho-2; m++) + frho_1[i][m] = ((frho_0[i][m-2]-frho_0[i][m+2]) + + 8.0*(frho_0[i][m+1]-frho_0[i][m-1]))/12.0; + + for (m = 1; m <= nrho-1; m++) { + frho_2[i][m] = 3.*(frho_0[i][m+1]-frho_0[i][m]) - + 2.0*frho_1[i][m] - frho_1[i][m+1]; + frho_3[i][m] = frho_1[i][m] + frho_1[i][m+1] - + 2.0*(frho_0[i][m+1]-frho_0[i][m]); + } + + frho_2[i][nrho] = 0.0; + frho_3[i][nrho] = 0.0; + + for (m = 1; m <= nrho; m++) { + frho_4[i][m] = frho_1[i][m]/drho; + frho_5[i][m] = 2.0*frho_2[i][m]/drho; + frho_6[i][m] = 3.0*frho_3[i][m]/drho; + } + } + + // rhor interpolation for 1:ntypes + // skip if setflag = 0 (if hybrid, some may not be set) + + for (i = 1; i <= atom->ntypes; i++) { + if (setflag[i][i] == 0) continue; + + for (m = 1; m <= nr; m++) rhor_0[i][m] = rhor[i][m]; + + rhor_1[i][1] = rhor_0[i][2]-rhor_0[i][1]; + rhor_1[i][2] = 0.5*(rhor_0[i][3]-rhor_0[i][1]); + rhor_1[i][nr-1] = 0.5*(rhor_0[i][nr]-rhor_0[i][nr-2]); + rhor_1[i][nr] = 0.0; + + for (m = 3; m <= nr-2; m++) + rhor_1[i][m] = ((rhor_0[i][m-2]-rhor_0[i][m+2]) + + 8.0*(rhor_0[i][m+1]-rhor_0[i][m-1]))/12.; + + for (m = 1; m <= nr-1; m++) { + rhor_2[i][m] = 3.0*(rhor_0[i][m+1]-rhor_0[i][m]) - + 2.0*rhor_1[i][m] - rhor_1[i][m+1]; + rhor_3[i][m] = rhor_1[i][m] + rhor_1[i][m+1] - + 2.0*(rhor_0[i][m+1]-rhor_0[i][m]); + } + + rhor_2[i][nr] = 0.0; + rhor_3[i][nr] = 0.0; + + for (m = 1; m <= nr; m++) { + rhor_4[i][m] = rhor_1[i][m]/dr; + rhor_5[i][m] = 2.0*rhor_2[i][m]/dr; + rhor_6[i][m] = 3.0*rhor_3[i][m]/dr; + } + } + + // z2r interpolation for 1:ntypes,1:ntypes + // skip if setflag i,j = 0 (if hybrid, some may not be set) + // set j,i coeffs = i,j coeffs + + for (i = 1; i <= atom->ntypes; i++) { + for (j = i; j <= atom->ntypes; j++) { + if (setflag[i][j] == 0) continue; + + for (m = 1; m <= nr; m++) z2r_0[i][j][m] = z2r[i][j][m]; + + z2r_1[i][j][1] = z2r_0[i][j][2]-z2r_0[i][j][1]; + z2r_1[i][j][2] = 0.5*(z2r_0[i][j][3]-z2r_0[i][j][1]); + z2r_1[i][j][nr-1] = 0.5*(z2r_0[i][j][nr]-z2r_0[i][j][nr-2]); + z2r_1[i][j][nr] = 0.0; + + for (m = 3; m <= nr-2; m++) + z2r_1[i][j][m] = ((z2r_0[i][j][m-2]-z2r_0[i][j][m+2]) + + 8.0*(z2r_0[i][j][m+1]-z2r_0[i][j][m-1]))/12.; + + for (m = 1; m <= nr-1; m++) { + z2r_2[i][j][m] = 3.0*(z2r_0[i][j][m+1]-z2r_0[i][j][m]) - + 2.0*z2r_1[i][j][m] - z2r_1[i][j][m+1]; + z2r_3[i][j][m] = z2r_1[i][j][m] + z2r_1[i][j][m+1] - + 2.0*(z2r_0[i][j][m+1]-z2r_0[i][j][m]); + } + + z2r_2[i][j][nr] = 0.0; + z2r_3[i][j][nr] = 0.0; + + for (m = 1; m <= nr; m++) { + z2r_4[i][j][m] = z2r_1[i][j][m]/dr; + z2r_5[i][j][m] = 2.0*z2r_2[i][j][m]/dr; + z2r_6[i][j][m] = 3.0*z2r_3[i][j][m]/dr; + } + + for (m = 1; m <= nr; m++) { + z2r_0[j][i][m] = z2r_0[i][j][m]; + z2r_1[j][i][m] = z2r_1[i][j][m]; + z2r_2[j][i][m] = z2r_2[i][j][m]; + z2r_3[j][i][m] = z2r_3[i][j][m]; + z2r_4[j][i][m] = z2r_4[i][j][m]; + z2r_5[j][i][m] = z2r_5[i][j][m]; + z2r_6[j][i][m] = z2r_6[i][j][m]; + } + } + } +} diff --git a/src/pair_eam_alloy.h b/src/pair_eam_alloy.h new file mode 100644 index 0000000000000000000000000000000000000000..9296baff48d30cf548624b489f9d8eb2e198b22d --- /dev/null +++ b/src/pair_eam_alloy.h @@ -0,0 +1,32 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_EAM_ALLOY_H +#define PAIR_EAM_ALLOY_H + +#include "pair_eam.h" + +class PairEAMAlloy : public PairEAM { + public: + PairEAMAlloy(); + void coeff(int, char **); + double init_one(int, int); + void init_style(); + + private: + int read_setfl(char *, int, int); + void store_setfl(); + void interpolate(); +}; + +#endif diff --git a/src/pair_eam_fs.cpp b/src/pair_eam_fs.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e1cf0cc78bd26b957125db57e35f142f2c75eab3 --- /dev/null +++ b/src/pair_eam_fs.cpp @@ -0,0 +1,771 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Tim Lau (MIT) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "pair_eam_fs.h" +#include "atom.h" +#include "force.h" +#include "update.h" +#include "comm.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +#define MAXLINE 1024 + +/* ---------------------------------------------------------------------- */ + +PairEAMFS::PairEAMFS() +{ + one_coeff = 1; +} + +/* ---------------------------------------------------------------------- */ + +PairEAMFS::~PairEAMFS() { + + // deallocate array unique to derived class, parent will deallocate the rest + + if (frho) memory->destroy_3d_double_array(rhor_fs); + + // insure derived-class's deallocate is called + // set ptr to NULL to prevent parent class from calling it's deallocate + + if (frho_0) interpolate_deallocate(); + frho_0 = NULL; +} + +/* ---------------------------------------------------------------------- */ + +void PairEAMFS::compute(int eflag, int vflag) +{ + int i,j,k,m,numneigh,itype,jtype; + double xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r,p,fforce,rhoip,rhojp,z2,z2p,recip,phi,phip,psip; + int *neighs; + double **f; + + // grow energy array if necessary + + if (atom->nmax > nmax) { + memory->sfree(rho); + memory->sfree(fp); + nmax = atom->nmax; + rho = (double *) memory->smalloc(nmax*sizeof(double),"eam:rho"); + fp = (double *) memory->smalloc(nmax*sizeof(double),"eam:fp"); + } + + eng_vdwl = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + int *type = atom->type; + int nlocal = atom->nlocal; + int newton_pair = force->newton_pair; + + // zero out density + + if (newton_pair) { + m = nlocal + atom->nghost; + for (i = 0; i < m; i++) rho[i] = 0.0; + } else for (i = 0; i < nlocal; i++) rho[i] = 0.0; + + // rho = density at each atom + // loop over neighbors of my atoms + // FS has type-specific rho functional + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq < cutforcesq) { + jtype = type[j]; + p = sqrt(rsq)*rdr + 1.0; + m = static_cast<int> (p); + m = MIN(m,nr-1); + p -= m; + p = MIN(p,1.0); + rho[i] += + ((rhor_fs_3[jtype][itype][m]*p + rhor_fs_2[jtype][itype][m])*p + + rhor_fs_1[jtype][itype][m])*p + rhor_fs_0[jtype][itype][m]; + if (newton_pair || j < nlocal) + rho[j] += + ((rhor_fs_3[itype][jtype][m]*p + rhor_fs_2[itype][jtype][m])*p + + rhor_fs_1[itype][jtype][m])*p + rhor_fs_0[itype][jtype][m]; + } + } + } + + // communicate and sum densities + + if (newton_pair) comm->reverse_comm_pair(this); + + // fp = derivative of embedding energy at each atom + // phi = embedding energy at each atom + // FS is same as standard EAM + + for (i = 0; i < nlocal; i++) { + itype = type[i]; + p = rho[i]*rdrho + 1.0; + m = static_cast<int> (p); + m = MAX(1,MIN(m,nrho-1)); + p -= m; + p = MIN(p,1.0); + fp[i] = (frho_6[itype][m]*p + frho_5[itype][m])*p + frho_4[itype][m]; + if (eflag) { + phi = ((frho_3[itype][m]*p + frho_2[itype][m])*p + + frho_1[itype][m])*p + frho_0[itype][m]; + eng_vdwl += phi; + } + } + + // communicate derivative of embedding function + + comm->comm_pair(this); + + // compute forces on each atom + // loop over neighbors of my atoms + // FS has type-specific rhoip and rhojp + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq < cutforcesq) { + jtype = type[j]; + r = sqrt(rsq); + p = r*rdr + 1.0; + m = static_cast<int> (p); + m = MIN(m,nr-1); + p -= m; + p = MIN(p,1.0); + + // rhoip = derivative of (density at atom j due to atom i) + // rhojp = derivative of (density at atom i due to atom j) + // phi = pair potential energy + // phip = phi' + // z2 = phi * r + // z2p = (phi * r)' = (phi' r) + phi + // psip needs both fp[i] and fp[j] terms since r_ij appears in two + // terms of embed eng: Fi(sum rho_ij) and Fj(sum rho_ji) + // hence embed' = Fi(sum rho_ij) rhojp + Fj(sum rho_ji) rhoip + + rhoip = + (rhor_fs_6[itype][jtype][m]*p + rhor_fs_5[itype][jtype][m])*p + + rhor_fs_4[itype][jtype][m]; + rhojp = + (rhor_fs_6[jtype][itype][m]*p + rhor_fs_5[jtype][itype][m])*p + + rhor_fs_4[jtype][itype][m]; + z2 = ((z2r_3[itype][jtype][m]*p + z2r_2[itype][jtype][m])*p + + z2r_1[itype][jtype][m])*p + z2r_0[itype][jtype][m]; + z2p = (z2r_6[itype][jtype][m]*p + z2r_5[itype][jtype][m])*p + + z2r_4[itype][jtype][m]; + + recip = 1.0/r; + phi = z2*recip; + phip = z2p*recip - phi*recip; + psip = fp[i]*rhojp + fp[j]*rhoip + phip; + fforce = psip*recip; + 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 (newton_pair || j < nlocal) eng_vdwl += phi; + else eng_vdwl += 0.5*phi; + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] -= 0.5*delx*delx*fforce; + virial[1] -= 0.5*dely*dely*fforce; + virial[2] -= 0.5*delz*delz*fforce; + virial[3] -= 0.5*delx*dely*fforce; + virial[4] -= 0.5*delx*delz*fforce; + virial[5] -= 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairEAMFS::coeff(int narg, char **arg) +{ + if (!allocated) allocate(); + + if (narg != 3 + atom->ntypes) + error->all("Incorrect args for pair coefficients"); + + // insure I,J args are * * + // can only have one file, so clear all setflag settings + + if (strcmp(arg[0],"*") != 0 || strcmp(arg[1],"*") != 0) + error->all("Incorrect args for pair coefficients"); + + // read Finnis/Sinclair modified setfl file + // first clear setflag since are doing this once for I,J = *,* + // read for all i,j pairs where ith,jth mapping is non-zero + // set setflag i,j for non-zero pairs + // set mass of atom type if i = j + + int n = atom->ntypes; + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) + setflag[i][j] = 0; + + int itable,ith,jth; + int ilo,ihi,jlo,jhi; + ilo = jlo = 1; + ihi = jhi = n; + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + for (int j = jlo; j <= jhi; j++) { + ith = atoi(arg[2+i]); + jth = atoi(arg[2+j]); + if (ith > 0 && jth > 0) { + itable = read_setfl(arg[2],ith,jth); + if (i == j) atom->set_mass(i,tables[itable].mass); + tabindex[i][j] = itable; + setflag[i][j] = 1; + count++; + } + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairEAMFS::init_one(int i, int j) +{ + if (setflag[i][j] == 0) + error->all("All EAM pair coeffs are not set"); + + // EAM has only one cutoff = max of all pairwise cutoffs + // determine max by checking table assigned to all type pairs + // only setflag[i][j] = 1 is relevant (if hybrid, some may not be set) + + cutmax = 0.0; + for (int ii = 1; ii <= atom->ntypes; ii++) { + for (int jj = ii; jj <= atom->ntypes; jj++) { + if (setflag[ii][jj] == 0) continue; + cutmax = MAX(cutmax,tables[tabindex[ii][jj]].cut); + } + } + + return cutmax; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairEAMFS::init_style() +{ + // set communication sizes in comm class + + comm->maxforward_pair = MAX(comm->maxforward_pair,1); + comm->maxreverse_pair = MAX(comm->maxreverse_pair,1); + + // copy read-in-tables to multi-type setfl format + // interpolate final spline coeffs + + store_setfl(); + interpolate(); + + cutforcesq = cutmax*cutmax; +} + +/* ---------------------------------------------------------------------- + read ith,jth potential values from a multi-element alloy EAM/FS file + read values into table and bcast values + this file has different format than standard EAM setfl file +------------------------------------------------------------------------- */ + +int PairEAMFS::read_setfl(char *file, int ith, int jth) +{ + // check if ith,jth portion of same file has already been read + // if yes, return index of table entry + // if no, extend table list + + for (int i = 0; i < ntables; i++) + if (ith == tables[i].ith && jth == tables[i].jth) return i; + + tables = (Table *) + memory->srealloc(tables,(ntables+1)*sizeof(Table),"pair:tables"); + + Table *tb = &tables[ntables]; + int n = strlen(file) + 1; + tb->filename = new char[n]; + strcpy(tb->filename,file); + tb->ith = ith; + tb->jth = jth; + + // open potential file + + int me = comm->me; + FILE *fp; + char line[MAXLINE]; + + if (me == 0) { + fp = fopen(file,"r"); + if (fp == NULL) { + char str[128]; + sprintf(str,"Cannot open EAM potential file %s",file); + error->one(str); + } + } + + // read and broadcast header + + int ntypes; + if (me == 0) { + fgets(line,MAXLINE,fp); + fgets(line,MAXLINE,fp); + fgets(line,MAXLINE,fp); + fgets(line,MAXLINE,fp); + sscanf(line,"%d",&ntypes); + fgets(line,MAXLINE,fp); + sscanf(line,"%d %lg %d %lg %lg", + &tb->nrho,&tb->drho,&tb->nr,&tb->dr,&tb->cut); + } + + MPI_Bcast(&ntypes,1,MPI_INT,0,world); + MPI_Bcast(&tb->nrho,1,MPI_INT,0,world); + MPI_Bcast(&tb->drho,1,MPI_DOUBLE,0,world); + MPI_Bcast(&tb->nr,1,MPI_INT,0,world); + MPI_Bcast(&tb->dr,1,MPI_DOUBLE,0,world); + MPI_Bcast(&tb->cut,1,MPI_DOUBLE,0,world); + + // check if ith,jth are consistent with ntypes + + if (ith > ntypes || jth > ntypes) + error->all("Requested atom types in EAM setfl file do not exist"); + + // allocate potential arrays and read/bcast them + // skip sections of file that don't correspond to ith,jth + // extract mass, frho for i,i from ith element section + // extract rhor for i,j from jth array of ith element section + // extract z2r for i,j from ith,jth array of z2r section + // note that ith can be < or > than jth + // set zr to NULL (funcl array) so it can be deallocated + + tb->frho = new double[tb->nrho+1]; + tb->rhor = new double[tb->nr+1]; + tb->z2r = new double[tb->nr+1]; + tb->zr = NULL; + + int i,j,tmp; + double mass; + + for (i = 1; i <= ntypes; i++) { + if (me == 0) { + fgets(line,MAXLINE,fp); + sscanf(line,"%d %lg",&tmp,&mass); + } + MPI_Bcast(&mass,1,MPI_DOUBLE,0,world); + + if (i == ith && ith == jth) { + tb->mass = mass; + if (me == 0) grab(fp,tb->nrho,&tb->frho[1]); + MPI_Bcast(&tb->frho[1],tb->nrho,MPI_DOUBLE,0,world); + + for (j = 1; j <= ntypes; j++) { + if (j == jth) { + if (me == 0) grab(fp,tb->nr,&tb->rhor[1]); + MPI_Bcast(&tb->rhor[1],tb->nr,MPI_DOUBLE,0,world); + } else if (me == 0) skip(fp,tb->nr); + } + + } else if (i == ith && ith != jth) { + if (me == 0) skip(fp,tb->nrho); + for (j = 1; j <= ntypes; j++) { + if (j == jth) { + if (me == 0) grab(fp,tb->nr,&tb->rhor[1]); + MPI_Bcast(&tb->rhor[1],tb->nr,MPI_DOUBLE,0,world); + } else if (me == 0) skip(fp,tb->nr); + } + + } else { + if (me == 0) skip(fp,tb->nrho); + if (me == 0) for (j = 1; j <= ntypes; j = j + 1) skip(fp,tb->nr); + } + } + + for (i = 1; i <= ntypes; i++) { + for (j = 1; j <= i; j++) { + if ((i == ith && j == jth) || (j == ith && i == jth)) { + if (me == 0) grab(fp,tb->nr,&tb->z2r[1]); + MPI_Bcast(&tb->z2r[1],tb->nr,MPI_DOUBLE,0,world); + } else if (me == 0) skip(fp,tb->nr); + } + } + + // close the potential file + + if (me == 0) fclose(fp); + + ntables++; + return ntables-1; +} + +/* ---------------------------------------------------------------------- + store read-in setfl values in multi-type setfl format +------------------------------------------------------------------------- */ + +void PairEAMFS::store_setfl() +{ + int i,j,m; + + int ntypes = atom->ntypes; + + // set nrho,nr,drho,dr from any i,i table entry since all the same + + for (i = 1; i <= ntypes; i++) + if (setflag[i][i]) break; + + nrho = tables[tabindex[i][i]].nrho; + nr = tables[tabindex[i][i]].nr; + drho = tables[tabindex[i][i]].drho; + dr = tables[tabindex[i][i]].dr; + + // allocate multi-type setfl arrays + + if (frho) { + memory->destroy_2d_double_array(frho); + memory->destroy_3d_double_array(rhor_fs); + memory->destroy_2d_double_array(zrtmp); + memory->destroy_3d_double_array(z2r); + } + + frho = (double **) + memory->create_2d_double_array(ntypes+1,nrho+1,"eam/fs:frho"); + rhor_fs = (double ***) + memory->create_3d_double_array(ntypes+1,ntypes+1,nr+1,"eam/fs:rhor_fs"); + zrtmp = (double **) + memory->create_2d_double_array(ntypes+1,nr+1,"eam/fs:zrtmp"); + z2r = (double ***) + memory->create_3d_double_array(ntypes+1,ntypes+1,nr+1,"eam/fs:frho"); + + // copy from read-in tables to multi-type setfl arrays + // frho,rhor are 1:ntypes, z2r is 1:ntypes,1:ntypes + // skip if setflag i,j = 0 (if hybrid, some may not be set) + + for (i = 1; i <= ntypes; i++) + for (j = 1; j <= ntypes; j++) { + if (setflag[i][j] == 0) continue; + Table *tb = &tables[tabindex[i][j]]; + if (i == j) for (m = 1; m <= nrho; m++) frho[i][m] = tb->frho[m]; + for (m = 1; m <= nr; m++) { + rhor_fs[i][j][m] = tb->rhor[m]; + z2r[i][j][m] = tb->z2r[m]; + } + } +} + +/* ---------------------------------------------------------------------- + interpolate EAM potentials +------------------------------------------------------------------------- */ + +void PairEAMFS::interpolate() +{ + // free memory from previous interpolation + + if (frho_0) interpolate_deallocate(); + + // interpolation spacings + + rdr = 1.0/dr; + rdrho = 1.0/drho; + + // allocate coeff arrays + + int n = atom->ntypes; + + frho_0 = memory->create_2d_double_array(n+1,nrho+1,"eam/fs:frho_0"); + frho_1 = memory->create_2d_double_array(n+1,nrho+1,"eam/fs:frho_1"); + frho_2 = memory->create_2d_double_array(n+1,nrho+1,"eam/fs:frho_2"); + frho_3 = memory->create_2d_double_array(n+1,nrho+1,"eam/fs:frho_3"); + frho_4 = memory->create_2d_double_array(n+1,nrho+1,"eam/fs:frho_4"); + frho_5 = memory->create_2d_double_array(n+1,nrho+1,"eam/fs:frho_5"); + frho_6 = memory->create_2d_double_array(n+1,nrho+1,"eam/fs:frho_6"); + + rhor_fs_0 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:rhor_fs_0"); + rhor_fs_1 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:rhor_fs_1"); + rhor_fs_2 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:rhor_fs_2"); + rhor_fs_3 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:rhor_fs_3"); + rhor_fs_4 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:rhor_fs_4"); + rhor_fs_5 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:rhor_fs_5"); + rhor_fs_6 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:rhor_fs_6"); + + z2r_0 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:z2r_0"); + z2r_1 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:z2r_1"); + z2r_2 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:z2r_2"); + z2r_3 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:z2r_3"); + z2r_4 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:z2r_4"); + z2r_5 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:z2r_5"); + z2r_6 = memory->create_3d_double_array(n+1,n+1,nr+1,"eam/fs:z2r_6"); + + // frho interpolation for 1:ntypes + // skip if setflag = 0 (if hybrid, some may not be set) + // if skip, set frho arrays to 0.0, since they will still be accessed + // for non-EAM atoms when compute() calculates embedding function + + int i,j,m; + + for (i = 1; i <= atom->ntypes; i++) { + if (setflag[i][i] == 0) { + for (j = 1; j <= n; j++) + for (m = 1; m <= nrho; m++) + frho_0[j][m] = frho_1[j][m] = frho_2[j][m] = frho_3[j][m] = + frho_4[j][m] = frho_5[j][m] = frho_6[j][m] = 0.0; + continue; + } + + for (m = 1; m <= nrho; m++) frho_0[i][m] = frho[i][m]; + + frho_1[i][1] = frho_0[i][2]-frho_0[i][1]; + frho_1[i][2] = 0.5*(frho_0[i][3]-frho_0[i][1]); + frho_1[i][nrho-1] = 0.5*(frho_0[i][nrho]-frho_0[i][nrho-2]); + frho_1[i][nrho] = frho_0[i][nrho]-frho_0[i][nrho-1]; + + for (m = 3; m <= nrho-2; m++) + frho_1[i][m] = ((frho_0[i][m-2]-frho_0[i][m+2]) + + 8.0*(frho_0[i][m+1]-frho_0[i][m-1]))/12.0; + + for (m = 1; m <= nrho-1; m++) { + frho_2[i][m] = 3.*(frho_0[i][m+1]-frho_0[i][m]) - + 2.0*frho_1[i][m] - frho_1[i][m+1]; + frho_3[i][m] = frho_1[i][m] + frho_1[i][m+1] - + 2.0*(frho_0[i][m+1]-frho_0[i][m]); + } + + frho_2[i][nrho] = 0.0; + frho_3[i][nrho] = 0.0; + + for (m = 1; m <= nrho; m++) { + frho_4[i][m] = frho_1[i][m]/drho; + frho_5[i][m] = 2.0*frho_2[i][m]/drho; + frho_6[i][m] = 3.0*frho_3[i][m]/drho; + } + } + + // rhor interpolation for 1:ntypes + // skip if setflag = 0 (if hybrid, some may not be set) + + for (i = 1; i <= atom->ntypes; i++) { + for (j = 1; j <= atom->ntypes; j++) { + if (setflag[i][j] == 0) continue; + + for (m = 1; m <= nr; m++) rhor_fs_0[i][j][m] = rhor_fs[i][j][m]; + + rhor_fs_1[i][j][1] = rhor_fs_0[i][j][2]-rhor_fs_0[i][j][1]; + rhor_fs_1[i][j][2] = 0.5*(rhor_fs_0[i][j][3]-rhor_fs_0[i][j][1]); + rhor_fs_1[i][j][nr-1] = 0.5*(rhor_fs_0[i][j][nr]-rhor_fs_0[i][j][nr-2]); + rhor_fs_1[i][j][nr] = 0.0; + + for (m = 3; m <= nr-2; m++) + rhor_fs_1[i][j][m] = ((rhor_fs_0[i][j][m-2]-rhor_fs_0[i][j][m+2]) + + 8.0*(rhor_fs_0[i][j][m+1]-rhor_fs_0[i][j][m-1]))/12.; + + for (m = 1; m <= nr-1; m++) { + rhor_fs_2[i][j][m] = 3.0*(rhor_fs_0[i][j][m+1]-rhor_fs_0[i][j][m]) - + 2.0*rhor_fs_1[i][j][m] - rhor_fs_1[i][j][m+1]; + rhor_fs_3[i][j][m] = rhor_fs_1[i][j][m] + rhor_fs_1[i][j][m+1] - + 2.0*(rhor_fs_0[i][j][m+1]-rhor_fs_0[i][j][m]); + } + + rhor_fs_2[i][j][nr] = 0.0; + rhor_fs_3[i][j][nr] = 0.0; + + for (m = 1; m <= nr; m++) { + rhor_fs_4[i][j][m] = rhor_fs_1[i][j][m]/dr; + rhor_fs_5[i][j][m] = 2.0*rhor_fs_2[i][j][m]/dr; + rhor_fs_6[i][j][m] = 3.0*rhor_fs_3[i][j][m]/dr; + } + } + } + + // z2r interpolation for 1:ntypes,1:ntypes + // skip if setflag i,j = 0 (if hybrid, some may not be set) + // set j,i coeffs = i,j coeffs + + for (i = 1; i <= atom->ntypes; i++) { + for (j = i; j <= atom->ntypes; j++) { + if (setflag[i][j] == 0) continue; + + for (m = 1; m <= nr; m++) z2r_0[i][j][m] = z2r[i][j][m]; + + z2r_1[i][j][1] = z2r_0[i][j][2]-z2r_0[i][j][1]; + z2r_1[i][j][2] = 0.5*(z2r_0[i][j][3]-z2r_0[i][j][1]); + z2r_1[i][j][nr-1] = 0.5*(z2r_0[i][j][nr]-z2r_0[i][j][nr-2]); + z2r_1[i][j][nr] = 0.0; + + for (m = 3; m <= nr-2; m++) + z2r_1[i][j][m] = ((z2r_0[i][j][m-2]-z2r_0[i][j][m+2]) + + 8.0*(z2r_0[i][j][m+1]-z2r_0[i][j][m-1]))/12.; + + for (m = 1; m <= nr-1; m++) { + z2r_2[i][j][m] = 3.0*(z2r_0[i][j][m+1]-z2r_0[i][j][m]) - + 2.0*z2r_1[i][j][m] - z2r_1[i][j][m+1]; + z2r_3[i][j][m] = z2r_1[i][j][m] + z2r_1[i][j][m+1] - + 2.0*(z2r_0[i][j][m+1]-z2r_0[i][j][m]); + } + + z2r_2[i][j][nr] = 0.0; + z2r_3[i][j][nr] = 0.0; + + for (m = 1; m <= nr; m++) { + z2r_4[i][j][m] = z2r_1[i][j][m]/dr; + z2r_5[i][j][m] = 2.0*z2r_2[i][j][m]/dr; + z2r_6[i][j][m] = 3.0*z2r_3[i][j][m]/dr; + } + + for (m = 1; m <= nr; m++) { + z2r_0[j][i][m] = z2r_0[i][j][m]; + z2r_1[j][i][m] = z2r_1[i][j][m]; + z2r_2[j][i][m] = z2r_2[i][j][m]; + z2r_3[j][i][m] = z2r_3[i][j][m]; + z2r_4[j][i][m] = z2r_4[i][j][m]; + z2r_5[j][i][m] = z2r_5[i][j][m]; + z2r_6[j][i][m] = z2r_6[i][j][m]; + } + } + } +} + +/* ---------------------------------------------------------------------- + deallocate spline interpolation arrays +------------------------------------------------------------------------- */ + +void PairEAMFS::interpolate_deallocate() +{ + memory->destroy_2d_double_array(frho_0); + memory->destroy_2d_double_array(frho_1); + memory->destroy_2d_double_array(frho_2); + memory->destroy_2d_double_array(frho_3); + memory->destroy_2d_double_array(frho_4); + memory->destroy_2d_double_array(frho_5); + memory->destroy_2d_double_array(frho_6); + + memory->destroy_3d_double_array(rhor_fs_0); + memory->destroy_3d_double_array(rhor_fs_1); + memory->destroy_3d_double_array(rhor_fs_2); + memory->destroy_3d_double_array(rhor_fs_3); + memory->destroy_3d_double_array(rhor_fs_4); + memory->destroy_3d_double_array(rhor_fs_5); + memory->destroy_3d_double_array(rhor_fs_6); + + memory->destroy_3d_double_array(z2r_0); + memory->destroy_3d_double_array(z2r_1); + memory->destroy_3d_double_array(z2r_2); + memory->destroy_3d_double_array(z2r_3); + memory->destroy_3d_double_array(z2r_4); + memory->destroy_3d_double_array(z2r_5); + memory->destroy_3d_double_array(z2r_6); +} + +/* ---------------------------------------------------------------------- */ + +void PairEAMFS::single(int i, int j, int itype, int jtype, + double rsq, double factor_coul, double factor_lj, + int eflag, One &one) +{ + double r,p,rhoip,rhojp,z2,z2p,recip,phi,phip,psip; + int m; + + r = sqrt(rsq); + p = r*rdr + 1.0; + m = static_cast<int> (p); + m = MIN(m,nr-1); + p -= m; + p = MIN(p,1.0); + + rhoip = (rhor_fs_6[itype][jtype][m]*p + rhor_fs_5[itype][jtype][m])*p + + rhor_fs_4[itype][jtype][m]; + rhojp = (rhor_fs_6[jtype][itype][m]*p + rhor_fs_5[jtype][itype][m])*p + + rhor_fs_4[jtype][itype][m]; + z2 = ((z2r_3[itype][jtype][m]*p + z2r_2[itype][jtype][m])*p + + z2r_1[itype][jtype][m])*p + z2r_0[itype][jtype][m]; + z2p = (z2r_6[itype][jtype][m]*p + z2r_5[itype][jtype][m])*p + + z2r_4[itype][jtype][m]; + + recip = 1.0/r; + phi = z2*recip; + phip = z2p*recip - phi*recip; + psip = fp[i]*rhojp + fp[j]*rhoip + phip; + one.fforce = -psip*recip; + + if (eflag) { + one.eng_vdwl = phi; + one.eng_coul = 0.0; + } +} diff --git a/src/pair_eam_fs.h b/src/pair_eam_fs.h new file mode 100644 index 0000000000000000000000000000000000000000..1787d1beabf5946c8cdcc3bb4fcb0054d9394b1f --- /dev/null +++ b/src/pair_eam_fs.h @@ -0,0 +1,40 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_EAM_FS_H +#define PAIR_EAM_FS_H + +#include "pair_eam.h" + +class PairEAMFS : public PairEAM { + public: + PairEAMFS(); + ~PairEAMFS(); + void compute(int, int); + void coeff(int, char **); + double init_one(int, int); + void init_style(); + void single(int, int, int, int, double, double, double, int, One &); + + private: + double ***rhor_fs; + double ***rhor_fs_0,***rhor_fs_1,***rhor_fs_2,***rhor_fs_3; + double ***rhor_fs_4,***rhor_fs_5,***rhor_fs_6; + + int read_setfl(char *, int, int); + void store_setfl(); + void interpolate(); + void interpolate_deallocate(); +}; + +#endif diff --git a/src/pair_hybrid.cpp b/src/pair_hybrid.cpp new file mode 100644 index 0000000000000000000000000000000000000000..97d793244796e09dc91a01b99b603f943d6027d8 --- /dev/null +++ b/src/pair_hybrid.cpp @@ -0,0 +1,666 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: James Fischer (High Performance Technologies, Inc) + Vincent Natoli (Stone Ridge Technology) + David Richie (Stone Ridge Technology) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "string.h" +#include "ctype.h" +#include "pair_hybrid.h" +#include "atom.h" +#include "force.h" +#include "pair.h" +#include "neighbor.h" +#include "update.h" +#include "comm.h" +#include "memory.h" +#include "error.h" + +#define NEIGHEXTRA 10000 + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +PairHybrid::PairHybrid() +{ + nstyles = 0; +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +PairHybrid::~PairHybrid() +{ + if (nstyles) { + for (int m = 0; m < nstyles; m++) delete styles[m]; + delete [] styles; + for (int m = 0; m < nstyles; m++) delete [] keywords[m]; + delete [] keywords; + } + + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_int_array(map); + memory->destroy_2d_double_array(cutsq); + + delete [] nnlist; + delete [] maxneigh; + for (int m = 0; m < nstyles; m++) memory->sfree(nlist[m]); + delete [] nlist; + + delete [] nnlist_full; + delete [] maxneigh_full; + for (int m = 0; m < nstyles; m++) memory->sfree(nlist_full[m]); + delete [] nlist_full; + + for (int m = 0; m < nstyles; m++) delete [] firstneigh[m]; + delete [] firstneigh; + for (int m = 0; m < nstyles; m++) delete [] numneigh[m]; + delete [] numneigh; + + for (int m = 0; m < nstyles; m++) delete [] firstneigh_full[m]; + delete [] firstneigh_full; + for (int m = 0; m < nstyles; m++) delete [] numneigh_full[m]; + delete [] numneigh_full; + } +} + +/* ---------------------------------------------------------------------- */ + +void PairHybrid::compute(int eflag, int vflag) +{ + int i,j,k,m,n,jfull,nneigh; + int *neighs,*mapi; + double **f_original; + + // save ptrs to original neighbor lists + + int **firstneigh_original = neighbor->firstneigh; + int *numneigh_original = neighbor->numneigh; + int **firstneigh_full_original = neighbor->firstneigh_full; + int *numneigh_full_original = neighbor->numneigh_full; + + // if this is re-neighbor step, create sub-style lists + + if (neighbor->ago == 0) { + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + + // realloc per-atom per-style firstneigh/numneigh half/full if necessary + + if (nlocal > maxlocal) { + maxlocal = nlocal; + if (neigh_half_every) { + for (m = 0; m < nstyles; m++) { + delete [] firstneigh[m]; + delete [] numneigh[m]; + } + for (m = 0; m < nstyles; m++) { + firstneigh[m] = new int*[maxlocal]; + numneigh[m] = new int[maxlocal]; + } + } + if (neigh_full_every) { + for (m = 0; m < nstyles; m++) { + delete [] firstneigh_full[m]; + delete [] numneigh_full[m]; + } + for (m = 0; m < nstyles; m++) { + firstneigh_full[m] = new int*[maxlocal]; + numneigh_full[m] = new int[maxlocal]; + } + } + } + + // nnlist[] = length of each sub-style half list + // nnlist_full[] = length of each sub-style full list + // count from half and/or full list depending on what sub-styles use + + for (m = 0; m < nstyles; m++) nnlist[m] = nnlist_full[m] = 0; + + if (neigh_half_every && neigh_full_every) { + for (i = 0; i < nlocal; i++) { + mapi = map[type[i]]; + neighs = firstneigh_original[i]; + nneigh = numneigh_original[i]; + for (k = 0; k < nneigh; k++) { + j = neighs[k]; + if (j >= nall) j %= nall; + m = mapi[type[j]]; + if (styles[m] && styles[m]->neigh_half_every) nnlist[m]++; + } + neighs = firstneigh_full_original[i]; + nneigh = numneigh_full_original[i]; + for (k = 0; k < nneigh; k++) { + j = neighs[k]; + if (j >= nall) j %= nall; + m = mapi[type[j]]; + if (styles[m] && styles[m]->neigh_full_every) nnlist_full[m]++; + } + } + + } else if (neigh_half_every) { + for (i = 0; i < nlocal; i++) { + mapi = map[type[i]]; + neighs = firstneigh_original[i]; + nneigh = numneigh_original[i]; + for (k = 0; k < nneigh; k++) { + j = neighs[k]; + if (j >= nall) j %= nall; + nnlist[mapi[type[j]]]++; + } + } + + } else if (neigh_full_every) { + for (i = 0; i < nlocal; i++) { + mapi = map[type[i]]; + neighs = firstneigh_full_original[i]; + nneigh = numneigh_full_original[i]; + for (k = 0; k < nneigh; k++) { + j = neighs[k]; + if (j >= nall) j %= nall; + nnlist_full[mapi[type[j]]]++; + } + } + } + + // realloc sub-style nlist and nlist_full if necessary + + if (neigh_half_every) { + for (m = 0; m < nstyles; m++) { + if (nnlist[m] > maxneigh[m]) { + memory->sfree(nlist[m]); + maxneigh[m] = nnlist[m] + NEIGHEXTRA; + nlist[m] = (int *) + memory->smalloc(maxneigh[m]*sizeof(int),"pair_hybrid:nlist"); + } + nnlist[m] = 0; + } + } + if (neigh_full_every) { + for (m = 0; m < nstyles; m++) { + if (nnlist_full[m] > maxneigh_full[m]) { + memory->sfree(nlist_full[m]); + maxneigh_full[m] = nnlist_full[m] + NEIGHEXTRA; + nlist_full[m] = (int *) + memory->smalloc(maxneigh_full[m]*sizeof(int), + "pair_hybrid:nlist_full"); + } + nnlist_full[m] = 0; + } + } + + // load sub-style half/full list with values from original lists + // load from half and/or full list depending on what sub-styles use + + if (neigh_half_every && neigh_full_every) { + for (i = 0; i < nlocal; i++) { + for (m = 0; m < nstyles; m++) { + firstneigh[m][i] = &nlist[m][nnlist[m]]; + numneigh[m][i] = nnlist[m]; + firstneigh_full[m][i] = &nlist_full[m][nnlist_full[m]]; + numneigh_full[m][i] = nnlist_full[m]; + } + mapi = map[type[i]]; + neighs = firstneigh_original[i]; + nneigh = numneigh_original[i]; + for (k = 0; k < nneigh; k++) { + j = jfull = neighs[k]; + if (j >= nall) j %= nall; + m = mapi[type[j]]; + if (styles[m] && styles[m]->neigh_half_every) + nlist[m][nnlist[m]++] = jfull; + } + neighs = firstneigh_full_original[i]; + nneigh = numneigh_full_original[i]; + for (k = 0; k < nneigh; k++) { + j = jfull = neighs[k]; + if (j >= nall) j %= nall; + m = mapi[type[j]]; + if (styles[m] && styles[m]->neigh_full_every) + nlist_full[m][nnlist_full[m]++] = jfull; + } + for (m = 0; m < nstyles; m++) { + numneigh[m][i] = nnlist[m] - numneigh[m][i]; + numneigh_full[m][i] = nnlist_full[m] - numneigh_full[m][i]; + } + } + + } else if (neigh_half_every) { + for (i = 0; i < nlocal; i++) { + for (m = 0; m < nstyles; m++) { + firstneigh[m][i] = &nlist[m][nnlist[m]]; + numneigh[m][i] = nnlist[m]; + } + mapi = map[type[i]]; + neighs = firstneigh_original[i]; + nneigh = numneigh_original[i]; + for (k = 0; k < nneigh; k++) { + j = jfull = neighs[k]; + if (j >= nall) j %= nall; + m = mapi[type[j]]; + nlist[m][nnlist[m]++] = jfull; + } + for (m = 0; m < nstyles; m++) + numneigh[m][i] = nnlist[m] - numneigh[m][i]; + } + + } else if (neigh_full_every) { + for (i = 0; i < nlocal; i++) { + for (m = 0; m < nstyles; m++) { + firstneigh_full[m][i] = &nlist_full[m][nnlist_full[m]]; + numneigh_full[m][i] = nnlist_full[m]; + } + mapi = map[type[i]]; + neighs = firstneigh_full_original[i]; + nneigh = numneigh_full_original[i]; + for (k = 0; k < nneigh; k++) { + j = jfull = neighs[k]; + if (j >= nall) j %= nall; + m = mapi[type[j]]; + nlist_full[m][nnlist_full[m]++] = jfull; + } + for (m = 0; m < nstyles; m++) + numneigh_full[m][i] = nnlist_full[m] - numneigh_full[m][i]; + } + } + } + + // call each sub-style's compute function + // set neighbor->firstneigh/numneigh to sub-style lists before call + // set half or full or both depending on what sub-style uses + // for vflag = 1: + // sub-style accumulates in its virial[6] + // sum sub-style virial[6] to hybrid's virial[6] + // for vflag = 2: + // set atom->f to update->f_pair so sub-style will sum its f to f_pair + // call sub-style compute() with vflag % 2 to prevent sub-style + // from calling virial_compute() + // reset atom->f to stored f_original + // call hybrid virial_compute() which will use update->f_pair + // accumulate sub-style energy,virial in hybrid's energy,virial + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (n = 0; n < 6; n++) virial[n] = 0.0; + + if (vflag == 2) { + f_original = atom->f; + atom->f = update->f_pair; + } + + for (m = 0; m < nstyles; m++) { + if (styles[m] == NULL) continue; + if (styles[m]->neigh_half_every) { + neighbor->firstneigh = firstneigh[m]; + neighbor->numneigh = numneigh[m]; + } + if (styles[m]->neigh_full_every) { + neighbor->firstneigh_full = firstneigh_full[m]; + neighbor->numneigh_full = numneigh_full[m]; + } + styles[m]->compute(eflag,vflag % 2); + if (eflag) { + eng_vdwl += styles[m]->eng_vdwl; + eng_coul += styles[m]->eng_coul; + } + if (vflag == 1) for (n = 0; n < 6; n++) virial[n] += styles[m]->virial[n]; + } + + if (vflag == 2) { + atom->f = f_original; + virial_compute(); + } + + // restore ptrs to original neighbor lists + + neighbor->firstneigh = firstneigh_original; + neighbor->numneigh = numneigh_original; + neighbor->firstneigh_full = firstneigh_full_original; + neighbor->numneigh_full = numneigh_full_original; +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairHybrid::allocate() +{ + allocated = 1; + int n = atom->ntypes; + + map = memory->create_2d_int_array(n+1,n+1,"pair:map"); + for (int i = 1; i <= n; i++) + for (int j = i; j <= n; j++) + map[i][j] = -1; + + 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"); + + nnlist = new int[nstyles]; + maxneigh = new int[nstyles]; + nlist = new int*[nstyles]; + for (int m = 0; m < nstyles; m++) maxneigh[m] = 0; + for (int m = 0; m < nstyles; m++) nlist[m] = NULL; + + nnlist_full = new int[nstyles]; + maxneigh_full = new int[nstyles]; + nlist_full = new int*[nstyles]; + for (int m = 0; m < nstyles; m++) maxneigh_full[m] = 0; + for (int m = 0; m < nstyles; m++) nlist_full[m] = NULL; + + maxlocal = 0; + firstneigh = new int**[nstyles]; + numneigh = new int*[nstyles]; + for (int m = 0; m < nstyles; m++) firstneigh[m] = NULL; + for (int m = 0; m < nstyles; m++) numneigh[m] = NULL; + firstneigh_full = new int**[nstyles]; + numneigh_full = new int*[nstyles]; + for (int m = 0; m < nstyles; m++) firstneigh_full[m] = NULL; + for (int m = 0; m < nstyles; m++) numneigh_full[m] = NULL; +} + +/* ---------------------------------------------------------------------- + create one pair style for each arg in list +------------------------------------------------------------------------- */ + +void PairHybrid::settings(int narg, char **arg) +{ + int i,m,istyle; + + if (narg < 1) error->all("Illegal pair_style command"); + + // delete old lists, since cannot just change settings + + if (nstyles) { + for (m = 0; m < nstyles; m++) delete styles[m]; + delete [] styles; + for (m = 0; m < nstyles; m++) delete [] keywords[m]; + delete [] keywords; + } + + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_int_array(map); + memory->destroy_2d_double_array(cutsq); + } + allocated = 0; + + // count sub-styles by skipping numeric args + // one exception is 1st arg of style "table", which is non-numeric word + + nstyles = i = 0; + while (i < narg) { + if (strcmp(arg[i],"table") == 0) i++; + i++; + while (i < narg && !isalpha(arg[i][0])) i++; + nstyles++; + } + + // allocate list of sub-styles + + styles = new Pair*[nstyles]; + keywords = new char*[nstyles]; + + // allocate each sub-style and call its settings() with subset of args + // define subset of sub-styles by skipping numeric args + // one exception is 1st arg of style "table", which is non-numeric word + + nstyles = i = 0; + while (i < narg) { + for (m = 0; m < nstyles; m++) + if (strcmp(arg[i],keywords[m]) == 0) + error->all("Pair style hybrid cannot use same pair style twice"); + if (strcmp(arg[i],"hybrid") == 0) + error->all("Pair style hybrid cannot have hybrid as an argument"); + styles[nstyles] = force->new_pair(arg[i]); + keywords[nstyles] = new char[strlen(arg[i])+1]; + strcpy(keywords[nstyles],arg[i]); + istyle = i; + if (strcmp(arg[i],"table") == 0) i++; + i++; + while (i < narg && !isalpha(arg[i][0])) i++; + if (styles[nstyles]) styles[nstyles]->settings(i-istyle-1,&arg[istyle+1]); + nstyles++; + } + + // neigh_every = 1 if any sub-style = 1 + + neigh_half_every = neigh_full_every = 0; + for (m = 0; m < nstyles; m++) { + if (styles[m] && styles[m]->neigh_half_every) neigh_half_every = 1; + if (styles[m] && styles[m]->neigh_full_every) neigh_full_every = 1; + } + + // single_enable = 0 if any sub-style = 0 + + for (m = 0; m < nstyles; m++) + if (styles[m] && styles[m]->single_enable == 0) single_enable = 0; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairHybrid::coeff(int narg, char **arg) +{ + if (narg < 3) 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); + + // 3rd arg = pair style name + + int m; + for (m = 0; m < nstyles; m++) + if (strcmp(arg[2],keywords[m]) == 0) break; + if (m == nstyles) error->all("Pair coeff for hybrid has invalid style"); + + // move 1st/2nd args to 2nd/3rd args + + sprintf(arg[2],"%s",arg[1]); + sprintf(arg[1],"%s",arg[0]); + + // invoke sub-style coeff() starting with 1st arg + + if (styles[m]) styles[m]->coeff(narg-1,&arg[1]); + + // if pair style only allows one pair coeff call (with * * and type mapping) + // then unset any old setflag/map assigned to that style first + // in case pair coeff for this sub-style is being called for 2nd time + + if (styles[m] && styles[m]->one_coeff) + for (int i = 1; i <= atom->ntypes; i++) + for (int j = i; j <= atom->ntypes; j++) + if (map[i][j] == m) { + map[i][j] = -1; + setflag[i][j] = 0; + } + + // set hybrid map & setflag only if substyle set its setflag + // if sub-style is NULL for "none", still set setflag + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + for (int j = MAX(jlo,i); j <= jhi; j++) { + if (styles[m] == NULL || styles[m]->setflag[i][j]) { + map[i][j] = m; + setflag[i][j] = 1; + count++; + } + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairHybrid::init_one(int i, int j) +{ + // if i,j is set explicity, call its sub-style + // if i,j is not set and i,i sub-style = j,j sub-style + // then set map[i][j] to this sub-style and call sub-style for init/mixing + // else i,j has not been set by user + // check for special case = style none + + double cut = 0.0; + if (setflag[i][j]) { + if (styles[map[i][j]]) { + cut = styles[map[i][j]]->init_one(i,j); + styles[map[i][j]]->cutsq[i][j] = + styles[map[i][j]]->cutsq[j][i] = cut*cut; + if (tail_flag) { + etail_ij = styles[map[i][j]]->etail_ij; + ptail_ij = styles[map[i][j]]->ptail_ij; + } + } + } else if (map[i][i] == map[j][j]) { + map[i][j] = map[i][i]; + if (styles[map[i][j]]) { + cut = styles[map[i][j]]->init_one(i,j); + styles[map[i][j]]->cutsq[i][j] = + styles[map[i][j]]->cutsq[j][i] = cut*cut; + if (tail_flag) { + etail_ij = styles[map[i][j]]->etail_ij; + ptail_ij = styles[map[i][j]]->ptail_ij; + } + } + } else error->one("All pair coeffs are not set"); + + map[j][i] = map[i][j]; + + return cut; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairHybrid::init_style() +{ + for (int m = 0; m < nstyles; m++) + if (styles[m]) styles[m]->init_style(); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairHybrid::write_restart(FILE *fp) +{ + fwrite(&nstyles,sizeof(int),1,fp); + + // each sub-style writes its settings + + int n; + for (int m = 0; m < nstyles; m++) { + n = strlen(keywords[m]) + 1; + fwrite(&n,sizeof(int),1,fp); + fwrite(keywords[m],sizeof(char),n,fp); + if (styles[m]) styles[m]->write_restart_settings(fp); + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairHybrid::read_restart(FILE *fp) +{ + allocate(); + + int me = comm->me; + if (me == 0) fread(&nstyles,sizeof(int),1,fp); + MPI_Bcast(&nstyles,1,MPI_INT,0,world); + + styles = new Pair*[nstyles]; + keywords = new char*[nstyles]; + + // each sub-style is created via new_pair() and reads its settings + + int n; + for (int m = 0; m < nstyles; m++) { + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + keywords[m] = new char[n]; + if (me == 0) fread(keywords[m],sizeof(char),n,fp); + MPI_Bcast(keywords[m],n,MPI_CHAR,0,world); + styles[m] = force->new_pair(keywords[m]); + if (styles[m]) styles[m]->read_restart_settings(fp); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairHybrid::single(int i, int j, int itype, int jtype, + double rsq, double factor_coul, double factor_lj, + int eflag, One &one) +{ + if (map[itype][jtype] == -1) + error->one("Invoked pair single on pair style none"); + + styles[map[itype][jtype]]-> + single(i,j,itype,jtype,rsq,factor_coul,factor_lj,eflag,one); +} + +/* ---------------------------------------------------------------------- */ + +void PairHybrid::single_embed(int i, int itype, double &fpi, + int eflag, double &phi) +{ + if (map[itype][itype] == -1) + error->one("Invoked pair single on pair style none"); + + styles[map[itype][itype]]->single_embed(i,itype,fpi,eflag,phi); +} + +/* ---------------------------------------------------------------------- + modify parameters of the pair style + simply pass command args to each sub-style of hybrid +------------------------------------------------------------------------- */ + +void PairHybrid::modify_params(int narg, char **arg) +{ + for (int m = 0; m < nstyles; m++) + if (styles[m]) styles[m]->modify_params(narg,arg); +} + +/* ---------------------------------------------------------------------- + memory usage of sub-style firstneigh, numneigh, neighbor list + add in memory usage of each sub-style itself +------------------------------------------------------------------------- */ + +int PairHybrid::memory_usage() +{ + int bytes = nstyles*maxlocal * (sizeof(int *) + sizeof(int)); + for (int m = 0; m < nstyles; m++) bytes += maxneigh[m] * sizeof(int); + for (int m = 0; m < nstyles; m++) + if (styles[m]) bytes += styles[m]->memory_usage(); + return bytes; +} diff --git a/src/pair_hybrid.h b/src/pair_hybrid.h new file mode 100644 index 0000000000000000000000000000000000000000..e7dce5fbf0d00aa5247b004767f7997ed6cef322 --- /dev/null +++ b/src/pair_hybrid.h @@ -0,0 +1,61 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_HYBRID_H +#define PAIR_HYBRID_H + +#include "stdio.h" +#include "pair.h" + +class PairHybrid : public Pair { + friend class Force; + + public: + PairHybrid(); + ~PairHybrid(); + void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void init_style(); + void write_restart(FILE *); + void read_restart(FILE *); + void single(int, int, int, int, double, double, double, int, One &); + void single_embed(int, int, double &, int, double &); + void modify_params(int narg, char **arg); + int memory_usage(); + + private: + int nstyles; // # of different pair styles + Pair **styles; // class list for each Pair style + char **keywords; // sub-style name for each Pair style + int **map; // which style each itype,jtype points to + + int *nnlist; // # of half neighs in sub-style neigh lists + int *maxneigh; // max # of neighs sub-style lists can store + int **nlist; // half neigh list for each sub-style + + int *nnlist_full; // # of full neighs in sub-style neigh lists + int *maxneigh_full; // max # of neighs sub-style lists can store + int **nlist_full; // full neigh list for each sub-style + + int ***firstneigh; // each sub-style's per-atom firstneigh + int **numneigh; // each sub-style's per-atom numneigh + int ***firstneigh_full; // each sub-style's per-atom firstneigh_full + int **numneigh_full; // each sub-style's per-atom numneigh_full + int maxlocal; // max length of each ss's firstneigh,numneigh + + void allocate(); +}; + +#endif diff --git a/src/pair_lj_charmm_coul_charmm.cpp b/src/pair_lj_charmm_coul_charmm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..101043e9977df744ffa5a04dd7e1f8d5b49fe812 --- /dev/null +++ b/src/pair_lj_charmm_coul_charmm.cpp @@ -0,0 +1,493 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "pair_lj_charmm_coul_charmm.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +PairLJCharmmCoulCharmm::~PairLJCharmmCoulCharmm() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(epsilon); + memory->destroy_2d_double_array(sigma); + memory->destroy_2d_double_array(eps14); + memory->destroy_2d_double_array(sigma14); + memory->destroy_2d_double_array(lj1); + memory->destroy_2d_double_array(lj2); + memory->destroy_2d_double_array(lj3); + memory->destroy_2d_double_array(lj4); + memory->destroy_2d_double_array(lj14_1); + memory->destroy_2d_double_array(lj14_2); + memory->destroy_2d_double_array(lj14_3); + memory->destroy_2d_double_array(lj14_4); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmm::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double factor,phicoul,philj,switch1,switch2; + int *neighs; + double **f; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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_bothsq) { + r2inv = 1.0/rsq; + + if (rsq < cut_coulsq) { + forcecoul = qqrd2e * qtmp*q[j]*sqrt(r2inv); + if (rsq > cut_coul_innersq) { + switch1 = (cut_coulsq-rsq) * (cut_coulsq-rsq) * + (cut_coulsq + 2.0*rsq - 3.0*cut_coul_innersq) / denom_coul; + switch2 = 12.0*rsq * (cut_coulsq-rsq) * + (rsq-cut_coul_innersq) / denom_coul; + forcecoul *= switch1 + switch2; + } + } else forcecoul = 0.0; + + if (rsq < cut_ljsq) { + r6inv = r2inv*r2inv*r2inv; + jtype = type[j]; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + } else forcelj = 0.0; + + fforce = (factor_coul*forcecoul + factor_lj*forcelj) * 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 (newton_pair || j < nlocal) factor = 1.0; + else factor = 0.5; + if (rsq < cut_coulsq) { + phicoul = qqrd2e * qtmp*q[j]*sqrt(r2inv); + if (rsq > cut_coul_innersq) { + switch1 = (cut_coulsq-rsq) * (cut_coulsq-rsq) * + (cut_coulsq + 2.0*rsq - 3.0*cut_coul_innersq) / + denom_coul; + phicoul *= switch1; + } + eng_coul += factor*factor_coul*phicoul; + } + if (rsq < cut_ljsq) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + philj *= switch1; + } + eng_vdwl += factor*factor_lj*philj; + } + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmm::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"); + + epsilon = memory->create_2d_double_array(n+1,n+1,"pair:epsilon"); + sigma = memory->create_2d_double_array(n+1,n+1,"pair:sigma"); + eps14 = memory->create_2d_double_array(n+1,n+1,"pair:eps14"); + sigma14 = memory->create_2d_double_array(n+1,n+1,"pair:sigma14"); + lj1 = memory->create_2d_double_array(n+1,n+1,"pair:lj1"); + lj2 = memory->create_2d_double_array(n+1,n+1,"pair:lj2"); + lj3 = memory->create_2d_double_array(n+1,n+1,"pair:lj3"); + lj4 = memory->create_2d_double_array(n+1,n+1,"pair:lj4"); + lj14_1 = memory->create_2d_double_array(n+1,n+1,"pair:lj14_1"); + lj14_2 = memory->create_2d_double_array(n+1,n+1,"pair:lj14_2"); + lj14_3 = memory->create_2d_double_array(n+1,n+1,"pair:lj14_3"); + lj14_4 = memory->create_2d_double_array(n+1,n+1,"pair:lj14_4"); +} + +/* ---------------------------------------------------------------------- + global settings + unlike other pair styles, + there are no individual pair settings that these override +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmm::settings(int narg, char **arg) +{ + if (narg != 2 && narg != 4) + error->all("Illegal pair_style command"); + + cut_lj_inner = atof(arg[0]); + cut_lj = atof(arg[1]); + if (narg == 2) { + cut_coul_inner = cut_lj_inner; + cut_coul = cut_lj; + } else { + cut_coul_inner = atof(arg[2]); + cut_coul = atof(arg[3]); + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmm::coeff(int narg, char **arg) +{ + if (narg != 4 && narg != 6) + 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); + + double epsilon_one = atof(arg[2]); + double sigma_one = atof(arg[3]); + double eps14_one = epsilon_one; + double sigma14_one = sigma_one; + if (narg == 6) { + eps14_one = atof(arg[4]); + sigma14_one = atof(arg[5]); + } + + 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; + eps14[i][j] = eps14_one; + sigma14[i][j] = sigma14_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairLJCharmmCoulCharmm::init_one(int i, int j) +{ + // always mix arithmetically + + if (setflag[i][j] == 0) { + epsilon[i][j] = sqrt(epsilon[i][i]*epsilon[j][j]); + sigma[i][j] = 0.5 * (sigma[i][i] + sigma[j][j]); + eps14[i][j] = sqrt(eps14[i][i]*eps14[j][j]); + sigma14[i][j] = 0.5 * (sigma14[i][i] + sigma14[j][j]); + } + + double cut = MAX(cut_lj,cut_coul); + + 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); + lj14_1[i][j] = 48.0 * eps14[i][j] * pow(sigma14[i][j],12.0); + lj14_2[i][j] = 24.0 * eps14[i][j] * pow(sigma14[i][j],6.0); + lj14_3[i][j] = 4.0 * eps14[i][j] * pow(sigma14[i][j],12.0); + lj14_4[i][j] = 4.0 * eps14[i][j] * pow(sigma14[i][j],6.0); + + lj1[j][i] = lj1[i][j]; + lj2[j][i] = lj2[i][j]; + lj3[j][i] = lj3[i][j]; + lj4[j][i] = lj4[i][j]; + lj14_1[j][i] = lj14_1[i][j]; + lj14_2[j][i] = lj14_2[i][j]; + lj14_3[j][i] = lj14_3[i][j]; + lj14_4[j][i] = lj14_4[i][j]; + + return cut; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmm::init_style() +{ + // require an atom style with charge defined + + if (atom->charge_allow == 0) + error->all("Must use charged atom style with this pair style"); + + // require cut_lj_inner < cut_lj, cut_coul_inner < cut_coul + + if (cut_lj_inner >= cut_lj || cut_coul_inner >= cut_coul) + error->all("Pair inner cutoff >= Pair outer cutoff"); + + cut_lj_innersq = cut_lj_inner * cut_lj_inner; + cut_ljsq = cut_lj * cut_lj; + cut_coul_innersq = cut_coul_inner * cut_coul_inner; + cut_coulsq = cut_coul * cut_coul; + cut_bothsq = MAX(cut_ljsq,cut_coulsq); + + denom_lj = (cut_ljsq-cut_lj_innersq) * (cut_ljsq-cut_lj_innersq) * + (cut_ljsq-cut_lj_innersq); + denom_coul = (cut_coulsq-cut_coul_innersq) * (cut_coulsq-cut_coul_innersq) * + (cut_coulsq-cut_coul_innersq); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmm::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + 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(&eps14[i][j],sizeof(double),1,fp); + fwrite(&sigma14[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmm::read_restart(FILE *fp) +{ + read_restart_settings(fp); + + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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(&eps14[i][j],sizeof(double),1,fp); + fread(&sigma14[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(&eps14[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&sigma14[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmm::write_restart_settings(FILE *fp) +{ + fwrite(&cut_lj_inner,sizeof(double),1,fp); + fwrite(&cut_lj,sizeof(double),1,fp); + fwrite(&cut_coul_inner,sizeof(double),1,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 PairLJCharmmCoulCharmm::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&cut_lj_inner,sizeof(double),1,fp); + fread(&cut_lj,sizeof(double),1,fp); + fread(&cut_coul_inner,sizeof(double),1,fp); + fread(&cut_coul,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + MPI_Bcast(&cut_lj_inner,1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_lj,1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_coul_inner,1,MPI_DOUBLE,0,world); + 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); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmm::single(int i, int j, int itype, int jtype, + double rsq, double factor_coul, + double factor_lj, + int eflag, One &one) +{ + double r2inv,r6inv,switch1,switch2,forcecoul,forcelj,phicoul,philj; + + r2inv = 1.0/rsq; + if (rsq < cut_coulsq) { + forcecoul = force->qqrd2e * atom->q[i]*atom->q[j]*sqrt(r2inv); + if (rsq > cut_coul_innersq) { + switch1 = (cut_coulsq-rsq) * (cut_coulsq-rsq) * + (cut_coulsq + 2.0*rsq - 3.0*cut_coul_innersq) / denom_coul; + switch2 = 12.0*rsq * (cut_coulsq-rsq) * + (rsq-cut_coul_innersq) / denom_coul; + forcecoul *= switch1 + switch2; + } + } else forcecoul = 0.0; + if (rsq < cut_ljsq) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + } else forcelj = 0.0; + one.fforce = (factor_coul*forcecoul + factor_lj*forcelj) * r2inv; + + if (eflag) { + if (rsq < cut_coulsq) { + phicoul = force->qqrd2e * atom->q[i]*atom->q[j]*sqrt(r2inv); + if (rsq > cut_coul_innersq) { + switch1 = (cut_coulsq-rsq) * (cut_coulsq-rsq) * + (cut_coulsq + 2.0*rsq - 3.0*cut_coul_innersq) / + denom_coul; + phicoul *= switch1; + } + one.eng_coul = factor_coul*phicoul; + } else one.eng_coul = 0.0; + if (rsq < cut_ljsq) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + philj *= switch1; + } + one.eng_vdwl = factor_lj*philj; + } else one.eng_vdwl = 0.0; + } +} diff --git a/src/pair_lj_charmm_coul_charmm.h b/src/pair_lj_charmm_coul_charmm.h new file mode 100644 index 0000000000000000000000000000000000000000..d6a6c63d29422a2415d6ad93b4a370305c74ca65 --- /dev/null +++ b/src/pair_lj_charmm_coul_charmm.h @@ -0,0 +1,47 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_LJ_CHARMM_COUL_CHARMM_H +#define PAIR_LJ_CHARMM_COUL_CHARMM_H + +#include "pair.h" + +class PairLJCharmmCoulCharmm : public Pair { + public: + // these variables are public so DihedralCharmm can see them + double **lj14_1,**lj14_2,**lj14_3,**lj14_4; + + PairLJCharmmCoulCharmm() {} + ~PairLJCharmmCoulCharmm(); + virtual void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void init_style(); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + virtual void single(int, int, int, int, double, double, double, int, One &); + + protected: + double cut_lj_inner,cut_lj,cut_coul_inner,cut_coul; + double cut_lj_innersq,cut_ljsq,cut_coul_innersq,cut_coulsq,cut_bothsq; + double denom_lj,denom_coul; + double **epsilon,**sigma,**eps14,**sigma14; + double **lj1,**lj2,**lj3,**lj4; + + void allocate(); +}; + +#endif diff --git a/src/pair_lj_charmm_coul_charmm_implicit.cpp b/src/pair_lj_charmm_coul_charmm_implicit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d4bc75b172c3b5eda2cd6084e1f8db38a05094e2 --- /dev/null +++ b/src/pair_lj_charmm_coul_charmm_implicit.cpp @@ -0,0 +1,214 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "pair_lj_charmm_coul_charmm_implicit.h" +#include "atom.h" +#include "force.h" +#include "update.h" +#include "neighbor.h" + +/* ---------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmmImplicit::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double factor,phicoul,philj,switch1,switch2; + int *neighs; + double **f; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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_bothsq) { + r2inv = 1.0/rsq; + + if (rsq < cut_coulsq) { + forcecoul = 2.0 * qqrd2e * qtmp*q[j]*r2inv; + if (rsq > cut_coul_innersq) { + switch1 = (cut_coulsq-rsq) * (cut_coulsq-rsq) * + (cut_coulsq + 2.0*rsq - 3.0*cut_coul_innersq) / denom_coul; + switch2 = 12.0*rsq * (cut_coulsq-rsq) * + (rsq-cut_coul_innersq) / denom_coul; + forcecoul *= switch1 + switch2; + } + } else forcecoul = 0.0; + + if (rsq < cut_ljsq) { + r6inv = r2inv*r2inv*r2inv; + jtype = type[j]; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + } else forcelj = 0.0; + + fforce = (factor_coul*forcecoul + factor_lj*forcelj) * 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 (newton_pair || j < nlocal) factor = 1.0; + else factor = 0.5; + if (rsq < cut_coulsq) { + phicoul = qqrd2e * qtmp*q[j]*r2inv; + if (rsq > cut_coul_innersq) { + switch1 = (cut_coulsq-rsq) * (cut_coulsq-rsq) * + (cut_coulsq + 2.0*rsq - 3.0*cut_coul_innersq) / + denom_coul; + phicoul *= switch1; + } + eng_coul += factor*factor_coul*phicoul; + } + if (rsq < cut_ljsq) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + philj *= switch1; + } + eng_vdwl += factor*factor_lj*philj; + } + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCharmmCoulCharmmImplicit::single(int i, int j, int itype, int jtype, + double rsq, double factor_coul, + double factor_lj, + int eflag, One &one) +{ + double r2inv,r6inv,switch1,switch2,forcecoul,forcelj,phicoul,philj; + + r2inv = 1.0/rsq; + if (rsq < cut_coulsq) { + forcecoul = 2.0 * force->qqrd2e * atom->q[i]*atom->q[j]*r2inv; + if (rsq > cut_coul_innersq) { + switch1 = (cut_coulsq-rsq) * (cut_coulsq-rsq) * + (cut_coulsq + 2.0*rsq - 3.0*cut_coul_innersq) / denom_coul; + switch2 = 12.0*rsq * (cut_coulsq-rsq) * + (rsq-cut_coul_innersq) / denom_coul; + forcecoul *= switch1 + switch2; + } + } else forcecoul = 0.0; + if (rsq < cut_ljsq) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + } else forcelj = 0.0; + one.fforce = (factor_coul*forcecoul + factor_lj*forcelj) * r2inv; + + if (eflag) { + if (rsq < cut_coulsq) { + phicoul = force->qqrd2e * atom->q[i]*atom->q[j]*r2inv; + if (rsq > cut_coul_innersq) { + switch1 = (cut_coulsq-rsq) * (cut_coulsq-rsq) * + (cut_coulsq + 2.0*rsq - 3.0*cut_coul_innersq) / + denom_coul; + phicoul *= switch1; + } + one.eng_coul = factor_coul*phicoul; + } else one.eng_coul = 0.0; + if (rsq < cut_ljsq) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + philj *= switch1; + } + one.eng_vdwl = factor_lj*philj; + } else one.eng_vdwl = 0.0; + } +} diff --git a/src/pair_lj_charmm_coul_charmm_implicit.h b/src/pair_lj_charmm_coul_charmm_implicit.h new file mode 100644 index 0000000000000000000000000000000000000000..2ef707551f754b70853e893b4594e748e0519c82 --- /dev/null +++ b/src/pair_lj_charmm_coul_charmm_implicit.h @@ -0,0 +1,25 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_LJ_CHARMM_COUL_CHARMM_IMPLICIT_H +#define PAIR_LJ_CHARMM_COUL_CHARMM_IMPLICIT_H + +#include "pair_lj_charmm_coul_charmm.h" + +class PairLJCharmmCoulCharmmImplicit : public PairLJCharmmCoulCharmm { + public: + void compute(int, int); + void single(int, int, int, int, double, double, double, int, One &); +}; + +#endif diff --git a/src/pair_lj_charmm_coul_long.cpp b/src/pair_lj_charmm_coul_long.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d9e8d1713d163832018f07f4eac5c7a3ea57fdc0 --- /dev/null +++ b/src/pair_lj_charmm_coul_long.cpp @@ -0,0 +1,1160 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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_lj_charmm_coul_long.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "kspace.h" +#include "update.h" +#include "integrate.h" +#include "respa.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#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 + +/* ---------------------------------------------------------------------- */ + +PairLJCharmmCoulLong::PairLJCharmmCoulLong() +{ + respa_enable = 1; + ftable = NULL; +} + +/* ---------------------------------------------------------------------- */ + +PairLJCharmmCoulLong::~PairLJCharmmCoulLong() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(epsilon); + memory->destroy_2d_double_array(sigma); + memory->destroy_2d_double_array(eps14); + memory->destroy_2d_double_array(sigma14); + memory->destroy_2d_double_array(lj1); + memory->destroy_2d_double_array(lj2); + memory->destroy_2d_double_array(lj3); + memory->destroy_2d_double_array(lj4); + memory->destroy_2d_double_array(lj14_1); + memory->destroy_2d_double_array(lj14_2); + memory->destroy_2d_double_array(lj14_3); + memory->destroy_2d_double_array(lj14_4); + } + if (ftable) free_tables(); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype,itable; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz,fraction,table; + double r,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double grij,expm2,prefactor,t,erfc; + double factor,phicoul,philj,switch1,switch2; + int *neighs; + double **f; + float rsq; + int *int_rsq = (int *) &rsq; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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_bothsq) { + r2inv = 1.0/rsq; + + if (rsq < cut_coulsq) { + 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; + } + } + } else forcecoul = 0.0; + + if (rsq < cut_ljsq) { + r6inv = r2inv*r2inv*r2inv; + jtype = type[j]; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + } else forcelj = 0.0; + + fforce = (forcecoul + factor_lj*forcelj) * 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 (newton_pair || j < nlocal) factor = 1.0; + else factor = 0.5; + if (rsq < cut_coulsq) { + 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; + eng_coul += factor*phicoul; + } + if (rsq < cut_ljsq) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + philj *= switch1; + } + eng_vdwl += factor*factor_lj*philj; + } + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::compute_inner() +{ + int i,j,k,numneigh,itype,jtype; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double rsw; + int *neighs; + + double **f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + double cut_out_on = cut_respa[0]; + double cut_out_off = cut_respa[1]; + + double cut_out_diff = cut_out_off - cut_out_on; + double cut_out_on_sq = cut_out_on*cut_out_on; + double cut_out_off_sq = cut_out_off*cut_out_off; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh_inner[i]; + numneigh = neighbor->numneigh_inner[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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_out_off_sq) { + r2inv = 1.0/rsq; + forcecoul = qqrd2e * qtmp*q[j]*sqrt(r2inv); + if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*forcecoul; + + r6inv = r2inv*r2inv*r2inv; + jtype = type[j]; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + + fforce = (forcecoul + factor_lj*forcelj) * r2inv; + + if (rsq > cut_out_on_sq) { + rsw = (sqrt(rsq) - cut_out_on)/cut_out_diff; + fforce *= 1.0 + rsw*rsw*(2.0*rsw-3.0); + } + + 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; + } + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::compute_middle() +{ + int i,j,k,numneigh,itype,jtype; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double philj,switch1,switch2; + double rsw; + int *neighs; + + double **f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + double cut_in_off = cut_respa[0]; + double cut_in_on = cut_respa[1]; + double cut_out_on = cut_respa[2]; + double cut_out_off = cut_respa[3]; + + double cut_in_diff = cut_in_on - cut_in_off; + double cut_out_diff = cut_out_off - cut_out_on; + double cut_in_off_sq = cut_in_off*cut_in_off; + double cut_in_on_sq = cut_in_on*cut_in_on; + double cut_out_on_sq = cut_out_on*cut_out_on; + double cut_out_off_sq = cut_out_off*cut_out_off; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh_middle[i]; + numneigh = neighbor->numneigh_middle[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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_out_off_sq && rsq > cut_in_off_sq) { + r2inv = 1.0/rsq; + forcecoul = qqrd2e * qtmp*q[j]*sqrt(r2inv); + if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*forcecoul; + + r6inv = r2inv*r2inv*r2inv; + jtype = type[j]; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + + fforce = (forcecoul + factor_lj*forcelj) * r2inv; + if (rsq < cut_in_on_sq) { + rsw = (sqrt(rsq) - cut_in_off)/cut_in_diff; + fforce *= rsw*rsw*(3.0 - 2.0*rsw); + } + if (rsq > cut_out_on_sq) { + rsw = (sqrt(rsq) - cut_out_on)/cut_out_diff; + fforce *= 1.0 + rsw*rsw*(2.0*rsw - 3.0); + } + + 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; + } + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::compute_outer(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype,itable; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz,fraction,table; + double r,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double grij,expm2,prefactor,t,erfc; + double factor,phicoul,philj,switch1,switch2; + double rsw; + int *neighs; + float rsq; + int *int_rsq = (int *) &rsq; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + double **f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + double cut_in_off = cut_respa[2]; + double cut_in_on = cut_respa[3]; + + double cut_in_diff = cut_in_on - cut_in_off; + double cut_in_off_sq = cut_in_off*cut_in_off; + double cut_in_on_sq = cut_in_on*cut_in_on; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cut_bothsq) { + r2inv = 1.0/rsq; + + if (rsq < cut_coulsq) { + 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 - 1.0); + if (rsq > cut_in_off_sq) { + if (rsq < cut_in_on_sq) { + rsw = (r - cut_in_off)/cut_in_diff; + forcecoul += prefactor*rsw*rsw*(3.0 - 2.0*rsw); + if (factor_coul < 1.0) + forcecoul -= + (1.0-factor_coul)*prefactor*rsw*rsw*(3.0 - 2.0*rsw); + } else { + forcecoul += prefactor; + 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; + } + } + } else forcecoul = 0.0; + + if (rsq < cut_ljsq && rsq > cut_in_off_sq) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + if (rsq < cut_in_on_sq) { + rsw = (sqrtf(rsq) - cut_in_off)/cut_in_diff; + forcelj *= rsw*rsw*(3.0 - 2.0*rsw); + } + } else forcelj = 0.0; + + fforce = (forcecoul + forcelj) * 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 (newton_pair || j < nlocal) factor = 1.0; + else factor = 0.5; + if (rsq < cut_coulsq) { + if (!ncoultablebits || rsq <= tabinnersq) { + phicoul = prefactor*erfc; + if (factor_coul < 1.0) phicoul -= (1.0-factor_coul)*prefactor; + } else { + table = etable[itable] + fraction*detable[itable]; + phicoul = qtmp*q[j] * table; + if (factor_coul < 1.0) { + table = ptable[itable] + fraction*dptable[itable]; + prefactor = qtmp*q[j] * table; + phicoul -= (1.0-factor_coul)*prefactor; + } + } + eng_coul += factor*phicoul; + } + if (rsq < cut_ljsq) { + r6inv = r2inv*r2inv*r2inv; + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + philj *= switch1; + } + eng_vdwl += factor*factor_lj*philj; + } + } + + if (vflag) { + if (rsq < cut_coulsq) { + if (!ncoultablebits || rsq <= tabinnersq) { + forcecoul = prefactor * (erfc + EWALD_F*grij*expm2); + if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*prefactor; + } else { + table = vtable[itable] + fraction*dvtable[itable]; + forcecoul = qtmp*q[j] * table; + if (factor_coul < 1.0) { + table = ptable[itable] + fraction*dptable[itable]; + prefactor = qtmp*q[j] * table; + phicoul -= (1.0-factor_coul)*prefactor; + } + } + } else forcecoul = 0.0; + + if (rsq <= cut_in_off_sq) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + } else if (rsq <= cut_in_on_sq) { + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + } + + fforce = (forcecoul + factor_lj*forcelj) * r2inv; + + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::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"); + + epsilon = memory->create_2d_double_array(n+1,n+1,"pair:epsilon"); + sigma = memory->create_2d_double_array(n+1,n+1,"pair:sigma"); + eps14 = memory->create_2d_double_array(n+1,n+1,"pair:eps14"); + sigma14 = memory->create_2d_double_array(n+1,n+1,"pair:sigma14"); + lj1 = memory->create_2d_double_array(n+1,n+1,"pair:lj1"); + lj2 = memory->create_2d_double_array(n+1,n+1,"pair:lj2"); + lj3 = memory->create_2d_double_array(n+1,n+1,"pair:lj3"); + lj4 = memory->create_2d_double_array(n+1,n+1,"pair:lj4"); + lj14_1 = memory->create_2d_double_array(n+1,n+1,"pair:lj14_1"); + lj14_2 = memory->create_2d_double_array(n+1,n+1,"pair:lj14_2"); + lj14_3 = memory->create_2d_double_array(n+1,n+1,"pair:lj14_3"); + lj14_4 = memory->create_2d_double_array(n+1,n+1,"pair:lj14_4"); +} + +/* ---------------------------------------------------------------------- + global settings + unlike other pair styles, + there are no individual pair settings that these override +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::settings(int narg, char **arg) +{ + if (narg != 2 && narg != 3) error->all("Illegal pair_style command"); + + cut_lj_inner = atof(arg[0]); + cut_lj = atof(arg[1]); + if (narg == 2) cut_coul = cut_lj; + else cut_coul = atof(arg[2]); +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::coeff(int narg, char **arg) +{ + if (narg != 4 && narg != 6) error->all("Illegal pair_coeff command"); + 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 = atof(arg[2]); + double sigma_one = atof(arg[3]); + double eps14_one = epsilon_one; + double sigma14_one = sigma_one; + if (narg == 6) { + eps14_one = atof(arg[4]); + sigma14_one = atof(arg[5]); + } + + 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; + eps14[i][j] = eps14_one; + sigma14[i][j] = sigma14_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairLJCharmmCoulLong::init_one(int i, int j) +{ + // always mix arithmetically + + if (setflag[i][j] == 0) { + epsilon[i][j] = sqrt(epsilon[i][i]*epsilon[j][j]); + sigma[i][j] = 0.5 * (sigma[i][i] + sigma[j][j]); + eps14[i][j] = sqrt(eps14[i][i]*eps14[j][j]); + sigma14[i][j] = 0.5 * (sigma14[i][i] + sigma14[j][j]); + } + + double cut = MAX(cut_lj,cut_coul); + + 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); + lj14_1[i][j] = 48.0 * eps14[i][j] * pow(sigma14[i][j],12.0); + lj14_2[i][j] = 24.0 * eps14[i][j] * pow(sigma14[i][j],6.0); + lj14_3[i][j] = 4.0 * eps14[i][j] * pow(sigma14[i][j],12.0); + lj14_4[i][j] = 4.0 * eps14[i][j] * pow(sigma14[i][j],6.0); + + lj1[j][i] = lj1[i][j]; + lj2[j][i] = lj2[i][j]; + lj3[j][i] = lj3[i][j]; + lj4[j][i] = lj4[i][j]; + lj14_1[j][i] = lj14_1[i][j]; + lj14_2[j][i] = lj14_2[i][j]; + lj14_3[j][i] = lj14_3[i][j]; + lj14_4[j][i] = lj14_4[i][j]; + + return cut; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::init_style() +{ + // require an atom style with charge defined + + if (atom->charge_allow == 0) + error->all("Must use charged atom style with this pair style"); + + // require cut_lj_inner < cut_lj + + if (cut_lj_inner >= cut_lj) + error->all("Pair inner cutoff >= Pair outer cutoff"); + + cut_lj_innersq = cut_lj_inner * cut_lj_inner; + cut_ljsq = cut_lj * cut_lj; + cut_coulsq = cut_coul * cut_coul; + cut_bothsq = MAX(cut_ljsq,cut_coulsq); + + denom_lj = (cut_ljsq-cut_lj_innersq) * (cut_ljsq-cut_lj_innersq) * + (cut_ljsq-cut_lj_innersq); + + // set & error check interior rRESPA cutoffs + + cut_respa = NULL; + if (strcmp(update->integrate_style,"respa") == 0) { + if (((Respa *) update->integrate)->level_inner >= 0) { + cut_respa = ((Respa *) update->integrate)->cutoff; + if (MIN(cut_lj,cut_coul) < cut_respa[3]) + error->all("Pair cutoff < Respa interior cutoff"); + if (cut_lj_inner < cut_respa[1]) + error->all("Pair inner 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"); + else if (strcmp(force->kspace_style,"ewald") == 0) + g_ewald = force->kspace->g_ewald; + else if (strcmp(force->kspace_style,"pppm") == 0) + g_ewald = force->kspace->g_ewald; + else if (strcmp(force->kspace_style,"pppm2") == 0) + g_ewald = force->kspace->g_ewald; + else error->all("Pair style is incompatible with KSpace style"); + + // setup force tables + + if (ncoultablebits) init_tables(); +} + +/* ---------------------------------------------------------------------- + setup force tables used in compute routines +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::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 PairLJCharmmCoulLong::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + 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(&eps14[i][j],sizeof(double),1,fp); + fwrite(&sigma14[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::read_restart(FILE *fp) +{ + read_restart_settings(fp); + + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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(&eps14[i][j],sizeof(double),1,fp); + fread(&sigma14[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(&eps14[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&sigma14[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJCharmmCoulLong::write_restart_settings(FILE *fp) +{ + fwrite(&cut_lj_inner,sizeof(double),1,fp); + fwrite(&cut_lj,sizeof(double),1,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 PairLJCharmmCoulLong::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&cut_lj_inner,sizeof(double),1,fp); + fread(&cut_lj,sizeof(double),1,fp); + fread(&cut_coul,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + MPI_Bcast(&cut_lj_inner,1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_lj,1,MPI_DOUBLE,0,world); + 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 PairLJCharmmCoulLong::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 PairLJCharmmCoulLong::single(int i, int j, int itype, int jtype, + double rsq, + double factor_coul, double factor_lj, + int eflag, One &one) +{ + double r2inv,r6inv,r,grij,expm2,t,erfc,prefactor; + double switch1,switch2,fraction,table,forcecoul,forcelj,phicoul,philj; + int itable; + + r2inv = 1.0/rsq; + if (rsq < cut_coulsq) { + 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; + } + } + } else forcecoul = 0.0; + if (rsq < cut_ljsq) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * + (rsq-cut_lj_innersq) / denom_lj; + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + forcelj = forcelj*switch1 + philj*switch2; + } + } else forcelj = 0.0; + one.fforce = (forcecoul + factor_lj*forcelj) * r2inv; + + if (eflag) { + if (rsq < cut_coulsq) { + 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; + } else one.eng_coul = 0.0; + if (rsq < cut_ljsq) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + philj *= switch1; + } + one.eng_vdwl = factor_lj*philj; + } else one.eng_vdwl = 0.0; + } +} diff --git a/src/pair_lj_charmm_coul_long.h b/src/pair_lj_charmm_coul_long.h new file mode 100644 index 0000000000000000000000000000000000000000..d25ce9879f63a10f93768a6d1d8715d5efbe6fe9 --- /dev/null +++ b/src/pair_lj_charmm_coul_long.h @@ -0,0 +1,62 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_LJ_CHARMM_COUL_LONG_H +#define PAIR_LJ_CHARMM_COUL_LONG_H + +#include "pair.h" + +class PairLJCharmmCoulLong : public Pair { + public: + double cut_coul; + // these variables are public so DihedralCharmm can see them + double **lj14_1,**lj14_2,**lj14_3,**lj14_4; + + PairLJCharmmCoulLong(); + ~PairLJCharmmCoulLong(); + void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void init_style(); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + void single(int, int, int, int, double, double, double, int, One &); + + void compute_inner(); + void compute_middle(); + void compute_outer(int, int); + + private: + double cut_lj_inner,cut_lj; + double cut_lj_innersq,cut_ljsq,cut_coulsq; + double cut_bothsq; + double denom_lj; + double **epsilon,**sigma,**eps14,**sigma14; + double **lj1,**lj2,**lj3,**lj4,**offset; + double *cut_respa; + double g_ewald; + + double tabinnersq; + double *rtable,*drtable,*ftable,*dftable,*ctable,*dctable; + double *etable,*detable,*ptable,*dptable,*vtable,*dvtable; + int ncoulshiftbits,ncoulmask; + + void allocate(); + void init_tables(); + void free_tables(); +}; + +#endif diff --git a/src/pair_lj_class2_coul_long.h b/src/pair_lj_class2_coul_long.h new file mode 100644 index 0000000000000000000000000000000000000000..830cb6f0ca75e04410c339f52c0255b9cc27abb7 --- /dev/null +++ b/src/pair_lj_class2_coul_long.h @@ -0,0 +1,47 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_LJ_CLASS2_COUL_LONG_H +#define PAIR_LJ_CLASS2_COUL_LONG_H + +#include "pair.h" + +class PairLJClass2CoulLong : public Pair { + public: + double cut_coul; + + PairLJClass2CoulLong() {} + ~PairLJClass2CoulLong(); + void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void init_style(); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + void single(int, int, int, int, double, double, double, int, One &); + + private: + double cut_lj_global; + double **cut_lj,**cut_ljsq; + double cut_coulsq; + double **epsilon,**sigma; + double **lj1,**lj2,**lj3,**lj4,**offset; + double g_ewald; + + void allocate(); +}; + +#endif diff --git a/src/pair_lj_cut.cpp b/src/pair_lj_cut.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2d341613a02a2d14d9d9d3f26174389e3ed5cf96 --- /dev/null +++ b/src/pair_lj_cut.cpp @@ -0,0 +1,670 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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_lj_cut.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "neighbor.h" +#include "update.h" +#include "integrate.h" +#include "respa.h" +#include "memory.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +PairLJCut::PairLJCut() +{ + respa_enable = 1; +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +PairLJCut::~PairLJCut() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(cut); + memory->destroy_2d_double_array(epsilon); + memory->destroy_2d_double_array(sigma); + memory->destroy_2d_double_array(lj1); + memory->destroy_2d_double_array(lj2); + memory->destroy_2d_double_array(lj3); + memory->destroy_2d_double_array(lj4); + memory->destroy_2d_double_array(offset); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCut::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcelj,fforce,factor_lj,philj; + int *neighs; + double **f; + + eng_vdwl = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_lj = 1.0; + else { + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r2inv = 1.0/rsq; + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + fforce = factor_lj*forcelj*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) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]) - + offset[itype][jtype]; + if (newton_pair || j < nlocal) eng_vdwl += factor_lj*philj; + else eng_vdwl += 0.5*factor_lj*philj; + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCut::compute_inner() +{ + int i,j,k,numneigh,itype,jtype; + double xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcelj,fforce,factor_lj; + double rsw; + int *neighs; + double **f; + + f = atom->f; + double **x = atom->x; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + + double cut_out_on = cut_respa[0]; + double cut_out_off = cut_respa[1]; + + double cut_out_diff = cut_out_off - cut_out_on; + double cut_out_on_sq = cut_out_on*cut_out_on; + double cut_out_off_sq = cut_out_off*cut_out_off; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh_inner[i]; + numneigh = neighbor->numneigh_inner[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_lj = 1.0; + else { + factor_lj = special_lj[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_out_off_sq) { + r2inv = 1.0/rsq; + r6inv = r2inv*r2inv*r2inv; + jtype = type[j]; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + fforce = factor_lj*forcelj*r2inv; + if (rsq > cut_out_on_sq) { + rsw = (sqrt(rsq) - cut_out_on)/cut_out_diff; + fforce *= 1.0 - rsw*rsw*(3.0 - 2.0*rsw); + } + + 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; + } + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCut::compute_middle() +{ + int i,j,k,numneigh,itype,jtype; + double xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcelj,fforce,factor_lj; + double rsw; + int *neighs; + double **f; + + f = atom->f; + double **x = atom->x; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + + double cut_in_off = cut_respa[0]; + double cut_in_on = cut_respa[1]; + double cut_out_on = cut_respa[2]; + double cut_out_off = cut_respa[3]; + + double cut_in_diff = cut_in_on - cut_in_off; + double cut_out_diff = cut_out_off - cut_out_on; + double cut_in_off_sq = cut_in_off*cut_in_off; + double cut_in_on_sq = cut_in_on*cut_in_on; + double cut_out_on_sq = cut_out_on*cut_out_on; + double cut_out_off_sq = cut_out_off*cut_out_off; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh_middle[i]; + numneigh = neighbor->numneigh_middle[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_lj = 1.0; + else { + factor_lj = special_lj[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_out_off_sq && rsq > cut_in_off_sq) { + r2inv = 1.0/rsq; + r6inv = r2inv*r2inv*r2inv; + jtype = type[j]; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + fforce = factor_lj*forcelj*r2inv; + if (rsq < cut_in_on_sq) { + rsw = (sqrt(rsq) - cut_in_off)/cut_in_diff; + fforce *= rsw*rsw*(3.0 - 2.0*rsw); + } + if (rsq > cut_out_on_sq) { + rsw = (sqrt(rsq) - cut_out_on)/cut_out_diff; + fforce *= 1.0 + rsw*rsw*(2.0*rsw - 3.0); + } + + 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; + } + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCut::compute_outer(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcelj,fforce,factor_lj,philj; + double rsw; + int *neighs; + double **f; + + eng_vdwl = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + f = atom->f; + double **x = atom->x; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + + double cut_in_off = cut_respa[2]; + double cut_in_on = cut_respa[3]; + + double cut_in_diff = cut_in_on - cut_in_off; + double cut_in_off_sq = cut_in_off*cut_in_off; + double cut_in_on_sq = cut_in_on*cut_in_on; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_lj = 1.0; + else { + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + if (rsq > cut_in_off_sq) { + r2inv = 1.0/rsq; + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + fforce = factor_lj*forcelj*r2inv; + if (rsq < cut_in_on_sq) { + rsw = (sqrt(rsq) - cut_in_off)/cut_in_diff; + fforce *= rsw*rsw*(3.0 - 2.0*rsw); + } + + 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) { + r2inv = 1.0/rsq; + r6inv = r2inv*r2inv*r2inv; + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]) - + offset[itype][jtype]; + if (newton_pair || j < nlocal) eng_vdwl += factor_lj*philj; + else eng_vdwl += 0.5*factor_lj*philj; + } + + if (vflag) { + if (rsq <= cut_in_off_sq) { + r2inv = 1.0/rsq; + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + fforce = factor_lj*forcelj*r2inv; + } else if (rsq < cut_in_on_sq) + fforce = factor_lj*forcelj*r2inv; + + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairLJCut::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"); + + cut = memory->create_2d_double_array(n+1,n+1,"pair:cut"); + epsilon = memory->create_2d_double_array(n+1,n+1,"pair:epsilon"); + sigma = memory->create_2d_double_array(n+1,n+1,"pair:sigma"); + lj1 = memory->create_2d_double_array(n+1,n+1,"pair:lj1"); + lj2 = memory->create_2d_double_array(n+1,n+1,"pair:lj2"); + lj3 = memory->create_2d_double_array(n+1,n+1,"pair:lj3"); + lj4 = memory->create_2d_double_array(n+1,n+1,"pair:lj4"); + offset = memory->create_2d_double_array(n+1,n+1,"pair:offset"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairLJCut::settings(int narg, char **arg) +{ + if (narg != 1) error->all("Illegal pair_style command"); + + cut_global = atof(arg[0]); + + // 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 PairLJCut::coeff(int narg, char **arg) +{ + if (narg < 4 || narg > 5) 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); + + double epsilon_one = atof(arg[2]); + double sigma_one = atof(arg[3]); + + double cut_one = cut_global; + if (narg == 5) cut_one = atof(arg[4]); + + 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; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairLJCut::init_one(int i, int j) +{ + 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; + + 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]; + + // set & error check interior rRESPA cutoff + + if (strcmp(update->integrate_style,"respa") == 0) { + if (((Respa *) update->integrate)->level_inner >= 0) { + cut_respa = ((Respa *) update->integrate)->cutoff; + if (cut[i][j] < cut_respa[3]) + error->all("Pair cutoff < Respa interior cutoff"); + } + } else cut_respa = NULL; + + // compute I,J contribution to long-range tail correction + // count total # of atoms of type I and J via Allreduce + + if (tail_flag) { + int *type = atom->type; + int nlocal = atom->nlocal; + + double count[2],all[2]; + count[0] = count[1] = 0.0; + for (int k = 0; k < nlocal; k++) { + if (type[k] == i) count[0] += 1.0; + if (type[k] == j) count[1] += 1.0; + } + MPI_Allreduce(count,all,2,MPI_DOUBLE,MPI_SUM,world); + + double PI = 4.0*atan(1.0); + double sig2 = sigma[i][j]*sigma[i][j]; + double sig6 = sig2*sig2*sig2; + double rc3 = cut[i][j]*cut[i][j]*cut[i][j]; + double rc6 = rc3*rc3; + double rc9 = rc3*rc6; + etail_ij = 8.0*PI*all[0]*all[1]*epsilon[i][j] * + sig6 * (sig6 - 3.0*rc6) / (9.0*rc9); + ptail_ij = 16.0*PI*all[0]*all[1]*epsilon[i][j] * + sig6 * (2.0*sig6 - 3.0*rc6) / (9.0*rc9); + } + + return cut[i][j]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJCut::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + 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 PairLJCut::read_restart(FILE *fp) +{ + read_restart_settings(fp); + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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 PairLJCut::write_restart_settings(FILE *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 PairLJCut::read_restart_settings(FILE *fp) +{ + int me = comm->me; + if (me == 0) { + fread(&cut_global,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + 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); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCut::single(int i, int j, int itype, int jtype, double rsq, + double factor_coul, double factor_lj, int eflag, + One &one) +{ + double r2inv,r6inv,forcelj,philj; + + r2inv = 1.0/rsq; + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + one.fforce = factor_lj*forcelj*r2inv; + + if (eflag) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]) - + offset[itype][jtype]; + one.eng_vdwl = factor_lj*philj; + one.eng_coul = 0.0; + } +} diff --git a/src/pair_lj_cut.h b/src/pair_lj_cut.h new file mode 100644 index 0000000000000000000000000000000000000000..fbe59c40ecdce58a4fa51c3919fd5e40dcdac46c --- /dev/null +++ b/src/pair_lj_cut.h @@ -0,0 +1,47 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_LJ_CUT_H +#define PAIR_LJ_CUT_H + +#include "pair.h" + +class PairLJCut : public Pair { + public: + PairLJCut(); + ~PairLJCut(); + void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + void single(int, int, int, int, double, double, double, int, One &); + + void compute_inner(); + void compute_middle(); + void compute_outer(int, int); + + private: + double cut_global; + double **cut; + double **epsilon,**sigma; + double **lj1,**lj2,**lj3,**lj4,**offset; + double *cut_respa; + + void allocate(); +}; + +#endif diff --git a/src/pair_lj_cut_coul_cut.cpp b/src/pair_lj_cut_coul_cut.cpp new file mode 100644 index 0000000000000000000000000000000000000000..03b80c860c824e53517b3f020e357639df1004b4 --- /dev/null +++ b/src/pair_lj_cut_coul_cut.cpp @@ -0,0 +1,445 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "pair_lj_cut_coul_cut.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +PairLJCutCoulCut::~PairLJCutCoulCut() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(cut_lj); + memory->destroy_2d_double_array(cut_ljsq); + memory->destroy_2d_double_array(cut_coul); + memory->destroy_2d_double_array(cut_coulsq); + memory->destroy_2d_double_array(epsilon); + memory->destroy_2d_double_array(sigma); + memory->destroy_2d_double_array(lj1); + memory->destroy_2d_double_array(lj2); + memory->destroy_2d_double_array(lj3); + memory->destroy_2d_double_array(lj4); + memory->destroy_2d_double_array(offset); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCutCoulCut::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double factor,phicoul,philj; + int *neighs; + double **f; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r2inv = 1.0/rsq; + + if (rsq < cut_coulsq[itype][jtype]) + forcecoul = qqrd2e * qtmp*q[j]*sqrt(r2inv); + else forcecoul = 0.0; + + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + } else forcelj = 0.0; + + fforce = (factor_coul*forcecoul + factor_lj*forcelj) * 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 (newton_pair || j < nlocal) factor = 1.0; + else factor = 0.5; + if (rsq < cut_coulsq[itype][jtype]) { + phicoul = qqrd2e * qtmp*q[j]*sqrt(r2inv); + eng_coul += factor*factor_coul*phicoul; + } + if (rsq < cut_ljsq[itype][jtype]) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]) - + offset[itype][jtype]; + eng_vdwl += factor*factor_lj*philj; + } + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairLJCutCoulCut::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"); + + cut_lj = memory->create_2d_double_array(n+1,n+1,"pair:cut_lj"); + cut_ljsq = memory->create_2d_double_array(n+1,n+1,"pair:cut_ljsq"); + cut_coul = memory->create_2d_double_array(n+1,n+1,"pair:cut_coul"); + cut_coulsq = memory->create_2d_double_array(n+1,n+1,"pair:cut_coulsq"); + epsilon = memory->create_2d_double_array(n+1,n+1,"pair:epsilon"); + sigma = memory->create_2d_double_array(n+1,n+1,"pair:sigma"); + lj1 = memory->create_2d_double_array(n+1,n+1,"pair:lj1"); + lj2 = memory->create_2d_double_array(n+1,n+1,"pair:lj2"); + lj3 = memory->create_2d_double_array(n+1,n+1,"pair:lj3"); + lj4 = memory->create_2d_double_array(n+1,n+1,"pair:lj4"); + offset = memory->create_2d_double_array(n+1,n+1,"pair:offset"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairLJCutCoulCut::settings(int narg, char **arg) +{ + if (narg < 1 || narg > 2) error->all("Illegal pair_style command"); + + cut_lj_global = atof(arg[0]); + if (narg == 1) cut_coul_global = cut_lj_global; + else cut_coul_global = atof(arg[1]); + + // 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_lj[i][j] = cut_lj_global; + cut_coul[i][j] = cut_coul_global; + } + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairLJCutCoulCut::coeff(int narg, char **arg) +{ + if (narg < 4 || narg > 6) 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); + + double epsilon_one = atof(arg[2]); + double sigma_one = atof(arg[3]); + + double cut_lj_one = cut_lj_global; + double cut_coul_one = cut_coul_global; + if (narg >= 5) cut_coul_one = cut_lj_one = atof(arg[4]); + if (narg == 6) cut_coul_one = atof(arg[5]); + + 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_lj[i][j] = cut_lj_one; + cut_coul[i][j] = cut_coul_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairLJCutCoulCut::init_one(int i, int j) +{ + 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_lj[i][j] = mix_distance(cut_lj[i][i],cut_lj[j][j]); + cut_coul[i][j] = mix_distance(cut_coul[i][i],cut_coul[j][j]); + } + + double cut = MAX(cut_lj[i][j],cut_coul[i][j]); + cut_ljsq[i][j] = cut_lj[i][j] * cut_lj[i][j]; + cut_coulsq[i][j] = cut_coul[i][j] * cut_coul[i][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_lj[i][j]; + offset[i][j] = 4.0 * epsilon[i][j] * (pow(ratio,12.0) - pow(ratio,6.0)); + } else offset[i][j] = 0.0; + + cut_ljsq[j][i] = cut_ljsq[i][j]; + cut_coulsq[j][i] = cut_coulsq[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]; + + // compute I,J contribution to long-range tail correction + // count total # of atoms of type I and J via Allreduce + + if (tail_flag) { + int *type = atom->type; + int nlocal = atom->nlocal; + + double count[2],all[2]; + count[0] = count[1] = 0.0; + for (int k = 0; k < nlocal; k++) { + if (type[k] == i) count[0] += 1.0; + if (type[k] == j) count[1] += 1.0; + } + MPI_Allreduce(count,all,2,MPI_DOUBLE,MPI_SUM,world); + + double PI = 4.0*atan(1.0); + double sig2 = sigma[i][j]*sigma[i][j]; + double sig6 = sig2*sig2*sig2; + double rc3 = cut_lj[i][j]*cut_lj[i][j]*cut_lj[i][j]; + double rc6 = rc3*rc3; + double rc9 = rc3*rc6; + etail_ij = 8.0*PI*all[0]*all[1]*epsilon[i][j] * + sig6 * (sig6 - 3.0*rc6) / (9.0*rc9); + ptail_ij = 16.0*PI*all[0]*all[1]*epsilon[i][j] * + sig6 * (2.0*sig6 - 3.0*rc6) / (9.0*rc9); + } + + return cut; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairLJCutCoulCut::init_style() +{ + // require an atom style with charge defined + + if (atom->charge_allow == 0) + error->all("Must use charged atom style with this pair style"); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJCutCoulCut::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + 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_lj[i][j],sizeof(double),1,fp); + fwrite(&cut_coul[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairLJCutCoulCut::read_restart(FILE *fp) +{ + read_restart_settings(fp); + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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_lj[i][j],sizeof(double),1,fp); + fread(&cut_coul[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_lj[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_coul[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJCutCoulCut::write_restart_settings(FILE *fp) +{ + fwrite(&cut_lj_global,sizeof(double),1,fp); + fwrite(&cut_coul_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 PairLJCutCoulCut::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&cut_lj_global,sizeof(double),1,fp); + fread(&cut_coul_global,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + MPI_Bcast(&cut_lj_global,1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_coul_global,1,MPI_DOUBLE,0,world); + MPI_Bcast(&offset_flag,1,MPI_INT,0,world); + MPI_Bcast(&mix_flag,1,MPI_INT,0,world); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCutCoulCut::single(int i, int j, int itype, int jtype, + double rsq, double factor_coul, double factor_lj, + int eflag, One &one) +{ + double r2inv,r6inv,forcecoul,forcelj,phicoul,philj; + + r2inv = 1.0/rsq; + if (rsq < cut_coulsq[itype][jtype]) + forcecoul = force->qqrd2e * atom->q[i]*atom->q[j]*sqrt(r2inv); + else forcecoul = 0.0; + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + } else forcelj = 0.0; + one.fforce = (factor_coul*forcecoul + factor_lj*forcelj) * r2inv; + + if (eflag) { + if (rsq < cut_coulsq[itype][jtype]) { + phicoul = force->qqrd2e * atom->q[i]*atom->q[j]*sqrt(r2inv); + one.eng_coul = factor_coul*phicoul; + } else one.eng_coul = 0.0; + if (rsq < cut_ljsq[itype][jtype]) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]) - + offset[itype][jtype]; + one.eng_vdwl = factor_lj*philj; + } else one.eng_vdwl = 0.0; + } +} diff --git a/src/pair_lj_cut_coul_cut.h b/src/pair_lj_cut_coul_cut.h new file mode 100644 index 0000000000000000000000000000000000000000..e11b2afd1a250501570dbcd02aaca63773bdc78b --- /dev/null +++ b/src/pair_lj_cut_coul_cut.h @@ -0,0 +1,44 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_LJ_CUT_COUL_CUT_H +#define PAIR_LJ_CUT_COUL_CUT_H + +#include "pair.h" + +class PairLJCutCoulCut : public Pair { + public: + PairLJCutCoulCut() {} + ~PairLJCutCoulCut(); + virtual void compute(int, int); + virtual void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void init_style(); + virtual void write_restart(FILE *); + virtual void read_restart(FILE *); + virtual void write_restart_settings(FILE *); + virtual void read_restart_settings(FILE *); + virtual void single(int, int, int, int, double, double, double, int, One &); + + protected: + double cut_lj_global,cut_coul_global; + double **cut_lj,**cut_ljsq; + double **cut_coul,**cut_coulsq; + double **epsilon,**sigma; + double **lj1,**lj2,**lj3,**lj4,**offset; + + void allocate(); +}; + +#endif diff --git a/src/pair_lj_cut_coul_debye.cpp b/src/pair_lj_cut_coul_debye.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ae0e45a91be19584bba3e7d43385a33822c17525 --- /dev/null +++ b/src/pair_lj_cut_coul_debye.cpp @@ -0,0 +1,285 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "pair_lj_cut_coul_debye.h" +#include "atom.h" +#include "neighbor.h" +#include "force.h" +#include "comm.h" +#include "update.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +void PairLJCutCoulDebye::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double factor,phicoul,philj; + double r,rinv,screening; + int *neighs; + double **f; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r2inv = 1.0/rsq; + + if (rsq < cut_coulsq[itype][jtype]) { + r = sqrt(rsq); + rinv = 1.0/r; + screening = exp(-kappa*r); + forcecoul = qqrd2e * qtmp*q[j] * screening * (kappa + rinv); + } else forcecoul = 0.0; + + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + } else forcelj = 0.0; + + fforce = (factor_coul*forcecoul + factor_lj*forcelj) * 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 (newton_pair || j < nlocal) factor = 1.0; + else factor = 0.5; + if (rsq < cut_coulsq[itype][jtype]) { + phicoul = qqrd2e * qtmp*q[j] * rinv * screening; + eng_coul += factor*factor_coul*phicoul; + } + if (rsq < cut_ljsq[itype][jtype]) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]) - + offset[itype][jtype]; + eng_vdwl += factor*factor_lj*philj; + } + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairLJCutCoulDebye::settings(int narg, char **arg) +{ + if (narg < 2 || narg > 3) error->all("Illegal pair_style command"); + + kappa = atof(arg[0]); + cut_lj_global = atof(arg[1]); + if (narg == 2) cut_coul_global = cut_lj_global; + else cut_coul_global = atof(arg[2]); + + // reset cutoffs that were previously set from data file + + if (allocated) { + int i,j; + for (i = 1; i <= atom->ntypes; i++) + for (j = i+1; j <= atom->ntypes; j++) + if (setflag[i][j] == 1) { + cut_lj[i][j] = cut_lj_global; + cut_coul[i][j] = cut_coul_global; + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJCutCoulDebye::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + 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_lj[i][j],sizeof(double),1,fp); + fwrite(&cut_coul[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairLJCutCoulDebye::read_restart(FILE *fp) +{ + read_restart_settings(fp); + + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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_lj[i][j],sizeof(double),1,fp); + fread(&cut_coul[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_lj[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_coul[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJCutCoulDebye::write_restart_settings(FILE *fp) +{ + fwrite(&cut_lj_global,sizeof(double),1,fp); + fwrite(&cut_coul_global,sizeof(double),1,fp); + fwrite(&kappa,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 PairLJCutCoulDebye::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&cut_lj_global,sizeof(double),1,fp); + fread(&cut_coul_global,sizeof(double),1,fp); + fread(&kappa,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + MPI_Bcast(&cut_lj_global,1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_coul_global,1,MPI_DOUBLE,0,world); + MPI_Bcast(&kappa,1,MPI_DOUBLE,0,world); + MPI_Bcast(&offset_flag,1,MPI_INT,0,world); + MPI_Bcast(&mix_flag,1,MPI_INT,0,world); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCutCoulDebye::single(int i, int j, int itype, int jtype, double rsq, + double factor_coul, double factor_lj, + int eflag, One &one) +{ + double r2inv,r6inv,r,rinv,screening,forcecoul,forcelj,phicoul,philj; + + r2inv = 1.0/rsq; + if (rsq < cut_coulsq[itype][jtype]) { + r = sqrt(rsq); + rinv = 1.0/r; + screening = exp(-kappa*r); + forcecoul = force->qqrd2e * atom->q[i]*atom->q[j] * + screening * (kappa + rinv); + } else forcecoul = 0.0; + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + } else forcelj = 0.0; + one.fforce = (factor_coul*forcecoul + factor_lj*forcelj) * r2inv; + + if (eflag) { + if (rsq < cut_coulsq[itype][jtype]) { + phicoul = force->qqrd2e * atom->q[i]*atom->q[j] * rinv * screening; + one.eng_coul = factor_coul*phicoul; + } else one.eng_coul = 0.0; + if (rsq < cut_ljsq[itype][jtype]) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]) - + offset[itype][jtype]; + one.eng_vdwl = factor_lj*philj; + } else one.eng_vdwl = 0.0; + } +} diff --git a/src/pair_lj_cut_coul_debye.h b/src/pair_lj_cut_coul_debye.h new file mode 100644 index 0000000000000000000000000000000000000000..b5aa1951018cbbbace5a6d4c5ebfdd0d79e82107 --- /dev/null +++ b/src/pair_lj_cut_coul_debye.h @@ -0,0 +1,33 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_LJ_CUT_COUL_DEBYE_H +#define PAIR_LJ_CUT_COUL_DEBYE_H + +#include "pair_lj_cut_coul_cut.h" + +class PairLJCutCoulDebye : public PairLJCutCoulCut { + public: + void compute(int, int); + void settings(int, char **); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + void single(int, int, int, int, double, double, double, int, One &); + + private: + double kappa; +}; + +#endif diff --git a/src/pair_lj_cut_coul_long.cpp b/src/pair_lj_cut_coul_long.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1b6a3649760ce87b1ecbafdd1f3aa181ba141d5b --- /dev/null +++ b/src/pair_lj_cut_coul_long.cpp @@ -0,0 +1,1104 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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_lj_cut_coul_long.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "kspace.h" +#include "update.h" +#include "integrate.h" +#include "respa.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#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 + +/* ---------------------------------------------------------------------- */ + +PairLJCutCoulLong::PairLJCutCoulLong() +{ + respa_enable = 1; + ftable = NULL; +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +PairLJCutCoulLong::~PairLJCutCoulLong() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(cut_lj); + memory->destroy_2d_double_array(cut_ljsq); + memory->destroy_2d_double_array(epsilon); + memory->destroy_2d_double_array(sigma); + memory->destroy_2d_double_array(lj1); + memory->destroy_2d_double_array(lj2); + memory->destroy_2d_double_array(lj3); + memory->destroy_2d_double_array(lj4); + memory->destroy_2d_double_array(offset); + } + if (ftable) free_tables(); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCutCoulLong::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype,itable; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz,fraction,table; + double r,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double grij,expm2,prefactor,t,erfc; + double factor,phicoul,philj; + int *neighs; + double **f; + float rsq; + int *int_rsq = (int *) &rsq; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r2inv = 1.0/rsq; + + if (rsq < cut_coulsq) { + 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; + } + } + } else forcecoul = 0.0; + + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + } else forcelj = 0.0; + + fforce = (forcecoul + factor_lj*forcelj) * 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 (newton_pair || j < nlocal) factor = 1.0; + else factor = 0.5; + if (rsq < cut_coulsq) { + 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; + eng_coul += factor*phicoul; + } + if (rsq < cut_ljsq[itype][jtype]) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]) - + offset[itype][jtype]; + eng_vdwl += factor*factor_lj*philj; + } + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCutCoulLong::compute_inner() +{ + int i,j,k,numneigh,itype,jtype; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double rsw; + int *neighs; + + double **f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + double cut_out_on = cut_respa[0]; + double cut_out_off = cut_respa[1]; + + double cut_out_diff = cut_out_off - cut_out_on; + double cut_out_on_sq = cut_out_on*cut_out_on; + double cut_out_off_sq = cut_out_off*cut_out_off; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh_inner[i]; + numneigh = neighbor->numneigh_inner[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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_out_off_sq) { + r2inv = 1.0/rsq; + forcecoul = qqrd2e * qtmp*q[j]*sqrt(r2inv); + if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*forcecoul; + + jtype = type[j]; + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + } else forcelj = 0.0; + + fforce = (forcecoul + factor_lj*forcelj) * r2inv; + if (rsq > cut_out_on_sq) { + rsw = (sqrt(rsq) - cut_out_on)/cut_out_diff; + fforce *= 1.0 + rsw*rsw*(2.0*rsw-3.0); + } + + 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; + } + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCutCoulLong::compute_middle() +{ + int i,j,k,numneigh,itype,jtype; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double rsw; + int *neighs; + + double **f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + double cut_in_off = cut_respa[0]; + double cut_in_on = cut_respa[1]; + double cut_out_on = cut_respa[2]; + double cut_out_off = cut_respa[3]; + + double cut_in_diff = cut_in_on - cut_in_off; + double cut_out_diff = cut_out_off - cut_out_on; + double cut_in_off_sq = cut_in_off*cut_in_off; + double cut_in_on_sq = cut_in_on*cut_in_on; + double cut_out_on_sq = cut_out_on*cut_out_on; + double cut_out_off_sq = cut_out_off*cut_out_off; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh_middle[i]; + numneigh = neighbor->numneigh_middle[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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_out_off_sq && rsq > cut_in_off_sq) { + r2inv = 1.0/rsq; + forcecoul = qqrd2e * qtmp*q[j]*sqrt(r2inv); + if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*forcecoul; + + jtype = type[j]; + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + } else forcelj = 0.0; + + fforce = (forcecoul + factor_lj*forcelj) * r2inv; + if (rsq < cut_in_on_sq) { + rsw = (sqrt(rsq) - cut_in_off)/cut_in_diff; + fforce *= rsw*rsw*(3.0 - 2.0*rsw); + } + if (rsq > cut_out_on_sq) { + rsw = (sqrt(rsq) - cut_out_on)/cut_out_diff; + fforce *= 1.0 + rsw*rsw*(2.0*rsw - 3.0); + } + + 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; + } + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCutCoulLong::compute_outer(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype,itable; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz,fraction,table; + double r,r2inv,r6inv,forcecoul,forcelj,fforce,factor_coul,factor_lj; + double grij,expm2,prefactor,t,erfc; + double factor,phicoul,philj; + double rsw; + int *neighs; + float rsq; + int *int_rsq = (int *) &rsq; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + double **f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + double cut_in_off = cut_respa[2]; + double cut_in_on = cut_respa[3]; + + double cut_in_diff = cut_in_on - cut_in_off; + double cut_in_off_sq = cut_in_off*cut_in_off; + double cut_in_on_sq = cut_in_on*cut_in_on; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r2inv = 1.0/rsq; + + if (rsq < cut_coulsq) { + 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 - 1.0); + if (rsq > cut_in_off_sq) { + if (rsq < cut_in_on_sq) { + rsw = (r - cut_in_off)/cut_in_diff; + forcecoul += prefactor*rsw*rsw*(3 - 2*rsw); + if (factor_coul < 1.0) + forcecoul -= (1.0-factor_coul)*prefactor*rsw*rsw*(3 - 2*rsw); + } else { + forcecoul += prefactor; + 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; + } + } + } else forcecoul = 0.0; + + if (rsq < cut_ljsq[itype][jtype] && rsq > cut_in_off_sq) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + if (rsq < cut_in_on_sq) { + rsw = (sqrtf(rsq) - cut_in_off)/cut_in_diff; + forcelj *= rsw*rsw*(3 - 2*rsw); + } + } else forcelj = 0.0; + + fforce = (forcecoul + forcelj) * 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 (newton_pair || j < nlocal) factor = 1.0; + else factor = 0.5; + if (rsq < cut_coulsq) { + if (!ncoultablebits || rsq <= tabinnersq) { + phicoul = prefactor*erfc; + if (factor_coul < 1.0) phicoul -= (1.0-factor_coul)*prefactor; + } else { + table = etable[itable] + fraction*detable[itable]; + phicoul = qtmp*q[j] * table; + if (factor_coul < 1.0) { + table = ptable[itable] + fraction*dptable[itable]; + prefactor = qtmp*q[j] * table; + phicoul -= (1.0-factor_coul)*prefactor; + } + } + eng_coul += factor*phicoul; + } + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]) - + offset[itype][jtype]; + eng_vdwl += factor*factor_lj*philj; + } + } + + if (vflag) { + if (rsq < cut_coulsq) { + if (!ncoultablebits || rsq <= tabinnersq) { + forcecoul = prefactor * (erfc + EWALD_F*grij*expm2); + if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*prefactor; + } else { + table = vtable[itable] + fraction*dvtable[itable]; + forcecoul = qtmp*q[j] * table; + if (factor_coul < 1.0) { + table = ptable[itable] + fraction*dptable[itable]; + prefactor = qtmp*q[j] * table; + phicoul -= (1.0-factor_coul)*prefactor; + } + } + } else forcecoul = 0.0; + + if (rsq <= cut_in_off_sq) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + } else if (rsq <= cut_in_on_sq) + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + + fforce = (forcecoul + factor_lj*forcelj) * r2inv; + + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairLJCutCoulLong::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"); + + cut_lj = memory->create_2d_double_array(n+1,n+1,"pair:cut_lj"); + cut_ljsq = memory->create_2d_double_array(n+1,n+1,"pair:cut_ljsq"); + epsilon = memory->create_2d_double_array(n+1,n+1,"pair:epsilon"); + sigma = memory->create_2d_double_array(n+1,n+1,"pair:sigma"); + lj1 = memory->create_2d_double_array(n+1,n+1,"pair:lj1"); + lj2 = memory->create_2d_double_array(n+1,n+1,"pair:lj2"); + lj3 = memory->create_2d_double_array(n+1,n+1,"pair:lj3"); + lj4 = memory->create_2d_double_array(n+1,n+1,"pair:lj4"); + offset = memory->create_2d_double_array(n+1,n+1,"pair:offset"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairLJCutCoulLong::settings(int narg, char **arg) +{ + if (narg < 1 || narg > 2) error->all("Illegal pair_style command"); + + cut_lj_global = atof(arg[0]); + if (narg == 1) cut_coul = cut_lj_global; + else cut_coul = atof(arg[1]); + + // 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_lj[i][j] = cut_lj_global; + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairLJCutCoulLong::coeff(int narg, char **arg) +{ + if (narg < 4 || narg > 5) 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); + + double epsilon_one = atof(arg[2]); + double sigma_one = atof(arg[3]); + + double cut_lj_one = cut_lj_global; + if (narg == 5) cut_lj_one = atof(arg[4]); + + 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_lj[i][j] = cut_lj_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairLJCutCoulLong::init_one(int i, int j) +{ + 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_lj[i][j] = mix_distance(cut_lj[i][i],cut_lj[j][j]); + } + + double cut = MAX(cut_lj[i][j],cut_coul); + cut_ljsq[i][j] = cut_lj[i][j] * cut_lj[i][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_lj[i][j]; + offset[i][j] = 4.0 * epsilon[i][j] * (pow(ratio,12.0) - pow(ratio,6.0)); + } else offset[i][j] = 0.0; + + cut_ljsq[j][i] = cut_ljsq[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]; + + // compute I,J contribution to long-range tail correction + // count total # of atoms of type I and J via Allreduce + + if (tail_flag) { + int *type = atom->type; + int nlocal = atom->nlocal; + + double count[2],all[2]; + count[0] = count[1] = 0.0; + for (int k = 0; k < nlocal; k++) { + if (type[k] == i) count[0] += 1.0; + if (type[k] == j) count[1] += 1.0; + } + MPI_Allreduce(count,all,2,MPI_DOUBLE,MPI_SUM,world); + + double PI = 4.0*atan(1.0); + double sig2 = sigma[i][j]*sigma[i][j]; + double sig6 = sig2*sig2*sig2; + double rc3 = cut_lj[i][j]*cut_lj[i][j]*cut_lj[i][j]; + double rc6 = rc3*rc3; + double rc9 = rc3*rc6; + etail_ij = 8.0*PI*all[0]*all[1]*epsilon[i][j] * + sig6 * (sig6 - 3.0*rc6) / (9.0*rc9); + ptail_ij = 16.0*PI*all[0]*all[1]*epsilon[i][j] * + sig6 * (2.0*sig6 - 3.0*rc6) / (9.0*rc9); + } + + return cut; +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairLJCutCoulLong::init_style() +{ + int i,j; + + // require an atom style with charge defined + + if (atom->charge_allow == 0) + error->all("Must use charged atom style with this pair style"); + + 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 (MIN(cut_lj[i][j],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"); + else if (strcmp(force->kspace_style,"ewald") == 0) + g_ewald = force->kspace->g_ewald; + else if (strcmp(force->kspace_style,"pppm") == 0) + g_ewald = force->kspace->g_ewald; + else error->all("Pair style is incompatible with KSpace style"); + + // setup force tables + + if (ncoultablebits) init_tables(); +} + +/* ---------------------------------------------------------------------- + setup force tables used in compute routines +------------------------------------------------------------------------- */ + +void PairLJCutCoulLong::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 PairLJCutCoulLong::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + 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_lj[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairLJCutCoulLong::read_restart(FILE *fp) +{ + read_restart_settings(fp); + + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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_lj[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_lj[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJCutCoulLong::write_restart_settings(FILE *fp) +{ + fwrite(&cut_lj_global,sizeof(double),1,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 PairLJCutCoulLong::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&cut_lj_global,sizeof(double),1,fp); + fread(&cut_coul,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + MPI_Bcast(&cut_lj_global,1,MPI_DOUBLE,0,world); + 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 PairLJCutCoulLong::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 PairLJCutCoulLong::single(int i, int j, int itype, int jtype, + double rsq, + double factor_coul, double factor_lj, + int eflag, One &one) +{ + double r2inv,r6inv,r,grij,expm2,t,erfc,prefactor; + double fraction,table,forcecoul,forcelj,phicoul,philj; + int itable; + + r2inv = 1.0/rsq; + if (rsq < cut_coulsq) { + 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; + } + } + } else forcecoul = 0.0; + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + } else forcelj = 0.0; + one.fforce = (forcecoul + factor_lj*forcelj) * r2inv; + + if (eflag) { + if (rsq < cut_coulsq) { + 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; + } else one.eng_coul = 0.0; + if (rsq < cut_ljsq[itype][jtype]) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]) - + offset[itype][jtype]; + one.eng_vdwl = factor_lj*philj; + } else one.eng_vdwl = 0.0; + } +} diff --git a/src/pair_lj_cut_coul_long.h b/src/pair_lj_cut_coul_long.h new file mode 100644 index 0000000000000000000000000000000000000000..bb5827489b66a460f74739653d239e3695299cd1 --- /dev/null +++ b/src/pair_lj_cut_coul_long.h @@ -0,0 +1,59 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_LJ_CUT_COUL_LONG_H +#define PAIR_LJ_CUT_COUL_LONG_H + +#include "pair.h" + +class PairLJCutCoulLong : public Pair { + public: + double cut_coul; + + PairLJCutCoulLong(); + ~PairLJCutCoulLong(); + virtual void compute(int, int); + virtual void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + virtual void init_style(); + void write_restart(FILE *); + void read_restart(FILE *); + virtual void write_restart_settings(FILE *); + virtual void read_restart_settings(FILE *); + virtual void single(int, int, int, int, double, double, double, int, One &); + + void compute_inner(); + void compute_middle(); + void compute_outer(int, int); + + protected: + double cut_lj_global; + double **cut_lj,**cut_ljsq; + double cut_coulsq; + double **epsilon,**sigma; + double **lj1,**lj2,**lj3,**lj4,**offset; + double *cut_respa; + double g_ewald; + + double tabinnersq; + double *rtable,*drtable,*ftable,*dftable,*ctable,*dctable; + double *etable,*detable,*ptable,*dptable,*vtable,*dvtable; + int ncoulshiftbits,ncoulmask; + + void allocate(); + void init_tables(); + void free_tables(); +}; + +#endif diff --git a/src/pair_lj_cut_coul_long_tip4p.cpp b/src/pair_lj_cut_coul_long_tip4p.cpp new file mode 100644 index 0000000000000000000000000000000000000000..151573bed7d5671a44d38b1bd77f91609a52a866 --- /dev/null +++ b/src/pair_lj_cut_coul_long_tip4p.cpp @@ -0,0 +1,528 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Amalie Frischknecht and Ahmed Ismail (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "pair_lj_cut_coul_long_tip4p.h" +#include "angle.h" +#include "atom.h" +#include "bond.h" +#include "comm.h" +#include "domain.h" +#include "force.h" +#include "kspace.h" +#include "update.h" +#include "respa.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#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 + +/* ---------------------------------------------------------------------- */ + +PairLJCutCoulLongTIP4P::PairLJCutCoulLongTIP4P() +{ + single_enable = 0; +} + +/* ---------------------------------------------------------------------- */ + +void PairLJCutCoulLongTIP4P::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype,itable; + double qtmp,xtmp,ytmp,ztmp,delx,dely,delz,fraction,table; + double delx1,dely1,delz1,delx2,dely2,delz2,delx3,dely3,delz3; + double r,r2inv,r6inv,forcecoul,forcelj,cforce,negforce; + double factor_coul,factor_lj; + double grij,expm2,prefactor,t,erfc; + double phicoul,philj; + int iH1,iH2,jH1,jH2; + double xiM[3],xjM[3]; + double *x1,*x2; + double fO[3],fH[3]; + int *neighs; + double **f; + float rsq; + int *int_rsq = (int *) &rsq; + + eng_vdwl = eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = tvirial[i] = 0.0; + + if (vflag == 2) { + f = update->f_pair; + tf = atom->f; + } + else f = atom->f; + double **x = atom->x; + double *q = atom->q; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + double qqrd2e = force->qqrd2e; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + qtmp = q[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + if (itype == typeO) { + find_M(i,iH1,iH2,xiM); + x1 = xiM; + } else x1 = x[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_coul = factor_lj = 1.0; + else { + factor_coul = special_coul[j/nall]; + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + + r2inv = 1.0/rsq; + + if (rsq < cut_ljsq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + forcelj *= factor_lj * r2inv; + + f[i][0] += delx*forcelj; + f[i][1] += dely*forcelj; + f[i][2] += delz*forcelj; + f[j][0] -= delx*forcelj; + f[j][1] -= dely*forcelj; + f[j][2] -= delz*forcelj; + + if (eflag) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]) - + offset[itype][jtype]; + eng_vdwl += factor_lj*philj; + } + } + + // adjust rsq for off-site O charge(s) + + if (itype == typeO || jtype == typeO) { + if (jtype == typeO) { + find_M(j,jH1,jH2,xjM); + x2 = xjM; + } else x2 = x[j]; + delx = x1[0] - x2[0]; + dely = x1[1] - x2[1]; + delz = x1[2] - x2[2]; + rsq = delx*delx + dely*dely + delz*delz; + } + + // test current rsq against cutoff and compute Coulombic force + + if (rsq < cut_coulsq) { + if (!ncoultablebits || rsq <= tabinnersq) { + r = sqrtf(rsq); + r2inv = 1 / 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 { + r2inv = 1 / rsq; + 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; + } + } + + cforce = forcecoul * r2inv; + + // if i,j are not O atoms, force is applied directly + // if i or j are O atoms, force is on fictitious atoms + // spread force to all 3 atoms in water molecule + // formulas due to Feenstra et al, J Comp Chem, 20, 786 (1999) + + if (itype != typeO) { + if (vflag == 0) { + f[i][0] += delx * cforce; + f[i][1] += dely * cforce; + f[i][2] += delz * cforce; + } else { + tf[i][0] += delx * cforce; + tf[i][1] += dely * cforce; + tf[i][2] += delz * cforce; + + tvirial[0] += 0.5 * delx * delx * cforce; + tvirial[1] += 0.5 * dely * dely * cforce; + tvirial[2] += 0.5 * delz * delz * cforce; + tvirial[3] += 0.5 * dely * delx * cforce; + tvirial[4] += 0.5 * delz * delx * cforce; + tvirial[5] += 0.5 * delz * dely * cforce; + } + + } else { + fO[0] = delx*cforce*(1.0-2.0*alpha); + fO[1] = dely*cforce*(1.0-2.0*alpha); + fO[2] = delz*cforce*(1.0-2.0*alpha); + + fH[0] = alpha * (delx*cforce); + fH[1] = alpha * (dely*cforce); + fH[2] = alpha * (delz*cforce); + + if (vflag == 0) { + f[i][0] += fO[0]; + f[i][1] += fO[1]; + f[i][2] += fO[2]; + + f[iH1][0] += fH[0]; + f[iH1][1] += fH[1]; + f[iH1][2] += fH[2]; + + f[iH2][0] += fH[0]; + f[iH2][1] += fH[1]; + f[iH2][2] += fH[2]; + + } else { + tf[i][0] += fO[0]; + tf[i][1] += fO[1]; + tf[i][2] += fO[2]; + + tf[iH1][0] += fH[0]; + tf[iH1][1] += fH[1]; + tf[iH1][2] += fH[2]; + + tf[iH2][0] += fH[0]; + tf[iH2][1] += fH[1]; + tf[iH2][2] += fH[2]; + + delx1 = x[i][0] - x2[0]; + dely1 = x[i][1] - x2[1]; + delz1 = x[i][2] - x2[2]; + domain->minimum_image(&delx1,&dely1,&delz1); + + delx2 = x[iH1][0] - x2[0]; + dely2 = x[iH1][1] - x2[1]; + delz2 = x[iH1][2] - x2[2]; + domain->minimum_image(&delx2,&dely2,&delz2); + + delx3 = x[iH2][0] - x2[0]; + dely3 = x[iH2][1] - x2[1]; + delz3 = x[iH2][2] - x2[2]; + domain->minimum_image(&delx3,&dely3,&delz3); + + tvirial[0] += 0.5 * (delx1 * fO[0] + (delx2 + delx3) * fH[0]); + tvirial[1] += 0.5 * (dely1 * fO[1] + (dely2 + dely3) * fH[1]); + tvirial[2] += 0.5 * (delz1 * fO[2] + (delz2 + delz3) * fH[2]); + tvirial[3] += 0.5 * (dely1 * fO[0] + (dely2 + dely3) * fH[0]); + tvirial[4] += 0.5 * (delz1 * fO[0] + (delz2 + delz3) * fH[0]); + tvirial[5] += 0.5 * (delz1 * fO[1] + (delz2 + delz3) * fH[1]); + } + } + + if (jtype != typeO) { + if (vflag == 0) { + f[j][0] -= delx * cforce; + f[j][1] -= dely * cforce; + f[j][2] -= delz * cforce; + } else { + tf[j][0] -= delx * cforce; + tf[j][1] -= dely * cforce; + tf[j][2] -= delz * cforce; + + tvirial[0] += 0.5 * (delx * delx * cforce); + tvirial[1] += 0.5 * (dely * dely * cforce); + tvirial[2] += 0.5 * (delz * delz * cforce); + tvirial[3] += 0.5 * (dely * delx * cforce); + tvirial[4] += 0.5 * (delz * delx * cforce); + tvirial[5] += 0.5 * (delz * dely * cforce); + } + + } else { + negforce = -cforce; + + fO[0] = delx*negforce*(1.0-2.0*alpha); + fO[1] = dely*negforce*(1.0-2.0*alpha); + fO[2] = delz*negforce*(1.0-2.0*alpha); + + fH[0] = alpha * (delx*negforce); + fH[1] = alpha * (dely*negforce); + fH[2] = alpha * (delz*negforce); + + if (vflag != 2) { + f[j][0] += fO[0]; + f[j][1] += fO[1]; + f[j][2] += fO[2]; + + f[jH1][0] += fH[0]; + f[jH1][1] += fH[1]; + f[jH1][2] += fH[2]; + + f[jH2][0] += fH[0]; + f[jH2][1] += fH[1]; + f[jH2][2] += fH[2]; + + } else { + tf[j][0] += fO[0]; + tf[j][1] += fO[1]; + tf[j][2] += fO[2]; + + tf[jH1][0] += fH[0]; + tf[jH1][1] += fH[1]; + tf[jH1][2] += fH[2]; + + tf[jH2][0] += fH[0]; + tf[jH2][1] += fH[1]; + tf[jH2][2] += fH[2]; + + delx1 = x[j][0] - x1[0]; + dely1 = x[j][1] - x1[1]; + delz1 = x[j][2] - x1[2]; + domain->minimum_image(&delx1,&dely1,&delz1); + + delx2 = x[jH1][0] - x1[0]; + dely2 = x[jH1][1] - x1[1]; + delz2 = x[jH1][2] - x1[2]; + domain->minimum_image(&delx2,&dely2,&delz2); + + delx3 = x[jH2][0] - x1[0]; + dely3 = x[jH2][1] - x1[1]; + delz3 = x[jH2][2] - x1[2]; + domain->minimum_image(&delx3,&dely3,&delz3); + + tvirial[0] += 0.5 * (delx1 * fO[0] + (delx2 + delx3) * fH[0]); + tvirial[1] += 0.5 * (dely1 * fO[1] + (dely2 + dely3) * fH[1]); + tvirial[2] += 0.5 * (delz1 * fO[2] + (delz2 + delz3) * fH[2]); + tvirial[3] += 0.5 * (dely1 * fO[0] + (dely2 + dely3) * fH[0]); + tvirial[4] += 0.5 * (delz1 * fO[0] + (delz2 + delz3) * fH[0]); + tvirial[5] += 0.5 * (delz1 * fO[1] + (delz2 + delz3) * fH[1]); + } + } + + 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; + eng_coul += phicoul; + } + } + } + } + } + if (vflag == 2) { + virial_compute(); + for (int i = 0; i < 6; i++) virial[i] += tvirial[i]; + } +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairLJCutCoulLongTIP4P::settings(int narg, char **arg) +{ + if (narg < 6 || narg > 7) error->all("Illegal pair_style command"); + + typeO = atoi(arg[0]); + typeH = atoi(arg[1]); + typeB = atoi(arg[2]); + typeA = atoi(arg[3]); + qdist = atof(arg[4]); + + cut_lj_global = atof(arg[5]); + if (narg == 6) cut_coul = cut_lj_global; + else cut_coul = atof(arg[6]); + + // 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_lj[i][j] = cut_lj_global; + } +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairLJCutCoulLongTIP4P::init_style() +{ + int i,j; + + if (atom->tag_enable == 0) + error->all("Pair style lj/cut/coul/long/tip4p requires atom IDs"); + if (!force->newton_pair) + error->all("Pair style lj/cut/coul/long/tip4p requires newton pair on"); + if (atom->charge_allow == 0) + error->all("Must use charged atom style with this pair style"); + + 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 (MIN(cut_lj[i][j],cut_coul) < cut_respa[3]) + error->all("Pair cutoff < Respa interior cutoff"); + } + } else cut_respa = NULL; + + // insure use of correct KSpace long-range solver, set g_ewald + + if (force->kspace == NULL) + error->all("Pair style is incompatible with KSpace style"); + if (strcmp(force->kspace_style,"pppm/tip4p") == 0) + g_ewald = force->kspace->g_ewald; + else error->all("Pair style is incompatible with KSpace style"); + + // setup force tables + + if (ncoultablebits) init_tables(); + + // set alpha parameter + + double theta = force->angle->equilibrium_angle(typeA); + double blen = force->bond->equilibrium_distance(typeB); + alpha = qdist / (2.0 * cos(0.5*theta) * blen); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJCutCoulLongTIP4P::write_restart_settings(FILE *fp) +{ + fwrite(&typeO,sizeof(int),1,fp); + fwrite(&typeH,sizeof(int),1,fp); + fwrite(&typeB,sizeof(int),1,fp); + fwrite(&typeA,sizeof(int),1,fp); + fwrite(&qdist,sizeof(double),1,fp); + + fwrite(&cut_lj_global,sizeof(double),1,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 PairLJCutCoulLongTIP4P::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&typeO,sizeof(int),1,fp); + fread(&typeH,sizeof(int),1,fp); + fread(&typeB,sizeof(int),1,fp); + fread(&typeA,sizeof(int),1,fp); + fread(&qdist,sizeof(double),1,fp); + + fread(&cut_lj_global,sizeof(double),1,fp); + fread(&cut_coul,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + + MPI_Bcast(&typeO,1,MPI_INT,0,world); + MPI_Bcast(&typeH,1,MPI_INT,0,world); + MPI_Bcast(&typeB,1,MPI_INT,0,world); + MPI_Bcast(&typeA,1,MPI_INT,0,world); + MPI_Bcast(&qdist,1,MPI_DOUBLE,0,world); + + MPI_Bcast(&cut_lj_global,1,MPI_DOUBLE,0,world); + 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); + +} + +/* ---------------------------------------------------------------------- + find 2 H atoms bonded to O atom i + compute position xM of fictitious charge site for O atom + also return local indices iH1,iH2 of H atoms +------------------------------------------------------------------------- */ + +void PairLJCutCoulLongTIP4P::find_M(int i, int &iH1, int &iH2, double *xM) +{ + // test that O is correctly bonded to 2 succesive H atoms + + iH1 = atom->map(atom->tag[i] + 1); + iH2 = atom->map(atom->tag[i] + 2); + + if (iH1 == -1 || iH2 == -1) error->one("TIP4P hydrogen is missing"); + if (atom->type[iH1] != typeH || atom->type[iH2] != typeH) + error->one("TIP4P hydrogen has incorrect atom type"); + + double **x = atom->x; + + double delx1 = x[iH1][0] - x[i][0]; + double dely1 = x[iH1][1] - x[i][1]; + double delz1 = x[iH1][2] - x[i][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + + double delx2 = x[iH2][0] - x[i][0]; + double dely2 = x[iH2][1] - x[i][1]; + double delz2 = x[iH2][2] - x[i][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + + xM[0] = x[i][0] + alpha * (delx1 + delx2); + xM[1] = x[i][1] + alpha * (dely1 + dely2); + xM[2] = x[i][2] + alpha * (delz1 + delz2); +} diff --git a/src/pair_lj_cut_coul_long_tip4p.h b/src/pair_lj_cut_coul_long_tip4p.h new file mode 100644 index 0000000000000000000000000000000000000000..e7873ebe98c161b8b0aca1ea487b70894ff16ef1 --- /dev/null +++ b/src/pair_lj_cut_coul_long_tip4p.h @@ -0,0 +1,41 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_LJ_CUT_COUL_LONG_TIP4P_H +#define PAIR_LJ_CUT_COUL_LONG_TIP4P_H + +#include "pair_lj_cut_coul_long.h" + +class PairLJCutCoulLongTIP4P : public PairLJCutCoulLong { + friend class PPPM; + + public: + PairLJCutCoulLongTIP4P(); + void compute(int, int); + void settings(int, char **); + void init_style(); + void write_restart_settings(FILE *fp); + void read_restart_settings(FILE *fp); + + private: + int typeH,typeO; // atom types of TIP4P water H and O atoms + int typeA,typeB; // angle and bond types of TIP4P water + double qdist; // distance from O site to negative charge + double alpha; // geometric constraint parameter for TIP4P + double **tf; + double tvirial[6]; + + void find_M(int, int &, int &, double *); +}; + +#endif diff --git a/src/pair_lj_expand.cpp b/src/pair_lj_expand.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cc9dc5a355d353bbb066f26a61a3f58c9238b914 --- /dev/null +++ b/src/pair_lj_expand.cpp @@ -0,0 +1,404 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "pair_lj_expand.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +PairLJExpand::~PairLJExpand() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(cut); + memory->destroy_2d_double_array(epsilon); + memory->destroy_2d_double_array(sigma); + memory->destroy_2d_double_array(shift); + memory->destroy_2d_double_array(lj1); + memory->destroy_2d_double_array(lj2); + memory->destroy_2d_double_array(lj3); + memory->destroy_2d_double_array(lj4); + memory->destroy_2d_double_array(offset); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairLJExpand::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,forcelj,fforce,factor_lj,philj; + double r,rshift,rshiftsq; + int *neighs; + double **f; + + eng_vdwl = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_lj = 1.0; + else { + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r = sqrt(rsq); + rshift = r - shift[itype][jtype]; + rshiftsq = rshift*rshift; + r2inv = 1.0/rshiftsq; + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + fforce = factor_lj*forcelj/rshift/r; + + 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) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]) - + offset[itype][jtype]; + if (newton_pair || j < nlocal) eng_vdwl += factor_lj*philj; + else eng_vdwl += 0.5*factor_lj*philj; + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairLJExpand::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"); + + cut = memory->create_2d_double_array(n+1,n+1,"pair:cut"); + epsilon = memory->create_2d_double_array(n+1,n+1,"pair:epsilon"); + sigma = memory->create_2d_double_array(n+1,n+1,"pair:sigma"); + shift = memory->create_2d_double_array(n+1,n+1,"pair:shift"); + lj1 = memory->create_2d_double_array(n+1,n+1,"pair:lj1"); + lj2 = memory->create_2d_double_array(n+1,n+1,"pair:lj2"); + lj3 = memory->create_2d_double_array(n+1,n+1,"pair:lj3"); + lj4 = memory->create_2d_double_array(n+1,n+1,"pair:lj4"); + offset = memory->create_2d_double_array(n+1,n+1,"pair:offset"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairLJExpand::settings(int narg, char **arg) +{ + if (narg != 1) error->all("Illegal pair_style command"); + + cut_global = atof(arg[0]); + + // 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 PairLJExpand::coeff(int narg, char **arg) +{ + if (narg < 5 || narg > 6) 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); + + double epsilon_one = atof(arg[2]); + double sigma_one = atof(arg[3]); + double shift_one = atof(arg[4]); + + double cut_one = cut_global; + if (narg == 6) cut_one = atof(arg[5]); + + 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; + shift[i][j] = shift_one; + cut[i][j] = cut_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairLJExpand::init_one(int i, int j) +{ + // always mix shift arithmetically + + 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]); + shift[i][j] = 0.5 * (shift[i][i] + shift[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; + + lj1[j][i] = lj1[i][j]; + lj2[j][i] = lj2[i][j]; + lj3[j][i] = lj3[i][j]; + lj4[j][i] = lj4[i][j]; + shift[j][i] = shift[i][j]; + offset[j][i] = offset[i][j]; + + // compute I,J contribution to long-range tail correction + // count total # of atoms of type I and J via Allreduce + + if (tail_flag) { + int *type = atom->type; + int nlocal = atom->nlocal; + + double count[2],all[2]; + count[0] = count[1] = 0.0; + for (int k = 0; k < nlocal; k++) { + if (type[k] == i) count[0] += 1.0; + if (type[k] == j) count[1] += 1.0; + } + MPI_Allreduce(count,all,2,MPI_DOUBLE,MPI_SUM,world); + + double PI = 4.0*atan(1.0); + double sig2 = sigma[i][j]*sigma[i][j]; + double sig6 = sig2*sig2*sig2; + double shiftcut = shift[i][j] - cut[i][j]; + double rc3 = shiftcut*shiftcut*shiftcut; + double rc4 = rc3*shiftcut; + double rc5 = rc4*shiftcut; + double rc6 = rc5*shiftcut; + double rc9 = rc6*rc3; + double rc10 = rc9*shiftcut; + double rc11 = rc10*shiftcut; + double rc12 = rc11*shiftcut; + double shift2 = shift[i][j]*shift[i][j]; + double shift3 = shift2*shift[i][j]; + etail_ij = 8.0*PI*all[0]*all[1]*epsilon[i][j] * + sig6*((-1.0/(9.0*rc9) + shift[i][j]/(5.0*rc10) - + shift2/(11.0*rc11))*sig6 + + 1.0/(3.0*rc3) - shift[i][j]/(2.0*rc4) + shift2/(5.0*rc5)); + ptail_ij = 8.0*PI*all[0]*all[1]*epsilon[i][j] * + sig6* ((-4.0/(3.0*rc9) + 18.0*shift[i][j]/(5.0*rc10) - + 36.0*shift2/(11.0*rc11) + shift3/rc12)*sig6 + + 2.0/rc3 - 9.0*shift[i][j]/(2.0*rc4) + + 18.0*shift2/(5.0*rc5) - shift3/rc6)/3.0; + } + + return cut[i][j] + shift[i][j]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJExpand::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + 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(&shift[i][j],sizeof(double),1,fp); + fwrite(&cut[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairLJExpand::read_restart(FILE *fp) +{ + read_restart_settings(fp); + + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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(&shift[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(&shift[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJExpand::write_restart_settings(FILE *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 PairLJExpand::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&cut_global,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + 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); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJExpand::single(int i, int j, int itype, int jtype, double rsq, + double factor_coul, double factor_lj, int eflag, + One &one) +{ + double r,rshift,rshiftsq,r2inv,r6inv,forcelj,philj; + + r = sqrt(rsq); + rshift = r - shift[itype][jtype]; + rshiftsq = rshift*rshift; + r2inv = 1.0/rshiftsq; + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv - lj2[itype][jtype]); + one.fforce = factor_lj*forcelj/rshift/r; + + if (eflag) { + philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]) - + offset[itype][jtype]; + one.eng_vdwl = factor_lj*philj; + one.eng_coul = 0.0; + } +} diff --git a/src/pair_lj_expand.h b/src/pair_lj_expand.h new file mode 100644 index 0000000000000000000000000000000000000000..c8a8290d190f97dba81667029cf34f775ec689f8 --- /dev/null +++ b/src/pair_lj_expand.h @@ -0,0 +1,42 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_LJ_EXPAND_H +#define PAIR_LJ_EXPAND_H + +#include "pair.h" + +class PairLJExpand : public Pair { + public: + PairLJExpand() {} + ~PairLJExpand(); + void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + void single(int, int, int, int, double, double, double, int, One &); + + private: + double cut_global; + double **cut; + double **epsilon,**sigma,**shift; + double **lj1,**lj2,**lj3,**lj4,**offset; + + void allocate(); +}; + +#endif diff --git a/src/pair_lj_smooth.cpp b/src/pair_lj_smooth.cpp new file mode 100644 index 0000000000000000000000000000000000000000..73c66862fabc2b19686aaefa148514f049334abb --- /dev/null +++ b/src/pair_lj_smooth.cpp @@ -0,0 +1,447 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Craig Maloney (UCSB) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "pair_lj_smooth.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "neighbor.h" +#include "update.h" +#include "memory.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +PairLJSmooth::~PairLJSmooth() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(cut); + memory->destroy_2d_double_array(cut_inner); + memory->destroy_2d_double_array(cut_inner_sq); + memory->destroy_2d_double_array(epsilon); + memory->destroy_2d_double_array(sigma); + memory->destroy_2d_double_array(lj1); + memory->destroy_2d_double_array(lj2); + memory->destroy_2d_double_array(lj3); + memory->destroy_2d_double_array(lj4); + memory->destroy_2d_double_array(ljsw0); + memory->destroy_2d_double_array(ljsw1); + memory->destroy_2d_double_array(ljsw2); + memory->destroy_2d_double_array(ljsw3); + memory->destroy_2d_double_array(ljsw4); + memory->destroy_2d_double_array(offset); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairLJSmooth::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r6inv,r,forcelj,fforce,factor_lj,philj; + double t,tsq,fskin; + int *neighs; + double **f; + + eng_vdwl = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_lj = 1.0; + else { + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r2inv = 1.0/rsq; + if (rsq < cut_inner_sq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv-lj2[itype][jtype]); + } else { + r = sqrt(rsq); + t = r - cut_inner[itype][jtype]; + tsq = t*t; + fskin = ljsw1[itype][jtype] + ljsw2[itype][jtype]*t + + ljsw3[itype][jtype]*tsq + ljsw4[itype][jtype]*tsq*t; + forcelj = fskin*r; + } + + fforce = factor_lj*forcelj*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 (rsq < cut_inner_sq[itype][jtype]) + philj = r6inv * (lj3[itype][jtype]*r6inv - + lj4[itype][jtype]) - offset[itype][jtype]; + else + philj = ljsw0[itype][jtype] - ljsw1[itype][jtype]*t - + ljsw2[itype][jtype]*tsq/2.0 - ljsw3[itype][jtype]*tsq*t/3.0 - + ljsw4[itype][jtype]*tsq*tsq/4.0 - offset[itype][jtype]; + if (newton_pair || j < nlocal) eng_vdwl += factor_lj*philj; + else eng_vdwl += 0.5*factor_lj*philj; + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairLJSmooth::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"); + + cut = memory->create_2d_double_array(n+1,n+1,"pair:cut"); + cut_inner = memory->create_2d_double_array(n+1,n+1,"pair:cut_inner"); + cut_inner_sq = memory->create_2d_double_array(n+1,n+1,"pair:cut_inner_sq"); + epsilon = memory->create_2d_double_array(n+1,n+1,"pair:epsilon"); + sigma = memory->create_2d_double_array(n+1,n+1,"pair:sigma"); + lj1 = memory->create_2d_double_array(n+1,n+1,"pair:lj1"); + lj2 = memory->create_2d_double_array(n+1,n+1,"pair:lj2"); + lj3 = memory->create_2d_double_array(n+1,n+1,"pair:lj3"); + lj4 = memory->create_2d_double_array(n+1,n+1,"pair:lj4"); + ljsw0 = memory->create_2d_double_array(n+1,n+1,"pair:ljsw0"); + ljsw1 = memory->create_2d_double_array(n+1,n+1,"pair:ljsw1"); + ljsw2 = memory->create_2d_double_array(n+1,n+1,"pair:ljsw2"); + ljsw3 = memory->create_2d_double_array(n+1,n+1,"pair:ljsw3"); + ljsw4 = memory->create_2d_double_array(n+1,n+1,"pair:ljsw4"); + offset = memory->create_2d_double_array(n+1,n+1,"pair:offset"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairLJSmooth::settings(int narg, char **arg) +{ + if (narg != 2) error->all("Illegal pair_style command"); + + cut_inner_global = atof(arg[0]); + cut_global = atof(arg[1]); + + if (cut_inner_global <= 0.0) error->all("Illegal pair_style command"); + + // 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_inner[i][j] = cut_inner_global; + cut[i][j] = cut_global; + } + } +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairLJSmooth::coeff(int narg, char **arg) +{ + if (narg != 4 && narg != 6) + 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); + + double epsilon_one = atof(arg[2]); + double sigma_one = atof(arg[3]); + + double cut_inner_one = cut_inner_global; + double cut_one = cut_global; + if (narg == 6) { + cut_inner_one = atof(arg[4]); + cut_one = atof(arg[5]); + } + + if (cut_inner_one <= 0.0) error->all("Incorrect args for pair coefficients"); + + 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_inner[i][j] = cut_inner_one; + cut[i][j] = cut_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairLJSmooth::init_one(int i, int j) +{ + 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_inner[i][j] = mix_distance(cut_inner[i][i],cut_inner[j][j]); + cut[i][j] = mix_distance(cut[i][i],cut[j][j]); + } + + cut_inner_sq[i][j] = cut_inner[i][j]*cut_inner[i][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 (cut_inner[i][j] != cut[i][j]) { + double r6inv = 1.0/pow(cut_inner[i][j],6.0); + double t = cut[i][j] - cut_inner[i][j]; + double tsq = t*t; + double ratio = sigma[i][j]/cut[i][j]; + ljsw0[i][j] = 4.0*epsilon[i][j]*(pow(ratio,12.0) - pow(ratio,6.0)); + ljsw1[i][j] = r6inv*(lj1[i][j]*r6inv-lj2[i][j]) / cut_inner[i][j]; + ljsw2[i][j] = -r6inv * (13.0*lj1[i][j]*r6inv - 7.0*lj2[i][j]) / + cut_inner_sq[i][j]; + ljsw3[i][j] = -(3.0/tsq) * (ljsw1[i][j] + 2.0/3.0*ljsw2[i][j]*t); + ljsw4[i][j] = -1.0/(3.0*tsq) * (ljsw2[i][j] + 2.0*ljsw3[i][j]*t); + if (offset_flag) + offset[i][j] = ljsw0[i][j] - ljsw1[i][j]*t - ljsw2[i][j]*tsq/2.0 - + ljsw3[i][j]*tsq*t/3.0 - ljsw4[i][j]*tsq*tsq/4.0; + else offset[i][j] = 0.0; + } else { + ljsw0[i][j] = 0.0; + ljsw1[i][j] = 0.0; + ljsw2[i][j] = 0.0; + ljsw3[i][j] = 0.0; + ljsw4[i][j] = 0.0; + double ratio = sigma[i][j] / cut[i][j]; + if (offset_flag) + offset[i][j] = 4.0 * epsilon[i][j] * (pow(ratio,12.0) - pow(ratio,6.0)); + else offset[i][j] = 0.0; + } + + cut_inner[j][i] = cut_inner[i][j]; + cut_inner_sq[j][i] = cut_inner_sq[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]; + ljsw0[j][i] = ljsw0[i][j]; + ljsw1[j][i] = ljsw1[i][j]; + ljsw2[j][i] = ljsw2[i][j]; + ljsw3[j][i] = ljsw3[i][j]; + ljsw4[j][i] = ljsw4[i][j]; + offset[j][i] = offset[i][j]; + + return cut[i][j]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJSmooth::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + 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_inner[i][j],sizeof(double),1,fp); + fwrite(&cut[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairLJSmooth::read_restart(FILE *fp) +{ + read_restart_settings(fp); + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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_inner[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_inner[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJSmooth::write_restart_settings(FILE *fp) +{ + fwrite(&cut_inner_global,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 PairLJSmooth::read_restart_settings(FILE *fp) +{ + int me = comm->me; + if (me == 0) { + fread(&cut_inner_global,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(&cut_inner_global,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); +} + +/* ---------------------------------------------------------------------- */ + +void PairLJSmooth::single(int i, int j, int itype, int jtype, double rsq, + double factor_coul, double factor_lj, int eflag, + One &one) +{ + double r2inv,r6inv,forcelj,philj,r,t,tsq,fskin; + + r2inv = 1.0/rsq; + if (rsq < cut_inner_sq[itype][jtype]) { + r6inv = r2inv*r2inv*r2inv; + forcelj = r6inv * (lj1[itype][jtype]*r6inv-lj2[itype][jtype]); + } else { + r = sqrt(rsq); + t = r - cut_inner[itype][jtype]; + tsq = t*t; + fskin = ljsw1[itype][jtype] + ljsw2[itype][jtype]*t + + ljsw3[itype][jtype]*tsq + ljsw4[itype][jtype]*tsq*t; + forcelj = fskin*r; + } + one.fforce = factor_lj*forcelj*r2inv; + + if (eflag) { + if (rsq < cut_inner_sq[itype][jtype]) + philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]) + - offset[itype][jtype]; + else + philj = ljsw0[itype][jtype] - ljsw1[itype][jtype]*t - + ljsw2[itype][jtype]*tsq/2.0 - ljsw3[itype][jtype]*tsq*t/3.0 - + ljsw4[itype][jtype]*tsq*tsq/4.0 - offset[itype][jtype]; + one.eng_vdwl = factor_lj*philj; + one.eng_coul = 0.0; + } +} diff --git a/src/pair_lj_smooth.h b/src/pair_lj_smooth.h new file mode 100644 index 0000000000000000000000000000000000000000..558e82bf7ef31e3fad9054ad4deea2cb7011b5e1 --- /dev/null +++ b/src/pair_lj_smooth.h @@ -0,0 +1,44 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_LJ_SMOOTH_H +#define PAIR_LJ_SMOOTH_H + +#include "pair.h" + +class PairLJSmooth : public Pair { + public: + PairLJSmooth() {} + ~PairLJSmooth(); + void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + void single(int, int, int, int, double, double, double, int, One &); + + private: + double cut_inner_global,cut_global; + double **cut,**cut_inner,**cut_inner_sq; + double **epsilon,**sigma; + double **lj1,**lj2,**lj3,**lj4; + double **ljsw0,**ljsw1,**ljsw2,**ljsw3,**ljsw4; + double **offset; + + void allocate(); +}; + +#endif diff --git a/src/pair_morse.cpp b/src/pair_morse.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0c29807f493ce49af237a13c6c7cf9e781103cc6 --- /dev/null +++ b/src/pair_morse.cpp @@ -0,0 +1,341 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "pair_morse.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +PairMorse::~PairMorse() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(cut); + memory->destroy_2d_double_array(d0); + memory->destroy_2d_double_array(alpha); + memory->destroy_2d_double_array(r0); + memory->destroy_2d_double_array(morse1); + memory->destroy_2d_double_array(offset); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairMorse::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r,dr,dexp,fforce,factor_lj,phi; + int *neighs; + double **f; + + eng_vdwl = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_lj = 1.0; + else { + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r = sqrt(rsq); + dr = r - r0[itype][jtype]; + dexp = exp(-alpha[itype][jtype] * dr); + fforce = factor_lj * morse1[itype][jtype] * (dexp*dexp - dexp) / r; + + 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) { + phi = d0[itype][jtype] * (dexp*dexp - 2.0*dexp) - + offset[itype][jtype]; + if (newton_pair || j < nlocal) eng_vdwl += factor_lj*phi; + else eng_vdwl += 0.5*factor_lj*phi; + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairMorse::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"); + + cut = memory->create_2d_double_array(n+1,n+1,"pair:cut"); + d0 = memory->create_2d_double_array(n+1,n+1,"pair:d0"); + alpha = memory->create_2d_double_array(n+1,n+1,"pair:alpha"); + r0 = memory->create_2d_double_array(n+1,n+1,"pair:r0"); + morse1 = memory->create_2d_double_array(n+1,n+1,"pair:morse1"); + offset = memory->create_2d_double_array(n+1,n+1,"pair:offset"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairMorse::settings(int narg, char **arg) +{ + if (narg != 1) error->all("Illegal pair_style command"); + + cut_global = atof(arg[0]); + + // 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 PairMorse::coeff(int narg, char **arg) +{ + if (narg < 5 || narg > 6) 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); + + double d0_one = atof(arg[2]); + double alpha_one = atof(arg[3]); + double r0_one = atof(arg[4]); + + double cut_one = cut_global; + if (narg == 6) cut_one = atof(arg[5]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + for (int j = MAX(jlo,i); j <= jhi; j++) { + d0[i][j] = d0_one; + alpha[i][j] = alpha_one; + r0[i][j] = r0_one; + cut[i][j] = cut_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairMorse::init_one(int i, int j) +{ + if (setflag[i][j] == 0) error->all("All pair coeffs are not set"); + + morse1[i][j] = 2.0*d0[i][j]*alpha[i][j]; + + if (offset_flag) { + double alpha_dr = -alpha[i][j] * (cut[i][j] - r0[i][j]); + offset[i][j] = d0[i][j] * (exp(2.0*alpha_dr) - 2.0*exp(alpha_dr)); + } else offset[i][j] = 0.0; + + d0[j][i] = d0[i][j]; + alpha[j][i] = alpha[i][j]; + r0[j][i] = r0[i][j]; + morse1[j][i] = morse1[i][j]; + offset[j][i] = offset[i][j]; + + return cut[i][j]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairMorse::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + for (j = i; j <= atom->ntypes; j++) { + fwrite(&setflag[i][j],sizeof(int),1,fp); + if (setflag[i][j]) { + fwrite(&d0[i][j],sizeof(double),1,fp); + fwrite(&alpha[i][j],sizeof(double),1,fp); + fwrite(&r0[i][j],sizeof(double),1,fp); + fwrite(&cut[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairMorse::read_restart(FILE *fp) +{ + read_restart_settings(fp); + + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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(&d0[i][j],sizeof(double),1,fp); + fread(&alpha[i][j],sizeof(double),1,fp); + fread(&r0[i][j],sizeof(double),1,fp); + fread(&cut[i][j],sizeof(double),1,fp); + } + MPI_Bcast(&d0[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&alpha[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&r0[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairMorse::write_restart_settings(FILE *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 PairMorse::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&cut_global,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + 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); +} + +/* ---------------------------------------------------------------------- */ + +void PairMorse::single(int i, int j, int itype, int jtype, double rsq, + double factor_coul, double factor_lj, int eflag, + One &one) +{ + double r,dr,dexp,phi; + + r = sqrt(rsq); + dr = r - r0[itype][jtype]; + dexp = exp(-alpha[itype][jtype] * dr); + one.fforce = factor_lj * morse1[itype][jtype] * (dexp*dexp - dexp) / r; + + if (eflag) { + phi = d0[itype][jtype] * (dexp*dexp - 2.0*dexp) - offset[itype][jtype]; + one.eng_vdwl = factor_lj*phi; + one.eng_coul = 0.0; + } +} diff --git a/src/pair_morse.h b/src/pair_morse.h new file mode 100644 index 0000000000000000000000000000000000000000..c5327d34b50fbf9748d4fa09c24459782152269a --- /dev/null +++ b/src/pair_morse.h @@ -0,0 +1,43 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_MORSE_H +#define PAIR_MORSE_H + +#include "pair.h" + +class PairMorse : public Pair { + public: + PairMorse() {} + ~PairMorse(); + void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + void single(int, int, int, int, double, double, double, int, One &); + + private: + double cut_global; + double **cut; + double **d0,**alpha,**r0; + double **morse1; + double **offset; + + void allocate(); +}; + +#endif diff --git a/src/pair_soft.cpp b/src/pair_soft.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f0b28ab13cc6e2ba9bf6a7b6adcaabd78b5c7229 --- /dev/null +++ b/src/pair_soft.cpp @@ -0,0 +1,355 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "pair_soft.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "update.h" +#include "memory.h" +#include "neighbor.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +PairSoft::PairSoft() +{ + PI = 4.0*atan(1.0); +} + +/* ---------------------------------------------------------------------- */ + +PairSoft::~PairSoft() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(prestart); + memory->destroy_2d_double_array(prestop); + memory->destroy_2d_double_array(prefactor); + memory->destroy_2d_double_array(cut); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairSoft::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double xtmp,ytmp,ztmp,delx,dely,delz; + double r,rsq,arg,fforce,factor_lj,philj; + int *neighs; + double **f; + + // set current prefactor + // for minimization, set to prestop + // for dynamics, ramp from prestart to prestop + // for 0-step dynamics, set to prestart + + double delta = update->ntimestep - update->beginstep; + if (update->whichflag == 1) delta = 1.0; + else if (update->nsteps) delta /= update->endstep - update->beginstep; + else delta = 0.0; + int ntypes = atom->ntypes; + for (i = 1; i <= ntypes; i++) + for (j = 1; j <= ntypes; j++) + prefactor[i][j] = prestart[i][j] + + delta * (prestop[i][j] - prestart[i][j]); + + eng_vdwl = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_lj = 1.0; + else { + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r = sqrt(rsq); + arg = PI*r/cut[itype][jtype]; + if (r == 0.0) fforce = 0.0; + else fforce = factor_lj * prefactor[itype][jtype] * + sin(arg) * PI/cut[itype][jtype]/r; + + 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) { + philj = prefactor[itype][jtype] * (1.0+cos(arg)); + if (newton_pair || j < nlocal) eng_vdwl += factor_lj*philj; + else eng_vdwl += 0.5*factor_lj*philj; + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairSoft::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"); + + prestart = memory->create_2d_double_array(n+1,n+1,"pair:prestart"); + prestop = memory->create_2d_double_array(n+1,n+1,"pair:prestop"); + prefactor = memory->create_2d_double_array(n+1,n+1,"pair:prefactor"); + cut = memory->create_2d_double_array(n+1,n+1,"pair:cut"); + + // init prestart and prestop to 0.0 + // since pair_hybrid can use all types even if pair_soft sub-class + // never sets them + + for (int i = 1; i <= n; i++) + for (int j = i; j <= n; j++) { + prestart[i][j] = 0.0; + prestop[i][j] = 0.0; + } +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairSoft::settings(int narg, char **arg) +{ + if (narg != 1) error->all("Illegal pair_style command"); + + cut_global = atof(arg[0]); + + // 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 PairSoft::coeff(int narg, char **arg) +{ + if (narg < 4 || narg > 5) 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); + + double prestart_one = atof(arg[2]); + double prestop_one = atof(arg[3]); + + double cut_one = cut_global; + if (narg == 5) cut_one = atof(arg[4]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + for (int j = MAX(jlo,i); j <= jhi; j++) { + prestart[i][j] = prestart_one; + prestop[i][j] = prestop_one; + cut[i][j] = cut_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairSoft::init_one(int i, int j) +{ + // always mix prefactors geometrically + + if (setflag[i][j] == 0) { + prestart[i][j] = sqrt(prestart[i][i]*prestart[j][j]); + prestop[i][j] = sqrt(prestop[i][i]*prestop[j][j]); + cut[i][j] = mix_distance(cut[i][i],cut[j][j]); + } + + prestart[j][i] = prestart[i][j]; + prestop[j][i] = prestop[i][j]; + cut[j][i] = cut[i][j]; + + return cut[i][j]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairSoft::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + for (j = i; j <= atom->ntypes; j++) { + fwrite(&setflag[i][j],sizeof(int),1,fp); + if (setflag[i][j]) { + fwrite(&prestart[i][j],sizeof(double),1,fp); + fwrite(&prestop[i][j],sizeof(double),1,fp); + fwrite(&cut[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairSoft::read_restart(FILE *fp) +{ + read_restart_settings(fp); + + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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(&prestart[i][j],sizeof(double),1,fp); + fread(&prestop[i][j],sizeof(double),1,fp); + fread(&cut[i][j],sizeof(double),1,fp); + } + MPI_Bcast(&prestart[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&prestop[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairSoft::write_restart_settings(FILE *fp) +{ + fwrite(&cut_global,sizeof(double),1,fp); + fwrite(&mix_flag,sizeof(int),1,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairSoft::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&cut_global,sizeof(double),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + } + MPI_Bcast(&cut_global,1,MPI_DOUBLE,0,world); + MPI_Bcast(&mix_flag,1,MPI_INT,0,world); +} + +/* ---------------------------------------------------------------------- */ + +void PairSoft::single(int i, int j, int itype, int jtype, double rsq, + double factor_coul, double factor_lj, int eflag, + One &one) +{ + double r,arg,philj; + + r = sqrt(rsq); + arg = PI*r/cut[itype][jtype]; + one.fforce = factor_lj * prefactor[itype][jtype] * + sin(arg) * PI/cut[itype][jtype]/r; + + if (eflag) { + philj = prefactor[itype][jtype] * (1.0+cos(arg)); + one.eng_vdwl = factor_lj*philj; + one.eng_coul = 0.0; + } +} diff --git a/src/pair_soft.h b/src/pair_soft.h new file mode 100644 index 0000000000000000000000000000000000000000..7901f0053eaa720d1e8ee032d07beb672efc639a --- /dev/null +++ b/src/pair_soft.h @@ -0,0 +1,45 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_SOFT_H +#define PAIR_SOFT_H + +#include "pair.h" + +class PairSoft : public Pair { + friend class Pair; + + public: + PairSoft(); + ~PairSoft(); + void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + void single(int, int, int, int, double, double, double, int, One &); + + private: + double PI; + double cut_global; + double **prestart,**prestop; + double **prefactor; + double **cut; + + void allocate(); +}; + +#endif diff --git a/src/pair_table.cpp b/src/pair_table.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3f910cc918b86d3cf418d8d43dcd25d310197065 --- /dev/null +++ b/src/pair_table.cpp @@ -0,0 +1,991 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "mpi.h" +#include "math.h" +#include "stdlib.h" +#include "string.h" +#include "pair_table.h" +#include "atom.h" +#include "force.h" +#include "update.h" +#include "comm.h" +#include "neighbor.h" +#include "memory.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +#define LOOKUP 0 +#define LINEAR 1 +#define SPLINE 2 +#define BITMAP 3 + +#define R 1 +#define RSQ 2 +#define BMP 3 + +#define MAXLINE 1024 + +/* ---------------------------------------------------------------------- */ + +PairTable::PairTable() +{ + ntables = 0; + tables = NULL; +} + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +PairTable::~PairTable() +{ + for (int m = 0; m < ntables; m++) free_table(&tables[m]); + memory->sfree(tables); + + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + memory->destroy_2d_int_array(tabindex); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairTable::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype,itable; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + double fforce,factor_lj,phi,fraction,value,a,b; + int *neighs; + double **f; + Table *tb; + float rsq_single; + int *int_rsq = (int *) &rsq_single; + + eng_vdwl = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + if (j < nall) factor_lj = 1.0; + else { + factor_lj = special_lj[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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + tb = &tables[tabindex[itype][jtype]]; + if (rsq < tb->innersq) + error->one("Pair distance < table inner cutoff"); + + if (tabstyle == LOOKUP) { + itable = static_cast<int> ((rsq - tb->innersq) * tb->invdelta); + if (itable >= nm1) + error->one("Pair distance > table outer cutoff"); + fforce = factor_lj * tb->f[itable]; + } else if (tabstyle == LINEAR) { + itable = static_cast<int> ((rsq - tb->innersq) * tb->invdelta); + if (itable >= nm1) + error->one("Pair distance > table outer cutoff"); + fraction = (rsq - tb->rsq[itable]) * tb->invdelta; + value = tb->f[itable] + fraction*tb->df[itable]; + fforce = factor_lj * value; + } else if (tabstyle == SPLINE) { + itable = static_cast<int> ((rsq - tb->innersq) * tb->invdelta); + if (itable >= nm1) + error->one("Pair distance > table outer cutoff"); + b = (rsq - tb->rsq[itable]) * tb->invdelta; + a = 1.0 - b; + value = a * tb->f[itable] + b * tb->f[itable+1] + + ((a*a*a-a)*tb->f2[itable] + (b*b*b-b)*tb->f2[itable+1]) * + tb->deltasq6; + fforce = factor_lj * value; + } else { + rsq_single = rsq; + itable = *int_rsq & tb->nmask; + itable >>= tb->nshiftbits; + fraction = (rsq_single - tb->rsq[itable]) * tb->drsq[itable]; + value = tb->f[itable] + fraction*tb->df[itable]; + fforce = factor_lj * value; + } + + 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 (tabstyle == LOOKUP) + phi = tb->e[itable]; + else if (tabstyle == LINEAR || tabstyle == BITMAP) + phi = tb->e[itable] + fraction*tb->de[itable]; + else + phi = a * tb->e[itable] + b * tb->e[itable+1] + + ((a*a*a-a)*tb->e2[itable] + (b*b*b-b)*tb->e2[itable+1]) * + tb->deltasq6; + if (newton_pair || j < nlocal) eng_vdwl += factor_lj*phi; + else eng_vdwl += 0.5*factor_lj*phi; + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairTable::allocate() +{ + allocated = 1; + int nt = atom->ntypes; + + setflag = memory->create_2d_int_array(nt+1,nt+1,"pair:setflag"); + for (int i = 1; i <= nt; i++) + for (int j = i; j <= nt; j++) + setflag[i][j] = 0; + + cutsq = memory->create_2d_double_array(nt+1,nt+1,"pair:cutsq"); + tabindex = memory->create_2d_int_array(nt+1,nt+1,"pair:tabindex"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairTable::settings(int narg, char **arg) +{ + if (narg != 2) error->all("Illegal pair_style command"); + + // new settings + + if (strcmp(arg[0],"lookup") == 0) tabstyle = LOOKUP; + else if (strcmp(arg[0],"linear") == 0) tabstyle = LINEAR; + else if (strcmp(arg[0],"spline") == 0) tabstyle = SPLINE; + else if (strcmp(arg[0],"bitmap") == 0) tabstyle = BITMAP; + else error->all("Unknown table style in pair_style command"); + + n = atoi(arg[1]); + nm1 = n - 1; + + // delete old tables, since cannot just change settings + + for (int m = 0; m < ntables; m++) free_table(&tables[m]); + memory->sfree(tables); + + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + memory->destroy_2d_int_array(tabindex); + } + allocated = 0; + + ntables = 0; + tables = NULL; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairTable::coeff(int narg, char **arg) +{ + if (narg != 4 && narg != 5) error->all("Illegal pair_coeff command"); + 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 me; + MPI_Comm_rank(world,&me); + tables = (Table *) + memory->srealloc(tables,(ntables+1)*sizeof(Table),"pair:tables"); + Table *tb = &tables[ntables]; + null_table(tb); + if (me == 0) read_table(tb,arg[2],arg[3]); + bcast_table(tb); + + // set table cutoff + + if (narg == 5) tb->cut = atof(arg[4]); + else if (tb->rflag) tb->cut = tb->rhi; + else tb->cut = tb->rfile[tb->ninput-1]; + + // error check on table parameters + // insure cutoff is within table + // for BITMAP tables, file values can be in non-ascending order + + if (tb->ninput <= 1) error->one("Invalid pair table length"); + double rlo,rhi; + if (tb->rflag == 0) { + rlo = tb->rfile[0]; + rhi = tb->rfile[tb->ninput-1]; + } else { + rlo = tb->rlo; + rhi = tb->rhi; + } + if (tb->cut <= rlo || tb->cut > rhi) error->all("Invalid pair table cutoff"); + if (rlo <= 0.0) error->all("Invalid pair table cutoff"); + + // match = 1 if don't need to spline read-in tables + // this is only the case if r values needed by final tables + // exactly match r values read from file + + tb->match = 0; + if (tabstyle == LINEAR && tb->ninput == n && + tb->rflag == RSQ && tb->rhi == tb->cut) tb->match = 1; + if (tabstyle == SPLINE && tb->ninput == n && + tb->rflag == RSQ && tb->rhi == tb->cut) tb->match = 1; + if (tabstyle == BITMAP && tb->ninput == 1 << n && + tb->rflag == BMP && tb->rhi == tb->cut) tb->match = 1; + + if (tb->rflag == BMP && tb->match == 0) + error->all("Bitmapped table in file does not match requested table"); + + // spline read-in values and compute r,e,f vectors within table + + if (tb->match == 0) spline_table(tb); + compute_table(tb); + + // store ptr to table in tabindex + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + for (int j = MAX(jlo,i); j <= jhi; j++) { + tabindex[i][j] = ntables; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Illegal pair_coeff command"); + ntables++; +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairTable::init_one(int i, int j) +{ + if (setflag[i][j] == 0) error->all("All pair coeffs are not set"); + + tabindex[j][i] = tabindex[i][j]; + + return tables[tabindex[i][j]].cut; +} + +/* ---------------------------------------------------------------------- + read a table section from a tabulated potential file + only called by proc 0 + this function sets these values in Table: + ninput,rfile,efile,ffile,rflag,rlo,rhi,fpflag,fplo,fphi,ntablebits +------------------------------------------------------------------------- */ + +void PairTable::read_table(Table *tb, char *file, char *keyword) +{ + char line[MAXLINE]; + + // open file + + FILE *fp = fopen(file,"r"); + if (fp == NULL) { + char str[128]; + sprintf(str,"Cannot open file %s",file); + error->one(str); + } + + // loop until section found with matching keyword + + while (1) { + if (fgets(line,MAXLINE,fp) == NULL) + error->one("Did not find keyword in table file"); + if (strspn(line," \t\n") == strlen(line)) continue; // blank line + if (line[0] == '#') continue; // comment + if (strstr(line,keyword) == line) break; // matching keyword + fgets(line,MAXLINE,fp); // no match, skip section + param_extract(tb,line); + fgets(line,MAXLINE,fp); + for (int i = 0; i < tb->ninput; i++) fgets(line,MAXLINE,fp); + } + + // read args on 2nd line of section + // allocate table arrays for file values + + fgets(line,MAXLINE,fp); + param_extract(tb,line); + tb->rfile = (double *) + memory->smalloc(tb->ninput*sizeof(double),"pair:rfile"); + tb->efile = (double *) + memory->smalloc(tb->ninput*sizeof(double),"pair:efile"); + tb->ffile = (double *) + memory->smalloc(tb->ninput*sizeof(double),"pair:ffile"); + + // setup bitmap parameters for table to read in + + tb->ntablebits = 0; + int masklo,maskhi,nmask,nshiftbits; + if (tb->rflag == BMP) { + while (1 << tb->ntablebits < tb->ninput) tb->ntablebits++; + if (1 << tb->ntablebits != tb->ninput) + error->one("Bitmapped table is incorrect length in table file"); + init_bitmap(tb->rlo,tb->rhi,tb->ntablebits,masklo,maskhi,nmask,nshiftbits); + } + + // read r,e,f table values from file + // if rflag set, compute r + // if rflag not set, use r from file + + int itmp; + double rtmp; + float rsq; + int *int_rsq = (int *) &rsq; + + fgets(line,MAXLINE,fp); + for (int i = 0; i < tb->ninput; i++) { + fgets(line,MAXLINE,fp); + sscanf(line,"%d %lg %lg %lg",&itmp,&rtmp,&tb->efile[i],&tb->ffile[i]); + + if (tb->rflag == R) + rtmp = tb->rlo + (tb->rhi - tb->rlo)*i/(tb->ninput-1); + else if (tb->rflag == RSQ) { + rtmp = tb->rlo*tb->rlo + + (tb->rhi*tb->rhi - tb->rlo*tb->rlo)*i/(tb->ninput-1); + rtmp = sqrt(rtmp); + } else if (tb->rflag == BMP) { + *int_rsq = i << nshiftbits; + *int_rsq = *int_rsq | masklo; + if (rsq < tb->rlo*tb->rlo) { + *int_rsq = i << nshiftbits; + *int_rsq = *int_rsq | maskhi; + } + rtmp = sqrtf(rsq); + } + + tb->rfile[i] = rtmp; + } + + // close file + + fclose(fp); +} + +/* ---------------------------------------------------------------------- + broadcast read-in table info from proc 0 to other procs + this function communicates these values in Table: + ninput,rfile,efile,ffile,rflag,rlo,rhi,fpflag,fplo,fphi +------------------------------------------------------------------------- */ + +void PairTable::bcast_table(Table *tb) +{ + MPI_Bcast(&tb->ninput,1,MPI_INT,0,world); + + int me; + MPI_Comm_rank(world,&me); + if (me > 0) { + tb->rfile = (double *) + memory->smalloc(tb->ninput*sizeof(double),"pair:rfile"); + tb->efile = (double *) + memory->smalloc(tb->ninput*sizeof(double),"pair:efile"); + tb->ffile = (double *) + memory->smalloc(tb->ninput*sizeof(double),"pair:ffile"); + } + + MPI_Bcast(tb->rfile,tb->ninput,MPI_DOUBLE,0,world); + MPI_Bcast(tb->efile,tb->ninput,MPI_DOUBLE,0,world); + MPI_Bcast(tb->ffile,tb->ninput,MPI_DOUBLE,0,world); + + MPI_Bcast(&tb->rflag,1,MPI_INT,0,world); + if (tb->rflag) { + MPI_Bcast(&tb->rlo,1,MPI_DOUBLE,0,world); + MPI_Bcast(&tb->rhi,1,MPI_DOUBLE,0,world); + } + MPI_Bcast(&tb->fpflag,1,MPI_INT,0,world); + if (tb->fpflag) { + MPI_Bcast(&tb->fplo,1,MPI_DOUBLE,0,world); + MPI_Bcast(&tb->fphi,1,MPI_DOUBLE,0,world); + } +} + +/* ---------------------------------------------------------------------- + build spline representation of e,f over entire range of read-in table + this function sets these values in Table: e2file,f2file +------------------------------------------------------------------------- */ + +void PairTable::spline_table(Table *tb) +{ + tb->e2file = (double *) + memory->smalloc(tb->ninput*sizeof(double),"pair:e2file"); + tb->f2file = (double *) + memory->smalloc(tb->ninput*sizeof(double),"pair:f2file"); + + double ep0 = - tb->ffile[0]; + double epn = - tb->ffile[tb->ninput-1]; + spline(tb->rfile,tb->efile,tb->ninput,ep0,epn,tb->e2file); + + if (tb->fpflag == 0) { + tb->fplo = (tb->ffile[1] - tb->ffile[0]) / (tb->rfile[1] - tb->rfile[0]); + tb->fphi = (tb->ffile[tb->ninput-1] - tb->ffile[tb->ninput-2]) / + (tb->rfile[tb->ninput-1] - tb->rfile[tb->ninput-2]); + } + + double fp0 = tb->fplo; + double fpn = tb->fphi; + spline(tb->rfile,tb->ffile,tb->ninput,fp0,fpn,tb->f2file); +} + +/* ---------------------------------------------------------------------- + extract attributes from parameter line in table section + format of line: N value R/RSQ/BITMAP lo hi FP fplo fphi + N is required, other params are optional +------------------------------------------------------------------------- */ + +void PairTable::param_extract(Table *tb, char *line) +{ + tb->ninput = 0; + tb->rflag = 0; + tb->fpflag = 0; + + char *word = strtok(line," \t\n\r\f"); + while (word) { + if (strcmp(word,"N") == 0) { + word = strtok(NULL," \t\n\r\f"); + tb->ninput = atoi(word); + } else if (strcmp(word,"R") == 0 || strcmp(word,"RSQ") == 0 || + strcmp(word,"BITMAP") == 0) { + if (strcmp(word,"R") == 0) tb->rflag = R; + else if (strcmp(word,"RSQ") == 0) tb->rflag = RSQ; + else if (strcmp(word,"BITMAP") == 0) tb->rflag = BMP; + word = strtok(NULL," \t\n\r\f"); + tb->rlo = atof(word); + word = strtok(NULL," \t\n\r\f"); + tb->rhi = atof(word); + } else if (strcmp(word,"FP") == 0) { + tb->fpflag = 1; + word = strtok(NULL," \t\n\r\f"); + tb->fplo = atof(word); + word = strtok(NULL," \t\n\r\f"); + tb->fphi = atof(word); + } else { + printf("WORD: %s\n",word); + error->one("Invalid keyword in pair table parameters"); + } + word = strtok(NULL," \t\n\r\f"); + } + + if (tb->ninput == 0) error->one("Pair table parameters did not set N"); +} + +/* ---------------------------------------------------------------------- + compute r,e,f vectors from splined values +------------------------------------------------------------------------- */ + +void PairTable::compute_table(Table *tb) +{ + // inner = inner table bound + // cut = outer table bound + // delta = table spacing in rsq for N-1 bins + + double inner; + if (tb->rflag) inner = tb->rlo; + else inner = tb->rfile[0]; + tb->innersq = inner*inner; + tb->delta = (tb->cut*tb->cut - tb->innersq) / nm1; + tb->invdelta = 1.0/tb->delta; + + // direct lookup tables + // N-1 evenly spaced bins in rsq from inner to cut + // e,f = value at midpt of bin + // e,f are N-1 in length since store 1 value at bin midpt + // f is converted to f/r when stored in f[i] + // e,f are never a match to read-in values, always computed via spline interp + + if (tabstyle == LOOKUP) { + tb->e = (double *) memory->smalloc(nm1*sizeof(double),"pair:e"); + tb->f = (double *) memory->smalloc(nm1*sizeof(double),"pair:f"); + + double r,rsq; + for (int i = 0; i < nm1; i++) { + rsq = tb->innersq + (i+0.5)*tb->delta; + r = sqrt(rsq); + tb->e[i] = splint(tb->rfile,tb->efile,tb->e2file,tb->ninput,r); + tb->f[i] = splint(tb->rfile,tb->ffile,tb->f2file,tb->ninput,r)/r; + } + } + + // linear tables + // N-1 evenly spaced bins in rsq from inner to cut + // rsq,e,f = value at lower edge of bin + // de,df values = delta from lower edge to upper edge of bin + // rsq,e,f are N in length so de,df arrays can compute difference + // f is converted to f/r when stored in f[i] + // e,f can match read-in values, else compute via spline interp + + if (tabstyle == LINEAR) { + tb->rsq = (double *) memory->smalloc(n*sizeof(double),"pair:rsq"); + tb->e = (double *) memory->smalloc(n*sizeof(double),"pair:e"); + tb->f = (double *) memory->smalloc(n*sizeof(double),"pair:f"); + tb->de = (double *) memory->smalloc(nm1*sizeof(double),"pair:de"); + tb->df = (double *) memory->smalloc(nm1*sizeof(double),"pair:df"); + + double r,rsq; + for (int i = 0; i < n; i++) { + rsq = tb->innersq + i*tb->delta; + r = sqrt(rsq); + tb->rsq[i] = rsq; + if (tb->match) { + tb->e[i] = tb->efile[i]; + tb->f[i] = tb->ffile[i]/r; + } else { + tb->e[i] = splint(tb->rfile,tb->efile,tb->e2file,tb->ninput,r); + tb->f[i] = splint(tb->rfile,tb->ffile,tb->f2file,tb->ninput,r)/r; + } + } + + for (int i = 0; i < nm1; i++) { + tb->de[i] = tb->e[i+1] - tb->e[i]; + tb->df[i] = tb->f[i+1] - tb->f[i]; + } + } + + // cubic spline tables + // N-1 evenly spaced bins in rsq from inner to cut + // rsq,e,f = value at lower edge of bin + // e2,f2 = spline coefficient for each bin + // rsq,e,f,e2,f2 are N in length so have N-1 spline bins + // f is converted to f/r after e is splined + // e,f can match read-in values, else compute via spline interp + + if (tabstyle == SPLINE) { + tb->rsq = (double *) memory->smalloc(n*sizeof(double),"pair:rsq"); + tb->e = (double *) memory->smalloc(n*sizeof(double),"pair:e"); + tb->f = (double *) memory->smalloc(n*sizeof(double),"pair:f"); + tb->e2 = (double *) memory->smalloc(n*sizeof(double),"pair:e2"); + tb->f2 = (double *) memory->smalloc(n*sizeof(double),"pair:f2"); + + tb->deltasq6 = tb->delta*tb->delta / 6.0; + + double r,rsq; + for (int i = 0; i < n; i++) { + rsq = tb->innersq + i*tb->delta; + r = sqrt(rsq); + tb->rsq[i] = rsq; + if (tb->match) { + tb->e[i] = tb->efile[i]; + tb->f[i] = tb->ffile[i]/r; + } else { + tb->e[i] = splint(tb->rfile,tb->efile,tb->e2file,tb->ninput,r); + tb->f[i] = splint(tb->rfile,tb->ffile,tb->f2file,tb->ninput,r); + } + } + + // ep0,epn = dE/dr at inner and at cut + + double ep0 = - tb->f[0]; + double epn = - tb->f[nm1]; + spline(tb->rsq,tb->e,n,ep0,epn,tb->e2); + + // fp0,fpn = dh/dg at inner and at cut + // h(r) = f(r)/r and g(r) = r^2 + // dh/dg = (1/r df/dr - f/r^2) / 2r + // dh/dg in secant approx = (f(r2)/r2 - f(r1)/r1) / (g(r2) - g(r1)) + + double fp0,fpn; + double secant_factor = 0.1; + if (tb->fpflag) fp0 = (tb->fplo/sqrt(tb->innersq) - tb->f[0]/tb->innersq) / + (2.0 * sqrt(tb->innersq)); + else { + double rsq1 = tb->innersq; + double rsq2 = rsq1 + secant_factor*tb->delta; + fp0 = (splint(tb->rfile,tb->ffile,tb->f2file,tb->ninput,sqrt(rsq2)) / + sqrt(rsq2) - tb->f[0] / sqrt(rsq1)) / (secant_factor*tb->delta); + } + + if (tb->fpflag && tb->cut == tb->rfile[tb->ninput-1]) fpn = + (tb->fphi/tb->cut - tb->f[nm1]/(tb->cut*tb->cut)) / (2.0 * tb->cut); + else { + double rsq2 = tb->cut * tb->cut; + double rsq1 = rsq2 - secant_factor*tb->delta; + fpn = (tb->f[nm1] / sqrt(rsq2) - + splint(tb->rfile,tb->ffile,tb->f2file,tb->ninput,sqrt(rsq1)) / + sqrt(rsq1)) / (secant_factor*tb->delta); + } + + for (int i = 0; i < n; i++) tb->f[i] /= sqrt(tb->rsq[i]); + spline(tb->rsq,tb->f,n,fp0,fpn,tb->f2); + } + + // bitmapped linear tables + // 2^N bins from inner to cut, spaced in bitmapped manner + // f is converted to f/r when stored in f[i] + // e,f can match read-in values, else compute via spline interp + + if (tabstyle == BITMAP) { + double r; + float rsq; + int *int_rsq = (int *) &rsq; + int masklo,maskhi; + + // linear lookup tables of length ntable = 2^n + // stored value = value at lower edge of bin + + init_bitmap(inner,tb->cut,n,masklo,maskhi,tb->nmask,tb->nshiftbits); + int ntable = 1 << n; + int ntablem1 = ntable - 1; + + tb->rsq = (double *) memory->smalloc(ntable*sizeof(double),"pair:rsq"); + tb->e = (double *) memory->smalloc(ntable*sizeof(double),"pair:e"); + tb->f = (double *) memory->smalloc(ntable*sizeof(double),"pair:f"); + tb->de = (double *) memory->smalloc(ntable*sizeof(double),"pair:de"); + tb->df = (double *) memory->smalloc(ntable*sizeof(double),"pair:df"); + tb->drsq = (double *) memory->smalloc(ntable*sizeof(double),"pair:drsq"); + + float minrsq; + int *int_minrsq = (int *) &minrsq; + *int_minrsq = 0 << tb->nshiftbits; + *int_minrsq = *int_minrsq | maskhi; + + for (int i = 0; i < ntable; i++) { + *int_rsq = i << tb->nshiftbits; + *int_rsq = *int_rsq | masklo; + if (rsq < tb->innersq) { + *int_rsq = i << tb->nshiftbits; + *int_rsq = *int_rsq | maskhi; + } + r = sqrtf(rsq); + tb->rsq[i] = rsq; + if (tb->match) { + tb->e[i] = tb->efile[i]; + tb->f[i] = tb->ffile[i]/r; + } else { + tb->e[i] = splint(tb->rfile,tb->efile,tb->e2file,tb->ninput,r); + tb->f[i] = splint(tb->rfile,tb->ffile,tb->f2file,tb->ninput,r)/r; + } + minrsq = MIN(minrsq,rsq); + } + + tb->innersq = minrsq; + + for (int i = 0; i < ntablem1; i++) { + tb->de[i] = tb->e[i+1] - tb->e[i]; + tb->df[i] = tb->f[i+1] - tb->f[i]; + tb->drsq[i] = 1.0/(tb->rsq[i+1] - tb->rsq[i]); + } + + // get the delta values for the last table entries + // tables are connected periodically between 0 and ntablem1 + + tb->de[ntablem1] = tb->e[0] - tb->e[ntablem1]; + tb->df[ntablem1] = tb->f[0] - tb->f[ntablem1]; + tb->drsq[ntablem1] = 1.0/(tb->rsq[0] - tb->rsq[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 + // if tb->match, data at cut*cut is unavailable, so we'll take + // deltas at itablemax-1 as a good approximation + + double e_tmp,f_tmp; + int itablemin = *int_minrsq & tb->nmask; + itablemin >>= tb->nshiftbits; + int itablemax = itablemin - 1; + if (itablemin == 0) itablemax = ntablem1; + int itablemaxm1 = itablemax - 1; + if (itablemax == 0) itablemaxm1 = ntablem1; + *int_rsq = itablemax << tb->nshiftbits; + *int_rsq = *int_rsq | maskhi; + if (rsq < tb->cut*tb->cut) { + if (tb->match) { + tb->de[itablemax] = tb->de[itablemaxm1]; + tb->df[itablemax] = tb->df[itablemaxm1]; + tb->drsq[itablemax] = tb->drsq[itablemaxm1]; + } else { + rsq = tb->cut*tb->cut; + r = sqrtf(rsq); + e_tmp = splint(tb->rfile,tb->efile,tb->e2file,tb->ninput,r); + f_tmp = splint(tb->rfile,tb->ffile,tb->f2file,tb->ninput,r)/r; + tb->de[itablemax] = e_tmp - tb->e[itablemax]; + tb->df[itablemax] = f_tmp - tb->f[itablemax]; + tb->drsq[itablemax] = 1.0/(rsq - tb->rsq[itablemax]); + } + } + } +} + +/* ---------------------------------------------------------------------- + set all ptrs in a table to NULL, so can be freed safely +------------------------------------------------------------------------- */ + +void PairTable::null_table(Table *tb) +{ + tb->rfile = tb->efile = tb->ffile = NULL; + tb->e2file = tb->f2file = NULL; + tb->rsq = tb->drsq = tb->e = tb->de = NULL; + tb->f = tb->df = tb->e2 = tb->f2 = NULL; +} + +/* ---------------------------------------------------------------------- + free all arrays in a table +------------------------------------------------------------------------- */ + +void PairTable::free_table(Table *tb) +{ + memory->sfree(tb->rfile); + memory->sfree(tb->efile); + memory->sfree(tb->ffile); + memory->sfree(tb->e2file); + memory->sfree(tb->f2file); + + memory->sfree(tb->rsq); + memory->sfree(tb->drsq); + memory->sfree(tb->e); + memory->sfree(tb->de); + memory->sfree(tb->f); + memory->sfree(tb->df); + memory->sfree(tb->e2); + memory->sfree(tb->f2); +} + +/* ---------------------------------------------------------------------- + spline and splint routines modified from Numerical Recipes +------------------------------------------------------------------------- */ + +void PairTable::spline(double *x, double *y, int n, + double yp1, double ypn, double *y2) +{ + int i,k; + double p,qn,sig,un; + double *u = new double[n]; + + if (yp1 > 0.99e30) y2[0] = u[0] = 0.0; + else { + y2[0] = -0.5; + u[0] = (3.0/(x[1]-x[0])) * ((y[1]-y[0]) / (x[1]-x[0]) - yp1); + } + for (i = 1; i < n-1; i++) { + sig = (x[i]-x[i-1]) / (x[i+1]-x[i-1]); + p = sig*y2[i-1] + 2.0; + y2[i] = (sig-1.0) / p; + u[i] = (y[i+1]-y[i]) / (x[i+1]-x[i]) - (y[i]-y[i-1]) / (x[i]-x[i-1]); + u[i] = (6.0*u[i] / (x[i+1]-x[i-1]) - sig*u[i-1]) / p; + } + if (ypn > 0.99e30) qn = un = 0.0; + else { + qn = 0.5; + un = (3.0/(x[n-1]-x[n-2])) * (ypn - (y[n-1]-y[n-2]) / (x[n-1]-x[n-2])); + } + y2[n-1] = (un-qn*u[n-2]) / (qn*y2[n-2] + 1.0); + for (k = n-2; k >= 0; k--) y2[k] = y2[k]*y2[k+1] + u[k]; + + delete [] u; +} + +/* ---------------------------------------------------------------------- */ + +double PairTable::splint(double *xa, double *ya, double *y2a, int n, double x) +{ + int klo,khi,k; + double h,b,a,y; + + klo = 0; + khi = n-1; + while (khi-klo > 1) { + k = (khi+klo) >> 1; + if (xa[k] > x) khi = k; + else klo = k; + } + h = xa[khi]-xa[klo]; + a = (xa[khi]-x) / h; + b = (x-xa[klo]) / h; + y = a*ya[klo] + b*ya[khi] + + ((a*a*a-a)*y2a[klo] + (b*b*b-b)*y2a[khi]) * (h*h)/6.0; + return y; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairTable::write_restart(FILE *fp) +{ + write_restart_settings(fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairTable::read_restart(FILE *fp) +{ + read_restart_settings(fp); + allocate(); +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairTable::write_restart_settings(FILE *fp) +{ + fwrite(&tabstyle,sizeof(int),1,fp); + fwrite(&n,sizeof(int),1,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairTable::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&tabstyle,sizeof(int),1,fp); + fread(&n,sizeof(int),1,fp); + } + MPI_Bcast(&tabstyle,1,MPI_DOUBLE,0,world); + MPI_Bcast(&n,1,MPI_INT,0,world); + nm1 = n - 1; +} + +/* ---------------------------------------------------------------------- */ + +void PairTable::single(int i, int j, int itype, int jtype, double rsq, + double factor_coul, double factor_lj, int eflag, + One &one) +{ + int itable; + double fraction,value,a,b,phi; + + Table *tb = &tables[tabindex[itype][jtype]]; + if (rsq < tb->innersq) error->one("Pair distance < table inner cutoff"); + + if (tabstyle == LOOKUP) { + itable = static_cast<int> ((rsq-tb->innersq) * tb->invdelta); + if (itable >= nm1) error->one("Pair distance > table outer cutoff"); + one.fforce = factor_lj * tb->f[itable]; + } else if (tabstyle == LINEAR) { + itable = static_cast<int> ((rsq-tb->innersq) * tb->invdelta); + if (itable >= nm1) error->one("Pair distance > table outer cutoff"); + fraction = (rsq - tb->rsq[itable]) * tb->invdelta; + value = tb->f[itable] + fraction*tb->df[itable]; + one.fforce = factor_lj * value; + } else if (tabstyle == SPLINE) { + itable = static_cast<int> ((rsq-tb->innersq) * tb->invdelta); + if (itable >= nm1) error->one("Pair distance > table outer cutoff"); + b = (rsq - tb->rsq[itable]) * tb->invdelta; + a = 1.0 - b; + value = a * tb->f[itable] + b * tb->f[itable+1] + + ((a*a*a-a)*tb->f2[itable] + (b*b*b-b)*tb->f2[itable+1]) * + tb->deltasq6; + one.fforce = factor_lj * value; + } else { + float rsq_single = rsq; + int *int_rsq = (int *) &rsq_single; + itable = *int_rsq & tb->nmask; + itable >>= tb->nshiftbits; + fraction = (rsq_single - tb->rsq[itable]) * tb->drsq[itable]; + value = tb->f[itable] + fraction*tb->df[itable]; + one.fforce = factor_lj * value; + } + + if (eflag) { + if (tabstyle == LOOKUP) + phi = tb->e[itable]; + else if (tabstyle == LINEAR || tabstyle == BITMAP) + phi = tb->e[itable] + fraction*tb->de[itable]; + else + phi = a * tb->e[itable] + b * tb->e[itable+1] + + ((a*a*a-a)*tb->e2[itable] + (b*b*b-b)*tb->e2[itable+1]) * tb->deltasq6; + one.eng_vdwl = factor_lj*phi; + one.eng_coul = 0.0; + } +} + +/* ---------------------------------------------------------------------- + return the cutoff for tabled potentials + called only by KSpace solvers to determine pairwise Coulombic cutoff + KSpace requires that all pairwise cutoffs be the same + loop over all tables not just those indexed by tabindex[i][j] + no way to know which tables are active since pair::init() not yet called +------------------------------------------------------------------------- */ + +double PairTable::cut_coul() +{ + if (ntables == 0) error->all("All pair coeffs are not set"); + + double cut = tables[0].cut; + for (int m = 1; m < ntables; m++) + if (tables[m].cut != cut) + error->all("Pair table cutoffs must all be equal to use with KSpace"); + return cut; +} diff --git a/src/pair_table.h b/src/pair_table.h new file mode 100644 index 0000000000000000000000000000000000000000..24d6ed51e7a3c6a8b000d8af448975d0411dfb9b --- /dev/null +++ b/src/pair_table.h @@ -0,0 +1,63 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_TABLE_H +#define PAIR_TABLE_H + +#include "pair.h" + +class PairTable : public Pair { + public: + PairTable(); + ~PairTable(); + void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + void single(int, int, int, int, double, double, double, int, One &); + + double cut_coul(); + + private: + int tabstyle,n,nm1; + struct Table { + int ninput,rflag,fpflag,match,ntablebits; + int nshiftbits,nmask; + double rlo,rhi,fplo,fphi,cut; + double *rfile,*efile,*ffile; + double *e2file,*f2file; + double innersq,delta,invdelta,deltasq6; + double *rsq,*drsq,*e,*de,*f,*df,*e2,*f2; + }; + int ntables; + Table *tables; + + int **tabindex; + + void allocate(); + void read_table(Table *, char *, char *); + void param_extract(Table *, char *); + void bcast_table(Table *); + void spline_table(Table *); + void compute_table(Table *); + void null_table(Table *); + void free_table(Table *); + void spline(double *, double *, int, double, double, double *); + double splint(double *, double *, double *, int, double); +}; + +#endif diff --git a/src/pair_yukawa.cpp b/src/pair_yukawa.cpp new file mode 100644 index 0000000000000000000000000000000000000000..55345e9e742f42e14df139cd4757e95266a4ffdc --- /dev/null +++ b/src/pair_yukawa.cpp @@ -0,0 +1,333 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "pair_yukawa.h" +#include "atom.h" +#include "neighbor.h" +#include "force.h" +#include "comm.h" +#include "update.h" +#include "memory.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- + free all arrays +------------------------------------------------------------------------- */ + +PairYukawa::~PairYukawa() +{ + if (allocated) { + memory->destroy_2d_int_array(setflag); + memory->destroy_2d_double_array(cutsq); + + memory->destroy_2d_double_array(cut); + memory->destroy_2d_double_array(a); + memory->destroy_2d_double_array(asq); + memory->destroy_2d_double_array(offset); + } +} + +/* ---------------------------------------------------------------------- */ + +void PairYukawa::compute(int eflag, int vflag) +{ + int i,j,k,numneigh,itype,jtype; + double xtmp,ytmp,ztmp,delx,dely,delz; + double rsq,r2inv,r,rinv,screening,forceyukawa,fforce,factor_coul,phi; + int *neighs; + double **f; + + eng_coul = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + if (vflag == 2) f = update->f_pair; + else f = atom->f; + double **x = atom->x; + int *type = atom->type; + int nlocal = atom->nlocal; + int nall = atom->nlocal + atom->nghost; + double *special_coul = force->special_coul; + int newton_pair = force->newton_pair; + + // loop over neighbors of my atoms + + for (i = 0; i < nlocal; i++) { + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + neighs = neighbor->firstneigh[i]; + numneigh = neighbor->numneigh[i]; + + for (k = 0; k < numneigh; k++) { + j = neighs[k]; + + 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; + jtype = type[j]; + + if (rsq < cutsq[itype][jtype]) { + r2inv = 1.0/rsq; + r = sqrt(rsq); + rinv = 1.0/r; + screening = exp(-kappa*r); + forceyukawa = asq[itype][jtype] * screening * (kappa + rinv); + + fforce = factor_coul*forceyukawa * 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) { + phi = asq[itype][jtype] * screening * rinv - offset[itype][jtype]; + if (newton_pair || j < nlocal) eng_coul += factor_coul*phi; + else eng_coul += 0.5*factor_coul*phi; + } + + if (vflag == 1) { + if (newton_pair || j < nlocal) { + 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; + } else { + virial[0] += 0.5*delx*delx*fforce; + virial[1] += 0.5*dely*dely*fforce; + virial[2] += 0.5*delz*delz*fforce; + virial[3] += 0.5*delx*dely*fforce; + virial[4] += 0.5*delx*delz*fforce; + virial[5] += 0.5*dely*delz*fforce; + } + } + } + } + } + if (vflag == 2) virial_compute(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairYukawa::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"); + + cut = memory->create_2d_double_array(n+1,n+1,"pair:cut"); + a = memory->create_2d_double_array(n+1,n+1,"pair:a"); + asq = memory->create_2d_double_array(n+1,n+1,"pair:asq"); + offset = memory->create_2d_double_array(n+1,n+1,"pair:offset"); +} + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairYukawa::settings(int narg, char **arg) +{ + if (narg != 2) error->all("Illegal pair_style command"); + + kappa = atof(arg[0]); + cut_global = atof(arg[1]); + + // 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 PairYukawa::coeff(int narg, char **arg) +{ + if (narg < 3 || narg > 4) 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); + + double a_one = atof(arg[2]); + + double cut_one = cut_global; + if (narg == 4) cut_one = atof(arg[3]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + for (int j = MAX(jlo,i); j <= jhi; j++) { + a[i][j] = a_one; + cut[i][j] = cut_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all("Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairYukawa::init_one(int i, int j) +{ + if (setflag[i][j] == 0) { + a[i][j] = mix_energy(a[i][i],a[j][j],1.0,1.0); + cut[i][j] = mix_distance(cut[i][i],cut[j][j]); + } + + asq[i][j] = a[i][j]*a[i][j]; + + if (offset_flag) { + double screening = exp(-kappa * cut[i][j]); + offset[i][j] = a[i][j] * screening / cut[i][j]; + } else offset[i][j] = 0.0; + + asq[j][i] = asq[i][j]; + offset[j][i] = offset[i][j]; + + return cut[i][j]; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairYukawa::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + for (j = i; j <= atom->ntypes; j++) { + fwrite(&setflag[i][j],sizeof(int),1,fp); + if (setflag[i][j]) { + fwrite(&a[i][j],sizeof(double),1,fp); + fwrite(&cut[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairYukawa::read_restart(FILE *fp) +{ + read_restart_settings(fp); + + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + 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(&a[i][j],sizeof(double),1,fp); + fread(&cut[i][j],sizeof(double),1,fp); + } + MPI_Bcast(&a[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairYukawa::write_restart_settings(FILE *fp) +{ + fwrite(&kappa,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 PairYukawa::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&kappa,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(&kappa,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); +} + +/* ---------------------------------------------------------------------- */ + +void PairYukawa::single(int i, int j, int itype, int jtype, double rsq, + double factor_coul, double factor_lj, + int eflag, One &one) +{ + double r2inv,r,rinv,screening,forceyukawa,phi; + + r2inv = 1.0/rsq; + r = sqrt(rsq); + rinv = 1.0/r; + screening = exp(-kappa*r); + forceyukawa = asq[itype][jtype] * screening * (kappa + rinv); + one.fforce = factor_coul*forceyukawa * r2inv; + + if (eflag) { + phi = asq[itype][jtype] * screening * rinv; + one.eng_coul = factor_coul*phi; + one.eng_vdwl = 0.0; + } +} diff --git a/src/pair_yukawa.h b/src/pair_yukawa.h new file mode 100644 index 0000000000000000000000000000000000000000..9380f7c8266024aa80fa58987a428d950aeab440 --- /dev/null +++ b/src/pair_yukawa.h @@ -0,0 +1,41 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PAIR_YUKAWA_H +#define PAIR_YUKAWA_H + +#include "pair.h" + +class PairYukawa : public Pair { + public: + PairYukawa() {} + ~PairYukawa(); + void compute(int, int); + void settings(int, char **); + void coeff(int, char **); + double init_one(int, int); + void write_restart(FILE *); + void read_restart(FILE *); + void write_restart_settings(FILE *); + void read_restart_settings(FILE *); + void single(int, int, int, int, double, double, double, int, One &); + + private: + double cut_global; + double kappa; + double **cut,**a,**asq,**offset; + + void allocate(); +}; + +#endif diff --git a/src/pppm.cpp b/src/pppm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8f486a58a6283ba467522b9bbd34a7a24008efc7 --- /dev/null +++ b/src/pppm.cpp @@ -0,0 +1,1867 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Roy Pollock (LLNL), Paul Crozier (SNL) +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "string.h" +#include "stdio.h" +#include "math.h" +#include "pppm.h" +#include "atom.h" +#include "comm.h" +#include "neighbor.h" +#include "force.h" +#include "pair_buck_coul_long.h" +#include "pair_lj_cut_coul_long.h" +#include "pair_lj_charmm_coul_long.h" +#include "pair_lj_class2_coul_long.h" +#include "pair_lj_cut_coul_long_tip4p.h" +#include "pair_table.h" +#include "bond.h" +#include "angle.h" +#include "domain.h" +#include "fft3d_wrap.h" +#include "remap_wrap.h" +#include "memory.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +#define MAXORDER 7 +#define OFFSET 4096 +#define SMALL 0.00001 +#define LARGE 10000.0 +#define EPS_HOC 1.0e-7 + +/* ---------------------------------------------------------------------- */ + +PPPM::PPPM(int narg, char **arg) : KSpace(narg, arg) +{ + if (narg != 1) error->all("Illegal kspace_style pppm command"); + + precision = atof(arg[0]); + PI = 4.0*atan(1.0); + + nfactors = 3; + factors = new int[nfactors]; + factors[0] = 2; + factors[1] = 3; + factors[2] = 5; + + MPI_Comm_rank(world,&me); + MPI_Comm_size(world,&nprocs); + + density_brick = vdx_brick = vdy_brick = vdz_brick = NULL; + density_fft = NULL; + greensfn = NULL; + work1 = work2 = NULL; + vg = NULL; + fkx = fky = fkz = NULL; + buf1 = buf2 = NULL; + + gf_b = NULL; + rho1d = rho_coeff = NULL; + + fft1 = fft2 = NULL; + remap = NULL; + + nmax = 0; + part2grid = NULL; +} + +/* ---------------------------------------------------------------------- + free all memory +------------------------------------------------------------------------- */ + +PPPM::~PPPM() +{ + delete [] factors; + deallocate(); + memory->destroy_2d_int_array(part2grid); +} + +/* ---------------------------------------------------------------------- + called once before run +------------------------------------------------------------------------- */ + +void PPPM::init() +{ + if (me == 0) { + if (screen) fprintf(screen,"PPPM initialization ...\n"); + if (logfile) fprintf(logfile,"PPPM initialization ...\n"); + } + + // error check + + if (force->dimension == 2) error->all("Cannot use PPPM with 2d simulation"); + + if (slabflag == 0 && domain->nonperiodic > 0) + error->all("Cannot use nonperiodic boundaries with PPPM"); + if (slabflag == 1) { + if (domain->xperiodic != 1 || domain->yperiodic != 1 || + domain->boundary[2][0] != 1 || domain->boundary[2][1] != 1) + error->all("Incorrect boundaries with slab PPPM"); + } + + if (order > MAXORDER) { + char str[128]; + sprintf(str,"PPPM order cannot be greater than %d",MAXORDER); + error->all(str); + } + + // free all arrays previously allocated + + deallocate(); + + // insure use of valid pair_style with long-range Coulombics + // set cutoff to short-range Coulombic cutoff + + qqrd2e = force->qqrd2e; + qdist = 0.0; + + Pair *anypair; + if (force->pair == NULL) + error->all("Pair style is incompatible with KSpace style"); + else if (anypair = force->pair_match("buck/coul/long")) + cutoff = ((PairBuckCoulLong *) anypair)->cut_coul; + else if (anypair = force->pair_match("lj/cut/coul/long")) + cutoff = ((PairLJCutCoulLong *) anypair)->cut_coul; + else if (anypair = force->pair_match("lj/charmm/coul/long")) + cutoff = ((PairLJCharmmCoulLong *) anypair)->cut_coul; + else if (anypair = force->pair_match("lj/class2/coul/long")) + cutoff = ((PairLJClass2CoulLong *) anypair)->cut_coul; + else if (anypair = force->pair_match("lj/cut/coul/long/tip4p")) { + if (strcmp(force->kspace_style,"pppm/tip4p") != 0) + error->all("Pair style is incompatible with KSpace style"); + cutoff = ((PairLJCutCoulLongTIP4P *) anypair)->cut_coul; + qdist = ((PairLJCutCoulLongTIP4P *) anypair)->qdist; + typeO = ((PairLJCutCoulLongTIP4P *) anypair)->typeO; + typeH = ((PairLJCutCoulLongTIP4P *) anypair)->typeH; + int typeA = ((PairLJCutCoulLongTIP4P *) anypair)->typeA; + int typeB = ((PairLJCutCoulLongTIP4P *) anypair)->typeB; + if (force->angle == NULL || force->bond == NULL) + error->all("Bond and angle potentials must be defined for TIP4P"); + double theta = force->angle->equilibrium_angle(typeA); + double blen = force->bond->equilibrium_distance(typeB); + alpha = qdist / (2.0 * cos(0.5*theta) * blen); + } + else if (anypair = force->pair_match("table")) + cutoff = ((PairTable *) anypair)->cut_coul(); + else error->all("Pair style is incompatible with KSpace style"); + + // compute qsum & qsqsum and warn if not charge-neutral + + qsum = qsqsum = 0.0; + for (int i = 0; i < atom->nlocal; i++) { + qsum += atom->q[i]; + qsqsum += atom->q[i]*atom->q[i]; + } + + double tmp; + MPI_Allreduce(&qsum,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + qsum = tmp; + MPI_Allreduce(&qsqsum,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + qsqsum = tmp; + + if (fabs(qsum) > SMALL && me == 0) { + char str[128]; + sprintf(str,"System is not charge neutral, net charge = %g",qsum); + error->warning(str); + } + + // setup FFT grid resolution and g_ewald + + set_grid(); + + if (nx_pppm >= OFFSET || ny_pppm >= OFFSET || nz_pppm >= OFFSET) + error->all("PPPM grid is too large"); + + // global indices of PPPM grid range from 0 to N-1 + // nlo_in,nhi_in = lower/upper limits of the 3d sub-brick of + // global PPPM grid that I own without ghost cells + // for slab PPPM, assign z grid as if it were not extended + + nxlo_in = comm->myloc[0]*nx_pppm / comm->procgrid[0]; + nxhi_in = (comm->myloc[0]+1)*nx_pppm / comm->procgrid[0] - 1; + nylo_in = comm->myloc[1]*ny_pppm / comm->procgrid[1]; + nyhi_in = (comm->myloc[1]+1)*ny_pppm / comm->procgrid[1] - 1; + nzlo_in = comm->myloc[2] * + (static_cast<int> (nz_pppm/slab_volfactor)) / comm->procgrid[2]; + nzhi_in = (comm->myloc[2]+1) * + (static_cast<int> (nz_pppm/slab_volfactor)) / comm->procgrid[2] - 1; + + // nlower,nupper = stencil size for mapping particles to PPPM grid + + nlower = -(order-1)/2; + nupper = order/2; + + // shift values for particle <-> grid mapping + // add/subtract OFFSET to avoid int(-0.75) = 0 when want it to be -1 + + if (order % 2) shift = OFFSET + 0.5; + else shift = OFFSET; + if (order % 2) shiftone = 0.0; + else shiftone = 0.5; + + // nlo_out,nhi_out = lower/upper limits of the 3d sub-brick of + // global PPPM grid that my particles can contribute charge to + // effectively nlo_in,nhi_in + ghost cells + // nlo,nhi = global coords of grid pt to "lower left" of smallest/largest + // position a particle in my box can be at + // particle position bound = subbox + skin/2.0 + qdist + // qdist = offset due to TIP4P fictitious charge + // nlo_out,nhi_out = nlo,nhi + stencil size for particle mapping + // for slab PPPM, assign z grid as if it were not extended + + int nlo,nhi; + double cuthalf = 0.5 * neighbor->skin + qdist; + + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + double zprd_slab = zprd*slab_volfactor; + + nlo = static_cast<int> ((domain->subxlo-cuthalf-domain->boxxlo) * + nx_pppm/xprd + shift) - OFFSET; + nhi = static_cast<int> ((domain->subxhi+cuthalf-domain->boxxlo) * + nx_pppm/xprd + shift) - OFFSET; + nxlo_out = nlo + nlower; + nxhi_out = nhi + nupper; + + nlo = static_cast<int> ((domain->subylo-cuthalf-domain->boxylo) * + ny_pppm/yprd + shift) - OFFSET; + nhi = static_cast<int> ((domain->subyhi+cuthalf-domain->boxylo) * + ny_pppm/yprd + shift) - OFFSET; + nylo_out = nlo + nlower; + nyhi_out = nhi + nupper; + + nlo = static_cast<int> ((domain->subzlo-cuthalf-domain->boxzlo) * + nz_pppm/zprd_slab + shift) - OFFSET; + nhi = static_cast<int> ((domain->subzhi+cuthalf-domain->boxzlo) * + nz_pppm/zprd_slab + shift) - OFFSET; + nzlo_out = nlo + nlower; + nzhi_out = nhi + nupper; + + // for slab PPPM, change the grid boundary for processors at +z end + // to include the empty volume between periodically repeating slabs + // for slab PPPM, want charge data communicated from -z proc to +z proc, + // but not vice versa, also want field data communicated from +z proc to + // -z proc, but not vice versa + // this is accomplished by nzhi_in = nzhi_out on +z end (no ghost cells) + + if (slabflag && ((comm->myloc[2]+1) == (comm->procgrid[2]))) { + nzhi_in = nz_pppm - 1; + nzhi_out = nz_pppm - 1; + } + + // nlo_ghost,nhi_ghost = # of planes I will recv from 6 directions + // that overlay domain I own + // proc in that direction tells me via sendrecv() + // if no neighbor proc, value comes from self since I have ghosts regardless + + int nplanes; + MPI_Status status; + + nplanes = nxlo_in - nxlo_out; + if (comm->procneigh[0][0] != me) + MPI_Sendrecv(&nplanes,1,MPI_INT,comm->procneigh[0][0],0, + &nxhi_ghost,1,MPI_INT,comm->procneigh[0][1],0,world,&status); + else nxhi_ghost = nplanes; + + nplanes = nxhi_out - nxhi_in; + if (comm->procneigh[0][1] != me) + MPI_Sendrecv(&nplanes,1,MPI_INT,comm->procneigh[0][1],0, + &nxlo_ghost,1,MPI_INT,comm->procneigh[0][0],0,world,&status); + else nxlo_ghost = nplanes; + + nplanes = nylo_in - nylo_out; + if (comm->procneigh[1][0] != me) + MPI_Sendrecv(&nplanes,1,MPI_INT,comm->procneigh[1][0],0, + &nyhi_ghost,1,MPI_INT,comm->procneigh[1][1],0,world,&status); + else nyhi_ghost = nplanes; + + nplanes = nyhi_out - nyhi_in; + if (comm->procneigh[1][1] != me) + MPI_Sendrecv(&nplanes,1,MPI_INT,comm->procneigh[1][1],0, + &nylo_ghost,1,MPI_INT,comm->procneigh[1][0],0,world,&status); + else nylo_ghost = nplanes; + + nplanes = nzlo_in - nzlo_out; + if (comm->procneigh[2][0] != me) + MPI_Sendrecv(&nplanes,1,MPI_INT,comm->procneigh[2][0],0, + &nzhi_ghost,1,MPI_INT,comm->procneigh[2][1],0,world,&status); + else nzhi_ghost = nplanes; + + nplanes = nzhi_out - nzhi_in; + if (comm->procneigh[2][1] != me) + MPI_Sendrecv(&nplanes,1,MPI_INT,comm->procneigh[2][1],0, + &nzlo_ghost,1,MPI_INT,comm->procneigh[2][0],0,world,&status); + else nzlo_ghost = nplanes; + + // test that ghost overlap is not bigger than my sub-domain + + int flag = 0; + if (nxlo_ghost > nxhi_in-nxlo_in+1) flag = 1; + if (nxhi_ghost > nxhi_in-nxlo_in+1) flag = 1; + if (nylo_ghost > nyhi_in-nylo_in+1) flag = 1; + if (nyhi_ghost > nyhi_in-nylo_in+1) flag = 1; + if (nzlo_ghost > nzhi_in-nzlo_in+1) flag = 1; + if (nzhi_ghost > nzhi_in-nzlo_in+1) flag = 1; + + int flag_all; + MPI_Allreduce(&flag,&flag_all,1,MPI_INT,MPI_SUM,world); + + if (flag_all) + error->all("PPPM stencil extends too far, reduce PPPM order"); + + // decomposition of FFT mesh + // global indices range from 0 to N-1 + // proc owns entire x-dimension, clump of columns in y,z dimensions + // npey_fft,npez_fft = # of procs in y,z dims + // if nprocs is small enough, proc can own 1 or more entire xy planes, + // else proc owns 2d sub-blocks of yz plane + // me_y,me_z = which proc (0-npe_fft-1) I am in y,z dimensions + // nlo_fft,nhi_fft = lower/upper limit of the section + // of the global FFT mesh that I own + + int npey_fft,npez_fft; + if (nz_pppm >= nprocs) { + npey_fft = 1; + npez_fft = nprocs; + } else procs2grid2d(nprocs,ny_pppm,nz_pppm,&npey_fft,&npez_fft); + + int me_y = me % npey_fft; + int me_z = me / npey_fft; + + nxlo_fft = 0; + nxhi_fft = nx_pppm - 1; + nylo_fft = me_y*ny_pppm/npey_fft; + nyhi_fft = (me_y+1)*ny_pppm/npey_fft - 1; + nzlo_fft = me_z*nz_pppm/npez_fft; + nzhi_fft = (me_z+1)*nz_pppm/npez_fft - 1; + + // PPPM grid for this proc, including ghosts + + ngrid = (nxhi_out-nxlo_out+1) * (nyhi_out-nylo_out+1) * + (nzhi_out-nzlo_out+1); + + // FFT arrays on this proc, without ghosts + // nfft = FFT points in FFT decomposition on this proc + // nfft_brick = FFT points in 3d brick-decomposition on this proc + // nfft_both = greater of 2 values + + nfft = (nxhi_fft-nxlo_fft+1) * (nyhi_fft-nylo_fft+1) * + (nzhi_fft-nzlo_fft+1); + int nfft_brick = (nxhi_in-nxlo_in+1) * (nyhi_in-nylo_in+1) * + (nzhi_in-nzlo_in+1); + nfft_both = MAX(nfft,nfft_brick); + + // buffer space for use in brick2fft and fillbrick + // idel = max # of ghost planes to send or recv in +/- dir of each dim + // nx,ny,nz = owned planes (including ghosts) in each dim + // nxx,nyy,nzz = max # of grid cells to send in each dim + // nbuf = max in any dim, augment by 3x for components of vd_xyz in fillbrick + + int idelx,idely,idelz,nx,ny,nz,nxx,nyy,nzz; + + idelx = MAX(nxlo_ghost,nxhi_ghost); + idelx = MAX(idelx,nxhi_out-nxhi_in); + idelx = MAX(idelx,nxlo_in-nxlo_out); + + idely = MAX(nylo_ghost,nyhi_ghost); + idely = MAX(idely,nyhi_out-nyhi_in); + idely = MAX(idely,nylo_in-nylo_out); + + idelz = MAX(nzlo_ghost,nzhi_ghost); + idelz = MAX(idelz,nzhi_out-nzhi_in); + idelz = MAX(idelz,nzlo_in-nzlo_out); + + nx = nxhi_out - nxlo_out + 1; + ny = nyhi_out - nylo_out + 1; + nz = nzhi_out - nzlo_out + 1; + + nxx = idelx * ny * nz; + nyy = idely * nx * nz; + nzz = idelz * nx * ny; + + nbuf = MAX(nxx,nyy); + nbuf = MAX(nbuf,nzz); + nbuf *= 3; + + // print stats + + int ngrid_max,nfft_both_max,nbuf_max; + MPI_Allreduce(&ngrid,&ngrid_max,1,MPI_INT,MPI_MAX,world); + MPI_Allreduce(&nfft_both,&nfft_both_max,1,MPI_INT,MPI_MAX,world); + MPI_Allreduce(&nbuf,&nbuf_max,1,MPI_INT,MPI_MAX,world); + + if (me == 0) { + if (screen) fprintf(screen," brick FFT buffer size/proc = %d %d %d\n", + ngrid_max,nfft_both_max,nbuf_max); + if (logfile) fprintf(logfile," brick FFT buffer size/proc = %d %d %d\n", + ngrid_max,nfft_both_max,nbuf_max); + } + + // allocate K-space dependent memory + + allocate(); + + // pre-compute Green's function denomiator expansion + // pre-compute 1d charge distribution coefficients + + compute_gf_denom(); + compute_rho_coeff(); +} + +/* ---------------------------------------------------------------------- + adjust PPPM coeffs, called initially and whenever volume has changed +------------------------------------------------------------------------- */ + +void PPPM::setup() +{ + int i,j,k,l,m,n; + + // volume-dependent factors + + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + + // adjustment of z dimension for 2d slab PPPM + // 3d PPPM just uses zprd since slab_volfactor = 1.0 + + double zprd_slab = zprd*slab_volfactor; + + volume = xprd * yprd * zprd_slab; + + delxinv = nx_pppm/xprd; + delyinv = ny_pppm/yprd; + delzinv = nz_pppm/zprd_slab; + + delvolinv = delxinv*delyinv*delzinv; + + double unitkx = (2.0*PI/xprd); + double unitky = (2.0*PI/yprd); + double unitkz = (2.0*PI/zprd_slab); + + // fkx,fky,fkz for my FFT grid pts + + double per; + + for (i = nxlo_fft; i <= nxhi_fft; i++) { + per = i - nx_pppm*(2*i/nx_pppm); + fkx[i] = unitkx*per; + } + + for (i = nylo_fft; i <= nyhi_fft; i++) { + per = i - ny_pppm*(2*i/ny_pppm); + fky[i] = unitky*per; + } + + for (i = nzlo_fft; i <= nzhi_fft; i++) { + per = i - nz_pppm*(2*i/nz_pppm); + fkz[i] = unitkz*per; + } + + // virial coefficients + + double sqk,vterm; + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) { + for (j = nylo_fft; j <= nyhi_fft; j++) { + for (i = nxlo_fft; i <= nxhi_fft; i++) { + sqk = fkx[i]*fkx[i] + fky[j]*fky[j] + fkz[k]*fkz[k]; + if (sqk == 0.0) { + vg[n][0] = 0.0; + vg[n][1] = 0.0; + vg[n][2] = 0.0; + vg[n][3] = 0.0; + vg[n][4] = 0.0; + vg[n][5] = 0.0; + } else { + vterm = -2.0 * (1.0/sqk + 0.25/(g_ewald*g_ewald)); + vg[n][0] = 1.0 + vterm*fkx[i]*fkx[i]; + vg[n][1] = 1.0 + vterm*fky[j]*fky[j]; + vg[n][2] = 1.0 + vterm*fkz[k]*fkz[k]; + vg[n][3] = vterm*fkx[i]*fky[j]; + vg[n][4] = vterm*fkx[i]*fkz[k]; + vg[n][5] = vterm*fky[j]*fkz[k]; + } + n++; + } + } + } + + // modified (Hockney-Eastwood) Coulomb Green's function + + int nx,ny,nz,kper,lper,mper; + double snx,sny,snz,snx2,sny2,snz2; + double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; + double sum1,dot1,dot2; + double numerator,denominator; + + int nbx = static_cast<int> ((g_ewald*xprd/(PI*nx_pppm)) * + pow(-log(EPS_HOC),0.25)); + int nby = static_cast<int> ((g_ewald*yprd/(PI*ny_pppm)) * + pow(-log(EPS_HOC),0.25)); + int nbz = static_cast<int> ((g_ewald*zprd_slab/(PI*nz_pppm)) * + pow(-log(EPS_HOC),0.25)); + + double form = 1.0; + + n = 0; + for (m = nzlo_fft; m <= nzhi_fft; m++) { + mper = m - nz_pppm*(2*m/nz_pppm); + snz = sin(0.5*unitkz*mper*zprd_slab/nz_pppm); + snz2 = snz*snz; + + for (l = nylo_fft; l <= nyhi_fft; l++) { + lper = l - ny_pppm*(2*l/ny_pppm); + sny = sin(0.5*unitky*lper*yprd/ny_pppm); + sny2 = sny*sny; + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + kper = k - nx_pppm*(2*k/nx_pppm); + snx = sin(0.5*unitkx*kper*xprd/nx_pppm); + snx2 = snx*snx; + + sqk = pow(unitkx*kper,2.0) + pow(unitky*lper,2.0) + + pow(unitkz*mper,2.0); + + if (sqk != 0.0) { + numerator = form*12.5663706/sqk; + denominator = gf_denom(snx2,sny2,snz2); + sum1 = 0.0; + for (nx = -nbx; nx <= nbx; nx++) { + qx = unitkx*(kper+nx_pppm*nx); + sx = exp(-.25*pow(qx/g_ewald,2.0)); + wx = 1.0; + argx = 0.5*qx*xprd/nx_pppm; + if (argx != 0.0) wx = pow(sin(argx)/argx,order); + for (ny = -nby; ny <= nby; ny++) { + qy = unitky*(lper+ny_pppm*ny); + sy = exp(-.25*pow(qy/g_ewald,2.0)); + wy = 1.0; + argy = 0.5*qy*yprd/ny_pppm; + if (argy != 0.0) wy = pow(sin(argy)/argy,order); + for (nz = -nbz; nz <= nbz; nz++) { + qz = unitkz*(mper+nz_pppm*nz); + sz = exp(-.25*pow(qz/g_ewald,2.0)); + wz = 1.0; + argz = 0.5*qz*zprd_slab/nz_pppm; + if (argz != 0.0) wz = pow(sin(argz)/argz,order); + + dot1 = unitkx*kper*qx + unitky*lper*qy + unitkz*mper*qz; + dot2 = qx*qx+qy*qy+qz*qz; + sum1 += (dot1/dot2) * sx*sy*sz * pow(wx*wy*wz,2.0); + } + } + } + greensfn[n++] = numerator*sum1/denominator; + } else greensfn[n++] = 0.0; + } + } + } +} + +/* ---------------------------------------------------------------------- + compute the PPPM long-range force, energy, virial +------------------------------------------------------------------------- */ + +void PPPM::compute(int eflag, int vflag) +{ + int i; + + // extend size of nlocal-dependent arrays if necessary + + if (atom->nlocal > nmax) { + memory->destroy_2d_int_array(part2grid); + nmax = atom->nmax; + part2grid = memory->create_2d_int_array(nmax,3,"pppm:part2grid"); + } + + energy = 0.0; + if (vflag) for (i = 0; i < 6; i++) virial[i] = 0.0; + + // find grid points for all my particles + // map my particle charge onto my local 3d density grid + + particle_map(); + make_rho(); + + // all procs communicate density values from their ghost cells + // to fully sum contribution in their 3d bricks + // remap from 3d decomposition to FFT decomposition + + brick2fft(); + + // compute potential gradient on my FFT grid and + // portion of e_long on this proc's FFT grid + // return gradients (electric fields) in 3d brick decomposition + + poisson(eflag,vflag); + + // all procs communicate E-field values to fill ghost cells + // surrounding their 3d bricks + + fillbrick(); + + // calculate the force on my particles + + fieldforce(); + + // sum energy across procs and add in volume-dependent term + + if (eflag) { + double energy_all; + MPI_Allreduce(&energy,&energy_all,1,MPI_DOUBLE,MPI_SUM,world); + energy = energy_all; + + energy *= 0.5*volume; + energy -= g_ewald*qsqsum/1.772453851 + + 0.5*PI*qsum*qsum / (g_ewald*g_ewald*volume); + energy *= qqrd2e; + } + + // sum virial across procs + + if (vflag) { + double virial_all[6]; + MPI_Allreduce(virial,virial_all,6,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < 6; i++) virial[i] = 0.5*qqrd2e*volume*virial_all[i]; + } + + // 2d slab correction + + if (slabflag) slabcorr(eflag); +} + +/* ---------------------------------------------------------------------- + allocate memory that depends on # of K-vectors and order +------------------------------------------------------------------------- */ + +void PPPM::allocate() +{ + density_brick = + memory->create_3d_double_array(nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:density_brick"); + vdx_brick = + memory->create_3d_double_array(nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:vdx_brick"); + vdy_brick = + memory->create_3d_double_array(nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:vdy_brick"); + vdz_brick = + memory->create_3d_double_array(nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:vdz_brick"); + + density_fft = new double[nfft_both]; + greensfn = new double[nfft_both]; + work1 = new double[2*nfft_both]; + work2 = new double[2*nfft_both]; + vg = memory->create_2d_double_array(nfft_both,6,"pppm:vg"); + + fkx = memory->create_1d_double_array(nxlo_fft,nxhi_fft,"pppm:fkx"); + fky = memory->create_1d_double_array(nylo_fft,nyhi_fft,"pppm:fky"); + fkz = memory->create_1d_double_array(nzlo_fft,nzhi_fft,"pppm:fkz"); + + buf1 = new double[nbuf]; + buf2 = new double[nbuf]; + + // summation coeffs + + gf_b = new double[order]; + rho1d = memory->create_2d_double_array(3,-order/2,order/2,"pppm:rho1d"); + rho_coeff = memory->create_2d_double_array(order,(1-order)/2,order/2, + "pppm:rho_coeff"); + + // create 2 FFTs and a Remap + // 1st FFT keeps data in FFT decompostion + // 2nd FFT returns data in 3d brick decomposition + // remap takes data from 3d brick to FFT decomposition + + int tmp; + + fft1 = new FFT3d(world,nx_pppm,ny_pppm,nz_pppm, + nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, + nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, + 0,0,&tmp); + + fft2 = new FFT3d(world,nx_pppm,ny_pppm,nz_pppm, + nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, + nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, + 0,0,&tmp); + + remap = new Remap(world, + nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, + nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, + 1,0,0,2); +} + +/* ---------------------------------------------------------------------- + deallocate memory that depends on # of K-vectors and order +------------------------------------------------------------------------- */ + +void PPPM::deallocate() +{ + memory->destroy_3d_double_array(density_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy_3d_double_array(vdx_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy_3d_double_array(vdy_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy_3d_double_array(vdz_brick,nzlo_out,nylo_out,nxlo_out); + + delete [] density_fft; + delete [] greensfn; + delete [] work1; + delete [] work2; + memory->destroy_2d_double_array(vg); + + memory->destroy_1d_double_array(fkx,nxlo_fft); + memory->destroy_1d_double_array(fky,nylo_fft); + memory->destroy_1d_double_array(fkz,nzlo_fft); + + delete [] buf1; + delete [] buf2; + + delete [] gf_b; + memory->destroy_2d_double_array(rho1d,-order/2); + memory->destroy_2d_double_array(rho_coeff,(1-order)/2); + + delete fft1; + delete fft2; + delete remap; +} + +/* ---------------------------------------------------------------------- + set size of FFT grid (nx,ny,nz_pppm) and g_ewald +------------------------------------------------------------------------- */ + +void PPPM::set_grid() +{ + // see JCP 109, pg. 7698 for derivation of coefficients + // higher order coefficients may be computed if needed + + double **acons = memory->create_2d_double_array(8,7,"pppm:acons"); + + acons[1][0] = 2.0 / 3.0; + acons[2][0] = 1.0 / 50.0; + acons[2][1] = 5.0 / 294.0; + acons[3][0] = 1.0 / 588.0; + acons[3][1] = 7.0 / 1440.0; + acons[3][2] = 21.0 / 3872.0; + acons[4][0] = 1.0 / 4320.0; + acons[4][1] = 3.0 / 1936.0; + acons[4][2] = 7601.0 / 2271360.0; + acons[4][3] = 143.0 / 28800.0; + acons[5][0] = 1.0 / 23232.0; + acons[5][1] = 7601.0 / 13628160.0; + acons[5][2] = 143.0 / 69120.0; + acons[5][3] = 517231.0 / 106536960.0; + acons[5][4] = 106640677.0 / 11737571328.0; + acons[6][0] = 691.0 / 68140800.0; + acons[6][1] = 13.0 / 57600.0; + acons[6][2] = 47021.0 / 35512320.0; + acons[6][3] = 9694607.0 / 2095994880.0; + acons[6][4] = 733191589.0 / 59609088000.0; + acons[6][5] = 326190917.0 / 11700633600.0; + acons[7][0] = 1.0 / 345600.0; + acons[7][1] = 3617.0 / 35512320.0; + acons[7][2] = 745739.0 / 838397952.0; + acons[7][3] = 56399353.0 / 12773376000.0; + acons[7][4] = 25091609.0 / 1560084480.0; + acons[7][5] = 1755948832039.0 / 36229939200000.0; + acons[7][6] = 4887769399.0 / 37838389248.0; + + double q2 = qsqsum / force->dielectric; + double natoms = atom->natoms; + + // adjustment of z dimension for 2d slab PPPM + // 3d PPPM just uses zprd since slab_volfactor = 1.0 + + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + double zprd_slab = zprd*slab_volfactor; + + // make initial g_ewald estimate + // based on desired error and real space cutoff + // fluid-occupied volume used to estimate real-space error + // zprd used rather than zprd_slab + + if (!gewaldflag) + g_ewald = sqrt(-log(precision*sqrt(natoms*cutoff*xprd*yprd*zprd) / + (2.0*q2))) / cutoff; + + // set optimal nx_pppm,ny_pppm,nz_pppm based on order and precision + // nz_pppm uses extended zprd_slab instead of zprd + + double h,h1,h2,err,er1,er2,lpr; + int ncount; + + if (!gridflag) { + h = 1.0; + h1 = 2.0; + + ncount = 0; + err = LARGE; + er1 = 0.0; + while (fabs(err) > SMALL) { + lpr = rms(h,xprd,natoms,q2,acons); + err = log(lpr) - log(precision); + er2 = er1; + er1 = err; + h2 = h1; + h1 = h; + if ((er1 - er2) == 0.0) h = h1 + er1; + else h = h1 + er1*(h2 - h1)/(er1 - er2); + ncount++; + if (ncount > LARGE) error->all("Cannot compute PPPM X grid spacing"); + } + nx_pppm = static_cast<int> (xprd/h + 1); + + ncount = 0; + err = LARGE; + er1 = 0.0; + while (fabs(err) > SMALL) { + lpr = rms(h,yprd,natoms,q2,acons); + err = log(lpr) - log(precision); + er2 = er1; + er1 = err; + h2 = h1; + h1 = h; + if ((er1 - er2) == 0.0) h = h1 + er1; + else h = h1 + er1*(h2 - h1)/(er1 - er2); + ncount++; + if (ncount > LARGE) error->all("Cannot compute PPPM Y grid spacing"); + } + ny_pppm = static_cast<int> (yprd/h + 1); + + ncount = 0; + err = LARGE; + er1 = 0.0; + while (fabs(err) > SMALL) { + lpr = rms(h,zprd_slab,natoms,q2,acons); + err = log(lpr) - log(precision); + er2 = er1; + er1 = err; + h2 = h1; + h1 = h; + if ((er1 - er2) == 0.0) h = h1 + er1; + else h = h1 + er1*(h2 - h1)/(er1 - er2); + ncount++; + if (ncount > LARGE) error->all("Cannot compute PPPM Z grid spacing"); + } + nz_pppm = static_cast<int> (zprd_slab/h + 1); + + } + + // convert grid into sizes that are factorable + + while (!factorable(nx_pppm)) nx_pppm++; + while (!factorable(ny_pppm)) ny_pppm++; + while (!factorable(nz_pppm)) nz_pppm++; + + // adjust g_ewald for new grid + + double dx = xprd/nx_pppm; + double dy = yprd/ny_pppm; + double dz = zprd_slab/nz_pppm; + + if (!gewaldflag) { + double gew1,gew2,lprx,lpry,lprz,spr; + + gew1 = g_ewald + 0.01; + ncount = 0; + err = LARGE; + er1 = 0.0; + + while (fabs(err) > SMALL) { + lprx = rms(dx,xprd,natoms,q2,acons); + lpry = rms(dy,yprd,natoms,q2,acons); + lprz = rms(dz,zprd_slab,natoms,q2,acons); + lpr = sqrt(lprx*lprx + lpry*lpry + lprz*lprz) / sqrt(3.0); + spr = 2.0*q2 * exp(-g_ewald*g_ewald*cutoff*cutoff) / + sqrt(natoms*cutoff*xprd*yprd*zprd_slab); + + err = log(lpr) - log(spr); + er2 = er1; + er1 = err; + gew2 = gew1; + gew1 = g_ewald; + if ((er1 - er2) == 0.0) g_ewald = gew1 + er1; + else g_ewald = gew1 + er1*(gew2 - gew1)/(er1 - er2); + ncount++; + if (ncount > LARGE) error->all("Cannot compute PPPM G"); + } + } + + // compute final RMS precision + + double lprx = rms(dx,xprd,natoms,q2,acons); + double lpry = rms(dy,yprd,natoms,q2,acons); + double lprz = rms(dz,zprd_slab,natoms,q2,acons); + lpr = sqrt(lprx*lprx + lpry*lpry + lprz*lprz) / sqrt(3.0); + double spr = 2.0*q2 * exp(-g_ewald*g_ewald*cutoff*cutoff) / + sqrt(natoms*cutoff*xprd*yprd*zprd_slab); + + // free local memory + + memory->destroy_2d_double_array(acons); + + // print info + + if (me == 0) { + if (screen) { + fprintf(screen," G vector = %g\n",g_ewald); + fprintf(screen," grid = %d %d %d\n",nx_pppm,ny_pppm,nz_pppm); + fprintf(screen," RMS precision = %g\n",MAX(lpr,spr)); + } + if (logfile) { + fprintf(logfile," G vector = %g\n",g_ewald); + fprintf(logfile," grid = %d %d %d\n",nx_pppm,ny_pppm,nz_pppm); + fprintf(logfile," RMS precision = %g\n",MAX(lpr,spr)); + } + } +} + +/* ---------------------------------------------------------------------- + check if all factors of n are in list of factors + return 1 if yes, 0 if no +------------------------------------------------------------------------- */ + +int PPPM::factorable(int n) +{ + int i; + + while (n > 1) { + for (i = 0; i < nfactors; i++) { + if (n % factors[i] == 0) { + n /= factors[i]; + break; + } + } + if (i == nfactors) return 0; + } + + return 1; +} + +/* ---------------------------------------------------------------------- + compute RMS precision for a dimension +------------------------------------------------------------------------- */ + +double PPPM::rms(double h, double prd, double natoms, + double q2, double **acons) +{ + double sum = 0.0; + for (int m = 0; m < order; m++) + sum += acons[order][m] * pow(h*g_ewald,2.0*m); + double value = q2 * pow(h*g_ewald,order) * + sqrt(g_ewald*prd*sqrt(2.0*PI)*sum/natoms) / (prd*prd); + return value; +} + +/* ---------------------------------------------------------------------- + denominator for Hockney-Eastwood Green's function + of x,y,z = sin(kx*deltax/2), etc + + inf n-1 + S(n,k) = Sum W(k+pi*j)**2 = Sum b(l)*(z*z)**l + j=-inf l=0 + + = -(z*z)**n /(2n-1)! * (d/dx)**(2n-1) cot(x) at z = sin(x) + gf_b = denominator expansion coeffs +------------------------------------------------------------------------- */ + +double PPPM::gf_denom(double x, double y, double z) +{ + double sx,sy,sz; + sz = sy = sx = 0.0; + for (int l = order-1; l >= 0; l--) { + sx = gf_b[l] + sx*x; + sy = gf_b[l] + sy*y; + sz = gf_b[l] + sz*z; + } + double s = sx*sy*sz; + return s*s; +} + +/* ---------------------------------------------------------------------- + pre-compute Green's function denominator expansion coeffs, Gamma(2n) +------------------------------------------------------------------------- */ + +void PPPM::compute_gf_denom() +{ + int k,l,m; + + for (l = 1; l < order; l++) gf_b[l] = 0.0; + gf_b[0] = 1.0; + + for (m = 1; m < order; m++) { + for (l = m; l > 0; l--) + gf_b[l] = 4.0 * (gf_b[l]*(l-m)*(l-m-0.5)-gf_b[l-1]*(l-m-1)*(l-m-1)); + gf_b[0] = 4.0 * (gf_b[0]*(l-m)*(l-m-0.5)); + } + + int ifact = 1; + for (k = 1; k < 2*order; k++) ifact *= k; + double gaminv = 1.0/ifact; + for (l = 0; l < order; l++) gf_b[l] *= gaminv; +} + +/* ---------------------------------------------------------------------- + ghost-swap to accumulate full density in brick decomposition + remap density from 3d brick decomposition to FFT decomposition +------------------------------------------------------------------------- */ + +void PPPM::brick2fft() +{ + int i,n,ix,iy,iz; + MPI_Request request; + MPI_Status status; + + // pack my ghosts for +x processor + // pass data to self or +x processor + // unpack and sum recv data into my real cells + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy <= nyhi_out; iy++) + for (ix = nxhi_in+1; ix <= nxhi_out; ix++) + buf1[n++] = density_brick[iz][iy][ix]; + + if (comm->procneigh[0][1] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[0][0],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[0][1],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy <= nyhi_out; iy++) + for (ix = nxlo_in; ix < nxlo_in+nxlo_ghost; ix++) + density_brick[iz][iy][ix] += buf2[n++]; + + // pack my ghosts for -x processor + // pass data to self or -x processor + // unpack and sum recv data into my real cells + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy <= nyhi_out; iy++) + for (ix = nxlo_out; ix < nxlo_in; ix++) + buf1[n++] = density_brick[iz][iy][ix]; + + if (comm->procneigh[0][0] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[0][1],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[0][0],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy <= nyhi_out; iy++) + for (ix = nxhi_in-nxhi_ghost+1; ix <= nxhi_in; ix++) + density_brick[iz][iy][ix] += buf2[n++]; + + // pack my ghosts for +y processor + // pass data to self or +y processor + // unpack and sum recv data into my real cells + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nyhi_in+1; iy <= nyhi_out; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + buf1[n++] = density_brick[iz][iy][ix]; + + if (comm->procneigh[1][1] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[1][0],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[1][1],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_in; iy < nylo_in+nylo_ghost; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + density_brick[iz][iy][ix] += buf2[n++]; + + // pack my ghosts for -y processor + // pass data to self or -y processor + // unpack and sum recv data into my real cells + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy < nylo_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + buf1[n++] = density_brick[iz][iy][ix]; + + if (comm->procneigh[1][0] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[1][1],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[1][0],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nyhi_in-nyhi_ghost+1; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + density_brick[iz][iy][ix] += buf2[n++]; + + // pack my ghosts for +z processor + // pass data to self or +z processor + // unpack and sum recv data into my real cells + + n = 0; + for (iz = nzhi_in+1; iz <= nzhi_out; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + buf1[n++] = density_brick[iz][iy][ix]; + + if (comm->procneigh[2][1] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[2][0],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[2][1],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_in; iz < nzlo_in+nzlo_ghost; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + density_brick[iz][iy][ix] += buf2[n++]; + + // pack my ghosts for -z processor + // pass data to self or -z processor + // unpack and sum recv data into my real cells + + n = 0; + for (iz = nzlo_out; iz < nzlo_in; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + buf1[n++] = density_brick[iz][iy][ix]; + + if (comm->procneigh[2][0] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[2][1],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[2][0],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzhi_in-nzhi_ghost+1; iz <= nzhi_in; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + density_brick[iz][iy][ix] += buf2[n++]; + + // remap from 3d brick decomposition to FFT decomposition + // copy grabs inner portion of density from 3d brick + // remap could be done as pre-stage of FFT, + // but this works optimally on only double values, not complex values + + n = 0; + for (iz = nzlo_in; iz <= nzhi_in; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + density_fft[n++] = density_brick[iz][iy][ix]; + + remap->perform(density_fft,density_fft,work1); +} + +/* ---------------------------------------------------------------------- + ghost-swap to fill ghost cells of my brick with field values +------------------------------------------------------------------------- */ + +void PPPM::fillbrick() +{ + int i,n,ix,iy,iz; + MPI_Request request; + MPI_Status status; + + // pack my real cells for +z processor + // pass data to self or +z processor + // unpack and sum recv data into my ghost cells + + n = 0; + for (iz = nzhi_in-nzhi_ghost+1; iz <= nzhi_in; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) { + buf1[n++] = vdx_brick[iz][iy][ix]; + buf1[n++] = vdy_brick[iz][iy][ix]; + buf1[n++] = vdz_brick[iz][iy][ix]; + } + + if (comm->procneigh[2][1] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[2][0],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[2][1],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_out; iz < nzlo_in; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) { + vdx_brick[iz][iy][ix] = buf2[n++]; + vdy_brick[iz][iy][ix] = buf2[n++]; + vdz_brick[iz][iy][ix] = buf2[n++]; + } + + // pack my real cells for -z processor + // pass data to self or -z processor + // unpack and sum recv data into my ghost cells + + n = 0; + for (iz = nzlo_in; iz < nzlo_in+nzlo_ghost; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) { + buf1[n++] = vdx_brick[iz][iy][ix]; + buf1[n++] = vdy_brick[iz][iy][ix]; + buf1[n++] = vdz_brick[iz][iy][ix]; + } + + if (comm->procneigh[2][0] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[2][1],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[2][0],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzhi_in+1; iz <= nzhi_out; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) { + vdx_brick[iz][iy][ix] = buf2[n++]; + vdy_brick[iz][iy][ix] = buf2[n++]; + vdz_brick[iz][iy][ix] = buf2[n++]; + } + + // pack my real cells for +y processor + // pass data to self or +y processor + // unpack and sum recv data into my ghost cells + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nyhi_in-nyhi_ghost+1; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) { + buf1[n++] = vdx_brick[iz][iy][ix]; + buf1[n++] = vdy_brick[iz][iy][ix]; + buf1[n++] = vdz_brick[iz][iy][ix]; + } + + if (comm->procneigh[1][1] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[1][0],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[1][1],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy < nylo_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) { + vdx_brick[iz][iy][ix] = buf2[n++]; + vdy_brick[iz][iy][ix] = buf2[n++]; + vdz_brick[iz][iy][ix] = buf2[n++]; + } + + // pack my real cells for -y processor + // pass data to self or -y processor + // unpack and sum recv data into my ghost cells + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_in; iy < nylo_in+nylo_ghost; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) { + buf1[n++] = vdx_brick[iz][iy][ix]; + buf1[n++] = vdy_brick[iz][iy][ix]; + buf1[n++] = vdz_brick[iz][iy][ix]; + } + + if (comm->procneigh[1][0] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[1][1],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[1][0],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nyhi_in+1; iy <= nyhi_out; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) { + vdx_brick[iz][iy][ix] = buf2[n++]; + vdy_brick[iz][iy][ix] = buf2[n++]; + vdz_brick[iz][iy][ix] = buf2[n++]; + } + + // pack my real cells for +x processor + // pass data to self or +x processor + // unpack and sum recv data into my ghost cells + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy <= nyhi_out; iy++) + for (ix = nxhi_in-nxhi_ghost+1; ix <= nxhi_in; ix++) { + buf1[n++] = vdx_brick[iz][iy][ix]; + buf1[n++] = vdy_brick[iz][iy][ix]; + buf1[n++] = vdz_brick[iz][iy][ix]; + } + + if (comm->procneigh[0][1] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[0][0],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[0][1],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy <= nyhi_out; iy++) + for (ix = nxlo_out; ix < nxlo_in; ix++) { + vdx_brick[iz][iy][ix] = buf2[n++]; + vdy_brick[iz][iy][ix] = buf2[n++]; + vdz_brick[iz][iy][ix] = buf2[n++]; + } + + // pack my real cells for -x processor + // pass data to self or -x processor + // unpack and sum recv data into my ghost cells + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy <= nyhi_out; iy++) + for (ix = nxlo_in; ix < nxlo_in+nxlo_ghost; ix++) { + buf1[n++] = vdx_brick[iz][iy][ix]; + buf1[n++] = vdy_brick[iz][iy][ix]; + buf1[n++] = vdz_brick[iz][iy][ix]; + } + + if (comm->procneigh[0][0] == me) + for (i = 0; i < n; i++) buf2[i] = buf1[i]; + else { + MPI_Irecv(buf2,nbuf,MPI_DOUBLE,comm->procneigh[0][1],0,world,&request); + MPI_Send(buf1,n,MPI_DOUBLE,comm->procneigh[0][0],0,world); + MPI_Wait(&request,&status); + } + + n = 0; + for (iz = nzlo_out; iz <= nzhi_out; iz++) + for (iy = nylo_out; iy <= nyhi_out; iy++) + for (ix = nxhi_in+1; ix <= nxhi_out; ix++) { + vdx_brick[iz][iy][ix] = buf2[n++]; + vdy_brick[iz][iy][ix] = buf2[n++]; + vdz_brick[iz][iy][ix] = buf2[n++]; + } +} + +/* ---------------------------------------------------------------------- + find center grid pt for each of my particles + check that full stencil for the particle will fit in my 3d brick + store central grid pt indices in part2grid array +------------------------------------------------------------------------- */ + +void PPPM::particle_map() +{ + int nx,ny,nz; + + double **x = atom->x; + int nlocal = atom->nlocal; + double boxxlo = domain->boxxlo; + double boxylo = domain->boxylo; + double boxzlo = domain->boxzlo; + + int flag = 0; + for (int i = 0; i < nlocal; i++) { + + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // current particle coord can be outside global and local box + // add/subtract OFFSET to avoid int(-0.75) = 0 when want it to be -1 + + nx = static_cast<int> ((x[i][0]-boxxlo)*delxinv+shift) - OFFSET; + ny = static_cast<int> ((x[i][1]-boxylo)*delyinv+shift) - OFFSET; + nz = static_cast<int> ((x[i][2]-boxzlo)*delzinv+shift) - OFFSET; + + part2grid[i][0] = nx; + part2grid[i][1] = ny; + part2grid[i][2] = nz; + + // check that entire stencil around nx,ny,nz will fit in my 3d brick + + if (nx+nlower < nxlo_out || nx+nupper > nxhi_out || + ny+nlower < nylo_out || ny+nupper > nyhi_out || + nz+nlower < nzlo_out || nz+nupper > nzhi_out) flag++; + } + + int flag_all; + MPI_Allreduce(&flag,&flag_all,1,MPI_INT,MPI_SUM,world); + if (flag_all) error->all("Out of range atoms - cannot compute PPPM"); +} + +/* ---------------------------------------------------------------------- + create discretized "density" on section of global grid due to my particles + density(x,y,z) = charge "density" at grid points of my 3d brick + (nxlo:nxhi,nylo:nyhi,nzlo:nzhi) is extent of my brick (including ghosts) + in global grid +------------------------------------------------------------------------- */ + +void PPPM::make_rho() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz; + double dx,dy,dz,x0,y0,z0; + + // clear 3d density array + + double *vec = &density_brick[nzlo_out][nylo_out][nxlo_out]; + for (i = 0; i < ngrid; i++) vec[i] = 0.0; + + // loop over my charges, add their contribution to nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + + double *q = atom->q; + double **x = atom->x; + int nlocal = atom->nlocal; + double boxxlo = domain->boxxlo; + double boxylo = domain->boxylo; + double boxzlo = domain->boxzlo; + + for (int i = 0; i < nlocal; i++) { + + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxxlo)*delxinv; + dy = ny+shiftone - (x[i][1]-boxylo)*delyinv; + dz = nz+shiftone - (x[i][2]-boxzlo)*delzinv; + + compute_rho1d(dx,dy,dz); + + z0 = delvolinv * q[i]; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + y0 = z0*rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + x0 = y0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + density_brick[mz][my][mx] += x0*rho1d[0][l]; + } + } + } + } +} + +/* ---------------------------------------------------------------------- + FFT-based Poisson solver +------------------------------------------------------------------------- */ + +void PPPM::poisson(int eflag, int vflag) +{ + int i,j,k,n; + double eng; + + // transform charge density (r -> k) + + n = 0; + for (i = 0; i < nfft; i++) { + work1[n++] = density_fft[i]; + work1[n++] = 0.0; + } + + fft1->compute(work1,work1,1); + + // if requested, compute energy and virial contribution + + double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); + double s2 = scaleinv*scaleinv; + + if (eflag || vflag) { + if (vflag) { + n = 0; + for (i = 0; i < nfft; i++) { + eng = s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); + for (j = 0; j < 6; j++) virial[j] += eng*vg[i][j]; + energy += eng; + n += 2; + } + } else { + n = 0; + for (i = 0; i < nfft; i++) { + energy += + s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); + n += 2; + } + } + } + + // scale by 1/total-grid-pts to get rho(k) + // multiply by Green's function to get V(k) + + n = 0; + for (i = 0; i < nfft; i++) { + work1[n++] *= scaleinv * greensfn[i]; + work1[n++] *= scaleinv * greensfn[i]; + } + + // compute gradients of V(r) in each of 3 dims by transformimg -ik*V(k) + // FFT leaves data in 3d brick decomposition + // copy it into inner portion of vdx,vdy,vdz arrays + + // x direction gradient + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) + for (j = nylo_fft; j <= nyhi_fft; j++) + for (i = nxlo_fft; i <= nxhi_fft; i++) { + work2[n] = fkx[i]*work1[n+1]; + work2[n+1] = -fkx[i]*work1[n]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + vdx_brick[k][j][i] = work2[n]; + n += 2; + } + + // y direction gradient + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) + for (j = nylo_fft; j <= nyhi_fft; j++) + for (i = nxlo_fft; i <= nxhi_fft; i++) { + work2[n] = fky[j]*work1[n+1]; + work2[n+1] = -fky[j]*work1[n]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + vdy_brick[k][j][i] = work2[n]; + n += 2; + } + + // z direction gradient + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) + for (j = nylo_fft; j <= nyhi_fft; j++) + for (i = nxlo_fft; i <= nxhi_fft; i++) { + work2[n] = fkz[k]*work1[n+1]; + work2[n+1] = -fkz[k]*work1[n]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + vdz_brick[k][j][i] = work2[n]; + n += 2; + } +} + +/* ---------------------------------------------------------------------- + interpolate from grid to get electric field & force on my particles +------------------------------------------------------------------------- */ + +void PPPM::fieldforce() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz; + double dx,dy,dz,x0,y0,z0; + double ek[3]; + + // loop over my charges, interpolate electric field from nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + // ek = 3 components of E-field on particle + + double *q = atom->q; + double **x = atom->x; + double **f = atom->f; + int nlocal = atom->nlocal; + double boxxlo = domain->boxxlo; + double boxylo = domain->boxylo; + double boxzlo = domain->boxzlo; + + for (i = 0; i < nlocal; i++) { + + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxxlo)*delxinv; + dy = ny+shiftone - (x[i][1]-boxylo)*delyinv; + dz = nz+shiftone - (x[i][2]-boxzlo)*delzinv; + + compute_rho1d(dx,dy,dz); + + ek[0] = ek[1] = ek[2] = 0.0; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + z0 = rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + y0 = z0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + x0 = y0*rho1d[0][l]; + ek[0] -= x0*vdx_brick[mz][my][mx];; + ek[1] -= x0*vdy_brick[mz][my][mx];; + ek[2] -= x0*vdz_brick[mz][my][mx];; + } + } + } + + // convert E-field to force + + f[i][0] += qqrd2e*q[i]*ek[0]; + f[i][1] += qqrd2e*q[i]*ek[1]; + f[i][2] += qqrd2e*q[i]*ek[2]; + } +} + +/* ---------------------------------------------------------------------- + map nprocs to NX by NY grid as PX by PY procs - return optimal px,py +------------------------------------------------------------------------- */ + +void PPPM::procs2grid2d(int nprocs, int nx, int ny, int *px, int *py) +{ + // loop thru all possible factorizations of nprocs + // surf = surface area of largest proc sub-domain + // innermost if test minimizes surface area and surface/volume ratio + + int bestsurf = 2 * (nx + ny); + int bestboxx = 0; + int bestboxy = 0; + + int boxx,boxy,surf,ipx,ipy; + + ipx = 1; + while (ipx <= nprocs) { + if (nprocs % ipx == 0) { + ipy = nprocs/ipx; + boxx = nx/ipx; + if (nx % ipx) boxx++; + boxy = ny/ipy; + if (ny % ipy) boxy++; + surf = boxx + boxy; + if (surf < bestsurf || + (surf == bestsurf && boxx*boxy > bestboxx*bestboxy)) { + bestsurf = surf; + bestboxx = boxx; + bestboxy = boxy; + *px = ipx; + *py = ipy; + } + } + ipx++; + } +} + +/* ---------------------------------------------------------------------- + charge assignment into rho1d + dx,dy,dz = distance of particle from "lower left" grid point +------------------------------------------------------------------------- */ + +void PPPM::compute_rho1d(double dx, double dy, double dz) +{ + int k,l; + + for (k = (1-order)/2; k <= order/2; k++) { + rho1d[0][k] = 0.0; + rho1d[1][k] = 0.0; + rho1d[2][k] = 0.0; + for (l = order-1; l >= 0; l--) { + rho1d[0][k] = rho_coeff[l][k] + rho1d[0][k]*dx; + rho1d[1][k] = rho_coeff[l][k] + rho1d[1][k]*dy; + rho1d[2][k] = rho_coeff[l][k] + rho1d[2][k]*dz; + } + } +} + +/* ---------------------------------------------------------------------- + generate coeffients for the weight function of order n + + (n-1) + Wn(x) = Sum wn(k,x) , Sum is over every other integer + k=-(n-1) + For k=-(n-1),-(n-1)+2, ....., (n-1)-2,n-1 + k is odd integers if n is even and even integers if n is odd + --- + | n-1 + | Sum a(l,j)*(x-k/2)**l if abs(x-k/2) < 1/2 + wn(k,x) = < l=0 + | + | 0 otherwise + --- + a coeffients are packed into the array rho_coeff to eliminate zeros + rho_coeff(l,((k+mod(n+1,2))/2) = a(l,k) +------------------------------------------------------------------------- */ + +void PPPM::compute_rho_coeff() +{ + int j,k,l,m; + double s; + + double **a = memory->create_2d_double_array(order,-order,order,"pppm:a"); + + for (k = -order; k <= order; k++) + for (l = 0; l < order; l++) + a[l][k] = 0.0; + + a[0][0] = 1.0; + for (j = 1; j < order; j++) { + for (k = -j; k <= j; k += 2) { + s = 0.0; + for (l = 0; l < j; l++) { + a[l+1][k] = (a[l][k+1]-a[l][k-1]) / (l+1); + s += pow(0.5,(double) l+1) * + (a[l][k-1] + pow(-1.0,(double) l) * a[l][k+1]) / (l+1); + } + a[0][k] = s; + } + } + + m = (1-order)/2; + for (k = -(order-1); k < order; k += 2) { + for (l = 0; l < order; l++) + rho_coeff[l][m] = a[l][k]; + m++; + } + + memory->destroy_2d_double_array(a,-order); +} + +/* ---------------------------------------------------------------------- + Slab-geometry correction term to dampen inter-slab interactions between + periodically repeating slabs. Yields good approximation to 2D Ewald if + adequate empty space is left between repeating slabs (J. Chem. Phys. + 111, 3155). Slabs defined here to be parallel to the xy plane. +------------------------------------------------------------------------- */ + +void PPPM::slabcorr(int eflag) +{ + // compute local contribution to global dipole moment + + double *q = atom->q; + double **x = atom->x; + int nlocal = atom->nlocal; + + double dipole = 0.0; + for (int i = 0; i < nlocal; i++) dipole += q[i]*x[i][2]; + + // sum local contributions to get global dipole moment + + double dipole_all; + MPI_Allreduce(&dipole,&dipole_all,1,MPI_DOUBLE,MPI_SUM,world); + + // compute corrections + + double e_slabcorr = 2.0*PI*dipole_all*dipole_all/volume; + + if (eflag) energy += qqrd2e*e_slabcorr; + + // add on force corrections + + double ffact = -4.0*PI*dipole_all/volume; + double **f = atom->f; + + for (int i = 0; i < nlocal; i++) f[i][2] += qqrd2e*q[i]*ffact; +} + +/* ---------------------------------------------------------------------- + perform and time the 4 FFTs required for N timesteps +------------------------------------------------------------------------- */ + +void PPPM::timing(int n, double &time3d, double &time1d) +{ + double time1,time2; + + for (int i = 0; i < 2*nfft_both; i++) work1[i] = 0.0; + + MPI_Barrier(world); + time1 = MPI_Wtime(); + + for (int i = 0; i < n; i++) { + fft1->compute(work1,work1,1); + fft2->compute(work1,work1,-1); + fft2->compute(work1,work1,-1); + fft2->compute(work1,work1,-1); + } + + MPI_Barrier(world); + time2 = MPI_Wtime(); + time3d = time2 - time1; + + MPI_Barrier(world); + time1 = MPI_Wtime(); + + for (int i = 0; i < n; i++) { + fft1->timing1d(work1,nfft_both,1); + fft2->timing1d(work1,nfft_both,-1); + fft2->timing1d(work1,nfft_both,-1); + fft2->timing1d(work1,nfft_both,-1); + } + + MPI_Barrier(world); + time2 = MPI_Wtime(); + time1d = time2 - time1; +} + +/* ---------------------------------------------------------------------- + memory usage of local arrays +------------------------------------------------------------------------- */ + +int PPPM::memory_usage() +{ + int bytes = nmax*3 * sizeof(double); + int nbrick = (nxhi_out-nxlo_out+1) * (nyhi_out-nylo_out+1) * + (nzhi_out-nzlo_out+1); + bytes += 4 * nbrick * sizeof(double); + bytes += 6 * nfft_both * sizeof(double); + bytes += nfft_both*6 * sizeof(double); + bytes += 2 * nbuf * sizeof(double); + return bytes; +} diff --git a/src/pppm.h b/src/pppm.h new file mode 100644 index 0000000000000000000000000000000000000000..f75009842684b16e334c5f63d88c2eed7a342c82 --- /dev/null +++ b/src/pppm.h @@ -0,0 +1,94 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PPPM_H +#define PPPM_H + +#include "kspace.h" + +class FFT3d; +class Remap; + +class PPPM : public KSpace { + public: + PPPM(int, char **); + ~PPPM(); + void init(); + void setup(); + void compute(int, int); + void timing(int, double &, double &); + int memory_usage(); + + protected: + int me,nprocs; + double PI; + double precision; + int nfactors; + int *factors; + double qsum,qsqsum; + double qqrd2e; + double cutoff; + double volume; + double delxinv,delyinv,delzinv,delvolinv; + double shift,shiftone; + + int nxlo_in,nylo_in,nzlo_in,nxhi_in,nyhi_in,nzhi_in; + int nxlo_out,nylo_out,nzlo_out,nxhi_out,nyhi_out,nzhi_out; + int nxlo_ghost,nxhi_ghost,nylo_ghost,nyhi_ghost,nzlo_ghost,nzhi_ghost; + int nxlo_fft,nylo_fft,nzlo_fft,nxhi_fft,nyhi_fft,nzhi_fft; + int nlower,nupper; + int ngrid,nfft,nbuf,nfft_both; + + double ***density_brick; + double ***vdx_brick,***vdy_brick,***vdz_brick; + double *greensfn; + double **vg; + double *fkx,*fky,*fkz; + double *density_fft; + double *work1,*work2; + double *buf1,*buf2; + + double *gf_b; + double **rho1d,**rho_coeff; + + FFT3d *fft1,*fft2; + Remap *remap; + + int **part2grid; // storage for particle -> grid mapping + int nmax; + + // TIP4P settings + int typeH,typeO; // atom types of TIP4P water H and O atoms + double qdist; // distance from O site to negative charge + double alpha; // geometric factor + + void set_grid(); + void allocate(); + void deallocate(); + int factorable(int); + double rms(double, double, double, double, double **); + void compute_gf_denom(); + double gf_denom(double, double, double); + virtual void particle_map(); + virtual void make_rho(); + void brick2fft(); + void fillbrick(); + void poisson(int, int); + virtual void fieldforce(); + void procs2grid2d(int,int,int,int *, int*); + void compute_rho1d(double, double, double); + void compute_rho_coeff(); + void slabcorr(int); +}; + +#endif diff --git a/src/pppm_tip4p.cpp b/src/pppm_tip4p.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b677629255a33040cb3ce3b9eb6010f9327a1fd3 --- /dev/null +++ b/src/pppm_tip4p.cpp @@ -0,0 +1,261 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Amalie Frischknecht and Ahmed Ismail (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "pppm_tip4p.h" +#include "atom.h" +#include "domain.h" +#include "memory.h" +#include "error.h" + +#define OFFSET 4096 + +/* ---------------------------------------------------------------------- */ + +PPPMTIP4P::PPPMTIP4P(int narg, char **arg) : PPPM(narg, arg) {} + +/* ---------------------------------------------------------------------- + find center grid pt for each of my particles + check that full stencil for the particle will fit in my 3d brick + store central grid pt indices in part2grid array +------------------------------------------------------------------------- */ + +void PPPMTIP4P::particle_map() +{ + int nx,ny,nz,iH1,iH2; + double *xi,xM[3]; + + int *type = atom->type; + double **x = atom->x; + int nlocal = atom->nlocal; + double boxxlo = domain->boxxlo; + double boxylo = domain->boxylo; + double boxzlo = domain->boxzlo; + + int flag = 0; + for (int i = 0; i < nlocal; i++) { + if (type[i] == typeO) { + find_M(i,iH1,iH2,xM); + xi = xM; + } else xi = x[i]; + + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // current particle coord can be outside global and local box + // add/subtract OFFSET to avoid int(-0.75) = 0 when want it to be -1 + + nx = static_cast<int> ((xi[0]-boxxlo)*delxinv+shift) - OFFSET; + ny = static_cast<int> ((xi[1]-boxylo)*delyinv+shift) - OFFSET; + nz = static_cast<int> ((xi[2]-boxzlo)*delzinv+shift) - OFFSET; + + part2grid[i][0] = nx; + part2grid[i][1] = ny; + part2grid[i][2] = nz; + + // check that entire stencil around nx,ny,nz will fit in my 3d brick + + if (nx+nlower < nxlo_out || nx+nupper > nxhi_out || + ny+nlower < nylo_out || ny+nupper > nyhi_out || + nz+nlower < nzlo_out || nz+nupper > nzhi_out) flag++; + } + + int flag_all; + MPI_Allreduce(&flag,&flag_all,1,MPI_INT,MPI_SUM,world); + if (flag_all) error->all("Out of range atoms - cannot compute PPPM"); +} + +/* ---------------------------------------------------------------------- + create discretized "density" on section of global grid due to my particles + density(x,y,z) = charge "density" at grid points of my 3d brick + (nxlo:nxhi,nylo:nyhi,nzlo:nzhi) is extent of my brick (including ghosts) + in global grid +------------------------------------------------------------------------- */ + +void PPPMTIP4P::make_rho() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz,iH1,iH2; + double dx,dy,dz,x0,y0,z0; + double *xi,xM[3]; + + // clear 3d density array + + double *vec = &density_brick[nzlo_out][nylo_out][nxlo_out]; + for (i = 0; i < ngrid; i++) vec[i] = 0.0; + + // loop over my charges, add their contribution to nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + + int *type = atom->type; + double *q = atom->q; + double **x = atom->x; + int nlocal = atom->nlocal; + double boxxlo = domain->boxxlo; + double boxylo = domain->boxylo; + double boxzlo = domain->boxzlo; + + for (int i = 0; i < nlocal; i++) { + if (type[i] == typeO) { + find_M(i,iH1,iH2,xM); + xi = xM; + } else xi = x[i]; + + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (xi[0]-boxxlo)*delxinv; + dy = ny+shiftone - (xi[1]-boxylo)*delyinv; + dz = nz+shiftone - (xi[2]-boxzlo)*delzinv; + + compute_rho1d(dx,dy,dz); + + z0 = delvolinv * q[i]; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + y0 = z0*rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + x0 = y0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + density_brick[mz][my][mx] += x0*rho1d[0][l]; + } + } + } + } +} + +/* ---------------------------------------------------------------------- + interpolate from grid to get electric field & force on my particles +------------------------------------------------------------------------- */ + +void PPPMTIP4P::fieldforce() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz; + double dx,dy,dz,x0,y0,z0; + double ek[3]; + double *xi; + int iH1,iH2; + double xM[3]; + double fx,fy,fz; + + // loop over my charges, interpolate electric field from nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + // ek = 3 components of E-field on particle + + double *q = atom->q; + double **x = atom->x; + double **f = atom->f; + int *type = atom->type; + int nlocal = atom->nlocal; + double boxxlo = domain->boxxlo; + double boxylo = domain->boxylo; + double boxzlo = domain->boxzlo; + + for (i = 0; i < nlocal; i++) { + if (type[i] == typeO) { + find_M(i,iH1,iH2,xM); + xi = xM; + } else xi = x[i]; + + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (xi[0]-boxxlo)*delxinv; + dy = ny+shiftone - (xi[1]-boxylo)*delyinv; + dz = nz+shiftone - (xi[2]-boxzlo)*delzinv; + + compute_rho1d(dx,dy,dz); + + ek[0] = ek[1] = ek[2] = 0.0; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + z0 = rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + y0 = z0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + x0 = y0*rho1d[0][l]; + ek[0] -= x0*vdx_brick[mz][my][mx]; + ek[1] -= x0*vdy_brick[mz][my][mx]; + ek[2] -= x0*vdz_brick[mz][my][mx]; + } + } + } + + // convert E-field to force + + if (type[i] != typeO) { + f[i][0] += qqrd2e*q[i]*ek[0]; + f[i][1] += qqrd2e*q[i]*ek[1]; + f[i][2] += qqrd2e*q[i]*ek[2]; + } else { + + fx = qqrd2e * q[i] * ek[0]; + fy = qqrd2e * q[i] * ek[1]; + fz = qqrd2e * q[i] * ek[2]; + find_M(i,iH1,iH2,xM); + + f[i][0] += fx*(1.0-2.0*alpha); + f[i][1] += fy*(1.0-2.0*alpha); + f[i][2] += fz*(1.0-2.0*alpha); + + f[iH1][0] += alpha*(fx); + f[iH1][1] += alpha*(fy); + f[iH1][2] += alpha*(fz); + + f[iH2][0] += alpha*(fx); + f[iH2][1] += alpha*(fy); + f[iH2][2] += alpha*(fz); + } + } +} + +/* ---------------------------------------------------------------------- + find 2 H atoms bonded to O atom i + compute position xM of fictitious charge site for O atom + also return local indices iH1,iH2 of H atoms +------------------------------------------------------------------------- */ + +void PPPMTIP4P::find_M(int i, int &iH1, int &iH2, double *xM) +{ + iH1 = atom->map(atom->tag[i] + 1); + iH2 = atom->map(atom->tag[i] + 2); + + if (iH1 == -1 || iH2 == -1) error->one("TIP4P hydrogen is missing"); + if (atom->type[iH1] != typeH || atom->type[iH2] != typeH) + error->one("TIP4P hydrogen has incorrect atom type"); + + double **x = atom->x; + + double delx1 = x[iH1][0] - x[i][0]; + double dely1 = x[iH1][1] - x[i][1]; + double delz1 = x[iH1][2] - x[i][2]; + domain->minimum_image(&delx1,&dely1,&delz1); + + double delx2 = x[iH2][0] - x[i][0]; + double dely2 = x[iH2][1] - x[i][1]; + double delz2 = x[iH2][2] - x[i][2]; + domain->minimum_image(&delx2,&dely2,&delz2); + + xM[0] = x[i][0] + alpha * (delx1 + delx2); + xM[1] = x[i][1] + alpha * (dely1 + dely2); + xM[2] = x[i][2] + alpha * (delz1 + delz2); +} diff --git a/src/pppm_tip4p.h b/src/pppm_tip4p.h new file mode 100644 index 0000000000000000000000000000000000000000..c23ca062aeaa50b9f9b8b6ecbcaa13ee21236483 --- /dev/null +++ b/src/pppm_tip4p.h @@ -0,0 +1,31 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PPPM_TIP4P_H +#define PPPM_TIP4P_H + +#include "pppm.h" + +class PPPMTIP4P : public PPPM { + public: + PPPMTIP4P(int, char **); + + private: + void particle_map(); + void make_rho(); + void fieldforce(); + + void find_M(int, int &, int &, double *); +}; + +#endif diff --git a/src/pressure.cpp b/src/pressure.cpp new file mode 100644 index 0000000000000000000000000000000000000000..49d6e71000d034574241b6980d1b406cd81d292e --- /dev/null +++ b/src/pressure.cpp @@ -0,0 +1,169 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "string.h" +#include "pressure.h" +#include "output.h" +#include "thermo.h" +#include "temperature.h" +#include "force.h" +#include "modify.h" +#include "fix.h" +#include "fix_npt.h" +#include "fix_nph.h" +#include "bond.h" +#include "angle.h" +#include "dihedral.h" +#include "improper.h" +#include "pair.h" +#include "kspace.h" +#include "atom.h" +#include "domain.h" + +/* ---------------------------------------------------------------------- */ + +void Pressure::init() +{ + boltz = force->boltz; + nktv2p = force->nktv2p; + + // set tensor flag if p_tensor will ever be used + // possibly used by thermo and fix_npt, fix_nph + + tensorflag = 0; + if (output->thermo->tensorflag) tensorflag = 1; + for (int i = 0; i < modify->nfix; i++) { + if (strcmp(modify->fix[i]->style,"npt") == 0) + if (((FixNPT *) modify->fix[i])->press_couple > 0) tensorflag = 1; + if (strcmp(modify->fix[i]->style,"nph") == 0) + if (((FixNPH *) modify->fix[i])->press_couple > 0) tensorflag = 1; + } + + // set flags/ptrs for all contributions to virial + + pairflag = bondflag = angleflag = dihedralflag = improperflag = 0; + kspaceflag = 0; + shakeflag = bodyflag = rigidflag = poemsflag = 0; + + if (force->pair) { + pairflag = 1; + pair_virial = force->pair->virial; + } + if (atom->molecular) { + if (force->bond) { + bondflag = 1; + bond_virial = force->bond->virial; + } + if (force->angle) { + angleflag = 1; + angle_virial = force->angle->virial; + } + if (force->dihedral) { + dihedralflag = 1; + dihedral_virial = force->dihedral->virial; + } + if (force->improper) { + improperflag = 1; + improper_virial = force->improper->virial; + } + } + + if (force->kspace) { + kspaceflag = 1; + kspace_virial = force->kspace->virial; + } + + for (int i = 0; i < modify->nfix; i++) { + if (strcmp(modify->fix[i]->style,"shake") == 0) { + shakeflag = 1; + shake_virial = modify->fix[i]->virial; + } + if (strcmp(modify->fix[i]->style,"rigid") == 0 || + strcmp(modify->fix[i]->style,"poems") == 0) { + bodyflag = 1; + if (strcmp(modify->fix[i]->style,"rigid") == 0) { + rigidflag = 1; + rigid_virial = modify->fix[i]->virial; + } else { + poemsflag = 1; + poems_virial = modify->fix[i]->virial; + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void Pressure::compute(Temperature *temperature) +{ + int i,n; + double v[6]; + + if (tensorflag) n = 6; + else n = 3; + for (i = 0; i < n; i++) v[i] = 0.0; + + // sum contributions to virial from various forces and fixes + + if (pairflag) + for (i = 0; i < n; i++) v[i] += pair_virial[i]; + + if (atom->molecular) { + if (bondflag) + for (i = 0; i < n; i++) v[i] += bond_virial[i]; + if (angleflag) + for (i = 0; i < n; i++) v[i] += angle_virial[i]; + if (dihedralflag) + for (i = 0; i < n; i++) v[i] += dihedral_virial[i]; + if (improperflag) + for (i = 0; i < n; i++) v[i] += improper_virial[i]; + if (shakeflag) + for (i = 0; i < n; i++) v[i] += shake_virial[i]; + } + + if (bodyflag) { + if (rigidflag) for (i = 0; i < n; i++) v[i] += rigid_virial[i]; + if (poemsflag) for (i = 0; i < n; i++) v[i] += poems_virial[i]; + } + + // sum virial across procs + + MPI_Allreduce(v,virial,n,MPI_DOUBLE,MPI_SUM,world); + + // KSpace virial contribution is already summed across procs + + if (force->kspace) + for (i = 0; i < n; i++) virial[i] += kspace_virial[i]; + + // LJ long-range tail correction + + double inv_volume = 1.0 / (domain->xprd * domain->yprd * domain->zprd); + + if (force->pair->tail_flag) + for (i = 0; i < n; i++) virial[i] += force->pair->ptail * inv_volume; + + // compute just total average pressure or entire pressure tensor + // p_total = total pressure = average of trace of tensor + // for tensor, must first compute 6 components of kinetic energy tensor + + if (tensorflag) { + temperature->tensor(); + double *ke_tensor = temperature->ke_tensor; + for (i = 0; i < n; i++) + p_tensor[i] = (ke_tensor[i] + virial[i]) * inv_volume * nktv2p; + p_total = (p_tensor[0] + p_tensor[1] + p_tensor[2]) / 3.0; + } else + p_total = (temperature->dof * boltz * temperature->t_total + + virial[0] + virial[1] + virial[2]) / 3.0 * inv_volume * nktv2p; +} diff --git a/src/pressure.h b/src/pressure.h new file mode 100644 index 0000000000000000000000000000000000000000..9fc0dfd7560371100c7c8a9a8817bb1589942ec3 --- /dev/null +++ b/src/pressure.h @@ -0,0 +1,41 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef PRESSURE_H +#define PRESSURE_H + +#include "lammps.h" + +class Temperature; + +class Pressure : public LAMMPS { + public: + double p_total; + double p_tensor[6],virial[6]; + + Pressure() {} + ~Pressure() {} + void init(); + void compute(Temperature *); + + private: + double boltz,nktv2p; + double *pair_virial,*bond_virial,*angle_virial; + double *dihedral_virial,*improper_virial,*kspace_virial; + double *shake_virial,*rigid_virial,*poems_virial; + int tensorflag; + int pairflag,bondflag,angleflag,dihedralflag,improperflag,kspaceflag; + int shakeflag,bodyflag,rigidflag,poemsflag; +}; + +#endif diff --git a/src/random_mars.cpp b/src/random_mars.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1fc41a3813e9a9e708f311510ba6172ae92bc77b --- /dev/null +++ b/src/random_mars.cpp @@ -0,0 +1,111 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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. +------------------------------------------------------------------------- */ + +// Marsaglia random number generator + +#include "math.h" +#include "random_mars.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +RanMars::RanMars(int seed) +{ + int ij,kl,i,j,k,l,ii,jj,m; + double s,t; + + if (seed == 0) error->all("Marsaglia RNG cannot use 0 seed"); + + u = new double[97+1]; + + ij = (seed-1)/30082; + kl = (seed-1) - 30082*ij; + i = (ij/177) % 177 + 2; + j = ij %177 + 2; + k = (kl/169) % 178 + 1; + l = kl % 169; + for (ii = 1; ii <= 97; ii++) { + s = 0.0; + t = 0.5; + for (jj = 1; jj <= 24; jj++) { + m = ((i*j) % 179)*k % 179; + i = j; + j = k; + k = m; + l = (53*l+1) % 169; + if ((l*m) % 64 >= 32) s = s + t; + t = 0.5*t; + } + u[ii] = s; + } + c = 362436.0 / 16777216.0; + cd = 7654321.0 / 16777216.0; + cm = 16777213.0 / 16777216.0; + i97 = 97; + j97 = 33; + uniform(); +} + +/* ---------------------------------------------------------------------- */ + +RanMars::~RanMars() +{ + delete [] u; +} + +/* ---------------------------------------------------------------------- + uniform RN +------------------------------------------------------------------------- */ + +double RanMars::uniform() +{ + double uni = u[i97] - u[j97]; + if (uni < 0.0) uni += 1.0; + u[i97] = uni; + i97--; + if (i97 == 0) i97 = 97; + j97--; + if (j97 == 0) j97 = 97; + c -= cd; + if (c < 0.0) c += cm; + uni -= c; + if (uni < 0.0) uni += 1.0; + return uni; +} + +/* ---------------------------------------------------------------------- + gaussian RN +------------------------------------------------------------------------- */ + +double RanMars::gaussian() +{ + double first,v1,v2,rsq,fac; + + if (!save) { + int again = 1; + while (again) { + v1 = 2.0*uniform()-1.0; + v2 = 2.0*uniform()-1.0; + rsq = v1*v1 + v2*v2; + if (rsq < 1.0 && rsq != 0.0) again = 0; + } + fac = sqrt(-2.0*log(rsq)/rsq); + second = v1*fac; + first = v2*fac; + save = 1; + } else { + first = second; + save = 0; + } + return first; +} diff --git a/src/random_mars.h b/src/random_mars.h new file mode 100644 index 0000000000000000000000000000000000000000..6940b384e8a526576e709d55cdc3d1c64e71f55f --- /dev/null +++ b/src/random_mars.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef RANMARS_H +#define RANMARS_H + +#include "lammps.h" + +class RanMars : public LAMMPS { + public: + RanMars(int); + ~RanMars(); + double uniform(); + double gaussian(); + + private: + int seed,save; + double second; + double *u; + int i97,j97; + double c,cd,cm; +}; + +#endif diff --git a/src/random_park.cpp b/src/random_park.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a61eef880b859f5c0a3cd5ff5aac14e3b9f74219 --- /dev/null +++ b/src/random_park.cpp @@ -0,0 +1,81 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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. +------------------------------------------------------------------------- */ + +// Park/Miller RNG + +#include "math.h" +#include "random_park.h" + +#define IA 16807 +#define IM 2147483647 +#define AM (1.0/IM) +#define IQ 127773 +#define IR 2836 + +/* ---------------------------------------------------------------------- */ + +RanPark::RanPark(int seed_init) +{ + seed = seed_init; + save = 0; +} + +/* ---------------------------------------------------------------------- + uniform RN +------------------------------------------------------------------------- */ + +double RanPark::uniform() +{ + int k = seed/IQ; + seed = IA*(seed-k*IQ) - IR*k; + if (seed < 0) seed += IM; + double ans = AM*seed; + return ans; +} + +/* ---------------------------------------------------------------------- + gaussian RN +------------------------------------------------------------------------- */ + +double RanPark::gaussian() +{ + double first,v1,v2,rsq,fac; + + if (!save) { + int again = 1; + while (again) { + v1 = 2.0*uniform()-1.0; + v2 = 2.0*uniform()-1.0; + rsq = v1*v1 + v2*v2; + if (rsq < 1.0 && rsq != 0.0) again = 0; + } + fac = sqrt(-2.0*log(rsq)/rsq); + second = v1*fac; + first = v2*fac; + save = 1; + } else { + first = second; + save = 0; + } + return first; +} + +/* ---------------------------------------------------------------------- + reset the seed to a fractional (0-1) value of the total IM +------------------------------------------------------------------------- */ + +void RanPark::reset(double fraction) +{ + seed = static_cast<int> (fraction*IM) + 1; + if (seed >= IM) seed = IM-1; +} diff --git a/src/random_park.h b/src/random_park.h new file mode 100644 index 0000000000000000000000000000000000000000..71b5c29a8f31e9827fde177d293a8742b0b1b14d --- /dev/null +++ b/src/random_park.h @@ -0,0 +1,30 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef RANPARK_H +#define RANPARK_H + +class RanPark { + public: + RanPark(int); + ~RanPark() {} + double uniform(); + double gaussian(); + void reset(double); + + private: + int seed,save; + double second; +}; + +#endif diff --git a/src/read_data.cpp b/src/read_data.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6f15431ee2e9e9790dc82098c5a97d5d7f8a6552 --- /dev/null +++ b/src/read_data.cpp @@ -0,0 +1,1227 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "string.h" +#include "stdlib.h" +#include "read_data.h" +#include "atom.h" +#include "comm.h" +#include "update.h" +#include "force.h" +#include "pair.h" +#include "domain.h" +#include "bond.h" +#include "angle.h" +#include "dihedral.h" +#include "improper.h" +#include "error.h" +#include "atom_atomic.h" +#include "memory.h" +#include "special.h" + +#define MAXLINE 256 +#define LB_FACTOR 1.1 +#define CHUNK 1024 +#define DELTA 4 + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +ReadData::ReadData() +{ + MPI_Comm_rank(world,&me); + line = new char[MAXLINE]; + keyword = new char[MAXLINE]; + buffer = new char[CHUNK*MAXLINE]; + narg = maxarg = 0; + arg = NULL; +} + +/* ---------------------------------------------------------------------- */ + +ReadData::~ReadData() +{ + delete [] line; + delete [] keyword; + delete [] buffer; + memory->sfree(arg); +} + +/* ---------------------------------------------------------------------- */ + +void ReadData::command(int narg, char **arg) +{ + if (narg != 1) error->all("Illegal read_data command"); + + if (atom == NULL) + error->all("Cannot read_data until atom_style is defined"); + if (domain->box_exist) + error->all("Cannot read_data after simulation box is defined"); + + if (force->dimension == 2 && domain->zperiodic == 0) + error->all("Cannot run 2d simulation with nonperiodic Z dimension"); + + // scan data file to determine max topology needed per atom + // allocate initial topology arrays + + if (atom->molecular) { + if (me == 0) { + if (screen) fprintf(screen,"Scanning data file ...\n"); + open(arg[0]); + header(0); + scan(&atom->bond_per_atom,&atom->angle_per_atom, + &atom->dihedral_per_atom,&atom->improper_per_atom); + fclose(fp); + } + MPI_Bcast(&atom->bond_per_atom,1,MPI_INT,0,world); + MPI_Bcast(&atom->angle_per_atom,1,MPI_INT,0,world); + MPI_Bcast(&atom->dihedral_per_atom,1,MPI_INT,0,world); + MPI_Bcast(&atom->improper_per_atom,1,MPI_INT,0,world); + } else + atom->bond_per_atom = atom->angle_per_atom = + atom->dihedral_per_atom = atom->improper_per_atom = 0; + + // read header info + + if (me == 0) { + if (screen) fprintf(screen,"Reading data file ...\n"); + open(arg[0]); + } + header(1); + domain->box_exist = 1; + + // problem setup using info from header + + update->ntimestep = 0; + + int n; + if (comm->nprocs == 1) n = static_cast<int> (atom->natoms); + else n = static_cast<int> (LB_FACTOR * atom->natoms / comm->nprocs); + + atom->allocate_type_arrays(); + atom->grow(n); + + domain->set_initial_box(); + domain->set_global_box(); + comm->set_procs(); + domain->set_local_box(); + + // read rest of file in free format + + int atomflag = 0; + parse_keyword(1,1); + + while (strlen(keyword)) { + + if (strcmp(keyword,"Atoms") == 0) { + atoms(); + atomflag = 1; + } else if (strcmp(keyword,"Velocities") == 0) { + if (atomflag == 0) error->all("Must read Atoms before Velocities"); + velocities(); + } else if (strcmp(keyword,"Bonds") == 0) { + if (atom->bonds_allow == 0) + error->all("Invalid data file section: Bonds"); + if (atomflag == 0) error->all("Must read Atoms before Bonds"); + bonds(); + } else if (strcmp(keyword,"Angles") == 0) { + if (atom->angles_allow == 0) + error->all("Invalid data file section: Angles"); + if (atomflag == 0) error->all("Must read Atoms before Angles"); + angles(); + } else if (strcmp(keyword,"Dihedrals") == 0) { + if (atom->dihedrals_allow == 0) + error->all("Invalid data file section: Dihedrals"); + if (atomflag == 0) error->all("Must read Atoms before Dihedrals"); + dihedrals(); + } else if (strcmp(keyword,"Impropers") == 0) { + if (atom->impropers_allow == 0) + error->all("Invalid data file section: Impropers"); + if (atomflag == 0) error->all("Must read Atoms before Impropers"); + impropers(); + + } else if (strcmp(keyword,"Masses") == 0) { + mass(); + } else if (strcmp(keyword,"Dipoles") == 0) { + dipole(); + } else if (strcmp(keyword,"Pair Coeffs") == 0) { + if (force->pair == NULL) + error->all("Must define pair_style before Pair Coeffs"); + paircoeffs(); + } else if (strcmp(keyword,"Bond Coeffs") == 0) { + if (atom->bonds_allow == 0) + error->all("Invalid data file section: Bond Coeffs"); + if (force->bond == NULL) + error->all("Must define bond_style before Bond Coeffs"); + bondcoeffs(); + } else if (strcmp(keyword,"Angle Coeffs") == 0) { + if (atom->angles_allow == 0) + error->all("Invalid data file section: Angle Coeffs"); + if (force->angle == NULL) + error->all("Must define angle_style before Angle Coeffs"); + anglecoeffs(0); + } else if (strcmp(keyword,"Dihedral Coeffs") == 0) { + if (atom->dihedrals_allow == 0) + error->all("Invalid data file section: Dihedral Coeffs"); + if (force->dihedral == NULL) + error->all("Must define dihedral_style before Dihedral Coeffs"); + dihedralcoeffs(0); + } else if (strcmp(keyword,"Improper Coeffs") == 0) { + if (atom->impropers_allow == 0) + error->all("Invalid data file section: Improper Coeffs"); + if (force->improper == NULL) + error->all("Must define improper_style before Improper Coeffs"); + impropercoeffs(0); + + } else if (strcmp(keyword,"BondBond Coeffs") == 0) { + if (atom->angles_allow == 0) + error->all("Invalid data file section: BondBond Coeffs"); + if (force->angle == NULL) + error->all("Must define angle_style before BondBond Coeffs"); + anglecoeffs(1); + } else if (strcmp(keyword,"BondAngle Coeffs") == 0) { + if (atom->angles_allow == 0) + error->all("Invalid data file section: BondAngle Coeffs"); + if (force->angle == NULL) + error->all("Must define angle_style before BondAngle Coeffs"); + anglecoeffs(2); + + } else if (strcmp(keyword,"MiddleBondTorsion Coeffs") == 0) { + if (atom->dihedrals_allow == 0) + error->all("Invalid data file section: MiddleBondTorsion Coeffs"); + if (force->dihedral == NULL) + error->all("Must define dihedral_style before MiddleBondTorsion Coeffs"); + dihedralcoeffs(1); + } else if (strcmp(keyword,"EndBondTorsion Coeffs") == 0) { + if (atom->dihedrals_allow == 0) + error->all("Invalid data file section: EndBondTorsion Coeffs"); + if (force->dihedral == NULL) + error->all("Must define dihedral_style before EndBondTorsion Coeffs"); + dihedralcoeffs(2); + } else if (strcmp(keyword,"AngleTorsion Coeffs") == 0) { + if (atom->dihedrals_allow == 0) + error->all("Invalid data file section: AngleTorsion Coeffs"); + if (force->dihedral == NULL) + error->all("Must define dihedral_style before AngleTorsion Coeffs"); + dihedralcoeffs(3); + } else if (strcmp(keyword,"AngleAngleTorsion Coeffs") == 0) { + if (atom->dihedrals_allow == 0) + error->all("Invalid data file section: AngleAngleTorsion Coeffs"); + if (force->dihedral == NULL) + error->all("Must define dihedral_style before AngleAngleTorsion Coeffs"); + dihedralcoeffs(4); + } else if (strcmp(keyword,"BondBond13 Coeffs") == 0) { + if (atom->dihedrals_allow == 0) + error->all("Invalid data file section: BondBond13 Coeffs"); + if (force->dihedral == NULL) + error->all("Must define dihedral_style before BondBond13 Coeffs"); + dihedralcoeffs(5); + + } else if (strcmp(keyword,"AngleAngle Coeffs") == 0) { + if (atom->impropers_allow == 0) + error->all("Invalid data file section: AngleAngle Coeffs"); + if (force->improper == NULL) + error->all("Must define improper_style before AngleAngle Coeffs"); + impropercoeffs(1); + + } else { + char str[128]; + sprintf(str,"Unknown section in data file: %s",keyword); + error->all(str); + } + + parse_keyword(0,1); + } + + // close file + + if (me == 0) fclose(fp); + + // error if natoms > 0 yet no atoms were read + + if (atom->natoms > 0 && atomflag == 0) error->all("No atoms in data file"); + + // create bond topology now that system is defined + + if (atom->molecular) { + Special special; + special.build(); + } +} + +/* ---------------------------------------------------------------------- + read free-format header of data file + if flag = 0, only called by proc 0 + if flag = 1, called by all procs so bcast lines as read them + 1st line and blank lines are skipped + non-blank lines are checked for header keywords and leading value is read + header ends with EOF or non-blank line containing no header keyword + if EOF, line is set to blank line + else line has first keyword line for rest of file +------------------------------------------------------------------------- */ + +void ReadData::header(int flag) +{ + int n; + char *ptr; + + // skip 1st line of file + + if (me == 0) { + char *eof = fgets(line,MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + } + + while (1) { + + // read a line and bcast length if flag is set + + if (me == 0) { + if (fgets(line,MAXLINE,fp) == NULL) n = 0; + else n = strlen(line) + 1; + } + if (flag) MPI_Bcast(&n,1,MPI_INT,0,world); + + // if n = 0 then end-of-file so return with blank line + + if (n == 0) { + line[0] = '\0'; + return; + } + + // bcast line if flag is set + + if (flag) MPI_Bcast(line,n,MPI_CHAR,0,world); + + // trim anything from '#' onward + // if line is blank, continue + + if (ptr = strchr(line,'#')) *ptr = '\0'; + if (strspn(line," \t\n\r") == strlen(line)) continue; + + // search line for header keyword and set corresponding variable + + if (strstr(line,"atoms")) sscanf(line,"%lg",&atom->natoms); + else if (strstr(line,"bonds")) sscanf(line,"%d",&atom->nbonds); + else if (strstr(line,"angles")) sscanf(line,"%d",&atom->nangles); + else if (strstr(line,"dihedrals")) sscanf(line,"%d",&atom->ndihedrals); + else if (strstr(line,"impropers")) sscanf(line,"%d",&atom->nimpropers); + else if (strstr(line,"atom types")) sscanf(line,"%d",&atom->ntypes); + else if (strstr(line,"bond types")) sscanf(line,"%d",&atom->nbondtypes); + else if (strstr(line,"angle types")) sscanf(line,"%d",&atom->nangletypes); + else if (strstr(line,"dihedral types")) + sscanf(line,"%d",&atom->ndihedraltypes); + else if (strstr(line,"improper types")) + sscanf(line,"%d",&atom->nimpropertypes); + else if (strstr(line,"xlo xhi")) + sscanf(line,"%lg %lg",&domain->boxxlo,&domain->boxxhi); + else if (strstr(line,"ylo yhi")) + sscanf(line,"%lg %lg",&domain->boxylo,&domain->boxyhi); + else if (strstr(line,"zlo zhi")) + sscanf(line,"%lg %lg",&domain->boxzlo,&domain->boxzhi); + else break; + } + + // error check on consistency of header values + + if ((atom->nbonds || atom->nbondtypes) && atom->bonds_allow == 0) + error->one("No bonds allowed with this atom style"); + if ((atom->nangles || atom->nangletypes) && atom->angles_allow == 0) + error->one("No angles allowed with this atom style"); + if ((atom->ndihedrals || atom->ndihedraltypes) && atom->dihedrals_allow == 0) + error->one("No dihedrals allowed with this atom style"); + if ((atom->nimpropers || atom->nimpropertypes) && atom->impropers_allow == 0) + error->one("No impropers allowed with this atom style"); + + if (atom->nbonds > 0 && atom->nbondtypes <= 0) + error->one("Bonds defined but no bond types"); + if (atom->nangles > 0 && atom->nangletypes <= 0) + error->one("Angles defined but no angle types"); + if (atom->ndihedrals > 0 && atom->ndihedraltypes <= 0) + error->one("Dihedrals defined but no dihedral types"); + if (atom->nimpropers > 0 && atom->nimpropertypes <= 0) + error->one("Impropers defined but no improper types"); + + if (domain->boxxlo >= domain->boxxhi || domain->boxylo >= domain->boxyhi || + domain->boxzlo >= domain->boxzhi) + error->one("Box bounds are invalid"); +} + +/* ---------------------------------------------------------------------- + read all atoms + to find atoms, must build atom map if not a molecular system + accumulate nread in double precision to allow natoms > 2^31 +------------------------------------------------------------------------- */ + +void ReadData::atoms() +{ + int i,m,nchunk; + + if (me == 0) { + char *eof = fgets(buffer,MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + m = strlen(buffer) + 1; + } + MPI_Bcast(&m,1,MPI_INT,0,world); + MPI_Bcast(buffer,m,MPI_CHAR,0,world); + if (atom->parse_data(buffer)) + error->all("Incorrect atom format in data file"); + + atom->unpack_data(1,buffer); + double nread = 1.0; + double natoms = atom->natoms; + + while (nread < natoms) { + if (natoms-nread > CHUNK) nchunk = CHUNK; + else nchunk = static_cast<int> (natoms - nread); + if (me == 0) { + char *eof; + m = 0; + for (i = 0; i < nchunk; i++) { + eof = fgets(&buffer[m],MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + m += strlen(&buffer[m]); + } + buffer[m++] = '\n'; + } + MPI_Bcast(&m,1,MPI_INT,0,world); + MPI_Bcast(buffer,m,MPI_CHAR,0,world); + + atom->unpack_data(nchunk,buffer); + nread += nchunk; + } + + // check that all atoms were assigned correctly + + double tmp = atom->nlocal; + MPI_Allreduce(&tmp,&natoms,1,MPI_DOUBLE,MPI_SUM,world); + + if (me == 0) { + if (screen) fprintf(screen," %.15g atoms\n",natoms); + if (logfile) fprintf(logfile," %.15g atoms\n",natoms); + } + + if (natoms != atom->natoms) error->all("Did not assign all atoms correctly"); + + // if any atom ID < 0, error + // if all atom IDs == 0, tag_enable = 0 + // if any atom ID > 0, error if any atom ID == 0 + // not checking if atom IDs > natoms or are unique + + int flag = 0; + for (int i = 0; i < atom->nlocal; i++) + if (atom->tag[i] < 0) flag = 1; + int flag_all; + MPI_Allreduce(&flag,&flag_all,1,MPI_INT,MPI_SUM,world); + if (flag_all) + error->all("Invalid atom ID in Atoms section of data file"); + + flag = 0; + for (int i = 0; i < atom->nlocal; i++) + if (atom->tag[i] > 0) flag = 1; + MPI_Allreduce(&flag,&flag_all,1,MPI_INT,MPI_MAX,world); + if (flag_all == 0) atom->tag_enable = 0; + + if (atom->tag_enable) { + flag = 0; + for (int i = 0; i < atom->nlocal; i++) + if (atom->tag[i] == 0) flag = 1; + MPI_Allreduce(&flag,&flag_all,1,MPI_INT,MPI_SUM,world); + if (flag_all) + error->all("Invalid atom ID in Atoms section of data file"); + } + + // create global mapping + + if (atom->map_style) { + atom->map_init(); + atom->map_set(); + } +} + +/* ---------------------------------------------------------------------- + read all velocities + to find atoms, must build atom map if not a molecular system + accumulate nread in double precision to allow natoms > 2^31 +------------------------------------------------------------------------- */ + +void ReadData::velocities() +{ + int i,m,nchunk; + + int mapflag = 0; + if (atom->map_style == 0) { + mapflag = 1; + atom->map_style = 1; + atom->map_init(); + atom->map_set(); + } + + double nread = 0.0; + double natoms = static_cast<int> (atom->natoms); + + while (nread < natoms) { + if (natoms-nread > CHUNK) nchunk = CHUNK; + else nchunk = static_cast<int> (natoms - nread); + if (me == 0) { + char *eof; + m = 0; + for (i = 0; i < nchunk; i++) { + eof = fgets(&buffer[m],MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + m += strlen(&buffer[m]); + } + buffer[m++] = '\n'; + } + MPI_Bcast(&m,1,MPI_INT,0,world); + MPI_Bcast(buffer,m,MPI_CHAR,0,world); + + atom->unpack_vels(nchunk,buffer); + nread += nchunk; + } + + if (mapflag) { + atom->map_delete(); + atom->map_style = 0; + } + + if (me == 0) { + if (screen) fprintf(screen," %.15g velocities\n",natoms); + if (logfile) fprintf(logfile," %.15g velocities\n",natoms); + } +} + +/* ---------------------------------------------------------------------- */ + +void ReadData::bonds() +{ + int i,m,nchunk; + + int nread = 0; + while (nread < atom->nbonds) { + nchunk = MIN(atom->nbonds-nread,CHUNK); + if (me == 0) { + char *eof; + m = 0; + for (i = 0; i < nchunk; i++) { + eof = fgets(&buffer[m],MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + m += strlen(&buffer[m]); + } + buffer[m++] = '\n'; + } + MPI_Bcast(&m,1,MPI_INT,0,world); + MPI_Bcast(buffer,m,MPI_CHAR,0,world); + + atom->unpack_bonds(nchunk,buffer); + nread += nchunk; + } + + // check that bonds were assigned correctly + + int sum; + int n = 0; + for (i = 0; i < atom->nlocal; i++) n += atom->num_bond[i]; + MPI_Allreduce(&n,&sum,1,MPI_INT,MPI_SUM,world); + int factor = 1; + if (!force->newton_bond) factor = 2; + + if (me == 0) { + if (screen) fprintf(screen," %d bonds\n",sum/factor); + if (logfile) fprintf(logfile," %d bonds\n",sum/factor); + } + if (sum != factor*atom->nbonds) error->all("Bonds assigned incorrectly"); +} + +/* ---------------------------------------------------------------------- */ + +void ReadData::angles() +{ + int i,m,nchunk; + + int nread = 0; + while (nread < atom->nangles) { + nchunk = MIN(atom->nangles-nread,CHUNK); + if (me == 0) { + char *eof; + m = 0; + for (i = 0; i < nchunk; i++) { + eof = fgets(&buffer[m],MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + m += strlen(&buffer[m]); + } + buffer[m++] = '\n'; + } + MPI_Bcast(&m,1,MPI_INT,0,world); + MPI_Bcast(buffer,m,MPI_CHAR,0,world); + + atom->unpack_angles(nchunk,buffer); + nread += nchunk; + } + + // check that angles were assigned correctly + + int sum; + int n = 0; + for (i = 0; i < atom->nlocal; i++) n += atom->num_angle[i]; + MPI_Allreduce(&n,&sum,1,MPI_INT,MPI_SUM,world); + int factor = 1; + if (!force->newton_bond) factor = 3; + + if (me == 0) { + if (screen) fprintf(screen," %d angles\n",sum/factor); + if (logfile) fprintf(logfile," %d angles\n",sum/factor); + } + if (sum != factor*atom->nangles) error->all("Angles assigned incorrectly"); +} + +/* ---------------------------------------------------------------------- */ + +void ReadData::dihedrals() +{ + int i,m,nchunk; + + int nread = 0; + while (nread < atom->ndihedrals) { + nchunk = MIN(atom->ndihedrals-nread,CHUNK); + if (me == 0) { + char *eof; + m = 0; + for (i = 0; i < nchunk; i++) { + eof = fgets(&buffer[m],MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + m += strlen(&buffer[m]); + } + buffer[m++] = '\n'; + } + MPI_Bcast(&m,1,MPI_INT,0,world); + MPI_Bcast(buffer,m,MPI_CHAR,0,world); + + atom->unpack_dihedrals(nchunk,buffer); + nread += nchunk; + } + + // check that dihedrals were assigned correctly + + int sum; + int n = 0; + for (i = 0; i < atom->nlocal; i++) n += atom->num_dihedral[i]; + MPI_Allreduce(&n,&sum,1,MPI_INT,MPI_SUM,world); + int factor = 1; + if (!force->newton_bond) factor = 4; + + if (me == 0) { + if (screen) fprintf(screen," %d dihedrals\n",sum/factor); + if (logfile) fprintf(logfile," %d dihedrals\n",sum/factor); + } + if (sum != factor*atom->ndihedrals) + error->all("Dihedrals assigned incorrectly"); +} + +/* ---------------------------------------------------------------------- */ + +void ReadData::impropers() +{ + int i,m,nchunk; + + int nread = 0; + while (nread < atom->nimpropers) { + nchunk = MIN(atom->nimpropers-nread,CHUNK); + if (me == 0) { + char *eof; + m = 0; + for (i = 0; i < nchunk; i++) { + eof = fgets(&buffer[m],MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + m += strlen(&buffer[m]); + } + buffer[m++] = '\n'; + } + MPI_Bcast(&m,1,MPI_INT,0,world); + MPI_Bcast(buffer,m,MPI_CHAR,0,world); + + atom->unpack_impropers(nchunk,buffer); + nread += nchunk; + } + + // check that impropers were assigned correctly + + int sum; + int n = 0; + for (i = 0; i < atom->nlocal; i++) n += atom->num_improper[i]; + MPI_Allreduce(&n,&sum,1,MPI_INT,MPI_SUM,world); + int factor = 1; + if (!force->newton_bond) factor = 4; + + if (me == 0) { + if (screen) fprintf(screen," %d impropers\n",sum/factor); + if (logfile) fprintf(logfile," %d impropers\n",sum/factor); + } + if (sum != factor*atom->nimpropers) + error->all("Impropers assigned incorrectly"); +} + +/* ---------------------------------------------------------------------- */ + +void ReadData::mass() +{ + int i,m; + char *buf = new char[atom->ntypes*MAXLINE]; + char *original = buf; + + if (me == 0) { + char *eof; + m = 0; + for (i = 0; i < atom->ntypes; i++) { + eof = fgets(&buf[m],MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + m += strlen(&buf[m]); + buf[m-1] = '\0'; + } + } + + MPI_Bcast(&m,1,MPI_INT,0,world); + MPI_Bcast(buf,m,MPI_CHAR,0,world); + + for (i = 0; i < atom->ntypes; i++) { + atom->set_mass(buf); + buf += strlen(buf) + 1; + } + delete [] original; +} + +/* ---------------------------------------------------------------------- */ + +void ReadData::dipole() +{ + int i,m; + char *buf = new char[atom->ntypes*MAXLINE]; + char *original = buf; + + if (me == 0) { + char *eof; + m = 0; + for (i = 0; i < atom->ntypes; i++) { + eof = fgets(&buf[m],MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + m += strlen(&buf[m]); + buf[m-1] = '\0'; + } + } + + MPI_Bcast(&m,1,MPI_INT,0,world); + MPI_Bcast(buf,m,MPI_CHAR,0,world); + + for (i = 0; i < atom->ntypes; i++) { + atom->set_dipole(buf); + buf += strlen(buf) + 1; + } + delete [] original; +} + +/* ---------------------------------------------------------------------- */ + +void ReadData::paircoeffs() +{ + int i,m; + char *buf = new char[atom->ntypes*MAXLINE]; + char *original = buf; + + if (me == 0) { + char *eof; + m = 0; + for (i = 0; i < atom->ntypes; i++) { + eof = fgets(&buf[m],MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + m += strlen(&buf[m]); + buf[m-1] = '\0'; + } + } + + MPI_Bcast(&m,1,MPI_INT,0,world); + MPI_Bcast(buf,m,MPI_CHAR,0,world); + + for (i = 0; i < atom->ntypes; i++) { + m = strlen(buf) + 1; + parse_coeffs(1,buf); + force->pair->coeff(narg,arg); + buf += m; + } + delete [] original; +} + +/* ---------------------------------------------------------------------- */ + +void ReadData::bondcoeffs() +{ + int i,m; + char *buf = new char[atom->nbondtypes*MAXLINE]; + char *original = buf; + + if (me == 0) { + char *eof; + m = 0; + for (i = 0; i < atom->nbondtypes; i++) { + eof = fgets(&buf[m],MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + m += strlen(&buf[m]); + buf[m-1] = '\0'; + } + } + + MPI_Bcast(&m,1,MPI_INT,0,world); + MPI_Bcast(buf,m,MPI_CHAR,0,world); + + for (i = 0; i < atom->nbondtypes; i++) { + m = strlen(buf) + 1; + parse_coeffs(0,buf); + force->bond->coeff(narg,arg); + buf += m; + } + delete [] original; +} + +/* ---------------------------------------------------------------------- */ + +void ReadData::anglecoeffs(int which) +{ + int i,m; + char *buf = new char[atom->nangletypes*MAXLINE]; + char *original = buf; + + if (me == 0) { + char *eof; + m = 0; + for (i = 0; i < atom->nangletypes; i++) { + eof = fgets(&buf[m],MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + m += strlen(&buf[m]); + buf[m-1] = '\0'; + } + } + + MPI_Bcast(&m,1,MPI_INT,0,world); + MPI_Bcast(buf,m,MPI_CHAR,0,world); + + for (i = 0; i < atom->nangletypes; i++) { + m = strlen(buf) + 1; + parse_coeffs(0,buf); + force->angle->coeff(which,narg,arg); + buf += m; + } + delete [] original; +} + +/* ---------------------------------------------------------------------- */ + +void ReadData::dihedralcoeffs(int which) +{ + int i,m; + char *buf = new char[atom->ndihedraltypes*MAXLINE]; + char *original = buf; + + if (me == 0) { + char *eof; + m = 0; + for (i = 0; i < atom->ndihedraltypes; i++) { + eof = fgets(&buf[m],MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + m += strlen(&buf[m]); + buf[m-1] = '\0'; + } + } + + MPI_Bcast(&m,1,MPI_INT,0,world); + MPI_Bcast(buf,m,MPI_CHAR,0,world); + + for (i = 0; i < atom->ndihedraltypes; i++) { + m = strlen(buf) + 1; + parse_coeffs(0,buf); + force->dihedral->coeff(which,narg,arg); + buf += m; + } + delete [] original; +} + +/* ---------------------------------------------------------------------- */ + +void ReadData::impropercoeffs(int which) +{ + int i,m; + char *buf = new char[atom->nimpropertypes*MAXLINE]; + char *original = buf; + + if (me == 0) { + char *eof; + m = 0; + for (i = 0; i < atom->nimpropertypes; i++) { + eof = fgets(&buf[m],MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + m += strlen(&buf[m]); + buf[m-1] = '\0'; + } + } + + MPI_Bcast(&m,1,MPI_INT,0,world); + MPI_Bcast(buf,m,MPI_CHAR,0,world); + + for (i = 0; i < atom->nimpropertypes; i++) { + m = strlen(buf) + 1; + parse_coeffs(0,buf); + force->improper->coeff(which,narg,arg); + buf += m; + } + delete [] original; +} + +/* ---------------------------------------------------------------------- + proc 0 scans the data file for topology maximums +------------------------------------------------------------------------- */ + +void ReadData::scan(int *pbond_per, int *pangle_per, + int *pdihedral_per, int *pimproper_per) +{ + int i,tmp1,tmp2,atom1,atom2,atom3,atom4; + int bond_per,angle_per,dihedral_per,improper_per; + char *eof; + + int natoms = static_cast<int> (atom->natoms); + bond_per = angle_per = dihedral_per = improper_per = 0; + + // allocate topology counting vector + // initially, array length = 1 to natoms + // will grow via reallocate() if atom IDs > natoms + + int cmax = natoms + 1; + int *count = (int *) memory->smalloc(cmax*sizeof(int),"read_data:count"); + + parse_keyword(1,0); + + while (strlen(keyword)) { + + if (strcmp(keyword,"Masses") == 0) skip_lines(atom->ntypes); + else if (strcmp(keyword,"Dipoles") == 0) skip_lines(atom->ntypes); + else if (strcmp(keyword,"Atoms") == 0) skip_lines(natoms); + else if (strcmp(keyword,"Velocities") == 0) skip_lines(natoms); + + else if (strcmp(keyword,"Pair Coeffs") == 0) skip_lines(atom->ntypes); + else if (strcmp(keyword,"Bond Coeffs") == 0) + skip_lines(atom->nbondtypes); + else if (strcmp(keyword,"Angle Coeffs") == 0) + skip_lines(atom->nangletypes); + else if (strcmp(keyword,"Dihedral Coeffs") == 0) + skip_lines(atom->ndihedraltypes); + else if (strcmp(keyword,"Improper Coeffs") == 0) + skip_lines(atom->nimpropertypes); + + else if (strcmp(keyword,"BondBond Coeffs") == 0) + skip_lines(atom->nangletypes); + else if (strcmp(keyword,"BondAngle Coeffs") == 0) + skip_lines(atom->nangletypes); + + else if (strcmp(keyword,"MiddleBondTorsion Coeffs") == 0) + skip_lines(atom->ndihedraltypes); + else if (strcmp(keyword,"EndBondTorsion Coeffs") == 0) + skip_lines(atom->ndihedraltypes); + else if (strcmp(keyword,"AngleTorsion Coeffs") == 0) + skip_lines(atom->ndihedraltypes); + else if (strcmp(keyword,"AngleAngleTorsion Coeffs") == 0) + skip_lines(atom->ndihedraltypes); + else if (strcmp(keyword,"BondBond13 Coeffs") == 0) + skip_lines(atom->ndihedraltypes); + + else if (strcmp(keyword,"AngleAngle Coeffs") == 0) + skip_lines(atom->nimpropertypes); + + else if (strcmp(keyword,"Bonds") == 0) { + + for (i = 1; i < cmax; i++) count[i] = 0; + if (force->newton_bond) + for (i = 0; i < atom->nbonds; i++) { + eof = fgets(line,MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + sscanf(line,"%d %d %d %d",&tmp1,&tmp2,&atom1,&atom2); + if (atom1 >= cmax) cmax = reallocate(&count,cmax,atom1); + count[atom1]++; + } + else + for (i = 0; i < atom->nbonds; i++) { + eof = fgets(line,MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + sscanf(line,"%d %d %d %d",&tmp1,&tmp2,&atom1,&atom2); + int amax = MAX(atom1,atom2); + if (amax >= cmax) cmax = reallocate(&count,cmax,amax); + count[atom1]++; + count[atom2]++; + } + for (i = 1; i < cmax; i++) bond_per = MAX(bond_per,count[i]); + if (screen) fprintf(screen," %d = max bonds/atom\n",bond_per); + if (logfile) fprintf(logfile," %d = max bonds/atom\n",bond_per); + + } else if (strcmp(keyword,"Angles") == 0) { + + for (i = 1; i < cmax; i++) count[i] = 0; + if (force->newton_bond) + for (i = 0; i < atom->nangles; i++) { + eof = fgets(line,MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + sscanf(line,"%d %d %d %d %d",&tmp1,&tmp2,&atom1,&atom2,&atom3); + if (atom2 >= cmax) cmax = reallocate(&count,cmax,atom2); + count[atom2]++; + } + else + for (i = 0; i < atom->nangles; i++) { + eof = fgets(line,MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + sscanf(line,"%d %d %d %d %d",&tmp1,&tmp2,&atom1,&atom2,&atom3); + int amax = MAX(atom1,atom2); + amax = MAX(amax,atom3); + if (amax >= cmax) cmax = reallocate(&count,cmax,amax); + count[atom1]++; + count[atom2]++; + count[atom3]++; + } + for (i = 1; i < cmax; i++) angle_per = MAX(angle_per,count[i]); + if (screen) fprintf(screen," %d = max angles/atom\n",angle_per); + if (logfile) fprintf(logfile," %d = max angles/atom\n",angle_per); + + } else if (strcmp(keyword,"Dihedrals") == 0) { + + for (i = 1; i < cmax; i++) count[i] = 0; + if (force->newton_bond) + for (i = 0; i < atom->ndihedrals; i++) { + eof = fgets(line,MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + sscanf(line,"%d %d %d %d %d %d", + &tmp1,&tmp2,&atom1,&atom2,&atom3,&atom4); + if (atom2 >= cmax) cmax = reallocate(&count,cmax,atom2); + count[atom2]++; + } + else + for (i = 0; i < atom->ndihedrals; i++) { + eof = fgets(line,MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + sscanf(line,"%d %d %d %d %d %d", + &tmp1,&tmp2,&atom1,&atom2,&atom3,&atom4); + int amax = MAX(atom1,atom2); + amax = MAX(amax,atom3); + amax = MAX(amax,atom4); + if (amax >= cmax) cmax = reallocate(&count,cmax,amax); + count[atom1]++; + count[atom2]++; + count[atom3]++; + count[atom4]++; + } + for (i = 1; i < cmax; i++) dihedral_per = MAX(dihedral_per,count[i]); + if (screen) + fprintf(screen," %d = max dihedrals/atom\n",dihedral_per); + if (logfile) + fprintf(logfile," %d = max dihedrals/atom\n",dihedral_per); + + } else if (strcmp(keyword,"Impropers") == 0) { + for (i = 1; i < cmax; i++) count[i] = 0; + if (force->newton_bond) + for (i = 0; i < atom->nimpropers; i++) { + eof = fgets(line,MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + sscanf(line,"%d %d %d %d %d %d", + &tmp1,&tmp2,&atom1,&atom2,&atom3,&atom4); + if (atom2 >= cmax) cmax = reallocate(&count,cmax,atom2); + count[atom2]++; + } + else + for (i = 0; i < atom->nimpropers; i++) { + eof = fgets(line,MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); + sscanf(line,"%d %d %d %d %d %d", + &tmp1,&tmp2,&atom1,&atom2,&atom3,&atom4); + int amax = MAX(atom1,atom2); + amax = MAX(amax,atom3); + amax = MAX(amax,atom4); + if (amax >= cmax) cmax = reallocate(&count,cmax,amax); + count[atom1]++; + count[atom2]++; + count[atom3]++; + count[atom4]++; + } + for (i = 1; i < cmax; i++) improper_per = MAX(improper_per,count[i]); + if (screen) + fprintf(screen," %d = max impropers/atom\n",improper_per); + if (logfile) + fprintf(logfile," %d = max impropers/atom\n",improper_per); + + } else { + char str[128]; + sprintf(str,"Unknown identifier in data file: %s",keyword); + error->one(str); + } + + parse_keyword(0,0); + } + + // free topology counting vector + + memory->sfree(count); + + // error check that topology was specified in file + + if ((atom->nbonds && !bond_per) || (atom->nangles && !angle_per) || + (atom->ndihedrals && !dihedral_per) || + (atom->nimpropers && !improper_per)) + error->one("Needed topology not in data file"); + + // return values + + *pbond_per = bond_per; + *pangle_per = angle_per; + *pdihedral_per = dihedral_per; + *pimproper_per = improper_per; +} + +/* ---------------------------------------------------------------------- + reallocate the count vector from cmax to amax+1 and return new length + zero new locations +------------------------------------------------------------------------- */ + +int ReadData::reallocate(int **pcount, int cmax, int amax) +{ + int *count = *pcount; + count = (int *) + memory->srealloc(count,(amax+1)*sizeof(int),"read_data:count"); + for (int i = cmax; i <= amax; i++) count[i] = 0; + *pcount = count; + return amax+1; +} + +/* ---------------------------------------------------------------------- + proc 0 opens data file + test if gzipped +------------------------------------------------------------------------- */ + +void ReadData::open(char *file) +{ + int compressed = 0; + char *suffix = file + strlen(file) - 3; + if (suffix > file && strcmp(suffix,".gz") == 0) compressed = 1; + if (!compressed) fp = fopen(file,"r"); + else { +#ifdef GZIP + char gunzip[128]; + sprintf(gunzip,"gunzip -c %s",file); + fp = popen(gunzip,"r"); +#else + error->one("Cannot open gzipped file"); +#endif + } + + if (fp == NULL) { + char str[128]; + sprintf(str,"Cannot open file %s",file); + error->one(str); + } +} + +/* ---------------------------------------------------------------------- + grab next keyword + read lines until one is non-blank + keyword is all text on line w/out leading & trailing white space + read one additional line (assumed blank) + if any read hits EOF, set keyword to empty + if first = 1, line variable holds non-blank line that ended header + if flag = 0, only proc 0 is calling so no bcast + else flag = 1, bcast keyword line to all procs +------------------------------------------------------------------------- */ + +void ReadData::parse_keyword(int first, int flag) +{ + int eof = 0; + + // proc 0 reads upto non-blank line plus 1 following line + // eof is set to 1 if any read hits end-of-file + + if (me == 0) { + if (!first) { + if (fgets(line,MAXLINE,fp) == NULL) eof = 1; + } + while (eof == 0 && strspn(line," \t\n\r") == strlen(line)) { + if (fgets(line,MAXLINE,fp) == NULL) eof = 1; + } + if (fgets(buffer,MAXLINE,fp) == NULL) eof = 1; + } + + // if eof, set keyword empty and return + + if (flag) MPI_Bcast(&eof,1,MPI_INT,0,world); + if (eof) { + keyword[0] = '\0'; + return; + } + + // bcast keyword line to all procs + + if (flag) { + int n; + if (me == 0) n = strlen(line) + 1; + MPI_Bcast(&n,1,MPI_INT,0,world); + MPI_Bcast(line,n,MPI_CHAR,0,world); + } + + // copy non-whitespace portion of line into keyword + + int start = strspn(line," \t\n\r"); + int stop = strlen(line) - 1; + while (line[stop] == ' ' || line[stop] == '\t' + || line[stop] == '\n' || line[stop] == '\r') stop--; + line[stop+1] = '\0'; + strcpy(keyword,&line[start]); +} + +/* ---------------------------------------------------------------------- + proc 0 reads N lines from file +------------------------------------------------------------------------- */ + +void ReadData::skip_lines(int n) +{ + char *eof; + for (int i = 0; i < n; i++) eof = fgets(line,MAXLINE,fp); + if (eof == NULL) error->one("Unexpected end of data file"); +} + +/* ---------------------------------------------------------------------- + parse a line of coeffs into words, storing them in narg,arg + trim anything from '#' onward + word strings remain in line, are not copied + if addflag, duplicate 1st word, so pair_coeff "2" looks like "2 2" +------------------------------------------------------------------------- */ + +void ReadData::parse_coeffs(int addflag, char *line) +{ + char *ptr; + if (ptr = strchr(line,'#')) *ptr = '\0'; + + narg = 0; + char *word = strtok(line," \t\n\r\f"); + while (word) { + if (narg == maxarg) { + maxarg += DELTA; + arg = (char **) + memory->srealloc(arg,maxarg*sizeof(char *),"read_data:arg"); + } + arg[narg++] = word; + if (addflag && narg == 1) continue; + word = strtok(NULL," \t\n\r\f"); + } +} diff --git a/src/read_data.h b/src/read_data.h new file mode 100644 index 0000000000000000000000000000000000000000..b4c3a7176220ee100db542c5b2bfa49ae6e4ff08 --- /dev/null +++ b/src/read_data.h @@ -0,0 +1,58 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef READ_DATA_H +#define READ_DATA_H + +#include "stdio.h" +#include "lammps.h" + +class ReadData : public LAMMPS { + public: + ReadData(); + ~ReadData(); + void command(int, char **); + + private: + int me; + char *line,*keyword,*buffer; + FILE *fp; + int narg,maxarg; + char **arg; + + void open(char *); + void scan(int *, int *, int *, int*); + int reallocate(int **, int, int); + void header(int); + void parse_keyword(int, int); + void skip_lines(int); + void parse_coeffs(int, char *); + + void atoms(); + void velocities(); + void bonds(); + void angles(); + void dihedrals(); + void impropers(); + + void mass(); + void dipole(); + + void paircoeffs(); + void bondcoeffs(); + void anglecoeffs(int); + void dihedralcoeffs(int); + void impropercoeffs(int); +}; + +#endif diff --git a/src/read_restart.cpp b/src/read_restart.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fec777d1837cfbf29e30d1b999dfb54777da790a --- /dev/null +++ b/src/read_restart.cpp @@ -0,0 +1,731 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "string.h" +#include "stdlib.h" +#include "sys/types.h" +#include "dirent.h" +#include "read_restart.h" +#include "atom.h" +#include "domain.h" +#include "comm.h" +#include "update.h" +#include "modify.h" +#include "group.h" +#include "force.h" +#include "pair.h" +#include "bond.h" +#include "angle.h" +#include "dihedral.h" +#include "improper.h" +#include "special.h" +#include "universe.h" +#include "memory.h" +#include "error.h" + +#define AtomInclude +#include "style.h" +#undef AtomInclude + +#define LB_FACTOR 1.1 + +/* ---------------------------------------------------------------------- */ + +void ReadRestart::command(int narg, char **arg) +{ + if (domain->box_exist) + error->all("Cannot read_restart after simulation box is defined"); + + if (narg != 1) error->all("Illegal read_restart command"); + + MPI_Comm_rank(world,&me); + + // if filename contains "*", search dir for latest restart file + + char *file = new char[strlen(arg[0]) + 16]; + if (strchr(arg[0],'*')) file_search(arg[0],file); + else strcpy(file,arg[0]); + + // check if filename contains "%" + + int multiproc; + if (strchr(file,'%')) multiproc = 1; + else multiproc = 0; + + // open single restart file or base file for multiproc case + + if (me == 0) { + if (screen) fprintf(screen,"Reading restart file ...\n"); + char *hfile; + if (multiproc) { + char *hfile = new char[strlen(file) + 16]; + char *ptr = strchr(file,'%'); + *ptr = '\0'; + sprintf(hfile,"%s%s%s",file,"base",ptr+1); + *ptr = '%'; + } else hfile = file; + fp = fopen(hfile,"rb"); + if (fp == NULL) { + char str[128]; + sprintf(str,"Cannot open restart file %s",hfile); + error->one(str); + } + if (multiproc) delete [] hfile; + } + + // read header info and create atom style and simulation box + + header(); + domain->box_exist = 1; + + // problem setup using info from header + + int n; + if (comm->nprocs == 1) n = static_cast<int> (atom->natoms); + else n = static_cast<int> (LB_FACTOR * atom->natoms / comm->nprocs); + + atom->allocate_type_arrays(); + atom->grow(n); + + domain->set_initial_box(); + domain->set_global_box(); + comm->set_procs(); + domain->set_local_box(); + + // read groups, ntype-length arrays, force field, fix info from file + // nextra = max # of extra quantities stored with each atom + + group->read_restart(fp); + if (atom->mass_require) mass(); + if (atom->dipole_require) dipole(); + force_fields(); + + int nextra = modify->read_restart(fp); + atom->nextra_store = nextra; + atom->extra = memory->create_2d_double_array(n,nextra,"atom:extra"); + + // if single file: + // proc 0 reads atoms from file, one chunk per proc (nprocs_file) + // else if one file per proc: + // proc 0 reads chunks from series of files (nprocs_file) + // proc 0 bcasts each chunk to other procs + // each proc unpacks the atoms, saving ones in it's sub-domain + + int maxbuf = 0; + double *buf = NULL; + + double subxlo = domain->subxlo; + double subxhi = domain->subxhi; + double subylo = domain->subylo; + double subyhi = domain->subyhi; + double subzlo = domain->subzlo; + double subzhi = domain->subzhi; + + int m; + double xtmp,ytmp,ztmp; + char *perproc = new char[strlen(file) + 16]; + char *ptr = strchr(file,'%'); + + for (int iproc = 0; iproc < nprocs_file; iproc++) { + if (me == 0) { + if (multiproc) { + fclose(fp); + *ptr = '\0'; + sprintf(perproc,"%s%d%s",file,iproc,ptr+1); + *ptr = '%'; + fp = fopen(perproc,"rb"); + if (fp == NULL) { + char str[128]; + sprintf(str,"Cannot open restart file %s",perproc); + error->one(str); + } + } + fread(&n,sizeof(int),1,fp); + } + + MPI_Bcast(&n,1,MPI_INT,0,world); + if (n > maxbuf) { + maxbuf = n; + delete [] buf; + buf = new double[maxbuf]; + } + + if (me == 0) fread(buf,sizeof(double),n,fp); + MPI_Bcast(buf,n,MPI_DOUBLE,0,world); + + m = 0; + while (m < n) { + xtmp = buf[m+1]; + ytmp = buf[m+2]; + ztmp = buf[m+3]; + if (xtmp >= subxlo && xtmp < subxhi && + ytmp >= subylo && ytmp < subyhi && + ztmp >= subzlo && ztmp < subzhi) + m += atom->unpack_restart(&buf[m]); + else m += static_cast<int> (buf[m]); + } + } + + // close restart file and clean-up memory + + if (me == 0) fclose(fp); + delete [] buf; + delete [] file; + delete [] perproc; + + // check that all atoms were assigned to procs + + double natoms; + double rlocal = atom->nlocal; + MPI_Allreduce(&rlocal,&natoms,1,MPI_DOUBLE,MPI_SUM,world); + + if (me == 0) { + if (screen) fprintf(screen," %.15g atoms\n",natoms); + if (logfile) fprintf(logfile," %.15g atoms\n",natoms); + } + + if (natoms != atom->natoms) error->all("Did not assign all atoms correctly"); + + if (me == 0) { + if (atom->nbonds) { + if (screen) fprintf(screen," %d bonds\n",atom->nbonds); + if (logfile) fprintf(logfile," %d bonds\n",atom->nbonds); + } + if (atom->nangles) { + if (screen) fprintf(screen," %d angles\n",atom->nangles); + if (logfile) fprintf(logfile," %d angles\n",atom->nangles); + } + if (atom->ndihedrals) { + if (screen) fprintf(screen," %d dihedrals\n",atom->ndihedrals); + if (logfile) fprintf(logfile," %d dihedrals\n",atom->ndihedrals); + } + if (atom->nimpropers) { + if (screen) fprintf(screen," %d impropers\n",atom->nimpropers); + if (logfile) fprintf(logfile," %d impropers\n",atom->nimpropers); + } + } + + // check if tags are being used + // create global mapping and bond topology now that system is defined + + int flag = 0; + for (int i = 0; i < atom->nlocal; i++) + if (atom->tag[i] > 0) flag = 1; + int flag_all; + MPI_Allreduce(&flag,&flag_all,1,MPI_INT,MPI_MAX,world); + if (flag_all == 0) atom->tag_enable = 0; + + if (atom->map_style) { + atom->map_init(); + atom->map_set(); + } + if (atom->molecular) { + Special special; + special.build(); + } +} + +/* ---------------------------------------------------------------------- + search for all files in dir matching infile which contains "*" + replace "*" with latest timestep value to create outfile name + if infile also contains "%", need to use "base" when search directory +------------------------------------------------------------------------- */ + +void ReadRestart::file_search(char *infile, char *outfile) +{ + // if filename contains "%" replace "%" with "base" + + int multiproc; + char *pattern = new char[strlen(infile) + 16]; + char *ptr; + + if (ptr = strchr(infile,'%')) { + multiproc = 1; + *ptr = '\0'; + sprintf(pattern,"%s%s%s",infile,"base",ptr+1); + *ptr = '%'; + } else { + multiproc = 0; + strcpy(pattern,infile); + } + + // scan all files in directory, searching for files that match pattern + // maxnum = largest int that matches "*" + + int n = strlen(pattern) + 16; + char *begin = new char[n]; + char *middle = new char[n]; + char *end = new char[n]; + + ptr = strchr(pattern,'*'); + *ptr = '\0'; + strcpy(begin,pattern); + strcpy(end,ptr+1); + int nbegin = strlen(begin); + int maxnum = -1; + + if (me == 0) { + struct dirent *ep; + DIR *dp = opendir("./"); + if (dp == NULL) + error->one("Cannot open dir to search for restart file"); + while (ep = readdir(dp)) { + if (strstr(ep->d_name,begin) != ep->d_name) continue; + if ((ptr = strstr(&ep->d_name[nbegin],end)) == NULL) continue; + if (strlen(end) == 0) ptr = ep->d_name + strlen(ep->d_name); + *ptr = '\0'; + if (strlen(&ep->d_name[nbegin]) < n) { + strcpy(middle,&ep->d_name[nbegin]); + if (atoi(middle) > maxnum) maxnum = atoi(middle); + } + } + closedir(dp); + if (maxnum < 0) error->one("Found no restart file matching pattern"); + } + + delete [] pattern; + delete [] begin; + delete [] middle; + delete [] end; + + // create outfile with maxint substituted for "*" + // use original infile, not pattern, since need to retain "%" in filename + + ptr = strchr(infile,'*'); + *ptr = '\0'; + sprintf(outfile,"%s%d%s",infile,maxnum,ptr+1); + *ptr = '*'; +} + +/* ---------------------------------------------------------------------- + read header of restart file +------------------------------------------------------------------------- */ + +void ReadRestart::header() +{ + int px,py,pz; + int xperiodic,yperiodic,zperiodic; + int boundary[3][2]; + + // read flags and values until flag = -1 + + int flag = read_int(); + while (flag >= 0) { + + // check restart file version, compare to LAMMPS version + + if (flag == 0) { + char *version = read_char(); + if (strcmp(version,universe->version) != 0) { + error->warning("Restart file version does not match LAMMPS version"); + if (screen) fprintf(screen," restart file = %s, LAMMPS = %s\n", + version,universe->version); + } + delete [] version; + + // reset unit_style only if different + // so that timestep,neighbor-skin are not changed + + } else if (flag == 1) { + char *style = read_char(); + if (strcmp(style,update->unit_style) != 0 && me == 0) + error->warning("Resetting unit_style to restart file value"); + if (strcmp(style,update->unit_style) != 0) update->set_units(style); + delete [] style; + + } else if (flag == 2) { + update->ntimestep = read_int(); + + // set dimension from restart file, warn if different + + } else if (flag == 3) { + int dimension = read_int(); + if (dimension != force->dimension && me == 0) + error->warning("Resetting dimension to restart file value"); + force->dimension = dimension; + if (force->dimension == 2 && domain->zperiodic == 0) + error->all("Cannot run 2d simulation with nonperiodic Z dimension"); + + // read nprocs_file from restart file, warn if different + + } else if (flag == 4) { + nprocs_file = read_int(); + if (nprocs_file != comm->nprocs && me == 0) + error->warning("Restart file used different # of processors"); + + // don't set procgrid, warn if different + + } else if (flag == 5) { + px = read_int(); + } else if (flag == 6) { + py = read_int(); + } else if (flag == 7) { + pz = read_int(); + if (comm->user_procgrid[0] != 0 && + (px != comm->user_procgrid[0] || py != comm->user_procgrid[1] || + pz != comm->user_procgrid[2]) && me == 0) + error->warning("Restart file used different 3d processor grid"); + + // don't set newton_pair, warn if different + // set newton_bond from restart file, warn if different + + } else if (flag == 8) { + int newton_pair = read_int(); + if (newton_pair != force->newton_pair && me == 0) + error->warning("Restart file used different newton pair setting"); + } else if (flag == 9) { + int newton_bond = read_int(); + if (newton_bond != force->newton_bond && me == 0) + error->warning("Resetting newton bond to restart file value"); + force->newton_bond = newton_bond; + if (force->newton_pair || force->newton_bond) force->newton = 1; + else force->newton = 0; + + // set boundary settings from restart file, warn if different + + } else if (flag == 10) { + xperiodic = read_int(); + } else if (flag == 11) { + yperiodic = read_int(); + } else if (flag == 12) { + zperiodic = read_int(); + } else if (flag == 13) { + boundary[0][0] = read_int(); + } else if (flag == 14) { + boundary[0][1] = read_int(); + } else if (flag == 15) { + boundary[1][0] = read_int(); + } else if (flag == 16) { + boundary[1][1] = read_int(); + } else if (flag == 17) { + boundary[2][0] = read_int(); + } else if (flag == 18) { + boundary[2][1] = read_int(); + + int flag = 0; + if ((xperiodic != domain->xperiodic || yperiodic != domain->yperiodic || + zperiodic != domain->zperiodic)) flag = 1; + if (boundary[0][0] != domain->boundary[0][0] || + boundary[0][1] != domain->boundary[0][1] || + boundary[1][0] != domain->boundary[1][0] || + boundary[1][1] != domain->boundary[1][1] || + boundary[2][0] != domain->boundary[2][0] || + boundary[2][1] != domain->boundary[2][1]) flag = 1; + if (flag && me == 0) + error->warning("Resetting boundary settings to restart file values"); + + domain->xperiodic = xperiodic; + domain->yperiodic = yperiodic; + domain->zperiodic = zperiodic; + domain->boundary[0][0] = boundary[0][0]; + domain->boundary[0][1] = boundary[0][1]; + domain->boundary[1][0] = boundary[1][0]; + domain->boundary[1][1] = boundary[1][1]; + domain->boundary[2][0] = boundary[2][0]; + domain->boundary[2][1] = boundary[2][1]; + + domain->nonperiodic = 0; + if (xperiodic == 0 || yperiodic == 0 || zperiodic == 0) { + domain->nonperiodic = 1; + if (boundary[0][0] >= 2 || boundary[0][1] >= 2 || + boundary[1][0] >= 2 || boundary[1][1] >= 2 || + boundary[2][0] >= 2 || boundary[2][1] >= 2) + domain->nonperiodic = 2; + } + + // create atom class + // if style = hybrid, read additional sub-class arguments + + } else if (flag == 19) { + char *style = read_char(); + + int nwords,n; + char **words; + + if (strcmp(style,"hybrid") != 0) { + nwords = 1; + words = new char*[nwords]; + words[0] = style; + } else { + if (me == 0) fread(&nwords,sizeof(int),1,fp); + MPI_Bcast(&nwords,1,MPI_INT,0,world); + nwords++; + words = new char*[nwords]; + words[0] = style; + for (int i = 1; i < nwords; i++) { + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + words[i] = new char[n]; + if (me == 0) fread(words[i],sizeof(char),n,fp); + MPI_Bcast(words[i],n,MPI_CHAR,0,world); + } + } + + Atom *old = atom; + + if (0) return; // dummy line to enable else-if macro expansion + +#define AtomClass +#define AtomStyle(key,Class) \ + else if (strcmp(style,#key) == 0) atom = new Class(nwords,words); +#include "style.h" +#undef AtomClass + + else error->all("Unknown atom style in restart file"); + + if (old) { + atom->settings(old); + delete old; + } + + for (int i = 0; i < nwords; i++) delete [] words[i]; + delete [] words; + + } else if (flag == 20) { + atom->natoms = read_double(); + } else if (flag == 21) { + atom->ntypes = read_int(); + } else if (flag == 22) { + atom->nbonds = read_int(); + } else if (flag == 23) { + atom->nbondtypes = read_int(); + } else if (flag == 24) { + atom->bond_per_atom = read_int(); + } else if (flag == 25) { + atom->nangles = read_int(); + } else if (flag == 26) { + atom->nangletypes = read_int(); + } else if (flag == 27) { + atom->angle_per_atom = read_int(); + } else if (flag == 28) { + atom->ndihedrals = read_int(); + } else if (flag == 29) { + atom->ndihedraltypes = read_int(); + } else if (flag == 30) { + atom->dihedral_per_atom = read_int(); + } else if (flag == 31) { + atom->nimpropers = read_int(); + } else if (flag == 32) { + atom->nimpropertypes = read_int(); + } else if (flag == 33) { + atom->improper_per_atom = read_int(); + + } else if (flag == 34) { + domain->boxxlo = read_double(); + } else if (flag == 35) { + domain->boxxhi = read_double(); + } else if (flag == 36) { + domain->boxylo = read_double(); + } else if (flag == 37) { + domain->boxyhi = read_double(); + } else if (flag == 38) { + domain->boxzlo = read_double(); + } else if (flag == 39) { + domain->boxzhi = read_double(); + + } else if (flag == 40) { + force->special_lj[1] = read_double(); + } else if (flag == 41) { + force->special_lj[2] = read_double(); + } else if (flag == 42) { + force->special_lj[3] = read_double(); + } else if (flag == 43) { + force->special_coul[1] = read_double(); + } else if (flag == 44) { + force->special_coul[2] = read_double(); + } else if (flag == 45) { + force->special_coul[3] = read_double(); + + } else error->all("Invalid flag in header of restart file"); + + flag = read_int(); + } +} + +/* ---------------------------------------------------------------------- */ + +void ReadRestart::mass() +{ + double *mass = new double[atom->ntypes+1]; + if (me == 0) fread(&mass[1],sizeof(double),atom->ntypes,fp); + MPI_Bcast(&mass[1],atom->ntypes,MPI_DOUBLE,0,world); + atom->set_mass(mass); + delete [] mass; +} + +/* ---------------------------------------------------------------------- */ + +void ReadRestart::dipole() +{ + double *dipole = new double[atom->ntypes+1]; + if (me == 0) fread(&dipole[1],sizeof(double),atom->ntypes,fp); + MPI_Bcast(&dipole[1],atom->ntypes,MPI_DOUBLE,0,world); + atom->set_dipole(dipole); + delete [] dipole; +} + +/* ---------------------------------------------------------------------- */ + +void ReadRestart::force_fields() +{ + int n; + char *style; + + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + if (n) { + style = (char *) memory->smalloc(n*sizeof(char),"read_restart:style"); + if (me == 0) fread(style,sizeof(char),n,fp); + MPI_Bcast(style,n,MPI_CHAR,0,world); + + if (force->pair == NULL || strcmp(style,force->pair_style)) { + if (force->pair) { + if (me == 0) + error->warning("Resetting pair_style to restart file value"); + delete force->pair; + } + force->create_pair(style); + } + + memory->sfree(style); + force->pair->read_restart(fp); + } else force->create_pair("none"); + + if (atom->bonds_allow) { + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + if (n) { + style = (char *) memory->smalloc(n*sizeof(char),"read_restart:style"); + if (me == 0) fread(style,sizeof(char),n,fp); + MPI_Bcast(style,n,MPI_CHAR,0,world); + + if (force->bond == NULL || strcmp(style,force->bond_style)) { + if (force->bond) { + if (me == 0) + error->warning("Resetting bond_style to restart file value"); + delete force->bond; + } + force->create_bond(style); + } + + memory->sfree(style); + force->bond->read_restart(fp); + } else force->create_bond("none"); + } + + if (atom->angles_allow) { + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + if (n) { + style = (char *) memory->smalloc(n*sizeof(char),"read_restart:style"); + if (me == 0) fread(style,sizeof(char),n,fp); + MPI_Bcast(style,n,MPI_CHAR,0,world); + + if (force->angle == NULL || strcmp(style,force->angle_style)) { + if (force->angle) { + if (me == 0) + error->warning("Resetting angle_style to restart file value"); + delete force->angle; + } + force->create_angle(style); + } + + memory->sfree(style); + force->angle->read_restart(fp); + } else force->create_angle("none"); + } + + if (atom->dihedrals_allow) { + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + if (n) { + style = (char *) memory->smalloc(n*sizeof(char),"read_restart:style"); + if (me == 0) fread(style,sizeof(char),n,fp); + MPI_Bcast(style,n,MPI_CHAR,0,world); + + if (force->dihedral == NULL || strcmp(style,force->dihedral_style)) { + if (force->dihedral) { + if (me == 0) + error->warning("Resetting dihedral_style to restart file value"); + delete force->dihedral; + } + force->create_dihedral(style); + } + + memory->sfree(style); + force->dihedral->read_restart(fp); + } else force->create_dihedral("none"); + } + + if (atom->impropers_allow) { + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + if (n) { + style = (char *) memory->smalloc(n*sizeof(char),"read_restart:style"); + if (me == 0) fread(style,sizeof(char),n,fp); + MPI_Bcast(style,n,MPI_CHAR,0,world); + + if (force->improper == NULL || strcmp(style,force->improper_style)) { + if (force->improper) { + if (me == 0) + error->warning("Resetting improper_style to restart file value"); + delete force->improper; + } + force->create_improper(style); + } + + memory->sfree(style); + force->improper->read_restart(fp); + } else force->create_improper("none"); + } +} + +/* ---------------------------------------------------------------------- + read an int from restart file +------------------------------------------------------------------------- */ + +int ReadRestart::read_int() +{ + int value; + if (me == 0) fread(&value,sizeof(int),1,fp); + MPI_Bcast(&value,1,MPI_INT,0,world); + return value; +} + +/* ---------------------------------------------------------------------- + read a double from restart file +------------------------------------------------------------------------- */ + +double ReadRestart::read_double() +{ + double value; + if (me == 0) fread(&value,sizeof(double),1,fp); + MPI_Bcast(&value,1,MPI_DOUBLE,0,world); + return value; +} + +/* ---------------------------------------------------------------------- + read a char str from restart file +------------------------------------------------------------------------- */ + +char *ReadRestart::read_char() +{ + int n; + if (me == 0) fread(&n,sizeof(int),1,fp); + MPI_Bcast(&n,1,MPI_INT,0,world); + char *value = new char[n]; + if (me == 0) fread(value,sizeof(char),n,fp); + MPI_Bcast(value,n,MPI_CHAR,0,world); + return value; +} diff --git a/src/read_restart.h b/src/read_restart.h new file mode 100644 index 0000000000000000000000000000000000000000..69d5162b4bd92da6679307ef7fe92719e660de18 --- /dev/null +++ b/src/read_restart.h @@ -0,0 +1,42 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef READ_RESTART_H +#define READ_RESTART_H + +#include "stdio.h" +#include "lammps.h" + +class ReadRestart : public LAMMPS { + public: + ReadRestart() {} + ~ReadRestart() {} + void command(int, char **); + + private: + int me; + FILE *fp; + int nprocs_file; + + void file_search(char *, char *); + void header(); + void mass(); + void dipole(); + void force_fields(); + + int read_int(); + double read_double(); + char *read_char(); +}; + +#endif diff --git a/src/region.cpp b/src/region.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b07cb6544907503d60ee48cf53c1946aa3e4f7e5 --- /dev/null +++ b/src/region.cpp @@ -0,0 +1,99 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "string.h" +#include "region.h" +#include "domain.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +Region::Region(int narg, char **arg) +{ + // region ID + + int n = strlen(arg[0]) + 1; + id = new char[n]; + strcpy(id,arg[0]); + + // set option defaults + + interior = 1; + scaleflag = 1; + + // read style and options from end of input line + + n = strlen(arg[1]) + 1; + style = new char[n]; + strcpy(style,arg[1]); + + if (strcmp(style,"block") == 0) options(narg-8,&arg[8]); + else if (strcmp(style,"sphere") == 0) options(narg-6,&arg[6]); + else if (strcmp(arg[1],"cylinder") == 0) options(narg-8,&arg[8]); + else if (strcmp(arg[1],"union") == 0) { + if (narg < 5) error->all("Illegal region command"); + n = atoi(arg[2]); + options(narg-(n+3),&arg[n+3]); + } else if (strcmp(arg[1],"intersect") == 0) { + if (narg < 5) error->all("Illegal region command"); + n = atoi(arg[2]); + options(narg-(n+3),&arg[n+3]); + } else error->all("Illegal region command"); + + // setup scaling + + if (scaleflag && strcmp(domain->lattice_style,"none") == 0) + error->all("Use of region with undefined lattice"); + + if (scaleflag) { + xscale = domain->xlattice; + yscale = domain->ylattice; + zscale = domain->zlattice; + } + else xscale = yscale = zscale = 1.0; +} + +/* ---------------------------------------------------------------------- */ + +Region::~Region() +{ + delete [] id; + delete [] style; +} + +/* ---------------------------------------------------------------------- + parse optional parameters at end of region input line +------------------------------------------------------------------------- */ + +void Region::options(int narg, char **arg) +{ + if (narg < 0) error->all("Illegal region command"); + + int iarg = 0; + while (iarg < narg) { + if (strcmp(arg[iarg],"units") == 0) { + if (iarg+2 > narg) error->all("Illegal region command"); + if (strcmp(arg[iarg+1],"box") == 0) scaleflag = 0; + else if (strcmp(arg[iarg+1],"lattice") == 0) scaleflag = 1; + else error->all("Illegal region command"); + iarg += 2; + } else if (strcmp(arg[iarg],"side") == 0) { + if (iarg+2 > narg) error->all("Illegal region command"); + if (strcmp(arg[iarg+1],"in") == 0) interior = 1; + else if (strcmp(arg[iarg+1],"out") == 0) interior = 0; + else error->all("Illegal region command"); + iarg += 2; + } else error->all("Illegal region command"); + } +} diff --git a/src/region.h b/src/region.h new file mode 100644 index 0000000000000000000000000000000000000000..38e68c6189b3e2a3017ac9fcb0071ebca945c9e1 --- /dev/null +++ b/src/region.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef REGION_H +#define REGION_H + +#include "lammps.h" + +class Region : public LAMMPS { + public: + char *id,*style; + int interior; // 1 for interior, 0 for exterior + int scaleflag; // 1 for lattice, 0 for box + double xscale,yscale,zscale; // scale factors for box/lattice units + double extent_xlo,extent_xhi; // bounding box on region + double extent_ylo,extent_yhi; + double extent_zlo,extent_zhi; + + Region(int, char **); + virtual ~Region(); + virtual int match(double, double, double) = 0; + + private: + void options(int, char **); +}; + +#endif diff --git a/src/region_block.cpp b/src/region_block.cpp new file mode 100644 index 0000000000000000000000000000000000000000..017c8d3b17122afc999e8d2fd39478ab43234881 --- /dev/null +++ b/src/region_block.cpp @@ -0,0 +1,85 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "string.h" +#include "region_block.h" +#include "domain.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +RegBlock::RegBlock(int narg, char **arg) : Region(narg, arg) +{ + if (strcmp(arg[2],"INF") == 0) { + if (domain->box_exist == 0) + error->all("Cannot use region INF when box does not exist"); + xlo = domain->boxxlo; + } else xlo = xscale*atof(arg[2]); + + if (strcmp(arg[3],"INF") == 0) { + if (domain->box_exist == 0) + error->all("Cannot use region INF when box does not exist"); + xhi = domain->boxxhi; + } else xhi = xscale*atof(arg[3]); + + if (strcmp(arg[4],"INF") == 0) { + if (domain->box_exist == 0) + error->all("Cannot use region INF when box does not exist"); + ylo = domain->boxylo; + } else ylo = yscale*atof(arg[4]); + + if (strcmp(arg[5],"INF") == 0) { + if (domain->box_exist == 0) + error->all("Cannot use region INF when box does not exist"); + yhi = domain->boxyhi; + } else yhi = yscale*atof(arg[5]); + + if (strcmp(arg[6],"INF") == 0) { + if (domain->box_exist == 0) + error->all("Cannot use region INF when box does not exist"); + zlo = domain->boxzlo; + } else zlo = zscale*atof(arg[6]); + + if (strcmp(arg[7],"INF") == 0) { + if (domain->box_exist == 0) + error->all("Cannot use region INF when box does not exist"); + zhi = domain->boxzhi; + } else zhi = zscale*atof(arg[7]); + + // error check + + if (xlo > xhi || ylo > yhi || zlo > zhi) + error->all("Illegal region block command"); + + // extent of block + + extent_xlo = xlo; + extent_xhi = xhi; + extent_ylo = ylo; + extent_yhi = yhi; + extent_zlo = zlo; + extent_zhi = zhi; +} + +/* ---------------------------------------------------------------------- */ + +int RegBlock::match(double x, double y, double z) +{ + int inside; + if (x >= xlo && x <= xhi && y >= ylo && y <= yhi && z >= zlo && z <= zhi) + inside = 1; + else inside = 0; + + return !(inside ^ interior); // 1 if same, 0 if different +} diff --git a/src/region_block.h b/src/region_block.h new file mode 100644 index 0000000000000000000000000000000000000000..056a2e65d6cccc2ff0875f765fb78cbab3ea1ec6 --- /dev/null +++ b/src/region_block.h @@ -0,0 +1,31 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef REGION_BLOCK_H +#define REGION_BLOCK_H + +#include "region.h" + +class RegBlock : public Region { + friend class FixInsert; + + public: + RegBlock(int, char **); + ~RegBlock() {} + int match(double, double, double); + + private: + double xlo,xhi,ylo,yhi,zlo,zhi; +}; + +#endif diff --git a/src/region_cylinder.cpp b/src/region_cylinder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5fbc6bef445f8efb93f3f22e9a3e76528ff8e014 --- /dev/null +++ b/src/region_cylinder.cpp @@ -0,0 +1,127 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "region_cylinder.h" +#include "domain.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +RegCylinder::RegCylinder(int narg, char **arg) : Region(narg, arg) +{ + if (strcmp(arg[2],"x") && strcmp(arg[2],"y") && strcmp(arg[2],"z")) + error->all("Illegal region cylinder command"); + axis = arg[2][0]; + + if (axis == 'x') { + c1 = yscale*atof(arg[3]); + c2 = zscale*atof(arg[4]); + } else if (axis == 'y') { + c1 = xscale*atof(arg[3]); + c2 = zscale*atof(arg[4]); + } else if (axis == 'z') { + c1 = xscale*atof(arg[3]); + c2 = yscale*atof(arg[4]); + } + radius = xscale*atof(arg[5]); + + if (strcmp(arg[6],"INF") == 0) { + if (domain->box_exist == 0) + error->all("Cannot use region INF when box does not exist"); + if (axis == 'x') lo = domain->boxxlo; + if (axis == 'y') lo = domain->boxylo; + if (axis == 'z') lo = domain->boxzlo; + } else { + if (axis == 'x') lo = xscale*atof(arg[6]); + if (axis == 'y') lo = yscale*atof(arg[6]); + if (axis == 'z') lo = zscale*atof(arg[6]); + } + + if (strcmp(arg[7],"INF") == 0) { + if (domain->box_exist == 0) + error->all("Cannot use region INF when box does not exist"); + if (axis == 'x') hi = domain->boxxhi; + if (axis == 'y') hi = domain->boxyhi; + if (axis == 'z') hi = domain->boxzhi; + } else { + if (axis == 'x') hi = xscale*atof(arg[7]); + if (axis == 'y') hi = yscale*atof(arg[7]); + if (axis == 'z') hi = zscale*atof(arg[7]); + } + + // error check + + if (radius < 0.0) error->all("Illegal region cylinder command"); + + // extent of cylinder + + if (axis == 'x') { + extent_xlo = lo; + extent_xhi = hi; + extent_ylo = c1 - radius; + extent_yhi = c1 + radius; + extent_zlo = c2 - radius; + extent_zhi = c2 + radius; + } + if (axis == 'y') { + extent_xlo = c1 - radius; + extent_xhi = c1 + radius; + extent_ylo = lo; + extent_yhi = hi; + extent_zlo = c2 - radius; + extent_zhi = c2 + radius; + } + if (axis == 'z') { + extent_xlo = c1 - radius; + extent_xhi = c1 + radius; + extent_ylo = c2 - radius; + extent_yhi = c2 + radius; + extent_zlo = lo; + extent_zhi = hi; + } +} + +/* ---------------------------------------------------------------------- */ + +int RegCylinder::match(double x, double y, double z) +{ + double del1,del2,dist; + int inside; + + if (axis == 'x') { + del1 = y - c1; + del2 = z - c2; + dist = sqrt(del1*del1 + del2*del2); + if (dist <= radius && x >= lo && x <= hi) inside = 1; + else inside = 0; + } + if (axis == 'y') { + del1 = x - c1; + del2 = z - c2; + dist = sqrt(del1*del1 + del2*del2); + if (dist <= radius && y >= lo && y <= hi) inside = 1; + else inside = 0; + } + if (axis == 'z') { + del1 = x - c1; + del2 = y - c2; + dist = sqrt(del1*del1 + del2*del2); + if (dist <= radius && z >= lo && z <= hi) inside = 1; + else inside = 0; + } + + return !(inside ^ interior); // 1 if same, 0 if different +} diff --git a/src/region_cylinder.h b/src/region_cylinder.h new file mode 100644 index 0000000000000000000000000000000000000000..0355f4c998d0693a98f5eca14e9798306dbe310e --- /dev/null +++ b/src/region_cylinder.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef REGION_CYLINDER_H +#define REGION_CYLINDER_H + +#include "region.h" + +class RegCylinder : public Region { + friend class FixInsert; + + public: + RegCylinder(int, char **); + ~RegCylinder() {} + int match(double, double, double); + + private: + char axis; + double c1,c2; + double radius; + double lo,hi; +}; + +#endif diff --git a/src/region_intersect.cpp b/src/region_intersect.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f8de6c909b0394a21400eb204b989e3701720b2f --- /dev/null +++ b/src/region_intersect.cpp @@ -0,0 +1,84 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "string.h" +#include "region_intersect.h" +#include "domain.h" +#include "error.h" + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +/* ---------------------------------------------------------------------- */ + +RegIntersect::RegIntersect(int narg, char **arg) : Region(narg, arg) +{ + // build list of regions to intersect + + int n = atoi(arg[2]); + list = new int[n]; + nregion = 0; + + int iregion; + for (int iarg = 0; iarg < n; iarg++) { + for (iregion = 0; iregion < domain->nregion; iregion++) + if (strcmp(arg[iarg+3],domain->regions[iregion]->id) == 0) break; + if (iregion == domain->nregion) + error->all("Region union region ID does not exist"); + list[nregion++] = iregion; + } + + // extent of intersection of regions + + Region **regions = domain->regions; + + extent_xlo = regions[list[0]]->extent_xlo; + extent_ylo = regions[list[0]]->extent_ylo; + extent_zlo = regions[list[0]]->extent_zlo; + extent_xhi = regions[list[0]]->extent_xhi; + extent_yhi = regions[list[0]]->extent_yhi; + extent_zhi = regions[list[0]]->extent_zhi; + + for (int ilist = 1; ilist < nregion; ilist++) { + extent_xlo = MAX(extent_xlo,regions[list[ilist]]->extent_xlo); + extent_ylo = MAX(extent_ylo,regions[list[ilist]]->extent_ylo); + extent_zlo = MAX(extent_zlo,regions[list[ilist]]->extent_zlo); + extent_xhi = MIN(extent_xhi,regions[list[ilist]]->extent_xhi); + extent_yhi = MIN(extent_yhi,regions[list[ilist]]->extent_yhi); + extent_zhi = MIN(extent_zhi,regions[list[ilist]]->extent_zhi); + } +} + +/* ---------------------------------------------------------------------- */ + +RegIntersect::~RegIntersect() +{ + delete [] list; +} + +/* ---------------------------------------------------------------------- */ + +int RegIntersect::match(double x, double y, double z) +{ + int ilist; + Region **regions = domain->regions; + for (ilist = 0; ilist < nregion; ilist++) + if (!regions[list[ilist]]->match(x,y,z)) break; + + int inside; // inside if matched all regions + if (ilist == nregion) inside = 1; + else inside = 0; + + return !(inside ^ interior); // 1 if same, 0 if different +} diff --git a/src/region_intersect.h b/src/region_intersect.h new file mode 100644 index 0000000000000000000000000000000000000000..df775bca2993384431ba507733aa2e37f6a7e199 --- /dev/null +++ b/src/region_intersect.h @@ -0,0 +1,30 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef REGION_INTERSECT_H +#define REGION_INTERSECT_H + +#include "region.h" + +class RegIntersect : public Region { + public: + RegIntersect(int, char **); + ~RegIntersect(); + int match(double, double, double); + + private: + int nregion; + int *list; +}; + +#endif diff --git a/src/region_sphere.cpp b/src/region_sphere.cpp new file mode 100644 index 0000000000000000000000000000000000000000..91fcb1460a4ac19d8c912c71ef80f558eb77a0c0 --- /dev/null +++ b/src/region_sphere.cpp @@ -0,0 +1,57 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "region_sphere.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +RegSphere::RegSphere(int narg, char **arg) : Region(narg, arg) +{ + xc = xscale*atof(arg[2]); + yc = yscale*atof(arg[3]); + zc = zscale*atof(arg[4]); + radius = xscale*atof(arg[5]); + + // error check + + if (radius < 0.0) error->all("Illegal region sphere command"); + + // extent of sphere + + extent_xlo = xc - radius; + extent_xhi = xc + radius; + extent_ylo = yc - radius; + extent_yhi = yc + radius; + extent_zlo = zc - radius; + extent_zhi = zc + radius; +} + +/* ---------------------------------------------------------------------- */ + +int RegSphere::match(double x, double y, double z) +{ + double delx = x - xc; + double dely = y - yc; + double delz = z - zc; + double dist = sqrt(delx*delx + dely*dely + delz*delz); + + int inside; + if (dist <= radius) inside = 1; + else inside = 0; + + return !(inside ^ interior); // 1 if same, 0 if different +} diff --git a/src/region_sphere.h b/src/region_sphere.h new file mode 100644 index 0000000000000000000000000000000000000000..90ac095b2221e7f505f6442a05aef6ff0b750953 --- /dev/null +++ b/src/region_sphere.h @@ -0,0 +1,30 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef REGION_SPHERE_H +#define REGION_SPHERE_H + +#include "region.h" + +class RegSphere : public Region { + public: + RegSphere(int, char **); + ~RegSphere() {} + int match(double, double, double); + + private: + double xc,yc,zc; + double radius; +}; + +#endif diff --git a/src/region_union.cpp b/src/region_union.cpp new file mode 100644 index 0000000000000000000000000000000000000000..03f0e83e41f8a7813bf9769a6b84656653acdbe4 --- /dev/null +++ b/src/region_union.cpp @@ -0,0 +1,82 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "string.h" +#include "region_union.h" +#include "domain.h" +#include "error.h" + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +#define BIG 1.0e20 + +/* ---------------------------------------------------------------------- */ + +RegUnion::RegUnion(int narg, char **arg) : Region(narg, arg) +{ + // build list of regions to union + + int n = atoi(arg[2]); + list = new int[n]; + nregion = 0; + + int iregion; + for (int iarg = 0; iarg < n; iarg++) { + for (iregion = 0; iregion < domain->nregion; iregion++) + if (strcmp(arg[iarg+3],domain->regions[iregion]->id) == 0) break; + if (iregion == domain->nregion) + error->all("Region union region ID does not exist"); + list[nregion++] = iregion; + } + + // extent of union of regions + + Region **regions = domain->regions; + + extent_xlo = extent_ylo = extent_zlo = BIG; + extent_xhi = extent_yhi = extent_zhi = -BIG; + + for (int ilist = 0; ilist < nregion; ilist++) { + extent_xlo = MIN(extent_xlo,regions[list[ilist]]->extent_xlo); + extent_ylo = MIN(extent_ylo,regions[list[ilist]]->extent_ylo); + extent_zlo = MIN(extent_zlo,regions[list[ilist]]->extent_zlo); + extent_xhi = MAX(extent_xhi,regions[list[ilist]]->extent_xhi); + extent_yhi = MAX(extent_yhi,regions[list[ilist]]->extent_yhi); + extent_zhi = MAX(extent_zhi,regions[list[ilist]]->extent_zhi); + } +} + +/* ---------------------------------------------------------------------- */ + +RegUnion::~RegUnion() +{ + delete [] list; +} + +/* ---------------------------------------------------------------------- */ + +int RegUnion::match(double x, double y, double z) +{ + int ilist; + Region **regions = domain->regions; + for (ilist = 0; ilist < nregion; ilist++) + if (regions[list[ilist]]->match(x,y,z)) break; + + int inside; // inside if matched any region + if (ilist == nregion) inside = 0; + else inside = 1; + + return !(inside ^ interior); // 1 if same, 0 if different +} diff --git a/src/region_union.h b/src/region_union.h new file mode 100644 index 0000000000000000000000000000000000000000..0a2dbe8631ea918b572a2c2c1b911d6f4806090f --- /dev/null +++ b/src/region_union.h @@ -0,0 +1,30 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef REGION_UNION_H +#define REGION_UNION_H + +#include "region.h" + +class RegUnion : public Region { + public: + RegUnion(int, char **); + ~RegUnion(); + int match(double, double, double); + + private: + int nregion; + int *list; +}; + +#endif diff --git a/src/remap.cpp b/src/remap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..215dc5b6d392942903de59611a2a98b3000b5437 --- /dev/null +++ b/src/remap.cpp @@ -0,0 +1,506 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "stdio.h" +#include "stdlib.h" +#include "remap.h" +#include "pack.h" + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +/* ---------------------------------------------------------------------- + Data layout for 3d remaps: + + data set of Nfast x Nmid x Nslow elements is owned by P procs + each element = nqty contiguous datums + on input, each proc owns a subsection of the elements + on output, each proc will own a (presumably different) subsection + my subsection must not overlap with any other proc's subsection, + i.e. the union of all proc's input (or output) subsections must + exactly tile the global Nfast x Nmid x Nslow data set + when called from C, all subsection indices are + C-style from 0 to N-1 where N = Nfast or Nmid or Nslow + when called from F77, all subsection indices are + F77-style from 1 to N where N = Nfast or Nmid or Nslow + a proc can own 0 elements on input or output + by specifying hi index < lo index + on both input and output, data is stored contiguously on a processor + with a fast-varying, mid-varying, and slow-varying index +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Perform 3d remap + + Arguments: + in starting address of input data on this proc + out starting address of where output data for this proc + will be placed (can be same as in) + buf extra memory required for remap + if memory=0 was used in call to remap_3d_create_plan + then buf must be big enough to hold output result + i.e. nqty * (out_ihi-out_ilo+1) * (out_jhi-out_jlo+1) * + (out_khi-out_klo+1) + if memory=1 was used in call to remap_3d_create_plan + then buf is not used, can just be a dummy pointer + plan plan returned by previous call to remap_3d_create_plan +------------------------------------------------------------------------- */ + +void remap_3d(double *in, double *out, double *buf, + struct remap_plan_3d *plan) + +{ + MPI_Status status; + int i,isend,irecv; + double *scratch; + + if (plan->memory == 0) + scratch = buf; + else + scratch = plan->scratch; + + // post all recvs into scratch space + + for (irecv = 0; irecv < plan->nrecv; irecv++) + MPI_Irecv(&scratch[plan->recv_bufloc[irecv]],plan->recv_size[irecv], + MPI_DOUBLE,plan->recv_proc[irecv],0, + plan->comm,&plan->request[irecv]); + + // send all messages to other procs + + for (isend = 0; isend < plan->nsend; isend++) { + plan->pack(&in[plan->send_offset[isend]], + plan->sendbuf,&plan->packplan[isend]); + MPI_Send(plan->sendbuf,plan->send_size[isend],MPI_DOUBLE, + plan->send_proc[isend],0,plan->comm); + } + + // copy in -> scratch -> out for self data + + if (plan->self) { + isend = plan->nsend; + irecv = plan->nrecv; + plan->pack(&in[plan->send_offset[isend]], + &scratch[plan->recv_bufloc[irecv]], + &plan->packplan[isend]); + plan->unpack(&scratch[plan->recv_bufloc[irecv]], + &out[plan->recv_offset[irecv]],&plan->unpackplan[irecv]); + } + + // unpack all messages from scratch -> out + + for (i = 0; i < plan->nrecv; i++) { + MPI_Waitany(plan->nrecv,plan->request,&irecv,&status); + plan->unpack(&scratch[plan->recv_bufloc[irecv]], + &out[plan->recv_offset[irecv]],&plan->unpackplan[irecv]); + } +} + +/* ---------------------------------------------------------------------- + Create plan for performing a 3d remap + + Arguments: + comm MPI communicator for the P procs which own the data + in_ilo,in_ihi input bounds of data I own in fast index + in_jlo,in_jhi input bounds of data I own in mid index + in_klo,in_khi input bounds of data I own in slow index + out_ilo,out_ihi output bounds of data I own in fast index + out_jlo,out_jhi output bounds of data I own in mid index + out_klo,out_khi output bounds of data I own in slow index + nqty # of datums per element + permute permutation in storage order of indices on output + 0 = no permutation + 1 = permute once = mid->fast, slow->mid, fast->slow + 2 = permute twice = slow->fast, fast->mid, mid->slow + memory user provides buffer memory for remap or system does + 0 = user provides memory + 1 = system provides memory + precision precision of data + 1 = single precision (4 bytes per datum) + 2 = double precision (8 bytes per datum) +------------------------------------------------------------------------- */ + +struct remap_plan_3d *remap_3d_create_plan( + MPI_Comm comm, + int in_ilo, int in_ihi, int in_jlo, int in_jhi, + int in_klo, int in_khi, + int out_ilo, int out_ihi, int out_jlo, int out_jhi, + int out_klo, int out_khi, + int nqty, int permute, int memory, int precision) + +{ + struct remap_plan_3d *plan; + struct extent_3d *array; + struct extent_3d in,out,overlap; + int i,iproc,nsend,nrecv,ibuf,size,me,nprocs; + + // query MPI info + + MPI_Comm_rank(comm,&me); + MPI_Comm_size(comm,&nprocs); + + // single precision not yet supported + + if (precision == 1) { + if (me == 0) printf("Single precision not supported\n"); + return NULL; + } + + // allocate memory for plan data struct + + plan = (struct remap_plan_3d *) malloc(sizeof(struct remap_plan_3d)); + if (plan == NULL) return NULL; + + // store parameters in local data structs + + in.ilo = in_ilo; + in.ihi = in_ihi; + in.isize = in.ihi - in.ilo + 1; + + in.jlo = in_jlo; + in.jhi = in_jhi; + in.jsize = in.jhi - in.jlo + 1; + + in.klo = in_klo; + in.khi = in_khi; + in.ksize = in.khi - in.klo + 1; + + out.ilo = out_ilo; + out.ihi = out_ihi; + out.isize = out.ihi - out.ilo + 1; + + out.jlo = out_jlo; + out.jhi = out_jhi; + out.jsize = out.jhi - out.jlo + 1; + + out.klo = out_klo; + out.khi = out_khi; + out.ksize = out.khi - out.klo + 1; + + // combine output extents across all procs + + array = (struct extent_3d *) malloc(nprocs*sizeof(struct extent_3d)); + if (array == NULL) return NULL; + + MPI_Allgather(&out,sizeof(struct extent_3d),MPI_BYTE, + array,sizeof(struct extent_3d),MPI_BYTE,comm); + + // count send collides, including self + + nsend = 0; + iproc = me; + for (i = 0; i < nprocs; i++) { + iproc++; + if (iproc == nprocs) iproc = 0; + nsend += remap_3d_collide(&in,&array[iproc],&overlap); + } + + // malloc space for send info + + if (nsend) { + if (precision == 1) + plan->pack = NULL; + else + plan->pack = pack_3d; + + plan->send_offset = (int *) malloc(nsend*sizeof(int)); + plan->send_size = (int *) malloc(nsend*sizeof(int)); + plan->send_proc = (int *) malloc(nsend*sizeof(int)); + plan->packplan = (struct pack_plan_3d *) + malloc(nsend*sizeof(struct pack_plan_3d)); + + if (plan->send_offset == NULL || plan->send_size == NULL || + plan->send_proc == NULL || plan->packplan == NULL) return NULL; + } + + // store send info, with self as last entry + + nsend = 0; + iproc = me; + for (i = 0; i < nprocs; i++) { + iproc++; + if (iproc == nprocs) iproc = 0; + if (remap_3d_collide(&in,&array[iproc],&overlap)) { + plan->send_proc[nsend] = iproc; + plan->send_offset[nsend] = nqty * + ((overlap.klo-in.klo)*in.jsize*in.isize + + ((overlap.jlo-in.jlo)*in.isize + overlap.ilo-in.ilo)); + plan->packplan[nsend].nfast = nqty*overlap.isize; + plan->packplan[nsend].nmid = overlap.jsize; + plan->packplan[nsend].nslow = overlap.ksize; + plan->packplan[nsend].nstride_line = nqty*in.isize; + plan->packplan[nsend].nstride_plane = nqty*in.jsize*in.isize; + plan->packplan[nsend].nqty = nqty; + plan->send_size[nsend] = nqty*overlap.isize*overlap.jsize*overlap.ksize; + nsend++; + } + } + + // plan->nsend = # of sends not including self + + if (nsend && plan->send_proc[nsend-1] == me) + plan->nsend = nsend - 1; + else + plan->nsend = nsend; + + // combine input extents across all procs + + MPI_Allgather(&in,sizeof(struct extent_3d),MPI_BYTE, + array,sizeof(struct extent_3d),MPI_BYTE,comm); + + // count recv collides, including self + + nrecv = 0; + iproc = me; + for (i = 0; i < nprocs; i++) { + iproc++; + if (iproc == nprocs) iproc = 0; + nrecv += remap_3d_collide(&out,&array[iproc],&overlap); + } + + // malloc space for recv info + + if (nrecv) { + if (precision == 1) { + if (permute == 0) + plan->unpack = NULL; + else if (permute == 1) { + if (nqty == 1) + plan->unpack = NULL; + else if (nqty == 2) + plan->unpack = NULL; + else + plan->unpack = NULL; + } + else if (permute == 2) { + if (nqty == 1) + plan->unpack = NULL; + else if (nqty == 2) + plan->unpack = NULL; + else + plan->unpack = NULL; + } + } + else if (precision == 2) { + if (permute == 0) + plan->unpack = unpack_3d; + else if (permute == 1) { + if (nqty == 1) + plan->unpack = unpack_3d_permute1_1; + else if (nqty == 2) + plan->unpack = unpack_3d_permute1_2; + else + plan->unpack = unpack_3d_permute1_n; + } + else if (permute == 2) { + if (nqty == 1) + plan->unpack = unpack_3d_permute2_1; + else if (nqty == 2) + plan->unpack = unpack_3d_permute2_2; + else + plan->unpack = unpack_3d_permute2_n; + } + } + + plan->recv_offset = (int *) malloc(nrecv*sizeof(int)); + plan->recv_size = (int *) malloc(nrecv*sizeof(int)); + plan->recv_proc = (int *) malloc(nrecv*sizeof(int)); + plan->recv_bufloc = (int *) malloc(nrecv*sizeof(int)); + plan->request = (MPI_Request *) malloc(nrecv*sizeof(MPI_Request)); + plan->unpackplan = (struct pack_plan_3d *) + malloc(nrecv*sizeof(struct pack_plan_3d)); + + if (plan->recv_offset == NULL || plan->recv_size == NULL || + plan->recv_proc == NULL || plan->recv_bufloc == NULL || + plan->request == NULL || plan->unpackplan == NULL) return NULL; + } + + // store recv info, with self as last entry + + ibuf = 0; + nrecv = 0; + iproc = me; + + for (i = 0; i < nprocs; i++) { + iproc++; + if (iproc == nprocs) iproc = 0; + if (remap_3d_collide(&out,&array[iproc],&overlap)) { + plan->recv_proc[nrecv] = iproc; + plan->recv_bufloc[nrecv] = ibuf; + + if (permute == 0) { + plan->recv_offset[nrecv] = nqty * + ((overlap.klo-out.klo)*out.jsize*out.isize + + (overlap.jlo-out.jlo)*out.isize + (overlap.ilo-out.ilo)); + plan->unpackplan[nrecv].nfast = nqty*overlap.isize; + plan->unpackplan[nrecv].nmid = overlap.jsize; + plan->unpackplan[nrecv].nslow = overlap.ksize; + plan->unpackplan[nrecv].nstride_line = nqty*out.isize; + plan->unpackplan[nrecv].nstride_plane = nqty*out.jsize*out.isize; + plan->unpackplan[nrecv].nqty = nqty; + } + else if (permute == 1) { + plan->recv_offset[nrecv] = nqty * + ((overlap.ilo-out.ilo)*out.ksize*out.jsize + + (overlap.klo-out.klo)*out.jsize + (overlap.jlo-out.jlo)); + plan->unpackplan[nrecv].nfast = overlap.isize; + plan->unpackplan[nrecv].nmid = overlap.jsize; + plan->unpackplan[nrecv].nslow = overlap.ksize; + plan->unpackplan[nrecv].nstride_line = nqty*out.jsize; + plan->unpackplan[nrecv].nstride_plane = nqty*out.ksize*out.jsize; + plan->unpackplan[nrecv].nqty = nqty; + } + else { + plan->recv_offset[nrecv] = nqty * + ((overlap.jlo-out.jlo)*out.isize*out.ksize + + (overlap.ilo-out.ilo)*out.ksize + (overlap.klo-out.klo)); + plan->unpackplan[nrecv].nfast = overlap.isize; + plan->unpackplan[nrecv].nmid = overlap.jsize; + plan->unpackplan[nrecv].nslow = overlap.ksize; + plan->unpackplan[nrecv].nstride_line = nqty*out.ksize; + plan->unpackplan[nrecv].nstride_plane = nqty*out.isize*out.ksize; + plan->unpackplan[nrecv].nqty = nqty; + } + + plan->recv_size[nrecv] = nqty*overlap.isize*overlap.jsize*overlap.ksize; + ibuf += plan->recv_size[nrecv]; + nrecv++; + } + } + + // plan->nrecv = # of recvs not including self + + if (nrecv && plan->recv_proc[nrecv-1] == me) + plan->nrecv = nrecv - 1; + else + plan->nrecv = nrecv; + + // init remaining fields in remap plan + + plan->memory = memory; + + if (nrecv == plan->nrecv) + plan->self = 0; + else + plan->self = 1; + + // free locally malloced space + + free(array); + + // find biggest send message (not including self) and malloc space for it + + plan->sendbuf = NULL; + + size = 0; + for (nsend = 0; nsend < plan->nsend; nsend++) + size = MAX(size,plan->send_size[nsend]); + + if (size) { + if (precision == 1) + plan->sendbuf = NULL; + else + plan->sendbuf = (double *) malloc(size*sizeof(double)); + if (plan->sendbuf == NULL) return NULL; + } + + // if requested, allocate internal scratch space for recvs, + // only need it if I will receive any data (including self) + + plan->scratch = NULL; + + if (memory == 1) { + if (nrecv > 0) { + if (precision == 1) + plan->scratch = NULL; + else + plan->scratch = + (double *) malloc(nqty*out.isize*out.jsize*out.ksize*sizeof(double)); + if (plan->scratch == NULL) return NULL; + } + } + + // create new MPI communicator for remap + + MPI_Comm_dup(comm,&plan->comm); + + // return pointer to plan + + return plan; +} + +/* ---------------------------------------------------------------------- + Destroy a 3d remap plan +------------------------------------------------------------------------- */ + +void remap_3d_destroy_plan(struct remap_plan_3d *plan) + +{ + // free MPI communicator + + MPI_Comm_free(&plan->comm); + + // free internal arrays + + if (plan->nsend || plan->self) { + free(plan->send_offset); + free(plan->send_size); + free(plan->send_proc); + free(plan->packplan); + if (plan->sendbuf) free(plan->sendbuf); + } + + if (plan->nrecv || plan->self) { + free(plan->recv_offset); + free(plan->recv_size); + free(plan->recv_proc); + free(plan->recv_bufloc); + free(plan->request); + free(plan->unpackplan); + if (plan->scratch) free(plan->scratch); + } + + // free plan itself + + free(plan); +} + +/* ---------------------------------------------------------------------- + collide 2 sets of indices to determine overlap + compare bounds of block1 with block2 to see if they overlap + return 1 if they do and put bounds of overlapping section in overlap + return 0 if they do not overlap +------------------------------------------------------------------------- */ + +int remap_3d_collide(struct extent_3d *block1, struct extent_3d *block2, + struct extent_3d *overlap) + +{ + overlap->ilo = MAX(block1->ilo,block2->ilo); + overlap->ihi = MIN(block1->ihi,block2->ihi); + overlap->jlo = MAX(block1->jlo,block2->jlo); + overlap->jhi = MIN(block1->jhi,block2->jhi); + overlap->klo = MAX(block1->klo,block2->klo); + overlap->khi = MIN(block1->khi,block2->khi); + + if (overlap->ilo > overlap->ihi || + overlap->jlo > overlap->jhi || + overlap->klo > overlap->khi) return 0; + + overlap->isize = overlap->ihi - overlap->ilo + 1; + overlap->jsize = overlap->jhi - overlap->jlo + 1; + overlap->ksize = overlap->khi - overlap->klo + 1; + + return 1; +} diff --git a/src/remap.h b/src/remap.h new file mode 100644 index 0000000000000000000000000000000000000000..15bcdbe41ea8d9eb19ccf3613eb4b5b535d7a399 --- /dev/null +++ b/src/remap.h @@ -0,0 +1,56 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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. +------------------------------------------------------------------------- */ + +// details of how to do a 3d remap + +struct remap_plan_3d { + double *sendbuf; // buffer for MPI sends + double *scratch; // scratch buffer for MPI recvs + void (*pack)(double *, double *, struct pack_plan_3d *); + // which pack function to use + void (*unpack)(double *, double *, struct pack_plan_3d *); + // which unpack function to use + int *send_offset; // extraction loc for each send + int *send_size; // size of each send message + int *send_proc; // proc to send each message to + struct pack_plan_3d *packplan; // pack plan for each send message + int *recv_offset; // insertion loc for each recv + int *recv_size; // size of each recv message + int *recv_proc; // proc to recv each message from + int *recv_bufloc; // offset in scratch buf for each recv + MPI_Request *request; // MPI request for each posted recv + struct pack_plan_3d *unpackplan; // unpack plan for each recv message + int nrecv; // # of recvs from other procs + int nsend; // # of sends to other procs + int self; // whether I send/recv with myself + int memory; // user provides scratch space or not + MPI_Comm comm; // group of procs performing remap +}; + +// collision between 2 regions + +struct extent_3d { + int ilo,ihi,isize; + int jlo,jhi,jsize; + int klo,khi,ksize; +}; + +// function prototypes + +void remap_3d(double *, double *, double *, struct remap_plan_3d *); +struct remap_plan_3d *remap_3d_create_plan(MPI_Comm, + int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int, int); +void remap_3d_destroy_plan(struct remap_plan_3d *); +int remap_3d_collide(struct extent_3d *, + struct extent_3d *, struct extent_3d *); diff --git a/src/remap_wrap.cpp b/src/remap_wrap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c652ab0f4c150ce96bf628a0d60f5aa5d9bc921 --- /dev/null +++ b/src/remap_wrap.cpp @@ -0,0 +1,46 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "remap_wrap.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +Remap::Remap(MPI_Comm comm, + int in_ilo, int in_ihi, int in_jlo, int in_jhi, + int in_klo, int in_khi, + int out_ilo, int out_ihi, int out_jlo, int out_jhi, + int out_klo, int out_khi, + int nqty, int permute, int memory, int precision) +{ + plan = remap_3d_create_plan(comm, + in_ilo,in_ihi,in_jlo,in_jhi,in_klo,in_khi, + out_ilo,out_ihi,out_jlo,out_jhi,out_klo,out_khi, + nqty,permute,memory,precision); + if (plan == NULL) error->one("Could not create 3d remap plan"); +} + +/* ---------------------------------------------------------------------- */ + +Remap::~Remap() +{ + remap_3d_destroy_plan(plan); +} + +/* ---------------------------------------------------------------------- */ + +void Remap::perform(double *in, double *out, double *buf) +{ + remap_3d(in,out,buf,plan); +} diff --git a/src/remap_wrap.h b/src/remap_wrap.h new file mode 100644 index 0000000000000000000000000000000000000000..b27a3593228e6f1b2a971650c6ea146c29d4e9d0 --- /dev/null +++ b/src/remap_wrap.h @@ -0,0 +1,31 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef REMAP_WRAP_H +#define REMAP_WRAP_H + +#include "lammps.h" +#include "remap.h" + +class Remap : public LAMMPS { + public: + Remap(MPI_Comm,int,int,int,int,int,int, + int,int,int,int,int,int,int,int,int,int); + ~Remap(); + void perform(double *, double *, double *); + + private: + struct remap_plan_3d *plan; +}; + +#endif diff --git a/src/replicate.cpp b/src/replicate.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a9232adf6e01f2ffdc298a1253d3387db3258627 --- /dev/null +++ b/src/replicate.cpp @@ -0,0 +1,345 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "string.h" +#include "replicate.h" +#include "atom.h" +#include "atom_atomic.h" +#include "force.h" +#include "domain.h" +#include "comm.h" +#include "special.h" +#include "memory.h" +#include "error.h" + +#define AtomInclude +#include "style.h" +#undef AtomInclude + +#define LB_FACTOR 1.1 +#define MAXATOMS 0x7FFFFFFF + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +void Replicate::command(int narg, char **arg) +{ + int i,j,m,n; + + if (domain->box_exist == 0) + error->all("Replicate command before simulation box is defined"); + if (narg != 3) error->all("Illegal replicate command"); + + int me = comm->me; + int nprocs = comm->nprocs; + + if (me == 0 && screen) fprintf(screen,"Replicating atoms ...\n"); + + // nrep = total # of replications + + int nx = atoi(arg[0]); + int ny = atoi(arg[1]); + int nz = atoi(arg[2]); + int nrep = nx*ny*nz; + + // error and warning checks + + if (nx <= 0 || ny <= 0 || nz <= 0) error->all("Illegal replicate command"); + if (force->dimension == 2 && nz != 1) + error->all("Cannot replicate 2d simulation in z dimension"); + if ((nx > 1 && domain->xperiodic == 0) || + (ny > 1 && domain->yperiodic == 0) || + (nz > 1 && domain->zperiodic == 0)) + error->warning("Replicating in a non-periodic dimension"); + + if (atom->nextra_grow || atom->nextra_restart || atom->nextra_store) + error->all("Cannot replicate with fixes that store atom quantities"); + + // maxtag = largest atom tag across all existing atoms + + int maxtag = 0; + for (i = 0; i < atom->nlocal; i++) maxtag = MAX(atom->tag[i],maxtag); + int maxtag_all; + MPI_Allreduce(&maxtag,&maxtag_all,1,MPI_INT,MPI_MAX,world); + maxtag = maxtag_all; + + // maxmol = largest molecule tag across all existing atoms + + int maxmol = 0; + if (atom->molecular) { + for (i = 0; i < atom->nlocal; i++) maxmol = MAX(atom->molecule[i],maxmol); + int maxmol_all; + MPI_Allreduce(&maxmol,&maxmol_all,1,MPI_INT,MPI_MAX,world); + maxmol = maxmol_all; + } + + // unmap existing atoms via image flags + + for (i = 0; i < atom->nlocal; i++) + domain->unmap(atom->x[i][0],atom->x[i][1],atom->x[i][2],atom->image[i]); + + // nwords, words = list of atom style keywords + // old = original atom class + // atom = new replicated atom class + + char **words; + int nwords = atom->style2arg(words); + + Atom *old = atom; + + if (0) return; // dummy line to enable else-if macro expansion + +#define AtomClass +#define AtomStyle(key,Class) \ + else if (strcmp(old->style,#key) == 0) atom = new Class(nwords,words); +#include "style.h" +#undef AtomClass + + atom->settings(old); + + for (i = 0; i < nwords; i++) delete [] words[i]; + delete [] words; + + // check that new problem size will not be too large + // if N > 2^31, turn off tags + // if molecular, N,Nbonds,etc cannot be > 2^31 else tags/counts invalid + + double rep = nrep; + if (rep*old->natoms > MAXATOMS) atom->tag_enable = 0; + + if (atom->molecular) { + if (rep*old->natoms > MAXATOMS || rep*old->nbonds > MAXATOMS || + rep*old->nangles > MAXATOMS || rep*old->ndihedrals > MAXATOMS || + rep*old->nimpropers > MAXATOMS) + error->all("Too big a problem to replicate with molecular atom style"); + } + + // assign atom and topology counts in new class from old one + + atom->natoms = old->natoms * nrep; + atom->nbonds = old->nbonds * nrep; + atom->nangles = old->nangles * nrep; + atom->ndihedrals = old->ndihedrals * nrep; + atom->nimpropers = old->nimpropers * nrep; + + atom->ntypes = old->ntypes; + atom->nbondtypes = old->nbondtypes; + atom->nangletypes = old->nangletypes; + atom->ndihedraltypes = old->ndihedraltypes; + atom->nimpropertypes = old->nimpropertypes; + + atom->bond_per_atom = old->bond_per_atom; + atom->angle_per_atom = old->angle_per_atom; + atom->dihedral_per_atom = old->dihedral_per_atom; + atom->improper_per_atom = old->improper_per_atom; + + // store old simulation box + + double oldxprd = domain->xprd; + double oldyprd = domain->yprd; + double oldzprd = domain->zprd; + + // setup new simulation box + + domain->boxxhi = domain->boxxlo + nx*oldxprd; + domain->boxyhi = domain->boxylo + ny*oldyprd; + domain->boxzhi = domain->boxzlo + nz*oldzprd; + + // new problem setup using new box boundaries + + if (nprocs == 1) n = static_cast<int> (atom->natoms); + else n = static_cast<int> (LB_FACTOR * atom->natoms / nprocs); + + atom->allocate_type_arrays(); + atom->grow(n); + + domain->set_initial_box(); + domain->set_global_box(); + comm->set_procs(); + domain->set_local_box(); + + // sub xyz lo/hi = new processor sub-domain + + double subxlo = domain->subxlo; + double subxhi = domain->subxhi; + double subylo = domain->subylo; + double subyhi = domain->subyhi; + double subzlo = domain->subzlo; + double subzhi = domain->subzhi; + + // copy type arrays to new atom class + + if (atom->mass_require) { + for (int itype = 1; itype <= atom->ntypes; itype++) { + atom->mass_setflag[itype] = old->mass_setflag[itype]; + if (atom->mass_setflag[itype]) atom->mass[itype] = old->mass[itype]; + } + } + + if (atom->dipole_require) { + for (int itype = 1; itype <= atom->ntypes; itype++) { + atom->dipole_setflag[itype] = old->dipole_setflag[itype]; + if (atom->dipole_setflag[itype]) + atom->dipole[itype] = old->dipole[itype]; + } + } + + // communication buffer for all my atom's info + // max_size = largest buffer needed by any proc + + int max_size; + int send_size = old->size_restart(); + MPI_Allreduce(&send_size,&max_size,1,MPI_INT,MPI_MAX,world); + + double *buf; + buf = (double *) memory->smalloc(max_size*sizeof(double),"replicate:buf"); + + // loop over all procs + // if this iteration of loop is me: + // pack my unmapped atom data into buf + // bcast it to all other procs + // for each atom in buf, each proc performs 3d replicate loop: + // xnew,ynew,znew = new replicated position + // unpack atom into new atom class from buf if I own it + // adjust tag, mol #, coord, topology info as needed + + int ix,iy,iz,image,atom_offset,mol_offset; + double xnew,ynew,znew; + int tag_enable = atom->tag_enable; + + for (int iproc = 0; iproc < nprocs; iproc++) { + if (me == iproc) { + n = 0; + for (i = 0; i < old->nlocal; i++) n += old->pack_restart(i,&buf[n]); + } + MPI_Bcast(&n,1,MPI_INT,iproc,world); + MPI_Bcast(buf,n,MPI_DOUBLE,iproc,world); + + for (ix = 0; ix < nx; ix++) { + for (iy = 0; iy < ny; iy++) { + for (iz = 0; iz < nz; iz++) { + + m = 0; + while (m < n) { + xnew = buf[m+1] + ix*oldxprd; + ynew = buf[m+2] + iy*oldyprd; + znew = buf[m+3] + iz*oldzprd; + image = (512 << 20) | (512 << 10) | 512; + domain->remap(xnew,ynew,znew,image); + + if (xnew >= subxlo && xnew < subxhi && + ynew >= subylo && ynew < subyhi && + znew >= subzlo && znew < subzhi) { + + m += atom->unpack_restart(&buf[m]); + + i = atom->nlocal - 1; + if (tag_enable) + atom_offset = iz*ny*nx*maxtag + iy*nx*maxtag + ix*maxtag; + else atom_offset = 0; + mol_offset = iz*ny*nx*maxmol + iy*nx*maxmol + ix*maxmol; + + atom->x[i][0] = xnew; + atom->x[i][1] = ynew; + atom->x[i][2] = znew; + + atom->tag[i] += atom_offset; + atom->image[i] = image; + + if (atom->molecular) { + if (atom->molecule[i] > 0) + atom->molecule[i] += mol_offset; + if (atom->bonds_allow) + for (j = 0; j < atom->num_bond[i]; j++) + atom->bond_atom[i][j] += atom_offset; + if (atom->angles_allow) + for (j = 0; j < atom->num_angle[i]; j++) { + atom->angle_atom1[i][j] += atom_offset; + atom->angle_atom2[i][j] += atom_offset; + atom->angle_atom3[i][j] += atom_offset; + } + if (atom->dihedrals_allow) + for (j = 0; j < atom->num_dihedral[i]; j++) { + atom->dihedral_atom1[i][j] += atom_offset; + atom->dihedral_atom2[i][j] += atom_offset; + atom->dihedral_atom3[i][j] += atom_offset; + atom->dihedral_atom4[i][j] += atom_offset; + } + if (atom->impropers_allow) + for (j = 0; j < atom->num_improper[i]; j++) { + atom->improper_atom1[i][j] += atom_offset; + atom->improper_atom2[i][j] += atom_offset; + atom->improper_atom3[i][j] += atom_offset; + atom->improper_atom4[i][j] += atom_offset; + } + } + } else m += static_cast<int> (buf[m]); + } // end of while loop over one proc's atom list + + } + } + } + } // end of proc loop + + // free communication buffer and old atom class + + memory->sfree(buf); + delete old; + + // check that all atoms were assigned to procs + + double natoms; + double rlocal = atom->nlocal; + MPI_Allreduce(&rlocal,&natoms,1,MPI_DOUBLE,MPI_SUM,world); + + if (me == 0) { + if (screen) fprintf(screen," %.15g atoms\n",natoms); + if (logfile) fprintf(logfile," %.15g atoms\n",natoms); + } + + if (natoms != atom->natoms) + error->all("Replicate did not assign all atoms correctly"); + + if (me == 0) { + if (atom->nbonds) { + if (screen) fprintf(screen," %d bonds\n",atom->nbonds); + if (logfile) fprintf(logfile," %d bonds\n",atom->nbonds); + } + if (atom->nangles) { + if (screen) fprintf(screen," %d angles\n",atom->nangles); + if (logfile) fprintf(logfile," %d angles\n",atom->nangles); + } + if (atom->ndihedrals) { + if (screen) fprintf(screen," %d dihedrals\n",atom->ndihedrals); + if (logfile) fprintf(logfile," %d dihedrals\n",atom->ndihedrals); + } + if (atom->nimpropers) { + if (screen) fprintf(screen," %d impropers\n",atom->nimpropers); + if (logfile) fprintf(logfile," %d impropers\n",atom->nimpropers); + } + } + + // create global mapping and bond topology now that system is defined + + if (atom->map_style) { + atom->map_init(); + atom->map_set(); + } + if (atom->molecular) { + Special special; + special.build(); + } +} diff --git a/src/replicate.h b/src/replicate.h new file mode 100644 index 0000000000000000000000000000000000000000..7ef68e3a778ecb3f07d9beda71ab05dbe6141b4a --- /dev/null +++ b/src/replicate.h @@ -0,0 +1,26 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef REPLICATE_H +#define REPLICATE_H + +#include "lammps.h" + +class Replicate : public LAMMPS { + public: + Replicate() {} + ~Replicate() {} + void command(int, char **); +}; + +#endif diff --git a/src/respa.cpp b/src/respa.cpp new file mode 100644 index 0000000000000000000000000000000000000000..380e9de384ef09867673b104afd6be0451e26d32 --- /dev/null +++ b/src/respa.cpp @@ -0,0 +1,584 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Mark Stevens (SNL), Paul Crozier (SNL) +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "string.h" +#include "respa.h" +#include "neighbor.h" +#include "atom.h" +#include "domain.h" +#include "comm.h" +#include "atom.h" +#include "force.h" +#include "pair.h" +#include "bond.h" +#include "angle.h" +#include "dihedral.h" +#include "improper.h" +#include "kspace.h" +#include "output.h" +#include "update.h" +#include "fix_respa.h" +#include "modify.h" +#include "memory.h" +#include "timer.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +Respa::Respa(int narg, char **arg) : Integrate(narg, arg) +{ + if (narg < 1) error->all("Illegal run_style respa command"); + + nlevels = atoi(arg[0]); + if (nlevels < 1) error->all("Respa levels must be >= 1"); + + if (narg < nlevels) error->all("Illegal run_style respa command"); + loop = new int[nlevels]; + for (int iarg = 1; iarg < nlevels; iarg++) { + loop[iarg-1] = atoi(arg[iarg]); + if (loop[iarg-1] <= 0) error->all("Illegal run_style respa command"); + } + loop[nlevels-1] = 1; + + // set level at which each force is computed + // argument settings override defaults + + level_bond = level_angle = level_dihedral = level_improper = -1; + level_pair = level_kspace = -1; + level_inner = level_middle = level_outer = -1; + + int iarg = nlevels; + while (iarg < narg) { + if (strcmp(arg[iarg],"bond") == 0) { + if (iarg+2 > narg) error->all("Illegal run_style respa command"); + level_bond = atoi(arg[iarg+1]) - 1; + iarg += 2; + } else if (strcmp(arg[iarg],"angle") == 0) { + if (iarg+2 > narg) error->all("Illegal run_style respa command"); + level_angle = atoi(arg[iarg+1]) - 1; + iarg += 2; + } else if (strcmp(arg[iarg],"dihedral") == 0) { + if (iarg+2 > narg) error->all("Illegal run_style respa command"); + level_dihedral = atoi(arg[iarg+1]) - 1; + iarg += 2; + } else if (strcmp(arg[iarg],"improper") == 0) { + if (iarg+2 > narg) error->all("Illegal run_style respa command"); + level_improper = atoi(arg[iarg+1]) - 1; + iarg += 2; + } else if (strcmp(arg[iarg],"pair") == 0) { + if (iarg+2 > narg) error->all("Illegal run_style respa command"); + level_pair = atoi(arg[iarg+1]) - 1; + iarg += 2; + } else if (strcmp(arg[iarg],"inner") == 0) { + if (iarg+4 > narg) error->all("Illegal run_style respa command"); + level_inner = atoi(arg[iarg+1]) - 1; + cutoff[0] = atof(arg[iarg+2]); + cutoff[1] = atof(arg[iarg+3]); + iarg += 4; + } else if (strcmp(arg[iarg],"middle") == 0) { + if (iarg+4 > narg) error->all("Illegal run_style respa command"); + level_middle = atoi(arg[iarg+1]) - 1; + cutoff[2] = atof(arg[iarg+2]); + cutoff[3] = atof(arg[iarg+3]); + iarg += 4; + } else if (strcmp(arg[iarg],"outer") == 0) { + if (iarg+2 > narg) error->all("Illegal run_style respa command"); + level_outer = atoi(arg[iarg+1]) - 1; + iarg += 2; + } else if (strcmp(arg[iarg],"kspace") == 0) { + if (iarg+2 > narg) error->all("Illegal run_style respa command"); + level_kspace = atoi(arg[iarg+1]) - 1; + iarg += 2; + } else error->all("Illegal run_style respa command"); + } + + // cannot specify both pair and inner/middle/outer + + if (level_pair >= 0 && + (level_inner >= 0 || level_middle >= 0 || level_outer >= 0)) + error->all("Cannot set both respa pair and inner/middle/outer"); + + // if either inner and outer is specified, then both must be + + if ((level_inner >= 0 && level_outer == -1) || + (level_outer >= 0 && level_inner == -1)) + error->all("Must set both respa inner and outer"); + + // middle cannot be set without inner/outer + + if (level_middle >= 0 && level_inner == -1) + error->all("Cannot set respa middle without inner/outer"); + + // set defaults if user did not specify level + // bond to innermost level + // angle same as bond, dihedral same as angle, improper same as dihedral + // pair to outermost level if no inner/middle/outer + // inner/middle/outer have no defaults + // kspace same as pair or outer + + if (level_bond == -1) level_bond = 0; + if (level_angle == -1) level_angle = level_bond; + if (level_dihedral == -1) level_dihedral = level_angle; + if (level_improper == -1) level_improper = level_dihedral; + if (level_pair == -1 && level_inner == -1) level_pair = nlevels-1; + if (level_kspace == -1 && level_pair >= 0) level_kspace = level_pair; + if (level_kspace == -1 && level_pair == -1) level_kspace = level_outer; + + // print respa levels + + if (comm->me == 0) { + if (screen) { + fprintf(screen,"Respa levels:\n"); + for (int i = 0; i < nlevels; i++) { + fprintf(screen," %d =",i); + if (level_bond == i) fprintf(screen," bond"); + if (level_angle == i) fprintf(screen," angle"); + if (level_dihedral == i) fprintf(screen," dihedral"); + if (level_improper == i) fprintf(screen," improper"); + if (level_pair == i) fprintf(screen," pair"); + if (level_inner == i) fprintf(screen," pair-inner"); + if (level_middle == i) fprintf(screen," pair-middle"); + if (level_outer == i) fprintf(screen," pair-outer"); + if (level_kspace == i) fprintf(screen," kspace"); + fprintf(screen,"\n"); + } + } + if (logfile) { + fprintf(logfile,"Respa levels:\n"); + for (int i = 0; i < nlevels; i++) { + fprintf(logfile," %d =",i); + if (level_bond == i) fprintf(logfile," bond"); + if (level_angle == i) fprintf(logfile," angle"); + if (level_dihedral == i) fprintf(logfile," dihedral"); + if (level_improper == i) fprintf(logfile," improper"); + if (level_pair == i) fprintf(logfile," pair"); + if (level_inner == i) fprintf(logfile," pair-inner"); + if (level_middle == i) fprintf(logfile," pair-middle"); + if (level_outer == i) fprintf(logfile," pair-outer"); + if (level_kspace == i) fprintf(logfile," kspace"); + fprintf(logfile,"\n"); + } + } + } + + // check that levels are in correct order + + if (level_angle < level_bond || level_dihedral < level_angle || + level_improper < level_dihedral) + error->all("Invalid order of forces within respa levels"); + if (level_pair >= 0) { + if (level_pair < level_improper || level_kspace < level_pair) + error->all("Invalid order of forces within respa levels"); + } + if (level_pair == -1 && level_middle == -1) { + if (level_inner < level_improper || level_outer < level_inner || + level_kspace != level_outer) + error->all("Invalid order of forces within respa levels"); + } + if (level_pair == -1 && level_middle >= 0) { + if (level_inner < level_improper || level_middle < level_inner || + level_outer < level_inner || level_kspace != level_outer) + error->all("Invalid order of forces within respa levels"); + } + + // warn if any levels are devoid of forces + + int flag = 0; + for (int i = 0; i < nlevels; i++) + if (level_bond != i && level_angle != i && level_dihedral != i && + level_improper != i && level_pair != i && level_inner != i && + level_middle != i && level_outer != i && level_kspace != i) flag = 1; + if (flag && comm->me == 0) + error->warning("One or more respa levels compute no forces"); + + // check cutoff consistency if inner/middle/outer are enabled + + if (level_inner >= 0 && cutoff[1] < cutoff[0]) + error->all("Respa inner cutoffs are invalid"); + if (level_middle >= 0 && (cutoff[3] < cutoff[2] || cutoff[2] < cutoff[1])) + error->all("Respa middle cutoffs are invalid"); + + // set outer pair of cutoffs to inner pair if middle is not enabled + + if (level_inner >= 0 && level_middle < 0) { + cutoff[2] = cutoff[0]; + cutoff[3] = cutoff[1]; + } + + // allocate other needed level arrays + + newton = new int[nlevels]; + step = new double[nlevels]; +} + +/* ---------------------------------------------------------------------- */ + +Respa::~Respa() +{ + delete [] loop; + delete [] newton; + delete [] step; +} + +/* ---------------------------------------------------------------------- + initialization before run +------------------------------------------------------------------------- */ + +void Respa::init() +{ + // warn if no fixes + + if (modify->nfix == 0) + error->warning("No fixes defined, atoms won't move"); + + // respa not allowed with granular atom style + + if (atom->check_style("granular")) + error->all("Respa not allowed with atom style granular"); + + // create fix needed for storing atom-based respa level forces + // will delete it at end of run + + char **fixarg = new char*[4]; + fixarg[0] = "RESPA"; + fixarg[1] = "all"; + fixarg[2] = "RESPA"; + fixarg[3] = new char[8]; + sprintf(fixarg[3],"%d",nlevels); + modify->add_fix(4,fixarg); + delete [] fixarg[3]; + delete [] fixarg; + + for (int i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"RESPA") == 0) ifix_respa = i; + + // insure respa inner/middle/outer is using Pair class that supports it + + if (level_inner >= 0) + if (force->pair && force->pair->respa_enable == 0) + error->all("Pair style does not support rRESPA inner/middle/outer"); + + // set flags for how virial should be computed when needed + // pressure_flag is 1 if NPT,NPH + // virial_every is how virial should be computed every timestep + // 0 = not computed, 1 = computed explicity by pair + // virial_thermo is how virial should be computed on thermo timesteps + // 1 = computed explicity by pair + // unlike Verlet, virial is never computed implicitly + + int pressure_flag = 0; + for (int i = 0; i < modify->nfix; i++) { + if (strcmp(modify->fix[i]->style,"npt") == 0) pressure_flag = 1; + if (strcmp(modify->fix[i]->style,"nph") == 0) pressure_flag = 1; + } + + if (pressure_flag) virial_every = 1; + else virial_every = 0; + + virial_thermo = 1; + + // step[] = timestep for each level + + step[nlevels-1] = update->dt; + for (int ilevel = nlevels-2; ilevel >= 0; ilevel--) + step[ilevel] = step[ilevel+1]/loop[ilevel]; + + // set newton flag for each level + + for (int ilevel = 0; ilevel < nlevels; ilevel++) { + newton[ilevel] = 0; + if (force->newton_bond) { + if (level_bond == ilevel || level_angle == ilevel || + level_dihedral == ilevel || level_improper == ilevel) + newton[ilevel] = 1; + } + if (force->newton_pair) { + if (level_pair == ilevel || level_inner == ilevel || + level_middle == ilevel || level_outer == ilevel) + newton[ilevel] = 1; + } + } +} + +/* ---------------------------------------------------------------------- + setup before run +------------------------------------------------------------------------- */ + +void Respa::setup() +{ + if (comm->me == 0 && screen) fprintf(screen,"Setting up run ...\n"); + + // setup domain, communication and neighboring + // acquire ghosts + // build neighbor lists + + domain->pbc(); + domain->reset_box(); + comm->setup(); + if (neighbor->style) neighbor->setup_bins(); + comm->exchange(); + comm->borders(); + neighbor->build(); + neighbor->ncalls = 0; + + // compute all forces + + int eflag = 1; + int vflag = virial_thermo; + + for (int ilevel = 0; ilevel < nlevels; ilevel++) { + force_clear(newton[ilevel]); + if (level_bond == ilevel && force->bond) + force->bond->compute(eflag,vflag); + if (level_angle == ilevel && force->angle) + force->angle->compute(eflag,vflag); + if (level_dihedral == ilevel && force->dihedral) + force->dihedral->compute(eflag,vflag); + if (level_improper == ilevel && force->improper) + force->improper->compute(eflag,vflag); + if (level_pair == ilevel && force->pair) + force->pair->compute(eflag,vflag); + if (level_inner == ilevel && force->pair) + force->pair->compute_inner(); + if (level_middle == ilevel && force->pair) + force->pair->compute_middle(); + if (level_outer == ilevel && force->pair) + force->pair->compute_outer(eflag,vflag); + if (level_kspace == ilevel && force->kspace) { + force->kspace->setup(); + force->kspace->compute(eflag,vflag); + } + if (newton[ilevel]) comm->reverse_communicate(); + copy_f_flevel(ilevel); + } + + modify->setup(); + sum_flevel_f(); + output->setup(1); +} + +/* ---------------------------------------------------------------------- + iterate for n steps +------------------------------------------------------------------------- */ + +void Respa::iterate(int n) +{ + for (int i = 0; i < n; i++) { + + update->ntimestep++; + + eflag = 0; + vflag = virial_every; + if (output->next_thermo == update->ntimestep) { + eflag = 1; + vflag = virial_thermo; + } + + recurse(nlevels-1); + + if (modify->n_end_of_step) modify->end_of_step(); + + if (output->next == update->ntimestep) { + timer->stamp(); + sum_flevel_f(); + output->write(update->ntimestep); + timer->stamp(TIME_OUTPUT); + } + } +} + +/* ---------------------------------------------------------------------- + delete fix at end of run, so its atom arrays won't persist +------------------------------------------------------------------------- */ + +void Respa::cleanup() +{ + modify->delete_fix("RESPA"); +} + +/* ---------------------------------------------------------------------- */ + +void Respa::recurse(int ilevel) +{ + copy_flevel_f(ilevel); + + for (int iloop = 0; iloop < loop[ilevel]; iloop++) { + + modify->initial_integrate_respa(ilevel,0); + + if (ilevel) recurse(ilevel-1); + + // at outermost level: + // call initial_integrate w/ flag = 1 so fix NPT,NPH perform 2nd dilate + // at correct symmetric position in rRESPA hierarchy + // check on rebuilding neighbor list + // at innermost level, communicate + // at middle levels, do nothing + + if (ilevel == nlevels-1) { + modify->initial_integrate_respa(ilevel,1); + int nflag = neighbor->decide(); + if (nflag) { + if (modify->n_pre_exchange) modify->pre_exchange(); + domain->pbc(); + if (domain->box_change) { + domain->reset_box(); + comm->setup(); + if (neighbor->style) neighbor->setup_bins(); + } + timer->stamp(); + comm->exchange(); + comm->borders(); + timer->stamp(TIME_COMM); + if (modify->n_pre_neighbor) modify->pre_neighbor(); + neighbor->build(); + timer->stamp(TIME_NEIGHBOR); + } + } else if (ilevel == 0) { + timer->stamp(); + comm->communicate(); + timer->stamp(TIME_COMM); + } + + force_clear(newton[ilevel]); + + timer->stamp(); + if (level_bond == ilevel && force->bond) { + force->bond->compute(eflag,vflag); + timer->stamp(TIME_BOND); + } + if (level_angle == ilevel && force->angle) { + force->angle->compute(eflag,vflag); + timer->stamp(TIME_BOND); + } + if (level_dihedral == ilevel && force->dihedral) { + force->dihedral->compute(eflag,vflag); + timer->stamp(TIME_BOND); + } + if (level_improper == ilevel && force->improper) { + force->improper->compute(eflag,vflag); + timer->stamp(TIME_BOND); + } + if (level_pair == ilevel && force->pair) { + force->pair->compute(eflag,vflag); + timer->stamp(TIME_PAIR); + } + if (level_inner == ilevel && force->pair) { + force->pair->compute_inner(); + timer->stamp(TIME_PAIR); + } + if (level_middle == ilevel && force->pair) { + force->pair->compute_middle(); + timer->stamp(TIME_PAIR); + } + if (level_outer == ilevel && force->pair) { + force->pair->compute_outer(eflag,vflag); + timer->stamp(TIME_PAIR); + } + if (level_kspace == ilevel && force->kspace) { + force->kspace->compute(eflag,vflag); + timer->stamp(TIME_KSPACE); + } + + if (newton[ilevel]) { + comm->reverse_communicate(); + timer->stamp(TIME_COMM); + } + + if (modify->n_post_force_respa) modify->post_force_respa(vflag,ilevel,iloop); + modify->final_integrate_respa(ilevel); + } + + copy_f_flevel(ilevel); +} + +/* ---------------------------------------------------------------------- + clear force on own & ghost atoms +------------------------------------------------------------------------- */ + +void Respa::force_clear(int newtonflag) +{ + // clear global force array + // nall includes ghosts only if newton flag is set + + int nall; + if (newtonflag) nall = atom->nlocal + atom->nghost; + else nall = atom->nlocal; + + double **f = atom->f; + for (int i = 0; i < nall; i++) { + f[i][0] = 0.0; + f[i][1] = 0.0; + f[i][2] = 0.0; + } +} + +/* ---------------------------------------------------------------------- + copy force components from atom->f to FixRespa->f_level +------------------------------------------------------------------------- */ + +void Respa::copy_f_flevel(int ilevel) +{ + double ***f_level = ((FixRespa *) modify->fix[ifix_respa])->f_level; + double **f = atom->f; + int n = atom->nlocal; + + for (int i = 0; i < n; i++) { + f_level[i][ilevel][0] = f[i][0]; + f_level[i][ilevel][1] = f[i][1]; + f_level[i][ilevel][2] = f[i][2]; + } +} + +/* ---------------------------------------------------------------------- + copy force components from FixRespa->f_level to atom->f +------------------------------------------------------------------------- */ + +void Respa::copy_flevel_f(int ilevel) +{ + double ***f_level = ((FixRespa *) modify->fix[ifix_respa])->f_level; + double **f = atom->f; + int n = atom->nlocal; + + for (int i = 0; i < n; i++) { + f[i][0] = f_level[i][ilevel][0]; + f[i][1] = f_level[i][ilevel][1]; + f[i][2] = f_level[i][ilevel][2]; + } +} + +/* ---------------------------------------------------------------------- + sum all force components from FixRespa->f_level to create full atom->f +------------------------------------------------------------------------- */ + +void Respa::sum_flevel_f() +{ + copy_flevel_f(0); + + double ***f_level = ((FixRespa *) modify->fix[ifix_respa])->f_level; + double **f = atom->f; + int n = atom->nlocal; + + for (int ilevel = 1; ilevel < nlevels; ilevel++) { + for (int i = 0; i < n; i++) { + f[i][0] += f_level[i][ilevel][0]; + f[i][1] += f_level[i][ilevel][1]; + f[i][2] += f_level[i][ilevel][2]; + } + } +} diff --git a/src/respa.h b/src/respa.h new file mode 100644 index 0000000000000000000000000000000000000000..a1d5e920e48397a35b36500c93539a9a5dc3a844 --- /dev/null +++ b/src/respa.h @@ -0,0 +1,57 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef RESPA_H +#define RESPA_H + +#include "integrate.h" + +class Respa : public Integrate { + public: + // public so Fixes, Pairs, Neighbor can see them + int nlevels; // number of rRESPA levels + // 0 = innermost level, nlevels-1 = outermost level + double *step; // timestep at each level + int *loop; // sub-cycling factor at each level + double cutoff[4]; // cutoff[0] and cutoff[1] = between inner and middle + // cutoff[2] and cutoff[3] = between middle and outer + // if no middle then 0,1 = 2,3 + + int level_bond,level_angle,level_dihedral; // level to compute forces at + int level_improper,level_pair,level_kspace; + int level_inner,level_middle,level_outer; + + Respa(int, char **); + ~Respa(); + void init(); + void setup(); + void iterate(int); + void cleanup(); + + void copy_f_flevel(int); + void copy_flevel_f(int); + + private: + int virial_every; // what vflag should be on every timestep (0,1) + int virial_thermo; // what vflag should be on thermo steps (1) + + int *newton; // newton flag at each level + int eflag,vflag; // flags for energy/virial computation + int ifix_respa; // which Fix stores the force level array + + void recurse(int); + void force_clear(int); + void sum_flevel_f(); +}; + +#endif diff --git a/src/run.cpp b/src/run.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0cb14e56dca3ac00e0a42a75e25edd4e58f9b069 --- /dev/null +++ b/src/run.cpp @@ -0,0 +1,208 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "stdlib.h" +#include "string.h" +#include "run.h" +#include "system.h" +#include "domain.h" +#include "update.h" +#include "integrate.h" +#include "output.h" +#include "finish.h" +#include "input.h" +#include "timer.h" +#include "error.h" + +#define CommandInclude +#include "style.h" +#undef CommandInclude + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +void Run::command(int narg, char **arg) +{ + if (narg < 1) error->all("Illegal run command"); + + if (domain->box_exist == 0) + error->all("Run command before simulation box is defined"); + + int nsteps = atoi(arg[0]); + + // parse optional args + + int uptoflag = 0; + int startflag = 0; + int stopflag = 0; + int start,stop; + int preflag = 1; + int postflag = 1; + int nevery = 0; + char *commandstr = NULL; + + int iarg = 1; + while (iarg < narg) { + if (strcmp(arg[iarg],"upto") == 0) { + if (iarg+1 > narg) error->all("Illegal run command"); + uptoflag = 1; + iarg += 1; + } else if (strcmp(arg[iarg],"start") == 0) { + if (iarg+2 > narg) error->all("Illegal run command"); + startflag = 1; + start = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"stop") == 0) { + if (iarg+2 > narg) error->all("Illegal run command"); + stopflag = 1; + stop = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"pre") == 0) { + if (iarg+2 > narg) error->all("Illegal run command"); + if (strcmp(arg[iarg+1],"no") == 0) preflag = 0; + else if (strcmp(arg[iarg+1],"yes") == 0) preflag = 1; + else error->all("Illegal run command"); + iarg += 2; + } else if (strcmp(arg[iarg],"post") == 0) { + if (iarg+2 > narg) error->all("Illegal run command"); + if (strcmp(arg[iarg+1],"no") == 0) postflag = 0; + else if (strcmp(arg[iarg+1],"yes") == 0) postflag = 1; + else error->all("Illegal run command"); + iarg += 2; + } else if (strcmp(arg[iarg],"every") == 0) { + if (iarg+3 > narg) error->all("Illegal run command"); + nevery = atoi(arg[iarg+1]); + if (nevery <= 0) error->all("Illegal run command"); + if (strcmp(arg[iarg+2],"NULL") != 0) { + int n = strlen(arg[iarg+2]) + 1; + commandstr = new char[n]; + strcpy(commandstr,arg[iarg+2]); + } + iarg += 3; + } else error->all("Illegal run command"); + } + + // adjust nsteps if upto was specified + + if (uptoflag) nsteps = nsteps - update->ntimestep; + + // error check + + if (uptoflag && nsteps < 0) + error->all("Run command upto value is before current timestep"); + if (startflag && start > update->ntimestep) + error->all("Run command start value is after start of run"); + if (stopflag && stop < update->ntimestep + nsteps) + error->all("Run command stop value is before end of run"); + + // perform a single run + // use start/stop to set begin/end step + // if pre or 1st run, do System init/setup, + // else just init timer and setup output + // if post, do full Finish, else just print time + + char *command; + update->whichflag = 0; + + if (nevery == 0) { + update->nsteps = nsteps; + update->firststep = update->ntimestep; + update->laststep = update->ntimestep + nsteps; + + if (startflag) update->beginstep = start; + else update->beginstep = update->firststep; + if (stopflag) update->endstep = stop; + else update->endstep = update->laststep; + + if (preflag || update->first_update == 0) { + sys->init(); + update->integrate->setup(); + } else { + timer->init(); + output->setup(0); + } + + timer->barrier_start(TIME_LOOP); + update->integrate->iterate(nsteps); + timer->barrier_stop(TIME_LOOP); + + update->integrate->cleanup(); + + Finish finish; + finish.end(postflag); + + // perform multiple runs interleaved with invocation of a command + // use start/stop to set begin/end step + // if pre or 1st iteration of multiple runs, do System init/setup, + // else just init timer and setup output + // if post or last iteration, do full Finish, else just print time + + } else { + int iter = 0; + int nleft = nsteps; + while (nleft > 0 || iter == 0) { + nsteps = MIN(nleft,nevery); + + update->nsteps = nsteps; + update->firststep = update->ntimestep; + update->laststep = update->ntimestep + nsteps; + + if (startflag) update->beginstep = start; + else update->beginstep = update->firststep; + if (stopflag) update->endstep = stop; + else update->endstep = update->laststep; + + if (preflag || iter == 0) { + sys->init(); + update->integrate->setup(); + } else { + timer->init(); + output->setup(0); + } + + timer->barrier_start(TIME_LOOP); + update->integrate->iterate(nsteps); + timer->barrier_stop(TIME_LOOP); + + update->integrate->cleanup(); + + Finish finish; + if (postflag || nleft == 0) finish.end(1); + else finish.end(0); + + if (commandstr) { + command = input->one(commandstr); + if (command) { + if (0) return; + +#define CommandClass +#define CommandStyle(key,Class) \ + else if (strcmp(command,#key) == 0) { \ + Class key; \ + key.command(input->narg,input->arg); \ + } +#include "style.h" +#undef CommandClass + } + } + + nleft -= nsteps; + iter++; + } + } + + update->whichflag = -1; + delete [] commandstr; +} diff --git a/src/run.h b/src/run.h new file mode 100644 index 0000000000000000000000000000000000000000..242102f47e4622007f0db6abe88ad26d76c357d2 --- /dev/null +++ b/src/run.h @@ -0,0 +1,26 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef RUN_H +#define RUN_H + +#include "lammps.h" + +class Run : public LAMMPS { + public: + Run() {} + ~Run() {} + void command(int, char **); +}; + +#endif diff --git a/src/set.cpp b/src/set.cpp new file mode 100644 index 0000000000000000000000000000000000000000..47371505d6f50fe3d42792af4c83e7de39663e7b --- /dev/null +++ b/src/set.cpp @@ -0,0 +1,284 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "math.h" +#include "stdlib.h" +#include "string.h" +#include "set.h" +#include "system.h" +#include "atom.h" +#include "domain.h" +#include "group.h" +#include "comm.h" +#include "neighbor.h" +#include "force.h" +#include "pair.h" +#include "random_park.h" +#include "error.h" + +#define ONEATOM 0 +#define ATOM 1 +#define BOND 2 +#define ANGLE 3 +#define DIHEDRAL 4 +#define IMPROPER 5 +#define CHARGE 6 +#define DIPOLE 7 + +#define WARMUP 100 + +/* ---------------------------------------------------------------------- */ + +void Set::command(int narg, char **arg) +{ + if (domain->box_exist == 0) + error->all("Set command before simulation box is defined"); + if (atom->natoms == 0) + error->all("Set command with no atoms existing"); + if (narg != 3) error->all("Illegal set command"); + + // set style + + int style; + if (strcmp(arg[1],"atom") == 0) style = ATOM; + else if (strcmp(arg[1],"bond") == 0) style = BOND; + else if (strcmp(arg[1],"angle") == 0) style = ANGLE; + else if (strcmp(arg[1],"dihedral") == 0) style = DIHEDRAL; + else if (strcmp(arg[1],"improper") == 0) style = IMPROPER; + else if (strcmp(arg[1],"charge") == 0) style = CHARGE; + else if (strcmp(arg[1],"dipole") == 0) style = DIPOLE; + else style = ONEATOM; + + // set atom or group + + int atomid,igroup,groupbit; + if (style == ONEATOM) atomid = atoi(arg[0]); + else { + igroup = group->find(arg[0]); + if (igroup == -1) error->all("Cannot find set command group ID"); + groupbit = group->bitmask[igroup]; + } + + // consistency checks + + if ((style == BOND && atom->bonds_allow == 0) || + (style == ANGLE && atom->angles_allow == 0) || + (style == DIHEDRAL && atom->dihedrals_allow == 0) || + (style == IMPROPER && atom->impropers_allow == 0) || + (style == CHARGE && atom->charge_allow == 0) || + (style == DIPOLE && atom->dipole_require == 0)) + error->all("Cannot set this attribute for this atom style"); + + if (style == ONEATOM && strcmp(arg[1],"q") == 0 && atom->charge_allow == 0) + error->all("Cannot set this attribute for this atom style"); + + if (style == ONEATOM && strcmp(arg[1],"mol") == 0 && atom->molecular == 0) + error->all("Cannot set this attribute for this atom style"); + + // border swap to insure type and mask is current for off-proc atoms + // only needed for BOND, ANGLE, etc types + // enforce PBC before in case atoms are outside box + // init entire system since comm->exchange is done + // comm::init needs neighbor::init needs pair::init needs kspace::init, etc + + if (style == BOND || style == ANGLE || + style == DIHEDRAL || style == IMPROPER) { + if (comm->me == 0 && screen) fprintf(screen,"System init for set ...\n"); + sys->init(); + + domain->pbc(); + domain->reset_box(); + comm->setup(); + comm->exchange(); + comm->borders(); + } + + if (comm->me == 0 && screen) fprintf(screen,"Setting atom values ...\n"); + + // set new values + + int *mask = atom->mask; + int nlocal = atom->nlocal; + int m,atom1,atom2,atom3,atom4; + + int count = 0; + + // for style ATOM, atom must be in group + + if (style == ATOM) { + int ivalue = atoi(arg[2]); + if (ivalue < 1 || ivalue > atom->ntypes) + error->all("Invalid type in set command"); + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + atom->type[i] = ivalue; + count++; + } + } + + // for style BOND, each of 2 atoms must be in group + + if (style == BOND) { + int ivalue = atoi(arg[2]); + if (ivalue < 1 || ivalue > atom->nbondtypes) + error->all("Invalid type in set command"); + for (int i = 0; i < nlocal; i++) + for (m = 0; m < atom->num_bond[i]; m++) { + atom1 = atom->map(atom->bond_atom[i][m]); + if (atom1 == -1) error->one("Bond atom missing in set command"); + if (mask[i] & groupbit && mask[atom1] & groupbit) { + atom->bond_type[i][m] = ivalue; + count++; + } + } + } + + // for style ANGLE, each of 3 atoms must be in group + + if (style == ANGLE) { + int ivalue = atoi(arg[2]); + if (ivalue < 1 || ivalue > atom->nangletypes) + error->all("Invalid type in set command"); + for (int i = 0; i < nlocal; i++) + for (m = 0; m < atom->num_angle[i]; m++) { + atom1 = atom->map(atom->angle_atom1[i][m]); + atom2 = atom->map(atom->angle_atom2[i][m]); + atom3 = atom->map(atom->angle_atom3[i][m]); + if (atom1 == -1 || atom2 == -1 || atom3 == -1) + error->one("Angle atom missing in set command"); + if (mask[atom1] & groupbit && mask[atom2] & groupbit && + mask[atom3] & groupbit) { + atom->angle_type[i][m] = ivalue; + count++; + } + } + } + + // for style DIHEDRAL, each of 4 atoms must be in group + + if (style == DIHEDRAL) { + int ivalue = atoi(arg[2]); + if (ivalue < 1 || ivalue > atom->ndihedraltypes) + error->all("Invalid type in set command"); + for (int i = 0; i < nlocal; i++) + for (m = 0; m < atom->num_dihedral[i]; m++) { + atom1 = atom->map(atom->dihedral_atom1[i][m]); + atom2 = atom->map(atom->dihedral_atom2[i][m]); + atom3 = atom->map(atom->dihedral_atom3[i][m]); + atom4 = atom->map(atom->dihedral_atom4[i][m]); + if (atom1 == -1 || atom2 == -1 || atom3 == -1 || atom4 == -1) + error->one("Dihedral atom missing in set command"); + if (mask[atom1] & groupbit && mask[atom2] & groupbit && + mask[atom3] & groupbit && mask[atom4] & groupbit) { + atom->dihedral_type[i][m] = ivalue; + count++; + } + } + } + + // for style IMPROPER, each of 4 atoms must be in group + + if (style == IMPROPER) { + int ivalue = atoi(arg[2]); + if (ivalue < 1 || ivalue > atom->nimpropertypes) + error->all("Invalid type in set command"); + for (int i = 0; i < nlocal; i++) + for (m = 0; m < atom->num_improper[i]; m++) { + atom1 = atom->map(atom->improper_atom1[i][m]); + atom2 = atom->map(atom->improper_atom2[i][m]); + atom3 = atom->map(atom->improper_atom3[i][m]); + atom4 = atom->map(atom->improper_atom4[i][m]); + if (atom1 == -1 || atom2 == -1 || atom3 == -1 || atom4 == -1) + error->one("Improper atom missing in set command"); + if (mask[atom1] & groupbit && mask[atom2] & groupbit && + mask[atom3] & groupbit && mask[atom4] & groupbit) { + atom->improper_type[i][m] = ivalue; + count++; + } + } + } + + // for style CHARGE, set charge of all atoms in group + + if (style == CHARGE) { + double value = atof(arg[2]); + double *q = atom->q; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + q[i] = value; + count++; + } + } + + // for style DIPOLE, create unique random number stream for each proc + // set dipole moment of each atom in group to random orientation + // dipole length is determined by dipole type array + + if (style == DIPOLE) { + int ivalue = atoi(arg[2]); + if (ivalue <= 0) error->all("Invalid random number seed in set command"); + double msq,scale; + RanPark *random = new RanPark(ivalue + comm->me); + for (int i = 0; i < WARMUP; i++) random->uniform(); + int *type = atom->type; + double *dipole = atom->dipole; + double **mu = atom->mu; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + mu[i][0] = random->uniform() - 0.5; + mu[i][1] = random->uniform() - 0.5; + mu[i][2] = random->uniform() - 0.5; + msq = mu[i][0]*mu[i][0] + mu[i][1]*mu[i][1] + mu[i][2]*mu[i][2]; + scale = dipole[type[i]]/sqrt(msq); + mu[i][0] *= scale; + mu[i][1] *= scale; + mu[i][2] *= scale; + count++; + } + delete random; + } + + // for style ONEATOM, set attribute of single atom ID + + if (style == ONEATOM) { + int *tag = atom->tag; + for (int i = 0; i < nlocal; i++) + if (atomid == tag[i]) { + if (strcmp(arg[1],"type") == 0) { + atom->type[i] = atoi(arg[2]); + if (atom->type[i] < 1 || atom->type[i] > atom->ntypes) + error->one("Invalid type in set command"); + } else if (strcmp(arg[1],"mol") == 0) atom->molecule[i] = atoi(arg[2]); + else if (strcmp(arg[1],"x") == 0) atom->x[i][0] = atof(arg[2]); + else if (strcmp(arg[1],"y") == 0) atom->x[i][1] = atof(arg[2]); + else if (strcmp(arg[1],"z") == 0) atom->x[i][2] = atof(arg[2]); + else if (strcmp(arg[1],"vx") == 0) atom->v[i][0] = atof(arg[2]); + else if (strcmp(arg[1],"vy") == 0) atom->v[i][1] = atof(arg[2]); + else if (strcmp(arg[1],"vz") == 0) atom->v[i][2] = atof(arg[2]); + else if (strcmp(arg[1],"q") == 0) atom->q[i] = atof(arg[2]); + else error->one("Illegal set command"); + count++; + } + } + + // statistics + + int allcount; + MPI_Allreduce(&count,&allcount,1,MPI_INT,MPI_SUM,world); + + if (comm->me == 0) { + if (screen) fprintf(screen," %d settings made\n",allcount); + if (logfile) fprintf(logfile," %d settings made\n",allcount); + } +} diff --git a/src/set.h b/src/set.h new file mode 100644 index 0000000000000000000000000000000000000000..e8b735af972909152e35d4dd97879f702a34e0a4 --- /dev/null +++ b/src/set.h @@ -0,0 +1,26 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef SET_H +#define SET_H + +#include "lammps.h" + +class Set : public LAMMPS { + public: + Set() {} + ~Set() {} + void command(int, char **); +}; + +#endif diff --git a/src/shell.cpp b/src/shell.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0e74621e63f2947f1f43a487bb9d1c91103a47b3 --- /dev/null +++ b/src/shell.cpp @@ -0,0 +1,54 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "unistd.h" +#include "sys/stat.h" +#include "shell.h" +#include "comm.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +void Shell::command(int narg, char **arg) +{ + if (narg < 1) error->all("Illegal shell command"); + + if (strcmp(arg[0],"cd") == 0) { + if (narg != 2) error->all("Illegal shell command"); + chdir(arg[1]); + + } else if (strcmp(arg[0],"mkdir") == 0) { + if (narg < 2) error->all("Illegal shell command"); + if (comm->me == 0) + for (int i = 1; i < narg; i++) + mkdir(arg[i], S_IRWXU | S_IRGRP | S_IXGRP); + + } else if (strcmp(arg[0],"mv") == 0) { + if (narg != 3) error->all("Illegal shell command"); + if (comm->me == 0) rename(arg[1],arg[2]); + + } else if (strcmp(arg[0],"rm") == 0) { + if (narg < 2) error->all("Illegal shell command"); + if (comm->me == 0) + for (int i = 1; i < narg; i++) + unlink(arg[i]); + + } else if (strcmp(arg[0],"rmdir") == 0) { + if (narg < 2) error->all("Illegal shell command"); + if (comm->me == 0) + for (int i = 1; i < narg; i++) + rmdir(arg[i]); + + } else error->all("Illegal shell command"); +} diff --git a/src/shell.h b/src/shell.h new file mode 100644 index 0000000000000000000000000000000000000000..ddfc35783cdaa92310caa0a624731ec6903ef30e --- /dev/null +++ b/src/shell.h @@ -0,0 +1,26 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef SHELL_H +#define SHELL_H + +#include "lammps.h" + +class Shell : public LAMMPS { + public: + Shell() {} + ~Shell() {} + void command(int, char **); +}; + +#endif diff --git a/src/special.cpp b/src/special.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d2e984d34b55f2ba23dbfeecf69ce6ec6a9796e2 --- /dev/null +++ b/src/special.cpp @@ -0,0 +1,686 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "stdio.h" +#include "special.h" +#include "atom.h" +#include "force.h" +#include "memory.h" +#include "error.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +Special::Special() +{ + MPI_Comm_rank(world,&me); + MPI_Comm_size(world,&nprocs); + + onetwo = onethree = onefour = NULL; +} + +/* ---------------------------------------------------------------------- */ + +Special::~Special() +{ + memory->destroy_2d_int_array(onetwo); + memory->destroy_2d_int_array(onethree); + memory->destroy_2d_int_array(onefour); +} + +/* ---------------------------------------------------------------------- + create 1-2, 1-3, 1-4 lists of topology neighbors + store in onetwo, onethree, onefour for each atom + store 3 counters in nspecial[i] +------------------------------------------------------------------------- */ + +void Special::build() +{ + int i,j,k,m,n,loop,size,original; + int num12,num13,num14; + int max,maxall,messtag,nbuf,nbufmax; + int *buf,*bufcopy,*count; + MPI_Request request; + MPI_Status status; + + MPI_Barrier(world); + + int nlocal = atom->nlocal; + + int *tag = atom->tag; + int *num_bond = atom->num_bond; + int **bond_atom = atom->bond_atom; + int **nspecial = atom->nspecial; + + if (me == 0 && screen) fprintf(screen,"Finding 1-2 1-3 1-4 neighbors ...\n"); + + // setup ring of procs + + int next = me + 1; + int prev = me -1; + if (next == nprocs) next = 0; + if (prev < 0) prev = nprocs - 1; + + // initialize nspecial counters to 0 + + for (i = 0; i < nlocal; i++) { + nspecial[i][0] = 0; + nspecial[i][1] = 0; + nspecial[i][2] = 0; + } + + // ----------------------------------------------------- + // compute nspecial[i][0] = # of 1-2 neighbors of atom i + // ----------------------------------------------------- + + // bond partners stored by atom itself + + for (i = 0; i < nlocal; i++) nspecial[i][0] = num_bond[i]; + + // if newton_bond off, then done + // else only counted 1/2 of all bonds, so count other half + + if (force->newton_bond) { + + // nbufmax = largest buffer needed to hold info from any proc + // info for each atom = global tag of 2nd atom in each bond + + nbuf = 0; + for (i = 0; i < nlocal; i++) nbuf += num_bond[i]; + MPI_Allreduce(&nbuf,&nbufmax,1,MPI_INT,MPI_MAX,world); + + buf = new int[nbufmax]; + bufcopy = new int[nbufmax]; + + // fill buffer with global tags of bond partners of my atoms + + size = 0; + for (i = 0; i < nlocal; i++) + for (j = 0; j < num_bond[i]; j++) + buf[size++] = bond_atom[i][j]; + + // cycle buffer around ring of procs back to self + // when receive buffer, scan tags for atoms I own + // when find one, increment nspecial count for that atom + + messtag = 1; + for (loop = 0; loop < nprocs; loop++) { + for (i = 0; i < size; i++) { + m = atom->map(buf[i]); + if (m >= 0 && m < nlocal) nspecial[m][0]++; + } + if (me != next) { + MPI_Irecv(bufcopy,nbufmax,MPI_INT,prev,messtag,world,&request); + MPI_Send(buf,size,MPI_INT,next,messtag,world); + MPI_Wait(&request,&status); + MPI_Get_count(&status,MPI_INT,&size); + for (j = 0; j < size; j++) buf[j] = bufcopy[j]; + } + } + + delete [] buf; + delete [] bufcopy; + } + + // ---------------------------------------------------- + // create onetwo[i] = list of 1-2 neighbors for atom i + // ---------------------------------------------------- + + max = 0; + for (i = 0; i < nlocal; i++) max = MAX(max,nspecial[i][0]); + + MPI_Allreduce(&max,&maxall,1,MPI_INT,MPI_MAX,world); + + if (me == 0) { + if (screen) fprintf(screen," %d = max # of 1-2 neighbors\n",maxall); + if (logfile) fprintf(logfile," %d = max # of 1-2 neighbors\n",maxall); + } + + onetwo = memory->create_2d_int_array(nlocal,maxall,"special:onetwo"); + + // count = accumulating counter + + count = new int[nlocal]; + for (i = 0; i < nlocal; i++) count[i] = 0; + + // add bond partners stored by atom to onetwo list + + for (i = 0; i < nlocal; i++) + for (j = 0; j < num_bond[i]; j++) + onetwo[i][count[i]++] = bond_atom[i][j]; + + // if newton_bond off, then done + // else only stored 1/2 of all bonds, so store other half + + if (force->newton_bond) { + + // nbufmax = largest buffer needed to hold info from any proc + // info for each atom = 2 global tags in each bond + + nbuf = 0; + for (i = 0; i < nlocal; i++) nbuf += 2*num_bond[i]; + MPI_Allreduce(&nbuf,&nbufmax,1,MPI_INT,MPI_MAX,world); + + buf = new int[nbufmax]; + bufcopy = new int[nbufmax]; + + // fill buffer with global tags of both atoms in bond + + size = 0; + for (i = 0; i < nlocal; i++) + for (j = 0; j < num_bond[i]; j++) { + buf[size++] = tag[i]; + buf[size++] = bond_atom[i][j]; + } + + // cycle buffer around ring of procs back to self + // when receive buffer, scan 2nd-atom tags for atoms I own + // when find one, add 1st-atom tag to onetwo list for 2nd atom + + messtag = 2; + for (loop = 0; loop < nprocs; loop++) { + for (i = 1; i < size; i += 2) { + m = atom->map(buf[i]); + if (m >= 0 && m < nlocal) onetwo[m][count[m]++] = buf[i-1]; + } + if (me != next) { + MPI_Irecv(bufcopy,nbufmax,MPI_INT,prev,messtag,world,&request); + MPI_Send(buf,size,MPI_INT,next,messtag,world); + MPI_Wait(&request,&status); + MPI_Get_count(&status,MPI_INT,&size); + for (j = 0; j < size; j++) buf[j] = bufcopy[j]; + } + } + + delete [] buf; + delete [] bufcopy; + } + + delete [] count; + + // ----------------------------------------------------- + // done if special_bonds for 1-3, 1-4 are set to 1.0 + // ----------------------------------------------------- + + if (force->special_lj[2] == 1.0 && force->special_coul[2] == 1.0 && + force->special_lj[3] == 1.0 && force->special_coul[3] == 1.0) { + combine(); + return; + } + + // ----------------------------------------------------- + // compute nspecial[i][1] = # of 1-3 neighbors of atom i + // ----------------------------------------------------- + + // nbufmax = largest buffer needed to hold info from any proc + // info for each atom = 2 scalars + list of 1-2 neighbors + + nbuf = 0; + for (i = 0; i < nlocal; i++) nbuf += 2 + nspecial[i][0]; + MPI_Allreduce(&nbuf,&nbufmax,1,MPI_INT,MPI_MAX,world); + + buf = new int[nbufmax]; + bufcopy = new int[nbufmax]; + + // fill buffer with: + // (1) = counter for 1-3 neighbors, initialized to 0 + // (2) = # of 1-2 neighbors + // (3:N) = list of 1-2 neighbors + + size = 0; + for (i = 0; i < nlocal; i++) { + buf[size++] = 0; + buf[size++] = nspecial[i][0]; + for (j = 0; j < nspecial[i][0]; j++) buf[size++] = onetwo[i][j]; + } + + // cycle buffer around ring of procs back to self + // when receive buffer, scan list of 1-2 neighbors for atoms I own + // when find one, increment 1-3 count by # of 1-2 neighbors of my atom, + // subtracting one since my list will contain original atom + + messtag = 3; + for (loop = 0; loop < nprocs; loop++) { + i = 0; + while (i < size) { + n = buf[i]; + num12 = buf[i+1]; + for (j = 0; j < num12; j++) { + m = atom->map(buf[i+2+j]); + if (m >= 0 && m < nlocal) n += nspecial[m][0] - 1; + } + buf[i] = n; + i += 2 + num12; + } + if (me != next) { + MPI_Irecv(bufcopy,nbufmax,MPI_INT,prev,messtag,world,&request); + MPI_Send(buf,size,MPI_INT,next,messtag,world); + MPI_Wait(&request,&status); + MPI_Get_count(&status,MPI_INT,&size); + for (j = 0; j < size; j++) buf[j] = bufcopy[j]; + } + } + + // extract count from buffer that has cycled back to me + // nspecial[i][1] = # of 1-3 neighbors of atom i + + j = 0; + for (i = 0; i < nlocal; i++) { + nspecial[i][1] = buf[j]; + j += 2 + nspecial[i][0]; + } + + delete [] buf; + delete [] bufcopy; + + // ---------------------------------------------------- + // create onethree[i] = list of 1-3 neighbors for atom i + // ---------------------------------------------------- + + max = 0; + for (i = 0; i < nlocal; i++) max = MAX(max,nspecial[i][1]); + + MPI_Allreduce(&max,&maxall,1,MPI_INT,MPI_MAX,world); + + if (me == 0) { + if (screen) fprintf(screen," %d = max # of 1-3 neighbors\n",maxall); + if (logfile) fprintf(logfile," %d = max # of 1-3 neighbors\n",maxall); + } + + onethree = memory->create_2d_int_array(nlocal,maxall,"special:onethree"); + + // nbufmax = largest buffer needed to hold info from any proc + // info for each atom = 4 scalars + list of 1-2 neighs + list of 1-3 neighs + + nbuf = 0; + for (i = 0; i < nlocal; i++) nbuf += 4 + nspecial[i][0] + nspecial[i][1]; + MPI_Allreduce(&nbuf,&nbufmax,1,MPI_INT,MPI_MAX,world); + + buf = new int[nbufmax]; + bufcopy = new int[nbufmax]; + + // fill buffer with: + // (1) = global tag of original atom + // (2) = # of 1-2 neighbors + // (3) = # of 1-3 neighbors + // (4) = counter for 1-3 neighbors, initialized to 0 + // (5:N) = list of 1-2 neighbors + // (N+1:2N) space for list of 1-3 neighbors + + size = 0; + for (i = 0; i < nlocal; i++) { + buf[size++] = tag[i]; + buf[size++] = nspecial[i][0]; + buf[size++] = nspecial[i][1]; + buf[size++] = 0; + for (j = 0; j < nspecial[i][0]; j++) buf[size++] = onetwo[i][j]; + size += nspecial[i][1]; + } + + // cycle buffer around ring of procs back to self + // when receive buffer, scan list of 1-2 neighbors for atoms I own + // when find one, add its neighbors to 1-3 list + // increment the count in buf(i+4) + // exclude the atom whose tag = original + // this process may include duplicates but they will be culled later + + messtag = 4; + for (loop = 0; loop < nprocs; loop++) { + i = 0; + while (i < size) { + original = buf[i]; + num12 = buf[i+1]; + num13 = buf[i+2]; + n = buf[i+3]; + for (j = 0; j < num12; j++) { + m = atom->map(buf[i+4+j]); + if (m >= 0 && m < nlocal) + for (k = 0; k < nspecial[m][0]; k++) + if (onetwo[m][k] != original) + buf[i+4+num12+(n++)] = onetwo[m][k]; + } + buf[i+3] = n; + i += 4 + num12 + num13; + } + if (me != next) { + MPI_Irecv(bufcopy,nbufmax,MPI_INT,prev,messtag,world,&request); + MPI_Send(buf,size,MPI_INT,next,messtag,world); + MPI_Wait(&request,&status); + MPI_Get_count(&status,MPI_INT,&size); + for (j = 0; j < size; j++) buf[j] = bufcopy[j]; + } + } + + // fill onethree with buffer values that have been returned to me + // sanity check: accumulated buf[i+3] count should equal + // nspecial[i][1] for each atom + + j = 0; + for (i = 0; i < nlocal; i++) { + if (buf[j+3] != nspecial[i][1]) + error->one("1-3 bond count is inconsistent"); + j += 4 + nspecial[i][0]; + for (k = 0; k < nspecial[i][1]; k++) + onethree[i][k] = buf[j++]; + } + + delete [] buf; + delete [] bufcopy; + + // done if special_bonds for 1-4 are set to 1.0 + + if (force->special_lj[3] == 1.0 && force->special_coul[3] == 1.0) { + combine(); + return; + } + + // ----------------------------------------------------- + // compute nspecial[i][2] = # of 1-4 neighbors of atom i + // ----------------------------------------------------- + + // nbufmax = largest buffer needed to hold info from any proc + // info for each atom = 2 scalars + list of 1-3 neighbors + + nbuf = 0; + for (i = 0; i < nlocal; i++) nbuf += 2 + nspecial[i][1]; + MPI_Allreduce(&nbuf,&nbufmax,1,MPI_INT,MPI_MAX,world); + + buf = new int[nbufmax]; + bufcopy = new int[nbufmax]; + + // fill buffer with: + // (1) = counter for 1-4 neighbors, initialized to 0 + // (2) = # of 1-3 neighbors + // (3:N) = list of 1-3 neighbors + + size = 0; + for (i = 0; i < nlocal; i++) { + buf[size++] = 0; + buf[size++] = nspecial[i][1]; + for (j = 0; j < nspecial[i][1]; j++) buf[size++] = onethree[i][j]; + } + + // cycle buffer around ring of procs back to self + // when receive buffer, scan list of 1-3 neighbors for atoms I own + // when find one, increment 1-4 count by # of 1-2 neighbors of my atom + // may include duplicates and original atom but they will be culled later + + messtag = 5; + for (loop = 0; loop < nprocs; loop++) { + i = 0; + while (i < size) { + n = buf[i]; + num13 = buf[i+1]; + for (j = 0; j < num13; j++) { + m = atom->map(buf[i+2+j]); + if (m >= 0 && m < nlocal) n += nspecial[m][0]; + } + buf[i] = n; + i += 2 + num13; + } + if (me != next) { + MPI_Irecv(bufcopy,nbufmax,MPI_INT,prev,messtag,world,&request); + MPI_Send(buf,size,MPI_INT,next,messtag,world); + MPI_Wait(&request,&status); + MPI_Get_count(&status,MPI_INT,&size); + for (j = 0; j < size; j++) buf[j] = bufcopy[j]; + } + } + + // extract count from buffer that has cycled back to me + // nspecial[i][2] = # of 1-4 neighbors of atom i + + j = 0; + for (i = 0; i < nlocal; i++) { + nspecial[i][2] = buf[j]; + j += 2 + nspecial[i][1]; + } + + delete [] buf; + delete [] bufcopy; + + // ---------------------------------------------------- + // create onefour[i] = list of 1-4 neighbors for atom i + // ---------------------------------------------------- + + max = 0; + for (i = 0; i < nlocal; i++) max = MAX(max,nspecial[i][2]); + + MPI_Allreduce(&max,&maxall,1,MPI_INT,MPI_MAX,world); + + if (me == 0) { + if (screen) fprintf(screen," %d = max # of 1-4 neighbors\n",maxall); + if (logfile) fprintf(logfile," %d = max # of 1-4 neighbors\n",maxall); + } + + onefour = memory->create_2d_int_array(nlocal,maxall,"special:onefour"); + + // nbufmax = largest buffer needed to hold info from any proc + // info for each atom = 3 scalars + list of 1-3 neighs + list of 1-4 neighs + + nbuf = 0; + for (i = 0; i < nlocal; i++) + nbuf += 3 + nspecial[i][1] + nspecial[i][2]; + MPI_Allreduce(&nbuf,&nbufmax,1,MPI_INT,MPI_MAX,world); + + buf = new int[nbufmax]; + bufcopy = new int[nbufmax]; + + // fill buffer with: + // (1) = # of 1-3 neighbors + // (2) = # of 1-4 neighbors + // (3) = counter for 1-4 neighbors, initialized to 0 + // (4:N) = list of 1-3 neighbors + // (N+1:2N) space for list of 1-4 neighbors + + size = 0; + for (i = 0; i < nlocal; i++) { + buf[size++] = nspecial[i][1]; + buf[size++] = nspecial[i][2]; + buf[size++] = 0; + for (j = 0; j < nspecial[i][1]; j++) buf[size++] = onethree[i][j]; + size += nspecial[i][2]; + } + + // cycle buffer around ring of procs back to self + // when receive buffer, scan list of 1-3 neighbors for atoms I own + // when find one, add its neighbors to 1-4 list + // incrementing the count in buf(i+4) + // this process may include duplicates but they will be culled later + + messtag = 6; + for (loop = 0; loop < nprocs; loop++) { + i = 0; + while (i < size) { + num13 = buf[i]; + num14 = buf[i+1]; + n = buf[i+2]; + for (j = 0; j < num13; j++) { + m = atom->map(buf[i+3+j]); + if (m >= 0 && m < nlocal) + for (k = 0; k < nspecial[m][0]; k++) + buf[i+3+num13+(n++)] = onetwo[m][k]; + } + buf[i+2] = n; + i += 3 + num13 + num14; + } + if (me != next) { + MPI_Irecv(bufcopy,nbufmax,MPI_INT,prev,messtag,world,&request); + MPI_Send(buf,size,MPI_INT,next,messtag,world); + MPI_Wait(&request,&status); + MPI_Get_count(&status,MPI_INT,&size); + for (j = 0; j < size; j++) buf[j] = bufcopy[j]; + } + } + + // fill onefour with buffer values that have been returned to me + // sanity check: accumulated buf[i+2] count should equal + // nspecial[i][2] for each atom + + j = 0; + for (i = 0; i < nlocal; i++) { + if (buf[j+2] != nspecial[i][2]) + error->one("1-4 bond count is inconsistent"); + j += 3 + nspecial[i][1]; + for (k = 0; k < nspecial[i][2]; k++) + onefour[i][k] = buf[j++]; + } + + delete [] buf; + delete [] bufcopy; + + combine(); +} + +/* ---------------------------------------------------------------------- + concatenate onetwo, onethree, onefour into master atom->special list + remove duplicates + convert nspecial[0], nspecial[1], nspecial[2] into cummulative counters +------------------------------------------------------------------------- */ + +void Special::combine() +{ + int i,j,m; + + int me; + MPI_Comm_rank(world,&me); + + int nlocal = atom->nlocal; + int **nspecial = atom->nspecial; + int *tag = atom->tag; + + // ---------------------------------------------------- + // compute culled maxspecial = max # of special neighs of any atom + // ---------------------------------------------------- + + // clear map so it can be used as scratch space + + atom->map_clear(); + + // unique = # of unique nspecial neighbors of one atom + // cull duplicates using map to check for them + // exclude original atom explicitly + // must re-clear map for each atom + + int unique; + int maxspecial = 0; + + for (i = 0; i < nlocal; i++) { + + unique = 0; + atom->map_one(tag[i],0); + + for (j = 0; j < nspecial[i][0]; j++) { + m = onetwo[i][j]; + if (atom->map(m) < 0) { + unique++; + atom->map_one(m,0); + } + } + for (j = 0; j < nspecial[i][1]; j++) { + m = onethree[i][j]; + if (atom->map(m) < 0) { + unique++; + atom->map_one(m,0); + } + } + for (j = 0; j < nspecial[i][2]; j++) { + m = onefour[i][j]; + if (atom->map(m) < 0) { + unique++; + atom->map_one(m,0); + } + } + + maxspecial = MAX(maxspecial,unique); + + atom->map_one(tag[i],-1); + for (j = 0; j < nspecial[i][0]; j++) atom->map_one(onetwo[i][j],-1); + for (j = 0; j < nspecial[i][1]; j++) atom->map_one(onethree[i][j],-1); + for (j = 0; j < nspecial[i][2]; j++) atom->map_one(onefour[i][j],-1); + + } + + // compute global maxspecial, must be at least 1 + // allocate correct special array with same nmax, new maxspecial + // previously allocated one must be destroyed + + MPI_Allreduce(&maxspecial,&atom->maxspecial,1,MPI_INT,MPI_MAX,world); + atom->maxspecial = MAX(atom->maxspecial,1); + + if (me == 0) { + if (screen) + fprintf(screen," %d = max # of special neighbors\n",atom->maxspecial); + if (logfile) + fprintf(logfile," %d = max # of special neighbors\n",atom->maxspecial); + } + + memory->destroy_2d_int_array(atom->special); + + atom->special = + memory->create_2d_int_array(atom->nmax,atom->maxspecial,"atom:special"); + int **special = atom->special; + + // ---------------------------------------------------- + // fill special array with 1-2, 1-3, 1-4 neighs for each atom + // ---------------------------------------------------- + + // again use map to cull duplicates + // exclude original atom explicitly + // adjust nspecial[i] values to reflect removed duplicates + // nspecial[i][1] and nspecial[i][2] now become cummulative counters + + for (i = 0; i < nlocal; i++) { + + unique = 0; + atom->map_one(tag[i],0); + + for (j = 0; j < nspecial[i][0]; j++) { + m = onetwo[i][j]; + if (atom->map(m) < 0) { + special[i][unique++] = m; + atom->map_one(m,0); + } + } + nspecial[i][0] = unique; + + for (j = 0; j < nspecial[i][1]; j++) { + m = onethree[i][j]; + if (atom->map(m) < 0) { + special[i][unique++] = m; + atom->map_one(m,0); + } + } + nspecial[i][1] = unique; + + for (j = 0; j < nspecial[i][2]; j++) { + m = onefour[i][j]; + if (atom->map(m) < 0) { + special[i][unique++] = m; + atom->map_one(m,0); + } + } + nspecial[i][2] = unique; + + atom->map_one(tag[i],-1); + for (j = 0; j < nspecial[i][2]; j++) atom->map_one(special[i][j],-1); + + } + + // re-create map + + atom->map_set(); +} diff --git a/src/special.h b/src/special.h new file mode 100644 index 0000000000000000000000000000000000000000..3e495fb5734e06b3c5280e0e78db5aca5b2c7b24 --- /dev/null +++ b/src/special.h @@ -0,0 +1,32 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef SPECIAL_H +#define SPECIAL_H + +#include "lammps.h" + +class Special : public LAMMPS { + public: + Special(); + ~Special(); + void build(); + + private: + int me,nprocs; + int **onetwo,**onethree,**onefour; + + void combine(); +}; + +#endif diff --git a/src/style.h b/src/style.h new file mode 100644 index 0000000000000000000000000000000000000000..36c22e1985cc1e99df50f4012649f5c4b3048c13 --- /dev/null +++ b/src/style.h @@ -0,0 +1,289 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifdef AngleInclude +#endif + +#ifdef AngleClass +#endif + +#ifdef AtomInclude +#include "atom_atomic.h" +#include "atom_charge.h" +#include "atom_hybrid.h" +#endif + +#ifdef AtomClass +AtomStyle(atomic,AtomAtomic) +AtomStyle(charge,AtomCharge) +AtomStyle(hybrid,AtomHybrid) +# endif + +#ifdef BondInclude +#endif + +#ifdef BondClass +#endif + +#ifdef CommandInclude +#include "create_atoms.h" +#include "create_box.h" +#include "delete_atoms.h" +#include "delete_bonds.h" +#include "displace_atoms.h" +#include "minimize.h" +#include "read_data.h" +#include "read_restart.h" +#include "replicate.h" +#include "run.h" +#include "set.h" +#include "shell.h" +#include "temper.h" +#include "velocity.h" +#include "write_restart.h" +#endif + +#ifdef CommandClass +CommandStyle(create_atoms,CreateAtoms) +CommandStyle(create_box,CreateBox) +CommandStyle(delete_atoms,DeleteAtoms) +CommandStyle(delete_bonds,DeleteBonds) +CommandStyle(displace_atoms,DisplaceAtoms) +CommandStyle(minimize,Minimize) +CommandStyle(read_data,ReadData) +CommandStyle(read_restart,ReadRestart) +CommandStyle(replicate,Replicate) +CommandStyle(run,Run) +CommandStyle(set,Set) +CommandStyle(shell,Shell) +CommandStyle(temper,Temper) +CommandStyle(velocity,Velocity) +CommandStyle(write_restart,WriteRestart) +#endif + +#ifdef DihedralInclude +#endif + +#ifdef DihedralClass +#endif + +#ifdef DumpInclude +#include "dump_atom.h" +#include "dump_custom.h" +#include "dump_dcd.h" +#include "dump_xyz.h" +#endif + +#ifdef DumpClass +DumpStyle(atom,DumpAtom) +DumpStyle(custom,DumpCustom) +DumpStyle(dcd,DumpDCD) +DumpStyle(xyz,DumpXYZ) +#endif + +#ifdef FixInclude +#include "fix_add_force.h" +#include "fix_ave_force.h" +#include "fix_centro.h" +#include "fix_com.h" +#include "fix_drag.h" +#include "fix_efield.h" +#include "fix_energy.h" +#include "fix_enforce2d.h" +#include "fix_gravity.h" +#include "fix_gyration.h" +#include "fix_indent.h" +#include "fix_langevin.h" +#include "fix_line_force.h" +#include "fix_minimize.h" +#include "fix_msd.h" +#include "fix_momentum.h" +#include "fix_nph.h" +#include "fix_npt.h" +#include "fix_nve.h" +#include "fix_nvt.h" +#include "fix_plane_force.h" +#include "fix_print.h" +#include "fix_orient_fcc.h" +#include "fix_rdf.h" +#include "fix_recenter.h" +#include "fix_respa.h" +#include "fix_rigid.h" +#include "fix_set_force.h" +#include "fix_shake.h" +#include "fix_spring.h" +#include "fix_spring_rg.h" +#include "fix_spring_self.h" +#include "fix_stress.h" +#include "fix_temp_rescale.h" +#include "fix_tmd.h" +#include "fix_uniaxial.h" +#include "fix_viscous.h" +#include "fix_volume_rescale.h" +#include "fix_wall_lj93.h" +#include "fix_wall_reflect.h" +#include "fix_wiggle.h" +#endif + +#ifdef FixClass +FixStyle(addforce,FixAddForce) +FixStyle(aveforce,FixAveForce) +FixStyle(CENTRO,FixCentro) +FixStyle(com,FixCOM) +FixStyle(drag,FixDrag) +FixStyle(efield,FixEfield) +FixStyle(ENERGY,FixEnergy) +FixStyle(enforce2d,FixEnforce2D) +FixStyle(gravity,FixGravity) +FixStyle(gyration,FixGyration) +FixStyle(indent,FixIndent) +FixStyle(langevin,FixLangevin) +FixStyle(lineforce,FixLineForce) +FixStyle(MINIMIZE,FixMinimize) +FixStyle(momentum,FixMomentum) +FixStyle(msd,FixMSD) +FixStyle(nph,FixNPH) +FixStyle(npt,FixNPT) +FixStyle(nve,FixNVE) +FixStyle(nvt,FixNVT) +FixStyle(orient/fcc,FixOrientFCC) +FixStyle(print,FixPrint) +FixStyle(planeforce,FixPlaneForce) +FixStyle(rdf,FixRDF) +FixStyle(recenter,FixRecenter) +FixStyle(RESPA,FixRespa) +FixStyle(rigid,FixRigid) +FixStyle(setforce,FixSetForce) +FixStyle(shake,FixShake) +FixStyle(spring,FixSpring) +FixStyle(spring/rg,FixSpringRG) +FixStyle(spring/self,FixSpringSelf) +FixStyle(STRESS,FixStress) +FixStyle(temp/rescale,FixTempRescale) +FixStyle(tmd,FixTMD) +FixStyle(uniaxial,FixUniaxial) +FixStyle(viscous,FixViscous) +FixStyle(volume/rescale,FixVolRescale) +FixStyle(wall/lj93,FixWallLJ93) +FixStyle(wall/reflect,FixWallReflect) +FixStyle(wiggle,FixWiggle) +#endif + +#ifdef ImproperInclude +#endif + +#ifdef ImproperClass +#endif + +#ifdef IntegrateInclude +#include "respa.h" +#include "verlet.h" +#endif + +#ifdef IntegrateClass +IntegrateStyle(respa,Respa) +IntegrateStyle(verlet,Verlet) +# endif + +#ifdef KSpaceInclude +#endif + +#ifdef KSpaceClass +#endif + +#ifdef MinimizeInclude +#include "min_cg.h" +#include "min_cg_fr.h" +#include "min_sd.h" +#endif + +#ifdef MinimizeClass +MinimizeStyle(cg,MinCG) +MinimizeStyle(cg/fr,MinCGFR) +MinimizeStyle(sd,MinSD) +# endif + +#ifdef PairInclude +#include "pair_buck.h" +#include "pair_buck_coul_cut.h" +#include "pair_hybrid.h" +#include "pair_lj_cut.h" +#include "pair_lj_cut_coul_cut.h" +#include "pair_lj_cut_coul_debye.h" +#include "pair_lj_expand.h" +#include "pair_lj_smooth.h" +#include "pair_morse.h" +#include "pair_soft.h" +#include "pair_table.h" +#include "pair_yukawa.h" +#endif + +#ifdef PairClass +PairStyle(buck,PairBuck) +PairStyle(buck/coul/cut,PairBuckCoulCut) +PairStyle(hybrid,PairHybrid) +PairStyle(lj/cut,PairLJCut) +PairStyle(lj/cut/coul/cut,PairLJCutCoulCut) +PairStyle(lj/cut/coul/debye,PairLJCutCoulDebye) +PairStyle(lj/expand,PairLJExpand) +PairStyle(lj/smooth,PairLJSmooth) +PairStyle(morse,PairMorse) +PairStyle(soft,PairSoft) +PairStyle(table,PairTable) +PairStyle(yukawa,PairYukawa) +#endif + +#ifdef RegionInclude +#include "region_block.h" +#include "region_cylinder.h" +#include "region_intersect.h" +#include "region_sphere.h" +#include "region_union.h" +#endif + +#ifdef RegionClass +RegionStyle(block,RegBlock) +RegionStyle(cylinder,RegCylinder) +RegionStyle(intersect,RegIntersect) +RegionStyle(sphere,RegSphere) +RegionStyle(union,RegUnion) +#endif + +#ifdef TempInclude +#include "temp_full.h" +#include "temp_partial.h" +#include "temp_ramp.h" +#include "temp_region.h" +#endif + +#ifdef TempClass +TempStyle(full,TempFull) +TempStyle(partial,TempPartial) +TempStyle(ramp,TempRamp) +TempStyle(region,TempRegion) +#endif + +// style files for optional packages + +#include "style_class2.h" +#include "style_dpd.h" +#include "style_granular.h" +#include "style_kspace.h" +#include "style_manybody.h" +#include "style_molecule.h" +#include "style_poems.h" +#include "style_xtc.h" + +// user add-ons + +#include "style_user.h" diff --git a/src/style_class2.h b/src/style_class2.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/style_dpd.h b/src/style_dpd.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/style_granular.h b/src/style_granular.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/style_kspace.h b/src/style_kspace.h new file mode 100644 index 0000000000000000000000000000000000000000..c6ea29b73ba6eed64bbff0f68bafbfe53a3682af --- /dev/null +++ b/src/style_kspace.h @@ -0,0 +1,38 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifdef KSpaceInclude +#include "ewald.h" +#include "pppm.h" +#include "pppm_tip4p.h" +#endif + +#ifdef KSpaceClass +KSpaceStyle(ewald,Ewald) +KSpaceStyle(pppm,PPPM) +KSpaceStyle(pppm/tip4p,PPPMTIP4P) +#endif + +#ifdef PairInclude +#include "pair_buck_coul_long.h" +#include "pair_lj_cut_coul_long.h" +#include "pair_lj_cut_coul_long_tip4p.h" +#include "pair_lj_charmm_coul_long.h" +#endif + +#ifdef PairClass +PairStyle(buck/coul/long,PairBuckCoulLong) +PairStyle(lj/cut/coul/long,PairLJCutCoulLong) +PairStyle(lj/cut/coul/long/tip4p,PairLJCutCoulLongTIP4P) +PairStyle(lj/charmm/coul/long,PairLJCharmmCoulLong) +#endif diff --git a/src/style_manybody.h b/src/style_manybody.h new file mode 100644 index 0000000000000000000000000000000000000000..659f63ca83f23addc633e3ddc1159f7c910240a0 --- /dev/null +++ b/src/style_manybody.h @@ -0,0 +1,24 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifdef PairInclude +#include "pair_eam.h" +#include "pair_eam_alloy.h" +#include "pair_eam_fs.h" +#endif + +#ifdef PairClass +PairStyle(eam,PairEAM) +PairStyle(eam/alloy,PairEAMAlloy) +PairStyle(eam/fs,PairEAMFS) +#endif diff --git a/src/style_molecule.h b/src/style_molecule.h new file mode 100644 index 0000000000000000000000000000000000000000..1da512819deb46125711b59dfbab8f93311e48fc --- /dev/null +++ b/src/style_molecule.h @@ -0,0 +1,116 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifdef AngleInclude +#include "angle_charmm.h" +#include "angle_cosine.h" +#include "angle_cosine_squared.h" +#include "angle_harmonic.h" +#include "angle_hybrid.h" +#endif + +#ifdef AngleClass +AngleStyle(charmm,AngleCharmm) +AngleStyle(cosine,AngleCosine) +AngleStyle(cosine/squared,AngleCosineSquared) +AngleStyle(harmonic,AngleHarmonic) +AngleStyle(hybrid,AngleHybrid) +#endif + +#ifdef AtomInclude +#include "atom_angle.h" +#include "atom_bond.h" +#include "atom_full.h" +#include "atom_molecular.h" +#endif + +#ifdef AtomClass +AtomStyle(angle,AtomAngle) +AtomStyle(bond,AtomBond) +AtomStyle(full,AtomFull) +AtomStyle(molecular,AtomMolecular) +#endif + +#ifdef BondInclude +#include "bond_fene.h" +#include "bond_fene_expand.h" +#include "bond_harmonic.h" +#include "bond_hybrid.h" +#include "bond_morse.h" +#include "bond_nonlinear.h" +#include "bond_quartic.h" +#endif + +#ifdef BondClass +BondStyle(fene,BondFENE) +BondStyle(fene/expand,BondFENEExpand) +BondStyle(harmonic,BondHarmonic) +BondStyle(hybrid,BondHybrid) +BondStyle(morse,BondMorse) +BondStyle(nonlinear,BondNonlinear) +BondStyle(quartic,BondQuartic) +#endif + +#ifdef DihedralInclude +#include "dihedral_charmm.h" +#include "dihedral_harmonic.h" +#include "dihedral_helix.h" +#include "dihedral_hybrid.h" +#include "dihedral_multi_harmonic.h" +#include "dihedral_opls.h" +#endif + +#ifdef DihedralClass +DihedralStyle(charmm,DihedralCharmm) +DihedralStyle(harmonic,DihedralHarmonic) +DihedralStyle(helix,DihedralHelix) +DihedralStyle(hybrid,DihedralHybrid) +DihedralStyle(multi/harmonic,DihedralMultiHarmonic) +DihedralStyle(opls,DihedralOPLS) +#endif + +#ifdef DumpInclude +#include "dump_bond.h" +#endif + +#ifdef DumpClass +DumpStyle(bond,DumpBond) +#endif + +#ifdef FixInclude +#endif + +#ifdef FixClass +#endif + +#ifdef ImproperInclude +#include "improper_cvff.h" +#include "improper_harmonic.h" +#include "improper_hybrid.h" +#endif + +#ifdef ImproperClass +ImproperStyle(cvff,ImproperCvff) +ImproperStyle(harmonic,ImproperHarmonic) +ImproperStyle(hybrid,ImproperHybrid) +#endif + +#ifdef PairInclude +#include "pair_lj_charmm_coul_charmm.h" +#include "pair_lj_charmm_coul_charmm_implicit.h" +#endif + +#ifdef PairClass +PairStyle(lj/charmm/coul/charmm,PairLJCharmmCoulCharmm) +PairStyle(lj/charmm/coul/charmm/implicit,PairLJCharmmCoulCharmmImplicit) +#endif diff --git a/src/style_poems.h b/src/style_poems.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/style_user.h b/src/style_user.h new file mode 100644 index 0000000000000000000000000000000000000000..213b114efdf217467659ea26e9b2a0b25b06d1c2 --- /dev/null +++ b/src/style_user.h @@ -0,0 +1,100 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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. +------------------------------------------------------------------------- */ + +// add new include files in appropriate Include ifdef +// add new style keywords and class names in appropriate Class ifdef +// see style.h for examples + +#ifdef AngleInclude +#endif + +#ifdef AngleClass +#endif + +#ifdef AtomInclude +#endif + +#ifdef AtomClass +#endif + +#ifdef BondInclude +#endif + +#ifdef BondClass +#endif + +#ifdef CommandInclude +#endif + +#ifdef CommandClass +#endif + +#ifdef DihedralInclude +#endif + +#ifdef DihedralClass +#endif + +#ifdef DumpInclude +#endif + +#ifdef DumpClass +#endif + +#ifdef FixInclude +#endif + +#ifdef FixClass +#endif + +#ifdef ImproperInclude +#endif + +#ifdef ImproperClass +#endif + +#ifdef IntegrateInclude +#endif + +#ifdef IntegrateClass +# endif + +#ifdef KSpaceInclude +#endif + +#ifdef KSpaceClass +#endif + +#ifdef MinimizeInclude +#endif + +#ifdef MinimizeClass +# endif + +#ifdef PairInclude +#endif + +#ifdef PairClass +#endif + +#ifdef RegionInclude +#endif + +#ifdef RegionClass +#endif + +#ifdef TempInclude +#endif + +#ifdef TempClass +#endif diff --git a/src/style_xtc.h b/src/style_xtc.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/system.cpp b/src/system.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1ee3afc87f37b449835a1c89aad23b1ae573185a --- /dev/null +++ b/src/system.cpp @@ -0,0 +1,322 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "string.h" +#include "system.h" +#include "memory.h" +#include "error.h" +#include "universe.h" +#include "input.h" +#include "atom.h" +#include "update.h" +#include "neighbor.h" +#include "comm.h" +#include "domain.h" +#include "force.h" +#include "modify.h" +#include "group.h" +#include "output.h" +#include "timer.h" + +/* ---------------------------------------------------------------------- + allocate fundamental classes (memory, error, universe, input) + parse input switches + initialize communicators, screen & logfile output + input is allocated at end after MPI info is setup +------------------------------------------------------------------------- */ + +void System::open(int narg, char **arg, MPI_Comm communicator) +{ + memory = new Memory; + error = new Error; + universe = new Universe(communicator); + + // parse input switches + + int inflag = 0; + int screenflag = 0; + int logflag = 0; + int iarg = 1; + + while (iarg < narg) { + if (strcmp(arg[iarg],"-partition") == 0) { + if (iarg+1 > narg) + error->universe_all("Invalid command-line argument"); + iarg++; + while (iarg < narg && arg[iarg][0] != '-') { + universe->add_world(arg[iarg]); + iarg++; + } + } else if (strcmp(arg[iarg],"-in") == 0) { + if (iarg+2 > narg) + error->universe_all("Invalid command-line argument"); + inflag = iarg + 1; + iarg += 2; + } else if (strcmp(arg[iarg],"-screen") == 0) { + if (iarg+2 > narg) + error->universe_all("Invalid command-line argument"); + screenflag = iarg + 1; + iarg += 2; + } else if (strcmp(arg[iarg],"-log") == 0) { + if (iarg+2 > narg) + error->universe_all("Invalid command-line argument"); + logflag = iarg + 1; + iarg += 2; + } else if (strcmp(arg[iarg],"-var") == 0) { + if (iarg+3 > narg) + error->universe_all("Invalid command-line argument"); + iarg += 3; + } else if (strcmp(arg[iarg],"-echo") == 0) { + if (iarg+2 > narg) + error->universe_all("Invalid command-line argument"); + iarg += 2; + } else error->universe_all("Invalid command-line argument"); + } + + // if procs was not a command-line switch, universe is one world w/ all procs + + if (universe->nworlds == 0) universe->add_world(NULL); + + // sum of procs in all worlds must equal total # of procs + + if (!universe->consistent()) + error->universe_all("Processor partitions are inconsistent"); + + // multiple-world universe must define input file + + if (universe->nworlds > 1 && inflag == 0) + error->universe_all("Must use -in switch with multiple partitions"); + + // set universe screen and logfile + + if (universe->me == 0) { + if (screenflag == 0) + universe->uscreen = stdout; + else if (strcmp(arg[screenflag],"none") == 0) + universe->uscreen = NULL; + else { + universe->uscreen = fopen(arg[screenflag],"w"); + if (universe->uscreen == NULL) + error->universe_one("Cannot open universe screen file"); + } + if (logflag == 0) { + universe->ulogfile = fopen("log.lammps","w"); + if (universe->ulogfile == NULL) + error->universe_one("Cannot open log.lammps"); + } else if (strcmp(arg[logflag],"none") == 0) + universe->ulogfile = NULL; + else { + universe->ulogfile = fopen(arg[logflag],"w"); + if (universe->ulogfile == NULL) + error->universe_one("Cannot open universe log file"); + } + } + + if (universe->me > 0) { + if (screenflag == 0) universe->uscreen = stdout; + else universe->uscreen = NULL; + universe->ulogfile = NULL; + } + + // universe is single world + // inherit settings from universe + // set world screen, logfile, communicator, infile + // open input script if from file + + if (universe->nworlds == 1) { + screen = universe->uscreen; + logfile = universe->ulogfile; + world = universe->uworld; + infile = NULL; + + if (universe->me == 0) { + if (inflag == 0) infile = stdin; + else infile = fopen(arg[inflag],"r"); + if (infile == NULL) { + char str[128]; + sprintf(str,"Cannot open input script %s",arg[inflag]); + error->one(str); + } + } + + if (universe->me == 0) { + if (screen) fprintf(screen,"LAMMPS (%s)\n",universe->version); + if (logfile) fprintf(logfile,"LAMMPS (%s)\n",universe->version); + } + + // universe is multiple worlds + // split into separate communicators + // set world screen, logfile, communicator, infile + // open input script + + } else { + int me; + MPI_Comm_split(universe->uworld,universe->iworld,0,&world); + MPI_Comm_rank(world,&me); + + if (me == 0) { + if (screenflag == 0) { + char str[32]; + sprintf(str,"screen.%d",universe->iworld); + screen = fopen(str,"w"); + if (screen == NULL) error->one("Cannot open screen file"); + } else if (strcmp(arg[screenflag],"none") == 0) + screen = NULL; + else { + char str[32]; + sprintf(str,"%s.%d",arg[screenflag],universe->iworld); + screen = fopen(str,"w"); + if (screen == NULL) error->one("Cannot open screen file"); + } + } else screen = NULL; + + if (me == 0) { + if (logflag == 0) { + char str[32]; + sprintf(str,"log.lammps.%d",universe->iworld); + logfile = fopen(str,"w"); + if (logfile == NULL) error->one("Cannot open logfile"); + } else if (strcmp(arg[logflag],"none") == 0) + logfile = NULL; + else { + char str[32]; + sprintf(str,"%s.%d",arg[logflag],universe->iworld); + logfile = fopen(str,"w"); + if (logfile == NULL) error->one("Cannot open logfile"); + } + } else logfile = NULL; + + if (me == 0) { + infile = fopen(arg[inflag],"r"); + if (infile == NULL) { + char str[128]; + sprintf(str,"Cannot open input script %s",arg[inflag]); + error->one(str); + } + } else infile = NULL; + + // screen and logfile messages for universe and world + + if (universe->me == 0) { + if (universe->uscreen) { + fprintf(universe->uscreen,"LAMMPS (%s)\n",universe->version); + fprintf(universe->uscreen,"Running on %d partitions of processors\n", + universe->nworlds); + } + if (universe->ulogfile) { + fprintf(universe->ulogfile,"LAMMPS (%s)\n",universe->version); + fprintf(universe->ulogfile,"Running on %d partitions of processors\n", + universe->nworlds); + } + } + + if (me == 0) { + if (screen) { + fprintf(screen,"LAMMPS (%s)\n",universe->version); + fprintf(screen,"Processor partition = %d\n",universe->iworld); + } + if (logfile) { + fprintf(logfile,"LAMMPS (%s)\n",universe->version); + fprintf(logfile,"Processor partition = %d\n",universe->iworld); + } + } + } + + // allocate input class now that MPI is fully setup + + input = new Input(narg,arg); +} + +/* ---------------------------------------------------------------------- + allocate single instance of top-level classes + fundamental classes are allocated in open() + atom is allocated via input script +------------------------------------------------------------------------- */ + +void System::create() +{ + neighbor = new Neighbor; + comm = new Comm; + domain = new Domain; + group = new Group; + force = new Force; // must be after group, to create temperature + modify = new Modify; + output = new Output; // must be after group, so "all" exists + update = new Update; // must be after output, force, neighbor + timer = new Timer; +} + +/* ---------------------------------------------------------------------- + init top-level classes +------------------------------------------------------------------------- */ + +void System::init() +{ + update->init(); + force->init(); + domain->init(); + atom->init(); // atom must come after force: + // atom deletes extra array + // used by fix shear_history::unpack_restart() + // when force->pair->gran_history creates fix ?? + modify->init(); // modify must come after update, force, atom + neighbor->init(); // neighbor must come after force, modify (due to min) + output->init(); // output must come after domain, force, modify + comm->init(); // comm must come after force, modify + timer->init(); +} + +/* ---------------------------------------------------------------------- + delete single instance of top-level classes + fundamental classes are deleted in close() +------------------------------------------------------------------------- */ + +void System::destroy() +{ + if (atom) delete atom; + atom = NULL; + + delete update; + delete neighbor; + delete comm; + delete domain; + delete force; + delete group; + delete output; + delete modify; // modify must come after output, force, update + delete timer; +} + +/* ---------------------------------------------------------------------- + shutdown system + close screen and log files in world and universe + output files were already closed in system::destroy() + delete fundamental classes +------------------------------------------------------------------------- */ + +void System::close() +{ + if (universe->nworlds > 1) { + if (screen && screen != stdout) fclose(screen); + if (logfile) fclose(logfile); + } + if (universe->ulogfile) fclose(universe->ulogfile); + + if (world != universe->uworld) MPI_Comm_free(&world); + + delete input; + delete universe; + delete error; + delete memory; +} diff --git a/src/system.h b/src/system.h new file mode 100644 index 0000000000000000000000000000000000000000..7d2735444d2ef2863715629dc00fc0e4b6d9f0f0 --- /dev/null +++ b/src/system.h @@ -0,0 +1,29 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef SYSTEM_H +#define SYSTEM_H + +#include "mpi.h" +#include "lammps.h" + +class System : public LAMMPS { + public: + void open(int, char **, MPI_Comm); + void create(); + void init(); + void destroy(); + void close(); +}; + +#endif diff --git a/src/temp_full.cpp b/src/temp_full.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d55400d2040452e24634068aff790bb8ff23a9ac --- /dev/null +++ b/src/temp_full.cpp @@ -0,0 +1,107 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "temp_full.h" +#include "atom.h" +#include "force.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +TempFull::TempFull(int narg, char **arg) : Temperature(narg, arg) {} + +/* ---------------------------------------------------------------------- */ + +void TempFull::init() +{ + count_atoms(); + count_fix(); + dof = force->dimension * ncount; + dof -= extra_dof + fix_dof; + if (ncount > 0) tfactor = force->mvv2e / (dof * force->boltz); + else tfactor = 0.0; +} + +/* ---------------------------------------------------------------------- */ + +double TempFull::compute() +{ + double **v = atom->v; + double *mass = atom->mass; + double *rmass = atom->rmass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double t = 0.0; + + if (atom->mass_require) { + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) + t += (v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]) * + mass[type[i]]; + } else { + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) + t += (v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]) * rmass[i]; + } + + MPI_Allreduce(&t,&t_total,1,MPI_DOUBLE,MPI_SUM,world); + t_total *= tfactor; + return t_total; +} + +/* ---------------------------------------------------------------------- */ + +void TempFull::tensor() +{ + int i; + + double **v = atom->v; + double *mass = atom->mass; + double *rmass = atom->rmass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double massone,t[6]; + for (i = 0; i < 6; i++) t[i] = 0.0; + + if (atom->mass_require) { + for (i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + massone = mass[type[i]]; + t[0] += massone * v[i][0]*v[i][0]; + t[1] += massone * v[i][1]*v[i][1]; + t[2] += massone * v[i][2]*v[i][2]; + t[3] += massone * v[i][0]*v[i][1]; + t[4] += massone * v[i][0]*v[i][2]; + t[5] += massone * v[i][1]*v[i][2]; + } + } else { + for (i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + massone = rmass[i]; + t[0] += massone * v[i][0]*v[i][0]; + t[1] += massone * v[i][1]*v[i][1]; + t[2] += massone * v[i][2]*v[i][2]; + t[3] += massone * v[i][0]*v[i][1]; + t[4] += massone * v[i][0]*v[i][2]; + t[5] += massone * v[i][1]*v[i][2]; + } + } + + MPI_Allreduce(&t,&ke_tensor,6,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < 6; i++) ke_tensor[i] *= force->mvv2e; +} diff --git a/src/temp_full.h b/src/temp_full.h new file mode 100644 index 0000000000000000000000000000000000000000..355258626fc8cb90464932d42b57f0da9392428d --- /dev/null +++ b/src/temp_full.h @@ -0,0 +1,28 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef TEMP_FULL_H +#define TEMP_FULL_H + +#include "temperature.h" + +class TempFull : public Temperature { + public: + TempFull(int, char **); + ~TempFull() {} + void init(); + double compute(); + void tensor(); +}; + +#endif diff --git a/src/temp_partial.cpp b/src/temp_partial.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f8236d19d1e4f758b9a01cbf8b677b0f68d6a9be --- /dev/null +++ b/src/temp_partial.cpp @@ -0,0 +1,92 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "stdlib.h" +#include "temp_partial.h" +#include "atom.h" +#include "force.h" +#include "group.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +TempPartial::TempPartial(int narg, char **arg) : Temperature(narg, arg) +{ + xflag = atoi(arg[3]); + yflag = atoi(arg[4]); + zflag = atoi(arg[5]); +} + +/* ---------------------------------------------------------------------- */ + +void TempPartial::init() +{ + count_atoms(); + count_fix(); + dof = (xflag+yflag+zflag) * ncount; + dof -= extra_dof + fix_dof; + if (ncount > 0) tfactor = force->mvv2e / (dof * force->boltz); + else tfactor = 0.0; +} + +/* ---------------------------------------------------------------------- */ + +double TempPartial::compute() +{ + double **v = atom->v; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double t = 0.0; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) + t += (xflag*v[i][0]*v[i][0] + yflag*v[i][1]*v[i][1] + + zflag*v[i][2]*v[i][2]) * mass[type[i]]; + + MPI_Allreduce(&t,&t_total,1,MPI_DOUBLE,MPI_SUM,world); + t_total *= tfactor; + return t_total; +} + +/* ---------------------------------------------------------------------- */ + +void TempPartial::tensor() +{ + int i; + + double **v = atom->v; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double rmass,t[6]; + for (i = 0; i < 6; i++) t[i] = 0.0; + + for (i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + rmass = mass[type[i]]; + t[0] += rmass * xflag*v[i][0]*v[i][0]; + t[1] += rmass * yflag*v[i][1]*v[i][1]; + t[2] += rmass * zflag*v[i][2]*v[i][2]; + t[3] += rmass * xflag*yflag*v[i][0]*v[i][1]; + t[4] += rmass * xflag*zflag*v[i][0]*v[i][2]; + t[5] += rmass * yflag*zflag*v[i][1]*v[i][2]; + } + + MPI_Allreduce(&t,&ke_tensor,6,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < 6; i++) ke_tensor[i] *= force->mvv2e; +} diff --git a/src/temp_partial.h b/src/temp_partial.h new file mode 100644 index 0000000000000000000000000000000000000000..8916a09c5f7c0eb1737f346b64101305e2afae03 --- /dev/null +++ b/src/temp_partial.h @@ -0,0 +1,31 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef TEMP_PARTIAL_H +#define TEMP_PARTIAL_H + +#include "temperature.h" + +class TempPartial : public Temperature { + public: + TempPartial(int, char **); + ~TempPartial() {} + void init(); + double compute(); + void tensor(); + + private: + int xflag,yflag,zflag; +}; + +#endif diff --git a/src/temp_ramp.cpp b/src/temp_ramp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7964068d46d6d7392df700b5c1a19677fc52a2c2 --- /dev/null +++ b/src/temp_ramp.cpp @@ -0,0 +1,146 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "stdlib.h" +#include "string.h" +#include "temp_ramp.h" +#include "atom.h" +#include "force.h" +#include "error.h" + +#define MIN(A,B) ((A) < (B)) ? (A) : (B) +#define MAX(A,B) ((A) > (B)) ? (A) : (B) + +/* ---------------------------------------------------------------------- */ + +TempRamp::TempRamp(int narg, char **arg) : Temperature(narg, arg) +{ + if (strcmp(arg[3],"vx") == 0) v_dim = 0; + else if (strcmp(arg[3],"vy") == 0) v_dim = 1; + else if (strcmp(arg[3],"vz") == 0) v_dim = 2; + else error->all("Illegal temperature ramp command"); + + if (v_dim == 0) { + v_lo = xscale*atof(arg[4]); + v_hi = xscale*atof(arg[5]); + } else if (v_dim == 1) { + v_lo = yscale*atof(arg[4]); + v_hi = yscale*atof(arg[5]); + } else if (v_dim == 0) { + v_lo = zscale*atof(arg[4]); + v_hi = zscale*atof(arg[5]); + } + + if (strcmp(arg[6],"x") == 0) coord_dim = 0; + else if (strcmp(arg[6],"y") == 0) coord_dim = 1; + else if (strcmp(arg[6],"z") == 0) coord_dim = 2; + else error->all("Illegal temperature ramp command"); + + if (coord_dim == 0) { + coord_lo = xscale*atof(arg[7]); + coord_hi = xscale*atof(arg[8]); + } else if (coord_dim == 1) { + coord_lo = yscale*atof(arg[7]); + coord_hi = yscale*atof(arg[8]); + } else if (coord_dim == 2) { + coord_lo = zscale*atof(arg[7]); + coord_hi = zscale*atof(arg[8]); + } +} + +/* ---------------------------------------------------------------------- */ + +void TempRamp::init() +{ + count_atoms(); + count_fix(); + dof = force->dimension * ncount; + dof -= extra_dof + fix_dof; + if (ncount > 0) tfactor = force->mvv2e / (dof * force->boltz); + else tfactor = 0.0; +} + +/* ---------------------------------------------------------------------- */ + +double TempRamp::compute() +{ + double fraction,vramp,vtmp[3]; + + double **x = atom->x; + double **v = atom->v; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double t = 0.0; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + fraction = (x[i][coord_dim] - coord_lo) / (coord_hi - coord_lo); + fraction = MAX(fraction,0.0); + fraction = MIN(fraction,1.0); + vramp = v_lo + fraction*(v_hi - v_lo); + vtmp[0] = v[i][0]; + vtmp[1] = v[i][1]; + vtmp[2] = v[i][2]; + vtmp[v_dim] -= vramp; + t += (vtmp[0]*vtmp[0] + vtmp[1]*vtmp[1] + vtmp[2]*vtmp[2]) * + mass[type[i]]; + } + + MPI_Allreduce(&t,&t_total,1,MPI_DOUBLE,MPI_SUM,world); + t_total *= tfactor; + return t_total; +} + +/* ---------------------------------------------------------------------- */ + +void TempRamp::tensor() +{ + int i; + double fraction,vramp,vtmp[3]; + + double **x = atom->x; + double **v = atom->v; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double rmass,t[6]; + for (i = 0; i < 6; i++) t[i] = 0.0; + + for (i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + fraction = (x[i][coord_dim] - coord_lo) / (coord_hi - coord_lo); + fraction = MAX(fraction,0.0); + fraction = MIN(fraction,1.0); + vramp = v_lo + fraction*(v_hi - v_lo); + vtmp[0] = v[i][0]; + vtmp[1] = v[i][1]; + vtmp[2] = v[i][2]; + vtmp[v_dim] -= vramp; + + rmass = mass[type[i]]; + t[0] += rmass * vtmp[0]*vtmp[0]; + t[1] += rmass * vtmp[1]*vtmp[1]; + t[2] += rmass * vtmp[2]*vtmp[2]; + t[3] += rmass * vtmp[0]*vtmp[1]; + t[4] += rmass * vtmp[0]*vtmp[2]; + t[5] += rmass * vtmp[1]*vtmp[2]; + } + + MPI_Allreduce(&t,&ke_tensor,6,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < 6; i++) ke_tensor[i] *= force->mvv2e; +} diff --git a/src/temp_ramp.h b/src/temp_ramp.h new file mode 100644 index 0000000000000000000000000000000000000000..5cf7f6201b8e8077f90d8b8af212dfa6b32ab49b --- /dev/null +++ b/src/temp_ramp.h @@ -0,0 +1,34 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef TEMP_RAMP_H +#define TEMP_RAMP_H + +#include "temperature.h" + +class TempRamp : public Temperature { + public: + TempRamp(int, char **); + ~TempRamp() {} + void init(); + double compute(); + void tensor(); + + private: + int coord_dim; + double coord_lo,coord_hi; + int v_dim; + double v_lo,v_hi; +}; + +#endif diff --git a/src/temp_region.cpp b/src/temp_region.cpp new file mode 100644 index 0000000000000000000000000000000000000000..344b0ee11629281e54549ea0d8e3a2a2c8548936 --- /dev/null +++ b/src/temp_region.cpp @@ -0,0 +1,101 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "string.h" +#include "temp_region.h" +#include "atom.h" +#include "force.h" +#include "domain.h" +#include "region.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +TempRegion::TempRegion(int narg, char **arg) : Temperature(narg, arg) +{ + for (iregion = 0; iregion < domain->nregion; iregion++) + if (strcmp(arg[3],domain->regions[iregion]->id) == 0) break; + if (iregion == domain->nregion) + error->all("Temperature region ID does not exist"); +} + +/* ---------------------------------------------------------------------- */ + +void TempRegion::init() +{ + dof = 0; +} + +/* ---------------------------------------------------------------------- */ + +double TempRegion::compute() +{ + double **x = atom->x; + double **v = atom->v; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + int count = 0; + double t = 0.0; + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit && + domain->regions[iregion]->match(x[i][0],x[i][1],x[i][2])) { + count++; + t += (v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]) * + mass[type[i]]; + } + + double tarray[2],tarray_all[2]; + tarray[0] = count; + tarray[1] = t; + MPI_Allreduce(tarray,tarray_all,2,MPI_DOUBLE,MPI_SUM,world); + dof = force->dimension * tarray_all[0] - extra_dof; + if (dof > 0) t_total = force->mvv2e * tarray_all[1] / (dof * force->boltz); + else t_total = 0.0; + return t_total; +} + +/* ---------------------------------------------------------------------- */ + +void TempRegion::tensor() +{ + int i; + + double **x = atom->x; + double **v = atom->v; + double *mass = atom->mass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double rmass,t[6]; + for (i = 0; i < 6; i++) t[i] = 0.0; + + for (i = 0; i < nlocal; i++) + if (mask[i] & groupbit && + domain->regions[iregion]->match(x[i][0],x[i][1],x[i][2])) { + rmass = mass[type[i]]; + t[0] += rmass * v[i][0]*v[i][0]; + t[1] += rmass * v[i][1]*v[i][1]; + t[2] += rmass * v[i][2]*v[i][2]; + t[3] += rmass * v[i][0]*v[i][1]; + t[4] += rmass * v[i][0]*v[i][2]; + t[5] += rmass * v[i][1]*v[i][2]; + } + + MPI_Allreduce(&t,&ke_tensor,6,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < 6; i++) ke_tensor[i] *= force->mvv2e; +} diff --git a/src/temp_region.h b/src/temp_region.h new file mode 100644 index 0000000000000000000000000000000000000000..387618db27bf6fdf8cfd6dce41d310c90101c327 --- /dev/null +++ b/src/temp_region.h @@ -0,0 +1,31 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef TEMP_REGION_H +#define TEMP_REGION_H + +#include "temperature.h" + +class TempRegion : public Temperature { + public: + TempRegion(int, char **); + ~TempRegion() {} + void init(); + double compute(); + void tensor(); + + private: + int iregion; +}; + +#endif diff --git a/src/temper.cpp b/src/temper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..889e13c5eeae2342e60c62450b5e512235072007 --- /dev/null +++ b/src/temper.cpp @@ -0,0 +1,322 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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: Mark Sears (SNL) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdlib.h" +#include "string.h" +#include "temper.h" +#include "system.h" +#include "universe.h" +#include "domain.h" +#include "update.h" +#include "integrate.h" +#include "modify.h" +#include "force.h" +#include "output.h" +#include "thermo.h" +#include "fix.h" +#include "fix_nvt.h" +#include "fix_langevin.h" +#include "random_park.h" +#include "finish.h" +#include "timer.h" +#include "memory.h" +#include "error.h" + +#define NVT 1 +#define LANGEVIN 2 +// #define TEMPER_DEBUG 1 + +/* ---------------------------------------------------------------------- + free all tempering memory +------------------------------------------------------------------------- */ + +Temper::~Temper() +{ + MPI_Comm_free(&roots); + if (ranswap) delete ranswap; + delete ranboltz; + delete [] set_temp; + delete [] temp2world; + delete [] world2temp; + delete [] world2root; +} + +/* ---------------------------------------------------------------------- + perform tempering with inter-world swaps +------------------------------------------------------------------------- */ + +void Temper::command(int narg, char **arg) +{ + if (universe->nworlds == 1) + error->all("Must have more than one processor partition to temper"); + + if (narg != 6 && narg != 7) error->universe_all("Illegal temper command"); + + if (domain->box_exist == 0) + error->all("Temper command before simulation box is defined"); + + update->nsteps = atoi(arg[0]); + + update->beginstep = update->firststep = update->ntimestep; + update->endstep = update->laststep = update->firststep + update->nsteps; + + update->whichflag = 0; + + sys->init(); + + // grab temper command args + + int nsteps = update->nsteps; + nevery = atoi(arg[1]); + double temp = atof(arg[2]); + + for (whichfix = 0; whichfix < modify->nfix; whichfix++) + if (strcmp(arg[3],modify->fix[whichfix]->id) == 0) break; + if (whichfix == modify->nfix) + error->universe_all("Tempering fix ID is not defined"); + + seed_swap = atoi(arg[4]); + seed_boltz = atoi(arg[5]); + + my_set_temp = universe->iworld; + if (narg == 7) my_set_temp = atoi(arg[6]); + + // swap frequency must evenly divide total # of timesteps + + nswaps = nsteps/nevery; + if (nswaps*nevery != nsteps) + error->universe_all("Non integer # of swaps in temper command"); + + // thermodynamics must be computed on swap steps + // potential energy must be computed by thermo + + if (nevery % output->thermo_every) + error->universe_all("Thermodynamics not computed on tempering swap steps"); + + if (output->thermo->peflag == 0) + error->universe_all("Thermodynamics must compute PE for temper"); + + // fix style must be appropriate for temperature control + + if (strcmp(modify->fix[whichfix]->style,"nvt") == 0) fixstyle = NVT; + else if (strcmp(modify->fix[whichfix]->style,"langevin") == 0) + fixstyle = LANGEVIN; + else error->universe_all("Tempering fix is not valid"); + + // local storage + + me_universe = universe->me; + MPI_Comm_rank(world,&me); + nworlds = universe->nworlds; + iworld = universe->iworld; + boltz = force->boltz; + + // create MPI communicator for root proc from each world + + int color; + if (me == 0) color = 0; + else color = 1; + MPI_Comm_split(universe->uworld,color,0,&roots); + + // RNGs for swaps and Boltzmann test + // warm up Boltzmann RNG + + if (seed_swap) ranswap = new RanPark(seed_swap); + else ranswap = NULL; + ranboltz = new RanPark(seed_boltz + me_universe); + for (int i = 0; i < 100; i++) ranboltz->uniform(); + + // world2root[i] = global proc that is root proc of world i + + world2root = new int[nworlds]; + if (me == 0) + MPI_Allgather(&me_universe,1,MPI_INT,world2root,1,MPI_INT,roots); + MPI_Bcast(world2root,nworlds,MPI_INT,0,world); + + // create static list of set temperatures + // allgather tempering arg "temp" across root procs + // bcast from each root to other procs in world + + set_temp = new double[nworlds]; + if (me == 0) MPI_Allgather(&temp,1,MPI_DOUBLE,set_temp,1,MPI_DOUBLE,roots); + MPI_Bcast(set_temp,nworlds,MPI_DOUBLE,0,world); + + // create world2temp only on root procs from my_set_temp + // create temp2world on root procs from world2temp, + // then bcast to all procs within world + + world2temp = new int[nworlds]; + temp2world = new int[nworlds]; + if (me == 0) { + MPI_Allgather(&my_set_temp,1,MPI_INT,world2temp,1,MPI_INT,roots); + for (int i = 0; i < nworlds; i++) temp2world[world2temp[i]] = i; + } + MPI_Bcast(temp2world,nworlds,MPI_INT,0,world); + + // if restarting tempering, reset temp target of Fix to current my_set_temp + + if (narg == 7) { + double new_temp = set_temp[my_set_temp]; + if (fixstyle == NVT) + ((FixNVT *) modify->fix[whichfix])->reset_target(new_temp); + else if (fixstyle == LANGEVIN) + ((FixLangevin *) modify->fix[whichfix])->reset_target(new_temp); + } + + // setup tempering runs + + int i,which,partner,swap,partner_set_temp,partner_world; + double pe,pe_partner,boltz_factor,new_temp; + MPI_Status status; + + if (me_universe == 0 && universe->uscreen) + fprintf(universe->uscreen,"Setting up tempering ...\n"); + + update->integrate->setup(); + + timer->barrier_start(TIME_LOOP); + + if (me_universe == 0) { + if (universe->uscreen) fprintf(universe->uscreen,"Step T1 T2 ...\n"); + if (universe->ulogfile) fprintf(universe->ulogfile,"Step T1 T2 ...\n"); + print_status(); + } + + for (int iswap = 0; iswap < nswaps; iswap++) { + + // run for nevery timesteps + + update->integrate->iterate(nevery); + + // which = which of 2 kinds of swaps to do (0,1) + + if (!ranswap) which = iswap % 2; + else if (ranswap->uniform() < 0.5) which = 0; + else which = 1; + + // partner_set_temp = which set temp I am partnering with for this swap + + if (which == 0) { + if (my_set_temp % 2 == 0) partner_set_temp = my_set_temp + 1; + else partner_set_temp = my_set_temp - 1; + } else { + if (my_set_temp % 2 == 1) partner_set_temp = my_set_temp + 1; + else partner_set_temp = my_set_temp - 1; + } + + // partner = proc ID to swap with + // if partner = -1, then I am not a proc that swaps + + partner = -1; + if (me == 0 && partner_set_temp >= 0 && partner_set_temp < nworlds) { + partner_world = temp2world[partner_set_temp]; + partner = world2root[partner_world]; + } + + // swap with a partner, only root procs in each world participate + // hi proc sends PE to low proc + // lo proc make Boltzmann decision on whether to swap + // lo proc communicates decision back to hi proc + + swap = 0; + if (partner != -1) { + pe = output->thermo->potential_energy; + if (me_universe > partner) + MPI_Send(&pe,1,MPI_DOUBLE,partner,0,universe->uworld); + else + MPI_Recv(&pe_partner,1,MPI_DOUBLE,partner,0,universe->uworld,&status); + + if (me_universe < partner) { + boltz_factor = (pe - pe_partner) * + (1.0/(boltz*set_temp[my_set_temp]) - + 1.0/(boltz*set_temp[partner_set_temp])); + if (boltz_factor >= 0.0) swap = 1; + else if (ranboltz->uniform() < exp(boltz_factor)) swap = 1; + } + + if (me_universe < partner) + MPI_Send(&swap,1,MPI_INT,partner,0,universe->uworld); + else + MPI_Recv(&swap,1,MPI_INT,partner,0,universe->uworld,&status); + +#ifdef TEMPER_DEBUG + if (me_universe < partner) + printf("SWAP %d & %d: yes = %d,Ts = %d %d, PEs = %g %g, Bz = %g %g\n", + me_universe,partner,swap,my_set_temp,partner_set_temp, + pe,pe_partner,boltz_factor,exp(boltz_factor)); +#endif + + } + + // bcast swap result to other procs in my world + + MPI_Bcast(&swap,1,MPI_INT,0,world); + + // if my world swapped, all procs in world reset temp target of Fix + + if (swap) { + new_temp = set_temp[partner_set_temp]; + if (fixstyle == NVT) + ((FixNVT *) modify->fix[whichfix])->reset_target(new_temp); + else if (fixstyle == LANGEVIN) + ((FixLangevin *) modify->fix[whichfix])->reset_target(new_temp); + } + + // update my_set_temp and temp2world on every proc + // root procs update their value if swap took place + // allgather across root procs + // bcast within my world + + if (swap) my_set_temp = partner_set_temp; + if (me == 0) { + MPI_Allgather(&my_set_temp,1,MPI_INT,world2temp,1,MPI_INT,roots); + for (i = 0; i < nworlds; i++) temp2world[world2temp[i]] = i; + } + MPI_Bcast(temp2world,nworlds,MPI_INT,0,world); + + // print out current swap status + + if (me_universe == 0) print_status(); + } + + timer->barrier_stop(TIME_LOOP); + + Finish finish; + finish.end(1); + update->whichflag = -1; +} + +/* ---------------------------------------------------------------------- + proc 0 prints current tempering status +------------------------------------------------------------------------- */ + +void Temper::print_status() +{ + if (universe->uscreen) { + fprintf(universe->uscreen,"%d ",update->ntimestep); + for (int i = 0; i < nworlds; i++) + fprintf(universe->uscreen,"%d ",world2temp[i]); + fprintf(universe->uscreen,"\n"); + } + if (universe->ulogfile) { + fprintf(universe->ulogfile,"%d ",update->ntimestep); + for (int i = 0; i < nworlds; i++) + fprintf(universe->ulogfile,"%d ",world2temp[i]); + fprintf(universe->ulogfile,"\n"); + } +} diff --git a/src/temper.h b/src/temper.h new file mode 100644 index 0000000000000000000000000000000000000000..025b143091980ecc5a1685c6b0c1418b1c481ff0 --- /dev/null +++ b/src/temper.h @@ -0,0 +1,49 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef TEMPER_H +#define TEMPER_H + +#include "lammps.h" + +class RanPark; + +class Temper : public LAMMPS { + public: + Temper() {} + ~Temper(); + void command(int, char **); + + private: + int me,me_universe; // my proc ID in world and universe + int iworld,nworlds; // world info + double boltz; // copy from output->boltz + MPI_Comm roots; // Comm with 1 root proc from each world + RanPark *ranswap,*ranboltz; // RNGs for swapping and Boltz factor + int nevery; // # of timesteps between swaps + int nswaps; // # of tempering swaps to perform + int seed_swap; // 0 = toggle swaps, n = RNG for swap direction + int seed_boltz; // seed for Boltz factor comparison + int whichfix; // index of temperature fix to use + int fixstyle; // what kind of temperature fix is used + + int my_set_temp; // which set temp I am simulating + double *set_temp; // static list of replica set temperatures + int *temp2world; // temp2world[i] = world simulating set temp i + int *world2temp; // world2temp[i] = temp simulated by world i + int *world2root; // world2root[i] = root proc of world i + + void print_status(); +}; + +#endif diff --git a/src/temperature.cpp b/src/temperature.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ea9436deb995b9832208f364a181c81dcdb8aa34 --- /dev/null +++ b/src/temperature.cpp @@ -0,0 +1,143 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "stdlib.h" +#include "string.h" +#include "temperature.h" +#include "atom.h" +#include "group.h" +#include "modify.h" +#include "domain.h" +#include "fix.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +Temperature::Temperature(int narg, char **arg) +{ + if (narg < 3) error->all("Illegal temperature command"); + + // temperature ID, group, and style + + int n = strlen(arg[0]) + 1; + id = new char[n]; + strcpy(id,arg[0]); + + igroup = group->find(arg[1]); + groupbit = group->bitmask[igroup]; + + n = strlen(arg[2]) + 1; + style = new char[n]; + strcpy(style,arg[2]); + + // set modify defaults + + extra_dof = 3; + + // set input line defaults + + scaleflag = 1; + + // read options from end of input line + + if (strcmp(style,"full") == 0) options(narg-3,&arg[3]); + else if (strcmp(style,"partial") == 0) options(narg-6,&arg[6]); + else if (strcmp(style,"ramp") == 0) options(narg-9,&arg[9]); + else if (strcmp(style,"region") == 0) options(narg-4,&arg[4]); + + // set scaling for RAMP style + + if (strcmp(style,"ramp") == 0) { + + if (scaleflag && strcmp(domain->lattice_style,"none") == 0) + error->all("Use of temperature ramp with undefined lattice"); + + if (scaleflag) { + xscale = domain->xlattice; + yscale = domain->ylattice; + zscale = domain->zlattice; + } + else xscale = yscale = zscale = 1.0; + } +} + +/* ---------------------------------------------------------------------- */ + +Temperature::~Temperature() +{ + delete [] id; + delete [] style; +} + +/* ---------------------------------------------------------------------- */ + +void Temperature::modify_params(int narg, char **arg) +{ + if (narg == 0) error->all("Illegal temp_modify command"); + + int iarg = 0; + while (iarg < narg) { + if (strcmp(arg[iarg],"extra") == 0) { + if (iarg+2 > narg) error->all("Illegal temp_modify command"); + extra_dof = atoi(arg[iarg+1]); + iarg += 2; + } else error->all("Illegal temp_modify command"); + } +} + +/* ---------------------------------------------------------------------- + ncount = atoms in Temperature group +------------------------------------------------------------------------- */ + +void Temperature::count_atoms() +{ + int *mask = atom->mask; + int nlocal = atom->nlocal; + + int icount = 0; + for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) icount++; + double rcount = icount; + MPI_Allreduce(&rcount,&ncount,1,MPI_DOUBLE,MPI_SUM,world); +} + +/* ---------------------------------------------------------------------- + count degrees of freedom subtracted by fixes +------------------------------------------------------------------------- */ + +void Temperature::count_fix() +{ + fix_dof = 0; + for (int i = 0; i < modify->nfix; i++) + fix_dof += modify->fix[i]->dof(igroup); +} + +/* ---------------------------------------------------------------------- + parse optional parameters at end of temperature input line +------------------------------------------------------------------------- */ + +void Temperature::options(int narg, char **arg) +{ + if (narg < 0) error->all("Illegal temperature command"); + + int iarg = 0; + while (iarg < narg) { + if (strcmp(arg[iarg],"units") == 0) { + if (iarg+2 > narg) error->all("Illegal temperature command"); + if (strcmp(arg[iarg+1],"box") == 0) scaleflag = 0; + else if (strcmp(arg[iarg+1],"lattice") == 0) scaleflag = 1; + else error->all("Illegal temperature command"); + iarg += 2; + } else error->all("Illegal temperature command"); + } +} diff --git a/src/temperature.h b/src/temperature.h new file mode 100644 index 0000000000000000000000000000000000000000..f33aa5de84da6c8446f89aea4d70997516a2aba0 --- /dev/null +++ b/src/temperature.h @@ -0,0 +1,43 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef TEMPERATURE_H +#define TEMPERATURE_H + +#include "lammps.h" + +class Temperature : public LAMMPS { + public: + char *id,*style; + int igroup,groupbit; + double t_total,dof; + double ke_tensor[6]; + double tfactor,ncount; + int extra_dof,fix_dof; + int scaleflag; + double xscale,yscale,zscale; + + Temperature(int, char **); + virtual ~Temperature(); + void modify_params(int, char **); + void count_atoms(); + void count_fix(); + virtual void init() = 0; + virtual double compute() = 0; + virtual void tensor() = 0; + + private: + void options(int, char **); +}; + +#endif diff --git a/src/thermo.cpp b/src/thermo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d46678e466bf644cb55fd3b8f6bac8ef9d0b0e14 --- /dev/null +++ b/src/thermo.cpp @@ -0,0 +1,1255 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "stdlib.h" +#include "string.h" +#include "thermo.h" +#include "force.h" +#include "temperature.h" +#include "pressure.h" +#include "update.h" +#include "modify.h" +#include "force.h" +#include "pair.h" +//#include "pair_dipole_cut.h" +//#include "pair_dipole_long.h" +#include "bond.h" +#include "angle.h" +#include "dihedral.h" +#include "improper.h" +#include "kspace.h" +#include "fix.h" +#include "comm.h" +#include "atom.h" +#include "domain.h" +#include "timer.h" +#include "memory.h" +#include "error.h" + +// customize by adding a keyword to this list: + +// step, atoms, cpu, temp, press, pe, ke, eng, +// evdwl, ecoul, epair, ebond, eangle, edihed, eimp, emol, elong, etail, erot +// vol, lx, ly, lz, pxx, pyy, pzz, pxy, pxz, pyz +// gke, grot (granular trans-ke and rotational-ke) +// tave, pave, eave, peave (time-averaged quantities) +// t_ID (user-defined temperatures) + +// customize by adding a DEFINE to this list + +#define ONE "step temp epair emol eng press" +#define MULTI "eng ke temp pe ebond eangle edihed eimp evdwl ecoul elong press" +#define GRANULAR "step atoms gke grot" +#define DIPOLE "step temp epair erot eng press" + +enum {IGNORE,WARN,ERROR}; // same as write_restart.cpp +enum {ONELINE,MULTILINE}; +enum {INT,FLOAT}; +enum {NEITHER,PRINT,ENERGY,BOTH}; // same as modify.cpp + +#define MAXLINE 1024 +#define INERTIA3D 0.4 // moments of inertia for sphere and disk +#define INERTIA2D 0.5 + +/* ---------------------------------------------------------------------- */ + +Thermo::Thermo(int narg, char **arg) +{ + MPI_Comm_rank(world,&me); + + int n = strlen(arg[0]) + 1; + style = new char[n]; + strcpy(style,arg[0]); + + // set thermo_modify defaults + + normuserflag = 0; + lineflag = ONELINE; + lostflag = ERROR; + lostbefore = 0; + flushflag = 0; + nwindow = 10; + ncurrent_t = ncurrent_p = ncurrent_e = ncurrent_pe = -1; + npartial_t = npartial_p = npartial_e = npartial_pe = 0; + + // initialize ptrs + + // set style and corresponding lineflag + // custom style builds its own line of keywords + // customize a new DEFINE style by adding to if statement + + line = new char[MAXLINE]; + + if (strcmp(style,"one") == 0) { + strcpy(line,ONE); + } else if (strcmp(style,"multi") == 0) { + strcpy(line,MULTI); + lineflag = MULTILINE; + } else if (strcmp(style,"granular") == 0) { + strcpy(line,GRANULAR); + } else if (strcmp(style,"dipole") == 0) { + strcpy(line,DIPOLE); + } else if (strcmp(style,"custom") == 0) { + if (narg == 1) error->all("Illegal thermo style custom command"); + line[0] = '\0'; + for (int iarg = 1; iarg < narg; iarg++) { + strcat(line,arg[iarg]); + strcat(line," "); + } + line[strlen(line)-1] = '\0'; + } else error->all("Illegal thermo style command"); + + // process line of keywords + + nfield = ntemp = nfix = 0; + tempindices = NULL; + parse_fields(line); + nfield_initial = nfield; + + // format strings + + char *str = "%8d"; + n = strlen(str) + 1; + format_int_def = new char[n]; + strcpy(format_int_def,str); + + str = "%12.8g"; + n = strlen(str) + 1; + format_g_def = new char[n]; + strcpy(format_g_def,str); + + str = "%14.4f"; + n = strlen(str) + 1; + format_f_def = new char[n]; + strcpy(format_f_def,str); + + str = "---------------- Step %8d ----- CPU = %11.4f (sec) ----------------"; + n = strlen(str) + 1; + format_multi = new char[n]; + strcpy(format_multi,str); + + format_iflag = 0; + format_fflag = 0; + format_int_user = NULL; + format_float_user = NULL; + + nformat = 0; + format_user = NULL; + format_index = NULL; + + format = NULL; + + // ptrs for T,P computation + // temperature is default in force->templist + + temperature = force->templist[0]; + pressure = force->pressure; + + // average quantities + + tsum = psum = esum = pesum = 0.0; + tpast = new double[nwindow]; + ppast = new double[nwindow]; + epast = new double[nwindow]; + pepast = new double[nwindow]; + + inertia = NULL; +} + +/* ---------------------------------------------------------------------- */ + +Thermo::~Thermo() +{ + delete [] style; + delete [] line; + + if (keyword) { + for (int i = 0; i < nfield; i++) delete [] keyword[i]; + memory->sfree(keyword); + memory->sfree(vfunc); + memory->sfree(vtype); + } + + delete [] format_int_def; + delete [] format_g_def; + delete [] format_f_def; + delete [] format_multi; + if (format_iflag) delete [] format_int_user; + if (format_fflag) delete [] format_float_user; + + if (nformat) { + for (int i = 0; i < nformat; i++) delete [] format_user[i]; + memory->sfree(format_user); + memory->sfree(format_index); + } + + if (format) { + for (int i = 0; i < nfield; i++) delete [] format[i]; + delete [] format; + } + + if (tempindices) memory->sfree(tempindices); + + if (nfix) { + delete [] fixflags; + delete [] fixprint; + delete [] fixenergy; + delete [] fixvalues; + } + + delete [] tpast; + delete [] ppast; + delete [] epast; + delete [] pepast; + + delete [] inertia; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::init() +{ + int i,j,n; + + // error checks + // mass/rmass for granular and non-granular styles + // dipole/omega for erot + + if (granflag && atom->check_style("granular") == 0) + error->all("Must use atom style granular with chosen thermo settings"); + if ((tempflag || pressflag) && atom->check_style("granular")) + error->all("Cannot use atom style granular with chosen thermo settings"); + if (dipoleflag && atom->check_style("dipole") == 0) + error->all("Must use atom style dipole with chosen thermo settings"); + + // set normvalue to default setting unless user has specified it + + if (normuserflag) normvalue = normuser; + else if (strcmp(style,"granular") == 0) normvalue = 0; + else if (strcmp(update->unit_style,"lj") == 0) normvalue = 1; + else normvalue = 0; + + // check for granular freeze Fix and set freeze_group_bit + + for (i = 0; i < modify->nfix; i++) + if (strcmp(modify->fix[i]->style,"freeze") == 0) break; + if (i < modify->nfix) freeze_group_bit = modify->fix[i]->groupbit; + else freeze_group_bit = 0; + + // setup moments of inertia if dipoleflag is set + // extract sigma as particle size to compute moment of inertia + + if (dipoleflag) { + delete [] inertia; + inertia = new double[atom->ntypes+1]; + double *mass = atom->mass; + + Pair *anypair; + double **sigma; + /* + if (anypair = force->pair_match("dipole/cut")) + sigma = ((PairDipoleCut *) anypair)->sigma; + else if (anypair = force->pair_match("dipole/long")) + sigma = ((PairDipoleLong *) anypair)->sigma; + else error->all("Pair style is incompatible with thermo_style erot"); + */ + + if (force->dimension == 3) + for (int i = 1; i <= atom->ntypes; i++) + inertia[i] = INERTIA3D * mass[i] * 0.25*sigma[i][i]*sigma[i][i]; + else + for (int i = 1; i <= atom->ntypes; i++) + inertia[i] = INERTIA2D * mass[i] * 0.25*sigma[i][i]*sigma[i][i]; + } + + // clear all format strings + // clear extra keyword strings beyond nfield_initial + + if (format) { + for (i = 0; i < nfield; i++) delete [] format[i]; + delete [] format; + } + for (i = nfield_initial; i < nfield; i++) delete [] keyword[i]; + nfield = nfield_initial; + + // add a field if volume changes and not style = custom + // this check must come after domain init, so box_change is set + + if (domain->box_change && strcmp(style,"custom") != 0) + addfield("Volume",&Thermo::compute_vol,FLOAT); + + // add fields due to fixes + + if (nfix) { + delete [] fixflags; + delete [] fixprint; + delete [] fixenergy; + delete [] fixvalues; + } + + nfix = nfix_energy = nfix_print = 0; + if (modify->n_thermo) nfix = modify->thermo_fields(0,NULL,NULL); + if (nfix) { + fixflags = new int[nfix]; + fixprint = new int[nfix]; + fixenergy = new int[nfix]; + fixvalues = new double[nfix]; + char **strings = new char*[nfix]; + for (i = 0; i < nfix; i++) strings[i] = new char[9]; + + nfix = modify->thermo_fields(nfix,fixflags,strings); + + for (i = 0; i < nfix; i++) { + if (fixflags[i] == PRINT || fixflags[i] == BOTH) { + fixprint[nfix_print++] = i; + addfield(strings[i],&Thermo::compute_fix,FLOAT); + } + if (fixflags[i] == ENERGY || fixflags[i] == BOTH) + fixenergy[nfix_energy++] = i; + } + + for (i = 0; i < nfix; i++) delete [] strings[i]; + delete [] strings; + } + + // nextra = 1 if printing extra temperature or fix values + + nextra = 0; + if (ntemp || nfix) nextra = 1; + + // set format string for each printed value + // include keyword if lineflag = MULTILINE + // add '/n' every 3 values if lineflag = MULTILINE + // add trailing '/n' to last value + + format = new char*[nfield]; + for (int i = 0; i < nfield; i++) format[i] = new char[32]; + char *ptr; + + for (i = 0; i < nfield; i++) { + format[i][0] = '\0'; + if (lineflag == MULTILINE && i % 3 == 0) strcat(format[i],"\n"); + + for (j = nformat-1; j >= 0; j--) + if (format_index[j] == i+1) break; + if (j >= 0) ptr = format_user[j]; + else if (vtype[i] == INT && format_iflag) ptr = format_int_user; + else if (vtype[i] == INT) ptr = format_int_def; + else if (vtype[i] == FLOAT && format_fflag) ptr = format_float_user; + else if (lineflag == MULTILINE) ptr = format_f_def; + else ptr = format_g_def; + + n = strlen(format[i]); + if (lineflag == ONELINE) sprintf(&format[i][n],"%s ",ptr); + else sprintf(&format[i][n],"%-8s = %s ",keyword[i],ptr); + + if (i == nfield-1) strcat(format[i],"\n"); + } +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::header() +{ + if (lineflag == MULTILINE) return; + + int loc = 0; + for (int i = 0; i < nfield; i++) + loc += sprintf(&line[loc],"%s ",keyword[i]); + sprintf(&line[loc],"\n"); + + if (me == 0) { + if (screen) fprintf(screen,line); + if (logfile) fprintf(logfile,line); + } +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute(int flag) +{ + firststep = flag; + + // check for lost atoms + // turn off normflag if natoms = 0 to avoid divide by 0 + // compute requested thermo quantities + + natoms = lost_check(); + if (natoms == 0) normflag = 0; + else normflag = normvalue; + + if (tempflag) tempvalue = temperature->compute(); + if (pressflag) pressure->compute(temperature); + if (nextra) { + itemp_print = ifix_print = 0; + if (nfix) modify->thermo_compute(fixvalues); + } + + // if lineflag = MULTILINE, prepend step/cpu header line + + int loc = 0; + if (lineflag == MULTILINE) { + double cpu; + if (flag) cpu = timer->elapsed(TIME_LOOP); + else cpu = 0.0; + loc = sprintf(&line[loc],format_multi,update->ntimestep,cpu); + } + + // add each thermo value to line with its specific format + + for (int i = 0; i < nfield; i++) { + (this->*vfunc[i])(); + if (vtype[i] == INT) loc += sprintf(&line[loc],format[i],ivalue); + else loc += sprintf(&line[loc],format[i],dvalue); + } + + // print line to screen and logfile + + if (me == 0) { + if (screen) fprintf(screen,line); + if (logfile) { + fprintf(logfile,line); + if (flushflag) fflush(logfile); + } + } +} + +/* ---------------------------------------------------------------------- + check for lost atoms, return current number of atoms +------------------------------------------------------------------------- */ + +double Thermo::lost_check() +{ + // natoms = current # of atoms + + double natoms; + double rlocal = atom->nlocal; + MPI_Allreduce(&rlocal,&natoms,1,MPI_DOUBLE,MPI_SUM,world); + if (natoms == atom->natoms) return natoms; + + // if not checking or already warned, just return + + if (lostflag == IGNORE) return natoms; + if (lostflag == WARN && lostbefore == 1) return natoms; + + // error message + + if (lostflag == ERROR) { + char str[128]; + sprintf(str,"Lost atoms: original %.15g current %.15g", + atom->natoms,natoms); + error->all(str); + } + + // warning message + + char str[128]; + sprintf(str,"Lost atoms: original %.15g current %.15g",atom->natoms,natoms); + if (me == 0) error->warning(str); + lostbefore = 1; + return natoms; +} + +/* ---------------------------------------------------------------------- + modify thermo parameters +------------------------------------------------------------------------- */ + +void Thermo::modify_params(int narg, char **arg) +{ + if (narg == 0) error->all("Illegal thermo_modify command"); + + int iarg = 0; + while (iarg < narg) { + if (strcmp(arg[iarg],"temp") == 0) { + if (iarg+2 > narg) error->all("Illegal thermo_modify command"); + int tempwhich; + for (tempwhich = 0; tempwhich < force->ntemp; tempwhich++) + if (strcmp(arg[iarg+1],force->templist[tempwhich]->id) == 0) break; + if (tempwhich == force->ntemp) + error->all("Could not find thermo_modify temperature ID"); + if (force->templist[tempwhich]->igroup != 0 && comm->me == 0) + error->warning("Temperature for thermo pressure is not for group all"); + if (strcmp(force->templist[tempwhich]->style,"region") == 0 && + comm->me == 0) + error->warning("Temperature for thermo pressure is style region"); + temperature = force->templist[tempwhich]; + iarg += 2; + } else if (strcmp(arg[iarg],"lost") == 0) { + if (iarg+2 > narg) error->all("Illegal thermo_modify command"); + if (strcmp(arg[iarg+1],"ignore") == 0) lostflag = IGNORE; + else if (strcmp(arg[iarg+1],"warn") == 0) lostflag = WARN; + else if (strcmp(arg[iarg+1],"error") == 0) lostflag = ERROR; + else error->all("Illegal thermo_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"norm") == 0) { + if (iarg+2 > narg) error->all("Illegal thermo_modify command"); + normuserflag = 1; + if (strcmp(arg[iarg+1],"no") == 0) normuser = 0; + else if (strcmp(arg[iarg+1],"yes") == 0) normuser = 1; + else error->all("Illegal thermo_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"flush") == 0) { + if (iarg+2 > narg) error->all("Illegal thermo_modify command"); + if (strcmp(arg[iarg+1],"no") == 0) flushflag = 0; + else if (strcmp(arg[iarg+1],"yes") == 0) flushflag = 1; + else error->all("Illegal thermo_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"line") == 0) { + if (iarg+2 > narg) error->all("Illegal thermo_modify command"); + if (strcmp(arg[iarg+1],"one") == 0) lineflag = ONELINE; + else if (strcmp(arg[iarg+1],"multi") == 0) lineflag = MULTILINE; + else error->all("Illegal thermo_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"format") == 0) { + if (iarg+3 > narg) error->all("Illegal thermo_modify command"); + if (strcmp(arg[iarg+1],"int") == 0) { + if (format_iflag) delete [] format_int_user; + format_iflag = 1; + int n = strlen(arg[iarg+2]) + 1; + format_int_user = new char[n]; + strcpy(format_int_user,arg[iarg+2]); + } else if (strcmp(arg[iarg+1],"float") == 0) { + if (format_fflag) delete [] format_float_user; + format_fflag = 1; + int n = strlen(arg[iarg+2]) + 1; + format_float_user = new char[n]; + strcpy(format_float_user,arg[iarg+2]); + } else { + int i = atoi(arg[iarg+1]); + if (i < 1) error->all("Illegal thermo_modify command"); + format_user = + (char **) memory->srealloc(format_user,(nformat+1)*sizeof(char *), + "thermo:format_user"); + format_index = + (int *) memory->srealloc(format_index,(nformat+1)*sizeof(int), + "thermo:format_index"); + int n = strlen(arg[iarg+2]) + 1; + format_user[nformat] = new char[n]; + strcpy(format_user[nformat],arg[iarg+2]); + format_index[nformat] = i; + nformat++; + } + iarg += 3; + } else if (strcmp(arg[iarg],"window") == 0) { + if (iarg+2 > narg) error->all("Illegal thermo_modify command"); + nwindow = atoi(arg[iarg+1]); + if (nwindow <= 0) error->all("Illegal thermo_modify command"); + ncurrent_t = ncurrent_p = ncurrent_e = ncurrent_pe = -1; + npartial_t = npartial_p = npartial_e = npartial_pe = 0; + tsum = psum = esum = pesum = 0.0; + delete [] tpast; + delete [] ppast; + delete [] epast; + delete [] pepast; + tpast = new double[nwindow]; + ppast = new double[nwindow]; + epast = new double[nwindow]; + pepast = new double[nwindow]; + iarg += 2; + } else error->all("Illegal thermo_modify command"); + } +} + +/* ---------------------------------------------------------------------- + parse list of thermo keywords from str + set compute flags (temp, press, etc) +------------------------------------------------------------------------- */ + +void Thermo::parse_fields(char *str) +{ + keyword = NULL; + vfunc = NULL; + vtype = NULL; + + tempflag = pressflag = tensorflag = granflag = peflag = dipoleflag = 0; + + // customize by adding to if statement + + char *word = strtok(str," \0"); + while (word) { + if (strcmp(word,"step") == 0) { + addfield("Step",&Thermo::compute_step,INT); + } else if (strcmp(word,"atoms") == 0) { + addfield("Atoms",&Thermo::compute_atoms,INT); + } else if (strcmp(word,"cpu") == 0) { + addfield("CPU",&Thermo::compute_cpu,FLOAT); + } else if (strcmp(word,"temp") == 0) { + addfield("Temp",&Thermo::compute_temp,FLOAT); + tempflag = 1; + } else if (strcmp(word,"press") == 0) { + addfield("Press",&Thermo::compute_press,FLOAT); + tempflag = pressflag = 1; + } else if (strcmp(word,"pe") == 0) { + peflag = 1; + addfield("PotEng",&Thermo::compute_pe,FLOAT); + } else if (strcmp(word,"ke") == 0) { + addfield("KinEng",&Thermo::compute_ke,FLOAT); + tempflag = 1; + } else if (strcmp(word,"eng") == 0) { + addfield("TotEng",&Thermo::compute_eng,FLOAT); + tempflag = 1; + } else if (strcmp(word,"evdwl") == 0) { + addfield("E_vdwl",&Thermo::compute_evdwl,FLOAT); + } else if (strcmp(word,"ecoul") == 0) { + addfield("E_coul",&Thermo::compute_ecoul,FLOAT); + } else if (strcmp(word,"epair") == 0) { + addfield("E_pair",&Thermo::compute_epair,FLOAT); + } else if (strcmp(word,"ebond") == 0) { + addfield("E_bond",&Thermo::compute_ebond,FLOAT); + } else if (strcmp(word,"eangle") == 0) { + addfield("E_angle",&Thermo::compute_eangle,FLOAT); + } else if (strcmp(word,"edihed") == 0) { + addfield("E_dihed",&Thermo::compute_edihed,FLOAT); + } else if (strcmp(word,"eimp") == 0) { + addfield("E_impro",&Thermo::compute_eimp,FLOAT); + } else if (strcmp(word,"emol") == 0) { + addfield("E_mol",&Thermo::compute_emol,FLOAT); + } else if (strcmp(word,"elong") == 0) { + addfield("E_long",&Thermo::compute_elong,FLOAT); + } else if (strcmp(word,"etail") == 0) { + addfield("E_tail",&Thermo::compute_etail,FLOAT); + } else if (strcmp(word,"erot") == 0) { + addfield("E_rot",&Thermo::compute_erot,FLOAT); + dipoleflag = 1; + } else if (strcmp(word,"vol") == 0) { + addfield("Volume",&Thermo::compute_vol,FLOAT); + } else if (strcmp(word,"lx") == 0) { + addfield("Lx",&Thermo::compute_lx,FLOAT); + } else if (strcmp(word,"ly") == 0) { + addfield("Ly",&Thermo::compute_ly,FLOAT); + } else if (strcmp(word,"lz") == 0) { + addfield("Lz",&Thermo::compute_lz,FLOAT); + } else if (strcmp(word,"pxx") == 0) { + addfield("Pxx",&Thermo::compute_pxx,FLOAT); + tempflag = pressflag = tensorflag = 1; + } else if (strcmp(word,"pyy") == 0) { + addfield("Pyy",&Thermo::compute_pyy,FLOAT); + tempflag = pressflag = tensorflag = 1; + } else if (strcmp(word,"pzz") == 0) { + addfield("Pzz",&Thermo::compute_pzz,FLOAT); + tempflag = pressflag = tensorflag = 1; + } else if (strcmp(word,"pxy") == 0) { + addfield("Pxy",&Thermo::compute_pxy,FLOAT); + tempflag = pressflag = tensorflag = 1; + } else if (strcmp(word,"pxz") == 0) { + addfield("Pxz",&Thermo::compute_pxz,FLOAT); + tempflag = pressflag = tensorflag = 1; + } else if (strcmp(word,"pyz") == 0) { + addfield("Pyz",&Thermo::compute_pyz,FLOAT); + tempflag = pressflag = tensorflag = 1; + } else if (strcmp(word,"gke") == 0) { + addfield("Trans-KE",&Thermo::compute_gke,FLOAT); + granflag = 1; + } else if (strcmp(word,"grot") == 0) { + addfield("Rot-KE",&Thermo::compute_grot,FLOAT); + granflag = 1; + } else if (strcmp(word,"tave") == 0) { + addfield("T_ave",&Thermo::compute_tave,FLOAT); + tempflag = 1; + } else if (strcmp(word,"pave") == 0) { + addfield("P_ave",&Thermo::compute_pave,FLOAT); + pressflag = 1; + } else if (strcmp(word,"eave") == 0) { + addfield("E_ave",&Thermo::compute_eave,FLOAT); + } else if (strcmp(word,"peave") == 0) { + addfield("PE_ave",&Thermo::compute_peave,FLOAT); + + // user-defined temperature (t_ID) + // only 1st 8 chars of ID are passed to addfield + // extend tempindices and store index of ID into force::templist + + } else if (strncmp(word,"t_",2) == 0) { + char copy[9]; + strncpy(copy,word,8); + copy[0] = 'T'; + copy[8] = '\0'; + addfield(copy,&Thermo::compute_t_id,FLOAT); + int tempwhich; + for (tempwhich = 0; tempwhich < force->ntemp; tempwhich++) + if (strcmp(&word[2],force->templist[tempwhich]->id) == 0) break; + if (tempwhich == force->ntemp) + error->all("Could not find thermo temperature ID"); + tempindices = (int *) memory->srealloc(tempindices, + (ntemp+1)*sizeof(int), + "thermo:tempindices"); + tempindices[ntemp++] = tempwhich; + } else error->all("Invalid keyword in thermo_style command"); + + word = strtok(NULL," \0"); + } +} + +/* ---------------------------------------------------------------------- + add one field to list of quantities to print + extend keyword,vfunc,vtype arrays +------------------------------------------------------------------------- */ + +void Thermo::addfield(char *key, FnPtr func, int typeflag) +{ + keyword = (char **) + memory->srealloc(keyword,(nfield+1)*sizeof(char *),"thermo:keyword"); + vfunc = (FnPtr *) + memory->srealloc(vfunc,(nfield+1)*sizeof(FnPtr),"thermo:vfunc"); + vtype = (int *) + memory->srealloc(vtype,(nfield+1)*sizeof(int),"thermo:vtype"); + + int n = strlen(key) + 1; + keyword[nfield] = new char[n]; + strcpy(keyword[nfield],key); + vfunc[nfield] = func; + vtype[nfield] = typeflag; + nfield++; +} + +/* ---------------------------------------------------------------------- + compute a single thermodyanmic value matching word to custom list + called by variable evaulation function from input script + return it as double in answer + return 0 if OK, 1 if str is invalid + customize by adding to if statement + not all keywords are supported + only add keyword if it makes sense in input script variable +------------------------------------------------------------------------- */ + +int Thermo::compute_value(char *word, double *answer) +{ + if (strcmp(word,"temp") == 0) { + tempvalue = temperature->compute(); + compute_temp(); + } else if (strcmp(word,"press") == 0) { + tempvalue = temperature->compute(); + pressure->compute(temperature); + compute_press(); + } else if (strcmp(word,"pe") == 0) { + fix_compute_pe(); + compute_pe(); + } else if (strcmp(word,"ke") == 0) { + tempvalue = temperature->compute(); + compute_ke(); + } else if (strcmp(word,"eng") == 0) { + tempvalue = temperature->compute(); + fix_compute_pe(); + compute_eng(); + } else if (strcmp(word,"evdwl") == 0) compute_evdwl(); + else if (strcmp(word,"ecoul") == 0) compute_ecoul(); + else if (strcmp(word,"epair") == 0) compute_epair(); + else if (strcmp(word,"ebond") == 0) compute_ebond(); + else if (strcmp(word,"eangle") == 0) compute_eangle(); + else if (strcmp(word,"edihed") == 0) compute_edihed(); + else if (strcmp(word,"eimp") == 0) compute_eimp(); + else if (strcmp(word,"emol") == 0) compute_emol(); + else if (strcmp(word,"elong") == 0) compute_elong(); + else if (strcmp(word,"etail") == 0) compute_etail(); + else if (strcmp(word,"erot") == 0) compute_erot(); + else if (strcmp(word,"gke") == 0) compute_gke(); + else if (strcmp(word,"grot") == 0) compute_grot(); + else if (strncmp(word,"t_",2) == 0) { + int tempwhich; + for (tempwhich = 0; tempwhich < force->ntemp; tempwhich++) + if (strcmp(&word[2],force->templist[tempwhich]->id) == 0) break; + if (tempwhich == force->ntemp) return 1; + dvalue = force->templist[tempwhich]->compute(); + } else return 1; + + *answer = dvalue; + return 0; +} + +/* ---------------------------------------------------------------------- + compute potential energy from any fixes + called when compute_pe() is called directly and not via Thermo::compute() + e.g. from minimize or Thermo::compute_value() +------------------------------------------------------------------------- */ + +void Thermo::fix_compute_pe() +{ + if (nfix_energy) modify->thermo_compute(fixvalues); +} + +/* ---------------------------------------------------------------------- + one routine for every quantity thermo can output + set ivalue/dvalue if value is integer/double + customize by adding a method +------------------------------------------------------------------------- */ + +void Thermo::compute_step() +{ + ivalue = update->ntimestep; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_atoms() +{ + ivalue = static_cast<int> (natoms); +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_cpu() +{ + if (firststep == 0) dvalue = 0.0; + else dvalue = timer->elapsed(TIME_LOOP); +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_temp() +{ + dvalue = tempvalue; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_press() +{ + dvalue = pressure->p_total; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_pe() +{ + double tmp = 0.0; + if (force->pair) tmp += force->pair->eng_vdwl + force->pair->eng_coul; + if (force->bond) tmp += force->bond->eng_vdwl; + if (force->dihedral) + tmp += force->dihedral->eng_vdwl + force->dihedral->eng_coul; + + if (atom->molecular) { + if (force->bond) tmp += force->bond->energy; + if (force->angle) tmp += force->angle->energy; + if (force->dihedral) tmp += force->dihedral->energy; + if (force->improper) tmp += force->improper->energy; + } + + MPI_Allreduce(&tmp,&dvalue,1,MPI_DOUBLE,MPI_SUM,world); + + if (force->kspace) dvalue += force->kspace->energy; + if (force->pair->tail_flag) { + double volume = domain->xprd * domain->yprd * domain->zprd; + dvalue += force->pair->etail / volume; + } + if (nfix_energy) + for (int i = 0; i < nfix_energy; i++) dvalue += fixvalues[fixenergy[i]]; + + if (normflag) dvalue /= natoms; + + // also store PE in potential_energy so other classes can grab it + + potential_energy = dvalue; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_ke() +{ + dvalue = 0.5 * temperature->dof * force->boltz * tempvalue; + if (normflag) dvalue /= natoms; +} + + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_eng() +{ + compute_pe(); + double ke = 0.5 * temperature->dof * force->boltz * tempvalue; + if (normflag) ke /= natoms; + dvalue += ke; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_evdwl() +{ + double tmp = 0.0; + if (force->pair) tmp += force->pair->eng_vdwl; + if (force->bond) tmp += force->bond->eng_vdwl; + if (force->dihedral) tmp += force->dihedral->eng_vdwl; + + MPI_Allreduce(&tmp,&dvalue,1,MPI_DOUBLE,MPI_SUM,world); + + if (force->pair->tail_flag) { + double volume = domain->xprd * domain->yprd * domain->zprd; + dvalue += force->pair->etail / volume; + } + + if (normflag) dvalue /= natoms; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_ecoul() +{ + double tmp = 0.0; + if (force->pair) tmp += force->pair->eng_coul; + if (force->dihedral) tmp += force->dihedral->eng_coul; + MPI_Allreduce(&tmp,&dvalue,1,MPI_DOUBLE,MPI_SUM,world); + if (normflag) dvalue /= natoms; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_epair() +{ + double tmp = 0.0; + if (force->pair) tmp += force->pair->eng_vdwl + force->pair->eng_coul; + if (force->bond) tmp += force->bond->eng_vdwl; + if (force->dihedral) + tmp += force->dihedral->eng_vdwl + force->dihedral->eng_coul; + MPI_Allreduce(&tmp,&dvalue,1,MPI_DOUBLE,MPI_SUM,world); + + if (force->kspace) dvalue += force->kspace->energy; + if (force->pair->tail_flag) { + double volume = domain->xprd * domain->yprd * domain->zprd; + dvalue += force->pair->etail / volume; + } + + if (normflag) dvalue /= natoms; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_ebond() +{ + if (force->bond) { + double tmp = force->bond->energy; + MPI_Allreduce(&tmp,&dvalue,1,MPI_DOUBLE,MPI_SUM,world); + if (normflag) dvalue /= natoms; + } else dvalue = 0.0; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_eangle() +{ + if (force->angle) { + double tmp = force->angle->energy; + MPI_Allreduce(&tmp,&dvalue,1,MPI_DOUBLE,MPI_SUM,world); + if (normflag) dvalue /= natoms; + } else dvalue = 0.0; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_edihed() +{ + if (force->dihedral) { + double tmp = force->dihedral->energy; + MPI_Allreduce(&tmp,&dvalue,1,MPI_DOUBLE,MPI_SUM,world); + if (normflag) dvalue /= natoms; + } else dvalue = 0.0; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_eimp() +{ + if (force->improper) { + double tmp = force->improper->energy; + MPI_Allreduce(&tmp,&dvalue,1,MPI_DOUBLE,MPI_SUM,world); + if (normflag) dvalue /= natoms; + } else dvalue = 0.0; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_emol() +{ + double tmp = 0.0; + if (atom->molecular) { + if (force->bond) tmp += force->bond->energy; + if (force->angle) tmp += force->angle->energy; + if (force->dihedral) tmp += force->dihedral->energy; + if (force->improper) tmp += force->improper->energy; + MPI_Allreduce(&tmp,&dvalue,1,MPI_DOUBLE,MPI_SUM,world); + if (normflag) dvalue /= natoms; + } else dvalue = 0.0; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_elong() +{ + if (force->kspace) { + dvalue = force->kspace->energy; + if (normflag) dvalue /= natoms; + } else dvalue = 0.0; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_etail() +{ + if (force->pair->tail_flag) { + double volume = domain->xprd * domain->yprd * domain->zprd; + dvalue = force->pair->etail / volume; + if (normflag) dvalue /= natoms; + } else dvalue = 0.0; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_erot() +{ + int *type = atom->type; + double *dipole = atom->dipole; + double **omega = atom->omega; + int nlocal = atom->nlocal; + + double erot = 0.0; + for (int i = 0; i < nlocal; i++) { + if (dipole[type[i]] > 0.0) + erot += inertia[type[i]] * + (omega[i][0]*omega[i][0] + omega[i][1]*omega[i][1] + + omega[i][2]*omega[i][2]); + } + double all; + MPI_Allreduce(&erot,&all,1,MPI_DOUBLE,MPI_SUM,world); + dvalue = 0.5 * all; + if (normflag) dvalue /= natoms; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_vol() +{ + dvalue = domain->xprd * domain->yprd * domain->zprd; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_lx() +{ + dvalue = domain->xprd; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_ly() +{ + dvalue = domain->yprd; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_lz() +{ + dvalue = domain->zprd; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_pxx() +{ + dvalue = pressure->p_tensor[0]; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_pyy() +{ + dvalue = pressure->p_tensor[1]; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_pzz() +{ + dvalue = pressure->p_tensor[2]; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_pxy() +{ + dvalue = pressure->p_tensor[3]; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_pxz() +{ + dvalue = pressure->p_tensor[4]; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_pyz() +{ + dvalue = pressure->p_tensor[5]; +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_gke() +{ + double *rmass = atom->rmass; + double **v = atom->v; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double nactive = 0.0; + double ke = 0.0; + for (int i = 0; i < nlocal; i++) + if (!(mask[i] & freeze_group_bit)) { + nactive++; + ke += (v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]) * rmass[i]; + } + + ke *= 0.5 * force->mvv2e; + MPI_Allreduce(&ke,&dvalue,1,MPI_DOUBLE,MPI_SUM,world); + if (normflag) { + ke = dvalue; + MPI_Allreduce(&nactive,&dvalue,1,MPI_DOUBLE,MPI_SUM,world); + if (dvalue > 0) dvalue = ke / dvalue; + } +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_grot() +{ + double *rmass = atom->rmass; + double *radius = atom->radius; + double **phiv = atom->phiv; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double nactive = 0.0; + double ephi = 0.0; + for (int i = 0; i < nlocal; i++) + if (!(mask[i] & freeze_group_bit)) { + nactive++; + ephi += (phiv[i][0]*phiv[i][0] + phiv[i][1]*phiv[i][1] + + phiv[i][2]*phiv[i][2]) * radius[i]*radius[i]*rmass[i]; + } + + if (force->dimension == 3) ephi *= 0.5 * force->mvv2e * INERTIA3D; + else ephi *= 0.5 * force->mvv2e * INERTIA2D; + MPI_Allreduce(&ephi,&dvalue,1,MPI_DOUBLE,MPI_SUM,world); + if (normflag) { + ephi = dvalue; + MPI_Allreduce(&nactive,&dvalue,1,MPI_DOUBLE,MPI_SUM,world); + if (dvalue > 0) dvalue = ephi / dvalue; + } +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_tave() +{ + if (firststep == 0) { + if (npartial_t == 0) { + ncurrent_t = 0; + npartial_t = 1; + tsum = tpast[0] = tempvalue; + } + dvalue = tsum/npartial_t; + } else { + ncurrent_t++; + if (ncurrent_t == nwindow) ncurrent_t = 0; + if (npartial_t == nwindow) tsum -= tpast[ncurrent_t]; + else npartial_t++; + tpast[ncurrent_t] = tempvalue; + tsum += tpast[ncurrent_t]; + dvalue = tsum/npartial_t; + } +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_pave() +{ + if (firststep == 0) { + if (npartial_p == 0) { + ncurrent_p = 0; + npartial_p = 1; + psum = ppast[0] = pressure->p_total; + } + dvalue = psum/npartial_p; + } else { + ncurrent_p++; + if (ncurrent_p == nwindow) ncurrent_p = 0; + if (npartial_p == nwindow) psum -= ppast[ncurrent_p]; + else npartial_p++; + ppast[ncurrent_p] = pressure->p_total; + psum += ppast[ncurrent_p]; + dvalue = psum/npartial_p; + } +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_eave() +{ + compute_eng(); + + if (firststep == 0) { + if (npartial_e == 0) { + ncurrent_e = 0; + npartial_e = 1; + esum = epast[0] = dvalue; + } + dvalue = esum/npartial_e; + } else { + ncurrent_e++; + if (ncurrent_e == nwindow) ncurrent_e = 0; + if (npartial_e == nwindow) esum -= epast[ncurrent_e]; + else npartial_e++; + epast[ncurrent_e] = dvalue; + esum += epast[ncurrent_e]; + dvalue = esum/npartial_e; + } +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_peave() +{ + compute_pe(); + + if (firststep == 0) { + if (npartial_pe == 0) { + ncurrent_pe = 0; + npartial_pe = 1; + pesum = pepast[0] = dvalue; + } + dvalue = pesum/npartial_pe; + } else { + ncurrent_pe++; + if (ncurrent_pe == nwindow) ncurrent_pe = 0; + if (npartial_pe == nwindow) pesum -= pepast[ncurrent_pe]; + else npartial_pe++; + pepast[ncurrent_pe] = dvalue; + pesum += pepast[ncurrent_pe]; + dvalue = pesum/npartial_pe; + } +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_t_id() +{ + dvalue = force->templist[tempindices[itemp_print++]]->compute(); +} + +/* ---------------------------------------------------------------------- */ + +void Thermo::compute_fix() +{ + dvalue = fixvalues[fixprint[ifix_print++]]; + if (normflag) dvalue /= natoms; +} diff --git a/src/thermo.h b/src/thermo.h new file mode 100644 index 0000000000000000000000000000000000000000..5de47e91b4710ea60dd6df56b2f56d706b3f9d9e --- /dev/null +++ b/src/thermo.h @@ -0,0 +1,133 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef THERMO_H +#define THERMO_H + +#include "lammps.h" + +class Temperature; +class Pressure; + +class Thermo : public LAMMPS { + friend class WriteRestart; // accesses lostflag + friend class Pressure; // accesses temperature,tensorflag + friend class Temper; // accesses peflag,potential_energy + friend class MinCG; // accesses compute_pe,potential_energy + + public: + char *style; + + Thermo(int, char **); + ~Thermo(); + void init(); + double lost_check(); + void modify_params(int, char **); + void header(); + void compute(int); + int compute_value(char *, double *); + void fix_compute_pe(); + + private: + int me; + char *line; + int nfield,nfield_initial; + char **keyword; + int *vtype; + int freeze_group_bit; + int tensorflag,granflag,dipoleflag; + double *inertia; + + char *format_int_def,*format_g_def,*format_f_def,*format_multi; + int format_iflag,format_fflag; + char *format_int_user,*format_float_user; + int nformat; + char **format_user; + int *format_index; + char **format; + + int normflag; // 0 if do not normalize by atoms, 1 if normalize + int normvalue; // use this for normflag unless natoms = 0 + int normuserflag; // 0 if user has not set, 1 if has + int normuser; + + int firststep; + int lostflag,lostbefore; + int flushflag,lineflag; + + Pressure *pressure; + Temperature *temperature; + + int ivalue,tempflag,pressflag,peflag; + double dvalue,natoms,tempvalue,potential_energy; + + int ntemp,itemp_print; + int *tempindices; + int nfix,ifix_print,nfix_print,nfix_energy; + double *fixvalues; + int *fixflags,*fixenergy,*fixprint; + int nextra; + + int nwindow; + int npartial_t,ncurrent_t,npartial_p,ncurrent_p; + int npartial_e,ncurrent_e,npartial_pe,ncurrent_pe; + double tsum,psum,esum,pesum; + double *tpast,*ppast,*epast,*pepast; + + typedef void (Thermo::*FnPtr)(); + void parse_fields(char *); + void addfield(char *, FnPtr, int); + FnPtr *vfunc; // list of ptrs to functions + + // customize by adding a method prototype + + void compute_step(); // functions that each compute one value to print + void compute_atoms(); + void compute_cpu(); + void compute_temp(); + void compute_press(); + void compute_pe(); + void compute_ke(); + void compute_eng(); + void compute_evdwl(); + void compute_ecoul(); + void compute_epair(); + void compute_ebond(); + void compute_eangle(); + void compute_edihed(); + void compute_eimp(); + void compute_emol(); + void compute_elong(); + void compute_etail(); + void compute_erot(); + void compute_vol(); + void compute_lx(); + void compute_ly(); + void compute_lz(); + void compute_pxx(); + void compute_pyy(); + void compute_pzz(); + void compute_pxy(); + void compute_pyz(); + void compute_pxz(); + void compute_gke(); + void compute_grot(); + void compute_fix(); + void compute_tave(); + void compute_pave(); + void compute_eave(); + void compute_peave(); + void compute_t_id(); +}; + +#endif diff --git a/src/timer.cpp b/src/timer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..385af066a8be8e0e4c4f18f107d5c1966ccb296f --- /dev/null +++ b/src/timer.cpp @@ -0,0 +1,82 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "timer.h" +#include "memory.h" + +/* ---------------------------------------------------------------------- */ + +Timer::Timer() +{ + array = (double *) memory->smalloc(TIME_N*sizeof(double),"array"); +} + +/* ---------------------------------------------------------------------- */ + +Timer::~Timer() +{ + memory->sfree(array); +} + +/* ---------------------------------------------------------------------- */ + +void Timer::init() +{ + for (int i = 0; i < TIME_N; i++) array[i] = 0.0; +} + +/* ---------------------------------------------------------------------- */ + +void Timer::stamp() +{ + // uncomment if want synchronized timing + // MPI_Barrier(world); + previous_time = MPI_Wtime(); +} + +/* ---------------------------------------------------------------------- */ + +void Timer::stamp(int which) +{ + // uncomment if want synchronized timing + // MPI_Barrier(world); + double current_time = MPI_Wtime(); + array[which] += current_time - previous_time; + previous_time = current_time; +} + +/* ---------------------------------------------------------------------- */ + +void Timer::barrier_start(int which) +{ + MPI_Barrier(world); + array[which] = MPI_Wtime(); +} + +/* ---------------------------------------------------------------------- */ + +void Timer::barrier_stop(int which) +{ + MPI_Barrier(world); + double current_time = MPI_Wtime(); + array[which] = current_time - array[which]; +} + +/* ---------------------------------------------------------------------- */ + +double Timer::elapsed(int which) +{ + double current_time = MPI_Wtime(); + return (current_time - array[which]); +} diff --git a/src/timer.h b/src/timer.h new file mode 100644 index 0000000000000000000000000000000000000000..be0be7a72ea78039fa31166f2e2b7a61b7dc36f6 --- /dev/null +++ b/src/timer.h @@ -0,0 +1,47 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef TIMER_H +#define TIMER_H + +#include "lammps.h" + +#define TIME_TOTAL 0 +#define TIME_LOOP 1 +#define TIME_PAIR 2 +#define TIME_BOND 3 +#define TIME_KSPACE 4 +#define TIME_NEIGHBOR 5 +#define TIME_COMM 6 +#define TIME_OUTPUT 7 + +#define TIME_N 8 + +class Timer : public LAMMPS { + public: + double *array; + + Timer(); + ~Timer(); + void init(); + void stamp(); + void stamp(int); + void barrier_start(int); + void barrier_stop(int); + double elapsed(int); + + private: + double previous_time; +}; + +#endif diff --git a/src/universe.cpp b/src/universe.cpp new file mode 100644 index 0000000000000000000000000000000000000000..90db6851b110cad380e9cfc757ab85a4fef8faca --- /dev/null +++ b/src/universe.cpp @@ -0,0 +1,100 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "stdlib.h" +#include "string.h" +#include "stdio.h" +#include "universe.h" +#include "memory.h" + +/* ---------------------------------------------------------------------- + create & initialize the universe of processors in communicator +------------------------------------------------------------------------- */ + +Universe::Universe(MPI_Comm communicator) +{ + version = "1 Oct 2006"; + + uworld = communicator; + MPI_Comm_rank(uworld,&me); + MPI_Comm_size(uworld,&nprocs); + + uscreen = stdout; + ulogfile = NULL; + + nworlds = 0; + procs_per_world = NULL; + root_proc = NULL; +} + +/* ---------------------------------------------------------------------- */ + +Universe::~Universe() +{ + memory->sfree(procs_per_world); + memory->sfree(root_proc); +} + +/* ---------------------------------------------------------------------- + add 1 or more worlds to universe + str == NULL -> add 1 world with all procs in universe + str = NxM -> add N worlds, each with M procs + str = P -> add 1 world with P procs +------------------------------------------------------------------------- */ + +void Universe::add_world(char *str) +{ + int n,nper; + char *ptr; + + if (str == NULL) { + n = 1; + nper = nprocs; + } else if ((ptr = strchr(str,'x')) != NULL) { + *ptr = '\0'; + n = atoi(str); + nper = atoi(ptr+1); + } else { + n = 1; + nper = atoi(str); + } + + procs_per_world = + (int *) memory->srealloc(procs_per_world,(nworlds+n)*sizeof(int), + "universe:procs_per_world"); + root_proc = + (int *) memory->srealloc(root_proc,(nworlds+n)*sizeof(int), + "universe:root_proc"); + + for (int i = 0; i < n; i++) { + procs_per_world[nworlds] = nper; + if (nworlds == 0) root_proc[nworlds] = 0; + else + root_proc[nworlds] = root_proc[nworlds-1] + procs_per_world[nworlds-1]; + if (me >= root_proc[nworlds]) iworld = nworlds; + nworlds++; + } +} + +/* ---------------------------------------------------------------------- + check if total procs in all worlds = procs in universe +------------------------------------------------------------------------- */ + +int Universe::consistent() +{ + int n = 0; + for (int i = 0; i < nworlds; i++) n += procs_per_world[i]; + if (n == nprocs) return 1; + else return 0; +} diff --git a/src/universe.h b/src/universe.h new file mode 100644 index 0000000000000000000000000000000000000000..fdcad37630161931cc1c014952bdcddac6884b62 --- /dev/null +++ b/src/universe.h @@ -0,0 +1,42 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef UNIVERSE_H +#define UNIVERSE_H + +#include "mpi.h" +#include "stdio.h" +#include "lammps.h" + +class Universe : public LAMMPS { + public: + char *version; // LAMMPS version string = date + + MPI_Comm uworld; // communicator for entire universe + int me,nprocs; // my place in universe + + FILE *uscreen; // universe screen output + FILE *ulogfile; // universe logfile + + int nworlds; // # of worlds in universe + int iworld; // which world I am in + int *procs_per_world; // # of procs in each world + int *root_proc; // root proc in each world + + Universe(MPI_Comm); + ~Universe(); + void add_world(char *); + int consistent(); +}; + +#endif diff --git a/src/update.cpp b/src/update.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8f8c6a5ac3ef539278a852c683830b3e48050700 --- /dev/null +++ b/src/update.cpp @@ -0,0 +1,188 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "update.h" +#include "neighbor.h" +#include "force.h" +#include "output.h" +#include "memory.h" +#include "error.h" + +#define IntegrateInclude +#define MinimizeInclude +#include "style.h" +#undef IntegrateInclude +#undef MinimizeInclude + +/* ---------------------------------------------------------------------- */ + +Update::Update() +{ + int n; + char *str; + + whichflag = -1; + ntimestep = 0; + first_update = 0; + + maxpair = 0; + f_pair = NULL; + + unit_style = NULL; + set_units("lj"); + + str = "verlet"; + n = strlen(str) + 1; + integrate_style = new char[n]; + strcpy(integrate_style,str); + integrate = new Verlet(0,NULL); + + str = "cg"; + n = strlen(str) + 1; + minimize_style = new char[n]; + strcpy(minimize_style,str); + minimize = new MinCG(); +} + +/* ---------------------------------------------------------------------- */ + +Update::~Update() +{ + memory->destroy_2d_double_array(f_pair); + + delete [] unit_style; + + delete [] integrate_style; + delete integrate; + + delete [] minimize_style; + delete minimize; +} + +/* ---------------------------------------------------------------------- */ + +void Update::init() +{ + // init the appropriate integrate or minimize class + // if neither (e.g. from write_restart) then just return + + if (whichflag == -1) return; + else if (whichflag == 0) integrate->init(); + else if (whichflag == 1) minimize->init(); + + // only set first_update if a run or minimize is being performed + + first_update = 1; +} + +/* ---------------------------------------------------------------------- */ + +void Update::set_units(char *style) +{ + if (strcmp(style,"lj") == 0) { + force->boltz = 1.0; + force->mvv2e = 1.0; + force->ftm2v = 1.0; + force->nktv2p = 1.0; + force->qqr2e = 1.0; + force->qe2f = 1.0; + dt = 0.005; + neighbor->skin = 0.3; + + } else if (strcmp(style,"real") == 0) { + force->boltz = 0.001987191; + force->mvv2e = 48.88821 * 48.88821; + force->ftm2v = 1.0 / 48.88821 / 48.88821; + force->nktv2p = 68589.796; + force->qqr2e = 332.0636; + force->qe2f = 23.0602; + dt = 1.0; + neighbor->skin = 2.0; + + } else if (strcmp(style,"metal") == 0) { + force->boltz = 8.617e-5; + force->mvv2e = 1.0365e-4; + force->ftm2v = 1 / 1.0365e-4; + force->nktv2p = 1.602e6; + force->qqr2e = 14.40659; + force->qe2f = 1.0; + dt = 0.001; + neighbor->skin = 2.0; + } else error->all("Illegal units command"); + + delete [] unit_style; + int n = strlen(style) + 1; + unit_style = new char[n]; + strcpy(unit_style,style); +} + +/* ---------------------------------------------------------------------- */ + +void Update::create_integrate(int narg, char **arg) +{ + if (narg < 1) error->all("Illegal run_style command"); + + delete [] integrate_style; + delete integrate; + + if (0) return; // dummy line to enable else-if macro expansion + +#define IntegrateClass +#define IntegrateStyle(key,Class) \ + else if (strcmp(arg[0],#key) == 0) integrate = new Class(narg-1,&arg[1]); +#include "style.h" +#undef IntegrateClass + + else error->all("Illegal run_style command"); + + int n = strlen(arg[0]) + 1; + integrate_style = new char[n]; + strcpy(integrate_style,arg[0]); +} + +/* ---------------------------------------------------------------------- */ + +void Update::create_minimize(int narg, char **arg) +{ + if (narg != 1) error->all("Illegal min_style command"); + + delete [] minimize_style; + delete minimize; + + if (0) return; // dummy line to enable else-if macro expansion + +#define MinimizeClass +#define MinimizeStyle(key,Class) \ + else if (strcmp(arg[0],#key) == 0) minimize = new Class(); +#include "style.h" +#undef MinimizeClass + + else error->all("Illegal min_style command"); + + int n = strlen(arg[0]) + 1; + minimize_style = new char[n]; + strcpy(minimize_style,arg[0]); +} + +/* ---------------------------------------------------------------------- + memory usage of update and integrate/minimize +------------------------------------------------------------------------- */ + +int Update::memory_usage() +{ + int bytes = maxpair*3 * sizeof(double); + if (whichflag == 0) bytes += integrate->memory_usage(); + else if (whichflag == 1) bytes += minimize->memory_usage(); + return bytes; +} diff --git a/src/update.h b/src/update.h new file mode 100644 index 0000000000000000000000000000000000000000..3db0dd0e20007b0af0c9154915d70e64a633aba9 --- /dev/null +++ b/src/update.h @@ -0,0 +1,54 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef UPDATE_H +#define UPDATE_H + +#include "lammps.h" + +class Integrate; +class Min; + +class Update : public LAMMPS { + public: + double dt; // timestep + double tolerance; // minimizer tolerance + int ntimestep; // current step (dynamics or min iter) + int nsteps; // # of steps to run (dynamics or min iter) + int firststep,laststep; // 1st & last step of this run + int beginstep,endstep; // 1st and last step of multiple runs + int first_update; // 0 before initial update, 1 after + int max_eval; // max force evaluations for minimizer + int whichflag; // 0 for time integration, 1 for minimization + + double **f_pair; // used by pair to compute force & virial + int maxpair; + + char *unit_style; + + Integrate *integrate; + char *integrate_style; + + Min *minimize; + char *minimize_style; + + Update(); + ~Update(); + void init(); + void set_units(char *); + void create_integrate(int, char **); + void create_minimize(int, char **); + int memory_usage(); +}; + +#endif diff --git a/src/variable.cpp b/src/variable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8405739d09f5cba1f15669a369ed0641b54f63c2 --- /dev/null +++ b/src/variable.cpp @@ -0,0 +1,695 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "unistd.h" +#include "variable.h" +#include "universe.h" +#include "atom.h" +#include "update.h" +#include "group.h" +#include "domain.h" +#include "output.h" +#include "thermo.h" +#include "memory.h" +#include "error.h" + +#define VARDELTA 4 + +enum{INDEX,LOOP,EQUAL,WORLD,UNIVERSE}; + +/* ---------------------------------------------------------------------- */ + +Variable::Variable() +{ + MPI_Comm_rank(world,&me); + + nvar = maxvar = 0; + names = NULL; + style = NULL; + num = NULL; + index = NULL; + data = NULL; +} + +/* ---------------------------------------------------------------------- */ + +Variable::~Variable() +{ + for (int i = 0; i < nvar; i++) { + delete [] names[i]; + for (int j = 0; j < num[i]; j++) delete [] data[i][j]; + delete [] data[i]; + } + memory->sfree(names); + memory->sfree(style); + memory->sfree(num); + memory->sfree(index); + memory->sfree(data); +} + +/* ---------------------------------------------------------------------- + called by variable command in input script +------------------------------------------------------------------------- */ + +void Variable::set(int narg, char **arg) +{ + if (narg < 3) error->all("Illegal variable command"); + + // if var already exists, just skip (except EQUAL vars) + + if (find(arg[0]) >= 0 && strcmp(arg[1],"equal") != 0) return; + + // make space for new variable + + if (nvar == maxvar) { + maxvar += VARDELTA; + names = (char **) + memory->srealloc(names,maxvar*sizeof(char *),"var:names"); + style = (int *) memory->srealloc(style,maxvar*sizeof(int),"var:style"); + num = (int *) memory->srealloc(num,maxvar*sizeof(int),"var:num"); + index = (int *) memory->srealloc(index,maxvar*sizeof(int),"var:index"); + data = (char ***) + memory->srealloc(data,maxvar*sizeof(char **),"var:data"); + } + + // INDEX + // num = listed args, index = 1st value, data = copied args + + if (strcmp(arg[1],"index") == 0) { + style[nvar] = INDEX; + num[nvar] = narg - 2; + index[nvar] = 0; + data[nvar] = new char*[num[nvar]]; + copy(num[nvar],&arg[2],data[nvar]); + + // LOOP + // num = N, index = 1st value, data = list of NULLS since never used + + } else if (strcmp(arg[1],"loop") == 0) { + style[nvar] = LOOP; + num[nvar] = atoi(arg[2]); + index[nvar] = 0; + data[nvar] = new char*[num[nvar]]; + for (int i = 0; i < num[nvar]; i++) data[nvar][i] = NULL; + + // EQUAL + // remove pre-existing var if also style EQUAL (allows it to be reset) + // num = 2, index = 1st value + // data = 2 values, 1st is string to eval, 2nd is filled on retrieval + + } else if (strcmp(arg[1],"equal") == 0) { + if (find(arg[0]) >= 0) { + if (style[find(arg[0])] != EQUAL) + error->all("Cannot redefine variable as a different style"); + remove(find(arg[0])); + } + style[nvar] = EQUAL; + num[nvar] = 2; + index[nvar] = 0; + data[nvar] = new char*[num[nvar]]; + copy(1,&arg[2],data[nvar]); + data[nvar][1] = NULL; + + // WORLD + // num = listed args, index = partition this proc is in, data = copied args + // error check that num = # of worlds in universe + + } else if (strcmp(arg[1],"world") == 0) { + style[nvar] = WORLD; + num[nvar] = narg - 2; + if (num[nvar] != universe->nworlds) + error->all("World variable count doesn't match # of partitions"); + index[nvar] = universe->iworld; + data[nvar] = new char*[num[nvar]]; + copy(num[nvar],&arg[2],data[nvar]); + + // UNIVERSE + // num = listed args, index = partition this proc is in, data = copied args + // universe proc 0 creates lock file + // error check that all other universe variables are same length + + } else if (strcmp(arg[1],"universe") == 0) { + style[nvar] = UNIVERSE; + num[nvar] = narg - 2; + if (num[nvar] < universe->nworlds) + error->all("Universe variable count < # of partitions"); + index[nvar] = universe->iworld; + data[nvar] = new char*[num[nvar]]; + copy(num[nvar],&arg[2],data[nvar]); + + if (universe->me == 0) { + FILE *fp = fopen("tmp.lammps.variable","w"); + fprintf(fp,"%d\n",universe->nworlds); + fclose(fp); + } + + for (int jvar = 0; jvar < nvar; jvar++) + if (num[jvar] && style[jvar] == UNIVERSE && num[nvar] != num[jvar]) + error->all("All universe variables must have same # of values"); + + if (me == 0) { + if (universe->uscreen) + fprintf(universe->uscreen, + "Initial ${%s} setting: value %d on partition %d\n", + arg[0],index[nvar]+1,universe->iworld); + if (universe->ulogfile) + fprintf(universe->ulogfile, + "Initial ${%s} setting: value %d on partition %d\n", + arg[0],index[nvar]+1,universe->iworld); + } + + } else error->all("Illegal variable command"); + + // set variable name (after possible EQUAL remove) + + names[nvar] = new char[strlen(arg[0]) + 1]; + strcpy(names[nvar],arg[0]); + nvar++; +} + +/* ---------------------------------------------------------------------- + single-value INDEX variable created by command-line argument +------------------------------------------------------------------------- */ + +void Variable::set(char *name, char *value) +{ + int ivar = find(name); + if (ivar >= 0) error->all("Command-line variable already exists"); + + if (nvar == maxvar) { + maxvar += VARDELTA; + names = (char **) + memory->srealloc(style,maxvar*sizeof(char *),"var:names"); + style = (int *) memory->srealloc(style,maxvar*sizeof(int),"var:style"); + num = (int *) memory->srealloc(num,maxvar*sizeof(int),"var:num"); + index = (int *) memory->srealloc(index,maxvar*sizeof(int),"var:index"); + data = (char ***) + memory->srealloc(data,maxvar*sizeof(char **),"var:data"); + } + + names[nvar] = new char[strlen(name) + 1]; + strcpy(names[nvar],name); + style[nvar] = INDEX; + num[nvar] = 1; + index[nvar] = 0; + data[nvar] = new char*[num[nvar]]; + copy(1,&value,data[nvar]); + nvar++; +} + +/* ---------------------------------------------------------------------- + increment variable(s) + return 0 if OK if successfully incremented + return 1 if any variable is exhausted, free the variable to allow re-use +------------------------------------------------------------------------- */ + +int Variable::next(int narg, char **arg) +{ + int ivar; + + if (narg == 0) error->all("Illegal next command"); + + // check that variables exist and are all the same style + + for (int iarg = 0; iarg < narg; iarg++) { + ivar = find(arg[iarg]); + if (ivar == -1) error->all("Invalid variable in next command"); + if (style[ivar] != style[find(arg[0])]) + error->all("All variables in next command must be same style"); + } + + // check for invalid styles EQUAL or WORLD + + int istyle = style[find(arg[0])]; + if (istyle == EQUAL || istyle == WORLD) + error->all("Invalid variable style with next command"); + + // increment all variables in list + // if any variable is exhausted, set flag = 1 and remove var to allow re-use + + int flag = 0; + + if (istyle == INDEX || istyle == LOOP) { + for (int iarg = 0; iarg < narg; iarg++) { + ivar = find(arg[iarg]); + index[ivar]++; + if (index[ivar] >= num[ivar]) { + flag = 1; + remove(ivar); + } + } + + } else if (istyle == UNIVERSE) { + + // wait until lock file can be created and owned by proc 0 of this world + // read next available index and Bcast it within my world + // set all variables in list to nextindex + + int nextindex; + if (me == 0) { + while (1) { + if (!rename("tmp.lammps.variable","tmp.lammps.variable.lock")) break; + usleep(100000); + } + FILE *fp = fopen("tmp.lammps.variable.lock","r"); + fscanf(fp,"%d",&nextindex); + fclose(fp); + fp = fopen("tmp.lammps.variable.lock","w"); + fprintf(fp,"%d\n",nextindex+1); + fclose(fp); + rename("tmp.lammps.variable.lock","tmp.lammps.variable"); + if (universe->uscreen) + fprintf(universe->uscreen, + "Increment via next: value %d on partition %d\n", + nextindex+1,universe->iworld); + if (universe->ulogfile) + fprintf(universe->ulogfile, + "Increment via next: value %d on partition %d\n", + nextindex+1,universe->iworld); + } + MPI_Bcast(&nextindex,1,MPI_INT,0,world); + + for (int iarg = 0; iarg < narg; iarg++) { + ivar = find(arg[iarg]); + index[ivar] = nextindex; + if (index[ivar] >= num[ivar]) { + flag = 1; + remove(ivar); + } + } + } + + return flag; +} + +/* ---------------------------------------------------------------------- + return ptr to the data text associated with a variable + return NULL if no variable or index is bad, caller must respond +------------------------------------------------------------------------- */ + +char *Variable::retrieve(char *name) +{ + int ivar = find(name); + if (ivar == -1) return NULL; + if (index[ivar] >= num[ivar]) return NULL; + + char *str; + if (style[ivar] == INDEX) { + str = data[ivar][index[ivar]]; + } else if (style[ivar] == LOOP) { + char *value = new char[16]; + sprintf(value,"%d",index[ivar]+1); + int n = strlen(value) + 1; + if (data[ivar][0]) delete [] data[ivar][0]; + data[ivar][0] = new char[n]; + strcpy(data[ivar][0],value); + delete [] value; + str = data[ivar][0]; + } else if (style[ivar] == EQUAL) { + char *value = evaluate(data[ivar][0]); + int n = strlen(value) + 1; + if (data[ivar][1]) delete [] data[ivar][1]; + data[ivar][1] = new char[n]; + strcpy(data[ivar][1],value); + delete [] value; + str = data[ivar][1]; + } else if (style[ivar] == WORLD) { + str = data[ivar][index[ivar]]; + } else if (style[ivar] == UNIVERSE) { + str = data[ivar][index[ivar]]; + } + return str; +} + +/* ---------------------------------------------------------------------- + search for name in list of variables names + return index or -1 if not found +------------------------------------------------------------------------- */ + +int Variable::find(char *name) +{ + for (int i = 0; i < nvar; i++) + if (strcmp(name,names[i]) == 0) return i; + return -1; +} + +/* ---------------------------------------------------------------------- + remove Nth variable from list and compact list +------------------------------------------------------------------------- */ + +void Variable::remove(int n) +{ + delete [] names[n]; + for (int i = 0; i < num[n]; i++) delete [] data[n][i]; + delete [] data[n]; + + for (int i = n+1; i < nvar; i++) { + names[i-1] = names[i]; + style[i-1] = style[i]; + num[i-1] = num[i]; + index[i-1] = index[i]; + data[i-1] = data[i]; + } + nvar--; +} + +/* ---------------------------------------------------------------------- + copy narg strings from **from to **to +------------------------------------------------------------------------- */ + +void Variable::copy(int narg, char **from, char **to) +{ + int n; + + for (int i = 0; i < narg; i++) { + n = strlen(from[i]) + 1; + to[i] = new char[n]; + strcpy(to[i],from[i]); + } +} + +/* ---------------------------------------------------------------------- + recursive method to evaluate a string + string can be a number: 0.0, -5.45, etc + string can be a keyword: lx, ly, lz, vol, etc + string can be a vector: x[123], y[3], vx[34], etc + value inside brackets must be global ID from 1 to Natoms + string can be a function: div(x,y), mult(x,y), add(x,y), xcm(group,x), etc + function args can be combo of number, keyword, vector, fn(), group, etc + see lists of valid keywords, vectors, and functions below + when string is evaluated, put result in a newly allocated string + return address of result string (will be freed by caller) +------------------------------------------------------------------------- */ + +char *Variable::evaluate(char *str) +{ + // allocate a new string for the eventual result + + char *result = new char[32]; + double answer; + + // if string is "function", grab one or two args, evaulate args recursively + + if (strchr(str,'(')) { + if (str[strlen(str)-1] != ')') + error->all("Cannot evaluate variable equal command"); + + char *ptr = strchr(str,'('); + int n = ptr - str; + char *func = new char[n+1]; + strncpy(func,str,n); + func[n] = '\0'; + + char *comma = ++ptr; + int level = 0; + while (1) { + if (*comma == '\0') + error->all("Cannot evaluate variable equal command"); + else if (*comma == ',' && level == 0) break; + else if (*comma == ')' && level == 0) break; + else if (*comma == '(') level++; + else if (*comma == ')') level--; + comma++; + } + + char *arg1,*arg2; + n = comma - ptr; + arg1 = new char[n+1]; + strncpy(arg1,ptr,n); + arg1[n] = '\0'; + + if (*comma == ',') { + ptr = comma + 1; + comma = &str[strlen(str)-1]; + n = comma - ptr; + arg2 = new char[n+1]; + strncpy(arg2,ptr,n); + arg2[n] = '\0'; + } else arg2 = NULL; + + double value1,value2; + char *strarg1 = NULL; + char *strarg2 = NULL; + + // customize by adding function to this list and to if statement + // math functions: add(x,y),sub(x,y),mult(x,y),div(x,y), + // neg(x),pow(x,y),exp(x),ln(x),sqrt(x) + // group functions: mass(group),charge(group),xcm(group,dim), + // vcm(group,dim),bound(group,xmin),gyration(group) + + if (strcmp(func,"add") == 0) { + if (!arg2) error->all("Cannot evaluate variable equal command"); + strarg1 = evaluate(arg1); + strarg2 = evaluate(arg2); + value1 = atof(strarg1); + value2 = atof(strarg2); + answer = value1 + value2; + + } else if (strcmp(func,"sub") == 0) { + if (!arg2) error->all("Cannot evaluate variable equal command"); + strarg1 = evaluate(arg1); + strarg2 = evaluate(arg2); + value1 = atof(strarg1); + value2 = atof(strarg2); + answer = value1 - value2; + + } else if (strcmp(func,"mult") == 0) { + if (!arg2) error->all("Cannot evaluate variable equal command"); + strarg1 = evaluate(arg1); + strarg2 = evaluate(arg2); + value1 = atof(strarg1); + value2 = atof(strarg2); + answer = value1 * value2; + + } else if (strcmp(func,"div") == 0) { + if (!arg2) error->all("Cannot evaluate variable equal command"); + strarg1 = evaluate(arg1); + strarg2 = evaluate(arg2); + value1 = atof(strarg1); + value2 = atof(strarg2); + if (value2 == 0.0) + error->all("Cannot evaluate variable equal command"); + answer = value1 / value2; + + } else if (strcmp(func,"neg") == 0) { + if (arg2) error->all("Cannot evaluate variable equal command"); + strarg1 = evaluate(arg1); + value1 = atof(strarg1); + answer = -value1; + + } else if (strcmp(func,"pow") == 0) { + if (!arg2) error->all("Cannot evaluate variable equal command"); + strarg1 = evaluate(arg1); + strarg2 = evaluate(arg2); + value1 = atof(strarg1); + value2 = atof(strarg2); + if (value2 == 0.0) + error->all("Cannot evaluate variable equal command"); + answer = pow(value1,value2); + + } else if (strcmp(func,"exp") == 0) { + if (arg2) error->all("Cannot evaluate variable equal command"); + strarg1 = evaluate(arg1); + value1 = atof(strarg1); + answer = exp(value1); + + } else if (strcmp(func,"ln") == 0) { + if (arg2) error->all("Cannot evaluate variable equal command"); + strarg1 = evaluate(arg1); + value1 = atof(strarg1); + if (value1 == 0.0) + error->all("Cannot evaluate variable equal command"); + answer = log(value1); + + } else if (strcmp(func,"sqrt") == 0) { + if (arg2) error->all("Cannot evaluate variable equal command"); + strarg1 = evaluate(arg1); + value1 = atof(strarg1); + if (value1 == 0.0) + error->all("Cannot evaluate variable equal command"); + answer = sqrt(value1); + + } else if (strcmp(func,"mass") == 0) { + if (arg2) error->all("Cannot evaluate variable equal command"); + int igroup = group->find(arg1); + if (igroup == -1) error->all("Variable equal group ID does not exist"); + atom->check_mass(); + answer = group->mass(igroup); + + } else if (strcmp(func,"charge") == 0) { + if (arg2) error->all("Cannot evaluate variable equal command"); + int igroup = group->find(arg1); + if (igroup == -1) error->all("Variable equal group ID does not exist"); + answer = group->charge(igroup); + + } else if (strcmp(func,"xcm") == 0) { + if (!arg2) error->all("Cannot evaluate variable equal command"); + int igroup = group->find(arg1); + if (igroup == -1) error->all("Variable equal group ID does not exist"); + atom->check_mass(); + double masstotal = group->mass(igroup); + double xcm[3]; + group->xcm(igroup,masstotal,xcm); + if (strcmp(arg2,"x") == 0) answer = xcm[0]; + else if (strcmp(arg2,"y") == 0) answer = xcm[1]; + else if (strcmp(arg2,"z") == 0) answer = xcm[2]; + else error->all("Cannot evaluate variable equal command"); + + } else if (strcmp(func,"vcm") == 0) { + if (!arg2) error->all("Cannot evaluate variable equal command"); + int igroup = group->find(arg1); + if (igroup == -1) error->all("Variable equal group ID does not exist"); + atom->check_mass(); + double masstotal = group->mass(igroup); + double vcm[3]; + group->vcm(igroup,masstotal,vcm); + if (strcmp(arg2,"x") == 0) answer = vcm[0]; + else if (strcmp(arg2,"y") == 0) answer = vcm[1]; + else if (strcmp(arg2,"z") == 0) answer = vcm[2]; + else error->all("Cannot evaluate variable equal command"); + + } else if (strcmp(func,"bound") == 0) { + if (!arg2) error->all("Cannot evaluate variable equal command"); + int igroup = group->find(arg1); + if (igroup == -1) error->all("Variable equal group ID does not exist"); + double minmax[6]; + group->bounds(igroup,minmax); + if (strcmp(arg2,"xmin") == 0) answer = minmax[0]; + else if (strcmp(arg2,"xmax") == 0) answer = minmax[1]; + if (strcmp(arg2,"ymin") == 0) answer = minmax[2]; + else if (strcmp(arg2,"ymax") == 0) answer = minmax[3]; + if (strcmp(arg2,"zmin") == 0) answer = minmax[4]; + else if (strcmp(arg2,"zmax") == 0) answer = minmax[5]; + else error->all("Cannot evaluate variable equal command"); + + } else if (strcmp(func,"gyration") == 0) { + if (!arg2) error->all("Cannot evaluate variable equal command"); + int igroup = group->find(arg1); + if (igroup == -1) error->all("Variable equal group ID does not exist"); + atom->check_mass(); + double masstotal = group->mass(igroup); + double xcm[3]; + group->xcm(igroup,masstotal,xcm); + answer = group->gyration(igroup,masstotal,xcm); + + } else error->all("Cannot evaluate variable equal command"); + + delete [] func; + delete [] arg1; + delete [] strarg1; + if (arg2) { + delete [] arg2; + delete [] strarg2; + } + sprintf(result,"%g",answer); + + // if string is "vector", find which proc owns atom, grab vector value + + } else if (strchr(str,'[')) { + if (str[strlen(str)-1] != ']') + error->all("Cannot evaluate variable equal command"); + if (atom->map_style == 0) + error->all("Cannot use vectors in variables unless atom map exists"); + + char *ptr = strchr(str,'['); + int n = ptr - str; + char *vector = new char[n+1]; + strncpy(vector,str,n); + vector[n] = '\0'; + + char *arg; + ptr++; + char *ptr2 = &str[strlen(str)-1]; + n = ptr2 - ptr; + arg = new char[n+1]; + strncpy(arg,ptr,n); + arg[n] = '\0'; + int i = atom->map(atoi(arg)); + + double mine; + if (i >= 0 && i < atom->nlocal) { + + // customize by adding vector to this list and to if statement + // x,y,z,vx,vy,vz,fx,fy,fz + + if (strcmp(vector,"x") == 0) mine = atom->x[i][0]; + else if (strcmp(vector,"y") == 0) mine = atom->x[i][1]; + else if (strcmp(vector,"z") == 0) mine = atom->x[i][2]; + else if (strcmp(vector,"vx") == 0) mine = atom->v[i][0]; + else if (strcmp(vector,"vy") == 0) mine = atom->v[i][1]; + else if (strcmp(vector,"vz") == 0) mine = atom->v[i][2]; + else if (strcmp(vector,"fx") == 0) mine = atom->f[i][0]; + else if (strcmp(vector,"fy") == 0) mine = atom->f[i][1]; + else if (strcmp(vector,"fz") == 0) mine = atom->f[i][2]; + + else error->one("Invalid vector in variable equal command"); + + } else mine = 0.0; + MPI_Allreduce(&mine,&answer,1,MPI_DOUBLE,MPI_SUM,world); + + delete [] vector; + delete [] arg; + sprintf(result,"%g",answer); + + // if string is "keyword", compute appropriate value via thermo + + } else if (str[0] - 'a' >= 0 && str[0] - 'a' < 26) { + + // customize by adding keyword to this list and to if statement + // keywords defined by thermo_style custom are passed to compute_value() + + if (domain->box_exist == 0) + error->all("Using variable equal keyword before simulation box is defined"); + + // process these keywords here, not in thermo + // they are allowed anytime after simulation box is defined + + if (strcmp(str,"step") == 0) + answer = update->ntimestep; + else if (strcmp(str,"atoms") == 0) + answer = atom->natoms; + else if (strcmp(str,"lx") == 0) + answer = domain->xprd; + else if (strcmp(str,"ly") == 0) + answer = domain->yprd; + else if (strcmp(str,"lz") == 0) + answer = domain->zprd; + else if (strcmp(str,"vol") == 0) + answer = domain->xprd * domain->yprd * domain->zprd; + + // process other keywords via thermo + // error if 1st run has not yet started + // warning if out-of-sync with thermo, since values can be bogus + + else { + if (update->first_update == 0) + error->all("Using variable equal keyword before initial run"); + if (update->ntimestep != output->next_thermo && me == 0) + error->warning("Using variable equal keyword with non-current thermo"); + int flag = output->thermo->compute_value(str,&answer); + if (flag) error->all("Invalid keyword in variable equal command"); + } + + sprintf(result,"%g",answer); + + // string is a number, just copy to result + + } else strcpy(result,str); + + // return newly allocated string + + return result; +} diff --git a/src/variable.h b/src/variable.h new file mode 100644 index 0000000000000000000000000000000000000000..1e9c3a04270b3fa19174928c8a55f6de152cc5e8 --- /dev/null +++ b/src/variable.h @@ -0,0 +1,44 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef VARIABLE_H +#define VARIABLE_H + +#include "lammps.h" + +class Variable : public LAMMPS { + public: + Variable(); + ~Variable(); + void set(int, char **); + void set(char *, char *); + int next(int, char **); + char *retrieve(char *); + + private: + int me; + int nvar; // # of defined variables + int maxvar; // max # of variables arrays can hold + char **names; // name of each variable + int *style; // style of each variable + int *num; // # of values for each variable + int *index; // next available value for each variable + char ***data; // str value of each variable's values + + int find(char *); + void copy(int, char **, char **); + char *evaluate(char *); + void remove(int); +}; + +#endif diff --git a/src/velocity.cpp b/src/velocity.cpp new file mode 100644 index 0000000000000000000000000000000000000000..16108899ceed80072c133e49f29fa6e99fb33f14 --- /dev/null +++ b/src/velocity.cpp @@ -0,0 +1,709 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "velocity.h" +#include "atom.h" +#include "update.h" +#include "domain.h" +#include "force.h" +#include "temperature.h" +#include "temp_full.h" +#include "random_park.h" +#include "group.h" +#include "comm.h" +#include "memory.h" +#include "error.h" + +#define CREATE 1 +#define SET 2 +#define SCALE 3 +#define RAMP 4 +#define ZERO 5 + +#define ALL 1 +#define LOCAL 2 +#define GEOM 3 + +#define WARMUP 100 +#define SMALL 0.001 + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* ---------------------------------------------------------------------- */ + +void Velocity::command(int narg, char **arg) +{ + // require atom masses to all be set + + if (domain->box_exist == 0) + error->all("Velocity command before simulation box is defined"); + if (atom->natoms == 0) + error->all("Velocity command with no atoms existing"); + atom->check_mass(); + + if (narg < 2) error->all("Illegal velocity command"); + + // identify group + + igroup = group->find(arg[0]); + if (igroup == -1) error->all("Could not find velocity group ID"); + groupbit = group->bitmask[igroup]; + + // identify style + + if (strcmp(arg[1],"create") == 0) style = CREATE; + else if (strcmp(arg[1],"set") == 0) style = SET; + else if (strcmp(arg[1],"scale") == 0) style = SCALE; + else if (strcmp(arg[1],"ramp") == 0) style = RAMP; + else if (strcmp(arg[1],"zero") == 0) style = ZERO; + else error->all("Illegal velocity command"); + + // set defaults + + tempwhich = -1; + dist_flag = 0; + sum_flag = 0; + momentum_flag = 1; + rotation_flag = 0; + loop_flag = ALL; + scale_flag = 1; + + // read options from end of input line + // change defaults as options specify + + if (style == CREATE) options(narg-4,&arg[4]); + else if (style == SET) options(narg-5,&arg[5]); + else if (style == SCALE) options(narg-3,&arg[3]); + else if (style == RAMP) options(narg-8,&arg[8]); + else if (style == ZERO) options(narg-3,&arg[3]); + + // set scaling for SET and RAMP styles + + if (style == SET || style == RAMP) { + if (scale_flag && strcmp(domain->lattice_style,"none") == 0) + error->all("Use of velocity with undefined lattice"); + + if (scale_flag) { + xscale = domain->xlattice; + yscale = domain->ylattice; + zscale = domain->zlattice; + } + else xscale = yscale = zscale = 1.0; + } + + // initialize velocities based on style + + if (style == CREATE) create(narg-2,&arg[2]); + else if (style == SET) set(narg-2,&arg[2]); + else if (style == SCALE) scale(narg-2,&arg[2]); + else if (style == RAMP) ramp(narg-2,&arg[2]); + else if (style == ZERO) zero(narg-2,&arg[2]); +} + +/* ---------------------------------------------------------------------- */ + +void Velocity::create(int narg, char **arg) +{ + int i; + + double t_desired = atof(arg[0]); + int seed = atoi(arg[1]); + + // if tempwhich = -1, create a new temperature full style with the vel group + // else use pre-defined temperature + + Temperature *temperature; + if (tempwhich == -1) { + char **arg = new char*[3]; + arg[0] = "temp"; + arg[1] = group->names[igroup]; + arg[2] = "full"; + temperature = new TempFull(3,arg); + delete [] arg; + } else temperature = force->templist[tempwhich]; + + // initialize temperature computation + // warn if groups don't match + + if (igroup != temperature->igroup && comm->me == 0) + error->warning("Mismatch between velocity and temperature groups"); + temperature->init(); + + // store a copy of current velocities + + double **v = atom->v; + int nlocal = atom->nlocal; + double **vhold = memory->create_2d_double_array(nlocal,3,"velocity:vnew"); + + for (i = 0; i < nlocal; i++) { + vhold[i][0] = v[i][0]; + vhold[i][1] = v[i][1]; + vhold[i][2] = v[i][2]; + } + + // create new velocities, in uniform or gaussian distribution + // loop option determines looping style, ALL is default + // ALL = loop over all natoms, only set those I own via atom->map + // cannot do this if atom IDs do not span 1-Natoms (some were deleted) + // will produce same V, independent of P, if atoms were read-in + // will NOT produce same V, independent of P, if used create_atoms + // LOCAL = only loop over my atoms, adjust RNG to be proc-specific + // will never produce same V, independent of P + // GEOM = only loop over my atoms + // choose RNG for each atom based on its xyz coord (geometry) + // will always produce same V, independent of P + // adjust by factor for atom mass + // for 2d, set Vz to 0.0 + + int *type = atom->type; + int *mask = atom->mask; + double *mass = atom->mass; + double *rmass = atom->rmass; + int dimension = force->dimension; + int mass_require = atom->mass_require; + + int m; + double vx,vy,vz,factor; + RanPark *random; + + if (loop_flag == ALL) { + + // create an atom map if one doesn't exist already + + int mapflag = 0; + if (atom->map_style == 0) { + mapflag = 1; + atom->map_style = 1; + atom->map_init(); + atom->map_set(); + } + + random = new RanPark(seed); + + if (atom->tag_enable == 0) + error->all("Cannot use velocity create loop all unless atoms have IDs"); + int natoms = static_cast<int> (atom->natoms); + + // check that atom IDs span range from 1 to natoms + + int *tag = atom->tag; + int idmin = natoms; + int idmax = 0; + + for (i = 0; i < nlocal; i++) { + idmin = MIN(idmin,tag[i]); + idmax = MAX(idmax,tag[i]); + } + int idminall,idmaxall; + MPI_Allreduce(&idmin,&idminall,1,MPI_INT,MPI_MIN,world); + MPI_Allreduce(&idmax,&idmaxall,1,MPI_INT,MPI_MAX,world); + + if (idminall != 1 || idmaxall != natoms) { + char *str = "Cannot use velocity create loop all with non-contiguous atom IDs"; + error->all(str); + } + + // loop over all atoms in system + // generate RNGs for all atoms, only assign to ones I own + // use either per-type mass or per-atom rmass + + for (i = 1; i <= natoms; i++) { + if (dist_flag == 0) { + vx = random->uniform(); + vy = random->uniform(); + vz = random->uniform(); + } else { + vx = random->gaussian(); + vy = random->gaussian(); + vz = random->gaussian(); + } + m = atom->map(i); + if (m >= 0 && m < nlocal) { + if (mask[m] & groupbit) { + if (mass_require) factor = 1.0/sqrt(mass[type[m]]); + else factor = 1.0/sqrt(rmass[m]); + v[m][0] = vx * factor; + v[m][1] = vy * factor; + if (dimension == 3) v[m][2] = vz * factor; + else v[m][2] = 0.0; + } + } + } + + // delete temporary atom map + + if (mapflag) { + atom->map_delete(); + atom->map_style = 0; + } + + } else if (loop_flag == LOCAL) { + random = new RanPark(seed + comm->me); + for (i = 0; i < WARMUP; i++) random->uniform(); + + for (i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + if (dist_flag == 0) { + vx = random->uniform(); + vy = random->uniform(); + vz = random->uniform(); + } else { + vx = random->gaussian(); + vy = random->gaussian(); + vz = random->gaussian(); + } + if (mass_require) factor = 1.0/sqrt(mass[type[i]]); + else factor = 1.0/sqrt(rmass[i]); + v[i][0] = vx * factor; + v[i][1] = vy * factor; + if (dimension == 3) v[i][2] = vz * factor; + else v[i][2] = 0.0; + } + } + + } else if (loop_flag == GEOM) { + random = new RanPark(seed); + double **x = atom->x; + + for (i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + triple(x[i][0],x[i][1],x[i][2],&vx,&vy,&vz,seed,random); + if (mass_require) factor = 1.0/sqrt(mass[type[i]]); + else factor = 1.0/sqrt(rmass[i]); + v[i][0] = vx * factor; + v[i][1] = vy * factor; + if (dimension == 3) v[i][2] = vz * factor; + else v[i][2] = 0.0; + } + } + } + + // apply momentum and rotation zeroing + + if (momentum_flag) zero_momentum(); + if (rotation_flag) zero_rotation(); + + // scale temp to desired value + + double t = temperature->compute(); + rescale(t,t_desired); + + // if sum_flag set, add back in previous velocities + + if (sum_flag) { + for (i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + v[i][0] += vhold[i][0]; + v[i][1] += vhold[i][1]; + v[i][2] += vhold[i][2]; + } + } + } + + // free local memory + // if temperature was created, delete it + + memory->destroy_2d_double_array(vhold); + delete random; + if (tempwhich == -1) delete temperature; +} + +/* ---------------------------------------------------------------------- */ + +void Velocity::set(int narg, char **arg) +{ + int xflag,yflag,zflag; + double vx,vy,vz; + + if (strcmp(arg[0],"NULL") == 0) xflag = 0; + else { + xflag = 1; + vx = xscale * atof(arg[0]); + } + if (strcmp(arg[1],"NULL") == 0) yflag = 0; + else { + yflag = 1; + vy = yscale * atof(arg[1]); + } + if (strcmp(arg[2],"NULL") == 0) zflag = 0; + else { + zflag = 1; + vz = zscale * atof(arg[2]); + } + + double **v = atom->v; + int *mask = atom->mask; + int nlocal = atom->nlocal; + int dimension = force->dimension; + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + if (sum_flag == 0) { + if (xflag) v[i][0] = vx; + if (yflag) v[i][1] = vy; + if (zflag && dimension == 3) v[i][2] = vz; + } else { + if (xflag) v[i][0] += vx; + if (yflag) v[i][1] += vy; + if (zflag && dimension == 3) v[i][2] += vz; + } + } + } +} + +/* ---------------------------------------------------------------------- + rescale velocities of a group after computing its temperature +------------------------------------------------------------------------- */ + +void Velocity::scale(int narg, char **arg) +{ + double t_desired = atof(arg[0]); + + // if tempwhich = -1, create a new temperature full style with the vel group + // else use pre-defined temperature + + Temperature *temperature; + if (tempwhich == -1) { + char **arg = new char*[3]; + arg[0] = "temp"; + arg[1] = group->names[igroup]; + arg[2] = "full"; + temperature = new TempFull(3,arg); + delete [] arg; + } else temperature = force->templist[tempwhich]; + + // initialize temperature computation + // warn if groups don't match + + if (igroup != temperature->igroup && comm->me == 0) + error->warning("Mismatch between velocity and temperature groups"); + temperature->init(); + + // scale temp to desired value + + double t = temperature->compute(); + rescale(t,t_desired); + + // if temperature was created, delete it + + if (tempwhich == -1) delete temperature; +} + +/* ---------------------------------------------------------------------- + apply a ramped set of velocities +------------------------------------------------------------------------- */ + +void Velocity::ramp(int narg, char **arg) +{ + int v_dim; + if (strcmp(arg[0],"vx") == 0) v_dim = 0; + else if (strcmp(arg[0],"vy") == 0) v_dim = 1; + else if (strcmp(arg[0],"vz") == 0) v_dim = 2; + else error->all("Illegal velocity command"); + + if (v_dim == 2 && force->dimension == 2) + error->all("Velocity ramp in z for a 2d problem"); + + double v_lo,v_hi; + if (v_dim == 0) { + v_lo = xscale*atof(arg[1]); + v_hi = xscale*atof(arg[2]); + } else if (v_dim == 1) { + v_lo = yscale*atof(arg[1]); + v_hi = yscale*atof(arg[2]); + } else if (v_dim == 0) { + v_lo = zscale*atof(arg[1]); + v_hi = zscale*atof(arg[2]); + } + + int coord_dim; + if (strcmp(arg[3],"x") == 0) coord_dim = 0; + else if (strcmp(arg[3],"y") == 0) coord_dim = 1; + else if (strcmp(arg[3],"z") == 0) coord_dim = 2; + else error->all("Illegal velocity command"); + + double coord_lo,coord_hi; + if (coord_dim == 0) { + coord_lo = xscale*atof(arg[4]); + coord_hi = xscale*atof(arg[5]); + } else if (coord_dim == 1) { + coord_lo = yscale*atof(arg[4]); + coord_hi = yscale*atof(arg[5]); + } else if (coord_dim == 2) { + coord_lo = zscale*atof(arg[4]); + coord_hi = zscale*atof(arg[5]); + } + + // vramp = ramped velocity component for v_dim + // add or set based on sum_flag + + double **x = atom->x; + double **v = atom->v; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double fraction,vramp; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + fraction = (x[i][coord_dim] - coord_lo) / (coord_hi - coord_lo); + fraction = MAX(fraction,0.0); + fraction = MIN(fraction,1.0); + vramp = v_lo + fraction*(v_hi - v_lo); + if (sum_flag) v[i][v_dim] += vramp; + else v[i][v_dim] = vramp; + } +} + +/* ---------------------------------------------------------------------- + zero linear or angular momentum of a group +------------------------------------------------------------------------- */ + +void Velocity::zero(int narg, char **arg) +{ + if (strcmp(arg[0],"linear") == 0) zero_momentum(); + else if (strcmp(arg[0],"angular") == 0) zero_rotation(); + else error->all("Illegal velocity command"); +} + +/* ---------------------------------------------------------------------- + rescale velocities of group atoms to t_new from t_old +------------------------------------------------------------------------- */ + +void Velocity::rescale(double t_old, double t_new) +{ + if (t_old == 0.0) error->all("Attempting to rescale a 0.0 temperature"); + + double factor = sqrt(t_new/t_old); + + double **v = atom->v; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + v[i][0] *= factor; + v[i][1] *= factor; + v[i][2] *= factor; + } +} + +/* ---------------------------------------------------------------------- + zero the linear momentum of a group of atoms by adjusting v by -Vcm +------------------------------------------------------------------------- */ + +void Velocity::zero_momentum() +{ + // cannot have 0 atoms in group + + if (group->count(igroup) == 0.0) + error->all("Cannot zero momentum of 0 atoms"); + + // compute velocity of center-of-mass of group + + double masstotal = group->mass(igroup); + double vcm[3]; + group->vcm(igroup,masstotal,vcm); + + // adjust velocities by vcm to zero linear momentum + + double **v = atom->v; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + v[i][0] -= vcm[0]; + v[i][1] -= vcm[1]; + v[i][2] -= vcm[2]; + } +} + +/* ---------------------------------------------------------------------- + zero the angular momentum of a group of atoms by adjusting v by -(w x r) +------------------------------------------------------------------------- */ + +void Velocity::zero_rotation() +{ + int i,j; + + // cannot have 0 atoms in group + + if (group->count(igroup) == 0.0) + error->all("Cannot zero momentum of 0 atoms"); + + // compute omega (angular velocity) of group around center-of-mass + + double xcm[3],angmom[3],inertia[3][3],omega[3]; + double masstotal = group->mass(igroup); + group->xcm(igroup,masstotal,xcm); + group->angmom(igroup,xcm,angmom); + group->inertia(igroup,xcm,inertia); + group->omega(igroup,angmom,inertia,omega); + + // adjust velocities to zero omega + // vnew_i = v_i - w x r_i + // must use unwrapped coords to compute r_i correctly + + double **x = atom->x; + double **v = atom->v; + int *mask = atom->mask; + int *image = atom->image; + int nlocal = atom->nlocal; + + int xbox,ybox,zbox; + double dx,dy,dz; + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + + for (i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + xbox = (image[i] & 1023) - 512; + ybox = (image[i] >> 10 & 1023) - 512; + zbox = (image[i] >> 20) - 512; + dx = (x[i][0] + xbox*xprd) - xcm[0]; + dy = (x[i][1] + ybox*yprd) - xcm[1]; + dz = (x[i][2] + zbox*zprd) - xcm[2]; + v[i][0] -= omega[1]*dz - omega[2]*dy; + v[i][1] -= omega[2]*dx - omega[0]*dy; + v[i][2] -= omega[0]*dy - omega[1]*dx; + } +} + +/* ---------------------------------------------------------------------- + parse optional parameters at end of velocity input line +------------------------------------------------------------------------- */ + +void Velocity::options(int narg, char **arg) +{ + if (narg < 0) error->all("Illegal velocity command"); + + int iarg = 0; + while (iarg < narg) { + if (strcmp(arg[iarg],"dist") == 0) { + if (iarg+2 > narg) error->all("Illegal velocity command"); + if (strcmp(arg[iarg+1],"uniform") == 0) dist_flag = 0; + else if (strcmp(arg[iarg+1],"gaussian") == 0) dist_flag = 1; + else error->all("Illegal velocity command"); + iarg += 2; + } else if (strcmp(arg[iarg],"sum") == 0) { + if (iarg+2 > narg) error->all("Illegal velocity command"); + if (strcmp(arg[iarg+1],"no") == 0) sum_flag = 0; + else if (strcmp(arg[iarg+1],"yes") == 0) sum_flag = 1; + else error->all("Illegal velocity command"); + iarg += 2; + } else if (strcmp(arg[iarg],"mom") == 0) { + if (iarg+2 > narg) error->all("Illegal velocity command"); + if (strcmp(arg[iarg+1],"no") == 0) momentum_flag = 0; + else if (strcmp(arg[iarg+1],"yes") == 0) momentum_flag = 1; + else error->all("Illegal velocity command"); + iarg += 2; + } else if (strcmp(arg[iarg],"rot") == 0) { + if (iarg+2 > narg) error->all("Illegal velocity command"); + if (strcmp(arg[iarg+1],"no") == 0) rotation_flag = 0; + else if (strcmp(arg[iarg+1],"yes") == 0) rotation_flag = 1; + else error->all("Illegal velocity command"); + iarg += 2; + } else if (strcmp(arg[iarg],"temp") == 0) { + if (iarg+2 > narg) error->all("Illegal velocity command"); + for (tempwhich = 0; tempwhich < force->ntemp; tempwhich++) + if (strcmp(arg[iarg+1],force->templist[tempwhich]->id) == 0) break; + if (tempwhich == force->ntemp) + error->all("Could not find velocity temperature ID"); + iarg += 2; + } else if (strcmp(arg[iarg],"loop") == 0) { + if (iarg+2 > narg) error->all("Illegal velocity command"); + if (strcmp(arg[iarg+1],"all") == 0) loop_flag = ALL; + else if (strcmp(arg[iarg+1],"local") == 0) loop_flag = LOCAL; + else if (strcmp(arg[iarg+1],"geom") == 0) loop_flag = GEOM; + else error->all("Illegal velocity command"); + iarg += 2; + } else if (strcmp(arg[iarg],"units") == 0) { + if (iarg+2 > narg) error->all("Illegal velocity command"); + if (strcmp(arg[iarg+1],"box") == 0) scale_flag = 0; + else if (strcmp(arg[iarg+1],"lattice") == 0) scale_flag = 1; + else error->all("Illegal velocity command"); + iarg += 2; + } else error->all("Illegal velocity command"); + } +} + +/* ---------------------------------------------------------------------- */ + +#define IA1 1366 +#define IC1 150889 +#define IM1 714025 +#define IA2 8121 +#define IC2 28411 +#define IM2 134456 +#define IA3 7141 +#define IC3 54773 +#define IM3 259200 + +void Velocity::triple(double x, double y, double z, + double *vx, double *vy, double *vz, + int seed, RanPark *random) +{ + // seed 1,2,3 = combination of atom coord in each dim and user-input seed + // map geometric extent into range of each of 3 RNGs + // warm-up each RNG by calling it twice + + double fraction; + int seed1,seed2,seed3; + + fraction = (x - domain->boxxlo) / domain->xprd; + seed1 = static_cast<int> (fraction * IM1); + seed1 = (seed1+seed) % IM1; + seed1 = (seed1*IA1+IC1) % IM1; + seed1 = (seed1*IA1+IC1) % IM1; + + fraction = (y - domain->boxylo) / domain->yprd; + seed2 = static_cast<int> (fraction * IM2); + seed2 = (seed2+seed) % IM2; + seed2 = (seed2*IA2+IC2) % IM2; + seed2 = (seed2*IA2+IC2) % IM2; + + fraction = (z - domain->boxzlo) / domain->zprd; + seed3 = static_cast<int> (fraction * IM3); + seed3 = (seed3+seed) % IM3; + seed3 = (seed3*IA3+IC3) % IM3; + seed3 = (seed3*IA3+IC3) % IM3; + + // fraction = 0-1 with giving each dim an equal weighting + // use fraction to reset Park/Miller RNG seed + + fraction = 1.0*seed1/(3*IM1) + 1.0*seed2/(3*IM2) + 1.0*seed3/(3*IM3); + random->reset(fraction); + + // use RNG to set velocities after warming up twice + + random->uniform(); + random->uniform(); + + if (dist_flag == 0) { + *vx = random->uniform(); + *vy = random->uniform(); + *vz = random->uniform(); + } else { + *vx = random->gaussian(); + *vy = random->gaussian(); + *vz = random->gaussian(); + } +} diff --git a/src/velocity.h b/src/velocity.h new file mode 100644 index 0000000000000000000000000000000000000000..facedc5a84ff4aca596e2099de66b4589a2a6bd4 --- /dev/null +++ b/src/velocity.h @@ -0,0 +1,50 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef VELOCITY_H +#define VELOCITY_H + +#include "lammps.h" + +class Temperature; +class RanPark; + +class Velocity : public LAMMPS { + public: + Velocity() {} + ~Velocity() {} + void command(int, char **); + + private: + int igroup,groupbit; + int style,tempwhich; + int dist_flag,sum_flag,momentum_flag,rotation_flag,loop_flag,scale_flag; + double xscale,yscale,zscale; + Temperature *temperature; + + void create(int, char **); + void set(int, char **); + void scale(int, char **); + void ramp(int, char **); + void zero(int, char **); + + void rescale(double, double); + void zero_momentum(); + void zero_rotation(); + + void options(int, char **); + void triple(double, double, double, double *, double *, double *, + int, RanPark *); +}; + +#endif diff --git a/src/verlet.cpp b/src/verlet.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b97383ac2ecdf1290c3b890ffe5822a73b5fc57b --- /dev/null +++ b/src/verlet.cpp @@ -0,0 +1,274 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + 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 "verlet.h" +#include "neighbor.h" +#include "domain.h" +#include "comm.h" +#include "atom.h" +#include "force.h" +#include "pair.h" +#include "bond.h" +#include "angle.h" +#include "dihedral.h" +#include "improper.h" +#include "kspace.h" +#include "output.h" +#include "update.h" +#include "modify.h" +#include "fix.h" +#include "timer.h" +#include "memory.h" +#include "error.h" + +/* ---------------------------------------------------------------------- */ + +Verlet::Verlet(int narg, char **arg) : Integrate(narg, arg) {} + +/* ---------------------------------------------------------------------- + initialization before run +------------------------------------------------------------------------- */ + +void Verlet::init() +{ + // warn if no fixes + + if (modify->nfix == 0) + error->warning("No fixes defined, atoms won't move"); + + // set flags for how virial should be computed when needed + // pressure_flag is 1 if NPT,NPH + // virial_every is how virial should be computed every timestep + // 0 = not computed, 1 = computed explicity by pair, + // 2 = computed implicitly by pair (via summation over ghost atoms) + // virial_thermo is how virial should be computed on thermo timesteps + // 1 = computed explicity by pair, 2 = computed implicitly by pair + + int pressure_flag = 0; + for (int i = 0; i < modify->nfix; i++) { + if (strcmp(modify->fix[i]->style,"npt") == 0) pressure_flag = 1; + if (strcmp(modify->fix[i]->style,"nph") == 0) pressure_flag = 1; + } + + if (pressure_flag && force->newton_pair) virial_every = 2; + else if (pressure_flag) virial_every = 1; + else virial_every = 0; + + if (force->newton_pair) virial_thermo = 2; + else virial_thermo = 1; + + // set flags for what arrays to clear in force_clear() + // need to clear torques if atom_style is dipole + // need to clear phia if atom_style is granular + // don't need to clear f_pair if atom_style is only granular (no virial) + + torqueflag = 0; + if (atom->check_style("dipole")) torqueflag = 1; + granflag = 0; + if (atom->check_style("granular")) granflag = 1; + pairflag = 1; + if (strcmp(atom->style,"granular") == 0) pairflag = 0; + + // local versions of Update quantities + + maxpair = update->maxpair; + f_pair = update->f_pair; +} + +/* ---------------------------------------------------------------------- + setup before run +------------------------------------------------------------------------- */ + +void Verlet::setup() +{ + if (comm->me == 0 && screen) fprintf(screen,"Setting up run ...\n"); + + // setup domain, communication and neighboring + // acquire ghosts + // build neighbor lists + + domain->pbc(); + domain->reset_box(); + comm->setup(); + if (neighbor->style) neighbor->setup_bins(); + comm->exchange(); + comm->borders(); + neighbor->build(); + neighbor->ncalls = 0; + + // compute all forces + + int eflag = 1; + int vflag = virial_thermo; + force_clear(vflag); + + if (atom->molecular) { + if (force->bond) force->bond->compute(eflag,vflag); + if (force->angle) force->angle->compute(eflag,vflag); + if (force->dihedral) force->dihedral->compute(eflag,vflag); + if (force->improper) force->improper->compute(eflag,vflag); + } + + if (force->pair) force->pair->compute(eflag,vflag); + + if (force->kspace) { + force->kspace->setup(); + force->kspace->compute(eflag,vflag); + } + + if (force->newton) comm->reverse_communicate(); + + modify->setup(); + output->setup(1); +} + +/* ---------------------------------------------------------------------- + iterate for n steps +------------------------------------------------------------------------- */ + +void Verlet::iterate(int n) +{ + int eflag,vflag,nflag; + + for (int i = 0; i < n; i++) { + + update->ntimestep++; + + modify->initial_integrate(); + + nflag = neighbor->decide(); + + if (nflag == 0) { + timer->stamp(); + comm->communicate(); + timer->stamp(TIME_COMM); + } else { + if (modify->n_pre_exchange) modify->pre_exchange(); + domain->pbc(); + if (domain->box_change) { + domain->reset_box(); + comm->setup(); + if (neighbor->style) neighbor->setup_bins(); + } + timer->stamp(); + comm->exchange(); + comm->borders(); + timer->stamp(TIME_COMM); + if (modify->n_pre_neighbor) modify->pre_neighbor(); + neighbor->build(); + timer->stamp(TIME_NEIGHBOR); + } + + eflag = 0; + vflag = virial_every; + if (output->next_thermo == update->ntimestep) { + eflag = 1; + vflag = virial_thermo; + } + force_clear(vflag); + + timer->stamp(); + if (atom->molecular) { + if (force->bond) force->bond->compute(eflag,vflag); + if (force->angle) force->angle->compute(eflag,vflag); + if (force->dihedral) force->dihedral->compute(eflag,vflag); + if (force->improper) force->improper->compute(eflag,vflag); + timer->stamp(TIME_BOND); + } + + if (force->pair) { + force->pair->compute(eflag,vflag); + timer->stamp(TIME_PAIR); + } + + if (force->kspace) { + force->kspace->compute(eflag,vflag); + timer->stamp(TIME_KSPACE); + } + + if (force->newton) { + comm->reverse_communicate(); + timer->stamp(TIME_COMM); + } + + if (modify->n_post_force) modify->post_force(vflag); + modify->final_integrate(); + if (modify->n_end_of_step) modify->end_of_step(); + + if (output->next == update->ntimestep) { + timer->stamp(); + output->write(update->ntimestep); + timer->stamp(TIME_OUTPUT); + } + } +} + +/* ---------------------------------------------------------------------- + clear force on own & ghost atoms + setup and clear other arrays as needed +------------------------------------------------------------------------- */ + +void Verlet::force_clear(int vflag) +{ + int i; + + // clear global force array + // nall includes ghosts only if either newton flag is set + + int nall; + if (force->newton) nall = atom->nlocal + atom->nghost; + else nall = atom->nlocal; + + double **f = atom->f; + for (i = 0; i < nall; i++) { + f[i][0] = 0.0; + f[i][1] = 0.0; + f[i][2] = 0.0; + } + + if (torqueflag) { + double **torque = atom->torque; + for (i = 0; i < nall; i++) { + torque[i][0] = 0.0; + torque[i][1] = 0.0; + torque[i][2] = 0.0; + } + } + + if (granflag) { + double **phia = atom->phia; + for (i = 0; i < nall; i++) { + phia[i][0] = 0.0; + phia[i][1] = 0.0; + phia[i][2] = 0.0; + } + } + + // clear f_pair array if using it this timestep to compute virial + + if (vflag == 2 && pairflag) { + if (atom->nmax > maxpair) { + maxpair = atom->nmax; + memory->destroy_2d_double_array(f_pair); + f_pair = memory->create_2d_double_array(maxpair,3,"verlet:f_pair"); + update->maxpair = maxpair; + update->f_pair = f_pair; + } + for (i = 0; i < nall; i++) { + f_pair[i][0] = 0.0; + f_pair[i][1] = 0.0; + f_pair[i][2] = 0.0; + } + } +} diff --git a/src/verlet.h b/src/verlet.h new file mode 100644 index 0000000000000000000000000000000000000000..d6a4ebb08cc280da40ed48a813b843f9f1293ec1 --- /dev/null +++ b/src/verlet.h @@ -0,0 +1,38 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef VERLET_H +#define VERLET_H + +#include "integrate.h" + +class Verlet : public Integrate { + public: + Verlet(int, char **); + ~Verlet() {} + void init(); + void setup(); + void iterate(int); + + private: + int virial_every; // what vflag should be on every timestep (0,1,2) + int virial_thermo; // what vflag should be on thermo steps (1,2) + int pairflag,torqueflag,granflag; + + int maxpair; // copies of Update quantities + double **f_pair; + + void force_clear(int); +}; + +#endif diff --git a/src/write_restart.cpp b/src/write_restart.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f0fe38f7d0dc844adcfc8ccd8cce0e14d95478f2 --- /dev/null +++ b/src/write_restart.cpp @@ -0,0 +1,396 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "mpi.h" +#include "string.h" +#include "write_restart.h" +#include "system.h" +#include "atom.h" +#include "group.h" +#include "force.h" +#include "pair.h" +#include "bond.h" +#include "angle.h" +#include "dihedral.h" +#include "improper.h" +#include "update.h" +#include "domain.h" +#include "modify.h" +#include "universe.h" +#include "comm.h" +#include "output.h" +#include "thermo.h" +#include "memory.h" +#include "error.h" + +enum {IGNORE,WARN,ERROR}; // same as thermo.cpp + +/* ---------------------------------------------------------------------- */ + +WriteRestart::WriteRestart() +{ + MPI_Comm_rank(world,&me); + MPI_Comm_size(world,&nprocs); +} + +/* ---------------------------------------------------------------------- + called as write_restart command in input script +------------------------------------------------------------------------- */ + +void WriteRestart::command(int narg, char **arg) +{ + if (domain->box_exist == 0) + error->all("Write_restart command before simulation box is defined"); + if (narg != 1) error->all("Illegal write_restart command"); + + // if filename contains a "*", replace with current timestep + + char *ptr; + int n = strlen(arg[0]) + 16; + char *file = new char[n]; + + if (ptr = strchr(arg[0],'*')) { + *ptr = '\0'; + sprintf(file,"%s%d%s",arg[0],update->ntimestep,ptr+1); + } else strcpy(file,arg[0]); + + // init entire system since comm->exchange is done + // comm::init needs neighbor::init needs pair::init needs kspace::init, etc + + if (comm->me == 0 && screen) + fprintf(screen,"System init for write_restart ...\n"); + sys->init(); + + // move atoms to new processors before writing file + // enforce PBC before in case atoms are outside box + + domain->pbc(); + domain->reset_box(); + comm->setup(); + comm->exchange(); + + write(file); + delete [] file; +} + +/* ---------------------------------------------------------------------- + called from output within run/minimize loop + file = final file name to write, except may contain a "%" +------------------------------------------------------------------------- */ + +void WriteRestart::write(char *file) +{ + // natoms = sum of nlocal = value to write into restart file + // if unequal and thermo lostflag is "error", don't write restart file + + double rlocal = atom->nlocal; + MPI_Allreduce(&rlocal,&natoms,1,MPI_DOUBLE,MPI_SUM,world); + if (natoms != atom->natoms && output->thermo->lostflag == ERROR) + error->all("Atom count is inconsistent, cannot write restart file"); + + // check if filename contains "%" + + int multiproc; + if (strchr(file,'%')) multiproc = 1; + else multiproc = 0; + + // open single restart file or base file for multiproc case + + if (me == 0) { + char *hfile; + if (multiproc) { + char *hfile = new char[strlen(file) + 16]; + char *ptr = strchr(file,'%'); + *ptr = '\0'; + sprintf(hfile,"%s%s%s",file,"base",ptr+1); + *ptr = '%'; + } else hfile = file; + fp = fopen(hfile,"wb"); + if (fp == NULL) { + char str[128]; + sprintf(str,"Cannot open restart file %s",hfile); + error->one(str); + } + if (multiproc) delete [] hfile; + } + + // write header, groups, ntype-length arrays, force field, fix info + + if (me == 0) { + header(); + group->write_restart(fp); + if (atom->mass_require) mass(); + if (atom->dipole_require) dipole(); + force_fields(); + } + + modify->write_restart(fp); + + // communication buffer for all my atom's info + // max_size = largest buffer needed by any proc + + int max_size; + int send_size = atom->size_restart(); + MPI_Allreduce(&send_size,&max_size,1,MPI_INT,MPI_MAX,world); + + double *buf; + if (me == 0) + buf = (double *) + memory->smalloc(max_size*sizeof(double),"write_restart:buf"); + else + buf = (double *) + memory->smalloc(send_size*sizeof(double),"write_restart:buf"); + + // pack my atom data into buf + + int n = 0; + for (int i = 0; i < atom->nlocal; i++) n += atom->pack_restart(i,&buf[n]); + + // if single file: + // write one chunk of atoms per proc to file + // proc 0 pings each proc, receives its chunk, writes to file + // all other procs wait for ping, send their chunk to proc 0 + // else if one file per proc: + // each proc opens its own file and writes its chunk directly + + if (multiproc == 0) { + int tmp,recv_size; + MPI_Status status; + MPI_Request request; + + if (me == 0) { + for (int iproc = 0; iproc < nprocs; iproc++) { + if (iproc) { + MPI_Irecv(buf,max_size,MPI_DOUBLE,iproc,0,world,&request); + MPI_Send(&tmp,0,MPI_INT,iproc,0,world); + MPI_Wait(&request,&status); + MPI_Get_count(&status,MPI_DOUBLE,&recv_size); + } else recv_size = send_size; + + fwrite(&recv_size,sizeof(int),1,fp); + fwrite(buf,sizeof(double),recv_size,fp); + } + fclose(fp); + + } else { + MPI_Recv(&tmp,0,MPI_INT,0,0,world,&status); + MPI_Rsend(buf,send_size,MPI_DOUBLE,0,0,world); + } + + } else { + if (me == 0) fclose(fp); + + char *perproc = new char[strlen(file) + 16]; + char *ptr = strchr(file,'%'); + *ptr = '\0'; + sprintf(perproc,"%s%d%s",file,me,ptr+1); + *ptr = '%'; + fp = fopen(perproc,"wb"); + delete [] perproc; + if (fp == NULL) error->one("Cannot open restart file"); + + fwrite(&send_size,sizeof(int),1,fp); + fwrite(buf,sizeof(double),send_size,fp); + fclose(fp); + } + + memory->sfree(buf); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out problem description +------------------------------------------------------------------------- */ + +void WriteRestart::header() +{ + write_char(0,universe->version); + write_char(1,update->unit_style); + write_int(2,update->ntimestep); + write_int(3,force->dimension); + write_int(4,nprocs); + write_int(5,comm->procgrid[0]); + write_int(6,comm->procgrid[1]); + write_int(7,comm->procgrid[2]); + write_int(8,force->newton_pair); + write_int(9,force->newton_bond); + write_int(10,domain->xperiodic); + write_int(11,domain->yperiodic); + write_int(12,domain->zperiodic); + write_int(13,domain->boundary[0][0]); + write_int(14,domain->boundary[0][1]); + write_int(15,domain->boundary[1][0]); + write_int(16,domain->boundary[1][1]); + write_int(17,domain->boundary[2][0]); + write_int(18,domain->boundary[2][1]); + + // atom_style must be written before atom class values + // so read_restart can create class before reading class values + // if style = hybrid, also write sub-class names (1 to nwords) + + write_char(19,atom->style); + + if (strcmp(atom->style,"hybrid") == 0) { + char **words; + int nwords = atom->style2arg(words); + int nm1 = nwords - 1; + fwrite(&nm1,sizeof(int),1,fp); + for (int i = 1; i < nwords; i++) { + int n = strlen(words[i]) + 1; + fwrite(&n,sizeof(int),1,fp); + fwrite(words[i],sizeof(char),n,fp); + } + for (int i = 0; i < nwords; i++) delete [] words[i]; + delete [] words; + } + + write_double(20,natoms); + write_int(21,atom->ntypes); + write_int(22,atom->nbonds); + write_int(23,atom->nbondtypes); + write_int(24,atom->bond_per_atom); + write_int(25,atom->nangles); + write_int(26,atom->nangletypes); + write_int(27,atom->angle_per_atom); + write_int(28,atom->ndihedrals); + write_int(29,atom->ndihedraltypes); + write_int(30,atom->dihedral_per_atom); + write_int(31,atom->nimpropers); + write_int(32,atom->nimpropertypes); + write_int(33,atom->improper_per_atom); + + write_double(34,domain->boxxlo); + write_double(35,domain->boxxhi); + write_double(36,domain->boxylo); + write_double(37,domain->boxyhi); + write_double(38,domain->boxzlo); + write_double(39,domain->boxzhi); + + write_double(40,force->special_lj[1]); + write_double(41,force->special_lj[2]); + write_double(42,force->special_lj[3]); + write_double(43,force->special_coul[1]); + write_double(44,force->special_coul[2]); + write_double(45,force->special_coul[3]); + + // -1 flag signals end of header + + int flag = -1; + fwrite(&flag,sizeof(int),1,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out atom masses +------------------------------------------------------------------------- */ + +void WriteRestart::mass() +{ + fwrite(&atom->mass[1],sizeof(double),atom->ntypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out dipole moments +------------------------------------------------------------------------- */ + +void WriteRestart::dipole() +{ + fwrite(&atom->dipole[1],sizeof(double),atom->ntypes,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 writes out force field styles and data +------------------------------------------------------------------------- */ + +void WriteRestart::force_fields() +{ + int n; + + if (force->pair) n = strlen(force->pair_style) + 1; + else n = 0; + fwrite(&n,sizeof(int),1,fp); + if (n) { + fwrite(force->pair_style,sizeof(char),n,fp); + force->pair->write_restart(fp); + } + + if (atom->bonds_allow) { + if (force->bond) n = strlen(force->bond_style) + 1; + else n = 0; + fwrite(&n,sizeof(int),1,fp); + if (n) { + fwrite(force->bond_style,sizeof(char),n,fp); + force->bond->write_restart(fp); + } + } + + if (atom->angles_allow) { + if (force->angle) n = strlen(force->angle_style) + 1; + else n = 0; + fwrite(&n,sizeof(int),1,fp); + if (n) { + fwrite(force->angle_style,sizeof(char),n,fp); + force->angle->write_restart(fp); + } + } + + if (atom->dihedrals_allow) { + if (force->dihedral) n = strlen(force->dihedral_style) + 1; + else n = 0; + fwrite(&n,sizeof(int),1,fp); + if (n) { + fwrite(force->dihedral_style,sizeof(char),n,fp); + force->dihedral->write_restart(fp); + } + } + + if (atom->impropers_allow) { + if (force->improper) n = strlen(force->improper_style) + 1; + else n = 0; + fwrite(&n,sizeof(int),1,fp); + if (n) { + fwrite(force->improper_style,sizeof(char),n,fp); + force->improper->write_restart(fp); + } + } +} + +/* ---------------------------------------------------------------------- + write_int - write a flag and an int into restart file +------------------------------------------------------------------------- */ + +void WriteRestart::write_int(int flag, int value) +{ + fwrite(&flag,sizeof(int),1,fp); + fwrite(&value,sizeof(int),1,fp); +} + +/* ---------------------------------------------------------------------- + write_double - write a flag and a double into restart file +------------------------------------------------------------------------- */ + +void WriteRestart::write_double(int flag, double value) +{ + fwrite(&flag,sizeof(int),1,fp); + fwrite(&value,sizeof(double),1,fp); +} + +/* ---------------------------------------------------------------------- + write_char - write a flag and a char str into restart file +------------------------------------------------------------------------- */ + +void WriteRestart::write_char(int flag, char *value) +{ + fwrite(&flag,sizeof(int),1,fp); + int n = strlen(value) + 1; + fwrite(&n,sizeof(int),1,fp); + fwrite(value,sizeof(char),n,fp); +} diff --git a/src/write_restart.h b/src/write_restart.h new file mode 100644 index 0000000000000000000000000000000000000000..f890c723b12a2f09e67408a8ffe79e6848c3f0d1 --- /dev/null +++ b/src/write_restart.h @@ -0,0 +1,43 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + www.cs.sandia.gov/~sjplimp/lammps.html + Steve Plimpton, sjplimp@sandia.gov, Sandia National Laboratories + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + + +#ifndef WRITE_RESTART_H +#define WRITE_RESTART_H + +#include "stdio.h" +#include "lammps.h" + +class WriteRestart : public LAMMPS { + public: + WriteRestart(); + ~WriteRestart() {} + void command(int, char **); + void write(char *); + + private: + int me,nprocs; + FILE *fp; + double natoms; // natoms (sum of nlocal) to write into file + + void header(); + void mass(); + void dipole(); + void force_fields(); + + void write_int(int, int); + void write_double(int, double); + void write_char(int, char *); +}; + +#endif