imdclient 0.1.4__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 CHANGED
@@ -1,18 +1,27 @@
1
1
  """
2
-
3
- IMDClient
4
- ^^^^^^^^^
2
+ IMDClient module
3
+ ================
5
4
 
6
5
  .. autoclass:: IMDClient
7
6
  :members:
8
7
 
8
+ .. autoclass:: BaseIMDProducer
9
+ :members:
10
+ :inherited-members:
11
+
12
+ .. autoclass:: IMDProducerV2
13
+ :members:
14
+ :inherited-members:
15
+
9
16
  .. autoclass:: IMDProducerV3
10
17
  :members:
11
18
  :inherited-members:
12
19
 
13
20
  .. autoclass:: IMDFrameBuffer
14
21
  :members:
15
-
22
+
23
+ .. autoclass:: IMDFrame
24
+ :members:
16
25
  """
17
26
 
18
27
  import socket
@@ -43,7 +52,7 @@ class IMDClient:
43
52
  socket_bufsize : int, (optional)
44
53
  Size of the socket buffer in bytes. Default is to use the system default
45
54
  buffer_size : int (optional)
46
- IMDFramebuffer will be filled with as many :class:`IMDFrame` fit in `buffer_size` bytes [``10MB``]
55
+ :class:`IMDFrameBuffer` will be filled with as many :class:`IMDFrame` fit in `buffer_size` bytes [``10MB``]
47
56
  timeout : int, optional
48
57
  Timeout for the socket in seconds [``5``]
49
58
  continue_after_disconnect : bool, optional [``None``]
@@ -138,10 +147,11 @@ class IMDClient:
138
147
 
139
148
  self._producer.start()
140
149
 
141
- def signal_handler(self, sig, frame):
142
- """Catch SIGINT to allow clean shutdown on CTRL+C
150
+ def signal_handler(self):
151
+ """Catch SIGINT to allow clean shutdown on CTRL+C.
152
+
143
153
  This also ensures that main thread execution doesn't get stuck
144
- waiting in buf.pop_full_imdframe()"""
154
+ waiting in ``buf.pop_full_imdframe()``"""
145
155
  logger.debug("Intercepted signal")
146
156
  self.stop()
147
157
  logger.debug("Shutdown success")
@@ -207,9 +217,7 @@ class IMDClient:
207
217
  # /proc/sys/net/core/rmem_default
208
218
  # Max (linux):
209
219
  # /proc/sys/net/core/rmem_max
210
- conn.setsockopt(
211
- socket.SOL_SOCKET, socket.SO_RCVBUF, socket_bufsize
212
- )
220
+ conn.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, socket_bufsize)
213
221
  try:
214
222
  logger.debug(f"IMDClient: Connecting to {host}:{port}")
215
223
  conn.connect((host, port))
@@ -307,8 +315,8 @@ class IMDClient:
307
315
  )
308
316
  self._conn.sendall(wait_packet)
309
317
  logger.debug(
310
- "IMDClient: Attempted to change wait behavior to %s",
311
- not self._continue_after_disconnect
318
+ "IMDClient: Attempted to change wait behavior to %s",
319
+ not self._continue_after_disconnect,
312
320
  )
313
321
 
314
322
  def _disconnect(self):
@@ -785,9 +793,7 @@ class IMDFrameBuffer:
785
793
  raise ValueError("pause_empty_proportion must be between 0 and 1")
786
794
  self._pause_empty_proportion = pause_empty_proportion
787
795
  if unpause_empty_proportion < 0 or unpause_empty_proportion > 1:
788
- raise ValueError(
789
- "unpause_empty_proportion must be between 0 and 1"
790
- )
796
+ raise ValueError("unpause_empty_proportion must be between 0 and 1")
791
797
  self._unpause_empty_proportion = unpause_empty_proportion
792
798
 
793
799
  if buffer_size <= 0:
@@ -854,9 +860,7 @@ class IMDFrameBuffer:
854
860
  logger.debug("IMDProducer: Noticing consumer finished")
