Exemple #1
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
Exemple #2
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
Exemple #3
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
Exemple #4
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
Exemple #5
0
def addNewC(last_bbone_ats, new_res, phi, **kw):

    last_C = last_bbone_ats['C']

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

    new_C = addDihedralAtom('C',
                            chimera.Element(6),
                            new_CA,
                            new_N,
                            last_C,
                            DIST_C_CA,
                            109.5,
                            phi,
                            residue=new_res)
    new_C.drawMode = kw['atomDrawMode']
    new_C.bfactor = kw['bFactor']

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

    return new_C
Exemple #6
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
Exemple #7
0
    def join(self, molecule, acceptor, donor, newres=False):
        """
        Take a molecule and bond it to `self.mol`, copying the atoms in the
        process so they're contained in the same `chimera.Molecule` object.

        Parameters
        ----------
        molecule : Compound
            The molecule that will be attached to `self`
        acceptor : chimera.Atom
            Atom of `self` that will participate in the new bond
        donor : chimera.Atom
            Atom of `molecule` that will participate in the new bond
        newres : bool
            If True, don't reuse `acceptor.residue` for the new molecule,
            and create a new blank one instead.

        Returns
        -------
        built_atoms : dict
            Maps original atoms in `molecules` to their new counterparts
            in `self.mol`.

        Notes
        -----
        .. note ::

            Chimera does not allow bonds between different chimera.Molecule
            objects, so firstly, we have to copy the atoms of ``molecule`` to
            ``self.mol`` and, only then, make the joining bond.

            It traverses the atoms of ``molecule`` and adds a copy of each
            of them to ``self.mol`` using ``chimera.molEdit.addAtom`` in the same spot
            of space. All the bonds are preserved and, finally, bond the two molecules.

            The algorithm starts by adding the bonding atom of ``molecule`` (``donor``), to
            the ``sprouts`` list. Then, the loop starts:

            .. code-block:: python

                while sprouts contains atoms:
                    sprout = sprouts.pop(0)
                    copy sprout to self.mol
                    for each neighbor of sprout
                        copy neighbor to self.mol
                        if neighbor itself has more than one neighbor (ie, sprout)
                            add neighbor to sprouts

            Along the way, we have to take care of already processed sprouts, so we
            don't repeat ourselves. That's what the built_atoms dict is for.

            Also, instead of letting addAtom guess new serial numbers, we calculate
            them beforehand by computing the highest serial number in ``self.mol``
            prior to the additions and then incremeting one by one on a per-element
            basis.

        .. todo ::

            This code is UGLY. Find a better way!
        """
        target = acceptor
        sprouts = [donor]
        res = _dummy_mol().residues[0] if newres else target.residue
        # I think I was trying to keep a copy of the original residue,
        # but this does not copy anything. Since lists are mutable, we
        # are just aliasing res.atoms with res_atoms.
        # Maybe I want something like:
        # res_atoms = res.atoms[:] # ?
        res_atoms = res.atoms

        i = max(a.serialNumber for a in self.mol.atoms)
        index = box.highest_atom_indices(self.mol)
        for a in molecule.mol.atoms:
            try:
                index[a.element.name] += 1
            except KeyError:
                index[a.element.name] = 1
            finally:
                a.name = a.element.name + str(index[a.element.name])
        built_atoms = {}
        while sprouts:
            sprout = sprouts.pop(0)  # get first atom
            if sprout.name in res.atomsMap:
                target = res.atomsMap[sprout.name][-1]
            else:
                i += 1
                built_atoms[sprout] = target = addAtom(sprout.name,
                                                       sprout.element,
                                                       res,
                                                       sprout.coord(),
                                                       bondedTo=target,
                                                       serialNumber=i)

            for a in sprout.neighbors:
                if a.element.number == 1:
                    continue
                if a.name not in res.atomsMap:
                    needBuild = True
                else:
                    # atom is already present, but it can be part of a cycle
                    # if we get to it it's because another atom is linking it
                    needBuild = False
                    built = res.atomsMap[a.name][-1]
                if needBuild:
                    i += 1
                    built_atoms[a] = built = addAtom(a.name,
                                                     a.element,
                                                     res,
                                                     a.coord(),
                                                     bondedTo=target,
                                                     serialNumber=i)
                    # if a has more than one neighbor:
                    if len(a.neighbors) > 1:
                        sprouts.append(a)  # this new atom can be a new sprout
                # link!
                if built not in target.bondsMap and built not in res_atoms:
                    addBond(target, built)

        self.built_atoms.append(
            {a.serialNumber: b
             for (a, b) in built_atoms.items()})
        return built_atoms
