def fillRulesByAveragingUp(self, rootTemplate, alreadyDone, verbose=False): """ Fill in gaps in the kinetics rate rules by averaging child nodes. If verbose is set to True, then exact sources of kinetics are saved in the kinetics comments (warning: this uses up a lot of memory due to the extensively long comments) """ rootLabel = ';'.join([g.label for g in rootTemplate]) if rootLabel in alreadyDone: return alreadyDone[rootLabel] # Generate the distance 1 pairings which must be averaged for this root template. # The distance 1 template is created by taking the parent node from one or more trees # and creating the combinations with children from a single remaining tree. # i.e. for some node (A,B), we want to fetch all combinations for the pairing of (A,B's children) and # (A's children, B). For node (A,B,C), we would retrieve all combinations of (A,B,C's children) # (A,B's children,C) etc... # If a particular node has no children, it is skipped from the children expansion altogether. childrenList = [] distanceList = [] for i, parent in enumerate(rootTemplate): # Start with the root template, and replace the ith member with its children if parent.children: childrenSet = [[group] for group in rootTemplate] childrenSet[i] = parent.children childrenList.extend(getAllCombinations(childrenSet)) distanceList.extend([k.nodalDistance for k in parent.children]) if distanceList != []: #average the minimum distance neighbors minDist = min(distanceList) closeChildrenList = [ childrenList[i] for i in xrange(len(childrenList)) if distanceList[i] == minDist ] else: closeChildrenList = [] kineticsList = [] for template in childrenList: label = ';'.join([g.label for g in template]) if label in alreadyDone: kinetics = alreadyDone[label] else: kinetics = self.fillRulesByAveragingUp(template, alreadyDone, verbose) if template in closeChildrenList and kinetics is not None: kineticsList.append([kinetics, template]) # See if we already have a rate rule for this exact template instead # and return it now that we have finished searching its children entry = self.getRule(rootTemplate) if entry is not None and entry.rank > 0: # We already have a rate rule for this exact template # If the entry has rank of zero, then we have so little faith # in it that we'd rather use an averaged value if possible # Since this entry does not have a rank of zero, we keep its # value alreadyDone[rootLabel] = entry.data return entry.data if len(kineticsList) > 0: if len(kineticsList) > 1: # We found one or more results! Let's average them together kinetics = self.__getAverageKinetics( [k for k, t in kineticsList]) if verbose: kinetics.comment = 'Average of [{0}]'.format(' + '.join( k.comment if k.comment != '' else ';'.join(g.label for g in t) for k, t in kineticsList)) else: kinetics.comment = 'Average of [{0}]'.format(' + '.join( ';'.join(g.label for g in t) for k, t in kineticsList)) else: k, t = kineticsList[0] kinetics = deepcopy(k) # Even though we are using just a single set of kinetics, it's still considered # an average. It just happens that the other distance 1 children had no data. if verbose: kinetics.comment = 'Average of [{0}]'.format( k.comment if k.comment != '' else ';'.join(g.label for g in t)) else: kinetics.comment = 'Average of [{0}]'.format(';'.join( g.label for g in t)) entry = Entry( index=0, label=rootLabel, item=rootTemplate, data=kinetics, rank=10, # Indicates this is an averaged estimate ) self.entries[entry.label] = [entry] alreadyDone[rootLabel] = entry.data return entry.data alreadyDone[rootLabel] = None return None
molecule=species.molecule[0].to_adjacency_list(), thermo=species.thermo, ) else: logging.warning( """Species {0} did not contain any thermo data and was omitted from the thermo library.""".format(str(species))) # load kinetics library entries kinetics_library = KineticsLibrary(name=name) kinetics_library.entries = {} for i in range(len(reaction_list)): reaction = reaction_list[i] entry = Entry( index=i + 1, label=reaction.to_labeled_str(), item=reaction, data=reaction.kinetics, ) try: entry.long_desc = 'Originally from reaction library: ' + reaction.library + "\n" + reaction.kinetics.comment except AttributeError: entry.long_desc = reaction.kinetics.comment kinetics_library.entries[i + 1] = entry # Mark as duplicates where there are mixed pressure dependent and non-pressure dependent duplicate kinetics # Even though CHEMKIN does not require a duplicate flag, RMG needs it. # Using flag mark_duplicates = True kinetics_library.check_for_duplicates(mark_duplicates=True) kinetics_library.convert_duplicates_to_multi() # Save in Py format
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
molecule=species.molecule[0].toAdjacencyList(removeH=removeH), thermo=species.thermo, shortDesc=species.thermo.comment) else: logging.warning( 'Species {0} did not contain any thermo data and was omitted from the thermo library.' .format(str(species))) # load kinetics library entries kineticsLibrary = KineticsLibrary() kineticsLibrary.entries = {} for i in range(len(reactionList)): reaction = reactionList[i] entry = Entry( index=i + 1, item=reaction, data=reaction.kinetics, ) entry.longDesc = reaction.kinetics.comment kineticsLibrary.entries[i + 1] = entry kineticsLibrary.checkForDuplicates() kineticsLibrary.convertDuplicatesToMulti() # Assign history to all entries user = getUsername() # Pulls username from current git repository #user = '******'.format(name, email) # If not in git repository, then enter user information manually event = [ time.asctime(), user, 'action', '{0} imported this entry from the old RMG database.'.format(user) ]
def getLibraries(self): """Get RMG kinetics and thermo libraries""" name = 'kineticsjobs' speciesList = self.speciesDict.values() reactionList = self.reactionDict.values() # remove duplicate species for rxn in reactionList: for i, rspc in enumerate(rxn.reactants): for spc in speciesList: if spc.isIsomorphic(rspc): rxn.reactants[i] = spc break for i, rspc in enumerate(rxn.products): for spc in speciesList: if spc.isIsomorphic(rspc): rxn.products[i] = spc break del_inds = [] for i, spc1 in enumerate(speciesList): for j, spc2 in enumerate(speciesList): if j > i and spc1.isIsomorphic(spc2): del_inds.append(j) for j in sorted(del_inds)[::-1]: del speciesList[j] thermoLibrary = ThermoLibrary(name=name) for i, species in enumerate(speciesList): if species.thermo: thermoLibrary.loadEntry(index=i + 1, label=species.label, molecule=species.molecule[0].toAdjacencyList(), thermo=species.thermo, shortDesc=species.thermo.comment) else: logging.warning( 'Species {0} did not contain any thermo data and was omitted from the thermo library.'.format( str(species))) # load kinetics library entries kineticsLibrary = KineticsLibrary(name=name, autoGenerated=True) kineticsLibrary.entries = {} for i, reaction in enumerate(reactionList): entry = Entry( index=i + 1, label=reaction.toLabeledStr(), item=reaction, data=reaction.kinetics) if reaction.kinetics is not None: if hasattr(reaction, 'library') and reaction.library: entry.longDesc = 'Originally from reaction library: ' + \ reaction.library + "\n" + reaction.kinetics.comment else: entry.longDesc = reaction.kinetics.comment kineticsLibrary.entries[i + 1] = entry kineticsLibrary.label = name return thermoLibrary, kineticsLibrary, speciesList
molecule=species.molecule[0].toAdjacencyList(), thermo=species.thermo, shortDesc=species.thermo.comment) else: logging.warning( 'Species {0} did not contain any thermo data and was omitted from the thermo library.' .format(str(species))) # load kinetics library entries kineticsLibrary = KineticsLibrary(name=name) kineticsLibrary.entries = {} for i in range(len(reactionList)): reaction = reactionList[i] entry = Entry( index=i + 1, label=str(reaction), item=reaction, data=reaction.kinetics, ) entry.longDesc = reaction.kinetics.comment kineticsLibrary.entries[i + 1] = entry # Mark as duplicates where there are mixed pressure dependent and non-pressure dependent duplicate kinetics # Even though CHEMKIN does not require a duplicate flag, RMG needs it. # Using flag markDuplicates = True kineticsLibrary.checkForDuplicates(markDuplicates=True) kineticsLibrary.convertDuplicatesToMulti() # Save in Py format databaseDirectory = settings['database.directory'] try: os.makedirs(
def getKinetics(self): """ Extracts the kinetic data from the chemkin file for plotting purposes. """ from rmgpy.chemkin import loadChemkinFile from rmgpy.kinetics import ArrheniusEP, Chebyshev from rmgpy.reaction import Reaction from rmgpy.data.base import Entry kineticsDataList = [] chemkinPath = self.path + '/chemkin/chem.inp' dictionaryPath = self.path + 'RMG_Dictionary.txt' if self.Foreign: readComments = False else: readComments = True if os.path.exists(dictionaryPath): speciesList, reactionList = loadChemkinFile( chemkinPath, dictionaryPath, readComments=readComments) else: speciesList, reactionList = loadChemkinFile( chemkinPath, readComments=readComments) for reaction in reactionList: # If the kinetics are ArrheniusEP, replace them with Arrhenius if isinstance(reaction.kinetics, ArrheniusEP): reaction.kinetics = reaction.kinetics.toArrhenius( reaction.getEnthalpyOfReaction(298)) if os.path.exists(dictionaryPath): reactants = ' + '.join([ moleculeToInfo(reactant) for reactant in reaction.reactants ]) arrow = '⇔' if reaction.reversible else '→' products = ' + '.join( [moleculeToInfo(product) for product in reaction.products]) href = reaction.getURL() else: reactants = ' + '.join( [reactant.label for reactant in reaction.reactants]) arrow = '⇔' if reaction.reversible else '→' products = ' + '.join( [product.label for product in reaction.products]) href = '' source = str(reaction).replace('<=>', '=') entry = Entry() entry.result = reactionList.index(reaction) + 1 forwardKinetics = reaction.kinetics forward = True chemkin = reaction.toChemkin(speciesList) reverseKinetics = reaction.generateReverseRateCoefficient() reverseKinetics.comment = 'Fitted reverse reaction. ' + reaction.kinetics.comment rev_reaction = Reaction(reactants=reaction.products, products=reaction.reactants, kinetics=reverseKinetics) chemkin_rev = rev_reaction.toChemkin(speciesList) kineticsDataList.append([ reactants, arrow, products, entry, forwardKinetics, source, href, forward, chemkin, reverseKinetics, chemkin_rev ]) return kineticsDataList
def save_kinetics_lib(rxn_list, path, name, lib_long_desc): """ Save an RMG kinetics library of all reactions in `rxn_list` in the supplied `path`. `rxn_list` is a list of ARCReaction objects. `name` is the library's name (or project's name). `long_desc` is a multiline string with level of theory description. """ entries = dict() if rxn_list: for i, rxn in enumerate(rxn_list): if rxn.kinetics is not None: if len(rxn.rmg_reaction.reactants): reactants = rxn.rmg_reaction.reactants products = rxn.rmg_reaction.products elif rxn.r_species.mol_list is not None: reactants = [ Species(molecule=arc_spc.mol_list) for arc_spc in rxn.r_species ] products = [ Species(molecule=arc_spc.mol_list) for arc_spc in rxn.p_species ] elif rxn.r_species.mol is not None: reactants = [ Species(molecule=[arc_spc.mol]) for arc_spc in rxn.r_species ] products = [ Species(molecule=[arc_spc.mol]) for arc_spc in rxn.p_species ] else: reactants = [ Species(molecule=[arc_spc.xyz_mol]) for arc_spc in rxn.r_species ] products = [ Species(molecule=[arc_spc.xyz_mol]) for arc_spc in rxn.p_species ] rxn.rmg_reaction.reactants = reactants rxn.rmg_reaction.products = products entry = Entry(index=i, item=rxn.rmg_reaction, data=rxn.kinetics, label=rxn.label) rxn.ts_species.make_ts_report() entry.longDesc = rxn.ts_species.ts_report + '\n\nOptimized TS geometry:\n' + rxn.ts_species.final_xyz rxn.rmg_reaction.kinetics = rxn.kinetics rxn.rmg_reaction.kinetics.comment = str('') entries[i + 1] = entry else: logger.warning( 'Reaction {0} did not contain any kinetic data and was omitted from the kinetics' ' library.'.format(rxn.label)) kinetics_library = KineticsLibrary(name=name, longDesc=lib_long_desc, autoGenerated=True) kinetics_library.entries = entries lib_path = os.path.join(path, 'kinetics', '') if os.path.exists(lib_path): shutil.rmtree(lib_path) try: os.makedirs(lib_path) except OSError: pass kinetics_library.save(os.path.join(lib_path, 'reactions.py')) kinetics_library.saveDictionary( os.path.join(lib_path, 'dictionary.txt'))