Skip to content

Commit 57b6b17

Browse files
authored
Merge branch 'main' into copilot/fix-84
2 parents bc25c68 + d46c802 commit 57b6b17

File tree

8 files changed

+91
-37
lines changed

8 files changed

+91
-37
lines changed

.github/workflows/gh-ci.yaml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ jobs:
4848
fail-fast: false
4949
matrix:
5050
os: [macOS-latest, ubuntu-latest]
51-
python-version: ["3.10", "3.11", "3.12"]
51+
python-version: ["3.10", "3.11", "3.12", "3.13"]
5252

5353
steps:
5454
- uses: actions/checkout@v4
@@ -171,7 +171,16 @@ jobs:
171171
172172
- name: Run simulation engine tests
173173
run: |
174-
pytest -v --color=yes ${{ matrix.script }}
174+
pytest -v --cov=imdclient --cov-report=xml --color=yes ${{ matrix.script }}
175+
176+
- name: codecov
177+
if: github.event_name != 'schedule'
178+
uses: codecov/codecov-action@v5.4.3
179+
with:
180+
token: ${{ secrets.CODECOV_TOKEN }}
181+
files: coverage.xml
182+
name: codecov-${{ matrix.name }}
183+
verbose: True
175184

176185
pylint_check:
177186
if: github.repository == 'Becksteinlab/imdclient'

AUTHORS.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,14 @@ The rules for this file:
1919
* Don't ever delete anything
2020
-->
2121

22+
**2024**
23+
- Lawson Woods <@ljwoods2>
24+
- Heekun Cho <@hcho38>
25+
- Oliver Beckstein <@orbeckst>
26+
2227
**2025**
2328
- Amruthesh Thirumalaiswamy <@amruthesht>
24-
25-
**2024**
26-
- Lawson <@ljwoods2>
29+
- Jennifer A. Clark <@jaclark5>
30+
- Hugo MacDermott-Opeskin <@hmacdope>
31+
- Matthias Heyden <@HeydenLabASU>
32+
- GitHub co-pilot <@copilot>

