Exemplo n.º 1
0
def save_entry(f, entry):
    """
    Write a Pythonic string representation of the given `entry` in the transport
    database to the file object `f`.
    """
    f.write('entry(\n')
    f.write('    index = {0:d},\n'.format(entry.index))
    f.write('    label = "{0}",\n'.format(entry.label))

    if isinstance(entry.item, Molecule):
        f.write('    molecule = \n')
        f.write('"""\n')
        f.write(entry.item.to_adjacency_list(remove_h=False))
        f.write('""",\n')
    elif isinstance(entry.item, Group):
        f.write('    group = \n')
        f.write('"""\n')
        f.write(entry.item.to_adjacency_list())
        f.write('""",\n')
    else:
        f.write('    group = "{0}",\n'.format(entry.item))

    if isinstance(entry.data, CriticalPointGroupContribution):
        f.write('    transportGroup = CriticalPointGroupContribution(\n')
        f.write('        Tc = {0!r},\n'.format(entry.data.Tc))
        f.write('        Pc = {0!r},\n'.format(entry.data.Pc))
        f.write('        Vc = {0!r},\n'.format(entry.data.Vc))
        f.write('        Tb = {0!r},\n'.format(entry.data.Tb))
        f.write('        structureIndex = {0!r},\n'.format(entry.data.structureIndex))
        f.write('    ),\n')
    elif entry.data is None:
        f.write('    transportGroup = None,\n')
    elif isinstance(entry.data, TransportData):
        f.write('    transport = TransportData(\n')
        f.write('        shapeIndex = {0!r},\n'.format(entry.data.shapeIndex))
        f.write('        epsilon = {0!r},\n'.format(entry.data.epsilon))
        f.write('        sigma = {0!r},\n'.format(entry.data.sigma))
        f.write('        dipoleMoment = {0!r},\n'.format(entry.data.dipoleMoment))
        f.write('        polarizability = {0!r},\n'.format(entry.data.polarizability))
        f.write('        rotrelaxcollnum = {0!r},\n'.format(entry.data.rotrelaxcollnum))
        f.write('    ),\n')
    else:
        raise DatabaseError("Not sure how to save {0!r}".format(entry.data))

    f.write(f'    shortDesc = """{entry.short_desc.strip()}""",\n')
    f.write(f'    longDesc = \n"""\n{entry.long_desc.strip()}\n""",\n')

    f.write(')\n\n')
Exemplo n.º 2
0
 def checkForDuplicates(self):
     """
     Check that all duplicate reactions in the kinetics library are
     properly marked (i.e. with their ``duplicate`` attribute set to 
     ``True``).
     """
     for entry0 in self.entries.values():
         reaction0 = entry0.item
         if not reaction0.duplicate:
             # This reaction is not marked as a duplicate reaction
             # This means that if we find any duplicate reactions, it is an error
             for entry in self.entries.values():
                 reaction = entry.item
                 if reaction0 is not reaction and reaction0.isIsomorphic(reaction): 
                     # We found a duplicate reaction that wasn't marked!
                     raise DatabaseError('Unexpected duplicate reaction {0} in kinetics library {1}.'.format(reaction0, self.label))        
Exemplo n.º 3
0
    def get_all_entries_on_metal(self, metal_name):
        """
        Get all entries from the database that are on a certain metal, for any facet,
        returning the labels.

        Raises DatabaseError (rather than an empty list) if none can be found.
        """
        matches = []
        for label, entry in self.entries.items():
            if entry.metal == metal_name:
                matches.append(label)

        if len(matches) == 0:
            raise DatabaseError(f'Metal {metal_name!r} not found in database.')

        return matches
Exemplo n.º 4
0
 def loadEntry(self, index, label, group, kinetics, reference=None, referenceType='', shortDesc='', longDesc=''):
     if group[0:3].upper() == 'OR{' or group[0:4].upper() == 'AND{' or group[0:7].upper() == 'NOT OR{' or group[0:8].upper() == 'NOT AND{':
         item = makeLogicNode(group)
     else:
         item = Group().fromAdjacencyList(group)
         
     if label in self.entries:
         raise DatabaseError("Duplicate group name {label} found in kinetics groups for {family} family.".format(label=label,family=self.label))
     self.entries[label] = Entry(
         index = index,
         label = label,
         item = item,
         data = kinetics,
         reference = reference,
         referenceType = referenceType,
         shortDesc = shortDesc,
         longDesc = longDesc.strip(),
     )
Exemplo n.º 5
0
 def loadRecommendedFamiliesList(self, filepath):
     """
     Load the list of recommended families from the given file
     
     The file is usually 'kinetics/families/recommended.py'.
     This is stored as a dictionary of True or False values (checked here),
     and should contain entries for every available family (checked in loadFamilies).
     """
     try:
         global_context = {}
         global_context['__builtins__'] = None
         global_context['True'] = True
         global_context['False'] = False
         local_context = {}
         local_context['__builtins__'] = None
         f = open(filepath, 'r')
         exec f in global_context, local_context
         f.close()
         self.recommendedFamilies = local_context['recommendedFamilies']
     except Exception, e:
         raise DatabaseError('Error while reading list of recommended families from {0}/recommended.py.\n{1}'.format(filepath,e))
Exemplo n.º 6
0
 def checkForDuplicates(self, markDuplicates=False):
     """
     Check that all duplicate reactions in the kinetics library are
     properly marked (i.e. with their ``duplicate`` attribute set to 
     ``True``).
     If ``markDuplicates`` is set to ``True``, then ignore and
     mark all duplicate reactions as duplicate.
     """
     for entry0 in self.entries.values():
         reaction0 = entry0.item
         if not reaction0.duplicate:
             # This reaction is not marked as a duplicate reaction
             # This means that if we find any duplicate reactions, it is an error
             for entry in self.entries.values():
                 reaction = entry.item
                 if reaction0 is not reaction and reaction0.isIsomorphic(reaction): 
                     # We found a duplicate reaction that wasn't marked!
                     # RMG requires all duplicate reactions to be marked, unlike CHEMKIN
                     if markDuplicates:
                         reaction0.duplicate = reaction.duplicate = True
                         logging.warning('Reaction indices {0} and {1} were marked as duplicate.'.format(entry0.index, entry.index))
                         continue
                     
                     raise DatabaseError('Unexpected duplicate reaction {0} in kinetics library {1}. Reaction index {2} matches index {3}.'.format(reaction0, self.label, entry.index, entry0.index))        
