def is_dihedral(self): """ Whether or not the reflection is a sigma_d """ if self.principal_reflection: return False for atom in self.molecule: # If there's an atom in the plane, it's a sigma_v. If there's an atom in the direction of the normal (or it's negative), # it's sigma_v. Otherwise, it's a sigma_d. If the atom is at the origin, ignore it. if atom.pos.is_zero(): continue elif SymmetryOperation.is_same_axis(self.axis, atom.pos): return False elif SymmetryOperation.is_perpendicular(self.axis, atom.pos): return False return True
def check_improper_rotation(atoms, vector, angle, center, n, result, order_no=0): a = atoms.copy() a.rotate(vector, a=angle, center=center) positions = mirror_through_plane(a, vector) equals = get_equals(atoms, positions) on = order_no # n = 2 is equal with inversion operator if n == 2 or (float(order_no + 1) / float(n)) == 0.5: return if equals != None: operation_name = "S%i" % n while order_no > 0: operation_name += "'" order_no -= 1 result.append( SymmetryOperation(operation_name, equals, None, vector=vector, magnitude=n, type='S', order_no=on))
def check_mirror(atoms, normal_vector, debug=False): com = atoms.get_center_of_mass() # normalize vector L = numpy.linalg.norm(normal_vector) normal_vector /= L mirrored = mirror_through_plane(atoms, normal_vector, debug=debug) if debug: print normal_vector print atoms.get_positions() print mirrored from ice_package.help_methods import get_oxygens import ase ase.visualize.view(get_oxygens(mirrored)) ase.visualize.view(atoms) print normal_vector raw_input("Continue") result = get_equals(atoms, mirrored, debug=debug) if result == None: return None return SymmetryOperation('sigma', result, None, vector=normal_vector, magnitude=1, order_no=0, type='sigma')
def element_with_matrix(self, mat): """ Returns the element that has the matrix `mat` in the group `self`. Raises a `GroupTheoryError` indicating the group is not closed if no such element is found. """ for op in self: if SymmetryOperation.is_same_matrix(op.matrix, mat): return op mat_list = [str(op.matrix) for op in sorted(self, key=lambda o: (o.matrix-mat).norm())] raise GroupTheoryError("Group not closed: In group with elements: \n[" + ", ".join(str(el) for el in self) + "],\n Could not find element with the following matrix:\n" + str(mat) + " in list (" + str(len(mat_list)) + " operations total):\n" + ",\n".join(mat_list))
def check_inversion_center(atoms): positions = positions_related_to_center_of_mass(atoms, atoms.get_positions()) positions = -positions + atoms.get_center_of_mass() result = get_equals(atoms, positions) if result == None: return None print "Inversion center found" return SymmetryOperation("i", result, None, type='i', order_no=0, magnitude=1)
def element_with_matrix(self, mat): """ Returns the element that has the matrix `mat` in the group `self`. Raises a `GroupTheoryError` indicating the group is not closed if no such element is found. """ for op in self: if SymmetryOperation.is_same_matrix(op.matrix, mat): return op mat_list = [ str(op.matrix) for op in sorted(self, key=lambda o: (o.matrix - mat).norm()) ] raise GroupTheoryError( "Group not closed: In group with elements: \n[" + ", ".join(str(el) for el in self) + "],\n Could not find element with the following matrix:\n" + str(mat) + " in list (" + str(len(mat_list)) + " operations total):\n" + ",\n".join(mat_list))
def check_single_rotation(atoms, vector, angle, center, n, result, order_no=0): a = atoms.copy() a.rotate(vector, a=angle, center=center) positions = a.get_positions() equals = get_equals(atoms, positions) on = order_no if equals != None: operation_name = "C%i" % n while order_no > 0: operation_name += "'" order_no -= 1 result.append( SymmetryOperation(operation_name, equals, None, vector=vector, magnitude=n, type='C', order_no=on))
def _get_name(self): """ Get the name of the point group """ # Using a flow charts from http://www.mineralatlas.com/fga/image007.gif and # http://capsicum.me.utexas.edu/ChE386K/html/flowchart_point_groups.htm # If two or more C_n with n > 2... if len( [rot for rot in self.rotations if rot.n > 2 and rot.exponent == 1 ]) >= 2: # 12 C5 axes? if self.num_C_n_axes(5) == 12: if self.inversion is None: self.name = 'I' return else: self.name = 'I_h' return # 6 C4s? elif self.num_C_n_axes(4) == 6: if self.inversion is None: self.name = 'O' return else: self.name = 'O_h' return # 4 C3s? elif self.num_C_n_axes(3) == 4: # 8 C3s? if self.inversion is None: if len(self.reflections) == 6: self.name = 'T_d' return else: self.name = 'T' return else: self.name = 'T_h' return else: raise GroupTheoryError( "Don't know the name of group with classes: " + str(self.classes) + ". (Perhaps adjust tolerances?)") elif len(self.rotations) > 0: highest_Cn = self.rotations[0] highest_n = highest_Cn.n highest_axis = highest_Cn.axis count = 0 for rot in self.rotations: if rot.n == 2 and SymmetryOperation.is_perpendicular( highest_axis, rot.axis): count += 1 if count == highest_n: if any(ref.is_principal_reflection() for ref in self.reflections): self.name = 'D_' + str(highest_n) + 'h' return elif len(self.reflections) == highest_n: self.name = 'D_' + str(highest_n) + 'd' return elif len(self.reflections) == 0: self.name = 'D_' + str(highest_n) else: raise GroupTheoryError( "Don't know the name of group with classes: " + str(self.classes) + ". (Perhaps adjust tolerances?)") else: if any(ref.is_principal_reflection() for ref in self.reflections): self.name = 'C_' + str(highest_n) + 'h' return elif len(self.reflections) == highest_n: self.name = 'C_' + str(highest_n) + 'v' return elif any(irot.n == 2 * highest_n for irot in self.improper_rotations): self.name = 'S_' + str(2 * highest_n) return else: self.name = 'C_' + str(highest_n) return else: if len(self.reflections) == 1: self.name = 'C_s' return elif not self.inversion is None: self.name = 'C_i' return elif len(self) == 1: self.name = 'C_1' else: raise GroupTheoryError( "Don't know the name of group with classes: " + str(self.classes) + ". (Perhaps adjust tolerances?)")
def _name_operations(self): """ Determines the most-likely-to-be human-readable names for the operations. """ # Figure out the principal reflections (sigma_h's) principal_rotations = [] for k, g in itertools.groupby(sorted(self.rotations), attrgetter('n', 'exponent')): principal_rotations = list(g) break if len(principal_rotations) > 0: if principal_rotations[0].n > 2: # Mark them all as sigma_h's for rot in principal_rotations: for op in self.reflections: if SymmetryOperation.is_same_axis(op.axis, rot.axis): op.principal_reflection = True else: # Otherwise, just choose the Z axis for op in self.reflections: if SymmetryOperation.is_same_axis( op.axis, principal_rotations[0].axis): op.principal_reflection = True break self.operations.sort() self._rotations = None self._improper_rotations = None self._reflections = None self.identity_element.name = "E" if not self.inversion is None: self.inversion.name = 'i' for key, rotiter in itertools.groupby(self.rotations, attrgetter('n')): rotgrp = list(rotiter) prime_count = 0 labels = [] order = rotgrp[0].n if len(rotgrp) == rotgrp[0].n - 1: # only one axis, label it simply rotgrp[0].name = "C_" + str(order) for i in xrange(1, len(rotgrp)): rotgrp[i].name = "C_" + str(order) + "^" + str( rotgrp[i].exponent) else: naxes = len([x for x in rotgrp if x.exponent == 1]) has_paren_label = False for i, rot in enumerate(rotgrp): if i < naxes: if SymmetryOperation.is_same_axis( rot.axis, Vector(0, 0, 1)): labels.append("(z)") has_paren_label = True rot.name = "C_" + str(order) + "(z)" elif SymmetryOperation.is_same_axis( rot.axis, Vector(0, 1, 1)): labels.append("(y)") has_paren_label = True rot.name = "C_" + str(order) + "(y)" elif SymmetryOperation.is_same_axis( rot.axis, Vector(1, 0, 0)): labels.append("(x)") has_paren_label = True rot.name = "C_" + str(order) + "(x)" else: label = "'" * (prime_count + (1 if has_paren_label else 0)) labels.append(label) prime_count += 1 rot.name = "C_" + str(order) + label else: rot.name = "C_" + str(order) + labels[ i % naxes] + "^" + str(rot.exponent) for key, rotiter in itertools.groupby(self.improper_rotations, attrgetter('n')): rotgrp = list(rotiter) prime_count = 0 labels = [] order = rotgrp[0].n if len(rotgrp) == rotgrp[0].n - 1: # only one axis, label it simply rotgrp[0].name = "S_" + str(order) for i in xrange(1, len(rotgrp)): rotgrp[i].name = "S_" + str(order) + "^" + str( rotgrp[i].exponent) else: has_paren_label = False naxes = len([x for x in rotgrp if x.exponent == 1]) for i, rot in enumerate(rotgrp): if i < naxes: if SymmetryOperation.is_same_axis( rot.axis, Vector(0, 0, 1)): labels.append("(z)") has_paren_label = True rot.name = "S_" + str(order) + "(z)" elif SymmetryOperation.is_same_axis( rot.axis, Vector(0, 1, 0)): labels.append("(y)") has_paren_label = True rot.name = "S_" + str(order) + "(y)" elif SymmetryOperation.is_same_axis( rot.axis, Vector(1, 0, 0)): labels.append("(x)") has_paren_label = True rot.name = "S_" + str(order) + "(x)" else: label = "'" * (prime_count + (1 if has_paren_label else 0)) labels.append(label) prime_count += 1 rot.name = "S_" + str(order) + label else: rot.name = "S_" + str(order) + labels[ i % naxes] + "^" + str(rot.exponent) prime_count = {'v': 0, 'd': 0, 'h': 0} has_paren_label = False for ref in self.reflections: subscript = '' if ref.is_principal_reflection(): subscript = 'h' elif ref.is_dihedral(): subscript = 'd' else: subscript = 'v' if SymmetryOperation.is_same_axis(ref.axis, Vector(0, 0, 1)): ref.name = "sigma_" + subscript + "(xy)" has_paren_label = True elif SymmetryOperation.is_same_axis(ref.axis, Vector(0, 1, 0)): ref.name = "sigma_" + subscript + "(xz)" has_paren_label = True elif SymmetryOperation.is_same_axis(ref.axis, Vector(1, 0, 0)): ref.name = "sigma_" + subscript + "(yz)" has_paren_label = True else: label = "'" * (prime_count[subscript] + (1 if has_paren_label else 0)) prime_count[subscript] += 1 ref.name = "sigma_" + subscript + label self.classes.sort(key=attrgetter('first_element'))
def get_periodic_symmetry_operations(atoms, error_tolerance=0.01, debug=False): from ase.utils import irotate from ase.visualize import view from pyspglib import spglib #symmetry = spglib.get_symmetry(atoms, symprec=1e-5) symmetry = spglib.get_symmetry(atoms, symprec=1e-2) dataset = spglib.get_symmetry_dataset(atoms, symprec=1e-2) result = [] if debug: cell, scaled_positions, numbers = spglib.find_primitive(atoms, symprec=1e-5) a = Atoms(symbols='O4', cell=cell, scaled_positions=scaled_positions, pbc=True) #symmetry = spglib.get_symmetry(a, symprec=1e-2) #dataset = spglib.get_symmetry_dataset(a, symprec=1e-2) print dataset['equivalent_atoms'] print dataset['international'] print dataset['hall'] print dataset['wyckoffs'] print dataset['transformation_matrix'] print "Number of symmetry operations %i" % len(dataset['rotations']) for i in range(dataset['rotations'].shape[0]): new_atoms = atoms.copy() test = atoms.copy() rot = dataset['rotations'][i] trans = dataset['translations'][i] if debug: x, y, z = irotate(rot) #print x, y, z print "------------------- %i -----------------------" % i print "Rotation" print rot print "Translation" print trans new_pos = new_atoms.get_scaled_positions() for l, pos in enumerate(new_atoms.get_scaled_positions()): #print new_pos[l] new_pos[l] = (numpy.dot(rot, pos)) new_pos[l] += trans #print new_pos[l] new_atoms.set_scaled_positions(new_pos) equals = get_equals_periodic(atoms, new_atoms, error_tolerance=error_tolerance, debug=debug) if equals != None: so = SymmetryOperation(str(i), equals, None, vector=None, magnitude=1, rotation_matrix=rot, translation_vector=trans) #if debug: # print so result.append(so) else: print "Equivalent not found" #view(test) #view(new_atoms) #raw_input() return result
def get_identity_operator(atoms): equals = numpy.arange(0, len(atoms), 1) return SymmetryOperation("Identity", equals, None)
def _get_name(self): """ Get the name of the point group """ # Using a flow charts from http://www.mineralatlas.com/fga/image007.gif and # http://capsicum.me.utexas.edu/ChE386K/html/flowchart_point_groups.htm # If two or more C_n with n > 2... if len([rot for rot in self.rotations if rot.n > 2 and rot.exponent == 1 ]) >= 2: # 12 C5 axes? if self.num_C_n_axes(5) == 12: if self.inversion is None: self.name = 'I' return else: self.name = 'I_h' return # 6 C4s? elif self.num_C_n_axes(4) == 6: if self.inversion is None: self.name = 'O' return else: self.name = 'O_h' return # 4 C3s? elif self.num_C_n_axes(3) == 4: # 8 C3s? if self.inversion is None: if len(self.reflections) == 6: self.name = 'T_d' return else: self.name = 'T' return else: self.name = 'T_h' return else: raise GroupTheoryError("Don't know the name of group with classes: " + str(self.classes) + ". (Perhaps adjust tolerances?)") elif len(self.rotations) > 0: highest_Cn = self.rotations[0] highest_n = highest_Cn.n highest_axis = highest_Cn.axis count = 0 for rot in self.rotations: if rot.n == 2 and SymmetryOperation.is_perpendicular(highest_axis, rot.axis): count += 1 if count == highest_n: if any(ref.is_principal_reflection() for ref in self.reflections): self.name = 'D_' + str(highest_n) + 'h' return elif len(self.reflections) == highest_n: self.name = 'D_' + str(highest_n) + 'd' return elif len(self.reflections) == 0: self.name = 'D_' + str(highest_n) else: raise GroupTheoryError("Don't know the name of group with classes: " + str(self.classes) + ". (Perhaps adjust tolerances?)") else: if any(ref.is_principal_reflection() for ref in self.reflections): self.name = 'C_' + str(highest_n) + 'h' return elif len(self.reflections) == highest_n: self.name = 'C_' + str(highest_n) + 'v' return elif any(irot.n == 2*highest_n for irot in self.improper_rotations): self.name = 'S_' + str(2*highest_n) return else: self.name = 'C_' + str(highest_n) return else: if len(self.reflections) == 1: self.name = 'C_s' return elif not self.inversion is None: self.name = 'C_i' return elif len(self) == 1: self.name = 'C_1' else: raise GroupTheoryError("Don't know the name of group with classes: " + str(self.classes) + ". (Perhaps adjust tolerances?)")
def _name_operations(self): """ Determines the most-likely-to-be human-readable names for the operations. """ # Figure out the principal reflections (sigma_h's) principal_rotations = [] for k, g in itertools.groupby(sorted(self.rotations), attrgetter('n', 'exponent')): principal_rotations = list(g) break if len(principal_rotations) > 0: if principal_rotations[0].n > 2: # Mark them all as sigma_h's for rot in principal_rotations: for op in self.reflections: if SymmetryOperation.is_same_axis(op.axis, rot.axis): op.principal_reflection = True else: # Otherwise, just choose the Z axis for op in self.reflections: if SymmetryOperation.is_same_axis(op.axis, principal_rotations[0].axis): op.principal_reflection = True break self.operations.sort() self._rotations = None self._improper_rotations = None self._reflections = None self.identity_element.name = "E" if not self.inversion is None: self.inversion.name = 'i' for key, rotiter in itertools.groupby(self.rotations, attrgetter('n')): rotgrp = list(rotiter) prime_count = 0 labels = [] order = rotgrp[0].n if len(rotgrp) == rotgrp[0].n - 1: # only one axis, label it simply rotgrp[0].name = "C_" + str(order) for i in xrange(1, len(rotgrp)): rotgrp[i].name = "C_" + str(order) + "^" + str(rotgrp[i].exponent) else: naxes = len([x for x in rotgrp if x.exponent == 1]) has_paren_label = False for i, rot in enumerate(rotgrp): if i < naxes: if SymmetryOperation.is_same_axis(rot.axis, Vector(0,0,1)): labels.append("(z)") has_paren_label = True rot.name = "C_" + str(order) + "(z)" elif SymmetryOperation.is_same_axis(rot.axis, Vector(0,1,1)): labels.append("(y)") has_paren_label = True rot.name = "C_" + str(order) + "(y)" elif SymmetryOperation.is_same_axis(rot.axis, Vector(1,0,0)): labels.append("(x)") has_paren_label = True rot.name = "C_" + str(order) + "(x)" else: label = "'" * (prime_count + (1 if has_paren_label else 0)) labels.append(label) prime_count += 1 rot.name = "C_" + str(order) + label else: rot.name = "C_" + str(order) + labels[i % naxes] + "^" + str(rot.exponent) for key, rotiter in itertools.groupby(self.improper_rotations, attrgetter('n')): rotgrp = list(rotiter) prime_count = 0 labels = [] order = rotgrp[0].n if len(rotgrp) == rotgrp[0].n - 1: # only one axis, label it simply rotgrp[0].name = "S_" + str(order) for i in xrange(1, len(rotgrp)): rotgrp[i].name = "S_" + str(order) + "^" + str(rotgrp[i].exponent) else: has_paren_label = False naxes = len([x for x in rotgrp if x.exponent == 1]) for i, rot in enumerate(rotgrp): if i < naxes: if SymmetryOperation.is_same_axis(rot.axis, Vector(0,0,1)): labels.append("(z)") has_paren_label = True rot.name = "S_" + str(order) + "(z)" elif SymmetryOperation.is_same_axis(rot.axis, Vector(0,1,0)): labels.append("(y)") has_paren_label = True rot.name = "S_" + str(order) + "(y)" elif SymmetryOperation.is_same_axis(rot.axis, Vector(1,0,0)): labels.append("(x)") has_paren_label = True rot.name = "S_" + str(order) + "(x)" else: label = "'" * (prime_count + (1 if has_paren_label else 0)) labels.append(label) prime_count += 1 rot.name = "S_" + str(order) + label else: rot.name = "S_" + str(order) + labels[i % naxes] + "^" + str(rot.exponent) prime_count = {'v':0, 'd':0,'h':0} has_paren_label = False for ref in self.reflections: subscript = '' if ref.is_principal_reflection(): subscript = 'h' elif ref.is_dihedral(): subscript = 'd' else: subscript = 'v' if SymmetryOperation.is_same_axis(ref.axis, Vector(0,0,1)): ref.name = "sigma_"+ subscript + "(xy)" has_paren_label = True elif SymmetryOperation.is_same_axis(ref.axis, Vector(0,1,0)): ref.name = "sigma_" + subscript + "(xz)" has_paren_label = True elif SymmetryOperation.is_same_axis(ref.axis, Vector(1,0,0)): ref.name = "sigma_" + subscript + "(yz)" has_paren_label = True else: label = "'" * (prime_count[subscript] + (1 if has_paren_label else 0)) prime_count[subscript] += 1 ref.name = "sigma_" + subscript + label self.classes.sort(key=attrgetter('first_element'))