diff --git a/doc/src/Eqs/pair_body_rounded.jpg b/doc/src/Eqs/pair_body_rounded.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..e7136ddd20c2acea0e89fa8412497333f15cb541
Binary files /dev/null and b/doc/src/Eqs/pair_body_rounded.jpg differ
diff --git a/doc/src/Eqs/pair_body_rounded.tex b/doc/src/Eqs/pair_body_rounded.tex
new file mode 100644
index 0000000000000000000000000000000000000000..54e095340f1ab3ae13d672d35797a56faeee927a
--- /dev/null
+++ b/doc/src/Eqs/pair_body_rounded.tex
@@ -0,0 +1,13 @@
+\documentstyle[12pt]{article}
+
+\begin{document}
+
+\begin{eqnarray*}
+ F_n &=& k_n \delta_n - c_n v_n, \qquad \delta_n \le 0 \\
+     &=& -k_{na} \delta_n - c_n v_n, \qquad 0 < \delta_n \le r_c \\
+     &=& 0 \qquad \qquad \qquad \qquad \delta_n > r_c \\
+ F_t &=& \mu k_n \delta_n - c_t v_t, \qquad \delta_n \le 0 \\
+     &=& 0 \qquad \qquad \qquad \qquad \delta_n > 0
+\end{eqnarray*}
+
+\end{document}
diff --git a/doc/src/JPG/pair_body_rounded.jpg b/doc/src/JPG/pair_body_rounded.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..cd745c44e27e96585be021b3dcca342f686e15e9
Binary files /dev/null and b/doc/src/JPG/pair_body_rounded.jpg differ
diff --git a/doc/src/Section_commands.txt b/doc/src/Section_commands.txt
index fa844b81cac9acfab6ce60d39cd7ca28ef7af398..32308b36cbf4d0a2ce8c22cfaa855dde96865bf3 100644
--- a/doc/src/Section_commands.txt
+++ b/doc/src/Section_commands.txt
@@ -678,6 +678,8 @@ USER-INTEL, k = KOKKOS, o = USER-OMP, t = OPT.
 "vector"_fix_vector.html,
 "viscosity"_fix_viscosity.html,
 "viscous"_fix_viscous.html,
+"wall/body/polygon"_fix_wall_body_polygon.html,
+"wall/body/polyhedron"_fix_wall_body_polyhedron.html,
 "wall/colloid"_fix_wall.html,
 "wall/gran"_fix_wall_gran.html,
 "wall/gran/region"_fix_wall_gran_region.html,
@@ -930,7 +932,9 @@ KOKKOS, o = USER-OMP, t = OPT.
 "airebo (oi)"_pair_airebo.html,
 "airebo/morse (oi)"_pair_airebo.html,
 "beck (go)"_pair_beck.html,
-"body"_pair_body.html,
+"body/nparticle"_pair_body_nparticle.html,
+"body/rounded/polygon"_pair_body_rounded/polygon.html,
+"body/rounded/polyhedron"_pair_body_rounded/polyhedron.html,
 "bop"_pair_bop.html,
 "born (go)"_pair_born.html,
 "born/coul/dsf"_pair_born.html,
diff --git a/doc/src/body.txt b/doc/src/body.txt
index 8d49efdae40eed30f9c7ec82e704adc3aef44771..e7baf626f54a1d0cc6170c920986088d8fcdfe65 100644
--- a/doc/src/body.txt
+++ b/doc/src/body.txt
@@ -27,18 +27,16 @@ styles supported by LAMMPS are as follows.  The name in the first
 column is used as the {bstyle} argument for the "atom_style
 body"_atom_style.html command.
 
-{nparticle} | rigid body with N sub-particles |
-{rounded/polygon} | 2d convex polygon with N vertices :tb(c=2,s=|)
+{nparticle} : rigid body with N sub-particles
+{rounded/polygon} : 2d polygons with N vertices
+{rounded/polyhedron} : 3d polyhedra with N vertices, E edges and F faces :tb(s=:)
 
 The body style determines what attributes are stored for each body and
 thus how they can be used to compute pairwise body/body or
 bond/non-body (point particle) interactions.  More details of each
 style are described below.
 
-NOTE: The rounded/polygon style listed in the table above and
-described below has not yet been relesed in LAMMPS.  It will be soon.
-
-We hope to add more styles in the future.  See "Section
+More styles may be added in the future.  See "Section
 10.12"_Section_modify.html#mod_12 for details on how to add a new body
 style to the code.
 
@@ -61,7 +59,7 @@ the simple particles.
 By contrast, when body particles are used, LAMMPS treats an entire
 body as a single particle for purposes of computing pairwise
 interactions, building neighbor lists, migrating particles between
-processors, outputting particles to a dump file, etc.  This means that
+processors, output of particles to a dump file, etc.  This means that
 interactions between pairs of bodies or between a body and non-body
 (point) particle need to be encoded in an appropriate pair style.  If
 such a pair style were to mimic the "fix rigid"_fix_rigid.html model,
@@ -72,17 +70,20 @@ single body/body interaction was computed.
 Thus it only makes sense to use body particles and develop such a pair
 style, when particle/particle interactions are more complex than what
 the "fix rigid"_fix_rigid.html command can already calculate.  For
-example, if particles have one or more of the following attributes:
+example, consider particles with one or more of the following
+attributes:
 
 represented by a surface mesh
 represented by a collection of geometric entities (e.g. planes + spheres)
 deformable
 internal stress that induces fragmentation :ul
 
-then the interaction between pairs of particles is likely to be more
-complex than the summation of simple sub-particle interactions.  An
-example is contact or frictional forces between particles with planar
-surfaces that inter-penetrate.
+For these models, the interaction between pairs of particles is likely
+to be more complex than the summation of simple pairwise interactions.
+An example is contact or frictional forces between particles with
+planar surfaces that inter-penetrate.  Likewise, the body particle may
+store internal state, such as a stress tensor used to compute a
+fracture criterion.
 
 These are additional LAMMPS commands that can be used with body
 particles of different styles
@@ -130,7 +131,9 @@ x1 y1 z1
 ...
 xN yN zN :pre
 
-N is the number of sub-particles in the body particle.  M = 6 + 3*N.
+where M = 6 + 3*N, and N is the number of sub-particles in the body
+particle.  
+
 The integer line has a single value N.  The floating point line(s)
 list 6 moments of inertia followed by the coordinates of the N
 sub-particles (x1 to zN) as 3N values.  These values can be listed on
@@ -175,15 +178,18 @@ The {bflag2} argument is ignored.
 
 [Specifics of body style rounded/polygon:]
 
-NOTE: Aug 2016 - This body style has not yet been added to LAMMPS.
-The info below is a placeholder.
+The {rounded/polygon} body style represents body particles as a 2d
+polygon with a variable number of N vertices.  This style can only be
+used for 2d models; see the "boundary"_boundary.html command.  See the
+"pair_style body/rounded/polygon" doc page for a diagram of two
+squares with rounded circles at the vertices.  Special cases for N = 1
+(circle) and N = 2 (rod with rounded ends) can also be specified.
+
+One use of this body style is for 2d discrete element models, as
+described in "Fraige"_#body-Fraige.
 
-The {rounded/polygon} body style represents body particles as a convex
-polygon with a variable number N > 2 of vertices, which can only be
-used for 2d models.  One example use of this body style is for 2d
-discrete element models, as described in "Fraige"_#Fraige.  Similar to
-body style {nparticle}, the atom_style body command for this body
-style takes two additional arguments:
+Similar to body style {nparticle}, the atom_style body command for
+this body style takes two additional arguments:
 
 atom_style body rounded/polygon Nmin Nmax
 Nmin = minimum # of vertices in any body in the system
@@ -203,17 +209,20 @@ x1 y1 z1
 ...
 xN yN zN
 i j j k k ...
-radius :pre
+diameter :pre
 
-N is the number of vertices in the body particle.  M = 6 + 3*N + 2*N +
-1.  The integer line has a single value N.  The floating point line(s)
+where M = 6 + 3*N + 2*N + 1, and N is the number of vertices in the
+body particle.
+
+The integer line has a single value N.  The floating point line(s)
 list 6 moments of inertia followed by the coordinates of the N
-vertices (x1 to zN) as 3N values, followed by 2N vertex indices
-corresponding to the end points of the N edges, followed by a single
-radius value = the smallest circle encompassing the polygon.  That
-last value is used to facilitate the body/body contact detection.
-These floating-point values can be listed on as many lines as you
-wish; see the "read_data"_read_data.html command for more details.
+vertices (x1 to zN) as 3N values (with z = 0.0 for each), followed by
+2N vertex indices corresponding to the end points of the N edges,
+followed by a single diameter value = the rounded diameter of the
+circle that surrounds each vertex. The diameter value can be different
+for each body particle. These floating-point values can be listed on
+as many lines as you wish; see the "read_data"_read_data.html command
+for more details.
 
 The 6 moments of inertia (ixx,iyy,izz,ixy,ixz,iyz) should be the
 values consistent with the current orientation of the rigid body
@@ -225,8 +234,11 @@ from the center-of-mass of the body particle.  The center-of-mass
 position of the particle is specified by the x,y,z values in the
 {Atoms} section of the data file.
 
-For example, the following information would specify a square
-particles whose edge length is sqrt(2):
+For example, the following information would specify a square particle
+whose edge length is sqrt(2) and rounded diameter is 1.0.  The
+orientation of the square is aligned with the xy coordinate axes which
+is consistent with the 6 moments of inertia: ixx iyy izz ixy ixz iyz =
+1 1 4 0 0 0. Note that only Izz matters in 2D simulations.
 
 3 1 27
 4
@@ -235,12 +247,178 @@ particles whose edge length is sqrt(2):
 -0.7071 0.7071 0
 0.7071 0.7071 0
 0.7071 -0.7071 0
-0 1 1 2 2 3 3 0
+0 1
+1 2
+2 3
+3 0
 1.0 :pre
 
+A rod in 2D, whose length is 4.0, mass 1.0, rounded at two ends
+by circles of diameter 0.5, is specified as follows:
+
+1 1 13
+2
+1 1 1.33333 0 0 0
+-2 0 0
+2 0 0
+0.5 :pre
+
+A disk, whose diameter is 3.0, mass 1.0, is specified as follows:
+
+1 1 10
+1
+1 1 4.5 0 0 0
+0 0 0
+3.0 :pre
+
 The "pair_style body/rounded/polygon"_pair_body_rounded_polygon.html
 command can be used with this body style to compute body/body
-interactions.
+interactions.  The "fix wall/body/polygon"_fix_wall_body_polygon.html
+command can be used with this body style to compute the interaction of
+body particles with a wall.
+
+:line
+
+[Specifics of body style rounded/polyhedron:]
+
+The {rounded/polyhedron} body style represents body particles as a 3d
+polyhedron with a variable number of N vertices, E edges and F faces.
+This style can only be used for 3d models; see the
+"boundary"_boundary.html command.  See the "pair_style
+body/rounded/polygon" doc page for a diagram of a two 2d squares with
+rounded circles at the vertices.  A 3d cube with rounded spheres at
+the 8 vertices and 12 rounded edges would be similar.  Special cases
+for N = 1 (sphere) and N = 2 (rod with rounded ends) can also be
+specified.
+
+This body style is for 3d discrete element models, as described in
+"Wang"_#body-Wang.
+
+Similar to body style {rounded/polygon}, the atom_style body command
+for this body style takes two additional arguments:
+
+atom_style body rounded/polyhedron Nmin Nmax
+Nmin = minimum # of vertices in any body in the system
+Nmax = maximum # of vertices in any body in the system :pre
+
+The Nmin and Nmax arguments are used to bound the size of data
+structures used internally by each particle.
+
+When the "read_data"_read_data.html command reads a data file for this
+body style, the following information must be provided for each entry
+in the {Bodies} section of the data file:
+
+atom-ID 3 M
+N E F
+ixx iyy izz ixy ixz iyz
+x1 y1 z1
+...
+xN yN zN
+0 1
+1 2 
+2 3
+...
+0 1 2 -1
+0 2 3 -1
+...
+1 2 3 4
+diameter :pre
+
+where M = 6 + 3*N + 2*E + 4*F + 1, and N is the number of vertices in
+the body particle, E = number of edges, F = number of faces.
+
+The integer line has three values: number of vertices (N), number of
+edges (E) and number of faces (F). The floating point line(s) list 6
+moments of inertia followed by the coordinates of the N vertices (x1
+to zN) as 3N values, followed by 2N vertex indices corresponding to
+the end points of the E edges, then 4*F vertex indices defining F
+faces.  The last value is the diameter value = the rounded diameter of
+the sphere that surrounds each vertex. The diameter value can be
+different for each body particle. These floating-point values can be
+listed on as many lines as you wish; see the
+"read_data"_read_data.html command for more details.  Because the
+maxmimum vertices per face is hard-coded to be 4
+(i.e. quadrilaterals), faces with more than 4 vertices need to be
+split into triangles or quadrilaterals.  For triangular faces, the
+last vertex index should be set to -1.
+
+The ordering of the 4 vertices within a face should follow
+the right-hand rule so that the normal vector of the face points
+outwards from the center of mass.
+
+The 6 moments of inertia (ixx,iyy,izz,ixy,ixz,iyz) should be the
+values consistent with the current orientation of the rigid body
+around its center of mass.  The values are with respect to the
+simulation box XYZ axes, not with respect to the principal axes of the
+rigid body itself.  LAMMPS performs the latter calculation internally.
+The coordinates of each vertex are specified as its x,y,z displacement
+from the center-of-mass of the body particle.  The center-of-mass
+position of the particle is specified by the x,y,z values in the
+{Atoms} section of the data file.
+
+For example, the following information would specify a cubic particle
+whose edge length is 2.0 and rounded diameter is 0.5.
+The orientation of the cube is aligned with the xyz coordinate axes
+which is consistent with the 6 moments of inertia: ixx iyy izz ixy ixz
+iyz = 0.667 0.667 0.667 0 0 0.
+
+1 3 79
+8 12 6
+0.667 0.667 0.667 0 0 0
+1 1 1
+1 -1 1
+-1 -1 1
+-1 1 1
+1 1 -1
+1 -1 -1
+-1 -1 -1
+-1 1 -1
+0 1
+1 2
+2 3
+3 0
+4 5
+5 6
+6 7
+7 4
+0 4
+1 5
+2 6
+3 7
+0 1 2 3
+4 5 6 7
+0 1 5 4
+1 2 6 5
+2 3 7 6
+3 0 4 7
+0.5 :pre
+
+A rod in 3D, whose length is 4.0, mass 1.0 and rounded at two ends
+by circles of diameter 0.5, is specified as follows:
+
+1 1 13
+2
+0 1.33333 1.33333 0 0 0
+-2 0 0
+2 0 0
+0.5 :pre
+
+A sphere whose diameter is 3.0 and mass 1.0, is specified as follows:
+
+1 1 10
+1
+0.9 0.9 0.9 0 0 0
+0 0 0
+3.0 :pre
+
+The "pair_style
+body/rounded/polhedron"_pair_body_rounded_polyhedron.html command can
+be used with this body style to compute body/body interactions.  The
+"fix wall/body/polyhedron"_fix_wall_body_polygon.html command can be
+used with this body style to compute the interaction of body particles
+with a wall.
+
+:line
 
 For output purposes via the "compute
 body/local"_compute_body_local.html and "dump local"_dump.html
@@ -257,10 +435,10 @@ the body particle itself.  These values are calculated using the
 current COM and orientation of the body particle.
 
 For images created by the "dump image"_dump_image.html command, if the
