def generate_isotope_reactions(isotopeless_reactions, isotopes): """ Find the list of isotope reactions based on the reactions in the isotopeless reaction. uses the reactSpecies method to find reactions with proper degeneracies and then filters out those that don't match products. the proper reactions are given kinetics of the previous reaction modified for the degeneracy difference. """ # make sure all isotopeless reactions have templates and are TemplateReaction objects for rxn in isotopeless_reactions: if not isinstance(rxn, TemplateReaction): raise TypeError( 'reactions sent to generate_isotope_reactions must be a TemplateReaction object' ) if rxn.template is None: raise AttributeError( 'isotope reaction {0} does not have a template attribute. ' 'The object is:\n\n{1}'.format(str(rxn), repr(rxn))) found_reactions = [] rxn_index = 0 while rxn_index < len(isotopeless_reactions): rxn = isotopeless_reactions[rxn_index] # find all reactions involving same reactants rxns_w_same_reactants = [rxn] rxn_index2 = rxn_index + 1 while rxn_index2 < len(isotopeless_reactions): if same_species_lists( isotopeless_reactions[rxn_index].reactants, isotopeless_reactions[rxn_index2].reactants, ): rxns_w_same_reactants.append(isotopeless_reactions[rxn_index2]) del isotopeless_reactions[rxn_index2] else: rxn_index2 += 1 ##### find all pairs of reacting isotoper species ##### # find the lists of reactants that have identical isotopomers reactants = [] for reactant in rxn.reactants: for iso_index, isotopomers in enumerate(isotopes): if compare_isotopomers(reactant, isotopomers[0]): reactants.append(iso_index) break # find pairs of all reactants to react together reactant_pairs = [] if len(rxn.reactants) == 1: reactant_pairs = [[spec] for spec in isotopes[reactants[0]]] elif len(rxn.reactants) == 2: for spec1 in isotopes[reactants[0]]: for spec2 in isotopes[reactants[1]]: reactant_pairs.append([spec1, spec2]) else: raise ValueError('Cannot process reactions with over 2 reactants') # remove identical pairs rxn_index3 = 0 while rxn_index3 < len(reactant_pairs): rxn_index4 = rxn_index3 + 1 while rxn_index4 < len(reactant_pairs): if same_species_lists(reactant_pairs[rxn_index3], reactant_pairs[rxn_index4]): del reactant_pairs[rxn_index4] else: rxn_index4 += 1 rxn_index3 += 1 # make reaction objects for pair in reactant_pairs: # copy species so they don't get modified species_tuple = tuple([spc.copy(deep=True) for spc in pair]) unfiltered_rxns = get_db( 'kinetics').generate_reactions_from_families( species_tuple, only_families=[rxn.family]) # remove reactions whose products don't match the original reactions rxn_index5 = 0 while rxn_index5 < len(unfiltered_rxns): for isotopeless_reaction in rxns_w_same_reactants: isotopeless_kinetics = isotopeless_reaction.kinetics isotopeless_degeneracy = isotopeless_reaction.degeneracy if compare_isotopomers(isotopeless_reaction, unfiltered_rxns[rxn_index5], either_direction=False) \ and isotopeless_reaction.family == unfiltered_rxns[rxn_index5].family \ and frozenset(isotopeless_reaction.template) == \ frozenset(unfiltered_rxns[rxn_index5].template): # apply kinetics to new reaction & modify for degeneracy unfiltered_rxns[rxn_index5].kinetics = deepcopy( isotopeless_kinetics) unfiltered_rxns[rxn_index5].kinetics.change_rate( unfiltered_rxns[rxn_index5].degeneracy / isotopeless_degeneracy) rxn_index5 += 1 break else: # did not find same prodcuts del unfiltered_rxns[rxn_index5] found_reactions.extend(unfiltered_rxns) rxn_index += 1 return found_reactions
def reactionHasReactants(reaction, reactants): """ Return ``True`` if the given `reaction` has all of the specified `reactants` (and no others), or ``False if not. """ return same_species_lists(reaction.reactants, reactants, strict=False)
def get_forward_reaction_for_family_entry(self, entry, family, thermo_database): """ For a given `entry` for a reaction of the given reaction `family` (the string label of the family), return the reaction with kinetics and degeneracy for the "forward" direction as defined by the reaction family. For families that are their own reverse, the direction the kinetics is given in will be preserved. If the entry contains functional groups for the reactants, assume that it is given in the forward direction and do nothing. Returns the reaction in the direction consistent with the reaction family template, and the matching template. Note that the returned reaction will have its kinetics and degeneracy set appropriately. In order to reverse the reactions that are given in the reverse of the direction the family is defined, we need to compute the thermodynamics of the reactants and products. For this reason you must also pass the `thermo_database` to use to generate the thermo data. """ reaction = None template = None # Get the indicated reaction family try: groups = self.families[family].groups except KeyError: raise ValueError( 'Invalid value "{0}" for family parameter.'.format(family)) if all([(isinstance(reactant, Group) or isinstance(reactant, LogicNode)) for reactant in entry.item.reactants]): # The entry is a rate rule, containing functional groups only # By convention, these are always given in the forward direction and # have kinetics defined on a per-site basis reaction = Reaction( reactants=entry.item.reactants[:], products=[], specific_collider=entry.item.specific_collider, kinetics=entry.data, degeneracy=1, ) template = [ groups.entries[label] for label in entry.label.split(';') ] elif (all([ isinstance(reactant, Molecule) for reactant in entry.item.reactants ]) and all( [isinstance(product, Molecule) for product in entry.item.products])): # The entry is a real reaction, containing molecules # These could be defined for either the forward or reverse direction # and could have a reaction-path degeneracy reaction = Reaction(reactants=[], products=[]) for molecule in entry.item.reactants: reactant = Species(molecule=[molecule]) reactant.generate_resonance_structures() reactant.thermo = thermo_database.get_thermo_data(reactant) reaction.reactants.append(reactant) for molecule in entry.item.products: product = Species(molecule=[molecule]) product.generate_resonance_structures() product.thermo = thermo_database.get_thermo_data(product) reaction.products.append(product) # Generate all possible reactions involving the reactant species generated_reactions = self.generate_reactions_from_families( [reactant.molecule for reactant in reaction.reactants], [], only_families=[family]) # Remove from that set any reactions that don't produce the desired reactants and products forward = [] reverse = [] for rxn in generated_reactions: if (same_species_lists(reaction.reactants, rxn.reactants) and same_species_lists(reaction.products, rxn.products)): forward.append(rxn) if (same_species_lists(reaction.reactants, rxn.products) and same_species_lists(reaction.products, rxn.reactants)): reverse.append(rxn) # We should now know whether the reaction is given in the forward or # reverse direction if len(forward) == 1 and len(reverse) == 0: # The reaction is in the forward direction, so use as-is reaction = forward[0] template = reaction.template # Don't forget to overwrite the estimated kinetics from the database with the kinetics for this entry reaction.kinetics = entry.data elif len(reverse) == 1 and len(forward) == 0: # The reaction is in the reverse direction # First fit Arrhenius kinetics in that direction T_data = 1000.0 / np.arange(0.5, 3.301, 0.1, np.float64) k_data = np.zeros_like(T_data) for i in range(T_data.shape[0]): k_data[i] = entry.data.get_rate_coefficient( T_data[i]) / reaction.get_equilibrium_constant( T_data[i]) try: k_units = ('s^-1', 'm^3/(mol*s)', 'm^6/(mol^2*s)')[len(reverse[0].reactants) - 1] except IndexError: raise NotImplementedError( 'Cannot reverse reactions with {} products'.format( len(reverse[0].reactants))) kinetics = Arrhenius().fit_to_data(T_data, k_data, k_units, T0=1.0) kinetics.Tmin = entry.data.Tmin kinetics.Tmax = entry.data.Tmax kinetics.Pmin = entry.data.Pmin kinetics.Pmax = entry.data.Pmax # Now flip the direction reaction = reverse[0] reaction.kinetics = kinetics template = reaction.template elif len(reverse) > 0 and len(forward) > 0: print('FAIL: Multiple reactions found for {0!r}.'.format( entry.label)) elif len(reverse) == 0 and len(forward) == 0: print('FAIL: No reactions found for "%s".' % (entry.label)) else: print('FAIL: Unable to estimate kinetics for {0!r}.'.format( entry.label)) assert reaction is not None assert template is not None return reaction, template
def loop_families(rmgdb, reaction): """ Loop through kinetic families and return a list of tuples of (family, degenerate_reactions) `reaction` is an RMG Reaction object. Returns a list of (family, degenerate_reactions) tuples. Args: rmgdb (RMGDatabase): The RMG database instance. reaction (Reaction): The RMG Reaction object. Returns: list Entries are corresponding RMG reaction families. """ reaction = reaction.copy( ) # use a copy to avoid changing atom order in the molecules by RMG fam_list = list() for family in rmgdb.kinetics.families.values(): degenerate_reactions = list() family_reactions_by_r = list( ) # family reactions for the specified reactants family_reactions_by_rnp = list( ) # family reactions for the specified reactants and products if len(reaction.reactants) == 1: for reactant0 in reaction.reactants[0].molecule: fam_rxn = family.generate_reactions(reactants=[reactant0], products=reaction.products) if fam_rxn: family_reactions_by_r.extend(fam_rxn) elif len(reaction.reactants) == 2: for reactant0 in reaction.reactants[0].molecule: for reactant1 in reaction.reactants[1].molecule: fam_rxn = family.generate_reactions( reactants=[reactant0, reactant1], products=reaction.products) if fam_rxn: family_reactions_by_r.extend(fam_rxn) elif len(reaction.reactants) == 3: for reactant0 in reaction.reactants[0].molecule: for reactant1 in reaction.reactants[1].molecule: for reactant2 in reaction.reactants[2].molecule: fam_rxn = family.generate_reactions( reactants=[reactant0, reactant1, reactant2], products=reaction.products) if fam_rxn: family_reactions_by_r.extend(fam_rxn) if len(reaction.products) == 1: for fam_rxn in family_reactions_by_r: for product0 in reaction.products[0].molecule: if same_species_lists([product0], fam_rxn.products): family_reactions_by_rnp.append(fam_rxn) degenerate_reactions = find_degenerate_reactions( rxn_list=family_reactions_by_rnp, same_reactants=False, kinetics_database=rmgdb.kinetics) elif len(reaction.products) == 2: for fam_rxn in family_reactions_by_r: for product0 in reaction.products[0].molecule: for product1 in reaction.products[1].molecule: if same_species_lists([product0, product1], fam_rxn.products): family_reactions_by_rnp.append(fam_rxn) degenerate_reactions = find_degenerate_reactions( rxn_list=family_reactions_by_rnp, same_reactants=False, kinetics_database=rmgdb.kinetics) elif len(reaction.products) == 3: for fam_rxn in family_reactions_by_r: for product0 in reaction.products[0].molecule: for product1 in reaction.products[1].molecule: for product2 in reaction.products[2].molecule: if same_species_lists( [product0, product1, product2], fam_rxn.products): family_reactions_by_rnp.append(fam_rxn) degenerate_reactions = find_degenerate_reactions( rxn_list=family_reactions_by_rnp, same_reactants=False, kinetics_database=rmgdb.kinetics) if degenerate_reactions: fam_list.append((family, degenerate_reactions)) return fam_list
def loop_families(rmgdb, reaction): """ Loop through kinetic families and return a list of tuples of (family, degenerate_reactions) `reaction` is an RMG Reaction object. Returns a list of (family, degenerate_reactions) tuples. """ fam_list = list() for family in rmgdb.kinetics.families.values(): degenerate_reactions = list() family_reactions_by_r = list() # family reactions for the specified reactants family_reactions_by_rnp = list() # family reactions for the specified reactants and products if len(reaction.reactants) == 1: for reactant0 in reaction.reactants[0].molecule: fam_rxn = family.generateReactions(reactants=[reactant0], products=reaction.products) if fam_rxn: family_reactions_by_r.extend(fam_rxn) elif len(reaction.reactants) == 2: for reactant0 in reaction.reactants[0].molecule: for reactant1 in reaction.reactants[1].molecule: fam_rxn = family.generateReactions(reactants=[reactant0, reactant1], products=reaction.products) if fam_rxn: family_reactions_by_r.extend(fam_rxn) elif len(reaction.reactants) == 3: for reactant0 in reaction.reactants[0].molecule: for reactant1 in reaction.reactants[1].molecule: for reactant2 in reaction.reactants[2].molecule: fam_rxn = family.generateReactions(reactants=[reactant0, reactant1, reactant2], products=reaction.products) if fam_rxn: family_reactions_by_r.extend(fam_rxn) if len(reaction.products) == 1: for fam_rxn in family_reactions_by_r: for product0 in reaction.products[0].molecule: if same_species_lists([product0], fam_rxn.products): family_reactions_by_rnp.append(fam_rxn) degenerate_reactions = find_degenerate_reactions(rxn_list=family_reactions_by_rnp, same_reactants=False, kinetics_database=rmgdb.kinetics) elif len(reaction.products) == 2: for fam_rxn in family_reactions_by_r: for product0 in reaction.products[0].molecule: for product1 in reaction.products[1].molecule: if same_species_lists([product0, product1], fam_rxn.products): family_reactions_by_rnp.append(fam_rxn) degenerate_reactions = find_degenerate_reactions(rxn_list=family_reactions_by_rnp, same_reactants=False, kinetics_database=rmgdb.kinetics) elif len(reaction.products) == 3: for fam_rxn in family_reactions_by_r: for product0 in reaction.products[0].molecule: for product1 in reaction.products[1].molecule: for product2 in reaction.products[2].molecule: if same_species_lists([product0, product1, product2], fam_rxn.products): family_reactions_by_rnp.append(fam_rxn) degenerate_reactions = find_degenerate_reactions(rxn_list=family_reactions_by_rnp, same_reactants=False, kinetics_database=rmgdb.kinetics) if degenerate_reactions: fam_list.append((family, degenerate_reactions)) return fam_list
def loop_families( rmgdb: RMGDatabase, reaction: Reaction, ) -> List[Tuple['KineticsFamily', list]]: """ Loop through kinetic families and return a list of tuples of (family, degenerate_reactions) corresponding to ``reaction``. Args: rmgdb (RMGDatabase): The RMG database instance. reaction (Reaction): The RMG Reaction object instance. Returns: List[Tuple['KineticsFamily', list]] Entries are tuples of a corresponding RMG KineticsFamily instance and a list of degenerate reactions. """ reaction = reaction.copy( ) # Use a copy to avoid changing atom order in the molecules by RMG. for spc in reaction.reactants + reaction.products: spc.generate_resonance_structures(save_order=True) fam_list = list() for family in rmgdb.kinetics.families.values(): family.save_order = True degenerate_reactions = list() family_reactions_by_r = list( ) # Family reactions for the specified reactants. family_reactions_by_rnp = list( ) # Family reactions for the specified reactants and products. if len(reaction.reactants) == 1: for reactant0 in reaction.reactants[0].molecule: fam_rxn = family.generate_reactions( reactants=[reactant0], products=reaction.products, delete_labels=False, ) if fam_rxn: family_reactions_by_r.extend(fam_rxn) elif len(reaction.reactants) == 2: for reactant0 in reaction.reactants[0].molecule: for reactant1 in reaction.reactants[1].molecule: fam_rxn = family.generate_reactions( reactants=[reactant0, reactant1], products=reaction.products, delete_labels=False, ) if fam_rxn: family_reactions_by_r.extend(fam_rxn) elif len(reaction.reactants) == 3: for reactant0 in reaction.reactants[0].molecule: for reactant1 in reaction.reactants[1].molecule: for reactant2 in reaction.reactants[2].molecule: fam_rxn = family.generate_reactions( reactants=[reactant0, reactant1, reactant2], products=reaction.products, delete_labels=False, ) if fam_rxn: family_reactions_by_r.extend(fam_rxn) if len(reaction.products) == 1: for fam_rxn in family_reactions_by_r: for product0 in reaction.products[0].molecule: if same_species_lists([product0], fam_rxn.products, save_order=True): family_reactions_by_rnp.append(fam_rxn) degenerate_reactions = find_degenerate_reactions( rxn_list=family_reactions_by_rnp, same_reactants=False, kinetics_database=rmgdb.kinetics, save_order=True) elif len(reaction.products) == 2: for fam_rxn in family_reactions_by_r: for product0 in reaction.products[0].molecule: for product1 in reaction.products[1].molecule: if same_species_lists([product0, product1], fam_rxn.products, save_order=True): family_reactions_by_rnp.append(fam_rxn) degenerate_reactions = find_degenerate_reactions( rxn_list=family_reactions_by_rnp, same_reactants=False, kinetics_database=rmgdb.kinetics, save_order=True) elif len(reaction.products) == 3: for fam_rxn in family_reactions_by_r: for product0 in reaction.products[0].molecule: for product1 in reaction.products[1].molecule: for product2 in reaction.products[2].molecule: if same_species_lists( [product0, product1, product2], fam_rxn.products, save_order=True): family_reactions_by_rnp.append(fam_rxn) degenerate_reactions = find_degenerate_reactions( rxn_list=family_reactions_by_rnp, same_reactants=False, kinetics_database=rmgdb.kinetics, save_order=True) if degenerate_reactions: fam_list.append((family, degenerate_reactions)) return fam_list