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')
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))
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
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(), )
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))
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))
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
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')
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" )
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')
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()
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
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))
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()
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
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))