Skip to content
Snippets Groups Projects
compute_voronoi_atom.cpp 20.6 KiB
Newer Older
sjplimp's avatar
sjplimp committed
/* ----------------------------------------------------------------------
   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
   http://lammps.sandia.gov, Sandia National Laboratories
   Steve Plimpton, sjplimp@sandia.gov

   Copyright (2003) Sandia Corporation.  Under the terms of Contract
   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
   certain rights in this software.  This software is distributed under
   the GNU General Public License.

   See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */

/* ----------------------------------------------------------------------
   Contributing author: Daniel Schwen
------------------------------------------------------------------------- */

sjplimp's avatar
 
sjplimp committed
#include <mpi.h>
#include <cmath>
#include <cstring>
#include <cstdlib>
sjplimp's avatar
sjplimp committed
#include "compute_voronoi_atom.h"
#include "atom.h"
sjplimp's avatar
 
sjplimp committed
#include "group.h"
sjplimp's avatar
sjplimp committed
#include "update.h"
#include "modify.h"
#include "domain.h"
#include "memory.h"
#include "error.h"
#include "comm.h"
sjplimp's avatar
 
sjplimp committed
#include "variable.h"
#include "input.h"
sjplimp's avatar
 
sjplimp committed
#include "force.h"
sjplimp's avatar
sjplimp committed

#include <vector>

using namespace LAMMPS_NS;
using namespace voro;

athomps's avatar
athomps committed
#define FACESDELTA 10000

sjplimp's avatar
sjplimp committed
/* ---------------------------------------------------------------------- */

ComputeVoronoi::ComputeVoronoi(LAMMPS *lmp, int narg, char **arg) :
  Compute(lmp, narg, arg), con_mono(NULL), con_poly(NULL),
  radstr(NULL), voro(NULL), edge(NULL), sendvector(NULL),
  rfield(NULL), tags(NULL), occvec(NULL), sendocc(NULL),
  lroot(NULL), lnext(NULL), faces(NULL)
