def MakeAlpha(ifs, ofs): phival = math.pi / -3.0 psival = math.pi / -3.0 chival = math.pi nrphis = 0 nrpsis = 0 nrchis = 0 mol = oechem.OEGraphMol() while oechem.OEReadMolecule(ifs, mol): if not oechem.OEHasResidues(mol): oechem.OEPerceiveResidues(mol, oechem.OEPreserveResInfo_All) # remove cross-links for bond in mol.GetBonds(): if bond.GetBgn().GetAtomicNum() == oechem.OEElemNo_S and \ bond.GetEnd().GetAtomicNum() == oechem.OEElemNo_S: mol.DeleteBond(bond) oechem.OEFindRingAtomsAndBonds(mol) hv = oechem.OEHierView(mol) for res in hv.GetResidues(): if not oechem.OEIsStandardProteinResidue(res): continue # set psi and phi angles if not oechem.OESetTorsion(res, oechem.OEProtTorType_Phi, phival): oeres = res.GetOEResidue() print("Unable to set phi for %s %d" % (oeres.GetName(), oeres.GetResidueNumber())) else: nrphis += 1 if not oechem.OESetTorsion(res, oechem.OEProtTorType_Psi, psival): oeres = res.GetOEResidue() print("Unable to set psi for %s %d" % (oeres.GetName(), oeres.GetResidueNumber())) else: nrpsis += 1 # set chis if oechem.OEGetResidueIndex( res.GetOEResidue().GetName()) == oechem.OEResidueIndex_PRO: continue # It does not make sense to set Proline chi angles to 180 for chi in oechem.OEGetChis(res): if not oechem.OESetTorsion(res, chi, chival): oeres = res.GetOEResidue() print("Unable to set chi %s for %s %d" % (oechem.OEGetProteinTorsionName(chi), oeres.GetName(), oeres.GetResidueNumber())) else: nrchis += 1 oechem.OEWriteMolecule(ofs, mol) print(nrphis, " phi torsion angle set to ", phival * oechem.cvar.Rad2Deg) print(nrpsis, " psi torsion angle set to ", psival * oechem.cvar.Rad2Deg) print(nrchis, " chis torsion angle set to ", chival * oechem.cvar.Rad2Deg)
def gen_torsional_confs(mol, dih, num_points, include_input=False): """Drives the primary torsion in the molecule and generates labeled torsional conformers. Inputs: mol - OEMol must have a 'CONFORMER_LABEL' in the sd_data """ angle_list = [2*i*oechem.Pi/num_points for i in range(num_points)] dih_atoms = [x for x in dih.GetAtoms()] # Create new output OEMol torsion_conformers = oechem.OEMol(mol) if not include_input: torsion_conformers.DeleteConfs() for conf_id, conf in enumerate(mol.GetConfs()): conf_name = get_sd_data(conf, 'CONFORMER_LABEL') for angle_idx, angle in enumerate(angle_list): angle_deg = int(round(angle*oechem.Rad2Deg)) oechem.OESetTorsion(conf, dih_atoms[0], dih_atoms[1], dih_atoms[2], dih_atoms[3], angle) new_conf = torsion_conformers.NewConf(conf) new_conf.SetDimension(3) new_conf_name = conf_name + '_{:02d}'.format(angle_idx) oechem.OESetSDData(new_conf, 'CONFORMER_LABEL', new_conf_name) oechem.OESetSDData(new_conf, 'TORSION_ANGLE', "{:.0f}".format(angle_deg)) new_conf.SetDoubleData('TORSION_ANGLE', angle_deg) new_conf.SetTitle('{}: Angle {:.0f}'.format(conf_name, angle_deg)) return torsion_conformers
def generate_grid_conformers(molecule, dihedrals, intervals, max_rotation=360, copy_mol=True): """ Generate conformers using torsion angle grids. Parameters ---------- molecule: OEMol dihedrals: list of intervals Returns ------- """ # molecule must be mapped if copy_mol: molecule = copy.deepcopy(molecule) if cmiles.utils.has_atom_map(molecule): remove_map(molecule) else: raise ValueError("Molecule must have map indices") # Check length of dihedrals match length of intervals conf_mol = generate_conformers(molecule, max_confs=1) conf = conf_mol.GetConfs().next() coords = oechem.OEFloatArray(conf.GetMaxAtomIdx() * 3) conf.GetCoords(coords) torsions = [[conf_mol.GetAtom(oechem.OEHasMapIdx(i + 1)) for i in dih] for dih in dihedrals] for i, tor in enumerate(torsions): copy_conf_mol = copy.deepcopy(conf_mol) conf_mol.DeleteConfs() for conf in copy_conf_mol.GetConfs(): coords = oechem.OEFloatArray(conf.GetMaxAtomIdx() * 3) conf.GetCoords(coords) for angle in range(5, max_rotation + 5, intervals[i]): newconf = conf_mol.NewConf(coords) oechem.OESetTorsion(newconf, tor[0], tor[1], tor[2], tor[3], radians(angle)) restore_map(conf_mol) return conf_mol
def get_best_conf(mol, dih, num_points): """Drive the primary torsion in the molecule and select the lowest energy conformer to represent each dihedral angle """ delta = 360.0 / num_points angle_list = [2 * i * oechem.Pi / num_points for i in range(num_points)] dih_atoms = [x for x in dih.GetAtoms()] # Create new output OEMol title = mol.GetTitle() tor_mol = oechem.OEMol() opts = oeszybki.OETorsionScanOptions() opts.SetDelta(delta) opts.SetForceFieldType(oeszybki.OEForceFieldType_MMFF94) opts.SetSolvationType(oeszybki.OESolventModel_NoSolv) tmp_angle = 0.0 tor = oechem.OETorsion(dih_atoms[0], dih_atoms[1], dih_atoms[2], dih_atoms[3], tmp_angle) oeszybki.OETorsionScan(tor_mol, mol, tor, opts) oechem.OECopySDData(tor_mol, mol) # if 0 and 360 sampled because of rounding if tor_mol.NumConfs() > num_points: for conf in tor_mol.GetConfs(): continue tor_mol.DeleteConf(conf) for angle, conf in zip(angle_list, tor_mol.GetConfs()): angle_deg = int(round(angle * oechem.Rad2Deg)) tor_mol.SetActive(conf) oechem.OESetTorsion(conf, dih_atoms[0], dih_atoms[1], dih_atoms[2], dih_atoms[3], angle) conf_name = title + '_{:02d}'.format(conf.GetIdx()) oechem.OESetSDData(conf, 'CONFORMER_LABEL', conf_name) oechem.OESetSDData(conf, 'TORSION_ANGLE', "{:.0f}".format(angle_deg)) conf.SetDoubleData('TORSION_ANGLE', angle_deg) conf.SetTitle('{}: Angle {:.0f}'.format(conf_name, angle_deg)) return tor_mol
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 process(self, mol, port): """ The input to this cube will be an OEMol with one or more conformers with "CONFORMER_LABEL" SD Data of the form 'XY-1234567_1_2_3_4_00_00' """ num_confs = mol.NumConfs() last_conf = mol.GetActive() last_conf_name = oechem.OEGetSDData(last_conf, "CONFORMER_LABEL") self.log.info( "Processing conformer {} on {} at {:%Y-%m-%d %H:%M:%S}".format( last_conf_name, os.environ["HOSTNAME"], datetime.datetime.now())) if num_confs == self.args.num_points: self.success.emit(mol) self.log.info( "Completed scan for {} on {} at {:%Y-%m-%d %H:%M:%S}".format( mol.GetTitle(), os.environ["HOSTNAME"], datetime.datetime.now())) return if num_confs == 1 and not mol.HasData(self.conf_selection_tag): self.log.info( "Conformer {} is a fresh starting conformer on {} at {:%Y-%m-%d %H:%M:%S}" .format(mol.GetTitle(), os.environ["HOSTNAME"], datetime.datetime.now())) mol.SetIntData(self.conf_selection_tag, last_conf.GetIdx()) last_conf.SetDoubleData("TORSION_ANGLE", 0.0) oechem.OESetSDData(last_conf, "TORSION_ANGLE", "0.0") self.log.info( "Sending conformer {} to energy calculation from {} at {:%Y-%m-%d %H:%M:%S}" .format(last_conf_name, os.environ["HOSTNAME"], datetime.datetime.now())) self.to_energy_calc.emit(mol) return try: torsion_tag = "TORSION_ATOMS_FRAGMENT" torsion_atoms_in_fragment = get_sd_data(mol, torsion_tag).split() dihedral_atom_indices = [ int(x) - 1 for x in torsion_atoms_in_fragment ] dih, _ = get_dihedral(mol, dihedral_atom_indices) dih_atoms = [x for x in dih.GetAtoms()] # if the last energy calculation failed if not oechem.OEHasSDData(last_conf, "PSI4_ENERGY"): self.log.info( "Conformer {} found to have NO ENERGY on {} at {:%Y-%m-%d %H:%M:%S}" .format(last_conf_name, os.environ["HOSTNAME"], datetime.datetime.now())) mol.PopActive() last_conf = mol.GetActive() new_conf = mol.NewConf(last_conf) mol.PushActive(new_conf) conf_no = num_confs conformer_label = last_conf_name[:-3] + "_{:02d}".format(conf_no) oechem.OESetSDData(new_conf, "CONFORMER_LABEL", conformer_label) angle = num_confs * 2 * oechem.Pi / self.args.num_points angle_deg = oechem.Rad2Deg * angle new_conf.SetDoubleData("TORSION_ANGLE", angle_deg) oechem.OESetSDData(new_conf, "TORSION_ANGLE", "{:.1f}".format(angle_deg)) if not oechem.OESetTorsion(new_conf, dih_atoms[0], dih_atoms[1], dih_atoms[2], dih_atoms[3], angle): self.log.error( "Could not rotate conformer {} by {:.1f} on {} at {:%Y-%m-%d %H:%M:%S}" .format( last_conf_name, angle_deg, os.environ["HOSTNAME"], datetime.datetime.now(), )) mol.SetIntData(self.conf_selection_tag, new_conf.GetIdx()) self.log.info( "Sending conformer {} to energy calculation from {} at {:%Y-%m-%d %H:%M:%S}" .format(conformer_label, os.environ["HOSTNAME"], datetime.datetime.now())) self.to_energy_calc.emit(mol) except Exception as e: self.log.error( "COuld not drive torsion in conformer {} on {} at {:%Y-%m-%d %H:%M:%S}: {}" .format(last_conf_name, os.environ["HOSTNAME"], datetime.datetime.now(), e)) self.failure.emit(mol)
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()