コード例 #1
0
ファイル: hbond.py プロジェクト: davem22101/semanticscience
def _findTarget(fromAtom, atPos, toAtom, asDonor, hbondInfo, finished):
	if toAtom in coordinations.get(fromAtom, []):
		return toAtom.xformCoord()
	if toAtom in finished:
		toInfo = hbondInfo[toAtom]
		# known positioning already
		protons, lonePairs = toInfo
		if asDonor:
			positions = lonePairs
		else:
			positions = protons
		nearest = None
		for pos in positions:
			dsq = (pos - atPos).sqlength()
			if not nearest or dsq < nsq:
				nearest = pos
				nsq = dsq
		return nearest
	toHPos = []
	toBonded = []
	toCoplanar = []
	toGeom = _typeInfo(toAtom).geometry
	for tb in toAtom.primaryNeighbors():
		toBonded.append(tb.xformCoord())
		if tb.element.number == 1:
			toHPos.append(tb.xformCoord())
		if toGeom == planar:
			toAtomLocs = toAtom.allLocations()
			for btb in tb.primaryNeighbors():
				if btb in toAtomLocs:
					continue
				toCoplanar.append(btb.xformCoord())
	if asDonor:
		# point towards nearest lone pair
		targets = bondPositions(toAtom.xformCoord(), toGeom,
					vdwRadius(toAtom), toBonded,
					coPlanar=toCoplanar, toward=atPos)
	else:
		# point to nearest hydrogen
		if _typeInfo(toAtom).substituents <= _numBonds(toAtom):
			targets = toHPos
		else:
			targets = toHPos + bondPositions(
				toAtom.xformCoord(), toGeom,
				bondWithHLength(toAtom, toGeom),
				toBonded, coPlanar=toCoplanar, toward=atPos)
	nearest = None
	for target in targets:
		dsq = (target - atPos).sqlength()
		if not nearest or dsq < nsq:
			nearest = target
			nsq = dsq
	return nearest
コード例 #2
0
def addNewOs(new_res, psi, **kw):

    new_N = new_res.atomsMap['N'][0]
    new_CA = new_res.atomsMap['CA'][0]
    new_C = new_res.atomsMap['C'][0]

    new_OXT = addDihedralAtom('OXT',
                              chimera.Element(8),
                              new_C,
                              new_CA,
                              new_N,
                              DIST_C_O,
                              114,
                              psi,
                              residue=new_res)
    new_OXT.drawMode = kw['atomDrawMode']
    new_OXT.bfactor = kw['bFactor']

    addBond(new_OXT, new_C, drawMode=kw['bondDrawMode'])

    avail_bond_pos = bondPositions(bondee=new_C.coord(),
                                   geom=chimera.Atom.Planar,
                                   bondLen=DIST_C_O,
                                   bonded=[a.coord() for a in new_C.neighbors])

    use_point = avail_bond_pos[0]

    new_O = addAtom('O', chimera.Element(8), new_res, use_point)
    new_O.drawMode = kw['atomDrawMode']
    new_O.bfactor = kw['bFactor']

    addBond(new_O, new_C, drawMode=kw['bondDrawMode'])

    return new_O, new_OXT
コード例 #3
0
def addNewCA(last_bbone_ats, new_res, **kw):
    last_CA = last_bbone_ats['CA']
    last_C = last_bbone_ats['C']
    last_O = last_bbone_ats['O']

    new_N = new_res.atomsMap['N'][0]

    ## add the CA atom (not dihedral!)
    ## first, find the location for the new CA atom
    new_CA_bond_pos = bondPositions(
        bondee=new_N.coord(),
        geom=chimera.Atom.Planar,
        bondLen=DIST_CA_N,
        bonded=[a.coord() for a in new_N.neighbors],
        coPlanar=[last_CA.coord(), last_O.coord()])

    shortest_point = getShortestPoint(last_O.coord(), new_CA_bond_pos)

    new_CA = addAtom('CA', chimera.Element(6), new_res, shortest_point)
    new_CA.drawMode = kw['atomDrawMode']
    new_CA.bfactor = kw['bFactor']

    addBond(new_N, new_CA, drawMode=kw['bondDrawMode'])

    return new_CA
コード例 #4
0
def addHydrogens(atom, bondingInfo, namingSchema, totalHydrogens, idatmType,
                 invert, coordinations):
    away = away2 = planar = None
    geom = bondingInfo.geometry
    substs = bondingInfo.substituents
    curBonds = len(atom.primaryBonds())
    needed = substs - curBonds
    if needed <= 0:
        return
    atPos = atom.xformCoord()
    exclude = coordinations + atom.primaryNeighbors()
    if geom == 3 and curBonds == 1:
        bonded = atom.primaryNeighbors()[0]
        grandBonded = bonded.primaryNeighbors()
        grandBonded.remove(atom)
        if len(grandBonded) < 3:
            planar = [a.xformCoord() for a in grandBonded]
    if geom == 4 and not exclude:
        away, d, natom = findNearest(atPos, atom, exclude, 3.5)
        if away:
            away2, d2, natom2 = findRotamerNearest(atPos, idatmType[atom],
                                                   atom, natom, 3.5)
    elif geom == 4 and len(exclude) == 1:
        away, d, natom = findRotamerNearest(atPos, idatmType[atom], atom,
                                            exclude[0], 3.5)
    else:
        away, d, natom = findNearest(atPos, atom, exclude, 3.5)

    bondedPos = []
    for bonded in atom.primaryNeighbors():
        bondedPos.append(bonded.xformCoord())

    if coordinations:
        toward = coordinations[0].xformCoord()
        away2 = away
        away = None
    else:
        toward = None
    positions = bondPositions(atPos,
                              geom,
                              bondWithHLength(atom, geom),
                              bondedPos,
                              toward=toward,
                              coPlanar=planar,
                              away=away,
                              away2=away2)
    if coordinations:
        coordPos = None
        for pos in positions:
            d = pos.sqdistance(toward)
            if coordPos is None or d < lowest:
                coordPos = pos
                lowest = d
        positions.remove(coordPos)
    if len(positions) > needed:
        positions = roomiest(positions, atom, 3.5)[:needed]
    for i, pos in enumerate(positions):
        newHydrogen(atom, i + 1, totalHydrogens, namingSchema,
                    invert.apply(pos))
コード例 #5
0
	def xformCoord(self):
		primary = self.primaryNeighbors()[0]
		pos = bondPositions(primary.xformCoord(), 4, 1.0, bonds)
		class FakeCoord:
			pass	
		crd = FakeCoord()
		crd = pos[0]
		return crd
コード例 #6
0
ファイル: simple.py プロジェクト: davem22101/semanticscience
def addHydrogens(atom, bondingInfo, namingSchema, totalHydrogens, idatmType,
							invert, coordinations):
	away = away2 = planar = None
	geom = bondingInfo.geometry
	substs = bondingInfo.substituents
	curBonds = len(atom.primaryBonds())
	needed = substs - curBonds
	if needed <= 0:
		return
	atPos = atom.xformCoord()
	exclude = coordinations + atom.primaryNeighbors()
	if geom == 3 and curBonds == 1:
		bonded = atom.primaryNeighbors()[0]
		grandBonded = bonded.primaryNeighbors()
		grandBonded.remove(atom)
		if len(grandBonded) < 3:
			planar = [a.xformCoord() for a in grandBonded]
	if geom == 4 and not exclude:
		away, d, natom = findNearest(atPos, atom, exclude, 3.5)
		if away:
			away2, d2, natom2 = findRotamerNearest(atPos,
				idatmType[atom], atom, natom, 3.5)
	elif geom == 4 and len(exclude) == 1:
		away, d, natom = findRotamerNearest(atPos,
				idatmType[atom], atom, exclude[0], 3.5)
	else:
		away, d, natom = findNearest(atPos, atom, exclude, 3.5)

	bondedPos = []
	for bonded in atom.primaryNeighbors():
		bondedPos.append(bonded.xformCoord())

	if coordinations:
		toward = coordinations[0].xformCoord()
		away2 = away
		away = None
	else:
		toward = None
	positions = bondPositions(atPos, geom, bondWithHLength(atom, geom),
		bondedPos, toward=toward, coPlanar=planar, away=away, away2=away2)
	if coordinations:
		coordPos = None
		for pos in positions:
			d = pos.sqdistance(toward)
			if coordPos is None or d < lowest:
				coordPos = pos
				lowest = d
		positions.remove(coordPos)
	if len(positions) > needed:
		positions = roomiest(positions, atom, 3.5)[:needed]
	for i, pos in enumerate(positions):
		newHydrogen(atom, i+1, totalHydrogens, namingSchema,
							invert.apply(pos))
