Package MolKit :: Package pdb2pqr :: Package src :: Module structures
[hide private]
[frames] | no frames]

Source Code for Module MolKit.pdb2pqr.src.structures

  1  """ 
  2      Structures for PDB2PQR 
  3   
  4      This module contains the structure objects used in PDB2PQR and their 
  5      associated methods. 
  6   
  7      ---------------------------- 
  8      
  9      PDB2PQR -- An automated pipeline for the setup, execution, and analysis of 
 10      Poisson-Boltzmann electrostatics calculations 
 11   
 12      Nathan A. Baker (baker@biochem.wustl.edu) 
 13      Todd Dolinsky (todd@ccb.wustl.edu) 
 14      Dept. of Biochemistry and Molecular Biophysics 
 15      Center for Computational Biology 
 16      Washington University in St. Louis 
 17   
 18      Jens Nielsen (Jens.Nielsen@ucd.ie) 
 19      University College Dublin 
 20   
 21      Additional contributing authors listed in documentation and supporting 
 22      package licenses. 
 23   
 24      Copyright (c) 2003-2007.  Washington University in St. Louis.   
 25      All Rights Reserved. 
 26   
 27      This file is part of PDB2PQR. 
 28   
 29      PDB2PQR is free software; you can redistribute it and/or modify 
 30      it under the terms of the GNU General Public License as published by 
 31      the Free Software Foundation; either version 2 of the License, or 
 32      (at your option) any later version. 
 33    
 34      PDB2PQR is distributed in the hope that it will be useful, 
 35      but WITHOUT ANY WARRANTY; without even the implied warranty of 
 36      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 37      GNU General Public License for more details. 
 38      
 39      You should have received a copy of the GNU General Public License 
 40      along with PDB2PQR; if not, write to the Free Software 
 41      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA 
 42   
 43      ---------------------------- 
 44   
 45  """ 
 46   
 47  __date__ = "28 February 2006" 
 48  __author__ = "Todd Dolinsky" 
 49   
 50  BACKBONE = ["N","CA","C","O","O2","HA","HN","H","tN"] 
 51   
 52  import string 
 53  from pdb import * 
 54  from utilities import * 
 55  from quatfit import * 
 56   
