imdclient 0.1.1__tar.gz → 0.1.3__tar.gz

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.
Files changed (61) hide show
  1. imdclient-0.1.3/LICENSE +5 -0
  2. {imdclient-0.1.1 → imdclient-0.1.3}/PKG-INFO +13 -24
  3. {imdclient-0.1.1 → imdclient-0.1.3}/README.md +8 -3
  4. imdclient-0.1.1/imdclient/IMDREADER.py → imdclient-0.1.3/imdclient/IMD.py +16 -15
  5. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/IMDClient.py +146 -71
  6. imdclient-0.1.3/imdclient/data/gromacs/md/gromacs_v3_nst8.mdp +58 -0
  7. imdclient-0.1.1/imdclient/data/lammps/md/lammps_v3.in → imdclient-0.1.3/imdclient/data/lammps/md/lammps_v3_nst_1.in +3 -3
  8. imdclient-0.1.3/imdclient/data/lammps/md/lammps_v3_nst_8.in +71 -0
  9. imdclient-0.1.1/imdclient/data/namd/md/namd_v3.namd → imdclient-0.1.3/imdclient/data/namd/md/namd_v3_nst_1.namd +17 -5
  10. imdclient-0.1.3/imdclient/data/namd/md/namd_v3_nst_8.namd +59 -0
  11. imdclient-0.1.3/imdclient/tests/base.py +210 -0
  12. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/tests/conftest.py +14 -10
  13. imdclient-0.1.3/imdclient/tests/datafiles.py +42 -0
  14. imdclient-0.1.3/imdclient/tests/docker_testing/docker.md +25 -0
  15. imdclient-0.1.3/imdclient/tests/test_gromacs.py +55 -0
  16. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/tests/test_imdclient.py +51 -0
  17. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/tests/test_imdreader.py +20 -6
  18. imdclient-0.1.3/imdclient/tests/test_lammps.py +83 -0
  19. imdclient-0.1.3/imdclient/tests/test_manual.py +93 -0
  20. imdclient-0.1.3/imdclient/tests/test_namd.py +125 -0
  21. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/tests/test_stream_analysis.py +2 -2
  22. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/tests/utils.py +0 -1
  23. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/utils.py +8 -8
  24. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient.egg-info/PKG-INFO +13 -24
  25. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient.egg-info/SOURCES.txt +8 -8
  26. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient.egg-info/requires.txt +1 -0
  27. {imdclient-0.1.1 → imdclient-0.1.3}/pyproject.toml +1 -0
  28. imdclient-0.1.1/LICENSE +0 -21
  29. imdclient-0.1.1/imdclient/data/gromacs/md/gromacs_v3_nst1.tpr +0 -0
  30. imdclient-0.1.1/imdclient/data/gromacs/md/gromacs_v3_nst1.trr +0 -0
  31. imdclient-0.1.1/imdclient/data/lammps/md/lammps_trj.h5md +0 -0
  32. imdclient-0.1.1/imdclient/data/namd/md/alanin.dcd +0 -0
  33. imdclient-0.1.1/imdclient/tests/base.py +0 -122
  34. imdclient-0.1.1/imdclient/tests/datafiles.py +0 -34
  35. imdclient-0.1.1/imdclient/tests/test_gromacs.py +0 -33
  36. imdclient-0.1.1/imdclient/tests/test_lammps.py +0 -38
  37. imdclient-0.1.1/imdclient/tests/test_manual.py +0 -70
  38. imdclient-0.1.1/imdclient/tests/test_namd.py +0 -38
  39. {imdclient-0.1.1 → imdclient-0.1.3}/AUTHORS.md +0 -0
  40. {imdclient-0.1.1 → imdclient-0.1.3}/CHANGELOG.md +0 -0
  41. {imdclient-0.1.1 → imdclient-0.1.3}/CODE_OF_CONDUCT.md +0 -0
  42. {imdclient-0.1.1 → imdclient-0.1.3}/MANIFEST.in +0 -0
  43. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/IMDProtocol.py +0 -0
  44. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/__init__.py +0 -0
  45. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/backends.py +0 -0
  46. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/data/__init__.py +0 -0
  47. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/data/gromacs/md/gromacs_struct.gro +0 -0
  48. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/data/gromacs/md/gromacs_v3.top +0 -0
  49. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/data/gromacs/md/gromacs_v3_nst1.mdp +0 -0
  50. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/data/lammps/md/lammps_topol.data +0 -0
  51. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/data/namd/md/alanin.params +0 -0
  52. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/data/namd/md/alanin.pdb +0 -0
  53. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/data/namd/md/alanin.psf +0 -0
  54. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/results.py +0 -0
  55. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/streamanalysis.py +0 -0
  56. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/streambase.py +0 -0
  57. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/tests/__init__.py +0 -0
  58. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/tests/server.py +0 -0
  59. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient.egg-info/dependency_links.txt +0 -0
  60. {imdclient-0.1.1 → imdclient-0.1.3}/imdclient.egg-info/top_level.txt +0 -0
  61. {imdclient-0.1.1 → imdclient-0.1.3}/setup.cfg +0 -0