コード例 #7
0
def _methylate(na, n, atomNames):
    added = []
    aname = _getAName("C", atomNames)
    from chimera.molEdit import addAtom
    nn = addAtom(aname, chimera.Element('C'), na.residue, n.coord())
    added.append(nn)
    na.molecule.newBond(na, nn)
    from chimera.bondGeom import bondPositions
    for pos in bondPositions(nn.coord(), 4, 1.1, [na.coord()]):
        hname = _getAName("H", atomNames)
        nh = addAtom(hname, chimera.Element('H'), na.residue, pos)
        added.append(nh)
        na.molecule.newBond(nn, nh)
    return added
コード例 #8
0
def _methylate(na, n, atomNames):
	added = []
	aname = _getAName("C", atomNames)
	from chimera.molEdit import addAtom
	nn = addAtom(aname, chimera.Element('C'), na.residue, n.coord())
	added.append(nn)
	na.molecule.newBond(na, nn)
	from chimera.bondGeom import bondPositions
	for pos in bondPositions(nn.coord(), 4, 1.1, [na.coord()]):
		hname = _getAName("H", atomNames)
		nh = addAtom(hname, chimera.Element('H'), na.residue, pos)
		added.append(nh)
		na.molecule.newBond(nn, nh)
	return added
コード例 #9
0
ファイル: hbond.py プロジェクト: davem22101/semanticscience
def _tet2check(tetPos, toward, toward2, partnerPos):
	if debug:
		print "2 position tetCheck",
	for pos in bondPositions(tetPos, 4, 1.0, [], toward=toward,
							toward2=toward2):
		if debug:
			print chimera.angle(partnerPos, tetPos, pos),
		if chimera.angle(partnerPos, tetPos, pos) < _testAngles[4]:
			if debug:
				print "true"
			return True
	if debug:
		print "false"
	return False
コード例 #10
0
def completeTerminalCarboxylate(cter):
    from chimera.bondGeom import bondPositions
    from chimera.molEdit import addAtom
    if "OXT" in cter.atomsMap:
        return
    try:
        cs = cter.atomsMap["C"]
    except KeyError:
        return
    for c in cs:  # alt locs are possible
        if len(c.primaryBonds()) != 2:
            return
        loc = bondPositions(c.coord(), 3, 1.229,
                            [n.coord() for n in c.primaryNeighbors()])[0]
        oxt = addAtom("OXT", chimera.Element("O"), cter, loc, bondedTo=c)
    replyobj.info("Missing OXT added to C-terminal residue %s\n" % str(cter))
コード例 #11
0
def addNewN(last_bbone_ats, new_res, **kw):

    ## add the N atom of new residue's backbone to the last residue
    last_C = last_bbone_ats['C']

    new_N_bond_pos = bondPositions(
        bondee=last_C.coord(),
        geom=chimera.Atom.Planar,
        bondLen=DIST_N_C,
        bonded=[a.coord() for a in last_C.neighbors])

    use_point = new_N_bond_pos[0]
    new_N = addAtom('N', chimera.Element(7), new_res, use_point)
    new_N.drawMode = kw['atomDrawMode']
    new_N.bfactor = kw['bFactor']

    addBond(last_C, new_N, drawMode=kw['bondDrawMode'])

    return new_N
コード例 #12
0
def reposLastO(last_res, last_bbone_ats, atom_drawMode, bond_drawMode):
    ## reposition the last residue's 'O' atom
    last_O = getBboneAtom(last_res, 'O')
    last_res.molecule.deleteAtom(last_O)

    last_C = last_bbone_ats['C']
    last_CA = last_bbone_ats['CA']

    old_O_bond_pos = bondPositions(
        bondee=last_C.coord(),
        geom=chimera.Atom.Planar,
        bondLen=1.229,
        bonded=[a.coord() for a in last_C.neighbors])

    ## should only be one spot left
    old_O_bond_pos = old_O_bond_pos[0]
    new_O = addAtom('O', chimera.Element(8), last_res, old_O_bond_pos)
    new_O.drawMode = atom_drawMode
    addBond(new_O, last_C, drawMode=bond_drawMode)

    return new_O
コード例 #13
0
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)
コード例 #14
0
ファイル: base.py プロジェクト: davem22101/semanticscience
def swap(res, newRes, preserve=0, bfactor=True):
	"""change 'res' into type 'newRes'"""

	if res == "HIS":
		res = "HIP"
	fixed, buds, start, end = getResInfo(res)
	
	tmplRes = chimera.restmplFindResidue(newRes, start, end)
	if not tmplRes:
		raise ValueError, "No connectivity template for residue '%s'"\
									% newRes
	# sanity check:  does the template have the bud atoms?
	for bud in buds:
		if not tmplRes.atomsMap.has_key(bud):
			raise ValueError, "New residue type (%s) " \
			  "not compatible with starting residue type (%s)" \
			  				% (newRes, res.type)
		
	# if bfactor not specified, find highest bfactor in molecule
	# and use that for swapped-in atoms
	if bfactor is False:
		bfactor = None
	if bfactor is True:
		for a in res.molecule.atoms:
			try:
				if bfactor is True or a.bfactor > bfactor:
					bfactor = a.bfactor
			except AttributeError:
				pass

	if preserve:
		raise ValueError, "'preserve' keyword not yet implemented"

	# prune non-backbone atoms
	for a in res.oslChildren():
		if a.name not in fixed:
			a.molecule.deleteAtom(a)

	# add new sidechain
	while len(buds) > 0:
		bud = buds.pop()
		tmplBud = tmplRes.atomsMap[bud]
		resBud = res.atomsMap[bud][0]

		try:
			info = typeInfo[tmplBud.idatmType]
			geom = info.geometry
			subs = info.substituents
		except KeyError:
			print tmplBud.idatmType
			raise AssertionError, "Can't determine atom type" \
				" information for atom %s of residue %s" % (
				bud, res.oslIdent())

		# use coord() rather than xformCoord():  we want to set
		# the new atom's coord(), to which the proper xform will
		# then be applied
		for a, b in tmplBud.bondsMap.items():
			if a.element.number == 1:
				# don't add hydrogens
				continue
			if res.atomsMap.has_key(a.name):
				resBonder = res.atomsMap[a.name][0]
				if not resBud.bondsMap.has_key(resBonder):
					addBond(a, resBonder)
				continue

			newAtom = None
			numBonded = len(resBud.bonds)
			if numBonded >= subs:
				raise AssertionError, \
					"Too many atoms bonded to %s of" \
					" residue %s" % (bud, res.oslIdent())
			if numBonded == 0:
				raise AssertionError, \
					"Atom %s of residue %s has no" \
					" neighbors after pruning?!?" % (
					bud, res.oslIdent())
			elif numBonded == 1:
				# only one connected atom -- have to use
				# dihedral
				real1 = resBud.neighbors[0]
				newAtom = formDihedral(resBud, real1,
								tmplRes, a, b)
			elif numBonded == 2:
				crd = resBud.coord()
				bonded = resBud.neighbors
				bcrds = map(lambda a: a.coord(), bonded)
				positions = bondPositions(crd, geom,
							b.length(), bcrds)
				if len(positions) > 1:
					# need to disambiguate choices;
					# check improper dihedral

					# but first, make sure the bonded
					# atoms are in this residue
					inres = filter(lambda a, r=res:
							a.residue == r, bonded)
					if len(inres) == 0:
						raise AssertionError, \
						"Can't disambiguate " \
						"tetrahedral position by " \
						"forming in-residue dihedral " \
						"for %s of residue %s" % (
						bud, res.oslIdent())
					if len(inres) == 1:
						newAtom = formDihedral(resBud,
							inres[0], tmplRes, a, b)
					else:
						# okay, check improper dihedral
						tmplBonded = map(lambda a,
							tra=tmplRes.atomsMap:
							tra[a.name], bonded)
						tmplPts = map(lambda a:
							a.coord(), [a, 
							tmplBud] + tmplBonded)
						tmplDihed = apply(dihedral,
								tmplPts, {})
						backPts = map(lambda a:
							a.coord(), [resBud]
							+ bonded)
						dh1 = apply(dihedral, tuple(
								positions[:1]
								+ backPts), {})
						dh2 = apply(dihedral, tuple(
								positions[1:]
								+ backPts), {})
						diff1 = abs(dh1 - tmplDihed)
						diff2 = abs(dh2 - tmplDihed)
						if diff1 > 180:
							diff1 = 360 - diff1
						if diff2 > 180:
							diff2 = 360 - diff2
						if diff1 < diff2:
							position = positions[0]
						else:
							position = positions[1]
				else:
					position = positions[0]
			else:
				crd = resBud.coord()
				bonded = resBud.neighbors
				bcrds = map(lambda a: a.coord(), bonded)
				positions = bondPositions(crd, geom,
							b.length(), bcrds)
				if len(positions) > 1:
					raise AssertionError, \
						"Too many positions returned" \
						" for atom %s of residue %s" % (
						bud, res.oslIdent())
				position = positions[0]

			if not newAtom:
				newAtom = addAtom(a.name, a.element,
								res, position)
			newAtom.drawMode = resBud.drawMode
			if bfactor is not None and bfactor is not True:
				newAtom.bfactor = bfactor

			# TODO: need to iterate over coordSets
			for bonded in a.bondsMap.keys():
				if not res.atomsMap.has_key(bonded.name):
					continue
				addBond(newAtom, res.atomsMap[bonded.name][0])
			buds.append(newAtom.name)

	res.label = res.label.replace(res.type, newRes)
	res.type = newRes