855
861
  raise EOFError
856
862
  except Exception as e:
857
- logger.debug(
858
- f"IMDProducer: Error waiting for space in buffer: {e}"
859
- )
863
+ logger.debug(f"IMDProducer: Error waiting for space in buffer: {e}")
860
864
 
861
865
  def pop_empty_imdframe(self):
862
866
  logger.debug("IMDProducer: Getting empty frame")
@@ -902,9 +906,7 @@ class IMDFrameBuffer:
902
906
  imdf = self._full_q.get()
903
907
  else:
904
908
  with self._full_imdf_avail:
905
- while (
906
- self._full_q.qsize() == 0 and not self._producer_finished
907
- ):
909
+ while self._full_q.qsize() == 0 and not self._producer_finished:
908
910
  self._full_imdf_avail.wait()
909
911
 
910
912
  if self._producer_finished and self._full_q.qsize() == 0:
imdclient/__init__.py CHANGED
@@ -6,9 +6,4 @@ IMDClient
6
6
  from .IMDClient import IMDClient
7
7
  from importlib.metadata import version
8
8
 
9
- from .streamanalysis import AnalysisBase, StackableAnalysis
10
- from MDAnalysis.analysis import base
11
-
12
- base.AnalysisBase = AnalysisBase
13
-
14
9
  __version__ = version("imdclient")
Binary file
@@ -1,7 +1,7 @@
1
1
  # This is a test namd configuration file
2
2
 
3
3
  timestep 0.5
4
- numsteps 10
4
+ numsteps 100
5
5
  structure alanin.psf
6
6
  parameters alanin.params
7
7
  coordinates alanin.pdb
imdclient/tests/base.py CHANGED
@@ -1,20 +1,17 @@
1
- from imdclient.IMDClient import IMDClient
2
- from imdclient.IMD import IMDReader
3
- import pytest
4
- from pathlib import Path
5
- import os
6
- import signal
7
- import subprocess
8
1
  import time
2
+ import logging
3
+ import shutil
4
+
5
+ import pytest
6
+ import numpy as np
9
7
  from numpy.testing import (
10
8
  assert_allclose,
11
9
  )
12
- import numpy as np
13
10
  import docker
14
- import logging
15
- import shutil
16
11
  import MDAnalysis as mda
12
+
17
13
  from .utils import get_free_port
14
+ from .minimalreader import MinimalReader
18
15
 
19
16
  logger = logging.getLogger("imdclient.IMDClient")
20
17
 
@@ -64,6 +61,10 @@ def assert_allclose_with_logging(a, b, rtol=1e-07, atol=0, equal_nan=False):
64
61
 
65
62
  class IMDv3IntegrationTest:
66
63
 
64
+ @pytest.fixture()
65
+ def container_name(self):
66
+ return "ghcr.io/becksteinlab/streaming-md-docker:main-common-cpu"
67
+
67
68
  @pytest.fixture()
68
69
  def setup_command(self):
69
70
  return None
@@ -80,12 +81,13 @@ class IMDv3IntegrationTest:
80
81
  setup_command,
81
82
  simulation_command,
82
83
  port,
84
+ container_name,
83
85
  ):
84
86
  # In CI, container process needs access to tmp_path
85
87
  tmp_path.chmod(0o777)
86
88
  docker_client = docker.from_env()
87
89
  img = docker_client.images.pull(
88
- "ghcr.io/becksteinlab/streaming-md-docker:main-Common-CPU"
90
+ container_name,
89
91
  )
90
92
  # Copy input files into tmp_path
91
93
  for inp in input_files:
@@ -123,13 +125,11 @@ class IMDv3IntegrationTest:
123
125
 
124
126
  @pytest.fixture()
125
127
  def imd_u(self, docker_client, topol, tmp_path, port):
