def testMMFFTorsionConstraints(self): m = Chem.MolFromMolBlock(self.molB, True, False) conf = m.GetConformer() rdMolTransforms.SetDihedralDeg(conf, 1, 3, 6, 8, 15.0) mp = ChemicalForceFields.MMFFGetMoleculeProperties(m) ff = ChemicalForceFields.MMFFGetMoleculeForceField(m, mp) self.assertTrue(ff) ff.MMFFAddTorsionConstraint(1, 3, 6, 8, False, 10.0, 20.0, 100.0) r = ff.Minimize() self.assertTrue(r == 0) dihedral = rdMolTransforms.GetDihedralDeg(conf, 1, 3, 6, 8) self.assertEquals(int(dihedral), 20) rdMolTransforms.SetDihedralDeg(conf, 1, 3, 6, 8, -30.0) ff = ChemicalForceFields.MMFFGetMoleculeForceField(m, mp) self.assertTrue(ff) ff.MMFFAddTorsionConstraint(1, 3, 6, 8, True, -10.0, 8.0, 100.0) r = ff.Minimize() self.assertTrue(r == 0) conf = m.GetConformer() dihedral = rdMolTransforms.GetDihedralDeg(conf, 1, 3, 6, 8) self.assertTrue(int(dihedral) == -40) rdMolTransforms.SetDihedralDeg(conf, 1, 3, 6, 8, -10.0) ff = ChemicalForceFields.MMFFGetMoleculeForceField(m, mp) self.assertTrue(ff) ff.MMFFAddTorsionConstraint(1, 3, 6, 8, False, -10.0, 8.0, 100.0) r = ff.Minimize(1000) self.assertTrue(r == 0) conf = m.GetConformer() dihedral = rdMolTransforms.GetDihedralDeg(conf, 1, 3, 6, 8) self.assertTrue(int(dihedral) == -10)
def testUFFTorsionConstraints(self): m = Chem.MolFromMolBlock(self.molB, True, False) conf = m.GetConformer() rdMolTransforms.SetDihedralDeg(conf, 1, 3, 6, 8, 15.0) ff = ChemicalForceFields.UFFGetMoleculeForceField(m) self.assertTrue(ff) ff.UFFAddTorsionConstraint(1, 3, 6, 8, False, 10.0, 20.0, 100.0) r = ff.Minimize() self.assertTrue(r == 0) dihedral = rdMolTransforms.GetDihedralDeg(conf, 1, 3, 6, 8) self.assertAlmostEqual(dihedral, 20, delta=0.1) rdMolTransforms.SetDihedralDeg(conf, 1, 3, 6, 8, -30.0) ff = ChemicalForceFields.UFFGetMoleculeForceField(m) self.assertTrue(ff) ff.UFFAddTorsionConstraint(1, 3, 6, 8, True, -10.0, 8.0, 100.0) r = ff.Minimize() self.assertTrue(r == 0) conf = m.GetConformer() dihedral = rdMolTransforms.GetDihedralDeg(conf, 1, 3, 6, 8) self.assertAlmostEqual(dihedral, -40, delta=0.1) rdMolTransforms.SetDihedralDeg(conf, 1, 3, 6, 8, -10.0) ff = ChemicalForceFields.UFFGetMoleculeForceField(m) self.assertTrue(ff) ff.UFFAddTorsionConstraint(1, 3, 6, 8, False, -10.0, 8.0, 1.0e6) r = ff.Minimize(500) self.assertTrue(r == 0) conf = m.GetConformer() dihedral = rdMolTransforms.GetDihedralDeg(conf, 1, 3, 6, 8) self.assertAlmostEqual(dihedral, -10, delta=0.1)
def testGetSetDihedralThroughTripleBond(self): file = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'MolTransforms', 'test_data', 'github1262_2.mol') m = Chem.MolFromMolFile(file, True, False) conf = m.GetConformer() rdmt.SetDihedralDeg(conf, 6, 1, 2, 9, 0.0) dihedral = rdmt.GetDihedralDeg(conf, 6, 1, 2, 9) self.assertAlmostEqual(dihedral, 0.0, 1) dist = rdmt.GetBondLength(conf, 6, 9) rdmt.SetDihedralDeg(conf, 6, 1, 2, 9, 120.0) dihedral = rdmt.GetDihedralDeg(conf, 6, 1, 2, 9) self.assertAlmostEqual(dihedral, 120.0, 1) dist2 = rdmt.GetBondLength(conf, 6, 7) self.assertAlmostEqual(dist, dist2, 1) rdmt.SetDihedralDeg(conf, 6, 1, 2, 9, 180.0) dihedral = rdmt.GetDihedralDeg(conf, 6, 1, 2, 9) self.assertAlmostEqual(dihedral, 180.0, 1) dist3 = rdmt.GetBondLength(conf, 6, 9) self.assertNotAlmostEqual(dist, dist3, 1) exceptionRaised = False try: rdmt.SetDihedralDeg(conf, 6, 0, 3, 9, 0.0) except ValueError: exceptionRaised = True self.assertTrue(exceptionRaised)
def testUFFTorsionConstraints(self) : m = Chem.MolFromMolBlock(self.molB, True, False) conf = m.GetConformer() rdMolTransforms.SetDihedralDeg(conf, 1, 3, 6, 8, 50.0); ff = ChemicalForceFields.UFFGetMoleculeForceField(m) self.assertTrue(ff) ff.UFFAddTorsionConstraint(1, 3, 6, 8, False, 10.0, 20.0, 1.0e5) r = ff.Minimize() self.assertTrue(r == 0) dihedral = rdMolTransforms.GetDihedralDeg(conf, 1, 3, 6, 8) self.assertTrue(int(dihedral) == 20) rdMolTransforms.SetDihedralDeg(conf, 1, 3, 6, 8, -30.0); ff = ChemicalForceFields.UFFGetMoleculeForceField(m) self.assertTrue(ff) ff.UFFAddTorsionConstraint(1, 3, 6, 8, True, -10.0, -8.0, 1.0e5) r = ff.Minimize() self.assertTrue(r == 0) conf = m.GetConformer() dihedral = rdMolTransforms.GetDihedralDeg(conf, 1, 3, 6, 8) self.assertTrue(int(dihedral) == -40) rdMolTransforms.SetDihedralDeg(conf, 1, 3, 6, 8, 30.0); ff = ChemicalForceFields.UFFGetMoleculeForceField(m) self.assertTrue(ff) ff.UFFAddTorsionConstraint(1, 3, 6, 8, False, -10.0, -8.0, 1.0e5) r = ff.Minimize(1000) self.assertTrue(r == 0) conf = m.GetConformer() dihedral = rdMolTransforms.GetDihedralDeg(conf, 1, 3, 6, 8) self.assertTrue(int(dihedral) == -10)
def tinker_minimize_angles(poltype,molecprefix,a,b,c,d,optmol,consttorlist,phaseanglelist,prevstrctfname,torsionrestraint,torang,bondtopology): tinkerstructnamelist=[] # load prevstruct # create xyz and key and write restraint then minimize, getting .xyz_2 for phaseangle in phaseanglelist: # we need to send back minimized structure in XYZ (not tinker) format to load for next tinker minimization,but append the xyz_2 tinker XYZ file so that com file can be generated from that prevstruct = opt.load_structfile(poltype,prevstrctfname) prevstruct = opt.PruneBonds(poltype,prevstruct,bondtopology) # sometimes extra bonds are made when atoms get too close during minimization prevstruct=opt.rebuild_bonds(poltype,prevstruct,optmol) obConversion = openbabel.OBConversion() obConversion.SetInFormat('mol') obConversion.SetOutFormat('mol2') obConversion.WriteFile(prevstruct, 'temp.mol2') try: rdmol=MolFromMol2File('temp.mol2',False,False) except: rdmol=MolFromMol2File('temp.mol2',True,False) conf = rdmol.GetConformer() dihedral = optmol.GetTorsion(a,b,c,d) newdihedral=round((dihedral+phaseangle)%360) rdmt.SetDihedralDeg(conf, a-1, b-1, c-1, d-1, newdihedral) try: print(Chem.MolToMolBlock(rdmol,kekulize=True),file=open('tempout.mol','w+')) except: print(Chem.MolToMolBlock(rdmol,kekulize=False),file=open('tempout.mol','w+')) obConversion.ReadFile(prevstruct, 'tempout.mol') prevstrctfname,torxyzfname,newtorxyzfname,keyfname=tinker_minimize(poltype,molecprefix,a,b,c,d,optmol,consttorlist,phaseangle,torsionrestraint,prevstruct,torang,'_preQMOPTprefit',poltype.key4fname,'../') tinkerstructnamelist.append(newtorxyzfname) return tinkerstructnamelist
def generate_dihedral_conformers(self, resolution): """ Given a resolution, it generates all the possible conformers that come out when rotating the dihedral's rotatable bond. Parameters ---------- resolution : float The resolution, in degrees, that is applied when rotating the dihedral rotatable bond. Default is 30 degrees Returns ------- rdkit_mol : an rdkit.Chem.rdchem.Mol object The RDKit's Molecule object with the conformers that were generated """ from copy import deepcopy from rdkit.Chem import rdMolTransforms rdkit_mol = deepcopy(self.molecule.rdkit_molecule) conformer = rdkit_mol.GetConformer() for angle in range(0, 360, resolution): rdMolTransforms.SetDihedralDeg(conformer, *self.atom_indexes, angle) rdkit_mol.AddConformer(conformer, assignId=True) # Remove initial conformer (which is repeated) rdkit_mol.RemoveConformer(conformer.GetId()) return rdkit_mol
def test12CrippenO3A(self): " test CrippenO3A with constraints " #we superimpose two identical coplanar 4-phenylpyridines: #1) the usual way #2) forcing the pyridine nitrogen to match with the para # carbon of the phenyl ring m = Chem.MolFromSmiles('n1ccc(cc1)-c1ccccc1') m1 = Chem.AddHs(m) rdDistGeom.EmbedMolecule(m1) mp = ChemicalForceFields.MMFFGetMoleculeProperties(m1) ff = ChemicalForceFields.MMFFGetMoleculeForceField(m1, mp) ff.Minimize() sub1 = m1.GetSubstructMatch(Chem.MolFromSmarts('nccc-cccc')) nIdx = sub1[0] cIdx = sub1[-1] dihe = sub1[2:6] rdMolTransforms.SetDihedralDeg(m1.GetConformer(), dihe[0], dihe[1], dihe[2], dihe[3], 0) m2 = copy.copy(m1) rdMolAlign.RandomTransform(m2) m3 = copy.copy(m2) pyO3A = rdMolAlign.GetCrippenO3A(m2, m1) pyO3A.Align() d = m2.GetConformer().GetAtomPosition(cIdx). \ Distance(m1.GetConformer().GetAtomPosition(cIdx)) self.assertAlmostEqual(d, 0, 0) pyO3A = rdMolAlign.GetCrippenO3A(m3, m1, constraintMap=[[cIdx, nIdx]]) pyO3A.Align() d = m3.GetConformer().GetAtomPosition(cIdx). \ Distance(m1.GetConformer().GetAtomPosition(cIdx)) self.assertAlmostEqual(d, 7, 0)
def _get_child_states(self): child_states = [] if len(self.state[1]) < len(self._rotatable_bonds): mol = self.state[0] conf = mol.GetConformer() mol.AddConformer(conf, assignId=True) bond_to_rotate = self._rotatable_bonds[len(self.state[1])] theta = rdMolTransforms.GetDihedralDeg(conf, bond_to_rotate[0], bond_to_rotate[1], bond_to_rotate[2], bond_to_rotate[3]) for allowed_angle_value in self._allowed_angle_values: new_theta = theta + allowed_angle_value rdMolTransforms.SetDihedralDeg(conf, bond_to_rotate[0], bond_to_rotate[1], bond_to_rotate[2], bond_to_rotate[3], new_theta) mol.AddConformer(conf, assignId=True) mol.RemoveConformer(conf.GetId()) for i, conf in enumerate(mol.GetConformers()): theta = rdMolTransforms.GetDihedralDeg(conf, bond_to_rotate[0], bond_to_rotate[1], bond_to_rotate[2], bond_to_rotate[3]) child_mol = Chem.Mol(mol, False, conf.GetId()) child_states.append((child_mol, self.state[1] + [theta])) return child_states
def set_dihedrals(self, list_of_changes): """ Change the dihedrals of the RDkit molecule. The molecule is not optimised """ current_angles = self.get_dihedrals() conf = self.molecule.GetConformer() for change, current in zip(list_of_changes, current_angles): rdMolTransforms.SetDihedralDeg(conf, current[0], current[1], current[2], current[3], change) self.dihedrals = list_of_changes
def testGetSetDihedral(self): file = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'MolTransforms', 'test_data', '3-cyclohexylpyridine.mol') m = Chem.MolFromMolFile(file, True, False) conf = m.GetConformer() dihedral = rdmt.GetDihedralDeg(conf, 0, 19, 21, 24) self.failUnlessAlmostEqual(dihedral, 176.05, 2) rdmt.SetDihedralDeg(conf, 8, 0, 19, 21, 65.0) dihedral = rdmt.GetDihedralDeg(conf, 8, 0, 19, 21) self.failUnlessAlmostEqual(dihedral, 65.0, 1) rdmt.SetDihedralDeg(conf, 8, 0, 19, 21, -130.0) dihedral = rdmt.GetDihedralDeg(conf, 8, 0, 19, 21) self.failUnlessAlmostEqual(dihedral, -130.0, 1) rdmt.SetDihedralRad(conf, 21, 19, 0, 8, -2. / 3. * math.pi) dihedral = rdmt.GetDihedralRad(conf, 8, 0, 19, 21) self.failUnlessAlmostEqual(dihedral, -2. / 3. * math.pi, 1) dihedral = rdmt.GetDihedralDeg(conf, 8, 0, 19, 21) self.failUnlessAlmostEqual(dihedral, -120.0, 1)
def set_torsion_angles(self, conf, angles, reflect=False): """ reset the torsion angles and update molecular xyz """ from rdkit.Chem import rdMolTransforms as rdmt for id, torsion in enumerate(self.torsionlist): (i, j, k, l) = torsion rdmt.SetDihedralDeg(conf, i, j, k, l, angles[id]) xyz = self.align(conf, reflect) return xyz
def SetTorsionDeg(self, torsion: list, degree: Union[float, int]): """ Set the dihedral angle of the torsion in degrees. The torsion can only be defined by a chain of bonded atoms. Args: torsion (list): A list of four atom indexes. degree (float, int): The dihedral angle of the torsion. """ rdMT.SetDihedralDeg(self._conf, *torsion, degree)
def _select_next_move_randomly(self, state): mol = Chem.Mol(state[0]) # create a copy of the Mol conf = mol.GetConformer() bond_to_rotate = self._rotatable_bonds[len(state[1])] theta = rdMolTransforms.GetDihedralDeg(conf, bond_to_rotate[0], bond_to_rotate[1], bond_to_rotate[2], bond_to_rotate[3]) theta += np.random.choice(self._allowed_angle_values) rdMolTransforms.SetDihedralDeg(conf, bond_to_rotate[0], bond_to_rotate[1], bond_to_rotate[2], bond_to_rotate[3], theta) conformer_id = mol.AddConformer(conf, assignId=True) return Chem.Mol(mol, False, conformer_id), state[1] + [theta]
def testUFFTorsionConstraints(self): m = Chem.MolFromMolBlock(self.molB, True, False) ff = ChemicalForceFields.UFFGetMoleculeForceField(m) self.failUnless(ff) conf = m.GetConformer() rdMolTransforms.SetDihedralDeg(conf, 1, 3, 6, 8, 60.0) ff.UFFAddTorsionConstraint(1, 3, 6, 8, False, 30.0, 30.0, 1.0e5) r = ff.Minimize() self.failUnless(r == 0) dihedral = rdMolTransforms.GetDihedralDeg(conf, 1, 3, 6, 8) self.failUnless(int(dihedral) == 30) ff = ChemicalForceFields.UFFGetMoleculeForceField(m) self.failUnless(ff) ff.UFFAddTorsionConstraint(1, 3, 6, 8, True, -10.0, 10.0, 1.0e5) r = ff.Minimize() self.failUnless(r == 0) conf = m.GetConformer() dihedral = rdMolTransforms.GetDihedralDeg(conf, 1, 3, 6, 8) self.failUnless(int(dihedral) == 40)
def systematic_conformers(mol, init_rdkit_mol, theta=120., charged_fragments=True, ff_variant='UFF', max_iters=200, threads=1, check_stero=False): """Systematic rotation of dihedral angles theta degrees""" # find dihedrals of parent molecule and create all combinations # where the angles are rotated theta degrees. dihedral_idx = RotatableBonds(mol) conf = mol.GetConformer() dihedrals = list() for k, i, j, l in dihedral_idx: mol_dihedral = rdMolTransforms.GetDihedralDeg(conf, k, i, j, l) new_dihedrals = [ mol_dihedral + x * theta for x in range(int(360. / theta)) ] dihedrals.append(new_dihedrals) dihedralCombs = list(itertools.product(*dihedrals)) # Create the conformations according to angle combinations # in dihedralCombs. for idx, dihedrals in enumerate(dihedralCombs): for (k, i, j, l), angle in zip(dihedral_idx, dihedrals): rdMolTransforms.SetDihedralDeg(conf, k, i, j, l, angle) mol.AddConformer(conf, assignId=True) # Clean conformers. Test if structure is corrupted or the stereo # chemistry changed. ff_clean(mol, init_rdkit_mol, ff_variant, max_iters, threads, charged_fragments, check_stero) return mol
def dihedral_set(sdf_string, position, value): """ Set the dihedral angle. Args: sdf_string (string): position (list): 4 atoms defining the dihedral value : value to set Returns: modified sdf_string Raises: ValueError: If the lenght of the list is not equal 4. """ if len(position) != 4: raise ValueError("The position needs to be defined by 4 integers") mol = Chem.MolFromMolBlock(sdf_string, removeHs=False) rdMolTransforms.SetDihedralDeg(mol.GetConformer(), ig(0)(position), ig(1)(position), ig(2)(position), ig(3)(position), value) return Chem.MolToMolBlock(mol)
def get_conformations(mol, max_number_of_conformations): raw_rot_bonds = mol.GetSubstructMatches( Chem.MolFromSmarts("[!#1]~[!$(*#*)&!D1]-!@[!$(*#*)&!D1]~[!#1]")) raw_rot_bonds += mol.GetSubstructMatches( Chem.MolFromSmarts("[*]~[*]-[O,S]-[#1]")) raw_rot_bonds += mol.GetSubstructMatches( Chem.MolFromSmarts("[*]~[*]-[NX3;H2]-[#1]")) bonds = [] rot_bonds = [] for k, i, j, l in raw_rot_bonds: if (i, j) not in bonds: bonds.append((i, j)) rot_bonds.append((k, i, j, l)) conf = mol.GetConformer() angles = [] for k, i, j, l in rot_bonds: theta = rdMolTransforms.GetDihedralDeg(conf, k, i, j, l) angles.append([theta, theta + 120., theta - 120.]) angle_combinations = list(itertools.product(*angles)) number_of_conformations = len(angle_combinations) if number_of_conformations > max_number_of_conformations: angle_combinations = get_subset(number_of_conformations, max_number_of_conformations, angle_combinations) for angle_combination in angle_combinations: for (k, i, j, l), angle in zip(rot_bonds, angle_combination): rdMolTransforms.SetDihedralDeg(conf, k, i, j, l, angle) mol.AddConformer(conf, assignId=True) #print mol.GetNumConformers() return mol
def set_rdkit_dihedrals(conf, rd_mol, index_map, rd_scan, deg_increment=None, deg_abs=None): """ A helper function for setting dihedral angles `conf` is the RDKit conformer with the current xyz information `rd_mol` is the RDKit molecule `indx_map` is an atom index mapping dictionary, keys are xyz_index, values are rd_index `rd_scan` is the torsion scan atom indices corresponding to the RDKit conformer indices Either `deg_increment` or `deg_abs` must be specified for the dihedral increment Returns xyz in an array format ordered according to the map, the elements in the xyz should be identified by the calling function from the context """ if deg_increment is None and deg_abs is None: raise SpeciesError('Cannot set dihedral without either a degree increment or an absolute degree') if deg_increment is not None: deg0 = rdMT.GetDihedralDeg(conf, rd_scan[0], rd_scan[1], rd_scan[2], rd_scan[3]) # get original dihedral deg = deg0 + deg_increment else: deg = deg_abs rdMT.SetDihedralDeg(conf, rd_scan[0], rd_scan[1], rd_scan[2], rd_scan[3], deg) new_xyz = list() for i in range(rd_mol.GetNumAtoms()): new_xyz.append([conf.GetAtomPosition(index_map[i]).x, conf.GetAtomPosition(index_map[i]).y, conf.GetAtomPosition(index_map[i]).z]) return new_xyz
def get_energies(self, resolution=30, get_thetas=False): """ It returns the energies of this energetic profile calculator. Parameters ---------- resolution : float The resolution, in degrees, that is applied when rotating the dihedral rotatable bond. Default is 30 degrees get_thetas : bool Whether to return thetas (dihedral angles) or not. Default is False Returns ------- dihedral_energies : a simtk.unit.Quantity object The array of energies represented with a simtk's Quantity object thetas : list[float] The array of thetas, only if requested in the corresponding function parameter """ import numpy as np from copy import deepcopy from simtk import unit from rdkit.Chem import rdMolTransforms xs = unit.Quantity(np.arange(0, 360, resolution), unit=unit.degrees) ys = unit.Quantity(np.zeros(len(xs)), unit=(unit.kilocalorie / unit.mole)) rdkit_mol = deepcopy(self.dihedral_benchmark.molecule.rdkit_molecule) conformer = rdkit_mol.GetConformer() propers = self.dihedral_benchmark.molecule.propers a1, a2 = self.dihedral_benchmark.rotatable_bond for proper in propers: rot_bond = set((proper.atom2_idx, proper.atom3_idx)) if a1 in rot_bond and a2 in rot_bond: if proper.constant == 0: continue constant = proper.constant prefactor = proper.prefactor periodicity = proper.periodicity phase = proper.phase for j, x in enumerate(xs): rdMolTransforms.SetDihedralDeg( conformer, *self.dihedral_benchmark.atom_indexes, float(x.value_in_unit(unit.degree))) theta = unit.Quantity(value=rdMolTransforms.GetDihedralDeg( conformer, *indexes), unit=unit.degree) ys[j] += constant * ( 1 + prefactor * np.cos(periodicity * theta.value_in_unit(unit.radian) - phase.value_in_unit(unit.radian))) if get_thetas: return ys, xs return ys
def changeAndOpt(rdkit, theta): Chem.SanitizeMol(rdkit) initconf = rdkit.GetConformer() # set outer most dihedral to 180 degrees. smarts_patt = "C-S-C-[C,Si,Ge;H0]" outer_dihedral_idx = find_dihedral_idx(rdkit, smarts_patt) for k, i, j, l in outer_dihedral_idx: rdMolTransforms.SetDihedralDeg(initconf, k, i, j, l, 180.0) # change second outmost dihedral with +-120 degrees. patt = "S-C-[C,Si,Ge;H0]-[C,Si,Ge]" dihedral_idx = find_dihedral_idx(rdkit, patt) new_angles = list() for k, i, j, l in dihedral_idx: init_dihedral_angle = rdMolTransforms.GetDihedralDeg( initconf, k, i, j, l) new_angles.append([ init_dihedral_angle + x * theta for x in range(int(360. / theta)) ]) angle_combinations = list( itertools.product(*new_angles)) # all combinations. for dihedrals in angle_combinations: for (k, i, j, l), angle in zip(dihedral_idx, dihedrals): rdMolTransforms.SetDihedralDeg(initconf, k, i, j, l, angle) rdkit.AddConformer(initconf, assignId=True) rdMolAlign.AlignMolConformers(rdkit) mol_list = list() for idx, conf in enumerate(rdkit.GetConformers()): if idx == 0: continue sdf_txt = Chem.SDWriter.GetText(rdkit, conf.GetId()) m = Chem.MolFromMolBlock(sdf_txt, removeHs=False) conf_name = m.GetProp("_Name") + "-" + str(idx - 1) m.SetProp("_Name", conf_name) mol_list.append(m) # Optimize structures with new dihedrals. confqmmol = QMMol(mol_list, fmt="mol_list", charge=0, multi=1, charged_fragments=True) confqmmol.optimize(program="xtb", method="opt", cpus=24, babelAC=True) # Write xyz files of conformers for newConf in confqmmol.GetConformers(): obConversion = openbabel.OBConversion() obConversion.SetInAndOutFormats("sdf", "xyz") newConfm = openbabel.OBMol() obConversion.ReadString(newConfm, Chem.MolToMolBlock(newConf)) new_xyz = obConversion.WriteString(newConfm) with open(newConf.GetProp("_Name") + ".xyz", 'w') as f: f.write(new_xyz)
def main(): import argparse parser = argparse.ArgumentParser() parser.add_argument('-s', '--smiles', action='store', help='', metavar="str") parser.add_argument('-f', '--filename', action='store', help='', metavar="filename", default="hash.sdf") parser.add_argument('-a', '--align', action='store_true', help='align molecules') parser.add_argument('--scan', action='store_true', help='scan angles') parser.add_argument('--scan_theta', action='store', type=float, help='', metavar="angle", default=170.0) parser.add_argument('--add_hydrogens', action='store_true', help='Add hydrogens', default=True) args = parser.parse_args() if not args.smiles: print("No smiles set") quit() m = Chem.MolFromSmiles(args.smiles) smart = "~".join(["[*]"]*4) idx_list = m.GetSubstructMatches(Chem.MolFromSmarts(smart)) m = Chem.AddHs(m) # if args.add_hydrogens: # m = Chem.AddHs(m) # AllChem.EmbedMolecule(m, AllChem.ETKDG()) AllChem.EmbedMultipleConfs(m, numConfs=1, useExpTorsionAnglePrefs=True, useBasicKnowledge=True) # AllChem.EmbedMultipleConfs(m, numConfs=1, useBasicKnowledge=True) atoms = m.GetAtoms() # for i, a in enumerate(atoms): # print(i, a.GetAtomicNum()) cm = m.GetConformer() if args.scan: for x in range(0, 1080, 10): rdMolTransforms.SetDihedralDeg(cm, 0, 1, 2, 3, x) m.AddConformer(cm, assignId=True) # if args.scan: # set_conformers_random(m, 1) # set_dehedral_angles(m, theta=args.scan_theta) else: n_conformers = 70 # n_conformers = rdMolDescriptors.CalcNumRotatableBonds(m) m = get_conformers(args.smiles, n_conformers) # Alignment if args.align: if True: Chem.rdMolAlign.AlignMolConformers(m) # if True: # for i, m in enumerate(m.GetConformations()): # if i == 0: continue # # # m.AddConformer(parent, assignId=True) writer = Chem.SDWriter(args.filename) for i in range(m.GetNumConformers()): writer.write(m, i) return
def set_dehedral_angles(m, theta=120.0, rotate_general=True, rotate_ol=True, rotate_ine=True): """ Systematic rotation of dihedral angles theta degrees Taken from Mads """ rotate_idx_list = list() if rotate_general: smart = "[!#1]~[!$(*#*)&!D1]-!@[!$(*#*)&!D1]~[!#1]" rotate_idx_list += m.GetSubstructMatches(Chem.MolFromSmarts(smart)) if rotate_ol: smart = "[*]~[*]-[O,S]-[#1]" rotate_idx_list += m.GetSubstructMatches(Chem.MolFromSmarts(smart)) if rotate_ine: smart = "[*]~[*]-[NX3;H2]-[#1]" rotate_idx_list += m.GetSubstructMatches(Chem.MolFromSmarts(smart)) # Find unique bonds and dihedral angles indexes idx_bonds = list() idx_dihedral = list() atoms = m.GetAtoms() for k, i, j, l in rotate_idx_list: if (i,j) in idx_bonds: continue idx_bonds.append((i,j)) idx_dihedral.append((k,i,j,l)) print("found", k,i,j,l) print(atoms[k].GetAtomicNum()) print(atoms[i].GetAtomicNum()) print(atoms[j].GetAtomicNum()) print(atoms[l].GetAtomicNum()) # find dihedrals of parent molecule and create all combinations # where the angles are rotated theta degrees. parent = m.GetConformer() # List of alle moveable angles dihedrals = list() for k, i, j, l in idx_dihedral: parent_dihedral = rdMolTransforms.GetDihedralDeg(parent, k, i, j, l) new_dihedrals = [ x*theta for x in range(int(360./theta))] print(new_dihedrals) dihedrals.append(new_dihedrals) # make all possible combinations of dihedral angles dihedral_combinations = list(itertools.product(*dihedrals)) # Create the conformations according to angle combinations for dihedrals in dihedral_combinations: for (k,i,j,l), angle in zip(idx_dihedral, dihedrals): print(k,i,j,l, angle) rdMolTransforms.SetDihedralDeg(parent, k, i, j, l, angle) # translate mol to centroid rdMolTransforms.CanonicalizeConformer(parent) m.AddConformer(parent, assignId=True) return m
def summ_search(mol, name, args, log, dup_data, dup_data_idx, coord_Map=None, alg_Map=None, mol_template=None): '''embeds core conformers, then optimizes and filters based on RMSD. Finally the rotatable torsions are systematically rotated''' sdwriter = Chem.SDWriter(name + '_' + 'rdkit' + args.output) Chem.SanitizeMol(mol) mol = Chem.AddHs(mol) mol.SetProp("_Name", name) # detects and applies auto-detection of initial number of conformers if args.sample == 'auto': initial_confs = int(auto_sampling(args.auto_sample, mol, log)) else: initial_confs = int(args.sample) # dup_data.at[dup_data_idx, 'Molecule'] = name dup_data.at[dup_data_idx, 'RDKIT-Initial-samples'] = initial_confs if args.nodihedrals == False: rotmatches = getDihedralMatches(mol, args.heavyonly, log) else: rotmatches = [] if len(rotmatches) > args.max_torsions: log.write("x Too many torsions (%d). Skipping %s" % (len(rotmatches), (name + args.output))) status = -1 else: if coord_Map == None and alg_Map == None and mol_template == None: if args.etkdg: ps = Chem.ETKDG() ps.randomSeed = args.seed ps.ignoreSmoothingFailures = True ps.numThreads = 0 cids = rdDistGeom.EmbedMultipleConfs(mol, initial_confs, params=ps) else: cids = rdDistGeom.EmbedMultipleConfs( mol, initial_confs, ignoreSmoothingFailures=True, randomSeed=args.seed, numThreads=0) if len(cids) == 0 or len(cids) == 1 and initial_confs != 1: log.write( "o conformers initially sampled with random coordinates") cids = rdDistGeom.EmbedMultipleConfs( mol, initial_confs, randomSeed=args.seed, useRandomCoords=True, boxSizeMult=10.0, ignoreSmoothingFailures=True, numZeroFail=1000, numThreads=0) if args.verbose: log.write("o " + str(len(cids)) + " conformers initially sampled") # case of embed for templates else: if args.etkdg: ps = Chem.ETKDG() ps.randomSeed = args.seed ps.coordMap = coord_Map ps.ignoreSmoothingFailures = True ps.numThreads = 0 cids = rdDistGeom.EmbedMultipleConfs(mol, initial_confs, params=ps) else: cids = rdDistGeom.EmbedMultipleConfs( mol, initial_confs, randomSeed=args.seed, ignoreSmoothingFailures=True, coordMap=coord_Map, numThreads=0) if len(cids) == 0 or len(cids) == 1 and initial_confs != 1: log.write( "o conformers initially sampled with random coordinates") cids = rdDistGeom.EmbedMultipleConfs( mol, initial_confs, randomSeed=args.seed, useRandomCoords=True, boxSizeMult=10.0, numZeroFail=1000, ignoreSmoothingFailures=True, coordMap=coord_Map, numThreads=0) if args.verbose: log.write("o " + str(len(cids)) + " conformers initially sampled") #energy minimize all to get more realistic results #identify the atoms and decide Force Field for atom in mol.GetAtoms(): if atom.GetAtomicNum() > 36: #upto Kr for MMFF, if not use UFF args.ff = "UFF" #log.write("UFF is used because there are atoms that MMFF doesn't recognise") if args.verbose: log.write("o Optimizing " + str(len(cids)) + " initial conformers with" + args.ff) if args.verbose: if args.nodihedrals == False: log.write("o Found " + str(len(rotmatches)) + " rotatable torsions") # for [a,b,c,d] in rotmatches: # log.write(' '+mol.GetAtomWithIdx(a).GetSymbol()+str(a+1)+ mol.GetAtomWithIdx(b).GetSymbol()+str(b+1)+ mol.GetAtomWithIdx(c).GetSymbol()+str(c+1)+mol.GetAtomWithIdx(d).GetSymbol()+str(d+1)) else: log.write("o Systematic torsion rotation is set to OFF") cenergy, outmols = [], [] bar = IncrementalBar('o Minimizing', max=len(cids)) for i, conf in enumerate(cids): if coord_Map == None and alg_Map == None and mol_template == None: if args.ff == "MMFF": GetFF = Chem.MMFFGetMoleculeForceField( mol, Chem.MMFFGetMoleculeProperties(mol), confId=conf) elif args.ff == "UFF": GetFF = Chem.UFFGetMoleculeForceField(mol, confId=conf) else: log.write(' Force field {} not supported!'.format( args.ff)) sys.exit() GetFF.Initialize() converged = GetFF.Minimize(maxIts=args.opt_steps_RDKit) energy = GetFF.CalcEnergy() cenergy.append(GetFF.CalcEnergy()) #if args.verbose: # log.write("- conformer", (i+1), "optimized: ", args.ff, "energy", GetFF.CalcEnergy()) #id template realign before doing calculations else: num_atom_match = mol.GetSubstructMatch(mol_template) # Force field parameters if args.ff == "MMFF": GetFF = lambda mol, confId=conf: Chem.MMFFGetMoleculeForceField( mol, Chem.MMFFGetMoleculeProperties(mol), confId=conf) elif args.ff == "UFF": GetFF = lambda mol, confId=conf: Chem.UFFGetMoleculeForceField( mol, confId=conf) else: log.write(' Force field {} not supported!'.format( options.ff)) sys.exit() getForceField = GetFF # clean up the conformation ff_temp = getForceField(mol, confId=conf) for k, idxI in enumerate(num_atom_match): for l in range(k + 1, len(num_atom_match)): idxJ = num_atom_match[l] d = coord_Map[idxI].Distance(coord_Map[idxJ]) ff_temp.AddDistanceConstraint(idxI, idxJ, d, d, 10000) ff_temp.Initialize() #reassignned n from 4 to 10 for better embed and minimzation n = 10 more = ff_temp.Minimize() while more and n: more = ff_temp.Minimize() n -= 1 energy = ff_temp.CalcEnergy() # rotate the embedded conformation onto the core_mol: rms = rdMolAlign.AlignMol(mol, mol_template, prbCid=conf, atomMap=alg_Map, reflect=True, maxIters=100) # elif len(num_atom_match) == 5: # ff_temp = GetFF(mol, confId=conf) # conf_temp = mol_template.GetConformer() # for k in range(mol_template.GetNumAtoms()): # p = conf_temp.GetAtomPosition(k) # q = mol.GetConformer(conf).GetAtomPosition(k) # pIdx = ff_temp.AddExtraPoint(p.x, p.y, p.z, fixed=True) - 1 # ff_temp.AddDistanceConstraint(pIdx, num_atom_match[k], 0, 0, 10000) # ff_temp.Initialize() # n = 10 # more = ff_temp.Minimize(energyTol=1e-6, forceTol=1e-5) # while more and n: # more = ff_temp.Minimize(energyTol=1e-6, forceTol=1e-5) # n -= 1 # # realign # energy = ff_temp.CalcEnergy() # rms = rdMolAlign.AlignMol(mol, mol_template,prbCid=conf, atomMap=alg_Map,reflect=True,maxIters=50) cenergy.append(energy) # outmols is gonna be a list containing "initial_confs" mol objects with "initial_confs" # conformers. We do this to SetProp (Name and Energy) to the different conformers # and log.write in the SDF file. At the end, since all the mol objects has the same # conformers, but the energies are different, we can log.write conformers to SDF files # with the energies of the parent mol objects. We measured the computing time and # it's the same as using only 1 parent mol object with 10 conformers, but we couldn'temp # SetProp correctly pmol = PropertyMol.PropertyMol(mol) outmols.append(pmol) bar.next() bar.finish() for i, cid in enumerate(cids): outmols[cid].SetProp('_Name', name + ' conformer ' + str(i + 1)) outmols[cid].SetProp('Energy', cenergy[cid]) cids = list(range(len(outmols))) sortedcids = sorted(cids, key=lambda cid: cenergy[cid]) log.write("\n\no Filters after intial embedding of " + str(initial_confs) + " conformers") selectedcids, selectedcids_initial, eng_dup, eng_rms_dup = [], [], -1, -1 bar = IncrementalBar('o Filtering based on energy (pre-filter)', max=len(sortedcids)) for i, conf in enumerate(sortedcids): # This keeps track of whether or not your conformer is unique excluded_conf = False # include the first conformer in the list to start the filtering process if i == 0: selectedcids_initial.append(conf) # check rmsd for seenconf in selectedcids_initial: E_diff = abs(cenergy[conf] - cenergy[seenconf]) # in kcal/mol if E_diff < args.initial_energy_threshold: eng_dup += 1 excluded_conf = True break if excluded_conf == False: if conf not in selectedcids_initial: selectedcids_initial.append(conf) bar.next() bar.finish() if args.verbose == True: log.write("o " + str(eng_dup) + " Duplicates removed pre-energy filter (E < " + str(args.initial_energy_threshold) + " kcal/mol )") #reduce to unique set if args.verbose: log.write("o Removing duplicate conformers ( RMSD < " + str(args.rms_threshold) + " and E difference < " + str(args.energy_threshold) + " kcal/mol)") bar = IncrementalBar('o Filtering based on energy and rms', max=len(selectedcids_initial)) #check rmsd for i, conf in enumerate(selectedcids_initial): #set torsions to same value for m in rotmatches: rdMolTransforms.SetDihedralDeg( outmols[conf].GetConformer(conf), *m, 180.0) # This keeps track of whether or not your conformer is unique excluded_conf = False # include the first conformer in the list to start the filtering process if i == 0: selectedcids.append(conf) # check rmsd for seenconf in selectedcids: E_diff = abs(cenergy[conf] - cenergy[seenconf]) # in kcal/mol if E_diff < args.energy_threshold: rms = get_conf_RMS(outmols[conf], outmols[conf], seenconf, conf, args.heavyonly, args.max_matches_RMSD, log) if rms < args.rms_threshold: excluded_conf = True eng_rms_dup += 1 break if excluded_conf == False: if conf not in selectedcids: selectedcids.append(conf) bar.next() bar.finish() # unique_mols, unique_energies = [],[] # for id in selectedcids: # unique_mols.append(outmols[id]) # unique_energies.append(cenergy[id]) # log.write(unique_mols[0:2].GetConformers()[0].GetPositions()) if args.verbose == True: log.write("o " + str(eng_rms_dup) + " Duplicates removed (RMSD < " + str(args.rms_threshold) + " / E < " + str(args.energy_threshold) + " kcal/mol) after rotation") if args.verbose: log.write("o " + str(len(selectedcids)) + " unique (ignoring torsions) starting conformers remain") dup_data.at[dup_data_idx, 'RDKit-energy-duplicates'] = eng_dup dup_data.at[dup_data_idx, 'RDKit-RMS-and-energy-duplicates'] = eng_rms_dup dup_data.at[dup_data_idx, 'RDKIT-Unique-conformers'] = len(selectedcids) # now exhaustively drive torsions of selected conformers n_confs = int(len(selectedcids) * (360 / args.degree)**len(rotmatches)) if args.verbose and len(rotmatches) != 0: log.write("\n\no Systematic generation of " + str(n_confs) + " confomers") bar = IncrementalBar( 'o Generating conformations based on dihedral rotation', max=len(selectedcids)) else: bar = IncrementalBar('o Generating conformations', max=len(selectedcids)) total = 0 for conf in selectedcids: #log.write(outmols[conf]) total += genConformer_r(outmols[conf], conf, 0, rotmatches, args.degree, sdwriter, args, outmols[conf].GetProp('_Name'), log) bar.next() bar.finish() if args.verbose and len(rotmatches) != 0: log.write("o %d total conformations generated" % total) status = 1 sdwriter.close() #getting the energy from and mols after rotations if len(rotmatches) != 0: rdmols = Chem.SDMolSupplier(name + '_' + 'rdkit' + args.output, removeHs=False) if rdmols is None: log.write("Could not open " + name + args.output) sys.exit(-1) bar = IncrementalBar( 'o Filtering based on energy and rms after rotation of dihedrals', max=len(rdmols)) sdwriter = Chem.SDWriter(name + '_' + 'rdkit' + '_' + 'rotated' + args.output) rd_count = 0 rd_selectedcids, rd_dup_energy, rd_dup_rms_eng = [], -1, 0 for i in range(len(rdmols)): # This keeps track of whether or not your conformer is unique excluded_conf = False # include the first conformer in the list to start the filtering process if rd_count == 0: rd_selectedcids.append(i) if args.metal_complex == True: for atom in rdmols[i].GetAtoms(): if atom.GetSymbol() == 'I' and ( len(atom.GetBonds()) == 6 or len(atom.GetBonds()) == 5 or len(atom.GetBonds()) == 4 or len(atom.GetBonds()) == 3 or len(atom.GetBonds()) == 2): for el in elementspt: if el.symbol == args.metal: atomic_number = el.number atom.SetAtomicNum(atomic_number) sdwriter.write(rdmols[i]) # Only the first ID gets included rd_count = 1 # check rmsd for j in rd_selectedcids: if abs( float(rdmols[i].GetProp('Energy')) - float(rdmols[j].GetProp('Energy')) ) < args.initial_energy_threshold: # comparison in kcal/mol excluded_conf = True rd_dup_energy += 1 break if abs( float(rdmols[i].GetProp('Energy')) - float(rdmols[j].GetProp('Energy')) ) < args.energy_threshold: # in kcal/mol rms = get_conf_RMS(rdmols[i], rdmols[j], -1, -1, args.heavyonly, args.max_matches_RMSD, log) if rms < args.rms_threshold: excluded_conf = True rd_dup_rms_eng += 1 break if excluded_conf == False: if args.metal_complex == True: for atom in rdmols[i].GetAtoms(): if atom.GetSymbol() == 'I' and ( len(atom.GetBonds()) == 6 or len(atom.GetBonds()) == 5 or len(atom.GetBonds()) == 4 or len(atom.GetBonds()) == 3 or len(atom.GetBonds()) == 2): for el in elementspt: if el.symbol == args.metal: atomic_number = el.number atom.SetAtomicNum(atomic_number) sdwriter.write(rdmols[i]) if i not in rd_selectedcids: rd_selectedcids.append(i) bar.next() bar.finish() sdwriter.close() if args.verbose == True: log.write("o " + str(rd_dup_energy) + " Duplicates removed initial energy ( E < " + str(args.initial_energy_threshold) + " kcal/mol )") if args.verbose == True: log.write("o " + str(rd_dup_rms_eng) + " Duplicates removed (RMSD < " + str(args.rms_threshold) + " / E < " + str(args.energy_threshold) + " kcal/mol) after rotation") if args.verbose == True: log.write("o " + str(len(rd_selectedcids)) + " unique (after torsions) conformers remain") #filtering process after rotations dup_data.at[dup_data_idx, 'RDKIT-Rotated-conformers'] = total dup_data.at[dup_data_idx, 'RDKIT-Rotated-Unique-conformers'] = len(rd_selectedcids) return status