コード例 #15
0
def swap(res, newRes, preserve=0, bfactor=True):
    """change 'res' into type 'newRes'"""

    if res == "HIS":
        res = "HIP"
    fixed, buds, start, end = getResInfo(res)

    tmplRes = chimera.restmplFindResidue(newRes, start, end)
    if not tmplRes:
        raise ValueError, "No connectivity template for residue '%s'"\
               % newRes
    # sanity check:  does the template have the bud atoms?
    for bud in buds:
        if not tmplRes.atomsMap.has_key(bud):
            raise ValueError, "New residue type (%s) " \
              "not compatible with starting residue type (%s)" \
                  % (newRes, res.type)

    # if bfactor not specified, find highest bfactor in molecule
    # and use that for swapped-in atoms
    if bfactor is False:
        bfactor = None
    if bfactor is True:
        for a in res.molecule.atoms:
            try:
                if bfactor is True or a.bfactor > bfactor:
                    bfactor = a.bfactor
            except AttributeError:
                pass

    if preserve:
        raise ValueError, "'preserve' keyword not yet implemented"

    # prune non-backbone atoms
    for a in res.oslChildren():
        if a.name not in fixed:
            a.molecule.deleteAtom(a)

    # add new sidechain
    while len(buds) > 0:
        bud = buds.pop()
        tmplBud = tmplRes.atomsMap[bud]
        resBud = res.atomsMap[bud][0]

        try:
            info = typeInfo[tmplBud.idatmType]
            geom = info.geometry
            subs = info.substituents
        except KeyError:
            print tmplBud.idatmType
            raise AssertionError, "Can't determine atom type" \
             " information for atom %s of residue %s" % (
             bud, res.oslIdent())

        # use coord() rather than xformCoord():  we want to set
        # the new atom's coord(), to which the proper xform will
        # then be applied
        for a, b in tmplBud.bondsMap.items():
            if a.element.number == 1:
                # don't add hydrogens
                continue
            if res.atomsMap.has_key(a.name):
                resBonder = res.atomsMap[a.name][0]
                if not resBud.bondsMap.has_key(resBonder):
                    addBond(a, resBonder)
                continue

            newAtom = None
            numBonded = len(resBud.bonds)
            if numBonded >= subs:
                raise AssertionError, \
                 "Too many atoms bonded to %s of" \
                 " residue %s" % (bud, res.oslIdent())
            if numBonded == 0:
                raise AssertionError, \
                 "Atom %s of residue %s has no" \
                 " neighbors after pruning?!?" % (
                 bud, res.oslIdent())
            elif numBonded == 1:
                # only one connected atom -- have to use
                # dihedral
                real1 = resBud.neighbors[0]
                newAtom = formDihedral(resBud, real1, tmplRes, a, b)
            elif numBonded == 2:
                crd = resBud.coord()
                bonded = resBud.neighbors
                bcrds = map(lambda a: a.coord(), bonded)
                positions = bondPositions(crd, geom, b.length(), bcrds)
                if len(positions) > 1:
                    # need to disambiguate choices;
                    # check improper dihedral

                    # but first, make sure the bonded
                    # atoms are in this residue
                    inres = filter(lambda a, r=res: a.residue == r, bonded)
                    if len(inres) == 0:
                        raise AssertionError, \
                        "Can't disambiguate " \
                        "tetrahedral position by " \
                        "forming in-residue dihedral " \
                        "for %s of residue %s" % (
                        bud, res.oslIdent())
                    if len(inres) == 1:
                        newAtom = formDihedral(resBud, inres[0], tmplRes, a, b)
                    else:
                        # okay, check improper dihedral
                        tmplBonded = map(
                            lambda a, tra=tmplRes.atomsMap: tra[a.name],
                            bonded)
                        tmplPts = map(lambda a: a.coord(),
                                      [a, tmplBud] + tmplBonded)
                        tmplDihed = apply(dihedral, tmplPts, {})
                        backPts = map(lambda a: a.coord(), [resBud] + bonded)
                        dh1 = apply(dihedral, tuple(positions[:1] + backPts),
                                    {})
                        dh2 = apply(dihedral, tuple(positions[1:] + backPts),
                                    {})
                        diff1 = abs(dh1 - tmplDihed)
                        diff2 = abs(dh2 - tmplDihed)
                        if diff1 > 180:
                            diff1 = 360 - diff1
                        if diff2 > 180:
                            diff2 = 360 - diff2
                        if diff1 < diff2:
                            position = positions[0]
                        else:
                            position = positions[1]
                else:
                    position = positions[0]
            else:
                crd = resBud.coord()
                bonded = resBud.neighbors
                bcrds = map(lambda a: a.coord(), bonded)
                positions = bondPositions(crd, geom, b.length(), bcrds)
                if len(positions) > 1:
                    raise AssertionError, \
                     "Too many positions returned" \
                     " for atom %s of residue %s" % (
                     bud, res.oslIdent())
                position = positions[0]

            if not newAtom:
                newAtom = addAtom(a.name, a.element, res, position)
            newAtom.drawMode = resBud.drawMode
            if bfactor is not None and bfactor is not True:
                newAtom.bfactor = bfactor

            # TODO: need to iterate over coordSets
            for bonded in a.bondsMap.keys():
                if not res.atomsMap.has_key(bonded.name):
                    continue
                addBond(newAtom, res.atomsMap[bonded.name][0])
            buds.append(newAtom.name)

    res.label = res.label.replace(res.type, newRes)
    res.type = newRes