@@ -0,0 +1,5 @@
1
+ Copyright 2024 Lawson Woods
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
@@ -1,31 +1,14 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: imdclient
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: Receiver for IMD v2 and v3 data from simulation engines like Gromacs, LAMMPS, and NAMD
5
5
  Author-email: Lawson <ljwoods2@asu.edu>
6
6
  Maintainer-email: Lawson <ljwoods2@asu.edu>
7
- License: MIT License
7
+ License: Copyright 2024 Lawson Woods
8
8
 
9
- Copyright (c) 2024 ljwoods2
10
-
11
- Permission is hereby granted, free of charge, to any person obtaining a copy
12
- of this software and associated documentation files (the "Software"), to deal
13
- in the Software without restriction, including without limitation the rights
14
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
- copies of the Software, and to permit persons to whom the Software is
16
- furnished to do so, subject to the following conditions:
17
-
18
- The above copyright notice and this permission notice shall be included in all
19
- copies or substantial portions of the Software.
20
-
21
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
- SOFTWARE.
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
28
10
 
11
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
29
12
  Keywords: molecular simulations
30
13
  Requires-Python: >=3.9
31
14
  Description-Content-Type: text/markdown
@@ -37,6 +20,7 @@ Requires-Dist: pytest>=6.0; extra == "test"
37
20
  Requires-Dist: pytest-xdist>=2.5; extra == "test"
38
21
  Requires-Dist: pytest-cov>=3.0; extra == "test"
39
22
  Requires-Dist: MDAnalysisTests>=2.7.0; extra == "test"
23
+ Requires-Dist: docker-py; extra == "test"
40
24
  Provides-Extra: doc
41
25
  Requires-Dist: sphinx; extra == "doc"
42
26
  Requires-Dist: sphinx_rtd_theme; extra == "doc"
@@ -48,7 +32,7 @@ IMDClient
48
32
  | **Latest release** | [![Last release tag][badge_release]][url_latest_release] ![GitHub commits since latest release (by date) for a branch][badge_commits_since] [![Documentation Status][badge_docs]][url_docs]|
49
33
  | :----------------- | :------- |
50
34
  | **Status** | [![GH Actions Status][badge_actions]][url_actions] [![codecov][badge_codecov]][url_codecov] |
51
- | **Community** | [![License: GPL v2][badge_license]][url_license] [![Powered by MDAnalysis][badge_mda]][url_mda]|
35
+ | **Community** | [![License: MIT][badge_license]][url_license] [![Powered by MDAnalysis][badge_mda]][url_mda]|
52
36
 
