def test_calculate_conformer(self): if os.path.exists(os.path.expandvars("$AUTOTST/test/species")): shutil.rmtree(os.path.expandvars("$AUTOTST/test/species")) conformer = Conformer(smiles='CC', index=0) result = self.job.calculate_conformer(conformer=conformer) self.assertTrue(result) conformer = Conformer(smiles='CC(Cl)(Cl)Cl', index=0) result = self.job.calculate_conformer(conformer=conformer) self.assertFalse(result)
def get_rdkit_mol(self): """ A method to create an rdkit geometry... slightly different than that of the conformer method returns both the rdkit_molecule and the bm """ self.rdkit_molecule = Conformer( rmg_molecule=self.rmg_molecule).get_rdkit_mol() self.get_labels() for i, atom in enumerate(self.rmg_molecule.atoms): assert atom.number == self.rdkit_molecule.GetAtoms( )[i].GetAtomicNum() if len(self.labels) == 3: rd_copy = Chem.RWMol(self.rdkit_molecule.__copy__()) lbl1, lbl2, lbl3 = self.labels if not rd_copy.GetBondBetweenAtoms(lbl1, lbl2): rd_copy.AddBond(lbl1, lbl2, order=rdkit.Chem.rdchem.BondType.SINGLE) elif not rd_copy.GetBondBetweenAtoms(lbl2, lbl3): rd_copy.AddBond(lbl2, lbl3, order=rdkit.Chem.rdchem.BondType.SINGLE) self._pseudo_geometry = rd_copy logging.info("Initially embedded molecule") self.bm = None if self.distance_data: logging.info("Getting bounds matrix") self.bm = self.get_bounds_matrix() if len(self.labels) > 0: logging.info("Editing bounds matrix") self.bm = self.edit_matrix() logging.info("Performing triangle smoothing on bounds matrix.") DistanceGeometry.DoTriangleSmoothing(self.bm) logging.info("Now attempting to embed using edited bounds matrix.") self.rd_embed() return self.rdkit_molecule
class TestSystematic(unittest.TestCase): def setUp(self): self.conformer = Conformer("NC(O)C=CC") self.conformer.get_molecules() def test_find_all_combos(self): combos = find_all_combos(self.conformer, delta=120, cistrans=True, chiral_centers=True) self.assertTrue(len(combos), 108) self.assertTrue(len(combos[0]), 3) def test_systematic_search(self): self.conformer.ase_molecule.set_calculator(EMT()) confs = systematic_search(self.conformer, delta=180) self.assertTrue(0 < len(confs) <= 3)
def get_rdkit_mol(self, rmg_molecule=None, reaction_family="H_Abstraction", distance_data=None): """ A method to create an rdkit geometry... slightly different than that of the conformer method returns both the rdkit_molecule and the bm """ if not rmg_molecule: rmg_molecule = self.rmg_molecule rdkit_molecule = Chem.RWMol( Conformer().get_rdkit_mol(rmg_molecule=rmg_molecule)) labels, atom_match = self.get_labels(rmg_molecule, reaction_family) for i, atom in enumerate(rmg_molecule.atoms): assert atom.number == rdkit_molecule.GetAtoms()[i].GetAtomicNum() if len(labels) == 3: rd_copy = rdkit_molecule.__copy__() lbl1, lbl2, lbl3 = labels if not rd_copy.GetBondBetweenAtoms(lbl1, lbl2): rd_copy.AddBond(lbl1, lbl2, order=rdkit.Chem.rdchem.BondType.SINGLE) else: rd_copy.AddBond(lbl2, lbl3, order=rdkit.Chem.rdchem.BondType.SINGLE) self._pseudo_geometry = rd_copy logging.info("Initially embedded molecule") bm = None if distance_data: logging.info("Getting bounds matrix") bm = self.get_bounds_matrix(rdkit_molecule=rdkit_molecule) if len(labels) > 0: logging.info("Editing bounds matrix") bm = self.edit_matrix(rmg_molecule, bm, labels, distance_data) logging.info("Performing triangle smoothing on bounds matrix.") DistanceGeometry.DoTriangleSmoothing(bm) logging.info("Now attempting to embed using edited bounds matrix.") rdkit_molecule = self.rd_embed(rdkit_molecule, 10000, bm=bm, match=atom_match)[0] return rdkit_molecule, bm
def test_load_conformer_attributes(self): charge = 0 mult = 1 label = 'CC(C)C' base = 'CC{C}C' self.orca.conformer = Conformer(smiles='CC(C)C') self.orca.load_conformer_attributes() self.assertTrue([charge,mult,label,base] == [self.orca.charge,self.orca.mult,self.orca.label,self.orca.base])
def get_angles(self): test_conf = Conformer() test_conf.rmg_molecule = self.rmg_molecule try: test_conf._rdkit_molecule = self._pseudo_geometry except: self.get_rdkit_mol() test_conf._rdkit_molecule = self._pseudo_geometry test_conf._ase_molecule = self.ase_molecule return test_conf.get_angles()
class TestUtilities(unittest.TestCase): def setUp(self): self.conformer = Conformer("NC(O)C=CC") self.conformer.get_molecules() def test_get_potential_energy(self): self.conformer.ase_molecule.set_calculator(EMT()) test_energy = get_energy(self.conformer) energy = self.conformer.ase_molecule.get_potential_energy() self.assertAlmostEqual(test_energy, energy, places=2) def test_find_torsions(self): terminal_torsions, non_terminal_torsions = find_terminal_torsions( self.conformer) self.assertEqual(len(self.conformer.torsions), len(terminal_torsions + non_terminal_torsions)) self.assertEqual(len(terminal_torsions), 1) # only one terminal methyl group self.assertEqual(len(non_terminal_torsions), 3) # N-C, O-C, C-C
def get_bonds(self): return Conformer().get_bonds(self._pseudo_geometry, self.ase_molecule, self.rmg_molecule)
def setUp(self): self.conformer = Conformer(smiles='CC')
class TestConformer(unittest.TestCase): def setUp(self): self.conformer = Conformer(smiles='CC') def test_rmg_molecules(self): self.assertIsInstance(self.conformer.rmg_molecule, RMGMolecule) def test_rdkit_mol(self): autotst_rdkit = self.conformer.get_rdkit_mol() self.assertIsInstance(autotst_rdkit, Mol) def test_ase_mol(self): autotst_ase_mol = self.conformer.get_ase_mol() self.assertIsInstance(autotst_ase_mol, Atoms) def test_get_molecules(self): autotst_rdkit, autotst_ase_mol = self.conformer.get_molecules() self.assertIsInstance(autotst_rdkit, Mol) self.assertIsInstance(autotst_ase_mol, Atoms) def test_get_bonds(self): bonds = self.conformer.get_bonds() self.assertIsInstance(bonds, list) self.assertIsInstance(bonds[0], Bond) self.assertEquals(len(bonds), 7) def test_get_angles(self): angles = self.conformer.get_angles() self.assertIsInstance(angles, list) self.assertIsInstance(angles[0], Angle) self.assertEquals(len(angles), 12) def test_get_torsions(self): torsions = self.conformer.get_torsions() self.assertIsInstance(torsions, list) self.assertIsInstance(torsions[0], Torsion) self.assertEquals(len(torsions), 1) def test_get_cistrans(self): cistrans = self.conformer.get_cistrans() self.assertIsInstance(cistrans, list) self.assertEquals(len(cistrans), 0) def test_get_chiralcenters(self): chiralcenters = self.conformer.get_chiral_centers() self.assertIsInstance(chiralcenters, list) self.assertEquals(len(chiralcenters), 0) def test_get_geometries(self): geometries = self.conformer.get_geometries() self.assertIsInstance(geometries, tuple) self.assertIsInstance(geometries[0], list) self.assertIsInstance(geometries[0][0], Bond) self.assertIsInstance(geometries[1], list) self.assertIsInstance(geometries[1][0], Angle) self.assertIsInstance(geometries[2], list) self.assertIsInstance(geometries[2][0], Torsion) self.assertIsInstance(geometries[3], list) self.assertIsInstance(geometries[4], list) def test_calculate_symmetry_number(self): self.assertEquals(self.conformer.calculate_symmetry_number(), 1) os.remove("./CC.symm")
def get_torsions(self): test_conf = Conformer() test_conf.rmg_molecule = self.rmg_molecule test_conf.rdkit_molecule = self._pseudo_geometry test_conf.ase_molecule = self.ase_molecule return test_conf.get_torsions()
class TS(Conformer): """ A class that defines the 3D geometry of a transition state (TS) """ 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 self.reaction_label = reaction_label self.direction = direction.lower() self.reaction_family = reaction_family self.distance_data = distance_data self.index = index self.bm = None assert direction in ["forward", "reverse"], "Please provide a valid direction" self._rdkit_molecule = None self._ase_molecule = None 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._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 __repr__(self): return '<TS "{}">'.format(self.smiles) def copy(self): copy_conf = TS(reaction_label=self.reaction_label, reaction_family=self.reaction_family) copy_conf.smiles = self.smiles copy_conf.rmg_molecule = self.rmg_molecule.copy() copy_conf.rdkit_molecule = self.rdkit_molecule.__copy__() copy_conf._pseudo_geometry = self._pseudo_geometry.__copy__() copy_conf.ase_molecule = self.ase_molecule.copy() copy_conf.get_geometries() copy_conf.energy = self.energy copy_conf._symmetry_number = self._symmetry_number return copy_conf @property def symmetry_number(self): if not self._symmetry_number: self._symmetry_number = self.calculate_symmetry_number() return self._symmetry_number @property def rdkit_molecule(self): if (self._rdkit_molecule is None) and self.distance_data: self._rdkit_molecule = self.get_rdkit_mol() return self._rdkit_molecule @property def ase_molecule(self): if (self._ase_molecule is None): self._ase_molecule = self.get_ase_mol() return self._ase_molecule def get_rdkit_mol(self): """ A method to create an rdkit geometry... slightly different than that of the conformer method returns both the rdkit_molecule and the bm """ self.rdkit_molecule = Conformer( rmg_molecule=self.rmg_molecule).get_rdkit_mol() self.get_labels() for i, atom in enumerate(self.rmg_molecule.atoms): assert atom.number == self.rdkit_molecule.GetAtoms( )[i].GetAtomicNum() if len(self.labels) == 3: rd_copy = Chem.RWMol(self.rdkit_molecule.__copy__()) lbl1, lbl2, lbl3 = self.labels if not rd_copy.GetBondBetweenAtoms(lbl1, lbl2): rd_copy.AddBond(lbl1, lbl2, order=rdkit.Chem.rdchem.BondType.SINGLE) elif not rd_copy.GetBondBetweenAtoms(lbl2, lbl3): rd_copy.AddBond(lbl2, lbl3, order=rdkit.Chem.rdchem.BondType.SINGLE) self._pseudo_geometry = rd_copy logging.info("Initially embedded molecule") self.bm = None if self.distance_data: logging.info("Getting bounds matrix") self.bm = self.get_bounds_matrix() if len(self.labels) > 0: logging.info("Editing bounds matrix") self.bm = self.edit_matrix() logging.info("Performing triangle smoothing on bounds matrix.") DistanceGeometry.DoTriangleSmoothing(self.bm) logging.info("Now attempting to embed using edited bounds matrix.") self.rd_embed() return self.rdkit_molecule def get_bounds_matrix(self): """ A method to obtain the bounds matrix """ self.bm = rdDistGeom.GetMoleculeBoundsMatrix(self.rdkit_molecule) return self.bm def set_limits(self, lbl1, lbl2, value, uncertainty): """ A method to set the limits of a particular distance between two atoms :param bm: an array of arrays corresponding to the bounds matrix :param lbl1: the label of one atom :param lbl2: the label of another atom :param value: the distance from a distance data object (float) :param uncertainty: the uncertainty of the `value` distance (float) :return bm: an array of arrays corresponding to the edited bounds matrix """ logging.info( "For atoms {0} and {1} we have a distance of: \t {2}".format( lbl1, lbl2, value)) if lbl1 > lbl2: self.bm[lbl2][lbl1] = value + uncertainty / 2 self.bm[lbl1][lbl2] = max(0, value - uncertainty / 2) else: self.bm[lbl2][lbl1] = max(0, value - uncertainty / 2) self.bm[lbl1][lbl2] = value + uncertainty / 2 return self.bm def bm_pre_edit(self, sect): """ Clean up some of the atom distance limits before attempting triangle smoothing. This ensures any edits made do not lead to unsolvable scenarios for the molecular embedding algorithm. sect is the list of atom indices belonging to one species. """ others = list(range(len(self.bm))) for idx in sect: others.remove(idx) for i in range(len(self.bm)): # sect: for j in range(i): # others: if i < j: continue for k in range(len(self.bm)): if k == i or k == j or i == j: continue Uik = self.bm[i, k] if k > i else self.bm[k, i] Ukj = self.bm[j, k] if k > j else self.bm[k, j] maxLij = Uik + Ukj - 0.1 if self.bm[i, j] > maxLij: logging.info("Changing lower limit {0} to {1}".format( self.bm[i, j], maxLij)) self.bm[i, j] = maxLij return self.bm def get_labels(self): """ A method to get the labeled atoms from a reaction :param reactants: a combined rmg_molecule object :return labels: the atom labels corresponding to the reaction center :return atomMatch: a tuple of tuples the atoms labels corresponding to the reaction center """ if len(self.rmg_molecule.getLabeledAtoms()) == 0: labels = [] atomMatch = () if self.reaction_family.lower() in [ 'h_abstraction', 'r_addition_multiplebond', 'intra_h_migration' ]: # for i, atom in enumerate(reactants.atoms): lbl1 = self.rmg_molecule.getLabeledAtoms()["*1"].sortingLabel lbl2 = self.rmg_molecule.getLabeledAtoms()["*2"].sortingLabel lbl3 = self.rmg_molecule.getLabeledAtoms()["*3"].sortingLabel labels = [lbl1, lbl2, lbl3] atomMatch = ((lbl1, ), (lbl2, ), (lbl3, )) elif self.reaction_family.lower() in ['disproportionation']: lbl1 = self.rmg_molecule.getLabeledAtoms()["*2"].sortingLabel lbl2 = self.rmg_molecule.getLabeledAtoms()["*4"].sortingLabel lbl3 = self.rmg_molecule.getLabeledAtoms()["*1"].sortingLabel labels = [lbl1, lbl2, lbl3] atomMatch = ((lbl1, ), (lbl2, ), (lbl3, )) #logging.info("The labled atoms are {}.".format(labels)) self.labels = labels self.atom_match = atomMatch return self.labels, self.atom_match def edit_matrix(self): """ A method to edit the bounds matrix using labels and distance data """ lbl1, lbl2, lbl3 = self.labels sect = [] for atom in self.rmg_molecule.split()[0].atoms: sect.append(atom.sortingLabel) uncertainties = {'d12': 0.02, 'd13': 0.02, 'd23': 0.02} self.bm = self.set_limits(lbl1, lbl2, self.distance_data.distances['d12'], uncertainties['d12']) self.bm = self.set_limits(lbl2, lbl3, self.distance_data.distances['d23'], uncertainties['d23']) self.bm = self.set_limits(lbl1, lbl3, self.distance_data.distances['d13'], uncertainties['d13']) self.bm = self.bm_pre_edit(sect) return self.bm def optimize_rdkit_molecule(self): """ Optimizes the rdmol object using UFF. Determines the energy level for each of the conformers identified in rdmol.GetConformer. :param rdmol: :param boundsMatrix: :param atomMatch: :return rdmol, minEid (index of the lowest energy conformer) """ energy = 0.0 minEid = 0 lowestE = 9.999999e99 # start with a very high number, which would never be reached for conf in self.rdkit_molecule.GetConformers(): if (self.bm is None) or (self.atom_match is None): AllChem.UFFOptimizeMolecule(self.rdkit_molecule, confId=conf.GetId()) energy = AllChem.UFFGetMoleculeForceField( self.rdkit_molecule, confId=conf.GetId()).CalcEnergy() else: _, energy = EmbedLib.OptimizeMol(self.rdkit_molecule, self.bm, atomMatches=self.atom_match, forceConstant=100000.0) if energy < lowestE: minEid = conf.GetId() lowestE = energy return self.rdkit_molecule, minEid def rd_embed(self): """ This portion of the script is literally taken from rmgpy but hacked to work without defining a geometry object Embed the RDKit molecule and create the crude molecule file. """ numConfAttempts = 10000 if (self.bm is None) or (self.atom_match is None): AllChem.EmbedMultipleConfs(self.rdkit_molecule, numConfAttempts, randomSeed=1) self.rdkit_molecule, minEid = self.optimize_rdkit_molecule() else: """ Embed the molecule according to the bounds matrix. Built to handle possible failures of some of the embedding attempts. """ self.rdkit_molecule.RemoveAllConformers() for i in range(0, numConfAttempts): try: EmbedLib.EmbedMol(self.rdkit_molecule, self.bm, atomMatch=self.atom_match) break except ValueError: logging.info( "RDKit failed to embed on attempt {0} of {1}".format( i + 1, numConfAttempts)) except RuntimeError: logging.info("RDKit failed to embed.") else: logging.error("RDKit failed all attempts to embed") return None, None """ RDKit currently embeds the conformers and sets the id as 0, so even though multiple conformers have been generated, only 1 can be called. Below the id's are resolved. """ for i in range(len(self.rdkit_molecule.GetConformers())): self.rdkit_molecule.GetConformers()[i].SetId(i) self.rdkit_molecule, minEid = self.optimize_rdkit_molecule() return self.rdkit_molecule, minEid def get_bonds(self): test_conf = Conformer() test_conf.rmg_molecule = self.rmg_molecule test_conf.rdkit_molecule = self._pseudo_geometry test_conf.ase_molecule = self.ase_molecule return test_conf.get_bonds() def get_torsions(self): test_conf = Conformer() test_conf.rmg_molecule = self.rmg_molecule test_conf.rdkit_molecule = self._pseudo_geometry test_conf.ase_molecule = self.ase_molecule return test_conf.get_torsions() def get_angles(self): test_conf = Conformer() test_conf.rmg_molecule = self.rmg_molecule test_conf.rdkit_molecule = self._pseudo_geometry test_conf.ase_molecule = self.ase_molecule return test_conf.get_angles()
def test_calculate_conformer(self): conformer = Conformer(smiles='CC', index=0) result = self.job.calculate_conformer(conformer=conformer) self.assertTrue(result)
def setUp(self): self.conformer = Conformer("CC(C)C=CC") self.conformer_3rad = Conformer("[C]=[CH]") self.conformer_4rad = Conformer("[C]=[C]")
def get_angles(self): return Conformer().get_angles(self._pseudo_geometry, self.ase_molecule)
def test_conformer_calc(self): autotst_gaussian_confomer = Gaussian(conformer=Conformer( smiles='CCC')).get_conformer_calc() calc_dict = autotst_gaussian_confomer.todict() self.assertIsInstance(autotst_gaussian_confomer, ASEGaussian) self.assertIsInstance(calc_dict, dict)
class TestConformer(unittest.TestCase): def setUp(self): self.conformer = Conformer(smiles='CC') def test_rmg_molecules(self): self.assertIsInstance(self.conformer.rmg_molecule, RMGMolecule) def test_rdkit_mol(self): autotst_rdkit = self.conformer.get_rdkit_mol() self.assertIsInstance(autotst_rdkit, Mol) def test_ase_mol(self): autotst_ase_mol = self.conformer.get_ase_mol() self.assertIsInstance(autotst_ase_mol, Atoms) def test_get_molecules(self): autotst_rdkit, autotst_ase_mol = self.conformer.get_molecules() self.assertIsInstance(autotst_rdkit, Mol) self.assertIsInstance(autotst_ase_mol, Atoms) def test_get_bonds(self): bonds = self.conformer.get_bonds() self.assertIsInstance(bonds, list) self.assertIsInstance(bonds[0], Bond) self.assertEquals(len(bonds), 7) def test_get_angles(self): angles = self.conformer.get_angles() self.assertIsInstance(angles, list) self.assertIsInstance(angles[0], Angle) self.assertEquals(len(angles), 12) def test_get_torsions(self): torsions = self.conformer.get_torsions() self.assertIsInstance(torsions, list) self.assertIsInstance(torsions[0], Torsion) self.assertEquals(len(torsions), 1) def test_get_cistrans(self): cistrans = self.conformer.get_cistrans() self.assertIsInstance(cistrans, list) self.assertEquals(len(cistrans), 0) def test_get_chiralcenters(self): chiralcenters = self.conformer.get_chiral_centers() self.assertIsInstance(chiralcenters, list) self.assertEquals(len(chiralcenters), 0) def test_get_geometries(self): geometries = self.conformer.get_geometries() self.assertIsInstance(geometries, tuple) self.assertIsInstance(geometries[0], list) self.assertIsInstance(geometries[0][0], Bond) self.assertIsInstance(geometries[1], list) self.assertIsInstance(geometries[1][0], Angle) self.assertIsInstance(geometries[2], list) self.assertIsInstance(geometries[2][0], Torsion) self.assertIsInstance(geometries[3], list) self.assertIsInstance(geometries[4], list) def test_calculate_symmetry_number(self): species_to_test = { "CC": 18.0, } self.assertEquals(self.conformer.calculate_symmetry_number(), 18.0) def test_get_xyz_block(self): xyz_block = self.conformer.get_xyz_block() positions = self.conformer.ase_molecule.arrays["positions"] for n in range(len(positions)): self.assertTrue((np.array([ float(x) for x in xyz_block.split('\n')[n].split()[1:] ]) == positions[n]).all())
def setUp(self): self.conformer = Conformer("NC(O)C=CC") self.conformer.get_molecules()
def setUp(self): conf = Conformer(smiles='C') self.orca = Orca(conformer=conf, directory=os.path.expandvars( "$AUTOTST/autotst/calculator/fod"))