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
示例#2
0
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
示例#3
0
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