def calculateCyclicSymmetryNumber(molecule): """ Get the symmetry number correction for cyclic regions of a molecule. For complicated fused rings the smallest set of smallest rings is used. """ symmetryNumber = 1 # for polycyclics, We should be getting the largest ring, not the smallest singleRings, polycyclicRings = molecule.getDisparateRings() for ring in polycyclicRings: singleRings.append(molecule.getLargestRing(ring[0])) # Get symmetry number for each ring in structure & multiply for ring in singleRings: ring = molecule._sortCyclicVertices(ring) size = len(ring) # look for twisting rotation # go through each possible number of symmetrical sets for num_sections in range(size,0,-1): # only go through if it can give symmetry (only factors of size) if size % num_sections == 0: # only check the minimum number of sections necessary num_rotations = size / num_sections # check rotation around the ring all_the_same = True starting_index = 0 while all_the_same and starting_index < size / 2: for atom_index in range(num_rotations,size,num_rotations): if not _indistinguishable(ring[starting_index],ring[(starting_index + atom_index) % size]): all_the_same = False break starting_index += 1 if all_the_same: symmetryNumber *= num_sections break # look for flipping rotation. if size % 2 == 0: # even length only has to go through half flipping_atom_indexes = range(int(size/2)) else: flipping_atom_indexes = range(size) for flipping_atom_index in flipping_atom_indexes: # check for flipping with across an axis containing atoms all_the_same = True min_index = flipping_atom_index + 1 max_index = flipping_atom_index + size - 1 while min_index <= max_index: # ensure the two atoms are different. use mod size to loop to the start # of the list when index out of bounds if not _indistinguishable(ring[min_index % size], ring[max_index % size]): all_the_same = False break min_index += 1 max_index += -1 # check to make sure that the groups are identical on centers of flipping if all_the_same: ringed_atom_ids = [id(_atom) for _atom in ring] if size % 2 == 0: # look at two atoms atom1 = ring[flipping_atom_index] atom2 = ring[flipping_atom_index + size/2] non_ring_bonded_atoms = [bonded_atom for bonded_atom in atom1.bonds.keys() + atom2.bonds.keys() if id(bonded_atom) not in ringed_atom_ids] if len(non_ring_bonded_atoms) < 3: pass # all_the_same still true elif len(non_ring_bonded_atoms) == 3: # at least one of these much be a match for flipping to happen identical = _indistinguishable(non_ring_bonded_atoms[0],non_ring_bonded_atoms[1]) identical2 = _indistinguishable(non_ring_bonded_atoms[0],non_ring_bonded_atoms[2]) identical3 = _indistinguishable(non_ring_bonded_atoms[1],non_ring_bonded_atoms[2]) if not (identical or identical2 or identical3): all_the_same = False elif len(non_ring_bonded_atoms) == 4: same_sides = _indistinguishable(non_ring_bonded_atoms[0],non_ring_bonded_atoms[1]) and \ _indistinguishable(non_ring_bonded_atoms[2],non_ring_bonded_atoms[3]) if not same_sides: atom0_matching = _indistinguishable(non_ring_bonded_atoms[0],non_ring_bonded_atoms[2]) or\ _indistinguishable(non_ring_bonded_atoms[0],non_ring_bonded_atoms[3]) atom1_matching = _indistinguishable(non_ring_bonded_atoms[1],non_ring_bonded_atoms[2]) or\ _indistinguishable(non_ring_bonded_atoms[1],non_ring_bonded_atoms[3]) if not (atom0_matching and atom1_matching): all_the_same = False else: atom = ring[flipping_atom_index] non_ring_bonded_atoms = [bonded_atom for bonded_atom in atom.bonds.keys() if id(bonded_atom) not in ringed_atom_ids] if len(non_ring_bonded_atoms) < 2: pass # all_the_same still true elif len(non_ring_bonded_atoms) == 2: identical = _indistinguishable(non_ring_bonded_atoms[0],non_ring_bonded_atoms[1]) if not identical: # flipping a tetrahedral will not work all_the_same = False else: # having 5+ bonnds is not modeled here pass # for even rings, check for flipping accross bonds too if not all_the_same and size % 2 == 0: all_the_same = True min_index = flipping_atom_index max_index = flipping_atom_index + size - 1 while min_index < max_index: # ensure the two atoms are different. use mod size to loop to the start # of the list when index out of bounds if not _indistinguishable(ring[min_index % size], ring[max_index % size]): all_the_same = False break min_index += 1 max_index += -1 if all_the_same: symmetryNumber *= 2 break return symmetryNumber
def calculateCyclicSymmetryNumber(molecule): """ Get the symmetry number correction for cyclic regions of a molecule. For complicated fused rings the smallest set of smallest rings is used. """ from rmgpy.molecule.vf2 import VF2 # setup isomorphism checker vf2 = VF2(molecule, molecule) symmetryNumber = 1 # for polycyclics, We should be getting the largest ring, not the smallest singleRings, polycyclicRings = molecule.getDisparateRings() for ring in polycyclicRings: singleRings.append(molecule.getLargestRing(ring[0])) # Get symmetry number for each ring in structure & multiply for ring in singleRings: ring = molecule._sortCyclicVertices(ring) size = len(ring) # look for twisting rotation # go through each possible number of symmetrical sets for num_sections in range(size, 0, -1): # only go through if it can give symmetry (only factors of size) if size % num_sections == 0: # only check the minimum number of sections necessary num_rotations = size / num_sections # check rotation around the ring all_the_same = True starting_index = 0 while all_the_same and starting_index < size / 2: for atom_index in range(num_rotations, size, num_rotations): if not vf2.feasible( ring[starting_index], ring[(starting_index + atom_index) % size]): all_the_same = False break starting_index += 1 if all_the_same: symmetryNumber *= num_sections break # look for flipping rotation. if size % 2 == 0: # even length only has to go through half flipping_atom_indexes = range(int(size / 2)) else: flipping_atom_indexes = range(size) for flipping_atom_index in flipping_atom_indexes: # check for flipping with across an axis containing atoms all_the_same = True min_index = flipping_atom_index + 1 max_index = flipping_atom_index + size - 1 while min_index <= max_index: # ensure the two atoms are different. use mod size to loop to the start # of the list when index out of bounds if not vf2.feasible(ring[min_index % size], ring[max_index % size]): all_the_same = False break min_index += 1 max_index += -1 # check to make sure that the groups are identical on centers of flipping if all_the_same: ringed_atom_ids = [id(_atom) for _atom in ring] if size % 2 == 0: # look at two atoms atom1 = ring[flipping_atom_index] atom2 = ring[flipping_atom_index + size / 2] non_ring_bonded_atoms = [ bonded_atom for bonded_atom in atom1.bonds.keys() + atom2.bonds.keys() if id(bonded_atom) not in ringed_atom_ids ] if len(non_ring_bonded_atoms) < 3: pass # all_the_same still true elif len(non_ring_bonded_atoms) == 3: # at least one of these much be a match for flipping to happen identical = vf2.feasible(non_ring_bonded_atoms[0], non_ring_bonded_atoms[1]) identical2 = vf2.feasible(non_ring_bonded_atoms[0], non_ring_bonded_atoms[2]) identical3 = vf2.feasible(non_ring_bonded_atoms[1], non_ring_bonded_atoms[2]) if not (identical or identical2 or identical3): all_the_same = False elif len(non_ring_bonded_atoms) == 4: same_sides = vf2.feasible(non_ring_bonded_atoms[0],non_ring_bonded_atoms[1]) and \ vf2.feasible(non_ring_bonded_atoms[2],non_ring_bonded_atoms[3]) if not same_sides: atom0_matching = vf2.feasible(non_ring_bonded_atoms[0],non_ring_bonded_atoms[2]) or\ vf2.feasible(non_ring_bonded_atoms[0],non_ring_bonded_atoms[3]) atom1_matching = vf2.feasible(non_ring_bonded_atoms[1],non_ring_bonded_atoms[2]) or\ vf2.feasible(non_ring_bonded_atoms[1],non_ring_bonded_atoms[3]) if not (atom0_matching and atom1_matching): all_the_same = False else: atom = ring[flipping_atom_index] non_ring_bonded_atoms = [ bonded_atom for bonded_atom in atom.bonds.keys() if id(bonded_atom) not in ringed_atom_ids ] if len(non_ring_bonded_atoms) < 2: pass # all_the_same still true elif len(non_ring_bonded_atoms) == 2: identical = vf2.feasible(non_ring_bonded_atoms[0], non_ring_bonded_atoms[1]) if not identical: # flipping a tetrahedral will not work all_the_same = False else: # having 5+ bonnds is not modeled here pass # for even rings, check for flipping accross bonds too if not all_the_same and size % 2 == 0: all_the_same = True min_index = flipping_atom_index max_index = flipping_atom_index + size - 1 while min_index < max_index: # ensure the two atoms are different. use mod size to loop to the start # of the list when index out of bounds if not vf2.feasible(ring[min_index % size], ring[max_index % size]): all_the_same = False break min_index += 1 max_index += -1 if all_the_same: symmetryNumber *= 2 break return symmetryNumber
def calculateCyclicSymmetryNumber(molecule): """ Get the symmetry number correction for cyclic regions of a molecule. For complicated fused rings the smallest set of smallest rings is used. """ from rmgpy.molecule.vf2 import VF2 # setup isomorphism checker vf2 = VF2(molecule, molecule) symmetryNumber = 1 # for polycyclics, We should be getting the largest ring, not the smallest singleRings, polycyclicRings = molecule.getDisparateRings() for ring in polycyclicRings: singleRings.append(molecule.getLargestRing(ring[0])) # Get symmetry number for each ring in structure & multiply for ring in singleRings: ring = molecule._sortCyclicVertices(ring) size = len(ring) # look for twisting rotation # go through each possible number of symmetrical sets for num_sections in range(size, 0, -1): # only go through if it can give symmetry (only factors of size) if size % num_sections == 0: # only check the minimum number of sections necessary num_rotations = size / num_sections # check rotation around the ring all_the_same = True starting_index = 0 while all_the_same and starting_index < size / 2: for atom_index in range(num_rotations, size, num_rotations): if not vf2.feasible( ring[starting_index], ring[(starting_index + atom_index) % size]): all_the_same = False break starting_index += 1 if all_the_same: symmetryNumber *= num_sections break # look for flipping rotation. if size % 2 == 0: # even length only has to go through half flipping_atom_indexes = range(int(size / 2)) else: flipping_atom_indexes = range(size) for flipping_atom_index in flipping_atom_indexes: # check for flipping with across an axis containing atoms all_the_same = True min_index = flipping_atom_index + 1 max_index = flipping_atom_index + size - 1 while min_index <= max_index: # ensure the two atoms are different. use mod size to loop to the start # of the list when index out of bounds if not vf2.feasible(ring[min_index % size], ring[max_index % size]): all_the_same = False break min_index += 1 max_index += -1 # for even rings, check for flipping accross bonds too if not all_the_same and size % 2 == 0: all_the_same = True min_index = flipping_atom_index max_index = flipping_atom_index + size - 1 while min_index < max_index: # ensure the two atoms are different. use mod size to loop to the start # of the list when index out of bounds if not vf2.feasible(ring[min_index % size], ring[max_index % size]): all_the_same = False break min_index += 1 max_index += -1 if all_the_same: symmetryNumber *= 2 break return symmetryNumber