Voro++
|
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 }