Exemplo n.º 7
0
    def loadEntry(
        self,
        index,
        reactant1,
        product1,
        kinetics,
        reactant2=None,
        reactant3=None,
        product2=None,
        product3=None,
        degeneracy=1,
        label='',
        duplicate=False,
        reversible=True,
        reference=None,
        referenceType='',
        shortDesc='',
        longDesc='',
    ):

        reactants = [
            Species(label=reactant1.strip().splitlines()[0].strip(),
                    molecule=[Molecule().fromAdjacencyList(reactant1)])
        ]
        if reactant2 is not None:
            reactants.append(
                Species(label=reactant2.strip().splitlines()[0].strip(),
                        molecule=[Molecule().fromAdjacencyList(reactant2)]))
        if reactant3 is not None:
            reactants.append(
                Species(label=reactant3.strip().splitlines()[0].strip(),
                        molecule=[Molecule().fromAdjacencyList(reactant3)]))

        products = [
            Species(label=product1.strip().splitlines()[0].strip(),
                    molecule=[Molecule().fromAdjacencyList(product1)])
        ]
        if product2 is not None:
            products.append(
                Species(label=product2.strip().splitlines()[0].strip(),
                        molecule=[Molecule().fromAdjacencyList(product2)]))
        if product3 is not None:
            products.append(
                Species(label=product3.strip().splitlines()[0].strip(),
                        molecule=[Molecule().fromAdjacencyList(product3)]))

        comment = "Reaction and kinetics from {0}.".format(self.label)
        if shortDesc.strip():
            comment += "{0!s}\n".format(shortDesc.strip())
        if longDesc.strip():
            comment += str(re.sub('\s*\n\s*', '\n', longDesc))
        kinetics.comment = comment.strip()

        # Perform mass balance check on the reaction
        rxn = Reaction(reactants=reactants,
                       products=products,
                       degeneracy=degeneracy,
                       duplicate=duplicate,
                       reversible=reversible)
        if not rxn.isBalanced():
            raise DatabaseError(
                'Reaction {0} in kinetics library {1} was not balanced! Please reformulate.'
                .format(rxn, self.label))

        assert index not in self.entries, "Reaction with index {0} already present!".format(
            index)
        self.entries[index] = Entry(
            index=index,
            label=label,
            item=rxn,
            data=kinetics,
            reference=reference,
            referenceType=referenceType,
            shortDesc=shortDesc,
            longDesc=longDesc.strip(),
        )

        # Convert SMILES to Molecule objects in collision efficiencies
        if isinstance(kinetics, PDepKineticsModel):
            efficiencies = {}
            for smiles, eff in kinetics.efficiencies.items():
                if isinstance(smiles, str):
                    efficiencies[Molecule().fromSMILES(smiles)] = eff
            kinetics.efficiencies = efficiencies
Exemplo n.º 8
0
def saveEntry(f, entry):
    """
    Save an `entry` in the kinetics database by writing a string to
    the given file object `f`.
    """
    from rmgpy.cantherm.output import prettify

    def sortEfficiencies(efficiencies0):
        efficiencies = {}
        for mol, eff in efficiencies0.iteritems():
            if isinstance(mol, str):
                # already in SMILES string format
                smiles = mol
            else:
                smiles = mol.toSMILES()
                
            efficiencies[smiles] = eff
        keys = efficiencies.keys()
        keys.sort()
        return [(key, efficiencies[key]) for key in keys]

    f.write('entry(\n')
    f.write('    index = {0:d},\n'.format(entry.index))
    if entry.label != '':
        f.write('    label = "{0}",\n'.format(entry.label))


    #Entries for kinetic rules, libraries, training reactions
    #and depositories will have an Reaction object for its item
    if isinstance(entry.item, Reaction):
        #Write out additional data if depository or library
        #kinetic rules would have a Group object for its reactants instead of Species
        if isinstance(entry.item.reactants[0], Species):
            # Add degeneracy if the reaction is coming from a depository or kinetics library
            f.write('    degeneracy = {0:.1f},\n'.format(entry.item.degeneracy))
            if entry.item.duplicate:
                f.write('    duplicate = {0!r},\n'.format(entry.item.duplicate))
            if not entry.item.reversible:
                f.write('    reversible = {0!r},\n'.format(entry.item.reversible))
    #Entries for groups with have a group or logicNode for its item
    elif isinstance(entry.item, Group):
        f.write('    group = \n')
        f.write('"""\n')
        f.write(entry.item.toAdjacencyList())
        f.write('""",\n')
    elif isinstance(entry.item, LogicNode):
        f.write('    group = "{0}",\n'.format(entry.item))
    else:
        raise DatabaseError("Encountered unexpected item of type {0} while saving database.".format(entry.item.__class__))

    # Write kinetics
    if isinstance(entry.data, str):
        f.write('    kinetics = "{0}",\n'.format(entry.data))
    elif entry.data is not None:
        efficiencies = None
        if hasattr(entry.data, 'efficiencies'):
            efficiencies = entry.data.efficiencies
            entry.data.efficiencies = dict(sortEfficiencies(entry.data.efficiencies))
        kinetics = prettify(repr(entry.data))
        kinetics = '    kinetics = {0},\n'.format(kinetics.replace('\n', '\n    '))
        f.write(kinetics)
        if hasattr(entry.data, 'efficiencies'):
            entry.data.efficiencies = efficiencies
    else:
        f.write('    kinetics = None,\n')
            
    # Write reference
    if entry.reference is not None:
        reference = entry.reference.toPrettyRepr()
        lines = reference.splitlines()
        f.write('    reference = {0}\n'.format(lines[0]))
        for line in lines[1:-1]:
            f.write('    {0}\n'.format(line))
        f.write('    ),\n'.format(lines[0]))
    
    if entry.referenceType != "":
        f.write('    referenceType = "{0}",\n'.format(entry.referenceType))
    if entry.rank is not None:
        f.write('    rank = {0},\n'.format(entry.rank))
        
    if entry.shortDesc.strip() !='':
        f.write('    shortDesc = u"""')
        try:
            f.write(entry.shortDesc.encode('utf-8'))
        except:
            f.write(entry.shortDesc.strip().encode('ascii', 'ignore')+ "\n")
        f.write('""",\n')
    
    if entry.longDesc.strip() !='':
        f.write('    longDesc = \n')
        f.write('u"""\n')
        try:
            f.write(entry.longDesc.strip().encode('utf-8') + "\n")
        except:
            f.write(entry.longDesc.strip().encode('ascii', 'ignore')+ "\n")
        f.write('""",\n')

    f.write(')\n\n')
