File: test_distances.py

package info (click to toggle)
mdanalysis 2.10.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 116,696 kB
  • sloc: python: 92,135; ansic: 8,156; makefile: 215; sh: 138
file content (252 lines) | stat: -rw-r--r-- 7,994 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding:utf-8 -*-
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 fileencoding=utf-8
#
# MDAnalysis --- https://www.mdanalysis.org
# Copyright (c) 2006-2017 The MDAnalysis Development Team and contributors
# (see the file AUTHORS for the full list of names)
#
# Released under the Lesser GNU Public Licence, v2.1 or any higher version
#
# Please cite your use of MDAnalysis in published work:
#
# R. J. Gowers, M. Linke, J. Barnoud, T. J. E. Reddy, M. N. Melo, S. L. Seyler,
# D. L. Dotson, J. Domanski, S. Buchoux, I. M. Kenney, and O. Beckstein.
# MDAnalysis: A Python package for the rapid analysis of molecular dynamics
# simulations. In S. Benthall and S. Rostrup editors, Proceedings of the 15th
# Python in Science Conference, pages 102-109, Austin, TX, 2016. SciPy.
# doi: 10.25080/majora-629e541a-00e
#
# N. Michaud-Agrawal, E. J. Denning, T. B. Woolf, and O. Beckstein.
# MDAnalysis: A Toolkit for the Analysis of Molecular Dynamics Simulations.
# J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787
#
import pytest
import scipy
import scipy.spatial

import MDAnalysis
from MDAnalysisTests.datafiles import GRO

import MDAnalysis.analysis.distances

from numpy.testing import (
    assert_equal,
    assert_array_equal,
    assert_almost_equal,
    assert_array_almost_equal,
    assert_allclose,
)
import numpy as np


class TestContactMatrix(object):
    @staticmethod
    @pytest.fixture()
    def coord():
        return np.array(
            [
                [1, 1, 1],
                [5, 5, 5],
                [1.1, 1.1, 1.1],
                [11, 11, 11],  # neighboring image with pbc
                [21, 21, 21],
            ],  # non neighboring image with pbc
            dtype=np.float32,
        )

    @staticmethod
    @pytest.fixture()
    def box():
        return np.array([10, 10, 10, 90, 90, 90], dtype=np.float32)

    @staticmethod
    @pytest.fixture()
    def shape():
        return 5, 5

    @staticmethod
    @pytest.fixture()
    def res_no_pbc():
        return np.array(
            [
                [1, 0, 1, 0, 0],
                [0, 1, 0, 0, 0],
                [1, 0, 1, 0, 0],
                [0, 0, 0, 1, 0],
                [0, 0, 0, 0, 1],
            ],
            dtype=bool,
        )

    @staticmethod
    @pytest.fixture()
    def res_pbc():
        return np.array(
            [
                [1, 0, 1, 1, 1],
                [0, 1, 0, 0, 0],
                [1, 0, 1, 1, 1],
                [1, 0, 1, 1, 1],
                [1, 0, 1, 1, 1],
            ],
            dtype=bool,
        )

    def test_np(self, coord, shape, res_no_pbc):
        contacts = MDAnalysis.analysis.distances.contact_matrix(
            coord, cutoff=1, returntype="numpy"
        )
        assert contacts.shape == shape, "wrong shape (should be {0})".format(
            shape
        )
        assert_equal(contacts, res_no_pbc)

    def test_sparse(self, coord, shape, res_no_pbc):
        contacts = MDAnalysis.analysis.distances.contact_matrix(
            coord, cutoff=1.5, returntype="sparse"
        )
        assert contacts.shape == shape, "wrong shape (should be {0})".format(
            shape
        )
        assert_equal(contacts.toarray(), res_no_pbc)

    def test_box_numpy(self, coord, box, shape, res_pbc):
        contacts = MDAnalysis.analysis.distances.contact_matrix(
            coord, box=box, cutoff=1
        )
        assert contacts.shape == shape, "wrong shape (should be {0})".format(
            shape
        )
        assert_equal(contacts, res_pbc)

    def test_box_sparse(self, coord, box, shape, res_pbc):
        contacts = MDAnalysis.analysis.distances.contact_matrix(
            coord, box=box, cutoff=1, returntype="sparse"
        )
        assert contacts.shape == shape, "wrong shape (should be {0})".format(
            shape
        )
        assert_equal(contacts.toarray(), res_pbc)


