Skip to content

Commit

Permalink
Merge pull request #1453 from ReactionMechanismGenerator/isotope_tran…
Browse files Browse the repository at this point in the history
…slation

Improved isotope support in converter and translator modules
  • Loading branch information
goldmanm authored Aug 19, 2018
2 parents 40a1649 + 57a7738 commit 1f1f4cf
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 3 deletions.
10 changes: 8 additions & 2 deletions rmgpy/molecule/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ def toRDKitMol(mol, removeHs=True, returnMapping=False, sanitize=True):
rdkitmol = Chem.rdchem.EditableMol(Chem.rdchem.Mol())
for index, atom in enumerate(mol.vertices):
rdAtom = Chem.rdchem.Atom(atom.element.symbol)
if atom.element.isotope != -1:
rdAtom.SetIsotope(atom.element.isotope)
rdAtom.SetNumRadicalElectrons(atom.radicalElectrons)
rdAtom.SetFormalCharge(atom.charge)
if atom.element.symbol == 'C' and atom.lonePairs == 1 and mol.multiplicity == 1: rdAtom.SetNumRadicalElectrons(2)
Expand Down Expand Up @@ -131,7 +133,8 @@ def fromRDKitMol(mol, rdkitmol):

# Use atomic number as key for element
number = rdkitatom.GetAtomicNum()
element = elements.getElement(number)
isotope = rdkitatom.GetIsotope()
element = elements.getElement(number, isotope or -1)

# Process charge
charge = rdkitatom.GetFormalCharge()
Expand Down Expand Up @@ -214,6 +217,8 @@ def toOBMol(mol, returnMapping=False):
for atom in atoms:
a = obmol.NewAtom()
a.SetAtomicNum(atom.number)
if atom.element.isotope != -1:
a.SetIsotope(atom.element.isotope)
a.SetFormalCharge(atom.charge)
obAtomIds[atom] = a.GetId()
orders = {1: 1, 2: 2, 3: 3, 1.5: 5}
Expand Down Expand Up @@ -255,7 +260,8 @@ def fromOBMol(mol, obmol):
for obatom in openbabel.OBMolAtomIter(obmol):
# Use atomic number as key for element
number = obatom.GetAtomicNum()
element = elements.getElement(number)
isotope = obatom.GetIsotope()
element = elements.getElement(number, isotope or -1)
# Process charge
charge = obatom.GetFormalCharge()
obatom_multiplicity = obatom.GetSpinMultiplicity()
Expand Down
37 changes: 36 additions & 1 deletion rmgpy/molecule/converterTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
import unittest

from rmgpy.exceptions import AtomTypeError
from rmgpy.molecule.converter import debugRDKitMol, toRDKitMol, fromRDKitMol
from rmgpy.molecule.converter import debugRDKitMol, toRDKitMol, fromRDKitMol, toOBMol, fromOBMol
from rmgpy.molecule.molecule import Molecule


Expand Down Expand Up @@ -123,3 +123,38 @@ def test_atom_mapping_2(self):
rdkitmol.GetBondBetweenAtoms(rdAtomIndices[at1], rdAtomIndices[at2])
except RuntimeError:
self.fail("RDKit failed in finding the bond in the original atom!")

class ConverterTest(unittest.TestCase):

def setUp(self):
"""Function run before each test in this class."""
self.test_mols = [
Molecule().fromSMILES('C'),
Molecule().fromSMILES('O'),
Molecule().fromSMILES('N'),
Molecule().fromSMILES('S'),
Molecule().fromSMILES('[CH2]C'),
Molecule().fromSMILES('[CH]C'),
Molecule().fromSMILES('C=CC=C'),
Molecule().fromSMILES('C#C[CH2]'),
Molecule().fromSMILES('c1ccccc1'),
Molecule().fromSMILES('[13CH3]C')
]

def test_rdkit_round_trip(self):
"""Test conversion to and from RDKitMol"""
for mol in self.test_mols:
rdkit_mol = toRDKitMol(mol)
new_mol = fromRDKitMol(Molecule(), rdkit_mol)

self.assertTrue(mol.isIsomorphic(new_mol))
self.assertEqual(mol.get_element_count(), new_mol.get_element_count())

def test_ob_round_trip(self):
"""Test conversion to and from OBMol"""
for mol in self.test_mols:
ob_mol = toOBMol(mol)
new_mol = fromOBMol(Molecule(), ob_mol)

self.assertTrue(mol.isIsomorphic(new_mol))
self.assertEqual(mol.get_element_count(), new_mol.get_element_count())
30 changes: 30 additions & 0 deletions rmgpy/molecule/translatorTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,22 @@ def test_disconnected_molecule(self):

self.assertEqual(mol.toInChI(), inchi)

def test_isotopic_molecule_1(self):
"""Test that we can generate an InChI for an isotopic molecule."""
mol = Molecule().fromSMILES('[13CH4]')

inchi = 'InChI=1S/CH4/h1H4/i1+1'

self.assertEqual(mol.toInChI(), inchi)

def test_isotopic_molecule_2(self):
"""Test that we can generate an InChI for an isotopic molecule."""
mol = Molecule().fromSMILES('[13CH3]C')

inchi = 'InChI=1S/C2H6/c1-2/h1-2H3/i1+1'

self.assertEqual(mol.toInChI(), inchi)


class SMILESGenerationTest(unittest.TestCase):
def compare(self, adjlist, smiles):
Expand Down Expand Up @@ -1372,3 +1388,17 @@ def test_NO(self):
inchi = 'InChI=1S/NO/c1-2'
u_indices = [1]
self.compare(inchi, u_indices)

def test_isotopic_molecule_1(self):
"""Test that we can parse an InChI for an isotopic molecule."""
mol = Molecule().fromInChI('InChI=1S/CH4/h1H4/i1+1')

self.assertTrue(len(mol.atoms), 4)
self.assertEqual([atom.element.isotope for atom in mol.atoms].count(13), 1)

def test_isotopic_molecule_2(self):
"""Test that we can parse an InChI for an isotopic molecule."""
mol = Molecule().fromInChI('InChI=1S/C2H6/c1-2/h1-2H3/i1+1')

self.assertTrue(len(mol.atoms), 6)
self.assertEqual([atom.element.isotope for atom in mol.atoms].count(13), 1)

0 comments on commit 1f1f4cf

Please sign in to comment.