Working with Spherical Sites
Spherical sites are the simplest site type in site_analysis, defined by a centre position and radius. They represent spherical volumes within a crystal structure where mobile ions can reside. An atom is considered to be within a spherical site if the distance between the atom and the site centre is less than or equal to the site’s radius.
For a conceptual overview of spherical sites, see the spherical sites concepts page.
This guide covers practical aspects of using spherical sites in your analysis, including when to use them, how to configure them, and how to handle their limitations.
When to Use Spherical Sites
Spherical sites are best suited for:
Initial exploratory analysis
Simple systems with well-separated sites
Comparing with published work that used spherical sites
Quick visualizations of site locations
Consider alternative site types when:
You need complete spatial coverage (use Voronoi or polyhedral sites)
Sites have specific coordination geometries (use polyhedral sites)
The framework undergoes topology-preserving distortions (use dynamic Voronoi or polyhedral sites)
You’re analyzing close-packed structures where polyhedral sites provide both complete coverage and correspond to crystallographic coordination environments
Understanding Spherical Site Properties
Spherical sites have unique characteristics that affect their use:
Fixed geometry: Sites remain spherical regardless of structure
Potential gaps: Space between non-overlapping spheres remains unassigned
Potential overlaps: Overlapping spheres create ambiguous regions
Radius dependence: Site definition requires choosing appropriate radii
The radius parameter presents a fundamental trade-off:
Small radii → gaps between sites → unassigned atoms
Large radii → overlapping sites → ambiguous assignments
The package handles overlaps through a persistence algorithm: atoms remain in their current site when in overlapping regions, reducing spurious transitions.
Creating Spherical Sites
Basic Setup
from site_analysis import TrajectoryBuilder
# Define site centres and radii
site_centres = [[0.5, 0.5, 0.5], [0.0, 0.0, 0.0]]
site_radii = 2.0 # Same radius for all sites
trajectory = (TrajectoryBuilder()
.with_structure(structure)
.with_mobile_species("Li")
.with_spherical_sites(centres=site_centres, radii=site_radii)
.build())
Variable Radii
Different sites often require different radii:
# Larger radius for octahedral sites, smaller for tetrahedral
octahedral_centres = [[0.5, 0.5, 0.5]]
tetrahedral_centres = [[0.25, 0.25, 0.25]]
trajectory = (TrajectoryBuilder()
.with_structure(structure)
.with_mobile_species("Li")
.with_spherical_sites(
centres=octahedral_centres,
radii=2.5,
labels="octahedral"
)
.with_spherical_sites(
centres=tetrahedral_centres,
radii=1.8,
labels="tetrahedral"
)
.build())
Choosing Appropriate Radii
Site radii should be chosen based on:
Crystal structure geometry
Typical bond distances
Desired site overlap/separation
A common approach is to start with radii based on typical bond distances and adjust empirically:
# Test different radii and check site occupations
test_radii = [1.5, 2.0, 2.5]
for radius in test_radii:
trajectory = (TrajectoryBuilder()
.with_structure(structure)
.with_mobile_species("Li")
.with_spherical_sites(centres=centres, radii=radius)
.build())
# Analyze first frame
trajectory.analyse_structure(structure)
# Check how many atoms are assigned
assigned_atoms = sum(1 for atom in trajectory.atoms if atom.in_site is not None)
print(f"Radius {radius}: {assigned_atoms}/{len(trajectory.atoms)} atoms assigned")
Example: Interstitial Sites in Close-Packed Structures
# FCC structure with octahedral and tetrahedral interstitials
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],
[0.75, 0.75, 0.75], [0.25, 0.25, 0.75],
[0.25, 0.75, 0.25], [0.75, 0.25, 0.25]
]
# Different radii for different site types
trajectory = (TrajectoryBuilder()
.with_structure(structure)
.with_mobile_species("Li")
.with_spherical_sites(centres=octahedral, radii=2.0, labels="oct")
.with_spherical_sites(centres=tetrahedral, radii=1.5, labels="tet")
.build())
Advanced Usage: Direct Site Creation
For more control over site creation, you can bypass the builder and create spherical sites directly.
Creating Individual Sites
from site_analysis.spherical_site import SphericalSite
from site_analysis.spherical_site_collection import SphericalSiteCollection
import numpy as np
# Create individual spherical sites
site1 = SphericalSite(
frac_coords=np.array([0.5, 0.5, 0.5]),
rcut=2.0,
label="octahedral"
)
site2 = SphericalSite(
frac_coords=np.array([0.0, 0.0, 0.0]),
rcut=1.5,
label="tetrahedral"
)
# Create a collection
sites = [site1, site2]
site_collection = SphericalSiteCollection(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 Existing Site Objects
# Create sites from a previous analysis
from site_analysis import SphericalSite
# Example: Load sites from a previous analysis or create programmatically
sites = []
site_data = [
{"pos": [0.5, 0.5, 0.5], "radius": 2.0, "label": "oct_1"},
{"pos": [0.0, 0.0, 0.0], "radius": 1.8, "label": "tet_1"},
]
for data in site_data:
site = SphericalSite(
frac_coords=np.array(data["pos"]),
rcut=data["radius"],
label=data["label"]
)
sites.append(site)
# Use sites with the builder
trajectory = (TrajectoryBuilder()
.with_structure(structure)
.with_mobile_species("Li")
.with_existing_sites(sites)
.build())
This approach gives you full control over:
Manually creating sites with specific properties
Reusing sites from previous analyses
Integrating with other workflows that generate site objects
Handling Overlapping Sites
When sites overlap, the assignment algorithm uses a priority-based approach: it checks the atom’s most recently occupied sites first, then sites ordered by learned transition frequency and distance from the current site. The first containing site found claims the atom. This means atoms tend to remain in their current sites even when they’re in overlapping regions, reducing spurious transitions. See the site collections page for details on the priority ordering.
Note: Overlapping sites can be deliberately used to minimize spurious “transitions” caused by large amplitude thermal vibrations that don’t represent true diffusive motion.
Handling Gaps Between Sites
Gaps are inherent to spherical sites. To monitor unassigned atoms:
# Analyze structure
trajectory.analyse_structure(structure)
# Find unassigned atoms
unassigned = [atom for atom in trajectory.atoms if atom.in_site is None]
if unassigned:
print(f"{len(unassigned)} atoms not assigned to any site")
# Check positions of unassigned atoms
for atom in unassigned:
pos = structure[atom.index].frac_coords
print(f"Atom {atom.index} at {pos} is unassigned")
Gap Mitigation Strategies
Increase radii (but may cause overlaps)
Add intermediate sites at key positions
Switch to space-filling sites (Voronoi or polyhedral)
Troubleshooting
Problem: Too many unassigned atoms
Solutions:
Increase site radii
Add more sites
Consider using Voronoi or polyhedral sites
Problem: Atoms jumping between overlapping sites
Solutions:
Reduce site radii to minimize overlap
The default priority-based assignment helps minimise spurious jumps
Consider polyhedral sites for better-defined boundaries
Problem: Sites too small/large for structure
# Check site occupancies
trajectory.analyse_structure(structure)
for site in trajectory.sites:
n_atoms = len(site.contains_atoms)
print(f"Site {site.index}: {n_atoms} atoms")
Solutions:
Adjust radii based on occupancy patterns
Use different radii for different site types