Example #1
0
def calculateSymmetryNumber(molecule):
    """
    Return the symmetry number for the structure. The symmetry number
    includes both external and internal modes.
    """
    symmetryNumber = 1

    for atom in molecule.vertices:
        if not molecule.isAtomInCycle(atom):
            symmetryNumber *= calculateAtomSymmetryNumber(molecule, atom)

    for atom1 in molecule.vertices:
        for atom2 in atom1.edges:
            if molecule.vertices.index(atom1) < molecule.vertices.index(
                    atom2) and not molecule.isBondInCycle(atom1.edges[atom2]):
                symmetryNumber *= calculateBondSymmetryNumber(
                    molecule, atom1, atom2)

    symmetryNumber *= calculateAxisSymmetryNumber(molecule)

    if molecule.isCyclic():
        symmetryNumber *= calculateCyclicSymmetryNumber(molecule)

    return symmetryNumber
Example #2
0
def calculateSymmetryNumber(molecule):
    """
    Return the symmetry number for the structure. The symmetry number
    includes both external and internal modes.
    """
    symmetryNumber = 1

    for atom in molecule.vertices:
        if not molecule.isAtomInCycle(atom):
            symmetryNumber *= calculateAtomSymmetryNumber(molecule, atom)

    for atom1 in molecule.vertices:
        for atom2 in atom1.edges:
            if molecule.vertices.index(atom1) < molecule.vertices.index(atom2) and not molecule.isBondInCycle(
                atom1.edges[atom2]
            ):
                symmetryNumber *= calculateBondSymmetryNumber(molecule, atom1, atom2)

    symmetryNumber *= calculateAxisSymmetryNumber(molecule)

    if molecule.isCyclic():
        symmetryNumber *= calculateCyclicSymmetryNumber(molecule)

    return symmetryNumber
