asyncmd 0.3.3__py3-none-any.whl → 0.4.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.
- asyncmd/__init__.py +7 -0
- asyncmd/_config.py +16 -9
- asyncmd/_version.py +22 -36
- asyncmd/config.py +66 -33
- asyncmd/gromacs/__init__.py +3 -0
- asyncmd/gromacs/mdconfig.py +6 -16
- asyncmd/gromacs/mdengine.py +429 -421
- asyncmd/gromacs/utils.py +34 -17
- asyncmd/mdconfig.py +53 -163
- asyncmd/mdengine.py +120 -39
- asyncmd/slurm.py +29 -46
- asyncmd/tools.py +284 -5
- asyncmd/trajectory/__init__.py +19 -1
- asyncmd/trajectory/convert.py +133 -97
- asyncmd/trajectory/functionwrapper.py +197 -161
- asyncmd/trajectory/propagate.py +301 -254
- asyncmd/trajectory/trajectory.py +498 -755
- asyncmd/trajectory/trajectory_cache.py +365 -0
- asyncmd/utils.py +18 -13
- asyncmd-0.4.0.dist-info/METADATA +90 -0
- asyncmd-0.4.0.dist-info/RECORD +24 -0
- {asyncmd-0.3.3.dist-info → asyncmd-0.4.0.dist-info}/WHEEL +1 -1
- asyncmd-0.3.3.dist-info/METADATA +0 -194
- asyncmd-0.3.3.dist-info/RECORD +0 -23
- {asyncmd-0.3.3.dist-info → asyncmd-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {asyncmd-0.3.3.dist-info → asyncmd-0.4.0.dist-info}/top_level.txt +0 -0
asyncmd/trajectory/convert.py
CHANGED
@@ -12,9 +12,14 @@
|
|
12
12
|
#
|
13
13
|
# You should have received a copy of the GNU General Public License
|
14
14
|
# along with asyncmd. If not, see <https://www.gnu.org/licenses/>.
|
15
|
+
"""
|
16
|
+
This module contains various classes used for trajectory extraction and concatenation.
|
17
|
+
|
18
|
+
Most notably the FrameExtractor classes and the TrajectoryConcatenator.
|
19
|
+
"""
|
15
20
|
import os
|
16
21
|
import abc
|
17
|
-
import
|
22
|
+
import collections.abc
|
18
23
|
import asyncio
|
19
24
|
import logging
|
20
25
|
import functools
|
@@ -48,8 +53,8 @@ def _is_documented_by(original):
|
|
48
53
|
|
49
54
|
def _attach_mda_trafos_to_universe(
|
50
55
|
universe: mda.Universe,
|
51
|
-
mda_transformations:
|
52
|
-
mda_transformations_setup_func:
|
56
|
+
mda_transformations: list[collections.abc.Callable] | None = None,
|
57
|
+
mda_transformations_setup_func: collections.abc.Callable | None = None,
|
53
58
|
) -> mda.Universe:
|
54
59
|
"""
|
55
60
|
Attach MDAnalysis transformations to a given universe.
|
@@ -64,10 +69,10 @@ def _attach_mda_trafos_to_universe(
|
|
64
69
|
----------
|
65
70
|
universe : MDAnalysis.core.universe.Universe
|
66
71
|
The universe to attach the transformations to.
|
67
|
-
mda_transformations :
|
72
|
+
mda_transformations : list[collections.abc.Callable] or None, optional
|
68
73
|
List of MDAnalysis transformations to attach, by default None
|
69
|
-
mda_transformations_setup_func :
|
70
|
-
Setup function to attach MDAnalysis
|
74
|
+
mda_transformations_setup_func : collections.abc.Callable or None, optional
|
75
|
+
Setup function to attach MDAnalysis transformations to the universe,
|
71
76
|
by default None
|
72
77
|
|
73
78
|
Returns
|
@@ -78,7 +83,7 @@ def _attach_mda_trafos_to_universe(
|
|
78
83
|
Raises
|
79
84
|
------
|
80
85
|
ValueError
|
81
|
-
If both ``mda_transformations`` and ``
|
86
|
+
If both ``mda_transformations`` and ``mda_transformations_setup_func``
|
82
87
|
are given.
|
83
88
|
"""
|
84
89
|
# NOTE: this func is used to attach the MDAnalysis transformations to
|
@@ -105,7 +110,9 @@ class TrajectoryConcatenator:
|
|
105
110
|
and returns one trajectory containing only the selected frames in the order
|
106
111
|
specified by the slices.
|
107
112
|
Velocities are automatically inverted if the step of a slice is negative,
|
108
|
-
this can be controlled via the invert_v_for_negative_step attribute.
|
113
|
+
this can be controlled via the ``invert_v_for_negative_step`` attribute.
|
114
|
+
Double frames are also automatically removed, which can be controlled via
|
115
|
+
the ``remove_double_frames`` attribute.
|
109
116
|
We assume that all trajs have the same structure file and attach the
|
110
117
|
structure of the first traj if not told otherwise.
|
111
118
|
Note that you can pass MDAnalysis transformations to this class to
|
@@ -116,12 +123,20 @@ class TrajectoryConcatenator:
|
|
116
123
|
----------
|
117
124
|
invert_v_for_negative_step : bool
|
118
125
|
Whether to invert all momenta for segments with negative stride.
|
126
|
+
remove_double_frames : bool
|
127
|
+
Whether we should (try to) remove double frames from the concatenated
|
128
|
+
output trajectory.
|
129
|
+
Note that a simple heuristic is used to determine double frames,
|
130
|
+
frames count as double if the integration time is the same for both
|
131
|
+
frames.
|
119
132
|
"""
|
120
133
|
|
121
|
-
def __init__(
|
122
|
-
|
123
|
-
|
124
|
-
|
134
|
+
def __init__(
|
135
|
+
self,
|
136
|
+
invert_v_for_negative_step: bool = True,
|
137
|
+
remove_double_frames: bool = True, *,
|
138
|
+
mda_transformations: list[collections.abc.Callable] | None = None,
|
139
|
+
mda_transformations_setup_func: collections.abc.Callable | None = None,
|
125
140
|
) -> None:
|
126
141
|
"""
|
127
142
|
Initialize a :class:`TrajectoryConcatenator`.
|
@@ -131,6 +146,9 @@ class TrajectoryConcatenator:
|
|
131
146
|
invert_v_for_negative_step : bool, optional
|
132
147
|
Whether to invert all momenta for segments with negative stride,
|
133
148
|
by default True.
|
149
|
+
remove_double_frames : bool, optional
|
150
|
+
Whether we should (try to) remove double frames from the concatenated
|
151
|
+
output trajectory, by default True.
|
134
152
|
mda_transformations : list of callables, optional
|
135
153
|
If given will be added as a list of transformations to the
|
136
154
|
MDAnalysis universe as
|
@@ -138,7 +156,8 @@ class TrajectoryConcatenator:
|
|
138
156
|
See the ``mda_transformations_setup_func`` argument if your
|
139
157
|
transformations need additional universe-dependant arguments, e.g.
|
140
158
|
atomgroups from the universe.
|
141
|
-
See
|
159
|
+
See
|
160
|
+
https://docs.mdanalysis.org/stable/documentation_pages/trajectory_transformations.html
|
142
161
|
for more on MDAnalysis transformations.
|
143
162
|
mda_transformations_setup_func: callable, optional
|
144
163
|
If given will be called to attach user-defined MDAnalysis
|
@@ -149,12 +168,16 @@ class TrajectoryConcatenator:
|
|
149
168
|
after defining ``list_of_trafos`` (potentially depending on the
|
150
169
|
universe or atomgroups therein) and then finally returns the
|
151
170
|
universe with trafos.
|
152
|
-
See
|
171
|
+
See
|
172
|
+
https://docs.mdanalysis.org/stable/documentation_pages/trajectory_transformations.html
|
153
173
|
for more on MDAnalysis transformations.
|
154
174
|
"""
|
155
175
|
self.invert_v_for_negative_step = invert_v_for_negative_step
|
156
|
-
|
157
|
-
|
176
|
+
self.remove_double_frames = remove_double_frames
|
177
|
+
if (
|
178
|
+
mda_transformations is not None
|
179
|
+
and mda_transformations_setup_func is not None
|
180
|
+
):
|
158
181
|
raise ValueError("`mda_transformations` and "
|
159
182
|
"`mda_transformations_setup_func` are mutually "
|
160
183
|
"exclusive, but both were given."
|
@@ -162,10 +185,11 @@ class TrajectoryConcatenator:
|
|
162
185
|
self.mda_transformations = mda_transformations
|
163
186
|
self.mda_transformations_setup_func = mda_transformations_setup_func
|
164
187
|
|
165
|
-
def concatenate(self, trajs:
|
166
|
-
tra_out: str,
|
188
|
+
def concatenate(self, trajs: list[Trajectory], slices: list[tuple],
|
189
|
+
tra_out: str, *,
|
190
|
+
struct_out: str | None = None,
|
167
191
|
overwrite: bool = False,
|
168
|
-
|
192
|
+
) -> Trajectory:
|
169
193
|
"""
|
170
194
|
Create concatenated trajectory from given trajectories and frames.
|
171
195
|
|
@@ -185,12 +209,6 @@ class TrajectoryConcatenator:
|
|
185
209
|
overwrite : bool, optional
|
186
210
|
Whether we should overwrite existing output trajectories,
|
187
211
|
by default False.
|
188
|
-
remove_double_frames : bool, optional
|
189
|
-
Wheter we should try to remove double frames from the concatenated
|
190
|
-
output trajectory.
|
191
|
-
Note that we use a simple heuristic to determine double frames,
|
192
|
-
we just check if the integration time is the same for both frames,
|
193
|
-
by default True
|
194
212
|
|
195
213
|
Returns
|
196
214
|
-------
|
@@ -217,68 +235,31 @@ class TrajectoryConcatenator:
|
|
217
235
|
)
|
218
236
|
|
219
237
|
# special treatment for traj0 because we need n_atoms for the writer
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
# if the file exists MDAnalysis will silently overwrite
|
230
|
-
with mda.Writer(tra_out, n_atoms=u0.trajectory.n_atoms) as W:
|
231
|
-
for ts in u0.trajectory[start0:stop0:step0]:
|
232
|
-
if (self.invert_v_for_negative_step and step0 < 0
|
233
|
-
and ts.has_velocities):
|
234
|
-
u0.atoms.velocities *= -1
|
235
|
-
W.write(u0.atoms)
|
236
|
-
if remove_double_frames:
|
237
|
-
# remember the last timestamp, so we can take it out
|
238
|
-
last_time_seen = ts.data["time"]
|
239
|
-
# close the trajectory file for and delete the original universe
|
240
|
-
u0.trajectory.close()
|
241
|
-
del u0
|
242
|
-
for traj, sl in zip(trajs[1:], slices[1:]):
|
243
|
-
u = mda.Universe(traj.structure_file, *traj.trajectory_files)
|
244
|
-
u = _attach_mda_trafos_to_universe(
|
245
|
-
universe=u,
|
246
|
-
mda_transformations=self.mda_transformations,
|
247
|
-
mda_transformations_setup_func=self.mda_transformations_setup_func,
|
248
|
-
)
|
249
|
-
start, stop, step = sl
|
250
|
-
for ts in u.trajectory[start:stop:step]:
|
251
|
-
if remove_double_frames and (last_time_seen is not None):
|
252
|
-
if last_time_seen == ts.data["time"]:
|
253
|
-
# this is a no-op, as they are they same...
|
254
|
-
# last_time_seen = ts.data["time"]
|
255
|
-
continue # skip this timestep/go to next iteration
|
256
|
-
if (self.invert_v_for_negative_step and step < 0
|
257
|
-
and ts.has_velocities):
|
258
|
-
u.atoms.velocities *= -1
|
259
|
-
W.write(u.atoms)
|
260
|
-
if remove_double_frames:
|
261
|
-
last_time_seen = ts.data["time"]
|
262
|
-
# make sure MDAnalysis closes the underlying trajectory file
|
263
|
-
u.trajectory.close()
|
264
|
-
del u # and delete the universe just because we can
|
238
|
+
u = mda.Universe(trajs[0].structure_file, *trajs[0].trajectory_files)
|
239
|
+
last_time_seen = None
|
240
|
+
with mda.Writer(tra_out, n_atoms=u.atoms.n_atoms) as writer:
|
241
|
+
# iterate over the trajectories
|
242
|
+
for traj, sl in zip(trajs, slices):
|
243
|
+
last_time_seen = self._write_one_traj_sliced_with_writer(
|
244
|
+
traj=traj, sl=sl, writer=writer,
|
245
|
+
last_time_seen=last_time_seen,
|
246
|
+
)
|
265
247
|
# return (file paths to) the finished trajectory
|
266
248
|
return Trajectory(tra_out, struct_out)
|
267
249
|
|
268
250
|
@_is_documented_by(concatenate)
|
269
251
|
# pylint: disable-next=missing-function-docstring
|
270
|
-
async def concatenate_async(self, trajs:
|
271
|
-
slices:
|
272
|
-
struct_out:
|
252
|
+
async def concatenate_async(self, trajs: list[Trajectory],
|
253
|
+
slices: list[tuple], tra_out: str, *,
|
254
|
+
struct_out: str | None = None,
|
273
255
|
overwrite: bool = False,
|
274
|
-
|
256
|
+
) -> Trajectory:
|
275
257
|
concat_fx = functools.partial(self.concatenate,
|
276
258
|
trajs=trajs,
|
277
259
|
slices=slices,
|
278
260
|
tra_out=tra_out,
|
279
261
|
struct_out=struct_out,
|
280
262
|
overwrite=overwrite,
|
281
|
-
remove_double_frames=remove_double_frames,
|
282
263
|
)
|
283
264
|
loop = asyncio.get_running_loop()
|
284
265
|
async with _SEMAPHORES["MAX_FILES_OPEN"]:
|
@@ -288,6 +269,55 @@ class TrajectoryConcatenator:
|
|
288
269
|
) as pool:
|
289
270
|
return await loop.run_in_executor(pool, concat_fx)
|
290
271
|
|
272
|
+
def _write_one_traj_sliced_with_writer(self, traj: Trajectory, sl: tuple, *,
|
273
|
+
writer: mda.Writer,
|
274
|
+
last_time_seen: float | None,
|
275
|
+
) -> float | None:
|
276
|
+
"""
|
277
|
+
Write one Trajectory (sliced) using the given MDAnalysis writer.
|
278
|
+
|
279
|
+
Take as argument and return (the updated) ``last_time_seen`` to enable
|
280
|
+
removal of double frames over multiple Trajectories and subsequent calls
|
281
|
+
to this method.
|
282
|
+
|
283
|
+
Parameters
|
284
|
+
----------
|
285
|
+
traj : Trajectory
|
286
|
+
The origin trajectory to iterate over (in a sliced manner).
|
287
|
+
sl : tuple
|
288
|
+
The slice defining which frames are written from ``traj``.
|
289
|
+
writer : mda.Writer
|
290
|
+
last_time_seen : float | None
|
291
|
+
Integration time of the last written/seen timestep.
|
292
|
+
|
293
|
+
Returns
|
294
|
+
-------
|
295
|
+
float | None
|
296
|
+
Updated ``last_time_seen``.
|
297
|
+
"""
|
298
|
+
u = mda.Universe(traj.structure_file, *traj.trajectory_files)
|
299
|
+
u = _attach_mda_trafos_to_universe(
|
300
|
+
universe=u,
|
301
|
+
mda_transformations=self.mda_transformations,
|
302
|
+
mda_transformations_setup_func=self.mda_transformations_setup_func,
|
303
|
+
)
|
304
|
+
start, stop, step = sl
|
305
|
+
for ts in u.trajectory[start:stop:step]:
|
306
|
+
if self.remove_double_frames and (last_time_seen is not None):
|
307
|
+
if last_time_seen == ts.data["time"]:
|
308
|
+
continue
|
309
|
+
if (
|
310
|
+
self.invert_v_for_negative_step and step < 0
|
311
|
+
and ts.has_velocities
|
312
|
+
):
|
313
|
+
u.atoms.velocities *= -1
|
314
|
+
writer.write(u.atoms)
|
315
|
+
if self.remove_double_frames:
|
316
|
+
last_time_seen = ts.data["time"]
|
317
|
+
# make sure MDAnalysis closes the underlying trajectory file
|
318
|
+
u.trajectory.close()
|
319
|
+
return last_time_seen
|
320
|
+
|
291
321
|
|
292
322
|
class FrameExtractor(abc.ABC):
|
293
323
|
"""
|
@@ -303,9 +333,9 @@ class FrameExtractor(abc.ABC):
|
|
303
333
|
# with inverted velocities, with random Maxwell-Boltzmann velocities, etc.
|
304
334
|
|
305
335
|
def __init__(
|
306
|
-
self,
|
307
|
-
mda_transformations:
|
308
|
-
mda_transformations_setup_func:
|
336
|
+
self, *,
|
337
|
+
mda_transformations: list[collections.abc.Callable] | None = None,
|
338
|
+
mda_transformations_setup_func: collections.abc.Callable | None = None,
|
309
339
|
) -> None:
|
310
340
|
"""
|
311
341
|
Initialize a :class:`FrameExtractor`.
|
@@ -319,7 +349,8 @@ class FrameExtractor(abc.ABC):
|
|
319
349
|
See the ``mda_transformations_setup_func`` argument if your
|
320
350
|
transformations need additional universe-dependant arguments, e.g.
|
321
351
|
atomgroups from the universe.
|
322
|
-
See
|
352
|
+
See
|
353
|
+
https://docs.mdanalysis.org/stable/documentation_pages/trajectory_transformations.html
|
323
354
|
for more on MDAnalysis transformations.
|
324
355
|
mda_transformations_setup_func: callable, optional
|
325
356
|
If given will be called to attach user-defined MDAnalysis
|
@@ -330,11 +361,14 @@ class FrameExtractor(abc.ABC):
|
|
330
361
|
after defining ``list_of_trafos`` (potentially depending on the
|
331
362
|
universe or atomgroups therein) and then finally returns the
|
332
363
|
universe with trafos.
|
333
|
-
See
|
364
|
+
See
|
365
|
+
https://docs.mdanalysis.org/stable/documentation_pages/trajectory_transformations.html
|
334
366
|
for more on MDAnalysis transformations.
|
335
367
|
"""
|
336
|
-
if (
|
337
|
-
|
368
|
+
if (
|
369
|
+
mda_transformations is not None
|
370
|
+
and mda_transformations_setup_func is not None
|
371
|
+
):
|
338
372
|
raise ValueError("`mda_transformations` and "
|
339
373
|
"`mda_transformations_setup_func` are mutually "
|
340
374
|
"exclusive, but both were given."
|
@@ -368,8 +402,9 @@ class FrameExtractor(abc.ABC):
|
|
368
402
|
"""
|
369
403
|
raise NotImplementedError
|
370
404
|
|
371
|
-
def extract(self, outfile, traj_in: Trajectory, idx: int,
|
372
|
-
struct_out=None, overwrite: bool = False
|
405
|
+
def extract(self, outfile: str, traj_in: Trajectory, idx: int, *,
|
406
|
+
struct_out: str | None = None, overwrite: bool = False,
|
407
|
+
) -> Trajectory:
|
373
408
|
"""
|
374
409
|
Extract a single frame from given trajectory and write it out.
|
375
410
|
|
@@ -402,11 +437,8 @@ class FrameExtractor(abc.ABC):
|
|
402
437
|
FileNotFoundError
|
403
438
|
If `struct_out` is given but does not exist.
|
404
439
|
"""
|
405
|
-
# TODO: should we check that idx is an idx, i.e. an int?
|
406
440
|
# TODO: make it possible to select a subset of atoms to write out
|
407
441
|
# and also for modification?
|
408
|
-
# TODO: should we make it possible to extract multiple frames, i.e.
|
409
|
-
# enable the use of slices (and iterables of indices?)
|
410
442
|
outfile = os.path.relpath(outfile)
|
411
443
|
if os.path.exists(outfile) and not overwrite:
|
412
444
|
raise FileExistsError(f"overwrite=False but outfile={outfile} exists.")
|
@@ -424,10 +456,10 @@ class FrameExtractor(abc.ABC):
|
|
424
456
|
mda_transformations=self.mda_transformations,
|
425
457
|
mda_transformations_setup_func=self.mda_transformations_setup_func,
|
426
458
|
)
|
427
|
-
with mda.Writer(outfile, n_atoms=u.trajectory.n_atoms) as
|
459
|
+
with mda.Writer(outfile, n_atoms=u.trajectory.n_atoms) as writer:
|
428
460
|
ts = u.trajectory[idx]
|
429
461
|
self.apply_modification(u, ts)
|
430
|
-
|
462
|
+
writer.write(u.atoms)
|
431
463
|
# make sure MDAnalysis closes the underlying trajectory files
|
432
464
|
u.trajectory.close()
|
433
465
|
del u
|
@@ -435,8 +467,9 @@ class FrameExtractor(abc.ABC):
|
|
435
467
|
|
436
468
|
@_is_documented_by(extract)
|
437
469
|
# pylint: disable-next=missing-function-docstring
|
438
|
-
async def extract_async(self, outfile, traj_in: Trajectory, idx: int,
|
439
|
-
struct_out=None, overwrite: bool = False
|
470
|
+
async def extract_async(self, outfile: str, traj_in: Trajectory, idx: int, *,
|
471
|
+
struct_out: str | None = None, overwrite: bool = False,
|
472
|
+
) -> Trajectory:
|
440
473
|
extract_fx = functools.partial(self.extract,
|
441
474
|
outfile=outfile,
|
442
475
|
traj_in=traj_in,
|
@@ -507,10 +540,10 @@ class RandomVelocitiesFrameExtractor(FrameExtractor):
|
|
507
540
|
|
508
541
|
def __init__(
|
509
542
|
self,
|
510
|
-
T: float,
|
511
|
-
mda_transformations:
|
512
|
-
mda_transformations_setup_func:
|
513
|
-
|
543
|
+
T: float, *,
|
544
|
+
mda_transformations: list[collections.abc.Callable] | None = None,
|
545
|
+
mda_transformations_setup_func: collections.abc.Callable | None = None,
|
546
|
+
) -> None:
|
514
547
|
"""
|
515
548
|
Initialize a :class:`RandomVelocitiesFrameExtractor`.
|
516
549
|
|
@@ -525,7 +558,8 @@ class RandomVelocitiesFrameExtractor(FrameExtractor):
|
|
525
558
|
See the ``mda_transformations_setup_func`` argument if your
|
526
559
|
transformations need additional universe-dependant arguments, e.g.
|
527
560
|
atomgroups from the universe.
|
528
|
-
See
|
561
|
+
See
|
562
|
+
https://docs.mdanalysis.org/stable/documentation_pages/trajectory_transformations.html
|
529
563
|
for more on MDAnalysis transformations.
|
530
564
|
mda_transformations_setup_func: callable, optional
|
531
565
|
If given will be called to attach user-defined MDAnalysis
|
@@ -536,13 +570,15 @@ class RandomVelocitiesFrameExtractor(FrameExtractor):
|
|
536
570
|
after defining ``list_of_trafos`` (potentially depending on the
|
537
571
|
universe or atomgroups therein) and then finally returns the
|
538
572
|
universe with trafos.
|
539
|
-
See
|
573
|
+
See
|
574
|
+
https://docs.mdanalysis.org/stable/documentation_pages/trajectory_transformations.html
|
540
575
|
for more on MDAnalysis transformations.
|
541
576
|
"""
|
542
577
|
super().__init__(
|
543
578
|
mda_transformations=mda_transformations,
|
544
579
|
mda_transformations_setup_func=mda_transformations_setup_func,
|
545
580
|
)
|
581
|
+
# pylint: disable-next=invalid-name
|
546
582
|
self.T = T # in K
|
547
583
|
self._rng = np.random.default_rng()
|
548
584
|
|