sjplimp's avatar
sjplimp committed
{
sjplimp's avatar
 
sjplimp committed
  int sgroup;
sjplimp's avatar
 
sjplimp committed

sjplimp's avatar
sjplimp committed
  size_peratom_cols = 2;
sjplimp's avatar
 
sjplimp committed
  peratom_flag = 1;
sjplimp's avatar
 
sjplimp committed
  comm_forward = 1;
athomps's avatar
athomps committed
  faces_flag = 0;
sjplimp's avatar
sjplimp committed

sjplimp's avatar
 
sjplimp committed
  surface = VOROSURF_NONE;
  maxedge = 0;
  fthresh = ethresh = 0.0;
  radstr = NULL;
  onlyGroup = false;
sjplimp's avatar
 
sjplimp committed
  occupation = false;

  con_mono = NULL;
  con_poly = NULL;
sjplimp's avatar
 
sjplimp committed
  tags = NULL;
sjplimp's avatar
 
sjplimp committed
  oldmaxtag = 0;
sjplimp's avatar
 
sjplimp committed
  occvec = sendocc = lroot = lnext = NULL;
athomps's avatar
athomps committed
  faces = NULL;
sjplimp's avatar
 
sjplimp committed

  int iarg = 3;
  while ( iarg<narg ) {
sjplimp's avatar
 
sjplimp committed
    if (strcmp(arg[iarg], "occupation") == 0) {
      occupation = true;
      iarg++;
sjplimp's avatar
 
sjplimp committed
    }
sjplimp's avatar
 
sjplimp committed
    else if (strcmp(arg[iarg], "only_group") == 0) {
sjplimp's avatar
 
sjplimp committed
      onlyGroup = true;
      iarg++;
sjplimp's avatar
 
sjplimp committed
    }
sjplimp's avatar
 
sjplimp committed
    else if (strcmp(arg[iarg], "radius") == 0) {
sjplimp's avatar
 
sjplimp committed
      if (iarg + 2 > narg || strstr(arg[iarg+1],"v_") != arg[iarg+1] )
Axel Kohlmeyer's avatar
Axel Kohlmeyer committed
        error->all(FLERR,"Illegal compute voronoi/atom command");
sjplimp's avatar
 
sjplimp committed
      int n = strlen(&arg[iarg+1][2]) + 1;
      radstr = new char[n];
      strcpy(radstr,&arg[iarg+1][2]);
      iarg += 2;
sjplimp's avatar
 
sjplimp committed
    }
sjplimp's avatar
 
sjplimp committed
    else if (strcmp(arg[iarg], "surface") == 0) {
sjplimp's avatar
 
sjplimp committed
      if (iarg + 2 > narg) error->all(FLERR,"Illegal compute voronoi/atom command");
sjplimp's avatar
 
sjplimp committed
      // 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]);
sjplimp's avatar
 
sjplimp committed
        if (sgroup == -1) error->all(FLERR,"Could not find compute/voronoi surface group ID");
sjplimp's avatar
 
sjplimp committed
        sgroupbit = group->bitmask[sgroup];
sjplimp's avatar
 
sjplimp committed
        surface = VOROSURF_GROUP;
      }
      size_peratom_cols = 3;
      iarg += 2;
    } else if (strcmp(arg[iarg], "edge_histo") == 0) {
sjplimp's avatar
 
sjplimp committed
      if (iarg + 2 > narg) error->all(FLERR,"Illegal compute voronoi/atom command");
sjplimp's avatar
 
sjplimp committed
      maxedge = force->inumeric(FLERR,arg[iarg+1]);
sjplimp's avatar
 
sjplimp committed
      iarg += 2;
    } else if (strcmp(arg[iarg], "face_threshold") == 0) {
sjplimp's avatar
 
sjplimp committed
      if (iarg + 2 > narg) error->all(FLERR,"Illegal compute voronoi/atom command");
sjplimp's avatar
 
sjplimp committed
      fthresh = force->numeric(FLERR,arg[iarg+1]);
sjplimp's avatar
 
sjplimp committed
      iarg += 2;
    } else if (strcmp(arg[iarg], "edge_threshold") == 0) {
sjplimp's avatar
 
sjplimp committed
      if (iarg + 2 > narg) error->all(FLERR,"Illegal compute voronoi/atom command");
sjplimp's avatar
 
sjplimp committed
      ethresh = force->numeric(FLERR,arg[iarg+1]);
sjplimp's avatar
 
sjplimp committed
      iarg += 2;
athomps's avatar
athomps committed
    } else if (strcmp(arg[iarg], "neighbors") == 0) {
      if (iarg + 2 > narg) error->all(FLERR,"Illegal compute voronoi/atom command");
      if (strcmp(arg[iarg+1],"yes") == 0) faces_flag = 1;
      else if (strcmp(arg[iarg+1],"no") == 0) faces_flag = 0;
      else error->all(FLERR,"Illegal compute voronoi/atom command");
      iarg += 2;
athomps's avatar
athomps committed
    } else if (strcmp(arg[iarg], "peratom") == 0) {
      if (iarg + 2 > narg) error->all(FLERR,"Illegal compute voronoi/atom command");
      if (strcmp(arg[iarg+1],"yes") == 0) peratom_flag = 1;
      else if (strcmp(arg[iarg+1],"no") == 0) peratom_flag = 0;
      else error->all(FLERR,"Illegal compute voronoi/atom command");
      iarg += 2;
sjplimp's avatar
 
sjplimp committed
    }
sjplimp's avatar
 
sjplimp committed
    else error->all(FLERR,"Illegal compute voronoi/atom command");
sjplimp's avatar
 
sjplimp committed
  }

sjplimp's avatar
 
sjplimp committed
  if (occupation && ( surface!=VOROSURF_NONE || maxedge>0 ) )
sjplimp's avatar
 
sjplimp committed
    error->all(FLERR,"Illegal compute voronoi/atom command (occupation and (surface or edges))");

sjplimp's avatar
sjplimp committed
  if (occupation && (atom->map_style == 0))
    error->all(FLERR,"Compute voronoi/atom occupation requires an atom map, see atom_modify");

sjplimp's avatar
 
sjplimp committed
  nmax = rmax = 0;
  edge = rfield = sendvector = NULL;
sjplimp's avatar
sjplimp committed
  voro = NULL;
sjplimp's avatar
 
sjplimp committed

  if ( maxedge > 0 ) {
    vector_flag = 1;
    size_vector = maxedge+1;
    memory->create(edge,maxedge+1,"voronoi/atom:edge");
    memory->create(sendvector,maxedge+1,"voronoi/atom:sendvector");
    vector = edge;
  }