Example #3
0
def calculateAxisSymmetryNumber(molecule):
    """
    Get the axis symmetry number correction. The "axis" refers to a series
    of two or more cumulated double bonds (e.g. C=C=C, etc.). Corrections
    for single C=C bonds are handled in getBondSymmetryNumber().
    
    Each axis (C=C=C) has the potential to double the symmetry number.
    If an end has 0 or 1 groups (eg. =C=CJJ or =C=C-R) then it cannot 
    alter the axis symmetry and is disregarded::
    
        A=C=C=C..        A-C=C=C=C-A
        
          s=1                s=1
    
    If an end has 2 groups that are different then it breaks the symmetry 
    and the symmetry for that axis is 1, no matter what's at the other end::
    
        A\               A\         /A
          T=C=C=C=C-A      T=C=C=C=T
        B/               A/         \B
              s=1             s=1
    
    If you have one or more ends with 2 groups, and neither end breaks the 
    symmetry, then you have an axis symmetry number of 2::
    
        A\         /B      A\         
          C=C=C=C=C          C=C=C=C-B
        A/         \B      A/         
              s=2                s=2
    """

    symmetryNumber = 1

    # List all double bonds in the structure
    doubleBonds = []
    for atom1 in molecule.vertices:
        for atom2 in atom1.edges:
            if atom1.edges[atom2].isDouble() and molecule.vertices.index(atom1) < molecule.vertices.index(atom2):
                doubleBonds.append((atom1, atom2))

    # Search for adjacent double bonds
    cumulatedBonds = []
    for i, bond1 in enumerate(doubleBonds):
        atom11, atom12 = bond1
        for bond2 in doubleBonds[i + 1 :]:
            atom21, atom22 = bond2
            if atom11 is atom21 or atom11 is atom22 or atom12 is atom21 or atom12 is atom22:
                listToAddTo = None
                for cumBonds in cumulatedBonds:
                    if (atom11, atom12) in cumBonds or (atom21, atom22) in cumBonds:
                        listToAddTo = cumBonds
                if listToAddTo is not None:
                    if (atom11, atom12) not in listToAddTo:
                        listToAddTo.append((atom11, atom12))
                    if (atom21, atom22) not in listToAddTo:
                        listToAddTo.append((atom21, atom22))
                else:
                    cumulatedBonds.append([(atom11, atom12), (atom21, atom22)])

    # Also keep isolated double bonds
    for bond1 in doubleBonds:
        for bonds in cumulatedBonds:
            if bond1 in bonds:
                break
        else:
            cumulatedBonds.append([bond1])

    # For each set of adjacent double bonds, check for axis symmetry
    for bonds in cumulatedBonds:

        # Do nothing if less than two cumulated bonds
        if len(bonds) < 1:
            continue

        # Do nothing if axis is in cycle
        found = False
        for atom1, atom2 in bonds:
            if molecule.isBondInCycle(atom1.edges[atom2]):
                found = True
        if found:
            continue

        # Find terminal atoms in axis
        # Terminal atoms labelled T:  T=C=C=C=T
        axis = []
        for bond in bonds:
            axis.extend(bond)
        terminalAtoms = []
        for atom in axis:
            if axis.count(atom) == 1:
                terminalAtoms.append(atom)
        if len(terminalAtoms) != 2:
            continue

        # Remove axis from (copy of) structure
        bondlist = []
        for atom1, atom2 in bonds:
            bond = atom1.edges[atom2]
            bondlist.append(bond)
            molecule.removeBond(bond)
        structure = molecule.copy(True)
        terminalAtoms = [structure.vertices[molecule.vertices.index(atom)] for atom in terminalAtoms]
        for bond in bondlist:
            molecule.addBond(bond)

        atomsToRemove = []
        for atom in structure.vertices:
            if len(atom.edges) == 0 and atom not in terminalAtoms:  # it's not bonded to anything
                atomsToRemove.append(atom)
        for atom in atomsToRemove:
            structure.removeAtom(atom)

        # Split remaining fragments of structure
        end_fragments = structure.split()

        #
        # there can be two groups at each end     A\         /B
        #                                           T=C=C=C=T
        #                                         A/         \B

        # to start with nothing has broken symmetry about the axis
        symmetry_broken = False
        end_fragments_to_remove = []
        for fragment in end_fragments:  # a fragment is one end of the axis
            # remove the atom that was at the end of the axis and split what's left into groups
            terminalAtom = None
            for atom in terminalAtoms:
                if atom in fragment.atoms:
                    terminalAtom = atom
                    fragment.removeAtom(atom)
                    break
            else:
                continue

            groups = []
            if len(fragment.atoms) > 0:
                groups = fragment.split()

            # If end has only one group then it can't contribute to (nor break) axial symmetry
            #   Eg. this has no axis symmetry:   A-T=C=C=C=T-A
            # so we remove this end from the list of interesting end fragments
            if len(groups) == 0:
                end_fragments_to_remove.append(fragment)
                continue  # next end fragment
            elif len(groups) == 1 and terminalAtom.radicalElectrons == 0:
                if terminalAtom.atomType.label == "N3d":
                    symmetry_broken = True
                else:
                    end_fragments_to_remove.append(fragment)
                    continue  # next end fragment
            elif len(groups) == 1 and terminalAtom.radicalElectrons != 0:
                symmetry_broken = True
            elif len(groups) == 2:
                if not groups[0].isIsomorphic(groups[1]):
                    # this end has broken the symmetry of the axis
                    symmetry_broken = True

        for fragment in end_fragments_to_remove:
            end_fragments.remove(fragment)

        # If there are end fragments left that can contribute to symmetry,
        # and none of them broke it, then double the symmetry number
        # NB>> This assumes coordination number of 4 (eg. Carbon).
        #      And would be wrong if we had /B
        #                         =C=C=C=C=T-B
        #                                   \B
        #      (for some T with coordination number 5).
        if end_fragments and not symmetry_broken:
            symmetryNumber *= 2

    return symmetryNumber