コード例 #16
0
def changeAtom(atom, element, geometry, numBonds, autoClose=True, name=None):
	if len(atom.primaryBonds()) > numBonds:
		raise ParamError("Atom already has more bonds than requested.\n"
			"Either delete some bonds or choose a different number"
			" of requested bonds.")
	from chimera.molEdit import addAtom, genAtomName
	changedAtoms = [atom]
	if not name:
		name = genAtomName(element, atom.residue)
	changeAtomName(atom, name)
	atom.element = element
	if hasattr(atom, 'mol2type'):
		delattr(atom, 'mol2type')
		
	# if we only have one bond, correct its length
	if len(atom.primaryBonds()) == 1:
		neighbor = atom.primaryNeighbors()[0]
		newLength = bondLength(atom, geometry, neighbor.element,
						a2info=(neighbor, numBonds))
		setBondLength(atom.primaryBonds()[0], newLength,
					movingSide="smaller side")

	if numBonds == len(atom.primaryBonds()):
		return changedAtoms

	from chimera.bondGeom import bondPositions
	coPlanar = None
	if geometry == 3 and len(atom.primaryBonds()) == 1:
		n = atom.primaryNeighbors()[0]
		if len(n.primaryBonds()) == 3:
			coPlanar = [nn.coord() for nn in n.primaryNeighbors()
								if nn != atom]
	away = None
	if geometry == 4 and len(atom.primaryBonds()) == 1:
		n = atom.primaryNeighbors()[0]
		if len(n.primaryBonds()) > 1:
			nn = n.primaryNeighbors()[0]
			if nn == atom:
				nn = n.primaryNeighbors()[1]
			away = nn.coord()
	hydrogen = Element("H")
	positions = bondPositions(atom.coord(), geometry,
		bondLength(atom, geometry, hydrogen),
		[n.coord() for n in atom.primaryNeighbors()], coPlanar=coPlanar,
		away=away)[:numBonds-len(atom.primaryBonds())]
	if autoClose:
		if len(atom.molecule.atoms) < 100:
			testAtoms = atom.molecule.atoms
		else:
			from CGLutil.AdaptiveTree import AdaptiveTree
			tree = AdaptiveTree([a.coord().data()
						for a in atom.molecule.atoms],
						a.molecule.atoms, 2.5)
			testAtoms = tree.searchTree(atom.coord().data(), 5.0)
	else:
		testAtoms = []
	for pos in positions:
		for ta in testAtoms:
			if ta == atom:
				continue
			testLen = bondLength(ta, 1, hydrogen)
			testLen2 = testLen * testLen
			if (ta.coord() - pos).sqlength() < testLen2:
				bonder = ta
				# possibly knock off a hydrogen to
				# accomodate the bond...
				for bn in bonder.primaryNeighbors():
					if bn.element.number > 1:
						continue
					if chimera.angle(atom.coord()
							- ta.coord(), bn.coord()
							- ta.coord()) > 45.0:
						continue
					if bn in testAtoms:
						testAtoms.remove(bn)
					atom.molecule.deleteAtom(bn)
					break
				break
		else:
			bonder = addAtom(genAtomName(hydrogen, atom.residue),
				hydrogen, atom.residue, pos, bondedTo=atom)
			changedAtoms.append(bonder)
	return changedAtoms
コード例 #17
0
def changeAtom(atom, element, geometry, numBonds, autoClose=True, name=None):
    if len(atom.primaryBonds()) > numBonds:
        raise ParamError(
            "Atom already has more bonds than requested.\n"
            "Either delete some bonds or choose a different number"
            " of requested bonds.")
    from chimera.molEdit import addAtom, genAtomName
    changedAtoms = [atom]
    if not name:
        name = genAtomName(element, atom.residue)
    changeAtomName(atom, name)
    atom.element = element
    if hasattr(atom, 'mol2type'):
        delattr(atom, 'mol2type')

    # if we only have one bond, correct its length
    if len(atom.primaryBonds()) == 1:
        neighbor = atom.primaryNeighbors()[0]
        newLength = bondLength(atom,
                               geometry,
                               neighbor.element,
                               a2info=(neighbor, numBonds))
        setBondLength(atom.primaryBonds()[0],
                      newLength,
                      movingSide="smaller side")

    if numBonds == len(atom.primaryBonds()):
        return changedAtoms

    from chimera.bondGeom import bondPositions
    coPlanar = None
    if geometry == 3 and len(atom.primaryBonds()) == 1:
        n = atom.primaryNeighbors()[0]
        if len(n.primaryBonds()) == 3:
            coPlanar = [
                nn.coord() for nn in n.primaryNeighbors() if nn != atom
            ]
    away = None
    if geometry == 4 and len(atom.primaryBonds()) == 1:
        n = atom.primaryNeighbors()[0]
        if len(n.primaryBonds()) > 1:
            nn = n.primaryNeighbors()[0]
            if nn == atom:
                nn = n.primaryNeighbors()[1]
            away = nn.coord()
    hydrogen = Element("H")
    positions = bondPositions(atom.coord(),
                              geometry,
                              bondLength(atom, geometry, hydrogen),
                              [n.coord() for n in atom.primaryNeighbors()],
                              coPlanar=coPlanar,
                              away=away)[:numBonds - len(atom.primaryBonds())]
    if autoClose:
        if len(atom.molecule.atoms) < 100:
            testAtoms = atom.molecule.atoms
        else:
            from CGLutil.AdaptiveTree import AdaptiveTree
            tree = AdaptiveTree(
                [a.coord().data() for a in atom.molecule.atoms],
                a.molecule.atoms, 2.5)
            testAtoms = tree.searchTree(atom.coord().data(), 5.0)
    else:
        testAtoms = []
    for pos in positions:
        for ta in testAtoms:
            if ta == atom:
                continue
            testLen = bondLength(ta, 1, hydrogen)
            testLen2 = testLen * testLen
            if (ta.coord() - pos).sqlength() < testLen2:
                bonder = ta
                # possibly knock off a hydrogen to
                # accomodate the bond...
                for bn in bonder.primaryNeighbors():
                    if bn.element.number > 1:
                        continue
                    if chimera.angle(atom.coord() - ta.coord(),
                                     bn.coord() - ta.coord()) > 45.0:
                        continue
                    if bn in testAtoms:
                        testAtoms.remove(bn)
                    atom.molecule.deleteAtom(bn)
                    break
                break
        else:
            bonder = addAtom(genAtomName(hydrogen, atom.residue),
                             hydrogen,
                             atom.residue,
                             pos,
                             bondedTo=atom)
            changedAtoms.append(bonder)
    return changedAtoms
コード例 #18
0
ファイル: hydpos.py プロジェクト: davem22101/semanticscience
def hydPositions(heavy, includeLonePairs=False):
	"""Return list of positions for hydrogens attached to this atom.
	   If a hydrogen could be in one of several positions, don't
	   return any of those.
	"""

	# first, find known attached atoms
	bondedHeavys = []
	hyds = []
	for atom in heavy.primaryNeighbors():
		if atom.element.number > 1:
			bondedHeavys.append(atom)
		else:
			hyds.append(atom)
	
	# convert to Points
	hydLocs = []
	for hyd in hyds:
		hydLocs.append(hyd.xformCoord())
	
	if hydLocs and not includeLonePairs:
		# explicit hydrogens "win" over atom types
		return hydLocs

	if typeInfo.has_key(heavy.idatmType):
		info = typeInfo[heavy.idatmType]
		geom = info.geometry
		if includeLonePairs:
			subs = geom
		else:
			subs = info.substituents
		bondedLocs = hydLocs[:]
		for bHeavy in bondedHeavys:
			bondedLocs.append(bHeavy.xformCoord())
	else:
		return hydLocs
	
	knownSubs = len(bondedLocs)
	if knownSubs >= subs or knownSubs == 0:
		return hydLocs
	# above eliminates 'single' geometry
	
	if knownSubs == 1 and geom == tetrahedral:
		# rotamer
		return hydLocs
	
	maxSubs = geom
	if maxSubs - subs > 0:
		# the "empty" bond could be anywhere
		return hydLocs

	heavyLoc = heavy.xformCoord()
	bondLen = Element.bondLength(heavy.element, Element(1))

	if geom == planar:
		coPlanar = []
		for bHeavy in bondedHeavys:
			try:
				bhGeom = typeInfo[bHeavy.idatmType].geometry
			except KeyError:
				bhGeom = None
			if bhGeom != planar:
				continue
			for atom in bHeavy.primaryNeighbors():
				if atom != heavy:
					coPlanar.append(atom.xformCoord())
	else:
		coPlanar = None

	hydLocs = hydLocs + bondPositions(heavyLoc, geom, bondLen, bondedLocs,
							coPlanar=coPlanar)
	return hydLocs
