From beb5a30f6750b7592cef773aa47b1ed5520fa9a2 Mon Sep 17 00:00:00 2001
From: Steve Plimpton <sjplimp@sandia.gov>
Date: Wed, 30 Nov 2016 14:01:27 -0700
Subject: [PATCH] new compute global/atom command, also bug fix for descending
 dump sorts

---
 doc/src/Section_commands.txt    |   1 +
 doc/src/compute_chunk_atom.txt  |   3 +-
 doc/src/compute_global_atom.txt | 220 +++++++++++++
 doc/src/lammps.book             |   1 +
 src/compute.cpp                 |   3 +-
 src/compute_bond_local.cpp      |   2 +-
 src/compute_global_atom.cpp     | 540 ++++++++++++++++++++++++++++++++
 src/compute_global_atom.h       | 143 +++++++++
 src/fix_ave_time.cpp            |   6 +-
 9 files changed, 914 insertions(+), 5 deletions(-)
 create mode 100644 doc/src/compute_global_atom.txt
 create mode 100644 src/compute_global_atom.cpp
 create mode 100644 src/compute_global_atom.h

diff --git a/doc/src/Section_commands.txt b/doc/src/Section_commands.txt
index 4707d25c4f..7cf0b3df90 100644
--- a/doc/src/Section_commands.txt
+++ b/doc/src/Section_commands.txt
@@ -767,6 +767,7 @@ KOKKOS, o = USER-OMP, t = OPT.
 "erotate/sphere"_compute_erotate_sphere.html,
 "erotate/sphere/atom"_compute_erotate_sphere_atom.html,
 "event/displace"_compute_event_displace.html,
+"global/atom"_compute_global_atom.html,
 "group/group"_compute_group_group.html,
 "gyration"_compute_gyration.html,
 "gyration/chunk"_compute_gyration_chunk.html,
diff --git a/doc/src/compute_chunk_atom.txt b/doc/src/compute_chunk_atom.txt
index 60516fe421..e6083ad615 100644
--- a/doc/src/compute_chunk_atom.txt
+++ b/doc/src/compute_chunk_atom.txt
@@ -641,7 +641,8 @@ the restarted simulation begins.
 
 [Related commands:]
 
-"fix ave/chunk"_fix_ave_chunk.html
+"fix ave/chunk"_fix_ave_chunk.html,
+"compute global/atom"_compute_global_atom.html
 
 [Default:]
 
