diff --git a/src/BODY/body_rounded_polygon.cpp b/src/BODY/body_rounded_polygon.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d848a8fa959adcf2fc1e723ba96a2b0b2f0c613c
--- /dev/null
+++ b/src/BODY/body_rounded_polygon.cpp
@@ -0,0 +1,452 @@
+/* ----------------------------------------------------------------------
+   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
+   http://lammps.sandia.gov, Sandia National Laboratories
+   Steve Plimpton, sjplimp@sandia.gov
+
+   Copyright (2003) Sandia Corporation.  Under the terms of Contract
+   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+   certain rights in this software.  This software is distributed under
+   the GNU General Public License.
+
+   See the README file in the top-level LAMMPS directory.
+------------------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------
+   Contributing author: Trung Dac Nguyen (ndactrung@gmail.com)
+------------------------------------------------------------------------- */
+
+#include <cstdlib>
+#include "body_rounded_polygon.h"
+#include "atom_vec_body.h"
+#include "atom.h"
+#include "force.h"
+#include "domain.h"
+#include "math_extra.h"
+#include "memory.h"
+#include "error.h"
+
+using namespace LAMMPS_NS;
+
+#define EPSILON 1.0e-7
+enum{SPHERE,LINE};           // also in DumpImage
+
+/* ---------------------------------------------------------------------- */
+
+BodyRoundedPolygon::BodyRoundedPolygon(LAMMPS *lmp, int narg, char **arg) :
+  Body(lmp, narg, arg)
+{
+  if (narg != 3) error->all(FLERR,"Invalid body rounded/polygon command");
+
+  if (domain->dimension != 2)
+    error->all(FLERR,"Atom_style body rounded/polygon "
+               "can only be used in 2d simulations");
+
+  // nmin and nmax are minimum and maximum number of vertices
+
+  int nmin = force->inumeric(FLERR,arg[1]);
+  int nmax = force->inumeric(FLERR,arg[2]);
+  if (nmin <= 0 || nmin > nmax)
+    error->all(FLERR,"Invalid body rounded/polygon command");
+
+  size_forward = 0;
+
+  // 1 integer for number of vertices,
+  // 3*nmax doubles for vertex coordinates + 2*nmax doubles for edge ends
+  // 1 double for the enclosing radius
+  // 1 double for the rounded radius
+
+  size_border = 1 + 3*nmax + 2*nmax + 1 + 1;
+
+  // NOTE: need to set appropriate nnbin param for dcp
+
+  icp = new MyPoolChunk<int>(1,1);
+  dcp = new MyPoolChunk<double>(3*nmin+2*nmin+1+1,3*nmax+2*nmax+1+1);
+
+  memory->create(imflag,nmax,"body/rounded/polygon:imflag");
+  memory->create(imdata,nmax,7,"body/nparticle:imdata");
+}
+
+/* ---------------------------------------------------------------------- */
+
+BodyRoundedPolygon::~BodyRoundedPolygon()
+{
+  delete icp;
+  delete dcp;
+  memory->destroy(imflag);
+  memory->destroy(imdata);
+}
+
+/* ---------------------------------------------------------------------- */
+
+int BodyRoundedPolygon::nsub(AtomVecBody::Bonus *bonus)
+{
+  return bonus->ivalue[0];
+}
+
+/* ---------------------------------------------------------------------- */
+
+double *BodyRoundedPolygon::coords(AtomVecBody::Bonus *bonus)
+{
+  return bonus->dvalue;
+}
+
+/* ---------------------------------------------------------------------- */
+
+int BodyRoundedPolygon::nedges(AtomVecBody::Bonus *bonus)
+{
+  int nvertices = bonus->ivalue[0];
+  if (nvertices == 1) return 0;
+  else if (nvertices == 2) return 1;
+  return nvertices;
+}
+
+/* ---------------------------------------------------------------------- */
+
+double *BodyRoundedPolygon::edges(AtomVecBody::Bonus *bonus)
+{
+  return bonus->dvalue+3*nsub(bonus);
+}
+
+/* ---------------------------------------------------------------------- */
+
+double BodyRoundedPolygon::enclosing_radius(struct AtomVecBody::Bonus *bonus)
+{
+  int nvertices = bonus->ivalue[0];
+  if (nvertices == 1 || nvertices == 2)
+	return *(bonus->dvalue+3*nsub(bonus)+2);
+  return *(bonus->dvalue+3*nsub(bonus)+2*nsub(bonus));
+}
+
+/* ---------------------------------------------------------------------- */
+
+double BodyRoundedPolygon::rounded_radius(struct AtomVecBody::Bonus *bonus)
+{
+  int nvertices = bonus->ivalue[0];
+  if (nvertices == 1 || nvertices == 2)
+	return *(bonus->dvalue+3*nsub(bonus)+2+1);
+  return *(bonus->dvalue+3*nsub(bonus)+2*nsub(bonus)+1);
+}
+
+/* ---------------------------------------------------------------------- */
+
+int BodyRoundedPolygon::pack_border_body(AtomVecBody::Bonus *bonus, double *buf)
+{
+  int nsub = bonus->ivalue[0];
+  buf[0] = nsub;
+  memcpy(&buf[1],bonus->dvalue,(3*nsub+2*nsub+1+1)*sizeof(double));
+  return 1+(3*nsub+2*nsub+1+1);
+}
+
+/* ---------------------------------------------------------------------- */
+
+int BodyRoundedPolygon::unpack_border_body(AtomVecBody::Bonus *bonus,
+                                           double *buf)
+{
+  int nsub = static_cast<int> (buf[0]);
+  bonus->ivalue[0] = nsub;
+  memcpy(bonus->dvalue,&buf[1],(3*nsub+2*nsub+1+1)*sizeof(double));
+  return 1+(3*nsub+2*nsub+1+1);
+}
+
+/* ----------------------------------------------------------------------
+   populate bonus data structure with data file values
+------------------------------------------------------------------------- */
+
+void BodyRoundedPolygon::data_body(int ibonus, int ninteger, int ndouble,
+				   int *ifile, double *dfile)
+{
+  AtomVecBody::Bonus *bonus = &avec->bonus[ibonus];
+
+  // set ninteger, ndouble in bonus and allocate 2 vectors of ints, doubles
+
+  if (ninteger != 1)
+    error->one(FLERR,"Incorrect # of integer values in "
+               "Bodies section of data file");
+  int nsub = ifile[0];
+  if (nsub < 1)
+    error->one(FLERR,"Incorrect integer value in "
+               "Bodies section of data file");
+
+  // nentries = number of double entries to be read from Body section:
+  //   6 for inertia + 3*nsub for vertex coords + 1 for rounded radius
+
+  int nentries = 6 + 3*nsub + 1; 
+  if (ndouble != nentries)
+    error->one(FLERR,"Incorrect # of floating-point values in "
+             "Bodies section of data file");
+
+  bonus->ninteger = 1;
+  bonus->ivalue = icp->get(bonus->iindex);
+  bonus->ivalue[0] = nsub;
+  bonus->ndouble = 3*nsub + 2*nsub + 1 + 1;
+  bonus->dvalue = dcp->get(bonus->ndouble,bonus->dindex);
+
+  // diagonalize inertia tensor
+
+  double tensor[3][3];
+  tensor[0][0] = dfile[0];
+  tensor[1][1] = dfile[1];
+  tensor[2][2] = dfile[2];
+  tensor[0][1] = tensor[1][0] = dfile[3];
+  tensor[0][2] = tensor[2][0] = dfile[4];
+  tensor[1][2] = tensor[2][1] = dfile[5];
+
+  double *inertia = bonus->inertia;
+  double evectors[3][3];
+  int ierror = MathExtra::jacobi(tensor,inertia,evectors);
+  if (ierror) error->one(FLERR,
+                         "Insufficient Jacobi rotations for body nparticle");
+
+  // if any principal moment < scaled EPSILON, set to 0.0
+
+  double max;
+  max = MAX(inertia[0],inertia[1]);
+  max = MAX(max,inertia[2]);
+
+  if (inertia[0] < EPSILON*max) inertia[0] = 0.0;
+  if (inertia[1] < EPSILON*max) inertia[1] = 0.0;
+  if (inertia[2] < EPSILON*max) inertia[2] = 0.0;
+
+  // exyz_space = principal axes in space frame
+
+  double ex_space[3],ey_space[3],ez_space[3];
+
+  ex_space[0] = evectors[0][0];
+  ex_space[1] = evectors[1][0];
+  ex_space[2] = evectors[2][0];
+  ey_space[0] = evectors[0][1];
+  ey_space[1] = evectors[1][1];
+  ey_space[2] = evectors[2][1];
+  ez_space[0] = evectors[0][2];
+  ez_space[1] = evectors[1][2];
+  ez_space[2] = evectors[2][2];
+
+  // enforce 3 evectors as a right-handed coordinate system
+  // flip 3rd vector if needed
+
+  double cross[3];
+  MathExtra::cross3(ex_space,ey_space,cross);
+  if (MathExtra::dot3(cross,ez_space) < 0.0) MathExtra::negate3(ez_space);
+
+  // create initial quaternion
+
+  MathExtra::exyz_to_q(ex_space,ey_space,ez_space,bonus->quat);
+
+  // bonus->dvalue = the first 3*nsub elements are sub-particle displacements
+  // find the enclosing radius of the body from the maximum displacement
+
+  int i,m;
+  double delta[3], rsq, erad, rrad;
+  double erad2 = 0;
+  int j = 6;
+  int k = 0;
+  for (i = 0; i < nsub; i++) {
+    delta[0] = dfile[j];
+    delta[1] = dfile[j+1];
+    delta[2] = dfile[j+2];
+    MathExtra::transpose_matvec(ex_space,ey_space,ez_space,
+                                delta,&bonus->dvalue[k]);
+    rsq = delta[0] * delta[0] + delta[1] * delta[1] +
+      delta[2] * delta[2];
+    if (rsq > erad2) erad2 = rsq;
+    j += 3;
+    k += 3;
+  }
+
+  // .. the next 2*nsub elements are edge ends
+
+  int nedges;
+  if (nsub == 1) { // spheres
+    nedges = 0;
+    bonus->dvalue[k] = 0;
+    *(&bonus->dvalue[k]+1) = 0;
+    k += 2;
+
+    // the last element of bonus->dvalue is the rounded & enclosing radius
+
+    rrad = 0.5 * dfile[j];
+    bonus->dvalue[k] = rrad;
+    erad = rrad;
+
+    k++;
+    bonus->dvalue[k] = rrad;
+
+    atom->radius[bonus->ilocal] = erad;
+
+  } else if (nsub == 2) { // rods
+    nedges = 1;
+    for (i = 0; i < nedges; i++) {
+      bonus->dvalue[k] = 0;
+      *(&bonus->dvalue[k]+1) = 1;
+      k += 2;
+    }    
+
+    erad = sqrt(erad2);
+    bonus->dvalue[k] = erad;
+
+    // the last element of bonus->dvalue is the rounded radius
+
+    rrad = 0.5 * dfile[j];
+    k++;
+    bonus->dvalue[k] = rrad;
+
+    atom->radius[bonus->ilocal] = erad + rrad;
+
+  } else { // polygons
+    nedges = nsub;
+    for (i = 0; i < nedges; i++) {
+      bonus->dvalue[k] = i;
+      m = i+1;
+      if (m == nedges) m = 0;
+      *(&bonus->dvalue[k]+1) = m;
+      k += 2;
+    }
+
+    // the next to last element is the enclosing radius
+
+    erad = sqrt(erad2);
+    bonus->dvalue[k] = erad;
+
+    // the last element of bonus->dvalue is the rounded radius
+
+    rrad = 0.5 * dfile[j];
+    k++;
+    bonus->dvalue[k] = rrad;
+
+    atom->radius[bonus->ilocal] = erad + rrad;
+  }
+}
+
+/* ----------------------------------------------------------------------
+   return radius of body particle defined by ifile/dfile params
+   params are ordered as in data file
+   called by Molecule class which needs single body size
+------------------------------------------------------------------------- */
+
+double BodyRoundedPolygon::radius_body(int ninteger, int ndouble,
+				       int *ifile, double *dfile)
+{
+  int nsub = ifile[0];
+  if (nsub < 1)
+    error->one(FLERR,"Incorrect integer value in "
+               "Bodies section of data file");
+  if (ndouble != 6 + 3*nsub + 1)
+    error->one(FLERR,"Incorrect # of floating-point values in "
+               "Bodies section of data file");
+
+  // sub-particle coords are relative to body center at (0,0,0)
+  // offset = 6 for sub-particle coords
+
+  double onerad;
+  double maxrad = 0.0;
+  double delta[3];
+
+  int offset = 6;          
+  for (int i = 0; i < nsub; i++) {
+    delta[0] = dfile[offset];
+    delta[1] = dfile[offset+1];
+    delta[2] = dfile[offset+2];
+    offset += 3;
+    onerad = MathExtra::len3(delta);
+    maxrad = MAX(maxrad,onerad);
+  }
+  
+  // add in radius of rounded corners
+  
+  return maxrad + 0.5*dfile[offset];
+}
+
+/* ---------------------------------------------------------------------- */
+
+int BodyRoundedPolygon::noutcol()
+{
+  // the number of columns for the vertex coordinates
+
+  return 3;
+}
+
+/* ---------------------------------------------------------------------- */
+
+int BodyRoundedPolygon::noutrow(int ibonus)
+{
+  // only return the first nsub rows for the vertex coordinates
+
+  return avec->bonus[ibonus].ivalue[0];
+}
+
+/* ---------------------------------------------------------------------- */
+
+void BodyRoundedPolygon::output(int ibonus, int m, double *values)
+{
+  AtomVecBody::Bonus *bonus = &avec->bonus[ibonus];
+
+  double p[3][3];
+  MathExtra::quat_to_mat(bonus->quat,p);
+  MathExtra::matvec(p,&bonus->dvalue[3*m],values);
+
+  double *x = atom->x[bonus->ilocal];
+  values[0] += x[0];
+  values[1] += x[1];
+  values[2] += x[2];
+}
+
+/* ---------------------------------------------------------------------- */
+
+int BodyRoundedPolygon::image(int ibonus, double flag1, double flag2,
+                              int *&ivec, double **&darray)
+{
+  int j;
+  double p[3][3];
+  double *x, rrad;
+
+  AtomVecBody::Bonus *bonus = &avec->bonus[ibonus];
+  int n = bonus->ivalue[0];
+  
+  if (n == 1) {
+    for (int i = 0; i < n; i++) {
+      imflag[i] = SPHERE;
+      MathExtra::quat_to_mat(bonus->quat,p);
+      MathExtra::matvec(p,&bonus->dvalue[3*i],imdata[i]);
+
+      rrad = enclosing_radius(bonus);
+      x = atom->x[bonus->ilocal];
+      imdata[i][0] += x[0];
+      imdata[i][1] += x[1];
+      imdata[i][2] += x[2];
+      if (flag1 <= 0) imdata[i][3] = 2*rrad;
+      else imdata[i][3] = flag1;
+    }
+
+  } else {
+  
+    // first end pt of each line
+
+    for (int i = 0; i < n; i++) {
+      imflag[i] = LINE;
+      MathExtra::quat_to_mat(bonus->quat,p);
+      MathExtra::matvec(p,&bonus->dvalue[3*i],imdata[i]);
+
+      rrad = rounded_radius(bonus);
+      x = atom->x[bonus->ilocal];
+      imdata[i][0] += x[0];
+      imdata[i][1] += x[1];
+      imdata[i][2] += x[2];
+      if (flag1 <= 0) imdata[i][6] = 2*rrad;
+      else imdata[i][6] = flag1;
+    }
+
+    // second end pt of each line
+  
+    for (int i = 0; i < n; i++) {
+      j = i+1;
+      if (j == n) j = 0;
+      imdata[i][3] = imdata[j][0];
+      imdata[i][4] = imdata[j][1];
+      imdata[i][5] = imdata[j][2];
+    }
+  }
+
+  ivec = imflag;
+  darray = imdata;
+  return n;
+}
diff --git a/src/BODY/body_rounded_polygon.h b/src/BODY/body_rounded_polygon.h
new file mode 100644
index 0000000000000000000000000000000000000000..b6f45c5cf551cc35fc1ff2d817684f5250cf23f4
--- /dev/null
+++ b/src/BODY/body_rounded_polygon.h
@@ -0,0 +1,86 @@
+/* -*- c++ -*- ----------------------------------------------------------
+   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
+   http://lammps.sandia.gov, Sandia National Laboratories
+   Steve Plimpton, sjplimp@sandia.gov
+
+   Copyright (2003) Sandia Corporation.  Under the terms of Contract
+   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+   certain rights in this software.  This software is distributed under
+   the GNU General Public License.
+
+   See the README file in the top-level LAMMPS directory.
+------------------------------------------------------------------------- */
+
+#ifdef BODY_CLASS
+
+BodyStyle(rounded/polygon,BodyRoundedPolygon)
+
+#else
+
+#ifndef LMP_BODY_ROUNDED_POLYGON_H
+#define LMP_BODY_ROUNDED_POLYGON_H
+
+#include "body.h"
+#include "atom_vec_body.h"
+
+namespace LAMMPS_NS {
+
+class BodyRoundedPolygon : public Body {
+ public:
+  BodyRoundedPolygon(class LAMMPS *, int, char **);
+  ~BodyRoundedPolygon();
+  int nsub(struct AtomVecBody::Bonus *);
+  double *coords(struct AtomVecBody::Bonus *);
+  int nedges(struct AtomVecBody::Bonus *);
+  double *edges(struct AtomVecBody::Bonus *);
+  double enclosing_radius(struct AtomVecBody::Bonus *);
+  double rounded_radius(struct AtomVecBody::Bonus *);
+
+  int pack_border_body(struct AtomVecBody::Bonus *, double *);
+  int unpack_border_body(struct AtomVecBody::Bonus *, double *);
+  void data_body(int, int, int, int *, double *);
+  double radius_body(int, int, int *, double *);
+
+  int noutrow(int);
+  int noutcol();
+  void output(int, int, double *);
+  int image(int, double, double, int *&, double **&);
+
+ private:
+  int *imflag;
+  double **imdata;
+};
+
+}
+
+#endif
+#endif
+
+/* ERROR/WARNING messages:
+
+E: Invalid body rounded/polygon command
+
+Arguments in atom-style command are not correct.
+
+E: Invalid format in Bodies section of data file
+
+The specified number of integer or floating point values does not
+appear.
+
+E: Incorrect # of integer values in Bodies section of data file
+
+See doc page for body style.
+
+E: Incorrect integer value in Bodies section of data file
+
+See doc page for body style.
+
+E: Incorrect # of floating-point values in Bodies section of data file
+
+See doc page for body style.
+
+E: Insufficient Jacobi rotations for body nparticle
+
+Eigensolve for rigid body was not sufficiently accurate.
+
+*/
diff --git a/src/BODY/body_rounded_polyhedron.cpp b/src/BODY/body_rounded_polyhedron.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a26b6d0cbd4ef0ddc0f1294a8c6d6a57349eafad
--- /dev/null
+++ b/src/BODY/body_rounded_polyhedron.cpp
@@ -0,0 +1,523 @@
+/* ----------------------------------------------------------------------
+   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
+   http://lammps.sandia.gov, Sandia National Laboratories
+   Steve Plimpton, sjplimp@sandia.gov
+
+   Copyright (2003) Sandia Corporation.  Under the terms of Contract
+   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+   certain rights in this software.  This software is distributed under
+   the GNU General Public License.
+
+   See the README file in the top-level LAMMPS directory.
+------------------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------
+   Contributing author: Trung Dac Nguyen (ndactrung@gmail.com)
+------------------------------------------------------------------------- */
+
+#include <cstdlib>
+#include "body_rounded_polyhedron.h"
+#include "atom_vec_body.h"
+#include "atom.h"
+#include "force.h"
+#include "domain.h"
+#include "math_extra.h"
+#include "memory.h"
+#include "error.h"
+
+using namespace LAMMPS_NS;
+
+#define EPSILON 1.0e-7
+#define MAX_FACE_SIZE 4  // maximum number of vertices per face (for now)
+
+enum{SPHERE,LINE};       // also in DumpImage
+
+/* ---------------------------------------------------------------------- */
+
+BodyRoundedPolyhedron::BodyRoundedPolyhedron(LAMMPS *lmp, int narg, char **arg) :
+  Body(lmp, narg, arg)
+{
+  if (narg != 3) error->all(FLERR,"Invalid body rounded/polygon command");
+
+  // nmin and nmax are minimum and maximum number of vertices
+
+  int nmin = force->inumeric(FLERR,arg[1]);
+  int nmax = force->inumeric(FLERR,arg[2]);
+  if (nmin <= 0 || nmin > nmax)
+    error->all(FLERR,"Invalid body rounded/polyhedron command");
+
+  size_forward = 0;
+
+  // 1 integer for number of vertices,
+  // 3*nmax doubles for vertex coordinates + 2*nmax doubles for edge ends +
+  // (MAX_FACE_SIZE+1)*nmax for faces
+  // 1 double for the enclosing radius
+  // 1 double for the rounded radius
+
+  size_border = 1 + 3*nmax + 2*nmax + MAX_FACE_SIZE*nmax + 1 + 1;
+
+  // NOTE: need to set appropriate nnbin param for dcp
+
+  icp = new MyPoolChunk<int>(1,3);
+  dcp = new MyPoolChunk<double>(3*nmin+2+1+1,
+                                3*nmax+2*nmax+MAX_FACE_SIZE*nmax+1+1);
+
+  memory->create(imflag,2*nmax,"body/rounded/polyhedron:imflag");
+  memory->create(imdata,2*nmax,7,"body/polyhedron:imdata");
+}
+
+/* ---------------------------------------------------------------------- */
+
+BodyRoundedPolyhedron::~BodyRoundedPolyhedron()
+{
+  delete icp;
+  delete dcp;
+  memory->destroy(imflag);
+  memory->destroy(imdata);
+}
+
+/* ---------------------------------------------------------------------- */
+
+int BodyRoundedPolyhedron::nsub(AtomVecBody::Bonus *bonus)
+{
+  return bonus->ivalue[0];
+}
+
+/* ---------------------------------------------------------------------- */
+
+double *BodyRoundedPolyhedron::coords(AtomVecBody::Bonus *bonus)
+{
+  return bonus->dvalue;
+}
+
+/* ---------------------------------------------------------------------- */
+
+int BodyRoundedPolyhedron::nedges(AtomVecBody::Bonus *bonus)
+{
+  int nvertices = bonus->ivalue[0];
+  int nedges = bonus->ivalue[1];
+  int nfaces = bonus->ivalue[2];
+  if (nvertices == 1) return 0;
+  else if (nvertices == 2) return 1;
+  return nedges; //(nvertices+nfaces-2); // Euler's polyon formula: V-E+F=2
+}
+
+/* ---------------------------------------------------------------------- */
+
+double *BodyRoundedPolyhedron::edges(AtomVecBody::Bonus *bonus)
+{
+  return bonus->dvalue+3*nsub(bonus);
+}
+
+/* ---------------------------------------------------------------------- */
+
+int BodyRoundedPolyhedron::nfaces(AtomVecBody::Bonus *bonus)
+{
+  return bonus->ivalue[2];
+}
+
+/* ---------------------------------------------------------------------- */
+
+double *BodyRoundedPolyhedron::faces(AtomVecBody::Bonus *bonus)
+{
+  int nvertices = bonus->ivalue[0];
+  if (nvertices == 1 || nvertices == 2) return NULL;
+  return bonus->dvalue+3*nsub(bonus)+2*nedges(bonus);
+}
+
+/* ---------------------------------------------------------------------- */
+
+double BodyRoundedPolyhedron::enclosing_radius(struct AtomVecBody::Bonus *bonus)
+{
+  int nvertices = bonus->ivalue[0];
+  if (nvertices == 1 || nvertices == 2)
+  	return *(bonus->dvalue+3*nsub(bonus)+2);
+  return *(bonus->dvalue+3*nsub(bonus)+2*nedges(bonus)+MAX_FACE_SIZE*nfaces(bonus));
+}
+
+/* ---------------------------------------------------------------------- */
+
+double BodyRoundedPolyhedron::rounded_radius(struct AtomVecBody::Bonus *bonus)
+{
+  int nvertices = bonus->ivalue[0];
+  if (nvertices == 1 || nvertices == 2)
+    return *(bonus->dvalue+3*nsub(bonus)+2+1);
+  return *(bonus->dvalue+3*nsub(bonus)+2*nedges(bonus)+MAX_FACE_SIZE*nfaces(bonus)+1);
+}
+
+/* ---------------------------------------------------------------------- */
+
+int BodyRoundedPolyhedron::pack_border_body(AtomVecBody::Bonus *bonus, double *buf)
+{
+  int nsub = bonus->ivalue[0];
+  int ned = bonus->ivalue[1];
+  int nfac = bonus->ivalue[2];
+  buf[0] = nsub;
+  buf[1] = ned;
+  buf[2] = nfac;
+  int ndouble;
+  if (nsub == 1 || nsub == 2) ndouble = 3*nsub+2+MAX_FACE_SIZE*nfac+1+1;
+  else ndouble = 3*nsub+2*nedges(bonus)+MAX_FACE_SIZE*nfac+1+1;
+  memcpy(&buf[3],bonus->dvalue,ndouble*sizeof(double));
+  return 3+ndouble;
+}
+
+/* ---------------------------------------------------------------------- */
+
+int BodyRoundedPolyhedron::unpack_border_body(AtomVecBody::Bonus *bonus,
+                                           double *buf)
+{
+  int nsub = static_cast<int> (buf[0]);
+  int ned = static_cast<int> (buf[1]);
+  int nfac = static_cast<int> (buf[2]);
+  bonus->ivalue[0] = nsub;
+  bonus->ivalue[1] = ned;
+  bonus->ivalue[2] = nfac;
+  int ndouble;
+  if (nsub == 1 || nsub == 2) ndouble = 3*nsub+2+MAX_FACE_SIZE*nfac+1+1;
+  else ndouble = 3*nsub+2*nedges(bonus)+MAX_FACE_SIZE*nfac+1+1;
+  memcpy(bonus->dvalue,&buf[3],ndouble*sizeof(double));
+  return 3+ndouble;
+}
+
+/* ----------------------------------------------------------------------
+   populate bonus data structure with data file values
+------------------------------------------------------------------------- */
+
+void BodyRoundedPolyhedron::data_body(int ibonus, int ninteger, int ndouble,
+                             int *ifile, double *dfile)
+{
+  AtomVecBody::Bonus *bonus = &avec->bonus[ibonus];
+
+  // set ninteger, ndouble in bonus and allocate 2 vectors of ints, doubles
+
+  if (ninteger != 3)
+    error->one(FLERR,"Incorrect # of integer values in "
+               "Bodies section of data file");
+  int nsub = ifile[0];
+  int ned = ifile[1];
+  int nfac = ifile[2];
+  if (nsub < 1)
+    error->one(FLERR,"Incorrect integer value in "
+               "Bodies section of data file");
+
+  // nentries = number of double entries to be read from Body section:
+  // nsub == 1 || nsub == 2 || nsub == 3:
+  //   6 for inertia + 3*nsub for vertex coords + 1 for rounded radius
+  // nsub > 3:
+  //   6 for inertia + 3*nsub for vertex coords + 2*nsub for edges + 3*nfaces + 1 for rounded radius
+
+  int nedges,nentries;
+  if (nsub == 1 || nsub == 2) {
+    nentries = 6 + 3*nsub + 1;
+  } else {
+    nedges = ned; //nsub + nfac - 2;
+    nentries = 6 + 3*nsub + 2*nedges + MAX_FACE_SIZE*nfac + 1;
+  }
+  if (ndouble != nentries)
+    error->one(FLERR,"Incorrect # of floating-point values in "
+             "Bodies section of data file");
+
+  bonus->ninteger = 3;
+  bonus->ivalue = icp->get(bonus->iindex);
+  bonus->ivalue[0] = nsub;
+  bonus->ivalue[1] = ned;
+  bonus->ivalue[2] = nfac;
+  if (nsub == 1 || nsub == 2) bonus->ndouble = 3*nsub + 2*nsub + 1 + 1;
+  else bonus->ndouble = 3*nsub + 2*nedges + MAX_FACE_SIZE*nfac + 1 + 1;
+  bonus->dvalue = dcp->get(bonus->ndouble,bonus->dindex);
+
+  // diagonalize inertia tensor
+
+  double tensor[3][3];
+  tensor[0][0] = dfile[0];
+  tensor[1][1] = dfile[1];
+  tensor[2][2] = dfile[2];
+  tensor[0][1] = tensor[1][0] = dfile[3];
+  tensor[0][2] = tensor[2][0] = dfile[4];
+  tensor[1][2] = tensor[2][1] = dfile[5];
+
+  double *inertia = bonus->inertia;
+  double evectors[3][3];
+  int ierror = MathExtra::jacobi(tensor,inertia,evectors);
+  if (ierror) error->one(FLERR,
+                         "Insufficient Jacobi rotations for body nparticle");
+
+  // if any principal moment < scaled EPSILON, set to 0.0
+
+  double max;
+  max = MAX(inertia[0],inertia[1]);
+  max = MAX(max,inertia[2]);
+
+  if (inertia[0] < EPSILON*max) inertia[0] = 0.0;
+  if (inertia[1] < EPSILON*max) inertia[1] = 0.0;
+  if (inertia[2] < EPSILON*max) inertia[2] = 0.0;
+
+  // exyz_space = principal axes in space frame
+
+  double ex_space[3],ey_space[3],ez_space[3];
+
+  ex_space[0] = evectors[0][0];
+  ex_space[1] = evectors[1][0];
+  ex_space[2] = evectors[2][0];
+  ey_space[0] = evectors[0][1];
+  ey_space[1] = evectors[1][1];
+  ey_space[2] = evectors[2][1];
+  ez_space[0] = evectors[0][2];
+  ez_space[1] = evectors[1][2];
+  ez_space[2] = evectors[2][2];
+
+  // enforce 3 evectors as a right-handed coordinate system
+  // flip 3rd vector if needed
+
+  double cross[3];
+  MathExtra::cross3(ex_space,ey_space,cross);
+  if (MathExtra::dot3(cross,ez_space) < 0.0) MathExtra::negate3(ez_space);
+
+  // create initial quaternion
+
+  MathExtra::exyz_to_q(ex_space,ey_space,ez_space,bonus->quat);
+
+  // bonus->dvalue = the first 3*nsub elements are sub-particle displacements
+  // find the enclosing radius of the body from the maximum displacement
+
+  int i,m;
+  double delta[3], rsq, erad, rrad;
+  double erad2 = 0;
+  int j = 6;
+  int k = 0;
+  for (i = 0; i < nsub; i++) {
+    delta[0] = dfile[j];
+    delta[1] = dfile[j+1];
+    delta[2] = dfile[j+2];
+    MathExtra::transpose_matvec(ex_space,ey_space,ez_space,
+                                delta,&bonus->dvalue[k]);
+    rsq = delta[0] * delta[0] + delta[1] * delta[1] +
+      delta[2] * delta[2];
+    if (rsq > erad2) erad2 = rsq;
+    j += 3;
+    k += 3;
+  }
+
+  // .. the next 2*nsub elements are edge ends
+
+  if (nsub == 1) { // spheres
+    nedges = 0;
+    bonus->dvalue[k] = 0;
+    *(&bonus->dvalue[k]+1) = 0;
+    k += 2;
+
+    rrad = 0.5 * dfile[j];
+    bonus->dvalue[k] = rrad;
+    erad = rrad; // enclosing radius = rounded_radius
+
+    // the last element of bonus->dvalue is the rounded radius
+
+    k++;
+    bonus->dvalue[k] = rrad;
+
+    atom->radius[bonus->ilocal] = erad;
+
+  } else if (nsub == 2) { // rods
+    nedges = 1;
+    for (i = 0; i < nedges; i++) {
+      bonus->dvalue[k] = 0;
+      *(&bonus->dvalue[k]+1) = 1;
+      k += 2;
+    }    
+
+    erad = sqrt(erad2);
+    bonus->dvalue[k] = erad;
+
+    // the last element of bonus->dvalue is the rounded radius
+
+    rrad = 0.5 * dfile[j];
+    k++;
+    bonus->dvalue[k] = rrad;
+
+    atom->radius[bonus->ilocal] = erad + rrad;
+
+  } else { // polyhedra
+
+    // edges
+
+    for (i = 0; i < nedges; i++) {
+      bonus->dvalue[k] = dfile[j];
+      *(&bonus->dvalue[k]+1) = dfile[j+1];
+      k += 2;
+      j += 2;
+    }
+
+    // faces
+
+    for (i = 0; i < nfac; i++) {
+      for (m = 0; m < MAX_FACE_SIZE; m++)
+        *(&bonus->dvalue[k]+m) = dfile[j+m];
+      k += MAX_FACE_SIZE;
+      j += MAX_FACE_SIZE;
+    }
+
+    // the next to last element is the enclosing radius
+
+    erad = sqrt(erad2);
+    bonus->dvalue[k] = erad;
+
+    // the last element bonus-> dvalue is the rounded radius
+
+    rrad = 0.5 * dfile[j];
+    k++;
+    bonus->dvalue[k] = rrad;
+
+    atom->radius[bonus->ilocal] = erad + rrad;
+  }
+}
+
+/* ----------------------------------------------------------------------
+   return radius of body particle defined by ifile/dfile params
+   params are ordered as in data file
+   called by Molecule class which needs single body size
+------------------------------------------------------------------------- */
+
+double BodyRoundedPolyhedron::radius_body(int ninteger, int ndouble,
+				       int *ifile, double *dfile)
+{
+  int nsub = ifile[0];
+  int ned = ifile[1];
+  int nfac = ifile[2];
+  int nedges = ned; //nsub + nfac - 2;
+
+  int nentries;
+  if (nsub == 1 || nsub == 2) nentries = 6 + 3*nsub + 1;
+  else nentries = 6 + 3*nsub + 2*nedges + MAX_FACE_SIZE*nfac + 1;
+
+  if (nsub < 1)
+    error->one(FLERR,"Incorrect integer value in "
+               "Bodies section of data file");
+  if (ndouble != nentries)
+    error->one(FLERR,"Incorrect # of floating-point values in "
+               "Bodies section of data file");
+
+  // sub-particle coords are relative to body center at (0,0,0)
+  // offset = 6 for sub-particle coords
+
+  double onerad;
+  double maxrad = 0.0;
+  double delta[3];
+
+  int offset = 6;          
+  for (int i = 0; i < nsub; i++) {
+    delta[0] = dfile[offset];
+    delta[1] = dfile[offset+1];
+    delta[2] = dfile[offset+2];
+    offset += 3;
+    onerad = MathExtra::len3(delta);
+    maxrad = MAX(maxrad,onerad);
+  }
+
+  if (nsub > 2) offset += (2*nedges+MAX_FACE_SIZE*nfac);
+
+  // add in radius of rounded corners
+  
+  return maxrad + 0.5*dfile[offset];
+}
+
+/* ---------------------------------------------------------------------- */
+
+int BodyRoundedPolyhedron::noutcol()
+{
+  // the number of columns for the vertex coordinates
+
+  return 3;
+}
+
+/* ---------------------------------------------------------------------- */
+
+int BodyRoundedPolyhedron::noutrow(int ibonus)
+{
+  // only return the first nsub rows for the vertex coordinates
+
+  return avec->bonus[ibonus].ivalue[0];
+}
+
+/* ---------------------------------------------------------------------- */
+
+void BodyRoundedPolyhedron::output(int ibonus, int m, double *values)
+{
+  AtomVecBody::Bonus *bonus = &avec->bonus[ibonus];
+
+  double p[3][3];
+  MathExtra::quat_to_mat(bonus->quat,p);
+  MathExtra::matvec(p,&bonus->dvalue[3*m],values);
+
+  double *x = atom->x[bonus->ilocal];
+  values[0] += x[0];
+  values[1] += x[1];
+  values[2] += x[2];
+}
+
+/* ---------------------------------------------------------------------- */
+
+int BodyRoundedPolyhedron::image(int ibonus, double flag1, double flag2,
+                              int *&ivec, double **&darray)
+{
+  int j, nelements;
+  double p[3][3];
+  double *x, rrad;
+
+  AtomVecBody::Bonus *bonus = &avec->bonus[ibonus];
+  int nvertices = bonus->ivalue[0];
+
+  if (nvertices == 1) { // spheres
+
+    for (int i = 0; i < nvertices; i++) {
+      imflag[i] = SPHERE;
+      MathExtra::quat_to_mat(bonus->quat,p);
+      MathExtra::matvec(p,&bonus->dvalue[3*i],imdata[i]);
+
+      rrad = enclosing_radius(bonus);
+      x = atom->x[bonus->ilocal];
+      imdata[i][0] += x[0];
+      imdata[i][1] += x[1];
+      imdata[i][2] += x[2];
+      if (flag1 <= 0) imdata[i][3] = 2*rrad;
+      else imdata[i][3] = flag1;
+    }
+
+    nelements = nvertices;
+  } else {
+    int nfaces = bonus->ivalue[2];
+    int nedges = bonus->ivalue[1]; //nvertices + nfaces - 2;
+    if (nvertices == 2) nedges = 1; // special case: rods
+    double* edge_ends = &bonus->dvalue[3*nvertices];
+    int pt1, pt2;
+
+    for (int i = 0; i < nedges; i++) {
+      imflag[i] = LINE;
+
+      pt1 = static_cast<int>(edge_ends[2*i]);
+      pt2 = static_cast<int>(edge_ends[2*i+1]);
+
+      MathExtra::quat_to_mat(bonus->quat,p);
+      MathExtra::matvec(p,&bonus->dvalue[3*pt1],imdata[i]);
+      MathExtra::matvec(p,&bonus->dvalue[3*pt2],&imdata[i][3]);
+
+      rrad = rounded_radius(bonus);
+      x = atom->x[bonus->ilocal];
+      imdata[i][0] += x[0];
+      imdata[i][1] += x[1];
+      imdata[i][2] += x[2];
+      imdata[i][3] += x[0];
+      imdata[i][4] += x[1];
+      imdata[i][5] += x[2];
+
+      if (flag1 <= 0) imdata[i][6] = 2*rrad;
+      else imdata[i][6] = flag1;
+    }
+
+    nelements = nedges;
+  }
+
+  ivec = imflag;
+  darray = imdata;
+  return nelements;
+}
diff --git a/src/BODY/body_rounded_polyhedron.h b/src/BODY/body_rounded_polyhedron.h
new file mode 100644
index 0000000000000000000000000000000000000000..e5b15fd8f93f6c0a65d896262a52dd85d7569c41
--- /dev/null
+++ b/src/BODY/body_rounded_polyhedron.h
@@ -0,0 +1,88 @@
+/* -*- c++ -*- ----------------------------------------------------------
+   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
+   http://lammps.sandia.gov, Sandia National Laboratories
+   Steve Plimpton, sjplimp@sandia.gov
+
+   Copyright (2003) Sandia Corporation.  Under the terms of Contract
+   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+   certain rights in this software.  This software is distributed under
+   the GNU General Public License.
+
+   See the README file in the top-level LAMMPS directory.
+------------------------------------------------------------------------- */
+
+#ifdef BODY_CLASS
+
+BodyStyle(rounded/polyhedron,BodyRoundedPolyhedron)
+
+#else
+
+#ifndef LMP_BODY_ROUNDED_POLYHEDRON_H
+#define LMP_BODY_ROUNDED_POLYHEDRON_H
+
+#include "body.h"
+#include "atom_vec_body.h"
+
+namespace LAMMPS_NS {
+
+class BodyRoundedPolyhedron : public Body {
+ public:
+  BodyRoundedPolyhedron(class LAMMPS *, int, char **);
+  ~BodyRoundedPolyhedron();
+  int nsub(struct AtomVecBody::Bonus *);
+  double *coords(struct AtomVecBody::Bonus *);
+  int nedges(struct AtomVecBody::Bonus *);
+  double *edges(struct AtomVecBody::Bonus *);
+  int nfaces(struct AtomVecBody::Bonus *);
+  double *faces(struct AtomVecBody::Bonus *);
+  double enclosing_radius(struct AtomVecBody::Bonus *);
+  double rounded_radius(struct AtomVecBody::Bonus *);
+
+  int pack_border_body(struct AtomVecBody::Bonus *, double *);
+  int unpack_border_body(struct AtomVecBody::Bonus *, double *);
+  void data_body(int, int, int, int *, double *);
+  double radius_body(int, int, int *, double *);
+
+  int noutrow(int);
+  int noutcol();
+  void output(int, int, double *);
+  int image(int, double, double, int *&, double **&);
+
+ private:
+  int *imflag;
+  double **imdata;
+};
+
+}
+
+#endif
+#endif
+
+/* ERROR/WARNING messages:
+
+E: Invalid body rounded/polyhedron command
+
+Arguments in atom-style command are not correct.
+
+E: Invalid format in Bodies section of data file
+
+The specified number of integer or floating point values does not
+appear.
+
+E: Incorrect # of integer values in Bodies section of data file
+
+See doc page for body style.
+
+E: Incorrect integer value in Bodies section of data file
+
+See doc page for body style.
+
+E: Incorrect # of floating-point values in Bodies section of data file
+
+See doc page for body style.
+
+E: Insufficient Jacobi rotations for body rounded/polyhedron
+
+Eigensolve for rigid body was not sufficiently accurate.
+
+*/
diff --git a/src/BODY/fix_wall_body_polygon.cpp b/src/BODY/fix_wall_body_polygon.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ea81ae26dfce881840769636e7a28c2d218ab43e
--- /dev/null
+++ b/src/BODY/fix_wall_body_polygon.cpp
@@ -0,0 +1,831 @@
+/* ----------------------------------------------------------------------
+   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
+   http://lammps.sandia.gov, Sandia National Laboratories
+   Steve Plimpton, sjplimp@sandia.gov
+
+   Copyright (2003) Sandia Corporation.  Under the terms of Contract
+   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+   certain rights in this software.  This software is distributed under
+   the GNU General Public License.
+
+   See the README file in the top-level LAMMPS directory.
+------------------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------
+   Contributing authors: Trung Dac Nguyen (ndactrung@gmail.com)
+------------------------------------------------------------------------- */
+
+#include <cmath>
+#include <cstdlib>
+#include <cstring>
+#include "fix_wall_body_polygon.h"
+#include "atom.h"
+#include "atom_vec_body.h"
+#include "body_rounded_polygon.h"
+#include "domain.h"
+#include "update.h"
+#include "force.h"
+#include "pair.h"
+#include "modify.h"
+#include "respa.h"
+#include "math_const.h"
+#include "math_extra.h"
+#include "memory.h"
+#include "error.h"
+
+using namespace LAMMPS_NS;
+using namespace FixConst;
+using namespace MathConst;
+
+enum{XPLANE=0,YPLANE=1,ZCYLINDER};    // XYZ PLANE need to be 0,1,2
+enum{HOOKE,HOOKE_HISTORY};
+
+enum {INVALID=0,NONE=1,VERTEX=2};
+enum {FAR=0,XLO,XHI,YLO,YHI};
+
+//#define _POLYGON_DEBUG
+#define DELTA 10000
+#define EPSILON 1e-2
+#define BIG 1.0e20
+#define MAX_CONTACTS 4  // maximum number of contacts for 2D models
+#define EFF_CONTACTS 2  // effective contacts for 2D models
+
+/* ---------------------------------------------------------------------- */
+
+FixWallBodyPolygon::FixWallBodyPolygon(LAMMPS *lmp, int narg, char **arg) :
+  Fix(lmp, narg, arg)
+{
+  if (narg < 7) error->all(FLERR,"Illegal fix wall/body/polygon command");
+
+  if (!atom->body_flag)
+    error->all(FLERR,"Fix wall/body/polygon requires atom style body/rounded/polygon");
+
+  restart_peratom = 1;
+  create_attribute = 1;
+
+  // wall/particle coefficients
+
+  kn = force->numeric(FLERR,arg[3]);
+
+  c_n = force->numeric(FLERR,arg[4]);
+  if (strcmp(arg[5],"NULL") == 0) c_t = 0.5 * c_n;
+  else c_t = force->numeric(FLERR,arg[5]);
+
+  if (kn < 0.0 || c_n < 0.0 || c_t < 0.0)
+    error->all(FLERR,"Illegal fix wall/body/polygon command");
+
+  // wallstyle args
+
+  int iarg = 6;
+  if (strcmp(arg[iarg],"xplane") == 0) {
+    if (narg < iarg+3) error->all(FLERR,"Illegal fix wall/body/polygon command");
+    wallstyle = XPLANE;
+    if (strcmp(arg[iarg+1],"NULL") == 0) lo = -BIG;
+    else lo = force->numeric(FLERR,arg[iarg+1]);
+    if (strcmp(arg[iarg+2],"NULL") == 0) hi = BIG;
+    else hi = force->numeric(FLERR,arg[iarg+2]);
+    iarg += 3;
+  } else if (strcmp(arg[iarg],"yplane") == 0) {
+    if (narg < iarg+3) error->all(FLERR,"Illegal fix wall/body/polygon command");
+    wallstyle = YPLANE;
+    if (strcmp(arg[iarg+1],"NULL") == 0) lo = -BIG;
+    else lo = force->numeric(FLERR,arg[iarg+1]);
+    if (strcmp(arg[iarg+2],"NULL") == 0) hi = BIG;
+    else hi = force->numeric(FLERR,arg[iarg+2]);
+    iarg += 3;
+  } else if (strcmp(arg[iarg],"zcylinder") == 0) {
+    if (narg < iarg+2) error->all(FLERR,"Illegal fix wall/body/polygon command");
+    wallstyle = ZCYLINDER;
+    lo = hi = 0.0;
+    cylradius = force->numeric(FLERR,arg[iarg+1]);
+    iarg += 2;
+  }
+
+  // check for trailing keyword/values
+
+  wiggle = 0;
+
+  while (iarg < narg) {
+    if (strcmp(arg[iarg],"wiggle") == 0) {
+      if (iarg+4 > narg) error->all(FLERR,"Illegal fix wall/body/polygon 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(FLERR,"Illegal fix wall/body/polygon command");
+      amplitude = force->numeric(FLERR,arg[iarg+2]);
+      period = force->numeric(FLERR,arg[iarg+3]);
+      wiggle = 1;
+      iarg += 4;
+    } else error->all(FLERR,"Illegal fix wall/body/polygon command");
+  }
+
+  if (wallstyle == XPLANE && domain->xperiodic)
+    error->all(FLERR,"Cannot use wall in periodic dimension");
+  if (wallstyle == YPLANE && domain->yperiodic)
+    error->all(FLERR,"Cannot use wall in periodic dimension");
+  if (wallstyle == ZCYLINDER && (domain->xperiodic || domain->yperiodic))
+    error->all(FLERR,"Cannot use wall in periodic dimension");
+
+  if (wiggle && wallstyle == ZCYLINDER && axis != 2)
+    error->all(FLERR,"Invalid wiggle direction for fix wall/body/polygon");
+
+  // setup oscillations
+
+  if (wiggle) omega = 2.0*MY_PI / period;
+
+  time_origin = update->ntimestep;
+
+  dmax = nmax = 0;
+  discrete = NULL;
+  dnum = dfirst = NULL;
+
+  edmax = ednummax = 0;
+  edge = NULL;
+  ednum = edfirst = NULL;
+
+  enclosing_radius = NULL;
+  rounded_radius = NULL;
+}
+
+/* ---------------------------------------------------------------------- */
+
+FixWallBodyPolygon::~FixWallBodyPolygon()
+{
+  memory->destroy(discrete);
+  memory->destroy(dnum);
+  memory->destroy(dfirst);
+
+  memory->destroy(edge);
+  memory->destroy(ednum);
+  memory->destroy(edfirst);
+
+  memory->destroy(enclosing_radius);
+  memory->destroy(rounded_radius);
+}
+
+/* ---------------------------------------------------------------------- */
+
+int FixWallBodyPolygon::setmask()
+{
+  int mask = 0;
+  mask |= POST_FORCE;
+  return mask;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void FixWallBodyPolygon::init()
+{
+  dt = update->dt;
+
+  avec = (AtomVecBody *) atom->style_match("body");
+  if (!avec)
+    error->all(FLERR,"Pair body/rounded/polygon requires atom style body");
+  if (strcmp(avec->bptr->style,"rounded/polygon") != 0)
+    error->all(FLERR,"Pair body/rounded/polygon requires body style rounded/polygon");
+  bptr = (BodyRoundedPolygon *) avec->bptr;
+
+  // set pairstyle from body/polygonular pair style
+
+  if (force->pair_match("body/rounded/polygon",1))
+    pairstyle = HOOKE;
+  else error->all(FLERR,"Fix wall/body/polygon is incompatible with Pair style");
+}
+
+/* ---------------------------------------------------------------------- */
+
+void FixWallBodyPolygon::setup(int vflag)
+{
+  if (strstr(update->integrate_style,"verlet"))
+    post_force(vflag);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void FixWallBodyPolygon::post_force(int vflag)
+{
+  double vwall[3],dx,dy,dz,del1,del2,delxy,delr,rsq,eradi,rradi,wall_pos;
+  int i,ni,npi,ifirst,nei,iefirst,side;
+  double facc[3];
+
+  // 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;
+    if (wallstyle == axis) {
+      wlo = lo + amplitude - amplitude*cos(arg);
+      whi = hi + amplitude - amplitude*cos(arg);
+    }
+    vwall[axis] = amplitude*omega*sin(arg);
+  }
+
+  // loop over all my atoms
+  // rsq = distance from wall
+  // dx,dy,dz = signed distance from wall
+  // for rotating cylinder, reset vwall based on particle position
+  // 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
+
+  double **x = atom->x;
+  double **v = atom->v;
+  double **f = atom->f;
+  int *body = atom->body;
+  double *radius = atom->radius;
+  double **torque = atom->torque;
+  double **angmom = atom->angmom;
+  int *mask = atom->mask;
+  int nlocal = atom->nlocal;
+
+  // grow the per-atom lists if necessary and initialize
+
+  if (atom->nmax > nmax) {
+    memory->destroy(dnum);
+    memory->destroy(dfirst);
+    memory->destroy(ednum);
+    memory->destroy(edfirst);
+    memory->destroy(enclosing_radius);
+    memory->destroy(rounded_radius);
+    nmax = atom->nmax;
+    memory->create(dnum,nmax,"fix:dnum");
+    memory->create(dfirst,nmax,"fix:dfirst");
+    memory->create(ednum,nmax,"fix:ednum");
+    memory->create(edfirst,nmax,"fix:edfirst");
+    memory->create(enclosing_radius,nmax,"fix:enclosing_radius");
+    memory->create(rounded_radius,nmax,"fix:rounded_radius");
+  }
+
+  ndiscrete = nedge = 0;
+  for (i = 0; i < nlocal; i++) 
+    dnum[i] = ednum[i] = 0;
+
+  for (i = 0; i < nlocal; i++) {
+    if (mask[i] & groupbit) {
+
+      if (body[i] < 0) continue;
+
+      dx = dy = dz = 0.0;
+      side = FAR;
+      if (wallstyle == XPLANE) {
+        del1 = x[i][0] - wlo;
+        del2 = whi - x[i][0];
+        if (del1 < del2) {
+          dx = del1;
+          wall_pos = wlo;
+          side = XLO;
+        } else {
+          dx = -del2;
+          wall_pos = whi;
+          side = XHI;
+        }
+      } else if (wallstyle == YPLANE) {
+        del1 = x[i][1] - wlo;
+        del2 = whi - x[i][1];
+        if (del1 < del2) {
+          dy = del1;
+          wall_pos = wlo;
+          side = YLO;
+        } else {
+          dy = -del2;
+          wall_pos = whi;
+          side = YHI;
+        }
+      } else if (wallstyle == ZCYLINDER) {
+        delxy = sqrt(x[i][0]*x[i][0] + x[i][1]*x[i][1]);
+        delr = cylradius - delxy;
+        if (delr > eradi) 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]) continue;
+
+      double r = sqrt(rsq);
+      double rsqinv = 1.0 / rsq;
+
+      if (dnum[i] == 0) body2space(i);
+      npi = dnum[i];
+      ifirst = dfirst[i];
+      nei = ednum[i];
+      iefirst = edfirst[i];
+      eradi = enclosing_radius[i];
+      rradi = rounded_radius[i];
+
+      // reset vertex and edge forces
+
+      for (ni = 0; ni < npi; ni++) {
+        discrete[ifirst+ni][3] = 0;
+        discrete[ifirst+ni][4] = 0;
+        discrete[ifirst+ni][5] = 0;
+      }
+
+      for (ni = 0; ni < nei; ni++) {
+        edge[iefirst+ni][2] = 0;
+        edge[iefirst+ni][3] = 0;
+        edge[iefirst+ni][4] = 0;
+      }
+
+      int interact, num_contacts, done;
+      double delta_a, delta_ua, j_a;
+      Contact contact_list[MAX_CONTACTS];
+
+      num_contacts = 0;
+      facc[0] = facc[1] = facc[2] = 0;
+      interact = vertex_against_wall(i, wall_pos, x, f, torque, side,
+                                     contact_list, num_contacts, facc);
+
+      if (num_contacts >= 2) {
+
+        // find the first two distinct contacts
+
+        done = 0;
+        for (int m = 0; m < num_contacts-1; m++) {
+          for (int n = m+1; n < num_contacts; n++) {
+            delta_a = contact_separation(contact_list[m], contact_list[n]);
+            if (delta_a > 0) {
+              delta_ua = 1.0;
+              j_a = delta_a / (EFF_CONTACTS * delta_ua);
+              if (j_a < 1.0) j_a = 1.0;
+
+              // scale the force at both contacts
+
+              contact_forces(contact_list[m], j_a, x, v, angmom, f, torque,
+                             vwall, facc);
+              contact_forces(contact_list[n], j_a, x, v, angmom, f, torque,
+                             vwall, facc);
+              done = 1;
+              break;
+            }
+          }
+          if (done == 1) break;
+        }
+
+      } else if (num_contacts == 1) {
+
+        // if there's only one contact, it should be handled here
+        // since forces/torques have not been accumulated from vertex2wall()
+
+        contact_forces(contact_list[0], 1.0, x, v, angmom, f, torque,
+                       vwall, facc);
+      }
+    } // group bit
+  }
+}
+
+/* ---------------------------------------------------------------------- */
+
+void FixWallBodyPolygon::reset_dt()
+{
+  dt = update->dt;
+}
+
+/* ----------------------------------------------------------------------
+   convert N sub-particles in body I to space frame using current quaternion
+   store sub-particle space-frame displacements from COM in discrete list
+------------------------------------------------------------------------- */
+
+void FixWallBodyPolygon::body2space(int i)
+{
+  int ibonus = atom->body[i];
+  AtomVecBody::Bonus *bonus = &avec->bonus[ibonus];
+  int nsub = bptr->nsub(bonus);
+  double *coords = bptr->coords(bonus);
+  int body_num_edges = bptr->nedges(bonus);
+  double* vertices = bptr->edges(bonus);
+  double eradius = bptr->enclosing_radius(bonus);
+  double rradius = bptr->rounded_radius(bonus);
+
+  // get the number of sub-particles (vertices)
+  // and the index of the first vertex of my body in the list
+
+  dnum[i] = nsub;
+  dfirst[i] = ndiscrete;
+
+  // grow the vertex list if necessary
+  // the first 3 columns are for coords, the last 3 for forces
+
+  if (ndiscrete + nsub > dmax) {
+    dmax += DELTA;
+    memory->grow(discrete,dmax,6,"fix:discrete");
+  }
+
+  double p[3][3];
+  MathExtra::quat_to_mat(bonus->quat,p);
+
+  for (int m = 0; m < nsub; m++) {
+    MathExtra::matvec(p,&coords[3*m],discrete[ndiscrete]);
+    discrete[ndiscrete][3] = 0;
+    discrete[ndiscrete][4] = 0;
+    discrete[ndiscrete][5] = 0;
+    ndiscrete++;
+  }
+
+  // get the number of edges (vertices)
+  // and the index of the first edge of my body in the list
+
+  ednum[i] = body_num_edges;
+  edfirst[i] = nedge;
+
+  // grow the edge list if necessary
+  // the first 2 columns are for vertex indices within body,
+  // the last 3 for forces
+
+  if (nedge + body_num_edges > edmax) {
+    edmax += DELTA;
+    memory->grow(edge,edmax,5,"fix:edge");
+  }
+
+  for (int m = 0; m < body_num_edges; m++) {
+    edge[nedge][0] = static_cast<int>(vertices[2*m+0]);
+    edge[nedge][1] = static_cast<int>(vertices[2*m+1]);
+    edge[nedge][2] = 0;
+    edge[nedge][3] = 0;
+    edge[nedge][4] = 0;
+    nedge++;
+  }
+
+  enclosing_radius[i] = eradius;
+  rounded_radius[i] = rradius;
+}
+
+/* ----------------------------------------------------------------------
+   Determine the interaction mode between i's vertices against the wall
+
+   i = atom i (body i)
+   x      = atoms' coordinates
+   f      = atoms' forces
+   torque = atoms' torques
+   Return:
+     contact_list = list of contacts between i and the wall
+     num_contacts = number of contacts between i's vertices and the wall
+     interact = 0 no interaction with the wall
+                1 there's at least one vertex of i interacts
+                  with the wall
+---------------------------------------------------------------------- */
+
+int FixWallBodyPolygon::vertex_against_wall(int i, double wall_pos,
+                double** x, double** f, double** torque, int side,
+                Contact* contact_list, int &num_contacts, double* facc)
+{
+  int ni, npi, ifirst, interact;
+  double xpi[3], xpj[3], dist, eradi, rradi;
+  double fx, fy, fz, rx, ry, rz;
+  int nlocal = atom->nlocal;
+
+  npi = dnum[i];
+  ifirst = dfirst[i];
+  eradi = enclosing_radius[i];
+  rradi = rounded_radius[i];
+
+  interact = 0;
+
+  // loop through body i's vertices
+
+  for (ni = 0; ni < npi; ni++) {
+
+    // convert body-fixed coordinates to space-fixed, xi
+
+    xpi[0] = x[i][0] + discrete[ifirst+ni][0];
+    xpi[1] = x[i][1] + discrete[ifirst+ni][1];
+    xpi[2] = x[i][2] + discrete[ifirst+ni][2];
+
+    int mode, contact, p2vertex;
+    double d, R, hi[3], t, delx, dely, delz, fpair, shift;
+    double xj[3], rij;
+
+    // compute the distance from the vertex xpi to the wall
+
+    mode = compute_distance_to_wall(xpi, rradi, wall_pos, side,
+                                    d, hi, contact);
+
+    if (mode == INVALID || mode == NONE) continue;
+
+    if (mode == VERTEX) {
+
+      interact = 1;
+
+      // vertex i interacts with the wall
+
+      delx = xpi[0] - hi[0];
+      dely = xpi[1] - hi[1];
+      delz = xpi[2] - hi[2];
+
+      // R = surface separation = d shifted by the rounded radius
+      // R = d - p1.rounded_radius;
+      // note: the force is defined for R, not for d
+      // R >  0: no interaction
+      // R <= 0: deformation between vertex i and the wall
+
+      rij = sqrt(delx*delx + dely*dely + delz*delz);
+      R = rij - rradi;
+
+      // the normal frictional term -c_n * vn will be added later
+
+      if (R <= 0) {           // deformation occurs
+        fpair = -kn * R;
+      } else fpair = 0.0;
+
+      fx = delx*fpair/rij;
+      fy = dely*fpair/rij;
+      fz = delz*fpair/rij;
+
+      #ifdef _POLYGON_DEBUG
+      printf("  Interaction between vertex %d of %d and wall:", ni);
+      printf("    mode = %d; contact = %d; d = %f; rij = %f\n",
+             mode, contact, d, rij);
+      printf("    R = %f\n", R);
+      printf("    fpair = %f\n", fpair);
+      #endif
+
+      if (contact == 1) {
+
+        // vertex ni of body i contacts with edge nj of body j
+
+        contact_list[num_contacts].ibody = i;
+        contact_list[num_contacts].jbody = -1;
+        contact_list[num_contacts].vertex = ni;
+        contact_list[num_contacts].edge = -1;
+        contact_list[num_contacts].xv[0] = xpi[0];
+        contact_list[num_contacts].xv[1] = xpi[1];
+        contact_list[num_contacts].xv[2] = xpi[2];
+        contact_list[num_contacts].xe[0] = hi[0];
+        contact_list[num_contacts].xe[1] = hi[1];
+        contact_list[num_contacts].xe[2] = hi[2];
+        contact_list[num_contacts].separation = R;
+        num_contacts++;
+
+        // store forces to vertex ni to be rescaled later,
+        // if there are 2 contacts
+
+        discrete[ifirst+ni][3] = fx;
+        discrete[ifirst+ni][4] = fy;
+        discrete[ifirst+ni][5] = fz;
+
+        #ifdef _POLYGON_DEBUG
+        printf("  Stored forces at vertex and edge for accumulating later.\n");
+        #endif
+
+      } else { // no contact
+
+        // accumulate force and torque to the body directly
+
+        f[i][0] += fx;
+        f[i][1] += fy;
+        f[i][2] += fz;
+        sum_torque(x[i], xpi, fx, fy, fz, torque[i]);
+
+      } // end if contact
+
+    } // end if mode
+
+  } // end for looping through the vertices of body i
+
+  return interact;
+}
+
+/* -------------------------------------------------------------------------
+  Compute the distance between a vertex to the wall
+  another body
+  Input:
+    x0         = coordinate of the tested vertex
+    rradi      = rounded radius of the vertex
+    wall_pos   = position of the wall
+  Output:
+    d          = Distance from a point x0 to an wall
+    hi         = coordinates of the projection of x0 on the wall
+  contact      = 0 no contact between the queried vertex and the wall
+                 1 contact detected
+  return NONE    if there is no interaction
+         EDGE    if the tested vertex interacts with the wall
+------------------------------------------------------------------------- */
+
+int FixWallBodyPolygon::compute_distance_to_wall(double* x0, double rradi,
+          double wall_pos, int side, double &d, double hi[3], int &contact)
+{
+  int mode;
+  double delxy;
+
+  // h0 = position of the projection of x0 on the wall
+  if (wallstyle == XPLANE) {
+    hi[0] = wall_pos;
+    hi[1] = x0[1];
+    hi[2] = x0[2];
+  } else if (wallstyle == YPLANE) {
+    hi[0] = x0[0];
+    hi[1] = wall_pos;
+    hi[2] = x0[2];
+  } else if (wallstyle == ZCYLINDER) {
+    delxy = sqrt(x0[0]*x0[0] + x0[1]*x0[1]);
+    hi[0] = x0[0]*cylradius/delxy;
+    hi[1] = x0[1]*cylradius/delxy;
+    hi[2] = x0[2];
+  }
+
+  // distance from x0 to the wall = distance from x0 to hi
+
+  distance(hi, x0, d);
+
+  // determine the interaction mode
+
+  if (d < rradi) {
+    mode = VERTEX;
+    contact = 1;
+  } else {
+    if (side == XLO) {
+      if (x0[0] < wall_pos) mode = VERTEX;
+      else mode = NONE;
+    } else if (side == XHI) {
+      if (x0[0] > wall_pos) mode = VERTEX;
+      else mode = NONE;
+    } else if (side == YLO) {
+      if (x0[1] < wall_pos) mode = VERTEX;
+      else mode = NONE;
+    } else if (side == YHI) {
+      if (x0[1] > wall_pos) mode = VERTEX;
+      else mode = NONE;
+    }
+  }
+
+  if (mode == NONE) contact = 0;
+  else contact = 1;
+
+  return mode;
+}
+
+/* ----------------------------------------------------------------------
+  Compute the contact forces between two bodies
+  modify the force stored at the vertex and edge in contact by j_a
+  sum forces and torque to the corresponding bodies
+    fn = normal friction component
+    ft = tangential friction component (-c_t * vrt)
+------------------------------------------------------------------------- */
+
+void FixWallBodyPolygon::contact_forces(Contact& contact, double j_a,
+                      double** x, double** v, double** angmom, double** f,
+                      double** torque, double* vwall, double* facc)
+{
+  int ibody,ibonus,ifirst, jefirst, ni;
+  double fx,fy,fz,delx,dely,delz,rsq,rsqinv;
+  double vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3;
+  double fn[3],ft[3],vi[3];
+  double *quat, *inertia;
+  AtomVecBody::Bonus *bonus;
+
+  ibody = contact.ibody;
+  
+  // compute the velocity of the vertex in the space-fixed frame
+
+  ibonus = atom->body[ibody];
+  bonus = &avec->bonus[ibonus];
+  quat = bonus->quat;
+  inertia = bonus->inertia;
+  total_velocity(contact.xv, x[ibody], v[ibody], angmom[ibody],
+                 inertia, quat, vi);
+
+  // vector pointing from the vertex to the point on the wall
+
+  delx = contact.xv[0] - contact.xe[0];
+  dely = contact.xv[1] - contact.xe[1];
+  delz = contact.xv[2] - contact.xe[2];
+  rsq = delx*delx + dely*dely + delz*delz;
+  rsqinv = 1.0/rsq;
+
+  // relative translational velocity
+
+  vr1 = vi[0] - vwall[0];
+  vr2 = vi[1] - vwall[1];
+  vr3 = vi[2] - vwall[2];
+
+  // normal component
+
+  vnnr = vr1*delx + vr2*dely + vr3*delz;
+  vn1 = delx*vnnr * rsqinv;
+  vn2 = dely*vnnr * rsqinv;
+  vn3 = delz*vnnr * rsqinv;
+
+  // tangential component
+
+  vt1 = vr1 - vn1;
+  vt2 = vr2 - vn2;
+  vt3 = vr3 - vn3;
+
+  // normal friction term at contact
+
+  fn[0] = -c_n * vn1;
+  fn[1] = -c_n * vn2;
+  fn[2] = -c_n * vn3;
+
+  // tangential friction term at contact
+  // excluding the tangential deformation term for now
+
+  ft[0] = -c_t * vt1;
+  ft[1] = -c_t * vt2;
+  ft[2] = -c_t * vt3;
+
+  // only the cohesive force is scaled by j_a
+
+  ifirst = dfirst[ibody];
+  ni = contact.vertex;
+
+  fx = discrete[ifirst+ni][3] * j_a + fn[0] + ft[0];
+  fy = discrete[ifirst+ni][4] * j_a + fn[1] + ft[1];
+  fz = discrete[ifirst+ni][5] * j_a + fn[2] + ft[2];
+  f[ibody][0] += fx;
+  f[ibody][1] += fy;
+  f[ibody][2] += fz;
+  sum_torque(x[ibody], contact.xv, fx, fy, fz, torque[ibody]);
+
+  // accumulate forces to the vertex only
+
+  facc[0] += fx; facc[1] += fy; facc[2] += fz;
+
+  #ifdef _POLYGON_DEBUG
+  printf("From contact forces: vertex fx %f fy %f fz %f\n"
+         "      torque body %d: %f %f %f\n",
+         discrete[ifirst+ni][3], discrete[ifirst+ni][4], discrete[ifirst+ni][5],
+         atom->tag[ibody],torque[ibody][0],torque[ibody][1],torque[ibody][2]);
+  #endif
+}
+
+/* ----------------------------------------------------------------------
+  Determine the length of the contact segment, i.e. the separation between
+  2 contacts
+------------------------------------------------------------------------- */
+
+double FixWallBodyPolygon::contact_separation(const Contact& c1,
+                                              const Contact& c2)
+{
+  double x1 = c1.xv[0];
+  double y1 = c1.xv[1];
+  double x2 = c1.xe[0];
+  double y2 = c1.xe[1];
+  double x3 = c2.xv[0];
+  double y3 = c2.xv[1];
+
+  double delta_a = 0.0;
+  if (fabs(x2 - x1) > EPSILON) {
+    double A = (y2 - y1) / (x2 - x1);
+    delta_a = fabs(y1 - A * x1 - y3 + A * x3) / sqrt(1 + A * A);
+  } else {
+    delta_a = fabs(x1 - x3);
+  }
+
+  return delta_a;
+}
+
+/* ----------------------------------------------------------------------
+  Accumulate torque to body from the force f=(fx,fy,fz) acting at point x
+------------------------------------------------------------------------- */
+
+void FixWallBodyPolygon::sum_torque(double* xm, double *x, double fx,
+                                    double fy, double fz, double* torque)
+{
+  double rx = x[0] - xm[0];
+  double ry = x[1] - xm[1];
+  double rz = x[2] - xm[2];
+  double tx = ry * fz - rz * fy;
+  double ty = rz * fx - rx * fz;
+  double tz = rx * fy - ry * fx;
+  torque[0] += tx;
+  torque[1] += ty;
+  torque[2] += tz;
+}
+
+/* ----------------------------------------------------------------------
+  Calculate the total velocity of a point (vertex, a point on an edge):
+    vi = vcm + omega ^ (p - xcm)
+------------------------------------------------------------------------- */
+
+void FixWallBodyPolygon::total_velocity(double* p, double *xcm,
+                              double* vcm, double *angmom, double *inertia,
+                              double *quat, double* vi)
+{
+  double r[3],omega[3],ex_space[3],ey_space[3],ez_space[3];
+  r[0] = p[0] - xcm[0];
+  r[1] = p[1] - xcm[1];
+  r[2] = p[2] - xcm[2];
+  MathExtra::q_to_exyz(quat,ex_space,ey_space,ez_space);
+  MathExtra::angmom_to_omega(angmom,ex_space,ey_space,ez_space,
+                             inertia,omega);
+  vi[0] = omega[1]*r[2] - omega[2]*r[1] + vcm[0];
+  vi[1] = omega[2]*r[0] - omega[0]*r[2] + vcm[1];
+  vi[2] = omega[0]*r[1] - omega[1]*r[0] + vcm[2];
+}
+
+/* ---------------------------------------------------------------------- */
+
+void FixWallBodyPolygon::distance(const double* x2, const double* x1,
+                                  double& r) {
+  r = sqrt((x2[0] - x1[0]) * (x2[0] - x1[0])
+    + (x2[1] - x1[1]) * (x2[1] - x1[1])
+    + (x2[2] - x1[2]) * (x2[2] - x1[2]));
+}
+
diff --git a/src/BODY/fix_wall_body_polygon.h b/src/BODY/fix_wall_body_polygon.h
new file mode 100644
index 0000000000000000000000000000000000000000..3521c6c797afe4169a0dea4a01cc3839cced02f2
--- /dev/null
+++ b/src/BODY/fix_wall_body_polygon.h
@@ -0,0 +1,129 @@
+/* -*- c++ -*- ----------------------------------------------------------
+   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
+   http://lammps.sandia.gov, Sandia National Laboratories
+   Steve Plimpton, sjplimp@sandia.gov
+
+   Copyright (2003) Sandia Corporation.  Under the terms of Contract
+   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+   certain rights in this software.  This software is distributed under
+   the GNU General Public License.
+
+   See the README file in the top-level LAMMPS directory.
+------------------------------------------------------------------------- */
+
+#ifdef FIX_CLASS
+
+FixStyle(wall/body/polygon,FixWallBodyPolygon)
+
+#else
+
+#ifndef LMP_FIX_WALL_BODY_POLYGON_H
+#define LMP_FIX_WALL_BODY_POLYGON_H
+
+#include "fix.h"
+
+namespace LAMMPS_NS {
+
+class FixWallBodyPolygon : public Fix {
+ public:
+  FixWallBodyPolygon(class LAMMPS *, int, char **);
+  virtual ~FixWallBodyPolygon();
+  int setmask();
+  void init();
+  void setup(int);
+  virtual void post_force(int);
+  void reset_dt();
+
+  struct Contact {
+    int ibody, jbody; // body (i.e. atom) indices (not tags)
+    int vertex;       // vertex of the first polygon
+    int edge;         // edge of the second polygon
+    double xv[3];     // coordinates of the vertex
+    double xe[3];     // coordinates of the projection of the vertex on the edge
+    double separation;// separation at contact
+  };
+
+ protected:
+  int wallstyle,pairstyle,wiggle,axis;
+  double kn,c_n,c_t;
+  double lo,hi,cylradius;
+  double amplitude,period,omega;
+  double dt;
+  int time_origin;
+
+  class AtomVecBody *avec;
+  class BodyRoundedPolygon *bptr;
+
+  double **discrete;  // list of all sub-particles for all bodies
+  int ndiscrete;      // number of discretes in list
+  int dmax;           // allocated size of discrete list
+  int *dnum;          // number of discretes per line, 0 if uninit
+  int *dfirst;        // index of first discrete per each line
+  int nmax;           // allocated size of dnum,dfirst vectors
+
+  double **edge;      // list of all edge for all bodies
+  int nedge;          // number of edge in list
+  int edmax;          // allocated size of edge list
+  int *ednum;         // number of edges per line, 0 if uninit
+  int *edfirst;       // index of first edge per each line
+  int ednummax;       // allocated size of ednum,edfirst vectors
+
+  double *enclosing_radius; // enclosing radii for all bodies
+  double *rounded_radius;   // rounded radii for all bodies
+
+  void body2space(int);
+
+  int vertex_against_wall(int ibody, double wall_pos, double** x,
+                          double** f, double** torque, int side,
+                          Contact* contact_list, int &num_contacts,
+                          double* facc);
+
+  int compute_distance_to_wall(double* x0, double rradi, double wall_pos,
+                               int side, double &d, double hi[3], int &contact);
+  double contact_separation(const Contact& c1, const Contact& c2);
+  void contact_forces(Contact& contact, double j_a, double** x,
+                      double** v, double** angmom, double** f, double** torque,
+                      double* vwall, double* facc);
+  void sum_torque(double* xm, double *x, double fx,
+                  double fy, double fz, double* torque);
+  void total_velocity(double* p, double *xcm, double* vcm, double *angmom,
+                      double *inertia, double *quat, double* vi);
+  void distance(const double* x2, const double* x1, double& r);
+
+};
+
+}
+
+#endif
+#endif
+
+/* ERROR/WARNING messages:
+
+E: Illegal ... command
+
+Self-explanatory.  Check the input script syntax and compare to the
+documentation for the command.  You can use -echo screen as a
+command-line option when running LAMMPS to see the offending line.
+
+E: Fix wall/body/polygon requires atom style body rounded/polygon
+
+Self-explanatory.
+
+E: Cannot use wall in periodic dimension
+
+Self-explanatory.
+
+E: Cannot wiggle and shear fix wall/body/polygon
+
+Cannot specify both options at the same time.
+
+E: Invalid wiggle direction for fix wall/body/polygon
+
+Self-explanatory.
+
+E: Fix wall/body/polygon is incompatible with Pair style
+
+Must use a body pair style to define the parameters needed for
+this fix.
+
+*/
diff --git a/src/BODY/fix_wall_body_polyhedron.cpp b/src/BODY/fix_wall_body_polyhedron.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4806da9673ecbeaddd06a4d80bf564c3ef740858
--- /dev/null
+++ b/src/BODY/fix_wall_body_polyhedron.cpp
@@ -0,0 +1,944 @@
+/* ----------------------------------------------------------------------
+   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
+   http://lammps.sandia.gov, Sandia National Laboratories
+   Steve Plimpton, sjplimp@sandia.gov
+
+   Copyright (2003) Sandia Corporation.  Under the terms of Contract
+   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+   certain rights in this software.  This software is distributed under
+   the GNU General Public License.
+
+   See the README file in the top-level LAMMPS directory.
+------------------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------
+   Contributing authors: Trung Dac Nguyen (ndactrung@gmail.com)
+------------------------------------------------------------------------- */
+
+#include <cmath>
+#include <cstdlib>
+#include <cstring>
+#include "fix_wall_body_polyhedron.h"
+#include "atom.h"
+#include "atom_vec_body.h"
+#include "body_rounded_polyhedron.h"
+#include "domain.h"
+#include "update.h"
+#include "force.h"
+#include "pair.h"
+#include "modify.h"
+#include "respa.h"
+#include "math_const.h"
+#include "math_extra.h"
+#include "memory.h"
+#include "error.h"
+
+using namespace LAMMPS_NS;
+using namespace FixConst;
+using namespace MathConst;
+
+enum{XPLANE=0,YPLANE=1,ZPLANE};    // XYZ PLANE need to be 0,1,2
+enum{HOOKE,HOOKE_HISTORY};
+
+enum {INVALID=0,NONE=1,VERTEX=2};
+enum {FAR=0,XLO,XHI,YLO,YHI,ZLO,ZHI};
+
+//#define _POLYHEDRON_DEBUG
+#define DELTA 10000
+#define EPSILON 1e-2
+#define BIG 1.0e20
+#define MAX_CONTACTS 4  // maximum number of contacts for 2D models
+#define EFF_CONTACTS 2  // effective contacts for 2D models
+
+/* ---------------------------------------------------------------------- */
+
+FixWallBodyPolyhedron::FixWallBodyPolyhedron(LAMMPS *lmp, int narg, char **arg) :
+  Fix(lmp, narg, arg)
+{
+  if (narg < 7) error->all(FLERR,"Illegal fix wall/body/polyhedron command");
+
+  if (!atom->body_flag)
+    error->all(FLERR,"Fix wall/body/polyhedron requires atom style body/rounded/polyhedron");
+
+  restart_peratom = 1;
+  create_attribute = 1;
+
+  // wall/particle coefficients
+
+  kn = force->numeric(FLERR,arg[3]);
+
+  c_n = force->numeric(FLERR,arg[4]);
+  if (strcmp(arg[5],"NULL") == 0) c_t = 0.5 * c_n;
+  else c_t = force->numeric(FLERR,arg[5]);
+
+  if (kn < 0.0 || c_n < 0.0 || c_t < 0.0)
+    error->all(FLERR,"Illegal fix wall/body/polyhedron command");
+
+  // wallstyle args
+
+  int iarg = 6;
+  if (strcmp(arg[iarg],"xplane") == 0) {
+    if (narg < iarg+3) error->all(FLERR,"Illegal fix wall/body/polyhedron command");
+    wallstyle = XPLANE;
+    if (strcmp(arg[iarg+1],"NULL") == 0) lo = -BIG;
+    else lo = force->numeric(FLERR,arg[iarg+1]);
+    if (strcmp(arg[iarg+2],"NULL") == 0) hi = BIG;
+    else hi = force->numeric(FLERR,arg[iarg+2]);
+    iarg += 3;
+  } else if (strcmp(arg[iarg],"yplane") == 0) {
+    if (narg < iarg+3) error->all(FLERR,"Illegal fix wall/body/polyhedron command");
+    wallstyle = YPLANE;
+    if (strcmp(arg[iarg+1],"NULL") == 0) lo = -BIG;
+    else lo = force->numeric(FLERR,arg[iarg+1]);
+    if (strcmp(arg[iarg+2],"NULL") == 0) hi = BIG;
+    else hi = force->numeric(FLERR,arg[iarg+2]);
+    iarg += 3;
+  } else if (strcmp(arg[iarg],"zplane") == 0) {
+    if (narg < iarg+3) error->all(FLERR,"Illegal fix wall/body/polyhedron command");
+    wallstyle = ZPLANE;
+    if (strcmp(arg[iarg+1],"NULL") == 0) lo = -BIG;
+    else lo = force->numeric(FLERR,arg[iarg+1]);
+    if (strcmp(arg[iarg+2],"NULL") == 0) hi = BIG;
+    else hi = force->numeric(FLERR,arg[iarg+2]);
+    iarg += 3;
+  } 
+
+  // check for trailing keyword/values
+
+  wiggle = 0;
+
+  while (iarg < narg) {
+    if (strcmp(arg[iarg],"wiggle") == 0) {
+      if (iarg+4 > narg) error->all(FLERR,"Illegal fix wall/body/polyhedron 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(FLERR,"Illegal fix wall/body/polyhedron command");
+      amplitude = force->numeric(FLERR,arg[iarg+2]);
+      period = force->numeric(FLERR,arg[iarg+3]);
+      wiggle = 1;
+      iarg += 4;
+    } else error->all(FLERR,"Illegal fix wall/body/polyhedron command");
+  }
+
+  if (wallstyle == XPLANE && domain->xperiodic)
+    error->all(FLERR,"Cannot use wall in periodic dimension");
+  if (wallstyle == YPLANE && domain->yperiodic)
+    error->all(FLERR,"Cannot use wall in periodic dimension");
+  if (wallstyle == ZPLANE && domain->zperiodic)
+    error->all(FLERR,"Cannot use wall in periodic dimension");
+
+  // setup oscillations
+
+  if (wiggle) omega = 2.0*MY_PI / period;
+
+  time_origin = update->ntimestep;
+
+  dmax = nmax = 0;
+  discrete = NULL;
+  dnum = dfirst = NULL;
+
+  edmax = ednummax = 0;
+  edge = NULL;
+  ednum = edfirst = NULL;
+
+  facmax = facnummax = 0;
+  face = NULL;
+  facnum = facfirst = NULL;
+
+  enclosing_radius = NULL;
+  rounded_radius = NULL;
+}
+
+/* ---------------------------------------------------------------------- */
+
+FixWallBodyPolyhedron::~FixWallBodyPolyhedron()
+{
+  memory->destroy(discrete);
+  memory->destroy(dnum);
+  memory->destroy(dfirst);
+
+  memory->destroy(edge);
+  memory->destroy(ednum);
+  memory->destroy(edfirst);
+
+  memory->destroy(face);
+  memory->destroy(facnum);
+  memory->destroy(facfirst);
+
+  memory->destroy(enclosing_radius);
+  memory->destroy(rounded_radius);
+}
+
+/* ---------------------------------------------------------------------- */
+
+int FixWallBodyPolyhedron::setmask()
+{
+  int mask = 0;
+  mask |= POST_FORCE;
+  return mask;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void FixWallBodyPolyhedron::init()
+{
+  dt = update->dt;
+
+  avec = (AtomVecBody *) atom->style_match("body");
+  if (!avec)
+    error->all(FLERR,"Pair body/rounded/polyhedron requires atom style body");
+  if (strcmp(avec->bptr->style,"rounded/polyhedron") != 0)
+    error->all(FLERR,"Pair body/rounded/polyhedron requires body style rounded/polyhedron");
+  bptr = (BodyRoundedPolyhedron *) avec->bptr;
+
+  // set pairstyle from body/polyhedronular pair style
+
+  if (force->pair_match("body/rounded/polyhedron",1))
+    pairstyle = HOOKE;
+  else error->all(FLERR,"Fix wall/body/polyhedron is incompatible with Pair style");
+}
+
+/* ---------------------------------------------------------------------- */
+
+void FixWallBodyPolyhedron::setup(int vflag)
+{
+  if (strstr(update->integrate_style,"verlet"))
+    post_force(vflag);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void FixWallBodyPolyhedron::post_force(int vflag)
+{
+  double vwall[3],dx,dy,dz,del1,del2,delxy,delr,rsq,eradi,rradi,wall_pos;
+  int i,ni,npi,ifirst,nei,iefirst,nfi,iffirst,side;
+  double facc[3];
+
+  // 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;
+    if (wallstyle == axis) {
+      wlo = lo + amplitude - amplitude*cos(arg);
+      whi = hi + amplitude - amplitude*cos(arg);
+    }
+    vwall[axis] = amplitude*omega*sin(arg);
+  }
+
+  // loop over all my atoms
+  // rsq = distance from wall
+  // dx,dy,dz = signed distance from wall
+  // for rotating cylinder, reset vwall based on particle position
+  // 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
+
+  double **x = atom->x;
+  double **v = atom->v;
+  double **f = atom->f;
+  int *body = atom->body;
+  double *radius = atom->radius;
+  double **torque = atom->torque;
+  double **angmom = atom->angmom;
+  int *mask = atom->mask;
+  int nlocal = atom->nlocal;
+
+  // grow the per-atom lists if necessary and initialize
+
+  if (atom->nmax > nmax) {
+    memory->destroy(dnum);
+    memory->destroy(dfirst);
+    memory->destroy(ednum);
+    memory->destroy(edfirst);
+    memory->destroy(facnum);
+    memory->destroy(facfirst);
+    memory->destroy(enclosing_radius);
+    memory->destroy(rounded_radius);
+    nmax = atom->nmax;
+    memory->create(dnum,nmax,"fix:dnum");
+    memory->create(dfirst,nmax,"fix:dfirst");
+    memory->create(ednum,nmax,"fix:ednum");
+    memory->create(edfirst,nmax,"fix:edfirst");
+    memory->create(facnum,nmax,"fix:facnum");
+    memory->create(facfirst,nmax,"fix:facfirst");
+    memory->create(enclosing_radius,nmax,"fix:enclosing_radius");
+    memory->create(rounded_radius,nmax,"fix:rounded_radius");
+  }
+
+  ndiscrete = nedge = nface = 0;
+  for (i = 0; i < nlocal; i++) 
+    dnum[i] = ednum[i] = facnum[i] = 0;
+
+  for (i = 0; i < nlocal; i++) {
+    if (mask[i] & groupbit) {
+
+      if (body[i] < 0) continue;
+
+      dx = dy = dz = 0.0;
+      side = FAR;
+      if (wallstyle == XPLANE) {
+        del1 = x[i][0] - wlo;
+        del2 = whi - x[i][0];
+        if (del1 < del2) {
+          dx = del1;
+          wall_pos = wlo;
+          side = XLO;
+        } else {
+          dx = -del2;
+          wall_pos = whi;
+          side = XHI;
+        }
+      } else if (wallstyle == YPLANE) {
+        del1 = x[i][1] - wlo;
+        del2 = whi - x[i][1];
+        if (del1 < del2) {
+          dy = del1;
+          wall_pos = wlo;
+          side = YLO;
+        } else {
+          dy = -del2;
+          wall_pos = whi;
+          side = YHI;
+        }
+      } else if (wallstyle == ZPLANE) {
+        del1 = x[i][2] - wlo;
+        del2 = whi - x[i][2];
+        if (del1 < del2) {
+          dy = del1;
+          wall_pos = wlo;
+          side = ZLO;
+        } else {
+          dy = -del2;
+          wall_pos = whi;
+          side = ZHI;
+        }
+      } 
+
+      rsq = dx*dx + dy*dy + dz*dz;
+      if (rsq > radius[i]*radius[i]) continue;
+
+      double r = sqrt(rsq);
+      double rsqinv = 1.0 / rsq;
+
+      if (dnum[i] == 0) body2space(i);
+      npi = dnum[i];
+      ifirst = dfirst[i];
+      nei = ednum[i];
+      iefirst = edfirst[i];
+      nfi = facnum[i];
+      iffirst = facfirst[i];
+      eradi = enclosing_radius[i];
+      rradi = rounded_radius[i];
+
+      if (npi == 1) {
+        sphere_against_wall(i, wall_pos, side, vwall, x, v, f, angmom, torque);
+        continue;
+      }
+
+      // reset vertex and edge forces
+
+      for (ni = 0; ni < npi; ni++) {
+        discrete[ifirst+ni][3] = 0;
+        discrete[ifirst+ni][4] = 0;
+        discrete[ifirst+ni][5] = 0;
+        discrete[ifirst+ni][6] = 0;
+      }
+
+      for (ni = 0; ni < nei; ni++) {
+        edge[iefirst+ni][2] = 0;
+        edge[iefirst+ni][3] = 0;
+        edge[iefirst+ni][4] = 0;
+        edge[iefirst+ni][5] = 0;
+      }
+
+      int interact, num_contacts, done;
+      double delta_a, delta_ua, j_a;
+      Contact contact_list[MAX_CONTACTS];
+
+      num_contacts = 0;
+      facc[0] = facc[1] = facc[2] = 0;
+      interact = edge_against_wall(i, wall_pos, side, vwall, x, f, torque,
+                                   contact_list, num_contacts, facc);
+
+    } // group bit
+  }
+}
+
+/* ---------------------------------------------------------------------- */
+
+void FixWallBodyPolyhedron::reset_dt()
+{
+  dt = update->dt;
+}
+
+/* ----------------------------------------------------------------------
+   convert N sub-particles in body I to space frame using current quaternion
+   store sub-particle space-frame displacements from COM in discrete list
+------------------------------------------------------------------------- */
+
+void FixWallBodyPolyhedron::body2space(int i)
+{
+  int ibonus = atom->body[i];
+  AtomVecBody::Bonus *bonus = &avec->bonus[ibonus];
+  int nsub = bptr->nsub(bonus);
+  double *coords = bptr->coords(bonus);
+  int body_num_edges = bptr->nedges(bonus);
+  double* edge_ends = bptr->edges(bonus);
+  int body_num_faces = bptr->nfaces(bonus);
+  double* face_pts = bptr->faces(bonus);
+  double eradius = bptr->enclosing_radius(bonus);
+  double rradius = bptr->rounded_radius(bonus);
+
+  // get the number of sub-particles (vertices)
+  // and the index of the first vertex of my body in the list
+
+  dnum[i] = nsub;
+  dfirst[i] = ndiscrete;
+
+  // grow the vertex list if necessary
+  // the first 3 columns are for coords, the last 3 for forces
+
+  if (ndiscrete + nsub > dmax) {
+    dmax += DELTA;
+    memory->grow(discrete,dmax,7,"fix:discrete");
+  }
+
+  double p[3][3];
+  MathExtra::quat_to_mat(bonus->quat,p);
+
+  for (int m = 0; m < nsub; m++) {
+    MathExtra::matvec(p,&coords[3*m],discrete[ndiscrete]);
+    discrete[ndiscrete][3] = 0;
+    discrete[ndiscrete][4] = 0;
+    discrete[ndiscrete][5] = 0;
+    discrete[ndiscrete][6] = 0;
+    ndiscrete++;
+  }
+
+  // get the number of edges (vertices)
+  // and the index of the first edge of my body in the list
+
+  ednum[i] = body_num_edges;
+  edfirst[i] = nedge;
+
+  // grow the edge list if necessary
+  // the first 2 columns are for vertex indices within body,
+  // the last 3 for forces
+
+  if (nedge + body_num_edges > edmax) {
+    edmax += DELTA;
+    memory->grow(edge,edmax,6,"fix:edge");
+  }
+
+  for (int m = 0; m < body_num_edges; m++) {
+    edge[nedge][0] = static_cast<int>(edge_ends[2*m+0]);
+    edge[nedge][1] = static_cast<int>(edge_ends[2*m+1]);
+    edge[nedge][2] = 0;
+    edge[nedge][3] = 0;
+    edge[nedge][4] = 0;
+    edge[nedge][5] = 0;
+    nedge++;
+  }
+
+  // get the number of faces and the index of the first face
+
+  facnum[i] = body_num_faces;
+  facfirst[i] = nface;
+
+  // grow the face list if necessary
+  // the first 3 columns are for vertex indices within body, the last 3 for forces
+
+  if (nface + body_num_faces > facmax) {
+    facmax += DELTA;
+    memory->grow(face,facmax,6,"pair:face");
+  }
+
+  for (int m = 0; m < body_num_faces; m++) {
+    face[nface][0] = static_cast<int>(face_pts[3*m+0]);
+    face[nface][1] = static_cast<int>(face_pts[3*m+1]);
+    face[nface][2] = static_cast<int>(face_pts[3*m+2]);
+    face[nface][3] = 0;
+    face[nface][4] = 0;
+    face[nface][5] = 0;
+    nface++;
+  }
+
+  enclosing_radius[i] = eradius;
+  rounded_radius[i] = rradius;
+}
+
+/* ----------------------------------------------------------------------
+   Determine the interaction mode between a sphere against the wall
+
+   i = atom i (body i)
+   x      = atoms' coordinates
+   f      = atoms' forces
+   torque = atoms' torques
+---------------------------------------------------------------------- */
+
+int FixWallBodyPolyhedron::sphere_against_wall(int i, double wall_pos,
+     int side, double* vwall, double** x, double** v, double** f,
+     double** angmom, double** torque)
+{
+  int mode;
+  double rradi,hi[3],d,delx,dely,delz,R,fpair,fx,fy,fz;
+
+  rradi = rounded_radius[i];
+  mode = NONE;
+
+  if (wallstyle == XPLANE) {
+    hi[0] = wall_pos;
+    hi[1] = x[i][1];
+    hi[2] = x[i][2];
+  } else if (wallstyle == YPLANE) {
+    hi[0] = x[i][0];
+    hi[1] = wall_pos;
+    hi[2] = x[i][2];
+  } else if (wallstyle == ZPLANE) {
+    hi[0] = x[i][0];
+    hi[1] = x[i][1];
+    hi[2] = wall_pos;
+  } 
+
+  distance(hi, x[i], d);
+
+  if (d <= rradi) {
+    delx = x[i][0] - hi[0];
+    dely = x[i][1] - hi[1];
+    delz = x[i][2] - hi[2];
+    R = d - rradi;
+
+    fpair = -kn * R;
+
+    fx = delx*fpair/d;
+    fy = dely*fpair/d;
+    fz = delz*fpair/d;
+
+    contact_forces(i, 1.0, x[i], hi, delx, dely, delz,
+                   fx, fy, fz, x, v, angmom, f, torque, vwall);
+    mode = VERTEX;
+  }
+
+  return mode;
+}
+
+/* ----------------------------------------------------------------------
+   Determine the interaction mode between i's vertices against the wall
+
+   i = atom i (body i)
+   x      = atoms' coordinates
+   f      = atoms' forces
+   torque = atoms' torques
+   Output:
+     contact_list = list of contacts between i and the wall
+     num_contacts = number of contacts between i's vertices and the wall
+   Return: 
+     number of contacts of the edge to the wall (0, 1 or 2)
+---------------------------------------------------------------------- */
+
+int FixWallBodyPolyhedron::edge_against_wall(int i, double wall_pos,
+     int side, double* vwall, double** x, double** f, double** torque,
+     Contact* contact_list, int &num_contacts, double* facc)
+{
+  int ni, nei, mode, contact;
+  double rradi;
+  int nlocal = atom->nlocal;
+
+  nei = ednum[i];
+  rradi = rounded_radius[i];
+
+  contact = 0;
+
+  // loop through body i's edges
+
+  for (ni = 0; ni < nei; ni++)
+    mode = compute_distance_to_wall(i, ni, x[i], rradi, wall_pos, side, vwall,
+                                    contact);
+
+  return contact;
+}
+
+/* -------------------------------------------------------------------------
+  Compute the distance between a vertex to the wall
+  another body
+  Input:
+    x0         = coordinate of the tested vertex
+    rradi      = rounded radius of the vertex
+    wall_pos   = position of the wall
+  Output:
+    d          = Distance from a point x0 to an wall
+    hi         = coordinates of the projection of x0 on the wall
+  contact      = 0 no contact between the queried vertex and the wall
+                 1 contact detected
+  return NONE    if there is no interaction
+         VERTEX  if the tested vertex interacts with the wall
+------------------------------------------------------------------------- */
+
+int FixWallBodyPolyhedron::compute_distance_to_wall(int ibody, int edge_index,
+                        double *xmi, double rounded_radius_i, double wall_pos, 
+                        int side, double* vwall, int &contact)
+{
+  int mode,ifirst,iefirst,npi1,npi2;
+  double d1,d2,xpi1[3],xpi2[3],hi[3];
+  double fx,fy,fz,fpair,delx,dely,delz,R;
+
+  double** x = atom->x;
+  double** v = atom->v;
+  double** f = atom->f;
+  double** torque = atom->torque;
+  double** angmom = atom->angmom;
+
+  // two ends of the edge from body i
+
+  ifirst = dfirst[ibody];
+  iefirst = edfirst[ibody];
+  npi1 = static_cast<int>(edge[iefirst+edge_index][0]);
+  npi2 = static_cast<int>(edge[iefirst+edge_index][1]);
+
+  xpi1[0] = xmi[0] + discrete[ifirst+npi1][0];
+  xpi1[1] = xmi[1] + discrete[ifirst+npi1][1];
+  xpi1[2] = xmi[2] + discrete[ifirst+npi1][2];
+
+  xpi2[0] = xmi[0] + discrete[ifirst+npi2][0];
+  xpi2[1] = xmi[1] + discrete[ifirst+npi2][1];
+  xpi2[2] = xmi[2] + discrete[ifirst+npi2][2];
+
+  // determine the intersection of the edge to the wall
+
+  mode = NONE;
+  double j_a = 1.0;
+
+  if (wallstyle == XPLANE) {
+    hi[0] = wall_pos;
+    hi[1] = xpi1[1];
+    hi[2] = xpi1[2];
+  } else if (wallstyle == YPLANE) {
+    hi[0] = xpi1[0];
+    hi[1] = wall_pos;
+    hi[2] = xpi1[2];
+  } else if (wallstyle == ZPLANE) {
+    hi[0] = xpi1[0];
+    hi[1] = xpi1[1];
+    hi[2] = wall_pos;
+  } 
+
+  distance(hi, xpi1, d1);
+
+  if (d1 <= rounded_radius_i && static_cast<int>(discrete[ifirst+npi1][6]) == 0) {
+    delx = xpi1[0] - hi[0];
+    dely = xpi1[1] - hi[1];
+    delz = xpi1[2] - hi[2];
+    R = d1 - rounded_radius_i;
+
+    fpair = -kn * R;
+
+    fx = delx*fpair/d1;
+    fy = dely*fpair/d1;
+    fz = delz*fpair/d1;
+
+    contact_forces(ibody, j_a, xpi1, hi, delx, dely, delz,
+                   fx, fy, fz, x, v, angmom, f, torque, vwall);
+    discrete[ifirst+npi1][6] = 1;
+    contact++;
+    mode = VERTEX;
+  }
+
+  if (wallstyle == XPLANE) {
+    hi[0] = wall_pos;
+    hi[1] = xpi2[1];
+    hi[2] = xpi2[2];
+  } else if (wallstyle == YPLANE) {
+    hi[0] = xpi2[0];
+    hi[1] = wall_pos;
+    hi[2] = xpi2[2];
+  } else if (wallstyle == ZPLANE) {
+    hi[0] = xpi2[0];
+    hi[1] = xpi2[1];
+    hi[2] = wall_pos;
+  } 
+
+  distance(hi, xpi2, d2);
+
+  if (d2 <= rounded_radius_i && static_cast<int>(discrete[ifirst+npi2][6]) == 0) {
+    delx = xpi2[0] - hi[0];
+    dely = xpi2[1] - hi[1];
+    delz = xpi2[2] - hi[2];
+    R = d2 - rounded_radius_i;
+
+    fpair = -kn * R;
+
+    fx = delx*fpair/d2;
+    fy = dely*fpair/d2;
+    fz = delz*fpair/d2;
+
+    contact_forces(ibody, j_a, xpi2, hi, delx, dely, delz,
+                   fx, fy, fz, x, v, angmom, f, torque, vwall);
+    discrete[ifirst+npi2][6] = 1;
+    contact++;
+    mode = VERTEX;
+  }
+
+  return mode;
+}
+
+/* ----------------------------------------------------------------------
+  Compute contact forces between two bodies
+  modify the force stored at the vertex and edge in contact by j_a
+  sum forces and torque to the corresponding bodies
+  fn = normal friction component
+  ft = tangential friction component (-c_t * v_t)
+------------------------------------------------------------------------- */
+
+void FixWallBodyPolyhedron::contact_forces(int ibody,
+  double j_a, double *xi, double *xj, double delx, double dely, double delz,
+  double fx, double fy, double fz, double** x, double** v, double** angmom,
+  double** f, double** torque, double* vwall)
+{
+  int ibonus,jbonus;
+  double fxt,fyt,fzt,rsq,rsqinv;
+  double vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3;
+  double fn[3],ft[3],vi[3],vj[3];
+  double *quat, *inertia;
+  AtomVecBody::Bonus *bonus;
+
+  // compute the velocity of the vertex in the space-fixed frame
+
+  ibonus = atom->body[ibody];
+  bonus = &avec->bonus[ibonus];
+  quat = bonus->quat;
+  inertia = bonus->inertia;
+  total_velocity(xi, x[ibody], v[ibody], angmom[ibody],
+                 inertia, quat, vi);
+
+  // vector pointing from the contact point on ibody to the wall
+
+  rsq = delx*delx + dely*dely + delz*delz;
+  rsqinv = 1.0/rsq;
+
+  // relative translational velocity
+
+  vr1 = vi[0] - vwall[0];
+  vr2 = vi[1] - vwall[1];
+  vr3 = vi[2] - vwall[2];
+
+  // normal component
+
+  vnnr = vr1*delx + vr2*dely + vr3*delz;
+  vn1 = delx*vnnr * rsqinv;
+  vn2 = dely*vnnr * rsqinv;
+  vn3 = delz*vnnr * rsqinv;
+
+  // tangential component
+
+  vt1 = vr1 - vn1;
+  vt2 = vr2 - vn2;
+  vt3 = vr3 - vn3;
+
+  // normal friction term at contact
+
+  fn[0] = -c_n * vn1;
+  fn[1] = -c_n * vn2;
+  fn[2] = -c_n * vn3;
+
+  // tangential friction term at contact
+  // excluding the tangential deformation term for now
+
+  ft[0] = -c_t * vt1;
+  ft[1] = -c_t * vt2;
+  ft[2] = -c_t * vt3;
+
+  fxt = fx; fyt = fy; fzt = fz;
+  fx = fxt * j_a + fn[0] + ft[0];
+  fy = fyt * j_a + fn[1] + ft[1];
+  fz = fzt * j_a + fn[2] + ft[2];
+
+  f[ibody][0] += fx;
+  f[ibody][1] += fy;
+  f[ibody][2] += fz;
+  sum_torque(x[ibody], xi, fx, fy, fz, torque[ibody]);
+
+  #ifdef _POLYHEDRON_DEBUG
+  printf("From contact forces: vertex fx %f fy %f fz %f\n"
+         "      torque body %d: %f %f %f\n"
+         "      torque body %d: %f %f %f\n",
+         fxt, fyt, fzt,
+         atom->tag[ibody],torque[ibody][0],torque[ibody][1],torque[ibody][2],
+         atom->tag[jbody],torque[jbody][0],torque[jbody][1],torque[jbody][2]);
+  #endif
+}
+
+/* ----------------------------------------------------------------------
+  Compute the contact forces between two bodies
+  modify the force stored at the vertex and edge in contact by j_a
+  sum forces and torque to the corresponding bodies
+    fn = normal friction component
+    ft = tangential friction component (-c_t * vrt)
+------------------------------------------------------------------------- */
+
+void FixWallBodyPolyhedron::contact_forces(Contact& contact, double j_a,
+                      double** x, double** v, double** angmom, double** f,
+                      double** torque, double* vwall, double* facc)
+{
+  int ibody,ibonus,ifirst, jefirst, ni;
+  double fx,fy,fz,delx,dely,delz,rsq,rsqinv;
+  double vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3;
+  double fn[3],ft[3],vi[3];
+  double *quat, *inertia;
+  AtomVecBody::Bonus *bonus;
+
+  ibody = contact.ibody;
+  
+  // compute the velocity of the vertex in the space-fixed frame
+
+  ibonus = atom->body[ibody];
+  bonus = &avec->bonus[ibonus];
+  quat = bonus->quat;
+  inertia = bonus->inertia;
+  total_velocity(contact.xv, x[ibody], v[ibody], angmom[ibody],
+                 inertia, quat, vi);
+
+  // vector pointing from the vertex to the point on the wall
+
+  delx = contact.xv[0] - contact.xe[0];
+  dely = contact.xv[1] - contact.xe[1];
+  delz = contact.xv[2] - contact.xe[2];
+  rsq = delx*delx + dely*dely + delz*delz;
+  rsqinv = 1.0/rsq;
+
+  // relative translational velocity
+
+  vr1 = vi[0] - vwall[0];
+  vr2 = vi[1] - vwall[1];
+  vr3 = vi[2] - vwall[2];
+
+  // normal component
+
+  vnnr = vr1*delx + vr2*dely + vr3*delz;
+  vn1 = delx*vnnr * rsqinv;
+  vn2 = dely*vnnr * rsqinv;
+  vn3 = delz*vnnr * rsqinv;
+
+  // tangential component
+
+  vt1 = vr1 - vn1;
+  vt2 = vr2 - vn2;
+  vt3 = vr3 - vn3;
+
+  // normal friction term at contact
+
+  fn[0] = -c_n * vn1;
+  fn[1] = -c_n * vn2;
+  fn[2] = -c_n * vn3;
+
+  // tangential friction term at contact
+  // excluding the tangential deformation term for now
+
+  ft[0] = -c_t * vt1;
+  ft[1] = -c_t * vt2;
+  ft[2] = -c_t * vt3;
+
+  // only the cohesive force is scaled by j_a
+
+  ifirst = dfirst[ibody];
+  ni = contact.vertex;
+
+  fx = discrete[ifirst+ni][3] * j_a + fn[0] + ft[0];
+  fy = discrete[ifirst+ni][4] * j_a + fn[1] + ft[1];
+  fz = discrete[ifirst+ni][5] * j_a + fn[2] + ft[2];
+  f[ibody][0] += fx;
+  f[ibody][1] += fy;
+  f[ibody][2] += fz;
+  sum_torque(x[ibody], contact.xv, fx, fy, fz, torque[ibody]);
+
+  // accumulate forces to the vertex only
+
+  facc[0] += fx; facc[1] += fy; facc[2] += fz;
+
+  #ifdef _POLYHEDRON_DEBUG
+  printf("From contact forces: vertex fx %f fy %f fz %f\n"
+         "      torque body %d: %f %f %f\n",
+         discrete[ifirst+ni][3], discrete[ifirst+ni][4], discrete[ifirst+ni][5],
+         atom->tag[ibody],torque[ibody][0],torque[ibody][1],torque[ibody][2]);
+  #endif
+}
+
+/* ----------------------------------------------------------------------
+  Determine the length of the contact segment, i.e. the separation between
+  2 contacts
+------------------------------------------------------------------------- */
+
+double FixWallBodyPolyhedron::contact_separation(const Contact& c1,
+                                              const Contact& c2)
+{
+  double x1 = c1.xv[0];
+  double y1 = c1.xv[1];
+  double x2 = c1.xe[0];
+  double y2 = c1.xe[1];
+  double x3 = c2.xv[0];
+  double y3 = c2.xv[1];
+
+  double delta_a = 0.0;
+  if (fabs(x2 - x1) > EPSILON) {
+    double A = (y2 - y1) / (x2 - x1);
+    delta_a = fabs(y1 - A * x1 - y3 + A * x3) / sqrt(1 + A * A);
+  } else {
+    delta_a = fabs(x1 - x3);
+  }
+
+  return delta_a;
+}
+
+/* ----------------------------------------------------------------------
+  Accumulate torque to body from the force f=(fx,fy,fz) acting at point x
+------------------------------------------------------------------------- */
+
+void FixWallBodyPolyhedron::sum_torque(double* xm, double *x, double fx,
+                                    double fy, double fz, double* torque)
+{
+  double rx = x[0] - xm[0];
+  double ry = x[1] - xm[1];
+  double rz = x[2] - xm[2];
+  double tx = ry * fz - rz * fy;
+  double ty = rz * fx - rx * fz;
+  double tz = rx * fy - ry * fx;
+  torque[0] += tx;
+  torque[1] += ty;
+  torque[2] += tz;
+}
+
+/* ----------------------------------------------------------------------
+  Calculate the total velocity of a point (vertex, a point on an edge):
+    vi = vcm + omega ^ (p - xcm)
+------------------------------------------------------------------------- */
+
+void FixWallBodyPolyhedron::total_velocity(double* p, double *xcm,
+                              double* vcm, double *angmom, double *inertia,
+                              double *quat, double* vi)
+{
+  double r[3],omega[3],ex_space[3],ey_space[3],ez_space[3];
+  r[0] = p[0] - xcm[0];
+  r[1] = p[1] - xcm[1];
+  r[2] = p[2] - xcm[2];
+  MathExtra::q_to_exyz(quat,ex_space,ey_space,ez_space);
+  MathExtra::angmom_to_omega(angmom,ex_space,ey_space,ez_space,
+                             inertia,omega);
+  vi[0] = omega[1]*r[2] - omega[2]*r[1] + vcm[0];
+  vi[1] = omega[2]*r[0] - omega[0]*r[2] + vcm[1];
+  vi[2] = omega[0]*r[1] - omega[1]*r[0] + vcm[2];
+}
+
+/* ---------------------------------------------------------------------- */
+
+void FixWallBodyPolyhedron::distance(const double* x2, const double* x1,
+                                  double& r) {
+  r = sqrt((x2[0] - x1[0]) * (x2[0] - x1[0])
+    + (x2[1] - x1[1]) * (x2[1] - x1[1])
+    + (x2[2] - x1[2]) * (x2[2] - x1[2]));
+}
+
diff --git a/src/BODY/fix_wall_body_polyhedron.h b/src/BODY/fix_wall_body_polyhedron.h
new file mode 100644
index 0000000000000000000000000000000000000000..ff7b7ca7cfd0806de2d5f4fcd16c79b48c980954
--- /dev/null
+++ b/src/BODY/fix_wall_body_polyhedron.h
@@ -0,0 +1,143 @@
+/* -*- c++ -*- ----------------------------------------------------------
+   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
+   http://lammps.sandia.gov, Sandia National Laboratories
+   Steve Plimpton, sjplimp@sandia.gov
+
+   Copyright (2003) Sandia Corporation.  Under the terms of Contract
+   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+   certain rights in this software.  This software is distributed under
+   the GNU General Public License.
+
+   See the README file in the top-level LAMMPS directory.
+------------------------------------------------------------------------- */
+
+#ifdef FIX_CLASS
+
+FixStyle(wall/body/polyhedron,FixWallBodyPolyhedron)
+
+#else
+
+#ifndef LMP_FIX_WALL_BODY_POLYHERON_H
+#define LMP_FIX_WALL_BODY_POLYHERON_H
+
+#include "fix.h"
+
+namespace LAMMPS_NS {
+
+class FixWallBodyPolyhedron : public Fix {
+ public:
+  FixWallBodyPolyhedron(class LAMMPS *, int, char **);
+  virtual ~FixWallBodyPolyhedron();
+  int setmask();
+  void init();
+  void setup(int);
+  virtual void post_force(int);
+  void reset_dt();
+
+  struct Contact {
+    int ibody, jbody; // body (i.e. atom) indices (not tags)
+    int vertex;       // vertex of the first polygon
+    int edge;         // edge of the second polygon
+    double xv[3];     // coordinates of the vertex
+    double xe[3];     // coordinates of the projection of the vertex on the edge
+    double separation;// separation at contact
+  };
+
+ protected:
+  int wallstyle,pairstyle,wiggle,axis;
+  double kn,c_n,c_t;
+  double lo,hi,cylradius;
+  double amplitude,period,omega;
+  double dt;
+  int time_origin;
+
+  class AtomVecBody *avec;
+  class BodyRoundedPolyhedron *bptr;
+
+  double **discrete;  // list of all sub-particles for all bodies
+  int ndiscrete;      // number of discretes in list
+  int dmax;           // allocated size of discrete list
+  int *dnum;          // number of discretes per line, 0 if uninit
+  int *dfirst;        // index of first discrete per each line
+  int nmax;           // allocated size of dnum,dfirst vectors
+
+  double **edge;      // list of all edge for all bodies
+  int nedge;          // number of edge in list
+  int edmax;          // allocated size of edge list
+  int *ednum;         // number of edges per line, 0 if uninit
+  int *edfirst;       // index of first edge per each line
+  int ednummax;       // allocated size of ednum,edfirst vectors
+
+  double **face;      // list of all edge for all bodies
+  int nface;          // number of faces in list
+  int facmax;         // allocated size of face list
+  int *facnum;        // number of faces per line, 0 if uninit
+  int *facfirst;      // index of first face per each line
+  int facnummax;      // allocated size of facnum,facfirst vectors
+
+  double *enclosing_radius; // enclosing radii for all bodies
+  double *rounded_radius;   // rounded radii for all bodies
+
+  void body2space(int);
+
+  int edge_against_wall(int ibody, double wall_pos, int side, double* vwall,
+     double** x, double** f, double** torque, Contact* contact_list,
+     int &num_contacts, double* facc);
+  int sphere_against_wall(int i, double wall_pos, int side, double* vwall,
+     double** x, double** v, double** f, double** angmom, double** torque);
+
+  int compute_distance_to_wall(int ibody, int edge_index, double *xmi,
+                               double rounded_radius_i, double wall_pos, int side,
+                               double* vwall, int &contact);
+  double contact_separation(const Contact& c1, const Contact& c2);
+  void contact_forces(int ibody, double j_a, double *xi, double *xj, 
+                      double delx, double dely, double delz,
+                      double fx, double fy, double fz, double** x, double** v,
+                      double** angmom, double** f, double** torque, double* vwall);
+
+  void contact_forces(Contact& contact, double j_a, double** x,
+                      double** v, double** angmom, double** f, double** torque,
+                      double* vwall, double* facc);
+  void sum_torque(double* xm, double *x, double fx,
+                  double fy, double fz, double* torque);
+  void total_velocity(double* p, double *xcm, double* vcm, double *angmom,
+                      double *inertia, double *quat, double* vi);
+  void distance(const double* x2, const double* x1, double& r);
+
+};
+
+}
+
+#endif
+#endif
+
+/* ERROR/WARNING messages:
+
+E: Illegal ... command
+
+Self-explanatory.  Check the input script syntax and compare to the
+documentation for the command.  You can use -echo screen as a
+command-line option when running LAMMPS to see the offending line.
+
+E: Fix wall/body/polyhedron requires atom style body rounded/polyhedron
+
+Self-explanatory.
+
+E: Cannot use wall in periodic dimension
+
+Self-explanatory.
+
+E: Cannot wiggle and shear fix wall/body/polygon
+
+Cannot specify both options at the same time.
+
+E: Invalid wiggle direction for fix wall/body/polygon
+
+Self-explanatory.
+
+E: Fix wall/body/polygon is incompatible with Pair style
+
+Must use a body pair style to define the parameters needed for
+this fix.
+
+*/
diff --git a/src/BODY/pair_body_rounded_polygon.cpp b/src/BODY/pair_body_rounded_polygon.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..72591d2bd04e3d2534ff5c87e4765c6a921d9580
--- /dev/null
+++ b/src/BODY/pair_body_rounded_polygon.cpp
@@ -0,0 +1,1359 @@
+/* ----------------------------------------------------------------------
+   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
+   http://lammps.sandia.gov, Sandia National Laboratories
+   Steve Plimpton, sjplimp@sandia.gov
+
+   Copyright (2003) Sandia Corporation.  Under the terms of Contract
+   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+   certain rights in this software.  This software is distributed under
+   the GNU General Public License.
+
+   See the README file in the top-level LAMMPS directory.
+------------------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------
+   Contributing author: Trung Dac Nguyen (ndactrung@gmail.com)
+   Ref: Fraige, Langston, Matchett and Dodds, Particuology 2008, 6:455-466
+   Note: The current implementation has not taken into account
+         the contact history for friction forces.
+------------------------------------------------------------------------- */
+
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include "pair_body_rounded_polygon.h"
+#include "math_extra.h"
+#include "atom.h"
+#include "atom_vec_body.h"
+#include "body_rounded_polygon.h"
+#include "comm.h"
+#include "force.h"
+#include "fix.h"
+#include "modify.h"
+#include "neighbor.h"
+#include "neigh_list.h"
+#include "memory.h"
+#include "error.h"
+
+using namespace LAMMPS_NS;
+
+//#define _POLYGON_DEBUG
+#define DELTA 10000
+#define EPSILON 1e-3
+#define MAX_CONTACTS 4  // maximum number of contacts for 2D models
+#define EFF_CONTACTS 2  // effective contacts for 2D models
+
+enum {INVALID=0,NONE=1,VERTEXI=2,VERTEXJ=3,EDGE=4};
+
+/* ---------------------------------------------------------------------- */
+
+PairBodyRoundedPolygon::PairBodyRoundedPolygon(LAMMPS *lmp) : Pair(lmp)
+{
+  dmax = nmax = 0;
+  discrete = NULL;
+  dnum = dfirst = NULL;
+
+  edmax = ednummax = 0;
+  edge = NULL;
+  ednum = edfirst = NULL;
+
+  enclosing_radius = NULL;
+  rounded_radius = NULL;
+  maxerad = NULL;
+
+  single_enable = 0;
+  restartinfo = 0;
+
+  c_n = 0.1;
+  c_t = 0.2;
+  mu = 0.0;
+  delta_ua = 1.0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+PairBodyRoundedPolygon::~PairBodyRoundedPolygon()
+{
+  memory->destroy(discrete);
+  memory->destroy(dnum);
+  memory->destroy(dfirst);
+
+  memory->destroy(edge);
+  memory->destroy(ednum);
+  memory->destroy(edfirst);
+
+  memory->destroy(enclosing_radius);
+  memory->destroy(rounded_radius);
+
+  if (allocated) {
+    memory->destroy(setflag);
+    memory->destroy(cutsq);
+
+    memory->destroy(k_n);
+    memory->destroy(k_na);
+    memory->destroy(maxerad);
+  }
+}
+
+/* ---------------------------------------------------------------------- */
+
+void PairBodyRoundedPolygon::compute(int eflag, int vflag)
+{
+  int i,j,ii,jj,inum,jnum,itype,jtype;
+  int ni,nj,npi,npj,ifirst,jfirst;
+  int nei,nej,iefirst,jefirst;
+  double xtmp,ytmp,ztmp,delx,dely,delz,evdwl,fx,fy,fz;
+  double rsq,rsqinv,r,radi,radj,eradi,eradj,rradi,rradj,k_nij,k_naij;
+  double xi[3],xj[3],fi[3],fj[3],ti[3],tj[3],facc[3];
+  double *dxi,*dxj;
+  int *ilist,*jlist,*numneigh,**firstneigh;
+
+  evdwl = 0.0;
+  if (eflag || vflag) ev_setup(eflag,vflag);
+  else evflag = vflag_fdotr = 0;
+
+  double **x = atom->x;
+  double **v = atom->v;
+  double **f = atom->f;
+  double **torque = atom->torque;
+  double **angmom = atom->angmom;
+  double *radius = atom->radius;
+  tagint* tag = atom->tag;
+  int *body = atom->body;
+  int *type = atom->type;
+  int nlocal = atom->nlocal;
+  int nall = nlocal + atom->nghost;
+  int newton_pair = force->newton_pair;
+
+  inum = list->inum;
+  ilist = list->ilist;
+  numneigh = list->numneigh;
+  firstneigh = list->firstneigh;
+
+  // grow the per-atom lists if necessary and initialize
+
+  if (atom->nmax > nmax) {
+    memory->destroy(dnum);
+    memory->destroy(dfirst);
+    memory->destroy(ednum);
+    memory->destroy(edfirst);
+    memory->destroy(enclosing_radius);
+    memory->destroy(rounded_radius);
+    nmax = atom->nmax;
+    memory->create(dnum,nmax,"pair:dnum");
+    memory->create(dfirst,nmax,"pair:dfirst");
+    memory->create(ednum,nmax,"pair:ednum");
+    memory->create(edfirst,nmax,"pair:edfirst");
+    memory->create(enclosing_radius,nmax,"pair:enclosing_radius");
+    memory->create(rounded_radius,nmax,"pair:rounded_radius");
+  }
+
+  ndiscrete = nedge = 0;
+  for (i = 0; i < nall; i++)
+    dnum[i] = ednum[i] = 0;
+
+  // loop over neighbors of my atoms
+
+  for (ii = 0; ii < inum; ii++) {
+    i = ilist[ii];
+    xtmp = x[i][0];
+    ytmp = x[i][1];
+    ztmp = x[i][2];
+    itype = type[i];
+    radi = radius[i];
+    jlist = firstneigh[i];
+    jnum = numneigh[i];
+
+    if (body[i] >= 0) {
+      if (dnum[i] == 0) body2space(i);
+      npi = dnum[i];
+      ifirst = dfirst[i];
+      nei = ednum[i];
+      iefirst = edfirst[i];
+      eradi = enclosing_radius[i];
+      rradi = rounded_radius[i];
+    }
+
+    for (jj = 0; jj < jnum; jj++) {
+      j = jlist[jj];
+      j &= NEIGHMASK;
+
+      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];
+      radj = radius[j];
+
+      // body/body interactions
+
+      evdwl = 0.0;
+      facc[0] = facc[1] = facc[2] = 0;
+
+      if (body[i] < 0 || body[j] < 0) continue;
+
+      if (dnum[j] == 0) body2space(j);
+      npj = dnum[j];
+      jfirst = dfirst[j];
+      nej = ednum[j];
+      jefirst = edfirst[j];
+      eradj = enclosing_radius[j];
+      rradj = rounded_radius[j];
+
+      k_nij = k_n[itype][jtype];
+      k_naij = k_na[itype][jtype];
+
+      // no interaction
+
+      r = sqrt(rsq);
+      if (r > radi + radj + cut_inner) continue;
+      rsqinv = 1.0 / rsq;
+
+      if (npi == 1 && npj == 1) {
+        sphere_against_sphere(i, j, delx, dely, delz, rsq,
+                            k_nij, k_naij, x, v, f, evflag);
+        continue;
+      }
+
+      // reset vertex and edge forces
+
+      for (ni = 0; ni < npi; ni++) {
+        discrete[ifirst+ni][3] = 0;
+        discrete[ifirst+ni][4] = 0;
+        discrete[ifirst+ni][5] = 0;
+      }
+
+      for (nj = 0; nj < npj; nj++) {
+        discrete[jfirst+nj][3] = 0;
+        discrete[jfirst+nj][4] = 0;
+        discrete[jfirst+nj][5] = 0;
+      }
+
+      for (ni = 0; ni < nei; ni++) {
+        edge[iefirst+ni][2] = 0;
+        edge[iefirst+ni][3] = 0;
+        edge[iefirst+ni][4] = 0;
+      }
+
+      for (nj = 0; nj < nej; nj++) {
+        edge[jefirst+nj][2] = 0;
+        edge[jefirst+nj][3] = 0;
+        edge[jefirst+nj][4] = 0;
+      }
+
+      int interact, num_contacts, done;
+      double delta_a, j_a;
+      Contact contact_list[MAX_CONTACTS];
+
+      num_contacts = 0;
+
+      // check interaction between i's vertices and j' edges
+
+      interact = vertex_against_edge(i, j, k_nij, k_naij,
+                                     x, f, torque, tag, contact_list,
+                                     num_contacts, evdwl, facc);
+
+      // check interaction between j's vertices and i' edges
+
+      interact = vertex_against_edge(j, i, k_nij, k_naij,
+                                     x, f, torque, tag, contact_list,
+                                     num_contacts, evdwl, facc);
+
+      if (num_contacts >= 2) {
+
+        // find the first two distinct contacts
+
+        done = 0;
+        for (int m = 0; m < num_contacts-1; m++) {
+          for (int n = m+1; n < num_contacts; n++) {
+            delta_a = contact_separation(contact_list[m], contact_list[n]);
+            if (delta_a > 0) {
+              j_a = delta_a / (EFF_CONTACTS * delta_ua);
+              if (j_a < 1.0) j_a = 1.0;
+
+              // scale the force at both contacts
+
+              contact_forces(contact_list[m], j_a, x, v, angmom, f, torque, evdwl, facc);
+              contact_forces(contact_list[n], j_a, x, v, angmom, f, torque, evdwl, facc);
+              done = 1;
+
+              #ifdef _POLYGON_DEBUG
+              printf("  Two separate contacts %d and %d: delta_a = %f; j_a = %f\n",
+                m, n, delta_a, j_a);
+              printf("    %d: vertex %d of body %d and edge %d of body %d; "
+                     "xv = %f %f %f; xe = %f %f %f\n",
+                m, contact_list[m].vertex, contact_list[m].ibody,
+                contact_list[m].edge, contact_list[m].jbody,
+                contact_list[m].xv[0], contact_list[m].xv[1], contact_list[m].xv[2],
+                contact_list[m].xe[0], contact_list[m].xe[1], contact_list[m].xe[2]);
+              printf("    %d: vertex %d of body %d and edge %d of body %d; "
+                     "xv = %f %f %f; xe = %f %f %f\n",
+                n, contact_list[n].vertex, contact_list[n].ibody,
+                contact_list[n].edge, contact_list[n].jbody,
+                contact_list[n].xv[0], contact_list[n].xv[1], contact_list[n].xv[2],
+                contact_list[n].xe[0], contact_list[n].xe[1], contact_list[n].xe[2]);
+              #endif
+
+              break;
+            }
+          }
+          if (done == 1) break;
+        }
+
+
+      } else if (num_contacts == 1) {
+
+        // if there's only one contact, it should be handled here
+        // since forces/torques have not been accumulated from vertex2edge()
+
+        contact_forces(contact_list[0], 1.0, x, v, angmom, f, torque, evdwl, facc);
+
+        #ifdef _POLYGON_DEBUG
+        printf("One contact between vertex %d of body %d and edge %d of body %d:\n",
+                contact_list[0].vertex, tag[contact_list[0].ibody],
+                contact_list[0].edge, tag[contact_list[0].jbody]);
+        printf("xv = %f %f %f; xe = %f %f %f\n",
+               contact_list[0].xv[0], contact_list[0].xv[1], contact_list[0].xv[2],
+               contact_list[0].xe[0], contact_list[0].xe[1], contact_list[0].xe[2]);
+        #endif
+      }
+
+      #ifdef _POLYGON_DEBUG
+      int num_overlapping_contacts = 0;
+      for (int m = 0; m < num_contacts-1; m++) {
+        for (int n = m+1; n < num_contacts; n++) {
+          double l = contact_separation(contact_list[m], contact_list[n]);
+          if (l < EPSILON) num_overlapping_contacts++;
+        }
+      }
+      printf("There are %d contacts detected, %d of which overlap.\n",
+             num_contacts, num_overlapping_contacts);
+      #endif
+
+      if (evflag) ev_tally_xyz(i,j,nlocal,newton_pair,evdwl,0.0,
+                               facc[0],facc[1],facc[2],delx,dely,delz);
+
+    } // end for jj
+  }
+
+  if (vflag_fdotr) virial_fdotr_compute();
+}
+
+/* ----------------------------------------------------------------------
+   allocate all arrays
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolygon::allocate()
+{
+  allocated = 1;
+  int n = atom->ntypes;
+
+  memory->create(setflag,n+1,n+1,"pair:setflag");
+  for (int i = 1; i <= n; i++)
+    for (int j = i; j <= n; j++)
+      setflag[i][j] = 0;
+
+  memory->create(cutsq,n+1,n+1,"pair:cutsq");
+
+  memory->create(k_n,n+1,n+1,"pair:k_n");
+  memory->create(k_na,n+1,n+1,"pair:k_na");
+  memory->create(maxerad,n+1,"pair:maxerad");
+}
+
+/* ----------------------------------------------------------------------
+   global settings
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolygon::settings(int narg, char **arg)
+{
+  if (narg < 5) error->all(FLERR,"Illegal pair_style command");
+
+  c_n = force->numeric(FLERR,arg[0]);
+  c_t = force->numeric(FLERR,arg[1]);
+  mu = force->numeric(FLERR,arg[2]);
+  delta_ua = force->numeric(FLERR,arg[3]);
+  cut_inner = force->numeric(FLERR,arg[4]);
+}
+
+/* ----------------------------------------------------------------------
+   set coeffs for one or more type pairs
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolygon::coeff(int narg, char **arg)
+{
+  if (narg < 4 || narg > 5)
+    error->all(FLERR,"Incorrect args for pair coefficients");
+  if (!allocated) allocate();
+
+  int ilo,ihi,jlo,jhi;
+  force->bounds(FLERR,arg[0],atom->ntypes,ilo,ihi);
+  force->bounds(FLERR,arg[1],atom->ntypes,jlo,jhi);
+
+  double k_n_one = force->numeric(FLERR,arg[2]);
+  double k_na_one = force->numeric(FLERR,arg[3]);
+
+  int count = 0;
+  for (int i = ilo; i <= ihi; i++) {
+    for (int j = MAX(jlo,i); j <= jhi; j++) {
+      k_n[i][j] = k_n_one;
+      k_na[i][j] = k_na_one;
+      setflag[i][j] = 1;
+      count++;
+    }
+  }
+
+  if (count == 0) error->all(FLERR,"Incorrect args for pair coefficients");
+}
+
+/* ----------------------------------------------------------------------
+   init specific to this pair style
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolygon::init_style()
+{
+  avec = (AtomVecBody *) atom->style_match("body");
+  if (!avec) error->all(FLERR,"Pair body/rounded/polygon requires atom style body");
+  if (strcmp(avec->bptr->style,"rounded/polygon") != 0)
+    error->all(FLERR,"Pair body/rounded/polygon requires body style rounded/polygon");
+  bptr = (BodyRoundedPolygon *) avec->bptr;
+
+  if (comm->ghost_velocity == 0)
+    error->all(FLERR,"Pair body/rounded/polygon requires ghost atoms store velocity");
+
+  neighbor->request(this);
+
+  // find the maximum enclosing radius for each atom type
+
+  int i, itype;
+  double eradi;
+  int* body = atom->body;
+  int* type = atom->type;
+  int ntypes = atom->ntypes;
+  int nlocal = atom->nlocal;
+
+  if (atom->nmax > nmax) {
+    memory->destroy(dnum);
+    memory->destroy(dfirst);
+    memory->destroy(ednum);
+    memory->destroy(edfirst);
+    memory->destroy(enclosing_radius);
+    memory->destroy(rounded_radius);
+    nmax = atom->nmax;
+    memory->create(dnum,nmax,"pair:dnum");
+    memory->create(dfirst,nmax,"pair:dfirst");
+    memory->create(ednum,nmax,"pair:ednum");
+    memory->create(edfirst,nmax,"pair:edfirst");
+    memory->create(enclosing_radius,nmax,"pair:enclosing_radius");
+    memory->create(rounded_radius,nmax,"pair:rounded_radius");
+  }
+
+  ndiscrete = nedge = 0;
+  for (i = 0; i < nlocal; i++)
+    dnum[i] = ednum[i] = 0;
+
+  double *merad = NULL;
+  memory->create(merad,ntypes+1,"pair:merad");
+  for (i = 1; i <= ntypes; i++)
+    maxerad[i] = merad[i] = 0;
+
+  int ipour;
+  for (ipour = 0; ipour < modify->nfix; ipour++)
+    if (strcmp(modify->fix[ipour]->style,"pour") == 0) break;
+  if (ipour == modify->nfix) ipour = -1;
+
+  int idep;
+  for (idep = 0; idep < modify->nfix; idep++)
+    if (strcmp(modify->fix[idep]->style,"deposit") == 0) break;
+  if (idep == modify->nfix) idep = -1;
+
+  for (i = 1; i <= ntypes; i++) {
+    merad[i] = 0.0;
+    if (ipour >= 0) {
+      itype = i;
+      merad[i] =
+        *((double *) modify->fix[ipour]->extract("radius",itype));
+    }
+    if (idep >= 0) {
+      itype = i;
+      merad[i] =
+        *((double *) modify->fix[idep]->extract("radius",itype));
+    }
+  }
+
+  for (i = 0; i < nlocal; i++) {
+    itype = type[i];
+    if (body[i] >= 0) {
+      if (dnum[i] == 0) body2space(i);
+      eradi = enclosing_radius[i];
+      if (eradi > merad[itype]) merad[itype] = eradi;
+    } else 
+      merad[itype] = 0;
+  }
+
+  MPI_Allreduce(&merad[1],&maxerad[1],ntypes,MPI_DOUBLE,MPI_MAX,world);
+
+  memory->destroy(merad);
+}
+
+/* ----------------------------------------------------------------------
+   init for one type pair i,j and corresponding j,i
+------------------------------------------------------------------------- */
+
+double PairBodyRoundedPolygon::init_one(int i, int j)
+{
+  k_n[j][i] = k_n[i][j];
+  k_na[j][i] = k_na[i][j];
+
+  return (maxerad[i]+maxerad[j]);
+}
+
+/* ----------------------------------------------------------------------
+   convert N sub-particles in body I to space frame using current quaternion
+   store sub-particle space-frame displacements from COM in discrete list
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolygon::body2space(int i)
+{
+  int ibonus = atom->body[i];
+  AtomVecBody::Bonus *bonus = &avec->bonus[ibonus];
+  int nsub = bptr->nsub(bonus);
+  double *coords = bptr->coords(bonus);
+  int body_num_edges = bptr->nedges(bonus);
+  double* edge_ends = bptr->edges(bonus);
+  double eradius = bptr->enclosing_radius(bonus);
+  double rradius = bptr->rounded_radius(bonus);
+
+  // get the number of sub-particles (vertices)
+  // and the index of the first vertex of my body in the list
+
+  dnum[i] = nsub;
+  dfirst[i] = ndiscrete;
+
+  // grow the vertex list if necessary
+  // the first 3 columns are for coords, the last 3 for forces
+
+  if (ndiscrete + nsub > dmax) {
+    dmax += DELTA;
+    memory->grow(discrete,dmax,6,"pair:discrete");
+  }
+
+  double p[3][3];
+  MathExtra::quat_to_mat(bonus->quat,p);
+
+  for (int m = 0; m < nsub; m++) {
+    MathExtra::matvec(p,&coords[3*m],discrete[ndiscrete]);
+    discrete[ndiscrete][3] = 0;
+    discrete[ndiscrete][4] = 0;
+    discrete[ndiscrete][5] = 0;
+    ndiscrete++;
+  }
+
+  // get the number of edges (vertices)
+  // and the index of the first edge of my body in the list
+
+  ednum[i] = body_num_edges;
+  edfirst[i] = nedge;
+
+  // grow the edge list if necessary
+  // the first 2 columns are for vertex indices within body, the last 3 for forces
+
+  if (nedge + body_num_edges > edmax) {
+    edmax += DELTA;
+    memory->grow(edge,edmax,5,"pair:edge");
+  }
+
+  for (int m = 0; m < body_num_edges; m++) {
+    edge[nedge][0] = static_cast<int>(edge_ends[2*m+0]);
+    edge[nedge][1] = static_cast<int>(edge_ends[2*m+1]);
+    edge[nedge][2] = 0;
+    edge[nedge][3] = 0;
+    edge[nedge][4] = 0;
+    nedge++;
+  }
+
+  enclosing_radius[i] = eradius;
+  rounded_radius[i] = rradius;
+}
+
+/* ----------------------------------------------------------------------
+   Interaction between two spheres with different radii
+   according to the 2D model from Fraige et al.
+---------------------------------------------------------------------- */
+
+void PairBodyRoundedPolygon::sphere_against_sphere(int i, int j,
+                       double delx, double dely, double delz, double rsq,
+                       double k_n, double k_na, double** x, double** v,
+                       double** f, int evflag)
+{
+  double eradi,eradj,rradi,rradj;
+  double vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3;
+  double rij,rsqinv,R,fx,fy,fz,fn[3],ft[3],fpair,shift,energy;
+  int nlocal = atom->nlocal;
+  int newton_pair = force->newton_pair;
+
+  eradi = enclosing_radius[i];
+  rradi = rounded_radius[i];
+
+  eradj = enclosing_radius[j];
+  rradj = rounded_radius[j];
+
+  rsqinv = 1.0/rsq;
+  rij = sqrt(rsq);
+  R = rij - (rradi + rradj);
+  shift = k_na * cut_inner;
+
+  energy = 0;
+
+  if (R <= 0) {           // deformation occurs
+    fpair = -k_n * R - shift;
+    energy = (0.5 * k_n * R + shift) * R;
+  } else if (R <= cut_inner) {   // not deforming but cohesive ranges overlap
+    fpair = k_na * R - shift;
+    energy = (-0.5 * k_na * R + shift) * R;
+  } else fpair = 0.0;
+
+  fx = delx*fpair/rij;
+  fy = dely*fpair/rij;
+  fz = delz*fpair/rij;
+
+  if (R <= EPSILON) { // in contact
+
+    // relative translational velocity
+
+    vr1 = v[i][0] - v[j][0];
+    vr2 = v[i][1] - v[j][1];
+    vr3 = v[i][2] - v[j][2];
+
+    // normal component
+
+    vnnr = vr1*delx + vr2*dely + vr3*delz;
+    vn1 = delx*vnnr * rsqinv;
+    vn2 = dely*vnnr * rsqinv;
+    vn3 = delz*vnnr * rsqinv;
+
+    // tangential component
+
+    vt1 = vr1 - vn1;
+    vt2 = vr2 - vn2;
+    vt3 = vr3 - vn3;
+
+    // normal friction term at contact
+
+    fn[0] = -c_n * vn1;
+    fn[1] = -c_n * vn2;
+    fn[2] = -c_n * vn3;
+
+    // tangential friction term at contact
+    // excluding the tangential deformation term
+
+    ft[0] = -c_t * vt1;
+    ft[1] = -c_t * vt2;
+    ft[2] = -c_t * vt3;
+  }
+
+  f[i][0] += fx;
+  f[i][1] += fy;
+  f[i][2] += fz;
+  
+  if (newton_pair || j < nlocal) {
+    f[j][0] -= fx;
+    f[j][1] -= fy;
+    f[j][2] -= fz;
+  }
+
+  if (evflag) ev_tally_xyz(i,j,nlocal,newton_pair,
+                           energy,0.0,fx,fy,fz,delx,dely,delz);
+}
+
+/* ----------------------------------------------------------------------
+   Determine the interaction mode between i's vertices against j's edges
+
+   i = atom i (body i)
+   j = atom j (body j)
+   x      = atoms' coordinates
+   f      = atoms' forces
+   torque = atoms' torques
+   tag    = atoms' tags
+   contact_list = list of contacts
+   num_contacts = number of contacts between i's vertices and j's edges
+   Return:
+     interact = 0 no interaction at all
+                1 there's at least one case where i's vertices interacts
+                  with j's edges
+---------------------------------------------------------------------- */
+
+int PairBodyRoundedPolygon::vertex_against_edge(int i, int j,
+                                                double k_n, double k_na,
+                                                double** x, double** f,
+                                                double** torque, tagint* tag,
+                                                Contact* contact_list,
+                                                int &num_contacts,
+                                                double &evdwl, double* facc)
+{
+  int ni, npi, ifirst, nei, iefirst;
+  int nj, npj, jfirst, nej, jefirst;
+  double xpi[3], xpj[3], dist, eradi, eradj, rradi, rradj;
+  double fx, fy, fz, rx, ry, rz, energy;
+  int interact;
+  int nlocal = atom->nlocal;
+
+  npi = dnum[i];
+  ifirst = dfirst[i];
+  nei = ednum[i];
+  iefirst = edfirst[i];
+  eradi = enclosing_radius[i];
+  rradi = rounded_radius[i];
+
+  npj = dnum[j];
+  jfirst = dfirst[j];
+  nej = ednum[j];
+  jefirst = edfirst[j];
+  eradj = enclosing_radius[j];
+  rradj = rounded_radius[j];
+
+  energy = 0;
+  interact = 0;
+
+  // loop through body i's vertices
+
+  for (ni = 0; ni < npi; ni++) {
+
+    // convert body-fixed coordinates to space-fixed, xi
+
+    xpi[0] = x[i][0] + discrete[ifirst+ni][0];
+    xpi[1] = x[i][1] + discrete[ifirst+ni][1];
+    xpi[2] = x[i][2] + discrete[ifirst+ni][2];
+
+    // compute the distance from the vertex to the COM of body j
+
+    distance(xpi, x[j], dist);
+
+    #ifdef _POLYGON_DEBUG
+    printf("Distance between vertex %d of body %d (%0.1f %0.1f %0.1f) "
+           "to body %d's COM: %f (cut = %0.1f)\n",
+           ni, xpi[0], xpi[1], xpi[2], atom->tag[i], atom->tag[j], dist,
+           eradj + rradi + rradj + cut_inner);
+    #endif
+
+    // the vertex is within the enclosing circle (sphere) of body j,
+    // possibly interacting
+
+    if (dist > eradj + rradj + rradi + cut_inner) continue;
+
+    int mode, contact, p2vertex;
+    double d, R, hi[3], t, delx, dely, delz, fpair, shift;
+    double xj[3], rij;
+
+    // loop through body j's edges
+
+    for (nj = 0; nj < nej; nj++) {
+
+      // compute the distance between the edge nj to the vertex xpi
+
+      mode = compute_distance_to_vertex(j, nj, x[j], rradj,
+                                        xpi, rradi, cut_inner,
+                                        d, hi, t, contact);
+
+      if (mode == INVALID || mode == NONE) continue;
+
+      if (mode == VERTEXI || mode == VERTEXJ) {
+
+        interact = 1;
+
+        // vertex i interacts with a vertex of the edge, but does not contact
+
+        if (mode == VERTEXI) p2vertex = edge[jefirst+nj][0];
+        else if (mode == VERTEXJ) p2vertex = edge[jefirst+nj][1];
+
+        // p2.body2space(p2vertex, xj);
+        xpj[0] = x[j][0] + discrete[jfirst+p2vertex][0];
+        xpj[1] = x[j][1] + discrete[jfirst+p2vertex][1];
+        xpj[2] = x[j][2] + discrete[jfirst+p2vertex][2];
+
+        delx = xpi[0] - xpj[0];
+        dely = xpi[1] - xpj[1];
+        delz = xpi[2] - xpj[2];
+
+        // R = surface separation = rij shifted by the rounded radii
+        // R = rij - (p1.rounded_radius + p2.rounded_radius);
+        // note: the force is defined for R, not for rij
+        // R > rc:     no interaction between vertex ni and p2vertex
+        // 0 < R < rc: cohesion between vertex ni and p2vertex
+        // R < 0:      deformation between vertex ni and p2vertex
+
+        rij = sqrt(delx*delx + dely*dely + delz*delz);
+        R = rij - (rradi + rradj);
+        shift = k_na * cut_inner;
+
+        // the normal frictional term -c_n * vn will be added later
+
+        if (R <= 0) {           // deformation occurs
+          fpair = -k_n * R - shift;
+          energy += (0.5 * k_n * R + shift) * R;
+        } else if (R <= cut_inner) {   // not deforming but cohesive ranges overlap
+          fpair = k_na * R - shift;
+          energy += (-0.5 * k_na * R + shift) * R;
+        } else fpair = 0.0;
+
+        fx = delx*fpair/rij;
+        fy = dely*fpair/rij;
+        fz = delz*fpair/rij;
+
+        #ifdef _POLYGON_DEBUG
+        printf("  Interaction between vertex %d of %d and vertex %d of %d:",
+               ni, tag[i], p2vertex, tag[j]);
+        printf("    mode = %d; contact = %d; d = %f; rij = %f, t = %f\n",
+               mode, contact, d, rij, t);
+        printf("    R = %f; cut_inner = %f\n", R, cut_inner);
+        printf("    fpair = %f\n", fpair);
+        #endif
+
+        // add forces to body i and body j directly
+        // avoid double counts this pair of vertices
+        // i and j can be either local or ghost atoms (bodies)
+        // probably need more work here when the vertices' interaction
+        // are not symmetric, e.g. j interacts with the edge
+        // consisting of i but in mode = EDGE instead of VERTEX*.
+        // OR, for the time being assume that the edge length is
+        // sufficiently greater than the rounded radius to distinguish
+        // vertex-vertex from vertex-edge contact modes.
+        // Special case: when i is a sphere, also accumulate
+
+        if (tag[i] < tag[j] || npi == 1) {
+
+          f[i][0] += fx;
+          f[i][1] += fy;
+          f[i][2] += fz;
+          sum_torque(x[i], xpi, fx, fy, fz, torque[i]);
+
+          f[j][0] -= fx;
+          f[j][1] -= fy;
+          f[j][2] -= fz;
+          sum_torque(x[j], xpj, -fx, -fy, -fz, torque[j]);
+
+          facc[0] += fx; facc[1] += fy; facc[2] += fz;
+
+          #ifdef _POLYGON_DEBUG
+          printf("    from vertex-vertex: "
+                 "force on vertex %d of body %d: fx %f fy %f fz %f\n"
+                 "      torque body %d: %f %f %f\n"
+                 "      torque body %d: %f %f %f\n", ni, tag[i], fx, fy, fz,
+            tag[i],torque[i][0],torque[i][1],torque[i][2],
+            tag[j],torque[j][0],torque[j][1],torque[j][2]);
+        #endif
+        }
+
+        // done with the edges from body j,
+        // given that vertex ni interacts with only one vertex from one edge of body j
+
+//        break;
+
+      } else if (mode == EDGE) {
+
+        interact = 1;
+
+        // vertex i interacts with the edge
+
+        delx = xpi[0] - hi[0];
+        dely = xpi[1] - hi[1];
+        delz = xpi[2] - hi[2];
+
+        // R = surface separation = d shifted by the rounded radii
+        // R = d - (p1.rounded_radius + p2.rounded_radius);
+        // Note: the force is defined for R, not for d
+        // R > rc:     no interaction between vertex i and edge j
+        // 0 < R < rc: cohesion between vertex i and edge j
+        // R < 0:      deformation between vertex i and edge j
+        // rij = sqrt(delx*delx + dely*dely + delz*delz);
+
+        R = d - (rradi + rradj);
+        shift = k_na * cut_inner;
+
+        // the normal frictional term -c_n * vn will be added later
+
+        if (R <= 0) {           // deformation occurs
+          fpair = -k_n * R - shift;
+          energy += (0.5 * k_n * R + shift) * R;
+        } else if (R <= cut_inner) {   // not deforming but cohesive ranges overlap
+          fpair = k_na * R - shift;
+          energy += (-0.5 * k_na * R + shift) * R;
+        } else fpair = 0.0;
+
+        fx = delx*fpair/d;
+        fy = dely*fpair/d;
+        fz = delz*fpair/d;
+
+        #ifdef _POLYGON_DEBUG
+        printf("  Interaction between vertex %d of %d and edge %d of %d:",
+               ni, tag[i], nj, tag[j]);
+        printf("    mode = %d; contact = %d; d = %f; t = %f\n",
+               mode, contact, d, t);
+        printf("    R = %f; cut_inner = %f\n", R, cut_inner);
+        printf("    fpair = %f\n", fpair);
+        #endif
+
+        if (contact == 1) {
+
+          // vertex ni of body i contacts with edge nj of body j
+
+          contact_list[num_contacts].ibody = i;
+          contact_list[num_contacts].jbody = j;
+          contact_list[num_contacts].vertex = ni;
+          contact_list[num_contacts].edge = nj;
+          contact_list[num_contacts].xv[0] = xpi[0];
+          contact_list[num_contacts].xv[1] = xpi[1];
+          contact_list[num_contacts].xv[2] = xpi[2];
+          contact_list[num_contacts].xe[0] = hi[0];
+          contact_list[num_contacts].xe[1] = hi[1];
+          contact_list[num_contacts].xe[2] = hi[2];
+          contact_list[num_contacts].separation = R;
+          num_contacts++;
+
+          // store forces to vertex ni and the edge nj
+          // to be rescaled later
+
+          discrete[ifirst+ni][3] = fx;
+          discrete[ifirst+ni][4] = fy;
+          discrete[ifirst+ni][5] = fz;
+
+          edge[jefirst+nj][2] = -fx;
+          edge[jefirst+nj][3] = -fy;
+          edge[jefirst+nj][4] = -fz;
+
+          #ifdef _POLYGON_DEBUG
+          printf("  Stored forces at vertex and edge for accumulating later.\n");
+          #endif
+
+        } else { // no contact
+
+          // accumulate force and torque to both bodies directly
+
+          f[i][0] += fx;
+          f[i][1] += fy;
+          f[i][2] += fz;
+          sum_torque(x[i], xpi, fx, fy, fz, torque[i]);
+
+          f[j][0] -= fx;
+          f[j][1] -= fy;
+          f[j][2] -= fz;
+          sum_torque(x[j], hi, -fx, -fy, -fz, torque[j]);
+
+          facc[0] += fx; facc[1] += fy; facc[2] += fz;
+
+          #ifdef _POLYGON_DEBUG
+          printf("    from vertex-edge, no contact: "
+                 "force on vertex %d of body %d: fx %f fy %f fz %f\n"
+                 "      torque body %d: %f %f %f\n"
+                 "      torque body %d: %f %f %f\n", ni, tag[i], fx, fy, fz,
+                 tag[i],torque[i][0],torque[i][1],torque[i][2],
+                 tag[j],torque[j][0],torque[j][1],torque[j][2]);
+          #endif
+        } // end if contact
+
+        // done with the edges from body j,
+        // given that vertex ni interacts with only one edge from body j
+
+//        break;
+
+      } // end if mode
+
+    } // end for looping through the edges of body j
+
+  } // end for looping through the vertices of body i
+
+  evdwl += energy;
+
+  return interact;
+}
+
+/* -------------------------------------------------------------------------
+  Compute the distance between an edge of body i and a vertex from
+  another body
+  Input:
+    ibody      = body i (i.e. atom i)
+    edge_index = edge index of body i
+    xmi        = atom i's coordinates (body i's center of mass)
+    x0         = coordinate of the tested vertex from another body
+    x0_rounded_radius = rounded radius of the tested vertex
+    cut_inner  = cutoff for vertex-vertex and vertex-edge interaction
+  Output:
+    d          = Distance from a point x0 to an edge
+    hi         = coordinates of the projection of x0 on the edge
+    t          = ratio to determine the relative position of hi
+                 wrt xi and xj on the segment
+  contact      = 0 no contact between the queried vertex and the edge
+                 1 contact detected
+  return
+    INVALID if the edge index is invalid
+    NONE    if there is no interaction
+    VERTEXI if the tested vertex interacts with the first vertex of the edge
+    VERTEXJ if the tested vertex interacts with the second vertex of the edge
+    EDGE    if the tested vertex interacts with the edge
+------------------------------------------------------------------------- */
+
+int PairBodyRoundedPolygon::compute_distance_to_vertex(int ibody,
+                                                int edge_index,
+                                                double *xmi,
+                                                double rounded_radius,
+                                                double* x0,
+                                                double x0_rounded_radius,
+                                                double cut_inner,
+                                                double &d,
+                                                double hi[3],
+                                                double &t,
+                                                int &contact)
+{
+  if (edge_index >= ednum[ibody]) return INVALID;
+
+  int mode,ifirst,iefirst,npi1,npi2;
+  double xi1[3],xi2[3],u[3],v[3],uij[3];
+  double udotv, magv, magucostheta;
+  double delx,dely,delz;
+
+  ifirst = dfirst[ibody];
+  iefirst = edfirst[ibody];
+  npi1 = static_cast<int>(edge[iefirst+edge_index][0]);
+  npi2 = static_cast<int>(edge[iefirst+edge_index][1]);
+
+  // compute the space-fixed coordinates for the vertices of the edge
+
+  xi1[0] = xmi[0] + discrete[ifirst+npi1][0];
+  xi1[1] = xmi[1] + discrete[ifirst+npi1][1];
+  xi1[2] = xmi[2] + discrete[ifirst+npi1][2];
+
+  xi2[0] = xmi[0] + discrete[ifirst+npi2][0];
+  xi2[1] = xmi[1] + discrete[ifirst+npi2][1];
+  xi2[2] = xmi[2] + discrete[ifirst+npi2][2];
+
+  // u = x0 - xi1
+
+  u[0] = x0[0] - xi1[0];
+  u[1] = x0[1] - xi1[1];
+  u[2] = x0[2] - xi1[2];
+
+  // v = xi2 - xi1
+
+  v[0] = xi2[0] - xi1[0];
+  v[1] = xi2[1] - xi1[1];
+  v[2] = xi2[2] - xi1[2];
+
+  // dot product between u and v = magu * magv * costheta
+
+  udotv = u[0] * v[0] + u[1] * v[1] + u[2] * v[2];
+  magv = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+  magucostheta = udotv / magv;
+
+  // uij is the unit vector pointing from xi to xj
+
+  uij[0] = v[0] / magv;
+  uij[1] = v[1] / magv;
+  uij[2] = v[2] / magv;
+
+  // position of the projection of x0 on the line (xi, xj)
+
+  hi[0] = xi1[0] + magucostheta * uij[0];
+  hi[1] = xi1[1] + magucostheta * uij[1];
+  hi[2] = xi1[2] + magucostheta * uij[2];
+
+  // distance from x0 to the line (xi, xj) = distance from x0 to hi
+
+  distance(hi, x0, d);
+
+  // determine the interaction mode
+  // for 2D: a vertex can interact with one edge at most
+  // for 3D: a vertex can interact with one face at most
+
+  mode = NONE;
+  contact = 0;
+
+  if (d > rounded_radius + x0_rounded_radius + cut_inner) {
+
+    // if the vertex is far away from the edge
+
+    mode = NONE;
+
+  } else {
+
+    // check if x0 (the queried vertex) and xmi (the body's center of mass)
+    // are on the different sides of the edge
+
+    int m = opposite_sides(xi1, xi2, x0, xmi);
+
+    if (m == 0) {
+
+      // x0 and xmi are on not the opposite sides of the edge
+      // leave xpi for another edge to detect
+
+      mode = NONE;
+
+    } else {
+
+      // x0 and xmi are on the different sides
+      // t is the ratio to detect if x0 is closer to the vertices xi or xj
+
+      if (fabs(xi2[0] - xi1[0]) > EPSILON)
+        t = (hi[0] - xi1[0]) / (xi2[0] - xi1[0]);
+      else if (fabs(xi2[1] - xi1[1]) > EPSILON)
+        t = (hi[1] - xi1[1]) / (xi2[1] - xi1[1]);
+      else if (fabs(xi2[2] - xi1[2]) > EPSILON)
+        t = (hi[2] - xi1[2]) / (xi2[2] - xi1[2]);
+
+      double contact_dist = rounded_radius + x0_rounded_radius;
+      if (t >= 0 && t <= 1) {
+        mode = EDGE;
+        if (d < contact_dist + EPSILON)
+          contact = 1;
+        
+      } else { // t < 0 || t > 1: closer to either vertices of the edge
+
+        if (t < 0) {
+          // measure the distance from x0 to xi1
+          delx = x0[0] - xi1[0];
+          dely = x0[1] - xi1[1];
+          delz = x0[2] - xi1[2];
+          double dx0xi1 = sqrt(delx*delx + dely*dely + delz*delz);
+          if (dx0xi1 > contact_dist + cut_inner)
+            mode = NONE;
+          else
+            mode = VERTEXI;
+        } else {
+          // measure the distance from x0 to xi2
+          delx = x0[0] - xi2[0];
+          dely = x0[1] - xi2[1];
+          delz = x0[2] - xi2[2];
+          double dx0xi2 = sqrt(delx*delx + dely*dely + delz*delz);
+          if (dx0xi2 > contact_dist + cut_inner)
+            mode = NONE;
+          else
+            mode = VERTEXJ;
+        }
+      } // end if t >= 0 && t <= 1
+    } // end if x0 and xmi are on the same side of the edge
+  }
+
+  return mode;
+}
+
+/* ----------------------------------------------------------------------
+  Compute contact forces between two bodies
+  modify the force stored at the vertex and edge in contact by j_a
+  sum forces and torque to the corresponding bodies
+  fn = normal friction component
+  ft = tangential friction component (-c_t * v_t)
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolygon::contact_forces(Contact& contact, double j_a,
+                       double** x, double** v, double** angmom, double** f,
+                       double** torque, double &evdwl, double* facc)
+{
+  int ibody,jbody,ibonus,jbonus,ifirst,jefirst,ni,nj;
+  double fx,fy,fz,delx,dely,delz,rsq,rsqinv;
+  double vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3;
+  double fn[3],ft[3],vi[3],vj[3];
+  double *quat, *inertia;
+  AtomVecBody::Bonus *bonus;
+
+  ibody = contact.ibody;
+  jbody = contact.jbody;
+
+  // compute the velocity of the vertex in the space-fixed frame
+
+  ibonus = atom->body[ibody];
+  bonus = &avec->bonus[ibonus];
+  quat = bonus->quat;
+  inertia = bonus->inertia;
+  total_velocity(contact.xv, x[ibody], v[ibody], angmom[ibody],
+                 inertia, quat, vi);
+
+  // compute the velocity of the point on the edge in the space-fixed frame
+
+  jbonus = atom->body[jbody];
+  bonus = &avec->bonus[jbonus];
+  quat = bonus->quat;
+  inertia = bonus->inertia;
+  total_velocity(contact.xe, x[jbody], v[jbody], angmom[jbody],
+                 inertia, quat, vj);
+
+  // vector pointing from the vertex to the point on the edge
+
+  delx = contact.xv[0] - contact.xe[0];
+  dely = contact.xv[1] - contact.xe[1];
+  delz = contact.xv[2] - contact.xe[2];
+  rsq = delx*delx + dely*dely + delz*delz;
+  rsqinv = 1.0/rsq;
+
+  // relative translational velocity
+
+  vr1 = vi[0] - vj[0];
+  vr2 = vi[1] - vj[1];
+  vr3 = vi[2] - vj[2];
+
+  // normal component
+
+  vnnr = vr1*delx + vr2*dely + vr3*delz;
+  vn1 = delx*vnnr * rsqinv;
+  vn2 = dely*vnnr * rsqinv;
+  vn3 = delz*vnnr * rsqinv;
+
+  // tangential component
+
+  vt1 = vr1 - vn1;
+  vt2 = vr2 - vn2;
+  vt3 = vr3 - vn3;
+
+  // normal friction term at contact
+
+  fn[0] = -c_n * vn1;
+  fn[1] = -c_n * vn2;
+  fn[2] = -c_n * vn3;
+
+  // tangential friction term at contact
+  // excluding the tangential deformation term for now
+
+  ft[0] = -c_t * vt1;
+  ft[1] = -c_t * vt2;
+  ft[2] = -c_t * vt3;
+
+  // only the cohesive force is scaled by j_a
+  // mu * fne = tangential friction deformation during gross sliding
+  // see Eq. 4, Fraige et al.
+
+  ifirst = dfirst[ibody];
+  ni = contact.vertex;
+
+  fx = discrete[ifirst+ni][3] * j_a + fn[0] + ft[0] +
+    mu * discrete[ifirst+ni][3];
+  fy = discrete[ifirst+ni][4] * j_a + fn[1] + ft[1] +
+    mu * discrete[ifirst+ni][4];
+  fz = discrete[ifirst+ni][5] * j_a + fn[2] + ft[2] +
+    mu * discrete[ifirst+ni][5];
+  f[ibody][0] += fx;
+  f[ibody][1] += fy;
+  f[ibody][2] += fz;
+  sum_torque(x[ibody], contact.xv, fx, fy, fz, torque[ibody]);
+
+  // accumulate forces to the vertex only
+
+  facc[0] += fx; facc[1] += fy; facc[2] += fz;
+
+  // only the cohesive force is scaled by j_a
+  // mu * fne = tangential friction deformation during gross sliding
+  // Eq. 4, Fraige et al.
+
+  jefirst = edfirst[jbody];
+  nj = contact.edge;
+
+  fx = edge[jefirst+nj][2] * j_a - fn[0] - ft[0] +
+    mu * edge[jefirst+nj][2];
+  fy = edge[jefirst+nj][3] * j_a - fn[1] - ft[1] +
+    mu * edge[jefirst+nj][3];
+  fz = edge[jefirst+nj][4] * j_a - fn[2] - ft[2] +
+    mu * edge[jefirst+nj][4];
+  f[jbody][0] += fx;
+  f[jbody][1] += fy;
+  f[jbody][2] += fz;
+  sum_torque(x[jbody], contact.xe, fx, fy, fz, torque[jbody]);
+
+  #ifdef _POLYGON_DEBUG
+  printf("From contact forces: vertex fx %f fy %f fz %f\n"
+         "      torque body %d: %f %f %f\n"
+         "      torque body %d: %f %f %f\n",
+         discrete[ifirst+ni][3], discrete[ifirst+ni][4], discrete[ifirst+ni][5],
+         atom->tag[ibody],torque[ibody][0],torque[ibody][1],torque[ibody][2],
+         atom->tag[jbody],torque[jbody][0],torque[jbody][1],torque[jbody][2]);
+  #endif
+}
+
+/* ----------------------------------------------------------------------
+  Determine the length of the contact segment, i.e. the separation between
+  2 contacts, should be extended for 3D models.
+------------------------------------------------------------------------- */
+
+double PairBodyRoundedPolygon::contact_separation(const Contact& c1,
+                                                  const Contact& c2)
+{
+  double x1 = c1.xv[0];
+  double y1 = c1.xv[1];
+  double x2 = c1.xe[0];
+  double y2 = c1.xe[1];
+  double x3 = c2.xv[0];
+  double y3 = c2.xv[1];
+
+  double delta_a = 0.0;
+  if (fabs(x2 - x1) > EPSILON) {
+    double A = (y2 - y1) / (x2 - x1);
+    delta_a = fabs(y1 - A * x1 - y3 + A * x3) / sqrt(1 + A * A);
+  } else {
+    delta_a = fabs(x1 - x3);
+  }
+
+  return delta_a;
+}
+
+/* ----------------------------------------------------------------------
+  Accumulate torque to body from the force f=(fx,fy,fz) acting at point x
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolygon::sum_torque(double* xm, double *x, double fx,
+                                        double fy, double fz, double* torque)
+{
+  double rx = x[0] - xm[0];
+  double ry = x[1] - xm[1];
+  double rz = x[2] - xm[2];
+  double tx = ry * fz - rz * fy;
+  double ty = rz * fx - rx * fz;
+  double tz = rx * fy - ry * fx;
+  torque[0] += tx;
+  torque[1] += ty;
+  torque[2] += tz;
+}
+
+/* ----------------------------------------------------------------------
+  Test if two points a and b are in opposite sides of the line that
+  connects two points x1 and x2
+------------------------------------------------------------------------- */
+
+int PairBodyRoundedPolygon::opposite_sides(double* x1, double* x2,
+                                           double* a, double* b)
+{
+  double m_a = (x1[1] - x2[1])*(a[0] - x1[0]) + (x2[0] - x1[0])*(a[1] - x1[1]);
+  double m_b = (x1[1] - x2[1])*(b[0] - x1[0]) + (x2[0] - x1[0])*(b[1] - x1[1]);
+  // equal to zero when either a or b is inline with the line x1-x2
+  if (m_a * m_b <= 0)
+    return 1;
+  else
+    return 0;
+}
+
+/* ----------------------------------------------------------------------
+  Calculate the total velocity of a point (vertex, a point on an edge):
+    vi = vcm + omega ^ (p - xcm)
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolygon::total_velocity(double* p, double *xcm,
+                              double* vcm, double *angmom, double *inertia,
+                              double *quat, double* vi)
+{
+  double r[3],omega[3],ex_space[3],ey_space[3],ez_space[3];
+  r[0] = p[0] - xcm[0];
+  r[1] = p[1] - xcm[1];
+  r[2] = p[2] - xcm[2];
+  MathExtra::q_to_exyz(quat,ex_space,ey_space,ez_space);
+  MathExtra::angmom_to_omega(angmom,ex_space,ey_space,ez_space,
+                             inertia,omega);
+  vi[0] = omega[1]*r[2] - omega[2]*r[1] + vcm[0];
+  vi[1] = omega[2]*r[0] - omega[0]*r[2] + vcm[1];
+  vi[2] = omega[0]*r[1] - omega[1]*r[0] + vcm[2];
+}
+
+/* ---------------------------------------------------------------------- */
+
+void PairBodyRoundedPolygon::distance(const double* x2, const double* x1,
+                                      double& r)
+{
+  r = sqrt((x2[0] - x1[0]) * (x2[0] - x1[0])
+    + (x2[1] - x1[1]) * (x2[1] - x1[1])
+    + (x2[2] - x1[2]) * (x2[2] - x1[2]));
+}
+
diff --git a/src/BODY/pair_body_rounded_polygon.h b/src/BODY/pair_body_rounded_polygon.h
new file mode 100644
index 0000000000000000000000000000000000000000..09c93b832e0d322594b55a7f78ddbb309e158cf2
--- /dev/null
+++ b/src/BODY/pair_body_rounded_polygon.h
@@ -0,0 +1,128 @@
+/* -*- c++ -*- ----------------------------------------------------------
+   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
+   http://lammps.sandia.gov, Sandia National Laboratories
+   Steve Plimpton, sjplimp@sandia.gov
+
+   Copyright (2003) Sandia Corporation.  Under the terms of Contract
+   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+   certain rights in this software.  This software is distributed under
+   the GNU General Public License.
+
+   See the README file in the top-level LAMMPS directory.
+------------------------------------------------------------------------- */
+
+#ifdef PAIR_CLASS
+
+PairStyle(body/rounded/polygon,PairBodyRoundedPolygon)
+
+#else
+
+#ifndef LMP_PAIR_BODY_ROUNDED_POLYGON_H
+#define LMP_PAIR_BODY_ROUNDED_POLYGON_H
+
+#include "pair.h"
+
+namespace LAMMPS_NS {
+
+class PairBodyRoundedPolygon : public Pair {
+ public:
+  PairBodyRoundedPolygon(class LAMMPS *);
+  ~PairBodyRoundedPolygon();
+  void compute(int, int);
+  void settings(int, char **);
+  void coeff(int, char **);
+  void init_style();
+  double init_one(int, int);
+
+  struct Contact {
+    int ibody, jbody; // body (i.e. atom) indices (not tags)
+    int vertex;       // vertex of the first polygon
+    int edge;         // edge of the second polygon
+    double xv[3];     // coordinates of the vertex
+    double xe[3];     // coordinates of the projection of the vertex on the edge
+    double separation;// separation at contact
+  };
+
+ protected:
+  double **k_n;       // normal repulsion strength
+  double **k_na;      // normal attraction strength
+  double c_n;         // normal damping coefficient
+  double c_t;         // tangential damping coefficient
+  double mu;          // normal friction coefficient during gross sliding
+  double delta_ua;    // contact line (area for 3D models) modification factor
+  double cut_inner;   // cutoff for interaction between vertex-edge surfaces
+
+  class AtomVecBody *avec;
+  class BodyRoundedPolygon *bptr;
+
+  double **discrete;  // list of all sub-particles for all bodies
+  int ndiscrete;      // number of discretes in list
+  int dmax;           // allocated size of discrete list
+  int *dnum;          // number of discretes per line, 0 if uninit
+  int *dfirst;        // index of first discrete per each line
+  int nmax;           // allocated size of dnum,dfirst vectors
+
+  double **edge;      // list of all edge for all bodies
+  int nedge;          // number of edge in list
+  int edmax;          // allocated size of edge list
+  int *ednum;         // number of edges per line, 0 if uninit
+  int *edfirst;       // index of first edge per each line
+  int ednummax;       // allocated size of ednum,edfirst vectors
+
+  double *enclosing_radius; // enclosing radii for all bodies
+  double *rounded_radius;   // rounded radii for all bodies
+  double *maxerad;          // per-type maximum enclosing radius
+
+  void allocate();
+  void body2space(int);
+
+  int vertex_against_edge(int i, int j, double k_n, double k_na,
+                          double** x, double** f, double** torque,
+                          tagint* tag, Contact* contact_list,
+                          int &num_contacts, double &evdwl, double* facc);
+  void sphere_against_sphere(int i, int j, double delx, double dely, double delz,
+                             double rsq, double k_n, double k_na,
+                             double** x, double** v, double** f, int evflag);
+  int compute_distance_to_vertex(int ibody, int edge_index, double* xmi,
+                                 double rounded_radius, double* x0,
+                                 double x0_rounded_radius, double cut_inner,
+                                 double &d, double hi[3], double &t,
+                                 int &contact);
+  double contact_separation(const Contact& c1, const Contact& c2);
+  void contact_forces(Contact& contact, double j_a, double** x, 
+                      double** v, double** f, double** angmom, 
+                      double** torque, double &evdwl, double* facc);
+  void sum_torque(double* xm, double *x, double fx,
+                  double fy, double fz, double* torque);
+  int opposite_sides(double* x1, double* x2, double* a, double* b);
+  void total_velocity(double* p, double *xcm, double* vcm, double *angmom,
+                      double *inertia, double *quat, double* vi);
+  inline void distance(const double* x2, const double* x1, double& r);
+};
+
+}
+
+#endif
+#endif
+
+/* ERROR/WARNING messages:
+
+E: Illegal ... command
+
+Self-explanatory.  Check the input script syntax and compare to the
+documentation for the command.  You can use -echo screen as a
+command-line option when running LAMMPS to see the offending line.
+
+E: Incorrect args for pair coefficients
+
+Self-explanatory.  Check the input script or data file.
+
+E: Pair body/rounded/polygon requires atom style body rounded/polygon
+
+Self-explanatory.
+
+E: Pair body requires body style rounded/polygon
+
+This pair style is specific to the rounded/polygon body style.
+
+*/
diff --git a/src/BODY/pair_body_rounded_polyhedron.cpp b/src/BODY/pair_body_rounded_polyhedron.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8d5f9ec72cb2d439c433b1f57c01d1536c6e4527
--- /dev/null
+++ b/src/BODY/pair_body_rounded_polyhedron.cpp
@@ -0,0 +1,2348 @@
+/* ----------------------------------------------------------------------
+   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
+   http://lammps.sandia.gov, Sandia National Laboratories
+   Steve Plimpton, sjplimp@sandia.gov
+
+   Copyright (2003) Sandia Corporation.  Under the terms of Contract
+   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+   certain rights in this software.  This software is distributed under
+   the GNU General Public License.
+
+   See the README file in the top-level LAMMPS directory.
+------------------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------
+   Contributing author: Trung Dac Nguyen (ndactrung@gmail.com)
+   Ref: Wang, Yu, Langston, Fraige, Particle shape effects in discrete
+   element modelling of cohesive angular particles, Granular Matter 2011,
+   13:1-12.
+   Note: The current implementation has not taken into account
+         the contact history for friction forces.
+------------------------------------------------------------------------- */
+
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include "pair_body_rounded_polyhedron.h"
+#include "math_extra.h"
+#include "atom.h"
+#include "atom_vec_body.h"
+#include "body_rounded_polyhedron.h"
+#include "comm.h"
+#include "force.h"
+#include "fix.h"
+#include "modify.h"
+#include "neighbor.h"
+#include "neigh_list.h"
+#include "memory.h"
+#include "error.h"
+#include "math_extra.h"
+#include "math_const.h"
+
+using namespace LAMMPS_NS;
+using namespace MathExtra;
+using namespace MathConst;
+
+#define DELTA 10000
+#define EPSILON 1e-3
+#define MAX_FACE_SIZE 4 // maximum number of vertices per face (same as BodyRoundedPolyhedron)
+#define MAX_CONTACTS 16  // for 3D models
+
+//#define _POLYHEDRON_DEBUG
+
+enum {EE_INVALID=0,EE_NONE,EE_INTERACT};
+enum {EF_INVALID=0,EF_NONE,EF_PARALLEL,EF_SAME_SIDE_OF_FACE,
+      EF_INTERSECT_INSIDE,EF_INTERSECT_OUTSIDE};
+
+/* ---------------------------------------------------------------------- */
+
+PairBodyRoundedPolyhedron::PairBodyRoundedPolyhedron(LAMMPS *lmp) : Pair(lmp)
+{
+  dmax = nmax = 0;
+  discrete = NULL;
+  dnum = dfirst = NULL;
+
+  edmax = ednummax = 0;
+  edge = NULL;
+  ednum = edfirst = NULL;
+
+  facmax = facnummax = 0;
+  face = NULL;
+  facnum = facfirst = NULL;
+
+  enclosing_radius = NULL;
+  rounded_radius = NULL;
+  maxerad = NULL;
+
+  single_enable = 0;
+  restartinfo = 0;
+
+  c_n = 0.1;
+  c_t = 0.2;
+  mu = 0.0;
+  A_ua = 1.0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+PairBodyRoundedPolyhedron::~PairBodyRoundedPolyhedron()
+{
+  memory->destroy(discrete);
+  memory->destroy(dnum);
+  memory->destroy(dfirst);
+
+  memory->destroy(edge);
+  memory->destroy(ednum);
+  memory->destroy(edfirst);
+
+  memory->destroy(face);
+  memory->destroy(facnum);
+  memory->destroy(facfirst);
+
+  memory->destroy(enclosing_radius);
+  memory->destroy(rounded_radius);
+  memory->destroy(maxerad);
+
+  if (allocated) {
+    memory->destroy(setflag);
+    memory->destroy(cutsq);
+
+    memory->destroy(k_n);
+    memory->destroy(k_na);
+  }
+}
+
+/* ---------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::compute(int eflag, int vflag)
+{
+  int i,j,ii,jj,inum,jnum,itype,jtype;
+  int ni,nj,npi,npj,ifirst,jfirst,nei,nej,iefirst,jefirst;
+  double xtmp,ytmp,ztmp,delx,dely,delz,evdwl,facc[3];
+  double rsq,eradi,eradj,k_nij,k_naij;
+  int *ilist,*jlist,*numneigh,**firstneigh;
+
+  evdwl = 0.0;
+  if (eflag || vflag) ev_setup(eflag,vflag);
+  else evflag = vflag_fdotr = 0;
+
+  double **x = atom->x;
+  double **v = atom->v;
+  double **f = atom->f;
+  double **torque = atom->torque;
+  double **angmom = atom->angmom;
+  int *body = atom->body;
+  int *type = atom->type;
+  int nlocal = atom->nlocal;
+  int nall = nlocal + atom->nghost;
+  int newton_pair = force->newton_pair;
+
+  inum = list->inum;
+  ilist = list->ilist;
+  numneigh = list->numneigh;
+  firstneigh = list->firstneigh;
+
+  // grow the per-atom lists if necessary and initialize
+
+  if (atom->nmax > nmax) {
+    memory->destroy(dnum);
+    memory->destroy(dfirst);
+    memory->destroy(ednum);
+    memory->destroy(edfirst);
+    memory->destroy(facnum);
+    memory->destroy(facfirst);
+    memory->destroy(enclosing_radius);
+    memory->destroy(rounded_radius);
+    nmax = atom->nmax;
+    memory->create(dnum,nmax,"pair:dnum");
+    memory->create(dfirst,nmax,"pair:dfirst");
+    memory->create(ednum,nmax,"pair:ednum");
+    memory->create(edfirst,nmax,"pair:edfirst");
+    memory->create(facnum,nmax,"pair:facnum");
+    memory->create(facfirst,nmax,"pair:facfirst");
+    memory->create(enclosing_radius,nmax,"pair:enclosing_radius");
+    memory->create(rounded_radius,nmax,"pair:rounded_radius");
+  }
+
+  ndiscrete = nedge = nface = 0;
+  for (i = 0; i < nall; i++)
+    dnum[i] = ednum[i] = facnum[i] = 0;
+
+  // loop over neighbors of my atoms
+
+  for (ii = 0; ii < inum; ii++) {
+    i = ilist[ii];
+    xtmp = x[i][0];
+    ytmp = x[i][1];
+    ztmp = x[i][2];
+    itype = type[i];
+    jlist = firstneigh[i];
+    jnum = numneigh[i];
+
+    if (body[i] >= 0) {
+      if (dnum[i] == 0) body2space(i);
+      npi = dnum[i];
+      ifirst = dfirst[i];
+      nei = ednum[i];
+      iefirst = edfirst[i];
+      eradi = enclosing_radius[i];
+     }
+
+    for (jj = 0; jj < jnum; jj++) {
+      j = jlist[jj];
+      j &= NEIGHMASK;
+
+      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];
+
+      // body/body interactions
+
+      evdwl = 0.0;
+      facc[0] = facc[1] = facc[2] = 0;
+
+      if (body[i] < 0 || body[j] < 0) continue;
+
+      if (dnum[j] == 0) body2space(j);
+      npj = dnum[j];
+      jfirst = dfirst[j];
+      nej = ednum[j];
+      jefirst = edfirst[j];
+      eradj = enclosing_radius[j];
+      
+      k_nij = k_n[itype][jtype];
+      k_naij = k_na[itype][jtype];
+
+      // no interaction
+
+      double r = sqrt(rsq);
+      if (r > eradi + eradj + cut_inner) continue;
+
+      // sphere-sphere interaction
+
+      if (npi == 1 && npj == 1) {
+        sphere_against_sphere(i, j, delx, dely, delz, rsq,
+                              k_nij, k_naij, v, f, evflag);
+        continue;
+      }
+
+      // reset vertex and edge forces
+
+      for (ni = 0; ni < npi; ni++) {
+        discrete[ifirst+ni][3] = 0;
+        discrete[ifirst+ni][4] = 0;
+        discrete[ifirst+ni][5] = 0;
+        discrete[ifirst+ni][6] = 0;
+      }
+
+      for (nj = 0; nj < npj; nj++) {
+        discrete[jfirst+nj][3] = 0;
+        discrete[jfirst+nj][4] = 0;
+        discrete[jfirst+nj][5] = 0;
+        discrete[jfirst+nj][6] = 0;
+      }
+
+      for (ni = 0; ni < nei; ni++) {
+        edge[iefirst+ni][2] = 0;
+        edge[iefirst+ni][3] = 0;
+        edge[iefirst+ni][4] = 0;
+        edge[iefirst+ni][5] = 0;
+      }
+
+      for (nj = 0; nj < nej; nj++) {
+        edge[jefirst+nj][2] = 0;
+        edge[jefirst+nj][3] = 0;
+        edge[jefirst+nj][4] = 0;
+        edge[jefirst+nj][5] = 0;
+      }
+
+      // one of the two bodies is a sphere
+
+      if (npj == 1) {
+        sphere_against_face(i, j, k_nij, k_naij, x, v, f, torque,
+                            angmom, evflag);
+        sphere_against_edge(i, j, k_nij, k_naij, x, v, f, torque,
+                            angmom, evflag);
+        continue;
+      } else if (npi == 1) {
+        sphere_against_face(j, i, k_nij, k_naij, x, v, f, torque,
+                            angmom, evflag);
+        sphere_against_edge(j, i, k_nij, k_naij, x, v, f, torque,
+                            angmom, evflag);
+        continue;
+      }
+
+      int interact, num_contacts;
+      Contact contact_list[MAX_CONTACTS];
+
+      num_contacts = 0;
+
+      // check interaction between i's edges and j' faces
+      #ifdef _POLYHEDRON_DEBUG
+      printf("INTERACTION between edges of %d vs. faces of %d:\n", i, j);
+      #endif 
+      interact = edge_against_face(i, j, k_nij, k_naij, x, contact_list,
+                                   num_contacts, evdwl, facc);
+
+      // check interaction between j's edges and i' faces
+      #ifdef _POLYHEDRON_DEBUG
+      printf("\nINTERACTION between edges of %d vs. faces of %d:\n", j, i);
+      #endif
+      interact = edge_against_face(j, i, k_nij, k_naij, x, contact_list,
+                                   num_contacts, evdwl, facc);
+
+      // check interaction between i's edges and j' edges
+      #ifdef _POLYHEDRON_DEBUG
+      printf("INTERACTION between edges of %d vs. edges of %d:\n", i, j);
+      #endif 
+      interact = edge_against_edge(i, j, k_nij, k_naij, x, contact_list,
+                                   num_contacts, evdwl, facc);
+
+      // estimate the contact area
+      // also consider point contacts and line contacts
+
+      if (num_contacts > 0) {
+        double contact_area;
+        if (num_contacts == 1) {
+          contact_area = 0;
+        } else if (num_contacts == 2) {
+          contact_area = num_contacts * A_ua;
+        } else {
+          int m;
+          double xc[3],dx,dy,dz;
+          xc[0] = xc[1] = xc[2] = 0;
+          for (m = 0; m < num_contacts; m++) {
+            xc[0] += contact_list[m].xi[0];
+            xc[1] += contact_list[m].xi[1];
+            xc[2] += contact_list[m].xi[2];
+          }
+
+          xc[0] /= (double)num_contacts;
+          xc[1] /= (double)num_contacts;
+          xc[2] /= (double)num_contacts;
+
+          contact_area = 0.0;
+          for (m = 0; m < num_contacts; m++) {
+            dx = contact_list[m].xi[0] - xc[0];
+            dy = contact_list[m].xi[1] - xc[1];
+            dz = contact_list[m].xi[2] - xc[2];
+            contact_area += (dx*dx + dy*dy + dz*dz);
+          }
+          contact_area *= (MY_PI/(double)num_contacts);
+        }
+        rescale_cohesive_forces(x, f, torque, contact_list, num_contacts,
+                                contact_area, k_nij, k_naij, facc);
+      }
+
+      if (evflag) ev_tally_xyz(i,j,nlocal,newton_pair,evdwl,0.0,
+                               facc[0],facc[1],facc[2],delx,dely,delz);
+
+    } // end for jj
+  }
+
+  if (vflag_fdotr) virial_fdotr_compute();
+}
+
+/* ----------------------------------------------------------------------
+   allocate all arrays
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::allocate()
+{
+  allocated = 1;
+  int n = atom->ntypes;
+
+  memory->create(setflag,n+1,n+1,"pair:setflag");
+  for (int i = 1; i <= n; i++)
+    for (int j = i; j <= n; j++)
+      setflag[i][j] = 0;
+
+  memory->create(cutsq,n+1,n+1,"pair:cutsq");
+
+  memory->create(k_n,n+1,n+1,"pair:k_n");
+  memory->create(k_na,n+1,n+1,"pair:k_na");
+  memory->create(maxerad,n+1,"pair:maxerad");
+}
+
+/* ----------------------------------------------------------------------
+   global settings
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::settings(int narg, char **arg)
+{
+  if (narg < 5) error->all(FLERR,"Illegal pair_style command");
+
+  c_n = force->numeric(FLERR,arg[0]);
+  c_t = force->numeric(FLERR,arg[1]);
+  mu = force->numeric(FLERR,arg[2]);
+  A_ua = force->numeric(FLERR,arg[3]);
+  cut_inner = force->numeric(FLERR,arg[4]);
+}
+
+/* ----------------------------------------------------------------------
+   set coeffs for one or more type pairs
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::coeff(int narg, char **arg)
+{
+  if (narg < 4 || narg > 5)
+    error->all(FLERR,"Incorrect args for pair coefficients");
+  if (!allocated) allocate();
+
+  int ilo,ihi,jlo,jhi;
+  force->bounds(FLERR,arg[0],atom->ntypes,ilo,ihi);
+  force->bounds(FLERR,arg[1],atom->ntypes,jlo,jhi);
+
+  double k_n_one = force->numeric(FLERR,arg[2]);
+  double k_na_one = force->numeric(FLERR,arg[3]);
+
+  int count = 0;
+  for (int i = ilo; i <= ihi; i++) {
+    for (int j = MAX(jlo,i); j <= jhi; j++) {
+      k_n[i][j] = k_n_one;
+      k_na[i][j] = k_na_one;
+      setflag[i][j] = 1;
+      count++;
+    }
+  }
+
+  if (count == 0) error->all(FLERR,"Incorrect args for pair coefficients");
+}
+
+/* ----------------------------------------------------------------------
+   init specific to this pair style
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::init_style()
+{
+  avec = (AtomVecBody *) atom->style_match("body");
+  if (!avec) error->all(FLERR,"Pair body/rounded/polyhedron requires atom style body");
+  if (strcmp(avec->bptr->style,"rounded/polyhedron") != 0)
+    error->all(FLERR,"Pair body/rounded/polyhedron requires body style rounded/polyhedron");
+  bptr = (BodyRoundedPolyhedron *) avec->bptr;
+
+  if (comm->ghost_velocity == 0)
+    error->all(FLERR,"Pair body/rounded/polyhedron requires ghost atoms store velocity");
+
+  neighbor->request(this);
+
+  // find the maximum enclosing radius for each atom type
+
+  int i, itype;
+  double eradi;
+  int* body = atom->body;
+  int* type = atom->type;
+  int ntypes = atom->ntypes;
+  int nlocal = atom->nlocal;
+
+  if (atom->nmax > nmax) {
+    memory->destroy(dnum);
+    memory->destroy(dfirst);
+    memory->destroy(ednum);
+    memory->destroy(edfirst);
+    memory->destroy(facnum);
+    memory->destroy(facfirst);
+    memory->destroy(enclosing_radius);
+    memory->destroy(rounded_radius);
+    nmax = atom->nmax;
+    memory->create(dnum,nmax,"pair:dnum");
+    memory->create(dfirst,nmax,"pair:dfirst");
+    memory->create(ednum,nmax,"pair:ednum");
+    memory->create(edfirst,nmax,"pair:edfirst");
+    memory->create(facnum,nmax,"pair:facnum");
+    memory->create(facfirst,nmax,"pair:facfirst");
+    memory->create(enclosing_radius,nmax,"pair:enclosing_radius");
+    memory->create(rounded_radius,nmax,"pair:rounded_radius");
+  }
+
+  ndiscrete = nedge = nface = 0;
+  for (i = 0; i < nlocal; i++)
+    dnum[i] = ednum[i] = facnum[i] = 0;
+
+  double *merad = NULL;
+  memory->create(merad,ntypes+1,"pair:merad");
+  for (i = 1; i <= ntypes; i++)
+    maxerad[i] = merad[i] = 0;
+
+  int ipour;
+  for (ipour = 0; ipour < modify->nfix; ipour++)
+    if (strcmp(modify->fix[ipour]->style,"pour") == 0) break;
+  if (ipour == modify->nfix) ipour = -1;
+
+  int idep;
+  for (idep = 0; idep < modify->nfix; idep++)
+    if (strcmp(modify->fix[idep]->style,"deposit") == 0) break;
+  if (idep == modify->nfix) idep = -1;
+
+  for (i = 1; i <= ntypes; i++) {
+    merad[i] = 0.0;
+    if (ipour >= 0) {
+      itype = i;
+      merad[i] =
+        *((double *) modify->fix[ipour]->extract("radius",itype));
+    }
+    if (idep >= 0) {
+      itype = i;
+      merad[i] =
+        *((double *) modify->fix[idep]->extract("radius",itype));
+    }
+  }
+
+  for (i = 0; i < nlocal; i++) {
+    itype = type[i];
+    if (body[i] >= 0) {
+      if (dnum[i] == 0) body2space(i);
+      eradi = enclosing_radius[i];
+      if (eradi > merad[itype]) merad[itype] = eradi;
+    } else 
+      merad[itype] = 0;
+  }
+
+  MPI_Allreduce(&merad[1],&maxerad[1],ntypes,MPI_DOUBLE,MPI_MAX,world);
+
+  memory->destroy(merad);
+
+  sanity_check();
+}
+
+/* ----------------------------------------------------------------------
+   init for one type pair i,j and corresponding j,i
+------------------------------------------------------------------------- */
+
+double PairBodyRoundedPolyhedron::init_one(int i, int j)
+{
+  k_n[j][i] = k_n[i][j];
+  k_na[j][i] = k_na[i][j];
+
+  return (maxerad[i]+maxerad[j]);
+}
+
+/* ----------------------------------------------------------------------
+   convert N sub-particles in body I to space frame using current quaternion
+   store sub-particle space-frame displacements from COM in discrete list
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::body2space(int i)
+{
+  int ibonus = atom->body[i];
+  AtomVecBody::Bonus *bonus = &avec->bonus[ibonus];
+  int nsub = bptr->nsub(bonus);
+  double *coords = bptr->coords(bonus);
+  int body_num_edges = bptr->nedges(bonus);
+  double* edge_ends = bptr->edges(bonus);
+  int body_num_faces = bptr->nfaces(bonus);
+  double* face_pts = bptr->faces(bonus);
+  double eradius = bptr->enclosing_radius(bonus);
+  double rradius = bptr->rounded_radius(bonus);
+
+  // get the number of sub-particles (vertices)
+  // and the index of the first vertex of my body in the list
+
+  dnum[i] = nsub;
+  dfirst[i] = ndiscrete;
+
+  // grow the vertex list if necessary
+  // the first 3 columns are for coords, the last 3 for forces
+
+  if (ndiscrete + nsub > dmax) {
+    dmax += DELTA;
+    memory->grow(discrete,dmax,7,"pair:discrete");
+  }
+
+  double p[3][3];
+  MathExtra::quat_to_mat(bonus->quat,p);
+
+  for (int m = 0; m < nsub; m++) {
+    MathExtra::matvec(p,&coords[3*m],discrete[ndiscrete]);
+    discrete[ndiscrete][3] = 0;
+    discrete[ndiscrete][4] = 0;
+    discrete[ndiscrete][5] = 0;
+    discrete[ndiscrete][6] = 0;
+    ndiscrete++;
+  }
+
+  // get the number of edges (vertices)
+  // and the index of the first edge of my body in the list
+
+  ednum[i] = body_num_edges;
+  edfirst[i] = nedge;
+
+  // grow the edge list if necessary
+  // the first 2 columns are for vertex indices within body, the last 3 for forces
+
+  if (nedge + body_num_edges > edmax) {
+    edmax += DELTA;
+    memory->grow(edge,edmax,6,"pair:edge");
+  }
+
+  for (int m = 0; m < body_num_edges; m++) {
+    edge[nedge][0] = static_cast<int>(edge_ends[2*m+0]);
+    edge[nedge][1] = static_cast<int>(edge_ends[2*m+1]);
+    edge[nedge][2] = 0;
+    edge[nedge][3] = 0;
+    edge[nedge][4] = 0;
+    edge[nedge][5] = 0;
+    nedge++;
+  }
+
+  // get the number of faces and the index of the first face
+
+  facnum[i] = body_num_faces;
+  facfirst[i] = nface;
+
+  // grow the face list if necessary
+  // the first 3 columns are for vertex indices within body, the last 3 for forces
+
+  if (nface + body_num_faces > facmax) {
+    facmax += DELTA;
+    memory->grow(face,facmax,MAX_FACE_SIZE,"pair:face");
+  }
+
+  for (int m = 0; m < body_num_faces; m++) {
+    for (int k = 0; k < MAX_FACE_SIZE; k++)
+      face[nface][k] = static_cast<int>(face_pts[MAX_FACE_SIZE*m+k]);
+    nface++;
+  }
+
+  enclosing_radius[i] = eradius;
+  rounded_radius[i] = rradius;
+}
+
+/* ----------------------------------------------------------------------
+   Interaction between two spheres with different radii
+   according to the 2D model from Fraige et al.
+---------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::sphere_against_sphere(int ibody, int jbody,
+                       double delx, double dely, double delz, double rsq,
+                       double k_n, double k_na, double** v, double** f,
+                       int evflag)
+{
+  double rradi,rradj,contact_dist;
+  double vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3;
+  double rij,rsqinv,R,fx,fy,fz,fn[3],ft[3],fpair,shift,energy;
+  int nlocal = atom->nlocal;
+  int newton_pair = force->newton_pair;
+
+  rradi = rounded_radius[ibody];
+  rradj = rounded_radius[jbody];
+  contact_dist = rradi + rradj;
+
+  rij = sqrt(rsq);
+  R = rij - contact_dist;
+  shift = k_na * cut_inner;
+
+  energy = 0;
+
+  if (R <= 0) {           // deformation occurs
+    fpair = -k_n * R - shift;
+    energy = (0.5 * k_n * R + shift) * R;
+  } else if (R <= cut_inner) {   // not deforming but cohesive ranges overlap
+    fpair = k_na * R - shift;
+    energy = (-0.5 * k_na * R + shift) * R;
+  } else fpair = 0.0;
+
+  fx = delx*fpair/rij;
+  fy = dely*fpair/rij;
+  fz = delz*fpair/rij;
+
+  if (R <= 0) { // in contact
+
+    // relative translational velocity
+
+    vr1 = v[ibody][0] - v[jbody][0];
+    vr2 = v[ibody][1] - v[jbody][1];
+    vr3 = v[ibody][2] - v[jbody][2];
+
+    // normal component
+    
+    rsqinv = 1.0/rsq;
+    vnnr = vr1*delx + vr2*dely + vr3*delz;
+    vn1 = delx*vnnr * rsqinv;
+    vn2 = dely*vnnr * rsqinv;
+    vn3 = delz*vnnr * rsqinv;
+
+    // tangential component
+
+    vt1 = vr1 - vn1;
+    vt2 = vr2 - vn2;
+    vt3 = vr3 - vn3;
+
+    // normal friction term at contact
+
+    fn[0] = -c_n * vn1;
+    fn[1] = -c_n * vn2;
+    fn[2] = -c_n * vn3;
+
+    // tangential friction term at contact,
+    // excluding the tangential deformation term for now
+
+    ft[0] = -c_t * vt1;
+    ft[1] = -c_t * vt2;
+    ft[2] = -c_t * vt3;
+
+    fx += fn[0] + ft[0];
+    fy += fn[1] + ft[1];
+    fz += fn[2] + ft[2];
+  }
+
+  f[ibody][0] += fx;
+  f[ibody][1] += fy;
+  f[ibody][2] += fz;
+  
+  if (newton_pair || jbody < nlocal) {
+    f[jbody][0] -= fx;
+    f[jbody][1] -= fy;
+    f[jbody][2] -= fz;
+  }
+
+  if (evflag) ev_tally_xyz(ibody,jbody,nlocal,newton_pair,
+                           energy,0.0,fx,fy,fz,delx,dely,delz);
+}
+
+/* ----------------------------------------------------------------------
+   Interaction bt the faces of a polyhedron (ibody) and a sphere (jbody)
+---------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::sphere_against_face(int ibody, int jbody,
+                       double k_n, double k_na, double** x, double** v,
+                       double** f, double** torque, double** angmom,
+                       int evflag)
+{
+  int ni,nfi,inside,ifirst,iffirst,npi1,npi2,npi3,ibonus,tmp;
+  double xi1[3],xi2[3],xi3[3],ui[3],vi[3],vti[3],n[3],h[3],fn[3],ft[3],d;
+  double delx,dely,delz,rsq,rij,rsqinv,R,fx,fy,fz,fpair,shift,energy;
+  double rradi,rradj,contact_dist;
+  double vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3;
+  double *quat, *inertia;
+  AtomVecBody::Bonus *bonus;
+
+  int nlocal = atom->nlocal;
+  int newton_pair = force->newton_pair;
+
+  ifirst = dfirst[ibody];
+  iffirst = facfirst[ibody];
+  nfi = facnum[ibody];
+
+  rradi = rounded_radius[ibody];
+  rradj = rounded_radius[jbody];
+  contact_dist = rradi + rradj;
+
+  for (ni = 0; ni < nfi; ni++) {
+
+    npi1 = static_cast<int>(face[iffirst+ni][0]);
+    npi2 = static_cast<int>(face[iffirst+ni][1]);
+    npi3 = static_cast<int>(face[iffirst+ni][2]);
+
+    // compute the space-fixed coordinates for the vertices of the face
+
+    xi1[0] = x[ibody][0] + discrete[ifirst+npi1][0];
+    xi1[1] = x[ibody][1] + discrete[ifirst+npi1][1];
+    xi1[2] = x[ibody][2] + discrete[ifirst+npi1][2];
+
+    xi2[0] = x[ibody][0] + discrete[ifirst+npi2][0];
+    xi2[1] = x[ibody][1] + discrete[ifirst+npi2][1];
+    xi2[2] = x[ibody][2] + discrete[ifirst+npi2][2];
+
+    xi3[0] = x[ibody][0] + discrete[ifirst+npi3][0];
+    xi3[1] = x[ibody][1] + discrete[ifirst+npi3][1];
+    xi3[2] = x[ibody][2] + discrete[ifirst+npi3][2];
+
+    // find the normal unit vector of the face
+  
+    MathExtra::sub3(xi2, xi1, ui);
+    MathExtra::sub3(xi3, xi1, vi);
+    MathExtra::cross3(ui, vi, n);
+    MathExtra::norm3(n);
+
+    // skip if the COM of the two bodies are in the same side of the face
+
+    if (opposite_sides(n, xi1, x[ibody], x[jbody]) == 0) continue;
+
+    // find the projection of the sphere on the face
+
+    project_pt_plane(x[jbody], xi1, xi2, xi3, h, d, inside);
+
+    inside_polygon(ibody, ni, x[ibody], h, NULL, inside, tmp);
+    if (inside == 0) continue;
+
+    delx = h[0] - x[jbody][0];
+    dely = h[1] - x[jbody][1];
+    delz = h[2] - x[jbody][2];
+    rsq = delx*delx + dely*dely + delz*delz;
+    rij = sqrt(rsq);
+    R = rij - contact_dist;
+    shift = k_na * cut_inner;
+
+    energy = 0;
+
+    if (R <= 0) { // deformation occurs
+      fpair = -k_n * R - shift;
+      energy = (0.5 * k_n * R + shift) * R;
+    } else if (R <= cut_inner) { // not deforming but cohesive ranges overlap
+      fpair = k_na * R - shift;
+      energy = (-0.5 * k_na * R + shift) * R;
+    } else fpair = 0.0;
+
+    fx = delx*fpair/rij;
+    fy = dely*fpair/rij;
+    fz = delz*fpair/rij;
+
+    if (R <= 0) { // in contact
+
+      // compute the velocity of the vertex in the space-fixed frame
+
+      ibonus = atom->body[ibody];
+      bonus = &avec->bonus[ibonus];
+      quat = bonus->quat;
+      inertia = bonus->inertia;
+      total_velocity(h, x[ibody], v[ibody], angmom[ibody],
+                     inertia, quat, vti);
+
+      // relative translational velocity
+
+      vr1 = vti[0] - v[jbody][0];
+      vr2 = vti[1] - v[jbody][1];
+      vr3 = vti[2] - v[jbody][2];
+
+      // normal component
+
+      rsqinv = 1.0/rsq;
+      vnnr = vr1*delx + vr2*dely + vr3*delz;
+      vn1 = delx*vnnr * rsqinv;
+      vn2 = dely*vnnr * rsqinv;
+      vn3 = delz*vnnr * rsqinv;
+
+      // tangential component
+
+      vt1 = vr1 - vn1;
+      vt2 = vr2 - vn2;
+      vt3 = vr3 - vn3;
+
+      // normal friction term at contact
+
+      fn[0] = -c_n * vn1;
+      fn[1] = -c_n * vn2;
+      fn[2] = -c_n * vn3;
+
+      // tangential friction term at contact,
+      // excluding the tangential deformation term for now
+
+      ft[0] = -c_t * vt1;
+      ft[1] = -c_t * vt2;
+      ft[2] = -c_t * vt3;
+
+      fx += fn[0] + ft[0];
+      fy += fn[1] + ft[1];
+      fz += fn[2] + ft[2];
+    }
+
+    f[ibody][0] += fx;
+    f[ibody][1] += fy;
+    f[ibody][2] += fz;
+    sum_torque(x[ibody], h, fx, fy, fz, torque[ibody]);
+
+    if (newton_pair || jbody < nlocal) {
+      f[jbody][0] -= fx;
+      f[jbody][1] -= fy;
+      f[jbody][2] -= fz;
+    }
+
+    if (evflag) ev_tally_xyz(ibody,jbody,nlocal,newton_pair,
+                           energy,0.0,fx,fy,fz,delx,dely,delz);
+  }
+}
+
+/* ----------------------------------------------------------------------
+   Interaction bt the edges of a polyhedron (ibody) and a sphere (jbody)
+---------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::sphere_against_edge(int ibody, int jbody,
+                       double k_n, double k_na, double** x, double** v,
+                       double** f, double** torque, double** angmom,
+                       int evflag)
+{
+  int ni,nei,ifirst,iefirst,npi1,npi2,ibonus;
+  double xi1[3],xi2[3],vti[3],h[3],fn[3],ft[3],d,t;
+  double delx,dely,delz,rij,rsqinv,R,fx,fy,fz,fpair,shift,energy;
+  double rradi,rradj,contact_dist;
+  double vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3;
+  double *quat, *inertia;
+  AtomVecBody::Bonus *bonus;
+
+  int nlocal = atom->nlocal;
+  int newton_pair = force->newton_pair;
+
+  ifirst = dfirst[ibody];
+  iefirst = edfirst[ibody];
+  nei = ednum[ibody];
+
+  rradi = rounded_radius[ibody];
+  rradj = rounded_radius[jbody];
+  contact_dist = rradi + rradj;
+
+  for (ni = 0; ni < nei; ni++) {
+
+    npi1 = static_cast<int>(edge[iefirst+ni][0]);
+    npi2 = static_cast<int>(edge[iefirst+ni][1]);
+
+    // compute the space-fixed coordinates for the vertices of the face
+
+    xi1[0] = x[ibody][0] + discrete[ifirst+npi1][0];
+    xi1[1] = x[ibody][1] + discrete[ifirst+npi1][1];
+    xi1[2] = x[ibody][2] + discrete[ifirst+npi1][2];
+
+    xi2[0] = x[ibody][0] + discrete[ifirst+npi2][0];
+    xi2[1] = x[ibody][1] + discrete[ifirst+npi2][1];
+    xi2[2] = x[ibody][2] + discrete[ifirst+npi2][2];
+
+    // find the projection of the jbody's COM on the edge
+
+    project_pt_line(x[jbody], xi1, xi2, h, d, t);
+
+    if (d > contact_dist + cut_inner) continue;
+    if (t < 0 || t > 1) continue;
+
+    if (fabs(t) < EPSILON) {
+      if (static_cast<int>(discrete[ifirst+npi1][6]) == 1)
+        continue;
+      else {
+        h[0] = xi1[0];
+        h[1] = xi1[1];
+        h[2] = xi1[2];
+        discrete[ifirst+npi1][6] = 1;
+      }
+    }
+
+    if (fabs(t-1) < EPSILON) {
+      if (static_cast<int>(discrete[ifirst+npi2][6]) == 1)
+        continue;
+      else {
+        h[0] = xi2[0];
+        h[1] = xi2[1];
+        h[2] = xi2[2];
+        discrete[ifirst+npi2][6] = 1;
+      }
+    }
+
+    delx = h[0] - x[jbody][0];
+    dely = h[1] - x[jbody][1];
+    delz = h[2] - x[jbody][2];
+    rij = sqrt(delx*delx + dely*dely + delz*delz);
+    R = rij - contact_dist;
+    shift = k_na * cut_inner;
+
+    energy = 0;
+
+    if (R <= 0) {           // deformation occurs
+      fpair = -k_n * R - shift;
+      energy = (0.5 * k_n * R + shift) * R;
+    } else if (R <= cut_inner) {   // not deforming but cohesive ranges overlap
+      fpair = k_na * R - shift;
+      energy = (-0.5 * k_na * R + shift) * R;
+    } else fpair = 0.0;
+
+    fx = delx*fpair/rij;
+    fy = dely*fpair/rij;
+    fz = delz*fpair/rij;
+
+    if (R <= 0) { // in contact
+
+      // compute the velocity of the vertex in the space-fixed frame
+
+      ibonus = atom->body[ibody];
+      bonus = &avec->bonus[ibonus];
+      quat = bonus->quat;
+      inertia = bonus->inertia;
+      total_velocity(h, x[ibody], v[ibody], angmom[ibody],
+                     inertia, quat, vti);
+
+      // relative translational velocity
+
+      vr1 = vti[0] - v[jbody][0];
+      vr2 = vti[1] - v[jbody][1];
+      vr3 = vti[2] - v[jbody][2];
+
+      // normal component
+
+      vnnr = vr1*delx + vr2*dely + vr3*delz;
+      vn1 = delx*vnnr * rsqinv;
+      vn2 = dely*vnnr * rsqinv;
+      vn3 = delz*vnnr * rsqinv;
+
+      // tangential component
+
+      vt1 = vr1 - vn1;
+      vt2 = vr2 - vn2;
+      vt3 = vr3 - vn3;
+
+      // normal friction term at contact
+
+      fn[0] = -c_n * vn1;
+      fn[1] = -c_n * vn2;
+      fn[2] = -c_n * vn3;
+
+      // tangential friction term at contact, excluding the tangential deformation term
+
+      ft[0] = -c_t * vt1;
+      ft[1] = -c_t * vt2;
+      ft[2] = -c_t * vt3;
+
+      fx += fn[0] + ft[0];
+      fy += fn[1] + ft[1];
+      fz += fn[2] + ft[2];
+    }
+
+    f[ibody][0] += fx;
+    f[ibody][1] += fy;
+    f[ibody][2] += fz;
+    sum_torque(x[ibody], h, fx, fy, fz, torque[ibody]);
+
+    if (newton_pair || jbody < nlocal) {
+      f[jbody][0] -= fx;
+      f[jbody][1] -= fy;
+      f[jbody][2] -= fz;
+    }
+
+    if (evflag) ev_tally_xyz(ibody,jbody,nlocal,newton_pair,
+                           energy,0.0,fx,fy,fz,delx,dely,delz);
+  }
+
+}
+
+/* ----------------------------------------------------------------------
+   Determine the interaction mode between i's edges against j's faces
+
+   i = atom i (body i)
+   j = atom j (body j)
+   x      = atoms' coordinates
+   f      = atoms' forces
+   torque = atoms' torques
+   tag    = atoms' tags
+   contact_list = list of contacts
+   num_contacts = number of contacts between i's edges and j's faces
+   Return:
+
+---------------------------------------------------------------------- */
+
+int PairBodyRoundedPolyhedron::edge_against_face(int ibody, int jbody,
+             double k_n, double k_na, double** x, Contact* contact_list,
+             int &num_contacts, double &evdwl, double* facc)
+{
+  int ni,nei,nj,nfj,contact,interact;
+  double rradi,rradj,energy;
+
+  nei = ednum[ibody];
+  rradi = rounded_radius[ibody];
+  nfj = facnum[jbody];
+  rradj = rounded_radius[jbody];
+
+  energy = 0;
+  interact = EF_NONE;
+
+  // loop through body i's edges
+
+  for (ni = 0; ni < nei; ni++) {
+
+    // loop through body j's faces
+
+    for (nj = 0; nj < nfj; nj++) {
+
+      // compute the distance between the face nj to the edge ni
+      #ifdef _POLYHEDRON_DEBUG
+      printf("Compute interaction between face %d of body %d with edge %d of body %d:\n",
+             nj, jbody, ni, ibody);
+      #endif
+
+      interact = interaction_face_to_edge(jbody, nj, x[jbody], rradj,
+                                          ibody, ni, x[ibody], rradi,
+                                          k_n, k_na, cut_inner,
+                                          contact_list, num_contacts,
+                                          energy, facc);
+    } 
+
+  } // end for looping through the edges of body i
+
+  evdwl += energy;
+
+  return interact;
+}
+
+/* ----------------------------------------------------------------------
+   Determine the interaction mode between i's edges against j's edges
+
+   i = atom i (body i)
+   j = atom j (body j)
+   x      = atoms' coordinates
+   f      = atoms' forces
+   torque = atoms' torques
+   tag    = atoms' tags
+   contact_list = list of contacts
+   num_contacts = number of contacts between i's edges and j's edges
+   Return:
+
+---------------------------------------------------------------------- */
+
+int PairBodyRoundedPolyhedron::edge_against_edge(int ibody, int jbody,
+             double k_n, double k_na, double** x, Contact* contact_list,
+             int &num_contacts, double &evdwl, double* facc)
+{
+  int ni,nei,nj,nej,contact,interact;
+  double rradi,rradj,energy;
+
+  nei = ednum[ibody];
+  rradi = rounded_radius[ibody];
+  nej = ednum[jbody];
+  rradj = rounded_radius[jbody];
+
+  energy = 0;
+  interact = EE_NONE;
+
+  // loop through body i's edges
+
+  for (ni = 0; ni < nei; ni++) {
+
+    for (nj = 0; nj < nej; nj++) {
+
+      // compute the distance between the edge nj to the edge ni
+      #ifdef _POLYHEDRON_DEBUG
+      printf("Compute interaction between edge %d of body %d with edge %d of body %d:\n",
+             nj, jbody, ni, ibody);
+      #endif
+
+      interact = interaction_edge_to_edge(ibody, ni, x[ibody], rradi,
+                                          jbody, nj, x[jbody], rradj,
+                                          k_n, k_na, cut_inner,
+                                          contact_list, num_contacts,
+                                          energy, facc);
+    }
+
+  } // end for looping through the edges of body i
+
+  evdwl += energy;
+
+  return interact;
+}
+
+/* -------------------------------------------------------------------------
+  Compute the interaction between a face of body i and an edge from
+  another body
+  Input:
+    ibody      = body i (i.e. atom i)
+    face_index = face index of body i
+    xmi        = atom i's coordinates (body i's center of mass)
+    rounded_radius_i = rounded radius of the body i
+    jbody      = body i (i.e. atom j)
+    edge_index = coordinate of the tested edge from another body
+    xmj        = atom j's coordinates (body j's center of mass)
+    rounded_radius_j = rounded radius of the body j
+    cut_inner  = cutoff for vertex-vertex and vertex-edge interaction
+  Output:
+    d          = Distance from a point x0 to an edge
+    hi         = coordinates of the projection of x0 on the edge
+
+  contact      = 0 no contact between the queried edge and the face
+                 1 contact detected
+  return
+    INVALID if the face index is invalid
+    NONE    if there is no interaction
+------------------------------------------------------------------------- */
+
+int PairBodyRoundedPolyhedron::interaction_face_to_edge(int ibody,
+                                                int face_index,
+                                                double *xmi,
+                                                double rounded_radius_i,
+                                                int jbody,
+                                                int edge_index,
+                                                double *xmj,
+                                                double rounded_radius_j,
+                                                double k_n,
+                                                double k_na,
+                                                double cut_inner,
+                                                Contact* contact_list,
+                                                int &num_contacts,
+                                                double &energy,
+                                                double* facc)
+{
+  if (face_index >= facnum[ibody]) return EF_INVALID;
+
+  int ifirst,iffirst,jfirst,npi1,npi2,npi3;
+  int jefirst,npj1,npj2;
+  double xi1[3],xi2[3],xi3[3],xpj1[3],xpj2[3],ui[3],vi[3],n[3];
+
+  double** x = atom->x;
+  double** v = atom->v;
+  double** f = atom->f;
+  double** torque = atom->torque;
+  double** angmom = atom->angmom;
+
+  ifirst = dfirst[ibody];
+  iffirst = facfirst[ibody];
+  npi1 = static_cast<int>(face[iffirst+face_index][0]);
+  npi2 = static_cast<int>(face[iffirst+face_index][1]);
+  npi3 = static_cast<int>(face[iffirst+face_index][2]);
+
+  // compute the space-fixed coordinates for the vertices of the face
+
+  xi1[0] = xmi[0] + discrete[ifirst+npi1][0];
+  xi1[1] = xmi[1] + discrete[ifirst+npi1][1];
+  xi1[2] = xmi[2] + discrete[ifirst+npi1][2];
+
+  xi2[0] = xmi[0] + discrete[ifirst+npi2][0];
+  xi2[1] = xmi[1] + discrete[ifirst+npi2][1];
+  xi2[2] = xmi[2] + discrete[ifirst+npi2][2];
+
+  xi3[0] = xmi[0] + discrete[ifirst+npi3][0];
+  xi3[1] = xmi[1] + discrete[ifirst+npi3][1];
+  xi3[2] = xmi[2] + discrete[ifirst+npi3][2];
+
+  // find the normal unit vector of the face, ensure it point outward of the body
+  
+  MathExtra::sub3(xi2, xi1, ui);
+  MathExtra::sub3(xi3, xi1, vi);
+  MathExtra::cross3(ui, vi, n);
+  MathExtra::norm3(n);
+
+  double xc[3], dot, ans[3];
+  xc[0] = (xi1[0] + xi2[0] + xi3[0])/3.0;
+  xc[1] = (xi1[1] + xi2[1] + xi3[1])/3.0;
+  xc[2] = (xi1[2] + xi2[2] + xi3[2])/3.0;
+  MathExtra::sub3(xc, xmi, ans);
+  dot = MathExtra::dot3(ans, n);
+  if (dot < 0) MathExtra::negate3(n);
+
+  // two ends of the edge from body j
+
+  jfirst = dfirst[jbody];
+  jefirst = edfirst[jbody];
+  npj1 = static_cast<int>(edge[jefirst+edge_index][0]);
+  npj2 = static_cast<int>(edge[jefirst+edge_index][1]);
+
+  xpj1[0] = xmj[0] + discrete[jfirst+npj1][0];
+  xpj1[1] = xmj[1] + discrete[jfirst+npj1][1];
+  xpj1[2] = xmj[2] + discrete[jfirst+npj1][2];
+
+  xpj2[0] = xmj[0] + discrete[jfirst+npj2][0];
+  xpj2[1] = xmj[1] + discrete[jfirst+npj2][1];
+  xpj2[2] = xmj[2] + discrete[jfirst+npj2][2];
+
+  // no interaction if two ends of the edge are on the same side with the COM wrt the face
+
+  if (opposite_sides(n, xi1, xmi, xpj1) == 0 &&
+      opposite_sides(n, xi1, xmi, xpj2) == 0)
+    return EF_NONE;
+
+  // determine the intersection of the edge to the face
+
+  double hi1[3], hi2[3], d1, d2, contact_dist, shift;
+  int inside1 = 0;
+  int inside2 = 0;
+
+  // enum {EF_PARALLEL=0,EF_SAME_SIDE_OF_FACE,EF_INTERSECT_INSIDE,EF_INTERSECT_OUTSIDE};
+  int interact = edge_face_intersect(xi1, xi2, xi3, xpj1, xpj2,
+                                     hi1, hi2, d1, d2, inside1, inside2);
+
+  inside_polygon(ibody, face_index, xmi, hi1, hi2, inside1, inside2);
+
+  contact_dist = rounded_radius_i + rounded_radius_j;
+  shift = k_na * cut_inner;
+
+  // both endpoints are on the same side of, or parallel to, the face
+  // and both are out of the interaction zone
+
+  if (interact == EF_SAME_SIDE_OF_FACE || interact == EF_PARALLEL) {
+
+    if (d1 > contact_dist + cut_inner && d2 > contact_dist + cut_inner)
+      return EF_NONE;
+
+    int num_outside = 0;
+    int jflag = 1;
+
+    #ifdef _POLYHEDRON_DEBUG
+    if (interact == EF_SAME_SIDE_OF_FACE) 
+      printf(" - same side of face\n");
+    else if (interact == EF_PARALLEL) 
+      printf(" - parallel\n");
+    printf("     face: xi1 (%f %f %f) xi2 (%f %f %f) xi3 (%f %f %f)\n",
+      xi1[0], xi1[1], xi1[2], xi2[0], xi2[1], xi2[2], xi3[0], xi3[1], xi3[2]);
+    printf("     edge: xpj1 (%f %f %f) xpj2 (%f %f %f)\n",
+      xpj1[0], xpj1[1], xpj1[2], xpj2[0], xpj2[1], xpj2[2]);
+    #endif
+
+    // xpj1 is in the interaction zone
+    // and its projection on the face is inside the triangle
+    // compute vertex-face interaction and accumulate force/torque to both bodies
+
+    if (d1 <= contact_dist + cut_inner) {
+      if (inside1) {
+        if (static_cast<int>(discrete[jfirst+npj1][6]) == 0) {
+          pair_force_and_torque(jbody, ibody, xpj1, hi1, d1, contact_dist,
+                                k_n, k_na, shift, x, v, f, torque, angmom,
+                                jflag, energy, facc);
+          #ifdef _POLYHEDRON_DEBUG
+          printf(" - compute pair force between vertex %d from edge %d of body %d "
+                 "with face %d of body %d: d1 = %f\n",
+            npj1, edge_index, jbody, face_index, ibody, d1);
+          #endif
+
+          if (d1 <= contact_dist) {
+            // store the contact info
+            contact_list[num_contacts].ibody = ibody;
+            contact_list[num_contacts].jbody = jbody;
+            contact_list[num_contacts].xi[0] = hi1[0];
+            contact_list[num_contacts].xi[1] = hi1[1];
+            contact_list[num_contacts].xi[2] = hi1[2];
+            contact_list[num_contacts].xj[0] = xpj1[0];
+            contact_list[num_contacts].xj[1] = xpj1[1];
+            contact_list[num_contacts].xj[2] = xpj1[2];
+            contact_list[num_contacts].type = 0;
+            contact_list[num_contacts].separation = d1 - contact_dist;
+            num_contacts++;
+          }
+
+          discrete[jfirst+npj1][6] = 1;
+        }
+      } else {
+        num_outside++;
+      }
+    } 
+
+    // xpj2 is in the interaction zone 
+    // and its projection on the face is inside the triangle
+    // compute vertex-face interaction and accumulate force/torque to both bodies
+
+    if (d2 <= contact_dist + cut_inner) {
+      if (inside2) {
+        if (static_cast<int>(discrete[jfirst+npj2][6]) == 0) {
+          pair_force_and_torque(jbody, ibody, xpj2, hi2, d2, contact_dist,
+                                k_n, k_na, shift, x, v, f, torque, angmom,
+                                jflag, energy, facc);
+          #ifdef _POLYHEDRON_DEBUG
+          printf(" - compute pair force between vertex %d from edge %d of body %d "
+                 "with face %d of body %d: d2 = %f\n", 
+                 npj2, edge_index, jbody, face_index, ibody, d2);
+          #endif
+
+          if (d2 <= contact_dist) {
+            // store the contact info
+            contact_list[num_contacts].ibody = ibody;
+            contact_list[num_contacts].jbody = jbody;
+            contact_list[num_contacts].xi[0] = hi2[0];
+            contact_list[num_contacts].xi[1] = hi2[1];
+            contact_list[num_contacts].xi[2] = hi2[2];
+            contact_list[num_contacts].xj[0] = xpj2[0];
+            contact_list[num_contacts].xj[1] = xpj2[1];
+            contact_list[num_contacts].xj[2] = xpj2[2];
+            contact_list[num_contacts].type = 0;
+            contact_list[num_contacts].separation = d2 - contact_dist;
+            num_contacts++;
+          }
+          discrete[jfirst+npj2][6] = 1;
+        }
+      } else {
+        num_outside++;
+      }
+    }
+
+    // both ends have projection outside of the face
+    // compute interaction between the edge with the three edges of the face
+
+    if (num_outside == 2) {
+
+      #ifdef _POLYHEDRON_DEBUG
+      printf(" - outside = 2\n");
+      printf(" - compute pair force between edge %d of body %d "
+             "with 3 edges of face %d of body %d\n",
+        edge_index, jbody, face_index, ibody);
+      #endif
+
+      interact = EF_INTERSECT_OUTSIDE;
+
+    }
+
+  } else if (interact == EF_INTERSECT_OUTSIDE) {
+
+    // compute interaction between the edge with the three edges of the face
+
+    #ifdef _POLYHEDRON_DEBUG
+    printf(" - intersect outside triangle\n"); 
+    printf(" - compute pair force between edge %d of body %d "
+           "with face %d of body %d\n", edge_index, jbody, face_index, ibody);
+    printf("     face: xi1 (%f %f %f) xi2 (%f %f %f) xi3 (%f %f %f)\n",
+      xi1[0], xi1[1], xi1[2], xi2[0], xi2[1], xi2[2], xi3[0], xi3[1], xi3[2]);
+    printf("     edge: xpj1 (%f %f %f) xpj2 (%f %f %f)\n",
+      xpj1[0], xpj1[1], xpj1[2], xpj2[0], xpj2[1], xpj2[2]);
+
+    #endif
+  } else if (interact == EF_INTERSECT_INSIDE) {
+
+  }
+
+  return interact;
+}
+
+/* -------------------------------------------------------------------------
+  Compute the distance between an edge of body i and an edge from
+  another body
+  Input:
+    ibody      = body i (i.e. atom i)
+    face_index = face index of body i
+    xmi        = atom i's coordinates (body i's center of mass)
+    rounded_radius_i = rounded radius of the body i
+    jbody      = body i (i.e. atom j)
+    edge_index = coordinate of the tested edge from another body
+    xmj        = atom j's coordinates (body j's center of mass)
+    rounded_radius_j = rounded radius of the body j
+    cut_inner  = cutoff for vertex-vertex and vertex-edge interaction
+  Output:
+    d          = Distance from a point x0 to an edge
+    hi         = coordinates of the projection of x0 on the edge
+
+  contact      = 0 no contact between the queried edge and the face
+                 1 contact detected
+  return
+    INVALID if the face index is invalid
+    NONE    if there is no interaction
+------------------------------------------------------------------------- */
+
+int PairBodyRoundedPolyhedron::interaction_edge_to_edge(int ibody,
+                                                int edge_index_i,
+                                                double *xmi,
+                                                double rounded_radius_i,
+                                                int jbody,
+                                                int edge_index_j,
+                                                double *xmj,
+                                                double rounded_radius_j,
+                                                double k_n,
+                                                double k_na,
+                                                double cut_inner,
+                                                Contact* contact_list,
+                                                int &num_contacts,
+                                                double &energy,
+                                                double* facc)
+{
+  int ifirst,iefirst,jfirst,jefirst,npi1,npi2,npj1,npj2,interact;
+  double xi1[3],xi2[3],xpj1[3],xpj2[3];
+  double r,t1,t2,h1[3],h2[3];
+  double contact_dist, shift;
+
+  double** x = atom->x;
+  double** v = atom->v;
+  double** f = atom->f;
+  double** torque = atom->torque;
+  double** angmom = atom->angmom;
+
+  ifirst = dfirst[ibody];
+  iefirst = edfirst[ibody];
+  npi1 = static_cast<int>(edge[iefirst+edge_index_i][0]);
+  npi2 = static_cast<int>(edge[iefirst+edge_index_i][1]);
+
+  // compute the space-fixed coordinates for the edge ends
+
+  xi1[0] = xmi[0] + discrete[ifirst+npi1][0];
+  xi1[1] = xmi[1] + discrete[ifirst+npi1][1];
+  xi1[2] = xmi[2] + discrete[ifirst+npi1][2];
+
+  xi2[0] = xmi[0] + discrete[ifirst+npi2][0];
+  xi2[1] = xmi[1] + discrete[ifirst+npi2][1];
+  xi2[2] = xmi[2] + discrete[ifirst+npi2][2];
+
+  // two ends of the edge from body j
+
+  jfirst = dfirst[jbody];
+  jefirst = edfirst[jbody];
+  npj1 = static_cast<int>(edge[jefirst+edge_index_j][0]);
+  npj2 = static_cast<int>(edge[jefirst+edge_index_j][1]);
+
+  xpj1[0] = xmj[0] + discrete[jfirst+npj1][0];
+  xpj1[1] = xmj[1] + discrete[jfirst+npj1][1];
+  xpj1[2] = xmj[2] + discrete[jfirst+npj1][2];
+
+  xpj2[0] = xmj[0] + discrete[jfirst+npj2][0];
+  xpj2[1] = xmj[1] + discrete[jfirst+npj2][1];
+  xpj2[2] = xmj[2] + discrete[jfirst+npj2][2];
+
+  contact_dist = rounded_radius_i + rounded_radius_j;
+  shift = k_na * cut_inner;
+
+  int jflag = 1;
+  distance_bt_edges(xpj1, xpj2, xi1, xi2, h1, h2, t1, t2, r);
+
+  #ifdef _POLYHEDRON_DEBUG
+  double ui[3],uj[3];
+  MathExtra::sub3(xi1,xi2,ui);
+  MathExtra::norm3(ui);
+  MathExtra::sub3(xpj1,xpj2,uj);
+  MathExtra::norm3(uj);
+  double dot = MathExtra::dot3(ui, uj);
+  printf("  edge npi1 = %d (%f %f %f); npi2 = %d (%f %f %f) vs."
+         "  edge npj1 = %d (%f %f %f); npj2 = %d (%f %f %f): "
+         "t1 = %f; t2 = %f; r = %f; dot = %f\n",
+    npi1, xi1[0], xi1[1], xi1[2], npi2, xi2[0], xi2[1], xi2[2], 
+    npj1, xpj1[0], xpj1[1], xpj1[2], npj2, xpj2[0], xpj2[1], xpj2[2],
+    t1, t2, r, dot);
+  #endif
+
+  interact = EE_NONE;
+
+  // singularity case, ignore interactions
+
+  if (r < EPSILON) return interact;
+
+  // include the vertices for interactions
+
+  if (t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1 &&
+      r < contact_dist + cut_inner) {
+    pair_force_and_torque(jbody, ibody, h1, h2, r, contact_dist,
+                          k_n, k_na, shift, x, v, f, torque, angmom,
+                          jflag, energy, facc);
+
+    interact = EE_INTERACT;
+    if (r <= contact_dist) {
+      // store the contact info
+      contact_list[num_contacts].ibody = ibody;
+      contact_list[num_contacts].jbody = jbody;
+      contact_list[num_contacts].xi[0] = h2[0];
+      contact_list[num_contacts].xi[1] = h2[1];
+      contact_list[num_contacts].xi[2] = h2[2];
+      contact_list[num_contacts].xj[0] = h1[0];
+      contact_list[num_contacts].xj[1] = h1[1];
+      contact_list[num_contacts].xj[2] = h1[2];
+      contact_list[num_contacts].type = 1;
+      contact_list[num_contacts].separation = r - contact_dist;
+      num_contacts++;
+    }
+  } else {
+
+  }
+
+  return interact;
+}
+
+/* ----------------------------------------------------------------------
+  Compute forces and torques between two bodies caused by the interaction
+  between a pair of points on either bodies (similar to sphere-sphere)
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::pair_force_and_torque(int ibody, int jbody,
+                 double* pi, double* pj, double r, double contact_dist,
+                 double k_n, double k_na, double shift, double** x,
+                 double** v, double** f, double** torque, double** angmom,
+                 int jflag, double& energy, double* facc)
+{
+  double delx,dely,delz,R,fx,fy,fz,fpair;
+
+  delx = pi[0] - pj[0];
+  dely = pi[1] - pj[1];
+  delz = pi[2] - pj[2];
+  R = r - contact_dist; 
+  if (R <= 0) {                // deformation occurs
+    fpair = -k_n * R - shift;
+    energy += (0.5 * k_n * R + shift) * R;
+  } else if (R <= cut_inner) { // not deforming but cohesive ranges overlap
+    fpair = k_na * R - shift;
+    energy += (-0.5 * k_na * R + shift) * R;
+  } else fpair = 0.0;
+
+  fx = delx*fpair/r;
+  fy = dely*fpair/r;
+  fz = delz*fpair/r;
+
+  #ifdef _POLYHEDRON_DEBUG
+  printf("  - R = %f; r = %f; k_na = %f; shift = %f; fpair = %f;"
+         " energy = %f; jflag = %d\n", R, r, k_na, shift, fpair,
+         energy, jflag);
+  #endif
+
+  if (R <= 0) {
+
+    // contact: accumulate normal and tangential contact force components
+
+    contact_forces(ibody, jbody, pi, pj, delx, dely, delz, fx, fy, fz,
+                   x, v, angmom, f, torque, facc);
+  } else {
+
+    // accumulate force and torque to both bodies directly
+
+    f[ibody][0] += fx;
+    f[ibody][1] += fy;
+    f[ibody][2] += fz;
+    sum_torque(x[ibody], pi, fx, fy, fz, torque[ibody]);
+
+    facc[0] += fx; facc[1] += fy; facc[2] += fz;
+
+    if (jflag) {
+      f[jbody][0] -= fx;
+      f[jbody][1] -= fy;
+      f[jbody][2] -= fz;
+      sum_torque(x[jbody], pj, -fx, -fy, -fz, torque[jbody]);
+    }
+  }
+}
+
+/* ----------------------------------------------------------------------
+  Compute contact forces between two bodies
+  modify the force stored at the vertex and edge in contact by j_a
+  sum forces and torque to the corresponding bodies
+  fx,fy,fz = unscaled cohesive forces
+  fn = normal friction component
+  ft = tangential friction component (-c_t * v_t)
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::contact_forces(int ibody, int jbody,
+  double *xi, double *xj, double delx, double dely, double delz,
+  double fx, double fy, double fz, double** x, double** v, double** angmom,
+  double** f, double** torque, double* facc)
+{
+  int ibonus,jbonus;
+  double rsq,rsqinv,vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3;
+  double fn[3],ft[3],vi[3],vj[3];
+  double *quat, *inertia;
+  AtomVecBody::Bonus *bonus;
+
+  // compute the velocity of the vertex in the space-fixed frame
+
+  ibonus = atom->body[ibody];
+  bonus = &avec->bonus[ibonus];
+  quat = bonus->quat;
+  inertia = bonus->inertia;
+  total_velocity(xi, x[ibody], v[ibody], angmom[ibody],
+                 inertia, quat, vi);
+
+  // compute the velocity of the point on the edge in the space-fixed frame
+
+  jbonus = atom->body[jbody];
+  bonus = &avec->bonus[jbonus];
+  quat = bonus->quat;
+  inertia = bonus->inertia;
+  total_velocity(xj, x[jbody], v[jbody], angmom[jbody],
+                 inertia, quat, vj);
+
+  // vector pointing from the contact point on ibody to that on jbody
+
+  rsq = delx*delx + dely*dely + delz*delz;
+  rsqinv = 1.0/rsq;
+
+  // relative translational velocity
+
+  vr1 = vi[0] - vj[0];
+  vr2 = vi[1] - vj[1];
+  vr3 = vi[2] - vj[2];
+
+  // normal component
+
+  vnnr = vr1*delx + vr2*dely + vr3*delz;
+  vn1 = delx*vnnr * rsqinv;
+  vn2 = dely*vnnr * rsqinv;
+  vn3 = delz*vnnr * rsqinv;
+
+  // tangential component
+
+  vt1 = vr1 - vn1;
+  vt2 = vr2 - vn2;
+  vt3 = vr3 - vn3;
+
+  // normal friction term at contact
+
+  fn[0] = -c_n * vn1;
+  fn[1] = -c_n * vn2;
+  fn[2] = -c_n * vn3;
+
+  // tangential friction term at contact
+  // excluding the tangential deformation term for now
+
+  ft[0] = -c_t * vt1;
+  ft[1] = -c_t * vt2;
+  ft[2] = -c_t * vt3;
+
+  // cohesive forces will be scaled by j_a after contact area is computed
+  // mu * fne = tangential friction deformation during gross sliding
+  // see Eq. 4, Fraige et al.
+
+  fx = fn[0] + ft[0] + mu * fx;
+  fy = fn[1] + ft[1] + mu * fy;
+  fz = fn[2] + ft[2] + mu * fz;
+
+  f[ibody][0] += fx;
+  f[ibody][1] += fy;
+  f[ibody][2] += fz;
+  sum_torque(x[ibody], xi, fx, fy, fz, torque[ibody]);
+
+  f[jbody][0] -= fx;
+  f[jbody][1] -= fy;
+  f[jbody][2] -= fz;
+  sum_torque(x[jbody], xj, -fx, -fy, -fz, torque[jbody]);
+
+  facc[0] += fx; facc[1] += fy; facc[2] += fz;
+
+  #ifdef _POLYHEDRON_DEBUG
+  printf("contact ibody = %d: f = %f %f %f; torque = %f %f %f\n", ibody,
+     f[ibody][0], f[ibody][1], f[ibody][2],
+     torque[ibody][0], torque[ibody][1], torque[ibody][2]);
+  printf("contact jbody = %d: f = %f %f %f; torque = %f %f %f\n", jbody,
+     f[jbody][0], f[jbody][1], f[jbody][2],
+     torque[jbody][0], torque[jbody][1], torque[jbody][2]);
+  #endif
+}
+
+/* ----------------------------------------------------------------------
+  Rescale the forces and torques for all the contacts
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::rescale_cohesive_forces(double** x,
+     double** f, double** torque, Contact* contact_list, int &num_contacts,
+     double contact_area, double k_n, double k_na, double* facc)
+{
+  int m,ibody,jbody;
+  double delx,dely,delz,fx,fy,fz,R,fpair,r,shift;
+  double j_a = contact_area / (num_contacts * A_ua);
+  if (j_a < 1.0) j_a = 1.0;
+
+  shift = k_na * cut_inner;
+
+  for (m = 0; m < num_contacts; m++) {
+    ibody = contact_list[m].ibody;
+    jbody = contact_list[m].jbody;
+
+    delx = contact_list[m].xi[0] - contact_list[m].xj[0];
+    dely = contact_list[m].xi[1] - contact_list[m].xj[1];
+    delz = contact_list[m].xi[2] - contact_list[m].xj[2];
+    r = sqrt(delx*delx + dely*dely + delz*delz);
+    R = contact_list[m].separation; 
+    if (R <= 0) {                // deformation occurs
+      fpair = -k_n * R - shift;
+    } else if (R <= cut_inner) { // not deforming but cohesive ranges overlap
+      fpair = k_na * R - shift;
+    } else fpair = 0.0;
+
+    fpair *= j_a;
+    fx = delx*fpair/r;
+    fy = dely*fpair/r;
+    fz = delz*fpair/r;
+
+    f[ibody][0] += fx;
+    f[ibody][1] += fy;
+    f[ibody][2] += fz;
+    sum_torque(x[ibody], contact_list[m].xi, fx, fy, fz, torque[ibody]);
+
+    f[jbody][0] -= fx;
+    f[jbody][1] -= fy;
+    f[jbody][2] -= fz;
+    sum_torque(x[jbody], contact_list[m].xj, -fx, -fy, -fz, torque[jbody]);
+
+    facc[0] += fx; facc[1] += fy; facc[2] += fz;
+  }
+}
+
+/* ----------------------------------------------------------------------
+  Accumulate torque to body from the force f=(fx,fy,fz) acting at point x
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::sum_torque(double* xm, double *x, double fx,
+                                      double fy, double fz, double* torque)
+{
+  double rx = x[0] - xm[0];
+  double ry = x[1] - xm[1];
+  double rz = x[2] - xm[2];
+  double tx = ry * fz - rz * fy;
+  double ty = rz * fx - rx * fz;
+  double tz = rx * fy - ry * fx;
+  torque[0] += tx;
+  torque[1] += ty;
+  torque[2] += tz;
+}
+
+/* ----------------------------------------------------------------------
+  Test if two points a and b are in opposite sides of a plane defined by
+  a normal vector n and a point x0
+------------------------------------------------------------------------- */
+
+int PairBodyRoundedPolyhedron::opposite_sides(double* n, double* x0,
+                                           double* a, double* b)
+{
+  double m_a = n[0]*(a[0] - x0[0])+n[1]*(a[1] - x0[1])+n[2]*(a[2] - x0[2]);
+  double m_b = n[0]*(b[0] - x0[0])+n[1]*(b[1] - x0[1])+n[2]*(b[2] - x0[2]);
+  // equal to zero when either a or b is on the plane
+  if (m_a * m_b <= 0)
+    return 1;
+  else
+    return 0;
+}
+
+/* ----------------------------------------------------------------------
+  Test if a line segment defined by two points a and b intersects with
+  a triangle defined by three points x1, x2 and x3
+------------------------------------------------------------------------- */
+
+int PairBodyRoundedPolyhedron::edge_face_intersect(double* x1, double* x2,
+               double* x3, double* a, double* b, double* h_a, double* h_b,
+               double& d_a, double& d_b, int& inside_a, int& inside_b)
+{
+  double s[3], u[3], v[3], n[3];
+
+  // line director
+
+  MathExtra::sub3(b, a, s);
+
+  // plane normal vector
+
+  MathExtra::sub3(x2, x1, u);
+  MathExtra::sub3(x3, x1, v);
+  MathExtra::cross3(u, v, n);
+  MathExtra::norm3(n);
+
+  // find the projection of a and b to the plane and the corresponding distances
+
+  project_pt_plane(a, x1, x2, x3, h_a, d_a, inside_a);
+
+  project_pt_plane(b, x1, x2, x3, h_b, d_b, inside_b);
+
+  // check if the line segment is parallel to the plane
+
+  double dot = MathExtra::dot3(s, n);
+  if (fabs(dot) < EPSILON) return EF_PARALLEL;
+
+  // solve for the intersection between the line and the plane
+
+  double m[3][3], invm[3][3], p[3], ans[3];
+  m[0][0] = -s[0];
+  m[0][1] = u[0];
+  m[0][2] = v[0];
+
+  m[1][0] = -s[1];
+  m[1][1] = u[1];
+  m[1][2] = v[1];
+
+  m[2][0] = -s[2];
+  m[2][1] = u[2];
+  m[2][2] = v[2];
+
+  MathExtra::sub3(a, x1, p);
+  MathExtra::invert3(m, invm);
+  MathExtra::matvec(invm, p, ans);
+
+  // p is reused for the intersection point
+  // s = b - a
+
+  double t = ans[0];
+  p[0] = a[0] + s[0] * t;
+  p[1] = a[1] + s[1] * t;
+  p[2] = a[2] + s[2] * t;
+
+  // check if p is inside the triangle, excluding the edges and vertices
+  // the edge-edge and edge-vertices are handled separately
+
+  int inside = 0;
+  if (ans[1] > 0 && ans[2] > 0 && ans[1] + ans[2] < 1)
+    inside = 1;
+
+  int interact;
+  if (t < 0 || t > 1) {
+    interact = EF_SAME_SIDE_OF_FACE;
+  } else {
+    if (inside == 1) 
+      interact = EF_INTERSECT_INSIDE;
+    else
+      interact = EF_INTERSECT_OUTSIDE;
+  }
+  
+  return interact;
+}
+
+/* ----------------------------------------------------------------------
+  Find the projection of q on the plane defined by point p and the normal
+  unit vector n: q_proj = q - dot(q - p, n) * n
+  and the distance d from q to the plane
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::project_pt_plane(const double* q,
+                                        const double* p, const double* n,
+                                        double* q_proj, double &d)
+{
+  double dot, ans[3], n_p[3];
+  n_p[0] = n[0]; n_p[1] = n[1]; n_p[2] = n[2];
+  MathExtra::sub3(q, p, ans);
+  dot = MathExtra::dot3(ans, n_p);
+  MathExtra::scale3(dot, n_p);
+  MathExtra::sub3(q, n_p, q_proj);
+  MathExtra::sub3(q, q_proj, ans);
+  d = MathExtra::len3(ans);
+}
+
+/* ----------------------------------------------------------------------
+  Check if points q1 and q2 are inside a convex polygon, i.e. a face of
+  a polyhedron
+    ibody       = atom i's index
+    face_index  = face index of the body
+    xmi         = atom i's coordinates
+    q1          = tested point on the face (e.g. the projection of a point)
+    q2          = another point (can be NULL) for face-edge intersection
+  Output:
+    inside1     = 1 if q1 is inside the polygon, 0 otherwise
+    inside2     = 1 if q2 is inside the polygon, 0 otherwise
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::inside_polygon(int ibody, int face_index,
+                            double* xmi, const double* q1, const double* q2,
+                            int& inside1, int& inside2)
+
+{
+  int i,n,ifirst,iffirst,npi1,npi2;
+  double xi1[3],xi2[3],u[3],v[3],costheta,anglesum1,anglesum2,magu,magv;
+
+  ifirst = dfirst[ibody];
+  iffirst = facfirst[ibody];
+  anglesum1 = anglesum2 = 0;;
+  for (i = 0; i < MAX_FACE_SIZE; i++) {
+    npi1 = static_cast<int>(face[iffirst+face_index][i]);
+    if (npi1 < 0) break;
+    n = i + 1;
+    if (n <= MAX_FACE_SIZE - 1) {
+      npi2 = static_cast<int>(face[iffirst+face_index][n]);
+      if (npi2 < 0) npi2 = static_cast<int>(face[iffirst+face_index][0]);
+    } else {
+      npi2 = static_cast<int>(face[iffirst+face_index][0]);
+    }
+
+    xi1[0] = xmi[0] + discrete[ifirst+npi1][0];
+    xi1[1] = xmi[1] + discrete[ifirst+npi1][1];
+    xi1[2] = xmi[2] + discrete[ifirst+npi1][2];
+
+    xi2[0] = xmi[0] + discrete[ifirst+npi2][0];
+    xi2[1] = xmi[1] + discrete[ifirst+npi2][1];
+    xi2[2] = xmi[2] + discrete[ifirst+npi2][2];
+
+    MathExtra::sub3(xi1,q1,u);
+    MathExtra::sub3(xi2,q1,v);
+    magu = MathExtra::len3(u);
+    magv = MathExtra::len3(v);
+
+    // the point is at either vertices
+
+    if (magu * magv < EPSILON) inside1 = 1;
+    else {
+      costheta = MathExtra::dot3(u,v)/(magu*magv);
+      anglesum1 += acos(costheta);
+    }
+
+    if (q2 != NULL) {
+      MathExtra::sub3(xi1,q2,u);
+      MathExtra::sub3(xi2,q2,v);
+      magu = MathExtra::len3(u);
+      magv = MathExtra::len3(v);
+      if (magu * magv < EPSILON) inside2 = 1;
+      else {
+        costheta = MathExtra::dot3(u,v)/(magu*magv);
+        anglesum2 += acos(costheta);
+      }
+    }
+  }
+
+  if (fabs(anglesum1 - MY_2PI) < EPSILON) inside1 = 1;
+  else inside1 = 0;
+
+  if (q2 != NULL) {
+    if (fabs(anglesum2 - MY_2PI) < EPSILON) inside2 = 1;
+    else inside2 = 0;
+  }
+}
+
+/* ----------------------------------------------------------------------
+  Find the projection of q on the plane defined by 3 points x1, x2 and x3
+  returns the distance d from q to the plane and whether the projected
+  point is inside the triangle defined by (x1, x2, x3)
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::project_pt_plane(const double* q,
+      const double* x1, const double* x2, const double* x3, double* q_proj,
+      double &d, int& inside)
+{
+  double u[3],v[3],n[3];
+
+  // plane normal vector
+
+  MathExtra::sub3(x2, x1, u);
+  MathExtra::sub3(x3, x1, v);
+  MathExtra::cross3(u, v, n);
+  MathExtra::norm3(n);
+
+  // solve for the intersection between the line and the plane
+
+  double m[3][3], invm[3][3], p[3], ans[3];
+  m[0][0] = -n[0];
+  m[0][1] = u[0];
+  m[0][2] = v[0];
+
+  m[1][0] = -n[1];
+  m[1][1] = u[1];
+  m[1][2] = v[1];
+
+  m[2][0] = -n[2];
+  m[2][1] = u[2];
+  m[2][2] = v[2];
+
+  MathExtra::sub3(q, x1, p);
+  MathExtra::invert3(m, invm);
+  MathExtra::matvec(invm, p, ans);
+
+  double t = ans[0];
+  q_proj[0] = q[0] + n[0] * t;
+  q_proj[1] = q[1] + n[1] * t;
+  q_proj[2] = q[2] + n[2] * t;
+
+  // check if the projection point is inside the triangle
+  // exclude the edges and vertices 
+  // edge-sphere and sphere-sphere interactions are handled separately
+
+  inside = 0;
+  if (ans[1] > 0 && ans[2] > 0 && ans[1] + ans[2] < 1) {
+    inside = 1;
+  }
+
+  // distance from q to q_proj
+
+  MathExtra::sub3(q, q_proj, ans);
+  d = MathExtra::len3(ans);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::project_pt_line(const double* q,
+     const double* xi1, const double* xi2, double* h, double& d, double& t)
+{
+  double u[3],v[3],r[3],s;
+
+  MathExtra::sub3(xi2, xi1, u);
+  MathExtra::norm3(u);
+  MathExtra::sub3(q, xi1, v);
+  
+  s = MathExtra::dot3(u, v);
+  h[0] = xi1[0] + s * u[0];
+  h[1] = xi1[1] + s * u[1];
+  h[2] = xi1[2] + s * u[2];
+
+  MathExtra::sub3(q, h, r);
+  d = MathExtra::len3(r);
+
+  if (fabs(xi2[0] - xi1[0]) > 0)
+    t = (h[0] - xi1[0])/(xi2[0] - xi1[0]);
+  else if (fabs(xi2[1] - xi1[1]) > 0)
+    t = (h[1] - xi1[1])/(xi2[1] - xi1[1]);
+  else if (fabs(xi2[2] - xi1[2]) > 0)
+    t = (h[2] - xi1[2])/(xi2[2] - xi1[2]);
+}
+
+/* ---------------------------------------------------------------------- 
+  compute the shortest distance between two edges (line segments)
+  x1, x2: two endpoints of the first edge
+  x3, x4: two endpoints of the second edge
+  h1: the end point of the shortest segment perpendicular to both edges 
+      on the line (x1;x2)
+  h2: the end point of the shortest segment perpendicular to both edges 
+      on the line (x3;x4)
+  t1: fraction of h1 in the segment (x1,x2)
+  t2: fraction of h2 in the segment (x3,x4)
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::distance_bt_edges(const double* x1,
+                  const double* x2, const double* x3, const double* x4,
+                  double* h1, double* h2, double& t1, double& t2, double& r)
+{
+  double u[3],v[3],n[3],dot;
+
+  // set the default returned values
+
+  t1 = -2;
+  t2 = 2;
+  r = 0;
+
+  // find the edge unit directors and their dot product
+
+  MathExtra::sub3(x2, x1, u);
+  MathExtra::norm3(u);
+  MathExtra::sub3(x4, x3, v);
+  MathExtra::norm3(v);
+  dot = MathExtra::dot3(u,v);
+  dot = fabs(dot);
+
+  // check if two edges are parallel
+  // find the two ends of the overlapping segment, if any
+
+  if (fabs(dot - 1.0) < EPSILON) {
+
+    double s1,s2,x13[3],x23[3],x13h[3];
+    double t13,t23,t31,t41,x31[3],x41[3];
+    
+    MathExtra::sub3(x1,x3,x13); // x13 = x1 - x3
+    MathExtra::sub3(x2,x3,x23); // x23 = x2 - x3
+
+    s1 = MathExtra::dot3(x13,v);
+    x13h[0] = x13[0] - s1*v[0];
+    x13h[1] = x13[1] - s1*v[1];
+    x13h[2] = x13[2] - s1*v[2];
+    r = MathExtra::len3(x13h);
+    
+    // x13 is the projection of x1 on x3-x4
+
+    x13[0] = x3[0] + s1*v[0];
+    x13[1] = x3[1] + s1*v[1];
+    x13[2] = x3[2] + s1*v[2];
+
+    // x23 is the projection of x2 on x3-x4
+
+    s2 = MathExtra::dot3(x23,v);
+    x23[0] = x3[0] + s2*v[0];
+    x23[1] = x3[1] + s2*v[1];
+    x23[2] = x3[2] + s2*v[2];
+    
+    // find the fraction of the projection points on the edges
+
+    if (fabs(x4[0] - x3[0]) > 0)
+      t13 = (x13[0] - x3[0])/(x4[0] - x3[0]);
+    else if (fabs(x4[1] - x3[1]) > 0)
+      t13 = (x13[1] - x3[1])/(x4[1] - x3[1]);
+    else if (fabs(x4[2] - x3[2]) > 0)
+      t13 = (x13[2] - x3[2])/(x4[2] - x3[2]);
+
+    if (fabs(x4[0] - x3[0]) > 0)
+      t23 = (x23[0] - x3[0])/(x4[0] - x3[0]);
+    else if (fabs(x4[1] - x3[1]) > 0)
+      t23 = (x23[1] - x3[1])/(x4[1] - x3[1]);
+    else if (fabs(x4[2] - x3[2]) > 0)
+      t23 = (x23[2] - x3[2])/(x4[2] - x3[2]);
+
+    if (fabs(x23[0] - x13[0]) > 0)
+      t31 = (x3[0] - x13[0])/(x23[0] - x13[0]);
+    else if (fabs(x23[1] - x13[1]) > 0)
+      t31 = (x3[1] - x13[1])/(x23[1] - x13[1]);
+    else if (fabs(x23[2] - x13[2]) > 0)
+      t31 = (x3[2] - x13[2])/(x23[2] - x13[2]);
+
+    // x31 is the projection of x3 on x1-x2
+
+    x31[0] = x1[0] + t31*(x2[0] - x1[0]);
+    x31[1] = x1[1] + t31*(x2[1] - x1[1]);
+    x31[2] = x1[2] + t31*(x2[2] - x1[2]);
+
+    if (fabs(x23[0] - x13[0]) > 0)
+      t41 = (x4[0] - x13[0])/(x23[0] - x13[0]);
+    else if (fabs(x23[1] - x13[1]) > 0)
+      t41 = (x4[1] - x13[1])/(x23[1] - x13[1]);
+    else if (fabs(x23[2] - x13[2]) > 0)
+      t41 = (x4[2] - x13[2])/(x23[2] - x13[2]);
+
+    // x41 is the projection of x4 on x1-x2
+
+    x41[0] = x1[0] + t41*(x2[0] - x1[0]);
+    x41[1] = x1[1] + t41*(x2[1] - x1[1]);
+    x41[2] = x1[2] + t41*(x2[2] - x1[2]);
+
+    // determine two ends from the overlapping segments
+
+    int n1 = 0;
+    int n2 = 0;
+    if (t13 >= 0 && t13 <= 1) {
+      h1[0] = x1[0];
+      h1[1] = x1[1];
+      h1[2] = x1[2];
+      h2[0] = x13[0];
+      h2[1] = x13[1];
+      h2[2] = x13[2];
+      t1 = 0;
+      t2 = t13;
+      n1++;
+      n2++;
+    }
+    if (t23 >= 0 && t23 <= 1) {
+      if (n1 == 0) {
+        h1[0] = x2[0];
+        h1[1] = x2[1];
+        h1[2] = x2[2];
+        h2[0] = x23[0];
+        h2[1] = x23[1];
+        h2[2] = x23[2];
+        t1 = 1;
+        t2 = t23;
+        n1++;
+        n2++;
+      } else {
+        h1[0] = (x1[0]+x2[0])/2;
+        h1[1] = (x1[1]+x2[1])/2;
+        h1[2] = (x1[2]+x2[2])/2;
+        h2[0] = (x13[0]+x23[0])/2; 
+        h2[1] = (x13[1]+x23[1])/2; 
+        h2[2] = (x13[2]+x23[2])/2;
+        t1 = 0.5;
+        t2 = (t13+t23)/2;
+        n1++;
+        n2++;
+      }
+    }
+
+    if (n1 == 0 && n2 == 0) {
+      if (t31 >= 0 && t31 <= 1) {
+        h1[0] = x31[0];
+        h1[1] = x31[1];
+        h1[2] = x31[2];
+        h2[0] = x3[0];
+        h2[1] = x3[1];
+        h2[2] = x3[2];
+        t1 = t31;
+        t2 = 0;
+        n1++;
+        n2++;
+      }
+      if (t41 >= 0 && t41 <= 1) {
+        if (n1 == 0) {
+          h1[0] = x41[0];
+          h1[1] = x41[1];
+          h1[2] = x41[2];
+          h2[0] = x4[0];
+          h2[1] = x4[1];
+          h2[2] = x4[2];
+          t1 = t41;
+          t2 = 1;
+          n1++;
+          n2++;
+        } else {
+          h1[0] = (x31[0]+x41[0])/2;
+          h1[1] = (x31[1]+x41[1])/2;
+          h1[2] = (x31[2]+x41[2])/2;
+          h2[0] = (x3[0]+x4[0])/2; 
+          h2[1] = (x3[1]+x4[1])/2; 
+          h2[2] = (x3[2]+x4[2])/2;
+          t1 = (t31+t41)/2;
+          t2 = 0.5;
+          n1++;
+          n2++;
+        }
+      }
+    }   
+
+    // if n1 == 0 and n2 == 0 at this point,
+    // which means no overlapping segments bt two parallel edges,
+    // return the default values of t1 and t2
+
+    return;
+
+  } 
+
+  // find the vector n perpendicular to both edges
+ 
+  MathExtra::cross3(u, v, n);
+  MathExtra::norm3(n);
+
+  // find the intersection of the line (x3,x4) and the plane (x1,x2,n)
+  // s = director of the line (x3,x4)
+  // n_p = plane normal vector of the plane (x1,x2,n)
+
+  double s[3], n_p[3];
+  MathExtra::sub3(x4, x3, s);
+  MathExtra::sub3(x2, x1, u);
+  MathExtra::cross3(u, n, n_p);
+  MathExtra::norm3(n_p);
+
+  // solve for the intersection between the line and the plane
+
+  double m[3][3], invm[3][3], p[3], ans[3];
+  m[0][0] = -s[0];
+  m[0][1] = u[0];
+  m[0][2] = n[0];
+
+  m[1][0] = -s[1];
+  m[1][1] = u[1];
+  m[1][2] = n[1];
+
+  m[2][0] = -s[2];
+  m[2][1] = u[2];
+  m[2][2] = n[2];
+
+  MathExtra::sub3(x3, x1, p);
+  MathExtra::invert3(m, invm);
+  MathExtra::matvec(invm, p, ans);
+
+  t2 = ans[0];
+  h2[0] = x3[0] + s[0] * t2;
+  h2[1] = x3[1] + s[1] * t2;
+  h2[2] = x3[2] + s[2] * t2;
+
+  project_pt_plane(h2, x1, n, h1, r);
+
+  if (fabs(x2[0] - x1[0]) > 0)
+    t1 = (h1[0] - x1[0])/(x2[0] - x1[0]);
+  else if (fabs(x2[1] - x1[1]) > 0)
+    t1 = (h1[1] - x1[1])/(x2[1] - x1[1]);
+  else if (fabs(x2[2] - x1[2]) > 0)
+    t1 = (h1[2] - x1[2])/(x2[2] - x1[2]);
+}
+
+/* ----------------------------------------------------------------------
+  Calculate the total velocity of a point (vertex, a point on an edge):
+    vi = vcm + omega ^ (p - xcm)
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::total_velocity(double* p, double *xcm,
+  double* vcm, double *angmom, double *inertia, double *quat, double* vi)
+{
+  double r[3],omega[3],ex_space[3],ey_space[3],ez_space[3];
+  r[0] = p[0] - xcm[0];
+  r[1] = p[1] - xcm[1];
+  r[2] = p[2] - xcm[2];
+  MathExtra::q_to_exyz(quat,ex_space,ey_space,ez_space);
+  MathExtra::angmom_to_omega(angmom,ex_space,ey_space,ez_space,
+                             inertia,omega);
+  vi[0] = omega[1]*r[2] - omega[2]*r[1] + vcm[0];
+  vi[1] = omega[2]*r[0] - omega[0]*r[2] + vcm[1];
+  vi[2] = omega[0]*r[1] - omega[1]*r[0] + vcm[2];
+}
+
+/* ---------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::sanity_check()
+{
+
+  double x1[3],x2[3],x3[3],x4[3],h_a[3],h_b[3],d_a,d_b,u[3],v[3],n[3];
+  double a[3],b[3],t_a,t_b;
+  int inside_a, inside_b;
+
+  x1[0] = 0; x1[1] = 3; x1[2] = 0;
+  x2[0] = 3; x2[1] = 0; x2[2] = 0;
+  x3[0] = 4; x3[1] = 3; x3[2] = 0;
+  x4[0] = 5; x4[1] = 3; x4[2] = 0;
+
+  a[0] = 0; a[1] = 0; a[2] = 0;
+  b[0] = 4; b[1] = 0; b[2] = 0;
+
+  project_pt_line(a, x1, x2, h_a, d_a, t_a);
+  project_pt_line(b, x1, x2, h_b, d_b, t_b);
+/*
+  printf("h_a: %f %f %f; h_b: %f %f %f; t_a = %f; t_b = %f; d = %f; d_b = %f\n",
+    h_a[0], h_a[1], h_a[2], h_b[0], h_b[1], h_b[2], t_a, t_b, d_a, d_b);
+*/
+/*
+  int mode = edge_face_intersect(x1, x2, x3, a, b, h_a, h_b, d_a, d_b,
+                                 inside_a, inside_b);
+
+  MathExtra::sub3(x2, x1, u);
+  MathExtra::sub3(x3, x1, v);
+  MathExtra::cross3(u, v, n);
+  MathExtra::norm3(n);
+*/
+/*
+  project_pt_plane(a, x1, x2, x3, h_a, d_a, inside_a);
+  printf("h_a: %f %f %f; d = %f: inside %d\n",
+    h_a[0], h_a[1], h_a[2], d_a, inside_a);
+  project_pt_plane(b, x1, x2, x3, h_b, d_b, inside_b);
+  printf("h_b: %f %f %f; d = %f: inside %d\n",
+    h_b[0], h_b[1], h_b[2], d_b, inside_b);
+*/
+/*
+  distance_bt_edges(x1, x2, x3, x4, h_a, h_b, t_a, t_b, d_a);
+  printf("h_a: %f %f %f; h_b: %f %f %f; t_a = %f; t_b = %f; d = %f\n",
+    h_a[0], h_a[1], h_a[2], h_b[0], h_b[1], h_b[2], t_a, t_b, d_a);
+*/
+}
+
diff --git a/src/BODY/pair_body_rounded_polyhedron.h b/src/BODY/pair_body_rounded_polyhedron.h
new file mode 100644
index 0000000000000000000000000000000000000000..d7ee1f013e66a837948bdd6bddfb5464630e50fe
--- /dev/null
+++ b/src/BODY/pair_body_rounded_polyhedron.h
@@ -0,0 +1,176 @@
+/* -*- c++ -*- ----------------------------------------------------------
+   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
+   http://lammps.sandia.gov, Sandia National Laboratories
+   Steve Plimpton, sjplimp@sandia.gov
+
+   Copyright (2003) Sandia Corporation.  Under the terms of Contract
+   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+   certain rights in this software.  This software is distributed under
+   the GNU General Public License.
+
+   See the README file in the top-level LAMMPS directory.
+------------------------------------------------------------------------- */
+
+#ifdef PAIR_CLASS
+
+PairStyle(body/rounded/polyhedron,PairBodyRoundedPolyhedron)
+
+#else
+
+#ifndef LMP_PAIR_BODY_ROUNDED_POLYHEDRON_H
+#define LMP_PAIR_BODY_ROUNDED_POLYHEDRON_H
+
+#include "pair.h"
+
+namespace LAMMPS_NS {
+
+class PairBodyRoundedPolyhedron : public Pair {
+ public:
+  PairBodyRoundedPolyhedron(class LAMMPS *);
+  ~PairBodyRoundedPolyhedron();
+  void compute(int, int);
+  void settings(int, char **);
+  void coeff(int, char **);
+  void init_style();
+  double init_one(int, int);
+
+  struct Contact {
+    int ibody, jbody; // body (i.e. atom) indices (not tags)
+    int type;         // 0 = VERTEX-FACE; 1 = EDGE-EDGE
+    double fx,fy,fz;  // unscaled cohesive forces at contact
+    double xi[3];     // coordinates of the contact point on ibody
+    double xj[3];     // coordinates of the contact point on jbody
+    double separation; // contact surface separation
+  };
+
+ protected:
+  double **k_n;       // normal repulsion strength
+  double **k_na;      // normal attraction strength
+  double c_n;         // normal damping coefficient
+  double c_t;         // tangential damping coefficient
+  double mu;          // normal friction coefficient during gross sliding
+  double A_ua;        // characteristic contact area
+  double cut_inner;   // cutoff for interaction between vertex-edge surfaces
+
+  class AtomVecBody *avec;
+  class BodyRoundedPolyhedron *bptr;
+
+  double **discrete;  // list of all sub-particles for all bodies
+  int ndiscrete;      // number of discretes in list
+  int dmax;           // allocated size of discrete list
+  int *dnum;          // number of discretes per line, 0 if uninit
+  int *dfirst;        // index of first discrete per each line
+  int nmax;           // allocated size of dnum,dfirst vectors
+
+  double **edge;      // list of all edge for all bodies
+  int nedge;          // number of edge in list
+  int edmax;          // allocated size of edge list
+  int *ednum;         // number of edges per line, 0 if uninit
+  int *edfirst;       // index of first edge per each line
+  int ednummax;       // allocated size of ednum,edfirst vectors
+
+  double **face;      // list of all edge for all bodies
+  int nface;          // number of faces in list
+  int facmax;         // allocated size of face list
+  int *facnum;        // number of faces per line, 0 if uninit
+  int *facfirst;      // index of first face per each line
+  int facnummax;      // allocated size of facnum,facfirst vectors
+
+  double *enclosing_radius; // enclosing radii for all bodies
+  double *rounded_radius;   // rounded radii for all bodies
+  double *maxerad;          // per-type maximum enclosing radius
+
+  void allocate();
+  void body2space(int);
+
+  int edge_against_face(int ibody, int jbody, double k_n, double k_na,
+                        double** x, Contact* contact_list, int &num_contacts,
+                        double &evdwl, double* facc);
+  int edge_against_edge(int ibody, int jbody, double k_n, double k_na,
+                        double** x,Contact* contact_list, int &num_contacts,
+                        double &evdwl, double* facc);
+  void sphere_against_sphere(int ibody, int jbody, double delx, double dely, double delz,
+                             double rsq, double k_n, double k_na,
+                             double** v, double** f, int evflag);
+  void sphere_against_face(int ibody, int jbody,
+                       double k_n, double k_na, double** x, double** v,
+                       double** f, double** torque, double** angmom, int evflag);
+  void sphere_against_edge(int ibody, int jbody,
+                       double k_n, double k_na, double** x, double** v,
+                       double** f, double** torque, double** angmom, int evflag);
+
+  int interaction_face_to_edge(int ibody, int face_index, double* xmi,
+                               double rounded_radius_i, int jbody, int edge_index,
+                               double* xmj, double rounded_radius_j,
+                               double k_n, double k_na, double cut_inner,
+                               Contact* contact_list, int &num_contacts,
+                               double& energy, double* facc);
+  int interaction_edge_to_edge(int ibody, int edge_index_i, double* xmi,
+                               double rounded_radius_i, int jbody, int edge_index_j,
+                               double* xmj, double rounded_radius_j,
+                               double k_n, double k_na, double cut_inner,
+                               Contact* contact_list, int &num_contacts,
+                               double& energy, double* facc);
+
+  void contact_forces(int ibody, int jbody, double *xi, double *xj,
+    double delx, double dely, double delz, double fx, double fy, double fz,
+    double** x, double** v, double** angmom, double** f, double** torque,
+    double* facc);
+
+  void pair_force_and_torque(int ibody, int jbody, double* pi, double* pj,
+                             double r, double contact_dist, double k_n, 
+                             double k_na, double shift, double** x, double** v,
+                             double** f, double** torque, double** angmom,
+                             int jflag, double& energy, double* facc);
+  void rescale_cohesive_forces(double** x, double** f, double** torque,
+                               Contact* contact_list, int &num_contacts,
+                               double contact_area, double k_n, double k_na, double* facc);
+
+  void sum_torque(double* xm, double *x, double fx, double fy, double fz, double* torque);
+  int opposite_sides(double* n, double* x0, double* a, double* b);
+  int edge_face_intersect(double* x1, double* x2, double* x3, double* a, double* b,
+                          double* hi1, double* hi2, double& d1, double& d2,
+                          int& inside_a, int& inside_b);
+  void project_pt_plane(const double* q, const double* p, 
+                        const double* n, double* q_proj, double &d);
+  void project_pt_plane(const double* q, const double* x1, const double* x2, 
+                        const double* x3, double* q_proj, double &d, int& inside);
+  void project_pt_line(const double* q, const double* xi1, const double* xi2,
+                          double* h, double& d, double& t);
+  void inside_polygon(int ibody, int face_index, double* xmi, 
+                     const double* q1, const double* q2, int& inside1, int& inside2);
+
+  void distance_bt_edges(const double* x1, const double* x2,
+                      const double* x3, const double* x4,
+                      double* h1, double* h2, double& t1, double& t2, double& r);
+  void total_velocity(double* p, double *xcm, double* vcm, double *angmom,
+                      double *inertia, double *quat, double* vi);
+  void sanity_check();
+};
+
+}
+
+#endif
+#endif
+
+/* ERROR/WARNING messages:
+
+E: Illegal ... command
+
+Self-explanatory.  Check the input script syntax and compare to the
+documentation for the command.  You can use -echo screen as a
+command-line option when running LAMMPS to see the offending line.
+
+E: Incorrect args for pair coefficients
+
+Self-explanatory.  Check the input script or data file.
+
+E: Pair body/rounded/polyhedron requires atom style body rounded/polyhedron
+
+Self-explanatory.
+
+E: Pair body requires body style rounded/polyhedron
+
+This pair style is specific to the rounded/polyhedron body style.
+
+*/