Example #1
0
    def testGenerateReverseRateCoefficientPDepArrhenius(self):
        """
        Test the Reaction.generateReverseRateCoefficient() method works for the PDepArrhenius format.
        """
        from rmgpy.kinetics import PDepArrhenius

        arrhenius0 = Arrhenius(
            A = (1.0e6,"s^-1"),
            n = 1.0, 
            Ea = (10.0,"kJ/mol"), 
            T0 = (300.0,"K"), 
            Tmin = (300.0,"K"), 
            Tmax = (2000.0,"K"), 
            comment = """This data is completely made up""",
        )

        arrhenius1 = Arrhenius(
            A = (1.0e12,"s^-1"), 
            n = 1.0, 
            Ea = (20.0,"kJ/mol"), 
            T0 = (300.0,"K"), 
            Tmin = (300.0,"K"), 
            Tmax = (2000.0,"K"), 
            comment = """This data is completely made up""",
        )

        pressures = numpy.array([0.1, 10.0])
        arrhenius = [arrhenius0, arrhenius1]
        Tmin = 300.0
        Tmax = 2000.0
        Pmin = 0.1
        Pmax = 10.0
        comment = """This data is completely made up"""

        original_kinetics = PDepArrhenius(
            pressures = (pressures,"bar"),
            arrhenius = arrhenius,
            Tmin = (Tmin,"K"), 
            Tmax = (Tmax,"K"), 
            Pmin = (Pmin,"bar"), 
            Pmax = (Pmax,"bar"),
            comment = comment,
        )

        self.reaction2.kinetics = original_kinetics

        reverseKinetics = self.reaction2.generateReverseRateCoefficient()

        self.reaction2.kinetics = reverseKinetics
        # reverse reactants, products to ensure Keq is correctly computed
        self.reaction2.reactants, self.reaction2.products = self.reaction2.products, self.reaction2.reactants
        reversereverseKinetics = self.reaction2.generateReverseRateCoefficient()

        # check that reverting the reverse yields the original
        Tlist = numpy.arange(Tmin, Tmax, 200.0, numpy.float64)
        P = 1e5
        for T in Tlist:
            korig = original_kinetics.getRateCoefficient(T, P)
            krevrev = reversereverseKinetics.getRateCoefficient(T, P)
            self.assertAlmostEqual(korig / krevrev, 1.0, 0)
Example #2
0
    def extractKinetics(reactionline):
        """
        Takes a reaction line from RMG and creates Arrhenius object from
        the kinetic data, as well as extracts names of reactants, products and comments.
        Units from RMG-Java are in cm3, mol, s.
        Reference Temperature T0 = 1 K.
        """
        lines = reactionline.split("\t")

        reaction_string = lines[0]
        reactants, products = reaction_string.split(" --> ")
        reactants = reactants.split(" + ")
        products = products.split(" + ")

        if len(reactants) == 1:
            Aunits = "s^-1"
        elif len(reactants) == 2:
            Aunits = "cm**3/mol/s"
        else:  # 3 reactants?
            Aunits = "cm**6/(mol^2*s)"

        kinetics = Arrhenius(
            A=(float(lines[1]), Aunits),
            n=float(lines[2]),
            Ea=(float(lines[3]), "kcal/mol"),
            T0=(1, "K"),
        )

        comments = "\t".join(lines[4:])
        kinetics.comment = "Estimated by RMG-Java:\n" + comments
        entry = Entry(long_desc=comments)

        return reactants, products, kinetics, entry
Example #3
0
    def testGenerateReverseRateCoefficientArrhenius(self):
        """
        Test the Reaction.generateReverseRateCoefficient() method works for the Arrhenius format.
        """
        original_kinetics = Arrhenius(
                    A = (2.65e12, 'cm^3/(mol*s)'),
                    n = 0.0,
                    Ea = (0.0, 'kJ/mol'),
                    T0 = (1, 'K'),
                    Tmin = (300, 'K'),
                    Tmax = (2000, 'K'),
                )
        self.reaction2.kinetics = original_kinetics

        reverseKinetics = self.reaction2.generateReverseRateCoefficient()

        self.reaction2.kinetics = reverseKinetics
        # reverse reactants, products to ensure Keq is correctly computed
        self.reaction2.reactants, self.reaction2.products = self.reaction2.products, self.reaction2.reactants
        reversereverseKinetics = self.reaction2.generateReverseRateCoefficient()

        # check that reverting the reverse yields the original
        Tlist = numpy.arange(original_kinetics.Tmin.value_si, original_kinetics.Tmax.value_si, 200.0, numpy.float64)
        P = 1e5
        for T in Tlist:
            korig = original_kinetics.getRateCoefficient(T, P)
            krevrev = reversereverseKinetics.getRateCoefficient(T, P)
            self.assertAlmostEqual(korig / krevrev, 1.0, 0)
 def extractKinetics(reactionline):
     """
     Takes a reaction line from RMG and creates Arrhenius object from
     the kinetic data, as well as extracts names of reactants, products and comments.
 
     Units from RMG-Java are in cm3, mol, s.
     Reference Temperature T0 = 1 K.
     """
     lines = reactionline.split("\t")
 
     reaction_string = lines[0]
     reactants, products = reaction_string.split(" --> ")
     reactants = reactants.split(" + ")
     products = products.split(" + ")
     
     if len(reactants) == 1:
         Aunits = "s^-1"
     elif len(reactants) == 2:
         Aunits = "cm**3/mol/s"
     else:   # 3 reactants?
         Aunits = "cm**6/(mol^2*s)"
         
     kinetics = Arrhenius(
         A = (float(lines[1]), Aunits),
         n = float(lines[2]),
         Ea = (float(lines[3]),"kcal/mol"),
         T0 = (1,"K"),
     )
 
     comments = "\t".join(lines[4:])
     kinetics.comment = "Estimated by RMG-Java:\n"+comments
     entry = Entry(longDesc=comments)
 
     return reactants, products, kinetics, entry
Example #5
0
def generateKineticsModel(reaction, tunneling='', plot=False):
    
    logging.info('Calculating rate coefficient for {0}...'.format(reaction))
    
    if len(reaction.reactants) == 1:
        kunits = 's^-1'
    elif len(reaction.reactants) == 2:
        kunits = 'm^3/(mol*s)'
    elif len(reaction.reactants) == 3:
        kunits = 'm^6/(mol^2*s)'
    else:
        kunits = ''
    
    Tlist = 1000.0/numpy.arange(0.4, 3.35, 0.05)
    klist = reaction.calculateTSTRateCoefficients(Tlist, tunneling)
    arrhenius = Arrhenius().fitToData(Tlist, klist, kunits)
    klist2 = arrhenius.getRateCoefficients(Tlist)
    
    reaction.kinetics = arrhenius
    
    if plot:
        logging.info('Plotting kinetics model for {0}...'.format(reaction))
        import pylab
        pylab.semilogy(1000.0 / Tlist, klist  * reaction.degeneracy, 'ok')
        pylab.semilogy(1000.0 / Tlist, klist2 * reaction.degeneracy, '-k')
        pylab.xlabel('1000 / Temperature (1000/K)')
        pylab.ylabel('Rate coefficient (SI units)')
        pylab.show()
Example #6
0
    def testGenerateReverseRateCoefficientMultiArrhenius(self):
        """
        Test the Reaction.generateReverseRateCoefficient() method works for the MultiArrhenius format.
        """
        from rmgpy.kinetics import MultiArrhenius

        pressures = numpy.array([0.1, 10.0])
        Tmin = 300.0
        Tmax = 2000.0
        Pmin = 0.1
        Pmax = 10.0
        comment = """This data is completely made up"""

        arrhenius = [
            Arrhenius(
                A = (9.3e-14,"cm^3/(molecule*s)"),
                n = 0.0,
                Ea = (4740*constants.R*0.001,"kJ/mol"),
                T0 = (1,"K"),
                Tmin = (Tmin,"K"),
                Tmax = (Tmax,"K"),
                comment = comment,
            ),
            Arrhenius(
                A = (1.4e-9,"cm^3/(molecule*s)"),
                n = 0.0,
                Ea = (11200*constants.R*0.001,"kJ/mol"),
                T0 = (1,"K"),
                Tmin = (Tmin,"K"),
                Tmax = (Tmax,"K"),
                comment = comment,
            ),
        ]

        original_kinetics = MultiArrhenius(
            arrhenius = arrhenius,
            Tmin = (Tmin,"K"),
            Tmax = (Tmax,"K"),
            comment = comment,
        )

        self.reaction2.kinetics = original_kinetics

        reverseKinetics = self.reaction2.generateReverseRateCoefficient()

        self.reaction2.kinetics = reverseKinetics
        # reverse reactants, products to ensure Keq is correctly computed
        self.reaction2.reactants, self.reaction2.products = self.reaction2.products, self.reaction2.reactants
        reversereverseKinetics = self.reaction2.generateReverseRateCoefficient()

        # check that reverting the reverse yields the original
        Tlist = numpy.arange(Tmin, Tmax, 200.0, numpy.float64)
        P = 1e5
        for T in Tlist:
            korig = original_kinetics.getRateCoefficient(T, P)
            krevrev = reversereverseKinetics.getRateCoefficient(T, P)
            self.assertAlmostEqual(korig / krevrev, 1.0, 0)
Example #7
0
    def testGenerateReverseRateCoefficientLindemann(self):
        """
        Test the Reaction.generateReverseRateCoefficient() method works for the Lindemann format.
        """

        from rmgpy.kinetics import Lindemann

        arrheniusHigh = Arrhenius(
            A = (1.39e+16,"cm^3/(mol*s)"), 
            n = -0.534, 
            Ea = (2.243,"kJ/mol"), 
            T0 = (1,"K"),
        )
        arrheniusLow = Arrhenius(
            A = (2.62e+33,"cm^6/(mol^2*s)"), 
            n = -4.76, 
            Ea = (10.21,"kJ/mol"), 
            T0 = (1,"K"),
        )
        efficiencies = {"C": 3, "C(=O)=O": 2, "CC": 3, "O": 6, "[Ar]": 0.7, "[C]=O": 1.5, "[H][H]": 2}
        Tmin = 300.
        Tmax = 2000.
        Pmin = 0.01
        Pmax = 100.
        comment = """H + CH3 -> CH4"""
        lindemann = Lindemann(
            arrheniusHigh = arrheniusHigh,
            arrheniusLow = arrheniusLow,
            Tmin = (Tmin,"K"),
            Tmax = (Tmax,"K"),
            Pmin = (Pmin,"bar"),
            Pmax = (Pmax,"bar"),
            efficiencies = efficiencies,
            comment = comment,
        )
         
        original_kinetics = lindemann
        
        self.reaction2.kinetics = original_kinetics

        reverseKinetics = self.reaction2.generateReverseRateCoefficient()

        self.reaction2.kinetics = reverseKinetics
        # reverse reactants, products to ensure Keq is correctly computed
        self.reaction2.reactants, self.reaction2.products = self.reaction2.products, self.reaction2.reactants
        reversereverseKinetics = self.reaction2.generateReverseRateCoefficient()

        # check that reverting the reverse yields the original
        Tlist = numpy.arange(Tmin, Tmax, 200.0, numpy.float64)
        P = 1e5
        for T in Tlist:
            korig = original_kinetics.getRateCoefficient(T, P)
            krevrev = reversereverseKinetics.getRateCoefficient(T, P)
            self.assertAlmostEqual(korig / krevrev, 1.0, 0)
Example #8
0
 def generate_high_p_limit_kinetics(self):
     """
     If the LibraryReactions represented by `self` has pressure dependent kinetics,
     try extracting the high pressure limit rate from it.
     Used for incorporating library reactions with pressure-dependent kinetics in PDep networks.
     Only reactions flagged as `elementary_high_p=True` should be processed here.
     If the kinetics is a :class:Lindemann or a :class:Troe, simply get the high pressure limit rate.
     If the kinetics is a :class:PDepArrhenius or a :class:Chebyshev, generate a :class:Arrhenius kinetics entry
     that represents the high pressure limit if Pmax >= 90 bar .
     This high pressure limit Arrhenius kinetics is assigned to the reaction network_kinetics attribute.
     If this method successfully generated the high pressure limit kinetics, return ``True``, otherwise ``False``.
     """
     logging.debug("Generating high pressure limit kinetics for {0}...".format(self))
     if not self.is_unimolecular():
         return False
     if isinstance(self.kinetics, Arrhenius):
         return self.elementary_high_p
     if self.network_kinetics is not None:
         return True
     if self.elementary_high_p:
         if isinstance(self.kinetics, (Lindemann, Troe)):
             self.network_kinetics = self.kinetics.arrheniusHigh
             self.network_kinetics.comment = self.kinetics.comment
             self.network_kinetics.comment = "Kinetics taken from the arrheniusHigh attribute of a" \
                 " Troe/Lindemann exprssion. Originally from reaction library {0}".format(self.library)
             return True
         if isinstance(self.kinetics, PDepArrhenius):
             if self.kinetics.pressures.value_si[-1] >= 9000000:  # Pa units
                 if isinstance(self.kinetics.arrhenius[-1], Arrhenius):
                     self.network_kinetics = self.kinetics.arrhenius[-1]
                     return True
                 else:
                     # This is probably MultiArrhenius entries inside a PDepArrhenius kinetics entry. Don't process
                     return False
         if isinstance(self.kinetics, Chebyshev):
             if self.kinetics.Pmax.value_si >= 9000000:  # Pa units
                 if len(self.reactants) == 1:
                     kunits = 's^-1'
                 elif len(self.reactants) == 2:
                     kunits = 'm^3/(mol*s)'
                 elif len(self.reactants) == 3:
                     kunits = 'm^6/(mol^2*s)'
                 else:
                     kunits = ''
                 t_step = (self.kinetics.Tmax.value_si - self.kinetics.Tmin.value_si) / 20
                 t_list = np.arange(int(self.kinetics.Tmin.value_si), int(self.kinetics.Tmax.value_si), int(t_step))
                 if t_list[-1] < int(self.kinetics.Tmax.value_si):
                     t_list = np.insert(t_list, -1, [int(self.kinetics.Tmax.value_si)])
                 k_list = []
                 for t in t_list:
                     k_list.append(self.kinetics.get_rate_coefficient(t, self.kinetics.Pmax.value_si))
                 k_list = np.array(k_list)
                 self.network_kinetics = Arrhenius().fit_to_data(Tlist=t_list, klist=k_list, kunits=kunits)
                 return True
         logging.info("NOT processing reaction {0} in a pressure-dependent reaction network.\n"
                      "Although it is marked with the `elementary_high_p=True` flag,"
                      " it doesn't answer either of the following criteria:\n1. Has a Lindemann or Troe"
                      " kinetics type; 2. Has a PDepArrhenius or Chebyshev kinetics type and has valid"
                      " kinetics at P >= 100 bar.\n".format(self))
     return False
Example #9
0
 def __multiplyKineticsData(self, kinetics1, kinetics2):
     """
     Multiply two kinetics objects `kinetics1` and `kinetics2` of the same
     class together, returning their product as a new kinetics object of 
     that class. Currently this only works for :class:`KineticsData` or
     :class:`Arrhenius` objects.
     """
     if isinstance(kinetics1, KineticsData) and isinstance(kinetics2, KineticsData):
         if len(kinetics1.Tdata.value_si) != len(kinetics2.Tdata.value_si) or any([T1 != T2 for T1, T2 in zip(kinetics1.Tdata.value_si, kinetics2.Tdata.value_si)]):
             raise KineticsError('Cannot add these KineticsData objects due to their having different temperature points.')
         kinetics = KineticsData(
             Tdata = (kinetics1.Tdata.value, kinetics2.Tdata.units),
             kdata = (kinetics1.kdata.value * kinetics2.kdata.value, kinetics1.kdata.units),
         )
     elif isinstance(kinetics1, Arrhenius) and isinstance(kinetics2, Arrhenius):
         assert kinetics1.A.units == kinetics2.A.units
         assert kinetics1.Ea.units == kinetics2.Ea.units
         assert kinetics1.T0.units == kinetics2.T0.units
         assert kinetics1.T0.value == kinetics2.T0.value
         kinetics = Arrhenius(
             A = (kinetics1.A.value * kinetics2.A.value, kinetics1.A.units),
             n = (kinetics1.n.value + kinetics2.n.value, kinetics1.n.units),
             Ea = (kinetics1.Ea.value + kinetics2.Ea.value, kinetics1.Ea.units),
             T0 = (kinetics1.T0.value, kinetics1.T0.units),
         )
     else:
         raise KineticsError('Unable to multiply kinetics types "{0}" and "{1}".'.format(kinetics1.__class__, kinetics2.__class__))
     
     if kinetics1.Tmin is not None and kinetics2.Tmin is not None:
         kinetics.Tmin = kinetics1.Tmin if kinetics1.Tmin.value_si > kinetics2.Tmin.value_si else kinetics2.Tmin
     elif kinetics1.Tmin is not None and kinetics2.Tmin is None:
         kinetics.Tmin = kinetics1.Tmin
     elif kinetics1.Tmin is None and kinetics2.Tmin is not None:
         kinetics.Tmin = kinetics2.Tmin
     
     if kinetics1.Tmax is not None and kinetics2.Tmax is not None:
         kinetics.Tmax = kinetics1.Tmax if kinetics1.Tmax.value_si < kinetics2.Tmax.value_si else kinetics2.Tmax
     elif kinetics1.Tmax is not None and kinetics2.Tmax is None:
         kinetics.Tmax = kinetics1.Tmax
     elif kinetics1.Tmax is None and kinetics2.Tmax is not None:
         kinetics.Tmax = kinetics2.Tmax
     
     if kinetics1.Pmin is not None and kinetics2.Pmin is not None:
         kinetics.Pmin = kinetics1.Pmin if kinetics1.Pmin.value_si > kinetics2.Pmin.value_si else kinetics2.Pmin
     elif kinetics1.Pmin is not None and kinetics2.Pmin is None:
         kinetics.Pmin = kinetics1.Pmin
     elif kinetics1.Pmin is None and kinetics2.Pmin is not None:
         kinetics.Pmin = kinetics2.Pmin
     
     if kinetics1.Pmax is not None and kinetics2.Pmax is not None:
         kinetics.Pmax = kinetics1.Pmax if kinetics1.Pmax.value_si < kinetics2.Pmax.value_si else kinetics2.Pmax
     elif kinetics1.Pmax is not None and kinetics2.Pmax is None:
         kinetics.Pmax = kinetics1.Pmax
     elif kinetics1.Pmax is None and kinetics2.Pmax is not None:
         kinetics.Pmax = kinetics2.Pmax
     
     if kinetics1.comment == '': kinetics.comment = kinetics2.comment
     elif kinetics2.comment == '': kinetics.comment = kinetics1.comment
     else: kinetics.comment = kinetics1.comment + ' + ' + kinetics2.comment
     return kinetics
