def get_ani_mol(coordinates, species, smiles): """ Given smiles string and list of elements as reference, get the RDKit mol with xyz. """ mol = oechem.OEGraphMol() for symbol in species: mol.NewAtom(getattr(oechem, 'OEElemNo_' + symbol)) mol.SetCoords(coordinates.reshape([-1])) mol.SetDimension(3) oechem.OEDetermineConnectivity(mol) oechem.OEFindRingAtomsAndBonds(mol) oechem.OEPerceiveBondOrders(mol) smiles_can = oechem.OECreateCanSmiString(mol) ims = oechem.oemolistream() ims.SetFormat(oechem.OEFormat_SMI) ims.openstring(smiles) mol_ref = next(ims.GetOEMols()) smiles_ref = oechem.OECreateCanSmiString(mol_ref) assert smiles_can == smiles_ref g = hgfp.graph.from_oemol(mol, use_fp=True) return g, mol
def createMolecule(self, mol, molTitle): ''' Get a OE residue, return a OE molecule ''' # Creating a new Res GraphMol newMol = oechem.OEGraphMol() newMol.SetTitle(molTitle) # Create every atom found in the OEHierResidue for atom in mol.GetAtoms(): newMol.NewAtom(atom) #---------------------------------------------# # Recreate the molecule from atom coordinates # #---------------------------------------------# # Using OpenEye's charge model. Works on molecules with fully specified # hydrogen counts. Charge determined based on atom valence. oechem.OEDetermineConnectivity(newMol) oechem.OEFindRingAtomsAndBonds(newMol) oechem.OEPerceiveBondOrders(newMol) oechem.OEAssignImplicitHydrogens(newMol) oechem.OEAssignFormalCharges(newMol) # Other things to consider # Set atom radius #oechem.OEAssignBondiVdWRadii(newMol) #oechem.OEAssignPartialCharges(newMol, # oechem.OECharges_None, # False, # False) return newMol
def mol_from_json(symbols, connectivity, geometry, permute_xyz=False): """ Generate OEMol from QCSchema molecule specs Parameters ---------- inp_molecule: dict Must have symbols and connectivity and/or geometry Note: If geometry is given, the molecule will have a tag indicating that the goemetry came from QCSchema. This will ensure that the order of the atoms and configuration is not change for generation of mapped SMILES and isomeric SMILES. Returns ------- molecule: OEMol """ molecule = oechem.OEMol() for s in symbols: molecule.NewAtom(_symbols[s]) # Add connectivity for bond in connectivity: a1 = molecule.GetAtom(oechem.OEHasAtomIdx(bond[0])) a2 = molecule.GetAtom(oechem.OEHasAtomIdx(bond[1])) bond_order = bond[-1] if not isinstance(bond_order, int) and not bond_order.is_integer(): raise ValueError( "bond order must be a whole number. A bond order of 1.5 is not allowed" ) molecule.NewBond(a1, a2, int(bond_order)) # Add geometry if molecule.NumAtoms() != geometry.shape[0] / 3: raise ValueError( "Number of atoms in molecule does not match length of position array" ) molecule.SetCoords(oechem.OEFloatArray(geometry)) molecule.SetDimension(3) if not permute_xyz: # Add tag that the geometry is from JSON and shouldn't be changed. geom_tag = oechem.OEGetTag("json_geometry") molecule.SetData(geom_tag, True) oechem.OEDetermineConnectivity(molecule) oechem.OEFindRingAtomsAndBonds(molecule) # No need to perceive Bond Order because the information was added from the connectivity table. Apparently, this # function does many different perceptions under the hood and it can add implicit hydrogens to divalent N that should be negatively charged. #oechem.OEPerceiveBondOrders(molecule) # This seems to add hydrogens that are not in the json #oechem.OEAssignImplicitHydrogens(molecule) oechem.OEAssignFormalCharges(molecule) oechem.OEAssignAromaticFlags(molecule) oechem.OEPerceiveChiral(molecule) oechem.OE3DToAtomStereo(molecule) oechem.OE3DToBondStereo(molecule) return molecule
def _pmdStructureToOEMol(self): top = self.structure.topology pos = self.structure.positions molecule = oeommtools.openmmTop_to_oemol(top, pos, verbose=False) oechem.OEPerceiveResidues(molecule) oechem.OEFindRingAtomsAndBonds(molecule) return molecule
def search(self, mol0, mol1): mol0 = mol0._struc mol1 = mol1._struc p0 = mol0.CreateCopy() p1 = mol1.CreateCopy() #set atom int type. for mol in ( p0, p1, ): for atom in mol.GetAtoms(): if (atom.IsHydrogen()): atom.SetIntType(1) else: atom.SetIntType(2) #suppress hydrogens before mcs search oechem.OESuppressHydrogens(p0) oechem.OESuppressHydrogens(p1) if (self._is_approximate): mcss = oechem.OEMCSSearch(p1, self._atom_expr, self._bond_expr, oechem.OEMCSType_Approximate) else: mcss = oechem.OEMCSSearch(p1, self._atom_expr, self._bond_expr) #set minimum atom of the mcs mcss.SetMinAtoms(1) #set the function to evalue the mcs search mcss.SetMCSFunc(oechem.OEMCSMaxAtomsCompleteCycles(1.5)) # There could be multiple matches. We select the one with the maximum number of atoms. # If there are more than 1 matches with the same maximum number of atoms, we arbitrarily select the first one. mcs_mol = None max_num = 0 #do the mcs search for match in mcss.Match(p0, True): num_atom = 0 mcs_tmp = oechem.OEMol() oechem.OESubsetMol(mcs_tmp, match, True) oechem.OEFindRingAtomsAndBonds(mcs_tmp) for atom in mcs_tmp.GetAtoms(): if (not atom.IsHydrogen()): num_atom += 1 if (num_atom > max_num): max_num = num_atom mcs_mol = mcs_tmp atom_match0 = [] atom_match1 = [] for matchpair in match.GetAtoms(): atom_match0.append(matchpair.target.GetIdx() + 1) atom_match1.append(matchpair.pattern.GetIdx() + 1) #dump search result to kbase if (mcs_mol): mol0 = struc.OeStruc(mol0) mol1 = struc.OeStruc(mol1) mcs_mol = struc.OeStruc(mcs_mol) return self.deposit_to_kbase(mol0.id(), mol1.id(), atom_match0, atom_match1)
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 mol_from_json(symbols, connectivity, geometry, permute_xyz=False): """ Generate OEMol from QCSchema molecule specs Parameters ---------- inp_molecule: dict Must have symbols and connectivity and/or geometry Note: If geometry is given, the molecule will have a tag indicating that the goemetry came from QCSchema. This will ensure that the order of the atoms and configuration is not change for generation of mapped SMILES and isomeric SMILES. Returns ------- molecule: OEMol """ molecule = oechem.OEMol() for s in symbols: molecule.NewAtom(_symbols[s]) # Add connectivity for bond in connectivity: a1 = molecule.GetAtom(oechem.OEHasAtomIdx(bond[0])) a2 = molecule.GetAtom(oechem.OEHasAtomIdx(bond[1])) molecule.NewBond(a1, a2, bond[-1]) # Add geometry if molecule.NumAtoms() != geometry.shape[0] / 3: raise ValueError( "Number of atoms in molecule does not match length of position array" ) molecule.SetCoords(oechem.OEFloatArray(geometry)) molecule.SetDimension(3) if not permute_xyz: # Add tag that the geometry is from JSON and shouldn't be changed. geom_tag = oechem.OEGetTag("json_geometry") molecule.SetData(geom_tag, True) oechem.OEDetermineConnectivity(molecule) oechem.OEFindRingAtomsAndBonds(molecule) oechem.OEPerceiveBondOrders(molecule) # This seems to add hydrogens that are not in the json #oechem.OEAssignImplicitHydrogens(molecule) oechem.OEAssignFormalCharges(molecule) oechem.OEAssignAromaticFlags(molecule) oechem.OEPerceiveChiral(molecule) oechem.OE3DToAtomStereo(molecule) oechem.OE3DToBondStereo(molecule) return molecule
def GetFragments(mol, minbonds, maxbonds): from openeye import oegraphsim frags = [] fptype = oegraphsim.OEGetFPType("Tree,ver=2.0.0,size=4096,bonds=%d-%d,atype=AtmNum,btype=Order" % (minbonds, maxbonds)) for abset in oegraphsim.OEGetFPCoverage(mol, fptype, True): fragatompred = oechem.OEIsAtomMember(abset.GetAtoms()) frag = oechem.OEGraphMol() adjustHCount = True oechem.OESubsetMol(frag, mol, fragatompred, adjustHCount) oechem.OEFindRingAtomsAndBonds(frag) frags.append(oechem.OEGraphMol(frag)) return frags
def CanSmi(mol, isomeric, kekule): oechem.OEFindRingAtomsAndBonds(mol) oechem.OEAssignAromaticFlags(mol, oechem.OEAroModel_OpenEye) smiflag = oechem.OESMILESFlag_Canonical if isomeric: smiflag |= oechem.OESMILESFlag_ISOMERIC if kekule: for bond in mol.GetBonds(oechem.OEIsAromaticBond()): bond.SetIntType(5) oechem.OECanonicalOrderAtoms(mol) oechem.OECanonicalOrderBonds(mol) oechem.OEClearAromaticFlags(mol) oechem.OEKekulize(mol) smi = oechem.OECreateSmiString(mol, smiflag) return smi
def extract(self, indices) : """ Return a new structure object which contains the atoms of the current structure that appear in the specified list. """ #make a copy before deleting atoms new_mol = self._struc.CreateCopy() for atom in new_mol.GetAtoms(): oe_idx = atom.GetIdx() + 1 if oe_idx not in indices: new_mol.DeleteAtom(atom) #make a copy after deleting to make sure the idx start from 1 new_mol_copy = new_mol.CreateCopy() oechem.OEFindRingAtomsAndBonds(new_mol_copy) ret = OeStruc(new_mol_copy) indices.sort() for i, e in enumerate(indices, start = 1): ret.atom_prop[i] = copy.deepcopy(self.atom_prop[e]) return ret
def GetSameRingAtoms(mol, atomSet): ''' @type mol: OEGraphMol @type atomSet: OEAtomBondSet @return list[OEAtombase] ''' oechem.OEFindRingAtomsAndBonds(mol) _, ringIdxPerAtom = oechem.OEDetermineRingSystems(mol) toKeepRings = {} for atom in atomSet.GetAtoms(): if ringIdxPerAtom[atom.GetIdx()] > 0: toKeepRings[ringIdxPerAtom[atom.GetIdx()]] = True ringAtoms = [] for i in range(0, len(ringIdxPerAtom)): if ringIdxPerAtom[i] in toKeepRings: ringAtoms.append(mol.GetAtom(oechem.OEHasAtomIdx(i))) return ringAtoms
def ring_size (self): """ Returns a dictionary, which is atom's index correspoding to the ring size the atom in. @rtype : C{dic} of C{int} : C{int} @return: A dictionary of atom indices correspoing to the size of the ring they are in. """ ring_size = {} oechem.OEFindRingAtomsAndBonds(self._struc) nrrings, rings = oechem.OEDetermineRingSystems(self._struc) ring_dic = {} for ring_idx in rings: if not ring_dic.has_key(ring_idx): ring_dic[ring_idx] = [] for key in ring_dic.keys(): if key > 0: ring_dic[key] = rings.count(key) else: ring_dic[key] = 0 for (idx, atom) in enumerate(rings): oe_idx = idx +1 ring_size [oe_idx] = ring_dic[atom] return ring_size
def CanSmi(mol, isomeric, kekule): """ Returns the cannonical smile from the OEMol provided :param mol: OEMolBase object :param isomeric: force isometric :param kekule: use kekule cleaning :return: string of OESmiles """ oechem.OEFindRingAtomsAndBonds(mol) oechem.OEAssignAromaticFlags(mol, oechem.OEAroModel_OpenEye) smiflag = oechem.OESMILESFlag_Canonical if isomeric: smiflag |= oechem.OESMILESFlag_ISOMERIC if kekule: for bond in mol.GetBonds(oechem.OEIsAromaticBond()): bond.SetIntType(5) oechem.OECanonicalOrderAtoms(mol) oechem.OECanonicalOrderBonds(mol) oechem.OEClearAromaticFlags(mol) oechem.OEKekulize(mol) smi = oechem.OECreateSmiString(mol, smiflag) return smi
def create_openeye_molecule(pdb, options, verbose=True): """ Create OpenEye molecule from PDB representation. The molecule will have hydrogens added and be normalized, but the overall geometry will not be altered. Parameters ---------- pdb : Pdb The PDB-extracted entries for the ligand. Returns ------- molecule : openeye.oechem.OEMol Molecule representation. options : options struct Options structure. """ # Create a molecule container. molecule = oechem.OEGraphMol() # Open a PDB file reader from the stored PDB string representation of HETATM and CONECT records. print pdb.pdb_extract ifs = oechem.oemolistream() ifs.openstring(pdb.pdb_extract) flavor = oechem.OEIFlavor_Generic_Default | oechem.OEIFlavor_PDB_Default | oechem.OEIFlavor_PDB_ALL ifs.SetFlavor(oechem.OEFormat_PDB, flavor) oechem.OEReadPDBFile(ifs, molecule) # Add explicit hydrogens. oechem.OEDetermineConnectivity(molecule) oechem.OEFindRingAtomsAndBonds(molecule) oechem.OEAssignAromaticFlags(molecule) # check aromaticity oechem.OEPerceiveBondOrders(molecule) # We must assign implicit hydrogens first so that the valence model will be correct. oechem.OEAssignImplicitHydrogens(molecule) oechem.OEAssignFormalCharges(molecule) # Now add explicit hydrogens. polarOnly = False set3D = True oechem.OEAddExplicitHydrogens(molecule, polarOnly, set3D) # TODO: Sequentially number hydrogen atoms. # Perceive stereochemostry. oechem.OEPerceiveChiral(molecule) # Set title. molecule.SetTitle(options.ligand) # Write out PDB form of this molecule. # TODO: Fix atom numbering. #if verbose: print "Writing input molecule as PDB..." #outmol = oechem.OEMol(molecule) #ofs = oechem.oemolostream() #flavor = oechem.OEOFlavor_Generic_Default | oechem.OEOFlavor_PDB_Default #ofs.SetFlavor(oechem.OEFormat_PDB, flavor) #ofs.open(options.ligand + '.pdb') #oechem.OEWriteMolecule(ofs, outmol) #ofs.close() # Write mol2 file for this molecule. if verbose: print "Writing input molecule as mol2..." outmol = oechem.OEMol(molecule) ofs = oechem.oemolostream() filename = options.ligand + '.mol2' ofs.open(filename) oechem.OEWriteMolecule(ofs, outmol) ofs.close() # Use low level writer to get atom names correct. ofs = oechem.oemolostream() ofs.open(filename) for (dest_atom, src_atom) in zip(outmol.GetAtoms(), molecule.GetAtoms()): dest_atom.SetName(src_atom.GetName()) oechem.OEWriteMol2File(ofs, outmol, True) ofs.close() # Read and write in PDB format. if verbose: print "Converting mol2 to PDB..." ifs = oechem.oemolistream() ofs = oechem.oemolostream() if ifs.open(options.ligand + '.mol2'): if ofs.open(options.ligand + '.pdb'): for mol in ifs.GetOEGraphMols(): oechem.OEWriteMolecule(ofs, mol) return molecule
def infer_mol_from_coordinates( coordinates, species, smiles_ref=None, coordinates_unit="angstrom", ): # local import from openeye import oechem from simtk import unit from simtk.unit import Quantity if isinstance(coordinates_unit, str): coordinates_unit = getattr(unit, coordinates_unit) # make sure we have the coordinates # in the unit system coordinates = Quantity(coordinates, coordinates_unit).value_in_unit( unit.angstrom # to make openeye happy ) # initialize molecule mol = oechem.OEGraphMol() if all(isinstance(symbol, str) for symbol in species): [ mol.NewAtom(getattr(oechem, "OEElemNo_" + symbol)) for symbol in species ] elif all(isinstance(symbol, int) for symbol in species): [ mol.NewAtom( getattr(oechem, "OEElemNo_" + oechem.OEGetAtomicSymbol(symbol))) for symbol in species ] else: raise RuntimeError( "The species can only be all strings or all integers.") mol.SetCoords(coordinates.reshape([-1])) mol.SetDimension(3) oechem.OEDetermineConnectivity(mol) oechem.OEFindRingAtomsAndBonds(mol) oechem.OEPerceiveBondOrders(mol) if smiles_ref is not None: smiles_can = oechem.OECreateCanSmiString(mol) ims = oechem.oemolistream() ims.SetFormat(oechem.OEFormat_SMI) ims.openstring(smiles_ref) mol_ref = next(ims.GetOEMols()) smiles_ref = oechem.OECreateCanSmiString(mol_ref) assert (smiles_ref == smiles_can ), "SMILES different. Input is %s, ref is %s" % ( smiles_can, smiles_ref, ) from openff.toolkit.topology import Molecule _mol = Molecule.from_openeye(mol, allow_undefined_stereo=True) g = esp.Graph(_mol) return g
# modifications. Modification of Sample Code is at Customer's sole and # exclusive risk. Sample Code may require Customer to have a then # current license or subscription to the applicable OpenEye offering. # THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED. OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT # NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall OpenEye be # liable for any damages or liability in connection with the Sample Code # or its use. # @ <SNIPPET> from openeye import oechem # @ <SNIPPET-CORRECT-COMPLEX> mol = oechem.OEGraphMol() oechem.OESmilesToMol(mol, "c1ccccc1") # Code that modifies the molecule # and creates new atoms and bonds oechem.OEFindRingAtomsAndBonds(mol) oechem.OEAssignAromaticFlags(mol) for bond in mol.GetBonds(): if bond.IsAromatic(): bond.SetIntType(5) elif bond.GetOrder() != 0: bond.SetIntType(bond.GetOrder()) else: bond.SetIntType(1) oechem.OEKekulize(mol) # @ </SNIPPET-CORRECT-COMPLEX> # @ </SNIPPET>
def main(): opt = parser.parse_args() global which_gaff if opt.gaff: which_gaff = opt.gaff else: which_gaff = 1 # 2--> gaff2 global lig_input_mol2 if opt.lig: lig_input_mol2 = opt.lig else: lig_input_mol2 = 'lig_in.mol2' # ============== # # new protocol: # (1) amber bond typer --> ac file (2) openeye convert aromatic mol2 --> kekule form assignment with oechem.OEKekulize(mol) # ============== # command = '$AMBERHOME/bin/antechamber -i %s -fi mol2 -o lig.ac -fo ac -j 2 -dr n -pf y' % lig_input_mol2 print(command) subprocess.call(command, shell=True) molecule = oechem.OEGraphMol() ifs = oechem.oemolistream(lig_input_mol2) flavor = oechem.OEIFlavor_Generic_Default | oechem.OEIFlavor_MOL2_Default | oechem.OEIFlavor_MOL2_Forcefield ifs.SetFlavor(oechem.OEFormat_MOL2, flavor) oechem.OEReadMol2File(ifs, molecule) print(molecule.GetTitle()) # Define mapping from GAFF bond orders to OpenEye bond orders. order_map = {1: 1, 2: 2, 3: 3, 7: 1, 8: 2, 9: 5, 10: 5} # Read bonds. infile = open('lig.ac') lines = infile.readlines() infile.close() antechamber_bond_types = list() for line in lines: elements = line.split() if elements[0] == 'BOND': antechamber_bond_types.append(int(elements[4])) oechem.OEClearAromaticFlags(molecule) for (bond, antechamber_bond_type) in zip(molecule.GetBonds(), antechamber_bond_types): #bond.SetOrder(order_map[antechamber_bond_type]) bond.SetIntType(order_map[antechamber_bond_type]) oechem.OEFindRingAtomsAndBonds(molecule) oechem.OEKekulize(molecule) oechem.OEAssignFormalCharges(molecule) oechem.OEAssignAromaticFlags(molecule, oechem.OEAroModelOpenEye) """ charges = [ atom.GetFormalCharge() for atom in molecule.GetAtoms() ] net_charge = np.array(charges).sum() print("Net Charge:", net_charge) """ subprocess.call('grep CHARGE lig.ac', shell=True) with open('lig.ac', "r") as f: line = f.readline() # if line.find("CHARGE") > -1 and len(line.split())> 2: # net_charge = int( float( line.split()[1] ) ) if line.find("CHARGE") > -1: net_charge = int(line[line.find("(") + 1:line.find(")")]) print("Net Charge:", net_charge) # Write mol2 file for this molecule. outmol = oechem.OEMol(molecule) ofs = oechem.oemolostream() filename = 'lig_oe.mol2' ofs.open(filename) oechem.OEWriteMolecule(ofs, outmol) ofs.close() if which_gaff == 1: gaff = "gaff" elif which_gaff == 2: gaff = "gaff2" command = "$AMBERHOME/bin/antechamber -i lig_oe.mol2 -fi mol2 -o lig_bcc.mol2 -fo mol2 -c bcc -at %s -nc %i -j 5 -pf y -dr n" \ % (gaff, net_charge) print(command) subprocess.call(command, shell=True) command = "$AMBERHOME/bin/parmchk2 -i lig_bcc.mol2 -f mol2 -s %d -o lig.frcmod" % ( which_gaff) print(command) subprocess.call(command, shell=True)
def generateOEMolFromTopologyResidue(residue, geometry=False, tripos_atom_names=False): """ Generate an OpenEye OEMol molecule from an OpenMM Topology Residue. Parameters ---------- residue : simtk.openmm.app.topology.Residue The topology Residue from which an OEMol is to be created. An Exception will be thrown if this residue has external bonds. geometry : bool, optional, default=False If True, will generate a single configuration with OEOmega. Note that stereochemistry will be *random*. tripos_atom_names : bool, optional, default=False If True, will generate and assign Tripos atom names. Returns ------- molecule : openeye.oechem.OEMol The OEMol molecule corresponding to the topology. Atom order will be preserved and bond orders assigned. The Antechamber `bondtype` program will be used to assign bond orders, and these will be converted back into OEMol bond type assignments. Note that there is no way to preserve stereochemistry since `Residue` does not note stereochemistry in any way. """ # Raise an Exception if this residue has external bonds. if len(list(residue.external_bonds())) > 0: raise Exception( "Cannot generate an OEMol from residue '%s' because it has external bonds." % residue.name) from openeye import oechem # Create OEMol where all atoms have bond order 1. molecule = oechem.OEMol() molecule.SetTitle(residue.name) # name molecule after first residue for atom in residue.atoms(): oeatom = molecule.NewAtom(atom.element.atomic_number) oeatom.SetName(atom.name) oeatom.AddData("topology_index", atom.index) oeatoms = {oeatom.GetName(): oeatom for oeatom in molecule.GetAtoms()} for (atom1, atom2) in residue.bonds(): order = 1 molecule.NewBond(oeatoms[atom1.name], oeatoms[atom2.name], order) # Write out a mol2 file without altering molecule. import tempfile tmpdir = tempfile.mkdtemp() mol2_input_filename = os.path.join(tmpdir, 'molecule-before-bond-perception.mol2') ac_output_filename = os.path.join(tmpdir, 'molecule-after-bond-perception.ac') ofs = oechem.oemolostream(mol2_input_filename) m2h = True substruct = False oechem.OEWriteMol2File(ofs, molecule, m2h, substruct) ofs.close() # Run Antechamber bondtype import subprocess #command = 'bondtype -i %s -o %s -f mol2 -j full' % (mol2_input_filename, ac_output_filename) command = 'antechamber -i %s -fi mol2 -o %s -fo ac -j 2' % ( mol2_input_filename, ac_output_filename) [status, output] = getstatusoutput(command) # Define mapping from GAFF bond orders to OpenEye bond orders. order_map = {1: 1, 2: 2, 3: 3, 7: 1, 8: 2, 9: 5, 10: 5} # Read bonds. infile = open(ac_output_filename) lines = infile.readlines() infile.close() antechamber_bond_types = list() for line in lines: elements = line.split() if elements[0] == 'BOND': antechamber_bond_types.append(int(elements[4])) oechem.OEClearAromaticFlags(molecule) for (bond, antechamber_bond_type) in zip(molecule.GetBonds(), antechamber_bond_types): #bond.SetOrder(order_map[antechamber_bond_type]) bond.SetIntType(order_map[antechamber_bond_type]) oechem.OEFindRingAtomsAndBonds(molecule) oechem.OEKekulize(molecule) oechem.OEAssignFormalCharges(molecule) oechem.OEAssignAromaticFlags(molecule, oechem.OEAroModelOpenEye) # Clean up. os.unlink(mol2_input_filename) os.unlink(ac_output_filename) os.rmdir(tmpdir) # Generate Tripos atom names if requested. if tripos_atom_names: oechem.OETriposAtomNames(molecule) # Assign geometry if geometry: from openeye import oeomega omega = oeomega.OEOmega() omega.SetMaxConfs(1) omega.SetIncludeInput(False) omega.SetStrictStereo(False) omega(molecule) return molecule
def oemol_from_rdmol(rdmol): """ Creates an openeye molecule object that is identical to the input rdkit molecule """ # RDK automatically includes explicit hydrogens in its SMILES patterns print("Starting molecule: ", Chem.MolToSmiles(Chem.RemoveHs(rdmol))) # openeye stores bond orders as integers regardless of aromaticity # in order to properly extract these, we need to have the "Kekulized" version of the rdkit mol kekul_mol = Chem.Mol(rdmol) Chem.Kekulize(kekul_mol, True) oemol = oechem.OEMol() map_atoms = dict() # {rd_idx: oe_atom} # setting chirality in openey requires using neighbor atoms # therefore we can't do it until after the atoms and bonds are all added chiral_atoms = dict() # {rd_idx: openeye chirality} for rda in rdmol.GetAtoms(): rd_idx = rda.GetIdx() # create a new atom oe_a = oemol.NewAtom(rda.GetAtomicNum()) map_atoms[rd_idx] = oe_a oe_a.SetFormalCharge(rda.GetFormalCharge()) oe_a.SetAromatic(rda.GetIsAromatic()) # If chiral, store the chirality to be set later tag = rda.GetChiralTag() if tag == Chem.CHI_TETRAHEDRAL_CCW: chiral_atoms[rd_idx] = oechem.OECIPAtomStereo_R if tag == Chem.CHI_TETRAHEDRAL_CW: chiral_atoms[rd_idx] = oechem.OECIPAtomStereo_S # Similar to chirality, stereochemistry of bonds in OE is set relative to their neighbors stereo_bonds = list() # stereo_bonds stores tuples in the form (oe_bond, rd_idx1, rd_idx2, OE stereo specification) # where rd_idx1 and 2 are the atoms on the outside of the bond # i.e. Cl and F in the example above aro_bond = 0 for rdb in rdmol.GetBonds(): a1 = rdb.GetBeginAtomIdx() a2 = rdb.GetEndAtomIdx() # create a new bond newbond = oemol.NewBond(map_atoms[a1], map_atoms[a2]) order = rdb.GetBondTypeAsDouble() if order == 1.5: # get the bond order for this bond in the kekulized molecule order = kekul_mol.GetBondWithIdx( rdb.GetIdx()).GetBondTypeAsDouble() newbond.SetAromatic(True) else: newbond.SetAromatic(False) newbond.SetOrder(int(order)) # determine if stereochemistry is needed tag = rdb.GetStereo() if tag == Chem.BondStereo.STEREOCIS or tag == Chem.BondStereo.STEREOZ: stereo_atoms = rdb.GetStereoAtoms() stereo_bonds.append((newbond, stereo_atoms[0], stereo_atoms[1], oechem.OEBondStereo_Cis)) bond2 = rdmol.GetBondBetweenAtoms(stereo_atoms[0], a1) bond4 = rdmol.GetBondBetweenAtoms(stereo_atoms[1], a2) print(tag, bond2.GetBondDir(), bond4.GetBondDir()) if tag == Chem.BondStereo.STEREOTRANS or tag == Chem.BondStereo.STEREOE: stereo_atoms = rdb.GetStereoAtoms() stereo_bonds.append((newbond, stereo_atoms[0], stereo_atoms[1], oechem.OEBondStereo_Trans)) bond2 = rdmol.GetBondBetweenAtoms(stereo_atoms[0], a1) bond4 = rdmol.GetBondBetweenAtoms(stereo_atoms[1], a2) print(tag, bond2.GetBondDir(), bond4.GetBondDir()) # Now that all of the atoms are connected we can set stereochemistry # starting with atom chirality for rd_idx, chirality in chiral_atoms.items(): # chirality is set relative to neighbors, so we will get neighboring atoms # assign Right handed direction, check the cip stereochemistry # if the cip stereochemistry isn't correct then we'll set left and double check oea = map_atoms[rd_idx] neighs = [n for n in oea.GetAtoms()] # incase you look at the documentation oe has two options for handedness for example: # oechem.OEAtomStereo_Left == oechem.OEAtomStereo_LeftHanded oea.SetStereo(neighs, oechem.OEAtomStereo_Tetra, oechem.OEAtomStereo_Right) cip = oechem.OEPerceiveCIPStereo(oemol, oea) if cip != chirality: oea.SetStereo(neighs, oechem.OEAtomStereo_Tetra, oechem.OEAtomStereo_Left) new_cip = oechem.OEPerceiveCIPStereo(oemol, oea) if new_cip != chirality: # Note, I haven't seen this happen yet, but it shouldn't be a problem since there # is only 2 directions for handedness and we're only running this for chiral atoms print("PANIC!") # Set stereochemistry using the reference atoms extracted above for oeb, idx1, idx2, oestereo in stereo_bonds: oeb.SetStereo([map_atoms[idx1], map_atoms[idx2]], oechem.OEBondStereo_CisTrans, oestereo) # If the rdmol has a conformer, add its coordinates to the oemol # Note, this currently only adds the first conformer, it will need to be adjusted if the # you wanted to convert multiple sets of coordinates if rdmol.GetConformers(): conf = rdmol.GetConformer() for rd_idx, oeatom in map_atoms.items(): coords = conf.GetAtomPosition(rd_idx) oemol.SetCoords(oeatom, oechem.OEFloatArray(coords)) # If RDMol has a title save it if rdmol.HasProp("_Name"): oemol.SetTitle(rdmol.GetProp("_Name")) # Clean Up phase # The only feature of a molecule that wasn't perceived above seemed to be ring connectivity, better to run it # here then for someone to inquire about ring sizes and get 0 when it shouldn't be oechem.OEFindRingAtomsAndBonds(oemol) print('Final Molecule: ', oechem.OEMolToSmiles(oemol)) return oemol