Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package/CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ Fixes
DSSP by porting upstream PyDSSP 0.9.1 fix (Issue #4913)

Enhancements
* Enables parallelization for analysis.atomicdistances.AtomicDistances
(Issue #4662, PR #4822)
* DSSP uses ``capped_distance`` and ``calc_bonds`` for faster distance
calculations and supports ``backend`` selection (e.g. ``"distopia"``)
(PR #5182)
Expand Down
31 changes: 25 additions & 6 deletions package/MDAnalysis/analysis/atomicdistances.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
from MDAnalysis.analysis.results import Results

import logging
from .base import AnalysisBase
from .base import AnalysisBase, ResultsGroup

logger = logging.getLogger("MDAnalysis.analysis.atomicdistances")

Expand Down Expand Up @@ -147,15 +147,27 @@ class AtomicDistances(AnalysisBase):

.. versionadded:: 2.5.0
.. versionchanged:: 2.11.0
Distance data are now made available in :attr:`results.distances` instead
of :attr:`results` and :attr:`results` is now a
:class:`~MDAnalysis.analysis.results.Results` instance; this fixes an API issue
(see `Issue #4819`_) in a *backwards-incompatible* manner.

* Distance data are now made available in :attr:`results.distances` instead
of :attr:`results` and :attr:`results` is now a
:class:`~MDAnalysis.analysis.results.Results` instance; this fixes an API issue
(see `Issue #4819`_) in a *backwards-incompatible* manner.
* Enabled **parallel execution** with the ``multiprocessing`` and ``dask``
backends; use the new method :meth:`get_supported_backends` to see all
supported backends.
.. _`Issue #4819`: https://github.com/MDAnalysis/mdanalysis/issues/4819

"""

_analysis_algorithm_is_parallelizable = True

@classmethod
def get_supported_backends(cls):
return (
"serial",
"multiprocessing",
"dask",
)

def __init__(self, ag1, ag2, pbc=True, **kwargs):
# check ag1 and ag2 have the same number of atoms
if ag1.atoms.n_atoms != ag2.atoms.n_atoms:
Expand Down Expand Up @@ -185,3 +197,10 @@ def _single_frame(self):
self.results.distances[self._frame_index] = calc_bonds(
self._ag1.positions, self._ag2.positions, box
)

def _get_aggregator(self):
return ResultsGroup(
lookup={
"distances": ResultsGroup.ndarray_vstack, # Get distances
}
)
9 changes: 9 additions & 0 deletions testsuite/MDAnalysisTests/analysis/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from MDAnalysis.analysis.lineardensity import LinearDensity
from MDAnalysis.analysis.polymer import PersistenceLength
from MDAnalysis.analysis.rdf import InterRDF, InterRDF_s
from MDAnalysis.analysis.atomicdistances import AtomicDistances
from MDAnalysis.lib.util import is_installed


Expand Down Expand Up @@ -217,3 +218,11 @@ def client_InterRDF_s(request):
@pytest.fixture(scope="module", params=params_for_cls(DistanceMatrix))
def client_DistanceMatrix(request):
return request.param


# MDAnalysis.analysis.atomicdistances


@pytest.fixture(scope="module", params=params_for_cls(AtomicDistances))
def client_AtomicDistances(request):
return request.param
16 changes: 12 additions & 4 deletions testsuite/MDAnalysisTests/analysis/test_atomicdistances.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,20 +117,28 @@ def test_ad_exceptions(self, ad_ag1, ad_ag3, ad_ag4):

# only need to test that this class correctly applies distance calcs
# calc_bonds() is tested elsewhere
def test_ad_pairwise_dist(self, ad_ag1, ad_ag2, expected_dist):
def test_ad_pairwise_dist(
self, ad_ag1, ad_ag2, expected_dist, client_AtomicDistances
):
"""Ensure that pairwise distances between atoms are
correctly calculated without PBCs."""
pairwise_no_pbc = ad.AtomicDistances(ad_ag1, ad_ag2, pbc=False).run()
pairwise_no_pbc = ad.AtomicDistances(ad_ag1, ad_ag2, pbc=False).run(
**client_AtomicDistances
)
actual = pairwise_no_pbc.results
assert isinstance(actual, Results)
distances = actual.distances
# compare with expected values from dist()
assert_allclose(distances, expected_dist)

def test_ad_pairwise_dist_pbc(self, ad_ag1, ad_ag2, expected_pbc_dist):
def test_ad_pairwise_dist_pbc(
self, ad_ag1, ad_ag2, expected_pbc_dist, client_AtomicDistances
):
"""Ensure that pairwise distances between atoms are
correctly calculated with PBCs."""
pairwise_pbc = ad.AtomicDistances(ad_ag1, ad_ag2).run()
pairwise_pbc = ad.AtomicDistances(ad_ag1, ad_ag2).run(
**client_AtomicDistances
)
actual = pairwise_pbc.results
assert isinstance(actual, Results)
distances = actual.distances
Expand Down
Loading