"""This class defines the SymmetryOperation and Symmetry classes andsubclasses.Symmetry and SymmetryOperation subclasses can be used as follows:>>> 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.positionsarray([[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"""importabcfromcollections.abcimportIterableimportaseimportnumpyasnpfromnumpy.linalgimportnormfromscipy.spatialimporttransformfromccu.structureimportcomparator
[docs]classSymmetryOperation(abc.ABC):"""An abstract base class for symmetry operations."""
[docs]@abc.abstractmethoddeftransform(self,structure:ase.Atoms)->ase.Atoms:"Subclasses should override this method."
[docs]classRotation(SymmetryOperation):"""A rotation operation. Attributes: angle: A float specifying a rotation angle in degrees. axis: A numpy.array representing the axis of rotation. """def__init__(self,angle:float,axis:Iterable[float])->None:self.angle=angleself.axis=np.array(axis)
[docs]deftransform(self,structure:ase.Atoms)->ase.Atoms:"""Rotates the given structure by the angle and about the axis specified as attributes of the Rotation object. Args: structure: An ase.Atoms instance representing structure to be rotated. Returns: A rotated copy of the original ase.Atoms instance. """new_structure=structure.copy()new_structure.rotate(self.angle,self.axis)returnnew_structure
[docs]defas_matrix(self)->np.ndarray:"""Returns the rotation operation of this instance as a numpy.ndarray which represents the rotation matrix. """rotvec=self.angle*(self.axis/norm(self.axis))rotation=transform.Rotation.from_rotvec(rotvec,degrees=True)returnrotation.as_matrix()
[docs]classSymmetry(abc.ABC):"""An abstract base class for molecule symmetries."""@property@abc.abstractmethoddefoperation(self)->SymmetryOperation:"Subclasses should override this method."
[docs]@abc.abstractmethoddefcheck_symmetry(self,structure:ase.Atoms,tol:float)->bool:"Subclasses should override this method."
[docs]classRotationSymmetry(Symmetry):"""A rotational symmetry."""def__init__(self,operation:Rotation)->None:self._operation=operation@propertydefoperation(self)->Rotation:"""The Rotation instance associated with this RotationSymmetry instance."""returnself._operation
[docs]defcheck_symmetry(self,structure:ase.Atoms,tol:float=5e-2)->bool:"""Determines the symmetry represented by the instance belongs to the given structure. Args: structure: An ase.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 boolean indicating whether or not the given structure possesses the symmetry of the RotationSymmetry object subject to the specified tolerance. """old_structure=structure# Rotate structurerotated_structure=self._operation.transform(structure)# Check for similarity wrt. tolerancereturncomparator.Comparator.check_similarity(old_structure,rotated_structure,tol=tol)