コード例 #19
0
ファイル: hbond.py プロジェクト: davem22101/semanticscience
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
コード例 #20
0
ファイル: hbond.py プロジェクト: davem22101/semanticscience
def addHydrogens(atomList, *args):
	"""Add hydrogens to maximize h-bonding and minimize steric clashes.

	   First, add hydrogens that are fixed and always present.  Then,
	   find H-bonds only to possibly-hydrogen-needing donors.  For
	   aromatic nitogens protonate at least one on a six-membered ring.
	   For remaining rotamers with H-bonds, place hydrogens
	   to maximize H-bond interactions.  Then, for remaining rotamers,
	   minimize steric clashes via torsion-driving.

	   May not work well for atoms that are already partially
	   hydrogenated.
	"""

	if debug:
		print "here (test)"
	if atomList:
		if len(atomList[0].molecule.coordSets) > 1:
			replyobj("Adding H-bond-preserving hydrogens to "
				"trajectories not supported.\n")
			return
	if debug:
		print "here 2"
	global addAtoms, typeInfo4Atom, namingSchemas, hydrogenTotals, \
					idatmType, inversionCache, coordinations
	typeInfo4Atom, namingSchemas, hydrogenTotals, idatmType, hisNs, \
							coordinations = args
	inversionCache = {}
	addAtoms = {}
	for atom in atomList:
		addAtoms[atom] = 1
	from chimera import replyobj
	replyobj.status("Categorizing heavy atoms\n", blankAfter=0)
	problemAtoms = []
	fixed = []
	flat = []
	aroNrings = {}
	global aroAmines
	aroAmines = {}
	unsaturated = []
	saturated = []
	finished = {}
	hbondInfo = {}
	for a, crds in coordinations.items():
		hbondInfo[a] = [(True, crd) for crd in crds]
	for atom in atomList:
		bondingInfo = typeInfo4Atom[atom]
		numBonds = _numBonds(atom)
		substs = bondingInfo.substituents
		if numBonds >= substs:
			continue
		
		geom = bondingInfo.geometry
		if numBonds == 0:
			unsaturated.append(atom)
		elif numBonds == 1:
			if geom == linear:
				fixed.append(atom)
			elif geom == planar:
				if substs == 2:
					unsaturated.append(atom)
				else:
					if idatmType[atom] == 'Npl' \
					and idatmType[
					atom.primaryNeighbors()[0]] == 'Car':
						# primary aromatic amine
						aroAmines[atom] = True
					else:
						flat.append(atom)
			elif geom == tetrahedral:
				if substs < 4:
					unsaturated.append(atom)
				else:
					saturated.append(atom)
			else:
				raise AssertionError, "bad geometry for atom" \
					 " (%s) with one bond partner" \
					 % atom.oslIdent()
		elif numBonds == 2:
			if geom == tetrahedral:
				if substs == 3:
					unsaturated.append(atom)
				else:
					fixed.append(atom)
			elif geom == planar:
				aroNring = None
				if atom.element.number == 7:
					for ring in atom.minimumRings():
						if ring.aromatic():
							aroNring = ring
							break
				if aroNring:
					if aroNrings.has_key(aroNring):
						aroNrings[aroNring].append(atom)
					else:
						aroNrings[aroNring] = [atom]
				else:
					fixed.append(atom)
			else:
				raise AssertionError, "bad geometry for atom" \
					 " (%s) with two bond partners" \
					 % atom.oslIdent()
		elif numBonds == 3:
			if geom != tetrahedral:
				raise AssertionError, "bad geometry for atom" \
					 " (%s) with three bond partners" \
					 % atom.oslIdent()
			fixed.append(atom)
			
	# protonate aromatic nitrogens if only one nitrogen in ring:
	multiNrings = {}
	aroNs = {}
	for ring, ns in aroNrings.items():
		if len(ns) == 1:
			fixed.append(ns[0])
		elif ns[0] in hisNs:
			for n in ns:
				if hisNs[n]:
					fixed.append(n)
		else:
			multiNrings[ring] = ns
			for n in ns:
				aroNs[n] = True
	if debug:
		print len(fixed), "fixed"
		print len(flat), "flat"
		print len(aroAmines), "primary aromatic amines"
		print len(multiNrings), "aromatic multi-nitrogen rings"
		print len(unsaturated), "unsaturated"
		print len(saturated), "saturated rotamers"

	replyobj.status("Building search tree of atom positions\n",
								blankAfter=0)
	# since adaptive search tree is static, it will not include
	# hydrogens added after this; they will have to be found by
	# looking off their heavy atoms

	for needCoplanar in [False, True]:
		if needCoplanar:
			atoms = flat
			replyobj.status("Adding co-planar hydrogens\n",
								blankAfter=0)
		else:
			atoms = fixed
			replyobj.status("Adding simple fixed hydrogens\n",
								blankAfter=0)
			
		for atom in atoms:
			bondingInfo = _typeInfo(atom)
			geom = bondingInfo.geometry
			bondLength = bondWithHLength(atom, geom)
			neighbors = []
			neighborAtoms = []
			for n in atom.primaryNeighbors():
				neighborAtoms.append(n)
				neighbors.append(n.xformCoord())

			coplanar = []
			if needCoplanar:
				for na in neighborAtoms:
					for nna in na.primaryNeighbors():
						if nna != atom:
							coplanar.append(
							  nna.xformCoord())
			if len(coplanar) > 2:
				problemAtoms.append(atom)
				continue
			Hpositions = bondPositions(atom.xformCoord(), geom,
				bondLength, neighbors, coPlanar=coplanar)
			_attachHydrogens(atom, Hpositions)
			finished[atom] = True
			hbondInfo[atom] = (Hpositions, [])
		
	from FindHBond import findHBonds, recDistSlop, recAngleSlop
	from chimera import replyobj

	replyobj.status("Finding hydrogen bonds\n", blankAfter=0)
	donors = {}
	acceptors = {}
	for atom in unsaturated:
		donors[atom] = unsaturated
		acceptors[atom] = unsaturated
	for atom in saturated:
		donors[atom] = saturated
	for ring, ns in multiNrings.items():
		for n in ns:
			donors[n] = ring
			acceptors[n] = ring
	for atom in aroAmines:
		donors[atom] = aroAmines
		acceptors[atom] = aroAmines

	hbonds = findHBonds(chimera.openModels.list(
				modelTypes=[chimera.Molecule]),
				distSlop=recDistSlop, angleSlop=recAngleSlop)
	replyobj.info("%d hydrogen bonds\n" % len(hbonds))
	# want to assign hydrogens to strongest (shortest) hydrogen
	# bonds first, so sort by distance
	replyobj.status("Sorting hydrogen bonds by distance\n", blankAfter=0)
	sortableHbonds = []
	for d, a in hbonds:
		if d in donors:
			sortableHbonds.append((d.xformCoord().sqdistance(
					a.xformCoord()), False, (d, a)))
		if a in acceptors:
			sortableHbonds.append((d.xformCoord().sqdistance(
					a.xformCoord()), True, (d, a)))
	sortableHbonds.sort()
	replyobj.status("Organizing h-bond info\n", blankAfter=0)
	hbonds = {}
	ambiguous = {}
	relBond = {}
	for dsq, isAcc, hbond in sortableHbonds:
		hbonds[hbond] = isAcc
		for da in hbond:
			relBond.setdefault(da, []).append(hbond)
	processed = set()
	breakAmbiguous = False
	breakNring = False
	candidates = {}
	candidates.update(donors)
	candidates.update(acceptors)
	pruned = set()
	prunedBy = {}
	reexamine = {}
	replyobj.status("Adding hydrogens by h-bond strength\n", blankAfter=0)
	while len(processed) < len(hbonds):
		replyobj.status("Adding hydrogens by h-bond strength (%d/%d)\n" % (len(processed), len(hbonds)), blankAfter=0)
		processedOne = False
		seenAmbiguous = {}
		for dsq, isAcc, hbond in sortableHbonds:
			if hbond in processed:
				continue
			d, a = hbond
			if hbond in pruned:
				if hbond in reexamine:
					del reexamine[hbond]
					if debug:
						print "re-examining", hbond[0].oslIdent(), "->", hbond[1].oslIdent()
				elif not breakAmbiguous:
					seenAmbiguous[d] = True
					seenAmbiguous[a] = True
					continue
			if (d not in candidates or d in finished) \
			and (a not in candidates or a in finished):
				# not relevant
				processed.add(hbond)
				continue

			if (a, d) in hbonds \
			and a not in finished \
			and d not in finished \
			and not _resolveAnilene(d, a, aroAmines, hbondInfo) \
			and _angleCheck(d, a, hbondInfo) == (True, True):
				# possibly still ambiguous

				# if one or both ends are aromatic nitrogen,
				# then ambiguity depends on whether other
				# nitrogens in ring are finished and are
				# acceptors...
				nring = False
				try:
					ns = multiNrings[acceptors[a]]
				except (KeyError, TypeError):
					ns = []
				if len(ns) > 1:
					nring = True
					for n in ns:
						if n not in finished:
							break
						protons, lps = hbondInfo[n]
						if protons:
							break
					else:
						# unambiguous, but in the
						# reverse direction!
						processed.add(hbond)
						if debug:
							print "reverse ambiguity resolved"
						continue
				unambiguous = False
				try:
					ns = multiNrings[donors[d]]
				except (KeyError, TypeError):
					ns = []
				if len(ns) > 1:
					nring = True
					for n in ns:
						if n not in finished:
							break
						protons, lps = hbondInfo[n]
						if protons:
							break
					else:
						if debug:
							print "ambiguity resolved due to ring acceptors"
						unambiguous = True

				if not unambiguous:
					if not breakAmbiguous \
					or nring and not breakNring:
						if debug:
							print "postponing", [a.oslIdent() for a in hbond]
						seenAmbiguous[d] = True
						seenAmbiguous[a] = True
						if hbond not in pruned:
							_doPrune(hbond, pruned, relBond, processed, prunedBy)
						continue
					if nring:
						if debug:
							print "breaking ambiguous N-ring hbond"
					else:
						if debug:
							print "breaking ambiguous hbond"
			if (a, d) in hbonds:
				if a in finished:
					if debug:
						print "breaking ambiguity because acceptor finished"
				elif d in finished:
					if debug:
						print "breaking ambiguity because donor finished"
				elif _resolveAnilene(d, a, aroAmines, hbondInfo):
					if debug:
						print "breaking ambiguity by resolving anilene"
				elif _angleCheck(d, a, hbondInfo) != (True, True):
					if debug:
						print "breaking ambiguity due to angle check"
			processed.add(hbond)
			from math import sqrt
			if debug:
				print "processed", d.oslIdent(), "->", a.oslIdent(), sqrt(dsq)
			# if donor or acceptor is tet,
			# see if geometry can work out
			if d not in finished and _typeInfo(d).geometry == 4:
				if not _tetCheck(d, a, hbondInfo, False):
					continue
			if a not in finished and _typeInfo(a).geometry == 4:
				if d in finished:
					checks = []
					for b in d.primaryNeighbors():
						if b.element.number == 1:
							checks.append(b)
				else:
					checks = [d]
				tetOkay = True
				for c in checks:
					if not _tetCheck(a, c, hbondInfo, True):
						tetOkay = False
						break
				if not tetOkay:
					continue
			if a in finished:
				# can d still donate to a?
				if not _canAccept(d, a, *hbondInfo[a]):
					# can't
					continue
				hbondInfo.setdefault(d, []).append((False, a))
			elif d in finished:
				# can d still donate to a?
				noProtonsOK = d in aroNs and _numBonds(d) == 2
				if not _canDonate(d, a, noProtonsOK,
								*hbondInfo[d]):
					# nope
					continue
				hbondInfo.setdefault(a, []).append((True, d))
			else:
				addAtoD, addDtoA = _angleCheck(d, a,
								hbondInfo)
				if not addAtoD and not addDtoA:
					continue
				if addAtoD:
					hbondInfo.setdefault(d, []).append(
								(False, a))
				if addDtoA:
					hbondInfo.setdefault(a, []).append(
								(True, d))
			if (a, d) in hbonds:
				processed.add((a, d))
			processedOne = True
			breakAmbigous = False
			breakNring = False

			if hbond not in pruned:
				_doPrune(hbond, pruned, relBond, processed, prunedBy)
			for end in hbond:
				if end in finished or end not in candidates:
					continue
				if end not in hbondInfo:
					# steric clash from other end
					if debug:
						print "no hbonds left for", end.oslIdent()
					continue
				if debug:
					print "try to finish", end.oslIdent(),
					for isAcc, da in hbondInfo[end]:
						if isAcc:
							print " accepting from", da.oslIdent(),
						else:
							print " donating to", da.oslIdent(),
					print
				didFinish = _tryFinish(end, hbondInfo, finished,
						aroAmines, prunedBy, processed)
				if not didFinish:
					continue
				if debug:
					print "finished", end.oslIdent()

				# if this atom is in candidates 
				# then actually add any hydrogens
				if end in candidates and hbondInfo[end][0]:
					_attachHydrogens(end, hbondInfo[end][0])
					if debug:
						print "protonated", end.oslIdent()
					# if ring nitrogen, eliminate
					# any hbonds where it is an acceptor
					if isinstance(donors[end],chimera.Ring):
						for rb in relBond[end]:
							if rb[1] != end:
								continue
							processed.add(rb)
								
			if a in seenAmbiguous or d in seenAmbiguous:
				# revisit previously ambiguous
				if debug:
					print "revisiting previous ambiguous"
				for da in hbond:
					for rel in relBond[da]:
						if rel in processed:
							continue
						if rel not in pruned:
							continue
						reexamine[rel] = True
				break
		if breakAmbiguous and not processedOne:
			breakNring = True
		breakAmbiguous = not processedOne
	numFinished = 0
	for a in candidates.keys():
		if a in finished:
			numFinished += 1
	if debug:
		print "finished", numFinished, "of", len(candidates), "atoms"

	replyobj.status("Adding hydrogens to primary aromatic amines\n",
								blankAfter=0)
	if debug:
		print "primary aromatic amines"
	for a in aroAmines:
		if a in finished:
			continue
		if a not in candidates:
			continue
		if debug:
			print "amine", a.oslIdent()
		finished[a] = True
		numFinished += 1
		
		# point protons right toward acceptors;
		# if also accepting, finish as tet, otherwise as planar
		acceptFrom = None
		targets = []
		atPos = a.xformCoord()
		for isAcc, other in hbondInfo.get(a, []):
			if isAcc:
				if not acceptFrom:
					if debug:
						print "accept from", other.oslIdent()
					acceptFrom = other.xformCoord()
				continue
			if debug:
				print "possible donate to", other.oslIdent()
			# find nearest lone pair position on acceptor
			target = _findTarget(a, atPos, other, not isAcc,
							hbondInfo, finished)
			# make sure this proton doesn't form
			# an angle < 90 degrees with any other bonds
			badAngle = False
			for bonded in a.primaryNeighbors():
				if chimera.angle(bonded.xformCoord(),
							atPos, target) < 90.0:
					badAngle = True
					break
			if badAngle:
				if debug:
					print "bad angle"
				continue
			if targets and chimera.angle(targets[0], atPos,
								target) < 90.0:
				if debug:
					print "bad angle"
				continue
			targets.append(target)
			if len(targets) > 1:
				break

		positions = []
		for target in targets:
			vec = target - atPos
			vec.normalize()
			positions.append(atPos +
					vec * bondWithHLength(a, _typeInfo(a).geometry))

		if len(positions) < 2:
			if acceptFrom:
				geom = 4
				knowns = positions + [acceptFrom]
				coPlanar = None
			else:
				geom = 3
				knowns = positions
				coPlanar = []
				for bonded in a.primaryNeighbors():
					for b2 in bonded.primaryNeighbors():
						if a == b2:
							continue
						coPlanar.append(b2.xformCoord())
			sumVec = chimera.Vector()
			for x in a.primaryNeighbors():
				vec = x.xformCoord() - atPos
				vec.normalize()
				sumVec += vec
			for k in knowns:
				vec = k - atPos
				vec.normalize()
				sumVec += vec
			sumVec.negate()
			sumVec.normalize()

			newPos = bondPositions(atPos, geom, bondWithHLength(a, geom),
				[nb.xformCoord() for nb in a.primaryNeighbors()] + knowns,
				coPlanar=coPlanar)
			positions.extend(newPos)
		_attachHydrogens(a, positions)
		if acceptFrom:
			accVec = acceptFrom - atPos
			accVec.length = vdwRadius(a)
			accs = [atPos + accVec]
		else:
			accs = []
		hbondInfo[a] = (positions, accs)

	if debug:
		print "finished", numFinished, "of", len(candidates), "atoms"

	replyobj.status("Using steric criteria to resolve partial h-bonders\n",
								blankAfter=0)
	for a in candidates.keys():
		if a in finished:
			continue
		if a not in hbondInfo:
			continue
		finished[a] = True
		numFinished += 1

		bondingInfo = _typeInfo(a)
		geom = bondingInfo.geometry

		numBonds = _numBonds(a)
		hydsToPosition = bondingInfo.substituents - numBonds
		openings = geom - numBonds

		hbInfo = hbondInfo[a]
		towardAtom = hbInfo[0][1]
		if debug:
			print a.oslIdent(), "toward", towardAtom.oslIdent(),
		towardPos = towardAtom.xformCoord()
		toward2 = away2 = None
		atPos = a.xformCoord()
		if len(hbInfo) > 1:
			toward2 = hbInfo[1][1].xformCoord()
			if debug:
				print "and toward", hbInfo[1][1].oslIdent()
		elif openings > 3:
			# okay, we need an "away from" atom just to position
			# the rotamer
			away2, dist, nearA = findNearest(atPos, a, [towardAtom],
								_nearDist)
			if debug:
				print "and away from nearest other",
				if away2:
					print nearA.oslIdent(), "[%.2f]" % dist
				else:
					print "(none)"
		else:
			if debug:
				print "with no other positioning determinant"
		bondedPos = []
		for bonded in a.primaryNeighbors():
			bondedPos.append(bonded.xformCoord())
		positions = bondPositions(atPos, geom,
			bondWithHLength(a, geom), bondedPos,
			toward=towardPos, toward2=toward2, away2=away2)
		if len(positions) == hydsToPosition:
			# easy, do them all...
			_attachHydrogens(a, positions)
			continue

		used = {}
		for isAcc, other in hbInfo:
			nearest = None
			otherPos = other.xformCoord()
			for pos in positions:
				dsq = (pos - otherPos).sqlength()
				if not nearest or dsq < nsq:
					nearest = pos
					nsq = dsq
			if nearest in used:
				continue
			used[nearest] = isAcc
			
		remaining = []
		for pos in positions:
			if pos in used:
				continue
			remaining.append(pos)
		# definitely protonate the positions where we donate...
		protonate = []
		for pos, isAcc in used.items():
			if not isAcc:
				protonate.append(pos)
		# ... and the "roomiest" remaining positions.
		rooms = roomiest(remaining, a, _roomDist)
		needed = hydsToPosition - len(protonate)
		protonate.extend(rooms[:needed])
		# then the least sterically challenged...
		_attachHydrogens(a, protonate)
		hbondInfo[a] = (protonate, rooms[needed:])

	if debug:
		print "finished", numFinished, "of", len(candidates), "atoms"

	replyobj.status("Adding hydrogens to non-h-bonding atoms\n",
								blankAfter=0)
	for a in candidates.keys():
		if a in finished:
			continue
		if a in aroNs:
			continue
		finished[a] = True
		numFinished += 1

		bondingInfo = _typeInfo(a)
		geom = bondingInfo.geometry

		primaryNeighbors = a.primaryNeighbors()
		numBonds = len(primaryNeighbors)
		hydsToPosition = bondingInfo.substituents - numBonds
		openings = geom - numBonds

		away = None
		away2 = None
		toward = None
		atPos = a.xformCoord()
		if debug:
			print "position", a.oslIdent(),
		if openings > 2:
			# okay, we need an "away from" atom for positioning
			#
			# if atom is tet with one bond (i.e. possible positions
			# describe a circle away from the bond), then use the
			# center of the circle as the test position rather 
			# than the atom position itself
			if geom == 4 and openings == 3:
				away, dist, awayAtom = findRotamerNearest(atPos,
						idatmType[a], a,
						primaryNeighbors[0], 3.5)
			else:
				away, dist, awayAtom = findNearest(atPos, a,
								[], _nearDist)

			# actually, if the nearest atom is a metal and we have
			# a free lone pair, we want to position (the lone
			# pair) towards the metal
			if awayAtom and awayAtom.element in metals \
			  and geom - bondingInfo.substituents > 0:
				if debug:
					print "towards metal", awayAtom.oslIdent(), "[%.2f]" % dist,
				toward = away
				away = None
			else:
				if debug:
					print "away from",
					if awayAtom:
						print awayAtom.oslIdent(), "[%.2f]" % dist,
					else:
						print "(none)",
		if openings > 3 and away is not None:
			# need another away from
			away2, dist, nearA = findNearest(atPos, a, [awayAtom],
								_nearDist)
			if debug:
				print "and away from",
				if nearA:
					print nearA.oslIdent(), "[%.2f]" % dist,
				else:
					print "(none)",
		if debug:
			print
		bondedPos = []
		for bonded in primaryNeighbors:
			bondedPos.append(bonded.xformCoord())
		positions = bondPositions(atPos, geom,
			bondWithHLength(a, geom),
			bondedPos, toward=toward, away=away, away2=away2)
		if len(positions) == hydsToPosition:
			# easy, do them all...
			_attachHydrogens(a, positions)
			continue

		# protonate "roomiest" positions
		_attachHydrogens(a, roomiest(positions, a, _roomDist)[:hydsToPosition])

	if debug:
		print "finished", numFinished, "of", len(candidates), "atoms"

	replyobj.status("Deciding aromatic nitrogen protonation\n",
								blankAfter=0)
	# protonate one N of an aromatic multiple-N ring if none are yet
	# protonated
	for ns in multiNrings.values():
		anyBonded = True
		Npls = []
		for n in ns:
			if _numBonds(n) > 2:
				break
			if n not in coordinations \
					and _typeInfo(n).substituents == 3:
				Npls.append(n)
		else:
			anyBonded = False
		if anyBonded:
			if debug:
				print ns[0].oslIdent(), "ring already protonated"
			continue
		if not Npls:
			Npls = ns

		positions = []
		for n in Npls:
			bondedPos = []
			for bonded in n.primaryNeighbors():
				bondedPos.append(bonded.xformCoord())
			positions.append(bondPositions(n.xformCoord(), planar,
				bondWithHLength(n, planar), bondedPos)[0])
		if len(positions) == 1:
			if debug:
				print "protonate precise ring", Npls[0].oslIdent()
			_attachHydrogens(Npls[0], positions)
		else:
			if debug:
				print "protonate roomiest ring", roomiest(positions, Npls, _roomDist)[0][0].oslIdent()
			_attachHydrogens(*roomiest(positions, Npls, _roomDist)[0])
	# now correct IDATM types of these rings...
	for ns in multiNrings.values():
		for n in ns:
			if len(n.bonds) == 3:
				n.idatmType = "Npl"
			else:
				n.idatmType = "N2"

	replyobj.status("Hydrogens added\n")
	if problemAtoms:
		replyobj.error("Problems adding hydrogens to %d atom(s); see Reply Log for details\n" % len(problemAtoms))
		from chimera.misc import chimeraLabel
		replyobj.info("Did not protonate following atoms:\n%s\n"
			% "\t".join(map(chimeraLabel, problemAtoms)))
	addAtoms = None
	typeInfo4Atom = namingSchemas = hydrogenTotals = idatmType \
			= aroAmines = inversionCache = coordinations = None