Example #10
0
    def test_compute_flux(self):
        """
        Test the liquid batch reactor with a simple kinetic model. 
        """

        rxn1 = Reaction(reactants=[self.C2H6, self.CH3],
                        products=[self.C2H5, self.CH4],
                        kinetics=Arrhenius(A=(686.375 * 6, 'm^3/(mol*s)'),
                                           n=4.40721,
                                           Ea=(7.82799, 'kcal/mol'),
                                           T0=(298.15, 'K')))

        core_species = [self.CH4, self.CH3, self.C2H6, self.C2H5]
        edge_species = []
        core_reactions = [rxn1]
        edge_reactions = []

        c0 = {self.C2H5: 0.1, self.CH3: 0.1, self.CH4: 0.4, self.C2H6: 0.4}

        rxn_system = LiquidReactor(self.T, c0, 1, termination=[])

        rxn_system.initialize_model(core_species, core_reactions, edge_species,
                                    edge_reactions)

        tlist = np.array([10**(i / 10.0) for i in range(-130, -49)],
                         np.float64)

        # Integrate to get the solution at each time point
        t, y, reaction_rates, species_rates = [], [], [], []
        for t1 in tlist:
            rxn_system.advance(t1)
            t.append(rxn_system.t)
            # You must make a copy of y because it is overwritten by DASSL at
            # each call to advance()
            y.append(rxn_system.y.copy())
            reaction_rates.append(rxn_system.core_reaction_rates.copy())
            species_rates.append(rxn_system.core_species_rates.copy())

        # Convert the solution vectors to np arrays
        t = np.array(t, np.float64)
        reaction_rates = np.array(reaction_rates, np.float64)
        species_rates = np.array(species_rates, np.float64)

        # Check that we're computing the species fluxes correctly
        for i in range(t.shape[0]):
            self.assertAlmostEqual(reaction_rates[i, 0],
                                   species_rates[i, 0],
                                   delta=1e-6 * reaction_rates[i, 0])
            self.assertAlmostEqual(reaction_rates[i, 0],
                                   -species_rates[i, 1],
                                   delta=1e-6 * reaction_rates[i, 0])
            self.assertAlmostEqual(reaction_rates[i, 0],
                                   -species_rates[i, 2],
                                   delta=1e-6 * reaction_rates[i, 0])
            self.assertAlmostEqual(reaction_rates[i, 0],
                                   species_rates[i, 3],
                                   delta=1e-6 * reaction_rates[i, 0])

        # Check that we've reached equilibrium
        self.assertAlmostEqual(reaction_rates[-1, 0], 0.0, delta=1e-2)
Example #11
0
    def test_kinetics_io(self):

        self.io.reaction.get_rmg_reaction()
        self.io.reaction.rmg_reaction.kinetics = Arrhenius()

        self.assertTrue(self.io.save_kinetics())
        self.assertIsInstance(self.io.read_kinetics_file(), dict)
def load_pseudo_fragment_reactions(fragments_dict):
    """
    Currently only returns a pseudo reaction. It can be
    extended to generate multiple ractions in the future.
    """

    pseudo_fragrxts = ['RC*C__C', 'RCCCCR']
    pseudo_fragprds = ['RCCCCC__CC*']
    pseudo_frag_pairs = [('RC*C__C', 'RCCCCC__CC*'), ('RCCCCR', 'RCCCCC__CC*')]

    fragrxts = [fragments_dict[label] for label in pseudo_fragrxts]

    fragprds = [fragments_dict[label] for label in pseudo_fragprds]

    fragpairs = [(fragments_dict[rxt_label], fragments_dict[prod_label])
                 for rxt_label, prod_label in pseudo_frag_pairs]

    pseudo_kinetics = Arrhenius(A=(2.000e+05, 'cm^3/(mol*s)'),
                                n=0.0,
                                Ea=(0.0, 'kcal/mol'),
                                T0=(1, 'K'))

    pseudo_fragrxn = FragmentReaction(index=-1,
                                      reactants=fragrxts,
                                      products=fragprds,
                                      kinetics=pseudo_kinetics,
                                      reversible=False,
                                      pairs=fragpairs,
                                      family='pseudo_rxn')

    return [pseudo_fragrxn]
Example #13
0
 def testTSTCalculation(self):
     """
     A test of the transition state theory k(T) calculation function,
     using the reaction H + C2H4 -> C2H5.
     """
     Tlist = 1000.0/numpy.arange(0.4, 3.35, 0.01)
     klist = numpy.array([self.reaction.calculateTSTRateCoefficient(T) for T in Tlist])
     arrhenius = Arrhenius().fitToData(Tlist, klist, kunits='m^3/(mol*s)')
     klist2 = numpy.array([arrhenius.getRateCoefficient(T) for T in Tlist])
     
     # Check that the correct Arrhenius parameters are returned
     self.assertAlmostEqual(arrhenius.A.value_si, 2265.2488, delta=1e-2)
     self.assertAlmostEqual(arrhenius.n.value_si, 1.45419, delta=1e-4)
     self.assertAlmostEqual(arrhenius.Ea.value_si, 6645.24, delta=1e-2)
     # Check that the fit is satisfactory (defined here as always within 5%)
     for i in range(len(Tlist)):
         self.assertAlmostEqual(klist[i], klist2[i], delta=5e-2 * klist[i])
Example #14
0
 def testTSTCalculation(self):
     """
     A test of the transition state theory k(T) calculation function,
     using the reaction H + C2H4 -> C2H5.
     """
     Tlist = 1000.0/numpy.arange(0.4, 3.35, 0.01)
     klist = numpy.array([self.reaction.calculateTSTRateCoefficient(T) for T in Tlist])
     arrhenius = Arrhenius().fitToData(Tlist, klist, kunits='m^3/(mol*s)')
     klist2 = numpy.array([arrhenius.getRateCoefficient(T) for T in Tlist])
     
     # Check that the correct Arrhenius parameters are returned
     self.assertAlmostEqual(arrhenius.A.value_si, 2265.2488, delta=1e-2)
     self.assertAlmostEqual(arrhenius.n.value_si, 1.45419, delta=1e-4)
     self.assertAlmostEqual(arrhenius.Ea.value_si, 6645.24, delta=1e-2)
     # Check that the fit is satisfactory (defined here as always within 5%)
     for i in range(len(Tlist)):
         self.assertAlmostEqual(klist[i], klist2[i], delta=5e-2 * klist[i])
Example #15
0
 def testTSTCalculation(self):
     """
     A test of the transition state theory k(T) calculation function,
     using the reaction H + C2H4 -> C2H5.
     """
     Tlist = 1000.0/numpy.arange(0.4, 3.35, 0.05)
     klist = self.reaction.calculateTSTRateCoefficients(Tlist, tunneling='')
     arrhenius = Arrhenius().fitToData(Tlist, klist, kunits='')
     klist2 = arrhenius.getRateCoefficients(Tlist)
     
     # Check that the correct Arrhenius parameters are returned
     self.assertAlmostEqual(arrhenius.A.value/1.07506e+07, 1.0, 3)
     self.assertAlmostEqual(arrhenius.n.value/1.47803, 1.0, 3)
     self.assertAlmostEqual(arrhenius.Ea.value/10194., 1.0, 3)
     # Check that the fit is satisfactory
     for i in range(len(Tlist)):
         self.assertTrue(abs(1 - klist2[i] / klist[i]) < 0.01)
Example #16
0
    def test_corespecies_rate(self):
        """
        Test if a specific core species rate is equal to 0 over time.
        """

        c0 = {self.C2H5: 0.1, self.CH3: 0.1, self.CH4: 0.4, self.C2H6: 0.4}
        rxn1 = Reaction(reactants=[self.C2H6, self.CH3],
                        products=[self.C2H5, self.CH4],
                        kinetics=Arrhenius(A=(686.375 * 6, 'm^3/(mol*s)'),
                                           n=4.40721,
                                           Ea=(7.82799, 'kcal/mol'),
                                           T0=(298.15, 'K')))

        core_species = [self.CH4, self.CH3, self.C2H6, self.C2H5]
        edge_species = []
        core_reactions = [rxn1]
        edge_reactions = []
        sensitivity = []
        termination_conversion = []
        sensitivity_threshold = 0.001
        const_species = ["CH4"]
        sens_conds = {
            self.C2H5: 0.1,
            self.CH3: 0.1,
            self.CH4: 0.4,
            self.C2H6: 0.4,
            'T': self.T
        }

        rxn_system = LiquidReactor(self.T,
                                   c0,
                                   1,
                                   termination_conversion,
                                   sensitivity,
                                   sensitivity_threshold,
                                   const_spc_names=const_species,
                                   sens_conditions=sens_conds)
        # The test regarding the writing of constantSPCindices from input file is check with the previous test.
        rxn_system.const_spc_indices = [0]

        rxn_system.initialize_model(core_species, core_reactions, edge_species,
                                    edge_reactions)

        tlist = np.array([10**(i / 10.0) for i in range(-130, -49)],
                         np.float64)

        # Integrate to get the solution at each time point
        t, y, reaction_rates, species_rates = [], [], [], []
        for t1 in tlist:
            rxn_system.advance(t1)
            t.append(rxn_system.t)
            self.assertEqual(
                rxn_system.core_species_rates[0], 0,
                "Core species rate has to be equal to 0 for species hold constant. "
                "Here it is equal to {0}".format(
                    rxn_system.core_species_rates[0]))
 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 #18
0
    def testGenerateReverseRateCoefficientArrhenius(self):
        """
        Test the Reaction.generateReverseRateCoefficient() method works for the Arrhenius format.
        """
        original_kinetics = Arrhenius(
            A=(2.65e12, "cm^3/(mol*s)"), n=0.0, Ea=(0.0, "kJ/mol"), T0=(1, "K"), Tmin=(300, "K"), Tmax=(2000, "K")
        )
        self.reaction2.kinetics = original_kinetics

        reverseKinetics = self.reaction2.generateReverseRateCoefficient()

        self.reaction2.kinetics = reverseKinetics
        # reverse reactants, products to ensure Keq is correctly computed
        self.reaction2.reactants, self.reaction2.products = self.reaction2.products, self.reaction2.reactants
        reversereverseKinetics = self.reaction2.generateReverseRateCoefficient()

        # check that reverting the reverse yields the original
        Tlist = numpy.arange(original_kinetics.Tmin.value_si, original_kinetics.Tmax.value_si, 200.0, numpy.float64)
        P = 1e5
        for T in Tlist:
            korig = original_kinetics.getRateCoefficient(T, P)
            krevrev = reversereverseKinetics.getRateCoefficient(T, P)
            self.assertAlmostEqual(korig / krevrev, 1.0, 0)
Example #19
0
    def generate_group_additivity_values(self, training_set, kunits, method='Arrhenius'):
        """
        Generate the group additivity values using the given `training_set`,
        a list of 2-tuples of the form ``(template, kinetics)``. You must also
        specify the `kunits` for the family and the `method` to use when
        generating the group values. Returns ``True`` if the group values have
        changed significantly since the last time they were fitted, or ``False``
        otherwise.
        """
        warnings.warn("Group additivity is no longer supported and may be"
                      " removed in version 2.3.", DeprecationWarning)
        # keep track of previous values so we can detect if they change
        old_entries = dict()
        for label, entry in self.entries.items():
            if entry.data is not None:
                old_entries[label] = entry.data

        # Determine a complete list of the entries in the database, sorted as in the tree
        group_entries = self.top[:]
        for entry in self.top:
            group_entries.extend(self.descendants(entry))

        # Determine a unique list of the groups we will be able to fit parameters for
        group_list = []
        for template, kinetics in training_set:
            for group in template:
                if group not in self.top:
                    group_list.append(group)
                    group_list.extend(self.ancestors(group)[:-1])
        group_list = list(set(group_list))
        group_list.sort(key=lambda x: x.index)

        if method == 'KineticsData':
            # Fit a discrete set of k(T) data points by training against k(T) data

            Tdata = np.array([300, 400, 500, 600, 800, 1000, 1500, 2000])

            # Initialize dictionaries of fitted group values and uncertainties
            group_values = {}
            group_uncertainties = {}
            group_counts = {}
            group_comments = {}
            for entry in group_entries:
                group_values[entry] = []
                group_uncertainties[entry] = []
                group_counts[entry] = []
                group_comments[entry] = set()

            # Generate least-squares matrix and vector
            A = []
            b = []

            kdata = []
            for template, kinetics in training_set:

                if isinstance(kinetics, (Arrhenius, KineticsData)):
                    kd = [kinetics.get_rate_coefficient(T) for T in Tdata]
                elif isinstance(kinetics, ArrheniusEP):
                    kd = [kinetics.get_rate_coefficient(T, 0) for T in Tdata]
                else:
                    raise TypeError('Unexpected kinetics model of type {0} for template '
                                    '{1}.'.format(kinetics.__class__, template))
                kdata.append(kd)

                # Create every combination of each group and its ancestors with each other
                combinations = []
                for group in template:
                    groups = [group]
                    groups.extend(self.ancestors(group))
                    combinations.append(groups)
                combinations = get_all_combinations(combinations)
                # Add a row to the matrix for each combination
                for groups in combinations:
                    Arow = [1 if group in groups else 0 for group in group_list]
                    Arow.append(1)
                    brow = [math.log10(k) for k in kd]
                    A.append(Arow)
                    b.append(brow)

                    for group in groups:
                        group_comments[group].add("{0!s}".format(template))

            if len(A) == 0:
                logging.warning('Unable to fit kinetics groups for family "{0}"; '
                                'no valid data found.'.format(self.label))
                return
            A = np.array(A)
            b = np.array(b)
            kdata = np.array(kdata)

            x, residues, rank, s = np.linalg.lstsq(A, b, rcond=RCOND)

            for t, T in enumerate(Tdata):

                # Determine error in each group (on log scale)
                stdev = np.zeros(len(group_list) + 1, np.float64)
                count = np.zeros(len(group_list) + 1, np.int)

                for index in range(len(training_set)):
                    template, kinetics = training_set[index]
                    kd = math.log10(kdata[index, t])
                    km = x[-1, t] + sum([x[group_list.index(group), t] for group in template if group in group_list])
                    variance = (km - kd) ** 2
                    for group in template:
                        groups = [group]
                        groups.extend(self.ancestors(group))
                        for g in groups:
                            if g not in self.top:
                                ind = group_list.index(g)
                                stdev[ind] += variance
                                count[ind] += 1
                    stdev[-1] += variance
                    count[-1] += 1
                stdev = np.sqrt(stdev / (count - 1))
                import scipy.stats
                ci = scipy.stats.t.ppf(0.975, count - 1) * stdev

                # Update dictionaries of fitted group values and uncertainties
                for entry in group_entries:
                    if entry == self.top[0]:
                        group_values[entry].append(10 ** x[-1, t])
                        group_uncertainties[entry].append(10 ** ci[-1])
                        group_counts[entry].append(count[-1])
                    elif entry in group_list:
                        index = group_list.index(entry)
                        group_values[entry].append(10 ** x[index, t])
                        group_uncertainties[entry].append(10 ** ci[index])
                        group_counts[entry].append(count[index])
                    else:
                        group_values[entry] = None
                        group_uncertainties[entry] = None
                        group_counts[entry] = None

            # Store the fitted group values and uncertainties on the associated entries
            for entry in group_entries:
                if group_values[entry] is not None:
                    entry.data = KineticsData(Tdata=(Tdata, "K"), kdata=(group_values[entry], kunits))
                    if not any(np.isnan(np.array(group_uncertainties[entry]))):
                        entry.data.kdata.uncertainties = np.array(group_uncertainties[entry])
                        entry.data.kdata.uncertainty_type = '*|/'
                    entry.short_desc = "Group additive kinetics."
                    entry.long_desc = "Fitted to {0} rates.\n".format(group_counts[entry])
                    entry.long_desc += "\n".join(group_comments[entry])
                else:
                    entry.data = None

        elif method == 'Arrhenius':
            # Fit Arrhenius parameters (A, n, Ea) by training against k(T) data

            Tdata = np.array([300, 400, 500, 600, 800, 1000, 1500, 2000])
            logTdata = np.log(Tdata)
            Tinvdata = 1000. / (constants.R * Tdata)

            A = []
            b = []

            kdata = []
            for template, kinetics in training_set:

                if isinstance(kinetics, (Arrhenius, KineticsData)):
                    kd = [kinetics.get_rate_coefficient(T) for T in Tdata]
                elif isinstance(kinetics, ArrheniusEP):
                    kd = [kinetics.get_rate_coefficient(T, 0) for T in Tdata]
                else:
                    raise TypeError('Unexpected kinetics model of type {0} for template '
                                    '{1}.'.format(kinetics.__class__, template))
                kdata.append(kd)

                # Create every combination of each group and its ancestors with each other
                combinations = []
                for group in template:
                    groups = [group]
                    groups.extend(self.ancestors(group))
                    combinations.append(groups)
                combinations = get_all_combinations(combinations)

                # Add a row to the matrix for each combination at each temperature
                for t, T in enumerate(Tdata):
                    logT = logTdata[t]
                    Tinv = Tinvdata[t]
                    for groups in combinations:
                        Arow = []
                        for group in group_list:
                            if group in groups:
                                Arow.extend([1, logT, -Tinv])
                            else:
                                Arow.extend([0, 0, 0])
                        Arow.extend([1, logT, -Tinv])
                        brow = math.log(kd[t])
                        A.append(Arow)
                        b.append(brow)

            if len(A) == 0:
                logging.warning('Unable to fit kinetics groups for family "{0}"; '
                                'no valid data found.'.format(self.label))
                return
            A = np.array(A)
            b = np.array(b)
            kdata = np.array(kdata)

            x, residues, rank, s = np.linalg.lstsq(A, b, rcond=RCOND)

            # Store the results
            self.top[0].data = Arrhenius(
                A=(math.exp(x[-3]), kunits),
                n=x[-2],
                Ea=(x[-1], "kJ/mol"),
                T0=(1, "K"),
            )
            for i, group in enumerate(group_list):
                group.data = Arrhenius(
                    A=(math.exp(x[3 * i]), kunits),
                    n=x[3 * i + 1],
                    Ea=(x[3 * i + 2], "kJ/mol"),
                    T0=(1, "K"),
                )

        elif method == 'Arrhenius2':
            # Fit Arrhenius parameters (A, n, Ea) by training against (A, n, Ea) values

            A = []
            b = []

            for template, kinetics in training_set:

                # Create every combination of each group and its ancestors with each other
                combinations = []
                for group in template:
                    groups = [group]
                    groups.extend(self.ancestors(group))
                    combinations.append(groups)
                combinations = get_all_combinations(combinations)

                # Add a row to the matrix for each parameter
                if (isinstance(kinetics, Arrhenius) or
                        (isinstance(kinetics, ArrheniusEP) and kinetics.alpha.value_si == 0)):
                    for groups in combinations:
                        Arow = []
                        for group in group_list:
                            if group in groups:
                                Arow.append(1)
                            else:
                                Arow.append(0)
                        Arow.append(1)
                        Ea = kinetics.E0.value_si if isinstance(kinetics, ArrheniusEP) else kinetics.Ea.value_si
                        brow = [math.log(kinetics.A.value_si), kinetics.n.value_si, Ea / 1000.]
                        A.append(Arow)
                        b.append(brow)

            if len(A) == 0:
                logging.warning('Unable to fit kinetics groups for family "{0}"; '
                                'no valid data found.'.format(self.label))
                return
            A = np.array(A)
            b = np.array(b)

            x, residues, rank, s = np.linalg.lstsq(A, b, rcond=RCOND)

            # Store the results
            self.top[0].data = Arrhenius(
                A=(math.exp(x[-1, 0]), kunits),
                n=x[-1, 1],
                Ea=(x[-1, 2], "kJ/mol"),
                T0=(1, "K"),
            )
            for i, group in enumerate(group_list):
                group.data = Arrhenius(
                    A=(math.exp(x[i, 0]), kunits),
                    n=x[i, 1],
                    Ea=(x[i, 2], "kJ/mol"),
                    T0=(1, "K"),
                )

        # Add a note to the history of each changed item indicating that we've generated new group values
        changed = False
        for label, entry in self.entries.items():
            if entry.data is not None and label in old_entries:
                if (isinstance(entry.data, KineticsData) and
                        isinstance(old_entries[label], KineticsData) and
                        len(entry.data.kdata.value_si) == len(old_entries[label].kdata.value_si) and
                        all(abs(entry.data.kdata.value_si / old_entries[label].kdata.value_si - 1) < 0.01)):
                    # New group values within 1% of old
                    pass
                elif (isinstance(entry.data, Arrhenius) and
                        isinstance(old_entries[label], Arrhenius) and
                        abs(entry.data.A.value_si / old_entries[label].A.value_si - 1) < 0.01 and
                        abs(entry.data.n.value_si / old_entries[label].n.value_si - 1) < 0.01 and
                        abs(entry.data.Ea.value_si / old_entries[label].Ea.value_si - 1) < 0.01 and
                        abs(entry.data.T0.value_si / old_entries[label].T0.value_si - 1) < 0.01):
                    # New group values within 1% of old
                    pass
                else:
                    changed = True
                    break
            else:
                changed = True
                break

        return changed
 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 #21