Exemplo n.º 9
0
class KineticsDatabase(object):
    """
    A class for working with the RMG kinetics database.
    """
    def __init__(self):
        self.recommendedFamilies = {}
        self.families = {}
        self.libraries = {}
        self.libraryOrder = [
        ]  # a list of tuples in the format ('library_label', LibraryType),
        # where LibraryType is set to either 'Reaction Library' or 'Seed'.
        self.local_context = {
            'KineticsData': KineticsData,
            'Arrhenius': Arrhenius,
            'ArrheniusEP': ArrheniusEP,
            'MultiArrhenius': MultiArrhenius,
            'MultiPDepArrhenius': MultiPDepArrhenius,
            'PDepArrhenius': PDepArrhenius,
            'Chebyshev': Chebyshev,
            'ThirdBody': ThirdBody,
            'Lindemann': Lindemann,
            'Troe': Troe,
            'R': constants.R,
        }
        self.global_context = {}

    def __reduce__(self):
        """
        A helper function used when pickling a KineticsDatabase object.
        """
        d = {
            'families': self.families,
            'libraries': self.libraries,
            'libraryOrder': self.libraryOrder,
        }
        return (KineticsDatabase, (), d)

    def __setstate__(self, d):
        """
        A helper function used when unpickling a KineticsDatabase object.
        """
        self.families = d['families']
        self.libraries = d['libraries']
        self.libraryOrder = d['libraryOrder']

    def load(self, path, families=None, libraries=None, depositories=None):
        """
        Load the kinetics database from the given `path` on disk, where `path`
        points to the top-level folder of the families database.
        """
        self.loadRecommendedFamiliesList(
            os.path.join(path, 'families', 'recommended.py')),
        self.loadFamilies(os.path.join(path, 'families'), families,
                          depositories)
        self.loadLibraries(os.path.join(path, 'libraries'), libraries)

    def loadRecommendedFamiliesList(self, filepath):
        """
        Load the list of recommended families from the given file
        
        The file is usually 'kinetics/families/recommended.py'.
        This is stored as a dictionary of True or False values (checked here),
        and should contain entries for every available family (checked in loadFamilies).
        """
        try:
            global_context = {}
            global_context['__builtins__'] = None
            global_context['True'] = True
            global_context['False'] = False
            local_context = {}
            local_context['__builtins__'] = None
            f = open(filepath, 'r')
            exec f in global_context, local_context
            f.close()
            self.recommendedFamilies = local_context['recommendedFamilies']
        except Exception, e:
            raise DatabaseError(
                'Error while reading list of recommended families from {0}/recommended.py.\n{1}'
                .format(filepath, e))
        for recommended in self.recommendedFamilies.values():
            if not isinstance(recommended, bool):
                raise DatabaseError(
                    "recommendedFamilies dictionary should contain only True or False values"
                )
Exemplo n.º 10
0
def saveEntry(f, entry):
    """
    Save an `entry` in the kinetics database by writing a string to
    the given file object `f`.
    """
    from rmgpy.cantherm.output import prettify

    def sortEfficiencies(efficiencies0):
        efficiencies = {}
        for mol, eff in efficiencies0.iteritems():
            if isinstance(mol, str):
                # already in SMILES string format
                smiles = mol
            else:
                smiles = mol.toSMILES()
                
            efficiencies[smiles] = eff
        keys = efficiencies.keys()
        keys.sort()
        return [(key, efficiencies[key]) for key in keys]

    f.write('entry(\n')
    f.write('    index = {0:d},\n'.format(entry.index))
    if entry.label != '':
        f.write('    label = "{0}",\n'.format(entry.label))

    if isinstance(entry.item, Reaction):
        if entry.label != str(entry.item):
            raise KineticsError("Reactions are now defined solely by their labels, "
                                                "but reaction {0!s} has label {1!r}".format(
                                                 entry.item, entry.label))
