From db7edd52e0aba8ecae80d6c2a5036d286535f9d4 Mon Sep 17 00:00:00 2001 From: Max Liu Date: Thu, 4 Apr 2019 16:23:42 -0400 Subject: [PATCH] Enable Species instantiation by SMILES or InChI argument Add SMILES property and unit tests --- rmgpy/species.pxd | 1 + rmgpy/species.py | 35 ++++++++++++++---- rmgpy/speciesTest.py | 87 ++++++++++++++++++-------------------------- 3 files changed, 65 insertions(+), 58 deletions(-) diff --git a/rmgpy/species.pxd b/rmgpy/species.pxd index b15e3a34fb..bc0a10d6e3 100644 --- a/rmgpy/species.pxd +++ b/rmgpy/species.pxd @@ -55,6 +55,7 @@ cdef class Species: cdef public bint explicitlyAllowed cdef str _fingerprint cdef str _inchi + cdef str _smiles cpdef generate_resonance_structures(self, bint keep_isomorphic=?, bint filter_structures=?) diff --git a/rmgpy/species.py b/rmgpy/species.py index 014c46051c..27f4c97ede 100644 --- a/rmgpy/species.py +++ b/rmgpy/species.py @@ -81,16 +81,16 @@ class Species(object): always considered regardless of this variable `props` A generic 'properties' dictionary to store user-defined flags `aug_inchi` Unique augmented inchi - `isSolvent` Boolean describing whether this species is the solvent + `symmetryNumber` Estimated symmetry number of the species, using the resonance hybrid `creationIteration` Iteration which the species is created within the reaction mechanism generation algorithm + `explicitlyAllowed` Flag to exempt species from forbidden structure checks ======================= ==================================================== """ - def __init__(self, index=-1, label='', thermo=None, conformer=None, - molecule=None, transportData=None, molecularWeight=None, - energyTransferModel=None, reactive=True, props=None, aug_inchi=None, - symmetryNumber = -1, creationIteration = 0, explicitlyAllowed=False): + def __init__(self, index=-1, label='', thermo=None, conformer=None, molecule=None, transportData=None, + molecularWeight=None, energyTransferModel=None, reactive=True, props=None, SMILES='', InChI='', + aug_inchi=None, symmetryNumber = -1, creationIteration = 0, explicitlyAllowed=False): self.index = index self.label = label self.thermo = thermo @@ -108,14 +108,23 @@ def __init__(self, index=-1, label='', thermo=None, conformer=None, self.explicitlyAllowed = explicitlyAllowed self._fingerprint = None self._inchi = None + self._smiles = None + + if InChI and SMILES: + logging.warning('Both InChI and SMILES provided for Species instantiation, using InChI and ignoring SMILES.') + if InChI: + self.molecule = [Molecule(InChI=InChI)] + self._inchi = InChI + elif SMILES: + self.molecule = [Molecule(SMILES=SMILES)] + self._smiles = SMILES + # Check multiplicity of each molecule is the same if molecule is not None and len(molecule)>1: mult = molecule[0].multiplicity for m in molecule[1:]: if mult != m.multiplicity: raise SpeciesError('Multiplicities of molecules in species {species} do not match.'.format(species=label)) - - def __repr__(self): """ @@ -172,6 +181,18 @@ def InChI(self): self._inchi = self.molecule[0].InChI return self._inchi + @property + def SMILES(self): + """ + SMILES string representation of this species. Read-only. + + Note that SMILES representations for different resonance structures of the same species may be different. + """ + if self._smiles is None: + if self.molecule: + self._smiles = self.molecule[0].SMILES + return self._smiles + @property def multiplicity(self): """Fingerprint of this species, taken from molecule attribute. Read-only.""" diff --git a/rmgpy/speciesTest.py b/rmgpy/speciesTest.py index ec8d507e7d..4a10930921 100644 --- a/rmgpy/speciesTest.py +++ b/rmgpy/speciesTest.py @@ -77,7 +77,23 @@ def setUp(self): molecularWeight=(28.03,'amu'), reactive=True, ) - + + self.species2 = Species().fromAdjacencyList( + """ + 1 C u0 p0 c0 {2,D} {6,S} {7,S} + 2 C u0 p0 c0 {1,D} {3,S} {8,S} + 3 C u0 p0 c0 {2,S} {4,D} {9,S} + 4 C u0 p0 c0 {3,D} {5,S} {10,S} + 5 C u0 p0 c0 {4,S} {6,D} {11,S} + 6 C u0 p0 c0 {1,S} {5,D} {12,S} + 7 H u0 p0 c0 {1,S} + 8 H u0 p0 c0 {2,S} + 9 H u0 p0 c0 {3,S} + 10 H u0 p0 c0 {4,S} + 11 H u0 p0 c0 {5,S} + 12 H u0 p0 c0 {6,S} + """) + def testPickle(self): """ Test that a Species object can be pickled and unpickled. @@ -324,63 +340,32 @@ def testGetTransportData(self): def test_fingerprint_property(self): """Test that the fingerprint property works""" - spc = Species().fromAdjacencyList( - """ - 1 C u0 p0 c0 {2,D} {6,S} {7,S} - 2 C u0 p0 c0 {1,D} {3,S} {8,S} - 3 C u0 p0 c0 {2,S} {4,D} {9,S} - 4 C u0 p0 c0 {3,D} {5,S} {10,S} - 5 C u0 p0 c0 {4,S} {6,D} {11,S} - 6 C u0 p0 c0 {1,S} {5,D} {12,S} - 7 H u0 p0 c0 {1,S} - 8 H u0 p0 c0 {2,S} - 9 H u0 p0 c0 {3,S} - 10 H u0 p0 c0 {4,S} - 11 H u0 p0 c0 {5,S} - 12 H u0 p0 c0 {6,S} - """) - - self.assertEqual(spc.fingerprint, 'C6H6') + self.assertEqual(self.species2.fingerprint, 'C6H6') def test_inchi_property(self): """Test that the InChI property works""" - spc = Species().fromAdjacencyList( - """ - 1 C u0 p0 c0 {2,D} {6,S} {7,S} - 2 C u0 p0 c0 {1,D} {3,S} {8,S} - 3 C u0 p0 c0 {2,S} {4,D} {9,S} - 4 C u0 p0 c0 {3,D} {5,S} {10,S} - 5 C u0 p0 c0 {4,S} {6,D} {11,S} - 6 C u0 p0 c0 {1,S} {5,D} {12,S} - 7 H u0 p0 c0 {1,S} - 8 H u0 p0 c0 {2,S} - 9 H u0 p0 c0 {3,S} - 10 H u0 p0 c0 {4,S} - 11 H u0 p0 c0 {5,S} - 12 H u0 p0 c0 {6,S} - """) - - self.assertEqual(spc.InChI, 'InChI=1S/C6H6/c1-2-4-6-5-3-1/h1-6H') + self.assertEqual(self.species2.InChI, 'InChI=1S/C6H6/c1-2-4-6-5-3-1/h1-6H') def test_multiplicity_property(self): """Test that the fingerprint property works""" - spc = Species().fromAdjacencyList( - """ - 1 C u0 p0 c0 {2,D} {6,S} {7,S} - 2 C u0 p0 c0 {1,D} {3,S} {8,S} - 3 C u0 p0 c0 {2,S} {4,D} {9,S} - 4 C u0 p0 c0 {3,D} {5,S} {10,S} - 5 C u0 p0 c0 {4,S} {6,D} {11,S} - 6 C u0 p0 c0 {1,S} {5,D} {12,S} - 7 H u0 p0 c0 {1,S} - 8 H u0 p0 c0 {2,S} - 9 H u0 p0 c0 {3,S} - 10 H u0 p0 c0 {4,S} - 11 H u0 p0 c0 {5,S} - 12 H u0 p0 c0 {6,S} - """) + self.assertEqual(self.species2.multiplicity, 1) + + def test_smiles_property(self): + """Test that the InChI property works""" + self.assertEqual(self.species2.SMILES, 'C1=CC=CC=C1') + + def test_inchi_instantiation(self): + """Test that we can create a species using the InChI argument""" + test = Species(InChI='InChI=1S/C6H6/c1-2-4-6-5-3-1/h1-6H') + + self.assertTrue(test.isIsomorphic(self.species2)) + + def test_smiles_instantiation(self): + """Test that we can create a species using the SMILES argument""" + test = Species(SMILES='C1=CC=CC=C1') + + self.assertTrue(test.isIsomorphic(self.species2)) - self.assertEqual(spc.multiplicity, 1) ################################################################################