Exemple #8
0
def useRotamer(oldRes, rots, log=False):
    """Takes a Residue instance and a list of one or more rotamers (as
	   returned by getRotamers) and swaps the Residue's side chain with
	   the given rotamers.  If more than one rotamer is in the list,
	   then alt locs will be used to distinguish the different side chains.
	"""
    try:
        oldN = oldRes.atomsMap["N"][0]
        oldCA = oldRes.atomsMap["CA"][0]
        oldC = oldRes.atomsMap["C"][0]
    except KeyError:
        raise LimitationError("N, CA, or C missing from %s:"
                              " needed for side-chain pruning algorithm" %
                              oldRes)
    import string
    altLocs = string.ascii_uppercase + string.ascii_lowercase \
       + string.digits + string.punctuation
    if len(rots) > len(altLocs):
        raise LimitationError("Don't have enough unique alternate "
                              "location characters to place %d rotamers." %
                              len(rots))
    rotAnchors = {}
    for rot in rots:
        rotRes = rot.residues[0]
        try:
            rotAnchors[rot] = (rotRes.atomsMap["N"][0],
                               rotRes.atomsMap["CA"][0])
        except KeyError:
            raise LimitationError("N or CA missing from rotamer:"
                                  " cannot matchup with original residue")
    # prune old side chain
    retain = set([oldN, oldCA, oldC])
    deathrow = [nb for nb in oldCA.neighbors if nb not in retain]
    serials = {}
    while deathrow:
        prune = deathrow.pop()
        serials[prune.name] = getattr(prune, "serialNumber", None)
        for nb in prune.neighbors:
            if nb not in deathrow and nb not in retain:
                deathrow.append(nb)
        oldRes.molecule.deleteAtom(prune)

    totProb = sum([r.rotamerProb for r in rots])
    oldAtoms = set([a.name for a in oldRes.atoms])
    for i, rot in enumerate(rots):
        rotRes = rot.residues[0]
        rotN, rotCA = rotAnchors[rot]
        if len(rots) > 1:
            altLoc = altLocs[i]
            extra = " using alt loc %s" % altLoc
        else:
            altLoc = None
            extra = ""
        if log:
            replyobj.info(
                "Applying %s rotamer (chi angles: %s) to"
                " %s%s\n" %
                (rotRes.type, " ".join(["%.1f" % c
                                        for c in rot.chis]), oldRes, extra))
        from BuildStructure import changeResidueType
        changeResidueType(oldRes, rotRes.type)
        # add new side chain
        from chimera.molEdit import addAtom, addBond
        sprouts = [rotCA]
        while sprouts:
            sprout = sprouts.pop()
            builtSprout = oldRes.atomsMap[sprout.name][-1]
            for nb, b in sprout.bondsMap.items():
                try:
                    builtNBs = oldRes.atomsMap[nb.name]
                except KeyError:
                    needBuild = True
                else:
                    if nb.name in oldAtoms or len(builtNBs) == i + 1:
                        needBuild = False
                    else:
                        needBuild = True
                if needBuild:
                    if i == 0:
                        serial = serials.get(nb.name, None)
                    else:
                        serial = None
                    builtNB = addAtom(nb.name,
                                      nb.element,
                                      oldRes,
                                      nb.coord(),
                                      serialNumber=serial,
                                      bondedTo=builtSprout)
                    if altLoc:
                        builtNB.altLoc = altLoc
                    builtNB.occupancy = rot.rotamerProb / totProb
                    sprouts.append(nb)
                else:
                    builtNB = builtNBs[-1]
                if builtNB not in builtSprout.bondsMap:
                    addBond(builtSprout, builtNB)
