torus.cc – Creating a custom wall object

Voronoi tessellation in a torus

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    : [email protected]
  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: }