diff --git a/doc/Manual.html b/doc/Manual.html
index f23b5a0cf..43e96bec9 100644
--- a/doc/Manual.html
+++ b/doc/Manual.html
@@ -1,441 +1,441 @@
 <HTML>
 <HEAD>
 <TITLE>LAMMPS-ICMS Users Manual</TITLE>
-<META NAME="docnumber" CONTENT="11 Jul 2014 version">
+<META NAME="docnumber" CONTENT="22 Jul 2014 version">
 <META NAME="author" CONTENT="http://lammps.sandia.gov - Sandia National Laboratories">
 <META NAME="copyright" CONTENT="Copyright (2003) Sandia Corporation.  This software and manual is distributed under the GNU General Public License.">
 </HEAD>
 
 <BODY>
 
 <CENTER><A HREF = "http://lammps.sandia.gov">LAMMPS WWW Site</A> - <A HREF = "Manual.html">LAMMPS Documentation</A> - <A HREF = "Section_commands.html#comm">LAMMPS Commands</A> 
 </CENTER>
 
 
 
 
 
 
 <HR>
 
 <H1></H1>
 
 <CENTER><H3>LAMMPS-ICMS Documentation 
 </H3></CENTER>
-<CENTER><H4>11 Jul 2014 version 
+<CENTER><H4>22 Jul 2014 version 
 </H4></CENTER>
 <H4>Version info: 
 </H4>
 <P>The LAMMPS "version" is the date when it was released, such as 1 May
 2010. LAMMPS is updated continuously.  Whenever we fix a bug or add a
 feature, we release it immediately, and post a notice on <A HREF = "http://lammps.sandia.gov/bug.html">this page of
 the WWW site</A>.  Each dated copy of LAMMPS contains all the
 features and bug-fixes up to and including that version date. The
 version date is printed to the screen and logfile every time you run
 LAMMPS. It is also in the file src/version.h and in the LAMMPS
 directory name created when you unpack a tarball, and at the top of
 the first page of the manual (this page).
 </P>
 <P>LAMMPS-ICMS is an experimental variant of LAMMPS with additional
 features made available for testing before they will be submitted
 for inclusion into the official LAMMPS tree. The source code is 
 based on the official LAMMPS svn repository mirror at the Institute
 for Computational Molecular Science at Temple University and generally
 kept up-to-date as much as possible. Sometimes, e.g. when additional
 development work is needed to adapt the upstream changes into
 LAMMPS-ICMS it can take longer until synchronization; and occasionally,
 e.g. in case of the rewrite of the multi-threading support, the
 development will be halted except for important bugfixes until
 all features of LAMMPS-ICMS fully compatible with the upstream
 version or replaced by alternate implementations.
 </P>
 <UL><LI>If you browse the HTML doc pages on the LAMMPS WWW site, they always
 describe the most current version of upstream LAMMPS, but may be
 missing some new features in LAMMPS-ICMS. 
 
 <LI>If you browse the HTML doc pages included in your tarball, they
 describe the version you have, however, not all new features in
 LAMMPS-ICMS are documented immediately. 
 
 <LI>The <A HREF = "Manual.pdf">PDF file</A> on the WWW site or in the tarball is updated
 about once per month.  This is because it is large, and we don't want
 it to be part of every patch. 
 
 <LI>There is also a <A HREF = "Developer.pdf">Developer.pdf</A> file in the doc
 directory, which describes the internal structure and algorithms of
 LAMMPS.  
 </UL>
 <P>LAMMPS stands for Large-scale Atomic/Molecular Massively Parallel
 Simulator.
 </P>
 <P>LAMMPS is a classical molecular dynamics simulation code designed to
 run efficiently on parallel computers.  It was developed at Sandia
 National Laboratories, a US Department of Energy facility, with
 funding from the DOE.  It is an open-source code, distributed freely
 under the terms of the GNU Public License (GPL).
 </P>
 <P>The primary developers of LAMMPS are <A HREF = "http://www.sandia.gov/~sjplimp">Steve Plimpton</A>, Aidan
 Thompson, and Paul Crozier who can be contacted at
 sjplimp,athomps,pscrozi at sandia.gov.  The <A HREF = "http://lammps.sandia.gov">LAMMPS WWW Site</A> at
 http://lammps.sandia.gov has more information about the code and its
 uses.
 </P>
 
 
 
 
 <HR>
 
 <P>The LAMMPS documentation is organized into the following sections.  If
 you find errors or omissions in this manual or have suggestions for
 useful information to add, please send an email to the developers so
 we can improve the LAMMPS documentation.
 </P>
 <P>Once you are familiar with LAMMPS, you may want to bookmark <A HREF = "Section_commands.html#comm">this
 page</A> at Section_commands.html#comm since
 it gives quick access to documentation for all LAMMPS commands.
 </P>
 <P><A HREF = "Manual.pdf">PDF file</A> of the entire manual, generated by
 <A HREF = "http://www.easysw.com/htmldoc">htmldoc</A>
 </P>
 <OL><LI><A HREF = "Section_intro.html">Introduction</A> 
 
 <UL>  1.1 <A HREF = "Section_intro.html#intro_1">What is LAMMPS</A> 
 <BR>
   1.2 <A HREF = "Section_intro.html#intro_2">LAMMPS features</A> 
 <BR>
   1.3 <A HREF = "Section_intro.html#intro_3">LAMMPS non-features</A> 
 <BR>
   1.4 <A HREF = "Section_intro.html#intro_4">Open source distribution</A> 
 <BR>
   1.5 <A HREF = "Section_intro.html#intro_5">Acknowledgments and citations</A> 
 <BR></UL>
 <LI><A HREF = "Section_start.html">Getting started</A> 
 
 <UL>  2.1 <A HREF = "Section_start.html#start_1">What's in the LAMMPS distribution</A> 
 <BR>
   2.2 <A HREF = "Section_start.html#start_2">Making LAMMPS</A> 
 <BR>
   2.3 <A HREF = "Section_start.html#start_3">Making LAMMPS with optional packages</A> 
 <BR>
   2.4 <A HREF = "Section_start.html#start_4">Building LAMMPS via the Make.py script</A> 
 <BR>
   2.5 <A HREF = "Section_start.html#start_5">Building LAMMPS as a library</A> 
 <BR>
   2.6 <A HREF = "Section_start.html#start_6">Running LAMMPS</A> 
 <BR>
   2.7 <A HREF = "Section_start.html#start_7">Command-line options</A> 
 <BR>
   2.8 <A HREF = "Section_start.html#start_8">Screen output</A> 
 <BR>
   2.9 <A HREF = "Section_start.html#start_9">Tips for users of previous versions</A> 
 <BR></UL>
 <LI><A HREF = "Section_commands.html">Commands</A> 
 
 <UL>  3.1 <A HREF = "Section_commands.html#cmd_1">LAMMPS input script</A> 
 <BR>
   3.2 <A HREF = "Section_commands.html#cmd_2">Parsing rules</A> 
 <BR>
   3.3 <A HREF = "Section_commands.html#cmd_3">Input script structure</A> 
 <BR>
   3.4 <A HREF = "Section_commands.html#cmd_4">Commands listed by category</A> 
 <BR>
   3.5 <A HREF = "Section_commands.html#cmd_5">Commands listed alphabetically</A> 
 <BR></UL>
 <LI><A HREF = "Section_packages.html">Packages</A> 
 
 <UL>  4.1 <A HREF = "Section_packages.html#pkg_1">Standard packages</A> 
 <BR>
   4.2 <A HREF = "Section_packages.html#pkg_2">User packages</A> 
 <BR></UL>
 <LI><A HREF = "Section_accelerate.html">Accelerating LAMMPS performance</A> 
 
 <UL>  5.1 <A HREF = "Section_accelerate.html#acc_1">Measuring performance</A> 
 <BR>
   5.2 <A HREF = "Section_accelerate.html#acc_2">General strategies</A> 
 <BR>
   5.3 <A HREF = "Section_accelerate.html#acc_3">Packages with optimized styles</A> 
 <BR>
   5.4 <A HREF = "Section_accelerate.html#acc_4">OPT package</A> 
 <BR>
   5.5 <A HREF = "Section_accelerate.html#acc_5">USER-OMP package</A> 
 <BR>
   5.6 <A HREF = "Section_accelerate.html#acc_6">GPU package</A> 
 <BR>
   5.7 <A HREF = "Section_accelerate.html#acc_7">USER-CUDA package</A> 
 <BR>
   5.8 <A HREF = "Section_accelerate.html#acc_8">KOKKOS package</A> 
 <BR>
   5.9 <A HREF = "Section_accelerate.html#acc_9">Comparison of GPU and USER-CUDA packages</A> 
 <BR></UL>
 <LI><A HREF = "Section_howto.html">How-to discussions</A> 
 
 <UL>  6.1 <A HREF = "Section_howto.html#howto_1">Restarting a simulation</A> 
 <BR>
   6.2 <A HREF = "Section_howto.html#howto_2">2d simulations</A> 
 <BR>
   6.3 <A HREF = "Section_howto.html#howto_3">CHARMM and AMBER force fields</A> 
 <BR>
   6.4 <A HREF = "Section_howto.html#howto_4">Running multiple simulations from one input script</A> 
 <BR>
   6.5 <A HREF = "Section_howto.html#howto_5">Multi-replica simulations</A> 
 <BR>
   6.6 <A HREF = "Section_howto.html#howto_6">Granular models</A> 
 <BR>
   6.7 <A HREF = "Section_howto.html#howto_7">TIP3P water model</A> 
 <BR>
   6.8 <A HREF = "Section_howto.html#howto_8">TIP4P water model</A> 
 <BR>
   6.9 <A HREF = "Section_howto.html#howto_9">SPC water model</A> 
 <BR>
   6.10 <A HREF = "Section_howto.html#howto_10">Coupling LAMMPS to other codes</A> 
 <BR>
   6.11 <A HREF = "Section_howto.html#howto_11">Visualizing LAMMPS snapshots</A> 
 <BR>
   6.12 <A HREF = "Section_howto.html#howto_12">Triclinic (non-orthogonal) simulation boxes</A> 
 <BR>
   6.13 <A HREF = "Section_howto.html#howto_13">NEMD simulations</A> 
 <BR>
   6.14 <A HREF = "Section_howto.html#howto_14">Finite-size spherical and aspherical particles</A> 
 <BR>
   6.15 <A HREF = "Section_howto.html#howto_15">Output from LAMMPS (thermo, dumps, computes, fixes, variables)</A> 
 <BR>
   6.16 <A HREF = "Section_howto.html#howto_16">Thermostatting, barostatting, and compute temperature</A> 
 <BR>
   6.17 <A HREF = "Section_howto.html#howto_17">Walls</A> 
 <BR>
   6.18 <A HREF = "Section_howto.html#howto_18">Elastic constants</A> 
 <BR>
   6.19 <A HREF = "Section_howto.html#howto_19">Library interface to LAMMPS</A> 
 <BR>
   6.20 <A HREF = "Section_howto.html#howto_20">Calculating thermal conductivity</A> 
 <BR>
   6.21 <A HREF = "Section_howto.html#howto_21">Calculating viscosity</A> 
 <BR>
   6.22 <A HREF = "howto_22">Calculating a diffusion coefficient</A> 
 <BR></UL>
 <LI><A HREF = "Section_example.html">Example problems</A> 
 
 <LI><A HREF = "Section_perf.html">Performance & scalability</A> 
 
 <LI><A HREF = "Section_tools.html">Additional tools</A> 
 
 <LI><A HREF = "Section_modify.html">Modifying & extending LAMMPS</A> 
 
 <UL>  10.1 <A HREF = "Section_modify.html#mod_1">Atom styles</A> 
 <BR>
   10.2 <A HREF = "Section_modify.html#mod_2">Bond, angle, dihedral, improper potentials</A> 
 <BR>
   10.3 <A HREF = "Section_modify.html#mod_3">Compute styles</A> 
 <BR>
   10.4 <A HREF = "Section_modify.html#mod_4">Dump styles</A> 
 <BR>
   10.5 <A HREF = "Section_modify.html#mod_5">Dump custom output options</A> 
 <BR>
   10.6 <A HREF = "Section_modify.html#mod_6">Fix styles</A> 
 <BR>
   10.7 <A HREF = "Section_modify.html#mod_7">Input script commands</A> 
 <BR>
   10.8 <A HREF = "Section_modify.html#mod_8">Kspace computations</A> 
 <BR>
   10.9 <A HREF = "Section_modify.html#mod_9">Minimization styles</A> 
 <BR>
   10.10 <A HREF = "Section_modify.html#mod_10">Pairwise potentials</A> 
 <BR>
   10.11 <A HREF = "Section_modify.html#mod_11">Region styles</A> 
 <BR>
   10.12 <A HREF = "Section_modify.html#mod_12">Body styles</A> 
 <BR>
   10.13 <A HREF = "Section_modify.html#mod_13">Thermodynamic output options</A> 
 <BR>
   10.14 <A HREF = "Section_modify.html#mod_14">Variable options</A> 
 <BR>
   10.15 <A HREF = "Section_modify.html#mod_15">Submitting new features for inclusion in LAMMPS</A> 
 <BR></UL>
 <LI><A HREF = "Section_python.html">Python interface</A> 
 
 <UL>  11.1 <A HREF = "Section_python.html#py_1">Building LAMMPS as a shared library</A> 
 <BR>
   11.2 <A HREF = "Section_python.html#py_2">Installing the Python wrapper into Python</A> 
 <BR>
   11.3 <A HREF = "Section_python.html#py_3">Extending Python with MPI to run in parallel</A> 
 <BR>
   11.4 <A HREF = "Section_python.html#py_4">Testing the Python-LAMMPS interface</A> 
 <BR>
   11.5 <A HREF = "Section_python.html#py_5">Using LAMMPS from Python</A> 
 <BR>
   11.6 <A HREF = "Section_python.html#py_6">Example Python scripts that use LAMMPS</A> 
 <BR></UL>
 <LI><A HREF = "Section_errors.html">Errors</A> 
 
 <UL>  12.1 <A HREF = "Section_errors.html#err_1">Common problems</A> 
 <BR>
   12.2 <A HREF = "Section_errors.html#err_2">Reporting bugs</A> 
 <BR>
   12.3 <A HREF = "Section_errors.html#err_3">Error & warning messages</A> 
 <BR></UL>
 <LI><A HREF = "Section_history.html">Future and history</A> 
 
 <UL>  13.1 <A HREF = "Section_history.html#hist_1">Coming attractions</A> 
 <BR>
   13.2 <A HREF = "Section_history.html#hist_2">Past versions</A> 
 <BR></UL>
 
 </OL>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 </BODY>
 
 </HTML>
diff --git a/doc/Manual.txt b/doc/Manual.txt
index 4dbbe43c9..28755def0 100644
--- a/doc/Manual.txt
+++ b/doc/Manual.txt
@@ -1,276 +1,276 @@
 <HEAD>
 <TITLE>LAMMPS-ICMS Users Manual</TITLE>
-<META NAME="docnumber" CONTENT="11 Jul 2014 version">
+<META NAME="docnumber" CONTENT="22 Jul 2014 version">
 <META NAME="author" CONTENT="http://lammps.sandia.gov - Sandia National Laboratories">
 <META NAME="copyright" CONTENT="Copyright (2003) Sandia Corporation.  This software and manual is distributed under the GNU General Public License.">
 </HEAD>
 
 <BODY>
 
 "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
 
 <H1></H1>
 
 LAMMPS-ICMS Documentation :c,h3
-11 Jul 2014 version :c,h4
+22 Jul 2014 version :c,h4
 
 Version info: :h4
 
 The LAMMPS "version" is the date when it was released, such as 1 May
 2010. LAMMPS is updated continuously.  Whenever we fix a bug or add a
 feature, we release it immediately, and post a notice on "this page of
 the WWW site"_bug.  Each dated copy of LAMMPS contains all the
 features and bug-fixes up to and including that version date. The
 version date is printed to the screen and logfile every time you run
 LAMMPS. It is also in the file src/version.h and in the LAMMPS
 directory name created when you unpack a tarball, and at the top of
 the first page of the manual (this page).
 
 LAMMPS-ICMS is an experimental variant of LAMMPS with additional
 features made available for testing before they will be submitted
 for inclusion into the official LAMMPS tree. The source code is 
 based on the official LAMMPS svn repository mirror at the Institute
 for Computational Molecular Science at Temple University and generally
 kept up-to-date as much as possible. Sometimes, e.g. when additional
 development work is needed to adapt the upstream changes into
 LAMMPS-ICMS it can take longer until synchronization; and occasionally,
 e.g. in case of the rewrite of the multi-threading support, the
 development will be halted except for important bugfixes until
 all features of LAMMPS-ICMS fully compatible with the upstream
 version or replaced by alternate implementations.
 
 If you browse the HTML doc pages on the LAMMPS WWW site, they always
 describe the most current version of upstream LAMMPS, but may be
 missing some new features in LAMMPS-ICMS. :ulb,l
 
 If you browse the HTML doc pages included in your tarball, they
 describe the version you have, however, not all new features in
 LAMMPS-ICMS are documented immediately. :l
 
 The "PDF file"_Manual.pdf on the WWW site or in the tarball is updated
 about once per month.  This is because it is large, and we don't want
 it to be part of every patch. :l
 
 There is also a "Developer.pdf"_Developer.pdf file in the doc
 directory, which describes the internal structure and algorithms of
 LAMMPS.  :ule,l
 
 LAMMPS stands for Large-scale Atomic/Molecular Massively Parallel
 Simulator.
 
 LAMMPS is a classical molecular dynamics simulation code designed to
 run efficiently on parallel computers.  It was developed at Sandia
 National Laboratories, a US Department of Energy facility, with
 funding from the DOE.  It is an open-source code, distributed freely
 under the terms of the GNU Public License (GPL).
 
 The primary developers of LAMMPS are "Steve Plimpton"_sjp, Aidan
 Thompson, and Paul Crozier who can be contacted at
 sjplimp,athomps,pscrozi at sandia.gov.  The "LAMMPS WWW Site"_lws at
 http://lammps.sandia.gov has more information about the code and its
 uses.
 
 :link(bug,http://lammps.sandia.gov/bug.html)
 :link(sjp,http://www.sandia.gov/~sjplimp)
 
 :line
 
 The LAMMPS documentation is organized into the following sections.  If
 you find errors or omissions in this manual or have suggestions for
 useful information to add, please send an email to the developers so
 we can improve the LAMMPS documentation.
 
 Once you are familiar with LAMMPS, you may want to bookmark "this
 page"_Section_commands.html#comm at Section_commands.html#comm since
 it gives quick access to documentation for all LAMMPS commands.
 
 "PDF file"_Manual.pdf of the entire manual, generated by
 "htmldoc"_http://www.easysw.com/htmldoc
 
 "Introduction"_Section_intro.html :olb,l
   1.1 "What is LAMMPS"_intro_1 :ulb,b
   1.2 "LAMMPS features"_intro_2 :b
   1.3 "LAMMPS non-features"_intro_3 :b
   1.4 "Open source distribution"_intro_4 :b
   1.5 "Acknowledgments and citations"_intro_5 :ule,b
 "Getting started"_Section_start.html :l
   2.1 "What's in the LAMMPS distribution"_start_1 :ulb,b
   2.2 "Making LAMMPS"_start_2 :b
   2.3 "Making LAMMPS with optional packages"_start_3 :b
   2.4 "Building LAMMPS via the Make.py script"_start_4 :b
   2.5 "Building LAMMPS as a library"_start_5 :b
   2.6 "Running LAMMPS"_start_6 :b
   2.7 "Command-line options"_start_7 :b
   2.8 "Screen output"_start_8 :b
   2.9 "Tips for users of previous versions"_start_9 :ule,b
 "Commands"_Section_commands.html :l
   3.1 "LAMMPS input script"_cmd_1 :ulb,b
   3.2 "Parsing rules"_cmd_2 :b
   3.3 "Input script structure"_cmd_3 :b
   3.4 "Commands listed by category"_cmd_4 :b
   3.5 "Commands listed alphabetically"_cmd_5 :ule,b
 "Packages"_Section_packages.html :l
   4.1 "Standard packages"_pkg_1 :ulb,b
   4.2 "User packages"_pkg_2 :ule,b
 "Accelerating LAMMPS performance"_Section_accelerate.html :l
   5.1 "Measuring performance"_acc_1 :ulb,b
   5.2 "General strategies"_acc_2 :b
   5.3 "Packages with optimized styles"_acc_3 :b
   5.4 "OPT package"_acc_4 :b
   5.5 "USER-OMP package"_acc_5 :b
   5.6 "GPU package"_acc_6 :b
   5.7 "USER-CUDA package"_acc_7 :b
   5.8 "KOKKOS package"_acc_8 :b
   5.9 "Comparison of GPU and USER-CUDA packages"_acc_9 :ule,b
 "How-to discussions"_Section_howto.html :l
   6.1 "Restarting a simulation"_howto_1 :ulb,b
   6.2 "2d simulations"_howto_2 :b
   6.3 "CHARMM and AMBER force fields"_howto_3 :b
   6.4 "Running multiple simulations from one input script"_howto_4 :b
   6.5 "Multi-replica simulations"_howto_5 :b
   6.6 "Granular models"_howto_6 :b
   6.7 "TIP3P water model"_howto_7 :b
   6.8 "TIP4P water model"_howto_8 :b
   6.9 "SPC water model"_howto_9 :b
   6.10 "Coupling LAMMPS to other codes"_howto_10 :b
   6.11 "Visualizing LAMMPS snapshots"_howto_11 :b
   6.12 "Triclinic (non-orthogonal) simulation boxes"_howto_12 :b
   6.13 "NEMD simulations"_howto_13 :b
   6.14 "Finite-size spherical and aspherical particles"_howto_14 :b
   6.15 "Output from LAMMPS (thermo, dumps, computes, fixes, variables)"_howto_15 :b
   6.16 "Thermostatting, barostatting, and compute temperature"_howto_16 :b
   6.17 "Walls"_howto_17 :b
   6.18 "Elastic constants"_howto_18 :b
   6.19 "Library interface to LAMMPS"_howto_19 :b
   6.20 "Calculating thermal conductivity"_howto_20 :b
   6.21 "Calculating viscosity"_howto_21 :b
   6.22 "Calculating a diffusion coefficient"_howto_22 :ule,b
 "Example problems"_Section_example.html :l
 "Performance & scalability"_Section_perf.html :l
 "Additional tools"_Section_tools.html :l
 "Modifying & extending LAMMPS"_Section_modify.html :l
   10.1 "Atom styles"_mod_1 :ulb,b
   10.2 "Bond, angle, dihedral, improper potentials"_mod_2 :b
   10.3 "Compute styles"_mod_3 :b
   10.4 "Dump styles"_mod_4 :b
   10.5 "Dump custom output options"_mod_5 :b
   10.6 "Fix styles"_mod_6 :b
   10.7 "Input script commands"_mod_7 :b
   10.8 "Kspace computations"_mod_8 :b
   10.9 "Minimization styles"_mod_9 :b
   10.10 "Pairwise potentials"_mod_10 :b
   10.11 "Region styles"_mod_11 :b
   10.12 "Body styles"_mod_12 :b
   10.13 "Thermodynamic output options"_mod_13 :b
   10.14 "Variable options"_mod_14 :b
   10.15 "Submitting new features for inclusion in LAMMPS"_mod_15 :ule,b
 "Python interface"_Section_python.html :l
   11.1 "Building LAMMPS as a shared library"_py_1 :ulb,b
   11.2 "Installing the Python wrapper into Python"_py_2 :b
   11.3 "Extending Python with MPI to run in parallel"_py_3 :b
   11.4 "Testing the Python-LAMMPS interface"_py_4 :b
   11.5 "Using LAMMPS from Python"_py_5 :b
   11.6 "Example Python scripts that use LAMMPS"_py_6 :ule,b
 "Errors"_Section_errors.html :l
   12.1 "Common problems"_err_1 :ulb,b
   12.2 "Reporting bugs"_err_2 :b
   12.3 "Error & warning messages"_err_3 :ule,b
 "Future and history"_Section_history.html :l
   13.1 "Coming attractions"_hist_1 :ulb,b
   13.2 "Past versions"_hist_2 :ule,b
 :ole
 
 :link(intro_1,Section_intro.html#intro_1)
 :link(intro_2,Section_intro.html#intro_2)
 :link(intro_3,Section_intro.html#intro_3)
 :link(intro_4,Section_intro.html#intro_4)
 :link(intro_5,Section_intro.html#intro_5)
 
 :link(start_1,Section_start.html#start_1)
 :link(start_2,Section_start.html#start_2)
 :link(start_3,Section_start.html#start_3)
 :link(start_4,Section_start.html#start_4)
 :link(start_5,Section_start.html#start_5)
 :link(start_6,Section_start.html#start_6)
 :link(start_7,Section_start.html#start_7)
 :link(start_8,Section_start.html#start_8)
 :link(start_9,Section_start.html#start_9)
 
 :link(cmd_1,Section_commands.html#cmd_1)
 :link(cmd_2,Section_commands.html#cmd_2)
 :link(cmd_3,Section_commands.html#cmd_3)
 :link(cmd_4,Section_commands.html#cmd_4)
 :link(cmd_5,Section_commands.html#cmd_5)
 
 :link(pkg_1,Section_packages.html#pkg_1)
 :link(pkg_2,Section_packages.html#pkg_2)
 
 :link(acc_1,Section_accelerate.html#acc_1)
 :link(acc_2,Section_accelerate.html#acc_2)
 :link(acc_3,Section_accelerate.html#acc_3)
 :link(acc_4,Section_accelerate.html#acc_4)
 :link(acc_5,Section_accelerate.html#acc_5)
 :link(acc_6,Section_accelerate.html#acc_6)
 :link(acc_7,Section_accelerate.html#acc_7)
 :link(acc_8,Section_accelerate.html#acc_8)
 :link(acc_9,Section_accelerate.html#acc_9)
 
 :link(howto_1,Section_howto.html#howto_1)
 :link(howto_2,Section_howto.html#howto_2)
 :link(howto_3,Section_howto.html#howto_3)
 :link(howto_4,Section_howto.html#howto_4)
 :link(howto_5,Section_howto.html#howto_5)
 :link(howto_6,Section_howto.html#howto_6)
 :link(howto_7,Section_howto.html#howto_7)
 :link(howto_8,Section_howto.html#howto_8)
 :link(howto_9,Section_howto.html#howto_9)
 :link(howto_10,Section_howto.html#howto_10)
 :link(howto_11,Section_howto.html#howto_11)
 :link(howto_12,Section_howto.html#howto_12)
 :link(howto_13,Section_howto.html#howto_13)
 :link(howto_14,Section_howto.html#howto_14)
 :link(howto_15,Section_howto.html#howto_15)
 :link(howto_16,Section_howto.html#howto_16)
 :link(howto_17,Section_howto.html#howto_17)
 :link(howto_18,Section_howto.html#howto_18)
 :link(howto_19,Section_howto.html#howto_19)
 :link(howto_20,Section_howto.html#howto_20)
 :link(howto_21,Section_howto.html#howto_21)
 
 :link(mod_1,Section_modify.html#mod_1)
 :link(mod_2,Section_modify.html#mod_2)
 :link(mod_3,Section_modify.html#mod_3)
 :link(mod_4,Section_modify.html#mod_4)
 :link(mod_5,Section_modify.html#mod_5)
 :link(mod_6,Section_modify.html#mod_6)
 :link(mod_7,Section_modify.html#mod_7)
 :link(mod_8,Section_modify.html#mod_8)
 :link(mod_9,Section_modify.html#mod_9)
 :link(mod_10,Section_modify.html#mod_10)
 :link(mod_11,Section_modify.html#mod_11)
 :link(mod_12,Section_modify.html#mod_12)
 :link(mod_13,Section_modify.html#mod_13)
 :link(mod_14,Section_modify.html#mod_14)
 :link(mod_15,Section_modify.html#mod_15)
 
 :link(py_1,Section_python.html#py_1)
 :link(py_2,Section_python.html#py_2)
 :link(py_3,Section_python.html#py_3)
 :link(py_4,Section_python.html#py_4)
 :link(py_5,Section_python.html#py_5)
 :link(py_6,Section_python.html#py_6)
 
 :link(err_1,Section_errors.html#err_1)
 :link(err_2,Section_errors.html#err_2)
 :link(err_3,Section_errors.html#err_3)
 
 :link(hist_1,Section_history.html#hist_1)
 :link(hist_2,Section_history.html#hist_2)
 
 </BODY>
diff --git a/doc/compute_property_atom.html b/doc/compute_property_atom.html
index 62a7e04da..d22e65c54 100644
--- a/doc/compute_property_atom.html
+++ b/doc/compute_property_atom.html
@@ -1,167 +1,168 @@
 <HTML>
 <CENTER><A HREF = "http://lammps.sandia.gov">LAMMPS WWW Site</A> - <A HREF = "Manual.html">LAMMPS Documentation</A> - <A HREF = "Section_commands.html#comm">LAMMPS Commands</A> 
 </CENTER>
 
 
 
 
 
 
 <HR>
 
 <H3>compute property/atom command 
 </H3>
 <P><B>Syntax:</B>
 </P>
 <PRE>compute ID group-ID property/atom input1 input2 ... 
 </PRE>
 <UL><LI>ID, group-ID are documented in <A HREF = "compute.html">compute</A> command 
 
 <LI>property/atom = style name of this compute command 
 
 <LI>input = one or more atom attributes 
 
-<PRE>  possible attributes = id, mol, type, mass,
+<PRE>  possible attributes = id, mol, proc, type, mass,
  	                x, y, z, xs, ys, zs, xu, yu, zu, ix, iy, iz,
 		        vx, vy, vz, fx, fy, fz,
                         q, mux, muy, muz, mu,
                         radius, diameter, omegax, omegay, omegaz,
 			angmomx, angmomy, angmomz,
 			shapex,shapey, shapez,
 		        quatw, quati, quatj, quatk, tqx, tqy, tqz,
 			end1x, end1y, end1z, end2x, end2y, end2z,
 			corner1x, corner1y, corner1z,
 			corner2x, corner2y, corner2z,
 			corner3x, corner3y, corner3z,
 			nbonds,
                         vfrac, s0,
 			spin, eradius, ervel, erforce,
                         rho, drho, e, de, cv,
                         i_name, d_name 
 </PRE>
 <PRE>      id = atom ID
       mol = molecule ID
+      proc = ID of processor that owns atom
       type = atom type
       mass = atom mass
       x,y,z = unscaled atom coordinates
       xs,ys,zs = scaled atom coordinates
       xu,yu,zu = unwrapped atom coordinates
       ix,iy,iz = box image that the atom is in
       vx,vy,vz = atom velocities
       fx,fy,fz = forces on atoms
       q = atom charge
       mux,muy,muz = orientation of dipole moment of atom
       mu = magnitude of dipole moment of atom
       radius,diameter = radius,diameter of spherical particle
       omegax,omegay,omegaz = angular velocity of spherical particle
       angmomx,angmomy,angmomz = angular momentum of aspherical particle
       shapex,shapey,shapez = 3 diameters of aspherical particle
       quatw,quati,quatj,quatk = quaternion components for aspherical or body particles
       tqx,tqy,tqz = torque on finite-size particles
       end12x, end12y, end12z = end points of line segment
       corner123x, corner123y, corner123z = corner points of triangle
       nbonds = number of bonds 
 </PRE>
 <PRE>      PERI package per-atom properties:
       vfrac = ???
       s0 = ??? 
 </PRE>
 <PRE>      USER-EFF and USER-AWPMD package per-atom properties:
       spin = electron spin
       eradius = electron radius
       ervel = electron radial velocity
       erforce = electron radial force 
 </PRE>
 <PRE>      USER-SPH package per-atom properties:
       rho = ???
       drho = ???
       e = ???
       de = ???
       cv = ??? 
 </PRE>
 <PRE>      <A HREF = "fix_property_atom.html">fix property/atom</A> per-atom properties:
       i_name = custom integer vector with name
       d_name = custom integer vector with name 
 </PRE>
 
 </UL>
 <P><B>Examples:</B>
 </P>
 <PRE>compute 1 all property/atom xs vx fx mux 
 compute 2 all property/atom type
 compute 1 all property/atom ix iy iz 
 </PRE>
 <P><B>Description:</B>
 </P>
 <P>Define a computation that simply stores atom attributes for each atom
 in the group.  This is useful so that the values can be used by other
 <A HREF = "Section_howto.html#howto_15">output commands</A> that take computes as
 inputs.  See for example, the <A HREF = "compute_reduce.html">compute reduce</A>,
 <A HREF = "fix_ave_atom.html">fix ave/atom</A>, <A HREF = "fix_ave_histo.html">fix ave/histo</A>,
 <A HREF = "fix_ave_spatial.html">fix ave/spatial</A>, and <A HREF = "variable.html">atom-style
 variable</A> commands.
 </P>
 <P>The list of possible attributes is the same as that used by the <A HREF = "dump.html">dump
 custom</A> command, which describes their meaning, with some
 additional quantities that are only defined for certain <A HREF = "atom_style.html">atom
-styles</A>.  Basically, this list gives your input script
-access to any per-atom quantity stored by LAMMPS.
+styles</A>.  Basically, this augmented list gives an
+input script access to any per-atom quantity stored by LAMMPS.
 </P>
 <P>The values are stored in a per-atom vector or array as discussed
 below.  Zeroes are stored for atoms not in the specified group or for
 quantities that are not defined for a particular particle in the group
 (e.g. <I>shapex</I> if the particle is not an ellipsoid).
 </P>
 <P>The additional quantities only accessible via this command, and not
 directly via the <A HREF = "dump.html">dump custom</A> command, are as follows.
 </P>
 <P><I>Shapex</I>, <I>shapey</I>, and <I>shapez</I> are defined for ellipsoidal particles
 and define the 3d shape of each particle.
 </P>
 <P><I>Quatw</I>, <I>quati</I>, <I>quatj</I>, and <I>quatk</I> are defined for ellipsoidal
 particles and body particles and store the 4-vector quaternion
 representing the orientation of each particle.  See the <A HREF = "set.html">set</A>
 command for an explanation of the quaternion vector.
 </P>
 <P><I>End1x</I>, <I>end1y</I>, <I>end1z</I>, <I>end2x</I>, <I>end2y</I>, <I>end2z</I>, are defined for
 line segment particles and define the end points of each line segment.
 </P>
 <P><I>Corner1x</I>, <I>corner1y</I>, <I>corner1z</I>, <I>corner2x</I>, <I>corner2y</I>,
 <I>corner2z</I>, <I>corner3x</I>, <I>corner3y</I>, <I>corner3z</I>, are defined for
 triangular particles and define the corner points of each triangle.
 </P>
 <P><I>nbonds</I> is available for all molecular atom styles and refers to
 the number of explicit bonds attached to an atom.
 </P>
 <P>The <I>i_name</I> and <I>d_name</I> attributes refer to custom integer and
 floating-point properties that have been added to each atom via the
 <A HREF = "fix_property_atom.html">fix property/atom</A> command.  When that command
 is used specific names are given to each attribute which are what is
 specified as the "name" portion of <I>i_name</I> or <I>d_name</I>.
 </P>
 <P><B>Output info:</B>
 </P>
 <P>This compute calculates a per-atom vector or per-atom array depending
 on the number of input values.  If a single input is specified, a
 per-atom vector is produced.  If two or more inputs are specified, a
 per-atom array is produced where the number of columns = the number of
 inputs.  The vector or array can be accessed by any command that uses
 per-atom values from a compute as input.  See <A HREF = "Section_howto.html#howto_15">this
 section</A> for an overview of LAMMPS output
 options.
 </P>
 <P>The vector or array values will be in whatever <A HREF = "units.html">units</A> the
 corresponding attribute is in, e.g. velocity units for vx, charge
 units for q, etc.
 </P>
 <P><B>Restrictions:</B> none
 </P>
 <P><B>Related commands:</B>
 </P>
 <P><A HREF = "dump.html">dump custom</A>, <A HREF = "compute_reduce.html">compute reduce</A>, <A HREF = "fix_ave_atom.html">fix
 ave/atom</A>, <A HREF = "fix_ave_spatial.html">fix ave/spatial</A>,
 <A HREF = "fix_property_atom.html">fix property/atom</A>
 </P>
 <P><B>Default:</B> none
 </P>
 </HTML>
diff --git a/doc/compute_property_atom.txt b/doc/compute_property_atom.txt
index b6c1cc2f0..82f6e651b 100644
--- a/doc/compute_property_atom.txt
+++ b/doc/compute_property_atom.txt
@@ -1,157 +1,158 @@
 "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
 
 compute property/atom command :h3
 
 [Syntax:]
 
 compute ID group-ID property/atom input1 input2 ... :pre
 
 ID, group-ID are documented in "compute"_compute.html command :ulb,l
 property/atom = style name of this compute command :l
 input = one or more atom attributes :l
-  possible attributes = id, mol, type, mass,
+  possible attributes = id, mol, proc, type, mass,
  	                x, y, z, xs, ys, zs, xu, yu, zu, ix, iy, iz,
 		        vx, vy, vz, fx, fy, fz,
                         q, mux, muy, muz, mu,
                         radius, diameter, omegax, omegay, omegaz,
 			angmomx, angmomy, angmomz,
 			shapex,shapey, shapez,
 		        quatw, quati, quatj, quatk, tqx, tqy, tqz,
 			end1x, end1y, end1z, end2x, end2y, end2z,
 			corner1x, corner1y, corner1z,
 			corner2x, corner2y, corner2z,
 			corner3x, corner3y, corner3z,
 			nbonds,
                         vfrac, s0,
 			spin, eradius, ervel, erforce,
                         rho, drho, e, de, cv,
                         i_name, d_name :pre
       id = atom ID
       mol = molecule ID
+      proc = ID of processor that owns atom
       type = atom type
       mass = atom mass
       x,y,z = unscaled atom coordinates
       xs,ys,zs = scaled atom coordinates
       xu,yu,zu = unwrapped atom coordinates
       ix,iy,iz = box image that the atom is in
       vx,vy,vz = atom velocities
       fx,fy,fz = forces on atoms
       q = atom charge
       mux,muy,muz = orientation of dipole moment of atom
       mu = magnitude of dipole moment of atom
       radius,diameter = radius,diameter of spherical particle
       omegax,omegay,omegaz = angular velocity of spherical particle
       angmomx,angmomy,angmomz = angular momentum of aspherical particle
       shapex,shapey,shapez = 3 diameters of aspherical particle
       quatw,quati,quatj,quatk = quaternion components for aspherical or body particles
       tqx,tqy,tqz = torque on finite-size particles
       end12x, end12y, end12z = end points of line segment
       corner123x, corner123y, corner123z = corner points of triangle
       nbonds = number of bonds :pre
 
       PERI package per-atom properties:
       vfrac = ???
       s0 = ??? :pre
 
       USER-EFF and USER-AWPMD package per-atom properties:
       spin = electron spin
       eradius = electron radius
       ervel = electron radial velocity
       erforce = electron radial force :pre
 
       USER-SPH package per-atom properties:
       rho = ???
       drho = ???
       e = ???
       de = ???
       cv = ??? :pre
 
       "fix property/atom"_fix_property_atom.html per-atom properties:
       i_name = custom integer vector with name
       d_name = custom integer vector with name :pre
 :ule
 
 [Examples:]
 
 compute 1 all property/atom xs vx fx mux 
 compute 2 all property/atom type
 compute 1 all property/atom ix iy iz :pre
 
 [Description:]
 
 Define a computation that simply stores atom attributes for each atom
 in the group.  This is useful so that the values can be used by other
 "output commands"_Section_howto.html#howto_15 that take computes as
 inputs.  See for example, the "compute reduce"_compute_reduce.html,
 "fix ave/atom"_fix_ave_atom.html, "fix ave/histo"_fix_ave_histo.html,
 "fix ave/spatial"_fix_ave_spatial.html, and "atom-style
 variable"_variable.html commands.
 
 The list of possible attributes is the same as that used by the "dump
 custom"_dump.html command, which describes their meaning, with some
 additional quantities that are only defined for certain "atom
-styles"_atom_style.html.  Basically, this list gives your input script
-access to any per-atom quantity stored by LAMMPS.
+styles"_atom_style.html.  Basically, this augmented list gives an
+input script access to any per-atom quantity stored by LAMMPS.
 
 The values are stored in a per-atom vector or array as discussed
 below.  Zeroes are stored for atoms not in the specified group or for
 quantities that are not defined for a particular particle in the group
 (e.g. {shapex} if the particle is not an ellipsoid).
 
 The additional quantities only accessible via this command, and not
 directly via the "dump custom"_dump.html command, are as follows.
 
 {Shapex}, {shapey}, and {shapez} are defined for ellipsoidal particles
 and define the 3d shape of each particle.
 
 {Quatw}, {quati}, {quatj}, and {quatk} are defined for ellipsoidal
 particles and body particles and store the 4-vector quaternion
 representing the orientation of each particle.  See the "set"_set.html
 command for an explanation of the quaternion vector.
 
 {End1x}, {end1y}, {end1z}, {end2x}, {end2y}, {end2z}, are defined for
 line segment particles and define the end points of each line segment.
 
 {Corner1x}, {corner1y}, {corner1z}, {corner2x}, {corner2y},
 {corner2z}, {corner3x}, {corner3y}, {corner3z}, are defined for
 triangular particles and define the corner points of each triangle.
 
 {nbonds} is available for all molecular atom styles and refers to
 the number of explicit bonds attached to an atom.
 
 The {i_name} and {d_name} attributes refer to custom integer and
 floating-point properties that have been added to each atom via the
 "fix property/atom"_fix_property_atom.html command.  When that command
 is used specific names are given to each attribute which are what is
 specified as the "name" portion of {i_name} or {d_name}.
 
 [Output info:]
 
 This compute calculates a per-atom vector or per-atom array depending
 on the number of input values.  If a single input is specified, a
 per-atom vector is produced.  If two or more inputs are specified, a
 per-atom array is produced where the number of columns = the number of
 inputs.  The vector or array can be accessed by any command that uses
 per-atom values from a compute as input.  See "this
 section"_Section_howto.html#howto_15 for an overview of LAMMPS output
 options.
 
 The vector or array values will be in whatever "units"_units.html the
 corresponding attribute is in, e.g. velocity units for vx, charge
 units for q, etc.
 
 [Restrictions:] none
 
 [Related commands:]
 
 "dump custom"_dump.html, "compute reduce"_compute_reduce.html, "fix
 ave/atom"_fix_ave_atom.html, "fix ave/spatial"_fix_ave_spatial.html,
 "fix property/atom"_fix_property_atom.html
 
 [Default:] none
diff --git a/doc/dump.html b/doc/dump.html
index 5527540cc..e83d9f5c5 100644
--- a/doc/dump.html
+++ b/doc/dump.html
@@ -1,609 +1,611 @@
 <HTML>
 <CENTER> <A HREF = "http://lammps.sandia.gov">LAMMPS WWW Site</A> - <A HREF = "Manual.html">LAMMPS Documentation</A> - <A HREF = "Section_commands.html#comm">LAMMPS Commands</A> 
 </CENTER>
 
 
 
 
 
 
 <HR>
 
 <H3>dump command 
 </H3>
 <H3><A HREF = "dump_image.html">dump image</A> command 
 </H3>
 <H3><A HREF = "dump_image.html">dump movie</A> command 
 </H3>
 <H3><A HREF = "dump_molfile.html">dump molfile</A> command 
 </H3>
 <P><B>Syntax:</B>
 </P>
 <PRE>dump ID group-ID style N file args 
 </PRE>
 <UL><LI>ID = user-assigned name for the dump 
 
 <LI>group-ID = ID of the group of atoms to be dumped 
 
 <LI>style = <I>atom</I> or <I>atom/mpiio</I> or <I>cfg</I> or <I>dcd</I> or <I>xtc</I> or <I>xyz</I> or <I>xyz/mpiio</I> or <I>image</I> or <I>movie</I> or <I>molfile</I> or <I>local</I> or <I>custom</I> or <I>custom/mpiio</I> 
 
 <LI>N = dump every this many timesteps 
 
 <LI>file = name of file to write dump info to 
 
 <LI>args = list of arguments for a particular style 
 
 <PRE>  <I>atom</I> args = none
   <I>atom/mpiio</I> args = none
   <I>cfg</I> args = same as <I>custom</I> args, see below
   <I>dcd</I> args = none
   <I>xtc</I> args = none
   <I>xyz</I> args = none 
 </PRE>
 <PRE>  <I>xyz/mpiio</I> args = none 
 </PRE>
 <PRE>  <I>image</I> args = discussed on <A HREF = "dump_image.html">dump image</A> doc page 
 </PRE>
 <PRE>  <I>movie</I> args = discussed on <A HREF = "dump_image.html">dump image</A> doc page 
 </PRE>
 <PRE>  <I>molfile</I> args = discussed on <A HREF = "dump_molfile.html">dump molfile</A> doc page 
 </PRE>
 <PRE>  <I>local</I> args = list of local attributes
     possible attributes = index, c_ID, c_ID[N], f_ID, f_ID[N]
       index = enumeration of local values
       c_ID = local vector calculated by a compute with ID
       c_ID[N] = Nth column of local array calculated by a compute with ID
       f_ID = local vector calculated by a fix with ID
       f_ID[N] = Nth column of local array calculated by a fix with ID 
 </PRE>
 <PRE>  <I>custom</I> of <I>custom/mpiio</I> args = list of atom attributes
     possible attributes = id, mol, type, element, mass,
 			  x, y, z, xs, ys, zs, xu, yu, zu, 
 			  xsu, ysu, zsu, ix, iy, iz,
 			  vx, vy, vz, fx, fy, fz,
                           q, mux, muy, muz, mu,
                           radius, diameter, omegax, omegay, omegaz,
 			  angmomx, angmomy, angmomz, tqx, tqy, tqz,
 			  c_ID, c_ID[N], f_ID, f_ID[N], v_name 
 </PRE>
 <PRE>      id = atom ID
       mol = molecule ID
+      proc = ID of processor that owns atom
       type = atom type
       element = name of atom element, as defined by <A HREF = "dump_modify.html">dump_modify</A> command
       mass = atom mass
       x,y,z = unscaled atom coordinates
       xs,ys,zs = scaled atom coordinates
       xu,yu,zu = unwrapped atom coordinates
       xsu,ysu,zsu = scaled unwrapped atom coordinates
       ix,iy,iz = box image that the atom is in
       vx,vy,vz = atom velocities
       fx,fy,fz = forces on atoms
       q = atom charge
       mux,muy,muz = orientation of dipole moment of atom
       mu = magnitude of dipole moment of atom
       radius,diameter = radius,diameter of spherical particle
       omegax,omegay,omegaz = angular velocity of spherical particle
       angmomx,angmomy,angmomz = angular momentum of aspherical particle
       tqx,tqy,tqz = torque on finite-size particles
       c_ID = per-atom vector calculated by a compute with ID
       c_ID[N] = Nth column of per-atom array calculated by a compute with ID
       f_ID = per-atom vector calculated by a fix with ID
       f_ID[N] = Nth column of per-atom array calculated by a fix with ID
       v_name = per-atom vector calculated by an atom-style variable with name 
 </PRE>
 
 </UL>
 <P><B>Examples:</B>
 </P>
 <PRE>dump myDump all atom 100 dump.atom
 dump myDump all atom/mpiio 100 dump.atom.mpiio
 dump 2 subgroup atom 50 dump.run.bin
 dump 2 subgroup atom 50 dump.run.mpiio.bin
 dump 4a all custom 100 dump.myforce.* id type x y vx fx
 dump 4b flow custom 100 dump.%.myforce id type c_myF[3] v_ke
 dump 2 inner cfg 10 dump.snap.*.cfg mass type xs ys zs vx vy vz
 dump snap all cfg 100 dump.config.*.cfg mass type xs ys zs id type c_Stress[2]
 dump 1 all xtc 1000 file.xtc 
 </PRE>
 <P><B>Description:</B>
 </P>
 <P>Dump a snapshot of atom quantities to one or more files every N
 timesteps in one of several styles.  The <I>image</I> and <I>movie</I> styles are
 the exception: the <I>image</I> style renders a JPG, PNG, or PPM image file
 of the atom configuration every N timesteps while the <I>movie</I> style
 combines and compresses them into a movie file; both are discussed in
 detail on the <A HREF = "dump_image.html">dump image</A> doc page.  The timesteps on
 which dump output is written can also be controlled by a variable.
 See the <A HREF = "dump_modify.html">dump_modify every</A> command.
 </P>
 <P>Only information for atoms in the specified group is dumped.  The
 <A HREF = "dump_modify.html">dump_modify thresh and region</A> commands can also
 alter what atoms are included.  Not all styles support all these
 options; see details below.
 </P>
 <P>As described below, the filename determines the kind of output (text
 or binary or gzipped, one big file or one per timestep, one big file
 or multiple smaller files).
 </P>
 <P>IMPORTANT NOTE: Because periodic boundary conditions are enforced only
 on timesteps when neighbor lists are rebuilt, the coordinates of an
 atom written to a dump file may be slightly outside the simulation
 box.
 </P>
 <P>IMPORTANT NOTE: Unless the <A HREF = "dump_modify.html">dump_modify sort</A> option
 is invoked, the lines of atom information written to dump files
 (typically one line per atom) will be in an indeterminate order for
 each snapshot.  This is even true when running on a single processor,
 if the <A HREF = "atom_modify.html">atom_modify sort</A> option is on, which it is
 by default.  In this case atoms are re-ordered periodically during a
 simulation, due to spatial sorting.  It is also true when running in
 parallel, because data for a single snapshot is collected from
 multiple processors, each of which owns a subset of the atoms.
 </P>
 <P>For the <I>atom</I>, <I>custom</I>, <I>cfg</I>, and <I>local</I> styles, sorting is off by
 default.  For the <I>dcd</I>, <I>xtc</I>, <I>xyz</I>, and <I>molfile</I> styles, sorting by
 atom ID is on by default. See the <A HREF = "dump_modify.html">dump_modify</A> doc
 page for details.
 </P>
 <P>As explained below, the <I>atom/mpiio</I>, <I>custom/mpiio</I>, and <I>xyz/mpiio</I>
 styles are identical in command syntax and in the format of the dump
 files they create, to the corresponding styles without "mpiio", except
 the single dump file they produce is written in parallel via the
 MPI-IO library.  For the remainder of this doc page, you should thus
 consider the <I>atom</I> and <I>atom/mpiio</I> styles (etc) to be
 inter-changeable.  The one exception is how the filename is specified
 for the MPI-IO styles, as explained below.
 </P>
 <HR>
 
 <P>The <I>style</I> keyword determines what atom quantities are written to the
 file and in what format.  Settings made via the
 <A HREF = "dump_modify.html">dump_modify</A> command can also alter the format of
 individual values and the file itself.
 </P>
 <P>The <I>atom</I>, <I>local</I>, and <I>custom</I> styles create files in a simple text
 format that is self-explanatory when viewing a dump file.  Many of the
 LAMMPS <A HREF = "Section_tools.html">post-processing tools</A>, including
 <A HREF = "http://www.sandia.gov/~sjplimp/pizza.html">Pizza.py</A>, work with this
 format, as does the <A HREF = "rerun.html">rerun</A> command.
 </P>
 <P>For post-processing purposes the <I>atom</I>, <I>local</I>, and <I>custom</I> text
 files are self-describing in the following sense.
 </P>
 <P>The dimensions of the simulation box are included in each snapshot.
 For an orthogonal simulation box this information is is formatted as:
 </P>
 <PRE>ITEM: BOX BOUNDS xx yy zz
 xlo xhi
 ylo yhi
 zlo zhi 
 </PRE>
 <P>where xlo,xhi are the maximum extents of the simulation box in the
 x-dimension, and similarly for y and z.  The "xx yy zz" represent 6
 characters that encode the style of boundary for each of the 6
 simulation box boundaries (xlo,xhi and ylo,yhi and zlo,zhi).  Each of
 the 6 characters is either p = periodic, f = fixed, s = shrink wrap,
 or m = shrink wrapped with a minimum value.  See the
 <A HREF = "boundary.html">boundary</A> command for details.
 </P>
 <P>For triclinic simulation boxes (non-orthogonal), an orthogonal
 bounding box which encloses the triclinic simulation box is output,
 along with the 3 tilt factors (xy, xz, yz) of the triclinic box,
 formatted as follows:
 </P>
 <PRE>ITEM: BOX BOUNDS xy xz yz xx yy zz 
 xlo_bound xhi_bound xy
 ylo_bound yhi_bound xz
 zlo_bound zhi_bound yz 
 </PRE>
 <P>The presence of the text "xy xz yz" in the ITEM line indicates that
 the 3 tilt factors will be included on each of the 3 following lines.
 This bounding box is convenient for many visualization programs.  The
 meaning of the 6 character flags for "xx yy zz" is the same as above.
 </P>
 <P>Note that the first two numbers on each line are now xlo_bound instead
 of xlo, etc, since they repesent a bounding box.  See <A HREF = "Section_howto.html#howto_12">this
 section</A> of the doc pages for a geometric
 description of triclinic boxes, as defined by LAMMPS, simple formulas
 for how the 6 bounding box extents (xlo_bound,xhi_bound,etc) are
 calculated from the triclinic parameters, and how to transform those
 parameters to and from other commonly used triclinic representations.
 </P>
 <P>The "ITEM: ATOMS" line in each snapshot lists column descriptors for
 the per-atom lines that follow.  For example, the descriptors would be
 "id type xs ys zs" for the default <I>atom</I> style, and would be the atom
 attributes you specify in the dump command for the <I>custom</I> style.
 </P>
 <P>For style <I>atom</I>, atom coordinates are written to the file, along with
 the atom ID and atom type.  By default, atom coords are written in a
 scaled format (from 0 to 1).  I.e. an x value of 0.25 means the atom
 is at a location 1/4 of the distance from xlo to xhi of the box
 boundaries.  The format can be changed to unscaled coords via the
 <A HREF = "dump_modify.html">dump_modify</A> settings.  Image flags can also be
 added for each atom via dump_modify.
 </P>
 <P>Style <I>custom</I> allows you to specify a list of atom attributes to be
 written to the dump file for each atom.  Possible attributes are
 listed above and will appear in the order specified.  You cannot
 specify a quantity that is not defined for a particular simulation -
 such as <I>q</I> for atom style <I>bond</I>, since that atom style doesn't
 assign charges.  Dumps occur at the very end of a timestep, so atom
 attributes will include effects due to fixes that are applied during
 the timestep.  An explanation of the possible dump custom attributes
 is given below.
 </P>
 <P>For style <I>local</I>, local output generated by <A HREF = "compute.html">computes</A>
 and <A HREF = "fix.html">fixes</A> is used to generate lines of output that is
 written to the dump file.  This local data is typically calculated by
 each processor based on the atoms it owns, but there may be zero or
 more entities per atom, e.g. a list of bond distances.  An explanation
 of the possible dump local attributes is given below.  Note that by
 using input from the <A HREF = "compute_property_local.html">compute
 property/local</A> command with dump local,
 it is possible to generate information on bonds, angles, etc that can
 be cut and pasted directly into a data file read by the
 <A HREF = "read_data.html">read_data</A> command.
 </P>
 <P>Style <I>cfg</I> has the same command syntax as style <I>custom</I> and writes
 extended CFG format files, as used by the
 <A HREF = "http://mt.seas.upenn.edu/Archive/Graphics/A">AtomEye</A> visualization
 package.  Since the extended CFG format uses a single snapshot of the
 system per file, a wildcard "*" must be included in the filename, as
 discussed below.  The list of atom attributes for style <I>cfg</I> must
 begin with either "mass type xs ys zs" or "mass type xsu ysu zsu"
 since these quantities are needed to write the CFG files in the
 appropriate format (though the "mass" and "type" fields do not appear
 explicitly in the file).  Any remaining attributes will be stored as
 "auxiliary properties" in the CFG files.  Note that you will typically
 want to use the <A HREF = "dump_modify.html">dump_modify element</A> command with
 CFG-formatted files, to associate element names with atom types, so
 that AtomEye can render atoms appropriately. When unwrapped
 coordinates <I>xsu</I>, <I>ysu</I>, and <I>zsu</I> are requested, the nominal AtomEye
 periodic cell dimensions are expanded by a large factor UNWRAPEXPAND =
 10.0, which ensures atoms that are displayed correctly for up to
 UNWRAPEXPAND/2 periodic boundary crossings in any direction.  Beyond
 this, AtomEye will rewrap the unwrapped coordinates.  The expansion
 causes the atoms to be drawn farther away from the viewer, but it is
 easy to zoom the atoms closer, and the interatomic distances are
 unaffected.
 </P>
 <P>The <I>dcd</I> style writes DCD files, a standard atomic trajectory format
 used by the CHARMM, NAMD, and XPlor molecular dynamics packages.  DCD
 files are binary and thus may not be portable to different machines.
 The number of atoms per snapshot cannot change with the <I>dcd</I> style.
 The <I>unwrap</I> option of the <A HREF = "dump_modify.html">dump_modify</A> command
 allows DCD coordinates to be written "unwrapped" by the image flags
 for each atom.  Unwrapped means that if the atom has passed through
 a periodic boundary one or more times, the value is printed for what
 the coordinate would be if it had not been wrapped back into the
 periodic box.  Note that these coordinates may thus be far outside
 the box size stored with the snapshot.
 </P>
 <P>The <I>xtc</I> style writes XTC files, a compressed trajectory format used
 by the GROMACS molecular dynamics package, and described
 <A HREF = "http://manual.gromacs.org/current/online/xtc.html">here</A>.
 The precision used in XTC files can be adjusted via the
 <A HREF = "dump_modify.html">dump_modify</A> command.  The default value of 1000
 means that coordinates are stored to 1/1000 nanometer accuracy.  XTC
 files are portable binary files written in the NFS XDR data format, 
 so that any machine which supports XDR should be able to read them. 
 The number of atoms per snapshot cannot change with the <I>xtc</I> style.
 The <I>unwrap</I> option of the <A HREF = "dump_modify.html">dump_modify</A> command allows
 XTC coordinates to be written "unwrapped" by the image flags for each
 atom.  Unwrapped means that if the atom has passed thru a periodic
 boundary one or more times, the value is printed for what the
 coordinate would be if it had not been wrapped back into the periodic
 box.  Note that these coordinates may thus be far outside the box size
 stored with the snapshot.
 </P>
 <P>The <I>xyz</I> style writes XYZ files, which is a simple text-based
 coordinate format that many codes can read. Specifically it has
 a line with the number of atoms, then a comment line that is
 usually ignored followed by one line per atom with the atom type
 and the x-, y-, and z-coordinate of that atom. You can use the
 <A HREF = "dump_modify.html">dump_modify element</A> option to change the output
 from using the (numerical) atom type to an element name (or some
 other label). This will help many visualization programs to guess
 bonds and colors.
 </P>
 <P>Note that <I>atom</I>, <I>custom</I>, <I>dcd</I>, <I>xtc</I>, and <I>xyz</I> style dump files
 can be read directly by <A HREF = "http://www.ks.uiuc.edu/Research/vmd">VMD</A>, a
 popular molecular viewing program.  See <A HREF = "Section_tools.html#vmd">Section
 tools</A> of the manual and the
 tools/lmp2vmd/README.txt file for more information about support in
 VMD for reading and visualizing LAMMPS dump files.
 </P>
 <HR>
 
 <P>Dumps are performed on timesteps that are a multiple of N (including
 timestep 0) and on the last timestep of a minimization if the
 minimization converges.  Note that this means a dump will not be
 performed on the initial timestep after the dump command is invoked,
 if the current timestep is not a multiple of N.  This behavior can be
 changed via the <A HREF = "dump_modify.html">dump_modify first</A> command, which
 can also be useful if the dump command is invoked after a minimization
 ended on an arbitrary timestep.  N can be changed between runs by
 using the <A HREF = "dump_modify.html">dump_modify every</A> command (not allowed
 for <I>dcd</I> style).  The <A HREF = "dump_modify.html">dump_modify every</A> command
 also allows a variable to be used to determine the sequence of
 timesteps on which dump files are written.  In this mode a dump on the
 first timestep of a run will also not be written unless the
 <A HREF = "dump_modify.html">dump_modify first</A> command is used.
 </P>
 <P>The specified filename determines how the dump file(s) is written.
 The default is to write one large text file, which is opened when the
 dump command is invoked and closed when an <A HREF = "undump.html">undump</A>
 command is used or when LAMMPS exits.  For the <I>dcd</I> and <I>xtc</I> styles,
 this is a single large binary file.
 </P>
 <P>Dump filenames can contain two wildcard characters.  If a "*"
 character appears in the filename, then one file per snapshot is
 written and the "*" character is replaced with the timestep value.
 For example, tmp.dump.* becomes tmp.dump.0, tmp.dump.10000,
 tmp.dump.20000, etc.  This option is not available for the <I>dcd</I> and
 <I>xtc</I> styles.  Note that the <A HREF = "dump_modify.html">dump_modify pad</A>
 command can be used to insure all timestep numbers are the same length
 (e.g. 00010), which can make it easier to read a series of dump files
 in order with some post-processing tools.
 </P>
 <P>If a "%" character appears in the filename, then each of P processors
 writes a portion of the dump file, and the "%" character is replaced
 with the processor ID from 0 to P-1.  For example, tmp.dump.% becomes
 tmp.dump.0, tmp.dump.1, ... tmp.dump.P-1, etc.  This creates smaller
 files and can be a fast mode of output on parallel machines that
 support parallel I/O for output. This option is not available for the
 <I>dcd</I>, <I>xtc</I>, and <I>xyz</I> styles.
 </P>
 <P>By default, P = the number of processors meaning one file per
 processor, but P can be set to a smaller value via the <I>nfile</I> or
 <I>fileper</I> keywords of the <A HREF = "dump_modify.html">dump_modify</A> command.
 These options can be the most efficient way of writing out dump files
 when running on large numbers of processors.
 </P>
 <P>Note that using the "*" and "%" characters together can produce a
 large number of small dump files!
 </P>
 <P>For the <I>atom/mpiio</I>, <I>custom/mpiio</I>, and <I>xyz/mpiio</I> styles, a single
 dump file is written in parallel via the MPI-IO library, which is part
 of the MPI standard for versions 2.0 and above.  Using MPI-IO requires
 two steps.  First, build LAMMPS with its MPIIO package installed, e.g.
 </P>
 <PRE>make yes-mpiio    # installs the MPIIO package
 make g++          # build LAMMPS for your platform 
 </PRE>
 <P>Second, use a dump filename which contains ".mpiio".  Note that it
 does not have to end in ".mpiio", just contain those characters.
 Unlike MPI-IO restart files, which must be both written and read using
 MPI-IO, the dump files produced by these MPI-IO styles are identical
 in format to the files produced by their non-MPI-IO style
 counterparts.  This means you can write a dump file using MPI-IO and
 use the <A HREF = "read_dump.html">read_dump</A> command or perform other
 post-processing, just as if the dump file was not written using
 MPI-IO.
 </P>
 <P>Note that MPI-IO dump files are one large file which all processors
 write to.  You thus cannot use the "%" wildcard character described
 above in the filename since that specifies generation of multiple
 files.  You can use the ".bin" suffix described below in an MPI-IO
 dump file; again this file will be written in parallel and have the
 same binary format as if it were written without MPI-IO.
 </P>
 <P>If the filename ends with ".bin", the dump file (or files, if "*" or
 "%" is also used) is written in binary format.  A binary dump file
 will be about the same size as a text version, but will typically
 write out much faster.  Of course, when post-processing, you will need
 to convert it back to text format (see the <A HREF = "Section_tools.html#binary">binary2txt
 tool</A>) or write your own code to read the
 binary file.  The format of the binary file can be understood by
 looking at the tools/binary2txt.cpp file.  This option is only
 available for the <I>atom</I> and <I>custom</I> styles.
 </P>
 <P>If the filename ends with ".gz", the dump file (or files, if "*" or "%"
 is also used) is written in gzipped format.  A gzipped dump file will
 be about 3x smaller than the text version, but will also take longer
 to write.  This option is not available for the <I>dcd</I> and <I>xtc</I>
 styles.
 </P>
 <HR>
 
 <P>This section explains the local attributes that can be specified as
 part of the <I>local</I> style.
 </P>
 <P>The <I>index</I> attribute can be used to generate an index number from 1
 to N for each line written into the dump file, where N is the total
 number of local datums from all processors, or lines of output that
 will appear in the snapshot.  Note that because data from different
 processors depend on what atoms they currently own, and atoms migrate
 between processor, there is no guarantee that the same index will be
 used for the same info (e.g. a particular bond) in successive
 snapshots.
 </P>
 <P>The <I>c_ID</I> and <I>c_ID[N]</I> attributes allow local vectors or arrays
 calculated by a <A HREF = "compute.html">compute</A> to be output.  The ID in the
 attribute should be replaced by the actual ID of the compute that has
 been defined previously in the input script.  See the
 <A HREF = "compute.html">compute</A> command for details.  There are computes for
 calculating local information such as indices, types, and energies for
 bonds and angles.
 </P>
 <P>Note that computes which calculate global or per-atom quantities, as
 opposed to local quantities, cannot be output in a dump local command.
 Instead, global quantities can be output by the <A HREF = "thermo_style.html">thermo_style
 custom</A> command, and per-atom quantities can be
 output by the dump custom command.
 </P>
 <P>If <I>c_ID</I> is used as a attribute, then the local vector calculated by
 the compute is printed.  If <I>c_ID[N]</I> is used, then N must be in the
 range from 1-M, which will print the Nth column of the M-length local
 array calculated by the compute.
 </P>
 <P>The <I>f_ID</I> and <I>f_ID[N]</I> attributes allow local vectors or arrays
 calculated by a <A HREF = "fix.html">fix</A> to be output.  The ID in the attribute
 should be replaced by the actual ID of the fix that has been defined
 previously in the input script.
 </P>
 <P>If <I>f_ID</I> is used as a attribute, then the local vector calculated by
 the fix is printed.  If <I>f_ID[N]</I> is used, then N must be in the
 range from 1-M, which will print the Nth column of the M-length local
 array calculated by the fix.
 </P>
 <P>Here is an example of how to dump bond info for a system,
 including the distance and energy of each bond:
 </P>
 <PRE>compute 1 all property/local batom1 batom2 btype 
 compute 2 all bond/local dist eng
 dump 1 all local 1000 tmp.dump index c_1[1] c_1[2] c_1[3] c_2[1] c_2[2] 
 </PRE>
 <HR>
 
 <P>This section explains the atom attributes that can be specified as
 part of the <I>custom</I> and <I>cfg</I> styles.
 </P>
-<P>The <I>id</I>, <I>mol</I>, <I>type</I>, <I>element</I>, <I>mass</I>, <I>vx</I>, <I>vy</I>, <I>vz</I>, <I>fx</I>, <I>fy</I>,
-<I>fz</I>, <I>q</I> attributes are self-explanatory.
+<P>The <I>id</I>, <I>mol</I>, <I>proc</I>, <I>type</I>, <I>element</I>, <I>mass</I>, <I>vx</I>, <I>vy</I>, <I>vz</I>,
+<I>fx</I>, <I>fy</I>, <I>fz</I>, <I>q</I> attributes are self-explanatory.
 </P>
 <P><I>Id</I> is the atom ID.  <I>Mol</I> is the molecule ID, included in the data
-file for molecular systems.  <I>Type</I> is the atom type.  <I>Element</I> is
-typically the chemical name of an element, which you must assign to
-each type via the <A HREF = "dump_modify.html">dump_modify element</A> command.
-More generally, it can be any string you wish to associated with an
-atom type.  <I>Mass</I> is the atom mass.  <I>Vx</I>, <I>vy</I>, <I>vz</I>, <I>fx</I>, <I>fy</I>,
-<I>fz</I>, and <I>q</I> are components of atom velocity and force and atomic
-charge.
+file for molecular systems.  <I>Proc</I> is the ID of the processor (0 to
+Nprocs-1) that currently owns the atom.  <I>Type</I> is the atom type.
+<I>Element</I> is typically the chemical name of an element, which you must
+assign to each type via the <A HREF = "dump_modify.html">dump_modify element</A>
+command.  More generally, it can be any string you wish to associated
+with an atom type.  <I>Mass</I> is the atom mass.  <I>Vx</I>, <I>vy</I>, <I>vz</I>, <I>fx</I>,
+<I>fy</I>, <I>fz</I>, and <I>q</I> are components of atom velocity and force and
+atomic charge.
 </P>
 <P>There are several options for outputting atom coordinates.  The <I>x</I>,
 <I>y</I>, <I>z</I> attributes write atom coordinates "unscaled", in the
 appropriate distance <A HREF = "units.html">units</A> (Angstroms, sigma, etc).  Use
 <I>xs</I>, <I>ys</I>, <I>zs</I> if you want the coordinates "scaled" to the box size,
 so that each value is 0.0 to 1.0.  If the simulation box is triclinic
 (tilted), then all atom coords will still be between 0.0 and 1.0.  Use
 <I>xu</I>, <I>yu</I>, <I>zu</I> if you want the coordinates "unwrapped" by the image
 flags for each atom.  Unwrapped means that if the atom has passed thru
 a periodic boundary one or more times, the value is printed for what
 the coordinate would be if it had not been wrapped back into the
 periodic box.  Note that using <I>xu</I>, <I>yu</I>, <I>zu</I> means that the
 coordinate values may be far outside the box bounds printed with the
 snapshot.  Using <I>xsu</I>, <I>ysu</I>, <I>zsu</I> is similar to using <I>xu</I>, <I>yu</I>, <I>zu</I>,
 except that the unwrapped coordinates are scaled by the box size. Atoms
 that have passed through a periodic boundary will have the corresponding
 cooordinate increased or decreased by 1.0.
 </P>
 <P>The image flags can be printed directly using the <I>ix</I>, <I>iy</I>, <I>iz</I>
 attributes.  For periodic dimensions, they specify which image of the
 simulation box the atom is considered to be in.  An image of 0 means
 it is inside the box as defined.  A value of 2 means add 2 box lengths
 to get the true value.  A value of -1 means subtract 1 box length to
 get the true value.  LAMMPS updates these flags as atoms cross
 periodic boundaries during the simulation.
 </P>
 <P>The <I>mux</I>, <I>muy</I>, <I>muz</I> attributes are specific to dipolar systems
 defined with an atom style of <I>dipole</I>.  They give the orientation of
 the atom's point dipole moment.  The <I>mu</I> attribute gives the
 magnitude of the atom's dipole moment.
 </P>
 <P>The <I>radius</I> and <I>diameter</I> attributes are specific to spherical
 particles that have a finite size, such as those defined with an atom
 style of <I>sphere</I>.
 </P>
 <P>The <I>omegax</I>, <I>omegay</I>, and <I>omegaz</I> attributes are specific to
 finite-size spherical particles that have an angular velocity.  Only
 certain atom styles, such as <I>sphere</I> define this quantity.
 </P>
 <P>The <I>angmomx</I>, <I>angmomy</I>, and <I>angmomz</I> attributes are specific to
 finite-size aspherical particles that have an angular momentum.  Only
 the <I>ellipsoid</I> atom style defines this quantity.
 </P>
 <P>The <I>tqx</I>, <I>tqy</I>, <I>tqz</I> attributes are for finite-size particles that
 can sustain a rotational torque due to interactions with other
 particles.
 </P>
 <P>The <I>c_ID</I> and <I>c_ID[N]</I> attributes allow per-atom vectors or arrays
 calculated by a <A HREF = "compute.html">compute</A> to be output.  The ID in the
 attribute should be replaced by the actual ID of the compute that has
 been defined previously in the input script.  See the
 <A HREF = "compute.html">compute</A> command for details.  There are computes for
 calculating the per-atom energy, stress, centro-symmetry parameter,
 and coordination number of individual atoms.
 </P>
 <P>Note that computes which calculate global or local quantities, as
 opposed to per-atom quantities, cannot be output in a dump custom
 command.  Instead, global quantities can be output by the
 <A HREF = "thermo_style.html">thermo_style custom</A> command, and local quantities
 can be output by the dump local command.
 </P>
 <P>If <I>c_ID</I> is used as a attribute, then the per-atom vector calculated
 by the compute is printed.  If <I>c_ID[N]</I> is used, then N must be in
 the range from 1-M, which will print the Nth column of the M-length
 per-atom array calculated by the compute.
 </P>
 <P>The <I>f_ID</I> and <I>f_ID[N]</I> attributes allow vector or array per-atom
 quantities calculated by a <A HREF = "fix.html">fix</A> to be output.  The ID in the
 attribute should be replaced by the actual ID of the fix that has been
 defined previously in the input script.  The <A HREF = "fix_ave_atom.html">fix
 ave/atom</A> command is one that calculates per-atom
 quantities.  Since it can time-average per-atom quantities produced by
 any <A HREF = "compute.html">compute</A>, <A HREF = "fix.html">fix</A>, or atom-style
 <A HREF = "variable.html">variable</A>, this allows those time-averaged results to
 be written to a dump file.
 </P>
 <P>If <I>f_ID</I> is used as a attribute, then the per-atom vector calculated
 by the fix is printed.  If <I>f_ID[N]</I> is used, then N must be in the
 range from 1-M, which will print the Nth column of the M-length
 per-atom array calculated by the fix.
 </P>
 <P>The <I>v_name</I> attribute allows per-atom vectors calculated by a
 <A HREF = "variable.html">variable</A> to be output.  The name in the attribute
 should be replaced by the actual name of the variable that has been
 defined previously in the input script.  Only an atom-style variable
 can be referenced, since it is the only style that generates per-atom
 values.  Variables of style <I>atom</I> can reference individual atom
 attributes, per-atom atom attributes, thermodynamic keywords, or
 invoke other computes, fixes, or variables when they are evaluated, so
 this is a very general means of creating quantities to output to a
 dump file.
 </P>
 <P>See <A HREF = "Section_modify.html">Section_modify</A> of the manual for information
 on how to add new compute and fix styles to LAMMPS to calculate
 per-atom quantities which could then be output into dump files.
 </P>
 <HR>
 
 <P><B>Restrictions:</B>
 </P>
 <P>To write gzipped dump files, you must compile LAMMPS with the
 -DLAMMPS_GZIP option - see the <A HREF = "Section_start.html#start_2">Making
 LAMMPS</A> section of the documentation.
 </P>
 <P>The <I>atom/mpiio</I>, <I>custom/mpiio</I>, and <I>xyz/mpiio</I> styles are part of
 the MPIIO package.  They are only enabled if LAMMPS was built with
 that package.  See the <A HREF = "Section_start.html#start_3">Making LAMMPS</A>
 section for more info.
 </P>
 <P>The <I>xtc</I> style is part of the XTC package.  It is only enabled if
 LAMMPS was built with that package.  See the <A HREF = "Section_start.html#start_3">Making
 LAMMPS</A> section for more info.  This is
 because some machines may not support the low-level XDR data format
 that XTC files are written with, which will result in a compile-time
 error when a low-level include file is not found.  Putting this style
 in a package makes it easy to exclude from a LAMMPS build for those
 machines.  However, the XTC package also includes two compatibility
 header files and associated functions, which should be a suitable
 substitute on machines that do not have the appropriate native header
 files.  This option can be invoked at build time by adding
 -DLAMMPS_XDR to the CCFLAGS variable in the appropriate low-level
 Makefile, e.g. src/MAKE/Makefile.foo.  This compatibility mode has
 been tested successfully on Cray XT3/XT4/XT5 and IBM BlueGene/L
 machines and should also work on IBM BG/P, and Windows XP/Vista/7
 machines.
 </P>
 <P><B>Related commands:</B>
 </P>
 <P><A HREF = "dump_image.html">dump image</A>, <A HREF = "dump_modify.html">dump_modify</A>,
 <A HREF = "undump.html">undump</A>
 </P>
 <P><B>Default:</B>
 </P>
 <P>The defaults for the image style are listed on the <A HREF = "dump_image.html">dump
 image</A> doc page.
 </P>
 </HTML>
diff --git a/doc/dump.txt b/doc/dump.txt
index 5d1be5881..3f83f217c 100644
--- a/doc/dump.txt
+++ b/doc/dump.txt
@@ -1,592 +1,600 @@
  "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
    
 dump command :h3
 "dump image"_dump_image.html command :h3
 "dump movie"_dump_image.html command :h3
 "dump molfile"_dump_molfile.html command :h3
 
 [Syntax:]
 
 dump ID group-ID style N file args :pre
 
 ID = user-assigned name for the dump :ulb,l
 group-ID = ID of the group of atoms to be dumped :l
 style = {atom} or {atom/mpiio} or {cfg} or {dcd} or {xtc} or {xyz} or {xyz/mpiio} or {image} or {movie} or {molfile} or {local} or {custom} or {custom/mpiio} :l
 N = dump every this many timesteps :l
 file = name of file to write dump info to :l
 args = list of arguments for a particular style :l
   {atom} args = none
   {atom/mpiio} args = none
   {cfg} args = same as {custom} args, see below
   {dcd} args = none
   {xtc} args = none
   {xyz} args = none :pre
   {xyz/mpiio} args = none :pre
 
   {image} args = discussed on "dump image"_dump_image.html doc page :pre
 
   {movie} args = discussed on "dump image"_dump_image.html doc page :pre
 
   {molfile} args = discussed on "dump molfile"_dump_molfile.html doc page :pre
 
   {local} args = list of local attributes
     possible attributes = index, c_ID, c_ID\[N\], f_ID, f_ID\[N\]
       index = enumeration of local values
       c_ID = local vector calculated by a compute with ID
       c_ID\[N\] = Nth column of local array calculated by a compute with ID
       f_ID = local vector calculated by a fix with ID
       f_ID\[N\] = Nth column of local array calculated by a fix with ID :pre
 
   {custom} of {custom/mpiio} args = list of atom attributes
     possible attributes = id, mol, type, element, mass,
 			  x, y, z, xs, ys, zs, xu, yu, zu, 
 			  xsu, ysu, zsu, ix, iy, iz,
 			  vx, vy, vz, fx, fy, fz,
                           q, mux, muy, muz, mu,
                           radius, diameter, omegax, omegay, omegaz,
 			  angmomx, angmomy, angmomz, tqx, tqy, tqz,
 			  c_ID, c_ID\[N\], f_ID, f_ID\[N\], v_name :pre
 
       id = atom ID
       mol = molecule ID
+      proc = ID of processor that owns atom
       type = atom type
       element = name of atom element, as defined by "dump_modify"_dump_modify.html command
       mass = atom mass
       x,y,z = unscaled atom coordinates
       xs,ys,zs = scaled atom coordinates
       xu,yu,zu = unwrapped atom coordinates
       xsu,ysu,zsu = scaled unwrapped atom coordinates
       ix,iy,iz = box image that the atom is in
       vx,vy,vz = atom velocities
       fx,fy,fz = forces on atoms
       q = atom charge
       mux,muy,muz = orientation of dipole moment of atom
       mu = magnitude of dipole moment of atom
       radius,diameter = radius,diameter of spherical particle
       omegax,omegay,omegaz = angular velocity of spherical particle
       angmomx,angmomy,angmomz = angular momentum of aspherical particle
       tqx,tqy,tqz = torque on finite-size particles
       c_ID = per-atom vector calculated by a compute with ID
       c_ID\[N\] = Nth column of per-atom array calculated by a compute with ID
       f_ID = per-atom vector calculated by a fix with ID
       f_ID\[N\] = Nth column of per-atom array calculated by a fix with ID
-      v_name = per-atom vector calculated by an atom-style variable with name :pre
+      v_name = per-atom vector calculated by an atom-style variable with name
+      d_name = per-atom floating point vector with name managed by fix property/atom
+      i_name = per-atom integer vector with name managed by fix property/atom :pre
 :ule
 
 [Examples:]
 
 dump myDump all atom 100 dump.atom
 dump myDump all atom/mpiio 100 dump.atom.mpiio
 dump 2 subgroup atom 50 dump.run.bin
 dump 2 subgroup atom 50 dump.run.mpiio.bin
 dump 4a all custom 100 dump.myforce.* id type x y vx fx
 dump 4b flow custom 100 dump.%.myforce id type c_myF\[3\] v_ke
 dump 2 inner cfg 10 dump.snap.*.cfg mass type xs ys zs vx vy vz
 dump snap all cfg 100 dump.config.*.cfg mass type xs ys zs id type c_Stress\[2\]
 dump 1 all xtc 1000 file.xtc :pre
 
 [Description:]
 
 Dump a snapshot of atom quantities to one or more files every N
 timesteps in one of several styles.  The {image} and {movie} styles are
 the exception: the {image} style renders a JPG, PNG, or PPM image file
 of the atom configuration every N timesteps while the {movie} style
 combines and compresses them into a movie file; both are discussed in
 detail on the "dump image"_dump_image.html doc page.  The timesteps on
 which dump output is written can also be controlled by a variable.
 See the "dump_modify every"_dump_modify.html command.
 
 Only information for atoms in the specified group is dumped.  The
 "dump_modify thresh and region"_dump_modify.html commands can also
 alter what atoms are included.  Not all styles support all these
 options; see details below.
 
 As described below, the filename determines the kind of output (text
 or binary or gzipped, one big file or one per timestep, one big file
 or one per processor).
 
 IMPORTANT NOTE: Because periodic boundary conditions are enforced only
 on timesteps when neighbor lists are rebuilt, the coordinates of an
 atom written to a dump file may be slightly outside the simulation
 box.
 
 IMPORTANT NOTE: Unless the "dump_modify sort"_dump_modify.html option
 is invoked, the lines of atom information written to dump files
 (typically one line per atom) will be in an indeterminate order for
 each snapshot.  This is even true when running on a single processor,
 if the "atom_modify sort"_atom_modify.html option is on, which it is
 by default.  In this case atoms are re-ordered periodically during a
 simulation, due to spatial sorting.  It is also true when running in
 parallel, because data for a single snapshot is collected from
 multiple processors.
 
 For the {atom}, {custom}, {cfg}, and {local} styles, sorting is off by
 default.  For the {dcd}, {xtc}, {xyz}, and {molfile} styles, sorting by
 atom ID is on by default. See the "dump_modify"_dump_modify.html doc
 page for details.
 
 As explained below, the {atom/mpiio}, {custom/mpiio}, and {xyz/mpiio}
 styles are identical in command syntax and in the format of the dump
 files they create, to the corresponding styles without "mpiio", except
 the single dump file they produce is written in parallel via the
 MPI-IO library.  For the remainder of this doc page, you should thus
 consider the {atom} and {atom/mpiio} styles (etc) to be
 inter-changeable.  The one exception is how the filename is specified
 for the MPI-IO styles, as explained below.
 
 :line
 
 The {style} keyword determines what atom quantities are written to the
 file and in what format.  Settings made via the
 "dump_modify"_dump_modify.html command can also alter the format of
 individual values and the file itself.
 
 The {atom}, {local}, and {custom} styles create files in a simple text
 format that is self-explanatory when viewing a dump file.  Many of the
 LAMMPS "post-processing tools"_Section_tools.html, including
 "Pizza.py"_http://www.sandia.gov/~sjplimp/pizza.html, work with this
 format, as does the "rerun"_rerun.html command.
 
 For post-processing purposes the {atom}, {local}, and {custom} text
 files are self-describing in the following sense.
 
 The dimensions of the simulation box are included in each snapshot.
 For an orthogonal simulation box this information is is formatted as:
 
 ITEM: BOX BOUNDS xx yy zz
 xlo xhi
 ylo yhi
 zlo zhi :pre
 
 where xlo,xhi are the maximum extents of the simulation box in the
 x-dimension, and similarly for y and z.  The "xx yy zz" represent 6
 characters that encode the style of boundary for each of the 6
 simulation box boundaries (xlo,xhi and ylo,yhi and zlo,zhi).  Each of
 the 6 characters is either p = periodic, f = fixed, s = shrink wrap,
 or m = shrink wrapped with a minimum value.  See the
 "boundary"_boundary.html command for details.
 
 For triclinic simulation boxes (non-orthogonal), an orthogonal
 bounding box which encloses the triclinic simulation box is output,
 along with the 3 tilt factors (xy, xz, yz) of the triclinic box,
 formatted as follows:
 
 ITEM: BOX BOUNDS xy xz yz xx yy zz 
 xlo_bound xhi_bound xy
 ylo_bound yhi_bound xz
 zlo_bound zhi_bound yz :pre
 
 The presence of the text "xy xz yz" in the ITEM line indicates that
 the 3 tilt factors will be included on each of the 3 following lines.
 This bounding box is convenient for many visualization programs.  The
 meaning of the 6 character flags for "xx yy zz" is the same as above.
 
 Note that the first two numbers on each line are now xlo_bound instead
 of xlo, etc, since they repesent a bounding box.  See "this
 section"_Section_howto.html#howto_12 of the doc pages for a geometric
 description of triclinic boxes, as defined by LAMMPS, simple formulas
 for how the 6 bounding box extents (xlo_bound,xhi_bound,etc) are
 calculated from the triclinic parameters, and how to transform those
 parameters to and from other commonly used triclinic representations.
 
 The "ITEM: ATOMS" line in each snapshot lists column descriptors for
 the per-atom lines that follow.  For example, the descriptors would be
 "id type xs ys zs" for the default {atom} style, and would be the atom
 attributes you specify in the dump command for the {custom} style.
 
 For style {atom}, atom coordinates are written to the file, along with
 the atom ID and atom type.  By default, atom coords are written in a
 scaled format (from 0 to 1).  I.e. an x value of 0.25 means the atom
 is at a location 1/4 of the distance from xlo to xhi of the box
 boundaries.  The format can be changed to unscaled coords via the
 "dump_modify"_dump_modify.html settings.  Image flags can also be
 added for each atom via dump_modify.
 
 Style {custom} allows you to specify a list of atom attributes to be
 written to the dump file for each atom.  Possible attributes are
 listed above and will appear in the order specified.  You cannot
 specify a quantity that is not defined for a particular simulation -
 such as {q} for atom style {bond}, since that atom style doesn't
 assign charges.  Dumps occur at the very end of a timestep, so atom
 attributes will include effects due to fixes that are applied during
 the timestep.  An explanation of the possible dump custom attributes
 is given below.
 
 For style {local}, local output generated by "computes"_compute.html
 and "fixes"_fix.html is used to generate lines of output that is
 written to the dump file.  This local data is typically calculated by
 each processor based on the atoms it owns, but there may be zero or
 more entities per atom, e.g. a list of bond distances.  An explanation
 of the possible dump local attributes is given below.  Note that by
 using input from the "compute
 property/local"_compute_property_local.html command with dump local,
 it is possible to generate information on bonds, angles, etc that can
 be cut and pasted directly into a data file read by the
 "read_data"_read_data.html command.
 
 Style {cfg} has the same command syntax as style {custom} and writes
 extended CFG format files, as used by the
 "AtomEye"_http://mt.seas.upenn.edu/Archive/Graphics/A visualization
 package.  Since the extended CFG format uses a single snapshot of the
 system per file, a wildcard "*" must be included in the filename, as
 discussed below.  The list of atom attributes for style {cfg} must
 begin with either "mass type xs ys zs" or "mass type xsu ysu zsu"
 since these quantities are needed to write the CFG files in the
 appropriate format (though the "mass" and "type" fields do not appear
 explicitly in the file).  Any remaining attributes will be stored as
 "auxiliary properties" in the CFG files.  Note that you will typically
 want to use the "dump_modify element"_dump_modify.html command with
 CFG-formatted files, to associate element names with atom types, so
 that AtomEye can render atoms appropriately. When unwrapped
 coordinates {xsu}, {ysu}, and {zsu} are requested, the nominal AtomEye
 periodic cell dimensions are expanded by a large factor UNWRAPEXPAND =
 10.0, which ensures atoms that are displayed correctly for up to
 UNWRAPEXPAND/2 periodic boundary crossings in any direction.  Beyond
 this, AtomEye will rewrap the unwrapped coordinates.  The expansion
 causes the atoms to be drawn farther away from the viewer, but it is
 easy to zoom the atoms closer, and the interatomic distances are
 unaffected.
 
 The {dcd} style writes DCD files, a standard atomic trajectory format
 used by the CHARMM, NAMD, and XPlor molecular dynamics packages.  DCD
 files are binary and thus may not be portable to different machines.
 The number of atoms per snapshot cannot change with the {dcd} style.
 The {unwrap} option of the "dump_modify"_dump_modify.html command
 allows DCD coordinates to be written "unwrapped" by the image flags
 for each atom.  Unwrapped means that if the atom has passed through
 a periodic boundary one or more times, the value is printed for what
 the coordinate would be if it had not been wrapped back into the
 periodic box.  Note that these coordinates may thus be far outside
 the box size stored with the snapshot.
 
 The {xtc} style writes XTC files, a compressed trajectory format used
 by the GROMACS molecular dynamics package, and described
 "here"_http://manual.gromacs.org/current/online/xtc.html.
 The precision used in XTC files can be adjusted via the
 "dump_modify"_dump_modify.html command.  The default value of 1000
 means that coordinates are stored to 1/1000 nanometer accuracy.  XTC
 files are portable binary files written in the NFS XDR data format, 
 so that any machine which supports XDR should be able to read them. 
 The number of atoms per snapshot cannot change with the {xtc} style.
 The {unwrap} option of the "dump_modify"_dump_modify.html command allows
 XTC coordinates to be written "unwrapped" by the image flags for each
 atom.  Unwrapped means that if the atom has passed thru a periodic
 boundary one or more times, the value is printed for what the
 coordinate would be if it had not been wrapped back into the periodic
 box.  Note that these coordinates may thus be far outside the box size
 stored with the snapshot.
 
 The {xyz} style writes XYZ files, which is a simple text-based
 coordinate format that many codes can read. Specifically it has
 a line with the number of atoms, then a comment line that is
 usually ignored followed by one line per atom with the atom type
 and the x-, y-, and z-coordinate of that atom. You can use the
 "dump_modify element"_dump_modify.html option to change the output
 from using the (numerical) atom type to an element name (or some
 other label). This will help many visualization programs to guess
 bonds and colors.
 
 Note that {atom}, {custom}, {dcd}, {xtc}, and {xyz} style dump files can
 be read directly by "VMD"_http://www.ks.uiuc.edu/Research/vmd (a popular
 molecular viewing program).  See "Section tools"_Section_tools.html#vmd
 of the manual and the tools/lmp2vmd/README.txt file for more information
 about support in VMD for reading and visualizing LAMMPS dump files.
 
 :line
 
 Dumps are performed on timesteps that are a multiple of N (including
 timestep 0) and on the last timestep of a minimization if the
 minimization converges.  Note that this means a dump will not be
 performed on the initial timestep after the dump command is invoked,
 if the current timestep is not a multiple of N.  This behavior can be
 changed via the "dump_modify first"_dump_modify.html command, which
 can also be useful if the dump command is invoked after a minimization
 ended on an arbitrary timestep.  N can be changed between runs by
 using the "dump_modify every"_dump_modify.html command (not allowed
 for {dcd} style).  The "dump_modify every"_dump_modify.html command
 also allows a variable to be used to determine the sequence of
 timesteps on which dump files are written.  In this mode a dump on the
 first timestep of a run will also not be written unless the
 "dump_modify first"_dump_modify.html command is used.
 
 The specified filename determines how the dump file(s) is written.
 The default is to write one large text file, which is opened when the
 dump command is invoked and closed when an "undump"_undump.html
 command is used or when LAMMPS exits.  For the {dcd} and {xtc} styles,
 this is a single large binary file.
 
 Dump filenames can contain two wildcard characters.  If a "*"
 character appears in the filename, then one file per snapshot is
 written and the "*" character is replaced with the timestep value.
 For example, tmp.dump.* becomes tmp.dump.0, tmp.dump.10000,
 tmp.dump.20000, etc.  This option is not available for the {dcd} and
 {xtc} styles.  Note that the "dump_modify pad"_dump_modify.html
 command can be used to insure all timestep numbers are the same length
 (e.g. 00010), which can make it easier to read a series of dump files
 in order by some post-processing tools.
 
 If a "%" character appears in the filename, then each of P processors
 writes a portion of the dump file, and the "%" character is replaced
 with the processor ID from 0 to P-1.  For example, tmp.dump.% becomes
 tmp.dump.0, tmp.dump.1, ... tmp.dump.P-1, etc.  This creates smaller
 files and can be a fast mode of output on parallel machines that
 support parallel I/O for output. This option is not available for the
 {dcd}, {xtc}, and {xyz} styles.
 
 By default, P = the number of processors meaning one file per
 processor, but P can be set to a smaller value via the {nfile} or
 {fileper} keywords of the "dump_modify"_dump_modify.html command.
 These options can be the most efficient way of writing out dump files
 when running on large numbers of processors.
 
 Note that using the "*" and "%" characters together can produce a
 large number of small dump files!
 
 For the {atom/mpiio}, {custom/mpiio}, and {xyz/mpiio} styles, a single
 dump file is written in parallel via the MPI-IO library, which is part
 of the MPI standard for versions 2.0 and above.  Using MPI-IO requires
 two steps.  First, build LAMMPS with its MPIIO package installed, e.g.
 
 make yes-mpiio    # installs the MPIIO package
 make g++          # build LAMMPS for your platform :pre
 
 Second, use a dump filename which contains ".mpiio".  Note that it
 does not have to end in ".mpiio", just contain those characters.
 Unlike MPI-IO restart files, which must be both written and read using
 MPI-IO, the dump files produced by these MPI-IO styles are identical
 in format to the files produced by their non-MPI-IO style
 counterparts.  This means you can write a dump file using MPI-IO and
 use the "read_dump"_read_dump.html command or perform other
 post-processing, just as if the dump file was not written using
 MPI-IO.
 
 Note that MPI-IO dump files are one large file which all processors
 write to.  You thus cannot use the "%" wildcard character described
 above in the filename since that specifies generation of multiple
 files.  You can use the ".bin" suffix described below in an MPI-IO
 dump file; again this file will be written in parallel and have the
 same binary format as if it were written without MPI-IO.
 
 If the filename ends with ".bin", the dump file (or files, if "*" or
 "%" is also used) is written in binary format.  A binary dump file
 will be about the same size as a text version, but will typically
 write out much faster.  Of course, when post-processing, you will need
 to convert it back to text format (see the "binary2txt
 tool"_Section_tools.html#binary) or write your own code to read the
 binary file.  The format of the binary file can be understood by
 looking at the tools/binary2txt.cpp file.  This option is only
 available for the {atom} and {custom} styles.
 
 If the filename ends with ".gz", the dump file (or files, if "*" or "%"
 is also used) is written in gzipped format.  A gzipped dump file will
 be about 3x smaller than the text version, but will also take longer
 to write.  This option is not available for the {dcd} and {xtc}
 styles.
 
 :line
 
 This section explains the local attributes that can be specified as
 part of the {local} style.
 
 The {index} attribute can be used to generate an index number from 1
 to N for each line written into the dump file, where N is the total
 number of local datums from all processors, or lines of output that
 will appear in the snapshot.  Note that because data from different
 processors depend on what atoms they currently own, and atoms migrate
 between processor, there is no guarantee that the same index will be
 used for the same info (e.g. a particular bond) in successive
 snapshots.
 
 The {c_ID} and {c_ID\[N\]} attributes allow local vectors or arrays
 calculated by a "compute"_compute.html to be output.  The ID in the
 attribute should be replaced by the actual ID of the compute that has
 been defined previously in the input script.  See the
 "compute"_compute.html command for details.  There are computes for
 calculating local information such as indices, types, and energies for
 bonds and angles.
 
 Note that computes which calculate global or per-atom quantities, as
 opposed to local quantities, cannot be output in a dump local command.
 Instead, global quantities can be output by the "thermo_style
 custom"_thermo_style.html command, and per-atom quantities can be
 output by the dump custom command.
 
 If {c_ID} is used as a attribute, then the local vector calculated by
 the compute is printed.  If {c_ID\[N\]} is used, then N must be in the
 range from 1-M, which will print the Nth column of the M-length local
 array calculated by the compute.
 
 The {f_ID} and {f_ID\[N\]} attributes allow local vectors or arrays
 calculated by a "fix"_fix.html to be output.  The ID in the attribute
 should be replaced by the actual ID of the fix that has been defined
 previously in the input script.
 
 If {f_ID} is used as a attribute, then the local vector calculated by
 the fix is printed.  If {f_ID\[N\]} is used, then N must be in the
 range from 1-M, which will print the Nth column of the M-length local
 array calculated by the fix.
 
 Here is an example of how to dump bond info for a system,
 including the distance and energy of each bond:
 
 compute 1 all property/local batom1 batom2 btype 
 compute 2 all bond/local dist eng
 dump 1 all local 1000 tmp.dump index c_1\[1\] c_1\[2\] c_1\[3\] c_2\[1\] c_2\[2\] :pre
 
 :line
 
 This section explains the atom attributes that can be specified as
 part of the {custom} and {cfg} styles.
 
-The {id}, {mol}, {type}, {element}, {mass}, {vx}, {vy}, {vz}, {fx}, {fy},
-{fz}, {q} attributes are self-explanatory.
+The {id}, {mol}, {proc}, {type}, {element}, {mass}, {vx}, {vy}, {vz},
+{fx}, {fy}, {fz}, {q} attributes are self-explanatory.
 
 {Id} is the atom ID.  {Mol} is the molecule ID, included in the data
-file for molecular systems.  {Type} is the atom type.  {Element} is
-typically the chemical name of an element, which you must assign to
-each type via the "dump_modify element"_dump_modify.html command.
-More generally, it can be any string you wish to associated with an
-atom type.  {Mass} is the atom mass.  {Vx}, {vy}, {vz}, {fx}, {fy},
-{fz}, and {q} are components of atom velocity and force and atomic
-charge.
+file for molecular systems.  {Proc} is the ID of the processor (0 to
+Nprocs-1) that currently owns the atom.  {Type} is the atom type.
+{Element} is typically the chemical name of an element, which you must
+assign to each type via the "dump_modify element"_dump_modify.html
+command.  More generally, it can be any string you wish to associated
+with an atom type.  {Mass} is the atom mass.  {Vx}, {vy}, {vz}, {fx},
+{fy}, {fz}, and {q} are components of atom velocity and force and
+atomic charge.
 
 There are several options for outputting atom coordinates.  The {x},
 {y}, {z} attributes write atom coordinates "unscaled", in the
 appropriate distance "units"_units.html (Angstroms, sigma, etc).  Use
 {xs}, {ys}, {zs} if you want the coordinates "scaled" to the box size,
 so that each value is 0.0 to 1.0.  If the simulation box is triclinic
 (tilted), then all atom coords will still be between 0.0 and 1.0.  Use
 {xu}, {yu}, {zu} if you want the coordinates "unwrapped" by the image
 flags for each atom.  Unwrapped means that if the atom has passed thru
 a periodic boundary one or more times, the value is printed for what
 the coordinate would be if it had not been wrapped back into the
 periodic box.  Note that using {xu}, {yu}, {zu} means that the
 coordinate values may be far outside the box bounds printed with the
 snapshot.  Using {xsu}, {ysu}, {zsu} is similar to using {xu}, {yu}, {zu},
 except that the unwrapped coordinates are scaled by the box size. Atoms
 that have passed through a periodic boundary will have the corresponding
 cooordinate increased or decreased by 1.0.
 
 The image flags can be printed directly using the {ix}, {iy}, {iz}
 attributes.  For periodic dimensions, they specify which image of the
 simulation box the atom is considered to be in.  An image of 0 means
 it is inside the box as defined.  A value of 2 means add 2 box lengths
 to get the true value.  A value of -1 means subtract 1 box length to
 get the true value.  LAMMPS updates these flags as atoms cross
 periodic boundaries during the simulation.
 
 The {mux}, {muy}, {muz} attributes are specific to dipolar systems
 defined with an atom style of {dipole}.  They give the orientation of
 the atom's point dipole moment.  The {mu} attribute gives the
 magnitude of the atom's dipole moment.
 
 The {radius} and {diameter} attributes are specific to spherical
 particles that have a finite size, such as those defined with an atom
 style of {sphere}.
 
 The {omegax}, {omegay}, and {omegaz} attributes are specific to
 finite-size spherical particles that have an angular velocity.  Only
 certain atom styles, such as {sphere} define this quantity.
 
 The {angmomx}, {angmomy}, and {angmomz} attributes are specific to
 finite-size aspherical particles that have an angular momentum.  Only
 the {ellipsoid} atom style defines this quantity.
 
 The {tqx}, {tqy}, {tqz} attributes are for finite-size particles that
 can sustain a rotational torque due to interactions with other
 particles.
 
 The {c_ID} and {c_ID\[N\]} attributes allow per-atom vectors or arrays
 calculated by a "compute"_compute.html to be output.  The ID in the
 attribute should be replaced by the actual ID of the compute that has
 been defined previously in the input script.  See the
 "compute"_compute.html command for details.  There are computes for
 calculating the per-atom energy, stress, centro-symmetry parameter,
 and coordination number of individual atoms.
 
 Note that computes which calculate global or local quantities, as
 opposed to per-atom quantities, cannot be output in a dump custom
 command.  Instead, global quantities can be output by the
 "thermo_style custom"_thermo_style.html command, and local quantities
 can be output by the dump local command.
 
 If {c_ID} is used as a attribute, then the per-atom vector calculated
 by the compute is printed.  If {c_ID\[N\]} is used, then N must be in
 the range from 1-M, which will print the Nth column of the M-length
 per-atom array calculated by the compute.
 
 The {f_ID} and {f_ID\[N\]} attributes allow vector or array per-atom
 quantities calculated by a "fix"_fix.html to be output.  The ID in the
 attribute should be replaced by the actual ID of the fix that has been
 defined previously in the input script.  The "fix
 ave/atom"_fix_ave_atom.html command is one that calculates per-atom
 quantities.  Since it can time-average per-atom quantities produced by
 any "compute"_compute.html, "fix"_fix.html, or atom-style
 "variable"_variable.html, this allows those time-averaged results to
 be written to a dump file.
 
 If {f_ID} is used as a attribute, then the per-atom vector calculated
 by the fix is printed.  If {f_ID\[N\]} is used, then N must be in the
 range from 1-M, which will print the Nth column of the M-length
 per-atom array calculated by the fix.
 
 The {v_name} attribute allows per-atom vectors calculated by a
 "variable"_variable.html to be output.  The name in the attribute
 should be replaced by the actual name of the variable that has been
 defined previously in the input script.  Only an atom-style variable
 can be referenced, since it is the only style that generates per-atom
 values.  Variables of style {atom} can reference individual atom
 attributes, per-atom atom attributes, thermodynamic keywords, or
 invoke other computes, fixes, or variables when they are evaluated, so
 this is a very general means of creating quantities to output to a
 dump file.
 
+The {d_name} and {i_name} attributes allow to output custom per atom
+floating point or integer properties that are managed by
+"fix property/atom"_fix_property_atom.html.
+
 See "Section_modify"_Section_modify.html of the manual for information
 on how to add new compute and fix styles to LAMMPS to calculate
 per-atom quantities which could then be output into dump files.
 
 :line
 
 [Restrictions:]
 
 To write gzipped dump files, you must compile LAMMPS with the
 -DLAMMPS_GZIP option - see the "Making
 LAMMPS"_Section_start.html#start_2 section of the documentation.
 
 The {atom/mpiio}, {custom/mpiio}, and {xyz/mpiio} styles are part of
 the MPIIO 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.
 
 The {xtc} style is part of the XTC 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.  This is
 because some machines may not support the low-level XDR data format
 that XTC files are written with, which will result in a compile-time
 error when a low-level include file is not found.  Putting this style
 in a package makes it easy to exclude from a LAMMPS build for those
 machines.  However, the XTC package also includes two compatibility
 header files and associated functions, which should be a suitable
 substitute on machines that do not have the appropriate native header
 files.  This option can be invoked at build time by adding
 -DLAMMPS_XDR to the CCFLAGS variable in the appropriate low-level
 Makefile, e.g. src/MAKE/Makefile.foo.  This compatibility mode has
 been tested successfully on Cray XT3/XT4/XT5 and IBM BlueGene/L
 machines and should also work on IBM BG/P, and Windows XP/Vista/7
 machines.
 
 [Related commands:]
 
 "dump image"_dump_image.html, "dump_modify"_dump_modify.html,
 "undump"_undump.html
 
 [Default:]
 
 The defaults for the {image} and {movie} styles are listed on the
 "dump image"_dump_image.html doc page.
diff --git a/doc/fix_store_state.txt b/doc/fix_store_state.txt
index bc3e9d2d8..e40a0f49c 100644
--- a/doc/fix_store_state.txt
+++ b/doc/fix_store_state.txt
@@ -1,127 +1,131 @@
 "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 store/state command :h3
 
 [Syntax:]
 
 fix ID group-ID store/state N input1 input2 ... keyword value ... :pre
 
 ID, group-ID are documented in "fix"_fix.html command :ulb,l
 store/state = style name of this fix command :l
 N = store atom attributes every N steps, N = 0 for initial store only :l
 input = one or more atom attributes :l
   possible attributes = id, mol, type, mass,
  	                x, y, z, xs, ys, zs, xu, yu, zu, ix, iy, iz,
 		        vx, vy, vz, fx, fy, fz,
                         q, mux, muy, muz,
                         radius, omegax, omegay, omegaz,
-                        angmomx, angmomy, angmomz, tqx, tqy, tqz
-			c_ID, c_ID\[N\], f_ID, f_ID\[N\], v_name :pre
+                        angmomx, angmomy, angmomz, tqx, tqy, tqz,
+                        c_ID, c_ID\[N\], f_ID, f_ID\[N\], v_name,
+                        d_name, i_name :pre
 
       id = atom ID
       mol = molecule ID
       type = atom type
       mass = atom mass
       x,y,z = unscaled atom coordinates
       xs,ys,zs = scaled atom coordinates
       xu,yu,zu = unwrapped atom coordinates
       ix,iy,iz = box image that the atom is in
       vx,vy,vz = atom velocities
       fx,fy,fz = forces on atoms
       q = atom charge
       mux,muy,muz = orientation of dipolar atom
       radius = radius of spherical particle
       omegax,omegay,omegaz = angular velocity of spherical particle
       angmomx,angmomy,angmomz = angular momentum of aspherical particle
       tqx,tqy,tqz = torque on finite-size particles
       c_ID = per-atom vector calculated by a compute with ID
       c_ID\[I\] = Ith column of per-atom array calculated by a compute with ID
       f_ID = per-atom vector calculated by a fix with ID
       f_ID\[I\] = Ith column of per-atom array calculated by a fix with ID
-      v_name = per-atom vector calculated by an atom-style variable with name :pre
+      v_name = per-atom vector calculated by an atom-style variable with name
+      d_name = per-atom floating point vector managed by fix property/atom
+      i_name = per-atom integer vector managed by fix property/atom :pre
 
 zero or more keyword/value pairs may be appended :l
 keyword = {com} :l
   {com} value = {yes} or {no} :pre
 :ule
 
 [Examples:]
 
 fix 1 all store/state 0 x y z
 fix 1 all store/state 0 xu yu zu com yes
 fix 2 all store/state 1000 vx vy vz :pre
 
 [Description:]
 
 Define a fix that stores attributes for each atom in the group at the
 time the fix is defined.  If {N} is 0, then the values are never
 updated, so this is a way of archiving an atom attribute at a given
 time for future use in a calculation or output.  See the discussion of
 "output commands"_Section_howto.html#howto_15 that take fixes as
 inputs.  And see for example, the "compute
 reduce"_compute_reduce.html, "fix ave/atom"_fix_ave_atom.html, "fix
 ave/histo"_fix_ave_histo.html, "fix ave/spatial"_fix_ave_spatial.html,
 and "atom-style variable"_variable.html commands.
 
 If {N} is not zero, then the attributes will be updated every {N}
 steps.
 
 IMPORTANT NOTE: Actually, only atom attributes specified by keywords
 like {xu} or {vy} are initially stored immediately at the point in
 your input script when the fix is defined.  Attributes specified by a
 compute, fix, or variable are not initially stored until the first run
 following the fix definition begins.  This is because calculating
 those attributes may require quantities that are not defined in
 between runs.
 
 The list of possible attributes is the same as that used by the "dump
 custom"_dump.html command, which describes their meaning.
 
 If the {com} keyword is set to {yes} then the {xu}, {yu}, and {zu}
 inputs store the position of each atom relative to the center-of-mass
 of the group of atoms, instead of storing the absolute position.  This
 option is used by the "compute msd"_compute_msd.html command.
 
 The requested values are stored in a per-atom vector or array as
 discussed below.  Zeroes are stored for atoms not in the specified
 group.
 
 [Restart, fix_modify, output, run start/stop, minimize info:]
 
 This fix writes the per-atom values it stores to "binary restart
 files"_restart.html, so that the values can be restored when a
 simulation is restarted.  See the "read_restart"_read_restart.html
 command for info on how to re-specify a fix in an input script that
 reads a restart file, so that the operation of the fix continues in an
 uninterrupted fashion.
 
 None of the "fix_modify"_fix_modify.html options are relevant to this
 fix.
 
 If a single input is specified, this fix produces a per-atom vector.
 If multiple inputs are specified, a per-atom array is produced where
 the number of columns for each atom is the number of inputs.  These
 can be accessed by various "output
 commands"_Section_howto.html#howto_15.  The per-atom values be
 accessed on any timestep.
 
 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:] none
 
 [Related commands:]
 
 "dump custom"_dump.html, "compute
-property/atom"_compute_property_atom.html, "variable"_variable.html
+property/atom"_compute_property_atom.html,
+"fix property/atom"_fix_property_atom.html, "variable"_variable.html
 
 [Default:]
 
 The option default is com = no.
diff --git a/doc/molecule.html b/doc/molecule.html
index 270a3742d..46c1cf346 100644
--- a/doc/molecule.html
+++ b/doc/molecule.html
@@ -1,399 +1,405 @@
 <HTML>
 <CENTER><A HREF = "http://lammps.sandia.gov">LAMMPS WWW Site</A> - <A HREF = "Manual.html">LAMMPS Documentation</A> - <A HREF = "Section_commands.html#comm">LAMMPS Commands</A> 
 </CENTER>
 
 
 
 
 
 
 <HR>
 
 <H3>molecule command 
 </H3>
 <P><B>Syntax:</B>
 </P>
 <PRE>molecule ID file1 file2 ... 
 </PRE>
 <UL><LI>ID = user-assigned name for the molecule template
 <LI>file1,file2,... = names of files containing molecule descriptions 
 </UL>
 <P><B>Examples:</B>
 </P>
 <PRE>molecule 1 mymol
 molecule 1 co2.txt h2o.txt
 molecule CO2 co2.txt 
 </PRE>
 <P><B>Description:</B>
 </P>
 <P>Define a molecule template that can be used as part of other LAMMPS
 commands, typically to define a collection of particles as a bonded
 molecule or a rigid body.  Commands that currently use molecule
-templates (or will in the future) include:
+templates include:
 </P>
 <UL><LI><A HREF = "fix_deposit.html">fix deposit</A>
 <LI><A HREF = "fix_pour.html">fix pour</A>
 <LI><A HREF = "fix_rigid.html">fix rigid/small</A>
 <LI><A HREF = "fix_shake.html">fix shake</A>
 <LI><A HREF = "fix_gcmc.html">fix gcmc</A>
 <LI><A HREF = "create_atoms.html">create_atoms</A> 
 <LI><A HREF = "atom_style.html">atom_style template</A> 
 </UL>
 <P>The ID of a molecule template can only contain alphanumeric characters
 and underscores.
 </P>
 <P>A single template can contain multiple molecules, listed one per file.
 Many of the commands listed above currently use only the first
 molecule in the template, and will issue a warning if the template
 contains multiple molecules.  The <A HREF = "atom_style.html">atom_style
 template</A> command allows multiple-molecule templates
 to define a system with more than one templated molecule.
 </P>
 <P>IMPORTANT NOTE: When using the <A HREF = "atom_style.html">atom_style template</A>
 command with a molecule template that contains multiple molecules, you
 should insure the atom types, bond types, angle_types, etc in all the
 molecules are consistent.  E.g. if one molecule represents H2O and
 another CO2, then you probably do not want each molecule file to
 define 2 atom types and a single bond type, because they will conflict
 with each other when a mixture system of H2O and CO2 molecules is
 defined, e.g.  by the <A HREF = "read_data.html">read_data</A> command.  Rather the
 H2O molecule should define atom types 1 and 2, and bond type 1.  And
 the CO2 molecule should define atom types 3 and 4 (or atom types 3 and
 2 if a single oxygen type is desired), and bond type 2.
 </P>
 <P>The format of an individual molecule file is similar to the data file
 read by the <A HREF = "read_data.html">read_data</A> commands, and is as follows.
 </P>
 <P>A molecule file has a header and a body.  The header appears first.
 The first line of the header is always skipped; it typically contains
 a description of the file.  Then lines are read one at a time.  Lines
 can have a trailing comment starting with '#' that is ignored.  If the
 line is blank (only whitespace after comment is deleted), it is
 skipped.  If the line contains a header keyword, the corresponding
 value(s) is read from the line.  If it doesn't contain a header
 keyword, the line begins the body of the file.
 </P>
 <P>The body of the file contains zero or more sections.  The first line
 of a section has only a keyword.  The next line is skipped.  The
 remaining lines of the section contain values.  The number of lines
 depends on the section keyword as described below.  Zero or more blank
 lines can be used between sections.  Sections can appear in any order,
 with a few exceptions as noted below.
 </P>
 <P>These are the recognized header keywords.  Header lines can come in
 any order.  The numeric value(s) are read from the beginning of the
 line.  The keyword should appear at the end of the line.  All these
 settings have default values, as explained below.  A line need only
 appear if the value(s) are different than the default.
 </P>
 <UL><LI>N <I>atoms</I> = # of atoms N in molecule, default = 0
 <LI>Nb <I>bonds</I> = # of bonds Nb in molecule, default = 0
 <LI>Na <I>angles</I> = # of angles Na in molecule, default = 0
 <LI>Nd <I>dihedrals</I> = # of dihedrals Nd in molecule, default = 0
 <LI>Ni <I>impropers</I> = # of impropers Ni in molecule, default = 0
 <LI>Mtotal <I>mass</I> = total mass of molecule
 <LI>Xc Yc Zc <I>com</I> = coordinates of center-of-mass of molecule
 <LI>Ixx Iyy Izz Ixy Ixz Iyz <I>inertia</I> = 6 components of inertia tensor of molecule 
 </UL>
 <P>For <I>mass</I>, <I>com</I>, and <I>inertia</I>, the default is for LAMMPS to
 calculate this quantity itself if needed, assuming the molecules
 consists of a set of point particles.  You typically only need to
 specify these values for a rigid body consisting of overlapping
 finite-size particles.
 </P>
 <P>The mass and center-of-mass coordinates (Xc,Yc,Zc) are
 self-explanatory.  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 prinicpal axes of
 the rigid body itself.  LAMMPS performs the latter calculation
 internally.
 </P>
 <P>These are the allowed section keywords for the body of the file.
 </P>
 <UL><LI><I>Coords, Types, Charges, Diameters, Masses</I> = atom-property sections 
 <LI><I>Bonds, Angles, Dihedrals, Impropers</I> = molecular topology sections 
 <LI><I>Special Bond Counts, Special Bonds</I> = special neighbor info
 <LI><I>Shake Flags, Shake Atoms, Shake Bond Types</I> = SHAKE info 
 </UL>
+<P>If a Bonds section is specified then the Special Bond Counts and
+Special Bonds sections must be also, since the latter is needed for
+LAMMPS to properly exclude or weight bonded pairwise interactions
+between bonded atoms.  See the <A HREF = "special_bonds.html">special_bonds</A>
+command for more details.
+</P>
 <P>IMPORTANT NOTE: Whether a section is required depends on how the
 molecule template is used by other LAMMPS commands.  For example, to
 add a molecule via the <A HREF = "fix_deposit.html">fix deposit</A> command, the
 Coords and Types sections are required.  To add a rigid body via the
 <A HREF = "fix_pout.html">fix pour</A> command, the Bonds (Angles, etc) sections are
 not required, since the molecule will be treated as a rigid body.
 Some sections are optional.  For example, the <A HREF = "fix_pour.html">fix pour</A>
 command can be used to add "molecules" which are clusters of
 finite-size granular particles.  If the Diameters section is not
 specified, each particle in the molecule will have a default diameter
 of 1.0.  See the doc pages for LAMMPS commands that use molecule
 templates for more details.
 </P>
 <P>Each section is listed below in alphabetic order.  The format of each
 section is described including the number of lines it must contain and
 rules (if any) for whether it can appear in the data file.  In each
 case the ID is ignored; it is simply included for readability, and
 should be a number from 1 to Nlines for the section, indicating which
 atom (or bond, etc) the entry applies to.  The lines are assumed to be
 listed in order from 1 to Nlines, but LAMMPS does not check for this.
 </P>
 <HR>
 
 <P><I>Coords</I> section:
 </P>
 <UL><LI>one line per atom
 <LI>line syntax: ID x y z
 <LI>x,y,z = coordinate of atom 
 </UL>
 <HR>
 
 <P><I>Types</I> section:
 </P>
 <UL><LI>one line per atom
 <LI>line syntax: ID type
 <LI>type = atom type of atom 
 </UL>
 <HR>
 
 <P><I>Charges</I> section:
 </P>
 <UL><LI>one line per atom
 <LI>line syntax: ID q
 <LI>q = charge on atom 
 </UL>
 <P>This section is only allowed for <A HREF = "atom_style.html">atom styles</A> that
 support charge.  If this section is not included, the default charge
 on each atom in the molecule is 0.0.
 </P>
 <HR>
 
 <P><I>Diameters</I> section:
 </P>
 <UL><LI>one line per atom
 <LI>line syntax: ID diam
 <LI>diam = diameter of atom 
 </UL>
 <P>This section is only allowed for <A HREF = "atom_style.html">atom styles</A> that
 support finite-size spherical particles, e.g. atom_style sphere.  If
 not listed, the default diameter of each atom in the molecule is 1.0.
 </P>
 <HR>
 
 <P><I>Masses</I> section:
 </P>
 <UL><LI>one line per atom
 <LI>line syntax: ID mass
 <LI>mass = mass of atom 
 </UL>
 <P>This section is only allowed for <A HREF = "atom_style.html">atom styles</A> that
 support per-atom mass, as opposed to per-type mass.  See the
 <A HREF = "mass.html">mass</A> command for details.  If this section is not
 included, the default mass for each atom is derived from its volume
 (see Diameters section) and a default density of 1.0, in
 <A HREF = "units.html">units</A> of mass/volume.
 </P>
 <HR>
 
 <P><I>Bonds</I> section:
 </P>
 <UL><LI>one line per bond
 <LI>line syntax: ID type atom1 atom2
 <LI>type = bond type (1-Nbondtype)
 <LI>atom1,atom2 = IDs of atoms in bond 
 </UL>
 <P>The IDs for the two atoms in each bond should be values
 from 1 to Natoms, where Natoms = # of atoms in the molecule.
 </P>
 <HR>
 
 <P><I>Angles</I> section:
 </P>
 <UL><LI>one line per angle
 <LI>line syntax: ID type atom1 atom2 atom3
 <LI>type = angle type (1-Nangletype)
 <LI>atom1,atom2,atom3 = IDs of atoms in angle 
 </UL>
 <P>The IDs for the three atoms in each angle should be values from 1 to
 Natoms, where Natoms = # of atoms in the molecule.  The 3 atoms are
 ordered linearly within the angle.  Thus the central atom (around
 which the angle is computed) is the atom2 in the list.
 </P>
 <HR>
 
 <P><I>Dihedrals</I> section:
 </P>
 <UL><LI>one line per dihedral
 <LI>line syntax: ID type atom1 atom2 atom3 atom4
 <LI>type = dihedral type (1-Ndihedraltype)
 <LI>atom1,atom2,atom3,atom4 = IDs of atoms in dihedral 
 </UL>
 <P>The IDs for the four atoms in each dihedral should be values from 1 to
 Natoms, where Natoms = # of atoms in the molecule.  The 4 atoms are
 ordered linearly within the dihedral.
 </P>
 <HR>
 
 <P><I>Impropers</I> section:
 </P>
 <UL><LI>one line per improper
 <LI>line syntax: ID type atom1 atom2 atom3 atom4
 <LI>type = improper type (1-Nimpropertype)
 <LI>atom1,atom2,atom3,atom4 = IDs of atoms in improper 
 </UL>
 <P>The IDs for the four atoms in each improper should be values from 1 to
 Natoms, where Natoms = # of atoms in the molecule.  The ordering of
 the 4 atoms determines the definition of the improper angle used in
 the formula for the defined <A HREF = "improper_style.html">improper style</A>.  See
 the doc pages for individual styles for details.
 </P>
 <HR>
 
 <P><I>Special Bond Counts</I> section:
 </P>
 <UL><LI>one line per atom
 <LI>line syntax: ID N1 N2 N3
 <LI>N1 = # of 1-2 bonds
 <LI>N2 = # of 1-3 bonds
 <LI>N3 = # of 1-4 bonds 
 </UL>
 <P>N1, N2, N3 are the number of 1-2, 1-3, 1-4 neighbors respectively of
 this atom within the topology of the molecule.  See the
 <A HREF = "special_bonds.html">special_bonds</A> doc page for more discussion of
 1-2, 1-3, 1-4 neighbors.  If this section appears, the Special Bonds
 section must also appear.  If this section is not specied, the
 atoms in the molecule will have no special bonds.
 </P>
 <HR>
 
 <P><I>Special Bonds</I> section:
 </P>
 <UL><LI>one line per atom
 <LI>line syntax: ID a b c d ...
 <LI>a,b,c,d,... = IDs of atoms in N1+N2+N3 special bonds 
 </UL>
 <P>A, b, c, d, etc are the IDs of the n1+n2+n3 atoms that are 1-2, 1-3,
 1-4 neighbors of this atom.  The IDs should be values from 1 to
 Natoms, where Natoms = # of atoms in the molecule.  The first N1
 values should be the 1-2 neighbors, the next N2 should be the 1-3
 neighbors, the last N3 should be the 1-4 neighbors.  No atom ID should
 appear more than once.  See the <A HREF = "special_bonds.html">special_bonds</A> doc
 page for more discussion of 1-2, 1-3, 1-4 neighbors.  If this section
 appears, the Special Bond Counts section must also appear.  If this
 section is not specied, the atoms in the molecule will have no special
 bonds.
 </P>
 <HR>
 
 <P><I>Shake Flags</I> section:
 </P>
 <UL><LI>one line per atom
 <LI>line syntax: ID flag
 <LI>flag = 0,1,2,3,4 
 </UL>
 <P>This section is only needed when molecules created using the template
 will be constrained by SHAKE via the "fix shake" command.  The other
 two Shake sections must also appear in the file, following this one.
 </P>
 <P>The meaning of the flag for each atom is as follows.  See the <A HREF = "fix_shake.html">fix
 shake</A> doc page for a further description of SHAKE
 clusters.
 </P>
 <UL><LI>0 = not part of a SHAKE cluster
 <LI>1 = part of a SHAKE angle cluster (two bonds and the angle they form)
 <LI>2 = part of a 2-atom SHAKE cluster with a single bond
 <LI>3 = part of a 3-atom SHAKE cluster with two bonds
 <LI>4 = part of a 4-atom SHAKE cluster with three bonds 
 </UL>
 <HR>
 
 <P><I>Shake Atoms</I> section:
 </P>
 <UL><LI>one line per atom
 <LI>line syntax: ID a b c d
 <LI>a,b,c,d = IDs of atoms in cluster 
 </UL>
 <P>This section is only needed when molecules created using the template
 will be constrained by SHAKE via the "fix shake" command.  The other
 two Shake sections must also appear in the file.
 </P>
 <P>The a,b,c,d values are atom IDs (from 1 to Natoms) for all the atoms
 in the SHAKE cluster that this atom belongs to.  The number of values
 that must appear is determined by the shake flag for the atom (see the
 Shake Flags section above).  All atoms in a particular cluster should
 list their a,b,c,d values identically.
 </P>
 <P>If flag = 0, no a,b,c,d values are listed on the line, just the
 (ignored) ID.
 </P>
 <P>If flag = 1, a,b,c are listed, where a = ID of central atom in the
 angle, and b,c the other two atoms in the angle.
 </P>
 <P>If flag = 2, a,b are listed, where a = ID of atom in bond with the the
 lowest ID, and b = ID of atom in bond with the highest ID.
 </P>
 <P>If flag = 3, a,b,c are listed, where a = ID of central atom,
 and b,c = IDs of other two atoms bonded to the central atom.
 </P>
 <P>If flag = 4, a,b,c,d are listed, where a = ID of central atom,
 and b,c,d = IDs of other three atoms bonded to the central atom.
 </P>
 <P>See the <A HREF = "fix_shake.html">fix shake</A> doc page for a further description
 of SHAKE clusters.
 </P>
 <HR>
 
 <P><I>Shake Bond Types</I> section:
 </P>
 <UL><LI>one line per atom
 <LI>line syntax: ID a b c
 <LI>a,b,c = bond types (or angle type) of bonds (or angle) in cluster 
 </UL>
 <P>This section is only needed when molecules created using the template
 will be constrained by SHAKE via the "fix shake" command.  The other
 two Shake sections must also appear in the file.
 </P>
 <P>The a,b,c values are bond types (from 1 to Nbondtypes) for all bonds
 in the SHAKE cluster that this atom belongs to.  The number of values
 that must appear is determined by the shake flag for the atom (see the
 Shake Flags section above).  All atoms in a particular cluster should
 list their a,b,c values identically.
 </P>
 <P>If flag = 0, no a,b,c values are listed on the line, just the
 (ignored) ID.
 </P>
 <P>If flag = 1, a,b,c are listed, where a = bondtype of the bond between
 the central atom and the first non-central atom (value b in the Shake
 Atoms section), b = bondtype of the bond between the central atom and
 the 2nd non-central atom (value c in the Shake Atoms section), and c =
 the angle type (1 to Nangletypes) of the angle between the 3 atoms.
 </P>
 <P>If flag = 2, only a is listed, where a = bondtype of the bond between
 the 2 atoms in the cluster.
 </P>
 <P>If flag = 3, a,b are listed, where a = bondtype of the bond between
 the central atom and the first non-central atom (value b in the Shake
 Atoms section), and b = bondtype of the bond between the central atom
 and the 2nd non-central atom (value c in the Shake Atoms section).
 </P>
 <P>If flag = 4, a,b,c are listed, where a = bondtype of the bond between
 the central atom and the first non-central atom (value b in the Shake
 Atoms section), b = bondtype of the bond between the central atom and
 the 2nd non-central atom (value c in the Shake Atoms section), and c =
 bondtype of the bond between the central atom and the 3rd non-central
 atom (value d in the Shake Atoms section).
 </P>
 <P>See the <A HREF = "fix_shake.html">fix shake</A> doc page for a further description
 of SHAKE clusters.
 </P>
 <HR>
 
 <P><B>Restrictions:</B> none
 </P>
 <P><B>Related commands:</B>
 </P>
 <P><A HREF = "fix_deposit.html">fix deposit</A>, <A HREF = "fix_pour.html">fix pour</A>,
 <A HREF = "fix_gcmc.html">fix_gcmc</A>
 </P>
 <P><B>Default:</B> none
 </P>
 </HTML>
diff --git a/doc/molecule.txt b/doc/molecule.txt
index 1af3078c6..f43722285 100644
--- a/doc/molecule.txt
+++ b/doc/molecule.txt
@@ -1,394 +1,400 @@
 "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
 
 molecule command :h3
 
 [Syntax:]
 
 molecule ID file1 file2 ... :pre
 
 ID = user-assigned name for the molecule template
 file1,file2,... = names of files containing molecule descriptions :ul
 
 [Examples:]
 
 molecule 1 mymol
 molecule 1 co2.txt h2o.txt
 molecule CO2 co2.txt :pre
 
 [Description:]
 
 Define a molecule template that can be used as part of other LAMMPS
 commands, typically to define a collection of particles as a bonded
 molecule or a rigid body.  Commands that currently use molecule
-templates (or will in the future) include:
+templates include:
 
 "fix deposit"_fix_deposit.html
 "fix pour"_fix_pour.html
 "fix rigid/small"_fix_rigid.html
 "fix shake"_fix_shake.html
 "fix gcmc"_fix_gcmc.html
 "create_atoms"_create_atoms.html 
 "atom_style template"_atom_style.html :ul
 
 The ID of a molecule template can only contain alphanumeric characters
 and underscores.
 
 A single template can contain multiple molecules, listed one per file.
 Many of the commands listed above currently use only the first
 molecule in the template, and will issue a warning if the template
 contains multiple molecules.  The "atom_style
 template"_atom_style.html command allows multiple-molecule templates
 to define a system with more than one templated molecule.
 
 IMPORTANT NOTE: When using the "atom_style template"_atom_style.html
 command with a molecule template that contains multiple molecules, you
 should insure the atom types, bond types, angle_types, etc in all the
 molecules are consistent.  E.g. if one molecule represents H2O and
 another CO2, then you probably do not want each molecule file to
 define 2 atom types and a single bond type, because they will conflict
 with each other when a mixture system of H2O and CO2 molecules is
 defined, e.g.  by the "read_data"_read_data.html command.  Rather the
 H2O molecule should define atom types 1 and 2, and bond type 1.  And
 the CO2 molecule should define atom types 3 and 4 (or atom types 3 and
 2 if a single oxygen type is desired), and bond type 2.
 
 The format of an individual molecule file is similar to the data file
 read by the "read_data"_read_data.html commands, and is as follows.
 
 A molecule file has a header and a body.  The header appears first.
 The first line of the header is always skipped; it typically contains
 a description of the file.  Then lines are read one at a time.  Lines
 can have a trailing comment starting with '#' that is ignored.  If the
 line is blank (only whitespace after comment is deleted), it is
 skipped.  If the line contains a header keyword, the corresponding
 value(s) is read from the line.  If it doesn't contain a header
 keyword, the line begins the body of the file.
 
 The body of the file contains zero or more sections.  The first line
 of a section has only a keyword.  The next line is skipped.  The
 remaining lines of the section contain values.  The number of lines
 depends on the section keyword as described below.  Zero or more blank
 lines can be used between sections.  Sections can appear in any order,
 with a few exceptions as noted below.
 
 These are the recognized header keywords.  Header lines can come in
 any order.  The numeric value(s) are read from the beginning of the
 line.  The keyword should appear at the end of the line.  All these
 settings have default values, as explained below.  A line need only
 appear if the value(s) are different than the default.
 
 N {atoms} = # of atoms N in molecule, default = 0
 Nb {bonds} = # of bonds Nb in molecule, default = 0
 Na {angles} = # of angles Na in molecule, default = 0
 Nd {dihedrals} = # of dihedrals Nd in molecule, default = 0
 Ni {impropers} = # of impropers Ni in molecule, default = 0
 Mtotal {mass} = total mass of molecule
 Xc Yc Zc {com} = coordinates of center-of-mass of molecule
 Ixx Iyy Izz Ixy Ixz Iyz {inertia} = 6 components of inertia tensor of molecule :ul
 
 For {mass}, {com}, and {inertia}, the default is for LAMMPS to
 calculate this quantity itself if needed, assuming the molecules
 consists of a set of point particles.  You typically only need to
 specify these values for a rigid body consisting of overlapping
 finite-size particles.
 
 The mass and center-of-mass coordinates (Xc,Yc,Zc) are
 self-explanatory.  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 prinicpal axes of
 the rigid body itself.  LAMMPS performs the latter calculation
 internally.
 
 These are the allowed section keywords for the body of the file.
 
 {Coords, Types, Charges, Diameters, Masses} = atom-property sections 
 {Bonds, Angles, Dihedrals, Impropers} = molecular topology sections 
 {Special Bond Counts, Special Bonds} = special neighbor info
 {Shake Flags, Shake Atoms, Shake Bond Types} = SHAKE info :ul
 
+If a Bonds section is specified then the Special Bond Counts and
+Special Bonds sections must be also, since the latter is needed for
+LAMMPS to properly exclude or weight bonded pairwise interactions
+between bonded atoms.  See the "special_bonds"_special_bonds.html
+command for more details.
+
 IMPORTANT NOTE: Whether a section is required depends on how the
 molecule template is used by other LAMMPS commands.  For example, to
 add a molecule via the "fix deposit"_fix_deposit.html command, the
 Coords and Types sections are required.  To add a rigid body via the
 "fix pour"_fix_pout.html command, the Bonds (Angles, etc) sections are
 not required, since the molecule will be treated as a rigid body.
 Some sections are optional.  For example, the "fix pour"_fix_pour.html
 command can be used to add "molecules" which are clusters of
 finite-size granular particles.  If the Diameters section is not
 specified, each particle in the molecule will have a default diameter
 of 1.0.  See the doc pages for LAMMPS commands that use molecule
 templates for more details.
 
 Each section is listed below in alphabetic order.  The format of each
 section is described including the number of lines it must contain and
 rules (if any) for whether it can appear in the data file.  In each
 case the ID is ignored; it is simply included for readability, and
 should be a number from 1 to Nlines for the section, indicating which
 atom (or bond, etc) the entry applies to.  The lines are assumed to be
 listed in order from 1 to Nlines, but LAMMPS does not check for this.
 
 :line
 
 {Coords} section:
 
 one line per atom
 line syntax: ID x y z
 x,y,z = coordinate of atom :ul
 
 :line
 
 {Types} section:
 
 one line per atom
 line syntax: ID type
 type = atom type of atom :ul
 
 :line
 
 {Charges} section:
 
 one line per atom
 line syntax: ID q
 q = charge on atom :ul
 
 This section is only allowed for "atom styles"_atom_style.html that
 support charge.  If this section is not included, the default charge
 on each atom in the molecule is 0.0.
 
 :line
 
 {Diameters} section:
 
 one line per atom
 line syntax: ID diam
 diam = diameter of atom :ul
 
 This section is only allowed for "atom styles"_atom_style.html that
 support finite-size spherical particles, e.g. atom_style sphere.  If
 not listed, the default diameter of each atom in the molecule is 1.0.
 
 :line
 
 {Masses} section:
 
 one line per atom
 line syntax: ID mass
 mass = mass of atom :ul
 
 This section is only allowed for "atom styles"_atom_style.html that
 support per-atom mass, as opposed to per-type mass.  See the
 "mass"_mass.html command for details.  If this section is not
 included, the default mass for each atom is derived from its volume
 (see Diameters section) and a default density of 1.0, in
 "units"_units.html of mass/volume.
 
 :line
 
 {Bonds} section:
 
 one line per bond
 line syntax: ID type atom1 atom2
 type = bond type (1-Nbondtype)
 atom1,atom2 = IDs of atoms in bond :ul
 
 The IDs for the two atoms in each bond should be values
 from 1 to Natoms, where Natoms = # of atoms in the molecule.
 
 :line
 
 {Angles} section:
 
 one line per angle
 line syntax: ID type atom1 atom2 atom3
 type = angle type (1-Nangletype)
 atom1,atom2,atom3 = IDs of atoms in angle :ul
 
 The IDs for the three atoms in each angle should be values from 1 to
 Natoms, where Natoms = # of atoms in the molecule.  The 3 atoms are
 ordered linearly within the angle.  Thus the central atom (around
 which the angle is computed) is the atom2 in the list.
 
 :line
 
 {Dihedrals} section:
 
 one line per dihedral
 line syntax: ID type atom1 atom2 atom3 atom4
 type = dihedral type (1-Ndihedraltype)
 atom1,atom2,atom3,atom4 = IDs of atoms in dihedral :ul
 
 The IDs for the four atoms in each dihedral should be values from 1 to
 Natoms, where Natoms = # of atoms in the molecule.  The 4 atoms are
 ordered linearly within the dihedral.
 
 :line
 
 {Impropers} section:
 
 one line per improper
 line syntax: ID type atom1 atom2 atom3 atom4
 type = improper type (1-Nimpropertype)
 atom1,atom2,atom3,atom4 = IDs of atoms in improper :ul
 
 The IDs for the four atoms in each improper should be values from 1 to
 Natoms, where Natoms = # of atoms in the molecule.  The ordering of
 the 4 atoms determines the definition of the improper angle used in
 the formula for the defined "improper style"_improper_style.html.  See
 the doc pages for individual styles for details.
 
 :line
 
 {Special Bond Counts} section:
 
 one line per atom
 line syntax: ID N1 N2 N3
 N1 = # of 1-2 bonds
 N2 = # of 1-3 bonds
 N3 = # of 1-4 bonds :ul
 
 N1, N2, N3 are the number of 1-2, 1-3, 1-4 neighbors respectively of
 this atom within the topology of the molecule.  See the
 "special_bonds"_special_bonds.html doc page for more discussion of
 1-2, 1-3, 1-4 neighbors.  If this section appears, the Special Bonds
 section must also appear.  If this section is not specied, the
 atoms in the molecule will have no special bonds.
 
 :line
 
 {Special Bonds} section:
 
 one line per atom
 line syntax: ID a b c d ...
 a,b,c,d,... = IDs of atoms in N1+N2+N3 special bonds :ul
 
 A, b, c, d, etc are the IDs of the n1+n2+n3 atoms that are 1-2, 1-3,
 1-4 neighbors of this atom.  The IDs should be values from 1 to
 Natoms, where Natoms = # of atoms in the molecule.  The first N1
 values should be the 1-2 neighbors, the next N2 should be the 1-3
 neighbors, the last N3 should be the 1-4 neighbors.  No atom ID should
 appear more than once.  See the "special_bonds"_special_bonds.html doc
 page for more discussion of 1-2, 1-3, 1-4 neighbors.  If this section
 appears, the Special Bond Counts section must also appear.  If this
 section is not specied, the atoms in the molecule will have no special
 bonds.
 
 :line
 
 {Shake Flags} section:
 
 one line per atom
 line syntax: ID flag
 flag = 0,1,2,3,4 :ul
 
 This section is only needed when molecules created using the template
 will be constrained by SHAKE via the "fix shake" command.  The other
 two Shake sections must also appear in the file, following this one.
 
 The meaning of the flag for each atom is as follows.  See the "fix
 shake"_fix_shake.html doc page for a further description of SHAKE
 clusters.
 
 0 = not part of a SHAKE cluster
 1 = part of a SHAKE angle cluster (two bonds and the angle they form)
 2 = part of a 2-atom SHAKE cluster with a single bond
 3 = part of a 3-atom SHAKE cluster with two bonds
 4 = part of a 4-atom SHAKE cluster with three bonds :ul
 
 :line
 
 {Shake Atoms} section:
 
 one line per atom
 line syntax: ID a b c d
 a,b,c,d = IDs of atoms in cluster :ul
 
 This section is only needed when molecules created using the template
 will be constrained by SHAKE via the "fix shake" command.  The other
 two Shake sections must also appear in the file.
 
 The a,b,c,d values are atom IDs (from 1 to Natoms) for all the atoms
 in the SHAKE cluster that this atom belongs to.  The number of values
 that must appear is determined by the shake flag for the atom (see the
 Shake Flags section above).  All atoms in a particular cluster should
 list their a,b,c,d values identically.
 
 If flag = 0, no a,b,c,d values are listed on the line, just the
 (ignored) ID.
 
 If flag = 1, a,b,c are listed, where a = ID of central atom in the
 angle, and b,c the other two atoms in the angle.
 
 If flag = 2, a,b are listed, where a = ID of atom in bond with the the
 lowest ID, and b = ID of atom in bond with the highest ID.
 
 If flag = 3, a,b,c are listed, where a = ID of central atom,
 and b,c = IDs of other two atoms bonded to the central atom.
 
 If flag = 4, a,b,c,d are listed, where a = ID of central atom,
 and b,c,d = IDs of other three atoms bonded to the central atom.
 
 See the "fix shake"_fix_shake.html doc page for a further description
 of SHAKE clusters.
 
 :line
 
 {Shake Bond Types} section:
 
 one line per atom
 line syntax: ID a b c
 a,b,c = bond types (or angle type) of bonds (or angle) in cluster :ul
 
 This section is only needed when molecules created using the template
 will be constrained by SHAKE via the "fix shake" command.  The other
 two Shake sections must also appear in the file.
 
 The a,b,c values are bond types (from 1 to Nbondtypes) for all bonds
 in the SHAKE cluster that this atom belongs to.  The number of values
 that must appear is determined by the shake flag for the atom (see the
 Shake Flags section above).  All atoms in a particular cluster should
 list their a,b,c values identically.
 
 If flag = 0, no a,b,c values are listed on the line, just the
 (ignored) ID.
 
 If flag = 1, a,b,c are listed, where a = bondtype of the bond between
 the central atom and the first non-central atom (value b in the Shake
 Atoms section), b = bondtype of the bond between the central atom and
 the 2nd non-central atom (value c in the Shake Atoms section), and c =
 the angle type (1 to Nangletypes) of the angle between the 3 atoms.
 
 If flag = 2, only a is listed, where a = bondtype of the bond between
 the 2 atoms in the cluster.
 
 If flag = 3, a,b are listed, where a = bondtype of the bond between
 the central atom and the first non-central atom (value b in the Shake
 Atoms section), and b = bondtype of the bond between the central atom
 and the 2nd non-central atom (value c in the Shake Atoms section).
 
 If flag = 4, a,b,c are listed, where a = bondtype of the bond between
 the central atom and the first non-central atom (value b in the Shake
 Atoms section), b = bondtype of the bond between the central atom and
 the 2nd non-central atom (value c in the Shake Atoms section), and c =
 bondtype of the bond between the central atom and the 3rd non-central
 atom (value d in the Shake Atoms section).
 
 See the "fix shake"_fix_shake.html doc page for a further description
 of SHAKE clusters.
 
 :line
 
 [Restrictions:] none
 
 [Related commands:]
 
 "fix deposit"_fix_deposit.html, "fix pour"_fix_pour.html,
 "fix_gcmc"_fix_gcmc.html
 
 [Default:] none
diff --git a/examples/COUPLE/lammps_quest/lmpqst.cpp b/examples/COUPLE/lammps_quest/lmpqst.cpp
index a9f364a23..e7f2d92c1 100644
--- a/examples/COUPLE/lammps_quest/lmpqst.cpp
+++ b/examples/COUPLE/lammps_quest/lmpqst.cpp
@@ -1,264 +1,270 @@
 // lmpqst = umbrella driver to couple LAMMPS + Quest
 //          for MD using quantum forces
 
 // Syntax: lmpqst Niter in.lammps in.quest
 //         Niter = # of MD iterations
 //         in.lammps = LAMMPS input script
 //         in.quest = Quest input script
 
 #include "mpi.h"
 #include "stdio.h"
 #include "stdlib.h"
 #include "string.h"
 #include "stdint.h"
 
 #include "many2one.h"
 #include "one2many.h"
 #include "files.h"
 #include "memory.h"
 #include "error.h"
 
 #define QUOTE_(x) #x
 #define QUOTE(x) QUOTE_(x)
 
 #include "lmppath.h"
 #include QUOTE(LMPPATH/src/lammps.h)
 #include QUOTE(LMPPATH/src/library.h)
 #include QUOTE(LMPPATH/src/input.h)
 #include QUOTE(LMPPATH/src/modify.h)
 #include QUOTE(LMPPATH/src/fix.h)
 #include QUOTE(LMPPATH/src/fix_external.h)
 
 #include "qstexe.h"
 
 using namespace LAMMPS_NS;
 
 #define ANGSTROM_per_BOHR 0.529
 #define EV_per_RYDBERG 13.6056923
 
 void quest_callback(void *, bigint, int, int *, double **, double **);
 
 struct Info {
   int me;
   Memory *memory;
   LAMMPS *lmp;
   char *quest_input;
 };
 
 /* ---------------------------------------------------------------------- */
 
 int main(int narg, char **arg)
 {
   int n;
   char str[128];
 
   // setup MPI
 
   MPI_Init(&narg,&arg);
   MPI_Comm comm = MPI_COMM_WORLD;
 
   int me,nprocs;
   MPI_Comm_rank(comm,&me);
   MPI_Comm_size(comm,&nprocs);
 
   Memory *memory = new Memory(comm);
   Error *error = new Error(comm);
 
   // command-line args
 
   if (narg != 4) error->all("Syntax: lmpqst Niter in.lammps in.quest");
 
   int niter = atoi(arg[1]);
   n = strlen(arg[2]) + 1;
   char *lammps_input = new char[n];
   strcpy(lammps_input,arg[2]);
   n = strlen(arg[3]) + 1;
   char *quest_input = new char[n];
   strcpy(quest_input,arg[3]);
 
   // instantiate LAMMPS
 
   LAMMPS *lmp = new LAMMPS(0,NULL,MPI_COMM_WORLD);
 
   // create simulation in LAMMPS from in.lammps
 
   lmp->input->file(lammps_input);
 
   // make info avaiable to callback function
 
   Info info;
   info.me = me;
   info.memory = memory;
   info.lmp = lmp;
   info.quest_input = quest_input;
 
   // set callback to Quest inside fix external
   // this could also be done thru Python, using a ctypes callback
 
   int ifix = lmp->modify->find_fix("2");
   FixExternal *fix = (FixExternal *) lmp->modify->fix[ifix];
   fix->set_callback(quest_callback,&info);
 
   // run LAMMPS for Niter
   // each time it needs forces, it will invoke quest_callback
 
   sprintf(str,"run %d",niter);
   lmp->input->one(str);
 
   // clean up
 
   delete lmp;
 
   delete memory;
   delete error;
 
   delete [] lammps_input;
   delete [] quest_input;
 
   MPI_Finalize();
 }
 
 /* ----------------------------------------------------------------------
    callback to Quest with atom IDs and coords from each proc
    invoke Quest to compute forces, load them into f for LAMMPS to use
    f can be NULL if proc owns no atoms
 ------------------------------------------------------------------------- */
 
 void quest_callback(void *ptr, bigint ntimestep,
 		    int nlocal, int *id, double **x, double **f)
 {
   int i,j;
   char str[128];
 
   Info *info = (Info *) ptr;
 
   // boxlines = LAMMPS box size converted into Quest lattice vectors
 
   char **boxlines = NULL;
   if (info->me == 0) {
     boxlines = new char*[3];
     for (i = 0; i < 3; i++) boxlines[i] = new char[128];
   }
 
   double boxxlo = *((double *) lammps_extract_global(info->lmp,"boxxlo"));
   double boxxhi = *((double *) lammps_extract_global(info->lmp,"boxxhi"));
   double boxylo = *((double *) lammps_extract_global(info->lmp,"boxylo"));
   double boxyhi = *((double *) lammps_extract_global(info->lmp,"boxyhi"));
   double boxzlo = *((double *) lammps_extract_global(info->lmp,"boxzlo"));
   double boxzhi = *((double *) lammps_extract_global(info->lmp,"boxzhi"));
+  double boxxy = *((double *) lammps_extract_global(info->lmp,"xy"));
+  double boxxz = *((double *) lammps_extract_global(info->lmp,"xz"));
+  double boxyz = *((double *) lammps_extract_global(info->lmp,"yz"));
 
   double xprd = (boxxhi-boxxlo)/ANGSTROM_per_BOHR;
   double yprd = (boxyhi-boxylo)/ANGSTROM_per_BOHR;
   double zprd = (boxzhi-boxzlo)/ANGSTROM_per_BOHR;
+  double xy = boxxy/ANGSTROM_per_BOHR;
+  double xz = boxxz/ANGSTROM_per_BOHR;
+  double yz = boxyz/ANGSTROM_per_BOHR;
 
   if (info->me == 0) {
     sprintf(boxlines[0],"%g %g %g\n",xprd,0.0,0.0);
-    sprintf(boxlines[1],"%g %g %g\n",0.0,yprd,0.0);
-    sprintf(boxlines[2],"%g %g %g\n",0.0,0.0,zprd);
+    sprintf(boxlines[1],"%g %g %g\n",xy,yprd,0.0);
+    sprintf(boxlines[2],"%g %g %g\n",xz,yz,zprd);
   }
 
   // xlines = x for atoms on each proc converted to text lines
   // xlines is suitable for insertion into Quest input file
   // convert LAMMPS Angstroms to Quest bohr
   
   int natoms;
   MPI_Allreduce(&nlocal,&natoms,1,MPI_INT,MPI_SUM,MPI_COMM_WORLD);
 
   Many2One *lmp2qst = new Many2One(MPI_COMM_WORLD);
   lmp2qst->setup(nlocal,id,natoms);
 
   char **xlines = NULL;
   double **xquest = NULL;
   if (info->me == 0) {
     xquest = info->memory->create_2d_double_array(natoms,3,"lmpqst:xquest");
     xlines = new char*[natoms];
     for (i = 0; i < natoms; i++) xlines[i] = new char[128];
   }
 
   if (info->me == 0) lmp2qst->gather(&x[0][0],3,&xquest[0][0]);
   else lmp2qst->gather(&x[0][0],3,NULL);
 
   if (info->me == 0) {
     for (i = 0; i < natoms; i++) {
       xquest[i][0] /= ANGSTROM_per_BOHR;
       xquest[i][1] /= ANGSTROM_per_BOHR;
       xquest[i][2] /= ANGSTROM_per_BOHR;
     }
     for (i = 0; i < natoms; i++) {
       sprintf(xlines[i],"%d %d %g %g %g\n",i+1,1,
 	      xquest[i][0],xquest[i][1],xquest[i][2]);
     }
   }
 
   // one-processor tasks:
   // whack all lcao.* files
   // cp quest_input to lcao.in
   // replace atom coords section of lcao.in with new atom coords
   // run Quest on one proc, save screen output to file
   // flines = atom forces extracted from Quest screen file
   // fquest = atom forces
   // convert Quest Ryd/bohr to LAMMPS eV/Angstrom
   
   char **flines = NULL;
   double **fquest = NULL;
   if (info->me == 0) {
     fquest = info->memory->create_2d_double_array(natoms,3,"lmpqst:fquest");
     flines = new char*[natoms];
     for (i = 0; i < natoms; i++) flines[i] = new char[128];
   }
 
   if (info->me == 0) {
     system("rm lcao.*");
     sprintf(str,"cp %s lcao.in",info->quest_input);
     system(str);
     sprintf(str,"cp %s lcao.x",QUOTE(QUEST));
     system(str);
     replace("lcao.in","primitive lattice vectors",3,boxlines);
     replace("lcao.in","atom, type, position vector",natoms,xlines);
     system("lcao.x > lcao.screen");
     extract("lcao.screen","atom       x force          "
 	    "y force          z force",natoms,flines);
     
     int itmp;
     for (i = 0; i < natoms; i++)
       sscanf(flines[i],"%d %lg %lg %lg",&itmp,
 	     &fquest[i][0],&fquest[i][1],&fquest[i][2]);
 
     for (i = 0; i < natoms; i++) {
       fquest[i][0] *= EV_per_RYDBERG / ANGSTROM_per_BOHR;
       fquest[i][1] *= EV_per_RYDBERG / ANGSTROM_per_BOHR;
       fquest[i][2] *= EV_per_RYDBERG / ANGSTROM_per_BOHR;
     }
   }
 
   // convert fquest on one proc into f for atoms on each proc
 
   One2Many *qst2lmp = new One2Many(MPI_COMM_WORLD);
   qst2lmp->setup(natoms,nlocal,id);
   double *fvec = NULL;
   if (f) fvec = &f[0][0];
   if (info->me == 0) qst2lmp->scatter(&fquest[0][0],3,fvec);
   else qst2lmp->scatter(NULL,3,fvec);
 
   // clean up
   // some data only exists on proc 0
 
   delete lmp2qst;
   delete qst2lmp;
 
   info->memory->destroy_2d_double_array(xquest);
   info->memory->destroy_2d_double_array(fquest);
 
   if (boxlines) {
     for (i = 0; i < 3; i++) delete [] boxlines[i];
     delete [] boxlines;
   }
   if (xlines) {
     for (i = 0; i < natoms; i++) delete [] xlines[i];
     delete [] xlines;
   }
   if (flines) {
     for (i = 0; i < natoms; i++) delete [] flines[i];
     delete [] flines;
   }
 }
diff --git a/src/KSPACE/pppm.cpp b/src/KSPACE/pppm.cpp
index 5f3f8ce2d..4d45ef222 100644
--- a/src/KSPACE/pppm.cpp
+++ b/src/KSPACE/pppm.cpp
@@ -1,3488 +1,3493 @@
 /* ----------------------------------------------------------------------
    LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
    http://lammps.sandia.gov, Sandia National Laboratories
    Steve Plimpton, sjplimp@sandia.gov
 
    Copyright (2003) Sandia Corporation.  Under the terms of Contract
    DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
    certain rights in this software.  This software is distributed under
    the GNU General Public License.
 
    See the README file in the top-level LAMMPS directory.
 ------------------------------------------------------------------------- */
 
 /* ----------------------------------------------------------------------
    Contributing authors: Roy Pollock (LLNL), Paul Crozier (SNL)
      per-atom energy/virial & group/group energy/force added by Stan Moore (BYU)
      analytic diff (2 FFT) option added by Rolf Isele-Holder (Aachen University)
      triclinic added by Stan Moore (SNL)
 ------------------------------------------------------------------------- */
 
 #include "lmptype.h"
 #include "mpi.h"
 #include "string.h"
 #include "stdio.h"
 #include "stdlib.h"
 #include "math.h"
 #include "pppm.h"
 #include "atom.h"
 #include "comm.h"
 #include "gridcomm.h"
 #include "neighbor.h"
 #include "force.h"
 #include "pair.h"
 #include "bond.h"
 #include "angle.h"
 #include "domain.h"
 #include "fft3d_wrap.h"
 #include "remap_wrap.h"
 #include "memory.h"
 #include "error.h"
 
 #include "math_const.h"
 #include "math_special.h"
 
 using namespace LAMMPS_NS;
 using namespace MathConst;
 using namespace MathSpecial;
 
 #define MAXORDER 7
 #define OFFSET 16384
 #define LARGE 10000.0
 #define SMALL 0.00001
 #define EPS_HOC 1.0e-7
 
 enum{REVERSE_RHO};
 enum{FORWARD_IK,FORWARD_AD,FORWARD_IK_PERATOM,FORWARD_AD_PERATOM};
 
 #ifdef FFT_SINGLE
 #define ZEROF 0.0f
 #define ONEF  1.0f
 #else
 #define ZEROF 0.0
 #define ONEF  1.0
 #endif
 
 /* ---------------------------------------------------------------------- */
 
 PPPM::PPPM(LAMMPS *lmp, int narg, char **arg) : KSpace(lmp, narg, arg)
 {
   if (narg < 1) error->all(FLERR,"Illegal kspace_style pppm command");
  
   pppmflag = 1;
   group_group_enable = 1;
 
   accuracy_relative = fabs(force->numeric(FLERR,arg[0]));
 
   nfactors = 3;
   factors = new int[nfactors];
   factors[0] = 2;
   factors[1] = 3;
   factors[2] = 5;
 
   MPI_Comm_rank(world,&me);
   MPI_Comm_size(world,&nprocs);
 
   density_brick = vdx_brick = vdy_brick = vdz_brick = NULL;
   density_fft = NULL;
   u_brick = NULL;
   v0_brick = v1_brick = v2_brick = v3_brick = v4_brick = v5_brick = NULL;
   greensfn = NULL;
   work1 = work2 = NULL;
   vg = NULL;
   fkx = fky = fkz = NULL;
 
   sf_precoeff1 = sf_precoeff2 = sf_precoeff3 = 
     sf_precoeff4 = sf_precoeff5 = sf_precoeff6 = NULL;
 
   density_A_brick = density_B_brick = NULL;
   density_A_fft = density_B_fft = NULL;
 
   gf_b = NULL;
   rho1d = rho_coeff = drho1d = drho_coeff = NULL;
 
   fft1 = fft2 = NULL;
   remap = NULL;
   cg = NULL;
   cg_peratom = NULL;
 
   nmax = 0;
   part2grid = NULL;
 
   peratom_allocate_flag = 0;
   group_allocate_flag = 0;
 
   // define acons coefficients for estimation of kspace errors
   // see JCP 109, pg 7698 for derivation of coefficients
   // higher order coefficients may be computed if needed
 
   memory->create(acons,8,7,"pppm:acons");
   acons[1][0] = 2.0 / 3.0;
   acons[2][0] = 1.0 / 50.0;
   acons[2][1] = 5.0 / 294.0;
   acons[3][0] = 1.0 / 588.0;
   acons[3][1] = 7.0 / 1440.0;
   acons[3][2] = 21.0 / 3872.0;
   acons[4][0] = 1.0 / 4320.0;
   acons[4][1] = 3.0 / 1936.0;
   acons[4][2] = 7601.0 / 2271360.0;
   acons[4][3] = 143.0 / 28800.0;
   acons[5][0] = 1.0 / 23232.0;
   acons[5][1] = 7601.0 / 13628160.0;
   acons[5][2] = 143.0 / 69120.0;
   acons[5][3] = 517231.0 / 106536960.0;
   acons[5][4] = 106640677.0 / 11737571328.0;
   acons[6][0] = 691.0 / 68140800.0;
   acons[6][1] = 13.0 / 57600.0;
   acons[6][2] = 47021.0 / 35512320.0;
   acons[6][3] = 9694607.0 / 2095994880.0;
   acons[6][4] = 733191589.0 / 59609088000.0;
   acons[6][5] = 326190917.0 / 11700633600.0;
   acons[7][0] = 1.0 / 345600.0;
   acons[7][1] = 3617.0 / 35512320.0;
   acons[7][2] = 745739.0 / 838397952.0;
   acons[7][3] = 56399353.0 / 12773376000.0;
   acons[7][4] = 25091609.0 / 1560084480.0;
   acons[7][5] = 1755948832039.0 / 36229939200000.0;
   acons[7][6] = 4887769399.0 / 37838389248.0;
 }
 
 /* ----------------------------------------------------------------------
    free all memory
 ------------------------------------------------------------------------- */
 
 PPPM::~PPPM()
 {
   delete [] factors;
   deallocate();
   if (peratom_allocate_flag) deallocate_peratom();
   if (group_allocate_flag) deallocate_groups();
   memory->destroy(part2grid);
   memory->destroy(acons);
 }
 
 /* ----------------------------------------------------------------------
    called once before run
 ------------------------------------------------------------------------- */
 
 void PPPM::init()
 {
   if (me == 0) {
     if (screen) fprintf(screen,"PPPM initialization ...\n");
     if (logfile) fprintf(logfile,"PPPM initialization ...\n");
   }
 
   // error check
 
   triclinic_check();
   if (domain->triclinic && differentiation_flag == 1)
     error->all(FLERR,"Cannot (yet) use PPPM with triclinic box "
                "and kspace_modify diff ad");
   if (domain->triclinic && slabflag)
     error->all(FLERR,"Cannot (yet) use PPPM with triclinic box and "
                "slab correction");
   if (domain->dimension == 2) error->all(FLERR,
                                          "Cannot use PPPM with 2d simulation");
 
   if (!atom->q_flag) error->all(FLERR,"Kspace style requires atom attribute q");
 
   if (slabflag == 0 && domain->nonperiodic > 0)
     error->all(FLERR,"Cannot use nonperiodic boundaries with PPPM");
   if (slabflag) {
     if (domain->xperiodic != 1 || domain->yperiodic != 1 ||
         domain->boundary[2][0] != 1 || domain->boundary[2][1] != 1)
       error->all(FLERR,"Incorrect boundaries with slab PPPM");
   }
 
   if (order < 2 || order > MAXORDER) {
     char str[128];
     sprintf(str,"PPPM order cannot be < 2 or > than %d",MAXORDER);
     error->all(FLERR,str);
   }
 
   // extract short-range Coulombic cutoff from pair style
 
   triclinic = domain->triclinic;
   pair_check();
 
   int itmp = 0;
   double *p_cutoff = (double *) force->pair->extract("cut_coul",itmp);
   if (p_cutoff == NULL)
     error->all(FLERR,"KSpace style is incompatible with Pair style");
   cutoff = *p_cutoff;
 
+  // a TIP4P pair style requires a matching long-range solver
+  if (!tip4pflag && force->pair->tip4pflag)
+      error->all(FLERR,"Using a TIP4P pair style without a "
+                   "compatible kspace style");
+
   // if kspace is TIP4P, extract TIP4P params from pair style
   // bond/angle are not yet init(), so insure equilibrium request is valid
 
   qdist = 0.0;
 
   if (tip4pflag) {
     double *p_qdist = (double *) force->pair->extract("qdist",itmp);
     int *p_typeO = (int *) force->pair->extract("typeO",itmp);
     int *p_typeH = (int *) force->pair->extract("typeH",itmp);
     int *p_typeA = (int *) force->pair->extract("typeA",itmp);
     int *p_typeB = (int *) force->pair->extract("typeB",itmp);
     if (!p_qdist || !p_typeO || !p_typeH || !p_typeA || !p_typeB)
       error->all(FLERR,"KSpace style is incompatible with Pair style");
     qdist = *p_qdist;
     typeO = *p_typeO;
     typeH = *p_typeH;
     int typeA = *p_typeA;
     int typeB = *p_typeB;
 
     if (force->angle == NULL || force->bond == NULL ||
         force->angle->setflag == NULL || force->bond->setflag == NULL)
       error->all(FLERR,"Bond and angle potentials must be defined for TIP4P");
     if (typeA < 1 || typeA > atom->nangletypes ||
         force->angle->setflag[typeA] == 0)
       error->all(FLERR,"Bad TIP4P angle type for PPPM/TIP4P");
     if (typeB < 1 || typeB > atom->nbondtypes ||
         force->bond->setflag[typeB] == 0)
       error->all(FLERR,"Bad TIP4P bond type for PPPM/TIP4P");
     double theta = force->angle->equilibrium_angle(typeA);
     double blen = force->bond->equilibrium_distance(typeB);
     alpha = qdist / (cos(0.5*theta) * blen);
     if (domain->triclinic)
       error->all(FLERR,"Cannot (yet) use PPPM with triclinic box and TIP4P");
   }
 
   // compute qsum & qsqsum and warn if not charge-neutral
 
   scale = 1.0;
   qqrd2e = force->qqrd2e;
   qsum_qsq(0);
   natoms_original = atom->natoms;
 
   // set accuracy (force units) from accuracy_relative or accuracy_absolute
 
   if (accuracy_absolute >= 0.0) accuracy = accuracy_absolute;
   else accuracy = accuracy_relative * two_charge_force;
 
   // free all arrays previously allocated
 
   deallocate();
   if (peratom_allocate_flag) deallocate_peratom();
   if (group_allocate_flag) deallocate_groups();
 
   // setup FFT grid resolution and g_ewald
   // normally one iteration thru while loop is all that is required
   // if grid stencil does not extend beyond neighbor proc
   //   or overlap is allowed, then done
   // else reduce order and try again
 
   int (*procneigh)[2] = comm->procneigh;
 
   GridComm *cgtmp = NULL;
   int iteration = 0;
 
   while (order >= minorder) {
     if (iteration && me == 0)
       error->warning(FLERR,"Reducing PPPM order b/c stencil extends "
                      "beyond nearest neighbor processor");
 
     if (stagger_flag && !differentiation_flag) compute_gf_denom();
     set_grid_global();
     set_grid_local();
     if (overlap_allowed) break;
 
     cgtmp = new GridComm(lmp,world,1,1,
                          nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in,
                          nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out,
                          procneigh[0][0],procneigh[0][1],procneigh[1][0],
                          procneigh[1][1],procneigh[2][0],procneigh[2][1]);
     cgtmp->ghost_notify();
     if (!cgtmp->ghost_overlap()) break;
     delete cgtmp;
 
     order--;
     iteration++;
   }
   
   if (order < minorder) error->all(FLERR,"PPPM order < minimum allowed order");
   if (!overlap_allowed && cgtmp->ghost_overlap())
     error->all(FLERR,"PPPM grid stencil extends "
                "beyond nearest neighbor processor");
   if (cgtmp) delete cgtmp;
 
   // adjust g_ewald
 
   if (!gewaldflag) adjust_gewald();
 
   // calculate the final accuracy
 
   double estimated_accuracy = final_accuracy();
 
   // print stats
 
   int ngrid_max,nfft_both_max;
   MPI_Allreduce(&ngrid,&ngrid_max,1,MPI_INT,MPI_MAX,world);
   MPI_Allreduce(&nfft_both,&nfft_both_max,1,MPI_INT,MPI_MAX,world);
 
   if (me == 0) {
 
 #ifdef FFT_SINGLE
     const char fft_prec[] = "single";
 #else
     const char fft_prec[] = "double";
 #endif
 
     if (screen) {
       fprintf(screen,"  G vector (1/distance) = %g\n",g_ewald);
       fprintf(screen,"  grid = %d %d %d\n",nx_pppm,ny_pppm,nz_pppm);
       fprintf(screen,"  stencil order = %d\n",order);
       fprintf(screen,"  estimated absolute RMS force accuracy = %g\n",
               estimated_accuracy);
       fprintf(screen,"  estimated relative force accuracy = %g\n",
               estimated_accuracy/two_charge_force);
       fprintf(screen,"  using %s precision FFTs\n",fft_prec);
       fprintf(screen,"  3d grid and FFT values/proc = %d %d\n",
               ngrid_max,nfft_both_max);
     }
     if (logfile) {
       fprintf(logfile,"  G vector (1/distance) = %g\n",g_ewald);
       fprintf(logfile,"  grid = %d %d %d\n",nx_pppm,ny_pppm,nz_pppm);
       fprintf(logfile,"  stencil order = %d\n",order);
       fprintf(logfile,"  estimated absolute RMS force accuracy = %g\n",
               estimated_accuracy);
       fprintf(logfile,"  estimated relative force accuracy = %g\n",
               estimated_accuracy/two_charge_force);
       fprintf(logfile,"  using %s precision FFTs\n",fft_prec);
       fprintf(logfile,"  3d grid and FFT values/proc = %d %d\n",
               ngrid_max,nfft_both_max);
     }
   }
 
   // allocate K-space dependent memory
   // don't invoke allocate peratom() or group(), will be allocated when needed
 
   allocate();
   cg->ghost_notify();
   cg->setup();
 
   // pre-compute Green's function denomiator expansion
   // pre-compute 1d charge distribution coefficients
 
   compute_gf_denom();
   if (differentiation_flag == 1) compute_sf_precoeff();
   compute_rho_coeff();
 }
 
 /* ----------------------------------------------------------------------
    adjust PPPM coeffs, called initially and whenever volume has changed
 ------------------------------------------------------------------------- */
 
 void PPPM::setup()
 {
   if (triclinic) {
     setup_triclinic();
     return;
   }
 
   int i,j,k,n;
   double *prd;
 
   // volume-dependent factors
   // adjust z dimension for 2d slab PPPM
   // z dimension for 3d PPPM is zprd since slab_volfactor = 1.0
 
   if (triclinic == 0) prd = domain->prd;
   else prd = domain->prd_lamda;
 
   double xprd = prd[0];
   double yprd = prd[1];
   double zprd = prd[2];
   double zprd_slab = zprd*slab_volfactor;
   volume = xprd * yprd * zprd_slab;
 
   delxinv = nx_pppm/xprd;
   delyinv = ny_pppm/yprd;
   delzinv = nz_pppm/zprd_slab;
 
   delvolinv = delxinv*delyinv*delzinv;
 
   double unitkx = (MY_2PI/xprd);
   double unitky = (MY_2PI/yprd);
   double unitkz = (MY_2PI/zprd_slab);
 
   // fkx,fky,fkz for my FFT grid pts
 
   double per;
 
   for (i = nxlo_fft; i <= nxhi_fft; i++) {
     per = i - nx_pppm*(2*i/nx_pppm);
     fkx[i] = unitkx*per;
   }
 
   for (i = nylo_fft; i <= nyhi_fft; i++) {
     per = i - ny_pppm*(2*i/ny_pppm);
     fky[i] = unitky*per;
   }
 
   for (i = nzlo_fft; i <= nzhi_fft; i++) {
     per = i - nz_pppm*(2*i/nz_pppm);
     fkz[i] = unitkz*per;
   }
 
   // virial coefficients
 
   double sqk,vterm;
 
   n = 0;
   for (k = nzlo_fft; k <= nzhi_fft; k++) {
     for (j = nylo_fft; j <= nyhi_fft; j++) {
       for (i = nxlo_fft; i <= nxhi_fft; i++) {
         sqk = fkx[i]*fkx[i] + fky[j]*fky[j] + fkz[k]*fkz[k];
         if (sqk == 0.0) {
           vg[n][0] = 0.0;
           vg[n][1] = 0.0;
           vg[n][2] = 0.0;
           vg[n][3] = 0.0;
           vg[n][4] = 0.0;
           vg[n][5] = 0.0;
         } else {
           vterm = -2.0 * (1.0/sqk + 0.25/(g_ewald*g_ewald));
           vg[n][0] = 1.0 + vterm*fkx[i]*fkx[i];
           vg[n][1] = 1.0 + vterm*fky[j]*fky[j];
           vg[n][2] = 1.0 + vterm*fkz[k]*fkz[k];
           vg[n][3] = vterm*fkx[i]*fky[j];
           vg[n][4] = vterm*fkx[i]*fkz[k];
           vg[n][5] = vterm*fky[j]*fkz[k];
         }
         n++;
       }
     }
   }
 
   if (differentiation_flag == 1) compute_gf_ad();
   else compute_gf_ik();
 }
 
 /* ----------------------------------------------------------------------
    adjust PPPM coeffs, called initially and whenever volume has changed
    for a triclinic system
 ------------------------------------------------------------------------- */
 
 void PPPM::setup_triclinic()
 {
   int i,j,k,n;
   double *prd;
 
   // volume-dependent factors
   // adjust z dimension for 2d slab PPPM
   // z dimension for 3d PPPM is zprd since slab_volfactor = 1.0
 
   prd = domain->prd;
 
   double xprd = prd[0];
   double yprd = prd[1];
   double zprd = prd[2];
   double zprd_slab = zprd*slab_volfactor;
   volume = xprd * yprd * zprd_slab;
 
   // use lamda (0-1) coordinates
 
   delxinv = nx_pppm;
   delyinv = ny_pppm;
   delzinv = nz_pppm;
   delvolinv = delxinv*delyinv*delzinv/volume;
 
   // fkx,fky,fkz for my FFT grid pts
 
   double per_i,per_j,per_k;
 
   n = 0;
   for (k = nzlo_fft; k <= nzhi_fft; k++) {
     per_k = k - nz_pppm*(2*k/nz_pppm);
     for (j = nylo_fft; j <= nyhi_fft; j++) {
       per_j = j - ny_pppm*(2*j/ny_pppm);
       for (i = nxlo_fft; i <= nxhi_fft; i++) {
         per_i = i - nx_pppm*(2*i/nx_pppm);
 
         double unitk_lamda[3];
         unitk_lamda[0] = 2.0*MY_PI*per_i;
         unitk_lamda[1] = 2.0*MY_PI*per_j;
         unitk_lamda[2] = 2.0*MY_PI*per_k;
         x2lamdaT(&unitk_lamda[0],&unitk_lamda[0]);
         fkx[n] = unitk_lamda[0];
         fky[n] = unitk_lamda[1];
         fkz[n] = unitk_lamda[2];
         n++;
       }
     }
   }
 
   // virial coefficients
 
   double sqk,vterm;
 
   for (n = 0; n < nfft; n++) {
     sqk = fkx[n]*fkx[n] + fky[n]*fky[n] + fkz[n]*fkz[n];
     if (sqk == 0.0) {
       vg[n][0] = 0.0;
       vg[n][1] = 0.0;
       vg[n][2] = 0.0;
       vg[n][3] = 0.0;
       vg[n][4] = 0.0;
       vg[n][5] = 0.0;
     } else {
       vterm = -2.0 * (1.0/sqk + 0.25/(g_ewald*g_ewald));
       vg[n][0] = 1.0 + vterm*fkx[n]*fkx[n];
       vg[n][1] = 1.0 + vterm*fky[n]*fky[n];
       vg[n][2] = 1.0 + vterm*fkz[n]*fkz[n];
       vg[n][3] = vterm*fkx[n]*fky[n];
       vg[n][4] = vterm*fkx[n]*fkz[n];
       vg[n][5] = vterm*fky[n]*fkz[n];
     }
   }
 
   compute_gf_ik_triclinic();
 }
 
 /* ----------------------------------------------------------------------
    reset local grid arrays and communication stencils
    called by fix balance b/c it changed sizes of processor sub-domains
 ------------------------------------------------------------------------- */
 
 void PPPM::setup_grid()
 {
   // free all arrays previously allocated
 
   deallocate();
   if (peratom_allocate_flag) deallocate_peratom();
   if (group_allocate_flag) deallocate_groups();
 
   // reset portion of global grid that each proc owns
 
   set_grid_local();
 
   // reallocate K-space dependent memory
   // check if grid communication is now overlapping if not allowed
   // don't invoke allocate peratom() or group(), will be allocated when needed
 
   allocate();
 
   cg->ghost_notify();
   if (overlap_allowed == 0 && cg->ghost_overlap())
     error->all(FLERR,"PPPM grid stencil extends "
                "beyond nearest neighbor processor");
   cg->setup();
 
   // pre-compute Green's function denomiator expansion
   // pre-compute 1d charge distribution coefficients
 
   compute_gf_denom();
   if (differentiation_flag == 1) compute_sf_precoeff();
   compute_rho_coeff();
 
   // pre-compute volume-dependent coeffs
 
   setup();
 }
 
 /* ----------------------------------------------------------------------
    compute the PPPM long-range force, energy, virial
 ------------------------------------------------------------------------- */
 
 void PPPM::compute(int eflag, int vflag)
 {
   int i,j;
 
   // set energy/virial flags
   // invoke allocate_peratom() if needed for first time
 
   if (eflag || vflag) ev_setup(eflag,vflag);
   else evflag = evflag_atom = eflag_global = vflag_global =
          eflag_atom = vflag_atom = 0;
 
   if (evflag_atom && !peratom_allocate_flag) {
     allocate_peratom();
     cg_peratom->ghost_notify();
     cg_peratom->setup();
   }
 
   // convert atoms from box to lamda coords
 
   if (triclinic == 0) boxlo = domain->boxlo;
   else {
     boxlo = domain->boxlo_lamda;
     domain->x2lamda(atom->nlocal);
   }
 
   // extend size of per-atom arrays if necessary
 
   if (atom->nlocal > nmax) {
     memory->destroy(part2grid);
     nmax = atom->nmax;
     memory->create(part2grid,nmax,3,"pppm:part2grid");
   }
 
   // find grid points for all my particles
   // map my particle charge onto my local 3d density grid
 
   particle_map();
   make_rho();
 
   // all procs communicate density values from their ghost cells
   //   to fully sum contribution in their 3d bricks
   // remap from 3d decomposition to FFT decomposition
 
   cg->reverse_comm(this,REVERSE_RHO);
   brick2fft();
 
   // compute potential gradient on my FFT grid and
   //   portion of e_long on this proc's FFT grid
   // return gradients (electric fields) in 3d brick decomposition
   // also performs per-atom calculations via poisson_peratom()
 
   poisson();
 
   // all procs communicate E-field values
   // to fill ghost cells surrounding their 3d bricks
 
   if (differentiation_flag == 1) cg->forward_comm(this,FORWARD_AD);
   else cg->forward_comm(this,FORWARD_IK);
 
   // extra per-atom energy/virial communication
 
   if (evflag_atom) {
     if (differentiation_flag == 1 && vflag_atom) 
       cg_peratom->forward_comm(this,FORWARD_AD_PERATOM);
     else if (differentiation_flag == 0)
       cg_peratom->forward_comm(this,FORWARD_IK_PERATOM);
   }
 
   // calculate the force on my particles
 
   fieldforce();
 
   // extra per-atom energy/virial communication
 
   if (evflag_atom) fieldforce_peratom();
 
   // sum global energy across procs and add in volume-dependent term
   // reset qsum and qsqsum if atom count has changed
 
   const double qscale = qqrd2e * scale;
 
   if (eflag_global) {
     double energy_all;
     MPI_Allreduce(&energy,&energy_all,1,MPI_DOUBLE,MPI_SUM,world);
     energy = energy_all;
 
     if (atom->natoms != natoms_original) {
       qsum_qsq(0);
       natoms_original = atom->natoms;
     }
 
     energy *= 0.5*volume;
     energy -= g_ewald*qsqsum/MY_PIS +
       MY_PI2*qsum*qsum / (g_ewald*g_ewald*volume);
     energy *= qscale;
   }
 
   // sum global virial across procs
 
   if (vflag_global) {
     double virial_all[6];
     MPI_Allreduce(virial,virial_all,6,MPI_DOUBLE,MPI_SUM,world);
     for (i = 0; i < 6; i++) virial[i] = 0.5*qscale*volume*virial_all[i];
   }
 
   // per-atom energy/virial
   // energy includes self-energy correction
   // notal accounts for TIP4P tallying eatom/vatom for ghost atoms
 
   if (evflag_atom) {
     double *q = atom->q;
     int nlocal = atom->nlocal;
     int ntotal = nlocal;
     if (tip4pflag) ntotal += atom->nghost;
 
     if (eflag_atom) {
       for (i = 0; i < nlocal; i++) {
         eatom[i] *= 0.5;
         eatom[i] -= g_ewald*q[i]*q[i]/MY_PIS + MY_PI2*q[i]*qsum /
           (g_ewald*g_ewald*volume);
         eatom[i] *= qscale;
       }
       for (i = nlocal; i < ntotal; i++) eatom[i] *= 0.5*qscale;
     }
 
     if (vflag_atom) {
       for (i = 0; i < ntotal; i++)
         for (j = 0; j < 6; j++) vatom[i][j] *= 0.5*qscale;
     }
   }
 
   // 2d slab correction
 
   if (slabflag == 1) slabcorr();
 
   // convert atoms back from lamda to box coords
 
   if (triclinic) domain->lamda2x(atom->nlocal);
 }
 
 /* ----------------------------------------------------------------------
    allocate memory that depends on # of K-vectors and order
 ------------------------------------------------------------------------- */
 
 void PPPM::allocate()
 {
   memory->create3d_offset(density_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out,
                           nxlo_out,nxhi_out,"pppm:density_brick");
 
   memory->create(density_fft,nfft_both,"pppm:density_fft");
   memory->create(greensfn,nfft_both,"pppm:greensfn");
   memory->create(work1,2*nfft_both,"pppm:work1");
   memory->create(work2,2*nfft_both,"pppm:work2");
   memory->create(vg,nfft_both,6,"pppm:vg");
 
   if (triclinic == 0) {
     memory->create1d_offset(fkx,nxlo_fft,nxhi_fft,"pppm:fkx");
     memory->create1d_offset(fky,nylo_fft,nyhi_fft,"pppm:fky");
     memory->create1d_offset(fkz,nzlo_fft,nzhi_fft,"pppm:fkz");
   } else {
     memory->create(fkx,nfft_both,"pppm:fkx");
     memory->create(fky,nfft_both,"pppm:fky");
     memory->create(fkz,nfft_both,"pppm:fkz");
   }
 
   if (differentiation_flag == 1) {
     memory->create3d_offset(u_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out,
                           nxlo_out,nxhi_out,"pppm:u_brick");
 
     memory->create(sf_precoeff1,nfft_both,"pppm:sf_precoeff1");
     memory->create(sf_precoeff2,nfft_both,"pppm:sf_precoeff2");
     memory->create(sf_precoeff3,nfft_both,"pppm:sf_precoeff3");
     memory->create(sf_precoeff4,nfft_both,"pppm:sf_precoeff4");
     memory->create(sf_precoeff5,nfft_both,"pppm:sf_precoeff5");
     memory->create(sf_precoeff6,nfft_both,"pppm:sf_precoeff6");
 
   } else {
     memory->create3d_offset(vdx_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out,
                             nxlo_out,nxhi_out,"pppm:vdx_brick");
     memory->create3d_offset(vdy_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out,
                             nxlo_out,nxhi_out,"pppm:vdy_brick");
     memory->create3d_offset(vdz_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out,
                             nxlo_out,nxhi_out,"pppm:vdz_brick");
   }
 
   // summation coeffs
 
   order_allocated = order;
   if (!stagger_flag) memory->create(gf_b,order,"pppm:gf_b");
   memory->create2d_offset(rho1d,3,-order/2,order/2,"pppm:rho1d");
   memory->create2d_offset(drho1d,3,-order/2,order/2,"pppm:drho1d");
   memory->create2d_offset(rho_coeff,order,(1-order)/2,order/2,"pppm:rho_coeff");
   memory->create2d_offset(drho_coeff,order,(1-order)/2,order/2,
                           "pppm:drho_coeff");
 
   // create 2 FFTs and a Remap
   // 1st FFT keeps data in FFT decompostion
   // 2nd FFT returns data in 3d brick decomposition
   // remap takes data from 3d brick to FFT decomposition
 
   int tmp;
 
   fft1 = new FFT3d(lmp,world,nx_pppm,ny_pppm,nz_pppm,
                    nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft,
                    nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft,
                    0,0,&tmp,collective_flag);
 
   fft2 = new FFT3d(lmp,world,nx_pppm,ny_pppm,nz_pppm,
                    nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft,
                    nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in,
                    0,0,&tmp,collective_flag);
 
   remap = new Remap(lmp,world,
                     nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in,
                     nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft,
                     1,0,0,FFT_PRECISION,collective_flag);
 
   // create ghost grid object for rho and electric field communication
 
   int (*procneigh)[2] = comm->procneigh;
 
   if (differentiation_flag == 1)
     cg = new GridComm(lmp,world,1,1,
                       nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in,
                       nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out,
                       procneigh[0][0],procneigh[0][1],procneigh[1][0],
                       procneigh[1][1],procneigh[2][0],procneigh[2][1]);
   else
     cg = new GridComm(lmp,world,3,1,
                       nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in,
                       nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out,
                       procneigh[0][0],procneigh[0][1],procneigh[1][0],
                       procneigh[1][1],procneigh[2][0],procneigh[2][1]);
 }
 
 /* ----------------------------------------------------------------------
    deallocate memory that depends on # of K-vectors and order
 ------------------------------------------------------------------------- */
 
 void PPPM::deallocate()
 {
   memory->destroy3d_offset(density_brick,nzlo_out,nylo_out,nxlo_out);
 
   if (differentiation_flag == 1) {
     memory->destroy3d_offset(u_brick,nzlo_out,nylo_out,nxlo_out);
     memory->destroy(sf_precoeff1);
     memory->destroy(sf_precoeff2);
     memory->destroy(sf_precoeff3);
     memory->destroy(sf_precoeff4);
     memory->destroy(sf_precoeff5);
     memory->destroy(sf_precoeff6);
   } else {
     memory->destroy3d_offset(vdx_brick,nzlo_out,nylo_out,nxlo_out);
     memory->destroy3d_offset(vdy_brick,nzlo_out,nylo_out,nxlo_out);
     memory->destroy3d_offset(vdz_brick,nzlo_out,nylo_out,nxlo_out);
   }
 
   memory->destroy(density_fft);
   memory->destroy(greensfn);
   memory->destroy(work1);
   memory->destroy(work2);
   memory->destroy(vg);
 
   if (triclinic == 0) {
     memory->destroy1d_offset(fkx,nxlo_fft);
     memory->destroy1d_offset(fky,nylo_fft);
     memory->destroy1d_offset(fkz,nzlo_fft);
   } else {
     memory->destroy(fkx);
     memory->destroy(fky);
     memory->destroy(fkz);
   }
 
   memory->destroy(gf_b);
   if (stagger_flag) gf_b = NULL;
   memory->destroy2d_offset(rho1d,-order_allocated/2);
   memory->destroy2d_offset(drho1d,-order_allocated/2);
   memory->destroy2d_offset(rho_coeff,(1-order_allocated)/2);
   memory->destroy2d_offset(drho_coeff,(1-order_allocated)/2);
 
   delete fft1;
   delete fft2;
   delete remap;
   delete cg;
 }
 
 /* ----------------------------------------------------------------------
    allocate per-atom memory that depends on # of K-vectors and order
 ------------------------------------------------------------------------- */
 
 void PPPM::allocate_peratom()
 {
   peratom_allocate_flag = 1;
 
   if (differentiation_flag != 1)
     memory->create3d_offset(u_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out,
                             nxlo_out,nxhi_out,"pppm:u_brick");
 
   memory->create3d_offset(v0_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out,
                           nxlo_out,nxhi_out,"pppm:v0_brick");
 
   memory->create3d_offset(v1_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out,
                           nxlo_out,nxhi_out,"pppm:v1_brick");
   memory->create3d_offset(v2_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out,
                           nxlo_out,nxhi_out,"pppm:v2_brick");
   memory->create3d_offset(v3_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out,
                           nxlo_out,nxhi_out,"pppm:v3_brick");
   memory->create3d_offset(v4_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out,
                           nxlo_out,nxhi_out,"pppm:v4_brick");
   memory->create3d_offset(v5_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out,
                           nxlo_out,nxhi_out,"pppm:v5_brick");
 
   // create ghost grid object for rho and electric field communication
 
   int (*procneigh)[2] = comm->procneigh;
 
   if (differentiation_flag == 1)
     cg_peratom =
       new GridComm(lmp,world,6,1,
                    nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in,
                    nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out,
                    procneigh[0][0],procneigh[0][1],procneigh[1][0],
                    procneigh[1][1],procneigh[2][0],procneigh[2][1]);
   else
     cg_peratom =
       new GridComm(lmp,world,7,1,
                    nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in,
                    nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out,
                    procneigh[0][0],procneigh[0][1],procneigh[1][0],
                    procneigh[1][1],procneigh[2][0],procneigh[2][1]);
 }
 
 /* ----------------------------------------------------------------------
    deallocate per-atom memory that depends on # of K-vectors and order
 ------------------------------------------------------------------------- */
 
 void PPPM::deallocate_peratom()
 {
   peratom_allocate_flag = 0;
 
   memory->destroy3d_offset(v0_brick,nzlo_out,nylo_out,nxlo_out);
   memory->destroy3d_offset(v1_brick,nzlo_out,nylo_out,nxlo_out);
   memory->destroy3d_offset(v2_brick,nzlo_out,nylo_out,nxlo_out);
   memory->destroy3d_offset(v3_brick,nzlo_out,nylo_out,nxlo_out);
   memory->destroy3d_offset(v4_brick,nzlo_out,nylo_out,nxlo_out);
   memory->destroy3d_offset(v5_brick,nzlo_out,nylo_out,nxlo_out);
 
   if (differentiation_flag != 1)
     memory->destroy3d_offset(u_brick,nzlo_out,nylo_out,nxlo_out);
 
   delete cg_peratom;
 }
 
 /* ----------------------------------------------------------------------
    set global size of PPPM grid = nx,ny,nz_pppm
    used for charge accumulation, FFTs, and electric field interpolation
 ------------------------------------------------------------------------- */
 
 void PPPM::set_grid_global()
 {
   // use xprd,yprd,zprd (even if triclinic, and then scale later)
   // adjust z dimension for 2d slab PPPM
   // 3d PPPM just uses zprd since slab_volfactor = 1.0
 
   double xprd = domain->xprd;
   double yprd = domain->yprd;
   double zprd = domain->zprd;
   double zprd_slab = zprd*slab_volfactor;
 
   // make initial g_ewald estimate
   // based on desired accuracy and real space cutoff
   // fluid-occupied volume used to estimate real-space error
   // zprd used rather than zprd_slab
 
   double h;
   bigint natoms = atom->natoms;
 
   if (!gewaldflag) {
     if (accuracy <= 0.0)
       error->all(FLERR,"KSpace accuracy must be > 0");
     g_ewald = accuracy*sqrt(natoms*cutoff*xprd*yprd*zprd) / (2.0*q2);
     if (g_ewald >= 1.0) g_ewald = (1.35 - 0.15*log(accuracy))/cutoff;
     else g_ewald = sqrt(-log(g_ewald)) / cutoff;
   }
 
   // set optimal nx_pppm,ny_pppm,nz_pppm based on order and accuracy
   // nz_pppm uses extended zprd_slab instead of zprd
   // reduce it until accuracy target is met
 
   if (!gridflag) {
 
     if (differentiation_flag == 1 || stagger_flag) {
 
       h = h_x = h_y = h_z = 4.0/g_ewald;
       int count = 0;
       while (1) {
 
         // set grid dimension
         nx_pppm = static_cast<int> (xprd/h_x);
         ny_pppm = static_cast<int> (yprd/h_y);
         nz_pppm = static_cast<int> (zprd_slab/h_z);
 
         if (nx_pppm <= 1) nx_pppm = 2;
         if (ny_pppm <= 1) ny_pppm = 2;
         if (nz_pppm <= 1) nz_pppm = 2;
 
         //set local grid dimension
         int npey_fft,npez_fft;
         if (nz_pppm >= nprocs) {
           npey_fft = 1;
           npez_fft = nprocs;
         } else procs2grid2d(nprocs,ny_pppm,nz_pppm,&npey_fft,&npez_fft);
 
         int me_y = me % npey_fft;
         int me_z = me / npey_fft;
 
         nxlo_fft = 0;
         nxhi_fft = nx_pppm - 1;
         nylo_fft = me_y*ny_pppm/npey_fft;
         nyhi_fft = (me_y+1)*ny_pppm/npey_fft - 1;
         nzlo_fft = me_z*nz_pppm/npez_fft;
         nzhi_fft = (me_z+1)*nz_pppm/npez_fft - 1;
 
         double df_kspace = compute_df_kspace();
 
         count++;
 
         // break loop if the accuracy has been reached or
         // too many loops have been performed
 
         if (df_kspace <= accuracy) break;
         if (count > 500) error->all(FLERR, "Could not compute grid size");
         h *= 0.95;
         h_x = h_y = h_z = h;
       }
 
     } else {
 
       double err;
       h_x = h_y = h_z = 1.0/g_ewald;
 
       nx_pppm = static_cast<int> (xprd/h_x) + 1;
       ny_pppm = static_cast<int> (yprd/h_y) + 1;
       nz_pppm = static_cast<int> (zprd_slab/h_z) + 1;
 
       err = estimate_ik_error(h_x,xprd,natoms);
       while (err > accuracy) {
         err = estimate_ik_error(h_x,xprd,natoms);
         nx_pppm++;
         h_x = xprd/nx_pppm;
       }
 
       err = estimate_ik_error(h_y,yprd,natoms);
       while (err > accuracy) {
         err = estimate_ik_error(h_y,yprd,natoms);
         ny_pppm++;
         h_y = yprd/ny_pppm;
       }
 
       err = estimate_ik_error(h_z,zprd_slab,natoms);
       while (err > accuracy) {
         err = estimate_ik_error(h_z,zprd_slab,natoms);
         nz_pppm++;
         h_z = zprd_slab/nz_pppm;
       }
     }
 
     // scale grid for triclinic skew
     
     if (triclinic) {
       double tmp[3];
       tmp[0] = nx_pppm/xprd;
       tmp[1] = ny_pppm/yprd;
       tmp[2] = nz_pppm/zprd;
       lamda2xT(&tmp[0],&tmp[0]);
       nx_pppm = static_cast<int>(tmp[0]) + 1;
       ny_pppm = static_cast<int>(tmp[1]) + 1;
       nz_pppm = static_cast<int>(tmp[2]) + 1;
     }
   }
 
   // boost grid size until it is factorable
 
   while (!factorable(nx_pppm)) nx_pppm++;
   while (!factorable(ny_pppm)) ny_pppm++;
   while (!factorable(nz_pppm)) nz_pppm++;
 
   if (triclinic == 0) {
     h_x = xprd/nx_pppm;
     h_y = yprd/ny_pppm;
     h_z = zprd_slab/nz_pppm;
   } else {
     double tmp[3];
     tmp[0] = nx_pppm;
     tmp[1] = ny_pppm;
     tmp[2] = nz_pppm;
     x2lamdaT(&tmp[0],&tmp[0]);
     h_x = 1.0/tmp[0];
     h_y = 1.0/tmp[1];
     h_z = 1.0/tmp[2];
   }
 
   if (nx_pppm >= OFFSET || ny_pppm >= OFFSET || nz_pppm >= OFFSET)
     error->all(FLERR,"PPPM grid is too large");
 }
 
 /* ----------------------------------------------------------------------
    check if all factors of n are in list of factors
    return 1 if yes, 0 if no
 ------------------------------------------------------------------------- */
 
 int PPPM::factorable(int n)
 {
   int i;
 
   while (n > 1) {
     for (i = 0; i < nfactors; i++) {
       if (n % factors[i] == 0) {
         n /= factors[i];
         break;
       }
     }
     if (i == nfactors) return 0;
   }
 
   return 1;
 }
 
 /* ----------------------------------------------------------------------
    compute estimated kspace force error
 ------------------------------------------------------------------------- */
 
 double PPPM::compute_df_kspace()
 {
   double xprd = domain->xprd;
   double yprd = domain->yprd;
   double zprd = domain->zprd;
   double zprd_slab = zprd*slab_volfactor;
   bigint natoms = atom->natoms;
   double df_kspace = 0.0;
   if (differentiation_flag == 1 || stagger_flag) {
     double qopt = compute_qopt();
     df_kspace = sqrt(qopt/natoms)*q2/(xprd*yprd*zprd_slab);
   } else {
     double lprx = estimate_ik_error(h_x,xprd,natoms);
     double lpry = estimate_ik_error(h_y,yprd,natoms);
     double lprz = estimate_ik_error(h_z,zprd_slab,natoms);
     df_kspace = sqrt(lprx*lprx + lpry*lpry + lprz*lprz) / sqrt(3.0);
   }
   return df_kspace;
 }
 
 /* ----------------------------------------------------------------------
    compute qopt
 ------------------------------------------------------------------------- */
 
 double PPPM::compute_qopt()
 {
   double qopt = 0.0;
   double *prd = domain->prd;
   
   const double xprd = prd[0];
   const double yprd = prd[1];
   const double zprd = prd[2];
   const double zprd_slab = zprd*slab_volfactor;
   volume = xprd * yprd * zprd_slab;
 
   const double unitkx = (MY_2PI/xprd);
   const double unitky = (MY_2PI/yprd);
   const double unitkz = (MY_2PI/zprd_slab);
 
   double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz;
   double u1, u2, sqk;
   double sum1,sum2,sum3,sum4,dot2;
 
   int k,l,m,nx,ny,nz;
   const int twoorder = 2*order;
 
   for (m = nzlo_fft; m <= nzhi_fft; m++) {
     const int mper = m - nz_pppm*(2*m/nz_pppm);
 
     for (l = nylo_fft; l <= nyhi_fft; l++) {
       const int lper = l - ny_pppm*(2*l/ny_pppm);
 
       for (k = nxlo_fft; k <= nxhi_fft; k++) {
         const int kper = k - nx_pppm*(2*k/nx_pppm);
 
         sqk = square(unitkx*kper) + square(unitky*lper) + square(unitkz*mper);
 
         if (sqk != 0.0) {
 
           sum1 = 0.0;
           sum2 = 0.0;
           sum3 = 0.0;
           sum4 = 0.0;
           for (nx = -2; nx <= 2; nx++) {
             qx = unitkx*(kper+nx_pppm*nx);
             sx = exp(-0.25*square(qx/g_ewald));
             argx = 0.5*qx*xprd/nx_pppm;
             wx = powsinxx(argx,twoorder);
             qx *= qx;
 
             for (ny = -2; ny <= 2; ny++) {
               qy = unitky*(lper+ny_pppm*ny);
               sy = exp(-0.25*square(qy/g_ewald));
               argy = 0.5*qy*yprd/ny_pppm;
               wy = powsinxx(argy,twoorder);
               qy *= qy;
 
               for (nz = -2; nz <= 2; nz++) {
                 qz = unitkz*(mper+nz_pppm*nz);
                 sz = exp(-0.25*square(qz/g_ewald));
                 argz = 0.5*qz*zprd_slab/nz_pppm;
                 wz = powsinxx(argz,twoorder);
                 qz *= qz;
 
                 dot2 = qx+qy+qz;
                 u1   = sx*sy*sz;
                 u2   = wx*wy*wz;
                 sum1 += u1*u1/dot2*MY_4PI*MY_4PI;
                 sum2 += u1 * u2 * MY_4PI;
                 sum3 += u2;
                 sum4 += dot2*u2;
               }
             }
           }
           sum2 *= sum2;
           qopt += sum1 - sum2/(sum3*sum4);
         }
       }
     }
   }
   double qopt_all;
   MPI_Allreduce(&qopt,&qopt_all,1,MPI_DOUBLE,MPI_SUM,world);
   return qopt_all;
 }
 
 /* ----------------------------------------------------------------------
    estimate kspace force error for ik method
 ------------------------------------------------------------------------- */
 
 double PPPM::estimate_ik_error(double h, double prd, bigint natoms)
 {
   double sum = 0.0;
   for (int m = 0; m < order; m++)
     sum += acons[order][m] * pow(h*g_ewald,2.0*m);
   double value = q2 * pow(h*g_ewald,(double)order) *
     sqrt(g_ewald*prd*sqrt(MY_2PI)*sum/natoms) / (prd*prd);
 
   return value;
 }
 
 /* ----------------------------------------------------------------------
    adjust the g_ewald parameter to near its optimal value
    using a Newton-Raphson solver
 ------------------------------------------------------------------------- */
 
 void PPPM::adjust_gewald()
 {
   double dx;
 
   for (int i = 0; i < LARGE; i++) {
     dx = newton_raphson_f() / derivf();
     g_ewald -= dx;
     if (fabs(newton_raphson_f()) < SMALL) return;
   }
 
   char str[128];
   sprintf(str, "Could not compute g_ewald");
   error->all(FLERR, str);
 }
 
 /* ----------------------------------------------------------------------
    calculate f(x) using Newton-Raphson solver
 ------------------------------------------------------------------------- */
 
 double PPPM::newton_raphson_f()
 {
   double xprd = domain->xprd;
   double yprd = domain->yprd;
   double zprd = domain->zprd;
   bigint natoms = atom->natoms;
 
   double df_rspace = 2.0*q2*exp(-g_ewald*g_ewald*cutoff*cutoff) /
        sqrt(natoms*cutoff*xprd*yprd*zprd);
 
   double df_kspace = compute_df_kspace();
 
   return df_rspace - df_kspace;
 }
 
 /* ----------------------------------------------------------------------
    calculate numerical derivative f'(x) using forward difference
    [f(x + h) - f(x)] / h
 ------------------------------------------------------------------------- */
 
 double PPPM::derivf()
 {
   double h = 0.000001;  //Derivative step-size
   double df,f1,f2,g_ewald_old;
 
   f1 = newton_raphson_f();
   g_ewald_old = g_ewald;
   g_ewald += h;
   f2 = newton_raphson_f();
   g_ewald = g_ewald_old;
   df = (f2 - f1)/h;
 
   return df;
 }
 
 /* ----------------------------------------------------------------------
    calculate the final estimate of the accuracy
 ------------------------------------------------------------------------- */
 
 double PPPM::final_accuracy()
 {
   double xprd = domain->xprd;
   double yprd = domain->yprd;
   double zprd = domain->zprd;
   bigint natoms = atom->natoms;
 
   double df_kspace = compute_df_kspace();
   double q2_over_sqrt = q2 / sqrt(natoms*cutoff*xprd*yprd*zprd);
   double df_rspace = 2.0 * q2_over_sqrt * exp(-g_ewald*g_ewald*cutoff*cutoff);
   double df_table = estimate_table_accuracy(q2_over_sqrt,df_rspace);
   double estimated_accuracy = sqrt(df_kspace*df_kspace + df_rspace*df_rspace +
                                    df_table*df_table);
 
   return estimated_accuracy;
 }
 
 /* ----------------------------------------------------------------------
    set local subset of PPPM/FFT grid that I own
    n xyz lo/hi in = 3d brick that I own (inclusive)
    n xyz lo/hi out = 3d brick + ghost cells in 6 directions (inclusive)
    n xyz lo/hi fft = FFT columns that I own (all of x dim, 2d decomp in yz)
 ------------------------------------------------------------------------- */
 
 void PPPM::set_grid_local()
 {
   // global indices of PPPM grid range from 0 to N-1
   // nlo_in,nhi_in = lower/upper limits of the 3d sub-brick of
   //   global PPPM grid that I own without ghost cells
   // for slab PPPM, assign z grid as if it were not extended
 
   nxlo_in = static_cast<int> (comm->xsplit[comm->myloc[0]] * nx_pppm);
   nxhi_in = static_cast<int> (comm->xsplit[comm->myloc[0]+1] * nx_pppm) - 1;
 
   nylo_in = static_cast<int> (comm->ysplit[comm->myloc[1]] * ny_pppm);
   nyhi_in = static_cast<int> (comm->ysplit[comm->myloc[1]+1] * ny_pppm) - 1;
 
   nzlo_in = static_cast<int>
       (comm->zsplit[comm->myloc[2]] * nz_pppm/slab_volfactor);
   nzhi_in = static_cast<int>
       (comm->zsplit[comm->myloc[2]+1] * nz_pppm/slab_volfactor) - 1;
 
   // nlower,nupper = stencil size for mapping particles to PPPM grid
 
   nlower = -(order-1)/2;
   nupper = order/2;
 
   // shift values for particle <-> grid mapping
   // add/subtract OFFSET to avoid int(-0.75) = 0 when want it to be -1
 
   if (order % 2) shift = OFFSET + 0.5;
   else shift = OFFSET;
   if (order % 2) shiftone = 0.0;
   else shiftone = 0.5;
 
   // nlo_out,nhi_out = lower/upper limits of the 3d sub-brick of
   //   global PPPM grid that my particles can contribute charge to
   // effectively nlo_in,nhi_in + ghost cells
   // nlo,nhi = global coords of grid pt to "lower left" of smallest/largest
   //           position a particle in my box can be at
   // dist[3] = particle position bound = subbox + skin/2.0 + qdist
   //   qdist = offset due to TIP4P fictitious charge
   //   convert to triclinic if necessary
   // nlo_out,nhi_out = nlo,nhi + stencil size for particle mapping
   // for slab PPPM, assign z grid as if it were not extended
 
   double *prd,*sublo,*subhi;
 
   if (triclinic == 0) {
     prd = domain->prd;
     boxlo = domain->boxlo;
     sublo = domain->sublo;
     subhi = domain->subhi;
   } else {
     prd = domain->prd_lamda;
     boxlo = domain->boxlo_lamda;
     sublo = domain->sublo_lamda;
     subhi = domain->subhi_lamda;
   }
 
   double xprd = prd[0];
   double yprd = prd[1];
   double zprd = prd[2];
   double zprd_slab = zprd*slab_volfactor;
 
   double dist[3];
   double cuthalf = 0.5*neighbor->skin + qdist;
   if (triclinic == 0) dist[0] = dist[1] = dist[2] = cuthalf;
   else kspacebbox(cuthalf,&dist[0]);
 
   int nlo,nhi;
 
   nlo = static_cast<int> ((sublo[0]-dist[0]-boxlo[0]) *
                             nx_pppm/xprd + shift) - OFFSET;
   nhi = static_cast<int> ((subhi[0]+dist[0]-boxlo[0]) *
                             nx_pppm/xprd + shift) - OFFSET;
   nxlo_out = nlo + nlower;
   nxhi_out = nhi + nupper;
 
   nlo = static_cast<int> ((sublo[1]-dist[1]-boxlo[1]) *
                             ny_pppm/yprd + shift) - OFFSET;
   nhi = static_cast<int> ((subhi[1]+dist[1]-boxlo[1]) *
                             ny_pppm/yprd + shift) - OFFSET;
   nylo_out = nlo + nlower;
   nyhi_out = nhi + nupper;
 
   nlo = static_cast<int> ((sublo[2]-dist[2]-boxlo[2]) *
                             nz_pppm/zprd_slab + shift) - OFFSET;
   nhi = static_cast<int> ((subhi[2]+dist[2]-boxlo[2]) *
                             nz_pppm/zprd_slab + shift) - OFFSET;
   nzlo_out = nlo + nlower;
   nzhi_out = nhi + nupper;
 
   if (stagger_flag) {
     nxhi_out++;
     nyhi_out++;
     nzhi_out++;
   }
 
   // for slab PPPM, change the grid boundary for processors at +z end
   //   to include the empty volume between periodically repeating slabs
   // for slab PPPM, want charge data communicated from -z proc to +z proc,
   //   but not vice versa, also want field data communicated from +z proc to
   //   -z proc, but not vice versa
   // this is accomplished by nzhi_in = nzhi_out on +z end (no ghost cells)
   // also insure no other procs use ghost cells beyond +z limit
 
   if (slabflag == 1) {
     if (comm->myloc[2] == comm->procgrid[2]-1)
       nzhi_in = nzhi_out = nz_pppm - 1;
     nzhi_out = MIN(nzhi_out,nz_pppm-1);
   }
     
   // decomposition of FFT mesh
   // global indices range from 0 to N-1
   // proc owns entire x-dimension, clumps of columns in y,z dimensions
   // npey_fft,npez_fft = # of procs in y,z dims
   // if nprocs is small enough, proc can own 1 or more entire xy planes,
   //   else proc owns 2d sub-blocks of yz plane
   // me_y,me_z = which proc (0-npe_fft-1) I am in y,z dimensions
   // nlo_fft,nhi_fft = lower/upper limit of the section
   //   of the global FFT mesh that I own
 
   int npey_fft,npez_fft;
   if (nz_pppm >= nprocs) {
     npey_fft = 1;
     npez_fft = nprocs;
   } else procs2grid2d(nprocs,ny_pppm,nz_pppm,&npey_fft,&npez_fft);
 
   int me_y = me % npey_fft;
   int me_z = me / npey_fft;
 
   nxlo_fft = 0;
   nxhi_fft = nx_pppm - 1;
   nylo_fft = me_y*ny_pppm/npey_fft;
   nyhi_fft = (me_y+1)*ny_pppm/npey_fft - 1;
   nzlo_fft = me_z*nz_pppm/npez_fft;
   nzhi_fft = (me_z+1)*nz_pppm/npez_fft - 1;
 
   // PPPM grid pts owned by this proc, including ghosts
 
   ngrid = (nxhi_out-nxlo_out+1) * (nyhi_out-nylo_out+1) *
     (nzhi_out-nzlo_out+1);
 
   // FFT grids owned by this proc, without ghosts
   // nfft = FFT points in FFT decomposition on this proc
   // nfft_brick = FFT points in 3d brick-decomposition on this proc
   // nfft_both = greater of 2 values
 
   nfft = (nxhi_fft-nxlo_fft+1) * (nyhi_fft-nylo_fft+1) *
     (nzhi_fft-nzlo_fft+1);
   int nfft_brick = (nxhi_in-nxlo_in+1) * (nyhi_in-nylo_in+1) *
     (nzhi_in-nzlo_in+1);
   nfft_both = MAX(nfft,nfft_brick);
 }
 
 /* ----------------------------------------------------------------------
    pre-compute Green's function denominator expansion coeffs, Gamma(2n)
 ------------------------------------------------------------------------- */
 
 void PPPM::compute_gf_denom()
 {
   int k,l,m;
 
   for (l = 1; l < order; l++) gf_b[l] = 0.0;
   gf_b[0] = 1.0;
 
   for (m = 1; m < order; m++) {
     for (l = m; l > 0; l--)
       gf_b[l] = 4.0 * (gf_b[l]*(l-m)*(l-m-0.5)-gf_b[l-1]*(l-m-1)*(l-m-1));
     gf_b[0] = 4.0 * (gf_b[0]*(l-m)*(l-m-0.5));
   }
 
   bigint ifact = 1;
   for (k = 1; k < 2*order; k++) ifact *= k;
   double gaminv = 1.0/ifact;
   for (l = 0; l < order; l++) gf_b[l] *= gaminv;
 }
 
 /* ----------------------------------------------------------------------
    pre-compute modified (Hockney-Eastwood) Coulomb Green's function
 ------------------------------------------------------------------------- */
 
 void PPPM::compute_gf_ik()
 {
   const double * const prd = domain->prd;
 
   const double xprd = prd[0];
   const double yprd = prd[1];
   const double zprd = prd[2];
   const double zprd_slab = zprd*slab_volfactor;
   const double unitkx = (MY_2PI/xprd);
   const double unitky = (MY_2PI/yprd);
   const double unitkz = (MY_2PI/zprd_slab);
 
   double snx,sny,snz;
   double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz;
   double sum1,dot1,dot2;
   double numerator,denominator;
   double sqk;
 
   int k,l,m,n,nx,ny,nz,kper,lper,mper;
 
   const int nbx = static_cast<int> ((g_ewald*xprd/(MY_PI*nx_pppm)) *
                                     pow(-log(EPS_HOC),0.25));
   const int nby = static_cast<int> ((g_ewald*yprd/(MY_PI*ny_pppm)) *
                                     pow(-log(EPS_HOC),0.25));
   const int nbz = static_cast<int> ((g_ewald*zprd_slab/(MY_PI*nz_pppm)) *
                                     pow(-log(EPS_HOC),0.25));
   const int twoorder = 2*order;
 
   n = 0;
   for (m = nzlo_fft; m <= nzhi_fft; m++) {
     mper = m - nz_pppm*(2*m/nz_pppm);
     snz = square(sin(0.5*unitkz*mper*zprd_slab/nz_pppm));
 
     for (l = nylo_fft; l <= nyhi_fft; l++) {
       lper = l - ny_pppm*(2*l/ny_pppm);
       sny = square(sin(0.5*unitky*lper*yprd/ny_pppm));
 
       for (k = nxlo_fft; k <= nxhi_fft; k++) {
         kper = k - nx_pppm*(2*k/nx_pppm);
         snx = square(sin(0.5*unitkx*kper*xprd/nx_pppm));
 
         sqk = square(unitkx*kper) + square(unitky*lper) + square(unitkz*mper);
 
         if (sqk != 0.0) {
           numerator = 12.5663706/sqk;
           denominator = gf_denom(snx,sny,snz);
           sum1 = 0.0;
 
           for (nx = -nbx; nx <= nbx; nx++) {
             qx = unitkx*(kper+nx_pppm*nx);
             sx = exp(-0.25*square(qx/g_ewald));
             argx = 0.5*qx*xprd/nx_pppm;
             wx = powsinxx(argx,twoorder);
 
             for (ny = -nby; ny <= nby; ny++) {
               qy = unitky*(lper+ny_pppm*ny);
               sy = exp(-0.25*square(qy/g_ewald));
               argy = 0.5*qy*yprd/ny_pppm;
               wy = powsinxx(argy,twoorder);
 
               for (nz = -nbz; nz <= nbz; nz++) {
                 qz = unitkz*(mper+nz_pppm*nz);
                 sz = exp(-0.25*square(qz/g_ewald));
                 argz = 0.5*qz*zprd_slab/nz_pppm;
                 wz = powsinxx(argz,twoorder);
 
                 dot1 = unitkx*kper*qx + unitky*lper*qy + unitkz*mper*qz;
                 dot2 = qx*qx+qy*qy+qz*qz;
                 sum1 += (dot1/dot2) * sx*sy*sz * wx*wy*wz;
               }
             }
           }
           greensfn[n++] = numerator*sum1/denominator;
         } else greensfn[n++] = 0.0;
       }
     }
   }
 }
 
 /* ----------------------------------------------------------------------
    pre-compute modified (Hockney-Eastwood) Coulomb Green's function
    for a triclinic system
 ------------------------------------------------------------------------- */
 
 void PPPM::compute_gf_ik_triclinic()
 {
   double snx,sny,snz;
   double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz;
   double sum1,dot1,dot2;
   double numerator,denominator;
   double sqk;
 
   int k,l,m,n,nx,ny,nz,kper,lper,mper;
 
   double tmp[3];
   tmp[0] = (g_ewald/(MY_PI*nx_pppm)) * pow(-log(EPS_HOC),0.25);
   tmp[1] = (g_ewald/(MY_PI*ny_pppm)) * pow(-log(EPS_HOC),0.25);
   tmp[2] = (g_ewald/(MY_PI*nz_pppm)) * pow(-log(EPS_HOC),0.25);
   lamda2xT(&tmp[0],&tmp[0]);
   const int nbx = static_cast<int> (tmp[0]);
   const int nby = static_cast<int> (tmp[1]);
   const int nbz = static_cast<int> (tmp[2]);
 
   const int twoorder = 2*order;
 
   n = 0;
   for (m = nzlo_fft; m <= nzhi_fft; m++) {
     mper = m - nz_pppm*(2*m/nz_pppm);
     snz = square(sin(MY_PI*mper/nz_pppm));
 
     for (l = nylo_fft; l <= nyhi_fft; l++) {
       lper = l - ny_pppm*(2*l/ny_pppm);
       sny = square(sin(MY_PI*lper/ny_pppm));
 
       for (k = nxlo_fft; k <= nxhi_fft; k++) {
         kper = k - nx_pppm*(2*k/nx_pppm);
         snx = square(sin(MY_PI*kper/nx_pppm));
 
         double unitk_lamda[3];
         unitk_lamda[0] = 2.0*MY_PI*kper;
         unitk_lamda[1] = 2.0*MY_PI*lper;
         unitk_lamda[2] = 2.0*MY_PI*mper;
         x2lamdaT(&unitk_lamda[0],&unitk_lamda[0]);
 
         sqk = square(unitk_lamda[0]) + square(unitk_lamda[1]) + square(unitk_lamda[2]);
 
         if (sqk != 0.0) {
           numerator = 12.5663706/sqk;
           denominator = gf_denom(snx,sny,snz);
           sum1 = 0.0;
 
           for (nx = -nbx; nx <= nbx; nx++) {
             argx = MY_PI*kper/nx_pppm + MY_PI*nx;
             wx = powsinxx(argx,twoorder);
 
             for (ny = -nby; ny <= nby; ny++) {
               argy = MY_PI*lper/ny_pppm + MY_PI*ny;
               wy = powsinxx(argy,twoorder);
 
               for (nz = -nbz; nz <= nbz; nz++) {
                 argz = MY_PI*mper/nz_pppm + MY_PI*nz;
                 wz = powsinxx(argz,twoorder);
 
                 double b[3];
                 b[0] = 2.0*MY_PI*nx_pppm*nx;
                 b[1] = 2.0*MY_PI*ny_pppm*ny;
                 b[2] = 2.0*MY_PI*nz_pppm*nz;
                 x2lamdaT(&b[0],&b[0]);
 
                 qx = unitk_lamda[0]+b[0];
                 sx = exp(-0.25*square(qx/g_ewald));
 
                 qy = unitk_lamda[1]+b[1];
                 sy = exp(-0.25*square(qy/g_ewald));
 
                 qz = unitk_lamda[2]+b[2];
                 sz = exp(-0.25*square(qz/g_ewald));
 
                 dot1 = unitk_lamda[0]*qx + unitk_lamda[1]*qy + unitk_lamda[2]*qz;
                 dot2 = qx*qx+qy*qy+qz*qz;
                 sum1 += (dot1/dot2) * sx*sy*sz * wx*wy*wz;
               }
             }
           }
           greensfn[n++] = numerator*sum1/denominator;
         } else greensfn[n++] = 0.0;
       }
     }
   }
 }
 
 /* ----------------------------------------------------------------------
    compute optimized Green's function for energy calculation
 ------------------------------------------------------------------------- */
 
 void PPPM::compute_gf_ad()
 {
   const double * const prd = domain->prd;
 
   const double xprd = prd[0];
   const double yprd = prd[1];
   const double zprd = prd[2];
   const double zprd_slab = zprd*slab_volfactor;
   const double unitkx = (MY_2PI/xprd);
   const double unitky = (MY_2PI/yprd);
   const double unitkz = (MY_2PI/zprd_slab);
 
   double snx,sny,snz,sqk;
   double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz;
   double numerator,denominator;
   int k,l,m,n,kper,lper,mper;
 
   const int twoorder = 2*order;
 
   for (int i = 0; i < 6; i++) sf_coeff[i] = 0.0;
 
   n = 0;
   for (m = nzlo_fft; m <= nzhi_fft; m++) {
     mper = m - nz_pppm*(2*m/nz_pppm);
     qz = unitkz*mper;
     snz = square(sin(0.5*qz*zprd_slab/nz_pppm));
     sz = exp(-0.25*square(qz/g_ewald));
     argz = 0.5*qz*zprd_slab/nz_pppm;
     wz = powsinxx(argz,twoorder);
 
     for (l = nylo_fft; l <= nyhi_fft; l++) {
       lper = l - ny_pppm*(2*l/ny_pppm);
       qy = unitky*lper;
       sny = square(sin(0.5*qy*yprd/ny_pppm));
       sy = exp(-0.25*square(qy/g_ewald));
       argy = 0.5*qy*yprd/ny_pppm;
       wy = powsinxx(argy,twoorder);
 
       for (k = nxlo_fft; k <= nxhi_fft; k++) {
         kper = k - nx_pppm*(2*k/nx_pppm);
         qx = unitkx*kper;
         snx = square(sin(0.5*qx*xprd/nx_pppm));
         sx = exp(-0.25*square(qx/g_ewald));
         argx = 0.5*qx*xprd/nx_pppm;
         wx = powsinxx(argx,twoorder);
 
         sqk = qx*qx + qy*qy + qz*qz;
 
         if (sqk != 0.0) {
           numerator = MY_4PI/sqk;
           denominator = gf_denom(snx,sny,snz);
           greensfn[n] = numerator*sx*sy*sz*wx*wy*wz/denominator;
           sf_coeff[0] += sf_precoeff1[n]*greensfn[n];
           sf_coeff[1] += sf_precoeff2[n]*greensfn[n];
           sf_coeff[2] += sf_precoeff3[n]*greensfn[n];
           sf_coeff[3] += sf_precoeff4[n]*greensfn[n];
           sf_coeff[4] += sf_precoeff5[n]*greensfn[n];
           sf_coeff[5] += sf_precoeff6[n]*greensfn[n];
           n++;
         } else {
           greensfn[n] = 0.0;
           sf_coeff[0] += sf_precoeff1[n]*greensfn[n];
           sf_coeff[1] += sf_precoeff2[n]*greensfn[n];
           sf_coeff[2] += sf_precoeff3[n]*greensfn[n];
           sf_coeff[3] += sf_precoeff4[n]*greensfn[n];
           sf_coeff[4] += sf_precoeff5[n]*greensfn[n];
           sf_coeff[5] += sf_precoeff6[n]*greensfn[n];
           n++;
         }
       }
     }
   }
 
   // compute the coefficients for the self-force correction
 
   double prex, prey, prez;
   prex = prey = prez = MY_PI/volume;
   prex *= nx_pppm/xprd;
   prey *= ny_pppm/yprd;
   prez *= nz_pppm/zprd_slab;
   sf_coeff[0] *= prex;
   sf_coeff[1] *= prex*2;
   sf_coeff[2] *= prey;
   sf_coeff[3] *= prey*2;
   sf_coeff[4] *= prez;
   sf_coeff[5] *= prez*2;
 
   // communicate values with other procs
 
   double tmp[6];
   MPI_Allreduce(sf_coeff,tmp,6,MPI_DOUBLE,MPI_SUM,world);
   for (n = 0; n < 6; n++) sf_coeff[n] = tmp[n];
 }
 
 /* ----------------------------------------------------------------------
    compute self force coefficients for ad-differentiation scheme
 ------------------------------------------------------------------------- */
 
 void PPPM::compute_sf_precoeff()
 {
   int i,k,l,m,n;
   int nx,ny,nz,kper,lper,mper;
   double wx0[5],wy0[5],wz0[5],wx1[5],wy1[5],wz1[5],wx2[5],wy2[5],wz2[5];
   double qx0,qy0,qz0,qx1,qy1,qz1,qx2,qy2,qz2;
   double u0,u1,u2,u3,u4,u5,u6;
   double sum1,sum2,sum3,sum4,sum5,sum6;
 
   n = 0;
   for (m = nzlo_fft; m <= nzhi_fft; m++) {
     mper = m - nz_pppm*(2*m/nz_pppm);
 
     for (l = nylo_fft; l <= nyhi_fft; l++) {
       lper = l - ny_pppm*(2*l/ny_pppm);
 
       for (k = nxlo_fft; k <= nxhi_fft; k++) {
         kper = k - nx_pppm*(2*k/nx_pppm);
 
         sum1 = sum2 = sum3 = sum4 = sum5 = sum6 = 0.0;
         for (i = 0; i < 5; i++) {
 
           qx0 = MY_2PI*(kper+nx_pppm*(i-2));
           qx1 = MY_2PI*(kper+nx_pppm*(i-1));
           qx2 = MY_2PI*(kper+nx_pppm*(i  ));
           wx0[i] = powsinxx(0.5*qx0/nx_pppm,order);
           wx1[i] = powsinxx(0.5*qx1/nx_pppm,order);
           wx2[i] = powsinxx(0.5*qx2/nx_pppm,order);
 
           qy0 = MY_2PI*(lper+ny_pppm*(i-2));
           qy1 = MY_2PI*(lper+ny_pppm*(i-1));
           qy2 = MY_2PI*(lper+ny_pppm*(i  ));
           wy0[i] = powsinxx(0.5*qy0/ny_pppm,order);
           wy1[i] = powsinxx(0.5*qy1/ny_pppm,order);
           wy2[i] = powsinxx(0.5*qy2/ny_pppm,order);
 
           qz0 = MY_2PI*(mper+nz_pppm*(i-2));
           qz1 = MY_2PI*(mper+nz_pppm*(i-1));
           qz2 = MY_2PI*(mper+nz_pppm*(i  ));
 
           wz0[i] = powsinxx(0.5*qz0/nz_pppm,order);
           wz1[i] = powsinxx(0.5*qz1/nz_pppm,order);
           wz2[i] = powsinxx(0.5*qz2/nz_pppm,order);
         }
 
         for (nx = 0; nx < 5; nx++) {
           for (ny = 0; ny < 5; ny++) {
             for (nz = 0; nz < 5; nz++) {
               u0 = wx0[nx]*wy0[ny]*wz0[nz];
               u1 = wx1[nx]*wy0[ny]*wz0[nz];
               u2 = wx2[nx]*wy0[ny]*wz0[nz];
               u3 = wx0[nx]*wy1[ny]*wz0[nz];
               u4 = wx0[nx]*wy2[ny]*wz0[nz];
               u5 = wx0[nx]*wy0[ny]*wz1[nz];
               u6 = wx0[nx]*wy0[ny]*wz2[nz];
 
               sum1 += u0*u1;
               sum2 += u0*u2;
               sum3 += u0*u3;
               sum4 += u0*u4;
               sum5 += u0*u5;
               sum6 += u0*u6;
             }
           }
         }
 
         // store values
 
         sf_precoeff1[n] = sum1;
         sf_precoeff2[n] = sum2;
         sf_precoeff3[n] = sum3;
         sf_precoeff4[n] = sum4;
         sf_precoeff5[n] = sum5;
         sf_precoeff6[n++] = sum6;
       }
     }
   }
 }
 
 /* ----------------------------------------------------------------------
    find center grid pt for each of my particles
    check that full stencil for the particle will fit in my 3d brick
    store central grid pt indices in part2grid array
 ------------------------------------------------------------------------- */
 
 void PPPM::particle_map()
 {
   int nx,ny,nz;
 
   double **x = atom->x;
   int nlocal = atom->nlocal;
 
   int flag = 0;
   for (int i = 0; i < nlocal; i++) {
 
     // (nx,ny,nz) = global coords of grid pt to "lower left" of charge
     // current particle coord can be outside global and local box
     // add/subtract OFFSET to avoid int(-0.75) = 0 when want it to be -1
 
     nx = static_cast<int> ((x[i][0]-boxlo[0])*delxinv+shift) - OFFSET;
     ny = static_cast<int> ((x[i][1]-boxlo[1])*delyinv+shift) - OFFSET;
     nz = static_cast<int> ((x[i][2]-boxlo[2])*delzinv+shift) - OFFSET;
 
     part2grid[i][0] = nx;
     part2grid[i][1] = ny;
     part2grid[i][2] = nz;
 
     // check that entire stencil around nx,ny,nz will fit in my 3d brick
 
     if (nx+nlower < nxlo_out || nx+nupper > nxhi_out ||
         ny+nlower < nylo_out || ny+nupper > nyhi_out ||
         nz+nlower < nzlo_out || nz+nupper > nzhi_out)
       flag = 1;
   }
 
   if (flag) error->one(FLERR,"Out of range atoms - cannot compute PPPM");
 }
 
 /* ----------------------------------------------------------------------
    create discretized "density" on section of global grid due to my particles
    density(x,y,z) = charge "density" at grid points of my 3d brick
    (nxlo:nxhi,nylo:nyhi,nzlo:nzhi) is extent of my brick (including ghosts)
    in global grid
 ------------------------------------------------------------------------- */
 
 void PPPM::make_rho()
 {
   int l,m,n,nx,ny,nz,mx,my,mz;
   FFT_SCALAR dx,dy,dz,x0,y0,z0;
 
   // clear 3d density array
 
   memset(&(density_brick[nzlo_out][nylo_out][nxlo_out]),0,
          ngrid*sizeof(FFT_SCALAR));
 
   // loop over my charges, add their contribution to nearby grid points
   // (nx,ny,nz) = global coords of grid pt to "lower left" of charge
   // (dx,dy,dz) = distance to "lower left" grid pt
   // (mx,my,mz) = global coords of moving stencil pt
 
   double *q = atom->q;
   double **x = atom->x;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
 
     nx = part2grid[i][0];
     ny = part2grid[i][1];
     nz = part2grid[i][2];
     dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv;
     dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv;
     dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv;
 
     compute_rho1d(dx,dy,dz);
 
     z0 = delvolinv * q[i];
     for (n = nlower; n <= nupper; n++) {
       mz = n+nz;
       y0 = z0*rho1d[2][n];
       for (m = nlower; m <= nupper; m++) {
         my = m+ny;
         x0 = y0*rho1d[1][m];
         for (l = nlower; l <= nupper; l++) {
           mx = l+nx;
           density_brick[mz][my][mx] += x0*rho1d[0][l];
         }
       }
     }
   }
 }
 
 /* ----------------------------------------------------------------------
    remap density from 3d brick decomposition to FFT decomposition
 ------------------------------------------------------------------------- */
 
 void PPPM::brick2fft()
 {
   int n,ix,iy,iz;
 
   // copy grabs inner portion of density from 3d brick
   // remap could be done as pre-stage of FFT,
   //   but this works optimally on only double values, not complex values
 
   n = 0;
   for (iz = nzlo_in; iz <= nzhi_in; iz++)
     for (iy = nylo_in; iy <= nyhi_in; iy++)
       for (ix = nxlo_in; ix <= nxhi_in; ix++)
         density_fft[n++] = density_brick[iz][iy][ix];
 
   remap->perform(density_fft,density_fft,work1);
 }
 
 /* ----------------------------------------------------------------------
    FFT-based Poisson solver
 ------------------------------------------------------------------------- */
 
 void PPPM::poisson()
 {
   if (differentiation_flag == 1) poisson_ad();
   else poisson_ik();
 }
 
 /* ----------------------------------------------------------------------
    FFT-based Poisson solver for ik
 ------------------------------------------------------------------------- */
 
 void PPPM::poisson_ik()
 {
   int i,j,k,n;
   double eng;
 
   // transform charge density (r -> k)
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     work1[n++] = density_fft[i];
     work1[n++] = ZEROF;
   }
 
   fft1->compute(work1,work1,1);
 
   // global energy and virial contribution
 
   double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm);
   double s2 = scaleinv*scaleinv;
 
   if (eflag_global || vflag_global) {
     if (vflag_global) {
       n = 0;
       for (i = 0; i < nfft; i++) {
         eng = s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]);
         for (j = 0; j < 6; j++) virial[j] += eng*vg[i][j];
         if (eflag_global) energy += eng;
         n += 2;
       }
     } else {
       n = 0;
       for (i = 0; i < nfft; i++) {
         energy +=
           s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]);
         n += 2;
       }
     }
   }
 
   // scale by 1/total-grid-pts to get rho(k)
   // multiply by Green's function to get V(k)
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     work1[n++] *= scaleinv * greensfn[i];
     work1[n++] *= scaleinv * greensfn[i];
   }
 
   // extra FFTs for per-atom energy/virial
 
   if (evflag_atom) poisson_peratom();
 
   // triclinic system
 
   if (triclinic) {
     poisson_ik_triclinic();
     return;
   }
 
   // compute gradients of V(r) in each of 3 dims by transformimg -ik*V(k)
   // FFT leaves data in 3d brick decomposition
   // copy it into inner portion of vdx,vdy,vdz arrays
 
   // x direction gradient
 
   n = 0;
   for (k = nzlo_fft; k <= nzhi_fft; k++)
     for (j = nylo_fft; j <= nyhi_fft; j++)
       for (i = nxlo_fft; i <= nxhi_fft; i++) {
         work2[n] = fkx[i]*work1[n+1];
         work2[n+1] = -fkx[i]*work1[n];
         n += 2;
       }
 
   fft2->compute(work2,work2,-1);
 
   n = 0;
   for (k = nzlo_in; k <= nzhi_in; k++)
     for (j = nylo_in; j <= nyhi_in; j++)
       for (i = nxlo_in; i <= nxhi_in; i++) {
         vdx_brick[k][j][i] = work2[n];
         n += 2;
       }
 
   // y direction gradient
 
   n = 0;
   for (k = nzlo_fft; k <= nzhi_fft; k++)
     for (j = nylo_fft; j <= nyhi_fft; j++)
       for (i = nxlo_fft; i <= nxhi_fft; i++) {
         work2[n] = fky[j]*work1[n+1];
         work2[n+1] = -fky[j]*work1[n];
         n += 2;
       }
 
   fft2->compute(work2,work2,-1);
 
   n = 0;
   for (k = nzlo_in; k <= nzhi_in; k++)
     for (j = nylo_in; j <= nyhi_in; j++)
       for (i = nxlo_in; i <= nxhi_in; i++) {
         vdy_brick[k][j][i] = work2[n];
         n += 2;
       }
 
   // z direction gradient
 
   n = 0;
   for (k = nzlo_fft; k <= nzhi_fft; k++)
     for (j = nylo_fft; j <= nyhi_fft; j++)
       for (i = nxlo_fft; i <= nxhi_fft; i++) {
         work2[n] = fkz[k]*work1[n+1];
         work2[n+1] = -fkz[k]*work1[n];
         n += 2;
       }
 
   fft2->compute(work2,work2,-1);
 
   n = 0;
   for (k = nzlo_in; k <= nzhi_in; k++)
     for (j = nylo_in; j <= nyhi_in; j++)
       for (i = nxlo_in; i <= nxhi_in; i++) {
         vdz_brick[k][j][i] = work2[n];
         n += 2;
       }
 }
 
 /* ----------------------------------------------------------------------
    FFT-based Poisson solver for ik for a triclinic system
 ------------------------------------------------------------------------- */
 
 void PPPM::poisson_ik_triclinic()
 {
   int i,j,k,n;
 
   // compute gradients of V(r) in each of 3 dims by transformimg -ik*V(k)
   // FFT leaves data in 3d brick decomposition
   // copy it into inner portion of vdx,vdy,vdz arrays
 
   // x direction gradient
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     work2[n] = fkx[i]*work1[n+1];
     work2[n+1] = -fkx[i]*work1[n];
     n += 2;
   }
 
   fft2->compute(work2,work2,-1);
 
   n = 0;
   for (k = nzlo_in; k <= nzhi_in; k++)
     for (j = nylo_in; j <= nyhi_in; j++)
       for (i = nxlo_in; i <= nxhi_in; i++) {
         vdx_brick[k][j][i] = work2[n];
         n += 2;
       }
 
   // y direction gradient
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     work2[n] = fky[i]*work1[n+1];
     work2[n+1] = -fky[i]*work1[n];
     n += 2;
   }
 
   fft2->compute(work2,work2,-1);
 
   n = 0;
   for (k = nzlo_in; k <= nzhi_in; k++)
     for (j = nylo_in; j <= nyhi_in; j++)
       for (i = nxlo_in; i <= nxhi_in; i++) {
         vdy_brick[k][j][i] = work2[n];
         n += 2;
       }
 
   // z direction gradient
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     work2[n] = fkz[i]*work1[n+1];
     work2[n+1] = -fkz[i]*work1[n];
     n += 2;
   }
 
   fft2->compute(work2,work2,-1);
 
   n = 0;
   for (k = nzlo_in; k <= nzhi_in; k++)
     for (j = nylo_in; j <= nyhi_in; j++)
       for (i = nxlo_in; i <= nxhi_in; i++) {
         vdz_brick[k][j][i] = work2[n];
         n += 2;
       }
 }
 
 /* ----------------------------------------------------------------------
    FFT-based Poisson solver for ad
 ------------------------------------------------------------------------- */
 
 void PPPM::poisson_ad()
 {
   int i,j,k,n;
   double eng;
 
   // transform charge density (r -> k)
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     work1[n++] = density_fft[i];
     work1[n++] = ZEROF;
   }
 
   fft1->compute(work1,work1,1);
 
   // global energy and virial contribution
 
   double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm);
   double s2 = scaleinv*scaleinv;
 
   if (eflag_global || vflag_global) {
     if (vflag_global) {
       n = 0;
       for (i = 0; i < nfft; i++) {
         eng = s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]);
         for (j = 0; j < 6; j++) virial[j] += eng*vg[i][j];
         if (eflag_global) energy += eng;
         n += 2;
       }
     } else {
       n = 0;
       for (i = 0; i < nfft; i++) {
         energy +=
           s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]);
         n += 2;
       }
     }
   }
 
   // scale by 1/total-grid-pts to get rho(k)
   // multiply by Green's function to get V(k)
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     work1[n++] *= scaleinv * greensfn[i];
     work1[n++] *= scaleinv * greensfn[i];
   }
 
   // extra FFTs for per-atom energy/virial
 
   if (vflag_atom) poisson_peratom();
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     work2[n] = work1[n];
     work2[n+1] = work1[n+1];
     n += 2;
   }
 
   fft2->compute(work2,work2,-1);
 
   n = 0;
   for (k = nzlo_in; k <= nzhi_in; k++)
     for (j = nylo_in; j <= nyhi_in; j++)
       for (i = nxlo_in; i <= nxhi_in; i++) {
         u_brick[k][j][i] = work2[n];
         n += 2;
       }
 }
 
 /* ----------------------------------------------------------------------
    FFT-based Poisson solver for per-atom energy/virial
 ------------------------------------------------------------------------- */
 
 void PPPM::poisson_peratom()
 {
   int i,j,k,n;
 
   // energy
 
   if (eflag_atom && differentiation_flag != 1) {
     n = 0;
     for (i = 0; i < nfft; i++) {
       work2[n] = work1[n];
       work2[n+1] = work1[n+1];
       n += 2;
     }
 
     fft2->compute(work2,work2,-1);
 
     n = 0;
     for (k = nzlo_in; k <= nzhi_in; k++)
       for (j = nylo_in; j <= nyhi_in; j++)
         for (i = nxlo_in; i <= nxhi_in; i++) {
           u_brick[k][j][i] = work2[n];
           n += 2;
         }
   }
 
   // 6 components of virial in v0 thru v5
 
   if (!vflag_atom) return;
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     work2[n] = work1[n]*vg[i][0];
     work2[n+1] = work1[n+1]*vg[i][0];
     n += 2;
   }
 
   fft2->compute(work2,work2,-1);
 
   n = 0;
   for (k = nzlo_in; k <= nzhi_in; k++)
     for (j = nylo_in; j <= nyhi_in; j++)
       for (i = nxlo_in; i <= nxhi_in; i++) {
         v0_brick[k][j][i] = work2[n];
         n += 2;
       }
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     work2[n] = work1[n]*vg[i][1];
     work2[n+1] = work1[n+1]*vg[i][1];
     n += 2;
   }
 
   fft2->compute(work2,work2,-1);
 
   n = 0;
   for (k = nzlo_in; k <= nzhi_in; k++)
     for (j = nylo_in; j <= nyhi_in; j++)
       for (i = nxlo_in; i <= nxhi_in; i++) {
         v1_brick[k][j][i] = work2[n];
         n += 2;
       }
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     work2[n] = work1[n]*vg[i][2];
     work2[n+1] = work1[n+1]*vg[i][2];
     n += 2;
   }
 
   fft2->compute(work2,work2,-1);
 
   n = 0;
   for (k = nzlo_in; k <= nzhi_in; k++)
     for (j = nylo_in; j <= nyhi_in; j++)
       for (i = nxlo_in; i <= nxhi_in; i++) {
         v2_brick[k][j][i] = work2[n];
         n += 2;
       }
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     work2[n] = work1[n]*vg[i][3];
     work2[n+1] = work1[n+1]*vg[i][3];
     n += 2;
   }
 
   fft2->compute(work2,work2,-1);
 
   n = 0;
   for (k = nzlo_in; k <= nzhi_in; k++)
     for (j = nylo_in; j <= nyhi_in; j++)
       for (i = nxlo_in; i <= nxhi_in; i++) {
         v3_brick[k][j][i] = work2[n];
         n += 2;
       }
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     work2[n] = work1[n]*vg[i][4];
     work2[n+1] = work1[n+1]*vg[i][4];
     n += 2;
   }
 
   fft2->compute(work2,work2,-1);
 
   n = 0;
   for (k = nzlo_in; k <= nzhi_in; k++)
     for (j = nylo_in; j <= nyhi_in; j++)
       for (i = nxlo_in; i <= nxhi_in; i++) {
         v4_brick[k][j][i] = work2[n];
         n += 2;
       }
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     work2[n] = work1[n]*vg[i][5];
     work2[n+1] = work1[n+1]*vg[i][5];
     n += 2;
   }
 
   fft2->compute(work2,work2,-1);
 
   n = 0;
   for (k = nzlo_in; k <= nzhi_in; k++)
     for (j = nylo_in; j <= nyhi_in; j++)
       for (i = nxlo_in; i <= nxhi_in; i++) {
         v5_brick[k][j][i] = work2[n];
         n += 2;
       }
 }
 
 /* ----------------------------------------------------------------------
    interpolate from grid to get electric field & force on my particles
 ------------------------------------------------------------------------- */
 
 void PPPM::fieldforce()
 {
   if (differentiation_flag == 1) fieldforce_ad();
   else fieldforce_ik();
 }
 
 /* ----------------------------------------------------------------------
    interpolate from grid to get electric field & force on my particles for ik
 ------------------------------------------------------------------------- */
 
 void PPPM::fieldforce_ik()
 {
   int i,l,m,n,nx,ny,nz,mx,my,mz;
   FFT_SCALAR dx,dy,dz,x0,y0,z0;
   FFT_SCALAR ekx,eky,ekz;
 
   // loop over my charges, interpolate electric field from nearby grid points
   // (nx,ny,nz) = global coords of grid pt to "lower left" of charge
   // (dx,dy,dz) = distance to "lower left" grid pt
   // (mx,my,mz) = global coords of moving stencil pt
   // ek = 3 components of E-field on particle
 
   double *q = atom->q;
   double **x = atom->x;
   double **f = atom->f;
 
   int nlocal = atom->nlocal;
 
   for (i = 0; i < nlocal; i++) {
     nx = part2grid[i][0];
     ny = part2grid[i][1];
     nz = part2grid[i][2];
     dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv;
     dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv;
     dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv;
 
     compute_rho1d(dx,dy,dz);
 
     ekx = eky = ekz = ZEROF;
     for (n = nlower; n <= nupper; n++) {
       mz = n+nz;
       z0 = rho1d[2][n];
       for (m = nlower; m <= nupper; m++) {
         my = m+ny;
         y0 = z0*rho1d[1][m];
         for (l = nlower; l <= nupper; l++) {
           mx = l+nx;
           x0 = y0*rho1d[0][l];
           ekx -= x0*vdx_brick[mz][my][mx];
           eky -= x0*vdy_brick[mz][my][mx];
           ekz -= x0*vdz_brick[mz][my][mx];
         }
       }
     }
 
     // convert E-field to force
 
     const double qfactor = qqrd2e * scale * q[i];
     f[i][0] += qfactor*ekx;
     f[i][1] += qfactor*eky;
     if (slabflag != 2) f[i][2] += qfactor*ekz;
   }
 }
 
 /* ----------------------------------------------------------------------
    interpolate from grid to get electric field & force on my particles for ad
 ------------------------------------------------------------------------- */
 
 void PPPM::fieldforce_ad()
 {
   int i,l,m,n,nx,ny,nz,mx,my,mz;
   FFT_SCALAR dx,dy,dz;
   FFT_SCALAR ekx,eky,ekz;
   double s1,s2,s3;
   double sf = 0.0;
   double *prd;
 
   prd = domain->prd;
   double xprd = prd[0];
   double yprd = prd[1];
   double zprd = prd[2];
 
   double hx_inv = nx_pppm/xprd;
   double hy_inv = ny_pppm/yprd;
   double hz_inv = nz_pppm/zprd;
 
   // loop over my charges, interpolate electric field from nearby grid points
   // (nx,ny,nz) = global coords of grid pt to "lower left" of charge
   // (dx,dy,dz) = distance to "lower left" grid pt
   // (mx,my,mz) = global coords of moving stencil pt
   // ek = 3 components of E-field on particle
 
   double *q = atom->q;
   double **x = atom->x;
   double **f = atom->f;
 
   int nlocal = atom->nlocal;
 
   for (i = 0; i < nlocal; i++) {
     nx = part2grid[i][0];
     ny = part2grid[i][1];
     nz = part2grid[i][2];
     dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv;
     dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv;
     dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv;
 
     compute_rho1d(dx,dy,dz);
     compute_drho1d(dx,dy,dz);
 
     ekx = eky = ekz = ZEROF;
     for (n = nlower; n <= nupper; n++) {
       mz = n+nz;
       for (m = nlower; m <= nupper; m++) {
         my = m+ny;
         for (l = nlower; l <= nupper; l++) {
           mx = l+nx;
           ekx += drho1d[0][l]*rho1d[1][m]*rho1d[2][n]*u_brick[mz][my][mx];
           eky += rho1d[0][l]*drho1d[1][m]*rho1d[2][n]*u_brick[mz][my][mx];
           ekz += rho1d[0][l]*rho1d[1][m]*drho1d[2][n]*u_brick[mz][my][mx];
         }
       }
     }
     ekx *= hx_inv;
     eky *= hy_inv;
     ekz *= hz_inv;
 
     // convert E-field to force and substract self forces
 
     const double qfactor = qqrd2e * scale;
 
     s1 = x[i][0]*hx_inv;
     s2 = x[i][1]*hy_inv;
     s3 = x[i][2]*hz_inv;
     sf = sf_coeff[0]*sin(2*MY_PI*s1);
     sf += sf_coeff[1]*sin(4*MY_PI*s1);
     sf *= 2*q[i]*q[i];
     f[i][0] += qfactor*(ekx*q[i] - sf);
 
     sf = sf_coeff[2]*sin(2*MY_PI*s2);
     sf += sf_coeff[3]*sin(4*MY_PI*s2);
     sf *= 2*q[i]*q[i];
     f[i][1] += qfactor*(eky*q[i] - sf);
 
 
     sf = sf_coeff[4]*sin(2*MY_PI*s3);
     sf += sf_coeff[5]*sin(4*MY_PI*s3);
     sf *= 2*q[i]*q[i];
     if (slabflag != 2) f[i][2] += qfactor*(ekz*q[i] - sf);
   }
 }
 
 /* ----------------------------------------------------------------------
    interpolate from grid to get per-atom energy/virial
 ------------------------------------------------------------------------- */
 
 void PPPM::fieldforce_peratom()
 {
   int i,l,m,n,nx,ny,nz,mx,my,mz;
   FFT_SCALAR dx,dy,dz,x0,y0,z0;
   FFT_SCALAR u,v0,v1,v2,v3,v4,v5;
 
   // loop over my charges, interpolate from nearby grid points
   // (nx,ny,nz) = global coords of grid pt to "lower left" of charge
   // (dx,dy,dz) = distance to "lower left" grid pt
   // (mx,my,mz) = global coords of moving stencil pt
 
   double *q = atom->q;
   double **x = atom->x;
 
   int nlocal = atom->nlocal;
 
   for (i = 0; i < nlocal; i++) {
     nx = part2grid[i][0];
     ny = part2grid[i][1];
     nz = part2grid[i][2];
     dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv;
     dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv;
     dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv;
 
     compute_rho1d(dx,dy,dz);
 
     u = v0 = v1 = v2 = v3 = v4 = v5 = ZEROF;
     for (n = nlower; n <= nupper; n++) {
       mz = n+nz;
       z0 = rho1d[2][n];
       for (m = nlower; m <= nupper; m++) {
         my = m+ny;
         y0 = z0*rho1d[1][m];
         for (l = nlower; l <= nupper; l++) {
           mx = l+nx;
           x0 = y0*rho1d[0][l];
           if (eflag_atom) u += x0*u_brick[mz][my][mx];
           if (vflag_atom) {
             v0 += x0*v0_brick[mz][my][mx];
             v1 += x0*v1_brick[mz][my][mx];
             v2 += x0*v2_brick[mz][my][mx];
             v3 += x0*v3_brick[mz][my][mx];
             v4 += x0*v4_brick[mz][my][mx];
             v5 += x0*v5_brick[mz][my][mx];
           }
         }
       }
     }
 
     if (eflag_atom) eatom[i] += q[i]*u;
     if (vflag_atom) {
       vatom[i][0] += q[i]*v0;
       vatom[i][1] += q[i]*v1;
       vatom[i][2] += q[i]*v2;
       vatom[i][3] += q[i]*v3;
       vatom[i][4] += q[i]*v4;
       vatom[i][5] += q[i]*v5;
     }
   }
 }
 
 /* ----------------------------------------------------------------------
    pack own values to buf to send to another proc
 ------------------------------------------------------------------------- */
 
 void PPPM::pack_forward(int flag, FFT_SCALAR *buf, int nlist, int *list)
 {
   int n = 0;
 
   if (flag == FORWARD_IK) {
     FFT_SCALAR *xsrc = &vdx_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *ysrc = &vdy_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *zsrc = &vdz_brick[nzlo_out][nylo_out][nxlo_out];
     for (int i = 0; i < nlist; i++) {
       buf[n++] = xsrc[list[i]];
       buf[n++] = ysrc[list[i]];
       buf[n++] = zsrc[list[i]];
     }
   } else if (flag == FORWARD_AD) {
     FFT_SCALAR *src = &u_brick[nzlo_out][nylo_out][nxlo_out];
     for (int i = 0; i < nlist; i++)
       buf[i] = src[list[i]];
   } else if (flag == FORWARD_IK_PERATOM) {
     FFT_SCALAR *esrc = &u_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out];
     for (int i = 0; i < nlist; i++) {
       if (eflag_atom) buf[n++] = esrc[list[i]];
       if (vflag_atom) {
         buf[n++] = v0src[list[i]];
         buf[n++] = v1src[list[i]];
         buf[n++] = v2src[list[i]];
         buf[n++] = v3src[list[i]];
         buf[n++] = v4src[list[i]];
         buf[n++] = v5src[list[i]];
       }
     }
   } else if (flag == FORWARD_AD_PERATOM) {
     FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out];
     for (int i = 0; i < nlist; i++) {
       buf[n++] = v0src[list[i]];
       buf[n++] = v1src[list[i]];
       buf[n++] = v2src[list[i]];
       buf[n++] = v3src[list[i]];
       buf[n++] = v4src[list[i]];
       buf[n++] = v5src[list[i]];
     }
   }
 }
 
 /* ----------------------------------------------------------------------
    unpack another proc's own values from buf and set own ghost values
 ------------------------------------------------------------------------- */
 
 void PPPM::unpack_forward(int flag, FFT_SCALAR *buf, int nlist, int *list)
 {
   int n = 0;
 
   if (flag == FORWARD_IK) {
     FFT_SCALAR *xdest = &vdx_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *ydest = &vdy_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *zdest = &vdz_brick[nzlo_out][nylo_out][nxlo_out];
     for (int i = 0; i < nlist; i++) {
       xdest[list[i]] = buf[n++];
       ydest[list[i]] = buf[n++];
       zdest[list[i]] = buf[n++];
     }
   } else if (flag == FORWARD_AD) {
     FFT_SCALAR *dest = &u_brick[nzlo_out][nylo_out][nxlo_out];
     for (int i = 0; i < nlist; i++)
       dest[list[i]] = buf[i];
   } else if (flag == FORWARD_IK_PERATOM) {
     FFT_SCALAR *esrc = &u_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out];
     for (int i = 0; i < nlist; i++) {
       if (eflag_atom) esrc[list[i]] = buf[n++];
       if (vflag_atom) {
         v0src[list[i]] = buf[n++];
         v1src[list[i]] = buf[n++];
         v2src[list[i]] = buf[n++];
         v3src[list[i]] = buf[n++];
         v4src[list[i]] = buf[n++];
         v5src[list[i]] = buf[n++];
       }
     }
   } else if (flag == FORWARD_AD_PERATOM) {
     FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out];
     FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out];
     for (int i = 0; i < nlist; i++) {
       v0src[list[i]] = buf[n++];
       v1src[list[i]] = buf[n++];
       v2src[list[i]] = buf[n++];
       v3src[list[i]] = buf[n++];
       v4src[list[i]] = buf[n++];
       v5src[list[i]] = buf[n++];
     }
   }
 }
 
 /* ----------------------------------------------------------------------
    pack ghost values into buf to send to another proc
 ------------------------------------------------------------------------- */
 
 void PPPM::pack_reverse(int flag, FFT_SCALAR *buf, int nlist, int *list)
 {
   if (flag == REVERSE_RHO) {
     FFT_SCALAR *src = &density_brick[nzlo_out][nylo_out][nxlo_out];
     for (int i = 0; i < nlist; i++)
       buf[i] = src[list[i]];
   }
 }
 
 /* ----------------------------------------------------------------------
    unpack another proc's ghost values from buf and add to own values
 ------------------------------------------------------------------------- */
 
 void PPPM::unpack_reverse(int flag, FFT_SCALAR *buf, int nlist, int *list)
 {
   if (flag == REVERSE_RHO) {
     FFT_SCALAR *dest = &density_brick[nzlo_out][nylo_out][nxlo_out];
     for (int i = 0; i < nlist; i++)
       dest[list[i]] += buf[i];
   } 
 }
 
 /* ----------------------------------------------------------------------
    map nprocs to NX by NY grid as PX by PY procs - return optimal px,py
 ------------------------------------------------------------------------- */
 
 void PPPM::procs2grid2d(int nprocs, int nx, int ny, int *px, int *py)
 {
   // loop thru all possible factorizations of nprocs
   // surf = surface area of largest proc sub-domain
   // innermost if test minimizes surface area and surface/volume ratio
 
   int bestsurf = 2 * (nx + ny);
   int bestboxx = 0;
   int bestboxy = 0;
 
   int boxx,boxy,surf,ipx,ipy;
 
   ipx = 1;
   while (ipx <= nprocs) {
     if (nprocs % ipx == 0) {
       ipy = nprocs/ipx;
       boxx = nx/ipx;
       if (nx % ipx) boxx++;
       boxy = ny/ipy;
       if (ny % ipy) boxy++;
       surf = boxx + boxy;
       if (surf < bestsurf ||
           (surf == bestsurf && boxx*boxy > bestboxx*bestboxy)) {
         bestsurf = surf;
         bestboxx = boxx;
         bestboxy = boxy;
         *px = ipx;
         *py = ipy;
       }
     }
     ipx++;
   }
 }
 
 /* ----------------------------------------------------------------------
    charge assignment into rho1d
    dx,dy,dz = distance of particle from "lower left" grid point
 ------------------------------------------------------------------------- */
 
 void PPPM::compute_rho1d(const FFT_SCALAR &dx, const FFT_SCALAR &dy,
                          const FFT_SCALAR &dz)
 {
   int k,l;
   FFT_SCALAR r1,r2,r3;
 
   for (k = (1-order)/2; k <= order/2; k++) {
     r1 = r2 = r3 = ZEROF;
 
     for (l = order-1; l >= 0; l--) {
       r1 = rho_coeff[l][k] + r1*dx;
       r2 = rho_coeff[l][k] + r2*dy;
       r3 = rho_coeff[l][k] + r3*dz;
     }
     rho1d[0][k] = r1;
     rho1d[1][k] = r2;
     rho1d[2][k] = r3;
   }
 }
 
 /* ----------------------------------------------------------------------
    charge assignment into drho1d
    dx,dy,dz = distance of particle from "lower left" grid point
 ------------------------------------------------------------------------- */
 
 void PPPM::compute_drho1d(const FFT_SCALAR &dx, const FFT_SCALAR &dy,
                           const FFT_SCALAR &dz)
 {
   int k,l;
   FFT_SCALAR r1,r2,r3;
 
   for (k = (1-order)/2; k <= order/2; k++) {
     r1 = r2 = r3 = ZEROF;
 
     for (l = order-2; l >= 0; l--) {
       r1 = drho_coeff[l][k] + r1*dx;
       r2 = drho_coeff[l][k] + r2*dy;
       r3 = drho_coeff[l][k] + r3*dz;
     }
     drho1d[0][k] = r1;
     drho1d[1][k] = r2;
     drho1d[2][k] = r3;
   }
 }
 
 /* ----------------------------------------------------------------------
    generate coeffients for the weight function of order n
 
               (n-1)
   Wn(x) =     Sum    wn(k,x) , Sum is over every other integer
            k=-(n-1)
   For k=-(n-1),-(n-1)+2, ....., (n-1)-2,n-1
       k is odd integers if n is even and even integers if n is odd
               ---
              | n-1
              | Sum a(l,j)*(x-k/2)**l   if abs(x-k/2) < 1/2
   wn(k,x) = <  l=0
              |
              |  0                       otherwise
               ---
   a coeffients are packed into the array rho_coeff to eliminate zeros
   rho_coeff(l,((k+mod(n+1,2))/2) = a(l,k)
 ------------------------------------------------------------------------- */
 
 void PPPM::compute_rho_coeff()
 {
   int j,k,l,m;
   FFT_SCALAR s;
 
   FFT_SCALAR **a;
   memory->create2d_offset(a,order,-order,order,"pppm:a");
 
   for (k = -order; k <= order; k++)
     for (l = 0; l < order; l++)
       a[l][k] = 0.0;
 
   a[0][0] = 1.0;
   for (j = 1; j < order; j++) {
     for (k = -j; k <= j; k += 2) {
       s = 0.0;
       for (l = 0; l < j; l++) {
         a[l+1][k] = (a[l][k+1]-a[l][k-1]) / (l+1);
 #ifdef FFT_SINGLE
         s += powf(0.5,(float) l+1) *
           (a[l][k-1] + powf(-1.0,(float) l) * a[l][k+1]) / (l+1);
 #else
         s += pow(0.5,(double) l+1) *
           (a[l][k-1] + pow(-1.0,(double) l) * a[l][k+1]) / (l+1);
 #endif
       }
       a[0][k] = s;
     }
   }
 
   m = (1-order)/2;
   for (k = -(order-1); k < order; k += 2) {
     for (l = 0; l < order; l++)
       rho_coeff[l][m] = a[l][k];
     for (l = 1; l < order; l++)
       drho_coeff[l-1][m] = l*a[l][k];
     m++;
   }
 
   memory->destroy2d_offset(a,-order);
 }
 
 /* ----------------------------------------------------------------------
    Slab-geometry correction term to dampen inter-slab interactions between
    periodically repeating slabs.  Yields good approximation to 2D Ewald if
    adequate empty space is left between repeating slabs (J. Chem. Phys.
    111, 3155).  Slabs defined here to be parallel to the xy plane. Also
    extended to non-neutral systems (J. Chem. Phys. 131, 094107).
 ------------------------------------------------------------------------- */
 
 void PPPM::slabcorr()
 {
   // compute local contribution to global dipole moment
 
   double *q = atom->q;
   double **x = atom->x;
   double zprd = domain->zprd;
   int nlocal = atom->nlocal;
 
   double dipole = 0.0;
   for (int i = 0; i < nlocal; i++) dipole += q[i]*x[i][2];
 
   // sum local contributions to get global dipole moment
 
   double dipole_all;
   MPI_Allreduce(&dipole,&dipole_all,1,MPI_DOUBLE,MPI_SUM,world);
 
   // need to make non-neutral systems and/or
   //  per-atom energy translationally invariant
 
   double dipole_r2 = 0.0;
   if (eflag_atom || fabs(qsum) > SMALL) {
     for (int i = 0; i < nlocal; i++)
       dipole_r2 += q[i]*x[i][2]*x[i][2];
 
     // sum local contributions
 
     double tmp;
     MPI_Allreduce(&dipole_r2,&tmp,1,MPI_DOUBLE,MPI_SUM,world);
     dipole_r2 = tmp;
   }
 
   // compute corrections
 
   const double e_slabcorr = MY_2PI*(dipole_all*dipole_all -
     qsum*dipole_r2 - qsum*qsum*zprd*zprd/12.0)/volume;
   const double qscale = qqrd2e * scale;
 
   if (eflag_global) energy += qscale * e_slabcorr;
 
   // per-atom energy
 
   if (eflag_atom) {
     double efact = qscale * MY_2PI/volume;
     for (int i = 0; i < nlocal; i++)
       eatom[i] += efact * q[i]*(x[i][2]*dipole_all - 0.5*(dipole_r2 +
         qsum*x[i][2]*x[i][2]) - qsum*zprd*zprd/12.0);
   }
 
   // add on force corrections
 
   double ffact = qscale * (-4.0*MY_PI/volume);
   double **f = atom->f;
 
   for (int i = 0; i < nlocal; i++) f[i][2] += ffact * q[i]*(dipole_all - qsum*x[i][2]);
 }
 
 /* ----------------------------------------------------------------------
    perform and time the 1d FFTs required for N timesteps
 ------------------------------------------------------------------------- */
 
 int PPPM::timing_1d(int n, double &time1d)
 {
   double time1,time2;
 
   for (int i = 0; i < 2*nfft_both; i++) work1[i] = ZEROF;
 
   MPI_Barrier(world);
   time1 = MPI_Wtime();
 
   for (int i = 0; i < n; i++) {
     fft1->timing1d(work1,nfft_both,1);
     fft2->timing1d(work1,nfft_both,-1);
     if (differentiation_flag != 1) {
       fft2->timing1d(work1,nfft_both,-1);
       fft2->timing1d(work1,nfft_both,-1);
     }
   }
 
   MPI_Barrier(world);
   time2 = MPI_Wtime();
   time1d = time2 - time1;
 
   if (differentiation_flag) return 2;
   return 4;
 }
 
 /* ----------------------------------------------------------------------
    perform and time the 3d FFTs required for N timesteps
 ------------------------------------------------------------------------- */
 
 int PPPM::timing_3d(int n, double &time3d)
 {
   double time1,time2;
 
   for (int i = 0; i < 2*nfft_both; i++) work1[i] = ZEROF;
 
   MPI_Barrier(world);
   time1 = MPI_Wtime();
 
   for (int i = 0; i < n; i++) {
     fft1->compute(work1,work1,1);
     fft2->compute(work1,work1,-1);
     if (differentiation_flag != 1) {
       fft2->compute(work1,work1,-1);
       fft2->compute(work1,work1,-1);
     }
   }
 
   MPI_Barrier(world);
   time2 = MPI_Wtime();
   time3d = time2 - time1;
 
   if (differentiation_flag) return 2;
   return 4;
 }
 
 /* ----------------------------------------------------------------------
    memory usage of local arrays
 ------------------------------------------------------------------------- */
 
 double PPPM::memory_usage()
 {
   double bytes = nmax*3 * sizeof(double);
   int nbrick = (nxhi_out-nxlo_out+1) * (nyhi_out-nylo_out+1) *
     (nzhi_out-nzlo_out+1);
   if (differentiation_flag == 1) {
     bytes += 2 * nbrick * sizeof(FFT_SCALAR);
   } else {
     bytes += 4 * nbrick * sizeof(FFT_SCALAR);
   }
   if (triclinic) bytes += 3 * nfft_both * sizeof(double);
   bytes += 6 * nfft_both * sizeof(double);
   bytes += nfft_both * sizeof(double);
   bytes += nfft_both*5 * sizeof(FFT_SCALAR);
 
   if (peratom_allocate_flag)
     bytes += 6 * nbrick * sizeof(FFT_SCALAR);
 
   if (group_allocate_flag) {
     bytes += 2 * nbrick * sizeof(FFT_SCALAR);
     bytes += 2 * nfft_both * sizeof(FFT_SCALAR);;
   }
 
   bytes += cg->memory_usage();
 
   return bytes;
 }
 
 /* ----------------------------------------------------------------------
    group-group interactions
  ------------------------------------------------------------------------- */
 
 /* ----------------------------------------------------------------------
    compute the PPPM total long-range force and energy for groups A and B
  ------------------------------------------------------------------------- */
 
 void PPPM::compute_group_group(int groupbit_A, int groupbit_B, int AA_flag)
 {
   if (slabflag && triclinic)
     error->all(FLERR,"Cannot (yet) use K-space slab "
                "correction with compute group/group for triclinic systems");
 
   if (differentiation_flag)
     error->all(FLERR,"Cannot (yet) use kspace_modify "
                "diff ad with compute group/group");
 
   if (!group_allocate_flag) allocate_groups();
 
   // convert atoms from box to lamda coords
 
   if (triclinic == 0) boxlo = domain->boxlo;
   else {
     boxlo = domain->boxlo_lamda;
     domain->x2lamda(atom->nlocal);
   }
 
   e2group = 0.0; //energy
   f2group[0] = 0.0; //force in x-direction
   f2group[1] = 0.0; //force in y-direction
   f2group[2] = 0.0; //force in z-direction
 
   // map my particle charge onto my local 3d density grid
 
   make_rho_groups(groupbit_A,groupbit_B,AA_flag);
 
   // all procs communicate density values from their ghost cells
   //   to fully sum contribution in their 3d bricks
   // remap from 3d decomposition to FFT decomposition
 
   // temporarily store and switch pointers so we can
   //  use brick2fft() for groups A and B (without
   //  writing an additional function)
 
   FFT_SCALAR ***density_brick_real = density_brick;
   FFT_SCALAR *density_fft_real = density_fft;
 
   // group A
 
   density_brick = density_A_brick;
   density_fft = density_A_fft;
 
   cg->reverse_comm(this,REVERSE_RHO);
   brick2fft();
 
   // group B
 
   density_brick = density_B_brick;
   density_fft = density_B_fft;
 
   cg->reverse_comm(this,REVERSE_RHO);
   brick2fft();
 
   // switch back pointers
 
   density_brick = density_brick_real;
   density_fft = density_fft_real;
 
   // compute potential gradient on my FFT grid and
   //   portion of group-group energy/force on this proc's FFT grid
 
   poisson_groups(AA_flag);
 
   const double qscale = qqrd2e * scale;
 
   // total group A <--> group B energy
   // self and boundary correction terms are in compute_group_group.cpp
 
   double e2group_all;
   MPI_Allreduce(&e2group,&e2group_all,1,MPI_DOUBLE,MPI_SUM,world);
   e2group = e2group_all;
 
   e2group *= qscale*0.5*volume;
 
   // total group A <--> group B force
 
   double f2group_all[3];
   MPI_Allreduce(f2group,f2group_all,3,MPI_DOUBLE,MPI_SUM,world);
 
   f2group[0] = qscale*volume*f2group_all[0];
   f2group[1] = qscale*volume*f2group_all[1];
   if (slabflag != 2) f2group[2] = qscale*volume*f2group_all[2];
 
   // convert atoms back from lamda to box coords
 
   if (triclinic) domain->lamda2x(atom->nlocal);
 
   if (slabflag == 1)
     slabcorr_groups(groupbit_A, groupbit_B, AA_flag);
 }
 
 /* ----------------------------------------------------------------------
  allocate group-group memory that depends on # of K-vectors and order
  ------------------------------------------------------------------------- */
 
 void PPPM::allocate_groups()
 {
   group_allocate_flag = 1;
 
   memory->create3d_offset(density_A_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out,
                           nxlo_out,nxhi_out,"pppm:density_A_brick");
   memory->create3d_offset(density_B_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out,
                           nxlo_out,nxhi_out,"pppm:density_B_brick");
   memory->create(density_A_fft,nfft_both,"pppm:density_A_fft");
   memory->create(density_B_fft,nfft_both,"pppm:density_B_fft");
 }
 
 /* ----------------------------------------------------------------------
  deallocate group-group memory that depends on # of K-vectors and order
  ------------------------------------------------------------------------- */
 
 void PPPM::deallocate_groups()
 {
   group_allocate_flag = 0;
 
   memory->destroy3d_offset(density_A_brick,nzlo_out,nylo_out,nxlo_out);
   memory->destroy3d_offset(density_B_brick,nzlo_out,nylo_out,nxlo_out);
   memory->destroy(density_A_fft);
   memory->destroy(density_B_fft);
 }
 
 /* ----------------------------------------------------------------------
  create discretized "density" on section of global grid due to my particles
  density(x,y,z) = charge "density" at grid points of my 3d brick
  (nxlo:nxhi,nylo:nyhi,nzlo:nzhi) is extent of my brick (including ghosts)
  in global grid for group-group interactions
  ------------------------------------------------------------------------- */
 
 void PPPM::make_rho_groups(int groupbit_A, int groupbit_B, int AA_flag)
 {
   int l,m,n,nx,ny,nz,mx,my,mz;
   FFT_SCALAR dx,dy,dz,x0,y0,z0;
 
   // clear 3d density arrays
 
   memset(&(density_A_brick[nzlo_out][nylo_out][nxlo_out]),0,
          ngrid*sizeof(FFT_SCALAR));
 
   memset(&(density_B_brick[nzlo_out][nylo_out][nxlo_out]),0,
          ngrid*sizeof(FFT_SCALAR));
 
   // loop over my charges, add their contribution to nearby grid points
   // (nx,ny,nz) = global coords of grid pt to "lower left" of charge
   // (dx,dy,dz) = distance to "lower left" grid pt
   // (mx,my,mz) = global coords of moving stencil pt
 
   double *q = atom->q;
   double **x = atom->x;
   int nlocal = atom->nlocal;
   int *mask = atom->mask;
 
   for (int i = 0; i < nlocal; i++) {
 
     if (!((mask[i] & groupbit_A) && (mask[i] & groupbit_B)))
       if (AA_flag) continue;
 
     if ((mask[i] & groupbit_A) || (mask[i] & groupbit_B)) {
 
       nx = part2grid[i][0];
       ny = part2grid[i][1];
       nz = part2grid[i][2];
       dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv;
       dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv;
       dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv;
 
       compute_rho1d(dx,dy,dz);
 
       z0 = delvolinv * q[i];
       for (n = nlower; n <= nupper; n++) {
         mz = n+nz;
         y0 = z0*rho1d[2][n];
         for (m = nlower; m <= nupper; m++) {
           my = m+ny;
           x0 = y0*rho1d[1][m];
           for (l = nlower; l <= nupper; l++) {
             mx = l+nx;
 
             // group A
 
             if (mask[i] & groupbit_A)
               density_A_brick[mz][my][mx] += x0*rho1d[0][l];
 
             // group B
 
             if (mask[i] & groupbit_B)
               density_B_brick[mz][my][mx] += x0*rho1d[0][l];
           }
         }
       }
     }
   }
 }
 
 /* ----------------------------------------------------------------------
    FFT-based Poisson solver for group-group interactions
  ------------------------------------------------------------------------- */
 
 void PPPM::poisson_groups(int AA_flag)
 {
   int i,j,k,n;
 
   // reuse memory (already declared)
 
   FFT_SCALAR *work_A = work1;
   FFT_SCALAR *work_B = work2;
 
   // transform charge density (r -> k)
 
   // group A
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     work_A[n++] = density_A_fft[i];
     work_A[n++] = ZEROF;
   }
 
   fft1->compute(work_A,work_A,1);
 
   // group B
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     work_B[n++] = density_B_fft[i];
     work_B[n++] = ZEROF;
   }
 
   fft1->compute(work_B,work_B,1);
 
   // group-group energy and force contribution,
   //  keep everything in reciprocal space so
   //  no inverse FFTs needed
 
   double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm);
   double s2 = scaleinv*scaleinv;
 
   // energy
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     e2group += s2 * greensfn[i] *
       (work_A[n]*work_B[n] + work_A[n+1]*work_B[n+1]);
     n += 2;
   }
 
   if (AA_flag) return;
 
 
   // multiply by Green's function and s2
   //  (only for work_A so it is not squared below)
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     work_A[n++] *= s2 * greensfn[i];
     work_A[n++] *= s2 * greensfn[i];
   }
 
   // triclinic system
   
   if (triclinic) {
     poisson_groups_triclinic();
     return;
   }
 
   double partial_group;
 
   // force, x direction
 
   n = 0;
   for (k = nzlo_fft; k <= nzhi_fft; k++)
     for (j = nylo_fft; j <= nyhi_fft; j++)
       for (i = nxlo_fft; i <= nxhi_fft; i++) {
         partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1];
         f2group[0] += fkx[i] * partial_group;
         n += 2;
       }
 
   // force, y direction
 
   n = 0;
   for (k = nzlo_fft; k <= nzhi_fft; k++)
     for (j = nylo_fft; j <= nyhi_fft; j++)
       for (i = nxlo_fft; i <= nxhi_fft; i++) {
         partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1];
         f2group[1] += fky[j] * partial_group;
         n += 2;
       }
 
   // force, z direction
 
   n = 0;
   for (k = nzlo_fft; k <= nzhi_fft; k++)
     for (j = nylo_fft; j <= nyhi_fft; j++)
       for (i = nxlo_fft; i <= nxhi_fft; i++) {
         partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1];
         f2group[2] += fkz[k] * partial_group;
         n += 2;
       }
 }
 
 /* ----------------------------------------------------------------------
    FFT-based Poisson solver for group-group interactions
    for a triclinic system
  ------------------------------------------------------------------------- */
 
 void PPPM::poisson_groups_triclinic()
 {
   int i,n;
 
   // reuse memory (already declared)
 
   FFT_SCALAR *work_A = work1;
   FFT_SCALAR *work_B = work2;
 
   double partial_group;
 
   // force, x direction
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1];
     f2group[0] += fkx[i] * partial_group;
     n += 2;
   }
 
   // force, y direction
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1];
     f2group[1] += fky[i] * partial_group;
     n += 2;
   }
 
   // force, z direction
 
   n = 0;
   for (i = 0; i < nfft; i++) {
     partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1];
     f2group[2] += fkz[i] * partial_group;
     n += 2;
   }
 }
 
 /* ----------------------------------------------------------------------
    Slab-geometry correction term to dampen inter-slab interactions between
    periodically repeating slabs.  Yields good approximation to 2D Ewald if
    adequate empty space is left between repeating slabs (J. Chem. Phys.
    111, 3155).  Slabs defined here to be parallel to the xy plane. Also
    extended to non-neutral systems (J. Chem. Phys. 131, 094107).
 ------------------------------------------------------------------------- */
 
 void PPPM::slabcorr_groups(int groupbit_A, int groupbit_B, int AA_flag)
 {
   // compute local contribution to global dipole moment
 
   double *q = atom->q;
   double **x = atom->x;
   double zprd = domain->zprd;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double qsum_A = 0.0;
   double qsum_B = 0.0;
   double dipole_A = 0.0;
   double dipole_B = 0.0;
   double dipole_r2_A = 0.0;
   double dipole_r2_B = 0.0;
 
   for (int i = 0; i < nlocal; i++) {
     if (!((mask[i] & groupbit_A) && (mask[i] & groupbit_B)))
       if (AA_flag) continue;
 
     if (mask[i] & groupbit_A) { 
       qsum_A += q[i];
       dipole_A += q[i]*x[i][2];
       dipole_r2_A += q[i]*x[i][2]*x[i][2];
     }
 
     if (mask[i] & groupbit_B) {
       qsum_B += q[i];
       dipole_B += q[i]*x[i][2];
       dipole_r2_B += q[i]*x[i][2]*x[i][2];
     }
   }
 
   // sum local contributions to get total charge and global dipole moment
   //  for each group
 
   double tmp;
   MPI_Allreduce(&qsum_A,&tmp,1,MPI_DOUBLE,MPI_SUM,world);
   qsum_A = tmp;
 
   MPI_Allreduce(&qsum_B,&tmp,1,MPI_DOUBLE,MPI_SUM,world);
   qsum_B = tmp;
 
   MPI_Allreduce(&dipole_A,&tmp,1,MPI_DOUBLE,MPI_SUM,world);
   dipole_A = tmp;
 
   MPI_Allreduce(&dipole_B,&tmp,1,MPI_DOUBLE,MPI_SUM,world);
   dipole_B = tmp;
 
   MPI_Allreduce(&dipole_r2_A,&tmp,1,MPI_DOUBLE,MPI_SUM,world);
   dipole_r2_A = tmp;
 
   MPI_Allreduce(&dipole_r2_B,&tmp,1,MPI_DOUBLE,MPI_SUM,world);
   dipole_r2_B = tmp;
 
   // compute corrections
 
   const double qscale = qqrd2e * scale;
   const double efact = qscale * MY_2PI/volume;
 
   e2group += efact * (dipole_A*dipole_B - 0.5*(qsum_A*dipole_r2_B +
     qsum_B*dipole_r2_A) - qsum_A*qsum_B*zprd*zprd/12.0);
 
   // add on force corrections
 
   const double ffact = qscale * (-4.0*MY_PI/volume);
   f2group[2] += ffact * (qsum_A*dipole_B - qsum_B*dipole_A);
 }
diff --git a/src/compute_property_atom.cpp b/src/compute_property_atom.cpp
index 0c20744a5..56282052c 100644
--- a/src/compute_property_atom.cpp
+++ b/src/compute_property_atom.cpp
@@ -1,1671 +1,1689 @@
 /* ----------------------------------------------------------------------
    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.
 ------------------------------------------------------------------------- */
 
 #include "math.h"
 #include "string.h"
 #include "compute_property_atom.h"
 #include "math_extra.h"
 #include "atom.h"
 #include "atom_vec.h"
 #include "atom_vec_ellipsoid.h"
 #include "atom_vec_line.h"
 #include "atom_vec_tri.h"
 #include "atom_vec_body.h"
 #include "update.h"
 #include "domain.h"
+#include "comm.h"
 #include "memory.h"
 #include "error.h"
 
 using namespace LAMMPS_NS;
 
 /* ---------------------------------------------------------------------- */
 
 ComputePropertyAtom::ComputePropertyAtom(LAMMPS *lmp, int narg, char **arg) :
   Compute(lmp, narg, arg)
 {
   if (narg < 4) error->all(FLERR,"Illegal compute property/atom command");
 
   peratom_flag = 1;
   nvalues = narg - 3;
   if (nvalues == 1) size_peratom_cols = 0;
   else size_peratom_cols = nvalues;
 
   // parse input values
   // customize a new keyword by adding to if statement
 
   pack_choice = new FnPtrPack[nvalues];
   index = new int[nvalues];
 
   int i;
   for (int iarg = 3; iarg < narg; iarg++) {
     i = iarg-3;
 
     if (strcmp(arg[iarg],"id") == 0) {
       pack_choice[i] = &ComputePropertyAtom::pack_id;
     } else if (strcmp(arg[iarg],"mol") == 0) {
       if (!atom->molecule_flag)
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_molecule;
+    } else if (strcmp(arg[iarg],"proc") == 0) {
+      pack_choice[i] = &ComputePropertyAtom::pack_proc;
     } else if (strcmp(arg[iarg],"type") == 0) {
       pack_choice[i] = &ComputePropertyAtom::pack_type;
     } else if (strcmp(arg[iarg],"mass") == 0) {
       pack_choice[i] = &ComputePropertyAtom::pack_mass;
 
     } else if (strcmp(arg[iarg],"x") == 0) {
       pack_choice[i] = &ComputePropertyAtom::pack_x;
     } else if (strcmp(arg[iarg],"y") == 0) {
       pack_choice[i] = &ComputePropertyAtom::pack_y;
     } else if (strcmp(arg[iarg],"z") == 0) {
       pack_choice[i] = &ComputePropertyAtom::pack_z;
     } else if (strcmp(arg[iarg],"xs") == 0) {
       if (domain->triclinic)
         pack_choice[i] = &ComputePropertyAtom::pack_xs_triclinic;
       else pack_choice[i] = &ComputePropertyAtom::pack_xs;
     } else if (strcmp(arg[iarg],"ys") == 0) {
       if (domain->triclinic)
         pack_choice[i] = &ComputePropertyAtom::pack_ys_triclinic;
       else pack_choice[i] = &ComputePropertyAtom::pack_ys;
     } else if (strcmp(arg[iarg],"zs") == 0) {
       if (domain->triclinic)
         pack_choice[i] = &ComputePropertyAtom::pack_zs_triclinic;
       else pack_choice[i] = &ComputePropertyAtom::pack_zs;
     } else if (strcmp(arg[iarg],"xu") == 0) {
       if (domain->triclinic)
         pack_choice[i] = &ComputePropertyAtom::pack_xu_triclinic;
       else pack_choice[i] = &ComputePropertyAtom::pack_xu;
     } else if (strcmp(arg[iarg],"yu") == 0) {
       if (domain->triclinic)
         pack_choice[i] = &ComputePropertyAtom::pack_yu_triclinic;
       else pack_choice[i] = &ComputePropertyAtom::pack_yu;
     } else if (strcmp(arg[iarg],"zu") == 0) {
       if (domain->triclinic)
         pack_choice[i] = &ComputePropertyAtom::pack_zu_triclinic;
       else pack_choice[i] = &ComputePropertyAtom::pack_zu;
     } else if (strcmp(arg[iarg],"ix") == 0) {
       pack_choice[i] = &ComputePropertyAtom::pack_ix;
     } else if (strcmp(arg[iarg],"iy") == 0) {
       pack_choice[i] = &ComputePropertyAtom::pack_iy;
     } else if (strcmp(arg[iarg],"iz") == 0) {
       pack_choice[i] = &ComputePropertyAtom::pack_iz;
 
     } else if (strcmp(arg[iarg],"vx") == 0) {
       pack_choice[i] = &ComputePropertyAtom::pack_vx;
     } else if (strcmp(arg[iarg],"vy") == 0) {
       pack_choice[i] = &ComputePropertyAtom::pack_vy;
     } else if (strcmp(arg[iarg],"vz") == 0) {
       pack_choice[i] = &ComputePropertyAtom::pack_vz;
     } else if (strcmp(arg[iarg],"fx") == 0) {
       pack_choice[i] = &ComputePropertyAtom::pack_fx;
     } else if (strcmp(arg[iarg],"fy") == 0) {
       pack_choice[i] = &ComputePropertyAtom::pack_fy;
     } else if (strcmp(arg[iarg],"fz") == 0) {
       pack_choice[i] = &ComputePropertyAtom::pack_fz;
 
     } else if (strcmp(arg[iarg],"q") == 0) {
       if (!atom->q_flag)
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_q;
     } else if (strcmp(arg[iarg],"mux") == 0) {
       if (!atom->mu_flag)
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_mux;
     } else if (strcmp(arg[iarg],"muy") == 0) {
       if (!atom->mu_flag)
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_muy;
     } else if (strcmp(arg[iarg],"muz") == 0) {
       if (!atom->mu_flag)
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_muz;
     } else if (strcmp(arg[iarg],"mu") == 0) {
       if (!atom->mu_flag)
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_mu;
 
     } else if (strcmp(arg[iarg],"radius") == 0) {
       if (!atom->radius_flag)
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_radius;
     } else if (strcmp(arg[iarg],"diameter") == 0) {
       if (!atom->radius_flag)
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_diameter;
     } else if (strcmp(arg[iarg],"omegax") == 0) {
       if (!atom->omega_flag)
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_omegax;
     } else if (strcmp(arg[iarg],"omegay") == 0) {
       if (!atom->omega_flag)
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_omegay;
     } else if (strcmp(arg[iarg],"omegaz") == 0) {
       if (!atom->omega_flag)
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_omegaz;
     } else if (strcmp(arg[iarg],"angmomx") == 0) {
       if (!atom->angmom_flag)
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_angmomx;
     } else if (strcmp(arg[iarg],"angmomy") == 0) {
       if (!atom->angmom_flag)
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_angmomy;
     } else if (strcmp(arg[iarg],"angmomz") == 0) {
       if (!atom->angmom_flag)
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_angmomz;
 
     } else if (strcmp(arg[iarg],"shapex") == 0) {
       avec_ellipsoid = (AtomVecEllipsoid *) atom->style_match("ellipsoid");
       if (!avec_ellipsoid) error->all(FLERR,"Compute property/atom for "
                                       "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_shapex;
     } else if (strcmp(arg[iarg],"shapey") == 0) {
       avec_ellipsoid = (AtomVecEllipsoid *) atom->style_match("ellipsoid");
       if (!avec_ellipsoid) error->all(FLERR,"Compute property/atom for "
                                       "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_shapey;
     } else if (strcmp(arg[iarg],"shapez") == 0) {
       avec_ellipsoid = (AtomVecEllipsoid *) atom->style_match("ellipsoid");
       if (!avec_ellipsoid) error->all(FLERR,"Compute property/atom for "
                                       "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_shapez;
 
     } else if (strcmp(arg[iarg],"quatw") == 0) {
       avec_ellipsoid = (AtomVecEllipsoid *) atom->style_match("ellipsoid");
       avec_body = (AtomVecBody *) atom->style_match("body");
       if (!avec_ellipsoid && !avec_body) 
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_quatw;
     } else if (strcmp(arg[iarg],"quati") == 0) {
       avec_ellipsoid = (AtomVecEllipsoid *) atom->style_match("ellipsoid");
       avec_body = (AtomVecBody *) atom->style_match("body");
       if (!avec_ellipsoid && !avec_body) 
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_quati;
     } else if (strcmp(arg[iarg],"quatj") == 0) {
       avec_ellipsoid = (AtomVecEllipsoid *) atom->style_match("ellipsoid");
       avec_body = (AtomVecBody *) atom->style_match("body");
       if (!avec_ellipsoid && !avec_body) 
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_quatj;
     } else if (strcmp(arg[iarg],"quatk") == 0) {
       avec_ellipsoid = (AtomVecEllipsoid *) atom->style_match("ellipsoid");
       avec_body = (AtomVecBody *) atom->style_match("body");
       if (!avec_ellipsoid && !avec_body) 
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_quatk;
 
     } else if (strcmp(arg[iarg],"tqx") == 0) {
       if (!atom->torque_flag)
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_tqx;
     } else if (strcmp(arg[iarg],"tqy") == 0) {
       if (!atom->torque_flag)
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_tqy;
     } else if (strcmp(arg[iarg],"tqz") == 0) {
       if (!atom->torque_flag)
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_tqz;
 
     } else if (strcmp(arg[iarg],"end1x") == 0) {
       avec_line = (AtomVecLine *) atom->style_match("line");
       if (!avec_line) error->all(FLERR,"Compute property/atom for "
                                  "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_end1x;
     } else if (strcmp(arg[iarg],"end1y") == 0) {
       avec_line = (AtomVecLine *) atom->style_match("line");
       if (!avec_line) error->all(FLERR,"Compute property/atom for "
                                  "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_end1y;
     } else if (strcmp(arg[iarg],"end1z") == 0) {
       avec_line = (AtomVecLine *) atom->style_match("line");
       if (!avec_line) error->all(FLERR,"Compute property/atom for "
                                  "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_end1z;
     } else if (strcmp(arg[iarg],"end2x") == 0) {
       avec_line = (AtomVecLine *) atom->style_match("line");
       if (!avec_line) error->all(FLERR,"Compute property/atom for "
                                  "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_end2x;
     } else if (strcmp(arg[iarg],"end2y") == 0) {
       avec_line = (AtomVecLine *) atom->style_match("line");
       if (!avec_line) error->all(FLERR,"Compute property/atom for "
                                  "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_end2y;
     } else if (strcmp(arg[iarg],"end2z") == 0) {
       avec_line = (AtomVecLine *) atom->style_match("line");
       if (!avec_line) error->all(FLERR,"Compute property/atom for "
                                  "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_end2z;
 
     } else if (strcmp(arg[iarg],"corner1x") == 0) {
       avec_tri = (AtomVecTri *) atom->style_match("tri");
       if (!avec_tri) error->all(FLERR,"Compute property/atom for "
                                  "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_corner1x;
     } else if (strcmp(arg[iarg],"corner1y") == 0) {
       avec_tri = (AtomVecTri *) atom->style_match("tri");
       if (!avec_tri) error->all(FLERR,"Compute property/atom for "
                                  "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_corner1y;
     } else if (strcmp(arg[iarg],"corner1z") == 0) {
       avec_tri = (AtomVecTri *) atom->style_match("tri");
       if (!avec_tri) error->all(FLERR,"Compute property/atom for "
                                  "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_corner1z;
     } else if (strcmp(arg[iarg],"corner2x") == 0) {
       avec_tri = (AtomVecTri *) atom->style_match("tri");
       if (!avec_tri) error->all(FLERR,"Compute property/atom for "
                                  "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_corner2x;
     } else if (strcmp(arg[iarg],"corner2y") == 0) {
       avec_tri = (AtomVecTri *) atom->style_match("tri");
       if (!avec_tri) error->all(FLERR,"Compute property/atom for "
                                  "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_corner2y;
     } else if (strcmp(arg[iarg],"corner2z") == 0) {
       avec_tri = (AtomVecTri *) atom->style_match("tri");
       if (!avec_tri) error->all(FLERR,"Compute property/atom for "
                                  "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_corner2z;
     } else if (strcmp(arg[iarg],"corner3x") == 0) {
       avec_tri = (AtomVecTri *) atom->style_match("tri");
       if (!avec_tri) error->all(FLERR,"Compute property/atom for "
                                  "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_corner3x;
     } else if (strcmp(arg[iarg],"corner3y") == 0) {
       avec_tri = (AtomVecTri *) atom->style_match("tri");
       if (!avec_tri) error->all(FLERR,"Compute property/atom for "
                                  "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_corner3y;
     } else if (strcmp(arg[iarg],"corner3z") == 0) {
       avec_tri = (AtomVecTri *) atom->style_match("tri");
       if (!avec_tri) error->all(FLERR,"Compute property/atom for "
                                  "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_corner3z;
 
     } else if (strcmp(arg[iarg],"nbonds") == 0) {
       if (!atom->molecule_flag)
         error->all(FLERR,"Compute property/atom for "
                    "atom property that isn't allocated");
       pack_choice[i] = &ComputePropertyAtom::pack_nbonds;
 
     } else if (strstr(arg[iarg],"i_") == arg[iarg]) {
       int flag;
       index[i] = atom->find_custom(&arg[iarg][2],flag);
       if (index[i] < 0 || flag != 0)
         error->all(FLERR,"Compute property/atom floating point "
                    "vector does not exist");
       pack_choice[i] = &ComputePropertyAtom::pack_iname;
     } else if (strstr(arg[iarg],"d_") == arg[iarg]) {
       int flag;
       index[i] = atom->find_custom(&arg[iarg][2],flag);
       if (index[i] < 0 || flag != 1)
         error->all(FLERR,"Compute property/atom integer "
                    "vector does not exist");
       pack_choice[i] = &ComputePropertyAtom::pack_dname;
 
     // check if atom style recognizes keyword
 
     } else {
       index[i] = atom->avec->property_atom(arg[iarg]);
       if (index[i] < 0)
         error->all(FLERR,"Invalid keyword in compute property/atom command");
       pack_choice[i] = &ComputePropertyAtom::pack_property_atom;
     }
   }
 
   nmax = 0;
   vector = NULL;
   array = NULL;
 }
 
 /* ---------------------------------------------------------------------- */
 
 ComputePropertyAtom::~ComputePropertyAtom()
 {
   delete [] pack_choice;
   delete [] index;
   memory->destroy(vector);
   memory->destroy(array);
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::init()
 {
   avec_ellipsoid = (AtomVecEllipsoid *) atom->style_match("ellipsoid");
   avec_line = (AtomVecLine *) atom->style_match("line");
   avec_tri = (AtomVecTri *) atom->style_match("tri");
   avec_body = (AtomVecBody *) atom->style_match("body");
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::compute_peratom()
 {
   invoked_peratom = update->ntimestep;
 
   // grow vector or array if necessary
 
   if (atom->nlocal > nmax) {
     nmax = atom->nmax;
     if (nvalues == 1) {
       memory->destroy(vector);
       memory->create(vector,nmax,"property/atom:vector");
       vector_atom = vector;
     } else {
       memory->destroy(array);
       memory->create(array,nmax,nvalues,"property/atom:array");
       array_atom = array;
     }
   }
 
   // fill vector or array with per-atom values
 
   if (nvalues == 1) {
     buf = vector;
     (this->*pack_choice[0])(0);
   } else {
     if (nmax) buf = &array[0][0];
     else buf = NULL;
     for (int n = 0; n < nvalues; n++)
       (this->*pack_choice[n])(n);
   }
 }
 
 /* ----------------------------------------------------------------------
    memory usage of local atom-based array
 ------------------------------------------------------------------------- */
 
 double ComputePropertyAtom::memory_usage()
 {
   double bytes = nmax*nvalues * sizeof(double);
   return bytes;
 }
 
 /* ----------------------------------------------------------------------
    one method for every keyword compute property/atom can output
    the atom property is packed into buf starting at n with stride nvalues
    customize a new keyword by adding a method
 ------------------------------------------------------------------------- */
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_id(int n)
 {
   tagint *tag = atom->tag;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = tag[i];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_molecule(int n)
 {
   tagint *molecule = atom->molecule;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = molecule[i];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
+void ComputePropertyAtom::pack_proc(int n)
+{
+  int *mask = atom->mask;
+  int nlocal = atom->nlocal;
+  int me = comm->me;
+
+  for (int i = 0; i < nlocal; i++) {
+    if (mask[i] & groupbit) buf[n] = me;
+    else buf[n] = 0.0;
+    n += nvalues;
+  }
+}
+
+/* ---------------------------------------------------------------------- */
+
 void ComputePropertyAtom::pack_type(int n)
 {
   int *type = atom->type;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = type[i];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_mass(int n)
 {
   int *type = atom->type;
   double *mass = atom->mass;
   double *rmass = atom->rmass;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   if (rmass) {
     for (int i = 0; i < nlocal; i++) {
       if (mask[i] & groupbit) buf[n] = rmass[i];
       else buf[n] = 0.0;
       n += nvalues;
     }
   } else {
     for (int i = 0; i < nlocal; i++) {
       if (mask[i] & groupbit) buf[n] = mass[type[i]];
       else buf[n] = 0.0;
       n += nvalues;
     }
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_x(int n)
 {
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = x[i][0];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_y(int n)
 {
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = x[i][1];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_z(int n)
 {
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = x[i][2];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_xs(int n)
 {
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double boxxlo = domain->boxlo[0];
   double invxprd = 1.0/domain->xprd;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = (x[i][0] - boxxlo) * invxprd;
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_ys(int n)
 {
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double boxylo = domain->boxlo[1];
   double invyprd = 1.0/domain->yprd;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = (x[i][1] - boxylo) * invyprd;
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_zs(int n)
 {
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double boxzlo = domain->boxlo[2];
   double invzprd = 1.0/domain->zprd;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = (x[i][2] - boxzlo) * invzprd;
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_xs_triclinic(int n)
 {
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double *boxlo = domain->boxlo;
   double *h_inv = domain->h_inv;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit)
       buf[n] = h_inv[0]*(x[i][0]-boxlo[0]) +
         h_inv[5]*(x[i][1]-boxlo[1]) + h_inv[4]*(x[i][2]-boxlo[2]);
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_ys_triclinic(int n)
 {
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double *boxlo = domain->boxlo;
   double *h_inv = domain->h_inv;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit)
       buf[n] = h_inv[1]*(x[i][1]-boxlo[1]) + h_inv[3]*(x[i][2]-boxlo[2]);
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_zs_triclinic(int n)
 {
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double *boxlo = domain->boxlo;
   double *h_inv = domain->h_inv;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit)
       buf[n] = h_inv[2]*(x[i][2]-boxlo[2]);
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_xu(int n)
 {
   double **x = atom->x;
   imageint *image = atom->image;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double xprd = domain->xprd;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit)
       buf[n] = x[i][0] + ((image[i] & IMGMASK) - IMGMAX) * xprd;
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_yu(int n)
 {
   double **x = atom->x;
   imageint *image = atom->image;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double yprd = domain->yprd;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit)
       buf[n] = x[i][1] + ((image[i] >> IMGBITS & IMGMASK) - IMGMAX) * yprd;
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_zu(int n)
 {
   double **x = atom->x;
   imageint *image = atom->image;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double zprd = domain->zprd;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit)
       buf[n] = x[i][2] + ((image[i] >> IMG2BITS) - IMGMAX) * zprd;
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_xu_triclinic(int n)
 {
   double **x = atom->x;
   imageint *image = atom->image;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double *h = domain->h;
   int xbox,ybox,zbox;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) {
       xbox = (image[i] & IMGMASK) - IMGMAX;
       ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX;
       zbox = (image[i] >> IMG2BITS) - IMGMAX;
       buf[n] = x[i][0] + h[0]*xbox + h[5]*ybox + h[4]*zbox;
     } else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_yu_triclinic(int n)
 {
   double **x = atom->x;
   imageint *image = atom->image;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double *h = domain->h;
   int ybox,zbox;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) {
       ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX;
       zbox = (image[i] >> IMG2BITS) - IMGMAX;
       buf[n] = x[i][1] + h[1]*ybox + h[3]*zbox;
     } else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_zu_triclinic(int n)
 {
   double **x = atom->x;
   imageint *image = atom->image;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double *h = domain->h;
   int zbox;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) {
       zbox = (image[i] >> IMG2BITS) - IMGMAX;
       buf[n] = x[i][2] + h[2]*zbox;
     } else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_ix(int n)
 {
   imageint *image = atom->image;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = (image[i] & IMGMASK) - IMGMAX;
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_iy(int n)
 {
   imageint *image = atom->image;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = (image[i] >> IMGBITS & IMGMASK) - IMGMAX;
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_iz(int n)
 {
   imageint *image = atom->image;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = (image[i] >> IMG2BITS) - IMGMAX;
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_vx(int n)
 {
   double **v = atom->v;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = v[i][0];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_vy(int n)
 {
   double **v = atom->v;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = v[i][1];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_vz(int n)
 {
   double **v = atom->v;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = v[i][2];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_fx(int n)
 {
   double **f = atom->f;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = f[i][0];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_fy(int n)
 {
   double **f = atom->f;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = f[i][1];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_fz(int n)
 {
   double **f = atom->f;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = f[i][2];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_q(int n)
 {
   double *q = atom->q;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = q[i];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_mux(int n)
 {
   double **mu = atom->mu;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = mu[i][0];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_muy(int n)
 {
   double **mu = atom->mu;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = mu[i][1];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_muz(int n)
 {
   double **mu = atom->mu;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = mu[i][2];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_mu(int n)
 {
   double **mu = atom->mu;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = mu[i][3];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_radius(int n)
 {
   double *radius = atom->radius;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = radius[i];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_diameter(int n)
 {
   double *radius = atom->radius;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = 2.0*radius[i];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_omegax(int n)
 {
   double **omega = atom->omega;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = omega[i][0];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_omegay(int n)
 {
   double **omega = atom->omega;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = omega[i][1];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_omegaz(int n)
 {
   double **omega = atom->omega;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = omega[i][2];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_angmomx(int n)
 {
   double **angmom = atom->angmom;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = angmom[i][0];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_angmomy(int n)
 {
   double **angmom = atom->angmom;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = angmom[i][1];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_angmomz(int n)
 {
   double **angmom = atom->angmom;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = angmom[i][2];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_shapex(int n)
 {
   AtomVecEllipsoid::Bonus *bonus = avec_ellipsoid->bonus;
   int *ellipsoid = atom->ellipsoid;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if ((mask[i] & groupbit) && ellipsoid[i] >= 0)
       buf[n] = bonus[ellipsoid[i]].shape[0];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_shapey(int n)
 {
   AtomVecEllipsoid::Bonus *bonus = avec_ellipsoid->bonus;
   int *ellipsoid = atom->ellipsoid;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if ((mask[i] & groupbit) && ellipsoid[i] >= 0)
       buf[n] = bonus[ellipsoid[i]].shape[1];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_shapez(int n)
 {
   AtomVecEllipsoid::Bonus *bonus = avec_ellipsoid->bonus;
   int *ellipsoid = atom->ellipsoid;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if ((mask[i] & groupbit) && ellipsoid[i] >= 0)
       buf[n] = bonus[ellipsoid[i]].shape[2];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_quatw(int n)
 {
   if (avec_ellipsoid) {
     AtomVecEllipsoid::Bonus *bonus = avec_ellipsoid->bonus;
     int *ellipsoid = atom->ellipsoid;
     int *mask = atom->mask;
     int nlocal = atom->nlocal;
 
     for (int i = 0; i < nlocal; i++) {
       if ((mask[i] & groupbit) && ellipsoid[i] >= 0)
         buf[n] = bonus[ellipsoid[i]].quat[0];
       else buf[n] = 0.0;
       n += nvalues;
     }
 
   } else {
     AtomVecBody::Bonus *bonus = avec_body->bonus;
     int *body = atom->body;
     int *mask = atom->mask;
     int nlocal = atom->nlocal;
 
     for (int i = 0; i < nlocal; i++) {
       if ((mask[i] & groupbit) && body[i] >= 0)
         buf[n] = bonus[body[i]].quat[0];
       else buf[n] = 0.0;
       n += nvalues;
     }
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_quati(int n)
 {
   if (avec_ellipsoid) {
     AtomVecEllipsoid::Bonus *bonus = avec_ellipsoid->bonus;
     int *ellipsoid = atom->ellipsoid;
     int *mask = atom->mask;
     int nlocal = atom->nlocal;
 
     for (int i = 0; i < nlocal; i++) {
       if ((mask[i] & groupbit) && ellipsoid[i] >= 0)
         buf[n] = bonus[ellipsoid[i]].quat[1];
       else buf[n] = 0.0;
       n += nvalues;
     }
 
   } else {
     AtomVecBody::Bonus *bonus = avec_body->bonus;
     int *body = atom->body;
     int *mask = atom->mask;
     int nlocal = atom->nlocal;
 
     for (int i = 0; i < nlocal; i++) {
       if ((mask[i] & groupbit) && body[i] >= 0)
         buf[n] = bonus[body[i]].quat[1];
       else buf[n] = 0.0;
       n += nvalues;
     }
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_quatj(int n)
 {
   if (avec_ellipsoid) {
     AtomVecEllipsoid::Bonus *bonus = avec_ellipsoid->bonus;
     int *ellipsoid = atom->ellipsoid;
     int *mask = atom->mask;
     int nlocal = atom->nlocal;
 
     for (int i = 0; i < nlocal; i++) {
       if ((mask[i] & groupbit) && ellipsoid[i] >= 0)
         buf[n] = bonus[ellipsoid[i]].quat[2];
       else buf[n] = 0.0;
       n += nvalues;
     }
 
   } else {
     AtomVecBody::Bonus *bonus = avec_body->bonus;
     int *body = atom->body;
     int *mask = atom->mask;
     int nlocal = atom->nlocal;
 
     for (int i = 0; i < nlocal; i++) {
       if ((mask[i] & groupbit) && body[i] >= 0)
         buf[n] = bonus[body[i]].quat[2];
       else buf[n] = 0.0;
       n += nvalues;
     }
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_quatk(int n)
 {
   if (avec_ellipsoid) {
     AtomVecEllipsoid::Bonus *bonus = avec_ellipsoid->bonus;
     int *ellipsoid = atom->ellipsoid;
     int *mask = atom->mask;
     int nlocal = atom->nlocal;
 
     for (int i = 0; i < nlocal; i++) {
       if ((mask[i] & groupbit) && ellipsoid[i] >= 0)
         buf[n] = bonus[ellipsoid[i]].quat[3];
       else buf[n] = 0.0;
       n += nvalues;
     }
 
   } else {
     AtomVecBody::Bonus *bonus = avec_body->bonus;
     int *body = atom->body;
     int *mask = atom->mask;
     int nlocal = atom->nlocal;
 
     for (int i = 0; i < nlocal; i++) {
       if ((mask[i] & groupbit) && body[i] >= 0)
         buf[n] = bonus[body[i]].quat[3];
       else buf[n] = 0.0;
       n += nvalues;
     }
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_tqx(int n)
 {
   double **torque = atom->torque;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = torque[i][0];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_tqy(int n)
 {
   double **torque = atom->torque;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = torque[i][1];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_tqz(int n)
 {
   double **torque = atom->torque;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = torque[i][2];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_end1x(int n)
 {
   AtomVecLine::Bonus *bonus = avec_line->bonus;
   int *line = atom->line;
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if ((mask[i] & groupbit) && line[i] >= 0)
       buf[n] = x[i][0] - 0.5*bonus[line[i]].length*cos(bonus[line[i]].theta);
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_end1y(int n)
 {
   AtomVecLine::Bonus *bonus = avec_line->bonus;
   int *line = atom->line;
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if ((mask[i] & groupbit) && line[i] >= 0)
       buf[n] = x[i][1] - 0.5*bonus[line[i]].length*sin(bonus[line[i]].theta);
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_end1z(int n)
 {
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = x[i][2];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_end2x(int n)
 {
   AtomVecLine::Bonus *bonus = avec_line->bonus;
   int *line = atom->line;
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if ((mask[i] & groupbit) && line[i] >= 0)
       buf[n] = x[i][0] + 0.5*bonus[line[i]].length*cos(bonus[line[i]].theta);
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_end2y(int n)
 {
   AtomVecLine::Bonus *bonus = avec_line->bonus;
   int *line = atom->line;
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if ((mask[i] & groupbit) && line[i] >= 0)
       buf[n] = x[i][1] + 0.5*bonus[line[i]].length*sin(bonus[line[i]].theta);
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_end2z(int n)
 {
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = x[i][2];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_corner1x(int n)
 {
   AtomVecTri::Bonus *bonus = avec_tri->bonus;
   int *tri = atom->tri;
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double p[3][3],c[3];
   for (int i = 0; i < nlocal; i++) {
     if ((mask[i] & groupbit) && tri[i] >= 0) {
       MathExtra::quat_to_mat(bonus[tri[i]].quat,p);
       MathExtra::matvec(p,bonus[tri[i]].c1,c);
       buf[n] = x[i][0] + c[0];
     } else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_corner1y(int n)
 {
   AtomVecTri::Bonus *bonus = avec_tri->bonus;
   int *tri = atom->tri;
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double p[3][3],c[3];
   for (int i = 0; i < nlocal; i++) {
     if ((mask[i] & groupbit) && tri[i] >= 0) {
       MathExtra::quat_to_mat(bonus[tri[i]].quat,p);
       MathExtra::matvec(p,bonus[tri[i]].c1,c);
       buf[n] = x[i][1] + c[1];
     } else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_corner1z(int n)
 {
   AtomVecTri::Bonus *bonus = avec_tri->bonus;
   int *tri = atom->tri;
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double p[3][3],c[3];
   for (int i = 0; i < nlocal; i++) {
     if ((mask[i] & groupbit) && tri[i] >= 0) {
       MathExtra::quat_to_mat(bonus[tri[i]].quat,p);
       MathExtra::matvec(p,bonus[tri[i]].c1,c);
       buf[n] = x[i][2] + c[2];
     } else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_corner2x(int n)
 {
   AtomVecTri::Bonus *bonus = avec_tri->bonus;
   int *tri = atom->tri;
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double p[3][3],c[3];
   for (int i = 0; i < nlocal; i++) {
     if ((mask[i] & groupbit) && tri[i] >= 0) {
       MathExtra::quat_to_mat(bonus[tri[i]].quat,p);
       MathExtra::matvec(p,bonus[tri[i]].c2,c);
       buf[n] = x[i][0] + c[0];
     } else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_corner2y(int n)
 {
   AtomVecTri::Bonus *bonus = avec_tri->bonus;
   int *tri = atom->tri;
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double p[3][3],c[3];
   for (int i = 0; i < nlocal; i++) {
     if ((mask[i] & groupbit) && tri[i] >= 0) {
       MathExtra::quat_to_mat(bonus[tri[i]].quat,p);
       MathExtra::matvec(p,bonus[tri[i]].c2,c);
       buf[n] = x[i][1] + c[1];
     } else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_corner2z(int n)
 {
   AtomVecTri::Bonus *bonus = avec_tri->bonus;
   int *tri = atom->tri;
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double p[3][3],c[3];
   for (int i = 0; i < nlocal; i++) {
     if ((mask[i] & groupbit) && tri[i] >= 0) {
       MathExtra::quat_to_mat(bonus[tri[i]].quat,p);
       MathExtra::matvec(p,bonus[tri[i]].c2,c);
       buf[n] = x[i][2] + c[2];
     } else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_corner3x(int n)
 {
   AtomVecTri::Bonus *bonus = avec_tri->bonus;
   int *tri = atom->tri;
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double p[3][3],c[3];
   for (int i = 0; i < nlocal; i++) {
     if ((mask[i] & groupbit) && tri[i] >= 0) {
       MathExtra::quat_to_mat(bonus[tri[i]].quat,p);
       MathExtra::matvec(p,bonus[tri[i]].c3,c);
       buf[n] = x[i][0] + c[0];
     } else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_corner3y(int n)
 {
   AtomVecTri::Bonus *bonus = avec_tri->bonus;
   int *tri = atom->tri;
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double p[3][3],c[3];
   for (int i = 0; i < nlocal; i++) {
     if ((mask[i] & groupbit) && tri[i] >= 0) {
       MathExtra::quat_to_mat(bonus[tri[i]].quat,p);
       MathExtra::matvec(p,bonus[tri[i]].c3,c);
       buf[n] = x[i][1] + c[1];
     } else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_corner3z(int n)
 {
   AtomVecTri::Bonus *bonus = avec_tri->bonus;
   int *tri = atom->tri;
   double **x = atom->x;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   double p[3][3],c[3];
   for (int i = 0; i < nlocal; i++) {
     if ((mask[i] & groupbit) && tri[i] >= 0) {
       MathExtra::quat_to_mat(bonus[tri[i]].quat,p);
       MathExtra::matvec(p,bonus[tri[i]].c3,c);
       buf[n] = x[i][2] + c[2];
     } else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_nbonds(int n)
 {
   int *num_bond = atom->num_bond;
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = num_bond[i];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_iname(int n)
 {
   int *ivector = atom->ivector[index[n]];
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = ivector[i];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_dname(int n)
 {
   double *dvector = atom->dvector[index[n]];
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     if (mask[i] & groupbit) buf[n] = dvector[i];
     else buf[n] = 0.0;
     n += nvalues;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputePropertyAtom::pack_property_atom(int n)
 {
   atom->avec->pack_property_atom(index[n],&buf[n],nvalues,groupbit);
 }
diff --git a/src/compute_property_atom.h b/src/compute_property_atom.h
index a1a4faa97..e9e957918 100644
--- a/src/compute_property_atom.h
+++ b/src/compute_property_atom.h
@@ -1,159 +1,160 @@
 /* -*- 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 COMPUTE_CLASS
 
 ComputeStyle(property/atom,ComputePropertyAtom)
 
 #else
 
 #ifndef LMP_COMPUTE_PROPERTY_ATOM_H
 #define LMP_COMPUTE_PROPERTY_ATOM_H
 
 #include "compute.h"
 
 namespace LAMMPS_NS {
 
 class ComputePropertyAtom : public Compute {
  public:
   ComputePropertyAtom(class LAMMPS *, int, char **);
   ~ComputePropertyAtom();
   void init();
   void compute_peratom();
   double memory_usage();
 
  private:
   int nvalues;
   int nmax;
   int *index;
   double *vector;
   double **array;
   double *buf;
   class AtomVecEllipsoid *avec_ellipsoid;
   class AtomVecLine *avec_line;
   class AtomVecTri *avec_tri;
   class AtomVecBody *avec_body;
 
   typedef void (ComputePropertyAtom::*FnPtrPack)(int);
   FnPtrPack *pack_choice;              // ptrs to pack functions
 
   void pack_id(int);
   void pack_molecule(int);
+  void pack_proc(int);
   void pack_type(int);
   void pack_mass(int);
 
   void pack_x(int);
   void pack_y(int);
   void pack_z(int);
   void pack_xs(int);
   void pack_ys(int);
   void pack_zs(int);
   void pack_xs_triclinic(int);
   void pack_ys_triclinic(int);
   void pack_zs_triclinic(int);
   void pack_xu(int);
   void pack_yu(int);
   void pack_zu(int);
   void pack_xu_triclinic(int);
   void pack_yu_triclinic(int);
   void pack_zu_triclinic(int);
   void pack_ix(int);
   void pack_iy(int);
   void pack_iz(int);
 
   void pack_vx(int);
   void pack_vy(int);
   void pack_vz(int);
   void pack_fx(int);
   void pack_fy(int);
   void pack_fz(int);
   void pack_q(int);
   void pack_mux(int);
   void pack_muy(int);
   void pack_muz(int);
   void pack_mu(int);
   void pack_radius(int);
   void pack_diameter(int);
 
   void pack_omegax(int);
   void pack_omegay(int);
   void pack_omegaz(int);
   void pack_angmomx(int);
   void pack_angmomy(int);
   void pack_angmomz(int);
   void pack_shapex(int);
   void pack_shapey(int);
   void pack_shapez(int);
   void pack_quatw(int);
   void pack_quati(int);
   void pack_quatj(int);
   void pack_quatk(int);
   void pack_tqx(int);
   void pack_tqy(int);
   void pack_tqz(int);
   void pack_end1x(int);
   void pack_end1y(int);
   void pack_end1z(int);
   void pack_end2x(int);
   void pack_end2y(int);
   void pack_end2z(int);
   void pack_corner1x(int);
   void pack_corner1y(int);
   void pack_corner1z(int);
   void pack_corner2x(int);
   void pack_corner2y(int);
   void pack_corner2z(int);
   void pack_corner3x(int);
   void pack_corner3y(int);
   void pack_corner3z(int);
 
   void pack_nbonds(int);
 
   void pack_iname(int);
   void pack_dname(int);
 
   void pack_property_atom(int);
 };
 
 }
 
 #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: Compute property/atom for atom property that isn't allocated
 
 Self-explanatory.
 
 E: Compute property/atom floating point vector does not exist
 
 The command is accessing a vector added by the fix property/atom
 command, that does not exist.
 
 E: Compute property/atom integer vector does not exist
 
 The command is accessing a vector added by the fix property/atom
 command, that does not exist.
 
 E: Invalid keyword in compute property/atom command
 
 Self-explanatory.
 
 */
diff --git a/src/dump_custom.cpp b/src/dump_custom.cpp
index 860f08405..f1cb9379f 100644
--- a/src/dump_custom.cpp
+++ b/src/dump_custom.cpp
@@ -1,2367 +1,2560 @@
 /* ----------------------------------------------------------------------
    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.
 ------------------------------------------------------------------------- */
 
 #include "math.h"
 #include "stdlib.h"
 #include "string.h"
 #include "dump_custom.h"
 #include "atom.h"
 #include "force.h"
 #include "domain.h"
 #include "region.h"
 #include "group.h"
 #include "input.h"
 #include "variable.h"
 #include "update.h"
 #include "modify.h"
 #include "compute.h"
 #include "fix.h"
 #include "memory.h"
 #include "error.h"
 
 using namespace LAMMPS_NS;
 
 // customize by adding keyword
 // also customize compute_atom_property.cpp
 
-enum{ID,MOL,TYPE,ELEMENT,MASS,
+enum{ID,MOL,PROC,TYPE,ELEMENT,MASS,
      X,Y,Z,XS,YS,ZS,XSTRI,YSTRI,ZSTRI,XU,YU,ZU,XUTRI,YUTRI,ZUTRI,
      XSU,YSU,ZSU,XSUTRI,YSUTRI,ZSUTRI,
      IX,IY,IZ,
      VX,VY,VZ,FX,FY,FZ,
      Q,MUX,MUY,MUZ,MU,RADIUS,DIAMETER,
      OMEGAX,OMEGAY,OMEGAZ,ANGMOMX,ANGMOMY,ANGMOMZ,
      TQX,TQY,TQZ,
-     COMPUTE,FIX,VARIABLE};
+     COMPUTE,FIX,VARIABLE,INAME,DNAME};
 enum{LT,LE,GT,GE,EQ,NEQ};
 enum{INT,DOUBLE,STRING,BIGINT};    // same as in DumpCFG
 
 #define INVOKED_PERATOM 8
 #define ONEFIELD 32
 #define DELTA 1048576
 
 /* ---------------------------------------------------------------------- */
 
 DumpCustom::DumpCustom(LAMMPS *lmp, int narg, char **arg) :
   Dump(lmp, narg, arg)
 {
   if (narg == 5) error->all(FLERR,"No dump custom arguments specified");
 
   clearstep = 1;
 
   nevery = force->inumeric(FLERR,arg[3]);
 
   // size_one may be shrunk below if additional optional args exist
 
   size_one = nfield = narg - 5;
   pack_choice = new FnPtrPack[nfield];
   vtype = new int[nfield];
 
   buffer_allow = 1;
   buffer_flag = 1;
   iregion = -1;
   idregion = NULL;
   nthresh = 0;
   thresh_array = NULL;
   thresh_op = NULL;
   thresh_value = NULL;
 
   // computes, fixes, variables which the dump accesses
 
   memory->create(field2index,nfield,"dump:field2index");
   memory->create(argindex,nfield,"dump:argindex");
 
   ncompute = 0;
   id_compute = NULL;
   compute = NULL;
 
   nfix = 0;
   id_fix = NULL;
   fix = NULL;
 
   nvariable = 0;
   id_variable = NULL;
   variable = NULL;
   vbuf = NULL;
 
+  ncustom = 0;
+  id_custom = NULL;
+  flag_custom = NULL;
+
   // process attributes
   // ioptional = start of additional optional args
   // only dump image and dump movie styles process optional args
 
   ioptional = parse_fields(narg,arg);
 
   if (ioptional < narg && 
       strcmp(style,"image") != 0 && strcmp(style,"movie") != 0)
     error->all(FLERR,"Invalid attribute in dump custom command");
   size_one = nfield = ioptional - 5;
 
   // atom selection arrays
 
   maxlocal = 0;
   choose = NULL;
   dchoose = NULL;
   clist = NULL;
 
   // element names
 
   ntypes = atom->ntypes;
   typenames = NULL;
 
   // setup format strings
 
   vformat = new char*[size_one];
 
   format_default = new char[3*size_one+1];
   format_default[0] = '\0';
 
   for (int i = 0; i < size_one; i++) {
     if (vtype[i] == INT) strcat(format_default,"%d ");
     else if (vtype[i] == DOUBLE) strcat(format_default,"%g ");
     else if (vtype[i] == STRING) strcat(format_default,"%s ");
     else if (vtype[i] == BIGINT) strcat(format_default,BIGINT_FORMAT " ");
     vformat[i] = NULL;
   }
 
   // setup column string
 
   int n = 0;
   for (int iarg = 5; iarg < narg; iarg++) n += strlen(arg[iarg]) + 2;
   columns = new char[n];
   columns[0] = '\0';
   for (int iarg = 5; iarg < narg; iarg++) {
     strcat(columns,arg[iarg]);
     strcat(columns," ");
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 DumpCustom::~DumpCustom()
 {
   delete [] pack_choice;
   delete [] vtype;
   memory->destroy(field2index);
   memory->destroy(argindex);
 
   delete [] idregion;
   memory->destroy(thresh_array);
   memory->destroy(thresh_op);
   memory->destroy(thresh_value);
 
   for (int i = 0; i < ncompute; i++) delete [] id_compute[i];
   memory->sfree(id_compute);
   delete [] compute;
 
   for (int i = 0; i < nfix; i++) delete [] id_fix[i];
   memory->sfree(id_fix);
   delete [] fix;
 
   for (int i = 0; i < nvariable; i++) delete [] id_variable[i];
   memory->sfree(id_variable);
   delete [] variable;
   for (int i = 0; i < nvariable; i++) memory->destroy(vbuf[i]);
   delete [] vbuf;
 
+  for (int i = 0; i < ncustom; i++) delete [] id_custom[i];
+  memory->sfree(id_custom);
+  delete [] flag_custom;
+
   memory->destroy(choose);
   memory->destroy(dchoose);
   memory->destroy(clist);
 
   if (typenames) {
     for (int i = 1; i <= ntypes; i++) delete [] typenames[i];
     delete [] typenames;
   }
 
   for (int i = 0; i < size_one; i++) delete [] vformat[i];
   delete [] vformat;
 
   delete [] columns;
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::init_style()
 {
   delete [] format;
   char *str;
   if (format_user) str = format_user;
   else str = format_default;
 
   int n = strlen(str) + 1;
   format = new char[n];
   strcpy(format,str);
 
   // default for element names = C
 
   if (typenames == NULL) {
     typenames = new char*[ntypes+1];
     for (int itype = 1; itype <= ntypes; itype++) {
       typenames[itype] = new char[2];
       strcpy(typenames[itype],"C");
     }
   }
 
   // tokenize the format string and add space at end of each format element
 
   char *ptr;
   for (int i = 0; i < size_one; i++) {
     if (i == 0) ptr = strtok(format," \0");
     else ptr = strtok(NULL," \0");
     if (ptr == NULL) error->all(FLERR,"Dump_modify format string is too short");
     delete [] vformat[i];
     vformat[i] = new char[strlen(ptr) + 2];
     strcpy(vformat[i],ptr);
     vformat[i] = strcat(vformat[i]," ");
   }
 
   // setup boundary string
 
   domain->boundary_string(boundstr);
 
   // setup function ptrs
 
   if (binary && domain->triclinic == 0)
     header_choice = &DumpCustom::header_binary;
   else if (binary && domain->triclinic == 1)
     header_choice = &DumpCustom::header_binary_triclinic;
   else if (!binary && domain->triclinic == 0)
     header_choice = &DumpCustom::header_item;
   else if (!binary && domain->triclinic == 1)
     header_choice = &DumpCustom::header_item_triclinic;
 
   if (binary) write_choice = &DumpCustom::write_binary;
   else if (buffer_flag == 1) write_choice = &DumpCustom::write_string;
   else write_choice = &DumpCustom::write_lines;
 
   // find current ptr for each compute,fix,variable
   // check that fix frequency is acceptable
 
   int icompute;
   for (int i = 0; i < ncompute; i++) {
     icompute = modify->find_compute(id_compute[i]);
     if (icompute < 0) error->all(FLERR,"Could not find dump custom compute ID");
     compute[i] = modify->compute[icompute];
   }
 
   int ifix;
   for (int i = 0; i < nfix; i++) {
     ifix = modify->find_fix(id_fix[i]);
     if (ifix < 0) error->all(FLERR,"Could not find dump custom fix ID");
     fix[i] = modify->fix[ifix];
     if (nevery % modify->fix[ifix]->peratom_freq)
       error->all(FLERR,"Dump custom and fix not computed at compatible times");
   }
 
   int ivariable;
   for (int i = 0; i < nvariable; i++) {
     ivariable = input->variable->find(id_variable[i]);
     if (ivariable < 0)
       error->all(FLERR,"Could not find dump custom variable name");
     variable[i] = ivariable;
   }
 
+  int icustom;
+  for (int i = 0; i < ncustom; i++) {
+    icustom = atom->find_custom(id_custom[i],flag_custom[i]);
+    if (icustom < 0)
+      error->all(FLERR,"Could not find custom per-atom property ID");
+  }
+
   // set index and check validity of region
 
   if (iregion >= 0) {
     iregion = domain->find_region(idregion);
     if (iregion == -1)
       error->all(FLERR,"Region ID for dump custom does not exist");
   }
 
   // open single file, one time only
 
   if (multifile == 0) openfile();
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::write_header(bigint ndump)
 {
   if (multiproc) (this->*header_choice)(ndump);
   else if (me == 0) (this->*header_choice)(ndump);
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::header_binary(bigint ndump)
 {
   fwrite(&update->ntimestep,sizeof(bigint),1,fp);
   fwrite(&ndump,sizeof(bigint),1,fp);
   fwrite(&domain->triclinic,sizeof(int),1,fp);
   fwrite(&domain->boundary[0][0],6*sizeof(int),1,fp);
   fwrite(&boxxlo,sizeof(double),1,fp);
   fwrite(&boxxhi,sizeof(double),1,fp);
   fwrite(&boxylo,sizeof(double),1,fp);
   fwrite(&boxyhi,sizeof(double),1,fp);
   fwrite(&boxzlo,sizeof(double),1,fp);
   fwrite(&boxzhi,sizeof(double),1,fp);
   fwrite(&size_one,sizeof(int),1,fp);
   if (multiproc) fwrite(&nclusterprocs,sizeof(int),1,fp);
   else fwrite(&nprocs,sizeof(int),1,fp);
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::header_binary_triclinic(bigint ndump)
 {
   fwrite(&update->ntimestep,sizeof(bigint),1,fp);
   fwrite(&ndump,sizeof(bigint),1,fp);
   fwrite(&domain->triclinic,sizeof(int),1,fp);
   fwrite(&domain->boundary[0][0],6*sizeof(int),1,fp);
   fwrite(&boxxlo,sizeof(double),1,fp);
   fwrite(&boxxhi,sizeof(double),1,fp);
   fwrite(&boxylo,sizeof(double),1,fp);
   fwrite(&boxyhi,sizeof(double),1,fp);
   fwrite(&boxzlo,sizeof(double),1,fp);
   fwrite(&boxzhi,sizeof(double),1,fp);
   fwrite(&boxxy,sizeof(double),1,fp);
   fwrite(&boxxz,sizeof(double),1,fp);
   fwrite(&boxyz,sizeof(double),1,fp);
   fwrite(&size_one,sizeof(int),1,fp);
   if (multiproc) fwrite(&nclusterprocs,sizeof(int),1,fp);
   else fwrite(&nprocs,sizeof(int),1,fp);
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::header_item(bigint ndump)
 {
   fprintf(fp,"ITEM: TIMESTEP\n");
   fprintf(fp,BIGINT_FORMAT "\n",update->ntimestep);
   fprintf(fp,"ITEM: NUMBER OF ATOMS\n");
   fprintf(fp,BIGINT_FORMAT "\n",ndump);
   fprintf(fp,"ITEM: BOX BOUNDS %s\n",boundstr);
   fprintf(fp,"%g %g\n",boxxlo,boxxhi);
   fprintf(fp,"%g %g\n",boxylo,boxyhi);
   fprintf(fp,"%g %g\n",boxzlo,boxzhi);
   fprintf(fp,"ITEM: ATOMS %s\n",columns);
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::header_item_triclinic(bigint ndump)
 {
   fprintf(fp,"ITEM: TIMESTEP\n");
   fprintf(fp,BIGINT_FORMAT "\n",update->ntimestep);
   fprintf(fp,"ITEM: NUMBER OF ATOMS\n");
   fprintf(fp,BIGINT_FORMAT "\n",ndump);
   fprintf(fp,"ITEM: BOX BOUNDS xy xz yz %s\n",boundstr);
   fprintf(fp,"%g %g %g\n",boxxlo,boxxhi,boxxy);
   fprintf(fp,"%g %g %g\n",boxylo,boxyhi,boxxz);
   fprintf(fp,"%g %g %g\n",boxzlo,boxzhi,boxyz);
   fprintf(fp,"ITEM: ATOMS %s\n",columns);
 }
 
 /* ---------------------------------------------------------------------- */
 
 int DumpCustom::count()
 {
   int i;
 
   // grow choose and variable vbuf arrays if needed
 
   int nlocal = atom->nlocal;
   if (nlocal > maxlocal) {
     maxlocal = atom->nmax;
 
     memory->destroy(choose);
     memory->destroy(dchoose);
     memory->destroy(clist);
     memory->create(choose,maxlocal,"dump:choose");
     memory->create(dchoose,maxlocal,"dump:dchoose");
     memory->create(clist,maxlocal,"dump:clist");
 
     for (i = 0; i < nvariable; i++) {
       memory->destroy(vbuf[i]);
       memory->create(vbuf[i],maxlocal,"dump:vbuf");
     }
   }
 
   // invoke Computes for per-atom quantities
 
   if (ncompute) {
     for (i = 0; i < ncompute; i++)
       if (!(compute[i]->invoked_flag & INVOKED_PERATOM)) {
         compute[i]->compute_peratom();
         compute[i]->invoked_flag |= INVOKED_PERATOM;
       }
   }
 
   // evaluate atom-style Variables for per-atom quantities
 
   if (nvariable)
     for (i = 0; i < nvariable; i++)
       input->variable->compute_atom(variable[i],igroup,vbuf[i],1,0);
 
   // choose all local atoms for output
 
   for (i = 0; i < nlocal; i++) choose[i] = 1;
 
   // un-choose if not in group
 
   if (igroup) {
     int *mask = atom->mask;
     for (i = 0; i < nlocal; i++)
       if (!(mask[i] & groupbit))
         choose[i] = 0;
   }
 
   // un-choose if not in region
 
   if (iregion >= 0) {
     Region *region = domain->regions[iregion];
     region->prematch();
     double **x = atom->x;
     for (i = 0; i < nlocal; i++)
       if (choose[i] && region->match(x[i][0],x[i][1],x[i][2]) == 0)
         choose[i] = 0;
   }
 
   // un-choose if any threshhold criterion isn't met
 
   if (nthresh) {
     double *ptr;
     double value;
     int nstride;
     int nlocal = atom->nlocal;
 
     for (int ithresh = 0; ithresh < nthresh; ithresh++) {
 
       // customize by adding to if statement
 
       if (thresh_array[ithresh] == ID) {
         tagint *tag = atom->tag;
         for (i = 0; i < nlocal; i++) dchoose[i] = tag[i];
         ptr = dchoose;
         nstride = 1;
       } else if (thresh_array[ithresh] == MOL) {
         if (!atom->molecule_flag)
           error->all(FLERR,
                      "Threshhold for an atom property that isn't allocated");
         tagint *molecule = atom->molecule;
         for (i = 0; i < nlocal; i++) dchoose[i] = molecule[i];
         ptr = dchoose;
         nstride = 1;
+      } else if (thresh_array[ithresh] == PROC) {
+        for (i = 0; i < nlocal; i++) dchoose[i] = me;
+        ptr = dchoose;
+        nstride = 1;
       } else if (thresh_array[ithresh] == TYPE) {
         int *type = atom->type;
         for (i = 0; i < nlocal; i++) dchoose[i] = type[i];
         ptr = dchoose;
         nstride = 1;
       } else if (thresh_array[ithresh] == ELEMENT) {
         int *type = atom->type;
         for (i = 0; i < nlocal; i++) dchoose[i] = type[i];
         ptr = dchoose;
         nstride = 1;
       } else if (thresh_array[ithresh] == MASS) {
         if (atom->rmass) {
           ptr = atom->rmass;
           nstride = 1;
         } else {
           double *mass = atom->mass;
           int *type = atom->type;
           for (i = 0; i < nlocal; i++) dchoose[i] = mass[type[i]];
           ptr = dchoose;
           nstride = 1;
         }
 
       } else if (thresh_array[ithresh] == X) {
         ptr = &atom->x[0][0];
         nstride = 3;
       } else if (thresh_array[ithresh] == Y) {
         ptr = &atom->x[0][1];
         nstride = 3;
       } else if (thresh_array[ithresh] == Z) {
         ptr = &atom->x[0][2];
         nstride = 3;
 
       } else if (thresh_array[ithresh] == XS) {
         double **x = atom->x;
         double boxxlo = domain->boxlo[0];
         double invxprd = 1.0/domain->xprd;
         for (i = 0; i < nlocal; i++)
           dchoose[i] = (x[i][0] - boxxlo) * invxprd;
         ptr = dchoose;
         nstride = 1;
       } else if (thresh_array[ithresh] == YS) {
         double **x = atom->x;
         double boxylo = domain->boxlo[1];
         double invyprd = 1.0/domain->yprd;
         for (i = 0; i < nlocal; i++)
           dchoose[i] = (x[i][1] - boxylo) * invyprd;
         ptr = dchoose;
         nstride = 1;
       } else if (thresh_array[ithresh] == ZS) {
         double **x = atom->x;
         double boxzlo = domain->boxlo[2];
         double invzprd = 1.0/domain->zprd;
         for (i = 0; i < nlocal; i++)
           dchoose[i] = (x[i][2] - boxzlo) * invzprd;
         ptr = dchoose;
         nstride = 1;
 
       } else if (thresh_array[ithresh] == XSTRI) {
         double **x = atom->x;
         double *boxlo = domain->boxlo;
         double *h_inv = domain->h_inv;
         for (i = 0; i < nlocal; i++)
           dchoose[i] = h_inv[0]*(x[i][0]-boxlo[0]) +
             h_inv[5]*(x[i][1]-boxlo[1]) + h_inv[4]*(x[i][2]-boxlo[2]);
         ptr = dchoose;
         nstride = 1;
       } else if (thresh_array[ithresh] == YSTRI) {
         double **x = atom->x;
         double *boxlo = domain->boxlo;
         double *h_inv = domain->h_inv;
         for (i = 0; i < nlocal; i++)
           dchoose[i] = h_inv[1]*(x[i][1]-boxlo[1]) +
             h_inv[3]*(x[i][2]-boxlo[2]);
         ptr = dchoose;
         nstride = 1;
       } else if (thresh_array[ithresh] == ZSTRI) {
         double **x = atom->x;
         double *boxlo = domain->boxlo;
         double *h_inv = domain->h_inv;
         for (i = 0; i < nlocal; i++)
           dchoose[i] = h_inv[2]*(x[i][2]-boxlo[2]);
         ptr = dchoose;
         nstride = 1;
 
       } else if (thresh_array[ithresh] == XU) {
         double **x = atom->x;
         imageint *image = atom->image;
         double xprd = domain->xprd;
         for (i = 0; i < nlocal; i++)
           dchoose[i] = x[i][0] + ((image[i] & IMGMASK) - IMGMAX) * xprd;
         ptr = dchoose;
         nstride = 1;
       } else if (thresh_array[ithresh] == YU) {
         double **x = atom->x;
         imageint *image = atom->image;
         double yprd = domain->yprd;
         for (i = 0; i < nlocal; i++)
           dchoose[i] = x[i][1] + 
             ((image[i] >> IMGBITS & IMGMASK) - IMGMAX) * yprd;
         ptr = dchoose;
         nstride = 1;
       } else if (thresh_array[ithresh] == ZU) {
         double **x = atom->x;
         imageint *image = atom->image;
         double zprd = domain->zprd;
         for (i = 0; i < nlocal; i++)
           dchoose[i] = x[i][2] + ((image[i] >> IMG2BITS) - IMGMAX) * zprd;
         ptr = dchoose;
         nstride = 1;
 
       } else if (thresh_array[ithresh] == XUTRI) {
         double **x = atom->x;
         imageint *image = atom->image;
         double *h = domain->h;
         int xbox,ybox,zbox;
         for (i = 0; i < nlocal; i++) {
           xbox = (image[i] & IMGMASK) - IMGMAX;
           ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX;
           zbox = (image[i] >> IMG2BITS) - IMGMAX;
           dchoose[i] = x[i][0] + h[0]*xbox + h[5]*ybox + h[4]*zbox;
         }
         ptr = dchoose;
         nstride = 1;
       } else if (thresh_array[ithresh] == YUTRI) {
         double **x = atom->x;
         imageint *image = atom->image;
         double *h = domain->h;
         int ybox,zbox;
         for (i = 0; i < nlocal; i++) {
           ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX;
           zbox = (image[i] >> IMG2BITS) - IMGMAX;
           dchoose[i] = x[i][1] + h[1]*ybox + h[3]*zbox;
         }
         ptr = dchoose;
         nstride = 1;
       } else if (thresh_array[ithresh] == ZUTRI) {
         double **x = atom->x;
         imageint *image = atom->image;
         double *h = domain->h;
         int zbox;
         for (i = 0; i < nlocal; i++) {
           zbox = (image[i] >> IMG2BITS) - IMGMAX;
           dchoose[i] = x[i][2] + h[2]*zbox;
         }
         ptr = dchoose;
         nstride = 1;
 
       } else if (thresh_array[ithresh] == XSU) {
         double **x = atom->x;
         imageint *image = atom->image;
         double boxxlo = domain->boxlo[0];
         double invxprd = 1.0/domain->xprd;
         for (i = 0; i < nlocal; i++)
           dchoose[i] = (x[i][0] - boxxlo) * invxprd + 
             (image[i] & IMGMASK) - IMGMAX;
         ptr = dchoose;
         nstride = 1;
 
       } else if (thresh_array[ithresh] == YSU) {
         double **x = atom->x;
         imageint *image = atom->image;
         double boxylo = domain->boxlo[1];
         double invyprd = 1.0/domain->yprd;
         for (i = 0; i < nlocal; i++)
           dchoose[i] =
             (x[i][1] - boxylo) * invyprd + 
             (image[i] >> IMGBITS & IMGMASK) - IMGMAX;
         ptr = dchoose;
         nstride = 1;
 
       } else if (thresh_array[ithresh] == ZSU) {
         double **x = atom->x;
         imageint *image = atom->image;
         double boxzlo = domain->boxlo[2];
         double invzprd = 1.0/domain->zprd;
         for (i = 0; i < nlocal; i++)
           dchoose[i] = (x[i][2] - boxzlo) * invzprd + 
             (image[i] >> IMG2BITS) - IMGMAX;
         ptr = dchoose;
         nstride = 1;
 
       } else if (thresh_array[ithresh] == XSUTRI) {
         double **x = atom->x;
         imageint *image = atom->image;
         double *boxlo = domain->boxlo;
         double *h_inv = domain->h_inv;
         for (i = 0; i < nlocal; i++)
           dchoose[i] = h_inv[0]*(x[i][0]-boxlo[0]) +
             h_inv[5]*(x[i][1]-boxlo[1]) +
             h_inv[4]*(x[i][2]-boxlo[2]) +
             (image[i] & IMGMASK) - IMGMAX;
         ptr = dchoose;
         nstride = 1;
       } else if (thresh_array[ithresh] == YSUTRI) {
         double **x = atom->x;
         imageint *image = atom->image;
         double *boxlo = domain->boxlo;
         double *h_inv = domain->h_inv;
         for (i = 0; i < nlocal; i++)
           dchoose[i] = h_inv[1]*(x[i][1]-boxlo[1]) +
             h_inv[3]*(x[i][2]-boxlo[2]) +
             (image[i] >> IMGBITS & IMGMASK) - IMGMAX;
         ptr = dchoose;
         nstride = 1;
       } else if (thresh_array[ithresh] == ZSUTRI) {
         double **x = atom->x;
         imageint *image = atom->image;
         double *boxlo = domain->boxlo;
         double *h_inv = domain->h_inv;
         for (i = 0; i < nlocal; i++)
           dchoose[i] = h_inv[2]*(x[i][2]-boxlo[2]) +
             (image[i] >> IMG2BITS) - IMGMAX;
         ptr = dchoose;
         nstride = 1;
 
       } else if (thresh_array[ithresh] == IX) {
         imageint *image = atom->image;
         for (i = 0; i < nlocal; i++)
           dchoose[i] = (image[i] & IMGMASK) - IMGMAX;
         ptr = dchoose;
         nstride = 1;
       } else if (thresh_array[ithresh] == IY) {
         imageint *image = atom->image;
         for (i = 0; i < nlocal; i++)
           dchoose[i] = (image[i] >> IMGBITS & IMGMASK) - IMGMAX;
         ptr = dchoose;
         nstride = 1;
       } else if (thresh_array[ithresh] == IZ) {
         imageint *image = atom->image;
         for (i = 0; i < nlocal; i++)
           dchoose[i] = (image[i] >> IMG2BITS) - IMGMAX;
         ptr = dchoose;
         nstride = 1;
 
       } else if (thresh_array[ithresh] == VX) {
         ptr = &atom->v[0][0];
         nstride = 3;
       } else if (thresh_array[ithresh] == VY) {
         ptr = &atom->v[0][1];
         nstride = 3;
       } else if (thresh_array[ithresh] == VZ) {
         ptr = &atom->v[0][2];
         nstride = 3;
       } else if (thresh_array[ithresh] == FX) {
         ptr = &atom->f[0][0];
         nstride = 3;
       } else if (thresh_array[ithresh] == FY) {
         ptr = &atom->f[0][1];
         nstride = 3;
       } else if (thresh_array[ithresh] == FZ) {
         ptr = &atom->f[0][2];
         nstride = 3;
 
       } else if (thresh_array[ithresh] == Q) {
         if (!atom->q_flag)
           error->all(FLERR,
                      "Threshhold for an atom property that isn't allocated");
         ptr = atom->q;
         nstride = 1;
       } else if (thresh_array[ithresh] == MUX) {
         if (!atom->mu_flag)
           error->all(FLERR,
                      "Threshhold for an atom property that isn't allocated");
         ptr = &atom->mu[0][0];
         nstride = 4;
       } else if (thresh_array[ithresh] == MUY) {
         if (!atom->mu_flag)
           error->all(FLERR,
                      "Threshhold for an atom property that isn't allocated");
         ptr = &atom->mu[0][1];
         nstride = 4;
       } else if (thresh_array[ithresh] == MUZ) {
         if (!atom->mu_flag)
           error->all(FLERR,
                      "Threshhold for an atom property that isn't allocated");
         ptr = &atom->mu[0][2];
         nstride = 4;
       } else if (thresh_array[ithresh] == MU) {
         if (!atom->mu_flag)
           error->all(FLERR,
                      "Threshhold for an atom property that isn't allocated");
         ptr = &atom->mu[0][3];
         nstride = 4;
 
       } else if (thresh_array[ithresh] == RADIUS) {
         if (!atom->radius_flag)
           error->all(FLERR,
                      "Threshhold for an atom property that isn't allocated");
         ptr = atom->radius;
         nstride = 1;
       } else if (thresh_array[ithresh] == DIAMETER) {
         if (!atom->radius_flag)
           error->all(FLERR,
                      "Threshhold for an atom property that isn't allocated");
         double *radius = atom->radius;
         for (i = 0; i < nlocal; i++) dchoose[i] = 2.0*radius[i];
         ptr = dchoose;
         nstride = 1;
       } else if (thresh_array[ithresh] == OMEGAX) {
         if (!atom->omega_flag)
           error->all(FLERR,
                      "Threshhold for an atom property that isn't allocated");
         ptr = &atom->omega[0][0];
         nstride = 3;
       } else if (thresh_array[ithresh] == OMEGAY) {
         if (!atom->omega_flag)
           error->all(FLERR,
                      "Threshhold for an atom property that isn't allocated");
         ptr = &atom->omega[0][1];
         nstride = 3;
       } else if (thresh_array[ithresh] == OMEGAZ) {
         if (!atom->omega_flag)
           error->all(FLERR,
                      "Threshhold for an atom property that isn't allocated");
         ptr = &atom->omega[0][2];
         nstride = 3;
       } else if (thresh_array[ithresh] == ANGMOMX) {
         if (!atom->angmom_flag)
           error->all(FLERR,
                      "Threshhold for an atom property that isn't allocated");
         ptr = &atom->angmom[0][0];
         nstride = 3;
       } else if (thresh_array[ithresh] == ANGMOMY) {
         if (!atom->angmom_flag)
           error->all(FLERR,
                      "Threshhold for an atom property that isn't allocated");
         ptr = &atom->angmom[0][1];
         nstride = 3;
       } else if (thresh_array[ithresh] == ANGMOMZ) {
         if (!atom->angmom_flag)
           error->all(FLERR,
                      "Threshhold for an atom property that isn't allocated");
         ptr = &atom->angmom[0][2];
         nstride = 3;
       } else if (thresh_array[ithresh] == TQX) {
         if (!atom->torque_flag)
           error->all(FLERR,
                      "Threshhold for an atom property that isn't allocated");
         ptr = &atom->torque[0][0];
         nstride = 3;
       } else if (thresh_array[ithresh] == TQY) {
         if (!atom->torque_flag)
           error->all(FLERR,
                      "Threshhold for an atom property that isn't allocated");
         ptr = &atom->torque[0][1];
         nstride = 3;
       } else if (thresh_array[ithresh] == TQZ) {
         if (!atom->torque_flag)
           error->all(FLERR,
                      "Threshhold for an atom property that isn't allocated");
         ptr = &atom->torque[0][2];
         nstride = 3;
 
       } else if (thresh_array[ithresh] == COMPUTE) {
         i = nfield + ithresh;
         if (argindex[i] == 0) {
           ptr = compute[field2index[i]]->vector_atom;
           nstride = 1;
         } else {
           ptr = &compute[field2index[i]]->array_atom[0][argindex[i]-1];
           nstride = compute[field2index[i]]->size_peratom_cols;
         }
 
       } else if (thresh_array[ithresh] == FIX) {
         i = nfield + ithresh;
         if (argindex[i] == 0) {
           ptr = fix[field2index[i]]->vector_atom;
           nstride = 1;
         } else {
           ptr = &fix[field2index[i]]->array_atom[0][argindex[i]-1];
           nstride = fix[field2index[i]]->size_peratom_cols;
         }
 
       } else if (thresh_array[ithresh] == VARIABLE) {
         i = nfield + ithresh;
         ptr = vbuf[field2index[i]];
         nstride = 1;
+
+      } else if (thresh_array[ithresh] == DNAME) {
+	int iwhich,tmp;
+        i = nfield + ithresh;
+	iwhich = atom->find_custom(id_custom[field2index[i]],tmp);
+        ptr = atom->dvector[iwhich];
+        nstride = 1;
+
+      } else if (thresh_array[ithresh] == INAME) {
+	int iwhich,tmp;
+        i = nfield + ithresh;
+	iwhich = atom->find_custom(id_custom[field2index[i]],tmp);
+
+        int *ivector = atom->ivector[iwhich];
+        for (i = 0; i < nlocal; i++)
+          dchoose[i] = ivector[i];
+        ptr = dchoose;
+        nstride = 1;
       }
 
       // unselect atoms that don't meet threshhold criterion
 
       value = thresh_value[ithresh];
 
       if (thresh_op[ithresh] == LT) {
         for (i = 0; i < nlocal; i++, ptr += nstride)
           if (choose[i] && *ptr >= value) choose[i] = 0;
       } else if (thresh_op[ithresh] == LE) {
         for (i = 0; i < nlocal; i++, ptr += nstride)
           if (choose[i] && *ptr > value) choose[i] = 0;
       } else if (thresh_op[ithresh] == GT) {
         for (i = 0; i < nlocal; i++, ptr += nstride)
           if (choose[i] && *ptr <= value) choose[i] = 0;
       } else if (thresh_op[ithresh] == GE) {
         for (i = 0; i < nlocal; i++, ptr += nstride)
           if (choose[i] && *ptr < value) choose[i] = 0;
       } else if (thresh_op[ithresh] == EQ) {
         for (i = 0; i < nlocal; i++, ptr += nstride)
           if (choose[i] && *ptr != value) choose[i] = 0;
       } else if (thresh_op[ithresh] == NEQ) {
         for (i = 0; i < nlocal; i++, ptr += nstride)
           if (choose[i] && *ptr == value) choose[i] = 0;
       }
     }
   }
 
   // compress choose flags into clist
   // nchoose = # of selected atoms
   // clist[i] = local index of each selected atom
 
   nchoose = 0;
   for (i = 0; i < nlocal; i++)
     if (choose[i]) clist[nchoose++] = i;
 
   return nchoose;
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack(tagint *ids)
 {
   for (int n = 0; n < size_one; n++) (this->*pack_choice[n])(n);
   if (ids) {
     tagint *tag = atom->tag;
     for (int i = 0; i < nchoose; i++)
       ids[i] = tag[clist[i]];
   }
 }
 
 /* ----------------------------------------------------------------------
    convert mybuf of doubles to one big formatted string in sbuf
    return -1 if strlen exceeds an int, since used as arg in MPI calls in Dump
 ------------------------------------------------------------------------- */
 
 int DumpCustom::convert_string(int n, double *mybuf)
 {
   int i,j;
 
   int offset = 0;
   int m = 0;
   for (i = 0; i < n; i++) {
     if (offset + size_one*ONEFIELD > maxsbuf) {
       if ((bigint) maxsbuf + DELTA > MAXSMALLINT) return -1;
       maxsbuf += DELTA;
       memory->grow(sbuf,maxsbuf,"dump:sbuf");
     }
 
     for (j = 0; j < size_one; j++) {
       if (vtype[j] == INT) 
         offset += sprintf(&sbuf[offset],vformat[j],static_cast<int> (mybuf[m]));
       else if (vtype[j] == DOUBLE) 
         offset += sprintf(&sbuf[offset],vformat[j],mybuf[m]);
       else if (vtype[j] == STRING)
         offset += sprintf(&sbuf[offset],vformat[j],typenames[(int) mybuf[m]]);
       else if (vtype[j] == BIGINT) 
         offset += sprintf(&sbuf[offset],vformat[j],
                           static_cast<bigint> (mybuf[m]));
       m++;
     }
     offset += sprintf(&sbuf[offset],"\n");
   }
 
   return offset;
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::write_data(int n, double *mybuf)
 {
   (this->*write_choice)(n,mybuf);
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::write_binary(int n, double *mybuf)
 {
   n *= size_one;
   fwrite(&n,sizeof(int),1,fp);
   fwrite(mybuf,sizeof(double),n,fp);
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::write_string(int n, double *mybuf)
 {
   fwrite(mybuf,sizeof(char),n,fp);
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::write_lines(int n, double *mybuf)
 {
   int i,j;
 
   int m = 0;
   for (i = 0; i < n; i++) {
     for (j = 0; j < size_one; j++) {
       if (vtype[j] == INT) fprintf(fp,vformat[j],static_cast<int> (mybuf[m]));
       else if (vtype[j] == DOUBLE) fprintf(fp,vformat[j],mybuf[m]);
       else if (vtype[j] == STRING)
         fprintf(fp,vformat[j],typenames[(int) mybuf[m]]);
       else if (vtype[j] == BIGINT) 
         fprintf(fp,vformat[j],static_cast<bigint> (mybuf[m]));
       m++;
     }
     fprintf(fp,"\n");
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 int DumpCustom::parse_fields(int narg, char **arg)
 {
   // customize by adding to if statement
 
   int i;
   for (int iarg = 5; iarg < narg; iarg++) {
     i = iarg-5;
 
     if (strcmp(arg[iarg],"id") == 0) {
       pack_choice[i] = &DumpCustom::pack_id;
       if (sizeof(tagint) == sizeof(smallint)) vtype[i] = INT;
       else vtype[i] = BIGINT;
     } else if (strcmp(arg[iarg],"mol") == 0) {
       if (!atom->molecule_flag)
         error->all(FLERR,"Dumping an atom property that isn't allocated");
       pack_choice[i] = &DumpCustom::pack_molecule;
       vtype[i] = INT;
+    } else if (strcmp(arg[iarg],"proc") == 0) {
+      pack_choice[i] = &DumpCustom::pack_proc;
+      vtype[i] = INT;
     } else if (strcmp(arg[iarg],"type") == 0) {
       pack_choice[i] = &DumpCustom::pack_type;
       vtype[i] = INT;
     } else if (strcmp(arg[iarg],"element") == 0) {
       pack_choice[i] = &DumpCustom::pack_type;
       vtype[i] = STRING;
     } else if (strcmp(arg[iarg],"mass") == 0) {
       pack_choice[i] = &DumpCustom::pack_mass;
       vtype[i] = DOUBLE;
 
     } else if (strcmp(arg[iarg],"x") == 0) {
       pack_choice[i] = &DumpCustom::pack_x;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"y") == 0) {
       pack_choice[i] = &DumpCustom::pack_y;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"z") == 0) {
       pack_choice[i] = &DumpCustom::pack_z;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"xs") == 0) {
       if (domain->triclinic) pack_choice[i] = &DumpCustom::pack_xs_triclinic;
       else pack_choice[i] = &DumpCustom::pack_xs;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"ys") == 0) {
       if (domain->triclinic) pack_choice[i] = &DumpCustom::pack_ys_triclinic;
       else pack_choice[i] = &DumpCustom::pack_ys;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"zs") == 0) {
       if (domain->triclinic) pack_choice[i] = &DumpCustom::pack_zs_triclinic;
       else pack_choice[i] = &DumpCustom::pack_zs;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"xu") == 0) {
       if (domain->triclinic) pack_choice[i] = &DumpCustom::pack_xu_triclinic;
       else pack_choice[i] = &DumpCustom::pack_xu;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"yu") == 0) {
       if (domain->triclinic) pack_choice[i] = &DumpCustom::pack_yu_triclinic;
       else pack_choice[i] = &DumpCustom::pack_yu;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"zu") == 0) {
       if (domain->triclinic) pack_choice[i] = &DumpCustom::pack_zu_triclinic;
       else pack_choice[i] = &DumpCustom::pack_zu;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"xsu") == 0) {
       if (domain->triclinic) pack_choice[i] = &DumpCustom::pack_xsu_triclinic;
       else pack_choice[i] = &DumpCustom::pack_xsu;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"ysu") == 0) {
       if (domain->triclinic) pack_choice[i] = &DumpCustom::pack_ysu_triclinic;
       else pack_choice[i] = &DumpCustom::pack_ysu;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"zsu") == 0) {
       if (domain->triclinic) pack_choice[i] = &DumpCustom::pack_zsu_triclinic;
       else pack_choice[i] = &DumpCustom::pack_zsu;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"ix") == 0) {
       pack_choice[i] = &DumpCustom::pack_ix;
       vtype[i] = INT;
     } else if (strcmp(arg[iarg],"iy") == 0) {
       pack_choice[i] = &DumpCustom::pack_iy;
       vtype[i] = INT;
     } else if (strcmp(arg[iarg],"iz") == 0) {
       pack_choice[i] = &DumpCustom::pack_iz;
       vtype[i] = INT;
 
     } else if (strcmp(arg[iarg],"vx") == 0) {
       pack_choice[i] = &DumpCustom::pack_vx;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"vy") == 0) {
       pack_choice[i] = &DumpCustom::pack_vy;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"vz") == 0) {
       pack_choice[i] = &DumpCustom::pack_vz;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"fx") == 0) {
       pack_choice[i] = &DumpCustom::pack_fx;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"fy") == 0) {
       pack_choice[i] = &DumpCustom::pack_fy;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"fz") == 0) {
       pack_choice[i] = &DumpCustom::pack_fz;
       vtype[i] = DOUBLE;
 
     } else if (strcmp(arg[iarg],"q") == 0) {
       if (!atom->q_flag)
         error->all(FLERR,"Dumping an atom property that isn't allocated");
       pack_choice[i] = &DumpCustom::pack_q;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"mux") == 0) {
       if (!atom->mu_flag)
         error->all(FLERR,"Dumping an atom property that isn't allocated");
       pack_choice[i] = &DumpCustom::pack_mux;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"muy") == 0) {
       if (!atom->mu_flag)
         error->all(FLERR,"Dumping an atom property that isn't allocated");
       pack_choice[i] = &DumpCustom::pack_muy;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"muz") == 0) {
       if (!atom->mu_flag)
         error->all(FLERR,"Dumping an atom property that isn't allocated");
       pack_choice[i] = &DumpCustom::pack_muz;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"mu") == 0) {
       if (!atom->mu_flag)
         error->all(FLERR,"Dumping an atom property that isn't allocated");
       pack_choice[i] = &DumpCustom::pack_mu;
       vtype[i] = DOUBLE;
 
     } else if (strcmp(arg[iarg],"radius") == 0) {
       if (!atom->radius_flag)
         error->all(FLERR,"Dumping an atom property that isn't allocated");
       pack_choice[i] = &DumpCustom::pack_radius;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"diameter") == 0) {
       if (!atom->radius_flag)
         error->all(FLERR,"Dumping an atom property that isn't allocated");
       pack_choice[i] = &DumpCustom::pack_diameter;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"omegax") == 0) {
       if (!atom->omega_flag)
         error->all(FLERR,"Dumping an atom property that isn't allocated");
       pack_choice[i] = &DumpCustom::pack_omegax;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"omegay") == 0) {
       if (!atom->omega_flag)
         error->all(FLERR,"Dumping an atom property that isn't allocated");
       pack_choice[i] = &DumpCustom::pack_omegay;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"omegaz") == 0) {
       if (!atom->omega_flag)
         error->all(FLERR,"Dumping an atom property that isn't allocated");
       pack_choice[i] = &DumpCustom::pack_omegaz;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"angmomx") == 0) {
       if (!atom->angmom_flag)
         error->all(FLERR,"Dumping an atom property that isn't allocated");
       pack_choice[i] = &DumpCustom::pack_angmomx;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"angmomy") == 0) {
       if (!atom->angmom_flag)
         error->all(FLERR,"Dumping an atom property that isn't allocated");
       pack_choice[i] = &DumpCustom::pack_angmomy;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"angmomz") == 0) {
       if (!atom->angmom_flag)
         error->all(FLERR,"Dumping an atom property that isn't allocated");
       pack_choice[i] = &DumpCustom::pack_angmomz;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"tqx") == 0) {
       if (!atom->torque_flag)
         error->all(FLERR,"Dumping an atom property that isn't allocated");
       pack_choice[i] = &DumpCustom::pack_tqx;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"tqy") == 0) {
       if (!atom->torque_flag)
         error->all(FLERR,"Dumping an atom property that isn't allocated");
       pack_choice[i] = &DumpCustom::pack_tqy;
       vtype[i] = DOUBLE;
     } else if (strcmp(arg[iarg],"tqz") == 0) {
       if (!atom->torque_flag)
         error->all(FLERR,"Dumping an atom property that isn't allocated");
       pack_choice[i] = &DumpCustom::pack_tqz;
       vtype[i] = DOUBLE;
 
     // compute value = c_ID
     // if no trailing [], then arg is set to 0, else arg is int between []
 
     } else if (strncmp(arg[iarg],"c_",2) == 0) {
       pack_choice[i] = &DumpCustom::pack_compute;
       vtype[i] = DOUBLE;
 
       int n = strlen(arg[iarg]);
       char *suffix = new char[n];
       strcpy(suffix,&arg[iarg][2]);
 
       char *ptr = strchr(suffix,'[');
       if (ptr) {
         if (suffix[strlen(suffix)-1] != ']')
           error->all(FLERR,"Invalid attribute in dump custom command");
         argindex[i] = atoi(ptr+1);
         *ptr = '\0';
       } else argindex[i] = 0;
 
       n = modify->find_compute(suffix);
       if (n < 0) error->all(FLERR,"Could not find dump custom compute ID");
       if (modify->compute[n]->peratom_flag == 0)
         error->all(FLERR,"Dump custom compute does not compute per-atom info");
       if (argindex[i] == 0 && modify->compute[n]->size_peratom_cols > 0)
         error->all(FLERR,
                    "Dump custom compute does not calculate per-atom vector");
       if (argindex[i] > 0 && modify->compute[n]->size_peratom_cols == 0)
         error->all(FLERR,
                    "Dump custom compute does not calculate per-atom array");
       if (argindex[i] > 0 &&
           argindex[i] > modify->compute[n]->size_peratom_cols)
         error->all(FLERR,"Dump custom compute vector is accessed out-of-range");
 
       field2index[i] = add_compute(suffix);
       delete [] suffix;
 
     // fix value = f_ID
     // if no trailing [], then arg is set to 0, else arg is between []
 
     } else if (strncmp(arg[iarg],"f_",2) == 0) {
       pack_choice[i] = &DumpCustom::pack_fix;
       vtype[i] = DOUBLE;
 
       int n = strlen(arg[iarg]);
       char *suffix = new char[n];
       strcpy(suffix,&arg[iarg][2]);
 
       char *ptr = strchr(suffix,'[');
       if (ptr) {
         if (suffix[strlen(suffix)-1] != ']')
           error->all(FLERR,"Invalid attribute in dump custom command");
         argindex[i] = atoi(ptr+1);
         *ptr = '\0';
       } else argindex[i] = 0;
 
       n = modify->find_fix(suffix);
       if (n < 0) error->all(FLERR,"Could not find dump custom fix ID");
       if (modify->fix[n]->peratom_flag == 0)
         error->all(FLERR,"Dump custom fix does not compute per-atom info");
       if (argindex[i] == 0 && modify->fix[n]->size_peratom_cols > 0)
         error->all(FLERR,"Dump custom fix does not compute per-atom vector");
       if (argindex[i] > 0 && modify->fix[n]->size_peratom_cols == 0)
         error->all(FLERR,"Dump custom fix does not compute per-atom array");
       if (argindex[i] > 0 &&
           argindex[i] > modify->fix[n]->size_peratom_cols)
         error->all(FLERR,"Dump custom fix vector is accessed out-of-range");
 
       field2index[i] = add_fix(suffix);
       delete [] suffix;
 
     // variable value = v_name
 
     } else if (strncmp(arg[iarg],"v_",2) == 0) {
       pack_choice[i] = &DumpCustom::pack_variable;
       vtype[i] = DOUBLE;
 
       int n = strlen(arg[iarg]);
       char *suffix = new char[n];
       strcpy(suffix,&arg[iarg][2]);
 
       argindex[i] = 0;
 
       n = input->variable->find(suffix);
       if (n < 0) error->all(FLERR,"Could not find dump custom variable name");
       if (input->variable->atomstyle(n) == 0)
         error->all(FLERR,"Dump custom variable is not atom-style variable");
 
       field2index[i] = add_variable(suffix);
       delete [] suffix;
 
+    // custom per-atom floating point value = d_ID
+
+    } else if (strncmp(arg[iarg],"d_",2) == 0) {
+      pack_choice[i] = &DumpCustom::pack_custom;
+      vtype[i] = DOUBLE;
+
+      int n = strlen(arg[iarg]);
+      char *suffix = new char[n];
+      strcpy(suffix,&arg[iarg][2]);
+      argindex[i] = 0;
+
+      int tmp = -1;
+      n = atom->find_custom(suffix,tmp);
+      if (n < 0)
+        error->all(FLERR,"Could not find custom per-atom property ID");
+      
+      if (tmp != 1)
+        error->all(FLERR,"Custom per-atom property ID is not floating point");
+
+      field2index[i] = add_custom(suffix,1);
+      delete [] suffix;
+
+    // custom per-atom integer value = i_ID
+
+    } else if (strncmp(arg[iarg],"i_",2) == 0) {
+      pack_choice[i] = &DumpCustom::pack_custom;
+      vtype[i] = INT;
+
+      int n = strlen(arg[iarg]);
+      char *suffix = new char[n];
+      strcpy(suffix,&arg[iarg][2]);
+      argindex[i] = 0;
+
+      int tmp = -1;
+      n = atom->find_custom(suffix,tmp);
+      if (n < 0)
+        error->all(FLERR,"Could not find custom per-atom property ID");
+      
+      if (tmp != 0)
+        error->all(FLERR,"Custom per-atom property ID is not integer");
+
+      field2index[i] = add_custom(suffix,0);
+      delete [] suffix;
+
     } else return iarg;
   }
 
   return narg;
 }
 
 /* ----------------------------------------------------------------------
    add Compute to list of Compute objects used by dump
    return index of where this Compute is in list
    if already in list, do not add, just return index, else add to list
 ------------------------------------------------------------------------- */
 
 int DumpCustom::add_compute(char *id)
 {
   int icompute;
   for (icompute = 0; icompute < ncompute; icompute++)
     if (strcmp(id,id_compute[icompute]) == 0) break;
   if (icompute < ncompute) return icompute;
 
   id_compute = (char **)
     memory->srealloc(id_compute,(ncompute+1)*sizeof(char *),"dump:id_compute");
   delete [] compute;
   compute = new Compute*[ncompute+1];
 
   int n = strlen(id) + 1;
   id_compute[ncompute] = new char[n];
   strcpy(id_compute[ncompute],id);
   ncompute++;
   return ncompute-1;
 }
 
 /* ----------------------------------------------------------------------
    add Fix to list of Fix objects used by dump
    return index of where this Fix is in list
    if already in list, do not add, just return index, else add to list
 ------------------------------------------------------------------------- */
 
 int DumpCustom::add_fix(char *id)
 {
   int ifix;
   for (ifix = 0; ifix < nfix; ifix++)
     if (strcmp(id,id_fix[ifix]) == 0) break;
   if (ifix < nfix) return ifix;
 
   id_fix = (char **)
     memory->srealloc(id_fix,(nfix+1)*sizeof(char *),"dump:id_fix");
   delete [] fix;
   fix = new Fix*[nfix+1];
 
   int n = strlen(id) + 1;
   id_fix[nfix] = new char[n];
   strcpy(id_fix[nfix],id);
   nfix++;
   return nfix-1;
 }
 
 /* ----------------------------------------------------------------------
    add Variable to list of Variables used by dump
    return index of where this Variable is in list
    if already in list, do not add, just return index, else add to list
 ------------------------------------------------------------------------- */
 
 int DumpCustom::add_variable(char *id)
 {
   int ivariable;
   for (ivariable = 0; ivariable < nvariable; ivariable++)
     if (strcmp(id,id_variable[ivariable]) == 0) break;
   if (ivariable < nvariable) return ivariable;
 
   id_variable = (char **)
     memory->srealloc(id_variable,(nvariable+1)*sizeof(char *),
                      "dump:id_variable");
   delete [] variable;
   variable = new int[nvariable+1];
   delete [] vbuf;
   vbuf = new double*[nvariable+1];
   for (int i = 0; i <= nvariable; i++) vbuf[i] = NULL;
 
   int n = strlen(id) + 1;
   id_variable[nvariable] = new char[n];
   strcpy(id_variable[nvariable],id);
   nvariable++;
   return nvariable-1;
 }
 
+/* ----------------------------------------------------------------------
+   add custom atom property to list used by dump
+   return index of where this property is in list
+   if already in list, do not add, just return index, else add to list
+------------------------------------------------------------------------- */
+
+int DumpCustom::add_custom(char *id, int flag)
+{
+  int icustom;
+  for (icustom = 0; icustom < ncustom; icustom++)
+    if ((strcmp(id,id_custom[icustom]) == 0)
+        && (flag == flag_custom[icustom])) break;
+  if (icustom < ncustom) return icustom;
+
+  id_custom = (char **)
+    memory->srealloc(id_custom,(ncustom+1)*sizeof(char *),"dump:id_custom");
+  flag_custom = (int *)
+    memory->srealloc(flag_custom,(ncustom+1)*sizeof(int),"dump:flag_custom");
+  
+  int n = strlen(id) + 1;
+  id_custom[ncustom] = new char[n];
+  strcpy(id_custom[ncustom],id);
+  flag_custom[ncustom] = flag;
+
+  ncustom++;
+  return ncustom-1;
+}
+
 /* ---------------------------------------------------------------------- */
 
 int DumpCustom::modify_param(int narg, char **arg)
 {
   if (strcmp(arg[0],"region") == 0) {
     if (narg < 2) error->all(FLERR,"Illegal dump_modify command");
     if (strcmp(arg[1],"none") == 0) iregion = -1;
     else {
       iregion = domain->find_region(arg[1]);
       if (iregion == -1)
         error->all(FLERR,"Dump_modify region ID does not exist");
       int n = strlen(arg[1]) + 1;
       idregion = new char[n];
       strcpy(idregion,arg[1]);
     }
     return 2;
   }
 
   if (strcmp(arg[0],"element") == 0) {
     if (narg < ntypes+1)
       error->all(FLERR,"Dump modify element names do not match atom types");
 
     if (typenames) {
       for (int i = 1; i <= ntypes; i++) delete [] typenames[i];
       delete [] typenames;
       typenames = NULL;
     }
 
     typenames = new char*[ntypes+1];
     for (int itype = 1; itype <= ntypes; itype++) {
       int n = strlen(arg[itype]) + 1;
       typenames[itype] = new char[n];
       strcpy(typenames[itype],arg[itype]);
     }
     return ntypes+1;
   }
 
   if (strcmp(arg[0],"thresh") == 0) {
     if (narg < 2) error->all(FLERR,"Illegal dump_modify command");
     if (strcmp(arg[1],"none") == 0) {
       if (nthresh) {
         memory->destroy(thresh_array);
         memory->destroy(thresh_op);
         memory->destroy(thresh_value);
         thresh_array = NULL;
         thresh_op = NULL;
         thresh_value = NULL;
       }
       nthresh = 0;
       return 2;
     }
 
     if (narg < 4) error->all(FLERR,"Illegal dump_modify command");
 
     // grow threshhold arrays
 
     memory->grow(thresh_array,nthresh+1,"dump:thresh_array");
     memory->grow(thresh_op,(nthresh+1),"dump:thresh_op");
     memory->grow(thresh_value,(nthresh+1),"dump:thresh_value");
 
     // set attribute type of threshhold
     // customize by adding to if statement
 
     if (strcmp(arg[1],"id") == 0) thresh_array[nthresh] = ID;
     else if (strcmp(arg[1],"mol") == 0) thresh_array[nthresh] = MOL;
+    else if (strcmp(arg[1],"proc") == 0) thresh_array[nthresh] = PROC;
     else if (strcmp(arg[1],"type") == 0) thresh_array[nthresh] = TYPE;
     else if (strcmp(arg[1],"mass") == 0) thresh_array[nthresh] = MASS;
 
     else if (strcmp(arg[1],"x") == 0) thresh_array[nthresh] = X;
     else if (strcmp(arg[1],"y") == 0) thresh_array[nthresh] = Y;
     else if (strcmp(arg[1],"z") == 0) thresh_array[nthresh] = Z;
 
     else if (strcmp(arg[1],"xs") == 0 && domain->triclinic == 0)
       thresh_array[nthresh] = XS;
     else if (strcmp(arg[1],"xs") == 0 && domain->triclinic == 1)
       thresh_array[nthresh] = XSTRI;
     else if (strcmp(arg[1],"ys") == 0 && domain->triclinic == 0)
       thresh_array[nthresh] = YS;
     else if (strcmp(arg[1],"ys") == 0 && domain->triclinic == 1)
       thresh_array[nthresh] = YSTRI;
     else if (strcmp(arg[1],"zs") == 0 && domain->triclinic == 0)
       thresh_array[nthresh] = ZS;
     else if (strcmp(arg[1],"zs") == 0 && domain->triclinic == 1)
       thresh_array[nthresh] = ZSTRI;
 
     else if (strcmp(arg[1],"xu") == 0 && domain->triclinic == 0)
       thresh_array[nthresh] = XU;
     else if (strcmp(arg[1],"xu") == 0 && domain->triclinic == 1)
       thresh_array[nthresh] = XUTRI;
     else if (strcmp(arg[1],"yu") == 0 && domain->triclinic == 0)
       thresh_array[nthresh] = YU;
     else if (strcmp(arg[1],"yu") == 0 && domain->triclinic == 1)
       thresh_array[nthresh] = YUTRI;
     else if (strcmp(arg[1],"zu") == 0 && domain->triclinic == 0)
       thresh_array[nthresh] = ZU;
     else if (strcmp(arg[1],"zu") == 0 && domain->triclinic == 1)
       thresh_array[nthresh] = ZUTRI;
 
     else if (strcmp(arg[1],"xsu") == 0 && domain->triclinic == 0)
       thresh_array[nthresh] = XSU;
     else if (strcmp(arg[1],"xsu") == 0 && domain->triclinic == 1)
       thresh_array[nthresh] = XSUTRI;
     else if (strcmp(arg[1],"ysu") == 0 && domain->triclinic == 0)
       thresh_array[nthresh] = YSU;
     else if (strcmp(arg[1],"ysu") == 0 && domain->triclinic == 1)
       thresh_array[nthresh] = YSUTRI;
     else if (strcmp(arg[1],"zsu") == 0 && domain->triclinic == 0)
       thresh_array[nthresh] = ZSU;
     else if (strcmp(arg[1],"zsu") == 0 && domain->triclinic == 1)
       thresh_array[nthresh] = ZSUTRI;
 
     else if (strcmp(arg[1],"ix") == 0) thresh_array[nthresh] = IX;
     else if (strcmp(arg[1],"iy") == 0) thresh_array[nthresh] = IY;
     else if (strcmp(arg[1],"iz") == 0) thresh_array[nthresh] = IZ;
     else if (strcmp(arg[1],"vx") == 0) thresh_array[nthresh] = VX;
     else if (strcmp(arg[1],"vy") == 0) thresh_array[nthresh] = VY;
     else if (strcmp(arg[1],"vz") == 0) thresh_array[nthresh] = VZ;
     else if (strcmp(arg[1],"fx") == 0) thresh_array[nthresh] = FX;
     else if (strcmp(arg[1],"fy") == 0) thresh_array[nthresh] = FY;
     else if (strcmp(arg[1],"fz") == 0) thresh_array[nthresh] = FZ;
 
     else if (strcmp(arg[1],"q") == 0) thresh_array[nthresh] = Q;
     else if (strcmp(arg[1],"mux") == 0) thresh_array[nthresh] = MUX;
     else if (strcmp(arg[1],"muy") == 0) thresh_array[nthresh] = MUY;
     else if (strcmp(arg[1],"muz") == 0) thresh_array[nthresh] = MUZ;
     else if (strcmp(arg[1],"mu") == 0) thresh_array[nthresh] = MU;
 
     else if (strcmp(arg[1],"radius") == 0) thresh_array[nthresh] = RADIUS;
     else if (strcmp(arg[1],"diameter") == 0) thresh_array[nthresh] = DIAMETER;
     else if (strcmp(arg[1],"omegax") == 0) thresh_array[nthresh] = OMEGAX;
     else if (strcmp(arg[1],"omegay") == 0) thresh_array[nthresh] = OMEGAY;
     else if (strcmp(arg[1],"omegaz") == 0) thresh_array[nthresh] = OMEGAZ;
     else if (strcmp(arg[1],"angmomx") == 0) thresh_array[nthresh] = ANGMOMX;
     else if (strcmp(arg[1],"angmomy") == 0) thresh_array[nthresh] = ANGMOMY;
     else if (strcmp(arg[1],"angmomz") == 0) thresh_array[nthresh] = ANGMOMZ;
     else if (strcmp(arg[1],"tqx") == 0) thresh_array[nthresh] = TQX;
     else if (strcmp(arg[1],"tqy") == 0) thresh_array[nthresh] = TQY;
     else if (strcmp(arg[1],"tqz") == 0) thresh_array[nthresh] = TQZ;
 
     // compute value = c_ID
     // if no trailing [], then arg is set to 0, else arg is between []
     // must grow field2index and argindex arrays, since access is beyond nfield
 
     else if (strncmp(arg[1],"c_",2) == 0) {
       thresh_array[nthresh] = COMPUTE;
       memory->grow(field2index,nfield+nthresh+1,"dump:field2index");
       memory->grow(argindex,nfield+nthresh+1,"dump:argindex");
       int n = strlen(arg[1]);
       char *suffix = new char[n];
       strcpy(suffix,&arg[1][2]);
 
       char *ptr = strchr(suffix,'[');
       if (ptr) {
         if (suffix[strlen(suffix)-1] != ']')
           error->all(FLERR,"Invalid attribute in dump modify command");
         argindex[nfield+nthresh] = atoi(ptr+1);
         *ptr = '\0';
       } else argindex[nfield+nthresh] = 0;
 
       n = modify->find_compute(suffix);
       if (n < 0) error->all(FLERR,"Could not find dump modify compute ID");
 
       if (modify->compute[n]->peratom_flag == 0)
         error->all(FLERR,
                    "Dump modify compute ID does not compute per-atom info");
       if (argindex[nfield+nthresh] == 0 &&
           modify->compute[n]->size_peratom_cols > 0)
         error->all(FLERR,
                    "Dump modify compute ID does not compute per-atom vector");
       if (argindex[nfield+nthresh] > 0 &&
           modify->compute[n]->size_peratom_cols == 0)
         error->all(FLERR,
                    "Dump modify compute ID does not compute per-atom array");
       if (argindex[nfield+nthresh] > 0 &&
           argindex[nfield+nthresh] > modify->compute[n]->size_peratom_cols)
         error->all(FLERR,"Dump modify compute ID vector is not large enough");
 
       field2index[nfield+nthresh] = add_compute(suffix);
       delete [] suffix;
 
     // fix value = f_ID
     // if no trailing [], then arg is set to 0, else arg is between []
     // must grow field2index and argindex arrays, since access is beyond nfield
 
     } else if (strncmp(arg[1],"f_",2) == 0) {
       thresh_array[nthresh] = FIX;
       memory->grow(field2index,nfield+nthresh+1,"dump:field2index");
       memory->grow(argindex,nfield+nthresh+1,"dump:argindex");
       int n = strlen(arg[1]);
       char *suffix = new char[n];
       strcpy(suffix,&arg[1][2]);
 
       char *ptr = strchr(suffix,'[');
       if (ptr) {
         if (suffix[strlen(suffix)-1] != ']')
           error->all(FLERR,"Invalid attribute in dump modify command");
         argindex[nfield+nthresh] = atoi(ptr+1);
         *ptr = '\0';
       } else argindex[nfield+nthresh] = 0;
 
       n = modify->find_fix(suffix);
       if (n < 0) error->all(FLERR,"Could not find dump modify fix ID");
 
       if (modify->fix[n]->peratom_flag == 0)
         error->all(FLERR,"Dump modify fix ID does not compute per-atom info");
       if (argindex[nfield+nthresh] == 0 &&
           modify->fix[n]->size_peratom_cols > 0)
         error->all(FLERR,"Dump modify fix ID does not compute per-atom vector");
       if (argindex[nfield+nthresh] > 0 &&
           modify->fix[n]->size_peratom_cols == 0)
         error->all(FLERR,"Dump modify fix ID does not compute per-atom array");
       if (argindex[nfield+nthresh] > 0 &&
           argindex[nfield+nthresh] > modify->fix[n]->size_peratom_cols)
         error->all(FLERR,"Dump modify fix ID vector is not large enough");
 
       field2index[nfield+nthresh] = add_fix(suffix);
       delete [] suffix;
 
     // variable value = v_ID
     // must grow field2index and argindex arrays, since access is beyond nfield
 
     } else if (strncmp(arg[1],"v_",2) == 0) {
       thresh_array[nthresh] = VARIABLE;
       memory->grow(field2index,nfield+nthresh+1,"dump:field2index");
       memory->grow(argindex,nfield+nthresh+1,"dump:argindex");
       int n = strlen(arg[1]);
       char *suffix = new char[n];
       strcpy(suffix,&arg[1][2]);
 
       argindex[nfield+nthresh] = 0;
 
       n = input->variable->find(suffix);
       if (n < 0) error->all(FLERR,"Could not find dump modify variable name");
       if (input->variable->atomstyle(n) == 0)
         error->all(FLERR,"Dump modify variable is not atom-style variable");
 
       field2index[nfield+nthresh] = add_variable(suffix);
       delete [] suffix;
 
+    // custom per atom floating point value = d_ID
+    // must grow field2index and argindex arrays, since access is beyond nfield
+
+    } else if (strncmp(arg[1],"d_",2) == 0) {
+      thresh_array[nthresh] = DNAME;
+      memory->grow(field2index,nfield+nthresh+1,"dump:field2index");
+      memory->grow(argindex,nfield+nthresh+1,"dump:argindex");
+      int n = strlen(arg[1]);
+      char *suffix = new char[n];
+      strcpy(suffix,&arg[1][2]);
+      argindex[nfield+nthresh] = 0;
+
+      int tmp = -1;
+      n = atom->find_custom(suffix,tmp);
+      if ((n < 0) || (tmp != 1)) 
+        error->all(FLERR,"Could not find dump modify "
+                   "custom atom floating point property ID");
+
+      field2index[nfield+nthresh] = add_custom(suffix,1);
+      delete [] suffix;
+
+    // custom per atom integer value = i_ID
+    // must grow field2index and argindex arrays, since access is beyond nfield
+
+    } else if (strncmp(arg[1],"i_",2) == 0) {
+      thresh_array[nthresh] = INAME;
+      memory->grow(field2index,nfield+nthresh+1,"dump:field2index");
+      memory->grow(argindex,nfield+nthresh+1,"dump:argindex");
+      int n = strlen(arg[1]);
+      char *suffix = new char[n];
+      strcpy(suffix,&arg[1][2]);
+      argindex[nfield+nthresh] = 0;
+
+      int tmp = -1;
+      n = atom->find_custom(suffix,tmp);
+      if ((n < 0) || (tmp != 0)) 
+        error->all(FLERR,"Could not find dump modify "
+                   "custom atom integer property ID");
+
+      field2index[nfield+nthresh] = add_custom(suffix,0);
+      delete [] suffix;
+
     } else error->all(FLERR,"Invalid dump_modify threshhold operator");
 
     // set operation type of threshhold
 
     if (strcmp(arg[2],"<") == 0) thresh_op[nthresh] = LT;
     else if (strcmp(arg[2],"<=") == 0) thresh_op[nthresh] = LE;
     else if (strcmp(arg[2],">") == 0) thresh_op[nthresh] = GT;
     else if (strcmp(arg[2],">=") == 0) thresh_op[nthresh] = GE;
     else if (strcmp(arg[2],"==") == 0) thresh_op[nthresh] = EQ;
     else if (strcmp(arg[2],"!=") == 0) thresh_op[nthresh] = NEQ;
     else error->all(FLERR,"Invalid dump_modify threshhold operator");
 
     // set threshhold value
 
     thresh_value[nthresh] = force->numeric(FLERR,arg[3]);
 
     nthresh++;
     return 4;
   }
 
   return 0;
 }
 
 /* ----------------------------------------------------------------------
    return # of bytes of allocated memory in buf, choose, variable arrays
 ------------------------------------------------------------------------- */
 
 bigint DumpCustom::memory_usage()
 {
   bigint bytes = Dump::memory_usage();
   bytes += memory->usage(choose,maxlocal);
   bytes += memory->usage(dchoose,maxlocal);
   bytes += memory->usage(clist,maxlocal);
   bytes += memory->usage(vbuf,nvariable,maxlocal);
   return bytes;
 }
 
 /* ----------------------------------------------------------------------
    extraction of Compute, Fix, Variable results
 ------------------------------------------------------------------------- */
 
 void DumpCustom::pack_compute(int n)
 {
   double *vector = compute[field2index[n]]->vector_atom;
   double **array = compute[field2index[n]]->array_atom;
   int index = argindex[n];
 
   if (index == 0) {
     for (int i = 0; i < nchoose; i++) {
       buf[n] = vector[clist[i]];
       n += size_one;
     }
   } else {
     index--;
     for (int i = 0; i < nchoose; i++) {
       buf[n] = array[clist[i]][index];
       n += size_one;
     }
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_fix(int n)
 {
   double *vector = fix[field2index[n]]->vector_atom;
   double **array = fix[field2index[n]]->array_atom;
   int index = argindex[n];
 
   if (index == 0) {
     for (int i = 0; i < nchoose; i++) {
       buf[n] = vector[clist[i]];
       n += size_one;
     }
   } else {
     index--;
     for (int i = 0; i < nchoose; i++) {
       buf[n] = array[clist[i]][index];
       n += size_one;
     }
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_variable(int n)
 {
   double *vector = vbuf[field2index[n]];
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = vector[clist[i]];
     n += size_one;
   }
 }
 
+/* ---------------------------------------------------------------------- */
+
+void DumpCustom::pack_custom(int n)
+{
+  
+  int index = field2index[n];
+
+  if (flag_custom[index] == 0) { // integer
+    int iwhich,tmp;
+    iwhich = atom->find_custom(id_custom[index],tmp);
+
+    int *ivector = atom->ivector[iwhich];
+    for (int i = 0; i < nchoose; i++) {
+      buf[n] = ivector[clist[i]];
+      n += size_one;
+    }
+  } else if (flag_custom[index] == 1) { // double
+    int iwhich,tmp;
+    iwhich = atom->find_custom(id_custom[index],tmp);
+
+    double *dvector = atom->dvector[iwhich];
+    for (int i = 0; i < nchoose; i++) {
+      buf[n] = dvector[clist[i]];
+      n += size_one;
+    }
+  }
+}
+
 /* ----------------------------------------------------------------------
    one method for every attribute dump custom can output
    the atom property is packed into buf starting at n with stride size_one
    customize a new attribute by adding a method
 ------------------------------------------------------------------------- */
 
 void DumpCustom::pack_id(int n)
 {
   tagint *tag = atom->tag;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = tag[clist[i]];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_molecule(int n)
 {
   tagint *molecule = atom->molecule;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = molecule[clist[i]];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
+void DumpCustom::pack_proc(int n)
+{
+  for (int i = 0; i < nchoose; i++) {
+    buf[n] = me;
+    n += size_one;
+  }
+}
+
+/* ---------------------------------------------------------------------- */
+
 void DumpCustom::pack_type(int n)
 {
   int *type = atom->type;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = type[clist[i]];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_mass(int n)
 {
   int *type = atom->type;
   double *mass = atom->mass;
   double *rmass = atom->rmass;
 
   if (rmass) {
     for (int i = 0; i < nchoose; i++) {
       buf[n] = rmass[clist[i]];
       n += size_one;
     }
   } else {
     for (int i = 0; i < nchoose; i++) {
       buf[n] = mass[type[clist[i]]];
       n += size_one;
     }
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_x(int n)
 {
   double **x = atom->x;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = x[clist[i]][0];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_y(int n)
 {
   double **x = atom->x;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = x[clist[i]][1];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_z(int n)
 {
   double **x = atom->x;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = x[clist[i]][2];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_xs(int n)
 {
   double **x = atom->x;
 
   double boxxlo = domain->boxlo[0];
   double invxprd = 1.0/domain->xprd;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = (x[clist[i]][0] - boxxlo) * invxprd;
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_ys(int n)
 {
   double **x = atom->x;
 
   double boxylo = domain->boxlo[1];
   double invyprd = 1.0/domain->yprd;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = (x[clist[i]][1] - boxylo) * invyprd;
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_zs(int n)
 {
   double **x = atom->x;
 
   double boxzlo = domain->boxlo[2];
   double invzprd = 1.0/domain->zprd;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = (x[clist[i]][2] - boxzlo) * invzprd;
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_xs_triclinic(int n)
 {
   int j;
   double **x = atom->x;
 
   double *boxlo = domain->boxlo;
   double *h_inv = domain->h_inv;
 
   for (int i = 0; i < nchoose; i++) {
     j = clist[i];
     buf[n] = h_inv[0]*(x[j][0]-boxlo[0]) + h_inv[5]*(x[j][1]-boxlo[1]) +
       h_inv[4]*(x[j][2]-boxlo[2]);
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_ys_triclinic(int n)
 {
   int j;
   double **x = atom->x;
 
   double *boxlo = domain->boxlo;
   double *h_inv = domain->h_inv;
 
   for (int i = 0; i < nchoose; i++) {
     j = clist[i];
     buf[n] = h_inv[1]*(x[j][1]-boxlo[1]) + h_inv[3]*(x[j][2]-boxlo[2]);
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_zs_triclinic(int n)
 {
   double **x = atom->x;
 
   double *boxlo = domain->boxlo;
   double *h_inv = domain->h_inv;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = h_inv[2]*(x[clist[i]][2]-boxlo[2]);
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_xu(int n)
 {
   int j;
   double **x = atom->x;
   imageint *image = atom->image;
 
   double xprd = domain->xprd;
 
   for (int i = 0; i < nchoose; i++) {
     j = clist[i];
     buf[n] = x[j][0] + ((image[j] & IMGMASK) - IMGMAX) * xprd;
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_yu(int n)
 {
   int j;
   double **x = atom->x;
   imageint *image = atom->image;
 
   double yprd = domain->yprd;
 
   for (int i = 0; i < nchoose; i++) {
     j = clist[i];
     buf[n] = x[j][1] + ((image[j] >> IMGBITS & IMGMASK) - IMGMAX) * yprd;
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_zu(int n)
 {
   int j;
   double **x = atom->x;
   imageint *image = atom->image;
 
   double zprd = domain->zprd;
 
   for (int i = 0; i < nchoose; i++) {
     j = clist[i];
     buf[n] = x[j][2] + ((image[j] >> IMG2BITS) - IMGMAX) * zprd;
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_xu_triclinic(int n)
 {
   int j;
   double **x = atom->x;
   imageint *image = atom->image;
 
   double *h = domain->h;
   int xbox,ybox,zbox;
 
   for (int i = 0; i < nchoose; i++) {
     j = clist[i];
     xbox = (image[j] & IMGMASK) - IMGMAX;
     ybox = (image[j] >> IMGBITS & IMGMASK) - IMGMAX;
     zbox = (image[j] >> IMG2BITS) - IMGMAX;
     buf[n] = x[j][0] + h[0]*xbox + h[5]*ybox + h[4]*zbox;
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_yu_triclinic(int n)
 {
   int j;
   double **x = atom->x;
   imageint *image = atom->image;
 
   double *h = domain->h;
   int ybox,zbox;
 
   for (int i = 0; i < nchoose; i++) {
     j = clist[i];
     ybox = (image[j] >> IMGBITS & IMGMASK) - IMGMAX;
     zbox = (image[j] >> IMG2BITS) - IMGMAX;
     buf[n] = x[j][1] + h[1]*ybox + h[3]*zbox;
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_zu_triclinic(int n)
 {
   int j;
   double **x = atom->x;
   imageint *image = atom->image;
 
   double *h = domain->h;
   int zbox;
 
   for (int i = 0; i < nchoose; i++) {
     j = clist[i];
     zbox = (image[j] >> IMG2BITS) - IMGMAX;
     buf[n] = x[j][2] + h[2]*zbox;
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_xsu(int n)
 {
   int j;
   double **x = atom->x;
   imageint *image = atom->image;
 
   double boxxlo = domain->boxlo[0];
   double invxprd = 1.0/domain->xprd;
 
   for (int i = 0; i < nchoose; i++) {
     j = clist[i];
     buf[n] = (x[j][0] - boxxlo) * invxprd + (image[j] & IMGMASK) - IMGMAX;
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_ysu(int n)
 {
   int j;
   double **x = atom->x;
   imageint *image = atom->image;
 
   double boxylo = domain->boxlo[1];
   double invyprd = 1.0/domain->yprd;
 
   for (int i = 0; i < nchoose; i++) {
     j = clist[i];
     buf[n] = (x[j][1] - boxylo) * invyprd + (image[j] >> IMGBITS & IMGMASK) - IMGMAX;
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_zsu(int n)
 {
   int j;
   double **x = atom->x;
   imageint *image = atom->image;
 
   double boxzlo = domain->boxlo[2];
   double invzprd = 1.0/domain->zprd;
 
   for (int i = 0; i < nchoose; i++) {
     j = clist[i];
     buf[n] = (x[j][2] - boxzlo) * invzprd + (image[j] >> IMG2BITS) - IMGMAX;
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_xsu_triclinic(int n)
 {
   int j;
   double **x = atom->x;
   imageint *image = atom->image;
 
   double *boxlo = domain->boxlo;
   double *h_inv = domain->h_inv;
 
   for (int i = 0; i < nchoose; i++) {
     j = clist[i];
     buf[n] = h_inv[0]*(x[j][0]-boxlo[0]) + h_inv[5]*(x[j][1]-boxlo[1]) +
       h_inv[4]*(x[j][2]-boxlo[2]) + (image[j] & IMGMASK) - IMGMAX;
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_ysu_triclinic(int n)
 {
   int j;
   double **x = atom->x;
   imageint *image = atom->image;
 
   double *boxlo = domain->boxlo;
   double *h_inv = domain->h_inv;
 
   for (int i = 0; i < nchoose; i++) {
     j = clist[i];
     buf[n] = h_inv[1]*(x[j][1]-boxlo[1]) + h_inv[3]*(x[j][2]-boxlo[2]) +
       (image[j] >> IMGBITS & IMGMASK) - IMGMAX;
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_zsu_triclinic(int n)
 {
   int j;
   double **x = atom->x;
   imageint *image = atom->image;
 
   double *boxlo = domain->boxlo;
   double *h_inv = domain->h_inv;
 
   for (int i = 0; i < nchoose; i++) {
     j = clist[i];
     buf[n] = h_inv[2]*(x[j][2]-boxlo[2]) + (image[j] >> IMG2BITS) - IMGMAX;
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_ix(int n)
 {
   imageint *image = atom->image;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = (image[clist[i]] & IMGMASK) - IMGMAX;
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_iy(int n)
 {
   imageint *image = atom->image;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = (image[clist[i]] >> IMGBITS & IMGMASK) - IMGMAX;
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_iz(int n)
 {
   imageint *image = atom->image;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = (image[clist[i]] >> IMG2BITS) - IMGMAX;
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_vx(int n)
 {
   double **v = atom->v;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = v[clist[i]][0];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_vy(int n)
 {
   double **v = atom->v;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = v[clist[i]][1];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_vz(int n)
 {
   double **v = atom->v;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = v[clist[i]][2];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_fx(int n)
 {
   double **f = atom->f;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = f[clist[i]][0];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_fy(int n)
 {
   double **f = atom->f;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = f[clist[i]][1];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_fz(int n)
 {
   double **f = atom->f;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = f[clist[i]][2];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_q(int n)
 {
   double *q = atom->q;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = q[clist[i]];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_mux(int n)
 {
   double **mu = atom->mu;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = mu[clist[i]][0];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_muy(int n)
 {
   double **mu = atom->mu;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = mu[clist[i]][1];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_muz(int n)
 {
   double **mu = atom->mu;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = mu[clist[i]][2];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_mu(int n)
 {
   double **mu = atom->mu;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = mu[clist[i]][3];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_radius(int n)
 {
   double *radius = atom->radius;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = radius[clist[i]];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_diameter(int n)
 {
   double *radius = atom->radius;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = 2.0*radius[clist[i]];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_omegax(int n)
 {
   double **omega = atom->omega;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = omega[clist[i]][0];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_omegay(int n)
 {
   double **omega = atom->omega;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = omega[clist[i]][1];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_omegaz(int n)
 {
   double **omega = atom->omega;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = omega[clist[i]][2];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_angmomx(int n)
 {
   double **angmom = atom->angmom;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = angmom[clist[i]][0];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_angmomy(int n)
 {
   double **angmom = atom->angmom;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = angmom[clist[i]][1];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_angmomz(int n)
 {
   double **angmom = atom->angmom;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = angmom[clist[i]][2];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_tqx(int n)
 {
   double **torque = atom->torque;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = torque[clist[i]][0];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_tqy(int n)
 {
   double **torque = atom->torque;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = torque[clist[i]][1];
     n += size_one;
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void DumpCustom::pack_tqz(int n)
 {
   double **torque = atom->torque;
 
   for (int i = 0; i < nchoose; i++) {
     buf[n] = torque[clist[i]][2];
     n += size_one;
   }
 }
diff --git a/src/dump_custom.h b/src/dump_custom.h
index b0153faf2..c483d5b7e 100644
--- a/src/dump_custom.h
+++ b/src/dump_custom.h
@@ -1,337 +1,344 @@
 /* -*- 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 DUMP_CLASS
 
 DumpStyle(custom,DumpCustom)
 
 #else
 
 #ifndef LMP_DUMP_CUSTOM_H
 #define LMP_DUMP_CUSTOM_H
 
 #include "dump.h"
 
 namespace LAMMPS_NS {
 
 class DumpCustom : public Dump {
  public:
   DumpCustom(class LAMMPS *, int, char **);
   virtual ~DumpCustom();
 
  protected:
   int nevery;                // dump frequency for output
   int iregion;               // -1 if no region, else which region
   char *idregion;            // region ID
   int nthresh;               // # of defined threshholds
   int *thresh_array;         // array to threshhhold on for each nthresh
   int *thresh_op;            // threshhold operation for each nthresh
   double *thresh_value;      // threshhold value for each nthresh
 
   int *vtype;                // type of each vector (INT, DOUBLE)
   char **vformat;            // format string for each vector element
 
   char *columns;             // column labels
 
   int nchoose;               // # of selected atoms
   int maxlocal;              // size of atom selection and variable arrays
   int *choose;               // local indices of selected atoms
   double *dchoose;           // value for each atom to threshhold against
   int *clist;                // compressed list of indices of selected atoms
 
   int nfield;                // # of keywords listed by user
   int ioptional;             // index of start of optional args
 
   int *field2index;          // which compute,fix,variable calcs this field
   int *argindex;             // index into compute,fix scalar_atom,vector_atom
                              // 0 for scalar_atom, 1-N for vector_atom values
 
   int ncompute;              // # of Compute objects used by dump
   char **id_compute;         // their IDs
   class Compute **compute;   // list of ptrs to the Compute objects
 
   int nfix;                  // # of Fix objects used by dump
   char **id_fix;             // their IDs
   class Fix **fix;           // list of ptrs to the Fix objects
 
   int nvariable;             // # of Variables used by dump
   char **id_variable;        // their names
   int *variable;             // list of indices for the Variables
   double **vbuf;             // local storage for variable evaluation
 
+  int ncustom;               // # of custom atom properties
+  char **id_custom;          // their names
+  int *flag_custom;          // their data type
+
   int ntypes;                // # of atom types
   char **typenames;             // array of element names for each type
 
   // private methods
 
   virtual void init_style();
   virtual void write_header(bigint);
   int count();
   void pack(tagint *);
   virtual int convert_string(int, double *);
   virtual void write_data(int, double *);
   bigint memory_usage();
 
   int parse_fields(int, char **);
   int add_compute(char *);
   int add_fix(char *);
   int add_variable(char *);
+  int add_custom(char *, int);
   virtual int modify_param(int, char **);
 
   typedef void (DumpCustom::*FnPtrHeader)(bigint);
   FnPtrHeader header_choice;           // ptr to write header functions
   void header_binary(bigint);
   void header_binary_triclinic(bigint);
   void header_item(bigint);
   void header_item_triclinic(bigint);
 
   typedef int (DumpCustom::*FnPtrConvert)(int, double *);
   FnPtrConvert convert_choice;          // ptr to convert data functions
   int convert_image(int, double *);
   int convert_noimage(int, double *);
 
   typedef void (DumpCustom::*FnPtrWrite)(int, double *);
   FnPtrWrite write_choice;             // ptr to write data functions
   void write_binary(int, double *);
   void write_string(int, double *);
   void write_lines(int, double *);
 
   // customize by adding a method prototype
 
   typedef void (DumpCustom::*FnPtrPack)(int);
   FnPtrPack *pack_choice;              // ptrs to pack functions
 
   void pack_compute(int);
   void pack_fix(int);
   void pack_variable(int);
+  void pack_custom(int);
 
   void pack_id(int);
   void pack_molecule(int);
+  void pack_proc(int);
   void pack_type(int);
   void pack_mass(int);
 
   void pack_x(int);
   void pack_y(int);
   void pack_z(int);
   void pack_xs(int);
   void pack_ys(int);
   void pack_zs(int);
   void pack_xs_triclinic(int);
   void pack_ys_triclinic(int);
   void pack_zs_triclinic(int);
   void pack_xu(int);
   void pack_yu(int);
   void pack_zu(int);
   void pack_xu_triclinic(int);
   void pack_yu_triclinic(int);
   void pack_zu_triclinic(int);
   void pack_xsu(int);
   void pack_ysu(int);
   void pack_zsu(int);
   void pack_xsu_triclinic(int);
   void pack_ysu_triclinic(int);
   void pack_zsu_triclinic(int);
   void pack_ix(int);
   void pack_iy(int);
   void pack_iz(int);
 
   void pack_vx(int);
   void pack_vy(int);
   void pack_vz(int);
   void pack_fx(int);
   void pack_fy(int);
   void pack_fz(int);
   void pack_q(int);
   void pack_mux(int);
   void pack_muy(int);
   void pack_muz(int);
   void pack_mu(int);
   void pack_radius(int);
   void pack_diameter(int);
 
   void pack_omegax(int);
   void pack_omegay(int);
   void pack_omegaz(int);
   void pack_angmomx(int);
   void pack_angmomy(int);
   void pack_angmomz(int);
   void pack_tqx(int);
   void pack_tqy(int);
   void pack_tqz(int);
 };
 
 }
 
 #endif
 #endif
 
 /* ERROR/WARNING messages:
 
 E: No dump custom arguments specified
 
 The dump custom command requires that atom quantities be specified to
 output to dump file.
 
 E: Invalid attribute in dump custom command
 
 Self-explantory.
 
 E: Dump_modify format string is too short
 
 There are more fields to be dumped in a line of output than your
 format string specifies.
 
 E: Could not find dump custom compute ID
 
 Self-explanatory.
 
 E: Could not find dump custom fix ID
 
 Self-explanatory.
 
 E: Dump custom and fix not computed at compatible times
 
 The fix must produce per-atom quantities on timesteps that dump custom
 needs them.
 
 E: Could not find dump custom variable name
 
 Self-explanatory.
 
 E: Region ID for dump custom does not exist
 
 Self-explanatory.
 
 E: Threshhold for an atom property that isn't allocated
 
 A dump threshhold has been requested on a quantity that is
 not defined by the atom style used in this simulation.
 
 E: Dumping an atom property that isn't allocated
 
 The chosen atom style does not define the per-atom quantity being
 dumped.
 
 E: Dumping an atom quantity that isn't allocated
 
 Only per-atom quantities that are defined for the atom style being
 used are allowed.
 
 E: Dump custom compute does not compute per-atom info
 
 Self-explanatory.
 
 E: Dump custom compute does not calculate per-atom vector
 
 Self-explanatory.
 
 E: Dump custom compute does not calculate per-atom array
 
 Self-explanatory.
 
 E: Dump custom compute vector is accessed out-of-range
 
 Self-explanatory.
 
 E: Dump custom fix does not compute per-atom info
 
 Self-explanatory.
 
 E: Dump custom fix does not compute per-atom vector
 
 Self-explanatory.
 
 E: Dump custom fix does not compute per-atom array
 
 Self-explanatory.
 
 E: Dump custom fix vector is accessed out-of-range
 
 Self-explanatory.
 
 E: Dump custom variable is not atom-style variable
 
 Only atom-style variables generate per-atom quantities, needed for
 dump output.
 
 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: Dump_modify region ID does not exist
 
 Self-explanatory.
 
 E: Dump modify element names do not match atom types
 
 Number of element names must equal number of atom types.
 
 E: Invalid attribute in dump modify command
 
 Self-explantory.
 
 E: Could not find dump modify compute ID
 
 Self-explanatory.
 
 E: Dump modify compute ID does not compute per-atom info
 
 Self-explanatory.
 
 E: Dump modify compute ID does not compute per-atom vector
 
 Self-explanatory.
 
 E: Dump modify compute ID does not compute per-atom array
 
 Self-explanatory.
 
 E: Dump modify compute ID vector is not large enough
 
 Self-explanatory.
 
 E: Could not find dump modify fix ID
 
 Self-explanatory.
 
 E: Dump modify fix ID does not compute per-atom info
 
 Self-explanatory.
 
 E: Dump modify fix ID does not compute per-atom vector
 
 Self-explanatory.
 
 E: Dump modify fix ID does not compute per-atom array
 
 Self-explanatory.
 
 E: Dump modify fix ID vector is not large enough
 
 Self-explanatory.
 
 E: Could not find dump modify variable name
 
 Self-explanatory.
 
 E: Dump modify variable is not atom-style variable
 
 Self-explanatory.
 
 E: Invalid dump_modify threshhold operator
 
 Operator keyword used for threshold specification in not recognized.
 
 */
diff --git a/src/library.cpp b/src/library.cpp
index 2ca41aa96..2018c0ee7 100644
--- a/src/library.cpp
+++ b/src/library.cpp
@@ -1,534 +1,537 @@
 /* ----------------------------------------------------------------------
    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.
 ------------------------------------------------------------------------- */
 
 // C or Fortran style library interface to LAMMPS
 // customize by adding new LAMMPS-specific functions
 
 #include "lmptype.h"
 #include "mpi.h"
 #include "string.h"
 #include "stdlib.h"
 #include "library.h"
 #include "lammps.h"
 #include "input.h"
 #include "atom.h"
 #include "domain.h"
 #include "update.h"
 #include "group.h"
 #include "input.h"
 #include "variable.h"
 #include "modify.h"
 #include "compute.h"
 #include "fix.h"
 #include "comm.h"
 #include "memory.h"
 #include "error.h"
 
 using namespace LAMMPS_NS;
 
 /* ----------------------------------------------------------------------
    create an instance of LAMMPS and return pointer to it
    pass in command-line args and MPI communicator to run on
 ------------------------------------------------------------------------- */
 
 void lammps_open(int argc, char **argv, MPI_Comm communicator, void **ptr)
 {
   LAMMPS *lmp = new LAMMPS(argc,argv,communicator);
   *ptr = (void *) lmp;
 }
 
 /* ----------------------------------------------------------------------
    create an instance of LAMMPS and return pointer to it
    caller doesn't know MPI communicator, so use MPI_COMM_WORLD
    intialize MPI if needed
 ------------------------------------------------------------------------- */
 
 void lammps_open_no_mpi(int argc, char **argv, void **ptr)
 {
   int flag;
   MPI_Initialized(&flag);
 
   if (!flag) {
     int argc = 0;
     char **argv = NULL;
     MPI_Init(&argc,&argv);
   }
 
   MPI_Comm communicator = MPI_COMM_WORLD;
 
   LAMMPS *lmp = new LAMMPS(argc,argv,communicator);
   *ptr = (void *) lmp;
 }
 
 /* ----------------------------------------------------------------------
    destruct an instance of LAMMPS
 ------------------------------------------------------------------------- */
 
 void lammps_close(void *ptr)
 {
   LAMMPS *lmp = (LAMMPS *) ptr;
   delete lmp;
 }
 
 /* ----------------------------------------------------------------------
    process an input script in filename str
 ------------------------------------------------------------------------- */
 
 void lammps_file(void *ptr, char *str)
 {
   LAMMPS *lmp = (LAMMPS *) ptr;
   lmp->input->file(str);
 }
 
 /* ----------------------------------------------------------------------
    process a single input command in str
 ------------------------------------------------------------------------- */
 
 char *lammps_command(void *ptr, char *str)
 {
   LAMMPS *lmp = (LAMMPS *) ptr;
   return lmp->input->one(str);
 }
 
 /* ----------------------------------------------------------------------
    clean-up function to free memory allocated by lib and returned to caller
 ------------------------------------------------------------------------- */
 
 void lammps_free(void *ptr)
 {
   free(ptr);
 }
 
 /* ----------------------------------------------------------------------
    add LAMMPS-specific library functions
    all must receive LAMMPS pointer as argument
    customize by adding a function here and in library.h header file
 ------------------------------------------------------------------------- */
 
 /* ----------------------------------------------------------------------
    extract a pointer to an internal LAMMPS global entity
    name = desired quantity, e.g. dt or boxyhi or natoms
    returns a void pointer to the entity
      which the caller can cast to the proper data type
    returns a NULL if name not listed below
    customize by adding names
 ------------------------------------------------------------------------- */
 
 void *lammps_extract_global(void *ptr, char *name)
 {
   LAMMPS *lmp = (LAMMPS *) ptr;
 
   if (strcmp(name,"dt") == 0) return (void *) &lmp->update->dt;
   if (strcmp(name,"boxxlo") == 0) return (void *) &lmp->domain->boxlo[0];
   if (strcmp(name,"boxxhi") == 0) return (void *) &lmp->domain->boxhi[0];
   if (strcmp(name,"boxylo") == 0) return (void *) &lmp->domain->boxlo[1];
   if (strcmp(name,"boxyhi") == 0) return (void *) &lmp->domain->boxhi[1];
   if (strcmp(name,"boxzlo") == 0) return (void *) &lmp->domain->boxlo[2];
   if (strcmp(name,"boxzhi") == 0) return (void *) &lmp->domain->boxhi[2];
+  if (strcmp(name,"xy") == 0) return (void *) &lmp->domain->xy;
+  if (strcmp(name,"xz") == 0) return (void *) &lmp->domain->xz;
+  if (strcmp(name,"yz") == 0) return (void *) &lmp->domain->yz;
   if (strcmp(name,"natoms") == 0) return (void *) &lmp->atom->natoms;
   if (strcmp(name,"nlocal") == 0) return (void *) &lmp->atom->nlocal;
   return NULL;
 }
 
 /* ----------------------------------------------------------------------
    extract a pointer to an internal LAMMPS atom-based entity
    name = desired quantity, e.g. x or mass
    returns a void pointer to the entity
      which the caller can cast to the proper data type
    returns a NULL if Atom::extract() does not recognize the name
    customize by adding names to Atom::extract()
 ------------------------------------------------------------------------- */
 
 void *lammps_extract_atom(void *ptr, char *name)
 {
   LAMMPS *lmp = (LAMMPS *) ptr;
   return lmp->atom->extract(name);
 }
 
 /* ----------------------------------------------------------------------
    extract a pointer to an internal LAMMPS compute-based entity
    id = compute ID
    style = 0 for global data, 1 for per-atom data, 2 for local data
    type = 0 for scalar, 1 for vector, 2 for array
    for global data, returns a pointer to the
      compute's internal data structure for the entity
      caller should cast it to (double *) for a scalar or vector
      caller should cast it to (double **) for an array
    for per-atom or local data, returns a pointer to the
      compute's internal data structure for the entity
      caller should cast it to (double *) for a vector
      caller should cast it to (double **) for an array
    returns a void pointer to the compute's internal data structure
      for the entity which the caller can cast to the proper data type
    returns a NULL if id is not recognized or style/type not supported
    IMPORTANT: if the compute is not current it will be invoked
      LAMMPS cannot easily check here if it is valid to invoke the compute,
      so caller must insure that it is OK
 ------------------------------------------------------------------------- */
 
 void *lammps_extract_compute(void *ptr, char *id, int style, int type)
 {
   LAMMPS *lmp = (LAMMPS *) ptr;
 
   int icompute = lmp->modify->find_compute(id);
   if (icompute < 0) return NULL;
   Compute *compute = lmp->modify->compute[icompute];
 
   if (style == 0) {
     if (type == 0) {
       if (!compute->scalar_flag) return NULL;
       if (compute->invoked_scalar != lmp->update->ntimestep)
         compute->compute_scalar();
       return (void *) &compute->scalar;
     }
     if (type == 1) {
       if (!compute->vector_flag) return NULL;
       if (compute->invoked_vector != lmp->update->ntimestep)
         compute->compute_vector();
       return (void *) compute->vector;
     }
     if (type == 2) {
       if (!compute->array_flag) return NULL;
       if (compute->invoked_array != lmp->update->ntimestep)
         compute->compute_array();
       return (void *) compute->array;
     }
   }
 
   if (style == 1) {
     if (!compute->peratom_flag) return NULL;
     if (type == 1) {
       if (compute->invoked_peratom != lmp->update->ntimestep)
         compute->compute_peratom();
       return (void *) compute->vector_atom;
     }
     if (type == 2) {
       if (compute->invoked_peratom != lmp->update->ntimestep)
         compute->compute_peratom();
       return (void *) compute->array_atom;
     }
   }
 
   if (style == 2) {
     if (!compute->local_flag) return NULL;
     if (type == 1) {
       if (compute->invoked_local != lmp->update->ntimestep)
         compute->compute_local();
       return (void *) compute->vector_local;
     }
     if (type == 2) {
       if (compute->invoked_local != lmp->update->ntimestep)
         compute->compute_local();
       return (void *) compute->array_local;
     }
   }
 
   return NULL;
 }
 
 /* ----------------------------------------------------------------------
    extract a pointer to an internal LAMMPS fix-based entity
    id = fix ID
    style = 0 for global data, 1 for per-atom data, 2 for local data
    type = 0 for scalar, 1 for vector, 2 for array
    i,j = indices needed only to specify which global vector or array value
    for global data, returns a pointer to a memory location
      which is allocated by this function
      which the caller can cast to a (double *) which points to the value
    for per-atom or local data, returns a pointer to the
      fix's internal data structure for the entity
      caller should cast it to (double *) for a vector
      caller should cast it to (double **) for an array
    returns a NULL if id is not recognized or style/type not supported
    IMPORTANT: for global data,
      this function allocates a double to store the value in,
      so the caller must free this memory to avoid a leak, e.g.
        double *dptr = (double *) lammps_extract_fix();
        double value = *dptr;
        lammps_free(dptr);
    IMPORTANT: LAMMPS cannot easily check here when info extracted from
      the fix is valid, so caller must insure that it is OK
 ------------------------------------------------------------------------- */
 
 void *lammps_extract_fix(void *ptr, char *id, int style, int type,
                          int i, int j)
 {
   LAMMPS *lmp = (LAMMPS *) ptr;
 
   int ifix = lmp->modify->find_fix(id);
   if (ifix < 0) return NULL;
   Fix *fix = lmp->modify->fix[ifix];
 
   if (style == 0) {
     double *dptr = (double *) malloc(sizeof(double));
     if (type == 0) {
       if (!fix->scalar_flag) return NULL;
       *dptr = fix->compute_scalar();
       return (void *) dptr;
     }
     if (type == 1) {
       if (!fix->vector_flag) return NULL;
       *dptr = fix->compute_vector(i);
       return (void *) dptr;
     }
     if (type == 2) {
       if (!fix->array_flag) return NULL;
       *dptr = fix->compute_array(i,j);
       return (void *) dptr;
     }
   }
 
   if (style == 1) {
     if (!fix->peratom_flag) return NULL;
     if (type == 1) return (void *) fix->vector_atom;
     if (type == 2) return (void *) fix->array_atom;
   }
 
   if (style == 2) {
     if (!fix->local_flag) return NULL;
     if (type == 1) return (void *) fix->vector_local;
     if (type == 2) return (void *) fix->array_local;
   }
 
   return NULL;
 }
 
 /* ----------------------------------------------------------------------
    extract a pointer to an internal LAMMPS evaluated variable
    name = variable name, must be equal-style or atom-style variable
    group = group ID for evaluating an atom-style variable, else NULL
    for equal-style variable, returns a pointer to a memory location
      which is allocated by this function
      which the caller can cast to a (double *) which points to the value
    for atom-style variable, returns a pointer to the
      vector of per-atom values on each processor,
      which the caller can cast to a (double *) which points to the values
    returns a NULL if name is not recognized or not equal-style or atom-style
    IMPORTANT: for both equal-style and atom-style variables,
      this function allocates memory to store the variable data in
      so the caller must free this memory to avoid a leak
      e.g. for equal-style variables
        double *dptr = (double *) lammps_extract_variable();
        double value = *dptr;
        lammps_free(dptr);
      e.g. for atom-style variables
        double *vector = (double *) lammps_extract_variable();
        use the vector values
        lammps_free(vector);
    IMPORTANT: LAMMPS cannot easily check here when it is valid to evaluate
      the variable or any fixes or computes or thermodynamic info it references,
      so caller must insure that it is OK
 ------------------------------------------------------------------------- */
 
 void *lammps_extract_variable(void *ptr, char *name, char *group)
 {
   LAMMPS *lmp = (LAMMPS *) ptr;
 
   int ivar = lmp->input->variable->find(name);
   if (ivar < 0) return NULL;
 
   if (lmp->input->variable->equalstyle(ivar)) {
     double *dptr = (double *) malloc(sizeof(double));
     *dptr = lmp->input->variable->compute_equal(ivar);
     return (void *) dptr;
   }
 
   if (lmp->input->variable->atomstyle(ivar)) {
     int igroup = lmp->group->find(group);
     if (igroup < 0) return NULL;
     int nlocal = lmp->atom->nlocal;
     double *vector = (double *) malloc(nlocal*sizeof(double));
     lmp->input->variable->compute_atom(ivar,igroup,vector,1,0);
     return (void *) vector;
   }
 
   return NULL;
 }
 
 /* ----------------------------------------------------------------------
    return the total number of atoms in the system
    useful before call to lammps_get_atoms() so can pre-allocate vector
 ------------------------------------------------------------------------- */
 
 int lammps_get_natoms(void *ptr)
 {
   LAMMPS *lmp = (LAMMPS *) ptr;
   if (lmp->atom->natoms > MAXSMALLINT) return 0;
   int natoms = static_cast<int> (lmp->atom->natoms);
   return natoms;
 }
 
 /* ----------------------------------------------------------------------
    gather the named atom-based entity across all processors
    name = desired quantity, e.g. x or charge
    type = 0 for integer values, 1 for double values
    count = # of per-atom values, e.g. 1 for type or charge, 3 for x or f
    return atom-based values in data, ordered by count, then by atom ID
      e.g. x[0][0],x[0][1],x[0][2],x[1][0],x[1][1],x[1][2],x[2][0],...
    data must be pre-allocated by caller to correct length
 ------------------------------------------------------------------------- */
 
 void lammps_gather_atoms(void *ptr, char *name, 
                          int type, int count, void *data)
 {
   LAMMPS *lmp = (LAMMPS *) ptr;
 
   // error if tags are not defined or not consecutive
 
   int flag = 0;
   if (lmp->atom->tag_enable == 0 || lmp->atom->tag_consecutive() == 0) flag = 1;
   if (lmp->atom->natoms > MAXSMALLINT) flag = 1;
   if (flag && lmp->comm->me == 0) {
     lmp->error->warning(FLERR,"Library error in lammps_gather_atoms");
     return;
   }
 
   int natoms = static_cast<int> (lmp->atom->natoms);
 
   int i,j,offset;
   void *vptr = lmp->atom->extract(name);
 
   // copy = Natom length vector of per-atom values
   // use atom ID to insert each atom's values into copy
   // MPI_Allreduce with MPI_SUM to merge into data, ordered by atom ID
 
   if (type == 0) {
     int *vector = NULL;
     int **array = NULL;
     if (count == 1) vector = (int *) vptr;
     else array = (int **) vptr;
 
     int *copy;
     lmp->memory->create(copy,count*natoms,"lib/gather:copy");
     for (i = 0; i < count*natoms; i++) copy[i] = 0;
 
     tagint *tag = lmp->atom->tag;
     int nlocal = lmp->atom->nlocal;
 
     if (count == 1)
       for (i = 0; i < nlocal; i++)
         copy[tag[i]-1] = vector[i];
     else
       for (i = 0; i < nlocal; i++) {
         offset = count*(tag[i]-1);
         for (j = 0; j < count; j++)
           copy[offset++] = array[i][0];
       }
 
     MPI_Allreduce(copy,data,count*natoms,MPI_INT,MPI_SUM,lmp->world);
     lmp->memory->destroy(copy);
 
   } else {
     double *vector = NULL;
     double **array = NULL;
     if (count == 1) vector = (double *) vptr;
     else array = (double **) vptr;
 
     double *copy;
     lmp->memory->create(copy,count*natoms,"lib/gather:copy");
     for (i = 0; i < count*natoms; i++) copy[i] = 0.0;
 
     tagint *tag = lmp->atom->tag;
     int nlocal = lmp->atom->nlocal;
 
     if (count == 1) {
       for (i = 0; i < nlocal; i++)
         copy[tag[i]-1] = vector[i];
     } else {
       for (i = 0; i < nlocal; i++) {
         offset = count*(tag[i]-1);
         for (j = 0; j < count; j++)
           copy[offset++] = array[i][j];
       }
     }
 
     MPI_Allreduce(copy,data,count*natoms,MPI_DOUBLE,MPI_SUM,lmp->world);
     lmp->memory->destroy(copy);
   }
 }
 
 /* ----------------------------------------------------------------------
    scatter the named atom-based entity across all processors
    name = desired quantity, e.g. x or charge
    type = 0 for integer values, 1 for double values
    count = # of per-atom values, e.g. 1 for type or charge, 3 for x or f
    data = atom-based values in data, ordered by count, then by atom ID
      e.g. x[0][0],x[0][1],x[0][2],x[1][0],x[1][1],x[1][2],x[2][0],...
 ------------------------------------------------------------------------- */
 
 void lammps_scatter_atoms(void *ptr, char *name,
                           int type, int count, void *data)
 {
   LAMMPS *lmp = (LAMMPS *) ptr;
 
   // error if tags are not defined or not consecutive or no atom map
 
   int flag = 0;
   if (lmp->atom->tag_enable == 0 || lmp->atom->tag_consecutive() == 0) flag = 1;
   if (lmp->atom->natoms > MAXSMALLINT) flag = 1;
   if (lmp->atom->map_style == 0) flag = 1;
   if (flag && lmp->comm->me == 0) {
     lmp->error->warning(FLERR,"Library error in lammps_scatter_atoms");
     return;
   }
 
   int natoms = static_cast<int> (lmp->atom->natoms);
 
   int i,j,m,offset;
   void *vptr = lmp->atom->extract(name);
 
   // copy = Natom length vector of per-atom values
   // use atom ID to insert each atom's values into copy
   // MPI_Allreduce with MPI_SUM to merge into data, ordered by atom ID
 
   if (type == 0) {
     int *vector = NULL;
     int **array = NULL;
     if (count == 1) vector = (int *) vptr;
     else array = (int **) vptr;
     int *dptr = (int *) data;
 
     if (count == 1) {
       for (i = 0; i < natoms; i++)
         if ((m = lmp->atom->map(i+1)) >= 0)
           vector[m] = dptr[i];
     } else {
       for (i = 0; i < natoms; i++)
         if ((m = lmp->atom->map(i+1)) >= 0) {
           offset = count*i;
           for (j = 0; j < count; j++)
             array[m][j] = dptr[offset++];
         }
     }
   } else {
     double *vector = NULL;
     double **array = NULL;
     if (count == 1) vector = (double *) vptr;
     else array = (double **) vptr;
     double *dptr = (double *) data;
 
     if (count == 1) {
       for (i = 0; i < natoms; i++)
         if ((m = lmp->atom->map(i+1)) >= 0)
           vector[m] = dptr[i];
     } else {
       for (i = 0; i < natoms; i++) {
         if ((m = lmp->atom->map(i+1)) >= 0) {
           offset = count*i;
           for (j = 0; j < count; j++)
             array[m][j] = dptr[offset++];
         }
       }
     }
   }
 }
diff --git a/src/molecule.cpp b/src/molecule.cpp
index c118431f9..af2dfaf60 100644
--- a/src/molecule.cpp
+++ b/src/molecule.cpp
@@ -1,1431 +1,1433 @@
 /* ----------------------------------------------------------------------
    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.
 ------------------------------------------------------------------------- */
 
 #include "stdlib.h"
 #include "string.h"
 #include "molecule.h"
 #include "atom.h"
 #include "atom_vec.h"
 #include "force.h"
 #include "comm.h"
 #include "domain.h"
 #include "math_extra.h"
 #include "math_const.h"
 #include "memory.h"
 #include "error.h"
 
 using namespace LAMMPS_NS;
 using namespace MathConst;
 
 #define MAXLINE 256
 #define EPSILON 1.0e-7
 #define BIG 1.0e20
 
 /* ---------------------------------------------------------------------- */
 
 Molecule::Molecule(LAMMPS *lmp, char *idarg, char *file) : Pointers(lmp)
 {
   me = comm->me;
 
   int n = strlen(idarg) + 1;
   id = new char[n];
   strcpy(id,idarg);
 
   for (int i = 0; i < n-1; i++)
     if (!isalnum(id[i]) && id[i] != '_')
       error->all(FLERR,"Molecule template ID must be "
                  "alphanumeric or underscore characters");
 
   // initialize all fields to empty
 
   initialize();
 
   // scan file for sizes of all fields and allocate them
 
   if (me == 0) open(file);
   read(0);
   if (me == 0) fclose(fp);
   allocate();
 
   // read file again to populate all fields
 
   if (me == 0) open(file);
   read(1);
   if (me == 0) fclose(fp);
 }
 
 /* ---------------------------------------------------------------------- */
 
 Molecule::~Molecule()
 {
   delete [] id;
   deallocate();
 }
 
 /* ----------------------------------------------------------------------
    compute center = geometric center of molecule
    also compute:
      dx = displacement of each atom from center
      molradius = radius of molecule from center including finite-size particles
 ------------------------------------------------------------------------- */
 
 void Molecule::compute_center()
 {
   if (centerflag) return;
   centerflag = 1;
 
   center[0] = center[1] = center[2] = 0.0;
   for (int i = 0; i < natoms; i++) {
     center[0] += x[i][0];
     center[1] += x[i][1];
     center[2] += x[i][2];
   }
   center[0] /= natoms;
   center[1] /= natoms;
   center[2] /= natoms;
 
   memory->destroy(dx);
   memory->create(dx,natoms,3,"molecule:dx");
 
   for (int i = 0; i < natoms; i++) {
     dx[i][0] = x[i][0] - center[0];
     dx[i][1] = x[i][1] - center[1];
     dx[i][2] = x[i][2] - center[2];
   }
 
   molradius = 0.0;
   for (int i = 0; i < natoms; i++) {
     double rad = MathExtra::len3(dx[i]);
     if (radiusflag) rad += radius[i];
     molradius = MAX(molradius,rad);
   }
 }
 
 /* ----------------------------------------------------------------------
    compute masstotal = total mass of molecule
    could have been set by user, otherwise calculate it
 ------------------------------------------------------------------------- */
 
 void Molecule::compute_mass()
 {
   if (massflag) return;
   massflag = 1;
 
   if (!rmassflag) atom->check_mass();
 
   masstotal = 0.0;
   for (int i = 0; i < natoms; i++) {
     if (rmassflag) masstotal += rmass[i];
     else masstotal += atom->mass[type[i]];
   }
 }
 
 /* ----------------------------------------------------------------------
    compute com = center of mass of molecule
    could have been set by user, otherwise calculate it
    NOTE: account for finite size particles?
    also compute:
      dxcom = displacement of each atom from COM
      comatom = which atom (1-Natom) is nearest the COM
      maxextent = furthest any atom in molecule is from comatom (not COM)
 ------------------------------------------------------------------------- */
 
 void Molecule::compute_com()
 {
   if (!comflag) {
     comflag = 1;
 
     if (!rmassflag) atom->check_mass();
 
     double onemass;
     com[0] = com[1] = com[2] = 0.0;
     for (int i = 0; i < natoms; i++) {
       if (rmassflag) onemass = rmass[i];
       else onemass = atom->mass[type[i]];
       com[0] += x[i][0]*onemass;
       com[1] += x[i][1]*onemass;
       com[2] += x[i][2]*onemass;
     }
     com[0] /= masstotal;
     com[1] /= masstotal;
     com[2] /= masstotal;
   }
 
   memory->destroy(dxcom);
   memory->create(dxcom,natoms,3,"molecule:dxcom");
 
   for (int i = 0; i < natoms; i++) {
     dxcom[i][0] = x[i][0] - com[0];
     dxcom[i][1] = x[i][1] - com[1];
     dxcom[i][2] = x[i][2] - com[2];
   }
 
   double rsqmin = BIG;
   for (int i = 0; i < natoms; i++) {
     double rsq = MathExtra::lensq3(dxcom[i]);
     if (rsq < rsqmin) {
       comatom = i;
       rsqmin = rsq;
     }
   }
 
   double rsqmax = 0.0;
   for (int i = 0; i < natoms; i++) {
     double dx = x[comatom][0] - x[i][0];
     double dy = x[comatom][1] - x[i][1];
     double dz = x[comatom][2] - x[i][2];
     double rsq = dx*dx + dy*dy + dz*dz;
     rsqmax = MAX(rsqmax,rsq);
   }
 
   comatom++;
   maxextent = sqrt(rsqmax);
 }
 
 /* ----------------------------------------------------------------------
    compute itensor = 6 moments of inertia of molecule around xyz axes
    could have been set by user, otherwise calculate it
    NOTE: account for finite size particles?
    also compute:
      inertia = 3 principal components of inertia
      ex,ey,ez = principal axes in space coords
      quat = quaternion for orientation of molecule
      dxbody = displacement of each atom from COM in body frame
 ------------------------------------------------------------------------- */
 
 void Molecule::compute_inertia()
 {
   if (!inertiaflag) {
     inertiaflag = 1;
 
     if (!rmassflag) atom->check_mass();
 
     double onemass,dx,dy,dz;
     for (int i = 0; i < 6; i++) itensor[i] = 0.0;
     for (int i = 0; i < natoms; i++) {
       if (rmassflag) onemass = rmass[i];
       else onemass = atom->type[type[i]];
       dx = dxcom[i][0];
       dy = dxcom[i][1];
       dz = dxcom[i][2];
       itensor[0] += onemass * (dy*dy + dz*dz);
       itensor[1] += onemass * (dx*dx + dz*dz);
       itensor[2] += onemass * (dx*dx + dy*dy);
       itensor[3] -= onemass * dy*dz;
       itensor[4] -= onemass * dx*dz;
       itensor[5] -= onemass * dx*dy;
     }
   }
 
   // diagonalize inertia tensor for each body via Jacobi rotations
   // inertia = 3 eigenvalues = principal moments of inertia
   // evectors and exzy = 3 evectors = principal axes of rigid body
 
   double cross[3];
   double tensor[3][3],evectors[3][3];
 
   tensor[0][0] = itensor[0];
   tensor[1][1] = itensor[1];
   tensor[2][2] = itensor[2];
   tensor[1][2] = tensor[2][1] = itensor[3];
   tensor[0][2] = tensor[2][0] = itensor[4];
   tensor[0][1] = tensor[1][0] = itensor[5];
   
   if (MathExtra::jacobi(tensor,inertia,evectors))
     error->all(FLERR,"Insufficient Jacobi rotations for rigid molecule");
   
   ex[0] = evectors[0][0];
   ex[1] = evectors[1][0];
   ex[2] = evectors[2][0];
   ey[0] = evectors[0][1];
   ey[1] = evectors[1][1];
   ey[2] = evectors[2][1];
   ez[0] = evectors[0][2];
   ez[1] = evectors[1][2];
   ez[2] = evectors[2][2];
 
   // 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;
   
   // enforce 3 evectors as a right-handed coordinate system
   // flip 3rd vector if needed
   
   MathExtra::cross3(ex,ey,cross);
   if (MathExtra::dot3(cross,ez) < 0.0) MathExtra::negate3(ez);
   
   // create quaternion
   
   MathExtra::exyz_to_q(ex,ey,ez,quat);
 
   // compute displacements in body frame defined by quat
 
   memory->destroy(dxbody);
   memory->create(dxbody,natoms,3,"molecule:dxbody");
 
   for (int i = 0; i < natoms; i++)
     MathExtra::transpose_matvec(ex,ey,ez,dxcom[i],dxbody[i]);
 }
 
 /* ----------------------------------------------------------------------
    read molecule info from file
    flag = 0, just scan for sizes of fields
    flag = 1, read and store fields
 ------------------------------------------------------------------------- */
 
 void Molecule::read(int flag)
 {
   char line[MAXLINE],keyword[MAXLINE];
   char *eof,*ptr;
 
   // skip 1st line of file
 
   if (me == 0) {
     eof = fgets(line,MAXLINE,fp);
     if (eof == NULL) error->one(FLERR,"Unexpected end of molecule file");
   }
 
   // read header lines
   // skip blank lines or lines that start with "#"
   // stop when read an unrecognized line
 
   while (1) {
 
     readline(line);
 
     // trim anything from '#' onward
     // if line is blank, continue
 
     if ((ptr = strchr(line,'#'))) *ptr = '\0';
     if (strspn(line," \t\n\r") == strlen(line)) continue;
 
     // search line for header keywords and set corresponding variable
 
     if (strstr(line,"atoms")) sscanf(line,"%d",&natoms);
     else if (strstr(line,"bonds")) sscanf(line,"%d",&nbonds);
     else if (strstr(line,"angles")) sscanf(line,"%d",&nangles);
     else if (strstr(line,"dihedrals")) sscanf(line,"%d",&ndihedrals);
     else if (strstr(line,"impropers")) sscanf(line,"%d",&nimpropers);
 
     else if (strstr(line,"mass")) {
       massflag = 1;
       sscanf(line,"%lg",&masstotal);
     }
     else if (strstr(line,"com")) {
       comflag = 1;
       sscanf(line,"%lg %lg %lg",&com[0],&com[1],&com[2]);
       if (domain->dimension == 2 && com[2] != 0.0)
         error->all(FLERR,"Molecule file z center-of-mass must be 0.0 for 2d");
     }
     else if (strstr(line,"inertia")) {
       inertiaflag = 1;
       sscanf(line,"%lg %lg %lg %lg %lg %lg",
              &itensor[0],&itensor[1],&itensor[2],
              &itensor[3],&itensor[4],&itensor[5]);
     }
 
     else break;
   }
 
   // error check
 
   if (flag == 0) {
     if (natoms == 0) error->all(FLERR,"No atom count in molecule file");
   }
 
   // count = vector for tallying bonds,angles,etc per atom
 
   if (flag == 0) memory->create(count,natoms,"molecule:count");
   else count = NULL;
   
   // grab keyword and skip next line
 
   parse_keyword(0,line,keyword);
   readline(line);
 
   // loop over sections of molecule file
 
   while (strlen(keyword)) {
     if (strcmp(keyword,"Coords") == 0) {
       xflag = 1;
       if (flag) coords(line);
       else skip_lines(natoms,line);
     } else if (strcmp(keyword,"Types") == 0) {
       typeflag = 1;
       if (flag) types(line);
       else skip_lines(natoms,line);
     } else if (strcmp(keyword,"Charges") == 0) {
       qflag = 1;
       if (flag) charges(line);
       else skip_lines(natoms,line);
     } else if (strcmp(keyword,"Diameters") == 0) {
       radiusflag = 1;
       if (flag) diameters(line);
       else skip_lines(natoms,line);
     } else if (strcmp(keyword,"Masses") == 0) {
       rmassflag = 1;
       if (flag) masses(line);
       else skip_lines(natoms,line);
 
     } else if (strcmp(keyword,"Bonds") == 0) {
       if (nbonds == 0)
 	error->all(FLERR,"Molecule file has bonds but no nbonds setting");
       bondflag = tag_require = 1;
       bonds(flag,line);
     } else if (strcmp(keyword,"Angles") == 0) {
       if (nangles == 0) 
 	error->all(FLERR,"Molecule file has angles but no nangles setting");
       angleflag = tag_require = 1;
       angles(flag,line);
     } else if (strcmp(keyword,"Dihedrals") == 0) {
       if (ndihedrals == 0) error->all(FLERR,"Molecule file has dihedrals "
 				      "but no ndihedrals setting");
       dihedralflag = tag_require = 1;
       dihedrals(flag,line);
     } else if (strcmp(keyword,"Impropers") == 0) {
       if (nimpropers == 0) error->all(FLERR,"Molecule file has impropers "
 				      "but no nimpropers setting");
       improperflag = tag_require = 1;
       impropers(flag,line);
 
     } else if (strcmp(keyword,"Special Bond Counts") == 0) {
       nspecialflag = 1;
       nspecial_read(flag,line);
     } else if (strcmp(keyword,"Special Bonds") == 0) {
       specialflag = tag_require = 1;
       if (flag) special_read(line);
       else skip_lines(natoms,line);
 
     } else if (strcmp(keyword,"Shake Flags") == 0) {
       shakeflagflag = 1;
       if (flag) shakeflag_read(line);
       else skip_lines(natoms,line);
     } else if (strcmp(keyword,"Shake Atoms") == 0) {
       shakeatomflag = tag_require = 1;
       if (shaketypeflag) shakeflag = 1;
       if (!shakeflagflag) 
 	error->all(FLERR,"Molecule file shake flags not before shake atoms");
       if (flag) shakeatom_read(line);
       else skip_lines(natoms,line);
     } else if (strcmp(keyword,"Shake Bond Types") == 0) {
       shaketypeflag = 1;
       if (shakeatomflag) shakeflag = 1;
       if (!shakeflagflag) 
 	error->all(FLERR,"Molecule file shake flags not before shake bonds");
       if (flag) shaketype_read(line);
       else skip_lines(natoms,line);
 
     } else error->one(FLERR,"Unknown section in molecule file");
 	     
     parse_keyword(1,line,keyword);
   }
 
   // clean up
 
   memory->destroy(count);
 
   // error check
 
   if (flag == 0) {
     if ((nspecialflag && !specialflag) || (!nspecialflag && specialflag))
       error->all(FLERR,"Molecule file needs both Special Bond sections");
     if (specialflag && !bondflag) 
       error->all(FLERR,"Molecule file has special flags but no bonds");
+    if (!specialflag && bondflag) 
+      error->all(FLERR,"Molecule file has bonds but no special flags");
 
     if ((shakeflagflag || shakeatomflag || shaketypeflag) && !shakeflag)
       error->all(FLERR,"Molecule file shake info is incomplete");
   }
 }
 
 /* ----------------------------------------------------------------------
    read coords from file
 ------------------------------------------------------------------------- */
 
 void Molecule::coords(char *line)
 {
   int tmp;
   for (int i = 0; i < natoms; i++) {
     readline(line);
     sscanf(line,"%d %lg %lg %lg",&tmp,&x[i][0],&x[i][1],&x[i][2]);
   }
 
   if (domain->dimension == 2) {
     for (int i = 0; i < natoms; i++)
       if (x[i][2] != 0.0) 
         error->all(FLERR,"Molecule file z coord must be 0.0 for 2d");
   }
 }
 
 /* ----------------------------------------------------------------------
    read types from file
    set ntypes = max of any atom type
 ------------------------------------------------------------------------- */
 
 void Molecule::types(char *line)
 {
   int tmp;
   for (int i = 0; i < natoms; i++) {
     readline(line);
     sscanf(line,"%d %d",&tmp,&type[i]);
   }
 
   for (int i = 0; i < natoms; i++)
     if (type[i] <= 0)
       error->all(FLERR,"Invalid atom type in molecule file");
 
   for (int i = 0; i < natoms; i++)
     ntypes = MAX(ntypes,type[i]);
 }
 
 /* ----------------------------------------------------------------------
    read charges from file
 ------------------------------------------------------------------------- */
 
 void Molecule::charges(char *line)
 {
   int tmp;
   for (int i = 0; i < natoms; i++) {
     readline(line);
     sscanf(line,"%d %lg",&tmp,&q[i]);
   }
 }
 
 /* ----------------------------------------------------------------------
    read diameters from file and set radii
 ------------------------------------------------------------------------- */
 
 void Molecule::diameters(char *line)
 {
   int tmp;
   for (int i = 0; i < natoms; i++) {
     readline(line);
     sscanf(line,"%d %lg",&tmp,&radius[i]);
     radius[i] *= 0.5;
   }
 
   for (int i = 0; i < natoms; i++)
     if (radius[i] < 0.0) 
       error->all(FLERR,"Invalid atom diameter in molecule file");
 }
 
 /* ----------------------------------------------------------------------
    read masses from file
 ------------------------------------------------------------------------- */
 
 void Molecule::masses(char *line)
 {
   int tmp;
   for (int i = 0; i < natoms; i++) {
     readline(line);
     sscanf(line,"%d %lg",&tmp,&rmass[i]);
   }
 
   for (int i = 0; i < natoms; i++)
     if (rmass[i] <= 0.0) error->all(FLERR,"Invalid atom mass in molecule file");
 }
 
 /* ----------------------------------------------------------------------
    read bonds from file
    set nbondtypes = max type of any bond
    store each with both atoms if newton_bond = 0
    if flag = 0, just count bonds/atom
    if flag = 1, store them with atoms
 ------------------------------------------------------------------------- */
 
 void Molecule::bonds(int flag, char *line)
 {
   int tmp,itype;
   tagint m,atom1,atom2;
   int newton_bond = force->newton_bond;
 
   if (flag == 0)
     for (int i = 0; i < natoms; i++) count[i] = 0;
   else
     for (int i = 0; i < natoms; i++) num_bond[i] = 0;
 
   for (int i = 0; i < nbonds; i++) {
     readline(line);
     sscanf(line,"%d %d " TAGINT_FORMAT " " TAGINT_FORMAT,
            &tmp,&itype,&atom1,&atom2);
 
     if (atom1 <= 0 || atom1 > natoms ||
 	atom2 <= 0 || atom2 > natoms)
       error->one(FLERR,"Invalid atom ID in Bonds section of molecule file");
     if (itype <= 0)
       error->one(FLERR,"Invalid bond type in Bonds section of molecule file");
 
     if (flag) {
       m = atom1-1;
       nbondtypes = MAX(nbondtypes,itype);
       bond_type[m][num_bond[m]] = itype;
       bond_atom[m][num_bond[m]] = atom2;
       num_bond[m]++;
       if (newton_bond == 0) {
 	m = atom2-1;
 	bond_type[m][num_bond[m]] = itype;
 	bond_atom[m][num_bond[m]] = atom1;
 	num_bond[m]++;
       }
     } else {
       count[atom1-1]++;
       if (newton_bond == 0) count[atom2-1]++;
     }
   }
 
   // bond_per_atom = max of count vector
 
   if (flag == 0) {
     bond_per_atom = 0;
     for (int i = 0; i < natoms; i++)
       bond_per_atom = MAX(bond_per_atom,count[i]);
   }
 }
 
 /* ----------------------------------------------------------------------
    read angles from file
    store each with all 3 atoms if newton_bond = 0
    if flag = 0, just count angles/atom
    if flag = 1, store them with atoms
 ------------------------------------------------------------------------- */
 
 void Molecule::angles(int flag, char *line)
 {
   int tmp,itype;
   tagint m,atom1,atom2,atom3;
   int newton_bond = force->newton_bond;
 
   if (flag == 0)
     for (int i = 0; i < natoms; i++) count[i] = 0;
   else
     for (int i = 0; i < natoms; i++) num_angle[i] = 0;
 
   for (int i = 0; i < nangles; i++) {
     readline(line);
     sscanf(line,"%d %d " TAGINT_FORMAT " " TAGINT_FORMAT " " TAGINT_FORMAT,
            &tmp,&itype,&atom1,&atom2,&atom3);
 
     if (atom1 <= 0 || atom1 > natoms ||
         atom2 <= 0 || atom2 > natoms ||
         atom3 <= 0 || atom3 > natoms)
       error->one(FLERR,"Invalid atom ID in Angles section of molecule file");
     if (itype <= 0)
       error->one(FLERR,"Invalid angle type in Angles section of molecule file");
 
     if (flag) {
       m = atom2-1;
       nangletypes = MAX(nangletypes,itype);
       angle_type[m][num_angle[m]] = itype;
       angle_atom1[m][num_angle[m]] = atom1;
       angle_atom2[m][num_angle[m]] = atom2;
       angle_atom3[m][num_angle[m]] = atom3;
       num_angle[m]++;
       if (newton_bond == 0) {
 	m = atom1-1;
 	angle_type[m][num_angle[m]] = itype;
 	angle_atom1[m][num_angle[m]] = atom1;
 	angle_atom2[m][num_angle[m]] = atom2;
 	angle_atom3[m][num_angle[m]] = atom3;
 	num_angle[m]++;
 	m = atom3-1;
 	angle_type[m][num_angle[m]] = itype;
 	angle_atom1[m][num_angle[m]] = atom1;
 	angle_atom2[m][num_angle[m]] = atom2;
 	angle_atom3[m][num_angle[m]] = atom3;
 	num_angle[m]++;
       }
     } else {
       count[atom2-1]++;
       if (newton_bond == 0) {
 	count[atom1-1]++;
 	count[atom3-1]++;
       }
     }
   }
 
   // angle_per_atom = max of count vector
 
   if (flag == 0) {
     angle_per_atom = 0;
     for (int i = 0; i < natoms; i++)
       angle_per_atom = MAX(angle_per_atom,count[i]);
   }
 }
 
 /* ----------------------------------------------------------------------
    read dihedrals from file
    store each with all 4 atoms if newton_bond = 0
    if flag = 0, just count dihedrals/atom
    if flag = 1, store them with atoms
 ------------------------------------------------------------------------- */
 
 void Molecule::dihedrals(int flag, char *line)
 {
   int tmp,itype;
   tagint m,atom1,atom2,atom3,atom4;
   int newton_bond = force->newton_bond;
 
   if (flag == 0)
     for (int i = 0; i < natoms; i++) count[i] = 0;
   else
     for (int i = 0; i < natoms; i++) num_dihedral[i] = 0;
 
   for (int i = 0; i < ndihedrals; i++) {
     readline(line);
     sscanf(line,"%d %d " TAGINT_FORMAT " " TAGINT_FORMAT " " 
            TAGINT_FORMAT " " TAGINT_FORMAT " ",
            &tmp,&itype,&atom1,&atom2,&atom3,&atom4);
 
     if (atom1 <= 0 || atom1 > natoms ||
         atom2 <= 0 || atom2 > natoms ||
         atom3 <= 0 || atom3 > natoms ||
         atom4 <= 0 || atom4 > natoms)
       error->one(FLERR,
 		 "Invalid atom ID in dihedrals section of molecule file");
     if (itype <= 0)
       error->one(FLERR,
 		 "Invalid dihedral type in dihedrals section of molecule file");
 
     if (flag) {
       m = atom2-1;
       ndihedraltypes = MAX(ndihedraltypes,itype);
       dihedral_type[m][num_dihedral[m]] = itype;
       dihedral_atom1[m][num_dihedral[m]] = atom1;
       dihedral_atom2[m][num_dihedral[m]] = atom2;
       dihedral_atom3[m][num_dihedral[m]] = atom3;
       dihedral_atom4[m][num_dihedral[m]] = atom4;
       num_dihedral[m]++;
       if (newton_bond == 0) {
 	m = atom1-1;
 	dihedral_type[m][num_dihedral[m]] = itype;
 	dihedral_atom1[m][num_dihedral[m]] = atom1;
 	dihedral_atom2[m][num_dihedral[m]] = atom2;
 	dihedral_atom3[m][num_dihedral[m]] = atom3;
 	dihedral_atom4[m][num_dihedral[m]] = atom4;
 	num_dihedral[m]++;
 	m = atom3-1;
 	dihedral_type[m][num_dihedral[m]] = itype;
 	dihedral_atom1[m][num_dihedral[m]] = atom1;
 	dihedral_atom2[m][num_dihedral[m]] = atom2;
 	dihedral_atom3[m][num_dihedral[m]] = atom3;
 	dihedral_atom4[m][num_dihedral[m]] = atom4;
 	num_dihedral[m]++;
 	m = atom4-1;
 	dihedral_type[m][num_dihedral[m]] = itype;
 	dihedral_atom1[m][num_dihedral[m]] = atom1;
 	dihedral_atom2[m][num_dihedral[m]] = atom2;
 	dihedral_atom3[m][num_dihedral[m]] = atom3;
 	dihedral_atom4[m][num_dihedral[m]] = atom4;
 	num_dihedral[m]++;
       }
     } else {
       count[atom2-1]++;
       if (newton_bond == 0) {
 	count[atom1-1]++;
 	count[atom3-1]++;
 	count[atom4-1]++;
       }
     }
   }
 
   // dihedral_per_atom = max of count vector
 
   if (flag == 0) {
     dihedral_per_atom = 0;
     for (int i = 0; i < natoms; i++)
       dihedral_per_atom = MAX(dihedral_per_atom,count[i]);
   }
 }
 
 /* ----------------------------------------------------------------------
    read impropers from file
    store each with all 4 atoms if newton_bond = 0
    if flag = 0, just count impropers/atom
    if flag = 1, store them with atoms
 ------------------------------------------------------------------------- */
 
 void Molecule::impropers(int flag, char *line)
 {
   int tmp,itype;
   tagint m,atom1,atom2,atom3,atom4;
   int newton_bond = force->newton_bond;
 
   if (flag == 0)
     for (int i = 0; i < natoms; i++) count[i] = 0;
   else
     for (int i = 0; i < natoms; i++) num_improper[i] = 0;
 
   for (int i = 0; i < nimpropers; i++) {
     readline(line);
     sscanf(line,"%d %d " TAGINT_FORMAT " " TAGINT_FORMAT " " 
            TAGINT_FORMAT " " TAGINT_FORMAT " ",
            &tmp,&itype,&atom1,&atom2,&atom3,&atom4);
 
     if (atom1 <= 0 || atom1 > natoms ||
         atom2 <= 0 || atom2 > natoms ||
         atom3 <= 0 || atom3 > natoms ||
         atom4 <= 0 || atom4 > natoms)
       error->one(FLERR,
 		 "Invalid atom ID in impropers section of molecule file");
     if (itype <= 0)
       error->one(FLERR,
 		 "Invalid improper type in impropers section of molecule file");
 
     if (flag) {
       m = atom2-1;
       nimpropertypes = MAX(nimpropertypes,itype);
       improper_type[m][num_improper[m]] = itype;
       improper_atom1[m][num_improper[m]] = atom1;
       improper_atom2[m][num_improper[m]] = atom2;
       improper_atom3[m][num_improper[m]] = atom3;
       improper_atom4[m][num_improper[m]] = atom4;
       num_improper[m]++;
       if (newton_bond == 0) {
 	m = atom1-1;
 	improper_type[m][num_improper[m]] = itype;
 	improper_atom1[m][num_improper[m]] = atom1;
 	improper_atom2[m][num_improper[m]] = atom2;
 	improper_atom3[m][num_improper[m]] = atom3;
 	improper_atom4[m][num_improper[m]] = atom4;
 	num_improper[m]++;
 	m = atom3-1;
 	improper_type[m][num_improper[m]] = itype;
 	improper_atom1[m][num_improper[m]] = atom1;
 	improper_atom2[m][num_improper[m]] = atom2;
 	improper_atom3[m][num_improper[m]] = atom3;
 	improper_atom4[m][num_improper[m]] = atom4;
 	num_improper[m]++;
 	m = atom4-1;
 	improper_type[m][num_improper[m]] = itype;
 	improper_atom1[m][num_improper[m]] = atom1;
 	improper_atom2[m][num_improper[m]] = atom2;
 	improper_atom3[m][num_improper[m]] = atom3;
 	improper_atom4[m][num_improper[m]] = atom4;
 	num_improper[m]++;
       }
     } else {
       count[atom2-1]++;
       if (newton_bond == 0) {
 	count[atom1-1]++;
 	count[atom3-1]++;
 	count[atom4-1]++;
       }
     }
   }
 
   // improper_per_atom = max of count vector
 
   if (flag == 0) {
     improper_per_atom = 0;
     for (int i = 0; i < natoms; i++)
       improper_per_atom = MAX(improper_per_atom,count[i]);
   }
 }
 
 /* ----------------------------------------------------------------------
    read 3 special bonds counts from file
    if flag = 0, just tally maxspecial
    if flag = 1, store them with atoms
 ------------------------------------------------------------------------- */
 
 void Molecule::nspecial_read(int flag, char *line)
 {
   int tmp,c1,c2,c3;
 
   if (flag == 0) maxspecial = 0;
 
   for (int i = 0; i < natoms; i++) {
     readline(line);
     sscanf(line,"%d %d %d %d",&tmp,&c1,&c2,&c3);
 
     if (flag) {
       nspecial[i][0] = c1;
       nspecial[i][1] = c1+c2;
       nspecial[i][2] = c1+c2+c3;
     } else maxspecial = MAX(maxspecial,c1+c2+c3);
   }
 }
 
 /* ----------------------------------------------------------------------
    read special bond indices from file
 ------------------------------------------------------------------------- */
 
 void Molecule::special_read(char *line)
 {
   int m,nwords;
   char **words = new char*[maxspecial+1];
 
   for (int i = 0; i < natoms; i++) {
     readline(line);
     nwords = parse(line,words,maxspecial+1);
     if (nwords != nspecial[i][2]+1)
       error->all(FLERR,"Molecule file special list "
 		 "does not match special count");
 
     for (m = 1; m < nwords; m++) {
       special[i][m-1] = ATOTAGINT(words[m]);
       if (special[i][m-1] <= 0 || special[i][m-1] > natoms ||
 	  special[i][m-1] == i+1)
 	error->all(FLERR,"Invalid special atom index in molecule file");
     }
   }
 
   delete [] words;
 }
 
 /* ----------------------------------------------------------------------
    read SHAKE flags from file
 ------------------------------------------------------------------------- */
 
 void Molecule::shakeflag_read(char *line)
 {
   int tmp;
   for (int i = 0; i < natoms; i++) {
     readline(line);
     sscanf(line,"%d %d",&tmp,&shake_flag[i]);
   }
 
   for (int i = 0; i < natoms; i++)
     if (shake_flag[i] < 0 || shake_flag[i] > 4) 
       error->all(FLERR,"Invalid shake flag in molecule file");
 }
 
 /* ----------------------------------------------------------------------
    read SHAKE atom info from file
 ------------------------------------------------------------------------- */
 
 void Molecule::shakeatom_read(char *line)
 {
   int tmp;
   for (int i = 0; i < natoms; i++) {
     readline(line);
     if (shake_flag[i] == 1)
       sscanf(line,"%d " TAGINT_FORMAT " " TAGINT_FORMAT " " TAGINT_FORMAT,
              &tmp,&shake_atom[i][0],&shake_atom[i][1],&shake_atom[i][2]);
     else if (shake_flag[i] == 2)
       sscanf(line,"%d " TAGINT_FORMAT " " TAGINT_FORMAT,
              &tmp,&shake_atom[i][0],&shake_atom[i][1]);
     else if (shake_flag[i] == 3)
       sscanf(line,"%d " TAGINT_FORMAT " " TAGINT_FORMAT " " TAGINT_FORMAT,
              &tmp,&shake_atom[i][0],&shake_atom[i][1],&shake_atom[i][2]);
     else if (shake_flag[i] == 4)
       sscanf(line,"%d " TAGINT_FORMAT " " TAGINT_FORMAT " " 
              TAGINT_FORMAT " " TAGINT_FORMAT,
              &tmp,&shake_atom[i][0],&shake_atom[i][1],
              &shake_atom[i][2],&shake_atom[i][3]);
   }
 
   for (int i = 0; i < natoms; i++) {
     int m = shake_flag[i];
     if (m == 1) m = 3;
     for (int j = 0; j < m; j++)
       if (shake_atom[i][j] <= 0 || shake_atom[i][j] > natoms)
         error->all(FLERR,"Invalid shake atom in molecule file");
   }
 }
 
 /* ----------------------------------------------------------------------
    read SHAKE bond type info from file
 ------------------------------------------------------------------------- */
 
 void Molecule::shaketype_read(char *line)
 {
   int tmp;
   for (int i = 0; i < natoms; i++) {
     readline(line);
     if (shake_flag[i] == 1)
       sscanf(line,"%d %d %d %d",&tmp,
              &shake_type[i][0],&shake_type[i][1],&shake_type[i][2]);
     else if (shake_flag[i] == 2)
       sscanf(line,"%d %d",&tmp,&shake_type[i][0]);
     else if (shake_flag[i] == 3)
       sscanf(line,"%d %d %d",&tmp,&shake_type[i][0],&shake_type[i][1]);
     else if (shake_flag[i] == 4)
       sscanf(line,"%d %d %d %d",&tmp,
              &shake_type[i][0],&shake_type[i][1],&shake_type[i][2]);
   }
 
   for (int i = 0; i < natoms; i++) {
     int m = shake_flag[i];
     if (m == 1) m = 3;
     for (int j = 0; j < m-1; j++)
       if (shake_type[i][j] <= 0)
         error->all(FLERR,"Invalid shake bond type in molecule file");
     if (shake_flag[i] == 1)
       if (shake_type[i][2] <= 0)
         error->all(FLERR,"Invalid shake angle type in molecule file");
   }
 }
 
 /* ----------------------------------------------------------------------
    error check molecule attributes and topology against system settings
    flag = 0, just check this molecule
    flag = 1, check all molecules in set, this is 1st molecule in set
 ------------------------------------------------------------------------- */
 
 void Molecule::check_attributes(int flag)
 {
   int n = 1;
   if (flag) n = nset;
   int imol = atom->find_molecule(id);
 
   for (int i = imol; i < imol+n; i++) {
     Molecule *onemol = atom->molecules[imol];
     
     // check per-atom attributes of molecule
     // warn if not a match
 
     int mismatch = 0;
     if (onemol->qflag && !atom->q_flag) mismatch = 1;
     if (onemol->radiusflag && !atom->radius_flag) mismatch = 1;
     if (onemol->rmassflag && !atom->rmass_flag) mismatch = 1;
 
     if (mismatch && me == 0) 
       error->warning(FLERR,
                      "Molecule attributes do not match system attributes");
 
     // for all atom styles, check nbondtype,etc
 
     mismatch = 0;
     if (atom->nbondtypes < onemol->nbondtypes) mismatch = 1;
     if (atom->nangletypes < onemol->nangletypes) mismatch = 1;
     if (atom->ndihedraltypes < onemol->ndihedraltypes) mismatch = 1;
     if (atom->nimpropertypes < onemol->nimpropertypes) mismatch = 1;
 
     if (mismatch) 
       error->all(FLERR,"Molecule topology type exceeds system topology type");
 
     // for molecular atom styles, check bond_per_atom,etc + maxspecial
     // do not check for atom style template, since nothing stored per atom
 
     if (atom->molecular == 1) {
       if (atom->avec->bonds_allow &&
           atom->bond_per_atom < onemol->bond_per_atom) mismatch = 1;
       if (atom->avec->angles_allow &&
           atom->angle_per_atom < onemol->angle_per_atom) mismatch = 1;
       if (atom->avec->dihedrals_allow &&
           atom->dihedral_per_atom < onemol->dihedral_per_atom) mismatch = 1;
       if (atom->avec->impropers_allow &&
           atom->improper_per_atom < onemol->improper_per_atom) mismatch = 1;
       if (atom->maxspecial < onemol->maxspecial) mismatch = 1;
 
       if (mismatch) 
         error->all(FLERR,"Molecule toplogy/atom exceeds system topology/atom");
 
     }
 
     // warn if molecule topology defined but no special settings
 
     if (onemol->bondflag && !onemol->specialflag) 
       if (me == 0) error->warning(FLERR,"Molecule has bond topology "
                                   "but no special bond settings");
   }
 }
 
 /* ----------------------------------------------------------------------
    init all data structures to empty
 ------------------------------------------------------------------------- */
 
 void Molecule::initialize()
 {
   natoms = 0;
   nbonds = nangles = ndihedrals = nimpropers = 0;
   ntypes = 0;
   nbondtypes = nangletypes = ndihedraltypes = nimpropertypes = 0;
 
   bond_per_atom = angle_per_atom = dihedral_per_atom = improper_per_atom = 0;
   maxspecial = 0;
 
   xflag = typeflag = qflag = radiusflag = rmassflag = 0;
   bondflag = angleflag = dihedralflag = improperflag = 0;
   nspecialflag = specialflag = 0;
   shakeflag = shakeflagflag = shakeatomflag = shaketypeflag = 0;
 
   centerflag = massflag = comflag = inertiaflag = 0;
   tag_require = 0;
 
   x = NULL;
   type = NULL;
   q = NULL;
   radius = NULL;
   rmass = NULL;
 
   num_bond = NULL;
   bond_type = NULL;
   bond_atom = NULL;
 
   num_angle = NULL;
   angle_type = NULL;
   angle_atom1 = angle_atom2 = angle_atom3 = NULL;
 
   num_dihedral = NULL;
   dihedral_type = NULL;
   dihedral_atom1 = dihedral_atom2 = dihedral_atom3 = dihedral_atom4 = NULL;
 
   num_improper = NULL;
   improper_type = NULL;
   improper_atom1 = improper_atom2 = improper_atom3 = improper_atom4 = NULL;
 
   nspecial = NULL;
   special = NULL;
 
   shake_flag = NULL;
   shake_atom = NULL;
   shake_type = NULL;
 
   dx = NULL;
   dxcom = NULL;
   dxbody = NULL;
 }
 
 /* ----------------------------------------------------------------------
    allocate all data structures
    also initialize values for data structures that are always allocated
 ------------------------------------------------------------------------- */
 
 void Molecule::allocate()
 {
   if (xflag) memory->create(x,natoms,3,"molecule:x");
   if (typeflag) memory->create(type,natoms,"molecule:type");
   if (qflag) memory->create(q,natoms,"molecule:q");
   if (radiusflag) memory->create(radius,natoms,"molecule:radius");
   if (rmassflag) memory->create(rmass,natoms,"molecule:rmass");
 
   // always allocate num_bond,num_angle,etc and nspecial even if not in file
   // initialize to 0 even if not in molecule file
   // this is so methods that use these arrays don't have to check they exist
 
   memory->create(num_bond,natoms,"molecule:num_bond");
   for (int i = 0; i < natoms; i++) num_bond[i] = 0;
   memory->create(num_angle,natoms,"molecule:num_angle");
   for (int i = 0; i < natoms; i++) num_angle[i] = 0;
   memory->create(num_dihedral,natoms,"molecule:num_dihedral");
   for (int i = 0; i < natoms; i++) num_dihedral[i] = 0;
   memory->create(num_improper,natoms,"molecule:num_improper");
   for (int i = 0; i < natoms; i++) num_improper[i] = 0;
   memory->create(nspecial,natoms,3,"molecule:nspecial");
   for (int i = 0; i < natoms; i++) 
     nspecial[i][0] = nspecial[i][1] = nspecial[i][2] = 0;
 
   if (bondflag) {
     memory->create(bond_type,natoms,bond_per_atom,
 		   "molecule:bond_type");
     memory->create(bond_atom,natoms,bond_per_atom,
 		   "molecule:bond_atom");
   }
 
   if (angleflag) {
     memory->create(angle_type,natoms,angle_per_atom,
 		   "molecule:angle_type");
     memory->create(angle_atom1,natoms,angle_per_atom,
 		   "molecule:angle_atom1");
     memory->create(angle_atom2,natoms,angle_per_atom,
 		   "molecule:angle_atom2");
     memory->create(angle_atom3,natoms,angle_per_atom,
 		   "molecule:angle_atom3");
   }
 
   if (dihedralflag) {
     memory->create(dihedral_type,natoms,dihedral_per_atom,
 		   "molecule:dihedral_type");
     memory->create(dihedral_atom1,natoms,dihedral_per_atom,
 		   "molecule:dihedral_atom1");
     memory->create(dihedral_atom2,natoms,dihedral_per_atom,
 		   "molecule:dihedral_atom2");
     memory->create(dihedral_atom3,natoms,dihedral_per_atom,
 		   "molecule:dihedral_atom3");
     memory->create(dihedral_atom4,natoms,dihedral_per_atom,
 		   "molecule:dihedral_atom4");
   }
 
   if (improperflag) {
     memory->create(improper_type,natoms,improper_per_atom,
 		   "molecule:improper_type");
     memory->create(improper_atom1,natoms,improper_per_atom,
 		   "molecule:improper_atom1");
     memory->create(improper_atom2,natoms,improper_per_atom,
 		   "molecule:improper_atom2");
     memory->create(improper_atom3,natoms,improper_per_atom,
 		   "molecule:improper_atom3");
     memory->create(improper_atom4,natoms,improper_per_atom,
 		   "molecule:improper_atom4");
   }
 
   if (specialflag)
     memory->create(special,natoms,maxspecial,"molecule:special");
 
   if (shakeflag) {
     memory->create(shake_flag,natoms,"molecule:shake_flag");
     memory->create(shake_atom,natoms,4,"molecule:shake_flag");
     memory->create(shake_type,natoms,3,"molecule:shake_flag");
   }
 }
 
 /* ----------------------------------------------------------------------
    deallocate all data structures
 ------------------------------------------------------------------------- */
 
 void Molecule::deallocate()
 {
   memory->destroy(x);
   memory->destroy(type);
   memory->destroy(q);
   memory->destroy(radius);
   memory->destroy(rmass);
   
   memory->destroy(num_bond);
   memory->destroy(bond_type);
   memory->destroy(bond_atom);
   
   memory->destroy(num_angle);
   memory->destroy(angle_type);
   memory->destroy(angle_atom1);
   memory->destroy(angle_atom2);
   memory->destroy(angle_atom3);
   
   memory->destroy(num_dihedral);
   memory->destroy(dihedral_type);
   memory->destroy(dihedral_atom1);
   memory->destroy(dihedral_atom2);
   memory->destroy(dihedral_atom3);
   memory->destroy(dihedral_atom4);
   
   memory->destroy(num_improper);
   memory->destroy(improper_type);
   memory->destroy(improper_atom1);
   memory->destroy(improper_atom2);
   memory->destroy(improper_atom3);
   memory->destroy(improper_atom4);
 
   memory->destroy(nspecial);
   memory->destroy(special);
 
   memory->destroy(shake_flag);
   memory->destroy(shake_atom);
   memory->destroy(shake_type);
 
   memory->destroy(dx);
   memory->destroy(dxcom);
   memory->destroy(dxbody);
 }
 
 /* ----------------------------------------------------------------------
    open molecule file
 ------------------------------------------------------------------------- */
 
 void Molecule::open(char *file)
 {
   fp = fopen(file,"r");
   if (fp == NULL) {
     char str[128];
     sprintf(str,"Cannot open molecule file %s",file);
     error->one(FLERR,str);
   }
 }
 
 /* ----------------------------------------------------------------------
    read and bcast a line
 ------------------------------------------------------------------------- */
 
 void Molecule::readline(char *line)
 {
   int n;
   if (me == 0) {
     if (fgets(line,MAXLINE,fp) == NULL) n = 0;
     else n = strlen(line) + 1;
   }
   MPI_Bcast(&n,1,MPI_INT,0,world);
   if (n == 0) error->all(FLERR,"Unexpected end of molecule file");
   MPI_Bcast(line,n,MPI_CHAR,0,world);
 }
 
 /* ----------------------------------------------------------------------
    extract keyword from line
    flag = 0, read and bcast line
    flag = 1, line has already been read
 ------------------------------------------------------------------------- */
 
 void Molecule::parse_keyword(int flag, char *line, char *keyword)
 {
   if (flag) {
 
     // read upto non-blank line plus 1 following line
     // eof is set to 1 if any read hits end-of-file
 
     int eof = 0;
     if (me == 0) {
       if (fgets(line,MAXLINE,fp) == NULL) eof = 1;
       while (eof == 0 && strspn(line," \t\n\r") == strlen(line)) {
 	if (fgets(line,MAXLINE,fp) == NULL) eof = 1;
       }
       if (fgets(keyword,MAXLINE,fp) == NULL) eof = 1;
     }
 
     // if eof, set keyword empty and return
 
     MPI_Bcast(&eof,1,MPI_INT,0,world);
     if (eof) {
       keyword[0] = '\0';
       return;
     }
 
     // bcast keyword line to all procs
 
     int n;
     if (me == 0) n = strlen(line) + 1;
     MPI_Bcast(&n,1,MPI_INT,0,world);
     MPI_Bcast(line,n,MPI_CHAR,0,world);
   }
 
   // copy non-whitespace portion of line into keyword
 
   int start = strspn(line," \t\n\r");
   int stop = strlen(line) - 1;
   while (line[stop] == ' ' || line[stop] == '\t'
          || line[stop] == '\n' || line[stop] == '\r') stop--;
   line[stop+1] = '\0';
   strcpy(keyword,&line[start]);
 }
 
 /* ----------------------------------------------------------------------
    skip N lines of file
 ------------------------------------------------------------------------- */
 
 void Molecule::skip_lines(int n, char *line)
 {
   for (int i = 0; i < n; i++) readline(line);
 }
 
 /* ----------------------------------------------------------------------
    parse line into words separated by whitespace
    return # of words
    max = max pointers storable in words
 ------------------------------------------------------------------------- */
 
 int Molecule::parse(char *line, char **words, int max)
 {
   char *ptr;
 
   int nwords = 0;
   words[nwords++] = strtok(line," \t\n\r\f");
 
   while ((ptr = strtok(NULL," \t\n\r\f"))) {
     if (nwords < max) words[nwords] = ptr;
     nwords++;
   }
 
   return nwords;
 }
 
 /* ----------------------------------------------------------------------
    proc 0 prints molecule params
 ------------------------------------------------------------------------- */
 
 /*
 
 void Molecule::print()
 {
   printf("MOLECULE %s\n",id);
   printf("  %d natoms\n",natoms);
   if (nbonds) printf("  %d nbonds\n",nbonds);
   if (nangles) printf("  %d nangles\n",nangles);
   if (ndihedrals) printf("  %d ndihedrals\n",ndihedrals);
   if (nimpropers) printf("  %d nimpropers\n",nimpropers);
 
   if (xflag) {
     printf(  "Coords:\n");
     for (int i = 0; i < natoms; i++)
       printf("    %d %g %g %g\n",i+1,x[i][0],x[i][1],x[i][2]);
   }
   if (typeflag) {
     printf(  "Types:\n");
     for (int i = 0; i < natoms; i++)
       printf("    %d %d\n",i+1,type[i]);
   }
   if (qflag) {
     printf(  "Charges:\n");
     for (int i = 0; i < natoms; i++)
       printf("    %d %g\n",i+1,q[i]);
   }
   if (radiusflag) {
     printf(  "Radii:\n");
     for (int i = 0; i < natoms; i++)
       printf("    %d %g\n",i+1,radius[i]);
   }
   if (rmassflag) {
     printf(  "Masses:\n");
     for (int i = 0; i < natoms; i++)
       printf("    %d %g\n",i+1,rmass[i]);
   }
       
   if (bondflag) {
     printf(  "Bonds:\n");
     for (int i = 0; i < natoms; i++) {
       printf("    %d %d\n",i+1,num_bond[i]);
       for (int j = 0; j < num_bond[i]; j++)
         printf("      %d %d %d %d\n",j+1,bond_type[i][j],i+1,bond_atom[i][j]);
     }
   }
   if (angleflag) {
     printf(  "Angles:\n");
     for (int i = 0; i < natoms; i++) {
       printf("    %d %d\n",i+1,num_angle[i]);
       for (int j = 0; j < num_angle[i]; j++)
         printf("      %d %d %d %d %d\n",
                j+1,angle_type[i][j],
                angle_atom1[i][j],angle_atom2[i][j],angle_atom3[i][j]);
     }
   }
   if (dihedralflag) {
     printf(  "Dihedrals:\n");
     for (int i = 0; i < natoms; i++) {
       printf("    %d %d\n",i+1,num_dihedral[i]);
       for (int j = 0; j < num_dihedral[i]; j++)
         printf("      %d %d %d %d %d %d\n",
                j+1,dihedral_type[i][j],
                dihedral_atom1[i][j],dihedral_atom2[i][j],
                dihedral_atom3[i][j],dihedral_atom4[i][j]);
     }
   }
   if (improperflag) {
     printf(  "Impropers:\n");
     for (int i = 0; i < natoms; i++) {
       printf("    %d %d\n",i+1,num_improper[i]);
       for (int j = 0; j < num_improper[i]; j++)
         printf("      %d %d %d %d %d %d\n",
                j+1,improper_type[i][j],
                improper_atom1[i][j],improper_atom2[i][j],
                improper_atom3[i][j],improper_atom4[i][j]);
     }
   }
 
   if (specialflag) {
     printf(  "Special neighs:\n");
     for (int i = 0; i < natoms; i++) {
       printf("    %d %d %d %d\n",i+1,
              nspecial[i][0],nspecial[i][1]-nspecial[i][0],
              nspecial[i][2]-nspecial[i][1]);
       printf("      ");
       for (int j = 0; j < nspecial[i][2]; j++)
         printf(" %d",special[i][j]);
       printf("\n");
     }
   }
 }
 
 */
diff --git a/src/update.cpp b/src/update.cpp
index aac3e7382..a2017db06 100644
--- a/src/update.cpp
+++ b/src/update.cpp
@@ -1,466 +1,478 @@
 /* ----------------------------------------------------------------------
    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.
 ------------------------------------------------------------------------- */
 
 #include "string.h"
 #include "stdlib.h"
 #include "update.h"
 #include "integrate.h"
 #include "min.h"
 #include "style_integrate.h"
 #include "style_minimize.h"
 #include "neighbor.h"
+#include "neigh_list.h"
 #include "force.h"
 #include "modify.h"
 #include "fix.h"
 #include "domain.h"
 #include "region.h"
 #include "compute.h"
 #include "output.h"
 #include "memory.h"
 #include "error.h"
 
 using namespace LAMMPS_NS;
 
 /* ---------------------------------------------------------------------- */
 
 Update::Update(LAMMPS *lmp) : Pointers(lmp)
 {
   char *str;
 
   ntimestep = 0;
   atime = 0.0;
   atimestep = 0;
   first_update = 0;
 
   whichflag = 0;
   firststep = laststep = 0;
   beginstep = endstep = 0;
   setupflag = 0;
   multireplica = 0;
 
   restrict_output = 0;
 
   eflag_global = vflag_global = -1;
 
   unit_style = NULL;
   set_units("lj");
 
   integrate_style = NULL;
   integrate = NULL;
   minimize_style = NULL;
   minimize = NULL;
 
   str = (char *) "verlet";
   create_integrate(1,&str,lmp->suffix);
 
   str = (char *) "cg";
   create_minimize(1,&str);
 }
 
 /* ---------------------------------------------------------------------- */
 
 Update::~Update()
 {
   delete [] unit_style;
 
   delete [] integrate_style;
   delete integrate;
 
   delete [] minimize_style;
   delete minimize;
 }
 
 /* ---------------------------------------------------------------------- */
 
 void Update::init()
 {
   // if USER-CUDA mode is enabled:
   // integrate/minimize style must be CUDA variant
 
   if (whichflag == 1 && lmp->cuda)
     if (strstr(integrate_style,"cuda") == NULL)
       error->all(FLERR,"USER-CUDA mode requires CUDA variant of run style");
   if (whichflag == 2 && lmp->cuda)
     if (strstr(minimize_style,"cuda") == NULL)
       error->all(FLERR,"USER-CUDA mode requires CUDA variant of min style");
 
   // init the appropriate integrate and/or minimize class
   // if neither (e.g. from write_restart) then just return
 
   if (whichflag == 0) return;
   if (whichflag == 1) integrate->init();
   else if (whichflag == 2) minimize->init();
 
   // only set first_update if a run or minimize is being performed
 
   first_update = 1;
 }
 
 /* ---------------------------------------------------------------------- */
 
 void Update::set_units(const char *style)
 {
   // physical constants from:
   // http://physics.nist.gov/cuu/Constants/Table/allascii.txt
   // using thermochemical calorie = 4.184 J
 
   if (strcmp(style,"lj") == 0) {
     force->boltz = 1.0;
     force->hplanck = 0.18292026;  // using LJ parameters for argon
     force->mvv2e = 1.0;
     force->ftm2v = 1.0;
     force->mv2d = 1.0;
     force->nktv2p = 1.0;
     force->qqr2e = 1.0;
     force->qe2f = 1.0;
     force->vxmu2f = 1.0;
     force->xxt2kmu = 1.0;
     force->e_mass = 0.0;    // not yet set
     force->hhmrr2e = 0.0;
     force->mvh2r = 0.0;
     force->angstrom = 1.0;
     force->femtosecond = 1.0;
     force->qelectron = 1.0;
 
     dt = 0.005;
     neighbor->skin = 0.3;
 
   } else if (strcmp(style,"real") == 0) {
     force->boltz = 0.0019872067;
     force->hplanck = 95.306976368;
     force->mvv2e = 48.88821291 * 48.88821291;
     force->ftm2v = 1.0 / 48.88821291 / 48.88821291;
     force->mv2d = 1.0 / 0.602214179;
     force->nktv2p = 68568.415;
     force->qqr2e = 332.06371;
     force->qe2f = 23.060549;
     force->vxmu2f = 1.4393264316e4;
     force->xxt2kmu = 0.1;
     force->e_mass = 1.0/1836.1527556560675;
     force->hhmrr2e = 0.0957018663603261;
     force->mvh2r = 1.5339009481951;
     force->angstrom = 1.0;
     force->femtosecond = 1.0;
     force->qelectron = 1.0;
 
     dt = 1.0;
     neighbor->skin = 2.0;
 
   } else if (strcmp(style,"metal") == 0) {
     force->boltz = 8.617343e-5;
     force->hplanck = 4.135667403e-3;
     force->mvv2e = 1.0364269e-4;
     force->ftm2v = 1.0 / 1.0364269e-4;
     force->mv2d = 1.0 / 0.602214179;
     force->nktv2p = 1.6021765e6;
     force->qqr2e = 14.399645;
     force->qe2f = 1.0;
     force->vxmu2f = 0.6241509647;
     force->xxt2kmu = 1.0e-4;
     force->e_mass = 0.0;    // not yet set
     force->hhmrr2e = 0.0;
     force->mvh2r = 0.0;
     force->angstrom = 1.0;
     force->femtosecond = 1.0e-3;
     force->qelectron = 1.0;
 
     dt = 0.001;
     neighbor->skin = 2.0;
 
   } else if (strcmp(style,"si") == 0) {
     force->boltz = 1.3806504e-23;
     force->hplanck = 6.62606896e-34;
     force->mvv2e = 1.0;
     force->ftm2v = 1.0;
     force->mv2d = 1.0;
     force->nktv2p = 1.0;
     force->qqr2e = 8.9876e9;
     force->qe2f = 1.0;
     force->vxmu2f = 1.0;
     force->xxt2kmu = 1.0;
     force->e_mass = 0.0;    // not yet set
     force->hhmrr2e = 0.0;
     force->mvh2r = 0.0;
     force->angstrom = 1.0e-10;
     force->femtosecond = 1.0e-15;
     force->qelectron = 1.6021765e-19;
 
     dt = 1.0e-8;
     neighbor->skin = 0.001;
 
   } else if (strcmp(style,"cgs") == 0) {
     force->boltz = 1.3806504e-16;
     force->hplanck = 6.62606896e-27;
     force->mvv2e = 1.0;
     force->ftm2v = 1.0;
     force->mv2d = 1.0;
     force->nktv2p = 1.0;
     force->qqr2e = 1.0;
     force->qe2f = 1.0;
     force->vxmu2f = 1.0;
     force->xxt2kmu = 1.0;
     force->e_mass = 0.0;    // not yet set
     force->hhmrr2e = 0.0;
     force->mvh2r = 0.0;
     force->angstrom = 1.0e-8;
     force->femtosecond = 1.0e-15;
     force->qelectron = 4.8032044e-10;
 
     dt = 1.0e-8;
     neighbor->skin = 0.1;
 
   } else if (strcmp(style,"electron") == 0) {
     force->boltz = 3.16681534e-6;
     force->hplanck = 0.1519829846;
     force->mvv2e = 1.06657236;
     force->ftm2v = 0.937582899;
     force->mv2d = 1.0;
     force->nktv2p = 2.94210108e13;
     force->qqr2e = 1.0;
     force->qe2f = 1.94469051e-10;
     force->vxmu2f = 3.39893149e1;
     force->xxt2kmu = 3.13796367e-2;
     force->e_mass = 0.0;    // not yet set
     force->hhmrr2e = 0.0;
     force->mvh2r = 0.0;
     force->angstrom = 1.88972612;
     force->femtosecond = 0.0241888428;
     force->qelectron = 1.0;
 
     dt = 0.001;
     neighbor->skin = 2.0;
 
   } else if (strcmp(style,"micro") == 0) {
     force->boltz = 1.3806504e-8;
     force->hplanck = 6.62606896e-13;
     force->mvv2e = 1.0;
     force->ftm2v = 1.0;
     force->mv2d = 1.0;
     force->nktv2p = 1.0;
     force->qqr2e = 8.987556e6;
     force->qe2f = 1.0;
     force->vxmu2f = 1.0;
     force->xxt2kmu = 1.0;
     force->e_mass = 0.0;    // not yet set
     force->hhmrr2e = 0.0;
     force->mvh2r = 0.0;
     force->angstrom = 1.0e-4;
     force->femtosecond = 1.0e-9;
     force->qelectron = 1.6021765e-7;
 
     dt = 2.0;
     neighbor->skin = 0.1;
                                               
   } else if (strcmp(style,"nano") == 0) {  
     force->boltz = 0.013806504;
     force->hplanck = 6.62606896e-4;
     force->mvv2e = 1.0;
     force->ftm2v = 1.0;
     force->mv2d = 1.0;
     force->nktv2p = 1.0;
     force->qqr2e = 230.7078669;
     force->qe2f = 1.0;
     force->vxmu2f = 1.0;
     force->xxt2kmu = 1.0;
     force->e_mass = 0.0;    // not yet set
     force->hhmrr2e = 0.0;
     force->mvh2r = 0.0;
     force->angstrom = 1.0e-1;
     force->femtosecond = 1.0e-6;
     force->qelectron = 1.0;
        
     dt = 0.00045;
     neighbor->skin = 0.1;
 
   } else error->all(FLERR,"Illegal units command");
 
   delete [] unit_style;
   int n = strlen(style) + 1;
   unit_style = new char[n];
   strcpy(unit_style,style);
 }
 
 /* ---------------------------------------------------------------------- */
 
 void Update::create_integrate(int narg, char **arg, char *suffix)
 {
   if (narg < 1) error->all(FLERR,"Illegal run_style command");
 
   delete [] integrate_style;
   delete integrate;
 
   int sflag;
   new_integrate(arg[0],narg-1,&arg[1],suffix,sflag);
 
   if (sflag) {
     char estyle[256];
     sprintf(estyle,"%s/%s",arg[0],suffix);
     int n = strlen(estyle) + 1;
     integrate_style = new char[n];
     strcpy(integrate_style,estyle);
   } else {
     int n = strlen(arg[0]) + 1;
     integrate_style = new char[n];
     strcpy(integrate_style,arg[0]);
   }
 }
 
 /* ----------------------------------------------------------------------
    create the Integrate style, first with suffix appended
 ------------------------------------------------------------------------- */
 
 void Update::new_integrate(char *style, int narg, char **arg,
                            char *suffix, int &sflag)
 {
   int success = 0;
 
   if (suffix && lmp->suffix_enable) {
     sflag = 1;
     char estyle[256];
     sprintf(estyle,"%s/%s",style,suffix);
     success = 1;
 
     if (0) return;
 
 #define INTEGRATE_CLASS
 #define IntegrateStyle(key,Class) \
     else if (strcmp(estyle,#key) == 0) integrate = new Class(lmp,narg,arg);
 #include "style_integrate.h"
 #undef IntegrateStyle
 #undef INTEGRATE_CLASS
 
     else success = 0;
   }
 
   if (!success) {
     sflag = 0;
 
     if (0) return;
 
 #define INTEGRATE_CLASS
 #define IntegrateStyle(key,Class) \
     else if (strcmp(style,#key) == 0) integrate = new Class(lmp,narg,arg);
 #include "style_integrate.h"
 #undef IntegrateStyle
 #undef INTEGRATE_CLASS
 
     else error->all(FLERR,"Illegal integrate style");
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 void Update::create_minimize(int narg, char **arg)
 {
   if (narg != 1) error->all(FLERR,"Illegal min_style command");
 
   delete [] minimize_style;
   delete minimize;
 
   if (0) return;      // dummy line to enable else-if macro expansion
 
 #define MINIMIZE_CLASS
 #define MinimizeStyle(key,Class) \
   else if (strcmp(arg[0],#key) == 0) minimize = new Class(lmp);
 #include "style_minimize.h"
 #undef MINIMIZE_CLASS
 
   else error->all(FLERR,"Illegal min_style command");
 
   int n = strlen(arg[0]) + 1;
   minimize_style = new char[n];
   strcpy(minimize_style,arg[0]);
 }
 
 /* ----------------------------------------------------------------------
    reset timestep as called from input script
 ------------------------------------------------------------------------- */
 
 void Update::reset_timestep(int narg, char **arg)
 {
   if (narg != 1) error->all(FLERR,"Illegal reset_timestep command");
   bigint newstep = ATOBIGINT(arg[0]);
   reset_timestep(newstep);
 }
 
 /* ----------------------------------------------------------------------
    reset timestep
-   set atimestep to new timestep, so future update_time() calls will be correct
-   trigger reset of timestep for output and for fixes that require it
-   do not allow any timestep-dependent fixes to be defined
-   reset eflag/vflag global so nothing will think eng/virial are current
-   reset invoked flags of computes,
-     so nothing will think they are current between runs
-   clear timestep list of computes that store future invocation times
    called from rerun command and input script (indirectly)
 ------------------------------------------------------------------------- */
 
 void Update::reset_timestep(bigint newstep)
 {
   ntimestep = newstep;
   if (ntimestep < 0) error->all(FLERR,"Timestep must be >= 0");
   if (ntimestep > MAXBIGINT) error->all(FLERR,"Too big a timestep");
 
+  // set atimestep to new timestep
+  // so future update_time() calls will be correct
+
   atimestep = ntimestep;
 
+  // trigger reset of timestep for output and for fixes that require it
+  // do not allow any timestep-dependent fixes to be defined
+
   output->reset_timestep(ntimestep);
 
   for (int i = 0; i < modify->nfix; i++) {
     if (modify->fix[i]->time_depend)
       error->all(FLERR,
                  "Cannot reset timestep with a time-dependent fix defined");
     modify->fix[i]->reset_timestep(ntimestep);
   }
 
+  // reset eflag/vflag global so no commands will think eng/virial are current
+
   eflag_global = vflag_global = -1;
 
+  // reset invoked flags of computes,
+  // so no commands will think they are current between runs
+
   for (int i = 0; i < modify->ncompute; i++) {
     modify->compute[i]->invoked_scalar = -1;
     modify->compute[i]->invoked_vector = -1;
     modify->compute[i]->invoked_array = -1;
     modify->compute[i]->invoked_peratom = -1;
     modify->compute[i]->invoked_local = -1;
   }
 
+  // clear timestep list of computes that store future invocation times
+
   for (int i = 0; i < modify->ncompute; i++)
     if (modify->compute[i]->timeflag) modify->compute[i]->clearstep();
 
+  // set last_build of all neigh lists to -1 to force rebuild
+
+  for (int i = 0; i < neighbor->nlist; i++)
+    neighbor->lists[i]->last_build = -1;
+
   // NOTE: 7Jun12, adding rerun command, don't think this is required
 
   //for (int i = 0; i < domain->nregion; i++)
   //  if (domain->regions[i]->dynamic_check())
   //    error->all(FLERR,"Cannot reset timestep with a dynamic region defined");
 }
 
 /* ----------------------------------------------------------------------
    update elapsed simulation time
    called at end of runs or when timestep size changes
 ------------------------------------------------------------------------- */
 
 void Update::update_time()
 {
   atime += (ntimestep-atimestep) * dt;
   atimestep = ntimestep;
 }
 
 /* ----------------------------------------------------------------------
    memory usage of update and integrate/minimize
 ------------------------------------------------------------------------- */
 
 bigint Update::memory_usage()
 {
   bigint bytes = 0;
   if (whichflag == 1) bytes += integrate->memory_usage();
   else if (whichflag == 2) bytes += minimize->memory_usage();
   return bytes;
 }
diff --git a/src/version.h b/src/version.h
index 7e1adf092..25d90d961 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1 +1 @@
-#define LAMMPS_VERSION "11 Jul 2014"
+#define LAMMPS_VERSION "22 Jul 2014"