Example #1
0
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
Example #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
Example #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
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))
Example #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
Example #6
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))
Example #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
Example #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
Example #9
0
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
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))
Example #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
Example #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
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)
Example #14
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
Example #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
Example #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
Example #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
Example #18
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
Example #19
0
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
Example #20
0
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
Example #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
Example #22
0
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
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
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)