athomps's avatar
athomps committed

  // store local face data: i, j, area

  if (faces_flag) {
    local_flag = 1;
    size_local_cols = 3;
athomps's avatar
athomps committed
    size_local_rows = 0;
athomps's avatar
athomps committed
    nfacesmax = 0;
  }
sjplimp's avatar
sjplimp committed
}

/* ---------------------------------------------------------------------- */

ComputeVoronoi::~ComputeVoronoi()
{
sjplimp's avatar
 
sjplimp committed
  memory->destroy(edge);
  memory->destroy(rfield);
  memory->destroy(sendvector);
sjplimp's avatar
sjplimp committed
  memory->destroy(voro);
sjplimp's avatar
 
sjplimp committed
  delete[] radstr;
sjplimp's avatar
 
sjplimp committed

  // 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);
athomps's avatar
athomps committed
  memory->destroy(faces);
sjplimp's avatar
sjplimp committed
}

/* ---------------------------------------------------------------------- */

void ComputeVoronoi::init()
{
sjplimp's avatar
 
sjplimp committed
  if (occupation && (atom->tag_enable == 0))
    error->all(FLERR,"Compute voronoi/atom occupation requires atom IDs");
sjplimp's avatar
sjplimp committed
}

/* ----------------------------------------------------------------------
   gather compute vector data from other nodes
------------------------------------------------------------------------- */

void ComputeVoronoi::compute_peratom()
{
  invoked_peratom = update->ntimestep;

  // grow per atom array if necessary
sjplimp's avatar
 
sjplimp committed
  if (atom->nmax > nmax) {
sjplimp's avatar
sjplimp committed
    memory->destroy(voro);
    nmax = atom->nmax;
    memory->create(voro,nmax,size_peratom_cols,"voronoi/atom:voro");
    array_atom = voro;
  }

sjplimp's avatar
 
sjplimp committed
  // decide between occupation or per-frame tesselation modes
  if (occupation) {
    // build cells only once
sjplimp's avatar
 
sjplimp committed
    int i, nall = atom->nlocal + atom->nghost;
sjplimp's avatar
 
sjplimp committed
    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)
sjplimp's avatar
 
sjplimp committed
      lnext = NULL;
sjplimp's avatar
 
sjplimp committed
      lmax = 0;
sjplimp's avatar
 
sjplimp committed

sjplimp's avatar
 
sjplimp committed
      // build the occupation buffer.
      // NOTE: we cannot make it of length oldnatoms, as tags may not be
      // consecutive at this point, e.g. due to deleted or lost atoms.
sjplimp's avatar
 
sjplimp committed
      oldnatoms = atom->natoms;
sjplimp's avatar
 
sjplimp committed
      oldmaxtag = atom->map_tag_max;
      memory->create(occvec,oldmaxtag,"voronoi/atom:occvec");
sjplimp's avatar
 
sjplimp committed
#ifdef NOTINPLACE
sjplimp's avatar
 
sjplimp committed
      memory->create(sendocc,oldmaxtag,"voronoi/atom:sendocc");
sjplimp's avatar
 
sjplimp committed
#endif
    }

    // get the occupation of each original voronoi cell
    checkOccupation();
  } else {
    // build cells for each output
    buildCells();
    loopCells();
  }
}

