def __init__(self, netcdfPath, startFrame, endFrame): from MMTK.Trajectory import Trajectory replyobj.status("Reading NetCDF file\n", blankAfter=0) try: self.trajectory = Trajectory(None, netcdfPath) finally: replyobj.status("Done reading NetCDF file\n") replyobj.status("Processing trajectory\n", blankAfter=0) self.atomNames = [] self.elements = [] self.resNames = [] self.atomIndices = {} self.bonds = [] self.ipres = [1] from chimera import Element univ = self.trajectory.universe for i, a in enumerate(univ.atomList()): self.atomIndices[a] = i self.atomNames.append(a.name) self.elements.append(Element(a.getAtomProperty(a, "symbol"))) for obj in univ: self._processObj(obj) delattr(self, "atomIndices") self.ipres.pop() self.startFrame = startFrame self.endFrame = endFrame self.name = os.path.basename(netcdfPath) replyobj.status("Done processing trajectory\n")
def __init__(self, fileName, ncInfo): from chimera import UserError, Molecule, Element, openModels, replyobj from chimera import Coord, connectMolecule self.name = "Particle trajectory from %s" % fileName self.molecule = m = Molecule() self.isRealMolecule = False try: m.name = ncInfo.title except AttributeError: m.name = fileName tc = Element("Tc") coords = ncInfo.variables['coordinates'] try: pnames = ncInfo.variables['particle_names'] except KeyError: pnames = ["prt"] * len(coords) try: radii = ncInfo.variables['radii'] except KeyError: radii = [1.0] * len(coords) try: mnames = ncInfo.variables['molecule_names'] mnumbers = ncInfo.variables['molecule_numbers'] except KeyError: mnames = ["PRT"] mnumbers = [1] * len(coords) serial = 1 atoms = [] residues = {} for name, radius, crd, mnumber in zip(pnames[:], radii[:], coords[0][:], mnumbers[:]): try: r = residues[mnumber] except KeyError: r = m.newResidue(string(mnames[mnumber - 1]), " ", mnumber, " ") name = string(name) a = m.newAtom(name, tc) atoms.append(a) a.radius = radius r.addAtom(a) a.setCoord(Coord(*tuple(crd))) a.serialNumber = serial serial += 1 try: connections = ncInfo.variables['connections'] except KeyError: connections = [] for i1, i2 in connections[:]: m.newBond(atoms[i1 - 1], atoms[i2 - 1]) csNum = 1 for crds in coords[1:]: cs = m.newCoordSet(csNum) for a, crd in zip(atoms, crds): a.setCoord(Coord(*tuple(crd)), cs) csNum += 1
def createCN(): cn= Molecule() # create an instance of a molecule # r = cn.newResidue(residue type, chain identifier, sequence number, insertion code) r = cn.newResidue("cn", " ", 1, " ") atomC = cn.newAtom("CE", Element("C")) # now create the atoms of the residue. atomN = cn.newAtom("NF", Element("N")) bondLength_cn = 1.156 atomC.setCoord(Coord(0, 0, 0)) atomN.setCoord(Coord(bondLength_cn, 0, 0)) r.addAtom(atomC) r.addAtom(atomN) cn.newBond(atomC, atomN) openModels.add([cn])
def bondWithHLength(heavy, geom): element = heavy.element.name if element == "C": if geom == 4: return 1.09 if geom == 3: return 1.08 if geom == 2: return 1.056 elif element == "N": return N_H elif element == "O": # can't rely on water being in chain "water" anymore... if len(heavy.bonds) == 0 or (len(heavy.bonds) == 2 and len( [nb for nb in heavy.neighbors if nb.element.number > 1]) == 0): return 0.9572 return 0.96 elif element == "S": return 1.336 return Element.bondLength(heavy.element, Element(1))
def determineElementFromMass(mass, considerHydrogens=True): from chimera import Element H = Element('H') nearest = None for high in range(1, 93): if Element(high).mass > mass: break else: high = 93 if considerHydrogens: maxHyds = 6 else: maxHyds = 0 for numHyds in range(maxHyds + 1): adjMass = mass - numHyds * H.mass lowMass = Element(high - 1).mass while lowMass > adjMass and high > 1: high -= 1 lowMass = Element(high - 1).mass highMass = Element(high).mass lowDiff = abs(adjMass - lowMass) highDiff = abs(adjMass - highMass) if lowDiff < highDiff: diff = lowDiff element = high - 1 else: diff = highDiff element = high if nearest is None or diff < nearest[1]: nearest = (element, diff) return Element(nearest[0])
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 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 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
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 from chimera import elements elementRadius = {} for i in range(len(chimera.elements.name)): element = Element(i) elementRadius[element] = 0.985 * Element.bondRadius(element) elementRadius[elements.C] = 0.7622 elementRadius[elements.H] = 0.1869 elementRadius[elements.N] = 0.6854 elementRadius[elements.O] = 0.6454 elementRadius[elements.P] = 0.9527 elementRadius[elements.S] = 1.0428 class ParamError(ValueError): pass def bondLength(a1, geom, e2, a2info=None): if e2.number == 1: from AddH import bondWithHLength return bondWithHLength(a1, geom) e1 = a1.element
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 hydPositions(heavy, includeLonePairs=False): """Return list of positions for hydrogens attached to this atom. If a hydrogen could be in one of several positions, don't return any of those. """ # first, find known attached atoms bondedHeavys = [] hyds = [] for atom in heavy.primaryNeighbors(): if atom.element.number > 1: bondedHeavys.append(atom) else: hyds.append(atom) # convert to Points hydLocs = [] for hyd in hyds: hydLocs.append(hyd.xformCoord()) if hydLocs and not includeLonePairs: # explicit hydrogens "win" over atom types return hydLocs if typeInfo.has_key(heavy.idatmType): info = typeInfo[heavy.idatmType] geom = info.geometry if includeLonePairs: subs = geom else: subs = info.substituents bondedLocs = hydLocs[:] for bHeavy in bondedHeavys: bondedLocs.append(bHeavy.xformCoord()) else: return hydLocs knownSubs = len(bondedLocs) if knownSubs >= subs or knownSubs == 0: return hydLocs # above eliminates 'single' geometry if knownSubs == 1 and geom == tetrahedral: # rotamer return hydLocs maxSubs = geom if maxSubs - subs > 0: # the "empty" bond could be anywhere return hydLocs heavyLoc = heavy.xformCoord() bondLen = Element.bondLength(heavy.element, Element(1)) if geom == planar: coPlanar = [] for bHeavy in bondedHeavys: try: bhGeom = typeInfo[bHeavy.idatmType].geometry except KeyError: bhGeom = None if bhGeom != planar: continue for atom in bHeavy.primaryNeighbors(): if atom != heavy: coPlanar.append(atom.xformCoord()) else: coPlanar = None hydLocs = hydLocs + bondPositions( heavyLoc, geom, bondLen, bondedLocs, coPlanar=coPlanar) return hydLocs
def findHBonds(models, intermodel=True, intramodel=True, donors=None, acceptors=None, distSlop=0.0, angleSlop=0.0, interSubmodel=False, cacheDA=False): # to restrict to specific donor/acceptor atoms, 'donors' and/or # acceptors should be atom lists (or dictionaries with atom keys) # # 'cacheDA' allows donors/acceptors in molecules to be cached if # it is anticipated that the same structures will be examined for # H-bonds repeatedly (e.g. a dynamics trajectory). if donors and not isinstance(donors, (dict, set)): limitedDonors = set(donors) else: limitedDonors = donors if acceptors and not isinstance(acceptors, (dict, set)): limitedAcceptors = set(acceptors) else: limitedAcceptors = acceptors global _Dcache, _Acache, _prevLimited if cacheDA: if limitedDonors: dIDs = [id(d) for d in limitedDonors] dIDs.sort() else: dIDs = None if limitedAcceptors: aIDs = [id(a) for a in limitedAcceptors] aIDs.sort() else: aIDs = None key = (dIDs, aIDs) if _prevLimited and _prevLimited != key: flushCache() _prevLimited = key from weakref import WeakKeyDictionary if _Dcache is None: _Dcache = WeakKeyDictionary() _Acache = WeakKeyDictionary() else: flushCache() global donorParams, acceptorParams global processedDonorParams, processedAcceptorParams global _computeCache global verbose global _problem _problem = None badConnectivities = 0 # Used as necessary to cache expensive calculations (by other # functions also) _computeCache = {} processKey = (distSlop, angleSlop) if processKey not in processedAcceptorParams: # copy.deepcopy() refuses to copy functions (even as # references), so do this instead... aParams = [] for p in acceptorParams: aParams.append(copy.copy(p)) for i in range(len(aParams)): aParams[i][3] = _processArgTuple(aParams[i][3], distSlop, angleSlop) processedAcceptorParams[processKey] = aParams else: aParams = processedAcceptorParams[processKey] # compute some info for generic acceptors/donors genericAccInfo = {} # oxygens... genericOAccArgs = _processArgTuple([3.53, 90], distSlop, angleSlop) genericAccInfo['miscO'] = (accGeneric, genericOAccArgs) # dictionary based on bonded atom's geometry... genericAccInfo['O2-'] = { single: (accGeneric, genericOAccArgs), linear: (accGeneric, genericOAccArgs), planar: (accPhiPsi, _processArgTuple([3.53, 90, 130], distSlop, angleSlop)), tetrahedral: (accGeneric, genericOAccArgs) } genericAccInfo['O3-'] = genericAccInfo['O2-'] genericAccInfo['O2'] = { single: (accGeneric, genericOAccArgs), linear: (accGeneric, genericOAccArgs), planar: (accPhiPsi, _processArgTuple([3.30, 110, 130], distSlop, angleSlop)), tetrahedral: (accThetaTau, _processArgTuple( [3.03, 100, -180, 145], distSlop, angleSlop)) } # list based on number of known bonded atoms... genericAccInfo['O3'] = [ (accGeneric, genericOAccArgs), (accThetaTau, _processArgTuple([3.17, 100, -161, 145], distSlop, angleSlop)), (accPhiPsi, _processArgTuple([3.42, 120, 135], distSlop, angleSlop)) ] # nitrogens... genericNAccArgs = _processArgTuple([3.42, 90], distSlop, angleSlop) genericAccInfo['miscN'] = (accGeneric, genericNAccArgs) genericAccInfo['N2'] = (accPhiPsi, _processArgTuple([3.42, 140, 135], distSlop, angleSlop)) # tuple based on number of bonded heavy atoms... genericN3MultHeavyAccArgs = _processArgTuple([3.30, 153, -180, 145], distSlop, angleSlop) genericAccInfo['N3'] = ( (accGeneric, genericNAccArgs), # only one example to draw from; weaken by .1A, 5 degrees (accThetaTau, _processArgTuple([3.13, 98, -180, 150], distSlop, angleSlop)), (accThetaTau, genericN3MultHeavyAccArgs), (accThetaTau, genericN3MultHeavyAccArgs) ) # one example only; weaken by .1A, 5 degrees genericAccInfo['N1'] = (accThetaTau, _processArgTuple( [3.40, 136, -180, 145], distSlop, angleSlop)) # sulfurs... # one example only; weaken by .1A, 5 degrees genericAccInfo['S2'] = (accPhiPsi, _processArgTuple([3.83, 85, 140], distSlop, angleSlop)) genericAccInfo['Sar'] = genericAccInfo['S3-'] = (accGeneric, _processArgTuple([3.83, 85], distSlop, angleSlop)) # now the donors... # planar nitrogens genDonNpl1HParams = (donThetaTau, _processArgTuple([2.23, 136, 2.23, 141, 140, 2.46, 136, 140], distSlop, angleSlop)) genDonNpl2HParams = (donUpsilonTau, _processArgTuple([3.30, 90, -153, 135, -45, 3.30, 90, -146, 140, -37.5, 130, 3.40, 108, -166, 125, -35, 140], distSlop, angleSlop)) genDonODists = [2.41, 2.28, 2.28, 3.27, 3.14, 3.14] genDonOParams = (donGeneric, _processArgTuple( genDonODists, distSlop, angleSlop)) genDonNDists = [2.36, 2.48, 2.48, 3.30, 3.42, 3.42] genDonNParams = (donGeneric, _processArgTuple( genDonNDists, distSlop, angleSlop)) genDonSDists = [2.42, 2.42, 2.42, 3.65, 3.65, 3.65] genDonSParams = (donGeneric, _processArgTuple( genDonSDists, distSlop, angleSlop)) genericDonInfo = { 'O': genDonOParams, 'N': genDonNParams, 'S': genDonSParams } accTrees = {} hbonds = [] hasSulfur = {} for model in models: replyobj.status("Finding acceptors in model '%s'\n" % model.name, blankAfter=0) if cacheDA \ and _Acache.has_key(model) \ and _Acache[model].has_key((distSlop, angleSlop)): accAtoms = [] accData = [] for accAtom, data in _Acache[model][(distSlop, angleSlop)].items(): if not accAtom.__destroyed__: accAtoms.append(accAtom) accData.append(data) else: accAtoms, accData = _findAcceptors(model, aParams, limitedAcceptors, genericAccInfo) if cacheDA: cache = WeakKeyDictionary() for i in range(len(accAtoms)): cache[accAtoms[i]] = accData[i] if not _Acache.has_key(model): _Acache[model] = {} _Acache[model][(distSlop, angleSlop)] = cache xyz = [] hasSulfur[model] = False for accAtom in accAtoms: c = accAtom.xformCoord() xyz.append([c.x, c.y, c.z]) if accAtom.element.number == Element.S: hasSulfur[model] = True replyobj.status("Building search tree of acceptor atoms\n", blankAfter=0) accTrees[model] = AdaptiveTree(xyz, accData, 3.0) if processKey not in processedDonorParams: # find max donor distances before they get squared.. # copy.deepcopy() refuses to copy functions (even as # references), so do this instead... dParams = [] for p in donorParams: dParams.append(copy.copy(p)) for di in range(len(dParams)): geomType = dParams[di][2] argList = dParams[di][4] donRad = Element.bondRadius(Element(Element.N)) if geomType == thetaTau: maxDist = max((argList[0], argList[2], argList[5])) elif geomType == upsilonTau: maxDist = max((argList[0], argList[5], argList[11])) elif geomType == water: maxDist = max((argList[1], argList[4], argList[8])) else: maxDist = max(genDonODists + genDonNDists + genDonSDists) donRad = Element.bondRadius(Element(Element.S)) dParams[di].append(maxDist + distSlop + donRad + Element.bondRadius(Element(Element.H))) for i in range(len(dParams)): dParams[i][4] = _processArgTuple(dParams[i][4], distSlop, angleSlop) processedDonorParams[processKey] = dParams else: dParams = processedDonorParams[processKey] genericWaterParams = _processArgTuple([2.36, 2.36 + OHbondDist, 146], distSlop, angleSlop) genericThetaTauParams = _processArgTuple([2.48, 132], distSlop, angleSlop) genericUpsilonTauParams = _processArgTuple([3.42, 90, -161, 125], distSlop, angleSlop) genericGenericParams = _processArgTuple([2.48, 3.42, 130, 90], distSlop, angleSlop) for dmi in range(len(models)): model = models[dmi] replyobj.status("Finding donors in model '%s'\n" % model.name, blankAfter=0) if cacheDA \ and _Dcache.has_key(model) \ and _Dcache[model].has_key((distSlop, angleSlop)): donAtoms = [] donData = [] for donAtom, data in _Dcache[model][(distSlop, angleSlop)].items(): if not donAtom.__destroyed__: donAtoms.append(donAtom) donData.append(data) else: donAtoms, donData = _findDonors(model, dParams, limitedDonors, genericDonInfo) if cacheDA: cache = WeakKeyDictionary() for i in range(len(donAtoms)): cache[donAtoms[i]] = donData[i] if not _Dcache.has_key(model): _Dcache[model] = {} _Dcache[model][(distSlop, angleSlop)] = cache replyobj.status("Matching donors in model '%s' to acceptors\n" % model.name, blankAfter=0) for i in range(len(donAtoms)): donorAtom = donAtoms[i] geomType, tauSym, argList, testDist = donData[i] donorHyds = hydPositions(donorAtom) coord = donorAtom.xformCoord() for accModel in models: if accModel == model and not intramodel\ or accModel != model and not intermodel: continue if accModel.id == model.id \ and not interSubmodel \ and accModel.subid != model.subid: continue if hasSulfur[accModel]: from commonGeom import SULFUR_COMP td = testDist + SULFUR_COMP else: td = testDist accs = accTrees[accModel].searchTree( [coord.x, coord.y, coord.z], td) if verbose: replyobj.message("Found %d possible acceptors for donor %s:\n" % (len(accs), donorAtom.oslIdent())) for accData in accs: replyobj.message("\t%s\n" % accData[0].oslIdent()) for accAtom, geomFunc, args in accs: if accAtom == donorAtom: # e.g. hydroxyl if verbose: print "skipping: donor == acceptor" continue # exclude hbonding between # differing alt locations of # same residue if accAtom.altLoc.isalnum() and donorAtom.altLoc.isalnum() and accAtom.residue == donorAtom.residue and accAtom.altLoc != donorAtom.altLoc: continue try: if not apply(geomFunc, (donorAtom, donorHyds) + args): continue except ConnectivityError, v: replyobj.message("Skipping possible acceptor with bad geometry: %s\n%s\n\n" % (accAtom.oslIdent(), v)) badConnectivities += 1 continue if verbose: replyobj.message("\t%s satisfies acceptor criteria\n" % accAtom.oslIdent()) if geomType == upsilonTau: donorFunc = donUpsilonTau addArgs = genericUpsilonTauParams + [tauSym] elif geomType == thetaTau: donorFunc = donThetaTau addArgs = genericThetaTauParams elif geomType == water: donorFunc = donWater addArgs = genericWaterParams else: if donorAtom.idatmType in ["Npl", "N2+"]: heavys = 0 for bonded in donorAtom.primaryNeighbors(): if bonded.element.number > 1: heavys += 1 if heavys > 1: info = genDonNpl1HParams else: info = genDonNpl2HParams else: info = genericDonInfo[donorAtom.element.name] donorFunc, argList = info addArgs = genericGenericParams if donorFunc == donUpsilonTau: # tack on generic # tau symmetry addArgs = genericUpsilonTauParams + [4] elif donorFunc == donThetaTau: addArgs = genericThetaTauParams try: if not apply(donorFunc, (donorAtom, donorHyds, accAtom) + tuple(argList + addArgs)): continue except ConnectivityError, v: replyobj.message("Skipping possible donor with bad geometry: %s\n%s\n\n" % (donorAtom.oslIdent(), v)) badConnectivities += 1 continue except AtomTypeError, v: _problem = ("atom type", donorAtom, v, None) continue if verbose: replyobj.message("\t%s satisfies donor criteria\n" % donorAtom.oslIdent()) hbonds.append((donorAtom, accAtom))
type of donor geometry degree of tau symmetry argument tuple used when geometry-check function is called in argument tuple, conversions will occur before function gets called. namely: positive floats are assumed to be distances, and will be squared integers are assumed to be angles in degrees, and will be converted to radians """ _het5NH = lambda mols: hetNH(mols, 5) _hetAro6NH = lambda mols: hetNH(mols, 6, aromaticOnly=1) water = intern("water") thetaTau = intern('thetaTau') upsilonTau = intern('upsilonTau') OHbondDist = Element.bondLength(Element(Element.O), Element(1)) donorParams = [ # neutral carboxylic acid [[['O3', ['Cac', H]], [1,1,0]], 0, upsilonTau, 2, (2.87, 103, -128, 140, -30, 2.87, 103, -128, 155, -30, 150, 2.87, 103, -128, 140, -30, 150)], # protonated nitrogen double-bonded to carbon [[['Npl', [['C2', [SingleBond, SingleBond]], H, H]], [1,1,0,0,0,0]], 0, upsilonTau, 4, (3.17, 90, -146, 140, -30, 3.17, 90, -146, 140, -22.5, 150, # next line extrapolated 3.17, 113, -161, 125, None, 140)], [[['Ng+', ['C2', H, H]], [1,1,0,0]],
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
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 from chimera import elements elementRadius = {} for i in range(len(chimera.elements.name)): element = Element(i) elementRadius[element] = 0.985 * Element.bondRadius(element) elementRadius[elements.C] = 0.7622 elementRadius[elements.H] = 0.1869 elementRadius[elements.N] = 0.6854 elementRadius[elements.O] = 0.6454 elementRadius[elements.P] = 0.9527 elementRadius[elements.S] = 1.0428 class ParamError(ValueError): pass def bondLength(a1, geom, e2, a2info=None): if e2.number == 1:
def hydPositions(heavy, includeLonePairs=False): """Return list of positions for hydrogens attached to this atom. If a hydrogen could be in one of several positions, don't return any of those. """ # first, find known attached atoms bondedHeavys = [] hyds = [] for atom in heavy.primaryNeighbors(): if atom.element.number > 1: bondedHeavys.append(atom) else: hyds.append(atom) # convert to Points hydLocs = [] for hyd in hyds: hydLocs.append(hyd.xformCoord()) if hydLocs and not includeLonePairs: # explicit hydrogens "win" over atom types return hydLocs if typeInfo.has_key(heavy.idatmType): info = typeInfo[heavy.idatmType] geom = info.geometry if includeLonePairs: subs = geom else: subs = info.substituents bondedLocs = hydLocs[:] for bHeavy in bondedHeavys: bondedLocs.append(bHeavy.xformCoord()) else: return hydLocs knownSubs = len(bondedLocs) if knownSubs >= subs or knownSubs == 0: return hydLocs # above eliminates 'single' geometry if knownSubs == 1 and geom == tetrahedral: # rotamer return hydLocs maxSubs = geom if maxSubs - subs > 0: # the "empty" bond could be anywhere return hydLocs heavyLoc = heavy.xformCoord() bondLen = Element.bondLength(heavy.element, Element(1)) if geom == planar: coPlanar = [] for bHeavy in bondedHeavys: try: bhGeom = typeInfo[bHeavy.idatmType].geometry except KeyError: bhGeom = None if bhGeom != planar: continue for atom in bHeavy.primaryNeighbors(): if atom != heavy: coPlanar.append(atom.xformCoord()) else: coPlanar = None hydLocs = hydLocs + bondPositions(heavyLoc, geom, bondLen, bondedLocs, coPlanar=coPlanar) return hydLocs
"Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm", # 91 - 100 "Md", "No", "Lr", # 101 - 103 ] LP = Element(Element.LonePair) H = Element(Element.H) He = Element(Element.He) Li = Element(Element.Li) Be = Element(Element.Be) B = Element(Element.B) C = Element(Element.C) N = Element(Element.N) O = Element(Element.O) F = Element(Element.F) Ne = Element(Element.Ne) Na = Element(Element.Na) Mg = Element(Element.Mg) Al = Element(Element.Al) Si = Element(Element.Si) P = Element(Element.P)
def _findDonors(model, dParams, limitedDonors, genericDonInfo): donAtoms = [] donData = [] stdDonors = {} for dp in dParams: groupKey, donorIndex, geomType, tauSym, argList, testDist = dp if groupKey: groups = findGroup(groupKey, [model]) else: # generic donors groups = [] for atom in model.atoms: if atom in stdDonors: continue if atom.element.number not in [7,8,16]: continue if limitedDonors \ and atom not in limitedDonors: continue # oxygen, nitrogen, or sulfur try: expectBonds = typeInfo[ atom.idatmType].substituents except KeyError: expectBonds = 0 numBonds = len(atom.primaryBonds()) # screen out the partial terminal N that # AddH can leave, since the geometry is # problematic and the H direction isn't # really determined if atom.idatmType == "Npl" \ and numBonds == 2 and 1 in [n.element.number for n in atom.primaryNeighbors()]: continue if numBonds < expectBonds: groups.append([atom]) continue for bonded in atom.primaryNeighbors(): if bonded.element.number == 1: groups.append([atom]) break if verbose: for g in groups: print "generic donor:", g[0].oslIdent() if groups and geomType == thetaTau: # extend probe distance by H-bond length # so that all relevant acceptors will be found testDist = testDist + Element.bondLength( groups[0][donorIndex].element, Element(1)) for group in groups: donorAtom = group[donorIndex] if limitedDonors \ and donorAtom not in limitedDonors: continue if donorAtom in stdDonors: if groupKey != stdDonors[donorAtom] and not ( # conflicts of non-ring groups with ring # groups not considered a problem (non-ring # groups "win") groupKey[0] in _ringFuncs and stdDonors[donorAtom][0] not in _ringFuncs): global _problem _problem = ("donor", donorAtom, stdDonors[donorAtom], groupKey) continue stdDonors[donorAtom] = groupKey donAtoms.append(donorAtom) donData.append((geomType, tauSym, argList, testDist)) return donAtoms, donData
from chimera import Coord, Point from chimera.bondGeom import tetrahedral, planar, linear, single, bondPositions from chimera.idatm import * from AddH import newHydrogen, findNearest, roomiest, bondWithHLength, \ findRotamerNearest from math import sin, cos, pi, sqrt from chimera import Element Element_H = Element(1) sin5475 = sin(pi * 54.75 / 180.0) cos5475 = cos(pi * 54.75 / 180.0) def addHydrogens(atom, bondingInfo, namingSchema, totalHydrogens, idatmType, invert, coordinations): away = away2 = planar = None geom = bondingInfo.geometry substs = bondingInfo.substituents curBonds = len(atom.primaryBonds()) needed = substs - curBonds if needed <= 0: return atPos = atom.xformCoord() exclude = coordinations + atom.primaryNeighbors() if geom == 3 and curBonds == 1: bonded = atom.primaryNeighbors()[0] grandBonded = bonded.primaryNeighbors() grandBonded.remove(atom) if len(grandBonded) < 3: planar = [a.xformCoord() for a in grandBonded] if geom == 4 and not exclude:
line = gl() retList = [] while line != marker: retList.append(line) line = gl() if not line: raise IOError, "EOF while reading to marker"\ " '%s'"% marker return retList def rewind(self): self.f.seek(0) from chimera import Element O = Element('O') N = Element('N') C = Element('C') H = Element('H') S = Element('S') Cu = Element('Cu') Fe = Element('Fe') Zn = Element('Zn') Mg = Element('Mg') Ca = Element('Ca') Ar = Element('Ar') F = Element('F') Cl = Element('Cl') Br = Element('Br') Na = Element('Na') Si = Element('Si')
def readSDF(fileName, identifyAs=None): from OpenSave import osOpen from chimera import UserError, Molecule, Element, openModels, replyobj from chimera import Coord state = "init" f = osOpen(fileName) mols = [] nonblank = False for l in f: line = l.strip() nonblank = nonblank or line if state == "init": state = "post header 1" molName = line elif state == "post header 1": state = "post header 2" elif state == "post header 2": state = "counts" elif state == "counts": if not line: break state = "atoms" serial = 1 anums = {} atoms = [] try: numAtoms = int(l[:3].strip()) numBonds = int(l[3:6].strip()) except ValueError: raise UserError("Atom/bond counts line of" " MOL/SDF file '%s' is botched: '%s'" % (fileName, l)) m = Molecule() mols.append(m) if identifyAs: m.name = identifyAs else: from chimera.misc import isInformativeName mn = molName.strip() if isInformativeName(mn): m.name = mn else: import os.path m.name = os.path.split(fileName)[-1] r = m.newResidue("UNK", " ", 1, " ") elif state == "atoms": numAtoms -= 1 if numAtoms == 0: if numBonds: state = "bonds" else: state = "properties" try: x = float(l[:10].strip()) y = float(l[10:20].strip()) z = float(l[21:30].strip()) elem = l[31:34].strip() except ValueError: raise UserError("Atom line of MOL/SDF file" " '%s' is not x y z element...: '%s'" % (fileName, l)) element = Element(elem) if element.number == 0: # lone pair or somesuch atoms.append(None) continue anum = anums.get(element.name, 0) + 1 anums[element.name] = anum a = m.newAtom("%s%d" % (element.name, anum), element) atoms.append(a) r.addAtom(a) a.setCoord(Coord(x, y, z)) a.serialNumber = serial serial += 1 elif state == "bonds": numBonds -= 1 if numBonds == 0: state = "properties" try: a1index = int(l[:3].strip()) a2index = int(l[3:6].strip()) order = int(l[6:9].strip()) except ValueError: raise UserError("Bond line of MOL/SDF file" " '%s' is not a1 a2 order...: '%s'" % (fileName, l)) a1 = atoms[a1index - 1] a2 = atoms[a2index - 1] if not a1 or not a2: continue m.newBond(a1, a2).order = order elif state == "properties": if not m.atoms: raise UserError("No atoms found for compound" " '%s' in MOL/SDF file '%s'" % (m.name, fileName)) if line.split() == ["M", "END"]: state = "data" elif state == "data": if line == "$$$$": nonblank = False state = "init" f.close() if nonblank and state not in ["data", "init"]: if mols: replyobj.warning("Extraneous text after final $$$$" " in MOL/SDF file '%s'" % fileName) else: raise UserError("Unexpected end of file (parser state:" " %s) in MOL/SDF file '%s'" % (state, fileName)) return mols