-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathIMD.py
More file actions
132 lines (106 loc) · 3.75 KB
/
IMD.py
File metadata and controls
132 lines (106 loc) · 3.75 KB
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
"""
MDAnalysis IMDReader
^^^^^^^^^^^^^^^^^^^^
.. autoclass:: IMDReader
:members:
:inherited-members:
"""
from MDAnalysis.coordinates import core
from MDAnalysis.lib.util import store_init_arguments
# NOTE: changeme
from .IMDClient import IMDClient
from .utils import *
import logging
from .streambase import StreamReaderBase
logger = logging.getLogger("imdclient.IMDClient")
class IMDReader(StreamReaderBase):
"""
Reader for IMD protocol packets.
Parameters
----------
filename : a string of the form "host:port" where host is the hostname
or IP address of the listening GROMACS server and port
is the port number.
n_atoms : int (optional)
number of atoms in the system. defaults to number of atoms
in the topology. don't set this unless you know what you're doing.
kwargs : dict (optional)
keyword arguments passed to the constructed :class:`IMDClient`
"""
format = "IMD"
one_pass = True
@store_init_arguments
def __init__(
self,
filename,
convert_units=True,
n_atoms=None,
**kwargs,
):
super(IMDReader, self).__init__(filename, **kwargs)
self._imdclient = None
logger.debug("IMDReader initializing")
if n_atoms is None:
raise ValueError("IMDReader: n_atoms must be specified")
self.n_atoms = n_atoms
host, port = parse_host_port(filename)
# This starts the simulation
self._imdclient = IMDClient(host, port, n_atoms, **kwargs)
imdsinfo = self._imdclient.get_imdsessioninfo()
# NOTE: after testing phase, fail out on IMDv2
self.ts = self._Timestep(
self.n_atoms,
positions=imdsinfo.positions,
velocities=imdsinfo.velocities,
forces=imdsinfo.forces,
**self._ts_kwargs,
)
self._frame = -1
try:
self._read_next_timestep()
except StopIteration:
raise RuntimeError("IMDReader: No data found in stream")
def _read_frame(self, frame):
try:
imdf = self._imdclient.get_imdframe()
except EOFError as e:
raise e
self._frame = frame
self._load_imdframe_into_ts(imdf)
logger.debug(f"IMDReader: Loaded frame {self._frame}")
return self.ts
def _load_imdframe_into_ts(self, imdf):
self.ts.frame = self._frame
if imdf.time is not None:
self.ts.time = imdf.time
# NOTE: timestep.pyx "dt" method is suspicious bc it uses "new" keyword for a float
self.ts.data["dt"] = imdf.dt
self.ts.data["step"] = imdf.step
if imdf.energies is not None:
self.ts.data.update(
{k: v for k, v in imdf.energies.items() if k != "step"}
)
if imdf.box is not None:
self.ts.dimensions = core.triclinic_box(*imdf.box)
if imdf.positions is not None:
# must call copy because reference is expected to reset
# see 'test_frame_collect_all_same' in MDAnalysisTests.coordinates.base
self.ts.positions = imdf.positions
if imdf.velocities is not None:
self.ts.velocities = imdf.velocities
if imdf.forces is not None:
self.ts.forces = imdf.forces
@staticmethod
def _format_hint(thing):
try:
parse_host_port(thing)
except:
return False
return True
def close(self):
"""Gracefully shut down the reader. Stops the producer thread."""
logger.debug("IMDReader close() called")
if self._imdclient is not None:
self._imdclient.stop()
# NOTE: removeme after testing
logger.debug("IMDReader shut down gracefully.")