Exemplo n.º 1
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
Exemplo n.º 2
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
Exemplo n.º 3
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
Exemplo n.º 4
0
 def generateReverseRateCoefficient(self):
     """
     Generate and return a rate coefficient model for the reverse reaction. 
     Currently this only works if the `kinetics` attribute is an 
     :class:`Arrhenius` or :class:`KineticsData` object.
     """
     
     cython.declare(Tlist=numpy.ndarray, klist=numpy.ndarray, i=cython.int)
         
     # Get the units for the reverse rate coefficient
     if len(self.products) == 1:
         kunits = 's^-1'
     elif len(self.products) == 2:
         kunits = 'm^3/(mol*s)'
     elif len(self.products) == 3:
         kunits = 'm^6/(mol^2*s)'
     else:
         kunits = ''
         
     kf = self.kinetics
     if isinstance(kf, KineticsData):
         
         Tlist = kf.Tdata.value_si
         klist = numpy.zeros_like(Tlist)
         print Tlist
         for i in range(len(Tlist)):
             print kf.getRateCoefficient(Tlist[i]), self.getEquilibriumConstant(Tlist[i])
             klist[i] = kf.getRateCoefficient(Tlist[i]) / self.getEquilibriumConstant(Tlist[i])
         
         kr = KineticsData(Tdata=(Tlist,"K"), kdata=(klist,kunits), Tmin=(numpy.min(Tlist),"K"), Tmax=(numpy.max(Tlist),"K"))
         return kr
         
     elif isinstance(kf, Arrhenius):
         
         if kf.Tmin is not None and kf.Tmax is not None:
             Tlist = 1.0/numpy.linspace(1.0/kf.Tmax.value_si, 1.0/kf.Tmin.value_si, 50)
         else:
             Tlist = 1.0/numpy.arange(0.0005, 0.0034, 0.0001)
             
         # Determine the values of the reverse rate coefficient k_r(T) at each temperature
         klist = numpy.zeros_like(Tlist)
         for i in range(len(Tlist)):
             klist[i] = kf.getRateCoefficient(Tlist[i]) / self.getEquilibriumConstant(Tlist[i])
 
         kr = Arrhenius()
         kr.fitToData(Tlist, klist, kunits, kf.T0.value_si)
         return kr
                 
     elif isinstance (kf, Chebyshev):
         Tlist = 1.0/numpy.linspace(1.0/kf.Tmax.value, 1.0/kf.Tmin.value, 50)
         Plist = numpy.linspace(kf.Pmin.value, kf.Pmax.value, 20)
         K = numpy.zeros((len(Tlist), len(Plist)), numpy.float64)
         for Tindex, T in enumerate(Tlist):
             for Pindex, P in enumerate(Plist):
                 K[Tindex, Pindex] = kf.getRateCoefficient(T, P) / self.getEquilibriumConstant(T)
         kr = Chebyshev()
         kr.fitToData(Tlist, Plist, K, kunits, kf.degreeT, kf.degreeP, kf.Tmin.value, kf.Tmax.value, kf.Pmin.value, kf.Pmax.value)
         return kr
     
     elif isinstance(kf, PDepArrhenius):  
         if kf.Tmin is not None and kf.Tmax is not None:
             Tlist = 1.0/numpy.linspace(1.0/kf.Tmax.value, 1.0/kf.Tmin.value, 50)
         else:
             Tlist = 1.0/numpy.arange(0.0005, 0.0035, 0.0001)
         Plist = kf.pressures.value_si
         K = numpy.zeros((len(Tlist), len(Plist)), numpy.float64)
         for Tindex, T in enumerate(Tlist):
             for Pindex, P in enumerate(Plist):
                 K[Tindex, Pindex] = kf.getRateCoefficient(T, P) / self.getEquilibriumConstant(T)
         kr = PDepArrhenius()
         kr.fitToData(Tlist, Plist, K, kunits, kf.arrhenius[0].T0.value)
         return kr       
     
     elif isinstance(kf, MultiArrhenius):
         kr = MultiArrhenius()
         kr.arrhenius = []            
         rxn = Reaction(reactants = self.reactants, products = self.products)            
         for kinetics in kf.arrhenius:
             rxn.kinetics = kinetics
             kr.arrhenius.append(rxn.generateReverseRateCoefficient())
         return kr
     
     elif isinstance(kf, MultiPDepArrhenius):
         kr = MultiPDepArrhenius()              
         kr.arrhenius = []                
         rxn = Reaction(reactants = self.reactants, products = self.products)            
         for kinetics in kf.arrhenius:
             rxn.kinetics = kinetics
             kr.arrhenius.append(rxn.generateReverseRateCoefficient())
         return kr
     
     else:
         raise ReactionError("Unexpected kinetics type {0}; should be Arrhenius, Chebyshev, PDepArrhenius, or KineticsData.".format(self.kinetics.__class__))
