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(&notag,&notag_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