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 _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
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
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__))
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))
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