0
    def getForwardReactionForFamilyEntry(self, entry, family, thermoDatabase):
        """
        For a given `entry` for a reaction of the given reaction `family` (the
        string label of the family), return the reaction with kinetics and
        degeneracy for the "forward" direction as defined by the reaction 
        family. For families that are their own reverse, the direction the
        kinetics is given in will be preserved. If the entry contains 
        functional groups for the reactants, assume that it is given in the 
        forward direction and do nothing. Returns the reaction in the direction
        consistent with the reaction family template, and the matching template.
        Note that the returned reaction will have its kinetics and degeneracy
        set appropriately.
        
        In order to reverse the reactions that are given in the reverse of the
        direction the family is defined, we need to compute the thermodynamics
        of the reactants and products. For this reason you must also pass
        the `thermoDatabase` to use to generate the thermo data.
        """

        def generateThermoData(species, thermoDatabase):
            thermoData = [thermoDatabase.getThermoData(species)]
            thermoData.sort(key=lambda x: x.getEnthalpy(298))
            return thermoData[0]
        
        def matchSpeciesToMolecules(species, molecules):
            if len(species) == len(molecules) == 1:
                return species[0].isIsomorphic(molecules[0])
            elif len(species) == len(molecules) == 2:
                if species[0].isIsomorphic(molecules[0]) and species[1].isIsomorphic(molecules[1]):
                    return True
                elif species[0].isIsomorphic(molecules[1]) and species[1].isIsomorphic(molecules[0]):
                    return True
            return False

        reaction = None; template = None

        # Get the indicated reaction family
        try:
            groups = self.families[family].groups
        except KeyError:
            raise ValueError('Invalid value "{0}" for family parameter.'.format(family))

        if all([(isinstance(reactant, Group) or isinstance(reactant, LogicNode)) for reactant in entry.item.reactants]):
            # The entry is a rate rule, containing functional groups only
            # By convention, these are always given in the forward direction and
            # have kinetics defined on a per-site basis
            reaction = Reaction(
                reactants = entry.item.reactants[:],
                products = [],
                kinetics = entry.data,
                degeneracy = 1,
            )
            template = [groups.entries[label] for label in entry.label.split(';')]

        elif (all([isinstance(reactant, Molecule) for reactant in entry.item.reactants]) and
            all([isinstance(product, Molecule) for product in entry.item.products])):
            # The entry is a real reaction, containing molecules
            # These could be defined for either the forward or reverse direction
            # and could have a reaction-path degeneracy

            reaction = Reaction(reactants=[], products=[])
            for molecule in entry.item.reactants:
                reactant = Species(molecule=[molecule])
                reactant.generateResonanceIsomers()
                reactant.thermo = generateThermoData(reactant, thermoDatabase)
                reaction.reactants.append(reactant)
            for molecule in entry.item.products:
                product = Species(molecule=[molecule])
                product.generateResonanceIsomers()
                product.thermo = generateThermoData(product, thermoDatabase)
                reaction.products.append(product)

            # Generate all possible reactions involving the reactant species
            generatedReactions = self.generateReactionsFromFamilies([reactant.molecule for reactant in reaction.reactants], [], only_families=[family])

            # Remove from that set any reactions that don't produce the desired reactants and products
            forward = []; reverse = []
            for rxn in generatedReactions:
                if matchSpeciesToMolecules(reaction.reactants, rxn.reactants) and matchSpeciesToMolecules(reaction.products, rxn.products):
                    forward.append(rxn)
                if matchSpeciesToMolecules(reaction.reactants, rxn.products) and matchSpeciesToMolecules(reaction.products, rxn.reactants):
                    reverse.append(rxn)

            # We should now know whether the reaction is given in the forward or
            # reverse direction
            if len(forward) == 1 and len(reverse) == 0:
                # The reaction is in the forward direction, so use as-is
                reaction = forward[0]
                template = reaction.template
                # Don't forget to overwrite the estimated kinetics from the database with the kinetics for this entry
                reaction.kinetics = entry.data
            elif len(reverse) == 1 and len(forward) == 0:
                # The reaction is in the reverse direction
                # First fit Arrhenius kinetics in that direction
                Tdata = 1000.0 / numpy.arange(0.5, 3.301, 0.1, numpy.float64)
                kdata = numpy.zeros_like(Tdata)
                for i in range(Tdata.shape[0]):
                    kdata[i] = entry.data.getRateCoefficient(Tdata[i]) / reaction.getEquilibriumConstant(Tdata[i])
                kunits = 'm^3/(mol*s)' if len(reverse[0].reactants) == 2 else 's^-1'
                kinetics = Arrhenius().fitToData(Tdata, kdata, kunits, T0=1.0)
                kinetics.Tmin = entry.data.Tmin
                kinetics.Tmax = entry.data.Tmax
                kinetics.Pmin = entry.data.Pmin
                kinetics.Pmax = entry.data.Pmax
                # Now flip the direction
                reaction = reverse[0]
                reaction.kinetics = kinetics
                template = reaction.template
            elif len(reverse) > 0 and len(forward) > 0:
                print 'FAIL: Multiple reactions found for {0!r}.'.format(entry.label)
            elif len(reverse) == 0 and len(forward) == 0:
                print 'FAIL: No reactions found for "%s".' % (entry.label)
            else:
                print 'FAIL: Unable to estimate kinetics for {0!r}.'.format(entry.label)

        assert reaction is not None
        assert template is not None
        return reaction, template
Example #22
0
    def testSolve(self):
        """
        Test the simple batch reactor with a simple kinetic model. Here we
        choose a kinetic model consisting of the hydrogen abstraction reaction
        CH4 + C2H5 <=> CH3 + C2H6.
        """
        CH4 = Species(
            molecule=[Molecule().fromSMILES("C")],
            thermo=ThermoData(
                Tdata=([300, 400, 500, 600, 800, 1000, 1500], "K"),
                Cpdata=([8.615, 9.687, 10.963, 12.301, 14.841, 16.976,
                         20.528], "cal/(mol*K)"),
                H298=(-17.714, "kcal/mol"),
                S298=(44.472, "cal/(mol*K)")))
        CH3 = Species(molecule=[Molecule().fromSMILES("[CH3]")],
                      thermo=ThermoData(
                          Tdata=([300, 400, 500, 600, 800, 1000, 1500], "K"),
                          Cpdata=([
                              9.397, 10.123, 10.856, 11.571, 12.899, 14.055,
                              16.195
                          ], "cal/(mol*K)"),
                          H298=(9.357, "kcal/mol"),
                          S298=(45.174, "cal/(mol*K)")))
        C2H6 = Species(molecule=[Molecule().fromSMILES("CC")],
                       thermo=ThermoData(
                           Tdata=([300, 400, 500, 600, 800, 1000, 1500], "K"),
                           Cpdata=([
                               12.684, 15.506, 18.326, 20.971, 25.500, 29.016,
                               34.595
                           ], "cal/(mol*K)"),
                           H298=(-19.521, "kcal/mol"),
                           S298=(54.799, "cal/(mol*K)")))
        C2H5 = Species(molecule=[Molecule().fromSMILES("C[CH2]")],
                       thermo=ThermoData(
                           Tdata=([300, 400, 500, 600, 800, 1000, 1500], "K"),
                           Cpdata=([
                               11.635, 13.744, 16.085, 18.246, 21.885, 24.676,
                               29.107
                           ], "cal/(mol*K)"),
                           H298=(29.496, "kcal/mol"),
                           S298=(56.687, "cal/(mol*K)")))

        rxn1 = Reaction(reactants=[C2H6, CH3],
                        products=[C2H5, CH4],
                        kinetics=Arrhenius(A=(686.375 * 6, 'm^3/(mol*s)'),
                                           n=4.40721,
                                           Ea=(7.82799, 'kcal/mol'),
                                           T0=(298.15, 'K')))

        coreSpecies = [CH4, CH3, C2H6, C2H5]
        edgeSpecies = []
        coreReactions = [rxn1]
        edgeReactions = []

        T = 1000
        P = 1.0e5
        rxnSystem = SimpleReactor(T,
                                  P,
                                  initialMoleFractions={
                                      C2H5: 0.1,
                                      CH3: 0.1,
                                      CH4: 0.4,
                                      C2H6: 0.4
                                  },
                                  termination=[])

        rxnSystem.initializeModel(coreSpecies, coreReactions, edgeSpecies,
                                  edgeReactions)

        tlist = numpy.array([10**(i / 10.0) for i in range(-130, -49)],
                            numpy.float64)

        # Integrate to get the solution at each time point
        t = []
        y = []
        reactionRates = []
        speciesRates = []
        for t1 in tlist:
            rxnSystem.advance(t1)
            t.append(rxnSystem.t)
            # You must make a copy of y because it is overwritten by DASSL at
            # each call to advance()
            y.append(rxnSystem.y.copy())
            reactionRates.append(rxnSystem.coreReactionRates.copy())
            speciesRates.append(rxnSystem.coreSpeciesRates.copy())

        # Convert the solution vectors to numpy arrays
        t = numpy.array(t, numpy.float64)
        y = numpy.array(y, numpy.float64)
        reactionRates = numpy.array(reactionRates, numpy.float64)
        speciesRates = numpy.array(speciesRates, numpy.float64)
        V = constants.R * rxnSystem.T.value_si * numpy.sum(
            y) / rxnSystem.P.value_si

        # Check that we're computing the species fluxes correctly
        for i in range(t.shape[0]):
            self.assertAlmostEqual(reactionRates[i, 0],
                                   speciesRates[i, 0],
                                   delta=1e-6 * reactionRates[i, 0])
            self.assertAlmostEqual(reactionRates[i, 0],
                                   -speciesRates[i, 1],
                                   delta=1e-6 * reactionRates[i, 0])
            self.assertAlmostEqual(reactionRates[i, 0],
                                   -speciesRates[i, 2],
                                   delta=1e-6 * reactionRates[i, 0])
            self.assertAlmostEqual(reactionRates[i, 0],
                                   speciesRates[i, 3],
                                   delta=1e-6 * reactionRates[i, 0])

        # Check that we've reached equilibrium
        self.assertAlmostEqual(reactionRates[-1, 0], 0.0, delta=1e-2)

        #######
        # Unit test for the jacobian function:
        # Solve a reaction system and check if the analytical jacobian matches the finite difference jacobian

        H2 = Species(molecule=[Molecule().fromSMILES("[H][H]")],
                     thermo=ThermoData(
                         Tdata=([300, 400, 500, 600, 800, 1000, 1500], "K"),
                         Cpdata=([6.89, 6.97, 6.99, 7.01, 7.08, 7.22,
                                  7.72], "cal/(mol*K)"),
                         H298=(0, "kcal/mol"),
                         S298=(31.23, "cal/(mol*K)")))

        rxnList = []
        rxnList.append(
            Reaction(reactants=[C2H6],
                     products=[CH3, CH3],
                     kinetics=Arrhenius(A=(686.375 * 6, '1/s'),
                                        n=4.40721,
                                        Ea=(7.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))))
        rxnList.append(
            Reaction(reactants=[CH3, CH3],
                     products=[C2H6],
                     kinetics=Arrhenius(A=(686.375 * 6, 'm^3/(mol*s)'),
                                        n=4.40721,
                                        Ea=(7.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))))

        rxnList.append(
            Reaction(reactants=[C2H6, CH3],
                     products=[C2H5, CH4],
                     kinetics=Arrhenius(A=(46.375 * 6, 'm^3/(mol*s)'),
                                        n=3.40721,
                                        Ea=(6.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))))
        rxnList.append(
            Reaction(reactants=[C2H5, CH4],
                     products=[C2H6, CH3],
                     kinetics=Arrhenius(A=(46.375 * 6, 'm^3/(mol*s)'),
                                        n=3.40721,
                                        Ea=(6.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))))

        rxnList.append(
            Reaction(reactants=[C2H5, CH4],
                     products=[CH3, CH3, CH3],
                     kinetics=Arrhenius(A=(246.375 * 6, 'm^3/(mol*s)'),
                                        n=1.40721,
                                        Ea=(3.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))))
        rxnList.append(
            Reaction(reactants=[CH3, CH3, CH3],
                     products=[C2H5, CH4],
                     kinetics=Arrhenius(A=(246.375 * 6, 'm^6/(mol^2*s)'),
                                        n=1.40721,
                                        Ea=(3.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))))  #

        rxnList.append(
            Reaction(reactants=[C2H6, CH3, CH3],
                     products=[C2H5, C2H5, H2],
                     kinetics=Arrhenius(A=(146.375 * 6, 'm^6/(mol^2*s)'),
                                        n=2.40721,
                                        Ea=(8.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))))
        rxnList.append(
            Reaction(reactants=[C2H5, C2H5, H2],
                     products=[C2H6, CH3, CH3],
                     kinetics=Arrhenius(A=(146.375 * 6, 'm^6/(mol^2*s)'),
                                        n=2.40721,
                                        Ea=(8.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))))

        rxnList.append(
            Reaction(reactants=[C2H6, C2H6],
                     products=[CH3, CH4, C2H5],
                     kinetics=Arrhenius(A=(1246.375 * 6, 'm^3/(mol*s)'),
                                        n=0.40721,
                                        Ea=(8.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))))
        rxnList.append(
            Reaction(reactants=[CH3, CH4, C2H5],
                     products=[C2H6, C2H6],
                     kinetics=Arrhenius(A=(46.375 * 6, 'm^6/(mol^2*s)'),
                                        n=0.10721,
                                        Ea=(8.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))))

        for rxn in rxnList:
            coreSpecies = [CH4, CH3, C2H6, C2H5, H2]
            edgeSpecies = []
            coreReactions = [rxn]

            rxnSystem0 = SimpleReactor(T,
                                       P,
                                       initialMoleFractions={
                                           CH4: 0.2,
                                           CH3: 0.1,
                                           C2H6: 0.35,
                                           C2H5: 0.15,
                                           H2: 0.2
                                       },
                                       termination=[])
            rxnSystem0.initializeModel(coreSpecies, coreReactions, edgeSpecies,
                                       edgeReactions)
            dydt0 = rxnSystem0.residual(0.0, rxnSystem0.y,
                                        numpy.zeros(rxnSystem0.y.shape))[0]
            numCoreSpecies = len(coreSpecies)
            dN = .000001 * sum(rxnSystem0.y)
            dN_array = dN * numpy.eye(numCoreSpecies)

            dydt = []
            for i in range(numCoreSpecies):
                rxnSystem0.y[i] += dN
                dydt.append(
                    rxnSystem0.residual(0.0, rxnSystem0.y,
                                        numpy.zeros(rxnSystem0.y.shape))[0])
                rxnSystem0.y[i] -= dN  # reset y to original y0

            # Let the solver compute the jacobian
            solverJacobian = rxnSystem0.jacobian(0.0, rxnSystem0.y, dydt0, 0.0)
            # Compute the jacobian using finite differences
            jacobian = numpy.zeros((numCoreSpecies, numCoreSpecies))
            for i in range(numCoreSpecies):
                for j in range(numCoreSpecies):
                    jacobian[i, j] = (dydt[j][i] - dydt0[i]) / dN
                    self.assertAlmostEqual(jacobian[i, j],
                                           solverJacobian[i, j],
                                           delta=abs(1e-4 * jacobian[i, j]))

        #print 'Solver jacobian'
        #print solverJacobian
        #print 'Numerical jacobian'
        #print jacobian

        ###
        # Unit test for the compute rate derivative
        rxnList = []
        rxnList.append(
            Reaction(reactants=[C2H6],
                     products=[CH3, CH3],
                     kinetics=Arrhenius(A=(686.375e6, '1/s'),
                                        n=4.40721,
                                        Ea=(7.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))))
        rxnList.append(
            Reaction(reactants=[C2H6, CH3],
                     products=[C2H5, CH4],
                     kinetics=Arrhenius(A=(46.375 * 6, 'm^3/(mol*s)'),
                                        n=3.40721,
                                        Ea=(6.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))))
        rxnList.append(
            Reaction(reactants=[C2H6, CH3, CH3],
                     products=[C2H5, C2H5, H2],
                     kinetics=Arrhenius(A=(146.375 * 6, 'm^6/(mol^2*s)'),
                                        n=2.40721,
                                        Ea=(8.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))))

        coreSpecies = [CH4, CH3, C2H6, C2H5, H2]
        edgeSpecies = []
        coreReactions = rxnList

        rxnSystem0 = SimpleReactor(T,
                                   P,
                                   initialMoleFractions={
                                       CH4: 0.2,
                                       CH3: 0.1,
                                       C2H6: 0.35,
                                       C2H5: 0.15,
                                       H2: 0.2
                                   },
                                   termination=[])
        rxnSystem0.initializeModel(coreSpecies, coreReactions, edgeSpecies,
                                   edgeReactions)
        dfdt0 = rxnSystem0.residual(0.0, rxnSystem0.y,
                                    numpy.zeros(rxnSystem0.y.shape))[0]
        solver_dfdk = rxnSystem0.computeRateDerivative()
        #print 'Solver d(dy/dt)/dk'
        #print solver_dfdk

        integrationTime = 1e-8
        rxnSystem0.termination.append(TerminationTime((integrationTime, 's')))
        rxnSystem0.simulate(coreSpecies, coreReactions, [], [], 0, 1, 0)

        y0 = rxnSystem0.y

        dfdk = numpy.zeros((numCoreSpecies, len(rxnList)))  # d(dy/dt)/dk

        for i in range(len(rxnList)):
            k0 = rxnList[i].getRateCoefficient(T, P)
            rxnList[i].kinetics.A.value_si = rxnList[i].kinetics.A.value_si * (
                1 + 1e-3)
            dk = rxnList[i].getRateCoefficient(T, P) - k0

            rxnSystem = SimpleReactor(T,
                                      P,
                                      initialMoleFractions={
                                          CH4: 0.2,
                                          CH3: 0.1,
                                          C2H6: 0.35,
                                          C2H5: 0.15,
                                          H2: 0.2
                                      },
                                      termination=[])
            rxnSystem.initializeModel(coreSpecies, coreReactions, edgeSpecies,
                                      edgeReactions)

            dfdt = rxnSystem.residual(0.0, rxnSystem.y,
                                      numpy.zeros(rxnSystem.y.shape))[0]
            dfdk[:, i] = (dfdt - dfdt0) / dk

            rxnSystem.termination.append(
                TerminationTime((integrationTime, 's')))
            rxnSystem.simulate(coreSpecies, coreReactions, [], [], 0, 1, 0)

            rxnList[i].kinetics.A.value_si = rxnList[i].kinetics.A.value_si / (
                1 + 1e-3)  # reset A factor

        for i in range(numCoreSpecies):
            for j in range(len(rxnList)):
                self.assertAlmostEqual(dfdk[i, j],
                                       solver_dfdk[i, j],
                                       delta=abs(1e-3 * dfdk[i, j]))