#        for i, reactant in enumerate(entry.item.reactants):
#            if isinstance(reactant, Molecule):
#                f.write('    reactant{0:d} = \n'.format(i+1))
#                f.write('"""\n')
#                f.write(reactant.toAdjacencyList(removeH=False))
#                f.write('""",\n')
#            elif isinstance(reactant, Species):
#                f.write('    reactant{0:d} = \n'.format(i+1))
#                f.write('"""\n')
#                f.write(reactant.molecule[0].toAdjacencyList(label=reactant.label, removeH=False))
#                f.write('""",\n')
#        for i, product in enumerate(entry.item.products):
#            if isinstance(product, Molecule):
#                f.write('    product{0:d} = \n'.format(i+1))
#                f.write('"""\n')
#                f.write(product.toAdjacencyList(removeH=False))
#                f.write('""",\n')
#            elif isinstance(reactant, Species):
#                f.write('    product{0:d} = \n'.format(i+1))
#                f.write('"""\n')
#                f.write(product.molecule[0].toAdjacencyList(label=product.label, removeH=False))
#                f.write('""",\n')
        if isinstance(entry.item.reactants[0], Species): 
            # Add degeneracy if the reaction is coming from a depository or kinetics library
            f.write('    degeneracy = {0:d},\n'.format(entry.item.degeneracy))
        if entry.item.duplicate: 
            f.write('    duplicate = {0!r},\n'.format(entry.item.duplicate))
        if not entry.item.reversible:
            f.write('    reversible = {0!r},\n'.format(entry.item.reversible))
    elif isinstance(entry.item, Group):
        f.write('    group = \n')
        f.write('"""\n')
        f.write(entry.item.toAdjacencyList())
        f.write('""",\n')
    elif isinstance(entry.item, LogicNode):
        f.write('    group = "{0}",\n'.format(entry.item))
    else:
        raise DatabaseError("Encountered unexpected item of type {0} while saving database.".format(entry.item.__class__))

    # Write kinetics
    if isinstance(entry.data, str):
        f.write('    kinetics = "{0}",\n'.format(entry.data))
    elif entry.data is not None:
        efficiencies = None
        if hasattr(entry.data, 'efficiencies'):
            efficiencies = entry.data.efficiencies
            entry.data.efficiencies = dict(sortEfficiencies(entry.data.efficiencies))
        kinetics = prettify(repr(entry.data))
        kinetics = '    kinetics = {0},\n'.format(kinetics.replace('\n', '\n    '))
        f.write(kinetics)
        if hasattr(entry.data, 'efficiencies'):
            entry.data.efficiencies = efficiencies
    else:
        f.write('    kinetics = None,\n')
            
    # Write reference
    if entry.reference is not None:
        reference = entry.reference.toPrettyRepr()
        lines = reference.splitlines()
        f.write('    reference = {0}\n'.format(lines[0]))
        for line in lines[1:-1]:
            f.write('    {0}\n'.format(line))
        f.write('    ),\n'.format(lines[0]))
    
    if entry.referenceType != "":
        f.write('    referenceType = "{0}",\n'.format(entry.referenceType))
    if entry.rank is not None:
        f.write('    rank = {0},\n'.format(entry.rank))
        
    if entry.shortDesc.strip() !='':
        f.write('    shortDesc = u"""')
        try:
            f.write(entry.shortDesc.encode('utf-8'))
        except:
            f.write(entry.shortDesc.strip().encode('ascii', 'ignore')+ "\n")
        f.write('""",\n')
    
    if entry.longDesc.strip() !='':
        f.write('    longDesc = \n')
        f.write('u"""\n')
        try:
            f.write(entry.longDesc.strip().encode('utf-8') + "\n")
        except:
            f.write(entry.longDesc.strip().encode('ascii', 'ignore')+ "\n")
        f.write('""",\n')

    f.write(')\n\n')
Exemplo n.º 11
0
    def load(self, path, local_context=None, global_context=None):
        # Clear any previously-loaded data
        self.entries = OrderedDict()
        self.top = []

        # Set up global and local context
        if global_context is None: global_context = {}
        global_context['__builtins__'] = None
        global_context['True'] = True
        global_context['False'] = False
        if local_context is None: local_context = {}
        local_context['__builtins__'] = None
        local_context['entry'] = self.loadEntry
        #local_context['tree'] = self.__loadTree
        local_context['name'] = self.name
        local_context['solvent'] = self.solvent
        local_context['shortDesc'] = self.shortDesc
        local_context['longDesc'] = self.longDesc
        # add in anything from the Class level dictionary.
        for key, value in Database.local_context.iteritems():
            local_context[key] = value

        # Process the file
        f = open(path, 'r')
        try:
            exec f in global_context, local_context
        except Exception:
            logging.error('Error while reading database {0!r}.'.format(path))
            raise
        f.close()

        # Extract the database metadata
        self.name = local_context['name']
        self.solvent = local_context['solvent']
        self.shortDesc = local_context['shortDesc']
        self.longDesc = local_context['longDesc'].strip()

        if 'duplicatesChecked' in local_context.keys():
            self.duplicatesChecked = local_context['duplicatesChecked']

        # Load a unique set of the species in the kinetics library
        speciesDict = self.getSpecies(
            os.path.join(os.path.dirname(path), 'dictionary.txt'))
        # Make sure all of the reactions draw from only this set
        entries = self.entries.values()
        for entry in entries:
            # Create a new reaction per entry
            rxn = entry.item
            rxn_string = entry.label
            # Convert the reactants and products to Species objects using the speciesDict
            reactants, products = rxn_string.split('=>')
            reversible = True
            if '<=>' in rxn_string:
                reactants = reactants[:-1]
                products = products[1:]
            elif '=>' in rxn_string:
                products = products[1:]
                reversible = False
            assert reversible == rxn.reversible, "Reaction string reversibility (=>) and entry attribute `reversible` (set to `False`) must agree if reaction is irreversible."

            collider = re.search('\(\+[^\)]+\)', reactants)
            if collider is not None:
                collider = collider.group(
                    0)  # save string value rather than the object
                assert collider == re.search('\(\+[^\)]+\)',products).group(0), "Third body colliders in reaction {0} in kinetics library {1} are missing" \
                                                                                " from products or are not identical!".format(rxn_string, self.label)
                extraParenthesis = collider.count('(') - 1
                for i in xrange(extraParenthesis):
                    collider += ')'  # allow for species like N2(5) or CH2(T)(15) to be read as specific colliders, although currently not implemented in Chemkin. See RMG-Py #1070
                reactants = reactants.replace(collider, '', 1)
                products = products.replace(collider, '', 1)
                if collider.upper().strip(
                ) != "(+M)":  # the collider is a specific species, not (+M) or (+m)
                    if collider.strip(
                    )[2:
                      -1] not in speciesDict:  # stripping spaces, '(+' and ')'
                        raise DatabaseError(
                            'Collider species {0} in kinetics library {1} is missing from its dictionary.'
                            .format(collider.strip()[2:-1], self.label))
                    rxn.specificCollider = speciesDict[collider.strip()[2:-1]]
            # verify there's no more than one specificCollider:
            collider = re.search('\(\+[^\)]+\)', reactants)
            if collider is not None:
                raise DatabaseError(
                    "Found TWO specific third body colliders, {0} and {1}, in reaction {2} in kinetics library {3), expecting no more than one!"
                    .format(rxn.specificCollider, collider.group(0),
                            rxn_string, self.label))

            for reactant in reactants.split('+'):
                reactant = reactant.strip()
                if reactant not in speciesDict:
                    raise DatabaseError(
                        'Species {0} in kinetics library {1} is missing from its dictionary.'
                        .format(reactant, self.label))
                rxn.reactants.append(speciesDict[reactant])
            for product in products.split('+'):
                product = product.strip()
                if product not in speciesDict:
                    raise DatabaseError(
                        'Species {0} in kinetics library {1} is missing from its dictionary.'
                        .format(product, self.label))
                rxn.products.append(speciesDict[product])

            if not rxn.isBalanced():
                raise DatabaseError(
                    'Reaction {0} in kinetics library {1} was not balanced! Please reformulate.'
                    .format(rxn, self.label))

            if len(rxn.reactants) > 3:
                raise DatabaseError(
                    'RMG does not accept reactions with more than 3 reactants in its solver.  Reaction {0} in kinetics library {1} has {2} reactants.'
                    .format(rxn, self.label, len(rxn.reactants)))
            if len(rxn.products) > 3:
                raise DatabaseError(
                    'RMG does not accept reactions with more than 3 products in its solver.  Reaction {0} in kinetics library {1} has {2} reactants.'
                    .format(rxn, self.label, len(rxn.products)))

        if self.duplicatesChecked:
            self.checkForDuplicates()
        else:
            self.checkForDuplicates(markDuplicates=True)
            self.convertDuplicatesToMulti()
