Example #1
0
 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)
Example #2
0
 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),
         }
Example #4
0
 def setUp(self):
     self.database = SolvationDatabase()
     self.database.load(
         os.path.join(settings['database.directory'], 'solvation'))
Example #5
0
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')
Example #6
0
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
                ]))
Example #7
0
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))
Example #8
0
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),
     }
Example #10
0
 def setUp(self):
     self.database = SolvationDatabase()
     self.database.load(os.path.join(settings['database.directory'], 'solvation'))
Example #11
0
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))
Example #12
0
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')
Example #13
0
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),
         }
Example #15
0
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))