def __makeChemCompBondCategory(self, ccId, oeMol): idCode = ccId rowL = [] iBondD = {1: "SING", 2: "DOUB", 3: "TRIP", 4: "QUAD", 5: "AROM", 0: "DELO"} for ii, bond in enumerate(oeMol.GetBonds()): oeOrder = str(bond.GetOrder()).upper() b1 = bond.GetBgn().GetName().strip() b2 = bond.GetEnd().GetName().strip() if (b1 is None) or (len(b1) < 1): continue if (b2 is None) or (len(b1) < 1): continue if int(oeOrder) in iBondD: bndRow = {} bndRow["comp_id"] = idCode bndRow["atom_id_1"] = b1 bndRow["atom_id_2"] = b2 bndRow["value_order"] = iBondD[int(oeOrder)] if bond.IsAromatic(): bndRow["pdbx_aromatic_flag"] = "Y" else: bndRow["pdbx_aromatic_flag"] = "N" oeSt = None if bond.GetOrder() == 2: cip = oechem.OEPerceiveCIPStereo(oeMol, bond) if bond.HasStereoSpecified(): if cip == oechem.OECIPBondStereo_E: oeSt = "E" if cip == oechem.OECIPBondStereo_Z: oeSt = "Z" if cip == oechem.OECIPBondStereo_NotStereo: oeSt = None if cip == oechem.OECIPBondStereo_UnspecStereo: oeSt = None if oeSt is not None and (len(oeSt) > 0) and (oeSt == "E" or oeSt == "Z"): bndRow["pdbx_stereo_config"] = oeSt else: bndRow["pdbx_stereo_config"] = "N" bndRow["pdbx_ordinal"] = str(ii + 1) rowL.append(bndRow) return rowL
def rdmol_from_oemol(oemol): """ Create an RDKit molecule identical to the input OpenEye molecule. Reference --------- Written by Caitlin Bannan: https://gist.github.com/bannanc/810ccc4636b930a4522636baab1965a6 May not be needed in newer openforcefield versions. See: https://github.com/openforcefield/openforcefield/issues/135 Parameters ---------- oemol : OEMol """ #print("Starting OpenEye molecule: ", oechem.OEMolToSmiles(oemol)) # start function rdmol = Chem.RWMol() # RDKit keeps bond order as a type instead using these values, I don't really understand 7, # I took them from Shuzhe's example linked above _bondtypes = { 1: Chem.BondType.SINGLE, 1.5: Chem.BondType.AROMATIC, 2: Chem.BondType.DOUBLE, 3: Chem.BondType.TRIPLE, 4: Chem.BondType.QUADRUPLE, 5: Chem.BondType.QUINTUPLE, 6: Chem.BondType.HEXTUPLE, 7: Chem.BondType.ONEANDAHALF, } # atom map lets you find atoms again map_atoms = dict() # {oe_idx: rd_idx} for oea in oemol.GetAtoms(): oe_idx = oea.GetIdx() rda = Chem.Atom(oea.GetAtomicNum()) rda.SetFormalCharge(oea.GetFormalCharge()) rda.SetIsAromatic(oea.IsAromatic()) # unlike OE, RDK lets you set chirality directly cip = oechem.OEPerceiveCIPStereo(oemol, oea) if cip == oechem.OECIPAtomStereo_S: rda.SetChiralTag(Chem.CHI_TETRAHEDRAL_CW) if cip == oechem.OECIPAtomStereo_R: rda.SetChiralTag(Chem.CHI_TETRAHEDRAL_CCW) map_atoms[oe_idx] = rdmol.AddAtom(rda) # As discussed above, setting bond stereochemistry requires neighboring bonds # so we will store that information by atom index in this list stereo_bonds = list() # stereo_bonds will have tuples with the form (rda1, rda2, rda3, rda4, is_cis) # where rda[n] is an atom index for a double bond of form 1-2=3-4 # and is_cis is a Boolean is True then onds 1-2 and 3-4 are cis to each other for oeb in oemol.GetBonds(): # get neighboring rd atoms rd_a1 = map_atoms[oeb.GetBgnIdx()] rd_a2 = map_atoms[oeb.GetEndIdx()] # AddBond returns the total number of bonds, so addbond and then get it rdmol.AddBond(rd_a1, rd_a2) rdbond = rdmol.GetBondBetweenAtoms(rd_a1, rd_a2) # Assign bond type, which is based on order unless it is aromatic order = oeb.GetOrder() if oeb.IsAromatic(): rdbond.SetBondType(_bondtypes[1.5]) rdbond.SetIsAromatic(True) else: rdbond.SetBondType(_bondtypes[order]) rdbond.SetIsAromatic(False) # If the bond has specified stereo add the required information to stereo_bonds if oeb.HasStereoSpecified(oechem.OEBondStereo_CisTrans): # OpenEye determined stereo based on neighboring atoms so get two outside atoms n1 = [n for n in oeb.GetBgn().GetAtoms() if n != oeb.GetEnd()][0] n2 = [n for n in oeb.GetEnd().GetAtoms() if n != oeb.GetBgn()][0] rd_n1 = map_atoms[n1.GetIdx()] rd_n2 = map_atoms[n2.GetIdx()] stereo = oeb.GetStereo([n1, n2], oechem.OEBondStereo_CisTrans) if stereo == oechem.OEBondStereo_Cis: print('cis') stereo_bonds.append((rd_n1, rd_a1, rd_a2, rd_n2, True)) elif stereo == oechem.OEBondStereo_Trans: print('trans') stereo_bonds.append((rd_n1, rd_a1, rd_a2, rd_n2, False)) # add bond stereochemistry: for (rda1, rda2, rda3, rda4, is_cis) in stereo_bonds: # get neighbor bonds bond1 = rdmol.GetBondBetweenAtoms(rda1, rda2) bond2 = rdmol.GetBondBetweenAtoms(rda3, rda4) # Since this is relative, the first bond always goes up # as explained above these names come from SMILES slashes so UP/UP is Trans and Up/Down is cis bond1.SetBondDir(Chem.BondDir.ENDUPRIGHT) if is_cis: bond2.SetBondDir(Chem.BondDir.ENDDOWNRIGHT) else: bond2.SetBondDir(Chem.BondDir.ENDUPRIGHT) # if oemol has coordinates (The dimension is non-zero) # add those coordinates to the rdmol if oechem.OEGetDimensionFromCoords(oemol) > 0: conformer = Chem.Conformer() oecoords = oemol.GetCoords() for oe_idx, rd_idx in map_atoms.items(): (x, y, z) = oecoords[oe_idx] conformer.SetAtomPosition(rd_idx, Geometry.Point3D(x, y, z)) rdmol.AddConformer(conformer) # Save the molecule title rdmol.SetProp("_Name", oemol.GetTitle()) # Cleanup the rdmol # Note I copied UpdatePropertyCache and GetSSSR from Shuzhe's code to convert oemol to rdmol here: rdmol.UpdatePropertyCache(strict=False) Chem.GetSSSR(rdmol) # I added AssignStereochemistry which takes the directions of the bond set # and assigns the stereochemistry tags on the double bonds Chem.AssignStereochemistry(rdmol, force=False) #print("Final RDKit molecule: ", Chem.MolToSmiles(Chem.RemoveHs(rdmol), isomericSmiles=True)) return rdmol.GetMol()
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
# 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 __future__ import print_function from openeye import oechem mol = oechem.OEGraphMol() oechem.OESmilesToMol(mol, "N[C@@H]1N[C@@H](O)CC(O)C1.C2[C@@H](O)CC[C@@H](O)C2") for atom in mol.GetAtoms(): cip = oechem.OEPerceiveCIPStereo(mol, atom) if atom.HasStereoSpecified(): print("atom %d is" % atom.GetIdx(), end=" ") if cip == oechem.OECIPAtomStereo_S: print('S') if cip == oechem.OECIPAtomStereo_R: print('R') if cip == oechem.OECIPAtomStereo_NotStereo: print('not a CIP stereo center') if cip == oechem.OECIPAtomStereo_UnspecStereo: print("atom %d is" % atom.GetIdx(), end=" ") print('a CIP stereo center without specified stereochemistry') # @ </SNIPPET>
def __makeChemCompAtomCategory(self, ccId, oeMol, writeIdealXyz=True): """Populate elements of chemical component definition for atoms and bonds.""" # idCode = ccId # rowL = [] for ii, atom in enumerate(oeMol.GetAtoms()): # atRow = {} atRow["comp_id"] = idCode atRow["atom_id"] = atom.GetName().strip() atRow["alt_atom_id"] = atom.GetName().strip() atRow["type_symbol"] = oechem.OEGetAtomicSymbol(atom.GetAtomicNum()) atRow["charge"] = atom.GetFormalCharge() (xC, yC, zC) = oeMol.GetCoords(atom) if writeIdealXyz: atRow["pdbx_model_Cartn_x_ideal"] = "%0.3f" % xC atRow["pdbx_model_Cartn_y_ideal"] = "%0.3f" % yC atRow["pdbx_model_Cartn_z_ideal"] = "%0.3f" % zC else: atRow["model_Cartn_x"] = "%0.3f" % xC atRow["model_Cartn_y"] = "%0.3f" % yC atRow["model_Cartn_z"] = "%0.3f" % zC if atom.GetStringData("pdbx_leaving_atom_flag") in ["Y", "N"]: atRow["pdbx_leaving_atom_flag"] = atom.GetStringData("pdbx_leaving_atom_flag") else: atRow["pdbx_leaving_atom_flag"] = "N" if len(atRow["atom_id"]) > 3 or len(atRow["type_symbol"]) == 2: atRow["pdbx_align"] = 0 else: atRow["pdbx_align"] = 1 if atom.IsAromatic(): atRow["pdbx_aromatic_flag"] = "Y" else: atRow["pdbx_aromatic_flag"] = "N" # oeSt = oechem.OEGetCIPStereo(mol, atom) oeSt = None # if atom.IsChiral(): cip = oechem.OEPerceiveCIPStereo(oeMol, atom) if atom.HasStereoSpecified(): if cip == oechem.OECIPAtomStereo_S: oeSt = "S" if cip == oechem.OECIPAtomStereo_R: oeSt = "R" if cip == oechem.OECIPAtomStereo_NotStereo: oeSt = None if cip == oechem.OECIPAtomStereo_UnspecStereo: oeSt = "U" # oeSt = oechem.OEGetCIPStereo(oeMol, atom) # oeSt = atom.GetCIPStereo() if oeSt is not None and (len(oeSt) > 0) and (oeSt == "R" or oeSt == "S"): atRow["pdbx_stereo_config"] = oeSt else: atRow["pdbx_stereo_config"] = "N" atRow["pdbx_component_atom_id"] = atom.GetName().strip() atRow["pdbx_component_comp_id"] = idCode atRow["pdbx_ordinal"] = str(ii + 1) rowL.append(atRow) # return rowL
# 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 __future__ import print_function from openeye import oechem mol = oechem.OEGraphMol() oechem.OESmilesToMol(mol, "C\\C(C)=C(\\C)C.O\\C(C)=C(\\C)O.O\\C(C)=C(\\O)C.CC(O)=C(O)C") for bond in mol.GetBonds(): if bond.GetOrder() == 2: cip = oechem.OEPerceiveCIPStereo(mol, bond) print("bond %d is" % bond.GetIdx(), end=" ") if bond.HasStereoSpecified(): if cip == oechem.OECIPBondStereo_E: print('E') if cip == oechem.OECIPBondStereo_Z: print('Z') if cip == oechem.OECIPBondStereo_NotStereo: print('not a CIP stereo center') if cip == oechem.OECIPBondStereo_UnspecStereo: print('a CIP stereo center without specified stereochemistry') # @ </SNIPPET>