Geometric analysis
The Core concepts page covers the basics of working with
coordination polyhedra: bond distances, angles, volume, edge graphs,
symmetry measures, and neighbour sharing. This guide covers the
remaining geometric analysis tools available on
CoordinationPolyhedron.
All examples assume you have a
Configuration with
polyhedra already constructed:
poly = config.polyhedra[0]
Vertex species composition
In mixed-anion systems (e.g. TiOF2, where Ti is coordinated by both O and F), it is useful to know which species occupy the vertex sites.
The
vertex_labels
property returns the species label for each vertex:
poly.vertex_labels
# ['F', 'F', 'O', 'O', 'O', 'O']
To get a count of vertices grouped by species, use
vertex_count:
poly.vertex_count
# Counter({'O': 4, 'F': 2})
To pair each vertex distance with its species label, use
vertex_distances_and_labels():
poly.vertex_distances_and_labels()
# ((1.98, 'F'), (2.01, 'F'), (1.95, 'O'), (1.96, 'O'), (1.97, 'O'), (1.97, 'O'))
This accepts an optional reference argument ('central_atom' or
'centroid') to choose the reference point for the distance
calculation.
Distances and vectors
The
vertex_distances()
method returns an array of distances from either the central atom or
the centroid to each vertex:
poly.vertex_distances() # from central atom (default)
poly.vertex_distances(reference='centroid') # from centroid
The existing
coordination_distances()
method is a convenience wrapper that always measures from the central
atom and returns a list rather than a numpy array.
The
vertex_vectors()
method returns the vectors from a reference point to each vertex:
poly.vertex_vectors() # from centroid (default)
poly.vertex_vectors(reference='central_atom') # from central atom
These vectors are the basis for most other geometric calculations (angles, orientations, symmetry measures).
The
centroid()
method returns the centroid of the vertex positions, correctly
accounting for periodic boundary conditions:
poly.centroid() # np.array([x, y, z])
Off-centre displacement
In ferroelectric and related materials, the displacement of the central atom away from the geometric centre of its coordination shell is physically significant.
The
centroid_to_central_atom_vector()
method returns the 3D displacement vector:
poly.centroid_to_central_atom_vector() # np.array([dx, dy, dz])
The scalar magnitude is available as the
off_centre_displacement
property:
poly.off_centre_displacement # 0.042
Radial distortion
The
radial_distortion_parameter()
method quantifies how much the vertex distances deviate from their
mean value. By default it computes the normalised mean squared
deviation (MSD):
poly.radial_distortion_parameter()
The method argument selects between MSD and mean absolute deviation
(MAD):
poly.radial_distortion_parameter(method='MAD')
Other options control the reference point and normalisation:
# Measure from the central atom instead of the centroid
poly.radial_distortion_parameter(reference='central_atom')
# Un-normalised (absolute deviations, not divided by the mean distance)
poly.radial_distortion_parameter(normalize=False)
Angular analysis
The
vertex_angles()
method calculates angles between specific pairs of vertices, identified
by their global atom indices:
pairs = ((3, 7), (3, 12))
poly.vertex_angles(pairs) # np.array([90.1, 88.5])
This complements the existing
angles()
method, which returns all pairwise vertex–centroid–vertex angles.
The
vertex_vector_orientations()
method returns the angular orientation of each vertex vector as
(theta, phi) pairs. Theta is the angle from [0, 0, 1] (0–180
degrees) and phi is the angle from [1, 0, 0] (-180 to +180 degrees):
poly.vertex_vector_orientations() # from centroid (default)
# [(45.0, 90.0), (135.0, -90.0), ...]
# In radians
poly.vertex_vector_orientations(units='radians')
# Also return the distance for each vertex
poly.vertex_vector_orientations(return_distance=True)
# [(45.0, 90.0, 1.98), (135.0, -90.0, 2.01), ...]
Vector projections
The
vertex_vector_projections()
method projects each vertex vector onto one or more input vectors. This
is useful for measuring the component of each bond along a specific
crystallographic direction:
import numpy as np
# Project onto the c-axis
c_axis = np.array([0.0, 0.0, 1.0])
poly.vertex_vector_projections(c_axis)
# np.array of shape (n_vertices, 1)
# Project onto multiple directions at once
axes = np.array([[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0]])
poly.vertex_vector_projections(axes)
# np.array of shape (n_vertices, 3)
Topology: faces, edges, and convex hull
The
faces()
method returns the faces of the polyhedron as tuples of vertex atom
indices:
poly.faces()
# ((3, 7, 12), (3, 7, 16), (3, 12, 19), ...)
The
edge_vertex_indices()
method returns all edges as sorted pairs:
poly.edge_vertex_indices()
# ((3, 7), (3, 12), (3, 16), (7, 12), ...)
This is an alternative representation of the same connectivity as the
edge_graph
property (documented in Core concepts), which uses an adjacency
list format instead.
For custom analysis, the underlying
convex_hull()
method returns a scipy
ConvexHull object:
hull = poly.convex_hull()
Identity and utility
Labels and indices:
Each polyhedron has a
label
(defaults to the central atom species) and a method to change it:
poly.label # 'Ti'
poly.set_label('Ti_1')
The
index
property returns the global atom index of the central atom, and
vertex_indices
returns the global indices of the vertex atoms:
poly.index # 0
poly.vertex_indices # [3, 7, 12, 16, 19, 23]
The Cartesian coordinates of the vertex atoms are available as an Nx3 array:
poly.vertex_coords # np.array of shape (6, 3)
Periodic boundary conditions:
The
minimum_image_vertex_coordinates()
method returns vertex coordinates where each vertex is the closest
periodic image to the central atom:
poly.minimum_image_vertex_coordinates() # np.array of shape (6, 3)
Shared vertices:
The
intersection()
method returns the atom indices shared with another polyhedron:
poly_a = config.polyhedra[0]
poly_b = config.polyhedra[1]
poly_a.intersection(poly_b) # (12, 16)
This is the underlying operation used by the neighbour analysis methods (see Neighbour analysis).
Comparing polyhedra:
Three methods provide different levels of equality testing:
# Same vertex atom indices?
poly_a.equal_vertices(poly_b)
# Same edge connectivity graph?
poly_a.equal_edge_graph(poly_b)
# Same central atom AND same vertices?
poly_a.equal_members(poly_b)
The default == operator uses equal_edge_graph.
Index lookups:
To retrieve a subset of vertex atoms by their global indices:
poly.vertices_by_indices([12, 16]) # [Atom, Atom]
To convert a global atom index to the internal (0-based) vertex index:
poly.vertex_internal_index_from_global_index(12) # 2
Creating polyhedra directly:
The
from_sites()
class method creates a polyhedron directly from pymatgen
Site objects, without going through a
Configuration:
from polyhedral_analysis.coordination_polyhedron import CoordinationPolyhedron
poly = CoordinationPolyhedron.from_sites(
central_site=central_site,
vertex_sites=vertex_sites,
label='Ti', # optional
)