Example #23
0
    def test_jacobian(self):
        """
        Unit test for the jacobian function:
        Solve a reaction system and check if the analytical jacobian matches the finite difference jacobian

        """

        coreSpecies = [self.CH4,self.CH3,self.C2H6,self.C2H5]
        edgeSpecies = []

        rxn1 = Reaction(reactants=[self.C2H6,self.CH3], products=[self.C2H5,self.CH4], kinetics=Arrhenius(A=(686.375*6,'m^3/(mol*s)'), n=4.40721, Ea=(7.82799,'kcal/mol'), T0=(298.15,'K')))
        coreReactions = [rxn1]
        edgeReactions = []
        numCoreSpecies = len(coreSpecies)

        rxnList = []
        rxnList.append(Reaction(reactants=[self.C2H6], products=[self.CH3,self.CH3], kinetics=Arrhenius(A=(686.375*6,'1/s'), n=4.40721, Ea=(7.82799,'kcal/mol'), T0=(298.15,'K'))))
        rxnList.append(Reaction(reactants=[self.CH3,self.CH3], products=[self.C2H6], kinetics=Arrhenius(A=(686.375*6,'m^3/(mol*s)'), n=4.40721, Ea=(7.82799,'kcal/mol'), T0=(298.15,'K'))))
        
        rxnList.append(Reaction(reactants=[self.C2H6,self.CH3], products=[self.C2H5,self.CH4], kinetics=Arrhenius(A=(46.375*6,'m^3/(mol*s)'), n=3.40721, Ea=(6.82799,'kcal/mol'), T0=(298.15,'K'))))        
        rxnList.append(Reaction(reactants=[self.C2H5,self.CH4], products=[self.C2H6,self.CH3], kinetics=Arrhenius(A=(46.375*6,'m^3/(mol*s)'), n=3.40721, Ea=(6.82799,'kcal/mol'), T0=(298.15,'K'))))        
        
        rxnList.append(Reaction(reactants=[self.C2H5,self.CH4], products=[self.CH3,self.CH3,self.CH3], kinetics=Arrhenius(A=(246.375*6,'m^3/(mol*s)'), n=1.40721, Ea=(3.82799,'kcal/mol'), T0=(298.15,'K'))))       
        rxnList.append(Reaction(reactants=[self.CH3,self.CH3,self.CH3], products=[self.C2H5,self.CH4], kinetics=Arrhenius(A=(246.375*6,'m^6/(mol^2*s)'), n=1.40721, Ea=(3.82799,'kcal/mol'), T0=(298.15,'K'))))#        
        
        rxnList.append(Reaction(reactants=[self.C2H6,self.CH3,self.CH3], products=[self.C2H5,self.C2H5,self.H2], kinetics=Arrhenius(A=(146.375*6,'m^6/(mol^2*s)'), n=2.40721, Ea=(8.82799,'kcal/mol'), T0=(298.15,'K'))))
        rxnList.append(Reaction(reactants=[self.C2H5,self.C2H5,self.H2], products=[self.C2H6,self.CH3,self.CH3], kinetics=Arrhenius(A=(146.375*6,'m^6/(mol^2*s)'), n=2.40721, Ea=(8.82799,'kcal/mol'), T0=(298.15,'K'))))
        
        rxnList.append(Reaction(reactants=[self.C2H6,self.C2H6], products=[self.CH3,self.CH4,self.C2H5], kinetics=Arrhenius(A=(1246.375*6,'m^3/(mol*s)'), n=0.40721, Ea=(8.82799,'kcal/mol'), T0=(298.15,'K'))))
        rxnList.append(Reaction(reactants=[self.CH3,self.CH4,self.C2H5], products=[self.C2H6,self.C2H6], kinetics=Arrhenius(A=(46.375*6,'m^6/(mol^2*s)'), n=0.10721, Ea=(8.82799,'kcal/mol'), T0=(298.15,'K'))))
        

        for rxn in rxnList:
            coreSpecies = [self.CH4,self.CH3,self.C2H6,self.C2H5,self.H2]
            edgeSpecies = []
            coreReactions = [rxn]
            
            c0={self.CH4:0.2,self.CH3:0.1,self.C2H6:0.35,self.C2H5:0.15, self.H2:0.2}
            rxnSystem0 = LiquidReactor(self.T, c0,termination=[])
            rxnSystem0.initializeModel(coreSpecies, coreReactions, edgeSpecies, edgeReactions)
            dydt0 = rxnSystem0.residual(0.0, rxnSystem0.y, numpy.zeros(rxnSystem0.y.shape))[0]
            
            dN = .000001*sum(rxnSystem0.y)
            dN_array = dN*numpy.eye(numCoreSpecies)
            
            dydt = []
            for i in xrange(numCoreSpecies):
                rxnSystem0.y[i] += dN 
                dydt.append(rxnSystem0.residual(0.0, rxnSystem0.y, numpy.zeros(rxnSystem0.y.shape))[0])
                rxnSystem0.y[i] -= dN  # reset y to original y0
            
            # Let the solver compute the jacobian       
            solverJacobian = rxnSystem0.jacobian(0.0, rxnSystem0.y, dydt0, 0.0)     
            # Compute the jacobian using finite differences
            jacobian = numpy.zeros((numCoreSpecies, numCoreSpecies))
            for i in xrange(numCoreSpecies):
                for j in xrange(numCoreSpecies):
                    jacobian[i,j] = (dydt[j][i]-dydt0[i])/dN
                    self.assertAlmostEqual(jacobian[i,j], solverJacobian[i,j], delta=abs(1e-4*jacobian[i,j]))
def generateRules(family, database):
    """
    For a given reaction `family` label, generate additional rate rules from
    the corresponding depository training set. This function does automatically
    what users used to do by hand to construct a rate rule from reaction
    kinetics found in the literature, i.e. determine the groups involved,
    adjust to a per-site basis, etc.
    """

    # Load rules and determine starting index
    rules = family.rules
    index = max([entry.index for entry in rules.entries.values()] or [0]) + 1

    # Load training entries
    for depository in family.depositories:
        if 'training' in depository.name:
            entries = sorted(depository.entries.values(),
                             key=lambda entry: (entry.index, entry.label))
            break

    # Generate a rate rule for each training entry
    for entry in entries:

        # Load entry's reaction, template, and kinetics
        reaction, template = database.kinetics.getForwardReactionForFamilyEntry(
            entry=entry, family=family.name, thermoDatabase=database.thermo)
        kinetics = reaction.kinetics

        # Convert KineticsData to Arrhenius
        if isinstance(kinetics, KineticsData):
            kinetics = Arrhenius().fitToData(Tdata=kinetics.Tdata.values,
                                             kdata=kinetics.kdata.values,
                                             kunits=kinetics.kdata.units,
                                             T0=1)

        # Ignore other kinetics types
        if not isinstance(kinetics, Arrhenius):
            continue

        # Change reference temperature to 1 K if necessary
        if kinetics.T0.value != 1:
            kinetics = kinetics.changeT0(1)

        # Convert kinetics to a per-site basis
        kinetics.A.value /= reaction.degeneracy

        # Convert to ArrheniusEP
        kinetics = ArrheniusEP(A=kinetics.A,
                               n=kinetics.n,
                               alpha=0,
                               E0=kinetics.Ea,
                               Tmin=kinetics.Tmin,
                               Tmax=kinetics.Tmax)

        # Add new rate rule
        rules.entries[index] = Entry(index=index,
                                     label=';'.join(
                                         [group.label for group in template]),
                                     item=Reaction(reactants=template[:],
                                                   products=None),
                                     data=kinetics,
                                     reference=entry.reference,
                                     rank=entry.rank,
                                     shortDesc=entry.shortDesc,
                                     longDesc=entry.longDesc,
                                     history=entry.history)
        index += 1
Example #25
0
    def test_jacobian(self):
        """
        Unit test for the jacobian function:
        Solve a reaction system and check if the analytical jacobian matches
        the finite difference jacobian.
        """

        core_species = [self.CH4, self.CH3, self.C2H6, self.C2H5, self.H2]
        edge_species = []
        num_core_species = len(core_species)
        c0 = {
            self.CH4: 0.2,
            self.CH3: 0.1,
            self.C2H6: 0.35,
            self.C2H5: 0.15,
            self.H2: 0.2
        }
        edge_reactions = []

        rxn_list = [
            Reaction(reactants=[self.C2H6],
                     products=[self.CH3, self.CH3],
                     kinetics=Arrhenius(A=(686.375 * 6, '1/s'),
                                        n=4.40721,
                                        Ea=(7.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))),
            Reaction(reactants=[self.CH3, self.CH3],
                     products=[self.C2H6],
                     kinetics=Arrhenius(A=(686.375 * 6, 'm^3/(mol*s)'),
                                        n=4.40721,
                                        Ea=(7.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))),
            Reaction(reactants=[self.C2H6, self.CH3],
                     products=[self.C2H5, self.CH4],
                     kinetics=Arrhenius(A=(46.375 * 6, 'm^3/(mol*s)'),
                                        n=3.40721,
                                        Ea=(6.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))),
            Reaction(reactants=[self.C2H5, self.CH4],
                     products=[self.C2H6, self.CH3],
                     kinetics=Arrhenius(A=(46.375 * 6, 'm^3/(mol*s)'),
                                        n=3.40721,
                                        Ea=(6.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))),
            Reaction(reactants=[self.C2H5, self.CH4],
                     products=[self.CH3, self.CH3, self.CH3],
                     kinetics=Arrhenius(A=(246.375 * 6, 'm^3/(mol*s)'),
                                        n=1.40721,
                                        Ea=(3.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))),
            Reaction(reactants=[self.CH3, self.CH3, self.CH3],
                     products=[self.C2H5, self.CH4],
                     kinetics=Arrhenius(A=(246.375 * 6, 'm^6/(mol^2*s)'),
                                        n=1.40721,
                                        Ea=(3.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))),
            Reaction(reactants=[self.C2H6, self.CH3, self.CH3],
                     products=[self.C2H5, self.C2H5, self.H2],
                     kinetics=Arrhenius(A=(146.375 * 6, 'm^6/(mol^2*s)'),
                                        n=2.40721,
                                        Ea=(8.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))),
            Reaction(reactants=[self.C2H5, self.C2H5, self.H2],
                     products=[self.C2H6, self.CH3, self.CH3],
                     kinetics=Arrhenius(A=(146.375 * 6, 'm^6/(mol^2*s)'),
                                        n=2.40721,
                                        Ea=(8.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))),
            Reaction(reactants=[self.C2H6, self.C2H6],
                     products=[self.CH3, self.CH4, self.C2H5],
                     kinetics=Arrhenius(A=(1246.375 * 6, 'm^3/(mol*s)'),
                                        n=0.40721,
                                        Ea=(8.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))),
            Reaction(reactants=[self.CH3, self.CH4, self.C2H5],
                     products=[self.C2H6, self.C2H6],
                     kinetics=Arrhenius(A=(46.375 * 6, 'm^6/(mol^2*s)'),
                                        n=0.10721,
                                        Ea=(8.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))),
        ]

        # Analytical Jacobian for reaction 6
        def jacobian_rxn6(c, kf, kr, s):
            c1, c2, c3, c4 = c[s[1]], c[s[2]], c[s[3]], c[s[4]]
            jaco = np.zeros((5, 5))

            jaco[1, 1] = -4 * kf * c1 * c2
            jaco[1, 2] = -2 * kf * c1 * c1
            jaco[1, 3] = 4 * kr * c3 * c4
            jaco[1, 4] = 2 * kr * c3 * c3
            jaco[2, 1:] = 0.5 * jaco[1, 1:]
            jaco[3, 1:] = -jaco[1, 1:]
            jaco[4, 1:] = -0.5 * jaco[1, 1:]
            return jaco

        # Analytical Jacobian for reaction 7
        def jacobian_rxn7(c, kf, kr, s):
            c1, c2, c3, c4 = c[s[1]], c[s[2]], c[s[3]], c[s[4]]
            jaco = np.zeros((5, 5))

            jaco[1, 1] = -4 * kr * c1 * c2
            jaco[1, 2] = -2 * kr * c1 * c1
            jaco[1, 3] = 4 * kf * c3 * c4
            jaco[1, 4] = 2 * kf * c3 * c3
            jaco[2, 1:] = 0.5 * jaco[1, 1:]
            jaco[3, 1:] = -jaco[1, 1:]
            jaco[4, 1:] = -0.5 * jaco[1, 1:]
            return jaco

        for rxn_num, rxn in enumerate(rxn_list):
            core_reactions = [rxn]

            rxn_system0 = LiquidReactor(self.T, c0, 1, termination=[])
            rxn_system0.initialize_model(core_species, core_reactions,
                                         edge_species, edge_reactions)
            dydt0 = rxn_system0.residual(0.0, rxn_system0.y,
                                         np.zeros(rxn_system0.y.shape))[0]

            dN = .000001 * sum(rxn_system0.y)

            # Let the solver compute the jacobian
            solver_jacobian = rxn_system0.jacobian(0.0, rxn_system0.y, dydt0,
                                                   0.0)

            if rxn_num not in (6, 7):
                dydt = []
                for i in range(num_core_species):
                    rxn_system0.y[i] += dN
                    dydt.append(
                        rxn_system0.residual(0.0, rxn_system0.y,
                                             np.zeros(rxn_system0.y.shape))[0])
                    rxn_system0.y[i] -= dN  # reset y

                # Compute the jacobian using finite differences
                jacobian = np.zeros((num_core_species, num_core_species))
                for i in range(num_core_species):
                    for j in range(num_core_species):
                        jacobian[i, j] = (dydt[j][i] - dydt0[i]) / dN
                        self.assertAlmostEqual(jacobian[i, j],
                                               solver_jacobian[i, j],
                                               delta=abs(1e-4 *
                                                         jacobian[i, j]))
            # The forward finite difference is very unstable for reactions
            # 6 and 7. Use Jacobians calculated by hand instead.
            elif rxn_num == 6:
                kforward = rxn.get_rate_coefficient(self.T)
                kreverse = kforward / rxn.get_equilibrium_constant(self.T)
                jacobian = jacobian_rxn6(c0, kforward, kreverse, core_species)
                for i in range(num_core_species):
                    for j in range(num_core_species):
                        self.assertAlmostEqual(jacobian[i, j],
                                               solver_jacobian[i, j],
                                               delta=abs(1e-4 *
                                                         jacobian[i, j]))
            elif rxn_num == 7:
                kforward = rxn.get_rate_coefficient(self.T)
                kreverse = kforward / rxn.get_equilibrium_constant(self.T)
                jacobian = jacobian_rxn7(c0, kforward, kreverse, core_species)
                for i in range(num_core_species):
                    for j in range(num_core_species):
                        self.assertAlmostEqual(jacobian[i, j],
                                               solver_jacobian[i, j],
                                               delta=abs(1e-4 *
                                                         jacobian[i, j]))
