def calculateAtomSymmetryNumber(molecule, atom): """ Return the symmetry number centered at `atom` in the structure. The `atom` of interest must not be in a cycle. """ symmetryNumber = 1 single = 0 double = 0 triple = 0 benzene = 0 numNeighbors = 0 for bond in atom.edges.values(): if bond.isSingle(): single += 1 elif bond.isDouble(): double += 1 elif bond.isTriple(): triple += 1 elif bond.isBenzene(): benzene += 1 numNeighbors += 1 # If atom has zero or one neighbors, the symmetry number is 1 if numNeighbors < 2: return symmetryNumber # Create temporary structures for each functional group attached to atom molecule0 = molecule molecule = molecule0.copy(True) atom = molecule.vertices[molecule0.vertices.index(atom)] molecule.removeAtom(atom) groups = molecule.split() # Determine equivalence of functional groups around atom groupIsomorphism = dict([(group, dict()) for group in groups]) for group1 in groups: for group2 in groups: if group1 is not group2 and group2 not in groupIsomorphism[group1]: groupIsomorphism[group1][group2] = group1.isIsomorphic(group2) groupIsomorphism[group2][group1] = groupIsomorphism[group1][group2] elif group1 is group2: groupIsomorphism[group1][group1] = True count = [sum([int(groupIsomorphism[group1][group2]) for group2 in groups]) for group1 in groups] for i in range(count.count(2) / 2): count.remove(2) for i in range(count.count(3) / 3): count.remove(3) count.remove(3) for i in range(count.count(4) / 4): count.remove(4) count.remove(4) count.remove(4) count.sort() count.reverse() if atom.radicalElectrons == 0: if single == 4: # Four single bonds if count == [4]: symmetryNumber *= 12 elif count == [3, 1]: symmetryNumber *= 3 elif count == [2, 2]: symmetryNumber *= 2 elif count == [2, 1, 1]: symmetryNumber *= 1 elif count == [1, 1, 1, 1]: symmetryNumber *= 1 elif single == 3: # Three single bonds if count == [3]: symmetryNumber *= 3 elif count == [2, 1]: symmetryNumber *= 1 elif count == [1, 1, 1]: symmetryNumber *= 1 elif single == 2: # Two single bonds if count == [2]: symmetryNumber *= 2 elif double == 2: # Two double bonds if count == [2]: symmetryNumber *= 2 elif atom.radicalElectrons == 1: if single == 3: # Three single bonds if count == [3]: symmetryNumber *= 6 elif count == [2, 1]: symmetryNumber *= 2 elif count == [1, 1, 1]: symmetryNumber *= 1 elif atom.radicalElectrons == 2: if single == 2: # Two single bonds if count == [2]: symmetryNumber *= 2 if atom.isNitrogen(): for groupN in groups: if groupN.toSMILES() == "[N+](=O)[O-]": symmetryNumber *= 2 return symmetryNumber
def calculateAtomSymmetryNumber(molecule, atom): """ Return the symmetry number centered at `atom` in the structure. The `atom` of interest must not be in a cycle. """ symmetryNumber = 1 single = 0 double = 0 triple = 0 benzene = 0 numNeighbors = 0 for bond in atom.edges.values(): if bond.isSingle(): single += 1 elif bond.isDouble(): double += 1 elif bond.isTriple(): triple += 1 elif bond.isBenzene(): benzene += 1 numNeighbors += 1 # If atom has zero or one neighbors, the symmetry number is 1 if numNeighbors < 2: return symmetryNumber # Create temporary structures for each functional group attached to atom molecule0 = molecule molecule = molecule0.copy(True) atom = molecule.vertices[molecule0.vertices.index(atom)] molecule.removeAtom(atom) groups = molecule.split() # Determine equivalence of functional groups around atom groupIsomorphism = dict([(group, dict()) for group in groups]) for group1 in groups: for group2 in groups: if group1 is not group2 and group2 not in groupIsomorphism[group1]: groupIsomorphism[group1][group2] = group1.isIsomorphic(group2) groupIsomorphism[group2][group1] = groupIsomorphism[group1][ group2] elif group1 is group2: groupIsomorphism[group1][group1] = True count = [ sum([int(groupIsomorphism[group1][group2]) for group2 in groups]) for group1 in groups ] for i in range(count.count(2) / 2): count.remove(2) for i in range(count.count(3) / 3): count.remove(3) count.remove(3) for i in range(count.count(4) / 4): count.remove(4) count.remove(4) count.remove(4) count.sort() count.reverse() if atom.radicalElectrons == 0: if single == 4: # Four single bonds if count == [4]: symmetryNumber *= 12 elif count == [3, 1]: symmetryNumber *= 3 elif count == [2, 2]: symmetryNumber *= 2 elif count == [2, 1, 1]: symmetryNumber *= 1 elif count == [1, 1, 1, 1]: symmetryNumber *= 0.5 # found chirality elif single == 3: # Three single bonds if count == [3]: symmetryNumber *= 3 elif count == [2, 1]: symmetryNumber *= 1 elif count == [1, 1, 1]: symmetryNumber *= 1 elif single == 2: # Two single bonds if count == [2]: symmetryNumber *= 2 # for resonance hybrids elif single == 1: if count == [2, 1]: symmetryNumber *= 2 elif double == 2: # Two double bonds if count == [2]: symmetryNumber *= 2 # for nitrogen resonance hybrids elif single == 0: if count == [2]: symmetryNumber *= 2 elif atom.radicalElectrons == 1: if single == 3: # Three single bonds if count == [3]: symmetryNumber *= 6 elif count == [2, 1]: symmetryNumber *= 2 elif count == [1, 1, 1]: symmetryNumber *= 1 elif atom.radicalElectrons == 2: if single == 2: # Two single bonds if count == [2]: symmetryNumber *= 2 return symmetryNumber