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
58 """
59 Chain class
60
61 The chain class contains information about each chain within a given
62 Protein object.
63 """
64
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
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
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
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
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
139 """
140 Return a list of Residue objects in this chain
141 """
142 return self.residues
143
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
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
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:
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
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
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
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
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
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
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
327
328 atom = self.map[atomname]
329 bonds = atom.bonds
330 del self.map[atomname]
331
332
333
334 self.atoms.remove(atom)
335
336
337
338 for bondatom in bonds:
339 if atom in bondatom.bonds:
340 bondatom.bonds.remove(atom)
341
342 del atom
343
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
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
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
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
400 return self.atoms
401
403 if name in self.map: return 1
404 else: return 0
405
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
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
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
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
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
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
509 for atom in self.atoms:
510 if atom.name not in ["N", "CA", "C", "O"]:
511 templist.append(atom)
512
513
514
515 self.atoms = templist[:]
516
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
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
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
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
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
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
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