def _phosphorylate(mols, status, deletes): replyobj.info("Deleting 5' phosphates from: %s\n" % ", ".join([str(r) for r in deletes])) from chimera.molEdit import addAtom for r in deletes: r.amberName += "5" for p in r.atomsMap['P']: o = None for nb in p.neighbors: for nnb in nb.neighbors: if nnb == p: continue if nnb.element.number > 1: o = nb continue r.molecule.deleteAtom(nnb) if nb != o: r.molecule.deleteAtom(nb) v = p.coord() - o.coord() sn = getattr(p, "serialNumber", None) r.molecule.deleteAtom(p) v.length = 0.96 addAtom("H5T", chimera.Element('H'), r, o.coord() + v, serialNumber=sn, bondedTo=o)
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
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
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 add_dummy_atom(self, where, name='dum', element=None, residue=None, bonded_to=None, serial=None): """ Adds a placeholder atom at the coordinates specified by `where` Parameters ---------- where : chimera.Atom or 3-tuple of float Coordinates of target location. A chimera.Atom can be supplied, in which case its coordinates will be used (via `.coord()`) name : str, optional Name for the new atom element : chimera.Element, optional Element of the new atom residue : chimera.Residue, optional Residue that will incorporate the new atom bonded_to : chimera.Atom, optional Atom that will form a bond with new atom serial : int Serial number that will be assigned to atom """ if isinstance(where, chimera.Atom): element = where.element if not element else element where = where.coord() else: element = chimera.Element('C') residue = self.mol.residues[-1] if not residue else residue return addAtom(name, element, residue, where, serial, bonded_to)
def _phosphorylate(mols, status, deletes): replyobj.info("Deleting 5' phosphates from: %s\n" % ", ".join([str(r) for r in deletes])) from chimera.molEdit import addAtom for r in deletes: r.amberName += "5" for p in r.atomsMap['P']: o = None for nb in p.neighbors: for nnb in nb.neighbors: if nnb == p: continue if nnb.element.number > 1: o = nb continue r.molecule.deleteAtom(nnb) if nb != o: r.molecule.deleteAtom(nb) v = p.coord() - o.coord() sn = getattr(p, "serialNumber", None) r.molecule.deleteAtom(p) v.length = 0.96 addAtom("H5T", chimera.Element('H'), r, o.coord() + v, serialNumber=sn, bondedTo=o)
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))
def placeFragment(fragment, resName, model="scratch", position=None): """place a Fragment (see Fragment.py) 'resName' is the name of the new residue that will contain the fragment. (It will be in the 'het' chain.) 'model' can either be a chimera.Molecule instance or a string. If the latter, then a new model is created with the string as its .name attribute. 'position' can either be a chimera.Point or None. If None, then the fragment is positioned at the center of the view. """ if isinstance(model, basestring): model = _newModel(model) r = _newResidue(model, resName) needFocus = False if position is None: if len(chimera.openModels.list()) == 1: needFocus = True xf = model.openState.xform position = xf.inverse().apply( Point(*chimera.viewer.camera.center)) # find fragment center x = y = z = 0.0 for element, xyz in fragment.atoms: x += xyz[0] y += xyz[1] z += xyz[2] numAtoms = len(fragment.atoms) fragCenter = Point(x / numAtoms, y / numAtoms, z / numAtoms) correction = position - fragCenter from chimera.molEdit import addAtom, genAtomName atoms = [] for element, xyz in fragment.atoms: atoms.append(addAtom(genAtomName(element, r), Element(element), r, Point(*xyz) + correction)) for indices, depict in fragment.bonds: r.molecule.newBond(atoms[indices[0]], atoms[indices[1]]) if needFocus: chimera.runCommand("focus") return r
def placeFragment(fragment, resName, model="scratch", position=None): """place a Fragment (see Fragment.py) 'resName' is the name of the new residue that will contain the fragment. (It will be in the 'het' chain.) 'model' can either be a chimera.Molecule instance or a string. If the latter, then a new model is created with the string as its .name attribute. 'position' can either be a chimera.Point or None. If None, then the fragment is positioned at the center of the view. """ if isinstance(model, basestring): model = _newModel(model) r = _newResidue(model, resName) needFocus = False if position is None: if len(chimera.openModels.list()) == 1: needFocus = True xf = model.openState.xform position = xf.inverse().apply(Point(*chimera.viewer.camera.center)) # find fragment center x = y = z = 0.0 for element, xyz in fragment.atoms: x += xyz[0] y += xyz[1] z += xyz[2] numAtoms = len(fragment.atoms) fragCenter = Point(x / numAtoms, y / numAtoms, z / numAtoms) correction = position - fragCenter from chimera.molEdit import addAtom, genAtomName atoms = [] for element, xyz in fragment.atoms: atoms.append( addAtom(genAtomName(element, r), Element(element), r, Point(*xyz) + correction)) for indices, depict in fragment.bonds: r.molecule.newBond(atoms[indices[0]], atoms[indices[1]]) if needFocus: chimera.runCommand("focus") return r
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 placeHelium(resName, model="scratch", position=None): """place a new helium atom 'resName' is the name of the new residue that will contain the helium. (It will be in the 'het' chain.) 'model' can either be a chimera.Molecule instance or a string. If the latter, then a new model is created with the string as its .name attribute. 'position' can either be a chimera.Point or None. If None, then the helium is positioned at the center of the view. """ if isinstance(model, basestring): model = _newModel(model) r = _newResidue(model, resName) if position is None: xf = model.openState.xform position = xf.inverse().apply(Point(*chimera.viewer.camera.center)) from chimera.molEdit import addAtom return addAtom('He1', Element('He'), r, position)
def newHydrogen(parentAtom, Hnum, totalHydrogens, namingSchema, pos): global _serial, _metals nearbyMetals = _metals.searchTree(pos.data(), _metalDist) for metal in nearbyMetals: if metal.molecule != parentAtom.molecule: continue metalPos = metal.coord() parentPos = parentAtom.coord() if metalClash(metalPos, pos, parentPos): return newH = addAtom(_Hname(parentAtom, Hnum, totalHydrogens, namingSchema), Element(1), parentAtom.residue, pos, serialNumber=_serial, bondedTo=parentAtom) _serial = newH.serialNumber + 1 from Midas import elementColor parentColor = parentAtom.color if parentColor == elementColor(parentAtom.element.name): newH.color = elementColor("H") else: newH.color = parentColor
def placeHelium(resName, model="scratch", position=None): """place a new helium atom 'resName' is the name of the new residue that will contain the helium. (It will be in the 'het' chain.) 'model' can either be a chimera.Molecule instance or a string. If the latter, then a new model is created with the string as its .name attribute. 'position' can either be a chimera.Point or None. If None, then the helium is positioned at the center of the view. """ if isinstance(model, basestring): model = _newModel(model) r = _newResidue(model, resName) if position is None: xf = model.openState.xform position = xf.inverse().apply( Point(*chimera.viewer.camera.center)) from chimera.molEdit import addAtom return addAtom('He1', Element('He'), r, position)
def include_dummies(self, metal_class): """ Include oriented Dummy Atoms inside the molecular system. Parameters ---------- metal_class: str Build - in Metal class pointer """ metal = metal_class.metal residue = metal.residue # Remove existing pseudobonds if hasattr(metal, 'pseudoBonds') and metal.pseudoBonds: metal.pseudoBonds[0].pseudoBondGroup.deleteAll() # Adding Dummies for i, dummy in enumerate(metal_class.dummies): dummy.atom = addAtom("D{}".format(i + 1), Element(dummy.Type), residue, chimera.Coord(dummy.xyz)) dummy.atom.drawMode = 3 dummy.atom.radius = 0.2
def postAdd(fakeN, fakeC): # fix up non-"true" terminal residues (terminal simply because # next residue is missing) for fn in fakeN: try: n = fn.atomsMap["N"][0] ca = fn.atomsMap["CA"][0] c = fn.atomsMap["C"][0] except KeyError: continue dihed = None for cnb in c.primaryNeighbors(): if cnb.name == "N": pn = cnb break else: dihed = 0.0 if dihed is None: try: pr = pn.residue pc = pr.atomsMap["C"][0] pca = pr.atomsMap["CA"][0] if pr.type == "PRO": ph = pr.atomsMap["CD"][0] else: ph = pr.atomsMap["H"][0] except KeyError: dihed = 0.0 for nb in n.primaryNeighbors(): if nb.element.number == 1: nb.molecule.deleteAtom(nb) if fn.type == "PRO": continue if dihed is None: dihed = chimera.dihedral(pc.coord(), pca.coord(), pn.coord(), ph.coord()) replyobj.info("Adding 'H' to %s\n" % str(fn)) from chimera.molEdit import addDihedralAtom, addBond h = addDihedralAtom("H", Element(1), n, ca, c, 1.01, 120.0, dihed, bonded=True) # also need to set N's IDATM type, because if we leave it as # N3+ then the residue will be identified by AddCharge as # terminal and there will be no charge for the H atom n.idatmType = "Npl" for fc in fakeC: try: c = fc.atomsMap["C"][0] except KeyError: continue for nb in c.primaryNeighbors(): if nb.element.number == 1: replyobj.info("Removing spurious proton from" " 'C' of %s\n" % str(fc)) nb.molecule.deleteAtom(nb) # the N proton may have been named 'HN'; fix that try: hn = fc.atomsMap["HN"][0] except KeyError: continue addAtom("H", Element(1), fc, hn.coord(), serialNumber=hn.serialNumber, bondedTo=hn.neighbors[0]) fc.molecule.deleteAtom(hn)
def placePeptide(sequence, phiPsis, model="scratch", position=None, rotlib=None, chainID='A'): """place a peptide sequence 'sequence' contains the (upper case) sequence 'phiPsis' is a list of phi/psi tuples, one per residue 'model' can either be a chimera.Molecule instance or a string. If the latter, then a new model is created with the string as its .name attribute. 'position' can either be a chimera.Point or None. If None, then the fragment is positioned at the center of the view. """ if not sequence: raise ValueError("No sequence supplied") sequence = sequence.upper() if not sequence.isupper(): raise ValueError("Sequence contains non-alphabetic characters") from chimera.resCode import protein1to3 for c in sequence: if c not in protein1to3: raise ValueError("Unrecognized protein 1-letter code:" " %s" % c) if len(sequence) != len(phiPsis): raise ValueError("Number of phi/psis not equal to" " sequence length") if isinstance(model, basestring): model = _newModel(model) needFocus = False if position is None: if len(chimera.openModels.list()) == 1: needFocus = True xf = model.openState.xform position = xf.inverse().apply( Point(*chimera.viewer.camera.center)) prev = [None] * 3 pos = 1 from Midas.addAA import DIST_N_C, DIST_CA_N, DIST_C_CA, DIST_C_O from chimera.molEdit import findPt, addAtom, addDihedralAtom serialNumber = None residues = [] for c, phiPsi in zip(sequence, phiPsis): phi, psi = phiPsi while model.findResidue(chimera.MolResId(chainID, pos)): pos += 1 r = model.newResidue(protein1to3[c], chainID, pos, ' ') residues.append(r) for backbone, dist, angle, dihed in ( ('N', DIST_N_C, 116.6, psi), ('CA', DIST_CA_N, 121.9, 180.0), ('C', DIST_C_CA, 110.1, phi)): if prev[0] == None: pt = Point(0.0, 0.0, 0.0) elif prev[1] == None: pt = Point(dist, 0.0, 0.0) elif prev[2] == None: pt = findPt(prev[0].coord(), prev[1].coord(), Point(0.0, 1.0, 0.0), dist, angle, 0.0) else: pt = findPt(prev[0].coord(), prev[1].coord(), prev[2].coord(), dist, angle, dihed) a = addAtom(backbone, Element(backbone[0]), r, pt, serialNumber=serialNumber, bondedTo=prev[0]) serialNumber = a.serialNumber + 1 prev = [a] + prev[:2] o = addDihedralAtom("O", Element("O"), prev[0], prev[1], prev[2], DIST_C_O, 120.4, 180.0 + psi, bonded=True) # C terminus O/OXT at different angle than mainchain O model.deleteAtom(o) addDihedralAtom("O", Element("O"), prev[0], prev[1], prev[2], DIST_C_O, 117.0, 180.0 + psi, bonded=True) addDihedralAtom("OXT", Element("O"), prev[0], prev[1], prev[2], DIST_C_O, 117.0, psi, bonded=True) from Rotamers import useBestRotamers # have to process one by one, otherwise side-chain clashes will occur kw = {} if rotlib: kw['lib'] = rotlib for r in residues: useBestRotamers("same", [r], criteria="cp", log=False, **kw) # find peptide center coords = [] for r in residues: coords.extend([a.coord() for a in r.atoms]) center = Point(coords) correction = position - center for r in residues: for a in r.atoms: a.setCoord(a.coord() + correction) from Midas import ksdssp ksdssp([model]) if needFocus: chimera.runCommand("focus") return residues
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
def placePeptide(sequence, phiPsis, model="scratch", position=None, rotlib=None, chainID='A'): """place a peptide sequence 'sequence' contains the (upper case) sequence 'phiPsis' is a list of phi/psi tuples, one per residue 'model' can either be a chimera.Molecule instance or a string. If the latter, then a new model is created with the string as its .name attribute. 'position' can either be a chimera.Point or None. If None, then the fragment is positioned at the center of the view. """ if not sequence: raise ValueError("No sequence supplied") sequence = sequence.upper() if not sequence.isupper(): raise ValueError("Sequence contains non-alphabetic characters") from chimera.resCode import protein1to3 for c in sequence: if c not in protein1to3: raise ValueError("Unrecognized protein 1-letter code:" " %s" % c) if len(sequence) != len(phiPsis): raise ValueError("Number of phi/psis not equal to" " sequence length") if isinstance(model, basestring): model = _newModel(model) needFocus = False if position is None: if len(chimera.openModels.list()) == 1: needFocus = True xf = model.openState.xform position = xf.inverse().apply(Point(*chimera.viewer.camera.center)) prev = [None] * 3 pos = 1 from Midas.addAA import DIST_N_C, DIST_CA_N, DIST_C_CA, DIST_C_O from chimera.molEdit import findPt, addAtom, addDihedralAtom serialNumber = None residues = [] for c, phiPsi in zip(sequence, phiPsis): phi, psi = phiPsi while model.findResidue(chimera.MolResId(chainID, pos)): pos += 1 r = model.newResidue(protein1to3[c], chainID, pos, ' ') residues.append(r) for backbone, dist, angle, dihed in (('N', DIST_N_C, 116.6, psi), ('CA', DIST_CA_N, 121.9, 180.0), ('C', DIST_C_CA, 110.1, phi)): if prev[0] == None: pt = Point(0.0, 0.0, 0.0) elif prev[1] == None: pt = Point(dist, 0.0, 0.0) elif prev[2] == None: pt = findPt(prev[0].coord(), prev[1].coord(), Point(0.0, 1.0, 0.0), dist, angle, 0.0) else: pt = findPt(prev[0].coord(), prev[1].coord(), prev[2].coord(), dist, angle, dihed) a = addAtom(backbone, Element(backbone[0]), r, pt, serialNumber=serialNumber, bondedTo=prev[0]) serialNumber = a.serialNumber + 1 prev = [a] + prev[:2] o = addDihedralAtom("O", Element("O"), prev[0], prev[1], prev[2], DIST_C_O, 120.4, 180.0 + psi, bonded=True) # C terminus O/OXT at different angle than mainchain O model.deleteAtom(o) addDihedralAtom("O", Element("O"), prev[0], prev[1], prev[2], DIST_C_O, 117.0, 180.0 + psi, bonded=True) addDihedralAtom("OXT", Element("O"), prev[0], prev[1], prev[2], DIST_C_O, 117.0, psi, bonded=True) from Rotamers import useBestRotamers # have to process one by one, otherwise side-chain clashes will occur kw = {} if rotlib: kw['lib'] = rotlib for r in residues: useBestRotamers("same", [r], criteria="cp", log=False, **kw) # find peptide center coords = [] for r in residues: coords.extend([a.coord() for a in r.atoms]) center = Point(coords) correction = position - center for r in residues: for a in r.atoms: a.setCoord(a.coord() + correction) from Midas import ksdssp ksdssp([model]) if needFocus: chimera.runCommand("focus") return residues
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 _nonStdCharge(residues, netCharge, method, gaffType, status, showCharges): r = residues[0] if status: status("Copying residue %s\n" % r.type) # create a fake Molecule that we can write to a Mol2 file nm = chimera.Molecule() nm.name = r.type # write out the residue's atoms first, since those are the # ones we will be caring about nr = nm.newResidue(r.type, ' ', 1, ' ') from chimera.molEdit import addAtom atomMap = {} atomNames = set() ratoms = r.atoms # use same ordering of atoms as they had in input, to improve # consistency of antechamber charges ratoms.sort(lambda a1, a2: cmp(a1.coordIndex, a2.coordIndex)) for a in ratoms: atomMap[a] = addAtom(a.name, a.element, nr, a.coord()) atomNames.add(a.name) # add the intraresidue bonds and remember the interresidue ones nearby = set() for a in ratoms: na = atomMap[a] for n in a.neighbors: if n.residue != r: nearby.add(n) continue nn = atomMap[n] if nn in na.bondsMap: continue nm.newBond(na, nn) from chimera.idatm import typeInfo extras = set() while nearby: nb = nearby.pop() aname = _getAName(str(nb.element), atomNames) na = addAtom(aname, nb.element, nr, nb.coord()) atomMap[nb] = na extras.add(na) for nbn in nb.neighbors: if nbn in atomMap: nm.newBond(na, atomMap[nbn]) else: try: ti = typeInfo[nbn.idatmType] except KeyError: fc = 0 geom = 4 else: fc = estimateNetCharge([nbn]) geom = ti.geometry if fc or geom != 4: nearby.add(nbn) else: extras.update(_methylate(na, nbn, atomNames)) totalNetCharge = netCharge + estimateNetCharge(extras) from tempfile import mkdtemp import os, os.path tempDir = mkdtemp() def _clean(): for fn in os.listdir(tempDir): os.unlink(os.path.join(tempDir, fn)) os.rmdir(tempDir) from WriteMol2 import writeMol2 anteIn = os.path.join(tempDir, "ante.in.mol2") writeMol2([nm], anteIn, status=status) chimeraRoot = os.environ["CHIMERA"] anteHome = os.path.join(chimeraRoot, 'bin', 'antechamber') anteOut = os.path.join(tempDir, "ante.out.mol2") if method.lower().startswith("am1"): mth = "bcc" elif method.lower().startswith("gas"): mth = "gas" else: _clean() raise ValueError("Unknown charge method: %s" % method) command = [ os.path.join(anteHome, "exe", "antechamber"), "-i", anteIn, "-fi", "mol2", "-o", anteOut, "-fo", "mol2", "-c", mth, "-nc", str(totalNetCharge), "-df", "0", "-j", "5", "-s", "2" ] if status: status("Running ANTECHAMBER for residue %s\n" % r.type) from subprocess import Popen, STDOUT, PIPE # For some reason on Windows, if shell==False then antechamber # cannot run bondtype via system(). import sys if sys.platform == "win32": shell = True else: shell = False replyobj.info("Running ANTECHAMBER command: %s\n" % " ".join(command)) import os os.environ['ACHOME'] = anteHome anteMessages = Popen(command, stdin=PIPE, stdout=PIPE, stderr=STDOUT, cwd=tempDir, shell=shell, bufsize=1).stdout while True: line = anteMessages.readline() if not line: break replyobj.status("(%s) %s" % (r.type, line), log=True) if not os.path.exists(anteOut): _clean() raise ChargeError("Failure running ANTECHAMBER for residue %s\n" "Check reply log for details\n" % r.type) if status: status("Reading ANTECHAMBER output for residue %s\n" % r.type) from chimera import Mol2io, defaultMol2ioHelper mol2io = Mol2io(defaultMol2ioHelper) mols = mol2io.readMol2file(anteOut) if not mol2io.ok(): _clean() raise IOError(mol2io.error()) if not mols: _clean() raise RuntimeError("No molecules in ANTECHAMBER output for" " residue %s" % r.type) chargedAtoms = mols[0].atoms if len(chargedAtoms) != len(nm.atoms): _clean() raise RuntimeError("Wrong number of atoms (%d, should be %d)" " in ANTECHAMBER output for residue %s" % (r.type, len(chargedAtoms), len(nm.atoms))) if status: status("Assigning charges for residue %s\n" % r.type) # put charges in template templateAtoms = nm.atoms # can't rely on order... templateAtoms.sort(lambda a1, a2: cmp(a1.serialNumber, a2.serialNumber)) nonZero = False addedChargeSum = 0.0 _totalCharge = 0.0 for ta, ca in zip(templateAtoms, chargedAtoms): _totalCharge += ca.charge if ta in extras: addedChargeSum += ca.charge continue if ca.charge: nonZero = True if not nonZero: _clean() raise ChargeError("Failure running ANTECHAMBER for residue %s\n" "Check reply log for details\n" % r.type) # adjust charges to compensate for added atoms... adjustment = (addedChargeSum - (totalNetCharge - netCharge)) / (len(templateAtoms) - len(extras)) for ta, ca in zip(templateAtoms, chargedAtoms): if ta in extras: continue ta.charge = ca.charge + adjustment if gaffType: ta.gaffType = ca.mol2type # map template charges onto first residue track = chimera.TrackChanges.get() for fa, ta in atomMap.items(): if ta in extras: continue fa.charge = ta.charge if showCharges: fa.label = "%+g" % fa.charge track.addModified(fa, ATTR_SET) if gaffType: fa.gaffType = ta.gaffType # map charges onto remaining residues for rr in residues[1:]: for fa, ra in zip(r.oslChildren(), rr.oslChildren()): ra.charge = fa.charge if showCharges: ra.label = "%+g" % ra.charge track.addModified(ra, ATTR_SET) if gaffType: ra.gaffType = fa.gaffType _clean() if status: status("Charges for residue %s determined\n" % r.type) replyobj.info("Charges for residue %s determined\n" % r.type) return True
def initiateAddions(models, iontype, numion, status): import os import chimera from chimera import replyobj from chimera.molEdit import addAtom from WriteMol2 import writeMol2 from tempfile import mkdtemp for m in models: tempDir = mkdtemp() def _clean(): for fn in os.listdir(tempDir): os.unlink(os.path.join(tempDir, fn)) os.rmdir(tempDir) sleapIn = os.path.join(tempDir, "sleap.in.mol2") sleapOut = os.path.join(tempDir, "sleap.out.mol2") writeMol2([m], sleapIn, status=status, gaffType=True) leaprc = os.path.join(tempDir, "solvate.cmd") writeLeaprc(tempDir, iontype, numion, leaprc) chimeraRoot = os.environ["CHIMERA"] amberHome = os.path.join(chimeraRoot, "bin", "amber10") command = [os.path.join(amberHome, "exe", "sleap"), "-f", leaprc] if status: status("Running sleap") from subprocess import Popen, STDOUT, PIPE # For some reason on Windows, if shell==False then antechamber # cannot run bondtype via system(). import sys if sys.platform == "win32": shell = True else: shell = False replyobj.info("Running sleap command: %s\n" % " ".join(command)) import os os.environ["AMBERHOME"] = amberHome sleapMessages = Popen(command, stdin=PIPE, stdout=PIPE, stderr=STDOUT, cwd=tempDir, shell=shell, bufsize=1).stdout while True: line = sleapMessages.readline() if not line: break replyobj.status("(addions) %s" % line, log=True) if not os.path.exists(sleapOut): _clean() from chimera import NonChimeraError raise NonChimeraError("Failure running sleap \n" "Check reply log for details\n") if status: status("Reading sleap output\n") from chimera import Mol2io, defaultMol2ioHelper mol2io = Mol2io(defaultMol2ioHelper) mols = mol2io.readMol2file(sleapOut) if not mol2io.ok(): _clean() raise IOError(mol2io.error()) if not mols: _clean() raise RuntimeError("No molecules in sleap output") assert len(mols) == 1 outm = mols[0] solute_nresd = get_solute_nresd(m) print "total, solute, solvent: ", len( m.residues), solute_nresd, len(m.residues) - solute_nresd if status: status("Deleting old solvents") while len(m.residues) > solute_nresd: m.deleteResidue(m.residues[solute_nresd]) inAtoms = m.atoms outAtoms = outm.atoms # sort in coordIndex (i.e. input) order # (due to deletions, coordIndex values need _not_ be consecutive) serialSort = lambda a1, a2: cmp(a1.coordIndex, a2.coordIndex) inAtoms.sort(serialSort) outAtoms.sort(serialSort) # sleap repositions solute... if status: status("Translating %d atoms" % len(inAtoms)) for inA, outA in zip(inAtoms, outAtoms[:len(inAtoms)]): inA.setCoord(outA.coord()) for r in outm.residues[solute_nresd:]: if status: status("Creating ions/solvent residue %d " % (r.id.position - solute_nresd)) atomMap = {} nr = m.newResidue(r.type, ' ', 1, ' ') for a in r.atoms: na = addAtom(a.name, a.element, nr, a.coord(), serialNumber=a.serialNumber) na.charge = a.charge na.gaffType = a.mol2type if len(a.neighbors) == 0: na.drawMode = chimera.Atom.Sphere atomMap[a] = na if a.name[0:2] == "Br": na.element = 35 elif a.name[0:2] == "Cl": na.element = 17 elif a.name[0:2] == "Cs": na.element = 47 elif a.name[0:2] == "Mg": na.element = 12 elif a.name[0:2] == "Na": na.element = 11 elif a.name[0:2] == "Rb": na.element = 48 elif a.name[0] == 'F': na.element = 9 elif a.name[0] == 'I': na.element = 53 elif a.name[0] == 'K': na.element = 19 elif a.name[0] == "H": na.element = 1 elif a.name[0] == "C": na.element = 6 elif a.name[0] == "N": na.element = 7 elif a.name[0] == "O": na.element = 8 elif a.name[0] == "P": na.element = 15 elif a.name[0] == "S": na.element = 16 for a in r.atoms: na = atomMap[a] for n in a.neighbors: assert n.residue == r nn = atomMap[n] if nn in na.bondsMap: continue m.newBond(na, nn) _clean()
def combine(mols, refMol, newChainIDs=True, log=False, returnMapping=False): from chimera import replyobj # figure out residue chain/number remapping origChainIDs = {} numChains = 0 minNums = {} maxNums = {} for m in mols: seenIDs = set() for r in m.residues: chainID = r.id.chainId num = r.id.position key = (m, chainID) if key in minNums: if num > maxNums[key]: maxNums[key] = num elif num < minNums[key]: minNums[key] = num else: minNums[key] = maxNums[key] = num if chainID in seenIDs: continue numChains += 1 seenIDs.add(chainID) origChainIDs.setdefault(chainID, []).append(m) resMap = {} if newChainIDs: if numChains > 62: raise CombineError("More than 62 chains; cannot" " uniquely identify with single characters") from string import uppercase, lowercase, digits possibleIDs = uppercase + lowercase + digits usedChainIDs = set(origChainIDs.keys()) chainIDmap = {} for chainID, idMols in origChainIDs.items(): if len(chainID) > 1: continue chainIDmap[(idMols[0], chainID)] = chainID usedChainIDs.add(chainID) for m in idMols[1:]: for c in possibleIDs: if c not in usedChainIDs: break if log: replyobj.info("Remapping %s chain %s" " to %s\n" % (m, chainID, c)) usedChainIDs.add(c) chainIDmap[(m, chainID)] = c for m in mols: for r in m.residues: chainID = r.id.chainId if len(chainID) > 1: continue resMap[r] = (chainIDmap[(m, chainID)], r.id.position) # handle renumbering for all chains if newChainIDs False, # otherwise just water/het chains offsets = {} for chainID, idMols in origChainIDs.items(): if newChainIDs and len(chainID) < 2: continue minMaxs = [] for m in idMols: mmin = minNums[(m, chainID)] mmax = maxNums[(m, chainID)] for omin, omax in minMaxs: if omin < mmin < omax or omin < mmax < omax: gmax = max([mm[1] for mm in minMaxs]) offset = gmax - mmin + 1 break else: offset = 0 offsets[(m, chainID)] = offset minMaxs.append((mmin + offset, mmax + offset)) if log and offset: replyobj.info("Adding %d to %s chain %s" " numbering\n" % (offset, m, chainID)) for m in mols: for r in m.residues: chainID = r.id.chainId try: offset = offsets[(m, chainID)] except KeyError: continue resMap[r] = (chainID, r.id.position + offset) # combine... from chimera import Atom, Bond, Residue, Molecule combined = Molecule() combined.name = "combination" from SimpleSession.save import optionalAttributes from chimera.molEdit import addAtom atomAttrs = optionalAttributes[Atom].keys() + [ 'color', 'vdwColor', 'labelColor', 'surfaceColor', 'drawMode', 'display', 'label', 'radius', 'surfaceDisplay', 'surfaceCategory', 'surfaceOpacity', 'vdw' ] bondAttrs = optionalAttributes[Bond].keys() + [ 'drawMode', 'display', 'radius' ] resAttrs = optionalAttributes[Residue].keys() + [ 'ribbonColor', 'labelColor', 'isHelix', 'isSheet', 'isTurn', 'ribbonDrawMode', 'ribbonDisplay', 'label', 'isHet' ] serial = 1 atomMap = {} for m in mols: if m.openState == refMol.openState: xform = None else: xform = refMol.openState.xform xform.invert() for r in m.residues: chainID, pos = resMap[r] nr = combined.newResidue(r.type, chainID, pos, r.id.insertionCode) for attrName in resAttrs: try: setattr(nr, attrName, getattr(r, attrName)) except AttributeError: continue ratoms = r.atoms ratoms.sort(lambda a1, a2: cmp(a1.coordIndex, a2.coordIndex)) for a in ratoms: if xform is None: crd = a.coord() else: crd = xform.apply(a.xformCoord()) na = addAtom(a.name, a.element, nr, crd, serialNumber=serial) na.altLoc = a.altLoc atomMap[a] = na serial += 1 for attrName in atomAttrs: try: setattr(na, attrName, getattr(a, attrName)) except AttributeError: continue for b in m.bonds: a1, a2 = b.atoms nb = combined.newBond(atomMap[a1], atomMap[a2]) for attrName in bondAttrs: try: setattr(nb, attrName, getattr(b, attrName)) except AttributeError: continue consensusAttrs = [ 'color', 'display', 'lineWidth', 'pointSize', 'stickScale', 'surfaceOpacity', 'ballScale', 'vdwDensity', 'autochain', 'ribbonHidesMainchain' ] for attrName in consensusAttrs: consensusVal = None for m in mols: val = getattr(m, attrName) if consensusVal == None: consensusVal = val elif val != consensusVal: break else: setattr(combined, attrName, consensusVal) associatedModels = set() for m in mols: associatedModels.update(m.associatedModels()) from chimera import PseudoBondMgr for opbg in PseudoBondMgr.mgr().pseudoBondGroups: if opbg in associatedModels: if opbg.category.startswith("internal-chain-"): continue assert (opbg.category.startswith("coordination complex")) from chimera.misc import getPseudoBondGroup pbg = getPseudoBondGroup("coordination complexes of %s" % combined.name, associateWith=[combined]) for attrName in [ 'color', 'showStubBonds', 'lineWidth', 'stickScale', 'lineType' ]: setattr(pbg, attrName, getattr(opbg, attrName)) else: pbg = opbg for opb in opbg.pseudoBonds: oa1, oa2 = opb.atoms na1 = atomMap.get(oa1, oa1) na2 = atomMap.get(oa2, oa2) if oa1 == na1 and oa2 == na2: continue pb = pbg.newPseudoBond(na1, na2) for attrName in [ 'drawMode', 'display', 'halfbond', 'label', 'color', 'labelColor' ]: setattr(pb, attrName, getattr(opb, attrName)) if returnMapping: return atomMap, combined return combined
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 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 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
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 _nonStdCharge(residues, netCharge, method, gaffType, status, showCharges): r = residues[0] if status: status("Copying residue %s\n" % r.type) # create a fake Molecule that we can write to a Mol2 file nm = chimera.Molecule() nm.name = r.type # write out the residue's atoms first, since those are the # ones we will be caring about nr = nm.newResidue(r.type, ' ', 1, ' ') from chimera.molEdit import addAtom atomMap = {} atomNames = set() ratoms = r.atoms # use same ordering of atoms as they had in input, to improve # consistency of antechamber charges ratoms.sort(lambda a1, a2: cmp(a1.coordIndex, a2.coordIndex)) for a in ratoms: atomMap[a] = addAtom(a.name, a.element, nr, a.coord()) atomNames.add(a.name) # add the intraresidue bonds and remember the interresidue ones nearby = set() for a in ratoms: na = atomMap[a] for n in a.neighbors: if n.residue != r: nearby.add(n) continue nn = atomMap[n] if nn in na.bondsMap: continue nm.newBond(na, nn) from chimera.idatm import typeInfo extras = set() while nearby: nb = nearby.pop() aname = _getAName(str(nb.element), atomNames) na = addAtom(aname, nb.element, nr, nb.coord()) atomMap[nb] = na extras.add(na) for nbn in nb.neighbors: if nbn in atomMap: nm.newBond(na, atomMap[nbn]) else: try: ti = typeInfo[nbn.idatmType] except KeyError: fc = 0 geom = 4 else: fc = estimateNetCharge([nbn]) geom = ti.geometry if fc or geom != 4: nearby.add(nbn) else: extras.update( _methylate(na, nbn, atomNames)) totalNetCharge = netCharge + estimateNetCharge(extras) from tempfile import mkdtemp import os, os.path tempDir = mkdtemp() def _clean(): for fn in os.listdir(tempDir): os.unlink(os.path.join(tempDir, fn)) os.rmdir(tempDir) from WriteMol2 import writeMol2 anteIn = os.path.join(tempDir, "ante.in.mol2") writeMol2([nm], anteIn, status=status) chimeraRoot = os.environ["CHIMERA"] anteHome = os.path.join(chimeraRoot, 'bin', 'antechamber') anteOut = os.path.join(tempDir, "ante.out.mol2") if method.lower().startswith("am1"): mth = "bcc" elif method.lower().startswith("gas"): mth = "gas" else: _clean() raise ValueError("Unknown charge method: %s" % method) command = [os.path.join(anteHome, "exe", "antechamber"), "-i", anteIn, "-fi", "mol2", "-o", anteOut, "-fo", "mol2", "-c", mth, "-nc", str(totalNetCharge), "-df", "0", "-j", "5", "-s", "2"] if status: status("Running ANTECHAMBER for residue %s\n" % r.type) from subprocess import Popen, STDOUT, PIPE # For some reason on Windows, if shell==False then antechamber # cannot run bondtype via system(). import sys if sys.platform == "win32": shell = True else: shell = False replyobj.info("Running ANTECHAMBER command: %s\n" % " ".join(command)) import os os.environ['ACHOME'] = anteHome anteMessages = Popen(command, stdin=PIPE, stdout=PIPE, stderr=STDOUT, cwd=tempDir, shell=shell, bufsize=1).stdout while True: line = anteMessages.readline() if not line: break replyobj.status("(%s) %s" % (r.type, line), log=True) if not os.path.exists(anteOut): _clean() raise ChargeError("Failure running ANTECHAMBER for residue %s\n" "Check reply log for details\n" % r.type) if status: status("Reading ANTECHAMBER output for residue %s\n" % r.type) from chimera import Mol2io, defaultMol2ioHelper mol2io = Mol2io(defaultMol2ioHelper) mols = mol2io.readMol2file(anteOut) if not mol2io.ok(): _clean() raise IOError(mol2io.error()) if not mols: _clean() raise RuntimeError("No molecules in ANTECHAMBER output for" " residue %s" % r.type) chargedAtoms = mols[0].atoms if len(chargedAtoms) != len(nm.atoms): _clean() raise RuntimeError("Wrong number of atoms (%d, should be %d)" " in ANTECHAMBER output for residue %s" % (r.type, len(chargedAtoms), len(nm.atoms))) if status: status("Assigning charges for residue %s\n" % r.type) # put charges in template templateAtoms = nm.atoms # can't rely on order... templateAtoms.sort(lambda a1, a2: cmp(a1.serialNumber, a2.serialNumber)) nonZero = False addedChargeSum = 0.0 _totalCharge = 0.0 for ta, ca in zip(templateAtoms, chargedAtoms): _totalCharge += ca.charge if ta in extras: addedChargeSum += ca.charge continue if ca.charge: nonZero = True if not nonZero: _clean() raise ChargeError("Failure running ANTECHAMBER for residue %s\n" "Check reply log for details\n" % r.type) # adjust charges to compensate for added atoms... adjustment = (addedChargeSum - (totalNetCharge - netCharge)) / ( len(templateAtoms) - len(extras)) for ta, ca in zip(templateAtoms, chargedAtoms): if ta in extras: continue ta.charge = ca.charge + adjustment if gaffType: ta.gaffType = ca.mol2type # map template charges onto first residue track = chimera.TrackChanges.get() for fa, ta in atomMap.items(): if ta in extras: continue fa.charge = ta.charge if showCharges: fa.label = "%+g" % fa.charge track.addModified(fa, ATTR_SET) if gaffType: fa.gaffType = ta.gaffType # map charges onto remaining residues for rr in residues[1:]: for fa, ra in zip(r.oslChildren(), rr.oslChildren()): ra.charge = fa.charge if showCharges: ra.label = "%+g" % ra.charge track.addModified(ra, ATTR_SET) if gaffType: ra.gaffType = fa.gaffType _clean() if status: status("Charges for residue %s determined\n" % r.type) replyobj.info("Charges for residue %s determined\n" % r.type) return True
def combine(mols, refMol, newChainIDs=True, log=False, returnMapping=False): from chimera import replyobj # figure out residue chain/number remapping origChainIDs = {} numChains = 0 minNums = {} maxNums = {} for m in mols: seenIDs = set() for r in m.residues: chainID = r.id.chainId num = r.id.position key = (m, chainID) if key in minNums: if num > maxNums[key]: maxNums[key] = num elif num < minNums[key]: minNums[key] = num else: minNums[key] = maxNums[key] = num if chainID in seenIDs: continue numChains += 1 seenIDs.add(chainID) origChainIDs.setdefault(chainID, []).append(m) resMap = {} if newChainIDs: if numChains > 62: raise CombineError("More than 62 chains; cannot" " uniquely identify with single characters") from string import uppercase, lowercase, digits possibleIDs = uppercase + lowercase + digits usedChainIDs = set(origChainIDs.keys()) chainIDmap = {} for chainID, idMols in origChainIDs.items(): if len(chainID) > 1: continue chainIDmap[(idMols[0], chainID)] = chainID usedChainIDs.add(chainID) for m in idMols[1:]: for c in possibleIDs: if c not in usedChainIDs: break if log: replyobj.info("Remapping %s chain %s" " to %s\n" % (m, chainID, c)) usedChainIDs.add(c) chainIDmap[(m, chainID)] = c for m in mols: for r in m.residues: chainID = r.id.chainId if len(chainID) > 1: continue resMap[r] = (chainIDmap[(m, chainID)], r.id.position) # handle renumbering for all chains if newChainIDs False, # otherwise just water/het chains offsets = {} for chainID, idMols in origChainIDs.items(): if newChainIDs and len(chainID) < 2: continue minMaxs = [] for m in idMols: mmin = minNums[(m, chainID)] mmax = maxNums[(m, chainID)] for omin, omax in minMaxs: if omin < mmin < omax or omin < mmax < omax: gmax = max([mm[1] for mm in minMaxs]) offset = gmax - mmin + 1 break else: offset = 0 offsets[(m, chainID)] = offset minMaxs.append((mmin+offset, mmax+offset)) if log and offset: replyobj.info("Adding %d to %s chain %s" " numbering\n" % (offset, m, chainID)) for m in mols: for r in m.residues: chainID = r.id.chainId try: offset = offsets[(m, chainID)] except KeyError: continue resMap[r] = (chainID, r.id.position + offset) # combine... from chimera import Atom, Bond, Residue, Molecule combined = Molecule() combined.name = "combination" from SimpleSession.save import optionalAttributes from chimera.molEdit import addAtom atomAttrs = optionalAttributes[Atom].keys() + ['color', 'vdwColor', 'labelColor', 'surfaceColor', 'drawMode', 'display', 'label', 'radius', 'surfaceDisplay', 'surfaceCategory', 'surfaceOpacity', 'vdw'] bondAttrs = optionalAttributes[Bond].keys() + ['drawMode', 'display', 'radius'] resAttrs = optionalAttributes[Residue].keys() + ['ribbonColor', 'labelColor', 'isHelix', 'isSheet', 'isTurn', 'ribbonDrawMode', 'ribbonDisplay', 'label', 'isHet'] serial = 1 atomMap = {} for m in mols: if m.openState == refMol.openState: xform = None else: xform = refMol.openState.xform xform.invert() for r in m.residues: chainID, pos = resMap[r] nr = combined.newResidue(r.type, chainID, pos, r.id.insertionCode) for attrName in resAttrs: try: setattr(nr, attrName, getattr(r, attrName)) except AttributeError: continue ratoms = r.atoms ratoms.sort(lambda a1, a2: cmp(a1.coordIndex, a2.coordIndex)) for a in ratoms: if xform is None: crd = a.coord() else: crd = xform.apply(a.xformCoord()) na = addAtom(a.name, a.element, nr, crd, serialNumber=serial) na.altLoc = a.altLoc atomMap[a] = na serial += 1 for attrName in atomAttrs: try: setattr(na, attrName, getattr(a, attrName)) except AttributeError: continue for b in m.bonds: a1, a2 = b.atoms nb = combined.newBond(atomMap[a1], atomMap[a2]) for attrName in bondAttrs: try: setattr(nb, attrName, getattr(b, attrName)) except AttributeError: continue consensusAttrs = ['color', 'display', 'lineWidth', 'pointSize', 'stickScale', 'surfaceOpacity', 'ballScale', 'vdwDensity', 'autochain', 'ribbonHidesMainchain'] for attrName in consensusAttrs: consensusVal = None for m in mols: val = getattr(m, attrName) if consensusVal == None: consensusVal = val elif val != consensusVal: break else: setattr(combined, attrName, consensusVal) associatedModels = set() for m in mols: associatedModels.update(m.associatedModels()) from chimera import PseudoBondMgr for opbg in PseudoBondMgr.mgr().pseudoBondGroups: if opbg in associatedModels: if opbg.category.startswith("internal-chain-"): continue assert(opbg.category.startswith("coordination complex")) from chimera.misc import getPseudoBondGroup pbg = getPseudoBondGroup("coordination complexes of %s" % combined.name, associateWith=[combined]) for attrName in ['color', 'showStubBonds', 'lineWidth', 'stickScale', 'lineType']: setattr(pbg, attrName, getattr(opbg, attrName)) else: pbg = opbg for opb in opbg.pseudoBonds: oa1, oa2 = opb.atoms na1 = atomMap.get(oa1, oa1) na2 = atomMap.get(oa2, oa2) if oa1 == na1 and oa2 == na2: continue pb = pbg.newPseudoBond(na1, na2) for attrName in ['drawMode', 'display', 'halfbond', 'label', 'color', 'labelColor']: setattr(pb, attrName, getattr(opb, attrName)) if returnMapping: return atomMap, combined return combined
def initiateAddions(models, iontype, numion, status): import os import chimera from chimera import replyobj from chimera.molEdit import addAtom from WriteMol2 import writeMol2 from tempfile import mkdtemp for m in models: tempDir = mkdtemp() def _clean(): for fn in os.listdir(tempDir): os.unlink(os.path.join(tempDir, fn)) os.rmdir(tempDir) sleapIn = os.path.join(tempDir, "sleap.in.mol2") sleapOut= os.path.join(tempDir, "sleap.out.mol2") writeMol2([m], sleapIn, status=status, gaffType=True) leaprc = os.path.join(tempDir, "solvate.cmd") writeLeaprc(tempDir, iontype, numion, leaprc) chimeraRoot = os.environ["CHIMERA"] amberHome = os.path.join(chimeraRoot, "bin", "amber10") command = [os.path.join(amberHome, "exe", "sleap"), "-f", leaprc] if status: status("Running sleap" ) from subprocess import Popen, STDOUT, PIPE # For some reason on Windows, if shell==False then antechamber # cannot run bondtype via system(). import sys if sys.platform == "win32": shell = True else: shell = False replyobj.info("Running sleap command: %s\n" % " ".join(command)) import os os.environ["AMBERHOME"]=amberHome sleapMessages = Popen(command, stdin=PIPE, stdout=PIPE, stderr=STDOUT, cwd=tempDir, shell=shell, bufsize=1).stdout while True: line = sleapMessages.readline() if not line: break replyobj.status("(addions) %s" % line, log=True) if not os.path.exists(sleapOut): _clean() from chimera import NonChimeraError raise NonChimeraError("Failure running sleap \n" "Check reply log for details\n") if status: status("Reading sleap output\n") from chimera import Mol2io, defaultMol2ioHelper mol2io = Mol2io(defaultMol2ioHelper) mols = mol2io.readMol2file(sleapOut) if not mol2io.ok(): _clean() raise IOError(mol2io.error()) if not mols: _clean() raise RuntimeError("No molecules in sleap output") assert len(mols)==1 outm = mols[0] solute_nresd = get_solute_nresd(m) print "total, solute, solvent: ", len(m.residues), solute_nresd, len(m.residues)-solute_nresd if status: status( "Deleting old solvents" ) while len(m.residues) > solute_nresd: m.deleteResidue( m.residues[solute_nresd] ) inAtoms = m.atoms outAtoms = outm.atoms # sort in coordIndex (i.e. input) order # (due to deletions, coordIndex values need _not_ be consecutive) serialSort = lambda a1, a2: cmp(a1.coordIndex, a2.coordIndex) inAtoms.sort(serialSort) outAtoms.sort(serialSort) # sleap repositions solute... if status: status("Translating %d atoms" % len(inAtoms)) for inA, outA in zip(inAtoms, outAtoms[:len(inAtoms)]): inA.setCoord(outA.coord()) for r in outm.residues[solute_nresd:]: if status: status("Creating ions/solvent residue %d " % (r.id.position-solute_nresd) ) atomMap = {} nr = m.newResidue(r.type, ' ', 1, ' ') for a in r.atoms: na = addAtom(a.name, a.element, nr, a.coord(), serialNumber=a.serialNumber) na.charge = a.charge na.gaffType = a.mol2type if len(a.neighbors)==0: na.drawMode = chimera.Atom.Sphere atomMap[a] = na if a.name[0:2]=="Br": na.element = 35 elif a.name[0:2]=="Cl": na.element = 17 elif a.name[0:2]=="Cs": na.element = 47 elif a.name[0:2]=="Mg": na.element = 12 elif a.name[0:2]=="Na": na.element = 11 elif a.name[0:2]=="Rb": na.element = 48 elif a.name[0]=='F': na.element = 9 elif a.name[0]=='I': na.element = 53 elif a.name[0]=='K': na.element = 19 elif a.name[0]=="H": na.element = 1 elif a.name[0]=="C": na.element = 6 elif a.name[0]=="N": na.element = 7 elif a.name[0]=="O": na.element = 8 elif a.name[0]=="P": na.element = 15 elif a.name[0]=="S": na.element = 16 for a in r.atoms: na = atomMap[a] for n in a.neighbors: assert n.residue == r nn = atomMap[n] if nn in na.bondsMap: continue m.newBond(na, nn) _clean()
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 initiateSolvate(models, method, solvent, extent, center, status): import os import chimera from chimera import replyobj from chimera.molEdit import addAtom from WriteMol2 import writeMol2 from tempfile import mkdtemp for m in models: tempDir = mkdtemp() print 'tempDir: ', tempDir def _clean(): for fn in os.listdir(tempDir): os.unlink(os.path.join(tempDir, fn)) os.rmdir(tempDir) sleapIn = os.path.join(tempDir, "sleap.in.mol2") sleapOut= os.path.join(tempDir, "sleap.out.mol2") writeMol2([m], sleapIn, status=status) leaprc = os.path.join(tempDir, "solvate.cmd") writeLeaprc(tempDir, method, solvent, extent, center, leaprc) chimeraRoot = os.environ["CHIMERA"] amberHome = os.path.join(chimeraRoot, "bin", "amber10") command = [os.path.join(amberHome, "exe", "sleap"), "-f", leaprc] print 'command: ', command if status: status("Running sleap" ) from subprocess import Popen, STDOUT, PIPE # For some reason on Windows, if shell==False then antechamber # cannot run bondtype via system(). import sys if sys.platform == "win32": shell = True else: shell = False replyobj.info("Running sleap command: %s\n" % " ".join(command)) import os os.environ["AMBERHOME"]=amberHome sleapMessages = Popen(command, stdin=PIPE, stdout=PIPE, stderr=STDOUT, cwd=tempDir, shell=shell, bufsize=1).stdout while True: line = sleapMessages.readline() if not line: break replyobj.status("(solvate) %s" % line, log=True) if not os.path.exists(sleapOut): _clean() from chimera import NonChimeraError raise NonChimeraError("Failure running sleap \n" "Check reply log for details\n") if status: status("Reading sleap output\n") from chimera import Mol2io, defaultMol2ioHelper mol2io = Mol2io(defaultMol2ioHelper) mols = mol2io.readMol2file(sleapOut) if not mol2io.ok(): _clean() raise IOError(mol2io.error()) if not mols: _clean() raise RuntimeError("No molecules in sleap output") assert len(mols)==1 outm = mols[0] natom = len(m.atoms) nresd = len(m.residues) inAtoms = m.atoms outAtoms = outm.atoms # sort in coordIndex (i.e. input) order # (due to deletions, coordIndex values need _not_ be consecutive) serialSort = lambda a1, a2: cmp(a1.coordIndex, a2.coordIndex) inAtoms.sort(serialSort) outAtoms.sort(serialSort) if status: status("Translating %d atoms" % len(inAtoms)) for inA, outA in zip(inAtoms, outAtoms[:len(inAtoms)]): inA.setCoord(outA.coord()) # added solvent hydrogens may not have been categorized yet, so use # this less obvious way of gathering solvent atoms... existingSolvent = set() from chimera.elements import metals, alkaliMetals nonAlkaliMetals = metals - alkaliMetals for r in m.residues: if len(r.atoms) == 1 and r.atoms[0].element in nonAlkaliMetals: continue for a in r.atoms: if a.surfaceCategory in ["solvent", "ions"]: existingSolvent.update(r.atoms) break # copy mol2 comment which contain the info of the solvent: shape, size, etc if hasattr( outm, "mol2comments" ) and len(outm.mol2comments) > 0: m.solventInfo = outm.mol2comments[0] print "solvent info: ", m.solventInfo if existingSolvent: solventCharges = {} for r in outm.residues[nresd:]: solventNum = r.id.position - nresd if status: status("Creating solvent residue %d " % solventNum ) atomMap = {} nr = m.newResidue(r.type, ' ', solventNum, ' ') # mark residue for exclusion by AddCharge... nr._solvateCharged = True for a in r.atoms: na = addAtom(a.name, a.element, nr, a.coord(), serialNumber=a.serialNumber) na.charge = a.charge na.gaffType = a.mol2type atomMap[a] = na if a.name[0]=="H": na.element = 1 if a.name[0]=="C": na.element = 6 if a.name[0]=="N": na.element = 7 if a.name[0]=="O": na.element = 8 if a.name[0]=="P": na.element = 15 if a.name[0]=="S": na.element = 16 if a.name[0:2]=="Cl": na.element = 17 if existingSolvent: solventCharges[(r.type, a.name)] = a.charge if r.type == "WAT": solventCharges[ ("HOH", a.name)] = a.charge for a in r.atoms: na = atomMap[a] for n in a.neighbors: assert n.residue == r nn = atomMap[n] if nn in na.bondsMap: continue m.newBond(na, nn) if existingSolvent: unknowns = set() for sa in existingSolvent: key = (sa.residue.type, sa.name) try: sa.charge = solventCharges[key] except KeyError: unknowns.add(key) sa.residue._solvateCharged = True if unknowns: replyobj.warning("Could not determine charges for" " pre-existing solvent/ions from added solvent" "/ions for: " + ", ".join([" ".join(x) for x in unknowns])) _clean() from Midas import window window(models)