126
- u = mda.Universe((tmp_path / topol), f"imd://localhost:{port}")
127
- with mda.Writer(
128
- (tmp_path / "imd.trr").as_posix(), u.trajectory.n_atoms
129
- ) as w:
130
- for ts in u.trajectory:
131
- w.write(u.atoms)
132
- yield mda.Universe((tmp_path / topol), (tmp_path / "imd.trr"))
128
+ n_atoms = mda.Universe(tmp_path / topol).atoms.n_atoms
129
+ u = MinimalReader(
130
+ f"imd://localhost:{port}", n_atoms=n_atoms, process_stream=True
131
+ )
132
+ yield u
133
133
 
134
134
  @pytest.fixture()
135
135
  def true_u(self, topol, traj, imd_u, tmp_path):
@@ -139,118 +139,97 @@ class IMDv3IntegrationTest:
139
139
  )
140
140
  yield u
141
141
 
142
- @pytest.fixture()
143
- def comp_time(self):
144
- return True
142
+ def test_compare_imd_to_true_traj(self, imd_u, true_u, first_frame, dt):
143
+ for i in range(first_frame, len(true_u.trajectory)):
145
144
 
146
- @pytest.fixture()
147
- def comp_dt(self):
148
- return True
145
+ assert_allclose(
146
+ true_u.trajectory[i].time,
147
+ imd_u.trajectory[i - first_frame].time,
148
+ atol=1e-03,
149
+ )
149
150
 
150
- @pytest.fixture()
151
- def comp_step(self):
152
- return True
151
+ assert_allclose(
152
+ dt,
153
+ imd_u.trajectory[i - first_frame].dt,
154
+ atol=1e-03,
155
+ )
153
156
 
154
- def test_compare_imd_to_true_traj(
155
- self, imd_u, true_u, first_frame, comp_time, comp_dt, comp_step
156
- ):
157
- for i in range(first_frame, len(true_u.trajectory)):
158
- if comp_time:
159
- assert_allclose(
160
- true_u.trajectory[i].time,
161
- imd_u.trajectory[i - first_frame].time,
162
- atol=1e-03,
163
- )
164
- if comp_dt:
165
- assert_allclose(
166
- true_u.trajectory[i].dt,
167
- imd_u.trajectory[i - first_frame].dt,
168
- atol=1e-03,
169
- )
170
- if comp_step:
171
- assert_allclose(
172
- true_u.trajectory[i].data["step"],
173
- imd_u.trajectory[i - first_frame].data["step"],
174
- )
175
- if (
176
- true_u.trajectory[i].dimensions is not None
177
- and imd_u.trajectory[i - first_frame].dimensions is not None
178
- ):
179
- assert_allclose_with_logging(
180
- true_u.trajectory[i].dimensions,
181
- imd_u.trajectory[i - first_frame].dimensions,
182
- atol=1e-03,
183
- )
184
- if (
185
- true_u.trajectory[i].has_positions
186
- and imd_u.trajectory[i - first_frame].has_positions
187
- ):
188
- assert_allclose_with_logging(
189
- true_u.trajectory[i].positions,
190
- imd_u.trajectory[i - first_frame].positions,
191
- atol=1e-03,
192
- )
193
- if (
194
- true_u.trajectory[i].has_velocities
195
- and imd_u.trajectory[i - first_frame].has_velocities
196
- ):
197
- assert_allclose_with_logging(
198
- true_u.trajectory[i].velocities,
199
- imd_u.trajectory[i - first_frame].velocities,
200
- atol=1e-03,
201
- )
202
- if (
203
- true_u.trajectory[i].has_forces
204
- and imd_u.trajectory[i - first_frame].has_forces
205
- ):
206
- assert_allclose_with_logging(
207
- true_u.trajectory[i].forces,
208
- imd_u.trajectory[i - first_frame].forces,
209
- atol=1e-03,
210
- )
157
+ assert_allclose(
158
+ true_u.trajectory[i].data["step"],
159
+ imd_u.trajectory[i - first_frame].step,
160
+ )
161
+
162
+ assert_allclose_with_logging(
163
+ true_u.trajectory[i].dimensions,
164
+ imd_u.trajectory[i - first_frame].dimensions,
165
+ atol=1e-03,
166
+ )
167
+
168
+ assert_allclose_with_logging(
169
+ true_u.trajectory[i].positions,
170
+ imd_u.trajectory[i - first_frame].positions,
171
+ atol=1e-03,
172
+ )
173
+
174
+ assert_allclose_with_logging(
175
+ true_u.trajectory[i].velocities,
176
+ imd_u.trajectory[i - first_frame].velocities,
177
+ atol=1e-03,
178
+ )
179
+
180
+ assert_allclose_with_logging(
181
+ true_u.trajectory[i].forces,
182
+ imd_u.trajectory[i - first_frame].forces,
183
+ atol=1e-03,
184
+ )
211
185
 
