polyphys 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- polyphys/__init__.py +7 -0
- polyphys/__version__.py +22 -0
- polyphys/analyze/__init__.py +51 -0
- polyphys/analyze/clusters.py +1738 -0
- polyphys/analyze/correlations.py +995 -0
- polyphys/analyze/density.py +1138 -0
- polyphys/analyze/measurer.py +1338 -0
- polyphys/analyze/pipeline.py +1868 -0
- polyphys/analyze/stats.py +810 -0
- polyphys/analyze/sumrule.py +1159 -0
- polyphys/manage/__init__.py +11 -0
- polyphys/manage/organizer.py +2188 -0
- polyphys/manage/parser.py +1264 -0
- polyphys/manage/types.py +171 -0
- polyphys/manage/utils.py +777 -0
- polyphys/probe/__init__.py +25 -0
- polyphys/probe/binders.py +711 -0
- polyphys/probe/chain_size.py +583 -0
- polyphys/probe/core.py +437 -0
- polyphys/probe/histograms.py +863 -0
- polyphys/probe/pairs.py +555 -0
- polyphys/simulate/__init__.py +48 -0
- polyphys/simulate/build.py +1231 -0
- polyphys/simulate/core.py +237 -0
- polyphys/simulate/data.py +977 -0
- polyphys/simulate/dump.py +752 -0
- polyphys/simulate/log.py +788 -0
- polyphys/spec.py +1359 -0
- polyphys/tests/analyze/test_clusters.py +1058 -0
- polyphys/tests/analyze/test_correlations.py +708 -0
- polyphys/tests/analyze/test_density.py +813 -0
- polyphys/tests/analyze/test_measurer.py +785 -0
- polyphys/tests/analyze/test_pipeline.py +1881 -0
- polyphys/tests/analyze/test_stats.py +732 -0
- polyphys/tests/analyze/test_sumrule.py +917 -0
- polyphys/tests/conftest.py +753 -0
- polyphys/tests/data/lammps_build/make_starts.py +70 -0
- polyphys/tests/manage/test_organizer.py +1495 -0
- polyphys/tests/manage/test_parser.py +1222 -0
- polyphys/tests/manage/test_utils.py +498 -0
- polyphys/tests/probe/conftest.py +407 -0
- polyphys/tests/probe/test_binders.py +686 -0
- polyphys/tests/probe/test_chain_size.py +557 -0
- polyphys/tests/probe/test_core.py +391 -0
- polyphys/tests/probe/test_histograms.py +779 -0
- polyphys/tests/probe/test_pairs.py +475 -0
- polyphys/tests/simulate/test_build.py +1042 -0
- polyphys/tests/simulate/test_core.py +106 -0
- polyphys/tests/simulate/test_data.py +858 -0
- polyphys/tests/simulate/test_dump.py +487 -0
- polyphys/tests/simulate/test_log.py +667 -0
- polyphys/tests/strategies.py +120 -0
- polyphys/tests/synthetic.py +410 -0
- polyphys/tests/test_conftest.py +414 -0
- polyphys/tests/test_docs.py +82 -0
- polyphys/tests/test_packaging.py +97 -0
- polyphys/tests/test_spec.py +772 -0
- polyphys/tests/viz/conftest.py +32 -0
- polyphys/tests/viz/test_colors.py +113 -0
- polyphys/tests/viz/test_helpers.py +292 -0
- polyphys/viz/__init__.py +15 -0
- polyphys/viz/colors.py +204 -0
- polyphys/viz/helpers.py +549 -0
- polyphys-1.0.0.dist-info/METADATA +339 -0
- polyphys-1.0.0.dist-info/RECORD +69 -0
- polyphys-1.0.0.dist-info/WHEEL +5 -0
- polyphys-1.0.0.dist-info/licenses/AUTHORS.rst +13 -0
- polyphys-1.0.0.dist-info/licenses/LICENSE +22 -0
- polyphys-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Fixtures shared by the probe tests.
|
|
3
|
+
|
|
4
|
+
Every test here needs MDAnalysis, which the ``mda`` extra installs; the test
|
|
5
|
+
modules call ``pytest.importorskip("MDAnalysis")``, and the fixtures import
|
|
6
|
+
the probe layer inside their factories so that collecting this folder
|
|
7
|
+
without MDAnalysis is harmless.
|
|
8
|
+
|
|
9
|
+
Fixtures
|
|
10
|
+
--------
|
|
11
|
+
make_chain_universe
|
|
12
|
+
The files of ``make_chain_files``, in ``polyphys/tests/conftest.py``,
|
|
13
|
+
opened with :func:`polyphys.probe.make_universe`.
|
|
14
|
+
study_spec
|
|
15
|
+
The spec module, ``projects/<study>/spec.py``, of a study.
|
|
16
|
+
real_polymer_whole, real_window_whole, real_crowded_whole, \
|
|
17
|
+
real_foci_whole, real_hns_whole, real_nh0_whole
|
|
18
|
+
The real wholes of the tier B and tier C tests, one test per whole (see
|
|
19
|
+
"Real frames" below).
|
|
20
|
+
real_files
|
|
21
|
+
The files of a real whole: its polymer dump and the topology it is
|
|
22
|
+
opened with, and its ``all`` window and data file when they are tier B.
|
|
23
|
+
private_window_files
|
|
24
|
+
The ``all`` window and data file of a crowded whole, tier C.
|
|
25
|
+
real_transfocicub_whole
|
|
26
|
+
The 4 TransFociCub wholes, whose research-code probe output is the
|
|
27
|
+
tier C reference of the pipeline tests.
|
|
28
|
+
research_probe_file
|
|
29
|
+
One property file of the research code's probe output of a whole,
|
|
30
|
+
tier C.
|
|
31
|
+
real_universe
|
|
32
|
+
A real dump opened with :func:`polyphys.probe.make_universe`.
|
|
33
|
+
trajectory_arrays
|
|
34
|
+
The positions, box lengths, and time steps of every frame of a
|
|
35
|
+
universe, in one pass.
|
|
36
|
+
reference_distances
|
|
37
|
+
Minimum-image distances between two sets of positions, frame by frame,
|
|
38
|
+
from :func:`MDAnalysis.lib.distances.distance_array`.
|
|
39
|
+
|
|
40
|
+
Real frames
|
|
41
|
+
-----------
|
|
42
|
+
The tier B files are in ``polyphys/tests/data/probe/<whole>/``: for each of
|
|
43
|
+
22 wholes of the four live studies, the last 50 frames of the polymer group
|
|
44
|
+
dump (``<whole>.ring.<group>.last50.lammpstrj.gz``), and for the wholes
|
|
45
|
+
without crowders and the HnsCyl wholes at ``ac2 nc386``, the ``all`` frames
|
|
46
|
+
of the same time window (``<whole>.j<NN>.ring.all.window.lammpstrj.gz``)
|
|
47
|
+
and the whole's data file (``<whole>.ring.all.data.gz``). The maintainer
|
|
48
|
+
trimmed them from the studies' runs. The HnsCyl runs name their wholes
|
|
49
|
+
without ``epshc``; their copies here insert ``epshc1``, the value of their
|
|
50
|
+
H-NS core-crowder pair coefficients, so that the parser reads them.
|
|
51
|
+
|
|
52
|
+
The tier C files, the ``all`` windows and data files of the other crowded
|
|
53
|
+
wholes, stay in the maintainer's folder
|
|
54
|
+
``<POLYPHYS_DATA_DIR>/<project>/trjs/`` under the runs' own names, with
|
|
55
|
+
``<project>`` the study's project name, such as ``HnsCub``.
|
|
56
|
+
|
|
57
|
+
A polymer dump holds only the polymer group, so it cannot be opened with
|
|
58
|
+
its whole's full data file. It is opened with the data file of the whole
|
|
59
|
+
without crowders of the same family and replicate (:attr:`RealWhole.topology`),
|
|
60
|
+
whose atoms are the polymer group: the ring for TransFoci, the ring and the
|
|
61
|
+
H-NS proteins for Hns. The research code did the same. The polymer atoms
|
|
62
|
+
carry the IDs 1 to n in every system, with the crowders after them.
|
|
63
|
+
"""
|
|
64
|
+
import importlib
|
|
65
|
+
import sys
|
|
66
|
+
import warnings
|
|
67
|
+
from dataclasses import dataclass
|
|
68
|
+
from pathlib import Path
|
|
69
|
+
from types import ModuleType
|
|
70
|
+
from typing import Any, Callable, Mapping, Optional
|
|
71
|
+
|
|
72
|
+
import numpy as np
|
|
73
|
+
import pytest
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@pytest.fixture
|
|
77
|
+
def make_chain_universe(
|
|
78
|
+
make_chain_files: Callable[..., tuple[Path, Path]]
|
|
79
|
+
) -> Callable[..., Any]:
|
|
80
|
+
"""Factory: the universe of the files ``make_chain_files`` writes, with
|
|
81
|
+
the same arguments."""
|
|
82
|
+
|
|
83
|
+
def _make(positions: Any, **kwargs: Any) -> Any:
|
|
84
|
+
from polyphys.probe import make_universe
|
|
85
|
+
|
|
86
|
+
data_path, dump_path = make_chain_files(positions, **kwargs)
|
|
87
|
+
return make_universe(data_path, dump_path)
|
|
88
|
+
|
|
89
|
+
return _make
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# Real frames
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@dataclass(frozen=True)
|
|
96
|
+
class RealWhole:
|
|
97
|
+
"""One real whole of the tier B and tier C tests.
|
|
98
|
+
|
|
99
|
+
Attributes
|
|
100
|
+
----------
|
|
101
|
+
id : str
|
|
102
|
+
The test id, such as ``'HnsCub-nh30-nc10743-ens1'``.
|
|
103
|
+
study : str
|
|
104
|
+
Its study's folder in ``projects/``, such as ``'hns_cub'``.
|
|
105
|
+
whole : str
|
|
106
|
+
The whole's name without ``.ring``, as its parser reads it.
|
|
107
|
+
topology : str
|
|
108
|
+
The whole whose data file opens the polymer dump: the whole itself
|
|
109
|
+
when it has no crowders, else the whole without crowders of its
|
|
110
|
+
family and replicate.
|
|
111
|
+
group : str
|
|
112
|
+
The polymer group: ``'nucleoid'`` or ``'bug'``.
|
|
113
|
+
n_mon, n_foci, n_hns, n_crowd : int
|
|
114
|
+
The monomers, the large monomers among them, the H-NS proteins, and
|
|
115
|
+
the crowders, from the name (keywords ``N`` or ``ns`` plus ``nl``,
|
|
116
|
+
``nl``, ``nh``, and ``nc``).
|
|
117
|
+
window_tier : str
|
|
118
|
+
``'B'`` when the whole's ``all`` window is in the repository,
|
|
119
|
+
``'C'`` when it is the maintainer's, ``''`` for none.
|
|
120
|
+
n_window_frames : int
|
|
121
|
+
The frames of the ``all`` window: 25 for Hns, 20 for TransFoci.
|
|
122
|
+
n_shared_frames : int
|
|
123
|
+
The window's frames that are also polymer frames, by time step:
|
|
124
|
+
all 25 for Hns, whose ``all`` dump period (10000) is twice the
|
|
125
|
+
``nucleoid`` one (5000); 10 for TransFoci, whose periods are 5000
|
|
126
|
+
and 2000.
|
|
127
|
+
source : str
|
|
128
|
+
The whole's folder in the maintainer's fixtures folder: `whole`,
|
|
129
|
+
but without ``epshc1`` for HnsCyl.
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
id: str
|
|
133
|
+
study: str
|
|
134
|
+
whole: str
|
|
135
|
+
topology: str
|
|
136
|
+
group: str
|
|
137
|
+
n_mon: int
|
|
138
|
+
n_foci: int
|
|
139
|
+
n_hns: int
|
|
140
|
+
n_crowd: int
|
|
141
|
+
window_tier: str
|
|
142
|
+
n_window_frames: int
|
|
143
|
+
n_shared_frames: int
|
|
144
|
+
source: str
|
|
145
|
+
|
|
146
|
+
def polymer_counts(self) -> dict[str, int]:
|
|
147
|
+
"""The atoms of each species of the study's spec in the polymer
|
|
148
|
+
dump: two patches per H-NS core (Jung, Sadeghi, and Ha, Sci. Rep.
|
|
149
|
+
14, 139 (2024)), the small monomers and the foci of a TransFoci
|
|
150
|
+
ring, and no crowders."""
|
|
151
|
+
if self.group == 'nucleoid':
|
|
152
|
+
return {'Mon': self.n_mon, 'HnsPatch': 2 * self.n_hns,
|
|
153
|
+
'Hns': self.n_hns, 'Crd': 0}
|
|
154
|
+
return {'Mon': self.n_mon, 'Foci': self.n_foci,
|
|
155
|
+
'Dna': self.n_mon - self.n_foci, 'Crd': 0}
|
|
156
|
+
|
|
157
|
+
def window_counts(self) -> dict[str, int]:
|
|
158
|
+
"""The atoms of each species of the study's spec in the ``all``
|
|
159
|
+
window: the polymer group plus the crowders."""
|
|
160
|
+
return {**self.polymer_counts(), 'Crd': self.n_crowd}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _real_wholes() -> list[RealWhole]:
|
|
164
|
+
"""The 22 real wholes: per study, two replicates without crowders and
|
|
165
|
+
two at a bulk crowder volume fraction of about 0.36 to 0.38, and for
|
|
166
|
+
the Hns studies the wholes without H-NS; HnsCyl adds crowders of size
|
|
167
|
+
1."""
|
|
168
|
+
hns_cub = 'N200kbmm2nh{nh}ac2l25epshc1nc{nc}ens{e}'
|
|
169
|
+
hns_cyl = 'N200kbmm2r4.5nh{nh}ac{ac}lz75epshc1nc{nc}ens{e}'
|
|
170
|
+
foci_cub = ('al6nl5ml216ns400ac1nc{nc}l{l}dt0.005bdump2000adump5000'
|
|
171
|
+
'ens{e}')
|
|
172
|
+
foci_cyl = ('epss5epsl5r10.5al6nl5ml216ns400ac1nc{nc}lz{l}dt0.005'
|
|
173
|
+
'bdump2000adump5000ens{e}')
|
|
174
|
+
wholes = []
|
|
175
|
+
for e in (1, 2):
|
|
176
|
+
for nh, nc, tier in ((0, 0, 'B'), (30, 0, 'B'), (30, 10743, 'C')):
|
|
177
|
+
name = hns_cub.format(nh=nh, nc=nc, e=e)
|
|
178
|
+
wholes.append(RealWhole(
|
|
179
|
+
f"HnsCub-nh{nh}-nc{nc}-ens{e}", 'hns_cub', name,
|
|
180
|
+
hns_cub.format(nh=nh, nc=0, e=e), 'nucleoid', 200, 0, nh,
|
|
181
|
+
nc, tier, 25, 25, name))
|
|
182
|
+
for nh, ac, nc, tier in ((0, 2, 0, 'B'), (30, 2, 0, 'B'),
|
|
183
|
+
(30, 2, 386, 'B'), (30, 1, 4032, 'C')):
|
|
184
|
+
name = hns_cyl.format(nh=nh, ac=ac, nc=nc, e=e)
|
|
185
|
+
wholes.append(RealWhole(
|
|
186
|
+
f"HnsCyl-nh{nh}-ac{ac}-nc{nc}-ens{e}", 'hns_cyl', name,
|
|
187
|
+
hns_cyl.format(nh=nh, ac=2, nc=0, e=e), 'nucleoid', 200, 0,
|
|
188
|
+
nh, nc, tier, 25, 25, name.replace('epshc1', '')))
|
|
189
|
+
for template, study, label, uncrowded, crowded in (
|
|
190
|
+
(foci_cub, 'trans_foci_cub', 'TransFociCub',
|
|
191
|
+
(0, '46.5'), (112776, '27')),
|
|
192
|
+
(foci_cyl, 'trans_foci_cyl', 'TransFociCyl',
|
|
193
|
+
(0, '82.5'), (22230, '57'))):
|
|
194
|
+
topology = template.format(nc=uncrowded[0], l=uncrowded[1], e=e)
|
|
195
|
+
for (nc, length), tier in ((uncrowded, 'B'), (crowded, 'C')):
|
|
196
|
+
name = template.format(nc=nc, l=length, e=e)
|
|
197
|
+
wholes.append(RealWhole(
|
|
198
|
+
f"{label}-nc{nc}-ens{e}", study, name, topology, 'bug',
|
|
199
|
+
405, 5, 0, nc, tier, 20, 10, name))
|
|
200
|
+
return sorted(wholes, key=lambda whole: whole.id)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _params(keep: Callable[[RealWhole], bool]) -> list[Any]:
|
|
204
|
+
"""The real wholes that `keep` accepts, as pytest params."""
|
|
205
|
+
return [pytest.param(whole, id=whole.id) for whole in _real_wholes()
|
|
206
|
+
if keep(whole)]
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
@pytest.fixture(params=_params(lambda whole: True))
|
|
210
|
+
def real_polymer_whole(request: pytest.FixtureRequest) -> RealWhole:
|
|
211
|
+
"""Each of the 22 real wholes, whose polymer dumps are all tier B."""
|
|
212
|
+
return request.param
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@pytest.fixture(params=_params(lambda whole: whole.window_tier == 'B'))
|
|
216
|
+
def real_window_whole(request: pytest.FixtureRequest) -> RealWhole:
|
|
217
|
+
"""Each real whole whose ``all`` window and data file are tier B: the
|
|
218
|
+
12 without crowders and the 2 HnsCyl wholes at ``ac2 nc386``."""
|
|
219
|
+
return request.param
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
@pytest.fixture(params=_params(lambda whole: whole.window_tier == 'C'))
|
|
223
|
+
def real_crowded_whole(request: pytest.FixtureRequest) -> RealWhole:
|
|
224
|
+
"""Each of the 8 crowded wholes whose ``all`` window and data file are
|
|
225
|
+
tier C."""
|
|
226
|
+
return request.param # pragma: no cover - needs data
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@pytest.fixture(params=_params(lambda whole: whole.n_foci > 0))
|
|
230
|
+
def real_foci_whole(request: pytest.FixtureRequest) -> RealWhole:
|
|
231
|
+
"""Each of the 8 TransFoci wholes."""
|
|
232
|
+
return request.param
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@pytest.fixture(params=_params(lambda whole: whole.n_hns > 0))
|
|
236
|
+
def real_hns_whole(request: pytest.FixtureRequest) -> RealWhole:
|
|
237
|
+
"""Each of the 10 Hns wholes with H-NS proteins."""
|
|
238
|
+
return request.param
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@pytest.fixture(params=_params(
|
|
242
|
+
lambda whole: whole.group == 'nucleoid' and whole.n_hns == 0))
|
|
243
|
+
def real_nh0_whole(request: pytest.FixtureRequest) -> RealWhole:
|
|
244
|
+
"""Each of the 4 Hns wholes without H-NS proteins."""
|
|
245
|
+
return request.param
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@pytest.fixture(scope='session')
|
|
249
|
+
def study_spec() -> Callable[[str], ModuleType]:
|
|
250
|
+
"""Factory: the spec module ``projects/<study>/spec.py`` of a study,
|
|
251
|
+
with its ``SPEC`` and ``PROBE``. The study folders are not an installed
|
|
252
|
+
package, so the repository root is put on the import path, as the
|
|
253
|
+
pipeline's tier C test does."""
|
|
254
|
+
root = str(Path(__file__).resolve().parents[3])
|
|
255
|
+
if root not in sys.path:
|
|
256
|
+
sys.path.insert(0, root)
|
|
257
|
+
|
|
258
|
+
def _load(study: str) -> ModuleType:
|
|
259
|
+
return importlib.import_module(f"projects.{study}.spec")
|
|
260
|
+
|
|
261
|
+
return _load
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
@dataclass(frozen=True)
|
|
265
|
+
class RealFiles:
|
|
266
|
+
"""The files of a real whole; `window` and `data` are None when they
|
|
267
|
+
are not in the repository."""
|
|
268
|
+
|
|
269
|
+
polymer: Path
|
|
270
|
+
topology: Path
|
|
271
|
+
window: Optional[Path]
|
|
272
|
+
data: Optional[Path]
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
@pytest.fixture
|
|
276
|
+
def real_files() -> Callable[[RealWhole], RealFiles]:
|
|
277
|
+
"""Factory: the tier B files of a real whole."""
|
|
278
|
+
root = Path(__file__).resolve().parents[1] / 'data' / 'probe'
|
|
279
|
+
|
|
280
|
+
def _files(whole: RealWhole) -> RealFiles:
|
|
281
|
+
folder = root / whole.whole
|
|
282
|
+
windows = sorted(folder.glob(f"{whole.whole}.j*.ring.all.window."
|
|
283
|
+
"lammpstrj.gz"))
|
|
284
|
+
data = folder / f"{whole.whole}.ring.all.data.gz"
|
|
285
|
+
return RealFiles(
|
|
286
|
+
polymer=folder / (f"{whole.whole}.ring.{whole.group}.last50."
|
|
287
|
+
"lammpstrj.gz"),
|
|
288
|
+
topology=root / whole.topology / (f"{whole.topology}.ring.all."
|
|
289
|
+
"data.gz"),
|
|
290
|
+
window=windows[0] if windows else None,
|
|
291
|
+
data=data if data.exists() else None)
|
|
292
|
+
|
|
293
|
+
return _files
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
@pytest.fixture
|
|
297
|
+
def private_window_files(
|
|
298
|
+
private_data_dir: Path,
|
|
299
|
+
study_spec: Callable[[str], ModuleType],
|
|
300
|
+
) -> Callable[[RealWhole], tuple[Path, Path]]:
|
|
301
|
+
"""Factory: the tier C ``all`` window and data file of a crowded whole,
|
|
302
|
+
under ``<POLYPHYS_DATA_DIR>/<project>/trjs/<source>/``, with
|
|
303
|
+
``<project>`` the study's project name (``SPEC.name``, such as
|
|
304
|
+
``HnsCub``). Use it only in a test marked ``private_data``."""
|
|
305
|
+
|
|
306
|
+
def _files(whole: RealWhole) -> tuple[Path, Path]: # pragma: no cover
|
|
307
|
+
project = study_spec(whole.study).SPEC.name
|
|
308
|
+
folder = private_data_dir / project / 'trjs' / whole.source
|
|
309
|
+
(window,) = sorted(folder.glob(f"{whole.source}.j*.ring.all.window."
|
|
310
|
+
"lammpstrj.gz"))
|
|
311
|
+
return window, folder / f"{whole.source}.ring.all.data.gz"
|
|
312
|
+
|
|
313
|
+
return _files # pragma: no cover - needs data
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
@pytest.fixture
|
|
317
|
+
def real_universe() -> Callable[[Path, Path], Any]:
|
|
318
|
+
"""Factory: a data file and dump opened with
|
|
319
|
+
:func:`polyphys.probe.make_universe`, without a time step: the tests
|
|
320
|
+
count frames, not time, so the reader's warning about the missing
|
|
321
|
+
``dt`` is silenced."""
|
|
322
|
+
|
|
323
|
+
def _open(topology: Path, trajectory: Path) -> Any:
|
|
324
|
+
from polyphys.probe import make_universe
|
|
325
|
+
|
|
326
|
+
with warnings.catch_warnings():
|
|
327
|
+
warnings.filterwarnings(
|
|
328
|
+
'ignore', 'Reader has no dt information', UserWarning)
|
|
329
|
+
return make_universe(topology, trajectory)
|
|
330
|
+
|
|
331
|
+
return _open
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
@pytest.fixture
|
|
335
|
+
def trajectory_arrays() -> Callable[[Any], Mapping[str, np.ndarray]]:
|
|
336
|
+
"""Factory: every frame of a universe in one pass: ``'positions'``,
|
|
337
|
+
shape ``(n_frames, n_atoms, 3)``, in the reader's frame;
|
|
338
|
+
``'boxes'``, shape ``(n_frames, 3)``, the box lengths; and
|
|
339
|
+
``'steps'``, shape ``(n_frames,)``, the LAMMPS time steps."""
|
|
340
|
+
|
|
341
|
+
def _arrays(universe: Any) -> Mapping[str, np.ndarray]:
|
|
342
|
+
frames = [(ts.positions.copy(), ts.dimensions[:3].copy(),
|
|
343
|
+
ts.data['step']) for ts in universe.trajectory]
|
|
344
|
+
return {'positions': np.stack([frame[0] for frame in frames]),
|
|
345
|
+
'boxes': np.stack([frame[1] for frame in frames]),
|
|
346
|
+
'steps': np.array([frame[2] for frame in frames])}
|
|
347
|
+
|
|
348
|
+
return _arrays
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
@pytest.fixture
|
|
352
|
+
def reference_distances() -> Callable[..., np.ndarray]:
|
|
353
|
+
"""Factory: the distances between the rows of `first` and `second`,
|
|
354
|
+
shapes ``(n_frames, n, 3)`` and ``(n_frames, m, 3)``, frame by frame
|
|
355
|
+
with :func:`MDAnalysis.lib.distances.distance_array` and the minimum
|
|
356
|
+
image along the geometry's periodic directions: all three in a cube;
|
|
357
|
+
only z in a cylinder, whose box lengths across the axis are replaced by
|
|
358
|
+
1e4, far beyond any separation in it, so that only z is imaged.
|
|
359
|
+
Returns shape ``(n_frames, n, m)``."""
|
|
360
|
+
|
|
361
|
+
def _distances(first: np.ndarray, second: np.ndarray, boxes: np.ndarray,
|
|
362
|
+
geometry: str) -> np.ndarray:
|
|
363
|
+
from MDAnalysis.lib.distances import distance_array
|
|
364
|
+
|
|
365
|
+
lengths = np.array(boxes, dtype=np.float64)
|
|
366
|
+
if geometry == 'cylindrical':
|
|
367
|
+
lengths[:, :2] = 1e4
|
|
368
|
+
return np.stack([
|
|
369
|
+
distance_array(a, b, box=np.concatenate([box, [90.0] * 3]))
|
|
370
|
+
for a, b, box in zip(first, second, lengths)])
|
|
371
|
+
|
|
372
|
+
return _distances
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
@pytest.fixture(params=_params(lambda whole: whole.study == 'trans_foci_cub'))
|
|
376
|
+
def real_transfocicub_whole(request: pytest.FixtureRequest) -> RealWhole:
|
|
377
|
+
"""Each of the 4 TransFociCub wholes: the research code probed the same
|
|
378
|
+
50 frames of their polymer dumps, and that output, tier C, is the
|
|
379
|
+
reference of the pipeline tests."""
|
|
380
|
+
return request.param # pragma: no cover - needs data
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
@pytest.fixture
|
|
384
|
+
def research_probe_file(
|
|
385
|
+
private_data_dir: Path,
|
|
386
|
+
study_spec: Callable[[str], ModuleType],
|
|
387
|
+
) -> Callable[..., Path]:
|
|
388
|
+
"""Factory: the file of one property in the research code's probe output
|
|
389
|
+
of a real whole,
|
|
390
|
+
``<POLYPHYS_DATA_DIR>/<project>/probes/<space>-<group>-probe/<whole>.ring/``
|
|
391
|
+
``<whole>.ring-<group>-<property>.<ext>``, with ``<project>`` the
|
|
392
|
+
study's ``SPEC.name``; the research probe named a whole with its
|
|
393
|
+
topology suffix. The test is skipped when the study has no single such
|
|
394
|
+
probe folder. Use it only in a test marked ``private_data``."""
|
|
395
|
+
|
|
396
|
+
def _file(whole: RealWhole, prop: str, # pragma: no cover - needs data
|
|
397
|
+
ext: str = 'npy') -> Path:
|
|
398
|
+
project = study_spec(whole.study).SPEC.name
|
|
399
|
+
probes = private_data_dir / project / 'probes'
|
|
400
|
+
folders = sorted(probes.glob(f"*-{whole.group}-probe"))
|
|
401
|
+
if len(folders) != 1:
|
|
402
|
+
pytest.skip(f"no single research {whole.group} probe folder "
|
|
403
|
+
f"under {probes}")
|
|
404
|
+
return (folders[0] / f"{whole.whole}.ring"
|
|
405
|
+
/ f"{whole.whole}.ring-{whole.group}-{prop}.{ext}")
|
|
406
|
+
|
|
407
|
+
return _file # pragma: no cover - needs data
|