diff --git a/src/VORONOI/README b/src/VORONOI/README
index 746859323e1990f4de25f47c945822585542b313..118abfa296c68daff2d88021a29f3dd3b02609b6 100644
--- a/src/VORONOI/README
+++ b/src/VORONOI/README
@@ -31,7 +31,7 @@ compute the tesselation locally on each processor.
 == Run tests ==
 
 Run the includes test input file
-./lmp_serial < lammps/examples/voronoi/in.vorotest | grep '^TEST_'
+./lmp_serial < lammps/examples/voronoi/in.voronoi | grep '^TEST_'
 
 The output should conclude with 'TEST_DONE' and every line should 
 report an error of 0%. 
diff --git a/src/VORONOI/compute_voronoi_atom.cpp b/src/VORONOI/compute_voronoi_atom.cpp
index 83ce6376a31dc9e73ca57ab7f0d18d4d3ed4f41f..c8868ce5118a92906fca223f7e0b2669270de313 100644
--- a/src/VORONOI/compute_voronoi_atom.cpp
+++ b/src/VORONOI/compute_voronoi_atom.cpp
@@ -29,6 +29,7 @@
 #include "comm.h"
 #include "variable.h"
 #include "input.h"
+#include "force.h"
 
 #include <vector>
 
@@ -50,56 +51,62 @@ ComputeVoronoi::ComputeVoronoi(LAMMPS *lmp, int narg, char **arg) :
   fthresh = ethresh = 0.0;
   radstr = NULL;
   onlyGroup = false;
