def iupac_to_oemol(iupac_name): """Create a OEMolBuilder from a iupac name. Parameters ---------- iupac_name : str IUPAC name of desired molecule. Returns ------- molecule : OEMol A normalized molecule with desired iupac name. """ oechem = import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise(ImportError("Need License for OEChem!")) oeiupac = import_("openeye.oeiupac") if not oeiupac.OEIUPACIsLicensed(): raise(ImportError("Need License for OEOmega!")) # Create an OEMol molecule from IUPAC name. molecule = oechem.OEMol() # create a molecule # Populate the MoleCule from the IUPAC name if not oeiupac.OEParseIUPACName(molecule, iupac_name): raise ValueError("The supplied IUPAC name '%s' could not be parsed." % iupac_name) molecule = normalize_molecule(molecule) return molecule
def iupac_to_oemol(iupac_name): """Create a OEMolBuilder from a iupac name. Parameters ---------- iupac_name : str IUPAC name of desired molecule. Returns ------- molecule : OEMol A normalized molecule with desired iupac name """ oechem = import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise(ImportError("Need License for OEChem!")) oeiupac = import_("openeye.oeiupac") if not oeiupac.OEIUPACIsLicensed(): raise(ImportError("Need License for OEOmega!")) # Create an OEMol molecule from IUPAC name. molecule = oechem.OEMol() # create a molecule # Populate the MoleCule from the IUPAC name if not oeiupac.OEParseIUPACName(molecule, iupac_name): raise ValueError("The supplied IUPAC name '%s' could not be parsed." % iupac_name) molecule = normalize_molecule(molecule) return molecule
def generate_conformers(molecule, max_confs=800, strictStereo=True, ewindow=15.0, rms_threshold=1.0, strictTypes=True): """Generate conformations for the supplied molecule Parameters ---------- molecule : OEMol Molecule for which to generate conformers max_confs : int, optional, default=800 Max number of conformers to generate. If None, use default OE Value. strictStereo : bool, optional, default=True If False, permits smiles strings with unspecified stereochemistry. strictTypes : bool, optional, default=True If True, requires that Omega have exact MMFF types for atoms in molecule; otherwise, allows the closest atom type of the same element to be used. Returns ------- molcopy : OEMol A multi-conformer molecule with up to max_confs conformers. Notes ----- Roughly follows http://docs.eyesopen.com/toolkits/cookbook/python/modeling/am1-bcc.html """ oechem = import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise (ImportError("Need License for OEChem!")) oeomega = import_("openeye.oeomega") if not oeomega.OEOmegaIsLicensed(): raise (ImportError("Need License for OEOmega!")) molcopy = oechem.OEMol(molecule) omega = oeomega.OEOmega() # These parameters were chosen to match http://docs.eyesopen.com/toolkits/cookbook/python/modeling/am1-bcc.html omega.SetMaxConfs(max_confs) omega.SetIncludeInput(True) omega.SetCanonOrder(False) omega.SetSampleHydrogens(True) # Word to the wise: skipping this step can lead to significantly different charges! omega.SetEnergyWindow(ewindow) omega.SetRMSThreshold( rms_threshold ) # Word to the wise: skipping this step can lead to significantly different charges! omega.SetStrictStereo(strictStereo) omega.SetStrictAtomTypes(strictTypes) omega.SetIncludeInput(False) # don't include input if max_confs is not None: omega.SetMaxConfs(max_confs) status = omega(molcopy) # generate conformation if not status: raise (RuntimeError("omega returned error code %d" % status)) return molcopy
def smiles_to_antechamber(smiles_string, gaff_mol2_filename, frcmod_filename, residue_name="MOL", strictStereo=False, protonation=False): """Build a molecule from a smiles string and run antechamber, generating GAFF mol2 and frcmod files from a smiles string. Charges will be generated using the OpenEye QuacPac AM1-BCC implementation. Parameters ---------- smiles_string : str Smiles string of molecule to construct and charge gaff_mol2_filename : str Filename of mol2 file output of antechamber, with charges created from openeye frcmod_filename : str Filename of frcmod file output of antechamber. Most likely this file will be almost empty, at least for typical molecules. residue_name : str, optional, default="MOL" OpenEye writes mol2 files with <0> as the residue / ligand name. This chokes many mol2 parsers, so we replace it with a string of your choosing. This might be useful for downstream applications if the residue names are required to be unique. strictStereo : bool, optional, default=False If False, permits smiles strings with unspecified stereochemistry. See https://docs.eyesopen.com/omega/usage.html protonation : bool, optional, default=False If True, uses OESetNeutralpHModel to set a pH model for the molecule to attempt to obtain protonation states appropriate for neutral pH. Depending on the application this may or may not be what you want, e.g. for hydration free energy calculations you may want the typical depicted (neutral) form. """ oechem = import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise (ImportError("Need License for oechem!")) oequacpac = import_("openeye.oequacpac") # Get the absolute path so we can find these filenames from inside a temporary directory. gaff_mol2_filename = os.path.abspath(gaff_mol2_filename) frcmod_filename = os.path.abspath(frcmod_filename) m = smiles_to_oemol(smiles_string) if protonation: oequacpac.OESetNeutralpHModel(m) m = get_charges(m, strictStereo=strictStereo, keep_confs=1) with enter_temp_directory( ): # Avoid dumping 50 antechamber files in local directory. _unused = molecule_to_mol2(m, "./tmp.mol2", residue_name=residue_name) net_charge = oechem.OENetCharge(m) tmp_gaff_mol2_filename, tmp_frcmod_filename = run_antechamber( "tmp", "./tmp.mol2", charge_method=None, net_charge=net_charge) # USE OE AM1BCC charges! shutil.copy(tmp_gaff_mol2_filename, gaff_mol2_filename) shutil.copy(tmp_frcmod_filename, frcmod_filename)
def get_charges(molecule, max_confs=800, strictStereo=True, keep_confs=None): """Generate charges for an OpenEye OEMol molecule. Parameters ---------- molecule : OEMol Molecule for which to generate conformers. Omega will be used to generate max_confs conformations. max_confs : int, optional, default=800 Max number of conformers to generate strictStereo : bool, optional, default=True If False, permits smiles strings with unspecified stereochemistry. See https://docs.eyesopen.com/omega/usage.html keep_confs : int, optional, default=None If not None, only keep this many conformations in the final charged OEMol. Multiple conformations are still used to *determine* the charges. For example, keep_confs=1 will return an OEMol with just a single conformation, while keep_confs=None returns all the conformations generated by Omega. Returns ------- charged_copy : OEMol A molecule with OpenEye's recommended AM1BCC charge selection scheme. Notes ----- Roughly follows http://docs.eyesopen.com/toolkits/cookbook/python/modeling/am1-bcc.html """ oechem = import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise (ImportError("Need License for OEChem!")) oequacpac = import_("openeye.oequacpac") if not oequacpac.OEQuacPacIsLicensed(): raise (ImportError("Need License for oequacpac!")) molecule = normalize_molecule(molecule) charged_copy = generate_conformers( molecule, max_confs=max_confs, strictStereo=strictStereo ) # Generate up to max_confs conformers status = oequacpac.OEAssignPartialCharges( charged_copy, oequacpac.OECharges_AM1BCCSym ) # AM1BCCSym recommended by Chris Bayly to KAB+JDC, Oct. 20 2014. if not status: raise (RuntimeError("OEAssignPartialCharges returned error code %d" % status)) for k, conf in enumerate(charged_copy.GetConfs()): if keep_confs is not None and k > keep_confs - 1: charged_copy.DeleteConf(conf) return charged_copy
def generate_conformers(molecule, max_confs=800, strictStereo=True, ewindow=15.0, rms_threshold=1.0, strictTypes = True): """Generate conformations for the supplied molecule Parameters ---------- molecule : OEMol Molecule for which to generate conformers max_confs : int, optional, default=800 Max number of conformers to generate. If None, use default OE Value. strictStereo : bool, optional, default=True If False, permits smiles strings with unspecified stereochemistry. strictTypes : bool, optional, default=True If True, requires that Omega have exact MMFF types for atoms in molecule; otherwise, allows the closest atom type of the same element to be used. Returns ------- molcopy : OEMol A multi-conformer molecule with up to max_confs conformers. Notes ----- Roughly follows http://docs.eyesopen.com/toolkits/cookbook/python/modeling/am1-bcc.html """ oechem = import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise(ImportError("Need License for OEChem!")) oeomega = import_("openeye.oeomega") if not oeomega.OEOmegaIsLicensed(): raise(ImportError("Need License for OEOmega!")) molcopy = oechem.OEMol(molecule) omega = oeomega.OEOmega() # These parameters were chosen to match http://docs.eyesopen.com/toolkits/cookbook/python/modeling/am1-bcc.html omega.SetMaxConfs(max_confs) omega.SetIncludeInput(True) omega.SetCanonOrder(False) omega.SetSampleHydrogens(True) # Word to the wise: skipping this step can lead to significantly different charges! omega.SetEnergyWindow(ewindow) omega.SetRMSThreshold(rms_threshold) # Word to the wise: skipping this step can lead to significantly different charges! omega.SetStrictStereo(strictStereo) omega.SetStrictAtomTypes(strictTypes) omega.SetIncludeInput(False) # don't include input if max_confs is not None: omega.SetMaxConfs(max_confs) status = omega(molcopy) # generate conformation if not status: raise(RuntimeError("omega returned error code %d" % status)) return molcopy
def smiles_to_antechamber(smiles_string, gaff_mol2_filename, frcmod_filename, residue_name="MOL", strictStereo=False, protonation=False): """Build a molecule from a smiles string and run antechamber, generating GAFF mol2 and frcmod files from a smiles string. Charges will be generated using the OpenEye QuacPac AM1-BCC implementation. Parameters ---------- smiles_string : str Smiles string of molecule to construct and charge gaff_mol2_filename : str Filename of mol2 file output of antechamber, with charges created from openeye frcmod_filename : str Filename of frcmod file output of antechamber. Most likely this file will be almost empty, at least for typical molecules. residue_name : str, optional, default="MOL" OpenEye writes mol2 files with <0> as the residue / ligand name. This chokes many mol2 parsers, so we replace it with a string of your choosing. This might be useful for downstream applications if the residue names are required to be unique. strictStereo : bool, optional, default=False If False, permits smiles strings with unspecified stereochemistry. See https://docs.eyesopen.com/omega/usage.html protonation : bool, optional, default=False If True, uses OESetNeutralpHModel to set a pH model for the molecule to attempt to obtain protonation states appropriate for neutral pH. Depending on the application this may or may not be what you want, e.g. for hydration free energy calculations you may want the typical depicted (neutral) form. """ oechem = import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise(ImportError("Need License for oechem!")) oequacpac = import_("openeye.oequacpac") # Get the absolute path so we can find these filenames from inside a temporary directory. gaff_mol2_filename = os.path.abspath(gaff_mol2_filename) frcmod_filename = os.path.abspath(frcmod_filename) m = smiles_to_oemol(smiles_string) if protonation: oequacpac.OESetNeutralpHModel(m) m = get_charges(m, strictStereo=strictStereo, keep_confs=1) with enter_temp_directory(): # Avoid dumping 50 antechamber files in local directory. _unused = molecule_to_mol2(m, "./tmp.mol2", residue_name=residue_name) net_charge = oechem.OENetCharge(m) tmp_gaff_mol2_filename, tmp_frcmod_filename = run_antechamber("tmp", "./tmp.mol2", charge_method=None, net_charge=net_charge) # USE OE AM1BCC charges! shutil.copy(tmp_gaff_mol2_filename, gaff_mol2_filename) shutil.copy(tmp_frcmod_filename, frcmod_filename)
def smiles_to_oemol(smiles): """Create a OEMolBuilder from a smiles string. Parameters ---------- smiles : str SMILES representation of desired molecule. Returns ------- molecule : OEMol A normalized molecule with desired smiles string. """ oechem = import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise (ImportError("Need License for OEChem!")) molecule = oechem.OEMol() if not oechem.OEParseSmiles(molecule, smiles): raise ValueError("The supplied SMILES '%s' could not be parsed." % smiles) molecule = normalize_molecule(molecule) return molecule
def get_names_to_charges(molecule): """Return a dictionary of atom names and partial charges, as well as a string representation. Parameters ---------- molecule : OEMol Molecule for which to grab charges Returns ------- data : dictionary A dictinoary whose (key, val) pairs are the atom names and partial charges, respectively. molrepr : str A string representation of data """ oechem = import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise(ImportError("Need License for oechem!")) molcopy = oechem.OEMol(molecule) molrepr = "" data = {} for atom in molcopy.GetAtoms(): name = atom.GetName() charge = atom.GetPartialCharge() data[name] = charge molrepr += "%s %f \n" % (name, charge) return data, molrepr
def molecule_to_mol2(molecule, tripos_mol2_filename=None, conformer=0, residue_name="MOL"): """Convert OE molecule to tripos mol2 file. Parameters ---------- molecule : openeye.oechem.OEGraphMol The molecule to be converted. tripos_mol2_filename : str, optional, default=None Output filename. If None, will create a filename similar to name.tripos.mol2, where name is the name of the OE molecule. conformer : int, optional, default=0 Save this frame residue_name : str, optional, default="MOL" OpenEye writes mol2 files with <0> as the residue / ligand name. This chokes many mol2 parsers, so we replace it with a string of your choosing. Returns ------- tripos_mol2_filename : str Filename of output tripos mol2 file """ oechem = import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise (ImportError("Need License for oechem!")) # Get molecule name. molecule_name = molecule.GetTitle() logger.debug(molecule_name) # Write molecule as Tripos mol2. if tripos_mol2_filename is None: tripos_mol2_filename = molecule_name + '.tripos.mol2' ofs = oechem.oemolostream(tripos_mol2_filename) ofs.SetFormat(oechem.OEFormat_MOL2H) for k, mol in enumerate(molecule.GetConfs()): if k == conformer: oechem.OEWriteMolecule(ofs, mol) ofs.close() # Replace <0> substructure names with valid text. infile = open(tripos_mol2_filename, 'r') lines = infile.readlines() infile.close() newlines = [line.replace('<0>', residue_name) for line in lines] outfile = open(tripos_mol2_filename, 'w') outfile.writelines(newlines) outfile.close() return molecule_name, tripos_mol2_filename
def molecule_to_mol2(molecule, tripos_mol2_filename=None, conformer=0, residue_name="MOL"): """Convert OE molecule to tripos mol2 file. Parameters ---------- molecule : openeye.oechem.OEGraphMol The molecule to be converted. tripos_mol2_filename : str, optional, default=None Output filename. If None, will create a filename similar to name.tripos.mol2, where name is the name of the OE molecule. conformer : int, optional, default=0 Save this frame residue_name : str, optional, default="MOL" OpenEye writes mol2 files with <0> as the residue / ligand name. This chokes many mol2 parsers, so we replace it with a string of your choosing. Returns ------- tripos_mol2_filename : str Filename of output tripos mol2 file """ oechem = import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise (ImportError("Need License for oechem!")) # Get molecule name. molecule_name = molecule.GetTitle() logger.debug(molecule_name) # Write molecule as Tripos mol2. if tripos_mol2_filename is None: tripos_mol2_filename = molecule_name + ".tripos.mol2" ofs = oechem.oemolostream(tripos_mol2_filename) ofs.SetFormat(oechem.OEFormat_MOL2H) for k, mol in enumerate(molecule.GetConfs()): if k == conformer: oechem.OEWriteMolecule(ofs, mol) ofs.close() # Replace <0> substructure names with valid text. infile = open(tripos_mol2_filename, "r") lines = infile.readlines() infile.close() newlines = [line.replace("<0>", residue_name) for line in lines] outfile = open(tripos_mol2_filename, "w") outfile.writelines(newlines) outfile.close() return molecule_name, tripos_mol2_filename
def normalize_molecule(molecule): """ Normalize a copy of the molecule by checking aromaticity, adding explicit hydrogens, and (if possible) renaming by IUPAC name. Parameters ---------- molecule : OEMol the molecule to be normalized. Returns ------- molcopy : OEMol A (copied) version of the normalized molecule """ oechem = import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise(ImportError("Need License for OEChem!")) oeiupac = import_("openeye.oeiupac") has_iupac = oeiupac.OEIUPACIsLicensed() molcopy = oechem.OEMol(molecule) # Assign aromaticity. oechem.OEAssignAromaticFlags(molcopy, oechem.OEAroModelOpenEye) # Add hydrogens. oechem.OEAddExplicitHydrogens(molcopy) # Set title to IUPAC name. if has_iupac: name = oeiupac.OECreateIUPACName(molcopy) molcopy.SetTitle(name) # Check for any missing atom names, if found reassign all of them. if any([atom.GetName() == '' for atom in molcopy.GetAtoms()]): oechem.OETriposAtomNames(molcopy) return molcopy
def writeSDF(mol2_filename, sdf_filename, mol_name): """For generating .sdf file format from .mol2 file, using OEIFlavor (OpenEye). Creates three tags (partial_charges, partial_bond_orders, and atom_types) in the .sdf file using values from the reference (.mol2 file). Parameters ---------- mol2_filename: str Mol2 filename used to write the .sdf file. The .mol2 file is preserved. sdf_filename: str SDF filename (path) used to save the sdf file generated. mol_name: str Name of molecule used to write the title for the .sdf file generated. Notes ----------- In partial_bonds_orders tag, .mol2 bond orders are translated to .sdf bond orders using OEIFlavor, aromatic bonds ('ar') are transformed to 1 or 2. Limitations ----------- Creates only three tags in .sdf file. The tags are partial_charges, partial_bond_orders, and atom_types (GAFF). Their values are set according to the correspondent .mol2 file properties. """ oechem = import_("openeye.oechem") ifs = oechem.oemolistream(mol2_filename) MOL2flavor = oechem.OEIFlavor_Generic_Default | oechem.OEIFlavor_MOL2_Default | oechem.OEIFlavor_MOL2_Forcefield ifs.SetFlavor(oechem.OEFormat_MOL2, MOL2flavor) ofs = oechem.oemolostream(sdf_filename) tag_names = ['partial_charges', 'partial_bond_orders', 'atom_types'] # Set the title and assign partial charges for the .mol2 file for mol in ifs.GetOEGraphMols(): mol.SetTitle(mol_name) molToCharge = oechem.OEMol(mol) # Get partial charges and atom types from .mol2 file and store them to put into the .sdf file as tags charges = [] atom_types = [] for atom, atomCharged in zip(mol.GetAtoms(), molToCharge.GetAtoms()): atom.SetPartialCharge( atomCharged.GetPartialCharge() ) charges += [atom.GetPartialCharge()] atom_types += [atom.GetType()] #print("partial charges " + str(charges)) #print("atom types: " + str(atom_types)) # Create the tags for the sdf file mol = createTag(tag_names, mol, charges, atom_types) oechem.OEWriteMolecule(ofs, mol) ifs.close() ofs.close()
def createTag(tag_names, mol, charges, atom_types): """Create the tags for sdf file.""" oechem = import_("openeye.oechem") mol_id = oechem.OEGetSDData(mol, 'Mol_Index') for tag in tag_names: if tag == 'partial_charges': value = manipulatePartialChargesTag(charges) elif tag == 'partial_bond_orders': value = manipulateBondOrdersTag(mol) elif tag == 'atom_types': value = manipulateAtomTypes(atom_types) else: #Tag Name Error raise Exception('Wrong tag name') break oechem.OESetSDData(mol, oechem.OESDDataPair(tag, value)) return mol
import simtk.openmm as mm import numpy as np import re from mdtraj.testing import eq from unittest import skipIf from openmoltools import utils, packmol import os import openmoltools.openeye import pandas as pd import mdtraj as md from numpy.testing import assert_raises smiles_fails_with_strictStereo = "CN1CCN(CC1)CCCOc2cc3c(cc2OC)C(=[NH+]c4cc(c(cc4Cl)Cl)OC)C(=C=[N-])C=[NH+]3" try: oechem = utils.import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise (ImportError("Need License for OEChem!")) oequacpac = utils.import_("openeye.oequacpac") if not oequacpac.OEQuacPacIsLicensed(): raise (ImportError("Need License for oequacpac!")) oeiupac = utils.import_("openeye.oeiupac") if not oeiupac.OEIUPACIsLicensed(): raise (ImportError("Need License for OEOmega!")) oeomega = utils.import_("openeye.oeomega") if not oeomega.OEOmegaIsLicensed(): raise (ImportError("Need License for OEOmega!")) HAVE_OE = True openeye_exception_message = str() except Exception as e: HAVE_OE = False
from nose.plugins.attrib import attr from unittest import skipIf from openmoltools import utils import os try: oechem = utils.import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise(ImportError("Need License for OEChem!")) oequacpac = utils.import_("openeye.oequacpac") if not oequacpac.OEQuacPacIsLicensed(): raise(ImportError("Need License for oequacpac!")) oeiupac = utils.import_("openeye.oeiupac") if not oeiupac.OEIUPACIsLicensed(): raise(ImportError("Need License for OEOmega!")) oeomega = utils.import_("openeye.oeomega") if not oeomega.OEOmegaIsLicensed(): raise(ImportError("Need License for OEOmega!")) HAVE_OE = True except: HAVE_OE = False @skipIf(not HAVE_OE, "Cannot run test_drugs() module without OpenEye tools.") @attr('slow') def test_drugs(): import openeye.oechem database_filename = utils.get_data_filename("chemicals/drugs/Zdd.mol2.gz") ifs = openeye.oechem.oemolistream(database_filename) for molecule in ifs.GetOEMols(): with utils.enter_temp_directory(): molecule_name, tripos_mol2_filename = utils.molecule_to_mol2(molecule) yield utils.tag_description(lambda : utils.test_molecule(molecule_name, tripos_mol2_filename), "Testing drugs %s" % molecule_name) @skipIf(not HAVE_OE, "Cannot test test_drug() without OpenEye tools.")
def get_charges(molecule, max_confs=800, strictStereo=True, keep_confs=None): """Generate charges for an OpenEye OEMol molecule. Parameters ---------- molecule : OEMol Molecule for which to generate conformers. Omega will be used to generate max_confs conformations. max_confs : int, optional, default=800 Max number of conformers to generate strictStereo : bool, optional, default=True If False, permits smiles strings with unspecified stereochemistry. See https://docs.eyesopen.com/omega/usage.html keep_confs : int, optional, default=None If None, apply the charges to the provided conformation and return this conformation. Otherwise, return some or all of the generated conformations. If -1, all generated conformations are returned. Otherwise, keep_confs = N will return an OEMol with up to N generated conformations. Multiple conformations are still used to *determine* the charges. Returns ------- charged_copy : OEMol A molecule with OpenEye's recommended AM1BCC charge selection scheme. Notes ----- Roughly follows http://docs.eyesopen.com/toolkits/cookbook/python/modeling/am1-bcc.html """ oechem = import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise (ImportError("Need License for OEChem!")) oequacpac = import_("openeye.oequacpac") if not oequacpac.OEQuacPacIsLicensed(): raise (ImportError("Need License for oequacpac!")) molecule = normalize_molecule(molecule) charged_copy = generate_conformers( molecule, max_confs=max_confs, strictStereo=strictStereo) # Generate up to max_confs conformers status = oequacpac.OEAssignPartialCharges( charged_copy, oequacpac.OECharges_AM1BCCSym ) # AM1BCCSym recommended by Chris Bayly to KAB+JDC, Oct. 20 2014. if not status: raise (RuntimeError("OEAssignPartialCharges returned error code %d" % status)) #Determine conformations to return if keep_confs == None: #If returning original conformation original = molecule.GetCoords() #Delete conformers over 1 for k, conf in enumerate(charged_copy.GetConfs()): if k > 0: charged_copy.DeleteConf(conf) #Copy coordinates to single conformer charged_copy.SetCoords(original) elif keep_confs > 0: #Otherwise if a number is provided, return this many confs if available for k, conf in enumerate(charged_copy.GetConfs()): if k > keep_confs - 1: charged_copy.DeleteConf(conf) elif keep_confs == -1: #If we want all conformations, continue pass else: #Not a valid option to keep_confs raise (ValueError('Not a valid option to keep_confs in get_charges.')) return charged_copy
def get_charges(molecule, max_confs=800, strictStereo=True, normalize=True, keep_confs=None, legacy=True): """Generate charges for an OpenEye OEMol molecule. Parameters ---------- molecule : OEMol Molecule for which to generate conformers. Omega will be used to generate max_confs conformations. max_confs : int, optional, default=800 Max number of conformers to generate strictStereo : bool, optional, default=True If False, permits smiles strings with unspecified stereochemistry. See https://docs.eyesopen.com/omega/usage.html normalize : bool, optional, default=True If True, normalize the molecule by checking aromaticity, adding explicit hydrogens, and renaming by IUPAC name. keep_confs : int, optional, default=None If None, apply the charges to the provided conformation and return this conformation, unless no conformation is present. Otherwise, return some or all of the generated conformations. If -1, all generated conformations are returned. Otherwise, keep_confs = N will return an OEMol with up to N generated conformations. Multiple conformations are still used to *determine* the charges. legacy : bool, default=True If False, uses the new OpenEye charging engine. See https://docs.eyesopen.com/toolkits/python/quacpactk/OEProtonFunctions/OEAssignCharges.html# Returns ------- charged_copy : OEMol A molecule with OpenEye's recommended AM1BCC charge selection scheme. Notes ----- Roughly follows http://docs.eyesopen.com/toolkits/cookbook/python/modeling/am1-bcc.html """ # If there is no geometry, return at least one conformation. if molecule.GetConfs() == 0: keep_confs = 1 oechem = import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise(ImportError("Need License for OEChem!")) oequacpac = import_("openeye.oequacpac") if not oequacpac.OEQuacPacIsLicensed(): raise(ImportError("Need License for oequacpac!")) if normalize: molecule = normalize_molecule(molecule) else: molecule = oechem.OEMol(molecule) charged_copy = generate_conformers(molecule, max_confs=max_confs, strictStereo=strictStereo) # Generate up to max_confs conformers if not legacy: # 2017.2.1 OEToolkits new charging function status = oequacpac.OEAssignCharges(charged_copy, oequacpac.OEAM1BCCCharges()) if not status: raise(RuntimeError("OEAssignCharges failed.")) else: # AM1BCCSym recommended by Chris Bayly to KAB+JDC, Oct. 20 2014. status = oequacpac.OEAssignPartialCharges(charged_copy, oequacpac.OECharges_AM1BCCSym) if not status: raise(RuntimeError("OEAssignPartialCharges returned error code %d" % status)) #Determine conformations to return if keep_confs == None: #If returning original conformation original = molecule.GetCoords() #Delete conformers over 1 for k, conf in enumerate( charged_copy.GetConfs() ): if k > 0: charged_copy.DeleteConf(conf) #Copy coordinates to single conformer charged_copy.SetCoords( original ) elif keep_confs > 0: logger.debug("keep_confs was set to %s. Molecule positions will be reset." % keep_confs) #Otherwise if a number is provided, return this many confs if available for k, conf in enumerate( charged_copy.GetConfs() ): if k > keep_confs - 1: charged_copy.DeleteConf(conf) elif keep_confs == -1: #If we want all conformations, continue pass else: #Not a valid option to keep_confs raise(ValueError('Not a valid option to keep_confs in get_charges.')) return charged_copy
def mole_fractions_to_n_monomers(self, density= 1 * grams/milliliter, cutoff=12*angstrom): """ This function is used to generate the number of molecules for each compound in the solution from the mole fractions of each molecule. Parameters ---------- density : openmm units the solution density cutoff : openmm units the cutoff distance of the largest compound in the solution Returns ------- self.n_monomers : integer list the list of molecule number for each compound in the solution size : float the edge of the box volume """ oechem = import_("openeye.oechem") # Calculate the maximum atomic distance in a molecule def max_dist_mol(mol): max_dist = 0.0 coords = mol.GetCoords() # Are the coords always in A in mol2 file? for i in range(0, mol.NumAtoms()): crdi = np.array([coords[i][0], coords[i][1], coords[i][2]]) for j in range(i+1, mol.NumAtoms()): crdj = np.array([coords[j][0], coords[j][1], coords[j][2]]) dist = np.linalg.norm(crdi-crdj) if dist > max_dist: max_dist = dist return max_dist * angstrom # The sum of all the mole fractions sum_fractions = sum([i for i in self.mole_fractions if i != None]) if sum_fractions > 1.0: raise ValueError('Error: The total molar fraction is greater than 1.0') if sum_fractions == 1.0 and self.filling_compound: raise ValueError('Error: The total molar fraction is 1.0 and it is not possible to add any filling compound to the solution, but a filling compound was specified') if sum_fractions < 1.0 and not self.filling_compound: raise ValueError('Error: The total molar fraction is less than 1.0 and no filling compound (i.e. compound with unspecified mole fraction) is provided') if self.filling_compound: self.filling_compound.mole_fraction = 1.0 - sum_fractions self.mole_fractions = [i if i != None else (1.0 - sum_fractions) for i in self.mole_fractions] max_dist_mols = 0.0 * angstrom delta_volume = 0.0 * angstrom**3 sum_wgt_frac = 0.0 * grams/mole for i in range(0, len(self.sdf_filenames)): istream = oechem.oemolistream(self.sdf_filenames[i])#gaff_mol2_files give wrong wgt because not sybyl format! mol = oechem.OEMol() if not oechem.OEReadMolecule(istream, mol): raise IOError('Error: It was not possible to create the OpenEye molecule object by reading the file: %s' % self.gaff_mol2_filenames[i]) # Molecular weight wgt = oechem.OECalculateMolecularWeight(mol) * grams/mole if self.component_list[i].mole_fraction == 0.0: delta_volume = oechem.OECalculateMolecularWeight(mol) * angstrom**3 sum_wgt_frac = sum_wgt_frac + wgt * self.component_list[i].mole_fraction max_dist= max_dist_mol(mol) if max_dist > max_dist_mols: max_dist_mols = max_dist cube_length = ((max_dist_mols + 2*cutoff)**3 + delta_volume)**(1.0/3.0) n_monomers = [] # n_i = Volume * Density * mole_fraction_i/sum_j(wgt_j * mole_fraction_j) self.n_monomers = [int(round(AVOGADRO_CONSTANT_NA * comp.mole_fraction * density * cube_length**3 / sum_wgt_frac)) \ if comp.mole_fraction !=0 else 1 for comp in self.component_list] return self.n_monomers, cube_length
def get_charges(molecule, max_confs=800, strictStereo=True, keep_confs=None): """Generate charges for an OpenEye OEMol molecule. Parameters ---------- molecule : OEMol Molecule for which to generate conformers. Omega will be used to generate max_confs conformations. max_confs : int, optional, default=800 Max number of conformers to generate strictStereo : bool, optional, default=True If False, permits smiles strings with unspecified stereochemistry. See https://docs.eyesopen.com/omega/usage.html keep_confs : int, optional, default=None If None, apply the charges to the provided conformation and return this conformation. Otherwise, return some or all of the generated conformations. If -1, all generated conformations are returned. Otherwise, keep_confs = N will return an OEMol with up to N generated conformations. Multiple conformations are still used to *determine* the charges. Returns ------- charged_copy : OEMol A molecule with OpenEye's recommended AM1BCC charge selection scheme. Notes ----- Roughly follows http://docs.eyesopen.com/toolkits/cookbook/python/modeling/am1-bcc.html """ oechem = import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise(ImportError("Need License for OEChem!")) oequacpac = import_("openeye.oequacpac") if not oequacpac.OEQuacPacIsLicensed(): raise(ImportError("Need License for oequacpac!")) molecule = normalize_molecule(molecule) charged_copy = generate_conformers(molecule, max_confs=max_confs, strictStereo=strictStereo) # Generate up to max_confs conformers status = oequacpac.OEAssignPartialCharges(charged_copy, oequacpac.OECharges_AM1BCCSym) # AM1BCCSym recommended by Chris Bayly to KAB+JDC, Oct. 20 2014. if not status: raise(RuntimeError("OEAssignPartialCharges returned error code %d" % status)) #Determine conformations to return if keep_confs == None: #If returning original conformation original = molecule.GetCoords() #Delete conformers over 1 for k, conf in enumerate( charged_copy.GetConfs() ): if k > 0: charged_copy.DeleteConf(conf) #Copy coordinates to single conformer charged_copy.SetCoords( original ) elif keep_confs > 0: #Otherwise if a number is provided, return this many confs if available for k, conf in enumerate( charged_copy.GetConfs() ): if k > keep_confs - 1: charged_copy.DeleteConf(conf) elif keep_confs == -1: #If we want all conformations, continue pass else: #Not a valid option to keep_confs raise(ValueError('Not a valid option to keep_confs in get_charges.')) return charged_copy
def __init__(self, name=None, label=None, smiles=None, number=None, mole_fraction=None): """ Initialization class function Parameters ---------- name : str the molecule name label : str the molecule label used to generates files smiles : str the molecule SMILES string number : int the number of molecule mole_fraction : float molecular mole fraction REQUIRED: A name and/or label. If no SMILES is provided, the name will be used to generate SMILES and if no name is provided, the label will be used to attempt to generate SMILES. """ oechem = import_("openeye.oechem") oeiupac = import_("openeye.oeiupac") # Checking name and label ref_str = '' if not name and not label: raise ValueError("Error: No component parameters name or label"+ " have been provided for the component") if label: if not isinstance(label, str): raise ValueError("Error: The component label %s is not a string" % label) ref_str = label if name: if not isinstance(name, str): raise ValueError("Error: The component name %s is not a string" % name) ref_str = name if label and not name: print('\nWARNING: component name not provided; label will be used as component name\n') # Checking smiles, molecule number and mole fraction if smiles: if not isinstance(smiles, str): raise ValueError("Error: The SMILES % for the component %s is not a string" % (smiles, ref_str)) #Check this is a valid SMILES string mol = oechem.OEMol() status = oechem.OEParseSmiles(mol, smiles) if not status: raise ValueError("Error: The SMILES %s for the component %s" " cannot be processed by OEChem." % (smiles, ref_str) ) if number is not None: if not isinstance(number, int): raise ValueError("Error: The molecule quantity %s for the component %s is not an integer" % (number, ref_str)) if number < 1: raise ValueError("Error: The molecule quantity %s for the component %s must be a positive integer" % (number, ref_str)) if mole_fraction: if not isinstance(mole_fraction, float): raise ValueError("Error: The mole fraction %s for the component %s is not a float number" % (mole_fraction, ref_str)) if mole_fraction < 0.0: raise ValueError("Error: The mole fraction %s for the component %s must be positive" % (mole_fraction, ref_str)) if mole_fraction > 1.0: raise ValueError("Error: The mole fraction %s for the component %s is greater than one" % (mole_fraction, ref_str)) if number and mole_fraction: raise ValueError("Error: molecule number and mole fraction for the compound %s cannot be both specified" % ref_str) if not smiles: mol = oechem.OEMol() if name: try: oeiupac.OEParseIUPACName(mol, name) smiles = oechem.OECreateIsoSmiString(mol) #If smiles is empty, didn't parse correctly if smiles == '': raise ValueError("Error: The supplied name '%s' could not be parsed" % name) except: raise ValueError("Error: The supplied name '%s' could not be parsed" % name) elif label: try: oeiupac.OEParseIUPACName(mol, label) smiles = oechem.OECreateIsoSmiString(mol) if smiles == '': raise ValueError("Error: The supplied name '%s' could not be parsed" % name) except: raise ValueError("Error: The supplied label '%s' could not be parsed" % label) self.name = name self.label = label self.smiles = smiles self.number = number self.mole_fraction = mole_fraction return
def molecule_to_mol2(molecule, tripos_mol2_filename=None, conformer=0, residue_name="MOL", standardize=True): """Convert OE molecule to tripos mol2 file. Parameters ---------- molecule : openeye.oechem.OEGraphMol The molecule to be converted. tripos_mol2_filename : str, optional, default=None Output filename. If None, will create a filename similar to name.tripos.mol2, where name is the name of the OE molecule. conformer : int, optional, default=0 Save this frame If None, save all conformers residue_name : str, optional, default="MOL" OpenEye writes mol2 files with <0> as the residue / ligand name. This chokes many mol2 parsers, so we replace it with a string of your choosing. standardize: bool, optional, default=True Use a high-level writer, which will standardize the molecular properties. Set this to false if you wish to retain things such as atom names. In this case, a low-level writer will be used. Returns ------- tripos_mol2_filename : str Filename of output tripos mol2 file """ oechem = import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise(ImportError("Need License for oechem!")) # Get molecule name. molecule_name = molecule.GetTitle() logger.debug(molecule_name) # Write molecule as Tripos mol2. if tripos_mol2_filename is None: tripos_mol2_filename = molecule_name + '.tripos.mol2' ofs = oechem.oemolostream(tripos_mol2_filename) ofs.SetFormat(oechem.OEFormat_MOL2H) for k, mol in enumerate(molecule.GetConfs()): if k == conformer or conformer is None: # Standardize will override molecular properties(atom names etc.) if standardize: oechem.OEWriteMolecule(ofs, mol) else: oechem.OEWriteMol2File(ofs, mol) ofs.close() # Replace <0> substructure names with valid text. infile = open(tripos_mol2_filename, 'r') lines = infile.readlines() infile.close() newlines = [line.replace('<0>', residue_name) for line in lines] outfile = open(tripos_mol2_filename, 'w') outfile.writelines(newlines) outfile.close() return molecule_name, tripos_mol2_filename