Example #1
0
def calculateBondSymmetryNumber(molecule, atom1, atom2):
    """
    Return the symmetry number centered at `bond` in the structure.
    """
    bond = atom1.edges[atom2]
    symmetryNumber = 1
    if bond.isSingle() or bond.isDouble() or bond.isTriple():
        if atom1.equivalent(atom2):
            # An O-O bond is considered to be an "optical isomer" and so no
            # symmetry correction will be applied
            if atom1.atomType.label == 'Os' and atom2.atomType.label == 'Os' and atom1.radicalElectrons == atom2.radicalElectrons == 0:
                return symmetryNumber
            # If the molecule is diatomic, then we don't have to check the
            # ligands on the two atoms in this bond (since we know there
            # aren't any)
            elif len(molecule.vertices) == 2:
                symmetryNumber = 2
            else:
                molecule.removeBond(bond)
                structure = molecule.copy(True)
                molecule.addBond(bond)

                atom1 = structure.atoms[molecule.atoms.index(atom1)]
                atom2 = structure.atoms[molecule.atoms.index(atom2)]
                fragments = structure.split()
                
                if len(fragments) != 2: return symmetryNumber

                fragment1, fragment2 = fragments
                if atom1 in fragment1.atoms: fragment1.removeAtom(atom1)
                if atom2 in fragment1.atoms: fragment1.removeAtom(atom2)
                if atom1 in fragment2.atoms: fragment2.removeAtom(atom1)
                if atom2 in fragment2.atoms: fragment2.removeAtom(atom2)
                groups1 = fragment1.split()
                groups2 = fragment2.split()

                # Test functional groups for symmetry
                if len(groups1) == len(groups2) == 1:
                    if groups1[0].isIsomorphic(groups2[0]): symmetryNumber *= 2
                elif len(groups1) == len(groups2) == 2:
                    if groups1[0].isIsomorphic(groups2[0]) and groups1[1].isIsomorphic(groups2[1]): symmetryNumber *= 2
                    elif groups1[1].isIsomorphic(groups2[0]) and groups1[0].isIsomorphic(groups2[1]): symmetryNumber *= 2
                elif len(groups1) == len(groups2) == 3:
                    if groups1[0].isIsomorphic(groups2[0]) and groups1[1].isIsomorphic(groups2[1]) and groups1[2].isIsomorphic(groups2[2]): symmetryNumber *= 2
                    elif groups1[0].isIsomorphic(groups2[0]) and groups1[1].isIsomorphic(groups2[2]) and groups1[2].isIsomorphic(groups2[1]): symmetryNumber *= 2
                    elif groups1[0].isIsomorphic(groups2[1]) and groups1[1].isIsomorphic(groups2[2]) and groups1[2].isIsomorphic(groups2[0]): symmetryNumber *= 2
                    elif groups1[0].isIsomorphic(groups2[1]) and groups1[1].isIsomorphic(groups2[0]) and groups1[2].isIsomorphic(groups2[2]): symmetryNumber *= 2
                    elif groups1[0].isIsomorphic(groups2[2]) and groups1[1].isIsomorphic(groups2[0]) and groups1[2].isIsomorphic(groups2[1]): symmetryNumber *= 2
                    elif groups1[0].isIsomorphic(groups2[2]) and groups1[1].isIsomorphic(groups2[1]) and groups1[2].isIsomorphic(groups2[0]): symmetryNumber *= 2
                
                
    return symmetryNumber
Example #2
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 #3
0
def calculateBondSymmetryNumber(molecule, atom1, atom2):
    """
    Return the symmetry number centered at `bond` in the structure.
    """
    bond = atom1.edges[atom2]
    symmetryNumber = 1
    if atom1.equivalent(atom2):
        # An O-O bond is considered to be an "optical isomer" and so no
        # symmetry correction will be applied
        if atom1.atomType.label == 'O2s' and atom2.atomType.label == 'O2s' and atom1.radicalElectrons == atom2.radicalElectrons == 0:
            return symmetryNumber
        # If the molecule is diatomic, then we don't have to check the
        # ligands on the two atoms in this bond (since we know there
        # aren't any)
        elif len(molecule.vertices) == 2:
            symmetryNumber = 2
        else:
            molecule.removeBond(bond)
            structure = molecule.copy(True)
            molecule.addBond(bond)

            atom1 = structure.atoms[molecule.atoms.index(atom1)]
            atom2 = structure.atoms[molecule.atoms.index(atom2)]
            fragments = structure.split()

            if len(fragments) != 2: return symmetryNumber

            fragment1, fragment2 = fragments
            if atom1 in fragment1.atoms: fragment1.removeAtom(atom1)
            if atom2 in fragment1.atoms: fragment1.removeAtom(atom2)
            if atom1 in fragment2.atoms: fragment2.removeAtom(atom1)
            if atom2 in fragment2.atoms: fragment2.removeAtom(atom2)
            groups1 = fragment1.split()
            groups2 = fragment2.split()

            # Test functional groups for symmetry
            if len(groups1) == len(groups2) == 1:
                if groups1[0].isIsomorphic(groups2[0]): symmetryNumber *= 2
            elif len(groups1) == len(groups2) == 2:
                if groups1[0].isIsomorphic(
                        groups2[0]) and groups1[1].isIsomorphic(groups2[1]):
                    symmetryNumber *= 2
                elif groups1[1].isIsomorphic(
                        groups2[0]) and groups1[0].isIsomorphic(groups2[1]):
                    symmetryNumber *= 2
            elif len(groups1) == len(groups2) == 3:
                if groups1[0].isIsomorphic(
                        groups2[0]) and groups1[1].isIsomorphic(
                            groups2[1]) and groups1[2].isIsomorphic(
                                groups2[2]):
                    symmetryNumber *= 2
                elif groups1[0].isIsomorphic(
                        groups2[0]) and groups1[1].isIsomorphic(
                            groups2[2]) and groups1[2].isIsomorphic(
                                groups2[1]):
                    symmetryNumber *= 2
                elif groups1[0].isIsomorphic(
                        groups2[1]) and groups1[1].isIsomorphic(
                            groups2[2]) and groups1[2].isIsomorphic(
                                groups2[0]):
                    symmetryNumber *= 2
                elif groups1[0].isIsomorphic(
                        groups2[1]) and groups1[1].isIsomorphic(
                            groups2[0]) and groups1[2].isIsomorphic(
                                groups2[2]):
                    symmetryNumber *= 2
                elif groups1[0].isIsomorphic(
                        groups2[2]) and groups1[1].isIsomorphic(
                            groups2[0]) and groups1[2].isIsomorphic(
                                groups2[1]):
                    symmetryNumber *= 2
                elif groups1[0].isIsomorphic(
                        groups2[2]) and groups1[1].isIsomorphic(
                            groups2[1]) and groups1[2].isIsomorphic(
                                groups2[0]):
                    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