Analysing trajectories

The Trajectory class extends the single-structure analysis to molecular dynamics trajectories, applying polyhedra recipes to every frame.

Loading from XDATCAR

For VASP trajectories stored as XDATCAR files:

from polyhedral_analysis.trajectory import Trajectory
from polyhedral_analysis.polyhedra_recipe import PolyhedraRecipe

recipe = PolyhedraRecipe(
    method='distance cutoff',
    coordination_cutoff=3.0,
    central_atoms='Ti',
    vertex_atoms=['O', 'F'],
)

trajectory = Trajectory.from_xdatcar(
    'XDATCAR',
    recipes=[recipe],
)

If your trajectory is split across multiple XDATCAR files (e.g. from consecutive VASP runs), use from_xdatcars():

trajectory = Trajectory.from_xdatcars(
    ['XDATCAR.1', 'XDATCAR.2', 'XDATCAR.3'],
    recipes=[recipe],
)

Loading from structures

You can also build a trajectory from any list of pymatgen Structure objects:

trajectory = Trajectory.from_structures(
    structures,
    recipes=[recipe],
)

This works with structures from any source that pymatgen supports.

Progress bars and parallelism

For long trajectories, enable a progress bar. The correct widget is chosen automatically for terminals and Jupyter notebooks (via tqdm.auto):

trajectory = Trajectory.from_xdatcar(
    'XDATCAR',
    recipes=[recipe],
    progress=True,
)

To parallelise configuration building across multiple CPU cores:

trajectory = Trajectory.from_xdatcar(
    'XDATCAR',
    recipes=[recipe],
    ncores=4,
)

Accessing configurations

A Trajectory contains a list of Configuration objects, one per frame:

len(trajectory)                   # number of frames
trajectory.configurations[0]      # first frame
trajectory.configurations[0].polyhedra  # polyhedra in first frame

# Iterate over all frames
for config in trajectory.configurations:
    for poly in config.polyhedra:
        print(poly.best_fit_geometry)

Working with configurations

Each Configuration provides convenience attributes and methods for querying its polyhedra and atoms.

Filtering polyhedra by label:

When you define a recipe with a label argument (see Defining polyhedra recipes), you can retrieve matching polyhedra with polyhedra_by_label():

config.polyhedra_by_label('Ti')          # all polyhedra labelled 'Ti'
config.polyhedra_by_label(['Ti', 'Nb'])  # multiple labels

The polyhedra_labels property lists the labels of all polyhedra:

config.polyhedra_labels  # ['Ti', 'Ti', 'Ti', ...]

Accessing atoms:

The central_atoms and coordination_atoms properties return the central and vertex atoms respectively:

config.central_atoms       # list of central Atom objects
config.coordination_atoms  # list of vertex Atom objects

To look up a specific coordination atom by its global index:

config.coordination_atom_by_index(12)  # Atom or None

Polyhedron trajectories

A PolyhedronTrajectory tracks a single polyhedron (identified by its central atom) across all frames of a trajectory:

from polyhedral_analysis.polyhedron_trajectory import PolyhedronTrajectory

# Track the first polyhedron across all frames
poly_traj = PolyhedronTrajectory(
    polyhedra=[config.polyhedra[0] for config in trajectory.configurations],
)

The polyhedra attribute gives access to the individual CoordinationPolyhedron objects. You can extract time-series data for any polyhedron property (see Geometric analysis for all available properties):

# Off-centre displacement over time
displacements = [p.off_centre_displacement for p in poly_traj.polyhedra]

# Symmetry measure over time
csm_values = [p.best_fit_geometry['symmetry_measure'] for p in poly_traj.polyhedra]

# Coordination number over time
cn_values = [p.coordination_number for p in poly_traj.polyhedra]

Combining trajectories

You can concatenate trajectories with the + operator:

combined = trajectory_1 + trajectory_2

Or extend an existing trajectory in place:

trajectory_1.extend(trajectory_2)

Exporting to lattice_mc

The to_lattice_mc() method exports a connectivity description for use with the lattice_mc Monte Carlo code. It takes a filename, a list of polyhedra labels, and a neighbour list (typically the face-sharing neighbour list):

neighbour_list = config.face_sharing_neighbour_list(labels=['Ti'])
config.to_lattice_mc('lattice.dat', labels=['Ti'], neighbour_list=neighbour_list)