57 -class Chain:
58 """ 59 Chain class 60 61 The chain class contains information about each chain within a given 62 Protein object. 63 """ 64
65 - def __init__(self, chainID):
66 """ 67 Initialize the class 68 69 Parameters 70 chainID: The chainID for this chain as denoted in 71 the PDB file (string) 72 """ 73 74 self.chainID = chainID 75 self.residues = []
76
77 - def get(self, name):
78 """ 79 Get a member of the Chain class 80 81 Parameters 82 name: The name of the member 83 Possible Values 84 ID: The ID of the chain 85 Residues: The list of residues within the Chain 86 Returns 87 item: The value of the member 88 """ 89 if name == "atoms": self.getAtoms() 90 else: 91 try: 92 item = getattr(self, name) 93 return item 94 except AttributeError: 95 message = "Unable to get object \"%s\" in class Chain" % name 96 raise ValueError, message
97
98 - def addResidue(self, residue):
99 """ 100 Add a residue to the chain 101 102 Parameters 103 residue: The residue to be added (Residue) 104 """ 105 self.residues.append(residue)
106
107 - def numResidues(self):
108 """ 109 Get the number of residues for the chain 110 111 Returns 112 count: Number of residues in the chain (int) 113 """ 114 count = 0 115 for residue in self.residues: 116 count += 1 117 return count
118
119 - def renumberResidues(self):
120 """ 121 Renumber Atoms based on actual Residue number and not PDB resSeq 122 """ 123 count = 1 124 for residue in self.residues: 125 residue.setResSeq(count) 126 count += 1
127
128 - def numAtoms(self):
129 """ 130 Get the number of atoms for the chain 131 132 Returns 133 count: Number of atoms in the chain (int) 134 """ 135 count = len(self.getAtoms()) 136 return count
137
138 - def getResidues(self):
139 """ 140 Return a list of Residue objects in this chain 141 """ 142 return self.residues
143
144 - def getAtoms(self):
145 """ 146 Return a list of Atom objects contained in this chain 147 148 Returns 149 atomlist: List of Atom objects (list) 150 """ 151 atomlist = [] 152 for residue in self.residues: 153 myList = residue.get("atoms") 154 for atom in myList: 155 atomlist.append(atom) 156 return atomlist
157 158
159 -class Residue:
160 """ 161 Residue class 162 163 The residue class contains a list of Atom objects associated with that 164 residue and other helper functions. 165 """ 166
167 - def __init__(self, atoms):
168 """ 169 Initialize the class 170 171 Parameters 172 atoms: A list of Atom objects to be stored in this class 173 (list) 174 """ 175 176 sampleAtom = atoms[-1] 177 178 self.atoms = [] 179 self.name = sampleAtom.resName 180 self.chainID = sampleAtom.chainID 181 self.resSeq = sampleAtom.resSeq 182 self.iCode = sampleAtom.iCode 183 184 self.map = {} 185 186 self.naname = None 187 188 atomclass = "" 189 for a in atoms: 190 if isinstance(a,ATOM): 191 atomclass = "ATOM" 192 elif isinstance(a, HETATM): 193 atomclass = "HETATM" 194 atom = Atom(a, atomclass, self) 195 atomname = atom.get("name") 196 if atomname not in self.map: 197 self.addAtom(atom) 198 else: # Don't add duplicate atom 199 oldatom = self.getAtom(atomname) 200 oldatom.set("altLoc","") 201 202 if self.name == "HOH": 203 self.name = "WAT" 204 for atom in self.atoms: 205 atom.set("resName","WAT")
206
207 - def __str__(self):
208 """ 209 Basic string representation for debugging 210 """ 211 text = "%s %s %i" % (self.name, self.chainID, self.resSeq) 212 return text
213
214 - def get(self, name):
215 """ 216 Get a member of the Residue class 217 218 Parameters 219 name: The name of the member (string) 220 Possible Values 221 atoms: The atoms in the residue 222 name: The name of the residue 223 chainID: The chainID associated with the residue 224 resSeq: The sequence number of the residue 225 icode: The iCode of the residue 226 SSbonded: 1 if the residue has a SS bond, 0 otherwise 227 SSbondpartner: The residue of the bond partner 228 type: The type associated with this residue 229 isNterm: # of hydrogens if the residue is the N-Terminus, 0 otherwise 230 isCterm: 1 if the residue is the C-Terminus, 0 otherwise 231 missing: List of missing atoms of the residue 232 Returns 233 item: The value of the member 234 """ 235 try: 236 item = getattr(self, name) 237 return item 238 except AttributeError: 239 message = "Unable to access object \"%s\" in class Residue" % name 240 raise ValueError, message
241
242 - def set(self, name, value):
243 """ 244 Set a member of the Residue class to a specific value 245 246 Parameters 247 name: The name of the object to set (string) 248 value: The object to append 249 Possible Values 250 atoms: The atoms in the residue 251 name: The name of the residue 252 chain: The chainID associated with the residue 253 resSeq: The sequence number of the residue 254 icode: The iCode of the residue 255 SSbonded: 1 if the residue has a SS bond, 0 otherwise 256 SSbondpartner: The residue of the bond partner 257 type: The type associated with this residue 258 isNterm: # of hydrogens if the residue is the N-Terminus, 0 otherwise 259 isCterm: 1 if the residue is the C-Terminus, 0 otherwise 260 isDirty: 1 if the residue is not missing atoms, 261 0 otherwise 262 Notes 263 resSeq points to the residue.setResSeq function 264 Returns 265 item: The value of the member 266 """ 267 if name == "resSeq": self.setResSeq(value) 268 else: 269 try: 270 setattr(self, name, value) 271 except AttributeError: 272 message = "Unable to set object \"%s\" in class Residue" % name 273 raise ValueError, message
274
275 - def numAtoms(self):
276 """ 277 Get the number of atoms for the residue 278 279 Returns 280 count: Number of atoms in the residue (int) 281 """ 282 count = len(self.atoms) 283 return count
284
285 - def setResSeq(self, value):
286 """ 287 Set the atom field resSeq to a certain value and 288 change the residue's information. The icode field is no longer 289 useful. 290 291 Parameters 292 value: The new value of resSeq (int) 293 """ 294 self.iCode = "" 295 self.resSeq = value 296 for atom in self.atoms: 297 atom.set("resSeq",value) 298 atom.set("iCode","")
299
300 - def setChainID(self, value):
301 """ 302 Set the chainID field to a certain value 303 """ 304 self.chainID = value 305 for atom in self.atoms: 306 atom.set("chainID", value)
307
308 - def addAtom(self, atom):
309 """ 310 Add the atom object to the residue. 311 312 Parameters 313 atom: The object to be added (ATOM) 314 """ 315 self.atoms.append(atom) 316 self.map[atom.get("name")] = atom
317
318 - def removeAtom(self, atomname):
319 """ 320 Remove an atom from the residue object. 321 322 Parameters 323 atomname: The name of the atom to be removed (string) 324 """ 325 326 # Delete the atom from the map 327 328 atom = self.map[atomname] 329 bonds = atom.bonds 330 del self.map[atomname] 331 332 # Delete the atom from the list 333 334 self.atoms.remove(atom) 335 336 # Delete all instances of the atom as a bond 337 338 for bondatom in bonds: 339 if atom in bondatom.bonds: 340 bondatom.bonds.remove(atom) 341 342 del atom
343
344 - def renameAtom(self, oldname, newname):
345 """ 346 Rename an atom to a new name 347 348 Parameters 349 oldname: The old atom name (string) 350 newname: The new atom name (string) 351 """ 352 atom = self.map[oldname] 353 atom.set("name",newname) 354 self.map[newname] = atom 355 del self.map[oldname]
356
357 - def createAtom(self, name, newcoords, type):
358 """ 359 Add a new atom object to the residue. Uses an atom 360 currently in the residue to seed the new atom 361 object, then replaces the coordinates and name accordingly. 362 363 Parameters 364 name: The name of the new atom (string) 365 newcoords: The x,y,z coordinates of the new atom (list) 366 type: The type of atom, ATOM or HETATM 367 """ 368 oldatom = self.atoms[0] 369 newatom = Atom(oldatom, type, self) 370 newatom.set("x",newcoords[0]) 371 newatom.set("y",newcoords[1]) 372 newatom.set("z",newcoords[2]) 373 newatom.set("name", name) 374 newatom.set("occupancy",1.00) 375 newatom.set("tempFactor",0.00) 376 self.addAtom(newatom)
377
378 - def addMissing(self, value):
379 """ 380 Add the value to the list of missing atoms 381 382 Parameters 383 value: The name of the missing atom (string) 384 """ 385 self.missing.append(value)
386
387 - def getAtom(self, name):
388 """ 389 Retrieve an atom from the mapping 390 391 Parameters 392 resname: The name of the residue to retrieve (string) 393 """ 394 try: 395 return self.map[name] 396 except KeyError: 397 return None
398
399 - def getAtoms(self):
400 return self.atoms
401
402 - def hasAtom(self, name):
403 if name in self.map: return 1 404 else: return 0
405
406 - def getCharge(self):
407 """ 408 Get the total charge of the residue. In order to get rid 409 of floating point rounding error, do the string 410 transformation. 411 412 Returns: 413 charge: The charge of the residue (float) 414 """ 415 charge = 0.0 416 for atom in self.atoms: 417 atomcharge = atom.get("ffcharge") 418 if atomcharge != None: 419 charge = charge + atomcharge 420 421 charge = float("%.4f" % charge) 422 return charge
423
424 - def renameResidue(self, name):
425 """ 426 Rename a given residue 427 428 Parameters 429 name: The new name of the residue 430 """ 431 self.name = name 432 for atom in self.atoms: 433 atom.resName = name
434
435 - def rotateTetrahedral(self, atom1, atom2, angle):
436 """ 437 Rotate about the atom1-atom2 bond by a given angle 438 All atoms connected to atom2 will rotate. 439 440 Parameters: 441 atom1: The first atom of the bond to rotate about (atom) 442 atom2: The second atom of the bond to rotate about (atom) 443 angle: The number of degrees to rotate (float) 444 """ 445 moveatoms = [] 446 movecoords = [] 447 448 initcoords = subtract(atom2.getCoords(), atom1.getCoords()) 449 450 # Determine which atoms to rotate 451 452 for atom in atom2.bonds: 453 if atom == atom1: continue 454 moveatoms.append(atom) 455 movecoords.append(subtract(atom.getCoords(), atom1.getCoords())) 456 457 newcoords = qchichange(initcoords, movecoords, angle) 458 for i in range(len(moveatoms)): 459 atom = moveatoms[i] 460 x = (newcoords[i][0] + atom1.get("x")) 461 y = (newcoords[i][1] + atom1.get("y")) 462 z = (newcoords[i][2] + atom1.get("z")) 463 atom.set("x", x) 464 atom.set("y", y) 465 atom.set("z", z)
466 467
468 - def setDonorsAndAcceptors(self):
469 """ 470 Set the donors and acceptors within the residue 471 """ 472 if not hasattr(self, "reference"): return 473 for atom in self.getAtoms(): 474 atomname = atom.get("name") 475 resname = self.name 476 477 atom.set("hdonor", 0) 478 atom.set("hacceptor", 0) 479 480 if atomname.startswith("N"): 481 bonded = 0 482 for bondedatom in atom.bonds: 483 if bondedatom.isHydrogen(): 484 atom.set("hdonor",1) 485 bonded = 1 486 break 487 if not bonded and self.reference.name == "HIS": 488 atom.set("hacceptor",1) 489 490 elif atomname.startswith("O") or \ 491 (atomname.startswith("S") and self.reference.name == "CYS"): 492 atom.set("hacceptor",1) 493 for bondedatom in atom.bonds: 494 if bondedatom.isHydrogen(): 495 atom.set("hdonor",1) 496 break
497
498 - def reorder(self):
499 """ 500 Reorder the atoms to start with N, CA, C, O if they exist 501 """ 502 templist = [] 503 if self.hasAtom("N"): templist.append(self.getAtom("N")) 504 if self.hasAtom("CA"): templist.append(self.getAtom("CA")) 505 if self.hasAtom("C"): templist.append(self.getAtom("C")) 506 if self.hasAtom("O"): templist.append(self.getAtom("O")) 507 508 # Add remaining atoms 509 for atom in self.atoms: 510 if atom.name not in ["N", "CA", "C", "O"]: 511 templist.append(atom) 512 513 # Change the list pointer 514 515 self.atoms = templist[:]
516
517 -class Atom(ATOM):
518 """ 519 Class Atom 520 521 The Atom class inherits off the ATOM object in pdb.py. It is used 522 for adding fields not found in the pdb that may be useful for analysis. 523 Also simplifies code by combining ATOM and HETATM objects into a 524 single class. 525 """ 526
527 - def __init__(self, atom, type, residue):
528 """ 529 Initialize the new Atom object by using the old object. 530 531 Parameters 532 atom: The original ATOM object (ATOM) 533 type: Either ATOM or HETATM (string) 534 residue: A pointer back to the parent residue object (Residue) 535 """ 536 if type == "ATOM" or type == "HETATM": 537 self.type = type 538 else: 539 raise ValueError, "Invalid atom type %s (Atom Class IN structures.py)!" 540 self.serial = atom.serial 541 self.name = atom.name 542 self.altLoc = "" 543 self.resName = atom.resName 544 self.chainID = atom.chainID 545 self.resSeq = atom.resSeq 546 self.iCode = atom.iCode 547 self.x = atom.x 548 self.y = atom.y 549 self.z = atom.z 550 self.occupancy = atom.occupancy 551 self.tempFactor = atom.tempFactor 552 self.segID = atom.segID 553 self.element = atom.element 554 self.charge = atom.charge 555 556 self.bonds = [] 557 self.reference = None 558 self.residue = residue 559 self.radius = None 560 self.ffcharge = None 561 self.hdonor = 0 562 self.hacceptor = 0 563 self.cell = None 564 self.added = 0 565 self.optimizeable = 0 566 self.refdistance = 0
567
568 - def __str__(self):
569 """ 570 Returns a string of the new atom type. Uses the ATOM string 571 output but changes the first field to either by ATOM or 572 HETATM as necessary. 573 574 Returns 575 str: String with ATOM/HETATM field set appropriately 576 """ 577 str = "" 578 tstr = self.type 579 str = str + string.ljust(tstr, 6)[:6] 580 tstr = "%d" % self.serial 581 str = str + string.rjust(tstr, 5)[:5] 582 str = str + " " 583 tstr = self.name 584 if len(tstr) == 4: 585 str = str + string.ljust(tstr, 4)[:4] 586 else: 587 str = str + " " + string.ljust(tstr, 3)[:3] 588 589 tstr = self.resName 590 if len(tstr) == 4: 591 str = str + string.ljust(tstr, 4)[:4] 592 else: 593 str = str + " " + string.ljust(tstr, 3)[:3] 594 595 str = str + " " 596 tstr = self.chainID 597 str = str + string.ljust(tstr, 1)[:1] 598 tstr = "%d" % self.resSeq 599 str = str + string.rjust(tstr, 4)[:4] 600 str = str + " " 601 tstr = "%8.3f" % self.x 602 str = str + string.ljust(tstr, 8)[:8] 603 tstr = "%8.3f" % self.y 604 str = str + string.ljust(tstr, 8)[:8] 605 tstr = "%8.3f" % self.z 606 str = str + string.ljust(tstr, 8)[:8] 607 if self.ffcharge != None: ffcharge = "%.4f" % self.ffcharge 608 else: ffcharge = "0.0000" 609 str = str + string.rjust(ffcharge, 8)[:8] 610 if self.radius != None: ffradius = "%.4f" % self.radius 611 else: ffradius = "0.0000" 612 str = str + string.rjust(ffradius, 7)[:7] 613 return str
614
615 - def get(self, name):
616 """ 617 Get a member of the Atom class 618 619 Parameters 620 name: The name of the member (string) 621 Possible Values 622 type: The type of Atom (either ATOM or HETATM) 623 serial: Atom serial number 624 name: Atom name 625 altLoc: Alternate location 626 resName: Residue name 627 chainID: Chain identifier 628 resSeq: Residue sequence number 629 iCode: Code for insertion of residues 630 x: Orthogonal coordinates for X in Angstroms. 631 y: Orthogonal coordinates for Y in Angstroms. 632 z: Orthogonal coordinates for Z in Angstroms. 633 occupancy: Occupancy 634 tempFactor: Temperature Factor 635 segID: Segment identifier 636 element: Element symbol 637 charge: Charge on the atom 638 bonds: The bonds associated with the atom 639 interbonds: The intrabonds associated with the atom 640 extrabonds: The extrabonds assocaited with the atom 641 residue: The parent residue of the atom 642 radius: The radius of the atom 643 ffcharge: The forcefield charge on the atom 644 hdonor: Whether the atom is a hydrogen donor 645 hacceptor: Whether the atom is a hydrogen acceptor 646 Returns 647 item: The value of the member 648 """ 649 try: 650 item = getattr(self, name) 651 return item 652 except AttributeError: 653 message = "Unable to access object \"%s\" in class Atom" % name 654 raise ValueError, message
655
656 - def set(self, name, value):
657 """ 658 Set a member of the Atom class 659 660 Parameters 661 name: The name of the member (string) 662 value: The value to set the member to 663 Possible Values 664 type: The type of Atom (either ATOM or HETATM) 665 serial: Atom serial number 666 name: Atom name 667 altLoc: Alternate location 668 resName: Residue name 669 chainID: Chain identifier 670 resSeq: Residue sequence number 671 iCode: Code for insertion of residues 672 x: Orthogonal coordinates for X in Angstroms. 673 y: Orthogonal coordinates for Y in Angstroms. 674 z: Orthogonal coordinates for Z in Angstroms. 675 occupancy: Occupancy 676 tempFactor: Temperature Factor 677 segID: Segment identifier 678 element: Element symbol 679 charge: Charge on the atom 680 residue: The parent residue of the atom 681 radius: The radius of the atom 682 ffcharge: The forcefield charge on the atom 683 hdonor: Whether the atom is a hydrogen donor 684 hacceptor: Whether the atom is a hydrogen acceptor 685 Returns 686 item: The value of the member 687 """ 688 try: 689 setattr(self, name, value) 690 except AttributeError: 691 message = "Unable to set object \"%s\" in class Atom" % name 692 raise ValueError, message
693
694 - def getCoords(self):
695 """ 696 Return the x,y,z coordinates of the atom in list form 697 698 Returns 699 List of the coordinates (list) 700 """ 701 return [self.x, self.y, self.z]
702
703 - def addBond(self, bondedatom):
704 """ 705 Add a bond to the list of bonds 706 707 Parameters: 708 bondedatom: The atom to bond to (Atom) 709 """ 710 self.bonds.append(bondedatom)
711
712 - def isHydrogen(self):
713 """ 714 Is this atom a Hydrogen atom? 715 716 Returns 717 value: 1 if Atom is a Hydrogen, 0 otherwise 718 """ 719 value = 0 720 if self.name[0] == "H": value = 1 721 return value
722
723 - def isBackbone(self):
724 """ 725 Return true if atom name is in backbone, otherwise false 726 727 Returns 728 state: 1 if true, 0 if false 729 """ 730 state = 0 731 if self.name in BACKBONE: 732 state = 1 733 return state
734
735 - def hasReference(self):
736 """ 737 Determine if the atom object has a reference object or not. 738 All known atoms should have reference objects. 739 740 Returns 741 1 if atom has a reference object, 0 otherwise. 742 """ 743 744 if self.reference != None: return 1 745 else: return 0
746