Working with Voronoi Sites

Voronoi sites divide space into regions where each point in a region is closer to its site centre than to any other site centre. This creates a complete partitioning of space with no gaps or overlaps, ensuring every atom is assigned to exactly one site.

For a conceptual overview of Voronoi sites, see the Voronoi sites concepts page.

This guide covers practical aspects of using Voronoi sites in your analysis, including when to use them, how to configure them, and how to leverage their unique properties.

When to Use Voronoi Sites

Voronoi sites are best suited for:

  • Ensuring complete spatial coverage (no unassigned atoms)

  • Systems where proximity-based assignment is appropriate

  • Avoiding ambiguity in site assignment

  • Simple systems where distance to site centres determines occupation

  • When computational efficiency is important

Consider alternative site types when:

  • You need sites tied to specific coordination environments (use polyhedral sites)

  • Site centres need to adapt to structural changes (use dynamic Voronoi sites)

  • You’re analysing complex coordination geometries

  • Chemical bonding patterns don’t align with simple distance metrics

  • You need to match published work using different site definitions

Understanding Space Partitioning

Voronoi sites completely fill space—every point belongs to exactly one site. This has important implications:

  • No gaps: All atoms will always be assigned to a site

  • No overlaps: Each atom belongs to exactly one site

  • Distance-based: Assignment is determined solely by proximity to site centres

This makes Voronoi sites ideal when you need to ensure every atom is tracked, but means the site boundaries may not correspond to physical or chemical boundaries in your system.

Creating Voronoi Sites

Basic Setup

from site_analysis import TrajectoryBuilder

# Define site centres
site_centres = [[0.5, 0.5, 0.5], [0.0, 0.0, 0.0], [0.5, 0.0, 0.0]]

trajectory = (TrajectoryBuilder()
    .with_structure(structure)
    .with_mobile_species("Li")
    .with_voronoi_sites(centres=site_centres)
    .build())

With Labels

Labels help identify different types of sites:

# Label different crystallographic positions
trajectory = (TrajectoryBuilder()
    .with_structure(structure)
    .with_mobile_species("Li")
    .with_voronoi_sites(
        centres=site_centres,
        labels=["octahedral", "tetrahedral_1", "tetrahedral_2"]
    )
    .build())

Choosing Site Centres

Unlike spherical sites where you must choose both centres and radii, Voronoi sites only require centre positions. The site boundaries are automatically determined by the relative positions of all centres.

Common approaches for choosing centres:

  • From crystallographic positions: Use known interstitial sites

  • From known ion positions: Use equilibrium positions from a relaxed structure

  • From symmetry analysis: Extract Wyckoff positions for the mobile species

Example: Interstitial Sites in FCC Structure

# Common interstitial positions in FCC structure
octahedral = [[0.5, 0.5, 0.5], [0.0, 0.5, 0.5], 
              [0.5, 0.0, 0.5], [0.5, 0.5, 0.0]]
tetrahedral = [[0.25, 0.25, 0.25], [0.75, 0.75, 0.25],
               [0.75, 0.25, 0.75], [0.25, 0.75, 0.75]]

all_centres = octahedral + tetrahedral

# Create Voronoi sites with meaningful labels
trajectory = (TrajectoryBuilder()
    .with_structure(structure)
    .with_mobile_species("Li")
    .with_voronoi_sites(
        centres=octahedral,
        labels=["oct_1", "oct_2", "oct_3", "oct_4"]
    )
    .with_voronoi_sites(
        centres=tetrahedral,
        labels=["tet_1", "tet_2", "tet_3", "tet_4"]
    )
    .build())

Advanced Usage: Direct Site Creation

For more control over site creation, you can bypass the builder and create Voronoi sites directly.

Creating Individual Sites

from site_analysis.voronoi_site import VoronoiSite
from site_analysis.voronoi_site_collection import VoronoiSiteCollection
import numpy as np

# Create individual Voronoi sites
site1 = VoronoiSite(frac_coords=np.array([0.5, 0.5, 0.5]), label="octahedral")
site2 = VoronoiSite(frac_coords=np.array([0.0, 0.0, 0.0]), label="tetrahedral")

# Create a collection
sites = [site1, site2]
site_collection = VoronoiSiteCollection(sites)

# Create atoms and trajectory
from site_analysis import atoms_from_structure, Trajectory
atoms = atoms_from_structure(structure, "Li")
trajectory = Trajectory(sites=sites, atoms=atoms)

Using with Custom Workflows

# Example: Create sites based on analysis of equilibrium structure
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer

# Analyze structure symmetry
analyzer = SpacegroupAnalyzer(equilibrium_structure)
sym_structure = analyzer.get_symmetrized_structure()

# Extract unique Li positions
li_sites = []
for equiv_sites in sym_structure.equivalent_sites:
    if equiv_sites[0].species_string == "Li":
        # Take one representative from each set of equivalent sites
        pos = equiv_sites[0].frac_coords
        li_sites.append(VoronoiSite(frac_coords=pos))

# Create collection and trajectory
site_collection = VoronoiSiteCollection(li_sites)
atoms = atoms_from_structure(md_structure, "Li")
trajectory = Trajectory(sites=li_sites, atoms=atoms)

This approach gives you full control over:

  • How sites are identified and created

  • Site labelling and metadata

  • Integration with other analysis tools

For a comparison of all site types and guidance on choosing between them, see the site selection guide.

Troubleshooting

Problem: Sites have unexpected shapes

Solutions:

  • Ensure centres are appropriately spaced

  • Add more centres in sparse regions

  • Consider if Voronoi is appropriate for your system

Problem: Sites don’t match expected chemistry

Solutions:

  • Voronoi sites are purely geometric—consider polyhedral sites for chemistry-based definitions

  • Adjust centre positions to better match chemical environments

  • Use dynamic Voronoi sites if centres should adapt to structure

Problem: Too many/too few sites

Solutions:

  • Review your crystallographic analysis

  • Ensure you’re not duplicating symmetry-equivalent positions

  • Consider the appropriate level of spatial discretisation for your analysis