def accGeneric(donor, donorHyds, acceptor, r2, minAngle): if base.verbose: print "generic acceptor" dp = donor.xformCoord() ap = acceptor.xformCoord() if donor.element.name == "S": r2 = sulphurCompensate(r2) if base.verbose: print "distance: %g, cut off: %g" % (distance(dp, ap), sqrt(r2)) if sqdistance(dp, ap) > r2: if base.verbose: print "dist criteria failed" return 0 if base.verbose: print "dist criteria okay" ap = acceptor.xformCoord() dp = donor.xformCoord() for bonded in acceptor.primaryNeighbors(): bp = bonded.xformCoord() if base.verbose: print "angle: %g" % angle(bp, ap, dp) ang = angle(bp, ap, dp) if ang < minAngle: if base.verbose: print "angle too sharp (%g < %g)" % (ang, minAngle) return 0 if base.verbose: print "angle(s) okay (all > %g)" % minAngle return 1
def testPhiPsi(dp, donorHyds, ap, bp, phiPlane, r2, phi, theta): if base.verbose: print "distance: %g, cut off: %g" % (distance(dp, ap), sqrt(r2)) if sqdistance(dp, ap) > r2: if base.verbose: print "dist criteria failed" return 0 if base.verbose: print "dist criteria OK" if not testPhi(dp, ap, bp, phiPlane, phi): return 0 return testTheta(dp, donorHyds, ap, theta)
def accThetaTau(donor, donorHyds, acceptor, upsilonPartner, r2, upsilonLow, upsilonHigh, theta): if base.verbose: print "accThetaTau" dp = donor.xformCoord() ap = acceptor.xformCoord() if donor.element.name == "S": r2 = sulphurCompensate(r2) if base.verbose: print "distance: %g, cut off: %g" % (distance(dp, ap), sqrt(r2)) if sqdistance(dp, ap) > r2: if base.verbose: print "dist criteria failed" return 0 if base.verbose: print "dist criteria okay" if upsilonPartner: upPos = upsilonPartner.xformCoord() else: # upsilon measured from "lone pair" (bisector of attached # atoms) bondedPos = [] for bonded in acceptor.primaryNeighbors(): bondedPos.append(bonded.xformCoord()) lonePairs = bondPositions(ap, tetrahedral, 1.0, bondedPos) bisectors = [] for lp in lonePairs: bisectors.append(ap - (lp - ap)) upPos = bisectors[0] for bs in bisectors[1:]: if base.verbose: print "Testing 'extra' lone pair" if testThetaTau(dp, donorHyds, ap, bs, upsilonLow, upsilonHigh, theta): return 1 return testThetaTau(dp, donorHyds, ap, upPos, upsilonLow, upsilonHigh, theta)
def _tryFinish(atom, hbondInfo, finished, aroAmines, prunedBy, processed): # do we have enough info to establish all H/LP positions for atom? bondingInfo = _typeInfo(atom) geom = bondingInfo.geometry # from number of donors/acceptors, determine # if we can position Hs/lone pairs numBonds = _numBonds(atom) hydsToPosition = bondingInfo.substituents - numBonds openings = geom - numBonds donors = [] acceptors = [] all = [] for isAcc, other in hbondInfo[atom]: all.append(other) if isAcc: donors.append(other) else: acceptors.append(other) if len(all) < openings \ and len(donors) < openings - hydsToPosition \ and len(acceptors) < hydsToPosition: if debug: print "not enough info (all/donors/acceptors):", len(all), len(donors), len(acceptors) return False # if so, find their positions and # record in hbondInfo; mark as finished atPos = atom.xformCoord() targets = [] for isAcc, other in hbondInfo[atom][:2]: targets.append(_findTarget(atom, atPos, other, not isAcc, hbondInfo, finished)) # for purposes of this intermediate measurement, use hydrogen # distances instead of lone pair distances; determine true # lone pair positions once hydrogens are found bondedPos = [] testPositions = [] coplanar = [] for bonded in atom.primaryNeighbors(): bondedPos.append(bonded.xformCoord()) if bonded.element.number == 1: testPositions.append(bonded.xformCoord()) if geom == planar: for btb in bonded.primaryNeighbors(): if btb == atom: continue coplanar.append(btb.xformCoord()) toward = targets[0] if len(targets) > 1: toward2 = targets[1] else: toward2 = None Hlen = bondWithHLength(atom, geom) LPlen = vdwRadius(atom) if debug: print atom.oslIdent(), "co-planar:", coplanar print atom.oslIdent(), "toward:", toward print atom.oslIdent(), "toward2:", toward2 normals = bondPositions(atPos, geom, 1.0, bondedPos, coPlanar=coplanar, toward=toward, toward2=toward2) if debug: print atom.oslIdent(), "bondPositions:", [str(x) for x in normals] # use vectors so we can switch between lone-pair length and H-length for normal in normals: testPositions.append(normal - atPos) # try to hook up positions with acceptors/donors if atom in aroAmines: if debug: print "delay finishing aromatic amine" return False all = {} protons = {} lonePairs = {} conflicting = [] for isAcc, other in hbondInfo[atom]: if debug: print "other:", other.oslIdent() nearest = None if other in finished: oprotons, olps = hbondInfo[other] if isAcc: opositions = oprotons mul = LPlen else: opositions = olps mul = Hlen for opos in opositions: for check in testPositions: if isinstance(check, chimera.Vector): pos = atPos + check * mul else: pos = check dsq = (opos - pos).sqlength() if nearest is None or dsq < nsq: nearest = check nsq = dsq else: otherPos = other.xformCoord() if isAcc: mul = LPlen else: mul = Hlen for check in testPositions: if isinstance(check, chimera.Vector): pos = atPos + check * mul else: pos = check dsq = (pos - otherPos).sqlength() if debug: print "dist from other to", if isinstance(check, chimera.Point): print "pre-existing proton:", elif check in all: if check in protons: print "new proton:", else: print "new lone pair:", else: print "unfilled position:", import math print math.sqrt(dsq) if nearest is None or dsq < nsq: nearest = check nsq = dsq if isinstance(nearest, chimera.Point): # closest to known hydrogen; no help in positioning... if isAcc: # other is trying to donate and is nearest # to one of our hydrogens conflicting.append((isAcc, other)) continue if nearest in all: if isAcc: if nearest in protons: conflicting.append((isAcc, other)) elif nearest in lonePairs: conflicting.append((isAcc, other)) continue # check for steric conflict (frequent with metal coordination) if isAcc: pos = atPos + nearest * LPlen atBump = 0.0 else: pos = atPos + nearest * Hlen atBump = Hrad checkDist = 2.19 + atBump # since searchTree is a module variable that changes, # need to access via the module... nearby = AddH.searchTree.searchTree(pos.data(), checkDist) stericClash = False okay = set([atom, other]) okay.update(atom.primaryNeighbors()) for nb in nearby: if nb in okay: continue if nb.molecule != atom.molecule \ and nb.molecule.id == atom.molecule.id: # ignore clashes with sibling submodels continue dChk = vdwRadius(nb) + atBump - 0.4 if dChk*dChk >= sqdistance(nb.xformCoord(), pos): stericClash = True if debug: print "steric clash with", nb.oslIdent(), "(%.3f < %.3f)" % (pos.distance(nb.xformCoord()), dChk) break if stericClash: conflicting.append((isAcc, other)) continue all[nearest] = 1 if isAcc: if debug: print "determined lone pair" lonePairs[nearest] = 1 else: if debug: print "determined proton" protons[nearest] = 1 for isAcc, other in conflicting: if debug: print "Removing hbond to %s due to conflict" % other.oslIdent() hbondInfo[atom].remove((isAcc, other)) if not hbondInfo[atom]: del hbondInfo[atom] if other in finished: continue try: hbondInfo[other].remove((not isAcc, atom)) if not hbondInfo[other]: del hbondInfo[other] except ValueError: pass # since any conflicting hbonds may have been used to determine # positions, determine the positions again with the remaining # hbonds if conflicting: # restore hbonds pruned by the conflicting hbonds for isAcc, other in conflicting: if isAcc: key = (other, atom) else: key = (atom, other) for phb in prunedBy.get(key, []): if debug: print "restoring %s/%s hbond pruned by hbond to %s" % (phb[0].oslIdent(), phb[1].oslIdent(), other.oslIdent()) processed.remove(phb) if atom not in hbondInfo: if debug: print "No non-conflicting hbonds left!" return False if debug: print "calling _tryFinish with non-conflicting hbonds" return _tryFinish(atom, hbondInfo, finished, aroAmines, prunedBy, processed) # did we determine enough positions? if len(all) < openings \ and len(protons) < hydsToPosition \ and len(lonePairs) < openings - hydsToPosition: if debug: print "not enough hookups (all/protons/lps):", len(all), len(protons), len(lonePairs) return False if len(protons) < hydsToPosition: for pos in testPositions: if isinstance(pos, chimera.Point): continue if pos not in all: protons[pos] = 1 Hlocs = [] for Hvec in protons.keys(): Hlocs.append(atPos + Hvec * Hlen) LPlocs = [] for vec in testPositions: if isinstance(vec, chimera.Point): continue if vec not in protons: LPlocs.append(atPos + vec * LPlen) hbondInfo[atom] = (Hlocs, LPlocs) finished[atom] = True return True
def donWater(donor, donorHyds, acceptor, sp2Orp2, sp2Or2, sp2Otheta, sp3Orp2, sp3Or2, sp3Otheta, sp3Ophi, sp3Nrp2, sp3Nr2, sp3Ntheta, sp3Nupsilon, genRp2, genR2, genTheta): if base.verbose: print "donWater" if len(donorHyds) > 0: # hydrogens explicitly present, # can immediately call donThetaTau return donThetaTau(donor, donorHyds, acceptor, sp2Orp2, sp2Otheta, sp3Orp2, sp3Otheta, sp3Ophi, sp3Nrp2, sp3Ntheta, sp3Nupsilon, genRp2, genTheta) ap = acceptor.xformCoord() dp = donor.xformCoord() accType = acceptor.idatmType if not typeInfo.has_key(accType): if base.verbose: print "Unknown acceptor type failure" return 0 geom = typeInfo[accType].geometry element = acceptor.element.name if element == 'O' and geom == planar: if base.verbose: print "planar O" sq = sqdistance(dp, ap) if sq > sp2Or2: if base.verbose: print "dist criteria failed (%g > %g)" % ( sqrt(sq), sqrt(sp2Or2)) return 0 elif element == 'O' and geom == tetrahedral \ or element == 'N' and geom == planar: if base.verbose: print "planar N or tet O" sq = sqdistance(dp, ap) if sq > sp3Or2: if base.verbose: print "dist criteria failed (%g > %g)" % ( sqrt(sq), sqrt(sp3Or2)) return 0 elif element == 'N' and geom == tetrahedral: if base.verbose: print "tet N" sq = sqdistance(dp, ap) if sq > sp3Nr2: if base.verbose: print "dist criteria failed (%g > %g)" % ( sqrt(sq), sqrt(sp3Nr2)) return 0 else: if base.verbose: print "generic acceptor" sq = sqdistance(dp, ap) if sq > genR2: if base.verbose: print "dist criteria failed (%g > %g)" % ( sqrt(sq), sqrt(genR2)) return 0 if base.verbose: print "dist criteria OK" return donThetaTau(donor, donorHyds, acceptor, sp2Orp2, sp2Otheta, sp3Orp2, sp3Otheta, sp3Ophi, sp3Nrp2, sp3Ntheta, sp3Nupsilon, genRp2, genTheta, isWater=1)
def donGeneric(donor, donorHyds, acceptor, sp2Orp2, sp3Orp2, sp3Nrp2, sp2Or2, sp3Or2, sp3Nr2, genRp2, genR2, minHydAngle, minBondedAngle): if base.verbose: print "donGeneric" dc = donor.xformCoord() ac = acceptor.xformCoord() accType = acceptor.idatmType if not typeInfo.has_key(accType): return 0 geom = typeInfo[accType].geometry element = acceptor.element.name if element == 'O' and geom == planar: if base.verbose: print "planar O" r2 = sp2Or2 rp2 = sp2Orp2 elif element == 'O' and geom == tetrahedral \ or element == 'N' and geom == planar: if base.verbose: print "planar N or tet O" r2 = sp3Or2 rp2 = sp3Orp2 elif element == 'N' and geom == tetrahedral: if base.verbose: print "tet N" r2 = sp3Nr2 rp2 = sp3Nrp2 else: if base.verbose: print "generic acceptor" if acceptor.element.name == "S": r2 = sulphurCompensate(genR2) minBondedAngle = minBondedAngle - 9 r2 = genR2 rp2 = genRp2 ap = acceptor.xformCoord() dp = donor.xformCoord() if len(donorHyds) == 0: D2 = dc.sqdistance(ac) if D2 > r2: if base.verbose: print "dist criteria failed (%g > %g)" % ( sqrt(D2), sqrt(r2)) return 0 else: for hydPos in donorHyds: if sqdistance(hydPos, ap) < rp2: break else: if base.verbose: print "hyd dist criteria failed (all >= %g)" % ( sqrt(rp2)) return 0 if base.verbose: print "dist criteria OK" for bonded in donor.primaryNeighbors(): if bonded.element.number <= 1: continue bp = bonded.xformCoord() ang = angle(bp, dp, ap) if ang < minBondedAngle: if base.verbose: print "bonded angle too sharp (%g < %g)" % ( ang, minBondedAngle) return 0 if len(donorHyds) == 0: if base.verbose: print "No specific hydrogen positions; default accept" return 1 for hydPos in donorHyds: ang = angle(dp, hydPos, ap) if ang >= minHydAngle: if base.verbose: print "hydrogen angle okay (%g >= %g)" % ( ang, minHydAngle) return 1 if base.verbose: print "hydrogen angle(s) too sharp (< %g)" % minHydAngle return 0
def donThetaTau(donor, donorHyds, acceptor, sp2Orp2, sp2Otheta, sp3Orp2, sp3Otheta, sp3Ophi, sp3Nrp2, sp3Ntheta, sp3Nupsilon, genRp2, genTheta, isWater=0): # 'isWater' only for hydrogenless water if base.verbose: print "donThetaTau" if len(donorHyds) == 0 and not isWater: if base.verbose: print "No hydrogens; default failure" return 0 ap = acceptor.xformCoord() dp = donor.xformCoord() accType = acceptor.idatmType if not typeInfo.has_key(accType): if base.verbose: print "Unknown acceptor type failure" return 0 geom = typeInfo[accType].geometry element = acceptor.element.name if element == 'O' and geom == planar: if base.verbose: print "planar O" for hydPos in donorHyds: if sqdistance(hydPos, ap) <= sp2Orp2: break else: if not isWater: if base.verbose: print "dist criteria failed (all > %g)"\ % sqrt(sp2Orp2) return 0 theta = sp2Otheta elif element == 'O' and geom == tetrahedral \ or element == 'N' and geom == planar: if base.verbose: print "planar N or tet O" for hydPos in donorHyds: if sqdistance(hydPos, ap) <= sp3Orp2: break else: if not isWater: if base.verbose: print "dist criteria failed (all > %g)"\ % sqrt(sp3Orp2) return 0 theta = sp3Otheta # only test phi for acceptors with two bonded atoms if len(acceptor.primaryBonds()) == 2: if base.verbose: print "testing donor phi" bonded = acceptor.primaryNeighbors() phiPlane, basePos = getPhiPlaneParams(acceptor, bonded[0], bonded[1]) if not testPhi(donor.xformCoord(), ap, basePos, phiPlane, sp3Ophi): return 0 elif element == 'N' and geom == tetrahedral: if base.verbose: print "tet N" for hydPos in donorHyds: if sqdistance(hydPos, ap) <= sp3Nrp2: break else: if not isWater: if base.verbose: print "dist criteria failed (all > %g)"\ % sqrt(sp3Nrp2) return 0 theta = sp3Ntheta # test upsilon against lone pair directions bondedPos = [] for bonded in acceptor.primaryNeighbors(): bondedPos.append(bonded.xformCoord()) lpPos = bondPositions(ap, geom, 1.0, bondedPos) if lpPos: # fixed lone pair positions for lp in bondPositions(ap, geom, 1.0, bondedPos): # invert position so that we are # measuring angles correctly ang = angle(dp, ap, ap - (lp - ap)) if ang > sp3Nupsilon: if base.verbose: print "acceptor upsilon okay"\ " (%g > %g)" % ( ang, sp3Nupsilon) break else: if base.verbose: print "all acceptor upsilons failed"\ " (< %g)" % sp3Nupsilon return 0 # else: indefinite lone pair positions; default okay else: if base.verbose: print "generic acceptor" if acceptor.element.name == "S": genRp2 = sulphurCompensate(genRp2) for hydPos in donorHyds: if sqdistance(hydPos, ap) <= genRp2: break else: if base.verbose: print "dist criteria failed (all > %g)" % sqrt( genRp2) return 0 theta = genTheta if base.verbose: print "dist criteria OK" return testTheta(dp, donorHyds, ap, theta)