Skip to content

Commit

Permalink
Changed AtomGroup methods to properties
Browse files Browse the repository at this point in the history
Implementation of Issue #372
  • Loading branch information
richardjgowers committed Jul 29, 2015
1 parent bd5f775 commit ff821bd
Show file tree
Hide file tree
Showing 26 changed files with 426 additions and 275 deletions.
5 changes: 5 additions & 0 deletions package/CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,13 @@ The rules for this file:
over periodic boundaries.
* Added triclinic_dimensions to Timestep, returns representation of unit
cell as triclinic vectors (Issue #276)
* Added setter to bfactors property (Issue #372)
* Added AtomGroup altLocs and serials properties with setters. (Issue #372)

Changes
* Changed many AtomGroup methods to properties. These are: indices, masses,
charges, names, types, radii, resids, resnames, resnums, segids
(Issue #372)
* A ProtoReader class intermediate between IObase and Reader was added so
specific Readers can be subclassed without __del__ (the ChainReader and
SingleFrameReader), thus preventing memleaks (Issue #312).
Expand Down
22 changes: 11 additions & 11 deletions package/MDAnalysis/analysis/align.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ def alignto(mobile, reference, select="all", mass_weighted=False,
tol_mass=tol_mass, strict=strict)

if mass_weighted:
weights = ref_atoms.masses() / numpy.mean(ref_atoms.masses())
weights = ref_atoms.masses / numpy.mean(ref_atoms.masses)
ref_com = ref_atoms.centerOfMass()
mobile_com = mobile_atoms.centerOfMass()
else:
Expand Down Expand Up @@ -464,7 +464,7 @@ def rms_fit_trj(traj, reference, select='all', filename=None, rmsdfile=None, pre
logger.info("RMS-fitting on %d atoms." % len(ref_atoms))
if mass_weighted:
# if performing a mass-weighted alignment/rmsd calculation
weight = ref_atoms.masses() / ref_atoms.masses().mean()
weight = ref_atoms.masses / ref_atoms.masses.mean()
else:
weight = None

Expand Down Expand Up @@ -830,7 +830,7 @@ def get_matching_atoms(ag1, ag2, tol_mass=0.1, strict=False):
ag1.numberOfResidues(), ag2.numberOfResidues())
dbgmsg = "mismatched residue numbers\n" + \
"\n".join(["{0} | {1}" for r1, r2 in
itertools.izip_longest(ag1.resids(), ag2.resids())])
itertools.izip_longest(ag1.resids, ag2.resids)])
logger.error(errmsg)
logger.debug(dbgmsg)
raise SelectionError(errmsg)
Expand All @@ -857,8 +857,8 @@ def get_matching_atoms(ag1, ag2, tol_mass=0.1, strict=False):
# pairwise2 consumes too much memory for thousands of characters in
# each sequence. Perhaps a solution would be pairwise alignment per residue.
#
# aln_elem = Bio.pairwise2.align.globalms("".join([MDAnalysis.topology.core.guess_atom_element(n) for n in gref.atoms.names()]),
# "".join([MDAnalysis.topology.core.guess_atom_element(n) for n in models[0].atoms.names()]),
# aln_elem = Bio.pairwise2.align.globalms("".join([MDAnalysis.topology.core.guess_atom_element(n) for n in gref.atoms.names]),
# "".join([MDAnalysis.topology.core.guess_atom_element(n) for n in models[0].atoms.names]),
# 2, -1, -1, -0.1,
# one_alignment_only=True)

Expand All @@ -875,8 +875,8 @@ def log_mismatch(number, ag, rsize, mismatch_resindex=mismatch_resindex):
logger.error("Offending residues: group {0}: {1}".format(
number,
", ".join(["{0[0]}{0[1]} ({0[2]})".format(r) for r in
itertools.izip(ag.resnames()[mismatch_resindex],
ag.resids()[mismatch_resindex],
itertools.izip(ag.resnames[mismatch_resindex],
ag.resids[mismatch_resindex],
rsize[mismatch_resindex]
)])))
logger.error("Found {0} residues with non-matching numbers of atoms (#)".format(
Expand All @@ -891,7 +891,7 @@ def get_atoms_byres(g, match_mask=numpy.logical_not(mismatch_mask)):
# not pretty... but need to do things on a per-atom basis in order
# to preserve original selection
ag = g.atoms
good = ag.resids()[match_mask]
good = ag.resids[match_mask]
resids = numpy.array([a.resid for a in ag]) # resid for each atom
ix_good = numpy.in1d(resids, good) # boolean array for all matching atoms
return ag[numpy.arange(len(ag))[ix_good]] # workaround for missing boolean indexing
Expand All @@ -904,14 +904,14 @@ def get_atoms_byres(g, match_mask=numpy.logical_not(mismatch_mask)):
mismatch_resindex = numpy.arange(ag1.numberOfResidues())[mismatch_mask]
logger.warn("Removed {0} residues with non-matching numbers of atoms".format(
mismatch_mask.sum()))
logger.debug("Removed residue ids: group 1: {0}".format(ag1.resids()[mismatch_resindex]))
logger.debug("Removed residue ids: group 2: {0}".format(ag2.resids()[mismatch_resindex]))
logger.debug("Removed residue ids: group 1: {0}".format(ag1.resids[mismatch_resindex]))
logger.debug("Removed residue ids: group 2: {0}".format(ag2.resids[mismatch_resindex]))
# replace after logging (still need old ag1 and ag2 for diagnostics)
ag1 = _ag1
ag2 = _ag2
del _ag1, _ag2

mass_mismatches = (numpy.absolute(ag1.masses() - ag2.masses()) > tol_mass)
mass_mismatches = (numpy.absolute(ag1.masses - ag2.masses) > tol_mass)
if numpy.any(mass_mismatches):
# Test 2 failed.
# diagnostic output:
Expand Down
4 changes: 2 additions & 2 deletions package/MDAnalysis/analysis/contacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -714,7 +714,7 @@ def _plot_qavg_pcolor(self, filename=None, **kwargs):
"""Plot :attr:`ContactAnalysis1.qavg`, the matrix of average native contacts."""
from pylab import pcolor, gca, meshgrid, xlabel, ylabel, xlim, ylim, colorbar, savefig

x, y = self.selections[0].resids(), self.selections[1].resids()
x, y = self.selections[0].resids, self.selections[1].resids
X, Y = meshgrid(x, y)

pcolor(X, Y, self.qavg.T, **kwargs)
Expand Down Expand Up @@ -742,7 +742,7 @@ def plot_qavg(self, filename=None, **kwargs):
"""
from pylab import imshow, xlabel, ylabel, xlim, ylim, colorbar, cm, clf, savefig

x, y = self.selections[0].resids(), self.selections[1].resids()
x, y = self.selections[0].resids, self.selections[1].resids

kwargs['origin'] = 'lower'
kwargs.setdefault('aspect', 'equal')
Expand Down
4 changes: 2 additions & 2 deletions package/MDAnalysis/analysis/distances.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,8 @@ def dist(A, B, offset=0):
off_A, off_B = offset
except (TypeError, ValueError):
off_A = off_B = int(offset)
residues_A = numpy.array(A.resids()) + off_A
residues_B = numpy.array(B.resids()) + off_B
residues_A = numpy.array(A.resids) + off_A
residues_B = numpy.array(B.resids) + off_B
r = A.coordinates() - B.coordinates()
d = numpy.sqrt(numpy.sum(r * r, axis=1))
return numpy.array([residues_A, residues_B, d])
Expand Down
16 changes: 8 additions & 8 deletions package/MDAnalysis/analysis/nuclinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ def wc_pair(universe, i, bp, seg1="SYSTEM", seg2="SYSTEM"):
.. versionadded:: 0.7.6
"""
if universe.selectAtoms(" resid %s " % (i,)).resnames()[0] in ["DC", "DT", "U", "C", "T", "CYT", "THY", "URA"]:
if universe.selectAtoms(" resid %s " % (i,)).resnames[0] in ["DC", "DT", "U", "C", "T", "CYT", "THY", "URA"]:
a1, a2 = "N3", "N1"
if universe.selectAtoms(" resid %s " % (i,)).resnames()[0] in ["DG", "DA", "A", "G", "ADE", "GUA"]:
if universe.selectAtoms(" resid %s " % (i,)).resnames[0] in ["DG", "DA", "A", "G", "ADE", "GUA"]:
a1, a2 = "N1", "N3"
wc_dist = universe.selectAtoms(
" (segid %s and resid %s and name %s) or (segid %s and resid %s and name %s) " % (seg1, i, a1, seg2, bp, a2))
Expand Down Expand Up @@ -159,9 +159,9 @@ def minor_pair(universe, i, bp, seg1="SYSTEM", seg2="SYSTEM"):
.. versionadded:: 0.7.6
"""
if universe.selectAtoms(" resid %s " % (i,)).resnames()[0] in ["DC", "DT", "U", "C", "T", "CYT", "THY", "URA"]:
if universe.selectAtoms(" resid %s " % (i,)).resnames[0] in ["DC", "DT", "U", "C", "T", "CYT", "THY", "URA"]:
a1, a2 = "O2", "C2"
if universe.selectAtoms(" resid %s " % (i,)).resnames()[0] in ["DG", "DA", "A", "G", "ADE", "GUA"]:
if universe.selectAtoms(" resid %s " % (i,)).resnames[0] in ["DG", "DA", "A", "G", "ADE", "GUA"]:
a1, a2 = "C2", "O2"
c2o2_dist = universe.selectAtoms(
" (segid %s and resid %s and name %s) or (segid %s and resid %s and name %s) " % (seg1, i, a1, seg2, bp, a2))
Expand Down Expand Up @@ -191,13 +191,13 @@ def major_pair(universe, i, bp, seg1="SYSTEM", seg2="SYSTEM"):
.. versionadded:: 0.7.6
"""
if universe.selectAtoms(" resid %s " % (i,)).resnames()[0] in ["DC", "DG", "C", "G", "CYT", "GUA"]:
if universe.selectAtoms(" resid %s " % (i,)).resnames()[0] in ["DC", "C", "CYT"]:
if universe.selectAtoms(" resid %s " % (i,)).resnames[0] in ["DC", "DG", "C", "G", "CYT", "GUA"]:
if universe.selectAtoms(" resid %s " % (i,)).resnames[0] in ["DC", "C", "CYT"]:
a1, a2 = "N4", "O6"
else:
a1, a2 = "O6", "N4"
if universe.selectAtoms(" resid %s " % (i,)).resnames()[0] in ["DT", "DA", "A", "T", "U", "ADE", "THY", "URA"]:
if universe.selectAtoms(" resid %s " % (i,)).resnames()[0] in ["DT", "T", "THY", "U", "URA"]:
if universe.selectAtoms(" resid %s " % (i,)).resnames[0] in ["DT", "DA", "A", "T", "U", "ADE", "THY", "URA"]:
if universe.selectAtoms(" resid %s " % (i,)).resnames[0] in ["DT", "T", "THY", "U", "URA"]:
a1, a2 = "O4", "N6"
else:
a1, a2 = "N6", "O4"
Expand Down
4 changes: 2 additions & 2 deletions package/MDAnalysis/analysis/rms.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ def __init__(self, traj, reference=None, select='all', groupselections=None, fil
"the same number of atoms: N_ref=%d, N_traj=%d" %
(len(self.ref_atoms), len(self.traj_atoms)))
logger.info("RMS calculation for %d atoms." % len(self.ref_atoms))
mass_mismatches = (numpy.absolute(self.ref_atoms.masses() - self.traj_atoms.masses()) > self.tol_mass)
mass_mismatches = (numpy.absolute(self.ref_atoms.masses - self.traj_atoms.masses) > self.tol_mass)
if numpy.any(mass_mismatches):
# diagnostic output:
logger.error("Atoms: reference | trajectory")
Expand Down Expand Up @@ -392,7 +392,7 @@ def run(self, **kwargs):

if mass_weighted:
# if performing a mass-weighted alignment/rmsd calculation
weight = self.ref_atoms.masses() / self.ref_atoms.masses().mean()
weight = self.ref_atoms.masses / self.ref_atoms.masses.mean()
else:
weight = None

Expand Down
2 changes: 1 addition & 1 deletion package/MDAnalysis/coordinates/DCD.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ def timeseries(self, asel, start=0, stop=-1, skip=1, format='afc'):
raise NoDataError("Timeseries requires at least one atom to analyze")
if len(format) != 3 and format not in ['afc', 'acf', 'caf', 'cfa', 'fac', 'fca']:
raise ValueError("Invalid timeseries format")
atom_numbers = list(asel.indices())
atom_numbers = list(asel.indices)
# Check if the atom numbers can be grouped for efficiency, then we can read partial buffers
# from trajectory file instead of an entire timestep
# XXX needs to be implemented
Expand Down
2 changes: 1 addition & 1 deletion package/MDAnalysis/coordinates/PDB.py
Original file line number Diff line number Diff line change
Expand Up @@ -1078,7 +1078,7 @@ def _write_timestep(self, ts, multiframe=False):
coor = ts._pos

if hasattr(self.obj, "indices"):
coor = coor[self.obj.indices()]
coor = coor[self.obj.indices]

if len(atoms) != len(coor):
raise ValueError(
Expand Down
8 changes: 4 additions & 4 deletions package/MDAnalysis/coordinates/XYZ.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,12 @@ def _get_atomnames(self, atoms):
"""Return a list of atom names"""
# AtomGroup
try:
return atoms.names()
return atoms.names
except AttributeError:
pass
# universe?
try:
return atoms.atoms.names()
return atoms.atoms.names
except AttributeError:
pass
# list or string (can be a single atom name... deal with this in write_next_timestep() once we know numatoms)
Expand Down Expand Up @@ -185,12 +185,12 @@ def write(self, obj):
ts = ts_full
else:
# Only populate a time step with the selected atoms.
ts = ts_full.copy_slice(atoms.indices())
ts = ts_full.copy_slice(atoms.indices)
elif hasattr(obj, 'trajectory'):
# For Universe only --- get everything
ts = obj.trajectory.ts
# update atom names
self.atomnames = atoms.names()
self.atomnames = atoms.names
else:
ts = obj

Expand Down
Loading

1 comment on commit ff821bd

@orbeckst
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using properties makes the use of these attributes much cleaner and more readable – no more ag.residues()[:20] ... good riddance ;-).

Please sign in to comment.