コード例 #21
0
def hydPositions(heavy, includeLonePairs=False):
    """Return list of positions for hydrogens attached to this atom.
	   If a hydrogen could be in one of several positions, don't
	   return any of those.
	"""

    # first, find known attached atoms
    bondedHeavys = []
    hyds = []
    for atom in heavy.primaryNeighbors():
        if atom.element.number > 1:
            bondedHeavys.append(atom)
        else:
            hyds.append(atom)

    # convert to Points
    hydLocs = []
    for hyd in hyds:
        hydLocs.append(hyd.xformCoord())

    if hydLocs and not includeLonePairs:
        # explicit hydrogens "win" over atom types
        return hydLocs

    if typeInfo.has_key(heavy.idatmType):
        info = typeInfo[heavy.idatmType]
        geom = info.geometry
        if includeLonePairs:
            subs = geom
        else:
            subs = info.substituents
        bondedLocs = hydLocs[:]
        for bHeavy in bondedHeavys:
            bondedLocs.append(bHeavy.xformCoord())
    else:
        return hydLocs

    knownSubs = len(bondedLocs)
    if knownSubs >= subs or knownSubs == 0:
        return hydLocs
    # above eliminates 'single' geometry

    if knownSubs == 1 and geom == tetrahedral:
        # rotamer
        return hydLocs

    maxSubs = geom
    if maxSubs - subs > 0:
        # the "empty" bond could be anywhere
        return hydLocs

    heavyLoc = heavy.xformCoord()
    bondLen = Element.bondLength(heavy.element, Element(1))

    if geom == planar:
        coPlanar = []
        for bHeavy in bondedHeavys:
            try:
                bhGeom = typeInfo[bHeavy.idatmType].geometry
            except KeyError:
                bhGeom = None
            if bhGeom != planar:
                continue
            for atom in bHeavy.primaryNeighbors():
                if atom != heavy:
                    coPlanar.append(atom.xformCoord())
    else:
        coPlanar = None

    hydLocs = hydLocs + bondPositions(
        heavyLoc, geom, bondLen, bondedLocs, coPlanar=coPlanar)
    return hydLocs
