Back to index

python-biopython  1.60
Atom.py
Go to the documentation of this file.
00001 # Copyright (C) 2002, Thomas Hamelryck (thamelry@binf.ku.dk)
00002 # This code is part of the Biopython distribution and governed by its
00003 # license.  Please see the LICENSE file that should have been included
00004 # as part of this package.           
00005 
00006 """Atom class, used in Structure objects."""
00007 
00008 import numpy
00009 import warnings
00010 import copy
00011 
00012 from Bio.PDB.Entity import DisorderedEntityWrapper
00013 from Bio.PDB.PDBExceptions import PDBConstructionWarning
00014 from Bio.PDB.Vector import Vector
00015 from Bio.Data import IUPACData
00016 
00017 class Atom(object):
00018     def __init__(self, name, coord, bfactor, occupancy, altloc, fullname, serial_number,
00019                  element=None):
00020         """
00021         Atom object.
00022 
00023         The Atom object stores atom name (both with and without spaces), 
00024         coordinates, B factor, occupancy, alternative location specifier
00025         and (optionally) anisotropic B factor and standard deviations of 
00026         B factor and positions.
00027   
00028         @param name: atom name (eg. "CA"). Note that spaces are normally stripped.
00029         @type name: string
00030 
00031         @param coord: atomic coordinates (x,y,z)
00032         @type coord: Numeric array (Float0, size 3)
00033 
00034         @param bfactor: isotropic B factor
00035         @type bfactor: number 
00036 
00037         @param occupancy: occupancy (0.0-1.0)
00038         @type occupancy: number
00039 
00040         @param altloc: alternative location specifier for disordered atoms
00041         @type altloc: string
00042 
00043         @param fullname: full atom name, including spaces, e.g. " CA ". Normally
00044         these spaces are stripped from the atom name. 
00045         @type fullname: string
00046 
00047         @param element: atom element, e.g. "C" for Carbon, "HG" for mercury,
00048         @type fullname: uppercase string (or None if unknown)
00049      
00050         """
00051         self.level="A"
00052         # Reference to the residue 
00053         self.parent=None
00054         # the atomic data
00055         self.name=name      # eg. CA, spaces are removed from atom name
00056         self.fullname=fullname  # e.g. " CA ", spaces included
00057         self.coord=coord
00058         self.bfactor=bfactor
00059         self.occupancy=occupancy
00060         self.altloc=altloc
00061         self.full_id=None   # (structure id, model id, chain id, residue id, atom id)
00062         self.id=name        # id of atom is the atom name (e.g. "CA")
00063         self.disordered_flag=0
00064         self.anisou_array=None
00065         self.siguij_array=None
00066         self.sigatm_array=None
00067         self.serial_number=serial_number
00068         # Dictionary that keeps addictional properties
00069         self.xtra={}
00070         assert not element or element == element.upper(), element
00071         self.element = self._assign_element(element)
00072         self.mass = self._assign_atom_mass()
00073         
00074     def _assign_element(self, element):
00075         """Tries to guess element from atom name if not recognised."""
00076         if not element or element.capitalize() not in IUPACData.atom_weights:
00077             # Inorganic elements have their name shifted left by one position 
00078             #  (is a convention in PDB, but not part of the standard).
00079             # isdigit() check on last two characters to avoid mis-assignment of 
00080             # hydrogens atoms (GLN HE21 for example)
00081 
00082             if self.fullname[0] != " " and not self.fullname[2:].isdigit():
00083                 putative_element = self.name.strip()
00084             else:
00085                 # Hs may have digit in [0]
00086                 if self.name[0].isdigit():
00087                     putative_element = self.name[1]
00088                 else:
00089                     putative_element = self.name[0]
00090             
00091             if putative_element.capitalize() in IUPACData.atom_weights:
00092                 msg = "Used element %r for Atom (name=%s) with given element %r" \
00093                       % (putative_element, self.name, element)
00094                 element = putative_element
00095             else:
00096                 msg = "Could not assign element %r for Atom (name=%s) with given element %r" \
00097                       % (putative_element, self.name, element)
00098                 element = ""
00099             warnings.warn(msg, PDBConstructionWarning)
00100                 
00101         return element
00102         
00103     def _assign_atom_mass(self):
00104         # Needed for Bio/Struct/Geometry.py C.O.M. function
00105         if self.element:
00106             return IUPACData.atom_weights[self.element.capitalize()]
00107         else:
00108             return float('NaN')
00109 
00110 
00111     # Special methods   
00112 
00113     def __repr__(self):
00114         "Print Atom object as <Atom atom_name>."
00115         return "<Atom %s>" % self.get_id()
00116 
00117     def __sub__(self, other):
00118         """
00119         Calculate distance between two atoms.
00120         
00121         Example:
00122             >>> distance=atom1-atom2
00123 
00124         @param other: the other atom
00125         @type other: L{Atom}
00126         """
00127         diff=self.coord-other.coord
00128         return numpy.sqrt(numpy.dot(diff,diff))
00129 
00130     # set methods
00131 
00132     def set_serial_number(self, n):
00133         self.serial_number=n
00134 
00135     def set_bfactor(self, bfactor):
00136         self.bfactor=bfactor
00137 
00138     def set_coord(self, coord):
00139         self.coord=coord
00140 
00141     def set_altloc(self, altloc):
00142         self.altloc=altloc
00143 
00144     def set_occupancy(self, occupancy):
00145         self.occupancy=occupancy
00146 
00147     def set_sigatm(self, sigatm_array):
00148         """
00149         Set standard deviation of atomic parameters.
00150 
00151         The standard deviation of atomic parameters consists
00152         of 3 positional, 1 B factor and 1 occupancy standard 
00153         deviation.
00154 
00155         @param sigatm_array: standard deviations of atomic parameters.
00156         @type sigatm_array: Numeric array (length 5)
00157         """
00158         self.sigatm_array=sigatm_array
00159 
00160     def set_siguij(self, siguij_array):
00161         """
00162         Set standard deviations of anisotropic temperature factors.
00163 
00164         @param siguij_array: standard deviations of anisotropic temperature factors.
00165         @type siguij_array: Numeric array (length 6)
00166         """
00167         self.siguij_array=siguij_array
00168 
00169     def set_anisou(self, anisou_array):
00170         """
00171         Set anisotropic B factor.
00172 
00173         @param anisou_array: anisotropic B factor.
00174         @type anisou_array: Numeric array (length 6)
00175         """
00176         self.anisou_array=anisou_array
00177 
00178 
00179     # Public methods    
00180 
00181     def flag_disorder(self):
00182         """Set the disordered flag to 1.
00183 
00184         The disordered flag indicates whether the atom is disordered or not.
00185         """
00186         self.disordered_flag=1
00187 
00188     def is_disordered(self):
00189         "Return the disordered flag (1 if disordered, 0 otherwise)."
00190         return self.disordered_flag 
00191 
00192     def set_parent(self, parent):
00193         """Set the parent residue.
00194 
00195         Arguments:
00196         o parent - Residue object
00197         """
00198         self.parent=parent
00199     
00200     def detach_parent(self):
00201         "Remove reference to parent."
00202         self.parent=None
00203 
00204     def get_sigatm(self):
00205         "Return standard deviation of atomic parameters."
00206         return self.sigatm_array
00207 
00208     def get_siguij(self):
00209         "Return standard deviations of anisotropic temperature factors."
00210         return self.siguij_array
00211 
00212     def get_anisou(self):
00213         "Return anisotropic B factor."
00214         return self.anisou_array
00215 
00216     def get_parent(self):
00217         "Return parent residue."
00218         return self.parent
00219 
00220     def get_serial_number(self):
00221         return self.serial_number
00222 
00223     def get_name(self):
00224         "Return atom name."
00225         return self.name
00226 
00227     def get_id(self):
00228         "Return the id of the atom (which is its atom name)."
00229         return self.id
00230 
00231     def get_full_id(self):
00232         """Return the full id of the atom.
00233 
00234         The full id of an atom is the tuple 
00235         (structure id, model id, chain id, residue id, atom name, altloc).
00236         """
00237         return self.parent.get_full_id()+((self.name, self.altloc),)
00238     
00239     def get_coord(self):
00240         "Return atomic coordinates."
00241         return self.coord
00242 
00243     def get_bfactor(self):
00244         "Return B factor."
00245         return self.bfactor
00246 
00247     def get_occupancy(self):
00248         "Return occupancy."
00249         return self.occupancy
00250 
00251     def get_fullname(self):
00252         "Return the atom name, including leading and trailing spaces."
00253         return self.fullname
00254 
00255     def get_altloc(self):
00256         "Return alternative location specifier."
00257         return self.altloc
00258 
00259     def get_level(self):
00260         return self.level
00261 
00262     def transform(self, rot, tran):
00263         """
00264         Apply rotation and translation to the atomic coordinates.
00265 
00266         Example:
00267                 >>> rotation=rotmat(pi, Vector(1,0,0))
00268                 >>> translation=array((0,0,1), 'f')
00269                 >>> atom.transform(rotation, translation)
00270 
00271         @param rot: A right multiplying rotation matrix
00272         @type rot: 3x3 Numeric array
00273 
00274         @param tran: the translation vector
00275         @type tran: size 3 Numeric array
00276         """
00277         self.coord=numpy.dot(self.coord, rot)+tran
00278         
00279     def get_vector(self):
00280         """
00281         Return coordinates as Vector.
00282 
00283         @return: coordinates as 3D vector
00284         @rtype: Vector
00285         """
00286         x,y,z=self.coord
00287         return Vector(x,y,z)
00288 
00289     def copy(self):
00290         """
00291         Create a copy of the Atom.
00292         Parent information is lost.
00293         """
00294         # Do a shallow copy then explicitly copy what needs to be deeper.
00295         shallow = copy.copy(self)
00296         shallow.detach_parent()
00297         shallow.set_coord(copy.copy(self.get_coord()))
00298         shallow.xtra = self.xtra.copy()
00299         return shallow
00300 
00301 
00302 class DisorderedAtom(DisorderedEntityWrapper):
00303     """
00304     This class contains all Atom objects that represent the same disordered
00305     atom. One of these atoms is "selected" and all method calls not caught
00306     by DisorderedAtom are forwarded to the selected Atom object. In that way, a
00307     DisorderedAtom behaves exactly like a normal Atom. By default, the selected 
00308     Atom object represents the Atom object with the highest occupancy, but a 
00309     different Atom object can be selected by using the disordered_select(altloc) 
00310     method. 
00311     """
00312     def __init__(self, id):
00313         """
00314         Arguments:
00315         o id - string, atom name
00316         """
00317         self.last_occupancy=-1
00318         DisorderedEntityWrapper.__init__(self, id)
00319 
00320     # Special methods
00321 
00322     def __repr__(self):
00323         return "<Disordered Atom %s>" % self.get_id() 
00324 
00325     def disordered_add(self, atom):
00326         "Add a disordered atom."
00327         # Add atom to dict, use altloc as key   
00328         atom.flag_disorder()
00329         # set the residue parent of the added atom
00330         residue=self.get_parent()
00331         atom.set_parent(residue)
00332         altloc=atom.get_altloc()
00333         occupancy=atom.get_occupancy()
00334         self[altloc]=atom
00335         if occupancy>self.last_occupancy:
00336             self.last_occupancy=occupancy
00337             self.disordered_select(altloc)