53
37
  [badge_actions]: https://github.com/becksteinlab/imdclient/actions/workflows/gh-ci.yaml/badge.svg
54
38
  [badge_codecov]: https://codecov.io/gh/becksteinlab/imdclient/branch/main/graph/badge.svg
@@ -61,15 +45,20 @@ IMDClient
61
45
  [url_codecov]: https://codecov.io/gh/becksteinlab/imdclient/branch/main
62
46
  [url_docs]: https://imdclient.readthedocs.io/en/latest/?badge=latest
63
47
  [url_latest_release]: https://github.com/becksteinlab/imdclient/releases
64
- [url_license]: https://www.gnu.org/licenses/gpl-2.0
48
+ [url_license]: https://opensource.org/license/mit
65
49
  [url_mda]: https://www.mdanalysis.org
66
50
 
67
- Receiver for IMDv3 protocol from simulation engines like Gromacs, LAMMPS, and NAMD.
51
+ Receiver for [IMDv3 protocol](https://imdclient.readthedocs.io/en/latest/protocol_v3.html) from simulation engines like Gromacs, LAMMPS, and NAMD.
68
52
 
69
53
  IMDClient is bound by a [Code of Conduct](https://github.com/becksteinlab/imdreader/blob/main/CODE_OF_CONDUCT.md).
70
54
 
71
55
  ### Installation
72
56
 
57
+ IMDClient is available via PyPi and can be installed with pip:
58
+ ```bash
59
+ pip install imdclient
60
+ ```
61
+
73
62
  To build IMDClient from source,
74
63
  we highly recommend using virtual environments.
75
64
  If possible, we strongly recommend that you use
@@ -5,7 +5,7 @@ IMDClient
5
5
  | **Latest release** | [![Last release tag][badge_release]][url_latest_release] ![GitHub commits since latest release (by date) for a branch][badge_commits_since] [![Documentation Status][badge_docs]][url_docs]|
6
6
  | :----------------- | :------- |
7
7
  | **Status** | [![GH Actions Status][badge_actions]][url_actions] [![codecov][badge_codecov]][url_codecov] |
8
- | **Community** | [![License: GPL v2][badge_license]][url_license] [![Powered by MDAnalysis][badge_mda]][url_mda]|
8
+ | **Community** | [![License: MIT][badge_license]][url_license] [![Powered by MDAnalysis][badge_mda]][url_mda]|
9
9
 
10
10
  [badge_actions]: https://github.com/becksteinlab/imdclient/actions/workflows/gh-ci.yaml/badge.svg
11
11
  [badge_codecov]: https://codecov.io/gh/becksteinlab/imdclient/branch/main/graph/badge.svg
@@ -18,15 +18,20 @@ IMDClient
18
18
  [url_codecov]: https://codecov.io/gh/becksteinlab/imdclient/branch/main
19
19
  [url_docs]: https://imdclient.readthedocs.io/en/latest/?badge=latest
20
20
  [url_latest_release]: https://github.com/becksteinlab/imdclient/releases
21
- [url_license]: https://www.gnu.org/licenses/gpl-2.0
21
+ [url_license]: https://opensource.org/license/mit
22
22
  [url_mda]: https://www.mdanalysis.org
23
23
 
24
- Receiver for IMDv3 protocol from simulation engines like Gromacs, LAMMPS, and NAMD.
24
+ Receiver for [IMDv3 protocol](https://imdclient.readthedocs.io/en/latest/protocol_v3.html) from simulation engines like Gromacs, LAMMPS, and NAMD.
25
25
 
26
26
  IMDClient is bound by a [Code of Conduct](https://github.com/becksteinlab/imdreader/blob/main/CODE_OF_CONDUCT.md).
27
27
 
28
28
  ### Installation
29
29
 
30
+ IMDClient is available via PyPi and can be installed with pip:
31
+ ```bash
32
+ pip install imdclient
33
+ ```
34
+
30
35
  To build IMDClient from source,
31
36
  we highly recommend using virtual environments.
32
37
  If possible, we strongly recommend that you use
@@ -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")
@@ -83,9 +83,8 @@ class IMDReader(StreamReaderBase):
83
83
 
84
84
  try:
85
85
  imdf = self._imdclient.get_imdframe()
86
- except EOFError:
87
- # Not strictly necessary, but for clarity
88
- raise StopIteration
86
+ except EOFError as e:
87
+ raise e
89
88
 
90
89
  self._frame = frame
91
90
  self._load_imdframe_into_ts(imdf)
@@ -101,7 +100,9 @@ class IMDReader(StreamReaderBase):
101
100
  self.ts.data["dt"] = imdf.dt
102
101
  self.ts.data["step"] = imdf.step
103
102
  if imdf.energies is not None:
104
- self.ts.data.update(imdf.energies)
103
+ self.ts.data.update(
104
+ {k: v for k, v in imdf.energies.items() if k != "step"}
105
+ )
105
106
  if imdf.box is not None:
106
107
  self.ts.dimensions = core.triclinic_box(*imdf.box)
107
108
  if imdf.positions is not None:
@@ -25,11 +25,31 @@ import time
25
25
  import numpy as np
26
26
  from typing import Union, Dict
27
27
  import signal
28
+ import atexit
28
29
 
29
30
  logger = logging.getLogger(__name__)
30
31
 
31
32
 
32
33
  class IMDClient:
34
+ """
35
+ Parameters
36
+ ----------
37
+ host : str
38
+ Hostname of the server
39
+ port : int
40
+ Port number of the server
41
+ n_atoms : int
42
+ Number of atoms in the simulation
43
+ socket_bufsize : int, (optional)
44
+ Size of the socket buffer in bytes. Default is to use the system default
45
+ buffer_size : int (optional)
46
+ IMDFramebuffer will be filled with as many :class:`IMDFrame` fit in `buffer_size` bytes [``10MB``]
47
+ timeout : int, optional
48
+ Timeout for the socket in seconds [``5``]
49
+ **kwargs : dict (optional)
50
+ Additional keyword arguments to pass to the :class:`BaseIMDProducer` and :class:`IMDFrameBuffer`
51
+ """
52
+
33
53
  def __init__(
34
54
  self,
35
55
  host,
@@ -39,22 +59,7 @@ class IMDClient:
39
59
  multithreaded=True,
40
60
  **kwargs,
41
61
  ):
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
- """
62
+
58
63
  self._stopped = False
59
64
  self._conn = self._connect_to_server(host, port, socket_bufsize)
60
65
  self._imdsinfo = self._await_IMD_handshake()
@@ -66,8 +71,10 @@ class IMDClient:
66
71
  n_atoms,
67
72
  **kwargs,
68
73
  )
74
+ self._error_queue = queue.Queue()
69
75
  else:
70
76
  self._buf = None
77
+ self._error_queue = None
71
78
  if self._imdsinfo.version == 2:
72
79
  self._producer = IMDProducerV2(
73
80
  self._conn,
@@ -75,6 +82,7 @@ class IMDClient:
75
82
  self._imdsinfo,
76
83
  n_atoms,
77
84
  multithreaded,
85
+ self._error_queue,
78
86
  **kwargs,
79
87
  )
80
88
  elif self._imdsinfo.version == 3:
@@ -84,23 +92,60 @@ class IMDClient:
84
92
  self._imdsinfo,
85
93
  n_atoms,
86
94
  multithreaded,
95
+ self._error_queue,
87
96
  **kwargs,
88
97
  )
89
98
 
90
99
  self._go()
91
100
 
92
101
  if self._multithreaded:
102
+ # Disconnect MUST occur. This covers typical cases (Python, IPython interpreter)
93
103
  signal.signal(signal.SIGINT, self.signal_handler)
104
+ signal.signal(signal.SIGTERM, self.signal_handler)
105
+
106
+ # Disconnect and socket shutdown MUST occur. This covers Jupyter use
107
+ # since in jupyter, the signal handler is reset to the default
108
+ # by pre- and post- hooks
109
+ # https://stackoverflow.com/questions/70841648/jupyter-reverts-signal-handler-to-default-when-running-next-cell
110
+ try:
111
+ import IPython
112
+ except ImportError:
113
+ has_ipython = False
114
+ else:
115
+ has_ipython = True
116
+
117
+ if has_ipython:
118
+ try:
119
+ from IPython import get_ipython
120
+
121
+ if get_ipython() is not None:
122
+ kernel = get_ipython().kernel
123
+ kernel.pre_handler_hook = lambda: None
124
+ kernel.post_handler_hook = lambda: None
125
+ logger.debug("Running in Jupyter")
126
+ except NameError:
127
+ logger.debug("Running in non-jupyter IPython environment")
128
+
129
+ # Final case: error is raised outside of IMDClient code
130
+ logger.debug("Registering atexit")
131
+ atexit.register(self.stop)
132
+
94
133
  self._producer.start()
95
134
 
96
135
  def signal_handler(self, sig, frame):
97
136
  """Catch SIGINT to allow clean shutdown on CTRL+C
98
137
  This also ensures that main thread execution doesn't get stuck
99
138
  waiting in buf.pop_full_imdframe()"""
139
+ logger.debug("Intercepted signal")
100
140
  self.stop()
141
+ logger.debug("Shutdown success")
101
142
 
102
143
  def get_imdframe(self):
103
144
  """
145
+ Returns
146
+ -------
147
+ IMDFrame
148
+ The next frame from the IMD server
104
149
  Raises
105
150
  ------
106
151
  EOFError
@@ -114,6 +159,9 @@ class IMDClient:
114
159
  # and doesn't need to be notified
115
160
  self._disconnect()
116
161
  self._stopped = True
162
+
163
+ if self._error_queue.qsize():
164
+ raise EOFError(f"{self._error_queue.get()}")
117
165
  raise EOFError
118
166
  else:
119
167
  try:
@@ -123,14 +171,23 @@ class IMDClient:
123
171
  raise EOFError
124
172
 
125
173
  def get_imdsessioninfo(self):
174
+ """
175
+ Returns
176
+ -------
177
+ IMDSessionInfo
178
+ Information about the IMD session
179
+ """
126
180
  return self._imdsinfo
127
181
 
128
182
  def stop(self):
183
+ """
184
+ Stop the client and close the connection
185
+ """
129
186
  if self._multithreaded:
130
187
  if not self._stopped:
131
- self._buf.notify_consumer_finished()
132
- self._disconnect()
133
188
  self._stopped = True
189
+ self._disconnect()
190
+ self._buf.notify_consumer_finished()
134
191
  else:
135
192
  self._disconnect()
136
193
 
@@ -144,9 +201,7 @@ class IMDClient:
144
201
  # /proc/sys/net/core/rmem_default
145
202
  # Max (linux):
146
203
  # /proc/sys/net/core/rmem_max
147
- conn.setsockopt(
148
- socket.SOL_SOCKET, socket.SO_RCVBUF, socket_bufsize
149
- )
204
+ conn.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, socket_bufsize)
150
205
  try:
151
206
  logger.debug(f"IMDClient: Connecting to {host}:{port}")
152
207
  conn.connect((host, port))
@@ -252,8 +307,37 @@ class IMDClient:
252
307
  finally:
253
308
  self._conn.close()
254
309
 
310
+ def __enter__(self):
311
+ return self
312
+
313
+ def __exit__(self, exc_type, exc_val, exc_tb):
314
+ self.stop()
315
+ return False
316
+
255
317
 
256
318
  class BaseIMDProducer(threading.Thread):
319
+ """
320
+
321
+ Parameters
322
+ ----------
323
+ conn : socket.socket
324
+ Connection object to the server
325
+ buffer : IMDFrameBuffer
326
+ Buffer object to hold IMD frames. If `multithreaded` is False, this
327
+ argument is ignored
328
+ sinfo : IMDSessionInfo
329
+ Information about the IMD session
330
+ n_atoms : int
331
+ Number of atoms in the simulation
332
+ multithreaded : bool
333
+ If True, socket interaction will occur in a separate thread &
334
+ frames will be buffered. Single-threaded, blocking IMDClient
335
+ should only be used in testing
336
+ error_queue: queue.Queue
337
+ Queue to hold errors produced by the producer thread
338
+ timeout : int, optional
339
+ Timeout for the socket in seconds [``5``]
340
+ """
257
341
 
258
342
  def __init__(
259
343
  self,
@@ -261,33 +345,17 @@ class BaseIMDProducer(threading.Thread):
261
345
  buffer,
262
346
  sinfo,
263
347
  n_atoms,
264
- multithreaded=True,
348
+ multithreaded,
349
+ error_queue,
265
350
  timeout=5,
266
351
  **kwargs,
267
352
  ):
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
353
  super(BaseIMDProducer, self).__init__(daemon=True)
287
354
  self._conn = conn
288
355
  self._imdsinfo = sinfo
289
356
  self._paused = False
290
357
 
358
+ self.error_queue = error_queue
291
359
  # Timeout for first frame should be longer
292
360
  # than rest of frames
293
361
  self._timeout = timeout
@@ -382,6 +450,7 @@ class BaseIMDProducer(threading.Thread):
382
450
  logger.debug("IMDProducer: Simulation ended normally, cleaning up")
383
451
  except Exception as e:
384
452
  logger.debug("IMDProducer: An unexpected error occurred: %s", e)
453
+ self.error_queue.put(e)
385
454
  finally:
386
455
  logger.debug("IMDProducer: Stopping run loop")
387
456
  # Tell consumer not to expect more frames to be added
@@ -397,9 +466,19 @@ class BaseIMDProducer(threading.Thread):
397
466
  )
398
467
  # Sometimes we do not care what the value is
399
468
  if expected_value is not None and header.length != expected_value:
400
- raise RuntimeError(
401
- f"IMDProducer: Expected header value {expected_value}, got {header.length}"
402
- )
469
+ if expected_type in [
470
+ IMDHeaderType.IMD_FCOORDS,
471
+ IMDHeaderType.IMD_VELOCITIES,
472
+ IMDHeaderType.IMD_FORCES,
473
+ ]:
474
+ raise RuntimeError(
475
+ f"IMDProducer: Expected n_atoms value {expected_value}, got {header.length}. "
476
+ + "Ensure you are using the correct topology file."
477
+ )
478
+ else:
479
+ raise RuntimeError(
480
+ f"IMDProducer: Expected header value {expected_value}, got {header.length}"
481
+ )
403
482
 
404
483
  def _get_header(self):
405
484
  self._read(self._header)
@@ -419,9 +498,11 @@ class BaseIMDProducer(threading.Thread):
419
498
 
420
499
 
421
500
  class IMDProducerV2(BaseIMDProducer):
422
- def __init__(self, conn, buffer, sinfo, n_atoms, multithreaded, **kwargs):
501
+ def __init__(
502
+ self, conn, buffer, sinfo, n_atoms, multithreaded, error_queue, **kwargs
503
+ ):
423
504
  super(IMDProducerV2, self).__init__(
424
- conn, buffer, sinfo, n_atoms, multithreaded, **kwargs
505
+ conn, buffer, sinfo, n_atoms, multithreaded, error_queue, **kwargs
425
506
  )
426
507
 
427
508
  self._energies = bytearray(IMDENERGYPACKETLENGTH)
@@ -514,6 +595,7 @@ class IMDProducerV3(BaseIMDProducer):
514
595
  sinfo,
515
596
  n_atoms,
516
597
  multithreaded,
598
+ error_queue,
517
599
  **kwargs,
518
600
  ):
519
601
  super(IMDProducerV3, self).__init__(
@@ -522,6 +604,7 @@ class IMDProducerV3(BaseIMDProducer):
522
604
  sinfo,
523
605
  n_atoms,
524
606
  multithreaded,
607
+ error_queue,
525
608
  **kwargs,
526
609
  )
527
610
  # The body of an x/v/f packet should contain
@@ -638,6 +721,21 @@ class IMDFrameBuffer:
638
721
  """
639
722
  Acts as interface between producer (IMDProducer) and consumer (IMDClient) threads
640
723
  when IMDClient runs in multithreaded mode
724
+
725
+ Parameters
726
+ ----------
727
+ imdsinfo : IMDSessionInfo
728
+ Information about the IMD session
729
+ n_atoms : int
730
+ Number of atoms in the simulation
731
+ buffer_size : int, optional
732
+ Size of the buffer in bytes [``10MB``]
733
+ pause_empty_proportion : float, optional
734
+ Lower threshold proportion of the buffer's IMDFrames that are empty
735
+ before the simulation is paused [``0.25``]
736
+ unpause_empty_proportion : float, optional
737
+ Proportion of the buffer's IMDFrames that must be empty
738
+ before the simulation is unpaused [``0.5``]
641
739
  """
642
740
 
643
741
  def __init__(
@@ -649,23 +747,6 @@ class IMDFrameBuffer:
649
747
  unpause_empty_proportion=0.5,
650
748
  **kwargs,
651
749
  ):
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
750
  # Syncing reader and producer
670
751
  self._producer_finished = False
671
752
  self._consumer_finished = False
@@ -681,9 +762,7 @@ class IMDFrameBuffer:
681
762
  raise ValueError("pause_empty_proportion must be between 0 and 1")
682
763
  self._pause_empty_proportion = pause_empty_proportion
683
764
  if unpause_empty_proportion < 0 or unpause_empty_proportion > 1:
684
- raise ValueError(
685
- "unpause_empty_proportion must be between 0 and 1"
686
- )
765
+ raise ValueError("unpause_empty_proportion must be between 0 and 1")
687
766
  self._unpause_empty_proportion = unpause_empty_proportion
688
767
 
689
768
  if buffer_size <= 0:
@@ -750,9 +829,7 @@ class IMDFrameBuffer:
750
829
  logger.debug("IMDProducer: Noticing consumer finished")
751
830
  raise EOFError
752
831
  except Exception as e:
753
- logger.debug(
754
- f"IMDProducer: Error waiting for space in buffer: {e}"
755
- )
832
+ logger.debug(f"IMDProducer: Error waiting for space in buffer: {e}")
756
833
 
757
834
  def pop_empty_imdframe(self):
758
835
  logger.debug("IMDProducer: Getting empty frame")
@@ -798,9 +875,7 @@ class IMDFrameBuffer:
798
875
  imdf = self._full_q.get()
799
876
  else:
800
877
  with self._full_imdf_avail:
801
- while (
802
- self._full_q.qsize() == 0 and not self._producer_finished
803
- ):
878
+ while self._full_q.qsize() == 0 and not self._producer_finished:
804
879
  self._full_imdf_avail.wait()
805
880
 
806
881
  if self._producer_finished and self._full_q.qsize() == 0:
@@ -0,0 +1,58 @@
1
+ title = PRODUCTION IN NPT
2
+ ld-seed = 1
3
+ ; Run parameters
4
+ integrator = md ; leap-frog integrator
5
+ nsteps = 100 ; 1 * 1000 = 1 ps
6
+ dt = 0.001 ; 1 fs
7
+ ; Output control
8
+ nstxout = 8 ; save coordinates every 1 fs
9
+ nstvout = 8 ; save velocities every 1 fs
10
+ nstfout = 8
11
+ nstenergy = 8 ; save energies every 1 fs
12
+ nstlog = 10 ; update log file every 1 ps
13
+ ; Center of mass (COM) motion
14
+ nstcomm = 10 ; remove COM motion every 10 steps
15
+ comm-mode = Linear ; remove only COM translation (liquids in PBC)
16
+ ; Bond parameters
17
+ continuation = yes ; first dynamics run
18
+ constraint_algorithm = lincs ; holonomic constraints
19
+ constraints = all-bonds ; all bonds lengths are constrained
20
+ lincs_iter = 1 ; accuracy of LINCS
21
+ lincs_order = 4 ; also related to accuracy
22
+ ; Nonbonded settings
23
+ cutoff-scheme = Verlet ; Buffered neighbor searching
24
+ ns_type = grid ; search neighboring grid cells
25
+ nstlist = 10 ; 10 fs, largely irrelevant with Verlet
26
+ rcoulomb = 1.0 ; short-range electrostatic cutoff (in nm)
27
+ rvdw = 1.0 ; short-range van der Waals cutoff (in nm)
28
+ DispCorr = EnerPres ; account for cut-off vdW scheme
29
+ ; Electrostatics
30
+ coulombtype = PME ; Particle Mesh Ewald for long-range electrostatics
31
+ pme_order = 4 ; cubic interpolation
32
+ fourierspacing = 0.12 ; grid spacing for FFT
33
+ ; Temperature coupling is on
34
+ tcoupl = Nose-Hoover ; good for production, after equilibration
35
+ ; we define separate thermostats for the solute and solvent (need to adapt)
36
+ ; see default groups defined by Gromacs for your system or define your own (make_ndx)
37
+ tc-grps = Protein SOL ; the separate groups for the thermostats
38
+ tau-t = 1.0 1.0 ; time constants for thermostats (ps)
39
+ ref-t = 300 300 ; reference temperature for thermostats (K)
40
+ ; Pressure coupling is off
41
+ pcoupl = Parrinello-Rahman ; good for production, after equilibration
42
+ tau-p = 2.0 ; time constant for barostat (ps)
43
+ compressibility = 4.5e-5 ; compressibility (1/bar) set to water at ~300K
44
+ ref-p = 1.0 ; reference pressure for barostat (bar)
45
+ ; Periodic boundary conditions
46
+ pbc = xyz ; 3-D PBC
47
+ ; Velocity generation
48
+ gen_vel = no
49
+ IMD-group = System
50
+ IMD-nst = 8
51
+ IMD-version = 3
52
+ IMD-time = yes
53
+ IMD-box = yes
54
+ IMD-coords = yes
55
+ IMD-unwrap = no
56
+ IMD-vels = yes
57
+ IMD-forces = yes
58
+ IMD-energies = no
@@ -52,8 +52,8 @@ velocity all create 300 102939 dist gaussian mom yes rot yes
52
52
  fix 1 all nve
53
53
 
54
54
  # Create source of truth trajectory
55
- # dump h5md1 all h5md 1 lammps_trj.h5md position velocity force box yes
56
- # dump_modify h5md1 unwrap no
55
+ dump h5md1 all h5md 1 lammps_trj.h5md position velocity force box yes
56
+ dump_modify h5md1 unwrap no
57
57
 
58
58
  ## IMD settings
59
59
  # https://docs.lammps.org/fix_imd.html
@@ -63,7 +63,7 @@ fix 2 all imd 8888 version 3 unwrap off nowait off
63
63
  run 100
64
64
 
65
65
  # Stop dumping information to the dump file.
66
- # undump h5md1
66
+ undump h5md1
67
67
 
68
68
  # Unfix the NVE. Additional lines if any will assume that this fix is off.
69
69
  unfix 1