212
186
  def test_continue_after_disconnect(
213
187
  self, docker_client, topol, tmp_path, port
214
188
  ):
215
- u = mda.Universe(
216
- (tmp_path / topol),
217
- f"imd://localhost:{port}",
218
- continue_after_disconnect=True,
189
+ n_atoms = mda.Universe(
190
+ tmp_path / topol,
219
191
  # Make sure LAMMPS topol can be read
220
192
  # Does nothing if not LAMMPS
221
193
  atom_style="id type x y z",
194
+ ).atoms.n_atoms
195
+ u = MinimalReader(
196
+ f"imd://localhost:{port}",
197
+ n_atoms=n_atoms,
198
+ continue_after_disconnect=True,
222
199
  )
223
200
  # Though we disconnect here, the simulation should continue
224
- u.trajectory.close()
201
+ u.close()
225
202
  # Wait for the simulation to finish running
226
203
  time.sleep(45)
227
204
 
228
205
  # Now, attempt to reconnect- should fail,
229
206
  # since the simulation should have continued
230
207
  with pytest.raises(IOError):
231
- u = mda.Universe(
232
- (tmp_path / topol),
233
- f"imd://localhost:{port}",
208
+ n_atoms = mda.Universe(
209
+ tmp_path / topol,
234
210
  atom_style="id type x y z",
235
- )
211
+ ).atoms.n_atoms
212
+ u = MinimalReader(f"imd://localhost:{port}", n_atoms=n_atoms)
236
213
 
237
214
  def test_wait_after_disconnect(self, docker_client, topol, tmp_path, port):
238
- u = mda.Universe(
239
- (tmp_path / topol),
240
- f"imd://localhost:{port}",
241
- # Could also use None here- just being explicit
242
- continue_after_disconnect=False,
215
+ n_atoms = mda.Universe(
216
+ tmp_path / topol,
243
217
  # Make sure LAMMPS topol can be read
244
218
  # Does nothing if not LAMMPS
245
219
  atom_style="id type x y z",
220
+ ).atoms.n_atoms
221
+ u = MinimalReader(
222
+ f"imd://localhost:{port}",
223
+ n_atoms=n_atoms,
224
+ continue_after_disconnect=False,
246
225
  )
247
- u.trajectory.close()
226
+ u.close()
248
227
  # Give the simulation engine
249
228
  # enough time to finish running (though it shouldn't)
250
229
  time.sleep(45)
251
230
 
252
- u = mda.Universe(
253
- (tmp_path / topol),
254
- f"imd://localhost:{port}",
231
+ n_atoms = mda.Universe(
232
+ tmp_path / topol,
255
233
  atom_style="id type x y z",
256
- )
234
+ ).atoms.n_atoms
235
+ u = MinimalReader(f"imd://localhost:{port}", n_atoms=n_atoms)
@@ -14,7 +14,7 @@ __all__ = [
14
14
  "LAMMPS_IN_NST_8",
15
15
  "GROMACS_TRAJ",
16
16
  "GROMACS_MDP",
17
- "GROMACS_TOP" "LAMMPS_IN_NST_1",
17
+ "GROMACS_TOP",
18
18
  "GROMACS_GRO",
19
19
  "GROMACS_MDP_NST_1",
20
20
  "GROMACS_MDP_NST_8",
@@ -5,7 +5,7 @@ Ensure [docker](https://www.docker.com/) and the [NVIDIA container toolkit](http
5
5
 
6
6
  To run the container:
7
7
  ```bash
8
- docker pull ghcr.io/becksteinlab/streaming-md-docker:main-Common-GPU
8
+ docker pull ghcr.io/becksteinlab/streaming-md-docker:main-common-gpu
9
9
 
10
10
  docker run -v $PWD/imdclient/data:/home/conda:rw -it --runtime=nvidia --gpus=all \
11
11
  ghcr.io/becksteinlab/streaming-md-docker:main-Common-GPU
@@ -40,7 +40,7 @@ Or, for MPI builds,
40
40
  To validate against your own simulation files, see `validate_lmp.sh` for
41
41
  command line arguments.
42
42
 
43
- ### Compiling on ASU's Sol supercomputer
43
+ ### Compiling on ASU's Sol supercomputer with MPI and GPU support
44
44
 
45
45
  Allocate a GPU node on SOL and clone in https://github.com/ljwoods2/lammps/tree/imd-v3
46
46
 
@@ -57,6 +57,6 @@ module load openmpi/4.1.5
57
57
  mkdir -p build_gpu
58
58
  cd build_gpu
59
59
 
60
- cmake ../cmake/ -D PKG_MISC=yes -D PKG_GPU=on -D GPU_API=cuda -D PKG_H5MD=yes -D BUILD_MPI=yes -D LAMMPS_ASYNC_IMD=yes
60
+ cmake ../cmake/ -D PKG_MISC=yes -D PKG_GPU=on -D GPU_API=cuda -D PKG_H5MD=yes -D BUILD_MPI=yes -DCMAKE_CXX_FLAGS="-DLAMMPS_ASYNC_IMD"
61
61
  make -j 4
62
62
  ```
@@ -35,39 +35,113 @@ command line arguments.
35
35
 
36
36
  ### Compiling on ASU's Sol supercomputer
37
37
 
38
- Allocate a GPU node on SOL and clone in https://gitlab.com/tcbgUIUC/namd.git
38
+ Allocate requisite resources (CPU cores, nodes, GPUs) on SOL and clone in https://gitlab.com/tcbgUIUC/namd.git
39
39
 
40
- After cloning, do:
40
+ After cloning and `cd`-ing to `namd` folder if you're not already there, do:
41
41
 
42
42
  ```bash
43
43
  git checkout feature_imdv3
44
- module load cmake-3.21.4-gcc-11.2.0
45
- module load gcc-10.3.0-gcc-11.2.0
46
- module load cuda-11.7.0-gcc-11.2.0
47
- module load openmpi/4.1.5
44
+ ```
48
45
 
49
- wget http://www.ks.uiuc.edu/Research/namd/libraries/fftw-linux-x86_64.tar.gz
50
- tar xzf fftw-linux-x86_64.tar.gz
51
- mv linux-x86_64 fftw
52
- wget http://www.ks.uiuc.edu/Research/namd/libraries/tcl8.6.13-linux-x86_64.tar.gz
53
- wget http://www.ks.uiuc.edu/Research/namd/libraries/tcl8.6.13-linux-x86_64-threaded.tar.gz
54
- tar xzf tcl8.6.13-linux-x86_64.tar.gz
55
- tar xzf tcl8.6.13-linux-x86_64-threaded.tar.gz
56
- mv tcl8.6.13-linux-x86_64 tcl
57
- mv tcl8.6.13-linux-x86_64-threaded tcl-threaded
46
+ Download and install TCL and FFTW libraries:
47
+ ```bash
48
+ wget http://www.ks.uiuc.edu/Research/namd/libraries/fftw-linux-x86_64.tar.gz
49
+ tar xzf fftw-linux-x86_64.tar.gz
50
+ mv linux-x86_64 fftw
51
+ wget http://www.ks.uiuc.edu/Research/namd/libraries/tcl8.6.13-linux-x86_64.tar.gz
52
+ wget http://www.ks.uiuc.edu/Research/namd/libraries/tcl8.6.13-linux-x86_64-threaded.tar.gz
53
+ tar xzf tcl8.6.13-linux-x86_64.tar.gz
54
+ tar xzf tcl8.6.13-linux-x86_64-threaded.tar.gz
55
+ mv tcl8.6.13-linux-x86_64 tcl
56
+ mv tcl8.6.13-linux-x86_64-threaded tcl-threaded
57
+ ```
58
58
 
59
+ Once can then download and unpack the Charm++ source code:
60
+
61
+ ```bash
59
62
  wget https://github.com/charmplusplus/charm/archive/refs/tags/v8.0.0.tar.gz
60
63
  tar xf v8.0.0.tar.gz
64
+ ```
65
+
66
+ Then, NAMD can be built with the following options:
67
+
68
+ #### Muticore version
69
+
70
+ ```bash
71
+ module load gcc-11.2.0-gcc-11.2.0
61
72
 
62
73
  cd charm-8.0.0
63
74
  ./build charm++ multicore-linux-x86_64 --with-production
64
- cd multicore-linux-x86_64/tests/charm++/megatest
65
- make -j 4
66
- ./megatest +p4
67
75
  cd ../../../../..
68
76
 
69
77
  ./config Linux-x86_64-g++ --charm-arch multicore-linux-x86_64
70
78
 
71
79
  cd Linux-x86_64-g++
72
- make -j 4
80
+ make
81
+ ```
82
+
83
+ #### InfiniBand UCX OpenMPI PMIx version
84
+
85
+ ```bash
86
+ module load gcc-11.2.0-gcc-11.2.0
87
+ module load openmpi/4.1.5
88
+ module load pmix/4.1.3-slurm
89
+
90
+ export CPATH="/packages/apps/pmix/4.1.3-slurm/include:$CPATH"
91
+ export LIBRARY_PATH="/packages/apps/pmix/4.1.3-slurm/lib:$LIBRARY_PATH"
92
+
93
+ cd charm-8.0.0
94
+ ./build charm++ ucx-linux-x86_64 ompipmix --with-production
95
+ cd ../../../../..
96
+
97
+ ./config Linux-x86_64-g++ --charm-arch ucx-linux-x86_64-ompipmix
98
+
99
+ cd Linux-x86_64-g++
100
+ make
101
+ ```
102
+
103
+ #### MPI version
104
+
105
+ ```bash
106
+ module load gcc-11.2.0-gcc-11.2.0
107
+ module load mpich/4.2.2
108
+
109
+ env MPICXX=mpicxx ./build charm++ mpi-linux-x86_64 --with-production
110
+ cd ../../../../..
111
+
112
+ ./config Linux-x86_64-g++ --charm-arch mpi-linux-x86_64
113
+
114
+ cd Linux-x86_64-g++
115
+ make
73
116
  ```
117
+
118
+ #### GPU-resident CUDA multicore version
119
+
120
+ ```bash
121
+ module load gcc-11.2.0-gcc-11.2.0
122
+ module load mpich/4.2.2
123
+ module load cuda-11.8.0-gcc-11.2.0
124
+ module load gsl-2.7.1-gcc-11.2.0
125
+
126
+ cd charm-8.0.0
127
+ ./build charm++ multicore-linux-x86_64 --with-production
128
+ cd ../../../../..
129
+
130
+ ./config Linux-x86_64-g++ --charm-arch multicore-linux-x86_64 --with-single-node-cuda
131
+
132
+ cd Linux-x86_64-g++
133
+ make
134
+ ```
135
+
136
+ #### GPU-resident CUDA MPI version
137
+
138
+ ```bash
139
+ cd charm-8.0.0
140
+ env MPICXX=mpicxx ./build charm++ mpi-linux-x86_64-smp --with-production
141
+ cd
142
+
143
+ ./config Linux-x86_64-g++ --charm-arch mpi-linux-x86_64-smp --with-single-node-cuda
144
+
145
+ cd Linux-x86_64-g++
146
+ make
147
+ ```
@@ -0,0 +1,86 @@
1
+ import logging
2
+ import copy
3
+
4
+ from MDAnalysis.coordinates import core
5
+
6
+ from imdclient.IMDClient import IMDClient
7
+ from imdclient.utils import parse_host_port
8
+
9
+ logger = logging.getLogger("imdclient.IMDClient")
10
+
11
+
12
+ class MinimalReader:
13
+ """
14
+ Minimal reader for testing purposes
15
+
16
+ Parameters
17
+ ----------
18
+ filename : str
19
+ a string of the form "host:port" where host is the hostname
20
+ or IP address of the listening MD engine server and port
21
+ is the port number.
22
+ n_atoms : int
23
+ number of atoms in the system. defaults to number of atoms
24
+ in the topology. don't set this unless you know what you're doing
25
+ process_stream : bool (optional)
26
+ if True, the reader will process the stream of frames and
27
+ store them in the `trajectory` attribute. defaults to False.
28
+ kwargs : dict (optional)
29
+ keyword arguments passed to the constructed :class:`IMDClient`
30
+ """
31
+
32
+ def __init__(self, filename, n_atoms, process_stream=False, **kwargs):
33
+
34
+ self.imd_frame = None
35
+
36
+ # a trajectory of imd frames
37
+ self.trajectory = []
38
+
39
+ self.n_atoms = n_atoms
40
+
41
+ host, port = parse_host_port(filename)
42
+
43
+ # This starts the simulation
44
+ self._imdclient = IMDClient(host, port, n_atoms, **kwargs)
45
+
46
+ self._frame = -1
47
+
48
+ if process_stream:
49
+ self._process_stream()
50
+
51
+ def _read_next_frame(self):
52
+ try:
53
+ imd_frame = self._imdclient.get_imdframe()
54
+ except EOFError:
55
+ raise
56
+
57
+ self._frame += 1
58
+ self.imd_frame = imd_frame
59
+
60
+ # Modify the box dimensions to be triclinic
61
+ self._modify_box_dimesions()
62
+
63
+ logger.debug(f"MinimalReader: Loaded frame {self._frame}")
64
+
65
+ return self.imd_frame
66
+
67
+ def _modify_box_dimesions(self):
68
+ self.imd_frame.dimensions = core.triclinic_box(*self.imd_frame.box)
69
+
70
+ def _process_stream(self):
71
+ # Process the stream of frames
72
+ while True:
73
+ try:
74
+ self.trajectory.append(copy.deepcopy(self._read_next_frame()))
75
+ # `.copy()` might not be required but adding it to cover any edge cases where a refernce gets passed
76
+ logger.debug(
77
+ f"MinimalReader: Added frame {self._frame} to trajectory"
78
+ )
79
+ except EOFError:
80
+ break
81
+
82
+ def close(self):
83
+ """Gracefully shut down the reader. Stops the producer thread."""
84
+ logger.debug("MinimalReader: close() called")
85
+ if self._imdclient is not None:
86
+ self._imdclient.stop()
imdclient/tests/server.py CHANGED
@@ -1,15 +1,16 @@
1
1
  import socket
2
2
  import struct
3
- import numpy as np
4
3
  import threading
5
- import time
4
+ import logging
5
+
6
+ import numpy as np
7
+
6
8
  from imdclient.IMDProtocol import (
7
9
  IMDHeaderType,
8
10
  create_header_bytes,
9
11
  create_energy_bytes,
10
12
  IMDHEADERSIZE,
11
13
  )
12
- import logging
13
14
  from imdclient.IMDClient import sock_contains_data, read_into_buf, IMDHeader
14
15
 
15
16
  logger = logging.getLogger("imdclient.IMDClient")