void ComputeVoronoi::buildCells()
{
sjplimp's avatar
 
sjplimp committed
  int i;
sjplimp's avatar
 
sjplimp committed
  const double e = 0.01;
  int nlocal = atom->nlocal;
sjplimp's avatar
 
sjplimp committed
  int dim = domain->dimension;
sjplimp's avatar
 
sjplimp committed

sjplimp's avatar
 
sjplimp committed
  // in the onlyGroup mode we are not setting values for all atoms later in the voro loop
  // initialize everything to zero here
  if (onlyGroup) {
sjplimp's avatar
 
sjplimp committed
    if (surface == VOROSURF_NONE)
sjplimp's avatar
 
sjplimp committed
      for (i = 0; i < nlocal; i++) voro[i][0] = voro[i][1] = 0.0;
    else
      for (i = 0; i < nlocal; i++) voro[i][0] = voro[i][1] = voro[i][2] = 0.0;
  }
sjplimp's avatar
sjplimp committed

sjplimp's avatar
 
sjplimp committed
  double *sublo = domain->sublo, *sublo_lamda = domain->sublo_lamda, *boxlo = domain->boxlo;
sjplimp's avatar
 
sjplimp committed
  double *subhi = domain->subhi, *subhi_lamda = domain->subhi_lamda;
sjplimp's avatar
sjplimp committed
  double *cut = comm->cutghost;
athomps's avatar
athomps committed
  double sublo_bound[3], subhi_bound[3];
sjplimp's avatar
sjplimp committed
  double **x = atom->x;
sjplimp's avatar
 
sjplimp committed

sjplimp's avatar
 
sjplimp committed
  // setup bounds for voro++ domain for orthogonal and triclinic simulation boxes
  if( domain->triclinic ) {
    // triclinic box: embed parallelepiped into orthogonal voro++ domain

    // cutghost is in lamda coordinates for triclinic boxes, use subxx_lamda
sjplimp's avatar
 
sjplimp committed
    double *h = domain->h;
    for( i=0; i<3; ++i ) {
athomps's avatar
athomps committed
      sublo_bound[i] = sublo_lamda[i]-cut[i]-e;
      subhi_bound[i] = subhi_lamda[i]+cut[i]+e;
      if (domain->periodicity[i]==0) {
Axel Kohlmeyer's avatar
Axel Kohlmeyer committed
        sublo_bound[i] = MAX(sublo_bound[i],0.0);
        subhi_bound[i] = MIN(subhi_bound[i],1.0);
      }
    }
    if (dim == 2) {
      sublo_bound[2] = 0.0;
      subhi_bound[2] = 1.0;
    }
    sublo_bound[0] = h[0]*sublo_bound[0] + h[5]*sublo_bound[1] + h[4]*sublo_bound[2] + boxlo[0];
    sublo_bound[1] = h[1]*sublo_bound[1] + h[3]*sublo_bound[2] + boxlo[1];
    sublo_bound[2] = h[2]*sublo_bound[2] + boxlo[2];
    subhi_bound[0] = h[0]*subhi_bound[0] + h[5]*subhi_bound[1] + h[4]*subhi_bound[2] + boxlo[0];
    subhi_bound[1] = h[1]*subhi_bound[1] + h[3]*subhi_bound[2] + boxlo[1];
    subhi_bound[2] = h[2]*subhi_bound[2] + boxlo[2];
sjplimp's avatar
 
sjplimp committed

  } else {
    // orthogonal box
    for( i=0; i<3; ++i ) {
      sublo_bound[i] = sublo[i]-cut[i]-e;
      subhi_bound[i] = subhi[i]+cut[i]+e;
      if (domain->periodicity[i]==0) {
Axel Kohlmeyer's avatar
Axel Kohlmeyer committed
        sublo_bound[i] = MAX(sublo_bound[i],domain->boxlo[i]);
        subhi_bound[i] = MIN(subhi_bound[i],domain->boxhi[i]);
      }
    }
    if (dim == 2) {
      sublo_bound[2] = sublo[2];
      subhi_bound[2] = subhi[2];
sjplimp's avatar
 
sjplimp committed
    }
  }
sjplimp's avatar
sjplimp committed

sjplimp's avatar
 
sjplimp committed
  // n = # of voro++ spatial hash cells (with approximately cubic cells)
  int nall = nlocal + atom->nghost;
  double n[3], V;
  for( i=0; i<3; ++i ) n[i] = subhi_bound[i] - sublo_bound[i];
  V = n[0]*n[1]*n[2];
  for( i=0; i<3; ++i ) {
    n[i] = round( n[i]*pow( double(nall)/(V*8.0), 0.333333 ) );
    n[i] = n[i]==0 ? 1 : n[i];
sjplimp's avatar
 
sjplimp committed
  }
sjplimp's avatar
sjplimp committed

sjplimp's avatar
 
sjplimp committed
  // clear edge statistics
  if ( maxedge > 0 )
    for (i = 0; i <= maxedge; ++i) edge[i]=0;
sjplimp's avatar
sjplimp committed

sjplimp's avatar
 
sjplimp committed
  // initialize voro++ container
  // preallocates 8 atoms per cell
  // voro++ allocates more memory if needed
  int *mask = atom->mask;
sjplimp's avatar
 
sjplimp committed
  if (radstr) {
sjplimp's avatar
 
sjplimp committed
    // check and fetch atom style variable data
    int radvar = input->variable->find(radstr);
    if (radvar < 0)
sjplimp's avatar
 
sjplimp committed
      error->all(FLERR,"Variable name for voronoi radius does not exist");
sjplimp's avatar
 
sjplimp committed
    if (!input->variable->atomstyle(radvar))
sjplimp's avatar
 
sjplimp committed
      error->all(FLERR,"Variable for voronoi radius is not atom style");
sjplimp's avatar
 
sjplimp committed
    // prepare destination buffer for variable evaluation
sjplimp's avatar
 
sjplimp committed
    if (atom->nmax > rmax) {
sjplimp's avatar
 
sjplimp committed
      memory->destroy(rfield);
      rmax = atom->nmax;
      memory->create(rfield,rmax,"voronoi/atom:rfield");
    }
    // compute atom style radius variable
    input->variable->compute_atom(radvar,0,rfield,1,0);

    // communicate values to ghost atoms of neighboring nodes
    comm->forward_comm_compute(this);

    // polydisperse voro++ container
sjplimp's avatar
 
sjplimp committed
    delete con_poly;
    con_poly = new container_poly(sublo_bound[0],
Axel Kohlmeyer's avatar
Axel Kohlmeyer committed
                                  subhi_bound[0],
                                  sublo_bound[1],
                                  subhi_bound[1],
                                  sublo_bound[2],
                                  subhi_bound[2],
                                  int(n[0]),int(n[1]),int(n[2]),
                                  false,false,false,8);
sjplimp's avatar
 
sjplimp committed

    // pass coordinates for local and ghost atoms to voro++
sjplimp's avatar
 
sjplimp committed
    for (i = 0; i < nall; i++) {
sjplimp's avatar
 
sjplimp committed
      if( !onlyGroup || (mask[i] & groupbit) )
sjplimp's avatar
 
sjplimp committed
        con_poly->put(i,x[i][0],x[i][1],x[i][2],rfield[i]);
sjplimp's avatar
 
sjplimp committed
    }
sjplimp's avatar
 
sjplimp committed
  } else {
    // monodisperse voro++ container
sjplimp's avatar
 
sjplimp committed
    delete con_mono;

    con_mono = new container(sublo_bound[0],
Axel Kohlmeyer's avatar
Axel Kohlmeyer committed
                             subhi_bound[0],
                             sublo_bound[1],
                             subhi_bound[1],
                             sublo_bound[2],
                             subhi_bound[2],
                             int(n[0]),int(n[1]),int(n[2]),
                             false,false,false,8);
sjplimp's avatar
 
sjplimp committed

    // pass coordinates for local and ghost atoms to voro++
    for (i = 0; i < nall; i++)
      if( !onlyGroup || (mask[i] & groupbit) )
sjplimp's avatar
 
sjplimp committed
        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
sjplimp's avatar
 
sjplimp committed
  if (atom->nmax > lmax) {
sjplimp's avatar
 
sjplimp committed
    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
sjplimp's avatar
 
sjplimp committed
      if (lroot[k]<0)
sjplimp's avatar
 
sjplimp committed
        lroot[k]=i;
      else {
        j = lroot[k];
        while (lnext[j]>=0) j=lnext[j];
sjplimp's avatar
 
sjplimp committed
        lnext[j] = i;
sjplimp's avatar
 
sjplimp committed
      }
    }
  }

  // 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];
    }
  }

