From 026a04e4f11aade0c4020591ff7ac49b972aa5a2 Mon Sep 17 00:00:00 2001 From: sjplimp <sjplimp@f3b2605a-c512-4ea7-a41b-209d697bcdaa> Date: Thu, 25 Jul 2013 23:58:07 +0000 Subject: [PATCH] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10394 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/VORONOI/README | 7 + src/VORONOI/compute_voronoi_atom.cpp | 333 +++++++++++++++++++++++---- src/VORONOI/compute_voronoi_atom.h | 14 +- 3 files changed, 310 insertions(+), 44 deletions(-) diff --git a/src/VORONOI/README b/src/VORONOI/README index 89cf838b45..746859323e 100644 --- a/src/VORONOI/README +++ b/src/VORONOI/README @@ -28,6 +28,13 @@ compute the tesselation locally on each processor. 5. Compile LAMMPS (you should know how that works) +== Run tests == + +Run the includes test input file +./lmp_serial < lammps/examples/voronoi/in.vorotest | grep '^TEST_' + +The output should conclude with 'TEST_DONE' and every line should +report an error of 0%. == Credits and license == diff --git a/src/VORONOI/compute_voronoi_atom.cpp b/src/VORONOI/compute_voronoi_atom.cpp index 3b94decdf5..d551fbee68 100644 --- a/src/VORONOI/compute_voronoi_atom.cpp +++ b/src/VORONOI/compute_voronoi_atom.cpp @@ -20,15 +20,17 @@ #include "stdlib.h" #include "compute_voronoi_atom.h" #include "atom.h" +#include "group.h" #include "update.h" #include "modify.h" #include "domain.h" #include "memory.h" #include "error.h" #include "comm.h" +#include "variable.h" +#include "input.h" #include <vector> -#include "voro++.hh" using namespace LAMMPS_NS; using namespace voro; @@ -38,29 +40,88 @@ using namespace voro; ComputeVoronoi::ComputeVoronoi(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg) { - if (narg != 3) error->all(FLERR,"Illegal compute voronoi/atom command"); - - peratom_flag = 1; + int sgroup; + size_peratom_cols = 2; + peratom_flag = 1; - nmax = 0; + surface = VOROSURF_NONE; + maxedge = 0; + fthresh = ethresh = 0.0; + radstr = NULL; + onlyGroup = false; + + int iarg = 3; + while ( iarg<narg ) { + 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,"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,"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"); + 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,"Missing maximum edge count after keyword 'edge_histo'."); + maxedge = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg], "face_threshold") == 0) { + if (iarg + 2 > narg) error->all(FLERR,"Missing minimum face area after keyword 'face_threshold'."); + fthresh = atof(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg], "edge_threshold") == 0) { + if (iarg + 2 > narg) error->all(FLERR,"Missing minimum edge length after keyword 'edge_threshold'."); + ethresh = atof(arg[iarg+1]); + iarg += 2; + } + else + error->all(FLERR,"Illegal compute voronoi/atom command"); + } + + nmax = rmax = 0; + edge = rfield = sendvector = NULL; voro = NULL; + + 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; + } } /* ---------------------------------------------------------------------- */ ComputeVoronoi::~ComputeVoronoi() { + memory->destroy(edge); + memory->destroy(rfield); + memory->destroy(sendvector); memory->destroy(voro); + delete[] radstr; } /* ---------------------------------------------------------------------- */ void ComputeVoronoi::init() { - if (domain->triclinic != 0) - error->all(FLERR,"Compute voronoi/atom not allowed for triclinic boxes"); - int count = 0; for (int i = 0; i < modify->ncompute; i++) if (strcmp(modify->compute[i]->style,"voronoi/atom") == 0) count++; @@ -74,7 +135,8 @@ void ComputeVoronoi::init() void ComputeVoronoi::compute_peratom() { - int i; + int i, j; + const double e = 0.01; invoked_peratom = update->ntimestep; @@ -88,55 +150,240 @@ void ComputeVoronoi::compute_peratom() array_atom = voro; } - // n = # of voro++ spatial hash cells - // TODO: make square - - int nall = nlocal + atom->nghost; - int n = int(floor( pow( double(nall)/8.0, 0.333333 ) )); - n = (n==0) ? 1 : n; - - // initialize voro++ container - // preallocates 8 atoms per cell - // voro++ allocates more memory if needed + // in the onlyGroup mode we are not setting values for all atoms later in the voro loop + // initialize everything to zero here + if (onlyGroup) { + if (surface == VOROSURF_NONE) + 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; + } - double *sublo = domain->sublo; - double *subhi = domain->subhi; + double *sublo = domain->sublo, *sublo_lamda = domain->sublo_lamda, *boxlo = domain->boxlo; + double *subhi = domain->subhi, *subhi_lamda = domain->subhi_lamda, *boxhi = domain->boxhi; double *cut = comm->cutghost; - - container con(sublo[0]-cut[0],subhi[0]+cut[0], - sublo[1]-cut[1],subhi[1]+cut[1], - sublo[2]-cut[2],subhi[2]+cut[2], - n,n,n,false,false,false,8); - - // pass coordinates for local and ghost atoms to voro++ - + double sublo_bound[3], subhi_bound[3], cut_bound[3]; double **x = atom->x; - for (i = 0; i < nall; i++) - con.put(i,x[i][0],x[i][1],x[i][2]); + + // setup bounds for voro++ domain for orthogonal and triclinic simulation boxes + if( domain->triclinic ) { + // triclinic box: embed parallelepiped into orthogonal voro++ domain + double mx, my, sxy,sxz,syz; + mx = (boxhi[0]-boxlo[0])/(subhi[0]-sublo[0]); + my = (boxhi[1]-boxlo[1])/(subhi[1]-sublo[1]); + sxy = domain->xy/mx; + sxz = domain->xz/mx; + syz = domain->yz/my; + + // cutghost is in lamda coordinates for triclinic boxes, use subxx_lamda + 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]; + subhi_bound[0] = h[0]*subhi_lamda[0] + h[5]*subhi_lamda[1] + h[4]*subhi_lamda[2] + boxlo[0]; + subhi_bound[1] = h[1]*subhi_lamda[1] + h[3]*subhi_lamda[2] + boxlo[1]; + subhi_bound[2] = h[2]*subhi_lamda[2] + boxlo[2]; + cut_bound[0] = h[0]*cut[0] + h[5]*cut[1] + h[4]*cut[2]; + cut_bound[1] = h[1]*cut[1] + h[3]*cut[2]; + cut_bound[2] = h[2]*cut[2]; + + } else { + // orthogonal box + for( i=0; i<3; ++i ) { + sublo_bound[i] = sublo[i]; + subhi_bound[i] = subhi[i]; + cut_bound[i] = cut[i]; + } + } - // invoke voro++ and fetch results for owned atoms in group + // 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]; + } - int *mask = atom->mask; - std::vector<int> neigh; + // clear edge statistics + for (i = 0; i < maxedge; ++i) edge[i]=0; + // initialize voro++ container + // preallocates 8 atoms per cell + // voro++ allocates more memory if needed voronoicell_neighbor c; - c_loop_all cl(con); - if (cl.start()) do if (con.compute_cell(c,cl)) { - i = cl.pid(); - if (i < nlocal && (mask[i] & groupbit)) { - voro[i][0] = c.volume(); - c.neighbors(neigh); - voro[i][1] = neigh.size(); - } else if (i < nlocal) voro[i][0] = voro[i][1] = 0.0; - } while (cl.inc()); + int *mask = atom->mask; + 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 set does not exist"); + if (!input->variable->atomstyle(radvar)) + error->all(FLERR,"Variable for voronoi radius is not atom style"); + // prepare destination buffer for variable evaluation + if (nlocal > rmax) { + 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 + container_poly con(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); + + // 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()); + } else { + // monodisperse voro++ container + container con(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); + + // 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]); + + // 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()); + } } /* ---------------------------------------------------------------------- memory usage of local atom-based array ------------------------------------------------------------------------- */ +void ComputeVoronoi::processCell(voronoicell_neighbor &c, int i) +{ + 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); + if (fthresh > 0) { + // count only faces above area threshold + c.face_areas(narea); + have_narea = true; + for (j=0; j<narea.size(); ++j) + if (narea[j] > fthresh) voro[i][1] += 1.0; + } else { + // unthresholded face count + voro[i][1] = neigh.size(); + } + + // 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); + // loop over all faces (neighbors) and check if they are in the surface group + for (j=0; j<voro[i][1]; ++j) + if (mask[neigh[j]] & sgroupbit) voro[i][2] += narea[j]; + } + + // histogram of number of face edges + if (maxedge>0) { + if (ethresh > 0) { + // count only edges above length threshold + 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; + for( j=0; j<vlist.size(); j+=vlist[j]+1 ) { + int a, b, nedge = 0; + // vlist[j] contains number of vertex indices for the current face + for( k=0; k<vlist[j]; ++k ) { + a = vlist[j+1+k]; // first vertex in edge + 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) { + if (nedge<=maxedge) + 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) { + if (norder[j]<=maxedge) + edge[norder[j]-1]++; + else + edge[maxedge]++; + } + } + } + } else if (i < atom->nlocal) voro[i][0] = voro[i][1] = 0.0; +} double ComputeVoronoi::memory_usage() { double bytes = size_peratom_cols * nmax * sizeof(double); return bytes; } + +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); +} + +/* ---------------------------------------------------------------------- */ + +int ComputeVoronoi::pack_comm(int n, int *list, double *buf, + int pbc_flag, int *pbc) +{ + int i,m=0; + for (i = 0; i < n; i++) buf[m++] = rfield[list[i]]; + return 1; +} + +/* ---------------------------------------------------------------------- */ + +void ComputeVoronoi::unpack_comm(int n, int first, double *buf) +{ + int i,last,m=0; + last = first + n; + for (i = first; i < last; i++) rfield[i] = buf[m++]; +} + diff --git a/src/VORONOI/compute_voronoi_atom.h b/src/VORONOI/compute_voronoi_atom.h index 7a8d267bd8..24f89cfabb 100644 --- a/src/VORONOI/compute_voronoi_atom.h +++ b/src/VORONOI/compute_voronoi_atom.h @@ -21,6 +21,7 @@ ComputeStyle(voronoi/atom,ComputeVoronoi) #define LMP_COMPUTE_VORONOI_H #include "compute.h" +#include "voro++.hh" namespace LAMMPS_NS { @@ -30,11 +31,22 @@ class ComputeVoronoi : public Compute { ~ComputeVoronoi(); void init(); void compute_peratom(); + void compute_vector(); double memory_usage(); + int pack_comm(int, int *, double *, int, int *); + void unpack_comm(int, int, double *); + private: - int nmax; + void processCell(voro::voronoicell_neighbor&, int); + + int nmax, rmax, maxedge, sgroupbit; + char *radstr; + double fthresh, ethresh; double **voro; + double *edge, *sendvector, *rfield; + enum { VOROSURF_NONE, VOROSURF_ALL, VOROSURF_GROUP } surface; + bool onlyGroup; }; } -- GitLab