def load_solvation(self, path): """ Load the RMG solvation database from the given `path` on disk, where `path` points to the top-level folder of the RMG solvation database. """ self.solvation = SolvationDatabase() self.solvation.load(path)
def load_old(self, path): """ Load the old RMG database from the given `path` on disk, where `path` points to the top-level folder of the old RMG database. """ self.thermo = ThermoDatabase() self.thermo.load_old(path) self.transport = TransportDatabase() # self.transport.load_old(path) # Currently no load_old import function available for transport groups self.forbidden_structures = ForbiddenStructures() self.forbidden_structures.load_old(os.path.join(path, 'ForbiddenStructures.txt')) self.kinetics = KineticsDatabase() self.kinetics.load_old(path) self.statmech = StatmechDatabase() self.statmech.load_old(path) self.solvation = SolvationDatabase()
def setUp(self): """ A function run before each unit test in this class. """ octyl_pri = Species(label="", thermo=NASA(polynomials=[ NASAPolynomial(coeffs=[-0.772759,0.093255,-5.84447e-05,1.8557e-08,-2.37127e-12,-3926.9,37.6131], Tmin=(298,'K'), Tmax=(1390,'K')), NASAPolynomial(coeffs=[25.051,0.036948,-1.25765e-05,1.94628e-09,-1.12669e-13,-13330.1,-102.557], Tmin=(1390,'K'), Tmax=(5000,'K')) ], Tmin=(298,'K'), Tmax=(5000,'K'), Cp0=(33.2579,'J/(mol*K)'), CpInf=(577.856,'J/(mol*K)'), comment="""Thermo library: JetSurF0.2"""), molecule=[Molecule(SMILES="[CH2]CCCCCCC")]) octyl_sec = Species(label="", thermo=NASA(polynomials=[ NASAPolynomial(coeffs=[-0.304233,0.0880077,-4.90743e-05,1.21858e-08,-8.87773e-13,-5237.93,36.6583], Tmin=(298,'K'), Tmax=(1383,'K')), NASAPolynomial(coeffs=[24.9044,0.0366394,-1.2385e-05,1.90835e-09,-1.10161e-13,-14713.5,-101.345], Tmin=(1383,'K'), Tmax=(5000,'K')) ], Tmin=(298,'K'), Tmax=(5000,'K'), Cp0=(33.2579,'J/(mol*K)'), CpInf=(577.856,'J/(mol*K)'), comment="""Thermo library: JetSurF0.2"""), molecule=[Molecule(SMILES="CC[CH]CCCCC")]) ethane = Species(label="", thermo=ThermoData( Tdata=([300,400,500,600,800,1000,1500],'K'), Cpdata=([10.294,12.643,14.933,16.932,20.033,22.438,26.281],'cal/(mol*K)'), H298=(12.549,'kcal/mol'), S298=(52.379,'cal/(mol*K)'), Cp0=(33.2579,'J/(mol*K)'), CpInf=(133.032,'J/(mol*K)'), comment="""Thermo library: CH"""), molecule=[Molecule(SMILES="C=C")]) decyl = Species(label="", thermo=NASA(polynomials=[ NASAPolynomial(coeffs=[-1.31358,0.117973,-7.51843e-05,2.43331e-08,-3.17523e-12,-9689.68,43.501], Tmin=(298,'K'), Tmax=(1390,'K')), NASAPolynomial(coeffs=[31.5697,0.0455818,-1.54995e-05,2.39711e-09,-1.3871e-13,-21573.8,-134.709], Tmin=(1390,'K'), Tmax=(5000,'K')) ], Tmin=(298,'K'), Tmax=(5000,'K'), Cp0=(33.2579,'J/(mol*K)'), CpInf=(719.202,'J/(mol*K)'), comment="""Thermo library: JetSurF0.2"""), molecule=[Molecule(SMILES="[CH2]CCCCCCCCC")]) self.database = SolvationDatabase() self.database.load(os.path.join(settings['database.directory'], 'solvation')) self.solvent = 'octane' diffusionLimiter.enable(self.database.getSolventData(self.solvent), self.database) self.T = 298 self.uni_reaction = Reaction(reactants=[octyl_pri], products=[octyl_sec]) self.uni_reaction.kinetics = Arrhenius(A=(2.0, '1/s'), n=0, Ea=(0,'kJ/mol')) self.bi_uni_reaction = Reaction(reactants=[octyl_pri, ethane], products=[decyl]) self.bi_uni_reaction.kinetics = Arrhenius(A=(1.0E-22, 'cm^3/molecule/s'), n=0, Ea=(0,'kJ/mol')) self.intrinsic_rates = { self.uni_reaction: self.uni_reaction.kinetics.getRateCoefficient(self.T, P=100e5), self.bi_uni_reaction: self.bi_uni_reaction.kinetics.getRateCoefficient(self.T, P=100e5), }
def setUp(self): self.database = SolvationDatabase() self.database.load( os.path.join(settings['database.directory'], 'solvation'))
class TestSoluteDatabase(TestCase): def setUp(self): self.database = SolvationDatabase() self.database.load( os.path.join(settings['database.directory'], 'solvation')) def tearDown(self): """ Reset the database & liquid parameters for solution """ import rmgpy.data.rmg rmgpy.data.rmg.database = None from rmgpy.rmg.model import Species as DifferentSpecies DifferentSpecies.solventData = None DifferentSpecies.solventName = None DifferentSpecies.solventStructure = None DifferentSpecies.solventViscosity = None def runTest(self): pass def testSoluteLibrary(self): "Test we can obtain solute parameters from a library" species = Species(molecule=[ Molecule(SMILES='COC=O') ]) #methyl formate - we know this is in the solute library libraryData = self.database.getSoluteDataFromLibrary( species, self.database.libraries['solute']) self.assertEqual(len(libraryData), 3) soluteData = self.database.getSoluteData(species) self.assertTrue(isinstance(soluteData, SoluteData)) S = soluteData.S self.assertEqual(S, 0.68) self.assertTrue(soluteData.V is not None) def testMcGowan(self): "Test we can calculate and set the McGowan volume for species containing H,C,O,N or S" self.testCases = [ ['CCCCCCCC', 1.2358], #n-octane, in library ['C(CO)O', 0.5078], #ethylene glycol ['CC#N', 0.4042], #acetonitrile ['CCS', 0.5539] #ethanethiol ] for smiles, volume in self.testCases: species = Species(molecule=[Molecule(SMILES=smiles)]) soluteData = self.database.getSoluteData(species) soluteData.setMcGowanVolume( species) # even if it was found in library, recalculate self.assertTrue( soluteData.V is not None ) # so if it wasn't found in library, we should have calculated it self.assertAlmostEqual( soluteData.V, volume ) # the volume is what we expect given the atoms and bonds def testDiffusivity(self): "Test that for a given solvent viscosity and temperature we can calculate a solute's diffusivity" species = Species(molecule=[Molecule(SMILES='O')]) # water soluteData = self.database.getSoluteData(species) T = 298. solventViscosity = 0.00089 # water is about 8.9e-4 Pa.s D = soluteData.getStokesDiffusivity(T, solventViscosity) # m2/s self.assertAlmostEqual((D * 1e9), 1.3, 1) # self-diffusivity of water is about 2e-9 m2/s def testSolventLibrary(self): "Test we can obtain solvent parameters from a library" solventData = self.database.getSolventData('water') self.assertTrue(solventData is not None) self.assertEqual(solventData.s_h, 2.836) self.assertRaises(DatabaseError, self.database.getSolventData, 'orange_juice') def testViscosity(self): "Test we can calculate the solvent viscosity given a temperature and its A-E correlation parameters" solventData = self.database.getSolventData('water') self.assertAlmostEqual(solventData.getSolventViscosity(298), 0.0009155) def testSoluteGeneration(self): "Test we can estimate Abraham solute parameters correctly using group contributions" self.testCases = [ [ '1,2-ethanediol', 'C(CO)O', 0.823, 0.685, 0.327, 2.572, 0.693, None ], ] for name, smiles, S, B, E, L, A, V in self.testCases: species = Species(molecule=[Molecule(SMILES=smiles)]) soluteData = self.database.getSoluteDataFromGroups( Species(molecule=[species.molecule[0]])) self.assertAlmostEqual(soluteData.S, S, places=2) self.assertAlmostEqual(soluteData.B, B, places=2) self.assertAlmostEqual(soluteData.E, E, places=2) self.assertAlmostEqual(soluteData.L, L, places=2) self.assertAlmostEqual(soluteData.A, A, places=2) def testLonePairSoluteGeneration(self): "Test we can obtain solute parameters via group additivity for a molecule with lone pairs" molecule = Molecule().fromAdjacencyList(""" CH2_singlet multiplicity 1 1 C u0 p1 c0 {2,S} {3,S} 2 H u0 p0 c0 {1,S} 3 H u0 p0 c0 {1,S} """) species = Species(molecule=[molecule]) soluteData = self.database.getSoluteDataFromGroups(species) self.assertTrue(soluteData is not None) def testSoluteDataGenerationAmmonia(self): "Test we can obtain solute parameters via group additivity for ammonia" molecule = Molecule().fromAdjacencyList(""" 1 N u0 p1 c0 {2,S} {3,S} {4,S} 2 H u0 p0 c0 {1,S} 3 H u0 p0 c0 {1,S} 4 H u0 p0 c0 {1,S} """) species = Species(molecule=[molecule]) soluteData = self.database.getSoluteDataFromGroups(species) self.assertTrue(soluteData is not None) def testSoluteDataGenerationAmide(self): "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} 2 H u0 {1,S} 3 C u0 {1,S} {6,S} {7,S} {8,S} 4 C u0 {1,S} {5,D} {9,S} 5 O u0 p2 {4,D} 6 H u0 {3,S} 7 H u0 {3,S} 8 H u0 {3,S} 9 H u0 {4,S} """) species = Species(molecule=[molecule]) soluteData = self.database.getSoluteDataFromGroups(species) self.assertTrue(soluteData is not None) def testSoluteDataGenerationCO(self): "Test that we can obtain solute parameters via group additivity for CO." molecule = Molecule().fromAdjacencyList(""" 1 C u0 p1 c-1 {2,T} 2 O u0 p1 c+1 {1,T} """) species = Species(molecule=[molecule]) soluteData = self.database.getSoluteDataFromGroups(species) self.assertTrue(soluteData is not None) def testRadicalandLonePairGeneration(self): """ Test we can obtain solute parameters via group additivity for a molecule with both lone pairs and a radical """ molecule = Molecule().fromAdjacencyList(""" [C]OH multiplicity 2 1 C u1 p1 c0 {2,S} 2 O u0 p2 c0 {1,S} {3,S} 3 H u0 p0 c0 {2,S} """) species = Species(molecule=[molecule]) soluteData = self.database.getSoluteDataFromGroups(species) self.assertTrue(soluteData is not None) def testCorrectionGeneration(self): "Test we can estimate solvation thermochemistry." self.testCases = [ # solventName, soluteName, soluteSMILES, Hsolv, Gsolv ['water', 'acetic acid', 'C(C)(=O)O', -56500, -6700 * 4.184], [ 'water', 'naphthalene', 'C1=CC=CC2=CC=CC=C12', -42800, -2390 * 4.184 ], ['1-octanol', 'octane', 'CCCCCCCC', -40080, -4180 * 4.184], ['1-octanol', 'tetrahydrofuran', 'C1CCOC1', -28320, -3930 * 4.184], ['benzene', 'toluene', 'C1(=CC=CC=C1)C', -37660, -5320 * 4.184], ['benzene', '1,4-dioxane', 'C1COCCO1', -39030, -5210 * 4.184] ] for solventName, soluteName, smiles, H, G in self.testCases: species = Species(molecule=[Molecule(SMILES=smiles)]) soluteData = self.database.getSoluteData(species) solventData = self.database.getSolventData(solventName) solvationCorrection = self.database.getSolvationCorrection( soluteData, solventData) self.assertAlmostEqual( solvationCorrection.enthalpy / 10000., H / 10000., 0, msg= "Solvation enthalpy discrepancy ({2:.0f}!={3:.0f}) for {0} in {1}" .format(soluteName, solventName, solvationCorrection.enthalpy, H)) #0 decimal place, in 10kJ. self.assertAlmostEqual( solvationCorrection.gibbs / 10000., G / 10000., 0, msg= "Solvation Gibbs free energy discrepancy ({2:.0f}!={3:.0f}) for {0} in {1}" .format(soluteName, solventName, solvationCorrection.gibbs, G)) def testInitialSpecies(self): " Test we can check whether the solvent is listed as one of the initial species in various scenarios " # Case 1. when SMILES for solvent is available, the molecular structures of the initial species and the solvent # are compared to check whether the solvent is in the initial species list # Case 1-1: the solvent water is not in the initialSpecies list, so it raises Exception rmg = RMG() rmg.initialSpecies = [] solute = Species(label='n-octane', molecule=[Molecule().fromSMILES('C(CCCCC)CC')]) rmg.initialSpecies.append(solute) rmg.solvent = 'water' solventStructure = Species().fromSMILES('O') self.assertRaises(Exception, self.database.checkSolventinInitialSpecies, rmg, solventStructure) # Case 1-2: the solvent is now octane and it is listed as the initialSpecies. Although the string # names of the solute and the solvent are different, because the solvent SMILES is provided, # it can identify the 'n-octane' as the solvent rmg.solvent = 'octane' solventStructure = Species().fromSMILES('CCCCCCCC') self.database.checkSolventinInitialSpecies(rmg, solventStructure) self.assertTrue(rmg.initialSpecies[0].isSolvent) # Case 2: the solvent SMILES is not provided. In this case, it can identify the species as the # solvent by looking at the string name. # Case 2-1: Since 'n-octane and 'octane' are not equal, it raises Exception solventStructure = None self.assertRaises(Exception, self.database.checkSolventinInitialSpecies, rmg, solventStructure) # Case 2-2: The label 'n-ocatne' is corrected to 'octane', so it is identified as the solvent rmg.initialSpecies[0].label = 'octane' self.database.checkSolventinInitialSpecies(rmg, solventStructure) 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 " # 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 # Case 1: When the solventDatabase does not contain the solvent SMILES, the item attribute is None solventlibrary = SolventLibrary() solventlibrary.loadEntry(index=1, label='water', solvent=None) self.assertTrue(solventlibrary.entries['water'].item is None) # 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)) # 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. # This will display the SMILES Parse Error message from the external function, but ignore it. solventlibrary.loadEntry(index=3, label='ethanol', solvent=None, molecule=""" 1 C u0 p0 c0 {2,S} {4,S} {5,S} {6,S} 2 C u0 p0 c0 {1,S} {3,S} {7,S} {8,S} 3 O u0 p2 c0 {2,S} {9,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {1,S} 6 H u0 p0 c0 {1,S} 7 H u0 p0 c0 {2,S} 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)) # 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')
class TestSoluteDatabase(TestCase): def setUp(self): self.database = SolvationDatabase() self.database.load( os.path.join(settings['database.directory'], 'solvation')) def tearDown(self): """ Reset the database & liquid parameters for solution """ import rmgpy.data.rmg rmgpy.data.rmg.database = None def test_solute_library(self): """Test we can obtain solute parameters from a library""" species = Species(molecule=[ Molecule(smiles='COC=O') ]) # methyl formate - we know this is in the solute library library_data = self.database.get_solute_data_from_library( species, self.database.libraries['solute']) self.assertEqual(len(library_data), 3) solute_data = self.database.get_solute_data(species) self.assertTrue(isinstance(solute_data, SoluteData)) s = solute_data.S self.assertEqual(s, 0.68) self.assertTrue(solute_data.V is not None) def test_mcgowan(self): """Test we can calculate and set the McGowan volume for species containing H,C,O,N or S""" self.testCases = [ ['CCCCCCCC', 1.2358], # n-octane, in library ['C(CO)O', 0.5078], # ethylene glycol ['CC#N', 0.4042], # acetonitrile ['CCS', 0.5539] # ethanethiol ] for smiles, volume in self.testCases: species = Species(molecule=[Molecule(smiles=smiles)]) solute_data = self.database.get_solute_data(species) solute_data.set_mcgowan_volume( species) # even if it was found in library, recalculate self.assertIsNotNone( solute_data.V ) # so if it wasn't found in library, we should have calculated it self.assertAlmostEqual( solute_data.V, volume ) # the volume is what we expect given the atoms and bonds def test_diffusivity(self): """Test that for a given solvent viscosity and temperature we can calculate a solute's diffusivity""" species = Species(molecule=[Molecule(smiles='O')]) # water solute_data = self.database.get_solute_data(species) temperature = 298. solvent_viscosity = 0.00089 # water is about 8.9e-4 Pa.s d = solute_data.get_stokes_diffusivity(temperature, solvent_viscosity) # m2/s self.assertAlmostEqual((d * 1e9), 1.3, 1) # self-diffusivity of water is about 2e-9 m2/s def test_solvent_library(self): """Test we can obtain solvent parameters from a library""" solvent_data = self.database.get_solvent_data('water') self.assertIsNotNone(solvent_data) self.assertEqual(solvent_data.s_h, 2.836) self.assertRaises(DatabaseError, self.database.get_solvent_data, 'orange_juice') def test_viscosity(self): """Test we can calculate the solvent viscosity given a temperature and its A-E correlation parameters""" solvent_data = self.database.get_solvent_data('water') self.assertAlmostEqual(solvent_data.get_solvent_viscosity(298), 0.0009155) def test_solute_generation(self): """Test we can estimate Abraham solute parameters correctly using group contributions""" self.testCases = [ [ '1,2-ethanediol', 'C(CO)O', 0.823, 0.685, 0.327, 2.572, 0.693, None ], ] for name, smiles, S, B, E, L, A, V in self.testCases: species = Species(smiles=smiles) solute_data = self.database.get_solute_data_from_groups(species) self.assertAlmostEqual(solute_data.S, S, places=2) self.assertAlmostEqual(solute_data.B, B, places=2) self.assertAlmostEqual(solute_data.E, E, places=2) self.assertAlmostEqual(solute_data.L, L, places=2) self.assertAlmostEqual(solute_data.A, A, places=2) def test_solute_with_resonance_structures(self): """ Test we can estimate Abraham solute parameters correctly using group contributions for the solute species with resonance structures. """ smiles = "CC1=CC=CC=C1N" species = Species(smiles=smiles) species.generate_resonance_structures() solute_data = self.database.get_solute_data(species) solvent_data = self.database.get_solvent_data('water') solvation_correction = self.database.get_solvation_correction( solute_data, solvent_data) dGsolv_spc = solvation_correction.gibbs / 1000 for mol in species.molecule: spc = Species(molecule=[mol]) solute_data = self.database.get_solute_data_from_groups(spc) solvation_correction = self.database.get_solvation_correction( solute_data, solvent_data) dGsolv_mol = solvation_correction.gibbs / 1000 if mol == species.molecule[0]: self.assertEqual(dGsolv_spc, dGsolv_mol) else: self.assertNotAlmostEqual(dGsolv_spc, dGsolv_mol) def test_lone_pair_solute_generation(self): """Test we can obtain solute parameters via group additivity for a molecule with lone pairs""" molecule = Molecule().from_adjacency_list(""" CH2_singlet multiplicity 1 1 C u0 p1 c0 {2,S} {3,S} 2 H u0 p0 c0 {1,S} 3 H u0 p0 c0 {1,S} """) species = Species(molecule=[molecule]) solute_data = self.database.get_solute_data_from_groups(species) self.assertIsNotNone(solute_data) def test_solute_data_generation_ammonia(self): """Test we can obtain solute parameters via group additivity for ammonia""" molecule = Molecule().from_adjacency_list(""" 1 N u0 p1 c0 {2,S} {3,S} {4,S} 2 H u0 p0 c0 {1,S} 3 H u0 p0 c0 {1,S} 4 H u0 p0 c0 {1,S} """) species = Species(molecule=[molecule]) solute_data = self.database.get_solute_data_from_groups(species) self.assertIsNotNone(solute_data) def test_solute_data_generation_amide(self): """Test that we can obtain solute parameters via group additivity for an amide""" molecule = Molecule().from_adjacency_list(""" 1 N u0 p1 {2,S} {3,S} {4,S} 2 H u0 {1,S} 3 C u0 {1,S} {6,S} {7,S} {8,S} 4 C u0 {1,S} {5,D} {9,S} 5 O u0 p2 {4,D} 6 H u0 {3,S} 7 H u0 {3,S} 8 H u0 {3,S} 9 H u0 {4,S} """) species = Species(molecule=[molecule]) solute_data = self.database.get_solute_data_from_groups(species) self.assertIsNotNone(solute_data) def test_solute_data_generation_co(self): """Test that we can obtain solute parameters via group additivity for CO.""" molecule = Molecule().from_adjacency_list(""" 1 C u0 p1 c-1 {2,T} 2 O u0 p1 c+1 {1,T} """) species = Species(molecule=[molecule]) solute_data = self.database.get_solute_data_from_groups(species) self.assertIsNotNone(solute_data) def test_radical_and_lone_pair_generation(self): """ Test we can obtain solute parameters via group additivity for a molecule with both lone pairs and a radical """ molecule = Molecule().from_adjacency_list(""" [C]OH multiplicity 2 1 C u1 p1 c0 {2,S} 2 O u0 p2 c0 {1,S} {3,S} 3 H u0 p0 c0 {2,S} """) species = Species(molecule=[molecule]) solute_data = self.database.get_solute_data_from_groups(species) self.assertIsNotNone(solute_data) def test_radical_solute_group(self): """Test that the existing radical group is found for the radical species when using group additivity""" # First check whether the radical group is found for the radical species rad_species = Species(smiles='[OH]') rad_solute_data = self.database.get_solute_data_from_groups( rad_species) self.assertTrue('radical' in rad_solute_data.comment) # Then check that the radical and its saturated species give different solvation free energies saturated_struct = rad_species.molecule[0].copy(deep=True) saturated_struct.saturate_radicals() sat_species = Species(molecule=[saturated_struct]) sat_solute_data = self.database.get_solute_data_from_groups( sat_species) solvent_data = self.database.get_solvent_data('water') rad_solvation_correction = self.database.get_solvation_correction( rad_solute_data, solvent_data) sat_solvation_correction = self.database.get_solvation_correction( sat_solute_data, solvent_data) self.assertNotAlmostEqual(rad_solvation_correction.gibbs / 1000, sat_solvation_correction.gibbs / 1000) def test_correction_generation(self): """Test we can estimate solvation thermochemistry.""" self.testCases = [ # solventName, soluteName, soluteSMILES, Hsolv, Gsolv ['water', 'acetic acid', 'C(C)(=O)O', -56500, -6700 * 4.184], [ 'water', 'naphthalene', 'C1=CC=CC2=CC=CC=C12', -42800, -2390 * 4.184 ], ['1-octanol', 'octane', 'CCCCCCCC', -40080, -4180 * 4.184], ['1-octanol', 'tetrahydrofuran', 'C1CCOC1', -28320, -3930 * 4.184], ['benzene', 'toluene', 'C1(=CC=CC=C1)C', -37660, -5320 * 4.184], ['benzene', '1,4-dioxane', 'C1COCCO1', -39030, -5210 * 4.184] ] for solventName, soluteName, smiles, H, G in self.testCases: species = Species(molecule=[Molecule(smiles=smiles)]) solute_data = self.database.get_solute_data(species) solvent_data = self.database.get_solvent_data(solventName) solvation_correction = self.database.get_solvation_correction( solute_data, solvent_data) self.assertAlmostEqual( solvation_correction.enthalpy / 10000., H / 10000., 0, # 0 decimal place, in 10kJ. msg= "Solvation enthalpy discrepancy ({2:.0f}!={3:.0f}) for {0} in {1}" "".format(soluteName, solventName, solvation_correction.enthalpy, H)) self.assertAlmostEqual( solvation_correction.gibbs / 10000., G / 10000., 0, msg= "Solvation Gibbs free energy discrepancy ({2:.0f}!={3:.0f}) for {0} in {1}" "".format(soluteName, solventName, solvation_correction.gibbs, G)) def test_initial_species(self): """Test we can check whether the solvent is listed as one of the initial species in various scenarios""" # Case 1. when SMILES for solvent is available, the molecular structures of the initial species and the solvent # are compared to check whether the solvent is in the initial species list # Case 1-1: the solvent water is not in the initialSpecies list, so it raises Exception rmg = RMG() rmg.initial_species = [] solute = Species(label='n-octane', molecule=[Molecule().from_smiles('C(CCCCC)CC')]) rmg.initial_species.append(solute) rmg.solvent = 'water' solvent_structure = Species().from_smiles('O') self.assertRaises(Exception, self.database.check_solvent_in_initial_species, rmg, solvent_structure) # Case 1-2: the solvent is now octane and it is listed as the initialSpecies. Although the string # names of the solute and the solvent are different, because the solvent SMILES is provided, # it can identify the 'n-octane' as the solvent rmg.solvent = 'octane' solvent_structure = Species().from_smiles('CCCCCCCC') self.database.check_solvent_in_initial_species(rmg, solvent_structure) self.assertTrue(rmg.initial_species[0].is_solvent) # Case 2: the solvent SMILES is not provided. In this case, it can identify the species as the # solvent by looking at the string name. # Case 2-1: Since 'n-octane and 'octane' are not equal, it raises Exception solvent_structure = None self.assertRaises(Exception, self.database.check_solvent_in_initial_species, rmg, solvent_structure) # Case 2-2: The label 'n-ocatne' is corrected to 'octane', so it is identified as the solvent rmg.initial_species[0].label = 'octane' self.database.check_solvent_in_initial_species(rmg, solvent_structure) self.assertTrue(rmg.initial_species[0].is_solvent) def test_solvent_molecule(self): """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 # Case 1: When the solventDatabase does not contain the solvent SMILES, the item attribute is None solventlibrary = SolventLibrary() solventlibrary.load_entry(index=1, label='water', solvent=None) self.assertTrue(solventlibrary.entries['water'].item is None) # 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.load_entry(index=2, label='octane', solvent=None, molecule='CCCCCCCC') solvent_species = Species().from_smiles('C(CCCCC)CC') self.assertTrue( solvent_species.is_isomorphic( 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. # This will display the SMILES Parse Error message from the external function, but ignore it. solventlibrary.load_entry(index=3, label='ethanol', solvent=None, molecule=""" 1 C u0 p0 c0 {2,S} {4,S} {5,S} {6,S} 2 C u0 p0 c0 {1,S} {3,S} {7,S} {8,S} 3 O u0 p2 c0 {2,S} {9,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {1,S} 6 H u0 p0 c0 {1,S} 7 H u0 p0 c0 {2,S} 8 H u0 p0 c0 {2,S} 9 H u0 p0 c0 {3,S} """) solvent_species = Species().from_smiles('CCO') self.assertTrue( solvent_species.is_isomorphic( 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.load_entry, index=4, label='benzene', solvent=None, molecule='ring') # Case 5: when the solventDatabase contains data for co-solvents. solventlibrary.load_entry(index=5, label='methanol_50_water_50', solvent=None, molecule=['CO', 'O']) solvent_species_list = [ Species().from_smiles('CO'), Species().from_smiles('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.is_isomorphic(spc2) for spc2 in solvent_species_list ]))
class TestSoluteDatabase(TestCase): def setUp(self): self.database = SolvationDatabase() self.database.load( os.path.join(settings['database.directory'], 'solvation')) def runTest(self): pass def testSoluteLibrary(self): "Test we can obtain solute parameters from a library" species = Species(molecule=[ Molecule(SMILES='COC=O') ]) #methyl formate - we know this is in the solute library libraryData = self.database.getSoluteDataFromLibrary( species, self.database.libraries['solute']) self.assertEqual(len(libraryData), 3) soluteData = self.database.getSoluteData(species) self.assertTrue(isinstance(soluteData, SoluteData)) S = soluteData.S self.assertEqual(S, 0.68) self.assertTrue(soluteData.V is not None) def testMcGowan(self): "Test we can calculate and set the McGowan volume for species containing H,C,O,N or S" self.testCases = [ ['CCCCCCCC', 1.2358], #n-octane, in library ['C(CO)O', 0.5078], #ethylene glycol ['CC#N', 0.4042], #acetonitrile ['CCS', 0.5539] #ethanethiol ] for smiles, volume in self.testCases: species = Species(molecule=[Molecule(SMILES=smiles)]) soluteData = self.database.getSoluteData(species) soluteData.setMcGowanVolume( species) # even if it was found in library, recalculate self.assertTrue( soluteData.V is not None ) # so if it wasn't found in library, we should have calculated it self.assertAlmostEqual( soluteData.V, volume ) # the volume is what we expect given the atoms and bonds def testDiffusivity(self): "Test that for a given solvent viscosity and temperature we can calculate a solute's diffusivity" species = Species(molecule=[Molecule(SMILES='O')]) # water soluteData = self.database.getSoluteData(species) T = 298. solventViscosity = 0.00089 # water is about 8.9e-4 Pa.s D = soluteData.getStokesDiffusivity(T, solventViscosity) # m2/s self.assertAlmostEqual((D * 1e9), 1.3, 1) # self-diffusivity of water is about 2e-9 m2/s def testSolventLibrary(self): "Test we can obtain solvent parameters from a library" solventData = self.database.getSolventData('water') self.assertTrue(solventData is not None) self.assertEqual(solventData.s_h, 2.836) self.assertRaises(DatabaseError, self.database.getSolventData, 'orange_juice') def testViscosity(self): "Test we can calculate the solvent viscosity given a temperature and its A-E correlation parameters" solventData = self.database.getSolventData('water') self.assertAlmostEqual(solventData.getSolventViscosity(298), 0.0009155) def testSoluteGeneration(self): "Test we can estimate Abraham solute parameters correctly using group contributions" self.testCases = [ [ '1,2-ethanediol', 'C(CO)O', 0.823, 0.685, 0.327, 2.572, 0.693, None ], ] for name, smiles, S, B, E, L, A, V in self.testCases: species = Species(molecule=[Molecule(SMILES=smiles)]) soluteData = self.database.getSoluteDataFromGroups( Species(molecule=[species.molecule[0]])) self.assertAlmostEqual(soluteData.S, S, places=2) self.assertAlmostEqual(soluteData.B, B, places=2) self.assertAlmostEqual(soluteData.E, E, places=2) self.assertAlmostEqual(soluteData.L, L, places=2) self.assertAlmostEqual(soluteData.A, A, places=2) def testLonePairSoluteGeneration(self): "Test we can obtain solute parameters via group additivity for a molecule with lone pairs" molecule = Molecule().fromAdjacencyList(""" CH2_singlet multiplicity 1 1 C u0 p1 c0 {2,S} {3,S} 2 H u0 p0 c0 {1,S} 3 H u0 p0 c0 {1,S} """) species = Species(molecule=[molecule]) soluteData = self.database.getSoluteDataFromGroups(species) self.assertTrue(soluteData is not None) def testSoluteDataGenerationAmmonia(self): "Test we can obtain solute parameters via group additivity for ammonia" molecule = Molecule().fromAdjacencyList(""" 1 N u0 p1 c0 {2,S} {3,S} {4,S} 2 H u0 p0 c0 {1,S} 3 H u0 p0 c0 {1,S} 4 H u0 p0 c0 {1,S} """) species = Species(molecule=[molecule]) soluteData = self.database.getSoluteDataFromGroups(species) self.assertTrue(soluteData is not None) def testSoluteDataGenerationAmide(self): "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} 2 H u0 {1,S} 3 C u0 {1,S} {6,S} {7,S} {8,S} 4 C u0 {1,S} {5,D} {9,S} 5 O u0 p2 {4,D} 6 H u0 {3,S} 7 H u0 {3,S} 8 H u0 {3,S} 9 H u0 {4,S} """) species = Species(molecule=[molecule]) soluteData = self.database.getSoluteDataFromGroups(species) self.assertTrue(soluteData is not None) def testSoluteDataGenerationCO(self): "Test that we can obtain solute parameters via group additivity for CO." molecule = Molecule().fromAdjacencyList(""" 1 C u0 p1 c-1 {2,T} 2 O u0 p1 c+1 {1,T} """) species = Species(molecule=[molecule]) soluteData = self.database.getSoluteDataFromGroups(species) self.assertTrue(soluteData is not None) def testRadicalandLonePairGeneration(self): """ Test we can obtain solute parameters via group additivity for a molecule with both lone pairs and a radical """ molecule = Molecule().fromAdjacencyList(""" [C]OH multiplicity 2 1 C u1 p1 c0 {2,S} 2 O u0 p2 c0 {1,S} {3,S} 3 H u0 p0 c0 {2,S} """) species = Species(molecule=[molecule]) soluteData = self.database.getSoluteDataFromGroups(species) self.assertTrue(soluteData is not None) def testCorrectionGeneration(self): "Test we can estimate solvation thermochemistry." self.testCases = [ # solventName, soluteName, soluteSMILES, Hsolv, Gsolv ['water', 'acetic acid', 'C(C)(=O)O', -56500, -6700 * 4.184], [ 'water', 'naphthalene', 'C1=CC=CC2=CC=CC=C12', -42800, -2390 * 4.184 ], ['1-octanol', 'octane', 'CCCCCCCC', -40080, -4180 * 4.184], ['1-octanol', 'tetrahydrofuran', 'C1CCOC1', -28320, -3930 * 4.184], ['benzene', 'toluene', 'C1(=CC=CC=C1)C', -37660, -5320 * 4.184], ['benzene', '1,4-dioxane', 'C1COCCO1', -39030, -5210 * 4.184] ] for solventName, soluteName, smiles, H, G in self.testCases: species = Species(molecule=[Molecule(SMILES=smiles)]) soluteData = self.database.getSoluteData(species) solventData = self.database.getSolventData(solventName) solvationCorrection = self.database.getSolvationCorrection( soluteData, solventData) self.assertAlmostEqual( solvationCorrection.enthalpy / 10000., H / 10000., 0, msg= "Solvation enthalpy discrepancy ({2:.0f}!={3:.0f}) for {0} in {1}" .format(soluteName, solventName, solvationCorrection.enthalpy, H)) #0 decimal place, in 10kJ. self.assertAlmostEqual( solvationCorrection.gibbs / 10000., G / 10000., 0, msg= "Solvation Gibbs free energy discrepancy ({2:.0f}!={3:.0f}) for {0} in {1}" .format(soluteName, solventName, solvationCorrection.gibbs, G))
class RMGDatabase(object): """ The primary class for working with the RMG database. """ def __init__(self): self.thermo = None self.transport = None self.forbidden_structures = None self.kinetics = None self.statmech = None self.solvation = None self.surface = None # Store the newly created database in the module. global database if database is not None: logging.warning( 'An instance of RMGDatabase already exists. Re-initializing it.' ) database = self def load( self, path, thermo_libraries=None, transport_libraries=None, reaction_libraries=None, seed_mechanisms=None, kinetics_families=None, kinetics_depositories=None, statmech_libraries=None, depository=True, solvation=True, surface=True, # on by default, because solvation is also on by default testing=False): """ Load the RMG database from the given `path` on disk, where `path` points to the top-level folder of the RMG database. If none of the optional arguments are provided, then the entire database will be loaded. You can use the optional arguments to specify that only certain components of the database be loaded. Argument testing will load a lighter version of the database used for unit-tests """ if not testing: self.load_transport(os.path.join(path, 'transport'), transport_libraries) self.load_forbidden_structures( os.path.join(path, 'forbiddenStructures.py')) self.load_kinetics(os.path.join(path, 'kinetics'), reaction_libraries, seed_mechanisms, kinetics_families, kinetics_depositories) if not testing: self.load_statmech(os.path.join(path, 'statmech'), statmech_libraries, depository) if solvation: self.load_solvation(os.path.join(path, 'solvation')) if surface: self.load_thermo(os.path.join(path, 'thermo'), thermo_libraries, depository, surface) def load_thermo(self, path, thermo_libraries=None, depository=True, surface=False): """ Load the RMG thermo database from the given `path` on disk, where `path` points to the top-level folder of the RMG thermo database. """ self.thermo = ThermoDatabase() self.thermo.load(path, thermo_libraries, depository, surface) def load_transport(self, path, transport_libraries=None): """ Load the RMG transport database from the given 'path' on disk, where 'path' points to the top-level folder of the RMG transport database. """ self.transport = TransportDatabase() self.transport.load(path, transport_libraries) def load_forbidden_structures(self, path=None): """ Load the RMG forbidden structures from the given `path` on disk, where `path` points to the forbidden structures file. If no path is given, a blank forbidden structures object is created. """ self.forbidden_structures = ForbiddenStructures() if path is not None: self.forbidden_structures.load(path) def load_kinetics(self, path, reaction_libraries=None, seed_mechanisms=None, kinetics_families=None, kinetics_depositories=None): """ Load the RMG kinetics database from the given `path` on disk, where `path` points to the top-level folder of the RMG kinetics database. """ kinetics_libraries = [] library_order = [] if seed_mechanisms is None and reaction_libraries is None: kinetics_libraries = None if seed_mechanisms is not None: for library in seed_mechanisms: kinetics_libraries.append(library) library_order.append((library, 'Seed')) if reaction_libraries is not None: for library in reaction_libraries: kinetics_libraries.append(library) library_order.append((library, 'Reaction Library')) self.kinetics = KineticsDatabase() self.kinetics.library_order = library_order self.kinetics.load(path, families=kinetics_families, libraries=kinetics_libraries, depositories=kinetics_depositories) def load_solvation(self, path): """ Load the RMG solvation database from the given `path` on disk, where `path` points to the top-level folder of the RMG solvation database. """ self.solvation = SolvationDatabase() self.solvation.load(path) def load_surface(self, path): """ Load the RMG metal database from the given `path` on disk, where `path` points to the top-level folder of the RMG surface database. """ self.surface = MetalDatabase() self.surface.load(path) def load_statmech(self, path, statmech_libraries=None, depository=True): """ Load the RMG statmech database from the given `path` on disk, where `path` points to the top-level folder of the RMG statmech database. """ self.statmech = StatmechDatabase() self.statmech.load(path, statmech_libraries, depository) def load_old(self, path): """ Load the old RMG database from the given `path` on disk, where `path` points to the top-level folder of the old RMG database. """ self.thermo = ThermoDatabase() self.thermo.load_old(path) self.transport = TransportDatabase() # self.transport.load_old(path) # Currently no load_old import function available for transport groups self.forbidden_structures = ForbiddenStructures() self.forbidden_structures.load_old( os.path.join(path, 'ForbiddenStructures.txt')) self.kinetics = KineticsDatabase() self.kinetics.load_old(path) self.statmech = StatmechDatabase() self.statmech.load_old(path) self.solvation = SolvationDatabase() # Not completely implemented # self.solvation.load_old(path) def save(self, path): """ Save the RMG database to the given `path` on disk. """ if not os.path.exists(path): os.makedirs(path) self.forbidden_structures.save( os.path.join(path, 'forbiddenStructures.py')) self.thermo.save(os.path.join(path, 'thermo')) # self.transport.save(os.path.join(path, 'transport')) #Currently no function for saving transport groups self.kinetics.save(os.path.join(path, 'kinetics')) self.statmech.save(os.path.join(path, 'statmech')) self.solvation.save(os.path.join(path, 'solvation')) self.transport.save(os.path.join(path, 'transport')) def save_old(self, path): """ Save the old RMG database to the given `path` on disk. """ if not os.path.exists(path): os.makedirs(path) self.thermo.save_old(path) self.transport.save_old(path) self.forbidden_structures.save_old( os.path.join(path, 'ForbiddenStructures.txt')) self.kinetics.save_old(path) self.statmech.save_old(path)
def setUp(self): """ A function run before each unit test in this class. """ octyl_pri = Species( label="", thermo=NASA(polynomials=[ NASAPolynomial(coeffs=[ -0.772759, 0.093255, -5.84447e-05, 1.8557e-08, -2.37127e-12, -3926.9, 37.6131 ], Tmin=(298, 'K'), Tmax=(1390, 'K')), NASAPolynomial(coeffs=[ 25.051, 0.036948, -1.25765e-05, 1.94628e-09, -1.12669e-13, -13330.1, -102.557 ], Tmin=(1390, 'K'), Tmax=(5000, 'K')) ], Tmin=(298, 'K'), Tmax=(5000, 'K'), Cp0=(33.2579, 'J/(mol*K)'), CpInf=(577.856, 'J/(mol*K)'), comment="""Thermo library: JetSurF0.2"""), molecule=[Molecule(SMILES="[CH2]CCCCCCC")]) octyl_sec = Species( label="", thermo=NASA(polynomials=[ NASAPolynomial(coeffs=[ -0.304233, 0.0880077, -4.90743e-05, 1.21858e-08, -8.87773e-13, -5237.93, 36.6583 ], Tmin=(298, 'K'), Tmax=(1383, 'K')), NASAPolynomial(coeffs=[ 24.9044, 0.0366394, -1.2385e-05, 1.90835e-09, -1.10161e-13, -14713.5, -101.345 ], Tmin=(1383, 'K'), Tmax=(5000, 'K')) ], Tmin=(298, 'K'), Tmax=(5000, 'K'), Cp0=(33.2579, 'J/(mol*K)'), CpInf=(577.856, 'J/(mol*K)'), comment="""Thermo library: JetSurF0.2"""), molecule=[Molecule(SMILES="CC[CH]CCCCC")]) ethane = Species(label="", thermo=ThermoData( Tdata=([300, 400, 500, 600, 800, 1000, 1500], 'K'), Cpdata=([ 10.294, 12.643, 14.933, 16.932, 20.033, 22.438, 26.281 ], 'cal/(mol*K)'), H298=(12.549, 'kcal/mol'), S298=(52.379, 'cal/(mol*K)'), Cp0=(33.2579, 'J/(mol*K)'), CpInf=(133.032, 'J/(mol*K)'), comment="""Thermo library: CH"""), molecule=[Molecule(SMILES="C=C")]) decyl = Species(label="", thermo=NASA(polynomials=[ NASAPolynomial(coeffs=[ -1.31358, 0.117973, -7.51843e-05, 2.43331e-08, -3.17523e-12, -9689.68, 43.501 ], Tmin=(298, 'K'), Tmax=(1390, 'K')), NASAPolynomial(coeffs=[ 31.5697, 0.0455818, -1.54995e-05, 2.39711e-09, -1.3871e-13, -21573.8, -134.709 ], Tmin=(1390, 'K'), Tmax=(5000, 'K')) ], Tmin=(298, 'K'), Tmax=(5000, 'K'), Cp0=(33.2579, 'J/(mol*K)'), CpInf=(719.202, 'J/(mol*K)'), comment="""Thermo library: JetSurF0.2"""), molecule=[Molecule(SMILES="[CH2]CCCCCCCCC")]) acetone = Species(label="", thermo=NASA(polynomials=[ NASAPolynomial(coeffs=[ 3.75568, 0.0264934, -6.55661e-05, 1.94971e-07, -1.82059e-10, -27905.3, 9.0162 ], Tmin=(10, 'K'), Tmax=(422.477, 'K')), NASAPolynomial(coeffs=[ 0.701289, 0.0344988, -1.9736e-05, 5.48052e-09, -5.92612e-13, -27460.6, 23.329 ], Tmin=(422.477, 'K'), Tmax=(3000, 'K')) ], Tmin=(10, 'K'), Tmax=(3000, 'K'), E0=(-232.025, 'kJ/mol'), Cp0=(33.2579, 'J/(mol*K)'), CpInf=(232.805, 'J/(mol*K)')), molecule=[Molecule(SMILES="CC(=O)C")]) peracetic_acid = Species(label="", thermo=NASA(polynomials=[ NASAPolynomial(coeffs=[ 3.81786, 0.016419, 3.32204e-05, -8.98403e-08, 6.63474e-11, -42057.8, 9.65245 ], Tmin=(10, 'K'), Tmax=(354.579, 'K')), NASAPolynomial(coeffs=[ 2.75993, 0.0283534, -1.72659e-05, 5.08158e-09, -5.77773e-13, -41982.8, 13.6595 ], Tmin=(354.579, 'K'), Tmax=(3000, 'K')) ], Tmin=(10, 'K'), Tmax=(3000, 'K'), E0=(-349.698, 'kJ/mol'), Cp0=(33.2579, 'J/(mol*K)'), CpInf=(199.547, 'J/(mol*K)')), molecule=[Molecule(SMILES="CC(=O)OO")]) acetic_acid = Species(label="", thermo=NASA(polynomials=[ NASAPolynomial(coeffs=[ 3.97665, 0.00159915, 8.5542e-05, -1.76486e-07, 1.20201e-10, -53911.5, 8.99309 ], Tmin=(10, 'K'), Tmax=(375.616, 'K')), NASAPolynomial(coeffs=[ 1.57088, 0.0272146, -1.67357e-05, 5.01453e-09, -5.82273e-13, -53730.7, 18.2442 ], Tmin=(375.616, 'K'), Tmax=(3000, 'K')) ], Tmin=(10, 'K'), Tmax=(3000, 'K'), E0=(-448.245, 'kJ/mol'), Cp0=(33.2579, 'J/(mol*K)'), CpInf=(182.918, 'J/(mol*K)')), molecule=[Molecule(SMILES="CC(=O)O")]) criegee = Species(label="", thermo=NASA(polynomials=[ NASAPolynomial(coeffs=[ 3.23876, 0.0679583, -3.35611e-05, 7.91519e-10, 3.13038e-12, -77986, 13.6438 ], Tmin=(10, 'K'), Tmax=(1053.46, 'K')), NASAPolynomial(coeffs=[ 9.84525, 0.0536795, -2.86165e-05, 7.39945e-09, -7.48482e-13, -79977.6, -21.4187 ], Tmin=(1053.46, 'K'), Tmax=(3000, 'K')) ], Tmin=(10, 'K'), Tmax=(3000, 'K'), E0=(-648.47, 'kJ/mol'), Cp0=(33.2579, 'J/(mol*K)'), CpInf=(457.296, 'J/(mol*K)')), molecule=[Molecule(SMILES="CC(=O)OOC(C)(O)C")]) self.database = SolvationDatabase() self.database.load( os.path.join(settings['database.directory'], 'solvation')) self.solvent = 'octane' diffusionLimiter.enable(self.database.getSolventData(self.solvent), self.database) self.T = 298 self.uni_reaction = Reaction(reactants=[octyl_pri], products=[octyl_sec]) self.uni_reaction.kinetics = Arrhenius(A=(2.0, '1/s'), n=0, Ea=(0, 'kJ/mol')) self.bi_uni_reaction = Reaction(reactants=[octyl_pri, ethane], products=[decyl]) self.bi_uni_reaction.kinetics = Arrhenius(A=(1.0E-22, 'cm^3/molecule/s'), n=0, Ea=(0, 'kJ/mol')) self.tri_bi_reaction = Reaction( reactants=[acetone, peracetic_acid, acetic_acid], products=[criegee, acetic_acid]) self.tri_bi_reaction.kinetics = Arrhenius(A=(1.07543e-11, 'cm^6/(mol^2*s)'), n=5.47295, Ea=(-38.5379, 'kJ/mol')) self.intrinsic_rates = { self.uni_reaction: self.uni_reaction.kinetics.getRateCoefficient(self.T, P=100e5), self.bi_uni_reaction: self.bi_uni_reaction.kinetics.getRateCoefficient(self.T, P=100e5), self.tri_bi_reaction: self.tri_bi_reaction.kinetics.getRateCoefficient(self.T, P=100e5), }
def setUp(self): self.database = SolvationDatabase() self.database.load(os.path.join(settings['database.directory'], 'solvation'))
class TestSoluteDatabase(TestCase): def setUp(self): self.database = SolvationDatabase() self.database.load(os.path.join(settings['database.directory'], 'solvation')) def runTest(self): pass def testSoluteLibrary(self): "Test we can obtain solute parameters from a library" species = Species(molecule=[Molecule(SMILES='COC=O')]) #methyl formate - we know this is in the solute library libraryData = self.database.getSoluteDataFromLibrary(species, self.database.libraries['solute']) self.assertEqual(len(libraryData), 3) soluteData = self.database.getSoluteData(species) self.assertTrue(isinstance(soluteData, SoluteData)) S = soluteData.S self.assertEqual(S, 0.68) self.assertTrue(soluteData.V is not None) def testMcGowan(self): "Test we can calculate and set the McGowan volume for species containing H,C,O,N or S" self.testCases = [ ['CCCCCCCC', 1.2358], #n-octane, in library ['C(CO)O', 0.5078], #ethylene glycol ['CC#N', 0.4042], #acetonitrile ['CCS', 0.5539] #ethanethiol ] for smiles, volume in self.testCases: species = Species(molecule=[Molecule(SMILES=smiles)]) soluteData = self.database.getSoluteData(species) soluteData.setMcGowanVolume(species) # even if it was found in library, recalculate self.assertTrue(soluteData.V is not None) # so if it wasn't found in library, we should have calculated it self.assertAlmostEqual(soluteData.V, volume) # the volume is what we expect given the atoms and bonds def testDiffusivity(self): "Test that for a given solvent viscosity and temperature we can calculate a solute's diffusivity" species = Species(molecule=[Molecule(SMILES='COC=O')]) soluteData = self.database.getSoluteData(species) T = 298 solventViscosity = 0.001 D = soluteData.getStokesDiffusivity(T, solventViscosity) self.assertAlmostEqual((D*1E12), 0.00000979) def testSolventLibrary(self): "Test we can obtain solvent parameters from a library" solventData = self.database.getSolventData('water') self.assertTrue(solventData is not None) self.assertEqual(solventData.s_h, 2.836) self.assertRaises(DatabaseError, self.database.getSolventData, 'orange_juice') def testViscosity(self): "Test we can calculate the solvent viscosity given a temperature and its A-E correlation parameters" solventData = self.database.getSolventData('water') self.assertAlmostEqual(solventData.getSolventViscosity(298), 0.0009155) def testSoluteGeneration(self): "Test we can estimate Abraham solute parameters correctly using group contributions" self.testCases = [ ['1,2-ethanediol', 'C(CO)O', 0.823, 0.685, 0.327, 2.572, 0.693, None], ] for name, smiles, S, B, E, L, A, V in self.testCases: species = Species(molecule=[Molecule(SMILES=smiles)]) soluteData = self.database.getSoluteDataFromGroups(Species(molecule=[species.molecule[0]])) self.assertAlmostEqual(soluteData.S, S, places=2) self.assertAlmostEqual(soluteData.B, B, places=2) self.assertAlmostEqual(soluteData.E, E, places=2) self.assertAlmostEqual(soluteData.L, L, places=2) self.assertAlmostEqual(soluteData.A, A, places=2) def testLonePairSoluteGeneration(self): "Test we can obtain solute parameters via group additivity for a molecule with lone pairs" molecule=Molecule().fromAdjacencyList( """ CH2_singlet multiplicity 1 1 C u0 p1 c0 {2,S} {3,S} 2 H u0 p0 c0 {1,S} 3 H u0 p0 c0 {1,S} """) species = Species(molecule=[molecule]) soluteData = self.database.getSoluteDataFromGroups(species) self.assertTrue(soluteData is not None) def testSoluteDataGenerationAmmonia(self): "Test we can obtain solute parameters via group additivity for ammonia" molecule=Molecule().fromAdjacencyList( """ 1 N u0 p1 c0 {2,S} {3,S} {4,S} 2 H u0 p0 c0 {1,S} 3 H u0 p0 c0 {1,S} 4 H u0 p0 c0 {1,S} """) species = Species(molecule=[molecule]) soluteData = self.database.getSoluteDataFromGroups(species) self.assertTrue(soluteData is not None) def testSoluteDataGenerationAmide(self): "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} 2 H u0 {1,S} 3 C u0 {1,S} {6,S} {7,S} {8,S} 4 C u0 {1,S} {5,D} {9,S} 5 O u0 p2 {4,D} 6 H u0 {3,S} 7 H u0 {3,S} 8 H u0 {3,S} 9 H u0 {4,S} """) species = Species(molecule=[molecule]) soluteData = self.database.getSoluteDataFromGroups(species) self.assertTrue(soluteData is not None) def testRadicalandLonePairGeneration(self): """ Test we can obtain solute parameters via group additivity for a molecule with both lone pairs and a radical """ molecule=Molecule().fromAdjacencyList( """ [C]OH multiplicity 2 1 C u1 p1 c0 {2,S} 2 O u0 p2 c0 {1,S} {3,S} 3 H u0 p0 c0 {2,S} """) species = Species(molecule=[molecule]) soluteData = self.database.getSoluteDataFromGroups(species) self.assertTrue(soluteData is not None) def testCorrectionGeneration(self): "Test we can estimate solvation thermochemistry." self.testCases = [ # solventName, soluteName, soluteSMILES, Hsolv, Gsolv ['water', 'acetic acid', 'C(C)(=O)O', -56500, -6700*4.184], ['water', 'naphthalene', 'C1=CC=CC2=CC=CC=C12', -42800, -2390*4.184], ['1-octanol', 'octane', 'CCCCCCCC', -40080, -4180*4.184], ['1-octanol', 'tetrahydrofuran', 'C1CCOC1', -28320, -3930*4.184], ['benzene', 'toluene', 'C1(=CC=CC=C1)C', -37660, -5320*4.184], ['benzene', '1,4-dioxane', 'C1COCCO1', -39030, -5210*4.184] ] for solventName, soluteName, smiles, H, G in self.testCases: species = Species(molecule=[Molecule(SMILES=smiles)]) soluteData = self.database.getSoluteData(species) solventData = self.database.getSolventData(solventName) solvationCorrection = self.database.getSolvationCorrection(soluteData, solventData) self.assertAlmostEqual(solvationCorrection.enthalpy / 10000., H / 10000., 0, msg="Solvation enthalpy discrepancy ({2:.0f}!={3:.0f}) for {0} in {1}".format(soluteName, solventName, solvationCorrection.enthalpy, H)) #0 decimal place, in 10kJ. self.assertAlmostEqual(solvationCorrection.gibbs / 10000., G / 10000., 0, msg="Solvation Gibbs free energy discrepancy ({2:.0f}!={3:.0f}) for {0} in {1}".format(soluteName, solventName, solvationCorrection.gibbs, G))
class TestSoluteDatabase(TestCase): def setUp(self): self.database = SolvationDatabase() self.database.load(os.path.join(settings['database.directory'], 'solvation')) def runTest(self): pass def testSoluteLibrary(self): "Test we can obtain solute parameters from a library" species = Species(molecule=[Molecule(SMILES='COC=O')]) #methyl formate - we know this is in the solute library libraryData = self.database.getSoluteDataFromLibrary(species, self.database.libraries['solute']) self.assertEqual(len(libraryData), 3) soluteData = self.database.getSoluteData(species) self.assertTrue(isinstance(soluteData, SoluteData)) S = soluteData.S self.assertEqual(S, 0.68) self.assertTrue(soluteData.V is not None) def testMcGowan(self): "Test we can calculate and set the McGowan volume for species containing H,C,O,N or S" self.testCases = [ ['CCCCCCCC', 1.2358], #n-octane, in library ['C(CO)O', 0.5078], #ethylene glycol ['CC#N', 0.4042], #acetonitrile ['CCS', 0.5539] #ethanethiol ] for smiles, volume in self.testCases: species = Species(molecule=[Molecule(SMILES=smiles)]) soluteData = self.database.getSoluteData(species) soluteData.setMcGowanVolume(species) # even if it was found in library, recalculate self.assertTrue(soluteData.V is not None) # so if it wasn't found in library, we should have calculated it self.assertAlmostEqual(soluteData.V, volume) # the volume is what we expect given the atoms and bonds def testDiffusivity(self): "Test that for a given solvent viscosity and temperature we can calculate a solute's diffusivity" species = Species(molecule=[Molecule(SMILES='O')]) # water soluteData = self.database.getSoluteData(species) T = 298. solventViscosity = 0.00089 # water is about 8.9e-4 Pa.s D = soluteData.getStokesDiffusivity(T, solventViscosity) # m2/s self.assertAlmostEqual((D * 1e9), 1.3, 1) # self-diffusivity of water is about 2e-9 m2/s def testSolventLibrary(self): "Test we can obtain solvent parameters from a library" solventData = self.database.getSolventData('water') self.assertTrue(solventData is not None) self.assertEqual(solventData.s_h, 2.836) self.assertRaises(DatabaseError, self.database.getSolventData, 'orange_juice') def testViscosity(self): "Test we can calculate the solvent viscosity given a temperature and its A-E correlation parameters" solventData = self.database.getSolventData('water') self.assertAlmostEqual(solventData.getSolventViscosity(298), 0.0009155) def testSoluteGeneration(self): "Test we can estimate Abraham solute parameters correctly using group contributions" self.testCases = [ ['1,2-ethanediol', 'C(CO)O', 0.823, 0.685, 0.327, 2.572, 0.693, None], ] for name, smiles, S, B, E, L, A, V in self.testCases: species = Species(molecule=[Molecule(SMILES=smiles)]) soluteData = self.database.getSoluteDataFromGroups(Species(molecule=[species.molecule[0]])) self.assertAlmostEqual(soluteData.S, S, places=2) self.assertAlmostEqual(soluteData.B, B, places=2) self.assertAlmostEqual(soluteData.E, E, places=2) self.assertAlmostEqual(soluteData.L, L, places=2) self.assertAlmostEqual(soluteData.A, A, places=2) def testLonePairSoluteGeneration(self): "Test we can obtain solute parameters via group additivity for a molecule with lone pairs" molecule=Molecule().fromAdjacencyList( """ CH2_singlet multiplicity 1 1 C u0 p1 c0 {2,S} {3,S} 2 H u0 p0 c0 {1,S} 3 H u0 p0 c0 {1,S} """) species = Species(molecule=[molecule]) soluteData = self.database.getSoluteDataFromGroups(species) self.assertTrue(soluteData is not None) def testSoluteDataGenerationAmmonia(self): "Test we can obtain solute parameters via group additivity for ammonia" molecule=Molecule().fromAdjacencyList( """ 1 N u0 p1 c0 {2,S} {3,S} {4,S} 2 H u0 p0 c0 {1,S} 3 H u0 p0 c0 {1,S} 4 H u0 p0 c0 {1,S} """) species = Species(molecule=[molecule]) soluteData = self.database.getSoluteDataFromGroups(species) self.assertTrue(soluteData is not None) def testSoluteDataGenerationAmide(self): "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} 2 H u0 {1,S} 3 C u0 {1,S} {6,S} {7,S} {8,S} 4 C u0 {1,S} {5,D} {9,S} 5 O u0 p2 {4,D} 6 H u0 {3,S} 7 H u0 {3,S} 8 H u0 {3,S} 9 H u0 {4,S} """) species = Species(molecule=[molecule]) soluteData = self.database.getSoluteDataFromGroups(species) self.assertTrue(soluteData is not None) def testSoluteDataGenerationCO(self): "Test that we can obtain solute parameters via group additivity for CO." molecule=Molecule().fromAdjacencyList( """ 1 C u0 p1 c-1 {2,T} 2 O u0 p1 c+1 {1,T} """) species = Species(molecule=[molecule]) soluteData = self.database.getSoluteDataFromGroups(species) self.assertTrue(soluteData is not None) def testRadicalandLonePairGeneration(self): """ Test we can obtain solute parameters via group additivity for a molecule with both lone pairs and a radical """ molecule=Molecule().fromAdjacencyList( """ [C]OH multiplicity 2 1 C u1 p1 c0 {2,S} 2 O u0 p2 c0 {1,S} {3,S} 3 H u0 p0 c0 {2,S} """) species = Species(molecule=[molecule]) soluteData = self.database.getSoluteDataFromGroups(species) self.assertTrue(soluteData is not None) def testCorrectionGeneration(self): "Test we can estimate solvation thermochemistry." self.testCases = [ # solventName, soluteName, soluteSMILES, Hsolv, Gsolv ['water', 'acetic acid', 'C(C)(=O)O', -56500, -6700*4.184], ['water', 'naphthalene', 'C1=CC=CC2=CC=CC=C12', -42800, -2390*4.184], ['1-octanol', 'octane', 'CCCCCCCC', -40080, -4180*4.184], ['1-octanol', 'tetrahydrofuran', 'C1CCOC1', -28320, -3930*4.184], ['benzene', 'toluene', 'C1(=CC=CC=C1)C', -37660, -5320*4.184], ['benzene', '1,4-dioxane', 'C1COCCO1', -39030, -5210*4.184] ] for solventName, soluteName, smiles, H, G in self.testCases: species = Species(molecule=[Molecule(SMILES=smiles)]) soluteData = self.database.getSoluteData(species) solventData = self.database.getSolventData(solventName) solvationCorrection = self.database.getSolvationCorrection(soluteData, solventData) self.assertAlmostEqual(solvationCorrection.enthalpy / 10000., H / 10000., 0, msg="Solvation enthalpy discrepancy ({2:.0f}!={3:.0f}) for {0} in {1}".format(soluteName, solventName, solvationCorrection.enthalpy, H)) #0 decimal place, in 10kJ. self.assertAlmostEqual(solvationCorrection.gibbs / 10000., G / 10000., 0, msg="Solvation Gibbs free energy discrepancy ({2:.0f}!={3:.0f}) for {0} in {1}".format(soluteName, solventName, solvationCorrection.gibbs, G)) def testInitialSpecies(self): " Test we can check whether the solvent is listed as one of the initial species in various scenarios " # Case 1. when SMILES for solvent is available, the molecular structures of the initial species and the solvent # are compared to check whether the solvent is in the initial species list # Case 1-1: the solvent water is not in the initialSpecies list, so it raises Exception rmg=RMG() rmg.initialSpecies = [] solute = Species(label='n-octane', molecule=[Molecule().fromSMILES('C(CCCCC)CC')]) rmg.initialSpecies.append(solute) rmg.solvent = 'water' solventStructure = Species().fromSMILES('O') self.assertRaises(Exception, self.database.checkSolventinInitialSpecies, rmg, solventStructure) # Case 1-2: the solvent is now octane and it is listed as the initialSpecies. Although the string # names of the solute and the solvent are different, because the solvent SMILES is provided, # it can identify the 'n-octane' as the solvent rmg.solvent = 'octane' solventStructure = Species().fromSMILES('CCCCCCCC') self.database.checkSolventinInitialSpecies(rmg, solventStructure) self.assertTrue(rmg.initialSpecies[0].isSolvent) # Case 2: the solvent SMILES is not provided. In this case, it can identify the species as the # solvent by looking at the string name. # Case 2-1: Since 'n-octane and 'octane' are not equal, it raises Exception solventStructure = None self.assertRaises(Exception, self.database.checkSolventinInitialSpecies, rmg, solventStructure) # Case 2-2: The label 'n-ocatne' is corrected to 'octane', so it is identified as the solvent rmg.initialSpecies[0].label = 'octane' self.database.checkSolventinInitialSpecies(rmg, solventStructure) 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 " # 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 # Case 1: When the solventDatabase does not contain the solvent SMILES, the item attribute is None solventlibrary = SolventLibrary() solventlibrary.loadEntry(index=1, label='water', solvent=None) self.assertTrue(solventlibrary.entries['water'].item is None) # 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)) # 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. # This will display the SMILES Parse Error message from the external function, but ignore it. solventlibrary.loadEntry(index=3, label='ethanol', solvent=None, molecule= """ 1 C u0 p0 c0 {2,S} {4,S} {5,S} {6,S} 2 C u0 p0 c0 {1,S} {3,S} {7,S} {8,S} 3 O u0 p2 c0 {2,S} {9,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {1,S} 6 H u0 p0 c0 {1,S} 7 H u0 p0 c0 {2,S} 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)) # 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')
class TestSoluteDatabase(TestCase): def setUp(self): self.database = SolvationDatabase() self.database.load( os.path.join(settings['database.directory'], 'solvation')) def tearDown(self): """ Reset the database & liquid parameters for solution """ import rmgpy.data.rmg rmgpy.data.rmg.database = None def test_solute_library(self): """Test we can obtain solute parameters from a library""" species = Species(molecule=[ Molecule(smiles='COC=O') ]) # methyl formate - we know this is in the solute library library_data = self.database.get_solute_data_from_library( species, self.database.libraries['solute']) self.assertEqual(len(library_data), 3) solute_data = self.database.get_solute_data(species) self.assertTrue(isinstance(solute_data, SoluteData)) s = solute_data.S self.assertEqual(s, 0.68) self.assertTrue(solute_data.V is not None) def test_mcgowan(self): """Test we can calculate and set the McGowan volume for species containing H,C,O,N or S""" self.testCases = [ ['CCCCCCCC', 1.2358], # n-octane, in library ['C(CO)O', 0.5078], # ethylene glycol ['CC#N', 0.4042], # acetonitrile ['CCS', 0.5539] # ethanethiol ] for smiles, volume in self.testCases: species = Species(molecule=[Molecule(smiles=smiles)]) solute_data = self.database.get_solute_data(species) solute_data.set_mcgowan_volume( species) # even if it was found in library, recalculate self.assertIsNotNone( solute_data.V ) # so if it wasn't found in library, we should have calculated it self.assertAlmostEqual( solute_data.V, volume ) # the volume is what we expect given the atoms and bonds def test_diffusivity(self): """Test that for a given solvent viscosity and temperature we can calculate a solute's diffusivity""" species = Species(molecule=[Molecule(smiles='O')]) # water solute_data = self.database.get_solute_data(species) temperature = 298. solvent_viscosity = 0.00089 # water is about 8.9e-4 Pa.s d = solute_data.get_stokes_diffusivity(temperature, solvent_viscosity) # m2/s self.assertAlmostEqual((d * 1e9), 1.3, 1) # self-diffusivity of water is about 2e-9 m2/s def test_solvent_library(self): """Test we can obtain solvent parameters and data count from a library""" solvent_data = self.database.get_solvent_data('water') self.assertIsNotNone(solvent_data) self.assertEqual(solvent_data.s_h, -0.75922) self.assertRaises(DatabaseError, self.database.get_solvent_data, 'orange_juice') solvent_data = self.database.get_solvent_data('cyclohexane') self.assertEqual(solvent_data.name_in_coolprop, 'CycloHexane') solvent_data_count = self.database.get_solvent_data_count( 'dodecan-1-ol') self.assertEqual(solvent_data_count.dGsolvCount, 11) dHsolvMAE = (0.05, 'kcal/mol') self.assertTrue(solvent_data_count.dHsolvMAE == dHsolvMAE) def test_viscosity(self): """Test we can calculate the solvent viscosity given a temperature and its A-E correlation parameters""" solvent_data = self.database.get_solvent_data('water') self.assertAlmostEqual(solvent_data.get_solvent_viscosity(298), 0.0009155) def test_critical_temperature(self): """ Test we can calculate the solvent critical temperature given the solvent's name_in_coolprop and we can raise DatabaseError when the solvent's name_in_coolprop is None. """ solvent_data = self.database.get_solvent_data('water') self.assertAlmostEqual(solvent_data.get_solvent_critical_temperature(), 647.096) solvent_data = self.database.get_solvent_data('dibutylether') self.assertRaises(DatabaseError, solvent_data.get_solvent_critical_temperature) def test_find_solvent(self): """ Test we can find solvents from the solvent library using SMILES""" # Case 1: one solvent is matched solvent_smiles = "NC=O" match_list = self.database.find_solvent_from_smiles(solvent_smiles) self.assertEqual(len(match_list), 1) self.assertTrue(match_list[0][0] == 'formamide') # Case 2: two solvents are matched solvent_smiles = "ClC=CCl" match_list = self.database.find_solvent_from_smiles(solvent_smiles) self.assertEqual(len(match_list), 2) self.assertTrue(match_list[0][0] == 'cis-1,2-dichloroethene') self.assertTrue(match_list[1][0] == 'trans-1,2-dichloroethene') # Case 3: no solvent is matched solvent_smiles = "C(CCl)O" match_list = self.database.find_solvent_from_smiles(solvent_smiles) self.assertEqual(len(match_list), 0) def test_solute_groups(self): """Test we can correctly load the solute groups from the solvation group database""" solute_group = self.database.groups['group'].entries['Cds-N3dCbCb'] self.assertEqual(solute_group.data_count.S, 28) self.assertEqual(solute_group.data.B, 0.06652) solute_group = self.database.groups['ring'].entries['FourMember'] self.assertIsNone(solute_group.data_count) self.assertEqual(solute_group.data, 'Cyclobutane') def test_solute_generation(self): """Test we can estimate Abraham solute parameters correctly using group contributions""" self.testCases = [[ '1,2-ethanediol', 'C(CO)O', 0.809, 0.740, 0.393, 2.482, 0.584, 0.508 ]] for name, smiles, S, B, E, L, A, V in self.testCases: species = Species(smiles=smiles) solute_data = self.database.get_solute_data_from_groups(species) self.assertAlmostEqual(solute_data.S, S, places=2) self.assertAlmostEqual(solute_data.B, B, places=2) self.assertAlmostEqual(solute_data.E, E, places=2) self.assertAlmostEqual(solute_data.L, L, places=2) self.assertAlmostEqual(solute_data.A, A, places=2) def test_solute_with_resonance_structures(self): """ Test we can estimate Abraham solute parameters correctly using group contributions for the solute species with resonance structures. """ smiles = "CC1=CC=CC=C1N" species = Species(smiles=smiles) species.generate_resonance_structures() solute_data = self.database.get_solute_data(species) solvent_data = self.database.get_solvent_data('water') solvation_correction = self.database.get_solvation_correction( solute_data, solvent_data) dGsolv_spc = solvation_correction.gibbs / 1000 for mol in species.molecule: spc = Species(molecule=[mol]) solute_data = self.database.get_solute_data_from_groups(spc) solvation_correction = self.database.get_solvation_correction( solute_data, solvent_data) dGsolv_mol = solvation_correction.gibbs / 1000 if mol == species.molecule[0]: self.assertEqual(dGsolv_spc, dGsolv_mol) else: self.assertNotAlmostEqual(dGsolv_spc, dGsolv_mol) def test_lone_pair_solute_generation(self): """Test we can obtain solute parameters via group additivity for a molecule with lone pairs""" molecule = Molecule().from_adjacency_list(""" CH2_singlet multiplicity 1 1 C u0 p1 c0 {2,S} {3,S} 2 H u0 p0 c0 {1,S} 3 H u0 p0 c0 {1,S} """) species = Species(molecule=[molecule]) solute_data = self.database.get_solute_data_from_groups(species) self.assertIsNotNone(solute_data) def test_solute_data_generation_ammonia(self): """Test we can obtain solute parameters via group additivity for ammonia""" molecule = Molecule().from_adjacency_list(""" 1 N u0 p1 c0 {2,S} {3,S} {4,S} 2 H u0 p0 c0 {1,S} 3 H u0 p0 c0 {1,S} 4 H u0 p0 c0 {1,S} """) species = Species(molecule=[molecule]) solute_data = self.database.get_solute_data_from_groups(species) self.assertIsNotNone(solute_data) def test_solute_data_generation_amide(self): """Test that we can obtain solute parameters via group additivity for an amide""" molecule = Molecule().from_adjacency_list(""" 1 N u0 p1 {2,S} {3,S} {4,S} 2 H u0 {1,S} 3 C u0 {1,S} {6,S} {7,S} {8,S} 4 C u0 {1,S} {5,D} {9,S} 5 O u0 p2 {4,D} 6 H u0 {3,S} 7 H u0 {3,S} 8 H u0 {3,S} 9 H u0 {4,S} """) species = Species(molecule=[molecule]) solute_data = self.database.get_solute_data_from_groups(species) self.assertIsNotNone(solute_data) def test_solute_data_generation_co(self): """Test that we can obtain solute parameters via group additivity for CO.""" molecule = Molecule().from_adjacency_list(""" 1 C u0 p1 c-1 {2,T} 2 O u0 p1 c+1 {1,T} """) species = Species(molecule=[molecule]) solute_data = self.database.get_solute_data_from_groups(species) self.assertIsNotNone(solute_data) def test_radical_and_lone_pair_generation(self): """ Test we can obtain solute parameters via group additivity for a molecule with both lone pairs and a radical """ molecule = Molecule().from_adjacency_list(""" [C]OH multiplicity 2 1 C u1 p1 c0 {2,S} 2 O u0 p2 c0 {1,S} {3,S} 3 H u0 p0 c0 {2,S} """) species = Species(molecule=[molecule]) solute_data = self.database.get_solute_data_from_groups(species) self.assertIsNotNone(solute_data) def test_radical_solute_group(self): """Test that the existing radical group is found for the radical species when using group additivity""" # First check whether the radical group is found for the radical species rad_species = Species(smiles='[OH]') rad_solute_data = self.database.get_solute_data_from_groups( rad_species) self.assertTrue('radical' in rad_solute_data.comment) # Then check that the radical and its saturated species give different solvation free energies saturated_struct = rad_species.molecule[0].copy(deep=True) saturated_struct.saturate_radicals() sat_species = Species(molecule=[saturated_struct]) sat_solute_data = self.database.get_solute_data_from_groups( sat_species) solvent_data = self.database.get_solvent_data('water') rad_solvation_correction = self.database.get_solvation_correction( rad_solute_data, solvent_data) sat_solvation_correction = self.database.get_solvation_correction( sat_solute_data, solvent_data) self.assertNotAlmostEqual(rad_solvation_correction.gibbs / 1000, sat_solvation_correction.gibbs / 1000) def test_halogen_solute_group(self): """Test that the correct halogen groups can be found for the halogenated species using get_solute_data method""" # Check the species whose halogen-replaced form can be found from solute library species = Species().from_smiles('CCCCCCl') solute_data = self.database.get_solute_data(species) self.assertTrue("Solute library: n-pentane + halogen(Cl-(Cs-CsHH))" in solute_data.comment) # Check the species whose halogen-replaced form cannot be found from solute library species = Species().from_smiles('OCCCCCCC(Br)CCCCCO') solute_data = self.database.get_solute_data(species) self.assertTrue("+ group(Cs-Cs(Os-H)HH) + halogen(Br-(Cs-CsCsH))" in solute_data.comment) def test_radical_halogen_solute_group(self): """Test that the correct halogen and radical groups can be found for the halogenated radical species using get_solute_data method""" # Check the species whose saturated and halogenated form can be found from solute library species = Species().from_smiles('[O]CCCCl') solute_data = self.database.get_solute_data(species) self.assertTrue("Solute library: 3-Chloropropan-1-ol + radical(ROJ)" == solute_data.comment) # Check the species whose saturated and halogen-replaced form can be found from solute library species = Species().from_smiles('[O]CCCC(Br)(I)Cl') solute_data = self.database.get_solute_data(species) self.assertTrue("Solute library: butan-1-ol + halogen(I-(Cs-CsHH)) + halogen(Br-(Cs-CsFCl)) + halogen(Cl-(Cs-CsFBr)) + radical(ROJ)" \ == solute_data.comment) # Check the species whose saturated and halogen-replaced form cannot be found from solute library species = Species().from_smiles('[NH]C(=O)CCCl') solute_data = self.database.get_solute_data(species) self.assertTrue( "group(Cds-Od(N3s-HH)Cs) + halogen(Cl-(Cs-CsHH)) + radical(N3_amide_pri)" in solute_data.comment) # Check the species whose radical site is bonded to halogen species = Species().from_smiles('F[N]C(=O)CCCl') solute_data = self.database.get_solute_data(species) self.assertTrue( "group(Cds-Od(N3s-HH)Cs) + halogen(Cl-(Cs-CsHH)) + halogen(F-N3s) + radical(N3_amide_sec)" in solute_data.comment) def test_correction_generation(self): """Test we can estimate solvation thermochemistry.""" self.testCases = [ # solventName, soluteName, soluteSMILES, Hsolv, Gsolv in kJ/mol ['water', 'acetic acid', 'C(C)(=O)O', -48.48, -28.12], ['water', 'naphthalene', 'C1=CC=CC2=CC=CC=C12', -37.15, -11.21], ['1-octanol', 'octane', 'CCCCCCCC', -39.44, -16.83], ['1-octanol', 'tetrahydrofuran', 'C1CCOC1', -32.27, -17.81], ['benzene', 'toluene', 'C1(=CC=CC=C1)C', -39.33, -23.81], ['benzene', '1,4-dioxane', 'C1COCCO1', -39.15, -22.01] ] for solventName, soluteName, smiles, H, G in self.testCases: species = Species().from_smiles(smiles) species.generate_resonance_structures() solute_data = self.database.get_solute_data(species) solvent_data = self.database.get_solvent_data(solventName) solvation_correction = self.database.get_solvation_correction( solute_data, solvent_data) self.assertAlmostEqual( solvation_correction.enthalpy / 1000, H, 2, # 2 decimal places, in kJ. msg= "Solvation enthalpy discrepancy ({2:.2f}!={3:.2f}) for {0} in {1}" "".format(soluteName, solventName, solvation_correction.enthalpy / 1000, H)) self.assertAlmostEqual( solvation_correction.gibbs / 1000, G, 2, # 2 decimal places, in kJ. msg= "Solvation Gibbs free energy discrepancy ({2:.2f}!={3:.2f}) for {0} in {1}" "".format(soluteName, solventName, solvation_correction.gibbs / 1000, G)) def test_Kfactor_parameters(self): """Test we can calculate the parameters for K-factor relationships""" species = Species().from_smiles('CCC(C)=O') # 2-Butanone for a solute solute_data = self.database.get_solute_data(species) solvent_data = self.database.get_solvent_data('water') kfactor_parameters = self.database.get_Kfactor_parameters( solute_data, solvent_data) self.assertAlmostEqual(kfactor_parameters.lower_T[0], -9.780, 3) # check up to 3 decimal places self.assertAlmostEqual(kfactor_parameters.lower_T[1], 0.492, 3) self.assertAlmostEqual(kfactor_parameters.lower_T[2], 10.485, 3) self.assertAlmostEqual(kfactor_parameters.higher_T, 1.147, 3) self.assertAlmostEqual(kfactor_parameters.T_transition, 485.3, 1) # check that DatabaseError is raised when the solvent's name_in_coolprop is None solvent_data = self.database.get_solvent_data('chloroform') self.assertRaises(DatabaseError, self.database.get_Kfactor_parameters, solute_data, solvent_data) def test_Tdep_solvation_calculation(self): '''Test we can calculate the temperature dependent K-factor and solvation free energy''' species = Species().from_smiles('CCC1=CC=CC=C1') # ethylbenzene species.generate_resonance_structures() solute_data = self.database.get_solute_data(species) solvent_data = self.database.get_solvent_data('benzene') T = 500 # in K Kfactor = self.database.get_Kfactor(solute_data, solvent_data, T) delG = self.database.get_T_dep_solvation_energy( solute_data, solvent_data, T) / 1000 # in kJ/mol self.assertAlmostEqual(Kfactor, 0.403, 3) self.assertAlmostEqual(delG, -13.59, 2) # For temperature greater than or equal to the critical temperature of the solvent, # it should raise InputError T = 1000 self.assertRaises(InputError, self.database.get_T_dep_solvation_energy, solute_data, solvent_data, T) def test_initial_species(self): """Test we can check whether the solvent is listed as one of the initial species in various scenarios""" # Case 1. when SMILES for solvent is available, the molecular structures of the initial species and the solvent # are compared to check whether the solvent is in the initial species list # Case 1-1: the solvent water is not in the initialSpecies list, so it raises Exception rmg = RMG() rmg.initial_species = [] solute = Species(label='n-octane', molecule=[Molecule().from_smiles('C(CCCCC)CC')]) rmg.initial_species.append(solute) rmg.solvent = 'water' solvent_structure = Species().from_smiles('O') self.assertRaises(Exception, self.database.check_solvent_in_initial_species, rmg, solvent_structure) # Case 1-2: the solvent is now octane and it is listed as the initialSpecies. Although the string # names of the solute and the solvent are different, because the solvent SMILES is provided, # it can identify the 'n-octane' as the solvent rmg.solvent = 'octane' solvent_structure = Species().from_smiles('CCCCCCCC') self.database.check_solvent_in_initial_species(rmg, solvent_structure) self.assertTrue(rmg.initial_species[0].is_solvent) # Case 2: the solvent SMILES is not provided. In this case, it can identify the species as the # solvent by looking at the string name. # Case 2-1: Since 'n-octane and 'octane' are not equal, it raises Exception solvent_structure = None self.assertRaises(Exception, self.database.check_solvent_in_initial_species, rmg, solvent_structure) # Case 2-2: The label 'n-ocatne' is corrected to 'octane', so it is identified as the solvent rmg.initial_species[0].label = 'octane' self.database.check_solvent_in_initial_species(rmg, solvent_structure) self.assertTrue(rmg.initial_species[0].is_solvent) def test_solvent_molecule(self): """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 # Case 1: When the solventDatabase does not contain the solvent SMILES, the item attribute is None solventlibrary = SolventLibrary() solventlibrary.load_entry(index=1, label='water', solvent=None) self.assertTrue(solventlibrary.entries['water'].item is None) # 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.load_entry(index=2, label='octane', solvent=None, molecule='CCCCCCCC') solvent_species = Species().from_smiles('C(CCCCC)CC') self.assertTrue( solvent_species.is_isomorphic( 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. # This will display the SMILES Parse Error message from the external function, but ignore it. solventlibrary.load_entry(index=3, label='ethanol', solvent=None, molecule=""" 1 C u0 p0 c0 {2,S} {4,S} {5,S} {6,S} 2 C u0 p0 c0 {1,S} {3,S} {7,S} {8,S} 3 O u0 p2 c0 {2,S} {9,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {1,S} 6 H u0 p0 c0 {1,S} 7 H u0 p0 c0 {2,S} 8 H u0 p0 c0 {2,S} 9 H u0 p0 c0 {3,S} """) solvent_species = Species().from_smiles('CCO') self.assertTrue( solvent_species.is_isomorphic( 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.load_entry, index=4, label='benzene', solvent=None, molecule='ring') # Case 5: when the solventDatabase contains data for co-solvents. solventlibrary.load_entry(index=5, label='methanol_50_water_50', solvent=None, molecule=['CO', 'O']) solvent_species_list = [ Species().from_smiles('CO'), Species().from_smiles('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.is_isomorphic(spc2) for spc2 in solvent_species_list ]))
def setUp(self): """ A function run before each unit test in this class. """ octyl_pri = Species(label="", thermo=NASA(polynomials=[ NASAPolynomial(coeffs=[-0.772759,0.093255,-5.84447e-05,1.8557e-08,-2.37127e-12,-3926.9,37.6131], Tmin=(298,'K'), Tmax=(1390,'K')), NASAPolynomial(coeffs=[25.051,0.036948,-1.25765e-05,1.94628e-09,-1.12669e-13,-13330.1,-102.557], Tmin=(1390,'K'), Tmax=(5000,'K')) ], Tmin=(298,'K'), Tmax=(5000,'K'), Cp0=(33.2579,'J/(mol*K)'), CpInf=(577.856,'J/(mol*K)'), comment="""Thermo library: JetSurF0.2"""), molecule=[Molecule(SMILES="[CH2]CCCCCCC")]) octyl_sec = Species(label="", thermo=NASA(polynomials=[ NASAPolynomial(coeffs=[-0.304233,0.0880077,-4.90743e-05,1.21858e-08,-8.87773e-13,-5237.93,36.6583], Tmin=(298,'K'), Tmax=(1383,'K')), NASAPolynomial(coeffs=[24.9044,0.0366394,-1.2385e-05,1.90835e-09,-1.10161e-13,-14713.5,-101.345], Tmin=(1383,'K'), Tmax=(5000,'K')) ], Tmin=(298,'K'), Tmax=(5000,'K'), Cp0=(33.2579,'J/(mol*K)'), CpInf=(577.856,'J/(mol*K)'), comment="""Thermo library: JetSurF0.2"""), molecule=[Molecule(SMILES="CC[CH]CCCCC")]) ethane = Species(label="", thermo=ThermoData( Tdata=([300,400,500,600,800,1000,1500],'K'), Cpdata=([10.294,12.643,14.933,16.932,20.033,22.438,26.281],'cal/(mol*K)'), H298=(12.549,'kcal/mol'), S298=(52.379,'cal/(mol*K)'), Cp0=(33.2579,'J/(mol*K)'), CpInf=(133.032,'J/(mol*K)'), comment="""Thermo library: CH"""), molecule=[Molecule(SMILES="C=C")]) decyl = Species(label="", thermo=NASA(polynomials=[ NASAPolynomial(coeffs=[-1.31358,0.117973,-7.51843e-05,2.43331e-08,-3.17523e-12,-9689.68,43.501], Tmin=(298,'K'), Tmax=(1390,'K')), NASAPolynomial(coeffs=[31.5697,0.0455818,-1.54995e-05,2.39711e-09,-1.3871e-13,-21573.8,-134.709], Tmin=(1390,'K'), Tmax=(5000,'K')) ], Tmin=(298,'K'), Tmax=(5000,'K'), Cp0=(33.2579,'J/(mol*K)'), CpInf=(719.202,'J/(mol*K)'), comment="""Thermo library: JetSurF0.2"""), molecule=[Molecule(SMILES="[CH2]CCCCCCCCC")]) acetone = Species(label="", thermo=NASA(polynomials=[ NASAPolynomial(coeffs = [3.75568, 0.0264934, -6.55661e-05, 1.94971e-07, -1.82059e-10, -27905.3, 9.0162], Tmin = (10, 'K'), Tmax = (422.477, 'K')), NASAPolynomial(coeffs = [0.701289, 0.0344988, -1.9736e-05, 5.48052e-09, -5.92612e-13, -27460.6, 23.329],Tmin = (422.477, 'K'),Tmax = (3000, 'K')) ], Tmin = (10, 'K'), Tmax = (3000, 'K'), E0 = (-232.025, 'kJ/mol'), Cp0 = (33.2579, 'J/(mol*K)'), CpInf = (232.805, 'J/(mol*K)')), molecule=[Molecule(SMILES="CC(=O)C")]) peracetic_acid = Species(label="", thermo=NASA(polynomials=[ NASAPolynomial(coeffs = [3.81786, 0.016419, 3.32204e-05, -8.98403e-08, 6.63474e-11, -42057.8, 9.65245], Tmin = (10, 'K'), Tmax = (354.579, 'K')), NASAPolynomial(coeffs = [2.75993, 0.0283534, -1.72659e-05, 5.08158e-09, -5.77773e-13, -41982.8, 13.6595], Tmin = (354.579, 'K'), Tmax = (3000, 'K')) ], Tmin = (10, 'K'), Tmax = (3000, 'K'), E0 = (-349.698, 'kJ/mol'),Cp0 = (33.2579, 'J/(mol*K)'), CpInf = (199.547, 'J/(mol*K)')), molecule=[Molecule(SMILES="CC(=O)OO")]) acetic_acid = Species(label="", thermo=NASA(polynomials=[ NASAPolynomial(coeffs = [3.97665, 0.00159915, 8.5542e-05, -1.76486e-07, 1.20201e-10, -53911.5, 8.99309], Tmin = (10, 'K'), Tmax = (375.616, 'K')), NASAPolynomial(coeffs = [1.57088, 0.0272146, -1.67357e-05, 5.01453e-09, -5.82273e-13, -53730.7, 18.2442], Tmin = (375.616, 'K'), Tmax = (3000, 'K')) ], Tmin = (10, 'K'), Tmax = (3000, 'K'), E0 = (-448.245, 'kJ/mol'), Cp0 = (33.2579, 'J/(mol*K)'), CpInf = (182.918, 'J/(mol*K)')), molecule=[Molecule(SMILES="CC(=O)O")]) criegee = Species(label="", thermo=NASA(polynomials=[ NASAPolynomial(coeffs = [3.23876, 0.0679583, -3.35611e-05, 7.91519e-10, 3.13038e-12, -77986, 13.6438], Tmin = (10, 'K'), Tmax = (1053.46, 'K')), NASAPolynomial(coeffs = [9.84525, 0.0536795, -2.86165e-05, 7.39945e-09, -7.48482e-13, -79977.6, -21.4187], Tmin = (1053.46, 'K'), Tmax = (3000, 'K')) ], Tmin = (10, 'K'), Tmax = (3000, 'K'), E0 = (-648.47, 'kJ/mol'),Cp0 = (33.2579, 'J/(mol*K)'), CpInf = (457.296, 'J/(mol*K)')), molecule=[Molecule(SMILES="CC(=O)OOC(C)(O)C")]) self.database = SolvationDatabase() self.database.load(os.path.join(settings['database.directory'], 'solvation')) self.solvent = 'octane' diffusionLimiter.enable(self.database.getSolventData(self.solvent), self.database) self.T = 298 self.uni_reaction = Reaction(reactants=[octyl_pri], products=[octyl_sec]) self.uni_reaction.kinetics = Arrhenius(A=(2.0, '1/s'), n=0, Ea=(0,'kJ/mol')) self.bi_uni_reaction = Reaction(reactants=[octyl_pri, ethane], products=[decyl]) self.bi_uni_reaction.kinetics = Arrhenius(A=(1.0E-22, 'cm^3/molecule/s'), n=0, Ea=(0,'kJ/mol')) self.tri_bi_reaction = Reaction(reactants=[acetone, peracetic_acid, acetic_acid], products=[criegee, acetic_acid]) self.tri_bi_reaction.kinetics = Arrhenius(A=(1.07543e-11, 'cm^6/(mol^2*s)'), n=5.47295, Ea=(-38.5379, 'kJ/mol')) self.intrinsic_rates = { self.uni_reaction: self.uni_reaction.kinetics.getRateCoefficient(self.T, P=100e5), self.bi_uni_reaction: self.bi_uni_reaction.kinetics.getRateCoefficient(self.T, P=100e5), self.tri_bi_reaction: self.tri_bi_reaction.kinetics.getRateCoefficient(self.T, P=100e5), }
class TestSoluteDatabase(TestCase): def setUp(self): self.database = SolvationDatabase() self.database.load(os.path.join(settings['database.directory'], 'solvation')) def runTest(self): pass def testSoluteLibrary(self): "Test we can obtain solute parameters from a library" species = Species(molecule=[Molecule(SMILES='COC=O')]) #methyl formate - we know this is in the solute library libraryData = self.database.getSoluteDataFromLibrary(species, self.database.libraries['solute']) self.assertEqual(len(libraryData), 3) soluteData = self.database.getSoluteData(species) self.assertTrue(isinstance(soluteData, SoluteData)) S = soluteData.S self.assertEqual(S, 0.68) self.assertTrue(soluteData.V is not None) def testMcGowan(self): "Test we can calculate and set the McGowan volume for species containing H,C,O,N or S" self.testCases = [ ['CCCCCCCC', 1.2358], #n-octane, in library ['C(CO)O', 0.5078], #ethylene glycol ['CC#N', 0.4042], #acetonitrile ['CCS', 0.5539] #ethanethiol ] for smiles, volume in self.testCases: species = Species(molecule=[Molecule(SMILES=smiles)]) soluteData = self.database.getSoluteData(species) soluteData.setMcGowanVolume(species) # even if it was found in library, recalculate self.assertTrue(soluteData.V is not None) # so if it wasn't found in library, we should have calculated it self.assertAlmostEqual(soluteData.V, volume) # the volume is what we expect given the atoms and bonds def testDiffusivity(self): "Test that for a given solvent viscosity and temperature we can calculate a solute's diffusivity" species = Species(molecule=[Molecule(SMILES='COC=O')]) soluteData = self.database.getSoluteData(species) T = 298 solventViscosity = 0.001 D = soluteData.getStokesDiffusivity(T, solventViscosity) self.assertAlmostEqual((D*1E12), 0.00000979) def testSolventLibrary(self): "Test we can obtain solvent parameters from a library" solventData = self.database.getSolventData('water') self.assertTrue(solventData is not None) self.assertEqual(solventData.s_h, 2.836) self.assertRaises(DatabaseError, self.database.getSolventData, 'orange_juice') def testViscosity(self): "Test we can calculate the solvent viscosity given a temperature and its A-E correlation parameters" solventData = self.database.getSolventData('water') self.assertAlmostEqual(solventData.getSolventViscosity(298), 0.0009155) def testSoluteGeneration(self): "Test we can estimate Abraham solute parameters correctly using group contributions" self.testCases = [ # from RMG-Java test runs by RWest (mostly in agreement with Jalan et. al. supplementary data) ['1,2-ethanediol', 'C(CO)O', 0.823, 0.685, 0.327, 2.572, 0.693, None], # a nitrogen case #['acetonitrile', 'CC#N', 0.9, 0.33, 0.237, 1.739, 0.04, None], # a sulfur case #['ethanethiol', 'CCS', 0.35, 0.24, 0.392, 2.173, 0, None] ] for name, smiles, S, B, E, L, A, V in self.testCases: species = Species(molecule=[Molecule(SMILES=smiles)]) soluteData = self.database.getSoluteDataFromGroups(Species(molecule=[species.molecule[0]])) self.assertAlmostEqual(soluteData.S, S, places=2) self.assertAlmostEqual(soluteData.B, B, places=2) self.assertAlmostEqual(soluteData.E, E, places=2) self.assertAlmostEqual(soluteData.L, L, places=2) self.assertAlmostEqual(soluteData.A, A, places=2) def testCorrectionGeneration(self): "Test we can estimate solvation thermochemistry." self.testCases = [ # solventName, soluteName, soluteSMILES, Hsolv, Gsolv ['water', 'acetic acid', 'C(C)(=O)O', -56500, -6700*4.184], ['water', 'naphthalene', 'C1=CC=CC2=CC=CC=C12', -42800, -2390*4.184], ['1-octanol', 'octane', 'CCCCCCCC', -40080, -4180*4.184], ['1-octanol', 'tetrahydrofuran', 'C1CCOC1', -28320, -3930*4.184], ['benzene', 'toluene', 'C1(=CC=CC=C1)C', -37660, -5320*4.184], ['benzene', '1,4-dioxane', 'C1COCCO1', -39030, -5210*4.184] ] for solventName, soluteName, smiles, H, G in self.testCases: species = Species(molecule=[Molecule(SMILES=smiles)]) soluteData = self.database.getSoluteData(species) solventData = self.database.getSolventData(solventName) solvationCorrection = self.database.getSolvationCorrection(soluteData, solventData) self.assertAlmostEqual(solvationCorrection.enthalpy / 10000., H / 10000., 0, msg="Solvation enthalpy discrepancy ({2:.0f}!={3:.0f}) for {0} in {1}".format(soluteName, solventName, solvationCorrection.enthalpy, H)) #0 decimal place, in 10kJ. self.assertAlmostEqual(solvationCorrection.gibbs / 10000., G / 10000., 0, msg="Solvation Gibbs free energy discrepancy ({2:.0f}!={3:.0f}) for {0} in {1}".format(soluteName, solventName, solvationCorrection.gibbs, G))