def test_rmg_reaction(self): test_reaction = RMGReaction( reactants=[RMGMolecule(smiles="CC"), RMGMolecule(smiles="[O]O")], products=[RMGMolecule(smiles="[CH2]C"), RMGMolecule(smiles="OO")]) self.assertTrue( test_reaction.is_isomorphic(self.reaction.get_rmg_reaction())) self.assertTrue( test_reaction.is_isomorphic(self.reaction2.get_rmg_reaction()))
def test_remove_isotope_for_reactions(self): """ Test that remove isotope algorithm works with Reaction objects. """ eth = Species().from_adjacency_list(""" 1 C u0 p0 c0 {2,S} {3,S} {4,S} {5,S} 2 C u0 p0 c0 {1,S} {6,S} {7,S} {8,S} 3 H u0 p0 c0 {1,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {1,S} 6 H u0 p0 c0 {2,S} 7 H u0 p0 c0 {2,S} 8 H u0 p0 c0 {2,S} """) ethi = Species().from_adjacency_list(""" 1 C u0 p0 c0 {2,S} {3,S} {4,S} {5,S} 2 C u0 p0 c0 i13 {1,S} {6,S} {7,S} {8,S} 3 H u0 p0 c0 {1,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {1,S} 6 H u0 p0 c0 {2,S} 7 H u0 p0 c0 {2,S} 8 H u0 p0 c0 {2,S} """) unlabeled_rxn = Reaction(reactants=[eth], products=[eth]) labeled_rxn = Reaction(reactants=[ethi], products=[ethi]) stripped = remove_isotope(labeled_rxn) self.assertTrue(unlabeled_rxn.is_isomorphic(stripped))
def determine_rmg_kinetics( rmgdb: RMGDatabase, reaction: Reaction, dh_rxn298: float, ) -> List[Reaction]: """ Determine kinetics for `reaction` (an RMG Reaction object) from RMG's database, if possible. Assigns a list of all matching entries from both libraries and families. Args: rmgdb (RMGDatabase): The RMG database instance. reaction (Reaction): The RMG Reaction object. dh_rxn298 (float): The heat of reaction at 298 K in J/mol. Returns: List[Reaction] All matching RMG reactions (both libraries and families) with a populated ``.kinetics`` attribute. """ reaction = reaction.copy( ) # use a copy to avoid changing atom order in the molecules by RMG rmg_reactions = list() # Libraries: for library in rmgdb.kinetics.libraries.values(): library_reactions = library.get_library_reactions() for library_reaction in library_reactions: if reaction.is_isomorphic(library_reaction): library_reaction.comment = f'Library: {library.label}' rmg_reactions.append(library_reaction) break # Families: fam_list = loop_families(rmgdb, reaction) for family, degenerate_reactions in fam_list: for deg_rxn in degenerate_reactions: template = family.retrieve_template(deg_rxn.template) kinetics = family.estimate_kinetics_using_rate_rules(template)[0] kinetics.change_rate(deg_rxn.degeneracy) kinetics = kinetics.to_arrhenius( dh_rxn298 ) # Convert ArrheniusEP to Arrhenius using the dHrxn at 298K deg_rxn.kinetics = kinetics deg_rxn.comment = f'Family: {deg_rxn.family}' deg_rxn.reactants = reaction.reactants deg_rxn.products = reaction.products rmg_reactions.extend(degenerate_reactions) worked_through_nist_fams = [] # NIST: for family, degenerate_reactions in fam_list: if family not in worked_through_nist_fams: worked_through_nist_fams.append(family) for depo in family.depositories: if 'NIST' in depo.label: for entry in depo.entries.values(): rxn = entry.item rxn.kinetics = entry.data rxn.comment = f'NIST: {entry.index}' if entry.reference is not None: rxn.comment += f'{entry.reference.authors[0]} {entry.reference.year}' rmg_reactions.append(rxn) return rmg_reactions
def test_labeled_reaction(self): test_reaction = RMGReaction( reactants=[RMGMolecule(smiles="CC"), RMGMolecule(smiles="[O]O")], products=[RMGMolecule(smiles="[CH2]C"), RMGMolecule(smiles="OO")]) labeled_reaction, reaction_family = self.reaction.get_labeled_reaction( ) self.assertEquals(reaction_family.lower(), "h_abstraction") self.assertTrue(test_reaction.is_isomorphic(labeled_reaction)) merged = labeled_reaction.reactants[0].merge( labeled_reaction.reactants[1]) self.assertTrue(merged.get_labeled_atoms("*1")[0].is_carbon()) self.assertTrue(merged.get_labeled_atoms("*2")[0].is_hydrogen()) self.assertTrue(merged.get_labeled_atoms("*3")[0].is_oxygen) merged = labeled_reaction.products[0].merge( labeled_reaction.products[1]) self.assertTrue(merged.get_labeled_atoms("*3")[0].is_carbon()) self.assertTrue(merged.get_labeled_atoms("*2")[0].is_hydrogen()) self.assertTrue(merged.get_labeled_atoms("*1")[0].is_oxygen) labeled_reaction, reaction_family = self.reaction2.get_labeled_reaction( ) self.assertEquals(reaction_family.lower(), "h_abstraction") self.assertTrue(test_reaction.is_isomorphic(labeled_reaction)) merged = labeled_reaction.reactants[0].merge( labeled_reaction.reactants[1]) self.assertTrue(merged.get_labeled_atoms("*1")[0].is_carbon()) self.assertTrue(merged.get_labeled_atoms("*2")[0].is_hydrogen()) self.assertTrue(merged.get_labeled_atoms("*3")[0].is_oxygen) merged = labeled_reaction.products[0].merge( labeled_reaction.products[1]) self.assertTrue(merged.get_labeled_atoms("*3")[0].is_carbon()) self.assertTrue(merged.get_labeled_atoms("*2")[0].is_hydrogen()) self.assertTrue(merged.get_labeled_atoms("*1")[0].is_oxygen)
def test_inplace_remove_isotope_for_reactions(self): """ Test that removeIsotope and redoIsotope works with reactions """ eth = Species().from_adjacency_list(""" 1 C u0 p0 c0 {2,S} {3,S} {4,S} {5,S} 2 C u0 p0 c0 {1,S} {6,S} {7,S} {8,S} 3 H u0 p0 c0 {1,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {1,S} 6 H u0 p0 c0 {2,S} 7 H u0 p0 c0 {2,S} 8 H u0 p0 c0 {2,S} """) ethi = Species().from_adjacency_list(""" 1 C u0 p0 c0 {2,S} {3,S} {4,S} {5,S} 2 C u0 p0 c0 i13 {1,S} {6,S} {7,S} {8,S} 3 H u0 p0 c0 {1,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {1,S} 6 H u0 p0 c0 {2,S} 7 H u0 p0 c0 {2,S} 8 H u0 p0 c0 {2,S} """) unlabeled_rxn = Reaction(reactants=[eth], products=[eth]) labeled_rxn = Reaction(reactants=[ethi], products=[ethi]) stored_labeled_rxn = labeled_rxn.copy() modified_atoms = remove_isotope(labeled_rxn, inplace=True) self.assertTrue(unlabeled_rxn.is_isomorphic(labeled_rxn)) redo_isotope(modified_atoms) self.assertTrue(stored_labeled_rxn.is_isomorphic(labeled_rxn))
def get_labeled_reaction(self): """ A method that will return a labeled reaction given a reaction label or rmg_reaction A label or an rmg_reaction needs to be provided in order for this method to work. If both are provided, we assert that the label matches the reaction. Variables: - label (str): the reaction label of interest - rmg_reaction (RMGReaction): the reaction of interest Returns: - reaction (RMGReaction): An RMGReaction with labeled reactants and products - name (str): The string corresponding to the reaction family matched to the reaction of interest """ if not (self.rmg_database or self.ts_databases): self.rmg_database, self.ts_databases = self.load_databases() assert (self.label or self.rmg_reaction ), "You must provide a reaction or a reaction label" match = False # We have not found a labeled reaction that matches the one provided. if self.label: # A reaction label was provided # Generating lists of lists. Main list is the reactans or products # Secondary list is composed of the resonance structures for that species r, p = self.label.split("_") rmg_reactants = [ RMGMolecule(smiles=smile).generate_resonance_structures() for smile in r.split("+") ] rmg_products = [ RMGMolecule(smiles=smile).generate_resonance_structures() for smile in p.split("+") ] combos_to_try = list( itertools.product(list(itertools.product(*rmg_reactants)), list(itertools.product(*rmg_products)))) # looping though each reaction family and each combination of reactants and products for name, family in list( self.rmg_database.kinetics.families.items()): logging.info("Trying to match reacction to {}".format(family)) for rmg_reactants, rmg_products in combos_to_try: # Making a test reaction test_reaction = RMGReaction(reactants=list(rmg_reactants), products=list(rmg_products)) try: # Trying to label the reaction labeled_r, labeled_p = family.get_labeled_reactants_and_products( test_reaction.reactants, test_reaction.products) except: # Failed to match a reaction to the family logging.error( "Couldn't match {} to {}, trying different combination..." .format(test_reaction, name)) continue if not (labeled_r and labeled_p): # Catching an error where it matched the reaction but the reactants and products we not return... # A weird bug in RMG that I can explain continue if ((len(labeled_r) > 0) and (len(labeled_p) > 0)): # We found a match. if match: # We found a match already, but we were able to label the reaction again... # This would happen if different combinations of resonance structures match up logging.warning( "This reaction has been already labeled... \ it seems that resonance structures \for reactants and products exist." ) logging.warning("Skipping this duplicate for now") continue logging.info( "Matched reaction to {} family".format(name)) # Copying labed reactions and saving them for later labeled_reactants = deepcopy(labeled_r) labeled_products = deepcopy(labeled_p) match_reaction = RMGReaction( reactants=deepcopy(labeled_r), products=deepcopy(labeled_p)) # Setting it true that we matche the reaction and remembering the # RMG reaction family and family name match = True final_family = family final_name = name elif self.rmg_reaction: #RMGReaction but no label rmg_reactants = [] rmg_products = [] for react in self.rmg_reaction.reactants: if isinstance(react, RMGSpecies): rmg_reactants.append(react.molecule) elif isinstance(react, RMGMolecule): rmg_reactants.append(react.generate_resonance_structures()) for prod in self.rmg_reaction.products: if isinstance(prod, RMGSpecies): rmg_products.append(prod.molecule) elif isinstance(prod, RMGMolecule): rmg_products.append(prod.generate_resonance_structures()) combos_to_try = list( itertools.product(list(itertools.product(*rmg_reactants)), list(itertools.product(*rmg_products)))) for name, family in list( self.rmg_database.kinetics.families.items()): logging.info("Trying to match reaction to {}".format(name)) for rmg_reactants, rmg_products in combos_to_try: # Making a test reaction test_reaction = RMGReaction(reactants=list(rmg_reactants), products=list(rmg_products)) try: # Trying to label the reaction labeled_r, labeled_p = family.get_labeled_reactants_and_products( test_reaction.reactants, test_reaction.products) if not (labeled_r and labeled_p): logging.error( "Unable to determine a reaction for the forward direction. Trying the reverse direction." ) raise ActionError except: try: # Trying the reverse reaction if the forward reaction doesn't work # This is useful for R_Addition reactions labeled_r, labeled_p = family.get_labeled_reactants_and_products( test_reaction.products, test_reaction.reactants) except: logging.error( "Couldn't match {} to {}, trying different combination..." .format(test_reaction, name)) continue if not (labeled_r and labeled_p): # Catching an error where it matched the reaction but the reactants and products we not return... # A weird bug in RMG that I can explain continue if ((len(labeled_r) > 0) and (len(labeled_p) > 0)): # We found a match. if match: # We found a match already, but we were able to label the reaction again... # This would happen if different combinations of resonance structures match up logging.warning( "This reaction has been already labeled... \ it seems that resonance structures \for reactants and products exist." ) logging.warning("Skipping this duplicate for now") continue logging.info( "Matched reaction to {} family".format(name)) # Copying labed reactions and saving them for later labeled_reactants = deepcopy(labeled_r) labeled_products = deepcopy(labeled_p) match_reaction = RMGReaction( reactants=deepcopy(labeled_r), products=deepcopy(labeled_p)) # Setting it true that we matche the reaction and remembering the # RMG reaction family and family name match = True final_family = family final_name = name assert match, "Could not idetify labeled reactants and products" reaction_list = final_family.generate_reactions( match_reaction.reactants, match_reaction.products) assert reaction_list, "Could not match a reaction to a reaction family..." for reaction in reaction_list: if match_reaction.is_isomorphic(reaction): reaction.reactants = labeled_reactants reaction.products = labeled_products break self.rmg_reaction = reaction self.reaction_family = final_name return self.rmg_reaction, self.reaction_family
def validate_irc(self): """ A method to verify an IRC calc """ logging.info("Validating IRC file...") irc_path = os.path.join( self.directory, "ts", self.conformer.reaction_label, "irc", self.conformer.reaction_label + "_irc_" + self.conformer.direction + "_" + str(self.conformer.index) + ".log") complete, converged = self.verify_output_file(irc_path) if not complete: logging.info("It seems that the IRC claculation did not complete") return False if not converged: logging.info("The IRC calculation did not converge...") return False pth1 = list() steps = list() with open(irc_path) as output_file: for line in output_file: line = line.strip() if line.startswith('Point Number:'): if int(line.split()[2]) > 0: if int(line.split()[-1]) == 1: pt_num = int(line.split()[2]) pth1.append(pt_num) else: pass elif line.startswith('# OF STEPS ='): num_step = int(line.split()[-1]) steps.append(num_step) # This indexes the coordinate to be used from the parsing if steps == []: logging.error('No steps taken in the IRC calculation!') return False else: pth1End = sum(steps[:pth1[-1]]) # Compare the reactants and products irc_parse = ccread(irc_path) atomcoords = irc_parse.atomcoords atomnos = irc_parse.atomnos mol1 = RMGMolecule() mol1.from_xyz(atomnos, atomcoords[pth1End]) mol2 = RMGMolecule() mol2.from_xyz(atomnos, atomcoords[-1]) test_reaction = RMGReaction( reactants=mol1.split(), products=mol2.split(), ) r, p = self.conformer.reaction_label.split("_") reactants = [] products = [] for react in r.split("+"): react = RMGMolecule(smiles=react) reactants.append(react) for prod in p.split("+"): prod = RMGMolecule(smiles=prod) products.append(prod) possible_reactants = [] possible_products = [] for reactant in reactants: possible_reactants.append( reactant.generate_resonance_structures()) for product in products: possible_products.append( product.generate_resonance_structures()) possible_reactants = list(itertools.product(*possible_reactants)) possible_products = list(itertools.product(*possible_products)) for possible_reactant in possible_reactants: reactant_list = [] for react in possible_reactant: reactant_list.append(react.to_single_bonds()) for possible_product in possible_products: product_list = [] for prod in possible_product: product_list.append(prod.to_single_bonds()) target_reaction = RMGReaction( reactants=list(reactant_list), products=list(product_list)) if target_reaction.is_isomorphic(test_reaction): logging.info("IRC calculation was successful!") return True logging.info("IRC calculation failed for {} :(".format(irc_path)) return False