Voro: A Powerful C++ Library

Introduction to Voro++ 0.4.6: Efficient Calculation of 3D Voronoi Diagrams in C++

What is a Voronoi Diagram?

Before introducing Voro++, we first need to understand Voronoi diagrams. Imagine a flat area with several trees (which we call “seed points” or “sites”). The task of a Voronoi diagram is to partition this flat area into multiple regions, with the rule being: for any tree, its region contains all points on the plane that are closer to it than to any other tree.

Extending this concept to three-dimensional space, the seed points become points in space, and the partitions are no longer flat regions but polyhedral cells. This three-dimensional Voronoi diagram has extremely wide applications in fields such as physics (e.g., simulating granular materials), chemistry (e.g., simulating molecular structures), geography, and computer graphics.

Introduction to Voro++

Voro++ is an open-source C++ software library developed by Chris Rycroft, specifically designed for calculating Voronoi diagrams of points in three-dimensional space. Its name is derived from “Voronoi” itself. The main features of Voro++ are:

  • High Performance: It uses a cell decomposition algorithm to break the computational container into multiple blocks, significantly improving computational efficiency.
  • Memory Efficiency: It supports incremental computation, meaning it does not require all data to be loaded into memory at once, making it particularly suitable for handling large-scale particle systems.
  • Flexibility: It provides various container classes that can handle periodic boundaries, non-periodic boundaries, and mixed boundary conditions, which aligns well with the needs of physical simulations.
  • Focus on Three Dimensions: Unlike many general-purpose geometric libraries, Voro++ specializes in three-dimensional problems and has been deeply optimized for this purpose.

Version 0.4.6 is a stable and widely used version.

Core Concepts and Code Examples

The core workflow of Voro++ is: Create a container -> Insert particle points into the container -> Traverse each cell in the container and retrieve its information.

Example 1: Basic Usage – Calculating the Voronoi Diagram Inside a Cubic Box

Suppose we have a cubic box with a side length of 10, containing 200 randomly distributed points. We want to calculate their Voronoi cells.

// filename: basic_example.cc
#include "voro++.hh"
using namespace voro;

// Note: This example demonstrates the most basic usage of Voro++

int main() {
    // 1. Set the boundaries of the container [x_min, x_max], [y_min, y_max], [z_min, z_max]
    double x_min = 0, x_max = 10;
    double y_min = 0, y_max = 10;
    double z_min = 0, z_max = 10;

    // 2. Set the number of blocks to decompose during computation. Typically, dividing each direction into 6-10 blocks yields good performance.
    int n_x = 6, n_y = 6, n_z = 6;

    // 3. Create a non-periodic container
    // container( x_min, x_max, y_min, y_max, z_min, z_max, n_x, n_y, n_z,
    //           x_periodicity, y_periodicity, z_periodicity )
    container con(x_min, x_max, y_min, y_max, z_min, z_max, n_x, n_y, n_z,
                  false, false, false);

    // 4. Randomly add particles to the container
    // put( particle_id, x, y, z )
    for (int i = 0; i < 200; i++) {
        double x = x_min + (x_max - x_min) * (rand() / (double)RAND_MAX);
        double y = y_min + (y_max - y_min) * (rand() / (double)RAND_MAX);
        double z = z_min + (z_max - z_min) * (rand() / (double)RAND_MAX);
        con.put(i, x, y, z);
    }

    // 5. Output all particle Voronoi cell information to a file
    con.draw_cells_gnuplot("pack_gnuplot.out");

    // Custom format information can also be output, e.g., particle ID, position, volume, number of faces, etc.
    con.print_custom("pack_custom.out", "ID=%i, Pos=(%x,%y,%z), Volume=%v, Faces=%s");

    return 0;
}

Compilation and Execution: Assuming the Voro++ header files and libraries are correctly installed, you can compile using the following command:

g++ -O2 -I/path/to/voro++/include basic_example.cc -L/path/to/voro++/lib -lvoro++

