Strip extra spaces from code.
[voro++.git] / branches / 2d / src / container_2d.cc
bloba913958ea26415afd4ca9d66f4d8d2b360bb2887
1 // Voro++, a 3D cell-based Voronoi library
2 //
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"
12 namespace voro {
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
18 * positions and IDs.
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
27 * particle. */
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_) {
33 int l;
34 //totpar=0;
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() {
43 int l;
44 for(l=nxy-1;l>=0;l--) delete [] p[l];
45 for(l=nxy-1;l>=0;l--) delete [] id[l];
46 delete [] id;
47 delete [] p;
48 delete [] co;
49 delete [] mem;
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) {
82 int ij;
83 if(put_locate_block(ij,x,y)) {
84 //totpar++;
85 id[ij][co[ij]]=n;
86 double *pp=p[ij]+2*co[ij]++;
87 *(pp++)=x;*pp=y;
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) {
96 int ij;
97 if(put_locate_block(ij,x,y)) {
98 //totpar++;
99 id[ij][co[ij]]=n;
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) {
112 int ij;
113 if(put_locate_block(ij,x,y)) {
114 //totpar++;
115 id[ij][co[ij]]=n;
116 vo.add(ij,co[ij]);
117 double *pp=p[ij]+2*co[ij]++;
118 *(pp++)=x;*pp=y;
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) {
129 int ij;
130 if(put_locate_block(ij,x,y)) {
131 //totpar++;
132 id[ij][co[ij]]=n;
133 vo.add(ij,co[ij]);
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
143 * it.
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);
152 return true;
154 #if VOROPP_REPORT_OUT_OF_BOUNDS ==1
155 fprintf(stderr,"Out of bounds: (x,y)=(%g,%g)\n",x,y);
156 #endif
157 return false;
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
166 * if necessary.
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) {
170 int l;
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;
180 ij+=nx*j;
181 return true;
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);
196 if(ci<0||ci>=nx) {
197 if(xperiodic) {ai=step_div(ci,nx);x-=ai*(bx-ax);ci-=ai*nx;}
198 else return false;
199 } else ai=0;
201 cj=step_int((y-ay)*ysp);
202 if(cj<0||cj>=ny) {
203 if(yperiodic) {aj=step_div(cj,ny);y-=aj*(by-ay);cj-=aj*ny;}
204 else return false;
205 } else aj=0;
207 ij=ci+nx*cj;
208 return true;
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) {
222 int ai,aj,ci,cj,ij;
223 particle_record_2d w;
224 double mrs;
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);
231 if(w.ij!=-1) {
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);
239 pid=id[w.ij][w.l];
240 return true;
243 // If no particle if found then just return false
244 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) {
257 int ai,aj,ci,cj,ij;
258 particle_record_2d w;
259 double mrs;
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);
266 if(w.ij!=-1) {
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);
274 pid=id[w.ij][w.l];
275 return true;
278 // If no particle if found then just return false
279 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);
293 #endif
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
302 mem[i]=nmem;
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) {
313 int i,j;
314 double x,y;
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) {
326 int i,j;
327 double x,y;
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) {
338 int i,j;
339 double x,y,r;
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) {
351 int i,j;
352 double x,y,r;
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() {
360 int i,j,*cop=co;
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
371 * to zero. */
372 void container_poly_2d::clear() {
373 for(int *cop=co;cop<co+nxy;cop++) *cop=0;
374 max_radius=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);
400 fclose(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);
410 fclose(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() {
418 voronoicell_2d c;
419 c_loop_all_2d vl(*this);
420 if(vl.start()) do compute_cell(c,vl);
421 while(vl.inc());
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() {
429 voronoicell_2d c;
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() {
439 voronoicell_2d c;
440 double area=0;
441 c_loop_all_2d vl(*this);
442 if(vl.start()) do if(compute_cell(c,vl)) area+=c.area();while(vl.inc());
443 return area;
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() {
451 voronoicell_2d c;
452 double area=0;
453 c_loop_all_2d vl(*this);
454 if(vl.start()) do if(compute_cell(c,vl)) area+=c.area();while(vl.inc());
455 return area;
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
462 * outside. */
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() {
493 delete [] walls;
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++);
514 delete [] walls;
515 walls=nwalls;wel=walls+current_wall_size;wep=nwp;