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