In [ ]:
from rmgpy.molecule import Molecule
from rmgpy.molecule.resonance import *
from rmgpy.molecule import PeriodicSystem
import numpy as np
import itertools
from IPython.display import display
import pdb
In [ ]:
mol = Molecule(SMILES='[C]1=CC2=CC=C3C=CC4=CC=C5C=CC6=CC=C1C1=C2C3=C4C5=C61')
out = generateAromaticResonanceStructures(mol)
display(out[0])
out[0].kekulize()
display(out[0])
In [ ]:
def kekulize(self):
rings = self.getAllCyclesOfSize(6)
# Identify aromatic rings
aromaticRings = []
for ring in rings:
endoBonds = set()
exoBonds = set()
aromatic = True
for atom1 in ring:
for atom2, bond in atom1.bonds.iteritems():
if atom2 in ring:
if bond.order[0] != 'B':
aromatic = False
break
endoBonds.add(bond)
else:
exoBonds.add(bond)
if not aromatic:
break
if aromatic:
aromaticRings.append([ring, list(endoBonds), list(exoBonds), -1, -1])
updateKekulizationDOF(aromaticRings)
resolvedRings = []
itercount = 0
maxiter = 2 * len(aromaticRings)
while aromaticRings and itercount < maxiter:
ringData = aromaticRings.pop()
successful = kekulizeRing(*ringData)
if successful:
resolvedRings.append(ringData)
updateKekulizationDOF(aromaticRings)
else:
print 'Unsuccessful kekulization!'
aromaticRings.append(ringData)
updateKekulizationDOF(aromaticRings)
itercount += 1
try:
self.updateAtomTypes()
except:
pdb.set_trace()
def updateKekulizationDOF(aromaticRings):
for ringData in aromaticRings:
endoDOF = 6
for bond in ringData[1]:
if bond.order != 'B':
# Remove one dof for each bond that is not aromatic
endoDOF -= 1
exoDOF = 6
for bond in ringData[2]:
if bond.order != 'B':
# Remove one dof for each bond that is not aromatic
exoDOF -= 1
for atom in ringData[0]:
exoDOF -= atom.radicalElectrons
exoDOF -= atom.lonePairs
ringData[3] = endoDOF
ringData[4] = exoDOF
aromaticRings.sort(key=lambda x: x[3] + x[4], reverse=True)
def kekulizeRing(ring, endoBonds, exoBonds, endoDOF, exoDOF):
print 'Solving ring'
bondOrders = {'S': 1, 'D': 2, 'T': 3, 'B': 1.5}
bondOrdersInv = {1: 'S', 2: 'D', 3: 'T'}
valences = PeriodicSystem.valences
# Make a copy of the list
unresolved = list(endoBonds)
resolved = []
# Check if any bonds are predetermined
for bond in endoBonds:
if bond.order[0] != 'B':
# Bond has already been assigned, so mark as resolved
resolved.append(bond)
unresolved.remove(bond)
elif bond.order == 'B+':
# Bond was incremented, so it must be a double bond
bond.order = 'D'
resolved.append(bond)
unresolved.remove(bond)
elif bond.order == 'B-':
# Bond was decremented, so it must be a single bond
bond.order = 'S'
resolved.append(bond)
unresolved.remove(bond)
# Check status
if len(resolved) == len(endoBonds):
return True
itercount = 0
maxiter = 2 * len(endoBonds)
while unresolved and itercount < maxiter:
bond0 = unresolved.pop()
# Check available valence for both atoms
doublePossible = True
doubleRequired = False
localEndoDOF = 2
localExoDOF = 2
for atom in [bond0.atom1, bond0.atom2]:
occupied = 0
uncertain = 0
for bond in atom.bonds.itervalues():
if bond.order[0] != 'B':
occupied += bondOrders[bond.order]
if bond is not bond0:
if bond in endoBonds:
localEndoDOF -= 1
else:
localExoDOF -= 1
elif bond.order == 'B':
# The atom has a benzene bond, so at least one electron is occupied, but there is a second uncertain electron
occupied += 1
uncertain += 1
else:
ValueError('Unexpected bond order {0}.'.format(bond.order))
occupied += atom.radicalElectrons
localExoDOF -= atom.radicalElectrons
occupied += 2 * atom.lonePairs
localExoDOF -= atom.lonePairs
available = valences[atom.element.symbol] - occupied
if available < 0:
ValueError('Atom {0} cannot have negative available valence.'.format(atom))
elif available == 0:
doublePossible = False
break
elif available == 1 and uncertain == 1:
doubleRequired = True
break
print doublePossible, doubleRequired
if doublePossible and doubleRequired:
bond0.order = 'D'
resolved.append(bond0)
endoDOF -= 1
elif doublePossible and not doubleRequired:
if (endoDOF == 6 and exoDOF == 0) or (endoDOF == 6 and localExoDOF == 0) or (localEndoDOF == 1 and localExoDOF == 2) or endoDOF == 1:
# Go ahead an assume this bond is double
bond0.order = 'D'
resolved.append(bond0)
endoDOF -= 1
else:
# Come back to this bond later
print 'Postponing resolution of this bond with endoDOF={0} and exoDOF={1}...'.format(endoDOF, exoDOF)
unresolved.insert(0, bond0)
else:
bond0.order = 'S'
resolved.append(bond0)
endoDOF -= 1
itercount += 1
if unresolved:
print 'Unable to solve ring...'
return False
print '-'.join([bond.order for bond in endoBonds])
return True
In [ ]: