torus.cc – Creating a custom wall object
In addition to the standard spherical, cylinder, conical, and planar wall objects that have been discussed in the previous examples, it is possible to create custom wall objects as derived classes. This example demonstrates this by carrying out a Voronoi tessellation in a torus, making use of a custom toroidal wall class.
On line 31, the class wall_torus
is derived from the pure
virtual wall
class. A class constructor is then defined on lines
40–41, that initializes the major and minor radii of the torus. Two key
functions must then be specified. The function point_inside()
returns whether a given vector is inside or outside torus or not. While this is
not used in the current example, it is used by several of the routines in the
container
class that determine whether a point is inside the
container domain, by checking that it is within all the wall objects that have
been specified.
On lines 56–74, a template called cut_cell_base()
is
defined, that will cut a Voronoi cell in response to the toroidal wall. In a
manner similar to the cylinder, sphere, and cone wall classes, the toroidal
wall is handled by making a single approximating plane cut. This function
returns true if the cell is still present after the plane cut, and false
otherwise.
On lines 79–82, two virtual functions called cut_cell()
are defined for voronoicell
and voronoicell_neighbor
classes. When the container
class is computing a Voronoi cell, it
will call all of the corresponding cut_cell()
functions for each
wall that has been created. These two functions just call the different
instances of the cut_cell_base()
. (C++ does not support virtual
templates, otherwise it would be possible to combine the
cut_cell()
and cut_cell_base()
sections into a single
declaration.)
The main section of the code from lines 92–112 follows the same layout as the previous cylinder example. POV-Ray files of the particles and the Voronoi cells are outputted to file, and these can be rendered using the header file “torus.pov”.
Code listing
1: // Custom wall class example code 2: // 3: // Author : Chris H. Rycroft (LBL / UC Berkeley) 4: // Email : chr@alum.mit.edu 5: // Date : August 30th 2011 6: 7: #include "voro++.hh" 8: using namespace voro; 9: 10: // Major and minor torus radii 11: const double arad=9,brad=3.5; 12: 13: // The outer radius of the torus, that determines how big the container should 14: // be 15: const double crad=arad+brad; 16: 17: // Set up constants for the container geometry 18: const double x_min=-crad-0.5,x_max=crad+0.5; 19: const double y_min=-crad-0.5,y_max=crad+0.5; 20: const double z_min=-brad-0.5,z_max=brad+0.5; 21: 22: // Set the computational grid size 23: const int n_x=10,n_y=10,n_z=3; 24: 25: // This class creates a custom toroidal wall object that is centered on the 26: // origin and is aligned with the xy plane. It is derived from the pure virtual 27: // "wall" class. The "wall" class contains virtual functions for cutting the 28: // Voronoi cell in response to a wall, and for telling whether a given point is 29: // inside the wall or not. In this derived class, specific implementations of 30: // these functions are given. 31: class wall_torus : public wall { 32: public: 33: 34: // The wall constructor initializes constants for the major and 35: // minor axes of the torus. It also initializes the wall ID 36: // number that is used when the plane cuts are made. This is 37: // only tracked with the voronoicell_neighbor class and is 38: // ignored otherwise. It can be omitted, and then an arbitrary 39: // value of -99 is used. 40: wall_torus(double imjr,double imnr,int iw_id=-99) 41: : w_id(iw_id), mjr(imjr), mnr(imnr) {}; 42: 43: // This returns true if a given vector is inside the torus, and 44: // false if it is outside. For the current example, this 45: // routine is not needed, but in general it would be, for use 46: // with the point_inside() routine in the container class. 47: bool point_inside(double x,double y,double z) { 48: double temp=sqrt(x*x+y*y)-mjr; 49: return temp*temp+z*z<mnr*mnr; 50: } 51: 52: // This template takes a reference to a voronoicell or 53: // voronoicell_neighbor object for a particle at a vector 54: // (x,y,z), and makes a plane cut to to the object to account 55: // for the toroidal wall 56: template<class vc_class> 57: inline bool cut_cell_base(vc_class &c,double x,double y,double z) { 58: double orad=sqrt(x*x+y*y); 59: double odis=orad-mjr; 60: double ot=odis*odis+z*z; 61: 62: // Unless the particle is within 1% of the major 63: // radius, then a plane cut is made 64: if(ot>0.01*mnr) { 65: ot=2*mnr/sqrt(ot)-2; 66: z*=ot; 67: odis*=ot/orad; 68: x*=odis; 69: y*=odis; 70: return c.nplane(x,y,z,w_id); 71: } 72: return true; 73: } 74: 75: // These virtual functions are called during the cell 76: // computation in the container class. They call instances of 77: // the template given above. 78: bool cut_cell(voronoicell &c,double x, 79: double y,double z) {return cut_cell_base(c,x,y,z);} 80: bool cut_cell(voronoicell_neighbor &c,double x, 81: double y,double z) {return cut_cell_base(c,x,y,z);} 82: private: 83: // The ID number associated with the wall 84: const int w_id; 85: // The major radius of the torus 86: const double mjr; 87: // The minor radius of the torus 88: const double mnr; 89: }; 90: 91: int main() { 92: 93: // Create a container with the geometry given above, and make it 94: // non-periodic in each of the three coordinates. Allocate space for 95: // eight particles within each computational block. 96: container con(x_min,x_max,y_min,y_max,z_min,z_max,n_x,n_y,n_z, 97: false,false,false,8); 98: 99: // Add the custom toroidal wall to the container 100: wall_torus tor(arad,brad); 101: con.add_wall(tor); 102: 103: // Import the particles from a file 104: con.import("pack_torus"); 105: 106: // Output the particle positions in POV-Ray format 107: con.draw_particles_pov("torus_p.pov"); 108: 109: // Output the Voronoi cells in POV-Ray format 110: con.draw_cells_pov("torus_v.pov"); 111: }