Exemplo n.º 12
0
    def save_entry(self, f, entry):
        """
        Save an `entry` in the kinetics database by writing a string to
        the given file object `f`.
        """
        from arkane.output import prettify

        def sort_efficiencies(efficiencies0):
            efficiencies = {}
            for mol, eff in efficiencies0.items():
                if isinstance(mol, str):
                    # already in SMILES string format
                    smiles = mol
                else:
                    smiles = mol.to_smiles()

                efficiencies[smiles] = eff
            keys = list(efficiencies.keys())
            keys.sort()
            return [(key, efficiencies[key]) for key in keys]

        f.write('entry(\n')
        f.write('    index = {0:d},\n'.format(entry.index))
        if entry.label != '':
            f.write('    label = "{0}",\n'.format(entry.label))

        # Entries for kinetic rules, libraries, training reactions
        # and depositories will have a Reaction object for its item
        if isinstance(entry.item, Reaction):
            # Write out additional data if depository or library
            # kinetic rules would have a Group object for its reactants instead of Species
            if isinstance(entry.item.reactants[0], RMGSpecies):
                # Add degeneracy if the reaction is coming from a depository or kinetics library
                f.write('    degeneracy = {0:.1f},\n'.format(
                    entry.item.degeneracy))
                if entry.item.duplicate:
                    f.write('    duplicate = {0!r},\n'.format(
                        entry.item.duplicate))
                if not entry.item.reversible:
                    f.write('    reversible = {0!r},\n'.format(
                        entry.item.reversible))
                if entry.item.allow_pdep_route:
                    f.write('    allow_pdep_route = {0!r},\n'.format(
                        entry.item.allow_pdep_route))
                if entry.item.elementary_high_p:
                    f.write('    elementary_high_p = {0!r},\n'.format(
                        entry.item.elementary_high_p))
                if entry.item.allow_max_rate_violation:
                    f.write('    allow_max_rate_violation = {0!r},\n'.format(
                        entry.item.allow_max_rate_violation))
            # Entries for groups with have a group or logicNode for its item
        elif isinstance(entry.item, Group):
            f.write('    group = \n')
            f.write('"""\n')
            f.write(entry.item.to_adjacency_list())
            f.write('""",\n')
        elif isinstance(entry.item, LogicNode):
            f.write('    group = "{0}",\n'.format(entry.item))
        else:
            raise DatabaseError(
                "Encountered unexpected item of type {0} while saving database.".format(entry.item.__class__))

        # Write distances
        if isinstance(entry.data, DistanceData):
            data_str = prettify(repr(entry.data))

            data_str = data_str.replace('\n', '\n   ')

            f.write('    distances = {},\n'.format(data_str))
        else:
            assert False

        # Write reference
        if entry.reference is not None:
            reference = entry.reference.to_pretty_repr()
            lines = reference.splitlines()
            f.write('    reference = {0}\n'.format(lines[0]))
            for line in lines[1:-1]:
                f.write('    {0}\n'.format(line))
            f.write('    ),\n'.format(lines[0]))

        if entry.reference_type != "":
            f.write('    reference_type = "{0}",\n'.format(entry.reference_type))
        if entry.rank is not None:
            f.write('    rank = {0},\n'.format(entry.rank))

        if entry.short_desc.strip() != '':
            f.write('    short_desc = u"""')
            try:
                f.write(entry.short_desc.encode('utf-8'))
            except:
                f.write(entry.short_desc.strip().encode(
                    'ascii', 'ignore') + "\n")
            f.write('""",\n')

        if entry.long_desc.strip() != '':
            f.write('    long_desc = \n')
            f.write('u"""\n')
            try:
                f.write(entry.long_desc.strip().encode('utf-8') + "\n")
            except:
                f.write(entry.long_desc.strip().encode(
                    'ascii', 'ignore') + "\n")
            f.write('""",\n')

        f.write(')\n\n')

        return
