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.
- imdclient-0.1.3/LICENSE +5 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/PKG-INFO +13 -24
- {imdclient-0.1.1 → imdclient-0.1.3}/README.md +8 -3
- imdclient-0.1.1/imdclient/IMDREADER.py → imdclient-0.1.3/imdclient/IMD.py +16 -15
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/IMDClient.py +146 -71
- imdclient-0.1.3/imdclient/data/gromacs/md/gromacs_v3_nst8.mdp +58 -0
- 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
- imdclient-0.1.3/imdclient/data/lammps/md/lammps_v3_nst_8.in +71 -0
- 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
- imdclient-0.1.3/imdclient/data/namd/md/namd_v3_nst_8.namd +59 -0
- imdclient-0.1.3/imdclient/tests/base.py +210 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/tests/conftest.py +14 -10
- imdclient-0.1.3/imdclient/tests/datafiles.py +42 -0
- imdclient-0.1.3/imdclient/tests/docker_testing/docker.md +25 -0
- imdclient-0.1.3/imdclient/tests/test_gromacs.py +55 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/tests/test_imdclient.py +51 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/tests/test_imdreader.py +20 -6
- imdclient-0.1.3/imdclient/tests/test_lammps.py +83 -0
- imdclient-0.1.3/imdclient/tests/test_manual.py +93 -0
- imdclient-0.1.3/imdclient/tests/test_namd.py +125 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/tests/test_stream_analysis.py +2 -2
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/tests/utils.py +0 -1
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/utils.py +8 -8
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient.egg-info/PKG-INFO +13 -24
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient.egg-info/SOURCES.txt +8 -8
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient.egg-info/requires.txt +1 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/pyproject.toml +1 -0
- imdclient-0.1.1/LICENSE +0 -21
- imdclient-0.1.1/imdclient/data/gromacs/md/gromacs_v3_nst1.tpr +0 -0
- imdclient-0.1.1/imdclient/data/gromacs/md/gromacs_v3_nst1.trr +0 -0
- imdclient-0.1.1/imdclient/data/lammps/md/lammps_trj.h5md +0 -0
- imdclient-0.1.1/imdclient/data/namd/md/alanin.dcd +0 -0
- imdclient-0.1.1/imdclient/tests/base.py +0 -122
- imdclient-0.1.1/imdclient/tests/datafiles.py +0 -34
- imdclient-0.1.1/imdclient/tests/test_gromacs.py +0 -33
- imdclient-0.1.1/imdclient/tests/test_lammps.py +0 -38
- imdclient-0.1.1/imdclient/tests/test_manual.py +0 -70
- imdclient-0.1.1/imdclient/tests/test_namd.py +0 -38
- {imdclient-0.1.1 → imdclient-0.1.3}/AUTHORS.md +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/CHANGELOG.md +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/CODE_OF_CONDUCT.md +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/MANIFEST.in +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/IMDProtocol.py +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/__init__.py +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/backends.py +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/data/__init__.py +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/data/gromacs/md/gromacs_struct.gro +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/data/gromacs/md/gromacs_v3.top +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/data/gromacs/md/gromacs_v3_nst1.mdp +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/data/lammps/md/lammps_topol.data +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/data/namd/md/alanin.params +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/data/namd/md/alanin.pdb +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/data/namd/md/alanin.psf +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/results.py +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/streamanalysis.py +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/streambase.py +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/tests/__init__.py +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient/tests/server.py +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient.egg-info/dependency_links.txt +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/imdclient.egg-info/top_level.txt +0 -0
- {imdclient-0.1.1 → imdclient-0.1.3}/setup.cfg +0 -0
imdclient-0.1.3/LICENSE
ADDED
@@ -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.
|
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:
|
7
|
+
License: Copyright 2024 Lawson Woods
|
8
8
|
|
9
|
-
|
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:
|
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://
|
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:
|
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://
|
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
|
-
|
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(
|
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
|
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
|
-
|
401
|
-
|
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__(
|
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
|
-
|
56
|
-
|
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
|
-
|
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
|