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

        original_kinetics = ArrheniusEP(
                    A = (2.65e12, 'cm^3/(mol*s)'),
                    n = 0.0,
                    alpha = 0.5,
                    E0 = (41.84, 'kJ/mol'),
                    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, original_kinetics.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 testGenerateReverseRateCoefficientArrheniusEP(self):
        """
        Test the Reaction.generateReverseRateCoefficient() method works for the ArrheniusEP format.
        """
        from rmgpy.kinetics import ArrheniusEP

        original_kinetics = ArrheniusEP(
            A=(2.65e12, "cm^3/(mol*s)"), n=0.0, alpha=0.5, E0=(41.84, "kJ/mol"), 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, original_kinetics.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 #3
0
 def __getAverageKinetics(self, kineticsList):
     """
     Based on averaging log k. For most complex case:
     k = AT^n * exp(-Ea+alpha*H)
     log k = log(A) * nlog(T) * (-Ea + alpha*H)
     
     Hence we average n, Ea, and alpha arithmetically, but we
     average log A (geometric average) 
     """
     logA = 0.0
     n = 0.0
     E0 = 0.0
     alpha = 0.0
     count = len(kineticsList)
     for kinetics in kineticsList:
         logA += math.log10(kinetics.A.value_si)
         n += kinetics.n.value_si
         alpha += kinetics.alpha.value_si
         E0 += kinetics.E0.value_si
     logA /= count
     n /= count
     alpha /= count
     E0 /= count
     Aunits = kineticsList[0].A.units
     if Aunits == 'cm^3/(mol*s)' or Aunits == 'cm^3/(molecule*s)' or Aunits == 'm^3/(molecule*s)':
         Aunits = 'm^3/(mol*s)'
     elif Aunits == 'cm^6/(mol^2*s)' or Aunits == 'cm^6/(molecule^2*s)' or Aunits == 'm^6/(molecule^2*s)':
         Aunits = 'm^6/(mol^2*s)'
     elif Aunits == 's^-1' or Aunits == 'm^3/(mol*s)' or Aunits == 'm^6/(mol^2*s)':
         pass
     else:
         raise Exception(
             'Invalid units {0} for averaging kinetics.'.format(Aunits))
     averagedKinetics = ArrheniusEP(
         A=(10**logA, Aunits),
         n=n,
         alpha=alpha,
         E0=(E0 * 0.001, "kJ/mol"),
     )
     return averagedKinetics
Example #4
0
    def processOldLibraryEntry(self, data):
        """
        Process a list of parameters `data` as read from an old-style RMG
        thermo database, returning the corresponding kinetics object.
        """

        # The names of all of the RMG reaction families that are bimolecular
        BIMOLECULAR_KINETICS_FAMILIES = [
            'H_Abstraction',
            'R_Addition_MultipleBond',
            'R_Recombination',
            'Disproportionation',
            '1+2_Cycloaddition',
            '2+2_cycloaddition_Cd',
            '2+2_cycloaddition_CO',
            '2+2_cycloaddition_CCO',
            'Diels_alder_addition',
            '1,2_Insertion',
            '1,3_Insertion_CO2',
            '1,3_Insertion_ROR',
            'R_Addition_COm',
            'Oa_R_Recombination',
            'Substitution_O',
            'SubstitutionS',
            'R_Addition_CSm',
            '1,3_Insertion_RSR',
            'lone_electron_pair_bond',
        ]

        # The names of all of the RMG reaction families that are unimolecular
        UNIMOLECULAR_KINETICS_FAMILIES = [
            'intra_H_migration', 'Birad_recombination', 'intra_OH_migration',
            'HO2_Elimination_from_PeroxyRadical', 'H_shift_cyclopentadiene',
            'Cyclic_Ether_Formation', 'Intra_R_Add_Exocyclic',
            'Intra_R_Add_Endocyclic', '1,2-Birad_to_alkene',
            'Intra_Disproportionation', 'Korcek_step1', 'Korcek_step2',
            '1,2_shiftS', 'intra_substitutionCS_cyclization',
            'intra_substitutionCS_isomerization',
            'intra_substitutionS_cyclization',
            'intra_substitutionS_isomerization', 'intra_NO2_ONO_conversion',
            '1,4_Cyclic_birad_scission', '1,4_Linear_birad_scission',
            'Intra_Diels_alder', 'ketoenol', 'Retroen'
        ]
        # This is hardcoding of reaction families!
        label = os.path.split(self.label)[-2]
        if label in BIMOLECULAR_KINETICS_FAMILIES:
            Aunits = 'cm^3/(mol*s)'
        elif label in UNIMOLECULAR_KINETICS_FAMILIES:
            Aunits = 's^-1'
        else:
            raise Exception(
                'Unable to determine preexponential units for old reaction family "{0}".'
                .format(self.label))

        try:
            Tmin, Tmax = data[0].split('-')
            Tmin = (float(Tmin), "K")
            Tmax = (float(Tmax), "K")
        except ValueError:
            Tmin = (float(data[0]), "K")
            Tmax = None

        A, n, alpha, E0, dA, dn, dalpha, dE0 = data[1:9]

        A = float(A)
        if dA[0] == '*':
            A = Quantity(A, Aunits, '*|/', float(dA[1:]))
        else:
            dA = float(dA)
            if dA != 0:
                A = Quantity(A, Aunits, '+|-', dA)
            else:
                A = Quantity(A, Aunits)

        n = float(n)
        dn = float(dn)
        if dn != 0:
            n = Quantity(n, '', '+|-', dn)
        else:
            n = Quantity(n, '')

        alpha = float(alpha)
        dalpha = float(dalpha)
        if dalpha != 0:
            alpha = Quantity(alpha, '', '+|-', dalpha)
        else:
            alpha = Quantity(alpha, '')

        E0 = float(E0)
        dE0 = float(dE0)
        if dE0 != 0:
            E0 = Quantity(E0, 'kcal/mol', '+|-', dE0)
        else:
            E0 = Quantity(E0, 'kcal/mol')

        rank = int(data[9])

        return ArrheniusEP(A=A, n=n, alpha=alpha, E0=E0, Tmin=Tmin,
                           Tmax=Tmax), rank
Example #5
0
    def _multiply_kinetics_data(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`, :class:`ArrheniusEP` 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.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_si + kinetics2.Ea.value_si, 'J/mol'),
                T0=(kinetics1.T0.value, kinetics1.T0.units),
            )
        elif isinstance(kinetics1, ArrheniusEP) and isinstance(kinetics2, ArrheniusEP):
            assert kinetics1.A.units == kinetics2.A.units
            kinetics = ArrheniusEP(
                A=(kinetics1.A.value * kinetics2.A.value, kinetics1.A.units),
                n=(kinetics1.n.value + kinetics2.n.value, kinetics1.n.units),
                alpha=kinetics1.alpha + kinetics2.alpha,
                E0=(kinetics1.E0.value_si + kinetics2.E0.value_si, 'J/mol'),
            )
        elif isinstance(kinetics1, Arrhenius) and isinstance(kinetics2, ArrheniusEP):
            assert kinetics1.A.units == kinetics2.A.units
            assert kinetics1.T0.units == 'K'
            assert kinetics1.T0.value == 1.0
            kinetics = ArrheniusEP(
                A=(kinetics1.A.value * kinetics2.A.value, kinetics1.A.units),
                n=(kinetics1.n.value + kinetics2.n.value, kinetics1.n.units),
                alpha=kinetics2.alpha,
                E0=(kinetics1.Ea.value_si + kinetics2.E0.value_si, 'J/mol'),
            )
        elif isinstance(kinetics1, ArrheniusEP) and isinstance(kinetics2, Arrhenius):
            assert kinetics1.A.units == kinetics2.A.units
            assert 'K' == kinetics2.T0.units
            assert 1.0 == kinetics2.T0.value
            kinetics = ArrheniusEP(
                A=(kinetics1.A.value * kinetics2.A.value, kinetics1.A.units),
                n=(kinetics1.n.value + kinetics2.n.value, kinetics1.n.units),
                alpha=kinetics1.alpha,
                E0=(kinetics1.E0.value_si + kinetics2.Ea.value_si, 'J/mol'),
            )
        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 #6
0
    def reconstructKineticsFromSource(self,
                                      reaction,
                                      source,
                                      fixBarrierHeight=False,
                                      forcePositiveBarrier=False):
        """
        Reaction is the original reaction with original kinetics.
        Note that for Library and PDep reactions this function does not do anything other than return the original kinetics...
        
        You must enter source data in the appropriate format such as returned from returned from self.extractSourceFromComments, 
        self-constructed.  
        fixBarrierHeight and forcePositiveBarrier will change the kinetics based on the Reaction.fixBarrierHeight function.
        Return Arrhenius form kinetics if the source is from training reaction or rate rules.
        """
        from rmgpy.data.thermo import findCp0andCpInf
        from rmgpy.thermo import Wilhoit
        if 'Library' in source:
            return reaction.kinetics
        elif 'PDep' in source:
            return reaction.kinetics
        else:
            rxnCopy = deepcopy(reaction)
            if 'Training' in source:
                trainingEntry = source['Training'][1]
                reverse = source['Training'][2]
                if reverse:
                    reverseKinetics = trainingEntry.data
                    rxnCopy.kinetics = reverseKinetics
                    forwardKinetics = rxnCopy.generateReverseRateCoefficient()
                    kinetics = forwardKinetics
                else:
                    kinetics = trainingEntry.data
            elif 'Rate Rules' in source:

                sourceDict = source['Rate Rules'][1]
                rules = sourceDict['rules']
                training = sourceDict['training']
                degeneracy = sourceDict['degeneracy']

                logA = 0
                n = 0
                alpha = 0
                E0 = 0
                for ruleEntry, weight in rules:
                    logA += numpy.log10(ruleEntry.data.A.value_si) * weight
                    n += ruleEntry.data.n.value_si * weight
                    alpha += ruleEntry.data.alpha.value_si * weight
                    E0 += ruleEntry.data.E0.value_si * weight
                for ruleEntry, trainingEntry, weight in training:
                    logA += numpy.log10(ruleEntry.data.A.value_si) * weight
                    n += ruleEntry.data.n.value_si * weight
                    alpha += ruleEntry.data.alpha.value_si * weight
                    E0 += ruleEntry.data.E0.value_si * weight

                Aunits = ruleEntry.data.A.units
                if Aunits == 'cm^3/(mol*s)' or Aunits == 'cm^3/(molecule*s)' or Aunits == 'm^3/(molecule*s)':
                    Aunits = 'm^3/(mol*s)'
                elif Aunits == 'cm^6/(mol^2*s)' or Aunits == 'cm^6/(molecule^2*s)' or Aunits == 'm^6/(molecule^2*s)':
                    Aunits = 'm^6/(mol^2*s)'
                elif Aunits == 's^-1' or Aunits == 'm^3/(mol*s)' or Aunits == 'm^6/(mol^2*s)':
                    pass
                else:
                    raise Exception(
                        'Invalid units {0} for averaging kinetics.'.format(
                            Aunits))
                kinetics = ArrheniusEP(
                    A=(degeneracy * 10**logA, Aunits),
                    n=n,
                    alpha=alpha,
                    E0=(E0 * 0.001, "kJ/mol"),
                )
            else:
                raise Exception(
                    "Source data must be either 'Library', 'PDep','Training', or 'Rate Rules'."
                )

            # Convert ArrheniusEP to Arrhenius
            if fixBarrierHeight:
                for spc in rxnCopy.reactants + rxnCopy.products:
                    # Need wilhoit to do this
                    if not isinstance(spc.thermo, Wilhoit):
                        findCp0andCpInf(spc, spc.thermo)
                        wilhoit = spc.thermo.toWilhoit()
                        spc.thermo = wilhoit

                rxnCopy.kinetics = kinetics
                rxnCopy.fixBarrierHeight(forcePositive=forcePositiveBarrier)

                return rxnCopy.kinetics
            else:

                H298 = rxnCopy.getEnthalpyOfReaction(298)
                if isinstance(kinetics, ArrheniusEP):
                    kinetics = kinetics.toArrhenius(H298)
                return kinetics
Example #7
0
    def processOldLibraryEntry(self, data):
        """
        Process a list of parameters `data` as read from an old-style RMG
        thermo database, returning the corresponding kinetics object.
        """
        # This is hardcoding of reaction families!
        label = os.path.split(self.label)[-2]
        if label in BIMOLECULAR_KINETICS_FAMILIES:
            Aunits = 'cm^3/(mol*s)'
        elif label in UNIMOLECULAR_KINETICS_FAMILIES:
            Aunits = 's^-1'
        else:
            raise Exception(
                'Unable to determine preexponential units for old reaction family "{0}".'
                .format(self.label))

        try:
            Tmin, Tmax = data[0].split('-')
            Tmin = (float(Tmin), "K")
            Tmax = (float(Tmax), "K")
        except ValueError:
            Tmin = (float(data[0]), "K")
            Tmax = None

        A, n, alpha, E0, dA, dn, dalpha, dE0 = data[1:9]

        A = float(A)
        if dA[0] == '*':
            A = Quantity(A, Aunits, '*|/', float(dA[1:]))
        else:
            dA = float(dA)
            if dA != 0:
                A = Quantity(A, Aunits, '+|-', dA)
            else:
                A = Quantity(A, Aunits)

        n = float(n)
        dn = float(dn)
        if dn != 0:
            n = Quantity(n, '', '+|-', dn)
        else:
            n = Quantity(n, '')

        alpha = float(alpha)
        dalpha = float(dalpha)
        if dalpha != 0:
            alpha = Quantity(alpha, '', '+|-', dalpha)
        else:
            alpha = Quantity(alpha, '')

        E0 = float(E0)
        dE0 = float(dE0)
        if dE0 != 0:
            E0 = Quantity(E0, 'kcal/mol', '+|-', dE0)
        else:
            E0 = Quantity(E0, 'kcal/mol')

        rank = int(data[9])

        return ArrheniusEP(A=A, n=n, alpha=alpha, E0=E0, Tmin=Tmin,
                           Tmax=Tmax), rank
Example #8
0
 def reconstructKineticsFromSource(self, reaction, source, fixBarrierHeight=False, forcePositiveBarrier=False):
     """
     Reaction is the original reaction with original kinetics.
     Note that for Library and PDep reactions this function does not do anything other than return the original kinetics...
     
     You must enter source data in the appropriate format such as returned from returned from self.extractSourceFromComments, 
     self-constructed.  
     fixBarrierHeight and forcePositiveBarrier will change the kinetics based on the Reaction.fixBarrierHeight function.
     Return Arrhenius form kinetics if the source is from training reaction or rate rules.
     """
     from rmgpy.data.thermo import findCp0andCpInf
     from rmgpy.thermo import Wilhoit
     if 'Library' in source:
         return reaction.kinetics
     elif 'PDep' in source:
         return reaction.kinetics
     else:
         rxnCopy = deepcopy(reaction)
         if 'Training' in source:
             trainingEntry = source['Training'][1]
             reverse = source['Training'][2]
             if reverse:
                 reverseKinetics = trainingEntry.data
                 rxnCopy.kinetics = reverseKinetics
                 forwardKinetics = rxnCopy.generateReverseRateCoefficient()
                 kinetics = forwardKinetics
             else:
                 kinetics = trainingEntry.data
         elif 'Rate Rules' in source:
 
             sourceDict = source['Rate Rules'][1]
             rules = sourceDict['rules']
             training = sourceDict['training']
             degeneracy = sourceDict['degeneracy']
 
             logA = 0
             n = 0
             alpha = 0
             E0 = 0
             for ruleEntry, weight in rules:
                 logA += numpy.log10(ruleEntry.data.A.value_si)*weight
                 n += ruleEntry.data.n.value_si*weight
                 alpha +=ruleEntry.data.alpha.value_si*weight
                 E0 +=ruleEntry.data.E0.value_si*weight
             for ruleEntry, trainingEntry, weight in training:
                 logA += numpy.log10(ruleEntry.data.A.value_si)*weight
                 n += ruleEntry.data.n.value_si*weight
                 alpha +=ruleEntry.data.alpha.value_si*weight
                 E0 +=ruleEntry.data.E0.value_si*weight
             
             Aunits = ruleEntry.data.A.units 
             if Aunits == 'cm^3/(mol*s)' or Aunits == 'cm^3/(molecule*s)' or Aunits == 'm^3/(molecule*s)':
                 Aunits = 'm^3/(mol*s)'
             elif Aunits == 'cm^6/(mol^2*s)' or Aunits == 'cm^6/(molecule^2*s)' or Aunits == 'm^6/(molecule^2*s)':
                 Aunits = 'm^6/(mol^2*s)'
             elif Aunits == 's^-1' or Aunits == 'm^3/(mol*s)' or Aunits == 'm^6/(mol^2*s)':
                 pass
             else:
                 raise Exception('Invalid units {0} for averaging kinetics.'.format(Aunits))
             kinetics = ArrheniusEP(
                 A = (degeneracy*10**logA, Aunits),
                 n = n,
                 alpha = alpha,
                 E0 = (E0*0.001,"kJ/mol"),
             )
         else:
             raise Exception("Source data must be either 'Library', 'PDep','Training', or 'Rate Rules'.")
             
         
         # Convert ArrheniusEP to Arrhenius
         if fixBarrierHeight:
             for spc in rxnCopy.reactants + rxnCopy.products:
                 # Need wilhoit to do this
                 if not isinstance(spc.thermo, Wilhoit):
                     findCp0andCpInf(spc, spc.thermo)
                     wilhoit = spc.thermo.toWilhoit()
                     spc.thermo = wilhoit
                     
             rxnCopy.kinetics = kinetics
             rxnCopy.fixBarrierHeight(forcePositive=forcePositiveBarrier)
             
             return rxnCopy.kinetics
         else:
             
             H298 = rxnCopy.getEnthalpyOfReaction(298)
             if isinstance(kinetics, ArrheniusEP):
                 kinetics = kinetics.toArrhenius(H298)
             return kinetics
Example #9
0
    def reconstruct_kinetics_from_source(self,
                                         reaction,
                                         source,
                                         fix_barrier_height=False,
                                         force_positive_barrier=False):
        """
        Reaction is the original reaction with original kinetics.
        Note that for Library and PDep reactions this function does not do anything other than return the original kinetics...
        
        You must enter source data in the appropriate format such as returned from returned from self.extract_source_from_comments,
        self-constructed.  
        fix_barrier_height and force_positive_barrier will change the kinetics based on the Reaction.fix_barrier_height function.
        Return Arrhenius form kinetics if the source is from training reaction or rate rules.
        """
        from rmgpy.data.thermo import find_cp0_and_cpinf
        from rmgpy.thermo import Wilhoit
        if 'Library' in source:
            return reaction.kinetics
        elif 'PDep' in source:
            return reaction.kinetics
        else:
            rxn_copy = deepcopy(reaction)
            if 'Training' in source:
                training_entry = source['Training'][1]
                reverse = source['Training'][2]
                if reverse:
                    reverse_kinetics = training_entry.data
                    rxn_copy.kinetics = reverse_kinetics
                    forward_kinetics = rxn_copy.generate_reverse_rate_coefficient(
                    )
                    kinetics = forward_kinetics
                else:
                    kinetics = training_entry.data
            elif 'Rate Rules' in source:

                source_dict = source['Rate Rules'][1]
                rules = source_dict['rules']
                training = source_dict['training']
                degeneracy = source_dict['degeneracy']

                log_a = 0
                n = 0
                alpha = 0
                E0 = 0
                for rule_entry, weight in rules:
                    log_a += np.log10(rule_entry.data.A.value_si) * weight
                    n += rule_entry.data.n.value_si * weight
                    alpha += rule_entry.data.alpha.value_si * weight
                    E0 += rule_entry.data.E0.value_si * weight
                for rule_entry, training_entry, weight in training:
                    log_a += np.log10(rule_entry.data.A.value_si) * weight
                    n += rule_entry.data.n.value_si * weight
                    alpha += rule_entry.data.alpha.value_si * weight
                    E0 += rule_entry.data.E0.value_si * weight

                a_units = rule_entry.data.A.units
                if a_units == 'cm^3/(mol*s)' or a_units == 'cm^3/(molecule*s)' or a_units == 'm^3/(molecule*s)':
                    a_units = 'm^3/(mol*s)'
                elif a_units == 'cm^6/(mol^2*s)' or a_units == 'cm^6/(molecule^2*s)' or a_units == 'm^6/(molecule^2*s)':
                    a_units = 'm^6/(mol^2*s)'
                elif a_units == 's^-1' or a_units == 'm^3/(mol*s)' or a_units == 'm^6/(mol^2*s)':
                    pass
                else:
                    raise ValueError(
                        'Invalid units {0} for averaging kinetics.'.format(
                            a_units))
                kinetics = ArrheniusEP(
                    A=(degeneracy * 10**log_a, a_units),
                    n=n,
                    alpha=alpha,
                    E0=(E0 * 0.001, "kJ/mol"),
                )
            else:
                raise ValueError(
                    "Source data must be either 'Library', 'PDep','Training', or 'Rate Rules'."
                )

            # Convert ArrheniusEP to Arrhenius
            if fix_barrier_height:
                for spc in rxn_copy.reactants + rxn_copy.products:
                    # Need wilhoit to do this
                    if not isinstance(spc.thermo, Wilhoit):
                        find_cp0_and_cpinf(spc, spc.thermo)
                        wilhoit = spc.thermo.to_wilhoit()
                        spc.thermo = wilhoit

                rxn_copy.kinetics = kinetics
                rxn_copy.fix_barrier_height(
                    force_positive=force_positive_barrier)

                return rxn_copy.kinetics
            else:

                h298 = rxn_copy.get_enthalpy_of_reaction(298)
                if isinstance(kinetics, (ArrheniusEP, ArrheniusBM)):
                    kinetics = kinetics.to_arrhenius(h298)
                return kinetics
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
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