+  occupation = false;
+
+  con_mono = NULL;
+  con_poly = NULL;
+  tags = occvec = sendocc = lroot = lnext = NULL;
 
   int iarg = 3;
   while ( iarg<narg ) {
-    if (strcmp(arg[iarg],"only_group") == 0) {
+    if (strcmp(arg[iarg], "occupation") == 0) {
+      occupation = true;
+      iarg++;
+    } 
+    else if (strcmp(arg[iarg], "only_group") == 0) {
       onlyGroup = true;
       iarg++;
     } 
-    else if (strcmp(arg[iarg],"radius") == 0) {
-      if (iarg + 2 > narg || strstr(arg[iarg+1],"v_") != arg[iarg+1] ) 
-        error->all(FLERR,"Illegal compute voronoi/atom command");
+    else if (strcmp(arg[iarg], "radius") == 0) {
+      if (iarg + 2 > narg || strstr(arg[iarg+1],"v_") != arg[iarg+1] ) error->all(FLERR,"Missing atom style variable for radical voronoi tesselation radius.");
       int n = strlen(&arg[iarg+1][2]) + 1;
       radstr = new char[n];
       strcpy(radstr,&arg[iarg+1][2]);
       iarg += 2;
     } 
-    else if (strcmp(arg[iarg],"surface") == 0) {
-      if (iarg + 2 > narg) 
-        error->all(FLERR,"Illegal compute voronoi/atom command");
+    else if (strcmp(arg[iarg], "surface") == 0) {
+      if (iarg + 2 > narg) error->all(FLERR,"Missing group name after keyword 'surface'.");
       // group all is a special case where we just skip group testing
       if(strcmp(arg[iarg+1], "all") == 0) {
         surface = VOROSURF_ALL;
       } else {
         sgroup = group->find(arg[iarg+1]);
-        if (sgroup == -1) 
-          error->all(FLERR,"Could not find compute/voronoi surface group ID");
+        if (sgroup == -1) error->all(FLERR,"Could not find compute/voronoi surface group ID");
         sgroupbit = group->bitmask[sgroup]; 
         surface = VOROSURF_GROUP;
       }
       size_peratom_cols = 3;
       iarg += 2;
     } else if (strcmp(arg[iarg], "edge_histo") == 0) {
-      if (iarg + 2 > narg) 
-        error->all(FLERR,"Illegal compute voronoi/atom command");
-      maxedge = atoi(arg[iarg+1]);
+      if (iarg + 2 > narg) error->all(FLERR,"Missing maximum edge count after keyword 'edge_histo'.");
+      maxedge = force->inumeric(FLERR,arg[iarg+1]);
       iarg += 2;
     } else if (strcmp(arg[iarg], "face_threshold") == 0) {
-      if (iarg + 2 > narg) 
-        error->all(FLERR,"Illegal compute voronoi/atom command");
-      fthresh = atof(arg[iarg+1]);
+      if (iarg + 2 > narg) error->all(FLERR,"Missing minimum face area after keyword 'face_threshold'.");
+      fthresh = force->numeric(FLERR,arg[iarg+1]);
       iarg += 2;
     } else if (strcmp(arg[iarg], "edge_threshold") == 0) {
-      if (iarg + 2 > narg) 
-        error->all(FLERR,"Illegal compute voronoi/atom command");
-      ethresh = atof(arg[iarg+1]);
+      if (iarg + 2 > narg) error->all(FLERR,"Missing minimum edge length after keyword 'edge_threshold'.");
+      ethresh = force->numeric(FLERR,arg[iarg+1]);
       iarg += 2;
     } 
     else 
       error->all(FLERR,"Illegal compute voronoi/atom command");
   }
 
+  if (occupation && ( surface!=VOROSURF_NONE || maxedge>0 ) ) 
+    error->all(FLERR,"Illegal compute voronoi/atom command (occupation and (surface or edges))");
+
   nmax = rmax = 0;
   edge = rfield = sendvector = NULL;
   voro = NULL;
@@ -122,17 +129,25 @@ ComputeVoronoi::~ComputeVoronoi()
   memory->destroy(sendvector);
   memory->destroy(voro);
   delete[] radstr;
+
+  // voro++ container classes
+  delete con_mono;
+  delete con_poly;
+
+  // occupation analysis stuff
+  memory->destroy(lroot);
+  memory->destroy(lnext);
+  memory->destroy(occvec);
+#ifdef NOTINPLACE
+  memory->destroy(sendocc);
+#endif
+  memory->destroy(tags);
 }
 
 /* ---------------------------------------------------------------------- */
 
 void ComputeVoronoi::init()
 {
-  int count = 0;
-  for (int i = 0; i < modify->ncompute; i++)
-    if (strcmp(modify->compute[i]->style,"voronoi/atom") == 0) count++;
-  if (count > 1 && comm->me == 0)
-    error->warning(FLERR,"More than one compute voronoi/atom command");
 }
 
 /* ----------------------------------------------------------------------
@@ -141,13 +156,9 @@ void ComputeVoronoi::init()
 
 void ComputeVoronoi::compute_peratom()
 {
-  int i;
-  const double e = 0.01;
-
   invoked_peratom = update->ntimestep;
 
   // grow per atom array if necessary
-
   int nlocal = atom->nlocal;
   if (nlocal > nmax) {
     memory->destroy(voro);
@@ -156,6 +167,48 @@ void ComputeVoronoi::compute_peratom()
     array_atom = voro;
   }
 
+  // decide between occupation or per-frame tesselation modes
+  if (occupation) {
+    // build cells only once
+    int i, j, 
+        nall = nlocal + atom->nghost;
+    if (con_mono==NULL && con_poly==NULL) {
+      // generate the voronoi cell network for the initial structure
+      buildCells();
+
+      // save tags of atoms (i.e. of each voronoi cell)
+      memory->create(tags,nall,"voronoi/atom:tags");
+      for (i=0; i<nall; i++) tags[i] = atom->tag[i];
+
+      // linked list structure for cell occupation count on the atoms
+      oldnall= nall;
+      memory->create(lroot,nall,"voronoi/atom:lroot"); // point to first atom index in cell (or -1 for empty cell)
+      lnext = NULL; 
+      lmax = 0;
+  
+      // build the occupation buffer
+      oldnatoms = atom->natoms;
+      memory->create(occvec,oldnatoms,"voronoi/atom:occvec");
+#ifdef NOTINPLACE
+      memory->create(sendocc,oldnatoms,"voronoi/atom:sendocc");
+#endif
+    }
+
+    // get the occupation of each original voronoi cell
+    checkOccupation();
+  } else {
+    // build cells for each output
+    buildCells();
+    loopCells();
+  }
+}
+
+void ComputeVoronoi::buildCells()
+{
+  int i, j;
+  const double e = 0.01;
+  int nlocal = atom->nlocal;
+
   // in the onlyGroup mode we are not setting values for all atoms later in the voro loop
   // initialize everything to zero here
   if (onlyGroup) {
@@ -182,7 +235,7 @@ void ComputeVoronoi::compute_peratom()
     syz = domain->yz/my;
 
     // cutghost is in lamda coordinates for triclinic boxes, use subxx_lamda
-    double *h = domain->h;
+    double *h = domain->h, cuttri[3];
     sublo_bound[0] = h[0]*sublo_lamda[0] + h[5]*sublo_lamda[1] + h[4]*sublo_lamda[2] + boxlo[0];
     sublo_bound[1] = h[1]*sublo_lamda[1] + h[3]*sublo_lamda[2] + boxlo[1];
     sublo_bound[2] = h[2]*sublo_lamda[2] + boxlo[2];
@@ -218,15 +271,14 @@ void ComputeVoronoi::compute_peratom()
   // initialize voro++ container
   // preallocates 8 atoms per cell
   // voro++ allocates more memory if needed
-  voronoicell_neighbor c;
   int *mask = atom->mask;
-  if(radstr) {
+  if (radstr) {
     // check and fetch atom style variable data
     int radvar = input->variable->find(radstr);
     if (radvar < 0)
-      error->all(FLERR,"Variable name for voronoi radius does not exist");
+            error->all(FLERR,"Variable name for voronoi radius set does not exist");
     if (!input->variable->atomstyle(radvar))
-      error->all(FLERR,"Variable for voronoi radius is not atom style");
+            error->all(FLERR,"Variable for voronoi radius is not atom style");
     // prepare destination buffer for variable evaluation
     if (nlocal > rmax) {
       memory->destroy(rfield);
@@ -240,7 +292,8 @@ void ComputeVoronoi::compute_peratom()
     comm->forward_comm_compute(this);
 
     // polydisperse voro++ container
-    container_poly con(sublo_bound[0]-cut_bound[0]-e,subhi_bound[0]+cut_bound[0]+e,
+    delete con_poly;
+    con_poly = new container_poly(sublo_bound[0]-cut_bound[0]-e,subhi_bound[0]+cut_bound[0]+e,
                        sublo_bound[1]-cut_bound[1]-e,subhi_bound[1]+cut_bound[1]+e,
                        sublo_bound[2]-cut_bound[2]-e,subhi_bound[2]+cut_bound[2]+e,
                        int(n[0]),int(n[1]),int(n[2]),false,false,false,8); 
@@ -248,17 +301,11 @@ void ComputeVoronoi::compute_peratom()
     // pass coordinates for local and ghost atoms to voro++
     for (i = 0; i < nall; i++)
       if( !onlyGroup || (mask[i] & groupbit) )
-        con.put(i,x[i][0],x[i][1],x[i][2],rfield[i]);
-
-    // invoke voro++ and fetch results for owned atoms in group
-    c_loop_all cl(con);
-    if (cl.start()) do if (con.compute_cell(c,cl)) {
-      i = cl.pid();
-      processCell(c,i);
-    } while (cl.inc());
+        con_poly->put(i,x[i][0],x[i][1],x[i][2],rfield[i]);
   } else {
     // monodisperse voro++ container
-    container con(sublo_bound[0]-cut_bound[0]-e,subhi_bound[0]+cut_bound[0]+e,
+    delete con_mono;
+    con_mono = new container(sublo_bound[0]-cut_bound[0]-e,subhi_bound[0]+cut_bound[0]+e,
                   sublo_bound[1]-cut_bound[1]-e,subhi_bound[1]+cut_bound[1]+e,
                   sublo_bound[2]-cut_bound[2]-e,subhi_bound[2]+cut_bound[2]+e,
                   int(n[0]),int(n[1]),int(n[2]),false,false,false,8); 
@@ -266,11 +313,102 @@ void ComputeVoronoi::compute_peratom()
     // pass coordinates for local and ghost atoms to voro++
     for (i = 0; i < nall; i++)
       if( !onlyGroup || (mask[i] & groupbit) )
-        con.put(i,x[i][0],x[i][1],x[i][2]);
+        con_mono->put(i,x[i][0],x[i][1],x[i][2]);
+  }
+}
+
+void ComputeVoronoi::checkOccupation()
+{
+  // clear occupation vector
+  memset(occvec, 0, oldnatoms*sizeof(*occvec));
+
+  int i, j, k,
+      nlocal = atom->nlocal,
+      nall = atom->nghost + nlocal;
+  double rx, ry, rz,
+         **x = atom->x;
+
+  // prepare destination buffer for variable evaluation
+  if (nall > lmax) {
+    memory->destroy(lnext);
+    lmax = atom->nmax;
+    memory->create(lnext,lmax,"voronoi/atom:lnext");
+  }
+
+  // clear lroot
+  for (i=0; i<oldnall; ++i) lroot[i] = -1;
+
+  // clear lnext
+  for (i=0; i<nall; ++i) lnext[i] = -1;
+
+  // loop over all local atoms and find out in which of the local first frame voronoi cells the are in
+  // (need to loop over ghosts, too, to get correct occupation numbers for the second column)
+  for (i=0; i<nall; ++i) {
+    // again: find_voronoi_cell() should be in the common base class. Why it is not, I don't know. Ask the voro++ author.
+    if ((  radstr && con_poly->find_voronoi_cell(x[i][0], x[i][1], x[i][2], rx, ry, rz, k)) ||
+        ( !radstr && con_mono->find_voronoi_cell(x[i][0], x[i][1], x[i][2], rx, ry, rz, k) )) {
+      // increase occupation count of this particular cell
+      // only for local atoms, as we do an MPI reduce sum later
+      if (i<nlocal) occvec[tags[k]-1]++;
+
+      // add this atom to the linked list of cell j
+      if (lroot[k]<0) 
+        lroot[k]=i;
+      else {
+        j = lroot[k];
+        while (lnext[j]>=0) j=lnext[j];
+        lnext[j] = i; 
+      }
+    }
+  }
+
+  // MPI sum occupation
+#ifdef NOTINPLACE
+  memcpy(sendocc, occvec, oldnatoms*sizeof(*occvec));
+  MPI_Allreduce(sendocc, occvec, oldnatoms, MPI_INT, MPI_SUM, world);
+#else
+  MPI_Allreduce(MPI_IN_PLACE, occvec, oldnatoms, MPI_INT, MPI_SUM, world);
+#endif
+
+  // determine the total number of atoms in this atom's currently occupied cell
+  int c;
+  for (i=0; i<oldnall; i++) { // loop over lroot (old voronoi cells)
+    // count
+    c = 0;
+    j = lroot[i];
+    while (j>=0) {
+      c++;
+      j = lnext[j];
+    }
+    // set
+    j = lroot[i];
+    while (j>=0) {
+      voro[j][1] = c;
+      j = lnext[j];
+    }
+  }
+
+  // cherry pick currently owned atoms 
+  for (i=0; i<nlocal; i++) {
+    // set the new atom count in the atom's first frame voronoi cell
+    voro[i][0] = occvec[atom->tag[i]-1];
+  }
+}
 
-    // invoke voro++ and fetch results for owned atoms in group
-    c_loop_all cl(con);
-    if (cl.start()) do if (con.compute_cell(c,cl)) {
+void ComputeVoronoi::loopCells()
+{
+  // invoke voro++ and fetch results for owned atoms in group
+  voronoicell_neighbor c;
+  int i;
+  if (radstr) {
+    c_loop_all cl(*con_poly);
+    if (cl.start()) do if (con_poly->compute_cell(c,cl)) {
+      i = cl.pid();
+      processCell(c,i);
+    } while (cl.inc());
+  } else {
+    c_loop_all cl(*con_mono);
+    if (cl.start()) do if (con_mono->compute_cell(c,cl)) {
       i = cl.pid();
       processCell(c,i);
     } while (cl.inc());
diff --git a/src/VORONOI/compute_voronoi_atom.h b/src/VORONOI/compute_voronoi_atom.h
index 1d6d335073f0ad01b25c55f60fc2d8b40800cac8..f762464f6beea5b87ee553845ed32e616c854ad2 100644
--- a/src/VORONOI/compute_voronoi_atom.h
+++ b/src/VORONOI/compute_voronoi_atom.h
@@ -38,6 +38,12 @@ class ComputeVoronoi : public Compute {
   void unpack_comm(int, int, double *);
 
  private:
+  voro::container *con_mono;
+  voro::container_poly *con_poly;
+
+  void buildCells();
+  void checkOccupation();
+  void loopCells();
   void processCell(voro::voronoicell_neighbor&, int);
 
   int nmax, rmax, maxedge, sgroupbit;
@@ -46,7 +52,10 @@ class ComputeVoronoi : public Compute {
   double **voro;
   double *edge, *sendvector, *rfield;
   enum { VOROSURF_NONE, VOROSURF_ALL, VOROSURF_GROUP } surface;
-  bool onlyGroup;
+  bool onlyGroup, occupation;
+
+  tagint *tags;
+  int *occvec, *sendocc, *lroot, *lnext, lmax, oldnatoms, oldnall;
 };
 
 }
@@ -62,21 +71,12 @@ 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: Could not find compute/voronoi surface group ID
+E: Compute voronoi/atom not allowed for triclinic boxes
 
-Self-explanatory.
+This is a current restriction of this command.
 
 W: More than one compute voronoi/atom command
 
 It is not efficient to use compute voronoi/atom more than once.
 
-E: Variable name for voronoi radius does not exist
-
-Self-explanatory.
-
-E: Variable for voronoi radius is not atom style
-
-The variable used for this command must be an atom-style variable.
-See the variable command for details.
-
 */