diff --git a/src/domain.cpp b/src/domain.cpp
index 0590b956d..2c3f61401 100644
--- a/src/domain.cpp
+++ b/src/domain.cpp
@@ -1,1930 +1,1933 @@
 /* ----------------------------------------------------------------------
    LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
    http://lammps.sandia.gov, Sandia National Laboratories
    Steve Plimpton, sjplimp@sandia.gov
 
    Copyright (2003) Sandia Corporation.  Under the terms of Contract
    DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
    certain rights in this software.  This software is distributed under
    the GNU General Public License.
 
    See the README file in the top-level LAMMPS directory.
 ------------------------------------------------------------------------- */
 
 /* ----------------------------------------------------------------------
    Contributing author (triclinic) : Pieter in 't Veld (SNL)
 ------------------------------------------------------------------------- */
 
 #include <mpi.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #include <math.h>
 #include "domain.h"
 #include "style_region.h"
 #include "atom.h"
 #include "atom_vec.h"
 #include "molecule.h"
 #include "force.h"
 #include "kspace.h"
 #include "update.h"
 #include "modify.h"
 #include "fix.h"
 #include "fix_deform.h"
 #include "region.h"
 #include "lattice.h"
 #include "comm.h"
 #include "output.h"
 #include "thermo.h"
 #include "universe.h"
 #include "math_const.h"
 #include "memory.h"
 #include "error.h"
 
 using namespace LAMMPS_NS;
 using namespace MathConst;
 
 enum{NO_REMAP,X_REMAP,V_REMAP};    // same as fix_deform.cpp
 enum{IGNORE,WARN,ERROR};           // same as thermo.cpp
 enum{LAYOUT_UNIFORM,LAYOUT_NONUNIFORM,LAYOUT_TILED};    // several files
 
 #define BIG   1.0e20
 #define SMALL 1.0e-4
 #define DELTAREGION 4
 #define BONDSTRETCH 1.1
 
 /* ----------------------------------------------------------------------
    default is periodic
 ------------------------------------------------------------------------- */
 
 Domain::Domain(LAMMPS *lmp) : Pointers(lmp)
 {
   box_exist = 0;
 
   dimension = 3;
   nonperiodic = 0;
   xperiodic = yperiodic = zperiodic = 1;
   periodicity[0] = xperiodic;
   periodicity[1] = yperiodic;
   periodicity[2] = zperiodic;
 
   boundary[0][0] = boundary[0][1] = 0;
   boundary[1][0] = boundary[1][1] = 0;
   boundary[2][0] = boundary[2][1] = 0;
 
   minxlo = minxhi = 0.0;
   minylo = minyhi = 0.0;
   minzlo = minzhi = 0.0;
 
   triclinic = 0;
   tiltsmall = 1;
 
   boxlo[0] = boxlo[1] = boxlo[2] = -0.5;
   boxhi[0] = boxhi[1] = boxhi[2] = 0.5;
   xy = xz = yz = 0.0;
 
   h[3] = h[4] = h[5] = 0.0;
   h_inv[3] = h_inv[4] = h_inv[5] = 0.0;
   h_rate[0] = h_rate[1] = h_rate[2] =
     h_rate[3] = h_rate[4] = h_rate[5] = 0.0;
   h_ratelo[0] = h_ratelo[1] = h_ratelo[2] = 0.0;
 
   prd_lamda[0] = prd_lamda[1] = prd_lamda[2] = 1.0;
   prd_half_lamda[0] = prd_half_lamda[1] = prd_half_lamda[2] = 0.5;
   boxlo_lamda[0] = boxlo_lamda[1] = boxlo_lamda[2] = 0.0;
   boxhi_lamda[0] = boxhi_lamda[1] = boxhi_lamda[2] = 1.0;
 
   lattice = NULL;
   char **args = new char*[2];
   args[0] = (char *) "none";
   args[1] = (char *) "1.0";
   set_lattice(2,args);
   delete [] args;
 
   nregion = maxregion = 0;
   regions = NULL;
 
   copymode = 0;
+
+  region_map = new RegionCreatorMap();
+
+#define REGION_CLASS
+#define RegionStyle(key,Class) \
+  (*region_map)[#key] = &region_creator<Class>;
+#include "style_region.h"
+#undef RegionStyle
+#undef REGION_CLASS
 }
 
 /* ---------------------------------------------------------------------- */
 
 Domain::~Domain()
 {
   if (copymode) return;
 
   delete lattice;
   for (int i = 0; i < nregion; i++) delete regions[i];
   memory->sfree(regions);
+
+  delete region_map;
 }
 
 /* ---------------------------------------------------------------------- */
 
 void Domain::init()
 {
   // set box_change flags if box size/shape/sub-domains ever change
   // due to shrink-wrapping or fixes that change box size/shape/sub-domains
 
   box_change_size = box_change_shape = box_change_domain = 0;
 
   if (nonperiodic == 2) box_change_size = 1;
   for (int i = 0; i < modify->nfix; i++) {
     if (modify->fix[i]->box_change_size) box_change_size = 1;
     if (modify->fix[i]->box_change_shape) box_change_shape = 1;
     if (modify->fix[i]->box_change_domain) box_change_domain = 1;
   }
 
   box_change = 0;
   if (box_change_size || box_change_shape || box_change_domain) box_change = 1;
 
   // check for fix deform
 
   deform_flag = deform_vremap = deform_groupbit = 0;
   for (int i = 0; i < modify->nfix; i++)
     if (strcmp(modify->fix[i]->style,"deform") == 0) {
       deform_flag = 1;
       if (((FixDeform *) modify->fix[i])->remapflag == V_REMAP) {
         deform_vremap = 1;
         deform_groupbit = modify->fix[i]->groupbit;
       }
     }
 
   // region inits
 
   for (int i = 0; i < nregion; i++) regions[i]->init();
 }
 
 /* ----------------------------------------------------------------------
    set initial global box
    assumes boxlo/hi and triclinic tilts are already set
    expandflag = 1 if need to expand box in shrink-wrapped dims
    not invoked by read_restart since box is already expanded
    if don't prevent further expansion, restarted triclinic box
      with unchanged tilt factors can become a box with atoms outside the box
 ------------------------------------------------------------------------- */
 
 void Domain::set_initial_box(int expandflag)
 {
   // error checks for orthogonal and triclinic domains
 
   if (boxlo[0] >= boxhi[0] || boxlo[1] >= boxhi[1] || boxlo[2] >= boxhi[2])
     error->one(FLERR,"Box bounds are invalid or missing");
 
   if (domain->dimension == 2 && (xz != 0.0 || yz != 0.0))
     error->all(FLERR,"Cannot skew triclinic box in z for 2d simulation");
 
   // error check or warning on triclinic tilt factors
 
   if (triclinic) {
     if ((fabs(xy/(boxhi[0]-boxlo[0])) > 0.5 && xperiodic) ||
         (fabs(xz/(boxhi[0]-boxlo[0])) > 0.5 && xperiodic) ||
         (fabs(yz/(boxhi[1]-boxlo[1])) > 0.5 && yperiodic)) {
       if (tiltsmall)
         error->all(FLERR,"Triclinic box skew is too large");
       else if (comm->me == 0)
         error->warning(FLERR,"Triclinic box skew is large");
     }
   }
 
   // set small based on box size and SMALL
   // this works for any unit system
 
   small[0] = SMALL * (boxhi[0] - boxlo[0]);
   small[1] = SMALL * (boxhi[1] - boxlo[1]);
   small[2] = SMALL * (boxhi[2] - boxlo[2]);
 
   // if expandflag, adjust box lo/hi for shrink-wrapped dims
 
   if (!expandflag) return;
 
   if (boundary[0][0] == 2) boxlo[0] -= small[0];
   else if (boundary[0][0] == 3) minxlo = boxlo[0];
   if (boundary[0][1] == 2) boxhi[0] += small[0];
   else if (boundary[0][1] == 3) minxhi = boxhi[0];
 
   if (boundary[1][0] == 2) boxlo[1] -= small[1];
   else if (boundary[1][0] == 3) minylo = boxlo[1];
   if (boundary[1][1] == 2) boxhi[1] += small[1];
   else if (boundary[1][1] == 3) minyhi = boxhi[1];
 
   if (boundary[2][0] == 2) boxlo[2] -= small[2];
   else if (boundary[2][0] == 3) minzlo = boxlo[2];
   if (boundary[2][1] == 2) boxhi[2] += small[2];
   else if (boundary[2][1] == 3) minzhi = boxhi[2];
 }
 
 /* ----------------------------------------------------------------------
    set global box params
    assumes boxlo/hi and triclinic tilts are already set
 ------------------------------------------------------------------------- */
 
 void Domain::set_global_box()
 {
   prd[0] = xprd = boxhi[0] - boxlo[0];
   prd[1] = yprd = boxhi[1] - boxlo[1];
   prd[2] = zprd = boxhi[2] - boxlo[2];
 
   h[0] = xprd;
   h[1] = yprd;
   h[2] = zprd;
   h_inv[0] = 1.0/h[0];
   h_inv[1] = 1.0/h[1];
   h_inv[2] = 1.0/h[2];
 
   prd_half[0] = xprd_half = 0.5*xprd;
   prd_half[1] = yprd_half = 0.5*yprd;
   prd_half[2] = zprd_half = 0.5*zprd;
 
   if (triclinic) {
     h[3] = yz;
     h[4] = xz;
     h[5] = xy;
     h_inv[3] = -h[3] / (h[1]*h[2]);
     h_inv[4] = (h[3]*h[5] - h[1]*h[4]) / (h[0]*h[1]*h[2]);
     h_inv[5] = -h[5] / (h[0]*h[1]);
 
     boxlo_bound[0] = MIN(boxlo[0],boxlo[0]+xy);
     boxlo_bound[0] = MIN(boxlo_bound[0],boxlo_bound[0]+xz);
     boxlo_bound[1] = MIN(boxlo[1],boxlo[1]+yz);
     boxlo_bound[2] = boxlo[2];
 
     boxhi_bound[0] = MAX(boxhi[0],boxhi[0]+xy);
     boxhi_bound[0] = MAX(boxhi_bound[0],boxhi_bound[0]+xz);
     boxhi_bound[1] = MAX(boxhi[1],boxhi[1]+yz);
     boxhi_bound[2] = boxhi[2];
   }
 }
 
 /* ----------------------------------------------------------------------
    set lamda box params
    assumes global box is defined and proc assignment has been made
    uses comm->xyz_split or comm->mysplit
      to define subbox boundaries in consistent manner
 ------------------------------------------------------------------------- */
 
 void Domain::set_lamda_box()
 {
   if (comm->layout != LAYOUT_TILED) {
     int *myloc = comm->myloc;
     double *xsplit = comm->xsplit;
     double *ysplit = comm->ysplit;
     double *zsplit = comm->zsplit;
 
     sublo_lamda[0] = xsplit[myloc[0]];
     subhi_lamda[0] = xsplit[myloc[0]+1];
     sublo_lamda[1] = ysplit[myloc[1]];
     subhi_lamda[1] = ysplit[myloc[1]+1];
     sublo_lamda[2] = zsplit[myloc[2]];
     subhi_lamda[2] = zsplit[myloc[2]+1];
 
   } else {
     double (*mysplit)[2] = comm->mysplit;
 
     sublo_lamda[0] = mysplit[0][0];
     subhi_lamda[0] = mysplit[0][1];
     sublo_lamda[1] = mysplit[1][0];
     subhi_lamda[1] = mysplit[1][1];
     sublo_lamda[2] = mysplit[2][0];
     subhi_lamda[2] = mysplit[2][1];
   }
 }
 
 /* ----------------------------------------------------------------------
    set local subbox params for orthogonal boxes
    assumes global box is defined and proc assignment has been made
    uses comm->xyz_split or comm->mysplit
      to define subbox boundaries in consistent manner
    insure subhi[max] = boxhi
 ------------------------------------------------------------------------- */
 
 void Domain::set_local_box()
 {
   if (triclinic) return;
 
   if (comm->layout != LAYOUT_TILED) {
     int *myloc = comm->myloc;
     int *procgrid = comm->procgrid;
     double *xsplit = comm->xsplit;
     double *ysplit = comm->ysplit;
     double *zsplit = comm->zsplit;
 
     sublo[0] = boxlo[0] + xprd*xsplit[myloc[0]];
     if (myloc[0] < procgrid[0]-1) subhi[0] = boxlo[0] + xprd*xsplit[myloc[0]+1];
     else subhi[0] = boxhi[0];
 
     sublo[1] = boxlo[1] + yprd*ysplit[myloc[1]];
     if (myloc[1] < procgrid[1]-1) subhi[1] = boxlo[1] + yprd*ysplit[myloc[1]+1];
     else subhi[1] = boxhi[1];
 
     sublo[2] = boxlo[2] + zprd*zsplit[myloc[2]];
     if (myloc[2] < procgrid[2]-1) subhi[2] = boxlo[2] + zprd*zsplit[myloc[2]+1];
     else subhi[2] = boxhi[2];
 
   } else {
     double (*mysplit)[2] = comm->mysplit;
 
     sublo[0] = boxlo[0] + xprd*mysplit[0][0];
     if (mysplit[0][1] < 1.0) subhi[0] = boxlo[0] + xprd*mysplit[0][1];
     else subhi[0] = boxhi[0];
 
     sublo[1] = boxlo[1] + yprd*mysplit[1][0];
     if (mysplit[1][1] < 1.0) subhi[1] = boxlo[1] + yprd*mysplit[1][1];
     else subhi[1] = boxhi[1];
 
     sublo[2] = boxlo[2] + zprd*mysplit[2][0];
     if (mysplit[2][1] < 1.0) subhi[2] = boxlo[2] + zprd*mysplit[2][1];
     else subhi[2] = boxhi[2];
   }
 }
 
 /* ----------------------------------------------------------------------
    reset global & local boxes due to global box boundary changes
    if shrink-wrapped, determine atom extent and reset boxlo/hi
    for triclinic, atoms must be in lamda coords (0-1) before reset_box is called
 ------------------------------------------------------------------------- */
 
 void Domain::reset_box()
 {
   // perform shrink-wrapping
   // compute extent of atoms on this proc
   // for triclinic, this is done in lamda space
 
   if (nonperiodic == 2) {
     double extent[3][2],all[3][2];
 
     extent[2][0] = extent[1][0] = extent[0][0] = BIG;
     extent[2][1] = extent[1][1] = extent[0][1] = -BIG;
 
     double **x = atom->x;
     int nlocal = atom->nlocal;
 
     for (int i = 0; i < nlocal; i++) {
       extent[0][0] = MIN(extent[0][0],x[i][0]);
       extent[0][1] = MAX(extent[0][1],x[i][0]);
       extent[1][0] = MIN(extent[1][0],x[i][1]);
       extent[1][1] = MAX(extent[1][1],x[i][1]);
       extent[2][0] = MIN(extent[2][0],x[i][2]);
       extent[2][1] = MAX(extent[2][1],x[i][2]);
     }
 
     // compute extent across all procs
     // flip sign of MIN to do it in one Allreduce MAX
 
     extent[0][0] = -extent[0][0];
     extent[1][0] = -extent[1][0];
     extent[2][0] = -extent[2][0];
 
     MPI_Allreduce(extent,all,6,MPI_DOUBLE,MPI_MAX,world);
 
     // for triclinic, convert back to box coords before changing box
 
     if (triclinic) lamda2x(atom->nlocal);
 
     // in shrink-wrapped dims, set box by atom extent
     // if minimum set, enforce min box size settings
     // for triclinic, convert lamda extent to box coords, then set box lo/hi
     // decided NOT to do the next comment - don't want to sneakily change tilt
     // for triclinic, adjust tilt factors if 2nd dim is shrink-wrapped,
     //   so that displacement in 1st dim stays the same
 
     if (triclinic == 0) {
       if (xperiodic == 0) {
         if (boundary[0][0] == 2) boxlo[0] = -all[0][0] - small[0];
         else if (boundary[0][0] == 3)
           boxlo[0] = MIN(-all[0][0]-small[0],minxlo);
         if (boundary[0][1] == 2) boxhi[0] = all[0][1] + small[0];
         else if (boundary[0][1] == 3) boxhi[0] = MAX(all[0][1]+small[0],minxhi);
         if (boxlo[0] > boxhi[0]) error->all(FLERR,"Illegal simulation box");
       }
       if (yperiodic == 0) {
         if (boundary[1][0] == 2) boxlo[1] = -all[1][0] - small[1];
         else if (boundary[1][0] == 3)
           boxlo[1] = MIN(-all[1][0]-small[1],minylo);
         if (boundary[1][1] == 2) boxhi[1] = all[1][1] + small[1];
         else if (boundary[1][1] == 3) boxhi[1] = MAX(all[1][1]+small[1],minyhi);
         if (boxlo[1] > boxhi[1]) error->all(FLERR,"Illegal simulation box");
       }
       if (zperiodic == 0) {
         if (boundary[2][0] == 2) boxlo[2] = -all[2][0] - small[2];
         else if (boundary[2][0] == 3)
           boxlo[2] = MIN(-all[2][0]-small[2],minzlo);
         if (boundary[2][1] == 2) boxhi[2] = all[2][1] + small[2];
         else if (boundary[2][1] == 3) boxhi[2] = MAX(all[2][1]+small[2],minzhi);
         if (boxlo[2] > boxhi[2]) error->all(FLERR,"Illegal simulation box");
       }
 
     } else {
       double lo[3],hi[3];
       if (xperiodic == 0) {
         lo[0] = -all[0][0]; lo[1] = 0.0; lo[2] = 0.0;
         lamda2x(lo,lo);
         hi[0] = all[0][1]; hi[1] = 0.0; hi[2] = 0.0;
         lamda2x(hi,hi);
         if (boundary[0][0] == 2) boxlo[0] = lo[0] - small[0];
         else if (boundary[0][0] == 3) boxlo[0] = MIN(lo[0]-small[0],minxlo);
         if (boundary[0][1] == 2) boxhi[0] = hi[0] + small[0];
         else if (boundary[0][1] == 3) boxhi[0] = MAX(hi[0]+small[0],minxhi);
         if (boxlo[0] > boxhi[0]) error->all(FLERR,"Illegal simulation box");
       }
       if (yperiodic == 0) {
         lo[0] = 0.0; lo[1] = -all[1][0]; lo[2] = 0.0;
         lamda2x(lo,lo);
         hi[0] = 0.0; hi[1] = all[1][1]; hi[2] = 0.0;
         lamda2x(hi,hi);
         if (boundary[1][0] == 2) boxlo[1] = lo[1] - small[1];
         else if (boundary[1][0] == 3) boxlo[1] = MIN(lo[1]-small[1],minylo);
         if (boundary[1][1] == 2) boxhi[1] = hi[1] + small[1];
         else if (boundary[1][1] == 3) boxhi[1] = MAX(hi[1]+small[1],minyhi);
         if (boxlo[1] > boxhi[1]) error->all(FLERR,"Illegal simulation box");
         //xy *= (boxhi[1]-boxlo[1]) / yprd;
       }
       if (zperiodic == 0) {
         lo[0] = 0.0; lo[1] = 0.0; lo[2] = -all[2][0];
         lamda2x(lo,lo);
         hi[0] = 0.0; hi[1] = 0.0; hi[2] = all[2][1];
         lamda2x(hi,hi);
         if (boundary[2][0] == 2) boxlo[2] = lo[2] - small[2];
         else if (boundary[2][0] == 3) boxlo[2] = MIN(lo[2]-small[2],minzlo);
         if (boundary[2][1] == 2) boxhi[2] = hi[2] + small[2];
         else if (boundary[2][1] == 3) boxhi[2] = MAX(hi[2]+small[2],minzhi);
         if (boxlo[2] > boxhi[2]) error->all(FLERR,"Illegal simulation box");
         //xz *= (boxhi[2]-boxlo[2]) / xprd;
         //yz *= (boxhi[2]-boxlo[2]) / yprd;
       }
     }
   }
 
   // reset box whether shrink-wrapping or not
 
   set_global_box();
   set_local_box();
 
   // if shrink-wrapped & kspace is defined (i.e. using MSM), call setup()
   // also call init() (to test for compatibility) ?
 
   if (nonperiodic == 2 && force->kspace) {
     //force->kspace->init();
     force->kspace->setup();
   }
 
   // if shrink-wrapped & triclinic, re-convert to lamda coords for new box
   // re-invoke pbc() b/c x2lamda result can be outside [0,1] due to roundoff
 
   if (nonperiodic == 2 && triclinic) {
     x2lamda(atom->nlocal);
     pbc();
   }
 }
 
 /* ----------------------------------------------------------------------
    enforce PBC and modify box image flags for each atom
    called every reneighboring and by other commands that change atoms
    resulting coord must satisfy lo <= coord < hi
    MAX is important since coord - prd < lo can happen when coord = hi
    if fix deform, remap velocity of fix group atoms by box edge velocities
    for triclinic, atoms must be in lamda coords (0-1) before pbc is called
    image = 10 or 20 bits for each dimension depending on sizeof(imageint)
    increment/decrement in wrap-around fashion
 ------------------------------------------------------------------------- */
 
 void Domain::pbc()
 {
   int i;
   imageint idim,otherdims;
   double *lo,*hi,*period;
   int nlocal = atom->nlocal;
   double **x = atom->x;
   double **v = atom->v;
   int *mask = atom->mask;
   imageint *image = atom->image;
 
   // verify owned atoms have valid numerical coords
   // may not if computed pairwise force between 2 atoms at same location
 
   double *coord;
   int n3 = 3*nlocal;
   coord = &x[0][0];  // note: x is always initialzed to at least one element.
   int flag = 0;
   for (i = 0; i < n3; i++)
     if (!ISFINITE(*coord++)) flag = 1;
   if (flag) error->one(FLERR,"Non-numeric atom coords - simulation unstable");
 
   // setup for PBC checks
 
   if (triclinic == 0) {
     lo = boxlo;
     hi = boxhi;
     period = prd;
   } else {
     lo = boxlo_lamda;
     hi = boxhi_lamda;
     period = prd_lamda;
   }
 
   // apply PBC to each owned atom
 
   for (i = 0; i < nlocal; i++) {
     if (xperiodic) {
       if (x[i][0] < lo[0]) {
         x[i][0] += period[0];
         if (deform_vremap && mask[i] & deform_groupbit) v[i][0] += h_rate[0];
         idim = image[i] & IMGMASK;
         otherdims = image[i] ^ idim;
         idim--;
         idim &= IMGMASK;
         image[i] = otherdims | idim;
       }
       if (x[i][0] >= hi[0]) {
         x[i][0] -= period[0];
         x[i][0] = MAX(x[i][0],lo[0]);
         if (deform_vremap && mask[i] & deform_groupbit) v[i][0] -= h_rate[0];
         idim = image[i] & IMGMASK;
         otherdims = image[i] ^ idim;
         idim++;
         idim &= IMGMASK;
         image[i] = otherdims | idim;
       }
     }
 
     if (yperiodic) {
       if (x[i][1] < lo[1]) {
         x[i][1] += period[1];
         if (deform_vremap && mask[i] & deform_groupbit) {
           v[i][0] += h_rate[5];
           v[i][1] += h_rate[1];
         }
         idim = (image[i] >> IMGBITS) & IMGMASK;
         otherdims = image[i] ^ (idim << IMGBITS);
         idim--;
         idim &= IMGMASK;
         image[i] = otherdims | (idim << IMGBITS);
       }
       if (x[i][1] >= hi[1]) {
         x[i][1] -= period[1];
         x[i][1] = MAX(x[i][1],lo[1]);
         if (deform_vremap && mask[i] & deform_groupbit) {
           v[i][0] -= h_rate[5];
           v[i][1] -= h_rate[1];
         }
         idim = (image[i] >> IMGBITS) & IMGMASK;
         otherdims = image[i] ^ (idim << IMGBITS);
         idim++;
         idim &= IMGMASK;
         image[i] = otherdims | (idim << IMGBITS);
       }
     }
 
     if (zperiodic) {
       if (x[i][2] < lo[2]) {
         x[i][2] += period[2];
         if (deform_vremap && mask[i] & deform_groupbit) {
           v[i][0] += h_rate[4];
           v[i][1] += h_rate[3];
           v[i][2] += h_rate[2];
         }
         idim = image[i] >> IMG2BITS;
         otherdims = image[i] ^ (idim << IMG2BITS);
         idim--;
         idim &= IMGMASK;
         image[i] = otherdims | (idim << IMG2BITS);
       }
       if (x[i][2] >= hi[2]) {
         x[i][2] -= period[2];
         x[i][2] = MAX(x[i][2],lo[2]);
         if (deform_vremap && mask[i] & deform_groupbit) {
           v[i][0] -= h_rate[4];
           v[i][1] -= h_rate[3];
           v[i][2] -= h_rate[2];
         }
         idim = image[i] >> IMG2BITS;
         otherdims = image[i] ^ (idim << IMG2BITS);
         idim++;
         idim &= IMGMASK;
         image[i] = otherdims | (idim << IMG2BITS);
       }
     }
   }
 }
 
 /* ----------------------------------------------------------------------
    check that point is inside box boundaries, in [lo,hi) sense
    return 1 if true, 0 if false
 ------------------------------------------------------------------------- */
 
 int Domain::inside(double* x)
 {
   double *lo,*hi;
   double lamda[3];
 
   if (triclinic == 0) {
     lo = boxlo;
     hi = boxhi;
 
     if (x[0] < lo[0] || x[0] >= hi[0] ||
 	x[1] < lo[1] || x[1] >= hi[1] ||
 	x[2] < lo[2] || x[2] >= hi[2]) return 0;
     else return 1;
     
   } else {
     lo = boxlo_lamda;
     hi = boxhi_lamda;
 
     x2lamda(x,lamda);
 
     if (lamda[0] < lo[0] || lamda[0] >= hi[0] ||
 	lamda[1] < lo[1] || lamda[1] >= hi[1] ||
 	lamda[2] < lo[2] || lamda[2] >= hi[2]) return 0;
     else return 1;
     
   }
 
 }
 
 /* ----------------------------------------------------------------------
    check that point is inside nonperiodic boundaries, in [lo,hi) sense
    return 1 if true, 0 if false
 ------------------------------------------------------------------------- */
 
 int Domain::inside_nonperiodic(double* x)
 {
   double *lo,*hi;
   double lamda[3];
 
   if (xperiodic && yperiodic && zperiodic) return 1;
 
   if (triclinic == 0) {
     lo = boxlo;
     hi = boxhi;
 
     if (!xperiodic && (x[0] < lo[0] || x[0] >= hi[0])) return 0;
     if (!yperiodic && (x[1] < lo[1] || x[1] >= hi[1])) return 0;
     if (!zperiodic && (x[2] < lo[2] || x[2] >= hi[2])) return 0;
     return 1;
 
   } else {
     lo = boxlo_lamda;
     hi = boxhi_lamda;
 
     x2lamda(x,lamda);
 
     if (!xperiodic && (lamda[0] < lo[0] || lamda[0] >= hi[0])) return 0;
     if (!yperiodic && (lamda[1] < lo[1] || lamda[1] >= hi[1])) return 0;
     if (!zperiodic && (lamda[2] < lo[2] || lamda[2] >= hi[2])) return 0;
     return 1;
   }
 
 }
 
 /* ----------------------------------------------------------------------
    warn if image flags of any bonded atoms are inconsistent
    could be a problem when using replicate or fix rigid
 ------------------------------------------------------------------------- */
 
 void Domain::image_check()
 {
   int i,j,k,n,imol,iatom;
   tagint tagprev;
 
   // only need to check if system is molecular and some dimension is periodic
   // if running verlet/split, don't check on KSpace partition since
   //    it has no ghost atoms and thus bond partners won't exist
 
   if (!atom->molecular) return;
   if (!xperiodic && !yperiodic && (dimension == 2 || !zperiodic)) return;
   if (strncmp(update->integrate_style,"verlet/split",12) == 0 &&
       universe->iworld != 0) return;
 
   // communicate unwrapped position of owned atoms to ghost atoms
 
   double **unwrap;
   memory->create(unwrap,atom->nmax,3,"domain:unwrap");
 
   double **x = atom->x;
   imageint *image = atom->image;
   int nlocal = atom->nlocal;
 
   for (i = 0; i < nlocal; i++)
     unmap(x[i],image[i],unwrap[i]);
 
   comm->forward_comm_array(3,unwrap);
 
   // compute unwrapped extent of each bond
   // flag if any bond component is longer than 1/2 of periodic box length
   // flag if any bond component is longer than non-periodic box length
   //   which means image flags in that dimension were different
 
   int molecular = atom->molecular;
 
   int *num_bond = atom->num_bond;
   tagint **bond_atom = atom->bond_atom;
   int **bond_type = atom->bond_type;
   tagint *tag = atom->tag;
   int *molindex = atom->molindex;
   int *molatom = atom->molatom;
   Molecule **onemols = atom->avec->onemols;
 
   double delx,dely,delz;
 
   int lostbond = output->thermo->lostbond;
   int nmissing = 0;
 
   int flag = 0;
   for (i = 0; i < nlocal; i++) {
     if (molecular == 1) n = num_bond[i];
     else {
       if (molindex[i] < 0) continue;
       imol = molindex[i];
       iatom = molatom[i];
       n = onemols[imol]->num_bond[iatom];
     }
 
     for (j = 0; j < n; j++) {
       if (molecular == 1) {
         if (bond_type[i][j] <= 0) continue;
         k = atom->map(bond_atom[i][j]);
       } else {
         if (onemols[imol]->bond_type[iatom][j] < 0) continue;
         tagprev = tag[i] - iatom - 1;
         k = atom->map(onemols[imol]->bond_atom[iatom][j]+tagprev);
       }
 
       if (k == -1) {
         nmissing++;
         if (lostbond == ERROR)
           error->one(FLERR,"Bond atom missing in image check");
         continue;
       }
 
       delx = unwrap[i][0] - unwrap[k][0];
       dely = unwrap[i][1] - unwrap[k][1];
       delz = unwrap[i][2] - unwrap[k][2];
 
       if (xperiodic && delx > xprd_half) flag = 1;
       if (xperiodic && dely > yprd_half) flag = 1;
       if (dimension == 3 && zperiodic && delz > zprd_half) flag = 1;
       if (!xperiodic && delx > xprd) flag = 1;
       if (!yperiodic && dely > yprd) flag = 1;
       if (dimension == 3 && !zperiodic && delz > zprd) flag = 1;
     }
   }
 
   int flagall;
   MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_MAX,world);
   if (flagall && comm->me == 0)
     error->warning(FLERR,"Inconsistent image flags");
 
   if (lostbond == WARN) {
     int all;
     MPI_Allreduce(&nmissing,&all,1,MPI_INT,MPI_SUM,world);
     if (all && comm->me == 0)
       error->warning(FLERR,"Bond atom missing in image check");
   }
 
   memory->destroy(unwrap);
 }
 
 /* ----------------------------------------------------------------------
    warn if end atoms in any bonded interaction
      are further apart than half a periodic box length
    could cause problems when bonded neighbor list is built since
      closest_image() could return wrong image
 ------------------------------------------------------------------------- */
 
 void Domain::box_too_small_check()
 {
   int i,j,k,n,imol,iatom;
   tagint tagprev;
 
   // only need to check if system is molecular and some dimension is periodic
   // if running verlet/split, don't check on KSpace partition since
   //    it has no ghost atoms and thus bond partners won't exist
 
   if (!atom->molecular) return;
   if (!xperiodic && !yperiodic && (dimension == 2 || !zperiodic)) return;
   if (strncmp(update->integrate_style,"verlet/split",12) == 0 &&
       universe->iworld != 0) return;
 
   // maxbondall = longest current bond length
   // if periodic box dim is tiny (less than 2 * bond-length),
   //   minimum_image() itself may compute bad bond lengths
   // in this case, image_check() should warn,
   //   assuming 2 atoms have consistent image flags
 
   int molecular = atom->molecular;
 
   double **x = atom->x;
   int *num_bond = atom->num_bond;
   tagint **bond_atom = atom->bond_atom;
   int **bond_type = atom->bond_type;
   tagint *tag = atom->tag;
   int *molindex = atom->molindex;
   int *molatom = atom->molatom;
   Molecule **onemols = atom->avec->onemols;
   int nlocal = atom->nlocal;
 
   double delx,dely,delz,rsq;
   double maxbondme = 0.0;
 
   int lostbond = output->thermo->lostbond;
   int nmissing = 0;
 
   for (i = 0; i < nlocal; i++) {
     if (molecular == 1) n = num_bond[i];
     else {
       if (molindex[i] < 0) continue;
       imol = molindex[i];
       iatom = molatom[i];
       n = onemols[imol]->num_bond[iatom];
     }
 
     for (j = 0; j < n; j++) {
       if (molecular == 1) {
         if (bond_type[i][j] <= 0) continue;
         k = atom->map(bond_atom[i][j]);
       } else {
         if (onemols[imol]->bond_type[iatom][j] < 0) continue;
         tagprev = tag[i] - iatom - 1;
         k = atom->map(onemols[imol]->bond_atom[iatom][j]+tagprev);
       }
 
       if (k == -1) {
         nmissing++;
         if (lostbond == ERROR)
           error->one(FLERR,"Bond atom missing in box size check");
         continue;
       }
 
       delx = x[i][0] - x[k][0];
       dely = x[i][1] - x[k][1];
       delz = x[i][2] - x[k][2];
       minimum_image(delx,dely,delz);
       rsq = delx*delx + dely*dely + delz*delz;
       maxbondme = MAX(maxbondme,rsq);
     }
   }
 
   if (lostbond == WARN) {
     int all;
     MPI_Allreduce(&nmissing,&all,1,MPI_INT,MPI_SUM,world);
     if (all && comm->me == 0)
       error->warning(FLERR,"Bond atom missing in box size check");
   }
 
   double maxbondall;
   MPI_Allreduce(&maxbondme,&maxbondall,1,MPI_DOUBLE,MPI_MAX,world);
   maxbondall = sqrt(maxbondall);
 
   // maxdelta = furthest apart 2 atoms in a bonded interaction can be
   // include BONDSTRETCH factor to account for dynamics
 
   double maxdelta = maxbondall * BONDSTRETCH;
   if (atom->nangles) maxdelta = 2.0 * maxbondall * BONDSTRETCH;
   if (atom->ndihedrals) maxdelta = 3.0 * maxbondall * BONDSTRETCH;
 
   // warn if maxdelta > than half any periodic box length
   // since atoms in the interaction could rotate into that dimension
 
   int flag = 0;
   if (xperiodic && maxdelta > xprd_half) flag = 1;
   if (yperiodic && maxdelta > yprd_half) flag = 1;
   if (dimension == 3 && zperiodic && maxdelta > zprd_half) flag = 1;
 
   int flagall;
   MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_MAX,world);
   if (flagall && comm->me == 0)
     error->warning(FLERR,
                    "Bond/angle/dihedral extent > half of periodic box length");
 }
 
 /* ----------------------------------------------------------------------
   check warn if any proc's subbox is smaller than thresh
     since may lead to lost atoms in comm->exchange()
   current callers set thresh = neighbor skin
 ------------------------------------------------------------------------- */
 
 void Domain::subbox_too_small_check(double thresh)
 {
   int flag = 0;
   if (!triclinic) {
     if (subhi[0]-sublo[0] < thresh || subhi[1]-sublo[1] < thresh) flag = 1;
     if (dimension == 3 && subhi[2]-sublo[2] < thresh) flag = 1;
   } else {
     double delta = subhi_lamda[0] - sublo_lamda[0];
     if (delta*prd[0] < thresh) flag = 1;
     delta = subhi_lamda[1] - sublo_lamda[1];
     if (delta*prd[1] < thresh) flag = 1;
     if (dimension == 3) {
       delta = subhi_lamda[2] - sublo_lamda[2];
       if (delta*prd[2] < thresh) flag = 1;
     }
   }
 
   int flagall;
   MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_SUM,world);
   if (flagall && comm->me == 0)
     error->warning(FLERR,"Proc sub-domain size < neighbor skin, "
                    "could lead to lost atoms");
 }
 
 /* ----------------------------------------------------------------------
    minimum image convention
    use 1/2 of box size as test
    for triclinic, also add/subtract tilt factors in other dims as needed
 ------------------------------------------------------------------------- */
 
 void Domain::minimum_image(double &dx, double &dy, double &dz)
 {
   if (triclinic == 0) {
     if (xperiodic) {
       if (fabs(dx) > xprd_half) {
         if (dx < 0.0) dx += xprd;
         else dx -= xprd;
       }
     }
     if (yperiodic) {
       if (fabs(dy) > yprd_half) {
         if (dy < 0.0) dy += yprd;
         else dy -= yprd;
       }
     }
     if (zperiodic) {
       if (fabs(dz) > zprd_half) {
         if (dz < 0.0) dz += zprd;
         else dz -= zprd;
       }
     }
 
   } else {
     if (zperiodic) {
       if (fabs(dz) > zprd_half) {
         if (dz < 0.0) {
           dz += zprd;
           dy += yz;
           dx += xz;
         } else {
           dz -= zprd;
           dy -= yz;
           dx -= xz;
         }
       }
     }
     if (yperiodic) {
       if (fabs(dy) > yprd_half) {
         if (dy < 0.0) {
           dy += yprd;
           dx += xy;
         } else {
           dy -= yprd;
           dx -= xy;
         }
       }
     }
     if (xperiodic) {
       if (fabs(dx) > xprd_half) {
         if (dx < 0.0) dx += xprd;
         else dx -= xprd;
       }
     }
   }
 }
 
 /* ----------------------------------------------------------------------
    minimum image convention
    use 1/2 of box size as test
    for triclinic, also add/subtract tilt factors in other dims as needed
 ------------------------------------------------------------------------- */
 
 void Domain::minimum_image(double *delta)
 {
   if (triclinic == 0) {
     if (xperiodic) {
       if (fabs(delta[0]) > xprd_half) {
         if (delta[0] < 0.0) delta[0] += xprd;
         else delta[0] -= xprd;
       }
     }
     if (yperiodic) {
       if (fabs(delta[1]) > yprd_half) {
         if (delta[1] < 0.0) delta[1] += yprd;
         else delta[1] -= yprd;
       }
     }
     if (zperiodic) {
       if (fabs(delta[2]) > zprd_half) {
         if (delta[2] < 0.0) delta[2] += zprd;
         else delta[2] -= zprd;
       }
     }
 
   } else {
     if (zperiodic) {
       if (fabs(delta[2]) > zprd_half) {
         if (delta[2] < 0.0) {
           delta[2] += zprd;
           delta[1] += yz;
           delta[0] += xz;
         } else {
           delta[2] -= zprd;
           delta[1] -= yz;
           delta[0] -= xz;
         }
       }
     }
     if (yperiodic) {
       if (fabs(delta[1]) > yprd_half) {
         if (delta[1] < 0.0) {
           delta[1] += yprd;
           delta[0] += xy;
         } else {
           delta[1] -= yprd;
           delta[0] -= xy;
         }
       }
     }
     if (xperiodic) {
       if (fabs(delta[0]) > xprd_half) {
         if (delta[0] < 0.0) delta[0] += xprd;
         else delta[0] -= xprd;
       }
     }
   }
 }
 
 /* ----------------------------------------------------------------------
    return local index of atom J or any of its images that is closest to atom I
    if J is not a valid index like -1, just return it
 ------------------------------------------------------------------------- */
 
 int Domain::closest_image(int i, int j)
 {
   if (j < 0) return j;
 
   int *sametag = atom->sametag;
   double **x = atom->x;
   double *xi = x[i];
 
   int closest = j;
   double delx = xi[0] - x[j][0];
   double dely = xi[1] - x[j][1];
   double delz = xi[2] - x[j][2];
   double rsqmin = delx*delx + dely*dely + delz*delz;
   double rsq;
 
   while (sametag[j] >= 0) {
     j = sametag[j];
     delx = xi[0] - x[j][0];
     dely = xi[1] - x[j][1];
     delz = xi[2] - x[j][2];
     rsq = delx*delx + dely*dely + delz*delz;
     if (rsq < rsqmin) {
       rsqmin = rsq;
       closest = j;
     }
   }
   return closest;
 }
 
 /* ----------------------------------------------------------------------
    find and return Xj image = periodic image of Xj that is closest to Xi
    for triclinic, add/subtract tilt factors in other dims as needed
 ------------------------------------------------------------------------- */
 
 void Domain::closest_image(const double * const xi, const double * const xj,
                            double * const xjimage)
 {
   double dx = xj[0] - xi[0];
   double dy = xj[1] - xi[1];
   double dz = xj[2] - xi[2];
 
   if (triclinic == 0) {
     if (xperiodic) {
       if (dx < 0.0) {
         while (dx < 0.0) dx += xprd;
         if (dx > xprd_half) dx -= xprd;
       } else {
         while (dx > 0.0) dx -= xprd;
         if (dx < -xprd_half) dx += xprd;
       }
     }
     if (yperiodic) {
       if (dy < 0.0) {
         while (dy < 0.0) dy += yprd;
         if (dy > yprd_half) dy -= yprd;
       } else {
         while (dy > 0.0) dy -= yprd;
         if (dy < -yprd_half) dy += yprd;
       }
     }
     if (zperiodic) {
       if (dz < 0.0) {
         while (dz < 0.0) dz += zprd;
         if (dz > zprd_half) dz -= zprd;
       } else {
         while (dz > 0.0) dz -= zprd;
         if (dz < -zprd_half) dz += zprd;
       }
     }
 
   } else {
     if (zperiodic) {
       if (dz < 0.0) {
         while (dz < 0.0) {
           dz += zprd;
           dy += yz;
           dx += xz;
         }
         if (dz > zprd_half) {
           dz -= zprd;
           dy -= yz;
           dx -= xz;
         }
       } else {
         while (dz > 0.0) {
           dz -= zprd;
           dy -= yz;
           dx -= xz;
         }
         if (dz < -zprd_half) {
           dz += zprd;
           dy += yz;
           dx += xz;
         }
       }
     }
     if (yperiodic) {
       if (dy < 0.0) {
         while (dy < 0.0) {
           dy += yprd;
           dx += xy;
         }
         if (dy > yprd_half) {
           dy -= yprd;
           dx -= xy;
         }
       } else {
         while (dy > 0.0) {
           dy -= yprd;
           dx -= xy;
         }
         if (dy < -yprd_half) {
           dy += yprd;
           dx += xy;
         }
       }
     }
     if (xperiodic) {
       if (dx < 0.0) {
         while (dx < 0.0) dx += xprd;
         if (dx > xprd_half) dx -= xprd;
       } else {
         while (dx > 0.0) dx -= xprd;
         if (dx < -xprd_half) dx += xprd;
       }
     }
   }
 
   xjimage[0] = xi[0] + dx;
   xjimage[1] = xi[1] + dy;
   xjimage[2] = xi[2] + dz;
 }
 
 /* ----------------------------------------------------------------------
    remap the point into the periodic box no matter how far away
    adjust 3 image flags encoded in image accordingly
    resulting coord must satisfy lo <= coord < hi
    MAX is important since coord - prd < lo can happen when coord = hi
    for triclinic, point is converted to lamda coords (0-1) before doing remap
    image = 10 bits for each dimension
    increment/decrement in wrap-around fashion
 ------------------------------------------------------------------------- */
 
 void Domain::remap(double *x, imageint &image)
 {
   double *lo,*hi,*period,*coord;
   double lamda[3];
   imageint idim,otherdims;
 
   if (triclinic == 0) {
     lo = boxlo;
     hi = boxhi;
     period = prd;
     coord = x;
   } else {
     lo = boxlo_lamda;
     hi = boxhi_lamda;
     period = prd_lamda;
     x2lamda(x,lamda);
     coord = lamda;
   }
 
   if (xperiodic) {
     while (coord[0] < lo[0]) {
       coord[0] += period[0];
       idim = image & IMGMASK;
       otherdims = image ^ idim;
       idim--;
       idim &= IMGMASK;
       image = otherdims | idim;
     }
     while (coord[0] >= hi[0]) {
       coord[0] -= period[0];
       idim = image & IMGMASK;
       otherdims = image ^ idim;
       idim++;
       idim &= IMGMASK;
       image = otherdims | idim;
     }
     coord[0] = MAX(coord[0],lo[0]);
   }
 
   if (yperiodic) {
     while (coord[1] < lo[1]) {
       coord[1] += period[1];
       idim = (image >> IMGBITS) & IMGMASK;
       otherdims = image ^ (idim << IMGBITS);
       idim--;
       idim &= IMGMASK;
       image = otherdims | (idim << IMGBITS);
     }
     while (coord[1] >= hi[1]) {
       coord[1] -= period[1];
       idim = (image >> IMGBITS) & IMGMASK;
       otherdims = image ^ (idim << IMGBITS);
       idim++;
       idim &= IMGMASK;
       image = otherdims | (idim << IMGBITS);
     }
     coord[1] = MAX(coord[1],lo[1]);
   }
 
   if (zperiodic) {
     while (coord[2] < lo[2]) {
       coord[2] += period[2];
       idim = image >> IMG2BITS;
       otherdims = image ^ (idim << IMG2BITS);
       idim--;
       idim &= IMGMASK;
       image = otherdims | (idim << IMG2BITS);
     }
     while (coord[2] >= hi[2]) {
       coord[2] -= period[2];
       idim = image >> IMG2BITS;
       otherdims = image ^ (idim << IMG2BITS);
       idim++;
       idim &= IMGMASK;
       image = otherdims | (idim << IMG2BITS);
     }
     coord[2] = MAX(coord[2],lo[2]);
   }
 
   if (triclinic) lamda2x(coord,x);
 }
 
 /* ----------------------------------------------------------------------
    remap the point into the periodic box no matter how far away
    no image flag calculation
    resulting coord must satisfy lo <= coord < hi
    MAX is important since coord - prd < lo can happen when coord = hi
    for triclinic, point is converted to lamda coords (0-1) before remap
 ------------------------------------------------------------------------- */
 
 void Domain::remap(double *x)
 {
   double *lo,*hi,*period,*coord;
   double lamda[3];
 
   if (triclinic == 0) {
     lo = boxlo;
     hi = boxhi;
     period = prd;
     coord = x;
   } else {
     lo = boxlo_lamda;
     hi = boxhi_lamda;
     period = prd_lamda;
     x2lamda(x,lamda);
     coord = lamda;
   }
 
   if (xperiodic) {
     while (coord[0] < lo[0]) coord[0] += period[0];
     while (coord[0] >= hi[0]) coord[0] -= period[0];
     coord[0] = MAX(coord[0],lo[0]);
   }
 
   if (yperiodic) {
     while (coord[1] < lo[1]) coord[1] += period[1];
     while (coord[1] >= hi[1]) coord[1] -= period[1];
     coord[1] = MAX(coord[1],lo[1]);
   }
 
   if (zperiodic) {
     while (coord[2] < lo[2]) coord[2] += period[2];
     while (coord[2] >= hi[2]) coord[2] -= period[2];
     coord[2] = MAX(coord[2],lo[2]);
   }
 
   if (triclinic) lamda2x(coord,x);
 }
 
 /* ----------------------------------------------------------------------
    remap xnew to be within half box length of xold
    do it directly, not iteratively, in case is far away
    for triclinic, both points are converted to lamda coords (0-1) before remap
 ------------------------------------------------------------------------- */
 
 void Domain::remap_near(double *xnew, double *xold)
 {
   int n;
   double *coordnew,*coordold,*period,*half;
   double lamdanew[3],lamdaold[3];
 
   if (triclinic == 0) {
     period = prd;
     half = prd_half;
     coordnew = xnew;
     coordold = xold;
   } else {
     period = prd_lamda;
     half = prd_half_lamda;
     x2lamda(xnew,lamdanew);
     coordnew = lamdanew;
     x2lamda(xold,lamdaold);
     coordold = lamdaold;
   }
 
   // iterative form
   // if (xperiodic) {
   //   while (coordnew[0]-coordold[0] > half[0]) coordnew[0] -= period[0];
   //   while (coordold[0]-coordnew[0] > half[0]) coordnew[0] += period[0];
   // }
 
   if (xperiodic) {
     if (coordnew[0]-coordold[0] > period[0]) {
       n = static_cast<int> ((coordnew[0]-coordold[0])/period[0]);
       coordnew[0] -= n*period[0];
     }
     while (coordnew[0]-coordold[0] > half[0]) coordnew[0] -= period[0];
     if (coordold[0]-coordnew[0] > period[0]) {
       n = static_cast<int> ((coordold[0]-coordnew[0])/period[0]);
       coordnew[0] += n*period[0];
     }
     while (coordold[0]-coordnew[0] > half[0]) coordnew[0] += period[0];
   }
 
   if (yperiodic) {
     if (coordnew[1]-coordold[1] > period[1]) {
       n = static_cast<int> ((coordnew[1]-coordold[1])/period[1]);
       coordnew[1] -= n*period[1];
     }
     while (coordnew[1]-coordold[1] > half[1]) coordnew[1] -= period[1];
     if (coordold[1]-coordnew[1] > period[1]) {
       n = static_cast<int> ((coordold[1]-coordnew[1])/period[1]);
       coordnew[1] += n*period[1];
     }
     while (coordold[1]-coordnew[1] > half[1]) coordnew[1] += period[1];
   }
 
   if (zperiodic) {
     if (coordnew[2]-coordold[2] > period[2]) {
       n = static_cast<int> ((coordnew[2]-coordold[2])/period[2]);
       coordnew[2] -= n*period[2];
     }
     while (coordnew[2]-coordold[2] > half[2]) coordnew[2] -= period[2];
     if (coordold[2]-coordnew[2] > period[2]) {
       n = static_cast<int> ((coordold[2]-coordnew[2])/period[2]);
       coordnew[2] += n*period[2];
     }
     while (coordold[2]-coordnew[2] > half[2]) coordnew[2] += period[2];
   }
 
   if (triclinic) lamda2x(coordnew,xnew);
 }
 
 /* ----------------------------------------------------------------------
    unmap the point via image flags
    x overwritten with result, don't reset image flag
    for triclinic, use h[] to add in tilt factors in other dims as needed
 ------------------------------------------------------------------------- */
 
 void Domain::unmap(double *x, imageint image)
 {
   int xbox = (image & IMGMASK) - IMGMAX;
   int ybox = (image >> IMGBITS & IMGMASK) - IMGMAX;
   int zbox = (image >> IMG2BITS) - IMGMAX;
 
   if (triclinic == 0) {
     x[0] += xbox*xprd;
     x[1] += ybox*yprd;
     x[2] += zbox*zprd;
   } else {
     x[0] += h[0]*xbox + h[5]*ybox + h[4]*zbox;
     x[1] += h[1]*ybox + h[3]*zbox;
     x[2] += h[2]*zbox;
   }
 }
 
 /* ----------------------------------------------------------------------
    unmap the point via image flags
    result returned in y, don't reset image flag
    for triclinic, use h[] to add in tilt factors in other dims as needed
 ------------------------------------------------------------------------- */
 
 void Domain::unmap(double *x, imageint image, double *y)
 {
   int xbox = (image & IMGMASK) - IMGMAX;
   int ybox = (image >> IMGBITS & IMGMASK) - IMGMAX;
   int zbox = (image >> IMG2BITS) - IMGMAX;
 
   if (triclinic == 0) {
     y[0] = x[0] + xbox*xprd;
     y[1] = x[1] + ybox*yprd;
     y[2] = x[2] + zbox*zprd;
   } else {
     y[0] = x[0] + h[0]*xbox + h[5]*ybox + h[4]*zbox;
     y[1] = x[1] + h[1]*ybox + h[3]*zbox;
     y[2] = x[2] + h[2]*zbox;
   }
 }
 
 /* ----------------------------------------------------------------------
    adjust image flags due to triclinic box flip
    flip operation is changing box vectors A,B,C to new A',B',C'
      A' = A              (A does not change)
      B' = B + mA         (B shifted by A)
      C' = C + pB + nA    (C shifted by B and/or A)
    this requires the image flags change from (a,b,c) to (a',b',c')
    so that x_unwrap for each atom is same before/after
      x_unwrap_before = xlocal + aA + bB + cC
      x_unwrap_after = xlocal + a'A' + b'B' + c'C'
    this requires:
      c' = c
      b' = b - cp
      a' = a - (b-cp)m - cn = a - b'm - cn
    in other words, for xy flip, change in x flag depends on current y flag
    this is b/c the xy flip dramatically changes which tiled image of
      simulation box an unwrapped point maps to
 ------------------------------------------------------------------------- */
 
 void Domain::image_flip(int m, int n, int p)
 {
   imageint *image = atom->image;
   int nlocal = atom->nlocal;
 
   for (int i = 0; i < nlocal; i++) {
     int xbox = (image[i] & IMGMASK) - IMGMAX;
     int ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX;
     int zbox = (image[i] >> IMG2BITS) - IMGMAX;
 
     ybox -= p*zbox;
     xbox -= m*ybox + n*zbox;
 
     image[i] = ((imageint) (xbox + IMGMAX) & IMGMASK) |
       (((imageint) (ybox + IMGMAX) & IMGMASK) << IMGBITS) |
       (((imageint) (zbox + IMGMAX) & IMGMASK) << IMG2BITS);
   }
 }
 
 /* ----------------------------------------------------------------------
    create a lattice
 ------------------------------------------------------------------------- */
 
 void Domain::set_lattice(int narg, char **arg)
 {
   if (lattice) delete lattice;
   lattice = new Lattice(lmp,narg,arg);
 }
 
 /* ----------------------------------------------------------------------
    create a new region
 ------------------------------------------------------------------------- */
 
 void Domain::add_region(int narg, char **arg)
 {
   if (narg < 2) error->all(FLERR,"Illegal region command");
 
   if (strcmp(arg[1],"delete") == 0) {
     delete_region(narg,arg);
     return;
   }
 
   if (find_region(arg[0]) >= 0) error->all(FLERR,"Reuse of region ID");
 
   // extend Region list if necessary
 
   if (nregion == maxregion) {
     maxregion += DELTAREGION;
     regions = (Region **)
       memory->srealloc(regions,maxregion*sizeof(Region *),"domain:regions");
   }
 
   // create the Region
 
   if (lmp->suffix_enable) {
     if (lmp->suffix) {
       char estyle[256];
       sprintf(estyle,"%s/%s",arg[1],lmp->suffix);
-
-      if (0) return;
-
-#define REGION_CLASS
-#define RegionStyle(key,Class) \
-      else if (strcmp(estyle,#key) == 0) { \
-            regions[nregion] = new Class(lmp,narg,arg); \
-            regions[nregion]->init(); \
-            nregion++; \
-            return; \
+      if (region_map->find(estyle) != region_map->end()) {
+        RegionCreator region_creator = (*region_map)[estyle];
+        regions[nregion] = region_creator(lmp, narg, arg);
+        regions[nregion]->init();
+        nregion++;
+        return;
       }
-#include "style_region.h"
-#undef RegionStyle
-#undef REGION_CLASS
     }
 
     if (lmp->suffix2) {
       char estyle[256];
       sprintf(estyle,"%s/%s",arg[1],lmp->suffix2);
-
-      if (0) return;
-
-#define REGION_CLASS
-#define RegionStyle(key,Class) \
-      else if (strcmp(estyle,#key) == 0) { \
-            regions[nregion] = new Class(lmp,narg,arg); \
-            regions[nregion]->init(); \
-            nregion++; \
-            return; \
+      if (region_map->find(estyle) != region_map->end()) {
+        RegionCreator region_creator = (*region_map)[estyle];
+        regions[nregion] = region_creator(lmp, narg, arg);
+        regions[nregion]->init();
+        nregion++;
+        return;
       }
-#include "style_region.h"
-#undef RegionStyle
-#undef REGION_CLASS
     }
   }
 
   if (strcmp(arg[1],"none") == 0) error->all(FLERR,"Unknown region style");
-
-#define REGION_CLASS
-#define RegionStyle(key,Class) \
-  else if (strcmp(arg[1],#key) == 0) \
-    regions[nregion] = new Class(lmp,narg,arg);
-#include "style_region.h"
-#undef REGION_CLASS
-
+  if (region_map->find(arg[1]) != region_map->end()) {
+    RegionCreator region_creator = (*region_map)[arg[1]];
+    regions[nregion] = region_creator(lmp, narg, arg);
+  }
   else error->all(FLERR,"Unknown region style");
 
   // initialize any region variables via init()
   // in case region is used between runs, e.g. to print a variable
 
   regions[nregion]->init();
   nregion++;
 }
 
+/* ----------------------------------------------------------------------
+   one instance per region style in style_region.h
+------------------------------------------------------------------------- */
+
+template <typename T>
+Region *Domain::region_creator(LAMMPS *lmp, int narg, char ** arg)
+{
+  return new T(lmp, narg, arg);
+}
+
 /* ----------------------------------------------------------------------
    delete a region
 ------------------------------------------------------------------------- */
 
 void Domain::delete_region(int narg, char **arg)
 {
   if (narg != 2) error->all(FLERR,"Illegal region command");
 
   int iregion = find_region(arg[0]);
   if (iregion == -1) error->all(FLERR,"Delete region ID does not exist");
 
   delete regions[iregion];
   regions[iregion] = regions[nregion-1];
   nregion--;
 }
 
 /* ----------------------------------------------------------------------
    return region index if name matches existing region ID
    return -1 if no such region
 ------------------------------------------------------------------------- */
 
 int Domain::find_region(char *name)
 {
   for (int iregion = 0; iregion < nregion; iregion++)
     if (strcmp(name,regions[iregion]->id) == 0) return iregion;
   return -1;
 }
 
 /* ----------------------------------------------------------------------
    (re)set boundary settings
    flag = 0, called from the input script
    flag = 1, called from change box command
 ------------------------------------------------------------------------- */
 
 void Domain::set_boundary(int narg, char **arg, int flag)
 {
   if (narg != 3) error->all(FLERR,"Illegal boundary command");
 
   char c;
   for (int idim = 0; idim < 3; idim++)
     for (int iside = 0; iside < 2; iside++) {
       if (iside == 0) c = arg[idim][0];
       else if (iside == 1 && strlen(arg[idim]) == 1) c = arg[idim][0];
       else c = arg[idim][1];
 
       if (c == 'p') boundary[idim][iside] = 0;
       else if (c == 'f') boundary[idim][iside] = 1;
       else if (c == 's') boundary[idim][iside] = 2;
       else if (c == 'm') boundary[idim][iside] = 3;
       else {
         if (flag == 0) error->all(FLERR,"Illegal boundary command");
         if (flag == 1) error->all(FLERR,"Illegal change_box command");
       }
     }
 
   for (int idim = 0; idim < 3; idim++)
     if ((boundary[idim][0] == 0 && boundary[idim][1]) ||
         (boundary[idim][0] && boundary[idim][1] == 0))
       error->all(FLERR,"Both sides of boundary must be periodic");
 
   if (boundary[0][0] == 0) xperiodic = 1;
   else xperiodic = 0;
   if (boundary[1][0] == 0) yperiodic = 1;
   else yperiodic = 0;
   if (boundary[2][0] == 0) zperiodic = 1;
   else zperiodic = 0;
 
   periodicity[0] = xperiodic;
   periodicity[1] = yperiodic;
   periodicity[2] = zperiodic;
 
   nonperiodic = 0;
   if (xperiodic == 0 || yperiodic == 0 || zperiodic == 0) {
     nonperiodic = 1;
     if (boundary[0][0] >= 2 || boundary[0][1] >= 2 ||
         boundary[1][0] >= 2 || boundary[1][1] >= 2 ||
         boundary[2][0] >= 2 || boundary[2][1] >= 2) nonperiodic = 2;
   }
 }
 
 /* ----------------------------------------------------------------------
    set domain attributes
 ------------------------------------------------------------------------- */
 
 void Domain::set_box(int narg, char **arg)
 {
   if (narg < 1) error->all(FLERR,"Illegal box command");
 
   int iarg = 0;
   while (iarg < narg) {
     if (strcmp(arg[iarg],"tilt") == 0) {
       if (iarg+2 > narg) error->all(FLERR,"Illegal box command");
       if (strcmp(arg[iarg+1],"small") == 0) tiltsmall = 1;
       else if (strcmp(arg[iarg+1],"large") == 0) tiltsmall = 0;
       else error->all(FLERR,"Illegal box command");
       iarg += 2;
     } else error->all(FLERR,"Illegal box command");
   }
 }
 
 /* ----------------------------------------------------------------------
    print box info, orthogonal or triclinic
 ------------------------------------------------------------------------- */
 
 void Domain::print_box(const char *str)
 {
   if (comm->me == 0) {
     if (screen) {
       if (triclinic == 0)
         fprintf(screen,"%sorthogonal box = (%g %g %g) to (%g %g %g)\n",
                 str,boxlo[0],boxlo[1],boxlo[2],boxhi[0],boxhi[1],boxhi[2]);
       else {
         char *format = (char *)
           "%striclinic box = (%g %g %g) to (%g %g %g) with tilt (%g %g %g)\n";
         fprintf(screen,format,
                 str,boxlo[0],boxlo[1],boxlo[2],boxhi[0],boxhi[1],boxhi[2],
                 xy,xz,yz);
       }
     }
     if (logfile) {
       if (triclinic == 0)
         fprintf(logfile,"%sorthogonal box = (%g %g %g) to (%g %g %g)\n",
                 str,boxlo[0],boxlo[1],boxlo[2],boxhi[0],boxhi[1],boxhi[2]);
       else {
         char *format = (char *)
           "%striclinic box = (%g %g %g) to (%g %g %g) with tilt (%g %g %g)\n";
         fprintf(logfile,format,
                 str,boxlo[0],boxlo[1],boxlo[2],boxhi[0],boxhi[1],boxhi[2],
                 xy,xz,yz);
       }
     }
   }
 }
 
 /* ----------------------------------------------------------------------
    format boundary string for output
    assume str is 9 chars or more in length
 ------------------------------------------------------------------------- */
 
 void Domain::boundary_string(char *str)
 {
   int m = 0;
   for (int idim = 0; idim < 3; idim++) {
     for (int iside = 0; iside < 2; iside++) {
       if (boundary[idim][iside] == 0) str[m++] = 'p';
       else if (boundary[idim][iside] == 1) str[m++] = 'f';
       else if (boundary[idim][iside] == 2) str[m++] = 's';
       else if (boundary[idim][iside] == 3) str[m++] = 'm';
     }
     str[m++] = ' ';
   }
   str[8] = '\0';
 }
 
 /* ----------------------------------------------------------------------
    convert triclinic 0-1 lamda coords to box coords for all N atoms
    x = H lamda + x0;
 ------------------------------------------------------------------------- */
 
 void Domain::lamda2x(int n)
 {
   double **x = atom->x;
 
   for (int i = 0; i < n; i++) {
     x[i][0] = h[0]*x[i][0] + h[5]*x[i][1] + h[4]*x[i][2] + boxlo[0];
     x[i][1] = h[1]*x[i][1] + h[3]*x[i][2] + boxlo[1];
     x[i][2] = h[2]*x[i][2] + boxlo[2];
   }
 }
 
 /* ----------------------------------------------------------------------
    convert box coords to triclinic 0-1 lamda coords for all N atoms
    lamda = H^-1 (x - x0)
 ------------------------------------------------------------------------- */
 
 void Domain::x2lamda(int n)
 {
   double delta[3];
   double **x = atom->x;
 
   for (int i = 0; i < n; i++) {
     delta[0] = x[i][0] - boxlo[0];
     delta[1] = x[i][1] - boxlo[1];
     delta[2] = x[i][2] - boxlo[2];
 
     x[i][0] = h_inv[0]*delta[0] + h_inv[5]*delta[1] + h_inv[4]*delta[2];
     x[i][1] = h_inv[1]*delta[1] + h_inv[3]*delta[2];
     x[i][2] = h_inv[2]*delta[2];
   }
 }
 
 /* ----------------------------------------------------------------------
    convert triclinic 0-1 lamda coords to box coords for one atom
    x = H lamda + x0;
    lamda and x can point to same 3-vector
 ------------------------------------------------------------------------- */
 
 void Domain::lamda2x(double *lamda, double *x)
 {
   x[0] = h[0]*lamda[0] + h[5]*lamda[1] + h[4]*lamda[2] + boxlo[0];
   x[1] = h[1]*lamda[1] + h[3]*lamda[2] + boxlo[1];
   x[2] = h[2]*lamda[2] + boxlo[2];
 }
 
 /* ----------------------------------------------------------------------
    convert box coords to triclinic 0-1 lamda coords for one atom
    lamda = H^-1 (x - x0)
    x and lamda can point to same 3-vector
 ------------------------------------------------------------------------- */
 
 void Domain::x2lamda(double *x, double *lamda)
 {
   double delta[3];
   delta[0] = x[0] - boxlo[0];
   delta[1] = x[1] - boxlo[1];
   delta[2] = x[2] - boxlo[2];
 
   lamda[0] = h_inv[0]*delta[0] + h_inv[5]*delta[1] + h_inv[4]*delta[2];
   lamda[1] = h_inv[1]*delta[1] + h_inv[3]*delta[2];
   lamda[2] = h_inv[2]*delta[2];
 }
 
 /* ----------------------------------------------------------------------
    convert box coords to triclinic 0-1 lamda coords for one atom
    use my_boxlo & my_h_inv stored by caller for previous state of box
    lamda = H^-1 (x - x0)
    x and lamda can point to same 3-vector
 ------------------------------------------------------------------------- */
 
 void Domain::x2lamda(double *x, double *lamda,
                      double *my_boxlo, double *my_h_inv)
 {
   double delta[3];
   delta[0] = x[0] - my_boxlo[0];
   delta[1] = x[1] - my_boxlo[1];
   delta[2] = x[2] - my_boxlo[2];
 
   lamda[0] = my_h_inv[0]*delta[0] + my_h_inv[5]*delta[1] + my_h_inv[4]*delta[2];
   lamda[1] = my_h_inv[1]*delta[1] + my_h_inv[3]*delta[2];
   lamda[2] = my_h_inv[2]*delta[2];
 }
 
 /* ----------------------------------------------------------------------
    convert 8 lamda corner pts of lo/hi box to box coords
    return bboxlo/hi = bounding box around 8 corner pts in box coords
 ------------------------------------------------------------------------- */
 
 void Domain::bbox(double *lo, double *hi, double *bboxlo, double *bboxhi)
 {
   double x[3];
 
   bboxlo[0] = bboxlo[1] = bboxlo[2] = BIG;
   bboxhi[0] = bboxhi[1] = bboxhi[2] = -BIG;
 
   x[0] = lo[0]; x[1] = lo[1]; x[2] = lo[2];
   lamda2x(x,x);
   bboxlo[0] = MIN(bboxlo[0],x[0]); bboxhi[0] = MAX(bboxhi[0],x[0]);
   bboxlo[1] = MIN(bboxlo[1],x[1]); bboxhi[1] = MAX(bboxhi[1],x[1]);
   bboxlo[2] = MIN(bboxlo[2],x[2]); bboxhi[2] = MAX(bboxhi[2],x[2]);
 
   x[0] = hi[0]; x[1] = lo[1]; x[2] = lo[2];
   lamda2x(x,x);
   bboxlo[0] = MIN(bboxlo[0],x[0]); bboxhi[0] = MAX(bboxhi[0],x[0]);
   bboxlo[1] = MIN(bboxlo[1],x[1]); bboxhi[1] = MAX(bboxhi[1],x[1]);
   bboxlo[2] = MIN(bboxlo[2],x[2]); bboxhi[2] = MAX(bboxhi[2],x[2]);
 
   x[0] = lo[0]; x[1] = hi[1]; x[2] = lo[2];
   lamda2x(x,x);
   bboxlo[0] = MIN(bboxlo[0],x[0]); bboxhi[0] = MAX(bboxhi[0],x[0]);
   bboxlo[1] = MIN(bboxlo[1],x[1]); bboxhi[1] = MAX(bboxhi[1],x[1]);
   bboxlo[2] = MIN(bboxlo[2],x[2]); bboxhi[2] = MAX(bboxhi[2],x[2]);
 
   x[0] = hi[0]; x[1] = hi[1]; x[2] = lo[2];
   lamda2x(x,x);
   bboxlo[0] = MIN(bboxlo[0],x[0]); bboxhi[0] = MAX(bboxhi[0],x[0]);
   bboxlo[1] = MIN(bboxlo[1],x[1]); bboxhi[1] = MAX(bboxhi[1],x[1]);
   bboxlo[2] = MIN(bboxlo[2],x[2]); bboxhi[2] = MAX(bboxhi[2],x[2]);
 
   x[0] = lo[0]; x[1] = lo[1]; x[2] = hi[2];
   lamda2x(x,x);
   bboxlo[0] = MIN(bboxlo[0],x[0]); bboxhi[0] = MAX(bboxhi[0],x[0]);
   bboxlo[1] = MIN(bboxlo[1],x[1]); bboxhi[1] = MAX(bboxhi[1],x[1]);
   bboxlo[2] = MIN(bboxlo[2],x[2]); bboxhi[2] = MAX(bboxhi[2],x[2]);
 
   x[0] = hi[0]; x[1] = lo[1]; x[2] = hi[2];
   lamda2x(x,x);
   bboxlo[0] = MIN(bboxlo[0],x[0]); bboxhi[0] = MAX(bboxhi[0],x[0]);
   bboxlo[1] = MIN(bboxlo[1],x[1]); bboxhi[1] = MAX(bboxhi[1],x[1]);
   bboxlo[2] = MIN(bboxlo[2],x[2]); bboxhi[2] = MAX(bboxhi[2],x[2]);
 
   x[0] = lo[0]; x[1] = hi[1]; x[2] = hi[2];
   lamda2x(x,x);
   bboxlo[0] = MIN(bboxlo[0],x[0]); bboxhi[0] = MAX(bboxhi[0],x[0]);
   bboxlo[1] = MIN(bboxlo[1],x[1]); bboxhi[1] = MAX(bboxhi[1],x[1]);
   bboxlo[2] = MIN(bboxlo[2],x[2]); bboxhi[2] = MAX(bboxhi[2],x[2]);
 
   x[0] = hi[0]; x[1] = hi[1]; x[2] = hi[2];
   lamda2x(x,x);
   bboxlo[0] = MIN(bboxlo[0],x[0]); bboxhi[0] = MAX(bboxhi[0],x[0]);
   bboxlo[1] = MIN(bboxlo[1],x[1]); bboxhi[1] = MAX(bboxhi[1],x[1]);
   bboxlo[2] = MIN(bboxlo[2],x[2]); bboxhi[2] = MAX(bboxhi[2],x[2]);
 }
 
 /* ----------------------------------------------------------------------
    compute 8 corner pts of my triclinic sub-box
    output is in corners, see ordering in lamda_box_corners
 ------------------------------------------------------------------------- */
 
 void Domain::box_corners()
 {
   lamda_box_corners(boxlo_lamda,boxhi_lamda);
 }
 
 /* ----------------------------------------------------------------------
    compute 8 corner pts of my triclinic sub-box
    output is in corners, see ordering in lamda_box_corners
 ------------------------------------------------------------------------- */
 
 void Domain::subbox_corners()
 {
   lamda_box_corners(sublo_lamda,subhi_lamda);
 }
 
 /* ----------------------------------------------------------------------
    compute 8 corner pts of any triclinic box with lo/hi in lamda coords
    8 output conners are ordered with x changing fastest, then y, finally z
    could be more efficient if just coded with xy,yz,xz explicitly
 ------------------------------------------------------------------------- */
 
 void Domain::lamda_box_corners(double *lo, double *hi)
 {
   corners[0][0] = lo[0]; corners[0][1] = lo[1]; corners[0][2] = lo[2];
   lamda2x(corners[0],corners[0]);
   corners[1][0] = hi[0]; corners[1][1] = lo[1]; corners[1][2] = lo[2];
   lamda2x(corners[1],corners[1]);
   corners[2][0] = lo[0]; corners[2][1] = hi[1]; corners[2][2] = lo[2];
   lamda2x(corners[2],corners[2]);
   corners[3][0] = hi[0]; corners[3][1] = hi[1]; corners[3][2] = lo[2];
   lamda2x(corners[3],corners[3]);
   corners[4][0] = lo[0]; corners[4][1] = lo[1]; corners[4][2] = hi[2];
   lamda2x(corners[4],corners[4]);
   corners[5][0] = hi[0]; corners[5][1] = lo[1]; corners[5][2] = hi[2];
   lamda2x(corners[5],corners[5]);
   corners[6][0] = lo[0]; corners[6][1] = hi[1]; corners[6][2] = hi[2];
   lamda2x(corners[6],corners[6]);
   corners[7][0] = hi[0]; corners[7][1] = hi[1]; corners[7][2] = subhi_lamda[2];
   lamda2x(corners[7],corners[7]);
 }
 
 
diff --git a/src/domain.h b/src/domain.h
index 8654b5916..ef3de4207 100644
--- a/src/domain.h
+++ b/src/domain.h
@@ -1,269 +1,278 @@
 /* -*- 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.
 ------------------------------------------------------------------------- */
 
 #ifndef LMP_DOMAIN_H
 #define LMP_DOMAIN_H
 
 #include <math.h>
 #include "pointers.h"
+#include <map>
+#include <string>
 
 namespace LAMMPS_NS {
 
 class Domain : protected Pointers {
  public:
   int box_exist;                         // 0 = not yet created, 1 = exists
   int dimension;                         // 2 = 2d, 3 = 3d
   int nonperiodic;                       // 0 = periodic in all 3 dims
                                          // 1 = periodic or fixed in all 6
                                          // 2 = shrink-wrap in any of 6
   int xperiodic,yperiodic,zperiodic;     // 0 = non-periodic, 1 = periodic
   int periodicity[3];                    // xyz periodicity as array
 
   int boundary[3][2];                    // settings for 6 boundaries
                                          // 0 = periodic
                                          // 1 = fixed non-periodic
                                          // 2 = shrink-wrap non-periodic
                                          // 3 = shrink-wrap non-per w/ min
 
   int triclinic;                         // 0 = orthog box, 1 = triclinic
   int tiltsmall;                         // 1 if limit tilt, else 0
 
                                          // orthogonal box
   double xprd,yprd,zprd;                 // global box dimensions
   double xprd_half,yprd_half,zprd_half;  // half dimensions
   double prd[3];                         // array form of dimensions
   double prd_half[3];                    // array form of half dimensions
 
                                          // triclinic box
                                          // xprd,xprd_half,prd,prd_half =
                                          // same as if untilted
   double prd_lamda[3];                   // lamda box = (1,1,1)
   double prd_half_lamda[3];              // lamda half box = (0.5,0.5,0.5)
 
   double boxlo[3],boxhi[3];              // orthogonal box global bounds
 
                                          // triclinic box
                                          // boxlo/hi = same as if untilted
   double boxlo_lamda[3],boxhi_lamda[3];  // lamda box = (0,1)
   double boxlo_bound[3],boxhi_bound[3];  // bounding box of tilted domain
   double corners[8][3];                  // 8 corner points
 
                                          // orthogonal box & triclinic box
   double minxlo,minxhi;                  // minimum size of global box
   double minylo,minyhi;                  // when shrink-wrapping
   double minzlo,minzhi;                  // tri only possible for non-skew dims
 
                                          // orthogonal box
   double sublo[3],subhi[3];              // sub-box bounds on this proc
 
                                          // triclinic box
                                          // sublo/hi = undefined
   double sublo_lamda[3],subhi_lamda[3];  // bounds of subbox in lamda
 
                                          // triclinic box
   double xy,xz,yz;                       // 3 tilt factors
   double h[6],h_inv[6];                  // shape matrix in Voigt notation
   double h_rate[6],h_ratelo[3];          // rate of box size/shape change
 
   int box_change;                // 1 if any of next 3 flags are set, else 0
   int box_change_size;           // 1 if box size changes, 0 if not
   int box_change_shape;          // 1 if box shape changes, 0 if not
   int box_change_domain;         // 1 if proc sub-domains change, 0 if not
 
   int deform_flag;                // 1 if fix deform exist, else 0
   int deform_vremap;              // 1 if fix deform remaps v, else 0
   int deform_groupbit;            // atom group to perform v remap for
 
   class Lattice *lattice;                  // user-defined lattice
 
   int nregion;                             // # of defined Regions
   int maxregion;                           // max # list can hold
   class Region **regions;                  // list of defined Regions
 
   int copymode;
 
+  typedef Region *(*RegionCreator)(LAMMPS *,int,char**);
+  typedef std::map<std::string,RegionCreator> RegionCreatorMap;
+  RegionCreatorMap *region_map;
+
   Domain(class LAMMPS *);
   virtual ~Domain();
   virtual void init();
   void set_initial_box(int expandflag=1);
   virtual void set_global_box();
   virtual void set_lamda_box();
   virtual void set_local_box();
   virtual void reset_box();
   virtual void pbc();
   void image_check();
   void box_too_small_check();
   void subbox_too_small_check(double);
   void minimum_image(double &, double &, double &);
   void minimum_image(double *);
   int closest_image(int, int);
   void closest_image(const double * const, const double * const,
                      double * const);
   void remap(double *, imageint &);
   void remap(double *);
   void remap_near(double *, double *);
   void unmap(double *, imageint);
   void unmap(double *, imageint, double *);
   void image_flip(int, int, int);
   void set_lattice(int, char **);
   void add_region(int, char **);
   void delete_region(int, char **);
   int find_region(char *);
   void set_boundary(int, char **, int);
   void set_box(int, char **);
   void print_box(const char *);
   void boundary_string(char *);
 
   virtual void lamda2x(int);
   virtual void x2lamda(int);
   virtual void lamda2x(double *, double *);
   virtual void x2lamda(double *, double *);
   int inside(double *);
   int inside_nonperiodic(double *);
   void x2lamda(double *, double *, double *, double *);
   void bbox(double *, double *, double *, double *);
   void box_corners();
   void subbox_corners();
   void lamda_box_corners(double *, double *);
 
   // minimum image convention check
   // return 1 if any distance > 1/2 of box size
   // indicates a special neighbor is actually not in a bond,
   //   but is a far-away image that should be treated as an unbonded neighbor
   // inline since called from neighbor build inner loop
   //
   inline int minimum_image_check(double dx, double dy, double dz) {
     if (xperiodic && fabs(dx) > xprd_half) return 1;
     if (yperiodic && fabs(dy) > yprd_half) return 1;
     if (zperiodic && fabs(dz) > zprd_half) return 1;
     return 0;
   }
 
  protected:
   double small[3];                  // fractions of box lengths
+
+ private:
+  template <typename T> static Region *region_creator(LAMMPS *,int,char**);
 };
 
 }
 
 #endif
 
 /* ERROR/WARNING messages:
 
 E: Box bounds are invalid
 
 The box boundaries specified in the read_data file are invalid.  The
 lo value must be less than the hi value for all 3 dimensions.
 
 E: Cannot skew triclinic box in z for 2d simulation
 
 Self-explanatory.
 
 E: Triclinic box skew is too large
 
 The displacement in a skewed direction must be less than half the box
 length in that dimension.  E.g. the xy tilt must be between -half and
 +half of the x box length.  This constraint can be relaxed by using
 the box tilt command.
 
 W: Triclinic box skew is large
 
 The displacement in a skewed direction is normally required to be less
 than half the box length in that dimension.  E.g. the xy tilt must be
 between -half and +half of the x box length.  You have relaxed the
 constraint using the box tilt command, but the warning means that a
 LAMMPS simulation may be inefficient as a result.
 
 E: Illegal simulation box
 
 The lower bound of the simulation box is greater than the upper bound.
 
 E: Bond atom missing in image check
 
 The 2nd atom in a particular bond is missing on this processor.
 Typically this is because the pairwise cutoff is set too short or the
 bond has blown apart and an atom is too far away.
 
 W: Inconsistent image flags
 
 The image flags for a pair on bonded atoms appear to be inconsistent.
 Inconsistent means that when the coordinates of the two atoms are
 unwrapped using the image flags, the two atoms are far apart.
 Specifically they are further apart than half a periodic box length.
 Or they are more than a box length apart in a non-periodic dimension.
 This is usually due to the initial data file not having correct image
 flags for the 2 atoms in a bond that straddles a periodic boundary.
 They should be different by 1 in that case.  This is a warning because
 inconsistent image flags will not cause problems for dynamics or most
 LAMMPS simulations.  However they can cause problems when such atoms
 are used with the fix rigid or replicate commands.
 
 W: Bond atom missing in image check
 
 The 2nd atom in a particular bond is missing on this processor.
 Typically this is because the pairwise cutoff is set too short or the
 bond has blown apart and an atom is too far away.
 
 E: Bond atom missing in box size check
 
 The 2nd atoms needed to compute a particular bond is missing on this
 processor.  Typically this is because the pairwise cutoff is set too
 short or the bond has blown apart and an atom is too far away.
 
 W: Bond atom missing in box size check
 
 The 2nd atoms needed to compute a particular bond is missing on this
 processor.  Typically this is because the pairwise cutoff is set too
 short or the bond has blown apart and an atom is too far away.
 
 W: Bond/angle/dihedral extent > half of periodic box length
 
 This is a restriction because LAMMPS can be confused about which image
 of an atom in the bonded interaction is the correct one to use.
 "Extent" in this context means the maximum end-to-end length of the
 bond/angle/dihedral.  LAMMPS computes this by taking the maximum bond
 length, multiplying by the number of bonds in the interaction (e.g. 3
 for a dihedral) and adding a small amount of stretch.
 
 W: Proc sub-domain size < neighbor skin, could lead to lost atoms
 
 The decomposition of the physical domain (likely due to load
 balancing) has led to a processor's sub-domain being smaller than the
 neighbor skin in one or more dimensions.  Since reneighboring is
 triggered by atoms moving the skin distance, this may lead to lost
 atoms, if an atom moves all the way across a neighboring processor's
 sub-domain before reneighboring is triggered.
 
 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: Reuse of region ID
 
 A region ID cannot be used twice.
 
 E: Unknown region style
 
 The choice of region style is unknown.
 
 E: Delete region ID does not exist
 
 Self-explanatory.
 
 E: Both sides of boundary must be periodic
 
 Cannot specify a boundary as periodic only on the lo or hi side.  Must
 be periodic on both sides.
 
 */
diff --git a/src/info.cpp b/src/info.cpp
index 22cb432ac..c150e0a88 100644
--- a/src/info.cpp
+++ b/src/info.cpp
@@ -1,1018 +1,1019 @@
 /* ----------------------------------------------------------------------
   fputs
    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:  Axel Kohlmeyer (Temple U),
                           Richard Berger (Temple U)
 ------------------------------------------------------------------------- */
 
 #include <string.h>
 #include "info.h"
 #include "accelerator_kokkos.h"
 #include "atom.h"
 #include "comm.h"
 #include "compute.h"
 #include "domain.h"
 #include "dump.h"
 #include "fix.h"
 #include "force.h"
 #include "pair.h"
 #include "pair_hybrid.h"
 #include "group.h"
 #include "input.h"
 #include "modify.h"
 #include "neighbor.h"
 #include "output.h"
 #include "region.h"
 #include "universe.h"
 #include "variable.h"
 #include "update.h"
 #include "error.h"
 
 #include <time.h>
 #include <vector>
 #include <string>
 #include <algorithm>
 
 #ifdef _WIN32
 #define PSAPI_VERSION=1
 #include <windows.h>
 #include <stdint.h>
 #include <psapi.h>
 #else
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <sys/utsname.h>
 #endif
 
 #if defined __linux
 #include <malloc.h>
 #endif
 
 namespace LAMMPS_NS {
 // same as in variable.cpp
 enum {INDEX,LOOP,WORLD,UNIVERSE,ULOOP,STRING,GETENV,
       SCALARFILE,ATOMFILE,FORMAT,EQUAL,ATOM,PYTHON};
 
 enum {COMPUTES=1<<0,
       DUMPS=1<<1,
       FIXES=1<<2,
       GROUPS=1<<3,
       REGIONS=1<<4,
       CONFIG=1<<5,
       TIME=1<<6,
       VARIABLES=1<<7,
       SYSTEM=1<<8,
       COMM=1<<9,
       ATOM_STYLES=1<<10,
       INTEGRATE_STYLES=1<<11,
       MINIMIZE_STYLES=1<<12,
       PAIR_STYLES=1<<13,
       BOND_STYLES=1<<14,
       ANGLE_STYLES=1<<15,
       DIHEDRAL_STYLES=1<<16,
       IMPROPER_STYLES=1<<17,
       KSPACE_STYLES=1<<18,
       FIX_STYLES=1<<19,
       COMPUTE_STYLES=1<<20,
       REGION_STYLES=1<<21,
       DUMP_STYLES=1<<22,
       COMMAND_STYLES=1<<23,
       ALL=~0};
 
 static const int STYLES = ATOM_STYLES | INTEGRATE_STYLES | MINIMIZE_STYLES | PAIR_STYLES | BOND_STYLES | \
                          ANGLE_STYLES | DIHEDRAL_STYLES | IMPROPER_STYLES | KSPACE_STYLES | FIX_STYLES | \
                          COMPUTE_STYLES | REGION_STYLES | DUMP_STYLES | COMMAND_STYLES;
 
 }
 
 
 static const char *varstyles[] = {
   "index", "loop", "world", "universe", "uloop", "string", "getenv",
   "file", "atomfile", "format", "equal", "atom", "python", "(unknown)"};
 
 static const char *mapstyles[] = { "none", "array", "hash" };
 
 static const char *commstyles[] = { "brick", "tiled" };
 static const char *commlayout[] = { "uniform", "nonuniform", "irregular" };
 
 static const char bstyles[] = "pfsm";
 
 using namespace LAMMPS_NS;
 using namespace std;
 
 static void print_columns(FILE* fp, vector<string> & styles);
 
 /* ---------------------------------------------------------------------- */
 
 void Info::command(int narg, char **arg)
 {
   FILE *out=screen;
   int flags=0;
 
   if (comm->me != 0) return;
 
   // parse arguments
   int idx = 0;
 
   while (idx < narg) {
     if (strncmp(arg[idx],"all",3) == 0) {
       flags |= ALL;
       ++idx;
     } else if ((idx+1 < narg) && (strncmp(arg[idx],"out",3) == 0)
                && (strncmp(arg[idx+1],"screen",3) == 0)) {
       if ((out != screen) && (out != logfile)) fclose(out);
       out = screen;
       idx += 2;
     } else if ((idx+1 < narg) && (strncmp(arg[idx],"out",3) == 0)
                && (strncmp(arg[idx+1],"log",3) == 0)) {
       if ((out != screen) && (out != logfile)) fclose(out);
       out = logfile;
       idx += 2;
     } else if ((idx+2 < narg) && (strncmp(arg[idx],"out",3) == 0)
                && (strncmp(arg[idx+1],"append",3) == 0)) {
       if ((out != screen) && (out != logfile)) fclose(out);
       out = fopen(arg[idx+2],"a");
       idx += 3;
     } else if ((idx+2 < narg) && (strncmp(arg[idx],"out",3) == 0)
                && (strncmp(arg[idx+1],"overwrite",3) == 0)) {
       if ((out != screen) && (out != logfile)) fclose(out);
       out = fopen(arg[idx+2],"w");
       idx += 3;
     } else if (strncmp(arg[idx],"atom_styles",3) == 0) {
       flags |= ATOM_STYLES;
       ++idx;
     } else if (strncmp(arg[idx],"integrate_styles",3) == 0) {
       flags |= INTEGRATE_STYLES;
       ++idx;
     } else if (strncmp(arg[idx],"minimize_styles",3) == 0) {
       flags |= MINIMIZE_STYLES;
       ++idx;
     } else if (strncmp(arg[idx],"pair_styles",3) == 0) {
       flags |= PAIR_STYLES;
       ++idx;
     } else if (strncmp(arg[idx],"bond_styles",3) == 0) {
       flags |= BOND_STYLES;
       ++idx;
     } else if (strncmp(arg[idx],"angle_styles",3) == 0) {
       flags |= ANGLE_STYLES;
       ++idx;
     } else if (strncmp(arg[idx],"dihedral_styles",3) == 0) {
       flags |= DIHEDRAL_STYLES;
       ++idx;
     } else if (strncmp(arg[idx],"improper_styles",3) == 0) {
       flags |= IMPROPER_STYLES;
       ++idx;
     } else if (strncmp(arg[idx],"kspace_styles",3) == 0) {
       flags |= KSPACE_STYLES;
       ++idx;
     } else if (strncmp(arg[idx],"fix_styles",3) == 0) {
       flags |= FIX_STYLES;
       ++idx;
     } else if (strncmp(arg[idx],"compute_styles",5) == 0) {
       flags |= COMPUTE_STYLES;
       ++idx;
     } else if (strncmp(arg[idx],"region_styles",3) == 0) {
       flags |= REGION_STYLES;
       ++idx;
     } else if (strncmp(arg[idx],"dump_styles",3) == 0) {
       flags |= DUMP_STYLES;
       ++idx;
     } else if (strncmp(arg[idx],"command_styles",5) == 0) {
       flags |= COMMAND_STYLES;
       ++idx;
     } else if (strncmp(arg[idx],"communication",5) == 0) {
       flags |= COMM;
       ++idx;
     } else if (strncmp(arg[idx],"computes",5) == 0) {
       flags |= COMPUTES;
       ++idx;
     } else if (strncmp(arg[idx],"dumps",5) == 0) {
       flags |= DUMPS;
       ++idx;
     } else if (strncmp(arg[idx],"fixes",5) == 0) {
       flags |= FIXES;
       ++idx;
     } else if (strncmp(arg[idx],"groups",3) == 0) {
       flags |= GROUPS;
       ++idx;
     } else if (strncmp(arg[idx],"regions",3) == 0) {
       flags |= REGIONS;
       ++idx;
     } else if (strncmp(arg[idx],"config",3) == 0) {
       flags |= CONFIG;
       ++idx;
     } else if (strncmp(arg[idx],"time",3) == 0) {
       flags |= TIME;
       ++idx;
     } else if (strncmp(arg[idx],"variables",3) == 0) {
       flags |= VARIABLES;
       ++idx;
     } else if (strncmp(arg[idx],"system",3) == 0) {
       flags |= SYSTEM;
       ++idx;
     } else if (strncmp(arg[idx],"styles",3) == 0) {
       flags |= STYLES;
       ++idx;
     } else {
       error->warning(FLERR,"Ignoring unknown or incorrect info command flag");
       ++idx;
     }
   }
 
   if (out == NULL) return;
 
   fputs("\nInfo-Info-Info-Info-Info-Info-Info-Info-Info-Info-Info\n",out);
   time_t now = time(NULL);
   fprintf(out,"Printed on %s\n",ctime(&now));
 
   if (flags & CONFIG) {
 
     fprintf(out,"\nLAMMPS version: %s / %s\n",
             universe->version, universe->num_ver);
     fprintf(out,"sizeof(smallint): %3d-bit\n",(int)sizeof(smallint)*8);
     fprintf(out,"sizeof(imageint): %3d-bit\n",(int)sizeof(imageint)*8);
     fprintf(out,"sizeof(tagint):   %3d-bit\n",(int)sizeof(tagint)*8);
     fprintf(out,"sizeof(bigint):   %3d-bit\n",(int)sizeof(bigint)*8);
 
 #if defined(_WIN32)
     DWORD fullversion,majorv,minorv,buildv=0;
 
     fullversion = GetVersion();
     majorv = (DWORD) (LOBYTE(LOWORD(fullversion)));
     minorv = (DWORD) (HIBYTE(LOWORD(fullversion)));
     if (fullversion < 0x80000000)
       buildv = (DWORD) (HIWORD(fullversion));
 
     SYSTEM_INFO si;
     GetSystemInfo(&si);
 
     const char *machine;
     switch (si.wProcessorArchitecture) {
     case PROCESSOR_ARCHITECTURE_AMD64:
       machine = (const char *) "x86_64";
       break;
     case PROCESSOR_ARCHITECTURE_ARM:
       machine = (const char *) "arm";
       break;
     case PROCESSOR_ARCHITECTURE_IA64:
       machine = (const char *) "ia64";
       break;
     case PROCESSOR_ARCHITECTURE_INTEL:
       machine = (const char *) "i386";
       break;
     default:
       machine = (const char *) "(unknown)";
     }
     fprintf(out,"\nOS information: Windows %d.%d (%d) on %s\n",
             majorv,minorv,buildv,machine);
 #else
     struct utsname ut;
     uname(&ut);
     fprintf(out,"\nOS information: %s %s on %s\n",
             ut.sysname, ut.release, ut.machine);
 #endif
 
     fprintf(out,"\nMemory allocation information (MPI rank 0)\n");
 
 #if defined(_WIN32)
     HANDLE phandle = GetCurrentProcess();
     PROCESS_MEMORY_COUNTERS_EX pmc;
     GetProcessMemoryInfo(phandle,(PROCESS_MEMORY_COUNTERS *)&pmc,sizeof(pmc));
     fprintf(out,"Non-shared memory use: %.3g Mbyte\n",
             (double)pmc.PrivateUsage/1048576.0);
     fprintf(out,"Maximum working set size: %.3g Mbyte\n",
             (double)pmc.PeakWorkingSetSize/1048576.0);
 #else
 #if defined(__linux)
     struct mallinfo mi;
     mi = mallinfo();
     fprintf(out,"Total dynamically allocated memory: %.3g Mbyte\n",
             (double)mi.uordblks/1048576.0);
 #endif
     struct rusage ru;
     if (getrusage(RUSAGE_SELF, &ru) == 0) {
       fprintf(out,"Maximum resident set size: %.3g Mbyte\n",
               (double)ru.ru_maxrss/1024.0);
     }
 #endif
   }
 
   if (flags & COMM) {
     int major,minor;
     MPI_Get_version(&major,&minor);
 
     fprintf(out,"\nCommunication information:\n");
     fprintf(out,"MPI library level: MPI v%d.%d\n",major,minor);
     fprintf(out,"Comm style = %s,  Comm layout = %s\n",
             commstyles[comm->style], commlayout[comm->layout]);
     fprintf(out,"Communicate velocities for ghost atoms = %s\n",
             comm->ghost_velocity ? "yes" : "no");
 
     if (comm->mode == 0) {
       fprintf(out,"Communication mode = single\n");
       fprintf(out,"Communication cutoff = %g\n",
               MAX(comm->cutghostuser,neighbor->cutneighmax));
     }
 
     if (comm->mode == 1) {
       fprintf(out,"Communication mode = multi\n");
       double cut;
       for (int i=1; i <= atom->ntypes && neighbor->cuttype; ++i) {
         cut = neighbor->cuttype[i];
         if (comm->cutusermulti) cut = MAX(cut,comm->cutusermulti[i]);
         fprintf(out,"Communication cutoff for type %d = %g\n", i, cut);
       }
     }
     fprintf(out,"Nprocs = %d,   Nthreads = %d\n",
             comm->nprocs, comm->nthreads);
     if (domain->box_exist)
       fprintf(out,"Processor grid = %d x %d x %d\n",comm->procgrid[0],
             comm->procgrid[1], comm->procgrid[2]);
   }
 
   if (flags & SYSTEM) {
     fprintf(out,"\nSystem information:\n");
     fprintf(out,"Units      = %s\n",update->unit_style);
     fprintf(out,"Atom style = %s\n", atom->atom_style);
     fprintf(out,"Atom map   = %s\n", mapstyles[atom->map_style]);
     if (atom->molecular > 0) {
       const char *msg;
       msg = (atom->molecular == 2) ? "template" : "standard";
       fprintf(out,"Molecule type = %s\n",msg);
     }
     fprintf(out,"Atoms = " BIGINT_FORMAT ",  types = %d,  style = %s\n",
             atom->natoms, atom->ntypes, force->pair_style);
 
     if (force->pair && strstr(force->pair_style,"hybrid")) {
       PairHybrid *hybrid = (PairHybrid *)force->pair;
       fprintf(out,"Hybrid sub-styles:");
       for (int i=0; i < hybrid->nstyles; ++i)
         fprintf(out," %s", hybrid->keywords[i]);
       fputc('\n',out);
     }
     if (atom->molecular > 0) {
       const char *msg;
       msg = force->bond_style ? force->bond_style : "none";
       fprintf(out,"Bonds = " BIGINT_FORMAT ",  types = %d,  style = %s\n",
               atom->nbonds, atom->nbondtypes, msg);
 
       msg = force->angle_style ? force->angle_style : "none";
       fprintf(out,"Angles = " BIGINT_FORMAT ",  types = %d,  style = %s\n",
               atom->nangles, atom->nangletypes, msg);
 
       msg = force->dihedral_style ? force->dihedral_style : "none";
       fprintf(out,"Dihedrals = " BIGINT_FORMAT ",  types = %d,  style = %s\n",
               atom->ndihedrals, atom->ndihedraltypes, msg);
 
       msg = force->improper_style ? force->improper_style : "none";
       fprintf(out,"Impropers = " BIGINT_FORMAT ",  types = %d,  style = %s\n",
               atom->nimpropers, atom->nimpropertypes, msg);
 
       const double * const special_lj   = force->special_lj;
       const double * const special_coul = force->special_coul;
 
       fprintf(out,"Special bond factors lj =   %-10g %-10g %-10g\n"
               "Special bond factors coul = %-10g %-10g %-10g\n",
               special_lj[1],special_lj[2],special_lj[3],
               special_coul[1],special_coul[2],special_coul[3]);
     }
 
     fprintf(out,"Kspace style = %s\n",
             force->kspace ? force->kspace_style : "none");
 
     if (domain->box_exist) {
       fprintf(out,"\nDimensions = %d\n",domain->dimension);
       fprintf(out,"%s box = %g x %g x %g\n",
               domain->triclinic ? "Triclinic" : "Orthogonal",
               domain->xprd, domain->yprd, domain->zprd);
       fprintf(out,"Boundaries = %c,%c %c,%c %c,%c\n",
               bstyles[domain->boundary[0][0]],bstyles[domain->boundary[0][1]],
               bstyles[domain->boundary[1][0]],bstyles[domain->boundary[1][1]],
               bstyles[domain->boundary[2][0]],bstyles[domain->boundary[2][1]]);
       fprintf(out,"xlo, xhi = %g, %g\n", domain->boxlo[0], domain->boxhi[0]);
       fprintf(out,"ylo, yhi = %g, %g\n", domain->boxlo[1], domain->boxhi[1]);
       fprintf(out,"zlo, zhi = %g, %g\n", domain->boxlo[2], domain->boxhi[2]);
       if (domain->triclinic)
           fprintf(out,"Xy, xz, yz = %g, %g, %g\n",
                   domain->xy, domain->xz, domain->yz);
     } else {
       fputs("\nBox has not yet been created\n",out);
     }
   }
 
   if (flags & GROUPS) {
     int ngroup = group->ngroup;
     char **names = group->names;
     int *dynamic = group->dynamic;
     fprintf(out,"\nGroup information:\n");
     for (int i=0; i < ngroup; ++i) {
       if (names[i])
         fprintf(out,"Group[%2d]: %s (%s)\n",
                 i, names[i], dynamic[i] ? "dynamic" : "static");
     }
   }
 
   if (flags & REGIONS) {
     int nreg = domain->nregion;
     Region **regs = domain->regions;
     fprintf(out,"\nRegion information:\n");
     for (int i=0; i < nreg; ++i) {
       fprintf(out,"Region[%3d]: %s,  style = %s,  side = %s\n",
               i, regs[i]->id, regs[i]->style,
               regs[i]->interior ? "in" : "out");
     }
   }
 
   if (flags & COMPUTES) {
     int ncompute = modify->ncompute;
     Compute **compute = modify->compute;
     char **names = group->names;
     fprintf(out,"\nCompute information:\n");
     for (int i=0; i < ncompute; ++i) {
       fprintf(out,"Compute[%3d]: %s,  style = %s,  group = %s\n",
               i, compute[i]->id, compute[i]->style,
               names[compute[i]->igroup]);
     }
   }
 
   if (flags & DUMPS) {
     int ndump = output->ndump;
     Dump **dump = output->dump;
     int *nevery = output->every_dump;           \
     char **vnames = output->var_dump;
     char **names = group->names;
     fprintf(out,"\nDump information:\n");
     for (int i=0; i < ndump; ++i) {
       fprintf(out,"Dump[%3d]: %s,  file = %s,  style = %s,  group = %s,  ",
               i, dump[i]->id, dump[i]->filename,
               dump[i]->style, names[dump[i]->igroup]);
       if (nevery[i]) {
         fprintf(out,"every = %d\n", nevery[i]);
       } else {
         fprintf(out,"every = %s\n", vnames[i]);
       }
     }
   }
 
   if (flags & FIXES) {
     int nfix = modify->nfix;
     Fix **fix = modify->fix;
     char **names = group->names;
     fprintf(out,"\nFix information:\n");
     for (int i=0; i < nfix; ++i) {
       fprintf(out,"Fix[%3d]: %s,  style = %s,  group = %s\n",
               i, fix[i]->id, fix[i]->style, names[fix[i]->igroup]);
     }
   }
 
   if (flags & VARIABLES) {
     int nvar = input->variable->nvar;
     int *style = input->variable->style;
     char **names = input->variable->names;
     char ***data = input->variable->data;
     fprintf(out,"\nVariable information:\n");
     for (int i=0; i < nvar; ++i) {
       int ndata = 1;
       fprintf(out,"Variable[%3d]: %-10s,  style = %-10s,  def =",
               i,names[i],varstyles[style[i]]);
       if ((style[i] != LOOP) && (style[i] != ULOOP))
         ndata = input->variable->num[i];
       for (int j=0; j < ndata; ++j)
         fprintf(out," %s",data[i][j]);
       fputs("\n",out);
     }
   }
 
   if (flags & TIME) {
     double wallclock = MPI_Wtime() - lmp->initclock;
     double cpuclock = 0.0;
 
 #if defined(_WIN32)
     // from MSD docs.
     FILETIME ct,et,kt,ut;
     union { FILETIME ft; uint64_t ui; } cpu;
     if (GetProcessTimes(GetCurrentProcess(),&ct,&et,&kt,&ut)) {
       cpu.ft = ut;
       cpuclock = cpu.ui * 0.0000001;
     }
 #else /* POSIX */
     struct rusage ru;
     if (getrusage(RUSAGE_SELF, &ru) == 0) {
       cpuclock  = (double) ru.ru_utime.tv_sec;
       cpuclock += (double) ru.ru_utime.tv_usec * 0.000001;
     }
 #endif /* ! _WIN32 */
 
     int cpuh,cpum,cpus,wallh,wallm,walls;
     cpus = fmod(cpuclock,60.0);
     cpuclock = (cpuclock - cpus) / 60.0;
     cpum = fmod(cpuclock,60.0);
     cpuh = (cpuclock - cpum) / 60.0;
     walls = fmod(wallclock,60.0);
     wallclock = (wallclock - walls) / 60.0;
     wallm = fmod(wallclock,60.0);
     wallh = (wallclock - wallm) / 60.0;
     fprintf(out,"\nTotal time information (MPI rank 0):\n"
             "  CPU time: %4d:%02d:%02d\n"
             " Wall time: %4d:%02d:%02d\n",
             cpuh,cpum,cpus,wallh,wallm,walls);
   }
 
   if (flags & STYLES) {
     available_styles(out, flags);
   }
 
   fputs("\nInfo-Info-Info-Info-Info-Info-Info-Info-Info-Info-Info\n\n",out);
 
   // close output file pointer if opened locally thus forcing a hard sync.
   if ((out != screen) && (out != logfile))
     fclose(out);
 }
 
 
 void Info::available_styles(FILE * out, int flags)
 {
 
   fprintf(out,"\nStyles information:\n");
 
   if(flags & ATOM_STYLES)      atom_styles(out);
   if(flags & INTEGRATE_STYLES) integrate_styles(out);
   if(flags & MINIMIZE_STYLES)  minimize_styles(out);
   if(flags & PAIR_STYLES)      pair_styles(out);
   if(flags & BOND_STYLES)      bond_styles(out);
   if(flags & ANGLE_STYLES)     angle_styles(out);
   if(flags & DIHEDRAL_STYLES)  dihedral_styles(out);
   if(flags & IMPROPER_STYLES)  improper_styles(out);
   if(flags & KSPACE_STYLES)    kspace_styles(out);
   if(flags & FIX_STYLES)       fix_styles(out);
   if(flags & COMPUTE_STYLES)   compute_styles(out);
   if(flags & REGION_STYLES)    region_styles(out);
   if(flags & DUMP_STYLES)      dump_styles(out);
   if(flags & COMMAND_STYLES)   command_styles(out);
 }
 
 void Info::atom_styles(FILE * out)
 {
   fprintf(out, "\nAtom styles:\n");
 
   vector<string> styles;
 
   for(Atom::AtomVecCreatorMap::iterator it = atom->avec_map->begin(); it != atom->avec_map->end(); ++it) {
     styles.push_back(it->first);
   }
 
   print_columns(out, styles);
   fprintf(out, "\n\n\n");
 }
 
 void Info::integrate_styles(FILE * out)
 {
   fprintf(out, "\nIntegrate styles:\n");
 
   vector<string> styles;
 
   for(Update::IntegrateCreatorMap::iterator it = update->integrate_map->begin(); it != update->integrate_map->end(); ++it) {
     styles.push_back(it->first);
   }
 
   print_columns(out, styles);
   fprintf(out, "\n\n\n");
 }
 
 void Info::minimize_styles(FILE * out)
 {
   fprintf(out, "\nMinimize styles:\n");
 
   vector<string> styles;
 
   for(Update::MinimizeCreatorMap::iterator it = update->minimize_map->begin(); it != update->minimize_map->end(); ++it) {
     styles.push_back(it->first);
   }
 
   print_columns(out, styles);
   fprintf(out, "\n\n\n");
 }
 
 void Info::pair_styles(FILE * out)
 {
   fprintf(out, "\nPair styles:\n");
 
   vector<string> styles;
 
   for(Force::PairCreatorMap::iterator it = force->pair_map->begin(); it != force->pair_map->end(); ++it) {
     styles.push_back(it->first);
   }
 
   print_columns(out, styles);
   fprintf(out, "\n\n\n");
 }
 
 void Info::bond_styles(FILE * out)
 {
   fprintf(out, "\nBond styles:\n");
 
   vector<string> styles;
 
   for(Force::BondCreatorMap::iterator it = force->bond_map->begin(); it != force->bond_map->end(); ++it) {
     styles.push_back(it->first);
   }
 
   print_columns(out, styles);
   fprintf(out, "\n\n\n");
 }
 
 void Info::angle_styles(FILE * out)
 {
   fprintf(out, "\nAngle styles:\n");
 
   vector<string> styles;
 
   for(Force::AngleCreatorMap::iterator it = force->angle_map->begin(); it != force->angle_map->end(); ++it) {
     styles.push_back(it->first);
   }
 
   print_columns(out, styles);
   fprintf(out, "\n\n\n");
 }
 
 void Info::dihedral_styles(FILE * out)
 {
   fprintf(out, "\nDihedral styles:\n");
 
   vector<string> styles;
 
   for(Force::DihedralCreatorMap::iterator it = force->dihedral_map->begin(); it != force->dihedral_map->end(); ++it) {
     styles.push_back(it->first);
   }
 
   print_columns(out, styles);
   fprintf(out, "\n\n\n");
 }
 
 void Info::improper_styles(FILE * out)
 {
   fprintf(out, "\nImproper styles:\n");
 
   vector<string> styles;
 
   for(Force::ImproperCreatorMap::iterator it = force->improper_map->begin(); it != force->improper_map->end(); ++it) {
     styles.push_back(it->first);
   }
 
   print_columns(out, styles);
   fprintf(out, "\n\n\n");
 }
 
 void Info::kspace_styles(FILE * out)
 {
   fprintf(out, "\nKSpace styles:\n");
 
   vector<string> styles;
 
   for(Force::KSpaceCreatorMap::iterator it = force->kspace_map->begin(); it != force->kspace_map->end(); ++it) {
     styles.push_back(it->first);
   }
 
   print_columns(out, styles);
   fprintf(out, "\n\n\n");
 }
 
 void Info::fix_styles(FILE * out)
 {
   fprintf(out, "\nFix styles:\n");
 
   vector<string> styles;
 
   for(Modify::FixCreatorMap::iterator it = modify->fix_map->begin(); it != modify->fix_map->end(); ++it) {
     styles.push_back(it->first);
   }
 
   print_columns(out, styles);
   fprintf(out, "\n\n\n");
 }
 
 void Info::compute_styles(FILE * out)
 {
   fprintf(out, "\nCompute styles:\n");
 
   vector<string> styles;
 
   for(Modify::ComputeCreatorMap::iterator it = modify->compute_map->begin(); it != modify->compute_map->end(); ++it) {
     styles.push_back(it->first);
   }
 
   print_columns(out, styles);
   fprintf(out, "\n\n\n");
 }
 
 void Info::region_styles(FILE * out)
 {
   fprintf(out, "\nRegion styles:\n");
 
   vector<string> styles;
-#define REGION_CLASS
-#define RegionStyle(key,Class) styles.push_back(#key);
-#include "style_region.h"
-#undef REGION_CLASS
+
+  for(Domain::RegionCreatorMap::iterator it = domain->region_map->begin(); it != domain->region_map->end(); ++it) {
+    styles.push_back(it->first);
+  }
+
   print_columns(out, styles);
   fprintf(out, "\n\n\n");
 }
 
 void Info::dump_styles(FILE * out)
 {
   fprintf(out, "\nDump styles:\n");
 
   vector<string> styles;
 
   for(Output::DumpCreatorMap::iterator it = output->dump_map->begin(); it != output->dump_map->end(); ++it) {
     styles.push_back(it->first);
   }
 
   print_columns(out, styles);
   fprintf(out, "\n\n\n");
 }
 
 void Info::command_styles(FILE * out)
 {
   fprintf(out, "\nCommand styles (add-on input script commands):\n");
 
   vector<string> styles;
 
   for(Input::CommandCreatorMap::iterator it = input->command_map->begin(); it != input->command_map->end(); ++it) {
     styles.push_back(it->first);
   }
 
   print_columns(out, styles);
   fprintf(out, "\n\n\n");
 }
 
 
 /* ---------------------------------------------------------------------- */
 
 // the is_active() function returns true if the selected style or name
 // in the selected category is currently in use.
 
 bool Info::is_active(const char *category, const char *name)
 {
   if ((category == NULL) || (name == NULL)) return false;
   const char *style = "none";
   const int len = strlen(name);
 
   if (strcmp(category,"package") == 0) {
     if (strcmp(name,"gpu") == 0) {
       return (modify->find_fix("package_gpu") >= 0) ? true : false;
     } else if (strcmp(name,"intel") == 0) {
       return (modify->find_fix("package_intel") >= 0) ? true : false;
     } else if (strcmp(name,"kokkos") == 0) {
       return (lmp->kokkos && lmp->kokkos->kokkos_exists) ? true : false;
     } else if (strcmp(name,"omp") == 0) {
       return (modify->find_fix("package_omp") >= 0) ? true : false;
     } else error->all(FLERR,"Unknown name for info package category");
 
   } else if (strcmp(category,"newton") == 0) {
     if (strcmp(name,"pair") == 0) return (force->newton_pair != 0);
     else if (strcmp(name,"bond") == 0) return (force->newton_bond != 0);
     else if (strcmp(name,"any") == 0) return (force->newton != 0);
     else error->all(FLERR,"Unknown name for info newton category");
 
   } else if (strcmp(category,"pair") == 0) {
     if (force->pair == NULL) return false;
     if (strcmp(name,"single") == 0) return (force->pair->single_enable != 0);
     else if (strcmp(name,"respa") == 0) return (force->pair->respa_enable != 0);
     else if (strcmp(name,"manybody") == 0) return (force->pair->manybody_flag != 0);
     else if (strcmp(name,"tail") == 0) return (force->pair->tail_flag != 0);
     else if (strcmp(name,"shift") == 0) return (force->pair->offset_flag != 0);
     else error->all(FLERR,"Unknown name for info pair category");
 
   } else if (strcmp(category,"comm_style") == 0) {
     style = commstyles[comm->style];
   } else if (strcmp(category,"min_style") == 0) {
     style = update->minimize_style;
   } else if (strcmp(category,"run_style") == 0) {
     style = update->integrate_style;
   } else if (strcmp(category,"atom_style") == 0) {
     style = atom->atom_style;
   } else if (strcmp(category,"pair_style") == 0) {
     style = force->pair_style;
   } else if (strcmp(category,"bond_style") == 0) {
     style = force->bond_style;
   } else if (strcmp(category,"angle_style") == 0) {
     style = force->angle_style;
   } else if (strcmp(category,"dihedral_style") == 0) {
     style = force->dihedral_style;
   } else if (strcmp(category,"improper_style") == 0) {
     style = force->improper_style;
   } else if (strcmp(category,"kspace_style") == 0) {
     style = force->kspace_style;
   } else error->all(FLERR,"Unknown category for info is_active()");
 
   int match = 0;
   if (strcmp(style,name) == 0) match = 1;
 
   if (!match && lmp->suffix_enable) {
     if (lmp->suffix) {
       char *name_w_suffix = new char [len + 2 + strlen(lmp->suffix)];
       sprintf(name_w_suffix,"%s/%s",name,lmp->suffix);
       if (strcmp(style,name_w_suffix) == 0) match = 1;
       delete[] name_w_suffix;
     }
     if (!match && lmp->suffix2) {
       char *name_w_suffix = new char [len + 2 + strlen(lmp->suffix2)];
       sprintf(name_w_suffix,"%s/%s",name,lmp->suffix2);
       if (strcmp(style,name_w_suffix) == 0) match = 1;
       delete[] name_w_suffix;
     }
   }
   return match ? true : false;
 }
 
 /* ---------------------------------------------------------------------- */
 
 // the is_available() function returns true if the selected style
 // or name in the selected category is available for use (but need
 // not be currently active).
 
 bool Info::is_available(const char *category, const char *name)
 {
   if ((category == NULL) || (name == NULL)) return false;
   const int len = strlen(name);
   int match = 0;
 
   if (strcmp(category,"command") == 0) {
     if (input->command_map->find(name) != input->command_map->end())
       match = 1;
 
   } else if (strcmp(category,"compute") == 0) {
     if (modify->compute_map->find(name) != modify->compute_map->end())
       match = 1;
 
     if (!match && lmp->suffix_enable) {
       if (lmp->suffix) {
         char *name_w_suffix = new char [len + 2 + strlen(lmp->suffix)];
         sprintf(name_w_suffix,"%s/%s",name,lmp->suffix);
         if (modify->compute_map->find(name_w_suffix) != modify->compute_map->end())
           match = 1;
         delete[] name_w_suffix;
       }
       if (!match && lmp->suffix2) {
         char *name_w_suffix = new char [len + 2 + strlen(lmp->suffix2)];
         sprintf(name_w_suffix,"%s/%s",name,lmp->suffix2);
         if (modify->compute_map->find(name_w_suffix) != modify->compute_map->end())
           match = 1;
         delete[] name_w_suffix;
       }
     }
   } else if (strcmp(category,"fix") == 0) {
     if (modify->fix_map->find(name) != modify->fix_map->end())
       match = 1;
 
     if (!match && lmp->suffix_enable) {
       if (lmp->suffix) {
         char *name_w_suffix = new char [len + 2 + strlen(lmp->suffix)];
         sprintf(name_w_suffix,"%s/%s",name,lmp->suffix);
         if (modify->fix_map->find(name_w_suffix) != modify->fix_map->end())
           match = 1;
         delete[] name_w_suffix;
       }
       if (!match && lmp->suffix2) {
         char *name_w_suffix = new char [len + 2 + strlen(lmp->suffix2)];
         sprintf(name_w_suffix,"%s/%s",name,lmp->suffix2);
         if (modify->fix_map->find(name_w_suffix) != modify->fix_map->end())
           match = 1;
         delete[] name_w_suffix;
       }
     }
   } else if (strcmp(category,"pair_style") == 0) {
     if (force->pair_map->find(name) != force->pair_map->end())
       match = 1;
 
     if (!match && lmp->suffix_enable) {
       if (lmp->suffix) {
         char *name_w_suffix = new char [len + 2 + strlen(lmp->suffix)];
         sprintf(name_w_suffix,"%s/%s",name,lmp->suffix);
         if (force->pair_map->find(name_w_suffix) != force->pair_map->end())
           match = 1;
         delete[] name_w_suffix;
       }
       if (!match && lmp->suffix2) {
         char *name_w_suffix = new char [len + 2 + strlen(lmp->suffix2)];
         sprintf(name_w_suffix,"%s/%s",name,lmp->suffix2);
         if (force->pair_map->find(name_w_suffix) != force->pair_map->end())
           match = 1;
         delete[] name_w_suffix;
       }
     }
   } else error->all(FLERR,"Unknown category for info is_available()");
 
   return match ? true : false;
 }
 
 /* ---------------------------------------------------------------------- */
 
 // the is_defined() function returns true if a particular ID of the
 // selected category (e.g. fix ID, group ID, region ID etc.) has been
 // defined and thus can be accessed. It does *NOT* check whether a
 // particular ID has a particular style.
 
 bool Info::is_defined(const char *category, const char *name)
 {
   if ((category == NULL) || (name == NULL)) return false;
 
   if (strcmp(category,"compute") == 0) {
     int ncompute = modify->ncompute;
     Compute **compute = modify->compute;
     for (int i=0; i < ncompute; ++i) {
       if (strcmp(compute[i]->id,name) == 0)
         return true;
     }
   } else if (strcmp(category,"dump") == 0) {
     int ndump = output->ndump;
     Dump **dump = output->dump;
     for (int i=0; i < ndump; ++i) {
       if (strcmp(dump[i]->id,name) == 0)
         return true;
     }
   } else if (strcmp(category,"fix") == 0) {
     int nfix = modify->nfix;
     Fix **fix = modify->fix;
     for (int i=0; i < nfix; ++i) {
       if (strcmp(fix[i]->id,name) == 0)
         return true;
     }
   } else if (strcmp(category,"group") == 0) {
     int ngroup = group->ngroup;
     char **names = group->names;
     for (int i=0; i < ngroup; ++i) {
       if (strcmp(names[i],name) == 0)
         return true;
     }
   } else if (strcmp(category,"region") == 0) {
     int nreg = domain->nregion;
     Region **regs = domain->regions;
     for (int i=0; i < nreg; ++i) {
       if (strcmp(regs[i]->id,name) == 0)
         return true;
     }
   } else if (strcmp(category,"variable") == 0) {
     int nvar = input->variable->nvar;
     char **names = input->variable->names;
     for (int i=0; i < nvar; ++i) {
       if (strcmp(names[i],name) == 0)
         return true;
     }
   } else error->all(FLERR,"Unknown category for info is_defined()");
 
   return false;
 }
 
 static void print_columns(FILE* fp, vector<string> & styles)
 {
   if (styles.size() == 0) {
     fprintf(fp, "\nNone");
     return;
   }
 
   std::sort(styles.begin(), styles.end());
 
   int pos = 80;
   for (int i = 0; i < styles.size(); ++i) {
 
     // skip "secret" styles
     if (isupper(styles[i][0])) continue;
 
     int len = styles[i].length();
     if (pos + len > 80) {
       fprintf(fp,"\n");
       pos = 0;
     }
 
     if (len < 16) {
       fprintf(fp,"%-16s",styles[i].c_str());
       pos += 16;
     } else if (len < 32) {
       fprintf(fp,"%-32s",styles[i].c_str());
       pos += 32;
     } else if (len < 48) {
       fprintf(fp,"%-48s",styles[i].c_str());
       pos += 48;
     } else if (len < 64) {
       fprintf(fp,"%-64s",styles[i].c_str());
       pos += 64;
     } else {
       fprintf(fp,"%-80s",styles[i].c_str());
       pos += 80;
     }
   }
 }