ccu.structure package

Structure analysis functions and utilities.

This subpackage enables determination of orientation axes (ccu.structure.axisfinder), comparison of structure graphs (ccu.structure.comparator, ccu.structure.fingerprint), calculation of closest distance between fragments (:mod:ccu.structure.geometry`), structure permutation (ccu.structure.defects ), and cell resizing (ccu.structure.resizecell).

Submodules

ccu.structure.axisfinder module

Utilities for determining a molecule’s orientation axes.

Example

The function get_axes() returns all three orientation axes for a given molecule.

>>> import ase
>>> from ccu.structure.axisfinder import get_axes
>>> coh = ase.Atoms("COH", positions=[[0, 0, 0], [-2, 0, 0], [-1, 0.5, 0]])
>>> get_axes(coh)
(array([1., 0., 0.]), array([0., 1., 0.]), array([0., 0., 1.]))

Example

The function find_farthest_atoms() returns the two atoms within a molecule whose separation is the greatest. For example,

>>> import ase
>>> from ccu.structure.axisfinder import find_farthest_atoms
>>> coh = ase.Atoms("COH", positions=[[0, 0, 0], [-2, 0, 0], [-1, 0.5, 0]])
>>> find_farthest_atoms(coh)
(Atom('C', [0.0, 0.0, 0.0], index=0), Atom('O', [-2.0, 0.0, 0.0], index=1))

Example

The function find_primary_axis() returns the primary orientation axis for a given molecule. For example,

>>> import ase
>>> from ccu.structure.axisfinder import find_primary_axis
>>> coh = ase.Atoms("COH", positions=[[0, 0, 0], [-2, 0, 0], [-1, 0.5, 0]])
>>> find_primary_axis(coh)
array([1., 0., 0.])

Example

The function find_secondary_axis() returns the primary orientation axis for a given molecule. For example,

>>> import ase
>>> from ccu.structure.axisfinder import find_secondary_axis
>>> coh = ase.Atoms("COH", positions=[[0, 0, 0], [-2, 0, 0], [-1, 0.5, 0]])
>>> find_secondary_axis(coh)
array([0., 1., 0.])

Example

The function find_tertiary_axis() returns the primary orientation axis for a given molecule. For example,

>>> import ase
>>> from ccu.structure.axisfinder import find_tertiary_axis
>>> coh = ase.Atoms("COH", positions=[[0, 0, 0], [-2, 0, 0], [-1, 0.5, 0]])
>>> find_tertiary_axis(coh)
array([0., 0., 1.])
ccu.structure.axisfinder.find_farthest_atoms(molecule: Atoms, tol: float = 1e-05) tuple[Atoms][source]

Find the two atoms in the molecule separated by the greatest distance.

In molecules for which there are several pairs of atoms with equidistant separations, this function will return the pair of atoms with lowest indices whose separation is within a given tolerance of the largest atomic separation in the molecule. Each pair is sorted according to the index of the lowest index atom and then the index of the second atom. For example,

  • If atoms 0 and 1 have the same separation as atoms 2 and 3, atoms 0 and 1 will be returned since 0 < 2.

  • If atoms 0 and 1 have the same separation as atoms 0 and 3, atoms 0 and 1 will be returned since 1 < 3.

  • If atoms 1 and 2 have the same separation as atoms 0 and 4, atoms 0 and 4 will be returned since 0 < 1.

  • If atoms 1 and 2 have the same separation as atoms 0 and 2, atoms 0 and 2 will be returned since 0 < 1.

Parameters:
  • molecule – The molecule for whom the two farthest atoms are to be determined.

  • tol – A float indicating the resolution (in Angstroms) between atomic distances.

Returns:

A tuple containing the two atoms in the molecule separated by the greatest distance. The atoms are ordered by lowest index within the structure.

ccu.structure.axisfinder.find_primary_axis(molecule: Atoms) array[source]

Determine the primary orientation axis of a molecule.

The primary axis is defined as the unit vector which is parallel to the direction vector between the two most distant atoms in the molecule and points from the higher index atom to the lower index atom.

Parameters:

molecule – An atoms.Atoms instance representing the molecule for whom the primary axis is to be determined.

Returns:

A numpy.array representing a unit vector in the direction of the primary orientation axis. Note that for zero-dimensional molecules, this function will return the zero vector.

ccu.structure.axisfinder.find_secondary_axis(molecule: Atoms, min_distance: float = 0.1) array[source]

Determine the secondary orientation axis of a molecule.

Let \(L\) be the line between the two farthest atoms in the molecule, let \(v\) be the vector which defines the primary axis, and let \(P\) be the position of the atom farthest from \(L\). Further, let \(w\) be the vector from \(L\) to \(P\), and let \(z\) be the component of \(w\) which is orthogonal to \(v\). The secondary axis is defined as the unit vector in the direction of \(z\).

Parameters:
  • molecule – An atoms.Atoms instance representing the molecule for whom the secondary axis is to be determined.

  • min_distance – A float specifying the minimum distance from the primary axis (in Angstroms) to be considered for defining the secondary axis. Defaults to 0.1.

Returns:

A numpy.array representing a unit vector in the direction of the secondary orientation axis. Note that for zero- and one-dimensional molecules, this function will return the zero vector.

ccu.structure.axisfinder.find_tertiary_axis(molecule: Atoms) array[source]

Determine the tertiary orientation axis of a molecule.

The tertiary orientation axis is simply the cross product of the primary and secondary orientation axes. See find_primary_axis() and find_secondary_axis() for information on how these axes are defined.

Parameters:

molecule – An atoms.Atoms instance representing the molecule for whom the tertiary axis is to be determined.

Returns:

A numpy.array representing a unit vector in the direction of the tertiary orientation axis. Note that if the molecule is zero- or one-dimensional, this function will return the zero vector.

ccu.structure.axisfinder.get_axes(molecule: Atoms) tuple[array][source]

Determine a molecule’s three orientation axes.

The primary axis is defined as the vector between the two most distant atoms. The secondary axis is defined as the orthogonal component (to the primary axis) of the vector from the primary axis to the atom farthest from the line between the two most distant atoms. The tertiary axis is the cross product of the primary and secondary axes. The axes so defined are orthogonal. Note that if the molecule is unimolecular, all three vectors will be the zero vector, and that if the molecule is linear only the primary axis will be nonzero.

Parameters:

molecule – An ase.Atoms instance whose axes are to be determined.

Returns:

A tuple containing unit vectors reprsenting the three orientation axes. The first, second, and third entries are the primary, secondary, and tertiary axes, respectively. For nonlinear molecules, the axes form an orthonormal set.

ccu.structure.cli module

This module contains the ccu.structure package CLI logic.

ccu.structure.comparator module

This module defines the Comparator class.

The Comparator class can be used to determine teh similarity of two structures as follows:

>>> import ase
>>> from ccu.structure.comparator import Comparator
>>> co1 = ase.Atoms("CO", positions=[[0, 0, 0], [1, 0, 0]])
>>> co2 = ase.Atoms("CO", positions=[[0, 1, 1], [1, 1, 1]])
>>> oc = ase.Atoms("OC", positions=[[0, 0, 0], [1, 0, 0]])
>>> Comparator.check_similarity(co1, co2)
True
>>> Comparator.check_similarity(co1, oc)
False
class ccu.structure.comparator.Comparator[source]

Bases: object

An object which compares the similarity of two structures.

static calculate_cumulative_displacement(fingerprint1: Fingerprint, fingerprint2: Fingerprint) float[source]

Calculates the cumulative displacement for fingerprint2.

The cumulative displacement is calculated for fingerprint2 relative to the corresponding atomic positions in fingerprint1.

The cumulative displacement is defined as follows:

Note that each row in each np.ndarray associated with each histogram key corresponds to a displacement vector between two atoms. With each such displacement vector in the histogram of fingerprint1, we can identify a corresponding displacement vector in the histogram of fingerprint2 as the displacement vector associated with the same histogram key and index. We then define a difference vector as the difference between a displacement vector in fingerprint1 and its counterpart in fingerprint2. The set of all difference vectors is defined on the basis of fingerprint1. That is, if \(X\) is the set of all displacement vectors in fingerprint1 and \(Y\) is the set of all corresponding vectors in fingerprint2, the set of all difference vectors is the set of all vectors \(x - y\) where \(x\) is a displacement vector in fingerprint1 and y is the corresponding displacement vector in \(Y\). (Note that this requires that the histogram of fingerprint2 must include all the keys that that of the histogram of fingerprint1 includes. Additionally, this requires that for each key in the histogram of fingerprint1, the value in fingerprint2 includes at least as many displacement vectors as the value in fingerprint1.) The cumulative displacement is then defined as the sum of the norms of all the difference vectors corresponding to fingerprint1 and fingerprint2.

Parameters:
Returns:

A float representing the cumulative displacement for fingerprint2 relative to fingerprint1.

static check_similarity(structure1: Atoms, structure2: Atoms, tol: float = 0.05) bool[source]

Determines similarity of two structures within a given tolerance.

Parameters:
  • structure1 – An atoms.Atoms instance representing the first structure to compare.

  • structure2 – An atoms.Atoms instance representing the second structure to compare.

  • tol – A float specifying the tolerance for the cumulative displacement for fingerprint in Angstroms. Defaults to 5e-2.

Returns:

A bool indicating whether or not the two structures are similar within the specified tolerance.

Note

The notion of similarity here can be summarized as:

Two structures are similar if they can be superimposed via a translation operation.

static cosort_fingerprints(fingerprints1: Iterable[Fingerprint], fingerprints2: Iterable[Fingerprint]) tuple[Fingerprint][source]

Determines the second fingerprints’s minimally displaced ordering.

The minimally displaced ordering of the second Fingerprint list relative to the first is the ordering of the second supplied iterable of Fingerprints which minimizes the cumulative displacement across the two iterables of Fingerprints.

Parameters:
  • fingerprints1 – An iterable containing fingerprint.Fingerprint instances.

  • fingerprints2 – An iterable containing fingerprintFingerprint instances.

  • Note that the two iterables must be of the same length and that the

  • :meth:`.fingerprint.Fingerprint.values` methods of all

  • :class:`.fingerprint.Fingerprint` instances across the two

  • iterables must be of the same length.

Returns:

A tuple containing the ordering of fingerprints2 which minimizes the cumulative displacement across the two iterables of Fingerprints.

static cosort_histograms(fingerprint1: Fingerprint, fingerprint2: Fingerprint) dict[str, ndarray][source]

Minimizes the cumulative displacement of atoms in each structure.

Given the first fingerprint, this method determines the ordering of the second fingerprint’s histogram which minizes the cumulative displacement of atoms in each structure.

The two supplied Fingerprints need not have the same keys or the same number of entries under each key. Such cases are handled as follows:

Let \(k\) be a key in both the histograms of fingerprint1 and fingerprint2. Let \(p\) be the iterable corresponding to the key \(k\) in the histogram of fingerprint1, and let \(q\) be the iterable corresponding to the key \(k\) in the histogram of fingerprint2.

If \(len(p) > len(q)\), then \(q\) is ordered according to its match with the first \(len(q)\) elements of \(p\).

If \(len(p) <= len(q)\), then \(q\) is ordered according to the best match with \(p\) and the first \(len(p)\) elements of \(q\).

Parameters:
  • fingerprint1 – The fingerprint.Fingerprint object to be used as a reference for each displacement in the other Fingerprint object’s histogram.

  • fingerprint2 – The fingerprint.Fingerprint object for which the optimally ordered histogram is to be determined.

Returns:

A dict constructed from fingerprint2._histogram mapping chemical symbols to a :class:numpy.ndarray` containing the displacement vectors to atoms with the corresponding chemical symbol. The order of the displacement vectors is such that the cumulative displacement of the displacement vectors is minimized relative to ``fingerprint1._histogram.

ccu.structure.defects module

Functions for creating defects within structures.

ccu.structure.defects.permute(*, structure: Atoms, sites: Iterable[int | str] | None = None, occupancies: Iterable[tuple[str, int]] | None = None) list[Atoms][source]

Create permutations of a structure considering a number of sites.

Parameters:
  • structure – an ase.atoms.Atoms object to permutate.

  • sites – an optional list of integers and strings representing the sites to permute. Integers are interpreted as structure indices. Strings are interpreted as all sites of a given element. Defaults to all the indices of the structure.

  • occupancies – an optional list of tuples whose first elements are strings representing chemical symbols and whose second elements are integers indicating the number of sites to fill with the given element. Empty strings can be passed to denote vacancy defects. Defaults to the occupancies of the sites defined in sites.

Example

Permute all atoms in a structure

>>> from ase import Atoms
>>> from ase.build import bulk
>>> from ccu.structure.defects import permute
>>> permuted_nios = permute(structure=bulk("NiO", "rocksalt", a=0.352) * 2)

Example

Permute all atoms in a set of sites of a structure

>>> from ase import Atoms
>>> from ase.build import bulk
>>> from ccu.structure.defects import permute
>>> permuted_nios = permute(
...     structure=bulk("NiO", "rocksalt", a=0.352) * 2,
...     sites=[0, 1, 2],
... )

Example

Create Ni-M bimetallics

>>> from ase import Atoms
>>> from ase.build import bulk
>>> from ccu.structure.defects import permute
>>> ni = bulk("Ni", "fcc", a=0.352) * 2
>>> metals = ["Co", "Cr", "Cu", "Fe", "Ti"]
>>> bimetallics = [ni]
>>> for metal in metal:
...     for atom in ni:
...         bimetallics.extend(
...             permute(structure=ni, occupancies=(metal, atom.index + 1))
...         )

ccu.structure.fingerprint module

This module defines the Fingerprint class.

class ccu.structure.fingerprint.Fingerprint(structure: ase.Atoms, reference: int, indices: Iterable[int] | None = None)[source]

Bases: MutableMapping

A set of displacement vectors relative to a particular atom.

The displacement vectors for atoms of a given chemical symbol can be accessed through the MutableMapping interface. For example:

structure = ase.Atoms("CO", positions=[[0, 0, 0], [1, 0, 0]])
fp = Fingerprint(structure, 0, [0, 1])
fp["C"]
Variables:
  • structure – The ase.Atoms instance to which the Fingerprint instance is related.

  • reference – An int indicating the index of the reference atom used to construct the Fingerprint instance.

  • indices – A tuple indicating the indices of the atoms within the structure used to construct the Fingerprint instance.

Generate a fingerprint from a structure.

Parameters:
  • structure – The structure for which the Fingerprint will be generated.

  • reference – The index of the reference atom within structure to Fingerprint.

  • indices – The indices of the atoms corresponding to the points for which the displacements will be calculated to generate the fingerprint. Defaults to None.

classmethod from_structure(structure: ase.Atoms) list[Fingerprint][source]

Creates a list of Fingerprint objects from an ase.Atoms object.

Parameters:

structure – An ase.Atoms instance representing the structure from which to create the list of Fingerprints.

Returns:

A list of the Fingerprints for each atom.

ccu.structure.geometry module

Geometry-related functions for atomic structures.

ccu.structure.geometry.calculate_separation(structure1: Atoms, structure2: Atoms) float[source]

Calculates the separation between two Atoms instances.

The distance is defined as the smallest distance between an atom in one structure and an atom in the second structure.

Parameters:
  • structure1 – An atoms.Atoms instance.

  • structure2 – An atoms.Atoms instance.

Returns:

A float representing the separation between the two structures.

ccu.structure.resizecell module

Cell resizing tools.

This script resizes the c vector of all the .traj files in the current working directory to the specified positive number.

ccu.structure.resizecell.run(structure: Path, length: float)[source]

Resize c-vector of structure and centres atoms in cell.

Parameters:
  • structure – A Path instance leading to the structure whose cell is to be resized.

  • length – A float specifying the new c-vector of the cell.

ccu.structure.symmetry module

Symmetry-related functions and classes.

This class defines the Symmetry and SymmetryOperation subclasses.

Example

>>> import ase
>>> from ccu.structure.symmetry import Rotation, RotationSymmetry
>>> rotation1 = Rotation(90, [0, 0, 1])
>>> symmetry1 = RotationSymmetry(rotation1)
>>> co = ase.Atoms("CO", positions=[[0, 0, 0], [1, 0, 0]])
>>> rotated = rotation1.transform(co)
>>> rotated.positions
array([[0.000000e+00, 0.000000e+00, 0.000000e+00],
      [6.123234e-17, 1.000000e+00, 0.000000e+00]])
>>> symmetry1.check_symmetry(co)
False
>>> h2 = ase.Atoms("HH", positions=[[0, 0, 0], [1, 0, 0]])
>>> rotation2 = Rotation(180, [0, 0, 1])
>>> symmetry2 = RotationSymmetry(rotation2)
>>> symmetry2.check_symmetry(h2)
True
class ccu.structure.symmetry.Inversion[source]

Bases: SymmetryOperation

An inversion operation.

class ccu.structure.symmetry.Rotation(angle: float, axis: Iterable[float])[source]

Bases: SymmetryOperation

A rotation operation.

Variables:
  • angle – A float specifying a rotation angle in degrees.

  • axis – A numpy.array representing the axis of rotation.

Create a rotation symmetry operation.

Parameters:
  • angle – The angle of rotation.

  • axis – The axis of rotation.

as_matrix() ndarray[source]

The rotation matrix of the symmetry operation.

transform(structure: Atoms) Atoms[source]

Rotate a structure.

This retthe given structure by the angle and about the axis specified as attributes of the Rotation object.

Parameters:

structure – An atoms.Atoms instance representing structure to be rotated.

Returns:

A copy of the original atoms.Atoms instance rotated by Rotation.angle about the axis Rotation.axis.

class ccu.structure.symmetry.RotationSymmetry(operation: Rotation)[source]

Bases: Symmetry

A rotational symmetry.

Create a rotation symmetry.

Parameters:

operation – A rotation operation.

check_symmetry(structure: Atoms, tol: float = 0.05) bool[source]

Check if the symmetry belongs to the structure’s symmetry group.

Parameters:
  • structure – An atoms.Atoms instance representing the structure whose symmetry is to be determined.

  • tol – A float specifying the absolute tolerance for positions. Defaults to 5e-2.

Returns:

A bool indicating whether or not the given structure possesses

the symmetry of the RotationSymmetry object subject to the specified tolerance.

property operation: Rotation

The Rotation associated with the symmetry.

class ccu.structure.symmetry.Symmetry[source]

Bases: ABC

An abstract base class for molecule symmetries.

abstract check_symmetry(structure: Atoms, tol: float) bool[source]

Subclasses should override this method.

abstract property operation: SymmetryOperation

Subclasses should override this method.

class ccu.structure.symmetry.SymmetryOperation[source]

Bases: ABC

An abstract base class for symmetry operations.

abstract transform(structure: Atoms) Atoms[source]

Subclasses should override this method.