imdclient 0.1.0__py3-none-any.whl → 0.1.2__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.
- imdclient/IMDClient.py +52 -51
- imdclient/IMDREADER.py +14 -12
- imdclient/tests/conftest.py +14 -10
- imdclient/tests/test_imdreader.py +2 -2
- imdclient/tests/test_manual.py +50 -29
- imdclient/tests/test_stream_analysis.py +1 -1
- imdclient/utils.py +8 -8
- imdclient-0.1.2.dist-info/LICENSE +674 -0
- imdclient-0.1.2.dist-info/METADATA +795 -0
- {imdclient-0.1.0.dist-info → imdclient-0.1.2.dist-info}/RECORD +13 -13
- {imdclient-0.1.0.dist-info → imdclient-0.1.2.dist-info}/WHEEL +1 -1
- imdclient-0.1.0.dist-info/LICENSE +0 -21
- imdclient-0.1.0.dist-info/METADATA +0 -143
- {imdclient-0.1.0.dist-info → imdclient-0.1.2.dist-info}/AUTHORS.md +0 -0
- {imdclient-0.1.0.dist-info → imdclient-0.1.2.dist-info}/top_level.txt +0 -0
imdclient/IMDClient.py
CHANGED
@@ -30,6 +30,23 @@ logger = logging.getLogger(__name__)
|
|
30
30
|
|
31
31
|
|
32
32
|
class IMDClient:
|
33
|
+
"""
|
34
|
+
Parameters
|
35
|
+
----------
|
36
|
+
host : str
|
37
|
+
Hostname of the server
|
38
|
+
port : int
|
39
|
+
Port number of the server
|
40
|
+
n_atoms : int
|
41
|
+
Number of atoms in the simulation
|
42
|
+
socket_bufsize : int, (optional)
|
43
|
+
Size of the socket buffer in bytes. Default is to use the system default
|
44
|
+
buffer_size : int (optional)
|
45
|
+
IMDFramebuffer will be filled with as many :class:`IMDFrame` fit in `buffer_size` [``10MB``]
|
46
|
+
**kwargs : dict (optional)
|
47
|
+
Additional keyword arguments to pass to the :class:`BaseIMDProducer` and :class:`IMDFrameBuffer`
|
48
|
+
"""
|
49
|
+
|
33
50
|
def __init__(
|
34
51
|
self,
|
35
52
|
host,
|
@@ -39,22 +56,7 @@ class IMDClient:
|
|
39
56
|
multithreaded=True,
|
40
57
|
**kwargs,
|
41
58
|
):
|
42
|
-
|
43
|
-
Parameters
|
44
|
-
----------
|
45
|
-
host : str
|
46
|
-
Hostname of the server
|
47
|
-
port : int
|
48
|
-
Port number of the server
|
49
|
-
n_atoms : int
|
50
|
-
Number of atoms in the simulation
|
51
|
-
socket_bufsize : int, optional
|
52
|
-
Size of the socket buffer in bytes. Default is to use the system default
|
53
|
-
buffer_size : int, optional
|
54
|
-
IMDFramebuffer will be filled with as many IMDFrames fit in `buffer_size` [``10MB``]
|
55
|
-
**kwargs : optional
|
56
|
-
Additional keyword arguments to pass to the IMDProducer and IMDFrameBuffer
|
57
|
-
"""
|
59
|
+
|
58
60
|
self._stopped = False
|
59
61
|
self._conn = self._connect_to_server(host, port, socket_bufsize)
|
60
62
|
self._imdsinfo = self._await_IMD_handshake()
|
@@ -254,6 +256,25 @@ class IMDClient:
|
|
254
256
|
|
255
257
|
|
256
258
|
class BaseIMDProducer(threading.Thread):
|
259
|
+
"""
|
260
|
+
|
261
|
+
Parameters
|
262
|
+
----------
|
263
|
+
conn : socket.socket
|
264
|
+
Connection object to the server
|
265
|
+
buffer : IMDFrameBuffer
|
266
|
+
Buffer object to hold IMD frames. If `multithreaded` is False, this
|
267
|
+
argument is ignored
|
268
|
+
sinfo : IMDSessionInfo
|
269
|
+
Information about the IMD session
|
270
|
+
n_atoms : int
|
271
|
+
Number of atoms in the simulation
|
272
|
+
multithreaded : bool, optional
|
273
|
+
If True, socket interaction will occur in a separate thread &
|
274
|
+
frames will be buffered. Single-threaded, blocking IMDClient
|
275
|
+
should only be used in testing [[``True``]]
|
276
|
+
|
277
|
+
"""
|
257
278
|
|
258
279
|
def __init__(
|
259
280
|
self,
|
@@ -265,24 +286,6 @@ class BaseIMDProducer(threading.Thread):
|
|
265
286
|
timeout=5,
|
266
287
|
**kwargs,
|
267
288
|
):
|
268
|
-
"""
|
269
|
-
Parameters
|
270
|
-
----------
|
271
|
-
conn : socket.socket
|
272
|
-
Connection object to the server
|
273
|
-
buffer : IMDFrameBuffer
|
274
|
-
Buffer object to hold IMD frames. If `multithreaded` is False, this
|
275
|
-
argument is ignored
|
276
|
-
sinfo : IMDSessionInfo
|
277
|
-
Information about the IMD session
|
278
|
-
n_atoms : int
|
279
|
-
Number of atoms in the simulation
|
280
|
-
multithreaded : bool, optional
|
281
|
-
If True, socket interaction will occur in a separate thread &
|
282
|
-
frames will be buffered. Single-threaded, blocking IMDClient
|
283
|
-
should only be used in testing [[``True``]]
|
284
|
-
|
285
|
-
"""
|
286
289
|
super(BaseIMDProducer, self).__init__(daemon=True)
|
287
290
|
self._conn = conn
|
288
291
|
self._imdsinfo = sinfo
|
@@ -638,6 +641,21 @@ class IMDFrameBuffer:
|
|
638
641
|
"""
|
639
642
|
Acts as interface between producer (IMDProducer) and consumer (IMDClient) threads
|
640
643
|
when IMDClient runs in multithreaded mode
|
644
|
+
|
645
|
+
Parameters
|
646
|
+
----------
|
647
|
+
imdsinfo : IMDSessionInfo
|
648
|
+
Information about the IMD session
|
649
|
+
n_atoms : int
|
650
|
+
Number of atoms in the simulation
|
651
|
+
buffer_size : int, optional
|
652
|
+
Size of the buffer in bytes [``10MB``]
|
653
|
+
pause_empty_proportion : float, optional
|
654
|
+
Lower threshold proportion of the buffer's IMDFrames that are empty
|
655
|
+
before the simulation is paused [``0.25``]
|
656
|
+
unpause_empty_proportion : float, optional
|
657
|
+
Proportion of the buffer's IMDFrames that must be empty
|
658
|
+
before the simulation is unpaused [``0.5``]
|
641
659
|
"""
|
642
660
|
|
643
661
|
def __init__(
|
@@ -649,23 +667,6 @@ class IMDFrameBuffer:
|
|
649
667
|
unpause_empty_proportion=0.5,
|
650
668
|
**kwargs,
|
651
669
|
):
|
652
|
-
"""
|
653
|
-
Parameters
|
654
|
-
----------
|
655
|
-
imdsinfo : IMDSessionInfo
|
656
|
-
Information about the IMD session
|
657
|
-
n_atoms : int
|
658
|
-
Number of atoms in the simulation
|
659
|
-
buffer_size : int, optional
|
660
|
-
Size of the buffer in bytes [``10MB``]
|
661
|
-
pause_empty_proportion : float, optional
|
662
|
-
Lower threshold proportion of the buffer's IMDFrames that are empty
|
663
|
-
before the simulation is paused [``0.25``]
|
664
|
-
unpause_empty_proportion : float, optional
|
665
|
-
Proportion of the buffer's IMDFrames that must be empty
|
666
|
-
before the simulation is unpaused [``0.5``]
|
667
|
-
"""
|
668
|
-
|
669
670
|
# Syncing reader and producer
|
670
671
|
self._producer_finished = False
|
671
672
|
self._consumer_finished = False
|
imdclient/IMDREADER.py
CHANGED
@@ -24,6 +24,17 @@ logger = logging.getLogger("imdclient.IMDClient")
|
|
24
24
|
class IMDReader(StreamReaderBase):
|
25
25
|
"""
|
26
26
|
Reader for IMD protocol packets.
|
27
|
+
|
28
|
+
Parameters
|
29
|
+
----------
|
30
|
+
filename : a string of the form "host:port" where host is the hostname
|
31
|
+
or IP address of the listening GROMACS server and port
|
32
|
+
is the port number.
|
33
|
+
n_atoms : int (optional)
|
34
|
+
number of atoms in the system. defaults to number of atoms
|
35
|
+
in the topology. don't set this unless you know what you're doing.
|
36
|
+
kwargs : dict (optional)
|
37
|
+
keyword arguments passed to the constructed :class:`IMDClient`
|
27
38
|
"""
|
28
39
|
|
29
40
|
format = "IMD"
|
@@ -37,17 +48,6 @@ class IMDReader(StreamReaderBase):
|
|
37
48
|
n_atoms=None,
|
38
49
|
**kwargs,
|
39
50
|
):
|
40
|
-
"""
|
41
|
-
Parameters
|
42
|
-
----------
|
43
|
-
filename : a string of the form "host:port" where host is the hostname
|
44
|
-
or IP address of the listening GROMACS server and port
|
45
|
-
is the port number.
|
46
|
-
n_atoms : int (optional)
|
47
|
-
number of atoms in the system. defaults to number of atoms
|
48
|
-
in the topology. don't set this unless you know what you're doing.
|
49
|
-
"""
|
50
|
-
|
51
51
|
super(IMDReader, self).__init__(filename, **kwargs)
|
52
52
|
|
53
53
|
logger.debug("IMDReader initializing")
|
@@ -101,7 +101,9 @@ class IMDReader(StreamReaderBase):
|
|
101
101
|
self.ts.data["dt"] = imdf.dt
|
102
102
|
self.ts.data["step"] = imdf.step
|
103
103
|
if imdf.energies is not None:
|
104
|
-
self.ts.data.update(
|
104
|
+
self.ts.data.update(
|
105
|
+
{k: v for k, v in imdf.energies.items() if k != "step"}
|
106
|
+
)
|
105
107
|
if imdf.box is not None:
|
106
108
|
self.ts.dimensions = core.triclinic_box(*imdf.box)
|
107
109
|
if imdf.positions is not None:
|
imdclient/tests/conftest.py
CHANGED
@@ -6,33 +6,37 @@ Global pytest fixtures
|
|
6
6
|
# Command line arguments for 'test_manual.py'
|
7
7
|
def pytest_addoption(parser):
|
8
8
|
parser.addoption(
|
9
|
-
"--
|
9
|
+
"--topol_path_arg",
|
10
10
|
action="store",
|
11
|
+
default=None,
|
11
12
|
)
|
12
13
|
parser.addoption(
|
13
|
-
"--
|
14
|
+
"--traj_path_arg",
|
14
15
|
action="store",
|
16
|
+
default=None,
|
17
|
+
)
|
18
|
+
parser.addoption(
|
19
|
+
"--first_frame_arg", action="store", type=int, default=None
|
15
20
|
)
|
16
|
-
parser.addoption("--first_frame_arg", action="store", type=int)
|
17
21
|
|
18
22
|
|
19
23
|
def pytest_generate_tests(metafunc):
|
20
24
|
# This is called for every test. Only get/set command line arguments
|
21
25
|
# if the argument is specified in the list of test "fixturenames".
|
22
|
-
topol = metafunc.config.option.
|
23
|
-
traj = metafunc.config.option.
|
26
|
+
topol = metafunc.config.option.topol_path_arg
|
27
|
+
traj = metafunc.config.option.traj_path_arg
|
24
28
|
first_frame = metafunc.config.option.first_frame_arg
|
25
29
|
|
26
30
|
if all(
|
27
31
|
arg in metafunc.fixturenames
|
28
|
-
for arg in ["
|
32
|
+
for arg in ["topol_path_arg", "traj_path_arg", "first_frame_arg"]
|
29
33
|
):
|
30
34
|
if topol is None or traj is None or first_frame is None:
|
31
35
|
raise ValueError(
|
32
|
-
"Must pass all three of '--
|
33
|
-
+ "'--
|
36
|
+
"Must pass all three of '--topol_path_arg <path/to/topology>', "
|
37
|
+
+ "'--traj_path_arg <path/to/trajectory>', "
|
34
38
|
+ "'--first_frame_arg <first traj frame to compare to IMD>"
|
35
39
|
)
|
36
|
-
metafunc.parametrize("
|
37
|
-
metafunc.parametrize("
|
40
|
+
metafunc.parametrize("topol_path_arg", [topol])
|
41
|
+
metafunc.parametrize("traj_path_arg", [traj])
|
38
42
|
metafunc.parametrize("first_frame_arg", [first_frame])
|
@@ -55,7 +55,7 @@ class IMDReference(BaseReference):
|
|
55
55
|
self.n_atoms = traj.n_atoms
|
56
56
|
self.prec = 3
|
57
57
|
|
58
|
-
self.trajectory = f"localhost:{self.port}"
|
58
|
+
self.trajectory = f"imd://localhost:{self.port}"
|
59
59
|
self.topology = COORDINATES_TOPOLOGY
|
60
60
|
self.changing_dimensions = True
|
61
61
|
self.reader = IMDReader
|
@@ -585,7 +585,7 @@ class TestStreamIteration:
|
|
585
585
|
server.set_imdsessioninfo(imdsinfo)
|
586
586
|
server.handshake_sequence("localhost", port, first_frame=True)
|
587
587
|
reader = IMDReader(
|
588
|
-
f"localhost:{port}",
|
588
|
+
f"imd://localhost:{port}",
|
589
589
|
n_atoms=universe.trajectory.n_atoms,
|
590
590
|
)
|
591
591
|
server.send_frames(1, 5)
|
imdclient/tests/test_manual.py
CHANGED
@@ -1,18 +1,24 @@
|
|
1
|
-
from imdclient.
|
1
|
+
from imdclient.IMDREADER import IMDReader
|
2
2
|
import pytest
|
3
3
|
import MDAnalysis as mda
|
4
4
|
from MDAnalysisTests.coordinates.base import assert_timestep_almost_equal
|
5
5
|
from numpy.testing import (
|
6
|
-
assert_array_almost_equal,
|
7
|
-
assert_almost_equal,
|
8
6
|
assert_allclose,
|
9
7
|
)
|
10
8
|
import numpy as np
|
11
9
|
from .base import assert_allclose_with_logging
|
10
|
+
from pathlib import Path
|
12
11
|
|
13
12
|
import logging
|
14
13
|
|
15
14
|
logger = logging.getLogger("imdclient.IMDClient")
|
15
|
+
file_handler = logging.FileHandler("manual_test.log")
|
16
|
+
formatter = logging.Formatter(
|
17
|
+
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
18
|
+
)
|
19
|
+
file_handler.setFormatter(formatter)
|
20
|
+
logger.addHandler(file_handler)
|
21
|
+
logger.setLevel(logging.DEBUG)
|
16
22
|
|
17
23
|
|
18
24
|
class TestIMDv3Manual:
|
@@ -23,48 +29,63 @@ class TestIMDv3Manual:
|
|
23
29
|
and then run this command relative to the root of the cloned respository:
|
24
30
|
|
25
31
|
pytest -s imdclient/tests/test_manual.py \
|
26
|
-
--
|
27
|
-
--
|
32
|
+
--topol_path_arg <path/to/topology> \
|
33
|
+
--traj_path_arg <path/to/trajectory> \
|
28
34
|
--first_frame_arg <first traj frame to compare to IMD>
|
29
35
|
|
30
|
-
Where the topology is the same topology as the IMD system, the trajectory is the
|
31
|
-
|
36
|
+
Where the topology is the same topology as the IMD system, the trajectory is the path where
|
37
|
+
the trajectory of the running simulation is being written, and the first frame is the first frame of the
|
32
38
|
trajectory which should be compared to IMD data read from the socket (0 for GROMACS and NAMD, 1 for LAMMPS)
|
33
39
|
"""
|
34
40
|
|
35
41
|
@pytest.fixture()
|
36
|
-
def
|
37
|
-
return mda.Universe(
|
42
|
+
def true_u(self, imd_u, topol_path_arg, traj_path_arg):
|
43
|
+
return mda.Universe(topol_path_arg, traj_path_arg)
|
38
44
|
|
39
45
|
@pytest.fixture()
|
40
|
-
def
|
41
|
-
|
42
|
-
|
43
|
-
|
46
|
+
def imd_u(self, topol_path_arg, tmp_path):
|
47
|
+
tmp_u = mda.Universe(topol_path_arg, "imd://localhost:8888")
|
48
|
+
with mda.Writer(
|
49
|
+
f"{tmp_path.as_posix()}/imd_test_traj.trr", tmp_u.atoms.n_atoms
|
50
|
+
) as w:
|
51
|
+
for ts in tmp_u.trajectory:
|
52
|
+
w.write(tmp_u.atoms)
|
53
|
+
imd_u = mda.Universe(
|
54
|
+
topol_path_arg, f"{tmp_path.as_posix()}/imd_test_traj.trr"
|
55
|
+
)
|
56
|
+
yield imd_u
|
44
57
|
|
45
|
-
def test_compare_imd_to_true_traj(self,
|
46
|
-
imdsinfo = client.get_imdsessioninfo()
|
58
|
+
def test_compare_imd_to_true_traj(self, true_u, imd_u, first_frame_arg):
|
47
59
|
|
48
|
-
for
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
60
|
+
for i in range(first_frame_arg, len(true_u.trajectory)):
|
61
|
+
assert_allclose(
|
62
|
+
true_u.trajectory[i].time, imd_u.trajectory[i].time, atol=1e-03
|
63
|
+
)
|
64
|
+
assert_allclose(
|
65
|
+
true_u.trajectory[i].data["step"],
|
66
|
+
imd_u.trajectory[i].data["step"],
|
67
|
+
)
|
68
|
+
if true_u.trajectory[i].dimensions is not None:
|
54
69
|
assert_allclose_with_logging(
|
55
|
-
|
56
|
-
|
70
|
+
true_u.trajectory[i].dimensions,
|
71
|
+
imd_u.trajectory[i].dimensions,
|
57
72
|
atol=1e-03,
|
58
73
|
)
|
59
|
-
if
|
74
|
+
if true_u.trajectory[i].has_positions:
|
60
75
|
assert_allclose_with_logging(
|
61
|
-
|
76
|
+
true_u.trajectory[i].positions,
|
77
|
+
imd_u.trajectory[i].positions,
|
78
|
+
atol=1e-03,
|
62
79
|
)
|
63
|
-
if
|
80
|
+
if true_u.trajectory[i].has_velocities:
|
64
81
|
assert_allclose_with_logging(
|
65
|
-
|
82
|
+
true_u.trajectory[i].velocities,
|
83
|
+
imd_u.trajectory[i].velocities,
|
84
|
+
atol=1e-03,
|
66
85
|
)
|
67
|
-
if
|
86
|
+
if true_u.trajectory[i].has_forces:
|
68
87
|
assert_allclose_with_logging(
|
69
|
-
|
88
|
+
true_u.trajectory[i].forces,
|
89
|
+
imd_u.trajectory[i].forces,
|
90
|
+
atol=1e-03,
|
70
91
|
)
|
@@ -38,7 +38,7 @@ class TestStreamAnalysis:
|
|
38
38
|
server.set_imdsessioninfo(imdsinfo)
|
39
39
|
server.handshake_sequence("localhost", port, first_frame=True)
|
40
40
|
|
41
|
-
imd_universe = mda.Universe(COORDINATES_TOPOLOGY, f"localhost:{port}")
|
41
|
+
imd_universe = mda.Universe(COORDINATES_TOPOLOGY, f"imd://localhost:{port}")
|
42
42
|
server.send_frames(1, 5)
|
43
43
|
|
44
44
|
yield imd_universe
|
imdclient/utils.py
CHANGED
@@ -43,22 +43,22 @@ class timeit(object):
|
|
43
43
|
# always propagate exceptions forward
|
44
44
|
return False
|
45
45
|
|
46
|
-
|
47
46
|
# NOTE: think of other edge cases as well- should be robust
|
48
47
|
def parse_host_port(filename):
|
48
|
+
if not filename.startswith("imd://"):
|
49
|
+
raise ValueError("IMDReader: URL must be in the format 'imd://host:port'")
|
50
|
+
|
49
51
|
# Check if the format is correct
|
50
|
-
parts = filename.split(":")
|
52
|
+
parts = filename.split("imd://")[1].split(":")
|
51
53
|
if len(parts) == 2:
|
52
|
-
host = parts[0]
|
54
|
+
host = parts[0]
|
53
55
|
try:
|
54
|
-
port = int(parts[1])
|
56
|
+
port = int(parts[1])
|
55
57
|
return (host, port)
|
56
58
|
except ValueError:
|
57
|
-
|
58
|
-
raise ValueError("Port must be an integer")
|
59
|
+
raise ValueError("IMDReader: Port must be an integer")
|
59
60
|
else:
|
60
|
-
|
61
|
-
raise ValueError("Filename must be in the format 'host:port'")
|
61
|
+
raise ValueError("IMDReader: URL must be in the format 'imd://host:port'")
|
62
62
|
|
63
63
|
|
64
64
|
def approximate_timestep_memsize(
|