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_
) {
36 for(l
=0;l
<nxy
;l
++) co
[l
]=0;
37 for(l
=0;l
<nxy
;l
++) mem
[l
]=init_mem
;
38 for(l
=0;l
<nxy
;l
++) id
[l
]=new int[init_mem
];
39 for(l
=0;l
<nxy
;l
++) p
[l
]=new double[ps
*init_mem
];
42 /** The container destructor frees the dynamically allocated memory. */
43 container_base_2d::~container_base_2d() {
45 for(l
=nxy
-1;l
>=0;l
--) delete [] p
[l
];
46 for(l
=nxy
-1;l
>=0;l
--) delete [] id
[l
];
53 /** The class constructor sets up the geometry of container.
54 * \param[in] (ax_,bx_) the minimum and maximum x coordinates.
55 * \param[in] (ay_,by_) the minimum and maximum y coordinates.
56 * \param[in] (nx_,ny_) the number of grid blocks in each of the three
57 * coordinate directions.
58 * \param[in] (xperiodic_,yperiodic_) flags setting whether the container is
59 * periodic in each coordinate direction.
60 * \param[in] init_mem the initial memory allocation for each block. */
61 container_2d::container_2d(double ax_
,double bx_
,double ay_
,double by_
,
62 int nx_
,int ny_
,bool xperiodic_
,bool yperiodic_
,int init_mem
)
63 : container_base_2d(ax_
,bx_
,ay_
,by_
,nx_
,ny_
,xperiodic_
,yperiodic_
,init_mem
,2),
64 vc(*this,xperiodic_
?2*nx_
+1:nx_
,yperiodic_
?2*ny_
+1:ny_
) {full_connect
=false;}
66 /** The class constructor sets up the geometry of container.
67 * \param[in] (ax_,bx_) the minimum and maximum x coordinates.
68 * \param[in] (ay_,by_) the minimum and maximum y coordinates.
69 * \param[in] (nx_,ny_) the number of grid blocks in each of the three
70 * coordinate directions.
71 * \param[in] (xperiodic_,yperiodic_) flags setting whether the container is
72 * periodic in each coordinate direction.
73 * \param[in] init_mem the initial memory allocation for each block. */
74 container_poly_2d::container_poly_2d(double ax_
,double bx_
,double ay_
,double by_
,
75 int nx_
,int ny_
,bool xperiodic_
,bool yperiodic_
,int init_mem
)
76 : container_base_2d(ax_
,bx_
,ay_
,by_
,nx_
,ny_
,xperiodic_
,yperiodic_
,init_mem
,3),
77 vc(*this,xperiodic_
?2*nx_
+1:nx_
,yperiodic_
?2*ny_
+1:ny_
) {ppr
=p
; full_connect
=false;}
79 /** Put a particle into the correct region of the container.
80 * \param[in] n the numerical ID of the inserted particle.
81 * \param[in] (x,y) the position vector of the inserted particle. */
82 void container_2d::put(int n
,double x
,double y
) {
84 if(put_locate_block(ij
,x
,y
)) {
87 double *pp
=p
[ij
]+2*co
[ij
]++;
92 /** Put a particle into the correct region of the container.
93 * \param[in] n the numerical ID of the inserted particle.
94 * \param[in] (x,y) the position vector of the inserted particle.
95 * \param[in] r the radius of the particle. */
96 void container_poly_2d::put(int n
,double x
,double y
,double r
) {
98 if(put_locate_block(ij
,x
,y
)) {
101 double *pp
=p
[ij
]+3*co
[ij
]++;
102 *(pp
++)=x
;*(pp
++)=y
;*pp
=r
;
103 if(max_radius
<r
) max_radius
=r
;
107 /** Put a particle into the correct region of the container, also recording
108 * into which region it was stored.
109 * \param[in] vo the ordering class in which to record the region.
110 * \param[in] n the numerical ID of the inserted particle.
111 * \param[in] (x,y) the position vector of the inserted particle. */
112 void container_2d::put(particle_order
&vo
,int n
,double x
,double y
) {
114 if(put_locate_block(ij
,x
,y
)) {
118 double *pp
=p
[ij
]+2*co
[ij
]++;
123 /** Put a particle into the correct region of the container, also recording
124 * into which region it was stored.
125 * \param[in] vo the ordering class in which to record the region.
126 * \param[in] n the numerical ID of the inserted particle.
127 * \param[in] (x,y) the position vector of the inserted particle.
128 * \param[in] r the radius of the particle. */
129 void container_poly_2d::put(particle_order
&vo
,int n
,double x
,double y
,double r
) {
131 if(put_locate_block(ij
,x
,y
)) {
135 double *pp
=p
[ij
]+3*co
[ij
]++;
136 *(pp
++)=x
;*(pp
++)=y
;*pp
=r
;
137 if(max_radius
<r
) max_radius
=r
;
141 /** This routine takes a particle position vector, tries to remap it into the
142 * primary domain. If successful, it computes the region into which it can be
143 * stored and checks that there is enough memory within this region to store
145 * \param[out] ij the region index.
146 * \param[in,out] (x,y) the particle position, remapped into the primary
147 * domain if necessary.
148 * \return True if the particle can be successfully placed into the container,
149 * false otherwise. */
150 inline bool container_base_2d::put_locate_block(int &ij
,double &x
,double &y
) {
151 if(put_remap(ij
,x
,y
)) {
152 if(co
[ij
]==mem
[ij
]) add_particle_memory(ij
);
155 #if VOROPP_REPORT_OUT_OF_BOUNDS ==1
156 fprintf(stderr
,"Out of bounds: (x,y)=(%g,%g)\n",x
,y
);
161 /** Takes a particle position vector and computes the region index into which
162 * it should be stored. If the container is periodic, then the routine also
163 * maps the particle position to ensure it is in the primary domain. If the
164 * container is not periodic, the routine bails out.
165 * \param[out] ij the region index.
166 * \param[in,out] (x,y) the particle position, remapped into the primary domain
168 * \return True if the particle can be successfully placed into the container,
169 * false otherwise. */
170 inline bool container_base_2d::put_remap(int &ij
,double &x
,double &y
) {
173 ij
=step_int((x
-ax
)*xsp
);
174 if(xperiodic
) {l
=step_mod(ij
,nx
);x
+=boxx
*(l
-ij
);ij
=l
;}
175 else if(ij
<0||ij
>=nx
) return false;
177 int j
=step_int((y
-ay
)*ysp
);
178 if(yperiodic
) {l
=step_mod(j
,ny
);y
+=boxy
*(l
-j
);j
=l
;}
179 else if(j
<0||j
>=ny
) return false;
185 /** Takes a position vector and attempts to remap it into the primary domain.
186 * \param[out] (ai,aj) the periodic image displacement that the vector is in,
187 * with (0,0,0) corresponding to the primary domain.
188 * \param[out] (ci,cj) the index of the block that the position vector is
189 * within, once it has been remapped.
190 * \param[in,out] (x,y) the position vector to consider, which is remapped into
191 * the primary domain during the routine.
192 * \param[out] ij the block index that the vector is within.
193 * \return True if the particle is within the container or can be remapped into
194 * it, false if it lies outside of the container bounds. */
195 inline bool container_base_2d::remap(int &ai
,int &aj
,int &ci
,int &cj
,double &x
,double &y
,int &ij
) {
196 ci
=step_int((x
-ax
)*xsp
);
198 if(xperiodic
) {ai
=step_div(ci
,nx
);x
-=ai
*(bx
-ax
);ci
-=ai
*nx
;}
202 cj
=step_int((y
-ay
)*ysp
);
204 if(yperiodic
) {aj
=step_div(cj
,ny
);y
-=aj
*(by
-ay
);cj
-=aj
*ny
;}
212 /** Takes a vector and finds the particle whose Voronoi cell contains that
213 * vector. This is equivalent to finding the particle which is nearest to the
214 * vector. Additional wall classes are not considered by this routine.
215 * \param[in] (x,y) the vector to test.
216 * \param[out] (rx,ry) the position of the particle whose Voronoi cell contains
217 * the vector. If the container is periodic, this may point
218 * to a particle in a periodic image of the primary domain.
219 * \param[out] pid the ID of the particle.
220 * \return True if a particle was found. If the container has no particles,
221 * then the search will not find a Voronoi cell and false is returned. */
222 bool container_2d::find_voronoi_cell(double x
,double y
,double &rx
,double &ry
,int &pid
) {
224 particle_record_2d w
;
227 // If the given vector lies outside the domain, but the container
228 // is periodic, then remap it back into the domain
229 if(!remap(ai
,aj
,ci
,cj
,x
,y
,ij
)) return false;
230 vc
.find_voronoi_cell(x
,y
,ci
,cj
,ij
,w
,mrs
);
234 // Assemble the position vector of the particle to be returned,
235 // applying a periodic remapping if necessary
236 if(xperiodic
) {ci
+=w
.di
;if(ci
<0||ci
>=nx
) ai
+=step_div(ci
,nx
);}
237 if(yperiodic
) {cj
+=w
.dj
;if(cj
<0||cj
>=ny
) aj
+=step_div(cj
,ny
);}
238 rx
=p
[w
.ij
][2*w
.l
]+ai
*(bx
-ax
);
239 ry
=p
[w
.ij
][2*w
.l
+1]+aj
*(by
-ay
);
244 // If no particle if found then just return false
248 /** Takes a vector and finds the particle whose Voronoi cell contains that
249 * vector. Additional wall classes are not considered by this routine.
250 * \param[in] (x,y) the vector to test.
251 * \param[out] (rx,ry) the position of the particle whose Voronoi cell contains
252 * the vector. If the container is periodic, this may point
253 * to a particle in a periodic image of the primary domain.
254 * \param[out] pid the ID of the particle.
255 * \return True if a particle was found. If the container has no particles,
256 * then the search will not find a Voronoi cell and false is returned. */
257 bool container_poly_2d::find_voronoi_cell(double x
,double y
,double &rx
,double &ry
,int &pid
) {
259 particle_record_2d w
;
262 // If the given vector lies outside the domain, but the container
263 // is periodic, then remap it back into the domain
264 if(!remap(ai
,aj
,ci
,cj
,x
,y
,ij
)) return false;
265 vc
.find_voronoi_cell(x
,y
,ci
,cj
,ij
,w
,mrs
);
269 // Assemble the position vector of the particle to be returned,
270 // applying a periodic remapping if necessary
271 if(xperiodic
) {ci
+=w
.di
;if(ci
<0||ci
>=nx
) ai
+=step_div(ci
,nx
);}
272 if(yperiodic
) {cj
+=w
.dj
;if(cj
<0||cj
>=ny
) aj
+=step_div(cj
,ny
);}
273 rx
=p
[w
.ij
][3*w
.l
]+ai
*(bx
-ax
);
274 ry
=p
[w
.ij
][3*w
.l
+1]+aj
*(by
-ay
);
279 // If no particle if found then just return false
283 /** Increase memory for a particular region.
284 * \param[in] i the index of the region to reallocate. */
285 void container_base_2d::add_particle_memory(int i
) {
286 int l
,nmem
=mem
[i
]<<1;
288 // Carry out a check on the memory allocation size, and
289 // print a status message if requested
290 if(nmem
>max_particle_memory_2d
)
291 voro_fatal_error("Absolute maximum memory allocation exceeded",VOROPP_MEMORY_ERROR
);
292 #if VOROPP_VERBOSE >=3
293 fprintf(stderr
,"Particle memory in region %d scaled up to %d\n",i
,nmem
);
296 // Allocate new memory and copy in the contents of the old arrays
297 int *idp
=new int[nmem
];
298 for(l
=0;l
<co
[i
];l
++) idp
[l
]=id
[i
][l
];
299 double *pp
=new double[ps
*nmem
];
300 for(l
=0;l
<ps
*co
[i
];l
++) pp
[l
]=p
[i
][l
];
302 // Update pointers and delete old arrays
304 delete [] id
[i
];id
[i
]=idp
;
305 delete [] p
[i
];p
[i
]=pp
;
308 /** Import a list of particles from an open file stream into the container.
309 * Entries of four numbers (Particle ID, x position, y position, z position)
310 * are searched for. If the file cannot be successfully read, then the routine
311 * causes a fatal error.
312 * \param[in] fp the file handle to read from. */
313 void container_2d::import(FILE *fp
) {
316 while((j
=fscanf(fp
,"%d %lg %lg",&i
,&x
,&y
))==3) put(i
,x
,y
);
317 if(j
!=EOF
) voro_fatal_error("File import error",VOROPP_FILE_ERROR
);
320 /** Import a list of particles from an open file stream, also storing the order
321 * of that the particles are read. Entries of four numbers (Particle ID, x
322 * position, y position, z position) are searched for. If the file cannot be
323 * successfully read, then the routine causes a fatal error.
324 * \param[in,out] vo a reference to an ordering class to use.
325 * \param[in] fp the file handle to read from. */
326 void container_2d::import(particle_order
&vo
,FILE *fp
) {
329 while((j
=fscanf(fp
,"%d %lg %lg",&i
,&x
,&y
))==3) put(vo
,i
,x
,y
);
330 if(j
!=EOF
) voro_fatal_error("File import error",VOROPP_FILE_ERROR
);
333 /** Import a list of particles from an open file stream into the container.
334 * Entries of five numbers (Particle ID, x position, y position, z position,
335 * radius) are searched for. If the file cannot be successfully read, then the
336 * routine causes a fatal error.
337 * \param[in] fp the file handle to read from. */
338 void container_poly_2d::import(FILE *fp
) {
341 while((j
=fscanf(fp
,"%d %lg %lg %lg",&i
,&x
,&y
,&r
))==4) put(i
,x
,y
,r
);
342 if(j
!=EOF
) voro_fatal_error("File import error",VOROPP_FILE_ERROR
);
345 /** Import a list of particles from an open file stream, also storing the order
346 * of that the particles are read. Entries of four numbers (Particle ID, x
347 * position, y position, z position, radius) are searched for. If the file
348 * cannot be successfully read, then the routine causes a fatal error.
349 * \param[in,out] vo a reference to an ordering class to use.
350 * \param[in] fp the file handle to read from. */
351 void container_poly_2d::import(particle_order
&vo
,FILE *fp
) {
354 while((j
=fscanf(fp
,"%d %lg %lg %lg",&i
,&x
,&y
,&r
))==4) put(vo
,i
,x
,y
,r
);
355 if(j
!=EOF
) voro_fatal_error("File import error",VOROPP_FILE_ERROR
);
358 /** Outputs the a list of all the container regions along with the number of
359 * particles stored within each. */
360 void container_base_2d::region_count() {
362 for(j
=0;j
<ny
;j
++) for(i
=0;i
<nx
;i
++)
363 printf("Region (%d,%d): %d particles\n",i
,j
,*(cop
++));
366 /** Clears a container of particles. */
367 void container_2d::clear() {
368 for(int *cop
=co
;cop
<co
+nxy
;cop
++) *cop
=0;
371 /** Clears a container of particles, also clearing resetting the maximum radius
373 void container_poly_2d::clear() {
374 for(int *cop
=co
;cop
<co
+nxy
;cop
++) *cop
=0;
378 /** Computes all the Voronoi cells and saves customized information about them.
379 * \param[in] format the custom output string to use.
380 * \param[in] fp a file handle to write to. */
381 void container_2d::print_custom(const char *format
,FILE *fp
) {
382 c_loop_all_2d
vl(*this);
383 print_custom(vl
,format
,fp
);
386 /** Computes all the Voronoi cells and saves customized
387 * information about them.
388 * \param[in] format the custom output string to use.
389 * \param[in] fp a file handle to write to. */
390 void container_poly_2d::print_custom(const char *format
,FILE *fp
) {
391 c_loop_all_2d
vl(*this);
392 print_custom(vl
,format
,fp
);
395 /** Computes all the Voronoi cells and saves customized information about them.
396 * \param[in] format the custom output string to use.
397 * \param[in] filename the name of the file to write to. */
398 void container_2d::print_custom(const char *format
,const char *filename
) {
399 FILE *fp
=safe_fopen(filename
,"w");
400 print_custom(format
,fp
);
404 /** Computes all the Voronoi cells and saves customized
405 * information about them
406 * \param[in] format the custom output string to use.
407 * \param[in] filename the name of the file to write to. */
408 void container_poly_2d::print_custom(const char *format
,const char *filename
) {
409 FILE *fp
=safe_fopen(filename
,"w");
410 print_custom(format
,fp
);
414 /** Computes all of the Voronoi cells in the container, but does nothing
415 * with the output. It is useful for measuring the pure computation time
416 * of the Voronoi algorithm, without any additional calculations such as
417 * volume evaluation or cell output. */
418 void container_2d::compute_all_cells() {
420 c_loop_all_2d
vl(*this);
421 if(vl
.start()) do compute_cell(c
,vl
);
425 /** Computes all of the Voronoi cells in the container, but does nothing
426 * with the output. It is useful for measuring the pure computation time
427 * of the Voronoi algorithm, without any additional calculations such as
428 * volume evaluation or cell output. */
429 void container_poly_2d::compute_all_cells() {
431 c_loop_all_2d
vl(*this);
432 if(vl
.start()) do compute_cell(c
,vl
);while(vl
.inc());
435 /** Calculates all of the Voronoi cells and sums their volumes. In most cases
436 * without walls, the sum of the Voronoi cell volumes should equal the volume
437 * of the container to numerical precision.
438 * \return The sum of all of the computed Voronoi volumes. */
439 double container_2d::sum_cell_areas() {
442 c_loop_all_2d
vl(*this);
443 if(vl
.start()) do if(compute_cell(c
,vl
)) area
+=c
.area();while(vl
.inc());
447 /** Calculates all of the Voronoi cells and sums their volumes. In most cases
448 * without walls, the sum of the Voronoi cell volumes should equal the volume
449 * of the container to numerical precision.
450 * \return The sum of all of the computed Voronoi volumes. */
451 double container_poly_2d::sum_cell_areas() {
454 c_loop_all_2d
vl(*this);
455 if(vl
.start()) do if(compute_cell(c
,vl
)) area
+=c
.area();while(vl
.inc());
459 /** This function tests to see if a given vector lies within the container
460 * bounds and any walls.
461 * \param[in] (x,y) the position vector to be tested.
462 * \return True if the point is inside the container, false if the point is
464 bool container_base_2d::point_inside(double x
,double y
) {
465 if(x
<ax
||x
>bx
||y
<ay
||y
>by
) return false;
466 return point_inside_walls(x
,y
);
469 /** Draws an outline of the domain in gnuplot format.
470 * \param[in] fp the file handle to write to. */
471 void container_base_2d::draw_domain_gnuplot(FILE *fp
) {
472 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
);
475 /** Draws an outline of the domain in POV-Ray format.
476 * \param[in] fp the file handle to write to. */
477 void container_base_2d::draw_domain_pov(FILE *fp
) {
478 fprintf(fp
,"cylinder{<%g,%g,0>,<%g,%g,0>,rr}\n"
479 "cylinder{<%g,%g,0>,<%g,%g,0>,rr}\n",ax
,ay
,bx
,ay
,ax
,by
,bx
,by
);
480 fprintf(fp
,"cylinder{<%g,%g,0>,<%g,%g,0>,rr}\n"
481 "cylinder{<%g,%g,0>,<%g,%g,0>,rr}\n",ax
,ay
,ax
,by
,bx
,ay
,bx
,by
);
482 fprintf(fp
,"sphere{<%g,%g,0>,rr}\nsphere{<%g,%g,0>,rr}\n"
483 "sphere{<%g,%g,0>,rr}\nsphere{<%g,%g,0>,rr}\n",ax
,ay
,bx
,ay
,ax
,by
,bx
,by
);
487 /** The wall_list constructor sets up an array of pointers to wall classes. */
488 wall_list_2d::wall_list_2d() : walls(new wall_2d
*[init_wall_size
]), wep(walls
), wel(walls
+init_wall_size
),
489 current_wall_size(init_wall_size
) {}
491 /** The wall_list destructor frees the array of pointers to the wall classes.
493 wall_list_2d::~wall_list_2d() {
497 /** Adds all of the walls on another wall_list to this class.
498 * \param[in] wl a reference to the wall class. */
499 void wall_list_2d::add_wall(wall_list_2d
&wl
) {
500 for(wall_2d
**wp
=wl
.walls
;wp
<wl
.wep
;wp
++) add_wall(*wp
);
503 /** Deallocates all of the wall classes pointed to by the wall_list. */
504 void wall_list_2d::deallocate() {
505 for(wall_2d
**wp
=walls
;wp
<wep
;wp
++) delete *wp
;
508 /** Increases the memory allocation for the walls array. */
509 void wall_list_2d::increase_wall_memory() {
510 current_wall_size
<<=1;
511 if(current_wall_size
>max_wall_size
)
512 voro_fatal_error("Wall memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR
);
513 wall_2d
**nwalls
=new wall_2d
*[current_wall_size
],**nwp
=nwalls
,**wp
=walls
;
514 while(wp
<wep
) *(nwp
++)=*(wp
++);
516 walls
=nwalls
;wel
=walls
+current_wall_size
;wep
=nwp
;