# -*- coding: utf-8 -*-
"""This module defines classes to handle individual atoms."""
import numpy as np
from . import flags
from .fields import ATOMIC_FIELDS, READONLY
from .fields import wrapGetMethod, wrapSetMethod
from .pointer import AtomPointer
from .bond import Bond
__all__ = ['Atom']
[docs]class Atom(AtomPointer):
"""A class for handling individual atoms in an :class:`.AtomGroup`."""
__slots__ = ['_ag', '_acsi', '_index']
def __init__(self, ag, index, acsi):
AtomPointer.__init__(self, ag, acsi)
self._index = int(index)
def __repr__(self):
n_csets = self._ag.numCoordsets()
if n_csets == 1:
return '<Atom: {0} from {1} (index {2})>'.format(
self.getName(), self._ag.getTitle(), self._index)
elif n_csets > 1:
return ('<Atom: {0} from {1} (index {2}; active #{3} of '
'{4} coordsets)>').format(self.getName(),
self._ag.getTitle(), self._index, self.getACSIndex(),
n_csets)
else:
return ('<Atom: {0} from {1} (index {2}; no coordinates)>'
).format(self.getName(), self._ag.getTitle(), self._index)
def __str__(self):
return 'Atom {0} (index {1})'.format(self.getName(), self._index)
def __len__(self):
return 1
def __int__(self):
return self._index
[docs] def numAtoms(self, flag=None):
"""Returns number of atoms, or number of atoms with given *flag*."""
return len(self._getSubset(flag)) if flag else 1
[docs] def getIndex(self):
"""Returns index of the atom."""
return self._index
[docs] def getIndices(self):
"""Returns index of the atom in an :class:`numpy.ndarray`."""
return np.array([self._index])
_getIndices = getIndices
[docs] def iterAtoms(self):
"""Yield atoms."""
yield Atom(ag=self._ag, index=self._index, acsi=self.getACSIndex())
__iter__ = iterAtoms
[docs] def getCoords(self):
"""Returns a copy of coordinates of the atom from the active coordinate
set."""
if self._ag._coords is not None:
return self._ag._coords[self.getACSIndex(), self._index].copy()
def _getCoords(self):
"""Returns a view of coordinates of the atom from the active coordinate
set."""
if self._ag._coords is not None:
return self._ag._coords[self.getACSIndex(), self._index]
[docs] def setCoords(self, coords):
"""Set coordinates of the atom in the active coordinate set."""
acsi = self.getACSIndex()
self._ag._coords[acsi, self._index] = coords
self._ag._setTimeStamp(acsi)
[docs] def getCoordsets(self, indices=None):
"""Returns a copy of coordinate set(s) at given *indices*."""
if self._ag._coords is None:
return None
if indices is None:
return self._ag._coords[:, self._index].copy()
if isinstance(indices, (int, slice)):
return self._ag._coords[indices, self._index].copy()
if isinstance(indices, (list, np.ndarray)):
return self._ag._coords[indices, self._index]
raise IndexError('indices must be an integer, a list/array of '
'integers, a slice, or None')
def _getCoordsets(self, indices=None):
"""Returns a view of coordinate set(s) at given *indices*."""
if self._ag._coords is None:
return None
if indices is None:
indices = slice(None)
return self._ag._coords[indices, self._index]
[docs] def iterCoordsets(self):
"""Yield copies of coordinate sets."""
for i in range(self.numCoordsets()):
yield self._ag._coords[i, self._index].copy()
def _iterCoordsets(self):
"""Yield views of coordinate sets."""
for i in range(self.numCoordsets()):
yield self._ag._coords[i, self._index]
[docs] def getMasses(self):
"""get the mass atom. """
from prody.utilities.misctools import getMasses
return getMasses(self.getElement())
[docs] def getData(self, label):
"""Returns a copy of data associated with *label*, if it is present."""
try:
data = self._ag._getData(label)
except KeyError:
pass
else:
if data.ndim > 1:
return data[self._index]
else:
return data[self._index].copy()
_getData = getData
[docs] def setData(self, label, data):
"""Update *data* associated with *label*.
:raise AttributeError: when *label* is not in use or read-only"""
if label in READONLY:
raise AttributeError('{0} is read-only'.format(repr(label)))
if label in ATOMIC_FIELDS:
getattr(self, 'set' + ATOMIC_FIELDS[label].meth)(data)
else:
try:
self._ag._data[label][self._index] = data
except KeyError:
raise AttributeError('data with label {0} must be set for'
' AtomGroup first'.format(repr(label)))
[docs] def getFlag(self, label):
"""Returns atom flag."""
return self._ag._getFlags(label)[self._index]
[docs] def setFlag(self, label, value):
"""Update flag associated with *label*.
:raise AttributeError: when *label* is not in use or read-only"""
if label in flags.PLANTERS:
raise AttributeError('flag {0} cannot be changed by user'
.format(repr(label)))
flags = self._ag._getFlags(label)
if flags is None:
raise AttributeError('flags with label {0} must be set for '
'AtomGroup first'.format(repr(label)))
flags[self._index] = value
[docs] def getSelstr(self):
"""Returns selection string that will select this atom."""
return 'index {0}'.format(self._index)
[docs] def numBonds(self):
"""Returns number of bonds formed by this atom. Bonds must be set first
using :meth:`.AtomGroup.setBonds`."""
numbonds = self._ag._data.get('numbonds')
if numbonds is not None:
return numbonds[self._index]
[docs] def iterBonds(self):
"""Yield bonds formed by the atom. Use :meth:`setBonds` for setting
bonds."""
ag = self._ag
acsi = self.getACSIndex()
for bond in self._iterBonds():
yield Bond(ag, bond, acsi)
def _iterBonds(self):
"""Yield pairs of bonded atom indices."""
ag = self._ag
if ag._bmap is None:
raise ValueError('bonds are not set, use `AtomGroup.setBonds`')
this = self._index
for other in ag._bmap[this]:
if other == -1:
break
yield this, other
[docs] def iterBonded(self):
"""Yield bonded atoms. Use :meth:`setBonds` for setting bonds."""
ag = self._ag
if ag._bmap is None:
raise ValueError('bonds are not set, use `AtomGroup.setBonds`')
acsi = self.getACSIndex()
this = self._index
for other in self._ag._bmap[this]:
if other == -1:
break
yield Atom(ag, other, acsi)
[docs] def toTEMPyAtom(self):
"""Returns a TEMPy BioPyAtom or Structure object as appropriate"""
try:
from TEMPy.protein.prot_rep_biopy import Atom as TEMPyAtom
except ImportError:
raise ImportError('TEMPy is needed for this functionality')
return TEMPyAtom(
self.getName(), self.getCoords(),
'HETATM' if self.getFlag('hetatm') else 'ATOM',
self.getSerial(), self.getBeta(),
self.getAltloc(), self.getIcode(),
self.getCharge(), self.getElement(),
self.getOccupancy(), self.getResname(),
None, self.getACSIndex(), self.getChid(),
self.getResnum())
for fname, field in ATOMIC_FIELDS.items():
if field.private:
continue
meth = field.meth
getMeth = 'get' + meth
setMeth = 'set' + meth
# Define public method for retrieving a copy of data array
def getData(self, meth=field.meth_pl, call=field.call):
data = getattr(self._ag, '_get' + meth)()
if data is not None:
return data[self._index]
getData = wrapGetMethod(getData)
getData.__name__ = getMeth
getData.__doc__ = field.getDocstr('get', False)
setattr(Atom, getMeth, getData)
setattr(Atom, '_' + getMeth, getData)
if field.readonly:
continue
# Define public method for setting values in data array
def setData(self, value, var=fname, none=field.none):
array = self._ag._data[var]
if array is None:
raise AttributeError('attribute of the AtomGroup is '
'not set')
array[self._index] = value
if none: self._ag._none(none)
setData = wrapSetMethod(setData)
setData.__name__ = setMeth
setData.__doc__ = field.getDocstr('set', False)
setattr(Atom, setMeth, setData)
del getData
del setData