sjplimp's avatar
 
sjplimp committed
  // cherry pick currently owned atoms
sjplimp's avatar
 
sjplimp committed
  for (i=0; i<nlocal; i++) {
    // set the new atom count in the atom's first frame voronoi cell
sjplimp's avatar
 
sjplimp committed
    // but take into account that new atoms might have been added to
    // the system, so we can only look up occupancy for tags that are
    // smaller or equal to the recorded largest tag.
    tagint mytag = atom->tag[i];
    if (mytag > oldmaxtag)
      voro[i][0] = 0;
    else
      voro[i][0] = occvec[mytag-1];
sjplimp's avatar
 
sjplimp committed
  }
}
sjplimp's avatar
 
sjplimp committed

sjplimp's avatar
 
sjplimp committed
void ComputeVoronoi::loopCells()
{
  // invoke voro++ and fetch results for owned atoms in group
  voronoicell_neighbor c;
  int i;
athomps's avatar
athomps committed
  if (faces_flag) nfaces = 0;
sjplimp's avatar
 
sjplimp committed
  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)) {
sjplimp's avatar
 
sjplimp committed
      i = cl.pid();
      processCell(c,i);
    } while (cl.inc());
  }
athomps's avatar
athomps committed
  if (faces_flag) size_local_rows = nfaces;

