def find_spherical_axes(self): """Looks for R5, R4, R3 and R2 axes in spherical top molecules. Point group T molecules have only one unique 3-fold and one unique 2-fold axis. O molecules have one unique 4, 3 and 2-fold axes. I molecules have a unique 5-fold axis. """ rot_present = {2: False, 3: False, 4: False, 5: False} test_set = self.find_possible_equivalent_positions() for c1, c2, c3 in itertools.combinations(test_set, 3): for cc1, cc2 in itertools.combinations([c1, c2, c3], 2): if not rot_present[2]: test_axis = cc1 + cc2 if numpy.linalg.norm(test_axis) > self.tol: op = rotation(axis=test_axis, order=2) if is_valid_op(self.mol, op): logger.debug("Found axis with order {0}".format(2)) rot_present[2] = True self.symmops["C"] += [ (2, test_axis, op), ] self.nrot += 1 test_axis = numpy.cross(c2 - c1, c3 - c1) if numpy.linalg.norm(test_axis) > self.tol: for order in (3, 4, 5): if not rot_present[order]: op = rotation(axis=test_axis, order=order) if is_valid_op(self.mol, op): logger.debug( "Found axis with order {0}".format(order)) rot_present[order] = True self.symmops["C"] += [ (order, test_axis, op), ] self.nrot += 1 break if (rot_present[2] & rot_present[3] & (rot_present[4] | rot_present[5])): break return
def has_perpendicular_C2(self, axis): """Checks for R2 axes perpendicular to unique axis. For handling symmetric top molecules. """ min_set = self.find_possible_equivalent_positions(axis=axis) found = False for s1, s2 in itertools.combinations(min_set, 2): test_axis = numpy.cross(s1 - s2, axis) if numpy.linalg.norm(test_axis) > self.tol: op = rotation(axis=test_axis, order=2) if is_valid_op(self.mol, op): self.symmops["C"] += [(2, test_axis, op), ] self.nrot += 1 found = True return found
def analyze_cyclic_groups(self): """Handles cyclic group molecules.""" order, axis, op = max(self.symmops["C"], key=lambda v: v[0]) self.schoenflies = "C{0}".format(order) mirror_type = self.find_reflection_plane(axis) if mirror_type == "h": self.schoenflies += "h" elif mirror_type == "v": self.schoenflies += "v" elif mirror_type is None: rotoref = reflection(axis).dot( rotation(axis=axis, order=2 * order)) if is_valid_op(self.mol, rotoref): self.schoenflies = "S{0}".format(2 * order) self.symmops["S"] += [(order, axis, rotoref), ]
def detect_rotational_symmetry(self, axis): """Determines the rotational symmetry about supplied axis. Used only for symmetric top molecules which has possible rotational symmetry operations > 2. """ min_set = self.find_possible_equivalent_positions(axis=axis) max_sym = len(min_set) for order in range(max_sym, 0, -1): if max_sym % order != 0: continue op = rotation(axis=axis, order=order) if is_valid_op(self.mol, op): logger.debug("Found axis with order {0}".format(order)) self.symmops["C"] += [(order, axis, op), ] self.nrot += 1 return order return 1
def analyze_asymmetric_top(self): """Handles assymetric top molecules, which cannot contain rotational symmetry larger than 2. """ for axis in self.eigvecs: op = rotation(axis=axis, order=2) if is_valid_op(self.mol, op): self.symmops["C"] += [(2, axis, op), ] self.nrot += 1 if self.nrot == 0: logger.debug("No rotation symmetries detected.") self.analyze_nonrotational_groups() elif self.nrot == 3: logger.debug("Dihedral group detected.") self.analyze_dihedral_groups() else: logger.debug("Cyclic group detected.") self.analyze_cyclic_groups()
def get_symmetry_elements(mol, max_order=8, epsilon=0.1): """Return an array counting the found symmetries of the object, up to axes of order max_order. Enables fast comparison of compatibility between objects: if array1 - array2 > 0, that means object1 fits the slot2 """ if len(mol) == 1: logger.debug("Point-symmetry detected.") symmetries = numpy.array([0, 0, 0, 0, 0, 1]) return symmetries if len(mol) == 2: logger.debug("Linear connectivity detected.") # simplest, linear case symmetries = numpy.array([1, 1, 0, 1, 1, 2]) return symmetries mol.center(about=0) # ensure there is a sufficient distance between connections dist = mol.get_all_distances().mean() if dist < 10.0: alpha = 10.0 / dist mol.positions = mol.positions.dot(numpy.eye(3) * alpha) logger.debug("DIST {}".format(dist)) axes = get_potential_axes(mol) # array for the operations: # size = (1inversion+rotationorders+rotinvorders+2planes+1multiplicity) symmetries = numpy.zeros(4 + 2 * max_order) # inversion inv = -1.0 * numpy.eye(3) has_inv = is_valid_op(mol, inv) symmetries[0] += int(has_inv) # rotations: principal_order = 1 principal_axes = [] for axis in axes: for order in range(2, max_order + 1): rot = rotation(axis, order) has_rot = is_valid_op(mol, rot) symmetries[order - 1] += int(has_rot) if has_rot: logger.debug("Detected: C{order}".format(order=order)) # bookkeeping for the planes if has_rot and order > principal_order: principal_order = order principal_axes = [axis, ] elif has_rot and order == principal_order: principal_axes.append(axis) # planes for axis in axes: ref = reflection(axis) has_ref = is_valid_op(mol, ref) if has_ref: dots = [abs(axis.dot(max_axis)) for max_axis in principal_axes] if not dots: continue mindot = numpy.amin(dots) maxdot = numpy.amax(dots) if maxdot > 1.0 - epsilon: # sigma h symmetries[-2] += 1 logger.debug("Detected: sigma h") elif mindot < epsilon: # sigma vd symmetries[-3] += 1 logger.debug("Detected: sigma vd") # rotoreflections for axis in axes: ref = reflection(axis) for order in range(2, max_order + 1): rot = rotation(axis, order) rr = rot.dot(ref) has_rr = is_valid_op(mol, rr) symmetries[order + max_order - 2] += int(has_rr) if has_rr: logger.debug("Detected: S{order}".format(order=order)) # multiplicity symmetries[-1] = len([x for x in mol if x.symbol == "X"]) return numpy.array(symmetries, dtype=int)