def main(argv=[__name__]): if len(argv) != 3: oechem.OEThrow.Usage("%s <mol-infile> <mol-outfile>" % argv[0]) ifs = oechem.oemolistream() if not ifs.open(argv[1]): oechem.OEThrow.Fatal("Unable to open %s for reading" % argv[1]) ofs = oechem.oemolostream() if not ofs.open(argv[2]): oechem.OEThrow.Fatal("Unable to open %s for writing" % argv[2]) for mol in ifs.GetOEGraphMols(): oequacpac.OEGetReasonableProtomer(mol) oechem.OEWriteMolecule(ofs, mol) return 0
def generate_restricted_conformers(receptor, refmol, mol, core_smarts=None): """ Generate and select a conformer of the specified molecule using the reference molecule Parameters ---------- receptor : openeye.oechem.OEGraphMol Receptor (already prepped for docking) for identifying optimal pose refmol : openeye.oechem.OEGraphMol Reference molecule which shares some part in common with the proposed molecule mol : openeye.oechem.OEGraphMol Molecule whose conformers are to be enumerated core_smarts : str, optional, default=None If core_smarts is specified, substructure will be extracted using SMARTS. """ from openeye import oechem, oeomega # DEBUG: For benzotriazoles, truncate refmol core_smarts = 'c1ccc(NC(=O)[C,N]n2nnc3ccccc32)cc1' # prospective core_smarts = 'NC(=O)[C,N]n2nnc3ccccc32' # retrospective # Get core fragment if core_smarts: # Truncate refmol to SMARTS if specified #print(f'Trunctating using SMARTS {refmol_smarts}') ss = oechem.OESubSearch(core_smarts) oechem.OEPrepareSearch(refmol, ss) for match in ss.Match(refmol): core_fragment = oechem.OEGraphMol() oechem.OESubsetMol(core_fragment, match) break #print(f'refmol has {refmol.NumAtoms()} atoms') else: core_fragment = GetCoreFragment(refmol, [mol]) oechem.OESuppressHydrogens(core_fragment) #print(f' Core fragment has {core_fragment.NumAtoms()} heavy atoms') MIN_CORE_ATOMS = 6 if core_fragment.NumAtoms() < MIN_CORE_ATOMS: return None # Create an Omega instance #omegaOpts = oeomega.OEOmegaOptions() omegaOpts = oeomega.OEOmegaOptions(oeomega.OEOmegaSampling_Dense) # Set the fixed reference molecule omegaFixOpts = oeomega.OEConfFixOptions() omegaFixOpts.SetFixMaxMatch(10) # allow multiple MCSS matches omegaFixOpts.SetFixDeleteH(True) # only use heavy atoms omegaFixOpts.SetFixMol(core_fragment) #omegaFixOpts.SetFixSmarts(smarts) omegaFixOpts.SetFixRMS(0.5) atomexpr = oechem.OEExprOpts_Aromaticity | oechem.OEExprOpts_Hybridization bondexpr = oechem.OEExprOpts_BondOrder | oechem.OEExprOpts_Aromaticity omegaFixOpts.SetAtomExpr(atomexpr) omegaFixOpts.SetBondExpr(bondexpr) omegaOpts.SetConfFixOptions(omegaFixOpts) molBuilderOpts = oeomega.OEMolBuilderOptions() molBuilderOpts.SetStrictAtomTypes(False) # don't give up if MMFF types are not found omegaOpts.SetMolBuilderOptions(molBuilderOpts) omegaOpts.SetWarts(False) # expand molecule title omegaOpts.SetStrictStereo(False) # set strict stereochemistry omegaOpts.SetIncludeInput(False) # don't include input omegaOpts.SetMaxConfs(1000) # generate lots of conformers #omegaOpts.SetEnergyWindow(10.0) # allow high energies omega = oeomega.OEOmega(omegaOpts) from openeye import oequacpac if not oequacpac.OEGetReasonableProtomer(mol): print('No reasonable protomer found') return None mol = oechem.OEMol(mol) # multi-conformer molecule ret_code = omega.Build(mol) if (mol.GetDimension() != 3) or (ret_code != oeomega.OEOmegaReturnCode_Success): print(f'Omega failure: {mol.GetDimension()} and {oeomega.OEGetOmegaError(ret_code)}') return None # Extract poses class Pose(object): def __init__(self, conformer): self.conformer = conformer self.clash_score = None self.docking_score = None self.overlap_score = None poses = [ Pose(conf) for conf in mol.GetConfs() ] # Score clashes bump_check = BumpCheck(receptor) for pose in poses: pose.clash_score = bump_check.count(pose.conformer) # Score docking poses from openeye import oedocking score = oedocking.OEScore(oedocking.OEScoreType_Chemgauss4) score.Initialize(receptor) for pose in poses: pose.docking_score = score.ScoreLigand(pose.conformer) # Compute overlap scores from openeye import oeshape overlap_prep = oeshape.OEOverlapPrep() overlap_prep.Prep(refmol) shapeFunc = oeshape.OEExactShapeFunc() shapeFunc.SetupRef(refmol) oeshape_result = oeshape.OEOverlapResults() for pose in poses: tmpmol = oechem.OEGraphMol(pose.conformer) overlap_prep.Prep(tmpmol) shapeFunc.Overlap(tmpmol, oeshape_result) pose.overlap_score = oeshape_result.GetRefTversky() # Filter poses based on top 10% of overlap poses = sorted(poses, key= lambda pose : pose.overlap_score) poses = poses[int(0.9*len(poses)):] # Select the best docking score import numpy as np poses = sorted(poses, key=lambda pose : pose.docking_score) pose = poses[0] mol.SetActive(pose.conformer) oechem.OESetSDData(mol, 'clash_score', str(pose.clash_score)) oechem.OESetSDData(mol, 'docking_score', str(pose.docking_score)) oechem.OESetSDData(mol, 'overlap_score', str(pose.overlap_score)) # Convert to single-conformer molecule mol = oechem.OEGraphMol(mol) return mol
def generate_restricted_conformers(receptor, refmol, mol, core_smarts=None): """ Generate and select a conformer of the specified molecule using the reference molecule Parameters ---------- receptor : openeye.oechem.OEGraphMol Receptor (already prepped for docking) for identifying optimal pose refmol : openeye.oechem.OEGraphMol Reference molecule which shares some part in common with the proposed molecule mol : openeye.oechem.OEGraphMol Molecule whose conformers are to be enumerated core_smarts : str, optional, default=None If core_smarts is specified, substructure will be extracted using SMARTS. """ from openeye import oechem, oeomega logging.debug( f'mol: {oechem.OEMolToSmiles(mol)} | core_smarts: {core_smarts}') # Be quiet from openeye import oechem oechem.OEThrow.SetLevel(oechem.OEErrorLevel_Quiet) #oechem.OEThrow.SetLevel(oechem.OEErrorLevel_Error) # Get core fragment if core_smarts: # Truncate refmol to SMARTS if specified #print(f'Trunctating using SMARTS {refmol_smarts}') ss = oechem.OESubSearch(core_smarts) oechem.OEPrepareSearch(refmol, ss) for match in ss.Match(refmol): core_fragment = oechem.OEGraphMol() oechem.OESubsetMol(core_fragment, match) logging.debug( f'Truncated refmol to generate core_fragment: {oechem.OEMolToSmiles(core_fragment)}' ) break #print(f'refmol has {refmol.NumAtoms()} atoms') else: core_fragment = GetCoreFragment(refmol, [mol]) oechem.OESuppressHydrogens(core_fragment) #print(f' Core fragment has {core_fragment.NumAtoms()} heavy atoms') MIN_CORE_ATOMS = 6 if core_fragment.NumAtoms() < MIN_CORE_ATOMS: return None # Create an Omega instance #omegaOpts = oeomega.OEOmegaOptions() omegaOpts = oeomega.OEOmegaOptions(oeomega.OEOmegaSampling_Dense) # Set the fixed reference molecule omegaFixOpts = oeomega.OEConfFixOptions() omegaFixOpts.SetFixMaxMatch(10) # allow multiple MCSS matches omegaFixOpts.SetFixDeleteH(True) # only use heavy atoms omegaFixOpts.SetFixMol(core_fragment) #omegaFixOpts.SetFixSmarts(core_smarts) # DEBUG omegaFixOpts.SetFixRMS(0.5) # This causes a warning: #Warning: OESubSearch::Match() is unable to match unset hybridization in the target (EN300-221518_3_1) for patterns with set hybridization, call OEPrepareSearch on the target first #atomexpr = oechem.OEExprOpts_Aromaticity | oechem.OEExprOpts_Hybridization atomexpr = oechem.OEExprOpts_Aromaticity | oechem.OEExprOpts_AtomicNumber bondexpr = oechem.OEExprOpts_BondOrder | oechem.OEExprOpts_Aromaticity omegaFixOpts.SetAtomExpr(atomexpr) omegaFixOpts.SetBondExpr(bondexpr) omegaOpts.SetConfFixOptions(omegaFixOpts) molBuilderOpts = oeomega.OEMolBuilderOptions() molBuilderOpts.SetStrictAtomTypes( False) # don't give up if MMFF types are not found omegaOpts.SetMolBuilderOptions(molBuilderOpts) omegaOpts.SetWarts(False) # expand molecule title omegaOpts.SetStrictStereo(True) # set strict stereochemistry omegaOpts.SetIncludeInput(False) # don't include input omegaOpts.SetMaxConfs(1000) # generate lots of conformers omegaOpts.SetEnergyWindow(20.0) # allow high energies omega = oeomega.OEOmega(omegaOpts) # TODO: Expand protonation states and tautomers from openeye import oequacpac if not oequacpac.OEGetReasonableProtomer(mol): logging.warning('No reasonable protomer found') return None mol = oechem.OEMol(mol) # multi-conformer molecule ret_code = omega.Build(mol) if (mol.GetDimension() != 3) or (ret_code != oeomega.OEOmegaReturnCode_Success): msg = f'\nOmega failure for {mol.GetTitle()} : SMILES {oechem.OEMolToSmiles(mol)} : core_smarts {core_smarts} : {oeomega.OEGetOmegaError(ret_code)}\n' logging.warning(msg) return None # Return the molecule with an error code #oechem.OESetSDData(mol, 'error', '{oeomega.OEGetOmegaError(ret_code)}') #return mol # Extract poses class Pose(object): def __init__(self, conformer): self.conformer = conformer self.clash_score = None self.docking_score = None self.overlap_score = None poses = [Pose(conf) for conf in mol.GetConfs()] # Score clashes bump_check = BumpCheck(receptor) for pose in poses: pose.clash_score = bump_check.count(pose.conformer) # Score docking poses from openeye import oedocking score = oedocking.OEScore(oedocking.OEScoreType_Chemgauss4) score.Initialize(receptor) for pose in poses: pose.docking_score = score.ScoreLigand(pose.conformer) # Compute overlap scores from openeye import oeshape overlap_prep = oeshape.OEOverlapPrep() overlap_prep.Prep(refmol) shapeFunc = oeshape.OEExactShapeFunc() shapeFunc.SetupRef(refmol) oeshape_result = oeshape.OEOverlapResults() for pose in poses: tmpmol = oechem.OEGraphMol(pose.conformer) overlap_prep.Prep(tmpmol) shapeFunc.Overlap(tmpmol, oeshape_result) pose.overlap_score = oeshape_result.GetRefTversky() # Filter poses based on top 10% of overlap poses = sorted(poses, key=lambda pose: pose.overlap_score) poses = poses[int(0.9 * len(poses)):] # Select the best docking score import numpy as np poses = sorted(poses, key=lambda pose: pose.docking_score) pose = poses[0] mol.SetActive(pose.conformer) oechem.OESetSDData(mol, 'clash_score', str(pose.clash_score)) oechem.OESetSDData(mol, 'docking_score', str(pose.docking_score)) oechem.OESetSDData(mol, 'overlap_score', str(pose.overlap_score)) # Convert to single-conformer molecule mol = oechem.OEGraphMol(mol) # Compute MMFF energy energy = mmff_energy(mol) oechem.OESetSDData(mol, 'MMFF_internal_energy', str(energy)) # Store SMILES docked_smiles = oechem.OEMolToSmiles(mol) oechem.OESetSDData(mol, 'docked_smiles', docked_smiles) return mol
oechem.OEReadMolecule(ifs, mol) ifs.close() else: # Create a OpenEye molecule object from the SMILES string if not oechem.OESmilesToMol(mol, smi): raise Exception('Invalid SMILES string', smi) oechem.OECanonicalOrderAtoms(mol) oechem.OECanonicalOrderBonds(mol) # Assign a reasonable protomer if args.RetainProtonation: for atom in mol.GetAtoms(): atom.SetImplicitHCount(0) else: if not oequacpac.OEGetReasonableProtomer(mol): print 'Failed to get a reasonable protomer at pH 7.4' oechem.OEAssignAromaticFlags(mol, oechem.OEAroModelOpenEye) if not args.RetainProtonation: oechem.OEAddExplicitHydrogens(mol) smi = oechem.OECreateSmiString(mol, oechem.OESMILESFlag_Canonical) print 'The canonical SMILES for a reasonably protonated state is', smi # Generate conformations from openeye import oeomega mol_multiconf = oechem.OEMol(mol) oechem.OECanonicalOrderAtoms(mol_multiconf)