CHANGELOG.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,32 @@ The rules for this file:
1616
* accompany each entry with github issue/PR number (Issue #xyz)
1717
-->
1818

19-
## [v0.2.0] - 2025-??-??
19+
## [v0.2.0] - 2025-07-18
20+
21+
**Breaking change**: The removal of the IMDReader and the associated
22+
streaming analysis functionality will *break code* (see issue
23+
#53). The IMDReader is being integrated into MDAnalysis and should be
24+
available in MDAnalysis release 2.10.0.
2025

2126
### Authors
2227
<!-- GitHub usernames of contributors to this release -->
23-
@amruthesht @ljwoods2
28+
@amruthesht @ljwoods2 @hmacdope @jaclark5 @orbeckst
29+
30+
### Changed
31+
<!-- Changes in existing functionality -->
32+
* IMDReader removed from imdclient by @amruthesht in https://github.com/Becksteinlab/imdclient/pull/54 (issue #53)
33+
* added support for Python 3.13 (issue #90)
2434

2535
### Added
2636
<!-- New added features -->
2737
* Updated installation instructions by @amruthesht in https://github.com/Becksteinlab/imdclient/pull/74
28-
* MD engine links updated in IMDClient documnetation by @amruthesht in https://github.com/Becksteinlab/imdclient/pull/73
29-
* Parse input files for DT when not in traj by @ljwoods2 in https://github.com/Becksteinlab/imdclient/pull/71
38+
* MD engine links updated in IMDClient documentation by @amruthesht in https://github.com/Becksteinlab/imdclient/pull/73
39+
* Testing: Parse input files for DT when not in traj by @ljwoods2 in https://github.com/Becksteinlab/imdclient/pull/71
3040

3141
### Fixed
3242
<!-- Bug fixes -->
43+
* updated deployment workflow to use latest pypa/gh-action-pypi-publish@v1.12.4 action (PR #78)
3344

34-
### Changed
35-
<!-- Changes in existing functionality -->
36-
* IMDReader removed from imdclient by @amruthesht in https://github.com/Becksteinlab/imdclient/pull/54
3745

3846
### Deprecated
3947
<!-- Soon-to-be removed features -->

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ IMDClient is bound by a [Code of Conduct](https://github.com/becksteinlab/imdcli
2525

2626
### Installation
2727

28+
IMDClient requires Python 3.10 or higher.
29+
2830
#### Install via mamba (recommended)
2931
To install the latest release of IMDClient from conda-forge:
3032

docs/source/getting_started.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Getting Started
44
Installation
55
############
66

7+
IMDClient requires Python 3.10 or higher.
8+
79
Install via mamba (recommended)
810
-------------------------------
911

docs/source/usage.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,18 @@ simulation engine to output stream data using IMDv3.
1313

1414
GROMACS
1515
-------
16+
The IMDv3 protocol is currently not available as part of the official GROMACS release
17+
or source code repository. However, the feature is currently available for use in
18+
the *imd-v3* branch of the forked repository
19+
https://gitlab.com/heydenlabasu/streaming-md/gromacs/-/tree/imd-v3 ; clone the
20+
repository and build GROMACS from source.
21+
1622
In GROMACS, you can use ``gmx mdrun`` with the ``-imdwait`` flag
1723
to ensure that GROMACS will wait for a client before starting the simulation.
24+
Specific IMDv3 options were added to GROMACS, as documented in the
25+
GROMACS User Guide `Molecular dynamics parameters (.mdp options)`_ under
26+
*Interactive Molecular Dynamics (IMD)*.
27+
1828
In GROMACS, you will know that the simulation is ready and waiting for the
1929
IMDClient when this line is printed to the terminal:
2030

@@ -24,8 +34,18 @@ IMDClient when this line is printed to the terminal:
2434
2535
You are now ready to connect to the simulation engine with a client.
2636

37+
.. TODO: update to official GROMACS docs (issue #79)
38+
.. _`Molecular dynamics parameters (.mdp options)`:
39+
https://gitlab.com/heydenlabasu/streaming-md/gromacs/-/blob/imd-v3/docs/user-guide/mdp-options.rst?ref_type=heads&plain=1
40+
41+
42+
2743
NAMD
2844
----
45+
The IMDv3 protocol has been implementaed in NAMD and will be made available through the
46+
official NAMD release in the near future. It is currently available as a part of the
47+
official NAMD GitLab repository - https://gitlab.com/tcbgUIUC/namd.
48+
2949
To use IMDv3 with NAMD, add the following lines to your NAMD configuration file:
3050

3151
.. code-block:: none
@@ -55,6 +75,11 @@ You are now ready to connect to the simulation engine with the IMDClient.
5575

5676
LAMMPS
5777
------
78+
The IMDv3 protocol is part of the official LAMMPS distribution since ``patch_4Feb2025``.
79+
It is available in the LAMMPS source code repository - https://github.com/lammps/lammps.
80+
Information on using IMDv3 with LAMMPS can be found in the LAMMPS documentation
81+
- https://docs.lammps.org/fix_imd.html
82+
5883
To use IMDv3 with LAMMPS, add the following lines to your LAMMPS input script:
5984

6085
.. code-block:: none

imdclient/tests/server.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,25 @@ def __init__(self, traj):
2929
self.listen_socket = None
3030
self.conn = None
3131
self.accept_thread = None
32+
self._bound_port = None
3233

3334
def set_imdsessioninfo(self, imdsinfo):
3435
self.imdsinfo = imdsinfo
3536

36-
def handshake_sequence(self, host, port, first_frame=True):
37+
@property
38+
def port(self):
39+
"""Get the port the server is bound to.
40+
41+
Returns:
42+
int: The port number, or None if not bound yet.
43+
"""
44+
return self._bound_port
45+
46+
def handshake_sequence(self, host, first_frame=True):
3747
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
38-
s.bind((host, port))
39-
logger.debug(f"InThreadIMDServer: Listening on {host}:{port}")
48+
s.bind((host, 0)) # Bind to port 0 to get a free port
49+
self._bound_port = s.getsockname()[1] # Store the actual bound port
50+
logger.debug(f"InThreadIMDServer: Listening on {host}:{self._bound_port}")
4051
s.listen(60)
4152
self.listen_socket = s
4253

imdclient/tests/test_imdclient.py

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
from imdclient.IMDClient import imdframe_memsize, IMDClient
1616
from imdclient.IMDProtocol import IMDHeaderType
1717
from .utils import (
18-
get_free_port,
1918
create_default_imdsinfo_v3,
2019
)
2120
from .server import InThreadIMDServer
@@ -46,10 +45,6 @@
4645

4746
class TestIMDClientV3:
4847

49-
@pytest.fixture
50-
def port(self):
51-
return get_free_port()
52-
5348
@pytest.fixture
5449
def universe(self):
5550
return mda.Universe(COORDINATES_TOPOLOGY, COORDINATES_H5MD)
@@ -59,13 +54,13 @@ def imdsinfo(self):
5954
return create_default_imdsinfo_v3()
6055

6156
@pytest.fixture
62-
def server_client_two_frame_buf(self, universe, imdsinfo, port):
57+
def server_client_two_frame_buf(self, universe, imdsinfo):
6358
server = InThreadIMDServer(universe.trajectory)
6459
server.set_imdsessioninfo(imdsinfo)
65-
server.handshake_sequence("localhost", port, first_frame=False)
60+
server.handshake_sequence("localhost", first_frame=False)
6661
client = IMDClient(
6762
f"localhost",
68-
port,
63+
server.port,
6964
universe.trajectory.n_atoms,
7065
buffer_size=imdframe_memsize(universe.trajectory.n_atoms, imdsinfo)
7166
* 2,
@@ -76,14 +71,14 @@ def server_client_two_frame_buf(self, universe, imdsinfo, port):
7671
server.cleanup()
7772

7873
@pytest.fixture(params=[">", "<"])
79-
def server_client(self, universe, imdsinfo, port, request):
74+
def server_client(self, universe, imdsinfo, request):
8075
server = InThreadIMDServer(universe.trajectory)
8176
imdsinfo.endianness = request.param
8277
server.set_imdsessioninfo(imdsinfo)
83-
server.handshake_sequence("localhost", port, first_frame=False)
78+
server.handshake_sequence("localhost", first_frame=False)
8479
client = IMDClient(
8580
f"localhost",
86-
port,
81+
server.port,
8782
universe.trajectory.n_atoms,
8883
)
8984
server.join_accept_thread()
@@ -168,13 +163,13 @@ def test_pause_resume_no_disconnect(self, server_client_two_frame_buf):
168163
server.expect_packet(IMDHeaderType.IMD_DISCONNECT)
169164

170165
@pytest.mark.parametrize("cont", [True, False])
171-
def test_continue_after_disconnect(self, universe, imdsinfo, port, cont):
166+
def test_continue_after_disconnect(self, universe, imdsinfo, cont):
172167
server = InThreadIMDServer(universe.trajectory)
173168
server.set_imdsessioninfo(imdsinfo)
174-
server.handshake_sequence("localhost", port, first_frame=False)
169+
server.handshake_sequence("localhost", first_frame=False)
175170
client = IMDClient(
176171
f"localhost",
177-
port,
172+
server.port,
178173
universe.trajectory.n_atoms,
179174
continue_after_disconnect=cont,
180175
)
@@ -198,10 +193,6 @@ def test_incorrect_atom_count(self, server_client_incorrect_atoms, universe):
198193

199194

200195
class TestIMDClientV3ContextManager:
201-
@pytest.fixture
202-
def port(self):
203-
return get_free_port()
204-
205196
@pytest.fixture
206197
def universe(self):
207198
return mda.Universe(COORDINATES_TOPOLOGY, COORDINATES_H5MD)
@@ -211,19 +202,19 @@ def imdsinfo(self):
211202
return create_default_imdsinfo_v3()
212203

213204
@pytest.fixture
214-
def server(self, universe, imdsinfo, port):
205+
def server(self, universe, imdsinfo):
215206
server = InThreadIMDServer(universe.trajectory)
216207
server.set_imdsessioninfo(imdsinfo)
217208
yield server
218209
server.cleanup()
219210

220-
def test_context_manager_traj_unchanged(self, server, port, universe):
221-
server.handshake_sequence("localhost", port, first_frame=False)
211+
def test_context_manager_traj_unchanged(self, server, universe):
212+
server.handshake_sequence("localhost", first_frame=False)
222213

223214
i = 0
224215
with IMDClient(
225216
"localhost",
226-
port,
217+
server.port,
227218
universe.trajectory.n_atoms,
228219
) as client:
229220
server.send_frames(0, 5)

0 commit comments

Comments
 (0)