Package Bio :: Package PDB :: Module Atom
[hide private]
[frames] | no frames]

Source Code for Module Bio.PDB.Atom

  1  # Copyright (C) 2002, Thomas Hamelryck (thamelry@binf.ku.dk) 
  2  # This code is part of the Biopython distribution and governed by its 
  3  # license.  Please see the LICENSE file that should have been included 
  4  # as part of this package. 
  5   
  6  """Atom class, used in Structure objects.""" 
  7   
  8  import numpy 
  9  import warnings 
 10  import copy 
 11   
 12  from Bio.PDB.Entity import DisorderedEntityWrapper 
 13  from Bio.PDB.PDBExceptions import PDBConstructionWarning 
 14  from Bio.PDB.vectors import Vector 
 15  from Bio.Data import IUPACData 
 16   
 17   
18 -class Atom(object):
19 - def __init__(self, name, coord, bfactor, occupancy, altloc, fullname, serial_number, 20 element=None):
21 """Create Atom object. 22 23 The Atom object stores atom name (both with and without spaces), 24 coordinates, B factor, occupancy, alternative location specifier 25 and (optionally) anisotropic B factor and standard deviations of 26 B factor and positions. 27 28 :param name: atom name (eg. "CA"). Note that spaces are normally stripped. 29 :type name: string 30 31 :param coord: atomic coordinates (x,y,z) 32 :type coord: Numeric array (Float0, size 3) 33 34 :param bfactor: isotropic B factor 35 :type bfactor: number 36 37 :param occupancy: occupancy (0.0-1.0) 38 :type occupancy: number 39 40 :param altloc: alternative location specifier for disordered atoms 41 :type altloc: string 42 43 :param fullname: full atom name, including spaces, e.g. " CA ". Normally 44 these spaces are stripped from the atom name. 45 :type fullname: string 46 47 :param element: atom element, e.g. "C" for Carbon, "HG" for mercury, 48 :type element: uppercase string (or None if unknown) 49 """ 50 self.level = "A" 51 # Reference to the residue 52 self.parent = None 53 # the atomic data 54 self.name = name # eg. CA, spaces are removed from atom name 55 self.fullname = fullname # e.g. " CA ", spaces included 56 self.coord = coord 57 self.bfactor = bfactor 58 self.occupancy = occupancy 59 self.altloc = altloc 60 self.full_id = None # (structure id, model id, chain id, residue id, atom id) 61 self.id = name # id of atom is the atom name (e.g. "CA") 62 self.disordered_flag = 0 63 self.anisou_array = None 64 self.siguij_array = None 65 self.sigatm_array = None 66 self.serial_number = serial_number 67 # Dictionary that keeps additional properties 68 self.xtra = {} 69 assert not element or element == element.upper(), element 70 self.element = self._assign_element(element) 71 self.mass = self._assign_atom_mass() 72 73 # For atom sorting (protein backbone atoms first) 74 self._sorting_keys = {'N': 0, 'CA': 1, 'C': 2, 'O': 3}
75 76 # Sorting Methods 77 # standard across different objects and allows direct comparison
78 - def __eq__(self, other):
79 """Test equality.""" 80 if isinstance(other, Atom): 81 return self.full_id[1:] == other.full_id[1:] 82 else: 83 return NotImplemented
84
85 - def __ne__(self, other):
86 """Test inequality.""" 87 if isinstance(other, Atom): 88 return self.full_id[1:] != other.full_id[1:] 89 else: 90 return NotImplemented
91
92 - def __gt__(self, other):
93 """Test greater then.""" 94 if isinstance(other, Atom): 95 if self.parent != other.parent: 96 return self.parent > other.parent 97 order_s = self._sorting_keys.get(self.name, 4) 98 order_o = self._sorting_keys.get(other.name, 4) 99 if order_s != order_o: 100 return order_s > order_o 101 elif self.name != other.name: 102 return self.name > other.name 103 else: 104 return self.altloc > other.altloc 105 else: 106 return NotImplemented
107
108 - def __ge__(self, other):
109 """Test greater or equal.""" 110 if isinstance(other, Atom): 111 if self.parent != other.parent: 112 return self.parent >= other.parent 113 order_s = self._sorting_keys.get(self.name, 4) 114 order_o = self._sorting_keys.get(other.name, 4) 115 if order_s != order_o: 116 return order_s >= order_o 117 elif self.name != other.name: 118 return self.name >= other.name 119 else: 120 return self.altloc >= other.altloc 121 else: 122 return NotImplemented
123
124 - def __lt__(self, other):
125 """Test less than.""" 126 if isinstance(other, Atom): 127 if self.parent != other.parent: 128 return self.parent < other.parent 129 order_s = self._sorting_keys.get(self.name, 4) 130 order_o = self._sorting_keys.get(other.name, 4) 131 if order_s != order_o: 132 return order_s < order_o 133 elif self.name != other.name: 134 return self.name < other.name 135 else: 136 return self.altloc < other.altloc 137 else: 138 return NotImplemented
139
140 - def __le__(self, other):
141 """Test less or equal.""" 142 if isinstance(other, Atom): 143 if self.parent != other.parent: 144 return self.parent <= other.parent 145 order_s = self._sorting_keys.get(self.name, 4) 146 order_o = self._sorting_keys.get(other.name, 4) 147 if order_s != order_o: 148 return order_s <= order_o 149 elif self.name != other.name: 150 return self.name <= other.name 151 else: 152 return self.altloc <= other.altloc 153 else: 154 return NotImplemented
155 156 # Hash method to allow uniqueness (set)
157 - def __hash__(self):
158 return hash(self.get_full_id())
159
160 - def _assign_element(self, element):
161 """Guess element from atom name if not recognised (PRIVATE).""" 162 if not element or element.capitalize() not in IUPACData.atom_weights: 163 # Inorganic elements have their name shifted left by one position 164 # (is a convention in PDB, but not part of the standard). 165 # isdigit() check on last two characters to avoid mis-assignment of 166 # hydrogens atoms (GLN HE21 for example) 167 168 if self.fullname[0].isalpha() and not self.fullname[2:].isdigit(): 169 putative_element = self.name.strip() 170 else: 171 # Hs may have digit in [0] 172 if self.name[0].isdigit(): 173 putative_element = self.name[1] 174 else: 175 putative_element = self.name[0] 176 177 if putative_element.capitalize() in IUPACData.atom_weights: 178 msg = "Used element %r for Atom (name=%s) with given element %r" \ 179 % (putative_element, self.name, element) 180 element = putative_element 181 else: 182 msg = "Could not assign element %r for Atom (name=%s) with given element %r" \ 183 % (putative_element, self.name, element) 184 element = "" 185 warnings.warn(msg, PDBConstructionWarning) 186 187 return element
188
189 - def _assign_atom_mass(self):
190 # Needed for Bio/Struct/Geometry.py C.O.M. function 191 if self.element: 192 return IUPACData.atom_weights[self.element.capitalize()] 193 else: 194 return float('NaN')
195 196 # Special methods 197
198 - def __repr__(self):
199 """Print Atom object as <Atom atom_name>.""" 200 return "<Atom %s>" % self.get_id()
201
202 - def __sub__(self, other):
203 """Calculate distance between two atoms. 204 205 :param other: the other atom 206 :type other: L{Atom} 207 208 Examples 209 -------- 210 >>> distance = atom1 - atom2 211 212 """ 213 diff = self.coord - other.coord 214 return numpy.sqrt(numpy.dot(diff, diff))
215 216 # set methods 217
218 - def set_serial_number(self, n):
219 self.serial_number = n
220
221 - def set_bfactor(self, bfactor):
222 self.bfactor = bfactor
223
224 - def set_coord(self, coord):
225 self.coord = coord
226
227 - def set_altloc(self, altloc):
228 self.altloc = altloc
229
230 - def set_occupancy(self, occupancy):
231 self.occupancy = occupancy
232
233 - def set_sigatm(self, sigatm_array):
234 """Set standard deviation of atomic parameters. 235 236 The standard deviation of atomic parameters consists 237 of 3 positional, 1 B factor and 1 occupancy standard 238 deviation. 239 240 :param sigatm_array: standard deviations of atomic parameters. 241 :type sigatm_array: Numeric array (length 5) 242 """ 243 self.sigatm_array = sigatm_array
244
245 - def set_siguij(self, siguij_array):
246 """Set standard deviations of anisotropic temperature factors. 247 248 :param siguij_array: standard deviations of anisotropic temperature factors. 249 :type siguij_array: Numeric array (length 6) 250 """ 251 self.siguij_array = siguij_array
252
253 - def set_anisou(self, anisou_array):
254 """Set anisotropic B factor. 255 256 :param anisou_array: anisotropic B factor. 257 :type anisou_array: Numeric array (length 6) 258 """ 259 self.anisou_array = anisou_array
260 261 # Public methods 262
263 - def flag_disorder(self):
264 """Set the disordered flag to 1. 265 266 The disordered flag indicates whether the atom is disordered or not. 267 """ 268 self.disordered_flag = 1
269
270 - def is_disordered(self):
271 """Return the disordered flag (1 if disordered, 0 otherwise).""" 272 return self.disordered_flag
273
274 - def set_parent(self, parent):
275 """Set the parent residue. 276 277 Arguments: 278 - parent - Residue object 279 280 """ 281 self.parent = parent 282 self.full_id = self.get_full_id()
283
284 - def detach_parent(self):
285 """Remove reference to parent.""" 286 self.parent = None
287
288 - def get_sigatm(self):
289 """Return standard deviation of atomic parameters.""" 290 return self.sigatm_array
291
292 - def get_siguij(self):
293 """Return standard deviations of anisotropic temperature factors.""" 294 return self.siguij_array
295
296 - def get_anisou(self):
297 """Return anisotropic B factor.""" 298 return self.anisou_array
299
300 - def get_parent(self):
301 """Return parent residue.""" 302 return self.parent
303
304 - def get_serial_number(self):
305 return self.serial_number
306
307 - def get_name(self):
308 """Return atom name.""" 309 return self.name
310
311 - def get_id(self):
312 """Return the id of the atom (which is its atom name).""" 313 return self.id
314
315 - def get_full_id(self):
316 """Return the full id of the atom. 317 318 The full id of an atom is the tuple 319 (structure id, model id, chain id, residue id, atom name, altloc). 320 """ 321 return self.parent.get_full_id() + ((self.name, self.altloc),)
322
323 - def get_coord(self):
324 """Return atomic coordinates.""" 325 return self.coord
326
327 - def get_bfactor(self):
328 """Return B factor.""" 329 return self.bfactor
330
331 - def get_occupancy(self):
332 """Return occupancy.""" 333 return self.occupancy
334
335 - def get_fullname(self):
336 """Return the atom name, including leading and trailing spaces.""" 337 return self.fullname
338
339 - def get_altloc(self):
340 """Return alternative location specifier.""" 341 return self.altloc
342
343 - def get_level(self):
344 return self.level
345
346 - def transform(self, rot, tran):
347 """Apply rotation and translation to the atomic coordinates. 348 349 :param rot: A right multiplying rotation matrix 350 :type rot: 3x3 Numeric array 351 352 :param tran: the translation vector 353 :type tran: size 3 Numeric array 354 355 Examples 356 -------- 357 >>> rotation=rotmat(pi, Vector(1, 0, 0)) 358 >>> translation=array((0, 0, 1), 'f') 359 >>> atom.transform(rotation, translation) 360 361 """ 362 self.coord = numpy.dot(self.coord, rot) + tran
363
364 - def get_vector(self):
365 """Return coordinates as Vector. 366 367 :return: coordinates as 3D vector 368 :rtype: Bio.PDB.Vector class 369 """ 370 x, y, z = self.coord 371 return Vector(x, y, z)
372
373 - def copy(self):
374 """Create a copy of the Atom. 375 376 Parent information is lost. 377 """ 378 # Do a shallow copy then explicitly copy what needs to be deeper. 379 shallow = copy.copy(self) 380 shallow.detach_parent() 381 shallow.set_coord(copy.copy(self.get_coord())) 382 shallow.xtra = self.xtra.copy() 383 return shallow
384 385
386 -class DisorderedAtom(DisorderedEntityWrapper):
387 """Contains all Atom objects that represent the same disordered atom. 388 389 One of these atoms is "selected" and all method calls not caught 390 by DisorderedAtom are forwarded to the selected Atom object. In that way, a 391 DisorderedAtom behaves exactly like a normal Atom. By default, the selected 392 Atom object represents the Atom object with the highest occupancy, but a 393 different Atom object can be selected by using the disordered_select(altloc) 394 method. 395 """ 396
397 - def __init__(self, id):
398 """Create DisorderedAtom. 399 400 Arguments: 401 - id - string, atom name 402 403 """ 404 # TODO - make this a private attribute? 405 self.last_occupancy = -999999 406 DisorderedEntityWrapper.__init__(self, id)
407 408 # Special methods 409 # Override parent class __iter__ method
410 - def __iter__(self):
411 for i in self.disordered_get_list(): 412 yield i
413
414 - def __repr__(self):
415 return "<Disordered Atom %s>" % self.get_id()
416
417 - def disordered_add(self, atom):
418 """Add a disordered atom.""" 419 # Add atom to dict, use altloc as key 420 atom.flag_disorder() 421 # set the residue parent of the added atom 422 residue = self.get_parent() 423 atom.set_parent(residue) 424 altloc = atom.get_altloc() 425 occupancy = atom.get_occupancy() 426 self[altloc] = atom 427 if occupancy > self.last_occupancy: 428 self.last_occupancy = occupancy 429 self.disordered_select(altloc)
430