Example #26
0
    def test_compute_derivative(self):
        rxn_list = [
            Reaction(reactants=[self.C2H6],
                     products=[self.CH3, self.CH3],
                     kinetics=Arrhenius(A=(686.375e6, '1/s'),
                                        n=4.40721,
                                        Ea=(7.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))),
            Reaction(reactants=[self.C2H6, self.CH3],
                     products=[self.C2H5, self.CH4],
                     kinetics=Arrhenius(A=(46.375 * 6, 'm^3/(mol*s)'),
                                        n=3.40721,
                                        Ea=(6.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))),
            Reaction(reactants=[self.C2H6, self.CH3, self.CH3],
                     products=[self.C2H5, self.C2H5, self.H2],
                     kinetics=Arrhenius(A=(146.375 * 6, 'm^6/(mol^2*s)'),
                                        n=2.40721,
                                        Ea=(8.82799, 'kcal/mol'),
                                        T0=(298.15, 'K'))),
        ]

        core_species = [self.CH4, self.CH3, self.C2H6, self.C2H5, self.H2]
        edge_species = []
        core_reactions = rxn_list
        edge_reactions = []
        num_core_species = len(core_species)

        c0 = {
            self.CH4: 0.2,
            self.CH3: 0.1,
            self.C2H6: 0.35,
            self.C2H5: 0.15,
            self.H2: 0.2
        }

        rxn_system0 = LiquidReactor(self.T, c0, 1, termination=[])
        rxn_system0.initialize_model(core_species, core_reactions,
                                     edge_species, edge_reactions)
        dfdt0 = rxn_system0.residual(0.0, rxn_system0.y,
                                     np.zeros(rxn_system0.y.shape))[0]
        solver_dfdk = rxn_system0.compute_rate_derivative()
        # print 'Solver d(dy/dt)/dk'
        # print solver_dfdk

        integration_time = 1e-8

        model_settings = ModelSettings(tol_keep_in_edge=0,
                                       tol_move_to_core=1,
                                       tol_interrupt_simulation=0)
        simulator_settings = SimulatorSettings()

        rxn_system0.termination.append(TerminationTime(
            (integration_time, 's')))

        rxn_system0.simulate(core_species,
                             core_reactions, [], [], [], [],
                             model_settings=model_settings,
                             simulator_settings=simulator_settings)

        y0 = rxn_system0.y

        dfdk = np.zeros((num_core_species, len(rxn_list)))  # d(dy/dt)/dk

        c0 = {
            self.CH4: 0.2,
            self.CH3: 0.1,
            self.C2H6: 0.35,
            self.C2H5: 0.15,
            self.H2: 0.2
        }

        for i in range(len(rxn_list)):
            k0 = rxn_list[i].get_rate_coefficient(self.T)
            rxn_list[i].kinetics.A.value_si = rxn_list[
                i].kinetics.A.value_si * (1 + 1e-3)
            dk = rxn_list[i].get_rate_coefficient(self.T) - k0

            rxn_system = LiquidReactor(self.T, c0, 1, termination=[])
            rxn_system.initialize_model(core_species, core_reactions,
                                        edge_species, edge_reactions)

            dfdt = rxn_system.residual(0.0, rxn_system.y,
                                       np.zeros(rxn_system.y.shape))[0]
            dfdk[:, i] = (dfdt - dfdt0) / dk

            rxn_system.termination.append(
                TerminationTime((integration_time, 's')))
            model_settings = ModelSettings(tol_keep_in_edge=0,
                                           tol_move_to_core=1,
                                           tol_interrupt_simulation=0)
            simulator_settings = SimulatorSettings()
            rxn_system.simulate(core_species,
                                core_reactions, [], [], [], [],
                                model_settings=model_settings,
                                simulator_settings=simulator_settings)

            rxn_list[i].kinetics.A.value_si = rxn_list[
                i].kinetics.A.value_si / (1 + 1e-3)  # reset A factor

        for i in range(num_core_species):
            for j in range(len(rxn_list)):
                self.assertAlmostEqual(dfdk[i, j],
                                       solver_dfdk[i, j],
                                       delta=abs(1e-3 * dfdk[i, j]))
Example #27
0
    def get_forward_reaction_for_family_entry(self, entry, family,
                                              thermo_database):
        """
        For a given `entry` for a reaction of the given reaction `family` (the
        string label of the family), return the reaction with kinetics and
        degeneracy for the "forward" direction as defined by the reaction 
        family. For families that are their own reverse, the direction the
        kinetics is given in will be preserved. If the entry contains 
        functional groups for the reactants, assume that it is given in the 
        forward direction and do nothing. Returns the reaction in the direction
        consistent with the reaction family template, and the matching template.
        Note that the returned reaction will have its kinetics and degeneracy
        set appropriately.
        
        In order to reverse the reactions that are given in the reverse of the
        direction the family is defined, we need to compute the thermodynamics
        of the reactants and products. For this reason you must also pass
        the `thermo_database` to use to generate the thermo data.
        """
        reaction = None
        template = None

        # Get the indicated reaction family
        try:
            groups = self.families[family].groups
        except KeyError:
            raise ValueError(
                'Invalid value "{0}" for family parameter.'.format(family))

        if all([(isinstance(reactant, Group)
                 or isinstance(reactant, LogicNode))
                for reactant in entry.item.reactants]):
            # The entry is a rate rule, containing functional groups only
            # By convention, these are always given in the forward direction and
            # have kinetics defined on a per-site basis
            reaction = Reaction(
                reactants=entry.item.reactants[:],
                products=[],
                specific_collider=entry.item.specific_collider,
                kinetics=entry.data,
                degeneracy=1,
            )
            template = [
                groups.entries[label] for label in entry.label.split(';')
            ]

        elif (all([
                isinstance(reactant, Molecule)
                for reactant in entry.item.reactants
        ]) and all(
            [isinstance(product, Molecule)
             for product in entry.item.products])):
            # The entry is a real reaction, containing molecules
            # These could be defined for either the forward or reverse direction
            # and could have a reaction-path degeneracy

            reaction = Reaction(reactants=[], products=[])
            for molecule in entry.item.reactants:
                reactant = Species(molecule=[molecule])
                reactant.generate_resonance_structures()
                reactant.thermo = thermo_database.get_thermo_data(reactant)
                reaction.reactants.append(reactant)
            for molecule in entry.item.products:
                product = Species(molecule=[molecule])
                product.generate_resonance_structures()
                product.thermo = thermo_database.get_thermo_data(product)
                reaction.products.append(product)

            # Generate all possible reactions involving the reactant species
            generated_reactions = self.generate_reactions_from_families(
                [reactant.molecule for reactant in reaction.reactants], [],
                only_families=[family])

            # Remove from that set any reactions that don't produce the desired reactants and products
            forward = []
            reverse = []
            for rxn in generated_reactions:
                if (same_species_lists(reaction.reactants, rxn.reactants) and
                        same_species_lists(reaction.products, rxn.products)):
                    forward.append(rxn)
                if (same_species_lists(reaction.reactants, rxn.products) and
                        same_species_lists(reaction.products, rxn.reactants)):
                    reverse.append(rxn)

            # We should now know whether the reaction is given in the forward or
            # reverse direction
            if len(forward) == 1 and len(reverse) == 0:
                # The reaction is in the forward direction, so use as-is
                reaction = forward[0]
                template = reaction.template
                # Don't forget to overwrite the estimated kinetics from the database with the kinetics for this entry
                reaction.kinetics = entry.data
            elif len(reverse) == 1 and len(forward) == 0:
                # The reaction is in the reverse direction
                # First fit Arrhenius kinetics in that direction
                T_data = 1000.0 / np.arange(0.5, 3.301, 0.1, np.float64)
                k_data = np.zeros_like(T_data)
                for i in range(T_data.shape[0]):
                    k_data[i] = entry.data.get_rate_coefficient(
                        T_data[i]) / reaction.get_equilibrium_constant(
                            T_data[i])
                try:
                    k_units = ('s^-1', 'm^3/(mol*s)',
                               'm^6/(mol^2*s)')[len(reverse[0].reactants) - 1]
                except IndexError:
                    raise NotImplementedError(
                        'Cannot reverse reactions with {} products'.format(
                            len(reverse[0].reactants)))
                kinetics = Arrhenius().fit_to_data(T_data,
                                                   k_data,
                                                   k_units,
                                                   T0=1.0)
                kinetics.Tmin = entry.data.Tmin
                kinetics.Tmax = entry.data.Tmax
                kinetics.Pmin = entry.data.Pmin
                kinetics.Pmax = entry.data.Pmax
                # Now flip the direction
                reaction = reverse[0]
                reaction.kinetics = kinetics
                template = reaction.template
            elif len(reverse) > 0 and len(forward) > 0:
                print('FAIL: Multiple reactions found for {0!r}.'.format(
                    entry.label))
            elif len(reverse) == 0 and len(forward) == 0:
                print('FAIL: No reactions found for "%s".' % (entry.label))
            else:
                print('FAIL: Unable to estimate kinetics for {0!r}.'.format(
                    entry.label))

        assert reaction is not None
        assert template is not None
        return reaction, template
Example #28
0
    def test_compute_derivative(self):

        rxnList = []
        rxnList.append(Reaction(reactants=[self.C2H6], products=[self.CH3,self.CH3], kinetics=Arrhenius(A=(686.375e6,'1/s'), n=4.40721, Ea=(7.82799,'kcal/mol'), T0=(298.15,'K')))) 
        rxnList.append(Reaction(reactants=[self.C2H6,self.CH3], products=[self.C2H5,self.CH4], kinetics=Arrhenius(A=(46.375*6,'m^3/(mol*s)'), n=3.40721, Ea=(6.82799,'kcal/mol'), T0=(298.15,'K'))))        
        rxnList.append(Reaction(reactants=[self.C2H6,self.CH3,self.CH3], products=[self.C2H5,self.C2H5,self.H2], kinetics=Arrhenius(A=(146.375*6,'m^6/(mol^2*s)'), n=2.40721, Ea=(8.82799,'kcal/mol'), T0=(298.15,'K'))))
        
        
        coreSpecies = [self.CH4,self.CH3,self.C2H6,self.C2H5, self.H2]
        edgeSpecies = []
        coreReactions = rxnList
        edgeReactions = []
        numCoreSpecies = len(coreSpecies)
        
        c0={self.CH4:0.2,self.CH3:0.1,self.C2H6:0.35,self.C2H5:0.15, self.H2:0.2}

        rxnSystem0 = LiquidReactor(self.T, c0,termination=[])
        rxnSystem0.initializeModel(coreSpecies, coreReactions, edgeSpecies, edgeReactions)
        dfdt0 = rxnSystem0.residual(0.0, rxnSystem0.y, numpy.zeros(rxnSystem0.y.shape))[0]
        solver_dfdk = rxnSystem0.computeRateDerivative()
        #print 'Solver d(dy/dt)/dk'
        #print solver_dfdk
        
        integrationTime = 1e-8
        rxnSystem0.termination.append(TerminationTime((integrationTime,'s')))
        rxnSystem0.simulate(coreSpecies, coreReactions, [], [], 0, 1, 0)

        y0 = rxnSystem0.y
        
        dfdk = numpy.zeros((numCoreSpecies,len(rxnList)))   # d(dy/dt)/dk
        
        c0={self.CH4:0.2,self.CH3:0.1,self.C2H6:0.35,self.C2H5:0.15, self.H2:0.2}

        for i in xrange(len(rxnList)):
            k0 = rxnList[i].getRateCoefficient(self.T)
            rxnList[i].kinetics.A.value_si = rxnList[i].kinetics.A.value_si*(1+1e-3)               
            dk = rxnList[i].getRateCoefficient(self.T) - k0

            rxnSystem = LiquidReactor(self.T, c0,termination=[])
            rxnSystem.initializeModel(coreSpecies, coreReactions, edgeSpecies, edgeReactions)

            dfdt = rxnSystem.residual(0.0, rxnSystem.y, numpy.zeros(rxnSystem.y.shape))[0]  
            dfdk[:,i]=(dfdt-dfdt0)/dk          
            
            
            rxnSystem.termination.append(TerminationTime((integrationTime,'s')))
            rxnSystem.simulate(coreSpecies, coreReactions, [], [], 0, 1, 0)
            
            rxnList[i].kinetics.A.value_si = rxnList[i].kinetics.A.value_si/(1+1e-3)  # reset A factor
            
        for i in xrange(numCoreSpecies):
            for j in xrange(len(rxnList)):
                self.assertAlmostEqual(dfdk[i,j], solver_dfdk[i,j], delta=abs(1e-3*dfdk[i,j]))
