Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow cosolvents to be defined in a solvent library #1558

Merged
merged 3 commits into from
Apr 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 19 additions & 20 deletions rmgpy/data/solvation.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,28 +277,26 @@ def loadEntry(self,
shortDesc='',
longDesc='',
):
spc = molecule
if molecule is not None:
try:
spc = Species().fromSMILES(molecule)
except:
logging.debug("Solvent '{0}' does not have a valid SMILES '{1}'" .format(label, molecule))
try:
spc = Species().fromAdjacencyList(molecule)
except:
logging.error("Can't understand '{0}' in solute library '{1}'".format(molecule, self.name))
raise
spc.generate_resonance_structures()
if not isinstance(molecule, list):
molecule = [molecule]
spc_list = []
for mol in molecule:
spc0 = Species(label=label)
spc0.set_structure(mol)
spc_list.append(spc0)
else:
spc_list = None

self.entries[label] = Entry(
index = index,
label = label,
item = spc,
data = solvent,
reference = reference,
referenceType = referenceType,
shortDesc = shortDesc,
longDesc = longDesc.strip(),
index=index,
label=label,
item=spc_list,
data=solvent,
reference=reference,
referenceType=referenceType,
shortDesc=shortDesc,
longDesc=longDesc.strip(),
)

def load(self, path):
Expand All @@ -324,7 +322,8 @@ def getSolventStructure(self, label):
Get a solvent's molecular structure as SMILES or adjacency list from its name
"""
return self.entries[label].item



class SoluteLibrary(Database):
"""
A class for working with a RMG solute library. Not currently used.
Expand Down
22 changes: 15 additions & 7 deletions rmgpy/data/solvationTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def testSoluteDataGenerationAmmonia(self):
self.assertTrue(soluteData is not None)

def testSoluteDataGenerationAmide(self):
"Test that we can obtain solute parameters via group additivity for an amide"
"Test that we can obtain solute parameters via group additivity for an amide"
molecule=Molecule().fromAdjacencyList(
"""
1 N u0 p1 {2,S} {3,S} {4,S}
Expand All @@ -171,7 +171,7 @@ def testSoluteDataGenerationAmide(self):
self.assertTrue(soluteData is not None)

def testSoluteDataGenerationCO(self):
"Test that we can obtain solute parameters via group additivity for CO."
"Test that we can obtain solute parameters via group additivity for CO."
molecule=Molecule().fromAdjacencyList(
"""
1 C u0 p1 c-1 {2,T}
Expand Down Expand Up @@ -254,7 +254,7 @@ def testInitialSpecies(self):
self.assertTrue(rmg.initialSpecies[0].isSolvent)

def testSolventMolecule(self):
" Test we can give a proper value for the solvent molecular structure when different solvent databases are given "
"""Test that we can assign a proper solvent molecular structure when different formats are given"""

# solventlibrary.entries['solvent_label'].item should be the instance of Species with the solvent's molecular structure
# if the solvent database contains the solvent SMILES or adjacency list. If not, then item is None
Expand All @@ -267,8 +267,8 @@ def testSolventMolecule(self):
# Case 2: When the solventDatabase contains the correct solvent SMILES, the item attribute is the instance of
# Species with the correct solvent molecular structure
solventlibrary.loadEntry(index=2, label='octane', solvent=None, molecule='CCCCCCCC')
solventSpecies = Species().fromSMILES('C(CCCCC)CC')
self.assertTrue(solventSpecies.isIsomorphic(solventlibrary.entries['octane'].item))
solvent_species = Species().fromSMILES('C(CCCCC)CC')
self.assertTrue(solvent_species.isIsomorphic(solventlibrary.entries['octane'].item[0]))

# Case 3: When the solventDatabase contains the correct solvent adjacency list, the item attribute is the instance of
# the species with the correct solvent molecular structure.
Expand All @@ -285,15 +285,23 @@ def testSolventMolecule(self):
8 H u0 p0 c0 {2,S}
9 H u0 p0 c0 {3,S}
""")
solventSpecies = Species().fromSMILES('CCO')
self.assertTrue(solventSpecies.isIsomorphic(solventlibrary.entries['ethanol'].item))
solvent_species = Species().fromSMILES('CCO')
self.assertTrue(solvent_species.isIsomorphic(solventlibrary.entries['ethanol'].item[0]))

# Case 4: when the solventDatabase contains incorrect values for the molecule attribute, it raises Exception
# This will display the SMILES Parse Error message from the external function, but ignore it.
self.assertRaises(Exception, solventlibrary.loadEntry, index=4, label='benzene', solvent=None, molecule='ring')

# Case 5: when the solventDatabase contains data for co-solvents.
solventlibrary.loadEntry(index=5, label='methanol_50_water_50', solvent=None, molecule=['CO', 'O'])
solvent_species_list = [Species().fromSMILES('CO'), Species().fromSMILES('O')]
self.assertEqual(len(solventlibrary.entries['methanol_50_water_50'].item), 2)
for spc1 in solventlibrary.entries['methanol_50_water_50'].item:
self.assertTrue(any([spc1.isIsomorphic(spc2) for spc2 in solvent_species_list]))

#####################################################


if __name__ == '__main__':
suite = TestLoader().loadTestsFromTestCase(TestSoluteDatabase)
TextTestRunner(verbosity=2).run(suite)
5 changes: 3 additions & 2 deletions rmgpy/rmg/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,8 +524,9 @@ def initialize(self, **kwargs):

# For liquidReactor, checks whether the solvent is listed as one of the initial species.
if self.solvent:
solventStructure = self.database.solvation.getSolventStructure(self.solvent)
self.database.solvation.checkSolventinInitialSpecies(self,solventStructure)
solvent_structure_list = self.database.solvation.getSolventStructure(self.solvent)
for spc in solvent_structure_list:
self.database.solvation.checkSolventinInitialSpecies(self, spc)

#Check to see if user has input Singlet O2 into their input file or libraries
#This constraint is special in that we only want to check it once in the input instead of every time a species is made
Expand Down
2 changes: 2 additions & 0 deletions rmgpy/species.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ cdef class Species:
cpdef bint has_reactive_molecule(self) except -1

cpdef Species copy(self, bint deep=?)

cpdef set_structure(self, str structure)

################################################################################

Expand Down
20 changes: 19 additions & 1 deletion rmgpy/species.py
Original file line number Diff line number Diff line change
Expand Up @@ -717,9 +717,27 @@ def generateEnergyTransferModel(self):
alpha0 = (300*0.011962,"kJ/mol"),
T0 = (300,"K"),
n = 0.85,
)
)

def set_structure(self, structure):
"""
Set self.molecule from `structure` which could be either a SMILES string or an adjacency list multi-line string
"""
if not self.molecule:
try:
self.molecule = [Molecule(SMILES=structure)]
except ValueError:
try:
self.molecule = [Molecule().fromAdjacencyList(structure)]
except ValueError:
logging.error("Cannot understand the given structure '{0}' of species {1}. Could not "
"interpret it as SMILES nor as adjacency list".format(structure, self.label))
raise
self.generate_resonance_structures()

################################################################################


class TransitionState():
"""
A chemical transition state, representing a first-order saddle point on a
Expand Down