Source code for diffpy.structure.spacegroupmod

#!/usr/bin/env python
# Copyright 2002 by PyMMLib Development Group, http://pymmlib.sourceforge.net/
# This code is part of the PyMMLib distribution and governed by
# its license. Please see the LICENSE_pymmlib file that should have been
# included as part of this package.
"""Symmetry operations as functions on vectors or arrays.
"""

import numpy

# 64 unique rotation matricies
Rot_Z_mY_X = numpy.array([[0.0, 0.0, 1.0], [0.0, -1.0, 0.0], [1.0, 0.0, 0.0]], float)
Rot_Y_mX_mZ = numpy.array([[0.0, 1.0, 0.0], [-1.0, 0.0, 0.0], [0.0, 0.0, -1.0]], float)
Rot_XmY_X_mZ = numpy.array([[1.0, -1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, -1.0]], float)
Rot_mX_Y_mZ = numpy.array([[-1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, -1.0]], float)
Rot_X_mZ_Y = numpy.array([[1.0, 0.0, 0.0], [0.0, 0.0, -1.0], [0.0, 1.0, 0.0]], float)
Rot_Y_mXY_Z = numpy.array([[0.0, 1.0, 0.0], [-1.0, 1.0, 0.0], [0.0, 0.0, 1.0]], float)
Rot_Y_mX_Z = numpy.array([[0.0, 1.0, 0.0], [-1.0, 0.0, 0.0], [0.0, 0.0, 1.0]], float)
Rot_XmY_X_Z = numpy.array([[1.0, -1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]], float)
Rot_mX_mXY_mZ = numpy.array([[-1.0, 0.0, 0.0], [-1.0, 1.0, 0.0], [0.0, 0.0, -1.0]], float)
Rot_Y_Z_X = numpy.array([[0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]], float)
Rot_mY_mZ_X = numpy.array([[0.0, -1.0, 0.0], [0.0, 0.0, -1.0], [1.0, 0.0, 0.0]], float)
Rot_X_Z_mY = numpy.array([[1.0, 0.0, 0.0], [0.0, 0.0, 1.0], [0.0, -1.0, 0.0]], float)
Rot_XmY_mY_Z = numpy.array([[1.0, -1.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]], float)
Rot_Y_X_mZ = numpy.array([[0.0, 1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, -1.0]], float)
Rot_Y_mZ_X = numpy.array([[0.0, 1.0, 0.0], [0.0, 0.0, -1.0], [1.0, 0.0, 0.0]], float)
Rot_mXY_Y_Z = numpy.array([[-1.0, 1.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], float)
Rot_mX_mY_mZ = numpy.array([[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0]], float)
Rot_X_Y_mZ = numpy.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, -1.0]], float)
Rot_mXY_mX_Z = numpy.array([[-1.0, 1.0, 0.0], [-1.0, 0.0, 0.0], [0.0, 0.0, 1.0]], float)
Rot_mZ_mY_mX = numpy.array([[0.0, 0.0, -1.0], [0.0, -1.0, 0.0], [-1.0, 0.0, 0.0]], float)
Rot_X_mZ_mY = numpy.array([[1.0, 0.0, 0.0], [0.0, 0.0, -1.0], [0.0, -1.0, 0.0]], float)
Rot_X_Y_Z = numpy.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], float)
Rot_mY_mX_mZ = numpy.array([[0.0, -1.0, 0.0], [-1.0, 0.0, 0.0], [0.0, 0.0, -1.0]], float)
Rot_mY_X_Z = numpy.array([[0.0, -1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]], float)
Rot_Z_X_Y = numpy.array([[0.0, 0.0, 1.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]], float)
Rot_X_XmY_Z = numpy.array([[1.0, 0.0, 0.0], [1.0, -1.0, 0.0], [0.0, 0.0, 1.0]], float)
Rot_mY_X_mZ = numpy.array([[0.0, -1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, -1.0]], float)
Rot_mY_Z_mX = numpy.array([[0.0, -1.0, 0.0], [0.0, 0.0, 1.0], [-1.0, 0.0, 0.0]], float)
Rot_mY_Z_X = numpy.array([[0.0, -1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]], float)
Rot_mX_mZ_mY = numpy.array([[-1.0, 0.0, 0.0], [0.0, 0.0, -1.0], [0.0, -1.0, 0.0]], float)
Rot_mX_Z_Y = numpy.array([[-1.0, 0.0, 0.0], [0.0, 0.0, 1.0], [0.0, 1.0, 0.0]], float)
Rot_mZ_mX_mY = numpy.array([[0.0, 0.0, -1.0], [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0]], float)
Rot_X_XmY_mZ = numpy.array([[1.0, 0.0, 0.0], [1.0, -1.0, 0.0], [0.0, 0.0, -1.0]], float)
Rot_mY_XmY_mZ = numpy.array([[0.0, -1.0, 0.0], [1.0, -1.0, 0.0], [0.0, 0.0, -1.0]], float)
Rot_Z_X_mY = numpy.array([[0.0, 0.0, 1.0], [1.0, 0.0, 0.0], [0.0, -1.0, 0.0]], float)
Rot_mZ_mY_X = numpy.array([[0.0, 0.0, -1.0], [0.0, -1.0, 0.0], [1.0, 0.0, 0.0]], float)
Rot_X_Z_Y = numpy.array([[1.0, 0.0, 0.0], [0.0, 0.0, 1.0], [0.0, 1.0, 0.0]], float)
Rot_Z_mX_mY = numpy.array([[0.0, 0.0, 1.0], [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0]], float)
Rot_mX_Z_mY = numpy.array([[-1.0, 0.0, 0.0], [0.0, 0.0, 1.0], [0.0, -1.0, 0.0]], float)
Rot_X_mY_Z = numpy.array([[1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]], float)
Rot_mY_mX_Z = numpy.array([[0.0, -1.0, 0.0], [-1.0, 0.0, 0.0], [0.0, 0.0, 1.0]], float)
Rot_Z_mY_mX = numpy.array([[0.0, 0.0, 1.0], [0.0, -1.0, 0.0], [-1.0, 0.0, 0.0]], float)
Rot_mX_mY_Z = numpy.array([[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]], float)
Rot_Z_Y_X = numpy.array([[0.0, 0.0, 1.0], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0]], float)
Rot_mZ_Y_mX = numpy.array([[0.0, 0.0, -1.0], [0.0, 1.0, 0.0], [-1.0, 0.0, 0.0]], float)
Rot_Y_Z_mX = numpy.array([[0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [-1.0, 0.0, 0.0]], float)
Rot_mY_XmY_Z = numpy.array([[0.0, -1.0, 0.0], [1.0, -1.0, 0.0], [0.0, 0.0, 1.0]], float)
Rot_mXY_Y_mZ = numpy.array([[-1.0, 1.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, -1.0]], float)
Rot_mZ_mX_Y = numpy.array([[0.0, 0.0, -1.0], [-1.0, 0.0, 0.0], [0.0, 1.0, 0.0]], float)
Rot_mX_mZ_Y = numpy.array([[-1.0, 0.0, 0.0], [0.0, 0.0, -1.0], [0.0, 1.0, 0.0]], float)
Rot_mX_Y_Z = numpy.array([[-1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], float)
Rot_X_mY_mZ = numpy.array([[1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0]], float)
Rot_mZ_X_Y = numpy.array([[0.0, 0.0, -1.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]], float)
Rot_Y_mZ_mX = numpy.array([[0.0, 1.0, 0.0], [0.0, 0.0, -1.0], [-1.0, 0.0, 0.0]], float)
Rot_mY_mZ_mX = numpy.array([[0.0, -1.0, 0.0], [0.0, 0.0, -1.0], [-1.0, 0.0, 0.0]], float)
Rot_mZ_Y_X = numpy.array([[0.0, 0.0, -1.0], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0]], float)
Rot_Z_Y_mX = numpy.array([[0.0, 0.0, 1.0], [0.0, 1.0, 0.0], [-1.0, 0.0, 0.0]], float)
Rot_mXY_mX_mZ = numpy.array([[-1.0, 1.0, 0.0], [-1.0, 0.0, 0.0], [0.0, 0.0, -1.0]], float)
Rot_XmY_mY_mZ = numpy.array([[1.0, -1.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0]], float)
Rot_Z_mX_Y = numpy.array([[0.0, 0.0, 1.0], [-1.0, 0.0, 0.0], [0.0, 1.0, 0.0]], float)
Rot_mX_mXY_Z = numpy.array([[-1.0, 0.0, 0.0], [-1.0, 1.0, 0.0], [0.0, 0.0, 1.0]], float)
Rot_Y_mXY_mZ = numpy.array([[0.0, 1.0, 0.0], [-1.0, 1.0, 0.0], [0.0, 0.0, -1.0]], float)
Rot_mZ_X_mY = numpy.array([[0.0, 0.0, -1.0], [1.0, 0.0, 0.0], [0.0, -1.0, 0.0]], float)
Rot_Y_X_Z = numpy.array([[0.0, 1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]], float)

# 32 unique translation vectors
Tr_0_0_34 = numpy.array([0.0, 0.0, 3.0 / 4.0], float)
Tr_12_0_34 = numpy.array([1.0 / 2.0, 0.0, 3.0 / 4.0], float)
Tr_0_0_56 = numpy.array([0.0, 0.0, 5.0 / 6.0], float)
Tr_12_0_12 = numpy.array([1.0 / 2.0, 0.0, 1.0 / 2.0], float)
Tr_0_12_12 = numpy.array([0.0, 1.0 / 2.0, 1.0 / 2.0], float)
Tr_12_0_14 = numpy.array([1.0 / 2.0, 0.0, 1.0 / 4.0], float)
Tr_0_12_14 = numpy.array([0.0, 1.0 / 2.0, 1.0 / 4.0], float)
Tr_14_14_14 = numpy.array([1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0], float)
Tr_0_12_34 = numpy.array([0.0, 1.0 / 2.0, 3.0 / 4.0], float)
Tr_34_14_14 = numpy.array([3.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0], float)
Tr_0_0_0 = numpy.array([0.0, 0.0, 0.0], float)
Tr_23_13_56 = numpy.array([2.0 / 3.0, 1.0 / 3.0, 5.0 / 6.0], float)
Tr_14_14_34 = numpy.array([1.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0], float)
Tr_12_12_0 = numpy.array([1.0 / 2.0, 1.0 / 2.0, 0.0], float)
Tr_23_13_13 = numpy.array([2.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0], float)
Tr_13_23_23 = numpy.array([1.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0], float)
Tr_12_12_12 = numpy.array([1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0], float)
Tr_12_12_14 = numpy.array([1.0 / 2.0, 1.0 / 2.0, 1.0 / 4.0], float)
Tr_14_34_14 = numpy.array([1.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0], float)
Tr_12_12_34 = numpy.array([1.0 / 2.0, 1.0 / 2.0, 3.0 / 4.0], float)
Tr_0_0_23 = numpy.array([0.0, 0.0, 2.0 / 3.0], float)
Tr_0_12_0 = numpy.array([0.0, 1.0 / 2.0, 0.0], float)
Tr_14_34_34 = numpy.array([1.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0], float)
Tr_34_34_14 = numpy.array([3.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0], float)
Tr_12_0_0 = numpy.array([1.0 / 2.0, 0.0, 0.0], float)
Tr_34_34_34 = numpy.array([3.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0], float)
Tr_0_0_13 = numpy.array([0.0, 0.0, 1.0 / 3.0], float)
Tr_0_0_12 = numpy.array([0.0, 0.0, 1.0 / 2.0], float)
Tr_13_23_16 = numpy.array([1.0 / 3.0, 2.0 / 3.0, 1.0 / 6.0], float)
Tr_0_0_14 = numpy.array([0.0, 0.0, 1.0 / 4.0], float)
Tr_0_0_16 = numpy.array([0.0, 0.0, 1.0 / 6.0], float)
Tr_34_14_34 = numpy.array([3.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0], float)


[docs] class SymOp(object): """The transformation of coordinates to a symmetry-related position. The SymOp operation involves rotation and translation in cell coordinates. Parameters ---------- R : numpy.ndarray The 3x3 matrix of rotation for this symmetry operation. t : numpy.ndarray The vector of translation in this symmetry operation. Attributes ---------- R : numpy.ndarray The 3x3 matrix of rotation pertaining to unit cell coordinates. This may be identity, simple rotation, improper rotation, mirror or inversion. The determinant of *R* is either +1 or -1. t : numpy.ndarray The translation of cell coordinates applied after rotation *R*. """ def __init__(self, R, t): self.R = R self.t = t return def __str__(self): """Printable representation of this SymOp object.""" x = "[%6.3f %6.3f %6.3f %6.3f]\n" % (self.R[0, 0], self.R[0, 1], self.R[0, 2], self.t[0]) x += "[%6.3f %6.3f %6.3f %6.3f]\n" % (self.R[1, 0], self.R[1, 1], self.R[1, 2], self.t[1]) x += "[%6.3f %6.3f %6.3f %6.3f]\n" % (self.R[2, 0], self.R[2, 1], self.R[2, 2], self.t[2]) return x
[docs] def __call__(self, vec): """Return symmetry-related position for the specified coordinates. Parameters ---------- vec : numpy.ndarray The initial position in fractional cell coordinates. Returns ------- numpy.ndarray The transformed position after this symmetry operation. """ return numpy.dot(self.R, vec) + self.t
[docs] def __eq__(self, symop): """Implement the ``self == symop`` test of equality. Return ``True`` when *self* and *symop* difference is within tiny round-off errors. """ return numpy.allclose(self.R, symop.R) and numpy.allclose(self.t, symop.t)
[docs] def is_identity(self): """Check if this SymOp is an identity operation. Returns ------- bool ``True`` if this is an identity operation within a small round-off. Return ``False`` otherwise. """ rv = numpy.allclose(self.R, numpy.identity(3, float)) and numpy.allclose(self.t, numpy.zeros(3, float)) return rv
# End of class SymOp
[docs] class SpaceGroup(object): """Definition and basic operations for a specific space group. Provide standard names and all symmetry operations contained in one space group. Parameters ---------- number : int The space group number. num_sym_equiv : int The number of symmetry equivalent sites for a general position. num_primitive_sym_equiv : int The number of symmetry equivalent sites in a primitive unit cell. short_name : str The short Hermann-Mauguin symbol of the space group. point_group_name : str The point group of this space group. crystal_system : str The crystal system of this space group. pdb_name : str The full Hermann-Mauguin symbol of the space group. symop_list : list of SymOp The symmetry operations contained in this space group. Attributes ---------- number : int A unique space group number. This may be incremented by several thousands to facilitate unique values for multiple settings of the same space group. Use ``number % 1000`` to get the standard space group number from International Tables. num_sym_equiv : int The number of symmetry equivalent sites for a general position. num_primitive_sym_equiv : int The number of symmetry equivalent sites in a primitive unit cell. short_name : str The short Hermann-Mauguin symbol of the space group. point_group_name : str The point group to which this space group belongs to. crystal_system : str The crystal system of this space group. The possible values are ``"TRICLINIC", "MONOCLINIC", "ORTHORHOMBIC", "TETRAGONAL", "TRIGONAL" "HEXAGONAL", "CUBIC"``. pdb_name : str The full Hermann-Mauguin symbol of the space group. symop_list : list of SymOp A list of `SymOp` objects for all symmetry operations in this space group. """ def __init__( self, number=None, num_sym_equiv=None, num_primitive_sym_equiv=None, short_name=None, point_group_name=None, crystal_system=None, pdb_name=None, symop_list=None, ): self.number = number self.num_sym_equiv = num_sym_equiv self.num_primitive_sym_equiv = num_primitive_sym_equiv self.short_name = short_name self.point_group_name = point_group_name self.crystal_system = crystal_system self.pdb_name = pdb_name self.symop_list = symop_list def __repr__(self): """Return a string representation of the space group.""" return ("SpaceGroup #%i (%s, %s). Symmetry matrices: %i, " "point sym. matr.: %i") % ( self.number, self.short_name, self.crystal_system[0] + self.crystal_system[1:].lower(), self.num_sym_equiv, self.num_primitive_sym_equiv, )
[docs] def iter_symops(self): """Iterate over all symmetry operations in the space group. Yields ------ SymOp Generate all symmetry operations for this space group. """ return iter(self.symop_list)
[docs] def check_group_name(self, name): """Check if given name matches this space group. Parameters ---------- name : str or int The space group identifier, a string name or number. Returns ------- bool ``True`` if the specified name matches one of the recognized names of this space group or if it equals its `number`. Return ``False`` otherwise. """ if name == self.short_name: return True if name == self.pdb_name: return True if name == self.point_group_name: return True if name == self.number: return True return False
[docs] def iter_equivalent_positions(self, vec): """Generate symmetry equivalent positions for the specified position. The initial position must be in fractional coordinates and so are the symmetry equivalent positions yielded by iteration. This generates `num_sym_equiv` positions regardless of initial coordinates being a special symmetry position or not. Parameters ---------- vec : numpy.ndarray The initial position in fractional coordinates. Yields ------ numpy.ndarray The symmetry equivalent positions in fractional coordinates. The positions may be duplicate or outside of the ``0 <= x < 1`` unit cell bounds. """ for symop in self.symop_list: yield symop(vec) pass
# End of class SpaceGroup