Exemplo n.º 13
0
def save_entry(f, entry):
    """
    Write a Pythonic string representation of the given `entry` in the solvation
    database to the file object `f`.
    """
    f.write('entry(\n')
    f.write('    index = {0:d},\n'.format(entry.index))
    f.write('    label = "{0}",\n'.format(entry.label))

    if isinstance(entry.item, Species):
        if Molecule(smiles=entry.item.molecule[0].to_smiles()).is_isomorphic(
                entry.item.molecule[0]):
            # The SMILES representation accurately describes the molecule, so we can save it that way.
            f.write('    molecule = "{0}",\n'.format(
                entry.item.molecule[0].to_smiles()))
        else:
            f.write('    molecule = \n')
            f.write('"""\n')
            f.write(entry.item.to_adjacency_list(remove_h=False))
            f.write('""",\n')
    elif isinstance(entry.item, Group):
        f.write('    group = \n')
        f.write('"""\n')
        f.write(entry.item.to_adjacency_list())
        f.write('""",\n')
    elif entry.item is not None:
        f.write('    group = "{0}",\n'.format(entry.item))

    if isinstance(entry.data, SoluteData):
        f.write('    solute = SoluteData(\n')
        f.write('        S = {0!r},\n'.format(entry.data.S))
        f.write('        B = {0!r},\n'.format(entry.data.B))
        f.write('        E = {0!r},\n'.format(entry.data.E))
        f.write('        L = {0!r},\n'.format(entry.data.L))
        f.write('        A = {0!r},\n'.format(entry.data.A))
        if entry.data.V is not None:
            f.write('        V = {0!r},\n'.format(entry.data.V))
        f.write('    ),\n')
    elif isinstance(entry.data, SolventData):
        f.write('    solvent = SolventData(\n')
        f.write('        s_g = {0!r},\n'.format(entry.data.s_g))
        f.write('        b_g = {0!r},\n'.format(entry.data.b_g))
        f.write('        e_g = {0!r},\n'.format(entry.data.e_g))
        f.write('        l_g = {0!r},\n'.format(entry.data.l_g))
        f.write('        a_g = {0!r},\n'.format(entry.data.a_g))
        f.write('        c_g = {0!r},\n'.format(entry.data.c_g))
        f.write('        s_h = {0!r},\n'.format(entry.data.s_h))
        f.write('        b_h = {0!r},\n'.format(entry.data.b_h))
        f.write('        e_h = {0!r},\n'.format(entry.data.e_h))
        f.write('        l_h = {0!r},\n'.format(entry.data.l_h))
        f.write('        a_h = {0!r},\n'.format(entry.data.a_h))
        f.write('        c_h = {0!r},\n'.format(entry.data.c_h))
        f.write('        A = {0!r},\n'.format(entry.data.A))
        f.write('        B = {0!r},\n'.format(entry.data.B))
        f.write('        C = {0!r},\n'.format(entry.data.C))
        f.write('        D = {0!r},\n'.format(entry.data.D))
        f.write('        E = {0!r},\n'.format(entry.data.E))
        f.write('        alpha = {0!r},\n'.format(entry.data.alpha))
        f.write('        beta = {0!r},\n'.format(entry.data.beta))
        f.write('        eps = {0!r},\n'.format(entry.data.eps))
        f.write('    ),\n')
    elif entry.data is None:
        f.write('    solute = None,\n')
    else:
        raise DatabaseError("Not sure how to save {0!r}".format(entry.data))

    f.write(f'    shortDesc = """{entry.short_desc.strip()}""",\n')
    f.write(f'    longDesc = \n"""\n{entry.long_desc.strip()}\n""",\n')

    f.write(')\n\n')
    def load(self, path, local_context=None, global_context=None):
        import os
        Database.load(self, path, local_context, global_context)

        # Load the species in the kinetics library
        speciesDict = self.getSpecies(
            os.path.join(os.path.dirname(path), 'dictionary.txt'))
        # Make sure all of the reactions draw from only this set
        entries = self.entries.values()
        for entry in entries:
            # Create a new reaction per entry
            rxn = entry.item
            rxn_string = entry.label
            # Convert the reactants and products to Species objects using the speciesDict
            reactants, products = rxn_string.split('=')
            reversible = True
            if '<=>' in rxn_string:
                reactants = reactants[:-1]
                products = products[1:]
            elif '=>' in rxn_string:
                products = products[1:]
                reversible = False
            assert reversible == rxn.reversible, "Reaction string reversibility (=>) and entry attribute `reversible` (set to `False`) must agree if reaction is irreversible."

            specificCollider = None
            collider = re.search('\(\+[^\)]+\)', reactants)
            if collider is not None:
                collider = collider.group(
                    0)  # save string value rather than the object
                assert collider == re.search('\(\+[^\)]+\)', products).group(
                    0
                ), "Third body colliders in reaction {0} in kinetics library {1} are not identical!".format(
                    rxn_string, self.label)
                extraParenthesis = collider.count('(') - 1
                for i in xrange(extraParenthesis):
                    collider += ')'  # allow for species like N2(5) or CH2(T)(15) to be read as specific colliders, although currently not implemented in Chemkin. See RMG-Py #1070
                reactants = reactants.replace(collider, '')
                products = products.replace(collider, '')
                if collider.upper().strip(
                ) != "(+M)":  # the collider is a specific species, not (+M) or (+m)
                    if collider.strip(
                    )[2:
                      -1] not in speciesDict:  # stripping spaces, '(+' and ')'
                        raise DatabaseError(
                            'Collider species {0} in kinetics library {1} is missing from its dictionary.'
                            .format(collider.strip()[2:-1], self.label))
                    specificCollider = speciesDict[collider.strip()[2:-1]]

            for reactant in reactants.split('+'):
                reactant = reactant.strip()
                if reactant not in speciesDict:
                    raise DatabaseError(
                        'Species {0} in kinetics depository {1} is missing from its dictionary.'
                        .format(reactant, self.label))
                # For some reason we need molecule objects in the depository rather than species objects
                rxn.reactants.append(speciesDict[reactant])
            for product in products.split('+'):
                product = product.strip()
                if product not in speciesDict:
                    raise DatabaseError(
                        'Species {0} in kinetics depository {1} is missing from its dictionary.'
                        .format(product, self.label))
                # For some reason we need molecule objects in the depository rather than species objects
                rxn.products.append(speciesDict[product])

            if not rxn.isBalanced():
                raise DatabaseError(
                    'Reaction {0} in kinetics depository {1} was not balanced! Please reformulate.'
                    .format(rxn, self.label))