def generateKineticsGroupValues(family, database, trainingSetLabels, method):
    """
    Evaluate the kinetics group additivity values for the given reaction 
    `family` using the specified lists of depository components 
    `trainingSetLabels` as the training set. The already-loaded RMG database 
    should be given as the `database` parameter.
    """
    
    kunits = getRateCoefficientUnits(family)
    
    print 'Categorizing reactions in training sets for {0}'.format(family.label)
    trainingSets = createDataSet(trainingSetLabels, family, database)
    trainingSet = []
    for label, data in trainingSets:
        trainingSet.extend(data)
    #reactions = [reaction for label, trainingSet in trainingSets for reaction, template, entry in trainingSet]
    #templates = [template for label, trainingSet in trainingSets for reaction, template, entry in trainingSet]
    #entries = [entry for label, trainingSet in trainingSets for reaction, template, entry in trainingSet]
    
    print 'Fitting new group additivity values for {0}...'.format(family.label)
    
    # keep track of previous values so we can detect if they change
    old_entries = dict()
    for label,entry in family.groups.entries.iteritems():
        if entry.data is not None:
            old_entries[label] = entry.data
    
    # Determine a complete list of the entries in the database, sorted as in the tree
    groupEntries = family.groups.top[:]
    for entry in family.groups.top:
        groupEntries.extend(family.groups.descendants(entry))
    
    # Determine a unique list of the groups we will be able to fit parameters for
    groupList = []
    for reaction, template, entry in trainingSet:
        for group in template:
            if group not in family.groups.top:
                groupList.append(group)
                groupList.extend(family.groups.ancestors(group)[:-1])
    groupList = list(set(groupList))
    groupList.sort(key=lambda x: x.index)
    
    if method == 'KineticsData':
        # Fit a discrete set of k(T) data points by training against k(T) data
        
        Tdata = [300,400,500,600,800,1000,1500,2000]
        
        #kmodel = numpy.zeros_like(kdata)
        
        # Initialize dictionaries of fitted group values and uncertainties
        groupValues = {}; groupUncertainties = {}; groupCounts = {}; groupComments = {}
        for entry in groupEntries:
            groupValues[entry] = []
            groupUncertainties[entry] = []
            groupCounts[entry] = []
            groupComments[entry] = set()
        
        # Generate least-squares matrix and vector
        A = []; b = []
        
        kdata = []
        for reaction, template, entry in trainingSet:
            
            if isinstance(reaction.kinetics, Arrhenius) or isinstance(reaction.kinetics, KineticsData):
                kd = [reaction.kinetics.getRateCoefficient(T) / reaction.degeneracy for T in Tdata]
            elif isinstance(reaction.kinetics, ArrheniusEP):
                kd = [reaction.kinetics.getRateCoefficient(T, 0) / reaction.degeneracy for T in Tdata]
            else:
                raise Exception('Unexpected kinetics model of type {0} for reaction {1}.'.format(reaction.kinetics.__class__, reaction))
            kdata.append(kd)
                
            # Create every combination of each group and its ancestors with each other
            combinations = []
            for group in template:
                groups = [group]; groups.extend(family.groups.ancestors(group))
                combinations.append(groups)
            combinations = getAllCombinations(combinations)
            # Add a row to the matrix for each combination
            for groups in combinations:
                Arow = [1 if group in groups else 0 for group in groupList]
                Arow.append(1)
                brow = [math.log10(k) for k in kd]
                A.append(Arow); b.append(brow)
                
                for group in groups:
                    groupComments[group].add("{0!s}".format(template))
            
        if len(A) == 0:
            logging.warning('Unable to fit kinetics groups for family "{0}"; no valid data found.'.format(family.groups.label))
            return
        A = numpy.array(A)
        b = numpy.array(b)
        kdata = numpy.array(kdata)
        
        x, residues, rank, s = numpy.linalg.lstsq(A, b)
        
        for t, T in enumerate(Tdata):
            
            # Determine error in each group (on log scale)
            stdev = numpy.zeros(len(groupList)+1, numpy.float64)
            count = numpy.zeros(len(groupList)+1, numpy.int)
            
            for index in range(len(trainingSet)):
                reaction, template, entry = trainingSet[index]
                kd = math.log10(kdata[index,t])
                km = x[-1,t] + sum([x[groupList.index(group),t] for group in template if group in groupList])
                variance = (km - kd)**2
                for group in template:
                    groups = [group]; groups.extend(family.groups.ancestors(group))
                    for g in groups:
                        if g not in family.groups.top:
                            ind = groupList.index(g)
                            stdev[ind] += variance
                            count[ind] += 1
                stdev[-1] += variance
                count[-1] += 1
            stdev = numpy.sqrt(stdev / (count - 1))
            ci = scipy.stats.t.ppf(0.975, count - 1) * stdev
            
            # Update dictionaries of fitted group values and uncertainties
            for entry in groupEntries:
                if entry == family.groups.top[0]:
                    groupValues[entry].append(10**x[-1,t])
                    groupUncertainties[entry].append(10**ci[-1])
                    groupCounts[entry].append(count[-1])
                elif entry in groupList:
                    index = groupList.index(entry)
                    groupValues[entry].append(10**x[index,t])
                    groupUncertainties[entry].append(10**ci[index])
                    groupCounts[entry].append(count[index])
                else:
                    groupValues[entry] = None
                    groupUncertainties[entry] = None
                    groupCounts[entry] = None
        
        # Store the fitted group values and uncertainties on the associated entries
        for entry in groupEntries:
            if groupValues[entry] is not None:
                entry.data = KineticsData(Tdata=(Tdata,"K"), kdata=(groupValues[entry],kunits))
                if not any(numpy.isnan(numpy.array(groupUncertainties[entry]))):
                    entry.data.kdata.uncertainties = numpy.array(groupUncertainties[entry])
                    entry.data.kdata.uncertaintyType = '*|/'
                entry.shortDesc = "Group additive kinetics."
                entry.longDesc = "Fitted to {0} rates.\n".format(groupCounts[entry])
                entry.longDesc += "\n".join(groupComments[entry])
            else:
                entry.data = None
        
        # Print the group values
        print '=============================== =========== =========== =========== ======='
        print 'Group                           T (K)       k(T) (SI)   CI (95%)    Count'
        print '=============================== =========== =========== =========== ======='
        entry = family.groups.top[0]
        for i in range(len(entry.data.Tdata.values)):
            label = ', '.join(['%s' % (top.label) for top in family.groups.top]) if i == 0 else ''
            T = Tdata[i]
            value = groupValues[entry][i]
            uncertainty = groupUncertainties[entry][i]
            count = groupCounts[entry][i]
            print '%-31s %-11g %-11.4e %-11.4e %-7i' % (label, T, value, uncertainty, count)
        print '------------------------------- ----------- ----------- ----------- -------'
        for entry in groupEntries:
            if entry.data is not None:
                for i in range(len(entry.data.Tdata.values)):
                    label = entry.label if i == 0 else ''
                    T = Tdata[i]
                    value = groupValues[entry][i]
                    uncertainty = groupUncertainties[entry][i]
                    count = groupCounts[entry][i]
                    print '%-31s %-11g %-11.4e %-11.4e %-7i' % (label, T, value, uncertainty, count)
        print '=============================== =========== =========== =========== ======='
    
    elif method == 'Arrhenius':
        # Fit Arrhenius parameters (A, n, Ea) by training against k(T) data
        
        Tdata = [300,400,500,600,800,1000,1500,2000]
        
        A = []; b = []
        
        kdata = []
        for reaction, template, entry in trainingSet:
            
            if isinstance(reaction.kinetics, Arrhenius) or isinstance(reaction.kinetics, KineticsData):
                kd = [reaction.kinetics.getRateCoefficient(T) / reaction.degeneracy for T in Tdata]
            elif isinstance(reaction.kinetics, ArrheniusEP):
                kd = [reaction.kinetics.getRateCoefficient(T, 0) / reaction.degeneracy for T in Tdata]
            else:
                raise Exception('Unexpected kinetics model of type {0} for reaction {1}.'.format(reaction.kinetics.__class__, reaction))
            kdata.append(kd)
            
            # Create every combination of each group and its ancestors with each other
            combinations = []
            for group in template:
                groups = [group]; groups.extend(family.groups.ancestors(group))
                combinations.append(groups)
            combinations = getAllCombinations(combinations)
            
            # Add a row to the matrix for each combination at each temperature
            for t, T in enumerate(Tdata):
                logT = math.log(T)
                Tinv = 1000.0 / (constants.R * T)
                for groups in combinations:
                    Arow = []
                    for group in groupList:
                        if group in groups:
                            Arow.extend([1,logT,-Tinv])
                        else:
                            Arow.extend([0,0,0])
                    Arow.extend([1,logT,-Tinv])
                    brow = math.log(kd[t])
                    A.append(Arow); b.append(brow)
        
        if len(A) == 0:
            logging.warning('Unable to fit kinetics groups for family "{0}"; no valid data found.'.format(family.groups.label))
            return
        A = numpy.array(A)
        b = numpy.array(b)
        kdata = numpy.array(kdata)
        
        x, residues, rank, s = numpy.linalg.lstsq(A, b)
        
        # Store the results
        family.groups.top[0].data = Arrhenius(
            A = (math.exp(x[-3]),kunits),
            n = x[-2],
            Ea = (x[-1]*1000.,"J/mol"),
            T0 = (1,"K"),
        )
        for i, group in enumerate(groupList):
            group.data = Arrhenius(
                A = (math.exp(x[3*i]),kunits),
                n = x[3*i+1],
                Ea = (x[3*i+2]*1000.,"J/mol"),
                T0 = (1,"K"),
            )
        
        # Print the results
        print '======================================= =========== =========== ==========='
        print 'Group                                   log A (SI)  n           Ea (kJ/mol)   '
        print '======================================= =========== =========== ==========='
        entry = family.groups.top[0]
        label = ', '.join(['%s' % (top.label) for top in family.groups.top])
        logA = math.log10(entry.data.A.value)
        n = entry.data.n.value
        Ea = entry.data.Ea.value / 1000.
        print '%-39s %11.3f %11.3f %11.3f' % (label, logA, n, Ea)
        print '--------------------------------------- ----------- ----------- -----------'
        for i, group in enumerate(groupList):
            label = group.label
            logA = math.log10(group.data.A.value)
            n = group.data.n.value
            Ea = group.data.Ea.value / 1000.
            print '%-39s %11.3f %11.3f %11.3f' % (label, logA, n, Ea)
        print '======================================= =========== =========== ==========='
        
    
    elif method == 'Arrhenius2':
        # Fit Arrhenius parameters (A, n, Ea) by training against (A, n, Ea) values
        
        A = []; b = []
        
        for reaction, template, entry in trainingSet:
            
            # Create every combination of each group and its ancestors with each other
            combinations = []
            for group in template:
                groups = [group]; groups.extend(family.groups.ancestors(group))
                combinations.append(groups)
            combinations = getAllCombinations(combinations)
                    
            # Add a row to the matrix for each parameter
            if isinstance(entry.data, Arrhenius) or (isinstance(entry.data, ArrheniusEP) and entry.data.alpha.value == 0):
                for groups in combinations:
                    Arow = []
                    for group in groupList:
                        if group in groups:
                            Arow.append(1)
                        else:
                            Arow.append(0)
                    Arow.append(1)
                    Ea = entry.data.E0.value if isinstance(entry.data, ArrheniusEP) else entry.data.Ea.value
                    brow = [math.log(entry.data.A.value), entry.data.n.value, Ea / 1000.]
                    A.append(Arow); b.append(brow)
        
        if len(A) == 0:
            logging.warning('Unable to fit kinetics groups for family "{0}"; no valid data found.'.format(family.groups.label))
            return
        A = numpy.array(A)
        b = numpy.array(b)
        
        x, residues, rank, s = numpy.linalg.lstsq(A, b)
        
        # Store the results
        family.groups.top[0].data = Arrhenius(
            A = (math.exp(x[-1,0]),kunits),
            n = x[-1,1],
            Ea = (x[-1,2]*1000.,"J/mol"),
            T0 = (1,"K"),
        )
        for i, group in enumerate(groupList):
            group.data = Arrhenius(
                A = (math.exp(x[i,0]),kunits),
                n = x[i,1],
                Ea = (x[i,2]*1000.,"J/mol"),
                T0 = (1,"K"),
            )
        
        # Print the results
        print '======================================= =========== =========== ==========='
        print 'Group                                   log A (SI)  n           Ea (kJ/mol)   '
        print '======================================= =========== =========== ==========='
        entry = family.groups.top[0]
        label = ', '.join(['%s' % (top.label) for top in family.groups.top])
        logA = math.log10(entry.data.A.value)
        n = entry.data.n.value
        Ea = entry.data.Ea.value / 1000.
        print '%-39s %11.3f %11.3f %11.3f' % (label, logA, n, Ea)
        print '--------------------------------------- ----------- ----------- -----------'
        for i, group in enumerate(groupList):
            label = group.label
            logA = math.log10(group.data.A.value)
            n = group.data.n.value
            Ea = group.data.Ea.value / 1000.
            print '%-39s %11.3f %11.3f %11.3f' % (label, logA, n, Ea)
        print '======================================= =========== =========== ==========='
    
    # Add a note to the history of each changed item indicating that we've generated new group values
    changed = False
    event = [time.asctime(),user,'action','Generated new group additivity values for this entry.']
    for label, entry in family.groups.entries.iteritems():
        if entry.data is not None and old_entries.has_key(label):
            if (isinstance(entry.data, KineticsData) and 
                isinstance(old_entries[label], KineticsData) and
                len(entry.data.kdata.values) == len(old_entries[label].kdata.values) and
                all(abs(entry.data.kdata.values / old_entries[label].kdata.values - 1) < 0.01)):
                #print "New group values within 1% of old."
                pass
            elif (isinstance(entry.data, Arrhenius) and 
                isinstance(old_entries[label], Arrhenius) and
                abs(entry.data.A.value / old_entries[label].A.value - 1) < 0.01 and
                abs(entry.data.n.value / old_entries[label].n.value - 1) < 0.01 and
                abs(entry.data.Ea.value / old_entries[label].Ea.value - 1) < 0.01 and
                abs(entry.data.T0.value / old_entries[label].T0.value - 1) < 0.01):
                #print "New group values within 1% of old."
                pass
            else:
                changed = True
                entry.history.append(event)
    
    return changed
Example #30
0
def parse_kinetics(xml, rxn, pdep=False):
    """
    Parse the information from a single kinetics XML. If a P-dep
    expression is encountered and pdep is set to False, a
    KineticsError will be raised.
    """
    tree = ET.parse(xml)
    root = tree.getroot()
    prime_id = root.attrib['primeID']  # Extract ID

    # Extract namespace
    ns = nsprog.match(root.tag).group(0)

    # Extract rate law type
    try:
        rate_law_type = root.attrib['rateLawType']
    except KeyError:
        # Check later if there is a rate coefficient
        rate_law_type = None
    else:
        if rate_law_type == 'mass action':
            pass
        elif rate_law_type == 'sum':
            # Save the links to the kinetics that are meant to be summed up
            reaction_link = root.find(ns + 'reactionLink')
            links = {
                rate_link.attrib['primeID']
                for rate_link in reaction_link
            }
            return PrIMeKinetics(prime_id, rate_law_type='sum', links=links)
        elif rate_law_type in ('third body', 'unimolecular',
                               'chemical activation'):
            if pdep:
                raise NotImplementedError(
                    'P-dep has not been implemented for "{}" type.'.format(
                        rate_law_type))
            else:
                raise KineticsError(
                    'P-dep kinetics: {}.'.format(rate_law_type))
        else:
            raise Exception('Unknown rate law type: {}'.format(rate_law_type))

    # Make sure kinetics exist
    rate_coeff = root.find(ns + 'rateCoefficient')
    if rate_coeff is None:
        raise KineticsError('No rate coefficient found.')

    # Extract kinetics
    direction = rate_coeff.attrib['direction'].lower()

    expression = rate_coeff.find(ns + 'expression')
    form = expression.attrib['form'].lower()
    if form == 'constant':
        raise KineticsError('Missing activation energy.')
    elif form != 'arrhenius':
        raise NotImplementedError(
            'Kinetics form "{}" has not been implemented yet.'.format(form))

    a = e = None
    n = 0.0
    has_n = False
    for param in expression:
        if param.tag == ns + 'parameter':
            if param.attrib['name'].lower() not in valid_params:
                raise Exception('Unknown parameter: {}'.format(
                    param.attrib['name']))
            if param.attrib['name'].lower() == 'a':
                units = _format_units(param.attrib['units'], rxn, direction)
                a = (float(param.find(ns + 'value').text), units)
            elif param.attrib['name'].lower() == 'e':
                units = _format_units(param.attrib['units'], rxn, direction)
                e = (float(param.find(ns + 'value').text), units)
            elif param.attrib['name'].lower() == 'n':
                n = float(param.find(ns + 'value').text)
                has_n = True

    if a is None:
        raise KineticsError('Missing prefactor.')
    if e is None:
        raise KineticsError('Missing activation energy.')

    # If A has units involving mol and is very small, then it's probably molecules
    # Smallness is assessed based on an "effective" A at 1000 K (i.e. A*T^n)
    if a[1] == 'cm^3/(mol*s)' and a[0] * 1000.0**n < 1.0:
        a[1].replace('mol', 'molecule')
        warnings.warn(
            'Replaced "mol" by "molecule" in units for {}.'.format(xml))

    # Extract year
    ref = root.find(ns + 'bibliographyLink')
    year_match = yearprog.search(ref.attrib['preferredKey'])
    if year_match is None:
        year = None
        warnings.warn('No year in {}.'.format(xml))
    else:
        year = int(year_match.group(0))

    return PrIMeKinetics(prime_id,
                         expression=Arrhenius(A=a, n=n, Ea=e),
                         has_n=has_n,
                         rate_law_type=rate_law_type,
                         direction=direction,
                         year=year)
Example #31
0
    def getForwardReactionForFamilyEntry(self, entry, family, thermoDatabase):
        """
        For a given `entry` for a reaction of the given reaction `family` (the
        string label of the family), return the reaction with kinetics and
        degeneracy for the "forward" direction as defined by the reaction 
        family. For families that are their own reverse, the direction the
        kinetics is given in will be preserved. If the entry contains 
        functional groups for the reactants, assume that it is given in the 
        forward direction and do nothing. Returns the reaction in the direction
        consistent with the reaction family template, and the matching template.
        Note that the returned reaction will have its kinetics and degeneracy
        set appropriately.
        
        In order to reverse the reactions that are given in the reverse of the
        direction the family is defined, we need to compute the thermodynamics
        of the reactants and products. For this reason you must also pass
        the `thermoDatabase` to use to generate the thermo data.
        """
        def generateThermoData(species, thermoDatabase):
            thermoData = [thermoDatabase.getThermoData(species)]
            thermoData.sort(key=lambda x: x.getEnthalpy(298))
            return thermoData[0]

        def matchSpeciesToMolecules(species, molecules):
            if len(species) == len(molecules) == 1:
                return species[0].isIsomorphic(molecules[0])
            elif len(species) == len(molecules) == 2:
                if species[0].isIsomorphic(
                        molecules[0]) and species[1].isIsomorphic(
                            molecules[1]):
                    return True
                elif species[0].isIsomorphic(
                        molecules[1]) and species[1].isIsomorphic(
                            molecules[0]):
                    return True
            return False

        reaction = None
        template = None

        # Get the indicated reaction family
        try:
            groups = self.families[family].groups
        except KeyError:
            raise ValueError(
                'Invalid value "{0}" for family parameter.'.format(family))

        if all([(isinstance(reactant, Group)
                 or isinstance(reactant, LogicNode))
                for reactant in entry.item.reactants]):
            # The entry is a rate rule, containing functional groups only
            # By convention, these are always given in the forward direction and
            # have kinetics defined on a per-site basis
            reaction = Reaction(
                reactants=entry.item.reactants[:],
                products=[],
                kinetics=entry.data,
                degeneracy=1,
            )
            template = [
                groups.entries[label] for label in entry.label.split(';')
            ]

        elif (all([
                isinstance(reactant, Molecule)
                for reactant in entry.item.reactants
        ]) and all(
            [isinstance(product, Molecule)
             for product in entry.item.products])):
            # The entry is a real reaction, containing molecules
            # These could be defined for either the forward or reverse direction
            # and could have a reaction-path degeneracy

            reaction = Reaction(reactants=[], products=[])
            for molecule in entry.item.reactants:
                reactant = Species(molecule=[molecule])
                reactant.generateResonanceIsomers()
                reactant.thermo = generateThermoData(reactant, thermoDatabase)
                reaction.reactants.append(reactant)
            for molecule in entry.item.products:
                product = Species(molecule=[molecule])
                product.generateResonanceIsomers()
                product.thermo = generateThermoData(product, thermoDatabase)
                reaction.products.append(product)

            # Generate all possible reactions involving the reactant species
            generatedReactions = self.generateReactionsFromFamilies(
                [reactant.molecule for reactant in reaction.reactants], [],
                only_families=[family])

            # Remove from that set any reactions that don't produce the desired reactants and products
            forward = []
            reverse = []
            for rxn in generatedReactions:
                if matchSpeciesToMolecules(
                        reaction.reactants,
                        rxn.reactants) and matchSpeciesToMolecules(
                            reaction.products, rxn.products):
                    forward.append(rxn)
                if matchSpeciesToMolecules(
                        reaction.reactants,
                        rxn.products) and matchSpeciesToMolecules(
                            reaction.products, rxn.reactants):
                    reverse.append(rxn)

            # We should now know whether the reaction is given in the forward or
            # reverse direction
            if len(forward) == 1 and len(reverse) == 0:
                # The reaction is in the forward direction, so use as-is
                reaction = forward[0]
                template = reaction.template
                # Don't forget to overwrite the estimated kinetics from the database with the kinetics for this entry
                reaction.kinetics = entry.data
            elif len(reverse) == 1 and len(forward) == 0:
                # The reaction is in the reverse direction
                # First fit Arrhenius kinetics in that direction
                Tdata = 1000.0 / numpy.arange(0.5, 3.301, 0.1, numpy.float64)
                kdata = numpy.zeros_like(Tdata)
                for i in range(Tdata.shape[0]):
                    kdata[i] = entry.data.getRateCoefficient(
                        Tdata[i]) / reaction.getEquilibriumConstant(Tdata[i])
                kunits = 'm^3/(mol*s)' if len(
                    reverse[0].reactants) == 2 else 's^-1'
                kinetics = Arrhenius().fitToData(Tdata, kdata, kunits, T0=1.0)
                kinetics.Tmin = entry.data.Tmin
                kinetics.Tmax = entry.data.Tmax
                kinetics.Pmin = entry.data.Pmin
                kinetics.Pmax = entry.data.Pmax
                # Now flip the direction
                reaction = reverse[0]
                reaction.kinetics = kinetics
                template = reaction.template
            elif len(reverse) > 0 and len(forward) > 0:
                print 'FAIL: Multiple reactions found for {0!r}.'.format(
                    entry.label)
            elif len(reverse) == 0 and len(forward) == 0:
                print 'FAIL: No reactions found for "%s".' % (entry.label)
            else:
                print 'FAIL: Unable to estimate kinetics for {0!r}.'.format(
                    entry.label)

        assert reaction is not None
        assert template is not None
        return reaction, template
