Skip to content
8 changes: 7 additions & 1 deletion package/CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The rules for this file:
calcraven,xiki-tempula, mieczyslaw, manuel.nuno.melo, PicoCentauri,
hanatok, rmeli, aditya-kamath, tirkarthi, LeonardoBarneschi, hejamu,
biogen98, orioncohen, z3y50n, hp115, ojeda-e, thadanipaarth, HenryKobin,
1ut, sulays, PicoCentauri, ahy3nz
1ut, sulays, ahy3nz

* 2.0.0

Expand Down Expand Up @@ -174,6 +174,9 @@ Enhancements
checking if it can be used in parallel analysis. (Issue #2996, PR #2950)

Changes
* `DensityAnalysis` now uses the `results.density` attribute for storing
data. The `DensityAnalysis.density` attribute is now deprecated
(Issue #3261)
* Deprecated analysis.hbonds.hbond_analysis has been removed in favour of
analysis.hydrogenbonds.hbond_analysis (Issues #2739, #2746)
* `GNMAnalysis`, `LinearDensity`, `PersistenceLength` and
Expand Down Expand Up @@ -222,6 +225,9 @@ Changes
* Added OpenMM coordinate and topology converters (Issue #2863, PR #2917)

Deprecations
* The `density` attribute of `analysis.density.DensityAnalysis` is now
deprecated in favour of `results.density`. It will be removed in 3.0.0
(Issue #3261)
* The analysis.hbonds.hbond_autocorrel code has been moved to
analysis.hydrogenbonds.hbond_autocorrel and will be removed
from analysis.hbonds in 3.0.0 (PR #3258)
Expand Down
61 changes: 40 additions & 21 deletions package/MDAnalysis/analysis/density.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@
ow = u.select_atoms("name OW")
D = DensityAnalysis(ow, delta=1.0)
D.run()
D.density.convert_density('TIP4P')
D.density.export("water.dx", type="double")
D.results.density.convert_density('TIP4P')
D.results.density.export("water.dx", type="double")

The positions of all water oxygens (the :class:`AtomGroup` `ow`) are
histogrammed on a grid with spacing *delta* = 1 Å. Initially the density is
Expand All @@ -87,9 +87,9 @@
can be read in VMD_, Chimera_, or PyMOL_.

The :class:`Density` object is accessible as the
:attr:`DensityAnalysis.density` attribute. In particular, the data for the
density is stored as a NumPy array in :attr:`Density.grid`, which can be
processed in any manner.
:attr:`DensityAnalysis.results.density` attribute. In particular, the data
for the density is stored as a NumPy array in :attr:`Density.grid`, which can
be processed in any manner.


Creating densities
Expand All @@ -102,10 +102,12 @@
:members:
:inherited-members: run

.. attribute:: density
.. attribute:: results.density

After the analysis (see the :meth:`~DensityAnalysis.run` method), the resulting density is
stored in the :attr:`density` attribute as a :class:`Density` instance.
After the analysis (see the :meth:`~DensityAnalysis.run` method), the
resulting density is stored in the :attr:`results.density` attribute as
a :class:`Density` instance. Note: this replaces the now deprecated
:attr:`density` attribute.

.. automethod:: _set_user_grid

Expand Down Expand Up @@ -166,7 +168,8 @@

import MDAnalysis
from MDAnalysis.core import groups
from MDAnalysis.lib.util import fixedwidth_bins, iterable, asiterable
from MDAnalysis.lib.util import (fixedwidth_bins, iterable, asiterable,
deprecate,)
from MDAnalysis.lib import NeighborSearch as NS
from MDAnalysis import NoDataError, MissingDataWarning
from .. import units
Expand Down Expand Up @@ -208,12 +211,19 @@ class DensityAnalysis(AnalysisBase):
zdim : float (optional)
User defined z dimension box edge in ångström.

Returns
-------
:class:`Density`
Attributes
----------
results.density : :class:`Density`
A :class:`Density` instance containing a physical density of units
:math:`Angstrom^{-3}`.

density : :class:`Density`
Alias to the :attr:`results.density`.

.. deprecated:: 2.0.0
Will be removed in MDAnalysis 3.0.0. Please use
:attr:`results.density` instead.

Raises
------
ValueError
Expand Down Expand Up @@ -282,11 +292,11 @@ class DensityAnalysis(AnalysisBase):
ow = u.select_atoms("name OW")
D = density.DensityAnalysis(ow, delta=1.0)
D.run()
D.density.convert_density('TIP4P')
D.results.density.convert_density('TIP4P')

The positions of all water oxygens are histogrammed on a grid with spacing
*delta* = 1 Å and stored as a :class:`Density` object in the attribute
:attr:`DensityAnalysis.density`.
:attr:`DensityAnalysis.results.density`.

.. rubric:: Working with a density

Expand All @@ -304,7 +314,7 @@ class DensityAnalysis(AnalysisBase):
density in units of Å\ :sup:`-3`. If you are interested in recovering the
underlying **probability density**, simply divide by the sum::

probability_density = D.density.grid / D.density.grid.sum()
probability_density = D.results.density.grid / D.results.density.grid.sum()

Similarly, if you would like to recover a grid containing a **histogram of
atom counts**, simply multiply by the volume `dV` of each bin (or voxel);
Expand All @@ -314,10 +324,10 @@ class DensityAnalysis(AnalysisBase):
import numpy as np

# ensure that the density is A^{-3}
D.density.convert_density("A^{-3}")
D.results.density.convert_density("A^{-3}")

dV = np.prod(D.density.delta)
atom_count_histogram = D.density.grid * dV
dV = np.prod(D.results.density.delta)
atom_count_histogram = D.results.density.grid * dV


.. rubric:: Writing the density to a file
Expand All @@ -330,7 +340,7 @@ class DensityAnalysis(AnalysisBase):
<https://www.mdanalysis.org/GridDataFormats/gridData/basic.html#writing-out-data>`_
``water.dx`` that can be read with VMD, PyMOL, or Chimera::

D.density.export("water.dx", type="double")
D.results.density.export("water.dx", type="double")


.. rubric:: Example: Water density in the whole simulation
Expand Down Expand Up @@ -384,6 +394,8 @@ class DensityAnalysis(AnalysisBase):
.. versionadded:: 1.0.0
.. versionchanged:: 2.0.0
:func:`_set_user_grid` is now a method of :class:`DensityAnalysis`.
:class:`Density` results are now stored in a
:class:`MDAnalysis.analysis.base.Results` instance.
"""

def __init__(self, atomgroup, delta=1.0,
Expand Down Expand Up @@ -457,7 +469,6 @@ def _prepare(self):
self._edges = edges
self._arange = arange
self._bins = bins
self.density = None

def _single_frame(self):
h, _ = np.histogramdd(self._atomgroup.positions,
Expand All @@ -476,7 +487,15 @@ def _conclude(self):
units={'length': "Angstrom"},
parameters={'isDensity': False})
density.make_density()
self.density = density
self.results.density = density

@property
def density(self):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sad the the docs become messy when we use mda's @deprecate decorator...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah it enforces docstring which is just not very human friendly :(
For the sake of a couple of extra lines I'm happy just adding the messages by hand.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yes, but this still looks good and as a good template for the other classes.

wmsg = ("The `density` attribute was deprecated in MDAnalysis 2.0.0 "
"and will be removed in MDAnalysis 3.0.0. Please use "
"`results.density` instead")
warnings.warn(wmsg, DeprecationWarning)
return self.results.density

@staticmethod
def _set_user_grid(gridcenter, xdim, ydim, zdim, smin, smax):
Expand Down
21 changes: 16 additions & 5 deletions testsuite/MDAnalysisTests/analysis/test_density.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,15 @@ def check_DensityAnalysis(self, ag, ref_meandensity,
runargs = runargs if runargs else {}
with tmpdir.as_cwd():
D = density.DensityAnalysis(ag, delta=self.delta, **kwargs).run(**runargs)
assert_almost_equal(D.density.grid.mean(), ref_meandensity,
assert_almost_equal(D.results.density.grid.mean(), ref_meandensity,
err_msg="mean density does not match")
D.density.export(self.outfile)
D.results.density.export(self.outfile)

D2 = density.Density(self.outfile)
assert_almost_equal(D.density.grid, D2.grid, decimal=self.precision,
err_msg="DX export failed: different grid sizes")
assert_almost_equal(
D.results.density.grid, D2.grid, decimal=self.precision,
err_msg="DX export failed: different grid sizes"
)

@pytest.mark.parametrize("mode", ("static", "dynamic"))
def test_run(self, mode, universe, tmpdir):
Expand Down Expand Up @@ -211,7 +213,7 @@ def test_userdefn_boxshape(self, universe):
universe.select_atoms(self.selections['static']),
delta=1.0, xdim=8.0, ydim=12.0, zdim=17.0,
gridcenter=self.gridcenters['static_defined']).run()
assert D.density.grid.shape == (8, 12, 17)
assert D.results.density.grid.shape == (8, 12, 17)

def test_warn_userdefn_padding(self, universe):
regex = (r"Box padding \(currently set at 1\.0\) is not used "
Expand Down Expand Up @@ -294,6 +296,15 @@ def test_ValueError_noatomgroup(self, universe):
D = density.DensityAnalysis(
universe.select_atoms(self.selections['none'])).run(step=5)

def test_warn_results_deprecated(self, universe):
D = density.DensityAnalysis(
universe.select_atoms(self.selections['static']))
D.run(stop=1)
wmsg = "The `density` attribute was deprecated in MDAnalysis 2.0.0"
with pytest.warns(DeprecationWarning, match=wmsg):
assert_equal(D.density.grid, D.results.density.grid)


class TestGridImport(object):

@block_import('gridData')
Expand Down