def main(args): if len(args) != 3: oechem.OEThrow.Usage("%s <input> <output>" % args[0]) ifs = oechem.oemolistream() if not ifs.open(args[1]): oechem.OEThrow.Fatal("Unable to open %s for reading" % args[1]) ofs = oechem.oemolostream() if not ofs.open(args[2]): oechem.OEThrow.Fatal("Unable to open %s for writing" % args[2]) mmff = oeff.OEMMFF() # Setup adaptor. The first (false) means not to pass ownership of mmff, # and the second (false) means not to exclude interactions related # to the subset which would be fixed for calculations. adaptor = oeff.OETorAdaptor(mmff, False, False) # Use a simple predicate for the subset of torsions to optimize adaptor.Set(oechem.OEIsRotor()) mol = oechem.OEMol() while oechem.OEReadMolecule(ifs, mol): oechem.OEAddExplicitHydrogens(mol) # Use a simple atoms predicate for the subset, followed by setup if (not mmff.PrepMol(mol)) or (not adaptor.Setup(mol)): oechem.OEThrow.Warning("Unable to process molecule: title = '%s'" % mol.GetTitle()) oechem.OEWriteMolecule(ofs, mol) continue vecCoords = oechem.OEDoubleArray(3*mol.GetMaxAtomIdx()) for conf in mol.GetConfs(): oechem.OEThrow.Info("Molecule: %s Conformer: %d" % (mol.GetTitle(), conf.GetIdx()+1)) conf.GetCoords(vecCoords) # Get adaptor variables set corresponding to the coordinates vecX = oechem.OEDoubleArray(adaptor.NumVar()) adaptor.GetVar(vecX, vecCoords) # Calculate energy using adaptor energy = adaptor(vecX) oechem.OEThrow.Info("Initial energy: %d kcal/mol" % energy) # Optimize the adaptor optimizer = oeff.OEBFGSOpt() energy = optimizer(adaptor, vecX, vecX) oechem.OEThrow.Info("Optimized energy: %d kcal/mol" % energy) # Get optimized coordinates corresponding to the adaptor optimized variables adaptor.AdaptVar(vecCoords, vecX) conf.SetCoords(vecCoords) oechem.OEWriteMolecule(ofs, mol) return 0
def gen_starting_confs(mol, torsion_library, max_one_bond_away=True, num_conformers=MAX_CONFS, rms_cutoff=0.0, energy_window=25): # Identify the atoms in the dihedral TAGNAME = 'TORSION_ATOMS_FRAGMENT' if not has_sd_data(mol, TAGNAME): raise ValueError("Molecule does not have the SD Data Tag '{}'.".format(TAGNAME)) dihedralAtomIndices = [int(x)-1 for x in get_sd_data(mol, TAGNAME).split()] inDih = \ oechem.OEOrAtom(oechem.OEOrAtom(oechem.OEHasAtomIdx(dihedralAtomIndices[0]), oechem.OEHasAtomIdx(dihedralAtomIndices[1])), oechem.OEOrAtom(oechem.OEHasAtomIdx(dihedralAtomIndices[2]), oechem.OEHasAtomIdx(dihedralAtomIndices[3])) ) mol1 = mol.CreateCopy() mc_mol = oechem.OEMol(mol1) if num_conformers > 1: # Set criterion for rotatable bond if False and max_one_bond_away: # this max function makes this seem potentially broken only_one_bond_away = distance_predicate(dihedralAtomIndices[1], dihedralAtomIndices[2]) rotor_predicate = oechem.OEAndBond(only_one_bond_away, oechem.PyBondPredicate(isRotatableBond)) elif False: # this ONLY samples special bonds & neglects "regualr" torsions rotor_predicate = oechem.PyBondPredicate(isRotatableBond) else: # try this more general sampling, but leave prior versions untouched rotor_predicate = oechem.OEOrBond(oechem.OEIsRotor(), oechem.PyBondPredicate(isRotatableBond)) #Initialize conformer generator and multi-conformer library conf_generator = configure_omega(torsion_library, rotor_predicate, rms_cutoff, energy_window, num_conformers) # Generator conformers if not conf_generator(mc_mol, inDih): raise ValueError("Conformers cannot be generated.") logging.debug("Generated a total of %d conformers for %s.", mc_mol.NumConfs(), mol.GetTitle()) for conf_no, conf in enumerate(mc_mol.GetConfs()): conformer_label = mol.GetTitle()+'_' +\ '_'.join(get_sd_data(mol, 'TORSION_ATOMS_ParentMol').split()) +\ '_{:02d}'.format(conf_no) oechem.OESetSDData(conf, "CONFORMER_LABEL", conformer_label) conf.SetTitle(conformer_label) return mc_mol
def CountRotors(ifs): rotcounts = [] for mol in ifs.GetOEMols(): nrots = oechem.OECount(mol, oechem.OEIsRotor()) while nrots >= len(rotcounts): rotcounts.append(0) rotcounts[nrots] += 1 print("Max rotors:", len(rotcounts) - 1) print("Rotorcount distribution:") for rots, numrot in enumerate(rotcounts): print("\t%d:\t%d" % (rots, numrot))
def gen_starting_confs(mol, torsion_library, num_conformers=MAX_CONFS, rms_cutoff=0.0, energy_window=25): # Identify the atoms in the dihedral TAGNAME = 'TORSION_ATOMS_FRAGMENT' if not has_sd_data(mol, TAGNAME): raise ValueError( "Molecule does not have the SD Data Tag '{}'.".format(TAGNAME)) dihedralAtomIndices = [ int(x) - 1 for x in get_sd_data(mol, TAGNAME).split() ] inDih = \ oechem.OEOrAtom(oechem.OEOrAtom(oechem.OEHasAtomIdx(dihedralAtomIndices[0]), oechem.OEHasAtomIdx(dihedralAtomIndices[1])), oechem.OEOrAtom(oechem.OEHasAtomIdx(dihedralAtomIndices[2]), oechem.OEHasAtomIdx(dihedralAtomIndices[3])) ) mol1 = mol.CreateCopy() mc_mol = oechem.OEMol(mol1) if num_conformers > 1: rotor_predicate = oechem.OEOrBond( oechem.OEIsRotor(), oechem.PyBondPredicate(isRotatableBond)) #Initialize conformer generator and multi-conformer library conf_generator = configure_omega(torsion_library, rotor_predicate, rms_cutoff, energy_window, num_conformers) # Generator conformers if not conf_generator(mc_mol, inDih): raise ValueError("Conformers cannot be generated.") logging.debug("Generated a total of %d conformers for %s.", mc_mol.NumConfs(), mol.GetTitle()) for conf_no, conf in enumerate(mc_mol.GetConfs()): conformer_label = mol.GetTitle()+'_' +\ '_'.join(get_sd_data(mol, 'TORSION_ATOMS_ParentMol').split()) +\ '_{:02d}'.format(conf_no) oechem.OESetSDData(conf, "CONFORMER_LABEL", conformer_label) conf.SetTitle(conformer_label) return mc_mol
def main(argv=[__name__]): if len(argv) != 2: oechem.OEThrow.Usage("%s <infile>" % argv[0]) ifs = oechem.oemolistream(sys.argv[1]) am1 = oequacpac.OEAM1() results = oequacpac.OEAM1Results() for mol in ifs.GetOEMols(): for conf in mol.GetConfs(): print("molecule: ", mol.GetTitle(), "conformer:", conf.GetIdx()) if am1.CalcAM1(results, mol): nbonds = 0 for bond in mol.GetBonds(oechem.OEIsRotor()): nbonds += 1 print( results.GetBondOrder(bond.GetBgnIdx(), bond.GetEndIdx())) print("Rotatable bonds: ", nbonds)
def __call__(self, bond): """ :type mol: oechem.OEBondBase :rtype: boolean """ if bond.GetOrder() != 1: return False if bond.IsAromatic(): return False isrotor = oechem.OEIsRotor() if isrotor(bond): return True if oechem.OEBondGetSmallestRingSize(bond) >= 10: return True return False
def main(argv=[__name__]): if len(argv) != 2: oechem.OEThrow.Usage("%s <infile>" % argv[0]) ifs = oechem.oemolistream() if not ifs.open(argv[1]): oechem.OEThrow.Fatal("Unable to open %s for reading" % argv[1]) print("Title MolWt NumAtoms NumHeavyAtoms NumRingAtoms NumRotors NumConfs") for mol in ifs.GetOEMols(): title = mol.GetTitle() if not title: title = "Untitled" print("%s %.3f %d %d %d %d %d" % (title, oechem.OECalculateMolecularWeight(mol), mol.NumAtoms(), oechem.OECount(mol, oechem.OEIsHeavy()), oechem.OECount(mol, oechem.OEAtomIsInRing()), oechem.OECount(mol, oechem.OEIsRotor()), mol.NumConfs()))
def calculate_ml_profiles(mols, sf_map, model, scaler): num_mols = len(mols) for count, mol in enumerate(mols): logging.info("Generating molecule profile %d/%d", (count + 1), num_mols) if mol.HasData(HAS_PROFILES_TAG) and mol.GetData( HAS_PROFILES_TAG) == True: for bond in mol.GetBonds(oechem.OEIsRotor()): if bond.HasData(ENERGY_PROFILE_TAG) and bond.HasData( PROFILE_OFFSET_TAG): continue if bond.HasData(SPECIFIC_INCHI_TAG): specific_inchi = bond.GetData(SPECIFIC_INCHI_TAG) profile, offset = calculate_sf_ML_profile( model, scaler, sf_map[specific_inchi]) bond.SetData(ENERGY_PROFILE_TAG, profile) bond.SetData(PROFILE_OFFSET_TAG, float(offset))
def generate_torsional_strain(mol): ''' Calculate strain energy of each rotatable bond using attached ML profiles @param mol: OEGraphMol @param gen_confs: bool @param num_confs: int @return: None ''' for bond in mol.GetBonds(oechem.OEIsRotor()): if bond.HasData(ENERGY_PROFILE_TAG): energy_profile = bond.GetData(ENERGY_PROFILE_TAG) x, y = extract_numeric_data_from_profile_str(energy_profile) _, f = get_global_min_interp1d(x, y) bond_strain = 1e10 torsion_atoms_list = extract_torsion_atoms(mol, bond) for torsion_atoms in torsion_atoms_list: a, b, c, d = torsion_atoms angle = oechem.OEGetTorsion(mol, a, b, c, d) * oechem.Rad2Deg strainE = float(f(angle)) if strainE < 0.0: strainE = 0.0 if bond_strain > strainE: bond_strain = strainE tor_atoms_str = ' '.join( list( map(str, [a.GetIdx(), b.GetIdx(), c.GetIdx(), d.GetIdx()]))) bond.SetData(TORSION_ATOMS_FRAGMENT_TAG, tor_atoms_str) bond.SetData(STRAIN_TAG, bond_strain) if mol.HasData(HAS_PROFILES_TAG) and mol.GetData(HAS_PROFILES_TAG): save_profile_as_sd(mol) return mol
def check_frag_complexity(frag_smi, filter_type=2, check_n_rings=True, filter_ortho=True): from openeye import oechem oemol = oechem.OEGraphMol() oechem.OESmilesToMol(oemol, frag_smi) nrots = oechem.OECount(oemol, oechem.OEIsRotor()) # print(f'{frag_smi}: {nrots}') num_components, component_membership = oechem.OEDetermineComponents(oemol) num_rings = oemol.NumBonds() - oemol.NumAtoms() + num_components if filter_type == 1: if nrots > 0: return True, f' nrot: {nrots}' else: if check_n_rings and num_rings > 1: return True, f' nrings: {num_rings}' else: if filter_ortho and find_ortho_substituents(frag_smi): return True, f'ortho substituent exists.' else: return False, 'pass' elif filter_type == 2: if nrots > 1: return True, f' nrot: {nrots}' else: # nrot = 0 or 1 if check_n_rings and num_rings > 1: return True, f' nrings: {num_rings}' # remain 1ring with nrot 0 or 1/ chain with nrot 0 or 1 elif check_n_rings and num_rings == 1 and nrots == 1: return True, f' nrings: {num_rings}, nrots: {nrots}' else: if filter_ortho and find_ortho_substituents(frag_smi): return True, f'ortho substituent exists.' else: return False, 'pass'
# 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#CCC1CCNC=C1") print("Number of non-rotatable bonds =", end=" ") print(oechem.OECount(mol, oechem.OENotBond(oechem.OEIsRotor()))) print("Number of ring double bonds =", end=" ") print( oechem.OECount( mol, oechem.OEAndBond(oechem.OEBondIsInRing(), oechem.OEHasOrder(2)))) print("Number of double or triple bonds =", end=" ") print( oechem.OECount(mol, oechem.OEOrBond(oechem.OEHasOrder(2), oechem.OEHasOrder(3)))) # @ </SNIPPET>
def generate_torsion_profile(mol_list): sf_map = {} for graph_mol in mol_list: if oechem.OECount(graph_mol, oechem.OEIsRotor()) == 0: logging.warning( 'WARNING: Skipping molecule %s... rotor count is zero', graph_mol.GetTitle()) continue frag_mols = get_molecule_torsion_fragments(graph_mol) if len(frag_mols) == 0: logging.warning( 'WARNING: Skipping molecule %s... cannot identify torsional fragments', graph_mol.GetTitle()) continue _, torsion_data = extract_molecule_torsion_data(graph_mol, frag_mols) for frag_mol in frag_mols: if has_undesirable_elements(frag_mol) or oechem.OECount( frag_mol, oechem.OEIsPhosphorus()) > 0: logging.warning( 'WARNING: Skipping a fragment in molecule %s... fragment has undesirable elements', graph_mol.GetTitle()) continue # skip fragments with one or more formal charge skip_torsion = False if oechem.OECount(frag_mol, oechem.OEHasFormalCharge(1)) > 0 \ or oechem.OECount(frag_mol, oechem.OEHasFormalCharge(2)) > 0: skip_torsion = True specific_inchi = get_specific_dihedral_inchi_key(frag_mol) if specific_inchi not in sf_map: sf_list = get_profile_sf(frag_mol) sf_map[specific_inchi] = sf_list torsion_data_items = torsion_data[specific_inchi] for torsion_data_item in torsion_data_items: a_idx, b_idx, c_idx, d_idx, _ = torsion_data_item b = graph_mol.GetAtom(oechem.OEHasAtomIdx(b_idx)) c = graph_mol.GetAtom(oechem.OEHasAtomIdx(c_idx)) bond = graph_mol.GetBond(b, c) if skip_torsion: bond.SetData(SKIP_TORSION_TAG, True) tor_atoms_str = ' '.join( list(map(str, [a_idx, b_idx, c_idx, d_idx]))) if not bond.HasData(TORSION_ATOMS_FRAGMENT_TAG): bond.SetData(TORSION_ATOMS_FRAGMENT_TAG, tor_atoms_str) bond.SetData(SPECIFIC_INCHI_TAG, specific_inchi) else: tmp_data = bond.GetData(TORSION_ATOMS_FRAGMENT_TAG) tmp_data = tmp_data + ':' + tor_atoms_str bond.SetData(TORSION_ATOMS_FRAGMENT_TAG, tmp_data) graph_mol.SetData(HAS_PROFILES_TAG, False) for bond in graph_mol.GetBonds(oechem.OEIsRotor()): if bond.HasData(TORSION_ATOMS_FRAGMENT_TAG): graph_mol.SetData(HAS_PROFILES_TAG, True) break return mol_list, sf_map
#!/usr/bin/env python # (C) 2017 OpenEye Scientific Software Inc. All rights reserved. # # TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is # provided to current licensees or subscribers of OpenEye products or # SaaS offerings (each a "Customer"). # Customer is hereby permitted to use, copy, and modify the Sample Code, # subject to these terms. OpenEye claims no rights to Customer's # 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 __future__ import print_function from openeye import oechem mol = oechem.OEGraphMol() oechem.OESmilesToMol(mol, "CC(=O)Nc1c[nH]cc1") print("Number of ring bonds =", oechem.OECount(mol, oechem.OEBondIsInRing())) print("Number of rotor bonds =", oechem.OECount(mol, oechem.OEIsRotor())) # @ </SNIPPET>
# 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. from openeye import oechem from openeye import oedepict from openeye import oegrapheme # @ <SNIPPET-ANNOTATE-BOND-PROPERTY> mol = oechem.OEGraphMol() oechem.OESmilesToMol(mol, "c1cc(NCC)cc(CS(=O)(=O)O)c1") oedepict.OEPrepareDepiction(mol) opts = oedepict.OE2DMolDisplayOptions(400, 250, oedepict.OEScale_AutoScale) opts.SetTitleLocation(oedepict.OETitleLocation_Hidden) disp = oedepict.OE2DMolDisplay(mol, opts) pen = oedepict.OEPen(oechem.OEDarkPurple, oechem.OEDarkPurple, oedepict.OEFill_Off, 2.0) glyph = oegrapheme.OEBondGlyphArrow(pen, 0.5) oegrapheme.OEAddGlyph(disp, glyph, oechem.OEIsRotor()) oedepict.OERenderMolecule("AnnotateBondPredicate.png", disp) # @ </SNIPPET-ANNOTATE-BOND-PROPERTY> oedepict.OERenderMolecule("AnnotateBondPredicate.pdf", disp)
def GetTorsions(mol): ''' Goes through each rotatable bond in the molecule and extracts torsion atoms (a-b-c-d) Core torsion atoms are extended by one bond If core or extended atoms are part of a ring, then entire ring is kept Keep ortho substitution Keep functional groups that have at least one atom overlap with the core/extended torsion atoms Functional group inclusion criteria: - <= 5 heavy atoms - must contain at least one hetero atom - non-ring Add methyl cap if bond involving hetero atom is broken @param mol: OEGraphMol @type mol: OEGraphMol @return: list[OEGraphMol] ''' # mol = OEGraphMol(input_mol) oechem.OEAssignHybridization(mol) funcGrps = TorsionGenerator.GetFuncGroups(mol) includedTorsions = oechem.OEAtomBondSet() torsionMols = [] for atom in mol.GetAtoms(): atom.SetData("idx", atom.GetIdx() + 1) torsions = get_canonical_torsions(mol) if torsions is None: torsions = oechem.OEGetTorsions(mol, oechem.OEIsRotor()) for torsion in torsions: if torsion.a.IsHydrogen() or torsion.b.IsHydrogen() or \ torsion.c.IsHydrogen() or torsion.d.IsHydrogen(): continue torsion_bond = mol.GetBond(torsion.b, torsion.c) if includedTorsions.HasBond(torsion_bond): continue # if includedTorsions.HasAtom(torsion.b) and \ # includedTorsions.HasAtom(torsion.c): # continue # revert map idx to zero in original mol for atom in mol.GetAtoms(): atom.SetMapIdx(0) # includedTorsions.AddAtom(torsion.b) # includedTorsions.AddAtom(torsion.c) includedTorsions.AddBond(torsion_bond) torsionSet = oechem.OEAtomBondSet(mol.GetBonds()) torsionSet.AddAtoms([torsion.a, torsion.b, torsion.c, torsion.d]) for atom in torsionSet.GetAtoms(): atom.SetMapIdx(1) # extend core torsion atoms by one bond nbrs = TorsionGenerator.GetNbrs(torsionSet) torsionSet.AddAtoms(nbrs) # include ring atoms ringAtoms = TorsionGenerator.GetSameRingAtoms(mol, torsionSet) torsionSet.AddAtoms(ringAtoms) for atom in torsionSet.GetAtoms(): if not atom.GetMapIdx() == 1: atom.SetMapIdx(2) # add functional groups that overlap with torsion set TorsionGenerator.AddFuncGroupAtoms(funcGrps, torsionSet) # add relevant ring atoms (ortho substituents and ring H) TorsionGenerator.AddRelevantRingAtoms(mol, torsion, torsionSet) # special treatment for C=O for atom in torsionSet.GetAtoms( oechem.OEAndAtom( oechem.OEIsOxygen(), oechem.OEIsAtomHybridization( oechem.OEHybridization_sp2))): for nbr in atom.GetAtoms(): if torsionSet.HasAtom(nbr): for nbr2 in nbr.GetAtoms(oechem.OEIsHeavy()): if not torsionSet.HasAtom(nbr2): nbr2.SetMapIdx(2) torsionSet.AddAtom(nbr2) # mark bridging atom and cap if needed BRIDGE_ATOM_IDX = 4 TorsionGenerator.MarkBridgingAtoms(BRIDGE_ATOM_IDX, mol, torsionSet) A_IDX = 11 B_IDX = 12 C_IDX = 13 D_IDX = 14 torsion.a.SetMapIdx(A_IDX) torsion.b.SetMapIdx(B_IDX) torsion.c.SetMapIdx(C_IDX) torsion.d.SetMapIdx(D_IDX) torsionMol = oechem.OEGraphMol() oechem.OESubsetMol(torsionMol, mol, torsionSet, True) torsionMol.Sweep() torsionMols.append(torsionMol) # change bridge atom to Carbon for atom in torsionMol.GetAtoms( oechem.OEHasMapIdx(BRIDGE_ATOM_IDX)): atom.SetAtomicNum(oechem.OEElemNo_C) explicit_valence = atom.GetExplicitValence() if explicit_valence < 4: atom.SetImplicitHCount(4 - explicit_valence) TorsionGenerator.SetSDData(A_IDX, B_IDX, C_IDX, D_IDX, torsion, torsionMol) # set map idx to zero in torsion mol for atom in torsionMol.GetAtoms(): atom.SetMapIdx(0) # revert map idx to zero in original mol for atom in mol.GetAtoms(): atom.SetMapIdx(0) return torsionMols
def gen_starting_confs( mol, torsion_library, max_one_bond_away=True, num_conformers=MAX_CONFS, rms_cutoff=0.0, energy_window=25, ): # Identify the atoms in the dihedral TAGNAME = "TORSION_ATOMS_FRAGMENT" if not has_sd_data(mol, TAGNAME): raise ValueError( "Molecule does not have the SD Data Tag '{}'.".format(TAGNAME)) dihedralAtomIndices = [ int(x) - 1 for x in get_sd_data(mol, TAGNAME).split() ] inDih = oechem.OEOrAtom( oechem.OEOrAtom( oechem.OEHasAtomIdx(dihedralAtomIndices[0]), oechem.OEHasAtomIdx(dihedralAtomIndices[1]), ), oechem.OEOrAtom( oechem.OEHasAtomIdx(dihedralAtomIndices[2]), oechem.OEHasAtomIdx(dihedralAtomIndices[3]), ), ) mol1 = mol.CreateCopy() mc_mol = oechem.OEMol(mol1) # Tag torsion atoms with their dihedral index for atom in mc_mol.GetAtoms(): if atom.GetIdx() == dihedralAtomIndices[0]: atom.SetData("dihidx", 0) if atom.GetIdx() == dihedralAtomIndices[1]: atom.SetData("dihidx", 1) if atom.GetIdx() == dihedralAtomIndices[2]: atom.SetData("dihidx", 2) if atom.GetIdx() == dihedralAtomIndices[3]: atom.SetData("dihidx", 3) if num_conformers > 1: # Set criterion for rotatable bond if False and max_one_bond_away: # this max function makes this seem potentially broken only_one_bond_away = distance_predicate(dihedralAtomIndices[1], dihedralAtomIndices[2]) rotor_predicate = oechem.OEAndBond( only_one_bond_away, oechem.PyBondPredicate(isRotatableBond)) elif False: # this ONLY samples special bonds & neglects "regualr" torsions rotor_predicate = oechem.PyBondPredicate(isRotatableBond) else: # try this more general sampling, but leave prior versions untouched rotor_predicate = oechem.OEOrBond( oechem.OEIsRotor(), oechem.PyBondPredicate(isRotatableBond)) # Initialize conformer generator and multi-conformer library conf_generator = configure_omega(torsion_library, rotor_predicate, rms_cutoff, energy_window, num_conformers) # Generator conformers if not conf_generator(mc_mol, inDih): raise ValueError("Conformers cannot be generated.") logging.debug( "Generated a total of %d conformers for %s.", mc_mol.NumConfs(), mol.GetTitle(), ) # Reassign new_didx = [-1, -1, -1, -1] for atom in mc_mol.GetAtoms(): if atom.HasData("dihidx"): new_didx[atom.GetData("dihidx")] = atom.GetIdx() oechem.OEClearSDData(mc_mol) oechem.OESetSDData(mc_mol, TAGNAME, " ".join(str(x + 1) for x in new_didx)) oechem.OESetSDData( mc_mol, "TORSION_ATOMS_ParentMol", get_sd_data(mol, "TORSION_ATOMS_ParentMol"), ) oechem.OESetSDData( mc_mol, "TORSION_ATOMPROP", f"cs1:0:1;1%{new_didx[0]+1}:1%{new_didx[1]+1}:1%{new_didx[2]+1}:1%{new_didx[3]+1}", ) for conf_no, conf in enumerate(mc_mol.GetConfs()): conformer_label = ( mol.GetTitle() + "_" + "_".join(get_sd_data(mol, "TORSION_ATOMS_ParentMol").split()) + "_{:02d}".format(conf_no)) oechem.OESetSDData(conf, "CONFORMER_LABEL", conformer_label) conf.SetTitle(conformer_label) return mc_mol
def cal_molecule_torsion_strain(mol, profiles_map): """ @type mol: oechem.OEGraphMol|oechem.OEMol :param mol: :param profiles_map: :return: """ if type(mol) is oechem.OEMol: graph_mol = oechem.OEGraphMol(mol.GetActive()) data = extract_molecule_torsion_data(graph_mol) else: data = extract_molecule_torsion_data(mol) if data is not None: _, tor_map = data if type(mol) is oechem.OEGraphMol: for tor_inchi, tor_data_list in tor_map.items(): if tor_inchi in profiles_map: for tor_data in tor_data_list: _, b_idx, c_idx, _, angle = tor_data bond = mol.GetBond( mol.GetAtom(oechem.OEHasAtomIdx(b_idx)), mol.GetAtom(oechem.OEHasAtomIdx(c_idx)), ) if bond is not None: strain_energy = profiles_map[tor_inchi](angle) if strain_energy < 0: strain_energy = 0 if (bond.HasData(STRAIN_TAG) and bond.GetData(STRAIN_TAG) > strain_energy): bond.SetData(STRAIN_TAG, strain_energy) total_strain = 0.0 for bond in mol.GetBonds(): if bond.HasData(STRAIN_TAG): total_strain += bond.GetData(STRAIN_TAG) mol.SetData(STRAIN_TAG, total_strain) elif type(mol) is oechem.OEMol: for conf in mol.GetConfs(): bondIdx2energy = {} bondIdx2profile = {} bondIdx2toratoms = {} bondIdx2angles = {} for tor_inchi, tor_data_list in tor_map.items(): if tor_inchi in profiles_map: for tor_data in tor_data_list: a_idx, b_idx, c_idx, d_idx, _ = tor_data b_atm = conf.GetAtom(oechem.OEHasAtomIdx(b_idx)) c_atm = conf.GetAtom(oechem.OEHasAtomIdx(c_idx)) bond = conf.GetBond(b_atm, c_atm) if bond is not None: a_atm = conf.GetAtom( oechem.OEHasAtomIdx(a_idx)) d_atm = conf.GetAtom( oechem.OEHasAtomIdx(d_idx)) angle = (oechem.OEGetTorsion( conf, a_atm, b_atm, c_atm, d_atm) * oechem.Rad2Deg) strain_energy = float( profiles_map[tor_inchi](angle)) if strain_energy < 0: strain_energy = 0 x = range(-165, 181, 15) y = [] for a in x: y.append(float(profiles_map[tor_inchi](a))) energy_profile = generate_energy_profile_sd_data_1d( list(zip(x, y))) bondIdx = bond.GetIdx() if bondIdx not in bondIdx2energy: bondIdx2energy[bondIdx] = strain_energy bondIdx2profile[bondIdx] = energy_profile bondIdx2toratoms[bondIdx] = [ a_idx + 1, b_idx + 1, c_idx + 1, d_idx + 1, ] bondIdx2angles[bondIdx] = angle elif bondIdx2energy[bondIdx] > strain_energy: bondIdx2energy[bondIdx] = strain_energy bondIdx2profile[bondIdx] = energy_profile bondIdx2toratoms[bondIdx] = [ a_idx + 1, b_idx + 1, c_idx + 1, d_idx + 1, ] bondIdx2angles[bondIdx] = angle # sd property place holder oechem.OESetSDData(conf, "QM_STRAIN", "0.0") oechem.OESetSDData(conf, "NUM_QM_TORSION_PROFILES", "0") oechem.OESetSDData(conf, "NUM_MISSING_QM_TORSIONS", "-1") total_strain = 0.0 tor_idx = 1 for tor_count, bond in enumerate( conf.GetBonds(oechem.OEIsRotor())): bidx = bond.GetIdx() if bidx in bondIdx2energy: total_strain += bondIdx2energy[bidx] tmp = ":1%".join(list(map(str, bondIdx2toratoms[bidx]))) tor_atomprop = "cs1:0:1;1%" + tmp oechem.OESetSDData( conf, "QM_TORSION_ATOMS_%d_" % tor_idx + "ATOMPROP", tor_atomprop, ) oechem.OESetSDData( conf, "QM_TORSION_%d_" % tor_idx + ENERGY_PROFILE_TAG, bondIdx2profile[bidx], ) oechem.OESetSDData( conf, "TORSION_ANGLE_%d" % tor_idx, "%.1f" % bondIdx2angles[bidx], ) oechem.OESetSDData( conf, "QM_STRAIN_TORSION_%d" % tor_idx, "%.1f" % bondIdx2energy[bidx], ) tor_idx += 1 oechem.OESetSDData(conf, "QM_STRAIN", "%.1f" % total_strain) oechem.OESetSDData(conf, "NUM_QM_TORSION_PROFILES", "%d" % (tor_count + 1)) num_missing_torsions = (tor_count + 1) - (tor_idx - 1) oechem.OESetSDData(conf, "NUM_MISSING_QM_TORSIONS", "%d" % num_missing_torsions) conf.SetData(STRAIN_TAG, total_strain)
def get_canonical_torsions(mol): ''' Return unique torsions in canonical order. Only one torsion containing the same central two atoms are return Cannonical ordering is determined using the order of atoms in canonical smiles representation 1. generate a canonical smiles representation from the input molecule 2. create a list of (min(b_idx, c_idx), min(a_idx, d_idx), max(a_idx, d_idx), OETorsion) 3. sort the list in #2, extract subset with unique rotatable bonds :param mol: OEGraphMol :return: list[OEGraphMol] ''' CANONICAL_IDX_TAG = 'can_idx' def assign_canonical_idx(mol): for atom in mol.GetAtoms(): atom.SetMapIdx(0) for map_idx, atom in enumerate(mol.GetAtoms(oechem.OEIsHeavy())): atom.SetMapIdx(map_idx + 1) can_smiles = oechem.OEMolToSmiles(mol) can_mol = oechem.OEGraphMol() # smiles_opt = OEParseSmilesOptions(canon=True) # OEParseSmiles(can_mol, can_smiles, smiles_opt) oechem.OESmilesToMol(can_mol, can_smiles) for can_atom in can_mol.GetAtoms(oechem.OEIsHeavy()): atom = mol.GetAtom(oechem.OEHasMapIdx(can_atom.GetMapIdx())) atom.SetData(CANONICAL_IDX_TAG, can_atom.GetIdx()) try: assign_canonical_idx(mol) except Exception as e: print('Error GetCanonicalizedTorsions. ', e) return None torsions = [] for torsion in oechem.OEGetTorsions(mol, oechem.OEIsRotor()): if torsion.a.IsHydrogen() or torsion.b.IsHydrogen() or \ torsion.c.IsHydrogen() or torsion.d.IsHydrogen(): continue sum_bc = torsion.b.GetData(CANONICAL_IDX_TAG) + torsion.c.GetData( CANONICAL_IDX_TAG) min_bc = min(torsion.b.GetData(CANONICAL_IDX_TAG), torsion.c.GetData(CANONICAL_IDX_TAG)) max_bc = max(torsion.b.GetData(CANONICAL_IDX_TAG), torsion.c.GetData(CANONICAL_IDX_TAG)) min_ad = min(torsion.a.GetData(CANONICAL_IDX_TAG), torsion.d.GetData(CANONICAL_IDX_TAG)) max_ad = max(torsion.a.GetData(CANONICAL_IDX_TAG), torsion.d.GetData(CANONICAL_IDX_TAG)) torsions.append((sum_bc, min_bc, max_bc, min_ad, max_ad, torsion)) # sort torsions.sort(key=operator.itemgetter(0, 1, 2, 3, 4)) seen = {} unique_torsions = [] for _, _, _, _, _, torsion in torsions: bond = mol.GetBond(torsion.b, torsion.c) if bond is not None and bond.GetIdx() not in seen: unique_torsions.append(torsion) seen[bond.GetIdx()] = True # revert mol to original state for atom in mol.GetAtoms(oechem.OEIsHeavy()): atom.SetMapIdx(0) atom.DeleteData(CANONICAL_IDX_TAG) return unique_torsions