def generateRules(family, database):
    """
    For a given reaction `family` label, generate additional rate rules from
    the corresponding depository training set. This function does automatically
    what users used to do by hand to construct a rate rule from reaction
    kinetics found in the literature, i.e. determine the groups involved,
    adjust to a per-site basis, etc.
    """

    # Load rules and determine starting index
    rules = family.rules
    index = max([entry.index for entry in rules.entries.values()] or [0]) + 1

    # Load training entries
    for depository in family.depositories:
        if 'training' in depository.name:
            entries = sorted(depository.entries.values(),
                             key=lambda entry: (entry.index, entry.label))
            break

    # Generate a rate rule for each training entry
    for entry in entries:

        # Load entry's reaction, template, and kinetics
        reaction, template = database.kinetics.getForwardReactionForFamilyEntry(entry=entry,
                                                                                family=family.name,
                                                                                thermoDatabase=database.thermo)
        kinetics = reaction.kinetics

        # Convert KineticsData to Arrhenius
        if isinstance(kinetics, KineticsData):
            kinetics = Arrhenius().fitToData(Tdata=kinetics.Tdata.values,
                                             kdata=kinetics.kdata.values,
                                             kunits=kinetics.kdata.units,
                                             T0=1)

        # Ignore other kinetics types
        if not isinstance(kinetics, Arrhenius):
            continue

        # Change reference temperature to 1 K if necessary
        if kinetics.T0.value != 1:
            kinetics = kinetics.changeT0(1)

        # Convert kinetics to a per-site basis
        kinetics.A.value /= reaction.degeneracy

        # Convert to ArrheniusEP
        kinetics = ArrheniusEP(A=kinetics.A,
                               n=kinetics.n,
                               alpha=0,
                               E0=kinetics.Ea,
                               Tmin=kinetics.Tmin,
                               Tmax=kinetics.Tmax)

        # Add new rate rule
        rules.entries[index] = Entry(index=index,
                                     label=';'.join([group.label for group in template]),
                                     item=Reaction(reactants=template[:],
                                                   products=None),
                                     data=kinetics,
                                     reference=entry.reference,
                                     rank=entry.rank,
                                     shortDesc=entry.shortDesc,
                                     longDesc=entry.longDesc,
                                     history=entry.history)
        index += 1
Example #33
0
    def setUp(self):
        """
        A method that is called prior to each unit test in this class.
        """
        ethylene = Species(
            label = 'C2H4',
            conformer = Conformer(
                E0 = (44.7127, 'kJ/mol'),
                modes = [
                    IdealGasTranslation(
                        mass = (28.0313, 'amu'),
                    ),
                    NonlinearRotor(
                        inertia = (
                            [3.41526, 16.6498, 20.065],
                            'amu*angstrom^2',
                        ),
                        symmetry = 4,
                    ),
                    HarmonicOscillator(
                        frequencies = (
                            [828.397, 970.652, 977.223, 1052.93, 1233.55, 1367.56, 1465.09, 1672.25, 3098.46, 3111.7, 3165.79, 3193.54],
                            'cm^-1',
                        ),
                    ),
                ],
                spinMultiplicity = 1,
                opticalIsomers = 1,
            ),
        )
        
        hydrogen = Species(          
            label = 'H',
            conformer = Conformer(
                E0 = (211.794, 'kJ/mol'),
                modes = [
                    IdealGasTranslation(
                        mass = (1.00783, 'amu'),
                    ),
                ],
                spinMultiplicity = 2,
                opticalIsomers = 1,
            ),
        )
        
        ethyl = Species(
            label = 'C2H5',
            conformer = Conformer(
                E0 = (111.603, 'kJ/mol'),
                modes = [
                    IdealGasTranslation(
                        mass = (29.0391, 'amu'),
                    ),
                    NonlinearRotor(
                        inertia = (
                            [4.8709, 22.2353, 23.9925],
                            'amu*angstrom^2',
                        ),
                        symmetry = 1,
                    ),
                    HarmonicOscillator(
                        frequencies = (
                            [482.224, 791.876, 974.355, 1051.48, 1183.21, 1361.36, 1448.65, 1455.07, 1465.48, 2688.22, 2954.51, 3033.39, 3101.54, 3204.73],
                            'cm^-1',
                        ),
                    ),
                    HinderedRotor(
                        inertia = (1.11481, 'amu*angstrom^2'),
                        symmetry = 6,
                        barrier = (0.244029, 'kJ/mol'),
                        semiclassical = None,
                    ),
                ],
                spinMultiplicity = 2,
                opticalIsomers = 1,
            ),
        )
        
        TS = TransitionState(
            label = 'TS',
            conformer = Conformer(
                E0 = (266.694, 'kJ/mol'),
                modes = [
                    IdealGasTranslation(
                        mass = (29.0391, 'amu'),
                    ),
                    NonlinearRotor(
                        inertia = (
                            [6.78512, 22.1437, 22.2114],
                            'amu*angstrom^2',
                        ),
                        symmetry = 1,
                    ),
                    HarmonicOscillator(
                        frequencies = (
                            [412.75, 415.206, 821.495, 924.44, 982.714, 1024.16, 1224.21, 1326.36, 1455.06, 1600.35, 3101.46, 3110.55, 3175.34, 3201.88],
                            'cm^-1',
                        ),
                    ),
                ],
                spinMultiplicity = 2,
                opticalIsomers = 1,
            ),
            frequency = (-750.232, 'cm^-1'),
        )
        
        self.reaction = Reaction(
            reactants = [hydrogen, ethylene],
            products = [ethyl], 
            kinetics = Arrhenius(
                A = (501366000.0, 'cm^3/(mol*s)'),
                n = 1.637,
                Ea = (4.32508, 'kJ/mol'),
                T0 = (1, 'K'),
                Tmin = (300, 'K'),
                Tmax = (2500, 'K'),
            ),
            transitionState = TS,
        )
    
        # CC(=O)O[O]
        acetylperoxy = Species(
            label='acetylperoxy',
            thermo=Wilhoit(Cp0=(4.0*constants.R,"J/(mol*K)"), CpInf=(21.0*constants.R,"J/(mol*K)"), a0=-3.95, a1=9.26, a2=-15.6, a3=8.55, B=(500.0,"K"), H0=(-6.151e+04,"J/mol"), S0=(-790.2,"J/(mol*K)")),
        )

        # C[C]=O
        acetyl = Species(
            label='acetyl',
            thermo=Wilhoit(Cp0=(4.0*constants.R,"J/(mol*K)"), CpInf=(15.5*constants.R,"J/(mol*K)"), a0=0.2541, a1=-0.4712, a2=-4.434, a3=2.25, B=(500.0,"K"), H0=(-1.439e+05,"J/mol"), S0=(-524.6,"J/(mol*K)")),
        )

        # [O][O]
        oxygen = Species(
            label='oxygen',
            thermo=Wilhoit(Cp0=(3.5*constants.R,"J/(mol*K)"), CpInf=(4.5*constants.R,"J/(mol*K)"), a0=-0.9324, a1=26.18, a2=-70.47, a3=44.12, B=(500.0,"K"), H0=(1.453e+04,"J/mol"), S0=(-12.19,"J/(mol*K)")),
        )
        
        self.reaction2 = Reaction(
            reactants=[acetyl, oxygen], 
            products=[acetylperoxy], 
            kinetics = Arrhenius(
                A = (2.65e12, 'cm^3/(mol*s)'),
                n = 0.0,
                Ea = (0.0, 'kJ/mol'),
                T0 = (1, 'K'),
                Tmin = (300, 'K'),
                Tmax = (2000, 'K'),
            ),
        )
Example #34
0
def loadFAMEInput(path, moleculeDict=None):
    """
    Load the contents of a FAME input file into the MEASURE object. FAME
    is an early version of MEASURE written in Fortran and used by RMG-Java.
    This script enables importing FAME input files into MEASURE so we can
    use the additional functionality that MEASURE provides. Note that it
    is mostly designed to load the FAME input files generated automatically
    by RMG-Java, and may not load hand-crafted FAME input files. If you
    specify a `moleculeDict`, then this script will use it to associate
    the species with their structures.
    """
    
    def readMeaningfulLine(f):
        line = f.readline()
        while line != '':
            line = line.strip()
            if len(line) > 0 and line[0] != '#':
                return line
            else:
                line = f.readline()
        return ''

    moleculeDict = moleculeDict or {}

    logging.info('Loading file "{0}"...'.format(path))
    f = open(path)

    job = PressureDependenceJob(network=None)
    
    # Read method
    method = readMeaningfulLine(f).lower()
    if method == 'modifiedstrongcollision': 
        job.method = 'modified strong collision'
    elif method == 'reservoirstate': 
        job.method = 'reservoir state'

    # Read temperatures
    Tcount, Tunits, Tmin, Tmax = readMeaningfulLine(f).split()
    job.Tmin = Quantity(float(Tmin), Tunits) 
    job.Tmax = Quantity(float(Tmax), Tunits)
    job.Tcount = int(Tcount)
    Tlist = []
    for i in range(int(Tcount)):
        Tlist.append(float(readMeaningfulLine(f)))
    job.Tlist = Quantity(Tlist, Tunits)
    
    # Read pressures
    Pcount, Punits, Pmin, Pmax = readMeaningfulLine(f).split()
    job.Pmin = Quantity(float(Pmin), Punits) 
    job.Pmax = Quantity(float(Pmax), Punits)
    job.Pcount = int(Pcount)
    Plist = []
    for i in range(int(Pcount)):
        Plist.append(float(readMeaningfulLine(f)))
    job.Plist = Quantity(Plist, Punits)
    
    # Read interpolation model
    model = readMeaningfulLine(f).split()
    if model[0].lower() == 'chebyshev':
        job.interpolationModel = ('chebyshev', int(model[1]), int(model[2]))
    elif model[0].lower() == 'pdeparrhenius':
        job.interpolationModel = ('pdeparrhenius',)
    
    # Read grain size or number of grains
    job.minimumGrainCount = 0
    job.maximumGrainSize = None
    for i in range(2):
        data = readMeaningfulLine(f).split()
        if data[0].lower() == 'numgrains':
            job.minimumGrainCount = int(data[1])
        elif data[0].lower() == 'grainsize':
            job.maximumGrainSize = (float(data[2]), data[1])

    # A FAME file is almost certainly created during an RMG job, so use RMG mode
    job.rmgmode = True

    # Create the Network
    job.network = Network()

    # Read collision model
    data = readMeaningfulLine(f)
    assert data.lower() == 'singleexpdown'
    alpha0units, alpha0 = readMeaningfulLine(f).split()
    T0units, T0 = readMeaningfulLine(f).split()
    n = readMeaningfulLine(f)
    energyTransferModel = SingleExponentialDown(
        alpha0 = Quantity(float(alpha0), alpha0units),
        T0 = Quantity(float(T0), T0units),
        n = float(n),
    )
    
    speciesDict = {}

    # Read bath gas parameters
    bathGas = Species(label='bath_gas', energyTransferModel=energyTransferModel)
    molWtunits, molWt = readMeaningfulLine(f).split()
    if molWtunits == 'u': molWtunits = 'amu'
    bathGas.molecularWeight = Quantity(float(molWt), molWtunits)
    sigmaLJunits, sigmaLJ = readMeaningfulLine(f).split()
    epsilonLJunits, epsilonLJ = readMeaningfulLine(f).split()
    assert epsilonLJunits == 'J'
    bathGas.transportData = TransportData(
        sigma = Quantity(float(sigmaLJ), sigmaLJunits),
        epsilon = Quantity(float(epsilonLJ) / constants.kB, 'K'),
    )
    job.network.bathGas = {bathGas: 1.0}
    
    # Read species data
    Nspec = int(readMeaningfulLine(f))
    for i in range(Nspec):
        species = Species()
        species.conformer = Conformer()
        species.energyTransferModel = energyTransferModel
        
        # Read species label
        species.label = readMeaningfulLine(f)
        speciesDict[species.label] = species
        if species.label in moleculeDict:
            species.molecule = [moleculeDict[species.label]]
        
        # Read species E0
        E0units, E0 = readMeaningfulLine(f).split()
        species.conformer.E0 = Quantity(float(E0), E0units)
        species.conformer.E0.units = 'kJ/mol'
        
        # Read species thermo data
        H298units, H298 = readMeaningfulLine(f).split()
        S298units, S298 = readMeaningfulLine(f).split()
        Cpcount, Cpunits = readMeaningfulLine(f).split()
        Cpdata = []
        for i in range(int(Cpcount)):
            Cpdata.append(float(readMeaningfulLine(f)))
        if S298units == 'J/mol*K': S298units = 'J/(mol*K)'
        if Cpunits == 'J/mol*K': Cpunits = 'J/(mol*K)'
        species.thermo = ThermoData(
            H298 = Quantity(float(H298), H298units),
            S298 = Quantity(float(S298), S298units),
            Tdata = Quantity([300,400,500,600,800,1000,1500], "K"),
            Cpdata = Quantity(Cpdata, Cpunits),
            Cp0 = (Cpdata[0], Cpunits),
            CpInf = (Cpdata[-1], Cpunits),
        )
        
        # Read species collision parameters
        molWtunits, molWt = readMeaningfulLine(f).split()
        if molWtunits == 'u': molWtunits = 'amu'
        species.molecularWeight = Quantity(float(molWt), molWtunits)
        sigmaLJunits, sigmaLJ = readMeaningfulLine(f).split()
        epsilonLJunits, epsilonLJ = readMeaningfulLine(f).split()
        assert epsilonLJunits == 'J'
        species.transportData = TransportData(
            sigma = Quantity(float(sigmaLJ), sigmaLJunits),
            epsilon = Quantity(float(epsilonLJ) / constants.kB, 'K'),
        )
        
        # Read species vibrational frequencies
        freqCount, freqUnits = readMeaningfulLine(f).split()
        frequencies = []
        for j in range(int(freqCount)):
            frequencies.append(float(readMeaningfulLine(f)))
        species.conformer.modes.append(HarmonicOscillator(
            frequencies = Quantity(frequencies, freqUnits),
        ))
        
        # Read species external rotors
        rotCount, rotUnits = readMeaningfulLine(f).split()
        if int(rotCount) > 0:
            raise NotImplementedError('Cannot handle external rotational modes in FAME input.')
        
        # Read species internal rotors
        freqCount, freqUnits = readMeaningfulLine(f).split()
        frequencies = []
        for j in range(int(freqCount)):
            frequencies.append(float(readMeaningfulLine(f)))
        barrCount, barrUnits = readMeaningfulLine(f).split()
        barriers = []
        for j in range(int(barrCount)):
            barriers.append(float(readMeaningfulLine(f)))
        if barrUnits == 'cm^-1':
            barrUnits = 'J/mol'
            barriers = [barr * constants.h * constants.c * constants.Na * 100. for barr in barriers]
        elif barrUnits in ['Hz', 's^-1']:
            barrUnits = 'J/mol'
            barriers = [barr * constants.h * constants.Na for barr in barriers]
        elif barrUnits != 'J/mol':
            raise Exception('Unexpected units "{0}" for hindered rotor barrier height.'.format(barrUnits))
        inertia = [V0 / 2.0 / (nu * constants.c * 100.)**2 / constants.Na for nu, V0 in zip(frequencies, barriers)]
        for I, V0 in zip(inertia, barriers):
            species.conformer.modes.append(HinderedRotor(
                inertia = Quantity(I,"kg*m^2"), 
                barrier = Quantity(V0,barrUnits), 
                symmetry = 1,
                semiclassical = False,
            ))
            
        # Read overall symmetry number
        species.conformer.spinMultiplicity = int(readMeaningfulLine(f))
        
    # Read isomer, reactant channel, and product channel data
    Nisom = int(readMeaningfulLine(f))
    Nreac = int(readMeaningfulLine(f))
    Nprod = int(readMeaningfulLine(f))
    for i in range(Nisom):
        data = readMeaningfulLine(f).split()
        assert data[0] == '1'
        job.network.isomers.append(speciesDict[data[1]])
    for i in range(Nreac):
        data = readMeaningfulLine(f).split()
        assert data[0] == '2'
        job.network.reactants.append([speciesDict[data[1]], speciesDict[data[2]]])
    for i in range(Nprod):
        data = readMeaningfulLine(f).split()
        if data[0] == '1':
            job.network.products.append([speciesDict[data[1]]])
        elif data[0] == '2':
            job.network.products.append([speciesDict[data[1]], speciesDict[data[2]]])

    # Read path reactions
    Nrxn = int(readMeaningfulLine(f))
    for i in range(Nrxn):
        
        # Read and ignore reaction equation
        equation = readMeaningfulLine(f)
        reaction = Reaction(transitionState=TransitionState(), reversible=True)
        job.network.pathReactions.append(reaction)
        reaction.transitionState.conformer = Conformer()
        
        # Read reactant and product indices
        data = readMeaningfulLine(f).split()
        reac = int(data[0]) - 1
        prod = int(data[1]) - 1
        if reac < Nisom:
            reaction.reactants = [job.network.isomers[reac]]
        elif reac < Nisom+Nreac:
            reaction.reactants = job.network.reactants[reac-Nisom]
        else:
            reaction.reactants = job.network.products[reac-Nisom-Nreac]
        if prod < Nisom:
            reaction.products = [job.network.isomers[prod]]
        elif prod < Nisom+Nreac:
            reaction.products = job.network.reactants[prod-Nisom]
        else:
            reaction.products = job.network.products[prod-Nisom-Nreac]
        
        # Read reaction E0
        E0units, E0 = readMeaningfulLine(f).split()
        reaction.transitionState.conformer.E0 = Quantity(float(E0), E0units)
        reaction.transitionState.conformer.E0.units = 'kJ/mol'
        
        # Read high-pressure limit kinetics
        data = readMeaningfulLine(f)
        assert data.lower() == 'arrhenius'
        Aunits, A = readMeaningfulLine(f).split()
        if '/' in Aunits:
            index = Aunits.find('/')
            Aunits = '{0}/({1})'.format(Aunits[0:index], Aunits[index+1:])
        Eaunits, Ea = readMeaningfulLine(f).split()
        n = readMeaningfulLine(f)
        reaction.kinetics = Arrhenius(
            A = Quantity(float(A), Aunits),
            Ea = Quantity(float(Ea), Eaunits),
            n = Quantity(float(n)),
        )
        reaction.kinetics.Ea.units = 'kJ/mol'

    f.close()
    
    job.network.isomers = [Configuration(isomer) for isomer in job.network.isomers]
    job.network.reactants = [Configuration(*reactants) for reactants in job.network.reactants]
    job.network.products = [Configuration(*products) for products in job.network.products]

    return job
