def loadChemkinFile(path, dictionaryPath=None): """ Load a Chemkin input file to `path` on disk, returning lists of the species and reactions in the Chemkin file. """ speciesList = []; speciesDict = {} reactionList = [] # If the dictionary path is given, the read it and generate Molecule objects # You need to append an additional adjacency list for nonreactive species, such # as N2, or else the species objects will not store any structures for the final # HTML output. if dictionaryPath: with open(dictionaryPath, 'r') as f: adjlist = '' for line in f: if line.strip() == '' and adjlist.strip() != '': # Finish this adjacency list species = Species().fromAdjacencyList(adjlist) species.generateResonanceIsomers() speciesDict[species.label] = species adjlist = '' else: if "InChI" in line: line = line.split()[0] + '\n' if '//' in line: index = line.index('//') line = line[0:index] adjlist += line def removeCommentFromLine(line): if '!' in line: index = line.index('!') comment = line[index+1:-1] line = line[0:index] + '\n' return line, comment else: comment = '' return line, comment def checkDuplicateKinetics(reaction, kinetics,comments,dupReactionList,reactionList): if 'DUP' in kinetics: kinetics = kinetics.replace('\nDUP','') reaction = readKineticsEntry(kinetics,speciesDict,energyUnits,moleculeUnits) reaction.kinetics.comment = comments if dupReactionList: if not reaction.hasTemplate(dupReactionList[-1].reactants,dupReactionList[-1].products): # It's not the same kind of duplicate reaction oldReactionKinetics = MultiKinetics() for item in dupReactionList: oldReactionKinetics.kineticsList.append(item.kinetics) oldReaction = dupReactionList[0] oldReaction.kinetics = oldReactionKinetics reactionList.append(oldReaction) dupReactionList = [] dupReactionList.append(reaction) kinetics = '' comments = '' return reaction, kinetics, comments, dupReactionList, reactionList else: # No more duplicate reactions if dupReactionList: # add previous reaction if they were duplicate reactions oldReactionKinetics = MultiKinetics() for item in dupReactionList: oldReactionKinetics.kineticsList.append(item.kinetics) oldReaction = dupReactionList[0] oldReaction.kinetics = oldReactionKinetics reactionList.append(oldReaction) dupReactionList = [] # add this new, nonduplicate reaction reaction = readKineticsEntry(kinetics,speciesDict,energyUnits,moleculeUnits) reaction.kinetics.comment = comments reactionList.append(reaction) kinetics = '' comments = '' return reaction, kinetics, comments, dupReactionList, reactionList with open(path, 'r') as f: line = f.readline() while line != '': line = removeCommentFromLine(line)[0] line = line.strip() tokens = line.split() if 'SPECIES' in line: # List of species identifiers index = tokens.index('SPECIES') tokens = tokens[index+1:] while 'END' not in tokens: line = f.readline() line = removeCommentFromLine(line)[0] line = line.strip() tokens.extend(line.split()) for token in tokens: if token == 'END': break if token in speciesDict: species = speciesDict[token] else: species = Species(label=token) speciesDict[token] = species speciesList.append(species) # Also always add in a few bath gases (since RMG-Java does) for label, smiles in [('Ar','[Ar]'), ('He','[He]'), ('Ne','[Ne]'), ('N2','N#N')]: molecule = Molecule().fromSMILES(smiles) for species in speciesList: if species.label == label: if len(species.molecule) == 0: species.molecule = [molecule] break if species.isIsomorphic(molecule): break else: species = Species(label=label, molecule=[molecule]) speciesList.append(species) speciesDict[label] = species elif 'THERM' in line: # List of thermodynamics (hopefully one per species!) line = f.readline() thermo = '' while line != '' and 'END' not in line: line = removeCommentFromLine(line)[0] if len(line) >= 80: if line[79] in ['1', '2', '3', '4']: thermo += line if line[79] == '4': label, thermo = readThermoEntry(thermo) try: speciesDict[label].thermo = thermo except KeyError: if label in ['Ar', 'N2', 'He', 'Ne']: pass else: logging.warning('Skipping unexpected species "{0}" while reading thermodynamics entry.'.format(label)) thermo = '' line = f.readline() elif 'REACTIONS' in line: # Reactions section energyUnits = 'CAL/MOL' moleculeUnits = 'MOLES' try: energyUnits = tokens[1] moleculeUnits = tokens[2] except IndexError: pass kineticsList = [] commentsList = [] kinetics = '' comments = '' line = f.readline() while line != '' and 'END' not in line: lineStartsWithComment = line.startswith('!') line, comment = removeCommentFromLine(line) line = line.strip(); comment = comment.strip() if 'rev' in line or 'REV' in line: # can no longer name reactants rev... line = f.readline() if '=' in line and not lineStartsWithComment: # Finish previous record kineticsList.append(kinetics) commentsList.append(comments) kinetics = '' comments = '' if line: kinetics += line + '\n' if comment: comments += comment + '\n' line = f.readline() # Don't forget the last reaction! if kinetics.strip() != '': kineticsList.append(kinetics) commentsList.append(comments) if kineticsList[0] == '' and commentsList[-1] == '': # True for Chemkin files generated from RMG-Py kineticsList.pop(0) commentsList.pop(-1) elif kineticsList[0] == '' and commentsList[0] == '': # True for Chemkin files generated from RMG-Java kineticsList.pop(0) commentsList.pop(0) else: # In reality, comments can occur anywhere in the Chemkin # file (e.g. either or both of before and after the # reaction equation) # If we can't tell what semantics we are using, then just # throw the comments away # (This is better than failing to load the Chemkin file at # all, which would likely occur otherwise) if kineticsList[0] == '': kineticsList.pop(0) if len(kineticsList) != len(commentsList): commentsList = ['' for kinetics in kineticsList] for kinetics, comments in zip(kineticsList, commentsList): reaction = readKineticsEntry(kinetics, speciesDict, energyUnits, moleculeUnits) reaction = readReactionComments(reaction, comments) reactionList.append(reaction) line = f.readline() # Check for marked (and unmarked!) duplicate reactions # Combine marked duplicate reactions into a single reaction using MultiKinetics # Raise exception for unmarked duplicate reactions duplicateReactionsToRemove = [] duplicateReactionsToAdd = [] for index1 in range(len(reactionList)): reaction1 = reactionList[index1] if reaction1 in duplicateReactionsToRemove: continue for index2 in range(index1+1, len(reactionList)): reaction2 = reactionList[index2] if reaction1.reactants == reaction2.reactants and reaction1.products == reaction2.products: if reaction1.duplicate and reaction2.duplicate: if not isinstance(reaction1, LibraryReaction) or not isinstance(reaction2, LibraryReaction): # Only make a MultiKinetics for library reactions, not template reactions continue for reaction in duplicateReactionsToAdd: if reaction1.reactants == reaction.reactants and reaction1.products == reaction.products: break else: assert reaction1.library.label == reaction2.library.label reaction = LibraryReaction( index = reaction1.index, reactants = reaction1.reactants, products = reaction1.products, kinetics = MultiKinetics(), library = reaction1.library, duplicate = False, ) duplicateReactionsToAdd.append(reaction) reaction.kinetics.kineticsList.append(reaction1.kinetics) duplicateReactionsToRemove.append(reaction1) reaction.kinetics.kineticsList.append(reaction2.kinetics) duplicateReactionsToRemove.append(reaction2) elif reaction1.kinetics.isPressureDependent() == reaction2.kinetics.isPressureDependent(): # If both reactions are pressure-independent or both are pressure-dependent, then they need duplicate tags # Chemkin treates pdep and non-pdep reactions as different, so those are okay raise ChemkinError('Encountered unmarked duplicate reaction {0}.'.format(reaction1)) for reaction in duplicateReactionsToRemove: reactionList.remove(reaction) reactionList.extend(duplicateReactionsToAdd) index = 0 for reaction in reactionList: index += 1 reaction.index = index return speciesList, reactionList