def __init__(self, smiles=None, rmg_molecule=None, index=0): self.energy = None self.index = index if (smiles or rmg_molecule): if smiles and rmg_molecule: assert rmg_molecule.isIsomorphic( RMGMolecule(SMILES=smiles) ), "SMILES string did not match RMG Molecule object" self.smiles = smiles self.rmg_molecule = rmg_molecule elif rmg_molecule: self.rmg_molecule = rmg_molecule self.smiles = rmg_molecule.toSMILES() else: self.smiles = smiles self.rmg_molecule = RMGMolecule(SMILES=smiles) self.rmg_molecule.updateMultiplicity() self.get_mols() self.get_geometries() else: self.smiles = None self.rmg_molecule = None self.rdkit_molecule = None self.ase_molecule = None self.bonds = [] self.angles = [] self.torsions = [] self.cistrans = [] self.chiral_centers = []
def setUp(self): self.reaction = Reaction("CC+[O]O_[CH2]C+OO") self.reaction2 = Reaction(rmg_reaction=RMGReaction( reactants=[RMGMolecule(smiles="CC"), RMGMolecule(smiles="[O]O")], products=[RMGMolecule(smiles="[CH2]C"), RMGMolecule(smiles="OO")]))
def check_isomorphic(conformer): """ Compares whatever is in the log file 'f' to the SMILES of the passed in 'conformer' """ starting_molecule = RMGMolecule(smiles=conformer.smiles) starting_molecule = starting_molecule.to_single_bonds() atoms = self.read_log( os.path.join(scratch_dir, f) ) test_molecule = RMGMolecule() test_molecule.from_xyz( atoms.arrays["numbers"], atoms.arrays["positions"] ) if not starting_molecule.is_isomorphic(test_molecule): logging.info( "Output geometry of {} is not isomorphic with input geometry".format(calc.label)) return False else: logging.info( "{} was successful and was validated!".format(calc.label)) return True
def get_complexes(self, rmg_reaction=None): """ A method to create a forward and reverse TS complexes used to initialize transition state geometries """ if not rmg_reaction: rmg_reaction = self.rmg_reaction reactant_complex = RMGMolecule() for react in rmg_reaction.reactants: if isinstance(react, RMGMolecule): reactant_complex = reactant_complex.merge(react) elif isinstance(react, RMGSpecies): reactant_complex = reactant_complex.merge(react.molecule[0]) product_complex = RMGMolecule() for prod in rmg_reaction.products: if isinstance(prod, RMGMolecule): product_complex = product_complex.merge(prod) elif isinstance(prod, RMGSpecies): product_complex = product_complex.merge(prod.molecule[0]) reactant_complex.updateMultiplicity() product_complex.updateMultiplicity() self.complexes = { "forward": reactant_complex, "reverse": product_complex } return self.complexes
def __init__( self, smiles=None, reaction_label=None, direction='forward', rmg_molecule=None, reaction_family="H_Abstraction", distance_data=None, index=0): self.energy = None ##################################################### ##################################################### assert reaction_label, "A reaction label needs to be provided in addition to a smiles or rmg_molecule" assert direction in ["forward", "reverse"], "Please provide a valid direction" self.reaction_label = reaction_label self.direction = direction.lower() self._rdkit_molecule = None self._ase_molecule = None self.reaction_family = reaction_family self.distance_data = distance_data self.index = index if (smiles or rmg_molecule): if smiles and rmg_molecule: assert rmg_molecule.isIsomorphic(RMGMolecule( SMILES=smiles)), "SMILES string did not match RMG Molecule object" self.smiles = smiles self.rmg_molecule = rmg_molecule elif rmg_molecule: self.rmg_molecule = rmg_molecule self.smiles = rmg_molecule.toSMILES() else: self.smiles = smiles self.rmg_molecule = RMGMolecule(SMILES=smiles) self.rmg_molecule.updateMultiplicity() self.get_mols() self.get_geometries() self._symmetry_number = None else: self.smiles = None self.rmg_molecule = None self.rdkit_molecule = None self._pseudo_geometry = None self.ase_molecule = None self.bonds = [] self.angles = [] self.torsions = [] self.cistrans = [] self.chiral_centers = [] self._symmetry_number = None
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 setUp(self): self.reaction = Reaction("CC+[O]O_[CH2]C+OO") self.reaction2 = Reaction(rmg_reaction=RMGReaction( reactants=[RMGMolecule(smiles="CC"), RMGMolecule(smiles="[O]O")], products=[RMGMolecule(smiles="[CH2]C"), RMGMolecule(smiles="OO")])) self.ts = self.reaction.ts["forward"][0] self.ts2 = self.reaction.ts["forward"][0] self.ts.get_molecules() self.ts2.get_molecules()
def get_rmg_reaction(self): if self.rmg_reaction: return self.rmg_reaction r, p = self.label.split("_") reactants = [] products = [] for react in r.split("+"): reactants.append(RMGMolecule(SMILES=react)) for prod in p.split("+"): products.append(RMGMolecule(SMILES=prod)) self.rmg_reaction = RMGReaction(reactants=reactants, products=products) return self.rmg_reaction
def __init__(self, smiles=None, reaction_label=None, rmg_molecule=None, reaction_family="H_Abstraction", distance_data=None): self.energy = None ##################################################### ##################################################### assert reaction_label, "A reaction label needs to be provided in addition to a smiles or rmg_molecule" self.reaction_label = reaction_label self._rdkit_molecule = None self._ase_molecule = None self.reaction_family = reaction_family self.distance_data = distance_data if (smiles or rmg_molecule): if smiles and rmg_molecule: assert rmg_molecule.isIsomorphic( RMGMolecule(SMILES=smiles) ), "SMILES string did not match RMG Molecule object" self.smiles = smiles self.rmg_molecule = rmg_molecule elif rmg_molecule: self.rmg_molecule = rmg_molecule self.smiles = rmg_molecule.toSMILES() else: self.smiles = smiles self.rmg_molecule = RMGMolecule(SMILES=smiles) self.rmg_molecule.updateMultiplicity() self.get_mols() self.get_geometries() else: self.smiles = None self.rmg_molecule = None self.rdkit_molecule = None self._pseudo_geometry = None self.ase_molecule = None self.bonds = [] self.angles = [] self.torsions = [] self.cistrans = [] self.chiral_centers = []
def get_rmg_complexes(self): """ A method to create a forward and reverse TS complexes used to initialize transition state geometries Variables: - rmg_reaction (RMGReaction): The RMGReaction of interest Returns: - complexes (dict): a dictionary containing RMGMolecules of the forward and reverse reaction complexes """ if self.rmg_reaction is None: self.get_labeled_reaction() reactant_complex = RMGMolecule() for react in self.rmg_reaction.reactants: if isinstance(react, RMGMolecule): reactant_complex = reactant_complex.merge(react) elif isinstance(react, RMGSpecies): for mol in react.molecule: if len(mol.getLabeledAtoms()) > 0: reactant_complex = reactant_complex.merge(mol) product_complex = RMGMolecule() for prod in self.rmg_reaction.products: if isinstance(prod, RMGMolecule): product_complex = product_complex.merge(prod) elif isinstance(prod, RMGSpecies): for mol in prod.molecule: if len(mol.getLabeledAtoms()) > 0: product_complex = product_complex.merge(mol) reactant_complex.updateMultiplicity() product_complex.updateMultiplicity() if len(reactant_complex.getLabeledAtoms()) == 0 or len( product_complex.getLabeledAtoms()) == 0: logging.warning( "REACTING ATOMS LABELES NOT PROVIDED. Please call `Reaction.get_labeled_reaction` to generate labeled complexes" ) self.complexes = { "forward": reactant_complex, "reverse": product_complex } return self.complexes
def get_molecules(self): if not self.rmg_molecule: self.rmg_molecule = RMGMolecule(SMILES=self.smiles) self.rdkit_molecule = self.get_rdkit_mol() self.ase_molecule = self.get_ase_mol() self.get_geometries() return self.rdkit_molecule, self.ase_molecule
def test_molecule(self): """ Test that a Molecule contains the `id` attribute. """ rmg_mol = RMGMolecule(smiles='C') mol = Molecule(smiles='C') self.assertIsInstance(mol, RMGMolecule) self.assertTrue(hasattr(mol, 'id')) self.assertFalse(hasattr(rmg_mol, 'id'))
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_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.isIsomorphic(labeled_reaction)) merged = labeled_reaction.reactants[0].merge( labeled_reaction.reactants[1]) self.assertTrue(merged.getLabeledAtom("*1").isCarbon()) self.assertTrue(merged.getLabeledAtom("*2").isHydrogen()) self.assertTrue(merged.getLabeledAtom("*3").isOxygen) merged = labeled_reaction.products[0].merge( labeled_reaction.products[1]) self.assertTrue(merged.getLabeledAtom("*3").isCarbon()) self.assertTrue(merged.getLabeledAtom("*2").isHydrogen()) self.assertTrue(merged.getLabeledAtom("*1").isOxygen) labeled_reaction, reaction_family = self.reaction2.get_labeled_reaction( ) self.assertEquals(reaction_family.lower(), "h_abstraction") self.assertTrue(test_reaction.isIsomorphic(labeled_reaction)) merged = labeled_reaction.reactants[0].merge( labeled_reaction.reactants[1]) self.assertTrue(merged.getLabeledAtom("*1").isCarbon()) self.assertTrue(merged.getLabeledAtom("*2").isHydrogen()) self.assertTrue(merged.getLabeledAtom("*3").isOxygen) merged = labeled_reaction.products[0].merge( labeled_reaction.products[1]) self.assertTrue(merged.getLabeledAtom("*3").isCarbon()) self.assertTrue(merged.getLabeledAtom("*2").isHydrogen()) self.assertTrue(merged.getLabeledAtom("*1").isOxygen)
def get_complexes(self, rmg_reaction=None): """ A method to create a forward and reverse TS complexes used to initialize transition state geometries Variables: - rmg_reaction (RMGReaction): The RMGReaction of interest Returns: - complexes (dict): a dictionary containing RMGMolecules of the forward and reverse reaction complexes """ if not rmg_reaction: rmg_reaction = self.rmg_reaction reactant_complex = RMGMolecule() for react in rmg_reaction.reactants: if isinstance(react, RMGMolecule): reactant_complex = reactant_complex.merge(react) elif isinstance(react, RMGSpecies): for mol in react.molecule: if len(mol.getLabeledAtoms()) > 0: reactant_complex = reactant_complex.merge(mol) product_complex = RMGMolecule() for prod in rmg_reaction.products: if isinstance(prod, RMGMolecule): product_complex = product_complex.merge(prod) elif isinstance(prod, RMGSpecies): for mol in prod.molecule: if len(mol.getLabeledAtoms()) > 0: product_complex = product_complex.merge(mol) reactant_complex.updateMultiplicity() product_complex.updateMultiplicity() self.complexes = { "forward": reactant_complex, "reverse": product_complex} return self.complexes
def calculate_symmetry_number(self): numbers = self.ase_molecule.numbers positions = self.ase_molecule.positions mol = RMGMolecule() mol.from_xyz(numbers, positions) try: species = RMGSpecies(molecule=[mol]) self._symmetry_number = species.get_symmetry_number() except ValueError: self._symmetry_number = mol.get_symmetry_number() return self._symmetry_number
def validate_irc(self, calc=None): """ A method to verify an IRC calc """ assert "irc" in calc.label, "The calculator provided is not an IRC calculator" reaction_label = calc.label.split("_irc")[0] logging.info("Validating IRC file...") irc_path = os.path.join(calc.scratch, calc.label + ".log") if not os.path.exists(irc_path): logging.info("It seems that the IRC claculation has not been run.") return False f = open(irc_path, "r") file_lines = f.readlines()[-5:] completed = False for file_line in file_lines: if "Normal termination" in file_line: logging.info("IRC successfully ran") completed = True if not completed: logging.info("IRC failed... could not be validated...") return False pth1 = list() steps = list() with open(irc_path) as outputFile: for line in outputFile: line = line.strip() if line.startswith('Point Number:'): if int(line.split()[2]) > 0: if int(line.split()[-1]) == 1: ptNum = int(line.split()[2]) pth1.append(ptNum) else: pass elif line.startswith('# OF STEPS ='): numStp = int(line.split()[-1]) steps.append(numStp) # 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 ircParse = ccread(irc_path) # cf. # http://cclib.sourceforge.net/wiki/index.php/Using_cclib#Additional_information atomcoords = ircParse.atomcoords atomnos = ircParse.atomnos # Convert the IRC geometries into RMG molecules # We don't know which is reactant or product, so take the two at the end of the # paths and compare to the reactants and products mol1 = RMGMolecule() mol1.fromXYZ(atomnos, atomcoords[pth1End]) mol2 = RMGMolecule() mol2.fromXYZ(atomnos, atomcoords[-1]) testReaction = RMGReaction( reactants=mol1.split(), products=mol2.split(), ) r, p = 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.toSingleBonds()) for possible_product in possible_products: product_list = [] for prod in possible_product: product_list.append(prod.toSingleBonds()) targetReaction = RMGReaction(reactants=list(reactant_list), products=list(product_list)) if targetReaction.isIsomorphic(testReaction): logging.info("IRC calculation was successful!") return True logging.info("IRC calculation failed for {} :(".format(calc.label)) return False
def __init__(self, label=None, rmg_reaction=None, reaction_family="H_Abstraction", calculator=None): self.possible_families = [ # These families (and only these) will be loaded from both RMG and AutoTST databases "R_Addition_MultipleBond", "H_Abstraction", "intra_H_migration" ] self.load_databases() self.calculator = calculator if rmg_reaction: self.rmg_reaction, self.reaction_family = self.get_labeled_reaction( rmg_reaction=rmg_reaction) if label: try: reactants = [] products = [] r, p = label.split("_") for react in r.split("+"): reactants.append(RMGMolecule(SMILES=react)) for prod in p.split("+"): products.append(RMGMolecule(SMILES=prod)) test_reaction = RMGReaction(reactants=reactants, products=products) if self.rmg_reaction.isIsomorphic(test_reaction): self.label = label self.generate_reactants_and_products() else: logging.info( "Label provided doesn't match the RMGReaction, creating a new label" ) self.label = self.get_reaction_label(self.rmg_reaction) self.generate_reactants_and_products() except BaseException: logging.info( "Label provided doesn't match the RMGReaction, creating a new label" ) self.label = self.get_reaction_label(self.rmg_reaction) self.generate_reactants_and_products() else: self.label = self.get_reaction_label(self.rmg_reaction) self.generate_reactants_and_products() elif label: try: self.rmg_reaction, self.reaction_family = self.get_labeled_reaction( label=label) self.label = label self.generate_reactants_and_products(self.rmg_reaction) except BaseException: logging.info( "Label provided is not valid... setting everything to None" ) self.rmg_reaction = None self.label = None self.reaction_family = reaction_family else: self.rmg_reaction = None self.reaction_family = reaction_family self.label = label # a bit clumsy, but saves refactoring code for now. self.ts_database = self.ts_databases[reaction_family] self._ts = None self._distance_data = None
def get_labeled_reaction(self, label=None, rmg_reaction=None): """ A method that will return a labeled reaction given a reaction label or rmg_reaction """ assert ( label or rmg_reaction), "You must provide a reaction or a reaction label" label_reaction = None rmg_reaction_reaction = None if label: rmg_reactants = [] rmg_products = [] r, p = label.split("_") for react in r.split("+"): s = RMGMolecule(SMILES=react) rmg_reactants.append(s) for prod in p.split("+"): s = RMGMolecule(SMILES=prod) rmg_products.append(s) label_reaction = RMGReaction(reactants=rmg_reactants, products=rmg_products) if rmg_reaction: assert rmg_reaction.isIsomorphic(label_reaction) test_reaction = label_reaction else: test_reaction = label_reaction elif rmg_reaction: rmg_reactants = [] rmg_products = [] for react in rmg_reaction.reactants: if isinstance(react, RMGSpecies): rmg_reactants.append(react.molecule) elif isinstance(react, RMGMolecule): rmg_reactants.append([react]) for prod in rmg_reaction.products: if isinstance(prod, RMGSpecies): rmg_products.append(prod.molecule) elif isinstance(prod, RMGMolecule): rmg_products.append([prod]) test_reactants = [] test_products = [] if len(rmg_reactants) == 1: test_reactants = rmg_reactants elif len(rmg_reactants) == 2: l1, l2 = rmg_reactants for i in l1: for j in l2: test_reactants.append([i, j]) if len(rmg_products) == 1: test_reactants = rmg_products elif len(rmg_products) == 2: l1, l2 = rmg_products for i in l1: for j in l2: test_products.append([i, j]) for test_reactant in test_reactants: for test_products in test_products: test_reaction = RMGReaction(reactants=test_reactant, products=test_products) if rmg_reaction.isIsomorphic(test_reaction): break got_one = False for name, family in self.rmg_database.kinetics.families.items(): try: labeled_r, labeled_p = family.getLabeledReactantsAndProducts( test_reaction.reactants, test_reaction.products) if not (labeled_r and labeled_p): continue got_one = True except BaseException: logging.info("Couldn't match {} family...".format(family)) if got_one: break assert got_one, "Couldn't find a match" reaction_list = self.rmg_database.kinetics.generate_reactions_from_families( test_reaction.reactants, test_reaction.products, only_families=[name]) assert reaction_list, "Could not match a reaction to a reaction family..." for reaction in reaction_list: if reaction.isIsomorphic(test_reaction): reaction.reactants = labeled_r reaction.products = labeled_p break return reaction, name
def __init__(self, smiles=[], rmg_species=None): assert isinstance(smiles, list) self._conformers = None if ((len(smiles) != 0) and rmg_species): # Provide both a list of smiles and an rmg_species assert isinstance(rmg_species, (rmgpy.molecule.Molecule, rmgpy.species.Species)) if isinstance(rmg_species, rmgpy.molecule.Molecule): rmg_species = RMGSpecies(molecule=[rmg_species]) rmg_species.generate_resonance_structures() else: rmg_species.generate_resonance_structures() smiles_list = [] for rmg_mol in rmg_species.molecule: smiles_list.append(rmg_mol.toSMILES()) for s in smiles_list: if s in smiles: continue if not (s in smiles): smiles.append(s) assert len(smiles) == len( smiles_list ), "The list of smiles presented does not match the possible species provided" self.smiles = smiles self.rmg_species = rmg_species elif (rmg_species and (len(smiles) == 0)): # RMG species provided, but not smiles assert isinstance(rmg_species, (rmgpy.molecule.Molecule, rmgpy.species.Species)) if isinstance(rmg_species, rmgpy.molecule.Molecule): rmg_species = RMGSpecies(molecule=[rmg_species]) rmg_species.generate_resonance_structures() else: rmg_species.generate_resonance_structures() smiles = [] for rmg_mol in rmg_species.molecule: smiles.append(rmg_mol.toSMILES()) self.smiles = smiles self.rmg_species = rmg_species elif ((not rmg_species) and (len(smiles) != 0)): # smiles provided but not species species_list = [] for smile in smiles: molecule = RMGMolecule(SMILES=smile) s = RMGSpecies(molecule=[molecule]) s.generate_resonance_structures() species_list.append(s) for s1 in species_list: for s2 in species_list: assert s1.isIsomorphic( s2), "SMILESs provided describe different species" self.smiles = smiles self.rmg_species = species_list[0] else: self.smiles = [] self.rmg_species = rmg_species
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 outputFile: for line in outputFile: line = line.strip() if line.startswith('Point Number:'): if int(line.split()[2]) > 0: if int(line.split()[-1]) == 1: ptNum = int(line.split()[2]) pth1.append(ptNum) else: pass elif line.startswith('# OF STEPS ='): numStp = int(line.split()[-1]) steps.append(numStp) # 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 ircParse = ccread(irc_path) atomcoords = ircParse.atomcoords atomnos = ircParse.atomnos mol1 = RMGMolecule() mol1.fromXYZ(atomnos, atomcoords[pth1End]) mol2 = RMGMolecule() mol2.fromXYZ(atomnos, atomcoords[-1]) testReaction = 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.toSingleBonds()) for possible_product in possible_products: product_list = [] for prod in possible_product: product_list.append(prod.toSingleBonds()) targetReaction = RMGReaction(reactants=list(reactant_list), products=list(product_list)) if targetReaction.isIsomorphic(testReaction): logging.info("IRC calculation was successful!") return True logging.info("IRC calculation failed for {} :(".format(calc.label)) return False
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 if self.label: rmg_reactants = [] rmg_products = [] r, p = self.label.split("_") for react in r.split("+"): s = RMGMolecule(SMILES=react) rmg_reactants.append(s) for prod in p.split("+"): s = RMGMolecule(SMILES=prod) rmg_products.append(s) test_reaction = RMGReaction(reactants=rmg_reactants, products=rmg_products) if self.rmg_reaction: assert self.rmg_reaction.isIsomorphic( test_reaction ), "The reaction label provided does not match the RMGReaction provided..." for name, family in list( self.rmg_database.kinetics.families.items()): if match: break try: labeled_r, labeled_p = family.getLabeledReactantsAndProducts( test_reaction.reactants, test_reaction.products) except ValueError: continue if not (labeled_r and labeled_p): continue if ((len(labeled_r) > 0) and (len(labeled_p) > 0)): logging.info("Matched reaction to {} family".format(name)) labeled_reactants = deepcopy(labeled_r) labeled_products = deepcopy(labeled_p) test_reaction.reactants = labeled_r[:] test_reaction.products = labeled_p[:] match = True final_family = family final_name = name break 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]) for prod in self.rmg_reaction.products: if isinstance(prod, RMGSpecies): rmg_products.append(prod.molecule) elif isinstance(prod, RMGMolecule): rmg_products.append([prod]) test_reactants = [] test_products = [] if len(rmg_reactants) == 1: for l1 in rmg_reactants[0]: test_reactants.append([l1]) elif len(rmg_reactants) == 2: l1, l2 = rmg_reactants for i in l1: for j in l2: test_reactants.append([i, j]) if len(rmg_products) == 1: for l1 in rmg_products[0]: test_products.append([l1]) elif len(rmg_products) == 2: l1, l2 = rmg_products for i in l1: for j in l2: test_products.append([i, j]) reacts = test_reactants[:] prods = test_products[:] for name, family in list( self.rmg_database.kinetics.families.items()): logging.info("Trying to match {} to {}".format( self.rmg_reaction, family)) if match: continue test_reactants = reacts[:] test_products = prods[:] for test_reactant in test_reactants: for test_product in test_products: if match: continue test_reaction = RMGReaction(reactants=test_reactant, products=test_product) try: labeled_r, labeled_p = family.getLabeledReactantsAndProducts( test_reaction.reactants, test_reaction.products) if not (labeled_r and labeled_p): logging.info( "Unable to determine a reaction for the forward direction. Trying the reverse direction." ) raise ActionError except (ValueError, ActionError, IndexError): try: # Trying the reverse reaction if the forward reaction doesn't work # This is useful for R_Addition reactions labeled_r, labeled_p = family.getLabeledReactantsAndProducts( test_reaction.products, test_reaction.reactants) except (ValueError, ActionError, IndexError): continue if not (labeled_r and labeled_p): labeled_r, labeled_p = family.getLabeledReactantsAndProducts( test_reaction.products, test_reaction.reactants) continue if ((len(labeled_r) > 0) and (len(labeled_p) > 0)) and ( self.rmg_reaction.isIsomorphic(test_reaction)): logging.info( "Matched reaction to {} family".format(name)) labeled_reactants = deepcopy(labeled_r) labeled_products = deepcopy(labeled_p) test_reaction.reactants = labeled_r[:] test_reaction.products = labeled_p[:] logging.info("{}".format(labeled_r)) logging.info("{}".format(labeled_p)) match = True final_family = family print final_family final_name = name assert match, "Could not idetify labeled reactants and products" #try: reaction_list = final_family.generateReactions(test_reaction.reactants, test_reaction.products) #except KeyError: # reaction_list = None # logging.info("For some reason, RMG is having trouble generating reactions for {}".format(test_reaction)) assert reaction_list, "Could not match a reaction to a reaction family..." for reaction in reaction_list: if test_reaction.isIsomorphic(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 get_labeled_reaction(self, label=None, rmg_reaction=None): """ 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 """ assert ( label or rmg_reaction), "You must provide a reaction or a reaction label" label_reaction = None rmg_reaction_reaction = None test_reactions = [] match = False if label: rmg_reactants = [] rmg_products = [] r, p = label.split("_") for react in r.split("+"): s = RMGMolecule(SMILES=react) rmg_reactants.append(s) for prod in p.split("+"): s = RMGMolecule(SMILES=prod) rmg_products.append(s) test_reaction = RMGReaction( reactants=rmg_reactants, products=rmg_products) if rmg_reaction: assert rmg_reaction.isIsomorphic( test_reaction), "The reaction label provided does not match the RMGReaction provided..." for name, family in list(self.rmg_database.kinetics.families.items()): if match: break labeled_r, labeled_p = family.getLabeledReactantsAndProducts( test_reaction.reactants, test_reaction.products) if not (labeled_r and labeled_p): continue if ((len(labeled_r) > 0) and (len(labeled_p) > 0)): logging.info("Matched reaction to {} family".format(name)) labeled_reactants = deepcopy(labeled_r) labeled_products = deepcopy(labeled_p) test_reaction.reactants = labeled_r[:] test_reaction.products = labeled_p[:] match = True final_name = name break elif rmg_reaction: rmg_reactants = [] rmg_products = [] for react in rmg_reaction.reactants: if isinstance(react, RMGSpecies): rmg_reactants.append(react.molecule) elif isinstance(react, RMGMolecule): rmg_reactants.append([react]) for prod in rmg_reaction.products: if isinstance(prod, RMGSpecies): rmg_products.append(prod.molecule) elif isinstance(prod, RMGMolecule): rmg_products.append([prod]) test_reactants = [] test_products = [] if len(rmg_reactants) == 1: test_reactants = rmg_reactants elif len(rmg_reactants) == 2: l1, l2 = rmg_reactants for i in l1: for j in l2: test_reactants.append([i, j]) if len(rmg_products) == 1: test_reactants = rmg_products elif len(rmg_products) == 2: l1, l2 = rmg_products for i in l1: for j in l2: test_products.append([i, j]) for name, family in list(self.rmg_database.kinetics.families.items()): if match: break for test_reactant in test_reactants: for test_products in test_products: if match: continue test_reaction = RMGReaction( reactants=test_reactant, products=test_products) labeled_r, labeled_p = family.getLabeledReactantsAndProducts( test_reaction.reactants, test_reaction.products) if not (labeled_r and labeled_p): continue if ((len(labeled_r) > 0) and (len(labeled_p) > 0)): logging.info( "Matched reaction to {} family".format(name)) labeled_reactants = deepcopy(labeled_r) labeled_products = deepcopy(labeled_p) test_reaction.reactants = labeled_r[:] test_reaction.products = labeled_p[:] logging.info("\n{}".format(labeled_r)) logging.info("\n{}".format(labeled_p)) match = True final_name = name break assert match, "Could not idetify labeled reactants and products" reaction_list = self.rmg_database.kinetics.generate_reactions_from_families( test_reaction.reactants, test_reaction.products, only_families=[final_name]) assert reaction_list, "Could not match a reaction to a reaction family..." for reaction in reaction_list: if test_reaction.isIsomorphic(reaction): reaction.reactants = labeled_reactants reaction.products = labeled_products break return reaction, name
def __init__(self, smiles=[], rmg_species=None): """ A class that holds information for Species object """ assert isinstance(smiles, list) self._conformers = None if ((len(smiles) != 0) and rmg_species): # Provide both a list of smiles and an rmg_species assert isinstance( rmg_species, (rmgpy.molecule.Molecule, rmgpy.species.Species)) if isinstance(rmg_species, rmgpy.molecule.Molecule): rmg_species = RMGSpecies(molecule=[rmg_species]) try: rmg_species.generate_resonance_structures() except: logging.info( "Could not generate resonance structures for this species... Using molecule provided") else: try: rmg_species.generate_resonance_structures() except: logging.info( "Could not generate resonance structures for this species... Using molecule provided") smiles_list = [] for rmg_mol in rmg_species.molecule: smiles_list.append(rmg_mol.toSMILES()) for s in smiles_list: if s in smiles: continue if not(s in smiles): smiles.append(s) assert len(smiles) == len( smiles_list), "The list of smiles presented does not match the possible species provided" self.smiles = smiles self.rmg_species = rmg_species elif (rmg_species and (len(smiles) == 0)): # RMG species provided, but not smiles assert isinstance( rmg_species, (rmgpy.molecule.Molecule, rmgpy.species.Species)) if isinstance(rmg_species, rmgpy.molecule.Molecule): rmg_species = RMGSpecies(molecule=[rmg_species]) try: rmg_species.generate_resonance_structures() except: logging.info( "Could not generate resonance structures for this species... Using molecule provided") else: try: rmg_species.generate_resonance_structures() except: logging.info( "Could not generate resonance structures for this species... Using molecule provided") smiles = [] for rmg_mol in rmg_species.molecule: smiles.append(rmg_mol.toSMILES()) self.smiles = smiles self.rmg_species = rmg_species elif ((not rmg_species) and (len(smiles) != 0)): # smiles provided but not species species_list = [] for smile in smiles: molecule = RMGMolecule(SMILES=smile) species_list.append(molecule.generate_resonance_structures()) if len(smiles) != 1: got_one = False for s1 in species_list: for s2 in species_list: for m1 in s1: for m2 in s2: if m1.isIsomorphic(m2): got_one = True assert got_one, "SMILESs provided describe different species" smiles_list = [] for mol in species_list[0]: smiles_list.append(mol.toSMILES()) self.smiles = smiles_list self.rmg_species = species_list[0] else: self.smiles = [] self.rmg_species = rmg_species
def update_dictionary_entries(old_entries, need_to_add): """ Expects dictionary of species entries and unique list of species (as SMILES) that need to be added Creates new entries for the species that need to be added Returns old and new entries """ list(set(need_to_add)) for j, species in enumerate(need_to_add): molecule = RMGMolecule(SMILES=species) adjlist = molecule.toAdjacencyList() multiplicity = None if re.search('(?<=multiplicity ).*', adjlist): multiplicity = int( re.search('(?<=multiplicity ).*', adjlist).group(0)) adjlist = re.sub(r'multiplicity .*', 'multiplicity [{}]'.format(multiplicity), adjlist) group = rmgpy.molecule.group.Group() group.fromAdjacencyList(adjlist) atom_counts = {} rel_label = '' for atom in ['C', 'H', 'O']: count = species.count(atom) if count > 0: rel_label = rel_label + atom + str(count) assert rel_label != '' """ 3 Scenerios: No old -> no need for ID number: max_ID = -1 Only one old -> needs to have ID of 1: max_ID = 0 Multiple old -> needs to have a unique ID: max_ID > 0 """ new_ID = None max_ID = -1 duplicate = False for old_label in old_entries: old_entry = old_entries[old_label] if group.isIsomorphic(old_entry.item): duplicate = True print '{} found to be duplicate'.format(old_entry) continue if rel_label not in old_label: continue if rel_label == old_label and max_ID == -1: # Atleast one with same label max_ID = 0 if old_label.find('-') > 0: old_label, ID_str = old_label.split('-') ID = int(ID_str) if old_label == rel_label and ID > max_ID: # Multiple exisitng labels max_ID = ID if max_ID > -1: # Existing label new_ID = max_ID + 1 rel_label = rel_label + '-' + str(new_ID) if not duplicate: entry = Entry() entry.label = rel_label entry.item = group assert rel_label not in old_entries.keys() old_entries[rel_label] = entry entry_labels = [old_entries[key].label for key in old_entries] assert len(entry_labels) == len(list( set(entry_labels))), 'Non-unique labels in dictionary' return old_entries
def calculate_conformer(self, conformer, calculator): if isinstance(calculator, Gaussian): calc = calculator.get_conformer_calc(conformer=conformer, convergence='Tight') else: calc = calculator.get_conformer_calc(conformer=conformer) scratch = calculator.scratch scratch_dir = os.path.join(calculator.scratch, "species", conformer.smiles, "conformers") f = calc.label + ".log" if not os.path.exists(os.path.join(scratch_dir, f)): logging.info("Submitting conformer calculation for {}".format( calc.label)) label = self.submit_conformer(conformer, calc, "general") while not self.check_complete(label): time.sleep(15) else: logging.info( "It appears that we already have a complete log file for {}". format(calc.label)) complete, converged = calculator.verify_output_file( os.path.join(scratch_dir, f)) if not complete: logging.info( "It seems that the file never completed for {} completed, running it again" .format(calc.label)) label = self.submit_conformer(conformer, calc, "general") while not self.check_complete(label): time.sleep(15) complete, converged = calculator.verify_output_file( os.path.join(scratch_dir, f)) if (complete and converged): logging.info("{} was successful and was validated!".format( calc.label)) atoms = self.read_log(os.path.join(scratch_dir, f)) starting_molecule = RMGMolecule(SMILES=conformer.smiles) starting_molecule = starting_molecule.toSingleBonds() test_molecule = RMGMolecule() test_molecule.fromXYZ(atoms.arrays["numbers"], atoms.arrays["positions"]) if not starting_molecule.isIsomorphic(test_molecule): logging.info( "Output geometry of {} is not isomorphic with input geometry" .format(calc.label)) result = False else: logging.info("{} was successful and was validated!".format( calc.label)) result = True if not complete: logging.info("It appears that {} was killed prematurely".format( calc.label)) result = False elif not converged: logging.info("{} did not converge".format(calc.label)) result = False if isinstance(calculator, Gaussian): if not calc.convergence.lower() in [ "tight", "verytight", "loose" ]: logging.info("{} failed QM optimization".format( calc.label)) else: logging.info( "Resubmitting {} with default convergence criteria". format(calc.label)) atoms = self.read_log(os.path.join(scratch_dir, f)) conformer.ase_molecule = atoms conformer.update_coords_from("ase") calc = calculator.get_conformer_calc(conformer, convergence="") logging.info( "Removing the old log file that didn't converge, restarting from last geometry" ) os.remove(os.path.join(scratch_dir, f)) label = self.submit_conformer(conformer, calc, "general") while not self.check_complete(label): time.sleep(15) scratch_dir = os.path.join(calculator.scratch, "species", conformer.smiles, "conformers") f = calc.label + ".log" if not os.path.exists(os.path.join(scratch_dir, f)): logging.info( "It seems that {} was never run...".format( calc.label)) result = False complete, converged = calculator.verify_output_file( os.path.join(scratch_dir, f)) if not complete: logging.info( "It appears that {} was killed prematurely".format( calc.label)) result = False elif not converged: logging.info("{} failed second QM optimization".format( calc.label)) result = False else: atoms = self.read_log(os.path.join(scratch_dir, f)) starting_molecule = RMGMolecule( SMILES=conformer.smiles) starting_molecule = starting_molecule.toSingleBonds() test_molecule = RMGMolecule() test_molecule.fromXYZ(atoms.arrays["numbers"], atoms.arrays["positions"]) if not starting_molecule.isIsomorphic(test_molecule): logging.info( "Output geometry of {} is not isomorphic with input geometry" .format(calc.label)) result = False else: logging.info( "{} was successful and was validated!".format( calc.label)) result = True if not result: fail_dir = os.path.join(scratch_dir, "failures") try: os.makedirs(os.path.join(scratch_dir, "failures")) except OSError: logging.info("{} already exists...".format(fail_dir)) move(os.path.join(scratch_dir, f), os.path.join(scratch_dir, "failures", f)) return False return True
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