Example #35
0
    def update(self, reaction_model, pdep_settings):
        """
        Regenerate the :math:`k(T,P)` values for this partial network if the
        network is marked as invalid.
        """
        from rmgpy.kinetics import Arrhenius, KineticsData, MultiArrhenius

        # Get the parameters for the pressure dependence calculation
        job = pdep_settings
        job.network = self
        output_directory = pdep_settings.output_file

        Tmin = job.Tmin.value_si
        Tmax = job.Tmax.value_si
        Pmin = job.Pmin.value_si
        Pmax = job.Pmax.value_si
        Tlist = job.Tlist.value_si
        Plist = job.Plist.value_si
        maximum_grain_size = job.maximum_grain_size.value_si if job.maximum_grain_size is not None else 0.0
        minimum_grain_count = job.minimum_grain_count
        method = job.method
        interpolation_model = job.interpolation_model
        active_j_rotor = job.active_j_rotor
        active_k_rotor = job.active_k_rotor
        rmgmode = job.rmgmode

        # Figure out which configurations are isomers, reactant channels, and product channels
        self.update_configurations(reaction_model)

        # Make sure we have high-P kinetics for all path reactions
        for rxn in self.path_reactions:
            if rxn.kinetics is None and rxn.reverse.kinetics is None:
                raise PressureDependenceError(
                    'Path reaction {0} with no high-pressure-limit kinetics encountered in '
                    'PDepNetwork #{1:d}.'.format(rxn, self.index))
            elif rxn.kinetics is not None and rxn.kinetics.is_pressure_dependent(
            ) and rxn.network_kinetics is None:
                raise PressureDependenceError(
                    'Pressure-dependent kinetics encountered for path reaction {0} in '
                    'PDepNetwork #{1:d}.'.format(rxn, self.index))

        # Do nothing if the network is already valid
        if self.valid:
            return
        # Do nothing if there are no explored wells
        if len(self.explored) == 0 and len(self.source) > 1:
            return
        # Log the network being updated
        logging.info("Updating {0!s}".format(self))

        # Generate states data for unimolecular isomers and reactants if necessary
        for isomer in self.isomers:
            spec = isomer.species[0]
            if not spec.has_statmech():
                spec.generate_statmech()
        for reactants in self.reactants:
            for spec in reactants.species:
                if not spec.has_statmech():
                    spec.generate_statmech()
        # Also generate states data for any path reaction reactants, so we can
        # always apply the ILT method in the direction the kinetics are known
        for reaction in self.path_reactions:
            for spec in reaction.reactants:
                if not spec.has_statmech():
                    spec.generate_statmech()
        # While we don't need the frequencies for product channels, we do need
        # the E0, so create a conformer object with the E0 for the product
        # channel species if necessary
        for products in self.products:
            for spec in products.species:
                if spec.conformer is None:
                    spec.conformer = Conformer(E0=spec.get_thermo_data().E0)

        # Determine transition state energies on potential energy surface
        # In the absence of any better information, we simply set it to
        # be the reactant ground-state energy + the activation energy
        # Note that we need Arrhenius kinetics in order to do this
        for rxn in self.path_reactions:
            if rxn.kinetics is None:
                raise Exception(
                    'Path reaction "{0}" in PDepNetwork #{1:d} has no kinetics!'
                    .format(rxn, self.index))
            elif isinstance(rxn.kinetics, KineticsData):
                if len(rxn.reactants) == 1:
                    kunits = 's^-1'
                elif len(rxn.reactants) == 2:
                    kunits = 'm^3/(mol*s)'
                elif len(rxn.reactants) == 3:
                    kunits = 'm^6/(mol^2*s)'
                else:
                    kunits = ''
                rxn.kinetics = Arrhenius().fit_to_data(
                    Tlist=rxn.kinetics.Tdata.value_si,
                    klist=rxn.kinetics.kdata.value_si,
                    kunits=kunits)
            elif isinstance(rxn.kinetics, MultiArrhenius):
                logging.info(
                    'Converting multiple kinetics to a single Arrhenius expression for reaction {rxn}'
                    .format(rxn=rxn))
                rxn.kinetics = rxn.kinetics.to_arrhenius(Tmin=Tmin, Tmax=Tmax)
            elif not isinstance(rxn.kinetics,
                                Arrhenius) and rxn.network_kinetics is None:
                raise Exception(
                    'Path reaction "{0}" in PDepNetwork #{1:d} has invalid kinetics '
                    'type "{2!s}".'.format(rxn, self.index,
                                           rxn.kinetics.__class__))
            rxn.fix_barrier_height(force_positive=True)
            if rxn.network_kinetics is None:
                E0 = sum(
                    [spec.conformer.E0.value_si
                     for spec in rxn.reactants]) + rxn.kinetics.Ea.value_si
            else:
                E0 = sum([
                    spec.conformer.E0.value_si for spec in rxn.reactants
                ]) + rxn.network_kinetics.Ea.value_si
            rxn.transition_state = rmgpy.species.TransitionState(
                conformer=Conformer(E0=(E0 * 0.001, "kJ/mol")))

        # Set collision model
        bath_gas = [
            spec for spec in reaction_model.core.species if not spec.reactive
        ]
        assert len(
            bath_gas) > 0, 'No unreactive species to identify as bath gas'

        self.bath_gas = {}
        for spec in bath_gas:
            # is this really the only/best way to weight them?
            self.bath_gas[spec] = 1.0 / len(bath_gas)

        # Save input file
        if not self.label:
            self.label = str(self.index)

        if output_directory:
            job.save_input_file(
                os.path.join(
                    output_directory, 'pdep',
                    'network{0:d}_{1:d}.py'.format(self.index,
                                                   len(self.isomers))))

        self.log_summary(level=logging.INFO)

        # Calculate the rate coefficients
        self.initialize(Tmin, Tmax, Pmin, Pmax, maximum_grain_size,
                        minimum_grain_count, active_j_rotor, active_k_rotor,
                        rmgmode)
        K = self.calculate_rate_coefficients(Tlist, Plist, method)

        # Generate PDepReaction objects
        configurations = []
        configurations.extend([isom.species[:] for isom in self.isomers])
        configurations.extend(
            [reactant.species[:] for reactant in self.reactants])
        configurations.extend(
            [product.species[:] for product in self.products])
        j = configurations.index(self.source)

        for i in range(K.shape[2]):
            if i != j:
                # Find the path reaction
                net_reaction = None
                for r in self.net_reactions:
                    if r.has_template(configurations[j], configurations[i]):
                        net_reaction = r
                # If net reaction does not already exist, make a new one
                if net_reaction is None:
                    net_reaction = PDepReaction(reactants=configurations[j],
                                                products=configurations[i],
                                                network=self,
                                                kinetics=None)
                    net_reaction = reaction_model.make_new_pdep_reaction(
                        net_reaction)
                    self.net_reactions.append(net_reaction)

                    # Place the net reaction in the core or edge if necessary
                    # Note that leak reactions are not placed in the edge
                    if all([s in reaction_model.core.species for s in net_reaction.reactants]) \
                            and all([s in reaction_model.core.species for s in net_reaction.products]):
                        # Check whether netReaction already exists in the core as a LibraryReaction
                        for rxn in reaction_model.core.reactions:
                            if isinstance(rxn, LibraryReaction) \
                                    and rxn.is_isomorphic(net_reaction, either_direction=True) \
                                    and not rxn.allow_pdep_route and not rxn.elementary_high_p:
                                logging.info(
                                    'Network reaction {0} matched an existing core reaction {1}'
                                    ' from the {2} library, and was not added to the model'
                                    .format(str(net_reaction), str(rxn),
                                            rxn.library))
                                break
                        else:
                            reaction_model.add_reaction_to_core(net_reaction)
                    else:
                        # Check whether netReaction already exists in the edge as a LibraryReaction
                        for rxn in reaction_model.edge.reactions:
                            if isinstance(rxn, LibraryReaction) \
                                    and rxn.is_isomorphic(net_reaction, either_direction=True) \
                                    and not rxn.allow_pdep_route and not rxn.elementary_high_p:
                                logging.info(
                                    'Network reaction {0} matched an existing edge reaction {1}'
                                    ' from the {2} library, and was not added to the model'
                                    .format(str(net_reaction), str(rxn),
                                            rxn.library))
                                break
                        else:
                            reaction_model.add_reaction_to_edge(net_reaction)

                # Set/update the net reaction kinetics using interpolation model
                kdata = K[:, :, i, j].copy()
                order = len(net_reaction.reactants)
                kdata *= 1e6**(order - 1)
                kunits = {
                    1: 's^-1',
                    2: 'cm^3/(mol*s)',
                    3: 'cm^6/(mol^2*s)'
                }[order]
                net_reaction.kinetics = job.fit_interpolation_model(
                    Tlist, Plist, kdata, kunits)

                # Check: For each net reaction that has a path reaction, make
                # sure the k(T,P) values for the net reaction do not exceed
                # the k(T) values of the path reaction
                # Only check the k(T,P) value at the highest P and lowest T,
                # as this is the one most likely to be in the high-pressure
                # limit
                t = 0
                p = len(Plist) - 1
                for pathReaction in self.path_reactions:
                    if pathReaction.is_isomerization():
                        # Don't check isomerization reactions, since their
                        # k(T,P) values potentially contain both direct and
                        # well-skipping contributions, and therefore could be
                        # significantly larger than the direct k(T) value
                        # (This can also happen for association/dissociation
                        # reactions, but the effect is generally not too large)
                        continue
                    if pathReaction.reactants == net_reaction.reactants and pathReaction.products == net_reaction.products:
                        if pathReaction.network_kinetics is not None:
                            kinf = pathReaction.network_kinetics.get_rate_coefficient(
                                Tlist[t])
                        else:
                            kinf = pathReaction.kinetics.get_rate_coefficient(
                                Tlist[t])
                        if K[t, p, i,
                             j] > 2 * kinf:  # To allow for a small discretization error
                            logging.warning(
                                'k(T,P) for net reaction {0} exceeds high-P k(T) by {1:g} at {2:g} K, '
                                '{3:g} bar'.format(net_reaction,
                                                   K[t, p, i, j] / kinf,
                                                   Tlist[t], Plist[p] / 1e5))
                            logging.info(
                                '    k(T,P) = {0:9.2e}    k(T) = {1:9.2e}'.
                                format(K[t, p, i, j], kinf))
                        break
                    elif pathReaction.products == net_reaction.reactants and pathReaction.reactants == net_reaction.products:
                        if pathReaction.network_kinetics is not None:
                            kinf = pathReaction.network_kinetics.get_rate_coefficient(
                                Tlist[t]
                            ) / pathReaction.get_equilibrium_constant(Tlist[t])
                        else:
                            kinf = pathReaction.kinetics.get_rate_coefficient(
                                Tlist[t]
                            ) / pathReaction.get_equilibrium_constant(Tlist[t])
                        if K[t, p, i,
                             j] > 2 * kinf:  # To allow for a small discretization error
                            logging.warning(
                                'k(T,P) for net reaction {0} exceeds high-P k(T) by {1:g} at {2:g} K, '
                                '{3:g} bar'.format(net_reaction,
                                                   K[t, p, i, j] / kinf,
                                                   Tlist[t], Plist[p] / 1e5))
                            logging.info(
                                '    k(T,P) = {0:9.2e}    k(T) = {1:9.2e}'.
                                format(K[t, p, i, j], kinf))
                        break

        # Delete intermediate arrays to conserve memory
        self.cleanup()

        # We're done processing this network, so mark it as valid
        self.valid = True
Example #36
0
def queryNISTKinetics(reactants, products, cookiejar):
    """
    Query the NIST website for the gas-phase reaction consisting of the given
    lists of `reactants` and `products`, which should already be in a form that
    NIST understands (e.g. CAS number).    
    """

    entries = []

    # Set the POST data to contain the parameters for our reaction kinetics query
    post = {
        'boolean1': '',
        'boolean2': 'and',
        'boolean3': 'and',
        'boolean4': 'and',
        'category': '0',
        'database': 'kinetics',
        'doc': 'SearchForm',
        'lp1': ' ',
        'lp2': ' ',
        'lp3': ' ',
        'lp4': ' ',
        'relate1': '=',
        'relate2': '=',
        'relate3': '=',
        'relate4': '=',
        'rp1': ' ',
        'rp2': ' ',
        'rp3': ' ',
        'rp4': ' ',
        'type': 'java',
        'Units': '',
    }
    count = 0
    for reactant in reactants:
        count += 1
        post['field{0:d}'.format(count)] = 'reactants'
        post['text{0:d}'.format(count)] = reactant
    for product in products:
        count += 1
        post['field{0:d}'.format(count)] = 'products'
        post['text{0:d}'.format(count)] = product
    post['numberOfFields'] = str(count)

    # Formally query the NIST kinetics database
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))
    request = opener.open('http://kinetics.nist.gov/kinetics/Search.jsp',
                          data=urllib.urlencode(post))
    searchHTML = request.read()
    request.close()

    # Some checks to ensure that the search was successful
    if "No records found" in searchHTML:
        return []
    elif "There was no match in the database for the following input" in searchHTML:
        return []
    elif "Click on a link in the table below to see detail on the selected reaction" in searchHTML:
        return []

    # Parse the returned HTML to extract the kinetics information
    # We use the BeautifulSoup package because the returned HTML is probably
    # not valid XML, so DOM and SAX parsers would choke
    soup = BeautifulSoup(searchHTML)

    form = soup.findAll(name='form', attrs={'name': 'KineticsResults'})[0]
    for tr in form.findAll(name='tr'):
        tdlist = tr.findAll(name='td')
        if len(tdlist) == 17:

            # Some extra effort is needed to extract the squib from the BeautifulSoup DOM tree
            squib = re.search('\d\d\d\d\w\w\w?(/\w\w\w?)?\w+(-\w+)?:\d+',
                              unicode(tdlist[2].a))
            squib = squib.group()

            # Assume the result is of modified Arrhenius form
            Trange = tdlist[6].text
            A = tdlist[8].text
            n = tdlist[10].text
            Ea = tdlist[12].text
            k300 = tdlist[14].text
            order = tdlist[16].text

            # Reject results that don't have a valid preexponential or activation energy
            if A == '&nbsp;' or Ea == '&nbsp;':
                continue
            # Reject results whose reaction order does not match the number of reactants
            if int(order) != len(reactants):
                continue

            # Many times n is not given, so set it to zero if that happens
            if n == '&nbsp;':
                n = 0.0

            # Convert the temperature range to separate minimum and maximum valies
            if '-' in Trange:
                Tmin, Tmax = Trange.split('-')
            else:
                Tmin = Trange
                Tmax = Trange

            # Create an entry for this result and append it to the list of entries
            entry = Entry(
                data=Arrhenius(
                    A=(float(A),
                       "cm^3/(mol*s)" if len(reactants) == 2 else "s^-1"),
                    n=float(n),
                    Ea=(float(Ea), "kJ/mol"),
                    T0=(1.0, "K"),
                    Tmin=(float(Tmin), "K"),
                    Tmax=(float(Tmax), "K"),
                ),
                reference=
                squib,  # Just save the squib for now; we'll get the actual reference later
            )
            entries.append(entry)

    return entries
def generateAdditionalRateRules(family, database):
    """
    For a given reaction `family` label, generate additional rate rules from
    the corresponding depository training set. This function does automatically
    what users used to do by hand to construct a rate rule from reaction
    kinetics found in the literature, i.e. determine the groups involved,
    adjust to a per-site basis, etc.
    """
    
    # Find the database components we will need
    rules = database.kinetics.depository['{0}/rules'.format(family)]
    groups = database.kinetics.groups[family]
    
    index = max([entry.index for entry in rules.entries.values()]) + 1
    
    for depositoryLabel in ['training']:
        depository = database.kinetics.depository['{0}/{1}'.format(family, depositoryLabel)]
        
        entries = depository.entries.values()
        entries.sort(key=lambda x: (x.index, x.label))
        
        for entry0 in entries:
            
            reaction, template = database.kinetics.getForwardReactionForFamilyEntry(entry=entry0, family=family, thermoDatabase=database.thermo)
            
            # We must convert the kinetics to ArrheniusEP to finish our rate rule
            kinetics = reaction.kinetics
            # If kinetics are KineticsData, then first convert to Arrhenius
            if isinstance(kinetics, KineticsData):
                kinetics = Arrhenius().fitToData(
                    Tdata=kinetics.Tdata.values,
                    kdata=kinetics.kdata.values,
                    kunits=kinetics.kdata.units,
                    T0=1,
                )
            
            # Now convert from Arrhenius to ArrheniusEP
            # If not Arrhenius (or KineticsData), then skip this entry
            if isinstance(kinetics, Arrhenius):
                if kinetics.T0.value != 1:
                    kinetics.changeT0(1)
                kinetics = ArrheniusEP(
                    A = kinetics.A,
                    n = kinetics.n,
                    alpha = 0,
                    E0 = kinetics.Ea,
                    Tmin = kinetics.Tmin,
                    Tmax = kinetics.Tmax,
                )
            else:
                break
            
            # Put the kinetics on a per-site basis
            kinetics.A.value /= reaction.degeneracy
                
            # Construct a new entry for the new rate rule
            label = ';'.join([group.label for group in template])
            item = Reaction(
                reactants = template[:],
                products = None,
            )
            entry = Entry(
                index = index,
                label = label,
                item = item,
                data = kinetics,
                reference = entry0.reference,
                rank = entry0.rank,
                shortDesc = entry0.shortDesc,
                longDesc = entry0.longDesc,
                history = entry0.history,
            )

            # Add the new rate rule to the depository of rate rules
            rules.entries[entry.index] = entry
            
            index += 1