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