1 // Voro++, a 3D cell-based Voronoi library
3 // Author : Chris H. Rycroft (LBL / UC Berkeley)
4 // Email : chr@alum.mit.edu
5 // Date : August 30th 2011
7 /** \file container_2d.cc
8 * \brief Function implementations for the container_2d and related classes. */
10 #include "container_2d.hh"
14 /** The class constructor sets up the geometry of container, initializing the
15 * minimum and maximum coordinates in each direction, and setting whether each
16 * direction is periodic or not. It divides the container into a rectangular
17 * grid of blocks, and allocates memory for each of these for storing particle
19 * \param[in] (ax_,bx_) the minimum and maximum x coordinates.
20 * \param[in] (ay_,by_) the minimum and maximum y coordinates.
21 * \param[in] (nx_,ny_) the number of grid blocks in each of the three
22 * coordinate directions.
23 * \param[in] (xperiodic_,yperiodic_) flags setting whether the container is
24 * periodic in each coordinate direction.
25 * \param[in] init_mem the initial memory allocation for each block.
26 * \param[in] ps_ the number of floating point entries to store for each
28 container_base_2d::container_base_2d(double ax_
,double bx_
,double ay_
,double by_
,
29 int nx_
,int ny_
,bool xperiodic_
,bool yperiodic_
,int init_mem
,int ps_
)
30 : voro_base_2d(nx_
,ny_
,(bx_
-ax_
)/nx_
,(by_
-ay_
)/ny_
),
31 ax(ax_
), bx(bx_
), ay(ay_
), by(by_
), xperiodic(xperiodic_
), yperiodic(yperiodic_
),
32 id(new int*[nxy
]), p(new double*[nxy
]), co(new int[nxy
]), mem(new int[nxy
]), ps(ps_
) {
35 for(l
=0;l
<nxy
;l
++) co
[l
]=0;
36 for(l
=0;l
<nxy
;l
++) mem
[l
]=init_mem
;
37 for(l
=0;l
<nxy
;l
++) id
[l
]=new int[init_mem
];
38 for(l
=0;l
<nxy
;l
++) p
[l
]=new double[ps
*init_mem
];
41 /** The container destructor frees the dynamically allocated memory. */
42 container_base_2d::~container_base_2d() {
44 for(l
=nxy
-1;l
>=0;l
--) delete [] p
[l
];
45 for(l
=nxy
-1;l
>=0;l
--) delete [] id
[l
];
52 /** The class constructor sets up the geometry of container.
53 * \param[in] (ax_,bx_) the minimum and maximum x coordinates.
54 * \param[in] (ay_,by_) the minimum and maximum y coordinates.
55 * \param[in] (nx_,ny_) the number of grid blocks in each of the three
56 * coordinate directions.
57 * \param[in] (xperiodic_,yperiodic_) flags setting whether the container is
58 * periodic in each coordinate direction.
59 * \param[in] init_mem the initial memory allocation for each block. */
60 container_2d::container_2d(double ax_
,double bx_
,double ay_
,double by_
,
61 int nx_
,int ny_
,bool xperiodic_
,bool yperiodic_
,int init_mem
)
62 : container_base_2d(ax_
,bx_
,ay_
,by_
,nx_
,ny_
,xperiodic_
,yperiodic_
,init_mem
,2),
63 vc(*this,xperiodic_
?2*nx_
+1:nx_
,yperiodic_
?2*ny_
+1:ny_
) {}
65 /** The class constructor sets up the geometry of container.
66 * \param[in] (ax_,bx_) the minimum and maximum x coordinates.
67 * \param[in] (ay_,by_) the minimum and maximum y coordinates.
68 * \param[in] (nx_,ny_) the number of grid blocks in each of the three
69 * coordinate directions.
70 * \param[in] (xperiodic_,yperiodic_) flags setting whether the container is
71 * periodic in each coordinate direction.
72 * \param[in] init_mem the initial memory allocation for each block. */
73 container_poly_2d::container_poly_2d(double ax_
,double bx_
,double ay_
,double by_
,
74 int nx_
,int ny_
,bool xperiodic_
,bool yperiodic_
,int init_mem
)
75 : container_base_2d(ax_
,bx_
,ay_
,by_
,nx_
,ny_
,xperiodic_
,yperiodic_
,init_mem
,3),
76 vc(*this,xperiodic_
?2*nx_
+1:nx_
,yperiodic_
?2*ny_
+1:ny_
) {ppr
=p
;}
78 /** Put a particle into the correct region of the container.
79 * \param[in] n the numerical ID of the inserted particle.
80 * \param[in] (x,y) the position vector of the inserted particle. */
81 void container_2d::put(int n
,double x
,double y
) {
83 if(put_locate_block(ij
,x
,y
)) {
86 double *pp
=p
[ij
]+2*co
[ij
]++;
91 /** Put a particle into the correct region of the container.
92 * \param[in] n the numerical ID of the inserted particle.
93 * \param[in] (x,y) the position vector of the inserted particle.
94 * \param[in] r the radius of the particle. */
95 void container_poly_2d::put(int n
,double x
,double y
,double r
) {
97 if(put_locate_block(ij
,x
,y
)) {
100 double *pp
=p
[ij
]+3*co
[ij
]++;
101 *(pp
++)=x
;*(pp
++)=y
;*pp
=r
;
102 if(max_radius
<r
) max_radius
=r
;
106 /** Put a particle into the correct region of the container, also recording
107 * into which region it was stored.
108 * \param[in] vo the ordering class in which to record the region.
109 * \param[in] n the numerical ID of the inserted particle.
110 * \param[in] (x,y) the position vector of the inserted particle. */
111 void container_2d::put(particle_order
&vo
,int n
,double x
,double y
) {
113 if(put_locate_block(ij
,x
,y
)) {
117 double *pp
=p
[ij
]+2*co
[ij
]++;
122 /** Put a particle into the correct region of the container, also recording
123 * into which region it was stored.
124 * \param[in] vo the ordering class in which to record the region.
125 * \param[in] n the numerical ID of the inserted particle.
126 * \param[in] (x,y) the position vector of the inserted particle.
127 * \param[in] r the radius of the particle. */
128 void container_poly_2d::put(particle_order
&vo
,int n
,double x
,double y
,double r
) {
130 if(put_locate_block(ij
,x
,y
)) {
134 double *pp
=p
[ij
]+3*co
[ij
]++;
135 *(pp
++)=x
;*(pp
++)=y
;*pp
=r
;
136 if(max_radius
<r
) max_radius
=r
;
140 /** This routine takes a particle position vector, tries to remap it into the
141 * primary domain. If successful, it computes the region into which it can be
142 * stored and checks that there is enough memory within this region to store
144 * \param[out] ij the region index.
145 * \param[in,out] (x,y) the particle position, remapped into the primary
146 * domain if necessary.
147 * \return True if the particle can be successfully placed into the container,
148 * false otherwise. */
149 inline bool container_base_2d::put_locate_block(int &ij
,double &x
,double &y
) {
150 if(put_remap(ij
,x
,y
)) {
151 if(co
[ij
]==mem
[ij
]) add_particle_memory(ij
);
154 #if VOROPP_REPORT_OUT_OF_BOUNDS ==1
155 fprintf(stderr
,"Out of bounds: (x,y)=(%g,%g)\n",x
,y
);
160 /** Takes a particle position vector and computes the region index into which
161 * it should be stored. If the container is periodic, then the routine also
162 * maps the particle position to ensure it is in the primary domain. If the
163 * container is not periodic, the routine bails out.
164 * \param[out] ij the region index.
165 * \param[in,out] (x,y) the particle position, remapped into the primary domain
167 * \return True if the particle can be successfully placed into the container,
168 * false otherwise. */
169 inline bool container_base_2d::put_remap(int &ij
,double &x
,double &y
) {
172 ij
=step_int((x
-ax
)*xsp
);
173 if(xperiodic
) {l
=step_mod(ij
,nx
);x
+=boxx
*(l
-ij
);ij
=l
;}
174 else if(ij
<0||ij
>=nx
) return false;
176 int j
=step_int((y
-ay
)*ysp
);
177 if(yperiodic
) {l
=step_mod(j
,ny
);y
+=boxy
*(l
-j
);j
=l
;}
178 else if(j
<0||j
>=ny
) return false;
184 /** Takes a position vector and attempts to remap it into the primary domain.
185 * \param[out] (ai,aj) the periodic image displacement that the vector is in,
186 * with (0,0,0) corresponding to the primary domain.
187 * \param[out] (ci,cj) the index of the block that the position vector is
188 * within, once it has been remapped.
189 * \param[in,out] (x,y) the position vector to consider, which is remapped into
190 * the primary domain during the routine.
191 * \param[out] ij the block index that the vector is within.
192 * \return True if the particle is within the container or can be remapped into
193 * it, false if it lies outside of the container bounds. */
194 inline bool container_base_2d::remap(int &ai
,int &aj
,int &ci
,int &cj
,double &x
,double &y
,int &ij
) {
195 ci
=step_int((x
-ax
)*xsp
);
197 if(xperiodic
) {ai
=step_div(ci
,nx
);x
-=ai
*(bx
-ax
);ci
-=ai
*nx
;}
201 cj
=step_int((y
-ay
)*ysp
);
203 if(yperiodic
) {aj
=step_div(cj
,ny
);y
-=aj
*(by
-ay
);cj
-=aj
*ny
;}
211 /** Takes a vector and finds the particle whose Voronoi cell contains that
212 * vector. This is equivalent to finding the particle which is nearest to the
213 * vector. Additional wall classes are not considered by this routine.
214 * \param[in] (x,y) the vector to test.
215 * \param[out] (rx,ry) the position of the particle whose Voronoi cell contains
216 * the vector. If the container is periodic, this may point
217 * to a particle in a periodic image of the primary domain.
218 * \param[out] pid the ID of the particle.
219 * \return True if a particle was found. If the container has no particles,
220 * then the search will not find a Voronoi cell and false is returned. */
221 bool container_2d::find_voronoi_cell(double x
,double y
,double &rx
,double &ry
,int &pid
) {
223 particle_record_2d w
;
226 // If the given vector lies outside the domain, but the container
227 // is periodic, then remap it back into the domain
228 if(!remap(ai
,aj
,ci
,cj
,x
,y
,ij
)) return false;
229 vc
.find_voronoi_cell(x
,y
,ci
,cj
,ij
,w
,mrs
);
233 // Assemble the position vector of the particle to be returned,
234 // applying a periodic remapping if necessary
235 if(xperiodic
) {ci
+=w
.di
;if(ci
<0||ci
>=nx
) ai
+=step_div(ci
,nx
);}
236 if(yperiodic
) {cj
+=w
.dj
;if(cj
<0||cj
>=ny
) aj
+=step_div(cj
,ny
);}
237 rx
=p
[w
.ij
][2*w
.l
]+ai
*(bx
-ax
);
238 ry
=p
[w
.ij
][2*w
.l
+1]+aj
*(by
-ay
);
243 // If no particle if found then just return false
247 /** Takes a vector and finds the particle whose Voronoi cell contains that
248 * vector. Additional wall classes are not considered by this routine.
249 * \param[in] (x,y) the vector to test.
250 * \param[out] (rx,ry) the position of the particle whose Voronoi cell contains
251 * the vector. If the container is periodic, this may point
252 * to a particle in a periodic image of the primary domain.
253 * \param[out] pid the ID of the particle.
254 * \return True if a particle was found. If the container has no particles,
255 * then the search will not find a Voronoi cell and false is returned. */
256 bool container_poly_2d::find_voronoi_cell(double x
,double y
,double &rx
,double &ry
,int &pid
) {
258 particle_record_2d w
;
261 // If the given vector lies outside the domain, but the container
262 // is periodic, then remap it back into the domain
263 if(!remap(ai
,aj
,ci
,cj
,x
,y
,ij
)) return false;
264 vc
.find_voronoi_cell(x
,y
,ci
,cj
,ij
,w
,mrs
);
268 // Assemble the position vector of the particle to be returned,
269 // applying a periodic remapping if necessary
270 if(xperiodic
) {ci
+=w
.di
;if(ci
<0||ci
>=nx
) ai
+=step_div(ci
,nx
);}
271 if(yperiodic
) {cj
+=w
.dj
;if(cj
<0||cj
>=ny
) aj
+=step_div(cj
,ny
);}
272 rx
=p
[w
.ij
][3*w
.l
]+ai
*(bx
-ax
);
273 ry
=p
[w
.ij
][3*w
.l
+1]+aj
*(by
-ay
);
278 // If no particle if found then just return false
282 /** Increase memory for a particular region.
283 * \param[in] i the index of the region to reallocate. */
284 void container_base_2d::add_particle_memory(int i
) {
285 int l
,nmem
=mem
[i
]<<1;
287 // Carry out a check on the memory allocation size, and
288 // print a status message if requested
289 if(nmem
>max_particle_memory_2d
)
290 voro_fatal_error("Absolute maximum memory allocation exceeded",VOROPP_MEMORY_ERROR
);
291 #if VOROPP_VERBOSE >=3
292 fprintf(stderr
,"Particle memory in region %d scaled up to %d\n",i
,nmem
);
295 // Allocate new memory and copy in the contents of the old arrays
296 int *idp
=new int[nmem
];
297 for(l
=0;l
<co
[i
];l
++) idp
[l
]=id
[i
][l
];
298 double *pp
=new double[ps
*nmem
];
299 for(l
=0;l
<ps
*co
[i
];l
++) pp
[l
]=p
[i
][l
];
301 // Update pointers and delete old arrays
303 delete [] id
[i
];id
[i
]=idp
;
304 delete [] p
[i
];p
[i
]=pp
;
307 /** Import a list of particles from an open file stream into the container.
308 * Entries of four numbers (Particle ID, x position, y position, z position)
309 * are searched for. If the file cannot be successfully read, then the routine
310 * causes a fatal error.
311 * \param[in] fp the file handle to read from. */
312 void container_2d::import(FILE *fp
) {
315 while((j
=fscanf(fp
,"%d %lg %lg",&i
,&x
,&y
))==3) put(i
,x
,y
);
316 if(j
!=EOF
) voro_fatal_error("File import error",VOROPP_FILE_ERROR
);
319 /** Import a list of particles from an open file stream, also storing the order
320 * of that the particles are read. Entries of four numbers (Particle ID, x
321 * position, y position, z position) are searched for. If the file cannot be
322 * successfully read, then the routine causes a fatal error.
323 * \param[in,out] vo a reference to an ordering class to use.
324 * \param[in] fp the file handle to read from. */
325 void container_2d::import(particle_order
&vo
,FILE *fp
) {
328 while((j
=fscanf(fp
,"%d %lg %lg",&i
,&x
,&y
))==3) put(vo
,i
,x
,y
);
329 if(j
!=EOF
) voro_fatal_error("File import error",VOROPP_FILE_ERROR
);
332 /** Import a list of particles from an open file stream into the container.
333 * Entries of five numbers (Particle ID, x position, y position, z position,
334 * radius) are searched for. If the file cannot be successfully read, then the
335 * routine causes a fatal error.
336 * \param[in] fp the file handle to read from. */
337 void container_poly_2d::import(FILE *fp
) {
340 while((j
=fscanf(fp
,"%d %lg %lg %lg",&i
,&x
,&y
,&r
))==4) put(i
,x
,y
,r
);
341 if(j
!=EOF
) voro_fatal_error("File import error",VOROPP_FILE_ERROR
);
344 /** Import a list of particles from an open file stream, also storing the order
345 * of that the particles are read. Entries of four numbers (Particle ID, x
346 * position, y position, z position, radius) are searched for. If the file
347 * cannot be successfully read, then the routine causes a fatal error.
348 * \param[in,out] vo a reference to an ordering class to use.
349 * \param[in] fp the file handle to read from. */
350 void container_poly_2d::import(particle_order
&vo
,FILE *fp
) {
353 while((j
=fscanf(fp
,"%d %lg %lg %lg",&i
,&x
,&y
,&r
))==4) put(vo
,i
,x
,y
,r
);
354 if(j
!=EOF
) voro_fatal_error("File import error",VOROPP_FILE_ERROR
);
357 /** Outputs the a list of all the container regions along with the number of
358 * particles stored within each. */
359 void container_base_2d::region_count() {
361 for(j
=0;j
<ny
;j
++) for(i
=0;i
<nx
;i
++)
362 printf("Region (%d,%d): %d particles\n",i
,j
,*(cop
++));
365 /** Clears a container of particles. */
366 void container_2d::clear() {
367 for(int *cop
=co
;cop
<co
+nxy
;cop
++) *cop
=0;
370 /** Clears a container of particles, also clearing resetting the maximum radius
372 void container_poly_2d::clear() {
373 for(int *cop
=co
;cop
<co
+nxy
;cop
++) *cop
=0;
377 /** Computes all the Voronoi cells and saves customized information about them.
378 * \param[in] format the custom output string to use.
379 * \param[in] fp a file handle to write to. */
380 void container_2d::print_custom(const char *format
,FILE *fp
) {
381 c_loop_all_2d
vl(*this);
382 print_custom(vl
,format
,fp
);
385 /** Computes all the Voronoi cells and saves customized
386 * information about them.
387 * \param[in] format the custom output string to use.
388 * \param[in] fp a file handle to write to. */
389 void container_poly_2d::print_custom(const char *format
,FILE *fp
) {
390 c_loop_all_2d
vl(*this);
391 print_custom(vl
,format
,fp
);
394 /** Computes all the Voronoi cells and saves customized information about them.
395 * \param[in] format the custom output string to use.
396 * \param[in] filename the name of the file to write to. */
397 void container_2d::print_custom(const char *format
,const char *filename
) {
398 FILE *fp
=safe_fopen(filename
,"w");
399 print_custom(format
,fp
);
403 /** Computes all the Voronoi cells and saves customized
404 * information about them
405 * \param[in] format the custom output string to use.
406 * \param[in] filename the name of the file to write to. */
407 void container_poly_2d::print_custom(const char *format
,const char *filename
) {
408 FILE *fp
=safe_fopen(filename
,"w");
409 print_custom(format
,fp
);
413 /** Computes all of the Voronoi cells in the container, but does nothing
414 * with the output. It is useful for measuring the pure computation time
415 * of the Voronoi algorithm, without any additional calculations such as
416 * volume evaluation or cell output. */
417 void container_2d::compute_all_cells() {
419 c_loop_all_2d
vl(*this);
420 if(vl
.start()) do compute_cell(c
,vl
);
424 /** Computes all of the Voronoi cells in the container, but does nothing
425 * with the output. It is useful for measuring the pure computation time
426 * of the Voronoi algorithm, without any additional calculations such as
427 * volume evaluation or cell output. */
428 void container_poly_2d::compute_all_cells() {
430 c_loop_all_2d
vl(*this);
431 if(vl
.start()) do compute_cell(c
,vl
);while(vl
.inc());
434 /** Calculates all of the Voronoi cells and sums their volumes. In most cases
435 * without walls, the sum of the Voronoi cell volumes should equal the volume
436 * of the container to numerical precision.
437 * \return The sum of all of the computed Voronoi volumes. */
438 double container_2d::sum_cell_areas() {
441 c_loop_all_2d
vl(*this);
442 if(vl
.start()) do if(compute_cell(c
,vl
)) area
+=c
.area();while(vl
.inc());
446 /** Calculates all of the Voronoi cells and sums their volumes. In most cases
447 * without walls, the sum of the Voronoi cell volumes should equal the volume
448 * of the container to numerical precision.
449 * \return The sum of all of the computed Voronoi volumes. */
450 double container_poly_2d::sum_cell_areas() {
453 c_loop_all_2d
vl(*this);
454 if(vl
.start()) do if(compute_cell(c
,vl
)) area
+=c
.area();while(vl
.inc());
458 /** This function tests to see if a given vector lies within the container
459 * bounds and any walls.
460 * \param[in] (x,y) the position vector to be tested.
461 * \return True if the point is inside the container, false if the point is
463 bool container_base_2d::point_inside(double x
,double y
) {
464 if(x
<ax
||x
>bx
||y
<ay
||y
>by
) return false;
465 return point_inside_walls(x
,y
);
468 /** Draws an outline of the domain in gnuplot format.
469 * \param[in] fp the file handle to write to. */
470 void container_base_2d::draw_domain_gnuplot(FILE *fp
) {
471 fprintf(fp
,"%g %g\n%g %g\n%g %g\n%g %g\n%g %g\n",ax
,ay
,bx
,ay
,bx
,by
,ax
,by
,ax
,ay
);
474 /** Draws an outline of the domain in POV-Ray format.
475 * \param[in] fp the file handle to write to. */
476 void container_base_2d::draw_domain_pov(FILE *fp
) {
477 fprintf(fp
,"cylinder{<%g,%g,0>,<%g,%g,0>,rr}\n"
478 "cylinder{<%g,%g,0>,<%g,%g,0>,rr}\n",ax
,ay
,bx
,ay
,ax
,by
,bx
,by
);
479 fprintf(fp
,"cylinder{<%g,%g,0>,<%g,%g,0>,rr}\n"
480 "cylinder{<%g,%g,0>,<%g,%g,0>,rr}\n",ax
,ay
,ax
,by
,bx
,ay
,bx
,by
);
481 fprintf(fp
,"sphere{<%g,%g,0>,rr}\nsphere{<%g,%g,0>,rr}\n"
482 "sphere{<%g,%g,0>,rr}\nsphere{<%g,%g,0>,rr}\n",ax
,ay
,bx
,ay
,ax
,by
,bx
,by
);
486 /** The wall_list constructor sets up an array of pointers to wall classes. */
487 wall_list_2d::wall_list_2d() : walls(new wall_2d
*[init_wall_size
]), wep(walls
), wel(walls
+init_wall_size
),
488 current_wall_size(init_wall_size
) {}
490 /** The wall_list destructor frees the array of pointers to the wall classes.
492 wall_list_2d::~wall_list_2d() {
496 /** Adds all of the walls on another wall_list to this class.
497 * \param[in] wl a reference to the wall class. */
498 void wall_list_2d::add_wall(wall_list_2d
&wl
) {
499 for(wall_2d
**wp
=wl
.walls
;wp
<wl
.wep
;wp
++) add_wall(*wp
);
502 /** Deallocates all of the wall classes pointed to by the wall_list. */
503 void wall_list_2d::deallocate() {
504 for(wall_2d
**wp
=walls
;wp
<wep
;wp
++) delete *wp
;
507 /** Increases the memory allocation for the walls array. */
508 void wall_list_2d::increase_wall_memory() {
509 current_wall_size
<<=1;
510 if(current_wall_size
>max_wall_size
)
511 voro_fatal_error("Wall memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR
);
512 wall_2d
**nwalls
=new wall_2d
*[current_wall_size
],**nwp
=nwalls
,**wp
=walls
;
513 while(wp
<wep
) *(nwp
++)=*(wp
++);
515 walls
=nwalls
;wel
=walls
+current_wall_size
;wep
=nwp
;