Exemplo n.º 1
0
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