Exemplo n.º 15
0
    def saveOld(self, path):
        """
        Save an old-style reaction library to `path`. This creates files named
        ``species.txt``, ``reactions.txt``, and ``pdepreactions.txt`` in the
        given directory; these contain the species dictionary, high-pressure
        limit reactions and kinetics, and pressure-dependent reactions and
        kinetics, respectively.
        """
        try:
            os.makedirs(path)
        except OSError:
            pass

        def writeArrhenius(f, arrhenius):
            f.write(
                ' {0:<12.3E} {1:>7.3f} {2:>11.2f}    {3}{4:g} {5:g} {6:g}\n'.
                format(
                    arrhenius.A.value_si,
                    arrhenius.n.value_si,
                    arrhenius.Ea.value_si / 4.184,
                    '*' if arrhenius.A.isUncertaintyMultiplicative() else '',
                    arrhenius.A.uncertainty,
                    arrhenius.n.uncertainty,
                    arrhenius.Ea.uncertainty / 4.184,
                ))

        # Gather all of the species used in this kinetics library
        speciesDict = self.getSpecies()
        # Also include colliders in the above
        for entry in self.entries.values():
            if isinstance(entry.data, ThirdBody):
                for molecule in entry.data.efficiencies:
                    formula = molecule.getFormula()
                    if formula in ['He', 'Ar', 'N2', 'Ne']:
                        pass
                    else:
                        found = False
                        for species in speciesDict.values():
                            for mol in species.molecule:
                                if mol.isIsomorphic(molecule):
                                    found = True
                                    break
                        if not found:
                            speciesDict[formula] = Species(label=formula,
                                                           molecule=[molecule])

        entries = self.entries.values()
        entries.sort(key=lambda x: x.index)

        # Save the species dictionary
        speciesList = speciesDict.values()
        speciesList.sort(key=lambda x: x.label)
        f = open(os.path.join(path, 'species.txt'), 'w')
        for species in speciesList:
            f.write(species.molecule[0].toAdjacencyList(label=species.label,
                                                        removeH=False) + "\n")
        f.close()

        # Save the high-pressure limit reactions
        # Currently only Arrhenius kinetics are allowed
        f = open(os.path.join(path, 'reactions.txt'), 'w')
        f.write('Unit:\n')
        f.write('A: mol/m3/s\n')
        f.write('E: cal/mol\n\n')
        f.write('Reactions:\n')
        for entry in entries:
            kinetics = entry.data
            rateList = []
            if isinstance(kinetics, MultiArrhenius):
                entry.item.duplicate = True
                rateList = kinetics.arrhenius[:]
            else:
                if not kinetics.isPressureDependent():
                    rateList.append(kinetics)
            for rate in rateList:
                # Write reaction equation
                f.write('{0:<59}'.format(entry.item))
                # Write kinetics
                if isinstance(rate, Arrhenius):
                    writeArrhenius(f, rate)
                else:
                    raise DatabaseError(
                        'Unexpected kinetics type "{0}" encountered while saving old kinetics library (reactions.txt).'
                        .format(rate.__class__))
                # Mark as duplicate if needed
                if entry.item.duplicate:
                    f.write(' DUPLICATE\n')
        f.close()

        # Save the pressure-dependent reactions
        # Currently only ThirdBody, Lindemann, Troe, and PDepArrhenius kinetics are allowed
        f = open(os.path.join(path, 'pdepreactions.txt'), 'w')
        f.write('Unit:\n')
        f.write('A: mol/m3/s\n')
        f.write('E: cal/mol\n\n')
        f.write('Reactions:\n')
        for entry in entries:
            kinetics = entry.data
            if not kinetics.isPressureDependent():
                continue
            rateList = []
            if isinstance(kinetics, MultiPDepArrhenius):
                entry.item.duplicate = True
                rateList = kinetics.arrhenius[:]
            else:
                rateList.append(kinetics)
            for rate in rateList:
                # Write reaction equation
                equation = str(entry.item)
                if entry.item.reversible:
                    index = equation.find('<=>')
                else:
                    index = equation.find('=>')
                if isinstance(rate,
                              ThirdBody) and not isinstance(rate, Lindemann):
                    equation = '{0}+ M {1} + M'.format(equation[0:index],
                                                       equation[index:])
                elif isinstance(rate, PDepArrhenius):
                    pass
                else:
                    equation = '{0}(+M) {1} (+M)'.format(
                        equation[0:index], equation[index:])
                f.write('{0:<59}'.format(equation))
                # Write kinetics
                if isinstance(rate, (ThirdBody, Lindemann, Troe)):
                    if isinstance(rate, Lindemann):
                        # Lindemann (and Troe) fall-off have the High-P as default, and Low-P labeled LOW
                        writeArrhenius(f, rate.arrheniusHigh)
                    else:
                        # Non-falloff ThirdBody reactions are always in the Low-P limit
                        writeArrhenius(f, rate.arrheniusLow)
                    if len(rate.efficiencies) > 0:
                        eff_line = ''
                        for molecule, efficiency in rate.efficiencies.iteritems(
                        ):
                            for spec in speciesDict.values():
                                if molecule in spec.molecule:
                                    mol_label = spec.label
                                    break
                            else:
                                mol_label = molecule.getFormula().upper()
                            eff_line += '{0}/{1:g}/  '.format(
                                mol_label, efficiency)
                        f.write(eff_line.strip() + '\n')
                    if isinstance(rate, Lindemann):
                        f.write('     LOW  /  {0:10.3e} {1:9.3f} {2:10.2f}/\n'.
                                format(
                                    rate.arrheniusLow.A.value_si,
                                    rate.arrheniusLow.n.value_si,
                                    rate.arrheniusLow.Ea.value_si / 4.184,
                                ))
                    if isinstance(rate, Troe):
                        if rate.T2 is not None:
                            f.write(
                                '     TROE /  {0:10.4f} {1:10.2g} {2:10.2g} {3:10.2g}/\n'
                                .format(
                                    rate.alpha,
                                    rate.T3.value_si,
                                    rate.T1.value_si,
                                    rate.T2.value_si,
                                ))
                        else:
                            f.write(
                                '     TROE /  {0:10.4f} {1:10.2g} {2:10.2g}/\n'
                                .format(
                                    rate.alpha,
                                    rate.T3.value_si,
                                    rate.T1.value_si,
                                ))

                elif isinstance(rate, PDepArrhenius):
                    writeArrhenius(f, rate.arrhenius[-1])
                    for pressure, arrhenius in zip(rate.pressures.value_si,
                                                   rate.arrhenius):
                        f.write(
                            '     PLOG /  {0:10g} {1:10.3e} {2:9.3f} {3:10.2f} /\n'
                            .format(
                                pressure / 1e5,
                                arrhenius.A.value_si,
                                arrhenius.n.value_si,
                                arrhenius.Ea.value_si / 4.184,
                            ))
                else:
                    raise DatabaseError(
                        'Unexpected kinetics type "{0}" encountered while saving old kinetics library (reactions.txt).'
                        .format(rate.__class__))
                # Mark as duplicate if needed
                if entry.item.duplicate:
                    f.write(' DUPLICATE\n')
                f.write('\n')
        f.close()