Exemple #9
0
def getRotamers(res,
                phi=None,
                psi=None,
                cisTrans="trans",
                resType=None,
                lib="Dunbrack",
                log=False):
    """Takes a Residue instance and optionally phi/psi angles  
	   (if different from the Residue), residue type (e.g. "TYR"), and/or  
	   rotamer library name.  Returns a boolean and a list of Molecule  
	   instances.  The boolean indicates whether the rotamers are backbone  
	   dependent.  The Molecules are each a single residue (a rotamer) and  
	   are in descending probability order.  Each has an attribute  
	   "rotamerProb" for the probability and "chis" for the chi angles.
	"""
    # find n/c/ca early to identify swapping non-amino acid before
    # the NoResidueRotamersError gets raised, which will attempt
    # a swap for ALA/GLY (and result in a traceback)
    resAtomsMap = res.atomsMap
    try:
        n = resAtomsMap["N"][0]
        ca = resAtomsMap["CA"][0]
        c = resAtomsMap["C"][0]
    except KeyError:
        raise LimitationError("N, CA, or C missing from %s:"
                              " needed to position CB" % res)
    resType = resType or res.type
    if not phi and not psi:
        ignore, phi, psi, cisTrans = extractResInfo(res)
        if log:

            def _info(ang):
                if ang is None:
                    return "none"
                return "%.1f" % ang

            replyobj.info("%s: phi %s, psi %s" % (res, _info(phi), _info(psi)))
            if cisTrans:
                replyobj.info(" " + cisTrans)
            replyobj.info("\n")
    replyobj.status("Retrieving rotamers from %s library\n" %
                    getattr(lib, "displayName", lib))
    bbdep, params = getRotamerParams(resType,
                                     phi=phi,
                                     psi=psi,
                                     cisTrans=cisTrans,
                                     lib=lib)
    replyobj.status("Rotamers retrieved from %s library\n" %
                    getattr(lib, "displayName", lib))

    template = chimera.restmplFindResidue(resType, False, False)
    tmplMap = template.atomsMap
    tmplN = tmplMap["N"]
    tmplCA = tmplMap["CA"]
    tmplC = tmplMap["C"]
    tmplCB = tmplMap["CB"]
    from chimera.molEdit import addAtom, addDihedralAtom, addBond
    from chimera.match import matchPositions, _coordArray
    xform, rmsd = matchPositions(_coordArray([n, ca, c]),
                                 _coordArray([tmplN, tmplCA, tmplC]))
    ncoord = xform.apply(tmplN.coord())
    cacoord = xform.apply(tmplCA.coord())
    cbcoord = xform.apply(tmplCB.coord())
    from data import chiInfo
    info = chiInfo[resType]
    bondCache = {}
    angleCache = {}
    torsionCache = {}
    from chimera.bondGeom import bondPositions
    mols = []
    middles = {}
    ends = {}
    for i, rp in enumerate(params):
        m = chimera.Molecule()
        mols.append(m)
        m.name = "rotamer %d of %s" % (i + 1, res)
        r = m.newResidue(resType, ' ', 1, ' ')
        # can't use a local variable for r.atomsMap since we receive
        # only an unchanging copy of the map
        m.rotamerProb = rp.p
        m.chis = rp.chis
        rotN = addAtom("N", tmplN.element, r, ncoord)
        rotCA = addAtom("CA", tmplCA.element, r, cacoord, bondedTo=rotN)
        rotCB = addAtom("CB", tmplCB.element, r, cbcoord, bondedTo=rotCA)
        todo = []
        for i, chi in enumerate(rp.chis):
            n3, n2, n1, new = info[i]
            blen, angle = _lenAngle(new, n1, n2, tmplMap, bondCache,
                                    angleCache)
            n3 = r.atomsMap[n3][0]
            n2 = r.atomsMap[n2][0]
            n1 = r.atomsMap[n1][0]
            new = tmplMap[new]
            a = addDihedralAtom(new.name,
                                new.element,
                                n1,
                                n2,
                                n3,
                                blen,
                                angle,
                                chi,
                                bonded=True)
            todo.append(a)
            middles[n1] = [a, n1, n2]
            ends[a] = [a, n1, n2]

        # if there are any heavy non-backbone atoms bonded to template
        # N and they haven't been added by the above (which is the
        # case for Richardson proline parameters) place them now
        for tnnb in tmplN.bondsMap.keys():
            if tnnb.name in r.atomsMap or tnnb.element.number == 1:
                continue
            tnnbcoord = xform.apply(tnnb.coord())
            addAtom(tnnb.name, tnnb.element, r, tnnbcoord, bondedTo=rotN)

        # fill out bonds and remaining heavy atoms
        from chimera.idatm import typeInfo
        from chimera import distance
        done = set([rotN, rotCA])
        while todo:
            a = todo.pop(0)
            if a in done:
                continue
            tmplA = tmplMap[a.name]
            for bonded, bond in tmplA.bondsMap.items():
                if bonded.element.number == 1:
                    continue
                try:
                    rbonded = r.atomsMap[bonded.name][0]
                except KeyError:
                    # use middles if possible...
                    try:
                        p1, p2, p3 = middles[a]
                        conn = p3
                    except KeyError:
                        p1, p2, p3 = ends[a]
                        conn = p2
                    t1 = tmplMap[p1.name]
                    t2 = tmplMap[p2.name]
                    t3 = tmplMap[p3.name]
                    xform, rmsd = matchPositions(_coordArray([p1, p2, p3]),
                                                 _coordArray([t1, t2, t3]))
                    pos = xform.apply(tmplMap[bonded.name].coord())
                    rbonded = addAtom(bonded.name,
                                      bonded.element,
                                      r,
                                      pos,
                                      bondedTo=a)
                    middles[a] = [rbonded, a, conn]
                    ends[rbonded] = [rbonded, a, conn]
                if a not in rbonded.bondsMap:
                    addBond(a, rbonded)
                if rbonded not in done:
                    todo.append(rbonded)
            done.add(a)
    return bbdep, mols
Exemple #10
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