def __init__(self, distCutoff=distCutoff, distCutoff2=distCutoff2, d2min=d2min, d2max=d2max, d3min=d3min, d3max=d3max, a2min=a2min, a2max=a2max, a3min=a3min, a3max=a3max, donorTypes=allDonors, acceptorTypes=allAcceptors, distOnly=False): d = self.paramDict = {} d['distCutoff'] = distCutoff d['distCutoff2'] = distCutoff2 d['d2min'] = d2min d['d2max'] = d2max d['d3min'] = d3min d['d3max'] = d3max d['a2min'] = a2min d['a2max'] = a2max d['a3min'] = a3min d['a3max'] = a3max d['donorTypes'] = donorTypes d['acceptorTypes'] = acceptorTypes d['distOnly'] = distOnly self.distSelector = DistanceSelector(return_dist=0)
def __init__(self, receptor_file, percentCutoff=1., detect_pi=False, dist_cutoff=6, verbose=False, distanceSelector=None, hydrogen_bond_builder=None, distanceSelectorWithCutoff=None, aromatic_cycle_bond_selector=None): self.receptor_file = receptor_file receptor = Read(receptor_file) assert(len(receptor)==1) assert isinstance(receptor, MoleculeSet) self.macro = receptor[0] self.macro_atoms = self.macro.allAtoms self.macro.buildBondsByDistance() self.verbose = verbose #??useful?? self.percentCutoff = percentCutoff self.detect_pi = detect_pi self.distanceSelector = distanceSelector if self.distanceSelector is None: self.distanceSelector = CloserThanVDWSelector(return_dist=0) self.hydrogen_bond_builder = hydrogen_bond_builder if self.hydrogen_bond_builder is None: self.hydrogen_bond_builder = HydrogenBondBuilder() self.distanceSelectorWithCutoff = distanceSelectorWithCutoff if self.distanceSelectorWithCutoff is None: self.distanceSelectorWithCutoff = DistanceSelector() self.dist_cutoff=float(dist_cutoff) self.results = d = {} self.report_list =['lig_hb_atoms','lig_close_atoms'] self.aromatic_cycle_bond_selector = aromatic_cycle_bond_selector if self.aromatic_cycle_bond_selector is None: self.aromatic_cycle_bond_selector = AromaticCycleBondSelector() if detect_pi: self.report_list.extend(['pi_cation','pi_pi', 'cation_pi', 't_shaped']) if self.verbose: print("self.report_list=", self.report_list)
def __init__(self, receptor_file, percentCutoff=1., detect_pi=False, dist_cutoff=6, verbose=False, distanceSelector=None, hydrogen_bond_builder=None, distanceSelectorWithCutoff=None, aromatic_cycle_bond_selector=None): self.receptor_file = receptor_file receptor = Read(receptor_file) assert(len(receptor)==1) assert isinstance(receptor, MoleculeSet) self.macro = receptor[0] self.macro_atoms = self.macro.allAtoms self.macro.buildBondsByDistance() self.verbose = verbose #??useful?? self.percentCutoff = percentCutoff self.detect_pi = detect_pi self.distanceSelector = distanceSelector if self.distanceSelector is None: self.distanceSelector = CloserThanVDWSelector(return_dist=0) self.hydrogen_bond_builder = hydrogen_bond_builder if self.hydrogen_bond_builder is None: self.hydrogen_bond_builder = HydrogenBondBuilder() self.distanceSelectorWithCutoff = distanceSelectorWithCutoff if self.distanceSelectorWithCutoff is None: self.distanceSelectorWithCutoff = DistanceSelector() self.dist_cutoff=float(dist_cutoff) self.results = d = {} self.report_list =['lig_hb_atoms','lig_close_atoms'] self.aromatic_cycle_bond_selector = aromatic_cycle_bond_selector if self.aromatic_cycle_bond_selector is None: self.aromatic_cycle_bond_selector = AromaticCycleBondSelector() if detect_pi: self.report_list.extend(['pi_cation','pi_pi', 'cation_pi', 't_shaped']) if self.verbose: print "self.report_list=", self.report_list
class HydrogenBondBuilder: """ object which can build hydrogen bonds between atoms according to their coords and atom type """ def __init__(self, distCutoff=distCutoff, distCutoff2=distCutoff2, d2min=d2min, d2max=d2max, d3min=d3min, d3max=d3max, a2min=a2min, a2max=a2max, a3min=a3min, a3max=a3max, donorTypes=allDonors, acceptorTypes=allAcceptors, distOnly=False): d = self.paramDict = {} d['distCutoff'] = distCutoff d['distCutoff2'] = distCutoff2 d['d2min'] = d2min d['d2max'] = d2max d['d3min'] = d3min d['d3max'] = d3max d['a2min'] = a2min d['a2max'] = a2max d['a3min'] = a3min d['a3max'] = a3max d['donorTypes'] = donorTypes d['acceptorTypes'] = acceptorTypes d['distOnly'] = distOnly self.distSelector = DistanceSelector(return_dist=0) def check_babel_types(self, ats): num_ats = len(ats) num_babel_type = len(ats.get(lambda x: hasattr(x, 'babel_type'))) num_bnd_type = len(ats.get(lambda x: hasattr(x, 'bnd_type'))) if (num_babel_type != num_ats) or (num_bnd_type != num_ats): babel = AtomHybridization() bond_orderer = BondOrder() tops = ats.top.uniq() for mol in tops: babel.assignHybridization(mol.allAtoms) bond_orderer.assignBondOrder(mol.allAtoms, mol.allAtoms.bonds[0]) mol.allAtoms._bndtyped = 1 def reset(self, ats): tops = ats.top.uniq() for mol in tops: for a in mol.allAtoms: if hasattr(a, 'hbonds'): for item in a.hbonds: del item delattr(a, 'hbonds') def build(self, group1, group2=None, reset=True, paramDict=None): """atDict <- build(group1, group2, reset, paramDict=None, **kw): group1: atoms group2: atoms reset: remove all previous hbonds, default True! paramDict: a dictionary with these keys and default values distCutoff: 2.25 hydrogen--acceptor distance distCutoff2: 3.00 donor... acceptor distance d2min: 120 <min theta for sp2 hybridized donors> d2max: 180 <max theta for sp2 hybridized donors> d3min: 120 <min theta for sp3 hybridized donors> d3max: 170 <max theta for sp3 hybridized donors> a2min: 120 <min phi for sp2 hybridized donors> a2max: 150 <max phi for sp2 hybridized donors> a3min: 100 <min phi for sp3 hybridized donors> a3max: 150 <max phi for sp3 hybridized donors> @@FIX THIS: these do not seem to be input here donorTypes = allDonors acceptorTypes = allAcceptors """ # setup parameter dictionary if paramDict is None: paramDict = self.paramDict # setup group2 if group2 is None: group2 = group1 # process each group # group1 if group1.__class__ != Atom: group1 = group1.findType(Atom) # print "now group1=", group1.full_name() if not len(group1): return "ERROR" # @@ OR WHAT? self.check_babel_types(group1) # group2 if group2.__class__ != Atom: group2 = group2.findType(Atom) # print "now group2=", group2.full_name() if not len(group2): return "ERROR" # @@ OR WHAT? self.check_babel_types(group2) if reset: # do optional reset: remove all prior hbonds self.reset(group1) self.reset(group2) # print "group1=", len(group1) # print "group2=", len(group2) # buildHbonds atDict = {} dict1 = self.buildD(group1, paramDict) # @@what is this doing here??? # sp2 hybridized atoms # dAts2 = ats.get(lambda x, l=sp2: x.babel_type in l) if group1 == group2: # only one step: atD1 = self.process(dict1, dict1, paramDict) # print 'len(atD1).keys()=', len(atD1.keys()) atD2 = {} else: # two steps: # 1: group1 donors v group2 acceptors # 2: group1 acceptors vs group2 donors dict2 = self.buildD(group2, paramDict) atD1 = self.process(dict1, dict2, paramDict) atD2 = self.process(dict2, dict1, paramDict) # print 'len(atD1).keys()=', len(atD1.keys()) # print 'len(atD2).keys()=', len(atD2.keys()) # if called with 1 atom could get tuple of two empty dictionaries if type(atD1) == type(atDict): if len(atD1): atDict.update(atD1) if type(atD2) == type(atDict): if len(atD2): atDict.update(atD2) # @@DESCRIBE atDict # this dict has atomSets as keys and as values(?check that) return atDict def checkForPossibleH(self, ats, blen): # @@FIX THIS: WHAT IS THE POINT OF THIS??? # check that if at has all bonds, at least one is to a hydrogen # have to do this by element?? probAts = AtomSet(ats.get(lambda x, blen=blen: len(x.bonds) == blen)) # probOAts = ats.get(lambda x, blen=blen: len(x.bonds)==blen) # probSAts = ats.get(lambda x, blen=blen: len(x.bonds)==blen) if probAts: rAts = AtomSet([]) for at in probAts: if not len(at.findHydrogens()): rAts.append(at) if len(rAts): ats = ats.subtract(rAts) return ats def getHBDonors(self, ats, donorList): # getHBDonors sp2 = [] sp3 = [] for item in sp2Donors: if item in donorList: sp2.append(item) for item in sp3Donors: if item in donorList: sp3.append(item) dAts2 = ats.get(lambda x, l=sp2: x.babel_type in l) if not dAts2: dAts2 = AtomSet([]) else: dAts2 = self.checkForPossibleH(dAts2, 3) dAts3 = ats.get(lambda x, l=sp3: x.babel_type in l) if not dAts3: dAts3 = AtomSet([]) else: dAts3 = self.checkForPossibleH(dAts3, 4) return dAts2, dAts3 def filterAcceptors(self, accAts): ntypes = ['Npl', 'Nam'] npls = accAts.get(lambda x, ntypes=ntypes: x.babel_type == 'Npl') nams = accAts.get(lambda x, ntypes=ntypes: x.babel_type == 'Nam') # nAts = accAts.get(lambda x, ntypes=ntypes: x.babel_type in ntypes) restAts = accAts.get( lambda x, ntypes=ntypes: x.babel_type not in ntypes) if not restAts: restAts = AtomSet([]) # if nAts: if npls: # for at in nAts: for at in npls: s = 0 for b in at.bonds: if b.bondOrder == 'aromatic': s = s + 2 else: s = s + b.bondOrder # if s<3: # apparently this is wrong if s < 4: restAts.append(at) if nams: # for at in nAts: for at in nams: s = 0 for b in at.bonds: if b.bondOrder == 'aromatic': s = s + 2 else: s = s + b.bondOrder # s = s + b.bondOrder if s < 3: restAts.append(at) return restAts def getHBAcceptors(self, ats, acceptorList): # print "getHBAcceptors: acceptorList=", acceptorList # getHBAcceptors sp2 = [] sp3 = [] for item in sp2Acceptors: if item in acceptorList: sp2.append(item) for item in sp3Acceptors: if item in acceptorList: sp3.append(item) dAts2 = AtomSet(ats.get(lambda x, l=sp2: x.babel_type in l)) if dAts2: dAts2 = self.filterAcceptors(dAts2) dAts3 = AtomSet(ats.get(lambda x, l=sp3: x.babel_type in l)) return dAts2, dAts3 def buildD(self, ats, paramDict=None): if paramDict is None: paramDict = self.paramDict # these are from the __call__ method of vf.buildHBonds if 'distCutoff' not in paramDict: paramDict['distCutoff'] = 2.25 if 'distCutoff2' not in paramDict: paramDict['distCutoff2'] = 3.00 if 'd2min' not in paramDict: paramDict['d2min'] = 120. if 'd2max' not in paramDict: paramDict['d2max'] = 180. if 'd3min' not in paramDict: paramDict['d3min'] = 120. if 'd3max' not in paramDict: paramDict['d3max'] = 170. if 'a2min' not in paramDict: paramDict['a2min'] = 130. if 'a2max' not in paramDict: paramDict['a2max'] = 170. if 'a3min' not in paramDict: paramDict['a3min'] = 120. if 'a3max' not in paramDict: paramDict['a3max'] = 170. if 'distOnly' not in paramDict: paramDict['distOnly'] = 0 if 'donorTypes' not in paramDict: paramDict['donorTypes'] = allDonors if 'acceptorTypes' not in paramDict: paramDict['acceptorTypes'] = allAcceptors d = {} donorTypes = paramDict['donorTypes'] donor2Ats, donor3Ats = self.getHBDonors(ats, donorTypes) d23 = donor2Ats + donor3Ats # hAts = ats.get(lambda x, d23=d23: x.element=='H' \ # and x.bonds[0].neighborAtom(x) in d23) hydrogen_atoms = ats.get(lambda x: x.element == 'H' and len(x.bonds)) # hAts = AtomSet(ats.get(lambda x, donorTypes=donorTypes: x.element=='H' \ hAts = AtomSet( hydrogen_atoms.get( lambda x, donorTypes=donorTypes: x.bonds[0].atom1.babel_type in donorTypes or x.bonds[0].atom2.babel_type in donorTypes)) d['hAts'] = hAts d['donor2Ats'] = donor2Ats d['donor3Ats'] = donor3Ats acceptorTypes = paramDict['acceptorTypes'] # print "about to call getHBAcceptors with acceptorTypes=", acceptorTypes acceptor2Ats, acceptor3Ats = self.getHBAcceptors(ats, acceptorTypes) d['acceptor2Ats'] = acceptor2Ats d['acceptor3Ats'] = acceptor3Ats if acceptor2Ats: acceptorAts = acceptor2Ats if acceptor3Ats: acceptorAts = acceptorAts + acceptor3Ats elif acceptor3Ats: acceptorAts = acceptor3Ats else: # CHECK THIS: should it be None or AtomSet([]) acceptorAts = None d['acceptorAts'] = acceptorAts return d def getMat(self, ats): pass # tops = ats.top.uniq() # if len(tops)>1: # self.warningMsg('transformation mat=None:>1 mol in atomset!') # return None # g = tops[0].geomContainer.geoms['master'] # return g.GetMatrix(g) def process(self, dict1, dict2, paramDict): # hAts are keys, aceptorAts are checks hAts = dict1['hAts'] tAts = hAts dist = paramDict['distCutoff'] distOnly = paramDict['distOnly'] if not hAts: # then use donors and a different distance tAts = dict1['donor2Ats'] + dict1['donor3Ats'] dist = paramDict['distCutoff2'] acceptorAts = dict2['acceptorAts'] # print "acceptorAts=", acceptorAts if not acceptorAts or not tAts: # 6/14/2004 return {}, {} # call distanceSelector on two groups of atoms with dist # keyMat = self.getMat(tAts) # checkMat = self.getMat(acceptorAts) atDict = self.distSelector.select(tAts, acceptorAts, dist) # keyMat=keyMat, checkMat=checkMat) # atDict = self.distSelector.select(tAts, acceptorAts, dist) # first remove bonded angles atDict = self.removeNeighbors(atDict) donor2Ats = dict1['donor2Ats'] donor3Ats = dict1['donor3Ats'] acceptor2Ats = dict2['acceptor2Ats'] acceptor3Ats = dict2['acceptor3Ats'] if distOnly: # need to build hbonds and return dictionary self.makeBonds(atDict, donor2Ats, donor3Ats, acceptor2Ats, acceptor3Ats, paramDict) return atDict badAtDict = self.filterBasedOnAngs(atDict, donor2Ats, donor3Ats, acceptor2Ats, acceptor3Ats, paramDict) atDict = self.removeBadAts(atDict, badAtDict) if atDict is None: atDict = {} return atDict def makeBonds(self, pD, d2Ats, d3Ats, a2Ats, a3ats, paramDict): for k in list(pD.keys()): if k.element == 'H': if hasattr(k, 'hbonds') and len(k.hbonds): continue d = k.bonds[0].atom1 if id(d) == id(k): d = k.bonds[0].atom2 # d = k.bonds[0].neighborAtom(k) h = k else: d = k h = None # pD[k] is a list of close-enough ats for ac in pD[k]: if ac == d: continue dSp2 = d in d2Ats aSp2 = ac in a2Ats if dSp2: if aSp2: typ = 22 else: typ = 23 elif aSp2: typ = 32 else: typ = 33 # THEY could be already bonded alreadyBonded = 0 if hasattr(d, 'hbonds') and hasattr(ac, 'hbonds'): for hb in d.hbonds: if hb.donAt == ac or hb.accAt == ac: alreadyBonded = 1 if not alreadyBonded: newHB = HydrogenBond(d, ac, h, typ=typ) if not hasattr(ac, 'hbonds'): ac.hbonds = [] if not hasattr(d, 'hbonds'): d.hbonds = [] ac.hbonds.append(newHB) d.hbonds.append(newHB) if h is not None: # hydrogens can have only 1 hbond h.hbonds = [newHB] def filterBasedOnAngs(self, pD, d2Ats, d3Ats, a2Ats, a3ats, paramDict): badAtDict = {} d2max = paramDict['d2max'] d2min = paramDict['d2min'] d3max = paramDict['d3max'] d3min = paramDict['d3min'] # NEED these parameters a2max = paramDict['a2max'] a2min = paramDict['a2min'] a3max = paramDict['a3max'] a3min = paramDict['a3min'] # NB now pD keys could be hydrogens OR donors for k in list(pD.keys()): if k.element == 'H': d = k.bonds[0].atom1 if id(d) == id(k): d = k.bonds[0].atom2 # d = k.bonds[0].neighborAtom(k) h = k else: d = k h = None badAts = AtomSet([]) ct = 0 for ac in pD[k]: if h is not None: ang = getAngle(ac, h, d) else: acN = ac.bonds[0].atom1 if id(acN) == id(ac): acN = ac.bonds[0].atom2 # acN = ac.bonds[0].neighborAtom(ac) ang = getAngle(d, ac, acN) # print 'ang=', ang dSp2 = d in d2Ats aSp2 = ac in a2Ats # these limits could be adjustable if h is not None: if dSp2: upperLim = d2max lowerLim = d2min # upperLim = 170 # lowerLim = 130 else: upperLim = d3max lowerLim = d3min # upperLim = 180 # lowerLim = 120 else: # if there is no hydrogen use d-ac-acN angles if dSp2: upperLim = a2max lowerLim = a2min # upperLim = 150 # lowerLim = 110 else: upperLim = a3max lowerLim = a3min # upperLim = 150 # lowerLim = 100 if ang > lowerLim and ang < upperLim: # AT THIS MOMENT BUILD HYDROGEN BOND: if dSp2: if aSp2: typ = 22 else: typ = 23 elif aSp2: typ = 32 else: typ = 33 # THEY could be already bonded alreadyBonded = 0 if hasattr(d, 'hbonds') and hasattr(ac, 'hbonds'): for hb in d.hbonds: if hb.donAt == ac or hb.accAt == ac: alreadyBonded = 1 if not alreadyBonded: newHB = HydrogenBond(d, ac, h, theta=ang, typ=typ) if not hasattr(ac, 'hbonds'): ac.hbonds = [] if not hasattr(d, 'hbonds'): d.hbonds = [] ac.hbonds.append(newHB) d.hbonds.append(newHB) if h is not None: # hydrogens can have only 1 hbond h.hbonds = [newHB] # newHB.hlen = dist # else: # newHB.dlen = dist else: badAts.append(ac) ct = ct + 1 badAtDict[k] = badAts return badAtDict def removeBadAts(self, atDict, badAtDict): # clean-up function called after filtering on angles badKeys = list(badAtDict.keys()) for at in list(atDict.keys()): if at not in badKeys: continue if not len(badAtDict[at]): continue closeAts = atDict[at] badAts = badAtDict[at] goodAts = [] for i in range(len(closeAts)): cAt = closeAts[i] if cAt not in badAts: goodAts.append(cAt) if len(goodAts): atDict[at] = goodAts else: del atDict[at] return atDict def removeNeighbors(self, atDict): # filter out at-itself and at-bondedat up to 1:4 # NB keys could be hydrogens OR donors for at in list(atDict.keys()): closeAts = atDict[at] bondedAts = AtomSet([]) for b in at.bonds: ###at2 = b.neighborAtom(at) at2 = b.atom1 if id(at2) == id(at): at2 = b.atom2 bondedAts.append(at2) # 9/13 remove this: ##also remove 1-3 for b2 in at2.bonds: at3 = b2.atom1 if id(at3) == id(at2): at3 = b.atom2 # at3 = b2.neighborAtom(at2) if id(at3) != id(at): bondedAts.append(at3) # for b3 in at3.bonds: # at4 = b2.neighborAtom(at3) # if at4!=at and at4!=at2: # bondedAts.append(at4) bondedAts = bondedAts.uniq() goodAts = [] for i in range(len(closeAts)): cAt = closeAts[i] if cAt not in bondedAts: goodAts.append(cAt) if len(goodAts): atDict[at] = goodAts else: del atDict[at] return atDict def getDonors(self, nodes, paramDict): donorList = paramDict['donorTypes'] # print 'donorList=', donorList # currently this is a set of hydrogens hats = AtomSet(nodes.get(lambda x: x.element == 'H')) # hats are optional: if none, process donors # if there are hats: dAts are all atoms bonded to all hydrogens if hats: dAts = AtomSet([]) for at in hats: for b in at.bonds: at2 = b.atom1 if id(at2) == id(at): at2 = b.atom2 dAts.append(at2) # dAts.append(b.neighborAtom(at)) else: dAts = nodes # get the sp2 hybridized possible donors which are all ns sp2 = [] for t in ['Nam', 'Ng+', 'Npl']: if t in donorList: sp2.append(t) # ntypes = ['Nam', 'Ng+', 'Npl'] sp2DAts = None if len(sp2): sp2DAts = AtomSet(dAts.get(lambda x, sp2=sp2: x.babel_type in sp2)) hsp2 = AtomSet([]) if sp2DAts: if hats: hsp2 = AtomSet( hats.get(lambda x, sp2DAts=sp2DAts: x.bonds[0].atom1 in sp2DAts or x.bonds[0].atom2 in sp2DAts)) if sp2DAts: # remove any sp2 N atoms which already have 3 bonds not to hydrogens n2Dons = AtomSet(sp2DAts.get(lambda x: x.element == 'N')) if n2Dons: n2Dons.bl = 0 for at in n2Dons: for b in at.bonds: if type(b.bondOrder) == type(2): at.bl = at.bl + b.bondOrder else: at.bl = at.bl + 2 # allow that there might already be a hydrogen nH = at.findHydrogens() at.bl = at.bl - len(nH) badAts = AtomSet(n2Dons.get(lambda x: x.bl > 2)) if badAts: sp2DAts = sp2DAts - badAts delattr(n2Dons, 'bl') # get the sp3 hybridized possible donors sp3 = [] for t in ['N3+', 'S3', 'O3']: if t in donorList: sp3.append(t) n3DAts = None if 'N3+' in sp3: n3DAts = AtomSet(dAts.get(lambda x: x.babel_type == 'N3+')) o3DAts = None if 'O3' in sp3: o3DAts = AtomSet(dAts.get(lambda x: x.babel_type == 'O3')) if o3DAts: # remove any O3 atoms which already have 2 bonds not to hydrogens badO3s = AtomSet([]) for at in o3DAts: if len(at.bonds) < 2: continue if len(at.findHydrogens()): continue else: badO3s.append(at) if len(badO3s): o3DAts = o3DAts - badO3s s3DAts = None if 'S3' in sp3: s3DAts = AtomSet(dAts.get(lambda x: x.babel_type == 'S3')) sp3DAts = AtomSet([]) for item in [n3DAts, o3DAts, s3DAts]: if item: sp3DAts = sp3DAts + item hsp3 = AtomSet([]) if sp3DAts: if hats: hsp3 = AtomSet( hats.get(lambda x, sp3DAts=sp3DAts: x.bonds[0].atom1 in sp3DAts or x.bonds[0].atom2 in sp3DAts)) hsp = hsp2 + hsp3 # print 'hsp=', hsp.name # print 'sp2DAts=', sp2DAts.name # print 'sp3DAts=', sp3DAts.name return hsp, sp2DAts, sp3DAts def getAcceptors(self, nodes, paramDict): acceptorList = paramDict['acceptorTypes'] # print 'acceptorList=', acceptorList sp2 = [] for t in ['Npl', 'Nam']: if t in acceptorList: sp2.append(t) n2Accs = None if 'Npl' in sp2: n2Accs = AtomSet(nodes.get(lambda x: x.babel_type == 'Npl')) if 'Nam' in sp2: n2Accs2 = AtomSet(nodes.get(lambda x: x.babel_type == 'Nam')) if n2Accs2: if n2Accs: n2Accs = n2Accs + n2Accs2 else: n2Accs = n2Accs2 if n2Accs is None: n2Accs = AtomSet([]) o_sp2 = [] for t in ['O2', 'O-']: if t in acceptorList: sp2.append(t) o2Accs = None if 'O2' in o_sp2: o2Accs = AtomSet(nodes.get(lambda x: x.babel_type == 'O2')) if 'O-' in sp2: o2Accs2 = AtomSet(nodes.get(lambda x: x.babel_type == 'O-')) if o2Accs2: if o2Accs: o2Accs = o2Accs + o2Accs2 else: o2Accs = o2Accs2 if o2Accs is None: o2Accs = AtomSet([]) o3Accs = None if 'O3' in acceptorList: o3Accs = AtomSet(nodes.get(lambda x: x.babel_type == 'O3')) if o3Accs is None: o3Accs = AtomSet([]) s3Accs = None if 'S3' in acceptorList: s3Accs = AtomSet(nodes.get(lambda x: x.babel_type == 'S3')) if s3Accs is None: s3Accs = AtomSet([]) ret2Ats = AtomSet([]) for item in [n2Accs, o2Accs]: ret2Ats = ret2Ats + item ret3Ats = AtomSet([]) for item in [s3Accs, o3Accs]: ret3Ats = ret3Ats + item if ret2Ats: print('ret2Ats=', ret2Ats.name) else: print('no ret2Ats') if ret3Ats: print('ret3Ats=', ret3Ats.name) else: print('no ret3Ats') return ret2Ats, ret3Ats
class InteractionDetector: """ Base class for object to detect interactions between a receptor and potential ligands... such as virtual screen results initialized with the receptor file containing entire molecule or residues of interest. processLigand method takes as input a pdbqt file containing docked coordinates and returns a string composed of hbondStr + macrocloseContactStr + ligcloseContactStr If interections are found, a new pdbqt containing interaction description is also output: """ def __init__(self, receptor_file, percentCutoff=1., detect_pi=False, dist_cutoff=6, verbose=False, distanceSelector=None, hydrogen_bond_builder=None, distanceSelectorWithCutoff=None, aromatic_cycle_bond_selector=None): self.receptor_file = receptor_file receptor = Read(receptor_file) assert(len(receptor)==1) assert isinstance(receptor, MoleculeSet) self.macro = receptor[0] self.macro_atoms = self.macro.allAtoms self.macro.buildBondsByDistance() self.verbose = verbose #??useful?? self.percentCutoff = percentCutoff self.detect_pi = detect_pi self.distanceSelector = distanceSelector if self.distanceSelector is None: self.distanceSelector = CloserThanVDWSelector(return_dist=0) self.hydrogen_bond_builder = hydrogen_bond_builder if self.hydrogen_bond_builder is None: self.hydrogen_bond_builder = HydrogenBondBuilder() self.distanceSelectorWithCutoff = distanceSelectorWithCutoff if self.distanceSelectorWithCutoff is None: self.distanceSelectorWithCutoff = DistanceSelector() self.dist_cutoff=float(dist_cutoff) self.results = d = {} self.report_list =['lig_hb_atoms','lig_close_atoms'] self.aromatic_cycle_bond_selector = aromatic_cycle_bond_selector if self.aromatic_cycle_bond_selector is None: self.aromatic_cycle_bond_selector = AromaticCycleBondSelector() if detect_pi: self.report_list.extend(['pi_cation','pi_pi', 'cation_pi', 't_shaped']) if self.verbose: print("self.report_list=", self.report_list) def processLigand(self, ligand_file, percentCutoff=None, output=1, outputfilename=None, comment="USER AD> ", buildHB=1, remove_modelStr=False): #if outputfilename is not None, have to (1) update all the strings or (2) rename ligand self.results = d = {} ligand = Read(ligand_file) assert isinstance(ligand, MoleculeSet) assert(len(ligand)==1) ligand = ligand[0] first = ligand.name.find('_model') last = ligand.name.rfind('_model') if first!=last: ligand.name = ligand.name[:last] if remove_modelStr: curName = ligand.name modelIndex=curName.find("_model") if modelIndex>-1: curName = curName[:modelIndex] #setup outputfilestem ligand.name = curName ligand.buildBondsByDistance() if not percentCutoff: percentCutoff = self.percentCutoff # first detect sets of atoms forming hydrogen bonds # hbondStr,hblist and has_hbonds added to process vina results which do not include valid hydrogen bonds hbondStr = "" hblist = [""] has_hbonds = False if buildHB: has_hbonds = True hbondStr = self.buildHydrogenBonds(ligand, comment=comment) # if self.verbose: print("hbondStr=", hbondStr) hblist = hbondStr.split('\n') # hbond info if hblist[0]=='lig_hb_atoms : 0': has_hbonds = False # next detect sets of atoms in close contact not forming hydrogen bonds macrocloseContactStr, ligcloseContactStr = self.buildCloseContactAtoms(percentCutoff, ligand, comment=comment) # self.piResults = "" if self.detect_pi: self.detectPiInteractions() if self.results['pi_cation']: self.piResults = self.get_pi_cation_result(print_ctr=1, comment=comment) if self.verbose: print("found pi_cation in ", ligand_file) print("set self.piResults to ", self.piResults) if self.results['pi_pi']: self.piResults += self.get_pi_pi(print_ctr=1, comment=comment) if self.verbose: print("set self.piResults to ", self.piResults) macro_cclist = macrocloseContactStr.split(';') lig_cclist = ligcloseContactStr.split(';') if has_hbonds or len(macro_cclist): fptr = open(ligand_file) lines = fptr.readlines() fptr.close() if outputfilename is not None: optr = open(outputfilename, 'w') else: optr = open(ligand_file, 'w') for new_l in hblist: if not(len(new_l)): #don't output empty lines continue if new_l.find(comment)!=0: new_l = comment + new_l if new_l != hblist[-1]: optr.write(new_l+"\n") else: optr.write(new_l) # no newline after the last one for new_l in macro_cclist: if not(len(new_l)): #don't output empty lines continue if new_l.find(comment)!=0: new_l = comment + new_l if new_l != macro_cclist[-1]: optr.write(new_l + "\n") else: optr.write(new_l) for new_l in lig_cclist: if not(len(new_l)): #don't output empty lines continue if new_l.find(comment)!=0: new_l = comment + new_l if new_l != lig_cclist[-1]: optr.write(new_l + "\n") else: optr.write(new_l) if len(self.piResults): ##??? for s in self.piResults.split("\n"): optr.write(s+"\n") for l in lines: optr.write(l) optr.close() return hbondStr + macrocloseContactStr + ligcloseContactStr def getResultStr(self, verbose=False): #only write important ones: hbonds, vdw_contacts, ?pi_pi,pi_cation etc? ss = "" for k in self.report_list: v = self.results[k] #key:atom1.full_name(); if len(v): if verbose: print("gRS: report for ", k) if verbose: print("USER: "******":" + str(len(v))) if verbose: print(" start ss= ", ss) ss += k + ":" + str(len(v)) + "\n" if k=='lig_hb_atoms': if verbose: print("@@ getResultStr lig_hb_atoms") hbstr = "" for lig_at in v: for hb in lig_at.hbonds: if hasattr(hb, 'hAt'): hbstr += 'USER %s-%s~%s\n'%(hb.donAt.full_name(), hb.hAt.full_name(),hb.accAt.full_name()) else: hbstr += 'USER %s~%s\n'%(hb.donAt.full_name(), hb.accAt.full_name()) if verbose: print("hbstr=") #add it to ss here ss += hbstr if verbose: print("with hbstr: ss=", ss) #if self.verbose: elif k == 'pi_cation': for res in v: #v=[(<Atom instance> HSG1:A:ARG8:CZ, [<Atom instance> IND: : INI 20:C1])] #res=(<Atom instance> HSG1:A:ARG8:CZ, [<Atom instance> IND: : INI 20:C1]) #res[0]=<Atom instance> HSG1:A:ARG8:CZ #res[1]= [<Atom instance> IND: : INI 20:C1] #res[1][0]= <Atom instance> IND: : INI 20:C1 # ss += "USER %s~~%s\n"%(res[0].full_name(), res[1][0].full_name()) else: for w in v: if verbose: print("@@ getResultStr w=", w) try: ss += 'USER ' + w.full_name()+'\n;' except: if verbose: print("except on ", w) ss += "USER " + str(w) ss += "\n" if verbose: print(" end ss= ", ss) return ss def buildHydrogenBonds(self, ligand, comment="USER AD> "): h_pairDict = self.hydrogen_bond_builder.build(ligand.allAtoms, self.macro_atoms) self.h_pairDict = h_pairDict #keys should be from lig, values from macro #sometimes are not...@@check this@@ h_results = {} for k, v in list(h_pairDict.items()): h_results[k] = 1 for at in v: h_results[at] = 1 all_hb_ats = AtomSet(list(h_results.keys())) #all d = self.results macro_hb_ats = d['macro_hb_atoms'] = all_hb_ats.get(lambda x: x.top==self.macro) self.macro_hb_ats = macro_hb_ats # process lig lig_hb_ats = d['lig_hb_atoms'] = all_hb_ats.get(lambda x: x in ligand.allAtoms) self.lig_hb_ats = lig_hb_ats outS = comment + "lig_hb_atoms : %d\n"%(len(lig_hb_ats)) for p in self.lig_hb_ats: #intD.results['lig_hb_atoms']: for hb in p.hbonds: if hasattr(hb, 'used'): continue if hb.hAt is not None: outS += comment + "%s,%s~%s\n"%(hb.donAt.full_name(), hb.hAt.name, hb.accAt.full_name()) else: outS += comment + "%s~%s\n"%(hb.donAt.full_name(), hb.accAt.full_name()) hb.used = 1 #hsg1V:B:ARG8:NH2,HH22~clean: : INI 20:N5 #clean: : INI 20:O4,H3~hsg1V:B:ASP29:OD2 #clean: : INI 20:O4,H3~hsg1V:B:ASP29:OD2 #clean: : INI 20:N4,H3~hsg1V:B:GLY27:O #clean: : INI 20:O2,H2~hsg1V:B:ASP25:OD1 #macroHStr = self.macro.allAtoms.get(lambda x: hasattr(x, 'hbonds') and len(x.hbonds)).full_name() #ligHStr = ligand.allAtoms.get(lambda x: hasattr(x, 'hbonds') and len(x.hbonds)).full_name() #return macroHStr + '==' + ligHStr if self.verbose: print("buildHB returning:") print(outS) return outS def buildCloseContactAtoms(self, percentCutoff, ligand, comment="USER AD> "): pairDict = self.distanceSelector.select(ligand.allAtoms, self.macro_atoms, percentCutoff=percentCutoff) self.pairDict = pairDict #reset here lig_close_ats = AtomSet() macro_close_ats = AtomSet() cdict = {} for k,v in list(pairDict.items()): if len(v): cdict[k] = 1 for at in v: if at not in macro_close_ats: cdict[at] = 1 closeAtoms = AtomSet(list(cdict.keys())) lig_close_ats = closeAtoms.get(lambda x: x.top==ligand).uniq() #ligClAtStr = lig_close_ats.full_name() ligClAtStr = comment + "lig_close_ats: %d\n" %( len(lig_close_ats)) if len(lig_close_ats): ligClAtStr += comment + "%s\n" %( lig_close_ats.full_name()) macro_close_ats = closeAtoms.get(lambda x: x in self.macro_atoms).uniq() macroClAtStr = comment + "macro_close_ats: %d\n" %( len(macro_close_ats)) if len(macro_close_ats): macroClAtStr += comment + "%s\n" %( macro_close_ats.full_name()) #macroClAtStr = "macro_close_ats: " + len(macro_close_ats)+"\n" +macro_close_ats.full_name() rdict = self.results rdict['lig_close_atoms'] = lig_close_ats rdict['macro_close_atoms'] = macro_close_ats if self.verbose: print("macroClAtStr=", macroClAtStr) if self.verbose: print("ligClAtStr=", ligClAtStr) if self.verbose: print("returning "+ macroClAtStr + '==' + ligClAtStr) return macroClAtStr , ligClAtStr def getCations(self, atoms): #select atoms in ARG and LYS residues arg_cations = atoms.get(lambda x: (x.parent.type=='ARG' and \ x.name in ['CZ'])) lys_cations = atoms.get(lambda x: (x.parent.type=='LYS' and \ x.name in ['NZ', 'HZ1', 'HZ2', 'HZ3'])) #select any positively-charged metal ions... cannot include CA here metal_cations = atoms.get(lambda x: x.name in ['Mn','MN', 'Mg',\ 'MG', 'FE', 'Fe', 'Zn', 'ZN']) ca_cations = atoms.get(lambda x: x.name in ['CA', 'Ca'] and x.parent.type=='CA') cations = AtomSet() #cations.extend(arg_cations) for a in arg_cations: cations.append(a) #cations.extend(lys_cations) for a in lys_cations: cations.append(a) #cations.extend(metal_cations) for a in metal_cations: cations.append(a) #cations.extend(ca_cations) for a in ca_cations: cations.append(a) return cations def detectPiInteractions(self, tolerance=0.95, debug=False, use_all_cycles=False): if debug: print("in detectPiInteractions") self.results['pi_pi'] = [] #stacked rings...? self.results['t_shaped'] = [] #one ring perpendicular to the other self.results['cation_pi'] = [] # self.results['pi_cation'] = [] # self.results['macro_cations'] = []# self.results['lig_cations'] = [] # #at this point have self.results if not len(self.results['lig_close_atoms']): return lig_atoms = self.results['lig_close_atoms'].parent.uniq().atoms macro_res = self.results['macro_close_atoms'].parent.uniq() if not len(macro_res): return macro_atoms = macro_res.atoms l_rf = RingFinder() #Ligand l_rf.findRings2(lig_atoms, lig_atoms.bonds[0]) #rf.rings is list of dictionaries, one per ring, with keys 'bonds' and 'atoms' if debug: print("LIG: len(l_rf.rings)=", len(l_rf.rings)) if not len(l_rf.rings): if debug: print("no lig rings found by l_rf!") return acbs = self.aromatic_cycle_bond_selector #acbs = AromaticCycleBondSelector() lig_rings = [] for r in l_rf.rings: ring_bnds = r['bonds'] if use_all_cycles: lig_rings.append(ring_bnds) else: arom_bnds = acbs.select(ring_bnds) if len(arom_bnds)>4: lig_rings.append(arom_bnds) if debug: print("LIG: len(lig_arom_rings)=", len(lig_rings)) self.results['lig_rings'] = lig_rings self.results['lig_ring_atoms'] = AtomSet() #only check for pi-cation if lig_rings exist if len(lig_rings): macro_cations = self.results['macro_cations'] = self.getCations(macro_atoms) macro_cations = macro_cations.get(lambda x: x.element!='H') lig_ring_atoms = AtomSet() u = {} for r in lig_rings: for a in BondSet(r).getAtoms(): u[a] = 1 if len(u): lig_ring_atoms = AtomSet(list(u.keys())) lig_ring_atoms.sort() self.results['lig_ring_atoms'] = lig_ring_atoms if len(macro_cations): if debug: print("check distances from lig_rings to macro_cations here") #macro cations->lig rings pairDict2 = self.distanceSelector.select(lig_ring_atoms,macro_cations) z = {} for key,v in list(pairDict2.items()): val = v.tolist()[0] if val in macro_cations: z[val] = [key] if len(z): self.results['pi_cation'] = (list(z.items())) else: self.results['pi_cation'] = [] #check the distance between the rings and the macro_cations self.results['lig_cations'] = self.getCations(lig_atoms) lig_cations = self.results['lig_cations'] #remove hydrogens lig_cations = lig_cations.get(lambda x: x.element!='H') #Macromolecule m_rf = RingFinder() m_rf.findRings2(macro_res.atoms, macro_res.atoms.bonds[0]) #rf.rings is list of dictionaries, one per ring, with keys 'bonds' and 'atoms' if debug: print("MACRO: len(m_rf.rings)=", len(m_rf.rings)) if not len(m_rf.rings): if debug: print("no macro rings found by m_rf!") return macro_rings = [] for r in m_rf.rings: ring_bnds = r['bonds'] if use_all_cycles: macro_rings.append(ring_bnds) else: arom_bnds = acbs.select(ring_bnds) if len(arom_bnds)>4: macro_rings.append(arom_bnds) if debug: print("len(macro_arom_rings)=", len(macro_rings)) self.results['macro_rings'] = macro_rings self.results['macro_ring_atoms'] = AtomSet() #only check for pi-cation if macro_rings exist if len(macro_rings): macro_ring_atoms = AtomSet() u = {} for r in macro_rings: for a in BondSet(r).getAtoms(): #new method of bondSets u[a] = 1 if len(u): macro_ring_atoms = AtomSet(list(u.keys())) macro_ring_atoms.sort() self.results['macro_ring_atoms'] = macro_ring_atoms if len(lig_cations): if debug: print("check distances from macro_rings to lig_cations here") pairDict3 = self.distanceSelector.select(macro_ring_atoms,lig_cations) z = {} for x in list(pairDict3.items()): #lig cations->macro rings z.setdefault(x[1].tolist()[0], []).append(x[0]) if len(z): self.results['cation_pi'] = (list(z.items())) else: self.results['cation_pi'] = [] #macro_pi_atoms = AtomSet(pairDict3.keys()) #l_cations = AtomSet() #for v in pairDict3.values(): # for x in v: # l_cations.append(x) #self.results['cation_pi'] = pairDict3.items() #self.results['cation_pi'] = (l_cations, macro_pi_atoms) #check for intermol distance <6 Angstrom (J.ComputChem 29:275-279, 2009) #compare each lig_ring vs each macro_ring for lig_ring_bnds in lig_rings: lig_atoms = acbs.getAtoms(lig_ring_bnds) lig_atoms.sort() if debug: print("len(lig_atoms)=", len(lig_atoms)) #--------------------------------- # compute the normal to lig ring #--------------------------------- a1 = numpy.array(lig_atoms[0].coords) a2 = numpy.array(lig_atoms[2].coords) a3 = numpy.array(lig_atoms[4].coords) if debug: print("a1,a2, a3=", a1.tolist(), a2.tolist(), a3.tolist()) for macro_ring_bnds in macro_rings: macro_atoms = acbs.getAtoms(macro_ring_bnds) macro_atoms.sort() if debug: print("len(macro_atoms)=", len(macro_atoms)) pD_dist = self.distanceSelectorWithCutoff.select(macro_ring_atoms, lig_atoms, cutoff=self.dist_cutoff) if not len(pD_dist[0]): if debug: print("skipping ligand ring ", lig_rings.index(lig_ring_bnds), " vs ", end=' ') print("macro ring", macro_rings.index(macro_ring_bnds)) continue #--------------------------------- # compute the normal to macro ring #--------------------------------- b1 = numpy.array(macro_atoms[0].coords) b2 = numpy.array(macro_atoms[2].coords) b3 = numpy.array(macro_atoms[4].coords) if debug: print("b1,b2, b3=", b1.tolist(), b2.tolist(), b3.tolist()) # check for stacking a2_1 = a2-a1 a3_1 = a3-a1 b2_1 = b2-b1 b3_1 = b3-b1 if debug: print("a2_1 = ", a2-a1) if debug: print("a3_1 = ", a3-a1) if debug: print("b2_1 = ", b2-b1) if debug: print("b3_1 = ", b3-b1) n1 = crossProduct(a3_1,a2_1) #to get the normal for the first ring n2 = crossProduct(b3_1,b2_1) #to get the normal for the second ring if debug: print("n1=", n1) if debug: print("n2=", n2) n1 = numpy.array(n1) n2 = numpy.array(n2) n1_dot_n2 = numpy.dot(n1,n2) if debug: print("n1_dot_n2", numpy.dot(n1,n2)) if abs(n1_dot_n2) >= 1*tolerance: if debug: print("The rings are stacked vertically") new_result = (acbs.getAtoms(lig_ring_bnds), acbs.getAtoms(macro_ring_bnds)) self.results['pi_pi'].append(new_result) if abs(n1_dot_n2) <= 0.01*tolerance: if debug: print("The rings are stacked perpendicularly") new_result = (acbs.getAtoms(lig_ring_bnds), acbs.getAtoms(macro_ring_bnds)) self.results['t_shaped'].append(new_result) def get_pi_pi(self, print_ctr=1, comment="USER AD> "): #one ring parallel to another #@@ UNTESTED! ... need test data if not len(self.results.get( 'pi_pi')): return "" # self.results['pi_pi'] = [] #stacked rings...? res = self.results['pi_pi'] ss = comment + "pi_pi: %d\n"%(len(self.results['pi_pi'])) #### #3l1m_lig_vs:A:HEM150:C4A,C3D,C1A,C2D,C4D,C2A,NA,ND,C3A,C1D : 3l1m_rec:A:PHE65:CD2,CD1,CG,CZ,CE1,CE2 #3l1m_lig_vs:A:HEM150:C3B,NB,C4A,C1A,C4B,NA,C2A,C2B,C3A,C1B : 3l1m_rec:A:PHE65:CD2,CD1,CG,CZ,CE1,CE2 #3l1m_lig_vs:A:HEM150:C3B,C1B,C3C,NB,C4C,NC,C2B,C1C,C4B,C2C : 3l1m_rec:A:PHE65:CD2,CD1,CG,CZ,CE1,CE2 #3l1m_lig_vs:A:HEM150:NC,C4C,C1C,C3C,C2C : 3l1m_rec:A:PHE65:CD2,CD1,CG,CZ,CE1,CE2 #3l1m_lig_vs:A:HEM150:C1A,NA,C2A,C3A,C4A : 3l1m_rec:A:PHE65:CD2,CD1,CG,CZ,CE1,CE2 #3l1m_lig_vs:A:HEM150:NB,C2B,C3B,C1B,C4B : 3l1m_rec:A:PHE65:CD2,CD1,CG,CZ,CE1,CE2 #3l1m_lig_vs:A:HEM150:NC,C4C,C1C,C3C,C2C : 3l1m_rec:A:PHE65:CD2,CD1,CG,CZ,CE1,CE2 #3l1m_lig_vs:A:HEM150:C4D,ND,C1D,C3D,C2D : 3l1m_rec:A:PHE65:CD2,CD1,CG,CZ,CE1,CE2 # # build string for self.results['pi_pi'] for res in self.results['pi_pi']: ss += comment + "%s~~%s\n"%(res[0].full_name(), res[1].full_name()) ###ss += "USER %s~~%s\n"%(res[0].full_name(), res[1].full_name()) #ss += "USER %s~~%s\n"%(res[0].full_name(), res[1][0].full_name()) #print "returning ss=" , ss return ss def get_t_shaped(self, print_ctr=1, comment="USER AD> "): #one ring perpendicular to the other #@@ UNTESTED! ... need test data if not len(self.results.get( 't_shaped')): return "" #print "in gpcr: self.results[t_shaped]=", self.results['t_shaped'] ss = comment + "t_shaped: %d\n"%(len(self.results['t_shaped'])) # build string for self.results['t_shaped'] for res in self.results['t_shaped']: #for example: #???? #???? #???? # ss += comment + "%s~~%s\n"%(res[0].full_name(), res[1][0].full_name()) return ss def get_cation_pi(self, print_ctr=1, comment="USER AD> "): #cation near aromatic ring #@@ UNTESTED! ... need test data if not len(self.results.get( 'cation_pi')): return "" ss = comment + "cation_pi: %d\n"%(len(self.results['cation_pi'])) # build string for self.results['cation_pi'] for res in self.results['cation_pi']: #for example: #???? #???? #???? # ss += comment + "%s~~%s\n"%(res[0].full_name(), res[1][0].full_name()) return ss def get_pi_cation_result(self, print_ctr=1, comment="USER AD> "): #aromatic ring near cation #print "in gpcr: self.results[pi_cation]=", self.results['pi_cation'] if not len(self.results.get( 'pi_cation')): return "" ss = comment + "pi_cation: %d\n"%(len(self.results['pi_cation'])) # build string for self.results['pi_cation'] for res in self.results['pi_cation']: #for example: #v=[(<Atom instance> HSG1:A:ARG8:CZ, [<Atom instance> IND: : INI 20:C1])] #res=(<Atom instance> HSG1:A:ARG8:CZ, [<Atom instance> IND: : INI 20:C1]) #res[0]=<Atom instance> HSG1:A:ARG8:CZ #res[1]= [<Atom instance> IND: : INI 20:C1] #res[1][0]= <Atom instance> IND: : INI 20:C1 # #attempt to get names for all atoms in the whole ring: res1_str = res[1][0].full_name() for rr in self.results['lig_rings']: ats = rr.getAtoms() if res[1][0] in ats: res1_str = ats.full_name() break ss += comment + "%s~~%s\n"%(res[0].full_name(), res1_str) #ss += "USER %s~~%s\n"%(res[0].full_name(), res[1][0].full_name()) return ss def print_ligand_residue_contacts(self, print_ctr=1): ctr = 1 #pairDict is atom-based #for residue-based report: # need to build lists of unique parents of keys # and lists of unique parents of corresponding values res_d = {} for at in list(self.pairDict.keys()): if at.parent not in list(res_d.keys()): res_d[at.parent] = {} for close_at in self.pairDict[at]: res_d[at.parent][close_at.parent] = 1 #print it out for lig_res in list(res_d.keys()): if print_ctr: print(ctr, lig_res.parent.name+':'+ lig_res.name + '->', end=' ') else: print(lig_res.parent.name+':'+ lig_res.name + '->', end=' ') for macro_res in res_d[lig_res]: print(macro_res.parent.name + ':' + macro_res.name + ',', end=' ') print() ctr += 1 return res_d def print_macro_residue_contacts(self, print_ctr=1): ctr = 1 #pairDict is atom-based #for residue-based report: # need to build lists of unique parents of keys # and lists of unique parents of corresponding values res_d = {} for at_key, at_list in list(self.pairDict.items()): for at in at_list: if at.parent not in list(res_d.keys()): res_d[at.parent] = {} res_d[at.parent][at_key.parent] = 1 #print it out for macro_res in list(res_d.keys()): if print_ctr: print(ctr, macro_res.parent.name+':'+ macro_res.name + '->', end=' ') else: print(macro_res.parent.name+':'+ macro_res.name + '->', end=' ') for lig_res in res_d[macro_res]: print(lig_res.parent.name + ':' + lig_res.name + ',', end=' ') print() ctr += 1 return res_d def print_report(self, keylist=[]): if not len(keylist): keylist = [ 'lig_close_atoms', 'lig_hb_atoms', 'lig_hbas', 'macro_close_atoms', 'macro_hb_atoms', ] d = self.results if self.verbose: for k in keylist: print(k, ':', len(d[k]), '-', d[k].__class__) def print_hb_residue(self, print_ctr=1): ctr = 1 #pairDict is atom-based res_d = {} for at in list(self.h_pairDict.keys()): if at.parent not in list(res_d.keys()): res_d[at.parent] = {} for close_at in self.h_pairDict[at]: res_d[at.parent][close_at.parent] = 1 # print it out # Hbond instance are define with donAt,hAt ~ accAtt (tilde) for don_res in list(res_d.keys()): if print_ctr: print(ctr, don_res.top.name+':'+don_res.parent.name+':'+ don_res.name + '->', end=' ') else: print(don_res.top.name+':'+don_res.parent.name+':'+ don_res.name + '->', end=' ') for acc_res in res_d[don_res]: print(acc_res.top.name+':'+acc_res.parent.name + ':' + acc_res.name + ',', end=' ') print() ctr += 1 return res_d
class HydrogenBondBuilder: """ object which can build hydrogen bonds between atoms according to their coords and atom type """ def __init__(self, distCutoff=distCutoff, distCutoff2=distCutoff2, d2min=d2min, d2max=d2max, d3min=d3min, d3max=d3max, a2min=a2min, a2max=a2max, a3min=a3min, a3max=a3max, donorTypes=allDonors, acceptorTypes=allAcceptors, distOnly=False): d = self.paramDict = {} d['distCutoff'] = distCutoff d['distCutoff2'] = distCutoff2 d['d2min'] = d2min d['d2max'] = d2max d['d3min'] = d3min d['d3max'] = d3max d['a2min'] = a2min d['a2max'] = a2max d['a3min'] = a3min d['a3max'] = a3max d['donorTypes'] = donorTypes d['acceptorTypes'] = acceptorTypes d['distOnly'] = distOnly self.distSelector = DistanceSelector(return_dist=0) def check_babel_types(self, ats): num_ats = len(ats) num_babel_type = len(ats.get(lambda x: hasattr(x, 'babel_type'))) num_bnd_type = len(ats.get(lambda x: hasattr(x, 'bnd_type'))) if (num_babel_type!=num_ats) or (num_bnd_type!=num_ats): babel = AtomHybridization() bond_orderer = BondOrder() tops = ats.top.uniq() for mol in tops: babel.assignHybridization(mol.allAtoms) bond_orderer.assignBondOrder(mol.allAtoms, mol.allAtoms.bonds[0]) mol.allAtoms._bndtyped = 1 def reset(self, ats): tops = ats.top.uniq() for mol in tops: for a in mol.allAtoms: if hasattr(a, 'hbonds'): for item in a.hbonds: del item delattr(a, 'hbonds') def build(self, group1, group2=None, reset=True, paramDict=None): """atDict <- build(group1, group2, reset, paramDict=None, **kw): group1: atoms group2: atoms reset: remove all previous hbonds, default True! paramDict: a dictionary with these keys and default values distCutoff: 2.25 hydrogen--acceptor distance distCutoff2: 3.00 donor... acceptor distance d2min: 120 <min theta for sp2 hybridized donors> d2max: 180 <max theta for sp2 hybridized donors> d3min: 120 <min theta for sp3 hybridized donors> d3max: 170 <max theta for sp3 hybridized donors> a2min: 120 <min phi for sp2 hybridized donors> a2max: 150 <max phi for sp2 hybridized donors> a3min: 100 <min phi for sp3 hybridized donors> a3max: 150 <max phi for sp3 hybridized donors> @@FIX THIS: these do not seem to be input here donorTypes = allDonors acceptorTypes = allAcceptors """ #setup parameter dictionary if paramDict is None: paramDict = self.paramDict #setup group2 if group2 is None: group2 = group1 #process each group #group1 if group1.__class__!=Atom: group1 = group1.findType(Atom) #print "now group1=", group1.full_name() if not len(group1): return "ERROR" #@@ OR WHAT? self.check_babel_types(group1) #group2 if group2.__class__!=Atom: group2 = group2.findType(Atom) #print "now group2=", group2.full_name() if not len(group2): return "ERROR" #@@ OR WHAT? self.check_babel_types(group2) if reset: #do optional reset: remove all prior hbonds self.reset(group1) self.reset(group2) #print "group1=", len(group1) #print "group2=", len(group2) #buildHbonds atDict = {} dict1 = self.buildD(group1, paramDict) #@@what is this doing here??? #sp2 hybridized atoms #dAts2 = ats.get(lambda x, l=sp2: x.babel_type in l) if group1==group2: #only one step: atD1 = self.process(dict1, dict1, paramDict) #print 'len(atD1).keys()=', len(atD1.keys()) atD2 = {} else: # two steps: # 1: group1 donors v group2 acceptors # 2: group1 acceptors vs group2 donors dict2 = self.buildD(group2, paramDict) atD1 = self.process(dict1, dict2, paramDict) atD2 = self.process(dict2, dict1, paramDict) #print 'len(atD1).keys()=', len(atD1.keys()) #print 'len(atD2).keys()=', len(atD2.keys()) #if called with 1 atom could get tuple of two empty dictionaries if type(atD1)==type(atDict): if len(atD1): atDict.update(atD1) if type(atD2)==type(atDict): if len(atD2): atDict.update(atD2) #@@DESCRIBE atDict #this dict has atomSets as keys and as values(?check that) return atDict def checkForPossibleH(self, ats, blen): #@@FIX THIS: WHAT IS THE POINT OF THIS??? #check that if at has all bonds, at least one is to a hydrogen # have to do this by element?? probAts = AtomSet(ats.get(lambda x, blen=blen: len(x.bonds)==blen)) #probOAts = ats.get(lambda x, blen=blen: len(x.bonds)==blen) #probSAts = ats.get(lambda x, blen=blen: len(x.bonds)==blen) if probAts: rAts = AtomSet([]) for at in probAts: if not len(at.findHydrogens()): rAts.append(at) if len(rAts): ats = ats.subtract(rAts) return ats def getHBDonors(self, ats, donorList): #getHBDonors sp2 = [] sp3 = [] for item in sp2Donors: if item in donorList: sp2.append(item) for item in sp3Donors: if item in donorList: sp3.append(item) dAts2 = ats.get(lambda x, l=sp2: x.babel_type in l) if not dAts2: dAts2=AtomSet([]) else: dAts2 = self.checkForPossibleH(dAts2, 3) dAts3 = ats.get(lambda x, l=sp3: x.babel_type in l) if not dAts3: dAts3=AtomSet([]) else: dAts3 = self.checkForPossibleH(dAts3, 4) return dAts2, dAts3 def filterAcceptors(self, accAts): ntypes = ['Npl', 'Nam'] npls = accAts.get(lambda x, ntypes=ntypes: x.babel_type=='Npl') nams = accAts.get(lambda x, ntypes=ntypes: x.babel_type=='Nam') #nAts = accAts.get(lambda x, ntypes=ntypes: x.babel_type in ntypes) restAts = accAts.get(lambda x, ntypes=ntypes: x.babel_type not in ntypes) if not restAts: restAts = AtomSet([]) #if nAts: if npls: #for at in nAts: for at in npls: s = 0 for b in at.bonds: if b.bondOrder=='aromatic': s = s + 2 else: s = s + b.bondOrder #if s<3: #apparently this is wrong if s<4: restAts.append(at) if nams: #for at in nAts: for at in nams: s = 0 for b in at.bonds: if b.bondOrder=='aromatic': s = s + 2 else: s = s + b.bondOrder #s = s + b.bondOrder if s<3: restAts.append(at) return restAts def getHBAcceptors(self, ats, acceptorList): #print "getHBAcceptors: acceptorList=", acceptorList #getHBAcceptors sp2 = [] sp3 = [] for item in sp2Acceptors: if item in acceptorList: sp2.append(item) for item in sp3Acceptors: if item in acceptorList: sp3.append(item) dAts2 = AtomSet(ats.get(lambda x, l=sp2: x.babel_type in l)) if dAts2: dAts2 = self.filterAcceptors(dAts2) dAts3 = AtomSet(ats.get(lambda x, l=sp3: x.babel_type in l)) return dAts2, dAts3 def buildD(self, ats, paramDict=None): if paramDict is None: paramDict = self.paramDict #these are from the __call__ method of vf.buildHBonds if not paramDict.has_key('distCutoff'): paramDict['distCutoff'] = 2.25 if not paramDict.has_key('distCutoff2'): paramDict['distCutoff2'] = 3.00 if not paramDict.has_key('d2min'): paramDict['d2min'] = 120. if not paramDict.has_key('d2max'): paramDict['d2max'] = 180. if not paramDict.has_key('d3min'): paramDict['d3min'] = 120. if not paramDict.has_key('d3max'): paramDict['d3max'] = 170. if not paramDict.has_key('a2min'): paramDict['a2min'] = 130. if not paramDict.has_key('a2max'): paramDict['a2max'] = 170. if not paramDict.has_key('a3min'): paramDict['a3min'] = 120. if not paramDict.has_key('a3max'): paramDict['a3max'] = 170. if not paramDict.has_key('distOnly'): paramDict['distOnly'] = 0 if not paramDict.has_key('donorTypes'): paramDict['donorTypes'] = allDonors if not paramDict.has_key('acceptorTypes'): paramDict['acceptorTypes'] = allAcceptors d = {} donorTypes = paramDict['donorTypes'] donor2Ats, donor3Ats = self.getHBDonors(ats, donorTypes) d23 = donor2Ats + donor3Ats #hAts = ats.get(lambda x, d23=d23: x.element=='H' \ #and x.bonds[0].neighborAtom(x) in d23) hydrogen_atoms = ats.get(lambda x: x.element=='H' and len(x.bonds)) #hAts = AtomSet(ats.get(lambda x, donorTypes=donorTypes: x.element=='H' \ hAts = AtomSet(hydrogen_atoms.get(lambda x, donorTypes=donorTypes: x.bonds[0].atom1.babel_type in donorTypes\ or x.bonds[0].atom2.babel_type in donorTypes)) d['hAts'] = hAts d['donor2Ats'] = donor2Ats d['donor3Ats'] = donor3Ats acceptorTypes = paramDict['acceptorTypes'] #print "about to call getHBAcceptors with acceptorTypes=", acceptorTypes acceptor2Ats, acceptor3Ats = self.getHBAcceptors(ats, acceptorTypes) d['acceptor2Ats'] = acceptor2Ats d['acceptor3Ats'] = acceptor3Ats if acceptor2Ats: acceptorAts = acceptor2Ats if acceptor3Ats: acceptorAts = acceptorAts + acceptor3Ats elif acceptor3Ats: acceptorAts = acceptor3Ats else: #CHECK THIS: should it be None or AtomSet([]) acceptorAts = None d['acceptorAts'] = acceptorAts return d def getMat(self, ats): pass #tops = ats.top.uniq() #if len(tops)>1: # self.warningMsg('transformation mat=None:>1 mol in atomset!') # return None #g = tops[0].geomContainer.geoms['master'] #return g.GetMatrix(g) def process(self, dict1, dict2, paramDict): #hAts are keys, aceptorAts are checks hAts = dict1['hAts'] tAts = hAts dist = paramDict['distCutoff'] distOnly = paramDict['distOnly'] if not hAts: #then use donors and a different distance tAts = dict1['donor2Ats'] + dict1['donor3Ats'] dist = paramDict['distCutoff2'] acceptorAts = dict2['acceptorAts'] #print "acceptorAts=", acceptorAts if not acceptorAts or not tAts: #6/14/2004 return {}, {} #call distanceSelector on two groups of atoms with dist #keyMat = self.getMat(tAts) #checkMat = self.getMat(acceptorAts) atDict = self.distSelector.select(tAts, acceptorAts, dist) # keyMat=keyMat, checkMat=checkMat) #atDict = self.distSelector.select(tAts, acceptorAts, dist) #first remove bonded angles atDict = self.removeNeighbors(atDict) donor2Ats = dict1['donor2Ats'] donor3Ats = dict1['donor3Ats'] acceptor2Ats = dict2['acceptor2Ats'] acceptor3Ats = dict2['acceptor3Ats'] if distOnly: #need to build hbonds and return dictionary self.makeBonds(atDict, donor2Ats, donor3Ats, \ acceptor2Ats, acceptor3Ats, paramDict) return atDict badAtDict = self.filterBasedOnAngs(atDict, donor2Ats, donor3Ats, \ acceptor2Ats, acceptor3Ats, paramDict) atDict = self.removeBadAts(atDict, badAtDict) if atDict is None: atDict = {} return atDict def makeBonds(self, pD, d2Ats, d3Ats, a2Ats, a3ats, paramDict): for k in pD.keys(): if k.element=='H': if hasattr(k, 'hbonds') and len(k.hbonds): continue d = k.bonds[0].atom1 if id(d)==id(k): d = k.bonds[0].atom2 #d = k.bonds[0].neighborAtom(k) h = k else: d = k h = None #pD[k] is a list of close-enough ats for ac in pD[k]: if ac==d: continue dSp2 = d in d2Ats aSp2 = ac in a2Ats if dSp2: if aSp2: typ = 22 else: typ = 23 elif aSp2: typ = 32 else: typ = 33 #THEY could be already bonded alreadyBonded = 0 if hasattr(d, 'hbonds') and hasattr(ac,'hbonds'): for hb in d.hbonds: if hb.donAt==ac or hb.accAt==ac: alreadyBonded = 1 if not alreadyBonded: newHB = HydrogenBond(d, ac, h, typ=typ) if not hasattr(ac, 'hbonds'): ac.hbonds=[] if not hasattr(d, 'hbonds'): d.hbonds=[] ac.hbonds.append(newHB) d.hbonds.append(newHB) if h is not None: #hydrogens can have only 1 hbond h.hbonds = [newHB] def filterBasedOnAngs(self, pD, d2Ats, d3Ats, a2Ats, a3ats, paramDict): badAtDict = {} d2max = paramDict['d2max'] d2min = paramDict['d2min'] d3max = paramDict['d3max'] d3min = paramDict['d3min'] #NEED these parameters a2max = paramDict['a2max'] a2min = paramDict['a2min'] a3max = paramDict['a3max'] a3min = paramDict['a3min'] #NB now pD keys could be hydrogens OR donors for k in pD.keys(): if k.element=='H': d = k.bonds[0].atom1 if id(d)==id(k): d = k.bonds[0].atom2 #d = k.bonds[0].neighborAtom(k) h = k else: d = k h = None badAts = AtomSet([]) ct = 0 for ac in pD[k]: if h is not None: ang = getAngle(ac, h, d) else: acN = ac.bonds[0].atom1 if id(acN) == id(ac): acN = ac.bonds[0].atom2 #acN = ac.bonds[0].neighborAtom(ac) ang = getAngle(d, ac, acN) #print 'ang=', ang dSp2 = d in d2Ats aSp2 = ac in a2Ats #these limits could be adjustable if h is not None: if dSp2: upperLim = d2max lowerLim = d2min #upperLim = 170 #lowerLim = 130 else: upperLim = d3max lowerLim = d3min #upperLim = 180 #lowerLim = 120 else: #if there is no hydrogen use d-ac-acN angles if dSp2: upperLim = a2max lowerLim = a2min #upperLim = 150 #lowerLim = 110 else: upperLim = a3max lowerLim = a3min #upperLim = 150 #lowerLim = 100 if ang>lowerLim and ang <upperLim: #AT THIS MOMENT BUILD HYDROGEN BOND: if dSp2: if aSp2: typ = 22 else: typ = 23 elif aSp2: typ = 32 else: typ = 33 #THEY could be already bonded alreadyBonded = 0 if hasattr(d, 'hbonds') and hasattr(ac,'hbonds'): for hb in d.hbonds: if hb.donAt==ac or hb.accAt==ac: alreadyBonded = 1 if not alreadyBonded: newHB = HydrogenBond(d, ac, h, theta=ang, typ=typ) if not hasattr(ac, 'hbonds'): ac.hbonds=[] if not hasattr(d, 'hbonds'): d.hbonds=[] ac.hbonds.append(newHB) d.hbonds.append(newHB) if h is not None: #hydrogens can have only 1 hbond h.hbonds = [newHB] # newHB.hlen = dist #else: # newHB.dlen = dist else: badAts.append(ac) ct = ct + 1 badAtDict[k] = badAts return badAtDict def removeBadAts(self, atDict, badAtDict): #clean-up function called after filtering on angles badKeys= badAtDict.keys() for at in atDict.keys(): if at not in badKeys: continue if not len(badAtDict[at]): continue closeAts = atDict[at] badAts = badAtDict[at] goodAts = [] for i in range(len(closeAts)): cAt = closeAts[i] if cAt not in badAts: goodAts.append(cAt) if len(goodAts): atDict[at] = goodAts else: del atDict[at] return atDict def removeNeighbors(self, atDict): #filter out at-itself and at-bondedat up to 1:4 #NB keys could be hydrogens OR donors for at in atDict.keys(): closeAts = atDict[at] bondedAts = AtomSet([]) for b in at.bonds: ###at2 = b.neighborAtom(at) at2 = b.atom1 if id(at2)==id(at): at2 = b.atom2 bondedAts.append(at2) #9/13 remove this: ##also remove 1-3 for b2 in at2.bonds: at3 = b2.atom1 if id(at3)==id(at2): at3 = b.atom2 #at3 = b2.neighborAtom(at2) if id(at3)!=id(at): bondedAts.append(at3) #for b3 in at3.bonds: #at4 = b2.neighborAtom(at3) #if at4!=at and at4!=at2: #bondedAts.append(at4) bondedAts = bondedAts.uniq() goodAts = [] for i in range(len(closeAts)): cAt = closeAts[i] if cAt not in bondedAts: goodAts.append(cAt) if len(goodAts): atDict[at] = goodAts else: del atDict[at] return atDict def getDonors(self, nodes, paramDict): donorList = paramDict['donorTypes'] #print 'donorList=', donorList # currently this is a set of hydrogens hats = AtomSet(nodes.get(lambda x: x.element=='H')) #hats are optional: if none, process donors # if there are hats: dAts are all atoms bonded to all hydrogens if hats: dAts = AtomSet([]) for at in hats: for b in at.bonds: at2 = b.atom1 if id(at2)==id(at): at2 = b.atom2 dAts.append(at2) #dAts.append(b.neighborAtom(at)) else: dAts = nodes #get the sp2 hybridized possible donors which are all ns sp2 = [] for t in ['Nam', 'Ng+', 'Npl']: if t in donorList: sp2.append(t) #ntypes = ['Nam', 'Ng+', 'Npl'] sp2DAts = None if len(sp2): sp2DAts = AtomSet(dAts.get(lambda x, sp2=sp2: x.babel_type in sp2)) hsp2 = AtomSet([]) if sp2DAts: if hats: hsp2 = AtomSet(hats.get(lambda x, sp2DAts=sp2DAts:x.bonds[0].atom1 \ in sp2DAts or x.bonds[0].atom2 in sp2DAts)) if sp2DAts: #remove any sp2 N atoms which already have 3 bonds not to hydrogens n2Dons = AtomSet(sp2DAts.get(lambda x: x.element=='N')) if n2Dons: n2Dons.bl=0 for at in n2Dons: for b in at.bonds: if type(b.bondOrder)==type(2): at.bl = at.bl + b.bondOrder else: at.bl = at.bl + 2 #allow that there might already be a hydrogen nH = at.findHydrogens() at.bl = at.bl - len(nH) badAts = AtomSet(n2Dons.get(lambda x: x.bl>2)) if badAts: sp2DAts = sp2DAts - badAts delattr(n2Dons,'bl') #get the sp3 hybridized possible donors sp3 = [] for t in ['N3+', 'S3', 'O3']: if t in donorList: sp3.append(t) n3DAts = None if 'N3+' in sp3: n3DAts = AtomSet(dAts.get(lambda x: x.babel_type=='N3+')) o3DAts = None if 'O3' in sp3: o3DAts = AtomSet(dAts.get(lambda x: x.babel_type=='O3')) if o3DAts: #remove any O3 atoms which already have 2 bonds not to hydrogens badO3s = AtomSet([]) for at in o3DAts: if len(at.bonds)<2: continue if len(at.findHydrogens()): continue else: badO3s.append(at) if len(badO3s): o3DAts = o3DAts - badO3s s3DAts = None if 'S3' in sp3: s3DAts = AtomSet(dAts.get(lambda x: x.babel_type=='S3')) sp3DAts = AtomSet([]) for item in [n3DAts, o3DAts, s3DAts]: if item: sp3DAts = sp3DAts + item hsp3 = AtomSet([]) if sp3DAts: if hats: hsp3 = AtomSet(hats.get(lambda x, sp3DAts=sp3DAts:x.bonds[0].atom1 \ in sp3DAts or x.bonds[0].atom2 in sp3DAts)) hsp = hsp2 + hsp3 #print 'hsp=', hsp.name #print 'sp2DAts=', sp2DAts.name #print 'sp3DAts=', sp3DAts.name return hsp, sp2DAts, sp3DAts def getAcceptors(self, nodes, paramDict): acceptorList = paramDict['acceptorTypes'] #print 'acceptorList=', acceptorList sp2 = [] for t in ['Npl', 'Nam']: if t in acceptorList: sp2.append(t) n2Accs = None if 'Npl' in sp2: n2Accs = AtomSet(nodes.get(lambda x: x.babel_type=='Npl')) if 'Nam' in sp2: n2Accs2 = AtomSet(nodes.get(lambda x: x.babel_type=='Nam')) if n2Accs2: if n2Accs: n2Accs = n2Accs+n2Accs2 else: n2Accs = n2Accs2 if n2Accs is None: n2Accs = AtomSet([]) o_sp2 = [] for t in ['O2', 'O-']: if t in acceptorList: sp2.append(t) o2Accs = None if 'O2' in o_sp2: o2Accs = AtomSet(nodes.get(lambda x: x.babel_type=='O2')) if 'O-' in sp2: o2Accs2 = AtomSet(nodes.get(lambda x: x.babel_type=='O-')) if o2Accs2: if o2Accs: o2Accs = o2Accs+o2Accs2 else: o2Accs = o2Accs2 if o2Accs is None: o2Accs = AtomSet([]) o3Accs = None if 'O3' in acceptorList: o3Accs = AtomSet(nodes.get(lambda x: x.babel_type=='O3')) if o3Accs is None: o3Accs = AtomSet([]) s3Accs = None if 'S3' in acceptorList: s3Accs = AtomSet(nodes.get(lambda x: x.babel_type=='S3')) if s3Accs is None: s3Accs = AtomSet([]) ret2Ats = AtomSet([]) for item in [n2Accs, o2Accs]: ret2Ats = ret2Ats + item ret3Ats = AtomSet([]) for item in [s3Accs, o3Accs]: ret3Ats = ret3Ats + item if ret2Ats: print 'ret2Ats=', ret2Ats.name else: print 'no ret2Ats' if ret3Ats: print 'ret3Ats=', ret3Ats.name else: print 'no ret3Ats' return ret2Ats, ret3Ats
class InteractionDetector: """ Base class for object to detect interactions between a receptor and potential ligands... such as virtual screen results initialized with the receptor file containing entire molecule or residues of interest. processLigand method takes as input a pdbqt file containing docked coordinates and returns a string composed of hbondStr + macrocloseContactStr + ligcloseContactStr If interections are found, a new pdbqt containing interaction description is also output: """ def __init__(self, receptor_file, percentCutoff=1., detect_pi=False, dist_cutoff=6, verbose=False, distanceSelector=None, hydrogen_bond_builder=None, distanceSelectorWithCutoff=None, aromatic_cycle_bond_selector=None): self.receptor_file = receptor_file receptor = Read(receptor_file) assert(len(receptor)==1) assert isinstance(receptor, MoleculeSet) self.macro = receptor[0] self.macro_atoms = self.macro.allAtoms self.macro.buildBondsByDistance() self.verbose = verbose #??useful?? self.percentCutoff = percentCutoff self.detect_pi = detect_pi self.distanceSelector = distanceSelector if self.distanceSelector is None: self.distanceSelector = CloserThanVDWSelector(return_dist=0) self.hydrogen_bond_builder = hydrogen_bond_builder if self.hydrogen_bond_builder is None: self.hydrogen_bond_builder = HydrogenBondBuilder() self.distanceSelectorWithCutoff = distanceSelectorWithCutoff if self.distanceSelectorWithCutoff is None: self.distanceSelectorWithCutoff = DistanceSelector() self.dist_cutoff=float(dist_cutoff) self.results = d = {} self.report_list =['lig_hb_atoms','lig_close_atoms'] self.aromatic_cycle_bond_selector = aromatic_cycle_bond_selector if self.aromatic_cycle_bond_selector is None: self.aromatic_cycle_bond_selector = AromaticCycleBondSelector() if detect_pi: self.report_list.extend(['pi_cation','pi_pi', 'cation_pi', 't_shaped']) if self.verbose: print "self.report_list=", self.report_list def processLigand(self, ligand_file, percentCutoff=None, output=1, outputfilename=None, comment="USER AD> ", buildHB=1, remove_modelStr=False): #if outputfilename is not None, have to (1) update all the strings or (2) rename ligand self.results = d = {} ligand = Read(ligand_file) assert isinstance(ligand, MoleculeSet) assert(len(ligand)==1) ligand = ligand[0] first = ligand.name.find('_model') last = ligand.name.rfind('_model') if first!=last: ligand.name = ligand.name[:last] if remove_modelStr: curName = ligand.name modelIndex=curName.find("_model") if modelIndex>-1: curName = curName[:modelIndex] #setup outputfilestem ligand.name = curName ligand.buildBondsByDistance() if not percentCutoff: percentCutoff = self.percentCutoff # first detect sets of atoms forming hydrogen bonds # hbondStr,hblist and has_hbonds added to process vina results which do not include valid hydrogen bonds hbondStr = "" hblist = [""] has_hbonds = False if buildHB: has_hbonds = True hbondStr = self.buildHydrogenBonds(ligand, comment=comment) # if self.verbose: print "hbondStr=", hbondStr hblist = hbondStr.split('\n') # hbond info if hblist[0]=='lig_hb_atoms : 0': has_hbonds = False # next detect sets of atoms in close contact not forming hydrogen bonds macrocloseContactStr, ligcloseContactStr = self.buildCloseContactAtoms(percentCutoff, ligand, comment=comment) # self.piResults = "" if self.detect_pi: self.detectPiInteractions() if self.results['pi_cation']: self.piResults = self.get_pi_cation_result(print_ctr=1, comment=comment) if self.verbose: print "found pi_cation in ", ligand_file print "set self.piResults to ", self.piResults if self.results['pi_pi']: self.piResults += self.get_pi_pi(print_ctr=1, comment=comment) if self.verbose: print "set self.piResults to ", self.piResults macro_cclist = macrocloseContactStr.split(';') lig_cclist = ligcloseContactStr.split(';') if has_hbonds or len(macro_cclist): fptr = open(ligand_file) lines = fptr.readlines() fptr.close() if outputfilename is not None: optr = open(outputfilename, 'w') else: optr = open(ligand_file, 'w') for new_l in hblist: if not(len(new_l)): #don't output empty lines continue if new_l.find(comment)!=0: new_l = comment + new_l if new_l != hblist[-1]: optr.write(new_l+"\n") else: optr.write(new_l) # no newline after the last one for new_l in macro_cclist: if not(len(new_l)): #don't output empty lines continue if new_l.find(comment)!=0: new_l = comment + new_l if new_l != macro_cclist[-1]: optr.write(new_l + "\n") else: optr.write(new_l) for new_l in lig_cclist: if not(len(new_l)): #don't output empty lines continue if new_l.find(comment)!=0: new_l = comment + new_l if new_l != lig_cclist[-1]: optr.write(new_l + "\n") else: optr.write(new_l) if len(self.piResults): ##??? for s in self.piResults.split("\n"): optr.write(s+"\n") for l in lines: optr.write(l) optr.close() return hbondStr + macrocloseContactStr + ligcloseContactStr def getResultStr(self, verbose=False): #only write important ones: hbonds, vdw_contacts, ?pi_pi,pi_cation etc? ss = "" for k in self.report_list: v = self.results[k] #key:atom1.full_name(); if len(v): if verbose: print "gRS: report for ", k if verbose: print "USER: "******":" + str(len(v)) if verbose: print " start ss= ", ss ss += k + ":" + str(len(v)) + "\n" if k=='lig_hb_atoms': if verbose: print "@@ getResultStr lig_hb_atoms" hbstr = "" for lig_at in v: for hb in lig_at.hbonds: if hasattr(hb, 'hAt'): hbstr += 'USER %s-%s~%s\n'%(hb.donAt.full_name(), hb.hAt.full_name(),hb.accAt.full_name()) else: hbstr += 'USER %s~%s\n'%(hb.donAt.full_name(), hb.accAt.full_name()) if verbose: print "hbstr=" #add it to ss here ss += hbstr if verbose: print "with hbstr: ss=", ss #if self.verbose: elif k == 'pi_cation': for res in v: #v=[(<Atom instance> HSG1:A:ARG8:CZ, [<Atom instance> IND: : INI 20:C1])] #res=(<Atom instance> HSG1:A:ARG8:CZ, [<Atom instance> IND: : INI 20:C1]) #res[0]=<Atom instance> HSG1:A:ARG8:CZ #res[1]= [<Atom instance> IND: : INI 20:C1] #res[1][0]= <Atom instance> IND: : INI 20:C1 # ss += "USER %s~~%s\n"%(res[0].full_name(), res[1][0].full_name()) else: for w in v: if verbose: print "@@ getResultStr w=", w try: ss += 'USER ' + w.full_name()+'\n;' except: if verbose: print "except on ", w ss += "USER " + str(w) ss += "\n" if verbose: print " end ss= ", ss return ss def buildHydrogenBonds(self, ligand, comment="USER AD> "): h_pairDict = self.hydrogen_bond_builder.build(ligand.allAtoms, self.macro_atoms) self.h_pairDict = h_pairDict #keys should be from lig, values from macro #sometimes are not...@@check this@@ h_results = {} for k, v in h_pairDict.items(): h_results[k] = 1 for at in v: h_results[at] = 1 all_hb_ats = AtomSet(h_results.keys()) #all d = self.results macro_hb_ats = d['macro_hb_atoms'] = all_hb_ats.get(lambda x: x.top==self.macro) self.macro_hb_ats = macro_hb_ats # process lig lig_hb_ats = d['lig_hb_atoms'] = all_hb_ats.get(lambda x: x in ligand.allAtoms) self.lig_hb_ats = lig_hb_ats outS = comment + "lig_hb_atoms : %d\n"%(len(lig_hb_ats)) for p in self.lig_hb_ats: #intD.results['lig_hb_atoms']: for hb in p.hbonds: if hasattr(hb, 'used'): continue if hb.hAt is not None: outS += comment + "%s,%s~%s\n"%(hb.donAt.full_name(), hb.hAt.name, hb.accAt.full_name()) else: outS += comment + "%s~%s\n"%(hb.donAt.full_name(), hb.accAt.full_name()) hb.used = 1 #hsg1V:B:ARG8:NH2,HH22~clean: : INI 20:N5 #clean: : INI 20:O4,H3~hsg1V:B:ASP29:OD2 #clean: : INI 20:O4,H3~hsg1V:B:ASP29:OD2 #clean: : INI 20:N4,H3~hsg1V:B:GLY27:O #clean: : INI 20:O2,H2~hsg1V:B:ASP25:OD1 #macroHStr = self.macro.allAtoms.get(lambda x: hasattr(x, 'hbonds') and len(x.hbonds)).full_name() #ligHStr = ligand.allAtoms.get(lambda x: hasattr(x, 'hbonds') and len(x.hbonds)).full_name() #return macroHStr + '==' + ligHStr if self.verbose: print "buildHB returning:" print outS return outS def buildCloseContactAtoms(self, percentCutoff, ligand, comment="USER AD> "): pairDict = self.distanceSelector.select(ligand.allAtoms, self.macro_atoms, percentCutoff=percentCutoff) self.pairDict = pairDict #reset here lig_close_ats = AtomSet() macro_close_ats = AtomSet() cdict = {} for k,v in pairDict.items(): if len(v): cdict[k] = 1 for at in v: if at not in macro_close_ats: cdict[at] = 1 closeAtoms = AtomSet(cdict.keys()) lig_close_ats = closeAtoms.get(lambda x: x.top==ligand).uniq() #ligClAtStr = lig_close_ats.full_name() ligClAtStr = comment + "lig_close_ats: %d\n" %( len(lig_close_ats)) if len(lig_close_ats): ligClAtStr += comment + "%s\n" %( lig_close_ats.full_name()) macro_close_ats = closeAtoms.get(lambda x: x in self.macro_atoms).uniq() macroClAtStr = comment + "macro_close_ats: %d\n" %( len(macro_close_ats)) if len(macro_close_ats): macroClAtStr += comment + "%s\n" %( macro_close_ats.full_name()) #macroClAtStr = "macro_close_ats: " + len(macro_close_ats)+"\n" +macro_close_ats.full_name() rdict = self.results rdict['lig_close_atoms'] = lig_close_ats rdict['macro_close_atoms'] = macro_close_ats if self.verbose: print "macroClAtStr=", macroClAtStr if self.verbose: print "ligClAtStr=", ligClAtStr if self.verbose: print "returning "+ macroClAtStr + '==' + ligClAtStr return macroClAtStr , ligClAtStr def getCations(self, atoms): #select atoms in ARG and LYS residues arg_cations = atoms.get(lambda x: (x.parent.type=='ARG' and \ x.name in ['CZ'])) lys_cations = atoms.get(lambda x: (x.parent.type=='LYS' and \ x.name in ['NZ', 'HZ1', 'HZ2', 'HZ3'])) #select any positively-charged metal ions... cannot include CA here metal_cations = atoms.get(lambda x: x.name in ['Mn','MN', 'Mg',\ 'MG', 'FE', 'Fe', 'Zn', 'ZN']) ca_cations = atoms.get(lambda x: x.name in ['CA', 'Ca'] and x.parent.type=='CA') cations = AtomSet() #cations.extend(arg_cations) for a in arg_cations: cations.append(a) #cations.extend(lys_cations) for a in lys_cations: cations.append(a) #cations.extend(metal_cations) for a in metal_cations: cations.append(a) #cations.extend(ca_cations) for a in ca_cations: cations.append(a) return cations def detectPiInteractions(self, tolerance=0.95, debug=False, use_all_cycles=False): if debug: print "in detectPiInteractions" self.results['pi_pi'] = [] #stacked rings...? self.results['t_shaped'] = [] #one ring perpendicular to the other self.results['cation_pi'] = [] # self.results['pi_cation'] = [] # self.results['macro_cations'] = []# self.results['lig_cations'] = [] # #at this point have self.results if not len(self.results['lig_close_atoms']): return lig_atoms = self.results['lig_close_atoms'].parent.uniq().atoms macro_res = self.results['macro_close_atoms'].parent.uniq() if not len(macro_res): return macro_atoms = macro_res.atoms l_rf = RingFinder() #Ligand l_rf.findRings2(lig_atoms, lig_atoms.bonds[0]) #rf.rings is list of dictionaries, one per ring, with keys 'bonds' and 'atoms' if debug: print "LIG: len(l_rf.rings)=", len(l_rf.rings) if not len(l_rf.rings): if debug: print "no lig rings found by l_rf!" return acbs = self.aromatic_cycle_bond_selector #acbs = AromaticCycleBondSelector() lig_rings = [] for r in l_rf.rings: ring_bnds = r['bonds'] if use_all_cycles: lig_rings.append(ring_bnds) else: arom_bnds = acbs.select(ring_bnds) if len(arom_bnds)>4: lig_rings.append(arom_bnds) if debug: print "LIG: len(lig_arom_rings)=", len(lig_rings) self.results['lig_rings'] = lig_rings self.results['lig_ring_atoms'] = AtomSet() #only check for pi-cation if lig_rings exist if len(lig_rings): macro_cations = self.results['macro_cations'] = self.getCations(macro_atoms) macro_cations = macro_cations.get(lambda x: x.element!='H') lig_ring_atoms = AtomSet() u = {} for r in lig_rings: for a in BondSet(r).getAtoms(): u[a] = 1 if len(u): lig_ring_atoms = AtomSet(u.keys()) lig_ring_atoms.sort() self.results['lig_ring_atoms'] = lig_ring_atoms if len(macro_cations): if debug: print "check distances from lig_rings to macro_cations here" #macro cations->lig rings pairDict2 = self.distanceSelector.select(lig_ring_atoms,macro_cations) z = {} for key,v in pairDict2.items(): val = v.tolist()[0] if val in macro_cations: z[val] = [key] if len(z): self.results['pi_cation'] = (z.items()) else: self.results['pi_cation'] = [] #check the distance between the rings and the macro_cations self.results['lig_cations'] = self.getCations(lig_atoms) lig_cations = self.results['lig_cations'] #remove hydrogens lig_cations = lig_cations.get(lambda x: x.element!='H') #Macromolecule m_rf = RingFinder() m_rf.findRings2(macro_res.atoms, macro_res.atoms.bonds[0]) #rf.rings is list of dictionaries, one per ring, with keys 'bonds' and 'atoms' if debug: print "MACRO: len(m_rf.rings)=", len(m_rf.rings) if not len(m_rf.rings): if debug: print "no macro rings found by m_rf!" return macro_rings = [] for r in m_rf.rings: ring_bnds = r['bonds'] if use_all_cycles: macro_rings.append(ring_bnds) else: arom_bnds = acbs.select(ring_bnds) if len(arom_bnds)>4: macro_rings.append(arom_bnds) if debug: print "len(macro_arom_rings)=", len(macro_rings) self.results['macro_rings'] = macro_rings self.results['macro_ring_atoms'] = AtomSet() #only check for pi-cation if macro_rings exist if len(macro_rings): macro_ring_atoms = AtomSet() u = {} for r in macro_rings: for a in BondSet(r).getAtoms(): #new method of bondSets u[a] = 1 if len(u): macro_ring_atoms = AtomSet(u.keys()) macro_ring_atoms.sort() self.results['macro_ring_atoms'] = macro_ring_atoms if len(lig_cations): if debug: print "check distances from macro_rings to lig_cations here" pairDict3 = self.distanceSelector.select(macro_ring_atoms,lig_cations) z = {} for x in pairDict3.items(): #lig cations->macro rings z.setdefault(x[1].tolist()[0], []).append(x[0]) if len(z): self.results['cation_pi'] = (z.items()) else: self.results['cation_pi'] = [] #macro_pi_atoms = AtomSet(pairDict3.keys()) #l_cations = AtomSet() #for v in pairDict3.values(): # for x in v: # l_cations.append(x) #self.results['cation_pi'] = pairDict3.items() #self.results['cation_pi'] = (l_cations, macro_pi_atoms) #check for intermol distance <6 Angstrom (J.ComputChem 29:275-279, 2009) #compare each lig_ring vs each macro_ring for lig_ring_bnds in lig_rings: lig_atoms = acbs.getAtoms(lig_ring_bnds) lig_atoms.sort() if debug: print "len(lig_atoms)=", len(lig_atoms) #--------------------------------- # compute the normal to lig ring #--------------------------------- a1 = Numeric.array(lig_atoms[0].coords) a2 = Numeric.array(lig_atoms[2].coords) a3 = Numeric.array(lig_atoms[4].coords) if debug: print "a1,a2, a3=", a1.tolist(), a2.tolist(), a3.tolist() for macro_ring_bnds in macro_rings: macro_atoms = acbs.getAtoms(macro_ring_bnds) macro_atoms.sort() if debug: print "len(macro_atoms)=", len(macro_atoms) pD_dist = self.distanceSelectorWithCutoff.select(macro_ring_atoms, lig_atoms, cutoff=self.dist_cutoff) if not len(pD_dist[0]): if debug: print "skipping ligand ring ", lig_rings.index(lig_ring_bnds), " vs ", print "macro ring", macro_rings.index(macro_ring_bnds) continue #--------------------------------- # compute the normal to macro ring #--------------------------------- b1 = Numeric.array(macro_atoms[0].coords) b2 = Numeric.array(macro_atoms[2].coords) b3 = Numeric.array(macro_atoms[4].coords) if debug: print "b1,b2, b3=", b1.tolist(), b2.tolist(), b3.tolist() # check for stacking a2_1 = a2-a1 a3_1 = a3-a1 b2_1 = b2-b1 b3_1 = b3-b1 if debug: print "a2_1 = ", a2-a1 if debug: print "a3_1 = ", a3-a1 if debug: print "b2_1 = ", b2-b1 if debug: print "b3_1 = ", b3-b1 n1 = crossProduct(a3_1,a2_1) #to get the normal for the first ring n2 = crossProduct(b3_1,b2_1) #to get the normal for the second ring if debug: print "n1=", n1 if debug: print "n2=", n2 n1 = Numeric.array(n1) n2 = Numeric.array(n2) n1_dot_n2 = Numeric.dot(n1,n2) if debug: print "n1_dot_n2", Numeric.dot(n1,n2) if abs(n1_dot_n2) >= 1*tolerance: if debug: print "The rings are stacked vertically" new_result = (acbs.getAtoms(lig_ring_bnds), acbs.getAtoms(macro_ring_bnds)) self.results['pi_pi'].append(new_result) if abs(n1_dot_n2) <= 0.01*tolerance: if debug: print "The rings are stacked perpendicularly" new_result = (acbs.getAtoms(lig_ring_bnds), acbs.getAtoms(macro_ring_bnds)) self.results['t_shaped'].append(new_result) def get_pi_pi(self, print_ctr=1, comment="USER AD> "): #one ring parallel to another #@@ UNTESTED! ... need test data if not len(self.results.get( 'pi_pi')): return "" # self.results['pi_pi'] = [] #stacked rings...? res = self.results['pi_pi'] ss = comment + "pi_pi: %d\n"%(len(self.results['pi_pi'])) #### #3l1m_lig_vs:A:HEM150:C4A,C3D,C1A,C2D,C4D,C2A,NA,ND,C3A,C1D : 3l1m_rec:A:PHE65:CD2,CD1,CG,CZ,CE1,CE2 #3l1m_lig_vs:A:HEM150:C3B,NB,C4A,C1A,C4B,NA,C2A,C2B,C3A,C1B : 3l1m_rec:A:PHE65:CD2,CD1,CG,CZ,CE1,CE2 #3l1m_lig_vs:A:HEM150:C3B,C1B,C3C,NB,C4C,NC,C2B,C1C,C4B,C2C : 3l1m_rec:A:PHE65:CD2,CD1,CG,CZ,CE1,CE2 #3l1m_lig_vs:A:HEM150:NC,C4C,C1C,C3C,C2C : 3l1m_rec:A:PHE65:CD2,CD1,CG,CZ,CE1,CE2 #3l1m_lig_vs:A:HEM150:C1A,NA,C2A,C3A,C4A : 3l1m_rec:A:PHE65:CD2,CD1,CG,CZ,CE1,CE2 #3l1m_lig_vs:A:HEM150:NB,C2B,C3B,C1B,C4B : 3l1m_rec:A:PHE65:CD2,CD1,CG,CZ,CE1,CE2 #3l1m_lig_vs:A:HEM150:NC,C4C,C1C,C3C,C2C : 3l1m_rec:A:PHE65:CD2,CD1,CG,CZ,CE1,CE2 #3l1m_lig_vs:A:HEM150:C4D,ND,C1D,C3D,C2D : 3l1m_rec:A:PHE65:CD2,CD1,CG,CZ,CE1,CE2 # # build string for self.results['pi_pi'] for res in self.results['pi_pi']: ss += comment + "%s~~%s\n"%(res[0].full_name(), res[1].full_name()) ###ss += "USER %s~~%s\n"%(res[0].full_name(), res[1].full_name()) #ss += "USER %s~~%s\n"%(res[0].full_name(), res[1][0].full_name()) #print "returning ss=" , ss return ss def get_t_shaped(self, print_ctr=1, comment="USER AD> "): #one ring perpendicular to the other #@@ UNTESTED! ... need test data if not len(self.results.get( 't_shaped')): return "" #print "in gpcr: self.results[t_shaped]=", self.results['t_shaped'] ss = comment + "t_shaped: %d\n"%(len(self.results['t_shaped'])) # build string for self.results['t_shaped'] for res in self.results['t_shaped']: #for example: #???? #???? #???? # ss += comment + "%s~~%s\n"%(res[0].full_name(), res[1][0].full_name()) return ss def get_cation_pi(self, print_ctr=1, comment="USER AD> "): #cation near aromatic ring #@@ UNTESTED! ... need test data if not len(self.results.get( 'cation_pi')): return "" ss = comment + "cation_pi: %d\n"%(len(self.results['cation_pi'])) # build string for self.results['cation_pi'] for res in self.results['cation_pi']: #for example: #???? #???? #???? # ss += comment + "%s~~%s\n"%(res[0].full_name(), res[1][0].full_name()) return ss def get_pi_cation_result(self, print_ctr=1, comment="USER AD> "): #aromatic ring near cation #print "in gpcr: self.results[pi_cation]=", self.results['pi_cation'] if not len(self.results.get( 'pi_cation')): return "" ss = comment + "pi_cation: %d\n"%(len(self.results['pi_cation'])) # build string for self.results['pi_cation'] for res in self.results['pi_cation']: #for example: #v=[(<Atom instance> HSG1:A:ARG8:CZ, [<Atom instance> IND: : INI 20:C1])] #res=(<Atom instance> HSG1:A:ARG8:CZ, [<Atom instance> IND: : INI 20:C1]) #res[0]=<Atom instance> HSG1:A:ARG8:CZ #res[1]= [<Atom instance> IND: : INI 20:C1] #res[1][0]= <Atom instance> IND: : INI 20:C1 # #attempt to get names for all atoms in the whole ring: res1_str = res[1][0].full_name() for rr in self.results['lig_rings']: ats = rr.getAtoms() if res[1][0] in ats: res1_str = ats.full_name() break ss += comment + "%s~~%s\n"%(res[0].full_name(), res1_str) #ss += "USER %s~~%s\n"%(res[0].full_name(), res[1][0].full_name()) return ss def print_ligand_residue_contacts(self, print_ctr=1): ctr = 1 #pairDict is atom-based #for residue-based report: # need to build lists of unique parents of keys # and lists of unique parents of corresponding values res_d = {} for at in self.pairDict.keys(): if at.parent not in res_d.keys(): res_d[at.parent] = {} for close_at in self.pairDict[at]: res_d[at.parent][close_at.parent] = 1 #print it out for lig_res in res_d.keys(): if print_ctr: print ctr, lig_res.parent.name+':'+ lig_res.name + '->', else: print lig_res.parent.name+':'+ lig_res.name + '->', for macro_res in res_d[lig_res]: print macro_res.parent.name + ':' + macro_res.name + ',', print ctr += 1 return res_d def print_macro_residue_contacts(self, print_ctr=1): ctr = 1 #pairDict is atom-based #for residue-based report: # need to build lists of unique parents of keys # and lists of unique parents of corresponding values res_d = {} for at_key, at_list in self.pairDict.items(): for at in at_list: if at.parent not in res_d.keys(): res_d[at.parent] = {} res_d[at.parent][at_key.parent] = 1 #print it out for macro_res in res_d.keys(): if print_ctr: print ctr, macro_res.parent.name+':'+ macro_res.name + '->', else: print macro_res.parent.name+':'+ macro_res.name + '->', for lig_res in res_d[macro_res]: print lig_res.parent.name + ':' + lig_res.name + ',', print ctr += 1 return res_d def print_report(self, keylist=[]): if not len(keylist): keylist = [ 'lig_close_atoms', 'lig_hb_atoms', 'lig_hbas', 'macro_close_atoms', 'macro_hb_atoms', ] d = self.results if self.verbose: for k in keylist: print k, ':', len(d[k]), '-', d[k].__class__ def print_hb_residue(self, print_ctr=1): ctr = 1 #pairDict is atom-based res_d = {} for at in self.h_pairDict.keys(): if at.parent not in res_d.keys(): res_d[at.parent] = {} for close_at in self.h_pairDict[at]: res_d[at.parent][close_at.parent] = 1 # print it out # Hbond instance are define with donAt,hAt ~ accAtt (tilde) for don_res in res_d.keys(): if print_ctr: print ctr, don_res.top.name+':'+don_res.parent.name+':'+ don_res.name + '->', else: print don_res.top.name+':'+don_res.parent.name+':'+ don_res.name + '->', for acc_res in res_d[don_res]: print acc_res.top.name+':'+acc_res.parent.name + ':' + acc_res.name + ',', print ctr += 1 return res_d