Exemplo n.º 16
0
    def loadFamilies(self, path, families=None, depositories=None):
        """
        Load the kinetics families from the given `path` on disk, where `path`
        points to the top-level folder of the kinetics families.
        """

        familiesToLoad = []
        for (root, dirs, files) in os.walk(os.path.join(path)):
            if root == path:
                break  # all we wanted was the list of dirs in the base path

        if families == 'default':
            logging.info(
                'Loading default kinetics families from {0}'.format(path))
            for d in dirs:  # load them in directory listing order, like other methods (better than a random dict order)
                try:
                    recommended = self.recommendedFamilies[d]
                except KeyError:
                    raise DatabaseError(
                        'Family {0} not found in recommendation list (probably at {1}/recommended.py)'
                        .format(d, path))
                if recommended:
                    familiesToLoad.append(d)
            for label, value in self.recommendedFamilies.iteritems():
                if label not in dirs:
                    raise DatabaseError(
                        'Family {0} found (in {1}/recommended.py) not found on disk.'
                        .format(label, path))

        elif families == 'all':
            # All families are loaded
            logging.info(
                'Loading all of the kinetics families from {0}'.format(path))
            for d in dirs:
                familiesToLoad.append(d)
        elif families == 'none':
            logging.info(
                'Not loading any of the kinetics families from {0}'.format(
                    path))
            # Don't load any of the families
            familiesToLoad = []
        elif isinstance(families, list):
            logging.info(
                'Loading the user-specified kinetics families from {0}'.format(
                    path))
            # If all items in the list start with !, all families will be loaded except these
            if len(families) == 0:
                raise DatabaseError(
                    'Kinetics families should be a non-empty list, or set to `default`, `all`, or `none`.'
                )
            elif all([label.startswith('!') for label in families]):
                for d in dirs:
                    if '!{0}'.format(d) not in families:
                        familiesToLoad.append(d)
            elif any([label.startswith('!') for label in families]):
                raise DatabaseError(
                    'Families list must either all or none have prefix "!", but not a mix.'
                )
            else:  # only the families given will be loaded
                for d in dirs:
                    if d in families:
                        familiesToLoad.append(d)
                for label in families:
                    if label not in dirs:
                        raise DatabaseError(
                            'Family {0} not found on disk.'.format(label))
        else:
            raise DatabaseError(
                'Kinetics families was not specified properly.  Should be set to `default`,`all`,`none`, or a list.'
            )

        # Now we know what families to load, so let's load them
        self.families = {}
        for label in familiesToLoad:
            familyPath = os.path.join(path, label)
            family = KineticsFamily(label=label)
            family.load(familyPath,
                        self.local_context,
                        self.global_context,
                        depositoryLabels=depositories)
            self.families[label] = family
Exemplo n.º 17
0
    def load(self, path, local_context=None, global_context=None):
        import os
        Database.load(self, path, local_context, global_context)

        # Load the species in the kinetics library
        # Do not generate resonance structures, since training reactions may be written for a specific resonance form
        species_dict = self.get_species(os.path.join(os.path.dirname(path),
                                                     'dictionary.txt'),
                                        resonance=False)
        # Make sure all of the reactions draw from only this set
        entries = self.entries.values()
        for entry in entries:
            # Create a new reaction per entry
            rxn = entry.item
            rxn_string = entry.label
            # Convert the reactants and products to Species objects using the species_dict
            reactants, products = rxn_string.split('=')
            reversible = True
            if '<=>' in rxn_string:
                reactants = reactants[:-1]
                products = products[1:]
            elif '=>' in rxn_string:
                products = products[1:]
                reversible = False
            if reversible != rxn.reversible:
                raise DatabaseError(
                    'Reaction string reversibility ({0}) and entry attribute `reversible` ({1}) '
                    'must agree if reaction is irreversible.'.format(
                        rxn.reversible, reversible))

            specific_collider = None
            collider = re.search(r'\(\+[^\)]+\)', reactants)
            if collider is not None:
                collider = collider.group(
                    0)  # save string value rather than the object
                if collider != re.search(r'\(\+[^\)]+\)', products).group(0):
                    raise ValueError(
                        'Third body colliders in reaction {0} in kinetics library {1} are not identical!'
                        ''.format(rxn_string, self.label))
                extra_parenthesis = collider.count('(') - 1
                for i in range(extra_parenthesis):
                    # allow for species like N2(5) or CH2(T)(15) to be read as specific colliders,
                    # although currently not implemented in Chemkin. See RMG-Py #1070
                    collider += ')'
                reactants = reactants.replace(collider, '')
                products = products.replace(collider, '')
                if collider.upper().strip(
                ) != "(+M)":  # the collider is a specific species, not (+M) or (+m)
                    if collider.strip(
                    )[2:
                      -1] not in species_dict:  # stripping spaces, '(+' and ')'
                        raise DatabaseError(
                            'Collider species {0} in kinetics library {1} is missing from its '
                            'dictionary.'.format(collider.strip()[2:-1],
                                                 self.label))
                    specific_collider = species_dict[collider.strip()[2:-1]]

            for reactant in reactants.split('+'):
                reactant = reactant.strip()
                if reactant not in species_dict:
                    raise DatabaseError(
                        'Species {0} in kinetics depository {1} is missing from its dictionary.'
                        ''.format(reactant, self.label))
                # Depository reactions should have molecule objects because they are needed in order to descend the
                # tree using `get_reaction_template()` later, but species objects work because `get_reaction_template()`
                # will simply pick the first molecule object in `Species().molecule`.
                rxn.reactants.append(species_dict[reactant])
            for product in products.split('+'):
                product = product.strip()
                if product not in species_dict:
                    raise DatabaseError(
                        'Species {0} in kinetics depository {1} is missing from its dictionary.'
                        ''.format(product, self.label))
                # Same comment about molecule vs species objects as above.
                rxn.products.append(species_dict[product])

            if not rxn.is_balanced():
                raise DatabaseError(
                    'Reaction {0} in kinetics depository {1} was not balanced! Please reformulate.'
                    ''.format(rxn, self.label))