def testMMFFAngleConstraints(self): m = Chem.MolFromMolBlock(self.molB, True, False) mp = ChemicalForceFields.MMFFGetMoleculeProperties(m) ff = ChemicalForceFields.MMFFGetMoleculeForceField(m, mp) self.assertTrue(ff) ff.MMFFAddAngleConstraint(1, 3, 6, False, 90.0, 90.0, 100.0) r = ff.Minimize() self.assertTrue(r == 0) conf = m.GetConformer() angle = rdMolTransforms.GetAngleDeg(conf, 1, 3, 6) self.assertTrue(int(angle) == 90) ff = ChemicalForceFields.MMFFGetMoleculeForceField(m, mp) self.assertTrue(ff) ff.MMFFAddAngleConstraint(1, 3, 6, True, -10.0, 10.0, 100.0) r = ff.Minimize() self.assertTrue(r == 0) conf = m.GetConformer() angle = rdMolTransforms.GetAngleDeg(conf, 1, 3, 6) self.assertTrue(int(angle) == 100) ff = ChemicalForceFields.MMFFGetMoleculeForceField(m, mp) self.assertTrue(ff) ff.MMFFAddAngleConstraint(1, 3, 6, False, -10.0, 10.0, 100.0) r = ff.Minimize() self.assertTrue(r == 0) conf = m.GetConformer() angle = rdMolTransforms.GetAngleDeg(conf, 1, 3, 6) self.assertEquals(int(angle), 10) #(int(angle) == 10)
def testUFFAngleConstraints(self): m = Chem.MolFromMolBlock(self.molB, True, False) ff = ChemicalForceFields.UFFGetMoleculeForceField(m) self.assertTrue(ff) ff.UFFAddAngleConstraint(1, 3, 6, False, 90.0, 90.0, 100.0) r = ff.Minimize() self.assertTrue(r == 0) conf = m.GetConformer() angle = rdMolTransforms.GetAngleDeg(conf, 1, 3, 6) self.assertAlmostEqual(angle, 90, delta=0.1) ff = ChemicalForceFields.UFFGetMoleculeForceField(m) self.assertTrue(ff) ff.UFFAddAngleConstraint(1, 3, 6, True, -10.0, 10.0, 100.0) r = ff.Minimize() self.assertTrue(r == 0) conf = m.GetConformer() angle = rdMolTransforms.GetAngleDeg(conf, 1, 3, 6) self.assertAlmostEqual(angle, 100, delta=0.1) ff = ChemicalForceFields.UFFGetMoleculeForceField(m) self.assertTrue(ff) ff.UFFAddAngleConstraint(1, 3, 6, False, -10.0, 10.0, 100.0) r = ff.Minimize() self.assertTrue(r == 0) conf = m.GetConformer() angle = rdMolTransforms.GetAngleDeg(conf, 1, 3, 6) self.assertAlmostEqual(angle, 10, delta=0.1)
def sp2_indices(mol): """ Determine which atoms in the rdkit-mol object are sp2 hybridized by probing for the following criteria: 1) is the atom bounded by 3 neighbors 2) is the angle to all neighbors between 109 and 145 degrees input: rdkit molecule object returns: list of atomic indices being sp2-hybridized """ sp2_index = [] atoms = mol.GetAtoms() bonded = [] for bond in mol.GetAtoms()[0].GetBonds(): bonded.append(bond.GetEndAtomIdx()) for atom in atoms: neighbors = atom.GetNeighbors() if len(neighbors) == 3: for a, b in ((0, 1), (1, 2), (2, 0)): angle = rdMolTransforms.GetAngleDeg(mol.GetConformer(), bonded[a], 0, bonded[b]) if (angle > 109 and angle < 145): sp2_index.append(atom.GetIdx()) return list(set(sp2_index))
def testGetSetAngle(self): file = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'MolTransforms', 'test_data', '3-cyclohexylpyridine.mol') m = Chem.MolFromMolFile(file, True, False) conf = m.GetConformer() angle = rdmt.GetAngleDeg(conf, 0, 19, 21) self.failUnlessAlmostEqual(angle, 109.7, 1) rdmt.SetAngleDeg(conf, 0, 19, 21, 125.0) angle = rdmt.GetAngleDeg(conf, 0, 19, 21) self.failUnlessAlmostEqual(angle, 125.0, 1) rdmt.SetAngleRad(conf, 21, 19, 0, math.pi / 2.) angle = rdmt.GetAngleRad(conf, 0, 19, 21) self.failUnlessAlmostEqual(angle, math.pi / 2., 1) angle = rdmt.GetAngleDeg(conf, 0, 19, 21) self.failUnlessAlmostEqual(angle, 90.0, 1)
def testUFFAngleConstraints(self): m = Chem.MolFromMolBlock(self.molB, True, False) ff = ChemicalForceFields.UFFGetMoleculeForceField(m) self.failUnless(ff) ff.UFFAddAngleConstraint(1, 3, 6, False, 90.0, 90.0, 1.0e5) r = ff.Minimize() self.failUnless(r == 0) conf = m.GetConformer() angle = rdMolTransforms.GetAngleDeg(conf, 1, 3, 6) self.failUnless(int(angle) == 90) ff = ChemicalForceFields.UFFGetMoleculeForceField(m) self.failUnless(ff) ff.UFFAddAngleConstraint(1, 3, 6, True, -10.0, 10.0, 1.0e5) r = ff.Minimize() self.failUnless(r == 0) conf = m.GetConformer() angle = rdMolTransforms.GetAngleDeg(conf, 1, 3, 6) self.failUnless(int(angle) == 100)
def test_measure_angles(acetone): """ Make sure we can correctly measure all of the angles in the molecule. """ angle_values = acetone.measure_angles() rdkit_mol = acetone.to_rdkit() for angle, value in angle_values.items(): assert (pytest.approx( rdMolTransforms.GetAngleDeg(rdkit_mol.GetConformer(), *angle)) == value)
def get_angs(f, atid=None): #rdkit mol = Chem.MolFromMolFile(f, removeHs=False, sanitize=False) c = mol.GetConformer() if atid == None: atid = enumerateAngles(f) angs = [] for i in range(len(atid)): m = mol.GetAtomWithIdx(atid[i][1]) if m.GetHybridization() != Chem.HybridizationType.SP: angs.append(rdt.GetAngleDeg(c, atid[i][0], atid[i][1], atid[i][2])) return np.array(angs), atid
def smiles_tors(): # Given a pdb file, translate into Smiles string (with possible given starting atom) # and generate a list of rotatable bonds. Rotatable bond list is written to CHARMM # stream files (mc_add.str, mc_delete.str) that are used by the MC module used by EnzyDock. # A crucial assumption is that the rotatable bonds are written in an "outwards" manner # when covalent docking is done (otherwise CHARMM could rotate protein and not ligand!) pdb_file = sys.argv[1] pdb_file = pdb_file.lower() # CHARMM sends in upper case parameters, change to lower print("Parsing ligand file: ",pdb_file) mcfile1 = "../stream/mc/mc_add.str" mcfile2 = "../stream/mc/mc_delete.str" # Write the mc_add.str file for EnzyDock file = open(mcfile1,"w") print_part1(file) print("Writing file: ", mcfile1) try: mol_pdb = Chem.MolFromPDBFile(pdb_file) num_atoms = mol_pdb.GetNumAtoms(onlyExplicit=True) except: print("Error in RDKit read of ligand pdb file %s ..." % pdb_file) print("Stopping EnzyDock ...\n") file.write("\nstop\n") print("Number of ligand atoms: ",num_atoms) idx = -1 # Find atom that serves as seed (covalently bonded) if len(sys.argv) > 2: for i in range(num_atoms): atom = mol_pdb.GetAtomWithIdx(i) # print(atom.GetPDBResidueInfo().GetName().strip()) if (atom.GetPDBResidueInfo().GetName().strip() == sys.argv[2]): idx = atom.GetIdx() smiles = Chem.MolToSmiles(mol_pdb,True,False,idx) print("Ligand Smiles: ",smiles) map = mol_pdb.GetProp('_smilesAtomOutputOrder') atomorder = [int(i) for i in map.replace('[','').replace(']','').split(',') if i != ''''''] orderarray = np.array(atomorder) indx_array = np.empty(num_atoms) # index array for i in range(num_atoms): indx = int(orderarray[i]) # print(i,indx,mol_pdb.GetAtomWithIdx(indx).GetPDBResidueInfo().GetName()) indx_array[i] = indx mol = Chem.MolFromSmiles(smiles) rot_atom_pairs = mol.GetSubstructMatches(RotatableBondSmarts) print(rot_atom_pairs) ntors = len(rot_atom_pairs) k = 0 for i in range(len(rot_atom_pairs)): indx1 = int(indx_array[ rot_atom_pairs[i][0] ]) indx2 = int(indx_array[ rot_atom_pairs[i][1] ]) atom1 = mol_pdb.GetAtomWithIdx(indx1).GetPDBResidueInfo().GetName() atom2 = mol_pdb.GetAtomWithIdx(indx2).GetPDBResidueInfo().GetName() iatom1 = mol_pdb.GetAtomWithIdx(indx1) iatom2 = mol_pdb.GetAtomWithIdx(indx2) #print(atom1, atom2, iatom1, iatom2) conf=mol_pdb.GetConformer(0) # Check if any angle is linear (torsion not defined). Using 10 degrees as cutoff. tors_flag = True for nn in iatom1.GetNeighbors(): indx3 = nn.GetIdx() if (indx3 != indx2): angle = rdMolTransforms.GetAngleDeg(conf,indx3, indx1, indx2) #print(i, indx1, indx2, indx3, angle) if (abs(180.0 - abs(angle)) < 10.0): tors_flag = False for nn in iatom2.GetNeighbors(): indx3 = nn.GetIdx() if (indx3 != indx1): angle = rdMolTransforms.GetAngleDeg(conf,indx1, indx2, indx3) #print(i, indx1, indx2, indx3, angle) if (abs(180.0 - abs(angle)) < 10.0): tors_flag = False if (tors_flag): j = k + 3 line = "move add mvtp tors weight 1.00 dmax 180.0 label tr" + str(j) + " fewer 0 sele atom ligand_@currligand 1 " \ + str(atom1) + " show end -\n" file.write(line) line = " sele atom ligand_@currligand 1 " \ + str(atom2) + " show end\n" file.write(line) k += 1 else: ntors -= 1 line = "\nincr ntors by " + str(ntors) + "\n\n" file.write(line) print_part2(file) file.close() # Write the mc_delete.str file for EnzyDock file = open(mcfile2,"w") print("Writing file: ", mcfile2) print_part3(file) for i in range(ntors): j = i + 3 line = "move dele label tr" + str(j) + "\n" file.write(line) line = "decr ntors by " + str(ntors) + "\n" file.write(line) print_part4(file) file.close() # print(Chem.MolToMolBlock(mol_pdb)) # print(Chem.MolToMolBlock(mol)) map = mol_pdb.GetProp('_smilesAtomOutputOrder') atomorder = [int(i) for i in map.replace('[','').replace(']','').split(',') if i != ''''''] orderarray = np.array(atomorder) for i in range(num_atoms): indx = int(orderarray[i])
def exp_rules_output(mol, args, log): if args.exp_rules == 'Ir_bidentate_x3': passing = True ligand_links = [] atom_indexes = [] for atom in mol.GetAtoms(): # Finds the Ir atom and gets the atom types and indexes of all its neighbours if atom.GetSymbol() in args.metal: atomic_number = possible_atoms.index(atom.GetSymbol()) atom.SetAtomicNum(atomic_number) for atom in mol.GetAtoms(): if atom.GetAtomicNum() == atomic_number: metal_idx = atom.GetIdx() for x in atom.GetNeighbors(): ligand_links.append(x.GetSymbol()) atom_indexes.append(x.GetIdx()) # I need to get the only 3D conformer generated in that mol object for rdMolTransforms mol_conf = mol.GetConformer(0) # This part will identify the pairs of C and N atoms that are part of the same Ph_Py ligand. # The shape of the atom pairs is '[[C1_ATOM_NUMBER, N1_ATOM_NUMBER],[C2, N2],...]'. # This information is required for the subsequent filtering process based on angles if len(atom_indexes) == args.complex_coord[0]: ligand_atoms = [] for i, _ in enumerate(atom_indexes): # This is a filter that excludes molecules that fell apart during DFT geometry # optimization (i.e. a N atom from one of the ligands separated from Ir). The # max distance allowed can be tuned in length_filter bond_length = rdMolTransforms.GetBondLength( mol_conf, metal_idx, atom_indexes[i]) if ligand_links[i] == 'P': length_filter = 2.60 else: length_filter = 2.25 if bond_length > length_filter: passing = False break for j, _ in enumerate(atom_indexes): # Avoid combinations of the same atom with itself if atom_indexes[i] != atom_indexes[j]: # We know that the ligands never have 2 carbon atoms bonding the Ir atom. We # only use atom_indexes[i] for C atoms, and atom_indexes[j] for the potential # N atoms that are part of the same Ph_Py ligand if ligand_links[i] == 'C': # This part detects the Ir-C bond and breaks it, breaking the Ph_Py ring bond = mol.GetBondBetweenAtoms( atom_indexes[i], metal_idx) new_mol = Chem.FragmentOnBonds( mol, [bond.GetIdx()], addDummies=True, dummyLabels=[(atom_indexes[i], metal_idx)]) if new_mol.GetAtomWithIdx( atom_indexes[i]).IsInRingSize(5): five_mem = True else: five_mem = False # identify whether or not the initial 5-membered ring formed between [-Ir-C-C-C-N-] is broken when we break the Ir-C bond. This works # because Ph_Py units bind Ir in the same way always, through 1 C and 1 N that are in the same position, forming a 5-membered ring. # If this ring is broken, atom_indexes[j] will not be part of a 5-membered ring (atom.IsInRingSize(5) == False) which means that # this atom was initially inside the same ligand as the parent C of atom_indexes[i]) if not five_mem: if not new_mol.GetAtomWithIdx( atom_indexes[j]).IsInRingSize(5): bond_2 = mol.GetBondBetweenAtoms( atom_indexes[j], metal_idx) new_mol_2 = Chem.FragmentOnBonds( mol, [bond_2.GetIdx()], addDummies=True, dummyLabels=[(atom_indexes[j], metal_idx)]) #doing backwards as well eg. Ir N bond if not new_mol_2.GetAtomWithIdx( atom_indexes[i]).IsInRingSize(5): ligand_atoms.append( [atom_indexes[i], atom_indexes[j]]) break else: if not new_mol.GetAtomWithIdx( atom_indexes[j]).IsInRingSize(5): if mol.GetAtomWithIdx( atom_indexes[j]).IsInRingSize(5): ligand_atoms.append( [atom_indexes[i], atom_indexes[j]]) break if passing: # This stop variable and the breaks inside the inner loops will break the nested loop if there # is one angle that does not meet the criteria for valid conformers stop = False # For complexes with 3 Ph_Py ligands: if len(ligand_atoms) == 3: for i, _ in enumerate(ligand_atoms): if not stop: for j, _ in enumerate(ligand_atoms): # the i<=j part avoids repeating atoms, the i != j part avoid angles # containing the same number twice (i.e. 4-16-4, this angle will fail) if i <= j and i != j: # Calculate the angle between 2 N atoms from different Ph_Py ligands. # When there are 3 Ph_Py ligands, no 2 N atoms must be in 180 degrees angle = rdMolTransforms.GetAngleDeg( mol_conf, ligand_atoms[i][1], metal_idx, ligand_atoms[j][1]) if (180 - args.angle_off) <= angle <= ( 180 + args.angle_off): passing = False break # For complexes with 2 Ph_Py ligands + 1 ligand that is not Ph_Py if len(ligand_atoms) == 2: # Since there are only 2 N atoms, we do not need to include a nested loop angle = rdMolTransforms.GetAngleDeg( mol_conf, ligand_atoms[0][1], metal_idx, ligand_atoms[1][1]) # Calculate the angle between 2 N atoms from different Ph_Py ligands. # When there are 2 Ph_Py ligands, the 2 N atoms from the 2 Ph_Py ligands must be in 180 degrees if (180 - args.angle_off) <= angle <= (180 + args.angle_off): pass else: passing = False # it filters off molecules that the SDF only detects 5 Ir neighbours else: passing = False return passing