imdclient 0.1.3__py3-none-any.whl → 0.2.0b0__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 +43 -12
- imdclient/IMDProtocol.py +1 -0
- imdclient/__init__.py +0 -5
- imdclient/data/gromacs/md/gromacs_v3_nst1.mdp +3 -3
- imdclient/data/namd/md/namd3 +0 -0
- imdclient/data/namd/md/namd_v3_nst_1.namd +1 -1
- imdclient/tests/base.py +108 -83
- imdclient/tests/conftest.py +0 -39
- imdclient/tests/datafiles.py +16 -1
- imdclient/tests/docker_testing/docker.md +1 -1
- imdclient/tests/hpc_testing/gromacs/README.md +112 -0
- imdclient/tests/hpc_testing/gromacs/gmx_gpu_test.mdp +58 -0
- imdclient/tests/hpc_testing/gromacs/gmx_gpu_test.top +11764 -0
- imdclient/tests/hpc_testing/gromacs/struct.gro +21151 -0
- imdclient/tests/hpc_testing/gromacs/validate_gmx.sh +90 -0
- imdclient/tests/hpc_testing/lammps/README.md +62 -0
- imdclient/tests/hpc_testing/lammps/lammps_v3_nst_1.in +71 -0
- imdclient/tests/hpc_testing/lammps/topology_after_min.data +8022 -0
- imdclient/tests/hpc_testing/lammps/validate_lmp.sh +66 -0
- imdclient/tests/hpc_testing/namd/README.md +147 -0
- imdclient/tests/hpc_testing/namd/alanin.params +402 -0
- imdclient/tests/hpc_testing/namd/alanin.pdb +77 -0
- imdclient/tests/hpc_testing/namd/alanin.psf +206 -0
- imdclient/tests/hpc_testing/namd/namd_v3_nst_1.namd +59 -0
- imdclient/tests/hpc_testing/namd/validate_namd.sh +71 -0
- imdclient/tests/minimalreader.py +86 -0
- imdclient/tests/server.py +6 -14
- imdclient/tests/test_gromacs.py +15 -3
- imdclient/tests/test_imdclient.py +26 -7
- imdclient/tests/test_lammps.py +22 -19
- imdclient/tests/test_manual.py +224 -66
- imdclient/tests/test_namd.py +39 -16
- imdclient/tests/test_utils.py +31 -0
- imdclient/utils.py +50 -17
- {imdclient-0.1.3.dist-info → imdclient-0.2.0b0.dist-info}/METADATA +60 -39
- imdclient-0.2.0b0.dist-info/RECORD +53 -0
- {imdclient-0.1.3.dist-info → imdclient-0.2.0b0.dist-info}/WHEEL +1 -1
- {imdclient-0.1.3.dist-info → imdclient-0.2.0b0.dist-info/licenses}/AUTHORS.md +4 -1
- {imdclient-0.1.3.dist-info → imdclient-0.2.0b0.dist-info/licenses}/LICENSE +3 -1
- imdclient/IMD.py +0 -130
- imdclient/backends.py +0 -352
- imdclient/results.py +0 -332
- imdclient/streamanalysis.py +0 -1056
- imdclient/streambase.py +0 -199
- imdclient/tests/test_imdreader.py +0 -658
- imdclient/tests/test_stream_analysis.py +0 -61
- imdclient-0.1.3.dist-info/RECORD +0 -42
- {imdclient-0.1.3.dist-info → imdclient-0.2.0b0.dist-info}/top_level.txt +0 -0
imdclient/tests/test_manual.py
CHANGED
@@ -1,14 +1,10 @@
|
|
1
|
-
from
|
2
|
-
import pytest
|
1
|
+
from .minimalreader import MinimalReader
|
3
2
|
import MDAnalysis as mda
|
4
|
-
from
|
5
|
-
from numpy.testing import (
|
6
|
-
assert_allclose,
|
7
|
-
)
|
3
|
+
from numpy.testing import assert_allclose
|
8
4
|
import numpy as np
|
9
|
-
from .base import assert_allclose_with_logging
|
10
5
|
from pathlib import Path
|
11
|
-
|
6
|
+
import time
|
7
|
+
import argparse
|
12
8
|
import logging
|
13
9
|
|
14
10
|
logger = logging.getLogger("imdclient.IMDClient")
|
@@ -20,74 +16,236 @@ file_handler.setFormatter(formatter)
|
|
20
16
|
logger.addHandler(file_handler)
|
21
17
|
logger.setLevel(logging.DEBUG)
|
22
18
|
|
19
|
+
"""
|
20
|
+
Tool for running IMDv3 integration tests via the command line.
|
23
21
|
|
24
|
-
|
25
|
-
|
26
|
-
Tool for running IMDv3 integration tests via the command line.
|
22
|
+
To use, start the simulation, wait for it to be ready for an IMD connection,
|
23
|
+
and then run this command relative to the root of the cloned respository:
|
27
24
|
|
28
|
-
|
29
|
-
|
25
|
+
python imdclient/tests/test_manual.py \
|
26
|
+
--topol_path <path/to/topology> \
|
27
|
+
--traj_path <path/to/trajectory> \
|
28
|
+
--first_frame <first frame to compare> \
|
29
|
+
--tmp_path <temporary directory for IMD trajectory>
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
Where the topology is the same topology as the IMD system, the trajectory is the path where
|
32
|
+
the trajectory of the running simulation is being written, and the first frame is the first frame of the
|
33
|
+
trajectory which should be compared to IMD data read from the socket (0 for GROMACS and NAMD, 1 for LAMMPS)
|
34
|
+
"""
|
35
|
+
|
36
|
+
|
37
|
+
def assert_allclose_with_logging(a, b, rtol=1e-07, atol=0, equal_nan=False):
|
38
|
+
"""
|
39
|
+
Custom function to compare two arrays element-wise, similar to np.testing.assert_allclose,
|
40
|
+
but logs all non-matching values.
|
35
41
|
|
36
|
-
|
37
|
-
|
38
|
-
|
42
|
+
Parameters:
|
43
|
+
a, b : array_like
|
44
|
+
Input arrays to compare.
|
45
|
+
rtol : float
|
46
|
+
Relative tolerance.
|
47
|
+
atol : float
|
48
|
+
Absolute tolerance.
|
49
|
+
equal_nan : bool
|
50
|
+
Whether to compare NaNs as equal.
|
39
51
|
"""
|
52
|
+
# Convert inputs to numpy arrays
|
53
|
+
a = np.asarray(a)
|
54
|
+
b = np.asarray(b)
|
55
|
+
|
56
|
+
# Compute the absolute difference
|
57
|
+
diff = np.abs(a - b)
|
58
|
+
|
59
|
+
# Check if values are within tolerance
|
60
|
+
not_close = diff > (atol + rtol * np.abs(b))
|
61
|
+
|
62
|
+
# Check if there are any NaNs and handle them if necessary
|
63
|
+
if equal_nan:
|
64
|
+
nan_mask = np.isnan(a) & np.isnan(b)
|
65
|
+
not_close &= ~nan_mask
|
66
|
+
|
67
|
+
# Log all the values that are not close
|
68
|
+
if np.any(not_close):
|
69
|
+
print("The following values do not match within tolerance:")
|
70
|
+
for idx in np.argwhere(not_close):
|
71
|
+
logger.debug(
|
72
|
+
f"a[{tuple(idx)}]: {a[tuple(idx)]}, b[{tuple(idx)}]: {b[tuple(idx)]}, diff: {diff[tuple(idx)]}"
|
73
|
+
)
|
74
|
+
# Optionally raise an error after logging if you want it to behave like assert
|
75
|
+
raise AssertionError("Arrays are not almost equal.")
|
76
|
+
else:
|
77
|
+
print("All values are within tolerance.")
|
78
|
+
|
40
79
|
|
41
|
-
|
42
|
-
|
43
|
-
return mda.Universe(
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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"
|
80
|
+
def load_true_universe(topol_path, traj_path):
|
81
|
+
if topol_path.endswith(".data"):
|
82
|
+
return mda.Universe(
|
83
|
+
topol_path,
|
84
|
+
traj_path,
|
85
|
+
atom_style="id type x y z",
|
86
|
+
convert_units=False,
|
55
87
|
)
|
56
|
-
|
88
|
+
return mda.Universe(topol_path, traj_path)
|
57
89
|
|
58
|
-
def test_compare_imd_to_true_traj(self, true_u, imd_u, first_frame_arg):
|
59
90
|
|
60
|
-
|
91
|
+
def load_imd_universe(topol_path, tmp_path):
|
92
|
+
# Pass atom_style (ignored if not using LAMMPS topol)
|
93
|
+
n_atoms = mda.Universe(
|
94
|
+
topol_path,
|
95
|
+
atom_style="id type x y z",
|
96
|
+
convert_units=False,
|
97
|
+
).atoms.n_atoms
|
98
|
+
tmp_u = MinimalReader(
|
99
|
+
f"imd://localhost:8888", n_atoms=n_atoms, process_stream=True
|
100
|
+
)
|
101
|
+
return tmp_u
|
102
|
+
|
103
|
+
|
104
|
+
def test_compare_imd_to_true_traj_vel(imd_u, true_u_vel, first_frame):
|
105
|
+
for i in range(first_frame, len(true_u_vel.trajectory)):
|
106
|
+
# Manually convert unit
|
107
|
+
assert_allclose_with_logging(
|
108
|
+
true_u_vel.trajectory[i].positions * 20.45482706,
|
109
|
+
imd_u.trajectory[i - first_frame].velocities,
|
110
|
+
atol=1e-03,
|
111
|
+
)
|
112
|
+
|
113
|
+
|
114
|
+
def test_compare_imd_to_true_traj_forces(imd_u, true_u_force, first_frame):
|
115
|
+
for i in range(first_frame, len(true_u_force.trajectory)):
|
116
|
+
assert_allclose_with_logging(
|
117
|
+
true_u_force.trajectory[i].positions,
|
118
|
+
imd_u.trajectory[i - first_frame].forces,
|
119
|
+
atol=1e-03,
|
120
|
+
)
|
121
|
+
|
122
|
+
|
123
|
+
def test_compare_imd_to_true_traj(
|
124
|
+
imd_u, true_u, first_frame, vel, force, dt, step
|
125
|
+
):
|
126
|
+
for i in range(first_frame, len(true_u.trajectory)):
|
127
|
+
assert_allclose(
|
128
|
+
true_u.trajectory[i].time,
|
129
|
+
imd_u.trajectory[i - first_frame].time,
|
130
|
+
atol=1e-03,
|
131
|
+
)
|
132
|
+
# Issue #63
|
133
|
+
# if dt:
|
134
|
+
# assert_allclose(
|
135
|
+
# true_u.trajectory[i].dt,
|
136
|
+
# imd_u.trajectory[i - first_frame].dt,
|
137
|
+
# atol=1e-03,
|
138
|
+
# )
|
139
|
+
if step:
|
61
140
|
assert_allclose(
|
62
|
-
true_u.trajectory[i].
|
63
|
-
imd_u.trajectory[i -
|
141
|
+
true_u.trajectory[i].data["step"],
|
142
|
+
imd_u.trajectory[i - first_frame].data["step"],
|
143
|
+
)
|
144
|
+
assert_allclose_with_logging(
|
145
|
+
true_u.trajectory[i].dimensions,
|
146
|
+
imd_u.trajectory[i - first_frame].dimensions,
|
147
|
+
atol=1e-03,
|
148
|
+
)
|
149
|
+
assert_allclose_with_logging(
|
150
|
+
true_u.trajectory[i].positions,
|
151
|
+
imd_u.trajectory[i - first_frame].positions,
|
152
|
+
atol=1e-03,
|
153
|
+
)
|
154
|
+
if vel:
|
155
|
+
assert_allclose_with_logging(
|
156
|
+
true_u.trajectory[i].velocities,
|
157
|
+
imd_u.trajectory[i - first_frame].velocities,
|
64
158
|
atol=1e-03,
|
65
159
|
)
|
66
|
-
|
67
|
-
|
68
|
-
|
160
|
+
if force:
|
161
|
+
assert_allclose_with_logging(
|
162
|
+
true_u.trajectory[i].forces,
|
163
|
+
imd_u.trajectory[i - first_frame].forces,
|
164
|
+
atol=1e-03,
|
165
|
+
)
|
166
|
+
|
167
|
+
|
168
|
+
def main():
|
169
|
+
parser = argparse.ArgumentParser(description="IMDv3 Integration Test Tool")
|
170
|
+
parser.add_argument(
|
171
|
+
"--topol_path", required=True, help="Path to topology file"
|
172
|
+
)
|
173
|
+
parser.add_argument(
|
174
|
+
"--traj_path", required=True, help="Path to trajectory file"
|
175
|
+
)
|
176
|
+
parser.add_argument(
|
177
|
+
"--vel_path",
|
178
|
+
required=False,
|
179
|
+
help="Path to velocities trajectory file (NAMD only)",
|
180
|
+
)
|
181
|
+
parser.add_argument(
|
182
|
+
"--force_path",
|
183
|
+
required=False,
|
184
|
+
help="Path to forces trajectory file (NAMD only)",
|
185
|
+
)
|
186
|
+
parser.add_argument(
|
187
|
+
"--first_frame",
|
188
|
+
type=int,
|
189
|
+
required=True,
|
190
|
+
help="First frame to compare (0 for GROMACS and NAMD, 1 for LAMMPS)",
|
191
|
+
)
|
192
|
+
parser.add_argument(
|
193
|
+
"--tmp_path", default=".", help="Temporary directory for IMD traj"
|
194
|
+
)
|
195
|
+
|
196
|
+
args = parser.parse_args()
|
197
|
+
|
198
|
+
print(
|
199
|
+
"Writing IMD trajectory to temporary directory...\n===================="
|
200
|
+
)
|
201
|
+
imd_u = load_imd_universe(args.topol_path, args.tmp_path)
|
202
|
+
|
203
|
+
print("Loading source of truth trajectory...\n====================")
|
204
|
+
true_u = load_true_universe(args.topol_path, args.traj_path)
|
205
|
+
|
206
|
+
try:
|
207
|
+
print("Comparing trajectories...\n====================")
|
208
|
+
vel_in_trr = args.vel_path is None
|
209
|
+
force_in_trr = args.force_path is None
|
210
|
+
dt_in_trr = not args.topol_path.endswith(".data")
|
211
|
+
# True when not using DCDReader
|
212
|
+
step_in_trr = not args.traj_path.endswith(".coor")
|
213
|
+
|
214
|
+
test_compare_imd_to_true_traj(
|
215
|
+
imd_u,
|
216
|
+
true_u,
|
217
|
+
args.first_frame,
|
218
|
+
vel_in_trr,
|
219
|
+
force_in_trr,
|
220
|
+
dt_in_trr,
|
221
|
+
step_in_trr,
|
222
|
+
)
|
223
|
+
|
224
|
+
if args.vel_path is not None:
|
225
|
+
print(
|
226
|
+
"Loading source of truth velocity trajectory...\n===================="
|
227
|
+
)
|
228
|
+
true_vel = load_true_universe(args.topol_path, args.vel_path)
|
229
|
+
print("Comparing velocities...")
|
230
|
+
test_compare_imd_to_true_traj_vel(imd_u, true_vel, args.first_frame)
|
231
|
+
|
232
|
+
if args.force_path is not None:
|
233
|
+
print(
|
234
|
+
"Loading source of truth force trajectory...\n===================="
|
69
235
|
)
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
atol=1e-03,
|
87
|
-
)
|
88
|
-
if true_u.trajectory[i].has_forces:
|
89
|
-
assert_allclose_with_logging(
|
90
|
-
true_u.trajectory[i].forces,
|
91
|
-
imd_u.trajectory[i - first_frame_arg].forces,
|
92
|
-
atol=1e-03,
|
93
|
-
)
|
236
|
+
true_force = load_true_universe(args.topol_path, args.force_path)
|
237
|
+
print("Comparing forces...")
|
238
|
+
test_compare_imd_to_true_traj_forces(
|
239
|
+
imd_u, true_force, args.first_frame
|
240
|
+
)
|
241
|
+
|
242
|
+
print("All tests passed!")
|
243
|
+
|
244
|
+
except AssertionError as e:
|
245
|
+
logger.error("Comparison failed!")
|
246
|
+
print(f"Test failed: {e}")
|
247
|
+
raise e
|
248
|
+
|
249
|
+
|
250
|
+
if __name__ == "__main__":
|
251
|
+
main()
|
imdclient/tests/test_namd.py
CHANGED
@@ -1,6 +1,13 @@
|
|
1
|
-
import MDAnalysis as mda
|
2
|
-
import pytest
|
3
1
|
import logging
|
2
|
+
from pathlib import Path
|
3
|
+
import re
|
4
|
+
|
5
|
+
import pytest
|
6
|
+
from numpy.testing import (
|
7
|
+
assert_allclose,
|
8
|
+
)
|
9
|
+
import MDAnalysis as mda
|
10
|
+
|
4
11
|
from .base import IMDv3IntegrationTest, assert_allclose_with_logging
|
5
12
|
from .datafiles import (
|
6
13
|
NAMD_TOPOL,
|
@@ -9,10 +16,6 @@ from .datafiles import (
|
|
9
16
|
NAMD_PARAMS,
|
10
17
|
NAMD_PSF,
|
11
18
|
)
|
12
|
-
from pathlib import Path
|
13
|
-
from numpy.testing import (
|
14
|
-
assert_allclose,
|
15
|
-
)
|
16
19
|
|
17
20
|
logger = logging.getLogger("imdclient.IMDClient")
|
18
21
|
file_handler = logging.FileHandler("namd_test.log")
|
@@ -26,6 +29,10 @@ logger.setLevel(logging.DEBUG)
|
|
26
29
|
|
27
30
|
class TestIMDv3NAMD(IMDv3IntegrationTest):
|
28
31
|
|
32
|
+
@pytest.fixture()
|
33
|
+
def container_name(self):
|
34
|
+
return "ghcr.io/becksteinlab/streaming-namd-docker:main-common-cpu"
|
35
|
+
|
29
36
|
@pytest.fixture(params=[NAMD_CONF_NST_1, NAMD_CONF_NST_8])
|
30
37
|
def inp(self, request):
|
31
38
|
return request.param
|
@@ -42,6 +49,18 @@ class TestIMDv3NAMD(IMDv3IntegrationTest):
|
|
42
49
|
def topol(self):
|
43
50
|
return Path(NAMD_TOPOL).name
|
44
51
|
|
52
|
+
@pytest.fixture()
|
53
|
+
def dt(self, inp):
|
54
|
+
pattern = re.compile(r"^\s*timestep\s*(\S+)")
|
55
|
+
with open(inp, "r") as file:
|
56
|
+
for line in file:
|
57
|
+
match = pattern.match(line)
|
58
|
+
if match:
|
59
|
+
# NAMD timestep is in femtoseconds, convert to picoseconds
|
60
|
+
# as IMDv3 expects dt in ps, 1fs = 0.001ps
|
61
|
+
return float(match.group(1)) * 0.001
|
62
|
+
raise ValueError(f"No dt found in {inp}")
|
63
|
+
|
45
64
|
@pytest.fixture()
|
46
65
|
def true_u(self, topol, imd_u, tmp_path):
|
47
66
|
u = mda.Universe(
|
@@ -75,40 +94,43 @@ class TestIMDv3NAMD(IMDv3IntegrationTest):
|
|
75
94
|
return 0
|
76
95
|
|
77
96
|
# Compare coords, box, time, dt, step
|
78
|
-
def test_compare_imd_to_true_traj(self, imd_u, true_u, first_frame):
|
97
|
+
def test_compare_imd_to_true_traj(self, imd_u, true_u, first_frame, dt):
|
79
98
|
for i in range(first_frame, len(true_u.trajectory)):
|
99
|
+
|
80
100
|
assert_allclose(
|
81
101
|
true_u.trajectory[i].time,
|
82
102
|
imd_u.trajectory[i - first_frame].time,
|
83
103
|
atol=1e-03,
|
84
104
|
)
|
105
|
+
|
85
106
|
assert_allclose(
|
86
|
-
|
107
|
+
dt,
|
87
108
|
imd_u.trajectory[i - first_frame].dt,
|
88
109
|
atol=1e-03,
|
89
110
|
)
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
)
|
111
|
+
# step in DCDReader is frame index, not integration step
|
112
|
+
# don't compare step
|
113
|
+
|
94
114
|
assert_allclose_with_logging(
|
95
115
|
true_u.trajectory[i].dimensions,
|
96
116
|
imd_u.trajectory[i - first_frame].dimensions,
|
97
117
|
atol=1e-03,
|
98
118
|
)
|
119
|
+
|
99
120
|
assert_allclose_with_logging(
|
100
121
|
true_u.trajectory[i].positions,
|
101
122
|
imd_u.trajectory[i - first_frame].positions,
|
102
123
|
atol=1e-03,
|
103
124
|
)
|
104
125
|
|
126
|
+
# Since NAMD does not write velocities, forces to the DCD file, we need to do so seperately by extracting that info from their respective DCD files
|
105
127
|
# Compare velocities
|
106
|
-
def test_compare_imd_to_true_traj_vel(
|
107
|
-
self, imd_u, true_u_vel, first_frame
|
108
|
-
):
|
128
|
+
def test_compare_imd_to_true_traj_vel(self, imd_u, true_u_vel, first_frame):
|
109
129
|
for i in range(first_frame, len(true_u_vel.trajectory)):
|
130
|
+
|
110
131
|
assert_allclose_with_logging(
|
111
|
-
|
132
|
+
# Unit conversion
|
133
|
+
true_u_vel.trajectory[i].positions * 20.45482706,
|
112
134
|
imd_u.trajectory[i - first_frame].velocities,
|
113
135
|
atol=1e-03,
|
114
136
|
)
|
@@ -118,6 +140,7 @@ class TestIMDv3NAMD(IMDv3IntegrationTest):
|
|
118
140
|
self, imd_u, true_u_force, first_frame
|
119
141
|
):
|
120
142
|
for i in range(first_frame, len(true_u_force.trajectory)):
|
143
|
+
|
121
144
|
assert_allclose_with_logging(
|
122
145
|
true_u_force.trajectory[i].positions,
|
123
146
|
imd_u.trajectory[i - first_frame].forces,
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# tests/test_utils.py
|
2
|
+
import pytest
|
3
|
+
from imdclient.utils import parse_host_port
|
4
|
+
|
5
|
+
|
6
|
+
@pytest.mark.parametrize(
|
7
|
+
"server_address,host_port",
|
8
|
+
[
|
9
|
+
("imd://localhost:8888", ("localhost", 8888)),
|
10
|
+
("imd://example.com:12345", ("example.com", 12345)),
|
11
|
+
],
|
12
|
+
)
|
13
|
+
def test_parse_host_port_valid(server_address, host_port):
|
14
|
+
assert parse_host_port(server_address) == host_port
|
15
|
+
|
16
|
+
|
17
|
+
@pytest.mark.parametrize(
|
18
|
+
"server_address",
|
19
|
+
[
|
20
|
+
"", # empty
|
21
|
+
"http://localhost:80", # wrong protocol prefix
|
22
|
+
"imd://", # missing host and port
|
23
|
+
"imd://localhost:", # missing port
|
24
|
+
"imd://:8080", # missing host
|
25
|
+
"imd://host:notaport", # port not integer
|
26
|
+
"imd://host:80:90", # too many segments
|
27
|
+
],
|
28
|
+
)
|
29
|
+
def test_parse_host_port_invalid(server_address):
|
30
|
+
with pytest.raises(ValueError):
|
31
|
+
parse_host_port(server_address)
|
imdclient/utils.py
CHANGED
@@ -43,23 +43,6 @@ class timeit(object):
|
|
43
43
|
# always propagate exceptions forward
|
44
44
|
return False
|
45
45
|
|
46
|
-
# NOTE: think of other edge cases as well- should be robust
|
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
|
-
|
51
|
-
# Check if the format is correct
|
52
|
-
parts = filename.split("imd://")[1].split(":")
|
53
|
-
if len(parts) == 2:
|
54
|
-
host = parts[0]
|
55
|
-
try:
|
56
|
-
port = int(parts[1])
|
57
|
-
return (host, port)
|
58
|
-
except ValueError:
|
59
|
-
raise ValueError("IMDReader: Port must be an integer")
|
60
|
-
else:
|
61
|
-
raise ValueError("IMDReader: URL must be in the format 'imd://host:port'")
|
62
|
-
|
63
46
|
|
64
47
|
def approximate_timestep_memsize(
|
65
48
|
n_atoms, energies, dimensions, positions, velocities, forces
|
@@ -116,3 +99,53 @@ def sock_contains_data(sock, timeout) -> bool:
|
|
116
99
|
[sock], [], [], timeout
|
117
100
|
)
|
118
101
|
return sock in ready_to_read
|
102
|
+
|
103
|
+
|
104
|
+
def parse_host_port(filename):
|
105
|
+
"""
|
106
|
+
Parses a URL in the format 'imd://host:port' and returns the host and port.
|
107
|
+
Parameters
|
108
|
+
----------
|
109
|
+
filename : str
|
110
|
+
The URL to parse, must be in the format 'imd://host:port'.
|
111
|
+
|
112
|
+
Returns
|
113
|
+
-------
|
114
|
+
tuple[str, int]
|
115
|
+
A 2-tuple ``(host, port)`` where `host` is the host server name
|
116
|
+
and `port` is the TCP port number.
|
117
|
+
Raises
|
118
|
+
------
|
119
|
+
ValueError
|
120
|
+
If the URL is not in the correct format or if the host or port is invalid.
|
121
|
+
|
122
|
+
Examples
|
123
|
+
--------
|
124
|
+
>>> parse_host_port("imd://localhost:8888")
|
125
|
+
('localhost', 8888)
|
126
|
+
>>> parse_host_port("invalid://localhost:12345")
|
127
|
+
Traceback (most recent call last):
|
128
|
+
... ValueError: IMDClient: URL must be in the format 'imd://host:port'
|
129
|
+
"""
|
130
|
+
if not filename.startswith("imd://"):
|
131
|
+
raise ValueError(
|
132
|
+
"IMDClient: URL must be in the format 'imd://host:port'"
|
133
|
+
)
|
134
|
+
|
135
|
+
# Check if the format is correct
|
136
|
+
parts = filename.split("imd://")[1].split(":")
|
137
|
+
if len(parts) == 2:
|
138
|
+
host = parts[0]
|
139
|
+
if not host:
|
140
|
+
raise ValueError("IMDClient: Host cannot be empty")
|
141
|
+
if not parts[1]:
|
142
|
+
raise ValueError("IMDClient: Port cannot be empty")
|
143
|
+
try:
|
144
|
+
port = int(parts[1])
|
145
|
+
return (host, port)
|
146
|
+
except ValueError:
|
147
|
+
raise ValueError("IMDClient: Port must be an integer")
|
148
|
+
else:
|
149
|
+
raise ValueError(
|
150
|
+
"IMDClient: URL must be in the format 'imd://host:port'"
|
151
|
+
)
|