# Filter out receptor atoms with atom names of UNK for atom in receptor.GetAtoms(): if atom.GetName() == 'UNK': receptor.DeleteAtom(atom) # Write joined PDB with ligand and receptor output_filename = os.path.join(docking_basedir, f'{molecule.GetTitle()} - complex.pdb') if not os.path.exists(output_filename): with oechem.oemolostream(output_filename) as ofs: oechem.OEClearResidues(docked_molecule) #oechem.OEWriteMolecule(ofs, docked_molecule) oechem.OETriposAtomNames(docked_molecule) oechem.OEWritePDBFile(ofs, docked_molecule, oechem.OEOFlavor_PDB_Default | oechem.OEOFlavor_PDB_BONDS) set_serial(receptor, 'X', docked_molecule.NumAtoms()+1) #oechem.OEWritePDBFile(ofs, receptor, oechem.OEOFlavor_PDB_Default | oechem.OEOFlavor_PDB_BONDS) oechem.OEWriteMolecule(ofs, receptor) # Write PDB of just ligand output_filename = os.path.join(docking_basedir, f'{molecule.GetTitle()} - ligand.pdb') if not os.path.exists(output_filename): with oechem.oemolostream(output_filename) as ofs: oechem.OEClearResidues(docked_molecule) #oechem.OEWriteMolecule(ofs, docked_molecule) oechem.OETriposAtomNames(docked_molecule)
def generate_constraint_opt_input(qc_molecule, dihedrals, maximum_rotation=30, interval=5, filename=None): """ Parameters ---------- qc_molecule dihedrals Returns ------- QCFractal optimization jobs input """ from openeye import oechem optimization_jobs = {} tagged_smiles = qc_molecule['identifiers']['canonical_isomeric_explicit_hydrogen_mapped_smiles'] mol = oechem.OEMol() oechem.OESmilesToMol(mol, tagged_smiles) atom_map = get_atom_map(mol, tagged_smiles) coords = chemi.from_mapped_xyz_to_mol_idx_order(qc_molecule['geometry'], atom_map) # convert coord to Angstrom coords = coords * utils.BOHR_2_ANGSTROM conf = mol.GetConfs().next() conf.SetCoords(oechem.OEFloatArray(coords)) # new molecule for setting dihedral angles mol_2 = oechem.OEMol(mol) conf_2 = mol_2.GetConfs().next() coords_2 = oechem.OEFloatArray(conf_2.GetMaxAtomIdx()*3) conf.GetCoords(coords_2) mol_2.DeleteConfs() interval = radians(interval) max_rot = radians(maximum_rotation) for dihedral in dihedrals: #j = 0 dih_idx = dihedrals[dihedral] tor = [] for i in dih_idx: a = mol.GetAtom(oechem.OEHasMapIdx(i+1)) tor.append(a) dih_angle = oechem.OEGetTorsion(conf, tor[0], tor[1], tor[2], tor[3]) for i, angle in enumerate(np.arange(dih_angle-max_rot, dih_angle+max_rot, interval)): newconf = mol.NewConf(coords_2) oechem.OESetTorsion(newconf, tor[0], tor[1], tor[2], tor[3], angle) #new_angle = oechem.OEGetTorsion(newconf, tor[0], tor[1], tor[2], tor[3]) # if new_angle == dih_angle: # j += 1 # if j > 1: # # One equivalent angle should be generated. # logger().warning("Openeye did not generate a new conformer for torsion and angle {} {}. Will not generate" # "qcfractal optimizaiton input".format(dih_idx, angle)) # break if filename: pdb = oechem.oemolostream("{}_{}.pdb".format(filename, i)) oechem.OEWritePDBFile(pdb, newconf) symbols, geometry = chemi.to_mapped_geometry(newconf, atom_map) qc_molecule = copy.deepcopy(qc_molecule) qc_molecule['geometry'] = geometry qc_molecule['symbols'] = symbols degree = degrees(angle) optimization_jobs['{}_{}'.format(dih_idx, int(round(degree)))] = { 'type': 'optimization_input', 'initial_molecule': qc_molecule, 'dihedral': dih_idx, 'constraints': { "set": [{ "type": "dihedral", "indices": dih_idx, "value": degree }] } } return optimization_jobs
def generate_torsions(inp_mol, output_path, interval, base_name=None, tar=True): """ This function takes a 3D molecule (pdf, mol2 or sd file) and generates structures for a torsion drive on all torsions in the molecule. This function uses OpenEye Parameters ---------- mol : OEMol molecule to generate 1D torsion scans output_path: str path to output file directory interval: int angle (in degrees) of interval for torsion drive base_name: str base name for file. Default is None. If default, use title in OEMol for base name tar: bool If true, will compress output """ if not base_name: base_name = inp_mol.GetTitle() mid_tors = [[tor.a, tor.b, tor.c, tor.d] for tor in oechem.OEGetTorsions(inp_mol)] # This smarts should match terminal torsions such as -CH3, -NH2, -NH3+, -OH, and -SH smarts = '[*]~[*]-[X2H1,X3H2,X4H3]-[#1]' qmol = oechem.OEQMol() if not oechem.OEParseSmarts(qmol, smarts): warnings.warn('OEParseSmarts failed') ss = oechem.OESubSearch(qmol) mol = oechem.OEMol(inp_mol) h_tors = [] oechem.OEPrepareSearch(mol, ss) unique = True for match in ss.Match(mol, unique): tor = [] for ma in match.GetAtoms(): tor.append(ma.target) h_tors.append(tor) # Combine middle and terminal torsions all_tors = mid_tors + h_tors # Sort all_tors so that it's grouped by central bond central_bonds = np.zeros((len(all_tors), 3), dtype=int) for i, tor in enumerate(all_tors): central_bonds[i][0] = i central_bonds[i][1] = tor[1].GetIdx() central_bonds[i][2] = tor[2].GetIdx() grouped = central_bonds[central_bonds[:, 2].argsort()] sorted_tors = [all_tors[i] for i in grouped[:, 0]] # Keep only one torsion per rotatable bond tors = [] best_tor = [ sorted_tors[0][0], sorted_tors[0][0], sorted_tors[0][0], sorted_tors[0][0] ] first_pass = True for tor in sorted_tors: logger().info("Idxs: {} {} {} {}".format(tor[0].GetIdx(), tor[1].GetIdx(), tor[2].GetIdx(), tor[3].GetIdx())) logger().info("Atom Numbers: {} {} {} {}".format( tor[0].GetAtomicNum(), tor[1].GetAtomicNum(), tor[2].GetAtomicNum(), tor[3].GetAtomicNum())) if tor[1].GetIdx() != best_tor[1].GetIdx() or tor[2].GetIdx( ) != best_tor[2].GetIdx(): new_tor = True if not first_pass: logger().info("Adding to list: {} {} {} {}".format( best_tor[0].GetIdx(), best_tor[1].GetIdx(), best_tor[2].GetIdx(), best_tor[3].GetIdx())) tors.append(best_tor) first_pass = False best_tor = tor best_tor_order = tor[0].GetAtomicNum() + tor[3].GetAtomicNum() logger().info( "new_tor with central bond across atoms: {} {}".format( tor[1].GetIdx(), tor[2].GetIdx())) else: logger().info("Not a new_tor but now with end atoms: {} {}".format( tor[0].GetIdx(), tor[3].GetIdx())) tor_order = tor[0].GetAtomicNum() + tor[3].GetAtomicNum() if tor_order > best_tor_order: best_tor = tor best_tor_order = tor_order logger().info("Adding to list: {} {} {} {}".format(best_tor[0].GetIdx(), best_tor[1].GetIdx(), best_tor[2].GetIdx(), best_tor[3].GetIdx())) tors.append(best_tor) logger().info("List of torsion to drive:") for tor in tors: logger().info("Idx: {} {} {} {}".format(tor[0].GetIdx(), tor[1].GetIdx(), tor[2].GetIdx(), tor[3].GetIdx())) logger().info("Atom numbers: {} {} {} {}".format( tor[0].GetAtomicNum(), tor[1].GetAtomicNum(), tor[2].GetAtomicNum(), tor[3].GetAtomicNum())) conf = mol.GetConfs().next() coords = oechem.OEFloatArray(conf.GetMaxAtomIdx() * 3) conf.GetCoords(coords) # Check if coordinates are not zero values = np.asarray( [coords.__getitem__(i) == 0 for i in range(coords.__len__())]) if values.all(): # Generate new coordinates. mol2 = generate_conformers(mol, max_confs=1) conf = mol2.GetConfs().next() coords = oechem.OEFloatArray(conf.GetMaxAtomIdx() * 3) conf.GetCoords(coords) mol2.DeleteConfs() mol.DeleteConfs() for tor in tors: tor_name = str((tor[0].GetIdx()) + 1) + '_' + str( (tor[1].GetIdx()) + 1) + '_' + str( (tor[2].GetIdx()) + 1) + '_' + str((tor[3].GetIdx()) + 1) folder = os.path.join(output_path, tor_name) try: os.makedirs(folder) except FileExistsError: logger().info("Overwriting existing directory {}".format(tor_name)) for angle in range(0, 360, interval): angle_folder = os.path.join(folder, str(angle)) try: os.mkdir(angle_folder) except FileExistsError: logger().info( "Overwriting existing directory {}".format(tor_name)) newconf = mol.NewConf(coords) oechem.OESetTorsion(newconf, tor[0], tor[1], tor[2], tor[3], radians(angle)) pdb = oechem.oemolostream('{}/{}_{}_{}.pdb'.format( angle_folder, base_name, tor_name, angle)) oechem.OEWritePDBFile(pdb, newconf) if tar: # tar archive output out = tarfile.open('{}.tar.gz'.format(output_path), mode='w:gz') os.chdir(output_path) os.chdir('../') out.add('{}'.format(base_name)) out.close()