Voro++
unitcell.cc
Go to the documentation of this file.
00001 // Voro++, a 3D cell-based Voronoi library
00002 //
00003 // Author   : Chris H. Rycroft (LBL / UC Berkeley)
00004 // Email    : chr@alum.mit.edu
00005 // Date     : August 30th 2011
00006 
00007 /** \file unitcell.cc
00008  * \brief Function implementations for the unitcell class. */
00009 
00010 #include <queue>
00011 using namespace std;
00012 
00013 #include "unitcell.hh"
00014 #include "cell.hh"
00015 
00016 namespace voro {
00017 
00018 /** Initializes the unit cell class for a particular non-orthogonal periodic
00019  * geometry, corresponding to a parallelepiped with sides given by three
00020  * vectors. The class constructs the unit Voronoi cell corresponding to this
00021  * geometry.
00022  * \param[in] (bx_) The x coordinate of the first unit vector.
00023  * \param[in] (bxy_,by_) The x and y coordinates of the second unit vector.
00024  * \param[in] (bxz_,byz_,bz_) The x, y, and z coordinates of the third unit
00025  *                            vector. */
00026 unitcell::unitcell(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_)
00027         : bx(bx_), bxy(bxy_), by(by_), bxz(bxz_), byz(byz_), bz(bz_) {
00028         int i,j,l=1;
00029 
00030         // Initialize the Voronoi cell to be a very large rectangular box
00031         const double ucx=max_unit_voro_shells*bx,ucy=max_unit_voro_shells*by,ucz=max_unit_voro_shells*bz;
00032         unit_voro.init(-ucx,ucx,-ucy,ucy,-ucz,ucz);
00033 
00034         // Repeatedly cut the cell by shells of periodic image particles
00035         while(l<2*max_unit_voro_shells) {
00036 
00037                 // Check to see if any of the planes from the current shell
00038                 // will cut the cell
00039                 if(unit_voro_intersect(l)) {
00040 
00041                         // If they do, apply the plane cuts from the current
00042                         // shell
00043                         unit_voro_apply(l,0,0);
00044                         for(i=1;i<l;i++) {
00045                                 unit_voro_apply(l,i,0);
00046                                 unit_voro_apply(-l,i,0);
00047                         }
00048                         for(i=-l;i<=l;i++) unit_voro_apply(i,l,0);
00049                         for(i=1;i<l;i++) for(j=-l+1;j<=l;j++) {
00050                                 unit_voro_apply(l,j,i);
00051                                 unit_voro_apply(-j,l,i);
00052                                 unit_voro_apply(-l,-j,i);
00053                                 unit_voro_apply(j,-l,i);
00054                         }
00055                         for(i=-l;i<=l;i++) for(j=-l;j<=l;j++) unit_voro_apply(i,j,l);
00056                 } else {
00057 
00058                         // Calculate a bound on the maximum y and z coordinates
00059                         // that could possibly cut the cell. This is based upon
00060                         // a geometric result that particles with z>l can't cut
00061                         // a cell lying within the paraboloid
00062                         // z<=(l*l-x*x-y*y)/(2*l). It is always a tighter bound
00063                         // than the one based on computing the maximum radius
00064                         // of a Voronoi cell vertex.
00065                         max_uv_y=max_uv_z=0;
00066                         double y,z,q,*pts=unit_voro.pts,*pp=pts;
00067                         while(pp<pts+3*unit_voro.p) {
00068                                 q=*(pp++);y=*(pp++);z=*(pp++);q=sqrt(q*q+y*y+z*z);
00069                                 if(y+q>max_uv_y) max_uv_y=y+q;
00070                                 if(z+q>max_uv_z) max_uv_z=z+q;
00071                         }
00072                         max_uv_z*=0.5;
00073                         max_uv_y*=0.5;
00074                         return;
00075                 }
00076                 l++;
00077         }
00078 
00079         // If the routine makes it here, then the unit cell still hasn't been
00080         // completely bounded by the plane cuts. Give the memory error code,
00081         // because this is mainly a case of hitting a safe limit, than any
00082         // inherent problem.
00083         voro_fatal_error("Periodic cell computation failed",VOROPP_MEMORY_ERROR);
00084 }
00085 
00086 /** Applies a pair of opposing plane cuts from a periodic image point
00087  * to the unit Voronoi cell.
00088  * \param[in] (i,j,k) the index of the periodic image to consider. */
00089 inline void unitcell::unit_voro_apply(int i,int j,int k) {
00090         double x=i*bx+j*bxy+k*bxz,y=j*by+k*byz,z=k*bz;
00091         unit_voro.plane(x,y,z);
00092         unit_voro.plane(-x,-y,-z);
00093 }
00094 
00095 /** Calculates whether the unit Voronoi cell intersects a given periodic image
00096  * of the domain.
00097  * \param[in] (dx,dy,dz) the displacement of the periodic image.
00098  * \param[out] vol the proportion of the unit cell volume within this image,
00099  *                 only computed in the case that the two intersect.
00100  * \return True if they intersect, false otherwise. */
00101 bool unitcell::intersects_image(double dx,double dy,double dz,double &vol) {
00102         const double bxinv=1/bx,byinv=1/by,bzinv=1/bz,ivol=bxinv*byinv*bzinv;
00103         voronoicell c;
00104         c=unit_voro;
00105         dx*=2;dy*=2;dz*=2;
00106         if(!c.plane(0,0,bzinv,dz+1)) return false;
00107         if(!c.plane(0,0,-bzinv,-dz+1)) return false;
00108         if(!c.plane(0,byinv,-byz*byinv*bzinv,dy+1)) return false;
00109         if(!c.plane(0,-byinv,byz*byinv*bzinv,-dy+1)) return false;
00110         if(!c.plane(bxinv,-bxy*bxinv*byinv,(bxy*byz-by*bxz)*ivol,dx+1)) return false;
00111         if(!c.plane(-bxinv,bxy*bxinv*byinv,(-bxy*byz+by*bxz)*ivol,-dx+1)) return false;
00112         vol=c.volume()*ivol;
00113         return true;
00114 }
00115 
00116 /** Computes a list of periodic domain images that intersect the unit Voronoi cell.
00117  * \param[out] vi a vector containing triplets (i,j,k) corresponding to domain
00118  *                images that intersect the unit Voronoi cell, when it is
00119  *                centered in the middle of the primary domain.
00120  * \param[out] vd a vector containing the fraction of the Voronoi cell volume
00121  *                within each corresponding image listed in vi. */
00122 void unitcell::images(vector<int> &vi,vector<double> &vd) {
00123         const int ms2=max_unit_voro_shells*2+1,mss=ms2*ms2*ms2;
00124         bool *a=new bool[mss],*ac=a+max_unit_voro_shells*(1+ms2*(1+ms2)),*ap(a);
00125         int i,j,k;
00126         double vol;
00127 
00128         // Initialize mask
00129         while(ap<ac) *(ap++)=true;
00130         *(ap++)=false;
00131         while(ap<a+mss) *(ap++)=true;
00132 
00133         // Set up the queue and add (0,0,0) image to it
00134         queue<int> q;
00135         q.push(0);q.push(0);q.push(0);
00136 
00137         while(!q.empty()) {
00138 
00139                 // Read the next entry on the queue
00140                 i=q.front();q.pop();
00141                 j=q.front();q.pop();
00142                 k=q.front();q.pop();
00143 
00144                 // Check intersection of this image
00145                 if(intersects_image(i,j,k,vol)) {
00146 
00147                         // Add this entry to the output vectors
00148                         vi.push_back(i);
00149                         vi.push_back(j);
00150                         vi.push_back(k);
00151                         vd.push_back(vol);
00152 
00153                         // Add neighbors to the queue if they have not been
00154                         // tested
00155                         ap=ac+i+ms2*(j+ms2*k);
00156                         if(k>-max_unit_voro_shells&&*(ap-ms2*ms2)) {q.push(i);q.push(j);q.push(k-1);*(ap-ms2*ms2)=false;}
00157                         if(j>-max_unit_voro_shells&&*(ap-ms2)) {q.push(i);q.push(j-1);q.push(k);*(ap-ms2)=false;}
00158                         if(i>-max_unit_voro_shells&&*(ap-1)) {q.push(i-1);q.push(j);q.push(k);*(ap-1)=false;}
00159                         if(i<max_unit_voro_shells&&*(ap+1)) {q.push(i+1);q.push(j);q.push(k);*(ap+1)=false;}
00160                         if(j<max_unit_voro_shells&&*(ap+ms2)) {q.push(i);q.push(j+1);q.push(k);*(ap+ms2)=false;}
00161                         if(k<max_unit_voro_shells&&*(ap+ms2*ms2)) {q.push(i);q.push(j);q.push(k+1);*(ap+ms2*ms2)=false;}
00162                 }
00163         }
00164 
00165         // Remove mask memory
00166         delete [] a;
00167 }
00168 
00169 /** Tests to see if a shell of periodic images could possibly cut the periodic
00170  * unit cell.
00171  * \param[in] l the index of the shell to consider.
00172  * \return True if a point in the shell cuts the cell, false otherwise. */
00173 bool unitcell::unit_voro_intersect(int l) {
00174         int i,j;
00175         if(unit_voro_test(l,0,0)) return true;
00176         for(i=1;i<l;i++) {
00177                 if(unit_voro_test(l,i,0)) return true;
00178                 if(unit_voro_test(-l,i,0)) return true;
00179         }
00180         for(i=-l;i<=l;i++) if(unit_voro_test(i,l,0)) return true;
00181         for(i=1;i<l;i++) for(j=-l+1;j<=l;j++) {
00182                 if(unit_voro_test(l,j,i)) return true;
00183                 if(unit_voro_test(-j,l,i)) return true;
00184                 if(unit_voro_test(-l,-j,i)) return true;
00185                 if(unit_voro_test(j,-l,i)) return true;
00186         }
00187         for(i=-l;i<=l;i++) for(j=-l;j<=l;j++) if(unit_voro_test(i,j,l)) return true;
00188         return false;
00189 }
00190 
00191 /** Tests to see if a plane cut from a particular periodic image will cut th
00192  * unit Voronoi cell.
00193  * \param[in] (i,j,k) the index of the periodic image to consider.
00194  * \return True if the image cuts the cell, false otherwise. */
00195 inline bool unitcell::unit_voro_test(int i,int j,int k) {
00196         double x=i*bx+j*bxy+k*bxz,y=j*by+k*byz,z=k*bz;
00197         double rsq=x*x+y*y+z*z;
00198         return unit_voro.plane_intersects(x,y,z,rsq);
00199 }
00200 
00201 /** Draws the periodic domain in gnuplot format.
00202  * \param[in] fp the file handle to write to. */
00203 void unitcell::draw_domain_gnuplot(FILE *fp) {
00204         fprintf(fp,"0 0 0\n%g 0 0\n%g %g 0\n%g %g 0\n",bx,bx+bxy,by,bxy,by);
00205         fprintf(fp,"%g %g %g\n%g %g %g\n%g %g %g\n%g %g %g\n",bxy+bxz,by+byz,bz,bx+bxy+bxz,by+byz,bz,bx+bxz,byz,bz,bxz,byz,bz);
00206         fprintf(fp,"0 0 0\n%g %g 0\n\n%g %g %g\n%g %g %g\n\n",bxy,by,bxz,byz,bz,bxy+bxz,by+byz,bz);
00207         fprintf(fp,"%g 0 0\n%g %g %g\n\n%g %g 0\n%g %g %g\n\n",bx,bx+bxz,byz,bz,bx+bxy,by,bx+bxy+bxz,by+byz,bz);
00208 }
00209 
00210 /** Draws the periodic domain in POV-Ray format.
00211  * \param[in] fp the file handle to write to. */
00212 void unitcell::draw_domain_pov(FILE *fp) {
00213         fprintf(fp,"cylinder{0,0,0>,<%g,0,0>,rr}\n"
00214                    "cylinder{<%g,%g,0>,<%g,%g,0>,rr}\n",bx,bxy,by,bx+bxy,by);
00215         fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n"
00216                    "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",bxz,byz,bz,bx+bxz,byz,bz,bxy+bxz,by+byz,bz,bx+bxy+bxz,by+byz,bz);
00217         fprintf(fp,"cylinder{<0,0,0>,<%g,%g,0>,rr}\n"
00218                    "cylinder{<%g,0,0>,<%g,%g,0>,rr}\n",bxy,by,bx,bx+bxy,by);
00219         fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n"
00220                    "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",bxz,byz,bz,bxy+bxz,by+byz,bz,bx+bxz,byz,bz,bx+bxy+bxz,by+byz,bz);
00221         fprintf(fp,"cylinder{<0,0,0>,<%g,%g,%g>,rr}\n"
00222                    "cylinder{<%g,0,0>,<%g,%g,%g>,rr}\n",bxz,byz,bz,bx,bx+bxz,byz,bz);
00223         fprintf(fp,"cylinder{<%g,%g,0>,<%g,%g,%g>,rr}\n"
00224                    "cylinder{<%g,%g,0>,<%g,%g,%g>,rr}\n",bxy,by,bxy+bxz,by+byz,bz,bx+bxy,by,bx+bxy+bxz,by+byz,bz);
00225         fprintf(fp,"sphere{<0,0,0>,rr}\nsphere{<%g,0,0>,rr}\n"
00226                    "sphere{<%g,%g,0>,rr}\nsphere{<%g,%g,0>,rr}\n",bx,bxy,by,bx+bxy,by);
00227         fprintf(fp,"sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n"
00228                    "sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n",bxz,byz,bz,bx+bxz,byz,bz,bxy+bxz,by+byz,bz,bx+bxy+bxz,by+byz,bz);
00229 }
00230 
00231 }