Output File Description:

  • pack_gnuplot.out: This file can be read by gnuplot to plot the contours of the Voronoi diagram.
  • pack_custom.out: This file contains custom information for each cell. Format description:
    • %i: Particle ID.
    • %x, %y, %z: Particle coordinates.
    • %v: Volume of the cell.
    • %s: Number of faces of the cell.
Example 2: Using Periodic Boundary Conditions

In many physical simulations (e.g., simulating liquids or crystals), the system is considered to be infinitely extended, which is achieved through periodic boundaries. Voro++ has excellent support for this.

// filename: periodic_example.cc
#include "voro++.hh"
using namespace voro;

int main() {
    // Define a cubic periodic box
    double x_min = 0, x_max = 10;
    double y_min = 0, y_max = 10;
    double z_min = 0, z_max = 10;
    int n_x = 6, n_y = 6, n_z = 6;

    // Create a container with periodicity in all directions
    // Change the last three `false` to `true`
    container_poly con_poly(x_min, x_max, y_min, y_max, z_min, z_max, n_x, n_y, n_z,
                            true, true, true, 8); // Note that here we use container_poly, and the last parameter is the initial memory allocation size

    // Add some particles
    con_poly.put(0, 1.0, 1.0, 1.0); // Particle 0
    con_poly.put(1, 9.5, 9.5, 9.5); // Particle 1

    // In a periodic system, particle 1 and particle 0 are very close at the boundary
    // Their Voronoi cells will interact across the boundary

    // Calculate and output the neighbor information for each cell
    con_poly.print_custom("periodic_neighbors.out", "ID=%i, Pos=(%x,%y,%z), Neighbors=%n, Volume=%v");

    return 0;
}

In this example, we used container_poly, which is more versatile than the basic container and can support periodic boundaries and future possible feature expansions. The %n format specifier will output the IDs of all neighboring particles of that cell, which is very useful for analyzing the local topological structure between particles.

Example 3: Analyzing Cells Individually

Sometimes we do not need to output all cells but want to programmatically access the data of each cell and process it.

// filename: cell_analysis.cc
#include "voro++.hh"
#include <iostream>
using namespace voro;
using namespace std;

int main() {
    container con(0, 10, 0, 10, 0, 10, 5, 5, 5, false, false, false);

    // Add some particles
    con.put(0, 2.0, 5.0, 5.0);
    con.put(1, 5.0, 5.0, 5.0);
    con.put(2, 8.0, 5.0, 5.0);

    // Create a voronoicell object to store the computation results of a single cell
    voronoicell c;

    // Traverse all particles in the container
    int particle_id = 1; // We want to compute the cell for the particle with ID 1
    double x, y, z;

    // Use the compute_cell method to calculate the cell of the specified particle
    if (con.compute_cell(c, particle_id, x, y, z)) {
        cout << "Particle " << particle_id << " at (" << x << ", " << y << ", " << z << ")" << endl;
        cout << "  Volume: " << c.volume() << endl;
        cout << "  Surface Area: " << c.surface_area() << endl;
        cout << "  Number of Faces: " << c.number_of_faces() << endl;

        // Get the total number of edges (sum of edges of all faces)
        vector<int> face_vertices;
        c.face_vertices(face_vertices);
        cout << "  Total number of edges (sum over all faces): " << face_vertices.size() << endl;
    } else {
        cerr << "Error computing cell for particle " << particle_id << endl;
    }

    return 0;
}

This example demonstrates how to interact directly with the voronoicell object, extracting geometric information for further analysis.

Conclusion

Voro++ 0.4.6 is a powerful and efficient tool that encapsulates the complex computation of three-dimensional Voronoi diagrams into an easy-to-use C++ interface. With its flexible container system, it can easily handle a variety of scenarios from simple closed boxes to complex fully periodic systems. Whether for data preprocessing in scientific research or as a component in large simulation software, Voro++ is a reliable choice.

Leave a Comment