def getAromaticRes(receptor): # TODO add acids and amides? aromatic_res = { "PHE": ["A"], "TYR": ["A"], "HIS": ["A", "N", "NA"], "TRP": ["A", "N", "NA"], "ARG": ["N", "C" , "NA"], "DA" : ["A", "NA", "N"], # DNA "DC" : ["A", "N", "NA"], "DT" : ["A", "N", "NA"], "DG" : ["A", "N", "NA"], "A" : ["A", "NA", "N"], # DNA/RNA "C" : ["A", "N", "NA"], "G" : ["A", "N", "NA"], "T" : ["A", "N", "NA"], "U" : ["A", "N", "NA"], } residues = {} special = [] # must be treated as a ligand part for a in receptor: a = a.strip() rtype = a[16:21].strip().upper() # NOTE: we can recognize only all caps! if a.startswith("HETATM"): special.append(a) elif a.startswith('ATOM') and rtype in aromatic_res.keys(): if hf.getAtype(a) in aromatic_res[rtype]: res_id = hf.pmvAtomStrip(a).rsplit(":",1)[0] # A:GLY48:N -> A:GLY48 if not res_id in residues.keys(): residues[res_id] = [] residues[res_id].append(a) if special and DEBUG: print "found potential co-factor: %d" % len(special) for a in special: print a return residues, special # residues = { "A:GLY48" : [ atom, atom, atom..]}
def findLigandRings(atoms): matrix = hf.getDistMatrix(atoms) bdist = 2.0 # angstrom of bond dinstance graph = hf.makeGraph(nodes_list = range(len(matrix)), distance_matrix = matrix, cutoff = bdist**2) rings5,rings6 = findRings(graph) RING = rings5+rings6 ligand_rings = {} for r in range(len(RING)): # res name first... # get the first atom PDB string in the ring atstring = atoms[RING[r][0]][2] ringname = hf.pmvAtomStrip(atstring) ringname_short = ringname.rsplit(":",1)[0].strip() if ringname_short: c = 0 while ringname_short+":"+str(c) in ligand_rings.keys(): c+=1 if c > 20: ringname_short = None break if ringname_short: lnam = ringname_short+":"+str(c) else: lnam = "rng:"+str(r) ligand_rings[lnam] = [] for a in RING[r]: ligand_rings[lnam].append(atoms[a][2]) return ligand_rings
def normalizeWaterScore(self): #pose,entropyPenalty=False): # keep track of the original energy values if self.debug: old_e = self.pose['energy'] old_leff = self.pose['leff'] # rec_displaced waters rec_displaced = 0 for p in self.pose['water_over_rec_penalty']: rec_displaced += p # print p # XXX #print "RECDISP", rec_displaced # XXX # lig_displaced waters lig_displaced = 0 for p in self.pose['water_over_lig_penalty']: lig_displaced += p # clustering-displaced clust_displaced = 0 for p in self.pose['water_cluster_penalty']: clust_displaced += p # count heavy at for lig.efficiency heavy_atoms = 0 for a in self.pose['text']: if hf.isAtom(a) and (not hf.getAtype(a) in ['HD', 'W']): heavy_atoms += 1 # energy correction if self.conservedwaterentropy: # XXX read the penalty from a variable! entropy = (0.2) * len(self.pose['water_bridge']) #print " ENTROPY PENALTY = ", entropy else: entropy = 0 self.pose[ 'energy'] += lig_displaced + rec_displaced + clust_displaced + entropy # lig.efficiency correction self.pose['leff'] = self.pose['energy'] / float(heavy_atoms) if self.debug: #print "\n# PENALTY:\t +%2.3f [rec: %2.3f, lig: %2.3f, clust: %2.3f ]" % (rec_displaced+lig_displaced+clust_displaced, # rec_displaced, lig_displaced, clust_displaced) print " Penalties:" print " - lig overlap : %2.3f" % lig_displaced print " - rec overlap : %2.3f" % rec_displaced print " - cluster penalty : %2.3f" % clust_displaced print " - extra entropy : %2.3f" % entropy print " ENERGY:\t %2.3f [ old: %2.3f ]" % (self.pose['energy'], old_e) print " L.EFF.:\t %2.3f [ old: %2.3f ]\n" % (self.pose['leff'], old_leff)
def weakWaterCleanup(self): # #pose, cutoff_weak = -0.35, cutoff_strong=-0.5, interact_dist=3.5 ): """ clean-up extra_weak waters (increment ['water_rec_over_penalty'], if necessary""" org_waters = len(self.pose['water_bridge']) #ignore_types = ['HD'] lone_water = [] interact_dist = self.interact_dist**2 hb_types = ['OA', 'NA', 'SA', 'N', 'O', 'OA', 'NA', 'SA'] hb_types += hf.METALS for idx in range(len(self.pose['water_bridge'])): water = self.pose['water_bridge'][idx] score = self.pose['water_bridge_scores'][idx] # analyze only waters that are not ranked strong if score >= self.cutoff_strong: atom_pool = [ a for a in self.pose['water_bridge_contacts'][idx] if hf.getAtype(a) in hb_types] if self.debug: hf.writeList('ATOM_POOL_%d_HB_%d.pdb' % (idx, sess), atom_pool, addNewLine=True) lone_water.append(water) try: for a in atom_pool: if (dist(water, a, sq=0)<= interact_dist): #print "FOUND MATE", #print dist(water, a, sq=True) #print a.strip() #print "------------------" raise self.qs #print "NO MATE FOR THIS", score #print water.strip() except: lone_water.remove(water) for w in lone_water: idx = self.pose['water_bridge'].index(w) del self.pose['water_bridge'][idx] del self.pose['water_bridge_contacts'][idx] # EXPERIMENTAL #w_score = self.pose['water_bridge_scores'][idx] #if w_score > # XXX THIS shouldn't happen: # a weakly bound water could be hanging in the bulk, but it should be accounted for # ...? # how to describe the bulk solvent? # combine desolv map + discrete waters? # self.pose['water_over_rec_penalty'].append( -self.pose['water_bridge_scores'].pop(idx) ) # update PDBQT text #print pose['water_over_rec_penalty'] # XXX text_idx = self.pose['text'].index(w) del self.pose['text'][text_idx] if self.debug: print "LONE WATERS # REMOVED: ", (org_waters- len(self.pose['water_bridge']) )
def normalizeWaterScore(self): #pose,entropyPenalty=False): # keep track of the original energy values if self.debug: old_e = self.pose['energy'] old_leff = self.pose['leff'] # rec_displaced waters rec_displaced = 0 for p in self.pose['water_over_rec_penalty']: rec_displaced += p # print p # XXX #print "RECDISP", rec_displaced # XXX # lig_displaced waters lig_displaced = 0 for p in self.pose['water_over_lig_penalty']: lig_displaced += p # clustering-displaced clust_displaced = 0 for p in self.pose['water_cluster_penalty']: clust_displaced += p # count heavy at for lig.efficiency heavy_atoms = 0 for a in self.pose['text']: if hf.isAtom(a) and ( not hf.getAtype(a) in ['HD', 'W'] ): heavy_atoms += 1 # energy correction if self.conservedwaterentropy: # XXX read the penalty from a variable! entropy = (0.2)*len(self.pose['water_bridge']) #print " ENTROPY PENALTY = ", entropy else: entropy = 0 self.pose['energy'] += lig_displaced + rec_displaced + clust_displaced + entropy # lig.efficiency correction self.pose['leff'] = self.pose['energy'] / float( heavy_atoms ) if self.debug: #print "\n# PENALTY:\t +%2.3f [rec: %2.3f, lig: %2.3f, clust: %2.3f ]" % (rec_displaced+lig_displaced+clust_displaced, # rec_displaced, lig_displaced, clust_displaced) print " Penalties:" print " - lig overlap : %2.3f" % lig_displaced print " - rec overlap : %2.3f" % rec_displaced print " - cluster penalty : %2.3f" % clust_displaced print " - extra entropy : %2.3f" % entropy print " ENERGY:\t %2.3f [ old: %2.3f ]" % (self.pose['energy'], old_e) print " L.EFF.:\t %2.3f [ old: %2.3f ]\n" % (self.pose['leff'], old_leff)
def getWaterGridEnergy(self): # cutoff_weak = -0.35, # weak water e.cutoff # cutoff_strong = -0.5, # strong water e.cutoff # desolvEntropy=-0.2, # desolvation entropy # mapdistrange = 0.5): # TODO to be used to 'minimize' the water position too? # looking for the best spot for a water # and increase the energy accordingly? # calculate the score for bridging waters; self.pose['water_bridge_scores'] = [] for w in self.pose['water_bridge']: points, best_point = self.getMapPoints( coords = hf.atomCoord(w), distance = self.mapdistrange) self.pose['water_bridge_scores'].append(best_point) # calculate ligand-overlapping penalties self.pose['water_over_lig_penalty'] = [] for w in self.pose['water_over_lig']: points, best_point = self.getMapPoints( coords = atomCoord(w), distance = self.mapdistrange) self.pose['water_over_lig_penalty'].append( -best_point) # calculate rec-overlapping penalties self.pose['water_over_rec_penalty'] = [] for w in self.pose['water_over_rec']: self.pose['water_over_rec_penalty'].append(-self.desolvEntropy)
def getWaterGridEnergy(self): # cutoff_weak = -0.35, # weak water e.cutoff # cutoff_strong = -0.5, # strong water e.cutoff # desolvEntropy=-0.2, # desolvation entropy # mapdistrange = 0.5): # TODO to be used to 'minimize' the water position too? # looking for the best spot for a water # and increase the energy accordingly? # calculate the score for bridging waters; self.pose['water_bridge_scores'] = [] for w in self.pose['water_bridge']: points, best_point = self.getMapPoints(coords=hf.atomCoord(w), distance=self.mapdistrange) self.pose['water_bridge_scores'].append(best_point) # calculate ligand-overlapping penalties self.pose['water_over_lig_penalty'] = [] for w in self.pose['water_over_lig']: points, best_point = self.getMapPoints(coords=atomCoord(w), distance=self.mapdistrange) self.pose['water_over_lig_penalty'].append(-best_point) # calculate rec-overlapping penalties self.pose['water_over_rec_penalty'] = [] for w in self.pose['water_over_rec']: self.pose['water_over_rec_penalty'].append(-self.desolvEntropy)
def recOverlapWaters(self): # pose, cutoff = CLASH, #ignore_types = ['HD', 'H', 'W'] ): """ INPUT : pdb lig (multi)model and pdb target model OUTPUT: cleaned pdb lig model """ #print "WARNING! missing interactions! Line B1" overlapping = [] atom_pool = [] cutoff = self.clash**2 #cutoff += 0.5 #cutoff = 0 #for a in self.pose['vdw_contacts']: # PDBQT, no-HD # XXX TODO BUG Line B1 for aset in self.pose[ 'water_bridge_contacts']: # PDBQT, no-HD # XXX TODO BUG Line B1 for a in aset: if not hf.getAtype(a) in self.ignore_types: atom_pool.append(a) if self.debug: fp = open('DEBUG_rec_overlap.pdb', 'w') fp.write("REMARK WATERS REMOVED BY RECEPTOR CLASHES") for w in self.pose['water_bridge']: try: # try/except is faster for a in atom_pool: #if hf.dist(w, a, sq=False) < self.clash: if hf.dist(w, a, sq=False) < cutoff: overlapping.append(w) self.pose['text'].remove(w) #print "\n\n\n #### REMOVED", w if self.debug: fp.write(w + "\n") fp.write(a + "\n") raise self.qs except: pass if self.debug: fp.close() print "Processed: %d | Rec-overlap %d" % (len( self.pose['water_bridge']), len(overlapping)) for w in overlapping: idx = self.pose['water_bridge'].index(w) del self.pose['water_bridge_contacts'][idx] self.pose['water_bridge'].remove(w) self.pose['water_over_rec'] = overlapping
def recOverlapWaters(self): # pose, cutoff = CLASH, #ignore_types = ['HD', 'H', 'W'] ): """ INPUT : pdb lig (multi)model and pdb target model OUTPUT: cleaned pdb lig model """ #print "WARNING! missing interactions! Line B1" overlapping = [] atom_pool = [] cutoff = self.clash**2 #cutoff += 0.5 #cutoff = 0 #for a in self.pose['vdw_contacts']: # PDBQT, no-HD # XXX TODO BUG Line B1 for aset in self.pose['water_bridge_contacts']: # PDBQT, no-HD # XXX TODO BUG Line B1 for a in aset: if not hf.getAtype(a) in self.ignore_types: atom_pool.append(a) if self.debug: fp = open('DEBUG_rec_overlap.pdb','w') fp.write("REMARK WATERS REMOVED BY RECEPTOR CLASHES") for w in self.pose['water_bridge']: try: # try/except is faster for a in atom_pool: #if hf.dist(w, a, sq=False) < self.clash: if hf.dist(w, a, sq=False) < cutoff: overlapping.append(w) self.pose['text'].remove(w) #print "\n\n\n #### REMOVED", w if self.debug: fp.write(w+"\n") fp.write(a+"\n") raise self.qs except: pass if self.debug: fp.close() print "Processed: %d | Rec-overlap %d" % (len(self.pose['water_bridge']), len(overlapping) ) for w in overlapping: idx = self.pose['water_bridge'].index(w) del self.pose['water_bridge_contacts'][idx] self.pose['water_bridge'].remove(w) self.pose['water_over_rec'] = overlapping
def searchPiGroupsLig(lig): # input : ligand atom lines (list) atoms = hf.getFlatAtoms(lig, flat_only = True) ligand_rings = findLigandRings(atoms) if ligand_rings: for r in ligand_rings.keys(): if DEBUG: print "+++" for a in ligand_rings[r]: print a[:-1] print "+++\n" ligand_rings[r] = atomsToVec(ligand_rings[r]) return ligand_rings else: return False
def __init__( self, pose=None, # advanced pose to process [text, coords, water_bridge, ...] gridmap=None, # grid map (file/dictionary) # distance parms mapdistrange=1.0, # distance around W coords to look for grid points clust_tol=2.0, # cluster tolerance interact_dist=3.5, # W-RecAtom interaction distance clash=2.03, # min.distance allowed between W/atoms (lig, rec) # XX to remove spurious watre loneliness_dist=3.5, # max distance between W and interacting rec atoms # energy parms cutoff_weak=-0.35, # weak water e.cutoff cutoff_strong=-0.5, # strong water e.cutoff desolvEntropy=-0.2, # desolvation entropy conservedwaterentropy=0, # add an entropy penalty also for conserved waters ignore_types=['H', 'HD', 'W'], # atoms ignored for lig_overlap debug=False): # ignore atom types for contacts if gridmap == None: #raise "No water map provided" print "No water map provided" return False print "WARNING processHydroDocking:_init_> clash distance [%2.3f] is extremely low! Test with some case studies" % clash self.gridmap = gridmap self.pose = pose self.mapdistrange = mapdistrange self.clust_tol = clust_tol self.interact_dist = interact_dist self.clash = clash self.loneliness_dist = loneliness_dist self.cutoff_weak = cutoff_weak self.cutoff_strong = cutoff_strong self.desolvEntropy = desolvEntropy self.ignore_types = ignore_types self.conservedwaterentropy = conservedwaterentropy self.qs = hf.QuickStop() self.debug = debug if not (pose == None) and not (gridmap == None): self.process
def atomsToVec(atoms): a1 = hf.atomCoord(atoms[1]) a2 = hf.atomCoord(atoms[2]) a3 = hf.atomCoord(atoms[3]) centroid = hf.avgCoord(atoms) pvector = hf.calcPlane(a1, a2, a3) v1 = hf.vector(centroid, a1) v2 = hf.vector(centroid, a2) normal1 = hf.normalize(cross(v1 ,v2)) normal2 = hf.normalize(cross(v2 ,v1)) centroid_norm1 = hf.normalize(hf.vector(centroid, normal1)) centroid_norm2 = hf.normalize(hf.vector(centroid, normal2)) return {"centroid":centroid,"plane":hf.normalize(pvector),"normal":[normal1, normal2],\ 'cnormal' : [centroid_norm1, centroid_norm2]}
def clusterWaters(self): #pose, clust_tolerance=2.0, desolvEntropy=0): # cluster waters # XXX # energy from the cluster? # once 3 waters are reclustered (3->1) # two desolvEntropy penalties should be issued? # XXX unclustered_waters = self.pose['water_bridge'][:] # XXX NOT SURE IT"S NECESSARY # cluster waters water_clusters = hf.clusterAtoms(unclustered_waters, tol=self.clust_tol) # calculate clustering energy self.pose['water_cluster_penalty'] = [] remove_from_text = [] for pop in water_clusters: if len(pop)>1: this_cluster_centroid = hf.avgCoord(pop) this_cluster_penalty = -self.desolvEntropy * (len(pop)-1) this_cluster_energy = 0 this_cluster_contacts = [] closest_to_centroid = None closest_dist = 999999 for w in pop: # get the w_index + w_score idx = self.pose['water_bridge'].index(w) this_cluster_energy += self.pose['water_bridge_scores'][idx] remove_from_text.append(w) # find water closest to centroid (to be conserved) # get the distance from the cluster average centr_dist = hf.quickdist( hf.atomCoord(w), this_cluster_centroid, sq = False) a = [ hf.makePdb(coord=this_cluster_centroid) ] #writeList('centroid.pdb', a) if centr_dist <= closest_dist: closest_dist = centr_dist closest_to_centroid = w del self.pose['water_bridge'][idx] del self.pose['water_bridge_scores'][idx] # update contacts for a in self.pose['water_bridge_contacts'][idx]: if not a in this_cluster_contacts: this_cluster_contacts.append(a) del self.pose['water_bridge_contacts'][idx] # update PDBQT text # XXX DEBUGGINGH #print type(remove_from_text), remove_from_text[0] #writeList('REMOVING_WATERS.pdb', remove_from_text) #writeList('current_ligand.pdbqt', pose['text']) for w in remove_from_text: if not w == closest_to_centroid: try: text_idx = self.pose['text'].index(w) del self.pose['text'][text_idx] except: pass # use the closest to be updated with clustering coords closest_index = self.pose['text'].index(closest_to_centroid) water = self.pose['text'][closest_index] coord_text = "%8.3f%8.3f%8.3f" % (this_cluster_centroid[0], this_cluster_centroid[1], this_cluster_centroid[2]) cluster_water = water[0:30]+coord_text+water[54:] # XXX ugly, but it works... self.pose['text'][closest_index] = cluster_water # update water_bridge list+score, water_cluster_penalty list self.pose['water_bridge'].append(cluster_water) self.pose['water_bridge_scores'].append(this_cluster_energy) self.pose['water_cluster_penalty'].append(this_cluster_penalty) self.pose['water_bridge_contacts'].append(this_cluster_contacts) if self.debug: print "CLUSTER SIZE", len(pop) print "CLUSTER ENRG", this_cluster_energy print "CLUSTER PENL", this_cluster_penalty #print "CLUSTER FINL", clust_energy+clust_penalty print "=============="
def findRings(graph): """ a very simple ring detection algorightm to identify 5, and 6 member aromatic rings A <- head / \ B C <- tier 1 | | D E <- tier 2 \ / F <- tier 3, ring closure (6 member-ring) A <- head / \ B C <- tier 1 | | D---E <- tier 2, ring closure (5-member ring) A <- head | B <- tier 1 / \ C---D <- tier 2 # SPURIOUS, RECOGNIZED BUT NOT COUNTED """ # TODO add a planarity check? rings5 = [] rings6 = [] if DEBUG: print "- starting ring detection..." for head in graph.keys(): tier1 = graph[head] tier2 = [] tier3 = [] # populate tier2 for node1 in tier1: for tmp in graph[node1]: if not tmp == head and not tmp in tier2 and (not tmp in tier1) : tier2.append(tmp) # populate tier3 for node2 in tier2: for tmp in graph[node2]: if (not tmp == head) and (not tmp in tier2) and (not tmp in tier1) and (not tmp in tier3): tier3.append(tmp) # 6 member rings for x in tier3: candidate = [] for c in tier2: if x in graph[c]: if not c in candidate: candidate.append(c) if len(candidate) >1: r6 = [ head ] r6.append(x) r6 += candidate for c in candidate: r6 += hf.intersect( graph[head], graph[c]) r6.sort() if not r6 in rings6: rings6.append( r6 ) if DEBUG: print " 6member!", r6 break # 5 member rings for c1 in tier2: for c2 in tier2: if not c1 == c2: if (c2 in graph[c1]) and (c1 in graph[c2]): is_3_ring = False for k in graph[c1]: if k in graph[c2]: is_3_ring =True if DEBUG: print " [ ...catched a cycle_3... ]" break if not is_3_ring : r5 = [ head ] r5.append(c1) r5.append(c2) r5 += hf.intersect( graph[head], graph[c1]) r5 += hf.intersect( graph[head], graph[c2]) r5.sort() if not r5 in rings5: if DEBUG: print " 5member ring!",r5 rings5.append(r5) break return rings5, rings6
def findStacking(set1, set2 = None): """ INPUT(set1, set2): scan the sets to find p- and t-stackings INPUT(set1): scan the set to find self-stackings (p-, t-) P-STACKING ---c--- | A V | ---c--- """ DEBUG = 0 MAXDIST_TSTACK = 5.0 # centroids distance (T-stacking)Angstroms MAXDIST_PSTACK = 4.2 # centroids distance (P-stacking) Angstroms MAXDIST_PSTACK = MAXDIST_PSTACK**2 MAXDIST_TSTACK = MAXDIST_TSTACK**2 #MAXDIST = 5.0 # Angstroms #MAXDIST = MAXDIST**2 PTOL = 29.9 # P-stacking plane angle tolerance (degr) TODO test values on a wide set! TTOL = 14.9 # T-stacking plane angle tolerance (degr) TODO test values on a wide set! PLANE_DIST_TSTACK = 2.5 t_accepted = [] p_accepted = [] if not set2: set2 = set1 for g1 in set1.keys(): R1 = set1[g1] for g2 in set2.keys(): if not g1 == g2: # this is for intramolecular stackings (set1 = set2) R2 = set2[g2] if not ([g1,g2] in p_accepted) and not ([g2,g1] in p_accepted): if (not [g1,g2] in t_accepted) and not ([g2,g1] in t_accepted): d = hf.quickdist(R1['centroid'], R2['centroid']) if d <= MAXDIST_TSTACK: #print "GOOD DIST: %2.3f" % sqrt(d), pdist = abs(hf.dot( R1['normal'][0], (R1['centroid']-R2['centroid']) )) pangle = abs(hf.vecAngle( R1['plane'], R2['plane'])) #print "DISTANCE: %2.2f <"% math.sqrt(d), math.sqrt(MAXDIST_TSTACK) #print "PTOL", PTOL #print "PDIST:", pdist #print "PANGLE:", pangle #print "======" #print "PANGLE-180", pangle-180 #print abs(pangle-180)<=PTOL or (pangle-PTOL)<=0 #print "PAANGLE-180 < PTOL", abs(pangle-180)<=PTOL #print "PANGLE-PTOL", pangle-PTOL #print "PANGLE-PTOL < 0", (pangle-PTOL)<=0 #print "\n\n" if d <= MAXDIST_PSTACK and ((abs(pangle-180)<=PTOL) or ((pangle-PTOL)<=0)): p_accepted.append([ g1, g2]) if DEBUG: print " ==> P-stacking", print "%s--%s plane angle: %2.1f | pdist: %2.2f\n\n" % (g1, g2, pangle, pdist), elif (( abs(pangle-90) <= TTOL) or ( abs(pangle-270)<=TTOL) ) and pdist < PLANE_DIST_TSTACK: t_accepted.append([ g1, g2]) print "XXX" if DEBUG: print "== > T-stacking", print "%s--%s plane angle: %2.1f " % (g1, g2, pangle), if ( abs(pangle-90) <= TTOL): print "ANGLE:",abs(pangle-90), "<", TTOL if ( abs(pangle-270)<=TTOL): print "ANGLE:", abs(pangle-270), "<", TTOL print "PLANE:",pdist, "<", PLANE_DIST_TSTACK return p_accepted, t_accepted
if lts: for p in lts: res = p[0] rec_centroid = rec_vectors[p[0]]['centroid'] lig_centroid = lig_vectors[p[1]]['centroid'] tstack.append([res, rec_centroid, lig_centroid]) return pstack, tstack ################################################################## if __name__ == "__main__": from sys import argv import os # input must be PDBQT ligand = hf.getLines(argv[1]) name = os.path.basename(argv[1]) name = os.path.splitext(name)[0] try: if argv[2] =='-d': DEBUG=True except: pass lig_atoms = hf.getAtoms(ligand) ligand_rings = searchPiGroupsLig(lig_atoms) if not ligand_rings: print "No rings in the ligand" exit() else:
def clusterWaters(self): #pose, clust_tolerance=2.0, desolvEntropy=0): # cluster waters # XXX # energy from the cluster? # once 3 waters are reclustered (3->1) # two desolvEntropy penalties should be issued? # XXX unclustered_waters = self.pose[ 'water_bridge'][:] # XXX NOT SURE IT"S NECESSARY # cluster waters water_clusters = hf.clusterAtoms(unclustered_waters, tol=self.clust_tol) # calculate clustering energy self.pose['water_cluster_penalty'] = [] remove_from_text = [] for pop in water_clusters: if len(pop) > 1: this_cluster_centroid = hf.avgCoord(pop) this_cluster_penalty = -self.desolvEntropy * (len(pop) - 1) this_cluster_energy = 0 this_cluster_contacts = [] closest_to_centroid = None closest_dist = 999999 for w in pop: # get the w_index + w_score idx = self.pose['water_bridge'].index(w) this_cluster_energy += self.pose['water_bridge_scores'][ idx] remove_from_text.append(w) # find water closest to centroid (to be conserved) # get the distance from the cluster average centr_dist = hf.quickdist(hf.atomCoord(w), this_cluster_centroid, sq=False) a = [hf.makePdb(coord=this_cluster_centroid)] #writeList('centroid.pdb', a) if centr_dist <= closest_dist: closest_dist = centr_dist closest_to_centroid = w del self.pose['water_bridge'][idx] del self.pose['water_bridge_scores'][idx] # update contacts for a in self.pose['water_bridge_contacts'][idx]: if not a in this_cluster_contacts: this_cluster_contacts.append(a) del self.pose['water_bridge_contacts'][idx] # update PDBQT text # XXX DEBUGGINGH #print type(remove_from_text), remove_from_text[0] #writeList('REMOVING_WATERS.pdb', remove_from_text) #writeList('current_ligand.pdbqt', pose['text']) for w in remove_from_text: if not w == closest_to_centroid: try: text_idx = self.pose['text'].index(w) del self.pose['text'][text_idx] except: pass # use the closest to be updated with clustering coords closest_index = self.pose['text'].index(closest_to_centroid) water = self.pose['text'][closest_index] coord_text = "%8.3f%8.3f%8.3f" % (this_cluster_centroid[0], this_cluster_centroid[1], this_cluster_centroid[2]) cluster_water = water[0:30] + coord_text + water[ 54:] # XXX ugly, but it works... self.pose['text'][closest_index] = cluster_water # update water_bridge list+score, water_cluster_penalty list self.pose['water_bridge'].append(cluster_water) self.pose['water_bridge_scores'].append(this_cluster_energy) self.pose['water_cluster_penalty'].append(this_cluster_penalty) self.pose['water_bridge_contacts'].append( this_cluster_contacts) if self.debug: print "CLUSTER SIZE", len(pop) print "CLUSTER ENRG", this_cluster_energy print "CLUSTER PENL", this_cluster_penalty #print "CLUSTER FINL", clust_energy+clust_penalty print "=============="