Exemplo n.º 5
0
    def generateReverseRateCoefficient(self):
        """
        Generate and return a rate coefficient model for the reverse reaction. 
        Currently this only works if the `kinetics` attribute is one of several
        (but not necessarily all) kinetics types.
        """
        cython.declare(Tlist=numpy.ndarray, klist=numpy.ndarray, i=cython.int)

        supported_types = (
                            KineticsData.__name__,
                            Arrhenius.__name__,
                            MultiArrhenius.__name__,
                            PDepArrhenius.__name__,
                            MultiPDepArrhenius.__name__,
                            Chebyshev.__name__,
                            ThirdBody.__name__,
                            Lindemann.__name__,
                            Troe.__name__,
                            )

        # Get the units for the reverse rate coefficient
        kunits = getRateCoefficientUnitsFromReactionOrder(len(self.products))
            
        kf = self.kinetics
        if isinstance(kf, KineticsData):
            
            Tlist = kf.Tdata.value_si
            klist = numpy.zeros_like(Tlist)
            for i in range(len(Tlist)):
                klist[i] = kf.getRateCoefficient(Tlist[i]) / self.getEquilibriumConstant(Tlist[i])
            
            kr = KineticsData(Tdata=(Tlist,"K"), kdata=(klist,kunits), Tmin=(numpy.min(Tlist),"K"), Tmax=(numpy.max(Tlist),"K"))
            return kr
            
        elif isinstance(kf, Arrhenius):
            return self.reverseThisArrheniusRate(kf, kunits)
                    
        elif isinstance (kf, Chebyshev):
            Tlist = 1.0/numpy.linspace(1.0/kf.Tmax.value, 1.0/kf.Tmin.value, 50)
            Plist = numpy.linspace(kf.Pmin.value, kf.Pmax.value, 20)
            K = numpy.zeros((len(Tlist), len(Plist)), numpy.float64)
            for Tindex, T in enumerate(Tlist):
                for Pindex, P in enumerate(Plist):
                    K[Tindex, Pindex] = kf.getRateCoefficient(T, P) / self.getEquilibriumConstant(T)
            kr = Chebyshev()
            kr.fitToData(Tlist, Plist, K, kunits, kf.degreeT, kf.degreeP, kf.Tmin.value, kf.Tmax.value, kf.Pmin.value, kf.Pmax.value)
            return kr
        
        elif isinstance(kf, PDepArrhenius):  
            if kf.Tmin is not None and kf.Tmax is not None:
                Tlist = 1.0/numpy.linspace(1.0/kf.Tmax.value, 1.0/kf.Tmin.value, 50)
            else:
                Tlist = 1.0/numpy.arange(0.0005, 0.0035, 0.0001)
            Plist = kf.pressures.value_si
            K = numpy.zeros((len(Tlist), len(Plist)), numpy.float64)
            for Tindex, T in enumerate(Tlist):
                for Pindex, P in enumerate(Plist):
                    K[Tindex, Pindex] = kf.getRateCoefficient(T, P) / self.getEquilibriumConstant(T)
            kr = PDepArrhenius()
            kr.fitToData(Tlist, Plist, K, kunits, kf.arrhenius[0].T0.value)
            return kr       
        
        elif isinstance(kf, MultiArrhenius):
            kr = MultiArrhenius()
            kr.arrhenius = []            
            rxn = Reaction(reactants = self.reactants, products = self.products)            
            for kinetics in kf.arrhenius:
                rxn.kinetics = kinetics
                kr.arrhenius.append(rxn.generateReverseRateCoefficient())
            return kr
        
        elif isinstance(kf, MultiPDepArrhenius):
            kr = MultiPDepArrhenius()              
            kr.arrhenius = []                
            rxn = Reaction(reactants = self.reactants, products = self.products)            
            for kinetics in kf.arrhenius:
                rxn.kinetics = kinetics
                kr.arrhenius.append(rxn.generateReverseRateCoefficient())
            return kr

        elif isinstance(kf, ThirdBody):
            lowPkunits = getRateCoefficientUnitsFromReactionOrder(len(self.products) + 1)
            krLow = self.reverseThisArrheniusRate(kf.arrheniusLow, lowPkunits)
            parameters = kf.__reduce__()[1]  # use the pickle helper to get all the other things needed
            kr = ThirdBody(krLow, *parameters[1:])
            return kr

        elif isinstance(kf, Lindemann):
            krHigh = self.reverseThisArrheniusRate(kf.arrheniusHigh, kunits)
            lowPkunits = getRateCoefficientUnitsFromReactionOrder(len(self.products) + 1)
            krLow = self.reverseThisArrheniusRate(kf.arrheniusLow, lowPkunits)
            parameters = kf.__reduce__()[1]  # use the pickle helper to get all the other things needed
            kr = Lindemann(krHigh, krLow, *parameters[2:])
            return kr

        elif isinstance(kf, Troe):
            krHigh = self.reverseThisArrheniusRate(kf.arrheniusHigh, kunits)
            lowPkunits = getRateCoefficientUnitsFromReactionOrder(len(self.products) + 1)
            krLow = self.reverseThisArrheniusRate(kf.arrheniusLow, lowPkunits)
            parameters = kf.__reduce__()[1]  # use the pickle helper to get all the other things needed
            kr = Troe(krHigh, krLow, *parameters[2:])
            return kr
        else:
            raise ReactionError(("Unexpected kinetics type {0}; should be one of {1}").format(self.kinetics.__class__, supported_types))
Exemplo n.º 6
0
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