diff --git a/src/variable.cpp b/src/variable.cpp
index f315ceb38..8298fecf6 100644
--- a/src/variable.cpp
+++ b/src/variable.cpp
@@ -1,1442 +1,1446 @@
 /* ----------------------------------------------------------------------
    LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
    http://lammps.sandia.gov, Sandia National Laboratories
    Steve Plimpton, sjplimp@sandia.gov
 
    Copyright (2003) Sandia Corporation.  Under the terms of Contract
    DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
    certain rights in this software.  This software is distributed under 
    the GNU General Public License.
 
    See the README file in the top-level LAMMPS directory.
 ------------------------------------------------------------------------- */
 
 #include "math.h"
 #include "stdlib.h"
 #include "string.h"
 #include "ctype.h"
 #include "unistd.h"
 #include "variable.h"
 #include "universe.h"
 #include "atom.h"
 #include "update.h"
 #include "group.h"
 #include "domain.h"
 #include "modify.h"
 #include "compute.h"
 #include "fix.h"
 #include "output.h"
 #include "thermo.h"
 #include "memory.h"
 #include "error.h"
 
 using namespace LAMMPS_NS;
 
 #define VARDELTA 4
 #define MAXLEVEL 4
 
 enum{INDEX,LOOP,EQUAL,WORLD,UNIVERSE,ULOOP,ATOM};
 enum{ARG,OP};
 enum{DONE,ADD,SUBTRACT,MULTIPLY,DIVIDE,CARAT,UNARY,
      SQRT,EXP,LN,VALUE,ATOMARRAY,TYPEARRAY};
 
 #define INVOKED_SCALAR 1      // same as in computes
 #define INVOKED_VECTOR 2
 #define INVOKED_PERATOM 4
 
 /* ---------------------------------------------------------------------- */
 
 Variable::Variable(LAMMPS *lmp) : Pointers(lmp)
 {
   MPI_Comm_rank(world,&me);
 
   nvar = maxvar = 0;
   names = NULL;
   style = NULL;
   num = NULL;
   index = NULL;
   data = NULL;
 
   precedence[DONE] = 0;
   precedence[ADD] = precedence[SUBTRACT] = 1;
   precedence[MULTIPLY] = precedence[DIVIDE] = 2;
   precedence[CARAT] = 3;
   precedence[UNARY] = 4;
 }
 
 /* ---------------------------------------------------------------------- */
 
 Variable::~Variable()
 {
   for (int i = 0; i < nvar; i++) {
     delete [] names[i];
     for (int j = 0; j < num[i]; j++) delete [] data[i][j];
     delete [] data[i];
   }
   memory->sfree(names);
   memory->sfree(style);
   memory->sfree(num);
   memory->sfree(index);
   memory->sfree(data);
 }
 
 /* ----------------------------------------------------------------------
    called by variable command in input script
 ------------------------------------------------------------------------- */
 
 void Variable::set(int narg, char **arg)
 {
   if (narg < 3) error->all("Illegal variable command");
 
   // if var already exists, just skip, except EQUAL and ATOM vars
 
   if (find(arg[0]) >= 0 && 
       strcmp(arg[1],"equal") != 0 && strcmp(arg[1],"atom") != 0) return;
       
   // make space for new variable
 
   if (nvar == maxvar) {
     maxvar += VARDELTA;
     names = (char **)
       memory->srealloc(names,maxvar*sizeof(char *),"var:names");
     style = (int *) memory->srealloc(style,maxvar*sizeof(int),"var:style");
     num = (int *) memory->srealloc(num,maxvar*sizeof(int),"var:num");
     index = (int *) memory->srealloc(index,maxvar*sizeof(int),"var:index");
     data = (char ***) 
       memory->srealloc(data,maxvar*sizeof(char **),"var:data");
   }
 
   // INDEX
   // num = listed args, index = 1st value, data = copied args
 
   if (strcmp(arg[1],"index") == 0) {
     style[nvar] = INDEX;
     num[nvar] = narg - 2;
     index[nvar] = 0;
     data[nvar] = new char*[num[nvar]];
     copy(num[nvar],&arg[2],data[nvar]);
 
   // LOOP
   // num = N, index = 1st value, data = list of NULLS since never used
 
   } else if (strcmp(arg[1],"loop") == 0) {
     if (narg != 3) error->all("Illegal variable command");
     style[nvar] = LOOP;
     num[nvar] = atoi(arg[2]);
     index[nvar] = 0;
     data[nvar] = new char*[num[nvar]];
     for (int i = 0; i < num[nvar]; i++) data[nvar][i] = NULL;
     
   // EQUAL
   // remove pre-existing var if also style EQUAL (allows it to be reset)
   // num = 2, index = 1st value
   // data = 2 values, 1st is string to eval, 2nd is filled on retrieval
 
   } else if (strcmp(arg[1],"equal") == 0) {
     if (narg != 3) error->all("Illegal variable command");
     if (find(arg[0]) >= 0) {
       if (style[find(arg[0])] != EQUAL)
 	error->all("Cannot redefine variable as a different style");
       remove(find(arg[0]));
     }
     style[nvar] = EQUAL;
     num[nvar] = 2;
     index[nvar] = 0;
     data[nvar] = new char*[num[nvar]];
     copy(1,&arg[2],data[nvar]);
     data[nvar][1] = NULL;
     
   // WORLD
   // num = listed args, index = partition this proc is in, data = copied args
   // error check that num = # of worlds in universe
 
   } else if (strcmp(arg[1],"world") == 0) {
     style[nvar] = WORLD;
     num[nvar] = narg - 2;
     if (num[nvar] != universe->nworlds)
       error->all("World variable count doesn't match # of partitions");
     index[nvar] = universe->iworld;
     data[nvar] = new char*[num[nvar]];
     copy(num[nvar],&arg[2],data[nvar]);
 
   // UNIVERSE and ULOOP
   // for UNIVERSE: num = listed args, data = copied args
   // for ULOOP: num = N, data = list of NULLS since never used
   // index = partition this proc is in
   // universe proc 0 creates lock file
   // error check that all other universe/uloop variables are same length
 
   } else if (strcmp(arg[1],"universe") == 0 || strcmp(arg[1],"uloop") == 0) {
     if (strcmp(arg[1],"universe") == 0) {
       style[nvar] = UNIVERSE;
       num[nvar] = narg - 2;
       data[nvar] = new char*[num[nvar]];
       copy(num[nvar],&arg[2],data[nvar]);
     } else {
       if (narg != 3) error->all("Illegal variable command");
       style[nvar] = ULOOP;
       num[nvar] = atoi(arg[2]);
       data[nvar] = new char*[num[nvar]];
       for (int i = 0; i < num[nvar]; i++) data[nvar][i] = NULL;
     }
 
     if (num[nvar] < universe->nworlds)
       error->all("Universe/uloop variable count < # of partitions");
     index[nvar] = universe->iworld;
 
     if (universe->me == 0) {
       FILE *fp = fopen("tmp.lammps.variable","w");
       fprintf(fp,"%d\n",universe->nworlds);
       fclose(fp);
     }
 
     for (int jvar = 0; jvar < nvar; jvar++)
       if (num[jvar] && (style[jvar] == UNIVERSE || style[jvar] == ULOOP) && 
 	  num[nvar] != num[jvar])
 	error->all("All universe/uloop variables must have same # of values");
 
     if (me == 0) {
       if (universe->uscreen)
 	fprintf(universe->uscreen,
 		"Initial ${%s} setting: value %d on partition %d\n",
 		arg[0],index[nvar]+1,universe->iworld);
       if (universe->ulogfile)
 	fprintf(universe->ulogfile,
 		"Initial ${%s} setting: value %d on partition %d\n",
 		arg[0],index[nvar]+1,universe->iworld);
     }
 
   // ATOM
   // remove pre-existing var if also style ATOM (allows it to be reset)
   // num = 1, index = 1st value
   // data = 1 value, string to eval
 
   } else if (strcmp(arg[1],"atom") == 0) {
     if (narg != 3) error->all("Illegal variable command");
     if (find(arg[0]) >= 0) {
       if (style[find(arg[0])] != ATOM)
 	error->all("Cannot redefine variable as a different style");
       remove(find(arg[0]));
     }
     style[nvar] = ATOM;
     num[nvar] = 1;
     index[nvar] = 0;
     data[nvar] = new char*[num[nvar]];
     copy(1,&arg[2],data[nvar]);
     
   } else error->all("Illegal variable command");
 
   // set name of variable
   // must come at end, since EQUAL/ATOM reset may have removed name
   // name must be all alphanumeric chars or underscores
 
   int n = strlen(arg[0]) + 1;
   names[nvar] = new char[n];
   strcpy(names[nvar],arg[0]);
 
   for (int i = 0; i < n-1; i++)
     if (!isalnum(names[nvar][i]) && names[nvar][i] != '_')
       error->all("Variable name must be alphanumeric or underscore characters");
 
   nvar++;
 }
 
 /* ----------------------------------------------------------------------
    single-value INDEX variable created by command-line argument
 ------------------------------------------------------------------------- */
 
 void Variable::set(char *name, char *value)
 {
   char **newarg = new char*[3];
   newarg[0] = name;
   newarg[1] = (char *) "index";
   newarg[2] = value;
   set(3,newarg);
   delete [] newarg;
 }
 
 /* ----------------------------------------------------------------------
    increment variable(s)
    return 0 if OK if successfully incremented
    return 1 if any variable is exhausted, free the variable to allow re-use
 ------------------------------------------------------------------------- */
 
 int Variable::next(int narg, char **arg)
 {
   int ivar;
 
   if (narg == 0) error->all("Illegal next command");
 
   // check that variables exist and are all the same style
   // exception: UNIVERSE and ULOOP variables can be mixed in same next command
 
   for (int iarg = 0; iarg < narg; iarg++) {
     ivar = find(arg[iarg]);
     if (ivar == -1) error->all("Invalid variable in next command");
     if (style[ivar] == ULOOP && style[find(arg[0])] == UNIVERSE) continue;
     else if (style[ivar] == UNIVERSE && style[find(arg[0])] == ULOOP) continue;
     else if (style[ivar] != style[find(arg[0])])
       error->all("All variables in next command must be same style");
   }
 
   // invalid styles EQUAL or WORLD or ATOM
 
   int istyle = style[find(arg[0])];
   if (istyle == EQUAL || istyle == WORLD || istyle == ATOM)
     error->all("Invalid variable style with next command");
 
   // increment all variables in list
   // if any variable is exhausted, set flag = 1 and remove var to allow re-use
 
   int flag = 0;
 
   if (istyle == INDEX || istyle == LOOP) {
     for (int iarg = 0; iarg < narg; iarg++) {
       ivar = find(arg[iarg]);
       index[ivar]++;
       if (index[ivar] >= num[ivar]) {
 	flag = 1;
 	remove(ivar);
       }
     }
 
   } else if (istyle == UNIVERSE || istyle == ULOOP) {
 
     // wait until lock file can be created and owned by proc 0 of this world
     // read next available index and Bcast it within my world
     // set all variables in list to nextindex
 
     int nextindex;
     if (me == 0) {
       while (1) {
 	if (!rename("tmp.lammps.variable","tmp.lammps.variable.lock")) break;
 	usleep(100000);
       }
       FILE *fp = fopen("tmp.lammps.variable.lock","r");
       fscanf(fp,"%d",&nextindex);
       fclose(fp);
       fp = fopen("tmp.lammps.variable.lock","w");
       fprintf(fp,"%d\n",nextindex+1);
       fclose(fp);
       rename("tmp.lammps.variable.lock","tmp.lammps.variable");
       if (universe->uscreen)
 	fprintf(universe->uscreen,
 		"Increment via next: value %d on partition %d\n",
 		nextindex+1,universe->iworld);
       if (universe->ulogfile)
 	fprintf(universe->ulogfile,
 		"Increment via next: value %d on partition %d\n",
 		nextindex+1,universe->iworld);
     }
     MPI_Bcast(&nextindex,1,MPI_INT,0,world);
 
     for (int iarg = 0; iarg < narg; iarg++) {
       ivar = find(arg[iarg]);
       index[ivar] = nextindex;
       if (index[ivar] >= num[ivar]) {
 	flag = 1;
 	remove(ivar);
       }
     }
   }
 
   return flag;
 }
 
 /* ----------------------------------------------------------------------
    return ptr to the data text associated with a variable
    if EQUAL var, evaluates variable and puts result in str
    return NULL if no variable or index is bad, caller must respond
 ------------------------------------------------------------------------- */
 
 char *Variable::retrieve(char *name)
 {
   int ivar = find(name);
   if (ivar == -1) return NULL;
   if (index[ivar] >= num[ivar]) return NULL;
 
   char *str;
   if (style[ivar] == INDEX || style[ivar] == WORLD || 
       style[ivar] == UNIVERSE) {
     str = data[ivar][index[ivar]];
   } else if (style[ivar] == LOOP || style[ivar] == ULOOP) {
     char *value = new char[16];
     sprintf(value,"%d",index[ivar]+1);
     int n = strlen(value) + 1;
     if (data[ivar][0]) delete [] data[ivar][0];
     data[ivar][0] = new char[n];
     strcpy(data[ivar][0],value);
     delete [] value;
     str = data[ivar][0];
   } else if (style[ivar] == EQUAL) {
     char result[32];
     double answer = evaluate(data[ivar][0],NULL);
     sprintf(result,"%.10g",answer);
     int n = strlen(result) + 1;
     if (data[ivar][1]) delete [] data[ivar][1];
     data[ivar][1] = new char[n];
     strcpy(data[ivar][1],result);
     str = data[ivar][1];
   } else if (style[ivar] == ATOM) return NULL;
 
   return str;
 }
 
 /* ----------------------------------------------------------------------
    return result of equal-style variable evaluation
 ------------------------------------------------------------------------- */
 
 double Variable::compute_equal(int ivar)
 {
   return evaluate(data[ivar][0],NULL);
 }
 
 /* ----------------------------------------------------------------------
    compute result of atom-style variable evaluation
    stride used since result may not be contiguous memory locs
    if sumflag, add variable values to existing result
 ------------------------------------------------------------------------- */
 
 void Variable::compute_atom(int ivar, int igroup,
 			    double *result, int stride, int sumflag)
 {
   Tree *tree;
   double tmp = evaluate(data[ivar][0],&tree);
 
   int groupbit = group->bitmask[igroup];
   int *mask = atom->mask;
   int nlocal = atom->nlocal;
 
   if (sumflag == 0) {
     int m = 0;
     for (int i = 0; i < nlocal; i++) {
       if (mask[i] && groupbit) result[m] = eval_tree(tree,i);
       else result[m] = 0.0;
       m += stride;
     }
 
   } else {
     int m = 0;
     for (int i = 0; i < nlocal; i++) {
       if (mask[i] && groupbit) result[m] += eval_tree(tree,i);
       m += stride;
     }
   }
 
   free_tree(tree);
 }
 
 /* ----------------------------------------------------------------------
    search for name in list of variables names
    return index or -1 if not found
 ------------------------------------------------------------------------- */
   
 int Variable::find(char *name)
 {
   for (int i = 0; i < nvar; i++)
     if (strcmp(name,names[i]) == 0) return i;
   return -1;
 }
 
 /* ----------------------------------------------------------------------
    return 1 if variable is EQUAL style, 0 if not
 ------------------------------------------------------------------------- */
   
 int Variable::equalstyle(int ivar)
 {
   if (style[ivar] == EQUAL) return 1;
   return 0;
 }
 
 /* ----------------------------------------------------------------------
    return 1 if variable is ATOM style, 0 if not
 ------------------------------------------------------------------------- */
   
 int Variable::atomstyle(int ivar)
 {
   if (style[ivar] == ATOM) return 1;
   return 0;
 }
 
 /* ----------------------------------------------------------------------
    remove Nth variable from list and compact list
 ------------------------------------------------------------------------- */
   
 void Variable::remove(int n)
 {
   delete [] names[n];
   for (int i = 0; i < num[n]; i++) delete [] data[n][i];
   delete [] data[n];
 
   for (int i = n+1; i < nvar; i++) {
     names[i-1] = names[i];
     style[i-1] = style[i];
     num[i-1] = num[i];
     index[i-1] = index[i];
     data[i-1] = data[i];
   }
   nvar--;
 }
 
 /* ----------------------------------------------------------------------
    copy narg strings from **from to **to 
 ------------------------------------------------------------------------- */
   
 void Variable::copy(int narg, char **from, char **to)
 {
   int n;
 
   for (int i = 0; i < narg; i++) {
     n = strlen(from[i]) + 1;
     to[i] = new char[n];
     strcpy(to[i],from[i]);
   }
 }
 
 /* ----------------------------------------------------------------------
    recursive evaluation of a string str
    string is a equal-style or atom-style formula containing one or more items:
      number = 0.0, -5.45, 2.8e-4, ...
      thermo keyword = ke, vol, atoms, ...
      math operation = (),-x,x+y,x-y,x*y,x/y,x^y,sqrt(x),exp(x),ln(x)
      group function = count(group), mass(group), xcm(group,x), ...
      atom value = x[123], y[3], vx[34], ...
      atom vector = x[], y[], vx[], ...
      compute = c_ID, c_ID[2], c_ID[], c_ID[][2], c_ID[N], c_ID[N][2]
      fix = f_ID, f_ID[2], f_ID[], f_ID[][2], f_ID[N], f_ID[N][2]
      variable = v_name, v_name[], v_name[N]
    if tree ptr passed in, return ptr to parse tree created for formula
    if no tree ptr passed in, return value of string as double
 ------------------------------------------------------------------------- */
 
 double Variable::evaluate(char *str, Tree **tree)
 {
   int op,opprevious;
   double value1,value2;
   char onechar;
 
   double argstack[MAXLEVEL];
   Tree *treestack[MAXLEVEL];
   int opstack[MAXLEVEL];
   int nargstack = 0;
   int ntreestack = 0;
   int nopstack = 0;
 
   int i = 0;
   int expect = ARG;
 
   while (1) {
     onechar = str[i];
 
     // whitespace: just skip
 
     if (isspace(onechar)) i++;
 
     // ----------------
     // parentheses: recursively evaluate contents of parens
     // ----------------
 
     else if (onechar == '(') {
       if (expect == OP) error->all("Invalid syntax in variable formula");
       expect = OP;
 
       char *contents;
       i = find_matching_paren(str,i,contents);
       i++;
 
       // evaluate contents and push on stack
 
       if (tree) {
 	Tree *newtree;
 	double tmp = evaluate(contents,&newtree);
 	treestack[ntreestack++] = newtree;
       } else argstack[nargstack++] = evaluate(contents,NULL);
 
       delete [] contents;
 
     // ----------------
     // number: push value onto stack
     // ----------------
 
     } else if (isdigit(onechar) || onechar == '.') {
       if (expect == OP) error->all("Invalid syntax in variable formula");
       expect = OP;
 
       // istop = end of number, including scientific notation
 
       int istart = i;
-      int istop = istart + strspn(&str[i],"0123456789eE.-") - 1;
-      while (str[istop] == '-') istop--;
-      i = istop + 1;
+      while (isdigit(str[i]) || str[i] == '.') i++;
+      if (str[i] == 'e' || str[i] == 'E') {
+	i++;
+	if (str[i] == '+' || str[i] == '-') i++;
+	while (isdigit(str[i])) i++;
+      }
+      int istop = i - 1;
 
       int n = istop - istart + 1;
       char *number = new char[n+1];
       strncpy(number,&str[istart],n);
       number[n] = '\0';
 
       if (tree) {
         Tree *newtree = new Tree();
 	newtree->type = VALUE;
 	newtree->value = atof(number);
 	treestack[ntreestack++] = newtree;
       } else argstack[nargstack++] = atof(number);
 
       delete [] number;
 
     // ----------------
     // letter: c_ID, f_ID, v_name, exp(), xcm(,), x[234], x[], vol
     // ----------------
 
     } else if (islower(onechar)) {
       if (expect == OP) error->all("Invalid syntax in variable formula");
       expect = OP;
 
       // istop = end of word
       // word = all alphanumeric or underscore
 
       int istart = i;
       while (isalnum(str[i]) || str[i] == '_') i++;
       int istop = i-1;
 
       int n = istop - istart + 1;
       char *word = new char[n+1];
       strncpy(word,&str[istart],n);
       word[n] = '\0';
 
       // ----------------
       // compute
       // ----------------
 
       if (strncmp(word,"c_",2) == 0) {
 	n = strlen(word) - 2 + 1;
 	char *id = new char[n];
 	strcpy(id,&word[2]);
 
 	if (update->first_update == 0)
 	  error->all("Compute in variable formula before initial run");
 
 	int icompute = modify->find_compute(id);
 	if (icompute < 0) error->all("Invalid compute ID in variable formula");
 	Compute *compute = modify->compute[icompute];
 
 	delete [] id;
 
 	// parse zero or one or two trailing brackets
 	// point i beyond last bracket
 	// nbracket = # of bracket pairs
 	// index1,index2 = int inside each bracket pair, 0 if empty
 
 	int nbracket,index1,index2;
 	if (str[i] != '[') nbracket = 0;
 	else {
 	  nbracket = 1;
 	  i = int_between_brackets(str,i,index1,1);
 	  i++;
 	  if (str[i] == '[') {
 	    nbracket = 2;
 	    i = int_between_brackets(str,i,index2,0);
 	    i++;
 	  }
 	}
 
         // c_ID = global scalar
 
 	if (nbracket == 0 && compute->scalar_flag) {
 
 	  if (!(compute->invoked & INVOKED_SCALAR))
 	    value1 = compute->compute_scalar();
 	  else value1 = compute->scalar;
 	  if (tree) {
 	    Tree *newtree = new Tree();
 	    newtree->type = VALUE;
 	    newtree->value = value1;
 	    treestack[ntreestack++] = newtree;
 	  } else argstack[nargstack++] = value1;
 
         // c_ID[2] = global vector
 
 	} else if (nbracket == 1 && index1 > 0 && compute->vector_flag) {
 
 	  if (index1 > compute->size_vector)
 	      error->all("Compute vector in variable formula is too small");
 	  if (!(compute->invoked & INVOKED_VECTOR)) compute->compute_vector();
 	  value1 = compute->vector[index1-1];
 	  if (tree) {
 	    Tree *newtree = new Tree();
 	    newtree->type = VALUE;
 	    newtree->value = value1;
 	    treestack[ntreestack++] = newtree;
 	  } else argstack[nargstack++] = value1;
 
         // c_ID[] = per-atom scalar
 
 	} else if (nbracket == 1 && index1 == 0 && 
 		   compute->peratom_flag && compute->size_peratom == 0) {
 
 	  if (tree == NULL)
 	    error->all("Per-atom compute in equal-style variable formula");
 	  if (!(compute->invoked & INVOKED_PERATOM))
 	    compute->compute_peratom();
 	  Tree *newtree = new Tree();
 	  newtree->type = ATOMARRAY;
 	  newtree->array = compute->scalar_atom;
 	  newtree->nstride = 1;
 	  treestack[ntreestack++] = newtree;
 
         // c_ID[N] = global value from per-atom scalar
 
 	} else if (nbracket == 1 && index1 > 0 && 
 		   compute->peratom_flag && compute->size_peratom == 0) {
 
 	  if (!(compute->invoked & INVOKED_PERATOM))
 	    compute->compute_peratom();
 	  peratom2global(1,NULL,compute->scalar_atom,1,index1,
 			 tree,treestack,ntreestack,argstack,nargstack);
 
         // c_ID[][2] = per-atom vector
 
 	} else if (nbracket == 2 && index1 == 0 && index2 > 0 &&
 		   compute->peratom_flag) {
 
 	  if (tree == NULL)
 	    error->all("Per-atom compute in equal-style variable formula");
 	  if (index2 > compute->size_peratom)
 	    error->all("Compute vector in variable formula is too small");
 	  if (!(compute->invoked & INVOKED_PERATOM))
 	    compute->compute_peratom();
 	  Tree *newtree = new Tree();
 	  newtree->type = ATOMARRAY;
 	  newtree->array = &compute->vector_atom[0][index2-1];
 	  newtree->nstride = compute->size_peratom;
 	  treestack[ntreestack++] = newtree;
 
         // c_ID[N][2] = global value from per-atom vector
 
 	} else if (nbracket == 2 && index1 > 0 && index2 > 0 &&
 		   compute->peratom_flag) {
 
 	  if (index2 > compute->size_peratom)
 	    error->all("Compute vector in variable formula is too small");
 	  if (!(compute->invoked & INVOKED_PERATOM))
 	    compute->compute_peratom();
 	  peratom2global(1,NULL,&compute->vector_atom[0][index2-1],
 			 compute->size_peratom,index1,
 			 tree,treestack,ntreestack,argstack,nargstack);
 
 	} else error->all("Mismatched compute in variable formula");
 
       // ----------------
       // fix
       // ----------------
 
       } else if (strncmp(word,"f_",2) == 0) {
 	n = strlen(word) - 2 + 1;
 	char *id = new char[n];
 	strcpy(id,&word[2]);
 
 	if (update->first_update == 0)
 	  error->all("Fix in variable formula before initial run");
 
 	int ifix = modify->find_fix(id);
 	if (ifix < 0) error->all("Invalid fix ID in variable formula");
 	Fix *fix = modify->fix[ifix];
 
 	delete [] id;
 
 	// parse zero or one or two trailing brackets
 	// point i beyond last bracket
 	// nbracket = # of bracket pairs
 	// index1,index2 = int inside each bracket pair, 0 if empty
 
 	int nbracket,index1,index2;
 	if (str[i] != '[') nbracket = 0;
 	else {
 	  nbracket = 1;
 	  i = int_between_brackets(str,i,index1,1);
 	  i++;
 	  if (str[i] == '[') {
 	    nbracket = 2;
 	    i = int_between_brackets(str,i,index2,0);
 	    i++;
 	  }
 	}
 
         // f_ID = global scalar
 
 	if (nbracket == 0 && fix->scalar_flag) {
 
 	  if (update->ntimestep % fix->scalar_vector_freq)
 	    error->all("Fix in variable not computed at compatible time");
 	  value1 = fix->compute_scalar();
 	  if (tree) {
 	    Tree *newtree = new Tree();
 	    newtree->type = VALUE;
 	    newtree->value = value1;
 	    treestack[ntreestack++] = newtree;
 	  } else argstack[nargstack++] = value1;
 
         // f_ID[2] = global vector
 
 	} else if (nbracket == 1 && index1 > 0 && fix->vector_flag) {
 
 	  if (index1 > fix->size_vector)
 	      error->all("Fix vector in variable formula is too small");
 	  if (update->ntimestep % fix->scalar_vector_freq)
 	    error->all("Fix in variable not computed at compatible time");
 	  value1 = fix->compute_vector(index1-1);
 	  if (tree) {
 	    Tree *newtree = new Tree();
 	    newtree->type = VALUE;
 	    newtree->value = value1;
 	    treestack[ntreestack++] = newtree;
 	  } else argstack[nargstack++] = value1;
 
         // f_ID[] = per-atom scalar
 
 	} else if (nbracket == 1 && index1 == 0 && 
 		   fix->peratom_flag && fix->size_peratom == 0) {
 
 	  if (tree == NULL)
 	    error->all("Per-atom fix in equal-style variable formula");
 	  if (update->ntimestep % fix->peratom_freq)
 	    error->all("Fix in variable not computed at compatible time");
 	  Tree *newtree = new Tree();
 	  newtree->type = ATOMARRAY;
 	  newtree->array = fix->scalar_atom;
 	  newtree->nstride = 1;
 	  treestack[ntreestack++] = newtree;
 
         // f_ID[N] = global value from per-atom scalar
 
 	} else if (nbracket == 1 && index1 > 0 && 
 		   fix->peratom_flag && fix->size_peratom == 0) {
 
 	  if (update->ntimestep % fix->peratom_freq)
 	    error->all("Fix in variable not computed at compatible time");
 	  peratom2global(1,NULL,fix->scalar_atom,1,index1,
 			 tree,treestack,ntreestack,argstack,nargstack);
 
         // f_ID[][2] = per-atom vector
 
 	} else if (nbracket == 2 && index1 == 0 && index2 > 0 &&
 		   fix->peratom_flag) {
 
 	  if (tree == NULL)
 	    error->all("Per-atom fix in equal-style variable formula");
 	  if (index2 > fix->size_peratom)
 	    error->all("Fix vector in variable formula is too small");
 	  if (update->ntimestep % fix->peratom_freq)
 	    error->all("Fix in variable not computed at compatible time");
 	  Tree *newtree = new Tree();
 	  newtree->type = ATOMARRAY;
 	  newtree->array = &fix->vector_atom[0][index2-1];
 	  newtree->nstride = fix->size_peratom;
 	  treestack[ntreestack++] = newtree;
 
         // f_ID[N][2] = global value from per-atom vector
 
 	} else if (nbracket == 2 && index1 > 0 && index2 > 0 &&
 		   fix->peratom_flag) {
 
 	  if (index2 > fix->size_peratom)
 	    error->all("Fix vector in variable formula is too small");
 	  if (update->ntimestep % fix->peratom_freq)
 	    error->all("Fix in variable not computed at compatible time");
 	  peratom2global(1,NULL,&fix->vector_atom[0][index2-1],
 			 fix->size_peratom,index1,
 			 tree,treestack,ntreestack,argstack,nargstack);
 
 	} else error->all("Mismatched fix in variable formula");
 
       // ----------------
       // variable
       // ----------------
 
       } else if (strncmp(word,"v_",2) == 0) {
 	n = strlen(word) - 2 + 1;
 	char *id = new char[n];
 	strcpy(id,&word[2]);
 
 	int ivar = find(id);
 	if (ivar < 0) error->all("Invalid variable name in variable formula");
 
 	// parse zero or one trailing brackets
 	// point i beyond last bracket
 	// nbracket = # of bracket pairs
 	// index = int inside bracket, 0 if empty
 
 	int nbracket,index;
 	if (str[i] != '[') nbracket = 0;
 	else {
 	  nbracket = 1;
 	  i = int_between_brackets(str,i,index,1);
 	  i++;
 	}
 
         // v_name = non atom-style variable = global value
 
 	if (nbracket == 0 && style[ivar] != ATOM) {
 
 	  char *var = retrieve(id);
 	  if (var == NULL)
 	    error->all("Invalid variable evaluation in variable formula");
 	  if (tree) {
 	    Tree *newtree = new Tree();
 	    newtree->type = VALUE;
 	    newtree->value = atof(var);
 	    treestack[ntreestack++] = newtree;
 	  } else argstack[nargstack++] = atof(var);
 
         // v_name[] = atom-style variable
 
 	} else if (nbracket && index == 0 && style[ivar] == ATOM) {
 
 	  if (tree == NULL)
 	    error->all("Atom-style variable in equal-style variable formula");
 	  Tree *newtree;
 	  double tmp = evaluate(data[ivar][0],&newtree);
 	  treestack[ntreestack++] = newtree;
 
         // v_name[N] = global value from atom-style variable
 	// compute the per-atom variable in result
 	// use peratom2global to extract single value from result
 
 	} else if (nbracket && index > 0 && style[ivar] == ATOM) {
 
 	  double *result = (double *)
 	    memory->smalloc(atom->nlocal*sizeof(double),"variable:result");
 	  compute_atom(ivar,0,result,1,0);
 	  peratom2global(1,NULL,result,1,index,
 			 tree,treestack,ntreestack,argstack,nargstack);
 	  memory->sfree(result);
 
 	} else error->all("Mismatched variable in variable formula");
 
 	delete [] id;
 
       // ----------------
       // math/group function or atom value/vector or thermo keyword
       // ----------------
 
       } else {
 
 	// math or group function
 
 	if (str[i] == '(') {
 	  char *contents;
 	  i = find_matching_paren(str,i,contents);
 	  i++;
 
 	  if (math_function(word,contents,tree,
 			    treestack,ntreestack,argstack,nargstack));
 	  else if (group_function(word,contents,tree,
 				  treestack,ntreestack,argstack,nargstack));
 	  else error->all("Invalid math or group function in variable formula");
 
 	  delete [] contents;
 
 	// ----------------
 	// atom value or vector
 	// ----------------
 
 	} else if (str[i] == '[') {
 	  int id;
 	  i = int_between_brackets(str,i,id,1);
 	  i++;
 
 	  // ID between brackets exists: atom value
 	  // empty brackets: atom vector
 
 	  if (id > 0)
 	    peratom2global(0,word,NULL,0,id,
 			   tree,treestack,ntreestack,argstack,nargstack);
 	  else
 	    atom_vector(word,tree,treestack,ntreestack);
 
 	// ----------------
 	// thermo keyword
 	// ----------------
 
 	} else {
 	  if (update->first_update == 0)
 	    error->all("Thermo keyword in variable formula before initial run");
 	  int flag = output->thermo->evaluate_keyword(word,&value1);
 	  if (flag) error->all("Invalid thermo keyword in variable formula");
 	  if (tree) {
 	    Tree *newtree = new Tree();
 	    newtree->type = VALUE;
 	    newtree->value = value1;
 	    treestack[ntreestack++] = newtree;
 	  } else argstack[nargstack++] = value1;
 	}
       }
 
       delete [] word;
 
     // ----------------
     // math operator, including end-of-string
     // ----------------
 
     } else if (strchr("+-*/^\0",onechar)) {
       if (onechar == '+') op = ADD;
       else if (onechar == '-') op = SUBTRACT;
       else if (onechar == '*') op = MULTIPLY;
       else if (onechar == '/') op = DIVIDE;
       else if (onechar == '^') op = CARAT;
       else op = DONE;
       i++;
 
       if (op == SUBTRACT && expect == ARG) {
 	opstack[nopstack++] = UNARY;
 	continue;
       }
 
       if (expect == ARG) error->all("Invalid syntax in variable formula");
       expect = ARG;
 
       // evaluate stack as deep as possible while respecting precedence
       // before pushing current op onto stack
 
       while (nopstack && precedence[opstack[nopstack-1]] >= precedence[op]) {
 	opprevious = opstack[--nopstack];
 
 	if (tree) {
 	  Tree *newtree = new Tree();
 	  newtree->type = opprevious;
 	  newtree->right = treestack[--ntreestack];
 	  if (opprevious == UNARY) newtree->left = newtree->right;
 	  else newtree->left = treestack[--ntreestack];
 	  treestack[ntreestack++] = newtree;
 
 	} else {
 	  value2 = argstack[--nargstack];
 	  if (opprevious != UNARY) value1 = argstack[--nargstack];
 
 	  if (opprevious == ADD)
 	    argstack[nargstack++] = value1 + value2;
 	  else if (opprevious == SUBTRACT)
 	    argstack[nargstack++] = value1 - value2;
 	  else if (opprevious == MULTIPLY)
 	    argstack[nargstack++] = value1 * value2;
 	  else if (opprevious == DIVIDE) {
 	    if (value2 == 0.0) error->all("Divide by 0 in variable formula");
 	    argstack[nargstack++] = value1 / value2;
 	  } else if (opprevious == CARAT) {
 	    if (value2 == 0.0) error->all("Power by 0 in variable formula");
 	    argstack[nargstack++] = pow(value1,value2);
 	  } else if (opprevious == UNARY)
 	    argstack[nargstack++] = -value2;
 	}
       }
 
       // if end-of-string, break out of entire formula evaluation loop
 
       if (op == DONE) break;
 
       // push current operation onto stack
 
       opstack[nopstack++] = op;
 
     } else error->all("Invalid syntax in variable formula");
   }
 
   if (nopstack) error->all("Invalid syntax in variable formula");
 
   // for atom-style variable, return remaining tree
   // for equal-style variable, return remaining arg
 
   if (tree) {
     if (ntreestack != 1) error->all("Invalid syntax in variable formula");
     *tree = treestack[0];
     return 0.0;
   } else {
     if (nargstack != 1) error->all("Invalid syntax in variable formula");
     return argstack[0];
   }
 }
 
 /* ---------------------------------------------------------------------- */
 
 double Variable::eval_tree(Tree *tree, int i)
 {
   if (tree->type == VALUE) return tree->value;
   if (tree->type == ATOMARRAY) return tree->array[i*tree->nstride];
   if (tree->type == TYPEARRAY) return tree->array[atom->type[i]];
 
   if (tree->type == ADD)
     return eval_tree(tree->left,i) + eval_tree(tree->right,i);
   if (tree->type == SUBTRACT)
     return eval_tree(tree->left,i) - eval_tree(tree->right,i);
   if (tree->type == MULTIPLY)
     return eval_tree(tree->left,i) * eval_tree(tree->right,i);
   if (tree->type == DIVIDE) {
     double denom = eval_tree(tree->right,i);
     if (denom == 0.0) error->all("Divide by 0 in variable formula");
     return eval_tree(tree->left,i) / denom;
   }
   if (tree->type == CARAT) {
     double exponent = eval_tree(tree->right,i);
     if (exponent == 0.0) error->all("Power by 0 in variable formula");
     return pow(eval_tree(tree->left,i),exponent);
   }
   if (tree->type == UNARY)
     return -eval_tree(tree->left,i);
   if (tree->type == SQRT) {
     double arg = eval_tree(tree->left,i);
     if (arg < 0.0) error->all("Sqrt of negative in variable formula");
     return sqrt(arg);
   }
   if (tree->type == EXP)
     return exp(eval_tree(tree->left,i));
   if (tree->type == LN) {
     double arg = eval_tree(tree->left,i);
     if (arg <= 0.0) error->all("Log of negative/zero in variable formula");
     return log(arg);
   }
 
   return 0.0;
 }
 
 /* ---------------------------------------------------------------------- */
 
 void Variable::free_tree(Tree *tree)
 {
   if (tree->left) free_tree(tree->left);
   if (tree->right) free_tree(tree->right);
   delete tree;
 }
 
 /* ----------------------------------------------------------------------
    find matching parenthesis in str, allocate contents = str between parens
    i = left paren
    return loc or right paren
 ------------------------------------------------------------------------- */
 
 int Variable::find_matching_paren(char *str, int i,char *&contents)
 {
   // istop = matching ')' at same level, allowing for nested parens
 
   int istart = i;
   int ilevel = 0;
   while (1) {
     i++;
     if (!str[i]) break;
     if (str[i] == '(') ilevel++;
     else if (str[i] == ')' && ilevel) ilevel--;
     else if (str[i] == ')') break;
   }
   if (!str[i]) error->all("Invalid syntax in variable formula");
   int istop = i;
 
   int n = istop - istart - 1;
   contents = new char[n+1];
   strncpy(contents,&str[istart+1],n);
   contents[n] = '\0';
 
   return istop;
 }
 
 /* ----------------------------------------------------------------------
    find int between brackets and set index to its value
    if emptyflag, then brackets can be empty (index = 0)
    else if not emptyflag and brackets empty, is an error
    else contents of brackets must be positive int
    i = left bracket
    return loc of right bracket
 ------------------------------------------------------------------------- */
 
 int Variable::int_between_brackets(char *str, int i, int &index, int emptyflag)
 {
   // istop = matching ']'
 
   int istart = i;
   while (!str[i] || str[i] != ']') i++;
   if (!str[i]) error->all("Invalid syntax in variable formula");
   int istop = i;
 
   int n = istop - istart - 1;
   char *arg = new char[n+1];
   strncpy(arg,&str[istart+1],n);
   arg[n] = '\0';
 
   if (n == 0 && emptyflag) index = 0;
   else if (n == 0) error->all("Empty brackets in variable formula");
   else {
     index = atoi(arg);
     if (index <= 0) error->all("Invalid index in variable formula");
   }
   delete [] arg;
  
   return istop;
 }
 
 /* ----------------------------------------------------------------------
    process a math function in formula
    push result onto tree or arg stack
    word = math function
    contents = str bewteen parentheses
    return 0 if not a match, 1 if successfully processed
    customize by adding a math function: sqrt(),exp(),log()
 ------------------------------------------------------------------------- */
 
 int Variable::math_function(char *word, char *contents, Tree **tree,
 			    Tree **treestack, int &ntreestack,
 			    double *argstack, int &nargstack)
 {
   // word not a match to any math function
 
   if (strcmp(word,"sqrt") && strcmp(word,"exp") && strcmp(word,"ln"))
     return 0;
     
   Tree *newtree;
   double value;
 
   if (tree) {
     newtree = new Tree();
     Tree *argtree;
     double tmp = evaluate(contents,&argtree);
     newtree->left = argtree;
     treestack[ntreestack++] = newtree;
   } else value = evaluate(contents,NULL);
     
   if (strcmp(word,"sqrt") == 0) {
     if (tree) newtree->type = SQRT;
     else {
       if (value < 0.0) error->all("Sqrt of negative in variable formula");
       argstack[nargstack++] = sqrt(value);
     }
 
   } else if (strcmp(word,"exp") == 0) {
     if (tree) newtree->type = EXP;
     else argstack[nargstack++] = exp(value);
 
   } else if (strcmp(word,"ln") == 0) {
     if (tree) newtree->type = LN;
     else {
       if (value <= 0.0) error->all("Log of zero/negative in variable formula");
       argstack[nargstack++] = log(value);
     }
   }
 
   return 1;
 }    
 
 
 /* ----------------------------------------------------------------------
    process a group function in formula
    push result onto tree or arg stack
    word = group function
    contents = str bewteen parentheses with one or two args
    return 0 if not a match, 1 if successfully processed
    customize by adding a group function:
      count(group),mass(group),charge(group),
      xcm(group,dim),vcm(group,dim),fcm(group,dim),
      bound(group,xmin),gyration(group)
 ------------------------------------------------------------------------- */
 
 int Variable::group_function(char *word, char *contents, Tree **tree,
 			     Tree **treestack, int &ntreestack,
 			     double *argstack, int &nargstack)
 {
   // parse contents for arg1,arg2 separated by comma
 
   char *arg1,*arg2;
   char *ptr = strchr(contents,',');
   if (ptr) *ptr = '\0';
   int n = strlen(contents) + 1;
   arg1 = new char[n];
   strcpy(arg1,contents);
   if (ptr) {
     n = strlen(ptr+1) + 1;
     arg2 = new char[n];
     strcpy(arg2,ptr+1);
   } else arg2 = NULL;
   
   int igroup = group->find(arg1);
   if (igroup == -1)
     error->all("Group ID in variable formula does not exist");
 
   Tree *newtree;
   double value;
   
   if (tree) {
     newtree = new Tree();
     newtree->type = VALUE;
     treestack[ntreestack++] = newtree;
   }
 
   // match word to group function
 
   if (strcmp(word,"count") == 0) {
     if (arg2)
       error->all("Invalid group function in variable formula");
     value = group->count(igroup);
 
   } else if (strcmp(word,"mass") == 0) {
     if (arg2)
       error->all("Invalid group function in variable formula");
     value = group->mass(igroup);
 
   } else if (strcmp(word,"charge") == 0) {
     if (arg2)
       error->all("Invalid group function in variable formula");
     value = group->charge(igroup);
 
   } else if (strcmp(word,"xcm") == 0) {
     if (!arg2)
       error->all("Invalid group function in variable formula");
     atom->check_mass();
     double masstotal = group->mass(igroup);
     double xcm[3];
     group->xcm(igroup,masstotal,xcm);
     if (strcmp(arg2,"x") == 0) value = xcm[0];
     else if (strcmp(arg2,"y") == 0) value = xcm[1];
     else if (strcmp(arg2,"z") == 0) value = xcm[2];
     else error->all("Invalid group function in variable formula");
 
   } else if (strcmp(word,"vcm") == 0) {
     if (!arg2)
       error->all("Invalid group function in variable formula");
     atom->check_mass();
     double masstotal = group->mass(igroup);
     double vcm[3];
     group->vcm(igroup,masstotal,vcm);
     if (strcmp(arg2,"x") == 0) value = vcm[0];
     else if (strcmp(arg2,"y") == 0) value = vcm[1];
     else if (strcmp(arg2,"z") == 0) value = vcm[2];
     else error->all("Invalid group function in variable formula");
 
   } else if (strcmp(word,"fcm") == 0) {
     if (!arg2)
       error->all("Invalid group function in variable formula");
     double fcm[3];
     group->fcm(igroup,fcm);
     if (strcmp(arg2,"x") == 0) value = fcm[0];
     else if (strcmp(arg2,"y") == 0) value = fcm[1];
     else if (strcmp(arg2,"z") == 0) value = fcm[2];
     else error->all("Invalid group function in variable formula");
 
   } else if (strcmp(word,"bound") == 0) {
     if (!arg2)
       error->all("Invalid group function in variable formula");
     double minmax[6];
     group->bounds(igroup,minmax);
     if (strcmp(arg2,"xmin") == 0) value = minmax[0];
     else if (strcmp(arg2,"xmax") == 0) value = minmax[1];
     else if (strcmp(arg2,"ymin") == 0) value = minmax[2];
     else if (strcmp(arg2,"ymax") == 0) value = minmax[3];
     else if (strcmp(arg2,"zmin") == 0) value = minmax[4];
     else if (strcmp(arg2,"zmax") == 0) value = minmax[5];
     else error->all("Invalid group function in variable formula");
 
   } else if (strcmp(word,"gyration") == 0) {
     atom->check_mass();
     double masstotal = group->mass(igroup);
     double xcm[3];
     group->xcm(igroup,masstotal,xcm);
     value = group->gyration(igroup,masstotal,xcm);
 
   } else return 0;
     
   delete [] arg1;
   delete [] arg2;
     
   if (tree) newtree->value= value;
   else argstack[nargstack++] = value;
 
   return 1;
 }
 
 /* ----------------------------------------------------------------------
    extract a global value from a per-atom quantity in a formula
    flag = 0 -> word is an atom vector
    flag = 1 -> vector is a per-atom compute or fix quantity with nstride
    id = positive global ID of atom, converted to local index
    push result onto tree or arg stack
    customize by adding an atom vector: mass,x,y,z,vx,vy,vz,fx,fy,fz
 ------------------------------------------------------------------------- */
 
 void Variable::peratom2global(int flag, char *word,
 			      double *vector, int nstride, int id,
 			      Tree **tree, Tree **treestack, int &ntreestack,
 			      double *argstack, int &nargstack)
 {
   if (atom->map_style == 0)
     error->all("Indexed per-atom vector in variable formula without atom map");
 
   int index = atom->map(id);
 
   double mine;
   if (index >= 0 && index < atom->nlocal) {
 
     if (flag == 0) {
       if (strcmp(word,"mass") == 0) {
 	if (atom->mass) mine = atom->mass[atom->type[index]];
 	else mine = atom->rmass[index];
       }
       else if (strcmp(word,"x") == 0) mine = atom->x[index][0];
       else if (strcmp(word,"y") == 0) mine = atom->x[index][1];
       else if (strcmp(word,"z") == 0) mine = atom->x[index][2];
       else if (strcmp(word,"vx") == 0) mine = atom->v[index][0];
       else if (strcmp(word,"vy") == 0) mine = atom->v[index][1];
       else if (strcmp(word,"vz") == 0) mine = atom->v[index][2];
       else if (strcmp(word,"fx") == 0) mine = atom->f[index][0];
       else if (strcmp(word,"fy") == 0) mine = atom->f[index][1];
       else if (strcmp(word,"fz") == 0) mine = atom->f[index][2];
       
       else error->one("Invalid atom vector in variable formula");
 
     } else mine = vector[index*nstride];
     
   } else mine = 0.0;
 
   double value;
   MPI_Allreduce(&mine,&value,1,MPI_DOUBLE,MPI_SUM,world);
   
   if (tree) {
     Tree *newtree = new Tree();
     newtree->type = VALUE;
     newtree->value = value;
     treestack[ntreestack++] = newtree;
   } else argstack[nargstack++] = value;
 }
 
 /* ----------------------------------------------------------------------
    process an atom vector in formula
    push result onto tree
    word = atom vector
    customize by adding an atom vector: mass,x,y,z,vx,vy,vz,fx,fy,fz
 ------------------------------------------------------------------------- */
 
 void Variable::atom_vector(char *word, Tree **tree,
 			   Tree **treestack, int &ntreestack)
 {
   if (tree == NULL)
     error->all("Atom vector in equal-style variable formula");
 
   Tree *newtree = new Tree();
   newtree->type = ATOMARRAY;
   newtree->nstride = 3;
   treestack[ntreestack++] = newtree;
 	    
   if (strcmp(word,"mass") == 0) {
     if (atom->mass) {
       newtree->type = TYPEARRAY;
       newtree->array = atom->mass;
     } else {
       newtree->nstride = 1;
       newtree->array = atom->rmass;
     }
   }
   else if (strcmp(word,"x") == 0) newtree->array = &atom->x[0][0];
   else if (strcmp(word,"y") == 0) newtree->array = &atom->x[0][1];
   else if (strcmp(word,"z") == 0) newtree->array = &atom->x[0][2];
   else if (strcmp(word,"vx") == 0) newtree->array = &atom->v[0][0];
   else if (strcmp(word,"vy") == 0) newtree->array = &atom->v[0][1];
   else if (strcmp(word,"vz") == 0) newtree->array = &atom->v[0][2];
   else if (strcmp(word,"fx") == 0) newtree->array = &atom->f[0][0];
   else if (strcmp(word,"fy") == 0) newtree->array = &atom->f[0][1];
   else if (strcmp(word,"fz") == 0) newtree->array = &atom->f[0][2];
   
   else error->all("Invalid atom vector in variable formula");
 }