Example #4
0
def calculateAxisSymmetryNumber(molecule):
    """
    Get the axis symmetry number correction. The "axis" refers to a series
    of two or more cumulated double bonds (e.g. C=C=C, etc.). Corrections
    for single C=C bonds are handled in getBondSymmetryNumber().
    
    Each axis (C=C=C) has the potential to double the symmetry number.
    If an end has 0 or 1 groups (eg. =C=CJJ or =C=C-R) then it cannot 
    alter the axis symmetry and is disregarded::
    
        A=C=C=C..        A-C=C=C=C-A
        
          s=1                s=1
    
    If an end has 2 groups that are different then it breaks the symmetry 
    and the symmetry for that axis is 1, no matter what's at the other end::
    
        A\               A\         /A
          T=C=C=C=C-A      T=C=C=C=T
        B/               A/         \B
              s=1             s=1
    
    If you have one or more ends with 2 groups, and neither end breaks the 
    symmetry, then you have an axis symmetry number of 2::
    
        A\         /B      A\         
          C=C=C=C=C          C=C=C=C-B
        A/         \B      A/         
              s=2                s=2
    """

    symmetryNumber = 1

    # List all double bonds in the structure
    doubleBonds = []
    for atom1 in molecule.vertices:
        for atom2 in atom1.edges:
            if (atom1.edges[atom2].isDouble() or atom1.edges[atom2].order > 2) \
                and molecule.vertices.index(atom1) < molecule.vertices.index(atom2):
                doubleBonds.append((atom1, atom2))

    # Search for adjacent double bonds
    cumulatedBonds = []
    for i, bond1 in enumerate(doubleBonds):
        atom11, atom12 = bond1
        for bond2 in doubleBonds[i + 1:]:
            atom21, atom22 = bond2
            if atom11 is atom21 or atom11 is atom22 or atom12 is atom21 or atom12 is atom22:
                listToAddTo = None
                for cumBonds in cumulatedBonds:
                    if (atom11, atom12) in cumBonds or (atom21,
                                                        atom22) in cumBonds:
                        listToAddTo = cumBonds
                if listToAddTo is not None:
                    if (atom11, atom12) not in listToAddTo:
                        listToAddTo.append((atom11, atom12))
                    if (atom21, atom22) not in listToAddTo:
                        listToAddTo.append((atom21, atom22))
                else:
                    cumulatedBonds.append([(atom11, atom12), (atom21, atom22)])

    # Also keep isolated double bonds
    for bond1 in doubleBonds:
        for bonds in cumulatedBonds:
            if bond1 in bonds:
                break
        else:
            cumulatedBonds.append([bond1])

    # For each set of adjacent double bonds, check for axis symmetry
    for bonds in cumulatedBonds:

        # Do nothing if less than two cumulated bonds
        if len(bonds) < 1: continue

        # Do nothing if axis is in cycle
        found = False
        for atom1, atom2 in bonds:
            if molecule.isBondInCycle(atom1.edges[atom2]): found = True
        if found: continue

        # Find terminal atoms in axis
        # Terminal atoms labelled T:  T=C=C=C=T
        axis = []
        for bond in bonds:
            axis.extend(bond)
        terminalAtoms = []
        for atom in axis:
            if axis.count(atom) == 1: terminalAtoms.append(atom)
        if len(terminalAtoms) != 2: continue

        # Remove axis from (copy of) structure
        bondlist = []
        for atom1, atom2 in bonds:
            bond = atom1.edges[atom2]
            bondlist.append(bond)
            molecule.removeBond(bond)
        structure = molecule.copy(True)
        terminalAtoms = [
            structure.vertices[molecule.vertices.index(atom)]
            for atom in terminalAtoms
        ]
        for bond in bondlist:
            molecule.addBond(bond)

        atomsToRemove = []
        for atom in structure.vertices:
            if len(
                    atom.edges
            ) == 0 and atom not in terminalAtoms:  # it's not bonded to anything
                atomsToRemove.append(atom)
        for atom in atomsToRemove:
            structure.removeAtom(atom)

        # Split remaining fragments of structure
        end_fragments = structure.split()

        #
        # there can be two groups at each end     A\         /B
        #                                           T=C=C=C=T
        #                                         A/         \B

        # to start with nothing has broken symmetry about the axis
        symmetry_broken = False
        end_fragments_to_remove = []
        for fragment in end_fragments:  # a fragment is one end of the axis
            # remove the atom that was at the end of the axis and split what's left into groups
            terminalAtom = None
            for atom in terminalAtoms:
                if atom in fragment.atoms:
                    terminalAtom = atom
                    fragment.removeAtom(atom)
                    break
            else:
                continue

            groups = []
            if len(fragment.atoms) > 0:
                groups = fragment.split()

            # If end has only one group then it can't contribute to (nor break) axial symmetry
            #   Eg. this has no axis symmetry:   A-T=C=C=C=T-A
            # so we remove this end from the list of interesting end fragments
            if len(groups) == 0:
                end_fragments_to_remove.append(fragment)
                continue  # next end fragment
            elif len(groups) == 1 and terminalAtom.radicalElectrons == 0:
                if terminalAtom.atomType.label == 'N3d':
                    symmetry_broken = True
                else:
                    end_fragments_to_remove.append(fragment)
                    continue  # next end fragment
            elif len(groups) == 1 and terminalAtom.radicalElectrons != 0:
                symmetry_broken = True
            elif len(groups) == 2:
                if not groups[0].isIsomorphic(groups[1]):
                    # this end has broken the symmetry of the axis
                    symmetry_broken = True

        for fragment in end_fragments_to_remove:
            end_fragments.remove(fragment)

        # If there are end fragments left that can contribute to symmetry,
        # and none of them broke it, then double the symmetry number
        # NB>> This assumes coordination number of 4 (eg. Carbon).
        #      And would be wrong if we had /B
        #                         =C=C=C=C=T-B
        #                                   \B
        #      (for some T with coordination number 5).
        if end_fragments and not symmetry_broken:
            symmetryNumber *= 2

    return symmetryNumber