コード例 #22
0
ファイル: hbond.py プロジェクト: davem22101/semanticscience
def _resolveAnilene(donor, acceptor, aroAmines, hbondInfo):
	# donor/acceptor are currently ambiguous;  if donor and/or acceptor are
	# anilenes, see if they can be determined to prefer to donate/accept
	# respectively (if a proton and/or lone pair has been added, see if
	# vector to other atom is more planar or more tetrahedral)

	if donor in aroAmines and donor in hbondInfo:
		toward = None
		for isAcc, da in hbondInfo[donor]:
			if isAcc:
				return True
			if toward:
				break
			toward = da.xformCoord()
		else:
			# one proton attached
			donorPos = donor.xformCoord()
			acceptorPos = acceptor.xformCoord()
			attached = [
				donor.primaryNeighbors()[0].xformCoord()]
			planars = bondPositions(donorPos, 3, N_H, attached,
							toward=toward)
			planarDist = None
			for planar in planars:
				dist = (acceptorPos - planar).sqlength()
				if planarDist is None or dist < planarDist:
					planarDist = dist

			for tetPos in bondPositions(donorPos, 4, N_H, attached):
				if (tetPos - acceptorPos).sqlength() \
								< planarDist:
					# closer to tet position,
					# prefer acceptor-like behavior
					return False
			if debug:
				print "resolving", donor.oslIdent(), "->", acceptor.oslIdent(), "because of donor"
			return True

	if acceptor in aroAmines and acceptor in hbondInfo:
		toward = None
		for isAcc, da in hbondInfo[acceptor]:
			if isAcc:
				return False
			if toward:
				break
			toward = da.xformCoord()
		else:
			# one proton attached
			donorPos = donor.xformCoord()
			acceptorPos = acceptor.xformCoord()
			attached = [acceptor.primaryNeighbors()[0].xformCoord()]
			planars = bondPositions(acceptorPos, 3, N_H, attached,
							toward=toward)
			planarDist = None
			for planar in planars:
				dist = (acceptorPos - planar).sqlength()
				if planarDist is None or dist < planarDist:
					planarDist = dist

			for tetPos in bondPositions(acceptorPos, 4, N_H,
								attached):
				if (tetPos - donorPos).sqlength() \
								< planarDist:
					# closer to tet position,
					# prefer acceptor-like behavior
					if debug:
						print "resolving", donor.oslIdent(), "->", acceptor.oslIdent(), "because of acceptor"
					return True
			return False
	return False