class TestDist(object):

    @staticmethod
    @pytest.fixture()
    def ag():
        u = MDAnalysis.Universe(GRO)
        return u.atoms[1:10]

    # TODO: How are ag and ag2 different?!
    @staticmethod
    @pytest.fixture()
    def ag2():
        u2 = MDAnalysis.Universe(GRO)
        return u2.atoms[11:20]

    @staticmethod
    @pytest.fixture()
    def box():
        return np.array([8, 8, 8, 90, 90, 90], dtype=np.float32)

    @staticmethod
    @pytest.fixture()
    def expected(ag, ag2):

        return np.diag(
            scipy.spatial.distance.cdist(ag.positions, ag2.positions)
        )

    @staticmethod
    @pytest.fixture()
    def expected_box(ag, ag2, box):

        rp = np.abs(ag.positions - ag2.positions)
        box_2d = box[np.newaxis, 0:3]
        rp = np.where(rp > box_2d / 2, box_2d - rp, rp)
        return np.sqrt(np.square(rp).sum(axis=1))

    def test_pairwise_dist(self, ag, ag2, expected):
        """Ensure that pairwise distances between atoms are
        correctly calculated."""
        actual = MDAnalysis.analysis.distances.dist(ag, ag2)[2]
        assert_allclose(actual, expected)

    def test_pairwise_dist_box(self, ag, ag2, expected_box, box):
        """Ensure that pairwise distances between atoms are
        correctly calculated."""
        actual = MDAnalysis.analysis.distances.dist(ag, ag2, 0, box)[2]
        assert_allclose(actual, expected_box, rtol=1e-05, atol=10)

    def test_pairwise_dist_offset_effect(self, ag, ag2, expected):
        """Test that feeding in offsets to dist() doesn't alter
        pairwise distance matrix."""
        actual = MDAnalysis.analysis.distances.dist(ag, ag2, offset=229)[2]
        assert_allclose(actual, expected)

    def test_offset_calculation(self, ag, ag2):
        """Test that offsets fed to dist() are correctly calculated."""
        actual = MDAnalysis.analysis.distances.dist(ag, ag2, offset=33)[:2]
        assert_equal(
            actual, np.array([ag.atoms.resids + 33, ag2.atoms.resids + 33])
        )

    def test_mismatch_exception(self, ag, ag2, expected):
        """A ValueError should be raised if the two atomgroups
        don't have the same number of atoms."""
        with pytest.raises(ValueError):
            MDAnalysis.analysis.distances.dist(ag[:8], ag2)


class TestBetween(object):
    @staticmethod
    @pytest.fixture()
    def u():
        return MDAnalysis.Universe(GRO)

    @staticmethod
    @pytest.fixture()
    def ag(u):
        return u.atoms[:10]

    @staticmethod
    @pytest.fixture()
    def ag2(u):
        return u.atoms[12:33]

    @staticmethod
    @pytest.fixture()
    def group(u):
        return u.atoms[40:]

    distance = 5.9

    @pytest.fixture()
    def expected(self, group, ag, ag2):
        distance_matrix_1 = scipy.spatial.distance.cdist(
            group.positions, ag.positions
        )
        mask_1 = np.unique(np.where(distance_matrix_1 <= self.distance)[0])
        group_filtered = group[mask_1]
        distance_matrix_2 = scipy.spatial.distance.cdist(
            group_filtered.positions, ag2.positions
        )
        mask_2 = np.unique(np.where(distance_matrix_2 <= self.distance)[0])
        return group_filtered[mask_2].indices

    def test_between_simple_case_indices_only(self, group, ag, ag2, expected):
        """Test MDAnalysis.analysis.distances.between() for
        a simple input case. Checks atom indices
        of returned AtomGroup against sorted expected index
        values."""
        actual = MDAnalysis.analysis.distances.between(
            group, ag, ag2, self.distance
        ).indices
        assert_equal(actual, expected)

    @pytest.mark.parametrize("dists", [5.9, 0.0])
    def test_between_return_type(self, dists, group, ag, ag2):
        """Test that MDAnalysis.analysis.distances.between()
        returns an AtomGroup even when the returned group is empty."""
        actual = MDAnalysis.analysis.distances.between(group, ag, ag2, dists)
        assert isinstance(actual, MDAnalysis.core.groups.AtomGroup)