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
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 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
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 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
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
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
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)
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
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