コード例 #23
0
def donUpsilonTau(donor, donorHyds, acceptor,
  sp2Or2, sp2OupsilonLow, sp2OupsilonHigh, sp2Otheta, sp2Otau,
  sp3Or2, sp3OupsilonLow, sp3OupsilonHigh, sp3Otheta, sp3Otau, sp3Ophi,
  sp3Nr2, sp3NupsilonLow, sp3NupsilonHigh, sp3Ntheta, sp3Ntau, sp3NupsilonN,
  genR2, genUpsilonLow, genUpsilonHigh, genTheta, tauSym):

	if base.verbose:
		print "donUpsilonTau"

	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"
		return testUpsilonTauAcceptor(donor, donorHyds, acceptor,
			sp2Or2, sp2OupsilonLow, sp2OupsilonHigh, sp2Otheta,
			sp2Otau, tauSym)
	elif element == 'O' and geom == tetrahedral \
	or element == 'N' and geom == planar:
		if base.verbose:
			print "planar N or tet O"
		return testUpsilonTauAcceptor(donor, donorHyds, acceptor,
			sp3Or2, sp3OupsilonLow, sp3OupsilonHigh, sp3Otheta,
			sp3Otau, tauSym)
	elif element == 'N' and geom == tetrahedral:
		if base.verbose:
			print "tet N"
		# test upsilon at the N
		# see if lone pairs point at the donor
		bondedPos = []
		for bonded in acceptor.primaryNeighbors():
			bondedPos.append(bonded.xformCoord())
		if len(bondedPos) > 1:
			ap = acceptor.xformCoord()
			dp = donor.xformCoord()
			lonePairs = bondPositions(ap, tetrahedral, 1.0,
								bondedPos)
			for lp in lonePairs:
				upPos = ap - (lp - ap)
				ang = angle(upPos, ap, dp)
				if ang >= sp3NupsilonN:
					if base.verbose:
						print "upsilon(N) okay" \
							" (%g >= %g)" % (ang,
							sp3NupsilonN)
					break
			else:
				if base.verbose:
					print "all upsilon(N) failed (< %g)" % (
								sp3NupsilonN)
				return 0
		elif base.verbose:
			print "lone pair positions indeterminate at N;" \
						"upsilon(N) default okay"
		return testUpsilonTauAcceptor(donor, donorHyds, acceptor,
			sp3Nr2, sp3NupsilonLow, sp3NupsilonHigh, sp3Ntheta,
			sp3Ntau, tauSym)
	else:
		if base.verbose:
			print "generic acceptor"
		if acceptor.element.name == "S":
			genR2 = sulphurCompensate(genR2)
		return testUpsilonTauAcceptor(donor, donorHyds, acceptor,
			genR2, genUpsilonLow, genUpsilonHigh, genTheta,
			None, None)
	if base.verbose:
		print "failed criteria"
	return 0
コード例 #24
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)