sjplimp's avatar
sjplimp committed
}

/* ----------------------------------------------------------------------
   memory usage of local atom-based array
------------------------------------------------------------------------- */
sjplimp's avatar
 
sjplimp committed
void ComputeVoronoi::processCell(voronoicell_neighbor &c, int i)
sjplimp's avatar
 
sjplimp committed
{
  int j,k, *mask = atom->mask;
  std::vector<int> neigh, norder, vlist;
  std::vector<double> narea, vcell;
  bool have_narea = false;

  // zero out surface area if surface computation was requested
  if (surface != VOROSURF_NONE && !onlyGroup) voro[i][2] = 0.0;

  if (i < atom->nlocal && (mask[i] & groupbit)) {
    // cell volume
    voro[i][0] = c.volume();

    // number of cell faces
    c.neighbors(neigh);
sjplimp's avatar
 
sjplimp committed
    int neighs = neigh.size();

sjplimp's avatar
 
sjplimp committed
    if (fthresh > 0) {
      // count only faces above area threshold
      c.face_areas(narea);
      have_narea = true;
sjplimp's avatar
 
sjplimp committed
      voro[i][1] = 0.0;
sjplimp's avatar
 
sjplimp committed
      for (j=0; j<narea.size(); ++j)
sjplimp's avatar
 
sjplimp committed
        if (narea[j] > fthresh) voro[i][1] += 1.0;
    } else {
      // unthresholded face count
sjplimp's avatar
 
sjplimp committed
      voro[i][1] = neighs;
sjplimp's avatar
 
sjplimp committed
    }

    // cell surface area
    if (surface == VOROSURF_ALL) {
      voro[i][2] = c.surface_area();
    } else if (surface == VOROSURF_GROUP) {
      if (!have_narea) c.face_areas(narea);
sjplimp's avatar
 
sjplimp committed
      voro[i][2] = 0.0;
sjplimp's avatar
 
sjplimp committed

sjplimp's avatar
 
sjplimp committed
      // each entry in neigh should correspond to an entry in narea
sjplimp's avatar
 
sjplimp committed
      if (neighs != narea.size())
athomps's avatar
athomps committed
        error->one(FLERR,"Voro++ error: narea and neigh have a different size");
sjplimp's avatar
 
sjplimp committed

sjplimp's avatar
 
sjplimp committed
      // loop over all faces (neighbors) and check if they are in the surface group
sjplimp's avatar
 
sjplimp committed
      for (j=0; j<neighs; ++j)
        if (neigh[j] >= 0 && mask[neigh[j]] & sgroupbit)
          voro[i][2] += narea[j];
sjplimp's avatar
 
sjplimp committed
    }

    // histogram of number of face edges
sjplimp's avatar
 
sjplimp committed
    if (maxedge>0) {
      if (ethresh > 0) {
sjplimp's avatar
 
sjplimp committed
        // count only edges above length threshold
sjplimp's avatar
 
sjplimp committed
        c.vertices(vcell);
        c.face_vertices(vlist); // for each face: vertex count followed list of vertex indices (n_1,v1_1,v2_1,v3_1,..,vn_1,n_2,v2_1,...)
        double dx, dy, dz, r2, t2 = ethresh*ethresh;
sjplimp's avatar
 
sjplimp committed
        for( j=0; j<vlist.size(); j+=vlist[j]+1 ) {
sjplimp's avatar
 
sjplimp committed
          int a, b, nedge = 0;
          // vlist[j] contains number of vertex indices for the current face
sjplimp's avatar
 
sjplimp committed
          for( k=0; k<vlist[j]; ++k ) {
            a = vlist[j+1+k];              // first vertex in edge
sjplimp's avatar
 
sjplimp committed
            b = vlist[j+1+(k+1)%vlist[j]]; // second vertex in edge (possible wrap around to first vertex in list)
            dx = vcell[a*3]   - vcell[b*3];
            dy = vcell[a*3+1] - vcell[b*3+1];
            dz = vcell[a*3+2] - vcell[b*3+2];
            r2 = dx*dx+dy*dy+dz*dz;
            if (r2 > t2) nedge++;
          }
          // counted edges above threshold, now put into the correct bin
          if (nedge>0) {
sjplimp's avatar
 
sjplimp committed
            if (nedge<=maxedge)
sjplimp's avatar
 
sjplimp committed
              edge[nedge-1]++;
            else
              edge[maxedge]++;
          }
        }
      } else {
        // unthresholded edge counts
        c.face_orders(norder);
        for (j=0; j<voro[i][1]; ++j)
          if (norder[j]>0) {
sjplimp's avatar
 
sjplimp committed
            if (norder[j]<=maxedge)
sjplimp's avatar
 
sjplimp committed
              edge[norder[j]-1]++;
            else
              edge[maxedge]++;
          }
      }
    }
athomps's avatar
athomps committed

    // store info for local faces

    if (faces_flag) {
      if (nfaces+voro[i][1] > nfacesmax) {
Axel Kohlmeyer's avatar
Axel Kohlmeyer committed
        while (nfacesmax < nfaces+voro[i][1]) nfacesmax += FACESDELTA;
        memory->grow(faces,nfacesmax,size_local_cols,"compute/voronoi/atom:faces");
        array_local = faces;
athomps's avatar
athomps committed
      }

      if (!have_narea) c.face_areas(narea);

      if (neighs != narea.size())
        error->one(FLERR,"Voro++ error: narea and neigh have a different size");
      tagint itag, jtag;
      tagint *tag = atom->tag;
      itag = tag[i];
      for (j=0; j<neighs; ++j)
Axel Kohlmeyer's avatar
Axel Kohlmeyer committed
        if (narea[j] > fthresh) {
Axel Kohlmeyer's avatar
Axel Kohlmeyer committed
          // external faces assigned the tag 0
Axel Kohlmeyer's avatar
Axel Kohlmeyer committed
          int jj = neigh[j];
          if (jj >= 0) jtag = tag[jj];
          else jtag = 0;
Axel Kohlmeyer's avatar
Axel Kohlmeyer committed
          faces[nfaces][0] = itag;
          faces[nfaces][1] = jtag;
          faces[nfaces][2] = narea[j];
          nfaces++;
        }
sjplimp's avatar
 
sjplimp committed
  } else if (i < atom->nlocal) voro[i][0] = voro[i][1] = 0.0;
}
sjplimp's avatar
sjplimp committed

double ComputeVoronoi::memory_usage()
{
  double bytes = size_peratom_cols * nmax * sizeof(double);
athomps's avatar
athomps committed
  // estimate based on average coordination of 12
  if (faces_flag) bytes += 12 * size_local_cols * nmax * sizeof(double);
sjplimp's avatar
sjplimp committed
  return bytes;
}
sjplimp's avatar
 
sjplimp committed

void ComputeVoronoi::compute_vector()
{
  invoked_vector = update->ntimestep;
  if( invoked_peratom < invoked_vector ) compute_peratom();

  for( int i=0; i<size_vector; ++i ) sendvector[i] = edge[i];
  MPI_Allreduce(sendvector,edge,size_vector,MPI_DOUBLE,MPI_SUM,world);
}

/* ---------------------------------------------------------------------- */

athomps's avatar
athomps committed
void ComputeVoronoi::compute_local()
{
  invoked_local = update->ntimestep;
  if( invoked_peratom < invoked_local ) compute_peratom();
}

/* ---------------------------------------------------------------------- */

sjplimp's avatar
 
sjplimp committed
int ComputeVoronoi::pack_forward_comm(int n, int *list, double *buf,
sjplimp's avatar
 
sjplimp committed
                                  int pbc_flag, int *pbc)
{
  int i,m=0;
sjplimp's avatar
 
sjplimp committed
  for (i = 0; i < n; ++i) buf[m++] = rfield[list[i]];
sjplimp's avatar
 
sjplimp committed
  return m;
sjplimp's avatar
 
sjplimp committed
}

/* ---------------------------------------------------------------------- */

sjplimp's avatar
 
sjplimp committed
void ComputeVoronoi::unpack_forward_comm(int n, int first, double *buf)
sjplimp's avatar
 
sjplimp committed
{
  int i,last,m=0;
  last = first + n;
sjplimp's avatar
 
sjplimp committed
  for (i = first; i < last; ++i) rfield[i] = buf[m++];
sjplimp's avatar
 
sjplimp committed
}