diff --git a/doc/src/compute_global_atom.txt b/doc/src/compute_global_atom.txt
new file mode 100644
index 0000000000..f62efcff2e
--- /dev/null
+++ b/doc/src/compute_global_atom.txt
@@ -0,0 +1,220 @@
+"LAMMPS WWW Site"_lws - "LAMMPS Documentation"_ld - "LAMMPS Commands"_lc :c
+
+:link(lws,http://lammps.sandia.gov)
+:link(ld,Manual.html)
+:link(lc,Section_commands.html#comm)
+
+:line
+
+compute global/atom command :h3
+
+[Syntax:]
+
+compute ID group-ID style index input1 input2 ... :pre
+
+ID, group-ID are documented in "compute"_compute.html command :ulb,l
+global/atom = style name of this compute command :l
+index = c_ID, c_ID\[N\], f_ID, f_ID\[N\], v_name :l
+  c_ID = per-atom vector calculated by a compute with ID
+  c_ID\[I\] = Ith column of per-atom array calculated by a compute with ID
+  f_ID = per-atom vector calculated by a fix with ID
+  f_ID\[I\] = Ith column of per-atom array calculated by a fix with ID
+  v_name = per-atom vector calculated by an atom-style variable with name :pre
+one or more inputs can be listed :l
+input = c_ID, c_ID\[N\], f_ID, f_ID\[N\], v_name :l
+  c_ID = global vector calculated by a compute with ID
+  c_ID\[I\] = Ith column of global array calculated by a compute with ID, I can include wildcard (see below)
+  f_ID = global vector calculated by a fix with ID
+  f_ID\[I\] = Ith column of global array calculated by a fix with ID, I can include wildcard (see below)
+  v_name = global vector calculated by a vector-style variable with name :pre
+:ule
+
+[Examples:]
+
+compute 1 all global/atom c_chunk c_com\[1\\] c_com\[2\\] c_com\[3\\]
+compute 1 all global/atom c_chunk c_com\[*\\] :pre
+
+[Description:]
+
+Define a calculation that assigns global values to each atom from
+vectors or arrays of global values.  The specified {index} parameter
+is used to determine which global value is assigned to each atom.
+
+The {index} parameter must reference a per-atom vector or array from a
+"compute"_compute.html or "fix"_fix.html or the evaluation of an
+atom-style "variable"_variable.html.  Each {input} value must
+reference a global vector or array from a "compute"_compute.html or
+"fix"_fix.html or the evaluation of an vector-style
+"variable"_variable.html.  Details are given below.
+
+The {index} value for an atom is used as a index I (from 1 to N) into
+the vector associated with each of the input values.  The Ith value
+from the input vector becomes one output value for that atom.  If the
+atom is not in the specified group, or the index I < 1 or I > M, where
+M is the actual length of the input vector, then an output value of
+0.0 is assigned to the atom.
+
+An example of how this command is useful, is in the context of
+"chunks" which are static or dyanmic subsets of atoms.  The "compute
+chunk/atom"_compute_chunk_atom.html command assigns unique chunk IDs
+to each atom.  It's output can be used as the {index} parameter for
+this command.  Various other computes with "chunk" in their style
+name, such as "compute com/chunk"_compute_com_chunk.html or "compute
+msd/chunk"_compute_msd_chunk.html, calculate properties for each
+chunk.  The output of these commands are global vectors or arrays,
+with one or more values per chunk, and can be used as input values for
+this command.  This command will then assign the global chunk value to
+each atom in the chunk, producing a per-atom vector or per-atom array
+as output.  The per-atom values can then be output to a dump file or
+used by any command that uses per-atom values from a compute as input,
+as discussed in "Section 6.15"_Section_howto.html#howto_15.
+
+As a concrete example, these commands will calculate the displacement
+of each atom from the center-of-mass of the molecule it is in, and
+dump those values to a dump file.  In this case, each molecule is a
+chunk.
+
+compute cc1 all chunk/atom molecule
+compute myChunk all com/chunk cc1
+compute prop all property/atom xu yu zu
+compute glob all global/atom c_cc1 c_myChunk\[*\]
+variable dx atom c_prop\[1\]-c_glob\[1\]
+variable dy atom c_prop\[2\]-c_glob\[2\]
+variable dz atom c_prop\[3\]-c_glob\[3\]
+variable dist atom sqrt(v_dx*v_dx+v_dy*v_dy+v_dz*v_dz)
+dump 1 all custom 100 tmp.dump id xu yu zu c_glob\[1\] c_glob\[2\] c_glob\[3\] &
+     v_dx v_dy v_dz v_dist
+dump_modify 1 sort id :pre
+
+You can add these commands to the bench/in.chain script to see how
+they work.
+
+:line
+
+Note that for input values from a compute or fix, the bracketed index
+I can be specified using a wildcard asterisk with the index to
+effectively specify multiple values.  This takes the form "*" or "*n"
+or "n*" or "m*n".  If N = the size of the vector (for {mode} = scalar)
+or the number of columns in the array (for {mode} = vector), then an
+asterisk with no numeric values means all indices from 1 to N.  A
+leading asterisk means all indices from 1 to n (inclusive).  A
+trailing asterisk means all indices from n to N (inclusive).  A middle
+asterisk means all indices from m to n (inclusive).
+
+Using a wildcard is the same as if the individual columns of the array
+had been listed one by one.  E.g. these 2 compute global/atom commands
+are equivalent, since the "compute com/chunk"_compute_com_chunk.html
+command creates a global array with 3 columns:
+
+compute cc1 all chunk/atom molecule
+compute com all com/chunk cc1
+compute 1 all global/atom c_cc1 c_com\[1\] c_com\[2\] c_com\[3\]
+compute 1 all global/atom c_cc1 c_com\[*\] :pre
+
+:line
+
+This section explains the {index} parameter.  Note that it must
+reference per-atom values, as contrasted with the {input} values which
+must reference global values.
+
+Note that all of these options generate floating point values.  When
+they are used as an index into the specified input vectors, they
+simple rounded down to convert the value to integer indices.  The
+final values should range from 1 to N (inclusive), since they are used
+to access values from N-length vectors.
+
+If {index} begins with "c_", a compute ID must follow which has been
+previously defined in the input script.  The compute must generate
+per-atom quantities.  See the individual "compute"_compute.html doc
+page for details.  If no bracketed integer is appended, the per-atom
+vector calculated by the compute is used.  If a bracketed integer is
+appended, the Ith column of the per-atom array calculated by the
+compute is used.  Users can also write code for their own compute
+styles and "add them to LAMMPS"_Section_modify.html.  See the
+discussion above for how I can be specified with a wildcard asterisk
+to effectively specify multiple values.
+
+If {index} begins with "f_", a fix ID must follow which has been
+previously defined in the input script.  The Fix must generate
+per-atom quantities.  See the individual "fix"_fix.html doc page for
+details.  Note that some fixes only produce their values on certain
+timesteps, which must be compatible with when compute global/atom
+references the values, else an error results.  If no bracketed integer
+is appended, the per-atom vector calculated by the fix is used.  If a
+bracketed integer is appended, the Ith column of the per-atom array
+calculated by the fix is used.  Users can also write code for their
+own fix style and "add them to LAMMPS"_Section_modify.html.  See the
+discussion above for how I can be specified with a wildcard asterisk
+to effectively specify multiple values.
+
+If {index} begins with "v_", a variable name must follow which has
+been previously defined in the input script.  It must be an
+"atom-style variable"_variable.html.  Atom-style variables can
+reference thermodynamic keywords and various per-atom attributes, or
+invoke other computes, fixes, or variables when they are evaluated, so
+this is a very general means of generating per-atom quantities to use
+as {index}.
+
+:line
+
+This section explains the kinds of {input} values that can be used.
+Note that inputs reference global values, as contrasted with the
+{index} parameter which must reference per-atom values.
+
+If a value begins with "c_", a compute ID must follow which has been
+previously defined in the input script.  The compute must generate a
+global vector or array.  See the individual "compute"_compute.html doc
+page for details.  If no bracketed integer is appended, the vector
+calculated by the compute is used.  If a bracketed integer is
+appended, the Ith column of the array calculated by the compute is
+used.  Users can also write code for their own compute styles and "add
+them to LAMMPS"_Section_modify.html.  See the discussion above for how
+I can be specified with a wildcard asterisk to effectively specify
+multiple values.
+
+If a value begins with "f_", a fix ID must follow which has been
+previously defined in the input script.  The fix must generate a
+global vector or array.  See the individual "fix"_fix.html doc page
+for details.  Note that some fixes only produce their values on
+certain timesteps, which must be compatible with when compute
+global/atom references the values, else an error results.  If no
+bracketed integer is appended, the vector calculated by the fix is
+used.  If a bracketed integer is appended, the Ith column of the array
+calculated by the fix is used.  Users can also write code for their
+own fix style and "add them to LAMMPS"_Section_modify.html.  See the
+discussion above for how I can be specified with a wildcard asterisk
+to effectively specify multiple values.
+
+If a value begins with "v_", a variable name must follow which has
+been previously defined in the input script.  It must be a
+"vector-style variable"_variable.html.  Vector-style variables can
+reference thermodynamic keywords and various other attributes of
+atoms, or invoke other computes, fixes, or variables when they are
+evaluated, so this is a very general means of generating a vector of
+global quantities which the {index} parameter will reference for
+assignement of global values to atoms.
+
+:line
+
+[Output info:]
+
+If a single input is specified this compute produces a per-atom
+vector.  If multiple inputs are specified, this compute produces a
+per-atom array values, where the number of columns is equal to the
+number of inputs specified.  These values can be used by any command
+that uses per-atom vector or array values from a compute as input.
+See "Section 6.15"_Section_howto.html#howto_15 for an overview of
+LAMMPS output options.
+
+The per-atom vector or array values will be in whatever units the
+corresponsing input values are in.
+
+[Restrictions:] none
+
+[Related commands:]
+
+"compute"_compute.html, "fix"_fix.html, "variable"_variable.html,
+"compute chunk/atom"_compute_chunk_atom.html, "compute
+reduce"_compute_reduce.html
+
+[Default:] none
diff --git a/doc/src/lammps.book b/doc/src/lammps.book
index b83b2021c4..ec689adf15 100644
--- a/doc/src/lammps.book
+++ b/doc/src/lammps.book
@@ -311,6 +311,7 @@ compute_erotate_sphere.html
 compute_erotate_sphere_atom.html
 compute_event_displace.html
 compute_fep.html
+compute_global_atom.html
 compute_group_group.html
 compute_gyration.html
 compute_gyration_chunk.html
diff --git a/src/compute.cpp b/src/compute.cpp
index d306b0b34f..88ae70c067 100644
--- a/src/compute.cpp
+++ b/src/compute.cpp
@@ -40,7 +40,8 @@ int Compute::instance_total = 0;
 
 Compute::Compute(LAMMPS *lmp, int narg, char **arg) : Pointers(lmp),
   id(NULL), style(NULL),
-  vector(NULL), array(NULL), vector_atom(NULL), array_atom(NULL), vector_local(NULL), array_local(NULL),
+  vector(NULL), array(NULL), vector_atom(NULL),
+  array_atom(NULL), vector_local(NULL), array_local(NULL),
   tlist(NULL), vbiasall(NULL)
 {
   instance_me = instance_total++;
diff --git a/src/compute_bond_local.cpp b/src/compute_bond_local.cpp
index b866be6991..424059aa86 100644
--- a/src/compute_bond_local.cpp
+++ b/src/compute_bond_local.cpp
@@ -70,7 +70,7 @@ ComputeBondLocal::ComputeBondLocal(LAMMPS *lmp, int narg, char **arg) :
   // set velflag if compute any quantities based on velocities
 
   singleflag = 0;
-  ghostvelflag = 0;
+  velflag = 0;
   for (int i = 0; i < nvalues; i++) {
     if (bstyle[i] == ENGPOT || bstyle[i] == FORCE) singleflag = 1;
     if (bstyle[i] == VELVIB || bstyle[i] == OMEGA || bstyle[i] == ENGTRANS ||
diff --git a/src/compute_global_atom.cpp b/src/compute_global_atom.cpp
new file mode 100644
index 0000000000..b786043a34
--- /dev/null
+++ b/src/compute_global_atom.cpp
@@ -0,0 +1,540 @@
+/* ----------------------------------------------------------------------
+   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
+   http://lammps.sandia.gov, Sandia National Laboratories
+   Steve Plimpton, sjplimp@sandia.gov
+
+   Copyright (2003) Sandia Corporation.  Under the terms of Contract
+   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+   certain rights in this software.  This software is distributed under
+   the GNU General Public License.
+
+   See the README file in the top-level LAMMPS directory.
+------------------------------------------------------------------------- */
+
+#include <string.h>
+#include <stdlib.h>
+#include "compute_global_atom.h"
+#include "atom.h"
+#include "update.h"
+#include "domain.h"
+#include "modify.h"
+#include "fix.h"
+#include "force.h"
+#include "comm.h"
+#include "group.h"
+#include "input.h"
+#include "variable.h"
+#include "memory.h"
+#include "error.h"
+
+using namespace LAMMPS_NS;
+
+enum{COMPUTE,FIX,VARIABLE};
+enum{VECTOR,ARRAY};
+
+#define INVOKED_VECTOR 2
+#define INVOKED_ARRAY 4
+#define INVOKED_PERATOM 8
+
+#define BIG 1.0e20
+
+/* ---------------------------------------------------------------------- */
+
+ComputeGlobalAtom::ComputeGlobalAtom(LAMMPS *lmp, int narg, char **arg) :
+  Compute(lmp, narg, arg),
+  idref(NULL), which(NULL), argindex(NULL), value2index(NULL), ids(NULL),
+  indices(NULL), varatom(NULL), vecglobal(NULL)
+{
+  if (narg < 5) error->all(FLERR,"Illegal compute global/atom command");
+
+  peratom_flag = 1;
+
+  // process index arg
+
+  int iarg = 3;
+
+  if (strncmp(arg[iarg],"c_",2) == 0 ||
+      strncmp(arg[iarg],"f_",2) == 0 ||
+      strncmp(arg[iarg],"v_",2) == 0) {
+    if (arg[iarg][0] == 'c') whichref = COMPUTE;
+    else if (arg[iarg][0] == 'f') whichref = FIX;
+    else if (arg[iarg][0] == 'v') whichref = VARIABLE;
+    
+    int n = strlen(arg[iarg]);
+    char *suffix = new char[n];
+    strcpy(suffix,&arg[iarg][2]);
+    
+    char *ptr = strchr(suffix,'[');
+    if (ptr) {
+      if (suffix[strlen(suffix)-1] != ']')
+        error->all(FLERR,"Illegal compute global/atom command");
+      indexref = atoi(ptr+1);
+      *ptr = '\0';
+    } else indexref = 0;
+    
+    n = strlen(suffix) + 1;
+    idref = new char[n];
+    strcpy(idref,suffix);
+    delete [] suffix;
+  } else error->all(FLERR,"Illegal compute global/atom command");
+
+  iarg++;
+
+  // expand args if any have wildcard character "*"
+
+  int expand = 0;
+  char **earg;
+  int nargnew = input->expand_args(narg-iarg,&arg[iarg],1,earg);
+
+  if (earg != &arg[iarg]) expand = 1;
+  arg = earg;
+
+  // parse values until one isn't recognized
+
+  which = new int[nargnew];
+  argindex = new int[nargnew];
+  ids = new char*[nargnew];
+  value2index = new int[nargnew];
+  nvalues = 0;
+
+  iarg = 0;
+  while (iarg < nargnew) {
+    ids[nvalues] = NULL;
+    if (strncmp(arg[iarg],"c_",2) == 0 ||
+        strncmp(arg[iarg],"f_",2) == 0 ||
+        strncmp(arg[iarg],"v_",2) == 0) {
+      if (arg[iarg][0] == 'c') which[nvalues] = COMPUTE;
+      else if (arg[iarg][0] == 'f') which[nvalues] = FIX;
+      else if (arg[iarg][0] == 'v') which[nvalues] = VARIABLE;
+
+      int n = strlen(arg[iarg]);
+      char *suffix = new char[n];
+      strcpy(suffix,&arg[iarg][2]);
+
+      char *ptr = strchr(suffix,'[');
+      if (ptr) {
+        if (suffix[strlen(suffix)-1] != ']')
+          error->all(FLERR,"Illegal compute global/atom command");
+        argindex[nvalues] = atoi(ptr+1);
+        *ptr = '\0';
+      } else argindex[nvalues] = 0;
+
+      n = strlen(suffix) + 1;
+      ids[nvalues] = new char[n];
+      strcpy(ids[nvalues],suffix);
+      nvalues++;
+      delete [] suffix;
+
+    } else error->all(FLERR,"Illegal compute global/atom command");
+
+    iarg++;
+  }
+
+  // if wildcard expansion occurred, free earg memory from expand_args()
+
+  if (expand) {
+    for (int i = 0; i < nargnew; i++) delete [] earg[i];
+    memory->sfree(earg);
+  }
+
+  // setup and error check both index arg and values
+
+  if (whichref == COMPUTE) {
+    int icompute = modify->find_compute(idref);
+    if (icompute < 0)
+      error->all(FLERR,"Compute ID for compute global/atom does not exist");
+
+    if (!modify->compute[icompute]->peratom_flag)
+      error->all(FLERR,"Compute global/atom compute does not "
+                 "calculate a per-atom vector or array");
+    if (indexref == 0 &&
+        modify->compute[icompute]->size_peratom_cols != 0)
+      error->all(FLERR,"Compute global/atom compute does not "
+                 "calculate a per-atom vector");
+    if (indexref && modify->compute[icompute]->size_peratom_cols == 0)
+      error->all(FLERR,"Compute global/atom compute does not "
+                 "calculate a per-atom array");
+    if (indexref && indexref > modify->compute[icompute]->size_peratom_cols)
+      error->all(FLERR,
+                 "Compute global/atom compute array is accessed out-of-range");
+    
+  } else if (whichref == FIX) {
+    int ifix = modify->find_fix(idref);
+    if (ifix < 0)
+      error->all(FLERR,"Fix ID for compute global/atom does not exist");
+    if (!modify->fix[ifix]->peratom_flag)
+      error->all(FLERR,"Compute global/atom fix does not "
+                 "calculate a per-atom vector or array");
+    if (indexref == 0 &&
+        modify->fix[ifix]->size_peratom_cols != 0)
+      error->all(FLERR,"Compute global/atom fix does not "
+                 "calculate a per-atom vector");
+    if (indexref && modify->fix[ifix]->size_peratom_cols == 0)
+      error->all(FLERR,"Compute global/atom fix does not "
+                 "calculate a per-atom array");
+    if (indexref && indexref > modify->fix[ifix]->size_peratom_cols)
+      error->all(FLERR,
+                 "Compute global/atom fix array is accessed out-of-range");
+
+  } else if (whichref == VARIABLE) {
+    int ivariable = input->variable->find(idref);
+    if (ivariable < 0)
+      error->all(FLERR,"Variable name for compute global/atom does not exist");
+    if (input->variable->atomstyle(ivariable) == 0)
+      error->all(FLERR,"Compute global/atom variable is not "
+                 "atom-style variable");
+  }
+
+  for (int i = 0; i < nvalues; i++) {
+    if (which[i] == COMPUTE) {
+      int icompute = modify->find_compute(ids[i]);
+      if (icompute < 0)
+        error->all(FLERR,"Compute ID for compute global/atom does not exist");
+      if (argindex[i] == 0) {
+        if (!modify->compute[icompute]->vector_flag)
+          error->all(FLERR,"Compute global/atom compute does not "
+                     "calculate a global vector");
+      } else {
+        if (!modify->compute[icompute]->array_flag)
+          error->all(FLERR,"Compute global/atom compute does not "
+                     "calculate a global array");
+        if (argindex[i] > modify->compute[icompute]->size_array_cols)
+          error->all(FLERR,"Compute global/atom compute array is "
+                     "accessed out-of-range");
+      }
+
+    } else if (which[i] == FIX) {
+      int ifix = modify->find_fix(ids[i]);
+      if (ifix < 0)
+        error->all(FLERR,"Fix ID for compute global/atom does not exist");
+      if (argindex[i] == 0) {
+        if (!modify->fix[ifix]->vector_flag)
+          error->all(FLERR,"Compute global/atom fix does not "
+                     "calculate a global vector");
+      } else {
+        if (!modify->fix[ifix]->array_flag)
+          error->all(FLERR,"Compute global/atom fix does not "
+                     "calculate a global array");
+        if (argindex[i] > modify->fix[ifix]->size_array_cols)
+          error->all(FLERR,"Compute global/atom fix array is "
+                     "accessed out-of-range");
+      }
+
+    } else if (which[i] == VARIABLE) {
+      int ivariable = input->variable->find(ids[i]);
+      if (ivariable < 0)
+        error->all(FLERR,"Variable name for compute global/atom "
+                   "does not exist");
+      if (input->variable->vectorstyle(ivariable) == 0)
+        error->all(FLERR,"Compute global/atom variable is not "
+                   "vector-style variable");
+    }
+  }
+
+  // this compute produces either a peratom vector or array
+
+  if (nvalues == 1) size_peratom_cols = 0;
+  else size_peratom_cols = nvalues;
+
+  nmax = maxvector = 0;
+  indices = NULL;
+  varatom = NULL;
+  vecglobal = NULL;
+
+  vector_atom = NULL;
+  array_atom = NULL;
+}
+
+/* ---------------------------------------------------------------------- */
+
+ComputeGlobalAtom::~ComputeGlobalAtom()
+{
+  delete [] idref;
+
+  delete [] which;
+  delete [] argindex;
+  for (int m = 0; m < nvalues; m++) delete [] ids[m];
+  delete [] ids;
+  delete [] value2index;
+
+  memory->destroy(indices);
+  memory->destroy(varatom);
+  memory->destroy(vecglobal);
+  memory->destroy(vector_atom);
+  memory->destroy(array_atom);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeGlobalAtom::init()
+{
+  // set indices of all computes,fixes,variables
+
+  if (whichref == COMPUTE) {
+    int icompute = modify->find_compute(idref);
+    if (icompute < 0)
+      error->all(FLERR,"Compute ID for compute global/atom does not exist");
+    ref2index = icompute;
+  } else if (whichref == FIX) {
+    int ifix = modify->find_fix(idref);
+    if (ifix < 0)
+      error->all(FLERR,"Fix ID for compute global/atom does not exist");
+    ref2index = ifix;
+  } else if (whichref == VARIABLE) {
+    int ivariable = input->variable->find(idref);
+    if (ivariable < 0)
+      error->all(FLERR,"Variable name for compute global/atom does not exist");
+    ref2index = ivariable;
+  }
+  
+  for (int m = 0; m < nvalues; m++) {
+    if (which[m] == COMPUTE) {
+      int icompute = modify->find_compute(ids[m]);
+      if (icompute < 0)
+        error->all(FLERR,"Compute ID for compute global/atom does not exist");
+      value2index[m] = icompute;
+
+    } else if (which[m] == FIX) {
+      int ifix = modify->find_fix(ids[m]);
+      if (ifix < 0)
+        error->all(FLERR,"Fix ID for compute global/atom does not exist");
+      value2index[m] = ifix;
+
+    } else if (which[m] == VARIABLE) {
+      int ivariable = input->variable->find(ids[m]);
+      if (ivariable < 0)
+        error->all(FLERR,"Variable name for compute global/atom "
+                   "does not exist");
+      value2index[m] = ivariable;
+    }
+  }
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeGlobalAtom::compute_peratom()
+{
+  int i,j;
+
+  invoked_peratom = update->ntimestep;
+
+  // grow indices and output vector or array if necessary
+
+  if (atom->nmax > nmax) {
+    nmax = atom->nmax;
+    memory->destroy(indices);
+    memory->create(indices,nmax,"global/atom:indices");
+    if (nvalues == 1) {
+      memory->destroy(vector_atom);
+      memory->create(vector_atom,nmax,"global/atom:vector_atom");
+    } else {
+      memory->destroy(array_atom);
+      memory->create(array_atom,nmax,nvalues,"global/atom:array_atom");
+    }
+  }
+  
+  // setup current peratom indices
+  // integer indices are rounded down from double values
+  // indices are decremented from 1 to N -> 0 to N-1
+
+  int *mask = atom->mask;
+  int nlocal = atom->nlocal;
+  
+  if (whichref == COMPUTE) {
+    Compute *compute = modify->compute[ref2index];
+    
+    if (!(compute->invoked_flag & INVOKED_PERATOM)) {
+      compute->compute_peratom();
+      compute->invoked_flag |= INVOKED_PERATOM;
+    }
+
+    if (indexref == 0) {
+      double *compute_vector = compute->vector_atom;
+      for (i = 0; i < nlocal; i++)
+        if (mask[i] & groupbit) 
+          indices[i] = static_cast<int> (compute_vector[i]) - 1;
+    } else {
+      double **compute_array = compute->array_atom;
+      int im1 = indexref - 1;
+      for (i = 0; i < nlocal; i++)
+        if (mask[i] & groupbit) 
+          indices[i] = static_cast<int> (compute_array[i][im1]) - 1;
+    }
+  
+  } else if (whichref == FIX) {
+    if (update->ntimestep % modify->fix[ref2index]->peratom_freq)
+      error->all(FLERR,"Fix used in compute global/atom not "
+                 "computed at compatible time");
+    Fix *fix = modify->fix[ref2index];
+    
+    if (indexref == 0) {
+      double *fix_vector = fix->vector_atom;
+      for (i = 0; i < nlocal; i++)
+        if (mask[i] & groupbit) 
+          indices[i] = static_cast<int> (fix_vector[i]) - 1;
+    } else {
+      double **fix_array = fix->array_atom;
+      int im1 = indexref - 1;
+      for (i = 0; i < nlocal; i++)
+        if (mask[i] & groupbit) 
+          indices[i] = static_cast<int> (fix_array[i][im1]) - 1;
+    }
+    
+  } else if (whichref == VARIABLE) {
+    if (atom->nmax > nmax) {
+      nmax = atom->nmax;
+      memory->destroy(varatom);
+      memory->create(varatom,nmax,"global/atom:varatom");
+    }
+      
+    input->variable->compute_atom(ref2index,igroup,varatom,1,0);
+    for (i = 0; i < nlocal; i++)
+      if (mask[i] & groupbit)
+        indices[i] = static_cast<int> (varatom[i]) - 1;
+  }
+
+  // loop over values to fill output vector or array
+
+  for (int m = 0; m < nvalues; m++) {
+
+    // output = vector
+
+    if (argindex[m] == 0) {
+      int vmax;
+      double *source;
+
+      if (which[m] == COMPUTE) {
+        Compute *compute = modify->compute[value2index[m]];
+
+        if (!(compute->invoked_flag & INVOKED_VECTOR)) {
+          compute->compute_vector();
+          compute->invoked_flag |= INVOKED_VECTOR;
+        }
+        
+        source = compute->vector;
+        vmax = compute->size_vector;
+
+      } else if (which[m] == FIX) {
+        if (update->ntimestep % modify->fix[value2index[m]]->peratom_freq)
+          error->all(FLERR,"Fix used in compute global/atom not "
+                     "computed at compatible time");
+        Fix *fix = modify->fix[value2index[m]];
+        vmax = fix->size_vector;
+
+        if (vmax > maxvector) {
+          maxvector = vmax;
+          memory->destroy(vecglobal);
+          memory->create(vecglobal,maxvector,"global/atom:vecglobal");
+        }
+
+        for (i = 0; i < vmax; i++)
+          vecglobal[i] = fix->compute_vector(i);
+
+        source = vecglobal;
+
+      } else if (which[m] == VARIABLE) {
+        vmax = input->variable->compute_vector(value2index[m],&source);
+      }
+
+      if (nvalues == 1) {
+        for (i = 0; i < nlocal; i++) {
+          vector_atom[i] = 0.0;
+          if (mask[i] & groupbit) {
+            j = indices[i];
+            if (j >= 0 && j < vmax) vector_atom[i] = source[j];
+          }
+        }
+      } else {
+        for (i = 0; i < nlocal; i++) {
+          array_atom[i][m] = 0.0;
+          if (mask[i] & groupbit) {
+            j = indices[i];
+            if (j >= 0 && j < vmax) array_atom[i][m] = source[j];
+          }
+        }
+      }
+
+    // output = array
+
+    } else {
+      int vmax;
+      double *source;
+      int col = argindex[m] - 1;
+
+      if (which[m] == COMPUTE) {
+        Compute *compute = modify->compute[value2index[m]];
+
+        if (!(compute->invoked_flag & INVOKED_ARRAY)) {
+          compute->compute_array();
+          compute->invoked_flag |= INVOKED_ARRAY;
+        }
+          
+        double **compute_array = compute->array;
+        vmax = compute->size_array_rows;
+
+        if (vmax > maxvector) {
+          maxvector = vmax;
+          memory->destroy(vecglobal);
+          memory->create(vecglobal,maxvector,"global/atom:vecglobal");
+        }
+
+        for (i = 0; i < vmax; i++)
+          vecglobal[i] = compute_array[i][col];
+
+        source = vecglobal;
+
+      } else if (which[m] == FIX) {
+        if (update->ntimestep % modify->fix[value2index[m]]->peratom_freq)
+          error->all(FLERR,"Fix used in compute global/atom not "
+                     "computed at compatible time");
+        Fix *fix = modify->fix[value2index[m]];
+        vmax = fix->size_array_rows;
+
+        if (vmax > maxvector) {
+          maxvector = vmax;
+          memory->destroy(vecglobal);
+          memory->create(vecglobal,maxvector,"global/atom:vecglobal");
+        }
+
+        for (i = 0; i < vmax; i++)
+          vecglobal[i] = fix->compute_array(i,col);
+
+        source = vecglobal;
+
+      } else if (which[m] == VARIABLE) {
+        vmax = input->variable->compute_vector(value2index[m],&source);
+      }
+
+      if (nvalues == 1) {
+        for (i = 0; i < nlocal; i++) {
+          vector_atom[i] = 0.0;
+          if (mask[i] & groupbit) {
+            j = indices[i];
+            if (j >= 0 && j < vmax) vector_atom[i] = source[j];
+          }
+        }
+      } else {
+        for (i = 0; i < nlocal; i++) {
+          array_atom[i][m] = 0.0;
+          if (mask[i] & groupbit) {
+            j = indices[i];
+            if (j >= 0 && j < vmax) array_atom[i][m] = source[j];
+          }
+        }
+      }
+    }
+  }
+}
+
+/* ----------------------------------------------------------------------
+   memory usage of local atom-based array
+------------------------------------------------------------------------- */
+
+double ComputeGlobalAtom::memory_usage()
+{
+  double bytes = nmax*nvalues * sizeof(double);
+  bytes += nmax * sizeof(int);                    // indices
+  if (varatom) bytes += nmax * sizeof(double);    // varatom
+  bytes += maxvector * sizeof(double);            // vecglobal
+  return bytes;
+}
diff --git a/src/compute_global_atom.h b/src/compute_global_atom.h
new file mode 100644
index 0000000000..439720f712
--- /dev/null
+++ b/src/compute_global_atom.h
@@ -0,0 +1,143 @@
+/* -*- c++ -*- ----------------------------------------------------------
+   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
+   http://lammps.sandia.gov, Sandia National Laboratories
+   Steve Plimpton, sjplimp@sandia.gov
+
+   Copyright (2003) Sandia Corporation.  Under the terms of Contract
+   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+   certain rights in this software.  This software is distributed under
+   the GNU General Public License.
+
+   See the README file in the top-level LAMMPS directory.
+------------------------------------------------------------------------- */
+
+#ifdef COMPUTE_CLASS
+
+ComputeStyle(global/atom,ComputeGlobalAtom)
+
+#else
+
+#ifndef LMP_COMPUTE_GLOBAL_ATOM_H
+#define LMP_COMPUTE_GLOBAL_ATOM_H
+
+#include "compute.h"
+
+namespace LAMMPS_NS {
+
+class ComputeGlobalAtom : public Compute {
+ public:
+  ComputeGlobalAtom(class LAMMPS *, int, char **);
+  virtual ~ComputeGlobalAtom();
+  void init();
+  void compute_peratom();
+  double memory_usage();
+
+ protected:
+  int whichref,indexref,ref2index;
+  char *idref;
+
+  int nvalues;
+  int *which,*argindex,*value2index;
+  char **ids;
+
+  int nmax,maxvector;
+  int *indices;
+  double *varatom;
+  double *vecglobal;
+};
+
+}
+
+#endif
+#endif
+
+/* ERROR/WARNING messages:
+
+E: Illegal ... command
+
+Self-explanatory.  Check the input script syntax and compare to the
+documentation for the command.  You can use -echo screen as a
+command-line option when running LAMMPS to see the offending line.
+
+E: Region ID for compute reduce/region does not exist
+
+Self-explanatory.
+
+E: Compute reduce replace requires min or max mode
+
+Self-explanatory.
+
+E: Invalid replace values in compute reduce
+
+Self-explanatory.
+
+E: Compute ID for compute reduce does not exist
+
+Self-explanatory.
+
+E: Compute reduce compute does not calculate a per-atom vector
+
+Self-explanatory.
+
+E: Compute reduce compute does not calculate a per-atom array
+
+Self-explanatory.
+
+E: Compute reduce compute array is accessed out-of-range
+
+An index for the array is out of bounds.
+
+E: Compute reduce compute does not calculate a local vector
+
+Self-explanatory.
+
+E: Compute reduce compute does not calculate a local array
+
+Self-explanatory.
+
+E: Compute reduce compute calculates global values
+
+A compute that calculates peratom or local values is required.
+
+E: Fix ID for compute reduce does not exist
+
+Self-explanatory.
+
+E: Compute reduce fix does not calculate a per-atom vector
+
+Self-explanatory.
+
+E: Compute reduce fix does not calculate a per-atom array
+
+Self-explanatory.
+
+E: Compute reduce fix array is accessed out-of-range
+
+An index for the array is out of bounds.
+
+E: Compute reduce fix does not calculate a local vector
+
+Self-explanatory.
+
+E: Compute reduce fix does not calculate a local array
+
+Self-explanatory.
+
+E: Compute reduce fix calculates global values
+
+A fix that calculates peratom or local values is required.
+
+E: Variable name for compute reduce does not exist
+
+Self-explanatory.
+
+E: Compute reduce variable is not atom-style variable
+
+Self-explanatory.
+
+E: Fix used in compute reduce not computed at compatible time
+
+Fixes generate their values on specific timesteps.  Compute reduce is
+requesting a value on a non-allowed timestep.
+
+*/
diff --git a/src/fix_ave_time.cpp b/src/fix_ave_time.cpp
index ff47af03a8..56a2458aad 100644
--- a/src/fix_ave_time.cpp
+++ b/src/fix_ave_time.cpp
@@ -43,8 +43,10 @@ enum{SCALAR,VECTOR};
 
 FixAveTime::FixAveTime(LAMMPS *lmp, int narg, char **arg) :
   Fix(lmp, narg, arg),
-  nvalues(0), which(NULL), argindex(NULL), value2index(NULL), offcol(NULL), varlen(NULL), ids(NULL),
-  fp(NULL), offlist(NULL), format(NULL), format_user(NULL), vector(NULL), vector_total(NULL), vector_list(NULL),
+  nvalues(0), which(NULL), argindex(NULL), value2index(NULL), 
+  offcol(NULL), varlen(NULL), ids(NULL),
+  fp(NULL), offlist(NULL), format(NULL), format_user(NULL), 
+  vector(NULL), vector_total(NULL), vector_list(NULL),
   column(NULL), array(NULL), array_total(NULL), array_list(NULL)
 {
   if (narg < 7) error->all(FLERR,"Illegal fix ave/time command");
-- 
GitLab