-{body} keyword is set, then each body particle is drawn as a convex
-polygon consisting of N line segments.  Note that the line segments
-are drawn between the N vertices, which does not correspond exactly to
-the physical extent of the body (because the "pair_style
+{body} keyword is set, then each body particle is drawn as a polygon
+consisting of N line segments.  Note that the line segments are drawn
+between the N vertices, which does not correspond exactly to the
+physical extent of the body (because the "pair_style
 rounded/polygon"_pair_body_rounded_polygon.html defines finite-size
 spheres at those point and the line segments between the spheres are
 tangent to the spheres).  The drawn diameter of each line segment is
@@ -269,6 +447,10 @@ determined by the {bflag1} parameter for the {body} keyword.  The
 
 :line
 
-:link(Fraige)
+:link(body-Fraige)
 [(Fraige)] F. Y. Fraige, P. A. Langston, A. J. Matchett, J. Dodds,
 Particuology, 6, 455 (2008).
+
+:link(body-Wang)
+[(Wang)] J. Wang, H. S. Yu, P. A. Langston, F. Y. Fraige, Granular
+Matter, 13, 1 (2011).
diff --git a/doc/src/fix_wall_body_polygon.txt b/doc/src/fix_wall_body_polygon.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4ba16b56c79fab2c4cecf6798a71a895b862c5cf
--- /dev/null
+++ b/doc/src/fix_wall_body_polygon.txt
@@ -0,0 +1,104 @@
+"LAMMPS WWW Site"_lws - "LAMMPS Documentation"_ld - "LAMMPS Commands"_lc :c
+
+:link(lws,http://lammps.sandia.gov)
+:link(ld,Manual.html)
+:link(lc,Section_commands.html#comm)
+
+:line
+
+fix wall/body/polygon command :h3
+
+[Syntax:]
+
+fix ID group-ID wall/body/polygon k_n c_n c_t wallstyle args keyword values ... :pre
+
+ID, group-ID are documented in "fix"_fix.html command :ulb,l
+wall/body/polygon = style name of this fix command :l
+k_n = normal repulsion strength (force/distance or pressure units) :l
+c_n = normal damping coefficient (force/distance or pressure units) :l
+c_t = tangential damping coefficient (force/distance or pressure units) :l
+wallstyle = {xplane} or {yplane} or {zplane} or {zcylinder} :l
+args = list of arguments for a particular style :l
+  {xplane} or {yplane} args = lo hi
+    lo,hi = position of lower and upper plane (distance units), either can be NULL)
+  {zcylinder} args = radius
+    radius = cylinder radius (distance units) :pre
+zero or more keyword/value pairs may be appended to args :l
+keyword = {wiggle} :l
+  {wiggle} values = dim amplitude period
+    dim = {x} or {y} or {z}
+    amplitude = size of oscillation (distance units)
+    period = time of oscillation (time units) :pre
+:ule
+
+[Examples:]
+
+fix 1 all wall/body/polygon 1000.0 20.0 5.0 xplane -10.0 10.0
+
+[Description:]
+
+This fix is for use with 2d models of body particles of style
+{rounded/polygon}.  It bounds the simulation domain with wall(s).  All
+particles in the group interact with the wall when they are close
+enough to touch it.  The nature of the interaction between the wall
+and the polygon particles is the same as that between the polygon
+particles themselves, which is similar to a Hookean potential.  See
+"Section 6.14"_Section_howto.html#howto_14 of the manual and the
+"body"_body.html doc page for more details on using body particles.
+
+The parameters {k_n}, {c_n}, {c_t} have the same meaning and units as
+those specified with the "pair_style
+body/rounded/polygon"_pair_body_rounded_polygon.html command.
+
+The {wallstyle} can be planar or cylindrical.  The 2 planar options
+specify a pair of walls in a dimension.  Wall positions are given by
+{lo} and {hi}.  Either of the values can be specified as NULL if a
+single wall is desired.  For a {zcylinder} wallstyle, the cylinder's
+axis is at x = y = 0.0, and the radius of the cylinder is specified.
+
+Optionally, the wall can be moving, if the {wiggle} keyword is
+appended.
+
+For the {wiggle} keyword, the wall oscillates sinusoidally, similar to
+the oscillations of particles which can be specified by the "fix
+move"_fix_move.html command.  This is useful in packing simulations of
+particles.  The arguments to the {wiggle} keyword specify a dimension
+for the motion, as well as it's {amplitude} and {period}.  Note that
+if the dimension is in the plane of the wall, this is effectively a
+shearing motion.  If the dimension is perpendicular to the wall, it is
+more of a shaking motion.  A {zcylinder} wall can only be wiggled in
+the z dimension.
+
+Each timestep, the position of a wiggled wall in the appropriate {dim}
+is set according to this equation:
+
+position = coord + A - A cos (omega * delta) :pre
+
+where {coord} is the specified initial position of the wall, {A} is
+the {amplitude}, {omega} is 2 PI / {period}, and {delta} is the time
+elapsed since the fix was specified.  The velocity of the wall is set
+to the derivative of this expression.
+
+[Restart, fix_modify, output, run start/stop, minimize info:]
+
+None of the "fix_modify"_fix_modify.html options are relevant to this
+fix.  No global or per-atom quantities are stored by this fix for
+access by various "output commands"_Section_howto.html#howto_15.  No
+parameter of this fix can be used with the {start/stop} keywords of
+the "run"_run.html command.  This fix is not invoked during "energy
+minimization"_minimize.html.
+
+[Restrictions:]
+
+This fix is part of the BODY package.  It is only enabled if LAMMPS
+was built with that package.  See the "Making
+LAMMPS"_Section_start.html#start_3 section for more info.
+
+Any dimension (xy) that has a wall must be non-periodic.
+
+[Related commands:]
+
+"atom_style body"_atom_style.html, "pair_style
+body/rounded/polygon"_pair_body_rounded_polygon.html
+
+[Default:] none
diff --git a/doc/src/fix_wall_body_polyhedron.txt b/doc/src/fix_wall_body_polyhedron.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c937cbdbbc80a6c711c845212a0a47d85d0800be
--- /dev/null
+++ b/doc/src/fix_wall_body_polyhedron.txt
@@ -0,0 +1,103 @@
+"LAMMPS WWW Site"_lws - "LAMMPS Documentation"_ld - "LAMMPS Commands"_lc :c
+
+:link(lws,http://lammps.sandia.gov)
+:link(ld,Manual.html)
+:link(lc,Section_commands.html#comm)
+
+:line
+
+fix wall/body/polyhedron command :h3
+
+[Syntax:]
+
+fix ID group-ID wall/body/polyhedron k_n c_n c_t wallstyle args keyword values ... :pre
+
+ID, group-ID are documented in "fix"_fix.html command :ulb,l
+wall/body/polyhedron = style name of this fix command :l
+k_n = normal repulsion strength (force/distance units or pressure units - see discussion below) :l
+c_n = normal damping coefficient (force/distance units or pressure units - see discussion below) :l
+c_t = tangential damping coefficient (force/distance units or pressure units - see discussion below) :l
+wallstyle = {xplane} or {yplane} or {zplane} or {zcylinder} :l
+args = list of arguments for a particular style :l
+  {xplane} or {yplane} args = lo hi
+    lo,hi = position of lower and upper plane (distance units), either can be NULL)
+  {zcylinder} args = radius
+    radius = cylinder radius (distance units) :pre
+zero or more keyword/value pairs may be appended to args :l
+keyword = {wiggle} :l
+  {wiggle} values = dim amplitude period
+    dim = {x} or {y} or {z}
+    amplitude = size of oscillation (distance units)
+    period = time of oscillation (time units) :pre
+:ule
+
+[Examples:]
+
+fix 1 all wall/body/polyhedron 1000.0 20.0 5.0 xplane -10.0 10.0
+
+[Description:]
+
+This fix is for use with 3d models of body particles of style
+{rounded/polyhedron}.  It bounds the simulation domain with wall(s).
+All particles in the group interact with the wall when they are close
+enough to touch it.  The nature of the interaction between the wall
+and the polygon particles is the same as that between the polygon
+particles themselves, which is similar to a Hookean potential.  See
+"Section 6.14"_Section_howto.html#howto_14 of the manual and the
+"body"_body.html doc page for more details on using body particles.
+
+The parameters {k_n}, {c_n}, {c_t} have the same meaning and units as
+those specified with the "pair_style
+body/rounded/polyhedron"_pair_body_rounded_polyhedron.html command.
+
+The {wallstyle} can be planar or cylindrical.  The 3 planar options
+specify a pair of walls in a dimension.  Wall positions are given by
+{lo} and {hi}.  Either of the values can be specified as NULL if a
+single wall is desired.  For a {zcylinder} wallstyle, the cylinder's
+axis is at x = y = 0.0, and the radius of the cylinder is specified.
+
+Optionally, the wall can be moving, if the {wiggle} keyword is appended.
+
+For the {wiggle} keyword, the wall oscillates sinusoidally, similar to
+the oscillations of particles which can be specified by the "fix
+move"_fix_move.html command.  This is useful in packing simulations of
+particles.  The arguments to the {wiggle} keyword specify a dimension
+for the motion, as well as it's {amplitude} and {period}.  Note that
+if the dimension is in the plane of the wall, this is effectively a
+shearing motion.  If the dimension is perpendicular to the wall, it is
+more of a shaking motion.  A {zcylinder} wall can only be wiggled in
+the z dimension.
+
+Each timestep, the position of a wiggled wall in the appropriate {dim}
+is set according to this equation:
+
+position = coord + A - A cos (omega * delta) :pre
+
+where {coord} is the specified initial position of the wall, {A} is
+the {amplitude}, {omega} is 2 PI / {period}, and {delta} is the time
+elapsed since the fix was specified.  The velocity of the wall is set
+to the derivative of this expression.
+
+[Restart, fix_modify, output, run start/stop, minimize info:]
+
+None of the "fix_modify"_fix_modify.html options are relevant to this
+fix.  No global or per-atom quantities are stored by this fix for
+access by various "output commands"_Section_howto.html#howto_15.  No
+parameter of this fix can be used with the {start/stop} keywords of
+the "run"_run.html command.  This fix is not invoked during "energy
+minimization"_minimize.html.
+
+[Restrictions:]
+
+This fix is part of the BODY package.  It is only enabled if
+LAMMPS was built with that package.  See the "Making
+LAMMPS"_Section_start.html#start_3 section for more info.
+
+Any dimension (xyz) that has a wall must be non-periodic.
+
+[Related commands:]
+
+"atom_style body"_atom_style.html, "pair_style
+body/rounded/polyhedron"_pair_body_rounded_polyhedron.html
+
+[Default:] none
diff --git a/doc/src/lammps.book b/doc/src/lammps.book
index 1f181a1bd35bb6e104127e15f66d0c82fa008e70..06f4bf371853b4f4ccdba35c94bba57022fff9fa 100644
--- a/doc/src/lammps.book
+++ b/doc/src/lammps.book
@@ -283,6 +283,8 @@ fix_vector.html
 fix_viscosity.html
 fix_viscous.html
 fix_wall.html
+fix_wall_body_polygon.html
+fix_wall_body_polyhedron.html
 fix_wall_ees.html
 fix_wall_gran.html
 fix_wall_gran_region.html
@@ -424,8 +426,9 @@ pair_agni.html
 pair_airebo.html
 pair_awpmd.html
 pair_beck.html
-pair_body.html
+pair_body_nparticle.html
 pair_body_rounded_polygon.html
+pair_body_rounded_polyhedron.html
 pair_bop.html
 pair_born.html
 pair_brownian.html
diff --git a/doc/src/pair_body.txt b/doc/src/pair_body_nparticle.txt
similarity index 93%
rename from doc/src/pair_body.txt
rename to doc/src/pair_body_nparticle.txt
index 7899da832bdbf722bb1c5403dd547890bae5cdb1..8c5b6e155d801729a72b99276373ef4b0f525185 100644
--- a/doc/src/pair_body.txt
+++ b/doc/src/pair_body_nparticle.txt
@@ -10,21 +10,21 @@ pair_style body command :h3
 
 [Syntax:]
 
-pair_style body cutoff :pre
+pair_style body/nparticle cutoff :pre
 
 cutoff = global cutoff for interactions (distance units)
 
 [Examples:]
 
-pair_style body 3.0
+pair_style body/nparticle 3.0
 pair_coeff * * 1.0 1.0
 pair_coeff 1 1 1.0 1.5 2.5 :pre
 
 [Description:]
 
-Style {body} is for use with body particles and calculates pairwise
-body/body interactions as well as interactions between body and
-point-particles.  See "Section 6.14"_Section_howto.html#howto_14
+Style {body/nparticle} is for use with body particles and calculates
+pairwise body/body interactions as well as interactions between body
+and point-particles.  See "Section 6.14"_Section_howto.html#howto_14
 of the manual and the "body"_body.html doc page for more details on
 using body particles.
 
diff --git a/doc/src/pair_body_rounded_polygon.txt b/doc/src/pair_body_rounded_polygon.txt
index b6dc2e37b5a80fce04b7b753ffb7e007c4703ae6..9daeb08e9a9e3310380c4b5e40534aecedd59b09 100644
--- a/doc/src/pair_body_rounded_polygon.txt
+++ b/doc/src/pair_body_rounded_polygon.txt
@@ -8,12 +8,127 @@
 
 pair_style body/rounded/polygon command :h3
 
+[Syntax:]
+
+pair_style body/rounded/polygon c_n c_t mu delta_ua cutoff :pre
+
+c_n = normal damping coefficient
+c_t = tangential damping coefficient
+mu = normal friction coefficient during gross sliding
+delta_ua = multiple contact scaling factor
+cutoff = global separation cutoff for interactions (distance units), see below for definition :pre
+
+[Examples:]
+
+pair_style body/rounded/polygon 20.0 5.0 0.0 1.0 0.5
+pair_coeff * * 100.0 1.0
+pair_coeff 1 1 100.0 1.0 :pre
+
 [Description:]
 
-Note: This feature is not yet implemented.
+Style {body/rounded/polygon} is for use with 2d models of body
+particles of style {rounded/polygon}.  It calculates pairwise
+body/body interactions which can include body particles modeled as
+1-vertex circular disks with a specified diameter.  See "Section
+6.14"_Section_howto.html#howto_14 of the manual and the
+"body"_body.html doc page for more details on using body
+rounded/polygon particles.
+
+This pairwise interaction between rounded polygons is described in
+"Fraige"_#pair-Fraige, where a polygon does not have sharp corners,
+but is rounded at its vertices by circles centered on each vertex with
+a specified diameter.  The edges of the polygon are defined between
+pairs of adjacent vertices.  The circle diameter for each polygon is
+specified in the data file read by the "read data"_read_data.html
+command.  This is a 2d discrete element model (DEM) which allows for
+multiple contact points.
+
+Note that when two particles interact, the effective surface of each
+polygon particle is displaced outward from each of its vertices and
+edges by half its circle diameter (as in the diagram below of a gray
+and yellow square particle).  The interaction forces and energies
+between two particles are defined with respect to the separation of
+their respective rounded surfaces, not by the separation of the
+vertices and edges themselves.
+
+This means that the specified cutoff in the pair_style command is the
+cutoff distance, r_c, for the surface separation, \delta_n (see figure
+below).  This is the distance at which two particles no longer
+interact.  If r_c is specified as 0.0, then it is a contact-only
+interaction.  I.e. the two particles must overlap in order to exert a
+repulsive force on each other.  If r_c > 0.0, then the force between
+two particles will be attractive for surface separations from 0 to
+r_c, and repulsive once the particles overlap.
+
+Note that unlike for other pair styles, the specified cutoff is not
+the distance between the centers of two particles at which they stop
+interacting.  This center-to-center distance depends on the shape and
+size of the two particles and their relative orientation.  LAMMPS
+takes that into account when computing the surface separation distance
+and applying the r_c cutoff.
+
+The forces between vertex-vertex, vertex-edge, and edge-edge overlaps
+are given by:
+
+:c,image(Eqs/pair_body_rounded.jpg)
+
+:c,image(JPG/pair_body_rounded.jpg)
+
+Note that F_n and F_t are functions of the surface separation \delta_n
+= d - (R_i + R_j).  In this model, when (R_i + R_j) < d < (R_i + R_j)
++ r_c, that is, 0 < \delta_n < r_c, the cohesive region of the two
+surfaces overlap and the two surfaces are attractive to each other.
+
+In "Fraige"_#pair-Fraige, the tangential friction force between two
+particles that are in contact is modeled differently prior to gross
+sliding (i.e. static friction) and during gross-sliding (kinetic
+friction).  The latter takes place when the tangential deformation
+exceeds the Coulomb frictional limit.  In the current implementation,
+however, we do not take into account frictional history, i.e. we do
+not keep track of how many time steps the two particles have been in
+contact nor calculate the tangential deformation.  Instead, we assume
+that gross sliding takes place as soon as two particles are in
+contact.
+
+The following coefficients must be defined for each pair of atom types
+via the "pair_coeff"_pair_coeff.html command as in the examples above,
+or in the data file read by the "read_data"_read_data.html command:
+
+k_n (energy/distance^2 units)
+k_na (energy/distance^2 units) :ul
+
+Effectively, k_n and k_na are the slopes of the red lines in the plot
+above for force versus surface separation, for \delta_n < 0 and 0 <
+\delta_n < r_c respectively.
+
+[Mixing, shift, table, tail correction, restart, rRESPA info]:
+
+This pair style does not support the "pair_modify"_pair_modify.html
+mix, shift, table, and tail options.
+
+This pair style does not write its information to "binary restart
+files"_restart.html.  Thus, you need to re-specify the pair_style and
+pair_coeff commands in an input script that reads a restart file.
+
+This pair style can only be used via the {pair} keyword of the
+"run_style respa"_run_style.html command.  It does not support the
+{inner}, {middle}, {outer} keywords.
+
+[Restrictions:]
+
+These pair styles are part of the BODY package.  They are only enabled
+if LAMMPS was built with that package.  See the "Making
+LAMMPS"_Section_start.html#start_3 section for more info.
+
+This pair style requires the "newton"_newton.html setting to be "on"
+for pair interactions.
 
 [Related commands:]
 
-"pair_style body"_pair_body.html
+"pair_coeff"_pair_coeff.html
 
 [Default:] none
+
+:link(pair-Fraige)
+[(Fraige)] F. Y. Fraige, P. A. Langston, A. J. Matchett, J. Dodds,
+Particuology, 6, 455 (2008).
diff --git a/doc/src/pair_body_rounded_polyhedron.txt b/doc/src/pair_body_rounded_polyhedron.txt
new file mode 100644
index 0000000000000000000000000000000000000000..dc559feaafb6715e7f4b06800103951725078964
--- /dev/null
+++ b/doc/src/pair_body_rounded_polyhedron.txt
@@ -0,0 +1,130 @@
+"LAMMPS WWW Site"_lws - "LAMMPS Documentation"_ld - "LAMMPS Commands"_lc :c
+
+:link(lws,http://lammps.sandia.gov)
+:link(ld,Manual.html)
+:link(lc,Section_commands.html#comm)
+
+:line
+
+pair_style body/rounded/polyhedron command :h3
+
+[Syntax:]
+
+pair_style body/rounded/polyhedron c_n c_t mu delta_ua cutoff :pre
+
+c_n = normal damping coefficient
+c_t = tangential damping coefficient
+mu = normal friction coefficient during gross sliding
+delta_ua = multiple contact scaling factor
+cutoff = global separation cutoff for interactions (distance units), see below for definition :pre
+
+[Examples:]
+
+pair_style body/rounded/polyhedron 20.0 5.0 0.0 1.0 0.5
+pair_coeff * * 100.0 1.0
+pair_coeff 1 1 100.0 1.0 :pre
+
+[Description:]
+
+Style {body/rounded/polygon} is for use with 3d models of body
+particles of style {rounded/polyhedron}.  It calculates pairwise
+body/body interactions which can include body particles modeled as
+1-vertex spheres with a specified diameter.  See "Section
+6.14"_Section_howto.html#howto_14 of the manual and the
+"body"_body.html doc page for more details on using body
+rounded/polyhedron particles.
+
+This pairwise interaction between the rounded polyhedra is described
+in "Wang"_#pair-Wang, where a polyhedron does not have sharp corners
+and edges, but is rounded at its vertices and edges by spheres
+centered on each vertex with a specified diameter.  The edges if the
+polyhedron are defined between pairs of adjacent vertices.  Its faces
+are defined by a loop of edges.  The sphere diameter for each polygon
+is specified in the data file read by the "read data"_read_data.html
+command.  This is a discrete element model (DEM) which allows for
+multiple contact points.
+
+Note that when two particles interact, the effective surface of each
+polyhedron particle is displaced outward from each of its vertices,
+edges, and faces by half its sphere diameter.  The interaction forces
+and energies between two particles are defined with respect to the
+separation of their respective rounded surfaces, not by the separation
+of the vertices, edges, and faces themselves.
+
+This means that the specified cutoff in the pair_style command is the
+cutoff distance, r_c, for the surface separation, \delta_n (see figure
+below).  This is the distance at which two particles no longer
+interact.  If r_c is specified as 0.0, then it is a contact-only
+interaction.  I.e. the two particles must overlap in order to exert a
+repulsive force on each other.  If r_c > 0.0, then the force between
+two particles will be attractive for surface separations from 0 to
+r_c, and repulsive once the particles overlap.
+
+Note that unlike for other pair styles, the specified cutoff is not
+the distance between the centers of two particles at which they stop
+interacting.  This center-to-center distance depends on the shape and
+size of the two particles and their relative orientation.  LAMMPS
+takes that into account when computing the surface separation distance
+and applying the r_c cutoff.
+
+The forces between vertex-vertex, vertex-edge, vertex-face, edge-edge,
+and edge-face overlaps are given by:
+
+:c,image(Eqs/pair_body_rounded.jpg)
+
+:c,image(JPG/pair_body_rounded.jpg)
+
+In "Wang"_#pair-Wang, the tangential friction force between two
+particles that are in contact is modeled differently prior to gross
+sliding (i.e. static friction) and during gross-sliding (kinetic
+friction).  The latter takes place when the tangential deformation
+exceeds the Coulomb frictional limit.  In the current implementation,
+however, we do not take into account frictional history, i.e. we do
+not keep track of how many time steps the two particles have been in
+contact nor calculate the tangential deformation.  Instead, we assume
+that gross sliding takes place as soon as two particles are in
+contact.
+
+The following coefficients must be defined for each pair of atom types
+via the "pair_coeff"_pair_coeff.html command as in the examples above,
+or in the data file read by the "read_data"_read_data.html command:
+
+k_n (energy/distance^2 units)
+k_na (energy/distance^2 units) :ul
+
+Effectively, k_n and k_na are the slopes of the red lines in the plot
+above for force versus surface separation, for \delta_n < 0 and 0 <
+\delta_n < r_c respectively.
+
+[Mixing, shift, table, tail correction, restart, rRESPA info]:
+
+This pair style does not support the "pair_modify"_pair_modify.html
+mix, shift, table, and tail options.
+
+This pair style does not write its information to "binary restart
+files"_restart.html.  Thus, you need to re-specify the pair_style and
+pair_coeff commands in an input script that reads a restart file.
+
+This pair style can only be used via the {pair} keyword of the
+"run_style respa"_run_style.html command.  It does not support the
+{inner}, {middle}, {outer} keywords.
+
+[Restrictions:]
+
+These pair styles are part of the BODY package.  They are only enabled
+if LAMMPS was built with that package.  See the "Making
+LAMMPS"_Section_start.html#start_3 section for more info.
+
+This pair style requires the "newton"_newton.html setting to be "on"
+for pair interactions.
+
+[Related commands:]
+
+"pair_coeff"_pair_coeff.html
+
+[Default:] none
+
+:link(pair-Wang)
+[(Wang)] J. Wang, H. S. Yu, P. A. Langston, F. Y. Fraige, Granular
+Matter, 13, 1 (2011).
+
diff --git a/examples/body/data.cubes b/examples/body/data.cubes
new file mode 100644
index 0000000000000000000000000000000000000000..c1323ca3503bb10745df9a9bc176ce531850a4eb
--- /dev/null
+++ b/examples/body/data.cubes
@@ -0,0 +1,76 @@
+LAMMPS data file for polygons: cubes, moment of inertia I = m edge^2/ 6
+2 atoms
+2 bodies
+1 atom types
+0 6 xlo xhi
+0 6 ylo yhi
+0 6 zlo zhi
+
+Atoms
+
+1 1 1 1 1.5 1.5 1.5
+2 1 1 1 4.0 4.0 4.0
+
+Bodies
+
+1 3 79
+8 12 6
+0.667 0.667 0.667 0 0 0
+1 1 1
+1 -1 1
+-1 -1 1
+-1 1 1
+1 1 -1
+1 -1 -1
+-1 -1 -1
+-1 1 -1
+0 1
+1 2
+2 3
+3 0
+4 5
+5 6
+6 7
+7 4
+0 4
+1 5
+2 6
+3 7
+0 1 2 3
+4 5 6 7
+0 1 5 4
+1 2 6 5
+2 3 7 6
+3 0 4 7
+0.5
+2 3 79
+8 12 6
+0.667 0.667 0.667 0 0 0
+1 1 1
+1 -1 1
+-1 -1 1
+-1 1 1
+1 1 -1
+1 -1 -1
+-1 -1 -1
+-1 1 -1
+0 1
+1 2
+2 3
+3 0
+4 5
+5 6
+6 7
+7 4
+0 4
+1 5
+2 6
+3 7
+0 1 2 3
+4 5 6 7
+0 1 5 4
+1 2 6 5
+2 3 7 6
+3 0 4 7
+0.5
+
diff --git a/examples/body/data.squares b/examples/body/data.squares
new file mode 100755
index 0000000000000000000000000000000000000000..6b198fd422dc0e5d8f736d70bcd9906ca133898e
--- /dev/null
+++ b/examples/body/data.squares
@@ -0,0 +1,32 @@
+LAMMPS data file for polygons: squares of edge length L: Izz = 1/6mL^2
+2 atoms
+2 bodies
+1 atom types
+0 12 xlo xhi
+0 12 ylo yhi
+-0.5 0.5 zlo zhi
+
+Atoms
+
+1 1 1 1 4 5 0
+2 1 1 1 9 6 0
+
+Bodies
+
+1 1 19
+4
+1 1 2.67 0 0 0
+-2 -2 0
+-2 2 0
+2 2 0
+2 -2 0
+0.5
+2 1 19
+4
+1 1 2.67 0 0 0
+-2 -2 0
+-2 2 0
+2 2 0
+2 -2 0
+0.5
+
diff --git a/examples/body/in.body b/examples/body/in.body
index 5879ed5e45bc8810e82df81fdac0928306ae9e23..815b8531545a4348fb54dd1553abcfb642f44bf5 100644
--- a/examples/body/in.body
+++ b/examples/body/in.body
@@ -8,7 +8,7 @@ read_data       data.body
 
 velocity	all create 1.44 87287 loop geom
 
-pair_style	body 5.0
+pair_style	body/nparticle 5.0
 pair_coeff	* * 1.0 1.0
 
 neighbor	0.5 bin
diff --git a/examples/body/in.cubes b/examples/body/in.cubes
new file mode 100644
index 0000000000000000000000000000000000000000..a22599fe9607873aab7bc2214f59b8268415814d
--- /dev/null
+++ b/examples/body/in.cubes
@@ -0,0 +1,53 @@
+# 3d rounded cubes
+
+variable    r     index 3
+variable    steps index 10000
+
+units       lj
+dimension   3
+
+atom_style  body rounded/polyhedron 1 10
+
+read_data   data.cubes
+
+replicate   $r $r $r
+
+velocity    all create 1.2 187287 dist gaussian mom yes rot yes
+
+variable cut_inner  equal 0.5
+variable k_n        equal 100
+variable k_na       equal 1
+variable c_n        equal 20
+variable c_t        equal 5
+variable mu         equal 0
+variable A_ua       equal 1
+
+pair_style body/rounded/polyhedron ${c_n} ${c_t} ${mu} ${A_ua} ${cut_inner}
+pair_coeff * * ${k_n} ${k_na}
+
+comm_modify vel yes
+
+neighbor     0.5 bin
+neigh_modify every 1 delay 0 check yes
+
+timestep     0.001
+
+#fix          1 all nve/body
+fix          1 all nvt/body temp 1.2 1.2 0.1
+#fix          1 all npt/body temp 1.2 1.2 0.1 iso 0.002 0.02 1.0
+
+compute      p2 all pressure 1_temp
+
+#compute      1 all body/local id 1 2 3
+#dump         1 all local 1000 dump.* index c_1[1] c_1[2] c_1[3] c_1[4]
+
+#dump         2 all image 1000 image.*.jpg type type &
+#             zoom 1.5 adiam 1.5 body type 0 0 view 60 15
+#dump_modify  2 pad 6
+
+thermo_style custom step ke pe etotal c_p2 c_1_temp
+
+thermo       1000
+
+run          ${steps}
+
diff --git a/examples/body/in.pour3d b/examples/body/in.pour3d
new file mode 100644
index 0000000000000000000000000000000000000000..bcba950e593606065837cdbaeb85a4376fd0c4c4
--- /dev/null
+++ b/examples/body/in.pour3d
@@ -0,0 +1,57 @@
+# pouring 3d rounded polyhedron bodies
+
+variable    steps index 6000
+
+units       lj
+boundary    p p fm
+comm_modify vel yes
+
+atom_style  body rounded/polyhedron 1 8
+atom_modify map array
+
+region		reg block 0 50 0 50 0 50 units box
+create_box	4 reg
+
+variable cut_inner  equal 0.5
+variable k_n        equal 100
+variable k_na       equal 5
+variable c_n        equal 20
+variable c_t        equal 5
+variable mu         equal 0
+variable A_ua       equal 1
+
+pair_style body/rounded/polyhedron ${c_n} ${c_t} ${mu} ${A_ua} ${cut_inner}
+pair_coeff * * ${k_n} ${k_na}
+
+neighbor     0.5 bin
+neigh_modify every 1 delay 0 check yes
+
+timestep     0.001
+
+fix          1 all nve/body
+fix          2 all gravity 1.0 spherical 0.0 -180.0
+
+molecule     object molecule.cube molecule.tetra toff 1 &
+             molecule.rod3d toff 2 molecule.point3d toff 3
+
+region       slab block 5 45 5 45 25 35 units box
+fix          ins all pour 500 0 4767548 vol 0.4 10 region slab mol object &
+             molfrac 0.25 0.25 0.25 0.25
+
+fix          4 all wall/body/polyhedron 2000 50 50 zplane 0.0 NULL
+
+#compute      1 all body/local type 1 2 3
+#dump         1 all local 1000 dump.polyhedron index c_1[1] c_1[2] c_1[3] c_1[4]
+#dump         10 all custom 1000 tmp.dump id type x y z radius
+
+thermo_style custom step atoms ke pe etotal press
+
+thermo       1000
+
+#dump	     2 all image 500 image.*.jpg type type &
+#	     zoom 1.5 adiam 1.5 body type 0 0 view 75 15
+#dump_modify  2 pad 6
+
+run	     ${steps}
+
+
diff --git a/examples/body/in.squares b/examples/body/in.squares
new file mode 100755
index 0000000000000000000000000000000000000000..3b05b5cead88cd6265b17ce2772873b97c475383
--- /dev/null
+++ b/examples/body/in.squares
@@ -0,0 +1,55 @@
+# 2d rounded polygon bodies
+
+variable    r     index 4
+variable    steps index 100000
+variable    T     index 0.5
+variable    P     index 0.1
+variable    seed  index 980411
+
+units       lj
+dimension   2
+
+atom_style  body rounded/polygon 1 6
+atom_modify map array
+read_data   data.squares
+
+replicate   $r $r 1
+
+velocity    all create $T ${seed} dist gaussian mom yes rot yes
+
+variable cut_inner  equal 0.5
+variable k_n        equal 100
+variable k_na       equal 2
+variable c_n        equal 1
+variable c_t        equal 1
+variable mu         equal 0.1
+variable delta_ua   equal 0.5
+
+pair_style body/rounded/polygon ${c_n} ${c_t} ${mu} ${delta_ua} ${cut_inner}
+pair_coeff * * ${k_n} ${k_na}
+
+comm_modify vel yes
+
+neighbor     0.5 bin
+neigh_modify every 1 delay 0 check yes
+
+timestep     0.001
+
+#fix         1 all nve/body
+#fix         1 all nvt/body temp $T $T 1.0
+fix          1 all npt/body temp $T $T 1.0 x 0.001 $P 1.0 &
+             y 0.001 $P 1.0 couple xy fixedpoint 0 0 0
+
+fix          2 all enforce2d
+
+#compute      1 all body/local id 1 2 3
+#dump         1 all local 100000 dump.polygon.* index c_1[1] c_1[2] c_1[3] c_1[4]
+
+thermo_style custom step ke pe etotal press
+thermo       1000
+
+#dump	     2 all image 10000 image.*.jpg type type zoom 2.0 &
+#             adiam 1.5 body type 0 0
+#dump_modify  2 pad 6
+
+run          ${steps}
diff --git a/examples/body/in.wall2d b/examples/body/in.wall2d
new file mode 100755
index 0000000000000000000000000000000000000000..04e7f31cb6810151570f164353f683ac6b675afa
--- /dev/null
+++ b/examples/body/in.wall2d
@@ -0,0 +1,57 @@
+# 2d rounded polygon bodies
+
+variable    r     index 4
+variable    steps index 100000
+variable    T     index 0.5
+variable    P     index 0.1
+variable    seed  index 980411
+
+units       lj
+dimension   2
+
+atom_style  body rounded/polygon 1 6
+atom_modify map array
+read_data   data.squares
+
+replicate   $r $r 1
+
+velocity    all create $T ${seed} dist gaussian mom yes rot yes
+
+change_box  all boundary p f p
+
+variable cut_inner  equal 0.5
+variable k_n        equal 100
+variable k_na       equal 2
+variable c_n        equal 0.1
+variable c_t        equal 0.1
+variable mu         equal 0.1
+variable delta_ua   equal 0.5
+
+pair_style body/rounded/polygon ${c_n} ${c_t} ${mu} ${delta_ua} ${cut_inner}
+pair_coeff * * ${k_n} ${k_na}
+
+comm_modify vel yes
+
+neighbor     0.5 bin
+neigh_modify every 1 delay 0 check yes
+
+timestep     0.001
+
+#fix         1 all nve/body
+#fix         1 all nvt/body temp $T $T 1.0
+fix          1 all npt/body temp $T $T 1.0 x 0.001 $P 1.0 fixedpoint 0 0 0
+
+fix          2 all enforce2d
+fix          3 all wall/body/polygon 2000 50 50 yplane 0.0 48.0
+
+#compute      1 all body/local id 1 2 3
+#dump         1 all local 100000 dump.polygon.* index c_1[1] c_1[2] c_1[3] c_1[4]
+
+thermo_style custom step ke pe etotal press
+thermo       1000
+
+#dump	     2 all image 10000 image.*.jpg type type zoom 2.0 &
+#             adiam 1.5 body type 0 0
+#dump_modify  2 pad 6
+
+run          ${steps}
diff --git a/examples/body/log.9Jul18.body.cubes.g++.1 b/examples/body/log.9Jul18.body.cubes.g++.1
new file mode 100644
index 0000000000000000000000000000000000000000..c9a799c0b51192a5420fb75fb8aeaa32a0b1a3df
--- /dev/null
+++ b/examples/body/log.9Jul18.body.cubes.g++.1
@@ -0,0 +1,125 @@
+LAMMPS (29 Jun 2018)
+# 3d rounded cubes
+
+variable    r     index 3
+variable    steps index 10000
+
+units       lj
+dimension   3
+
+atom_style  body rounded/polyhedron 1 10
+
+read_data   data.cubes
+  orthogonal box = (0 0 0) to (6 6 6)
+  1 by 1 by 1 MPI processor grid
+  reading atoms ...
+  2 atoms
+  2 bodies
+
+replicate   $r $r $r
+replicate   3 $r $r
+replicate   3 3 $r
+replicate   3 3 3
+  orthogonal box = (0 0 0) to (18 18 18)
+  1 by 1 by 1 MPI processor grid
+  54 atoms
+  Time spent = 0.000217915 secs
+
+velocity    all create 1.2 187287 dist gaussian mom yes rot yes
+
+variable cut_inner  equal 0.5
+variable k_n        equal 100
+variable k_na       equal 1
+variable c_n        equal 20
+variable c_t        equal 5
+variable mu         equal 0
+variable A_ua       equal 1
+
+pair_style body/rounded/polyhedron ${c_n} ${c_t} ${mu} ${A_ua} ${cut_inner}
+pair_style body/rounded/polyhedron 20 ${c_t} ${mu} ${A_ua} ${cut_inner}
+pair_style body/rounded/polyhedron 20 5 ${mu} ${A_ua} ${cut_inner}
+pair_style body/rounded/polyhedron 20 5 0 ${A_ua} ${cut_inner}
+pair_style body/rounded/polyhedron 20 5 0 1 ${cut_inner}
+pair_style body/rounded/polyhedron 20 5 0 1 0.5
+pair_coeff * * ${k_n} ${k_na}
+pair_coeff * * 100 ${k_na}
+pair_coeff * * 100 1
+
+comm_modify vel yes
+
+neighbor     0.5 bin
+neigh_modify every 1 delay 0 check yes
+
+timestep     0.001
+
+#fix          1 all nve/body
+fix          1 all nvt/body temp 1.2 1.2 0.1
+#fix          1 all npt/body temp 1.2 1.2 0.1 iso 0.002 0.02 1.0
+
+compute      p2 all pressure 1_temp
+
+#compute      1 all body/local id 1 2 3
+#dump         1 all local 1000 dump.* index c_1[1] c_1[2] c_1[3] c_1[4]
+
+#dump         2 all image 1000 image.*.jpg type type #             zoom 1.5 adiam 1.5 body type 0 0 view 60 15
+#dump_modify  2 pad 6
+
+thermo_style custom step ke pe etotal c_p2 c_1_temp
+
+thermo       1000
+
+run          ${steps}
+run          10000
+Neighbor list info ...
+  update every 1 steps, delay 0 steps, check yes
+  max neighbors/atom: 2000, page size: 100000
+  master list distance cutoff = 3.9641
+  ghost atom cutoff = 3.9641
+  binsize = 1.98205, bins = 10 10 10
+  1 neighbor lists, perpetual/occasional/extra = 1 0 0
+  (1) pair body/rounded/polyhedron, perpetual
+      attributes: half, newton on
+      pair build: half/bin/atomonly/newton
+      stencil: half/bin/3d/newton
+      bin: standard
+Per MPI rank memory allocation (min/avg/max) = 4.952 | 4.952 | 4.952 Mbytes
+Step KinEng PotEng TotEng c_p2 c_1_temp 
+       0    1.7666667            0    1.7666667   0.01090535   0.59439252 
+    1000    3.1462962   0.17392649    3.3202227   0.02361912    1.1654694 
+    2000    2.9311648   0.13836102    3.0695258  0.021748224    1.1950624 
+    3000     3.090491   0.16511199     3.255603  0.018691142      1.23672 
+    4000    2.7401565   0.17792155    2.9180781  0.015093853    1.1180839 
+    5000    3.0880849   0.17587085    3.2639557  0.030563042    1.2831154 
+    6000    3.2180776   0.19732251    3.4154001  0.028338151     1.258839 
+    7000    2.9514882   0.25088882     3.202377  0.025296925    1.1746326 
+    8000    3.0101226   0.28825968    3.2983823  0.027273454    1.2138056 
+    9000    3.0164253    0.1901733    3.2065986  0.033228915    1.3095914 
+   10000    2.3780401   0.34082434    2.7188644  0.031838531    1.0208679 
+Loop time of 38.5686 on 1 procs for 10000 steps with 54 atoms
+
+Performance: 22401.653 tau/day, 259.278 timesteps/s
+100.0% CPU use with 1 MPI tasks x no OpenMP threads
+
+MPI task timing breakdown:
+Section |  min time  |  avg time  |  max time  |%varavg| %total
+---------------------------------------------------------------
+Pair    | 38.426     | 38.426     | 38.426     |   0.0 | 99.63
+Neigh   | 0.0043154  | 0.0043154  | 0.0043154  |   0.0 |  0.01
+Comm    | 0.047616   | 0.047616   | 0.047616   |   0.0 |  0.12
+Output  | 0.00017595 | 0.00017595 | 0.00017595 |   0.0 |  0.00
+Modify  | 0.082948   | 0.082948   | 0.082948   |   0.0 |  0.22
+Other   |            | 0.007761   |            |       |  0.02
+
+Nlocal:    54 ave 54 max 54 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+Nghost:    96 ave 96 max 96 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+Neighs:    100 ave 100 max 100 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+
+Total # of neighbors = 100
+Ave neighs/atom = 1.85185
+Neighbor list builds = 268
+Dangerous builds = 0
+
+Total wall time: 0:00:38
diff --git a/examples/body/log.9Jul18.body.cubes.g++.4 b/examples/body/log.9Jul18.body.cubes.g++.4
new file mode 100644
index 0000000000000000000000000000000000000000..e2407e972532938d39ba0e62964d74dc9c69740f
--- /dev/null
+++ b/examples/body/log.9Jul18.body.cubes.g++.4
@@ -0,0 +1,125 @@
+LAMMPS (29 Jun 2018)
+# 3d rounded cubes
+
+variable    r     index 3
+variable    steps index 10000
+
+units       lj
+dimension   3
+
+atom_style  body rounded/polyhedron 1 10
+
+read_data   data.cubes
+  orthogonal box = (0 0 0) to (6 6 6)
+  1 by 2 by 2 MPI processor grid
+  reading atoms ...
+  2 atoms
+  2 bodies
+
+replicate   $r $r $r
+replicate   3 $r $r
+replicate   3 3 $r
+replicate   3 3 3
+  orthogonal box = (0 0 0) to (18 18 18)
+  1 by 2 by 2 MPI processor grid
+  54 atoms
+  Time spent = 0.00103807 secs
+
+velocity    all create 1.2 187287 dist gaussian mom yes rot yes
+
+variable cut_inner  equal 0.5
+variable k_n        equal 100
+variable k_na       equal 1
+variable c_n        equal 20
+variable c_t        equal 5
+variable mu         equal 0
+variable A_ua       equal 1
+
+pair_style body/rounded/polyhedron ${c_n} ${c_t} ${mu} ${A_ua} ${cut_inner}
+pair_style body/rounded/polyhedron 20 ${c_t} ${mu} ${A_ua} ${cut_inner}
+pair_style body/rounded/polyhedron 20 5 ${mu} ${A_ua} ${cut_inner}
+pair_style body/rounded/polyhedron 20 5 0 ${A_ua} ${cut_inner}
+pair_style body/rounded/polyhedron 20 5 0 1 ${cut_inner}
+pair_style body/rounded/polyhedron 20 5 0 1 0.5
+pair_coeff * * ${k_n} ${k_na}
+pair_coeff * * 100 ${k_na}
+pair_coeff * * 100 1
+
+comm_modify vel yes
+
+neighbor     0.5 bin
+neigh_modify every 1 delay 0 check yes
+
+timestep     0.001
+
+#fix          1 all nve/body
+fix          1 all nvt/body temp 1.2 1.2 0.1
+#fix          1 all npt/body temp 1.2 1.2 0.1 iso 0.002 0.02 1.0
+
+compute      p2 all pressure 1_temp
+
+#compute      1 all body/local id 1 2 3
+#dump         1 all local 1000 dump.* index c_1[1] c_1[2] c_1[3] c_1[4]
+
+#dump         2 all image 1000 image.*.jpg type type #             zoom 1.5 adiam 1.5 body type 0 0 view 60 15
+#dump_modify  2 pad 6
+
+thermo_style custom step ke pe etotal c_p2 c_1_temp
+
+thermo       1000
+
+run          ${steps}
+run          10000
+Neighbor list info ...
+  update every 1 steps, delay 0 steps, check yes
+  max neighbors/atom: 2000, page size: 100000
+  master list distance cutoff = 3.9641
+  ghost atom cutoff = 3.9641
+  binsize = 1.98205, bins = 10 10 10
+  1 neighbor lists, perpetual/occasional/extra = 1 0 0
+  (1) pair body/rounded/polyhedron, perpetual
+      attributes: half, newton on
+      pair build: half/bin/atomonly/newton
+      stencil: half/bin/3d/newton
+      bin: standard
+Per MPI rank memory allocation (min/avg/max) = 4.879 | 5.068 | 5.256 Mbytes
+Step KinEng PotEng TotEng c_p2 c_1_temp 
+       0    1.7666667            0    1.7666667   0.01090535   0.59439252 
+    1000    3.1462962   0.17392649    3.3202227   0.02361912    1.1654694 
+    2000    2.9311648   0.13836102    3.0695258  0.021748224    1.1950624 
+    3000     3.090491   0.16511199     3.255603  0.018691142      1.23672 
+    4000    2.7401565   0.17792155    2.9180781  0.015093853    1.1180839 
+    5000    3.0880849   0.17587085    3.2639557  0.030563042    1.2831154 
+    6000    3.2180776   0.19732251    3.4154001  0.028338151     1.258839 
+    7000    2.9514882   0.25088882     3.202377  0.025296925    1.1746326 
+    8000    3.0101226   0.28825968    3.2983823  0.027273454    1.2138056 
+    9000    3.0164253    0.1901733    3.2065986  0.033228915    1.3095914 
+   10000    2.3780401   0.34082434    2.7188644  0.031838531    1.0208679 
+Loop time of 20.5306 on 4 procs for 10000 steps with 54 atoms
+
+Performance: 42083.509 tau/day, 487.078 timesteps/s
+100.0% CPU use with 4 MPI tasks x no OpenMP threads
+
+MPI task timing breakdown:
+Section |  min time  |  avg time  |  max time  |%varavg| %total
+---------------------------------------------------------------
+Pair    | 7.5288     | 10.878     | 19.952     | 159.0 | 52.98
+Neigh   | 0.0014424  | 0.0016552  | 0.0021195  |   0.7 |  0.01
+Comm    | 0.50623    | 9.5805     | 12.93      | 169.4 | 46.66
+Output  | 0.00011921 | 0.00014341 | 0.00021386 |   0.0 |  0.00
+Modify  | 0.044663   | 0.047684   | 0.05382    |   1.6 |  0.23
+Other   |            | 0.023      |            |       |  0.11
+
+Nlocal:    13.5 ave 17 max 9 min
+Histogram: 1 0 0 1 0 0 0 0 1 1
+Nghost:    63.5 ave 68 max 58 min
+Histogram: 1 0 0 1 0 0 0 0 0 2
+Neighs:    25 ave 38 max 6 min
+Histogram: 1 0 0 0 0 1 0 0 1 1
+
+Total # of neighbors = 100
+Ave neighs/atom = 1.85185
+Neighbor list builds = 268
+Dangerous builds = 0
+
+Total wall time: 0:00:20
diff --git a/examples/body/log.9Jul18.body.pour3d.g++.1 b/examples/body/log.9Jul18.body.pour3d.g++.1
new file mode 100644
index 0000000000000000000000000000000000000000..213dd2e18fa9959a87ca922760e53508cace973d
--- /dev/null
+++ b/examples/body/log.9Jul18.body.pour3d.g++.1
@@ -0,0 +1,138 @@
+LAMMPS (29 Jun 2018)
+# pouring 3d rounded polyhedron bodies
+
+variable    steps index 6000
+
+units       lj
+boundary    p p fm
+comm_modify vel yes
+
+atom_style  body rounded/polyhedron 1 8
+atom_modify map array
+
+region		reg block 0 50 0 50 0 50 units box
+create_box	4 reg
+Created orthogonal box = (0 0 0) to (50 50 50)
+  1 by 1 by 1 MPI processor grid
+
+variable cut_inner  equal 0.5
+variable k_n        equal 100
+variable k_na       equal 5
+variable c_n        equal 20
+variable c_t        equal 5
+variable mu         equal 0
+variable A_ua       equal 1
+
+pair_style body/rounded/polyhedron ${c_n} ${c_t} ${mu} ${A_ua} ${cut_inner}
+pair_style body/rounded/polyhedron 20 ${c_t} ${mu} ${A_ua} ${cut_inner}
+pair_style body/rounded/polyhedron 20 5 ${mu} ${A_ua} ${cut_inner}
+pair_style body/rounded/polyhedron 20 5 0 ${A_ua} ${cut_inner}
+pair_style body/rounded/polyhedron 20 5 0 1 ${cut_inner}
+pair_style body/rounded/polyhedron 20 5 0 1 0.5
+pair_coeff * * ${k_n} ${k_na}
+pair_coeff * * 100 ${k_na}
+pair_coeff * * 100 5
+
+neighbor     0.5 bin
+neigh_modify every 1 delay 0 check yes
+
+timestep     0.001
+
+fix          1 all nve/body
+fix          2 all gravity 1.0 spherical 0.0 -180.0
+
+molecule     object molecule.cube molecule.tetra toff 1              molecule.rod3d toff 2 molecule.point3d toff 3
+Read molecule object:
+  1 atoms with max type 1
+  0 bonds with max type 0
+  0 angles with max type 0
+  0 dihedrals with max type 0
+  0 impropers with max type 0
+Read molecule object:
+  1 atoms with max type 2
+  0 bonds with max type 0
+  0 angles with max type 0
+  0 dihedrals with max type 0
+  0 impropers with max type 0
+Read molecule object:
+  1 atoms with max type 3
+  0 bonds with max type 0
+  0 angles with max type 0
+  0 dihedrals with max type 0
+  0 impropers with max type 0
+Read molecule object:
+  1 atoms with max type 4
+  0 bonds with max type 0
+  0 angles with max type 0
+  0 dihedrals with max type 0
+  0 impropers with max type 0
+
+region       slab block 5 45 5 45 25 35 units box
+fix          ins all pour 500 0 4767548 vol 0.4 10 region slab mol object              molfrac 0.25 0.25 0.25 0.25
+Particle insertion: 134 every 4472 steps, 500 by step 13417
+
+fix          4 all wall/body/polyhedron 2000 50 50 zplane 0.0 NULL
+
+#compute      1 all body/local type 1 2 3
+#dump         1 all local 1000 dump.polyhedron index c_1[1] c_1[2] c_1[3] c_1[4]
+#dump         10 all custom 1000 tmp.dump id type x y z radius
+
+thermo_style custom step atoms ke pe etotal press
+
+thermo       1000
+
+#dump	     2 all image 500 image.*.jpg type type #	     zoom 1.5 adiam 1.5 body type 0 0 view 75 15
+#dump_modify  2 pad 6
+
+run	     ${steps}
+run	     6000
+Neighbor list info ...
+  update every 1 steps, delay 0 steps, check yes
+  max neighbors/atom: 2000, page size: 100000
+  master list distance cutoff = 5
+  ghost atom cutoff = 5
+  binsize = 2.5, bins = 20 20 20
+  1 neighbor lists, perpetual/occasional/extra = 1 0 0
+  (1) pair body/rounded/polyhedron, perpetual
+      attributes: half, newton on
+      pair build: half/bin/atomonly/newton
+      stencil: half/bin/3d/newton
+      bin: standard
+Per MPI rank memory allocation (min/avg/max) = 0.5065 | 0.5065 | 0.5065 Mbytes
+Step Atoms KinEng PotEng TotEng Press 
+       0        0           -0            0            0            0 
+    1000      134           -0 0.00083010524 0.00083010524 -2.1515152e-06 
+    2000      134           -0 -0.00069962476 -0.00069962476 -1.4170663e-08 
+    3000      134           -0 -0.00069962687 -0.00069962687 -4.1478181e-11 
+    4000      134           -0 -0.00069962687 -0.00069962687 -1.2141026e-13 
+    5000      268           -0  0.014969705  0.014969705 3.0797164e-05 
+    6000      268           -0  0.042467887  0.042467887 0.00056148005 
+Loop time of 0.634737 on 1 procs for 6000 steps with 268 atoms
+
+Performance: 816716.196 tau/day, 9452.734 timesteps/s
+100.0% CPU use with 1 MPI tasks x no OpenMP threads
+
+MPI task timing breakdown:
+Section |  min time  |  avg time  |  max time  |%varavg| %total
+---------------------------------------------------------------
+Pair    | 0.41391    | 0.41391    | 0.41391    |   0.0 | 65.21
+Neigh   | 0.010547   | 0.010547   | 0.010547   |   0.0 |  1.66
+Comm    | 0.0030921  | 0.0030921  | 0.0030921  |   0.0 |  0.49
+Output  | 0.00011492 | 0.00011492 | 0.00011492 |   0.0 |  0.02
+Modify  | 0.19736    | 0.19736    | 0.19736    |   0.0 | 31.09
+Other   |            | 0.009719   |            |       |  1.53
+
+Nlocal:    268 ave 268 max 268 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+Nghost:    3 ave 3 max 3 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+Neighs:    68 ave 68 max 68 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+
+Total # of neighbors = 68
+Ave neighs/atom = 0.253731
+Neighbor list builds = 168
+Dangerous builds = 0
+
+
+Total wall time: 0:00:00
diff --git a/examples/body/log.9Jul18.body.squares.g++.1 b/examples/body/log.9Jul18.body.squares.g++.1
new file mode 100644
index 0000000000000000000000000000000000000000..7b539797bd6d399ade37eeb28d580447c8721d9e
--- /dev/null
+++ b/examples/body/log.9Jul18.body.squares.g++.1
@@ -0,0 +1,221 @@
+LAMMPS (29 Jun 2018)
+# 2d rounded polygon bodies
+
+variable    r     index 4
+variable    steps index 100000
+variable    T     index 0.5
+variable    P     index 0.1
+variable    seed  index 980411
+
+units       lj
+dimension   2
+
+atom_style  body rounded/polygon 1 6
+atom_modify map array
+read_data   data.squares
+  orthogonal box = (0 0 -0.5) to (12 12 0.5)
+  1 by 1 by 1 MPI processor grid
+  reading atoms ...
+  2 atoms
+  2 bodies
+
+replicate   $r $r 1
+replicate   4 $r 1
+replicate   4 4 1
+  orthogonal box = (0 0 -0.5) to (48 48 0.5)
+  1 by 1 by 1 MPI processor grid
+  32 atoms
+  Time spent = 0.00020504 secs
+
+velocity    all create $T ${seed} dist gaussian mom yes rot yes
+velocity    all create 0.5 ${seed} dist gaussian mom yes rot yes
+velocity    all create 0.5 980411 dist gaussian mom yes rot yes
+
+variable cut_inner  equal 0.5
+variable k_n        equal 100
+variable k_na       equal 2
+variable c_n        equal 1
+variable c_t        equal 1
+variable mu         equal 0.1
+variable delta_ua   equal 0.5
+
+pair_style body/rounded/polygon ${c_n} ${c_t} ${mu} ${delta_ua} ${cut_inner}
+pair_style body/rounded/polygon 1 ${c_t} ${mu} ${delta_ua} ${cut_inner}
+pair_style body/rounded/polygon 1 1 ${mu} ${delta_ua} ${cut_inner}
+pair_style body/rounded/polygon 1 1 0.1 ${delta_ua} ${cut_inner}
+pair_style body/rounded/polygon 1 1 0.1 0.5 ${cut_inner}
+pair_style body/rounded/polygon 1 1 0.1 0.5 0.5
+pair_coeff * * ${k_n} ${k_na}
+pair_coeff * * 100 ${k_na}
+pair_coeff * * 100 2
+
+comm_modify vel yes
+
+neighbor     0.5 bin
+neigh_modify every 1 delay 0 check yes
+
+timestep     0.001
+
+#fix         1 all nve/body
+#fix         1 all nvt/body temp $T $T 1.0
+fix          1 all npt/body temp $T $T 1.0 x 0.001 $P 1.0              y 0.001 $P 1.0 couple xy fixedpoint 0 0 0
+fix          1 all npt/body temp 0.5 $T 1.0 x 0.001 $P 1.0              y 0.001 $P 1.0 couple xy fixedpoint 0 0 0
+fix          1 all npt/body temp 0.5 0.5 1.0 x 0.001 $P 1.0              y 0.001 $P 1.0 couple xy fixedpoint 0 0 0
+fix          1 all npt/body temp 0.5 0.5 1.0 x 0.001 0.1 1.0              y 0.001 $P 1.0 couple xy fixedpoint 0 0 0
+fix          1 all npt/body temp 0.5 0.5 1.0 x 0.001 0.1 1.0              y 0.001 0.1 1.0 couple xy fixedpoint 0 0 0
+
+fix          2 all enforce2d
+
+#compute      1 all body/local id 1 2 3
+#dump         1 all local 100000 dump.polygon.* index c_1[1] c_1[2] c_1[3] c_1[4]
+
+thermo_style custom step ke pe etotal press
+thermo       1000
+
+#dump	     2 all image 10000 image.*.jpg type type zoom 2.0 #             adiam 1.5 body type 0 0
+#dump_modify  2 pad 6
+
+run          ${steps}
+run          100000
+Neighbor list info ...
+  update every 1 steps, delay 0 steps, check yes
+  max neighbors/atom: 2000, page size: 100000
+  master list distance cutoff = 6.15685
+  ghost atom cutoff = 6.15685
+  binsize = 3.07843, bins = 16 16 1
+  1 neighbor lists, perpetual/occasional/extra = 1 0 0
+  (1) pair body/rounded/polygon, perpetual
+      attributes: half, newton on
+      pair build: half/bin/atomonly/newton
+      stencil: half/bin/2d/newton
+      bin: standard
+Per MPI rank memory allocation (min/avg/max) = 4.781 | 4.781 | 4.781 Mbytes
+Step KinEng PotEng TotEng Press 
+       0     0.484375         0.25     0.734375 0.0067274306 
+    1000   0.39423376 0.0017918048   0.39602557 0.0021941612 
+    2000   0.42284177   0.01346585   0.43630762 0.0029377883 
+    3000   0.58154405  0.011321689   0.59286574  0.003667871 
+    4000   0.73518304  0.034603175   0.76978621 0.0018689207 
+    5000   0.84367476  0.025292163   0.86896692 0.0089161373 
+    6000   0.70803236 0.0085631016   0.71659546 0.0045552895 
+    7000   0.56206452   0.10453031   0.66659483  0.010255161 
+    8000   0.64538994  0.088817673   0.73420761 0.0037633655 
+    9000   0.90540819  0.063696004   0.96910419 0.0077673359 
+   10000   0.68632042  0.093265016   0.77958544 0.0057864838 
+   11000   0.59118074  0.025654748   0.61683549  0.012518759 
+   12000   0.67522767  0.038176401   0.71340407   0.01741153 
+   13000    0.7644843   0.10429844   0.86878274  0.013161339 
+   14000   0.56152694  0.067836655   0.62936359  0.016852121 
+   15000   0.41895506  0.019513348   0.43846841  0.015225695 
+   16000   0.55799421    0.1564559   0.71445011  0.011703561 
+   17000   0.59391964  0.034450221   0.62836986  0.026215002 
+   18000   0.75911858  0.030885726    0.7900043  0.018396366 
+   19000   0.64417995   0.12110912   0.76528907  0.010247952 
+   20000   0.57751435   0.16965651   0.74717086  0.023392323 
+   21000    0.7613368   0.13405354   0.89539034  0.021498982 
+   22000   0.57676692   0.18011879   0.75688571  0.024469161 
+   23000   0.54043723   0.11842026   0.65885749  0.019799067 
+   24000   0.62276061  0.038967924   0.66172853  0.019080086 
+   25000   0.53157536   0.11651937   0.64809473  0.017019298 
+   26000   0.72213293  0.039012448   0.76114538  0.015434904 
+   27000   0.62157832   0.13697494   0.75855326  0.028711011 
+   28000   0.41323738   0.16301101   0.57624839  0.041792632 
+   29000   0.45774328   0.17569066   0.63343394  0.019975231 
+   30000   0.78901796  0.099791386   0.88880934  0.024116947 
+   31000   0.85205397   0.11977547   0.97182945  0.026667489 
+   32000   0.37137095    0.1232622   0.49463315 0.00087637364 
+   33000   0.26860871   0.26056381   0.52917252  0.036110517 
+   34000    0.3018636   0.21336905   0.51523265  0.040315549 
+   35000   0.39915129   0.28245957   0.68161085  0.034876856 
+   36000   0.25761236    0.2352705   0.49288286  0.022772767 
+   37000    0.1071233   0.31692858   0.42405188  0.017994666 
+   38000  0.083729577   0.28473145   0.36846103 -0.0045370431 
+   39000  0.070355565   0.26682083   0.33717639  0.017921556 
+   40000  0.075894079   0.20077896   0.27667304  0.014873186 
+   41000   0.05891028   0.15989064   0.21880092  0.025547873 
+   42000    0.1225107   0.16583605   0.28834675  0.038842785 
+   43000   0.17049189   0.14323991    0.3137318  0.029550161 
+   44000   0.26823939   0.15208257   0.42032196  0.028113612 
+   45000   0.10172203    0.1729706   0.27469264 -0.013769913 
+   46000   0.14841355   0.19085074   0.33926429 -0.00073741985 
+   47000   0.27654927   0.19097937   0.46752864   0.04021431 
+   48000   0.53432331  0.080769923   0.61509323  0.029932845 
+   49000   0.69111634   0.13064951   0.82176585  0.028985406 
+   50000   0.24520806   0.18317453   0.42838258   0.05179746 
+   51000   0.23541368   0.14281364   0.37822732  0.071884238 
+   52000   0.25464996  0.095730242    0.3503802  0.034488204 
+   53000   0.53677633    0.1058745   0.64265084  0.059932498 
+   54000   0.32970921   0.27979128   0.60950049  0.062869716 
+   55000   0.49094054  0.096735015   0.58767556   0.04728005 
+   56000   0.54398249    0.2216472   0.76562969  0.056712022 
+   57000   0.60869068    0.2338422   0.84253288  0.077143302 
+   58000   0.72175509   0.18687368   0.90862877  0.019357656 
+   59000   0.79442757  0.092502981   0.88693055  0.066882632 
+   60000    0.6810555  0.077699385   0.75875488  0.095975173 
+   61000   0.63178834   0.05071143   0.68249977  0.043586668 
+   62000   0.76589344  0.044615704   0.81050914  0.085718411 
+   63000   0.84815889  0.030527848   0.87868674  0.053072795 
+   64000    0.7309043  0.051938637   0.78284294  0.058887766 
+   65000   0.62498816  0.034474465   0.65946262  0.068446407 
+   66000   0.69817494  0.068546004   0.76672094  0.062634433 
+   67000   0.86444275  0.010184259   0.87462701  0.073635055 
+   68000   0.77820319 0.0079319524   0.78613515  0.090330925 
+   69000   0.56938919 0.0092629332   0.57865213  0.061838729 
+   70000   0.61870712  0.010047381    0.6287545  0.066501338 
+   71000   0.71651803 0.0088366199   0.72535465  0.079136316 
+   72000   0.76278925  0.008828151   0.77161741  0.063672771 
+   73000   0.75447428 0.0083985526   0.76287283  0.078256913 
+   74000   0.66185251 0.0091910052   0.67104351  0.069840511 
+   75000   0.58458829 0.0097671568   0.59435544  0.076123422 
+   76000    0.7487564    0.0100022    0.7587586  0.076171741 
+   77000   0.89505465  0.009250681   0.90430533  0.074921699 
+   78000   0.73738164 0.0092029279   0.74658457  0.078835344 
+   79000   0.65735281  0.010099528   0.66745233  0.077940627 
+   80000   0.70247542  0.010306464   0.71278189  0.079560093 
+   81000   0.74839505  0.010199092   0.75859415  0.080835104 
+   82000   0.75193767  0.010274058   0.76221173  0.081086684 
+   83000   0.71392598  0.010495573   0.72442156  0.082746145 
+   84000   0.58498928  0.011027388   0.59601667   0.08356465 
+   85000   0.59022869  0.011729474   0.60195817  0.084519397 
+   86000   0.81753578  0.011208964   0.82874475  0.085490261 
+   87000   0.83480682  0.010542579    0.8453494  0.086268527 
+   88000   0.67322538  0.011170734   0.68439611   0.08751623 
+   89000   0.62637389  0.012033316    0.6384072  0.088548094 
+   90000   0.92828557  0.011750388   0.94003596  0.089199823 
+   91000   0.96072564  0.010324509   0.97105015  0.090204803 
+   92000   0.72105071  0.011484152   0.73253486   0.09140819 
+   93000   0.65762527  0.012558219   0.67018349  0.092453474 
+   94000   0.73991591   0.01261909     0.752535  0.093373477 
+   95000   0.91791653  0.011980455   0.92989699  0.094182136 
+   96000   0.76562561  0.011807085    0.7774327  0.095323684 
+   97000   0.57292104  0.013610205   0.58653124  0.096505977 
+   98000   0.68141076  0.013863204   0.69527396  0.097380069 
+   99000   0.82390969  0.013002341   0.83691203  0.098235926 
+  100000   0.77639728  0.012989342   0.78938662  0.099274147 
+Loop time of 3.88899 on 1 procs for 100000 steps with 32 atoms
+
+Performance: 2221655.884 tau/day, 25713.610 timesteps/s
+99.9% CPU use with 1 MPI tasks x no OpenMP threads
+
+MPI task timing breakdown:
+Section |  min time  |  avg time  |  max time  |%varavg| %total
+---------------------------------------------------------------
+Pair    | 3.056      | 3.056      | 3.056      |   0.0 | 78.58
+Neigh   | 0.0051048  | 0.0051048  | 0.0051048  |   0.0 |  0.13
+Comm    | 0.091444   | 0.091444   | 0.091444   |   0.0 |  2.35
+Output  | 0.0011995  | 0.0011995  | 0.0011995  |   0.0 |  0.03
+Modify  | 0.69909    | 0.69909    | 0.69909    |   0.0 | 17.98
+Other   |            | 0.03616    |            |       |  0.93
+
+Nlocal:    32 ave 32 max 32 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+Nghost:    21 ave 21 max 21 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+Neighs:    57 ave 57 max 57 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+
+Total # of neighbors = 57
+Ave neighs/atom = 1.78125
+Neighbor list builds = 1445
+Dangerous builds = 0
+Total wall time: 0:00:03
diff --git a/examples/body/log.9Jul18.body.squares.g++.4 b/examples/body/log.9Jul18.body.squares.g++.4
new file mode 100644
index 0000000000000000000000000000000000000000..56d7734b7ba2a1919c1581799bb2eb83660aa4c9
--- /dev/null
+++ b/examples/body/log.9Jul18.body.squares.g++.4
@@ -0,0 +1,221 @@
+LAMMPS (29 Jun 2018)
+# 2d rounded polygon bodies
+
+variable    r     index 4
+variable    steps index 100000
+variable    T     index 0.5
+variable    P     index 0.1
+variable    seed  index 980411
+
+units       lj
+dimension   2
+
+atom_style  body rounded/polygon 1 6
+atom_modify map array
+read_data   data.squares
+  orthogonal box = (0 0 -0.5) to (12 12 0.5)
+  2 by 2 by 1 MPI processor grid
+  reading atoms ...
+  2 atoms
+  2 bodies
+
+replicate   $r $r 1
+replicate   4 $r 1
+replicate   4 4 1
+  orthogonal box = (0 0 -0.5) to (48 48 0.5)
+  2 by 2 by 1 MPI processor grid
+  32 atoms
+  Time spent = 0.000324011 secs
+
+velocity    all create $T ${seed} dist gaussian mom yes rot yes
+velocity    all create 0.5 ${seed} dist gaussian mom yes rot yes
+velocity    all create 0.5 980411 dist gaussian mom yes rot yes
+
+variable cut_inner  equal 0.5
+variable k_n        equal 100
+variable k_na       equal 2
+variable c_n        equal 1
+variable c_t        equal 1
+variable mu         equal 0.1
+variable delta_ua   equal 0.5
+
+pair_style body/rounded/polygon ${c_n} ${c_t} ${mu} ${delta_ua} ${cut_inner}
+pair_style body/rounded/polygon 1 ${c_t} ${mu} ${delta_ua} ${cut_inner}
+pair_style body/rounded/polygon 1 1 ${mu} ${delta_ua} ${cut_inner}
+pair_style body/rounded/polygon 1 1 0.1 ${delta_ua} ${cut_inner}
+pair_style body/rounded/polygon 1 1 0.1 0.5 ${cut_inner}
+pair_style body/rounded/polygon 1 1 0.1 0.5 0.5
+pair_coeff * * ${k_n} ${k_na}
+pair_coeff * * 100 ${k_na}
+pair_coeff * * 100 2
+
+comm_modify vel yes
+
+neighbor     0.5 bin
+neigh_modify every 1 delay 0 check yes
+
+timestep     0.001
+
+#fix         1 all nve/body
+#fix         1 all nvt/body temp $T $T 1.0
+fix          1 all npt/body temp $T $T 1.0 x 0.001 $P 1.0              y 0.001 $P 1.0 couple xy fixedpoint 0 0 0
+fix          1 all npt/body temp 0.5 $T 1.0 x 0.001 $P 1.0              y 0.001 $P 1.0 couple xy fixedpoint 0 0 0
+fix          1 all npt/body temp 0.5 0.5 1.0 x 0.001 $P 1.0              y 0.001 $P 1.0 couple xy fixedpoint 0 0 0
+fix          1 all npt/body temp 0.5 0.5 1.0 x 0.001 0.1 1.0              y 0.001 $P 1.0 couple xy fixedpoint 0 0 0
+fix          1 all npt/body temp 0.5 0.5 1.0 x 0.001 0.1 1.0              y 0.001 0.1 1.0 couple xy fixedpoint 0 0 0
+
+fix          2 all enforce2d
+
+#compute      1 all body/local id 1 2 3
+#dump         1 all local 100000 dump.polygon.* index c_1[1] c_1[2] c_1[3] c_1[4]
+
+thermo_style custom step ke pe etotal press
+thermo       1000
+
+#dump	     2 all image 10000 image.*.jpg type type zoom 2.0 #             adiam 1.5 body type 0 0
+#dump_modify  2 pad 6
+
+run          ${steps}
+run          100000
+Neighbor list info ...
+  update every 1 steps, delay 0 steps, check yes
+  max neighbors/atom: 2000, page size: 100000
+  master list distance cutoff = 6.15685
+  ghost atom cutoff = 6.15685
+  binsize = 3.07843, bins = 16 16 1
+  1 neighbor lists, perpetual/occasional/extra = 1 0 0
+  (1) pair body/rounded/polygon, perpetual
+      attributes: half, newton on
+      pair build: half/bin/atomonly/newton
+      stencil: half/bin/2d/newton
+      bin: standard
+Per MPI rank memory allocation (min/avg/max) = 4.774 | 4.774 | 4.774 Mbytes
+Step KinEng PotEng TotEng Press 
+       0     0.484375         0.25     0.734375 0.0067274306 
+    1000   0.39423376 0.0017918048   0.39602557 0.0021941612 
+    2000   0.42284177   0.01346585   0.43630762 0.0029377883 
+    3000   0.58154405  0.011321689   0.59286574  0.003667871 
+    4000   0.73518304  0.034603175   0.76978621 0.0018689207 
+    5000   0.84367476  0.025292163   0.86896692 0.0089161373 
+    6000   0.70803236 0.0085631016   0.71659546 0.0045552895 
+    7000   0.56206452   0.10453031   0.66659483  0.010255161 
+    8000   0.64538994  0.088817673   0.73420761 0.0037633655 
+    9000   0.90540819  0.063696004   0.96910419 0.0077673359 
+   10000   0.68632042  0.093265016   0.77958544 0.0057864837 
+   11000   0.59118074  0.025654748   0.61683549  0.012518759 
+   12000   0.67522767  0.038176401   0.71340407   0.01741153 
+   13000    0.7644843   0.10429844   0.86878274  0.013161339 
+   14000   0.56152694  0.067836656    0.6293636  0.016852113 
+   15000   0.41895505  0.019513353   0.43846841  0.015225696 
+   16000   0.55799443   0.15645637    0.7144508  0.011703646 
+   17000   0.59385248   0.03451986   0.62837234  0.025482966 
+   18000   0.75902169  0.031103586   0.79012527  0.018263354 
+   19000   0.64266826   0.12535314   0.76802141  0.014884119 
+   20000   0.57836261   0.16581188   0.74417449  0.024667165 
+   21000   0.78281936   0.11877527   0.90159464 -0.0090089213 
+   22000    0.5312006   0.13300874   0.66420934  0.025797278 
+   23000   0.56458861  0.084369128   0.64895774  0.024630917 
+   24000   0.65126875   0.06122992   0.71249867  0.034377198 
+   25000   0.55173441   0.15694886   0.70868327  0.021634086 
+   26000   0.59121615   0.17071182   0.76192797  0.024758366 
+   27000    0.6394843   0.17442949   0.81391378  0.034919937 
+   28000   0.31144221   0.41243036   0.72387256  0.074115225 
+   29000   0.13516917    0.3075419   0.44271107  0.023861298 
+   30000   0.14094934   0.24407203   0.38502137  0.037030438 
+   31000   0.26313749  0.087395422   0.35053291  0.042347005 
+   32000   0.51602457  0.063012079   0.57903664  0.018550299 
+   33000   0.55628829     0.200213   0.75650129  0.026507686 
+   34000   0.97399408  0.082504517    1.0564986  0.037889878 
+   35000   0.64710533   0.17662002   0.82372535  0.058295508 
+   36000   0.45769083   0.08241194   0.54010277  0.014957415 
+   37000   0.72850105  0.053874061   0.78237512  0.037194593 
+   38000   0.44177995   0.28939498   0.73117493  0.045194029 
+   39000   0.46828451  0.077630686   0.54591519  0.089849009 
+   40000   0.46786451  0.092828423   0.56069294  0.028042052 
+   41000   0.71861856  0.097085715   0.81570427  0.036473296 
+   42000   0.74121021   0.10553127   0.84674148  0.054058843 
+   43000   0.62945489   0.12770673   0.75716161  0.047267994 
+   44000   0.49900638  0.085150056   0.58415644  0.054798793 
+   45000   0.70199572  0.063415877    0.7654116  0.038363546 
+   46000   0.49513142   0.10649384   0.60162526  0.059392561 
+   47000    0.3858898  0.079458749   0.46534855  0.051825764 
+   48000   0.62585854  0.028585902   0.65444444  0.054074424 
+   49000   0.65934482   0.51865062    1.1779954 -0.035272836 
+   50000    0.5420438  0.082056756   0.62410056  0.031187494 
+   51000   0.36685223   0.14224019   0.50909241  0.073790397 
+   52000   0.19044627   0.15368389   0.34413016  0.059034266 
+   53000   0.26847678  0.075693324    0.3441701  0.032276915 
+   54000    0.3593711   0.19034549   0.54971659  0.070827883 
+   55000   0.21659198    0.1929074   0.40949939  0.035916364 
+   56000   0.28242715   0.12313241   0.40555956  0.062083926 
+   57000   0.34067475   0.14711992   0.48779467  0.059321458 
+   58000    0.4842796   0.16143425   0.64571385  0.059048247 
+   59000   0.84438871  0.076546849   0.92093556  0.048046901 
+   60000   0.92794849  0.054331626   0.98228012  0.058392272 
+   61000    0.6916736  0.076168342   0.76784194  0.058654987 
+   62000   0.63317965  0.094506389   0.72768604  0.061044719 
+   63000   0.63317266  0.038785593   0.67195825  0.097236147 
+   64000   0.81696668     0.121811   0.93877769  0.064935373 
+   65000   0.82644758   0.25188344     1.078331  0.093352359 
+   66000   0.64975019   0.17930857   0.82905876  0.058805254 
+   67000   0.63487678   0.16877059   0.80364737  0.070254696 
+   68000   0.79140717   0.11631004    0.9077172  0.064646394 
+   69000   0.85687272  0.057835331   0.91470805  0.071057291 
+   70000   0.67785976  0.040686768   0.71854653  0.074687222 
+   71000   0.60594577  0.032193155   0.63813893  0.069349268 
+   72000   0.77586745  0.024068533   0.79993598  0.083394193 
+   73000   0.88877625  0.025746326   0.91452258  0.081511105 
+   74000   0.73507888  0.036574786   0.77165367  0.075360233 
+   75000   0.68787782  0.042098622   0.72997644  0.068651098 
+   76000   0.72515745   0.04360868   0.76876613  0.069594624 
+   77000   0.77580944  0.041826702   0.81763614  0.071937144 
+   78000   0.76640394  0.039285046   0.80568899  0.074274921 
+   79000   0.62504309  0.039593585   0.66463667  0.076443295 
+   80000   0.60001642  0.043468215   0.64348464  0.094547719 
+   81000   0.82175037  0.045608873   0.86735924  0.080186295 
+   82000   0.85783276  0.042692576   0.90052534  0.081576548 
+   83000   0.71367707  0.042172193   0.75584926   0.08256625 
+   84000   0.68532406  0.044724759   0.73004882  0.083672013 
+   85000   0.72576789  0.046982462   0.77275035  0.084789331 
+   86000   0.75597701   0.04765086   0.80362787  0.085758056 
+   87000   0.74190598  0.047629096   0.78953507  0.086679976 
+   88000   0.60967704  0.049906172   0.65958321  0.085526191 
+   89000   0.54490288  0.054768238   0.59967112  0.090604027 
+   90000   0.75398341  0.057153453   0.81113686  0.091900858 
+   91000   0.84577472  0.052753512   0.89852823  0.091913909 
+   92000    0.7176235  0.050677427   0.76830093  0.092032507 
+   93000   0.61699446  0.054097013   0.67109147  0.092071275 
+   94000   0.76330752  0.057398618   0.82070614  0.092435043 
+   95000   0.98754458  0.053801311    1.0413459  0.093526707 
+   96000    0.7405897  0.052135628   0.79272533  0.095011929 
+   97000   0.65587599  0.057011962   0.71288795  0.096692123 
+   98000   0.72345634  0.060700171   0.78415651  0.097510345 
+   99000   0.88283624  0.061795247   0.94463149   0.09799633 
+  100000   0.86303812  0.058912988   0.92195111   0.09892993 
+Loop time of 2.80074 on 4 procs for 100000 steps with 32 atoms
+
+Performance: 3084895.573 tau/day, 35704.810 timesteps/s
+99.9% CPU use with 4 MPI tasks x no OpenMP threads
+
+MPI task timing breakdown:
+Section |  min time  |  avg time  |  max time  |%varavg| %total
+---------------------------------------------------------------
+Pair    | 0.81169    | 0.89466    | 0.97669    |   8.4 | 31.94
+Neigh   | 0.0017524  | 0.0018129  | 0.0018773  |   0.1 |  0.06
+Comm    | 0.91307    | 0.99193    | 1.0691     |   7.3 | 35.42
+Output  | 0.00076914 | 0.00093722 | 0.0013936  |   0.0 |  0.03
+Modify  | 0.75335    | 0.75779    | 0.76346    |   0.4 | 27.06
+Other   |            | 0.1536     |            |       |  5.48
+
+Nlocal:    8 ave 10 max 4 min
+Histogram: 1 0 0 0 0 0 1 0 0 2
+Nghost:    17.25 ave 19 max 15 min
+Histogram: 1 0 1 0 0 0 0 0 0 2
+Neighs:    13.5 ave 21 max 5 min
+Histogram: 1 0 0 0 1 0 1 0 0 1
+
+Total # of neighbors = 54
+Ave neighs/atom = 1.6875
+Neighbor list builds = 1443
+Dangerous builds = 0
+Total wall time: 0:00:02
diff --git a/examples/body/log.9Jul18.body.wall2d.g++.1 b/examples/body/log.9Jul18.body.wall2d.g++.1
new file mode 100644
index 0000000000000000000000000000000000000000..f22c3663802a487158fffc74c13931f9d83a9416
--- /dev/null
+++ b/examples/body/log.9Jul18.body.wall2d.g++.1
@@ -0,0 +1,223 @@
+LAMMPS (29 Jun 2018)
+# 2d rounded polygon bodies
+
+variable    r     index 4
+variable    steps index 100000
+variable    T     index 0.5
+variable    P     index 0.1
+variable    seed  index 980411
+
+units       lj
+dimension   2
+
+atom_style  body rounded/polygon 1 6
+atom_modify map array
+read_data   data.squares
+  orthogonal box = (0 0 -0.5) to (12 12 0.5)
+  1 by 1 by 1 MPI processor grid
+  reading atoms ...
+  2 atoms
+  2 bodies
+
+replicate   $r $r 1
+replicate   4 $r 1
+replicate   4 4 1
+  orthogonal box = (0 0 -0.5) to (48 48 0.5)
+  1 by 1 by 1 MPI processor grid
+  32 atoms
+  Time spent = 0.00029707 secs
+
+velocity    all create $T ${seed} dist gaussian mom yes rot yes
+velocity    all create 0.5 ${seed} dist gaussian mom yes rot yes
+velocity    all create 0.5 980411 dist gaussian mom yes rot yes
+
+change_box  all boundary p f p
+
+variable cut_inner  equal 0.5
+variable k_n        equal 100
+variable k_na       equal 2
+variable c_n        equal 0.1
+variable c_t        equal 0.1
+variable mu         equal 0.1
+variable delta_ua   equal 0.5
+
+pair_style body/rounded/polygon ${c_n} ${c_t} ${mu} ${delta_ua} ${cut_inner}
+pair_style body/rounded/polygon 0.1 ${c_t} ${mu} ${delta_ua} ${cut_inner}
+pair_style body/rounded/polygon 0.1 0.1 ${mu} ${delta_ua} ${cut_inner}
+pair_style body/rounded/polygon 0.1 0.1 0.1 ${delta_ua} ${cut_inner}
+pair_style body/rounded/polygon 0.1 0.1 0.1 0.5 ${cut_inner}
+pair_style body/rounded/polygon 0.1 0.1 0.1 0.5 0.5
+pair_coeff * * ${k_n} ${k_na}
+pair_coeff * * 100 ${k_na}
+pair_coeff * * 100 2
+
+comm_modify vel yes
+
+neighbor     0.5 bin
+neigh_modify every 1 delay 0 check yes
+
+timestep     0.001
+
+#fix         1 all nve/body
+#fix         1 all nvt/body temp $T $T 1.0
+fix          1 all npt/body temp $T $T 1.0 x 0.001 $P 1.0 fixedpoint 0 0 0
+fix          1 all npt/body temp 0.5 $T 1.0 x 0.001 $P 1.0 fixedpoint 0 0 0
+fix          1 all npt/body temp 0.5 0.5 1.0 x 0.001 $P 1.0 fixedpoint 0 0 0
+fix          1 all npt/body temp 0.5 0.5 1.0 x 0.001 0.1 1.0 fixedpoint 0 0 0
+
+fix          2 all enforce2d
+fix          3 all wall/body/polygon 2000 50 50 yplane 0.0 48.0
+
+#compute      1 all body/local id 1 2 3
+#dump         1 all local 100000 dump.polygon.* index c_1[1] c_1[2] c_1[3] c_1[4]
+
+thermo_style custom step ke pe etotal press
+thermo       1000
+
+#dump	     2 all image 10000 image.*.jpg type type zoom 2.0 #             adiam 1.5 body type 0 0
+#dump_modify  2 pad 6
+
+run          ${steps}
+run          100000
+Neighbor list info ...
+  update every 1 steps, delay 0 steps, check yes
+  max neighbors/atom: 2000, page size: 100000
+  master list distance cutoff = 6.15685
+  ghost atom cutoff = 6.15685
+  binsize = 3.07843, bins = 16 16 1
+  1 neighbor lists, perpetual/occasional/extra = 1 0 0
+  (1) pair body/rounded/polygon, perpetual
+      attributes: half, newton on
+      pair build: half/bin/atomonly/newton
+      stencil: half/bin/2d/newton
+      bin: standard
+Per MPI rank memory allocation (min/avg/max) = 4.771 | 4.771 | 4.771 Mbytes
+Step KinEng PotEng TotEng Press 
+       0     0.484375         0.25     0.734375 0.0067274306 
+    1000   0.49241101 0.0031318767   0.49554289  0.017768281 
+    2000   0.56118632 0.0026068888   0.56379321  0.003410416 
+    3000   0.75565115  0.025578366   0.78122951 0.0071862988 
+    4000   0.72298647  0.093150646   0.81613712  0.003190158 
+    5000   0.51684166  0.049164868   0.56600653 0.0096960168 
+    6000   0.56627905  0.048132853    0.6144119  0.020733586 
+    7000   0.58122129  0.018223718   0.59944501 0.0038160759 
+    8000   0.64297977  0.025934821   0.66891459 0.0041091784 
+    9000   0.41748404 0.0077890042   0.42527305 0.0039270065 
+   10000   0.35738377  0.078487805   0.43587158 3.9079782e-05 
+   11000   0.41529308   0.13619284   0.55148592 -0.0067482285 
+   12000   0.43274718  0.071315497   0.50406268  0.007006378 
+   13000    0.4748331  0.069904647   0.54473775 0.0010384372 
+   14000    0.6287791   0.12721033   0.75598943 0.0047792448 
+   15000    0.4692413   0.12344005   0.59268136  0.018033616 
+   16000   0.43157074   0.14306789   0.57463862  0.042356676 
+   17000   0.53085999   0.22126296   0.75212294  0.027509646 
+   18000   0.52688968   0.13225282    0.6591425 0.0021558013 
+   19000   0.55032328   0.12513047   0.67545375  0.025036251 
+   20000   0.48465097    0.1431055   0.62775647  0.017193781 
+   21000   0.53166734   0.21928574   0.75095307  0.011564317 
+   22000   0.62177353   0.09296159   0.71473512  0.017660922 
+   23000    0.6972939   0.12434123   0.82163514  0.024432327 
+   24000   0.42767372   0.22152311   0.64919684 -0.013712449 
+   25000    0.4816037   0.19272865   0.67433236  0.052386055 
+   26000   0.72642579   0.19697046   0.92339625  0.020407694 
+   27000   0.39649144   0.15058326    0.5470747  0.023705766 
+   28000   0.44896324   0.18500106    0.6339643 -0.0089410286 
+   29000    0.5565759   0.11085772   0.66743362  0.048437166 
+   30000   0.58173584   0.21773281   0.79946865 0.0057357773 
+   31000   0.49199415   0.23601982   0.72801397  0.046744152 
+   32000   0.55665496   0.20542161   0.76207658 -0.0038756805 
+   33000   0.62730739   0.24460524   0.87191263  0.045330682 
+   34000   0.58107044   0.16395278   0.74502322 -0.0049496051 
+   35000   0.56838849   0.21842922   0.78681771 0.0062086036 
+   36000   0.45910273   0.28464172   0.74374445 -0.011700747 
+   37000   0.37092037   0.27646862     0.647389  0.022305679 
+   38000    0.7278047   0.30674438    1.0345491   0.07698342 
+   39000    0.5132923   0.27395066   0.78724295  0.026898634 
+   40000   0.62348649   0.24424644   0.86773293  0.039403899 
+   41000    0.3658401   0.15512326   0.52096337  0.022559003 
+   42000    0.4912253   0.35712978   0.84835508 -0.010336341 
+   43000   0.70225957   0.36314638    1.0654059  0.004148866 
+   44000   0.56958157   0.25488927   0.82447084  0.067537066 
+   45000   0.45854352   0.30149439   0.76003791 -0.017002401 
+   46000   0.62787247   0.34567995   0.97355242   0.11894801 
+   47000   0.61348914   0.29378625   0.90727539  0.067873976 
+   48000   0.71301829   0.34135284    1.0543711  0.021077736 
+   49000   0.53520804   0.30593196   0.84113999 0.0059257647 
+   50000   0.44966403   0.35370793   0.80337195 0.0020395669 
+   51000    0.5236113   0.32296924   0.84658054 -0.051011506 
+   52000   0.53905573     0.351771   0.89082672  0.013720106 
+   53000   0.55978158   0.41293947   0.97272106  0.068558589 
+   54000   0.52170459    0.2718066    0.7935112 0.0093138985 
+   55000   0.61078876   0.43353897    1.0443277  0.045377392 
+   56000   0.51300655   0.33182278   0.84482933 -0.018418487 
+   57000   0.54882822   0.38380093   0.93262915   0.10249946 
+   58000   0.72106212   0.45361279    1.1746749  0.030313481 
+   59000   0.55871447   0.63823029    1.1969448  0.019079703 
+   60000   0.49395192   0.58283102    1.0767829    0.0179349 
+   61000   0.45991079   0.62540573    1.0853165  0.074398804 
+   62000    0.4655788   0.60862262    1.0742014   0.11472976 
+   63000   0.55634524   0.63069255    1.1870378 -0.0025676135 
+   64000   0.57688903   0.45435264    1.0312417 0.0083813852 
+   65000   0.57168922   0.42217005   0.99385927  0.044931269 
+   66000    0.6206044   0.46727538    1.0878798  0.019686229 
+   67000   0.61037155   0.41840109    1.0287726    0.0195109 
+   68000   0.63848598   0.41305347    1.0515395  0.072940144 
+   69000   0.49244916    0.3834095   0.87585866   0.07963677 
+   70000   0.41847062   0.51907975   0.93755037   0.18447904 
+   71000   0.45198986   0.52973709   0.98172695  0.078419371 
+   72000   0.47064262   0.37808165   0.84872427 -0.00046308054 
+   73000    0.6690143   0.37549359    1.0445079  0.061208432 
+   74000   0.60444955   0.33779636   0.94224592 -0.068840321 
+   75000   0.61762382    0.3916421    1.0092659   0.16253292 
+   76000   0.63657961   0.50277989    1.1393595  0.013857508 
+   77000   0.52524028   0.43597896   0.96121924  -0.03296482 
+   78000   0.43803533   0.33172284   0.76975817  0.078763029 
+   79000   0.67156089   0.55272177    1.2242827  0.080822223 
+   80000   0.68678238   0.46061627    1.1473987 0.0027036992 
+   81000   0.64956678   0.44959229    1.0991591   0.11201483 
+   82000   0.51060477   0.43508342    0.9456882  0.028000608 
+   83000   0.59550548   0.69026083    1.2857663 -0.0015809004 
+   84000   0.64222145   0.38768816    1.0299096  0.014153173 
+   85000    0.7661229   0.43445261    1.2005755  0.048034534 
+   86000   0.60025257   0.53027929    1.1305319 0.0056865157 
+   87000   0.46220939   0.47470035   0.93690974  0.075311946 
+   88000   0.54123847   0.62899839    1.1702369   0.13260162 
+   89000   0.61212272    0.6114241    1.2235468  0.033284822 
+   90000   0.63924773    0.6916249    1.3308726  0.045088296 
+   91000   0.49316865   0.51037033     1.003539  0.023203598 
+   92000   0.57572123   0.43496319    1.0106844     0.297092 
+   93000   0.65187559   0.56815972    1.2200353    0.1538215 
+   94000   0.64107331   0.58948521    1.2305585  0.031117778 
+   95000   0.64584158    0.6364688    1.2823104  0.096154676 
+   96000   0.60509093     0.601487    1.2065779   0.03457172 
+   97000   0.68837218   0.77974186     1.468114   0.17801164 
+   98000   0.62725266   0.64137144    1.2686241   0.17449001 
+   99000   0.46861221   0.67000291    1.1386151    0.2429588 
+  100000    0.5879119    0.7140612    1.3019731  0.064634257 
+Loop time of 2.50594 on 1 procs for 100000 steps with 32 atoms
+
+Performance: 3447804.126 tau/day, 39905.140 timesteps/s
+100.0% CPU use with 1 MPI tasks x no OpenMP threads
+
+MPI task timing breakdown:
+Section |  min time  |  avg time  |  max time  |%varavg| %total
+---------------------------------------------------------------
+Pair    | 1.5639     | 1.5639     | 1.5639     |   0.0 | 62.41
+Neigh   | 0.0086911  | 0.0086911  | 0.0086911  |   0.0 |  0.35
+Comm    | 0.058926   | 0.058926   | 0.058926   |   0.0 |  2.35
+Output  | 0.0012379  | 0.0012379  | 0.0012379  |   0.0 |  0.05
+Modify  | 0.83537    | 0.83537    | 0.83537    |   0.0 | 33.34
+Other   |            | 0.03781    |            |       |  1.51
+
+Nlocal:    32 ave 32 max 32 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+Nghost:    20 ave 20 max 20 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+Neighs:    57 ave 57 max 57 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+
+Total # of neighbors = 57
+Ave neighs/atom = 1.78125
+Neighbor list builds = 2705
+Dangerous builds = 0
+Total wall time: 0:00:02
diff --git a/examples/body/log.9Jul18.body.wall2d.g++.4 b/examples/body/log.9Jul18.body.wall2d.g++.4
new file mode 100644
index 0000000000000000000000000000000000000000..7239fd4dcd4fc6a55886d0bfd11a401ab3e97f23
--- /dev/null
+++ b/examples/body/log.9Jul18.body.wall2d.g++.4
@@ -0,0 +1,223 @@
+LAMMPS (29 Jun 2018)
+# 2d rounded polygon bodies
+
+variable    r     index 4
+variable    steps index 100000
+variable    T     index 0.5
+variable    P     index 0.1
+variable    seed  index 980411
+
+units       lj
+dimension   2
+
+atom_style  body rounded/polygon 1 6
+atom_modify map array
+read_data   data.squares
+  orthogonal box = (0 0 -0.5) to (12 12 0.5)
+  2 by 2 by 1 MPI processor grid
+  reading atoms ...
+  2 atoms
+  2 bodies
+
+replicate   $r $r 1
+replicate   4 $r 1
+replicate   4 4 1
+  orthogonal box = (0 0 -0.5) to (48 48 0.5)
+  2 by 2 by 1 MPI processor grid
+  32 atoms
+  Time spent = 0.000386 secs
+
+velocity    all create $T ${seed} dist gaussian mom yes rot yes
+velocity    all create 0.5 ${seed} dist gaussian mom yes rot yes
+velocity    all create 0.5 980411 dist gaussian mom yes rot yes
+
+change_box  all boundary p f p
+
+variable cut_inner  equal 0.5
+variable k_n        equal 100
+variable k_na       equal 2
+variable c_n        equal 0.1
+variable c_t        equal 0.1
+variable mu         equal 0.1
+variable delta_ua   equal 0.5
+
+pair_style body/rounded/polygon ${c_n} ${c_t} ${mu} ${delta_ua} ${cut_inner}
+pair_style body/rounded/polygon 0.1 ${c_t} ${mu} ${delta_ua} ${cut_inner}
+pair_style body/rounded/polygon 0.1 0.1 ${mu} ${delta_ua} ${cut_inner}
+pair_style body/rounded/polygon 0.1 0.1 0.1 ${delta_ua} ${cut_inner}
+pair_style body/rounded/polygon 0.1 0.1 0.1 0.5 ${cut_inner}
+pair_style body/rounded/polygon 0.1 0.1 0.1 0.5 0.5
+pair_coeff * * ${k_n} ${k_na}
+pair_coeff * * 100 ${k_na}
+pair_coeff * * 100 2
+
+comm_modify vel yes
+
+neighbor     0.5 bin
+neigh_modify every 1 delay 0 check yes
+
+timestep     0.001
+
+#fix         1 all nve/body
+#fix         1 all nvt/body temp $T $T 1.0
+fix          1 all npt/body temp $T $T 1.0 x 0.001 $P 1.0 fixedpoint 0 0 0
+fix          1 all npt/body temp 0.5 $T 1.0 x 0.001 $P 1.0 fixedpoint 0 0 0
+fix          1 all npt/body temp 0.5 0.5 1.0 x 0.001 $P 1.0 fixedpoint 0 0 0
+fix          1 all npt/body temp 0.5 0.5 1.0 x 0.001 0.1 1.0 fixedpoint 0 0 0
+
+fix          2 all enforce2d
+fix          3 all wall/body/polygon 2000 50 50 yplane 0.0 48.0
+
+#compute      1 all body/local id 1 2 3
+#dump         1 all local 100000 dump.polygon.* index c_1[1] c_1[2] c_1[3] c_1[4]
+
+thermo_style custom step ke pe etotal press
+thermo       1000
+
+#dump	     2 all image 10000 image.*.jpg type type zoom 2.0 #             adiam 1.5 body type 0 0
+#dump_modify  2 pad 6
+
+run          ${steps}
+run          100000
+Neighbor list info ...
+  update every 1 steps, delay 0 steps, check yes
+  max neighbors/atom: 2000, page size: 100000
+  master list distance cutoff = 6.15685
+  ghost atom cutoff = 6.15685
+  binsize = 3.07843, bins = 16 16 1
+  1 neighbor lists, perpetual/occasional/extra = 1 0 0
+  (1) pair body/rounded/polygon, perpetual
+      attributes: half, newton on
+      pair build: half/bin/atomonly/newton
+      stencil: half/bin/2d/newton
+      bin: standard
+Per MPI rank memory allocation (min/avg/max) = 4.773 | 4.773 | 4.773 Mbytes
+Step KinEng PotEng TotEng Press 
+       0     0.484375         0.25     0.734375 0.0067274306 
+    1000   0.49241101 0.0031318767   0.49554289  0.017768281 
+    2000   0.56118632 0.0026068888   0.56379321  0.003410416 
+    3000   0.75565115  0.025578366   0.78122951 0.0071862988 
+    4000   0.72298647  0.093150646   0.81613712  0.003190158 
+    5000   0.51684166  0.049164868   0.56600653 0.0096960168 
+    6000   0.56627905  0.048132853    0.6144119  0.020733586 
+    7000   0.58122129  0.018223718   0.59944501 0.0038160759 
+    8000   0.64297977  0.025934821   0.66891459 0.0041091784 
+    9000   0.41748404 0.0077890042   0.42527305 0.0039270065 
+   10000   0.35738377  0.078487805   0.43587158 3.9079865e-05 
+   11000   0.41529307   0.13619284   0.55148591 -0.0067482285 
+   12000   0.43274718  0.071315527   0.50406271  0.007006369 
+   13000    0.4748324  0.069905666   0.54473807 0.0010385254 
+   14000   0.62603727  0.098905625    0.7249429 0.0048876764 
+   15000   0.44512086   0.10415235   0.54927321   0.01902062 
+   16000   0.47460177   0.18053316   0.65513493  0.045013976 
+   17000   0.52742676   0.10110706   0.62853382  0.013615471 
+   18000   0.46111734  0.096118795   0.55723613 0.0073676834 
+   19000   0.59668439   0.13652292   0.73320731  0.029403553 
+   20000   0.46840192   0.11611719   0.58451911 -0.00034412499 
+   21000   0.53550533  0.096457461    0.6319628 0.0019785732 
+   22000   0.46599715   0.13206373   0.59806087  0.031970672 
+   23000   0.49280776   0.20404726   0.69685501   0.03657433 
+   24000   0.60901688   0.18255214   0.79156902  0.044955017 
+   25000   0.47345185   0.13671357   0.61016542  0.020313539 
+   26000   0.47653832   0.12448225   0.60102057   0.01878099 
+   27000   0.50008212   0.24740634   0.74748845  0.021862639 
+   28000   0.41627204    0.2519463   0.66821834  0.054683701 
+   29000   0.55608273   0.23100212   0.78708485 -0.0043318497 
+   30000   0.53884537    0.3001584   0.83900377 -0.012838186 
+   31000   0.53036238    0.2300328   0.76039518 -0.0061688449 
+   32000   0.42666792   0.20536256   0.63203048  0.045305282 
+   33000   0.62908185    0.1652033   0.79428515 0.0072777588 
+   34000   0.47028154     0.388736   0.85901754   0.04332288 
+   35000   0.54602322    0.2775624   0.82358562   0.02898206 
+   36000   0.59860544   0.21824655   0.81685199 0.0025936194 
+   37000   0.62467827   0.11983499   0.74451326  0.050052743 
+   38000   0.72594229   0.36584781    1.0917901   0.04280621 
+   39000   0.51129656   0.23859043   0.74988699  0.050817447 
+   40000   0.53263836   0.24212889   0.77476725  0.036245922 
+   41000   0.50288088   0.36668283   0.86956371  0.018381415 
+   42000   0.46653688   0.21974887   0.68628574  0.012661062 
+   43000   0.61738785   0.32131037   0.93869821  0.012709433 
+   44000   0.56603903   0.26515554   0.83119457   0.03315102 
+   45000   0.56231638   0.32111693   0.88343331   0.06079756 
+   46000    0.7096208    0.2570131   0.96663391  0.048770468 
+   47000     0.588755    0.1880748    0.7768298  0.035962604 
+   48000   0.56296339   0.25783519   0.82079858  0.053019928 
+   49000     0.419885   0.42328618   0.84317118  0.038105269 
+   50000   0.63073351   0.41426285    1.0449964 0.0015271048 
+   51000   0.59357935     0.184222   0.77780136  0.015996218 
+   52000   0.60608471   0.36247533   0.96856003   0.10984665 
+   53000    0.5227842   0.27686739   0.79965159   0.02761699 
+   54000   0.39435923   0.34197355   0.73633278  0.061183263 
+   55000   0.46748455   0.34230903   0.80979358  0.077441382 
+   56000   0.59819827   0.29212061   0.89031889  0.043772353 
+   57000   0.61682559   0.32788566   0.94471124   0.03992069 
+   58000   0.52702478   0.24891506   0.77593984  0.058480883 
+   59000   0.66925719    0.4109031    1.0801603  0.072434423 
+   60000   0.66807714   0.39233068    1.0604078  0.082370324 
+   61000    0.5724275   0.43308567    1.0055132 0.0072945426 
+   62000   0.49433556   0.38453743   0.87887299 0.0036097443 
+   63000   0.57575143   0.54067119    1.1164226  0.073339638 
+   64000   0.68045383   0.38246533    1.0629192  0.025314593 
+   65000   0.59843527   0.42928622    1.0277215 -0.030096445 
+   66000   0.60274797   0.50186417    1.1046121  0.069797184 
+   67000   0.47450407   0.52689807    1.0014021  0.008758012 
+   68000    0.5514135   0.64113187    1.1925454  0.093863314 
+   69000   0.52008074   0.45749565   0.97757639 -0.066061381 
+   70000   0.69042662   0.50416006    1.1945867  0.014128617 
+   71000   0.63925854   0.35153425    0.9907928  -0.01134957 
+   72000   0.52088835   0.47626986   0.99715821   0.10198133 
+   73000   0.46333852    0.5515537    1.0148922 0.00060582772 
+   74000   0.53481418   0.50409531    1.0389095   0.00919451 
+   75000   0.67182749   0.50380162    1.1756291  0.043301985 
+   76000   0.70492289    0.4112122    1.1161351   0.14880484 
+   77000   0.59781817   0.50197661    1.0997948 -0.057111711 
+   78000   0.51677429    0.4348232   0.95159749 -0.0074619446 
+   79000   0.50663297   0.55000424    1.0566372 0.0052071216 
+   80000   0.59392006   0.48394003    1.0778601 -0.018990234 
+   81000   0.66323593   0.40358336    1.0668193  -0.02961345 
+   82000   0.61596979   0.49177944    1.1077492    0.1314853 
+   83000   0.63917554   0.61656584    1.2557414   0.11908351 
+   84000   0.49305291   0.46161646   0.95466937  0.033558488 
+   85000   0.52552044   0.54250555     1.068026   0.13015174 
+   86000   0.55140914   0.38924725   0.94065638  0.047412499 
+   87000   0.60952504   0.52603688    1.1355619  0.039230066 
+   88000   0.50119735     0.547539    1.0487364  0.019659933 
+   89000   0.40331401   0.50331134   0.90662535 -0.056906034 
+   90000   0.47067839   0.51306911    0.9837475   0.11918166 
+   91000   0.45564995   0.38693455    0.8425845   0.12040045 
+   92000   0.64163032   0.34232532   0.98395564 0.0057051641 
+   93000   0.70375593   0.53646186    1.2402178   0.16044241 
+   94000   0.53378112   0.51971406    1.0534952   0.11389004 
+   95000   0.47055342   0.50396004   0.97451346  0.079424215 
+   96000   0.59543473   0.40204536   0.99748009  0.096813093 
+   97000   0.64821917   0.50051728    1.1487365  0.054071312 
+   98000   0.55723937    0.4945909    1.0518303  0.047316424 
+   99000   0.56044424   0.50773312    1.0681774    0.0149959 
+  100000   0.68254229   0.32704484    1.0095871 0.0069212661 
+Loop time of 2.20043 on 4 procs for 100000 steps with 32 atoms
+
+Performance: 3926501.701 tau/day, 45445.622 timesteps/s
+100.0% CPU use with 4 MPI tasks x no OpenMP threads
+
+MPI task timing breakdown:
+Section |  min time  |  avg time  |  max time  |%varavg| %total
+---------------------------------------------------------------
+Pair    | 0.41008    | 0.41366    | 0.41719    |   0.4 | 18.80
+Neigh   | 0.0027823  | 0.0030481  | 0.0034747  |   0.5 |  0.14
+Comm    | 0.74581    | 0.7675     | 0.78684    |   2.0 | 34.88
+Output  | 0.00082111 | 0.0010884  | 0.0016899  |   1.1 |  0.05
+Modify  | 0.83828    | 0.85329    | 0.86656    |   1.4 | 38.78
+Other   |            | 0.1618     |            |       |  7.36
+
+Nlocal:    8 ave 9 max 7 min
+Histogram: 1 0 0 0 0 2 0 0 0 1
+Nghost:    12.75 ave 14 max 12 min
+Histogram: 2 0 0 0 0 1 0 0 0 1
+Neighs:    11 ave 19 max 5 min
+Histogram: 1 0 0 2 0 0 0 0 0 1
+
+Total # of neighbors = 44
+Ave neighs/atom = 1.375
+Neighbor list builds = 2663
+Dangerous builds = 0
+Total wall time: 0:00:02
diff --git a/examples/body/molecule.cube b/examples/body/molecule.cube
new file mode 100644
index 0000000000000000000000000000000000000000..2a8a7bca5023155889095b303b22d3aef201d2cc
--- /dev/null
+++ b/examples/body/molecule.cube
@@ -0,0 +1,52 @@
+# 3d polygon body: cubes, moment of inertia I = m edge^2/ 6
+
+1 atoms
+3 79 body
+
+Coords
+
+1 0 0 0
+
+Types
+
+1 1
+
+Masses
+
+1 1.0
+
+Body Integers
+
+8 12 6
+
+Body Doubles
+
+0.667 0.667 0.667 0 0 0
+1 1 1
+1 -1 1
+-1 -1 1
+-1 1 1
+1 1 -1
+1 -1 -1
+-1 -1 -1
+-1 1 -1
+0 1
+1 2
+2 3
+3 0
+4 5
+5 6
+6 7
+7 4
+0 4
+1 5
+2 6
+3 7
+0 1 2 3
+4 5 6 7
+0 1 5 4
+1 2 6 5
+2 3 7 6
+3 0 4 7
+0.5
+
diff --git a/examples/body/molecule.point3d b/examples/body/molecule.point3d
new file mode 100644
index 0000000000000000000000000000000000000000..d22bfbe6fa9d58e0df10b2cba22dd1b402185e9a
--- /dev/null
+++ b/examples/body/molecule.point3d
@@ -0,0 +1,26 @@
+# 2d polygon body: disks Izz = 1/2 m radius^2
+
+1 atoms
+3 10 body
+
+Coords
+
+1 0 0 0
+
+Types
+
+1 1
+
+Masses
+
+1 1.0
+
+Body Integers
+
+1 0 0
+
+Body Doubles
+
+1 1 1.125 0 0 0
+0 0 0 
+3.0
diff --git a/examples/body/molecule.rod3d b/examples/body/molecule.rod3d
new file mode 100644
index 0000000000000000000000000000000000000000..1c8a0a8cd3eecfa325d2a1c99a38edca26d0a789
--- /dev/null
+++ b/examples/body/molecule.rod3d
@@ -0,0 +1,27 @@
+# 2d polygon body: rods Izz = 1/12 m length^2
+
+1 atoms
+3 13 body
+
+Coords
+
+1 0 0 0
+
+Types
+
+1 1
+
+Masses
+
+1 1.0
+
+Body Integers
+
+2 1 0
+
+Body Doubles
+
+1 1 1.333 0 0 0
+-2 0 0
+2 0 0
+0.5
diff --git a/examples/body/molecule.tetra b/examples/body/molecule.tetra
new file mode 100644
index 0000000000000000000000000000000000000000..d67ec906c64868521eaae59f8284697e18ff7d72
--- /dev/null
+++ b/examples/body/molecule.tetra
@@ -0,0 +1,39 @@
+# 3d polygon body: regular tetrahedra, moment of inertia I = m edge^2/ 20
+
+1 atoms
+3 47 body
+
+Coords
+
+1 0 0 0
+
+Types
+
+1 1
+
+Masses
+
+1 1.0
+
+Body Integers
+
+4 6 4
+
+Body Doubles
+
+0.4 0.4 0.4 0 0 0
+1 1 1
+1 -1 -1
+-1 1 -1
+-1 -1 1
+0 1
+0 2
+0 3
+1 2
+2 3
+3 1
+0 1 2 -1
+0 1 3 -1
+0 2 3 -1
+1 2 3 -1
+0.5
diff --git a/src/BODY/body_rounded_polygon.cpp b/src/BODY/body_rounded_polygon.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1e232f0f3f59fbbdab6bf470920bcf2a70ff2036
--- /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..6a9b97ae2348ec344b75656afbb3d5261282e5b5
--- /dev/null
+++ b/src/BODY/body_rounded_polyhedron.cpp
@@ -0,0 +1,526 @@
+/* ----------------------------------------------------------------------
+   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;
+
+  // 3 integers: 1 for no. of vertices, 1 for no. of edges, 1 for no. of faces
+  // 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 = 3 + 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*ned+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*ned+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..5ec5a7cca8eb30309294a14b13f8e2d10e56bd26
--- /dev/null
+++ b/src/BODY/fix_wall_body_polygon.cpp
@@ -0,0 +1,832 @@
+/* ----------------------------------------------------------------------
+   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 <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..b71dcb06832ba312fb6a3853d185d1c37d23d314
--- /dev/null
+++ b/src/BODY/fix_wall_body_polygon.h
@@ -0,0 +1,131 @@
+/* -*- 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;          // normal repulsion strength
+  double c_n;         // normal damping coefficient
+  double c_t;         // tangential damping coefficient
+  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..17e9f0b8b5e05441dff0c93f333dde24793fd3e8
--- /dev/null
+++ b/src/BODY/fix_wall_body_polyhedron.cpp
@@ -0,0 +1,945 @@
+/* ----------------------------------------------------------------------
+   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 <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.cpp b/src/BODY/pair_body_nparticle.cpp
similarity index 95%
rename from src/BODY/pair_body.cpp
rename to src/BODY/pair_body_nparticle.cpp
index 8c12c0cf36aa0384c50e3745b45c969c8aa29cc2..80b45beb4e9ecf174503ea7f46c19c0e8a7f46a1 100644
--- a/src/BODY/pair_body.cpp
+++ b/src/BODY/pair_body_nparticle.cpp
@@ -15,7 +15,7 @@
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
-#include "pair_body.h"
+#include "pair_body_nparticle.h"
 #include "math_extra.h"
 #include "atom.h"
 #include "atom_vec_body.h"
@@ -32,7 +32,7 @@ using namespace LAMMPS_NS;
 
 /* ---------------------------------------------------------------------- */
 
-PairBody::PairBody(LAMMPS *lmp) : Pair(lmp)
+PairBodyNparticle::PairBodyNparticle(LAMMPS *lmp) : Pair(lmp)
 {
   dmax = nmax = 0;
   discrete = NULL;
@@ -44,7 +44,7 @@ PairBody::PairBody(LAMMPS *lmp) : Pair(lmp)
 
 /* ---------------------------------------------------------------------- */
 
-PairBody::~PairBody()
+PairBodyNparticle::~PairBodyNparticle()
 {
   memory->destroy(discrete);
   memory->destroy(dnum);
@@ -66,7 +66,7 @@ PairBody::~PairBody()
 
 /* ---------------------------------------------------------------------- */
 
-void PairBody::compute(int eflag, int vflag)
+void PairBodyNparticle::compute(int eflag, int vflag)
 {
   int i,j,ii,jj,inum,jnum,itype,jtype;
   int ni,nj,npi,npj,ifirst,jfirst;
@@ -336,7 +336,7 @@ void PairBody::compute(int eflag, int vflag)
    allocate all arrays
 ------------------------------------------------------------------------- */
 
-void PairBody::allocate()
+void PairBodyNparticle::allocate()
 {
   allocated = 1;
   int n = atom->ntypes;
@@ -361,7 +361,7 @@ void PairBody::allocate()
    global settings
 ------------------------------------------------------------------------- */
 
-void PairBody::settings(int narg, char **arg)
+void PairBodyNparticle::settings(int narg, char **arg)
 {
   if (narg != 1) error->all(FLERR,"Illegal pair_style command");
 
@@ -381,7 +381,7 @@ void PairBody::settings(int narg, char **arg)
    set coeffs for one or more type pairs
 ------------------------------------------------------------------------- */
 
-void PairBody::coeff(int narg, char **arg)
+void PairBodyNparticle::coeff(int narg, char **arg)
 {
   if (narg < 4 || narg > 5)
     error->all(FLERR,"Incorrect args for pair coefficients");
@@ -415,12 +415,12 @@ void PairBody::coeff(int narg, char **arg)
    init specific to this pair style
 ------------------------------------------------------------------------- */
 
-void PairBody::init_style()
+void PairBodyNparticle::init_style()
 {
   avec = (AtomVecBody *) atom->style_match("body");
-  if (!avec) error->all(FLERR,"Pair body requires atom style body");
+  if (!avec) error->all(FLERR,"Pair body/nparticle requires atom style body");
   if (strcmp(avec->bptr->style,"nparticle") != 0)
-    error->all(FLERR,"Pair body requires body style nparticle");
+    error->all(FLERR,"Pair body/nparticle requires body style nparticle");
   bptr = (BodyNparticle *) avec->bptr;
 
   neighbor->request(this,instance_me);
@@ -430,7 +430,7 @@ void PairBody::init_style()
    init for one type pair i,j and corresponding j,i
 ------------------------------------------------------------------------- */
 
-double PairBody::init_one(int i, int j)
+double PairBodyNparticle::init_one(int i, int j)
 {
   if (setflag[i][j] == 0) {
     epsilon[i][j] = mix_energy(epsilon[i][i],epsilon[j][j],
@@ -459,7 +459,7 @@ double PairBody::init_one(int i, int j)
    store sub-particle space-frame displacements from COM in discrete list
 ------------------------------------------------------------------------- */
 
-void PairBody::body2space(int i)
+void PairBodyNparticle::body2space(int i)
 {
   int ibonus = atom->body[i];
   AtomVecBody::Bonus *bonus = &avec->bonus[ibonus];
diff --git a/src/BODY/pair_body.h b/src/BODY/pair_body_nparticle.h
similarity index 90%
rename from src/BODY/pair_body.h
rename to src/BODY/pair_body_nparticle.h
index 94fbdf34dffd12000165d4f0e936bc64dcc5ae00..9c7d832b64bcacf7fa61baaa0b3ca917f9cbd4ce 100644
--- a/src/BODY/pair_body.h
+++ b/src/BODY/pair_body_nparticle.h
@@ -13,21 +13,21 @@
 
 #ifdef PAIR_CLASS
 
-PairStyle(body,PairBody)
+PairStyle(body/nparticle,PairBodyNparticle)
 
 #else
 
-#ifndef LMP_PAIR_BODY_H
-#define LMP_PAIR_BODY_H
+#ifndef LMP_PAIR_BODY_NPARTICLE_H
+#define LMP_PAIR_BODY_NPARTICLE_H
 
 #include "pair.h"
 
 namespace LAMMPS_NS {
 
-class PairBody : public Pair {
+class PairBodyNparticle : public Pair {
  public:
-  PairBody(class LAMMPS *);
-  ~PairBody();
+  PairBodyNparticle(class LAMMPS *);
+  ~PairBodyNparticle();
   void compute(int, int);
   void settings(int, char **);
   void coeff(int, char **);
diff --git a/src/BODY/pair_body_rounded_polygon.cpp b/src/BODY/pair_body_rounded_polygon.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..14ef70f476061f1240dad92b7e694e6aa1f5957c
--- /dev/null
+++ b/src/BODY/pair_body_rounded_polygon.cpp
@@ -0,0 +1,1380 @@
+/* ----------------------------------------------------------------------
+   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 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
+
+//#define _CONVEX_POLYGON
+//#define _POLYGON_DEBUG
+
+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]);
+
+  if (delta_ua < 0) delta_ua = 1;
+}
+
+/* ----------------------------------------------------------------------
+   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 (force->newton_pair == 0)
+    error->all(FLERR,"Pair style body/rounded/polygon requires "
+               "newton pair on");
+
+  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
+        }
+
+        #ifdef _CONVEX_POLYGON
+        // done with the edges from body j,
+        // given that vertex ni interacts with only one vertex 
+        //   from one edge of body j
+        break;
+        #endif
+
+      } 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
+
+        #ifdef _CONVEX_POLYGON
+        // done with the edges from body j,
+        // given that vertex ni interacts with only one edge from body j
+        break;
+        #endif
+      } // 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
+
+    #ifdef _CONVEX_POLYGON
+    int m = opposite_sides(xi1, xi2, x0, xmi);
+    #else
+    int m = 1;
+    #endif
+
+    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..aabe86270cd621a56836370130532cf42c93be59
--- /dev/null
+++ b/src/BODY/pair_body_rounded_polygon.h
@@ -0,0 +1,137 @@
+/* -*- 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);
+
+  // sphere-sphere interaction
+  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);
+  // vertex-edge interaction
+  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);
+  // compute distance between a point and an edge from another body
+  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);
+  // compute contact forces if contact points are detected
+  void contact_forces(Contact& contact, double j_a,
+                      double** x, double** v, double** angmom, double** f,
+                      double** torque, double &evdwl, double* facc);
+
+  // compute the separation between two contacts
+  double contact_separation(const Contact& c1, const Contact& c2);
+
+  // accumulate torque to a body given a force at a given point
+  void sum_torque(double* xm, double *x, double fx,
+                  double fy, double fz, double* torque);
+  // helper functions
+  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..2ff209d609bf1d5f31f82ddf0c6b3063949b5e2e
--- /dev/null
+++ b/src/BODY/pair_body_rounded_polyhedron.cpp
@@ -0,0 +1,2386 @@
+/* ----------------------------------------------------------------------
+   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 32  // for 3D models (including duplicated counts)
+
+//#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;
+
+  k_n = NULL;
+  k_na = NULL;
+}
+
+/* ---------------------------------------------------------------------- */
+
+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;
+  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];
+
+      // 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, itype, jtype, delx, dely, delz,
+                              rsq, 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, itype, jtype, x, v, f, torque,
+                            angmom, evflag);
+        sphere_against_edge(i, j, itype, jtype, x, v, f, torque,
+                            angmom, evflag);
+        continue;
+      } else if (npi == 1) {
+        sphere_against_face(j, i, jtype, itype, x, v, f, torque,
+                            angmom, evflag);
+        sphere_against_edge(j, i, jtype, itype, 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, itype, jtype, 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, jtype, itype, 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, itype, jtype, x, contact_list,
+                                   num_contacts, evdwl, facc);
+
+      // estimate the contact area
+      // also consider point contacts and line contacts
+
+      if (num_contacts > 0) {
+        rescale_cohesive_forces(x, f, torque, contact_list, num_contacts,
+                                itype, jtype, 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]);
+
+  if (A_ua < 0) A_ua = 1;
+}
+
+/* ----------------------------------------------------------------------
+   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 (force->newton_pair == 0)
+    error->all(FLERR,"Pair style body/rounded/polyhedron requires "
+               "newton pair on");
+
+  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,
+  int itype, int jtype, double delx, double dely, double delz, double rsq,
+  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;
+
+  energy = 0;
+  kernel_force(R, itype, jtype, energy, fpair);
+
+  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 edges of a polyhedron (ibody) and a sphere (jbody)
+---------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::sphere_against_edge(int ibody, int jbody,
+  int itype, int jtype, 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;
+
+    energy = 0;
+    kernel_force(R, itype, jtype, energy, fpair);
+
+    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);
+  }
+}
+
+/* ----------------------------------------------------------------------
+   Interaction bt the faces of a polyhedron (ibody) and a sphere (jbody)
+---------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::sphere_against_face(int ibody, int jbody,
+ int itype, int jtype, 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;
+
+    energy = 0;
+    kernel_force(R, itype, jtype, energy, fpair);
+
+    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);
+  }
+}
+
+/* ----------------------------------------------------------------------
+   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,
+  int itype, int jtype, 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,
+                                          itype, jtype, 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 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,
+  int itype, int jtype, 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,
+                                          itype, jtype, cut_inner,
+                                          contact_list, num_contacts,
+                                          energy, facc);
+    } 
+
+  } // end for looping through the edges of body i
+
+  evdwl += energy;
+
+  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,
+  int itype, int jtype, 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;
+
+  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,
+                          jtype, itype, 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;
+      contact_list[num_contacts].unique = 1;
+      num_contacts++;
+    }
+  } else {
+
+  }
+
+  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,
+  int itype, int jtype, 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;
+
+  // 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,
+                                jtype, itype, 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;
+            contact_list[num_contacts].unique = 1;
+            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,
+                                jtype, itype, 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;
+            contact_list[num_contacts].unique = 1;
+            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) {
+    // need to do something here to resolve overlap!!
+    // p is the intersection between the edge and the face
+    int jflag = 1;
+    if (d1 < d2)
+      pair_force_and_torque(jbody, ibody, xpj1, hi1, d1, contact_dist,
+                            jtype, itype, x, v, f, torque, angmom,
+                            jflag, energy, facc);
+    else
+      pair_force_and_torque(jbody, ibody, xpj2, hi2, d2, contact_dist,
+                            jtype, itype, x, v, f, torque, angmom,
+                            jflag, energy, facc);
+  }
+
+  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,
+                 int itype, int jtype, 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;
+
+  kernel_force(R, itype, jtype, energy, fpair);
+
+  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]);
+    }
+  }
+}
+
+/* ----------------------------------------------------------------------
+  Kernel force is model-dependent and can be derived for other styles
+    here is the harmonic potential (linear piece-wise forces) in Wang et al.
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::kernel_force(double R, int itype, int jtype,
+  double& energy, double& fpair)
+{
+  double kn = k_n[itype][jtype];
+  double kna = k_na[itype][jtype];
+  double shift = kna * cut_inner;
+  double e = 0;
+  if (R <= 0) {           // deformation occurs
+    fpair = -kn * R - shift;
+    e = (0.5 * kn * R + shift) * R;
+  } else if (R <= cut_inner) {   // not deforming but cohesive ranges overlap
+    fpair = kna * R - shift;
+    e = (-0.5 * kna * R + shift) * R;
+  } else fpair = 0.0;
+  energy += e;
+}
+
+/* ----------------------------------------------------------------------
+  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;
+
+  // these are contact forces (F_n, F_t and F_ne) only
+  // 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,
+     int itype, int jtype, double* facc)
+{
+  int m,ibody,jbody;
+  double delx,dely,delz,fx,fy,fz,R,fpair,r,contact_area;
+
+  int num_unique_contacts = 0;
+  if (num_contacts == 1) {
+    num_unique_contacts = 1;
+    contact_area = 0;
+  } else if (num_contacts == 2) {
+    num_unique_contacts = 2;
+    contact_area = num_contacts * A_ua;
+  } else {
+    find_unique_contacts(contact_list, num_contacts);
+
+    double xc[3],dx,dy,dz;
+    xc[0] = xc[1] = xc[2] = 0;
+    num_unique_contacts = 0;
+    for (int m = 0; m < num_contacts; m++) {
+      if (contact_list[m].unique == 0) continue;
+      xc[0] += contact_list[m].xi[0];
+      xc[1] += contact_list[m].xi[1];
+      xc[2] += contact_list[m].xi[2];
+      num_unique_contacts++;
+    }
+
+    xc[0] /= (double)num_unique_contacts;
+    xc[1] /= (double)num_unique_contacts;
+    xc[2] /= (double)num_unique_contacts;
+    
+    contact_area = 0.0;
+    for (int m = 0; m < num_contacts; m++) {
+      if (contact_list[m].unique == 0) continue;
+      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_unique_contacts);
+  }
+
+  double j_a = contact_area / (num_unique_contacts * A_ua);
+  if (j_a < 1.0) j_a = 1.0;
+  for (m = 0; m < num_contacts; m++) {
+    if (contact_list[m].unique == 0) continue;
+
+    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;
+
+    double energy = 0;
+    kernel_force(R, itype, jtype, energy, fpair);
+
+    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];
+}
+
+/* ----------------------------------------------------------------------
+  Determine the length of the contact segment, i.e. the separation between
+  2 contacts, should be extended for 3D models.
+------------------------------------------------------------------------- */
+
+double PairBodyRoundedPolyhedron::contact_separation(const Contact& c1,
+                                                     const Contact& c2)
+{
+  double x1 = 0.5*(c1.xi[0] + c1.xj[0]);
+  double y1 = 0.5*(c1.xi[1] + c1.xj[1]);
+  double z1 = 0.5*(c1.xi[2] + c1.xj[2]);
+  double x2 = 0.5*(c2.xi[0] + c2.xj[0]);
+  double y2 = 0.5*(c2.xi[1] + c2.xj[1]);
+  double z2 = 0.5*(c2.xi[2] + c2.xj[2]);
+  double rsq = (x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1) + (z2 - z1)*(z2 - z1);
+  return rsq;
+}
+
+/* ----------------------------------------------------------------------
+   find the number of unique contacts
+------------------------------------------------------------------------- */
+
+void PairBodyRoundedPolyhedron::find_unique_contacts(Contact* contact_list, 
+                                                     int& num_contacts)
+{
+  int n = num_contacts;
+  for (int i = 0; i < n - 1; i++) {
+
+    for (int j = i + 1; j < n; j++) {
+      if (contact_list[i].unique == 0) continue;
+      double d = contact_separation(contact_list[i], contact_list[j]);
+      if (d < EPSILON) contact_list[j].unique = 0;
+    }
+  }
+}
+
+/* ---------------------------------------------------------------------- */
+
+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..71c04ff9665aa0b24b7e7f4069cc4f4f4766bf4d
--- /dev/null
+++ b/src/BODY/pair_body_rounded_polyhedron.h
@@ -0,0 +1,200 @@
+/* -*- 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);
+
+  virtual void kernel_force(double R, int itype, int jtype,
+    double& energy, double& fpair);
+
+  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
+    int unique;
+  };
+
+ 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);
+
+  // sphere-sphere interaction
+  void sphere_against_sphere(int ibody, int jbody, int itype, int jtype,
+                             double delx, double dely, double delz, double rsq,
+                             double** v, double** f, int evflag);
+  // sphere-edge interaction
+  void sphere_against_edge(int ibody, int jbody, int itype, int jtype,
+                           double** x, double** v, double** f, double** torque,
+                           double** angmom, int evflag);
+  // sphere-face interaction
+  void sphere_against_face(int ibody, int jbody, int itype, int jtype,
+                           double** x, double** v, double** f, double** torque,
+                           double** angmom, int evflag);
+  // edge-edge interactions
+  int edge_against_edge(int ibody, int jbody, int itype, int jtype,
+                        double** x,Contact* contact_list, int &num_contacts,
+                        double &evdwl, double* facc);
+  // edge-face interactions
+  int edge_against_face(int ibody, int jbody, int itype, int jtype,
+                        double** x, Contact* contact_list, int &num_contacts,
+                        double &evdwl, double* facc);
+
+  // a face vs. a single edge
+  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,
+                               int itype, int jtype, double cut_inner,
+                               Contact* contact_list, int &num_contacts,
+                               double& energy, double* facc);
+  // an edge vs. an edge from another body
+  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,
+                               int itype, int jtype, double cut_inner,
+                               Contact* contact_list, int &num_contacts,
+                               double& energy, double* facc);
+
+  // compute contact forces if contact points are detected
+  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);
+
+  // compute force and torque between two bodies given a pair of interacting points
+  void pair_force_and_torque(int ibody, int jbody, double* pi, double* pj,
+                             double r, double contact_dist, int itype, int jtype,
+                             double** x, double** v, double** f, double** torque,
+                             double** angmom, int jflag, double& energy, double* facc);
+
+  // rescale the cohesive forces if a contact area is detected
+  void rescale_cohesive_forces(double** x, double** f, double** torque,
+                               Contact* contact_list, int &num_contacts,
+                               int itype, int jtype, double* facc);
+
+  // compute the separation between two contacts
+  double contact_separation(const Contact& c1, const Contact& c2);
+
+  // detect the unique contact points (as there may be double counts)
+  void find_unique_contacts(Contact* contact_list, int& num_contacts);
+
+  // accumulate torque to a body given a force at a given point
+  void sum_torque(double* xm, double *x, double fx, double fy, double fz, double* torque);
+
+  // find the intersection point (if any) between an edge and a face
+  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);
+  // helper functions
+  int opposite_sides(double* n, double* x0, double* a, double* 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.
+
+*/